diff --git a/src/assets/model-instance-rendering.ts b/src/assets/model-instance-rendering.ts index 873879e8..b2be90bf 100644 --- a/src/assets/model-instance-rendering.ts +++ b/src/assets/model-instance-rendering.ts @@ -1,4 +1,4 @@ -import { BoxGeometry, Group, Mesh, MeshBasicMaterial } from "three"; +import { BoxGeometry, Group, Mesh, MeshBasicMaterial, type Material, type Texture } from "three"; import type { Vec3 } from "../core/vector"; @@ -59,6 +59,59 @@ function createWireframeBox(size: Vec3, color: number, opacity: number): Mesh { ); } +function disposeTexture(texture: Texture, seenTextures: Set) { + if (seenTextures.has(texture)) { + return; + } + + seenTextures.add(texture); + texture.dispose(); +} + +function disposeMaterialResources(material: Material, disposeTextures: boolean, seenTextures: Set) { + if (disposeTextures) { + for (const value of Object.values(material as unknown as Record)) { + if (value === null || value === undefined) { + continue; + } + + if (Array.isArray(value)) { + for (const entry of value) { + if (entry !== null && typeof entry === "object" && "isTexture" in entry) { + disposeTexture(entry as Texture, seenTextures); + } + } + + continue; + } + + if (typeof value === "object" && "isTexture" in value) { + disposeTexture(value as Texture, seenTextures); + } + } + } + + material.dispose(); +} + +function disposeMeshResources(object: Group | Mesh, disposeTextures: boolean, seenTextures: Set) { + const maybeMesh = object as Mesh & { isMesh?: boolean }; + + if (maybeMesh.isMesh !== true) { + return; + } + + maybeMesh.geometry.dispose(); + + if (Array.isArray(maybeMesh.material)) { + for (const material of maybeMesh.material) { + disposeMaterialResources(material, disposeTextures, seenTextures); + } + } else { + disposeMaterialResources(maybeMesh.material, disposeTextures, seenTextures); + } +} + export function createModelInstanceRenderGroup( modelInstance: ModelInstance, asset: ProjectAssetRecord | undefined, @@ -94,3 +147,11 @@ export function createModelInstanceRenderGroup( return group; } + +export function disposeModelInstance(instance: Group) { + const seenTextures = new Set(); + + instance.traverse((object) => { + disposeMeshResources(object as Group | Mesh, false, seenTextures); + }); +}