Add external triangle scene GLTF and update file import handling

This commit is contained in:
2026-03-31 18:45:19 +02:00
parent 76b23cf007
commit 9ceff5999f
3 changed files with 87 additions and 8 deletions

View File

@@ -0,0 +1,65 @@
{
"asset": {
"version": "2.0",
"generator": "webeditor3d fixture"
},
"scene": 0,
"scenes": [
{
"name": "External Triangle Scene",
"nodes": [0]
}
],
"nodes": [
{
"mesh": 0,
"name": "External Triangle Node"
}
],
"meshes": [
{
"name": "External Triangle Mesh",
"primitives": [
{
"attributes": {
"POSITION": 0
},
"material": 0
}
]
}
],
"materials": [
{
"name": "External Material",
"pbrMetallicRoughness": {
"baseColorFactor": [0.2, 0.6, 0.85, 1],
"metallicFactor": 0,
"roughnessFactor": 1
}
}
],
"buffers": [
{
"byteLength": 36,
"uri": "triangle.bin"
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 36
}
],
"accessors": [
{
"bufferView": 0,
"componentType": 5126,
"count": 3,
"type": "VEC3",
"min": [0, 0, 0],
"max": [1, 1, 0]
}
]
}

View File

@@ -45,6 +45,7 @@ import {
} from "../assets/model-instance-labels";
import {
importModelAssetFromFile,
importModelAssetFromFiles,
loadModelAssetFromStorage,
disposeModelTemplate,
type ImportedModelAssetResult,
@@ -1938,9 +1939,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
const handleImportModelChange = async (event: ChangeEvent<HTMLInputElement>) => {
const input = event.currentTarget;
const file = input.files?.[0];
const files = Array.from(input.files ?? []);
if (file === undefined) {
if (files.length === 0) {
return;
}
@@ -1953,7 +1954,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
let importedModelForCleanup: ImportedModelAssetResult | null = null;
try {
const importedModel = await importModelAssetFromFile(file, projectAssetStorage);
const importedModel = files.length === 1
? await importModelAssetFromFile(files[0], projectAssetStorage)
: await importModelAssetFromFiles(files, projectAssetStorage);
importedModelForCleanup = importedModel;
store.executeCommand(
@@ -4014,6 +4017,7 @@ export function App({ store, initialStatusMessage }: AppProps) {
ref={importModelInputRef}
className="visually-hidden"
type="file"
multiple
accept=".glb,.gltf,model/gltf-binary,model/gltf+json,application/octet-stream"
onChange={handleImportModelChange}
/>

View File

@@ -4,21 +4,31 @@ import { createProjectAssetStorageKey } from "../../src/assets/project-assets";
import { createInMemoryProjectAssetStorage } from "../../src/assets/project-asset-storage";
describe("project asset storage", () => {
it("stores, clones, and deletes binary asset payloads", async () => {
it("stores, clones, and deletes binary asset file packages", async () => {
const storage = createInMemoryProjectAssetStorage();
const storageKey = createProjectAssetStorageKey("asset-model-triangle");
const bytes = new Uint8Array([0, 1, 2, 3, 4]).buffer;
const sidecarBytes = new Uint8Array([9, 8, 7]).buffer;
await storage.putAsset(storageKey, {
bytes,
mimeType: "model/gltf+json"
files: {
"tiny-triangle.gltf": {
bytes,
mimeType: "model/gltf+json"
},
"triangle.bin": {
bytes: sidecarBytes,
mimeType: "application/octet-stream"
}
}
});
const loadedAsset = await storage.getAsset(storageKey);
expect(loadedAsset).not.toBeNull();
expect(loadedAsset?.mimeType).toBe("model/gltf+json");
expect(Array.from(new Uint8Array(loadedAsset?.bytes ?? new ArrayBuffer(0)))).toEqual([0, 1, 2, 3, 4]);
expect(Object.keys(loadedAsset?.files ?? {})).toEqual(["tiny-triangle.gltf", "triangle.bin"]);
expect(Array.from(new Uint8Array(loadedAsset?.files["tiny-triangle.gltf"].bytes ?? new ArrayBuffer(0)))).toEqual([0, 1, 2, 3, 4]);
expect(Array.from(new Uint8Array(loadedAsset?.files["triangle.bin"].bytes ?? new ArrayBuffer(0)))).toEqual([9, 8, 7]);
await storage.deleteAsset(storageKey);