workspaces folders
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import type { ObjectType, RuntimeGameObjectInstance, RuntimeObjectInstance } from "../types";
|
||||
import { useEffect, useImperativeHandle, useRef, type Ref } from "react";
|
||||
import { Euler, Quaternion, type Group } from "three";
|
||||
import { Instance, Instances } from "@react-three/drei";
|
||||
import type { ThreeEvent } from "@react-three/fiber";
|
||||
import { ConvexHullCollider, RapierRigidBody } from "@react-three/rapier";
|
||||
import { SyncRigidBody } from "./SyncRigidBody";
|
||||
import { reaction, runInAction } from "mobx";
|
||||
import { v3toRapier } from "../utils";
|
||||
|
||||
export type ObjectViewInternalHandle = {
|
||||
group: Group | null;
|
||||
rb: RapierRigidBody | null;
|
||||
}
|
||||
|
||||
type ObjectViewInternalProps = {
|
||||
object: Omit<RuntimeObjectInstance, 'typeId'>;
|
||||
objectType: ObjectType;
|
||||
isEditor?: boolean;
|
||||
isPlayer?: boolean;
|
||||
onClick?: () => void;
|
||||
ref?: Ref<ObjectViewInternalHandle>;
|
||||
}
|
||||
|
||||
// not observer, instead reacts only to object.version field
|
||||
export const ObjectViewInternal = function ({ object, objectType, ref, ...props }: ObjectViewInternalProps) {
|
||||
const rbRef = useRef<RapierRigidBody>(null);
|
||||
const groupRef = useRef<Group | null>(null);
|
||||
useImperativeHandle(ref, () => ({ group: groupRef.current, rb: rbRef.current }));
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (props.isPlayer)
|
||||
return;
|
||||
return reaction(
|
||||
() => 'version' in object ? object.version : 0,
|
||||
() => {
|
||||
if (!rbRef.current)
|
||||
return;
|
||||
|
||||
const gameObj = object as RuntimeGameObjectInstance;
|
||||
|
||||
rbRef.current.setTranslation(v3toRapier(gameObj.position), true);
|
||||
|
||||
const euler = new Euler(gameObj.rotation[0], gameObj.rotation[1], gameObj.rotation[2]);
|
||||
const q = new Quaternion().setFromEuler(euler);
|
||||
rbRef.current.setRotation({ x: q.x, y: q.y, z: q.z, w: q.w }, true);
|
||||
|
||||
groupRef.current?.scale.set(...gameObj.scale);
|
||||
|
||||
rbRef.current.setLinvel(v3toRapier(gameObj.linearVelocity), true);
|
||||
|
||||
rbRef.current.setAngvel(v3toRapier(gameObj.angularVelocity), true);
|
||||
},
|
||||
);
|
||||
},
|
||||
[object, object.id, props.isPlayer]
|
||||
);
|
||||
|
||||
const gameObj = object as RuntimeGameObjectInstance;
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
return reaction(
|
||||
() => gameObj.pendingActions.impulse,
|
||||
(impulse) => {
|
||||
if (!impulse || !rbRef.current)
|
||||
return;
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { direction, amplitude } = impulse;
|
||||
amplitude *= 100;
|
||||
const v = { x: direction[0] * amplitude, y: direction[1] * amplitude, z: direction[2] * amplitude };
|
||||
rbRef.current.applyImpulse(v, true);
|
||||
runInAction(() => {
|
||||
gameObj.pendingActions.impulse = undefined;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
[gameObj.pendingActions, object.id]
|
||||
);
|
||||
|
||||
function handleClick(e: ThreeEvent<MouseEvent>) {
|
||||
if (e.delta > 5)
|
||||
return;
|
||||
e.stopPropagation();
|
||||
|
||||
props.onClick?.();
|
||||
}
|
||||
|
||||
return (
|
||||
<SyncRigidBody
|
||||
ref={rbRef}
|
||||
colliders={false}
|
||||
type={props.isPlayer ? 'kinematicPosition' : (object.physics ? 'dynamic' : 'fixed')}
|
||||
gravityScale={object.gravityScale}
|
||||
position={object.position}
|
||||
rotation={object.rotation}
|
||||
onSync={(data) => {
|
||||
runInAction(() => {
|
||||
object.position = data.position;
|
||||
object.rotation = data.rotation;
|
||||
if (!props.isEditor) {
|
||||
(object as RuntimeGameObjectInstance).linearVelocity = data.linearVelocity;
|
||||
(object as RuntimeGameObjectInstance).angularVelocity = data.radialVelocity;
|
||||
}
|
||||
// do not change object.version to not trigger rerender
|
||||
});
|
||||
}}
|
||||
>
|
||||
<group
|
||||
ref={groupRef}
|
||||
name={`${object.id} (${objectType.id} instance)`}
|
||||
scale={object.scale}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{
|
||||
object.cache.voxelGroups.map((vg) =>
|
||||
<Instances key={`${vg.id}-${object.cache.updatedAt}`} limit={vg.positions.length}>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color={vg.color} opacity={vg.opacity} transparent={vg.opacity < 1} />
|
||||
{vg.positions.map((pos, i) => <Instance key={i} position={pos} />)}
|
||||
</Instances>
|
||||
)
|
||||
}
|
||||
{object.cache.colliderMesh && <ConvexHullCollider args={[object.cache.colliderMesh[0]]} />}
|
||||
</group>
|
||||
</SyncRigidBody>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user