bool processStateMachineNode(AnimNode::Pointer node, const QJsonObject& jsonObj, const QString& nodeId, const QUrl& jsonUrl) { auto smNode = std::static_pointer_cast<AnimStateMachine>(node); assert(smNode); READ_STRING(currentState, jsonObj, nodeId, jsonUrl, false); auto statesValue = jsonObj.value("states"); if (!statesValue.isArray()) { qCCritical(animation) << "AnimNodeLoader, bad array \"states\" in stateMachine 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<AnimStateMachine::State::Pointer, StringPair>; TransitionMap transitionMap; using StateMap = std::map<QString, AnimStateMachine::State::Pointer>; StateMap stateMap; auto statesArray = statesValue.toArray(); for (const auto& stateValue : statesArray) { if (!stateValue.isObject()) { qCCritical(animation) << "AnimNodeLoader, bad state object in \"states\", id =" << nodeId; return false; } auto stateObj = stateValue.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_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 stateMachine child (state) with nodeId =" << nodeId << "stateId =" << id; return false; } AnimStateMachine::InterpType interpTypeEnum = AnimStateMachine::InterpType::SnapshotPrev; // default value if (!interpType.isEmpty()) { interpTypeEnum = stringToInterpType(interpType); if (interpTypeEnum == AnimStateMachine::InterpType::NumTypes) { qCCritical(animation) << "AnimNodeLoader, bad interpType on stateMachine state, nodeId = " << nodeId << "stateId =" << id; return false; } } auto statePtr = std::make_shared<AnimStateMachine::State>(id, iter->second, interpTarget, interpDuration, interpTypeEnum); assert(statePtr); if (!interpTargetVar.isEmpty()) { statePtr->setInterpTargetVar(interpTargetVar); } if (!interpDurationVar.isEmpty()) { statePtr->setInterpDurationVar(interpDurationVar); } if (!interpTypeVar.isEmpty()) { statePtr->setInterpTypeVar(interpTypeVar); } smNode->addState(statePtr); stateMap.insert(StateMap::value_type(statePtr->getID(), statePtr)); auto transitionsValue = stateObj.value("transitions"); if (!transitionsValue.isArray()) { qCritical(animation) << "AnimNodeLoader, bad array \"transitions\" in stateMachine 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 \"transtions\", stateId =" << id << "nodeId =" << nodeId; return false; } auto transitionObj = transitionValue.toObject(); READ_STRING(var, transitionObj, nodeId, jsonUrl, false); READ_STRING(state, transitionObj, nodeId, jsonUrl, false); transitionMap.insert(TransitionMap::value_type(statePtr, StringPair(var, state))); } } // second pass: now iterate thru all transitions and add them to the appropriate states. for (auto& transition : transitionMap) { AnimStateMachine::State::Pointer srcState = transition.first; auto iter = stateMap.find(transition.second.second); if (iter != stateMap.end()) { srcState->addTransition(AnimStateMachine::State::Transition(transition.second.first, iter->second)); } else { qCCritical(animation) << "AnimNodeLoader, bad state machine transtion from srcState =" << srcState->_id << "dstState =" << transition.second.second << "nodeId =" << nodeId; return false; } } auto iter = stateMap.find(currentState); if (iter == stateMap.end()) { qCCritical(animation) << "AnimNodeLoader, bad currentState =" << currentState << "could not find child node" << "id =" << nodeId; } smNode->setCurrentState(iter->second); return true; }
/** * Creates a new state. Make sure the state doesn't exist when calling this * function! * This function also inserts the state into the _states map. * @param id The id of the new state. **/ State *createState(StateId id) { State *state = stateFactory(id); state->load(); _states.insert(StateMapPair(id, state)); return state; }