diff --git a/src/assets/avatars/Bot_SkinnedWithAnim.fbx b/src/assets/avatars/Bot_SkinnedWithAnim.fbx new file mode 100755 index 0000000000000000000000000000000000000000..38bfe3673af74de3599aadb3fc473cd2196ee820 Binary files /dev/null and b/src/assets/avatars/Bot_SkinnedWithAnim.fbx differ diff --git a/src/assets/avatars/Bot_SkinnedWithAnim.glb b/src/assets/avatars/Bot_SkinnedWithAnim.glb new file mode 100755 index 0000000000000000000000000000000000000000..5531da9cf237d1de307ec227c400a7f6e9b80d12 Binary files /dev/null and b/src/assets/avatars/Bot_SkinnedWithAnim.glb differ diff --git a/src/components/hand-controls2.js b/src/components/hand-controls2.js index 28b0062358ef463815cadc2e1d38fc8fb73bfec3..655bb54cf639bd22e15ccd2d6fbe7ded3ebb61b9 100644 --- a/src/components/hand-controls2.js +++ b/src/components/hand-controls2.js @@ -60,10 +60,7 @@ AFRAME.registerComponent("hand-controls2", { this.onControllerDisconnected = this.onControllerDisconnected.bind(this); el.addEventListener("controllerconnected", this.onControllerConnected); - el.addEventListener( - "controllerdisconnected", - this.onControllerDisconnected - ); + el.addEventListener("controllerdisconnected", this.onControllerDisconnected); el.setAttribute("visible", false); }, @@ -80,10 +77,7 @@ AFRAME.registerComponent("hand-controls2", { pause() { const el = this.el; - el.removeEventListener( - "middle_ring_pinky_down", - this.onMiddleRingPinkyDown - ); + el.removeEventListener("middle_ring_pinky_down", this.onMiddleRingPinkyDown); el.removeEventListener("middle_ring_pinky_up", this.onMiddleRingPinkyUp); el.removeEventListener("thumb_down", this.onThumbDown); el.removeEventListener("thumb_up", this.onThumbUp); @@ -113,10 +107,7 @@ AFRAME.registerComponent("hand-controls2", { remove() { const el = this.el; el.removeEventListener("controllerconnected", this.onControllerConnected); - el.removeEventListener( - "controllerdisconnected", - this.onControllerDisconnected - ); + el.removeEventListener("controllerdisconnected", this.onControllerDisconnected); }, updateGesture(nextFingersDown) { @@ -124,9 +115,9 @@ AFRAME.registerComponent("hand-controls2", { const gesture = this.determineGesture(); if (gesture !== this.gesture) { + var previous = this.gesture; this.gesture = gesture; - this.el.emit(this.last + "end"); - this.el.emit(this.gesture + "start"); + this.el.emit("hand-gesture", { previous: previous, current: this.gesture }); } }, diff --git a/src/components/robot-hand-gestures.js b/src/components/robot-hand-gestures.js new file mode 100644 index 0000000000000000000000000000000000000000..6715fe531460544b866dff45489bd83d9b220cd4 --- /dev/null +++ b/src/components/robot-hand-gestures.js @@ -0,0 +1,86 @@ +// Global THREE, AFRAME +const robotGestures = { + open: "pinch", //TODO + point: "point", + pointThumb: "point", + fist: "grip", + hold: "grip", + thumbUp: "thumbup" +}; + +AFRAME.registerComponent("robot-hand-gestures", { + schema: { + leftHand: { type: "selector", default: "#left-hand" }, + rightHand: { type: "selector", default: "#right-hand" } + }, + + init: function() { + this.playAnimation = this.playAnimation.bind(this); + this.onModelLoaded = this.onModelLoaded.bind(this); + this.el.addEventListener("model-loaded", this.onModelLoaded); + }, + + onModelLoaded: function() { + var root = this.el.object3D.children[0].children[0].children[0]; // The "Root Scene" in threejs land of type "Scene" + console.log(this.el); + this.mixer = new THREE.AnimationMixer(root); + this.loaded = true; + }, + + play: function() { + this.data.leftHand.addEventListener("hand-gesture", this.playAnimation); + this.data.rightHand.addEventListener("hand-gesture", this.playAnimation); + }, + + pause: function() { + this.data.leftHand.removeEventListener("hand-gesture", this.playAnimation); + this.data.rightHand.removeEventListener("hand-gesture", this.playAnimation); + }, + + tick: function(_t, dt) { + if (!this.loaded) return; + this.mixer.update(dt * 0.001); + }, + + /** + * Play hand animation based on button state. + * + * @param {string} gesture - Name of the animation as specified by the model. + * @param {string} lastGesture - Previous pose. + */ + playAnimation: function(evt) { + const { current, previous } = evt.detail; + var fromAction; + var toAction; + var mixer = this.mixer; + const suffix = evt.target === this.data.leftHand ? "_L" : "_R"; + const from = robotGestures[previous] + suffix; + const to = robotGestures[current] + suffix; + + // Grab clip action. + toAction = mixer.clipAction(to); + toAction.clampWhenFinished = true; + // toAction.loop = THREE.LoopRepeat; + toAction.repetitions = 0; + toAction.weight = 1; + + // No gesture to gesture or gesture to no gesture. + if (!previous || current === previous) { + // Stop all current animations. + mixer.stopAllAction(); + + // Play animation. + toAction.play(); + return; + } + + // Animate or crossfade from gesture to gesture. + + fromAction = mixer.clipAction(from); + mixer.stopAllAction(); + fromAction.weight = 0.15; + fromAction.play(); + toAction.play(); + fromAction.crossFadeTo(toAction, 0.15, true); + } +}); diff --git a/src/room.js b/src/room.js index d6e4fe75eac02e0ff34812713c9663277d303970..2138bf934bb49149d797da2880ad59dd75f0b7c4 100644 --- a/src/room.js +++ b/src/room.js @@ -37,6 +37,7 @@ import "./components/water"; import "./components/skybox"; import "./components/layers"; import "./components/spawn-controller"; +import "./components/robot-hand-gestures"; import "./systems/personal-space-bubble"; import { promptForName, getCookie, parseJwt } from "./utils/identity"; @@ -45,10 +46,7 @@ import { inGameActions, config } from "./input-mappings"; import registerTelemetry from "./telemetry"; AFRAME.registerInputBehaviour("vive_trackpad_dpad4", vive_trackpad_dpad4); -AFRAME.registerInputBehaviour( - "oculus_touch_joystick_dpad4", - oculus_touch_joystick_dpad4 -); +AFRAME.registerInputBehaviour("oculus_touch_joystick_dpad4", oculus_touch_joystick_dpad4); AFRAME.registerInputActivator("pressedmove", PressedMove); AFRAME.registerInputActivator("reverseY", ReverseY); AFRAME.registerInputActions(inGameActions, "default"); @@ -85,10 +83,7 @@ window.App = { const scene = document.querySelector("a-scene"); scene.setAttribute("networked-scene", { - room: - qs.room && !isNaN(parseInt(qs.room)) - ? parseInt(qs.room) - : window.CONFIG.default_room, + room: qs.room && !isNaN(parseInt(qs.room)) ? parseInt(qs.room) : window.CONFIG.default_room, serverURL: window.CONFIG.janus_server_url }); @@ -122,10 +117,7 @@ window.App = { const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true, - video: - qs.screen === "true" - ? { mediaSource: "screen", height: 720, frameRate: 30 } - : false + video: qs.screen === "true" ? { mediaSource: "screen", height: 720, frameRate: 30 } : false }); // Don't send video by deafult diff --git a/templates/room.hbs b/templates/room.hbs index 63c3f90db866b36c6042a05e10ae0a39befa8998..fffd1373bd908c2eec5d11e34d42d0c7dbec47ba 100644 --- a/templates/room.hbs +++ b/templates/room.hbs @@ -48,6 +48,7 @@ <a-asset-item id="bot-body-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_Body_Mesh.glb" }}"></a-asset-item> <a-asset-item id="bot-left-hand-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_LeftHand_Mesh.glb" }}"></a-asset-item> <a-asset-item id="bot-right-hand-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_RightHand_Mesh.glb"}}"></a-asset-item> + <a-asset-item id="bot-whole-body-mesh" response-type="arraybuffer" src="{{asset "assets/avatars/Bot_SkinnedWithAnim.glb"}}"></a-asset-item> <a-asset-item id="watch-model" response-type="arraybuffer" src="{{asset "assets/hud/watch.glb"}}"></a-asset-item> @@ -82,6 +83,14 @@ position="0 -0.05 0" ></a-entity> </script> + <script id="body-template2" type="text/html"> + <a-entity + class="body" + cached-gltf-model="#bot-whole-body-mesh" + rotation="0 180 0" + position="0 -0.05 0" + ></a-entity> + </script> <script id="left-hand-template" type="text/html"> <a-entity @@ -128,17 +137,30 @@ > <a-entity id="head" - camera="userHeight: 1.6" personal-space-bubble - look-controls networked="template: #head-template; showLocalTemplate: false;" - ></a-entity> + position ="0 1.6 0" + > + <a-entity + id="third-person-cam" + camera="" + look-controls + position ="0 2 -1" + > + </a-entity> + </a-entity> <a-entity id="body" - body-controller="eyeNeckOffset: 0 -0.11 0.09; neckHeight: 0.05" + body-controller="camera: #head; eyeNeckOffset: 0 -0.11 0.09; neckHeight: 0.05" networked="template: #body-template;" ></a-entity> + <a-entity + id="body2" + body-controller="camera: #head; eyeNeckOffset: 0 -0.11 0.09; neckHeight: 0.05" + networked="template: #body-template2;" + robot-hand-gestures="leftHand: #left-hand; rightHand: #right-hand" + ></a-entity> <a-entity id="nametag" networked="template: #nametag-template; showLocalTemplate: false;"