diff --git a/package.json b/package.json index 7309a9d77dad1954fa1e49254de2fc4957d2bbdc..190ff318264873ec03c11b0744566b2ac1935502 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ }, "dependencies": { "aframe-billboard-component": "^1.0.0", - "aframe-extras": "^3.12.4", "aframe-input-mapping-component": "https://github.com/johnshaughnessy/aframe-input-mapping-component#feature/map-to-array", "aframe-teleport-controls": "https://github.com/netpro2k/aframe-teleport-controls#feature/teleport-origin", "aframe-xr": "github:brianpeiris/aframe-xr#3162aed", diff --git a/src/assets/avatars/BotDefault_Avatar.glb b/src/assets/avatars/BotDefault_Avatar.glb index ccb77bf21362f3a5b2058f59eafd56fa17a55ec4..0784e7354e0fe2e35b3df8329c5587616203b7c3 100644 Binary files a/src/assets/avatars/BotDefault_Avatar.glb and b/src/assets/avatars/BotDefault_Avatar.glb differ diff --git a/src/assets/avatars/BotDefault_Avatar_Unlit.glb b/src/assets/avatars/BotDefault_Avatar_Unlit.glb index ba33589d4c42fe6bf9756d5164f331e9830dab91..850f7552c4183553779dfe0776fb92f042b7f8ca 100644 Binary files a/src/assets/avatars/BotDefault_Avatar_Unlit.glb and b/src/assets/avatars/BotDefault_Avatar_Unlit.glb differ diff --git a/src/assets/avatars/Bot_SingleSkin_Rigged.glb b/src/assets/avatars/Bot_SingleSkin_Rigged.glb deleted file mode 100755 index 468db3fd21422c94a19c67fc3fd5e0d4e7b8e447..0000000000000000000000000000000000000000 Binary files a/src/assets/avatars/Bot_SingleSkin_Rigged.glb and /dev/null differ diff --git a/src/components/animated-robot-hands.js b/src/components/animated-robot-hands.js index ef7b429d177864cc4d78a907f333e5e38411d7f9..1b26402848a8cb641560d76307708c4d77d53352 100644 --- a/src/components/animated-robot-hands.js +++ b/src/components/animated-robot-hands.js @@ -13,24 +13,25 @@ const POSES = { // TODO: When we have analog values of index-finger triggers or middle-finger grips, // it would be nice to animate the hands proportionally to those analog values. AFRAME.registerComponent("animated-robot-hands", { + dependencies: ["animation-mixer"], schema: { leftHand: { type: "selector", default: "#player-left-controller" }, rightHand: { type: "selector", default: "#player-right-controller" } }, init: function() { - window.hands = this; this.playAnimation = this.playAnimation.bind(this); - // Get the three.js object in the scene graph that has the animation data - const root = this.el.querySelector("a-gltf-entity .RootScene").object3D.children[0]; - this.mixer = new THREE.AnimationMixer(root); - this.root = root; + this.mixer = this.el.components["animation-mixer"].mixer; - // Set hands to open pose because the bind pose is funky due + const object3DMap = this.el.object3DMap; + const rootObj = object3DMap.mesh || object3DMap.scene; + this.clipActionObject = rootObj.parent; + + // Set hands to open pose because the bind pose is funky dues // to the workaround for FBX2glTF animations. - this.openL = this.mixer.clipAction(POSES.open + "_L", root.parent); - this.openR = this.mixer.clipAction(POSES.open + "_R", root.parent); + this.openL = this.mixer.clipAction(POSES.open + "_L", this.clipActionObject); + this.openR = this.mixer.clipAction(POSES.open + "_R", this.clipActionObject); this.openL.play(); this.openR.play(); }, @@ -45,10 +46,6 @@ AFRAME.registerComponent("animated-robot-hands", { this.data.rightHand.removeEventListener("hand-pose", this.playAnimation); }, - tick: function(t, dt) { - this.mixer.update(dt / 1000); - }, - // Animate from pose to pose. // TODO: Transition from current pose (which may be BETWEEN two other poses) // to the target pose, rather than stopping previous actions altogether. @@ -81,8 +78,8 @@ AFRAME.registerComponent("animated-robot-hands", { // console.log( // `Animating ${isLeft ? "left" : "right"} hand from ${prevPose} to ${currPose} over ${duration} seconds.` // ); - const from = mixer.clipAction(prevPose, this.root.parent); - const to = mixer.clipAction(currPose, this.root.parent); + const from = mixer.clipAction(prevPose, this.clipActionObject); + const to = mixer.clipAction(currPose, this.clipActionObject); from.fadeOut(duration); to.fadeIn(duration); to.play(); diff --git a/src/components/animation-mixer.js b/src/components/animation-mixer.js new file mode 100644 index 0000000000000000000000000000000000000000..791854f1eab2b6f30f20f094b9fb9a35c3ecb9f2 --- /dev/null +++ b/src/components/animation-mixer.js @@ -0,0 +1,36 @@ +AFRAME.registerComponent("animation-mixer", { + init() { + this.mixer = null; + + const object3DMap = this.el.object3DMap; + const rootObject3D = object3DMap.mesh || object3DMap.scene; + + if (rootObject3D) { + this.setAnimationMixer(rootObject3D); + } else { + this.onModelLoaded = this.onModelLoaded.bind(this); + this.el.addEventListener("model-loaded", this.onModelLoaded); + } + }, + + onModelLoaded(event) { + const sceneObject3D = event.detail.model; + this.setAnimationMixer(sceneObject3D); + + this.el.removeEventListener(this.onModelLoaded); + }, + + setAnimationMixer(rootObject3D) { + this.mixer = new THREE.AnimationMixer(rootObject3D); + }, + + tick: function(t, dt) { + if (this.mixer) { + this.mixer.update(dt / 1000); + } + }, + + destroy() { + this.el.removeEventListener(this.onModelLoaded); + } +}); diff --git a/src/components/loop-animation.js b/src/components/loop-animation.js new file mode 100644 index 0000000000000000000000000000000000000000..09a9e9dafabf8c05c7f13a02a9ecb820018ef573 --- /dev/null +++ b/src/components/loop-animation.js @@ -0,0 +1,58 @@ +AFRAME.registerComponent("loop-animation", { + dependencies: ["animation-mixer"], + schema: { + clip: { type: "string", required: true } + }, + init() { + const object3DMap = this.el.object3DMap; + this.model = object3DMap.mesh || object3DMap.scene; + + if (this.model) { + this.mixer = this.el.components["animation-mixer"].mixer; + } else { + this.onModelLoaded = this.onModelLoaded.bind(this); + this.el.addEventListener("model-loaded", this.onModelLoaded); + } + }, + + onModelLoaded(event) { + const animationMixerComponent = this.el.components["animation-mixer"]; + this.model = event.detail.model; + this.mixer = animationMixerComponent.mixer; + + this.updateClipState(true); + + this.el.removeEventListener(this.onModelLoaded); + }, + + update(oldData) { + if (oldData.clip !== this.data.clip && this.model) { + this.updateClipState(true); + } + }, + + updateClipState(play) { + const model = this.model; + const clipName = this.data.clip; + + for (const clip of this.model.animations) { + if (clip.name === clipName) { + const action = this.mixer.clipAction(clip, model.parent); + + if (play) { + action.enabled = true; + action.setLoop(THREE.LoopRepeat, Infinity).play(); + } else { + action.stop(); + } + + break; + } + } + }, + + destroy() { + this.updateClipState(false); + this.el.removeEventListener(this.onModelLoaded); + } +}); diff --git a/src/gltf-component-mappings.js b/src/gltf-component-mappings.js index e73692a8ff9270985f3b533ff8bd1f8fdc0cb835..e4cf086733ad1e0dc547abafe3fb37ed025a5608 100644 --- a/src/gltf-component-mappings.js +++ b/src/gltf-component-mappings.js @@ -1,3 +1,4 @@ import "./elements/a-gltf-entity"; AFRAME.AGLTFEntity.registerComponent("scale-audio-feedback", "scale-audio-feedback"); +AFRAME.AGLTFEntity.registerComponent("loop-animation", "loop-animation"); diff --git a/src/room.html b/src/room.html index bf25b9b22a87d6c4702546bba5abac4e179f2709..96e34430192a2c41611f27eaf4cf1a2a5799d4d4 100644 --- a/src/room.html +++ b/src/room.html @@ -58,6 +58,10 @@ <a-entity class="right-controller"></a-entity> <a-gltf-entity src="#bot-skinned-mesh" inflate="true" ik-controller > + <template data-selector=".RootScene"> + <a-entity animation-mixer ></a-entity> + </template> + <template data-selector=".Neck"> <a-entity> <a-entity @@ -100,7 +104,6 @@ wasd-to-analog2d character-controller="pivot: #player-camera" ik-root - animated-robot-hands > <a-entity id="player-camera" @@ -129,7 +132,11 @@ haptic-feedback ></a-entity> - <a-gltf-entity src="#bot-skinned-mesh" inflate="true" ik-controller> + <a-gltf-entity src="#bot-skinned-mesh" inflate="true" ik-controller > + <template data-selector=".RootScene"> + <a-entity animation-mixer animated-robot-hands ></a-entity> + </template> + <template data-selector=".Neck"> <a-entity> <a-entity class="nametag" visible="false" text ></a-entity> diff --git a/src/room.js b/src/room.js index 5af49f9b95507ad437d018af44d85251b425ed34..9e5f4aed820d53f372bdb842ddaa48be8fd71657 100644 --- a/src/room.js +++ b/src/room.js @@ -13,9 +13,6 @@ import "aframe-input-mapping-component"; import "aframe-billboard-component"; import "webrtc-adapter"; -import animationMixer from "aframe-extras/src/loaders/animation-mixer"; -AFRAME.registerComponent("animation-mixer", animationMixer); - import { vive_trackpad_dpad4 } from "./behaviours/vive-trackpad-dpad4"; import { oculus_touch_joystick_dpad4 } from "./behaviours/oculus-touch-joystick-dpad4"; import { PressedMove } from "./activators/pressedmove"; @@ -40,6 +37,8 @@ import "./components/layers"; import "./components/spawn-controller"; import "./components/animated-robot-hands"; import "./components/hide-when-quality"; +import "./components/animation-mixer"; +import "./components/loop-animation"; import "./systems/personal-space-bubble"; diff --git a/yarn.lock b/yarn.lock index 7a240ecf65a49d83a87c51db8a9056bb9af94b1a..7284e9761b90246d2c0f0981f68334d756cc9de6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44,13 +44,6 @@ aframe-billboard-component@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/aframe-billboard-component/-/aframe-billboard-component-1.0.0.tgz#10ce2482729eef7386c5844d65917581a62d3adc" -aframe-extras@^3.12.4: - version "3.13.1" - resolved "https://registry.yarnpkg.com/aframe-extras/-/aframe-extras-3.13.1.tgz#f8b6ef18c29e92538d05d94913640942a307c46c" - dependencies: - aframe-physics-system "^1.4.3" - three-pathfinding "^0.2.2" - "aframe-input-mapping-component@https://github.com/johnshaughnessy/aframe-input-mapping-component#feature/map-to-array": version "0.1.2" resolved "https://github.com/johnshaughnessy/aframe-input-mapping-component#4c7e493ad6c4a25eef27d32551c94d8b78541191" @@ -61,13 +54,6 @@ aframe-lerp-component@^1.1.0: dependencies: almost-equal "^1.1.0" -aframe-physics-system@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/aframe-physics-system/-/aframe-physics-system-1.4.3.tgz#c6927e847081bfe546658314aa4c04958ef27934" - dependencies: - cannon "github:donmccurdy/cannon.js#v0.6.2-dev1" - three-to-cannon "^1.1.1" - "aframe-teleport-controls@https://github.com/netpro2k/aframe-teleport-controls#feature/teleport-origin": version "0.3.0" resolved "https://github.com/netpro2k/aframe-teleport-controls#41fe311d3123503ba44761acce69d0f0634139cc" @@ -1360,10 +1346,6 @@ caniuse-lite@^1.0.30000792: version "1.0.30000810" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000810.tgz#47585fffce0e9f3593a6feea4673b945424351d9" -"cannon@github:donmccurdy/cannon.js#v0.6.2-dev1": - version "0.6.2" - resolved "https://codeload.github.com/donmccurdy/cannon.js/tar.gz/022e8ba53fa83abf0ad8a0e4fd08623123838a17" - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -6443,14 +6425,6 @@ textextensions@2: version "2.2.0" resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286" -three-pathfinding@^0.2.2: - version "0.2.3" - resolved "https://registry.yarnpkg.com/three-pathfinding/-/three-pathfinding-0.2.3.tgz#469bb26fb6b331f536c9ec88fde78e9c9219f637" - -three-to-cannon@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/three-to-cannon/-/three-to-cannon-1.2.0.tgz#92b9a756a270851aa98c3058c51ef15891507c01" - through2@^2.0.0, through2@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"