79 lines
2.7 KiB
TypeScript
79 lines
2.7 KiB
TypeScript
import { RigidBody, type RigidBodyProps, type RapierRigidBody } from "@react-three/rapier";
|
|
import { useFrame } from "@react-three/fiber";
|
|
import { useImperativeHandle, useRef, type Ref } from "react";
|
|
import { Euler, Quaternion } from "three";
|
|
import type { R3, V3 } from "../types";
|
|
|
|
export type SyncRigidBodyData = {
|
|
position: V3;
|
|
rotation: R3;
|
|
linearVelocity: V3;
|
|
radialVelocity: R3;
|
|
}
|
|
|
|
export type SyncRigidBodyOnSyncFunction = (data: SyncRigidBodyData) => void;
|
|
|
|
type SyncRigidBodyProps = RigidBodyProps & {
|
|
onSync: SyncRigidBodyOnSyncFunction;
|
|
ref?: Ref<RapierRigidBody | null>;
|
|
};
|
|
|
|
const _q = new Quaternion();
|
|
const _e = new Euler();
|
|
const EPS = 1e-6;
|
|
|
|
// indices: 0-2 position, 3-5 rotation, 6-8 linearVelocity, 9-11 radialVelocity
|
|
const PREV_INIT = new Float64Array(12).fill(Infinity);
|
|
|
|
function syncRigidBodyDataToArray(data: SyncRigidBodyData, arr: Float64Array): void {
|
|
arr[0] = data.position[0]; arr[1] = data.position[1]; arr[2] = data.position[2];
|
|
arr[3] = data.rotation[0]; arr[4] = data.rotation[1]; arr[5] = data.rotation[2];
|
|
arr[6] = data.linearVelocity[0]; arr[7] = data.linearVelocity[1]; arr[8] = data.linearVelocity[2];
|
|
arr[9] = data.radialVelocity[0]; arr[10] = data.radialVelocity[1]; arr[11] = data.radialVelocity[2];
|
|
}
|
|
|
|
function compareTwoFloatArrays(a: Float64Array, b: Float64Array, epsilon: number): boolean {
|
|
for (let i = 0; i < a.length; i++)
|
|
if (Math.abs(a[i] - b[i]) > epsilon)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
export function SyncRigidBody({ onSync, ref, children, ...props }: SyncRigidBodyProps) {
|
|
const rbRef = useRef<RapierRigidBody>(null);
|
|
|
|
useImperativeHandle(ref, () => rbRef.current!);
|
|
|
|
const prevData = useRef<Float64Array>(PREV_INIT.slice());
|
|
const currentData = useRef<Float64Array>(PREV_INIT.slice());
|
|
|
|
useFrame(() => {
|
|
const body = rbRef.current;
|
|
if (!body)
|
|
return;
|
|
|
|
const { x, y, z } = body.translation();
|
|
const rot = body.rotation();
|
|
_q.set(rot.x, rot.y, rot.z, rot.w);
|
|
_e.setFromQuaternion(_q);
|
|
const lv = body.linvel();
|
|
const av = body.angvel();
|
|
|
|
const data: SyncRigidBodyData = {
|
|
position: [x, y, z],
|
|
rotation: [_e.x, _e.y, _e.z],
|
|
linearVelocity: [lv.x, lv.y, lv.z],
|
|
radialVelocity: [av.x, av.y, av.z],
|
|
};
|
|
|
|
syncRigidBodyDataToArray(data, currentData.current);
|
|
|
|
if (compareTwoFloatArrays(currentData.current, prevData.current, EPS)) {
|
|
prevData.current.set(currentData.current);
|
|
onSync(data);
|
|
}
|
|
});
|
|
|
|
return <RigidBody ref={rbRef} {...props}>{children}</RigidBody>;
|
|
}
|