update: merge instanced map geometry

This commit is contained in:
Tom Boullay
2026-05-15 23:29:07 +02:00
parent 9dff245aab
commit 6957b9e4f0
5 changed files with 149 additions and 100 deletions
+9
View File
@@ -31,3 +31,12 @@ declare module "three/addons/math/Octree.js" {
capsuleIntersect(capsule: Capsule): CapsuleIntersectResult | false;
}
}
declare module "three/addons/utils/BufferGeometryUtils.js" {
import { BufferGeometry } from "three";
export function mergeGeometries(
geometries: BufferGeometry[],
useGroups?: boolean,
): BufferGeometry | null;
}
+45 -15
View File
@@ -1,6 +1,7 @@
import { useEffect, useMemo, useRef } from "react";
import * as THREE from "three";
import { useGLTF } from "@react-three/drei";
import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js";
import type { VegetationInstance } from "@/world/vegetation/useVegetationData";
import { disposeInstancedMesh } from "@/utils/three/dispose";
@@ -13,27 +14,59 @@ interface InstancedVegetationProps {
interface MeshData {
geometry: THREE.BufferGeometry;
material: THREE.Material | THREE.Material[];
localMatrix: THREE.Matrix4;
material: THREE.Material;
}
function extractMeshes(scene: THREE.Group): MeshData[] {
const meshes: MeshData[] = [];
const meshesByMaterial = new Map<
string,
{ geometries: THREE.BufferGeometry[]; material: THREE.Material }
>();
scene.updateMatrixWorld(true);
scene.traverse((child) => {
if (child instanceof THREE.Mesh) {
meshes.push({
geometry: child.geometry.clone(),
material: Array.isArray(child.material)
? child.material.map((m) => m.clone())
: child.material.clone(),
localMatrix: child.matrixWorld.clone(),
if (!(child instanceof THREE.Mesh)) return;
const material = Array.isArray(child.material)
? child.material[0]
: child.material;
if (!material) return;
const geometry = child.geometry.clone();
geometry.applyMatrix4(child.matrixWorld);
const existing = meshesByMaterial.get(material.uuid);
if (existing) {
existing.geometries.push(geometry);
} else {
meshesByMaterial.set(material.uuid, {
geometries: [geometry],
material: material.clone(),
});
}
});
return meshes;
return [...meshesByMaterial.values()]
.map(({ geometries, material }) => {
const mergedGeometry = mergeGeometries(geometries, false);
for (const geometry of geometries) {
if (geometry !== mergedGeometry) {
geometry.dispose();
}
}
if (!mergedGeometry) {
material.dispose();
return null;
}
return {
geometry: mergedGeometry,
material,
};
})
.filter((meshData): meshData is MeshData => meshData !== null);
}
function createInstanceMatrices(
@@ -84,10 +117,7 @@ export function InstancedVegetation({
for (let i = 0; i < matrices.length; i++) {
const matrix = matrices[i];
if (matrix) {
instancedMesh.setMatrixAt(
i,
new THREE.Matrix4().multiplyMatrices(matrix, meshData.localMatrix),
);
instancedMesh.setMatrixAt(i, matrix);
}
}
-3
View File
@@ -8,7 +8,4 @@ export const INSTANCED_MAP_EXCEPTIONS = new Set([
"Scene",
"blocking",
"terrain",
"ecole",
"generateur",
"lafabrik",
]);