diff --git a/assets/licenses.txt b/assets/licenses.txt index db83252..8f6b074 100644 --- a/assets/licenses.txt +++ b/assets/licenses.txt @@ -2225,13 +2225,13 @@ THE SOFTWARE. The following npm package may be included in this product: - - three@0.162.0 + - three@0.160.1 This package contains the following license and notice below: The MIT License -Copyright © 2010-2024 three.js authors +Copyright © 2010-2023 three.js authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/frontend/misc/gltf.ts b/frontend/misc/gltf.ts index f5c32c5..efe40b2 100644 --- a/frontend/misc/gltf.ts +++ b/frontend/misc/gltf.ts @@ -21,7 +21,32 @@ export async function mergePartial(url: string, name: string, document: Document networkFinished(); // Load the new document - let newDoc = await io.readBinary(new Uint8Array(buffer)); + let newDoc = null; + let alreadyTried: { [name: string]: boolean } = {} + while (newDoc == null) { // Retry adding extensions as required until the document is loaded + try { // Try to load fast if no extensions are used + newDoc = await io.readBinary(new Uint8Array(buffer)); + } catch (e) { // Fallback to wait for download and register big extensions + if (e instanceof Error && e.message.toLowerCase().includes("khr_draco_mesh_compression")) { + if (alreadyTried["draco"]) throw e; else alreadyTried["draco"] = true; + // WARNING: Draco decompression on web is really slow for non-trivial models! (it should work?) + let {KHRDracoMeshCompression} = await import("@gltf-transform/extensions") + let dracoDecoderWeb = await import("three/examples/jsm/libs/draco/draco_decoder.js"); + let dracoEncoderWeb = await import("three/examples/jsm/libs/draco/draco_encoder.js"); + io.registerExtensions([KHRDracoMeshCompression]) + .registerDependencies({ + 'draco3d.decoder': await dracoDecoderWeb.default({}), + 'draco3d.encoder': await dracoEncoderWeb.default({}) + }); + } else if (e instanceof Error && e.message.toLowerCase().includes("ext_texture_webp")) { + if (alreadyTried["webp"]) throw e; else alreadyTried["webp"] = true; + let {EXTTextureWebP} = await import("@gltf-transform/extensions") + io.registerExtensions([EXTTextureWebP]); + } else { // TODO: Add more extensions as required + throw e; + } + } + } // Remove any previous model with the same name await document.transform(dropByName(name)); diff --git a/frontend/misc/network.ts b/frontend/misc/network.ts index f2e410b..6666e14 100644 --- a/frontend/misc/network.ts +++ b/frontend/misc/network.ts @@ -93,28 +93,45 @@ export class NetworkManager extends EventTarget { private foundModel(name: string, hash: string | null, url: string, isRemove: boolean | null, disconnect: () => void = () => { }) { - let prevHash = this.knownObjectHashes[name]; // console.debug("Found model", name, "with hash", hash, "and previous hash", prevHash); - if (!hash || hash !== prevHash || isRemove) { - // Update known hashes - if (isRemove == false) { - this.knownObjectHashes[name] = hash; - } else if (isRemove == true) { - if (!(name in this.knownObjectHashes)) return; // Nothing to remove... - delete this.knownObjectHashes[name]; - // Also update buffered updates if the model is removed - this.bufferedUpdates = this.bufferedUpdates.filter(m => m.name !== name); - } - let newModel = new NetworkUpdateEventModel(name, url, hash, isRemove); - this.bufferedUpdates.push(newModel); + // Also update buffered updates to have only the latest one per model + this.bufferedUpdates = this.bufferedUpdates.filter(m => m.name !== name); - // Optimization: try to batch updates automatically for faster rendering - if (this.batchTimeout !== null) clearTimeout(this.batchTimeout); - this.batchTimeout = setTimeout(() => { - this.dispatchEvent(new NetworkUpdateEvent(this.bufferedUpdates, disconnect)); - this.bufferedUpdates = []; - }, batchTimeout); - } + // Add the new model to the list of updates + let newModel = new NetworkUpdateEventModel(name, url, hash, isRemove); + this.bufferedUpdates.push(newModel); + + // Optimization: try to batch updates automatically for faster rendering + if (this.batchTimeout !== null) clearTimeout(this.batchTimeout); + this.batchTimeout = setTimeout(() => { + // Update known hashes for minimal updates + for (let model of this.bufferedUpdates) { + if (model.hash && model.hash === this.knownObjectHashes[model.name]) { + // Delete this useless update + let foundFirst = false; + this.bufferedUpdates = this.bufferedUpdates.filter(m => { + if (m === model) { + if (!foundFirst) { // Remove only first full match + foundFirst = true; + return false; + } + } + return true; + }) + } else { + // Keep this update and update the last known hash + if (model.isRemove == true) { + if (model.name in this.knownObjectHashes) delete this.knownObjectHashes[model.name]; + } else if (model.isRemove == false) { + this.knownObjectHashes[model.name] = model.hash; + } + } + } + + // Dispatch the event to actually update the models + this.dispatchEvent(new NetworkUpdateEvent(this.bufferedUpdates, disconnect)); + this.bufferedUpdates = []; + }, batchTimeout); } } diff --git a/frontend/shims.d.ts b/frontend/shims.d.ts index fd144a1..e657ea2 100644 --- a/frontend/shims.d.ts +++ b/frontend/shims.d.ts @@ -1,3 +1,5 @@ // Avoids typescript error when importing some files declare module '@jamescoyle/vue-icon' declare module 'three-orientation-gizmo/src/OrientationGizmo' +declare module 'three/examples/jsm/libs/draco/draco_decoder.js' +declare module 'three/examples/jsm/libs/draco/draco_encoder.js' \ No newline at end of file diff --git a/package.json b/package.json index e388543..115dd91 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "update-licenses": "generate-license-file --input package.json --output assets/licenses.txt --overwrite" }, "dependencies": { - "@gltf-transform/core": "^3.10.0", + "@gltf-transform/core": "^3.10.1", + "@gltf-transform/extensions": "^3.10.1", "@gltf-transform/functions": "^3.10.1", "@google/model-viewer": "^3.4.0", "@jamescoyle/vue-icon": "^0.1.2", diff --git a/vite.config.ts b/vite.config.ts index dfba31f..940a46a 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -28,7 +28,7 @@ export default defineConfig({ build: { assetsDir: '.', cssCodeSplit: false, // Small enough to inline - chunkSizeWarningLimit: 550, // Three.js is huge + chunkSizeWarningLimit: 550, // Three.js is big. Draco is even bigger but not likely to be used. }, define: { __APP_NAME__: JSON.stringify(name), diff --git a/yarn.lock b/yarn.lock index d3c63a7..c7a6561 100644 --- a/yarn.lock +++ b/yarn.lock @@ -390,7 +390,7 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== -"@gltf-transform/core@^3.10.0", "@gltf-transform/core@^3.10.1": +"@gltf-transform/core@^3.10.1": version "3.10.1" resolved "https://registry.yarnpkg.com/@gltf-transform/core/-/core-3.10.1.tgz#d99c060b499482ed2c3304466405bf4c10939831" integrity sha512-50OYemknGNxjBmiOM6iJp04JAu0bl9jvXJfN/gFt9QdJO02cPDcoXlTfSPJG6TVWDcfl0xPlsx1vybcbPVGFcQ==