cleaaning
This commit is contained in:
@@ -4,12 +4,14 @@ import {
|
||||
useCallback,
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
type ElementRef,
|
||||
} from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { OrbitControls } from "@react-three/drei";
|
||||
import type { OrbitControls as OrbitControlsType } from "three-stdlib";
|
||||
import * as THREE from "three";
|
||||
|
||||
type OrbitControlsRef = ElementRef<typeof OrbitControls>;
|
||||
|
||||
interface FlyControllerProps {
|
||||
speed?: number;
|
||||
verticalSpeed?: number;
|
||||
@@ -17,8 +19,8 @@ interface FlyControllerProps {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface FlyControllerRef {
|
||||
controls: OrbitControlsType | null;
|
||||
interface FlyControllerRef {
|
||||
controls: OrbitControlsRef | null;
|
||||
}
|
||||
|
||||
export const FlyController = forwardRef<FlyControllerRef, FlyControllerProps>(
|
||||
@@ -29,7 +31,7 @@ export const FlyController = forwardRef<FlyControllerRef, FlyControllerProps>(
|
||||
const { camera: rawCamera } = useThree();
|
||||
const cameraRef = useRef(rawCamera);
|
||||
const keys = useRef<{ [key: string]: boolean }>({});
|
||||
const controlsRef = useRef<OrbitControlsType | null>(null);
|
||||
const controlsRef = useRef<OrbitControlsRef | null>(null);
|
||||
const lastPosition = useRef(new THREE.Vector3());
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
@@ -54,13 +56,12 @@ export const FlyController = forwardRef<FlyControllerRef, FlyControllerProps>(
|
||||
}, [handleKeyDown, handleKeyUp]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
// En mode disabled: ZQSD désactivé, on garde que OrbitControls
|
||||
// Disabled mode keeps OrbitControls active without keyboard movement.
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ZQSD (AZERTY): Z=forward, S=backward, Q=left, D=right
|
||||
// Support aussi QWERTY et flèches
|
||||
// Supports AZERTY, QWERTY, and arrow-key movement.
|
||||
const isForward =
|
||||
keys.current["KeyW"] || keys.current["KeyZ"] || keys.current["ArrowUp"];
|
||||
const isBackward = keys.current["KeyS"] || keys.current["ArrowDown"];
|
||||
@@ -89,7 +90,7 @@ export const FlyController = forwardRef<FlyControllerRef, FlyControllerProps>(
|
||||
cameraRef.current.position.add(direction);
|
||||
}
|
||||
|
||||
// Space = monter, Shift = descendre
|
||||
// Space moves up; Shift moves down.
|
||||
if (keys.current["Space"]) {
|
||||
cameraRef.current.position.y += verticalSpeed * delta;
|
||||
}
|
||||
|
||||
@@ -11,8 +11,11 @@ interface ObjectTransform {
|
||||
class HistoryManager {
|
||||
private history: ObjectTransform[][] = [];
|
||||
private currentIndex = -1;
|
||||
private maxSize: number;
|
||||
|
||||
constructor(private maxSize = 50) {}
|
||||
constructor(maxSize = 50) {
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
saveSnapshot(objects: ObjectTransform[]): void {
|
||||
if (this.currentIndex < this.history.length - 1) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useMemo, useRef, useEffect, useState } from "react";
|
||||
import { Grid, TransformControls, useGLTF } from "@react-three/drei";
|
||||
import type { ThreeEvent } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
|
||||
import type { SceneData, MapNode, TransformMode } from "@/types/editor";
|
||||
@@ -16,6 +17,53 @@ interface EditorMapProps {
|
||||
onNodeTransform: (nodeIndex: number, transform: MapNode) => void;
|
||||
}
|
||||
|
||||
type EditorNodeObjectRef = React.RefObject<Map<number, THREE.Object3D>>;
|
||||
|
||||
interface EditorNodeCommonProps {
|
||||
index: number;
|
||||
node: MapNode;
|
||||
isSelected: boolean;
|
||||
isHovered: boolean;
|
||||
objectsMapRef: EditorNodeObjectRef;
|
||||
onSelectNode: (index: number | null) => void;
|
||||
onHoverNode: (index: number | null) => void;
|
||||
}
|
||||
|
||||
function applyNodeTransform(object: THREE.Object3D, node: MapNode): void {
|
||||
object.position.set(...node.position);
|
||||
object.rotation.set(...node.rotation);
|
||||
object.scale.set(...node.scale);
|
||||
}
|
||||
|
||||
function useRegisteredEditorNode(
|
||||
objectRef: React.RefObject<THREE.Object3D | null>,
|
||||
index: number,
|
||||
node: MapNode,
|
||||
objectsMapRef: EditorNodeObjectRef,
|
||||
): void {
|
||||
useEffect(() => {
|
||||
const object = objectRef.current;
|
||||
if (object) {
|
||||
applyNodeTransform(object, node);
|
||||
object.userData = { nodeIndex: index, nodeName: node.name };
|
||||
objectsMapRef.current.set(index, object);
|
||||
}
|
||||
|
||||
const currentMap = objectsMapRef.current;
|
||||
const currentIndex = index;
|
||||
return () => {
|
||||
currentMap.delete(currentIndex);
|
||||
};
|
||||
}, [index, node, objectRef, objectsMapRef]);
|
||||
|
||||
useEffect(() => {
|
||||
const object = objectRef.current;
|
||||
if (object) {
|
||||
applyNodeTransform(object, node);
|
||||
}
|
||||
}, [node, objectRef]);
|
||||
}
|
||||
|
||||
export function EditorMap({
|
||||
sceneData,
|
||||
selectedNodeIndex,
|
||||
@@ -82,8 +130,8 @@ export function EditorMap({
|
||||
<axesHelper args={[10]} />
|
||||
|
||||
<group
|
||||
onClick={(e: unknown) => {
|
||||
(e as { stopPropagation?: () => void }).stopPropagation?.();
|
||||
onClick={(e: ThreeEvent<MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
onSelectNode(null);
|
||||
}}
|
||||
>
|
||||
@@ -142,68 +190,30 @@ function EditorModelNode({
|
||||
objectsMapRef,
|
||||
onSelectNode,
|
||||
onHoverNode,
|
||||
}: {
|
||||
index: number;
|
||||
node: MapNode;
|
||||
}: EditorNodeCommonProps & {
|
||||
modelUrl: string;
|
||||
isSelected: boolean;
|
||||
isHovered: boolean;
|
||||
objectsMapRef: React.RefObject<Map<number, THREE.Object3D>>;
|
||||
onSelectNode: (index: number | null) => void;
|
||||
onHoverNode: (index: number | null) => void;
|
||||
}) {
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const { scene } = useGLTF(modelUrl);
|
||||
|
||||
const sceneInstance = useMemo(() => scene.clone(true), [scene]);
|
||||
|
||||
useEffect(() => {
|
||||
if (groupRef.current) {
|
||||
groupRef.current.position.set(...node.position);
|
||||
groupRef.current.rotation.set(...node.rotation);
|
||||
groupRef.current.scale.set(...node.scale);
|
||||
groupRef.current.userData = { nodeIndex: index, nodeName: node.name };
|
||||
objectsMapRef.current.set(index, groupRef.current);
|
||||
}
|
||||
const currentMap = objectsMapRef.current;
|
||||
const currentIndex = index;
|
||||
return () => {
|
||||
currentMap.delete(currentIndex);
|
||||
};
|
||||
}, [
|
||||
index,
|
||||
node.name,
|
||||
node.position,
|
||||
node.rotation,
|
||||
node.scale,
|
||||
objectsMapRef,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (groupRef.current) {
|
||||
groupRef.current.position.set(...node.position);
|
||||
groupRef.current.rotation.set(...node.rotation);
|
||||
groupRef.current.scale.set(...node.scale);
|
||||
}
|
||||
}, [node.position, node.rotation, node.scale]);
|
||||
useRegisteredEditorNode(groupRef, index, node, objectsMapRef);
|
||||
|
||||
useEffect(() => {
|
||||
if (!groupRef.current) return;
|
||||
|
||||
groupRef.current.traverse((child) => {
|
||||
if ((child as THREE.Mesh).isMesh) {
|
||||
const mesh = child as THREE.Mesh;
|
||||
if (
|
||||
mesh.material &&
|
||||
mesh.material instanceof THREE.MeshStandardMaterial
|
||||
) {
|
||||
if (isSelected) {
|
||||
mesh.material = mesh.material.clone();
|
||||
(mesh.material as THREE.MeshStandardMaterial).color.set("#ffffff");
|
||||
} else if (isHovered) {
|
||||
mesh.material = mesh.material.clone();
|
||||
(mesh.material as THREE.MeshStandardMaterial).color.set("#b8b8b8");
|
||||
}
|
||||
if (!(child instanceof THREE.Mesh)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (child.material instanceof THREE.MeshStandardMaterial) {
|
||||
if (isSelected) {
|
||||
child.material = child.material.clone();
|
||||
child.material.color.set("#ffffff");
|
||||
} else if (isHovered) {
|
||||
child.material = child.material.clone();
|
||||
child.material.color.set("#b8b8b8");
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -216,16 +226,16 @@ function EditorModelNode({
|
||||
position={node.position}
|
||||
rotation={node.rotation}
|
||||
scale={node.scale}
|
||||
onClick={(e: unknown) => {
|
||||
(e as { stopPropagation?: () => void }).stopPropagation?.();
|
||||
onClick={(e: ThreeEvent<MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
onSelectNode(index);
|
||||
}}
|
||||
onPointerEnter={(e: unknown) => {
|
||||
(e as { stopPropagation?: () => void }).stopPropagation?.();
|
||||
onPointerEnter={(e: ThreeEvent<PointerEvent>) => {
|
||||
e.stopPropagation();
|
||||
onHoverNode(index);
|
||||
}}
|
||||
onPointerLeave={(e: unknown) => {
|
||||
(e as { stopPropagation?: () => void }).stopPropagation?.();
|
||||
onPointerLeave={(e: ThreeEvent<PointerEvent>) => {
|
||||
e.stopPropagation();
|
||||
onHoverNode(null);
|
||||
}}
|
||||
/>
|
||||
@@ -240,46 +250,9 @@ function EditorFallbackNode({
|
||||
objectsMapRef,
|
||||
onSelectNode,
|
||||
onHoverNode,
|
||||
}: {
|
||||
index: number;
|
||||
node: MapNode;
|
||||
isSelected: boolean;
|
||||
isHovered: boolean;
|
||||
objectsMapRef: React.RefObject<Map<number, THREE.Object3D>>;
|
||||
onSelectNode: (index: number | null) => void;
|
||||
onHoverNode: (index: number | null) => void;
|
||||
}) {
|
||||
}: EditorNodeCommonProps) {
|
||||
const meshRef = useRef<THREE.Mesh>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (meshRef.current) {
|
||||
meshRef.current.position.set(...node.position);
|
||||
meshRef.current.rotation.set(...node.rotation);
|
||||
meshRef.current.scale.set(...node.scale);
|
||||
meshRef.current.userData = { nodeIndex: index, nodeName: node.name };
|
||||
objectsMapRef.current.set(index, meshRef.current);
|
||||
}
|
||||
const currentMap = objectsMapRef.current;
|
||||
const currentIndex = index;
|
||||
return () => {
|
||||
currentMap.delete(currentIndex);
|
||||
};
|
||||
}, [
|
||||
index,
|
||||
node.name,
|
||||
node.position,
|
||||
node.rotation,
|
||||
node.scale,
|
||||
objectsMapRef,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (meshRef.current) {
|
||||
meshRef.current.position.set(...node.position);
|
||||
meshRef.current.rotation.set(...node.rotation);
|
||||
meshRef.current.scale.set(...node.scale);
|
||||
}
|
||||
}, [node.position, node.rotation, node.scale]);
|
||||
useRegisteredEditorNode(meshRef, index, node, objectsMapRef);
|
||||
|
||||
const color = isSelected ? "#ffffff" : isHovered ? "#b8b8b8" : "#6f6f6f";
|
||||
|
||||
@@ -289,16 +262,16 @@ function EditorFallbackNode({
|
||||
position={node.position}
|
||||
rotation={node.rotation}
|
||||
scale={node.scale}
|
||||
onClick={(e: unknown) => {
|
||||
(e as { stopPropagation?: () => void }).stopPropagation?.();
|
||||
onClick={(e: ThreeEvent<MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
onSelectNode(index);
|
||||
}}
|
||||
onPointerEnter={(e: unknown) => {
|
||||
(e as { stopPropagation?: () => void }).stopPropagation?.();
|
||||
onPointerEnter={(e: ThreeEvent<PointerEvent>) => {
|
||||
e.stopPropagation();
|
||||
onHoverNode(index);
|
||||
}}
|
||||
onPointerLeave={(e: unknown) => {
|
||||
(e as { stopPropagation?: () => void }).stopPropagation?.();
|
||||
onPointerLeave={(e: ThreeEvent<PointerEvent>) => {
|
||||
e.stopPropagation();
|
||||
onHoverNode(null);
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user