/
Writing an Atoms operator (AtomsUnreal)

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

Related content

Writing an Atoms operator
Writing an Atoms operator
More like this
Access agent data (AtomsUnreal)
Access agent data (AtomsUnreal)
More like this
Writing an Atoms graph node (AtomsUnreal)
Writing an Atoms graph node (AtomsUnreal)
More like this
Writing an Atoms graph node
Writing an Atoms graph node
More like this
Writing a Metadata Replicator module
Writing a Metadata Replicator module
More like this
State machine debugger data
State machine debugger data
More like this

Copyright © 2017, Toolchefs LTD.