From 2ff8e9fe4b28efe199efa8de54ad2af6b920f8a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CIsaac?= Date: Mon, 29 Jan 2024 08:48:26 +1000 Subject: [PATCH] feat: update @dimforge/rapier3d-compat to 0.12.0 --- .changeset/cold-apples-drop.md | 10 +++ demo/src/App.tsx | 31 ++++--- demo/src/examples/cradle/CradleExample.tsx | 9 +- .../examples/rope-joint/RopeJointExample.tsx | 41 +++++++++ demo/src/examples/spring/SpringExample.tsx | 39 +++++++++ packages/react-three-rapier/package.json | 2 +- .../src/components/Physics.tsx | 86 ++++++++++++------- .../react-three-rapier/src/hooks/joints.ts | 75 ++++++++++++---- packages/react-three-rapier/src/types.ts | 17 +++- .../tests/__snapshots__/physics.test.tsx.snap | 12 +-- .../react-three-rapier/tests/physics.test.tsx | 4 +- yarn.lock | 8 +- 12 files changed, 252 insertions(+), 82 deletions(-) create mode 100644 .changeset/cold-apples-drop.md create mode 100644 demo/src/examples/rope-joint/RopeJointExample.tsx create mode 100644 demo/src/examples/spring/SpringExample.tsx diff --git a/.changeset/cold-apples-drop.md b/.changeset/cold-apples-drop.md new file mode 100644 index 00000000..2a13060b --- /dev/null +++ b/.changeset/cold-apples-drop.md @@ -0,0 +1,10 @@ +--- +"@react-three/rapier": minor +--- + +feat: update @dimforge/rapier3d-compat to 0.12.0 + +- Physics component + - World integration parameters have changed in the new rapier version. As the new version of rapier has changes to it's constraint solver, there aren't direct alternatives for all old parameters. +- Add 'useSpringJoint' +- Add 'useRopeJoint' diff --git a/demo/src/App.tsx b/demo/src/App.tsx index e4699111..3fa511ae 100644 --- a/demo/src/App.tsx +++ b/demo/src/App.tsx @@ -1,15 +1,14 @@ import { Box, Environment, OrbitControls } from "@react-three/drei"; import { Canvas } from "@react-three/fiber"; -import { Physics, RigidBody, useRapier } from "@react-three/rapier"; +import { Physics, RigidBody } from "@react-three/rapier"; import { Perf } from "r3f-perf"; import { - createContext, ReactNode, + StrictMode, Suspense, + createContext, useContext, - useState, - StrictMode, - useEffect + useState } from "react"; import { NavLink, NavLinkProps, Route, Routes } from "react-router-dom"; import { AllCollidersExample } from "./examples/all-colliders/AllCollidersExample"; @@ -24,21 +23,23 @@ import { ComponentsExample } from "./examples/components/ComponentsExample"; import { ContactForceEventsExample } from "./examples/contact-force-events/ContactForceEventsExample"; import { CradleExample } from "./examples/cradle/CradleExample"; import { Damping } from "./examples/damping/DampingExample"; +import { DynamicTypeChangeExample } from "./examples/dynamic-type-change/DynamicTypeChangeExample"; +import { ImmutablePropsExample } from "./examples/immutable-props/ImmutablePropsExample"; import { InstancedMeshes } from "./examples/instanced-meshes/InstancedMeshesExample"; import { InstancedMeshesCompound } from "./examples/instances-meshes-compound/InstancedMeshesCompoundExample"; import { Joints } from "./examples/joints/JointsExample"; import { Kinematics } from "./examples/kinematics/KinematicsExample"; +import { LockedTransformsExample } from "./examples/locked-transforms/LockedTransformsExample"; import { ManualStepExample } from "./examples/manual-step/ManualStepExamples"; import { MeshColliderTest } from "./examples/mesh-collider-test/MeshColliderExample"; -import { SensorsExample } from "./examples/sensors/SensorsExample"; -import Shapes from "./examples/plinko/ShapesExample"; -import { Transforms } from "./examples/transforms/TransformsExample"; -import { LockedTransformsExample } from "./examples/locked-transforms/LockedTransformsExample"; import { PerformanceExample } from "./examples/performance/PeformanceExample"; -import { DynamicTypeChangeExample } from "./examples/dynamic-type-change/DynamicTypeChangeExample"; +import Shapes from "./examples/plinko/ShapesExample"; +import { RopeJointExample } from "./examples/rope-joint/RopeJointExample"; +import { SensorsExample } from "./examples/sensors/SensorsExample"; +import { SnapshotExample } from "./examples/snapshot/SnapshotExample"; +import { SpringExample } from "./examples/spring/SpringExample"; import { StutteringExample } from "./examples/stuttering/StutteringExample"; -import { ImmutablePropsExample } from "./examples/immutable-props/ImmutablePropsExample"; -import { SnapshotExample } from './examples/snapshot/SnapshotExample'; +import { Transforms } from "./examples/transforms/TransformsExample"; const demoContext = createContext<{ setDebug?(f: boolean): void; @@ -116,7 +117,9 @@ const routes: Record = { "dynamic-type-changes": , stuttering: , "immutable-props": , - snapshot: + snapshot: , + spring: , + ropeJoint: }; export const App = () => { @@ -149,7 +152,7 @@ export const App = () => { interpolate={interpolate} debug={debug} timeStep={1 / 60} - // erp={0.2} + // erp={0.2} > { @@ -20,12 +19,6 @@ const Rod = (props: RigidBodyOptions) => { [0, 0, 0] ]); - const { world } = useRapier(); - - useEffect(() => { - world.maxStabilizationIterations = 10; - }, []); - return ( diff --git a/demo/src/examples/rope-joint/RopeJointExample.tsx b/demo/src/examples/rope-joint/RopeJointExample.tsx new file mode 100644 index 00000000..4606f54c --- /dev/null +++ b/demo/src/examples/rope-joint/RopeJointExample.tsx @@ -0,0 +1,41 @@ +import { Sphere } from "@react-three/drei"; +import { + BallCollider, + RapierRigidBody, + RigidBody, + RigidBodyOptions, + useRopeJoint +} from "@react-three/rapier"; +import { useRef } from "react"; +import { Demo } from "../../App"; + +// todo: flesh out this demo + +const RopeJoint = (props: RigidBodyOptions) => { + const anchor = useRef(null); + const ball = useRef(null); + + useRopeJoint(anchor, ball, [[0, 0, 0], [0, 0, 0], 1]); + + return ( + + + + + + + + + + + + ); +}; + +export const RopeJointExample: Demo = () => { + return ( + + + + ); +}; diff --git a/demo/src/examples/spring/SpringExample.tsx b/demo/src/examples/spring/SpringExample.tsx new file mode 100644 index 00000000..9bb18724 --- /dev/null +++ b/demo/src/examples/spring/SpringExample.tsx @@ -0,0 +1,39 @@ +import { Sphere } from "@react-three/drei"; +import { + BallCollider, + RapierRigidBody, + RigidBody, + RigidBodyOptions, + useSpringJoint +} from "@react-three/rapier"; +import { useRef } from "react"; +import { Demo } from "../../App"; + +const Spring = (props: RigidBodyOptions) => { + const anchor = useRef(null); + const ball = useRef(null); + + useSpringJoint(anchor, ball, [[0, 0, 0], [0, 0, 0], 2, 20, 2]); + + return ( + + + + + + + + + + + + ); +}; + +export const SpringExample: Demo = () => { + return ( + + + + ); +}; diff --git a/packages/react-three-rapier/package.json b/packages/react-three-rapier/package.json index e03268f9..1de2da64 100644 --- a/packages/react-three-rapier/package.json +++ b/packages/react-three-rapier/package.json @@ -29,7 +29,7 @@ "three": ">=0.139.0" }, "dependencies": { - "@dimforge/rapier3d-compat": "0.11.2", + "@dimforge/rapier3d-compat": "0.12.0", "three-stdlib": "2.23.9", "use-asset": "1.0.4" }, diff --git a/packages/react-three-rapier/src/components/Physics.tsx b/packages/react-three-rapier/src/components/Physics.tsx index 3efae2f1..d681505a 100644 --- a/packages/react-three-rapier/src/components/Physics.tsx +++ b/packages/react-three-rapier/src/components/Physics.tsx @@ -257,32 +257,37 @@ export interface PhysicsProps { gravity?: Vector3Tuple; /** - * The maximum velocity iterations the velocity-based constraint solver can make to attempt - * to remove the energy introduced by constraint stabilization. - * - * @defaultValue 1 + * Amount of penetration the engine wont attempt to correct + * @defaultValue 0.001 */ - maxStabilizationIterations?: number; + allowedLinearError?: number; /** - * The maximum velocity iterations the velocity-based friction constraint solver can make. - * - * The greater this value is, the most realistic friction will be. + * The number of solver iterations run by the constraints solver for calculating forces. + * The greater this value is, the most rigid and realistic the physics simulation will be. * However a greater number of iterations is more computationally intensive. * - * @defaultValue 8 + * @defaultValue 4 */ - maxVelocityFrictionIterations?: number; + numSolverIterations?: number; /** - * The maximum velocity iterations the velocity-based force constraint solver can make. - * - * The greater this value is, the most rigid and realistic the physics simulation will be. + * Number of addition friction resolution iteration run during the last solver sub-step. + * The greater this value is, the most realistic friction will be. * However a greater number of iterations is more computationally intensive. * * @defaultValue 4 */ - maxVelocityIterations?: number; + numAdditionalFrictionIterations?: number; + + /** + * Number of internal Project Gauss Seidel (PGS) iterations run at each solver iteration. + * Increasing this parameter will improve stability of the simulation. It will have a lesser effect than + * increasing `numSolverIterations` but is also less computationally expensive. + * + * @defaultValue 1 + */ + numInternalPgsIterations?: number; /** * The maximal distance separating two objects that will generate predictive contacts @@ -293,7 +298,22 @@ export interface PhysicsProps { predictionDistance?: number; /** - * The Error Reduction Parameter in between 0 and 1, is the proportion of the positional error to be corrected at each time step + * Minimum number of dynamic bodies in each active island + * + * @defaultValue 128 + */ + minIslandSize?: number + + /** + * Maximum number of substeps performed by the solver + * + * @defaultValue 1 + */ + maxCcdSubsteps?: number + + /** + * The Error Reduction Parameter in between 0 and 1, is the proportion of the positional error to be corrected at each time step. + * * @defaultValue 0.8 */ erp?: number; @@ -380,11 +400,14 @@ export const Physics: FC = (props) => { debug = false, gravity = [0, -9.81, 0], - maxStabilizationIterations = 1, - maxVelocityFrictionIterations = 8, - maxVelocityIterations = 4, - predictionDistance = 0.002, - erp = 0.8 + allowedLinearError = 0.001, + predictionDistance = 4, + numSolverIterations = 4, + numAdditionalFrictionIterations = 4, + numInternalPgsIterations = 1, + minIslandSize = 128, + maxCcdSubsteps = 1, + erp = 0.2 } = props; const rapier = useAsset(importRapier); const { invalidate } = useThree(); @@ -421,20 +444,25 @@ export const Physics: FC = (props) => { // Update mutable props useEffect(() => { worldProxy.gravity = vectorArrayToVector3(gravity); - worldProxy.integrationParameters.maxStabilizationIterations = - maxStabilizationIterations; - worldProxy.integrationParameters.maxVelocityFrictionIterations = - maxVelocityFrictionIterations; - worldProxy.integrationParameters.maxVelocityIterations = - maxVelocityIterations; + + worldProxy.numSolverIterations = numSolverIterations + worldProxy.numAdditionalFrictionIterations = numAdditionalFrictionIterations + worldProxy.numInternalPgsIterations = numInternalPgsIterations + + worldProxy.integrationParameters.allowedLinearError = allowedLinearError + worldProxy.integrationParameters.minIslandSize = minIslandSize + worldProxy.integrationParameters.maxCcdSubsteps = maxCcdSubsteps worldProxy.integrationParameters.predictionDistance = predictionDistance; worldProxy.integrationParameters.erp = erp; }, [ worldProxy, ...gravity, - maxStabilizationIterations, - maxVelocityIterations, - maxVelocityFrictionIterations, + numSolverIterations, + numAdditionalFrictionIterations, + numInternalPgsIterations, + allowedLinearError, + minIslandSize, + maxCcdSubsteps, predictionDistance, erp ]); diff --git a/packages/react-three-rapier/src/hooks/joints.ts b/packages/react-three-rapier/src/hooks/joints.ts index 889efc10..df8a2d03 100644 --- a/packages/react-three-rapier/src/hooks/joints.ts +++ b/packages/react-three-rapier/src/hooks/joints.ts @@ -1,28 +1,25 @@ import { - ImpulseJoint, FixedImpulseJoint, - SphericalImpulseJoint, + ImpulseJoint, + PrismaticImpulseJoint, RevoluteImpulseJoint, - PrismaticImpulseJoint + RopeImpulseJoint, + SphericalImpulseJoint, + SpringImpulseJoint } from "@dimforge/rapier3d-compat"; -import React, { - useRef, - useEffect, - useMemo, - useState, - MutableRefObject, - RefObject -} from "react"; +import { RefObject, useRef } from "react"; import { - useRapier, - RapierRigidBody, - UseImpulseJoint, FixedJointParams, - SphericalJointParams, + PrismaticJointParams, + RapierRigidBody, RevoluteJointParams, - PrismaticJointParams + RopeJointParams, + SphericalJointParams, + SpringJointParams, + UseImpulseJoint, + useRapier } from ".."; -import { vectorArrayToVector3, tupleToObject } from "../utils/utils"; +import { tupleToObject, vectorArrayToVector3 } from "../utils/utils"; import type Rapier from "@dimforge/rapier3d-compat"; import { useImperativeInstance } from "./use-imperative-instance"; @@ -173,3 +170,47 @@ export const usePrismaticJoint: UseImpulseJoint< return useImpulseJoint(body1, body2, params); }; + +/** + * The rope joint limits the max distance between two bodies. + * @category Hooks - Joints + */ +export const useRopeJoint: UseImpulseJoint< + RopeJointParams, + RopeImpulseJoint +> = (body1, body2, [body1Anchor, body2Anchor, length]) => { + const { rapier } = useRapier(); + + const params = rapier.JointData.rope( + length, + vectorArrayToVector3(body1Anchor), + vectorArrayToVector3(body2Anchor) + ); + + return useImpulseJoint(body1, body2, params); +}; + +/** + * The spring joint applies a force proportional to the distance between two objects. + * @category Hooks - Joints + */ +export const useSpringJoint: UseImpulseJoint< + SpringJointParams, + SpringImpulseJoint +> = ( + body1, + body2, + [body1Anchor, body2Anchor, restLength, stiffness, damping] +) => { + const { rapier } = useRapier(); + + const params = rapier.JointData.spring( + restLength, + stiffness, + damping, + vectorArrayToVector3(body1Anchor), + vectorArrayToVector3(body2Anchor) + ); + + return useImpulseJoint(body1, body2, params); +}; diff --git a/packages/react-three-rapier/src/types.ts b/packages/react-three-rapier/src/types.ts index 2e03bdc4..4e835ccd 100644 --- a/packages/react-three-rapier/src/types.ts +++ b/packages/react-three-rapier/src/types.ts @@ -6,7 +6,8 @@ import { ImpulseJoint, InteractionGroups, RigidBody as RapierRigidBody, - TempContactManifold + TempContactManifold, + RopeImpulseJoint } from "@dimforge/rapier3d-compat"; import { Rotation, Vector } from "@dimforge/rapier3d-compat/math"; import { Object3DProps } from "@react-three/fiber"; @@ -475,6 +476,20 @@ export type RevoluteJointParams = [ limits?: [min: number, max: number] ]; +export type RopeJointParams = [ + body1Anchor: Vector3Tuple, + body2Anchor: Vector3Tuple, + length: number, +]; + +export type SpringJointParams = [ + body1Anchor: Vector3Tuple, + body2Anchor: Vector3Tuple, + restLength: number, + stiffness: number, + damping: number, +]; + export interface UseImpulseJoint { ( body1: RefObject, diff --git a/packages/react-three-rapier/tests/__snapshots__/physics.test.tsx.snap b/packages/react-three-rapier/tests/__snapshots__/physics.test.tsx.snap index 1e9c468c..fff728d7 100644 --- a/packages/react-three-rapier/tests/__snapshots__/physics.test.tsx.snap +++ b/packages/react-three-rapier/tests/__snapshots__/physics.test.tsx.snap @@ -4,17 +4,17 @@ exports[`physics > snapshots > restores snapshots correctly 1`] = ` [ Vector3 { "x": 0, - "y": -13.761244773864746, + "y": -13.659093856811523, "z": 0, }, Vector3 { "x": 2, - "y": -11.761244773864746, + "y": -11.659093856811523, "z": 2, }, Vector3 { "x": -2, - "y": -15.761244773864746, + "y": -15.659093856811523, "z": -2, }, ] @@ -24,17 +24,17 @@ exports[`physics > snapshots > restores snapshots correctly 2`] = ` [ Vector3 { "x": 0, - "y": -13.761244773864746, + "y": -13.659093856811523, "z": 0, }, Vector3 { "x": 2, - "y": -11.761244773864746, + "y": -11.659093856811523, "z": 2, }, Vector3 { "x": -2, - "y": -15.761244773864746, + "y": -15.659093856811523, "z": -2, }, ] diff --git a/packages/react-three-rapier/tests/physics.test.tsx b/packages/react-three-rapier/tests/physics.test.tsx index c06ee473..da9d783a 100644 --- a/packages/react-three-rapier/tests/physics.test.tsx +++ b/packages/react-three-rapier/tests/physics.test.tsx @@ -67,14 +67,14 @@ describe("physics", () => { }); expect(vec3(rigidBody.current?.translation()).toArray()).to.deep.eq([ - 0.6666666865348816, 0.6639417409896851, 0.6666666865348816 + 0.6666666269302368, 0.6649635434150696, 0.6666666269302368, ]); await pause(100); // expect nothing to have changed expect(vec3(rigidBody.current?.translation()).toArray()).to.deep.eq([ - 0.6666666865348816, 0.6639417409896851, 0.6666666865348816 + 0.6666666269302368, 0.6649635434150696, 0.6666666269302368 ]); }); }); diff --git a/yarn.lock b/yarn.lock index dda8b6d7..52faa5ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1595,10 +1595,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@dimforge/rapier3d-compat@0.11.2": - version "0.11.2" - resolved "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.11.2.tgz" - integrity sha512-vdWmlkpS3G8nGAzLuK7GYTpNdrkn/0NKCe0l1Jqxc7ZZOB3N0q9uG/Ap9l9bothWuAvxscIt0U97GVLr0lXWLg== +"@dimforge/rapier3d-compat@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz#7b3365e1dfdc5cd957b45afe920b4ac06c7cd389" + integrity sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow== "@esbuild/android-arm64@0.17.14": version "0.17.14"