perf: applique les échelles aux assets instanciés

This commit is contained in:
tom-boullay
2026-05-28 15:49:26 +02:00
parent 0b3d49e8d1
commit fcdbf7270c
3 changed files with 48 additions and 18 deletions
+7 -7
View File
@@ -4,16 +4,11 @@ import {
MAP_INSTANCING_ASSET_TYPES, MAP_INSTANCING_ASSET_TYPES,
type MapInstancingAssetType, type MapInstancingAssetType,
} from "@/data/world/mapInstancingConfig"; } from "@/data/world/mapInstancingConfig";
import type { MapNode } from "@/types/map/mapScene"; import type { MapAssetInstance, MapNode } from "@/types/map/mapScene";
import { import { mapNodeToInstanceTransform } from "@/utils/map/mapInstanceTransform";
type MapNodeInstanceTransform,
mapNodeToInstanceTransform,
} from "@/utils/map/mapInstanceTransform";
import { logger } from "@/utils/core/Logger"; import { logger } from "@/utils/core/Logger";
import { getMapNodes, loadMapSceneData } from "@/utils/map/loadMapSceneData"; import { getMapNodes, loadMapSceneData } from "@/utils/map/loadMapSceneData";
export type MapAssetInstance = MapNodeInstanceTransform;
export type MapInstancingData = Map<MapInstancingAssetType, MapAssetInstance[]>; export type MapInstancingData = Map<MapInstancingAssetType, MapAssetInstance[]>;
function extractMapInstancingData(mapNodes: MapNode[]): MapInstancingData { function extractMapInstancingData(mapNodes: MapNode[]): MapInstancingData {
@@ -65,6 +60,11 @@ export function useMapInstancingData(): {
logger.error("MapInstancing", "Failed to load map instancing data", { logger.error("MapInstancing", "Failed to load map instancing data", {
error: error instanceof Error ? error : String(error), error: error instanceof Error ? error : String(error),
}); });
if (!cancelled) {
setData(null);
setIsLoading(false);
}
return;
} }
const nodes = getMapNodes(); const nodes = getMapNodes();
+22 -4
View File
@@ -7,12 +7,13 @@ import {
normalizeMapScale, normalizeMapScale,
useTerrainHeightSampler, useTerrainHeightSampler,
} from "@/hooks/three/useTerrainHeight"; } from "@/hooks/three/useTerrainHeight";
import type { MapAssetInstance } from "@/hooks/world/useMapInstancingData"; import type { MapAssetInstance } from "@/types/map/mapScene";
import { optimizeGLTFSceneTextures } from "@/utils/three/optimizeGLTFScene"; import { optimizeGLTFSceneTextures } from "@/utils/three/optimizeGLTFScene";
interface InstancedMapAssetProps { interface InstancedMapAssetProps {
modelPath: string; modelPath: string;
instances: MapAssetInstance[]; instances: MapAssetInstance[];
scaleMultiplier: number;
castShadow: boolean; castShadow: boolean;
receiveShadow: boolean; receiveShadow: boolean;
} }
@@ -133,6 +134,7 @@ function extractMeshes(scene: THREE.Group): MeshData[] {
function setInstanceMatrices( function setInstanceMatrices(
instancedMesh: THREE.InstancedMesh, instancedMesh: THREE.InstancedMesh,
instances: MapAssetInstance[], instances: MapAssetInstance[],
scaleMultiplier: number,
geometryBottomY: number, geometryBottomY: number,
): void { ): void {
const position = new THREE.Vector3(); const position = new THREE.Vector3();
@@ -148,7 +150,11 @@ function setInstanceMatrices(
position.set(...instance.position); position.set(...instance.position);
rotation.set(...instance.rotation); rotation.set(...instance.rotation);
quaternion.setFromEuler(rotation); quaternion.setFromEuler(rotation);
scale.set(...instance.scale); scale.set(
instance.scale[0] * scaleMultiplier,
instance.scale[1] * scaleMultiplier,
instance.scale[2] * scaleMultiplier,
);
position.y += -geometryBottomY * scale.y; position.y += -geometryBottomY * scale.y;
matrix.compose(position, quaternion, scale); matrix.compose(position, quaternion, scale);
instancedMesh.setMatrixAt(i, matrix); instancedMesh.setMatrixAt(i, matrix);
@@ -174,6 +180,7 @@ function getMeshBottomY(meshDataList: MeshData[]): number {
export function InstancedMapAsset({ export function InstancedMapAsset({
modelPath, modelPath,
instances, instances,
scaleMultiplier,
castShadow, castShadow,
receiveShadow, receiveShadow,
}: InstancedMapAssetProps): React.JSX.Element | null { }: InstancedMapAssetProps): React.JSX.Element | null {
@@ -219,7 +226,12 @@ export function InstancedMapAsset({
groundedInstances.length, groundedInstances.length,
); );
setInstanceMatrices(instancedMesh, groundedInstances, geometryBottomY); setInstanceMatrices(
instancedMesh,
groundedInstances,
scaleMultiplier,
geometryBottomY,
);
instancedMesh.castShadow = castShadow; instancedMesh.castShadow = castShadow;
instancedMesh.receiveShadow = receiveShadow; instancedMesh.receiveShadow = receiveShadow;
instancedMesh.name = `instanced-map-asset-${index}`; instancedMesh.name = `instanced-map-asset-${index}`;
@@ -239,7 +251,13 @@ export function InstancedMapAsset({
disposeInstancedMapMesh(mesh); disposeInstancedMapMesh(mesh);
} }
}; };
}, [castShadow, groundedInstances, meshDataList, receiveShadow]); }, [
castShadow,
groundedInstances,
meshDataList,
receiveShadow,
scaleMultiplier,
]);
if (instances.length === 0) { if (instances.length === 0) {
return null; return null;
@@ -14,10 +14,13 @@ import {
type MapInstancingAssetConfig, type MapInstancingAssetConfig,
type MapInstancingAssetType, type MapInstancingAssetType,
} from "@/data/world/mapInstancingConfig"; } from "@/data/world/mapInstancingConfig";
import { import { useMapInstancingData } from "@/hooks/world/useMapInstancingData";
type MapAssetInstance, import type { MapAssetInstance } from "@/types/map/mapScene";
useMapInstancingData,
} from "@/hooks/world/useMapInstancingData"; interface MapInstancingSystemProps {
onlyModelName?: string | null;
streaming?: boolean;
}
interface MapAssetChunk { interface MapAssetChunk {
key: string; key: string;
@@ -72,14 +75,20 @@ function createMapAssetChunks(
}); });
} }
export function MapInstancingSystem(): React.JSX.Element | null { export function MapInstancingSystem({
onlyModelName = null,
streaming = true,
}: MapInstancingSystemProps): React.JSX.Element | null {
const cameraMode = useCameraMode(); const cameraMode = useCameraMode();
const sceneMode = useSceneMode(); const sceneMode = useSceneMode();
const groups = useMapPerformanceStore((state) => state.groups); const groups = useMapPerformanceStore((state) => state.groups);
const models = useMapPerformanceStore((state) => state.models); const models = useMapPerformanceStore((state) => state.models);
const { data, isLoading } = useMapInstancingData(); const { data, isLoading } = useMapInstancingData();
const streamingEnabled = const streamingEnabled =
CHUNK_CONFIG.enabled && sceneMode === "game" && cameraMode === "player"; streaming &&
CHUNK_CONFIG.enabled &&
sceneMode === "game" &&
cameraMode === "player";
const chunks = useMemo(() => { const chunks = useMemo(() => {
if (!data) return []; if (!data) return [];
@@ -87,6 +96,8 @@ export function MapInstancingSystem(): React.JSX.Element | null {
return MAP_INSTANCING_ASSET_TYPES.flatMap((type) => { return MAP_INSTANCING_ASSET_TYPES.flatMap((type) => {
const config = MAP_INSTANCING_ASSETS[type]; const config = MAP_INSTANCING_ASSETS[type];
if (onlyModelName && config.mapName !== onlyModelName) return [];
if ( if (
!config.enabled || !config.enabled ||
!isMapModelVisible(config.mapName, { groups, models }) !isMapModelVisible(config.mapName, { groups, models })
@@ -99,7 +110,7 @@ export function MapInstancingSystem(): React.JSX.Element | null {
return createMapAssetChunks(type, config, instances); return createMapAssetChunks(type, config, instances);
}); });
}, [data, groups, models]); }, [data, groups, models, onlyModelName]);
const visibleChunks = useVisibleWorldChunks(chunks, streamingEnabled); const visibleChunks = useVisibleWorldChunks(chunks, streamingEnabled);
@@ -114,6 +125,7 @@ export function MapInstancingSystem(): React.JSX.Element | null {
<InstancedMapAsset <InstancedMapAsset
modelPath={chunk.config.modelPath} modelPath={chunk.config.modelPath}
instances={chunk.instances} instances={chunk.instances}
scaleMultiplier={chunk.config.scaleMultiplier}
castShadow={chunk.config.castShadow} castShadow={chunk.config.castShadow}
receiveShadow={chunk.config.receiveShadow} receiveShadow={chunk.config.receiveShadow}
/> />