Add support for some gltf extensions and better multi-object updates

This commit is contained in:
Yeicor
2024-03-28 19:12:21 +01:00
parent 7196fb2f32
commit 9afa2e5786
7 changed files with 71 additions and 26 deletions

View File

@@ -2225,13 +2225,13 @@ THE SOFTWARE.
The following npm package may be included in this product: 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: This package contains the following license and notice below:
The MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -21,7 +21,32 @@ export async function mergePartial(url: string, name: string, document: Document
networkFinished(); networkFinished();
// Load the new document // 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 // Remove any previous model with the same name
await document.transform(dropByName(name)); await document.transform(dropByName(name));

View File

@@ -93,28 +93,45 @@ export class NetworkManager extends EventTarget {
private foundModel(name: string, hash: string | null, url: string, isRemove: boolean | null, disconnect: () => void = () => { 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); // console.debug("Found model", name, "with hash", hash, "and previous hash", prevHash);
if (!hash || hash !== prevHash || isRemove) { // Also update buffered updates to have only the latest one per model
// Update known hashes this.bufferedUpdates = this.bufferedUpdates.filter(m => m.name !== name);
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);
// Optimization: try to batch updates automatically for faster rendering // Add the new model to the list of updates
if (this.batchTimeout !== null) clearTimeout(this.batchTimeout); let newModel = new NetworkUpdateEventModel(name, url, hash, isRemove);
this.batchTimeout = setTimeout(() => { this.bufferedUpdates.push(newModel);
this.dispatchEvent(new NetworkUpdateEvent(this.bufferedUpdates, disconnect));
this.bufferedUpdates = []; // Optimization: try to batch updates automatically for faster rendering
}, batchTimeout); 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);
} }
} }

2
frontend/shims.d.ts vendored
View File

@@ -1,3 +1,5 @@
// Avoids typescript error when importing some files // Avoids typescript error when importing some files
declare module '@jamescoyle/vue-icon' declare module '@jamescoyle/vue-icon'
declare module 'three-orientation-gizmo/src/OrientationGizmo' 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'

View File

@@ -15,7 +15,8 @@
"update-licenses": "generate-license-file --input package.json --output assets/licenses.txt --overwrite" "update-licenses": "generate-license-file --input package.json --output assets/licenses.txt --overwrite"
}, },
"dependencies": { "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", "@gltf-transform/functions": "^3.10.1",
"@google/model-viewer": "^3.4.0", "@google/model-viewer": "^3.4.0",
"@jamescoyle/vue-icon": "^0.1.2", "@jamescoyle/vue-icon": "^0.1.2",

View File

@@ -28,7 +28,7 @@ export default defineConfig({
build: { build: {
assetsDir: '.', assetsDir: '.',
cssCodeSplit: false, // Small enough to inline 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: { define: {
__APP_NAME__: JSON.stringify(name), __APP_NAME__: JSON.stringify(name),

View File

@@ -390,7 +390,7 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc"
integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== 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" version "3.10.1"
resolved "https://registry.yarnpkg.com/@gltf-transform/core/-/core-3.10.1.tgz#d99c060b499482ed2c3304466405bf4c10939831" resolved "https://registry.yarnpkg.com/@gltf-transform/core/-/core-3.10.1.tgz#d99c060b499482ed2c3304466405bf4c10939831"
integrity sha512-50OYemknGNxjBmiOM6iJp04JAu0bl9jvXJfN/gFt9QdJO02cPDcoXlTfSPJG6TVWDcfl0xPlsx1vybcbPVGFcQ== integrity sha512-50OYemknGNxjBmiOM6iJp04JAu0bl9jvXJfN/gFt9QdJO02cPDcoXlTfSPJG6TVWDcfl0xPlsx1vybcbPVGFcQ==