mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 16:10:40 +02:00
Merge pull request #15271 from amantley/overrideHandAnimsSquashed
Allow Hand Animations to be Overridden
This commit is contained in:
commit
da314694b4
5 changed files with 522 additions and 221 deletions
|
@ -196,6 +196,42 @@
|
|||
{
|
||||
"id": "rightHandStateMachine",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "rightHandAnimNone",
|
||||
"states": [
|
||||
{
|
||||
"id": "rightHandAnimNone",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "rightHandAnimA", "state": "rightHandAnimA" },
|
||||
{ "var": "rightHandAnimB", "state": "rightHandAnimB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightHandAnimA",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "rightHandAnimNone", "state": "rightHandAnimNone" },
|
||||
{ "var": "rightHandAnimB", "state": "rightHandAnimB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightHandAnimB",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "rightHandAnimNone", "state": "rightHandAnimNone" },
|
||||
{ "var": "rightHandAnimA", "state": "rightHandAnimA" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "rightHandAnimNone",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "rightHandGrasp",
|
||||
"states": [
|
||||
|
@ -379,6 +415,32 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rightHandAnimA",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "rightHandAnimB",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_right.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandOverlay",
|
||||
|
@ -392,6 +454,42 @@
|
|||
{
|
||||
"id": "leftHandStateMachine",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "leftHandAnimNone",
|
||||
"states": [
|
||||
{
|
||||
"id": "leftHandAnimNone",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandAnimA", "state": "leftHandAnimA" },
|
||||
{ "var": "leftHandAnimB", "state": "leftHandAnimB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandAnimA",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandAnimNone", "state": "leftHandAnimNone" },
|
||||
{ "var": "leftHandAnimB", "state": "leftHandAnimB" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandAnimB",
|
||||
"interpTarget": 1,
|
||||
"interpDuration": 3,
|
||||
"transitions": [
|
||||
{ "var": "leftHandAnimNone", "state": "leftHandAnimNone" },
|
||||
{ "var": "leftHandAnimA", "state": "leftHandAnimA" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "leftHandAnimNone",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"currentState": "leftHandGrasp",
|
||||
"states": [
|
||||
|
@ -576,6 +674,32 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "leftHandAnimA",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "leftHandAnimB",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "qrc:///avatar/animations/touch_thumb_point_open_left.fbx",
|
||||
"startFrame": 15.0,
|
||||
"endFrame": 15.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mainStateMachine",
|
||||
"type": "stateMachine",
|
||||
|
|
|
@ -1199,6 +1199,15 @@ void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float
|
|||
_skeletonModel->getRig().overrideAnimation(url, fps, loop, firstFrame, lastFrame);
|
||||
}
|
||||
|
||||
void MyAvatar::overrideHandAnimation(bool isLeft, const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "overrideHandAnimation", Q_ARG(bool, isLeft), Q_ARG(const QString&, url), Q_ARG(float, fps),
|
||||
Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame));
|
||||
return;
|
||||
}
|
||||
_skeletonModel->getRig().overrideHandAnimation(isLeft, url, fps, loop, firstFrame, lastFrame);
|
||||
}
|
||||
|
||||
void MyAvatar::restoreAnimation() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "restoreAnimation");
|
||||
|
@ -1207,6 +1216,14 @@ void MyAvatar::restoreAnimation() {
|
|||
_skeletonModel->getRig().restoreAnimation();
|
||||
}
|
||||
|
||||
void MyAvatar::restoreHandAnimation(bool isLeft) {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "restoreHandAnimation", Q_ARG(bool, isLeft));
|
||||
return;
|
||||
}
|
||||
_skeletonModel->getRig().restoreHandAnimation(isLeft);
|
||||
}
|
||||
|
||||
QStringList MyAvatar::getAnimationRoles() {
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QStringList result;
|
||||
|
|
|
@ -597,6 +597,26 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void overrideAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
|
||||
/**jsdoc
|
||||
* <code>overrideHandAnimation()</code> Gets the overrides the default hand poses that are triggered with controller buttons.
|
||||
* use {@link MyAvatar.restoreHandAnimation}.</p> to restore the default poses.
|
||||
* @function MyAvatar.overrideHandAnimation
|
||||
* @param isLeft {boolean} Set true if using the left hand
|
||||
* @param url {string} The URL to the animation file. Animation files need to be FBX format, but only need to contain the
|
||||
* avatar skeleton and animation data.
|
||||
* @param fps {number} The frames per second (FPS) rate for the animation playback. 30 FPS is normal speed.
|
||||
* @param loop {boolean} Set to true if the animation should loop.
|
||||
* @param firstFrame {number} The frame the animation should start at.
|
||||
* @param lastFrame {number} The frame the animation should end at
|
||||
* @example <caption> Override left hand animation for three seconds. </caption>
|
||||
* // Override the left hand pose then restore the default pose.
|
||||
* MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53);
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.restoreHandAnimation();
|
||||
* }, 3000);
|
||||
*/
|
||||
Q_INVOKABLE void overrideHandAnimation(bool isLeft, const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
|
||||
/**jsdoc
|
||||
* Restores the default animations.
|
||||
* <p>The avatar animation system includes a set of default animations along with rules for how those animations are blended
|
||||
|
@ -615,6 +635,24 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE void restoreAnimation();
|
||||
|
||||
/**jsdoc
|
||||
* Restores the default hand animation state machine that is driven by the state machine in the avatar-animation json.
|
||||
* <p>The avatar animation system includes a set of default animations along with rules for how those animations are blended
|
||||
* together with procedural data (such as look at vectors, hand sensors etc.). Playing your own custom animations will
|
||||
* override the default animations. <code>restoreHandAnimation()</code> is used to restore the default hand poses
|
||||
* If you aren't currently playing an override hand
|
||||
* animation, this function has no effect.</p>
|
||||
* @function MyAvatar.restoreHandAnimation
|
||||
* @param isLeft {boolean} Set to true if using the left hand
|
||||
* @example <caption> Override left hand animation for three seconds. </caption>
|
||||
* // Override the left hand pose then restore the default pose.
|
||||
* MyAvatar.overrideHandAnimation(isLeft, ANIM_URL, 30, true, 0, 53);
|
||||
* Script.setTimeout(function () {
|
||||
* MyAvatar.restoreHandAnimation();
|
||||
* }, 3000);
|
||||
*/
|
||||
Q_INVOKABLE void restoreHandAnimation(bool isLeft);
|
||||
|
||||
/**jsdoc
|
||||
* Gets the current animation roles.
|
||||
* <p>Each avatar has an avatar-animation.json file that defines which animations are used and how they are blended together
|
||||
|
|
|
@ -370,6 +370,88 @@ void Rig::restoreAnimation() {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -2068,6 +2150,20 @@ void Rig::initAnimGraph(const QUrl& url) {
|
|||
overrideAnimation(origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
|
||||
}
|
||||
|
||||
if (_rightHandAnimState.clipNodeEnum != HandAnimState::None) {
|
||||
// restore the right hand animation we had before reset.
|
||||
HandAnimState origState = _rightHandAnimState;
|
||||
_rightHandAnimState = { HandAnimState::None, "", 30.0f, false, 0.0f, 0.0f };
|
||||
overrideHandAnimation(false, origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
|
||||
}
|
||||
|
||||
if (_leftHandAnimState.clipNodeEnum != HandAnimState::None) {
|
||||
// restore the left hand animation we had before reset.
|
||||
HandAnimState origState = _leftHandAnimState;
|
||||
_leftHandAnimState = { HandAnimState::None, "", 30.0f, false, 0.0f, 0.0f };
|
||||
overrideHandAnimation(true, origState.url, origState.fps, origState.loop, origState.firstFrame, origState.lastFrame);
|
||||
}
|
||||
|
||||
// restore the role animations we had before reset.
|
||||
for (auto& roleAnimState : _roleAnimStates) {
|
||||
auto roleState = roleAnimState.second;
|
||||
|
|
|
@ -119,6 +119,9 @@ public:
|
|||
bool isPlayingOverrideAnimation() const { return _userAnimState.clipNodeEnum != UserAnimState::None; };
|
||||
void restoreAnimation();
|
||||
|
||||
void overrideHandAnimation(bool isLeft, const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
void restoreHandAnimation(bool isLeft);
|
||||
|
||||
void overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame);
|
||||
void triggerNetworkRole(const QString& role);
|
||||
void restoreNetworkAnimation();
|
||||
|
@ -357,6 +360,27 @@ protected:
|
|||
float blendTime;
|
||||
};
|
||||
|
||||
struct HandAnimState {
|
||||
enum ClipNodeEnum {
|
||||
None = 0,
|
||||
A,
|
||||
B
|
||||
};
|
||||
|
||||
HandAnimState() : clipNodeEnum(HandAnimState::None) {}
|
||||
HandAnimState(ClipNodeEnum clipNodeEnumIn, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) :
|
||||
clipNodeEnum(clipNodeEnumIn), url(urlIn), fps(fpsIn), loop(loopIn), firstFrame(firstFrameIn), lastFrame(lastFrameIn) {
|
||||
}
|
||||
|
||||
|
||||
ClipNodeEnum clipNodeEnum;
|
||||
QString url;
|
||||
float fps;
|
||||
bool loop;
|
||||
float firstFrame;
|
||||
float lastFrame;
|
||||
};
|
||||
|
||||
struct UserAnimState {
|
||||
enum ClipNodeEnum {
|
||||
None = 0,
|
||||
|
@ -391,6 +415,8 @@ protected:
|
|||
|
||||
UserAnimState _userAnimState;
|
||||
NetworkAnimState _networkAnimState;
|
||||
HandAnimState _rightHandAnimState;
|
||||
HandAnimState _leftHandAnimState;
|
||||
std::map<QString, RoleAnimState> _roleAnimStates;
|
||||
|
||||
float _leftHandOverlayAlpha { 0.0f };
|
||||
|
|
Loading…
Reference in a new issue