Merge remote map editor updates

This commit is contained in:
Tom Boullay
2026-05-27 22:24:59 +02:00
20 changed files with 718 additions and 209 deletions
+6 -4
View File
@@ -7,7 +7,7 @@ import type {
interface ObjectTransform {
uuid: string;
path: number[];
sourcePath?: number[];
position: { x: number; y: number; z: number };
rotation: { x: number; y: number; z: number };
scale: { x: number; y: number; z: number };
@@ -166,12 +166,14 @@ export function useEditorHistory(
scale: [transform.scale.x, transform.scale.y, transform.scale.z],
} satisfies MapNode;
mapTree = updateTreeNodeAtPath(mapTree, node.path, transform);
if (mapTree && node.sourcePath) {
mapTree = updateTreeNodeAtPath(mapTree, node.sourcePath, transform);
}
return nextNode;
});
return { ...prev, mapNodes, mapTree };
return mapTree ? { ...prev, mapNodes, mapTree } : { ...prev, mapNodes };
});
},
[setSceneData],
@@ -217,7 +219,7 @@ export function useEditorHistory(
function createSnapshot(sceneData: SceneData): ObjectTransform[] {
return sceneData.mapNodes.map((node, index) => ({
uuid: `node-${index}`,
path: node.path,
...(node.sourcePath ? { sourcePath: node.sourcePath } : {}),
position: {
x: node.position[0],
y: node.position[1],
+29 -4
View File
@@ -1,12 +1,16 @@
import { useMemo } from "react";
import { useGLTF } from "@react-three/drei";
import * as THREE from "three";
import { TERRAIN_MODEL_PATH } from "@/data/world/terrainConfig";
import type { Vector3Tuple } from "@/types/three/three";
import { getMapNodesByName } from "@/utils/map/loadMapSceneData";
const TERRAIN_MODEL_PATH = "/models/terrain/model.gltf";
const RAYCAST_Y = 500;
const RAYCAST_FAR = 1000;
const DOWN = new THREE.Vector3(0, -1, 0);
const DEFAULT_TERRAIN_POSITION: Vector3Tuple = [0, 0, 0];
const DEFAULT_TERRAIN_ROTATION: Vector3Tuple = [0, 0, 0];
const DEFAULT_TERRAIN_SCALE: Vector3Tuple = [1, 1, 1];
interface TerrainHeightSampler {
getHeight: (x: number, z: number) => number | null;
@@ -14,8 +18,17 @@ interface TerrainHeightSampler {
function createTerrainHeightSampler(
scene: THREE.Object3D,
position: Vector3Tuple,
rotation: Vector3Tuple,
scale: Vector3Tuple,
): TerrainHeightSampler {
const meshes: THREE.Mesh[] = [];
const terrainMatrix = new THREE.Matrix4().compose(
new THREE.Vector3(...position),
new THREE.Quaternion().setFromEuler(new THREE.Euler(...rotation)),
new THREE.Vector3(...scale),
);
const inverseTerrainMatrix = terrainMatrix.clone().invert();
const raycaster = new THREE.Raycaster(
new THREE.Vector3(),
DOWN,
@@ -32,17 +45,29 @@ function createTerrainHeightSampler(
return {
getHeight: (x, z) => {
raycaster.set(new THREE.Vector3(x, RAYCAST_Y, z), DOWN);
const localOrigin = new THREE.Vector3(x, RAYCAST_Y, z).applyMatrix4(
inverseTerrainMatrix,
);
const localDirection =
DOWN.clone().transformDirection(inverseTerrainMatrix);
raycaster.set(localOrigin, localDirection);
const hit = raycaster.intersectObjects(meshes, false)[0];
return hit?.point.y ?? null;
return hit?.point.applyMatrix4(terrainMatrix).y ?? null;
},
};
}
export function useTerrainHeightSampler(): TerrainHeightSampler {
const { scene } = useGLTF(TERRAIN_MODEL_PATH);
const terrainNode = getMapNodesByName("terrain")[0];
const position = terrainNode?.position ?? DEFAULT_TERRAIN_POSITION;
const rotation = terrainNode?.rotation ?? DEFAULT_TERRAIN_ROTATION;
const scale = terrainNode?.scale ?? DEFAULT_TERRAIN_SCALE;
return useMemo(() => createTerrainHeightSampler(scene), [scene]);
return useMemo(
() => createTerrainHeightSampler(scene, position, rotation, scale),
[position, rotation, scale, scene],
);
}
export function useTerrainSnappedPosition(