import { findAncestorWithComponent } from "../utils/scene-graph"; const POSES = { open: "allOpen", thumbDown: "thumbDown", indexDown: "indexDown", mrpDown: "mrpDown", thumbUp: "thumbsUp", point: "point", fist: "allGrip", pinch: "pinch" }; const NETWORK_POSES = ["allOpen", "thumbDown", "indexDown", "mrpDown", "thumbsUp", "point", "allGrip", "pinch"]; /** * Animates between poses based on networked pose state using an animation mixer. * @namespace avatar * @component hand-pose */ AFRAME.registerComponent("hand-pose", { multiple: true, init() { this.pose = 0; this.animatePose = this.animatePose.bind(this); const mixerEl = findAncestorWithComponent(this.el, "animation-mixer"); this.mixer = mixerEl.components["animation-mixer"].mixer; const suffix = this.id == "left" ? "_L" : "_R"; this.from = this.to = this.mixer.clipAction(POSES.open + suffix); this.from.play(); const getNetworkedAvatar = el => { const networkedAvatar = el.components["networked-avatar"]; if (networkedAvatar) { return networkedAvatar; } return getNetworkedAvatar(el.parentEl); }; this.networkedAvatar = getNetworkedAvatar(this.el); }, tick() { if ( !this.networkedAvatar || !this.networkedAvatar.data || this.networkedAvatar.data[`${this.id}_hand_pose`] === this.pose ) { return; } this.animatePose(NETWORK_POSES[this.pose], NETWORK_POSES[this.networkedAvatar.data[`${this.id}_hand_pose`]]); this.pose = this.networkedAvatar.data[`${this.id}_hand_pose`]; }, animatePose(prev, curr) { this.from.stop(); this.to.stop(); const duration = 0.065; const suffix = this.id == "left" ? "_L" : "_R"; this.from = this.mixer.clipAction(prev + suffix); this.to = this.mixer.clipAction(curr + suffix); this.from.fadeOut(duration); this.to.fadeIn(duration); this.to.play(); this.from.play(); this.mixer.update(0.001); } }); /** * Sets the networked hand pose state based on hand-pose events. * @namespace avatar * @component hand-pose-controller */ AFRAME.registerComponent("hand-pose-controller", { multiple: true, schema: { eventSrc: { type: "selector" }, networkedAvatar: { type: "selector" } }, init: function() { this.setHandPose = this.setHandPose.bind(this); }, play: function() { this.data.eventSrc.addEventListener("hand-pose", this.setHandPose); }, pause: function() { this.data.eventSrc.removeEventListener("hand-pose", this.setHandPose); }, setHandPose: function(evt) { this.data.networkedAvatar.setAttribute( "networked-avatar", `${this.id}_hand_pose`, NETWORK_POSES.indexOf(POSES[evt.detail.current]) ); } });