mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 19:29:47 +02:00
Merge pull request #15445 from amantley/AnimRandomSwitchSquash
AnimRandomSwitch class for random idle implementation
This commit is contained in:
commit
93b03a588f
25 changed files with 1362 additions and 392 deletions
BIN
interface/resources/avatar/animations/idleWS.fbx
Normal file
BIN
interface/resources/avatar/animations/idleWS.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idleWS_all.fbx
Normal file
BIN
interface/resources/avatar/animations/idleWS_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_LFF_all.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_LFF_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_RFF_all.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_RFF_all.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_lookaround01.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_lookaround01.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_armstretch.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_armstretch.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_bigstretch.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_bigstretch.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_checkwatch.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_checkwatch.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_headtilt.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_headtilt.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_lookaround.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_lookaround.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_neckstretch.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_neckstretch.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/idle_once_slownod.fbx
Normal file
BIN
interface/resources/avatar/animations/idle_once_slownod.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/talk04.fbx
Normal file
BIN
interface/resources/avatar/animations/talk04.fbx
Normal file
Binary file not shown.
BIN
interface/resources/avatar/animations/talk_righthand.fbx
Normal file
BIN
interface/resources/avatar/animations/talk_righthand.fbx
Normal file
Binary file not shown.
|
@ -196,42 +196,6 @@
|
||||||
{
|
{
|
||||||
"id": "rightHandStateMachine",
|
"id": "rightHandStateMachine",
|
||||||
"type": "stateMachine",
|
"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": {
|
"data": {
|
||||||
"currentState": "rightHandGrasp",
|
"currentState": "rightHandGrasp",
|
||||||
"states": [
|
"states": [
|
||||||
|
@ -415,32 +379,6 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"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",
|
"id": "leftHandOverlay",
|
||||||
|
@ -454,42 +392,6 @@
|
||||||
{
|
{
|
||||||
"id": "leftHandStateMachine",
|
"id": "leftHandStateMachine",
|
||||||
"type": "stateMachine",
|
"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": {
|
"data": {
|
||||||
"currentState": "leftHandGrasp",
|
"currentState": "leftHandGrasp",
|
||||||
"states": [
|
"states": [
|
||||||
|
@ -674,32 +576,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"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",
|
"id": "mainStateMachine",
|
||||||
"type": "stateMachine",
|
"type": "stateMachine",
|
||||||
|
@ -1027,35 +903,170 @@
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "idle",
|
"id": "idle",
|
||||||
"type": "stateMachine",
|
"type": "overlay",
|
||||||
"data": {
|
"data": {
|
||||||
"currentState": "idleStand",
|
"alpha": 1.0,
|
||||||
|
"alphaVar": "idleOverlayAlpha",
|
||||||
|
"boneSet": "upperBody"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "idleTalk",
|
||||||
|
"type": "randomSwitchStateMachine",
|
||||||
|
"data": {
|
||||||
|
"currentState": "idleTalk1",
|
||||||
|
"triggerRandomSwitch": "idleTalkSwitch",
|
||||||
|
"randomSwitchTimeMin": 5.0,
|
||||||
|
"randomSwitchTimeMax": 10.0,
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"id": "idleStand",
|
"id": "idleTalk1",
|
||||||
"interpTarget": 6,
|
"interpTarget": 6,
|
||||||
"interpDuration": 10,
|
"interpDuration": 15,
|
||||||
"transitions": [
|
"priority": 0.33,
|
||||||
{ "var": "isTalking", "state": "idleTalk" }
|
"resume": true,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "idleTalk2",
|
||||||
|
"interpTarget": 6,
|
||||||
|
"interpDuration": 15,
|
||||||
|
"priority": 0.33,
|
||||||
|
"resume": true,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "idleTalk3",
|
||||||
|
"interpTarget": 6,
|
||||||
|
"interpDuration": 15,
|
||||||
|
"priority": 0.33,
|
||||||
|
"resume": true,
|
||||||
|
"transitions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "idleTalk1",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/talk.fbx",
|
||||||
|
"startFrame": 1.0,
|
||||||
|
"endFrame": 800.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "idleTalk2",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/talk_righthand.fbx",
|
||||||
|
"startFrame": 1.0,
|
||||||
|
"endFrame": 501.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "idleTalk3",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/talk04.fbx",
|
||||||
|
"startFrame": 1.0,
|
||||||
|
"endFrame": 499.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "idleTalk",
|
"id": "idleStand",
|
||||||
"interpTarget": 6,
|
"type": "randomSwitchStateMachine",
|
||||||
"interpDuration": 10,
|
"data": {
|
||||||
|
"currentState": "masterIdle",
|
||||||
|
"triggerTimeMin": 10.0,
|
||||||
|
"triggerTimeMax": 60.0,
|
||||||
|
"transitionVar": "timeToFidget",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "masterIdle",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 1.0,
|
||||||
|
"resume": false,
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{ "var": "notIsTalking", "state": "idleStand" }
|
{ "var": "timeToFidget", "randomSwitchState": "fidget" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fidget",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": -1.0,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": [
|
||||||
|
{ "var": "movement1OnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "movement2OnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "movement3OnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "movement4OnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "movement5OnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "movement6OnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "movement7OnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "movement8OnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "alt1ToMasterIdleOnDone", "randomSwitchState": "masterIdle" },
|
||||||
|
{ "var": "alt2ToMasterIdleOnDone", "randomSwitchState": "masterIdle" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "idleStand",
|
"id": "masterIdle",
|
||||||
|
"type": "randomSwitchStateMachine",
|
||||||
|
"data": {
|
||||||
|
"currentState": "masterIdle1",
|
||||||
|
"triggerRandomSwitch": "masterIdleSwitch",
|
||||||
|
"randomSwitchTimeMin": 10.0,
|
||||||
|
"randomSwitchTimeMax": 60.0,
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "masterIdle1",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.33,
|
||||||
|
"resume": true,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "masterIdle2",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.33,
|
||||||
|
"resume": true,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "masterIdle3",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.33,
|
||||||
|
"resume": true,
|
||||||
|
"transitions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "masterIdle1",
|
||||||
"type": "clip",
|
"type": "clip",
|
||||||
"data": {
|
"data": {
|
||||||
"url": "qrc:///avatar/animations/idle.fbx",
|
"url": "qrc:///avatar/animations/idle.fbx",
|
||||||
"startFrame": 0.0,
|
"startFrame": 1.0,
|
||||||
"endFrame": 300.0,
|
"endFrame": 300.0,
|
||||||
"timeScale": 1.0,
|
"timeScale": 1.0,
|
||||||
"loopFlag": true
|
"loopFlag": true
|
||||||
|
@ -1063,16 +1074,386 @@
|
||||||
"children": []
|
"children": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "idleTalk",
|
"id": "masterIdle2",
|
||||||
"type": "clip",
|
"type": "clip",
|
||||||
"data": {
|
"data": {
|
||||||
"url": "qrc:///avatar/animations/talk.fbx",
|
"url": "qrc:///avatar/animations/idleWS_all.fbx",
|
||||||
"startFrame": 0.0,
|
"startFrame": 1.0,
|
||||||
"endFrame": 800.0,
|
"endFrame": 1620.0,
|
||||||
"timeScale": 1.0,
|
"timeScale": 1.0,
|
||||||
"loopFlag": true
|
"loopFlag": true
|
||||||
},
|
},
|
||||||
"children": []
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "masterIdle3",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_lookaround01.fbx",
|
||||||
|
"startFrame": 1.0,
|
||||||
|
"endFrame": 901.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fidget",
|
||||||
|
"type": "randomSwitchStateMachine",
|
||||||
|
"data": {
|
||||||
|
"currentState": "movement",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "movement",
|
||||||
|
"interpTarget": 17,
|
||||||
|
"interpDuration": 15,
|
||||||
|
"priority": 0.8,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "alternateIdle",
|
||||||
|
"interpTarget": 17,
|
||||||
|
"interpDuration": 15,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "movement",
|
||||||
|
"type": "randomSwitchStateMachine",
|
||||||
|
"data": {
|
||||||
|
"currentState": "movement1",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "movement1",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement2",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement3",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement4",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement5",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement6",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement7",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement8",
|
||||||
|
"interpTarget": 21,
|
||||||
|
"interpDuration": 20,
|
||||||
|
"priority": 0.2,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "movement1",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_once_slownod.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 91.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement2",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_once_headtilt.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 154,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement3",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_once_headtilt.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 154,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement4",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idleWS_all.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 1620,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement5",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_once_lookaround.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 324,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement6",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_once_neckstretch.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 169,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement7",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idleWS_all.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 1620,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "movement8",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_once_lookaround.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 324,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "alternateIdle",
|
||||||
|
"type": "randomSwitchStateMachine",
|
||||||
|
"data": {
|
||||||
|
"currentState": "transitionToAltIdle1",
|
||||||
|
"triggerTimeMin": 10.0,
|
||||||
|
"triggerTimeMax": 60.0,
|
||||||
|
"transitionVar": "finishAltIdle2",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"id": "transitionToAltIdle1",
|
||||||
|
"interpTarget": 11,
|
||||||
|
"interpDuration": 10,
|
||||||
|
"priority": 0.5,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": [
|
||||||
|
{
|
||||||
|
"var": "transitionToAltIdle1OnDone",
|
||||||
|
"randomSwitchState": "altIdle1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "transitionToAltIdle2",
|
||||||
|
"interpTarget": 11,
|
||||||
|
"interpDuration": 10,
|
||||||
|
"priority": 0.5,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": [
|
||||||
|
{
|
||||||
|
"var": "transitionToAltIdle2OnDone",
|
||||||
|
"randomSwitchState": "altIdle2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "altIdle1",
|
||||||
|
"interpTarget": 11,
|
||||||
|
"interpDuration": 10,
|
||||||
|
"priority": -1.0,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": [
|
||||||
|
{
|
||||||
|
"var": "finishAltIdle2",
|
||||||
|
"randomSwitchState": "alt1ToMasterIdle"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "altIdle2",
|
||||||
|
"interpTarget": 11,
|
||||||
|
"interpDuration": 10,
|
||||||
|
"priority": -1.0,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": [
|
||||||
|
{
|
||||||
|
"var": "finishAltIdle2",
|
||||||
|
"randomSwitchState": "alt2ToMasterIdle"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "alt1ToMasterIdle",
|
||||||
|
"interpTarget": 11,
|
||||||
|
"interpDuration": 10,
|
||||||
|
"priority": -1.0,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "alt2ToMasterIdle",
|
||||||
|
"interpTarget": 11,
|
||||||
|
"interpDuration": 10,
|
||||||
|
"priority": -1.0,
|
||||||
|
"resume": false,
|
||||||
|
"transitions": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "transitionToAltIdle1",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_LFF_all.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 55,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "transitionToAltIdle2",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_RFF_all.fbx",
|
||||||
|
"startFrame": 1,
|
||||||
|
"endFrame": 56,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "altIdle1",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_LFF_all.fbx",
|
||||||
|
"startFrame": 55,
|
||||||
|
"endFrame": 389,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "altIdle2",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_RFF_all.fbx",
|
||||||
|
"startFrame": 56,
|
||||||
|
"endFrame": 390,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": true
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "alt1ToMasterIdle",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_LFF_all.fbx",
|
||||||
|
"startFrame": 389,
|
||||||
|
"endFrame": 472,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "alt2ToMasterIdle",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "qrc:///avatar/animations/idle_RFF_all.fbx",
|
||||||
|
"startFrame": 390,
|
||||||
|
"endFrame": 453,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,11 +11,12 @@
|
||||||
#include "AnimContext.h"
|
#include "AnimContext.h"
|
||||||
|
|
||||||
AnimContext::AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
|
AnimContext::AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
|
||||||
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix) :
|
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix, int evaluationCount) :
|
||||||
_enableDebugDrawIKTargets(enableDebugDrawIKTargets),
|
_enableDebugDrawIKTargets(enableDebugDrawIKTargets),
|
||||||
_enableDebugDrawIKConstraints(enableDebugDrawIKConstraints),
|
_enableDebugDrawIKConstraints(enableDebugDrawIKConstraints),
|
||||||
_enableDebugDrawIKChains(enableDebugDrawIKChains),
|
_enableDebugDrawIKChains(enableDebugDrawIKChains),
|
||||||
_geometryToRigMatrix(geometryToRigMatrix),
|
_geometryToRigMatrix(geometryToRigMatrix),
|
||||||
_rigToWorldMatrix(rigToWorldMatrix)
|
_rigToWorldMatrix(rigToWorldMatrix),
|
||||||
|
_evaluationCount(evaluationCount)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ enum class AnimNodeType {
|
||||||
BlendLinearMove,
|
BlendLinearMove,
|
||||||
Overlay,
|
Overlay,
|
||||||
StateMachine,
|
StateMachine,
|
||||||
|
RandomSwitchStateMachine,
|
||||||
Manipulator,
|
Manipulator,
|
||||||
InverseKinematics,
|
InverseKinematics,
|
||||||
DefaultPose,
|
DefaultPose,
|
||||||
|
@ -37,13 +38,14 @@ class AnimContext {
|
||||||
public:
|
public:
|
||||||
AnimContext() {}
|
AnimContext() {}
|
||||||
AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
|
AnimContext(bool enableDebugDrawIKTargets, bool enableDebugDrawIKConstraints, bool enableDebugDrawIKChains,
|
||||||
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix);
|
const glm::mat4& geometryToRigMatrix, const glm::mat4& rigToWorldMatrix, int evaluationCount);
|
||||||
|
|
||||||
bool getEnableDebugDrawIKTargets() const { return _enableDebugDrawIKTargets; }
|
bool getEnableDebugDrawIKTargets() const { return _enableDebugDrawIKTargets; }
|
||||||
bool getEnableDebugDrawIKConstraints() const { return _enableDebugDrawIKConstraints; }
|
bool getEnableDebugDrawIKConstraints() const { return _enableDebugDrawIKConstraints; }
|
||||||
bool getEnableDebugDrawIKChains() const { return _enableDebugDrawIKChains; }
|
bool getEnableDebugDrawIKChains() const { return _enableDebugDrawIKChains; }
|
||||||
const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; }
|
const glm::mat4& getGeometryToRigMatrix() const { return _geometryToRigMatrix; }
|
||||||
const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; }
|
const glm::mat4& getRigToWorldMatrix() const { return _rigToWorldMatrix; }
|
||||||
|
int getEvaluationCount() const { return _evaluationCount; }
|
||||||
|
|
||||||
float getDebugAlpha(const QString& key) const {
|
float getDebugAlpha(const QString& key) const {
|
||||||
auto it = _debugAlphaMap.find(key);
|
auto it = _debugAlphaMap.find(key);
|
||||||
|
@ -85,6 +87,7 @@ protected:
|
||||||
bool _enableDebugDrawIKChains { false };
|
bool _enableDebugDrawIKChains { false };
|
||||||
glm::mat4 _geometryToRigMatrix;
|
glm::mat4 _geometryToRigMatrix;
|
||||||
glm::mat4 _rigToWorldMatrix;
|
glm::mat4 _rigToWorldMatrix;
|
||||||
|
int _evaluationCount{ 0 };
|
||||||
|
|
||||||
// used for debugging internal state of animation system.
|
// used for debugging internal state of animation system.
|
||||||
mutable DebugAlphaMap _debugAlphaMap;
|
mutable DebugAlphaMap _debugAlphaMap;
|
||||||
|
|
|
@ -43,6 +43,7 @@ public:
|
||||||
friend class AnimDebugDraw;
|
friend class AnimDebugDraw;
|
||||||
friend void buildChildMap(std::map<QString, Pointer>& map, Pointer node);
|
friend void buildChildMap(std::map<QString, Pointer>& map, Pointer node);
|
||||||
friend class AnimStateMachine;
|
friend class AnimStateMachine;
|
||||||
|
friend class AnimRandomSwitch;
|
||||||
|
|
||||||
AnimNode(Type type, const QString& id) : _type(type), _id(id) {}
|
AnimNode(Type type, const QString& id) : _type(type), _id(id) {}
|
||||||
virtual ~AnimNode() {}
|
virtual ~AnimNode() {}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "AnimationLogging.h"
|
#include "AnimationLogging.h"
|
||||||
#include "AnimOverlay.h"
|
#include "AnimOverlay.h"
|
||||||
#include "AnimStateMachine.h"
|
#include "AnimStateMachine.h"
|
||||||
|
#include "AnimRandomSwitch.h"
|
||||||
#include "AnimManipulator.h"
|
#include "AnimManipulator.h"
|
||||||
#include "AnimInverseKinematics.h"
|
#include "AnimInverseKinematics.h"
|
||||||
#include "AnimDefaultPose.h"
|
#include "AnimDefaultPose.h"
|
||||||
|
@ -38,6 +39,7 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q
|
||||||
static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadOverlayNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
static AnimNode::Pointer loadRandomSwitchStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
static AnimNode::Pointer loadDefaultPoseNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
@ -51,6 +53,7 @@ static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f;
|
||||||
// returns node on success, nullptr on failure.
|
// returns node on success, nullptr on failure.
|
||||||
static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
static bool processDoNothing(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return true; }
|
||||||
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||||
|
|
||||||
static const char* animNodeTypeToString(AnimNode::Type type) {
|
static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -59,6 +62,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||||
case AnimNode::Type::BlendLinearMove: return "blendLinearMove";
|
case AnimNode::Type::BlendLinearMove: return "blendLinearMove";
|
||||||
case AnimNode::Type::Overlay: return "overlay";
|
case AnimNode::Type::Overlay: return "overlay";
|
||||||
case AnimNode::Type::StateMachine: return "stateMachine";
|
case AnimNode::Type::StateMachine: return "stateMachine";
|
||||||
|
case AnimNode::Type::RandomSwitchStateMachine: return "randomSwitchStateMachine";
|
||||||
case AnimNode::Type::Manipulator: return "manipulator";
|
case AnimNode::Type::Manipulator: return "manipulator";
|
||||||
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
||||||
case AnimNode::Type::DefaultPose: return "defaultPose";
|
case AnimNode::Type::DefaultPose: return "defaultPose";
|
||||||
|
@ -92,6 +96,16 @@ static AnimStateMachine::InterpType stringToInterpType(const QString& str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimRandomSwitch::InterpType stringToRandomInterpType(const QString& str) {
|
||||||
|
if (str == "snapshotBoth") {
|
||||||
|
return AnimRandomSwitch::InterpType::SnapshotBoth;
|
||||||
|
} else if (str == "snapshotPrev") {
|
||||||
|
return AnimRandomSwitch::InterpType::SnapshotPrev;
|
||||||
|
} else {
|
||||||
|
return AnimRandomSwitch::InterpType::NumTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {
|
static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case AnimManipulator::JointVar::Type::Absolute: return "absolute";
|
case AnimManipulator::JointVar::Type::Absolute: return "absolute";
|
||||||
|
@ -122,6 +136,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
||||||
case AnimNode::Type::BlendLinearMove: return loadBlendLinearMoveNode;
|
case AnimNode::Type::BlendLinearMove: return loadBlendLinearMoveNode;
|
||||||
case AnimNode::Type::Overlay: return loadOverlayNode;
|
case AnimNode::Type::Overlay: return loadOverlayNode;
|
||||||
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
||||||
|
case AnimNode::Type::RandomSwitchStateMachine: return loadRandomSwitchStateMachineNode;
|
||||||
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
case AnimNode::Type::Manipulator: return loadManipulatorNode;
|
||||||
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
||||||
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;
|
||||||
|
@ -140,6 +155,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
||||||
case AnimNode::Type::BlendLinearMove: return processDoNothing;
|
case AnimNode::Type::BlendLinearMove: return processDoNothing;
|
||||||
case AnimNode::Type::Overlay: return processDoNothing;
|
case AnimNode::Type::Overlay: return processDoNothing;
|
||||||
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
||||||
|
case AnimNode::Type::RandomSwitchStateMachine: return processRandomSwitchStateMachineNode;
|
||||||
case AnimNode::Type::Manipulator: return processDoNothing;
|
case AnimNode::Type::Manipulator: return processDoNothing;
|
||||||
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
case AnimNode::Type::InverseKinematics: return processDoNothing;
|
||||||
case AnimNode::Type::DefaultPose: return processDoNothing;
|
case AnimNode::Type::DefaultPose: return processDoNothing;
|
||||||
|
@ -463,6 +479,11 @@ static AnimNode::Pointer loadStateMachineNode(const QJsonObject& jsonObj, const
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static AnimNode::Pointer loadRandomSwitchStateMachineNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||||
|
auto node = std::make_shared<AnimRandomSwitch>(id);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
static AnimNode::Pointer loadManipulatorNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||||
|
|
||||||
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
|
READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);
|
||||||
|
@ -780,6 +801,141 @@ bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) {
|
||||||
|
auto smNode = std::static_pointer_cast<AnimRandomSwitch>(node);
|
||||||
|
assert(smNode);
|
||||||
|
|
||||||
|
READ_STRING(currentState, jsonObj, nodeId, jsonUrl, false);
|
||||||
|
READ_OPTIONAL_FLOAT(randomSwitchTimeMin, jsonObj, -1.0f);
|
||||||
|
READ_OPTIONAL_FLOAT(randomSwitchTimeMax, jsonObj, -1.0f);
|
||||||
|
READ_OPTIONAL_STRING(triggerRandomSwitch, jsonObj);
|
||||||
|
READ_OPTIONAL_FLOAT(triggerTimeMin, jsonObj, -1.0f);
|
||||||
|
READ_OPTIONAL_FLOAT(triggerTimeMax, jsonObj, -1.0f);
|
||||||
|
READ_OPTIONAL_STRING(transitionVar, jsonObj);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auto statesValue = jsonObj.value("states");
|
||||||
|
if (!statesValue.isArray()) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad array \"states\" in random switch state Machine node, id =" << nodeId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a map for all children by name.
|
||||||
|
std::map<QString, int> childMap;
|
||||||
|
buildChildMap(childMap, node);
|
||||||
|
|
||||||
|
// first pass parse all the states and build up the state and transition map.
|
||||||
|
using StringPair = std::pair<QString, QString>;
|
||||||
|
using TransitionMap = std::multimap<AnimRandomSwitch::RandomSwitchState::Pointer, StringPair>;
|
||||||
|
TransitionMap transitionMap;
|
||||||
|
|
||||||
|
using RandomStateMap = std::map<QString, AnimRandomSwitch::RandomSwitchState::Pointer>;
|
||||||
|
RandomStateMap randomStateMap;
|
||||||
|
|
||||||
|
auto randomStatesArray = statesValue.toArray();
|
||||||
|
for (const auto& randomStateValue : randomStatesArray) {
|
||||||
|
if (!randomStateValue.isObject()) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad state object in \"random states\", id =" << nodeId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto stateObj = randomStateValue.toObject();
|
||||||
|
|
||||||
|
READ_STRING(id, stateObj, nodeId, jsonUrl, false);
|
||||||
|
READ_FLOAT(interpTarget, stateObj, nodeId, jsonUrl, false);
|
||||||
|
READ_FLOAT(interpDuration, stateObj, nodeId, jsonUrl, false);
|
||||||
|
READ_OPTIONAL_STRING(interpType, stateObj);
|
||||||
|
READ_FLOAT(priority, stateObj, nodeId, jsonUrl, false);
|
||||||
|
READ_BOOL(resume, stateObj, nodeId, jsonUrl, false);
|
||||||
|
|
||||||
|
READ_OPTIONAL_STRING(interpTargetVar, stateObj);
|
||||||
|
READ_OPTIONAL_STRING(interpDurationVar, stateObj);
|
||||||
|
READ_OPTIONAL_STRING(interpTypeVar, stateObj);
|
||||||
|
|
||||||
|
auto iter = childMap.find(id);
|
||||||
|
if (iter == childMap.end()) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, could not find random stateMachine child (state) with nodeId =" << nodeId << "random stateId =" << id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimRandomSwitch::InterpType interpTypeEnum = AnimRandomSwitch::InterpType::SnapshotPrev; // default value
|
||||||
|
if (!interpType.isEmpty()) {
|
||||||
|
interpTypeEnum = stringToRandomInterpType(interpType);
|
||||||
|
if (interpTypeEnum == AnimRandomSwitch::InterpType::NumTypes) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad interpType on random state Machine state, nodeId = " << nodeId << "random stateId =" << id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto randomStatePtr = std::make_shared<AnimRandomSwitch::RandomSwitchState>(id, iter->second, interpTarget, interpDuration, interpTypeEnum, priority, resume);
|
||||||
|
if (priority > 0.0f) {
|
||||||
|
smNode->addToPrioritySum(priority);
|
||||||
|
}
|
||||||
|
assert(randomStatePtr);
|
||||||
|
|
||||||
|
if (!interpTargetVar.isEmpty()) {
|
||||||
|
randomStatePtr->setInterpTargetVar(interpTargetVar);
|
||||||
|
}
|
||||||
|
if (!interpDurationVar.isEmpty()) {
|
||||||
|
randomStatePtr->setInterpDurationVar(interpDurationVar);
|
||||||
|
}
|
||||||
|
if (!interpTypeVar.isEmpty()) {
|
||||||
|
randomStatePtr->setInterpTypeVar(interpTypeVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
smNode->addState(randomStatePtr);
|
||||||
|
randomStateMap.insert(RandomStateMap::value_type(randomStatePtr->getID(), randomStatePtr));
|
||||||
|
|
||||||
|
auto transitionsValue = stateObj.value("transitions");
|
||||||
|
if (!transitionsValue.isArray()) {
|
||||||
|
qCritical(animation) << "AnimNodeLoader, bad array \"transitions\" in random state Machine node, stateId =" << id << "nodeId =" << nodeId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto transitionsArray = transitionsValue.toArray();
|
||||||
|
for (const auto& transitionValue : transitionsArray) {
|
||||||
|
if (!transitionValue.isObject()) {
|
||||||
|
qCritical(animation) << "AnimNodeLoader, bad transition object in \"transitions\", random stateId =" << id << "nodeId =" << nodeId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto transitionObj = transitionValue.toObject();
|
||||||
|
|
||||||
|
READ_STRING(var, transitionObj, nodeId, jsonUrl, false);
|
||||||
|
READ_STRING(randomSwitchState, transitionObj, nodeId, jsonUrl, false);
|
||||||
|
|
||||||
|
transitionMap.insert(TransitionMap::value_type(randomStatePtr, StringPair(var, randomSwitchState)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// second pass: now iterate thru all transitions and add them to the appropriate states.
|
||||||
|
for (auto& transition : transitionMap) {
|
||||||
|
AnimRandomSwitch::RandomSwitchState::Pointer srcState = transition.first;
|
||||||
|
auto iter = randomStateMap.find(transition.second.second);
|
||||||
|
if (iter != randomStateMap.end()) {
|
||||||
|
srcState->addTransition(AnimRandomSwitch::RandomSwitchState::Transition(transition.second.first, iter->second));
|
||||||
|
} else {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad random state machine transition from srcState =" << srcState->_id << "dstState =" << transition.second.second << "nodeId =" << nodeId;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = randomStateMap.find(currentState);
|
||||||
|
if (iter == randomStateMap.end()) {
|
||||||
|
qCCritical(animation) << "AnimNodeLoader, bad currentState =" << currentState << "could not find child node" << "id =" << nodeId;
|
||||||
|
}
|
||||||
|
smNode->setCurrentState(iter->second);
|
||||||
|
smNode->setRandomSwitchTimeMin(randomSwitchTimeMin);
|
||||||
|
smNode->setRandomSwitchTimeMax(randomSwitchTimeMax);
|
||||||
|
smNode->setTriggerRandomSwitchVar(triggerRandomSwitch);
|
||||||
|
smNode->setTriggerTimeMin(triggerTimeMin);
|
||||||
|
smNode->setTriggerTimeMax(triggerTimeMax);
|
||||||
|
smNode->setTransitionVar(transitionVar);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
|
AnimNodeLoader::AnimNodeLoader(const QUrl& url) :
|
||||||
_url(url)
|
_url(url)
|
||||||
{
|
{
|
||||||
|
|
212
libraries/animation/src/AnimRandomSwitch.cpp
Normal file
212
libraries/animation/src/AnimRandomSwitch.cpp
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
//
|
||||||
|
// AnimRandomSwitch.cpp
|
||||||
|
//
|
||||||
|
// Created by Angus Antley on 4/8/2019.
|
||||||
|
// Copyright (c) 2019 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "AnimRandomSwitch.h"
|
||||||
|
#include "AnimUtil.h"
|
||||||
|
#include "AnimationLogging.h"
|
||||||
|
|
||||||
|
AnimRandomSwitch::AnimRandomSwitch(const QString& id) :
|
||||||
|
AnimNode(AnimNode::Type::RandomSwitchStateMachine, id) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimRandomSwitch::~AnimRandomSwitch() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) {
|
||||||
|
float parentDebugAlpha = context.getDebugAlpha(_id);
|
||||||
|
|
||||||
|
AnimRandomSwitch::RandomSwitchState::Pointer desiredState = _currentState;
|
||||||
|
if (abs(_randomSwitchEvaluationCount - context.getEvaluationCount()) > 1 || animVars.lookup(_triggerRandomSwitchVar, false)) {
|
||||||
|
|
||||||
|
// get a random number and decide which motion to choose.
|
||||||
|
bool currentStateHasPriority = false;
|
||||||
|
float dice = randFloatInRange(0.0f, 1.0f);
|
||||||
|
float lowerBound = 0.0f;
|
||||||
|
for (const RandomSwitchState::Pointer& randState : _randomStates) {
|
||||||
|
if (randState->getPriority() > 0.0f) {
|
||||||
|
float upperBound = lowerBound + (randState->getPriority() / _totalPriorities);
|
||||||
|
if ((dice > lowerBound) && (dice < upperBound)) {
|
||||||
|
desiredState = randState;
|
||||||
|
}
|
||||||
|
lowerBound = upperBound;
|
||||||
|
|
||||||
|
// this indicates if the curent state is one that can be selected randomly, or is one that was transitioned to by the random duration timer.
|
||||||
|
currentStateHasPriority = currentStateHasPriority || (_currentState == randState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (abs(_randomSwitchEvaluationCount - context.getEvaluationCount()) > 1) {
|
||||||
|
_duringInterp = false;
|
||||||
|
switchRandomState(animVars, context, desiredState, _duringInterp);
|
||||||
|
} else {
|
||||||
|
// firing a random switch, be sure that we aren't completing a previously triggered transition
|
||||||
|
if (currentStateHasPriority) {
|
||||||
|
if (desiredState->getID() != _currentState->getID()) {
|
||||||
|
_duringInterp = true;
|
||||||
|
switchRandomState(animVars, context, desiredState, _duringInterp);
|
||||||
|
} else {
|
||||||
|
_duringInterp = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
|
||||||
|
_randomSwitchTime = randFloatInRange(_randomSwitchTimeMin, _randomSwitchTimeMax);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// here we are checking to see if we want a temporary movement
|
||||||
|
// evaluate currentState transitions
|
||||||
|
auto transitionState = evaluateTransitions(animVars);
|
||||||
|
if (transitionState != _currentState) {
|
||||||
|
_duringInterp = true;
|
||||||
|
switchRandomState(animVars, context, transitionState, _duringInterp);
|
||||||
|
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
|
||||||
|
_randomSwitchTime = randFloatInRange(_randomSwitchTimeMin, _randomSwitchTimeMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_triggerTime -= dt;
|
||||||
|
if ((_triggerTime < 0.0f) && (_triggerTimeMin > 0.0f) && (_triggerTimeMax > 0.0f)) {
|
||||||
|
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
|
||||||
|
triggersOut.setTrigger(_transitionVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
_randomSwitchTime -= dt;
|
||||||
|
if ((_randomSwitchTime < 0.0f) && (_randomSwitchTimeMin > 0.0f) && (_randomSwitchTimeMax > 0.0f)) {
|
||||||
|
_randomSwitchTime = randFloatInRange(_randomSwitchTimeMin, _randomSwitchTimeMax);
|
||||||
|
// restart the trigger timer if it is also enabled
|
||||||
|
_triggerTime = randFloatInRange(_triggerTimeMin, _triggerTimeMax);
|
||||||
|
triggersOut.setTrigger(_triggerRandomSwitchVar);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(_currentState);
|
||||||
|
auto currentStateNode = _children[_currentState->getChildIndex()];
|
||||||
|
assert(currentStateNode);
|
||||||
|
|
||||||
|
if (_duringInterp) {
|
||||||
|
_alpha += _alphaVel * dt;
|
||||||
|
if (_alpha < 1.0f) {
|
||||||
|
AnimPoseVec* nextPoses = nullptr;
|
||||||
|
AnimPoseVec* prevPoses = nullptr;
|
||||||
|
AnimPoseVec localNextPoses;
|
||||||
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
|
// interp between both snapshots
|
||||||
|
prevPoses = &_prevPoses;
|
||||||
|
nextPoses = &_nextPoses;
|
||||||
|
} else if (_interpType == InterpType::SnapshotPrev) {
|
||||||
|
// interp between the prev snapshot and evaluated next target.
|
||||||
|
// this is useful for interping into a blend
|
||||||
|
localNextPoses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
prevPoses = &_prevPoses;
|
||||||
|
nextPoses = &localNextPoses;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
if (_poses.size() > 0 && nextPoses && prevPoses && nextPoses->size() > 0 && prevPoses->size() > 0) {
|
||||||
|
::blend(_poses.size(), &(prevPoses->at(0)), &(nextPoses->at(0)), _alpha, &_poses[0]);
|
||||||
|
}
|
||||||
|
context.setDebugAlpha(_currentState->getID(), _alpha * parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||||
|
} else {
|
||||||
|
_duringInterp = false;
|
||||||
|
_prevPoses.clear();
|
||||||
|
_nextPoses.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_duringInterp){
|
||||||
|
context.setDebugAlpha(_currentState->getID(), parentDebugAlpha, _children[_currentState->getChildIndex()]->getType());
|
||||||
|
_poses = currentStateNode->evaluate(animVars, context, dt, triggersOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
_randomSwitchEvaluationCount = context.getEvaluationCount();
|
||||||
|
processOutputJoints(triggersOut);
|
||||||
|
|
||||||
|
context.addStateMachineInfo(_id, _currentState->getID(), _previousState->getID(), _duringInterp, _alpha);
|
||||||
|
if (_duringInterp) {
|
||||||
|
// hack: add previoius state to debug alpha map, with parens around it's name.
|
||||||
|
context.setDebugAlpha(QString("(%1)").arg(_previousState->getID()), 1.0f - _alpha, AnimNodeType::Clip);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _poses;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimRandomSwitch::setCurrentState(RandomSwitchState::Pointer randomState) {
|
||||||
|
_previousState = _currentState ? _currentState : randomState;
|
||||||
|
_currentState = randomState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimRandomSwitch::addState(RandomSwitchState::Pointer randomState) {
|
||||||
|
_randomStates.push_back(randomState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimRandomSwitch::switchRandomState(const AnimVariantMap& animVars, const AnimContext& context, RandomSwitchState::Pointer desiredState, bool shouldInterp) {
|
||||||
|
|
||||||
|
auto nextStateNode = _children[desiredState->getChildIndex()];
|
||||||
|
if (shouldInterp) {
|
||||||
|
|
||||||
|
const float FRAMES_PER_SECOND = 30.0f;
|
||||||
|
|
||||||
|
auto prevStateNode = _children[_currentState->getChildIndex()];
|
||||||
|
|
||||||
|
_alpha = 0.0f;
|
||||||
|
float duration = std::max(0.001f, animVars.lookup(desiredState->_interpDurationVar, desiredState->_interpDuration));
|
||||||
|
_alphaVel = FRAMES_PER_SECOND / duration;
|
||||||
|
_interpType = (InterpType)animVars.lookup(desiredState->_interpTypeVar, (int)desiredState->_interpType);
|
||||||
|
|
||||||
|
// because dt is 0, we should not encounter any triggers
|
||||||
|
const float dt = 0.0f;
|
||||||
|
AnimVariantMap triggers;
|
||||||
|
|
||||||
|
if (_interpType == InterpType::SnapshotBoth) {
|
||||||
|
// snapshot previous pose.
|
||||||
|
_prevPoses = _poses;
|
||||||
|
// snapshot next pose at the target frame.
|
||||||
|
if (!desiredState->getResume()) {
|
||||||
|
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
|
||||||
|
}
|
||||||
|
_nextPoses = nextStateNode->evaluate(animVars, context, dt, triggers);
|
||||||
|
} else if (_interpType == InterpType::SnapshotPrev) {
|
||||||
|
// snapshot previoius pose
|
||||||
|
_prevPoses = _poses;
|
||||||
|
// no need to evaluate _nextPoses we will do it dynamically during the interp,
|
||||||
|
// however we need to set the current frame.
|
||||||
|
if (!desiredState->getResume()) {
|
||||||
|
nextStateNode->setCurrentFrame(desiredState->_interpTarget - duration);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!desiredState->getResume()) {
|
||||||
|
nextStateNode->setCurrentFrame(desiredState->_interpTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WANT_DEBUG
|
||||||
|
qCDebug(animation) << "AnimRandomSwitch::switchState:" << _currentState->getID() << "->" << desiredState->getID() << "duration =" << duration << "targetFrame =" << desiredState->_interpTarget << "interpType = " << (int)_interpType;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
setCurrentState(desiredState);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimRandomSwitch::RandomSwitchState::Pointer AnimRandomSwitch::evaluateTransitions(const AnimVariantMap& animVars) const {
|
||||||
|
assert(_currentState);
|
||||||
|
for (auto& transition : _currentState->_transitions) {
|
||||||
|
if (animVars.lookup(transition._var, false)) {
|
||||||
|
return transition._randomSwitchState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AnimPoseVec& AnimRandomSwitch::getPosesInternal() const {
|
||||||
|
return _poses;
|
||||||
|
}
|
184
libraries/animation/src/AnimRandomSwitch.h
Normal file
184
libraries/animation/src/AnimRandomSwitch.h
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
//
|
||||||
|
// AnimRandomSwitch.h
|
||||||
|
//
|
||||||
|
// Created by Angus Antley on 4/8/19.
|
||||||
|
// Copyright (c) 2019 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// 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_AnimRandomSwitch_h
|
||||||
|
#define hifi_AnimRandomSwitch_h
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "AnimNode.h"
|
||||||
|
|
||||||
|
// Random Switch State Machine for random transitioning between children AnimNodes
|
||||||
|
//
|
||||||
|
// This is mechanisim for choosing and playing a random animation and smoothly interpolating/fading
|
||||||
|
// between them. A RandomSwitch has a set of States, which typically reference
|
||||||
|
// child AnimNodes. Each Random Switch State has a list of Transitions, which are evaluated
|
||||||
|
// to determine when we should switch to a new State. Parameters for the smooth
|
||||||
|
// interpolation/fading are read from the Random Switch State that you are transitioning to.
|
||||||
|
//
|
||||||
|
// The currentState can be set directly via the setCurrentStateVar() and will override
|
||||||
|
// any State transitions.
|
||||||
|
//
|
||||||
|
// Each Random Switch State has two parameters that can be changed via AnimVars,
|
||||||
|
// * interpTarget - (frames) The destination frame of the interpolation. i.e. the first frame of the animation that will
|
||||||
|
// visible after interpolation is complete.
|
||||||
|
// * interpDuration - (frames) The total length of time it will take to interp between the current pose and the
|
||||||
|
// interpTarget frame.
|
||||||
|
// * interpType - How the interpolation is performed.
|
||||||
|
// * priority - this number represents how likely this Random Switch State will be chosen.
|
||||||
|
// the priority for each Random Switch State will be normalized, so their relative size is what is important
|
||||||
|
// * resume - if resume is false then if this state is chosen twice in a row it will remember what frame it was playing on.
|
||||||
|
// * SnapshotBoth: Stores two snapshots, the previous animation before interpolation begins and the target state at the
|
||||||
|
// interTarget frame. Then during the interpolation period the two snapshots are interpolated to produce smooth motion between them.
|
||||||
|
// * SnapshotPrev: Stores a snapshot of the previous animation before interpolation begins. However the target state is
|
||||||
|
// evaluated dynamically. During the interpolation period the previous snapshot is interpolated with the target pose
|
||||||
|
// to produce smooth motion between them. This mode is useful for interping into a blended animation where the actual
|
||||||
|
// blend factor is not known at the start of the interp or is might change dramatically during the interp.
|
||||||
|
//
|
||||||
|
|
||||||
|
class AnimRandomSwitch : public AnimNode {
|
||||||
|
public:
|
||||||
|
friend class AnimNodeLoader;
|
||||||
|
friend bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl);
|
||||||
|
|
||||||
|
enum class InterpType {
|
||||||
|
SnapshotBoth = 0,
|
||||||
|
SnapshotPrev,
|
||||||
|
NumTypes
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
class RandomSwitchState {
|
||||||
|
public:
|
||||||
|
friend AnimRandomSwitch;
|
||||||
|
friend bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl);
|
||||||
|
|
||||||
|
using Pointer = std::shared_ptr<RandomSwitchState>;
|
||||||
|
using ConstPointer = std::shared_ptr<const RandomSwitchState>;
|
||||||
|
|
||||||
|
class Transition {
|
||||||
|
public:
|
||||||
|
friend AnimRandomSwitch;
|
||||||
|
Transition(const QString& var, RandomSwitchState::Pointer randomState) : _var(var), _randomSwitchState(randomState) {}
|
||||||
|
protected:
|
||||||
|
QString _var;
|
||||||
|
RandomSwitchState::Pointer _randomSwitchState;
|
||||||
|
};
|
||||||
|
|
||||||
|
RandomSwitchState(const QString& id, int childIndex, float interpTarget, float interpDuration, InterpType interpType, float priority, bool resume) :
|
||||||
|
_id(id),
|
||||||
|
_childIndex(childIndex),
|
||||||
|
_interpTarget(interpTarget),
|
||||||
|
_interpDuration(interpDuration),
|
||||||
|
_interpType(interpType),
|
||||||
|
_priority(priority),
|
||||||
|
_resume(resume){
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInterpTargetVar(const QString& interpTargetVar) { _interpTargetVar = interpTargetVar; }
|
||||||
|
void setInterpDurationVar(const QString& interpDurationVar) { _interpDurationVar = interpDurationVar; }
|
||||||
|
void setInterpTypeVar(const QString& interpTypeVar) { _interpTypeVar = interpTypeVar; }
|
||||||
|
|
||||||
|
int getChildIndex() const { return _childIndex; }
|
||||||
|
float getPriority() const { return _priority; }
|
||||||
|
bool getResume() const { return _resume; }
|
||||||
|
const QString& getID() const { return _id; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void setInterpTarget(float interpTarget) { _interpTarget = interpTarget; }
|
||||||
|
void setInterpDuration(float interpDuration) { _interpDuration = interpDuration; }
|
||||||
|
void setPriority(float priority) { _priority = priority; }
|
||||||
|
void setResumeFlag(bool resume) { _resume = resume; }
|
||||||
|
|
||||||
|
void addTransition(const Transition& transition) { _transitions.push_back(transition); }
|
||||||
|
|
||||||
|
QString _id;
|
||||||
|
int _childIndex;
|
||||||
|
float _interpTarget; // frames
|
||||||
|
float _interpDuration; // frames
|
||||||
|
InterpType _interpType;
|
||||||
|
float _priority {0.0f};
|
||||||
|
bool _resume {false};
|
||||||
|
|
||||||
|
QString _interpTargetVar;
|
||||||
|
QString _interpDurationVar;
|
||||||
|
QString _interpTypeVar;
|
||||||
|
|
||||||
|
std::vector<Transition> _transitions;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// no copies
|
||||||
|
RandomSwitchState(const RandomSwitchState&) = delete;
|
||||||
|
RandomSwitchState& operator=(const RandomSwitchState&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit AnimRandomSwitch(const QString& id);
|
||||||
|
virtual ~AnimRandomSwitch() override;
|
||||||
|
|
||||||
|
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, AnimVariantMap& triggersOut) override;
|
||||||
|
|
||||||
|
void setCurrentStateVar(QString& currentStateVar) { _currentStateVar = currentStateVar; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void setCurrentState(RandomSwitchState::Pointer randomState);
|
||||||
|
void setTriggerRandomSwitchVar(const QString& triggerRandomSwitchVar) { _triggerRandomSwitchVar = triggerRandomSwitchVar; }
|
||||||
|
void setRandomSwitchTimeMin(float randomSwitchTimeMin) { _randomSwitchTimeMin = randomSwitchTimeMin; }
|
||||||
|
void setRandomSwitchTimeMax(float randomSwitchTimeMax) { _randomSwitchTimeMax = randomSwitchTimeMax; }
|
||||||
|
void setTransitionVar(const QString& transitionVar) { _transitionVar = transitionVar; }
|
||||||
|
void setTriggerTimeMin(float triggerTimeMin) { _triggerTimeMin = triggerTimeMin; }
|
||||||
|
void setTriggerTimeMax(float triggerTimeMax) { _triggerTimeMax = triggerTimeMax; }
|
||||||
|
void addToPrioritySum(float priority) { _totalPriorities += priority; }
|
||||||
|
|
||||||
|
void addState(RandomSwitchState::Pointer randomState);
|
||||||
|
|
||||||
|
void switchRandomState(const AnimVariantMap& animVars, const AnimContext& context, RandomSwitchState::Pointer desiredState, bool shouldInterp);
|
||||||
|
RandomSwitchState::Pointer evaluateTransitions(const AnimVariantMap& animVars) const;
|
||||||
|
|
||||||
|
// for AnimDebugDraw rendering
|
||||||
|
virtual const AnimPoseVec& getPosesInternal() const override;
|
||||||
|
|
||||||
|
AnimPoseVec _poses;
|
||||||
|
|
||||||
|
int _randomSwitchEvaluationCount { 0 };
|
||||||
|
// interpolation state
|
||||||
|
bool _duringInterp = false;
|
||||||
|
InterpType _interpType{ InterpType::SnapshotPrev };
|
||||||
|
float _alphaVel = 0.0f;
|
||||||
|
float _alpha = 0.0f;
|
||||||
|
AnimPoseVec _prevPoses;
|
||||||
|
AnimPoseVec _nextPoses;
|
||||||
|
float _totalPriorities { 0.0f };
|
||||||
|
|
||||||
|
RandomSwitchState::Pointer _currentState;
|
||||||
|
RandomSwitchState::Pointer _previousState;
|
||||||
|
std::vector<RandomSwitchState::Pointer> _randomStates;
|
||||||
|
|
||||||
|
QString _currentStateVar;
|
||||||
|
QString _triggerRandomSwitchVar;
|
||||||
|
QString _transitionVar;
|
||||||
|
float _triggerTimeMin { 10.0f };
|
||||||
|
float _triggerTimeMax { 20.0f };
|
||||||
|
float _triggerTime { 0.0f };
|
||||||
|
float _randomSwitchTimeMin { 10.0f };
|
||||||
|
float _randomSwitchTimeMax { 20.0f };
|
||||||
|
float _randomSwitchTime { 0.0f };
|
||||||
|
|
||||||
|
private:
|
||||||
|
// no copies
|
||||||
|
AnimRandomSwitch(const AnimRandomSwitch&) = delete;
|
||||||
|
AnimRandomSwitch& operator=(const AnimRandomSwitch&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AnimRandomSwitch_h
|
|
@ -1480,13 +1480,15 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
||||||
if (_animNode && _enabledAnimations) {
|
if (_animNode && _enabledAnimations) {
|
||||||
DETAILED_PERFORMANCE_TIMER("handleTriggers");
|
DETAILED_PERFORMANCE_TIMER("handleTriggers");
|
||||||
|
|
||||||
|
++_evaluationCount;
|
||||||
|
|
||||||
updateAnimationStateHandlers();
|
updateAnimationStateHandlers();
|
||||||
_animVars.setRigToGeometryTransform(_rigToGeometryTransform);
|
_animVars.setRigToGeometryTransform(_rigToGeometryTransform);
|
||||||
if (_networkNode) {
|
if (_networkNode) {
|
||||||
_networkVars.setRigToGeometryTransform(_rigToGeometryTransform);
|
_networkVars.setRigToGeometryTransform(_rigToGeometryTransform);
|
||||||
}
|
}
|
||||||
AnimContext context(_enableDebugDrawIKTargets, _enableDebugDrawIKConstraints, _enableDebugDrawIKChains,
|
AnimContext context(_enableDebugDrawIKTargets, _enableDebugDrawIKConstraints, _enableDebugDrawIKChains,
|
||||||
getGeometryToRigTransform(), rigToWorldTransform);
|
getGeometryToRigTransform(), rigToWorldTransform, _evaluationCount);
|
||||||
|
|
||||||
// evaluate the animation
|
// evaluate the animation
|
||||||
AnimVariantMap triggersOut;
|
AnimVariantMap triggersOut;
|
||||||
|
@ -2009,8 +2011,35 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_animVars.set("isTalking", params.isTalking);
|
if (_previousIsTalking != params.isTalking) {
|
||||||
_animVars.set("notIsTalking", !params.isTalking);
|
if (_talkIdleInterpTime < 1.0f) {
|
||||||
|
_talkIdleInterpTime = 1.0f - _talkIdleInterpTime;
|
||||||
|
} else {
|
||||||
|
_talkIdleInterpTime = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_previousIsTalking = params.isTalking;
|
||||||
|
|
||||||
|
const float TOTAL_EASE_IN_TIME = 0.75f;
|
||||||
|
const float TOTAL_EASE_OUT_TIME = 1.5f;
|
||||||
|
if (params.isTalking) {
|
||||||
|
if (_talkIdleInterpTime < 1.0f) {
|
||||||
|
_talkIdleInterpTime += dt / TOTAL_EASE_IN_TIME;
|
||||||
|
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
||||||
|
_animVars.set("idleOverlayAlpha", easeOutInValue);
|
||||||
|
} else {
|
||||||
|
_animVars.set("idleOverlayAlpha", 1.0f);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_talkIdleInterpTime < 1.0f) {
|
||||||
|
_talkIdleInterpTime += dt / TOTAL_EASE_OUT_TIME;
|
||||||
|
float easeOutInValue = _talkIdleInterpTime < 0.5f ? 4.0f * powf(_talkIdleInterpTime, 3.0f) : 4.0f * powf((_talkIdleInterpTime - 1.0f), 3.0f) + 1.0f;
|
||||||
|
float talkAlpha = 1.0f - easeOutInValue;
|
||||||
|
_animVars.set("idleOverlayAlpha", talkAlpha);
|
||||||
|
} else {
|
||||||
|
_animVars.set("idleOverlayAlpha", 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
|
_headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
|
||||||
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
|
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
|
||||||
|
|
|
@ -418,9 +418,12 @@ protected:
|
||||||
HandAnimState _rightHandAnimState;
|
HandAnimState _rightHandAnimState;
|
||||||
HandAnimState _leftHandAnimState;
|
HandAnimState _leftHandAnimState;
|
||||||
std::map<QString, RoleAnimState> _roleAnimStates;
|
std::map<QString, RoleAnimState> _roleAnimStates;
|
||||||
|
int _evaluationCount{ 0 };
|
||||||
|
|
||||||
float _leftHandOverlayAlpha { 0.0f };
|
float _leftHandOverlayAlpha { 0.0f };
|
||||||
float _rightHandOverlayAlpha { 0.0f };
|
float _rightHandOverlayAlpha { 0.0f };
|
||||||
|
float _talkIdleInterpTime { 0.0f };
|
||||||
|
bool _previousIsTalking { false };
|
||||||
|
|
||||||
SimpleMovingAverage _averageForwardSpeed { 10 };
|
SimpleMovingAverage _averageForwardSpeed { 10 };
|
||||||
SimpleMovingAverage _averageLateralSpeed { 10 };
|
SimpleMovingAverage _averageLateralSpeed { 10 };
|
||||||
|
|
|
@ -94,7 +94,7 @@ void makeTestFBXJoints(HFMModel& hfmModel) {
|
||||||
|
|
||||||
void AnimInverseKinematicsTests::testSingleChain() {
|
void AnimInverseKinematicsTests::testSingleChain() {
|
||||||
|
|
||||||
AnimContext context(false, false, false, glm::mat4(), glm::mat4());
|
AnimContext context(false, false, false, glm::mat4(), glm::mat4(), 0);
|
||||||
|
|
||||||
HFMModel hfmModel;
|
HFMModel hfmModel;
|
||||||
makeTestFBXJoints(hfmModel);
|
makeTestFBXJoints(hfmModel);
|
||||||
|
|
|
@ -72,7 +72,7 @@ static float framesToSec(float secs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimTests::testClipEvaulate() {
|
void AnimTests::testClipEvaulate() {
|
||||||
AnimContext context(false, false, false, glm::mat4(), glm::mat4());
|
AnimContext context(false, false, false, glm::mat4(), glm::mat4(), 0);
|
||||||
QString id = "myClipNode";
|
QString id = "myClipNode";
|
||||||
QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
|
QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
|
||||||
float startFrame = 2.0f;
|
float startFrame = 2.0f;
|
||||||
|
@ -109,7 +109,7 @@ void AnimTests::testClipEvaulate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimTests::testClipEvaulateWithVars() {
|
void AnimTests::testClipEvaulateWithVars() {
|
||||||
AnimContext context(false, false, false, glm::mat4(), glm::mat4());
|
AnimContext context(false, false, false, glm::mat4(), glm::mat4(), 0);
|
||||||
QString id = "myClipNode";
|
QString id = "myClipNode";
|
||||||
QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
|
QString url = "https://hifi-public.s3.amazonaws.com/ozan/support/FightClubBotTest1/Animations/standard_idle.fbx";
|
||||||
float startFrame = 2.0f;
|
float startFrame = 2.0f;
|
||||||
|
|
Loading…
Reference in a new issue