//// Rig.cpp// libraries/animation/src///// Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15.// Copyright (c) 2015 High Fidelity, Inc. All rights reserved.// Copyright 2023 Overte e.V.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// SPDX-License-Identifier: Apache-2.0// #include "Rig.h" #include <glm/gtx/vector_angle.hpp>#include <queue>#include <QWriteLocker>#include <QReadLocker> #include <GeometryUtil.h>#include <NumericalConstants.h>#include <DebugDraw.h>#include <PerfStat.h>#include <ScriptEngine.h>#include <ScriptManager.h>#include <ScriptValueUtils.h>#include <ScriptValue.h>#include <shared/NsightHelpers.h> #include "AnimationLogging.h"#include "AnimClip.h"#include "AnimInverseKinematics.h"#include "AnimOverlay.h"#include "AnimSkeleton.h"#include "AnimStateMachine.h"#include "AnimUtil.h"#include "AvatarConstants.h"#include "IKTarget.h"#include "PathUtils.h" static int nextRigId = 1;static std::map<int, Rig*> rigRegistry;static std::mutex rigRegistryMutex; static bool isEqual(const float p, float q) { const float EPSILON = 0.00001f; return fabsf(p - q) <= EPSILON;} static bool isEqual(const glm::vec3& u, const glm::vec3& v) { return isEqual(u.x, v.x) && isEqual(u.y, v.y) && isEqual(u.z, v.z);} 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;} #define ASSERT(cond) assert(cond) // 2 meter tall dudeconst 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); static const QString LEFT_FOOT_POSITION("leftFootPosition");static const QString LEFT_FOOT_ROTATION("leftFootRotation");static const QString LEFT_FOOT_IK_POSITION_VAR("leftFootIKPositionVar");static const QString LEFT_FOOT_IK_ROTATION_VAR("leftFootIKRotationVar");static const QString MAIN_STATE_MACHINE_LEFT_FOOT_POSITION("mainStateMachineLeftFootPosition");static const QString MAIN_STATE_MACHINE_LEFT_FOOT_ROTATION("mainStateMachineLeftFootRotation"); static const QString RIGHT_FOOT_POSITION("rightFootPosition");static const QString RIGHT_FOOT_ROTATION("rightFootRotation");static const QString RIGHT_FOOT_IK_POSITION_VAR("rightFootIKPositionVar");static const QString RIGHT_FOOT_IK_ROTATION_VAR("rightFootIKRotationVar");static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION("mainStateMachineRightFootRotation");static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION("mainStateMachineRightFootPosition"); static const QString LEFT_HAND_POSITION("leftHandPosition");static const QString LEFT_HAND_ROTATION("leftHandRotation");static const QString LEFT_HAND_IK_POSITION_VAR("leftHandIKPositionVar");static const QString LEFT_HAND_IK_ROTATION_VAR("leftHandIKRotationVar");static const QString MAIN_STATE_MACHINE_LEFT_HAND_POSITION("mainStateMachineLeftHandPosition");static const QString MAIN_STATE_MACHINE_LEFT_HAND_ROTATION("mainStateMachineLeftHandRotation"); static const QString RIGHT_HAND_POSITION("rightHandPosition");static const QString RIGHT_HAND_ROTATION("rightHandRotation");static const QString RIGHT_HAND_IK_POSITION_VAR("rightHandIKPositionVar");static const QString RIGHT_HAND_IK_ROTATION_VAR("rightHandIKRotationVar");static const QString MAIN_STATE_MACHINE_RIGHT_HAND_ROTATION("mainStateMachineRightHandRotation");static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRightHandPosition"); /*@jsdoc * <p>An <code>AnimStateDictionary</code> object may have the following properties. It may also have other properties, set by * scripts.</p> * <p><strong>Warning:</strong> These properties are subject to change. * <table> * <thead> * <tr><th>Name</th><th>Type</th><th>Description</th> * </thead> * <tbody> * <tr><td><code>userAnimNone</code></td><td>boolean</td><td><code>true</code> when no user overrideAnimation is * playing.</td></tr> * <tr><td><code>userAnimA</code></td><td>boolean</td><td><code>true</code> when a user overrideAnimation is * playing.</td></tr> * <tr><td><code>userAnimB</code></td><td>boolean</td><td><code>true</code> when a user overrideAnimation is * playing.</td></tr> * * <tr><td><code>sine</code></td><td>number</td><td>Oscillating sine wave.</td></tr> * <tr><td><code>moveForwardSpeed</code></td><td>number</td><td>Controls the blend between the various forward walking * & running animations.</td></tr> * <tr><td><code>moveBackwardSpeed</code></td><td>number</td><td>Controls the blend between the various backward walking * & running animations.</td></tr> * <tr><td><code>moveLateralSpeed</code></td><td>number</td><td>Controls the blend between the various sidestep walking * & running animations.</td></tr> * * <tr><td><code>isMovingForward</code></td><td>boolean</td><td><code>true</code> if the avatar is moving * forward.</td></tr> * <tr><td><code>isMovingBackward</code></td><td>boolean</td><td><code>true</code> if the avatar is moving * backward.</td></tr> * <tr><td><code>isMovingRight</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the * right.</td></tr> * <tr><td><code>isMovingLeft</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the * left.</td></tr> * <tr><td><code>isMovingRightHmd</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the right * while the user is in HMD mode.</td></tr> * <tr><td><code>isMovingLeftHmd</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the left while * the user is in HMD mode.</td></tr> * <tr><td><code>isNotMoving</code></td><td>boolean</td><td><code>true</code> if the avatar is stationary.</td></tr> * * <tr><td><code>isTurningRight</code></td><td>boolean</td><td><code>true</code> if the avatar is turning * clockwise.</td></tr> * <tr><td><code>isTurningLeft</code></td><td>boolean</td><td><code>true</code> if the avatar is turning * counter-clockwise.</td></tr> * <tr><td><code>isNotTurning</code></td><td>boolean</td><td><code>true</code> if the avatar is not turning.</td></tr> * <tr><td><code>isFlying</code></td><td>boolean</td><td><code>true</code> if the avatar is flying.</td></tr> * <tr><td><code>isNotFlying</code></td><td>boolean</td><td><code>true</code> if the avatar is not flying.</td></tr> * <tr><td><code>isTakeoffStand</code></td><td>boolean</td><td><code>true</code> if the avatar is about to execute a * standing jump.</td></tr> * <tr><td><code>isTakeoffRun</code></td><td>boolean</td><td><code>true</code> if the avatar is about to execute a running * jump.</td></tr> * <tr><td><code>isNotTakeoff</code></td><td>boolean</td><td><code>true</code> if the avatar is not jumping.</td></tr> * <tr><td><code>isInAirStand</code></td><td>boolean</td><td><code>true</code> if the avatar is in the air after a standing * jump.</td></tr> * <tr><td><code>isInAirRun</code></td><td>boolean</td><td><code>true</code> if the avatar is in the air after a running * jump.</td></tr> * <tr><td><code>isNotInAir</code></td><td>boolean</td><td><code>true</code> if the avatar on the ground.</td></tr> * * <tr><td><code>inAirAlpha</code></td><td>number</td><td>Used to interpolate between the up, apex, and down in-air * animations.</td></tr> * <tr><td><code>ikOverlayAlpha</code></td><td>number</td><td>The blend between upper body and spline IK versus the * underlying animation</td></tr> * * <tr><td><code>headPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>Head</code> joint in * rig coordinates.</td></tr> * <tr><td><code>headRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>Head</code> joint in * rig coordinates.</td></tr> * <tr><td><code>headType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the * head.</td></tr> * <tr><td><code>headWeight</code></td><td>number</td><td>How strongly the head chain blends with the other IK * chains.</td></tr> * * <tr><td><code>leftHandPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>LeftHand</code> * joint in rig coordinates.</td></tr> * <tr><td><code>leftHandRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>LeftHand</code> * joint in rig coordinates.</td></tr> * <tr><td><code>leftHandType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the * left arm.</td></tr> * <tr><td><code>leftHandPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the elbow angle is * controlled by the <code>rightHandPoleVector</code> property value. Otherwise the elbow direction comes from the * underlying animation.</td></tr> * <tr><td><code>leftHandPoleReferenceVector</code></td><td>{@link Vec3}</td><td>The direction of the elbow in the local * coordinate system of the elbow.</td></tr> * <tr><td><code>leftHandPoleVector</code></td><td>{@link Vec3}</td><td>The direction the elbow should point in rig * coordinates.</td></tr> * * <tr><td><code>rightHandPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>RightHand</code> * joint in rig coordinates.</td></tr> * <tr><td><code>rightHandRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the * <code>RightHand</code> joint in rig coordinates.</td></tr> * <tr><td><code>rightHandType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for * the right arm.</td></tr> * <tr><td><code>rightHandPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the elbow angle is * controlled by the <code>rightHandPoleVector</code> property value. Otherwise the elbow direction comes from the * underlying animation.</td></tr> * <tr><td><code>rightHandPoleReferenceVector</code></td><td>{@link Vec3}</td><td>The direction of the elbow in the local * coordinate system of the elbow.</td></tr> * <tr><td><code>rightHandPoleVector</code></td><td>{@link Vec3}</td><td>The direction the elbow should point in rig * coordinates.</td></tr> * * <tr><td><code>leftFootIKEnabled</code></td><td>boolean</td><td><code>true</code> if IK is enabled for the left * foot.</td></tr> * <tr><td><code>rightFootIKEnabled</code></td><td>boolean</td><td><code>true</code> if IK is enabled for the right * foot.</td></tr> * * <tr><td><code>leftFootIKPositionVar</code></td><td>string</td><td>The name of the source for the desired position * of the <code>LeftFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr> * <tr><td><code>leftFootIKRotationVar</code></td><td>string</td><td>The name of the source for the desired rotation * of the <code>LeftFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr> * <tr><td><code>leftFootPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the knee angle is * controlled by the <code>leftFootPoleVector</code> property value. Otherwise the knee direction comes from the * underlying animation.</td></tr> * <tr><td><code>leftFootPoleVector</code></td><td>{@link Vec3}</td><td>The direction the knee should face in rig * coordinates.</td></tr> * <tr><td><code>rightFootIKPositionVar</code></td><td>string</td><td>The name of the source for the desired position * of the <code>RightFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr> * <tr><td><code>rightFootIKRotationVar</code></td><td>string</td><td>The name of the source for the desired rotation * of the <code>RightFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr> * <tr><td><code>rightFootPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the knee angle is * controlled by the <code>rightFootPoleVector</code> property value. Otherwise the knee direction comes from the * underlying animation.</td></tr> * <tr><td><code>rightFootPoleVector</code></td><td>{@link Vec3}</td><td>The direction the knee should face in rig * coordinates.</td></tr> * * <tr><td><code>isTalking</code></td><td>boolean</td><td><code>true</code> if the avatar is talking.</td></tr> * <tr><td><code>notIsTalking</code></td><td>boolean</td><td><code>true</code> if the avatar is not talking.</td></tr> * * <tr><td><code>solutionSource</code></td><td>{@link MyAvatar.AnimIKSolutionSource|AnimIKSolutionSource}</td> * <td>Determines the initial conditions of the IK solver.</td></tr> * <tr><td><code>defaultPoseOverlayAlpha</code></td><td>number</td><td>Controls the blend between the main animation state * machine and the default pose. Mostly used during full body tracking so that walking & jumping animations do not * affect the IK of the figure.</td></tr> * <tr><td><code>defaultPoseOverlayBoneSet</code></td><td>{@link MyAvatar.AnimOverlayBoneSet|AnimOverlayBoneSet}</td> * <td>Specifies which bones will be replace by the source overlay.</td></tr> * <tr><td><code>hipsType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the * hips.</td></tr> * <tr><td><code>hipsPosition</code></td><td>{@link Vec3}</td><td>The desired position of <code>Hips</code> joint in rig * coordinates.</td></tr> * <tr><td><code>hipsRotation</code></td><td>{@link Quat}</td><td>the desired orientation of the <code>Hips</code> joint in * rig coordinates.</td></tr> * <tr><td><code>spine2Type</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the * <code>Spine2</code> joint.</td></tr> * <tr><td><code>spine2Position</code></td><td>{@link Vec3}</td><td>The desired position of the <code>Spine2</code> joint * in rig coordinates.</td></tr> * <tr><td><code>spine2Rotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>Spine2</code> * joint in rig coordinates.</td></tr> * * <tr><td><code>leftFootIKAlpha</code></td><td>number</td><td>Blends between full IK for the leg and the underlying * animation.</td></tr> * <tr><td><code>rightFootIKAlpha</code></td><td>number</td><td>Blends between full IK for the leg and the underlying * animation.</td></tr> * <tr><td><code>hipsWeight</code></td><td>number</td><td>How strongly the hips target blends with the IK solution for * other IK chains.</td></tr> * <tr><td><code>leftHandWeight</code></td><td>number</td><td>How strongly the left hand blends with IK solution of other * IK chains.</td></tr> * <tr><td><code>rightHandWeight</code></td><td>number</td><td>How strongly the right hand blends with IK solution of other * IK chains.</td></tr> * <tr><td><code>spine2Weight</code></td><td>number</td><td>How strongly the spine2 chain blends with the rest of the IK * solution.</td></tr> * * <tr><td><code>leftHandOverlayAlpha</code></td><td>number</td><td>Used to blend in the animated hand gesture poses, such * as point and thumbs up.</td></tr> * <tr><td><code>leftHandGraspAlpha</code></td><td>number</td><td>Used to blend between an open hand and a closed hand. * Usually changed as you squeeze the trigger of the hand controller.</td></tr> * <tr><td><code>rightHandOverlayAlpha</code></td><td>number</td><td>Used to blend in the animated hand gesture poses, * such as point and thumbs up.</td></tr> * <tr><td><code>rightHandGraspAlpha</code></td><td>number</td><td>Used to blend between an open hand and a closed hand. * Usually changed as you squeeze the trigger of the hand controller.</td></tr> * <tr><td><code>isLeftIndexPoint</code></td><td>boolean</td><td><code>true</code> if the left hand should be * pointing.</td></tr> * <tr><td><code>isLeftThumbRaise</code></td><td>boolean</td><td><code>true</code> if the left hand should be * thumbs-up.</td></tr> * <tr><td><code>isLeftIndexPointAndThumbRaise</code></td><td>boolean</td><td><code>true</code> if the left hand should be * pointing and thumbs-up.</td></tr> * <tr><td><code>isLeftHandGrasp</code></td><td>boolean</td><td><code>true</code> if the left hand should be at rest, * grasping the controller.</td></tr> * <tr><td><code>isRightIndexPoint</code></td><td>boolean</td><td><code>true</code> if the right hand should be * pointing.</td></tr> * <tr><td><code>isRightThumbRaise</code></td><td>boolean</td><td><code>true</code> if the right hand should be * thumbs-up.</td></tr> * <tr><td><code>isRightIndexPointAndThumbRaise</code></td><td>boolean</td><td><code>true</code> if the right hand should * be pointing and thumbs-up.</td></tr> * <tr><td><code>isRightHandGrasp</code></td><td>boolean</td><td><code>true</code> if the right hand should be at rest, * grasping the controller.</td></tr> * * </tbody> * </table> * <p>Note: Rig coordinates are <code>+z</code> forward and <code>+y</code> up.</p> * @typedef {object} MyAvatar.AnimStateDictionary */// Note: The following animVars are intentionally not documented:// - leftFootPosition// - leftFootRotation// - rightFooKPosition// - rightFooKRotation// Note: The following items aren't set in the code below but are still intentionally documented:// - leftFootIKAlpha// - rightFootIKAlpha// - hipsWeight// - leftHandWeight// - rightHandWeight// - spine2Weight// - rightHandOverlayAlpha// - rightHandGraspAlpha// - leftHandOverlayAlpha// - leftHandGraspAlpha// - isRightIndexPoint// - isRightThumbRaise// - isRightIndexPointAndThumbRaise// - isRightHandGrasp// - isLeftIndexPoint// - isLeftThumbRaise// - isLeftIndexPointAndThumbRaise// - isLeftHandGraspRig::Rig() : _internalFlow(this), _networkFlow(this) { // Ensure thread-safe access to the rigRegistry. std::lock_guard<std::mutex> guard(rigRegistryMutex); // Insert this newly allocated rig into the rig registry _rigId = nextRigId; rigRegistry[_rigId] = this; nextRigId++;} Rig::~Rig() { // Ensure thread-safe access to the rigRegstry, but also prevent the rig from being deleted // while Rig::animationStateHandlerResult is being invoked on a script thread. std::lock_guard<std::mutex> guard(rigRegistryMutex); auto iter = rigRegistry.find(_rigId); if (iter != rigRegistry.end()) { rigRegistry.erase(iter); }} void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { UserAnimState::ClipNodeEnum clipNodeEnum; if (_userAnimState.clipNodeEnum == UserAnimState::None || _userAnimState.clipNodeEnum == UserAnimState::B) { clipNodeEnum = UserAnimState::A; } else { clipNodeEnum = UserAnimState::B; } if (_animNode) { // find an unused AnimClip clipNode std::shared_ptr<AnimClip> clip; if (clipNodeEnum == UserAnimState::A) { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("userAnimA")); } else { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("userAnimB")); } if (clip) { // 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); } } // store current user anim state. _userAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; // notify the userAnimStateMachine the desired state. _animVars.set("userAnimNone", false); _animVars.set("userAnimA", clipNodeEnum == UserAnimState::A); _animVars.set("userAnimB", clipNodeEnum == UserAnimState::B);} void Rig::restoreAnimation() { if (_userAnimState.clipNodeEnum != UserAnimState::None) { _userAnimState.clipNodeEnum = UserAnimState::None; // notify the userAnimStateMachine the desired state. _animVars.set("userAnimNone", true); _animVars.set("userAnimA", false); _animVars.set("userAnimB", false); }} void Rig::overrideHandAnimation(bool isLeft, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { HandAnimState::ClipNodeEnum clipNodeEnum; if (isLeft) { if (_leftHandAnimState.clipNodeEnum == HandAnimState::None || _leftHandAnimState.clipNodeEnum == HandAnimState::B) { clipNodeEnum = HandAnimState::A; } else { clipNodeEnum = HandAnimState::B; } } else { if (_rightHandAnimState.clipNodeEnum == HandAnimState::None || _rightHandAnimState.clipNodeEnum == HandAnimState::B) { clipNodeEnum = HandAnimState::A; } else { clipNodeEnum = HandAnimState::B; } } if (_animNode) { std::shared_ptr<AnimClip> clip; if (isLeft) { if (clipNodeEnum == HandAnimState::A) { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("leftHandAnimA")); } else { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("leftHandAnimB")); } } else { if (clipNodeEnum == HandAnimState::A) { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("rightHandAnimA")); } else { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("rightHandAnimB")); } } if (clip) { // 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); } } // notify the handAnimStateMachine the desired state. if (isLeft) { // store current hand anim state. _leftHandAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; _animVars.set("leftHandAnimNone", false); _animVars.set("leftHandAnimA", clipNodeEnum == HandAnimState::A); _animVars.set("leftHandAnimB", clipNodeEnum == HandAnimState::B); } else { // store current hand anim state. _rightHandAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; _animVars.set("rightHandAnimNone", false); _animVars.set("rightHandAnimA", clipNodeEnum == HandAnimState::A); _animVars.set("rightHandAnimB", clipNodeEnum == HandAnimState::B); }} void Rig::restoreHandAnimation(bool isLeft) { if (isLeft) { if (_leftHandAnimState.clipNodeEnum != HandAnimState::None) { _leftHandAnimState.clipNodeEnum = HandAnimState::None; // notify the handAnimStateMachine the desired state. _animVars.set("leftHandAnimNone", true); _animVars.set("leftHandAnimA", false); _animVars.set("leftHandAnimB", false); } } else { if (_rightHandAnimState.clipNodeEnum != HandAnimState::None) { _rightHandAnimState.clipNodeEnum = HandAnimState::None; // notify the handAnimStateMachine the desired state. _animVars.set("rightHandAnimNone", true); _animVars.set("rightHandAnimA", false); _animVars.set("rightHandAnimB", false); } }} void Rig::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { NetworkAnimState::ClipNodeEnum clipNodeEnum = NetworkAnimState::None; if (_networkAnimState.clipNodeEnum == NetworkAnimState::None || _networkAnimState.clipNodeEnum == NetworkAnimState::B) { clipNodeEnum = NetworkAnimState::A; } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::A) { clipNodeEnum = NetworkAnimState::B; } if (_networkNode) { // find an unused AnimClip clipNode std::shared_ptr<AnimClip> clip; if (clipNodeEnum == NetworkAnimState::A) { clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("userNetworkAnimA")); } else { clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("userNetworkAnimB")); } if (clip) { // 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); } } // store current user anim state. _networkAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; // notify the userAnimStateMachine the desired state. _networkVars.set("transitAnimStateMachine", false); _networkVars.set("userNetworkAnimA", clipNodeEnum == NetworkAnimState::A); _networkVars.set("userNetworkAnimB", clipNodeEnum == NetworkAnimState::B); if (!_computeNetworkAnimation) { _networkAnimState.blendTime = 0.0f; _computeNetworkAnimation = true; }}Lines 1–500 of 2852. Load more
// // Rig.cpp // libraries/animation/src/ // // Created by Howard Stearns, Seth Alves, Anthony Thibault, Andrew Meadows on 7/15/15. // Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // Copyright 2023 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // SPDX-License-Identifier: Apache-2.0 // #include "Rig.h" #include <glm/gtx/vector_angle.hpp> #include <queue> #include <QWriteLocker> #include <QReadLocker> #include <GeometryUtil.h> #include <NumericalConstants.h> #include <DebugDraw.h> #include <PerfStat.h> #include <ScriptEngine.h> #include <ScriptManager.h> #include <ScriptValueUtils.h> #include <ScriptValue.h> #include <shared/NsightHelpers.h> #include "AnimationLogging.h" #include "AnimClip.h" #include "AnimInverseKinematics.h" #include "AnimOverlay.h" #include "AnimSkeleton.h" #include "AnimStateMachine.h" #include "AnimUtil.h" #include "AvatarConstants.h" #include "IKTarget.h" #include "PathUtils.h" static int nextRigId = 1; static std::map<int, Rig*> rigRegistry; static std::mutex rigRegistryMutex; static bool isEqual(const float p, float q) { const float EPSILON = 0.00001f; return fabsf(p - q) <= EPSILON; } static bool isEqual(const glm::vec3& u, const glm::vec3& v) { return isEqual(u.x, v.x) && isEqual(u.y, v.y) && isEqual(u.z, v.z); } 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; } #define ASSERT(cond) assert(cond) // 2 meter tall dude 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); static const QString LEFT_FOOT_POSITION("leftFootPosition"); static const QString LEFT_FOOT_ROTATION("leftFootRotation"); static const QString LEFT_FOOT_IK_POSITION_VAR("leftFootIKPositionVar"); static const QString LEFT_FOOT_IK_ROTATION_VAR("leftFootIKRotationVar"); static const QString MAIN_STATE_MACHINE_LEFT_FOOT_POSITION("mainStateMachineLeftFootPosition"); static const QString MAIN_STATE_MACHINE_LEFT_FOOT_ROTATION("mainStateMachineLeftFootRotation"); static const QString RIGHT_FOOT_POSITION("rightFootPosition"); static const QString RIGHT_FOOT_ROTATION("rightFootRotation"); static const QString RIGHT_FOOT_IK_POSITION_VAR("rightFootIKPositionVar"); static const QString RIGHT_FOOT_IK_ROTATION_VAR("rightFootIKRotationVar"); static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_ROTATION("mainStateMachineRightFootRotation"); static const QString MAIN_STATE_MACHINE_RIGHT_FOOT_POSITION("mainStateMachineRightFootPosition"); static const QString LEFT_HAND_POSITION("leftHandPosition"); static const QString LEFT_HAND_ROTATION("leftHandRotation"); static const QString LEFT_HAND_IK_POSITION_VAR("leftHandIKPositionVar"); static const QString LEFT_HAND_IK_ROTATION_VAR("leftHandIKRotationVar"); static const QString MAIN_STATE_MACHINE_LEFT_HAND_POSITION("mainStateMachineLeftHandPosition"); static const QString MAIN_STATE_MACHINE_LEFT_HAND_ROTATION("mainStateMachineLeftHandRotation"); static const QString RIGHT_HAND_POSITION("rightHandPosition"); static const QString RIGHT_HAND_ROTATION("rightHandRotation"); static const QString RIGHT_HAND_IK_POSITION_VAR("rightHandIKPositionVar"); static const QString RIGHT_HAND_IK_ROTATION_VAR("rightHandIKRotationVar"); static const QString MAIN_STATE_MACHINE_RIGHT_HAND_ROTATION("mainStateMachineRightHandRotation"); static const QString MAIN_STATE_MACHINE_RIGHT_HAND_POSITION("mainStateMachineRightHandPosition"); /*@jsdoc * <p>An <code>AnimStateDictionary</code> object may have the following properties. It may also have other properties, set by * scripts.</p> * <p><strong>Warning:</strong> These properties are subject to change. * <table> * <thead> * <tr><th>Name</th><th>Type</th><th>Description</th> * </thead> * <tbody> * <tr><td><code>userAnimNone</code></td><td>boolean</td><td><code>true</code> when no user overrideAnimation is * playing.</td></tr> * <tr><td><code>userAnimA</code></td><td>boolean</td><td><code>true</code> when a user overrideAnimation is * playing.</td></tr> * <tr><td><code>userAnimB</code></td><td>boolean</td><td><code>true</code> when a user overrideAnimation is * playing.</td></tr> * * <tr><td><code>sine</code></td><td>number</td><td>Oscillating sine wave.</td></tr> * <tr><td><code>moveForwardSpeed</code></td><td>number</td><td>Controls the blend between the various forward walking * & running animations.</td></tr> * <tr><td><code>moveBackwardSpeed</code></td><td>number</td><td>Controls the blend between the various backward walking * & running animations.</td></tr> * <tr><td><code>moveLateralSpeed</code></td><td>number</td><td>Controls the blend between the various sidestep walking * & running animations.</td></tr> * * <tr><td><code>isMovingForward</code></td><td>boolean</td><td><code>true</code> if the avatar is moving * forward.</td></tr> * <tr><td><code>isMovingBackward</code></td><td>boolean</td><td><code>true</code> if the avatar is moving * backward.</td></tr> * <tr><td><code>isMovingRight</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the * right.</td></tr> * <tr><td><code>isMovingLeft</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the * left.</td></tr> * <tr><td><code>isMovingRightHmd</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the right * while the user is in HMD mode.</td></tr> * <tr><td><code>isMovingLeftHmd</code></td><td>boolean</td><td><code>true</code> if the avatar is moving to the left while * the user is in HMD mode.</td></tr> * <tr><td><code>isNotMoving</code></td><td>boolean</td><td><code>true</code> if the avatar is stationary.</td></tr> * * <tr><td><code>isTurningRight</code></td><td>boolean</td><td><code>true</code> if the avatar is turning * clockwise.</td></tr> * <tr><td><code>isTurningLeft</code></td><td>boolean</td><td><code>true</code> if the avatar is turning * counter-clockwise.</td></tr> * <tr><td><code>isNotTurning</code></td><td>boolean</td><td><code>true</code> if the avatar is not turning.</td></tr> * <tr><td><code>isFlying</code></td><td>boolean</td><td><code>true</code> if the avatar is flying.</td></tr> * <tr><td><code>isNotFlying</code></td><td>boolean</td><td><code>true</code> if the avatar is not flying.</td></tr> * <tr><td><code>isTakeoffStand</code></td><td>boolean</td><td><code>true</code> if the avatar is about to execute a * standing jump.</td></tr> * <tr><td><code>isTakeoffRun</code></td><td>boolean</td><td><code>true</code> if the avatar is about to execute a running * jump.</td></tr> * <tr><td><code>isNotTakeoff</code></td><td>boolean</td><td><code>true</code> if the avatar is not jumping.</td></tr> * <tr><td><code>isInAirStand</code></td><td>boolean</td><td><code>true</code> if the avatar is in the air after a standing * jump.</td></tr> * <tr><td><code>isInAirRun</code></td><td>boolean</td><td><code>true</code> if the avatar is in the air after a running * jump.</td></tr> * <tr><td><code>isNotInAir</code></td><td>boolean</td><td><code>true</code> if the avatar on the ground.</td></tr> * * <tr><td><code>inAirAlpha</code></td><td>number</td><td>Used to interpolate between the up, apex, and down in-air * animations.</td></tr> * <tr><td><code>ikOverlayAlpha</code></td><td>number</td><td>The blend between upper body and spline IK versus the * underlying animation</td></tr> * * <tr><td><code>headPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>Head</code> joint in * rig coordinates.</td></tr> * <tr><td><code>headRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>Head</code> joint in * rig coordinates.</td></tr> * <tr><td><code>headType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the * head.</td></tr> * <tr><td><code>headWeight</code></td><td>number</td><td>How strongly the head chain blends with the other IK * chains.</td></tr> * * <tr><td><code>leftHandPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>LeftHand</code> * joint in rig coordinates.</td></tr> * <tr><td><code>leftHandRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>LeftHand</code> * joint in rig coordinates.</td></tr> * <tr><td><code>leftHandType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the * left arm.</td></tr> * <tr><td><code>leftHandPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the elbow angle is * controlled by the <code>rightHandPoleVector</code> property value. Otherwise the elbow direction comes from the * underlying animation.</td></tr> * <tr><td><code>leftHandPoleReferenceVector</code></td><td>{@link Vec3}</td><td>The direction of the elbow in the local * coordinate system of the elbow.</td></tr> * <tr><td><code>leftHandPoleVector</code></td><td>{@link Vec3}</td><td>The direction the elbow should point in rig * coordinates.</td></tr> * * <tr><td><code>rightHandPosition</code></td><td>{@link Vec3}</td><td>The desired position of the <code>RightHand</code> * joint in rig coordinates.</td></tr> * <tr><td><code>rightHandRotation</code></td><td>{@link Quat}</td><td>The desired orientation of the * <code>RightHand</code> joint in rig coordinates.</td></tr> * <tr><td><code>rightHandType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for * the right arm.</td></tr> * <tr><td><code>rightHandPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the elbow angle is * controlled by the <code>rightHandPoleVector</code> property value. Otherwise the elbow direction comes from the * underlying animation.</td></tr> * <tr><td><code>rightHandPoleReferenceVector</code></td><td>{@link Vec3}</td><td>The direction of the elbow in the local * coordinate system of the elbow.</td></tr> * <tr><td><code>rightHandPoleVector</code></td><td>{@link Vec3}</td><td>The direction the elbow should point in rig * coordinates.</td></tr> * * <tr><td><code>leftFootIKEnabled</code></td><td>boolean</td><td><code>true</code> if IK is enabled for the left * foot.</td></tr> * <tr><td><code>rightFootIKEnabled</code></td><td>boolean</td><td><code>true</code> if IK is enabled for the right * foot.</td></tr> * * <tr><td><code>leftFootIKPositionVar</code></td><td>string</td><td>The name of the source for the desired position * of the <code>LeftFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr> * <tr><td><code>leftFootIKRotationVar</code></td><td>string</td><td>The name of the source for the desired rotation * of the <code>LeftFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr> * <tr><td><code>leftFootPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the knee angle is * controlled by the <code>leftFootPoleVector</code> property value. Otherwise the knee direction comes from the * underlying animation.</td></tr> * <tr><td><code>leftFootPoleVector</code></td><td>{@link Vec3}</td><td>The direction the knee should face in rig * coordinates.</td></tr> * <tr><td><code>rightFootIKPositionVar</code></td><td>string</td><td>The name of the source for the desired position * of the <code>RightFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr> * <tr><td><code>rightFootIKRotationVar</code></td><td>string</td><td>The name of the source for the desired rotation * of the <code>RightFoot</code> joint. If not set, the foot rotation of the underlying animation will be used.</td></tr> * <tr><td><code>rightFootPoleVectorEnabled</code></td><td>boolean</td><td>When <code>true</code>, the knee angle is * controlled by the <code>rightFootPoleVector</code> property value. Otherwise the knee direction comes from the * underlying animation.</td></tr> * <tr><td><code>rightFootPoleVector</code></td><td>{@link Vec3}</td><td>The direction the knee should face in rig * coordinates.</td></tr> * * <tr><td><code>isTalking</code></td><td>boolean</td><td><code>true</code> if the avatar is talking.</td></tr> * <tr><td><code>notIsTalking</code></td><td>boolean</td><td><code>true</code> if the avatar is not talking.</td></tr> * * <tr><td><code>solutionSource</code></td><td>{@link MyAvatar.AnimIKSolutionSource|AnimIKSolutionSource}</td> * <td>Determines the initial conditions of the IK solver.</td></tr> * <tr><td><code>defaultPoseOverlayAlpha</code></td><td>number</td><td>Controls the blend between the main animation state * machine and the default pose. Mostly used during full body tracking so that walking & jumping animations do not * affect the IK of the figure.</td></tr> * <tr><td><code>defaultPoseOverlayBoneSet</code></td><td>{@link MyAvatar.AnimOverlayBoneSet|AnimOverlayBoneSet}</td> * <td>Specifies which bones will be replace by the source overlay.</td></tr> * <tr><td><code>hipsType</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the * hips.</td></tr> * <tr><td><code>hipsPosition</code></td><td>{@link Vec3}</td><td>The desired position of <code>Hips</code> joint in rig * coordinates.</td></tr> * <tr><td><code>hipsRotation</code></td><td>{@link Quat}</td><td>the desired orientation of the <code>Hips</code> joint in * rig coordinates.</td></tr> * <tr><td><code>spine2Type</code></td><td>{@link MyAvatar.IKTargetType|IKTargetType}</td><td>The type of IK used for the * <code>Spine2</code> joint.</td></tr> * <tr><td><code>spine2Position</code></td><td>{@link Vec3}</td><td>The desired position of the <code>Spine2</code> joint * in rig coordinates.</td></tr> * <tr><td><code>spine2Rotation</code></td><td>{@link Quat}</td><td>The desired orientation of the <code>Spine2</code> * joint in rig coordinates.</td></tr> * * <tr><td><code>leftFootIKAlpha</code></td><td>number</td><td>Blends between full IK for the leg and the underlying * animation.</td></tr> * <tr><td><code>rightFootIKAlpha</code></td><td>number</td><td>Blends between full IK for the leg and the underlying * animation.</td></tr> * <tr><td><code>hipsWeight</code></td><td>number</td><td>How strongly the hips target blends with the IK solution for * other IK chains.</td></tr> * <tr><td><code>leftHandWeight</code></td><td>number</td><td>How strongly the left hand blends with IK solution of other * IK chains.</td></tr> * <tr><td><code>rightHandWeight</code></td><td>number</td><td>How strongly the right hand blends with IK solution of other * IK chains.</td></tr> * <tr><td><code>spine2Weight</code></td><td>number</td><td>How strongly the spine2 chain blends with the rest of the IK * solution.</td></tr> * * <tr><td><code>leftHandOverlayAlpha</code></td><td>number</td><td>Used to blend in the animated hand gesture poses, such * as point and thumbs up.</td></tr> * <tr><td><code>leftHandGraspAlpha</code></td><td>number</td><td>Used to blend between an open hand and a closed hand. * Usually changed as you squeeze the trigger of the hand controller.</td></tr> * <tr><td><code>rightHandOverlayAlpha</code></td><td>number</td><td>Used to blend in the animated hand gesture poses, * such as point and thumbs up.</td></tr> * <tr><td><code>rightHandGraspAlpha</code></td><td>number</td><td>Used to blend between an open hand and a closed hand. * Usually changed as you squeeze the trigger of the hand controller.</td></tr> * <tr><td><code>isLeftIndexPoint</code></td><td>boolean</td><td><code>true</code> if the left hand should be * pointing.</td></tr> * <tr><td><code>isLeftThumbRaise</code></td><td>boolean</td><td><code>true</code> if the left hand should be * thumbs-up.</td></tr> * <tr><td><code>isLeftIndexPointAndThumbRaise</code></td><td>boolean</td><td><code>true</code> if the left hand should be * pointing and thumbs-up.</td></tr> * <tr><td><code>isLeftHandGrasp</code></td><td>boolean</td><td><code>true</code> if the left hand should be at rest, * grasping the controller.</td></tr> * <tr><td><code>isRightIndexPoint</code></td><td>boolean</td><td><code>true</code> if the right hand should be * pointing.</td></tr> * <tr><td><code>isRightThumbRaise</code></td><td>boolean</td><td><code>true</code> if the right hand should be * thumbs-up.</td></tr> * <tr><td><code>isRightIndexPointAndThumbRaise</code></td><td>boolean</td><td><code>true</code> if the right hand should * be pointing and thumbs-up.</td></tr> * <tr><td><code>isRightHandGrasp</code></td><td>boolean</td><td><code>true</code> if the right hand should be at rest, * grasping the controller.</td></tr> * * </tbody> * </table> * <p>Note: Rig coordinates are <code>+z</code> forward and <code>+y</code> up.</p> * @typedef {object} MyAvatar.AnimStateDictionary */ // Note: The following animVars are intentionally not documented: // - leftFootPosition // - leftFootRotation // - rightFooKPosition // - rightFooKRotation // Note: The following items aren't set in the code below but are still intentionally documented: // - leftFootIKAlpha // - rightFootIKAlpha // - hipsWeight // - leftHandWeight // - rightHandWeight // - spine2Weight // - rightHandOverlayAlpha // - rightHandGraspAlpha // - leftHandOverlayAlpha // - leftHandGraspAlpha // - isRightIndexPoint // - isRightThumbRaise // - isRightIndexPointAndThumbRaise // - isRightHandGrasp // - isLeftIndexPoint // - isLeftThumbRaise // - isLeftIndexPointAndThumbRaise // - isLeftHandGrasp Rig::Rig() : _internalFlow(this), _networkFlow(this) { // Ensure thread-safe access to the rigRegistry. std::lock_guard<std::mutex> guard(rigRegistryMutex); // Insert this newly allocated rig into the rig registry _rigId = nextRigId; rigRegistry[_rigId] = this; nextRigId++; } Rig::~Rig() { // Ensure thread-safe access to the rigRegstry, but also prevent the rig from being deleted // while Rig::animationStateHandlerResult is being invoked on a script thread. std::lock_guard<std::mutex> guard(rigRegistryMutex); auto iter = rigRegistry.find(_rigId); if (iter != rigRegistry.end()) { rigRegistry.erase(iter); } } void Rig::overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { UserAnimState::ClipNodeEnum clipNodeEnum; if (_userAnimState.clipNodeEnum == UserAnimState::None || _userAnimState.clipNodeEnum == UserAnimState::B) { clipNodeEnum = UserAnimState::A; } else { clipNodeEnum = UserAnimState::B; } if (_animNode) { // find an unused AnimClip clipNode std::shared_ptr<AnimClip> clip; if (clipNodeEnum == UserAnimState::A) { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("userAnimA")); } else { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("userAnimB")); } if (clip) { // 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); } } // store current user anim state. _userAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; // notify the userAnimStateMachine the desired state. _animVars.set("userAnimNone", false); _animVars.set("userAnimA", clipNodeEnum == UserAnimState::A); _animVars.set("userAnimB", clipNodeEnum == UserAnimState::B); } void Rig::restoreAnimation() { if (_userAnimState.clipNodeEnum != UserAnimState::None) { _userAnimState.clipNodeEnum = UserAnimState::None; // notify the userAnimStateMachine the desired state. _animVars.set("userAnimNone", true); _animVars.set("userAnimA", false); _animVars.set("userAnimB", false); } } void Rig::overrideHandAnimation(bool isLeft, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { HandAnimState::ClipNodeEnum clipNodeEnum; if (isLeft) { if (_leftHandAnimState.clipNodeEnum == HandAnimState::None || _leftHandAnimState.clipNodeEnum == HandAnimState::B) { clipNodeEnum = HandAnimState::A; } else { clipNodeEnum = HandAnimState::B; } } else { if (_rightHandAnimState.clipNodeEnum == HandAnimState::None || _rightHandAnimState.clipNodeEnum == HandAnimState::B) { clipNodeEnum = HandAnimState::A; } else { clipNodeEnum = HandAnimState::B; } } if (_animNode) { std::shared_ptr<AnimClip> clip; if (isLeft) { if (clipNodeEnum == HandAnimState::A) { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("leftHandAnimA")); } else { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("leftHandAnimB")); } } else { if (clipNodeEnum == HandAnimState::A) { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("rightHandAnimA")); } else { clip = std::dynamic_pointer_cast<AnimClip>(_animNode->findByName("rightHandAnimB")); } } if (clip) { // 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); } } // notify the handAnimStateMachine the desired state. if (isLeft) { // store current hand anim state. _leftHandAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; _animVars.set("leftHandAnimNone", false); _animVars.set("leftHandAnimA", clipNodeEnum == HandAnimState::A); _animVars.set("leftHandAnimB", clipNodeEnum == HandAnimState::B); } else { // store current hand anim state. _rightHandAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; _animVars.set("rightHandAnimNone", false); _animVars.set("rightHandAnimA", clipNodeEnum == HandAnimState::A); _animVars.set("rightHandAnimB", clipNodeEnum == HandAnimState::B); } } void Rig::restoreHandAnimation(bool isLeft) { if (isLeft) { if (_leftHandAnimState.clipNodeEnum != HandAnimState::None) { _leftHandAnimState.clipNodeEnum = HandAnimState::None; // notify the handAnimStateMachine the desired state. _animVars.set("leftHandAnimNone", true); _animVars.set("leftHandAnimA", false); _animVars.set("leftHandAnimB", false); } } else { if (_rightHandAnimState.clipNodeEnum != HandAnimState::None) { _rightHandAnimState.clipNodeEnum = HandAnimState::None; // notify the handAnimStateMachine the desired state. _animVars.set("rightHandAnimNone", true); _animVars.set("rightHandAnimA", false); _animVars.set("rightHandAnimB", false); } } } void Rig::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) { NetworkAnimState::ClipNodeEnum clipNodeEnum = NetworkAnimState::None; if (_networkAnimState.clipNodeEnum == NetworkAnimState::None || _networkAnimState.clipNodeEnum == NetworkAnimState::B) { clipNodeEnum = NetworkAnimState::A; } else if (_networkAnimState.clipNodeEnum == NetworkAnimState::A) { clipNodeEnum = NetworkAnimState::B; } if (_networkNode) { // find an unused AnimClip clipNode std::shared_ptr<AnimClip> clip; if (clipNodeEnum == NetworkAnimState::A) { clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("userNetworkAnimA")); } else { clip = std::dynamic_pointer_cast<AnimClip>(_networkNode->findByName("userNetworkAnimB")); } if (clip) { // 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); } } // store current user anim state. _networkAnimState = { clipNodeEnum, url, fps, loop, firstFrame, lastFrame }; // notify the userAnimStateMachine the desired state. _networkVars.set("transitAnimStateMachine", false); _networkVars.set("userNetworkAnimA", clipNodeEnum == NetworkAnimState::A); _networkVars.set("userNetworkAnimB", clipNodeEnum == NetworkAnimState::B); if (!_computeNetworkAnimation) { _networkAnimState.blendTime = 0.0f; _computeNetworkAnimation = true; } }