Refactor GLTF model import and project asset storage

This commit is contained in:
2026-03-31 17:30:26 +02:00
parent bcb6f5686e
commit 082f1ffc3a
2 changed files with 10 additions and 21 deletions

View File

@@ -1,9 +1,6 @@
import { Box3, BoxGeometry, Group, Mesh, MeshStandardMaterial, Object3D, type BufferGeometry, type Material } from "three";
import { Box3, Group, Mesh, Object3D } from "three";
import { GLTFLoader, type GLTF } from "three/examples/jsm/loaders/GLTFLoader.js";
import { createOpaqueId } from "../core/ids";
import type { Vec3 } from "../core/vector";
import { createModelInstance, type ModelInstance } from "./model-instances";
import {
createProjectAssetStorageKey,
@@ -11,6 +8,7 @@ import {
type ModelAssetRecord,
type ProjectAssetStorage
} from "./project-assets";
import { createOpaqueId } from "../core/ids";
import type { ProjectAssetStorageRecord } from "./project-asset-storage";
export interface LoadedModelAsset {
@@ -26,14 +24,6 @@ export interface ImportedModelAssetResult {
loadedAsset: LoadedModelAsset;
}
function cloneVec3(vector: Vec3): Vec3 {
return {
x: vector.x,
y: vector.y,
z: vector.z
};
}
function getErrorDetail(error: unknown): string {
if (error instanceof Error && error.message.trim().length > 0) {
return error.message.trim();
@@ -98,7 +88,7 @@ function collectMaterialNames(scene: Group): string[] {
const names = new Set<string>();
scene.traverse((object) => {
const maybeMesh = object as Mesh | null;
const maybeMesh = object as Mesh & { isMesh?: boolean };
if (maybeMesh.isMesh !== true) {
return;
@@ -131,7 +121,7 @@ function collectTextureNames(parserJson: { textures?: Array<{ name?: string }> }
function collectAnimationNames(gltf: GLTF): string[] {
return gltf.animations
.map((animation, index) => animation.name.trim().length > 0 ? animation.name : `Animation ${index + 1}`)
.map((animation, index) => (animation.name.trim().length > 0 ? animation.name : `Animation ${index + 1}`))
.sort((left, right) => left.localeCompare(right));
}
@@ -148,7 +138,6 @@ function countNodes(scene: Group): number {
export function extractModelAssetMetadata(gltf: GLTF, format: "glb" | "gltf"): ModelAssetMetadata {
gltf.scene.updateMatrixWorld(true);
const boundingBox = createBoundingBoxFromObject(gltf.scene);
const meshCount = gltf.scene.children.length === 0 ? 0 : gltf.scene.getObjectByProperty("isMesh", true) !== undefined ? 1 : 0;
let actualMeshCount = 0;
@@ -185,7 +174,7 @@ export function extractModelAssetMetadata(gltf: GLTF, format: "glb" | "gltf"): M
format,
sceneName: gltf.scene.name.trim().length > 0 ? gltf.scene.name : null,
nodeCount: countNodes(gltf.scene),
meshCount: actualMeshCount ?? meshCount,
meshCount: actualMeshCount,
materialNames: [...new Set(materialNames)].sort((left, right) => left.localeCompare(right)),
textureNames,
animationNames,
@@ -286,4 +275,3 @@ export async function loadModelAssetFromStorage(
return createLoadedModelAsset(asset, cloneTemplateScene(gltf.scene));
}

View File

@@ -70,8 +70,8 @@ function openIndexedDb(): Promise<IDBDatabase> {
class IndexedDbProjectAssetStorage implements ProjectAssetStorage {
private readonly databasePromise: Promise<IDBDatabase>;
constructor() {
this.databasePromise = openIndexedDb();
constructor(databasePromise: Promise<IDBDatabase>) {
this.databasePromise = databasePromise;
}
private async withStore<T>(mode: IDBTransactionMode, callback: (store: IDBObjectStore) => IDBRequest<T>): Promise<T> {
@@ -172,8 +172,10 @@ export async function getBrowserProjectAssetStorageAccess(): Promise<ProjectAsse
}
try {
const databasePromise = openIndexedDb();
await databasePromise;
return {
storage: new IndexedDbProjectAssetStorage(),
storage: new IndexedDbProjectAssetStorage(databasePromise),
diagnostic: null
};
} catch (error) {
@@ -183,4 +185,3 @@ export async function getBrowserProjectAssetStorageAccess(): Promise<ProjectAsse
};
}
}