fix(editor): update transforms while dragging
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
This commit is contained in:
@@ -38,7 +38,8 @@ interface EditorControlsProps {
|
||||
redoCount: number;
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
onResetCamera: () => void;
|
||||
cameraActionLabel: string;
|
||||
onCameraAction: () => void;
|
||||
onExportJson: () => void;
|
||||
onSaveToServer?: (() => void | Promise<void>) | undefined;
|
||||
onPlayerMode?: (() => void) | undefined;
|
||||
@@ -103,7 +104,8 @@ export function EditorControls({
|
||||
redoCount,
|
||||
onUndo,
|
||||
onRedo,
|
||||
onResetCamera,
|
||||
cameraActionLabel,
|
||||
onCameraAction,
|
||||
onExportJson,
|
||||
onSaveToServer,
|
||||
onPlayerMode,
|
||||
@@ -271,9 +273,9 @@ export function EditorControls({
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button className="editor-action-button" onClick={onResetCamera}>
|
||||
<button className="editor-action-button" onClick={onCameraAction}>
|
||||
<ScanSearch size={16} aria-hidden="true" />
|
||||
Reset camera
|
||||
{cameraActionLabel}
|
||||
</button>
|
||||
|
||||
<label className="editor-checkbox-row">
|
||||
|
||||
@@ -6,16 +6,11 @@ import * as THREE from "three";
|
||||
import { TerrainModel } from "@/components/three/world/TerrainModel";
|
||||
import { useClonedObject } from "@/hooks/three/useClonedObject";
|
||||
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
|
||||
import {
|
||||
normalizeMapScale,
|
||||
useTerrainSnappedPosition,
|
||||
} from "@/hooks/three/useTerrainHeight";
|
||||
import type { SceneData, MapNode, TransformMode } from "@/types/editor/editor";
|
||||
import {
|
||||
isEditorVisibleMapNode,
|
||||
getTerrainMapNode,
|
||||
} from "@/utils/map/mapRuntimeClassification";
|
||||
import { getVegetationScaleMultiplier } from "@/world/vegetation/vegetationConfig";
|
||||
|
||||
interface EditorMapProps {
|
||||
sceneData: SceneData;
|
||||
@@ -161,6 +156,11 @@ export function EditorMap({
|
||||
};
|
||||
|
||||
const handleTransformMouseUp = () => {
|
||||
syncSelectedObjectTransform();
|
||||
onTransformEnd();
|
||||
};
|
||||
|
||||
const syncSelectedObjectTransform = () => {
|
||||
if (selectedNodeIndex !== null) {
|
||||
const obj = objectsMapRef.current.get(selectedNodeIndex);
|
||||
if (!obj) return;
|
||||
@@ -175,7 +175,6 @@ export function EditorMap({
|
||||
onNodeTransform(selectedNodeIndex, updatedNode);
|
||||
}
|
||||
}
|
||||
onTransformEnd();
|
||||
};
|
||||
|
||||
const [selectedObject, setSelectedObject] = useState<THREE.Object3D | null>(
|
||||
@@ -279,6 +278,7 @@ export function EditorMap({
|
||||
mode={transformMode}
|
||||
onMouseDown={handleTransformMouseDown}
|
||||
onMouseUp={handleTransformMouseUp}
|
||||
onObjectChange={syncSelectedObjectTransform}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -299,23 +299,14 @@ function EditorModelNode({
|
||||
modelUrl: string;
|
||||
}) {
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const snappedPosition = useTerrainSnappedPosition(node.position);
|
||||
const vegetationScaleMultiplier = getVegetationScaleMultiplier(node.name);
|
||||
const normalizedScale = vegetationScaleMultiplier
|
||||
? ([
|
||||
vegetationScaleMultiplier,
|
||||
vegetationScaleMultiplier,
|
||||
vegetationScaleMultiplier,
|
||||
] satisfies MapNode["scale"])
|
||||
: normalizeMapScale(node.scale);
|
||||
const originalMaterialsRef = useRef(
|
||||
new Map<THREE.Mesh, THREE.Material | THREE.Material[]>(),
|
||||
);
|
||||
const { scene } = useLoggedGLTF(modelUrl, {
|
||||
scope: "EditorMap.EditorModelNode",
|
||||
position: snappedPosition,
|
||||
position: node.position,
|
||||
rotation: node.rotation,
|
||||
scale: normalizedScale,
|
||||
scale: node.scale,
|
||||
});
|
||||
const sceneInstance = useClonedObject(scene);
|
||||
const pointerHandlers = createEditorNodePointerHandlers(
|
||||
@@ -324,16 +315,7 @@ function EditorModelNode({
|
||||
isSelectionLocked,
|
||||
onHoverNode,
|
||||
);
|
||||
useRegisteredEditorNode(
|
||||
groupRef,
|
||||
index,
|
||||
{
|
||||
...node,
|
||||
position: snappedPosition,
|
||||
scale: normalizedScale,
|
||||
},
|
||||
objectsMapRef,
|
||||
);
|
||||
useRegisteredEditorNode(groupRef, index, node, objectsMapRef);
|
||||
|
||||
useEffect(() => {
|
||||
if (!groupRef.current) return;
|
||||
@@ -390,9 +372,9 @@ function EditorModelNode({
|
||||
<primitive
|
||||
ref={groupRef}
|
||||
object={sceneInstance}
|
||||
position={snappedPosition}
|
||||
position={node.position}
|
||||
rotation={node.rotation}
|
||||
scale={normalizedScale}
|
||||
scale={node.scale}
|
||||
{...pointerHandlers}
|
||||
/>
|
||||
);
|
||||
@@ -440,33 +422,22 @@ function EditorFallbackNode({
|
||||
onHoverNode,
|
||||
}: EditorNodeCommonProps) {
|
||||
const meshRef = useRef<THREE.Mesh>(null);
|
||||
const snappedPosition = useTerrainSnappedPosition(node.position);
|
||||
const normalizedScale = normalizeMapScale(node.scale);
|
||||
const pointerHandlers = createEditorNodePointerHandlers(
|
||||
index,
|
||||
onSelectNode,
|
||||
isSelectionLocked,
|
||||
onHoverNode,
|
||||
);
|
||||
useRegisteredEditorNode(
|
||||
meshRef,
|
||||
index,
|
||||
{
|
||||
...node,
|
||||
position: snappedPosition,
|
||||
scale: normalizedScale,
|
||||
},
|
||||
objectsMapRef,
|
||||
);
|
||||
useRegisteredEditorNode(meshRef, index, node, objectsMapRef);
|
||||
|
||||
const color = getNodeHighlightColor(isSelected, isHovered) ?? "#6f6f6f";
|
||||
|
||||
return (
|
||||
<mesh
|
||||
ref={meshRef}
|
||||
position={snappedPosition}
|
||||
position={node.position}
|
||||
rotation={node.rotation}
|
||||
scale={normalizedScale}
|
||||
scale={node.scale}
|
||||
{...pointerHandlers}
|
||||
>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { OrbitControls } from "@react-three/drei";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import gsap from "gsap";
|
||||
@@ -33,6 +33,7 @@ interface EditorSceneProps {
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
resetCameraRequest: number;
|
||||
focusSelectedCameraRequest: number;
|
||||
isPlayerMode?: boolean;
|
||||
cinematicPreviewRequest?: EditorCinematicPreviewRequest | null;
|
||||
onCinematicPreviewComplete?: (() => void) | undefined;
|
||||
@@ -54,6 +55,7 @@ export function EditorScene({
|
||||
onUndo,
|
||||
onRedo,
|
||||
resetCameraRequest,
|
||||
focusSelectedCameraRequest,
|
||||
isPlayerMode = false,
|
||||
cinematicPreviewRequest = null,
|
||||
onCinematicPreviewComplete,
|
||||
@@ -63,6 +65,21 @@ export function EditorScene({
|
||||
const orbitControlsRef = useRef<OrbitControlsImpl | null>(null);
|
||||
const previousSelectedNodeIndexRef = useRef<number | null>(null);
|
||||
|
||||
const focusCameraOnNode = useCallback(
|
||||
(node: MapNode): void => {
|
||||
const controls = orbitControlsRef.current;
|
||||
const target = new THREE.Vector3(...node.position);
|
||||
const currentTarget = controls?.target ?? EDITOR_CAMERA_HOME_TARGET;
|
||||
const cameraOffset = camera.position.clone().sub(currentTarget);
|
||||
|
||||
camera.position.copy(target).add(cameraOffset);
|
||||
camera.lookAt(target);
|
||||
controls?.target.copy(target);
|
||||
controls?.update();
|
||||
},
|
||||
[camera],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedNodeIndex === previousSelectedNodeIndexRef.current) return;
|
||||
previousSelectedNodeIndexRef.current = selectedNodeIndex;
|
||||
@@ -74,19 +91,35 @@ export function EditorScene({
|
||||
const selectedNode = sceneData.mapNodes[selectedNodeIndex];
|
||||
if (!selectedNode) return;
|
||||
|
||||
const controls = orbitControlsRef.current;
|
||||
const target = new THREE.Vector3(...selectedNode.position);
|
||||
const currentTarget = controls?.target ?? EDITOR_CAMERA_HOME_TARGET;
|
||||
const cameraOffset = camera.position.clone().sub(currentTarget);
|
||||
|
||||
camera.position.copy(target).add(cameraOffset);
|
||||
camera.lookAt(target);
|
||||
controls?.target.copy(target);
|
||||
controls?.update();
|
||||
focusCameraOnNode(selectedNode);
|
||||
}, [
|
||||
camera,
|
||||
isCinematicPreviewing,
|
||||
isPlayerMode,
|
||||
focusCameraOnNode,
|
||||
sceneData,
|
||||
selectedNodeIndex,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
focusSelectedCameraRequest === 0 ||
|
||||
selectedNodeIndex === null ||
|
||||
isPlayerMode ||
|
||||
isCinematicPreviewing
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedNode = sceneData.mapNodes[selectedNodeIndex];
|
||||
if (!selectedNode) return;
|
||||
|
||||
focusCameraOnNode(selectedNode);
|
||||
}, [
|
||||
focusSelectedCameraRequest,
|
||||
focusCameraOnNode,
|
||||
isCinematicPreviewing,
|
||||
isPlayerMode,
|
||||
sceneData,
|
||||
selectedNodeIndex,
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user