fix(review): address audit findings before merge
🔍 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:
@@ -1,9 +1,9 @@
|
||||
import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
|
||||
import {
|
||||
PERSONNAGE_CONFIGS,
|
||||
PERSONNAGE_IDS,
|
||||
} from "@/data/world/personnages/personnageConfig";
|
||||
import { usePersonnageDebugStore } from "@/managers/stores/usePersonnageDebugStore";
|
||||
CHARACTER_CONFIGS,
|
||||
CHARACTER_IDS,
|
||||
} from "@/data/world/characters/characterConfig";
|
||||
import { useCharacterDebugStore } from "@/managers/stores/useCharacterDebugStore";
|
||||
|
||||
function createAnimationOptions(
|
||||
animations: readonly string[],
|
||||
@@ -17,13 +17,13 @@ function createAnimationOptions(
|
||||
);
|
||||
}
|
||||
|
||||
export function usePersonnageDebug(): void {
|
||||
export function useCharacterDebug(): void {
|
||||
useDebugFolder("Personnages", (folder) => {
|
||||
const store = usePersonnageDebugStore.getState();
|
||||
const store = useCharacterDebugStore.getState();
|
||||
|
||||
for (const id of PERSONNAGE_IDS) {
|
||||
const config = PERSONNAGE_CONFIGS[id];
|
||||
const state = store.personnages[id];
|
||||
for (const id of CHARACTER_IDS) {
|
||||
const config = CHARACTER_CONFIGS[id];
|
||||
const state = store.characters[id];
|
||||
const characterFolder = folder.addFolder(config.label);
|
||||
const controls = {
|
||||
animation: state.animation,
|
||||
@@ -42,64 +42,64 @@ export function usePersonnageDebug(): void {
|
||||
.add(controls, "animation", createAnimationOptions(config.animations))
|
||||
.name("Animation")
|
||||
.onChange((animation: string) => {
|
||||
usePersonnageDebugStore.getState().setAnimation(id, animation);
|
||||
useCharacterDebugStore.getState().setAnimation(id, animation);
|
||||
});
|
||||
|
||||
characterFolder
|
||||
.add(controls, "positionX", -120, 120, 0.1)
|
||||
.name("Position X")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setPosition(id, 0, value);
|
||||
useCharacterDebugStore.getState().setPosition(id, 0, value);
|
||||
});
|
||||
characterFolder
|
||||
.add(controls, "positionY", -20, 40, 0.1)
|
||||
.name("Position Y")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setPosition(id, 1, value);
|
||||
useCharacterDebugStore.getState().setPosition(id, 1, value);
|
||||
});
|
||||
characterFolder
|
||||
.add(controls, "positionZ", -120, 120, 0.1)
|
||||
.name("Position Z")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setPosition(id, 2, value);
|
||||
useCharacterDebugStore.getState().setPosition(id, 2, value);
|
||||
});
|
||||
|
||||
characterFolder
|
||||
.add(controls, "rotationX", -Math.PI, Math.PI, 0.01)
|
||||
.name("Rotation X")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setRotation(id, 0, value);
|
||||
useCharacterDebugStore.getState().setRotation(id, 0, value);
|
||||
});
|
||||
characterFolder
|
||||
.add(controls, "rotationY", -Math.PI, Math.PI, 0.01)
|
||||
.name("Rotation Y")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setRotation(id, 1, value);
|
||||
useCharacterDebugStore.getState().setRotation(id, 1, value);
|
||||
});
|
||||
characterFolder
|
||||
.add(controls, "rotationZ", -Math.PI, Math.PI, 0.01)
|
||||
.name("Rotation Z")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setRotation(id, 2, value);
|
||||
useCharacterDebugStore.getState().setRotation(id, 2, value);
|
||||
});
|
||||
|
||||
characterFolder
|
||||
.add(controls, "scaleX", 0.1, 5, 0.05)
|
||||
.name("Scale X")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setScale(id, 0, value);
|
||||
useCharacterDebugStore.getState().setScale(id, 0, value);
|
||||
});
|
||||
characterFolder
|
||||
.add(controls, "scaleY", 0.1, 5, 0.05)
|
||||
.name("Scale Y")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setScale(id, 1, value);
|
||||
useCharacterDebugStore.getState().setScale(id, 1, value);
|
||||
});
|
||||
characterFolder
|
||||
.add(controls, "scaleZ", 0.1, 5, 0.05)
|
||||
.name("Scale Z")
|
||||
.onChange((value: number) => {
|
||||
usePersonnageDebugStore.getState().setScale(id, 2, value);
|
||||
useCharacterDebugStore.getState().setScale(id, 2, value);
|
||||
});
|
||||
|
||||
characterFolder.close();
|
||||
@@ -2,29 +2,53 @@ import { useEffect, useMemo } from "react";
|
||||
import * as THREE from "three";
|
||||
import { disposeObject3D } from "@/utils/three/dispose";
|
||||
|
||||
function cloneObjectWithOwnedResources<T extends THREE.Object3D>(object: T): T {
|
||||
interface UseClonedObjectOptions {
|
||||
cloneResources?: boolean;
|
||||
}
|
||||
|
||||
function cloneMaterial(
|
||||
material: THREE.Material | THREE.Material[],
|
||||
): THREE.Material | THREE.Material[] {
|
||||
return Array.isArray(material)
|
||||
? material.map((item) => item.clone())
|
||||
: material.clone();
|
||||
}
|
||||
|
||||
function cloneObject<T extends THREE.Object3D>(
|
||||
object: T,
|
||||
cloneResources: boolean,
|
||||
): T {
|
||||
const clone = object.clone(true) as T;
|
||||
|
||||
if (!cloneResources) return clone;
|
||||
|
||||
clone.traverse((child) => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
|
||||
child.geometry = child.geometry.clone();
|
||||
child.material = Array.isArray(child.material)
|
||||
? child.material.map((material) => material.clone())
|
||||
: child.material.clone();
|
||||
child.material = cloneMaterial(child.material);
|
||||
});
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
export function useClonedObject<T extends THREE.Object3D>(object: T): T {
|
||||
const clone = useMemo(() => cloneObjectWithOwnedResources(object), [object]);
|
||||
export function useClonedObject<T extends THREE.Object3D>(
|
||||
object: T,
|
||||
options: UseClonedObjectOptions = {},
|
||||
): T {
|
||||
const cloneResources = options.cloneResources ?? false;
|
||||
const clone = useMemo(
|
||||
() => cloneObject(object, cloneResources),
|
||||
[cloneResources, object],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!cloneResources) return undefined;
|
||||
|
||||
return () => {
|
||||
disposeObject3D(clone);
|
||||
};
|
||||
}, [clone]);
|
||||
}, [clone, cloneResources]);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import type { RefObject } from "react";
|
||||
import type { Object3D } from "three";
|
||||
import { Octree } from "three/addons/math/Octree.js";
|
||||
import { Octree } from "three-stdlib";
|
||||
import type { OctreeReadyHandler } from "@/types/three/three";
|
||||
|
||||
export function useOctreeGraphNode(
|
||||
|
||||
@@ -47,6 +47,9 @@ function createTerrainHeightSampler(
|
||||
new THREE.Vector3(...scale),
|
||||
);
|
||||
const inverseTerrainMatrix = terrainMatrix.clone().invert();
|
||||
const localOrigin = new THREE.Vector3();
|
||||
const localDirection = DOWN.clone().transformDirection(inverseTerrainMatrix);
|
||||
const hits: THREE.Intersection[] = [];
|
||||
const raycaster = new THREE.Raycaster(
|
||||
new THREE.Vector3(),
|
||||
DOWN,
|
||||
@@ -63,13 +66,11 @@ function createTerrainHeightSampler(
|
||||
|
||||
return {
|
||||
getHeight: (x, z) => {
|
||||
const localOrigin = new THREE.Vector3(x, RAYCAST_Y, z).applyMatrix4(
|
||||
inverseTerrainMatrix,
|
||||
);
|
||||
const localDirection =
|
||||
DOWN.clone().transformDirection(inverseTerrainMatrix);
|
||||
localOrigin.set(x, RAYCAST_Y, z).applyMatrix4(inverseTerrainMatrix);
|
||||
raycaster.set(localOrigin, localDirection);
|
||||
const hit = raycaster.intersectObjects(meshes, false)[0];
|
||||
hits.length = 0;
|
||||
raycaster.intersectObjects(meshes, false, hits);
|
||||
const hit = hits[0];
|
||||
return hit?.point.applyMatrix4(terrainMatrix).y ?? null;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { INSTANCED_MAP_EXCEPTIONS } from "@/data/world/vegetationConfig";
|
||||
import { VEGETATION_INSTANCE_EXCLUDED_NODE_NAMES } from "@/data/world/vegetationConfig";
|
||||
import type { MapNode, VegetationInstance } from "@/types/map/mapScene";
|
||||
import { mapNodeToInstanceTransform } from "@/utils/map/mapInstanceTransform";
|
||||
import { logger } from "@/utils/core/Logger";
|
||||
import { loadMapSceneData } from "@/utils/map/loadMapSceneData";
|
||||
import {
|
||||
createPotagerMapNode,
|
||||
isPotagerSourceMapNode,
|
||||
POTAGER_MAP_NAME,
|
||||
} from "@/utils/map/potagerMapNodes";
|
||||
|
||||
interface InstancedMapEntry {
|
||||
modelPath: string;
|
||||
@@ -17,10 +12,6 @@ interface InstancedMapEntry {
|
||||
|
||||
export type VegetationData = Map<string, InstancedMapEntry>;
|
||||
|
||||
function createPositionKey(node: MapNode): string {
|
||||
return node.position.map((value) => value.toFixed(3)).join(":");
|
||||
}
|
||||
|
||||
function extractVegetationData(
|
||||
mapNodes: MapNode[],
|
||||
models: Map<string, string>,
|
||||
@@ -48,7 +39,7 @@ function extractVegetationData(
|
||||
|
||||
for (const node of mapNodes) {
|
||||
if (node.type !== "Object3D") continue;
|
||||
if (INSTANCED_MAP_EXCEPTIONS.has(node.name)) continue;
|
||||
if (VEGETATION_INSTANCE_EXCLUDED_NODE_NAMES.has(node.name)) continue;
|
||||
|
||||
const modelPath = models.get(node.name);
|
||||
if (!modelPath) continue;
|
||||
@@ -56,35 +47,6 @@ function extractVegetationData(
|
||||
addInstance(node.name, modelPath, node);
|
||||
}
|
||||
|
||||
const existingPotagerPositionKeys = new Set(
|
||||
mapNodes
|
||||
.filter((node) => node.name === POTAGER_MAP_NAME)
|
||||
.map(createPositionKey),
|
||||
);
|
||||
|
||||
for (const node of mapNodes) {
|
||||
if (!isPotagerSourceMapNode(node)) continue;
|
||||
if (existingPotagerPositionKeys.has(createPositionKey(node))) continue;
|
||||
|
||||
addInstance(
|
||||
POTAGER_MAP_NAME,
|
||||
"/models/potager/potager.gltf",
|
||||
createPotagerMapNode(node),
|
||||
);
|
||||
}
|
||||
|
||||
const potagerEntry = data.get(POTAGER_MAP_NAME);
|
||||
if (potagerEntry) {
|
||||
const uniqueInstances = new Map<string, VegetationInstance>();
|
||||
for (const instance of potagerEntry.instances) {
|
||||
uniqueInstances.set(
|
||||
instance.position.map((value) => value.toFixed(3)).join(":"),
|
||||
instance,
|
||||
);
|
||||
}
|
||||
potagerEntry.instances = [...uniqueInstances.values()];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import type { Octree } from "three/addons/math/Octree.js";
|
||||
import type { Octree } from "three-stdlib";
|
||||
import type { SceneMode } from "@/types/debug/debug";
|
||||
import type { SceneLoadingChangeHandler } from "@/types/world/sceneLoading";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user