From 1098f03c7d52c52f986fd17904f08703ee9f1737 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Thu, 20 Nov 2025 13:19:24 -0600 Subject: [PATCH] Optimize STL asset loading with promise-based caching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace ImportMeshAsync with LoadAssetContainerAsync for person.stl - Cache loading promise to prevent race conditions and multiple fetches - Use instantiateModelsToScene() to create mesh instances from cached container - Simplify buildMesh signature to use DefaultScene singleton - Add Havok physics WASM prefetch hint to index.html 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- index.html | 1 + src/controllers/functions/grabAndClone.ts | 3 +- src/toolbox/functions/buildMesh.ts | 35 ++++++++++++++++++----- src/toolbox/functions/buildTool.ts | 2 +- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index fc23190..9e262fd 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ + DASFAD diff --git a/src/controllers/functions/grabAndClone.ts b/src/controllers/functions/grabAndClone.ts index d59f167..d9f04d7 100644 --- a/src/controllers/functions/grabAndClone.ts +++ b/src/controllers/functions/grabAndClone.ts @@ -4,6 +4,7 @@ import {DiagramObject} from "../../diagram/diagramObject"; import log from "loglevel"; import {vectoxys} from "../../diagram/functions/vectorConversion"; import {DiagramEntityType} from "../../diagram/types/diagramEntity"; +import {DefaultScene} from "../../defaultScene"; export function grabAndClone(diagramManager: DiagramManager, mesh: AbstractMesh, parent: AbstractMesh): DiagramObject { @@ -28,7 +29,7 @@ export function grabAndClone(diagramManager: DiagramManager, mesh: AbstractMesh, type: DiagramEntityType.ENTITY } - const obj = new DiagramObject(parent.getScene(), + const obj = new DiagramObject(DefaultScene.Scene, diagramManager.onDiagramEventObservable, { diagramEntity: entity, diff --git a/src/toolbox/functions/buildMesh.ts b/src/toolbox/functions/buildMesh.ts index 9a732b7..4075fdc 100644 --- a/src/toolbox/functions/buildMesh.ts +++ b/src/toolbox/functions/buildMesh.ts @@ -1,16 +1,21 @@ import {ToolType} from "../types/toolType"; -import {Mesh, MeshBuilder, Scene, SceneLoader} from "@babylonjs/core"; +import {AssetContainer, LoadAssetContainerAsync, Mesh, MeshBuilder, SceneLoader} from "@babylonjs/core"; import {DefaultScene} from "../../defaultScene"; +import log from "loglevel"; const detail = { tesselation: 16, subdivisions: 5 } -export async function buildMesh(type: ToolType, toolname: string, scene: Scene): Promise { +// Cache the loading promise to prevent multiple fetches and handle concurrent requests +let personAssetContainerPromise: Promise | null = null; +export async function buildMesh(type: ToolType, toolname: string): Promise { + const logger = log.getLogger('buldMesh'); + const scene = DefaultScene.Scene; switch (type) { case ToolType.BOX: - return MeshBuilder.CreateBox(toolname, {width: 1, height: 1, depth: 1}, scene); + return MeshBuilder.CreateBox(toolname, {width: 1, height: 1, depth: 1}); case ToolType.SPHERE: return MeshBuilder.CreateIcoSphere(toolname, { @@ -37,10 +42,26 @@ export async function buildMesh(type: ToolType, toolname: string, scene: Scene): tessellation: detail.tesselation }, scene); case ToolType.PERSON: - const result = await SceneLoader.ImportMeshAsync(null, '/assets/models/', 'person.stl', DefaultScene.Scene); - result.meshes[0].id = toolname; - result.meshes[0].name = toolname; - return result.meshes[0] as Mesh; + // If not already loading, start loading and cache the promise + if (!personAssetContainerPromise) { + logger.debug('Loading person.stl for first time'); + personAssetContainerPromise = LoadAssetContainerAsync( + '/assets/models/person.stl', + scene + ); + } + + // All concurrent calls await the same promise + const container = await personAssetContainerPromise; + + // Create new instance using BabylonJS's built-in instantiation + const entries = container.instantiateModelsToScene(); + const mesh = entries.rootNodes[0] as Mesh; + mesh.setParent(null); + mesh.id = toolname; + mesh.name = toolname; + + return mesh; case ToolType.PLANE: return MeshBuilder.CreatePlane(toolname, {width: 1, height: 1}, scene); diff --git a/src/toolbox/functions/buildTool.ts b/src/toolbox/functions/buildTool.ts index 53c55b8..dd52ef7 100644 --- a/src/toolbox/functions/buildTool.ts +++ b/src/toolbox/functions/buildTool.ts @@ -24,7 +24,7 @@ export async function buildTool(tool: ToolType, colorParent: AbstractMesh, mater } - const newItem = await buildMesh(tool, `tool-${id}`, colorParent.getScene()); + const newItem = await buildMesh(tool, `tool-${id}`); if (!newItem) { return null; }