mirror of
https://github.com/lubosz/overte.git
synced 2025-04-14 14:46:55 +02:00
AnimNodeLoader support for InverseKinematics node.
This commit is contained in:
parent
c1d7287652
commit
756eb54a0a
6 changed files with 340 additions and 220 deletions
|
@ -22,38 +22,7 @@ AnimController::~AnimController() {
|
|||
}
|
||||
|
||||
const AnimPoseVec& AnimController::evaluate(const AnimVariantMap& animVars, float dt, Triggers& triggersOut) {
|
||||
_alpha = animVars.lookup(_alphaVar, _alpha);
|
||||
|
||||
for (auto& jointVar : _jointVars) {
|
||||
if (!jointVar.hasPerformedJointLookup) {
|
||||
QString qJointName = QString::fromStdString(jointVar.jointName);
|
||||
jointVar.jointIndex = _skeleton->nameToJointIndex(qJointName);
|
||||
if (jointVar.jointIndex < 0) {
|
||||
qCWarning(animation) << "AnimController could not find jointName" << jointVar.jointIndex << "in skeleton";
|
||||
}
|
||||
jointVar.hasPerformedJointLookup = true;
|
||||
}
|
||||
if (jointVar.jointIndex >= 0) {
|
||||
|
||||
// jointVar is an absolute rotation, if it is not set we will use the bindPose as our default value
|
||||
AnimPose defaultPose = _skeleton->getAbsoluteBindPose(jointVar.jointIndex);
|
||||
glm::quat absRot = animVars.lookup(jointVar.var, defaultPose.rot);
|
||||
|
||||
// because jointVar is absolute, we must use an absolute parent frame to convert into a relative pose
|
||||
// here we use the bind pose
|
||||
int parentIndex = _skeleton->getParentIndex(jointVar.jointIndex);
|
||||
glm::quat parentAbsRot;
|
||||
if (parentIndex >= 0) {
|
||||
parentAbsRot = _skeleton->getAbsoluteBindPose(parentIndex).rot;
|
||||
}
|
||||
|
||||
// convert from absolute to relative
|
||||
glm::quat relRot = glm::inverse(parentAbsRot) * absRot;
|
||||
_poses[jointVar.jointIndex] = AnimPose(defaultPose.scale, relRot, defaultPose.trans);
|
||||
}
|
||||
}
|
||||
|
||||
return _poses;
|
||||
return overlay(animVars, dt, triggersOut, _skeleton->getRelativeBindPoses());
|
||||
}
|
||||
|
||||
const AnimPoseVec& AnimController::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
|
@ -64,7 +33,7 @@ const AnimPoseVec& AnimController::overlay(const AnimVariantMap& animVars, float
|
|||
QString qJointName = QString::fromStdString(jointVar.jointName);
|
||||
jointVar.jointIndex = _skeleton->nameToJointIndex(qJointName);
|
||||
if (jointVar.jointIndex < 0) {
|
||||
qCWarning(animation) << "AnimController could not find jointName" << jointVar.jointIndex << "in skeleton";
|
||||
qCWarning(animation) << "AnimController could not find jointName" << qJointName << "in skeleton";
|
||||
}
|
||||
jointVar.hasPerformedJointLookup = true;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "ElbowConstraint.h"
|
||||
#include "SwingTwistConstraint.h"
|
||||
#include "AnimationLogging.h"
|
||||
|
||||
AnimInverseKinematics::AnimInverseKinematics(const std::string& id) : AnimNode(AnimNode::Type::InverseKinematics, id) {
|
||||
}
|
||||
|
@ -51,6 +52,23 @@ void AnimInverseKinematics::computeAbsolutePoses(AnimPoseVec& absolutePoses) con
|
|||
}
|
||||
}
|
||||
|
||||
void AnimInverseKinematics::setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar) {
|
||||
// if there are dups, last one wins.
|
||||
_targetVarVec.push_back(IKTargetVar(jointName, positionVar.toStdString(), rotationVar.toStdString()));
|
||||
}
|
||||
|
||||
static int findRootJointInSkeleton(AnimSkeleton::ConstPointer skeleton, int index) {
|
||||
// walk down the skeleton hierarchy to find the joint's root
|
||||
int rootIndex = -1;
|
||||
int parentIndex = skeleton->getParentIndex(index);
|
||||
while (parentIndex != -1) {
|
||||
rootIndex = parentIndex;
|
||||
parentIndex = skeleton->getParentIndex(parentIndex);
|
||||
}
|
||||
return rootIndex;
|
||||
}
|
||||
|
||||
/*
|
||||
void AnimInverseKinematics::updateTarget(int index, const glm::vec3& position, const glm::quat& rotation) {
|
||||
std::map<int, IKTarget>::iterator targetItr = _absoluteTargets.find(index);
|
||||
if (targetItr != _absoluteTargets.end()) {
|
||||
|
@ -104,15 +122,47 @@ void AnimInverseKinematics::clearAllTargets() {
|
|||
_absoluteTargets.clear();
|
||||
_maxTargetIndex = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
//virtual
|
||||
const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) {
|
||||
|
||||
// NOTE: we assume that _relativePoses are up to date (e.g. loadPoses() was just called)
|
||||
if (_relativePoses.empty()) {
|
||||
return _relativePoses;
|
||||
}
|
||||
|
||||
relaxTowardDefaults(dt);
|
||||
// evaluate target vars
|
||||
for (auto& targetVar : _targetVarVec) {
|
||||
|
||||
// lazy look up of jointIndices and insertion into _absoluteTargets map
|
||||
if (!targetVar.hasPerformedJointLookup) {
|
||||
targetVar.jointIndex = _skeleton->nameToJointIndex(targetVar.jointName);
|
||||
if (targetVar.jointIndex >= 0) {
|
||||
// insert into _absoluteTargets map
|
||||
IKTarget target;
|
||||
target.pose = AnimPose::identity;
|
||||
target.rootIndex = findRootJointInSkeleton(_skeleton, targetVar.jointIndex);
|
||||
_absoluteTargets[targetVar.jointIndex] = target;
|
||||
} else {
|
||||
qCWarning(animation) << "AnimInverseKinematics could not find jointName" << targetVar.jointName << "in skeleton";
|
||||
}
|
||||
targetVar.hasPerformedJointLookup = true;
|
||||
}
|
||||
|
||||
if (targetVar.jointIndex >= 0) {
|
||||
// update pose in _absoluteTargets map
|
||||
auto iter = _absoluteTargets.find(targetVar.jointIndex);
|
||||
if (iter != _absoluteTargets.end()) {
|
||||
AnimPose defaultPose = _skeleton->getAbsolutePose(targetVar.jointIndex, _relativePoses);
|
||||
iter->second.pose.trans = animVars.lookup(targetVar.positionVar, defaultPose.trans);
|
||||
iter->second.pose.rot = animVars.lookup(targetVar.rotationVar, defaultPose.rot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RELAX! Don't do it.
|
||||
// relaxTowardDefaults(dt);
|
||||
|
||||
if (_absoluteTargets.empty()) {
|
||||
// no IK targets but still need to enforce constraints
|
||||
|
@ -258,6 +308,12 @@ const AnimPoseVec& AnimInverseKinematics::evaluate(const AnimVariantMap& animVar
|
|||
return _relativePoses;
|
||||
}
|
||||
|
||||
//virtual
|
||||
const AnimPoseVec& AnimInverseKinematics::overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) {
|
||||
loadPoses(underPoses);
|
||||
return evaluate(animVars, dt, triggersOut);
|
||||
}
|
||||
|
||||
RotationConstraint* AnimInverseKinematics::getConstraint(int index) {
|
||||
RotationConstraint* constraint = nullptr;
|
||||
std::map<int, RotationConstraint*>::iterator constraintItr = _constraints.find(index);
|
||||
|
@ -284,7 +340,7 @@ void AnimInverseKinematics::initConstraints() {
|
|||
if (!_skeleton) {
|
||||
return;
|
||||
}
|
||||
// We create constraints for the joints shown here
|
||||
// We create constraints for the joints shown here
|
||||
// (and their Left counterparts if applicable).
|
||||
//
|
||||
// O RightHand
|
||||
|
@ -543,11 +599,23 @@ void AnimInverseKinematics::initConstraints() {
|
|||
|
||||
void AnimInverseKinematics::setSkeletonInternal(AnimSkeleton::ConstPointer skeleton) {
|
||||
AnimNode::setSkeletonInternal(skeleton);
|
||||
|
||||
// invalidate all targetVars
|
||||
for (auto& targetVar : _targetVarVec) {
|
||||
targetVar.hasPerformedJointLookup = false;
|
||||
}
|
||||
|
||||
// invalidate all targets
|
||||
_absoluteTargets.clear();
|
||||
|
||||
// No constraints!
|
||||
/*
|
||||
if (skeleton) {
|
||||
initConstraints();
|
||||
} else {
|
||||
clearConstraints();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void AnimInverseKinematics::relaxTowardDefaults(float dt) {
|
||||
|
|
|
@ -27,6 +27,9 @@ public:
|
|||
const AnimPoseVec& getRelativePoses() const { return _relativePoses; }
|
||||
void computeAbsolutePoses(AnimPoseVec& absolutePoses) const;
|
||||
|
||||
void setTargetVars(const QString& jointName, const QString& positionVar, const QString& rotationVar);
|
||||
|
||||
/*
|
||||
/// \param index index of end effector
|
||||
/// \param position target position in the frame of the end-effector's root (not the parent!)
|
||||
/// \param rotation target rotation in the frame of the end-effector's root (not the parent!)
|
||||
|
@ -39,8 +42,10 @@ public:
|
|||
|
||||
void clearTarget(int index);
|
||||
void clearAllTargets();
|
||||
*/
|
||||
|
||||
virtual const AnimPoseVec& evaluate(const AnimVariantMap& animVars, float dt, AnimNode::Triggers& triggersOut) override;
|
||||
virtual const AnimPoseVec& overlay(const AnimVariantMap& animVars, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) override;
|
||||
|
||||
protected:
|
||||
virtual void setSkeletonInternal(AnimSkeleton::ConstPointer skeleton);
|
||||
|
@ -54,12 +59,28 @@ protected:
|
|||
void clearConstraints();
|
||||
void initConstraints();
|
||||
|
||||
struct IKTargetVar {
|
||||
IKTargetVar(const QString& jointNameIn, const std::string& positionVarIn, const std::string& rotationVarIn) :
|
||||
jointName(jointNameIn),
|
||||
positionVar(positionVarIn),
|
||||
rotationVar(rotationVarIn),
|
||||
jointIndex(-1),
|
||||
hasPerformedJointLookup(false) {}
|
||||
|
||||
std::string positionVar;
|
||||
std::string rotationVar;
|
||||
QString jointName;
|
||||
int jointIndex; // cached joint index
|
||||
bool hasPerformedJointLookup = false;
|
||||
};
|
||||
|
||||
struct IKTarget {
|
||||
AnimPose pose; // in root-frame
|
||||
int rootIndex; // cached root index
|
||||
AnimPose pose;
|
||||
int rootIndex;
|
||||
};
|
||||
|
||||
std::map<int, RotationConstraint*> _constraints;
|
||||
std::vector<IKTargetVar> _targetVarVec;
|
||||
std::map<int, IKTarget> _absoluteTargets; // IK targets of end-points
|
||||
AnimPoseVec _defaultRelativePoses; // poses of the relaxed state
|
||||
AnimPoseVec _relativePoses; // current relative poses
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "AnimNodeLoader.h"
|
||||
#include "AnimStateMachine.h"
|
||||
#include "AnimController.h"
|
||||
#include "AnimInverseKinematics.h"
|
||||
|
||||
using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
using NodeProcessFunc = AnimNode::Pointer (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
@ -31,6 +32,7 @@ static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const Q
|
|||
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 loadControllerNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
|
||||
// called after children have been loaded
|
||||
// returns node on success, nullptr on failure.
|
||||
|
@ -39,6 +41,7 @@ static AnimNode::Pointer processBlendLinearNode(AnimNode::Pointer node, const QJ
|
|||
static AnimNode::Pointer processOverlayNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return node; }
|
||||
AnimNode::Pointer processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);
|
||||
static AnimNode::Pointer processControllerNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return node; }
|
||||
static AnimNode::Pointer processInverseKinematicsNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) { return node; }
|
||||
|
||||
static const char* animNodeTypeToString(AnimNode::Type type) {
|
||||
switch (type) {
|
||||
|
@ -47,7 +50,7 @@ static const char* animNodeTypeToString(AnimNode::Type type) {
|
|||
case AnimNode::Type::Overlay: return "overlay";
|
||||
case AnimNode::Type::StateMachine: return "stateMachine";
|
||||
case AnimNode::Type::Controller: return "controller";
|
||||
case AnimNode::Type::InverseKinematics: return nullptr;
|
||||
case AnimNode::Type::InverseKinematics: return "inverseKinematics";
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -60,7 +63,7 @@ static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::Overlay: return loadOverlayNode;
|
||||
case AnimNode::Type::StateMachine: return loadStateMachineNode;
|
||||
case AnimNode::Type::Controller: return loadControllerNode;
|
||||
case AnimNode::Type::InverseKinematics: return nullptr;
|
||||
case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -73,7 +76,7 @@ static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {
|
|||
case AnimNode::Type::Overlay: return processOverlayNode;
|
||||
case AnimNode::Type::StateMachine: return processStateMachineNode;
|
||||
case AnimNode::Type::Controller: return processControllerNode;
|
||||
case AnimNode::Type::InverseKinematics: return nullptr;
|
||||
case AnimNode::Type::InverseKinematics: return processInverseKinematicsNode;
|
||||
case AnimNode::Type::NumTypes: return nullptr;
|
||||
};
|
||||
return nullptr;
|
||||
|
@ -312,6 +315,33 @@ static AnimNode::Pointer loadControllerNode(const QJsonObject& jsonObj, const QS
|
|||
return node;
|
||||
}
|
||||
|
||||
AnimNode::Pointer loadInverseKinematicsNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {
|
||||
auto node = std::make_shared<AnimInverseKinematics>(id.toStdString());
|
||||
|
||||
auto targetsValue = jsonObj.value("targets");
|
||||
if (!targetsValue.isArray()) {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad array \"targets\" in inverseKinematics node, id =" << id << ", url =" << jsonUrl.toDisplayString();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto targetsArray = targetsValue.toArray();
|
||||
for (const auto& targetValue : targetsArray) {
|
||||
if (!targetValue.isObject()) {
|
||||
qCCritical(animation) << "AnimNodeLoader, bad state object in \"targets\", id =" << id << ", url =" << jsonUrl.toDisplayString();
|
||||
return nullptr;
|
||||
}
|
||||
auto targetObj = targetValue.toObject();
|
||||
|
||||
READ_STRING(jointName, targetObj, id, jsonUrl);
|
||||
READ_STRING(positionVar, targetObj, id, jsonUrl);
|
||||
READ_STRING(rotationVar, targetObj, id, jsonUrl);
|
||||
|
||||
node->setTargetVars(jointName, positionVar, rotationVar);
|
||||
};
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void buildChildMap(std::map<std::string, AnimNode::Pointer>& map, AnimNode::Pointer node) {
|
||||
for ( auto child : node->_children ) {
|
||||
map.insert(std::pair<std::string, AnimNode::Pointer>(child->_id, child));
|
||||
|
|
|
@ -444,6 +444,9 @@ void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPos
|
|||
static float t = 0.0f;
|
||||
_animVars.set("sine", static_cast<float>(0.5 * sin(t) + 0.5));
|
||||
|
||||
_animVars.set("rightHandPosition", glm::vec3(cos(t), sin(t) + 1.0f, 1.0f));
|
||||
_animVars.set("leftHandPosition", glm::vec3(cos(-t + 3.1415f), sin(-t + 3.1415f) + 1.0f, 1.0f));
|
||||
|
||||
// default anim vars to notMoving and notTurning
|
||||
_animVars.set("isMovingForward", false);
|
||||
_animVars.set("isMovingBackward", false);
|
||||
|
|
|
@ -1,209 +1,238 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"root": {
|
||||
"id": "root",
|
||||
"id": "ikOverlay",
|
||||
"type": "overlay",
|
||||
"data": {
|
||||
"alpha": 1.0,
|
||||
"boneSet": "spineOnly"
|
||||
"boneSet": "fullBody"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "spineLean",
|
||||
"type": "controller",
|
||||
"id": "ik",
|
||||
"type": "inverseKinematics",
|
||||
"data": {
|
||||
"alpha": 1.0,
|
||||
"joints": [
|
||||
{ "var": "lean", "jointName": "Spine" }
|
||||
"targets": [
|
||||
{
|
||||
"jointName": "RightHand",
|
||||
"positionVar": "rightHandPosition",
|
||||
"rotationVar": "rightHandRotation"
|
||||
},
|
||||
{
|
||||
"jointName": "LeftHand",
|
||||
"positionVar": "leftHandPosition",
|
||||
"rotationVar": "leftHandRotation"
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "mainStateMachine",
|
||||
"type": "stateMachine",
|
||||
"id": "controllerOverlay",
|
||||
"type": "overlay",
|
||||
"data": {
|
||||
"currentState": "idle",
|
||||
"states": [
|
||||
{
|
||||
"id": "idle",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "walkFwd",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "walkBwd",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "strafeRight",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "strafeLeft",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "turnRight",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotTurning", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "turnLeft",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotTurning", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" }
|
||||
]
|
||||
}
|
||||
]
|
||||
"alpha": 1.0,
|
||||
"boneSet": "spineOnly"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "idle",
|
||||
"type": "clip",
|
||||
"id": "spineLean",
|
||||
"type": "controller",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
"alpha": 1.0,
|
||||
"joints": [
|
||||
{ "var": "lean", "jointName": "Spine" }
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "walkFwd",
|
||||
"type": "clip",
|
||||
"id": "mainStateMachine",
|
||||
"type": "stateMachine",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 35.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true,
|
||||
"timeScaleVar": "walkTimeScale"
|
||||
"currentState": "idle",
|
||||
"states": [
|
||||
{
|
||||
"id": "idle",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "walkFwd",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "walkBwd",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "strafeRight",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "strafeLeft",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotMoving", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "turnRight",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotTurning", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningLeft", "state": "turnLeft" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "turnLeft",
|
||||
"interpTarget": 6,
|
||||
"interpDuration": 6,
|
||||
"transitions": [
|
||||
{ "var": "isNotTurning", "state": "idle" },
|
||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||
{ "var": "isTurningRight", "state": "turnRight" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "walkBwd",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 37.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true,
|
||||
"timeScaleVar": "walkTimeScale"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "turnLeft",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 28.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "turnRight",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 30.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "strafeLeft",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 31.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "strafeRight",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 31.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
"children": [
|
||||
{
|
||||
"id": "idle",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/idle.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 90.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "walkFwd",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 35.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true,
|
||||
"timeScaleVar": "walkTimeScale"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "walkBwd",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_bwd.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 37.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true,
|
||||
"timeScaleVar": "walkTimeScale"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "turnLeft",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 28.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "turnRight",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/turn_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 30.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "strafeLeft",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_left.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 31.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": "strafeRight",
|
||||
"type": "clip",
|
||||
"data": {
|
||||
"url": "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/strafe_right.fbx",
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 31.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue