diff --git a/public/room.html b/public/room.html index 85f93514a863761945a8a6564fc24d31d4cfec1e..03c8dd6a33c4e9e6e095aa9d195f8243e2759df9 100644 --- a/public/room.html +++ b/public/room.html @@ -50,10 +50,10 @@ <script id="head-template" type="text/html"> <a-entity class="head" - gltf-model2="#bot-head-mesh" networked-audio-source networked-audio-analyser matcolor-audio-feedback="objectName: Head_Mesh" + cached-gltf-model="#bot-head-mesh" scale-audio-feedback personal-space-invader rotation="0 180 0" @@ -64,7 +64,7 @@ <script id="body-template" type="text/html"> <a-entity class="body" - gltf-model2="#bot-body-mesh" + cached-gltf-model="#bot-body-mesh" personal-space-invader rotation="0 180 0" position="0 -0.05 0" @@ -74,7 +74,7 @@ <script id="left-hand-template" type="text/html"> <a-entity class="hand" - gltf-model2="#bot-left-hand-mesh" + cached-gltf-model="#bot-left-hand-mesh" animation-mixer personal-space-invader rotation="-90 90 0" @@ -85,7 +85,7 @@ <script id="right-hand-template" type="text/html"> <a-entity class="hand" - gltf-model2="#bot-right-hand-mesh" + cached-gltf-model="#bot-right-hand-mesh" personal-space-invader rotation="-90 -90 0" position="0 0 0.075" @@ -144,7 +144,7 @@ > <a-entity id="watch" - gltf-model2="assets/hud/watch.gltf" + cached-gltf-model="assets/hud/watch.gltf" position="0 0.0015 0.147" rotation="3.5 0 0" > @@ -175,17 +175,17 @@ <!-- Environment --> <a-entity - gltf-model2="#meeting-space1-mesh" + cached-gltf-model="#meeting-space1-mesh" position="0 0 0" ></a-entity> <a-entity - gltf-model2="#outdoor-facade-mesh" + cached-gltf-model="#outdoor-facade-mesh" position="0 0 0" ></a-entity> <a-entity - gltf-model2="#floor-nav-mesh" + cached-gltf-model="#floor-nav-mesh" visible="false" position="0 0 0" ></a-entity> diff --git a/src/components/cached-gltf-model.js b/src/components/cached-gltf-model.js new file mode 100644 index 0000000000000000000000000000000000000000..c50b6788228ddb2a79beaa9202ac50be6a75adcf --- /dev/null +++ b/src/components/cached-gltf-model.js @@ -0,0 +1,126 @@ +import "../vendor/GLTFLoader"; + +const GLTFCache = {}; + +// From https://gist.github.com/cdata/f2d7a6ccdec071839bc1954c32595e87 +// Tracking glTF cloning here: https://github.com/mrdoob/three.js/issues/11573 +function cloneGltf(gltf) { + const clone = { + animations: gltf.animations, + scene: gltf.scene.clone(true) + }; + + const skinnedMeshes = {}; + + gltf.scene.traverse(node => { + if (node.isSkinnedMesh) { + skinnedMeshes[node.name] = node; + } + }); + + const cloneBones = {}; + const cloneSkinnedMeshes = {}; + + clone.scene.traverse(node => { + if (node.isBone) { + cloneBones[node.name] = node; + } + + if (node.isSkinnedMesh) { + cloneSkinnedMeshes[node.name] = node; + } + }); + + for (const name in skinnedMeshes) { + const skinnedMesh = skinnedMeshes[name]; + const skeleton = skinnedMesh.skeleton; + const cloneSkinnedMesh = cloneSkinnedMeshes[name]; + + const orderedCloneBones = []; + + for (let i = 0; i < skeleton.bones.length; ++i) { + const cloneBone = cloneBones[skeleton.bones[i].name]; + orderedCloneBones.push(cloneBone); + } + + cloneSkinnedMesh.bind( + new THREE.Skeleton(orderedCloneBones, skeleton.boneInverses), + cloneSkinnedMesh.matrixWorld + ); + + cloneSkinnedMesh.material = skinnedMesh.material.clone(); + } + + return clone; +} + +/** + * glTF model loader. + */ +AFRAME.registerComponent("cached-gltf-model", { + schema: { type: "model" }, + + init: function() { + this.model = null; + this.onLoad = this.onLoad.bind(this); + this.onError = this.onError.bind(this); + }, + + update: function() { + const self = this; + const el = this.el; + const src = this.data; + + if (!src) { + return; + } + + // Remove any existing model + this.remove(); + + // Load the gltf model from the cache if it exists. + const gltf = GLTFCache[src]; + + if (gltf) { + // Use a cloned copy of the cached model. + const clonedGltf = cloneGltf(gltf); + this.onLoad(clonedGltf); + return; + } + + // Otherwise load the new gltf model. + new THREE.GLTFLoader().load( + src, + this.onLoad, + undefined /* onProgress */, + this.onError + ); + }, + + onLoad(gltfModel) { + if (!GLTFCache[this.data]) { + // Store a cloned copy of the gltf model. + GLTFCache[this.data] = cloneGltf(gltfModel); + } + + this.model = gltfModel.scene || gltfModel.scenes[0]; + this.model.animations = gltfModel.animations; + + this.el.setObject3D("mesh", this.model); + this.el.emit("model-loaded", { format: "gltf", model: this.model }); + }, + + onError(error) { + const message = + error && error.message ? error.message : "Failed to load glTF model"; + console.warn(message); + this.el.emit("model-error", { format: "gltf", src: this.data }); + }, + + remove: function() { + if (!this.model) { + return; + } + this.el.removeObject3D("mesh"); + } +}); diff --git a/src/components/gltf-model2.js b/src/components/gltf-model2.js deleted file mode 100644 index 868c1a4604e0fcca36eec7f9ef5382bd1863c7e0..0000000000000000000000000000000000000000 --- a/src/components/gltf-model2.js +++ /dev/null @@ -1,68 +0,0 @@ -import "../vendor/GLTFLoader"; - -const GLTFCache = {}; - -/** - * glTF model loader. - */ -AFRAME.registerComponent("gltf-model2", { - schema: { type: "model" }, - - init: function() { - this.model = null; - this.loader = new THREE.GLTFLoader(); - this.onLoad = this.onLoad.bind(this); - this.onError = this.onError.bind(this); - }, - - update: function() { - const self = this; - const el = this.el; - const src = this.data; - - if (!src) { - return; - } - - const cachedModel = GLTFCache[src]; - - if (cachedModel) { - this.model = cachedModel.clone(true); - this.model.visible = true; - this.model.animations = cachedModel.animations; - this.el.setObject3D("mesh", this.model); - this.el.emit("model-loaded", { format: "gltf", model: this.model }); - } - - this.remove(); - - this.loader.load( - src, - this.onLoad, - undefined /* onProgress */, - this.onError - ); - }, - - onLoad(gltfModel) { - this.model = gltfModel.scene || gltfModel.scenes[0]; - this.model.animations = gltfModel.animations; - GLTFCache[this.data] = this.model; - this.el.setObject3D("mesh", this.model); - this.el.emit("model-loaded", { format: "gltf", model: this.model }); - }, - - onError(error) { - const message = - error && error.message ? error.message : "Failed to load glTF model"; - console.warn(message); - this.el.emit("model-error", { format: "gltf", src: this.data }); - }, - - remove: function() { - if (!this.model) { - return; - } - this.el.removeObject3D("mesh"); - } -}); diff --git a/src/index.js b/src/index.js index 29948633419588c3d441f456fac77b25b3d935ce..a39a9762915693ada0adf431f655b3b71f0379f3 100644 --- a/src/index.js +++ b/src/index.js @@ -22,7 +22,7 @@ import "./components/character-controller"; import "./components/split-axis-events"; import "./components/networked-video-player"; import "./components/offset-relative-to"; -import "./components/gltf-model2"; +import "./components/cached-gltf-model"; import "./systems/personal-space-bubble"; import registerNetworkScheams from "./network-schemas";