diff --git a/interface/resources/avatar/network-animation.json b/interface/resources/avatar/network-animation.json
index 0ba4ea465c..3db40742c9 100644
--- a/interface/resources/avatar/network-animation.json
+++ b/interface/resources/avatar/network-animation.json
@@ -1,70 +1,138 @@
{
"version": "1.1",
"root": {
- "id": "userAnimStateMachine",
+ "id": "networkAnimStateMachine",
"type": "stateMachine",
"data": {
- "currentState": "idleAnim",
+ "currentState": "transitAnimStateMachine",
"states": [
{
- "id": "idleAnim",
+ "id": "transitAnimStateMachine",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
- { "var": "postTransitAnim", "state": "postTransitAnim" },
- { "var": "preTransitAnim", "state": "preTransitAnim" }
+ { "var": "userNetworkAnimA", "state": "userNetworkAnimA" },
+ { "var": "userNetworkAnimB", "state": "userNetworkAnimB" }
]
},
{
- "id": "preTransitAnim",
+ "id": "userNetworkAnimA",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
- { "var": "idleAnim", "state": "idleAnim" },
- { "var": "transitAnim", "state": "transitAnim" }
+ { "var": "transitAnimStateMachine", "state": "transitAnimStateMachine" },
+ { "var": "userNetworkAnimB", "state": "userNetworkAnimB" }
]
},
{
- "id": "transitAnim",
+ "id": "userNetworkAnimB",
"interpTarget": 6,
"interpDuration": 6,
"transitions": [
- { "var": "preTransitAnim", "state": "preTransitAnim" },
- { "var": "postTransitAnim", "state": "postTransitAnim" }
- ]
- },
- {
- "id": "postTransitAnim",
- "interpTarget": 6,
- "interpDuration": 6,
- "transitions": [
- { "var": "transitAnim", "state": "transitAnim" },
- { "var": "idleAnim", "state": "idleAnim" }
- ]
- },
- {
- "id": "userAnimA",
- "interpTarget": 6,
- "interpDuration": 6,
- "transitions": [
- { "var": "idleAnim", "state": "idleAnim" },
- { "var": "userAnimB", "state": "userAnimB" }
- ]
- },
- {
- "id": "userAnimB",
- "interpTarget": 6,
- "interpDuration": 6,
- "transitions": [
- { "var": "idleAnim", "state": "idleAnim" },
- { "var": "userAnimA", "state": "userAnimA" }
+ { "var": "transitAnimStateMachine", "state": "transitAnimStateMachine" },
+ { "var": "userNetworkAnimA", "state": "userNetworkAnimA" }
]
}
]
},
"children": [
{
- "id": "idleAnim",
+ "id": "transitAnimStateMachine",
+ "type": "stateMachine",
+ "data": {
+ "currentState": "idleAnim",
+ "states": [
+ {
+ "id": "idleAnim",
+ "interpTarget": 6,
+ "interpDuration": 6,
+ "transitions": [
+ { "var": "postTransitAnim", "state": "postTransitAnim" },
+ { "var": "preTransitAnim", "state": "preTransitAnim" }
+ ]
+ },
+ {
+ "id": "preTransitAnim",
+ "interpTarget": 6,
+ "interpDuration": 6,
+ "transitions": [
+ { "var": "idleAnim", "state": "idleAnim" },
+ { "var": "transitAnim", "state": "transitAnim" }
+ ]
+ },
+ {
+ "id": "transitAnim",
+ "interpTarget": 6,
+ "interpDuration": 6,
+ "transitions": [
+ { "var": "preTransitAnim", "state": "preTransitAnim" },
+ { "var": "postTransitAnim", "state": "postTransitAnim" }
+ ]
+ },
+ {
+ "id": "postTransitAnim",
+ "interpTarget": 6,
+ "interpDuration": 6,
+ "transitions": [
+ { "var": "transitAnim", "state": "transitAnim" },
+ { "var": "idleAnim", "state": "idleAnim" }
+ ]
+ }
+ ]
+ },
+ "children" : [
+ {
+ "id": "idleAnim",
+ "type": "clip",
+ "data": {
+ "url": "qrc:///avatar/animations/idle.fbx",
+ "startFrame": 0.0,
+ "endFrame": 90.0,
+ "timeScale": 1.0,
+ "loopFlag": true
+ },
+ "children": []
+ },
+ {
+ "id": "preTransitAnim",
+ "type": "clip",
+ "data": {
+ "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
+ "startFrame": 0.0,
+ "endFrame": 10.0,
+ "timeScale": 1.0,
+ "loopFlag": false
+ },
+ "children": []
+ },
+ {
+ "id": "transitAnim",
+ "type": "clip",
+ "data": {
+ "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
+ "startFrame": 11.0,
+ "endFrame": 11.0,
+ "timeScale": 1.0,
+ "loopFlag": true
+ },
+ "children": []
+ },
+ {
+ "id": "postTransitAnim",
+ "type": "clip",
+ "data": {
+ "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
+ "startFrame": 22.0,
+ "endFrame": 49.0,
+ "timeScale": 1.0,
+ "loopFlag": false
+ },
+ "children": []
+ }
+ ]
+ },
+ {
+ "id": "userNetworkAnimA",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/idle.fbx",
@@ -76,55 +144,7 @@
"children": []
},
{
- "id": "preTransitAnim",
- "type": "clip",
- "data": {
- "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
- "startFrame": 0.0,
- "endFrame": 10.0,
- "timeScale": 1.0,
- "loopFlag": false
- },
- "children": []
- },
- {
- "id": "transitAnim",
- "type": "clip",
- "data": {
- "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
- "startFrame": 11.0,
- "endFrame": 11.0,
- "timeScale": 1.0,
- "loopFlag": true
- },
- "children": []
- },
- {
- "id": "postTransitAnim",
- "type": "clip",
- "data": {
- "url": "https://hifi-content.s3.amazonaws.com/luis/test_scripts/transitApp/animations/teleport01_warp.fbx",
- "startFrame": 22.0,
- "endFrame": 49.0,
- "timeScale": 1.0,
- "loopFlag": false
- },
- "children": []
- },
- {
- "id": "userAnimA",
- "type": "clip",
- "data": {
- "url": "qrc:///avatar/animations/idle.fbx",
- "startFrame": 0.0,
- "endFrame": 90.0,
- "timeScale": 1.0,
- "loopFlag": true
- },
- "children": []
- },
- {
- "id": "userAnimB",
+ "id": "userNetworkAnimB",
"type": "clip",
"data": {
"url": "qrc:///avatar/animations/idle.fbx",
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index e12ea67230..0e1dc947f1 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -128,19 +128,19 @@ void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) {
switch (status) {
case AvatarTransit::Status::STARTED:
qDebug() << "START_FRAME";
- _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("preTransitAnim");
+ _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("preTransitAnim");
break;
case AvatarTransit::Status::START_TRANSIT:
qDebug() << "START_TRANSIT";
- _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("transitAnim");
+ _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("transitAnim");
break;
case AvatarTransit::Status::END_TRANSIT:
qDebug() << "END_TRANSIT";
- _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("postTransitAnim");
+ _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("postTransitAnim");
break;
case AvatarTransit::Status::ENDED:
qDebug() << "END_FRAME";
- _myAvatar->getSkeletonModel()->getRig().triggerNetworkAnimation("idleAnim");
+ _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("idleAnim");
break;
case AvatarTransit::Status::PRE_TRANSIT:
break;
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 82baec628d..c502bcb40d 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -1162,6 +1162,15 @@ void MyAvatar::overrideAnimation(const QString& url, float fps, bool loop, float
_skeletonModel->getRig().overrideAnimation(url, fps, loop, firstFrame, lastFrame);
}
+void MyAvatar::overrideNetworkAnimation(const QString& url, float fps, bool loop, float firstFrame, float lastFrame) {
+ if (QThread::currentThread() != thread()) {
+ QMetaObject::invokeMethod(this, "overrideNetworkAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps),
+ Q_ARG(bool, loop), Q_ARG(float, firstFrame), Q_ARG(float, lastFrame));
+ return;
+ }
+ _skeletonModel->getRig().overrideNetworkAnimation(url, fps, loop, firstFrame, lastFrame);
+}
+
void MyAvatar::restoreAnimation() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "restoreAnimation");
@@ -1170,6 +1179,14 @@ void MyAvatar::restoreAnimation() {
_skeletonModel->getRig().restoreAnimation();
}
+void MyAvatar::restoreNetworkAnimation() {
+ if (QThread::currentThread() != thread()) {
+ QMetaObject::invokeMethod(this, "restoreNetworkAnimation");
+ return;
+ }
+ _skeletonModel->getRig().restoreNetworkAnimation();
+}
+
QStringList MyAvatar::getAnimationRoles() {
if (QThread::currentThread() != thread()) {
QStringList result;
diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h
index 08a7c09fa4..64cc3e6357 100644
--- a/interface/src/avatar/MyAvatar.h
+++ b/interface/src/avatar/MyAvatar.h
@@ -393,6 +393,47 @@ public:
*/
Q_INVOKABLE void restoreAnimation();
+ /**jsdoc
+ * Similarly to {@link MyAvatar.overrideAnimation}, this function will override the default avatar animations,
+ * but only affecting the data the gets sent to other clients over the network. In the client, the avatar will move according
+ * to the default animations but other clients will render your avatar movements according to the new animation.
+ * To continue sending the default animations, use {@link MyAvatar.restoreNetworkAnimation}.
+ *
Note: When using pre-built animation data, it's critical that the joint orientation of the source animation and target + * rig are equivalent, since the animation data applies absolute values onto the joints. If the orientations are different, + * the avatar will move in unpredictable ways. For more information about avatar joint orientation standards, see + * Avatar Standards.
+ * @function MyAvatar.overrideNetworkAnimation + * @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