Feat/map-environment #6

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