From a0f21228f689af54c1b0b210fe116ed1ecd32aa2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 9 Nov 2015 12:19:01 -0800 Subject: [PATCH 01/84] AnimGraph support for start and stop animation from JavaScript Follows the same model as the existing startAnimation and stopAnimation calls. See kneel.js for an example. --- examples/kneel.js | 89 ++ .../defaultAvatar_full/avatar-animation.json | 1158 +++++++++-------- libraries/animation/src/AnimClip.h | 7 +- libraries/animation/src/AnimUtil.cpp | 11 +- libraries/animation/src/Rig.cpp | 92 +- libraries/animation/src/Rig.h | 41 +- 6 files changed, 807 insertions(+), 591 deletions(-) create mode 100644 examples/kneel.js diff --git a/examples/kneel.js b/examples/kneel.js new file mode 100644 index 0000000000..2ba029677a --- /dev/null +++ b/examples/kneel.js @@ -0,0 +1,89 @@ +// +// kneel.js +// examples +// +// Created by Anthony Thibault on 11/9/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// Example of how to play an animation on an avatar. +// + +var buttonImageUrl = "https://s3.amazonaws.com/hifi-public/images/tools/kneel.svg"; +var windowDimensions = Controller.getViewportDimensions(); + +var buttonWidth = 37; +var buttonHeight = 46; +var buttonPadding = 10; + +var buttonPositionX = windowDimensions.x - buttonPadding - buttonWidth; +var buttonPositionY = (windowDimensions.y - buttonHeight) / 2 - (buttonHeight + buttonPadding); + +var kneelDownImageOverlay = { + x: buttonPositionX, + y: buttonPositionY, + width: buttonWidth, + height: buttonHeight, + subImage: { x: 0, y: buttonHeight, width: buttonWidth, height: buttonHeight }, + imageURL: buttonImageUrl, + visible: true, + alpha: 1.0 +}; + +var standUpImageOverlay = { + x: buttonPositionX, + y: buttonPositionY, + width: buttonWidth, + height: buttonHeight, + subImage: { x: buttonWidth, y: buttonHeight, width: buttonWidth, height: buttonHeight }, + imageURL: buttonImageUrl, + visible: false, + alpha: 1.0 +}; + +var kneelDownButton = Overlays.addOverlay("image", kneelDownImageOverlay); +var standUpButton = Overlays.addOverlay("image", standUpImageOverlay); +var kneeling = false; + +var KNEEL_ANIM_URL = "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx"; + +function kneelDown() { + kneeling = true; + + var playbackRate = 30; // 30 fps is normal speed. + var priority = 0; // obsolete + var loopFlag = false; + var holdFlag = false; // obsolete + var startFrame = 0; + var endFrame = 82; + var maskedJoints = []; // obsolete + MyAvatar.startAnimation(KNEEL_ANIM_URL, playbackRate, priority, loopFlag, holdFlag, startFrame, endFrame, maskedJoints); + + Overlays.editOverlay(kneelDownButton, { visible: false }); + Overlays.editOverlay(standUpButton, { visible: true }); +} + +function standUp() { + kneeling = false; + + MyAvatar.stopAnimation(KNEEL_ANIM_URL); + + Overlays.editOverlay(standUpButton, { visible: false }); + Overlays.editOverlay(kneelDownButton, { visible: true }); +} + +Controller.mousePressEvent.connect(function (event) { + var clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); + if (clickedOverlay == kneelDownButton) { + kneelDown(); + } else if (clickedOverlay == standUpButton) { + standUp(); + } +}); + +Script.scriptEnding.connect(function() { + Overlays.deleteOverlay(kneelDownButton); + Overlays.deleteOverlay(standUpButton); +}); diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index df8b9b16c8..8e5953fa69 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -1,297 +1,184 @@ { "version": "1.0", "root": { - "id": "ikOverlay", - "type": "overlay", + "id": "userAnimStateMachine", + "type": "stateMachine", "data": { - "alpha": 1.0, - "alphaVar": "ikOverlayAlpha", - "boneSet": "fullBody" + "currentState": "userAnimNone", + "states": [ + { + "id": "userAnimNone", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "userAnimA", "state": "userAnimA" }, + { "var": "userAnimB", "state": "userAnimB" } + ] + }, + { + "id": "userAnimA", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "userAnimNone", "state": "userAnimNone" }, + { "var": "userAnimB", "state": "userAnimB" } + ] + }, + { + "id": "userAnimB", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "userAnimNone", "state": "userAnimNone" }, + { "var": "userAnimA", "state": "userAnimA" } + ] + } + ] }, "children": [ { - "id": "ik", - "type": "inverseKinematics", - "data": { - "targets": [ - { - "jointName": "RightHand", - "positionVar": "rightHandPosition", - "rotationVar": "rightHandRotation", - "typeVar": "rightHandType" - }, - { - "jointName": "LeftHand", - "positionVar": "leftHandPosition", - "rotationVar": "leftHandRotation", - "typeVar": "leftHandType" - }, - { - "jointName": "RightFoot", - "positionVar": "rightFootPosition", - "rotationVar": "rightFootRotation", - "typeVar": "rightFootType" - }, - { - "jointName": "LeftFoot", - "positionVar": "leftFootPosition", - "rotationVar": "leftFootRotation", - "typeVar": "leftFootType" - }, - { - "jointName": "Neck", - "positionVar": "neckPosition", - "rotationVar": "neckRotation", - "typeVar": "neckType" - }, - { - "jointName": "Head", - "positionVar": "headPosition", - "rotationVar": "headRotation", - "typeVar": "headType" - } - ] - }, - "children": [] - }, - { - "id": "manipulatorOverlay", + "id": "userAnimNone", "type": "overlay", "data": { "alpha": 1.0, - "boneSet": "spineOnly" + "alphaVar": "ikOverlayAlpha", + "boneSet": "fullBody" }, "children": [ { - "id": "spineLean", - "type": "manipulator", + "id": "ik", + "type": "inverseKinematics", "data": { - "alpha": 0.0, - "joints": [ - { "var": "lean", "jointName": "Spine" } + "targets": [ + { + "jointName": "RightHand", + "positionVar": "rightHandPosition", + "rotationVar": "rightHandRotation", + "typeVar": "rightHandType" + }, + { + "jointName": "LeftHand", + "positionVar": "leftHandPosition", + "rotationVar": "leftHandRotation", + "typeVar": "leftHandType" + }, + { + "jointName": "RightFoot", + "positionVar": "rightFootPosition", + "rotationVar": "rightFootRotation", + "typeVar": "rightFootType" + }, + { + "jointName": "LeftFoot", + "positionVar": "leftFootPosition", + "rotationVar": "leftFootRotation", + "typeVar": "leftFootType" + }, + { + "jointName": "Neck", + "positionVar": "neckPosition", + "rotationVar": "neckRotation", + "typeVar": "neckType" + }, + { + "jointName": "Head", + "positionVar": "headPosition", + "rotationVar": "headRotation", + "typeVar": "headType" + } ] }, "children": [] }, { - "id": "rightHandOverlay", + "id": "manipulatorOverlay", "type": "overlay", "data": { "alpha": 1.0, - "boneSet": "rightHand", - "alphaVar": "rightHandOverlayAlpha" + "boneSet": "spineOnly" }, "children": [ { - "id": "rightHandStateMachine", - "type": "stateMachine", + "id": "spineLean", + "type": "manipulator", "data": { - "currentState": "rightHandIdle", - "states": [ - { - "id": "rightHandIdle", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { "var": "isRightHandPoint", "state": "rightHandPointIntro" }, - { "var": "isRightHandGrab", "state": "rightHandGrab" } - ] - }, - { - "id": "rightHandPointIntro", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { "var": "isRightHandIdle", "state": "rightHandIdle" }, - { "var": "isRightHandPointIntroOnDone", "state": "rightHandPointHold" }, - { "var": "isRightHandGrab", "state": "rightHandGrab" } - ] - }, - { - "id": "rightHandPointHold", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { "var": "isRightHandIdle", "state": "rightHandPointOutro" }, - { "var": "isRightHandGrab", "state": "rightHandGrab" } - ] - }, - { - "id": "rightHandPointOutro", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { "var": "isRightHandPointOutroOnDone", "state": "rightHandIdle" }, - { "var": "isRightHandGrab", "state": "rightHandGrab" }, - { "var": "isRightHandPoint", "state": "rightHandPointHold" } - ] - }, - { - "id": "rightHandGrab", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { "var": "isRightHandIdle", "state": "rightHandIdle" }, - { "var": "isRightHandPoint_DISABLED", "state": "rightHandPointHold" } - ] - } + "alpha": 0.0, + "joints": [ + { "var": "lean", "jointName": "Spine" } ] }, - "children": [ - { - "id": "rightHandIdle", - "type": "clip", - "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", - "startFrame": 0.0, - "endFrame": 0.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "rightHandPointHold", - "type": "clip", - "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", - "startFrame": 12.0, - "endFrame": 12.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "rightHandPointIntro", - "type": "clip", - "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", - "startFrame": 0.0, - "endFrame": 12.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "rightHandPointOutro", - "type": "clip", - "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", - "startFrame": 12.0, - "endFrame": 65.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "rightHandGrab", - "type": "blendLinear", - "data": { - "alpha": 0.0, - "alphaVar": "rightHandGrabBlend" - }, - "children": [ - { - "id": "rightHandOpen", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_right.fbx", - "startFrame": 0.0, - "endFrame": 0.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "rightHandClose", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_right.fbx", - "startFrame": 10.0, - "endFrame": 10.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - } - ] + "children": [] }, { - "id": "leftHandOverlay", + "id": "rightHandOverlay", "type": "overlay", "data": { "alpha": 1.0, - "boneSet": "leftHand", - "alphaVar" : "leftHandOverlay" + "boneSet": "rightHand", + "alphaVar": "rightHandOverlayAlpha" }, "children": [ { - "id": "leftHandStateMachine", + "id": "rightHandStateMachine", "type": "stateMachine", "data": { - "currentState": "leftHandIdle", + "currentState": "rightHandIdle", "states": [ { - "id": "leftHandIdle", + "id": "rightHandIdle", "interpTarget": 3, "interpDuration": 3, "transitions": [ - { "var": "isLeftHandPoint", "state": "leftHandPointIntro" }, - { "var": "isLeftHandGrab", "state": "leftHandGrab" } + { "var": "isRightHandPoint", "state": "rightHandPointIntro" }, + { "var": "isRightHandGrab", "state": "rightHandGrab" } ] }, { - "id": "leftHandPointIntro", + "id": "rightHandPointIntro", "interpTarget": 3, "interpDuration": 3, "transitions": [ - { "var": "isLeftHandIdle", "state": "leftHandIdle" }, - { "var": "isLeftHandPointIntroOnDone", "state": "leftHandPointHold" }, - { "var": "isLeftHandGrab", "state": "leftHandGrab" } + { "var": "isRightHandIdle", "state": "rightHandIdle" }, + { "var": "isRightHandPointIntroOnDone", "state": "rightHandPointHold" }, + { "var": "isRightHandGrab", "state": "rightHandGrab" } ] }, { - "id": "leftHandPointHold", + "id": "rightHandPointHold", "interpTarget": 3, "interpDuration": 3, "transitions": [ - { "var": "isLeftHandIdle", "state": "leftHandPointOutro" }, - { "var": "isLeftHandGrab", "state": "leftHandGrab" } + { "var": "isRightHandIdle", "state": "rightHandPointOutro" }, + { "var": "isRightHandGrab", "state": "rightHandGrab" } ] }, { - "id": "leftHandPointOutro", + "id": "rightHandPointOutro", "interpTarget": 3, "interpDuration": 3, "transitions": [ - { "var": "isLeftHandPointOutroOnDone", "state": "leftHandIdle" }, - { "var": "isLeftHandGrab", "state": "leftHandGrab" }, - { "var": "isLeftHandPoint", "state": "leftHandPointHold" } + { "var": "isRightHandPointOutroOnDone", "state": "rightHandIdle" }, + { "var": "isRightHandGrab", "state": "rightHandGrab" }, + { "var": "isRightHandPoint", "state": "rightHandPointHold" } ] }, { - "id": "leftHandGrab", + "id": "rightHandGrab", "interpTarget": 3, "interpDuration": 3, "transitions": [ - { "var": "isLeftHandIdle", "state": "leftHandIdle" }, - { "var": "isLeftHandPoint_DISABLED", "state": "leftHandPointHold" } + { "var": "isRightHandIdle", "state": "rightHandIdle" }, + { "var": "isRightHandPoint_DISABLED", "state": "rightHandPointHold" } ] } ] }, "children": [ { - "id": "leftHandIdle", + "id": "rightHandIdle", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", "startFrame": 0.0, "endFrame": 0.0, "timeScale": 1.0, @@ -300,10 +187,10 @@ "children": [] }, { - "id": "leftHandPointHold", + "id": "rightHandPointHold", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", "startFrame": 12.0, "endFrame": 12.0, "timeScale": 1.0, @@ -312,10 +199,10 @@ "children": [] }, { - "id": "leftHandPointIntro", + "id": "rightHandPointIntro", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", "startFrame": 0.0, "endFrame": 12.0, "timeScale": 1.0, @@ -324,10 +211,10 @@ "children": [] }, { - "id": "leftHandPointOutro", + "id": "rightHandPointOutro", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_right_hand.fbx", "startFrame": 12.0, "endFrame": 65.0, "timeScale": 1.0, @@ -336,18 +223,18 @@ "children": [] }, { - "id": "leftHandGrab", + "id": "rightHandGrab", "type": "blendLinear", "data": { "alpha": 0.0, - "alphaVar": "leftHandGrabBlend" + "alphaVar": "rightHandGrabBlend" }, "children": [ { - "id": "leftHandOpen", + "id": "rightHandOpen", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_left.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_right.fbx", "startFrame": 0.0, "endFrame": 0.0, "timeScale": 1.0, @@ -356,10 +243,10 @@ "children": [] }, { - "id": "leftHandClose", + "id": "rightHandClose", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_left.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_right.fbx", "startFrame": 10.0, "endFrame": 10.0, "timeScale": 1.0, @@ -372,313 +259,427 @@ ] }, { - "id": "mainStateMachine", - "type": "stateMachine", + "id": "leftHandOverlay", + "type": "overlay", "data": { - "currentState": "idle", - "states": [ - { - "id": "idle", - "interpTarget": 15, - "interpDuration": 15, - "transitions": [ - { "var": "isMovingForward", "state": "walkFwd" }, - { "var": "isMovingBackward", "state": "walkBwd" }, - { "var": "isMovingRight", "state": "strafeRight" }, - { "var": "isMovingLeft", "state": "strafeLeft" }, - { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" }, - { "var": "isAway", "state": "awayIntro" } - ] - }, - { - "id": "walkFwd", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "isNotMoving", "state": "idle" }, - { "var": "isMovingBackward", "state": "walkBwd" }, - { "var": "isMovingRight", "state": "strafeRight" }, - { "var": "isMovingLeft", "state": "strafeLeft" }, - { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" }, - { "var": "isAway", "state": "awayIntro" } - ] - }, - { - "id": "walkBwd", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "isNotMoving", "state": "idle" }, - { "var": "isMovingForward", "state": "walkFwd" }, - { "var": "isMovingRight", "state": "strafeRight" }, - { "var": "isMovingLeft", "state": "strafeLeft" }, - { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" }, - { "var": "isAway", "state": "awayIntro" } - ] - }, - { - "id": "strafeRight", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "isNotMoving", "state": "idle" }, - { "var": "isMovingForward", "state": "walkFwd" }, - { "var": "isMovingBackward", "state": "walkBwd" }, - { "var": "isMovingLeft", "state": "strafeLeft" }, - { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" }, - { "var": "isAway", "state": "awayIntro" } - ] - }, - { - "id": "strafeLeft", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "isNotMoving", "state": "idle" }, - { "var": "isMovingForward", "state": "walkFwd" }, - { "var": "isMovingBackward", "state": "walkBwd" }, - { "var": "isMovingRight", "state": "strafeRight" }, - { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isTurningLeft", "state": "turnLeft" }, - { "var": "isAway", "state": "awayIntro" } - ] - }, - { - "id": "turnRight", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "isNotTurning", "state": "idle" }, - { "var": "isMovingForward", "state": "walkFwd" }, - { "var": "isMovingBackward", "state": "walkBwd" }, - { "var": "isMovingRight", "state": "strafeRight" }, - { "var": "isMovingLeft", "state": "strafeLeft" }, - { "var": "isTurningLeft", "state": "turnLeft" }, - { "var": "isAway", "state": "awayIntro" } - ] - }, - { - "id": "turnLeft", - "interpTarget": 6, - "interpDuration": 6, - "transitions": [ - { "var": "isNotTurning", "state": "idle" }, - { "var": "isMovingForward", "state": "walkFwd" }, - { "var": "isMovingBackward", "state": "walkBwd" }, - { "var": "isMovingRight", "state": "strafeRight" }, - { "var": "isMovingLeft", "state": "strafeLeft" }, - { "var": "isTurningRight", "state": "turnRight" }, - { "var": "isAway", "state": "awayIntro" } - ] - }, - { - "id": "awayIntro", - "interpTarget": 30, - "interpDuration": 30, - "transitions": [ - { "var": "awayIntroOnDone", "state": "away"} - ] - }, - { - "id": "away", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { "var": "isNotAway", "state": "awayOutro" } - ] - }, - { - "id": "awayOutro", - "interpTarget": 3, - "interpDuration": 3, - "transitions": [ - { "var": "awayOutroOnDone", "state": "idle" } - ] - } - ] + "alpha": 1.0, + "boneSet": "leftHand", + "alphaVar" : "leftHandOverlay" }, "children": [ { - "id": "idle", + "id": "leftHandStateMachine", "type": "stateMachine", "data": { - "currentState": "idleStand", + "currentState": "leftHandIdle", "states": [ { - "id": "idleStand", - "interpTarget": 6, - "interpDuration": 6, + "id": "leftHandIdle", + "interpTarget": 3, + "interpDuration": 3, "transitions": [ - { "var": "isTalking", "state": "idleTalk" } + { "var": "isLeftHandPoint", "state": "leftHandPointIntro" }, + { "var": "isLeftHandGrab", "state": "leftHandGrab" } ] }, { - "id": "idleTalk", - "interpTarget": 6, - "interpDuration": 6, + "id": "leftHandPointIntro", + "interpTarget": 3, + "interpDuration": 3, "transitions": [ - { "var": "notIsTalking", "state": "idleStand" } + { "var": "isLeftHandIdle", "state": "leftHandIdle" }, + { "var": "isLeftHandPointIntroOnDone", "state": "leftHandPointHold" }, + { "var": "isLeftHandGrab", "state": "leftHandGrab" } + ] + }, + { + "id": "leftHandPointHold", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isLeftHandIdle", "state": "leftHandPointOutro" }, + { "var": "isLeftHandGrab", "state": "leftHandGrab" } + ] + }, + { + "id": "leftHandPointOutro", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isLeftHandPointOutroOnDone", "state": "leftHandIdle" }, + { "var": "isLeftHandGrab", "state": "leftHandGrab" }, + { "var": "isLeftHandPoint", "state": "leftHandPointHold" } + ] + }, + { + "id": "leftHandGrab", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isLeftHandIdle", "state": "leftHandIdle" }, + { "var": "isLeftHandPoint_DISABLED", "state": "leftHandPointHold" } ] } ] }, "children": [ { - "id": "idleStand", + "id": "leftHandIdle", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", "startFrame": 0.0, - "endFrame": 90.0, + "endFrame": 0.0, "timeScale": 1.0, "loopFlag": true }, "children": [] }, { - "id": "idleTalk", + "id": "leftHandPointHold", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/talk/talk.fbx", - "startFrame": 0.0, - "endFrame": 801.0, + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", + "startFrame": 12.0, + "endFrame": 12.0, "timeScale": 1.0, "loopFlag": true }, "children": [] + }, + { + "id": "leftHandPointIntro", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", + "startFrame": 0.0, + "endFrame": 12.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "leftHandPointOutro", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/hand_anims/point_left_hand.fbx", + "startFrame": 12.0, + "endFrame": 65.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "leftHandGrab", + "type": "blendLinear", + "data": { + "alpha": 0.0, + "alphaVar": "leftHandGrabBlend" + }, + "children": [ + { + "id": "leftHandOpen", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_left.fbx", + "startFrame": 0.0, + "endFrame": 0.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "leftHandClose", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/grab/grab_left.fbx", + "startFrame": 10.0, + "endFrame": 10.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] } ] }, { - "id": "walkFwd", - "type": "blendLinearMove", + "id": "mainStateMachine", + "type": "stateMachine", "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [0.5, 1.4, 4.5], - "alphaVar": "moveForwardAlpha", - "desiredSpeedVar": "moveForwardSpeed" + "currentState": "idle", + "states": [ + { + "id": "idle", + "interpTarget": 15, + "interpDuration": 15, + "transitions": [ + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "walkFwd", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "walkBwd", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "strafeRight", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "strafeLeft", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotMoving", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "turnRight", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotTurning", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningLeft", "state": "turnLeft" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "turnLeft", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isNotTurning", "state": "idle" }, + { "var": "isMovingForward", "state": "walkFwd" }, + { "var": "isMovingBackward", "state": "walkBwd" }, + { "var": "isMovingRight", "state": "strafeRight" }, + { "var": "isMovingLeft", "state": "strafeLeft" }, + { "var": "isTurningRight", "state": "turnRight" }, + { "var": "isAway", "state": "awayIntro" } + ] + }, + { + "id": "awayIntro", + "interpTarget": 30, + "interpDuration": 30, + "transitions": [ + { "var": "awayIntroOnDone", "state": "away"} + ] + }, + { + "id": "away", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "isNotAway", "state": "awayOutro" } + ] + }, + { + "id": "awayOutro", + "interpTarget": 3, + "interpDuration": 3, + "transitions": [ + { "var": "awayOutroOnDone", "state": "idle" } + ] + } + ] }, "children": [ { - "id": "walkFwdShort", - "type": "clip", + "id": "idle", + "type": "stateMachine", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_fwd.fbx", - "startFrame": 0.0, - "endFrame": 39.0, - "timeScale": 1.0, - "loopFlag": true + "currentState": "idleStand", + "states": [ + { + "id": "idleStand", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "isTalking", "state": "idleTalk" } + ] + }, + { + "id": "idleTalk", + "interpTarget": 6, + "interpDuration": 6, + "transitions": [ + { "var": "notIsTalking", "state": "idleStand" } + ] + } + ] }, - "children": [] + "children": [ + { + "id": "idleStand", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "idleTalk", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/talk/talk.fbx", + "startFrame": 0.0, + "endFrame": 801.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { - "id": "walkFwdNormal", - "type": "clip", + "id": "walkFwd", + "type": "blendLinearMove", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", - "startFrame": 0.0, - "endFrame": 35.0, - "timeScale": 1.0, - "loopFlag": true + "alpha": 0.0, + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.5, 1.4, 4.5], + "alphaVar": "moveForwardAlpha", + "desiredSpeedVar": "moveForwardSpeed" }, - "children": [] + "children": [ + { + "id": "walkFwdShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_fwd.fbx", + "startFrame": 0.0, + "endFrame": 39.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walkFwdNormal", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", + "startFrame": 0.0, + "endFrame": 35.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walkFwdRun", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/run_fwd.fbx", + "startFrame": 0.0, + "endFrame": 21.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { - "id": "walkFwdRun", - "type": "clip", + "id": "walkBwd", + "type": "blendLinearMove", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/run_fwd.fbx", - "startFrame": 0.0, - "endFrame": 21.0, - "timeScale": 1.0, - "loopFlag": true + "alpha": 0.0, + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.6, 1.45], + "alphaVar": "moveBackwardAlpha", + "desiredSpeedVar": "moveBackwardSpeed" }, - "children": [] - } - ] - }, - { - "id": "walkBwd", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [0.6, 1.45], - "alphaVar": "moveBackwardAlpha", - "desiredSpeedVar": "moveBackwardSpeed" - }, - "children": [ - { - "id": "walkBwdShort", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_bwd.fbx", - "startFrame": 0.0, - "endFrame": 38.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] + "children": [ + { + "id": "walkBwdShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_short_bwd.fbx", + "startFrame": 0.0, + "endFrame": 38.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "walkBwdNormal", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", + "startFrame": 0.0, + "endFrame": 36.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] }, { - "id": "walkBwdNormal", + "id": "turnLeft", "type": "clip", "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx", - "startFrame": 0.0, - "endFrame": 36.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - } - ] - }, - { - "id": "turnLeft", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx", - "startFrame": 0.0, - "endFrame": 28.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "turnRight", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx", - "startFrame": 0.0, - "endFrame": 30.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "strafeLeft", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [0.2, 0.65], - "alphaVar": "moveLateralAlpha", - "desiredSpeedVar": "moveLateralSpeed" - }, - "children": [ - { - "id": "strafeLeftShort", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_left.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx", "startFrame": 0.0, "endFrame": 28.0, "timeScale": 1.0, @@ -687,91 +688,128 @@ "children": [] }, { - "id": "strafeLeftNormal", + "id": "turnRight", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx", "startFrame": 0.0, "endFrame": 30.0, "timeScale": 1.0, "loopFlag": true }, "children": [] - } - ] - }, - { - "id": "strafeRight", - "type": "blendLinearMove", - "data": { - "alpha": 0.0, - "desiredSpeed": 1.4, - "characteristicSpeeds": [0.2, 0.65], - "alphaVar": "moveLateralAlpha", - "desiredSpeedVar": "moveLateralSpeed" - }, - "children": [ - { - "id": "strafeRightShort", - "type": "clip", - "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_right.fbx", - "startFrame": 0.0, - "endFrame": 28.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] }, { - "id": "strafeRightNormal", + "id": "strafeLeft", + "type": "blendLinearMove", + "data": { + "alpha": 0.0, + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.2, 0.65], + "alphaVar": "moveLateralAlpha", + "desiredSpeedVar": "moveLateralSpeed" + }, + "children": [ + { + "id": "strafeLeftShort", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_left.fbx", + "startFrame": 0.0, + "endFrame": 28.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "strafeLeftNormal", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_left.fbx", + "startFrame": 0.0, + "endFrame": 30.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] + }, + { + "id": "strafeRight", + "type": "blendLinearMove", + "data": { + "alpha": 0.0, + "desiredSpeed": 1.4, + "characteristicSpeeds": [0.2, 0.65], + "alphaVar": "moveLateralAlpha", + "desiredSpeedVar": "moveLateralSpeed" + }, + "children": [ + { + "id": "strafeRightShort", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_short_right.fbx", + "startFrame": 0.0, + "endFrame": 28.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "strafeRightNormal", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", + "startFrame": 0.0, + "endFrame": 30.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + } + ] + }, + { + "id": "awayIntro", "type": "clip", "data": { - "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/side_step_right.fbx", + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", "startFrame": 0.0, - "endFrame": 30.0, + "endFrame": 83.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] + }, + { + "id": "away", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", + "startFrame": 83.0, + "endFrame": 84.0, "timeScale": 1.0, "loopFlag": true }, "children": [] + }, + { + "id": "awayOutro", + "type": "clip", + "data": { + "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", + "startFrame": 84.0, + "endFrame": 167.0, + "timeScale": 1.0, + "loopFlag": false + }, + "children": [] } ] - }, - { - "id": "awayIntro", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", - "startFrame": 0.0, - "endFrame": 83.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] - }, - { - "id": "away", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", - "startFrame": 83.0, - "endFrame": 84.0, - "timeScale": 1.0, - "loopFlag": true - }, - "children": [] - }, - { - "id": "awayOutro", - "type": "clip", - "data": { - "url": "https://hifi-public.s3.amazonaws.com/ozan/anim/kneel/kneel.fbx", - "startFrame": 84.0, - "endFrame": 167.0, - "timeScale": 1.0, - "loopFlag": false - }, - "children": [] } ] } @@ -780,6 +818,30 @@ ] } ] + }, + { + "id": "userAnimA", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] + }, + { + "id": "userAnimB", + "type": "clip", + "data": { + "url": "http://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx", + "startFrame": 0.0, + "endFrame": 90.0, + "timeScale": 1.0, + "loopFlag": true + }, + "children": [] } ] } diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index 36867e622b..934f3f3ed8 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -37,13 +37,18 @@ public: void setFrameVar(const QString& frameVar) { _frameVar = frameVar; } float getStartFrame() const { return _startFrame; } + void setStartFrame(float startFrame) { _startFrame = startFrame; } float getEndFrame() const { return _endFrame; } + void setEndFrame(float endFrame) { _endFrame = endFrame; } void setTimeScale(float timeScale) { _timeScale = timeScale; } float getTimeScale() const { return _timeScale; } -protected: + bool getLoopFlag() const { return _loopFlag; } + void setLoopFlag(bool loopFlag) { _loopFlag = loopFlag; } + void loadURL(const QString& url); +protected: virtual void setCurrentFrameInternal(float frame) override; diff --git a/libraries/animation/src/AnimUtil.cpp b/libraries/animation/src/AnimUtil.cpp index e9e5ea95de..eb01bc31c2 100644 --- a/libraries/animation/src/AnimUtil.cpp +++ b/libraries/animation/src/AnimUtil.cpp @@ -18,8 +18,17 @@ void blend(size_t numPoses, const AnimPose* a, const AnimPose* b, float alpha, A for (size_t i = 0; i < numPoses; i++) { const AnimPose& aPose = a[i]; const AnimPose& bPose = b[i]; + + // adjust signs if necessary + const glm::quat& q1 = aPose.rot; + glm::quat q2 = bPose.rot; + float dot = glm::dot(q1, q2); + if (dot < 0.0f) { + q2 = -q2; + } + result[i].scale = lerp(aPose.scale, bPose.scale, alpha); - result[i].rot = glm::normalize(glm::lerp(aPose.rot, bPose.rot, alpha)); + result[i].rot = glm::normalize(glm::lerp(aPose.rot, q2, alpha)); result[i].trans = lerp(aPose.trans, bPose.trans, alpha); } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7926b268b5..72a1eeae5c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -21,6 +21,7 @@ #include "AnimationHandle.h" #include "AnimationLogging.h" #include "AnimSkeleton.h" +#include "AnimClip.h" #include "IKTarget.h" void insertSorted(QList& handles, const AnimationHandlePointer& handle) { @@ -45,28 +46,61 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) { } void Rig::startAnimation(const QString& url, float fps, float priority, - bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { - // This is different than startAnimationByRole, in which we use the existing values if the animation already exists. - // Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted). - AnimationHandlePointer handle = nullptr; - foreach (const AnimationHandlePointer& candidate, _animationHandles) { - if (candidate->getURL() == url) { - handle = candidate; + bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { + if (_enableAnimGraph) { + + // NOTE: mask joints are unsupported, priority is now meaningless, hold flag is essentially always true + // when using the AnimGraph system. + + // find an unused AnimClip clipNode + std::shared_ptr clip; + if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) { + _userAnimState = UserAnimState::A; + clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); + } else if (_userAnimState == UserAnimState::A) { + _userAnimState = UserAnimState::B; + clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); } + + // set parameters + clip->setLoopFlag(loop); + clip->setStartFrame(firstFrame); + clip->setEndFrame(lastFrame); + const float REFERENCE_FRAMES_PER_SECOND = 30.0f; + clip->setTimeScale(fps / REFERENCE_FRAMES_PER_SECOND); + clip->loadURL(url); + + _currentUserAnimURL = url; + + // notify the userAnimStateMachine the desired state. + _animVars.set("userAnimNone", false); + _animVars.set("userAnimA", _userAnimState == UserAnimState::A); + _animVars.set("userAnimB", _userAnimState == UserAnimState::B); + + } else { + + // This is different than startAnimationByRole, in which we use the existing values if the animation already exists. + // Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted). + AnimationHandlePointer handle = nullptr; + foreach (const AnimationHandlePointer& candidate, _animationHandles) { + if (candidate->getURL() == url) { + handle = candidate; + } + } + if (!handle) { + handle = createAnimationHandle(); + handle->setURL(url); + } + handle->setFade(1.0f); // If you want to fade, use the startAnimationByRole system. + handle->setFPS(fps); + handle->setPriority(priority); + handle->setLoop(loop); + handle->setHold(hold); + handle->setFirstFrame(firstFrame); + handle->setLastFrame(lastFrame); + handle->setMaskedJoints(maskedJoints); + handle->start(); } - if (!handle) { - handle = createAnimationHandle(); - handle->setURL(url); - } - handle->setFade(1.0f); // If you want to fade, use the startAnimationByRole system. - handle->setFPS(fps); - handle->setPriority(priority); - handle->setLoop(loop); - handle->setHold(hold); - handle->setFirstFrame(firstFrame); - handle->setLastFrame(lastFrame); - handle->setMaskedJoints(maskedJoints); - handle->start(); } AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority, @@ -138,10 +172,20 @@ void Rig::stopAnimationByRole(const QString& role) { } void Rig::stopAnimation(const QString& url) { - foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { - if (handle->getURL() == url) { - handle->setFade(0.0f); // right away. Will be remove during updateAnimations, without locking - handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // so that the updateAnimation code notices + if (_enableAnimGraph) { + if (url == _currentUserAnimURL) { + _currentUserAnimURL = ""; + // notify the userAnimStateMachine the desired state. + _animVars.set("userAnimNone", true); + _animVars.set("userAnimA", false); + _animVars.set("userAnimB", false); + } + } else { + foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { + if (handle->getURL() == url) { + handle->setFade(0.0f); // right away. Will be remove during updateAnimations, without locking + handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // so that the updateAnimation code notices + } } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 98847b9915..e086b81e39 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -223,21 +223,21 @@ public: void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; QVector _jointStates; - int _rootJointIndex = -1; + int _rootJointIndex { -1 }; - int _leftHandJointIndex = -1; - int _leftElbowJointIndex = -1; - int _leftShoulderJointIndex = -1; + int _leftHandJointIndex { -1 }; + int _leftElbowJointIndex { -1 }; + int _leftShoulderJointIndex { -1 }; - int _rightHandJointIndex = -1; - int _rightElbowJointIndex = -1; - int _rightShoulderJointIndex = -1; + int _rightHandJointIndex { -1 }; + int _rightElbowJointIndex { -1 }; + int _rightShoulderJointIndex { -1 }; QList _animationHandles; QList _runningAnimations; - bool _enableRig = false; - bool _enableAnimGraph = false; + bool _enableRig { false }; + bool _enableAnimGraph { false }; glm::vec3 _lastFront; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; @@ -251,18 +251,25 @@ public: Turn, Move }; - RigRole _state = RigRole::Idle; - RigRole _desiredState = RigRole::Idle; - float _desiredStateAge = 0.0f; - float _leftHandOverlayAlpha = 0.0f; - float _rightHandOverlayAlpha = 0.0f; + RigRole _state { RigRole::Idle }; + RigRole _desiredState { RigRole::Idle }; + float _desiredStateAge { 0.0f }; + enum class UserAnimState { + None = 0, + A, + B + }; + UserAnimState _userAnimState { UserAnimState::None }; + QString _currentUserAnimURL; + float _leftHandOverlayAlpha { 0.0f }; + float _rightHandOverlayAlpha { 0.0f }; - SimpleMovingAverage _averageForwardSpeed{ 10 }; - SimpleMovingAverage _averageLateralSpeed{ 10 }; + SimpleMovingAverage _averageForwardSpeed { 10 }; + SimpleMovingAverage _averageLateralSpeed { 10 }; private: QMap _stateHandlers; - int _nextStateHandlerId {0}; + int _nextStateHandlerId { 0 }; QMutex _stateMutex; }; From af2b3bb9d59fcfd2e8e3b20b7b467fa4ee06eb0a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 9 Nov 2015 16:56:49 -0800 Subject: [PATCH 02/84] MyAvatar: removed priority, hold and maskedJoints from playAnimation js call --- examples/kneel.js | 6 ++---- interface/src/avatar/MyAvatar.cpp | 8 +++----- interface/src/avatar/MyAvatar.h | 4 +--- libraries/animation/src/Rig.cpp | 11 ++++++----- libraries/animation/src/Rig.h | 3 +-- 5 files changed, 13 insertions(+), 19 deletions(-) diff --git a/examples/kneel.js b/examples/kneel.js index 2ba029677a..5dd772c507 100644 --- a/examples/kneel.js +++ b/examples/kneel.js @@ -53,13 +53,11 @@ function kneelDown() { kneeling = true; var playbackRate = 30; // 30 fps is normal speed. - var priority = 0; // obsolete var loopFlag = false; - var holdFlag = false; // obsolete var startFrame = 0; var endFrame = 82; - var maskedJoints = []; // obsolete - MyAvatar.startAnimation(KNEEL_ANIM_URL, playbackRate, priority, loopFlag, holdFlag, startFrame, endFrame, maskedJoints); + + MyAvatar.startAnimation(KNEEL_ANIM_URL, playbackRate, loopFlag, startFrame, endFrame); Overlays.editOverlay(kneelDownButton, { visible: false }); Overlays.editOverlay(standUpButton, { visible: true }); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 57af9e732d..ab8b78d6e3 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -657,15 +657,13 @@ void MyAvatar::loadLastRecording() { _player->loadRecording(_recorder->getRecording()); } -void MyAvatar::startAnimation(const QString& url, float fps, float priority, - bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { +void MyAvatar::startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), - Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame), - Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); + Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); return; } - _rig->startAnimation(url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints); + _rig->startAnimation(url, fps, loop, firstFrame, lastFrame); } void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d6f51636f3..0a6fdb4af2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -118,9 +118,7 @@ public: AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); } void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); } /// Allows scripts to run animations. - Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, - bool hold = false, float firstFrame = 0.0f, - float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); + Q_INVOKABLE void startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); /// Stops an animation as identified by a URL. Q_INVOKABLE void stopAnimation(const QString& url); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 72a1eeae5c..e0369b23a4 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -45,13 +45,9 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) { _animationHandles.removeOne(handle); } -void Rig::startAnimation(const QString& url, float fps, float priority, - bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { +void Rig::startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { if (_enableAnimGraph) { - // NOTE: mask joints are unsupported, priority is now meaningless, hold flag is essentially always true - // when using the AnimGraph system. - // find an unused AnimClip clipNode std::shared_ptr clip; if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) { @@ -79,6 +75,10 @@ void Rig::startAnimation(const QString& url, float fps, float priority, } else { + float priority = 1.0f; + bool hold = true; + QStringList maskedJoints; + // This is different than startAnimationByRole, in which we use the existing values if the animation already exists. // Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted). AnimationHandlePointer handle = nullptr; @@ -105,6 +105,7 @@ void Rig::startAnimation(const QString& url, float fps, float priority, AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority, bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) { + // check for a configured animation for the role //qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically; foreach (const AnimationHandlePointer& candidate, _animationHandles) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index e086b81e39..f36dd19dda 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -113,8 +113,7 @@ public: void deleteAnimations(); void destroyAnimGraph(); const QList& getAnimationHandles() const { return _animationHandles; } - void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false, - bool hold = false, float firstFrame = 0.0f, float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); + void startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void stopAnimation(const QString& url); void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, From eacc2cae0c99bf0a8b2874c4c6e69c12d119867d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 9 Nov 2015 18:36:23 -0800 Subject: [PATCH 03/84] WIP checkpoint --- examples/kneel.js | 2 +- interface/src/Application.cpp | 8 -- interface/src/avatar/MyAvatar.cpp | 103 +++++++--------------- interface/src/avatar/MyAvatar.h | 29 ++++--- libraries/animation/src/AnimNode.h | 26 +++++- libraries/animation/src/Rig.cpp | 134 +++++++++++++---------------- libraries/animation/src/Rig.h | 14 ++- 7 files changed, 140 insertions(+), 176 deletions(-) diff --git a/examples/kneel.js b/examples/kneel.js index 5dd772c507..3270a77df7 100644 --- a/examples/kneel.js +++ b/examples/kneel.js @@ -66,7 +66,7 @@ function kneelDown() { function standUp() { kneeling = false; - MyAvatar.stopAnimation(KNEEL_ANIM_URL); + MyAvatar.stopAnimation(); Overlays.editOverlay(standUpButton, { visible: false }); Overlays.editOverlay(kneelDownButton, { visible: true }); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7a564bbbf0..07ddd0a1f7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4294,10 +4294,6 @@ void Application::stopAllScripts(bool restart) { it.value()->stop(); qCDebug(interfaceapp) << "stopping script..." << it.key(); } - // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities - // whenever a script stops in case it happened to have been setting joint rotations. - // TODO: expose animation priorities and provide a layered animation control system. - getMyAvatar()->clearJointAnimationPriorities(); getMyAvatar()->clearScriptableSettings(); } @@ -4313,10 +4309,6 @@ bool Application::stopScript(const QString& scriptHash, bool restart) { scriptEngine->stop(); stoppedScript = true; qCDebug(interfaceapp) << "stopping script..." << scriptHash; - // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities - // whenever a script stops in case it happened to have been setting joint rotations. - // TODO: expose animation priorities and provide a layered animation control system. - getMyAvatar()->clearJointAnimationPriorities(); } if (_scriptEnginesHash.empty()) { getMyAvatar()->clearScriptableSettings(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ab8b78d6e3..14b66c5465 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -666,65 +666,47 @@ void MyAvatar::startAnimation(const QString& url, float fps, bool loop, float fi _rig->startAnimation(url, fps, loop, firstFrame, lastFrame); } -void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, - bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { +void MyAvatar::stopAnimation() { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "startAnimationByRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url), - Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(float, firstFrame), - Q_ARG(float, lastFrame), Q_ARG(const QStringList&, maskedJoints)); + QMetaObject::invokeMethod(this, "stopAnimation"); return; } - _rig->startAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints); + _rig->stopAnimation(); } -void MyAvatar::stopAnimationByRole(const QString& role) { +QStringList MyAvatar::getAnimationRoles() { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role)); - return; - } - _rig->stopAnimationByRole(role); -} - -void MyAvatar::stopAnimation(const QString& url) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url)); - return; - } - _rig->stopAnimation(url); -} - -AnimationDetails MyAvatar::getAnimationDetailsByRole(const QString& role) { - AnimationDetails result; - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "getAnimationDetailsByRole", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(AnimationDetails, result), - Q_ARG(const QString&, role)); + QStringList result; + QMetaObject::invokeMethod(this, "getAnimationRoles", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QStringList, result)); return result; } - foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) { - if (handle->getRole() == role) { - result = handle->getAnimationDetails(); - break; - } - } - return result; + return _rig->getAnimationRoles(); } -AnimationDetails MyAvatar::getAnimationDetails(const QString& url) { - AnimationDetails result; +void MyAvatar::overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, + float firstFrame, float lastFrame) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "getAnimationDetails", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(AnimationDetails, result), - Q_ARG(const QString&, url)); - return result; + QMetaObject::invokeMethod(this, "overrideAnimationRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url), + Q_ARG(float, fps), Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); + return; } - foreach (const AnimationHandlePointer& handle, _rig->getRunningAnimations()) { - if (handle->getURL() == url) { - result = handle->getAnimationDetails(); - break; - } + _rig->overrideAnimationRole(role, url, fps, loop, firstFrame, lastFrame); +} + +void MyAvatar::restoreAnimationRole(const QString& role) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "restoreAnimationRole", Q_ARG(const QString&, role)); + return; } - return result; + _rig->restoreAnimationRole(role); +} + +void MyAvatar::prefetchAnimation(const QString& url) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "prefetchAnimation", Q_ARG(const QString&, url)); + return; + } + _rig->prefetchAnimation(url); } void MyAvatar::saveData() { @@ -797,6 +779,7 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) { // If we demand the animation from the update thread while we're locked, we'll deadlock. // Until we untangle this, code puts the updates back on the main thread temporarilly and starts all the loading. void MyAvatar::safelyLoadAnimations() { + /* _rig->addAnimationByRole("idle"); _rig->addAnimationByRole("walk"); _rig->addAnimationByRole("backup"); @@ -804,6 +787,7 @@ void MyAvatar::safelyLoadAnimations() { _rig->addAnimationByRole("rightTurn"); _rig->addAnimationByRole("leftStrafe"); _rig->addAnimationByRole("rightStrafe"); + */ } void MyAvatar::setEnableRigAnimations(bool isEnabled) { @@ -905,23 +889,6 @@ void MyAvatar::loadData() { settings.endArray(); setAttachmentData(attachmentData); - int animationCount = settings.beginReadArray("animationHandles"); - _rig->deleteAnimations(); - for (int i = 0; i < animationCount; i++) { - settings.setArrayIndex(i); - _rig->addAnimationByRole(settings.value("role", "idle").toString(), - settings.value("url").toString(), - loadSetting(settings, "fps", 30.0f), - loadSetting(settings, "priority", 1.0f), - settings.value("loop", true).toBool(), - settings.value("hold", false).toBool(), - settings.value("firstFrame", 0.0f).toFloat(), - settings.value("lastFrame", INT_MAX).toFloat(), - settings.value("maskedJoints").toStringList(), - settings.value("startAutomatically", true).toBool()); - } - settings.endArray(); - setDisplayName(settings.value("displayName").toString()); setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); @@ -1177,14 +1144,7 @@ void MyAvatar::clearJointData(int index) { } void MyAvatar::clearJointsData() { - clearJointAnimationPriorities(); -} - -void MyAvatar::clearJointAnimationPriorities() { - int numStates = _skeletonModel.getJointStateCount(); - for (int i = 0; i < numStates; ++i) { - _rig->clearJointAnimationPriority(i); - } + //clearJointAnimationPriorities(); } void MyAvatar::setFaceModelURL(const QUrl& faceModelURL) { @@ -1382,7 +1342,6 @@ void MyAvatar::setScriptedMotorFrame(QString frame) { } void MyAvatar::clearScriptableSettings() { - clearJointAnimationPriorities(); _scriptedMotorVelocity = glm::vec3(0.0f); _scriptedMotorTimescale = DEFAULT_SCRIPTED_MOTOR_TIMESCALE; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0a6fdb4af2..2e5f13f81d 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -117,22 +117,25 @@ public: const QList& getAnimationHandles() const { return _rig->getAnimationHandles(); } AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); } void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); } - /// Allows scripts to run animations. + + // Interrupt the current animation with a custom animation. Q_INVOKABLE void startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - /// Stops an animation as identified by a URL. - Q_INVOKABLE void stopAnimation(const QString& url); + // Stops the animation that was started with startAnimation and go back to the standard animation. + Q_INVOKABLE void stopAnimation(); + + // Returns a list of all clips that are available + Q_INVOKABLE QStringList getAnimationRoles(); + + // Replace an existing standard role animation with a custom one. + Q_INVOKABLE void overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + + // remove an animation role override and return to the standard animation. + Q_INVOKABLE void restoreAnimationRole(const QString& role); + + // prefetch animation + Q_INVOKABLE void prefetchAnimation(const QString& url); - /// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom - /// animation for the role. - Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, - float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, - float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); - /// Stops an animation identified by its role. - Q_INVOKABLE void stopAnimationByRole(const QString& role); - Q_INVOKABLE AnimationDetails getAnimationDetailsByRole(const QString& role); - Q_INVOKABLE AnimationDetails getAnimationDetails(const QString& url); - void clearJointAnimationPriorities(); // Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update. // The handler will be called with an animStateDictionaryIn that has all those properties specified by the (possibly empty) // propertiesList argument. However for debugging, if the properties argument is null, all internal animGraph state is provided. diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index ac3365c272..f773651ddc 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -33,7 +33,7 @@ class QJsonObject; // * evaluate method, perform actual joint manipulations here and return result by reference. // Also, append any triggers that are detected during evaluation. -class AnimNode { +class AnimNode : public std::enable_shared_from_this { public: enum class Type { Clip = 0, @@ -78,6 +78,30 @@ public: void setCurrentFrame(float frame); + template + bool traverse(F func) { + if (func(shared_from_this())) { + for (auto&& child : _children) { + if (!child->traverse(func)) { + return false; + } + } + } + return true; + } + + Pointer findByName(const QString& id) { + Pointer result; + traverse([&](Pointer node) { + if (id == node->getID()) { + result = node; + return true; + } + return false; + }); + return result; + } + protected: virtual void setCurrentFrameInternal(float frame) {} diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index e0369b23a4..3d653985f3 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -79,6 +79,8 @@ void Rig::startAnimation(const QString& url, float fps, bool loop, float firstFr bool hold = true; QStringList maskedJoints; + _currentUserAnimURL = url; + // This is different than startAnimationByRole, in which we use the existing values if the animation already exists. // Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted). AnimationHandlePointer handle = nullptr; @@ -102,79 +104,12 @@ void Rig::startAnimation(const QString& url, float fps, bool loop, float firstFr handle->start(); } } - -AnimationHandlePointer Rig::addAnimationByRole(const QString& role, const QString& url, float fps, float priority, - bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints, bool startAutomatically) { - - // check for a configured animation for the role - //qCDebug(animation) << "addAnimationByRole" << role << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints << startAutomatically; - foreach (const AnimationHandlePointer& candidate, _animationHandles) { - if (candidate->getRole() == role) { - if (startAutomatically) { - candidate->start(); - } - return candidate; - } - } - AnimationHandlePointer handle = createAnimationHandle(); - QString standard = ""; - if (url.isEmpty()) { // Default animations for fight club - const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/"; - if (role == "walk") { - standard = base + "walk_fwd.fbx"; - } else if (role == "backup") { - standard = base + "walk_bwd.fbx"; - } else if (role == "leftTurn") { - standard = base + "turn_left.fbx"; - } else if (role == "rightTurn") { - standard = base + "turn_right.fbx"; - } else if (role == "leftStrafe") { - standard = base + "strafe_left.fbx"; - } else if (role == "rightStrafe") { - standard = base + "strafe_right.fbx"; - } else if (role == "idle") { - standard = base + "idle.fbx"; - fps = 25.0f; - } - if (!standard.isEmpty()) { - loop = true; - } - } - handle->setRole(role); - handle->setURL(url.isEmpty() ? standard : url); - handle->setFPS(fps); - handle->setPriority(priority); - handle->setLoop(loop); - handle->setHold(hold); - handle->setFirstFrame(firstFrame); - handle->setLastFrame(lastFrame); - handle->setMaskedJoints(maskedJoints); - if (startAutomatically) { - handle->start(); - } - return handle; -} - -const float FADE_FRAMES = 30.0f; const float FRAMES_PER_SECOND = 30.0f; +const float FADE_FRAMES = 30.0f; -void Rig::startAnimationByRole(const QString& role, const QString& url, float fps, float priority, - bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) { - AnimationHandlePointer handle = addAnimationByRole(role, url, fps, priority, loop, hold, firstFrame, lastFrame, maskedJoints, true); - handle->setFadePerSecond(FRAMES_PER_SECOND / FADE_FRAMES); // For now. Could be individualized later. -} - -void Rig::stopAnimationByRole(const QString& role) { - foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { - if (handle->getRole() == role) { - handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // For now. Could be individualized later. - } - } -} - -void Rig::stopAnimation(const QString& url) { +void Rig::stopAnimation() { if (_enableAnimGraph) { - if (url == _currentUserAnimURL) { + if (_currentUserAnimURL != "") { _currentUserAnimURL = ""; // notify the userAnimStateMachine the desired state. _animVars.set("userAnimNone", true); @@ -183,7 +118,7 @@ void Rig::stopAnimation(const QString& url) { } } else { foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { - if (handle->getURL() == url) { + if (handle->getURL() == _currentUserAnimURL) { handle->setFade(0.0f); // right away. Will be remove during updateAnimations, without locking handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // so that the updateAnimation code notices } @@ -191,6 +126,59 @@ void Rig::stopAnimation(const QString& url) { } } +QStringList Rig::getAnimationRoles() const { + if (_enableAnimGraph && _animNode) { + QStringList list; + _animNode->traverse([&](AnimNode::Pointer node) { + list.append(node->getID()); + return true; + }); + return list; + } else { + return QStringList(); + } +} + +void Rig::overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { + if (_enableAnimGraph && _animNode) { + AnimNode::Pointer node = _animNode->findByName(role); + if (node) { + //_previousRoleAnimations[role] = node; + // TODO: create clip node. + // TODO: AnimNode needs the following methods. + // Pointer getParent() const; + // void swapChild(Pointer child, Pointer newChild); + // + // pseudo code + // + // auto clipNode = std::make_shared(role, url, fps, firstFrame, lastFrame, loop); + // node->getParent()->swapChild(node, clipNode); + + } else { + qCWarning(animation) << "Rig::overrideAnimationRole could not find role " << role; + } + } +} + +void Rig::restoreAnimationRole(const QString& role) { + if (_enableAnimGraph && _animNode) { + AnimNode::Pointer node = _animNode->findByName(role); + if (node) { + // TODO: pseudo code + // origNode = _previousRoleAnimations.find(role); + // if (origNode) { + // node->getParent()->swapChild(node, origNode); + // } + } + } +} + +void Rig::prefetchAnimation(const QString& url) { + if (_enableAnimGraph) { + // TODO: + } +} + bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { return _runningAnimations.removeOne(animationHandle); } @@ -661,13 +649,13 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos if (isOn) { if (!isRunningRole(role)) { qCDebug(animation) << "Rig STARTING" << role; - startAnimationByRole(role); + //startAnimationByRole(role); } } else { if (isRunningRole(role)) { qCDebug(animation) << "Rig stopping" << role; - stopAnimationByRole(role); + //stopAnimationByRole(role); } } }; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index f36dd19dda..eb7f07f653 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -113,15 +113,13 @@ public: void deleteAnimations(); void destroyAnimGraph(); const QList& getAnimationHandles() const { return _animationHandles; } + void startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - void stopAnimation(const QString& url); - void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, - float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, - float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList()); - void stopAnimationByRole(const QString& role); - AnimationHandlePointer addAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f, - float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f, - float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false); + void stopAnimation(); + QStringList getAnimationRoles() const; + void overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + void restoreAnimationRole(const QString& role); + void prefetchAnimation(const QString& url); void initJointStates(QVector states, glm::mat4 rootTransform, int rootJointIndex, From 936c55a94ee99fe945a774e4a6d67a18e4b224db Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 10 Nov 2015 16:34:38 -0800 Subject: [PATCH 04/84] New MyAvatar animation JS interface JavaScript changes: * removed MyAvatar.playAnimation * removed MyAvatar.stopAnimation * removed MyAVatar.getGetAnimationDetails * removed MyAvatar.startAnimationByRole * removed MyAvatar.stopAnimationByRole * removed MyAVatar.getGetAnimationDetailsByRole * removed MyAVatar.clearJointPriorities * added MyAvatar.overrideAnimation(url, fps, loop, firstFrame, lastFrame) * added MyAvatar.restoreAnimation() * added MyAvatar.getAnimationRoles() * added MyAvatar.overrideRoleAnimation(role, url, fps, loop, firstFrame, lastFrame) * added MyAvatar.restoreRoleAnimation(role) * added MyAvatar.prefetchAnimation(url) * update kneel.js with new api. * added theBird.js to test role override api. C++ changes: * Added getParent() and replaceChild() to AnimNode * Added findByName() and traverse() to AnimNode * Changed AnimStateMachine to hold nodes by childIndex instead of smart pointer. This allows script to replace nodes dynamically via overrideRoleAnimation --- examples/kneel.js | 4 +- examples/theBird.js | 32 ++++++++++++ interface/src/avatar/MyAvatar.cpp | 24 ++++----- interface/src/avatar/MyAvatar.h | 10 ++-- libraries/animation/src/AnimNode.cpp | 27 ++++++++++- libraries/animation/src/AnimNode.h | 12 +++-- libraries/animation/src/AnimNodeLoader.cpp | 8 +-- libraries/animation/src/AnimStateMachine.cpp | 6 +-- libraries/animation/src/AnimStateMachine.h | 8 +-- libraries/animation/src/Rig.cpp | 51 +++++++++++--------- libraries/animation/src/Rig.h | 10 ++-- 11 files changed, 128 insertions(+), 64 deletions(-) create mode 100644 examples/theBird.js diff --git a/examples/kneel.js b/examples/kneel.js index 3270a77df7..246655949c 100644 --- a/examples/kneel.js +++ b/examples/kneel.js @@ -57,7 +57,7 @@ function kneelDown() { var startFrame = 0; var endFrame = 82; - MyAvatar.startAnimation(KNEEL_ANIM_URL, playbackRate, loopFlag, startFrame, endFrame); + MyAvatar.overrideAnimation(KNEEL_ANIM_URL, playbackRate, loopFlag, startFrame, endFrame); Overlays.editOverlay(kneelDownButton, { visible: false }); Overlays.editOverlay(standUpButton, { visible: true }); @@ -66,7 +66,7 @@ function kneelDown() { function standUp() { kneeling = false; - MyAvatar.stopAnimation(); + MyAvatar.restoreAnimation(); Overlays.editOverlay(standUpButton, { visible: false }); Overlays.editOverlay(kneelDownButton, { visible: true }); diff --git a/examples/theBird.js b/examples/theBird.js new file mode 100644 index 0000000000..c7fd5a38b7 --- /dev/null +++ b/examples/theBird.js @@ -0,0 +1,32 @@ +// +// theBird.js +// examples +// +// Created by Anthony Thibault on 11/9/2015 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// Example of how to play an animation on an avatar. +// + +var THE_BIRD_RIGHT_URL = "https://hifi-public.s3.amazonaws.com/ozan/anim/the_bird/the_bird_right.fbx"; + +var roles = MyAvatar.getAnimationRoles(); +var i, l = roles.length +print("getAnimationRoles()"); +for (i = 0; i < l; i++) { + print(roles[i]); +} + +// replace point with the bird! +MyAvatar.overrideRoleAnimation("rightHandPointIntro", THE_BIRD_RIGHT_URL, 30, false, 0, 12); +MyAvatar.overrideRoleAnimation("rightHandPointHold", THE_BIRD_RIGHT_URL, 30, false, 12, 12); +MyAvatar.overrideRoleAnimation("rightHandPointOutro", THE_BIRD_RIGHT_URL, 30, false, 19, 30); + +Script.scriptEnding.connect(function() { + MyAvatar.restoreRoleAnimation("rightHandPointIntro"); + MyAvatar.restoreRoleAnimation("rightHandPointHold"); + MyAvatar.restoreRoleAnimation("rightHandPointOutro"); +}); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 14b66c5465..4dbc6494b8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -657,21 +657,21 @@ void MyAvatar::loadLastRecording() { _player->loadRecording(_recorder->getRecording()); } -void MyAvatar::startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { +void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), + QMetaObject::invokeMethod(this, "overrideAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps), Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); return; } - _rig->startAnimation(url, fps, loop, firstFrame, lastFrame); + _rig->overrideAnimation(url, fps, loop, firstFrame, lastFrame); } -void MyAvatar::stopAnimation() { +void MyAvatar::restoreAnimation() { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "stopAnimation"); + QMetaObject::invokeMethod(this, "restoreAnimation"); return; } - _rig->stopAnimation(); + _rig->restoreAnimation(); } QStringList MyAvatar::getAnimationRoles() { @@ -683,22 +683,22 @@ QStringList MyAvatar::getAnimationRoles() { return _rig->getAnimationRoles(); } -void MyAvatar::overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, +void MyAvatar::overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "overrideAnimationRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url), + QMetaObject::invokeMethod(this, "overrideRoleAnimation", Q_ARG(const QString&, role), Q_ARG(const QString&, url), Q_ARG(float, fps), Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame)); return; } - _rig->overrideAnimationRole(role, url, fps, loop, firstFrame, lastFrame); + _rig->overrideRoleAnimation(role, url, fps, loop, firstFrame, lastFrame); } -void MyAvatar::restoreAnimationRole(const QString& role) { +void MyAvatar::restoreRoleAnimation(const QString& role) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "restoreAnimationRole", Q_ARG(const QString&, role)); + QMetaObject::invokeMethod(this, "restoreRoleAnimation", Q_ARG(const QString&, role)); return; } - _rig->restoreAnimationRole(role); + _rig->restoreRoleAnimation(role); } void MyAvatar::prefetchAnimation(const QString& url) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 2e5f13f81d..1331291f03 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -119,19 +119,19 @@ public: void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); } // Interrupt the current animation with a custom animation. - Q_INVOKABLE void startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - // Stops the animation that was started with startAnimation and go back to the standard animation. - Q_INVOKABLE void stopAnimation(); + // Stop the animation that was started with overrideAnimation and go back to the standard animation. + Q_INVOKABLE void restoreAnimation(); // Returns a list of all clips that are available Q_INVOKABLE QStringList getAnimationRoles(); // Replace an existing standard role animation with a custom one. - Q_INVOKABLE void overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + Q_INVOKABLE void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); // remove an animation role override and return to the standard animation. - Q_INVOKABLE void restoreAnimationRole(const QString& role); + Q_INVOKABLE void restoreRoleAnimation(const QString& role); // prefetch animation Q_INVOKABLE void prefetchAnimation(const QString& url); diff --git a/libraries/animation/src/AnimNode.cpp b/libraries/animation/src/AnimNode.cpp index 864252ff47..80093e43db 100644 --- a/libraries/animation/src/AnimNode.cpp +++ b/libraries/animation/src/AnimNode.cpp @@ -8,12 +8,35 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "AnimNode.h" -void AnimNode::removeChild(AnimNode::Pointer child) { +AnimNode::Pointer AnimNode::getParent() { + return _parent.lock(); +} + +void AnimNode::addChild(Pointer child) { + _children.push_back(child); + child->_parent = shared_from_this(); +} + +void AnimNode::removeChild(Pointer child) { auto iter = std::find(_children.begin(), _children.end(), child); if (iter != _children.end()) { _children.erase(iter); + child->_parent.reset(); + } +} + +void AnimNode::replaceChild(Pointer oldChild, Pointer newChild) { + auto iter = std::find(_children.begin(), _children.end(), oldChild); + if (iter != _children.end()) { + oldChild->_parent.reset(); + newChild->_parent = shared_from_this(); + if (_skeleton) { + newChild->setSkeleton(_skeleton); + } + *iter = newChild; } } @@ -22,7 +45,7 @@ AnimNode::Pointer AnimNode::getChild(int i) const { return _children[i]; } -void AnimNode::setSkeleton(const AnimSkeleton::Pointer skeleton) { +void AnimNode::setSkeleton(AnimSkeleton::ConstPointer skeleton) { setSkeletonInternal(skeleton); for (auto&& child : _children) { child->setSkeleton(skeleton); diff --git a/libraries/animation/src/AnimNode.h b/libraries/animation/src/AnimNode.h index f773651ddc..23f2e1c7b3 100644 --- a/libraries/animation/src/AnimNode.h +++ b/libraries/animation/src/AnimNode.h @@ -60,14 +60,15 @@ public: Type getType() const { return _type; } // hierarchy accessors - void addChild(Pointer child) { _children.push_back(child); } + Pointer getParent(); + void addChild(Pointer child); void removeChild(Pointer child); - + void replaceChild(Pointer oldChild, Pointer newChild); Pointer getChild(int i) const; int getChildCount() const { return (int)_children.size(); } // pair this AnimNode graph with a skeleton. - void setSkeleton(const AnimSkeleton::Pointer skeleton); + void setSkeleton(AnimSkeleton::ConstPointer skeleton); AnimSkeleton::ConstPointer getSkeleton() const { return _skeleton; } @@ -95,9 +96,9 @@ public: traverse([&](Pointer node) { if (id == node->getID()) { result = node; - return true; + return false; } - return false; + return true; }); return result; } @@ -114,6 +115,7 @@ protected: QString _id; std::vector _children; AnimSkeleton::ConstPointer _skeleton; + std::weak_ptr _parent; // no copies AnimNode(const AnimNode&) = delete; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 2a52e04e1d..83f72b9b5a 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -393,9 +393,9 @@ AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QS return node; } -void buildChildMap(std::map& map, AnimNode::Pointer node) { - for ( auto child : node->_children ) { - map.insert(std::pair(child->_id, child)); +void buildChildMap(std::map& map, AnimNode::Pointer node) { + for (int i = 0; i < (int)node->getChildCount(); ++i) { + map.insert(std::pair(node->getChild(i)->getID(), i)); } } @@ -412,7 +412,7 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, } // build a map for all children by name. - std::map childMap; + std::map childMap; buildChildMap(childMap, node); // first pass parse all the states and build up the state and transition map. diff --git a/libraries/animation/src/AnimStateMachine.cpp b/libraries/animation/src/AnimStateMachine.cpp index c0bb66c2a0..3f5a81a861 100644 --- a/libraries/animation/src/AnimStateMachine.cpp +++ b/libraries/animation/src/AnimStateMachine.cpp @@ -46,7 +46,7 @@ const AnimPoseVec& AnimStateMachine::evaluate(const AnimVariantMap& animVars, fl } assert(_currentState); - auto currentStateNode = _currentState->getNode(); + auto currentStateNode = _children[_currentState->getChildIndex()]; assert(currentStateNode); if (_duringInterp) { @@ -79,8 +79,8 @@ void AnimStateMachine::switchState(const AnimVariantMap& animVars, State::Pointe const float FRAMES_PER_SECOND = 30.0f; - auto prevStateNode = _currentState->getNode(); - auto nextStateNode = desiredState->getNode(); + auto prevStateNode = _children[_currentState->getChildIndex()]; + auto nextStateNode = _children[desiredState->getChildIndex()]; _duringInterp = true; _alpha = 0.0f; diff --git a/libraries/animation/src/AnimStateMachine.h b/libraries/animation/src/AnimStateMachine.h index cb6c99f067..dda56235d5 100644 --- a/libraries/animation/src/AnimStateMachine.h +++ b/libraries/animation/src/AnimStateMachine.h @@ -55,16 +55,16 @@ protected: State::Pointer _state; }; - State(const QString& id, AnimNode::Pointer node, float interpTarget, float interpDuration) : + State(const QString& id, int childIndex, float interpTarget, float interpDuration) : _id(id), - _node(node), + _childIndex(childIndex), _interpTarget(interpTarget), _interpDuration(interpDuration) {} void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; } void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; } - AnimNode::Pointer getNode() const { return _node; } + int getChildIndex() const { return _childIndex; } const QString& getID() const { return _id; } protected: @@ -75,7 +75,7 @@ protected: void addTransition(const Transition& transition) { _transitions.push_back(transition); } QString _id; - AnimNode::Pointer _node; + int _childIndex; float _interpTarget; // frames float _interpDuration; // frames diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 3d653985f3..e1cc5ec7f5 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -45,7 +45,7 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) { _animationHandles.removeOne(handle); } -void Rig::startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { +void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { if (_enableAnimGraph) { // find an unused AnimClip clipNode @@ -63,7 +63,8 @@ void Rig::startAnimation(const QString& url, float fps, bool loop, float firstFr clip->setStartFrame(firstFrame); clip->setEndFrame(lastFrame); const float REFERENCE_FRAMES_PER_SECOND = 30.0f; - clip->setTimeScale(fps / REFERENCE_FRAMES_PER_SECOND); + float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; + clip->setTimeScale(timeScale); clip->loadURL(url); _currentUserAnimURL = url; @@ -107,7 +108,7 @@ void Rig::startAnimation(const QString& url, float fps, bool loop, float firstFr const float FRAMES_PER_SECOND = 30.0f; const float FADE_FRAMES = 30.0f; -void Rig::stopAnimation() { +void Rig::restoreAnimation() { if (_enableAnimGraph) { if (_currentUserAnimURL != "") { _currentUserAnimURL = ""; @@ -130,7 +131,14 @@ QStringList Rig::getAnimationRoles() const { if (_enableAnimGraph && _animNode) { QStringList list; _animNode->traverse([&](AnimNode::Pointer node) { - list.append(node->getID()); + // only report clip nodes as valid roles. + auto clipNode = std::dynamic_pointer_cast(node); + if (clipNode) { + // filter out the userAnims, they are for internal use only. + if (!clipNode->getID().startsWith("userAnim")) { + list.append(node->getID()); + } + } return true; }); return list; @@ -139,36 +147,33 @@ QStringList Rig::getAnimationRoles() const { } } -void Rig::overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { +void Rig::overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { if (_enableAnimGraph && _animNode) { AnimNode::Pointer node = _animNode->findByName(role); if (node) { - //_previousRoleAnimations[role] = node; - // TODO: create clip node. - // TODO: AnimNode needs the following methods. - // Pointer getParent() const; - // void swapChild(Pointer child, Pointer newChild); - // - // pseudo code - // - // auto clipNode = std::make_shared(role, url, fps, firstFrame, lastFrame, loop); - // node->getParent()->swapChild(node, clipNode); - + _origRoleAnimations[role] = node; + const float REFERENCE_FRAMES_PER_SECOND = 30.0f; + float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; + auto clipNode = std::make_shared(role, url, firstFrame, lastFrame, timeScale, loop); + AnimNode::Pointer parent = node->getParent(); + parent->replaceChild(node, clipNode); } else { - qCWarning(animation) << "Rig::overrideAnimationRole could not find role " << role; + qCWarning(animation) << "Rig::overrideRoleAnimation could not find role " << role; } } } -void Rig::restoreAnimationRole(const QString& role) { +void Rig::restoreRoleAnimation(const QString& role) { if (_enableAnimGraph && _animNode) { AnimNode::Pointer node = _animNode->findByName(role); if (node) { - // TODO: pseudo code - // origNode = _previousRoleAnimations.find(role); - // if (origNode) { - // node->getParent()->swapChild(node, origNode); - // } + auto iter = _origRoleAnimations.find(role); + if (iter != _origRoleAnimations.end()) { + node->getParent()->replaceChild(node, iter->second); + _origRoleAnimations.erase(iter); + } else { + qCWarning(animation) << "Rig::restoreRoleAnimation could not find role " << role; + } } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index eb7f07f653..cd5152152a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -114,11 +114,11 @@ public: void destroyAnimGraph(); const QList& getAnimationHandles() const { return _animationHandles; } - void startAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - void stopAnimation(); + void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + void restoreAnimation(); QStringList getAnimationRoles() const; - void overrideAnimationRole(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); - void restoreAnimationRole(const QString& role); + void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); + void restoreRoleAnimation(const QString& role); void prefetchAnimation(const QString& url); void initJointStates(QVector states, glm::mat4 rootTransform, @@ -264,6 +264,8 @@ public: SimpleMovingAverage _averageForwardSpeed { 10 }; SimpleMovingAverage _averageLateralSpeed { 10 }; + std::map _origRoleAnimations; + private: QMap _stateHandlers; int _nextStateHandlerId { 0 }; From e1d0a97807a89b7bdbb4e118380e9518333ed6f0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 10 Nov 2015 16:51:48 -0800 Subject: [PATCH 05/84] Implemented MyAvatar.prefetchAnimation() JS method --- examples/theBird.js | 4 +++- libraries/animation/src/Rig.cpp | 5 ++++- libraries/animation/src/Rig.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/theBird.js b/examples/theBird.js index c7fd5a38b7..02b2e7fc5d 100644 --- a/examples/theBird.js +++ b/examples/theBird.js @@ -20,7 +20,9 @@ for (i = 0; i < l; i++) { print(roles[i]); } -// replace point with the bird! +MyAvatar.prefetchAnimation(THE_BIRD_RIGHT_URL); + +// replace point animations with the bird! MyAvatar.overrideRoleAnimation("rightHandPointIntro", THE_BIRD_RIGHT_URL, 30, false, 0, 12); MyAvatar.overrideRoleAnimation("rightHandPointHold", THE_BIRD_RIGHT_URL, 30, false, 12, 12); MyAvatar.overrideRoleAnimation("rightHandPointOutro", THE_BIRD_RIGHT_URL, 30, false, 19, 30); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index e1cc5ec7f5..94e98457fd 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -180,7 +180,10 @@ void Rig::restoreRoleAnimation(const QString& role) { void Rig::prefetchAnimation(const QString& url) { if (_enableAnimGraph) { - // TODO: + // This will begin loading the NetworkGeometry for the given URL. + // which should speed us up if we request it later via overrideAnimation. + auto clipNode = std::make_shared("prefetch", url, 0, 0, 1.0, false); + _prefetchedAnimations.push_back(clipNode); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index cd5152152a..191029f2af 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -265,6 +265,7 @@ public: SimpleMovingAverage _averageLateralSpeed { 10 }; std::map _origRoleAnimations; + std::vector _prefetchedAnimations; private: QMap _stateHandlers; From cce967df944300130b69f9ab6e2aa40a9c5c49d9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 11 Nov 2015 12:14:56 -0800 Subject: [PATCH 06/84] Removed AnimationsDialog --- interface/src/Menu.cpp | 2 - interface/src/ui/AnimationsDialog.cpp | 210 -------------------------- interface/src/ui/AnimationsDialog.h | 87 ----------- interface/src/ui/DialogsManager.cpp | 10 -- interface/src/ui/DialogsManager.h | 1 - 5 files changed, 310 deletions(-) delete mode 100644 interface/src/ui/AnimationsDialog.cpp delete mode 100644 interface/src/ui/AnimationsDialog.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 24033325f6..a8af92bafe 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -145,8 +145,6 @@ Menu::Menu() { addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, dialogsManager.data(), SLOT(editAttachments())); - addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, - dialogsManager.data(), SLOT(editAnimations())); MenuWrapper* toolsMenu = addMenu("Tools"); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, diff --git a/interface/src/ui/AnimationsDialog.cpp b/interface/src/ui/AnimationsDialog.cpp deleted file mode 100644 index 5960ffc1fa..0000000000 --- a/interface/src/ui/AnimationsDialog.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// -// AnimationsDialog.cpp -// interface/src/ui -// -// Created by Andrzej Kapolka on 5/19/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "avatar/AvatarManager.h" - -#include "AnimationsDialog.h" - -AnimationsDialog::AnimationsDialog(QWidget* parent) : - QDialog(parent) -{ - setWindowTitle("Edit Animations"); - setAttribute(Qt::WA_DeleteOnClose); - - QVBoxLayout* layout = new QVBoxLayout(); - setLayout(layout); - - QScrollArea* area = new QScrollArea(); - layout->addWidget(area); - area->setWidgetResizable(true); - QWidget* container = new QWidget(); - container->setLayout(_animations = new QVBoxLayout()); - container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); - area->setWidget(container); - _animations->addStretch(1); - - MyAvatar* myAvatar = DependencyManager::get()->getMyAvatar(); - foreach (const AnimationHandlePointer& handle, myAvatar->getAnimationHandles()) { - _animations->insertWidget(_animations->count() - 1, new AnimationPanel(this, handle)); - } - - QPushButton* newAnimation = new QPushButton("New Animation"); - connect(newAnimation, SIGNAL(clicked(bool)), SLOT(addAnimation())); - layout->addWidget(newAnimation); - - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok); - layout->addWidget(buttons); - connect(buttons, SIGNAL(accepted()), SLOT(deleteLater())); - _ok = buttons->button(QDialogButtonBox::Ok); - - setMinimumSize(600, 600); -} - -void AnimationsDialog::setVisible(bool visible) { - QDialog::setVisible(visible); - - // un-default the OK button - if (visible) { - _ok->setDefault(false); - } -} - -void AnimationsDialog::addAnimation() { - _animations->insertWidget(_animations->count() - 1, new AnimationPanel(this, - DependencyManager::get()->getMyAvatar()->addAnimationHandle())); -} - -Setting::Handle AnimationPanel::_animationDirectory("animation_directory", QString()); - -AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePointer& handle) : - _dialog(dialog), - _handle(handle), - _applying(false) { - setFrameStyle(QFrame::StyledPanel); - - QFormLayout* layout = new QFormLayout(); - layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); - setLayout(layout); - - layout->addRow("Role:", _role = new QComboBox()); - _role->addItem("idle"); - _role->addItem("sit"); - _role->setEditable(true); - _role->setCurrentText(handle->getRole()); - connect(_role, SIGNAL(currentTextChanged(const QString&)), SLOT(updateHandle())); - - QHBoxLayout* urlBox = new QHBoxLayout(); - layout->addRow("URL:", urlBox); - urlBox->addWidget(_url = new QLineEdit(handle->getURL().toString()), 1); - connect(_url, SIGNAL(editingFinished()), SLOT(updateHandle())); - QPushButton* chooseURL = new QPushButton("Choose"); - urlBox->addWidget(chooseURL); - connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseURL())); - - layout->addRow("FPS:", _fps = new QDoubleSpinBox()); - _fps->setSingleStep(0.01); - _fps->setMinimum(-FLT_MAX); - _fps->setMaximum(FLT_MAX); - _fps->setValue(handle->getFPS()); - connect(_fps, SIGNAL(valueChanged(double)), SLOT(updateHandle())); - - layout->addRow("Priority:", _priority = new QDoubleSpinBox()); - _priority->setSingleStep(0.01); - _priority->setMinimum(-FLT_MAX); - _priority->setMaximum(FLT_MAX); - _priority->setValue(handle->getPriority()); - connect(_priority, SIGNAL(valueChanged(double)), SLOT(updateHandle())); - - QHBoxLayout* maskedJointBox = new QHBoxLayout(); - layout->addRow("Masked Joints:", maskedJointBox); - maskedJointBox->addWidget(_maskedJoints = new QLineEdit(handle->getMaskedJoints().join(", ")), 1); - connect(_maskedJoints, SIGNAL(editingFinished()), SLOT(updateHandle())); - maskedJointBox->addWidget(_chooseMaskedJoints = new QPushButton("Choose")); - connect(_chooseMaskedJoints, SIGNAL(clicked(bool)), SLOT(chooseMaskedJoints())); - - layout->addRow("Loop:", _loop = new QCheckBox()); - _loop->setChecked(handle->getLoop()); - connect(_loop, SIGNAL(toggled(bool)), SLOT(updateHandle())); - - layout->addRow("Hold:", _hold = new QCheckBox()); - _hold->setChecked(handle->getHold()); - connect(_hold, SIGNAL(toggled(bool)), SLOT(updateHandle())); - - layout->addRow("Start Automatically:", _startAutomatically = new QCheckBox()); - _startAutomatically->setChecked(handle->getStartAutomatically()); - connect(_startAutomatically, SIGNAL(toggled(bool)), SLOT(updateHandle())); - - layout->addRow("First Frame:", _firstFrame = new QDoubleSpinBox()); - _firstFrame->setSingleStep(0.01); - _firstFrame->setMaximum(INT_MAX); - _firstFrame->setValue(handle->getFirstFrame()); - connect(_firstFrame, SIGNAL(valueChanged(double)), SLOT(updateHandle())); - - layout->addRow("Last Frame:", _lastFrame = new QDoubleSpinBox()); - _lastFrame->setSingleStep(0.01); - _lastFrame->setMaximum(INT_MAX); - _lastFrame->setValue(handle->getLastFrame()); - connect(_lastFrame, SIGNAL(valueChanged(double)), SLOT(updateHandle())); - - QHBoxLayout* buttons = new QHBoxLayout(); - layout->addRow(buttons); - buttons->addWidget(_start = new QPushButton("Start")); - _handle->connect(_start, SIGNAL(clicked(bool)), SLOT(start())); - buttons->addWidget(_stop = new QPushButton("Stop")); - _handle->connect(_stop, SIGNAL(clicked(bool)), SLOT(stop())); - QPushButton* remove = new QPushButton("Delete"); - buttons->addWidget(remove); - connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle())); - - _stop->connect(_handle.get(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool))); - _stop->setEnabled(_handle->isRunning()); -} - -void AnimationPanel::chooseURL() { - QString filename = QFileDialog::getOpenFileName(this, "Choose Animation", - _animationDirectory.get(), "Animation files (*.fbx)"); - if (filename.isEmpty()) { - return; - } - _animationDirectory.set(QFileInfo(filename).path()); - _url->setText(QUrl::fromLocalFile(filename).toString()); - emit _url->editingFinished(); -} - -void AnimationPanel::chooseMaskedJoints() { - QMenu menu; - QStringList maskedJoints = _handle->getMaskedJoints(); - foreach (const QString& jointName, DependencyManager::get()->getMyAvatar()->getJointNames()) { - QAction* action = menu.addAction(jointName); - action->setCheckable(true); - action->setChecked(maskedJoints.contains(jointName)); - } - QAction* action = menu.exec(_chooseMaskedJoints->mapToGlobal(QPoint(0, 0))); - if (action) { - if (action->isChecked()) { - maskedJoints.append(action->text()); - } else { - maskedJoints.removeOne(action->text()); - } - _handle->setMaskedJoints(maskedJoints); - _maskedJoints->setText(maskedJoints.join(", ")); - } -} - -void AnimationPanel::updateHandle() { - _handle->setRole(_role->currentText()); - _handle->setURL(_url->text()); - _handle->setFPS(_fps->value()); - _handle->setPriority(_priority->value()); - _handle->setLoop(_loop->isChecked()); - _handle->setHold(_hold->isChecked()); - _handle->setStartAutomatically(_startAutomatically->isChecked()); - _handle->setFirstFrame(_firstFrame->value()); - _handle->setLastFrame(_lastFrame->value()); - _handle->setMaskedJoints(_maskedJoints->text().split(QRegExp("\\s*,\\s*"))); -} - -void AnimationPanel::removeHandle() { - DependencyManager::get()->getMyAvatar()->removeAnimationHandle(_handle); - deleteLater(); -} diff --git a/interface/src/ui/AnimationsDialog.h b/interface/src/ui/AnimationsDialog.h deleted file mode 100644 index ace13b6859..0000000000 --- a/interface/src/ui/AnimationsDialog.h +++ /dev/null @@ -1,87 +0,0 @@ -// -// AnimationsDialog.h -// interface/src/ui -// -// Created by Andrzej Kapolka on 5/19/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AnimationsDialog_h -#define hifi_AnimationsDialog_h - -#include -#include -#include - -#include - -#include "avatar/MyAvatar.h" - -class QCheckBox; -class QComboBox; -class QDoubleSpinner; -class QLineEdit; -class QPushButton; -class QVBoxLayout; - -/// Allows users to edit the avatar animations. -class AnimationsDialog : public QDialog { - Q_OBJECT - -public: - - AnimationsDialog(QWidget* parent = nullptr); - - virtual void setVisible(bool visible); - -private slots: - - void addAnimation(); - -private: - - QVBoxLayout* _animations = nullptr; - QPushButton* _ok = nullptr; -}; - -/// A panel controlling a single animation. -class AnimationPanel : public QFrame { - Q_OBJECT - -public: - - AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePointer& handle); - -private slots: - - void chooseURL(); - void chooseMaskedJoints(); - void updateHandle(); - void removeHandle(); - -private: - - AnimationsDialog* _dialog = nullptr; - AnimationHandlePointer _handle; - QComboBox* _role = nullptr; - QLineEdit* _url = nullptr; - QDoubleSpinBox* _fps = nullptr; - QDoubleSpinBox* _priority = nullptr; - QCheckBox* _loop = nullptr; - QCheckBox* _hold = nullptr; - QCheckBox* _startAutomatically = nullptr; - QDoubleSpinBox* _firstFrame = nullptr; - QDoubleSpinBox* _lastFrame = nullptr; - QLineEdit* _maskedJoints = nullptr; - QPushButton* _chooseMaskedJoints = nullptr; - QPushButton* _start = nullptr; - QPushButton* _stop = nullptr; - bool _applying; - - static Setting::Handle _animationDirectory; -}; - -#endif // hifi_AnimationsDialog_h diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 00bc95b5fa..1acbf3a595 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -19,7 +19,6 @@ #include #include "AddressBarDialog.h" -#include "AnimationsDialog.h" #include "AttachmentsDialog.h" #include "BandwidthDialog.h" #include "CachesSizeDialog.h" @@ -110,15 +109,6 @@ void DialogsManager::editAttachments() { } } -void DialogsManager::editAnimations() { - if (!_animationsDialog) { - maybeCreateDialog(_animationsDialog); - _animationsDialog->show(); - } else { - _animationsDialog->close(); - } -} - void DialogsManager::audioStatsDetails() { if (! _audioStatsDialog) { _audioStatsDialog = new AudioStatsDialog(qApp->getWindow()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 133fe459d0..f6ad1d6f98 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -52,7 +52,6 @@ public slots: void cachesSizeDialog(); void editPreferences(); void editAttachments(); - void editAnimations(); void audioStatsDetails(); void bandwidthDetails(); void lodTools(); From 533773d1cd7ee7785a89a3784793ae8da61a1a8e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 11 Nov 2015 13:13:01 -0800 Subject: [PATCH 07/84] Removed animationHandles section from Avatar prefs --- interface/src/avatar/MyAvatar.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 980cac31b6..3dd2d337eb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -741,24 +741,6 @@ void MyAvatar::saveData() { } settings.endArray(); - settings.beginWriteArray("animationHandles"); - auto animationHandles = _rig->getAnimationHandles(); - for (int i = 0; i < animationHandles.size(); i++) { - settings.setArrayIndex(i); - const AnimationHandlePointer& pointer = animationHandles.at(i); - settings.setValue("role", pointer->getRole()); - settings.setValue("url", pointer->getURL()); - settings.setValue("fps", pointer->getFPS()); - settings.setValue("priority", pointer->getPriority()); - settings.setValue("loop", pointer->getLoop()); - settings.setValue("hold", pointer->getHold()); - settings.setValue("startAutomatically", pointer->getStartAutomatically()); - settings.setValue("firstFrame", pointer->getFirstFrame()); - settings.setValue("lastFrame", pointer->getLastFrame()); - settings.setValue("maskedJoints", pointer->getMaskedJoints()); - } - settings.endArray(); - settings.setValue("displayName", _displayName); settings.setValue("collisionSoundURL", _collisionSoundURL); From 11440f92f4e571ccc27a93c52263dfb7c669b41a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 11 Nov 2015 15:32:57 -0800 Subject: [PATCH 08/84] Removed rig animations * Deleted AnimationHandle class * Removed enableAnimGraph and anableRigAnimations from Menu. * Removed *some* references to old IK system. But it is still used when computing collision bounding volumes --- interface/src/Application.cpp | 6 - interface/src/Menu.cpp | 4 - interface/src/avatar/Avatar.h | 1 + interface/src/avatar/MyAvatar.cpp | 84 +----- interface/src/avatar/MyAvatar.h | 10 +- interface/src/avatar/SkeletonModel.cpp | 36 --- interface/src/ui/PreferencesDialog.cpp | 5 - libraries/animation/src/AnimationHandle.cpp | 218 -------------- libraries/animation/src/AnimationHandle.h | 138 --------- libraries/animation/src/Rig.cpp | 312 ++++---------------- libraries/animation/src/Rig.h | 25 -- libraries/render-utils/src/Model.cpp | 2 - libraries/render-utils/src/Model.h | 2 +- 13 files changed, 55 insertions(+), 788 deletions(-) delete mode 100644 libraries/animation/src/AnimationHandle.cpp delete mode 100644 libraries/animation/src/AnimationHandle.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 07ddd0a1f7..e35e6997ec 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2503,17 +2503,11 @@ void Application::setAvatarUpdateThreading(bool isThreaded) { } auto myAvatar = getMyAvatar(); - bool isRigEnabled = myAvatar->getEnableRigAnimations(); - bool isGraphEnabled = myAvatar->getEnableAnimGraph(); if (_avatarUpdate) { _avatarUpdate->terminate(); // Must be before we shutdown anim graph. } - myAvatar->setEnableRigAnimations(false); - myAvatar->setEnableAnimGraph(false); _avatarUpdate = new AvatarUpdate(); _avatarUpdate->initialize(isThreaded); - myAvatar->setEnableRigAnimations(isRigEnabled); - myAvatar->setEnableAnimGraph(isGraphEnabled); } void Application::updateLOD() { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a8af92bafe..b17aca94fb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -440,10 +440,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarUpdateThreading, 0, false, qApp, SLOT(setAvatarUpdateThreading(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableRigAnimations, 0, false, - avatar, SLOT(setEnableRigAnimations(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAnimGraph, 0, true, - avatar, SLOT(setEnableAnimGraph(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBindPose, 0, false, avatar, SLOT(setEnableDebugDrawBindPose(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false, diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 44b5d91015..2d0ab029ef 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -26,6 +26,7 @@ #include "Head.h" #include "SkeletonModel.h" #include "world.h" +#include "Rig.h" namespace render { template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3dd2d337eb..94e644b22e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -143,16 +142,9 @@ QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) { } void MyAvatar::reset(bool andReload) { - // Gather animation mode... - // This should be simpler when we have only graph animations always on. - bool isRig = _rig->getEnableRig(); - // seting rig animation to true, below, will clear the graph animation menu item, so grab it now. - bool isGraph = _rig->getEnableAnimGraph() || Menu::getInstance()->isOptionChecked(MenuOption::EnableAnimGraph); - // ... and get to sane configuration where other activity won't bother us. + if (andReload) { qApp->setRawAvatarUpdateThreading(false); - _rig->disableHands = true; - setEnableRigAnimations(true); } // Reset dynamic state. @@ -189,19 +181,6 @@ void MyAvatar::reset(bool andReload) { //_bodySensorMatrix = newBodySensorMatrix; //updateSensorToWorldMatrix(); // Uses updated position/orientation and _bodySensorMatrix changes - _skeletonModel.simulate(0.1f); // non-zero - setEnableRigAnimations(false); - _skeletonModel.simulate(0.1f); - } - if (isRig) { - setEnableRigAnimations(true); - Menu::getInstance()->setIsOptionChecked(MenuOption::EnableRigAnimations, true); - } else if (isGraph) { - setEnableAnimGraph(true); - Menu::getInstance()->setIsOptionChecked(MenuOption::EnableAnimGraph, true); - } - if (andReload) { - _rig->disableHands = false; qApp->setRawAvatarUpdateThreading(); } } @@ -760,57 +739,6 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) { // Meanwhile, the main thread will also eventually lock as it tries to render us. // If we demand the animation from the update thread while we're locked, we'll deadlock. // Until we untangle this, code puts the updates back on the main thread temporarilly and starts all the loading. -void MyAvatar::safelyLoadAnimations() { - /* - _rig->addAnimationByRole("idle"); - _rig->addAnimationByRole("walk"); - _rig->addAnimationByRole("backup"); - _rig->addAnimationByRole("leftTurn"); - _rig->addAnimationByRole("rightTurn"); - _rig->addAnimationByRole("leftStrafe"); - _rig->addAnimationByRole("rightStrafe"); - */ -} - -void MyAvatar::setEnableRigAnimations(bool isEnabled) { - if (_rig->getEnableRig() == isEnabled) { - return; - } - if (isEnabled) { - qApp->setRawAvatarUpdateThreading(false); - setEnableAnimGraph(false); - Menu::getInstance()->setIsOptionChecked(MenuOption::EnableAnimGraph, false); - safelyLoadAnimations(); - qApp->setRawAvatarUpdateThreading(); - _rig->setEnableRig(true); - } else { - _rig->setEnableRig(false); - _rig->deleteAnimations(); - } -} - -void MyAvatar::setEnableAnimGraph(bool isEnabled) { - if (_rig->getEnableAnimGraph() == isEnabled) { - return; - } - if (isEnabled) { - qApp->setRawAvatarUpdateThreading(false); - setEnableRigAnimations(false); - Menu::getInstance()->setIsOptionChecked(MenuOption::EnableRigAnimations, false); - safelyLoadAnimations(); - if (_skeletonModel.readyToAddToScene()) { - _rig->setEnableAnimGraph(true); - initAnimGraph(); // must be enabled for the init to happen - _rig->setEnableAnimGraph(false); // must be disable to safely reset threading - } - qApp->setRawAvatarUpdateThreading(); - _rig->setEnableAnimGraph(true); - } else { - _rig->setEnableAnimGraph(false); - destroyAnimGraph(); - } -} - void MyAvatar::setEnableDebugDrawBindPose(bool isEnabled) { _enableDebugDrawBindPose = isEnabled; @@ -875,7 +803,7 @@ void MyAvatar::loadData() { setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString()); settings.endGroup(); - _rig->setEnableRig(Menu::getInstance()->isOptionChecked(MenuOption::EnableRigAnimations)); + setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible)); setEnableDebugDrawBindPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBindPose)); setEnableDebugDrawAnimPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawAnimPose)); @@ -1163,16 +1091,8 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN const QString& urlString = fullAvatarURL.toString(); if (urlString.isEmpty() || (fullAvatarURL != getSkeletonModelURL())) { - bool isRigEnabled = getEnableRigAnimations(); - bool isGraphEnabled = getEnableAnimGraph(); qApp->setRawAvatarUpdateThreading(false); - setEnableRigAnimations(false); - setEnableAnimGraph(false); - setSkeletonModelURL(fullAvatarURL); - - setEnableRigAnimations(isRigEnabled); - setEnableAnimGraph(isGraphEnabled); qApp->setRawAvatarUpdateThreading(); UserActivityLogger::getInstance().changedModel("skeleton", urlString); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b3dfb3891b..4e5a0eae89 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -114,10 +114,6 @@ public: float getRealWorldFieldOfView() { return _realWorldFieldOfView.get(); } - const QList& getAnimationHandles() const { return _rig->getAnimationHandles(); } - AnimationHandlePointer addAnimationHandle() { return _rig->createAnimationHandle(); } - void removeAnimationHandle(const AnimationHandlePointer& handle) { _rig->removeAnimationHandle(handle); } - // Interrupt the current animation with a custom animation. Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); @@ -266,11 +262,8 @@ public slots: virtual void rebuildSkeletonBody() override; - bool getEnableRigAnimations() const { return _rig->getEnableRig(); } - void setEnableRigAnimations(bool isEnabled); - bool getEnableAnimGraph() const { return _rig->getEnableAnimGraph(); } const QString& getAnimGraphUrl() const { return _animGraphUrl; } - void setEnableAnimGraph(bool isEnabled); + void setEnableDebugDrawBindPose(bool isEnabled); void setEnableDebugDrawAnimPose(bool isEnabled); void setEnableMeshVisible(bool isEnabled); @@ -376,7 +369,6 @@ private: void maybeUpdateBillboard(); void initHeadBones(); void initAnimGraph(); - void safelyLoadAnimations(); // Avatar Preferences QUrl _fullAvatarURLFromPreferences; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 1347c69d61..e670ee8cbc 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -251,40 +251,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { // Don't take inputs if playing back a recording. return; } - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - - // Don't Relax toward hand positions when in animGraph mode. - if (!_rig->getEnableAnimGraph()) { - - Hand* hand = _owningAvatar->getHand(); - auto leftPalm = hand->getCopyOfPalmData(HandData::LeftHand); - auto rightPalm = hand->getCopyOfPalmData(HandData::RightHand); - - const float HAND_RESTORATION_RATE = 0.25f; - if (!leftPalm.isActive() && !rightPalm.isActive()) { - // palms are not yet set, use mouse - if (_owningAvatar->getHandState() == HAND_STATE_NULL) { - restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); - } else { - // transform into model-frame - glm::vec3 handPosition = glm::inverse(_rotation) * (_owningAvatar->getHandPosition() - _translation); - applyHandPosition(geometry.rightHandJointIndex, handPosition); - } - restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); - } else { - if (leftPalm.isActive()) { - applyPalmData(geometry.leftHandJointIndex, leftPalm); - } else { - restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); - } - if (rightPalm.isActive()) { - applyPalmData(geometry.rightHandJointIndex, rightPalm); - } else { - restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY); - } - } - } } void SkeletonModel::renderIKConstraints(gpu::Batch& batch) { @@ -344,8 +310,6 @@ void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) { glm::quat inverseRotation = glm::inverse(_rotation); glm::vec3 palmPosition = inverseRotation * (palm.getPosition() - _translation); glm::quat palmRotation = inverseRotation * palm.getRotation(); - - inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); } void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index d4bab86126..fe39a7c7ed 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -247,12 +247,7 @@ void PreferencesDialog::savePreferences() { myAvatar->setLeanScale(ui.leanScaleSpin->value()); myAvatar->setClampedTargetScale(ui.avatarScaleSpin->value()); if (myAvatar->getAnimGraphUrl() != ui.avatarAnimationEdit->text()) { // If changed, destroy the old and start with the new - bool isEnabled = myAvatar->getEnableAnimGraph(); - myAvatar->setEnableAnimGraph(false); myAvatar->setAnimGraphUrl(ui.avatarAnimationEdit->text()); - if (isEnabled) { - myAvatar->setEnableAnimGraph(true); - } } myAvatar->setRealWorldFieldOfView(ui.realWorldFieldOfViewSpin->value()); diff --git a/libraries/animation/src/AnimationHandle.cpp b/libraries/animation/src/AnimationHandle.cpp deleted file mode 100644 index 93ce3613e7..0000000000 --- a/libraries/animation/src/AnimationHandle.cpp +++ /dev/null @@ -1,218 +0,0 @@ -// -// AnimationHandle.cpp -// libraries/animation/src/ -// -// Created by Andrzej Kapolka on 10/18/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "AnimationHandle.h" -#include "AnimationLogging.h" - -void AnimationHandle::setURL(const QUrl& url) { - if (_url != url) { - _animation = DependencyManager::get()->getAnimation(_url = url); - _animation->ensureLoading(); - QObject::connect(_animation.data(), &Resource::onRefresh, this, &AnimationHandle::clearJoints); - _jointMappings.clear(); - } -} - -void AnimationHandle::setPriority(float priority) { - if (_priority == priority) { - return; - } - if (isRunning()) { - _rig->removeRunningAnimation(getAnimationHandlePointer()); - if (priority < _priority) { - replaceMatchingPriorities(priority); - } - _priority = priority; - _rig->addRunningAnimation(getAnimationHandlePointer()); - } else { - _priority = priority; - } -} - -void AnimationHandle::setStartAutomatically(bool startAutomatically) { - if (startAutomatically && !isRunning()) { - // Start before setting _animationLoop value so that code in setRunning() is executed - start(); - } - _animationLoop.setStartAutomatically(startAutomatically); -} - -void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) { - _maskedJoints = maskedJoints; - _jointMappings.clear(); -} - -void AnimationHandle::setRunning(bool running, bool doRestoreJoints) { - if (running && isRunning() && (getFadePerSecond() >= 0.0f)) { - // if we're already running, this is the same as a restart -- unless we're fading out. - setCurrentFrame(getFirstFrame()); - return; - } - _animationLoop.setRunning(running); - if (isRunning()) { - if (!_rig->isRunningAnimation(getAnimationHandlePointer())) { - _rig->addRunningAnimation(getAnimationHandlePointer()); - } - } else { - _rig->removeRunningAnimation(getAnimationHandlePointer()); - if (doRestoreJoints) { - restoreJoints(); - } - replaceMatchingPriorities(0.0f); - } - emit runningChanged(isRunning()); -} - -AnimationHandle::AnimationHandle(RigPointer rig) : - QObject(rig.get()), - _rig(rig), - _priority(1.0f), - _fade(0.0f), - _fadePerSecond(0.0f) -{ -} - -AnimationDetails AnimationHandle::getAnimationDetails() const { - AnimationDetails details(_role, _url, getFPS(), _priority, getLoop(), getHold(), - getStartAutomatically(), getFirstFrame(), getLastFrame(), isRunning(), getCurrentFrame()); - return details; -} - -void AnimationHandle::setAnimationDetails(const AnimationDetails& details) { - setRole(details.role); - setURL(details.url); - setFPS(details.fps); - setPriority(details.priority); - setLoop(details.loop); - setHold(details.hold); - setStartAutomatically(details.startAutomatically); - setFirstFrame(details.firstFrame); - setLastFrame(details.lastFrame); - setRunning(details.running); - setCurrentFrame(details.currentFrame); - - // NOTE: AnimationDetails doesn't support maskedJoints - //setMaskedJoints(const QStringList& maskedJoints); -} - - -void AnimationHandle::setJointMappings(QVector jointMappings) { - _jointMappings = jointMappings; -} - -QVector AnimationHandle::getJointMappings() { - if (_jointMappings.isEmpty()) { - QVector animationJoints = _animation->getGeometry().joints; - for (int i = 0; i < animationJoints.count(); i++) { - _jointMappings.append(_rig->indexOfJoint(animationJoints.at(i).name)); - } - } - return _jointMappings; -} - -void AnimationHandle::simulate(float deltaTime) { - if (!_animation || !_animation->isLoaded()) { - return; - } - - _animationLoop.simulate(deltaTime); - - if (getJointMappings().isEmpty()) { - qDebug() << "AnimationHandle::simulate -- _jointMappings.isEmpty()"; - return; - } - - // // update the joint mappings if necessary/possible - // if (_jointMappings.isEmpty()) { - // if (_model && _model->isActive()) { - // _jointMappings = _model->getGeometry()->getJointMappings(_animation); - // } - // if (_jointMappings.isEmpty()) { - // return; - // } - // if (!_maskedJoints.isEmpty()) { - // const FBXGeometry& geometry = _model->getGeometry()->getFBXGeometry(); - // for (int i = 0; i < _jointMappings.size(); i++) { - // int& mapping = _jointMappings[i]; - // if (mapping != -1 && _maskedJoints.contains(geometry.joints.at(mapping).name)) { - // mapping = -1; - // } - // } - // } - // } - - const FBXGeometry& animationGeometry = _animation->getGeometry(); - if (animationGeometry.animationFrames.isEmpty()) { - stop(); - return; - } - - if (_animationLoop.getMaxFrameIndexHint() != animationGeometry.animationFrames.size()) { - _animationLoop.setMaxFrameIndexHint(animationGeometry.animationFrames.size()); - } - - // blend between the closest two frames - applyFrame(getCurrentFrame()); -} - -void AnimationHandle::applyFrame(float currentFrame) { - if (!_animation || !_animation->isLoaded()) { - return; - } - - const FBXGeometry& animationGeometry = _animation->getGeometry(); - int frameCount = animationGeometry.animationFrames.size(); - const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at((int)glm::floor(currentFrame) % frameCount); - const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at((int)glm::ceil(currentFrame) % frameCount); - float frameFraction = glm::fract(currentFrame); - - for (int i = 0; i < _jointMappings.size(); i++) { - int mapping = _jointMappings.at(i); - if (mapping != -1) { // allow missing bones - _rig->setJointRotationInConstrainedFrame(mapping, - safeMix(floorFrame.rotations.at(i), - ceilFrame.rotations.at(i), - frameFraction), - _priority, - _mix); - - // This isn't working. - // glm::vec3 floorTranslationPart = floorFrame.translations.at(i) * (1.0f - frameFraction); - // glm::vec3 ceilTranslationPart = ceilFrame.translations.at(i) * frameFraction; - // glm::vec3 floorCeilFraction = floorTranslationPart + ceilTranslationPart; - // glm::vec3 defaultTrans = _rig->getJointDefaultTranslationInConstrainedFrame(i); - // glm::vec3 mixedTranslation = floorCeilFraction * (1.0f - _mix) + defaultTrans * _mix; - // _rig->setJointTranslation(mapping, true, mixedTranslation, _priority); - - } - } -} - -void AnimationHandle::replaceMatchingPriorities(float newPriority) { - for (int i = 0; i < _jointMappings.size(); i++) { - int mapping = _jointMappings.at(i); - if (mapping != -1) { - if (_priority == _rig->getJointAnimatinoPriority(mapping)) { - _rig->setJointAnimatinoPriority(mapping, newPriority); - } - } - } -} - -void AnimationHandle::restoreJoints() { - for (int i = 0; i < _jointMappings.size(); i++) { - int mapping = _jointMappings.at(i); - if (mapping != -1) { - _rig->restoreJointRotation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping)); - _rig->restoreJointTranslation(mapping, 1.0f, _rig->getJointAnimatinoPriority(mapping)); - } - } -} diff --git a/libraries/animation/src/AnimationHandle.h b/libraries/animation/src/AnimationHandle.h deleted file mode 100644 index 7492d3fa48..0000000000 --- a/libraries/animation/src/AnimationHandle.h +++ /dev/null @@ -1,138 +0,0 @@ -// -// AnimationHandle.h -// libraries/animation/src/ -// -// Created by Andrzej Kapolka on 10/18/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AnimationHandle_h -#define hifi_AnimationHandle_h - -#include -#include -#include -#include -#include - -#include "AnimationCache.h" -#include "AnimationLoop.h" -#include "Rig.h" - -class AnimationHandle; -class Model; - -typedef std::shared_ptr AnimationHandlePointer; -typedef std::weak_ptr WeakAnimationHandlePointer; -inline uint qHash(const std::shared_ptr& a, uint seed) { - // return qHash(a.get(), seed); - AnimationHandle* strongRef = a ? a.get() : nullptr; - return qHash(strongRef, seed); -} -inline uint qHash(const std::weak_ptr& a, uint seed) { - AnimationHandlePointer strongPointer = a.lock(); - AnimationHandle* strongRef = strongPointer ? strongPointer.get() : nullptr; - return qHash(strongRef, seed); -} - - -// inline uint qHash(const WeakAnimationHandlePointer& handle, uint seed) { -// return qHash(handle.data(), seed); -// } - - - -/// Represents a handle to a model animation. I.e., an Animation in use by a given Rig. -class AnimationHandle : public QObject, public std::enable_shared_from_this { - Q_OBJECT - -public: - - AnimationHandle(RigPointer rig); - - AnimationHandlePointer getAnimationHandlePointer() { return shared_from_this(); } - - void setRole(const QString& role) { _role = role; } - const QString& getRole() const { return _role; } - - void setURL(const QUrl& url); - const QUrl& getURL() const { return _url; } - - void setPriority(float priority); - float getPriority() const { return _priority; } - void setMix(float mix) { _mix = mix; } - void setFade(float fade) { _fade = fade; } - float getFade() const { return _fade; } - void setFadePerSecond(float fadePerSecond) { _fadePerSecond = fadePerSecond; } - float getFadePerSecond() const { return _fadePerSecond; } - - void setMaskedJoints(const QStringList& maskedJoints); - const QStringList& getMaskedJoints() const { return _maskedJoints; } - - - void setFPS(float fps) { _animationLoop.setFPS(fps); } - float getFPS() const { return _animationLoop.getFPS(); } - - void setLoop(bool loop) { _animationLoop.setLoop(loop); } - bool getLoop() const { return _animationLoop.getLoop(); } - - void setHold(bool hold) { _animationLoop.setHold(hold); } - bool getHold() const { return _animationLoop.getHold(); } - - void setStartAutomatically(bool startAutomatically); - bool getStartAutomatically() const { return _animationLoop.getStartAutomatically(); } - - void setFirstFrame(float firstFrame) { _animationLoop.setFirstFrame(firstFrame); } - float getFirstFrame() const { return _animationLoop.getFirstFrame(); } - - void setLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); } - float getLastFrame() const { return _animationLoop.getLastFrame(); } - - void setRunning(bool running, bool restoreJoints = true); - bool isRunning() const { return _animationLoop.getRunning(); } - - void setCurrentFrame(float currentFrame) { _animationLoop.setCurrentFrame(currentFrame); } - float getCurrentFrame() const { return _animationLoop.getCurrentFrame(); } - - AnimationDetails getAnimationDetails() const; - void setAnimationDetails(const AnimationDetails& details); - - void setJointMappings(QVector jointMappings); - QVector getJointMappings(); // computing if necessary - void simulate(float deltaTime); - void applyFrame(float currentFrame); - void replaceMatchingPriorities(float newPriority); - void restoreJoints(); - void clearJoints() { _jointMappings.clear(); } - -signals: - - void runningChanged(bool running); - -public slots: - - void start() { setRunning(true); } - void stop() { setRunning(false); _fadePerSecond = _fade = 0.0f; } - -private: - - RigPointer _rig; - AnimationPointer _animation; - QString _role; - QUrl _url; - float _priority; - float _mix; // How much of this animation to blend against what is already there. 1.0 sets to just this animation. - float _fade; // How far are we into full strength. 0.0 uses none of this animation, 1.0 (the max) is as much as possible. - float _fadePerSecond; // How fast should _fade change? +1.0 means _fade is increasing to 1.0 in 1 second. Negative is fading out. - - QStringList _maskedJoints; - QVector _jointMappings; - - AnimationLoop _animationLoop; -}; - - -#endif // hifi_AnimationHandle_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 94e98457fd..a0523dbfe6 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -18,117 +18,55 @@ #include #include -#include "AnimationHandle.h" #include "AnimationLogging.h" #include "AnimSkeleton.h" #include "AnimClip.h" #include "IKTarget.h" -void insertSorted(QList& handles, const AnimationHandlePointer& handle) { - for (QList::iterator it = handles.begin(); it != handles.end(); it++) { - if (handle->getPriority() > (*it)->getPriority()) { - handles.insert(it, handle); - return; - } - } - handles.append(handle); -} - -AnimationHandlePointer Rig::createAnimationHandle() { - AnimationHandlePointer handle(new AnimationHandle(getRigPointer())); - _animationHandles.append(handle); - return handle; -} -void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) { - handle->stop(); - // FIXME? Do we need to also animationHandle->clearJoints()? deleteAnimations(), below, was first written to do so, but did not first stop it. - _animationHandles.removeOne(handle); -} - void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { - if (_enableAnimGraph) { - // find an unused AnimClip clipNode - std::shared_ptr clip; - if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) { - _userAnimState = UserAnimState::A; - clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); - } else if (_userAnimState == UserAnimState::A) { - _userAnimState = UserAnimState::B; - clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); - } - - // set parameters - clip->setLoopFlag(loop); - clip->setStartFrame(firstFrame); - clip->setEndFrame(lastFrame); - const float REFERENCE_FRAMES_PER_SECOND = 30.0f; - float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; - clip->setTimeScale(timeScale); - clip->loadURL(url); - - _currentUserAnimURL = url; - - // notify the userAnimStateMachine the desired state. - _animVars.set("userAnimNone", false); - _animVars.set("userAnimA", _userAnimState == UserAnimState::A); - _animVars.set("userAnimB", _userAnimState == UserAnimState::B); - - } else { - - float priority = 1.0f; - bool hold = true; - QStringList maskedJoints; - - _currentUserAnimURL = url; - - // This is different than startAnimationByRole, in which we use the existing values if the animation already exists. - // Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted). - AnimationHandlePointer handle = nullptr; - foreach (const AnimationHandlePointer& candidate, _animationHandles) { - if (candidate->getURL() == url) { - handle = candidate; - } - } - if (!handle) { - handle = createAnimationHandle(); - handle->setURL(url); - } - handle->setFade(1.0f); // If you want to fade, use the startAnimationByRole system. - handle->setFPS(fps); - handle->setPriority(priority); - handle->setLoop(loop); - handle->setHold(hold); - handle->setFirstFrame(firstFrame); - handle->setLastFrame(lastFrame); - handle->setMaskedJoints(maskedJoints); - handle->start(); + // find an unused AnimClip clipNode + std::shared_ptr clip; + if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) { + _userAnimState = UserAnimState::A; + clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); + } else if (_userAnimState == UserAnimState::A) { + _userAnimState = UserAnimState::B; + clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); } + + // set parameters + clip->setLoopFlag(loop); + clip->setStartFrame(firstFrame); + clip->setEndFrame(lastFrame); + const float REFERENCE_FRAMES_PER_SECOND = 30.0f; + float timeScale = fps / REFERENCE_FRAMES_PER_SECOND; + clip->setTimeScale(timeScale); + clip->loadURL(url); + + _currentUserAnimURL = url; + + // notify the userAnimStateMachine the desired state. + _animVars.set("userAnimNone", false); + _animVars.set("userAnimA", _userAnimState == UserAnimState::A); + _animVars.set("userAnimB", _userAnimState == UserAnimState::B); } + const float FRAMES_PER_SECOND = 30.0f; const float FADE_FRAMES = 30.0f; void Rig::restoreAnimation() { - if (_enableAnimGraph) { - if (_currentUserAnimURL != "") { - _currentUserAnimURL = ""; - // notify the userAnimStateMachine the desired state. - _animVars.set("userAnimNone", true); - _animVars.set("userAnimA", false); - _animVars.set("userAnimB", false); - } - } else { - foreach (const AnimationHandlePointer& handle, getRunningAnimations()) { - if (handle->getURL() == _currentUserAnimURL) { - handle->setFade(0.0f); // right away. Will be remove during updateAnimations, without locking - handle->setFadePerSecond(-(FRAMES_PER_SECOND / FADE_FRAMES)); // so that the updateAnimation code notices - } - } + if (_currentUserAnimURL != "") { + _currentUserAnimURL = ""; + // notify the userAnimStateMachine the desired state. + _animVars.set("userAnimNone", true); + _animVars.set("userAnimA", false); + _animVars.set("userAnimB", false); } } QStringList Rig::getAnimationRoles() const { - if (_enableAnimGraph && _animNode) { + if (_animNode) { QStringList list; _animNode->traverse([&](AnimNode::Pointer node) { // only report clip nodes as valid roles. @@ -148,7 +86,7 @@ QStringList Rig::getAnimationRoles() const { } void Rig::overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { - if (_enableAnimGraph && _animNode) { + if (_animNode) { AnimNode::Pointer node = _animNode->findByName(role); if (node) { _origRoleAnimations[role] = node; @@ -160,11 +98,13 @@ void Rig::overrideRoleAnimation(const QString& role, const QString& url, float f } else { qCWarning(animation) << "Rig::overrideRoleAnimation could not find role " << role; } + } else { + qCWarning(animation) << "Rig::overrideRoleAnimation avatar not ready yet"; } } void Rig::restoreRoleAnimation(const QString& role) { - if (_enableAnimGraph && _animNode) { + if (_animNode) { AnimNode::Pointer node = _animNode->findByName(role); if (node) { auto iter = _origRoleAnimations.find(role); @@ -175,43 +115,17 @@ void Rig::restoreRoleAnimation(const QString& role) { qCWarning(animation) << "Rig::restoreRoleAnimation could not find role " << role; } } + } else { + qCWarning(animation) << "Rig::overrideRoleAnimation avatar not ready yet"; } } void Rig::prefetchAnimation(const QString& url) { - if (_enableAnimGraph) { - // This will begin loading the NetworkGeometry for the given URL. - // which should speed us up if we request it later via overrideAnimation. - auto clipNode = std::make_shared("prefetch", url, 0, 0, 1.0, false); - _prefetchedAnimations.push_back(clipNode); - } -} -bool Rig::removeRunningAnimation(AnimationHandlePointer animationHandle) { - return _runningAnimations.removeOne(animationHandle); -} - -void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) { - insertSorted(_runningAnimations, animationHandle); -} - -bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) { - return _runningAnimations.contains(animationHandle); -} -bool Rig::isRunningRole(const QString& role) { //obviously, there are more efficient ways to do this - for (auto animation : _runningAnimations) { - if ((animation->getRole() == role) && (animation->getFadePerSecond() >= 0.0f)) { // Don't count those being faded out - return true; - } - } - return false; -} - -void Rig::deleteAnimations() { - for (auto animation : _animationHandles) { - removeAnimationHandle(animation); - } - _animationHandles.clear(); + // This will begin loading the NetworkGeometry for the given URL. + // which should speed us up if we request it later via overrideAnimation. + auto clipNode = std::make_shared("prefetch", url, 0, 0, 1.0, false); + _prefetchedAnimations.push_back(clipNode); } void Rig::destroyAnimGraph() { @@ -483,8 +397,7 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos _lastVelocity = workingVelocity; } - if (_enableAnimGraph) { - + { glm::vec3 localVel = glm::inverse(worldRotation) * workingVelocity; float forwardSpeed = glm::dot(localVel, IDENTITY_FRONT); @@ -636,47 +549,6 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos t += deltaTime; } - if (_enableRig) { - bool isMoving = false; - - glm::vec3 right = worldRotation * IDENTITY_RIGHT; - const float PERCEPTIBLE_DELTA = 0.001f; - const float PERCEPTIBLE_SPEED = 0.1f; - - // Note: Separately, we've arranged for starting/stopping animations by role (as we've done here) to pick up where they've left off when fading, - // so that you wouldn't notice the start/stop if it happens fast enough (e.g., one frame). But the print below would still be noisy. - - float forwardSpeed = glm::dot(workingVelocity, front); - float rightLateralSpeed = glm::dot(workingVelocity, right); - float rightTurningDelta = glm::orientedAngle(front, _lastFront, IDENTITY_UP); - float rightTurningSpeed = rightTurningDelta / deltaTime; - bool isTurning = (std::abs(rightTurningDelta) > PERCEPTIBLE_DELTA) && (std::abs(rightTurningSpeed) > PERCEPTIBLE_SPEED); - bool isStrafing = std::abs(rightLateralSpeed) > PERCEPTIBLE_SPEED; - auto updateRole = [&](const QString& role, bool isOn) { - isMoving = isMoving || isOn; - if (isOn) { - if (!isRunningRole(role)) { - qCDebug(animation) << "Rig STARTING" << role; - //startAnimationByRole(role); - - } - } else { - if (isRunningRole(role)) { - qCDebug(animation) << "Rig stopping" << role; - //stopAnimationByRole(role); - } - } - }; - updateRole("walk", forwardSpeed > PERCEPTIBLE_SPEED); - updateRole("backup", forwardSpeed < -PERCEPTIBLE_SPEED); - updateRole("rightTurn", isTurning && (rightTurningSpeed > 0.0f)); - updateRole("leftTurn", isTurning && (rightTurningSpeed < 0.0f)); - isStrafing = isStrafing && !isMoving; - updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f)); - updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f)); - updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus. - } - _lastFront = front; _lastPosition = worldPosition; } @@ -748,12 +620,10 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { - if (_enableAnimGraph) { - if (!_animNode) { - return; - } + if (_animNode) { updateAnimationStateHandlers(); + // evaluate the animation AnimNode::Triggers triggersOut; AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut); @@ -770,51 +640,6 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, 1.0f); setJointTranslation((int)i, true, poses[i].trans, PRIORITY); } - - } else { - - // First normalize the fades so that they sum to 1.0. - // update the fade data in each animation (not normalized as they are an independent propert of animation) - foreach (const AnimationHandlePointer& handle, _runningAnimations) { - float fadePerSecond = handle->getFadePerSecond(); - float fade = handle->getFade(); - if (fadePerSecond != 0.0f) { - fade += fadePerSecond * deltaTime; - if ((0.0f >= fade) || (fade >= 1.0f)) { - fade = glm::clamp(fade, 0.0f, 1.0f); - handle->setFadePerSecond(0.0f); - } - handle->setFade(fade); - if (fade <= 0.0f) { // stop any finished animations now - handle->setRunning(false, false); // but do not restore joints as it causes a flicker - } - } - } - // sum the remaining fade data - float fadeTotal = 0.0f; - foreach (const AnimationHandlePointer& handle, _runningAnimations) { - fadeTotal += handle->getFade(); - } - float fadeSumSoFar = 0.0f; - foreach (const AnimationHandlePointer& handle, _runningAnimations) { - handle->setPriority(1.0f); - // if no fadeTotal, everyone's (typically just one running) is starting at zero. In that case, blend equally. - float normalizedFade = (fadeTotal != 0.0f) ? (handle->getFade() / fadeTotal) : (1.0f / _runningAnimations.count()); - assert(normalizedFade != 0.0f); - // simulate() will blend each animation result into the result so far, based on the pairwise mix at at each step. - // i.e., slerp the 'mix' distance from the result so far towards this iteration's animation result. - // The formula here for mix is based on the idea that, at each step: - // fadeSum is to normalizedFade, as (1 - mix) is to mix - // i.e., fadeSumSoFar/normalizedFade = (1 - mix)/mix - // Then we solve for mix. - // Sanity check: For the first animation, fadeSum = 0, and the mix will always be 1. - // Sanity check: For equal blending, the formula is equivalent to mix = 1 / nAnimationsSoFar++ - float mix = 1.0f / ((fadeSumSoFar / normalizedFade) + 1.0f); - assert((0.0f <= mix) && (mix <= 1.0f)); - fadeSumSoFar += normalizedFade; - handle->setMix(mix); - handle->simulate(deltaTime); - } } for (int i = 0; i < _jointStates.size(); i++) { @@ -916,11 +741,6 @@ void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::q return; } - if (disableHands || (_enableAnimGraph && _animSkeleton)) { - // the hand data goes through a different path: Rig::updateFromHandParameters() --> early-exit - return; - } - if (freeLineage.isEmpty()) { return; } @@ -1125,10 +945,8 @@ void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { } updateNeckJoint(params.neckJointIndex, params); - if (_enableAnimGraph) { - _animVars.set("isTalking", params.isTalking); - _animVars.set("notIsTalking", !params.isTalking); - } + _animVars.set("isTalking", params.isTalking); + _animVars.set("notIsTalking", !params.isTalking); } void Rig::updateFromEyeParameters(const EyeParameters& params) { @@ -1144,21 +962,11 @@ static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f); void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { - if (_enableAnimGraph && _animSkeleton) { + if (_animSkeleton) { glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) * glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) * glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS)); _animVars.set("lean", absRot); - } else if (!_enableAnimGraph) { - auto& parentState = _jointStates[_jointStates[index].getParentIndex()]; - - // get the rotation axes in joint space and use them to adjust the rotation - glm::quat inverse = glm::inverse(parentState.getRotation() * getJointDefaultRotationInParentFrame(index)); - setJointRotationInConstrainedFrame(index, - glm::angleAxis(- RADIANS_PER_DEGREE * leanSideways, inverse * Z_AXIS) * - glm::angleAxis(- RADIANS_PER_DEGREE * leanForward, inverse * X_AXIS) * - glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, inverse * Y_AXIS) * - getJointState(index).getDefaultRotation(), DEFAULT_PRIORITY); } } } @@ -1224,7 +1032,7 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A void Rig::updateNeckJoint(int index, const HeadParameters& params) { if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { - if (_enableAnimGraph && _animSkeleton) { + if (_animSkeleton) { if (params.isInHMD) { glm::vec3 headPos, neckPos; @@ -1272,23 +1080,6 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { _animVars.unset("neckRotation"); _animVars.set("neckType", (int)IKTarget::Type::RotationOnly); } - } else if (!_enableAnimGraph) { - - auto& state = _jointStates[index]; - auto& parentState = _jointStates[state.getParentIndex()]; - - // get the rotation axes in joint space and use them to adjust the rotation - glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * - glm::translate(getJointDefaultTranslationInConstrainedFrame(index)) * - state.getPreTransform() * glm::mat4_cast(state.getPreRotation()))); - glm::vec3 pitchYawRoll = safeEulerAngles(params.localHeadOrientation); - glm::vec3 lean = glm::radians(glm::vec3(params.leanForward, params.torsoTwist, params.leanSideways)); - pitchYawRoll -= lean; - setJointRotationInConstrainedFrame(index, - glm::angleAxis(-pitchYawRoll.z, glm::normalize(inverse * Z_AXIS)) * - glm::angleAxis(pitchYawRoll.y, glm::normalize(inverse * Y_AXIS)) * - glm::angleAxis(-pitchYawRoll.x, glm::normalize(inverse * X_AXIS)) * - state.getDefaultRotation(), DEFAULT_PRIORITY); } } } @@ -1314,7 +1105,7 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm void Rig::updateFromHandParameters(const HandParameters& params, float dt) { - if (_enableAnimGraph && _animSkeleton && _animNode) { + if (_animSkeleton && _animNode) { // TODO: figure out how to obtain the yFlip from where it is actually stored glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); @@ -1387,9 +1178,6 @@ void Rig::makeAnimSkeleton(const FBXGeometry& fbxGeometry) { } void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) { - if (!_enableAnimGraph) { - return; - } makeAnimSkeleton(fbxGeometry); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 191029f2af..8dffdb6878 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -46,9 +46,6 @@ #include "AnimNodeLoader.h" #include "SimpleMovingAverage.h" -class AnimationHandle; -typedef std::shared_ptr AnimationHandlePointer; - class Rig; typedef std::shared_ptr RigPointer; @@ -101,18 +98,7 @@ public: virtual ~Rig() {} - RigPointer getRigPointer() { return shared_from_this(); } - - AnimationHandlePointer createAnimationHandle(); - void removeAnimationHandle(const AnimationHandlePointer& handle); - bool removeRunningAnimation(AnimationHandlePointer animationHandle); - void addRunningAnimation(AnimationHandlePointer animationHandle); - bool isRunningAnimation(AnimationHandlePointer animationHandle); - bool isRunningRole(const QString& role); // There can be multiple animations per role, so this is more general than isRunningAnimation. - const QList& getRunningAnimations() const { return _runningAnimations; } - void deleteAnimations(); void destroyAnimGraph(); - const QList& getAnimationHandles() const { return _animationHandles; } void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreAnimation(); @@ -187,11 +173,6 @@ public: virtual void updateJointState(int index, glm::mat4 rootTransform) = 0; - void setEnableRig(bool isEnabled) { _enableRig = isEnabled; } - bool getEnableRig() const { return _enableRig; } - void setEnableAnimGraph(bool isEnabled) { _enableAnimGraph = isEnabled; } - bool getEnableAnimGraph() const { return _enableAnimGraph; } - void updateFromHeadParameters(const HeadParameters& params, float dt); void updateFromEyeParameters(const EyeParameters& params); void updateFromHandParameters(const HandParameters& params, float dt); @@ -204,7 +185,6 @@ public: AnimNode::ConstPointer getAnimNode() const { return _animNode; } AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } - bool disableHands {false}; // should go away with rig animation (and Rig::inverseKinematics) QScriptValue addAnimationStateHandler(QScriptValue handler, QScriptValue propertiesList); void removeAnimationStateHandler(QScriptValue handler); void animationStateHandlerResult(int identifier, QScriptValue result); @@ -230,11 +210,6 @@ public: int _rightElbowJointIndex { -1 }; int _rightShoulderJointIndex { -1 }; - QList _animationHandles; - QList _runningAnimations; - - bool _enableRig { false }; - bool _enableAnimGraph { false }; glm::vec3 _lastFront; glm::vec3 _lastPosition; glm::vec3 _lastVelocity; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 5b9bfdca3d..6c06cb902d 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -22,7 +22,6 @@ #include #include "AbstractViewStateInterface.h" -#include "AnimationHandle.h" #include "Model.h" #include "MeshPartPayload.h" @@ -1101,7 +1100,6 @@ void Model::deleteGeometry() { _blendedVertexBuffers.clear(); _rig->clearJointStates(); _meshStates.clear(); - _rig->deleteAnimations(); _rig->destroyAnimGraph(); _blendedBlendshapeCoefficients.clear(); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index f5d5f40363..5e8ab0a189 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -28,10 +28,10 @@ #include #include -#include "AnimationHandle.h" #include "GeometryCache.h" #include "JointState.h" #include "TextureCache.h" +#include "Rig.h" class AbstractViewStateInterface; class QScriptEngine; From e698d3c1e8dc3144ede1c7c18a37360fb7b1dbc8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 11 Nov 2015 18:23:58 -0800 Subject: [PATCH 09/84] Isolate JointStates within the Rig class Except for SkeletalModel::computeBounds() JointStates are now completly encapsulated by the Rig. Now we can start using AnimPoses instead and in parallel with the JointState implementation. Then we can assert that they are identical, before removing JointStates. This check in has many comments with the AJT tag. Each one of these cases will need to be revisitied and fixed. In particular // AJT: LEGACY will be used to enclose all code in the Rig which manipulates the _jointState QVector. --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 48 ++---- interface/src/avatar/SkeletonModel.h | 6 +- libraries/animation/src/AvatarRig.cpp | 20 --- libraries/animation/src/AvatarRig.h | 3 +- libraries/animation/src/EntityRig.cpp | 18 --- libraries/animation/src/EntityRig.h | 3 +- libraries/animation/src/Rig.cpp | 195 ++++++++++++------------- libraries/animation/src/Rig.h | 57 +++----- libraries/render-utils/src/Model.cpp | 49 +------ libraries/render-utils/src/Model.h | 15 +- 11 files changed, 140 insertions(+), 276 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 94e644b22e..7c4f3e963c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1354,7 +1354,7 @@ void MyAvatar::initAnimGraph() { auto graphUrl = QUrl(_animGraphUrl.isEmpty() ? QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full/avatar-animation.json") : _animGraphUrl); - _rig->initAnimGraph(graphUrl, _skeletonModel.getGeometry()->getFBXGeometry()); + _rig->initAnimGraph(graphUrl); } void MyAvatar::destroyAnimGraph() { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index e670ee8cbc..b07d15b414 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -40,9 +40,9 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r SkeletonModel::~SkeletonModel() { } -void SkeletonModel::initJointStates(QVector states) { +void SkeletonModel::initJointStates() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 rootTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; int rootJointIndex = geometry.rootJointIndex; int leftHandJointIndex = geometry.leftHandJointIndex; @@ -52,7 +52,7 @@ void SkeletonModel::initJointStates(QVector states) { int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1; int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1; - _rig->initJointStates(states, rootTransform, + _rig->initJointStates(geometry, modelOffset, rootJointIndex, leftHandJointIndex, leftElbowJointIndex, @@ -80,12 +80,14 @@ void SkeletonModel::initJointStates(QVector states) { _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; } + /* AJT: DISABLED, only need this to compute bounding shape! // the SkeletonModel override of updateJointState() will clear the translation part // of its root joint and we need that done before we try to build shapes hence we // recompute all joint transforms at this time. for (int i = 0; i < _rig->getJointStateCount(); i++) { _rig->updateJointState(i, rootTransform); } + */ computeBoundingShape(); @@ -184,10 +186,11 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromEyeParameters(eyeParams); + /* AJT: FIXME this will likely break eye tracking... // rebuild the jointState transform for the eyes only. Must be after updateRig. _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform); _rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform); - + */ } else { Model::updateRig(deltaTime, parentTransform); @@ -268,34 +271,6 @@ bool operator<(const IndexValue& firstIndex, const IndexValue& secondIndex) { return firstIndex.value < secondIndex.value; } -void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) { - if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { - return; - } - // NOTE: 'position' is in model-frame - setJointPosition(jointIndex, position, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::vec3 handPosition, elbowPosition; - getJointPosition(jointIndex, handPosition); - getJointPosition(geometry.joints.at(jointIndex).parentIndex, elbowPosition); - glm::vec3 forearmVector = handPosition - elbowPosition; - float forearmLength = glm::length(forearmVector); - if (forearmLength < EPSILON) { - return; - } - glm::quat handRotation; - if (!_rig->getJointStateRotation(jointIndex, handRotation)) { - return; - } - - // align hand with forearm - float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; - _rig->applyJointRotationDelta(jointIndex, - rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector), - PALM_PRIORITY); -} - void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) { if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { return; @@ -521,6 +496,9 @@ void SkeletonModel::computeBoundingShape() { return; } + /* + AJT: HACK DISABLED + // BOUNDING SHAPE HACK: before we measure the bounds of the joints we use IK to put the // hands and feet into positions that are more correct than the default pose. @@ -624,6 +602,12 @@ void SkeletonModel::computeBoundingShape() { _rig->restoreJointRotation(i, 1.0f, 1.0f); _rig->restoreJointTranslation(i, 1.0f, 1.0f); } + */ + + // AJT: REMOVE HARDCODED BOUNDING VOLUME + _boundingCapsuleRadius = 0.5f; + _boundingCapsuleHeight = 2.0f; + _boundingCapsuleLocalOffset = glm::vec3(0.0f, 1.0f, 0.0f); } void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index dc08168a8c..9f57d403a9 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -27,7 +27,7 @@ public: SkeletonModel(Avatar* owningAvatar, QObject* parent = nullptr, RigPointer rig = nullptr); ~SkeletonModel(); - virtual void initJointStates(QVector states) override; + virtual void initJointStates() override; virtual void simulate(float deltaTime, bool fullUpdate = true) override; virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; @@ -114,10 +114,6 @@ protected: void computeBoundingShape(); - /// \param jointIndex index of joint in model - /// \param position position of joint in model-frame - void applyHandPosition(int jointIndex, const glm::vec3& position); - void applyPalmData(int jointIndex, const PalmData& palm); private: diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index b72e15a0ce..12b2c4624f 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -11,26 +11,6 @@ #include "AvatarRig.h" -/// Updates the state of the joint at the specified index. -void AvatarRig::updateJointState(int index, glm::mat4 rootTransform) { - if (index < 0 || index >= _jointStates.size()) { - return; // bail - } - JointState& state = _jointStates[index]; - - // compute model transforms - if (index == _rootJointIndex) { - state.computeTransform(rootTransform); - } else { - // guard against out-of-bounds access to _jointStates - int parentIndex = state.getParentIndex(); - if (parentIndex >= 0 && parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } -} - void AvatarRig::setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) { diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h index f45c2951e9..0fabacfe91 100644 --- a/libraries/animation/src/AvatarRig.h +++ b/libraries/animation/src/AvatarRig.h @@ -19,9 +19,8 @@ class AvatarRig : public Rig { Q_OBJECT - public: +public: ~AvatarRig() {} - virtual void updateJointState(int index, glm::mat4 rootTransform); virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority); virtual void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority); diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp index 8748214734..035760883d 100644 --- a/libraries/animation/src/EntityRig.cpp +++ b/libraries/animation/src/EntityRig.cpp @@ -11,24 +11,6 @@ #include "EntityRig.h" -/// Updates the state of the joint at the specified index. -void EntityRig::updateJointState(int index, glm::mat4 rootTransform) { - JointState& state = _jointStates[index]; - - // compute model transforms - int parentIndex = state.getParentIndex(); - if (parentIndex == -1) { - state.computeTransform(rootTransform); - } else { - // guard against out-of-bounds access to _jointStates - if (parentIndex >= 0 && parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } -} - - void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h index 17486bad78..e5d1f005d8 100644 --- a/libraries/animation/src/EntityRig.h +++ b/libraries/animation/src/EntityRig.h @@ -19,9 +19,8 @@ class EntityRig : public Rig { Q_OBJECT - public: +public: ~EntityRig() {} - virtual void updateJointState(int index, glm::mat4 rootTransform); virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) {} virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a0523dbfe6..fa5153473c 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -134,16 +134,40 @@ void Rig::destroyAnimGraph() { _animNode = nullptr; } -void Rig::initJointStates(QVector states, glm::mat4 rootTransform, - int rootJointIndex, - int leftHandJointIndex, - int leftElbowJointIndex, - int leftShoulderJointIndex, - int rightHandJointIndex, - int rightElbowJointIndex, - int rightShoulderJointIndex) { - _jointStates = states; +void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, int rootJointIndex, + int leftHandJointIndex, int leftElbowJointIndex, int leftShoulderJointIndex, + int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex) { + _animSkeleton = std::make_shared(geometry); + _relativePoses.resize(_animSkeleton->getNumJoints()); + + // AJT: LEGACY + { + // was Model::createJointStates + _jointStates.clear(); + for (int i = 0; i < geometry.joints.size(); ++i) { + const FBXJoint& joint = geometry.joints[i]; + // store a pointer to the FBXJoint in the JointState + JointState state(joint); + _jointStates.append(state); + } + + // was old Rig::initJointStates + // compute model transforms + int numStates = _animSkeleton->getNumJoints(); + for (int i = 0; i < numStates; ++i) { + JointState& state = _jointStates[i]; + int parentIndex = state.getParentIndex(); + if (parentIndex == -1) { + state.initTransform(modelOffset); + } else { + const JointState& parentState = _jointStates.at(parentIndex); + state.initTransform(parentState.getTransform()); + } + } + } + + // AJT: TODO: we could probaly just look these up by name? _rootJointIndex = rootJointIndex; _leftHandJointIndex = leftHandJointIndex; _leftElbowJointIndex = leftElbowJointIndex; @@ -152,7 +176,15 @@ void Rig::initJointStates(QVector states, glm::mat4 rootTransform, _rightElbowJointIndex = rightElbowJointIndex; _rightShoulderJointIndex = rightShoulderJointIndex; - initJointTransforms(rootTransform); + setModelOffset(modelOffset); +} + +bool Rig::jointStatesEmpty() { + return _relativePoses.empty(); +} + +int Rig::getJointStateCount() const { + return _relativePoses.size(); } // We could build and cache a dictionary, too.... @@ -166,7 +198,12 @@ int Rig::indexOfJoint(const QString& jointName) { return -1; } +void Rig::setModelOffset(const glm::mat4& modelOffset) { + _modelOffset = AnimPose(modelOffset); +} +// AJT: REMOVE +/* void Rig::initJointTransforms(glm::mat4 rootTransform) { // compute model transforms int numStates = _jointStates.size(); @@ -181,6 +218,7 @@ void Rig::initJointTransforms(glm::mat4 rootTransform) { } } } +*/ void Rig::clearJointTransformTranslation(int jointIndex) { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { @@ -642,97 +680,20 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { } } + // AJT: REMOVE + /* for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i, rootTransform); } + */ + setModelOffset(rootTransform); + buildAbsolutePoses(); + for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].resetTransformChanged(); } } -bool Rig::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, - const QVector& freeLineage, glm::mat4 rootTransform) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } - if (freeLineage.isEmpty()) { - return false; - } - if (lastFreeIndex == -1) { - lastFreeIndex = freeLineage.last(); - } - - // this is a cyclic coordinate descent algorithm: see - // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d - const int ITERATION_COUNT = 1; - glm::vec3 worldAlignment = alignment; - for (int i = 0; i < ITERATION_COUNT; i++) { - // first, try to rotate the end effector as close as possible to the target rotation, if any - glm::quat endRotation; - if (useRotation) { - JointState& state = _jointStates[jointIndex]; - - state.setRotationInBindFrame(rotation, priority); - endRotation = state.getRotationInBindFrame(); - } - - // then, we go from the joint upwards, rotating the end as close as possible to the target - glm::vec3 endPosition = extractTranslation(_jointStates[jointIndex].getTransform()); - for (int j = 1; freeLineage.at(j - 1) != lastFreeIndex; j++) { - int index = freeLineage.at(j); - JointState& state = _jointStates[index]; - if (!(state.getIsFree() || allIntermediatesFree)) { - continue; - } - glm::vec3 jointPosition = extractTranslation(state.getTransform()); - glm::vec3 jointVector = endPosition - jointPosition; - glm::quat oldCombinedRotation = state.getRotation(); - glm::quat combinedDelta; - float combinedWeight; - if (useRotation) { - combinedDelta = safeMix(rotation * glm::inverse(endRotation), - rotationBetween(jointVector, position - jointPosition), 0.5f); - combinedWeight = 2.0f; - - } else { - combinedDelta = rotationBetween(jointVector, position - jointPosition); - combinedWeight = 1.0f; - } - if (alignment != glm::vec3() && j > 1) { - jointVector = endPosition - jointPosition; - glm::vec3 positionSum; - for (int k = j - 1; k > 0; k--) { - int index = freeLineage.at(k); - updateJointState(index, rootTransform); - positionSum += extractTranslation(_jointStates.at(index).getTransform()); - } - glm::vec3 projectedCenterOfMass = glm::cross(jointVector, - glm::cross(positionSum / (j - 1.0f) - jointPosition, jointVector)); - glm::vec3 projectedAlignment = glm::cross(jointVector, glm::cross(worldAlignment, jointVector)); - const float LENGTH_EPSILON = 0.001f; - if (glm::length(projectedCenterOfMass) > LENGTH_EPSILON && glm::length(projectedAlignment) > LENGTH_EPSILON) { - combinedDelta = safeMix(combinedDelta, rotationBetween(projectedCenterOfMass, projectedAlignment), - 1.0f / (combinedWeight + 1.0f)); - } - } - state.applyRotationDelta(combinedDelta, priority); - glm::quat actualDelta = state.getRotation() * glm::inverse(oldCombinedRotation); - endPosition = actualDelta * jointVector + jointPosition; - if (useRotation) { - endRotation = actualDelta * endRotation; - } - } - } - - // now update the joint states from the top - for (int j = freeLineage.size() - 1; j >= 0; j--) { - updateJointState(freeLineage.at(j), rootTransform); - } - - return true; -} - void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, const QVector& freeLineage, glm::mat4 rootTransform) { // NOTE: targetRotation is from in model-frame @@ -1171,15 +1132,7 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { } } -void Rig::makeAnimSkeleton(const FBXGeometry& fbxGeometry) { - if (!_animSkeleton) { - _animSkeleton = std::make_shared(fbxGeometry); - } -} - -void Rig::initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry) { - - makeAnimSkeleton(fbxGeometry); +void Rig::initAnimGraph(const QUrl& url) { // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); @@ -1200,3 +1153,41 @@ bool Rig::getModelOffset(glm::vec3& modelOffsetOut) const { return false; } } + +void Rig::buildAbsolutePoses() { + if (!_animSkeleton) { + return; + } + + assert(_animSkeleton->getNumJoints() == _relativePoses.size()); + _absolutePoses.resize(_relativePoses.size()); + for (int i = 0; i < (int)_relativePoses.size(); i++) { + int parentIndex = _animSkeleton->getParentIndex(i); + if (parentIndex == -1) { + _absolutePoses[i] = _modelOffset * _relativePoses[i]; + } else { + _absolutePoses[i] = _modelOffset * _absolutePoses[parentIndex] * _relativePoses[i]; + } + } + + // AJT: LEGACY + { + // Build the joint states + glm::mat4 rootTransform = (glm::mat4)_modelOffset; + for (int i = 0; i < (int)_animSkeleton->getNumJoints(); i++) { + JointState& state = _jointStates[i]; + + // compute model transforms + int parentIndex = state.getParentIndex(); + if (parentIndex == -1) { + state.computeTransform(rootTransform); + } else { + // guard against out-of-bounds access to _jointStates + if (parentIndex >= 0 && parentIndex < _jointStates.size()) { + const JointState& parentState = _jointStates.at(parentIndex); + state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); + } + } + } + } +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 8dffdb6878..f43f6629e6 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -10,28 +10,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* - Things we want to be able to do, that I think we cannot do now: - * Stop an animation at a given time so that it can be examined visually or in a test harness. (I think we can already stop animation and set frame to a computed float? But does that move the bones?) - * Play two animations, blending between them. (Current structure just has one, under script control.) - * Fade in an animation over another. - * Apply IK, lean, head pointing or other overrides relative to previous position. - All of this depends on coordinated state. - - TBD: - - What are responsibilities of Animation/AnimationPointer/AnimationCache/AnimationDetails/AnimationObject/AnimationLoop? - Is there common/copied code (e.g., ScriptableAvatar::update)? - - How do attachments interact with the physics of the attached entity? E.g., do hand joints need to reflect held object - physics? - - Is there any current need (i.e., for initial campatability) to have multiple animations per role (e.g., idle) with the - system choosing randomly? - - - Distribute some doc from here to the right files if it turns out to be correct: - - AnimationDetails is a script-useable copy of animation state, analogous to EntityItemProperties, but without anything - equivalent to editEntity. - But what's the intended difference vs AnimationObjection? Maybe AnimationDetails is to Animation as AnimationObject - is to AnimationPointer? - */ #ifndef __hifi__Rig__ #define __hifi__Rig__ @@ -107,19 +85,19 @@ public: void restoreRoleAnimation(const QString& role); void prefetchAnimation(const QString& url); - void initJointStates(QVector states, glm::mat4 rootTransform, - int rootJointIndex, - int leftHandJointIndex, - int leftElbowJointIndex, - int leftShoulderJointIndex, - int rightHandJointIndex, - int rightElbowJointIndex, - int rightShoulderJointIndex); - bool jointStatesEmpty() { return _jointStates.isEmpty(); }; - int getJointStateCount() const { return _jointStates.size(); } + void initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, int rootJointIndex, + int leftHandJointIndex, int leftElbowJointIndex, int leftShoulderJointIndex, + int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex); + bool jointStatesEmpty(); + int getJointStateCount() const; int indexOfJoint(const QString& jointName); + void setModelOffset(const glm::mat4& modelOffset); + + // AJT: REMOVE + /* void initJointTransforms(glm::mat4 rootTransform); + */ void clearJointTransformTranslation(int jointIndex); void reset(const QVector& fbxJoints); bool getJointStateRotation(int index, glm::quat& rotation) const; @@ -154,9 +132,6 @@ public: void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation); // Regardless of who started the animations or how many, update the joints. void updateAnimations(float deltaTime, glm::mat4 rootTransform); - bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority, - const QVector& freeLineage, glm::mat4 rootTransform); void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, const QVector& freeLineage, glm::mat4 rootTransform); bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage); @@ -171,8 +146,6 @@ public: glm::quat getJointDefaultRotationInParentFrame(int jointIndex); void clearJointStatePriorities(); - virtual void updateJointState(int index, glm::mat4 rootTransform) = 0; - void updateFromHeadParameters(const HeadParameters& params, float dt); void updateFromEyeParameters(const EyeParameters& params); void updateFromHandParameters(const HandParameters& params, float dt); @@ -180,8 +153,7 @@ public: virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) = 0; - void makeAnimSkeleton(const FBXGeometry& fbxGeometry); - void initAnimGraph(const QUrl& url, const FBXGeometry& fbxGeometry); + void initAnimGraph(const QUrl& url); AnimNode::ConstPointer getAnimNode() const { return _animNode; } AnimSkeleton::ConstPointer getAnimSkeleton() const { return _animSkeleton; } @@ -193,13 +165,20 @@ public: protected: void updateAnimationStateHandlers(); + void buildAbsolutePoses(); void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; + // AJT: TODO: LEGACY QVector _jointStates; + + AnimPose _modelOffset; + AnimPoseVec _relativePoses; + AnimPoseVec _absolutePoses; + int _rootJointIndex { -1 }; int _leftHandJointIndex { -1 }; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6c06cb902d..3d82ef3f97 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -106,24 +106,13 @@ void Model::setOffset(const glm::vec3& offset) { _snappedToRegistrationPoint = false; } -QVector Model::createJointStates(const FBXGeometry& geometry) { - QVector jointStates; - for (int i = 0; i < geometry.joints.size(); ++i) { - const FBXJoint& joint = geometry.joints[i]; - // store a pointer to the FBXJoint in the JointState - JointState state(joint); - jointStates.append(state); - } - return jointStates; -}; - void Model::initJointTransforms() { if (!_geometry || !_geometry->isLoaded()) { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - _rig->initJointTransforms(parentTransform); + glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + _rig->setModelOffset(modelOffset); } void Model::init() { @@ -139,7 +128,6 @@ void Model::reset() { bool Model::updateGeometry() { PROFILE_RANGE(__FUNCTION__); bool needFullUpdate = false; - bool needToRebuild = false; if (!_geometry || !_geometry->isLoaded()) { // geometry is not ready @@ -148,17 +136,10 @@ bool Model::updateGeometry() { _needsReload = false; - QSharedPointer geometry = _geometry; - if (_rig->jointStatesEmpty()) { - const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); - if (fbxGeometry.joints.size() > 0) { - initJointStates(createJointStates(fbxGeometry)); - needToRebuild = true; - } - } + if (_rig->jointStatesEmpty() && _geometry->getFBXGeometry().joints.size() > 0) { + initJointStates(); - if (needToRebuild) { - const FBXGeometry& fbxGeometry = geometry->getFBXGeometry(); + const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; state.clusterMatrices.resize(mesh.clusters.size()); @@ -181,9 +162,9 @@ bool Model::updateGeometry() { } // virtual -void Model::initJointStates(QVector states) { +void Model::initJointStates() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; int rootJointIndex = geometry.rootJointIndex; int leftHandJointIndex = geometry.leftHandJointIndex; @@ -193,7 +174,7 @@ void Model::initJointStates(QVector states) { int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1; int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1; - _rig->initJointStates(states, parentTransform, + _rig->initJointStates(geometry, modelOffset, rootJointIndex, leftHandJointIndex, leftElbowJointIndex, @@ -1025,18 +1006,6 @@ void Model::updateClusterMatrices() { } } -bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, - int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - if (_rig->setJointPosition(jointIndex, position, rotation, useRotation, - lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) { - return true; - } - return false; -} - void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; @@ -1142,8 +1111,6 @@ void Model::segregateMeshGroups() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const std::vector>& networkMeshes = _geometry->getMeshes(); - _rig->makeAnimSkeleton(geometry); - // all of our mesh vectors must match in size if ((int)networkMeshes.size() != geometry.meshes.size() || geometry.meshes.size() != _meshStates.size()) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 5e8ab0a189..ebb8a7f5c4 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -271,7 +271,7 @@ protected: // returns 'true' if needs fullUpdate after geometry change bool updateGeometry(); - virtual void initJointStates(QVector states); + virtual void initJointStates(); void setScaleInternal(const glm::vec3& scale); void scaleToFit(); @@ -280,18 +280,6 @@ protected: void simulateInternal(float deltaTime); virtual void updateRig(float deltaTime, glm::mat4 parentTransform); - /// \param jointIndex index of joint in model structure - /// \param position position of joint in model-frame - /// \param rotation rotation of joint in model-frame - /// \param useRotation false if rotation should be ignored - /// \param lastFreeIndex - /// \param allIntermediatesFree - /// \param alignment - /// \return true if joint exists - bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation = glm::quat(), - bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, - const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f); - /// Restores the indexed joint to its default position. /// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to /// the original position @@ -315,7 +303,6 @@ protected: private: void deleteGeometry(); - QVector createJointStates(const FBXGeometry& geometry); void initJointTransforms(); QSharedPointer _collisionGeometry; From d941be8d0bcd1c59367f42159f072aafe8c5b60b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 20 Oct 2015 17:11:37 -0700 Subject: [PATCH 10/84] allow assignment client to use AssetClient for ATP downloads --- assignment-client/src/Agent.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 063bf24de8..cfc641f12b 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,8 @@ Agent::Agent(NLPacket& packet) : DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION, false)) { DependencyManager::get()->setPacketSender(&_entityEditSender); - + + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -110,11 +112,12 @@ void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); auto nodeList = DependencyManager::get(); - nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() - << NodeType::AudioMixer - << NodeType::AvatarMixer - << NodeType::EntityServer - ); + nodeList->addSetOfNodeTypesToNodeInterestSet({ + NodeType::AudioMixer, + NodeType::AvatarMixer, + NodeType::EntityServer, + NodeType::AssetServer + }); _pingTimer = new QTimer(this); connect(_pingTimer, SIGNAL(timeout()), SLOT(sendPingRequests())); From 5908f2d398952cf5b487ff6093b5e5d3303cf533 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 26 Oct 2015 12:06:25 -0700 Subject: [PATCH 11/84] put Agent AssetClient on sep thread, cleanup --- assignment-client/src/Agent.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index cfc641f12b..0d69a645c3 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -44,7 +44,14 @@ Agent::Agent(NLPacket& packet) : { DependencyManager::get()->setPacketSender(&_entityEditSender); - DependencyManager::set(); + auto assetClient = DependencyManager::set(); + + QThread* assetThread = new QThread; + assetThread->setObjectName("Asset Thread"); + assetClient->moveToThread(assetThread); + connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init); + assetThread->start(); + DependencyManager::set(); DependencyManager::set(); @@ -371,6 +378,7 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) { void Agent::aboutToFinish() { setIsAvatar(false);// will stop timers for sending billboards and identity packets + if (_scriptEngine) { _scriptEngine->stop(); } @@ -382,6 +390,12 @@ void Agent::aboutToFinish() { // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(NULL); + + // cleanup the AssetClient thread + QThread* assetThread = DependencyManager::get()->thread(); + DependencyManager::destroy(); + assetThread->quit(); + assetThread->wait(); } void Agent::sendPingRequests() { From 3fa0bcbcd9d56a0bcd025e1de36fac06c62dabc5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 26 Oct 2015 16:06:12 -0700 Subject: [PATCH 12/84] ping the AssetClient from the agent --- assignment-client/src/Agent.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 0d69a645c3..471de33602 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -43,20 +43,20 @@ Agent::Agent(NLPacket& packet) : DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION, false)) { DependencyManager::get()->setPacketSender(&_entityEditSender); - + auto assetClient = DependencyManager::set(); - + QThread* assetThread = new QThread; assetThread->setObjectName("Asset Thread"); assetClient->moveToThread(assetThread); connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init); assetThread->start(); - + DependencyManager::set(); DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - + packetReceiver.registerListenerForTypes( { PacketType::MixedAudio, PacketType::SilentAudioFrame }, this, "handleAudioPacket"); @@ -75,7 +75,7 @@ void Agent::handleOctreePacket(QSharedPointer packet, SharedNodePointe if (packet->getPayloadSize() > statsMessageLength) { // pull out the piggybacked packet and create a new QSharedPointer for it int piggyBackedSizeWithHeader = packet->getPayloadSize() - statsMessageLength; - + auto buffer = std::unique_ptr(new char[piggyBackedSizeWithHeader]); memcpy(buffer.get(), packet->getPayload() + statsMessageLength, piggyBackedSizeWithHeader); @@ -102,7 +102,7 @@ void Agent::handleJurisdictionPacket(QSharedPointer packet, SharedNode DependencyManager::get()->getJurisdictionListener()-> queueReceivedPacket(packet, senderNode); } -} +} void Agent::handleAudioPacket(QSharedPointer packet) { _receivedAudioStream.parseData(*packet); @@ -176,7 +176,7 @@ void Agent::run() { // give this AvatarData object to the script engine setAvatarData(&scriptedAvatar, "Avatar"); - + auto avatarHashMap = DependencyManager::set(); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); @@ -189,7 +189,7 @@ void Agent::run() { // register ourselves to the script engine _scriptEngine->registerGlobalObject("Agent", this); - // FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why + // FIXME -we shouldn't be calling this directly, it's normally called by run(), not sure why // viewers would need this called. //_scriptEngine->init(); // must be done before we set up the viewers @@ -201,14 +201,14 @@ void Agent::run() { auto entityScriptingInterface = DependencyManager::get(); _scriptEngine->registerGlobalObject("EntityViewer", &_entityViewer); - + // we need to make sure that init has been called for our EntityScriptingInterface // so that it actually has a jurisdiction listener when we ask it for it next entityScriptingInterface->init(); _entityViewer.setJurisdictionListener(entityScriptingInterface->getJurisdictionListener()); - + _entityViewer.init(); - + entityScriptingInterface->setEntityTree(_entityViewer.getTree()); // wire up our additional agent related processing to the update signal @@ -241,7 +241,7 @@ void Agent::setIsAvatar(bool isAvatar) { delete _avatarIdentityTimer; _avatarIdentityTimer = nullptr; } - + if (_avatarBillboardTimer) { _avatarBillboardTimer->stop(); delete _avatarBillboardTimer; @@ -378,7 +378,7 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) { void Agent::aboutToFinish() { setIsAvatar(false);// will stop timers for sending billboards and identity packets - + if (_scriptEngine) { _scriptEngine->stop(); } @@ -390,7 +390,7 @@ void Agent::aboutToFinish() { // our entity tree is going to go away so tell that to the EntityScriptingInterface DependencyManager::get()->setEntityTree(NULL); - + // cleanup the AssetClient thread QThread* assetThread = DependencyManager::get()->thread(); DependencyManager::destroy(); @@ -406,6 +406,7 @@ void Agent::sendPingRequests() { case NodeType::AvatarMixer: case NodeType::AudioMixer: case NodeType::EntityServer: + case NodeType::AssetClient: return true; default: return false; From 52716b8f9558c0fd1264fce416131a50b3ea02f8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 16 Nov 2015 10:55:25 -0800 Subject: [PATCH 13/84] emit a ready signal if Sound has downloaded --- libraries/audio/src/Sound.cpp | 1 + libraries/audio/src/Sound.h | 3 +++ 2 files changed, 4 insertions(+) diff --git a/libraries/audio/src/Sound.cpp b/libraries/audio/src/Sound.cpp index 2ce2c47fef..12f63e0a12 100644 --- a/libraries/audio/src/Sound.cpp +++ b/libraries/audio/src/Sound.cpp @@ -86,6 +86,7 @@ void Sound::downloadFinished(const QByteArray& data) { } _isReady = true; + emit ready(); } void Sound::downSample(const QByteArray& rawAudioByteArray) { diff --git a/libraries/audio/src/Sound.h b/libraries/audio/src/Sound.h index 842c395a7d..91dbef8c6a 100644 --- a/libraries/audio/src/Sound.h +++ b/libraries/audio/src/Sound.h @@ -30,6 +30,9 @@ public: const QByteArray& getByteArray() { return _byteArray; } +signals: + void ready(); + private: QByteArray _byteArray; bool _isStereo; From 516aff2ee012230306e6248c0f476c249620dda8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 16 Nov 2015 11:37:12 -0800 Subject: [PATCH 14/84] Kneel.js: Added comment to clearify js API --- examples/kneel.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/kneel.js b/examples/kneel.js index 246655949c..ef68a1efd0 100644 --- a/examples/kneel.js +++ b/examples/kneel.js @@ -57,6 +57,8 @@ function kneelDown() { var startFrame = 0; var endFrame = 82; + // This will completly override all motion from the default animation system + // including inverse kinematics for hand and head controllers. MyAvatar.overrideAnimation(KNEEL_ANIM_URL, playbackRate, loopFlag, startFrame, endFrame); Overlays.editOverlay(kneelDownButton, { visible: false }); @@ -66,6 +68,8 @@ function kneelDown() { function standUp() { kneeling = false; + // this will restore all motion from the default animation system. + // inverse kinematics will work again normally. MyAvatar.restoreAnimation(); Overlays.editOverlay(standUpButton, { visible: false }); From aa77c4894c3ad5dfd39b3240ed327fd0a303683a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 16 Nov 2015 16:04:10 -0800 Subject: [PATCH 15/84] add an AudioInjectorManager for more efficient threading --- assignment-client/src/Agent.cpp | 2 +- examples/tests/injectorTest.js | 42 +++++++ interface/src/Application.cpp | 2 + libraries/audio/src/AudioInjector.cpp | 126 ++++++++++--------- libraries/audio/src/AudioInjector.h | 23 ++-- libraries/audio/src/AudioInjectorManager.cpp | 96 ++++++++++++++ libraries/audio/src/AudioInjectorManager.h | 53 ++++++++ 7 files changed, 276 insertions(+), 68 deletions(-) create mode 100644 examples/tests/injectorTest.js create mode 100644 libraries/audio/src/AudioInjectorManager.cpp create mode 100644 libraries/audio/src/AudioInjectorManager.h diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 471de33602..7ee696693d 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -406,7 +406,7 @@ void Agent::sendPingRequests() { case NodeType::AvatarMixer: case NodeType::AudioMixer: case NodeType::EntityServer: - case NodeType::AssetClient: + case NodeType::AssetServer: return true; default: return false; diff --git a/examples/tests/injectorTest.js b/examples/tests/injectorTest.js new file mode 100644 index 0000000000..0ad4aad84c --- /dev/null +++ b/examples/tests/injectorTest.js @@ -0,0 +1,42 @@ +// +// injectorTests.js +// examples +// +// Created by Stephen Birarda on 11/16/15. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var soundURL = "atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav"; +var audioOptions = { + position: MyAvatar.position, + volume: 0.5 +}; + +var sound = SoundCache.getSound(soundURL); +var injector = null; +var restarting = false; + +Script.update.connect(function(){ + if (sound.downloaded) { + if (!injector) { + injector = Audio.playSound(sound, audioOptions); + } else if (!injector.isPlaying && !restarting) { + restarting = true; + + Script.setTimeout(function(){ + print("Calling restart for a stopped injector from script."); + injector.restart(); + }, 1000); + } else if (injector.isPlaying) { + restarting = false; + + if (Math.random() < 0.0001) { + print("Calling restart for a running injector from script."); + injector.restart(); + } + } + } +}) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 7a564bbbf0..9bc0bf0179 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -332,6 +333,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); return true; diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index bd807f8dbd..1a00611ee0 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -18,6 +18,7 @@ #include #include "AbstractAudioInterface.h" +#include "AudioInjectorManager.h" #include "AudioRingBuffer.h" #include "AudioLogging.h" #include "SoundCache.h" @@ -35,6 +36,7 @@ AudioInjector::AudioInjector(Sound* sound, const AudioInjectorOptions& injectorO _audioData(sound->getByteArray()), _options(injectorOptions) { + } AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions) : @@ -44,32 +46,27 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt } -void AudioInjector::setIsFinished(bool isFinished) { - _isFinished = isFinished; - // In all paths, regardless of isFinished argument. restart() passes false to prepare for new play, and injectToMixer() needs _shouldStop reset. - _shouldStop = false; - - if (_isFinished) { - emit finished(); - - if (_localBuffer) { - _localBuffer->stop(); - _localBuffer->deleteLater(); - _localBuffer = NULL; - } - - _isStarted = false; - - if (_shouldDeleteAfterFinish) { - // we've been asked to delete after finishing, trigger a queued deleteLater here - QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); - } +void AudioInjector::finish() { + State oldState = std::atomic_exchange(&_state, State::Finished); + bool shouldDelete = (oldState == State::NotFinishedWithPendingDelete); + + emit finished(); + + if (_localBuffer) { + _localBuffer->stop(); + _localBuffer->deleteLater(); + _localBuffer = NULL; + } + + if (shouldDelete) { + // we've been asked to delete after finishing, trigger a queued deleteLater here + QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); } } void AudioInjector::injectAudio() { - if (!_isStarted) { - _isStarted = true; + if (!_hasStarted) { + _hasStarted = true; // check if we need to offset the sound by some number of seconds if (_options.secondOffset > 0.0f) { @@ -85,6 +82,7 @@ void AudioInjector::injectAudio() { if (_options.localOnly) { injectLocally(); } else { + qDebug() << "Calling inject to mixer from" << QThread::currentThread(); injectToMixer(); } } else { @@ -93,18 +91,26 @@ void AudioInjector::injectAudio() { } void AudioInjector::restart() { - _isPlaying = true; - connect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished); - if (!_isStarted || _isFinished) { - emit finished(); - } else { - stop(); + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "restart"); + return; + } + + // reset the current send offset to zero + _currentSendOffset = 0; + + // check our state to decide if we need extra handling for the restart request + if (!isPlaying()) { + // we finished playing, need to reset state so we can get going again + _hasStarted = false; + _shouldStop = false; + _state = State::NotFinished; + + qDebug() << "Calling inject audio again to restart an injector"; + + // call inject audio to start injection over again + injectAudio(); } -} -void AudioInjector::restartPortionAfterFinished() { - disconnect(this, &AudioInjector::finished, this, &AudioInjector::restartPortionAfterFinished); - setIsFinished(false); - QMetaObject::invokeMethod(this, "injectAudio", Qt::QueuedConnection); } void AudioInjector::injectLocally() { @@ -252,16 +258,17 @@ void AudioInjector::injectToMixer() { if (_currentSendOffset != bytesToCopy && _currentSendOffset < _audioData.size()) { - // process events in case we have been told to stop and be deleted - QCoreApplication::processEvents(); + if (_shouldStop) { break; } - + // not the first packet and not done // sleep for the appropriate time - int usecToSleep = (++nextFrame * (_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; + int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; + + qDebug() << "AudioInjector" << this << "will sleep on thread" << QThread::currentThread() << "for" << usecToSleep; if (usecToSleep > 0) { usleep(usecToSleep); @@ -274,8 +281,7 @@ void AudioInjector::injectToMixer() { } } - setIsFinished(true); - _isPlaying = !_isFinished; // Which can be false if a restart was requested + finish(); } void AudioInjector::stop() { @@ -283,8 +289,14 @@ void AudioInjector::stop() { if (_options.localOnly) { // we're only a local injector, so we can say we are finished right away too - _isPlaying = false; - setIsFinished(true); + finish(); + } +} + +void AudioInjector::triggerDeleteAfterFinish() { + auto expectedState = State::NotFinished; + if (!_state.compare_exchange_strong(expectedState, State::NotFinishedWithPendingDelete)) { + stopAndDeleteLater(); } } @@ -336,28 +348,26 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { AudioInjector* sound = playSound(buffer, options, localInterface); - sound->triggerDeleteAfterFinish(); + sound->_state = AudioInjector::State::NotFinishedWithPendingDelete; return sound; } AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { - QThread* injectorThread = new QThread(); - injectorThread->setObjectName("Audio Injector Thread"); - AudioInjector* injector = new AudioInjector(buffer, options); - injector->_isPlaying = true; injector->setLocalAudioInterface(localInterface); - - injector->moveToThread(injectorThread); - - // start injecting when the injector thread starts - connect(injectorThread, &QThread::started, injector, &AudioInjector::injectAudio); - - // connect the right slots and signals for AudioInjector and thread cleanup - connect(injector, &AudioInjector::destroyed, injectorThread, &QThread::quit); - connect(injectorThread, &QThread::finished, injectorThread, &QThread::deleteLater); - - injectorThread->start(); - return injector; + + // grab the AudioInjectorManager + auto injectorManager = DependencyManager::get(); + + // attempt to thread the new injector + if (injectorManager->threadInjector(injector)) { + // call inject audio on the correct thread + QMetaObject::invokeMethod(injector, "injectAudio", Qt::QueuedConnection); + + return injector; + } else { + // we failed to thread the new injector (we are at the max number of injector threads) + return nullptr; + } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 0e98fe1682..25b814f0f6 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -12,6 +12,8 @@ #ifndef hifi_AudioInjector_h #define hifi_AudioInjector_h +#include + #include #include #include @@ -32,11 +34,17 @@ class AudioInjector : public QObject { Q_OBJECT public: + enum class State : uint8_t { + NotFinished, + NotFinishedWithPendingDelete, + Finished + }; + AudioInjector(QObject* parent); AudioInjector(Sound* sound, const AudioInjectorOptions& injectorOptions); AudioInjector(const QByteArray& audioData, const AudioInjectorOptions& injectorOptions); - bool isFinished() const { return _isFinished; } + bool isFinished() const { return _state == State::Finished; } int getCurrentSendOffset() const { return _currentSendOffset; } void setCurrentSendOffset(int currentSendOffset) { _currentSendOffset = currentSendOffset; } @@ -55,15 +63,14 @@ public slots: void restart(); void stop(); - void triggerDeleteAfterFinish() { _shouldDeleteAfterFinish = true; } + void triggerDeleteAfterFinish(); void stopAndDeleteLater(); const AudioInjectorOptions& getOptions() const { return _options; } void setOptions(const AudioInjectorOptions& options) { _options = options; } float getLoudness() const { return _loudness; } - bool isPlaying() const { return _isPlaying; } - void restartPortionAfterFinished(); + bool isPlaying() const { return _state == State::NotFinished || _state == State::NotFinishedWithPendingDelete; } signals: void finished(); @@ -72,16 +79,14 @@ private: void injectToMixer(); void injectLocally(); - void setIsFinished(bool isFinished); + void finish(); QByteArray _audioData; AudioInjectorOptions _options; + std::atomic _state { State::NotFinished }; + bool _hasStarted = false; bool _shouldStop = false; float _loudness = 0.0f; - bool _isPlaying = false; - bool _isStarted = false; - bool _isFinished = false; - bool _shouldDeleteAfterFinish = false; int _currentSendOffset = 0; AbstractAudioInterface* _localAudioInterface = NULL; AudioInjectorLocalBuffer* _localBuffer = NULL; diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp new file mode 100644 index 0000000000..8eda670ddf --- /dev/null +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -0,0 +1,96 @@ +// +// AudioInjectorManager.cpp +// libraries/audio/src +// +// Created by Stephen Birarda on 2015-11-16. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AudioInjectorManager.h" + +#include + +#include "AudioInjector.h" + +AudioInjectorManager::~AudioInjectorManager() { + _shouldStop = true; + + // make sure any still living injectors are stopped and deleted + for (auto injector : _injectors) { + injector->stopAndDeleteLater(); + } + + // quit and wait on the injector thread, if we ever created it + if (_thread) { + _thread->quit(); + _thread->wait(); + } +} + +void AudioInjectorManager::createThread() { + _thread = new QThread; + _thread->setObjectName("Audio Injector Thread"); + + // when the thread is started, have it call our run to handle injection of audio + connect(_thread, &QThread::started, this, &AudioInjectorManager::run); + + // start the thread + _thread->start(); +} + +void AudioInjectorManager::run() { + while (!_shouldStop) { + // process events in case we have been told to stop or our injectors have been told to stop + QCoreApplication::processEvents(); + } +} + +static const int MAX_INJECTORS_PER_THREAD = 50; // calculated based on AudioInjector while loop time, with sufficient padding + +bool AudioInjectorManager::threadInjector(AudioInjector* injector) { + // guard the injectors vector with a mutex + std::unique_lock lock(_injectorsMutex); + + // check if we'll be able to thread this injector (do we have < MAX concurrent injectors) + if (_injectors.size() < MAX_INJECTORS_PER_THREAD) { + if (!_thread) { + createThread(); + } + + auto it = nextInjectorIterator(); + qDebug() << "Inserting injector at" << it - _injectors.begin(); + + // store a QPointer to this injector + _injectors.insert(it, QPointer(injector)); + + qDebug() << "Moving injector to thread" << _thread; + + // move the injector to the QThread + injector->moveToThread(_thread); + + return true; + } else { + // unable to thread this injector, at the max + qDebug() << "AudioInjectorManager::threadInjector could not thread AudioInjector - at max of" + << MAX_INJECTORS_PER_THREAD << "current audio injectors."; + return false; + } +} + +AudioInjectorVector::iterator AudioInjectorManager::nextInjectorIterator() { + // find the next usable iterator for an injector + auto it = _injectors.begin(); + + while (it != _injectors.end()) { + if (it->isNull()) { + return it; + } else { + ++it; + } + } + + return it; +} diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h new file mode 100644 index 0000000000..4e571d2d8d --- /dev/null +++ b/libraries/audio/src/AudioInjectorManager.h @@ -0,0 +1,53 @@ +// +// AudioInjectorManager.h +// libraries/audio/src +// +// Created by Stephen Birarda on 2015-11-16. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +#ifndef hifi_AudioInjectorManager_h +#define hifi_AudioInjectorManager_h + +#include +#include + +#include +#include + +#include + +class AudioInjector; +using AudioInjectorVector = std::vector>; + +class AudioInjectorManager : public QObject, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY +public: + ~AudioInjectorManager(); +private slots: + void run(); +private: + bool threadInjector(AudioInjector* injector); + + AudioInjectorManager() {}; + AudioInjectorManager(const AudioInjectorManager&) = delete; + AudioInjectorManager& operator=(const AudioInjectorManager&) = delete; + + void createThread(); + AudioInjectorVector::iterator nextInjectorIterator(); + + QThread* _thread { nullptr }; + bool _shouldStop { false }; + AudioInjectorVector _injectors; + std::mutex _injectorsMutex; + + friend class AudioInjector; +}; + +#endif // hifi_AudioInjectorManager_h From 80eb247b9c5ae79079bce65a99a72d8b6cb1fd87 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 16 Nov 2015 18:49:47 -0800 Subject: [PATCH 16/84] WIP checkin * AnimManipulator: added absolute and relative position and translation support * Rig: added _overrideFlags and _overridePoses for script overrides. --- examples/sit.js | 6 +- .../defaultAvatar_full/avatar-animation.json | 2 +- interface/src/avatar/SkeletonModel.cpp | 6 +- libraries/animation/src/AnimManipulator.cpp | 69 ++++++---- libraries/animation/src/AnimManipulator.h | 16 ++- libraries/animation/src/AnimNodeLoader.cpp | 59 +++++++-- libraries/animation/src/AvatarRig.cpp | 39 ++++-- libraries/animation/src/EntityRig.cpp | 21 ++- libraries/animation/src/Rig.cpp | 123 +++++++++++++++--- libraries/animation/src/Rig.h | 4 + 10 files changed, 258 insertions(+), 87 deletions(-) diff --git a/examples/sit.js b/examples/sit.js index 34a589bf99..70cb086e36 100644 --- a/examples/sit.js +++ b/examples/sit.js @@ -75,7 +75,7 @@ function updateJoints(factor){ for (var i = 0; i < startPoseAndTransition.length; i++){ var scaledTransition = Vec3.multiply(startPoseAndTransition[i].transition, factor); var rotation = Vec3.sum(startPoseAndTransition[i].start, scaledTransition); - MyAvatar.setJointData(startPoseAndTransition[i].joint, Quat.fromVec3Degrees( rotation )); + MyAvatar.setJointRotation(startPoseAndTransition[i].joint, Quat.fromVec3Degrees( rotation )); } } @@ -282,7 +282,8 @@ function update(deltaTime){ MyAvatar.position.z != avatarOldPosition.z || locationChanged) { avatarOldPosition = MyAvatar.position; - + + /* var SEARCH_RADIUS = 50; var foundModels = Entities.findEntities(MyAvatar.position, SEARCH_RADIUS); // Let's remove indicator that got out of radius @@ -306,6 +307,7 @@ function update(deltaTime){ if (hiddingSeats && passedTime >= animationLenght) { showIndicators(true); } + */ } } var oldHost = location.hostname; diff --git a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json index 8e5953fa69..ae470509be 100644 --- a/interface/resources/meshes/defaultAvatar_full/avatar-animation.json +++ b/interface/resources/meshes/defaultAvatar_full/avatar-animation.json @@ -104,7 +104,7 @@ "data": { "alpha": 0.0, "joints": [ - { "var": "lean", "jointName": "Spine" } + { "type": "absoluteRotation", "jointName": "Spine", "var": "lean" } ] }, "children": [] diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b07d15b414..dcdb6d4dcb 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -605,9 +605,9 @@ void SkeletonModel::computeBoundingShape() { */ // AJT: REMOVE HARDCODED BOUNDING VOLUME - _boundingCapsuleRadius = 0.5f; - _boundingCapsuleHeight = 2.0f; - _boundingCapsuleLocalOffset = glm::vec3(0.0f, 1.0f, 0.0f); + _boundingCapsuleRadius = 0.3f; + _boundingCapsuleHeight = 1.3f; + _boundingCapsuleLocalOffset = glm::vec3(0.0f, -0.25f, 0.0f); } void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index 8b9089f450..e8f796f4dd 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -46,39 +46,15 @@ const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, floa if (jointVar.jointIndex >= 0) { - AnimPose defaultAbsPose; + // use the underPose as our default value if we can. AnimPose defaultRelPose; - AnimPose parentAbsPose = AnimPose::identity; if (jointVar.jointIndex <= (int)underPoses.size()) { - - // jointVar is an absolute rotation, if it is not set we will use the underPose as our default value defaultRelPose = underPoses[jointVar.jointIndex]; - defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); - defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); - - // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. - int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); - if (parentIndex >= 0) { - parentAbsPose = _skeleton->getAbsolutePose(parentIndex, underPoses); - } - } else { - - // jointVar is an absolute rotation, if it is not set we will use the bindPose as our default value defaultRelPose = AnimPose::identity; - defaultAbsPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex); - defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); - - // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose - // here we use the bind pose - int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); - if (parentIndex >= 0) { - parentAbsPose = _skeleton->getAbsoluteBindPose(parentIndex); - } } - // convert from absolute to relative - AnimPose relPose = parentAbsPose.inverse() * defaultAbsPose; + AnimPose relPose = computeRelativePoseFromJointVar(animVars, jointVar, defaultRelPose, underPoses); // blend with underPose ::blend(1, &defaultRelPose, &relPose, _alpha, &_poses[jointVar.jointIndex]); @@ -114,3 +90,44 @@ const AnimPoseVec& AnimManipulator::getPosesInternal() const { void AnimManipulator::addJointVar(const JointVar& jointVar) { _jointVars.push_back(jointVar); } + +void AnimManipulator::removeAllJointVars() { + _jointVars.clear(); +} + +AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap& animVars, const JointVar& jointVar, + const AnimPose& defaultRelPose, const AnimPoseVec& underPoses) { + + AnimPose defaultAbsPose = _skeleton->getAbsolutePose(jointVar.jointIndex, underPoses); + + if (jointVar.type == JointVar::Type::AbsoluteRotation || jointVar.type == JointVar::Type::AbsolutePosition) { + + if (jointVar.type == JointVar::Type::AbsoluteRotation) { + defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); + } else if (jointVar.type == JointVar::Type::AbsolutePosition) { + defaultAbsPose.trans = animVars.lookup(jointVar.var, defaultAbsPose.trans); + } + + // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. + AnimPose parentAbsPose = AnimPose::identity; + int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex); + if (parentIndex >= 0) { + parentAbsPose = _skeleton->getAbsolutePose(parentIndex, underPoses); + } + + // convert from absolute to relative + return parentAbsPose.inverse() * defaultAbsPose; + + } else { + + // override the default rel pose + AnimPose relPose = defaultRelPose; + if (jointVar.type == JointVar::Type::RelativeRotation) { + relPose.rot = animVars.lookup(jointVar.var, defaultRelPose.rot); + } else if (jointVar.type == JointVar::Type::RelativePosition) { + relPose.trans = animVars.lookup(jointVar.var, defaultRelPose.trans); + } + + return relPose; + } +} diff --git a/libraries/animation/src/AnimManipulator.h b/libraries/animation/src/AnimManipulator.h index eca1a4aa71..8534b9c269 100644 --- a/libraries/animation/src/AnimManipulator.h +++ b/libraries/animation/src/AnimManipulator.h @@ -30,19 +30,33 @@ public: virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) override; struct JointVar { - JointVar(const QString& varIn, const QString& jointNameIn) : var(varIn), jointName(jointNameIn), jointIndex(-1), hasPerformedJointLookup(false) {} + enum class Type { + AbsoluteRotation = 0, + AbsolutePosition, + RelativeRotation, + RelativePosition, + NumTypes + }; + + JointVar(const QString& varIn, const QString& jointNameIn, Type typeIn) : var(varIn), jointName(jointNameIn), type(typeIn), jointIndex(-1), hasPerformedJointLookup(false) {} QString var = ""; QString jointName = ""; + Type type = Type::AbsoluteRotation; int jointIndex = -1; bool hasPerformedJointLookup = false; + bool isRelative = false; }; void addJointVar(const JointVar& jointVar); + void removeAllJointVars(); protected: // for AnimDebugDraw rendering virtual const AnimPoseVec& getPosesInternal() const override; + AnimPose computeRelativePoseFromJointVar(const AnimVariantMap& animVars, const JointVar& jointVar, + const AnimPose& defaultRelPose, const AnimPoseVec& underPoses); + AnimPoseVec _poses; float _alpha; QString _alphaVar; diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 83f72b9b5a..568da8dd63 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -55,6 +55,41 @@ static const char* animNodeTypeToString(AnimNode::Type type) { return nullptr; } +static AnimNode::Type stringToAnimNodeType(const QString& str) { + // O(n), move to map when number of types becomes large. + const int NUM_TYPES = static_cast(AnimNode::Type::NumTypes); + for (int i = 0; i < NUM_TYPES; i++) { + AnimNode::Type type = static_cast(i); + if (str == animNodeTypeToString(type)) { + return type; + } + } + return AnimNode::Type::NumTypes; +} + +static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) { + switch (type) { + case AnimManipulator::JointVar::Type::AbsoluteRotation: return "absoluteRotation"; + case AnimManipulator::JointVar::Type::AbsolutePosition: return "absolutePosition"; + case AnimManipulator::JointVar::Type::RelativeRotation: return "relativeRotation"; + case AnimManipulator::JointVar::Type::RelativePosition: return "relativePosition"; + case AnimManipulator::JointVar::Type::NumTypes: return nullptr; + }; + return nullptr; +} + +static AnimManipulator::JointVar::Type stringToAnimManipulatorJointVarType(const QString& str) { + // O(n), move to map when number of types becomes large. + const int NUM_TYPES = static_cast(AnimManipulator::JointVar::Type::NumTypes); + for (int i = 0; i < NUM_TYPES; i++) { + AnimManipulator::JointVar::Type type = static_cast(i); + if (str == animManipulatorJointVarTypeToString(type)) { + return type; + } + } + return AnimManipulator::JointVar::Type::NumTypes; +} + static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) { switch (type) { case AnimNode::Type::Clip: return loadClipNode; @@ -120,17 +155,6 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) { } \ float NAME = (float)NAME##_VAL.toDouble() -static AnimNode::Type stringToEnum(const QString& str) { - // O(n), move to map when number of types becomes large. - const int NUM_TYPES = static_cast(AnimNode::Type::NumTypes); - for (int i = 0; i < NUM_TYPES; i++) { - AnimNode::Type type = static_cast(i); - if (str == animNodeTypeToString(type)) { - return type; - } - } - return AnimNode::Type::NumTypes; -} static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) { auto idVal = jsonObj.value("id"); @@ -146,7 +170,7 @@ static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUr return nullptr; } QString typeStr = typeVal.toString(); - AnimNode::Type type = stringToEnum(typeStr); + AnimNode::Type type = stringToAnimNodeType(typeStr); if (type == AnimNode::Type::NumTypes) { qCCritical(animation) << "AnimNodeLoader, unknown node type" << typeStr << ", id =" << id << ", url =" << jsonUrl.toDisplayString(); return nullptr; @@ -355,10 +379,17 @@ static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const Q } auto jointObj = jointValue.toObject(); - READ_STRING(var, jointObj, id, jsonUrl, nullptr); + READ_STRING(type, jointObj, id, jsonUrl, nullptr); READ_STRING(jointName, jointObj, id, jsonUrl, nullptr); + READ_STRING(var, jointObj, id, jsonUrl, nullptr); - AnimManipulator::JointVar jointVar(var, jointName); + AnimManipulator::JointVar::Type jointVarType = stringToAnimManipulatorJointVarType(type); + if (jointVarType == AnimManipulator::JointVar::Type::NumTypes) { + qCCritical(animation) << "AnimNodeLoader, bad type in \"joints\", id =" << id << ", url =" << jsonUrl.toDisplayString(); + return nullptr; + } + + AnimManipulator::JointVar jointVar(var, jointName, jointVarType); node->addJointVar(jointVar); }; diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 12b2c4624f..eb522735b7 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -78,25 +78,40 @@ void AvatarRig::setHandPosition(int jointIndex, void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setTranslation(translation, priority); - } else { - state.restoreTranslation(1.0f, priority); + + // AJT: LEGACY + { + JointState& state = _jointStates[index]; + if (valid) { + state.setTranslation(translation, priority); + } else { + state.restoreTranslation(1.0f, priority); + } } + + _overrideFlags[index] = true; + _overridePoses[index].trans = translation; } } void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - state.setTranslation(translation, priority); - } else { - state.restoreRotation(1.0f, priority); - state.restoreTranslation(1.0f, priority); + + // AJT: LEGACY + { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + state.setTranslation(translation, priority); + } else { + state.restoreRotation(1.0f, priority); + state.restoreTranslation(1.0f, priority); + } } + + _overrideFlags[index] = true; + _overridePoses[index].rot = rotation; + _overridePoses[index].trans = translation; } } diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp index 035760883d..9667fc569f 100644 --- a/libraries/animation/src/EntityRig.cpp +++ b/libraries/animation/src/EntityRig.cpp @@ -13,13 +13,20 @@ void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - // state.setTranslation(translation, priority); - } else { - state.restoreRotation(1.0f, priority); - // state.restoreTranslation(1.0f, priority); + + // AJT: LEGACY + { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + // state.setTranslation(translation, priority); + } else { + state.restoreRotation(1.0f, priority); + // state.restoreTranslation(1.0f, priority); + } } + + _overrideFlags[index] = true; + _overridePoses[index].rot = rotation; } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index fa5153473c..f937144491 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -23,16 +23,28 @@ #include "AnimClip.h" #include "IKTarget.h" +#ifdef NDEBUG +#define ASSERT() +#else +#define ASSERT(cond) \ + do { \ + if (!(cond)) { \ + int* ptr = nullptr; \ + *ptr = 10; \ + } \ + } while (0) +#endif + void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { // find an unused AnimClip clipNode std::shared_ptr clip; if (_userAnimState == UserAnimState::None || _userAnimState == UserAnimState::B) { _userAnimState = UserAnimState::A; - clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); + clip = std::dynamic_pointer_cast(_animNode->findByName("userAnimA")); } else if (_userAnimState == UserAnimState::A) { _userAnimState = UserAnimState::B; - clip = std::dynamic_pointer_cast(_animNode->getChild((int)_userAnimState)); + clip = std::dynamic_pointer_cast(_animNode->findByName("userAnimB")); } // set parameters @@ -132,6 +144,10 @@ void Rig::destroyAnimGraph() { _animSkeleton = nullptr; _animLoader = nullptr; _animNode = nullptr; + _relativePoses.clear(); + _absolutePoses.clear(); + _overridePoses.clear(); + _overrideFlags.clear(); } void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, int rootJointIndex, @@ -139,7 +155,18 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex) { _animSkeleton = std::make_shared(geometry); - _relativePoses.resize(_animSkeleton->getNumJoints()); + + _relativePoses.clear(); + _relativePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + + _absolutePoses.clear(); + _absolutePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + + _overridePoses.clear(); + _overridePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + + _overrideFlags.clear(); + _overrideFlags.resize(_animSkeleton->getNumJoints(), true); // AJT: LEGACY { @@ -264,14 +291,25 @@ bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { void Rig::clearJointState(int index) { if (index != -1 && index < _jointStates.size()) { + // AJT: REMOVE + /* JointState& state = _jointStates[index]; state.setRotationInConstrainedFrame(glm::quat(), 0.0f); state.setTranslation(state.getDefaultTranslationInConstrainedFrame(), 0.0f); + */ + _overrideFlags[index] = false; } } void Rig::clearJointStates() { - _jointStates.clear(); + // AJT: LEGACY + /* + { + _jointStates.clear(); + } + */ + _overrideFlags.clear(); + _overrideFlags.resize(_animSkeleton->getNumJoints()); } void Rig::clearJointAnimationPriority(int index) { @@ -375,12 +413,33 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return glm::mat4(); } + + /* + // AJT: test if _absolutePoses are the same as jointTransforms + glm::mat4 newMat = _absolutePoses[jointIndex]; + glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); + + const float EPSILON = 0.0001f; + if (glm::length(newMat[0] - oldMat[0]) > EPSILON || + glm::length(newMat[1] - oldMat[1]) > EPSILON || + glm::length(newMat[2] - oldMat[2]) > EPSILON || + glm::length(newMat[3] - oldMat[3]) > EPSILON) { + + // error? + qCDebug(animation) << "AJT: mismatch for joint[" << jointIndex; + qCDebug(animation) << "AJT: oldMat = " << AnimPose(oldMat); + qCDebug(animation) << "AJT: newMat = " << AnimPose(newMat); + + } + */ + + // AJT: LEGACY return _jointStates[jointIndex].getTransform(); } void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const { - assert(referenceSpeeds.size() > 0); + ASSERT(referenceSpeeds.size() > 0); // calculate alpha from linear combination of referenceSpeeds. float alpha = 0.0f; @@ -664,33 +723,38 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { // evaluate the animation AnimNode::Triggers triggersOut; - AnimPoseVec poses = _animNode->evaluate(_animVars, deltaTime, triggersOut); + _relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut); + if (_relativePoses.size() != _animSkeleton->getNumJoints()) { + // animations haven't been loaded yet. + _relativePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + } _animVars.clearTriggers(); for (auto& trigger : triggersOut) { _animVars.setTrigger(trigger); } - clearJointStatePriorities(); + // AJT: LEGACY + { + clearJointStatePriorities(); - // copy poses into jointStates - const float PRIORITY = 1.0f; - for (size_t i = 0; i < poses.size(); i++) { - setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * poses[i].rot, PRIORITY, 1.0f); - setJointTranslation((int)i, true, poses[i].trans, PRIORITY); + // copy poses into jointStates + const float PRIORITY = 1.0f; + for (size_t i = 0; i < _relativePoses.size(); i++) { + setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * _relativePoses[i].rot, PRIORITY, 1.0f); + setJointTranslation((int)i, true, _relativePoses[i].trans, PRIORITY); + } } } - // AJT: REMOVE - /* - for (int i = 0; i < _jointStates.size(); i++) { - updateJointState(i, rootTransform); - } - */ setModelOffset(rootTransform); + //applyOverridePoses(); buildAbsolutePoses(); - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); + // AJT: LEGACY + { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].resetTransformChanged(); + } } } @@ -1154,12 +1218,29 @@ bool Rig::getModelOffset(glm::vec3& modelOffsetOut) const { } } +void Rig::applyOverridePoses() { + if (!_animSkeleton) { + return; + } + + ASSERT(_animSkeleton->getNumJoints() == _relativePoses.size()); + ASSERT(_animSkeleton->getNumJoints() == _overrideFlags.size()); + ASSERT(_animSkeleton->getNumJoints() == _overridePoses.size()); + + for (size_t i = 0; i < _overrideFlags.size(); i++) { + if (_overrideFlags[i]) { + _relativePoses[i] = _overridePoses[i]; + } + } +} + void Rig::buildAbsolutePoses() { if (!_animSkeleton) { return; } - assert(_animSkeleton->getNumJoints() == _relativePoses.size()); + ASSERT(_animSkeleton->getNumJoints() == _relativePoses.size()); + _absolutePoses.resize(_relativePoses.size()); for (int i = 0; i < (int)_relativePoses.size(); i++) { int parentIndex = _animSkeleton->getParentIndex(i); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index f43f6629e6..2eea59bd7f 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS @@ -165,6 +166,7 @@ public: protected: void updateAnimationStateHandlers(); + void applyOverridePoses(); void buildAbsolutePoses(); void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); @@ -178,6 +180,8 @@ public: AnimPose _modelOffset; AnimPoseVec _relativePoses; AnimPoseVec _absolutePoses; + AnimPoseVec _overridePoses; + std::vector _overrideFlags; int _rootJointIndex { -1 }; From 80115d38e9335ab125c36d502bf168c8ba2422a6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 16 Nov 2015 18:53:09 -0800 Subject: [PATCH 17/84] tweak the AudioInjectorManager for injector threading --- libraries/audio/src/AudioInjector.cpp | 279 ++++++++++--------- libraries/audio/src/AudioInjector.h | 14 +- libraries/audio/src/AudioInjectorManager.cpp | 98 +++++-- libraries/audio/src/AudioInjectorManager.h | 12 +- 4 files changed, 234 insertions(+), 169 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 1a00611ee0..e88a858608 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -64,9 +64,10 @@ void AudioInjector::finish() { } } -void AudioInjector::injectAudio() { +void AudioInjector::setupInjection() { if (!_hasStarted) { _hasStarted = true; + // check if we need to offset the sound by some number of seconds if (_options.secondOffset > 0.0f) { @@ -81,12 +82,9 @@ void AudioInjector::injectAudio() { if (_options.localOnly) { injectLocally(); - } else { - qDebug() << "Calling inject to mixer from" << QThread::currentThread(); - injectToMixer(); } } else { - qCDebug(audio) << "AudioInjector::injectAudio called but already started."; + qCDebug(audio) << "AudioInjector::setupInjection called but already started."; } } @@ -109,7 +107,10 @@ void AudioInjector::restart() { qDebug() << "Calling inject audio again to restart an injector"; // call inject audio to start injection over again - injectAudio(); + setupInjection(); + + // emit our restarted signal, this allows the AudioInjectorManager to start considering us again + emit restartedWhileFinished(); } } @@ -148,140 +149,148 @@ void AudioInjector::injectLocally() { } const uchar MAX_INJECTOR_VOLUME = 0xFF; +static const uint64_t NEXT_FRAME_DELTA_ERROR_OR_FINISHED = 0; +static const uint64_t NEXT_FRAME_DELTA_IMMEDIATELY = 1; -void AudioInjector::injectToMixer() { - if (_currentSendOffset < 0 || - _currentSendOffset >= _audioData.size()) { - _currentSendOffset = 0; +uint64_t AudioInjector::injectNextFrame() { + + if (_state == AudioInjector::State::Finished) { + qDebug() << "AudioInjector::injectNextFrame called but AudioInjector has finished and was not restarted. Returning."; + return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } - - auto nodeList = DependencyManager::get(); - - // make sure we actually have samples downloaded to inject - if (_audioData.size()) { - - auto audioPacket = NLPacket::create(PacketType::InjectAudio); - - // setup the packet for injected audio - QDataStream audioPacketStream(audioPacket.get()); - - // pack some placeholder sequence number for now - audioPacketStream << (quint16) 0; - - // pack stream identifier (a generated UUID) - audioPacketStream << QUuid::createUuid(); - - // pack the stereo/mono type of the stream - audioPacketStream << _options.stereo; - - // pack the flag for loopback - uchar loopbackFlag = (uchar) true; - audioPacketStream << loopbackFlag; - - // pack the position for injected audio - int positionOptionOffset = audioPacket->pos(); - audioPacketStream.writeRawData(reinterpret_cast(&_options.position), - sizeof(_options.position)); - - // pack our orientation for injected audio - audioPacketStream.writeRawData(reinterpret_cast(&_options.orientation), - sizeof(_options.orientation)); - - // pack zero for radius - float radius = 0; - audioPacketStream << radius; - - // pack 255 for attenuation byte - int volumeOptionOffset = audioPacket->pos(); - quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; - audioPacketStream << volume; - - audioPacketStream << _options.ignorePenumbra; - - int audioDataOffset = audioPacket->pos(); - - QElapsedTimer timer; - timer.start(); - int nextFrame = 0; - - bool shouldLoop = _options.loop; - - // loop to send off our audio in NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL byte chunks - quint16 outgoingInjectedAudioSequenceNumber = 0; - - while (_currentSendOffset < _audioData.size() && !_shouldStop) { - - int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, - _audioData.size() - _currentSendOffset); - - // Measure the loudness of this frame - _loudness = 0.0f; - for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { - _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendOffset + i)) / - (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); - } - _loudness /= (float)(bytesToCopy / sizeof(int16_t)); + + // if we haven't setup the packet to send then do so now + static int positionOptionOffset = -1; + static int volumeOptionOffset = -1; + static int audioDataOffset = -1; + static quint16 outgoingInjectedAudioSequenceNumber = 0; + static int nextFrame = 0; + static QElapsedTimer frameTimer; + + if (!_currentPacket) { + if (_currentSendOffset < 0 || + _currentSendOffset >= _audioData.size()) { + _currentSendOffset = 0; + } + + // make sure we actually have samples downloaded to inject + if (_audioData.size()) { - audioPacket->seek(0); + nextFrame = 0; + frameTimer.restart(); - // pack the sequence number - audioPacket->writePrimitive(outgoingInjectedAudioSequenceNumber); + _currentPacket = NLPacket::create(PacketType::InjectAudio); - audioPacket->seek(positionOptionOffset); - audioPacket->writePrimitive(_options.position); - audioPacket->writePrimitive(_options.orientation); - - volume = MAX_INJECTOR_VOLUME * _options.volume; - audioPacket->seek(volumeOptionOffset); - audioPacket->writePrimitive(volume); - - audioPacket->seek(audioDataOffset); - - // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet - audioPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); - - // set the correct size used for this packet - audioPacket->setPayloadSize(audioPacket->pos()); - - // grab our audio mixer from the NodeList, if it exists - SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); + // setup the packet for injected audio + QDataStream audioPacketStream(_currentPacket.get()); - if (audioMixer) { - // send off this audio packet - nodeList->sendUnreliablePacket(*audioPacket, *audioMixer); - outgoingInjectedAudioSequenceNumber++; - } - - _currentSendOffset += bytesToCopy; - - // send two packets before the first sleep so the mixer can start playback right away - - if (_currentSendOffset != bytesToCopy && _currentSendOffset < _audioData.size()) { - - - - if (_shouldStop) { - break; - } - - // not the first packet and not done - // sleep for the appropriate time - int usecToSleep = (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - timer.nsecsElapsed() / 1000; - - qDebug() << "AudioInjector" << this << "will sleep on thread" << QThread::currentThread() << "for" << usecToSleep; - - if (usecToSleep > 0) { - usleep(usecToSleep); - } - } - - if (shouldLoop && _currentSendOffset >= _audioData.size()) { - _currentSendOffset = 0; - } + // pack some placeholder sequence number for now + audioPacketStream << (quint16) 0; + + // pack stream identifier (a generated UUID) + audioPacketStream << QUuid::createUuid(); + + // pack the stereo/mono type of the stream + audioPacketStream << _options.stereo; + + // pack the flag for loopback + uchar loopbackFlag = (uchar) true; + audioPacketStream << loopbackFlag; + + // pack the position for injected audio + positionOptionOffset = _currentPacket->pos(); + audioPacketStream.writeRawData(reinterpret_cast(&_options.position), + sizeof(_options.position)); + + // pack our orientation for injected audio + audioPacketStream.writeRawData(reinterpret_cast(&_options.orientation), + sizeof(_options.orientation)); + + // pack zero for radius + float radius = 0; + audioPacketStream << radius; + + // pack 255 for attenuation byte + volumeOptionOffset = _currentPacket->pos(); + quint8 volume = MAX_INJECTOR_VOLUME; + audioPacketStream << volume; + + audioPacketStream << _options.ignorePenumbra; + + audioDataOffset = _currentPacket->pos(); + + } else { + // no samples to inject, return immediately + qDebug() << "AudioInjector::injectNextFrame() called with no samples to inject. Returning."; + return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; } } - - finish(); + + int bytesToCopy = std::min((_options.stereo ? 2 : 1) * AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL, + _audioData.size() - _currentSendOffset); + + // Measure the loudness of this frame + _loudness = 0.0f; + for (int i = 0; i < bytesToCopy; i += sizeof(int16_t)) { + _loudness += abs(*reinterpret_cast(_audioData.data() + _currentSendOffset + i)) / + (AudioConstants::MAX_SAMPLE_VALUE / 2.0f); + } + _loudness /= (float)(bytesToCopy / sizeof(int16_t)); + + _currentPacket->seek(0); + + // pack the sequence number + _currentPacket->writePrimitive(outgoingInjectedAudioSequenceNumber); + + _currentPacket->seek(positionOptionOffset); + _currentPacket->writePrimitive(_options.position); + _currentPacket->writePrimitive(_options.orientation); + + quint8 volume = MAX_INJECTOR_VOLUME * _options.volume; + _currentPacket->seek(volumeOptionOffset); + _currentPacket->writePrimitive(volume); + + _currentPacket->seek(audioDataOffset); + + // copy the next NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL bytes to the packet + _currentPacket->write(_audioData.data() + _currentSendOffset, bytesToCopy); + + // set the correct size used for this packet + _currentPacket->setPayloadSize(_currentPacket->pos()); + + // grab our audio mixer from the NodeList, if it exists + auto nodeList = DependencyManager::get(); + SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); + + if (audioMixer) { + // send off this audio packet + nodeList->sendUnreliablePacket(*_currentPacket, *audioMixer); + outgoingInjectedAudioSequenceNumber++; + } + + _currentSendOffset += bytesToCopy; + + if (_currentSendOffset >= _audioData.size()) { + // we're at the end of the audio data to send + if (_options.loop) { + // we were asked to loop, set our send offset to 0 + _currentSendOffset = 0; + } else { + // we weren't to loop, say that we're done now + qDebug() << "AudioInjector::injectNextFrame has sent all data and was not asked to loop - calling finish()."; + finish(); + return NEXT_FRAME_DELTA_ERROR_OR_FINISHED; + } + } + + if (_currentSendOffset == bytesToCopy) { + // ask AudioInjectorManager to call us right away again to + // immediately send the first two frames so the mixer can start using the audio right away + return NEXT_FRAME_DELTA_IMMEDIATELY; + } else { + return (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - frameTimer.nsecsElapsed() / 1000; + } + } void AudioInjector::stop() { @@ -360,11 +369,11 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj // grab the AudioInjectorManager auto injectorManager = DependencyManager::get(); + // setup parameters required for injection + injector->setupInjection(); + // attempt to thread the new injector if (injectorManager->threadInjector(injector)) { - // call inject audio on the correct thread - QMetaObject::invokeMethod(injector, "injectAudio", Qt::QueuedConnection); - return injector; } else { // we failed to thread the new injector (we are at the max number of injector threads) diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 25b814f0f6..7567a18388 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -26,6 +26,8 @@ #include "Sound.h" class AbstractAudioInterface; +class AudioInjectorManager; +class NLPacket; // In order to make scripting cleaner for the AudioInjector, the script now holds on to the AudioInjector object // until it dies. @@ -59,7 +61,6 @@ public: static AudioInjector* playSound(const QString& soundUrl, const float volume, const float stretchFactor, const glm::vec3 position); public slots: - void injectAudio(); void restart(); void stop(); @@ -74,9 +75,11 @@ public slots: signals: void finished(); + void restartedWhileFinished(); private: - void injectToMixer(); + void setupInjection(); + uint64_t injectNextFrame(); void injectLocally(); void finish(); @@ -88,8 +91,11 @@ private: bool _shouldStop = false; float _loudness = 0.0f; int _currentSendOffset = 0; - AbstractAudioInterface* _localAudioInterface = NULL; - AudioInjectorLocalBuffer* _localBuffer = NULL; + std::unique_ptr _currentPacket { nullptr }; + AbstractAudioInterface* _localAudioInterface { nullptr }; + AudioInjectorLocalBuffer* _localBuffer { nullptr }; + + friend class AudioInjectorManager; }; diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 8eda670ddf..8435048032 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -13,17 +13,28 @@ #include +#include + +#include "AudioConstants.h" #include "AudioInjector.h" AudioInjectorManager::~AudioInjectorManager() { _shouldStop = true; + std::unique_lock lock(_injectorsMutex); + // make sure any still living injectors are stopped and deleted - for (auto injector : _injectors) { - injector->stopAndDeleteLater(); + while (!_injectors.empty()) { + // grab the injector at the front + auto& timePointerPair = _injectors.front(); + + // ask it to stop and be deleted + timePointerPair.second->stopAndDeleteLater(); + + _injectors.pop(); } - // quit and wait on the injector thread, if we ever created it + // quit and wait on the manager thread, if we ever created it if (_thread) { _thread->quit(); _thread->wait(); @@ -43,7 +54,46 @@ void AudioInjectorManager::createThread() { void AudioInjectorManager::run() { while (!_shouldStop) { - // process events in case we have been told to stop or our injectors have been told to stop + // wait until the next injector is ready, or until we get a new injector given to us + std::unique_lock lock(_injectorsMutex); + + if (_injectors.size() > 0) { + // when does the next injector need to send a frame? + // do we get to wait or should we just go for it now? + + auto timeInjectorPair = _injectors.front(); + + auto nextTimestamp = timeInjectorPair.first; + int64_t difference = int64_t(nextTimestamp - usecTimestampNow()); + + if (difference > 0) { + _injectorReady.wait_for(lock, std::chrono::microseconds(difference)); + } + + // loop through the injectors in the map and send whatever frames need to go out + auto front = _injectors.front(); + while (_injectors.size() > 0 && front.first <= usecTimestampNow()) { + // either way we're popping this injector off - get a copy first + auto injector = front.second; + _injectors.pop(); + + if (!injector.isNull()) { + // this is an injector that's ready to go, have it send a frame now + auto nextCallDelta = injector->injectNextFrame(); + + if (nextCallDelta > 0 && !injector->isFinished()) { + // re-enqueue the injector with the correct timing + _injectors.push({ usecTimestampNow() + nextCallDelta, injector }); + } + } + + front = _injectors.front(); + } + } else { + // we have no current injectors, wait until we get at least one before we do anything + _injectorReady.wait(lock); + } + QCoreApplication::processEvents(); } } @@ -54,23 +104,21 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { // guard the injectors vector with a mutex std::unique_lock lock(_injectorsMutex); - // check if we'll be able to thread this injector (do we have < MAX concurrent injectors) + // check if we'll be able to thread this injector (do we have < max concurrent injectors) if (_injectors.size() < MAX_INJECTORS_PER_THREAD) { if (!_thread) { createThread(); } - auto it = nextInjectorIterator(); - qDebug() << "Inserting injector at" << it - _injectors.begin(); - - // store a QPointer to this injector - _injectors.insert(it, QPointer(injector)); - - qDebug() << "Moving injector to thread" << _thread; - // move the injector to the QThread injector->moveToThread(_thread); + // handle a restart once the injector has finished + connect(injector, &AudioInjector::restartedWhileFinished, this, &AudioInjectorManager::restartFinishedInjector); + + // store a QPointer to this injector + addInjectorToQueue(injector); + return true; } else { // unable to thread this injector, at the max @@ -80,17 +128,15 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { } } -AudioInjectorVector::iterator AudioInjectorManager::nextInjectorIterator() { - // find the next usable iterator for an injector - auto it = _injectors.begin(); - - while (it != _injectors.end()) { - if (it->isNull()) { - return it; - } else { - ++it; - } - } - - return it; +void AudioInjectorManager::restartFinishedInjector() { + auto injector = qobject_cast(sender()); + addInjectorToQueue(injector); +} + +void AudioInjectorManager::addInjectorToQueue(AudioInjector* injector) { + // add the injector to the queue with a send timestamp of now + _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); + + // notify our wait condition so we can inject two frames for this injector immediately + _injectorReady.notify_one(); } diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 4e571d2d8d..2b21f0f0ec 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -14,7 +14,7 @@ #ifndef hifi_AudioInjectorManager_h #define hifi_AudioInjectorManager_h -#include +#include #include #include @@ -23,7 +23,9 @@ #include class AudioInjector; -using AudioInjectorVector = std::vector>; +using InjectorQPointer = QPointer; +using TimeInjectorPointerPair = std::pair; +using InjectorQueue = std::queue; class AudioInjectorManager : public QObject, public Dependency { Q_OBJECT @@ -34,18 +36,20 @@ private slots: void run(); private: bool threadInjector(AudioInjector* injector); + void restartFinishedInjector(); + void addInjectorToQueue(AudioInjector* injector); AudioInjectorManager() {}; AudioInjectorManager(const AudioInjectorManager&) = delete; AudioInjectorManager& operator=(const AudioInjectorManager&) = delete; void createThread(); - AudioInjectorVector::iterator nextInjectorIterator(); QThread* _thread { nullptr }; bool _shouldStop { false }; - AudioInjectorVector _injectors; + InjectorQueue _injectors; std::mutex _injectorsMutex; + std::condition_variable _injectorReady; friend class AudioInjector; }; From 4fa934dccf539e546e72cc0f44d030104eda5e4e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 16 Nov 2015 18:54:16 -0800 Subject: [PATCH 18/84] make sure AudioInjectorManager process is on AI thread --- libraries/audio/src/AudioInjectorManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 8435048032..3f83dcc5b5 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -46,7 +46,7 @@ void AudioInjectorManager::createThread() { _thread->setObjectName("Audio Injector Thread"); // when the thread is started, have it call our run to handle injection of audio - connect(_thread, &QThread::started, this, &AudioInjectorManager::run); + connect(_thread, &QThread::started, this, &AudioInjectorManager::run, Qt::DirectConnection); // start the thread _thread->start(); From 7da6ec46c430eaa27972ee83bfce0ea63a4521cc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 14:20:59 -0800 Subject: [PATCH 19/84] more handling of various AudioInjector states --- libraries/audio/src/AudioInjector.cpp | 75 ++++++++++++-------- libraries/audio/src/AudioInjector.h | 4 +- libraries/audio/src/AudioInjectorManager.cpp | 15 ++-- libraries/audio/src/AudioInjectorManager.h | 1 - 4 files changed, 58 insertions(+), 37 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index e88a858608..b0b0527e11 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -65,8 +65,8 @@ void AudioInjector::finish() { } void AudioInjector::setupInjection() { - if (!_hasStarted) { - _hasStarted = true; + if (!_hasSetup) { + _hasSetup = true; // check if we need to offset the sound by some number of seconds if (_options.secondOffset > 0.0f) { @@ -79,18 +79,14 @@ void AudioInjector::setupInjection() { } else { _currentSendOffset = 0; } - - if (_options.localOnly) { - injectLocally(); - } } else { - qCDebug(audio) << "AudioInjector::setupInjection called but already started."; + qCDebug(audio) << "AudioInjector::setupInjection called but already setup."; } } void AudioInjector::restart() { if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "restart"); + QMetaObject::invokeMethod(this, "restart"); return; } @@ -98,23 +94,28 @@ void AudioInjector::restart() { _currentSendOffset = 0; // check our state to decide if we need extra handling for the restart request - if (!isPlaying()) { + if (_state == State::Finished) { // we finished playing, need to reset state so we can get going again - _hasStarted = false; + _hasSetup = false; _shouldStop = false; _state = State::NotFinished; - qDebug() << "Calling inject audio again to restart an injector"; + qDebug() << "Emitting restartedWhileFinished to inject audio again to restart an injector"; // call inject audio to start injection over again setupInjection(); - // emit our restarted signal, this allows the AudioInjectorManager to start considering us again + // if we're a local injector call inject locally to start injecting again + if (_options.localOnly) { + injectLocally(); + } + + // emit our restarted signal, for network injectors this allows the AudioInjectorManager to start considering us again emit restartedWhileFinished(); } } -void AudioInjector::injectLocally() { +bool AudioInjector::injectLocally() { bool success = false; if (_localAudioInterface) { if (_audioData.size() > 0) { @@ -145,7 +146,8 @@ void AudioInjector::injectLocally() { // we never started so we are finished, call our stop method stop(); } - + + return success; } const uchar MAX_INJECTOR_VOLUME = 0xFF; @@ -294,18 +296,21 @@ uint64_t AudioInjector::injectNextFrame() { } void AudioInjector::stop() { - _shouldStop = true; - - if (_options.localOnly) { - // we're only a local injector, so we can say we are finished right away too - finish(); - } + // trigger a call on the injector's thread to change state to finished + QMetaObject::invokeMethod(this, "finish"); } void AudioInjector::triggerDeleteAfterFinish() { - auto expectedState = State::NotFinished; - if (!_state.compare_exchange_strong(expectedState, State::NotFinishedWithPendingDelete)) { + // make sure this fires on the AudioInjector thread + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "triggerDeleteAfterFinish", Qt::QueuedConnection); + return; + } + + if (_state == State::Finished) { stopAndDeleteLater(); + } else { + _state = State::NotFinishedWithPendingDelete; } } @@ -357,7 +362,11 @@ AudioInjector* AudioInjector::playSound(const QString& soundUrl, const float vol AudioInjector* AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options, AbstractAudioInterface* localInterface) { AudioInjector* sound = playSound(buffer, options, localInterface); - sound->_state = AudioInjector::State::NotFinishedWithPendingDelete; + + if (sound) { + sound->_state = AudioInjector::State::NotFinishedWithPendingDelete; + } + return sound; } @@ -372,11 +381,21 @@ AudioInjector* AudioInjector::playSound(const QByteArray& buffer, const AudioInj // setup parameters required for injection injector->setupInjection(); - // attempt to thread the new injector - if (injectorManager->threadInjector(injector)) { - return injector; + if (options.localOnly) { + if (injector->injectLocally()) { + // local injection succeeded, return the pointer to injector + return injector; + } else { + // unable to inject locally, return a nullptr + return nullptr; + } } else { - // we failed to thread the new injector (we are at the max number of injector threads) - return nullptr; + // attempt to thread the new injector + if (injectorManager->threadInjector(injector)) { + return injector; + } else { + // we failed to thread the new injector (we are at the max number of injector threads) + return nullptr; + } } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 7567a18388..9bacb44fde 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -80,14 +80,14 @@ signals: private: void setupInjection(); uint64_t injectNextFrame(); - void injectLocally(); + bool injectLocally(); void finish(); QByteArray _audioData; AudioInjectorOptions _options; std::atomic _state { State::NotFinished }; - bool _hasStarted = false; + bool _hasSetup = false; bool _shouldStop = false; float _loudness = 0.0f; int _currentSendOffset = 0; diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 3f83dcc5b5..eaf1cdd406 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -116,8 +116,11 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { // handle a restart once the injector has finished connect(injector, &AudioInjector::restartedWhileFinished, this, &AudioInjectorManager::restartFinishedInjector); - // store a QPointer to this injector - addInjectorToQueue(injector); + // add the injector to the queue with a send timestamp of now + _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); + + // notify our wait condition so we can inject two frames for this injector immediately + _injectorReady.notify_one(); return true; } else { @@ -130,10 +133,10 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { void AudioInjectorManager::restartFinishedInjector() { auto injector = qobject_cast(sender()); - addInjectorToQueue(injector); -} - -void AudioInjectorManager::addInjectorToQueue(AudioInjector* injector) { + + // guard the injectors vector with a mutex + std::unique_lock lock(_injectorsMutex); + // add the injector to the queue with a send timestamp of now _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 2b21f0f0ec..2b1506ffb3 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -37,7 +37,6 @@ private slots: private: bool threadInjector(AudioInjector* injector); void restartFinishedInjector(); - void addInjectorToQueue(AudioInjector* injector); AudioInjectorManager() {}; AudioInjectorManager(const AudioInjectorManager&) = delete; From d764ff2e87b35efda81c7a49fbca28e0ee205059 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 15:12:04 -0800 Subject: [PATCH 20/84] remove static variables in AudioInjector.cpp --- libraries/audio/src/AudioInjector.cpp | 19 +++++++++++-------- libraries/audio/src/AudioInjector.h | 5 +++++ libraries/audio/src/AudioInjectorManager.cpp | 3 +++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index b0b0527e11..f43325a846 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -165,9 +165,6 @@ uint64_t AudioInjector::injectNextFrame() { static int positionOptionOffset = -1; static int volumeOptionOffset = -1; static int audioDataOffset = -1; - static quint16 outgoingInjectedAudioSequenceNumber = 0; - static int nextFrame = 0; - static QElapsedTimer frameTimer; if (!_currentPacket) { if (_currentSendOffset < 0 || @@ -178,8 +175,14 @@ uint64_t AudioInjector::injectNextFrame() { // make sure we actually have samples downloaded to inject if (_audioData.size()) { - nextFrame = 0; - frameTimer.restart(); + _outgoingSequenceNumber = 0; + _nextFrame = 0; + + if (!_frameTimer) { + _frameTimer = std::unique_ptr(new QElapsedTimer); + } + + _frameTimer->restart(); _currentPacket = NLPacket::create(PacketType::InjectAudio); @@ -242,7 +245,7 @@ uint64_t AudioInjector::injectNextFrame() { _currentPacket->seek(0); // pack the sequence number - _currentPacket->writePrimitive(outgoingInjectedAudioSequenceNumber); + _currentPacket->writePrimitive(_outgoingSequenceNumber); _currentPacket->seek(positionOptionOffset); _currentPacket->writePrimitive(_options.position); @@ -267,7 +270,7 @@ uint64_t AudioInjector::injectNextFrame() { if (audioMixer) { // send off this audio packet nodeList->sendUnreliablePacket(*_currentPacket, *audioMixer); - outgoingInjectedAudioSequenceNumber++; + _outgoingSequenceNumber++; } _currentSendOffset += bytesToCopy; @@ -290,7 +293,7 @@ uint64_t AudioInjector::injectNextFrame() { // immediately send the first two frames so the mixer can start using the audio right away return NEXT_FRAME_DELTA_IMMEDIATELY; } else { - return (++nextFrame * AudioConstants::NETWORK_FRAME_USECS) - frameTimer.nsecsElapsed() / 1000; + return (++_nextFrame * AudioConstants::NETWORK_FRAME_USECS) - _frameTimer->nsecsElapsed() / 1000; } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 9bacb44fde..a02a20c1e7 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -14,6 +14,7 @@ #include +#include #include #include #include @@ -95,6 +96,10 @@ private: AbstractAudioInterface* _localAudioInterface { nullptr }; AudioInjectorLocalBuffer* _localBuffer { nullptr }; + int _nextFrame { 0 }; + std::unique_ptr _frameTimer { nullptr }; + quint16 _outgoingSequenceNumber { 0 }; + friend class AudioInjectorManager; }; diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index eaf1cdd406..ee0a1af77b 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -94,6 +94,9 @@ void AudioInjectorManager::run() { _injectorReady.wait(lock); } + // unlock the lock in case something in process events needs to modify the queue + lock.unlock(); + QCoreApplication::processEvents(); } } From ab5c8e072faada61d0dfbaf2f8d1be279926754e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 16:31:18 -0800 Subject: [PATCH 21/84] handle blocked AudioInjectorManager for restart --- examples/tests/injectorTest.js | 2 +- libraries/audio/src/AudioInjector.cpp | 19 +++++++++++++------ libraries/audio/src/AudioInjector.h | 9 +++++---- libraries/audio/src/AudioInjectorManager.cpp | 5 +---- libraries/audio/src/AudioInjectorManager.h | 3 ++- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/examples/tests/injectorTest.js b/examples/tests/injectorTest.js index 0ad4aad84c..d383b7ef9a 100644 --- a/examples/tests/injectorTest.js +++ b/examples/tests/injectorTest.js @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var soundURL = "atp:44a83a788ccfd2924e35c902c34808b24dbd0309d000299ce01a355f91cf8115.wav"; +var soundURL = "http://hifi-public.s3.amazonaws.com/birarda/medium-crowd.wav"; var audioOptions = { position: MyAvatar.position, volume: 0.5 diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index f43325a846..b3435bb9b7 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -85,8 +85,17 @@ void AudioInjector::setupInjection() { } void AudioInjector::restart() { + // grab the AudioInjectorManager + auto injectorManager = DependencyManager::get(); + if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "restart"); + QMetaObject::invokeMethod(this, "restart"); + + if (!_options.localOnly) { + // notify the AudioInjectorManager to wake up in case it's waiting for new injectors + injectorManager->notifyInjectorReadyCondition(); + } + return; } @@ -100,18 +109,16 @@ void AudioInjector::restart() { _shouldStop = false; _state = State::NotFinished; - qDebug() << "Emitting restartedWhileFinished to inject audio again to restart an injector"; - // call inject audio to start injection over again setupInjection(); // if we're a local injector call inject locally to start injecting again if (_options.localOnly) { injectLocally(); + } else { + // wake the AudioInjectorManager back up if it's stuck waiting + injectorManager->restartFinishedInjector(this); } - - // emit our restarted signal, for network injectors this allows the AudioInjectorManager to start considering us again - emit restartedWhileFinished(); } } diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index a02a20c1e7..0c549eaabb 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -76,15 +76,16 @@ public slots: signals: void finished(); - void restartedWhileFinished(); - + void restarting(); + +private slots: + void finish(); + private: void setupInjection(); uint64_t injectNextFrame(); bool injectLocally(); - void finish(); - QByteArray _audioData; AudioInjectorOptions _options; std::atomic _state { State::NotFinished }; diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index ee0a1af77b..33df3e4e94 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -117,7 +117,6 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { injector->moveToThread(_thread); // handle a restart once the injector has finished - connect(injector, &AudioInjector::restartedWhileFinished, this, &AudioInjectorManager::restartFinishedInjector); // add the injector to the queue with a send timestamp of now _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); @@ -134,9 +133,7 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { } } -void AudioInjectorManager::restartFinishedInjector() { - auto injector = qobject_cast(sender()); - +void AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { // guard the injectors vector with a mutex std::unique_lock lock(_injectorsMutex); diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 2b1506ffb3..7518bb1df8 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -36,7 +36,8 @@ private slots: void run(); private: bool threadInjector(AudioInjector* injector); - void restartFinishedInjector(); + void restartFinishedInjector(AudioInjector* injector); + void notifyInjectorReadyCondition() { _injectorReady.notify_one(); } AudioInjectorManager() {}; AudioInjectorManager(const AudioInjectorManager&) = delete; From 935c0cc13710765520b29bad4d659eeeee4daced Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 16:39:23 -0800 Subject: [PATCH 22/84] handle AudioInjectorManager cleanup in Application --- interface/src/Application.cpp | 4 ++++ libraries/audio/src/AudioInjectorManager.cpp | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9bc0bf0179..6f3116ef9b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -863,6 +863,10 @@ void Application::cleanupBeforeQuit() { // destroy the AudioClient so it and its thread have a chance to go down safely DependencyManager::destroy(); + + // destroy the AudioInjectorManager so it and its thread have a chance to go down safely + // this will also stop any ongoing network injectors + DependencyManager::destroy(); // Destroy third party processes after scripts have finished using them. #ifdef HAVE_DDE diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 33df3e4e94..a441f679d6 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -34,6 +34,12 @@ AudioInjectorManager::~AudioInjectorManager() { _injectors.pop(); } + // get rid of the lock now that we've stopped all living injectors + lock.unlock(); + + // in case the thread is waiting for injectors wake it up now + _injectorReady.notify_one(); + // quit and wait on the manager thread, if we ever created it if (_thread) { _thread->quit(); From 8e4d7c69cee082d4657d4d759ed27b6461049929 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 17:01:11 -0800 Subject: [PATCH 23/84] handle AudioInjectorManager for scripted ACs --- assignment-client/src/Agent.cpp | 10 +++- examples/tests/injectorTest.js | 2 +- libraries/audio/src/AudioInjectorManager.cpp | 56 ++++++++++++-------- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 7ee696693d..953da03d24 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -16,8 +16,9 @@ #include #include -#include #include +#include +#include #include #include #include @@ -43,7 +44,7 @@ Agent::Agent(NLPacket& packet) : DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION, false)) { DependencyManager::get()->setPacketSender(&_entityEditSender); - + auto assetClient = DependencyManager::set(); QThread* assetThread = new QThread; @@ -54,6 +55,8 @@ Agent::Agent(NLPacket& packet) : DependencyManager::set(); DependencyManager::set(); + + DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); @@ -396,6 +399,9 @@ void Agent::aboutToFinish() { DependencyManager::destroy(); assetThread->quit(); assetThread->wait(); + + // cleanup the AudioInjectorManager (and any still running injectors) + DependencyManager::set(); } void Agent::sendPingRequests() { diff --git a/examples/tests/injectorTest.js b/examples/tests/injectorTest.js index d383b7ef9a..171186c91e 100644 --- a/examples/tests/injectorTest.js +++ b/examples/tests/injectorTest.js @@ -11,7 +11,7 @@ var soundURL = "http://hifi-public.s3.amazonaws.com/birarda/medium-crowd.wav"; var audioOptions = { - position: MyAvatar.position, + position: { x: 0.0, y: 0.0, z: 0.0 }, volume: 0.5 }; diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index a441f679d6..4770371e48 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -76,25 +76,28 @@ void AudioInjectorManager::run() { _injectorReady.wait_for(lock, std::chrono::microseconds(difference)); } - // loop through the injectors in the map and send whatever frames need to go out - auto front = _injectors.front(); - while (_injectors.size() > 0 && front.first <= usecTimestampNow()) { - // either way we're popping this injector off - get a copy first - auto injector = front.second; - _injectors.pop(); - - if (!injector.isNull()) { - // this is an injector that's ready to go, have it send a frame now - auto nextCallDelta = injector->injectNextFrame(); + if (_injectors.size() > 0) { + // loop through the injectors in the map and send whatever frames need to go out + auto front = _injectors.front(); + while (_injectors.size() > 0 && front.first <= usecTimestampNow()) { + // either way we're popping this injector off - get a copy first + auto injector = front.second; + _injectors.pop(); - if (nextCallDelta > 0 && !injector->isFinished()) { - // re-enqueue the injector with the correct timing - _injectors.push({ usecTimestampNow() + nextCallDelta, injector }); + if (!injector.isNull()) { + // this is an injector that's ready to go, have it send a frame now + auto nextCallDelta = injector->injectNextFrame(); + + if (nextCallDelta > 0 && !injector->isFinished()) { + // re-enqueue the injector with the correct timing + _injectors.push({ usecTimestampNow() + nextCallDelta, injector }); + } } + + front = _injectors.front(); } - - front = _injectors.front(); } + } else { // we have no current injectors, wait until we get at least one before we do anything _injectorReady.wait(lock); @@ -110,6 +113,11 @@ void AudioInjectorManager::run() { static const int MAX_INJECTORS_PER_THREAD = 50; // calculated based on AudioInjector while loop time, with sufficient padding bool AudioInjectorManager::threadInjector(AudioInjector* injector) { + if (_shouldStop) { + qDebug() << "AudioInjectorManager::threadInjector asked to thread injector but is shutting down."; + return false; + } + // guard the injectors vector with a mutex std::unique_lock lock(_injectorsMutex); @@ -140,12 +148,14 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { } void AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { - // guard the injectors vector with a mutex - std::unique_lock lock(_injectorsMutex); - - // add the injector to the queue with a send timestamp of now - _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); - - // notify our wait condition so we can inject two frames for this injector immediately - _injectorReady.notify_one(); + if (!_shouldStop) { + // guard the injectors vector with a mutex + std::unique_lock lock(_injectorsMutex); + + // add the injector to the queue with a send timestamp of now + _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); + + // notify our wait condition so we can inject two frames for this injector immediately + _injectorReady.notify_one(); + } } From 62b218632d1d441322f72d358ef3b4b09506bc80 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 17:06:21 -0800 Subject: [PATCH 24/84] don't use unneeded atomic for AudioInjector State --- libraries/audio/src/AudioInjector.cpp | 4 ++-- libraries/audio/src/AudioInjector.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index b3435bb9b7..fc7bcfd95d 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -47,8 +47,8 @@ AudioInjector::AudioInjector(const QByteArray& audioData, const AudioInjectorOpt } void AudioInjector::finish() { - State oldState = std::atomic_exchange(&_state, State::Finished); - bool shouldDelete = (oldState == State::NotFinishedWithPendingDelete); + bool shouldDelete = (_state == State::NotFinishedWithPendingDelete); + _state = State::Finished; emit finished(); diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 0c549eaabb..85d01c8ce6 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -88,7 +88,7 @@ private: QByteArray _audioData; AudioInjectorOptions _options; - std::atomic _state { State::NotFinished }; + State _state { State::NotFinished }; bool _hasSetup = false; bool _shouldStop = false; float _loudness = 0.0f; From cf2ad9a8776cd5568e107636c4e6e0112ef290a3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 17:07:29 -0800 Subject: [PATCH 25/84] remove atomic include from AudioInjector --- libraries/audio/src/AudioInjector.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index 85d01c8ce6..d8b168d2e5 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -12,8 +12,6 @@ #ifndef hifi_AudioInjector_h #define hifi_AudioInjector_h -#include - #include #include #include From d20fd6d7e71fc914a3eb4f5c3fef4034cfa274d6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 17:08:48 -0800 Subject: [PATCH 26/84] cap max to 40, clarify comment --- libraries/audio/src/AudioInjectorManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 4770371e48..ee2f9ad5fe 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -110,7 +110,7 @@ void AudioInjectorManager::run() { } } -static const int MAX_INJECTORS_PER_THREAD = 50; // calculated based on AudioInjector while loop time, with sufficient padding +static const int MAX_INJECTORS_PER_THREAD = 40; // calculated based on AudioInjector time to send frame, with sufficient padding bool AudioInjectorManager::threadInjector(AudioInjector* injector) { if (_shouldStop) { From 1c485bacffef9b5ed7ff3105a0aaa3eea642fdaa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 17:11:40 -0800 Subject: [PATCH 27/84] include the condition_variable header in AIM --- libraries/audio/src/AudioInjectorManager.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 7518bb1df8..5e88ade4db 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -14,6 +14,7 @@ #ifndef hifi_AudioInjectorManager_h #define hifi_AudioInjectorManager_h +#include #include #include From 35dd5fb564d9ebd94fc0fbe8d099fbfefe39d15b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 17:26:07 -0800 Subject: [PATCH 28/84] include NLPacket in AudioInjector for unique_ptr --- libraries/audio/src/AudioInjector.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index d8b168d2e5..ae9048db83 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -20,13 +20,14 @@ #include #include +#include + #include "AudioInjectorLocalBuffer.h" #include "AudioInjectorOptions.h" #include "Sound.h" class AbstractAudioInterface; class AudioInjectorManager; -class NLPacket; // In order to make scripting cleaner for the AudioInjector, the script now holds on to the AudioInjector object // until it dies. From 2aa56b538410fd5f8be79b7bd4fb4df754973fe1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 17 Nov 2015 17:26:45 -0800 Subject: [PATCH 29/84] include memory header for unique_ptr --- libraries/audio/src/AudioInjector.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/audio/src/AudioInjector.h b/libraries/audio/src/AudioInjector.h index ae9048db83..f815b6fe3a 100644 --- a/libraries/audio/src/AudioInjector.h +++ b/libraries/audio/src/AudioInjector.h @@ -12,6 +12,8 @@ #ifndef hifi_AudioInjector_h #define hifi_AudioInjector_h +#include + #include #include #include From 9a39da9050950103701a2bfe30d5819d9a1fcc55 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 17 Nov 2015 18:53:38 -0800 Subject: [PATCH 30/84] new absolutePoses work for avatars, but not for model entities. --- libraries/animation/src/AnimSkeleton.h | 1 + libraries/animation/src/Rig.cpp | 100 +++++++++++++++---------- libraries/animation/src/Rig.h | 8 +- 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 9dda313528..ebf6bfa3d6 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -31,6 +31,7 @@ public: // absolute pose, not relative to parent const AnimPose& getAbsoluteBindPose(int jointIndex) const; + const AnimPoseVec& getAbsoluteBindPoses() const { return _absoluteBindPoses; } AnimPose getRootAbsoluteBindPoseByChildName(const QString& childName) const; // relative to parent pose diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index f937144491..71e7a27d47 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -24,7 +24,7 @@ #include "IKTarget.h" #ifdef NDEBUG -#define ASSERT() +#define ASSERT(cond) #else #define ASSERT(cond) \ do { \ @@ -157,16 +157,16 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in _animSkeleton = std::make_shared(geometry); _relativePoses.clear(); - _relativePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + _relativePoses = _animSkeleton->getRelativeBindPoses(); _absolutePoses.clear(); - _absolutePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + _absolutePoses = _animSkeleton->getAbsoluteBindPoses(); _overridePoses.clear(); - _overridePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + _overridePoses = _animSkeleton->getRelativeBindPoses(); _overrideFlags.clear(); - _overrideFlags.resize(_animSkeleton->getNumJoints(), true); + _overrideFlags.resize(_animSkeleton->getNumJoints(), false); // AJT: LEGACY { @@ -226,7 +226,12 @@ int Rig::indexOfJoint(const QString& jointName) { } void Rig::setModelOffset(const glm::mat4& modelOffset) { + // AJT: LEGACY + { + _legacyModelOffset = modelOffset; + } _modelOffset = AnimPose(modelOffset); + _modelScale = _modelOffset.scale.x; } // AJT: REMOVE @@ -409,34 +414,6 @@ bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm: return true; } -glm::mat4 Rig::getJointTransform(int jointIndex) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return glm::mat4(); - } - - /* - // AJT: test if _absolutePoses are the same as jointTransforms - glm::mat4 newMat = _absolutePoses[jointIndex]; - glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); - - const float EPSILON = 0.0001f; - if (glm::length(newMat[0] - oldMat[0]) > EPSILON || - glm::length(newMat[1] - oldMat[1]) > EPSILON || - glm::length(newMat[2] - oldMat[2]) > EPSILON || - glm::length(newMat[3] - oldMat[3]) > EPSILON) { - - // error? - qCDebug(animation) << "AJT: mismatch for joint[" << jointIndex; - qCDebug(animation) << "AJT: oldMat = " << AnimPose(oldMat); - qCDebug(animation) << "AJT: newMat = " << AnimPose(newMat); - - } - */ - - // AJT: LEGACY - return _jointStates[jointIndex].getTransform(); -} - void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const { ASSERT(referenceSpeeds.size() > 0); @@ -725,8 +702,8 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { AnimNode::Triggers triggersOut; _relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut); if (_relativePoses.size() != _animSkeleton->getNumJoints()) { - // animations haven't been loaded yet. - _relativePoses.resize(_animSkeleton->getNumJoints(), AnimPose::identity); + // animations haven't fully loaded yet. + _relativePoses = _animSkeleton->getRelativeBindPoses(); } _animVars.clearTriggers(); for (auto& trigger : triggersOut) { @@ -1245,16 +1222,22 @@ void Rig::buildAbsolutePoses() { for (int i = 0; i < (int)_relativePoses.size(); i++) { int parentIndex = _animSkeleton->getParentIndex(i); if (parentIndex == -1) { - _absolutePoses[i] = _modelOffset * _relativePoses[i]; + _absolutePoses[i] = _relativePoses[i]; } else { - _absolutePoses[i] = _modelOffset * _absolutePoses[parentIndex] * _relativePoses[i]; + _absolutePoses[i] = _absolutePoses[parentIndex] * _relativePoses[i]; } } + for (int i = 0; i < (int)_absolutePoses.size(); i++) { + _absolutePoses[i].trans = _modelOffset.trans + _absolutePoses[i].trans; + _absolutePoses[i].rot = _modelOffset.rot * _absolutePoses[i].rot; + _absolutePoses[i].scale = glm::vec3(_modelScale); + } + // AJT: LEGACY { // Build the joint states - glm::mat4 rootTransform = (glm::mat4)_modelOffset; + glm::mat4 rootTransform = _legacyModelOffset; for (int i = 0; i < (int)_animSkeleton->getNumJoints(); i++) { JointState& state = _jointStates[i]; @@ -1272,3 +1255,44 @@ void Rig::buildAbsolutePoses() { } } } + +glm::mat4 Rig::getJointTransform(int jointIndex) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return glm::mat4(); + } + + // AJT: test if _absolutePoses are the same as jointTransforms + // only test once we have a full skeleton (ryan avatar) + //if (_jointStates.size() == 73) { + /* + if (!_animNode && _jointStates.size() != 73) { + // should display for model entities + glm::mat4 newMat = _absolutePoses[jointIndex]; + glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); + + const float EPSILON = 0.005f; + if (glm::length(newMat[0] - oldMat[0]) > EPSILON || + glm::length(newMat[1] - oldMat[1]) > EPSILON || + glm::length(newMat[2] - oldMat[2]) > EPSILON || + glm::length(newMat[3] - oldMat[3]) > EPSILON) { + + // error? + qCDebug(animation).nospace() << "AJT: mismatch for joint[" << jointIndex << "]"; + qCDebug(animation) << "AJT: oldMat = " << AnimPose(oldMat); + qCDebug(animation) << "AJT: newMat = " << AnimPose(newMat); + + } + } + */ + + // AJT: LEGACY + { + return _jointStates[jointIndex].getTransform(); + } + + // AJT: TODO this works for MyAvatar but not model entities. WTF + { + //return _absolutePoses[jointIndex]; + } +} + diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 2eea59bd7f..09ec7c0e2f 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -176,11 +176,13 @@ public: // AJT: TODO: LEGACY QVector _jointStates; + glm::mat4 _legacyModelOffset; AnimPose _modelOffset; - AnimPoseVec _relativePoses; - AnimPoseVec _absolutePoses; - AnimPoseVec _overridePoses; + float _modelScale; + AnimPoseVec _relativePoses; // in fbx model space relative to parent. + AnimPoseVec _absolutePoses; // in fbx model space after _modelOffset is applied. + AnimPoseVec _overridePoses; // in fbx model space relative to parent. std::vector _overrideFlags; int _rootJointIndex { -1 }; From 5ffef7f41ad8a30288e7f32fe19aae2663842189 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 18 Nov 2015 16:02:30 -0800 Subject: [PATCH 31/84] AnimPose: bug fix for extracting rotations from matrices with large scale --- libraries/animation/src/AnimPose.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index bae34509ae..235dc902af 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -9,7 +9,9 @@ // #include "AnimPose.h" -#include "GLMHelpers.h" +#include +#include +#include const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), glm::quat(), @@ -17,7 +19,13 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), AnimPose::AnimPose(const glm::mat4& mat) { scale = extractScale(mat); - rot = glm::normalize(glm::quat_cast(mat)); + if (std::max(std::max(scale.x, scale.y), scale.z) > 1.01f) { + // quat_cast doesn't work so well with scaled matrices, so cancel it out. + mat4 tmp = glm::scale(mat, 1.0f / scale); + rot = glm::normalize(glm::quat_cast(tmp)); + } else { + rot = glm::normalize(glm::quat_cast(mat)); + } trans = extractTranslation(mat); } From 3a74d188b0b71500323a5fa04548b01092374281 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 18 Nov 2015 16:03:28 -0800 Subject: [PATCH 32/84] AnimSkeleton: Added default poses --- libraries/animation/src/AnimSkeleton.cpp | 77 +++++++++++++++++++----- libraries/animation/src/AnimSkeleton.h | 9 +++ 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 7ec8db1490..f6c5ce09db 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -70,6 +70,14 @@ const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const { return _relativeBindPoses[jointIndex]; } +const AnimPose& AnimSkeleton::getRelativeDefaultPose(int jointIndex) const { + return _relativeDefaultPoses[jointIndex]; +} + +const AnimPose& AnimSkeleton::getAbsoluteDefaultPose(int jointIndex) const { + return _absoluteDefaultPoses[jointIndex]; +} + int AnimSkeleton::getParentIndex(int jointIndex) const { return _joints[jointIndex].parentIndex; } @@ -93,14 +101,31 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, _absoluteBindPoses.reserve(joints.size()); _relativeBindPoses.reserve(joints.size()); + // build a chache of default poses + _absoluteDefaultPoses.reserve(joints.size()); + _relativeDefaultPoses.reserve(joints.size()); + // iterate over FBXJoints and extract the bind pose information. for (size_t i = 0; i < joints.size(); i++) { + + // build relative and absolute default poses + glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation); + glm::mat4 relDefaultMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform; + AnimPose relDefaultPose(relDefaultMat); + _relativeDefaultPoses.push_back(relDefaultPose); + int parentIndex = getParentIndex(i); + if (parentIndex >= 0) { + _absoluteDefaultPoses.push_back(_absoluteDefaultPoses[parentIndex] * relDefaultPose); + } else { + _absoluteDefaultPoses.push_back(relDefaultPose); + } + + // build relative and absolute bind poses if (_joints[i].bindTransformFoundInCluster) { // Use the FBXJoint::bindTransform, which is absolute model coordinates // i.e. not relative to it's parent. AnimPose absoluteBindPose(_joints[i].bindTransform); _absoluteBindPoses.push_back(absoluteBindPose); - int parentIndex = getParentIndex(i); if (parentIndex >= 0) { AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse(); _relativeBindPoses.push_back(inverseParentAbsoluteBindPose * absoluteBindPose); @@ -108,39 +133,40 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, _relativeBindPoses.push_back(absoluteBindPose); } } else { - // use FBXJoint's local transform, instead - glm::mat4 rotTransform = glm::mat4_cast(_joints[i].preRotation * _joints[i].rotation * _joints[i].postRotation); - glm::mat4 relBindMat = glm::translate(_joints[i].translation) * _joints[i].preTransform * rotTransform * _joints[i].postTransform; - AnimPose relBindPose(relBindMat); - _relativeBindPoses.push_back(relBindPose); - - int parentIndex = getParentIndex(i); + // use default transform instead + _relativeBindPoses.push_back(relDefaultPose); if (parentIndex >= 0) { - _absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relBindPose); + _absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relDefaultPose); } else { - _absoluteBindPoses.push_back(relBindPose); + _absoluteBindPoses.push_back(relDefaultPose); } } } // now we want to normalize scale from geometryOffset to all poses. // This will ensure our bone translations will be in meters, even if the model was authored with some other unit of mesure. - for (auto& absPose : _absoluteBindPoses) { + normalizeScale(geometryOffset, _relativeBindPoses, _absoluteBindPoses); + normalizeScale(geometryOffset, _relativeDefaultPoses, _absoluteDefaultPoses); +} + +void AnimSkeleton::normalizeScale(const AnimPose& geometryOffset, AnimPoseVec& relPoses, AnimPoseVec& absPoses) const { + for (auto& absPose : absPoses) { absPose.trans = (geometryOffset * absPose).trans; - absPose.scale = vec3(1, 1, 1); } // re-compute relative poses based on the modified absolute poses. - for (size_t i = 0; i < _relativeBindPoses.size(); i++) { + for (size_t i = 0; i < relPoses.size(); i++) { int parentIndex = getParentIndex(i); if (parentIndex >= 0) { - _relativeBindPoses[i] = _absoluteBindPoses[parentIndex].inverse() * _absoluteBindPoses[i]; + relPoses[i] = absPoses[parentIndex].inverse() * absPoses[i]; } else { - _relativeBindPoses[i] = _absoluteBindPoses[i]; + relPoses[i] = absPoses[i]; } } } +#define DUMP_FBX_JOINTS + #ifndef NDEBUG void AnimSkeleton::dump() const { qCDebug(animation) << "["; @@ -150,6 +176,25 @@ void AnimSkeleton::dump() const { qCDebug(animation) << " name =" << getJointName(i); qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i); qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i); + qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i); + qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i); +#ifdef DUMP_FBX_JOINTS + qCDebug(animation) << " isFree =" << _joints[i].isFree; + qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage; + qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex; + qCDebug(animation) << " translation =" << _joints[i].translation; + qCDebug(animation) << " preTransform =" << _joints[i].preTransform; + qCDebug(animation) << " preRotation =" << _joints[i].preRotation; + qCDebug(animation) << " rotation =" << _joints[i].rotation; + qCDebug(animation) << " postRotation =" << _joints[i].postRotation; + qCDebug(animation) << " postTransform =" << _joints[i].postTransform; + qCDebug(animation) << " transform =" << _joints[i].transform; + qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax; + qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation; + qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation; + qCDebug(animation) << " bindTransform" << _joints[i].bindTransform; + qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint; +#endif if (getParentIndex(i) >= 0) { qCDebug(animation) << " parent =" << getJointName(getParentIndex(i)); } @@ -166,6 +211,8 @@ void AnimSkeleton::dump(const AnimPoseVec& poses) const { qCDebug(animation) << " name =" << getJointName(i); qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i); qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i); + qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i); + qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i); qCDebug(animation) << " pose =" << poses[i]; if (getParentIndex(i) >= 0) { qCDebug(animation) << " parent =" << getJointName(getParentIndex(i)); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index ebf6bfa3d6..7c57096280 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -38,6 +38,12 @@ public: const AnimPose& getRelativeBindPose(int jointIndex) const; const AnimPoseVec& getRelativeBindPoses() const { return _relativeBindPoses; } + // the default poses are the orientations of the joints on frame 0. + const AnimPose& getRelativeDefaultPose(int jointIndex) const; + const AnimPoseVec& getRelativeDefaultPoses() const { return _relativeDefaultPoses; } + const AnimPose& getAbsoluteDefaultPose(int jointIndex) const; + const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; } + int getParentIndex(int jointIndex) const; AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const; @@ -49,10 +55,13 @@ public: protected: void buildSkeletonFromJoints(const std::vector& joints, const AnimPose& geometryOffset); + void normalizeScale(const AnimPose& geometryOffset, AnimPoseVec& relPoses, AnimPoseVec& absPoses) const; std::vector _joints; AnimPoseVec _absoluteBindPoses; AnimPoseVec _relativeBindPoses; + AnimPoseVec _relativeDefaultPoses; + AnimPoseVec _absoluteDefaultPoses; // no copies AnimSkeleton(const AnimSkeleton&) = delete; From b054ef148894752814a03aef9148c1e6aabd47ce Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 18 Nov 2015 16:04:34 -0800 Subject: [PATCH 33/84] Rig: fixes for rigs used for ModelEntities. --- libraries/animation/src/Rig.cpp | 30 +++++++++++++++--------------- libraries/animation/src/Rig.h | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 00f93a5d5e..426661455f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -168,16 +168,19 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex) { _animSkeleton = std::make_shared(geometry); - computeEyesInRootFrame(_animSkeleton->getRelativeBindPoses()); + + _animSkeleton->dump(); + + computeEyesInRootFrame(_animSkeleton->getRelativeDefaultPoses()); _relativePoses.clear(); - _relativePoses = _animSkeleton->getRelativeBindPoses(); + _relativePoses = _animSkeleton->getRelativeDefaultPoses(); _absolutePoses.clear(); - _absolutePoses = _animSkeleton->getAbsoluteBindPoses(); + _absolutePoses = _animSkeleton->getAbsoluteDefaultPoses(); _overridePoses.clear(); - _overridePoses = _animSkeleton->getRelativeBindPoses(); + _overridePoses = _animSkeleton->getRelativeDefaultPoses(); _overrideFlags.clear(); _overrideFlags.resize(_animSkeleton->getNumJoints(), false); @@ -245,7 +248,7 @@ void Rig::setModelOffset(const glm::mat4& modelOffset) { _legacyModelOffset = modelOffset; } _modelOffset = AnimPose(modelOffset); - _modelScale = _modelOffset.scale.x; + _modelScale = _modelOffset.scale; } // AJT: REMOVE @@ -1262,7 +1265,7 @@ void Rig::buildAbsolutePoses() { for (int i = 0; i < (int)_absolutePoses.size(); i++) { _absolutePoses[i].trans = _modelOffset.trans + _absolutePoses[i].trans; _absolutePoses[i].rot = _modelOffset.rot * _absolutePoses[i].rot; - _absolutePoses[i].scale = glm::vec3(_modelScale); + _absolutePoses[i].scale = _absolutePoses[i].scale * _modelScale; } // AJT: LEGACY @@ -1295,8 +1298,9 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { // AJT: test if _absolutePoses are the same as jointTransforms // only test once we have a full skeleton (ryan avatar) //if (_jointStates.size() == 73) { - /* - if (!_animNode && _jointStates.size() != 73) { + + // check for differences between jointStates and _absolutePoses! + { // should display for model entities glm::mat4 newMat = _absolutePoses[jointIndex]; glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); @@ -1308,22 +1312,18 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { glm::length(newMat[3] - oldMat[3]) > EPSILON) { // error? - qCDebug(animation).nospace() << "AJT: mismatch for joint[" << jointIndex << "]"; + qCDebug(animation).nospace() << "AJT: mismatch for " << _animSkeleton->getJointName(jointIndex) << ", joint[" << jointIndex << "]"; qCDebug(animation) << "AJT: oldMat = " << AnimPose(oldMat); qCDebug(animation) << "AJT: newMat = " << AnimPose(newMat); } } - */ // AJT: LEGACY { - return _jointStates[jointIndex].getTransform(); + //return _jointStates[jointIndex].getTransform(); } - // AJT: TODO this works for MyAvatar but not model entities. WTF - { - //return _absolutePoses[jointIndex]; - } + return _absolutePoses[jointIndex]; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index d6bbee7ebf..88a0b45529 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -183,7 +183,7 @@ public: glm::mat4 _legacyModelOffset; AnimPose _modelOffset; - float _modelScale; + glm::vec3 _modelScale; AnimPoseVec _relativePoses; // in fbx model space relative to parent. AnimPoseVec _absolutePoses; // in fbx model space after _modelOffset is applied. AnimPoseVec _overridePoses; // in fbx model space relative to parent. From 721da2943257a54bad4e40b2a360189defee62bc Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 18 Nov 2015 18:47:33 -0800 Subject: [PATCH 34/84] WIP checkpoint * No longer normalizing scale in AnimSkeleton and AnimClip This means graph is animating in 'geometry' coordinates before unit scale is even applied. This is necessary to properly work with both Avatar based models and ModelEntity based models Many things are broken. * debug rendering (translations are x100) * IK hand targets * follow cam * I did not even dare to try HMD mode --- interface/src/avatar/MyAvatar.cpp | 21 ++++++++++++++------ interface/src/avatar/SkeletonModel.cpp | 9 +++++---- libraries/animation/src/AnimClip.cpp | 18 ++++++++--------- libraries/animation/src/AnimSkeleton.cpp | 17 +++++++++++++--- libraries/animation/src/AnimSkeleton.h | 7 ++++--- libraries/animation/src/JointState.cpp | 2 +- libraries/animation/src/Rig.cpp | 25 ++++++++++++++---------- libraries/animation/src/Rig.h | 8 +++++--- libraries/render-utils/src/Model.cpp | 8 ++++---- 9 files changed, 72 insertions(+), 43 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5bacb567e1..cc8f232436 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -359,11 +359,11 @@ void MyAvatar::updateHMDFollowVelocity() { // This is so the correct camera can be used for rendering. void MyAvatar::updateSensorToWorldMatrix() { -#ifdef DEBUG_RENDERING +//#ifdef DEBUG_RENDERING // draw marker about avatar's position const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); DebugDraw::getInstance().addMyAvatarMarker("pos", glm::quat(), glm::vec3(), red); -#endif +//#endif // update the sensor mat so that the body position will end up in the desired // position when driven from the head. @@ -1286,7 +1286,10 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { if (_enableDebugDrawAnimPose && _debugDrawSkeleton) { glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); + auto rig = _skeletonModel.getRig(); + // AJT: TODO move this into rig! // build AnimPoseVec from JointStates. + // AJT: TODO THIS SHIT IS ALL BROKEN AnimPoseVec poses; poses.reserve(_debugDrawSkeleton->getNumJoints()); for (int i = 0; i < _debugDrawSkeleton->getNumJoints(); i++) { @@ -1297,6 +1300,11 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { _rig->getJointTranslation(i, jointTrans); pose.rot = pose.rot * jointRot; pose.trans = jointTrans; + /* + if (_debugDrawSkeleton->getParentIndex(i) < 0) { + pose = _rig->getGeometryOffset() * pose; + } + */ poses.push_back(pose); } @@ -1765,6 +1773,7 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f); const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.05f, 0.0f); + AnimPose geometryOffset = _rig->getGeometryOffset(); vec3 localEyes, localNeck; if (!_debugDrawSkeleton) { const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); @@ -1780,10 +1789,10 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck"); int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips"); - glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; - glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; - glm::vec3 absNeckPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; - glm::vec3 absHipsPos = neckIndex != -1 ? _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS; + glm::vec3 absRightEyePos = rightEyeIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; + glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; + glm::vec3 absNeckPos = neckIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; + glm::vec3 absHipsPos = neckIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS; const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); localEyes = rotY180 * (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8b34d9248e..b60ed85429 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -43,7 +43,8 @@ SkeletonModel::~SkeletonModel() { void SkeletonModel::initJointStates() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + glm::mat4 geometryOffset = geometry.offset; + glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); int rootJointIndex = geometry.rootJointIndex; int leftHandJointIndex = geometry.leftHandJointIndex; @@ -241,9 +242,9 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { Model::simulate(deltaTime, fullUpdate); // let rig compute the model offset - glm::vec3 modelOffset; - if (_rig->getModelOffset(modelOffset)) { - setOffset(modelOffset); + glm::vec3 registrationPoint; + if (_rig->getModelRegistrationPoint(registrationPoint)) { + setOffset(registrationPoint); } if (!isActive() || !_owningAvatar->isMyAvatar()) { diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index b2b6f728fd..582a55cd62 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -107,8 +107,6 @@ void AnimClip::copyFromNetworkAnim() { const int skeletonJointCount = _skeleton->getNumJoints(); _anim.resize(frameCount); - const glm::vec3 offsetScale = extractScale(geom.offset); - for (int frame = 0; frame < frameCount; frame++) { // init all joints in animation to bind pose @@ -125,23 +123,25 @@ void AnimClip::copyFromNetworkAnim() { // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { - const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint] * offsetScale; - const AnimPose& relBindPose = _skeleton->getRelativeBindPose(skeletonJoint); + const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; + // AJT: TODO: use the actual preRotation not the bind pose here. + const AnimPose& jointOrientPose = _skeleton->getRelativeBindPose(skeletonJoint); + const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); // used to adjust translation offsets, so large translation animatons on the reference skeleton // will be adjusted when played on a skeleton with short limbs. - float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relBindPose.trans) / glm::length(fbxZeroTrans)); + float limbLengthScale = fabsf(glm::length(fbxZeroTrans)) <= 0.0001f ? 1.0f : (glm::length(relDefaultPose.trans) / glm::length(fbxZeroTrans)); AnimPose& pose = _anim[frame][skeletonJoint]; const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; - // rotation in fbxAnimationFrame is a delta from a reference skeleton bind pose. - pose.rot = relBindPose.rot * fbxAnimFrame.rotations[animJoint]; + // rotation in fbxAnimationFrame is a delta from its jointOrientPose (aka preRotation) + pose.rot = jointOrientPose.rot * fbxAnimFrame.rotations[animJoint]; // translation in fbxAnimationFrame is not a delta. // convert it into a delta by subtracting from the first frame. - const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint] * offsetScale; - pose.trans = relBindPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans); + const glm::vec3& fbxTrans = fbxAnimFrame.translations[animJoint]; + pose.trans = relDefaultPose.trans + limbLengthScale * (fbxTrans - fbxZeroTrans); } } } diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index f6c5ce09db..ae73d380e6 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -24,12 +24,17 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { joints.push_back(joint); } + // AJT: REMOVE + /* AnimPose geometryOffset(fbxGeometry.offset); buildSkeletonFromJoints(joints, geometryOffset); + */ + + buildSkeletonFromJoints(joints); } -AnimSkeleton::AnimSkeleton(const std::vector& joints, const AnimPose& geometryOffset) { - buildSkeletonFromJoints(joints, geometryOffset); +AnimSkeleton::AnimSkeleton(const std::vector& joints) { + buildSkeletonFromJoints(joints); } int AnimSkeleton::nameToJointIndex(const QString& jointName) const { @@ -94,7 +99,7 @@ AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses) } } -void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, const AnimPose& geometryOffset) { +void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { _joints = joints; // build a cache of bind poses @@ -143,12 +148,17 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } } + // AJT: REMOVE + /* // now we want to normalize scale from geometryOffset to all poses. // This will ensure our bone translations will be in meters, even if the model was authored with some other unit of mesure. normalizeScale(geometryOffset, _relativeBindPoses, _absoluteBindPoses); normalizeScale(geometryOffset, _relativeDefaultPoses, _absoluteDefaultPoses); + */ } +/* +// AJT: REMOVE void AnimSkeleton::normalizeScale(const AnimPose& geometryOffset, AnimPoseVec& relPoses, AnimPoseVec& absPoses) const { for (auto& absPose : absPoses) { absPose.trans = (geometryOffset * absPose).trans; @@ -164,6 +174,7 @@ void AnimSkeleton::normalizeScale(const AnimPose& geometryOffset, AnimPoseVec& r } } } +*/ #define DUMP_FBX_JOINTS diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 7c57096280..8711f24587 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -24,7 +24,7 @@ public: using ConstPointer = std::shared_ptr; AnimSkeleton(const FBXGeometry& fbxGeometry); - AnimSkeleton(const std::vector& joints, const AnimPose& geometryOffset); + AnimSkeleton(const std::vector& joints); int nameToJointIndex(const QString& jointName) const; const QString& getJointName(int jointIndex) const; int getNumJoints() const; @@ -54,8 +54,9 @@ public: #endif protected: - void buildSkeletonFromJoints(const std::vector& joints, const AnimPose& geometryOffset); - void normalizeScale(const AnimPose& geometryOffset, AnimPoseVec& relPoses, AnimPoseVec& absPoses) const; + void buildSkeletonFromJoints(const std::vector& joints); + // AJT: REMOVE + //void normalizeScale(const AnimPose& geometryOffset, AnimPoseVec& relPoses, AnimPoseVec& absPoses) const; std::vector _joints; AnimPoseVec _absoluteBindPoses; diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp index 5bc88ad57f..4c4e40119a 100644 --- a/libraries/animation/src/JointState.cpp +++ b/libraries/animation/src/JointState.cpp @@ -73,7 +73,7 @@ glm::quat JointState::getRotation() const { void JointState::initTransform(const glm::mat4& parentTransform) { - _unitsScale = extractScale(parentTransform); + //_unitsScale = extractScale(parentTransform); computeTransform(parentTransform); _positionInParentFrame = glm::inverse(extractRotation(parentTransform)) * (extractTranslation(_transform) - extractTranslation(parentTransform)); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 426661455f..5dfb90f0eb 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -167,9 +167,10 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in int leftHandJointIndex, int leftElbowJointIndex, int leftShoulderJointIndex, int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex) { + _geometryOffset = AnimPose(geometry.offset); _animSkeleton = std::make_shared(geometry); - _animSkeleton->dump(); + //_animSkeleton->dump(); computeEyesInRootFrame(_animSkeleton->getRelativeDefaultPoses()); @@ -198,12 +199,13 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in // was old Rig::initJointStates // compute model transforms + glm::mat4 rootTransform = (glm::mat4)(_modelOffset * _geometryOffset); int numStates = _animSkeleton->getNumJoints(); for (int i = 0; i < numStates; ++i) { JointState& state = _jointStates[i]; int parentIndex = state.getParentIndex(); if (parentIndex == -1) { - state.initTransform(modelOffset); + state.initTransform(rootTransform); } else { const JointState& parentState = _jointStates.at(parentIndex); state.initTransform(parentState.getTransform()); @@ -248,7 +250,6 @@ void Rig::setModelOffset(const glm::mat4& modelOffset) { _legacyModelOffset = modelOffset; } _modelOffset = AnimPose(modelOffset); - _modelScale = _modelOffset.scale; } // AJT: REMOVE @@ -1220,9 +1221,9 @@ void Rig::initAnimGraph(const QUrl& url) { }); } -bool Rig::getModelOffset(glm::vec3& modelOffsetOut) const { +bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const { if (_animSkeleton && _rootJointIndex >= 0) { - modelOffsetOut = -_animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; + modelRegistrationPointOut = _geometryOffset * -_animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; return true; } else { return false; @@ -1263,15 +1264,19 @@ void Rig::buildAbsolutePoses() { } for (int i = 0; i < (int)_absolutePoses.size(); i++) { + _absolutePoses[i] = _modelOffset * _geometryOffset * _absolutePoses[i]; + // AJT: REMOVE + /* _absolutePoses[i].trans = _modelOffset.trans + _absolutePoses[i].trans; _absolutePoses[i].rot = _modelOffset.rot * _absolutePoses[i].rot; _absolutePoses[i].scale = _absolutePoses[i].scale * _modelScale; + */ } // AJT: LEGACY { // Build the joint states - glm::mat4 rootTransform = _legacyModelOffset; + glm::mat4 rootTransform = (glm::mat4)(_modelOffset * _geometryOffset); for (int i = 0; i < (int)_animSkeleton->getNumJoints(); i++) { JointState& state = _jointStates[i]; @@ -1300,12 +1305,12 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { //if (_jointStates.size() == 73) { // check for differences between jointStates and _absolutePoses! - { + if (false) { // should display for model entities glm::mat4 newMat = _absolutePoses[jointIndex]; glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); - const float EPSILON = 0.005f; + const float EPSILON = 0.01f; if (glm::length(newMat[0] - oldMat[0]) > EPSILON || glm::length(newMat[1] - oldMat[1]) > EPSILON || glm::length(newMat[2] - oldMat[2]) > EPSILON || @@ -1321,9 +1326,9 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { // AJT: LEGACY { - //return _jointStates[jointIndex].getTransform(); + return _jointStates[jointIndex].getTransform(); } - return _absolutePoses[jointIndex]; + //return _absolutePoses[jointIndex]; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 88a0b45529..182b7e632b 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -94,6 +94,7 @@ public: int indexOfJoint(const QString& jointName); void setModelOffset(const glm::mat4& modelOffset); + const AnimPose& getGeometryOffset() const { return _geometryOffset; } // AJT: REMOVE /* @@ -162,7 +163,7 @@ public: void removeAnimationStateHandler(QScriptValue handler); void animationStateHandlerResult(int identifier, QScriptValue result); - bool getModelOffset(glm::vec3& modelOffsetOut) const; + bool getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const; const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } @@ -182,8 +183,9 @@ public: QVector _jointStates; glm::mat4 _legacyModelOffset; - AnimPose _modelOffset; - glm::vec3 _modelScale; + // AJT: TODO document these better + AnimPose _modelOffset; // model to avatar space (without 180 flip) + AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets) AnimPoseVec _relativePoses; // in fbx model space relative to parent. AnimPoseVec _absolutePoses; // in fbx model space after _modelOffset is applied. AnimPoseVec _overridePoses; // in fbx model space relative to parent. diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 19c3131233..07d624c73f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -107,7 +107,7 @@ void Model::initJointTransforms() { return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig->setModelOffset(modelOffset); } @@ -160,7 +160,7 @@ bool Model::updateGeometry() { // virtual void Model::initJointStates() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); int rootJointIndex = geometry.rootJointIndex; int leftHandJointIndex = geometry.leftHandJointIndex; @@ -947,7 +947,7 @@ void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints const FBXGeometry& geometry = _geometry->getFBXGeometry(); - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); updateRig(deltaTime, parentTransform); } void Model::updateClusterMatrices() { @@ -1011,7 +1011,7 @@ void Model::updateClusterMatrices() { void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); _rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); } From 982e2c06a9c3a7e9d3987718eebfb57d70f9e1df Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 19 Nov 2015 12:14:04 -0800 Subject: [PATCH 35/84] Rig: Switched over to use AnimPoses instead of JointStates * fixed debug rendering * improved jointState/animPose diff detection code. --- interface/src/avatar/MyAvatar.cpp | 35 ++++------ interface/src/avatar/SkeletonModel.cpp | 2 +- libraries/animation/src/AnimPose.cpp | 3 +- libraries/animation/src/Rig.cpp | 64 +++++++++++++----- libraries/render-utils/src/AnimDebugDraw.cpp | 71 ++++++++++++++++++-- libraries/render-utils/src/AnimDebugDraw.h | 10 ++- 6 files changed, 135 insertions(+), 50 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cc8f232436..75beec0fae 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -660,7 +660,7 @@ void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) { _enableDebugDrawAnimPose = isEnabled; if (!isEnabled) { - AnimDebugDraw::getInstance().removePoses("myAvatar"); + AnimDebugDraw::getInstance().removeAbsolutePoses("myAvatar"); } } @@ -1278,37 +1278,25 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f)); AnimPose xform(glm::vec3(1), getOrientation() * rotY180, getPosition()); + /* if (_enableDebugDrawBindPose && _debugDrawSkeleton) { glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); AnimDebugDraw::getInstance().addSkeleton("myAvatar", _debugDrawSkeleton, xform, gray); } + */ if (_enableDebugDrawAnimPose && _debugDrawSkeleton) { glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); auto rig = _skeletonModel.getRig(); - // AJT: TODO move this into rig! - // build AnimPoseVec from JointStates. - // AJT: TODO THIS SHIT IS ALL BROKEN - AnimPoseVec poses; - poses.reserve(_debugDrawSkeleton->getNumJoints()); - for (int i = 0; i < _debugDrawSkeleton->getNumJoints(); i++) { - AnimPose pose = _debugDrawSkeleton->getRelativeBindPose(i); - glm::quat jointRot; - _rig->getJointRotationInConstrainedFrame(i, jointRot); - glm::vec3 jointTrans; - _rig->getJointTranslation(i, jointTrans); - pose.rot = pose.rot * jointRot; - pose.trans = jointTrans; - /* - if (_debugDrawSkeleton->getParentIndex(i) < 0) { - pose = _rig->getGeometryOffset() * pose; - } - */ - poses.push_back(pose); - } - AnimDebugDraw::getInstance().addPoses("myAvatar", _debugDrawSkeleton, poses, xform, cyan); + // build AnimPoseVec from JointStates. + AnimPoseVec absPoses; + absPoses.reserve(_debugDrawSkeleton->getNumJoints()); + for (int i = 0; i < _rig->getJointStateCount(); i++) { + absPoses.push_back(AnimPose(_rig->getJointTransform(i))); + } + AnimDebugDraw::getInstance().addAbsolutePoses("myAvatar", _debugDrawSkeleton, absPoses, xform, cyan); } } @@ -1789,6 +1777,9 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck"); int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips"); + // AJT: TODO: perhaps expose default gets from rig? + // so this can become _rig->getAbsoluteDefaultPose(rightEyeIndex)... + glm::vec3 absRightEyePos = rightEyeIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; glm::vec3 absNeckPos = neckIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b60ed85429..104bd5b403 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -444,7 +444,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; - } + } const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (getJointPosition(geometry.leftEyeJointIndex, firstEyePosition) && getJointPosition(geometry.rightEyeJointIndex, secondEyePosition)) { diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 235dc902af..1fe1e21ba8 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -19,7 +19,8 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), AnimPose::AnimPose(const glm::mat4& mat) { scale = extractScale(mat); - if (std::max(std::max(scale.x, scale.y), scale.z) > 1.01f) { + float maxScale = std::max(std::max(scale.x, scale.y), scale.z); + if (maxScale > 1.01f || maxScale <= 0.99f) { // quat_cast doesn't work so well with scaled matrices, so cancel it out. mat4 tmp = glm::scale(mat, 1.0f / scale); rot = glm::normalize(glm::quat_cast(tmp)); diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 5dfb90f0eb..56884933d1 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -167,6 +167,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in int leftHandJointIndex, int leftElbowJointIndex, int leftShoulderJointIndex, int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex) { + setModelOffset(modelOffset); _geometryOffset = AnimPose(geometry.offset); _animSkeleton = std::make_shared(geometry); @@ -221,8 +222,6 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in _rightHandJointIndex = rightHandJointIndex; _rightElbowJointIndex = rightElbowJointIndex; _rightShoulderJointIndex = rightShoulderJointIndex; - - setModelOffset(modelOffset); } bool Rig::jointStatesEmpty() { @@ -732,6 +731,8 @@ void Rig::updateAnimationStateHandlers() { // called on avatar update thread (wh void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { + setModelOffset(rootTransform); + if (_animNode) { updateAnimationStateHandlers(); @@ -763,7 +764,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { computeEyesInRootFrame(_relativePoses); } - setModelOffset(rootTransform); + // AJT: enable this for script //applyOverridePoses(); buildAbsolutePoses(); @@ -1263,8 +1264,9 @@ void Rig::buildAbsolutePoses() { } } + AnimPose rootTransform(_modelOffset * _geometryOffset); for (int i = 0; i < (int)_absolutePoses.size(); i++) { - _absolutePoses[i] = _modelOffset * _geometryOffset * _absolutePoses[i]; + _absolutePoses[i] = rootTransform * _absolutePoses[i]; // AJT: REMOVE /* _absolutePoses[i].trans = _modelOffset.trans + _absolutePoses[i].trans; @@ -1306,29 +1308,57 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { // check for differences between jointStates and _absolutePoses! if (false) { - // should display for model entities - glm::mat4 newMat = _absolutePoses[jointIndex]; + glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); + AnimPose oldPose(oldMat); - const float EPSILON = 0.01f; - if (glm::length(newMat[0] - oldMat[0]) > EPSILON || - glm::length(newMat[1] - oldMat[1]) > EPSILON || - glm::length(newMat[2] - oldMat[2]) > EPSILON || - glm::length(newMat[3] - oldMat[3]) > EPSILON) { + glm::mat4 newMat = _absolutePoses[jointIndex]; + AnimPose newPose(newMat); - // error? + bool badTrans = false; + const float TRANS_EPSILON = 0.01f; + if (glm::length(newPose.trans - oldPose.trans) > TRANS_EPSILON) { + badTrans = true; + } + + bool badScale = false; + const float SCALE_EPSILON = 0.00001f; + if (glm::length(newPose.scale - oldPose.scale) > SCALE_EPSILON) { + badScale = true; + } + + bool badRot = false; + const float ROT_EPSILON = 0.0001f; + glm::quat oldLog = glm::log(newPose.rot); + glm::quat newLog = glm::log(oldPose.rot); + glm::vec3 oldLogVec(oldLog.x, oldLog.y, oldLog.z); + glm::vec3 newLogVec(newLog.x, newLog.y, newLog.z); + if (glm::length(oldLogVec - newLogVec) > ROT_EPSILON) { + badRot = true; + } + + if (badTrans || badScale || badRot) { qCDebug(animation).nospace() << "AJT: mismatch for " << _animSkeleton->getJointName(jointIndex) << ", joint[" << jointIndex << "]"; - qCDebug(animation) << "AJT: oldMat = " << AnimPose(oldMat); - qCDebug(animation) << "AJT: newMat = " << AnimPose(newMat); - + if (badTrans) { + qCDebug(animation) << "AJT: oldTrans = " << oldPose.trans; + qCDebug(animation) << "AJT: newTrans = " << newPose.trans; + } + if (badRot) { + qCDebug(animation) << "AJT: oldRot = " << oldPose.rot << "log =" << glm::log(oldPose.rot); + qCDebug(animation) << "AJT: newRot = " << newPose.rot << "log =" << glm::log(newPose.rot); + } + if (badScale) { + qCDebug(animation) << "AJT: oldScale = " << oldPose.scale; + qCDebug(animation) << "AJT: newScale = " << newPose.scale; + } } } // AJT: LEGACY { - return _jointStates[jointIndex].getTransform(); + //return _jointStates[jointIndex].getTransform(); } - //return _absolutePoses[jointIndex]; + return _absolutePoses[jointIndex]; } diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 73f7b43ddb..078f1ecabc 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -169,12 +169,20 @@ void AnimDebugDraw::removeAnimNode(const std::string& key) { _animNodes.erase(key); } -void AnimDebugDraw::addPoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color) { - _poses[key] = PosesInfo(skeleton, poses, rootPose, color); +void AnimDebugDraw::addRelativePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color) { + _relativePoses[key] = PosesInfo(skeleton, poses, rootPose, color); } -void AnimDebugDraw::removePoses(const std::string& key) { - _poses.erase(key); +void AnimDebugDraw::removeRelativePoses(const std::string& key) { + _relativePoses.erase(key); +} + +void AnimDebugDraw::addAbsolutePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color) { + _absolutePoses[key] = PosesInfo(skeleton, poses, rootPose, color); +} + +void AnimDebugDraw::removeAbsolutePoses(const std::string& key) { + _absolutePoses.erase(key); } static const uint32_t red = toRGBA(255, 0, 0, 255); @@ -338,6 +346,9 @@ void AnimDebugDraw::update() { // figure out how many verts we will need. int numVerts = 0; + + // AJT: FIX ME + /* for (auto& iter : _skeletons) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; @@ -362,7 +373,19 @@ void AnimDebugDraw::update() { } } - for (auto& iter : _poses) { + for (auto& iter : _relativePoses) { + AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); + numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; + for (int i = 0; i < skeleton->getNumJoints(); i++) { + auto parentIndex = skeleton->getParentIndex(i); + if (parentIndex >= 0) { + numVerts += VERTICES_PER_LINK; + } + } + } + */ + + for (auto& iter : _absolutePoses) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; for (int i = 0; i < skeleton->getNumJoints(); i++) { @@ -379,9 +402,14 @@ void AnimDebugDraw::update() { auto myAvatarMarkerMap = DebugDraw::getInstance().getMyAvatarMarkerMap(); numVerts += myAvatarMarkerMap.size() * VERTICES_PER_BONE; + // allocate verts! data._vertexBuffer->resize(sizeof(Vertex) * numVerts); Vertex* verts = (Vertex*)data._vertexBuffer->editData(); Vertex* v = verts; + + // AJT: fixme + // draw skeletons + /* for (auto& iter : _skeletons) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); AnimPose rootPose = std::get<1>(iter.second); @@ -408,7 +436,11 @@ void AnimDebugDraw::update() { } } } + */ + // AJT: FIXME + /* + // draw animNodes for (auto& iter : _animNodes) { AnimNode::ConstPointer& animNode = std::get<0>(iter.second); AnimPose rootPose = std::get<1>(iter.second); @@ -446,8 +478,12 @@ void AnimDebugDraw::update() { } } } + */ - for (auto& iter : _poses) { + // AJT: FIX ME + /* + // draw relative poses + for (auto& iter : _relativePoses) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); AnimPoseVec& poses = std::get<1>(iter.second); AnimPose rootPose = std::get<2>(iter.second); @@ -482,6 +518,29 @@ void AnimDebugDraw::update() { } } } + */ + + // draw absolute poses + for (auto& iter : _absolutePoses) { + AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); + AnimPoseVec& absPoses = std::get<1>(iter.second); + AnimPose rootPose = std::get<2>(iter.second); + glm::vec4 color = std::get<3>(iter.second); + + for (int i = 0; i < skeleton->getNumJoints(); i++) { + const float radius = BONE_RADIUS / (absPoses[i].scale.x * rootPose.scale.x); + + // draw bone + addBone(rootPose, absPoses[i], radius, v); + + // draw link to parent + auto parentIndex = skeleton->getParentIndex(i); + if (parentIndex >= 0) { + assert(parentIndex < skeleton->getNumJoints()); + addLink(rootPose, absPoses[i], absPoses[parentIndex], radius, color, v); + } + } + } // draw markers from shared DebugDraw singleton for (auto& iter : markerMap) { diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index ee0e52fe0f..9fcff2a16b 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -38,8 +38,11 @@ public: void removeAnimNode(const std::string& key); // draw a set of poses with a skeleton - void addPoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color); - void removePoses(const std::string& key); + void addRelativePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color); + void removeRelativePoses(const std::string& key); + + void addAbsolutePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color); + void removeAbsolutePoses(const std::string& key); void update(); @@ -56,7 +59,8 @@ protected: std::unordered_map _skeletons; std::unordered_map _animNodes; - std::unordered_map _poses; + std::unordered_map _relativePoses; + std::unordered_map _absolutePoses; // no copies AnimDebugDraw(const AnimDebugDraw&) = delete; From 8f5212acac9c41a29580cde80405741bfad2abc8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 17:26:10 -0800 Subject: [PATCH 36/84] use destroy instead of accidental set --- assignment-client/src/Agent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 37d3c1097e..f9c8c966a8 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -410,5 +410,5 @@ void Agent::aboutToFinish() { assetThread->wait(); // cleanup the AudioInjectorManager (and any still running injectors) - DependencyManager::set(); + DependencyManager::destroy(); } From 3b56df6e9995bb8a913008f8bf95a4c2de004984 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 17:27:18 -0800 Subject: [PATCH 37/84] use deleteLater immediately not that finish is right thread --- libraries/audio/src/AudioInjector.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index fc7bcfd95d..a0d78da7f8 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -59,8 +59,8 @@ void AudioInjector::finish() { } if (shouldDelete) { - // we've been asked to delete after finishing, trigger a queued deleteLater here - QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); + // we've been asked to delete after finishing, trigger a deleteLater here + deleteLater(); } } From adf41fce19f8d02d6e9cd0847bbf85c2a174c6bf Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 17:29:20 -0800 Subject: [PATCH 38/84] use alias for unique_lock and mutex --- libraries/audio/src/AudioInjectorManager.cpp | 8 ++++---- libraries/audio/src/AudioInjectorManager.h | 11 +++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index ee2f9ad5fe..862136f22f 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -21,7 +21,7 @@ AudioInjectorManager::~AudioInjectorManager() { _shouldStop = true; - std::unique_lock lock(_injectorsMutex); + Lock lock(_injectorsMutex); // make sure any still living injectors are stopped and deleted while (!_injectors.empty()) { @@ -61,7 +61,7 @@ void AudioInjectorManager::createThread() { void AudioInjectorManager::run() { while (!_shouldStop) { // wait until the next injector is ready, or until we get a new injector given to us - std::unique_lock lock(_injectorsMutex); + Lock lock(_injectorsMutex); if (_injectors.size() > 0) { // when does the next injector need to send a frame? @@ -119,7 +119,7 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { } // guard the injectors vector with a mutex - std::unique_lock lock(_injectorsMutex); + Lock lock(_injectorsMutex); // check if we'll be able to thread this injector (do we have < max concurrent injectors) if (_injectors.size() < MAX_INJECTORS_PER_THREAD) { @@ -150,7 +150,7 @@ bool AudioInjectorManager::threadInjector(AudioInjector* injector) { void AudioInjectorManager::restartFinishedInjector(AudioInjector* injector) { if (!_shouldStop) { // guard the injectors vector with a mutex - std::unique_lock lock(_injectorsMutex); + Lock lock(_injectorsMutex); // add the injector to the queue with a send timestamp of now _injectors.emplace(usecTimestampNow(), InjectorQPointer { injector }); diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 5e88ade4db..67fb1a7b6c 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -24,9 +24,6 @@ #include class AudioInjector; -using InjectorQPointer = QPointer; -using TimeInjectorPointerPair = std::pair; -using InjectorQueue = std::queue; class AudioInjectorManager : public QObject, public Dependency { Q_OBJECT @@ -36,6 +33,12 @@ public: private slots: void run(); private: + using InjectorQPointer = QPointer; + using TimeInjectorPointerPair = std::pair; + using InjectorQueue = std::queue; + using Mutex = std::mutex; + using Lock = std::unique_lock; + bool threadInjector(AudioInjector* injector); void restartFinishedInjector(AudioInjector* injector); void notifyInjectorReadyCondition() { _injectorReady.notify_one(); } @@ -49,7 +52,7 @@ private: QThread* _thread { nullptr }; bool _shouldStop { false }; InjectorQueue _injectors; - std::mutex _injectorsMutex; + Mutex _injectorsMutex; std::condition_variable _injectorReady; friend class AudioInjector; From 46d23a9f38f84b0fb4730b5afbe4c2ddf1ccfa8f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 19 Nov 2015 17:30:56 -0800 Subject: [PATCH 39/84] head IK and controller IK work again! --- interface/src/avatar/SkeletonModel.cpp | 3 - libraries/animation/src/Rig.cpp | 88 ++++++++++++++------------ libraries/animation/src/Rig.h | 22 ++++--- 3 files changed, 59 insertions(+), 54 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 104bd5b403..570bc0589a 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -114,9 +114,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { headParams.leanSideways = head->getFinalLeanSideways(); headParams.leanForward = head->getFinalLeanForward(); headParams.torsoTwist = head->getTorsoTwist(); - headParams.localHeadPitch = head->getFinalPitch(); - headParams.localHeadYaw = head->getFinalYaw(); - headParams.localHeadRoll = head->getFinalRoll(); if (qApp->getAvatarUpdater()->isHMDMode()) { headParams.isInHMD = true; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 56884933d1..a997d358a7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1014,30 +1014,15 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa } } -static AnimPose avatarToBonePose(AnimPose pose, AnimSkeleton::ConstPointer skeleton) { - AnimPose rootPose = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Hips")); - AnimPose rotY180(glm::vec3(1.0f), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0)); - return rootPose * rotY180 * pose; -} - -#ifdef DEBUG_RENDERING -static AnimPose boneToAvatarPose(AnimPose pose, AnimSkeleton::ConstPointer skeleton) { - AnimPose rootPose = skeleton->getAbsoluteBindPose(skeleton->nameToJointIndex("Hips")); - AnimPose rotY180(glm::vec3(1.0f), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3(0)); - return (rootPose * rotY180).inverse() * pose; -} -#endif - -static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const AnimPose& avatarHMDPose, +static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const AnimPose& geometryHMDPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut, glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) { // the input hmd values are in avatar space // we need to transform them into bone space. - AnimPose hmdPose = avatarToBonePose(avatarHMDPose, skeleton); + AnimPose hmdPose = geometryHMDPose; const glm::vec3 hmdPosition = hmdPose.trans; - const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); - const glm::quat hmdOrientation = hmdPose.rot * rotY180; // rotY180 will make z forward not -z + const glm::quat hmdOrientation = hmdPose.rot; // TODO: cache jointIndices int rightEyeIndex = skeleton->nameToJointIndex("RightEye"); @@ -1076,8 +1061,8 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { glm::vec3 headPos, neckPos; glm::quat headRot, neckRot; - AnimPose avatarHMDPose(glm::vec3(1.0f), params.localHeadOrientation, params.localHeadPosition); - computeHeadNeckAnimVars(_animSkeleton, avatarHMDPose, headPos, headRot, neckPos, neckRot); + AnimPose hmdPose(glm::vec3(1.0f), avatarToGeometryNegZForward(params.localHeadOrientation), avatarToGeometry(params.localHeadPosition)); + computeHeadNeckAnimVars(_animSkeleton, hmdPose, headPos, headRot, neckPos, neckRot); // debug rendering #ifdef DEBUG_RENDERING @@ -1086,12 +1071,10 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { // transform from bone into avatar space AnimPose headPose(glm::vec3(1), headRot, headPos); - headPose = boneToAvatarPose(headPose, _animSkeleton); DebugDraw::getInstance().addMyAvatarMarker("headTarget", headPose.rot, headPose.trans, red); // transform from bone into avatar space AnimPose neckPose(glm::vec3(1), neckRot, neckPos); - neckPose = boneToAvatarPose(neckPose, _animSkeleton); DebugDraw::getInstance().addMyAvatarMarker("neckTarget", neckPose.rot, neckPose.trans, green); #endif @@ -1105,13 +1088,21 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { } else { + /* // the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll. glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) * glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) * glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS)); + */ _animVars.unset("headPosition"); - _animVars.set("headRotation", realLocalHeadOrientation); + + /* + qCDebug(animation) << "AJT: input orientation " << params.localHeadOrientation; + qCDebug(animation) << "AJT: after transform" << avatarToGeometry(params.localHeadOrientation); + */ + + _animVars.set("headRotation", avatarToGeometryNegZForward(params.localHeadOrientation)); _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); _animVars.set("headType", (int)IKTarget::Type::RotationOnly); _animVars.unset("neckPosition"); @@ -1141,16 +1132,36 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm } } +glm::vec3 Rig::avatarToGeometry(const glm::vec3& pos) const { + // AJT: TODO cache transform + glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + AnimPose geometryToAvatarTransform = AnimPose(glm::vec3(1), yFlip, glm::vec3()) * _modelOffset * _geometryOffset; + glm::vec3 result = geometryToAvatarTransform.inverse() * pos; + return result; +} + +glm::quat Rig::avatarToGeometryNegZForward(const glm::quat& quat) const { + // AJT: TODO cache transform + AnimPose yFlip(glm::vec3(1), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3()); + AnimPose geometryToAvatarTransform = yFlip * _modelOffset * _geometryOffset; + return (geometryToAvatarTransform.inverse() * AnimPose(glm::vec3(1), quat, glm::vec3()) * yFlip).rot; +} + +glm::quat Rig::avatarToGeometryZForward(const glm::quat& quat) const { + // AJT: TODO cache transform + AnimPose yFlip(glm::vec3(1), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3()); + AnimPose geometryToAvatarTransform = yFlip * _modelOffset * _geometryOffset; + return (geometryToAvatarTransform.inverse() * AnimPose(glm::vec3(1), quat, glm::vec3())).rot; +} + void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (_animSkeleton && _animNode) { - // TODO: figure out how to obtain the yFlip from where it is actually stored - glm::quat yFlipHACK = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); AnimPose rootBindPose = _animSkeleton->getRootAbsoluteBindPoseByChildName("LeftHand"); if (params.isLeftEnabled) { - _animVars.set("leftHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.leftPosition); - _animVars.set("leftHandRotation", rootBindPose.rot * yFlipHACK * params.leftOrientation); + _animVars.set("leftHandPosition", avatarToGeometry(params.leftPosition)); + _animVars.set("leftHandRotation", avatarToGeometryZForward(params.leftOrientation)); _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("leftHandPosition"); @@ -1158,8 +1169,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } if (params.isRightEnabled) { - _animVars.set("rightHandPosition", rootBindPose.trans + rootBindPose.rot * yFlipHACK * params.rightPosition); - _animVars.set("rightHandRotation", rootBindPose.rot * yFlipHACK * params.rightOrientation); + _animVars.set("rightHandPosition", avatarToGeometry(params.rightPosition)); + _animVars.set("rightHandRotation", avatarToGeometryZForward(params.rightOrientation)); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("rightHandPosition"); @@ -1187,7 +1198,6 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { _animVars.set("leftHandOverlayAlpha", _leftHandOverlayAlpha); _animVars.set("leftHandGrabBlend", params.leftTrigger); - // set leftHand grab vars _animVars.set("isRightHandIdle", false); _animVars.set("isRightHandPoint", false); @@ -1307,7 +1317,7 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { //if (_jointStates.size() == 73) { // check for differences between jointStates and _absolutePoses! - if (false) { + if (true) { glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); AnimPose oldPose(oldMat); @@ -1316,24 +1326,20 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { AnimPose newPose(newMat); bool badTrans = false; - const float TRANS_EPSILON = 0.01f; - if (glm::length(newPose.trans - oldPose.trans) > TRANS_EPSILON) { + const float TRANS_EPSILON = 0.001f; + if (glm::length(newPose.trans - oldPose.trans) / glm::length(oldPose.trans) > TRANS_EPSILON) { badTrans = true; } bool badScale = false; - const float SCALE_EPSILON = 0.00001f; - if (glm::length(newPose.scale - oldPose.scale) > SCALE_EPSILON) { + const float SCALE_EPSILON = 0.0001f; + if (glm::length(newPose.scale - oldPose.scale) / glm::length(oldPose.scale) > SCALE_EPSILON) { badScale = true; } bool badRot = false; - const float ROT_EPSILON = 0.0001f; - glm::quat oldLog = glm::log(newPose.rot); - glm::quat newLog = glm::log(oldPose.rot); - glm::vec3 oldLogVec(oldLog.x, oldLog.y, oldLog.z); - glm::vec3 newLogVec(newLog.x, newLog.y, newLog.z); - if (glm::length(oldLogVec - newLogVec) > ROT_EPSILON) { + const float ROT_EPSILON = 0.00001f; + if (1.0f - fabsf(glm::dot(newPose.rot, oldPose.rot)) > ROT_EPSILON) { badRot = true; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 182b7e632b..0845c5a4a4 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -42,12 +42,9 @@ public: float leanForward = 0.0f; // degrees float torsoTwist = 0.0f; // degrees bool enableLean = false; - glm::quat worldHeadOrientation = glm::quat(); - glm::quat localHeadOrientation = glm::quat(); - float localHeadPitch = 0.0f; // degrees - float localHeadYaw = 0.0f; // degrees - float localHeadRoll = 0.0f; // degrees - glm::vec3 localHeadPosition = glm::vec3(); + glm::quat worldHeadOrientation = glm::quat(); // world space (-z forward) + glm::quat localHeadOrientation = glm::quat(); // avatar space (-z forward) + glm::vec3 localHeadPosition = glm::vec3(); // avatar space bool isInHMD = false; int leanJointIndex = -1; int neckJointIndex = -1; @@ -67,10 +64,10 @@ public: struct HandParameters { bool isLeftEnabled; bool isRightEnabled; - glm::vec3 leftPosition = glm::vec3(); - glm::quat leftOrientation = glm::quat(); - glm::vec3 rightPosition = glm::vec3(); - glm::quat rightOrientation = glm::quat(); + glm::vec3 leftPosition = glm::vec3(); // avatar space + glm::quat leftOrientation = glm::quat(); // avatar space (z forward) + glm::vec3 rightPosition = glm::vec3(); // avatar space + glm::quat rightOrientation = glm::quat(); // avatar space (z forward) float leftTrigger = 0.0f; float rightTrigger = 0.0f; }; @@ -167,6 +164,7 @@ public: const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } + protected: void updateAnimationStateHandlers(); void applyOverridePoses(); @@ -179,6 +177,10 @@ public: void computeEyesInRootFrame(const AnimPoseVec& poses); + glm::vec3 Rig::avatarToGeometry(const glm::vec3& pos) const; + glm::quat Rig::avatarToGeometryZForward(const glm::quat& quat) const; + glm::quat Rig::avatarToGeometryNegZForward(const glm::quat& quat) const; + // AJT: TODO: LEGACY QVector _jointStates; glm::mat4 _legacyModelOffset; From 5f9c3eea767ae7f408208d7210efaa865a741962 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 19 Nov 2015 17:42:14 -0800 Subject: [PATCH 40/84] use a priority queue to ensure ordering of injectors --- libraries/audio/src/AudioInjectorManager.cpp | 10 +++++----- libraries/audio/src/AudioInjectorManager.h | 14 +++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 862136f22f..942527caf1 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -26,7 +26,7 @@ AudioInjectorManager::~AudioInjectorManager() { // make sure any still living injectors are stopped and deleted while (!_injectors.empty()) { // grab the injector at the front - auto& timePointerPair = _injectors.front(); + auto& timePointerPair = _injectors.top(); // ask it to stop and be deleted timePointerPair.second->stopAndDeleteLater(); @@ -67,7 +67,7 @@ void AudioInjectorManager::run() { // when does the next injector need to send a frame? // do we get to wait or should we just go for it now? - auto timeInjectorPair = _injectors.front(); + auto timeInjectorPair = _injectors.top(); auto nextTimestamp = timeInjectorPair.first; int64_t difference = int64_t(nextTimestamp - usecTimestampNow()); @@ -78,7 +78,7 @@ void AudioInjectorManager::run() { if (_injectors.size() > 0) { // loop through the injectors in the map and send whatever frames need to go out - auto front = _injectors.front(); + auto front = _injectors.top(); while (_injectors.size() > 0 && front.first <= usecTimestampNow()) { // either way we're popping this injector off - get a copy first auto injector = front.second; @@ -90,11 +90,11 @@ void AudioInjectorManager::run() { if (nextCallDelta > 0 && !injector->isFinished()) { // re-enqueue the injector with the correct timing - _injectors.push({ usecTimestampNow() + nextCallDelta, injector }); + _injectors.emplace(usecTimestampNow() + nextCallDelta, injector); } } - front = _injectors.front(); + front = _injectors.top(); } } diff --git a/libraries/audio/src/AudioInjectorManager.h b/libraries/audio/src/AudioInjectorManager.h index 67fb1a7b6c..91648fff39 100644 --- a/libraries/audio/src/AudioInjectorManager.h +++ b/libraries/audio/src/AudioInjectorManager.h @@ -33,9 +33,19 @@ public: private slots: void run(); private: + using InjectorQPointer = QPointer; using TimeInjectorPointerPair = std::pair; - using InjectorQueue = std::queue; + + struct greaterTime { + bool operator() (const TimeInjectorPointerPair& x, const TimeInjectorPointerPair& y) const { + return x.first > y.first; + } + }; + + using InjectorQueue = std::priority_queue, + greaterTime>; using Mutex = std::mutex; using Lock = std::unique_lock; @@ -58,4 +68,6 @@ private: friend class AudioInjector; }; + + #endif // hifi_AudioInjectorManager_h From 9e36a79155825ef7faccee7ad0587f0e5e677ffa Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 19 Nov 2015 18:14:04 -0800 Subject: [PATCH 41/84] Rig: hooked up script overridePoses --- libraries/animation/src/AvatarRig.cpp | 18 ++++++--- libraries/animation/src/Rig.cpp | 58 ++++++++++++--------------- libraries/animation/src/Rig.h | 2 - 3 files changed, 37 insertions(+), 41 deletions(-) diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index eb522735b7..39af8ada04 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -77,10 +77,10 @@ void AvatarRig::setHandPosition(int jointIndex, } void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { - if (index != -1 && index < _jointStates.size()) { + // AJT: LEGACY + { - // AJT: LEGACY - { + if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; if (valid) { state.setTranslation(translation, priority); @@ -88,7 +88,10 @@ void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& tran state.restoreTranslation(1.0f, priority); } } + } + if (index > 0 && index < (int)_overrideFlags.size()) { + assert(_overrideFlags.size() == _overidePoses.size()); _overrideFlags[index] = true; _overridePoses[index].trans = translation; } @@ -96,10 +99,10 @@ void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& tran void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - if (index != -1 && index < _jointStates.size()) { + // AJT: LEGACY + { + if (index != -1 && index < _jointStates.size()) { - // AJT: LEGACY - { JointState& state = _jointStates[index]; if (valid) { state.setRotationInConstrainedFrame(rotation, priority); @@ -109,7 +112,10 @@ void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, state.restoreTranslation(1.0f, priority); } } + } + if (index > 0 && index < (int)_overrideFlags.size()) { + assert(_overrideFlags.size() == _overidePoses.size()); _overrideFlags[index] = true; _overridePoses[index].rot = rotation; _overridePoses[index].trans = translation; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index a997d358a7..6e0d7c6bdf 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -335,35 +335,38 @@ void Rig::clearJointStates() { } void Rig::clearJointAnimationPriority(int index) { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index].setAnimationPriority(0.0f); + // AJT: legacy + { + if (index != -1 && index < _jointStates.size()) { + _jointStates[index].setAnimationPriority(0.0f); + } } -} -float Rig::getJointAnimatinoPriority(int index) { - if (index != -1 && index < _jointStates.size()) { - return _jointStates[index].getAnimationPriority(); - } - return 0.0f; -} - -void Rig::setJointAnimatinoPriority(int index, float newPriority) { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index].setAnimationPriority(newPriority); + if (index >= 0 && index < (int)_overrideFlags.size()) { + _overrideFlags[index] = false; } } // Deprecated. // WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation. void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - } else { - state.restoreRotation(1.0f, priority); + // AJT: legacy + { + if (index != -1 && index < _jointStates.size()) { + JointState& state = _jointStates[index]; + if (valid) { + state.setRotationInConstrainedFrame(rotation, priority); + } else { + state.restoreRotation(1.0f, priority); + } } } + + if (index >= 0 && index < (int)_overrideFlags.size()) { + assert(_overrideFlags.size() == _overidePoses.size()); + _overrideFlags[index] = true; + _overridePoses[index].rot = rotation; + } } void Rig::restoreJointRotation(int index, float fraction, float priority) { @@ -750,7 +753,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { } // AJT: LEGACY - { + if (false) { clearJointStatePriorities(); // copy poses into jointStates @@ -764,8 +767,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { computeEyesInRootFrame(_relativePoses); } - // AJT: enable this for script - //applyOverridePoses(); + applyOverridePoses(); buildAbsolutePoses(); // AJT: LEGACY @@ -1277,12 +1279,6 @@ void Rig::buildAbsolutePoses() { AnimPose rootTransform(_modelOffset * _geometryOffset); for (int i = 0; i < (int)_absolutePoses.size(); i++) { _absolutePoses[i] = rootTransform * _absolutePoses[i]; - // AJT: REMOVE - /* - _absolutePoses[i].trans = _modelOffset.trans + _absolutePoses[i].trans; - _absolutePoses[i].rot = _modelOffset.rot * _absolutePoses[i].rot; - _absolutePoses[i].scale = _absolutePoses[i].scale * _modelScale; - */ } // AJT: LEGACY @@ -1312,12 +1308,8 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { return glm::mat4(); } - // AJT: test if _absolutePoses are the same as jointTransforms - // only test once we have a full skeleton (ryan avatar) - //if (_jointStates.size() == 73) { - // check for differences between jointStates and _absolutePoses! - if (true) { + if (false) { glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); AnimPose oldPose(oldMat); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 0845c5a4a4..510da2d812 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -106,8 +106,6 @@ public: void clearJointState(int index); void clearJointStates(); void clearJointAnimationPriority(int index); - float getJointAnimatinoPriority(int index); - void setJointAnimatinoPriority(int index, float newPriority); virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) = 0; From 302b6f7e266564d73de5937edcf142232fc146a7 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Thu, 19 Nov 2015 18:28:15 -0800 Subject: [PATCH 42/84] Mac and Debug build fixes --- libraries/animation/src/AvatarRig.cpp | 4 ++-- libraries/animation/src/Rig.cpp | 2 +- libraries/animation/src/Rig.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 39af8ada04..e9f20c2903 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -91,7 +91,7 @@ void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& tran } if (index > 0 && index < (int)_overrideFlags.size()) { - assert(_overrideFlags.size() == _overidePoses.size()); + assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; _overridePoses[index].trans = translation; } @@ -115,7 +115,7 @@ void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, } if (index > 0 && index < (int)_overrideFlags.size()) { - assert(_overrideFlags.size() == _overidePoses.size()); + assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; _overridePoses[index].rot = rotation; _overridePoses[index].trans = translation; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 6e0d7c6bdf..ac75040dbf 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -363,7 +363,7 @@ void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, flo } if (index >= 0 && index < (int)_overrideFlags.size()) { - assert(_overrideFlags.size() == _overidePoses.size()); + assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; _overridePoses[index].rot = rotation; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 510da2d812..2004a7ace2 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -175,9 +175,9 @@ public: void computeEyesInRootFrame(const AnimPoseVec& poses); - glm::vec3 Rig::avatarToGeometry(const glm::vec3& pos) const; - glm::quat Rig::avatarToGeometryZForward(const glm::quat& quat) const; - glm::quat Rig::avatarToGeometryNegZForward(const glm::quat& quat) const; + glm::vec3 avatarToGeometry(const glm::vec3& pos) const; + glm::quat avatarToGeometryZForward(const glm::quat& quat) const; + glm::quat avatarToGeometryNegZForward(const glm::quat& quat) const; // AJT: TODO: LEGACY QVector _jointStates; From c2ae6f0c8edb19922876952e1d5473b0d2fc0727 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 09:20:41 -0800 Subject: [PATCH 43/84] Fix for remote Avatar Make sure to copy the correct data to and from AvatarData::JointData --- libraries/animation/src/AvatarRig.cpp | 8 ++- libraries/animation/src/Rig.cpp | 86 +++++++++++++++++---------- 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index e9f20c2903..148f4115f1 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -91,9 +91,11 @@ void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& tran } if (index > 0 && index < (int)_overrideFlags.size()) { - assert(_overrideFlags.size() == _overridePoses.size()); - _overrideFlags[index] = true; - _overridePoses[index].trans = translation; + if (valid) { + assert(_overrideFlags.size() == _overridePoses.size()); + _overrideFlags[index] = true; + _overridePoses[index].trans = translation; + } } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ac75040dbf..4523cbeff7 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -23,6 +23,17 @@ #include "AnimClip.h" #include "IKTarget.h" +// TODO: maybe move into GLMHelpers? +static bool isEqual(const glm::vec3& u, const glm::vec3& v) { + const float EPSILON = 0.0001f; + return glm::length(u - v) / glm::length(u) <= EPSILON; +} + +static bool isEqual(const glm::quat& p, const glm::quat& q) { + const float EPSILON = 0.00001f; + return 1.0f - fabsf(glm::dot(p, q)) <= EPSILON; +} + #ifdef NDEBUG #define ASSERT(cond) #else @@ -35,6 +46,8 @@ } while (0) #endif +static bool AJT_HACK_USE_JOINT_STATES = false; + /* const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); @@ -294,21 +307,39 @@ JointState Rig::getJointState(int jointIndex) const { } bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { - if (index == -1 || index >= _jointStates.size()) { + if (AJT_HACK_USE_JOINT_STATES) { // AJT: LEGACY + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + rotation = state.getRotationInConstrainedFrame(); + return !state.rotationIsDefault(rotation); + } + + if (index >= 0 && index < (int)_relativePoses.size()) { + rotation = _relativePoses[index].rot; + return isEqual(rotation, _animSkeleton->getRelativeDefaultPose(index).rot); + } else { return false; } - const JointState& state = _jointStates.at(index); - rotation = state.getRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); } bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { - if (index == -1 || index >= _jointStates.size()) { + if (AJT_HACK_USE_JOINT_STATES) { // AJT: LEGACY + if (index == -1 || index >= _jointStates.size()) { + return false; + } + const JointState& state = _jointStates.at(index); + translation = state.getTranslation(); + return !state.translationIsDefault(translation); + } + + if (index >= 0 && index < (int)_relativePoses.size()) { + translation = _relativePoses[index].trans; + return isEqual(translation, _animSkeleton->getRelativeDefaultPose(index).trans); + } else { return false; } - const JointState& state = _jointStates.at(index); - translation = state.getTranslation(); - return !state.translationIsDefault(translation); } void Rig::clearJointState(int index) { @@ -363,9 +394,11 @@ void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, flo } if (index >= 0 && index < (int)_overrideFlags.size()) { - assert(_overrideFlags.size() == _overridePoses.size()); - _overrideFlags[index] = true; - _overridePoses[index].rot = rotation; + if (valid) { + assert(_overrideFlags.size() == _overridePoses.size()); + _overrideFlags[index] = true; + _overridePoses[index].rot = rotation; + } } } @@ -753,7 +786,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { } // AJT: LEGACY - if (false) { + if (AJT_HACK_USE_JOINT_STATES) { clearJointStatePriorities(); // copy poses into jointStates @@ -1309,6 +1342,7 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { } // check for differences between jointStates and _absolutePoses! + // AJT: TODO REMOVE if (false) { glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); @@ -1317,23 +1351,9 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { glm::mat4 newMat = _absolutePoses[jointIndex]; AnimPose newPose(newMat); - bool badTrans = false; - const float TRANS_EPSILON = 0.001f; - if (glm::length(newPose.trans - oldPose.trans) / glm::length(oldPose.trans) > TRANS_EPSILON) { - badTrans = true; - } - - bool badScale = false; - const float SCALE_EPSILON = 0.0001f; - if (glm::length(newPose.scale - oldPose.scale) / glm::length(oldPose.scale) > SCALE_EPSILON) { - badScale = true; - } - - bool badRot = false; - const float ROT_EPSILON = 0.00001f; - if (1.0f - fabsf(glm::dot(newPose.rot, oldPose.rot)) > ROT_EPSILON) { - badRot = true; - } + bool badTrans = !isEqual(newPose.trans, oldPose.trans); + bool badScale = !isEqual(newPose.scale, oldPose.scale); + bool badRot = !isEqual(newPose.rot, oldPose.rot); if (badTrans || badScale || badRot) { qCDebug(animation).nospace() << "AJT: mismatch for " << _animSkeleton->getJointName(jointIndex) << ", joint[" << jointIndex << "]"; @@ -1353,10 +1373,10 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { } // AJT: LEGACY - { - //return _jointStates[jointIndex].getTransform(); + if (AJT_HACK_USE_JOINT_STATES) { + return _jointStates[jointIndex].getTransform(); + } else { + return _absolutePoses[jointIndex]; } - - return _absolutePoses[jointIndex]; } From 27685e04259aff90150e8a2de9ef8e2b64a9113a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 09:33:57 -0800 Subject: [PATCH 44/84] Bug fixes for other Avatar root translation --- libraries/animation/src/AvatarRig.cpp | 5 +++-- libraries/animation/src/EntityRig.cpp | 11 +++++++---- libraries/animation/src/Rig.cpp | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 148f4115f1..0082acf03d 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -11,6 +11,7 @@ #include "AvatarRig.h" +// AJT: REMOVE, this should no longer be used void AvatarRig::setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, float scale, float priority) { @@ -90,7 +91,7 @@ void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& tran } } - if (index > 0 && index < (int)_overrideFlags.size()) { + if (index >= 0 && index < (int)_overrideFlags.size()) { if (valid) { assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; @@ -116,7 +117,7 @@ void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, } } - if (index > 0 && index < (int)_overrideFlags.size()) { + if (index >= 0 && index < (int)_overrideFlags.size()) { assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; _overridePoses[index].rot = rotation; diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp index 9667fc569f..ff89c9d50b 100644 --- a/libraries/animation/src/EntityRig.cpp +++ b/libraries/animation/src/EntityRig.cpp @@ -12,10 +12,9 @@ #include "EntityRig.h" void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - if (index != -1 && index < _jointStates.size()) { - - // AJT: LEGACY - { + // AJT: LEGACY + { + if (index != -1 && index < _jointStates.size()) { JointState& state = _jointStates[index]; if (valid) { state.setRotationInConstrainedFrame(rotation, priority); @@ -25,8 +24,12 @@ void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, // state.restoreTranslation(1.0f, priority); } } + } + if (index >= 0 && index < (int)_overrideFlags.size()) { + assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; _overridePoses[index].rot = rotation; + _overridePoses[index].trans = translation; } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4523cbeff7..9c803e1d7f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -318,7 +318,7 @@ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { if (index >= 0 && index < (int)_relativePoses.size()) { rotation = _relativePoses[index].rot; - return isEqual(rotation, _animSkeleton->getRelativeDefaultPose(index).rot); + return !isEqual(rotation, _animSkeleton->getRelativeDefaultPose(index).rot); } else { return false; } @@ -336,7 +336,7 @@ bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { if (index >= 0 && index < (int)_relativePoses.size()) { translation = _relativePoses[index].trans; - return isEqual(translation, _animSkeleton->getRelativeDefaultPose(index).trans); + return !isEqual(translation, _animSkeleton->getRelativeDefaultPose(index).trans); } else { return false; } From ad4b8e00011b35dfc133c22d4ac092e52be95c08 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 10:22:36 -0800 Subject: [PATCH 45/84] Avatar transmission fixes, moved JointData into shared * Moved JointData into shared library * added methods to the rig to copy into and out of JointData * JointData translations must be in meters this is so the fixed point compression wont overflow, also, it's a consistent wire format. --- interface/src/avatar/Avatar.cpp | 7 +------ interface/src/avatar/MyAvatar.cpp | 7 +------ libraries/animation/src/Rig.cpp | 19 +++++++++++++++++++ libraries/animation/src/Rig.h | 3 +++ libraries/avatars/src/AvatarData.h | 10 +--------- libraries/shared/src/JointData.h | 18 ++++++++++++++++++ 6 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 libraries/shared/src/JointData.h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b979334383..4e58163a86 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -202,12 +202,7 @@ void Avatar::simulate(float deltaTime) { if (!_shouldRenderBillboard && inViewFrustum) { { PerformanceTimer perfTimer("skeleton"); - for (int i = 0; i < _jointData.size(); i++) { - const JointData& data = _jointData.at(i); - _skeletonModel.setJointRotation(i, data.rotationSet, data.rotation, 1.0f); - _skeletonModel.setJointTranslation(i, data.translationSet, data.translation, 1.0f); - } - + _skeletonModel.getRig()->copyJointsFromJointData(_jointData); _skeletonModel.simulate(deltaTime, _hasNewJointRotations || _hasNewJointTranslations); simulateAttachments(deltaTime); _hasNewJointRotations = false; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 75beec0fae..3680d32797 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -269,12 +269,7 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model _jointData.resize(_rig->getJointStateCount()); - - for (int i = 0; i < _jointData.size(); i++) { - JointData& data = _jointData[i]; - data.rotationSet |= _rig->getJointStateRotation(i, data.rotation); - data.translationSet |= _rig->getJointStateTranslation(i, data.translation); - } + _rig->copyJointsIntoJointData(_jointData); } { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 9c803e1d7f..64c48d8755 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1380,3 +1380,22 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { } } +void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { + for (int i = 0; i < jointDataVec.size(); i++) { + JointData& data = jointDataVec[i]; + data.rotationSet |= getJointStateRotation(i, data.rotation); + // geometry offset is used here so that translations are in meters. + // this is what the avatar mixer expects + data.translationSet |= getJointStateTranslation(i, _geometryOffset * data.translation); + } +} + +void Rig::copyJointsFromJointData(const QVector& jointDataVec) { + AnimPose invGeometryOffset = _geometryOffset.inverse(); + for (int i = 0; i < jointDataVec.size(); i++) { + const JointData& data = jointDataVec.at(i); + setJointRotation(i, data.rotationSet, data.rotation, 1.0f); + // geometry offset is used here to undo the fact that avatar mixer translations are in meters. + setJointTranslation(i, data.translationSet, invGeometryOffset * data.translation, 1.0f); + } +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 2004a7ace2..61e32d625e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -18,6 +18,7 @@ #include #include #include +#include #include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS @@ -162,6 +163,8 @@ public: const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } + void copyJointsIntoJointData(QVector& jointDataVec) const; + void copyJointsFromJointData(const QVector& jointDataVec); protected: void updateAnimationStateHandlers(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 846c314e4b..27fedbb6d1 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -46,6 +46,7 @@ typedef unsigned long long quint64; #include #include +#include #include #include #include @@ -131,7 +132,6 @@ enum KeyState { class QDataStream; class AttachmentData; -class JointData; class Transform; using TransformPointer = std::shared_ptr; @@ -432,14 +432,6 @@ private: }; Q_DECLARE_METATYPE(AvatarData*) -class JointData { -public: - glm::quat rotation; - bool rotationSet = false; - glm::vec3 translation; - bool translationSet = false; -}; - QJsonValue toJsonValue(const JointData& joint); JointData jointDataFromJsonValue(const QJsonValue& q); diff --git a/libraries/shared/src/JointData.h b/libraries/shared/src/JointData.h new file mode 100644 index 0000000000..a05b5c649a --- /dev/null +++ b/libraries/shared/src/JointData.h @@ -0,0 +1,18 @@ + +#ifndef hifi_JointData_h +#define hifi_JointData_h + +#include +#include + +// Used by the avatar mixer to describe a single joint +// These are relative to their parent and translations are in meters +class JointData { +public: + glm::quat rotation; + bool rotationSet = false; + glm::vec3 translation; // meters + bool translationSet = false; +}; + +#endif From 923d60e4521e365949db22c4117443c3f1a83bb9 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 10:30:12 -0800 Subject: [PATCH 46/84] Rig: Bug fix for copyJointsIntoJointData --- libraries/animation/src/Rig.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 64c48d8755..b5bde1b8c3 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1386,7 +1386,8 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { data.rotationSet |= getJointStateRotation(i, data.rotation); // geometry offset is used here so that translations are in meters. // this is what the avatar mixer expects - data.translationSet |= getJointStateTranslation(i, _geometryOffset * data.translation); + data.translationSet |= getJointStateTranslation(i, data.translation); + data.translation = _geometryOffset * data.translation; } } From df7ca3bc3886aeda3e98ddc3dfa9ed8a8cd375d8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 10:36:40 -0800 Subject: [PATCH 47/84] Rig: getJointRotation and getJointTranslation use _relativePoses --- libraries/animation/src/Rig.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b5bde1b8c3..4f344ae4ea 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -444,19 +444,38 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const // Deprecated. // WARNING: this is not symmetric with setJointRotation. It's historical. Use the appropriate specific variation. bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + + // AJT: LEGACY + { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + rotation = _jointStates[jointIndex].getRotation(); + } + + if (jointIndex >= 0 && jointIndex < _relativePoses.size()) { + rotation = _relativePoses[jointIndex].rot; + return true; + } else { return false; } - rotation = _jointStates[jointIndex].getRotation(); - return true; } bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + // AJT: LEGACY + { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + translation = _jointStates[jointIndex].getTranslation(); + } + + if (jointIndex >= 0 && jointIndex < _relativePoses.size()) { + translation = _relativePoses[jointIndex].trans; + return true; + } else { return false; } - translation = _jointStates[jointIndex].getTranslation(); - return true; } bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const { From a4116e633a4b85db56f7e9469c21074b79dc312a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 11:26:54 -0800 Subject: [PATCH 48/84] Removed last consumer of JointState class Removed option to render IK constraints used by old animation system --- interface/src/Menu.cpp | 1 - interface/src/Menu.h | 1 - interface/src/avatar/FaceModel.cpp | 5 +- interface/src/avatar/MyAvatar.cpp | 6 +- interface/src/avatar/SkeletonModel.cpp | 101 +------------------------ interface/src/avatar/SkeletonModel.h | 20 ++--- libraries/animation/src/Rig.cpp | 10 ++- libraries/animation/src/Rig.h | 3 + 8 files changed, 26 insertions(+), 121 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index a6db205fe1..c56b430622 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -458,7 +458,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::EnableHandMouseInput, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::LowVelocityFilter, 0, true, qApp, SLOT(setLowVelocityFilter(bool))); - addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false); MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 7546ed59f2..4efb7e7749 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -271,7 +271,6 @@ namespace MenuOption { const QString ScriptedMotorControl = "Enable Scripted Motor Control"; const QString ShowDSConnectTable = "Show Domain Connection Timing"; const QString ShowBordersEntityNodes = "Show Entity Nodes"; - const QString ShowIKConstraints = "Show IK Constraints"; const QString ShowRealtimeEntityStats = "Show Realtime Entity Stats"; const QString ShowWhosLookingAtMe = "Show Who's Looking at Me"; const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index 0e8e5a6a91..9c22fc17ac 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -32,10 +32,7 @@ void FaceModel::simulate(float deltaTime, bool fullUpdate) { neckPosition = owningAvatar->getPosition(); } setTranslation(neckPosition); - glm::quat neckParentRotation; - if (!owningAvatar->getSkeletonModel().getNeckParentRotationFromDefaultOrientation(neckParentRotation)) { - neckParentRotation = owningAvatar->getOrientation(); - } + glm::quat neckParentRotation = owningAvatar->getOrientation(); setRotation(neckParentRotation); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3680d32797..fb72adafa1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -268,7 +268,6 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model - _jointData.resize(_rig->getJointStateCount()); _rig->copyJointsIntoJointData(_jointData); } @@ -507,11 +506,14 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { Avatar::render(renderArgs, cameraPosition); + // AJT: REMOVE + /* // don't display IK constraints in shadow mode if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints) && renderArgs && renderArgs->_batch) { _skeletonModel.renderIKConstraints(*renderArgs->_batch); } + */ } void MyAvatar::clearReferential() { @@ -1285,7 +1287,7 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { auto rig = _skeletonModel.getRig(); - // build AnimPoseVec from JointStates. + // build absolute AnimPoseVec from rig AnimPoseVec absPoses; absPoses.reserve(_debugDrawSkeleton->getNumJoints()); for (int i = 0; i < _rig->getJointStateCount(); i++) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 570bc0589a..8ee4e83ac4 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -26,7 +26,6 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer rig) : Model(rig, parent), - _triangleFanID(DependencyManager::get()->allocateID()), _owningAvatar(owningAvatar), _boundingCapsuleLocalOffset(0.0f), _boundingCapsuleRadius(0.0f), @@ -254,11 +253,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } } -void SkeletonModel::renderIKConstraints(gpu::Batch& batch) { - renderJointConstraints(batch, getRightHandJointIndex()); - renderJointConstraints(batch, getLeftHandJointIndex()); -} - class IndexValue { public: int index; @@ -285,98 +279,6 @@ void SkeletonModel::applyPalmData(int jointIndex, const PalmData& palm) { glm::quat palmRotation = inverseRotation * palm.getRotation(); } -void SkeletonModel::renderJointConstraints(gpu::Batch& batch, int jointIndex) { - if (jointIndex == -1 || jointIndex >= _rig->getJointStateCount()) { - return; - } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const float BASE_DIRECTION_SIZE = 0.3f; - float directionSize = BASE_DIRECTION_SIZE * extractUniformScale(_scale); - // FIXME: THe line width of 3.0f is not supported anymore, we ll need a workaround - - do { - const FBXJoint& joint = geometry.joints.at(jointIndex); - const JointState& jointState = _rig->getJointState(jointIndex); - glm::vec3 position = _rotation * jointState.getPosition() + _translation; - glm::quat parentRotation = (joint.parentIndex == -1) ? - _rotation : - _rotation * _rig->getJointState(joint.parentIndex).getRotation(); - float fanScale = directionSize * 0.75f; - - Transform transform = Transform(); - transform.setTranslation(position); - transform.setRotation(parentRotation); - transform.setScale(fanScale); - batch.setModelTransform(transform); - - const int AXIS_COUNT = 3; - - auto geometryCache = DependencyManager::get(); - - for (int i = 0; i < AXIS_COUNT; i++) { - if (joint.rotationMin[i] <= -PI + EPSILON && joint.rotationMax[i] >= PI - EPSILON) { - continue; // unconstrained - } - glm::vec3 axis; - axis[i] = 1.0f; - - glm::vec3 otherAxis; - if (i == 0) { - otherAxis.y = 1.0f; - } else { - otherAxis.x = 1.0f; - } - glm::vec4 color(otherAxis.r, otherAxis.g, otherAxis.b, 0.75f); - - QVector points; - points << glm::vec3(0.0f, 0.0f, 0.0f); - const int FAN_SEGMENTS = 16; - for (int j = 0; j < FAN_SEGMENTS; j++) { - glm::vec3 rotated = glm::angleAxis(glm::mix(joint.rotationMin[i], joint.rotationMax[i], - (float)j / (FAN_SEGMENTS - 1)), axis) * otherAxis; - points << rotated; - } - // TODO: this is really inefficient constantly recreating these vertices buffers. It would be - // better if the skeleton model cached these buffers for each of the joints they are rendering - geometryCache->updateVertices(_triangleFanID, points, color); - geometryCache->renderVertices(batch, gpu::TRIANGLE_FAN, _triangleFanID); - - } - - renderOrientationDirections(batch, jointIndex, position, _rotation * jointState.getRotation(), directionSize); - jointIndex = joint.parentIndex; - - } while (jointIndex != -1 && geometry.joints.at(jointIndex).isFree); -} - -void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointIndex, - glm::vec3 position, const glm::quat& orientation, float size) { - - auto geometryCache = DependencyManager::get(); - - if (!_jointOrientationLines.contains(jointIndex)) { - OrientationLineIDs jointLineIDs; - jointLineIDs._up = geometryCache->allocateID(); - jointLineIDs._front = geometryCache->allocateID(); - jointLineIDs._right = geometryCache->allocateID(); - _jointOrientationLines[jointIndex] = jointLineIDs; - } - OrientationLineIDs& jointLineIDs = _jointOrientationLines[jointIndex]; - - glm::vec3 pRight = position + orientation * IDENTITY_RIGHT * size; - glm::vec3 pUp = position + orientation * IDENTITY_UP * size; - glm::vec3 pFront = position + orientation * IDENTITY_FRONT * size; - - glm::vec3 red(1.0f, 0.0f, 0.0f); - geometryCache->renderLine(batch, position, pRight, red, jointLineIDs._right); - - glm::vec3 green(0.0f, 1.0f, 0.0f); - geometryCache->renderLine(batch, position, pUp, green, jointLineIDs._up); - - glm::vec3 blue(0.0f, 0.0f, 1.0f); - geometryCache->renderLine(batch, position, pFront, blue, jointLineIDs._front); -} - bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const { return getJointPositionInWorldFrame(getLeftHandJointIndex(), position); } @@ -421,6 +323,8 @@ bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const { return isActive() && getJointPosition(_geometry->getFBXGeometry().neckJointIndex, neckPosition); } +// AJT: REMOVE +/* bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const { if (!isActive()) { return false; @@ -437,6 +341,7 @@ bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckP } return success; } +*/ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 9f57d403a9..fbbe72f61a 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -33,7 +33,10 @@ public: virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; void updateAttitude(); + // AJT: REMOVE + /* void renderIKConstraints(gpu::Batch& batch); + */ /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; } @@ -82,10 +85,13 @@ public: bool getNeckPosition(glm::vec3& neckPosition) const; bool getLocalNeckPosition(glm::vec3& neckPosition) const; - + + // AJT: REMOVE + /* /// Returns the rotation of the neck joint's parent from default orientation /// \return whether or not the neck was found bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const; + */ /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found @@ -117,18 +123,6 @@ protected: void applyPalmData(int jointIndex, const PalmData& palm); private: - void renderJointConstraints(gpu::Batch& batch, int jointIndex); - void renderOrientationDirections(gpu::Batch& batch, int jointIndex, - glm::vec3 position, const glm::quat& orientation, float size); - - struct OrientationLineIDs { - int _up; - int _front; - int _right; - }; - QHash _jointOrientationLines; - int _triangleFanID; - bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; Avatar* _owningAvatar; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4f344ae4ea..f9f3f8086f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -299,12 +299,15 @@ void Rig::reset(const QVector& fbxJoints) { } } +// AJT: REMOVE +/* JointState Rig::getJointState(int jointIndex) const { if (jointIndex == -1 || jointIndex >= _jointStates.size()) { return JointState(); } return _jointStates[jointIndex]; } +*/ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { if (AJT_HACK_USE_JOINT_STATES) { // AJT: LEGACY @@ -453,7 +456,7 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { rotation = _jointStates[jointIndex].getRotation(); } - if (jointIndex >= 0 && jointIndex < _relativePoses.size()) { + if (jointIndex >= 0 && jointIndex < (int)_relativePoses.size()) { rotation = _relativePoses[jointIndex].rot; return true; } else { @@ -470,7 +473,7 @@ bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const { translation = _jointStates[jointIndex].getTranslation(); } - if (jointIndex >= 0 && jointIndex < _relativePoses.size()) { + if (jointIndex >= 0 && jointIndex < (int)_relativePoses.size()) { translation = _relativePoses[jointIndex].trans; return true; } else { @@ -1020,12 +1023,14 @@ void Rig::clearJointStatePriorities() { } } +/* void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return; } _jointStates[jointIndex].applyRotationDelta(delta, priority); } +*/ glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { if (jointIndex == -1 || _jointStates.isEmpty()) { @@ -1400,6 +1405,7 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { } void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { + jointDataVec.resize(getJointStateCount()); for (int i = 0; i < jointDataVec.size(); i++) { JointData& data = jointDataVec[i]; data.rotationSet |= getJointStateRotation(i, data.rotation); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 61e32d625e..dd1a5ec366 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -102,8 +102,11 @@ public: void reset(const QVector& fbxJoints); bool getJointStateRotation(int index, glm::quat& rotation) const; bool getJointStateTranslation(int index, glm::vec3& translation) const; + // AJT: REMOVE + /* void applyJointRotationDelta(int jointIndex, const glm::quat& delta, float priority); JointState getJointState(int jointIndex) const; // XXX + */ void clearJointState(int index); void clearJointStates(); void clearJointAnimationPriority(int index); From a77ea8da43f306c1dd266252c5f3de7f0a1529e2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 14:15:37 -0800 Subject: [PATCH 49/84] Removed JointStates! You won't be missed. --- interface/src/avatar/Avatar.cpp | 90 +-- interface/src/avatar/Avatar.h | 21 +- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/SkeletonModel.cpp | 19 +- libraries/animation/src/AnimSkeleton.cpp | 36 - libraries/animation/src/AnimSkeleton.h | 2 - libraries/animation/src/AvatarRig.cpp | 95 --- libraries/animation/src/AvatarRig.h | 2 - libraries/animation/src/EntityRig.cpp | 14 - libraries/animation/src/EntityRig.h | 2 - libraries/animation/src/JointState.cpp | 228 ------- libraries/animation/src/JointState.h | 148 ---- libraries/animation/src/Rig.cpp | 642 ++++-------------- libraries/animation/src/Rig.h | 35 +- .../src/RenderablePolyVoxEntityItem.cpp | 1 + libraries/render-utils/src/Model.cpp | 22 +- libraries/render-utils/src/Model.h | 1 - 17 files changed, 180 insertions(+), 1182 deletions(-) delete mode 100644 libraries/animation/src/JointState.cpp delete mode 100644 libraries/animation/src/JointState.h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 4e58163a86..a056819a82 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -246,7 +246,7 @@ bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) { const float HEAD_SPHERE_RADIUS = 0.1f; glm::vec3 theirLookAt = dynamic_pointer_cast(avatar)->getHead()->getLookAtPosition(); glm::vec3 myEyePosition = getHead()->getEyePosition(); - + return glm::distance(theirLookAt, myEyePosition) <= (HEAD_SPHERE_RADIUS * getScale()); } @@ -496,8 +496,8 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { eyeDiameter = DEFAULT_EYE_DIAMETER; } - DependencyManager::get()->renderSolidSphereInstance(batch, - Transform(transform).postScale(eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT), + DependencyManager::get()->renderSolidSphereInstance(batch, + Transform(transform).postScale(eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT), glm::vec4(LOOKING_AT_ME_COLOR, alpha)); position = getHead()->getRightEyePosition(); @@ -507,7 +507,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { eyeDiameter = DEFAULT_EYE_DIAMETER; } DependencyManager::get()->renderSolidSphereInstance(batch, - Transform(transform).postScale(eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT), + Transform(transform).postScale(eyeDiameter * _scale / 2.0f + RADIUS_INCREMENT), glm::vec4(LOOKING_AT_ME_COLOR, alpha)); } @@ -555,7 +555,7 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { if (!isMyAvatar() || cameraMode != CAMERA_MODE_FIRST_PERSON) { auto& frustum = *renderArgs->_viewFrustum; auto textPosition = getDisplayNamePosition(); - + if (frustum.pointInFrustum(textPosition, true) == ViewFrustum::INSIDE) { renderDisplayName(batch, frustum, textPosition); } @@ -611,7 +611,7 @@ void Avatar::fixupModelsInScene() { void Avatar::renderBody(RenderArgs* renderArgs, ViewFrustum* renderFrustum, float glowLevel) { fixupModelsInScene(); - + { if (_shouldRenderBillboard || !(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { // render the billboard until both models are loaded @@ -675,7 +675,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) { glm::quat rotation = getOrientation(); glm::vec3 cameraVector = glm::inverse(rotation) * (qApp->getCamera()->getPosition() - _position); rotation = rotation * glm::angleAxis(atan2f(-cameraVector.x, -cameraVector.z), glm::vec3(0.0f, 1.0f, 0.0f)); - + // compute the size from the billboard camera parameters and scale float size = getBillboardSize(); @@ -688,7 +688,7 @@ void Avatar::renderBillboard(RenderArgs* renderArgs) { glm::vec2 bottomRight(1.0f, 1.0f); glm::vec2 texCoordTopLeft(0.0f, 0.0f); glm::vec2 texCoordBottomRight(1.0f, 1.0f); - + gpu::Batch& batch = *renderArgs->_batch; PROFILE_RANGE_BATCH(batch, __FUNCTION__); batch.setResourceTexture(0, _billboardTexture->getGPUTexture()); @@ -721,29 +721,29 @@ glm::vec3 Avatar::getDisplayNamePosition() const { glm::vec3 namePosition(0.0f); glm::vec3 bodyUpDirection = getBodyUpDirection(); DEBUG_VALUE("bodyUpDirection =", bodyUpDirection); - + if (getSkeletonModel().getNeckPosition(namePosition)) { float headHeight = getHeadHeight(); DEBUG_VALUE("namePosition =", namePosition); DEBUG_VALUE("headHeight =", headHeight); - + static const float SLIGHTLY_ABOVE = 1.1f; namePosition += bodyUpDirection * headHeight * SLIGHTLY_ABOVE; } else { const float HEAD_PROPORTION = 0.75f; float billboardSize = getBillboardSize(); - + DEBUG_VALUE("_position =", _position); DEBUG_VALUE("billboardSize =", billboardSize); namePosition = _position + bodyUpDirection * (billboardSize * HEAD_PROPORTION); } - + if (glm::any(glm::isnan(namePosition)) || glm::any(glm::isinf(namePosition))) { qCWarning(interfaceapp) << "Invalid display name position" << namePosition << ", setting is to (0.0f, 0.5f, 0.0f)"; namePosition = glm::vec3(0.0f, 0.5f, 0.0f); } - + return namePosition; } @@ -751,16 +751,16 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, cons Q_ASSERT_X(frustum.pointInFrustum(textPosition, true) == ViewFrustum::INSIDE, "Avatar::calculateDisplayNameTransform", "Text not in viewfrustum."); glm::vec3 toFrustum = frustum.getPosition() - textPosition; - + // Compute orientation // If x and z are 0, atan(x, z) adais undefined, so default to 0 degrees const float yawRotation = (toFrustum.x == 0.0f && toFrustum.z == 0.0f) ? 0.0f : glm::atan(toFrustum.x, toFrustum.z); glm::quat orientation = glm::quat(glm::vec3(0.0f, yawRotation, 0.0f)); - + // Compute correct scale to apply static const float DESIRED_HEIGHT_RAD = glm::radians(1.5f); float scale = glm::length(toFrustum) * glm::tan(DESIRED_HEIGHT_RAD); - + // Set transform Transform result; result.setTranslation(textPosition); @@ -768,7 +768,7 @@ Transform Avatar::calculateDisplayNameTransform(const ViewFrustum& frustum, cons result.setScale(scale); // raise by half the scale up so that textPosition be the bottom result.postTranslate(Vectors::UP / 2.0f); - + return result; } @@ -796,14 +796,14 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co } renderedDisplayName += statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate()); } - + // Compute display name extent/position offset const glm::vec2 extent = renderer->computeExtent(renderedDisplayName); if (!glm::any(glm::isCompNull(extent, EPSILON))) { const QRect nameDynamicRect = QRect(0, 0, (int)extent.x, (int)extent.y); const int text_x = -nameDynamicRect.width() / 2; const int text_y = -nameDynamicRect.height() / 2; - + // Compute background position/size static const float SLIGHTLY_IN_FRONT = 0.1f; static const float BORDER_RELATIVE_SIZE = 0.1f; @@ -814,12 +814,12 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co const int width = nameDynamicRect.width() + 2.0f * border; const int height = nameDynamicRect.height() + 2.0f * border; const int bevelDistance = BEVEL_FACTOR * height; - + // Display name and background colors glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha); glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f, (_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA); - + // Compute display name transform auto textTransform = calculateDisplayNameTransform(frustum, textPosition); // Test on extent above insures abs(height) > 0.0f @@ -835,7 +835,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co // Render actual name QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); - + // Render text slightly in front to avoid z-fighting textTransform.postTranslate(glm::vec3(0.0f, 0.0f, SLIGHTLY_IN_FRONT * renderer->getFontSize())); batch.setModelTransform(textTransform); @@ -937,52 +937,6 @@ glm::vec3 Avatar::getJointPosition(const QString& name) const { return position; } -glm::quat Avatar::getJointCombinedRotation(int index) const { - if (QThread::currentThread() != thread()) { - glm::quat rotation; - QMetaObject::invokeMethod(const_cast(this), "getJointCombinedRotation", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(glm::quat, rotation), Q_ARG(const int, index)); - return rotation; - } - glm::quat rotation; - _skeletonModel.getJointCombinedRotation(index, rotation); - return rotation; -} - -glm::quat Avatar::getJointCombinedRotation(const QString& name) const { - if (QThread::currentThread() != thread()) { - glm::quat rotation; - QMetaObject::invokeMethod(const_cast(this), "getJointCombinedRotation", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(glm::quat, rotation), Q_ARG(const QString&, name)); - return rotation; - } - glm::quat rotation; - _skeletonModel.getJointCombinedRotation(getJointIndex(name), rotation); - return rotation; -} - -const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f; - -void Avatar::setJointModelPositionAndOrientation(int index, glm::vec3 position, const glm::quat& rotation) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(const_cast(this), "setJointModelPositionAndOrientation", - Qt::AutoConnection, Q_ARG(const int, index), Q_ARG(const glm::vec3, position), - Q_ARG(const glm::quat&, rotation)); - } else { - _skeletonModel.inverseKinematics(index, position, rotation, SCRIPT_PRIORITY); - } -} - -void Avatar::setJointModelPositionAndOrientation(const QString& name, glm::vec3 position, const glm::quat& rotation) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(const_cast(this), "setJointModelPositionAndOrientation", - Qt::AutoConnection, Q_ARG(const QString&, name), Q_ARG(const glm::vec3, position), - Q_ARG(const glm::quat&, rotation)); - } else { - _skeletonModel.inverseKinematics(getJointIndex(name), position, rotation, SCRIPT_PRIORITY); - } -} - void Avatar::scaleVectorRelativeToPosition(glm::vec3 &positionToScale) const { //Scale a world space vector as if it was relative to the position positionToScale = _position + _scale * (positionToScale - _position); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 2d0ab029ef..543c079d7a 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -66,7 +66,7 @@ public: typedef render::Payload Payload; typedef std::shared_ptr PayloadPointer; - + void init(); void simulate(float deltaTime); @@ -101,20 +101,20 @@ public: float getLODDistance() const; virtual bool isMyAvatar() const { return false; } - + virtual QVector getJointRotations() const; virtual glm::quat getJointRotation(int index) const; virtual glm::vec3 getJointTranslation(int index) const; virtual int getJointIndex(const QString& name) const; virtual QStringList getJointNames() const; - + virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); virtual void setAttachmentData(const QVector& attachmentData); virtual void setBillboard(const QByteArray& billboard); void setShowDisplayName(bool showDisplayName); - + virtual int parseDataFromBuffer(const QByteArray& buffer); static void renderJointConnectingCone( gpu::Batch& batch, glm::vec3 position1, glm::vec3 position2, @@ -125,16 +125,9 @@ public: Q_INVOKABLE void setSkeletonOffset(const glm::vec3& offset); Q_INVOKABLE glm::vec3 getSkeletonOffset() { return _skeletonOffset; } virtual glm::vec3 getSkeletonPosition() const; - + Q_INVOKABLE glm::vec3 getJointPosition(int index) const; Q_INVOKABLE glm::vec3 getJointPosition(const QString& name) const; - Q_INVOKABLE glm::quat getJointCombinedRotation(int index) const; - Q_INVOKABLE glm::quat getJointCombinedRotation(const QString& name) const; - - Q_INVOKABLE void setJointModelPositionAndOrientation(int index, const glm::vec3 position, const glm::quat& rotation); - Q_INVOKABLE void setJointModelPositionAndOrientation(const QString& name, const glm::vec3 position, - const glm::quat& rotation); - Q_INVOKABLE glm::vec3 getNeckPosition() const; Q_INVOKABLE glm::vec3 getAcceleration() const { return _acceleration; } @@ -195,9 +188,9 @@ protected: glm::vec3 _worldUpDirection; float _stringLength; bool _moving; ///< set when position is changing - + bool isLookingAtMe(AvatarSharedPointer avatar); - + // protected methods... glm::vec3 getBodyRightDirection() const { return getOrientation() * IDENTITY_RIGHT; } glm::vec3 getBodyUpDirection() const { return getOrientation() * IDENTITY_UP; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index fb72adafa1..935a18a3d1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -897,8 +897,8 @@ glm::vec3 MyAvatar::getDefaultEyePosition() const { return getPosition() + getWorldAlignedOrientation() * _skeletonModel.getDefaultEyeModelPosition(); } -const float SCRIPT_PRIORITY = DEFAULT_PRIORITY + 1.0f; -const float RECORDER_PRIORITY = SCRIPT_PRIORITY + 1.0f; +const float SCRIPT_PRIORITY = 1.0f + 1.0f; +const float RECORDER_PRIORITY = 1.0f + 1.0f; void MyAvatar::setJointRotations(QVector jointRotations) { int numStates = glm::min(_skeletonModel.getJointStateCount(), jointRotations.size()); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8ee4e83ac4..5ffc6d7b1e 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -44,23 +44,7 @@ void SkeletonModel::initJointStates() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 geometryOffset = geometry.offset; glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - - int rootJointIndex = geometry.rootJointIndex; - int leftHandJointIndex = geometry.leftHandJointIndex; - int leftElbowJointIndex = leftHandJointIndex >= 0 ? geometry.joints.at(leftHandJointIndex).parentIndex : -1; - int leftShoulderJointIndex = leftElbowJointIndex >= 0 ? geometry.joints.at(leftElbowJointIndex).parentIndex : -1; - int rightHandJointIndex = geometry.rightHandJointIndex; - int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1; - int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1; - - _rig->initJointStates(geometry, modelOffset, - rootJointIndex, - leftHandJointIndex, - leftElbowJointIndex, - leftShoulderJointIndex, - rightHandJointIndex, - rightElbowJointIndex, - rightShoulderJointIndex); + _rig->initJointStates(geometry, modelOffset); // Determine the default eye position for avatar scale = 1.0 int headJointIndex = _geometry->getFBXGeometry().headJointIndex; @@ -100,7 +84,6 @@ void SkeletonModel::initJointStates() { emit skeletonLoaded(); } -const float PALM_PRIORITY = DEFAULT_PRIORITY; // Called within Model::simulate call, below. void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { Head* head = _owningAvatar->getHead(); diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index ae73d380e6..55e56ae503 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -23,13 +23,6 @@ AnimSkeleton::AnimSkeleton(const FBXGeometry& fbxGeometry) { for (auto& joint : fbxGeometry.joints) { joints.push_back(joint); } - - // AJT: REMOVE - /* - AnimPose geometryOffset(fbxGeometry.offset); - buildSkeletonFromJoints(joints, geometryOffset); - */ - buildSkeletonFromJoints(joints); } @@ -147,37 +140,8 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) } } } - - // AJT: REMOVE - /* - // now we want to normalize scale from geometryOffset to all poses. - // This will ensure our bone translations will be in meters, even if the model was authored with some other unit of mesure. - normalizeScale(geometryOffset, _relativeBindPoses, _absoluteBindPoses); - normalizeScale(geometryOffset, _relativeDefaultPoses, _absoluteDefaultPoses); - */ } -/* -// AJT: REMOVE -void AnimSkeleton::normalizeScale(const AnimPose& geometryOffset, AnimPoseVec& relPoses, AnimPoseVec& absPoses) const { - for (auto& absPose : absPoses) { - absPose.trans = (geometryOffset * absPose).trans; - } - - // re-compute relative poses based on the modified absolute poses. - for (size_t i = 0; i < relPoses.size(); i++) { - int parentIndex = getParentIndex(i); - if (parentIndex >= 0) { - relPoses[i] = absPoses[parentIndex].inverse() * absPoses[i]; - } else { - relPoses[i] = absPoses[i]; - } - } -} -*/ - -#define DUMP_FBX_JOINTS - #ifndef NDEBUG void AnimSkeleton::dump() const { qCDebug(animation) << "["; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 8711f24587..0e0b07a8fd 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -55,8 +55,6 @@ public: protected: void buildSkeletonFromJoints(const std::vector& joints); - // AJT: REMOVE - //void normalizeScale(const AnimPose& geometryOffset, AnimPoseVec& relPoses, AnimPoseVec& absPoses) const; std::vector _joints; AnimPoseVec _absoluteBindPoses; diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp index 0082acf03d..aec696327e 100644 --- a/libraries/animation/src/AvatarRig.cpp +++ b/libraries/animation/src/AvatarRig.cpp @@ -11,86 +11,7 @@ #include "AvatarRig.h" -// AJT: REMOVE, this should no longer be used -void AvatarRig::setHandPosition(int jointIndex, - const glm::vec3& position, const glm::quat& rotation, - float scale, float priority) { - bool rightHand = (jointIndex == _rightHandJointIndex); - - int elbowJointIndex = rightHand ? _rightElbowJointIndex : _leftElbowJointIndex; - int shoulderJointIndex = rightHand ? _rightShoulderJointIndex : _leftShoulderJointIndex; - - // this algorithm is from sample code from sixense - if (elbowJointIndex == -1 || shoulderJointIndex == -1) { - return; - } - - glm::vec3 shoulderPosition; - if (!getJointPosition(shoulderJointIndex, shoulderPosition)) { - return; - } - - // precomputed lengths - float upperArmLength = _jointStates[elbowJointIndex].getDistanceToParent() * scale; - float lowerArmLength = _jointStates[jointIndex].getDistanceToParent() * scale; - - // first set wrist position - glm::vec3 wristPosition = position; - - glm::vec3 shoulderToWrist = wristPosition - shoulderPosition; - float distanceToWrist = glm::length(shoulderToWrist); - - // prevent gimbal lock - if (distanceToWrist > upperArmLength + lowerArmLength - EPSILON) { - distanceToWrist = upperArmLength + lowerArmLength - EPSILON; - shoulderToWrist = glm::normalize(shoulderToWrist) * distanceToWrist; - wristPosition = shoulderPosition + shoulderToWrist; - } - - // cosine of angle from upper arm to hand vector - float cosA = (upperArmLength * upperArmLength + distanceToWrist * distanceToWrist - lowerArmLength * lowerArmLength) / - (2 * upperArmLength * distanceToWrist); - float mid = upperArmLength * cosA; - float height = sqrt(upperArmLength * upperArmLength + mid * mid - 2 * upperArmLength * mid * cosA); - - // direction of the elbow - glm::vec3 handNormal = glm::cross(rotation * glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow rotating with wrist - glm::vec3 relaxedNormal = glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow pointing straight down - const float NORMAL_WEIGHT = 0.5f; - glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT); - - if (rightHand ? (finalNormal.y > 0.0f) : (finalNormal.y < 0.0f)) { - finalNormal.y = 0.0f; // dont allow elbows to point inward (y is vertical axis) - } - - glm::vec3 tangent = glm::normalize(glm::cross(shoulderToWrist, finalNormal)); - - // ik solution - glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height; - glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f); - glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition); - - setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, priority); - setJointRotationInBindFrame(elbowJointIndex, - rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) * - shoulderRotation, priority); - setJointRotationInBindFrame(jointIndex, rotation, priority); -} - void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { - // AJT: LEGACY - { - - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setTranslation(translation, priority); - } else { - state.restoreTranslation(1.0f, priority); - } - } - } - if (index >= 0 && index < (int)_overrideFlags.size()) { if (valid) { assert(_overrideFlags.size() == _overridePoses.size()); @@ -100,23 +21,7 @@ void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& tran } } - void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - // AJT: LEGACY - { - if (index != -1 && index < _jointStates.size()) { - - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - state.setTranslation(translation, priority); - } else { - state.restoreRotation(1.0f, priority); - state.restoreTranslation(1.0f, priority); - } - } - } - if (index >= 0 && index < (int)_overrideFlags.size()) { assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h index 0fabacfe91..230647998a 100644 --- a/libraries/animation/src/AvatarRig.h +++ b/libraries/animation/src/AvatarRig.h @@ -21,8 +21,6 @@ class AvatarRig : public Rig { public: ~AvatarRig() {} - virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, - float scale, float priority); virtual void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority); virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); }; diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp index ff89c9d50b..e96e5d58d5 100644 --- a/libraries/animation/src/EntityRig.cpp +++ b/libraries/animation/src/EntityRig.cpp @@ -12,20 +12,6 @@ #include "EntityRig.h" void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - // AJT: LEGACY - { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - // state.setTranslation(translation, priority); - } else { - state.restoreRotation(1.0f, priority); - // state.restoreTranslation(1.0f, priority); - } - } - } - if (index >= 0 && index < (int)_overrideFlags.size()) { assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h index e5d1f005d8..d283816a1a 100644 --- a/libraries/animation/src/EntityRig.h +++ b/libraries/animation/src/EntityRig.h @@ -21,8 +21,6 @@ class EntityRig : public Rig { public: ~EntityRig() {} - virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, - float scale, float priority) {} virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); }; diff --git a/libraries/animation/src/JointState.cpp b/libraries/animation/src/JointState.cpp deleted file mode 100644 index 4c4e40119a..0000000000 --- a/libraries/animation/src/JointState.cpp +++ /dev/null @@ -1,228 +0,0 @@ -// -// JointState.cpp -// libraries/animation/src/ -// -// Created by Andrzej Kapolka on 10/18/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include - -#include - -#include "JointState.h" - -JointState::~JointState() { -} - -void JointState::copyState(const JointState& other) { - _transformChanged = other._transformChanged; - _transform = other._transform; - _rotationIsValid = other._rotationIsValid; - _rotation = other._rotation; - _rotationInConstrainedFrame = other._rotationInConstrainedFrame; - _positionInParentFrame = other._positionInParentFrame; - _distanceToParent = other._distanceToParent; - _animationPriority = other._animationPriority; - - _name = other._name; - _isFree = other._isFree; - _parentIndex = other._parentIndex; - _defaultRotation = other._defaultRotation; - _defaultTranslation = other._defaultTranslation; - _inverseDefaultRotation = other._inverseDefaultRotation; - _translation = other._translation; - _preRotation = other._preRotation; - _postRotation = other._postRotation; - _preTransform = other._preTransform; - _postTransform = other._postTransform; - _inverseBindRotation = other._inverseBindRotation; -} -JointState::JointState(const FBXJoint& joint) { - _rotationInConstrainedFrame = joint.rotation; - _name = joint.name; - _isFree = joint.isFree; - _parentIndex = joint.parentIndex; - _translation = joint.translation; - _defaultRotation = joint.rotation; - _defaultTranslation = _translation; - _inverseDefaultRotation = joint.inverseDefaultRotation; - _preRotation = joint.preRotation; - _postRotation = joint.postRotation; - _preTransform = joint.preTransform; - _postTransform = joint.postTransform; - _inverseBindRotation = joint.inverseBindRotation; -} - -void JointState::buildConstraint() { -} - -glm::quat JointState::getRotation() const { - if (!_rotationIsValid) { - const_cast(this)->_rotation = extractRotation(_transform); - const_cast(this)->_rotationIsValid = true; - } - - return _rotation; -} - -void JointState::initTransform(const glm::mat4& parentTransform) { - - //_unitsScale = extractScale(parentTransform); - - computeTransform(parentTransform); - _positionInParentFrame = glm::inverse(extractRotation(parentTransform)) * (extractTranslation(_transform) - extractTranslation(parentTransform)); - _distanceToParent = glm::length(_positionInParentFrame); -} - -void JointState::computeTransform(const glm::mat4& parentTransform, bool parentTransformChanged, bool synchronousRotationCompute) { - if (!parentTransformChanged && !_transformChanged) { - return; - } - - glm::quat rotationInParentFrame = _preRotation * _rotationInConstrainedFrame * _postRotation; - glm::mat4 transformInParentFrame = _preTransform * glm::mat4_cast(rotationInParentFrame) * _postTransform; - glm::mat4 newTransform = parentTransform * glm::translate(_translation) * transformInParentFrame; - - if (newTransform != _transform) { - _transform = newTransform; - _transformChanged = true; - _rotationIsValid = false; - } -} - -glm::quat JointState::getRotationInBindFrame() const { - return getRotation() * _inverseBindRotation; -} - -glm::quat JointState::getRotationInParentFrame() const { - return _preRotation * _rotationInConstrainedFrame * _postRotation; -} - -void JointState::restoreRotation(float fraction, float priority) { - if (priority == _animationPriority || _animationPriority == 0.0f) { - setRotationInConstrainedFrameInternal(safeMix(_rotationInConstrainedFrame, _defaultRotation, fraction)); - _animationPriority = 0.0f; - } -} - -void JointState::restoreTranslation(float fraction, float priority) { - if (priority == _animationPriority || _animationPriority == 0.0f) { - _translation = _translation * (1.0f - fraction) + _defaultTranslation * fraction; - _animationPriority = 0.0f; - } -} - -void JointState::setRotationInBindFrame(const glm::quat& rotation, float priority) { - // rotation is from bind- to model-frame - if (priority >= _animationPriority) { - glm::quat targetRotation = _rotationInConstrainedFrame * glm::inverse(getRotation()) * rotation * glm::inverse(_inverseBindRotation); - setRotationInConstrainedFrameInternal(targetRotation); - _animationPriority = priority; - } -} - -void JointState::setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority) { - // rotation is from bind- to model-frame - if (priority >= _animationPriority) { - glm::quat parentRotation = computeParentRotation(); - - // R = Rp * Rpre * r * Rpost - // R' = Rp * Rpre * r' * Rpost - // r' = (Rp * Rpre)^ * R' * Rpost^ - glm::quat targetRotation = glm::inverse(parentRotation * _preRotation) * rotationInModelFrame * glm::inverse(_postRotation); - _rotationInConstrainedFrame = glm::normalize(targetRotation); - _transformChanged = true; - _animationPriority = priority; - } -} - -void JointState::clearTransformTranslation() { - _transform[3][0] = 0.0f; - _transform[3][1] = 0.0f; - _transform[3][2] = 0.0f; - _transformChanged = true; -} - -void JointState::applyRotationDelta(const glm::quat& delta, float priority) { - // NOTE: delta is in model-frame - if (priority < _animationPriority || delta == glm::quat()) { - return; - } - _animationPriority = priority; - glm::quat targetRotation = _rotationInConstrainedFrame * glm::inverse(getRotation()) * delta * getRotation(); - setRotationInConstrainedFrameInternal(targetRotation); -} - -/// Applies delta rotation to joint but mixes a little bit of the default pose as well. -/// This helps keep an IK solution stable. -void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float priority) { - // NOTE: delta is in model-frame - if (priority < _animationPriority) { - return; - } - _animationPriority = priority; - glm::quat targetRotation = _rotationInConstrainedFrame * glm::inverse(getRotation()) * delta * getRotation(); - if (mixFactor > 0.0f && mixFactor <= 1.0f) { - targetRotation = safeMix(targetRotation, _defaultRotation, mixFactor); - } - setRotationInConstrainedFrameInternal(targetRotation); -} - -glm::quat JointState::computeParentRotation() const { - // R = Rp * Rpre * r * Rpost - // Rp = R * (Rpre * r * Rpost)^ - return getRotation() * glm::inverse(_preRotation * _rotationInConstrainedFrame * _postRotation); -} - -void JointState::setRotationInConstrainedFrame(glm::quat targetRotation, float priority, float mix) { - if (priority >= _animationPriority || _animationPriority == 0.0f) { - auto rotation = (mix == 1.0f) ? targetRotation : safeMix(getRotationInConstrainedFrame(), targetRotation, mix); - setRotationInConstrainedFrameInternal(rotation); - _animationPriority = priority; - } -} - -void JointState::setTranslation(const glm::vec3& translation, float priority) { - if (priority >= _animationPriority || _animationPriority == 0.0f) { - _translation = translation / _unitsScale; - _transformChanged = true; - _animationPriority = priority; - } -} - -void JointState::setRotationInConstrainedFrameInternal(const glm::quat& targetRotation) { - if (_rotationInConstrainedFrame != targetRotation) { - glm::quat parentRotation = computeParentRotation(); - _rotationInConstrainedFrame = targetRotation; - _transformChanged = true; - // R' = Rp * Rpre * r' * Rpost - _rotation = parentRotation * _preRotation * _rotationInConstrainedFrame * _postRotation; - } -} - -bool JointState::rotationIsDefault(const glm::quat& rotation, float tolerance) const { - glm::quat defaultRotation = _defaultRotation; - return glm::abs(rotation.x - defaultRotation.x) < tolerance && - glm::abs(rotation.y - defaultRotation.y) < tolerance && - glm::abs(rotation.z - defaultRotation.z) < tolerance && - glm::abs(rotation.w - defaultRotation.w) < tolerance; -} - -bool JointState::translationIsDefault(const glm::vec3& translation, float tolerance) const { - return glm::distance(_defaultTranslation * _unitsScale, translation) < tolerance; -} - -glm::quat JointState::getDefaultRotationInParentFrame() const { - // NOTE: the result is constant and could be cached - return _preRotation * _defaultRotation * _postRotation; -} - -glm::vec3 JointState::getDefaultTranslationInConstrainedFrame() const { - return _defaultTranslation * _unitsScale; -} diff --git a/libraries/animation/src/JointState.h b/libraries/animation/src/JointState.h deleted file mode 100644 index 5dec7138b6..0000000000 --- a/libraries/animation/src/JointState.h +++ /dev/null @@ -1,148 +0,0 @@ -// -// JointState.h -// libraries/animation/src/ -// -// Created by Andrzej Kapolka on 10/18/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_JointState_h -#define hifi_JointState_h - -#include -#include -#include - -#include -#include -#include - -const float DEFAULT_PRIORITY = 3.0f; - -class JointState { -public: - JointState() {} - JointState(const JointState& other) { copyState(other); } - JointState(const FBXJoint& joint); - ~JointState(); - JointState& operator=(const JointState& other) { copyState(other); return *this; } - void copyState(const JointState& state); - void buildConstraint(); - - void initTransform(const glm::mat4& parentTransform); - // if synchronousRotationCompute is true, then _transform is still computed synchronously, - // but _rotation will be asynchronously extracted - void computeTransform(const glm::mat4& parentTransform, bool parentTransformChanged = true, bool synchronousRotationCompute = false); - - const glm::mat4& getTransform() const { return _transform; } - void resetTransformChanged() { _transformChanged = false; } - bool getTransformChanged() const { return _transformChanged; } - - glm::quat getRotation() const; - glm::vec3 getPosition() const { return extractTranslation(_transform); } - - /// \return rotation from bind to model frame - glm::quat getRotationInBindFrame() const; - - glm::quat getRotationInParentFrame() const; - const glm::vec3& getPositionInParentFrame() const { return _positionInParentFrame; } - float getDistanceToParent() const { return _distanceToParent; } - - int getParentIndex() const { return _parentIndex; } - - /// \param delta is in the model-frame - void applyRotationDelta(const glm::quat& delta, float priority = 1.0f); - - /// Applies delta rotation to joint but mixes a little bit of the default pose as well. - /// This helps keep an IK solution stable. - /// \param delta rotation change in model-frame - /// \param mixFactor fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all) - /// \param priority priority level of this animation blend - void mixRotationDelta(const glm::quat& delta, float mixFactor, float priority = 1.0f); - - /// Blends a fraciton of default pose into joint rotation. - /// \param fraction fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all) - /// \param priority priority level of this animation blend - void restoreRotation(float fraction, float priority); - - void restoreTranslation(float fraction, float priority); - - /// \param rotation is from bind- to model-frame - /// computes and sets new _rotationInConstrainedFrame - /// NOTE: the JointState's model-frame transform/rotation are NOT updated! - void setRotationInBindFrame(const glm::quat& rotation, float priority); - - /// \param rotationInModelRame is in model-frame - /// computes and sets new _rotationInConstrainedFrame to match rotationInModelFrame - /// NOTE: the JointState's model-frame transform/rotation are NOT updated! - void setRotationInModelFrame(const glm::quat& rotationInModelFrame, float priority); - - void setTranslation(const glm::vec3& translation, float priority); - - void setRotationInConstrainedFrame(glm::quat targetRotation, float priority, float mix = 1.0f); - - const glm::quat& getRotationInConstrainedFrame() const { return _rotationInConstrainedFrame; } - - bool rotationIsDefault(const glm::quat& rotation, float tolerance = EPSILON) const; - bool translationIsDefault(const glm::vec3& translation, float tolerance = EPSILON) const; - - glm::quat getDefaultRotationInParentFrame() const; - glm::vec3 getDefaultTranslationInConstrainedFrame() const; - - - void clearTransformTranslation(); - - /// \return parent model-frame rotation - // (used to keep _rotation consistent when modifying _rotationInWorldFrame directly) - glm::quat computeParentRotation() const; - - void setTransform(const glm::mat4& transform) { _transform = transform; } - - glm::vec3 getTranslation() const { return _translation * _unitsScale; } - const glm::mat4& getPreTransform() const { return _preTransform; } - const glm::mat4& getPostTransform() const { return _postTransform; } - const glm::quat& getPreRotation() const { return _preRotation; } - const glm::quat& getPostRotation() const { return _postRotation; } - const glm::quat& getDefaultRotation() const { return _defaultRotation; } - glm::vec3 getDefaultTranslation() const { return _defaultTranslation * _unitsScale; } - const glm::quat& getInverseDefaultRotation() const { return _inverseDefaultRotation; } - const QString& getName() const { return _name; } - bool getIsFree() const { return _isFree; } - float getAnimationPriority() const { return _animationPriority; } - void setAnimationPriority(float priority) { _animationPriority = priority; } - -private: - void setRotationInConstrainedFrameInternal(const glm::quat& targetRotation); - /// debug helper function - void loadBindRotation(); - - bool _transformChanged {true}; - bool _rotationIsValid {false}; - glm::vec3 _positionInParentFrame {0.0f}; // only changes when the Model is scaled - float _animationPriority {0.0f}; // the priority of the animation affecting this joint - float _distanceToParent {0.0f}; - - glm::mat4 _transform; // joint- to model-frame - glm::quat _rotation; // joint- to model-frame - glm::quat _rotationInConstrainedFrame; // rotation in frame where angular constraints would be applied - - glm::quat _defaultRotation; // Not necessarilly bind rotation. See FBXJoint transform/bindTransform - glm::quat _inverseDefaultRotation; - glm::vec3 _defaultTranslation; - glm::vec3 _translation; - QString _name; - int _parentIndex; - bool _isFree; - glm::quat _preRotation; - glm::quat _postRotation; - glm::mat4 _preTransform; - glm::mat4 _postTransform; - glm::quat _inverseBindRotation; - - glm::vec3 _unitsScale{1.0f, 1.0f, 1.0f}; -}; - -#endif // hifi_JointState_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index f9f3f8086f..ea85460897 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -46,8 +46,6 @@ static bool isEqual(const glm::quat& p, const glm::quat& q) { } while (0) #endif -static bool AJT_HACK_USE_JOINT_STATES = false; - /* const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); @@ -176,16 +174,12 @@ void Rig::destroyAnimGraph() { _overrideFlags.clear(); } -void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, int rootJointIndex, - int leftHandJointIndex, int leftElbowJointIndex, int leftShoulderJointIndex, - int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex) { +void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) { setModelOffset(modelOffset); _geometryOffset = AnimPose(geometry.offset); _animSkeleton = std::make_shared(geometry); - //_animSkeleton->dump(); - computeEyesInRootFrame(_animSkeleton->getRelativeDefaultPoses()); _relativePoses.clear(); @@ -200,41 +194,44 @@ void Rig::initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, in _overrideFlags.clear(); _overrideFlags.resize(_animSkeleton->getNumJoints(), false); - // AJT: LEGACY - { - // was Model::createJointStates - _jointStates.clear(); - for (int i = 0; i < geometry.joints.size(); ++i) { - const FBXJoint& joint = geometry.joints[i]; - // store a pointer to the FBXJoint in the JointState - JointState state(joint); - _jointStates.append(state); - } + _rootJointIndex = geometry.rootJointIndex; + _leftHandJointIndex = geometry.leftHandJointIndex; + _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; + _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1; + _rightHandJointIndex = geometry.rightHandJointIndex; + _rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1; + _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1; +} - // was old Rig::initJointStates - // compute model transforms - glm::mat4 rootTransform = (glm::mat4)(_modelOffset * _geometryOffset); - int numStates = _animSkeleton->getNumJoints(); - for (int i = 0; i < numStates; ++i) { - JointState& state = _jointStates[i]; - int parentIndex = state.getParentIndex(); - if (parentIndex == -1) { - state.initTransform(rootTransform); - } else { - const JointState& parentState = _jointStates.at(parentIndex); - state.initTransform(parentState.getTransform()); - } - } +void Rig::reset(const FBXGeometry& geometry) { + _geometryOffset = AnimPose(geometry.offset); + _animSkeleton = std::make_shared(geometry); + + computeEyesInRootFrame(_animSkeleton->getRelativeDefaultPoses()); + + _relativePoses.clear(); + _relativePoses = _animSkeleton->getRelativeDefaultPoses(); + + _absolutePoses.clear(); + _absolutePoses = _animSkeleton->getAbsoluteDefaultPoses(); + + _overridePoses.clear(); + _overridePoses = _animSkeleton->getRelativeDefaultPoses(); + + _overrideFlags.clear(); + _overrideFlags.resize(_animSkeleton->getNumJoints(), false); + + _rootJointIndex = geometry.rootJointIndex; + _leftHandJointIndex = geometry.leftHandJointIndex; + _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; + _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1; + _rightHandJointIndex = geometry.rightHandJointIndex; + _rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1; + _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1; + + if (!_animGraphURL.isEmpty()) { + initAnimGraph(_animGraphURL); } - - // AJT: TODO: we could probaly just look these up by name? - _rootJointIndex = rootJointIndex; - _leftHandJointIndex = leftHandJointIndex; - _leftElbowJointIndex = leftElbowJointIndex; - _leftShoulderJointIndex = leftShoulderJointIndex; - _rightHandJointIndex = rightHandJointIndex; - _rightElbowJointIndex = rightElbowJointIndex; - _rightShoulderJointIndex = rightShoulderJointIndex; } bool Rig::jointStatesEmpty() { @@ -245,80 +242,15 @@ int Rig::getJointStateCount() const { return _relativePoses.size(); } -// We could build and cache a dictionary, too.... -// Should we be using .fst mapping instead/also? int Rig::indexOfJoint(const QString& jointName) { - for (int i = 0; i < _jointStates.count(); i++) { - if (_jointStates[i].getName() == jointName) { - return i; - } - } - return -1; + return _animSkeleton->nameToJointIndex(jointName); } void Rig::setModelOffset(const glm::mat4& modelOffset) { - // AJT: LEGACY - { - _legacyModelOffset = modelOffset; - } _modelOffset = AnimPose(modelOffset); } -// AJT: REMOVE -/* -void Rig::initJointTransforms(glm::mat4 rootTransform) { - // compute model transforms - int numStates = _jointStates.size(); - for (int i = 0; i < numStates; ++i) { - JointState& state = _jointStates[i]; - int parentIndex = state.getParentIndex(); - if (parentIndex == -1) { - state.initTransform(rootTransform); - } else { - const JointState& parentState = _jointStates.at(parentIndex); - state.initTransform(parentState.getTransform()); - } - } -} -*/ - -void Rig::clearJointTransformTranslation(int jointIndex) { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return; - } - _jointStates[jointIndex].clearTransformTranslation(); -} - -void Rig::reset(const QVector& fbxJoints) { - if (_jointStates.isEmpty()) { - return; - } - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].setRotationInConstrainedFrame(fbxJoints.at(i).rotation, 0.0f); - _jointStates[i].setTranslation(fbxJoints.at(i).translation, 0.0f); - } -} - -// AJT: REMOVE -/* -JointState Rig::getJointState(int jointIndex) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return JointState(); - } - return _jointStates[jointIndex]; -} -*/ - bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { - if (AJT_HACK_USE_JOINT_STATES) { // AJT: LEGACY - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - rotation = state.getRotationInConstrainedFrame(); - return !state.rotationIsDefault(rotation); - } - if (index >= 0 && index < (int)_relativePoses.size()) { rotation = _relativePoses[index].rot; return !isEqual(rotation, _animSkeleton->getRelativeDefaultPose(index).rot); @@ -328,15 +260,6 @@ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { } bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { - if (AJT_HACK_USE_JOINT_STATES) { // AJT: LEGACY - if (index == -1 || index >= _jointStates.size()) { - return false; - } - const JointState& state = _jointStates.at(index); - translation = state.getTranslation(); - return !state.translationIsDefault(translation); - } - if (index >= 0 && index < (int)_relativePoses.size()) { translation = _relativePoses[index].trans; return !isEqual(translation, _animSkeleton->getRelativeDefaultPose(index).trans); @@ -346,36 +269,17 @@ bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { } void Rig::clearJointState(int index) { - if (index != -1 && index < _jointStates.size()) { - // AJT: REMOVE - /* - JointState& state = _jointStates[index]; - state.setRotationInConstrainedFrame(glm::quat(), 0.0f); - state.setTranslation(state.getDefaultTranslationInConstrainedFrame(), 0.0f); - */ + if (index >= 0 && index < (int)_relativePoses.size()) { _overrideFlags[index] = false; } } void Rig::clearJointStates() { - // AJT: LEGACY - /* - { - _jointStates.clear(); - } - */ _overrideFlags.clear(); _overrideFlags.resize(_animSkeleton->getNumJoints()); } void Rig::clearJointAnimationPriority(int index) { - // AJT: legacy - { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index].setAnimationPriority(0.0f); - } - } - if (index >= 0 && index < (int)_overrideFlags.size()) { _overrideFlags[index] = false; } @@ -384,21 +288,9 @@ void Rig::clearJointAnimationPriority(int index) { // Deprecated. // WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation. void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { - // AJT: legacy - { - if (index != -1 && index < _jointStates.size()) { - JointState& state = _jointStates[index]; - if (valid) { - state.setRotationInConstrainedFrame(rotation, priority); - } else { - state.restoreRotation(1.0f, priority); - } - } - } - if (index >= 0 && index < (int)_overrideFlags.size()) { if (valid) { - assert(_overrideFlags.size() == _overridePoses.size()); + ASSERT(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; _overridePoses[index].rot = rotation; } @@ -406,56 +298,49 @@ void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, flo } void Rig::restoreJointRotation(int index, float fraction, float priority) { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index].restoreRotation(fraction, priority); - } + // AJT: DEAD CODE? + ASSERT(false); } void Rig::restoreJointTranslation(int index, float fraction, float priority) { - if (index != -1 && index < _jointStates.size()) { - _jointStates[index].restoreTranslation(fraction, priority); - } + // AJT: DEAD CODE? + ASSERT(false); } -bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, - glm::vec3 translation, glm::quat rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { +// AJT: NOTE old code did not have 180 flip! +bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { + glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { + position = (rotation * yFlip * _absolutePoses[jointIndex].trans) + translation; + return true; + } else { return false; } - // position is in world-frame - position = translation + rotation * _jointStates[jointIndex].getPosition(); - return true; } +// AJT: NOTE old code did not have 180 flip! bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { + position = yFlip * _absolutePoses[jointIndex].trans; + return true; + } else { return false; } - // position is in model-frame - position = extractTranslation(_jointStates[jointIndex].getTransform()); - return true; } bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { + result = rotation * _absolutePoses[jointIndex].rot; + return true; + } else { return false; } - result = rotation * _jointStates[jointIndex].getRotation(); - return true; } // Deprecated. // WARNING: this is not symmetric with setJointRotation. It's historical. Use the appropriate specific variation. bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { - - // AJT: LEGACY - { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - rotation = _jointStates[jointIndex].getRotation(); - } - if (jointIndex >= 0 && jointIndex < (int)_relativePoses.size()) { rotation = _relativePoses[jointIndex].rot; return true; @@ -465,14 +350,6 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { } bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const { - // AJT: LEGACY - { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - translation = _jointStates[jointIndex].getTranslation(); - } - if (jointIndex >= 0 && jointIndex < (int)_relativePoses.size()) { translation = _relativePoses[jointIndex].trans; return true; @@ -482,11 +359,9 @@ bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const { } bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return false; - } - result = rotation * _jointStates[jointIndex].getRotation(); - return true; + // AJT: WTF IS THIS? + ASSERT(false); + return false; } void Rig::calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const { @@ -807,236 +682,56 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { _animVars.setTrigger(trigger); } - // AJT: LEGACY - if (AJT_HACK_USE_JOINT_STATES) { - clearJointStatePriorities(); - - // copy poses into jointStates - const float PRIORITY = 1.0f; - for (size_t i = 0; i < _relativePoses.size(); i++) { - setJointRotationInConstrainedFrame((int)i, glm::inverse(_animSkeleton->getRelativeBindPose(i).rot) * _relativePoses[i].rot, PRIORITY, 1.0f); - setJointTranslation((int)i, true, _relativePoses[i].trans, PRIORITY); - } - } - computeEyesInRootFrame(_relativePoses); } applyOverridePoses(); buildAbsolutePoses(); - - // AJT: LEGACY - { - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].resetTransformChanged(); - } - } } void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, const QVector& freeLineage, glm::mat4 rootTransform) { - // NOTE: targetRotation is from in model-frame - - if (endIndex == -1 || _jointStates.isEmpty()) { - return; - } - - if (freeLineage.isEmpty()) { - return; - } - - // store and remember topmost parent transform - glm::mat4 topParentTransform; - { - int index = freeLineage.last(); - const JointState& state = _jointStates.at(index); - int parentIndex = state.getParentIndex(); - if (parentIndex == -1) { - topParentTransform = rootTransform; - } else { - topParentTransform = _jointStates[parentIndex].getTransform(); - } - } - - // relax toward default rotation - // NOTE: ideally this should use dt and a relaxation timescale to compute how much to relax - int numFree = freeLineage.size(); - for (int j = 0; j < numFree; j++) { - int nextIndex = freeLineage.at(j); - JointState& nextState = _jointStates[nextIndex]; - if (! nextState.getIsFree()) { - continue; - } - - // Apply the zero rotationDelta, but use mixRotationDelta() which blends a bit of the default pose - // in the process. This provides stability to the IK solution for most models. - float mixFactor = 0.08f; - nextState.mixRotationDelta(glm::quat(), mixFactor, priority); - } - - // this is a cyclic coordinate descent algorithm: see - // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d - - // keep track of the position of the end-effector - JointState& endState = _jointStates[endIndex]; - glm::vec3 endPosition = endState.getPosition(); - float distanceToGo = glm::distance(targetPosition, endPosition); - - const int MAX_ITERATION_COUNT = 3; - const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm - int numIterations = 0; - do { - ++numIterations; - // moving up, rotate each free joint to get endPosition closer to target - for (int j = 1; j < numFree; j++) { - int nextIndex = freeLineage.at(j); - JointState& nextState = _jointStates[nextIndex]; - if (! nextState.getIsFree()) { - continue; - } - - glm::vec3 pivot = nextState.getPosition(); - glm::vec3 leverArm = endPosition - pivot; - float leverLength = glm::length(leverArm); - if (leverLength < EPSILON) { - continue; - } - glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot); - - // We want to mix the shortest rotation with one that will pull the system down with gravity - // so that limbs don't float unrealistically. To do this we compute a simplified center of mass - // where each joint has unit mass and we don't bother averaging it because we only need direction. - if (j > 1) { - - glm::vec3 centerOfMass(0.0f); - for (int k = 0; k < j; ++k) { - int massIndex = freeLineage.at(k); - centerOfMass += _jointStates[massIndex].getPosition() - pivot; - } - // the gravitational effect is a rotation that tends to align the two cross products - const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.0f, 0.0f); - glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), - glm::cross(worldAlignment, leverArm)); - - float gravityAngle = glm::angle(gravityDelta); - const float MIN_GRAVITY_ANGLE = 0.1f; - float mixFactor = 0.1f; - if (gravityAngle < MIN_GRAVITY_ANGLE) { - // the final rotation is a mix of the two - mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; - } - deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); - } - - // Apply the rotation delta. - glm::quat oldNextRotation = nextState.getRotation(); - nextState.applyRotationDelta(deltaRotation, priority); - - // measure the result of the rotation which may have been modified by blending - glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation); - endPosition = pivot + actualDelta * leverArm; - } - - // recompute transforms from the top down - glm::mat4 currentParentTransform = topParentTransform; - for (int j = numFree - 1; j >= 0; --j) { - JointState& freeState = _jointStates[freeLineage.at(j)]; - freeState.computeTransform(currentParentTransform); - currentParentTransform = freeState.getTransform(); - } - - // measure our success - endPosition = endState.getPosition(); - distanceToGo = glm::distance(targetPosition, endPosition); - } while (numIterations < MAX_ITERATION_COUNT && distanceToGo > ACCEPTABLE_IK_ERROR); - - // set final rotation of the end joint - endState.setRotationInModelFrame(targetRotation, priority); + ASSERT(false); } bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } - - foreach (int index, freeLineage) { - JointState& state = _jointStates[index]; - state.restoreRotation(fraction, priority); - state.restoreTranslation(fraction, priority); - } - return true; + ASSERT(false); + return false; } float Rig::getLimbLength(int jointIndex, const QVector& freeLineage, const glm::vec3 scale, const QVector& fbxJoints) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return 0.0f; - } - float length = 0.0f; - float lengthScale = (scale.x + scale.y + scale.z) / 3.0f; - for (int i = freeLineage.size() - 2; i >= 0; i--) { - length += fbxJoints.at(freeLineage.at(i)).distanceToParent * lengthScale; - } - return length; + ASSERT(false); + return 1.0f; } glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority) { - glm::quat endRotation; - if (jointIndex == -1 || _jointStates.isEmpty()) { - return endRotation; - } - JointState& state = _jointStates[jointIndex]; - state.setRotationInBindFrame(rotation, priority); - endRotation = state.getRotationInBindFrame(); - return endRotation; + ASSERT(false); + return glm::quat(); } glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return glm::vec3(); - } - return _jointStates[jointIndex].getDefaultTranslationInConstrainedFrame(); + ASSERT(false); + return glm::vec3(); } glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, float mix) { - glm::quat endRotation; - if (jointIndex == -1 || _jointStates.isEmpty()) { - return endRotation; - } - JointState& state = _jointStates[jointIndex]; - state.setRotationInConstrainedFrame(targetRotation, priority, mix); - endRotation = state.getRotationInConstrainedFrame(); - return endRotation; + ASSERT(false); + return glm::quat(); } bool Rig::getJointRotationInConstrainedFrame(int jointIndex, glm::quat& quatOut) const { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return false; - } - quatOut = _jointStates[jointIndex].getRotationInConstrainedFrame(); - return true; + ASSERT(false); + return false; } void Rig::clearJointStatePriorities() { - for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i].setAnimationPriority(0.0f); - } + ASSERT(false); } -/* -void Rig::applyJointRotationDelta(int jointIndex, const glm::quat& delta, float priority) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return; - } - _jointStates[jointIndex].applyRotationDelta(delta, priority); -} -*/ - glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { - if (jointIndex == -1 || _jointStates.isEmpty()) { - return glm::quat(); - } - return _jointStates[jointIndex].getDefaultRotationInParentFrame(); + ASSERT(false); + return glm::quat(); } void Rig::updateFromHeadParameters(const HeadParameters& params, float dt) { @@ -1063,7 +758,7 @@ static const glm::vec3 Y_AXIS(0.0f, 1.0f, 0.0f); static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f); void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { - if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { + if (index >= 0 && index) { if (_animSkeleton) { glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) * glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) * @@ -1113,82 +808,69 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A } void Rig::updateNeckJoint(int index, const HeadParameters& params) { - if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { - if (_animSkeleton) { + if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { + if (params.isInHMD) { + glm::vec3 headPos, neckPos; + glm::quat headRot, neckRot; - if (params.isInHMD) { - glm::vec3 headPos, neckPos; - glm::quat headRot, neckRot; + AnimPose hmdPose(glm::vec3(1.0f), avatarToGeometryNegZForward(params.localHeadOrientation), avatarToGeometry(params.localHeadPosition)); + computeHeadNeckAnimVars(_animSkeleton, hmdPose, headPos, headRot, neckPos, neckRot); - AnimPose hmdPose(glm::vec3(1.0f), avatarToGeometryNegZForward(params.localHeadOrientation), avatarToGeometry(params.localHeadPosition)); - computeHeadNeckAnimVars(_animSkeleton, hmdPose, headPos, headRot, neckPos, neckRot); - - // debug rendering + // debug rendering #ifdef DEBUG_RENDERING - const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); - const glm::vec4 green(0.0f, 1.0f, 0.0f, 1.0f); + const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); + const glm::vec4 green(0.0f, 1.0f, 0.0f, 1.0f); - // transform from bone into avatar space - AnimPose headPose(glm::vec3(1), headRot, headPos); - DebugDraw::getInstance().addMyAvatarMarker("headTarget", headPose.rot, headPose.trans, red); + // transform from bone into avatar space + AnimPose headPose(glm::vec3(1), headRot, headPos); + DebugDraw::getInstance().addMyAvatarMarker("headTarget", headPose.rot, headPose.trans, red); - // transform from bone into avatar space - AnimPose neckPose(glm::vec3(1), neckRot, neckPos); - DebugDraw::getInstance().addMyAvatarMarker("neckTarget", neckPose.rot, neckPose.trans, green); + // transform from bone into avatar space + AnimPose neckPose(glm::vec3(1), neckRot, neckPos); + DebugDraw::getInstance().addMyAvatarMarker("neckTarget", neckPose.rot, neckPose.trans, green); #endif - _animVars.set("headPosition", headPos); - _animVars.set("headRotation", headRot); - _animVars.set("headType", (int)IKTarget::Type::HmdHead); - _animVars.set("neckPosition", neckPos); - _animVars.set("neckRotation", neckRot); - //_animVars.set("neckType", (int)IKTarget::Type::RotationOnly); - _animVars.set("neckType", (int)IKTarget::Type::Unknown); // 'Unknown' disables the target + _animVars.set("headPosition", headPos); + _animVars.set("headRotation", headRot); + _animVars.set("headType", (int)IKTarget::Type::HmdHead); + _animVars.set("neckPosition", neckPos); + _animVars.set("neckRotation", neckRot); + _animVars.set("neckType", (int)IKTarget::Type::Unknown); // 'Unknown' disables the target - } else { - - /* - // the params.localHeadOrientation is composed incorrectly, so re-compose it correctly from pitch, yaw and roll. - glm::quat realLocalHeadOrientation = (glm::angleAxis(glm::radians(-params.localHeadRoll), Z_AXIS) * - glm::angleAxis(glm::radians(params.localHeadYaw), Y_AXIS) * - glm::angleAxis(glm::radians(-params.localHeadPitch), X_AXIS)); - */ - - _animVars.unset("headPosition"); - - /* - qCDebug(animation) << "AJT: input orientation " << params.localHeadOrientation; - qCDebug(animation) << "AJT: after transform" << avatarToGeometry(params.localHeadOrientation); - */ - - _animVars.set("headRotation", avatarToGeometryNegZForward(params.localHeadOrientation)); - _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); - _animVars.set("headType", (int)IKTarget::Type::RotationOnly); - _animVars.unset("neckPosition"); - _animVars.unset("neckRotation"); - _animVars.set("neckType", (int)IKTarget::Type::RotationOnly); - } + } else { + _animVars.unset("headPosition"); + _animVars.set("headRotation", avatarToGeometryNegZForward(params.localHeadOrientation)); + _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); + _animVars.set("headType", (int)IKTarget::Type::RotationOnly); + _animVars.unset("neckPosition"); + _animVars.unset("neckRotation"); + _animVars.set("neckType", (int)IKTarget::Type::RotationOnly); } } } void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { - if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { - auto& state = _jointStates[index]; - auto& parentState = _jointStates[state.getParentIndex()]; + // AJT: TODO: fix eye tracking! + /* + { + if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { + auto& state = _jointStates[index]; + auto& parentState = _jointStates[state.getParentIndex()]; - // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. - glm::mat4 inverse = glm::inverse(glm::mat4_cast(modelRotation) * parentState.getTransform() * - glm::translate(state.getDefaultTranslationInConstrainedFrame()) * - state.getPreTransform() * glm::mat4_cast(state.getPreRotation() * state.getDefaultRotation())); - glm::vec3 front = glm::vec3(inverse * glm::vec4(worldHeadOrientation * IDENTITY_FRONT, 0.0f)); - glm::vec3 lookAtDelta = lookAtSpot - modelTranslation; - glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * saccade, 1.0f)); - glm::quat between = rotationBetween(front, lookAt); - const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - state.getDefaultRotation(), DEFAULT_PRIORITY); + // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. + glm::mat4 inverse = glm::inverse(glm::mat4_cast(modelRotation) * parentState.getTransform() * + glm::translate(state.getDefaultTranslationInConstrainedFrame()) * + state.getPreTransform() * glm::mat4_cast(state.getPreRotation() * state.getDefaultRotation())); + glm::vec3 front = glm::vec3(inverse * glm::vec4(worldHeadOrientation * IDENTITY_FRONT, 0.0f)); + glm::vec3 lookAtDelta = lookAtSpot - modelTranslation; + glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * saccade, 1.0f)); + glm::quat between = rotationBetween(front, lookAt); + const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; + state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * + state.getDefaultRotation(), DEFAULT_PRIORITY); + } } + */ } glm::vec3 Rig::avatarToGeometry(const glm::vec3& pos) const { @@ -1279,6 +961,9 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { } void Rig::initAnimGraph(const QUrl& url) { + _animGraphURL = url; + + _animNode.reset(); // load the anim graph _animLoader.reset(new AnimNodeLoader(url)); @@ -1337,70 +1022,13 @@ void Rig::buildAbsolutePoses() { for (int i = 0; i < (int)_absolutePoses.size(); i++) { _absolutePoses[i] = rootTransform * _absolutePoses[i]; } - - // AJT: LEGACY - { - // Build the joint states - glm::mat4 rootTransform = (glm::mat4)(_modelOffset * _geometryOffset); - for (int i = 0; i < (int)_animSkeleton->getNumJoints(); i++) { - JointState& state = _jointStates[i]; - - // compute model transforms - int parentIndex = state.getParentIndex(); - if (parentIndex == -1) { - state.computeTransform(rootTransform); - } else { - // guard against out-of-bounds access to _jointStates - if (parentIndex >= 0 && parentIndex < _jointStates.size()) { - const JointState& parentState = _jointStates.at(parentIndex); - state.computeTransform(parentState.getTransform(), parentState.getTransformChanged()); - } - } - } - } } glm::mat4 Rig::getJointTransform(int jointIndex) const { - if (jointIndex == -1 || jointIndex >= _jointStates.size()) { - return glm::mat4(); - } - - // check for differences between jointStates and _absolutePoses! - // AJT: TODO REMOVE - if (false) { - - glm::mat4 oldMat = _jointStates[jointIndex].getTransform(); - AnimPose oldPose(oldMat); - - glm::mat4 newMat = _absolutePoses[jointIndex]; - AnimPose newPose(newMat); - - bool badTrans = !isEqual(newPose.trans, oldPose.trans); - bool badScale = !isEqual(newPose.scale, oldPose.scale); - bool badRot = !isEqual(newPose.rot, oldPose.rot); - - if (badTrans || badScale || badRot) { - qCDebug(animation).nospace() << "AJT: mismatch for " << _animSkeleton->getJointName(jointIndex) << ", joint[" << jointIndex << "]"; - if (badTrans) { - qCDebug(animation) << "AJT: oldTrans = " << oldPose.trans; - qCDebug(animation) << "AJT: newTrans = " << newPose.trans; - } - if (badRot) { - qCDebug(animation) << "AJT: oldRot = " << oldPose.rot << "log =" << glm::log(oldPose.rot); - qCDebug(animation) << "AJT: newRot = " << newPose.rot << "log =" << glm::log(newPose.rot); - } - if (badScale) { - qCDebug(animation) << "AJT: oldScale = " << oldPose.scale; - qCDebug(animation) << "AJT: newScale = " << newPose.scale; - } - } - } - - // AJT: LEGACY - if (AJT_HACK_USE_JOINT_STATES) { - return _jointStates[jointIndex].getTransform(); - } else { + if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { return _absolutePoses[jointIndex]; + } else { + return glm::mat4(); } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index dd1a5ec366..43977e058f 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -20,8 +20,6 @@ #include #include -#include "JointState.h" // We might want to change this (later) to something that doesn't depend on gpu, fbx and model. -HRS - #include "AnimNode.h" #include "AnimNodeLoader.h" #include "SimpleMovingAverage.h" @@ -76,7 +74,6 @@ public: virtual ~Rig() {} void destroyAnimGraph(); - void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreAnimation(); QStringList getAnimationRoles() const; @@ -84,9 +81,8 @@ public: void restoreRoleAnimation(const QString& role); void prefetchAnimation(const QString& url); - void initJointStates(const FBXGeometry& geometry, glm::mat4 modelOffset, int rootJointIndex, - int leftHandJointIndex, int leftElbowJointIndex, int leftShoulderJointIndex, - int rightHandJointIndex, int rightElbowJointIndex, int rightShoulderJointIndex); + void initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset); + void reset(const FBXGeometry& geometry); bool jointStatesEmpty(); int getJointStateCount() const; int indexOfJoint(const QString& jointName); @@ -94,19 +90,9 @@ public: void setModelOffset(const glm::mat4& modelOffset); const AnimPose& getGeometryOffset() const { return _geometryOffset; } - // AJT: REMOVE - /* - void initJointTransforms(glm::mat4 rootTransform); - */ - void clearJointTransformTranslation(int jointIndex); - void reset(const QVector& fbxJoints); bool getJointStateRotation(int index, glm::quat& rotation) const; bool getJointStateTranslation(int index, glm::vec3& translation) const; - // AJT: REMOVE - /* - void applyJointRotationDelta(int jointIndex, const glm::quat& delta, float priority); - JointState getJointState(int jointIndex) const; // XXX - */ + void clearJointState(int index); void clearJointStates(); void clearJointAnimationPriority(int index); @@ -151,9 +137,6 @@ public: void updateFromEyeParameters(const EyeParameters& params); void updateFromHandParameters(const HandParameters& params, float dt); - virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, - float scale, float priority) = 0; - void initAnimGraph(const QUrl& url); AnimNode::ConstPointer getAnimNode() const { return _animNode; } @@ -185,16 +168,11 @@ public: glm::quat avatarToGeometryZForward(const glm::quat& quat) const; glm::quat avatarToGeometryNegZForward(const glm::quat& quat) const; - // AJT: TODO: LEGACY - QVector _jointStates; - glm::mat4 _legacyModelOffset; - - // AJT: TODO document these better AnimPose _modelOffset; // model to avatar space (without 180 flip) AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets) - AnimPoseVec _relativePoses; // in fbx model space relative to parent. - AnimPoseVec _absolutePoses; // in fbx model space after _modelOffset is applied. - AnimPoseVec _overridePoses; // in fbx model space relative to parent. + AnimPoseVec _relativePoses; // geometry space relative to parent. + AnimPoseVec _absolutePoses; // avatar space, not relative to parent. + AnimPoseVec _overridePoses; // geometry space relative to parent. std::vector _overrideFlags; int _rootJointIndex { -1 }; @@ -212,6 +190,7 @@ public: glm::vec3 _lastVelocity; glm::vec3 _eyesInRootFrame { Vectors::ZERO }; + QUrl _animGraphURL; std::shared_ptr _animNode; std::shared_ptr _animSkeleton; std::unique_ptr _animLoader; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 2e8b8920ce..16252eb453 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 07d624c73f..960575844f 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -87,6 +87,8 @@ void Model::setScale(const glm::vec3& scale) { _scaledToFit = false; } +const float METERS_PER_MILLIMETER = 0.01f; + void Model::setScaleInternal(const glm::vec3& scale) { if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) { _scale = scale; @@ -117,7 +119,7 @@ void Model::init() { void Model::reset() { if (_geometry && _geometry->isLoaded()) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); - _rig->reset(geometry.joints); + _rig->reset(geometry); } } @@ -162,22 +164,7 @@ void Model::initJointStates() { const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); - int rootJointIndex = geometry.rootJointIndex; - int leftHandJointIndex = geometry.leftHandJointIndex; - int leftElbowJointIndex = leftHandJointIndex >= 0 ? geometry.joints.at(leftHandJointIndex).parentIndex : -1; - int leftShoulderJointIndex = leftElbowJointIndex >= 0 ? geometry.joints.at(leftElbowJointIndex).parentIndex : -1; - int rightHandJointIndex = geometry.rightHandJointIndex; - int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1; - int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1; - - _rig->initJointStates(geometry, modelOffset, - rootJointIndex, - leftHandJointIndex, - leftElbowJointIndex, - leftShoulderJointIndex, - rightHandJointIndex, - rightElbowJointIndex, - rightShoulderJointIndex); + _rig->initJointStates(geometry, modelOffset); } bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance, @@ -818,6 +805,7 @@ void Blender::run() { const float NORMAL_COEFFICIENT_SCALE = 0.01f; for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) { float vertexCoefficient = _blendshapeCoefficients.at(i); + const float EPSILON = 0.0001f; if (vertexCoefficient < EPSILON) { continue; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 38d2666246..bab4ae0aa8 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -29,7 +29,6 @@ #include #include "GeometryCache.h" -#include "JointState.h" #include "TextureCache.h" #include "Rig.h" From cb89f096357db72a927af39b247f385f360ccc6e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 14:27:01 -0800 Subject: [PATCH 50/84] Removed EntityRig and AvatarRig. One Rig to rule them all. --- interface/src/avatar/Avatar.cpp | 4 +-- interface/src/avatar/AvatarManager.cpp | 6 ++-- interface/src/avatar/Head.cpp | 4 +-- interface/src/ui/overlays/ModelOverlay.cpp | 6 ++-- libraries/animation/src/AvatarRig.cpp | 31 ------------------- libraries/animation/src/AvatarRig.h | 30 ------------------ libraries/animation/src/EntityRig.cpp | 21 ------------- libraries/animation/src/EntityRig.h | 27 ---------------- libraries/animation/src/Rig.cpp | 19 ++++++++++++ libraries/animation/src/Rig.h | 5 ++- .../src/EntityTreeRenderer.cpp | 6 ++-- tests/animation/src/RigTests.cpp | 4 +-- 12 files changed, 36 insertions(+), 127 deletions(-) delete mode 100644 libraries/animation/src/AvatarRig.cpp delete mode 100644 libraries/animation/src/AvatarRig.h delete mode 100644 libraries/animation/src/EntityRig.cpp delete mode 100644 libraries/animation/src/EntityRig.h diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index a056819a82..14ae1ccb83 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -44,7 +44,7 @@ #include "Util.h" #include "world.h" #include "InterfaceLogging.h" -#include "EntityRig.h" +#include using namespace std; @@ -965,7 +965,7 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { if (_unusedAttachments.size() > 0) { model = _unusedAttachments.takeFirst(); } else { - model = new Model(std::make_shared(), this); + model = new Model(std::make_shared(), this); } model->init(); _attachmentModels.append(model); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4b9bfd21a3..23e17ab864 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -35,7 +35,7 @@ #include "Menu.h" #include "MyAvatar.h" #include "SceneScriptingInterface.h" -#include "AvatarRig.h" +#include // 70 times per second - target is 60hz, but this helps account for any small deviations // in the update loop @@ -66,7 +66,7 @@ AvatarManager::AvatarManager(QObject* parent) : { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); - _myAvatar = std::make_shared(std::make_shared()); + _myAvatar = std::make_shared(std::make_shared()); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); @@ -169,7 +169,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { } AvatarSharedPointer AvatarManager::newSharedAvatar() { - return AvatarSharedPointer(std::make_shared(std::make_shared())); + return AvatarSharedPointer(std::make_shared(std::make_shared())); } // virtual diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index e8452583fc..29ec781f23 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -26,7 +26,7 @@ #include "devices/DdeFaceTracker.h" #include "devices/EyeTracker.h" #include "devices/Faceshift.h" -#include "AvatarRig.h" +#include using namespace std; @@ -62,7 +62,7 @@ Head::Head(Avatar* owningAvatar) : _isLookingAtMe(false), _lookingAtMeStarted(0), _wasLastLookingAtMe(0), - _faceModel(this, std::make_shared()), + _faceModel(this, std::make_shared()), _leftEyeLookAtID(DependencyManager::get()->allocateID()), _rightEyeLookAtID(DependencyManager::get()->allocateID()) { diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index b9cbde9f31..3738f829f3 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -10,7 +10,7 @@ // #include "ModelOverlay.h" -#include "EntityRig.h" +#include #include "Application.h" @@ -18,7 +18,7 @@ QString const ModelOverlay::TYPE = "model"; ModelOverlay::ModelOverlay() - : _model(std::make_shared()), + : _model(std::make_shared()), _modelTextures(QVariantMap()), _updateModel(false) { @@ -28,7 +28,7 @@ ModelOverlay::ModelOverlay() ModelOverlay::ModelOverlay(const ModelOverlay* modelOverlay) : Volume3DOverlay(modelOverlay), - _model(std::make_shared()), + _model(std::make_shared()), _modelTextures(QVariantMap()), _url(modelOverlay->_url), _updateModel(false) diff --git a/libraries/animation/src/AvatarRig.cpp b/libraries/animation/src/AvatarRig.cpp deleted file mode 100644 index aec696327e..0000000000 --- a/libraries/animation/src/AvatarRig.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// AvatarRig.cpp -// libraries/animation/src/ -// -// Created by SethAlves on 2015-7-22. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "AvatarRig.h" - -void AvatarRig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { - if (index >= 0 && index < (int)_overrideFlags.size()) { - if (valid) { - assert(_overrideFlags.size() == _overridePoses.size()); - _overrideFlags[index] = true; - _overridePoses[index].trans = translation; - } - } -} - -void AvatarRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - if (index >= 0 && index < (int)_overrideFlags.size()) { - assert(_overrideFlags.size() == _overridePoses.size()); - _overrideFlags[index] = true; - _overridePoses[index].rot = rotation; - _overridePoses[index].trans = translation; - } -} diff --git a/libraries/animation/src/AvatarRig.h b/libraries/animation/src/AvatarRig.h deleted file mode 100644 index 230647998a..0000000000 --- a/libraries/animation/src/AvatarRig.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// AvatarRig.h -// libraries/animation/src/ -// -// Created by SethAlves on 2015-7-22. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AvatarRig_h -#define hifi_AvatarRig_h - -#include - -#include "Rig.h" - -class AvatarRig : public Rig { - Q_OBJECT - -public: - ~AvatarRig() {} - virtual void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority); - virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); -}; - - - -#endif // hifi_AvatarRig_h diff --git a/libraries/animation/src/EntityRig.cpp b/libraries/animation/src/EntityRig.cpp deleted file mode 100644 index e96e5d58d5..0000000000 --- a/libraries/animation/src/EntityRig.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// EntityRig.cpp -// libraries/animation/src/ -// -// Created by SethAlves on 2015-7-22. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "EntityRig.h" - -void EntityRig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - if (index >= 0 && index < (int)_overrideFlags.size()) { - assert(_overrideFlags.size() == _overridePoses.size()); - _overrideFlags[index] = true; - _overridePoses[index].rot = rotation; - _overridePoses[index].trans = translation; - } -} diff --git a/libraries/animation/src/EntityRig.h b/libraries/animation/src/EntityRig.h deleted file mode 100644 index d283816a1a..0000000000 --- a/libraries/animation/src/EntityRig.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// EntityRig.h -// libraries/animation/src/ -// -// Created by SethAlves on 2015-7-22. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_EntityRig_h -#define hifi_EntityRig_h - -#include - -#include "Rig.h" - -class EntityRig : public Rig { - Q_OBJECT - -public: - ~EntityRig() {} - virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); -}; - -#endif // hifi_EntityRig_h diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ea85460897..b8d5075a9b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -285,6 +285,25 @@ void Rig::clearJointAnimationPriority(int index) { } } +void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { + if (index >= 0 && index < (int)_overrideFlags.size()) { + if (valid) { + assert(_overrideFlags.size() == _overridePoses.size()); + _overrideFlags[index] = true; + _overridePoses[index].trans = translation; + } + } +} + +void Rig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { + if (index >= 0 && index < (int)_overrideFlags.size()) { + assert(_overrideFlags.size() == _overridePoses.size()); + _overrideFlags[index] = true; + _overridePoses[index].rot = rotation; + _overridePoses[index].trans = translation; + } +} + // Deprecated. // WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation. void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 43977e058f..c73e343303 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -97,9 +97,8 @@ public: void clearJointStates(); void clearJointAnimationPriority(int index); - virtual void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, - float priority) = 0; - virtual void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) {} + void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); + void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority); void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority); void restoreJointRotation(int index, float fraction, float priority); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 8c81ceaeb8..1f5777a0e4 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -43,7 +43,7 @@ #include "RenderablePolyLineEntityItem.h" #include "EntitiesRendererLogging.h" #include "AddressManager.h" -#include "EntityRig.h" +#include EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : @@ -422,7 +422,7 @@ Model* EntityTreeRenderer::allocateModel(const QString& url, const QString& coll return model; } - model = new Model(std::make_shared()); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(url)); model->setCollisionModelURL(QUrl(collisionUrl)); @@ -455,7 +455,7 @@ Model* EntityTreeRenderer::updateModel(Model* original, const QString& newUrl, c } // create the model and correctly initialize it with the new url - model = new Model(std::make_shared()); + model = new Model(std::make_shared()); model->init(); model->setURL(QUrl(newUrl)); model->setCollisionModelURL(QUrl(collisionUrl)); diff --git a/tests/animation/src/RigTests.cpp b/tests/animation/src/RigTests.cpp index ff457ff804..3392f75476 100644 --- a/tests/animation/src/RigTests.cpp +++ b/tests/animation/src/RigTests.cpp @@ -44,7 +44,7 @@ #include "FBXReader.h" #include "OBJReader.h" -#include "AvatarRig.h" // We might later test Rig vs AvatarRig separately, but for now, we're concentrating on the main use case. +#include #include "RigTests.h" static void reportJoint(int index, JointState joint) { // Handy for debugging @@ -94,7 +94,7 @@ void RigTests::initTestCase() { jointStates.append(state); } - _rig = std::make_shared(); + _rig = std::make_shared(); _rig->initJointStates(jointStates, glm::mat4(), 0, 41, 40, 39, 17, 16, 15); // FIXME? get by name? do we really want to exclude the shoulder blades? std::cout << "Rig is ready " << geometry->joints.count() << " joints " << std::endl; reportAll(_rig); From 30087ef0bd9a25dd8bcf785553a26b594a781f5e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 14:37:53 -0800 Subject: [PATCH 51/84] Removed dead code --- interface/src/avatar/MyAvatar.cpp | 9 -------- interface/src/avatar/SkeletonModel.cpp | 31 +------------------------- interface/src/avatar/SkeletonModel.h | 12 ---------- libraries/animation/src/Rig.cpp | 6 +---- libraries/animation/src/Rig.h | 2 -- 5 files changed, 2 insertions(+), 58 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 935a18a3d1..99bcf64161 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -505,15 +505,6 @@ void MyAvatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) { } Avatar::render(renderArgs, cameraPosition); - - // AJT: REMOVE - /* - // don't display IK constraints in shadow mode - if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints) && - renderArgs && renderArgs->_batch) { - _skeletonModel.renderIKConstraints(*renderArgs->_batch); - } - */ } void MyAvatar::clearReferential() { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 5ffc6d7b1e..109d9cd62b 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -65,15 +65,6 @@ void SkeletonModel::initJointStates() { _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; } - /* AJT: DISABLED, only need this to compute bounding shape! - // the SkeletonModel override of updateJointState() will clear the translation part - // of its root joint and we need that done before we try to build shapes hence we - // recompute all joint transforms at this time. - for (int i = 0; i < _rig->getJointStateCount(); i++) { - _rig->updateJointState(i, rootTransform); - } - */ - computeBoundingShape(); Extents meshExtents = getMeshExtents(); @@ -306,26 +297,6 @@ bool SkeletonModel::getLocalNeckPosition(glm::vec3& neckPosition) const { return isActive() && getJointPosition(_geometry->getFBXGeometry().neckJointIndex, neckPosition); } -// AJT: REMOVE -/* -bool SkeletonModel::getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const { - if (!isActive()) { - return false; - } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.neckJointIndex == -1) { - return false; - } - int parentIndex = geometry.joints.at(geometry.neckJointIndex).parentIndex; - glm::quat worldFrameRotation; - bool success = getJointRotationInWorldFrame(parentIndex, worldFrameRotation); - if (success) { - neckParentRotation = worldFrameRotation * _rig->getJointState(parentIndex).getInverseDefaultRotation(); - } - return success; -} -*/ - bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const { if (!isActive()) { return false; @@ -383,7 +354,7 @@ void SkeletonModel::computeBoundingShape() { } /* - AJT: HACK DISABLED + AJT: TODO HACK DISABLED FIXME // BOUNDING SHAPE HACK: before we measure the bounds of the joints we use IK to put the // hands and feet into positions that are more correct than the default pose. diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index fbbe72f61a..9b73119238 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -33,11 +33,6 @@ public: virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override; void updateAttitude(); - // AJT: REMOVE - /* - void renderIKConstraints(gpu::Batch& batch); - */ - /// Returns the index of the left hand joint, or -1 if not found. int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; } @@ -86,13 +81,6 @@ public: bool getLocalNeckPosition(glm::vec3& neckPosition) const; - // AJT: REMOVE - /* - /// Returns the rotation of the neck joint's parent from default orientation - /// \return whether or not the neck was found - bool getNeckParentRotationFromDefaultOrientation(glm::quat& neckParentRotation) const; - */ - /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b8d5075a9b..7dde8a525b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -23,7 +23,6 @@ #include "AnimClip.h" #include "IKTarget.h" -// TODO: maybe move into GLMHelpers? static bool isEqual(const glm::vec3& u, const glm::vec3& v) { const float EPSILON = 0.0001f; return glm::length(u - v) / glm::length(u) <= EPSILON; @@ -378,7 +377,7 @@ bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const { } bool Rig::getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const { - // AJT: WTF IS THIS? + // AJT: TODO: used by attachments ASSERT(false); return false; } @@ -893,7 +892,6 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm } glm::vec3 Rig::avatarToGeometry(const glm::vec3& pos) const { - // AJT: TODO cache transform glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); AnimPose geometryToAvatarTransform = AnimPose(glm::vec3(1), yFlip, glm::vec3()) * _modelOffset * _geometryOffset; glm::vec3 result = geometryToAvatarTransform.inverse() * pos; @@ -901,14 +899,12 @@ glm::vec3 Rig::avatarToGeometry(const glm::vec3& pos) const { } glm::quat Rig::avatarToGeometryNegZForward(const glm::quat& quat) const { - // AJT: TODO cache transform AnimPose yFlip(glm::vec3(1), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3()); AnimPose geometryToAvatarTransform = yFlip * _modelOffset * _geometryOffset; return (geometryToAvatarTransform.inverse() * AnimPose(glm::vec3(1), quat, glm::vec3()) * yFlip).rot; } glm::quat Rig::avatarToGeometryZForward(const glm::quat& quat) const { - // AJT: TODO cache transform AnimPose yFlip(glm::vec3(1), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3()); AnimPose geometryToAvatarTransform = yFlip * _modelOffset * _geometryOffset; return (geometryToAvatarTransform.inverse() * AnimPose(glm::vec3(1), quat, glm::vec3())).rot; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index c73e343303..35d9e5747a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -112,8 +112,6 @@ public: bool getJointTranslation(int jointIndex, glm::vec3& translation) const; bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const; glm::mat4 getJointTransform(int jointIndex) const; - glm::mat4 getJointVisibleTransform(int jointIndex) const; - void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform); // Start or stop animations as needed. void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation); // Regardless of who started the animations or how many, update the joints. From 2f37335d779d7856753b738f8775ea44fb9abea0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 14:50:42 -0800 Subject: [PATCH 52/84] Warning fixes --- libraries/animation/src/Rig.cpp | 15 +++++---------- libraries/render-utils/src/Model.cpp | 3 --- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 7dde8a525b..696c2d463b 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -87,9 +87,6 @@ void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firs _animVars.set("userAnimB", _userAnimState == UserAnimState::B); } -const float FRAMES_PER_SECOND = 30.0f; -const float FADE_FRAMES = 30.0f; - void Rig::restoreAnimation() { if (_currentUserAnimURL != "") { _currentUserAnimURL = ""; @@ -691,7 +688,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { // evaluate the animation AnimNode::Triggers triggersOut; _relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut); - if (_relativePoses.size() != _animSkeleton->getNumJoints()) { + if ((int)_relativePoses.size() != _animSkeleton->getNumJoints()) { // animations haven't fully loaded yet. _relativePoses = _animSkeleton->getRelativeBindPoses(); } @@ -913,8 +910,6 @@ glm::quat Rig::avatarToGeometryZForward(const glm::quat& quat) const { void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (_animSkeleton && _animNode) { - - AnimPose rootBindPose = _animSkeleton->getRootAbsoluteBindPoseByChildName("LeftHand"); if (params.isLeftEnabled) { _animVars.set("leftHandPosition", avatarToGeometry(params.leftPosition)); _animVars.set("leftHandRotation", avatarToGeometryZForward(params.leftOrientation)); @@ -1005,9 +1000,9 @@ void Rig::applyOverridePoses() { return; } - ASSERT(_animSkeleton->getNumJoints() == _relativePoses.size()); - ASSERT(_animSkeleton->getNumJoints() == _overrideFlags.size()); - ASSERT(_animSkeleton->getNumJoints() == _overridePoses.size()); + ASSERT(_animSkeleton->getNumJoints() == (int)_relativePoses.size()); + ASSERT(_animSkeleton->getNumJoints() == (int)_overrideFlags.size()); + ASSERT(_animSkeleton->getNumJoints() == (int)_overridePoses.size()); for (size_t i = 0; i < _overrideFlags.size(); i++) { if (_overrideFlags[i]) { @@ -1021,7 +1016,7 @@ void Rig::buildAbsolutePoses() { return; } - ASSERT(_animSkeleton->getNumJoints() == _relativePoses.size()); + ASSERT(_animSkeleton->getNumJoints() == (int)_relativePoses.size()); _absolutePoses.resize(_relativePoses.size()); for (int i = 0; i < (int)_relativePoses.size(); i++) { diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 960575844f..0070b99591 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -108,7 +108,6 @@ void Model::initJointTransforms() { if (!_geometry || !_geometry->isLoaded()) { return; } - const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 modelOffset = glm::scale(_scale) * glm::translate(_offset); _rig->setModelOffset(modelOffset); } @@ -933,8 +932,6 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { } void Model::simulateInternal(float deltaTime) { // update the world space transforms for all joints - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); updateRig(deltaTime, parentTransform); } From 4f8cd6930d1f102182cd31aba67768f870cc8252 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 15:13:16 -0800 Subject: [PATCH 53/84] Rig: Fixes for initializing _absolutePoses during initJointStates --- libraries/animation/src/Rig.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 696c2d463b..2f2b1309ba 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -181,8 +181,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _relativePoses.clear(); _relativePoses = _animSkeleton->getRelativeDefaultPoses(); - _absolutePoses.clear(); - _absolutePoses = _animSkeleton->getAbsoluteDefaultPoses(); + buildAbsolutePoses(); _overridePoses.clear(); _overridePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -208,8 +207,7 @@ void Rig::reset(const FBXGeometry& geometry) { _relativePoses.clear(); _relativePoses = _animSkeleton->getRelativeDefaultPoses(); - _absolutePoses.clear(); - _absolutePoses = _animSkeleton->getAbsoluteDefaultPoses(); + buildAbsolutePoses(); _overridePoses.clear(); _overridePoses = _animSkeleton->getRelativeDefaultPoses(); From c8b5f6f7376cd8403ed6717e07be8d2ca2b04d6e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Nov 2015 16:03:18 -0800 Subject: [PATCH 54/84] fix for top on deque when empty --- libraries/audio/src/AudioInjectorManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioInjectorManager.cpp b/libraries/audio/src/AudioInjectorManager.cpp index 942527caf1..f504b31907 100644 --- a/libraries/audio/src/AudioInjectorManager.cpp +++ b/libraries/audio/src/AudioInjectorManager.cpp @@ -94,7 +94,12 @@ void AudioInjectorManager::run() { } } - front = _injectors.top(); + if (_injectors.size() > 0) { + front = _injectors.top(); + } else { + // no more injectors to look at, break + break; + } } } From b24b095c90ba102cb32d8354c2202dacb62b1df0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 20 Nov 2015 16:09:41 -0800 Subject: [PATCH 55/84] add a missing space in Agent --- assignment-client/src/Agent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 410e75e833..3406816588 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -416,7 +416,7 @@ void Agent::processAgentAvatarAndAudio(float deltaTime) { glm::quat headOrientation = scriptedAvatar->getHeadOrientation(); audioPacket->writePrimitive(headOrientation); - }else if (nextSoundOutput) { + } else if (nextSoundOutput) { // assume scripted avatar audio is mono and set channel flag to zero audioPacket->writePrimitive((quint8)0); From 54408a9c879aa8cf79bb96efef40c4636b7d4619 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 18:29:17 -0800 Subject: [PATCH 56/84] AnimVars are now in avatar/rig space This makes it much simpler for code out side of the rig to manipulate AnimVars * Removed mat4 type from AnimVars * AnimVariantMap now has a _rigToGeometryTransform matrix used to transform positions and rotations into the correct coordinate frame. * Moved AnimPose code to extract a quat from a scaled matrix into GLMHelpers --- interface/src/avatar/MyAvatar.cpp | 22 ++--- .../animation/src/AnimInverseKinematics.cpp | 4 +- libraries/animation/src/AnimManipulator.cpp | 8 +- libraries/animation/src/AnimPose.cpp | 10 +- libraries/animation/src/AnimVariant.cpp | 2 +- libraries/animation/src/AnimVariant.h | 36 +++++--- libraries/animation/src/Rig.cpp | 92 +++++++++---------- libraries/animation/src/Rig.h | 14 +-- libraries/shared/src/GLMHelpers.cpp | 14 ++- libraries/shared/src/GLMHelpers.h | 1 + 10 files changed, 102 insertions(+), 101 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 99bcf64161..e53eec95c1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1749,12 +1749,10 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f); const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.05f, 0.0f); - AnimPose geometryOffset = _rig->getGeometryOffset(); vec3 localEyes, localNeck; if (!_debugDrawSkeleton) { - const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); - localEyes = rotY180 * (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS); - localNeck = rotY180 * (DEFAULT_NECK_POS - DEFAULT_HIPS_POS); + localEyes = (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS); + localNeck = (DEFAULT_NECK_POS - DEFAULT_HIPS_POS); } else { // TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses. // for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig. @@ -1765,17 +1763,13 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck"); int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips"); - // AJT: TODO: perhaps expose default gets from rig? - // so this can become _rig->getAbsoluteDefaultPose(rightEyeIndex)... + glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; + glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; + glm::vec3 absNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_NECK_POS; + glm::vec3 absHipsPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans : DEFAULT_HIPS_POS; - glm::vec3 absRightEyePos = rightEyeIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; - glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; - glm::vec3 absNeckPos = neckIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; - glm::vec3 absHipsPos = neckIndex != -1 ? geometryOffset * _debugDrawSkeleton->getAbsoluteBindPose(hipsIndex).trans : DEFAULT_HIPS_POS; - - const glm::quat rotY180 = glm::angleAxis((float)PI, glm::vec3(0.0f, 1.0f, 0.0f)); - localEyes = rotY180 * (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); - localNeck = rotY180 * (absNeckPos - absHipsPos); + localEyes = (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); + localNeck = (absNeckPos - absHipsPos); } // apply simplistic head/neck model diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index d5728b79b4..c057147c03 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -98,8 +98,8 @@ void AnimInverseKinematics::computeTargets(const AnimVariantMap& animVars, std:: target.setType(animVars.lookup(targetVar.typeVar, (int)IKTarget::Type::RotationAndPosition)); if (target.getType() != IKTarget::Type::Unknown) { AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, underPoses); - glm::quat rotation = animVars.lookup(targetVar.rotationVar, defaultPose.rot); - glm::vec3 translation = animVars.lookup(targetVar.positionVar, defaultPose.trans); + glm::quat rotation = animVars.lookupRigToGeometry(targetVar.rotationVar, defaultPose.rot); + glm::vec3 translation = animVars.lookupRigToGeometry(targetVar.positionVar, defaultPose.trans); if (target.getType() == IKTarget::Type::HipsRelativeRotationAndPosition) { translation += _hipsOffset; } diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index e8f796f4dd..3eedec5dbd 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -103,9 +103,9 @@ AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap& if (jointVar.type == JointVar::Type::AbsoluteRotation || jointVar.type == JointVar::Type::AbsolutePosition) { if (jointVar.type == JointVar::Type::AbsoluteRotation) { - defaultAbsPose.rot = animVars.lookup(jointVar.var, defaultAbsPose.rot); + defaultAbsPose.rot = animVars.lookupRigToGeometry(jointVar.var, defaultAbsPose.rot); } else if (jointVar.type == JointVar::Type::AbsolutePosition) { - defaultAbsPose.trans = animVars.lookup(jointVar.var, defaultAbsPose.trans); + defaultAbsPose.trans = animVars.lookupRigToGeometry(jointVar.var, defaultAbsPose.trans); } // because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose. @@ -123,9 +123,9 @@ AnimPose AnimManipulator::computeRelativePoseFromJointVar(const AnimVariantMap& // override the default rel pose AnimPose relPose = defaultRelPose; if (jointVar.type == JointVar::Type::RelativeRotation) { - relPose.rot = animVars.lookup(jointVar.var, defaultRelPose.rot); + relPose.rot = animVars.lookupRigToGeometry(jointVar.var, defaultRelPose.rot); } else if (jointVar.type == JointVar::Type::RelativePosition) { - relPose.trans = animVars.lookup(jointVar.var, defaultRelPose.trans); + relPose.trans = animVars.lookupRigToGeometry(jointVar.var, defaultRelPose.trans); } return relPose; diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 1fe1e21ba8..5914031a3a 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -10,7 +10,6 @@ #include "AnimPose.h" #include -#include #include const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), @@ -19,14 +18,7 @@ const AnimPose AnimPose::identity = AnimPose(glm::vec3(1.0f), AnimPose::AnimPose(const glm::mat4& mat) { scale = extractScale(mat); - float maxScale = std::max(std::max(scale.x, scale.y), scale.z); - if (maxScale > 1.01f || maxScale <= 0.99f) { - // quat_cast doesn't work so well with scaled matrices, so cancel it out. - mat4 tmp = glm::scale(mat, 1.0f / scale); - rot = glm::normalize(glm::quat_cast(tmp)); - } else { - rot = glm::normalize(glm::quat_cast(mat)); - } + rot = glmExtractRotation(mat); trans = extractTranslation(mat); } diff --git a/libraries/animation/src/AnimVariant.cpp b/libraries/animation/src/AnimVariant.cpp index 234e9cef09..118d2e8ce9 100644 --- a/libraries/animation/src/AnimVariant.cpp +++ b/libraries/animation/src/AnimVariant.cpp @@ -43,7 +43,7 @@ QScriptValue AnimVariantMap::animVariantMapToScriptValue(QScriptEngine* engine, target.setProperty(name, quatToScriptValue(engine, value.getQuat())); break; default: - // Note that we don't do mat4 in Javascript currently, and there's not yet a reason to start now. + // Unknown type assert(QString("AnimVariant::Type") == QString("valid")); } }; diff --git a/libraries/animation/src/AnimVariant.h b/libraries/animation/src/AnimVariant.h index 0d7c657058..0cd7fb29ac 100644 --- a/libraries/animation/src/AnimVariant.h +++ b/libraries/animation/src/AnimVariant.h @@ -18,8 +18,9 @@ #include #include #include +#include +#include #include "AnimationLogging.h" -#include "StreamUtils.h" class AnimVariant { public: @@ -29,7 +30,6 @@ public: Float, Vec3, Quat, - Mat4, String, NumTypes }; @@ -40,7 +40,6 @@ public: AnimVariant(float value) : _type(Type::Float) { _val.floats[0] = value; } AnimVariant(const glm::vec3& value) : _type(Type::Vec3) { *reinterpret_cast(&_val) = value; } AnimVariant(const glm::quat& value) : _type(Type::Quat) { *reinterpret_cast(&_val) = value; } - AnimVariant(const glm::mat4& value) : _type(Type::Mat4) { *reinterpret_cast(&_val) = value; } AnimVariant(const QString& value) : _type(Type::String) { _stringVal = value; } bool isBool() const { return _type == Type::Bool; } @@ -48,7 +47,6 @@ public: bool isFloat() const { return _type == Type::Float; } bool isVec3() const { return _type == Type::Vec3; } bool isQuat() const { return _type == Type::Quat; } - bool isMat4() const { return _type == Type::Mat4; } bool isString() const { return _type == Type::String; } Type getType() const { return _type; } @@ -57,7 +55,6 @@ public: void setFloat(float value) { assert(_type == Type::Float); _val.floats[0] = value; } void setVec3(const glm::vec3& value) { assert(_type == Type::Vec3); *reinterpret_cast(&_val) = value; } void setQuat(const glm::quat& value) { assert(_type == Type::Quat); *reinterpret_cast(&_val) = value; } - void setMat4(const glm::mat4& value) { assert(_type == Type::Mat4); *reinterpret_cast(&_val) = value; } void setString(const QString& value) { assert(_type == Type::String); _stringVal = value; } bool getBool() const { assert(_type == Type::Bool); return _val.boolVal; } @@ -66,7 +63,6 @@ public: const glm::vec3& getVec3() const { assert(_type == Type::Vec3); return *reinterpret_cast(&_val); } const glm::quat& getQuat() const { assert(_type == Type::Quat); return *reinterpret_cast(&_val); } - const glm::mat4& getMat4() const { assert(_type == Type::Mat4); return *reinterpret_cast(&_val); } const QString& getString() const { assert(_type == Type::String); return _stringVal; } protected: @@ -112,7 +108,7 @@ public: } } - const glm::vec3& lookup(const QString& key, const glm::vec3& defaultValue) const { + const glm::vec3& lookupRaw(const QString& key, const glm::vec3& defaultValue) const { if (key.isEmpty()) { return defaultValue; } else { @@ -121,7 +117,16 @@ public: } } - const glm::quat& lookup(const QString& key, const glm::quat& defaultValue) const { + glm::vec3 lookupRigToGeometry(const QString& key, const glm::vec3& defaultValue) const { + if (key.isEmpty()) { + return defaultValue; + } else { + auto iter = _map.find(key); + return iter != _map.end() ? transformPoint(_rigToGeometryMat, iter->second.getVec3()) : defaultValue; + } + } + + const glm::quat& lookupRaw(const QString& key, const glm::quat& defaultValue) const { if (key.isEmpty()) { return defaultValue; } else { @@ -130,12 +135,12 @@ public: } } - const glm::mat4& lookup(const QString& key, const glm::mat4& defaultValue) const { + glm::quat lookupRigToGeometry(const QString& key, const glm::quat& defaultValue) const { if (key.isEmpty()) { return defaultValue; } else { auto iter = _map.find(key); - return iter != _map.end() ? iter->second.getMat4() : defaultValue; + return iter != _map.end() ? _rigToGeometryRot * iter->second.getQuat() : defaultValue; } } @@ -153,13 +158,17 @@ public: void set(const QString& key, float value) { _map[key] = AnimVariant(value); } void set(const QString& key, const glm::vec3& value) { _map[key] = AnimVariant(value); } void set(const QString& key, const glm::quat& value) { _map[key] = AnimVariant(value); } - void set(const QString& key, const glm::mat4& value) { _map[key] = AnimVariant(value); } void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); } void unset(const QString& key) { _map.erase(key); } void setTrigger(const QString& key) { _triggers.insert(key); } void clearTriggers() { _triggers.clear(); } + void setRigToGeometryTransform(const glm::mat4& rigToGeometry) { + _rigToGeometryMat = rigToGeometry; + _rigToGeometryRot = glmExtractRotation(rigToGeometry); + } + void clearMap() { _map.clear(); } bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); } @@ -189,9 +198,6 @@ public: case AnimVariant::Type::Quat: qCDebug(animation) << " " << pair.first << "=" << pair.second.getQuat(); break; - case AnimVariant::Type::Mat4: - qCDebug(animation) << " " << pair.first << "=" << pair.second.getMat4(); - break; case AnimVariant::Type::String: qCDebug(animation) << " " << pair.first << "=" << pair.second.getString(); break; @@ -205,6 +211,8 @@ public: protected: std::map _map; std::set _triggers; + glm::mat4 _rigToGeometryMat; + glm::quat _rigToGeometryRot; }; typedef std::function AnimVariantResultHandler; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2f2b1309ba..2442c0b8b4 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -236,12 +236,17 @@ int Rig::getJointStateCount() const { return _relativePoses.size(); } -int Rig::indexOfJoint(const QString& jointName) { +int Rig::indexOfJoint(const QString& jointName) const { return _animSkeleton->nameToJointIndex(jointName); } void Rig::setModelOffset(const glm::mat4& modelOffset) { _modelOffset = AnimPose(modelOffset); + + // compute geometryToAvatarTransforms + glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + _geometryToRigTransform = AnimPose(glm::vec3(1), yFlip180, glm::vec3()) * _modelOffset * _geometryOffset; + _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); } bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { @@ -322,9 +327,9 @@ void Rig::restoreJointTranslation(int index, float fraction, float priority) { // AJT: NOTE old code did not have 180 flip! bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { - glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { - position = (rotation * yFlip * _absolutePoses[jointIndex].trans) + translation; + glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + position = (rotation * yFlip180 * _absolutePoses[jointIndex].trans) + translation; return true; } else { return false; @@ -333,9 +338,9 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm: // AJT: NOTE old code did not have 180 flip! bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { - glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { - position = yFlip * _absolutePoses[jointIndex].trans; + glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + position = yFlip180 * _absolutePoses[jointIndex].trans; return true; } else { return false; @@ -419,6 +424,14 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) { } } +AnimPose Rig::getAbsoluteDefaultPose(int index) const { + if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { + return AnimPose(_geometryToRigTransform) * _animSkeleton->getAbsoluteDefaultPose(index); + } else { + return AnimPose::identity; + } +} + // animation reference speeds. static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s @@ -682,13 +695,14 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { if (_animNode) { updateAnimationStateHandlers(); + _animVars.setRigToGeometryTransform(_rigToGeometryTransform); // evaluate the animation AnimNode::Triggers triggersOut; _relativePoses = _animNode->evaluate(_animVars, deltaTime, triggersOut); if ((int)_relativePoses.size() != _animSkeleton->getNumJoints()) { // animations haven't fully loaded yet. - _relativePoses = _animSkeleton->getRelativeBindPoses(); + _relativePoses = _animSkeleton->getRelativeDefaultPoses(); } _animVars.clearTriggers(); for (auto& trigger : triggersOut) { @@ -781,27 +795,23 @@ void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, floa } } -static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const AnimPose& geometryHMDPose, - glm::vec3& headPositionOut, glm::quat& headOrientationOut, - glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) { +void Rig::computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut, + glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) const { - // the input hmd values are in avatar space - // we need to transform them into bone space. - AnimPose hmdPose = geometryHMDPose; + // the input hmd values are in avatar/rig space const glm::vec3 hmdPosition = hmdPose.trans; const glm::quat hmdOrientation = hmdPose.rot; // TODO: cache jointIndices - int rightEyeIndex = skeleton->nameToJointIndex("RightEye"); - int leftEyeIndex = skeleton->nameToJointIndex("LeftEye"); - int headIndex = skeleton->nameToJointIndex("Head"); - int neckIndex = skeleton->nameToJointIndex("Neck"); + int rightEyeIndex = indexOfJoint("RightEye"); + int leftEyeIndex = indexOfJoint("LeftEye"); + int headIndex = indexOfJoint("Head"); + int neckIndex = indexOfJoint("Neck"); - // Use absolute bindPose positions just in case the relBindPose have rotations we don't expect. - glm::vec3 absRightEyePos = rightEyeIndex != -1 ? skeleton->getAbsoluteBindPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; - glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? skeleton->getAbsoluteBindPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; - glm::vec3 absHeadPos = headIndex != -1 ? skeleton->getAbsoluteBindPose(headIndex).trans : DEFAULT_HEAD_POS; - glm::vec3 absNeckPos = neckIndex != -1 ? skeleton->getAbsoluteBindPose(neckIndex).trans : DEFAULT_NECK_POS; + glm::vec3 absRightEyePos = rightEyeIndex != -1 ? getAbsoluteDefaultPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; + glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; + glm::vec3 absHeadPos = headIndex != -1 ? getAbsoluteDefaultPose(headIndex).trans : DEFAULT_HEAD_POS; + glm::vec3 absNeckPos = neckIndex != -1 ? getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_NECK_POS; glm::vec3 absCenterEyePos = (absRightEyePos + absLeftEyePos) / 2.0f; glm::vec3 eyeOffset = absCenterEyePos - absHeadPos; @@ -816,18 +826,19 @@ static void computeHeadNeckAnimVars(AnimSkeleton::ConstPointer skeleton, const A // neck neckPositionOut = hmdPosition - hmdOrientation * (headOffset + eyeOffset); - // slerp between bind orientation and hmdOrientation - neckOrientationOut = safeMix(hmdOrientation, skeleton->getRelativeBindPose(skeleton->nameToJointIndex("Neck")).rot, 0.5f); + // slerp between default orientation and hmdOrientation + neckOrientationOut = safeMix(hmdOrientation, _animSkeleton->getRelativeDefaultPose(neckIndex).rot, 0.5f); } void Rig::updateNeckJoint(int index, const HeadParameters& params) { if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { + glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); if (params.isInHMD) { glm::vec3 headPos, neckPos; glm::quat headRot, neckRot; - AnimPose hmdPose(glm::vec3(1.0f), avatarToGeometryNegZForward(params.localHeadOrientation), avatarToGeometry(params.localHeadPosition)); - computeHeadNeckAnimVars(_animSkeleton, hmdPose, headPos, headRot, neckPos, neckRot); + AnimPose hmdPose(glm::vec3(1.0f), params.localHeadOrientation * yFlip180, params.localHeadPosition); + computeHeadNeckAnimVars(hmdPose, headPos, headRot, neckPos, neckRot); // debug rendering #ifdef DEBUG_RENDERING @@ -852,7 +863,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { } else { _animVars.unset("headPosition"); - _animVars.set("headRotation", avatarToGeometryNegZForward(params.localHeadOrientation)); + _animVars.set("headRotation", params.localHeadOrientation * yFlip180); _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); _animVars.set("headType", (int)IKTarget::Type::RotationOnly); _animVars.unset("neckPosition"); @@ -886,31 +897,12 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm */ } -glm::vec3 Rig::avatarToGeometry(const glm::vec3& pos) const { - glm::quat yFlip = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - AnimPose geometryToAvatarTransform = AnimPose(glm::vec3(1), yFlip, glm::vec3()) * _modelOffset * _geometryOffset; - glm::vec3 result = geometryToAvatarTransform.inverse() * pos; - return result; -} - -glm::quat Rig::avatarToGeometryNegZForward(const glm::quat& quat) const { - AnimPose yFlip(glm::vec3(1), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3()); - AnimPose geometryToAvatarTransform = yFlip * _modelOffset * _geometryOffset; - return (geometryToAvatarTransform.inverse() * AnimPose(glm::vec3(1), quat, glm::vec3()) * yFlip).rot; -} - -glm::quat Rig::avatarToGeometryZForward(const glm::quat& quat) const { - AnimPose yFlip(glm::vec3(1), glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)), glm::vec3()); - AnimPose geometryToAvatarTransform = yFlip * _modelOffset * _geometryOffset; - return (geometryToAvatarTransform.inverse() * AnimPose(glm::vec3(1), quat, glm::vec3())).rot; -} - void Rig::updateFromHandParameters(const HandParameters& params, float dt) { if (_animSkeleton && _animNode) { if (params.isLeftEnabled) { - _animVars.set("leftHandPosition", avatarToGeometry(params.leftPosition)); - _animVars.set("leftHandRotation", avatarToGeometryZForward(params.leftOrientation)); + _animVars.set("leftHandPosition", params.leftPosition); + _animVars.set("leftHandRotation", params.leftOrientation); _animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("leftHandPosition"); @@ -918,8 +910,8 @@ void Rig::updateFromHandParameters(const HandParameters& params, float dt) { _animVars.set("leftHandType", (int)IKTarget::Type::HipsRelativeRotationAndPosition); } if (params.isRightEnabled) { - _animVars.set("rightHandPosition", avatarToGeometry(params.rightPosition)); - _animVars.set("rightHandRotation", avatarToGeometryZForward(params.rightOrientation)); + _animVars.set("rightHandPosition", params.rightPosition); + _animVars.set("rightHandRotation", params.rightOrientation); _animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); } else { _animVars.unset("rightHandPosition"); @@ -986,7 +978,7 @@ void Rig::initAnimGraph(const QUrl& url) { bool Rig::getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const { if (_animSkeleton && _rootJointIndex >= 0) { - modelRegistrationPointOut = _geometryOffset * -_animSkeleton->getAbsoluteBindPose(_rootJointIndex).trans; + modelRegistrationPointOut = _geometryOffset * -_animSkeleton->getAbsoluteDefaultPose(_rootJointIndex).trans; return true; } else { return false; diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 35d9e5747a..6d5ce26df1 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -85,10 +85,9 @@ public: void reset(const FBXGeometry& geometry); bool jointStatesEmpty(); int getJointStateCount() const; - int indexOfJoint(const QString& jointName); + int indexOfJoint(const QString& jointName) const; void setModelOffset(const glm::mat4& modelOffset); - const AnimPose& getGeometryOffset() const { return _geometryOffset; } bool getJointStateRotation(int index, glm::quat& rotation) const; bool getJointStateTranslation(int index, glm::vec3& translation) const; @@ -146,6 +145,8 @@ public: const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } + AnimPose getAbsoluteDefaultPose(int index) const; // avatar space + void copyJointsIntoJointData(QVector& jointDataVec) const; void copyJointsFromJointData(const QVector& jointDataVec); @@ -156,15 +157,13 @@ public: void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); + void computeHeadNeckAnimVars(const AnimPose& hmdPose, glm::vec3& headPositionOut, glm::quat& headOrientationOut, + glm::vec3& neckPositionOut, glm::quat& neckOrientationOut) const; void updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade); void calcAnimAlpha(float speed, const std::vector& referenceSpeeds, float* alphaOut) const; void computeEyesInRootFrame(const AnimPoseVec& poses); - glm::vec3 avatarToGeometry(const glm::vec3& pos) const; - glm::quat avatarToGeometryZForward(const glm::quat& quat) const; - glm::quat avatarToGeometryNegZForward(const glm::quat& quat) const; - AnimPose _modelOffset; // model to avatar space (without 180 flip) AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets) AnimPoseVec _relativePoses; // geometry space relative to parent. @@ -172,6 +171,9 @@ public: AnimPoseVec _overridePoses; // geometry space relative to parent. std::vector _overrideFlags; + glm::mat4 _geometryToRigTransform; + glm::mat4 _rigToGeometryTransform; + int _rootJointIndex { -1 }; int _leftHandJointIndex { -1 }; diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index fa010a85bd..2199fefbce 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -10,7 +10,7 @@ // #include "GLMHelpers.h" - +#include #include "NumericalConstants.h" const vec3 Vectors::UNIT_X{ 1.0f, 0.0f, 0.0f }; @@ -276,6 +276,18 @@ glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal) { 0.5f * sqrtf(z2) * (upper[0][1] >= upper[1][0] ? 1.0f : -1.0f))); } +glm::quat glmExtractRotation(const glm::mat4& matrix) { + glm::vec3 scale = extractScale(matrix); + float maxScale = std::max(std::max(scale.x, scale.y), scale.z); + if (maxScale > 1.01f || maxScale <= 0.99f) { + // quat_cast doesn't work so well with scaled matrices, so cancel it out. + glm::mat4 tmp = glm::scale(matrix, 1.0f / scale); + return glm::normalize(glm::quat_cast(tmp)); + } else { + return glm::normalize(glm::quat_cast(matrix)); + } +} + glm::vec3 extractScale(const glm::mat4& matrix) { return glm::vec3(glm::length(matrix[0]), glm::length(matrix[1]), glm::length(matrix[2])); } diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 8d3410aaf2..54d422e465 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -131,6 +131,7 @@ glm::vec3 extractTranslation(const glm::mat4& matrix); void setTranslation(glm::mat4& matrix, const glm::vec3& translation); glm::quat extractRotation(const glm::mat4& matrix, bool assumeOrthogonal = false); +glm::quat glmExtractRotation(const glm::mat4& matrix); glm::vec3 extractScale(const glm::mat4& matrix); From 995958a8f041c18c9ab2cb13b937bf360b3c0302 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 18:45:53 -0800 Subject: [PATCH 57/84] Rig: normalized index bounds checking. --- libraries/animation/src/Rig.cpp | 40 ++++++++++++++++----------------- libraries/animation/src/Rig.h | 1 + 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 2442c0b8b4..4e47332e87 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -250,7 +250,7 @@ void Rig::setModelOffset(const glm::mat4& modelOffset) { } bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { - if (index >= 0 && index < (int)_relativePoses.size()) { + if (isValid(index)) { rotation = _relativePoses[index].rot; return !isEqual(rotation, _animSkeleton->getRelativeDefaultPose(index).rot); } else { @@ -259,7 +259,7 @@ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { } bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { - if (index >= 0 && index < (int)_relativePoses.size()) { + if (isValid(index)) { translation = _relativePoses[index].trans; return !isEqual(translation, _animSkeleton->getRelativeDefaultPose(index).trans); } else { @@ -268,7 +268,7 @@ bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { } void Rig::clearJointState(int index) { - if (index >= 0 && index < (int)_relativePoses.size()) { + if (isValid(index)) { _overrideFlags[index] = false; } } @@ -279,13 +279,13 @@ void Rig::clearJointStates() { } void Rig::clearJointAnimationPriority(int index) { - if (index >= 0 && index < (int)_overrideFlags.size()) { + if (isValid(index)) { _overrideFlags[index] = false; } } void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { - if (index >= 0 && index < (int)_overrideFlags.size()) { + if (isValid(index)) { if (valid) { assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; @@ -295,7 +295,7 @@ void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translatio } void Rig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - if (index >= 0 && index < (int)_overrideFlags.size()) { + if (isValid(index)) { assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; _overridePoses[index].rot = rotation; @@ -306,7 +306,7 @@ void Rig::setJointState(int index, bool valid, const glm::quat& rotation, const // Deprecated. // WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation. void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { - if (index >= 0 && index < (int)_overrideFlags.size()) { + if (isValid(index)) { if (valid) { ASSERT(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; @@ -327,7 +327,7 @@ void Rig::restoreJointTranslation(int index, float fraction, float priority) { // AJT: NOTE old code did not have 180 flip! bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { - if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { + if (isValid(jointIndex)) { glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); position = (rotation * yFlip180 * _absolutePoses[jointIndex].trans) + translation; return true; @@ -338,7 +338,7 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm: // AJT: NOTE old code did not have 180 flip! bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { - if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { + if (isValid(jointIndex)) { glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); position = yFlip180 * _absolutePoses[jointIndex].trans; return true; @@ -348,7 +348,7 @@ bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { } bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const { - if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { + if (isValid(jointIndex)) { result = rotation * _absolutePoses[jointIndex].rot; return true; } else { @@ -359,7 +359,7 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const // Deprecated. // WARNING: this is not symmetric with setJointRotation. It's historical. Use the appropriate specific variation. bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { - if (jointIndex >= 0 && jointIndex < (int)_relativePoses.size()) { + if (isValid(jointIndex)) { rotation = _relativePoses[jointIndex].rot; return true; } else { @@ -368,7 +368,7 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { } bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const { - if (jointIndex >= 0 && jointIndex < (int)_relativePoses.size()) { + if (isValid(jointIndex)) { translation = _relativePoses[jointIndex].trans; return true; } else { @@ -638,10 +638,12 @@ QScriptValue Rig::addAnimationStateHandler(QScriptValue handler, QScriptValue pr } return QScriptValue(_nextStateHandlerId); // suitable for giving to removeAnimationStateHandler } + void Rig::removeAnimationStateHandler(QScriptValue identifier) { // called in script thread QMutexLocker locker(&_stateMutex); _stateHandlers.remove(identifier.isNumber() ? identifier.toInt32() : 0); // silently continues if handler not present. 0 is unused } + void Rig::animationStateHandlerResult(int identifier, QScriptValue result) { // called synchronously from script QMutexLocker locker(&_stateMutex); auto found = _stateHandlers.find(identifier); @@ -785,13 +787,11 @@ static const glm::vec3 Y_AXIS(0.0f, 1.0f, 0.0f); static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f); void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { - if (index >= 0 && index) { - if (_animSkeleton) { - glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) * - glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) * - glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS)); - _animVars.set("lean", absRot); - } + if (isValid(index)) { + glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) * + glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) * + glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS)); + _animVars.set("lean", absRot); } } @@ -1025,7 +1025,7 @@ void Rig::buildAbsolutePoses() { } glm::mat4 Rig::getJointTransform(int jointIndex) const { - if (jointIndex >= 0 && jointIndex < (int)_absolutePoses.size()) { + if (isValid(jointIndex)) { return _absolutePoses[jointIndex]; } else { return glm::mat4(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 6d5ce26df1..ee208f6b4e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -151,6 +151,7 @@ public: void copyJointsFromJointData(const QVector& jointDataVec); protected: + bool isValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); void applyOverridePoses(); void buildAbsolutePoses(); From 007a3c5134b283965bbdffaf0f8e28e8d5484560 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Fri, 20 Nov 2015 19:00:51 -0800 Subject: [PATCH 58/84] Fix one frame lag in AnimDebugDraw --- interface/src/Application.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3216552719..ac03e2c037 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2935,11 +2935,6 @@ void Application::update(float deltaTime) { loadViewFrustum(_myCamera, _viewFrustum); } - // Update animation debug draw renderer - { - AnimDebugDraw::getInstance().update(); - } - quint64 now = usecTimestampNow(); // Update my voxel servers with my current voxel query... @@ -3499,6 +3494,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se myAvatar->preRender(renderArgs); myAvatar->endRender(); + // Update animation debug draw renderer + AnimDebugDraw::getInstance().update(); activeRenderingThread = QThread::currentThread(); PROFILE_RANGE(__FUNCTION__); From 6cfd831a5a5aaec8577325a1ef8af39ede85887a Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 21 Nov 2015 09:50:56 -0800 Subject: [PATCH 59/84] Menu: Added Developer > Avatar > Debug Draw Position Also renamed "Debug Draw Bind Pose" to "Debug Draw Default Pose" --- interface/src/Menu.cpp | 6 ++- interface/src/Menu.h | 3 +- interface/src/avatar/MyAvatar.cpp | 75 +++++++++++++------------------ interface/src/avatar/MyAvatar.h | 6 +-- libraries/animation/src/Rig.cpp | 12 +++-- libraries/animation/src/Rig.h | 1 + 6 files changed, 49 insertions(+), 54 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c56b430622..561f0c7e1b 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -443,10 +443,12 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarUpdateThreading, 0, false, qApp, SLOT(setAvatarUpdateThreading(bool))); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawBindPose, 0, false, - avatar, SLOT(setEnableDebugDrawBindPose(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, + avatar, SLOT(setEnableDebugDrawDefaultPose(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false, avatar, SLOT(setEnableDebugDrawAnimPose(bool))); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawPosition, 0, false, + avatar, SLOT(setEnableDebugDrawPosition(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::MeshVisible, 0, true, avatar, SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 4efb7e7749..c30c04cc09 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -131,7 +131,8 @@ namespace MenuOption { const QString AddressBar = "Show Address Bar"; const QString Animations = "Animations..."; const QString AnimDebugDrawAnimPose = "Debug Draw Animation"; - const QString AnimDebugDrawBindPose = "Debug Draw Bind Pose"; + const QString AnimDebugDrawDefaultPose = "Debug Draw Default Pose"; + const QString AnimDebugDrawPosition= "Debug Draw Position"; const QString Antialiasing = "Antialiasing"; const QString AssetMigration = "ATP Asset Migration"; const QString Atmosphere = "Atmosphere"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e53eec95c1..cec110d874 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -40,7 +40,6 @@ #include #include "devices/Faceshift.h" - #include "Application.h" #include "AvatarManager.h" #include "Environment.h" @@ -353,12 +352,6 @@ void MyAvatar::updateHMDFollowVelocity() { // This is so the correct camera can be used for rendering. void MyAvatar::updateSensorToWorldMatrix() { -//#ifdef DEBUG_RENDERING - // draw marker about avatar's position - const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); - DebugDraw::getInstance().addMyAvatarMarker("pos", glm::quat(), glm::vec3(), red); -//#endif - // update the sensor mat so that the body position will end up in the desired // position when driven from the head. glm::mat4 desiredMat = createMatFromQuatAndPos(getOrientation(), getPosition()); @@ -631,13 +624,8 @@ float loadSetting(QSettings& settings, const char* name, float defaultValue) { return value; } -// Resource loading is not yet thread safe. If an animation is not loaded when requested by other than tha main thread, -// we block in AnimationHandle::setURL => AnimationCache::getAnimation. -// Meanwhile, the main thread will also eventually lock as it tries to render us. -// If we demand the animation from the update thread while we're locked, we'll deadlock. -// Until we untangle this, code puts the updates back on the main thread temporarilly and starts all the loading. -void MyAvatar::setEnableDebugDrawBindPose(bool isEnabled) { - _enableDebugDrawBindPose = isEnabled; +void MyAvatar::setEnableDebugDrawDefaultPose(bool isEnabled) { + _enableDebugDrawDefaultPose = isEnabled; if (!isEnabled) { AnimDebugDraw::getInstance().removeSkeleton("myAvatar"); @@ -652,6 +640,15 @@ void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) { } } +void MyAvatar::setEnableDebugDrawPosition(bool isEnabled) { + if (isEnabled) { + const glm::vec4 red(1.0f, 0.0f, 0.0f, 1.0f); + DebugDraw::getInstance().addMyAvatarMarker("avatarPosition", glm::quat(), glm::vec3(), red); + } else { + DebugDraw::getInstance().removeMyAvatarMarker("avatarPosition"); + } +} + void MyAvatar::setEnableMeshVisible(bool isEnabled) { render::ScenePointer scene = qApp->getMain3DScene(); _skeletonModel.setVisibleInScene(isEnabled, scene); @@ -702,8 +699,9 @@ void MyAvatar::loadData() { settings.endGroup(); setEnableMeshVisible(Menu::getInstance()->isOptionChecked(MenuOption::MeshVisible)); - setEnableDebugDrawBindPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawBindPose)); + setEnableDebugDrawDefaultPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawDefaultPose)); setEnableDebugDrawAnimPose(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawAnimPose)); + setEnableDebugDrawPosition(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawPosition)); } void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const { @@ -1257,34 +1255,33 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { initHeadBones(); _skeletonModel.setCauterizeBoneSet(_headBoneSet); initAnimGraph(); - _debugDrawSkeleton = std::make_shared(_skeletonModel.getGeometry()->getFBXGeometry()); } - if (_enableDebugDrawBindPose || _enableDebugDrawAnimPose) { + if (_enableDebugDrawDefaultPose || _enableDebugDrawAnimPose) { + + auto animSkeleton = _rig->getAnimSkeleton(); // bones are aligned such that z is forward, not -z. glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f)); AnimPose xform(glm::vec3(1), getOrientation() * rotY180, getPosition()); - /* - if (_enableDebugDrawBindPose && _debugDrawSkeleton) { + if (_enableDebugDrawDefaultPose && animSkeleton) { glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); - AnimDebugDraw::getInstance().addSkeleton("myAvatar", _debugDrawSkeleton, xform, gray); + AnimDebugDraw::getInstance().addSkeleton("myAvatar", animSkeleton, xform, gray); } - */ - if (_enableDebugDrawAnimPose && _debugDrawSkeleton) { + if (_enableDebugDrawAnimPose && animSkeleton) { glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); auto rig = _skeletonModel.getRig(); // build absolute AnimPoseVec from rig AnimPoseVec absPoses; - absPoses.reserve(_debugDrawSkeleton->getNumJoints()); + absPoses.reserve(rig->getJointStateCount()); for (int i = 0; i < _rig->getJointStateCount(); i++) { absPoses.push_back(AnimPose(_rig->getJointTransform(i))); } - AnimDebugDraw::getInstance().addAbsolutePoses("myAvatar", _debugDrawSkeleton, absPoses, xform, cyan); + AnimDebugDraw::getInstance().addAbsolutePoses("myAvatar", animSkeleton, absPoses, xform, cyan); } } @@ -1749,28 +1746,18 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f); const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.05f, 0.0f); - vec3 localEyes, localNeck; - if (!_debugDrawSkeleton) { - localEyes = (((DEFAULT_RIGHT_EYE_POS + DEFAULT_LEFT_EYE_POS) / 2.0f) - DEFAULT_HIPS_POS); - localNeck = (DEFAULT_NECK_POS - DEFAULT_HIPS_POS); - } else { - // TODO: At the moment MyAvatar does not have access to the rig, which has the skeleton, which has the bind poses. - // for now use the _debugDrawSkeleton, which is initialized with the same FBX model as the rig. + int rightEyeIndex = _rig->indexOfJoint("RightEye"); + int leftEyeIndex = _rig->indexOfJoint("LeftEye"); + int neckIndex = _rig->indexOfJoint("Neck"); + int hipsIndex = _rig->indexOfJoint("Hips"); - // TODO: cache these indices. - int rightEyeIndex = _debugDrawSkeleton->nameToJointIndex("RightEye"); - int leftEyeIndex = _debugDrawSkeleton->nameToJointIndex("LeftEye"); - int neckIndex = _debugDrawSkeleton->nameToJointIndex("Neck"); - int hipsIndex = _debugDrawSkeleton->nameToJointIndex("Hips"); + glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; + glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; + glm::vec3 absNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_NECK_POS; + glm::vec3 absHipsPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans : DEFAULT_HIPS_POS; - glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; - glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; - glm::vec3 absNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_NECK_POS; - glm::vec3 absHipsPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans : DEFAULT_HIPS_POS; - - localEyes = (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); - localNeck = (absNeckPos - absHipsPos); - } + glm::vec3 localEyes = (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); + glm::vec3 localNeck = (absNeckPos - absHipsPos); // apply simplistic head/neck model // figure out where the avatar body should be by applying offsets from the avatar's neck & head joints. diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index e6578c4e13..13575388e3 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -255,8 +255,9 @@ public slots: const QString& getAnimGraphUrl() const { return _animGraphUrl; } - void setEnableDebugDrawBindPose(bool isEnabled); + void setEnableDebugDrawDefaultPose(bool isEnabled); void setEnableDebugDrawAnimPose(bool isEnabled); + void setEnableDebugDrawPosition(bool isEnabled); void setEnableMeshVisible(bool isEnabled); void setAnimGraphUrl(const QString& url) { _animGraphUrl = url; } @@ -386,9 +387,8 @@ private: RigPointer _rig; bool _prevShouldDrawHead; - bool _enableDebugDrawBindPose { false }; + bool _enableDebugDrawDefaultPose { false }; bool _enableDebugDrawAnimPose { false }; - AnimSkeleton::ConstPointer _debugDrawSkeleton { nullptr }; AudioListenerMode _audioListenerMode; glm::vec3 _customListenPosition; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 4e47332e87..455b447f17 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -161,9 +161,9 @@ void Rig::prefetchAnimation(const QString& url) { } void Rig::destroyAnimGraph() { - _animSkeleton = nullptr; - _animLoader = nullptr; - _animNode = nullptr; + _animSkeleton.reset(); + _animLoader.reset(); + _animNode.reset(); _relativePoses.clear(); _absolutePoses.clear(); _overridePoses.clear(); @@ -237,7 +237,11 @@ int Rig::getJointStateCount() const { } int Rig::indexOfJoint(const QString& jointName) const { - return _animSkeleton->nameToJointIndex(jointName); + if (_animSkeleton) { + return _animSkeleton->nameToJointIndex(jointName); + } else { + return -1; + } } void Rig::setModelOffset(const glm::mat4& modelOffset) { diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index ee208f6b4e..9428ae2c5e 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -74,6 +74,7 @@ public: virtual ~Rig() {} void destroyAnimGraph(); + void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreAnimation(); QStringList getAnimationRoles() const; From fe683edb66185fc1fdbbf88615d82e1cb1b7b504 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 21 Nov 2015 10:53:24 -0800 Subject: [PATCH 60/84] Avatar Debug Draw Default Pose now works --- interface/src/avatar/MyAvatar.cpp | 20 +++--- libraries/animation/src/Rig.cpp | 65 ++++++++++++++------ libraries/animation/src/Rig.h | 7 ++- libraries/render-utils/src/AnimDebugDraw.cpp | 42 ------------- 4 files changed, 61 insertions(+), 73 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cec110d874..46df660bc4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -628,7 +628,7 @@ void MyAvatar::setEnableDebugDrawDefaultPose(bool isEnabled) { _enableDebugDrawDefaultPose = isEnabled; if (!isEnabled) { - AnimDebugDraw::getInstance().removeSkeleton("myAvatar"); + AnimDebugDraw::getInstance().removeAbsolutePoses("myAvatarDefaultPoses"); } } @@ -636,7 +636,7 @@ void MyAvatar::setEnableDebugDrawAnimPose(bool isEnabled) { _enableDebugDrawAnimPose = isEnabled; if (!isEnabled) { - AnimDebugDraw::getInstance().removeAbsolutePoses("myAvatar"); + AnimDebugDraw::getInstance().removeAbsolutePoses("myAvatarAnimPoses"); } } @@ -1261,27 +1261,27 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { auto animSkeleton = _rig->getAnimSkeleton(); - // bones are aligned such that z is forward, not -z. - glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f)); - AnimPose xform(glm::vec3(1), getOrientation() * rotY180, getPosition()); - if (_enableDebugDrawDefaultPose && animSkeleton) { + AnimPose xform(glm::vec3(1), getOrientation(), getPosition()); glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); - AnimDebugDraw::getInstance().addSkeleton("myAvatar", animSkeleton, xform, gray); + AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarDefaultPoses", animSkeleton, _rig->getAbsoluteDefaultPoses(), xform, gray); } if (_enableDebugDrawAnimPose && animSkeleton) { glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); - auto rig = _skeletonModel.getRig(); + // getJointTransforms are aligned such that z is forward, not -z. + // so they need a 180 flip + glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f)); + AnimPose xform(glm::vec3(1), getOrientation() * rotY180, getPosition()); // build absolute AnimPoseVec from rig AnimPoseVec absPoses; - absPoses.reserve(rig->getJointStateCount()); + absPoses.reserve(_rig->getJointStateCount()); for (int i = 0; i < _rig->getJointStateCount(); i++) { absPoses.push_back(AnimPose(_rig->getJointTransform(i))); } - AnimDebugDraw::getInstance().addAbsolutePoses("myAvatar", animSkeleton, absPoses, xform, cyan); + AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, cyan); } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 455b447f17..e61f56ba78 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -172,8 +172,9 @@ void Rig::destroyAnimGraph() { void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) { - setModelOffset(modelOffset); _geometryOffset = AnimPose(geometry.offset); + setModelOffset(modelOffset); + _animSkeleton = std::make_shared(geometry); computeEyesInRootFrame(_animSkeleton->getRelativeDefaultPoses()); @@ -181,7 +182,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _relativePoses.clear(); _relativePoses = _animSkeleton->getRelativeDefaultPoses(); - buildAbsolutePoses(); + buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true); _overridePoses.clear(); _overridePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -189,6 +190,8 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _overrideFlags.clear(); _overrideFlags.resize(_animSkeleton->getNumJoints(), false); + buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); + _rootJointIndex = geometry.rootJointIndex; _leftHandJointIndex = geometry.leftHandJointIndex; _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; @@ -207,7 +210,7 @@ void Rig::reset(const FBXGeometry& geometry) { _relativePoses.clear(); _relativePoses = _animSkeleton->getRelativeDefaultPoses(); - buildAbsolutePoses(); + buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true); _overridePoses.clear(); _overridePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -215,6 +218,8 @@ void Rig::reset(const FBXGeometry& geometry) { _overrideFlags.clear(); _overrideFlags.resize(_animSkeleton->getNumJoints(), false); + buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); + _rootJointIndex = geometry.rootJointIndex; _leftHandJointIndex = geometry.leftHandJointIndex; _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; @@ -244,13 +249,22 @@ int Rig::indexOfJoint(const QString& jointName) const { } } -void Rig::setModelOffset(const glm::mat4& modelOffset) { - _modelOffset = AnimPose(modelOffset); +void Rig::setModelOffset(const glm::mat4& modelOffsetMat) { + AnimPose newModelOffset = AnimPose(modelOffsetMat); + if (!isEqual(_modelOffset.trans, newModelOffset.trans) || + !isEqual(_modelOffset.rot, newModelOffset.rot) || + !isEqual(_modelOffset.scale, newModelOffset.scale)) { - // compute geometryToAvatarTransforms - glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - _geometryToRigTransform = AnimPose(glm::vec3(1), yFlip180, glm::vec3()) * _modelOffset * _geometryOffset; - _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); + _modelOffset = newModelOffset; + + // compute geometryToAvatarTransforms + glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + _geometryToRigTransform = AnimPose(glm::vec3(1), yFlip180, glm::vec3()) * _modelOffset * _geometryOffset; + _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); + + // rebuild cached default poses + buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); + } } bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { @@ -430,12 +444,16 @@ void Rig::computeEyesInRootFrame(const AnimPoseVec& poses) { AnimPose Rig::getAbsoluteDefaultPose(int index) const { if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { - return AnimPose(_geometryToRigTransform) * _animSkeleton->getAbsoluteDefaultPose(index); + return _absoluteDefaultPoses[index]; } else { return AnimPose::identity; } } +const AnimPoseVec& Rig::getAbsoluteDefaultPoses() const { + return _absoluteDefaultPoses; +} + // animation reference speeds. static const std::vector FORWARD_SPEEDS = { 0.4f, 1.4f, 4.5f }; // m/s static const std::vector BACKWARD_SPEEDS = { 0.6f, 1.45f }; // m/s @@ -719,7 +737,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { } applyOverridePoses(); - buildAbsolutePoses(); + buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true); } void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, @@ -1005,26 +1023,35 @@ void Rig::applyOverridePoses() { } } -void Rig::buildAbsolutePoses() { +// AJT: TODO: we should ALWAYS have the 180 flip. +// However currently matrices used for rendering require it. +// we need to find were the 180 is applied down the pipe and remove it, +// cluster matrices? render parts? +void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut, bool omitY180Flip) { if (!_animSkeleton) { return; } - ASSERT(_animSkeleton->getNumJoints() == (int)_relativePoses.size()); + ASSERT(_animSkeleton->getNumJoints() == (int)relativePoses.size()); - _absolutePoses.resize(_relativePoses.size()); - for (int i = 0; i < (int)_relativePoses.size(); i++) { + // flatten all poses out so they are absolute not relative + absolutePosesOut.resize(relativePoses.size()); + for (int i = 0; i < (int)relativePoses.size(); i++) { int parentIndex = _animSkeleton->getParentIndex(i); if (parentIndex == -1) { - _absolutePoses[i] = _relativePoses[i]; + absolutePosesOut[i] = relativePoses[i]; } else { - _absolutePoses[i] = _absolutePoses[parentIndex] * _relativePoses[i]; + absolutePosesOut[i] = absolutePosesOut[parentIndex] * relativePoses[i]; } } - AnimPose rootTransform(_modelOffset * _geometryOffset); + // transform all absolute poses into rig space. + AnimPose geometryToRigTransform(_geometryToRigTransform); + if (omitY180Flip) { + geometryToRigTransform = _modelOffset * _geometryOffset; + } for (int i = 0; i < (int)_absolutePoses.size(); i++) { - _absolutePoses[i] = rootTransform * _absolutePoses[i]; + absolutePosesOut[i] = geometryToRigTransform * absolutePosesOut[i]; } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 9428ae2c5e..b0d2af464a 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -88,7 +88,7 @@ public: int getJointStateCount() const; int indexOfJoint(const QString& jointName) const; - void setModelOffset(const glm::mat4& modelOffset); + void setModelOffset(const glm::mat4& modelOffsetMat); bool getJointStateRotation(int index, glm::quat& rotation) const; bool getJointStateTranslation(int index, glm::vec3& translation) const; @@ -147,6 +147,7 @@ public: const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } AnimPose getAbsoluteDefaultPose(int index) const; // avatar space + const AnimPoseVec& getAbsoluteDefaultPoses() const; // avatar space void copyJointsIntoJointData(QVector& jointDataVec) const; void copyJointsFromJointData(const QVector& jointDataVec); @@ -155,7 +156,7 @@ public: bool isValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); void applyOverridePoses(); - void buildAbsolutePoses(); + void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut, bool omitY180Flip = false); void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); @@ -173,6 +174,8 @@ public: AnimPoseVec _overridePoses; // geometry space relative to parent. std::vector _overrideFlags; + AnimPoseVec _absoluteDefaultPoses; // avatar space, not relative to parent. + glm::mat4 _geometryToRigTransform; glm::mat4 _rigToGeometryTransform; diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 078f1ecabc..8c0af768d1 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -349,17 +349,6 @@ void AnimDebugDraw::update() { // AJT: FIX ME /* - for (auto& iter : _skeletons) { - AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); - numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; - for (int i = 0; i < skeleton->getNumJoints(); i++) { - auto parentIndex = skeleton->getParentIndex(i); - if (parentIndex >= 0) { - numVerts += VERTICES_PER_LINK; - } - } - } - for (auto& iter : _animNodes) { AnimNode::ConstPointer& animNode = std::get<0>(iter.second); auto poses = animNode->getPosesInternal(); @@ -407,37 +396,6 @@ void AnimDebugDraw::update() { Vertex* verts = (Vertex*)data._vertexBuffer->editData(); Vertex* v = verts; - // AJT: fixme - // draw skeletons - /* - for (auto& iter : _skeletons) { - AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); - AnimPose rootPose = std::get<1>(iter.second); - int hipsIndex = skeleton->nameToJointIndex("Hips"); - if (hipsIndex >= 0) { - rootPose.trans -= skeleton->getRelativeBindPose(hipsIndex).trans; - } - glm::vec4 color = std::get<2>(iter.second); - - for (int i = 0; i < skeleton->getNumJoints(); i++) { - AnimPose pose = skeleton->getAbsoluteBindPose(i); - - const float radius = BONE_RADIUS / (pose.scale.x * rootPose.scale.x); - - // draw bone - addBone(rootPose, pose, radius, v); - - // draw link to parent - auto parentIndex = skeleton->getParentIndex(i); - if (parentIndex >= 0) { - assert(parentIndex < skeleton->getNumJoints()); - AnimPose parentPose = skeleton->getAbsoluteBindPose(parentIndex); - addLink(rootPose, pose, parentPose, radius, color, v); - } - } - } - */ - // AJT: FIXME /* // draw animNodes From e2f031e77ff7c99887f8773ff7a727068230e103 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 21 Nov 2015 10:57:39 -0800 Subject: [PATCH 61/84] AnimDebugDraw Removed unused drawing code. --- libraries/render-utils/src/AnimDebugDraw.cpp | 133 ------------------- libraries/render-utils/src/AnimDebugDraw.h | 17 --- 2 files changed, 150 deletions(-) diff --git a/libraries/render-utils/src/AnimDebugDraw.cpp b/libraries/render-utils/src/AnimDebugDraw.cpp index 8c0af768d1..d110576c0d 100644 --- a/libraries/render-utils/src/AnimDebugDraw.cpp +++ b/libraries/render-utils/src/AnimDebugDraw.cpp @@ -153,30 +153,6 @@ void AnimDebugDraw::shutdown() { } } -void AnimDebugDraw::addSkeleton(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color) { - _skeletons[key] = SkeletonInfo(skeleton, rootPose, color); -} - -void AnimDebugDraw::removeSkeleton(const std::string& key) { - _skeletons.erase(key); -} - -void AnimDebugDraw::addAnimNode(const std::string& key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color) { - _animNodes[key] = AnimNodeInfo(animNode, rootPose, color); -} - -void AnimDebugDraw::removeAnimNode(const std::string& key) { - _animNodes.erase(key); -} - -void AnimDebugDraw::addRelativePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color) { - _relativePoses[key] = PosesInfo(skeleton, poses, rootPose, color); -} - -void AnimDebugDraw::removeRelativePoses(const std::string& key) { - _relativePoses.erase(key); -} - void AnimDebugDraw::addAbsolutePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color) { _absolutePoses[key] = PosesInfo(skeleton, poses, rootPose, color); } @@ -347,33 +323,6 @@ void AnimDebugDraw::update() { // figure out how many verts we will need. int numVerts = 0; - // AJT: FIX ME - /* - for (auto& iter : _animNodes) { - AnimNode::ConstPointer& animNode = std::get<0>(iter.second); - auto poses = animNode->getPosesInternal(); - numVerts += poses.size() * VERTICES_PER_BONE; - auto skeleton = animNode->getSkeleton(); - for (size_t i = 0; i < poses.size(); i++) { - auto parentIndex = skeleton->getParentIndex(i); - if (parentIndex >= 0) { - numVerts += VERTICES_PER_LINK; - } - } - } - - for (auto& iter : _relativePoses) { - AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); - numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; - for (int i = 0; i < skeleton->getNumJoints(); i++) { - auto parentIndex = skeleton->getParentIndex(i); - if (parentIndex >= 0) { - numVerts += VERTICES_PER_LINK; - } - } - } - */ - for (auto& iter : _absolutePoses) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); numVerts += skeleton->getNumJoints() * VERTICES_PER_BONE; @@ -396,88 +345,6 @@ void AnimDebugDraw::update() { Vertex* verts = (Vertex*)data._vertexBuffer->editData(); Vertex* v = verts; - // AJT: FIXME - /* - // draw animNodes - for (auto& iter : _animNodes) { - AnimNode::ConstPointer& animNode = std::get<0>(iter.second); - AnimPose rootPose = std::get<1>(iter.second); - if (animNode->_skeleton) { - int hipsIndex = animNode->_skeleton->nameToJointIndex("Hips"); - if (hipsIndex >= 0) { - rootPose.trans -= animNode->_skeleton->getRelativeBindPose(hipsIndex).trans; - } - } - glm::vec4 color = std::get<2>(iter.second); - - auto poses = animNode->getPosesInternal(); - - auto skeleton = animNode->getSkeleton(); - - std::vector absAnimPose; - absAnimPose.resize(skeleton->getNumJoints()); - - for (size_t i = 0; i < poses.size(); i++) { - - auto parentIndex = skeleton->getParentIndex(i); - if (parentIndex >= 0) { - absAnimPose[i] = absAnimPose[parentIndex] * poses[i]; - } else { - absAnimPose[i] = poses[i]; - } - - const float radius = BONE_RADIUS / (absAnimPose[i].scale.x * rootPose.scale.x); - addBone(rootPose, absAnimPose[i], radius, v); - - if (parentIndex >= 0) { - assert((size_t)parentIndex < poses.size()); - // draw line to parent - addLink(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, color, v); - } - } - } - */ - - // AJT: FIX ME - /* - // draw relative poses - for (auto& iter : _relativePoses) { - AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); - AnimPoseVec& poses = std::get<1>(iter.second); - AnimPose rootPose = std::get<2>(iter.second); - int hipsIndex = skeleton->nameToJointIndex("Hips"); - if (hipsIndex >= 0) { - rootPose.trans -= skeleton->getRelativeBindPose(hipsIndex).trans; - } - glm::vec4 color = std::get<3>(iter.second); - - std::vector absAnimPose; - absAnimPose.resize(skeleton->getNumJoints()); - - for (int i = 0; i < skeleton->getNumJoints(); i++) { - const AnimPose& pose = poses[i]; - - const float radius = BONE_RADIUS / (pose.scale.x * rootPose.scale.x); - - auto parentIndex = skeleton->getParentIndex(i); - if (parentIndex >= 0) { - absAnimPose[i] = absAnimPose[parentIndex] * pose; - } else { - absAnimPose[i] = pose; - } - - // draw bone - addBone(rootPose, absAnimPose[i], radius, v); - - // draw link to parent - if (parentIndex >= 0) { - assert(parentIndex < skeleton->getNumJoints()); - addLink(rootPose, absAnimPose[i], absAnimPose[parentIndex], radius, color, v); - } - } - } - */ - // draw absolute poses for (auto& iter : _absolutePoses) { AnimSkeleton::ConstPointer& skeleton = std::get<0>(iter.second); diff --git a/libraries/render-utils/src/AnimDebugDraw.h b/libraries/render-utils/src/AnimDebugDraw.h index 9fcff2a16b..eb4621a880 100644 --- a/libraries/render-utils/src/AnimDebugDraw.h +++ b/libraries/render-utils/src/AnimDebugDraw.h @@ -29,18 +29,6 @@ public: void shutdown(); - // draw a skeleton bind pose - void addSkeleton(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPose& rootPose, const glm::vec4& color); - void removeSkeleton(const std::string& key); - - // draw the interal poses for a specific animNode - void addAnimNode(const std::string& key, AnimNode::ConstPointer animNode, const AnimPose& rootPose, const glm::vec4& color); - void removeAnimNode(const std::string& key); - - // draw a set of poses with a skeleton - void addRelativePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color); - void removeRelativePoses(const std::string& key); - void addAbsolutePoses(const std::string& key, AnimSkeleton::ConstPointer skeleton, const AnimPoseVec& poses, const AnimPose& rootPose, const glm::vec4& color); void removeAbsolutePoses(const std::string& key); @@ -53,13 +41,8 @@ protected: static gpu::PipelinePointer _pipeline; - typedef std::tuple SkeletonInfo; - typedef std::tuple AnimNodeInfo; typedef std::tuple PosesInfo; - std::unordered_map _skeletons; - std::unordered_map _animNodes; - std::unordered_map _relativePoses; std::unordered_map _absolutePoses; // no copies From 0b410ecd92096f5ae0334098377c031dc3a806b6 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sat, 21 Nov 2015 11:09:31 -0800 Subject: [PATCH 62/84] RigTests build again --- tests/animation/src/RigTests.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/tests/animation/src/RigTests.cpp b/tests/animation/src/RigTests.cpp index 3392f75476..e8d4c41dff 100644 --- a/tests/animation/src/RigTests.cpp +++ b/tests/animation/src/RigTests.cpp @@ -47,21 +47,24 @@ #include #include "RigTests.h" -static void reportJoint(int index, JointState joint) { // Handy for debugging +static void reportJoint(RigPointer rig, int index) { // Handy for debugging std::cout << "\n"; - std::cout << index << " " << joint.getName().toUtf8().data() << "\n"; - std::cout << " pos:" << joint.getPosition() << "/" << joint.getPositionInParentFrame() << " from " << joint.getParentIndex() << "\n"; - std::cout << " rot:" << safeEulerAngles(joint.getRotation()) << "/" << safeEulerAngles(joint.getRotationInParentFrame()) << "/" << safeEulerAngles(joint.getRotationInBindFrame()) << "\n"; + std::cout << index << " " << rig->getAnimSkeleton()->getJointName(index).toUtf8().data() << "\n"; + glm::vec3 pos; + rig->getJointPosition(index, pos); + glm::quat rot; + rig->getJointRotation(index, rot); + std::cout << " pos:" << pos << "\n"; + std::cout << " rot:" << safeEulerAngles(rot) << "\n"; std::cout << "\n"; } static void reportByName(RigPointer rig, const QString& name) { int jointIndex = rig->indexOfJoint(name); - reportJoint(jointIndex, rig->getJointState(jointIndex)); + reportJoint(rig, jointIndex); } static void reportAll(RigPointer rig) { for (int i = 0; i < rig->getJointStateCount(); i++) { - JointState joint = rig->getJointState(i); - reportJoint(i, joint); + reportJoint(rig, i); } } static void reportSome(RigPointer rig) { @@ -88,17 +91,11 @@ void RigTests::initTestCase() { #endif QVERIFY((bool)geometry); - QVector jointStates; - for (int i = 0; i < geometry->joints.size(); ++i) { - JointState state(geometry->joints[i]); - jointStates.append(state); - } - _rig = std::make_shared(); - _rig->initJointStates(jointStates, glm::mat4(), 0, 41, 40, 39, 17, 16, 15); // FIXME? get by name? do we really want to exclude the shoulder blades? + _rig->initJointStates(*geometry, glm::mat4()); std::cout << "Rig is ready " << geometry->joints.count() << " joints " << std::endl; reportAll(_rig); - } +} void RigTests::initialPoseArmsDown() { reportSome(_rig); From dc169dc0aa30c80138a1d6f62f13c4e825ee2f2b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 22 Nov 2015 16:15:12 -0800 Subject: [PATCH 63/84] Rig.h: updated comments with coordinate spaces --- libraries/animation/src/Rig.h | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index b0d2af464a..ca8db08d96 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -42,8 +42,8 @@ public: float torsoTwist = 0.0f; // degrees bool enableLean = false; glm::quat worldHeadOrientation = glm::quat(); // world space (-z forward) - glm::quat localHeadOrientation = glm::quat(); // avatar space (-z forward) - glm::vec3 localHeadPosition = glm::vec3(); // avatar space + glm::quat localHeadOrientation = glm::quat(); // rig space (-z forward) + glm::vec3 localHeadPosition = glm::vec3(); // rig space bool isInHMD = false; int leanJointIndex = -1; int neckJointIndex = -1; @@ -63,10 +63,10 @@ public: struct HandParameters { bool isLeftEnabled; bool isRightEnabled; - glm::vec3 leftPosition = glm::vec3(); // avatar space - glm::quat leftOrientation = glm::quat(); // avatar space (z forward) - glm::vec3 rightPosition = glm::vec3(); // avatar space - glm::quat rightOrientation = glm::quat(); // avatar space (z forward) + glm::vec3 leftPosition = glm::vec3(); // rig space + glm::quat leftOrientation = glm::quat(); // rig space (z forward) + glm::vec3 rightPosition = glm::vec3(); // rig space + glm::quat rightOrientation = glm::quat(); // rig space (z forward) float leftTrigger = 0.0f; float rightTrigger = 0.0f; }; @@ -90,8 +90,8 @@ public: void setModelOffset(const glm::mat4& modelOffsetMat); - bool getJointStateRotation(int index, glm::quat& rotation) const; - bool getJointStateTranslation(int index, glm::vec3& translation) const; + bool getJointStateRotation(int index, glm::quat& rotation) const; // geometry space + bool getJointStateTranslation(int index, glm::vec3& translation) const; // geometry space void clearJointState(int index); void clearJointStates(); @@ -146,8 +146,8 @@ public: const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } - AnimPose getAbsoluteDefaultPose(int index) const; // avatar space - const AnimPoseVec& getAbsoluteDefaultPoses() const; // avatar space + AnimPose getAbsoluteDefaultPose(int index) const; // rig space + const AnimPoseVec& getAbsoluteDefaultPoses() const; // rig space void copyJointsIntoJointData(QVector& jointDataVec) const; void copyJointsFromJointData(const QVector& jointDataVec); @@ -167,14 +167,15 @@ public: void computeEyesInRootFrame(const AnimPoseVec& poses); - AnimPose _modelOffset; // model to avatar space (without 180 flip) + AnimPose _modelOffset; // model to rig space (without 180 flip) AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets) + AnimPoseVec _relativePoses; // geometry space relative to parent. - AnimPoseVec _absolutePoses; // avatar space, not relative to parent. + AnimPoseVec _absolutePoses; // rig space, not relative to parent. (without 180 flip) AnimPoseVec _overridePoses; // geometry space relative to parent. std::vector _overrideFlags; - AnimPoseVec _absoluteDefaultPoses; // avatar space, not relative to parent. + AnimPoseVec _absoluteDefaultPoses; // rig space, not relative to parent. glm::mat4 _geometryToRigTransform; glm::mat4 _rigToGeometryTransform; From db21db3cbcffffce823b763353d92c82780c513f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Sun, 22 Nov 2015 16:31:26 -0800 Subject: [PATCH 64/84] Rig.h: more coordinate space comments --- libraries/animation/src/Rig.h | 53 ++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index ca8db08d96..ebb8a427c1 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -90,44 +90,85 @@ public: void setModelOffset(const glm::mat4& modelOffsetMat); - bool getJointStateRotation(int index, glm::quat& rotation) const; // geometry space - bool getJointStateTranslation(int index, glm::vec3& translation) const; // geometry space + // geometry space + bool getJointStateRotation(int index, glm::quat& rotation) const; + + // geometry space + bool getJointStateTranslation(int index, glm::vec3& translation) const; void clearJointState(int index); void clearJointStates(); void clearJointAnimationPriority(int index); + // geometry space void setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority); + + // geometry space void setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority); + + // geometry space void setJointRotation(int index, bool valid, const glm::quat& rotation, float priority); + // legacy void restoreJointRotation(int index, float fraction, float priority); void restoreJointTranslation(int index, float fraction, float priority); + + // if translation and rotation is identity, position will be in rig space bool getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const; + // rig space bool getJointPosition(int jointIndex, glm::vec3& position) const; + + // if rotation is identity, result will be in rig space bool getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const; + + // geometry space bool getJointRotation(int jointIndex, glm::quat& rotation) const; + + // geometry space bool getJointTranslation(int jointIndex, glm::vec3& translation) const; + + // legacy bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const; + + // rig space (without y 180 flip) glm::mat4 getJointTransform(int jointIndex) const; + // Start or stop animations as needed. void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation); + // Regardless of who started the animations or how many, update the joints. void updateAnimations(float deltaTime, glm::mat4 rootTransform); + + // legacy void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, const QVector& freeLineage, glm::mat4 rootTransform); + + // legacy bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage); + + // legacy float getLimbLength(int jointIndex, const QVector& freeLineage, const glm::vec3 scale, const QVector& fbxJoints) const; + // legacy glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority); + + // legacy glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex); + + // legacy glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, float mix = 1.0f); + + // legacy bool getJointRotationInConstrainedFrame(int jointIndex, glm::quat& rotOut) const; + + // legacy glm::quat getJointDefaultRotationInParentFrame(int jointIndex); + + // legacy void clearJointStatePriorities(); void updateFromHeadParameters(const HeadParameters& params, float dt); @@ -142,12 +183,16 @@ public: void removeAnimationStateHandler(QScriptValue handler); void animationStateHandlerResult(int identifier, QScriptValue result); + // rig space (without y 180 flip) bool getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const; const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } - AnimPose getAbsoluteDefaultPose(int index) const; // rig space - const AnimPoseVec& getAbsoluteDefaultPoses() const; // rig space + // rig space + AnimPose getAbsoluteDefaultPose(int index) const; + + // rig space + const AnimPoseVec& getAbsoluteDefaultPoses() const; void copyJointsIntoJointData(QVector& jointDataVec) const; void copyJointsFromJointData(const QVector& jointDataVec); From dd3f43705c9d5232c1038af08a3c8e0168f1271e Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 23 Nov 2015 11:06:11 -0800 Subject: [PATCH 65/84] Fix for Model Entity animations. Because rig->setJoint* methods take parameters in geometry space the animations preRotation has to be applied manually. --- libraries/entities/src/ModelEntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index b4adde7467..af9541ceca 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -224,6 +224,7 @@ void ModelEntityItem::getAnimationFrame(bool& newFrame, if (myAnimation && myAnimation->isLoaded()) { const QVector& frames = myAnimation->getFramesReference(); // NOTE: getFrames() is too heavy + auto& fbxJoints = myAnimation->getGeometry().joints; int frameCount = frames.size(); if (frameCount > 0) { @@ -244,7 +245,7 @@ void ModelEntityItem::getAnimationFrame(bool& newFrame, for (int j = 0; j < _jointMapping.size(); j++) { int index = _jointMapping[j]; if (index != -1 && index < rotations.size()) { - _lastKnownFrameDataRotations[j] = rotations[index]; + _lastKnownFrameDataRotations[j] = fbxJoints[index].preRotation * rotations[index]; } if (index != -1 && index < translations.size()) { _lastKnownFrameDataTranslations[j] = translations[index]; From 8252bbed9b7af30a903d4fa4d000fcfc8cbb3a51 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 23 Nov 2015 13:50:14 -0800 Subject: [PATCH 66/84] AnimSkeleton: added pre rotation accessor methods --- libraries/animation/src/AnimSkeleton.cpp | 4 ++++ libraries/animation/src/AnimSkeleton.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 55e56ae503..ad358709c2 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -76,6 +76,10 @@ const AnimPose& AnimSkeleton::getAbsoluteDefaultPose(int jointIndex) const { return _absoluteDefaultPoses[jointIndex]; } +const glm::quat AnimSkeleton::getPreRotation(int jointIndex) const { + return _joints[jointIndex].preRotation; +} + int AnimSkeleton::getParentIndex(int jointIndex) const { return _joints[jointIndex].parentIndex; } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 0e0b07a8fd..5e0a80d077 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -44,6 +44,9 @@ public: const AnimPose& getAbsoluteDefaultPose(int jointIndex) const; const AnimPoseVec& getAbsoluteDefaultPoses() const { return _absoluteDefaultPoses; } + // get pre-rotation aka Maya's joint orientation. + const glm::quat getPreRotation(int jointIndex) const; + int getParentIndex(int jointIndex) const; AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const; From 8f46b8a765525b90a6d0a300cf9de7497b4a8e47 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 23 Nov 2015 15:58:18 -0800 Subject: [PATCH 67/84] Added USE_PRE_ROT_FROM_ANIM option to AnimClip This will allow us in the future to pull preRotations from animations instead of the model skeleton. It is disabled by default because our current animations preRotations are not correct for the left hand. --- libraries/animation/src/AnimClip.cpp | 33 +++++++++++++++++----------- libraries/fbx/src/FBXReader.cpp | 7 ------ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 582a55cd62..c90d2ae6ce 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -91,41 +91,48 @@ void AnimClip::copyFromNetworkAnim() { // build a mapping from animation joint indices to skeleton joint indices. // by matching joints with the same name. const FBXGeometry& geom = _networkAnim->getGeometry(); - const QVector& animJoints = geom.joints; + AnimSkeleton animSkeleton(geom); + const int animJointCount = animSkeleton.getNumJoints(); + const int skeletonJointCount = _skeleton->getNumJoints(); std::vector jointMap; - const int animJointCount = animJoints.count(); jointMap.reserve(animJointCount); for (int i = 0; i < animJointCount; i++) { - int skeletonJoint = _skeleton->nameToJointIndex(animJoints.at(i).name); + int skeletonJoint = _skeleton->nameToJointIndex(animSkeleton.getJointName(i)); if (skeletonJoint == -1) { - qCWarning(animation) << "animation contains joint =" << animJoints.at(i).name << " which is not in the skeleton, url =" << _url; + qCWarning(animation) << "animation contains joint =" << animSkeleton.getJointName(i) << " which is not in the skeleton, url =" << _url; } jointMap.push_back(skeletonJoint); } const int frameCount = geom.animationFrames.size(); - const int skeletonJointCount = _skeleton->getNumJoints(); _anim.resize(frameCount); for (int frame = 0; frame < frameCount; frame++) { - // init all joints in animation to bind pose - // this will give us a resonable result for bones in the skeleton but not in the animation. + // init all joints in animation to default pose + // this will give us a resonable result for bones in the model skeleton but not in the animation. _anim[frame].reserve(skeletonJointCount); for (int skeletonJoint = 0; skeletonJoint < skeletonJointCount; skeletonJoint++) { - _anim[frame].push_back(_skeleton->getRelativeBindPose(skeletonJoint)); + _anim[frame].push_back(_skeleton->getRelativeDefaultPose(skeletonJoint)); } for (int animJoint = 0; animJoint < animJointCount; animJoint++) { - int skeletonJoint = jointMap[animJoint]; // skip joints that are in the animation but not in the skeleton. if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { const glm::vec3& fbxZeroTrans = geom.animationFrames[0].translations[animJoint]; - // AJT: TODO: use the actual preRotation not the bind pose here. - const AnimPose& jointOrientPose = _skeleton->getRelativeBindPose(skeletonJoint); +#ifdef USE_PRE_ROT_FROM_ANIM + // TODO: This is the correct way to apply the pre rotations from maya, however + // the current animation set has incorrect preRotations for the left wrist and thumb + // so it looks wrong if we enable this code. + glm::quat preRotation = animSkeleton.getPreRotation(animJoint); +#else + // TODO: This is legacy approach, this does not work when animations and models do not + // have the same set of pre rotations. For example when mixing maya models with blender animations. + glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot; +#endif const AnimPose& relDefaultPose = _skeleton->getRelativeDefaultPose(skeletonJoint); // used to adjust translation offsets, so large translation animatons on the reference skeleton @@ -135,8 +142,8 @@ void AnimClip::copyFromNetworkAnim() { AnimPose& pose = _anim[frame][skeletonJoint]; const FBXAnimationFrame& fbxAnimFrame = geom.animationFrames[frame]; - // rotation in fbxAnimationFrame is a delta from its jointOrientPose (aka preRotation) - pose.rot = jointOrientPose.rot * fbxAnimFrame.rotations[animJoint]; + // rotation in fbxAnimationFrame is a delta from its preRotation. + pose.rot = preRotation * fbxAnimFrame.rotations[animJoint]; // translation in fbxAnimationFrame is not a delta. // convert it into a delta by subtracting from the first frame. diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2d2b2c4b0a..1be3bbb5f6 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -750,13 +750,6 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS model.preRotation = glm::quat(glm::radians(preRotation)); model.rotation = glm::quat(glm::radians(rotation)); model.postRotation = glm::quat(glm::radians(postRotation)); - - if (geometry.applicationName.startsWith("Blender")) { - // blender puts the jointOffset in the wrong place. - model.preRotation = model.rotation; - model.rotation = glm::quat(); - } - model.postTransform = glm::translate(-rotationPivot) * glm::translate(scaleOffset) * glm::translate(scalePivot) * glm::scale(scale) * glm::translate(-scalePivot); // NOTE: angles from the FBX file are in degrees From 14189ac90950cbc8b0c7143127c25a7a57972693 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 23 Nov 2015 19:26:30 -0800 Subject: [PATCH 68/84] Move Y_180 flip rotation out of Rig This Y_180 flip is defined in skeletonModel not in the rig. This is important if we wish to use the Rig for both Avatars (180 flip) and Entity models (no 180 flip). We can hide this 180 flip from script, if we wish, by including it in all the accessors to and from MyAvatar -> skeletalModel -> Rig. Added Quaternions::Y_180 to GLMHelpers. --- interface/src/avatar/MyAvatar.cpp | 43 +++++++++----------------- interface/src/avatar/SkeletonModel.cpp | 32 ++++++++----------- libraries/animation/src/AnimClip.cpp | 2 +- libraries/animation/src/Rig.cpp | 31 ++++++------------- libraries/animation/src/Rig.h | 14 ++++----- libraries/shared/src/GLMHelpers.cpp | 1 + libraries/shared/src/GLMHelpers.h | 1 + 7 files changed, 48 insertions(+), 76 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0f0ce4bf60..3ee5f8b202 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1319,26 +1319,22 @@ void MyAvatar::preRender(RenderArgs* renderArgs) { auto animSkeleton = _rig->getAnimSkeleton(); + // the rig is in the skeletonModel frame + AnimPose xform(glm::vec3(1), _skeletonModel.getRotation(), _skeletonModel.getTranslation()); + if (_enableDebugDrawDefaultPose && animSkeleton) { - AnimPose xform(glm::vec3(1), getOrientation(), getPosition()); glm::vec4 gray(0.2f, 0.2f, 0.2f, 0.2f); AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarDefaultPoses", animSkeleton, _rig->getAbsoluteDefaultPoses(), xform, gray); } if (_enableDebugDrawAnimPose && animSkeleton) { - glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); - - // getJointTransforms are aligned such that z is forward, not -z. - // so they need a 180 flip - glm::quat rotY180 = glm::angleAxis((float)M_PI, glm::vec3(0.0f, 1.0f, 0.0f)); - AnimPose xform(glm::vec3(1), getOrientation() * rotY180, getPosition()); - // build absolute AnimPoseVec from rig AnimPoseVec absPoses; absPoses.reserve(_rig->getJointStateCount()); for (int i = 0; i < _rig->getJointStateCount(); i++) { absPoses.push_back(AnimPose(_rig->getJointTransform(i))); } + glm::vec4 cyan(0.1f, 0.6f, 0.6f, 1.0f); AnimDebugDraw::getInstance().addAbsolutePoses("myAvatarAnimPoses", animSkeleton, absPoses, xform, cyan); } } @@ -1791,42 +1787,33 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { const glm::quat hmdOrientation = getHMDSensorOrientation(); const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); - /* - const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); - const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); - const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f); - const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.0f, 0.0f); - */ - // 2 meter tall dude - const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.9f, 0.0f); - const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.9f, 0.0f); - const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f); - const glm::vec3 DEFAULT_HIPS_POS(0.0f, 1.05f, 0.0f); + const glm::vec3 DEFAULT_RIG_MIDDLE_EYE_POS(0.0f, 1.9f, 0.0f); + const glm::vec3 DEFAULT_RIG_NECK_POS(0.0f, 1.70f, 0.0f); + const glm::vec3 DEFAULT_RIG_HIPS_POS(0.0f, 1.05f, 0.0f); int rightEyeIndex = _rig->indexOfJoint("RightEye"); int leftEyeIndex = _rig->indexOfJoint("LeftEye"); int neckIndex = _rig->indexOfJoint("Neck"); int hipsIndex = _rig->indexOfJoint("Hips"); - glm::vec3 absRightEyePos = rightEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(rightEyeIndex).trans : DEFAULT_RIGHT_EYE_POS; - glm::vec3 absLeftEyePos = leftEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_LEFT_EYE_POS; - glm::vec3 absNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_NECK_POS; - glm::vec3 absHipsPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans : DEFAULT_HIPS_POS; + glm::vec3 rigMiddleEyePos = leftEyeIndex != -1 ? _rig->getAbsoluteDefaultPose(leftEyeIndex).trans : DEFAULT_RIG_MIDDLE_EYE_POS; + glm::vec3 rigNeckPos = neckIndex != -1 ? _rig->getAbsoluteDefaultPose(neckIndex).trans : DEFAULT_RIG_NECK_POS; + glm::vec3 rigHipsPos = hipsIndex != -1 ? _rig->getAbsoluteDefaultPose(hipsIndex).trans : DEFAULT_RIG_HIPS_POS; - glm::vec3 localEyes = (((absRightEyePos + absLeftEyePos) / 2.0f) - absHipsPos); - glm::vec3 localNeck = (absNeckPos - absHipsPos); + glm::vec3 localEyes = (rigMiddleEyePos - rigHipsPos); + glm::vec3 localNeck = (rigNeckPos - rigHipsPos); // apply simplistic head/neck model // figure out where the avatar body should be by applying offsets from the avatar's neck & head joints. // eyeToNeck offset is relative full HMD orientation. // while neckToRoot offset is only relative to HMDs yaw. - glm::vec3 eyeToNeck = hmdOrientation * (localNeck - localEyes); - glm::vec3 neckToRoot = hmdOrientationYawOnly * -localNeck; + // Y_180 is necessary because rig is z forward and hmdOrientation is -z forward + glm::vec3 eyeToNeck = hmdOrientation * Quaternions::Y_180 * (localNeck - localEyes); + glm::vec3 neckToRoot = hmdOrientationYawOnly * Quaternions::Y_180 * -localNeck; glm::vec3 bodyPos = hmdPosition + eyeToNeck + neckToRoot; - // avatar facing is determined solely by hmd orientation. return createMatFromQuatAndPos(hmdOrientationYawOnly, bodyPos); } #endif diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 9d2c8c8aa2..8a9e47be5d 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -59,7 +59,6 @@ void SkeletonModel::initJointStates() { getJointPosition(rootJointIndex, rootModelPosition); _defaultEyeModelPosition = midEyePosition - rootModelPosition; - _defaultEyeModelPosition.z = -_defaultEyeModelPosition.z; // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; @@ -91,25 +90,20 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { if (qApp->getAvatarUpdater()->isHMDMode()) { headParams.isInHMD = true; - // get HMD position from sensor space into world space, and back into model space - AnimPose avatarToWorld(glm::vec3(1.0f), myAvatar->getOrientation(), myAvatar->getPosition()); - glm::mat4 worldToAvatar = glm::inverse((glm::mat4)avatarToWorld); + // get HMD position from sensor space into world space, and back into rig space glm::mat4 worldHMDMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - glm::mat4 hmdMat = worldToAvatar * worldHMDMat; - - // in local avatar space (i.e. relative to avatar pos/orientation.) - glm::vec3 hmdPosition = extractTranslation(hmdMat); - glm::quat hmdOrientation = extractRotation(hmdMat); - - headParams.localHeadPosition = hmdPosition; - headParams.localHeadOrientation = hmdOrientation; + glm::mat4 rigToWorld = createMatFromQuatAndPos(getRotation(), getTranslation()); + glm::mat4 worldToRig = glm::inverse(rigToWorld); + glm::mat4 rigHMDMat = worldToRig * worldHMDMat; + headParams.rigHeadPosition = extractTranslation(rigHMDMat); + headParams.rigHeadOrientation = extractRotation(rigHMDMat); headParams.worldHeadOrientation = extractRotation(worldHMDMat); } else { headParams.isInHMD = false; // We don't have a valid localHeadPosition. - headParams.localHeadOrientation = head->getFinalOrientationInLocalFrame(); + headParams.rigHeadOrientation = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame(); headParams.worldHeadOrientation = head->getFinalOrientationInWorldFrame(); } @@ -124,8 +118,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto leftPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand); if (leftPalm.isValid() && leftPalm.isActive()) { handParams.isLeftEnabled = true; - handParams.leftPosition = leftPalm.getRawPosition(); - handParams.leftOrientation = leftPalm.getRawRotation(); + handParams.leftPosition = Quaternions::Y_180 * leftPalm.getRawPosition(); + handParams.leftOrientation = Quaternions::Y_180 * leftPalm.getRawRotation(); handParams.leftTrigger = leftPalm.getTrigger(); } else { handParams.isLeftEnabled = false; @@ -134,8 +128,8 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { auto rightPalm = myAvatar->getHand()->getCopyOfPalmData(HandData::RightHand); if (rightPalm.isValid() && rightPalm.isActive()) { handParams.isRightEnabled = true; - handParams.rightPosition = rightPalm.getRawPosition(); - handParams.rightOrientation = rightPalm.getRawRotation(); + handParams.rightPosition = Quaternions::Y_180 * rightPalm.getRawPosition(); + handParams.rightOrientation = Quaternions::Y_180 * rightPalm.getRawRotation(); handParams.rightTrigger = rightPalm.getTrigger(); } else { handParams.isRightEnabled = false; @@ -144,6 +138,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromHandParameters(handParams, deltaTime); _rig->computeMotionAnimationState(deltaTime, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation()); + // evaluate AnimGraph animation and update jointStates. Model::updateRig(deltaTime, parentTransform); @@ -198,8 +193,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { void SkeletonModel::updateAttitude() { setTranslation(_owningAvatar->getSkeletonPosition()); - static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - setRotation(_owningAvatar->getOrientation() * refOrientation); + setRotation(_owningAvatar->getOrientation() * Quaternions::Y_180); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale()); } diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index c90d2ae6ce..9eed3ad14c 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -129,7 +129,7 @@ void AnimClip::copyFromNetworkAnim() { // so it looks wrong if we enable this code. glm::quat preRotation = animSkeleton.getPreRotation(animJoint); #else - // TODO: This is legacy approach, this does not work when animations and models do not + // TODO: This is the legacy approach, this does not work when animations and models do not // have the same set of pre rotations. For example when mixing maya models with blender animations. glm::quat preRotation = _skeleton->getRelativeBindPose(skeletonJoint).rot; #endif diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index e61f56ba78..997a0ad9ae 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -182,7 +182,7 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff _relativePoses.clear(); _relativePoses = _animSkeleton->getRelativeDefaultPoses(); - buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true); + buildAbsoluteRigPoses(_relativePoses, _absolutePoses); _overridePoses.clear(); _overridePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -210,7 +210,7 @@ void Rig::reset(const FBXGeometry& geometry) { _relativePoses.clear(); _relativePoses = _animSkeleton->getRelativeDefaultPoses(); - buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true); + buildAbsoluteRigPoses(_relativePoses, _absolutePoses); _overridePoses.clear(); _overridePoses = _animSkeleton->getRelativeDefaultPoses(); @@ -258,8 +258,7 @@ void Rig::setModelOffset(const glm::mat4& modelOffsetMat) { _modelOffset = newModelOffset; // compute geometryToAvatarTransforms - glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - _geometryToRigTransform = AnimPose(glm::vec3(1), yFlip180, glm::vec3()) * _modelOffset * _geometryOffset; + _geometryToRigTransform = _modelOffset * _geometryOffset; _rigToGeometryTransform = glm::inverse(_geometryToRigTransform); // rebuild cached default poses @@ -343,22 +342,18 @@ void Rig::restoreJointTranslation(int index, float fraction, float priority) { ASSERT(false); } -// AJT: NOTE old code did not have 180 flip! bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { if (isValid(jointIndex)) { - glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - position = (rotation * yFlip180 * _absolutePoses[jointIndex].trans) + translation; + position = (rotation * _absolutePoses[jointIndex].trans) + translation; return true; } else { return false; } } -// AJT: NOTE old code did not have 180 flip! bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { if (isValid(jointIndex)) { - glm::quat yFlip180 = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - position = yFlip180 * _absolutePoses[jointIndex].trans; + position = _absolutePoses[jointIndex].trans; return true; } else { return false; @@ -737,7 +732,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { } applyOverridePoses(); - buildAbsoluteRigPoses(_relativePoses, _absolutePoses, true); + buildAbsoluteRigPoses(_relativePoses, _absolutePoses); } void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, @@ -859,7 +854,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { glm::vec3 headPos, neckPos; glm::quat headRot, neckRot; - AnimPose hmdPose(glm::vec3(1.0f), params.localHeadOrientation * yFlip180, params.localHeadPosition); + AnimPose hmdPose(glm::vec3(1.0f), params.rigHeadOrientation * yFlip180, params.rigHeadPosition); computeHeadNeckAnimVars(hmdPose, headPos, headRot, neckPos, neckRot); // debug rendering @@ -885,7 +880,7 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { } else { _animVars.unset("headPosition"); - _animVars.set("headRotation", params.localHeadOrientation * yFlip180); + _animVars.set("headRotation", params.rigHeadOrientation * yFlip180); _animVars.set("headAndNeckType", (int)IKTarget::Type::RotationOnly); _animVars.set("headType", (int)IKTarget::Type::RotationOnly); _animVars.unset("neckPosition"); @@ -1023,11 +1018,7 @@ void Rig::applyOverridePoses() { } } -// AJT: TODO: we should ALWAYS have the 180 flip. -// However currently matrices used for rendering require it. -// we need to find were the 180 is applied down the pipe and remove it, -// cluster matrices? render parts? -void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut, bool omitY180Flip) { +void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut) { if (!_animSkeleton) { return; } @@ -1047,9 +1038,7 @@ void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& a // transform all absolute poses into rig space. AnimPose geometryToRigTransform(_geometryToRigTransform); - if (omitY180Flip) { - geometryToRigTransform = _modelOffset * _geometryOffset; - } + geometryToRigTransform = _modelOffset * _geometryOffset; for (int i = 0; i < (int)_absolutePoses.size(); i++) { absolutePosesOut[i] = geometryToRigTransform * absolutePosesOut[i]; } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index ebb8a427c1..df97d9bc66 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -42,8 +42,8 @@ public: float torsoTwist = 0.0f; // degrees bool enableLean = false; glm::quat worldHeadOrientation = glm::quat(); // world space (-z forward) - glm::quat localHeadOrientation = glm::quat(); // rig space (-z forward) - glm::vec3 localHeadPosition = glm::vec3(); // rig space + glm::quat rigHeadOrientation = glm::quat(); // rig space (-z forward) + glm::vec3 rigHeadPosition = glm::vec3(); // rig space bool isInHMD = false; int leanJointIndex = -1; int neckJointIndex = -1; @@ -132,7 +132,7 @@ public: // legacy bool getJointCombinedRotation(int jointIndex, glm::quat& result, const glm::quat& rotation) const; - // rig space (without y 180 flip) + // rig space glm::mat4 getJointTransform(int jointIndex) const; // Start or stop animations as needed. @@ -183,7 +183,7 @@ public: void removeAnimationStateHandler(QScriptValue handler); void animationStateHandlerResult(int identifier, QScriptValue result); - // rig space (without y 180 flip) + // rig space bool getModelRegistrationPoint(glm::vec3& modelRegistrationPointOut) const; const glm::vec3& getEyesInRootFrame() const { return _eyesInRootFrame; } @@ -201,7 +201,7 @@ public: bool isValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); void applyOverridePoses(); - void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut, bool omitY180Flip = false); + void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut); void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist); void updateNeckJoint(int index, const HeadParameters& params); @@ -212,11 +212,11 @@ public: void computeEyesInRootFrame(const AnimPoseVec& poses); - AnimPose _modelOffset; // model to rig space (without 180 flip) + AnimPose _modelOffset; // model to rig space AnimPose _geometryOffset; // geometry to model space (includes unit offset & fst offsets) AnimPoseVec _relativePoses; // geometry space relative to parent. - AnimPoseVec _absolutePoses; // rig space, not relative to parent. (without 180 flip) + AnimPoseVec _absolutePoses; // rig space, not relative to parent. AnimPoseVec _overridePoses; // geometry space relative to parent. std::vector _overrideFlags; diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 2199fefbce..7c5ba6bb48 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -34,6 +34,7 @@ const vec3& Vectors::UP = Vectors::UNIT_Y; const vec3& Vectors::FRONT = Vectors::UNIT_NEG_Z; const quat Quaternions::IDENTITY{ 1.0f, 0.0f, 0.0f, 0.0f }; +const quat Quaternions::Y_180{ 0.0f, 0.0f, 1.0f, 0.0f }; // Safe version of glm::mix; based on the code in Nick Bobick's article, // http://www.gamasutra.com/features/19980703/quaternions_01.htm (via Clyde, diff --git a/libraries/shared/src/GLMHelpers.h b/libraries/shared/src/GLMHelpers.h index 54d422e465..7daf1b7e43 100644 --- a/libraries/shared/src/GLMHelpers.h +++ b/libraries/shared/src/GLMHelpers.h @@ -58,6 +58,7 @@ glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); class Quaternions { public: static const quat IDENTITY; + static const quat Y_180; }; class Vectors { From 7f7d05fc42dce4f442eb9a7fbd81c69e91bbda9d Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 24 Nov 2015 10:46:35 -0600 Subject: [PATCH 69/84] fix double initialization of AssetClient in Agent --- assignment-client/src/Agent.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index a16e28df99..13904e6b75 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -52,14 +52,6 @@ Agent::Agent(NLPacket& packet) : DEFAULT_WINDOW_SECONDS_FOR_DESIRED_REDUCTION, false)) { DependencyManager::get()->setPacketSender(&_entityEditSender); - - auto assetClient = DependencyManager::set(); - - QThread* assetThread = new QThread; - assetThread->setObjectName("Asset Thread"); - assetClient->moveToThread(assetThread); - connect(assetThread, &QThread::started, assetClient.data(), &AssetClient::init); - assetThread->start(); auto assetClient = DependencyManager::set(); From 76034d62d578e6c442fbd9e8fd7155200e2bd807 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 24 Nov 2015 16:57:26 -0800 Subject: [PATCH 70/84] Eye tracking fixes --- interface/src/avatar/SkeletonModel.cpp | 7 +--- libraries/animation/src/Rig.cpp | 54 ++++++++++++-------------- libraries/animation/src/Rig.h | 2 +- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8a9e47be5d..376937fb13 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -153,11 +153,6 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { _rig->updateFromEyeParameters(eyeParams); - /* AJT: FIXME this will likely break eye tracking... - // rebuild the jointState transform for the eyes only. Must be after updateRig. - _rig->updateJointState(eyeParams.leftEyeJointIndex, parentTransform); - _rig->updateJointState(eyeParams.rightEyeJointIndex, parentTransform); - */ } else { Model::updateRig(deltaTime, parentTransform); @@ -171,6 +166,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // However, in the !isLookingAtMe case, the eyes aren't rotating the way they should right now. // We will revisit that as priorities allow, and particularly after the new rig/animation/joints. const FBXGeometry& geometry = _geometry->getFBXGeometry(); + // If the head is not positioned, updateEyeJoints won't get the math right glm::quat headOrientation; _rig->getJointRotation(geometry.headJointIndex, headOrientation); @@ -187,6 +183,7 @@ void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.modelTranslation = getTranslation(); eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + _rig->updateFromEyeParameters(eyeParams); } } diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 80a9dc9d53..8cd57cd7f1 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -267,7 +267,7 @@ void Rig::setModelOffset(const glm::mat4& modelOffsetMat) { } bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { - if (isValid(index)) { + if (isIndexValid(index)) { rotation = _relativePoses[index].rot; return !isEqual(rotation, _animSkeleton->getRelativeDefaultPose(index).rot); } else { @@ -276,7 +276,7 @@ bool Rig::getJointStateRotation(int index, glm::quat& rotation) const { } bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { - if (isValid(index)) { + if (isIndexValid(index)) { translation = _relativePoses[index].trans; return !isEqual(translation, _animSkeleton->getRelativeDefaultPose(index).trans); } else { @@ -285,7 +285,7 @@ bool Rig::getJointStateTranslation(int index, glm::vec3& translation) const { } void Rig::clearJointState(int index) { - if (isValid(index)) { + if (isIndexValid(index)) { _overrideFlags[index] = false; } } @@ -296,13 +296,13 @@ void Rig::clearJointStates() { } void Rig::clearJointAnimationPriority(int index) { - if (isValid(index)) { + if (isIndexValid(index)) { _overrideFlags[index] = false; } } void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translation, float priority) { - if (isValid(index)) { + if (isIndexValid(index)) { if (valid) { assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; @@ -312,7 +312,7 @@ void Rig::setJointTranslation(int index, bool valid, const glm::vec3& translatio } void Rig::setJointState(int index, bool valid, const glm::quat& rotation, const glm::vec3& translation, float priority) { - if (isValid(index)) { + if (isIndexValid(index)) { assert(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; _overridePoses[index].rot = rotation; @@ -323,7 +323,7 @@ void Rig::setJointState(int index, bool valid, const glm::quat& rotation, const // Deprecated. // WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation. void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { - if (isValid(index)) { + if (isIndexValid(index)) { if (valid) { ASSERT(_overrideFlags.size() == _overridePoses.size()); _overrideFlags[index] = true; @@ -343,7 +343,7 @@ void Rig::restoreJointTranslation(int index, float fraction, float priority) { } bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm::vec3 translation, glm::quat rotation) const { - if (isValid(jointIndex)) { + if (isIndexValid(jointIndex)) { position = (rotation * _absolutePoses[jointIndex].trans) + translation; return true; } else { @@ -352,7 +352,7 @@ bool Rig::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position, glm: } bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { - if (isValid(jointIndex)) { + if (isIndexValid(jointIndex)) { position = _absolutePoses[jointIndex].trans; return true; } else { @@ -361,7 +361,7 @@ bool Rig::getJointPosition(int jointIndex, glm::vec3& position) const { } bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const glm::quat& rotation) const { - if (isValid(jointIndex)) { + if (isIndexValid(jointIndex)) { result = rotation * _absolutePoses[jointIndex].rot; return true; } else { @@ -372,7 +372,7 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const // Deprecated. // WARNING: this is not symmetric with setJointRotation. It's historical. Use the appropriate specific variation. bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { - if (isValid(jointIndex)) { + if (isIndexValid(jointIndex)) { rotation = _relativePoses[jointIndex].rot; return true; } else { @@ -381,7 +381,7 @@ bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { } bool Rig::getJointTranslation(int jointIndex, glm::vec3& translation) const { - if (isValid(jointIndex)) { + if (isIndexValid(jointIndex)) { translation = _relativePoses[jointIndex].trans; return true; } else { @@ -804,7 +804,7 @@ static const glm::vec3 Y_AXIS(0.0f, 1.0f, 0.0f); static const glm::vec3 Z_AXIS(0.0f, 0.0f, 1.0f); void Rig::updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist) { - if (isValid(index)) { + if (isIndexValid(index)) { glm::quat absRot = (glm::angleAxis(-RADIANS_PER_DEGREE * leanSideways, Z_AXIS) * glm::angleAxis(-RADIANS_PER_DEGREE * leanForward, X_AXIS) * glm::angleAxis(RADIANS_PER_DEGREE * torsoTwist, Y_AXIS)); @@ -891,25 +891,19 @@ void Rig::updateNeckJoint(int index, const HeadParameters& params) { } void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm::quat& modelRotation, const glm::quat& worldHeadOrientation, const glm::vec3& lookAtSpot, const glm::vec3& saccade) { - // AJT: TODO: fix eye tracking! - /* - if (index >= 0 && _jointStates[index].getParentIndex() >= 0) { - auto& state = _jointStates[index]; - auto& parentState = _jointStates[state.getParentIndex()]; + if (isIndexValid(index)) { + glm::mat4 rigToWorld = createMatFromQuatAndPos(modelRotation, modelTranslation); + glm::mat4 worldToRig = glm::inverse(rigToWorld); + glm::vec3 zAxis = glm::normalize(_absolutePoses[index].trans - transformPoint(worldToRig, lookAtSpot)); + glm::quat q = rotationBetween(IDENTITY_FRONT, zAxis); - // NOTE: at the moment we do the math in the world-frame, hence the inverse transform is more complex than usual. - glm::mat4 inverse = glm::inverse(glm::mat4_cast(modelRotation) * parentState.getTransform() * - glm::translate(state.getUnscaledDefaultTranslation()) * - state.getPreTransform() * glm::mat4_cast(state.getPreRotation() * state.getDefaultRotation())); - glm::vec3 front = glm::vec3(inverse * glm::vec4(worldHeadOrientation * IDENTITY_FRONT, 0.0f)); - glm::vec3 lookAtDelta = lookAtSpot - modelTranslation; - glm::vec3 lookAt = glm::vec3(inverse * glm::vec4(lookAtDelta + glm::length(lookAtDelta) * saccade, 1.0f)); - glm::quat between = rotationBetween(front, lookAt); + // limit rotation const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - state.setRotationInConstrainedFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - state.getDefaultRotation(), DEFAULT_PRIORITY); + q = glm::angleAxis(glm::clamp(glm::angle(q), -MAX_ANGLE, MAX_ANGLE), glm::axis(q)); + + // directly set absolutePose rotation + _absolutePoses[index].rot = q; } - */ } void Rig::updateFromHandParameters(const HandParameters& params, float dt) { @@ -1043,7 +1037,7 @@ void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& a } glm::mat4 Rig::getJointTransform(int jointIndex) const { - if (isValid(jointIndex)) { + if (isIndexValid(jointIndex)) { return _absolutePoses[jointIndex]; } else { return glm::mat4(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index df97d9bc66..f1d7395f30 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -198,7 +198,7 @@ public: void copyJointsFromJointData(const QVector& jointDataVec); protected: - bool isValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } + bool isIndexValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); void applyOverridePoses(); void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut); From 3ae082f09c1f12c70b9be5c59a8b5085e3430838 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 24 Nov 2015 20:28:39 -0800 Subject: [PATCH 71/84] compute bounding capsule of avatars --- interface/src/avatar/SkeletonModel.cpp | 112 +----------------- .../animation/src/AnimInverseKinematics.h | 6 +- libraries/animation/src/AnimSkeleton.cpp | 10 ++ libraries/animation/src/AnimSkeleton.h | 2 + libraries/animation/src/Rig.cpp | 105 +++++++++++++++- libraries/animation/src/Rig.h | 2 + 6 files changed, 125 insertions(+), 112 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8a9e47be5d..bce3426bf7 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -347,114 +347,10 @@ void SkeletonModel::computeBoundingShape() { return; } - /* - AJT: TODO HACK DISABLED FIXME - - // BOUNDING SHAPE HACK: before we measure the bounds of the joints we use IK to put the - // hands and feet into positions that are more correct than the default pose. - - // Measure limb lengths so we can specify IK targets that will pull hands and feet tight to body - QVector endEffectors; - endEffectors.push_back("RightHand"); - endEffectors.push_back("LeftHand"); - endEffectors.push_back("RightFoot"); - endEffectors.push_back("LeftFoot"); - - QVector baseJoints; - baseJoints.push_back("RightArm"); - baseJoints.push_back("LeftArm"); - baseJoints.push_back("RightUpLeg"); - baseJoints.push_back("LeftUpLeg"); - - for (int i = 0; i < endEffectors.size(); ++i) { - QString tipName = endEffectors[i]; - QString baseName = baseJoints[i]; - float limbLength = 0.0f; - int tipIndex = _rig->indexOfJoint(tipName); - if (tipIndex == -1) { - continue; - } - // save tip's relative rotation for later - glm::quat tipRotation = _rig->getJointState(tipIndex).getRotationInConstrainedFrame(); - - // IK on each endpoint - int jointIndex = tipIndex; - QVector freeLineage; - float priority = 1.0f; - while (jointIndex > -1) { - JointState limbJoint = _rig->getJointState(jointIndex); - freeLineage.push_back(jointIndex); - if (limbJoint.getName() == baseName) { - glm::vec3 targetPosition = limbJoint.getPosition() - glm::vec3(0.0f, 1.5f * limbLength, 0.0f); - // do IK a few times to make sure the endpoint gets close to its target - for (int j = 0; j < 5; ++j) { - _rig->inverseKinematics(tipIndex, - targetPosition, - glm::quat(), - priority, - freeLineage, - glm::mat4()); - } - break; - } - limbLength += limbJoint.getDistanceToParent(); - jointIndex = limbJoint.getParentIndex(); - } - - // since this IK target is totally bogus we restore the tip's relative rotation - _rig->setJointRotationInConstrainedFrame(tipIndex, tipRotation, priority); - } - - // recompute all joint model-frame transforms - glm::mat4 rootTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; - for (int i = 0; i < _rig->getJointStateCount(); i++) { - _rig->updateJointState(i, rootTransform); - } - // END BOUNDING SHAPE HACK - - // compute bounding box that encloses all shapes - Extents totalExtents; - totalExtents.reset(); - totalExtents.addPoint(glm::vec3(0.0f)); - int numStates = _rig->getJointStateCount(); - for (int i = 0; i < numStates; i++) { - // Each joint contributes a capsule defined by FBXJoint.shapeInfo. - // For totalExtents we use the capsule endpoints expanded by the radius. - const JointState& state = _rig->getJointState(i); - const glm::mat4& jointTransform = state.getTransform(); - const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo; - if (shapeInfo.points.size() > 0) { - for (int j = 0; j < shapeInfo.points.size(); ++j) { - totalExtents.addPoint(extractTranslation(jointTransform * glm::translate(shapeInfo.points[j]))); - } - } - // HACK so that default legless robot doesn't knuckle-drag - if (shapeInfo.points.size() == 0 && (state.getName() == "LeftFoot" || state.getName() == "RightFoot")) { - totalExtents.addPoint(extractTranslation(jointTransform)); - } - } - - // compute bounding shape parameters - // NOTE: we assume that the longest side of totalExtents is the yAxis... - glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; - // ... and assume the radius is half the RMS of the X and Z sides: - _boundingCapsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - _boundingCapsuleHeight = diagonal.y - 2.0f * _boundingCapsuleRadius; - - glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition(); - _boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition; - - // RECOVER FROM BOUNINDG SHAPE HACK: now that we're all done, restore the default pose - for (int i = 0; i < numStates; i++) { - _rig->restoreJointRotation(i, 1.0f, 1.0f); - _rig->restoreJointTranslation(i, 1.0f, 1.0f); - } - */ - - // AJT: REMOVE HARDCODED BOUNDING VOLUME - _boundingCapsuleRadius = 0.3f; - _boundingCapsuleHeight = 1.3f; - _boundingCapsuleLocalOffset = glm::vec3(0.0f, -0.25f, 0.0f); + _rig->computeAvatarBoundingCapsule(geometry, + _boundingCapsuleRadius, + _boundingCapsuleHeight, + _boundingCapsuleLocalOffset); } void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha) { diff --git a/libraries/animation/src/AnimInverseKinematics.h b/libraries/animation/src/AnimInverseKinematics.h index f8f7fd9e9e..971d2d5ff1 100644 --- a/libraries/animation/src/AnimInverseKinematics.h +++ b/libraries/animation/src/AnimInverseKinematics.h @@ -54,9 +54,9 @@ protected: AnimInverseKinematics& operator=(const AnimInverseKinematics&) = delete; struct IKTargetVar { - IKTargetVar(const QString& jointNameIn, - const QString& positionVarIn, - const QString& rotationVarIn, + IKTargetVar(const QString& jointNameIn, + const QString& positionVarIn, + const QString& rotationVarIn, const QString& typeVarIn) : positionVar(positionVarIn), rotationVar(rotationVarIn), diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index ad358709c2..ded8c19671 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -96,6 +96,16 @@ AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses) } } +void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { + // poses start off relative and leave in absolute frame + for (int i = 0; i < poses.size() && i < _joints.size(); ++i) { + int parentIndex = _joints[i].parentIndex; + if (parentIndex != -1) { + poses[i] = poses[parentIndex] * poses[i]; + } + } +} + void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) { _joints = joints; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 5e0a80d077..aee9dcda21 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -51,6 +51,8 @@ public: AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& poses) const; + void convertRelativePosesToAbsolute(AnimPoseVec& poses) const; + #ifndef NDEBUG void dump() const; void dump(const AnimPoseVec& poses) const; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 997a0ad9ae..17dd43cd30 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -19,8 +19,9 @@ #include #include "AnimationLogging.h" -#include "AnimSkeleton.h" #include "AnimClip.h" +#include "AnimInverseKinematics.h" +#include "AnimSkeleton.h" #include "IKTarget.h" static bool isEqual(const glm::vec3& u, const glm::vec3& v) { @@ -1073,3 +1074,105 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { setJointTranslation(i, data.translationSet, invGeometryOffset * data.translation, 1.0f); } } + +void Rig::computeAvatarBoundingCapsule( + const FBXGeometry& geometry, + float& radiusOut, + float& heightOut, + glm::vec3& localOffsetOut) const { + if (! _animSkeleton) { + const float DEFAULT_AVATAR_CAPSULE_RADIUS = 0.3f; + const float DEFAULT_AVATAR_CAPSULE_HEIGHT = 1.3f; + const glm::vec3 DEFAULT_AVATAR_CAPSULE_LOCAL_OFFSET = glm::vec3(0.0f, -0.25f, 0.0f); + radiusOut = DEFAULT_AVATAR_CAPSULE_RADIUS; + heightOut = DEFAULT_AVATAR_CAPSULE_HEIGHT; + localOffsetOut = DEFAULT_AVATAR_CAPSULE_LOCAL_OFFSET; + return; + } + + AnimInverseKinematics ikNode("boundingShape"); + ikNode.setSkeleton(_animSkeleton); + ikNode.setTargetVars("LeftHand", + "leftHandPosition", + "leftHandRotation", + "leftHandType"); + ikNode.setTargetVars("RightHand", + "rightHandPosition", + "rightHandRotation", + "rightHandType"); + ikNode.setTargetVars("LeftFoot", + "leftFootPosition", + "leftFootRotation", + "leftFootType"); + ikNode.setTargetVars("RightFoot", + "rightFootPosition", + "rightFootRotation", + "rightFootType"); + + AnimPose geometryToRig = _modelOffset * _geometryOffset; + + AnimPose hips = geometryToRig * _animSkeleton->getRootAbsoluteBindPoseByChildName("LeftHand"); + AnimVariantMap animVars; + glm::quat handRotation = glm::angleAxis(PI, Vectors::UNIT_X); + animVars.set("leftHandPosition", hips.trans); + animVars.set("leftHandRotation", handRotation); + animVars.set("leftHandType", (int)IKTarget::Type::RotationAndPosition); + animVars.set("rightHandPosition", hips.trans); + animVars.set("rightHandRotation", handRotation); + animVars.set("rightHandType", (int)IKTarget::Type::RotationAndPosition); + + int rightFootIndex = _animSkeleton->nameToJointIndex("RightFoot"); + int leftFootIndex = _animSkeleton->nameToJointIndex("LeftFoot"); + if (rightFootIndex != -1 && leftFootIndex != -1) { + glm::vec3 foot = Vectors::ZERO; + glm::quat footRotation = glm::angleAxis(0.5f * PI, Vectors::UNIT_X); + animVars.set("leftFootPosition", foot); + animVars.set("leftFootRotation", footRotation); + animVars.set("leftFootType", (int)IKTarget::Type::RotationAndPosition); + animVars.set("rightFootPosition", foot); + animVars.set("rightFootRotation", footRotation); + animVars.set("rightFootType", (int)IKTarget::Type::RotationAndPosition); + } + + // call overlay twice: once to verify AnimPoseVec joints and again to do the IK + AnimNode::Triggers triggersOut; + float dt = 1.0f; // the value of this does not matter + ikNode.overlay(animVars, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); + AnimPoseVec finalPoses = ikNode.overlay(animVars, dt, triggersOut, _animSkeleton->getRelativeBindPoses()); + + // convert relative poses to absolute + _animSkeleton->convertRelativePosesToAbsolute(finalPoses); + + // compute bounding box that encloses all points + Extents totalExtents; + totalExtents.reset(); + int numPoses = finalPoses.size(); + for (int i = 0; i < numPoses; i++) { + const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo; + AnimPose pose = finalPoses[i]; + if (shapeInfo.points.size() > 0) { + for (int j = 0; j < shapeInfo.points.size(); ++j) { + totalExtents.addPoint((pose * shapeInfo.points[j])); + } + } else { + totalExtents.addPoint(pose.trans); + } + } + // HACK so that default legless robot doesn't knuckle-drag + if (rightFootIndex > -1) { + totalExtents.addPoint(finalPoses[rightFootIndex].trans); + } + if (leftFootIndex > -1) { + totalExtents.addPoint(finalPoses[leftFootIndex].trans); + } + + // compute bounding shape parameters + // NOTE: we assume that the longest side of totalExtents is the yAxis... + glm::vec3 diagonal = geometryToRig * (totalExtents.maximum - totalExtents.minimum); + // ... and assume the radiusOut is half the RMS of the X and Z sides: + radiusOut = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + heightOut = diagonal.y - 2.0f * radiusOut; + + glm::vec3 rootPosition = finalPoses[geometry.rootJointIndex].trans; + localOffsetOut = geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition); +} diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index df97d9bc66..20348067a7 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -197,6 +197,8 @@ public: void copyJointsIntoJointData(QVector& jointDataVec) const; void copyJointsFromJointData(const QVector& jointDataVec); + void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; + protected: bool isValid(int index) const { return _animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints(); } void updateAnimationStateHandlers(); From 2cc1dfe81936bc906250b3f4292e53f2f30d8f19 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 24 Nov 2015 20:39:26 -0800 Subject: [PATCH 72/84] getRootAbsoluteBindPoseByChildName() is deprecated --- libraries/animation/src/Rig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 17dd43cd30..8465e983d8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1111,7 +1111,7 @@ void Rig::computeAvatarBoundingCapsule( AnimPose geometryToRig = _modelOffset * _geometryOffset; - AnimPose hips = geometryToRig * _animSkeleton->getRootAbsoluteBindPoseByChildName("LeftHand"); + AnimPose hips = geometryToRig * _animSkeleton->getAbsoluteBindPose(_animSkeleton->nameToJointIndex("Hips")); AnimVariantMap animVars; glm::quat handRotation = glm::angleAxis(PI, Vectors::UNIT_X); animVars.set("leftHandPosition", hips.trans); From 868cf83b4819458a95e9a28b5fa2df51de57b83d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 25 Nov 2015 10:07:29 -0800 Subject: [PATCH 73/84] Fix for attachments and MyAvatar::getDefaultEyePosition() --- interface/src/avatar/Avatar.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/animation/src/Rig.cpp | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 99152bfb6c..d07c46b71b 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -639,7 +639,7 @@ void Avatar::simulateAttachments(float deltaTime) { glm::vec3 jointPosition; glm::quat jointRotation; if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) && - _skeletonModel.getJointCombinedRotation(jointIndex, jointRotation)) { + _skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) { model->setTranslation(jointPosition + jointRotation * attachment.translation * _scale); model->setRotation(jointRotation * attachment.rotation); model->setScaleToFit(true, _scale * attachment.scale, true); // hack to force rescale diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ccb69f1ff1..89ec7ad6ee 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -952,7 +952,7 @@ eyeContactTarget MyAvatar::getEyeContactTarget() { } glm::vec3 MyAvatar::getDefaultEyePosition() const { - return getPosition() + getWorldAlignedOrientation() * _skeletonModel.getDefaultEyeModelPosition(); + return getPosition() + getWorldAlignedOrientation() * Quaternions::Y_180 * _skeletonModel.getDefaultEyeModelPosition(); } const float SCRIPT_PRIORITY = 1.0f + 1.0f; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ae1786f905..34f391424a 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -321,8 +321,6 @@ void Rig::setJointState(int index, bool valid, const glm::quat& rotation, const } } -// Deprecated. -// WARNING: this is not symmetric with getJointRotation. It's historical. Use the appropriate specific variation. void Rig::setJointRotation(int index, bool valid, const glm::quat& rotation, float priority) { if (isIndexValid(index)) { if (valid) { @@ -370,8 +368,6 @@ bool Rig::getJointRotationInWorldFrame(int jointIndex, glm::quat& result, const } } -// Deprecated. -// WARNING: this is not symmetric with setJointRotation. It's historical. Use the appropriate specific variation. bool Rig::getJointRotation(int jointIndex, glm::quat& rotation) const { if (isIndexValid(jointIndex)) { rotation = _relativePoses[jointIndex].rot; From 97a2eb62d4771e08765a9c5bdac66cd38a86e23d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 25 Nov 2015 12:06:50 -0800 Subject: [PATCH 74/84] Fix for incorrect avatar bounds after changing avatars --- libraries/animation/src/Rig.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 34f391424a..8718f605bf 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -1027,7 +1027,6 @@ void Rig::buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& a // transform all absolute poses into rig space. AnimPose geometryToRigTransform(_geometryToRigTransform); - geometryToRigTransform = _modelOffset * _geometryOffset; for (int i = 0; i < (int)_absolutePoses.size(); i++) { absolutePosesOut[i] = geometryToRigTransform * absolutePosesOut[i]; } @@ -1134,6 +1133,12 @@ void Rig::computeAvatarBoundingCapsule( // compute bounding box that encloses all points Extents totalExtents; totalExtents.reset(); + + // HACK by convention our Avatars are always modeled such that y=0 is the ground plane. + // add the zero point so that our avatars will always have bounding volumes that are flush with the ground + // even if they do not have legs (default robot) + totalExtents.addPoint(glm::vec3(0.0f)); + int numPoses = finalPoses.size(); for (int i = 0; i < numPoses; i++) { const FBXJointShapeInfo& shapeInfo = geometry.joints.at(i).shapeInfo; @@ -1142,25 +1147,17 @@ void Rig::computeAvatarBoundingCapsule( for (int j = 0; j < shapeInfo.points.size(); ++j) { totalExtents.addPoint((pose * shapeInfo.points[j])); } - } else { - totalExtents.addPoint(pose.trans); } } - // HACK so that default legless robot doesn't knuckle-drag - if (rightFootIndex > -1) { - totalExtents.addPoint(finalPoses[rightFootIndex].trans); - } - if (leftFootIndex > -1) { - totalExtents.addPoint(finalPoses[leftFootIndex].trans); - } // compute bounding shape parameters // NOTE: we assume that the longest side of totalExtents is the yAxis... - glm::vec3 diagonal = geometryToRig * (totalExtents.maximum - totalExtents.minimum); + glm::vec3 diagonal = (geometryToRig * totalExtents.maximum) - (geometryToRig * totalExtents.minimum); // ... and assume the radiusOut is half the RMS of the X and Z sides: radiusOut = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); heightOut = diagonal.y - 2.0f * radiusOut; glm::vec3 rootPosition = finalPoses[geometry.rootJointIndex].trans; - localOffsetOut = geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition); + glm::vec3 rigCenter = (geometryToRig * (0.5f * (totalExtents.maximum + totalExtents.minimum))); + localOffsetOut = rigCenter - (geometryToRig * rootPosition); } From 2a9adafc099815e73ae8938100703197c08d0ce2 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 25 Nov 2015 12:13:40 -0800 Subject: [PATCH 75/84] Bumped avatar packet version number --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 2411ee23ac..2f685b7e08 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -45,7 +45,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: default: - return 16; + return 17; } } From c0d108e6f6002140ea882e04fc0c71393593ecb3 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 25 Nov 2015 13:51:08 -0800 Subject: [PATCH 76/84] Better avatar bounding shape debug rendering. --- interface/src/avatar/SkeletonModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 4241faa616..d438e6f528 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -354,7 +354,7 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha auto geometryCache = DependencyManager::get(); auto deferredLighting = DependencyManager::get(); // draw a blue sphere at the capsule top point - glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f); + glm::vec3 topPoint = _translation + getRotation() * (_boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f)); deferredLighting->renderSolidSphereInstance(batch, Transform().setTranslation(topPoint).postScale(_boundingCapsuleRadius), From 5072339a2241eb9405586a5bfa46ea4b23b8b68d Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 25 Nov 2015 14:02:14 -0800 Subject: [PATCH 77/84] Warning fix --- libraries/animation/src/AnimSkeleton.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 36b14a97ce..7879c8a776 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -81,7 +81,7 @@ AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& poses) void AnimSkeleton::convertRelativePosesToAbsolute(AnimPoseVec& poses) const { // poses start off relative and leave in absolute frame - for (int i = 0; i < poses.size() && i < _joints.size(); ++i) { + for (int i = 0; i < (int)poses.size() && i < (int)_joints.size(); ++i) { int parentIndex = _joints[i].parentIndex; if (parentIndex != -1) { poses[i] = poses[parentIndex] * poses[i]; From 549a8f7130dc8fc31c476049c4d16306fa9e2710 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 25 Nov 2015 17:24:55 -0800 Subject: [PATCH 78/84] Fix for avatars with no eyes For example: https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/howard/howard.fst https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/tony/tony.fst --- interface/src/avatar/MyAvatar.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 89ec7ad6ee..39b09fb9de 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1798,10 +1798,10 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { const glm::quat hmdOrientation = getHMDSensorOrientation(); const glm::quat hmdOrientationYawOnly = cancelOutRollAndPitch(hmdOrientation); - // 2 meter tall dude - const glm::vec3 DEFAULT_RIG_MIDDLE_EYE_POS(0.0f, 1.9f, 0.0f); - const glm::vec3 DEFAULT_RIG_NECK_POS(0.0f, 1.70f, 0.0f); - const glm::vec3 DEFAULT_RIG_HIPS_POS(0.0f, 1.05f, 0.0f); + // 2 meter tall dude (in rig coordinates) + const glm::vec3 DEFAULT_RIG_MIDDLE_EYE_POS(0.0f, 0.9f, 0.0f); + const glm::vec3 DEFAULT_RIG_NECK_POS(0.0f, 0.70f, 0.0f); + const glm::vec3 DEFAULT_RIG_HIPS_POS(0.0f, 0.05f, 0.0f); int rightEyeIndex = _rig->indexOfJoint("RightEye"); int leftEyeIndex = _rig->indexOfJoint("LeftEye"); From 171ae02b03f61801c43116abee5c70fbde9fb500 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 30 Nov 2015 13:12:39 -0800 Subject: [PATCH 79/84] update to parity with createWand.js --- unpublishedScripts/hiddenEntityReset.js | 31 ++++++++++++++---------- unpublishedScripts/masterReset.js | 32 ++++++++++++++----------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index cf9eaaa451..83c1d63f1b 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -931,16 +931,18 @@ function createWand(position) { var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/actual_no_top_collision_hull.obj'; - var entity = Entities.addEntity({ + var WAND_COLLISION_SHAPE = 'http://hifi-content.s3.amazonaws.com/james/bubblewand/wand_collision_hull.obj'; + + var wand = Entities.addEntity({ name: 'Bubble Wand', type: "Model", modelURL: WAND_MODEL, + shapeType: 'compound', position: position, gravity: { x: 0, - y: -9.8, - z: 0 + y: 0, + z: 0, }, dimensions: { x: 0.05, @@ -948,24 +950,27 @@ z: 0.05 }, //must be enabled to be grabbable in the physics engine - shapeType: 'compound', collisionsWillMove: true, compoundShapeURL: WAND_COLLISION_SHAPE, - //Look into why bubble wand is going through table when gravity is enabled - // gravity: {x: 0, y: -3.5, z: 0}, - // velocity: {x: 0, y: -0.01, z:0}, script: wandScriptURL, userData: JSON.stringify({ - resetMe: { - resetMe: true - }, grabbableKey: { - invertSolidWhileHeld: true + resetMe: { + resetMe: true + }, + invertSolidWhileHeld: true, + spatialKey: { + relativePosition: { + x: 0, + y: 0.1, + z: 0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 90) + } } }) }); - } function createBasketBall(position) { diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 60b4e7a72f..596cff3d99 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -883,7 +883,7 @@ MasterReset = function() { type: "Model", modelURL: MODEL_URL, shapeType: 'compound', - compoundShapeURL:COLLISION_HULL_URL, + compoundShapeURL: COLLISION_HULL_URL, script: pingPongScriptURL, position: position, rotation: rotation, @@ -913,17 +913,18 @@ MasterReset = function() { function createWand(position) { var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; - var WAND_COLLISION_SHAPE = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/actual_no_top_collision_hull.obj'; + var WAND_COLLISION_SHAPE = 'http://hifi-content.s3.amazonaws.com/james/bubblewand/wand_collision_hull.obj'; - var entity = Entities.addEntity({ + var wand = Entities.addEntity({ name: 'Bubble Wand', type: "Model", modelURL: WAND_MODEL, + shapeType: 'compound', position: position, gravity: { x: 0, - y: -9.8, - z: 0 + y: 0, + z: 0, }, dimensions: { x: 0.05, @@ -931,24 +932,27 @@ MasterReset = function() { z: 0.05 }, //must be enabled to be grabbable in the physics engine - shapeType: 'compound', collisionsWillMove: true, compoundShapeURL: WAND_COLLISION_SHAPE, - //Look into why bubble wand is going through table when gravity is enabled - // gravity: {x: 0, y: -3.5, z: 0}, - // velocity: {x: 0, y: -0.01, z:0}, script: wandScriptURL, userData: JSON.stringify({ - resetMe: { - resetMe: true - }, grabbableKey: { - invertSolidWhileHeld: true + resetMe: { + resetMe: true + }, + invertSolidWhileHeld: true, + spatialKey: { + relativePosition: { + x: 0, + y: 0.1, + z: 0 + }, + relativeRotation: Quat.fromPitchYawRollDegrees(0, 0, 90) + } } }) }); - } function createBasketBall(position) { From 9b90c19781f5562dc16deb8ce9df1c58f2b3c69c Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 30 Nov 2015 13:24:33 -0800 Subject: [PATCH 80/84] cleanup --- unpublishedScripts/hiddenEntityReset.js | 10 +++++----- unpublishedScripts/masterReset.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/unpublishedScripts/hiddenEntityReset.js b/unpublishedScripts/hiddenEntityReset.js index 83c1d63f1b..e50a658fba 100644 --- a/unpublishedScripts/hiddenEntityReset.js +++ b/unpublishedScripts/hiddenEntityReset.js @@ -930,7 +930,7 @@ } function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; + var WAND_MODEL = 'http://hifi-content.s3.amazonaws.com/james/bubblewand/wand.fbx'; var WAND_COLLISION_SHAPE = 'http://hifi-content.s3.amazonaws.com/james/bubblewand/wand_collision_hull.obj'; var wand = Entities.addEntity({ @@ -941,7 +941,7 @@ position: position, gravity: { x: 0, - y: 0, + y: -9.8, z: 0, }, dimensions: { @@ -954,10 +954,10 @@ compoundShapeURL: WAND_COLLISION_SHAPE, script: wandScriptURL, userData: JSON.stringify({ + resetMe: { + resetMe: true + }, grabbableKey: { - resetMe: { - resetMe: true - }, invertSolidWhileHeld: true, spatialKey: { relativePosition: { diff --git a/unpublishedScripts/masterReset.js b/unpublishedScripts/masterReset.js index 596cff3d99..108f299ae3 100644 --- a/unpublishedScripts/masterReset.js +++ b/unpublishedScripts/masterReset.js @@ -912,7 +912,7 @@ MasterReset = function() { } function createWand(position) { - var WAND_MODEL = 'http://hifi-public.s3.amazonaws.com/models/bubblewand/wand.fbx'; + var WAND_MODEL = 'http://hifi-content.s3.amazonaws.com/james/bubblewand/wand.fbx'; var WAND_COLLISION_SHAPE = 'http://hifi-content.s3.amazonaws.com/james/bubblewand/wand_collision_hull.obj'; var wand = Entities.addEntity({ @@ -923,7 +923,7 @@ MasterReset = function() { position: position, gravity: { x: 0, - y: 0, + y: -9.8, z: 0, }, dimensions: { @@ -936,10 +936,10 @@ MasterReset = function() { compoundShapeURL: WAND_COLLISION_SHAPE, script: wandScriptURL, userData: JSON.stringify({ + resetMe: { + resetMe: true + }, grabbableKey: { - resetMe: { - resetMe: true - }, invertSolidWhileHeld: true, spatialKey: { relativePosition: { From e2d9e37eb886046bf236b505dce227f05bc67a5f Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 30 Nov 2015 14:08:21 -0800 Subject: [PATCH 81/84] Fix for head offset for models with no eyes This was causing in correct body poses while wearing an HMD. --- libraries/animation/src/Rig.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 8718f605bf..c0b399ee0f 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -46,18 +46,11 @@ static bool isEqual(const glm::quat& p, const glm::quat& q) { } while (0) #endif -/* -const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.6f, 0.0f); -const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.6f, 0.0f); -const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.55f, 0.0f); -const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.5f, 0.0f); -*/ - // 2 meter tall dude -const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 1.9f, 0.0f); -const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 1.9f, 0.0f); -const glm::vec3 DEFAULT_HEAD_POS(0.0f, 1.75f, 0.0f); -const glm::vec3 DEFAULT_NECK_POS(0.0f, 1.70f, 0.0f); +const glm::vec3 DEFAULT_RIGHT_EYE_POS(-0.3f, 0.9f, 0.0f); +const glm::vec3 DEFAULT_LEFT_EYE_POS(0.3f, 0.9f, 0.0f); +const glm::vec3 DEFAULT_HEAD_POS(0.0f, 0.75f, 0.0f); +const glm::vec3 DEFAULT_NECK_POS(0.0f, 0.70f, 0.0f); void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { From f922f61c3dfc0bdfdf9dc843c7099e0c089013e8 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 30 Nov 2015 15:21:04 -0800 Subject: [PATCH 82/84] AvatarUpdate: fix for very large dt This was affecting threaded avatar update when enabled. --- interface/src/avatar/AvatarManager.cpp | 3 ++- interface/src/avatar/AvatarUpdate.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 7592315f5e..9df597109c 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -356,7 +356,8 @@ void AvatarManager::handleCollisionEvents(const CollisionEvents& collisionEvents AudioInjector::playSound(collisionSoundURL, energyFactorOfFull, AVATAR_STRETCH_FACTOR, myAvatar->getPosition()); myAvatar->collisionWithEntity(collision); - return; } + return; + } } } } diff --git a/interface/src/avatar/AvatarUpdate.cpp b/interface/src/avatar/AvatarUpdate.cpp index acdb251950..89b208d86f 100644 --- a/interface/src/avatar/AvatarUpdate.cpp +++ b/interface/src/avatar/AvatarUpdate.cpp @@ -44,9 +44,12 @@ void AvatarUpdate::synchronousProcess() { bool AvatarUpdate::process() { PerformanceTimer perfTimer("AvatarUpdate"); quint64 start = usecTimestampNow(); - quint64 deltaMicroseconds = start - _lastAvatarUpdate; - _lastAvatarUpdate = start; + quint64 deltaMicroseconds = 0; + if (_lastAvatarUpdate > 0) { + deltaMicroseconds = start - _lastAvatarUpdate; + } float deltaSeconds = (float) deltaMicroseconds / (float) USECS_PER_SECOND; + _lastAvatarUpdate = start; qApp->setAvatarSimrateSample(1.0f / deltaSeconds); QSharedPointer manager = DependencyManager::get(); From 31cecdc174af3bbd110992b46bde6cc2b8ce134b Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Mon, 30 Nov 2015 15:45:23 -0800 Subject: [PATCH 83/84] Menu: removed Enable Avatar Threading option. To re-enable, you can set ENABLE_AVATAR_UPDATE_THREADING to true on line 2552 of Application.cpp. Also, removed other unused strings. --- interface/src/Application.cpp | 5 +++-- interface/src/Menu.cpp | 2 -- interface/src/Menu.h | 3 --- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 76d14b74ab..efc5fe4a76 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2549,11 +2549,12 @@ void Application::init() { setAvatarUpdateThreading(); } +const bool ENABLE_AVATAR_UPDATE_THREADING = false; void Application::setAvatarUpdateThreading() { - setAvatarUpdateThreading(Menu::getInstance()->isOptionChecked(MenuOption::EnableAvatarUpdateThreading)); + setAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING); } void Application::setRawAvatarUpdateThreading() { - setRawAvatarUpdateThreading(Menu::getInstance()->isOptionChecked(MenuOption::EnableAvatarUpdateThreading)); + setRawAvatarUpdateThreading(ENABLE_AVATAR_UPDATE_THREADING); } void Application::setRawAvatarUpdateThreading(bool isThreaded) { if (_avatarUpdate) { diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3f472a5b09..d0c8b502c5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -441,8 +441,6 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::ShowWhosLookingAtMe, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::FixGaze, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableAvatarUpdateThreading, 0, false, - qApp, SLOT(setAvatarUpdateThreading(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawDefaultPose, 0, false, avatar, SLOT(setEnableDebugDrawDefaultPose(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AnimDebugDrawAnimPose, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 01164d9c0c..6b51987479 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -189,10 +189,7 @@ namespace MenuOption { const QString EchoServerAudio = "Echo Server Audio"; const QString EditEntitiesHelp = "Edit Entities Help..."; const QString Enable3DTVMode = "Enable 3DTV Mode"; - const QString EnableAvatarUpdateThreading = "Enable Avatar Update Threading"; - const QString EnableAnimGraph = "Enable Anim Graph"; const QString EnableCharacterController = "Enable avatar collisions"; - const QString EnableRigAnimations = "Enable Rig Animations"; const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; const QString ExpandMyAvatarTiming = "Expand /myAvatar"; const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; From 7c12379adae5763cf3aeda7c646fc10909d5b179 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 30 Nov 2015 17:00:40 -0800 Subject: [PATCH 84/84] fix release hand message --- examples/toybox/bow/bow.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/toybox/bow/bow.js b/examples/toybox/bow/bow.js index f38aa64921..14e3ed86ec 100644 --- a/examples/toybox/bow/bow.js +++ b/examples/toybox/bow/bow.js @@ -216,7 +216,7 @@ // print('RELEASE GRAB EVENT') if (this.isGrabbed === true && this.hand === this.initialHand) { - Messages.sendMessage('Hifi-Beam-Disabler', "none") + Messages.sendMessage('Hifi-Hand-Disabler', "none") this.isGrabbed = false; this.stringDrawn = false;