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 example operator adds an offset to a joint transform.
#include <AtomsGraph/Ports.h> #include <Atoms/Globals.h> #include <Atoms/Graph/Operator.h> class JointOffsetOperator : public Atoms::Operator { public: NODE_STANDARD_MEMBERS JointOffsetOperator(); virtual ~JointOffsetOperator(); virtual bool compute(const AtomsGraph::ComputeData* computeData) override; virtual void reset() override; private: AtomsGraph::PosePort* m_inPose; AtomsGraph::StringPort *m_jointName; AtomsGraph::MatrixPort *m_offsetMatrix; AtomsGraph::BooleanPort *m_worldSpace; int m_jointId; bool m_first; }; // this must be unique #define JOINT_OFFSET_OPERATOR_ID 9999991 NODE_STANDARD_MEMBERS_IMPL(JointOffsetOperator) unsigned int JointOffsetOperator::staticTypeId() { return JOINT_OFFSET_OPERATOR_ID; } std::string JointOffsetOperator::staticTypeStr() { return std::string("JointOffsetOperator"); } JointOffsetOperator::JointOffsetOperator() : Operator() { m_jointName= new AtomsGraph::StringPort("jointName"); m_jointName->set(""); addInputPort(m_jointName); m_offsetMatrix = new AtomsGraph::MatrixPort("offsetMatrix"); addInputPort(m_offsetMatrix); m_inPose = new AtomsGraph::PosePort("inPose"); addInputPort(m_inPose); m_worldSpace = new AtomsGraph::BooleanPort("worldSpace"); m_worldSpace->set(false); addInputPort(m_worldSpace ); m_jointId= -1; m_first = true; } JointConstraintOperator::~JointConstraintOperator() { } bool JointConstraintOperator::compute(const AtomsGraph::ComputeData* computeData) { AtomsCore::Pose &inPose = m_inPose->getRef(); if (inPose.numJoints() == 0) { AtomsUtils::Logger::warning() << "Empty input pose"; return false; } AtomsCore::Pose &outPose = m_outPose->getRef(); // Copy the input pose to the out pose port outPose = inPose; // Check if the agent and the agent type are valid if (!m_agent) { AtomsUtils::Logger::error() << "Invalid agent type"; return false; } if(!m_agent->agentType()) { AtomsUtils::Logger::error() << "Invalid agent type"; return false; } // Get the skeleton from the agent type const AtomsCore::Skeleton& skeleton = m_agent->agentType()->skeleton(); if (m_first) { m_jointId = skeleton.jointId(m_targetJoint->getRef()); } if (m_jointId == -1) { Logger::error() << "Could not find " << m_targetJoint->getRef() << "joint."; return false; } if (worldSpace->get()) { // Compute the offset in world space AtomsCore::Poser poser(&skeleton); AtomsCore::Matrix currentMtx = poser.getWorldMatrix(pose, jointId); poser.setWorldMatrix(pose, m_offsetMatrix->getRef()* currentMtx, jointIdTmp); } else { // Compute the offset in local space AtomsCore::JointPose& jp = outPose.jointPose(m_jointId); jp.setMatrix(m_offsetMatrix->getRef() * jp.matrix()); } return true; } void JointConstraintOperator::reset() { Operator::reset(); m_jointName->set(""); m_worldSpace->set(false); m_jointId= -1; m_first = true; }
The operator can be added by this custom behaviour module.
#include <Atoms/BehaviourModule.h> class JointOffsetModule : public Atoms::BehaviourModule { public: JointOffsetModule (); virtual ~JointOffsetModule (); void agentsCreated(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr); static Atoms::BehaviourModule* creator(const std::string& parameter); }; Atoms::BehaviourModule* JointOffsetModule::creator(const std::string& parameter) { return new JointOffsetModule(); } JointOffsetModule::JointOffsetModule() : Atoms::BehaviourModule() { AtomsCore::StringMetadata jointName(""); addAttribute("jointName", &jointName, false); AtomsCore::MatrixMetadata offsetMatrix; addAttribute("offsetMatrix", &offsetMatrix, true); AtomsCore::BoolMetadata worldSpace(false); addAttribute("worldSpace", &worldSpace); } JointOffsetModule::~JointOffsetModule() { } void JointOffsetModule::agentsCreated(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup) { AtomsCore::MapMetadata& metadata = attributes(); const std::string& jointName= metadata.getTypedEntry<AtomsCore::StringMetadata>("jointName")->get(); bool worldSpace = metadata.getTypedEntry<AtomsCore::BoolMetadata>("worldSpace")->get(); AtomsCore::Matrix& offsetMatrix = metadata.getTypedEntry<AtomsCore::MatrixMetadata>("offsetMatrix")->get(); AtomsPtr<AtomsCore::MapMetadata> offsetMatrixOverrideMap = metadata.getTypedEntry<AtomsCore::MapMetadata>("offsetMatrix_override"); tbb::parallel_for(tbb::blocked_range<unsigned int>(0, agents.size()), [&](const tbb::blocked_range<unsigned int>& r) { char groupIdChar[12]; std::string groupIdStr; groupIdStr.reserve(12); for (unsigned int i = r.begin(); i < r.end(); i++) { Atoms::Agent* agent = agents[i]; // Get the agent dg network Atoms::AgentBehaviourNetwork& network = agent->network(); // Get the last operator fron the dg Atoms::Operator* endOperator = network.buildPoseNode(); // Create the joint offset operator Atoms::JointOffsetOperator* jointOffset = static_cast<Atoms::JointOffsetOperator*>(network.manager().createNode(Atoms::JointOffsetOperator::staticTypeStr(), "jointOffset")); jointOffset->setAgent(agent); // Connect the last operator to the joint offset operator network.manager().connectAttr(endOperator->name(), "outPose", jointOffset ->name(), "inPose"); // Set the operator parameters jointOffset->getInputPort<AtomsGraph::StringPort>("jointName")->set(jointName); jointOffset->getInputPort<AtomsGraph::BooleanPort>("worldSpace")->set(worldSpace ); AtomsCore::Matrix agentOffsetMatrix = offsetMatrix; // Check if the offset matrix is overridden AtomsPtr<AtomsCore::IntMetadata> groupIdPtr = agent->metadata().getTypedEntry<AtomsCore::IntMetadata>(ATOMS_AGENT_GROUPID); if (groupIdPtr) { sprintf(groupIdChar, "%d", groupIdPtr->value()); groupIdStr = groupIdChar; if (offsetMatrixOverrideMap) { AtomsPtr<AtomsCore::MatrixMetadata> offsetMatrixAgentMeta = offsetMatrixOverrideMap->getTypedEntry<AtomsCore::MatrixMetadata>(groupIdStr); if (offsetMatrixAgentMeta) offsetMatrix = offsetMatrixAgentMeta->get(); } } jointOffset->getInputPort<AtomsGraph::MatrixPort>("offsetMatrix")->set(offsetMatrix); // Set the joint offset operator as the last operator to compute network.setBuildPoseNode(jointOffset); } }); } extern "C" { ATOMSPLUGIN_EXPORT bool initializePlugin() { AtomsUtils::Logger::info() << "Loading Joint offset plugin"; // Register the node to the factory AtomsGraph::NodeFactory& manager = AtomsGraph::NodeFactory::instance(); manager.registerNode(JointConstraintOperator::staticTypeStr(), &JointConstraintOperator::creator); Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance(); moduleManager.registerBehaviourModule("SimpleJointOffset", &JointOffsetModule::creator, Atoms::JointOffsetModule::kNative); return true; } ATOMSPLUGIN_EXPORT bool unitializePlugin() { AtomsUtils::Logger::info() << "Unloading Joint offset plugin"; // Deregister the node from the node factory AtomsGraph::NodeFactory& manager = AtomsGraph::NodeFactory::instance(); manager.deregisterNode(JointConstraintOperator::staticTypeStr()); Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance(); moduleManager.deregisterBehaviourModule("SimpleJointOffset"); return true; } }