From bde46f689bde6c05c5ecc2047d4f5e6866df6a49 Mon Sep 17 00:00:00 2001 From: netpro2k <netpro2k@gmail.com> Date: Wed, 14 Mar 2018 18:19:51 -0700 Subject: [PATCH] Add support for changing src on an a-gltf-entity, also some code cleanup --- src/components/debug.js | 25 +++++ src/elements/a-gltf-entity.js | 169 ++++++++++++++++++++-------------- src/room.js | 1 + 3 files changed, 127 insertions(+), 68 deletions(-) create mode 100644 src/components/debug.js diff --git a/src/components/debug.js b/src/components/debug.js new file mode 100644 index 000000000..77202f032 --- /dev/null +++ b/src/components/debug.js @@ -0,0 +1,25 @@ +AFRAME.registerComponent("lifecycle-checker", { + schema: { + tick: { default: false } + }, + init: function() { + console.log("init", this.el); + }, + update: function() { + console.log("update", this.el); + }, + tick: function() { + if (this.data.tick) { + console.log("tick", this.el); + } + }, + remove: function() { + console.log("remove", this.el); + }, + pause: function() { + console.log("pause", this.el); + }, + play: function() { + console.log("play", this.el); + } +}); diff --git a/src/elements/a-gltf-entity.js b/src/elements/a-gltf-entity.js index 8c274cb44..f1afe249b 100644 --- a/src/elements/a-gltf-entity.js +++ b/src/elements/a-gltf-entity.js @@ -70,7 +70,7 @@ function cloneGltf(gltf) { return clone; } -const inflateEntities = function(classPrefix, parentEl, node) { +const inflateEntities = function(parentEl, node) { // setObject3D mutates the node's parent, so we have to copy const children = node.children.slice(0); @@ -78,7 +78,7 @@ const inflateEntities = function(classPrefix, parentEl, node) { // Remove invalid CSS class name characters. const className = (node.name || node.uuid).replace(/[^\w-]/g, ""); - el.classList.add(classPrefix + className); + el.classList.add(className); parentEl.appendChild(el); // AFRAME rotation component expects rotations in YXZ, convert it @@ -135,8 +135,10 @@ const inflateEntities = function(classPrefix, parentEl, node) { } children.forEach(childNode => { - inflateEntities(classPrefix, el, childNode); + inflateEntities( el, childNode); }); + + return el; }; function attachTemplate(templateEl) { @@ -158,93 +160,124 @@ function attachTemplate(templateEl) { } } +function cachedLoadGLTF(src, onProgress) { + return new Promise((resolve, reject) => { + // Load the gltf model from the cache if it exists. + if (GLTFCache[src]) { + // Use a cloned copy of the cached model. + resolve(cloneGltf(GLTFCache[src])); + } else { + // Otherwise load the new gltf model. + new THREE.GLTFLoader().load( + src, + model => { + if (!GLTFCache[src]) { + // Store a cloned copy of the gltf model. + GLTFCache[src] = cloneGltf(model); + } + resolve(model); + }, + onProgress, + reject + ); + } + }); +} + AFRAME.registerElement("a-gltf-entity", { prototype: Object.create(AFRAME.AEntity.prototype, { load: { - value() { + async value() { if (this.hasLoaded || !this.parentEl) { return; } - // Get the src url. - let src = this.getAttribute("src"); - - // If the src attribute is a selector, get the url from the asset item. - if (src.charAt(0) === "#") { - const assetEl = document.getElementById(src.substring(1)); + // The code above and below this are from AEntity.prototype.load, we need to monkeypatch in gltf loading mid function + await this.setSrc(this.getAttribute("src")); - const fallbackSrc = assetEl.getAttribute("src"); - const highSrc = assetEl.getAttribute("high-src"); - const lowSrc = assetEl.getAttribute("low-src"); - - if (highSrc && window.APP.quality === "high") { - src = highSrc; - } else if (lowSrc && window.APP.quality === "low") { - src = lowSrc; - } else { - src = fallbackSrc; + AFRAME.ANode.prototype.load.call(this, () => { + // Check if entity was detached while it was waiting to load. + if (!this.parentEl) { + return; } - } - const onLoad = gltfModel => { - if (!GLTFCache[src]) { - // Store a cloned copy of the gltf model. - GLTFCache[src] = cloneGltf(gltfModel); + this.updateComponents(); + if (this.isScene || this.parentEl.isPlaying) { + this.play(); + } + }); + } + }, + + setSrc: { + async value(src) { + try { + // If the src attribute is a selector, get the url from the asset item. + if (src.charAt(0) === "#") { + const assetEl = document.getElementById(src.substring(1)); + + const fallbackSrc = assetEl.getAttribute("src"); + const highSrc = assetEl.getAttribute("high-src"); + const lowSrc = assetEl.getAttribute("low-src"); + + if (highSrc && window.APP.quality === "high") { + src = highSrc; + } else if (lowSrc && window.APP.quality === "low") { + src = lowSrc; + } else { + src = fallbackSrc; + } } - this.model = gltfModel.scene || gltfModel.scenes[0]; - this.model.animations = gltfModel.animations; - - this.setObject3D("mesh", this.model); - this.emit("model-loaded", { format: "gltf", model: this.model }); + if (src === this.lastSrc) return; + this.lastSrc = src; - if (this.getAttribute("inflate")) { - inflate(this.model, finalizeLoad); - } else { - finalizeLoad(); - } - }; + const model = await cachedLoadGLTF(src); - const inflate = (model, callback) => { - inflateEntities("", this, model); - this.querySelectorAll(":scope > template").forEach(attachTemplate); + // If we started loading something else already + // TODO: there should be a way to cancel loading instead + if (src != this.lastSrc) return; - // Wait one tick for the appended custom elements to be connected before calling finalizeLoad - setTimeout(callback, 0); - }; + // If we had inflated something already before, clean that up + if (this.inflatedEl) { + this.inflatedEl.parentNode.removeChild(this.inflatedEl); + delete this.inflatedEl; + } - const finalizeLoad = () => { - AFRAME.ANode.prototype.load.call(this, () => { - // Check if entity was detached while it was waiting to load. - if (!this.parentEl) { - return; - } + this.model = model.scene || model.scenes[0]; + this.model.animations = model.animations; - this.updateComponents(); - if (this.isScene || this.parentEl.isPlaying) { - this.play(); - } - }); - }; + this.setObject3D("mesh", this.model); - // Load the gltf model from the cache if it exists. - const gltf = GLTFCache[src]; + if (this.getAttribute("inflate")) { + this.inflatedEl = inflateEntities(this, this.model); + this.querySelectorAll(":scope > template").forEach(attachTemplate); + } - if (gltf) { - // Use a cloned copy of the cached model. - const clonedGltf = cloneGltf(gltf); - onLoad(clonedGltf); - return; + this.emit("model-loaded", { format: "gltf", model: this.model }); + } catch (e) { + const message = (e && e.message) || "Failed to load glTF model"; + console.error(message); + this.emit("model-error", { format: "gltf", src }); } + } + }, - // Otherwise load the new gltf model. - new THREE.GLTFLoader().load(src, onLoad, undefined /* onProgress */, error => { - // On glTF load error + attributeChangedCallback: { + value(attr, oldVal, newVal) { + if (attr === "src") { + this.setSrc(newVal); + } + AFRAME.AEntity.prototype.attributeChangedCallback.call(this, attr, oldVal, newVal); + } + }, - const message = error && error.message ? error.message : "Failed to load glTF model"; - console.warn(message); - this.emit("model-error", { format: "gltf", src }); - }); + setAttribute: { + value(attr, arg1, arg2) { + if (attr === "src") { + this.setSrc(arg1); + } + AFRAME.AEntity.prototype.setAttribute.call(this, attr, arg1, arg2); } } }) diff --git a/src/room.js b/src/room.js index 5af49f9b9..849cd012c 100644 --- a/src/room.js +++ b/src/room.js @@ -40,6 +40,7 @@ import "./components/layers"; import "./components/spawn-controller"; import "./components/animated-robot-hands"; import "./components/hide-when-quality"; +import "./components/debug"; import "./systems/personal-space-bubble"; -- GitLab