Writing an Atoms operator (AtomsUnreal)

An Atoms operator is a specialized Atoms graph node having a built-in out pose port and specific functions to access the owner agent.

This operator adds an offset to a joint.

 

#pragma once #include <AtomsGraph/Ports.h> #include <Atoms/Globals.h> #include <Atoms/Graph/Operator.h> class MyJointTransformOperator : public Atoms::Operator { public: NODE_STANDARD_MEMBERS MyJointTransformOperator(); virtual ~MyJointTransformOperator(); virtual bool compute(const AtomsGraph::ComputeData* computeData) override; virtual void reset() override; private: AtomsGraph::PosePort* m_inPose; AtomsGraph::StringPort *m_inJointName; AtomsGraph::LongPort *m_inRotationOrder; AtomsGraph::BooleanPort *m_inWorldSpace; AtomsGraph::BooleanPort *m_inOffset; AtomsGraph::VectorPort *m_inTranslate; AtomsGraph::VectorPort *m_inRotate; AtomsGraph::VectorPort *m_inScale; AtomsGraph::DoublePort *m_inWeight; int m_jointId; bool m_first; };

 

#include "MyJointTransformOperator.h" #include <Atoms/Graph/Operators/OperatorIds.h> #include <Atoms/AgentTypes.h> #include <Atoms/Agent.h> #include <AtomsCore/Metadata/StringMetadata.h> #include <AtomsCore/Poser.h> using namespace Atoms; using namespace AtomsGraph; #define MY_JOINT_TRANSFORM_OPERATOR_ID 9999991 NODE_STANDARD_MEMBERS_IMPL(MyJointTransformOperator) unsigned int MyJointTransformOperator::staticTypeId() { return JOINT_TRANSFORM_OPERATOR_ID; } std::string MyJointTransformOperator::staticTypeStr() { return std::string("MyJointTransformOperator"); } AtomsCore::Euler::Order jtOpConvertRotateOrderToEulerRot(int value) { AtomsCore::Euler::Order returnOrder; switch (value) { case 0: returnOrder = AtomsCore::Euler::XYZ; break; case 1: returnOrder = AtomsCore::Euler::YZX; break; case 2: returnOrder = AtomsCore::Euler::ZXY; break; case 3: returnOrder = AtomsCore::Euler::XZY; break; case 4: returnOrder = AtomsCore::Euler::YXZ; break; case 5: returnOrder = AtomsCore::Euler::ZYX; break; default: returnOrder = AtomsCore::Euler::XYZ; break; } return returnOrder; } MyJointTransformOperator::MyJointTransformOperator() : Operator() { m_inJointName = new AtomsGraph::StringPort("jointName"); m_inJointName->set(""); addInputPort(m_inJointName); m_inOffset = new AtomsGraph::BooleanPort("offset"); addInputPort(m_inOffset); m_inRotationOrder = new AtomsGraph::LongPort("rotationOrder"); addInputPort(m_inRotationOrder); m_inWorldSpace = new AtomsGraph::BooleanPort("worldSpace"); addInputPort(m_inWorldSpace); m_inTranslate = new AtomsGraph::VectorPort("translate"); m_inTranslate->set(AtomsCore::Vector3(0, 0, 0)); addInputPort(m_inTranslate); m_inRotate = new AtomsGraph::VectorPort("rotate"); m_inRotate->set(AtomsCore::Vector3(0, 0, 0)); addInputPort(m_inRotate); m_inScale = new AtomsGraph::VectorPort("scale"); m_inScale->set(AtomsCore::Vector3(1, 1, 1)); addInputPort(m_inScale); m_inPose = new AtomsGraph::PosePort("inPose"); addInputPort(m_inPose); m_inWeight = new AtomsGraph::DoublePort("weight"); addInputPort(m_inWeight); m_jointId = -1; m_first = true; } MyJointTransformOperator::~MyJointTransformOperator() { } bool MyJointTransformOperator::compute(const AtomsGraph::ComputeData* computeData) { AtomsCore::Pose &inPose = m_inPose->getRef(); if (inPose.numJoints() == 0) { Logger::warning() << "Empty input pose"; return false; } AtomsCore::Pose &outPose = m_outPose->getRef(); outPose = inPose; if (!m_agent || !m_agent->agentType()) { AtomsUtils::Logger::error() << "Invalid agent type"; return false; } const AtomsCore::Skeleton& skeleton = m_agent->agentType()->skeleton(); if (m_first) { m_jointId = skeleton.jointId(m_inJointName->getRef()); } if (m_jointId == -1) { Logger::error() << "Could not find joint."; return false; } double weight = m_inWeight->get(); if (weight < 0.0001) return true; bool offset = m_inOffset->get(); bool worldSpace = m_inWorldSpace->get(); AtomsCore::Vector3 translate = m_inTranslate->get(); AtomsCore::Vector3 rotate = m_inRotate->get(); AtomsCore::Vector3 scale = m_inScale->get(); int rotationOrder = m_inRotationOrder->get(); AtomsCore::Poser poser(&skeleton); if (worldSpace) { AtomsCore::Matrix jointMtx; if (!offset) { AtomsCore::Matrix currentMtx = poser.getWorldMatrix(outPose, m_jointId); AtomsCore::Euler currEulerRotation; AtomsCore::Vector3 currScale(1.0, 1.0, 1.0); AtomsCore::Vector3 shear(0.0, 0.0, 0.0); AtomsCore::Vector3 currTranslation(0.0, 0.0, 0.0); AtomsMath::extractSHRT(currentMtx, currScale, shear, currEulerRotation, currTranslation); AtomsCore::Quaternion currRotation = currEulerRotation.toQuat(); scale = currScale * (1.0 - weight) + scale * weight; translate = currTranslation * (1.0 - weight) + translate * weight; AtomsCore::Euler rotation(rotate.x * M_PI / 180.0, rotate.y * M_PI / 180.0, rotate.z * M_PI / 180.0, jtOpConvertRotateOrderToEulerRot(rotationOrder)); AtomsCore::Quaternion quatTmp = AtomsMath::slerp(currRotation, rotation.toQuat(), weight); jointMtx.makeIdentity(); jointMtx.translate(translate); jointMtx = quatTmp.toMatrix44() * jointMtx; jointMtx.scale(scale); poser.setWorldMatrix(outPose, jointMtx, m_jointId); } else { AtomsCore::Vector3 currScale(1.0, 1.0, 1.0); AtomsCore::Vector3 currTranslation(0.0, 0.0, 0.0); AtomsCore::Quaternion currRotation; scale = currScale * (1.0 - weight) + scale * weight; translate = currTranslation * (1.0 - weight) + translate * weight; AtomsCore::Euler rotation(rotate.x * M_PI / 180.0, rotate.y * M_PI / 180.0, rotate.z * M_PI / 180.0, jtOpConvertRotateOrderToEulerRot(rotationOrder)); AtomsCore::Quaternion quatTmp = AtomsMath::slerp(currRotation, rotation.toQuat(), weight); jointMtx.makeIdentity(); jointMtx.translate(translate); jointMtx = quatTmp.toMatrix44() * jointMtx; jointMtx.scale(scale); AtomsCore::Matrix currentMtx = poser.getWorldMatrix(outPose, m_jointId); poser.setWorldMatrix(outPose, jointMtx * currentMtx, m_jointId); } } else { if (!offset) { auto& jPose = outPose.jointPose(m_jointId); AtomsCore::Vector3& currScale = jPose.scale; AtomsCore::Vector3& currTranslation = jPose.translation; AtomsCore::Quaternion& currRotation = jPose.rotation; AtomsCore::Euler rotation(rotate.x * M_PI / 180.0, rotate.y * M_PI / 180.0, rotate.z * M_PI / 180.0, jtOpConvertRotateOrderToEulerRot(rotationOrder)); jPose.rotation = AtomsMath::slerp(currRotation, rotation.toQuat(), weight); jPose.scale = currScale * (1.0 - weight) + scale * weight; jPose.translation = currTranslation * (1.0 - weight) + translate * weight; } else { auto& jPose = outPose.jointPose(m_jointId); AtomsCore::Vector3 currScale(1.0, 1.0, 1.0); AtomsCore::Vector3 currTranslation(0.0, 0.0, 0.0); AtomsCore::Quaternion currRotation; AtomsCore::Euler rotation(rotate.x * M_PI / 180.0, rotate.y * M_PI / 180.0, rotate.z * M_PI / 180.0, jtOpConvertRotateOrderToEulerRot(rotationOrder)); AtomsCore::Quaternion quatTmp = AtomsMath::slerp(currRotation, rotation.toQuat(), weight); jPose.scale.x *= currScale.x * (1.0 - weight) + scale.x * weight; jPose.scale.y *= currScale.y * (1.0 - weight) + scale.y * weight; jPose.scale.z *= currScale.z * (1.0 - weight) + scale.z * weight; jPose.translation += currTranslation * (1.0 - weight) + translate * weight; jPose.rotation = jPose.rotation * quatTmp; } } return true; } void MyJointTransformOperator::reset() { Operator::reset(); m_first = true; m_jointId = -1; m_inScale->set(AtomsCore::Vector3(1, 1, 1)); m_inRotate->set(AtomsCore::Vector3(0, 0, 0)); m_inJointName->set(""); }

The operator can be added by this custom behaviour module.

#pragma once #include <Atoms/BehaviourModule.h> class MyJointTransformModule : public Atoms::BehaviourModule { public: MyJointTransformModule(); virtual ~MyJointTransformModule(); void agentsCreated(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup); void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup); static Atoms::BehaviourModule* creator(const std::string& parameter); };

And the module is exposed to Unreal by a custom component

Finally, the operator and the module is registered to Atoms

Copyright © 2017, Toolchefs LTD.