1141 lines · 48.1 KB · C++
RawDownload
////  AnimNodeLoader.cpp////  Created by Anthony J. Thibault on 9/2/15.//  Copyright (c) 2015 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 "AnimNodeLoader.h" #include <QJsonDocument>#include <QJsonObject>#include <QJsonArray>#include <QFile> #include "AnimNode.h"#include "AnimClip.h"#include "AnimBlendLinear.h"#include "AnimBlendLinearMove.h"#include "AnimationLogging.h"#include "AnimOverlay.h"#include "AnimStateMachine.h"#include "AnimRandomSwitch.h"#include "AnimManipulator.h"#include "AnimInverseKinematics.h"#include "AnimDefaultPose.h"#include "AnimTwoBoneIK.h"#include "AnimSplineIK.h"#include "AnimPoleVectorConstraint.h"#include "AnimBlendDirectional.h"#include "AnimUtil.h" using NodeLoaderFunc = AnimNode::Pointer (*)(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);using NodeProcessFunc = bool (*)(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); // factory functionsstatic AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);static AnimNode::Pointer loadBlendLinearNode(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 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 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 loadTwoBoneIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);static AnimNode::Pointer loadSplineIKNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);static AnimNode::Pointer loadPoleVectorConstraintNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);static AnimNode::Pointer loadBlendDirectionalNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static const float ANIM_GRAPH_LOAD_PRIORITY = 10.0f; // called after children have been loaded// returns node on success, nullptr on failure.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 processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl);bool processBlendDirectionalNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl); static const char* animNodeTypeToString(AnimNode::Type type) {    switch (type) {    case AnimNode::Type::Clip: return "clip";    case AnimNode::Type::BlendLinear: return "blendLinear";    case AnimNode::Type::BlendLinearMove: return "blendLinearMove";    case AnimNode::Type::Overlay: return "overlay";    case AnimNode::Type::StateMachine: return "stateMachine";    case AnimNode::Type::RandomSwitchStateMachine: return "randomSwitchStateMachine";    case AnimNode::Type::Manipulator: return "manipulator";    case AnimNode::Type::InverseKinematics: return "inverseKinematics";    case AnimNode::Type::DefaultPose: return "defaultPose";    case AnimNode::Type::TwoBoneIK: return "twoBoneIK";    case AnimNode::Type::SplineIK: return "splineIK";    case AnimNode::Type::PoleVectorConstraint: return "poleVectorConstraint";    case AnimNode::Type::BlendDirectional: return "blendDirectional";    case AnimNode::Type::NumTypes: return nullptr;    };    return nullptr;} static AnimNode::Type stringToAnimNodeType(const QString& str) {    // O(n), move to map when number of types becomes large.    const int NUM_TYPES = static_cast<int>(AnimNode::Type::NumTypes);    for (int i = 0; i < NUM_TYPES; i++) {        AnimNode::Type type = static_cast<AnimNode::Type>(i);        if (str == animNodeTypeToString(type)) {            return type;        }    }    return AnimNode::Type::NumTypes;} static AnimStateMachine::InterpType stringToInterpType(const QString& str) {    if (str == "snapshotBoth") {        return AnimStateMachine::InterpType::SnapshotBoth;    } else if (str == "snapshotPrev") {        return AnimStateMachine::InterpType::SnapshotPrev;    } else if (str == "evaluateBoth") {        return AnimStateMachine::InterpType::EvaluateBoth;    } else {        return AnimStateMachine::InterpType::NumTypes;    }} static AnimRandomSwitch::InterpType stringToRandomInterpType(const QString& str) {    if (str == "snapshotBoth") {        return AnimRandomSwitch::InterpType::SnapshotBoth;    } else if (str == "snapshotPrev") {        return AnimRandomSwitch::InterpType::SnapshotPrev;    } else if (str == "evaluateBoth") {        return AnimRandomSwitch::InterpType::EvaluateBoth;    } else {        return AnimRandomSwitch::InterpType::NumTypes;    }} static EasingType stringToEasingType(const QString& str) {    if (str == "linear") {        return EasingType_Linear;    } else if (str == "easeInSine") {        return EasingType_EaseInSine;    } else if (str == "easeOutSine") {        return EasingType_EaseOutSine;    } else if (str == "easeInOutSine") {        return EasingType_EaseInOutSine;    } else if (str == "easeInQuad") {        return EasingType_EaseInQuad;    } else if (str == "easeOutQuad") {        return EasingType_EaseOutQuad;    } else if (str == "easeInOutQuad") {        return EasingType_EaseInOutQuad;    } else if (str == "easeInCubic") {        return EasingType_EaseInCubic;    } else if (str == "easeOutCubic") {        return EasingType_EaseOutCubic;    } else if (str == "easeInOutCubic") {        return EasingType_EaseInOutCubic;    } else if (str == "easeInQuart") {        return EasingType_EaseInQuart;    } else if (str == "easeOutQuart") {        return EasingType_EaseOutQuart;    } else if (str == "easeInOutQuart") {        return EasingType_EaseInOutQuart;    } else if (str == "easeInQuint") {        return EasingType_EaseInQuint;    } else if (str == "easeOutQuint") {        return EasingType_EaseOutQuint;    } else if (str == "easeInOutQuint") {        return EasingType_EaseInOutQuint;    } else if (str == "easeInExpo") {        return EasingType_EaseInExpo;    } else if (str == "easeOutExpo") {        return EasingType_EaseOutExpo;    } else if (str == "easeInOutExpo") {        return EasingType_EaseInOutExpo;    } else if (str == "easeInCirc") {        return EasingType_EaseInCirc;    } else if (str == "easeOutCirc") {        return EasingType_EaseOutCirc;    } else if (str == "easeInOutCirc") {        return EasingType_EaseInOutCirc;    } else {        return EasingType_NumTypes;    }} static AnimBlendType stringToAnimBlendType(const QString& str) {    if (str == "normal") {        return AnimBlendType_Normal;    } else if (str == "addRelative") {        return AnimBlendType_AddRelative;    } else if (str == "addAbsolute") {        return AnimBlendType_AddAbsolute;    } else {        return AnimBlendType_NumTypes;    }}  static const char* animManipulatorJointVarTypeToString(AnimManipulator::JointVar::Type type) {    switch (type) {    case AnimManipulator::JointVar::Type::Absolute: return "absolute";    case AnimManipulator::JointVar::Type::Relative: return "relative";    case AnimManipulator::JointVar::Type::UnderPose: return "underPose";    case AnimManipulator::JointVar::Type::Default: return "default";    case AnimManipulator::JointVar::Type::NumTypes: return nullptr;    };    return nullptr;} static AnimManipulator::JointVar::Type stringToAnimManipulatorJointVarType(const QString& str) {    // O(n), move to map when number of types becomes large.    const int NUM_TYPES = static_cast<int>(AnimManipulator::JointVar::Type::NumTypes);    for (int i = 0; i < NUM_TYPES; i++) {        AnimManipulator::JointVar::Type type = static_cast<AnimManipulator::JointVar::Type>(i);        if (str == animManipulatorJointVarTypeToString(type)) {            return type;        }    }    return AnimManipulator::JointVar::Type::NumTypes;} static NodeLoaderFunc animNodeTypeToLoaderFunc(AnimNode::Type type) {    switch (type) {    case AnimNode::Type::Clip: return loadClipNode;    case AnimNode::Type::BlendLinear: return loadBlendLinearNode;    case AnimNode::Type::BlendLinearMove: return loadBlendLinearMoveNode;    case AnimNode::Type::Overlay: return loadOverlayNode;    case AnimNode::Type::StateMachine: return loadStateMachineNode;    case AnimNode::Type::RandomSwitchStateMachine: return loadRandomSwitchStateMachineNode;    case AnimNode::Type::Manipulator: return loadManipulatorNode;    case AnimNode::Type::InverseKinematics: return loadInverseKinematicsNode;    case AnimNode::Type::DefaultPose: return loadDefaultPoseNode;    case AnimNode::Type::TwoBoneIK: return loadTwoBoneIKNode;    case AnimNode::Type::SplineIK: return loadSplineIKNode;    case AnimNode::Type::PoleVectorConstraint: return loadPoleVectorConstraintNode;    case AnimNode::Type::BlendDirectional: return loadBlendDirectionalNode;    case AnimNode::Type::NumTypes: return nullptr;    };    return nullptr;} static NodeProcessFunc animNodeTypeToProcessFunc(AnimNode::Type type) {    switch (type) {    case AnimNode::Type::Clip: return processDoNothing;    case AnimNode::Type::BlendLinear: return processDoNothing;    case AnimNode::Type::BlendLinearMove: return processDoNothing;    case AnimNode::Type::Overlay: return processDoNothing;    case AnimNode::Type::StateMachine: return processStateMachineNode;    case AnimNode::Type::RandomSwitchStateMachine: return processRandomSwitchStateMachineNode;    case AnimNode::Type::Manipulator: return processDoNothing;    case AnimNode::Type::InverseKinematics: return processDoNothing;    case AnimNode::Type::DefaultPose: return processDoNothing;    case AnimNode::Type::TwoBoneIK: return processDoNothing;    case AnimNode::Type::SplineIK: return processDoNothing;    case AnimNode::Type::PoleVectorConstraint: return processDoNothing;    case AnimNode::Type::BlendDirectional: return processBlendDirectionalNode;    case AnimNode::Type::NumTypes: return nullptr;    };    return nullptr;} #define READ_STRING(NAME, JSON_OBJ, ID, URL, ERROR_RETURN)              \    auto NAME##_VAL = JSON_OBJ.value(#NAME);                            \    if (!NAME##_VAL.isString()) {                                       \        qCCritical(animation) << "AnimNodeLoader, error reading string" \                              << #NAME << ", id =" << ID                \                              << ", url =" << URL.toDisplayString();    \        return ERROR_RETURN;                                            \    }                                                                   \    QString NAME = NAME##_VAL.toString() #define READ_OPTIONAL_STRING(NAME, JSON_OBJ)                            \    auto NAME##_VAL = JSON_OBJ.value(#NAME);                            \    QString NAME;                                                       \    if (NAME##_VAL.isString()) {                                        \        NAME = NAME##_VAL.toString();                                   \    } #define READ_BOOL(NAME, JSON_OBJ, ID, URL, ERROR_RETURN)                \    auto NAME##_VAL = JSON_OBJ.value(#NAME);                            \    if (!NAME##_VAL.isBool()) {                                         \        qCCritical(animation) << "AnimNodeLoader, error reading bool"   \                              << #NAME << ", id =" << ID                \                              << ", url =" << URL.toDisplayString();    \        return ERROR_RETURN;                                            \    }                                                                   \    bool NAME = NAME##_VAL.toBool() #define READ_OPTIONAL_BOOL(NAME, JSON_OBJ, DEFAULT)                     \    auto NAME##_VAL = JSON_OBJ.value(#NAME);                            \    bool NAME = DEFAULT;                                                \    if (NAME##_VAL.isBool()) {                                          \        NAME = NAME##_VAL.toBool();                                     \    }                                                                   \    do {} while (0) #define READ_FLOAT(NAME, JSON_OBJ, ID, URL, ERROR_RETURN)               \    auto NAME##_VAL = JSON_OBJ.value(#NAME);                            \    if (!NAME##_VAL.isDouble()) {                                       \        qCCritical(animation) << "AnimNodeLoader, error reading double" \                              << #NAME << "id =" << ID                  \                              << ", url =" << URL.toDisplayString();    \        return ERROR_RETURN;                                            \    }                                                                   \    float NAME = (float)NAME##_VAL.toDouble() #define READ_OPTIONAL_FLOAT(NAME, JSON_OBJ, DEFAULT)                    \    auto NAME##_VAL = JSON_OBJ.value(#NAME);                            \    float NAME = (float)DEFAULT;                                        \    if (NAME##_VAL.isDouble()) {                                        \        NAME = (float)NAME##_VAL.toDouble();                            \    }                                                                   \    do {} while (0) #define READ_VEC3(NAME, JSON_OBJ, ID, URL, ERROR_RETURN)                \    auto NAME##_VAL = JSON_OBJ.value(#NAME);                            \    if (!NAME##_VAL.isArray()) {                                        \        qCCritical(animation) << "AnimNodeLoader, error reading vector" \                              << #NAME << "id =" << ID                  \                              << ", url =" << URL.toDisplayString();    \        return ERROR_RETURN;                                            \    }                                                                   \    QJsonArray NAME##_ARRAY = NAME##_VAL.toArray();                     \    if (NAME##_ARRAY.size() != 3) {                                     \        qCCritical(animation) << "AnimNodeLoader, vector size != 3"     \                              << #NAME << "id =" << ID                  \                              << ", url =" << URL.toDisplayString();    \        return ERROR_RETURN;                                            \    }                                                                   \    glm::vec3 NAME((float)NAME##_ARRAY.at(0).toDouble(),                \                   (float)NAME##_ARRAY.at(1).toDouble(),                \                   (float)NAME##_ARRAY.at(2).toDouble()) static AnimNode::Pointer loadNode(const QJsonObject& jsonObj, const QUrl& jsonUrl) {    auto idVal = jsonObj.value("id");    if (!idVal.isString()) {        qCCritical(animation) << "AnimNodeLoader, bad string \"id\"";        return nullptr;    }    QString id = idVal.toString();     auto typeVal = jsonObj.value("type");    if (!typeVal.isString()) {        qCCritical(animation) << "AnimNodeLoader, bad object \"type\", id =" << id;        return nullptr;    }    QString typeStr = typeVal.toString();    AnimNode::Type type = stringToAnimNodeType(typeStr);    if (type == AnimNode::Type::NumTypes) {        qCCritical(animation) << "AnimNodeLoader, unknown node type" << typeStr << ", id =" << id;        return nullptr;    }     auto dataValue = jsonObj.value("data");    if (!dataValue.isObject()) {        qCCritical(animation) << "AnimNodeLoader, bad string \"data\", id =" << id;        return nullptr;    }    auto dataObj = dataValue.toObject();     std::vector<QString> outputJoints;     auto outputJoints_VAL = dataObj.value("outputJoints");    if (outputJoints_VAL.isArray()) {        QJsonArray outputJoints_ARRAY = outputJoints_VAL.toArray();        for (int i = 0; i < outputJoints_ARRAY.size(); i++) {            outputJoints.push_back(outputJoints_ARRAY.at(i).toString());        }    }     assert((int)type >= 0 && type < AnimNode::Type::NumTypes);    auto node = (animNodeTypeToLoaderFunc(type))(dataObj, id, jsonUrl);    if (!node) {        return nullptr;    }     auto childrenValue = jsonObj.value("children");    if (!childrenValue.isArray()) {        qCCritical(animation) << "AnimNodeLoader, bad array \"children\", id =" << id;        return nullptr;    }    auto childrenArray = childrenValue.toArray();    for (const auto& childValue : childrenArray) {        if (!childValue.isObject()) {            qCCritical(animation) << "AnimNodeLoader, bad object in \"children\", id =" << id;            return nullptr;        }        AnimNode::Pointer child = loadNode(childValue.toObject(), jsonUrl);        if (child) {            node->addChild(child);        } else {            return nullptr;        }    }     if ((animNodeTypeToProcessFunc(type))(node, dataObj, id, jsonUrl)) {        for (auto&& outputJoint : outputJoints) {            node->addOutputJoint(outputJoint);        }        return node;    } else {        return nullptr;    }} static AnimNode::Pointer loadClipNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {     READ_STRING(url, jsonObj, id, jsonUrl, nullptr);    READ_FLOAT(startFrame, jsonObj, id, jsonUrl, nullptr);    READ_FLOAT(endFrame, jsonObj, id, jsonUrl, nullptr);    READ_FLOAT(timeScale, jsonObj, id, jsonUrl, nullptr);    READ_BOOL(loopFlag, jsonObj, id, jsonUrl, nullptr);    READ_OPTIONAL_BOOL(mirrorFlag, jsonObj, false);    READ_OPTIONAL_STRING(blendType, jsonObj);    READ_OPTIONAL_STRING(baseURL, jsonObj);    READ_OPTIONAL_FLOAT(baseFrame, jsonObj, 0.0f);     READ_OPTIONAL_STRING(startFrameVar, jsonObj);    READ_OPTIONAL_STRING(endFrameVar, jsonObj);    READ_OPTIONAL_STRING(timeScaleVar, jsonObj);    READ_OPTIONAL_STRING(loopFlagVar, jsonObj);    READ_OPTIONAL_STRING(mirrorFlagVar, jsonObj);      // animation urls can be relative to the containing url document.    auto tempUrl = QUrl(url);    tempUrl = jsonUrl.resolved(tempUrl);     AnimBlendType blendTypeEnum = AnimBlendType_Normal;  // default value    if (!blendType.isEmpty()) {        blendTypeEnum = stringToAnimBlendType(blendType);        if (blendTypeEnum == AnimBlendType_NumTypes) {            qCCritical(animation) << "AnimNodeLoader, bad blendType on clip, id = " << id;            return nullptr;        }    }     auto node = std::make_shared<AnimClip>(id, tempUrl.toString(), startFrame, endFrame, timeScale, loopFlag, mirrorFlag, blendTypeEnum, baseURL, baseFrame);     if (!startFrameVar.isEmpty()) {        node->setStartFrameVar(startFrameVar);    }    if (!endFrameVar.isEmpty()) {        node->setEndFrameVar(endFrameVar);    }    if (!timeScaleVar.isEmpty()) {        node->setTimeScaleVar(timeScaleVar);    }    if (!loopFlagVar.isEmpty()) {        node->setLoopFlagVar(loopFlagVar);    }    if (!mirrorFlagVar.isEmpty()) {        node->setMirrorFlagVar(mirrorFlagVar);    }     return node;} static AnimNode::Pointer loadBlendLinearNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {     READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);    READ_OPTIONAL_STRING(blendType, jsonObj);    READ_OPTIONAL_STRING(alphaVar, jsonObj);     AnimBlendType blendTypeEnum = AnimBlendType_Normal;  // default value    if (!blendType.isEmpty()) {        blendTypeEnum = stringToAnimBlendType(blendType);        if (blendTypeEnum == AnimBlendType_NumTypes) {            qCCritical(animation) << "AnimNodeLoader, bad blendType on blendLinear, id = " << id;            return nullptr;        }    }     auto node = std::make_shared<AnimBlendLinear>(id, alpha, blendTypeEnum);     if (!alphaVar.isEmpty()) {        node->setAlphaVar(alphaVar);    }     return node;} static AnimNode::Pointer loadBlendLinearMoveNode(const QJsonObject& jsonObj, const QString& id, const QUrl& jsonUrl) {     READ_FLOAT(alpha, jsonObj, id, jsonUrl, nullptr);    READ_FLOAT(desiredSpeed, jsonObj, id, jsonUrl, nullptr);     std::vector<float> characteristicSpeeds;    auto speedsValue = jsonObj.value("characteristicSpeeds");    if (!speedsValue.isArray()) {        qCCritical(animation) << "AnimNodeLoader, bad array \"characteristicSpeeds\" in blendLinearMove node, id =" << id;        return nullptr;    }     auto speedsArray = speedsValue.toArray();    for (const auto& speedValue : speedsArray) {        if (!speedValue.isDouble()) {            qCCritical(animation) << "AnimNodeLoader, bad number in \"characteristicSpeeds\", id =" << id;            return nullptr;        }        float speedVal = (float)speedValue.toDouble();        characteristicSpeeds.push_back(speedVal);    };     READ_OPTIONAL_STRING(alphaVar, jsonObj);    READ_OPTIONAL_STRING(desiredSpeedVar, jsonObj);     auto node = std::make_shared<AnimBlendLinearMove>(id, alpha, desiredSpeed, characteristicSpeeds);     if (!alphaVar.isEmpty()) {        node->setAlphaVar(alphaVar);    }     if (!desiredSpeedVar.isEmpty()) {        node->setDesiredSpeedVar(desiredSpeedVar);    }     return node;

Lines 1–500 of 1141.