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.
#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); };
#include "MyJointTransformModule.h" #include "MyJointTransformOperator.h" #include <AtomsCore/Metadata/IntMetadata.h> #include <AtomsCore/Metadata/BoolMetadata.h> #include <AtomsCore/Metadata/Vector3Metadata.h> #include <AtomsCore/Metadata/DoubleMetadata.h> #include <AtomsCore/Metadata/StringMetadata.h> #include <AtomsCore/Metadata/MatrixMetadata.h> #include <AtomsCore/Metadata/StringArrayMetadata.h> #include <Atoms/GlobalNames.h> #include <Atoms/Agent.h> #include <AtomsCore/Poser.h> #include <AtomsUtils/TaskScheduler.h> AtomsCore::Euler::Order jtmConvertRotateOrderToEulerRot(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; } MyJointTransformModule::MyJointTransformModule() : Atoms::BehaviourModule() { AtomsCore::StringMetadata jointName; addAttribute("jointName", &jointName, true); AtomsCore::StringMetadata tooltipJointName("The joint name"); addAttributeProperty("jointName", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipJointName); AtomsCore::StringMetadata jointNameConstructor(Atoms::GlobalNameKeys::ATOMS_BEHAVIOUR_HARD_CONSTRUCTOR_JOINT_NAME_KEY); addAttributeProperty("jointName", Atoms::GlobalNameKeys::ATOMS_BEHAVIOUR_HARD_CONSTRUCTOR_KEY, &jointNameConstructor); AtomsCore::IntMetadata order(0); addAttribute("rotationOrder", &order, true); AtomsCore::StringMetadata tooltipOrder("The rotation order"); addAttributeProperty("rotationOrder", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipOrder); AtomsCore::BoolMetadata worldSpace(false); addAttribute("worldSpace", &worldSpace, true); AtomsCore::StringMetadata tooltipWorldSpace("Apply the transformation in world space"); addAttributeProperty("worldSpace", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipWorldSpace); AtomsCore::BoolMetadata add(true); addAttribute("offset", &add, true); AtomsCore::StringMetadata tooltipOffset("Apply the transformation as an offset to the current one"); addAttributeProperty("offset", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipOffset); AtomsCore::Vector3Metadata rotate(AtomsCore::Vector3(0.0, 0.0, 0.0)); addAttribute("rotate", &rotate, true); AtomsCore::StringMetadata tooltipRotate("Rotation value in degrees"); addAttributeProperty("rotate", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipRotate); AtomsCore::Vector3Metadata scale(AtomsCore::Vector3(1.0, 1.0, 1.0)); addAttribute("scale", &scale, true); AtomsCore::StringMetadata tooltipScale("Scale value"); addAttributeProperty("scale", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipScale); AtomsCore::Vector3Metadata translate(AtomsCore::Vector3(0.0, 0.0, 0.0)); addAttribute("translate", &translate, true); AtomsCore::StringMetadata tooltipTranslate("Translation value"); addAttributeProperty("translate", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipTranslate); AtomsCore::DoubleMetadata weight(1.0); addAttribute("weight", &weight, true); AtomsCore::StringMetadata tooltipWeight("The weight"); addAttributeProperty("weight", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipWeight); AtomsCore::DoubleMetadata minWeight(0.0); addAttributeProperty("weight", ATOMS_BEHAVIOUR_MIN_VALUE, &minWeight); AtomsCore::DoubleMetadata maxWeight(1.0); addAttributeProperty("weight", ATOMS_BEHAVIOUR_MAX_VALUE, &maxWeight); AtomsCore::BoolMetadata useOperator(false); addAttribute("useOperator", &useOperator, true); AtomsCore::StringMetadata tooltipUseOperator( "If true, the transformation will be applied by an operator instead of the module itself at the endFrame stage.\n" "This option should be enabled when a joint transform module is used on an agent group using other modules\n" "actively modifying the pose with an operator and not at the endFrame stage(i.e.Sync)"); addAttributeProperty("useOperator", ATOMS_BEHAVIOUR_TOOLTIP, &tooltipUseOperator); // Display order std::vector<std::string> displayOrderVector{ "jointName", "offset", "translate", "rotate", "scale", "rotationOrder", "worldSpace", "useOperator", "weight" }; AtomsCore::StringArrayMetadata displayOrder(displayOrderVector); addAttributeProperty(ATOMS_BEHAVIOUR_MODULE_PROPERTIES, ATOMS_BEHAVIOUR_MODULE_DISPLAY_ORDER, &displayOrder); } MyJointTransformModule::~MyJointTransformModule() { } void MyJointTransformModule::agentsCreated(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup) { auto& attribs = attributes(); bool add = attribs.getTypedEntry<AtomsCore::BoolMetadata>("offset")->value(); bool worldSpace = attribs.getTypedEntry<AtomsCore::BoolMetadata>("worldSpace")->value(); const std::string& jointName = attribs.getTypedEntry<AtomsCore::StringMetadata>("jointName")->value(); int rotationOrder = attribs.getTypedEntry<AtomsCore::IntMetadata>("rotationOrder")->value(); AtomsPtr<AtomsCore::MapMetadata> jointNameOverrideMap = attribs.getTypedEntry<AtomsCore::MapMetadata>("jointName_override"); AtomsPtr<AtomsCore::MapMetadata> rotationOrderOverrideMap = attribs.getTypedEntry<AtomsCore::MapMetadata>("rotationOrder_override"); AtomsPtr<AtomsCore::MapMetadata> addOverrideMap = attribs.getTypedEntry<AtomsCore::MapMetadata>("offset_override"); AtomsPtr<AtomsCore::MapMetadata> worldSpaceOverrideMap = attribs.getTypedEntry<AtomsCore::MapMetadata>("worldSpace_override"); const std::string jointNameOverrideName = name() + "@jointName"; const std::string rotationOrderOverrideName = name() + "@rotationOrder"; const std::string offsetOverrideName = name() + "@offset"; const std::string worldSpaceOverrideName = name() + "@worldSpace"; std::string operatorName = name() + "Op"; AtomsUtils::parallel_for(AtomsUtils::ParallelForRange(0, agents.size()), [&](const AtomsUtils::ParallelForRange& r TASK_PARTITION_EXTRA_ARGS) { for (unsigned int i = r.begin(); i < r.end(); i++) { Atoms::Agent* agent = agents[i]; std::string jointNameTmp = jointName; int rotationOrderTmp = rotationOrder; bool worldSpaceTmp = worldSpace; bool addTmp = add; auto& groupIdStr = agent->groupIdStr()->get(); jointNameTmp = getAttributePerAgent(jointName, jointNameOverrideMap.get(), groupIdStr, agent->metadata(), jointNameOverrideName); rotationOrderTmp = getAttributePerAgent(rotationOrder, rotationOrderOverrideMap.get(), groupIdStr, agent->metadata(), rotationOrderOverrideName); addTmp = getAttributePerAgent(add, addOverrideMap.get(), groupIdStr, agent->metadata(), offsetOverrideName); worldSpaceTmp = getAttributePerAgent(worldSpace, worldSpaceOverrideMap.get(), groupIdStr, agent->metadata(), worldSpaceOverrideName); if (jointNameTmp.empty()) continue; Atoms::AgentBehaviourNetwork& network = agent->network(); Atoms::Operator* endOperator = network.buildPoseNode(); MyJointTransformOperator* jointTransform = static_cast<MyJointTransformOperator*>(network.manager().createNode(MyJointTransformOperator::staticTypeStr(), "jointTransform")); jointTransform->setAgent(agent); network.manager().connectAttr(endOperator->name(), "outPose", jointTransform->name(), "inPose"); AtomsCore::StringMetadata bm(jointTransform->name()); agent->metadata().addEntry(operatorName, &bm); jointTransform->getInputPort<AtomsGraph::StringPort>("jointName")->set(jointNameTmp); jointTransform->getInputPort<AtomsGraph::LongPort>("rotationOrder")->set(rotationOrderTmp); jointTransform->getInputPort<AtomsGraph::BooleanPort>("offset")->set(addTmp); jointTransform->getInputPort<AtomsGraph::BooleanPort>("worldSpace")->set(worldSpaceTmp); network.setBuildPoseNode(jointTransform); } }); } void MyJointTransformModule::initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup) { auto& attribs = attributes(); double weight = attribs.getTypedEntry<AtomsCore::DoubleMetadata>("weight")->value(); AtomsCore::Vector3 rotate = attribs.getTypedEntry<AtomsCore::Vector3Metadata>("rotate")->value(); AtomsCore::Vector3 scale = attribs.getTypedEntry<AtomsCore::Vector3Metadata>("scale")->value(); AtomsCore::Vector3 translate = attribs.getTypedEntry<AtomsCore::Vector3Metadata>("translate")->value(); AtomsPtr<AtomsCore::MapMetadata> rotateOverrideMap = attribs.getTypedEntry<AtomsCore::MapMetadata>("rotate_override"); AtomsPtr<AtomsCore::MapMetadata> scaleOverrideMap = attribs.getTypedEntry<AtomsCore::MapMetadata>("scale_override"); AtomsPtr<AtomsCore::MapMetadata> translateOverrideMap = attribs.getTypedEntry<AtomsCore::MapMetadata>("translate_override"); AtomsPtr<AtomsCore::MapMetadata> weightOverrideMap = attribs.getTypedEntry<AtomsCore::MapMetadata>("weight_override"); const std::string rotateOverrideName = name() + "@rotate"; const std::string scaleOverrideName = name() + "@scale"; const std::string translateOverrideName = name() + "@translate"; const std::string weightOverrideName = name() + "@weight"; const std::string operatorName = name() + "Op"; AtomsUtils::parallel_for(AtomsUtils::ParallelForRange(0, agents.size()), [&](const AtomsUtils::ParallelForRange& r TASK_PARTITION_EXTRA_ARGS) { for (unsigned int i = r.begin(); i < r.end(); i++) { Atoms::Agent* agent = agents[i]; if (!agent) continue; AtomsPtr<AtomsCore::StringMetadata> jointTransformOperatorNamePtr = agent->metadata().getTypedEntry<AtomsCore::StringMetadata>(operatorName); if (!jointTransformOperatorNamePtr) { continue; } AtomsGraph::Node *node = agent->network().manager().getNode(jointTransformOperatorNamePtr->get()); if (node == nullptr) { AtomsUtils::Logger::warning() << "Could not find joint transform operator"; continue; } auto agent_type = agent->agentType(); if (!agent_type) continue; auto& skeleton = agent_type->skeleton(); double weightTmp = getAttributePerAgent(weight, weightOverrideMap.get(), agent->groupIdStr()->get(), agent->metadata(), weightOverrideName); AtomsCore::Vector3 rotateTmp = getAttributePerAgent(rotate, rotateOverrideMap.get(), agent->groupIdStr()->get(), agent->metadata(), rotateOverrideName); AtomsCore::Vector3 scaleTmp = getAttributePerAgent(scale, scaleOverrideMap.get(), agent->groupIdStr()->get(), agent->metadata(), scaleOverrideName); AtomsCore::Vector3 translateTmp = getAttributePerAgent(translate, translateOverrideMap.get(), agent->groupIdStr()->get(), agent->metadata(), translateOverrideName); node->getInputPort<AtomsGraph::VectorPort>("translate")->set(translateTmp); node->getInputPort<AtomsGraph::VectorPort>("rotate")->set(rotateTmp); node->getInputPort<AtomsGraph::VectorPort>("scale")->set(scaleTmp); node->getInputPort<AtomsGraph::DoublePort>("weight")->set(weightTmp); } } ); } Atoms::BehaviourModule* MyJointTransformModule::creator(const std::string& parameter) { return new MyJointTransformModule(); }
And the module is exposed to Unreal by a custom component
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Components/AtomsBehaviourComponent.h" #include "AtomsUnrealUtils.h" #include "MyJointTransformComponent.generated.h" /** * */ UCLASS(ClassGroup = (AtomsBehaviours), meta = (BlueprintSpawnableComponent)) class UMyJointTransformComponent : public UAtomsBehaviourComponent { GENERATED_BODY() public: /** The joint name */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") FName jointName; /** The joint name */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") TMap<int32, FName> jointNameOverride; /** Apply the transformation as an offset to the current one */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") bool offset; /** Apply the transformation as an offset to the current one */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") TMap<int32, bool> offsetOverride; /** Translation value */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") FVector translate; /** Translation value */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") TMap<int32, FVector> translateOverride; /** Rotation value in degrees */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") FVector rotate; /** Rotation value in degrees */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") TMap<int32, FVector> rotateOverride; /** Scale value */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") FVector scale; /** Scale value */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") TMap<int32, FVector> scaleOverride; /** The rotation order */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") int32 rotationOrder; /** The rotation order */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") TMap<int32, int32> rotationOrderOverride; /** Apply the transformation in world space */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") bool worldSpace; /** Apply the transformation in world space */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") TMap<int32, bool> worldSpaceOverride; /** The weight */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour", meta = (UIMin = "0.00", UIMax = "1.00")) float weight; /** The weight */ UPROPERTY(BlueprintReadWrite, EditAnywhere, Interp, Category = "Behaviour") TMap<int32, float> weightOverride; UMyJointTransformComponent(); ~UMyJointTransformComponent(); virtual void GetAtomsAttributes(AtomsPtr<AtomsCore::MapMetadata>& attributes) override; /** The joint name */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetJointName(const FName& Value); /** The joint name */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetJointNameOverride(const FName& Value, const int32 AgentId); /** Remove agent override */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void RemoveJointNameOverride(const int32 AgentId); /** Apply the transformation as an offset to the current one */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetOffset(const bool Value); /** Apply the transformation as an offset to the current one */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetOffsetOverride(const bool Value, const int32 AgentId); /** Remove agent override */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void RemoveOffsetOverride(const int32 AgentId); /** Translation value */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetTranslate(const FVector& Value); /** Translation value */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetTranslateOverride(const FVector& Value, const int32 AgentId); /** Remove agent override */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void RemoveTranslateOverride(const int32 AgentId); /** Rotation value in degrees */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetRotate(const FVector& Value); /** Rotation value in degrees */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetRotateOverride(const FVector& Value, const int32 AgentId); /** Remove agent override */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void RemoveRotateOverride(const int32 AgentId); /** Scale value */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetScale(const FVector& Value); /** Scale value */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetScaleOverride(const FVector& Value, const int32 AgentId); /** Remove agent override */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void RemoveScaleOverride(const int32 AgentId); /** The rotation order */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetRotationOrder(const int32 Value); /** The rotation order */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetRotationOrderOverride(const int32 Value, const int32 AgentId); /** Remove agent override */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void RemoveRotationOrderOverride(const int32 AgentId); /** Apply the transformation in world space */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetWorldSpace(const bool Value); /** Apply the transformation in world space */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetWorldSpaceOverride(const bool Value, const int32 AgentId); /** Remove agent override */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void RemoveWorldSpaceOverride(const int32 AgentId); /** The weight */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetWeight(const float Value); /** The weight */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void SetWeightOverride(const float Value, const int32 AgentId); /** Remove agent override */ UFUNCTION(BlueprintCallable, Category = "JointTransformBehaviourComponent", meta = (CallInEditor = "true")) void RemoveWeightOverride(const int32 AgentId); #if WITH_EDITOR virtual void PreEditChange(UnrealProperty * PropertyAboutToChange) override; virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; #endif };
#include "MyJointTransformComponent.h" #include "Actors/AtomsAgentGroup.h" #include "AtomsModuleGeneratorUtils.h" #include "AtomsAttributeUtils.h" UMyJointTransformComponent::UMyJointTransformComponent() : UAtomsBehaviourComponent() { AtomsBehaviourModule = "MyJointTransform"; jointName = ""; offset = true; translate = FVector(0.0, 0.0, 0.0); rotate = FVector(0.0, 0.0, 0.0); scale = FVector(1.0, 1.0, 1.0); rotationOrder = 0; worldSpace = false; weight = 1.0; } UMyJointTransformComponent::~UMyJointTransformComponent() { } void UMyJointTransformComponent::GetAtomsAttributes(AtomsPtr<AtomsCore::MapMetadata>& attributes) { ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(jointName, AtomsCore::StringMetadata, UAtomsAttributeUtils::ToString); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META_OVERRIDE(jointName_override, jointNameOverride, AtomsCore::StringMetadata, UAtomsAttributeUtils::ToString); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(offset, AtomsCore::BoolMetadata, ); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META_OVERRIDE(offset_override, offsetOverride, AtomsCore::BoolMetadata, ); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(translate, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META_OVERRIDE(translate_override, translateOverride, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(rotate, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META_OVERRIDE(rotate_override, rotateOverride, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(scale, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META_OVERRIDE(scale_override, scaleOverride, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(rotationOrder, AtomsCore::IntMetadata, ); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META_OVERRIDE(rotationOrder_override, rotationOrderOverride, AtomsCore::IntMetadata, ); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(worldSpace, AtomsCore::BoolMetadata, ); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META_OVERRIDE(worldSpace_override, worldSpaceOverride, AtomsCore::BoolMetadata, ); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(weight, AtomsCore::DoubleMetadata, ); ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META_OVERRIDE(weight_override, weightOverride, AtomsCore::DoubleMetadata, ); } #if WITH_EDITOR void UMyJointTransformComponent::PreEditChange(UnrealProperty * PropertyAboutToChange) { if (!PropertyAboutToChange) return; Super::PreEditChange(PropertyAboutToChange); auto PropertyName = (PropertyAboutToChange != NULL) ? PropertyAboutToChange->GetFName() : NAME_None; } void UMyJointTransformComponent::PostEditChangeProperty(FPropertyChangedEvent & e) { auto PropertyName = (e.Property != NULL) ? e.Property->GetFName() : NAME_None; Super::PostEditChangeProperty(e); } #endif void UMyJointTransformComponent::SetJointName(const FName& Value) { jointName = Value; ATOMS_BEHAVIOUR_COMPONENT_GET_ATTRIBUTES() ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(jointName, AtomsCore::StringMetadata, UAtomsAttributeUtils::ToString); } void UMyJointTransformComponent::SetJointNameOverride(const FName& Value, const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_SET_META_OVERRIDE(jointName_override, jointNameOverride, AtomsCore::StringMetadata, UAtomsAttributeUtils::ToString, AgentId); } void UMyJointTransformComponent::RemoveJointNameOverride(const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_REMOVE_OVERRIDE(jointName_override, jointNameOverride, AgentId); } void UMyJointTransformComponent::SetOffset(const bool Value) { offset = Value; ATOMS_BEHAVIOUR_COMPONENT_GET_ATTRIBUTES() ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(offset, AtomsCore::BoolMetadata, ); } void UMyJointTransformComponent::SetOffsetOverride(const bool Value, const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_SET_META_OVERRIDE(offset_override, offsetOverride, AtomsCore::BoolMetadata, , AgentId); } void UMyJointTransformComponent::RemoveOffsetOverride(const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_REMOVE_OVERRIDE(offset_override, offsetOverride, AgentId); } void UMyJointTransformComponent::SetTranslate(const FVector& Value) { translate = Value; ATOMS_BEHAVIOUR_COMPONENT_GET_ATTRIBUTES() ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(translate, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); } void UMyJointTransformComponent::SetTranslateOverride(const FVector& Value, const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_SET_META_OVERRIDE(translate_override, translateOverride, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3, AgentId); } void UMyJointTransformComponent::RemoveTranslateOverride(const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_REMOVE_OVERRIDE(translate_override, translateOverride, AgentId); } void UMyJointTransformComponent::SetRotate(const FVector& Value) { rotate = Value; ATOMS_BEHAVIOUR_COMPONENT_GET_ATTRIBUTES() ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(rotate, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); } void UMyJointTransformComponent::SetRotateOverride(const FVector& Value, const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_SET_META_OVERRIDE(rotate_override, rotateOverride, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3, AgentId); } void UMyJointTransformComponent::RemoveRotateOverride(const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_REMOVE_OVERRIDE(rotate_override, rotateOverride, AgentId); } void UMyJointTransformComponent::SetScale(const FVector& Value) { scale = Value; ATOMS_BEHAVIOUR_COMPONENT_GET_ATTRIBUTES() ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(scale, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3); } void UMyJointTransformComponent::SetScaleOverride(const FVector& Value, const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_SET_META_OVERRIDE(scale_override, scaleOverride, AtomsCore::Vector3Metadata, AtomsConverter::ToVector3, AgentId); } void UMyJointTransformComponent::RemoveScaleOverride(const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_REMOVE_OVERRIDE(scale_override, scaleOverride, AgentId); } void UMyJointTransformComponent::SetRotationOrder(const int32 Value) { rotationOrder = Value; ATOMS_BEHAVIOUR_COMPONENT_GET_ATTRIBUTES() ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(rotationOrder, AtomsCore::IntMetadata, ); } void UMyJointTransformComponent::SetRotationOrderOverride(const int32 Value, const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_SET_META_OVERRIDE(rotationOrder_override, rotationOrderOverride, AtomsCore::IntMetadata, , AgentId); } void UMyJointTransformComponent::RemoveRotationOrderOverride(const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_REMOVE_OVERRIDE(rotationOrder_override, rotationOrderOverride, AgentId); } void UMyJointTransformComponent::SetWorldSpace(const bool Value) { worldSpace = Value; ATOMS_BEHAVIOUR_COMPONENT_GET_ATTRIBUTES() ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(worldSpace, AtomsCore::BoolMetadata, ); } void UMyJointTransformComponent::SetWorldSpaceOverride(const bool Value, const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_SET_META_OVERRIDE(worldSpace_override, worldSpaceOverride, AtomsCore::BoolMetadata, , AgentId); } void UMyJointTransformComponent::RemoveWorldSpaceOverride(const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_REMOVE_OVERRIDE(worldSpace_override, worldSpaceOverride, AgentId); } void UMyJointTransformComponent::SetWeight(const float Value) { weight = Value; ATOMS_BEHAVIOUR_COMPONENT_GET_ATTRIBUTES() ATOMS_BEHAVIOUR_COMPONENT_CONVERT_META(weight, AtomsCore::DoubleMetadata, ); } void UMyJointTransformComponent::SetWeightOverride(const float Value, const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_SET_META_OVERRIDE(weight_override, weightOverride, AtomsCore::DoubleMetadata, , AgentId); } void UMyJointTransformComponent::RemoveWeightOverride(const int32 AgentId) { ATOMS_BEHAVIOUR_COMPONENT_REMOVE_OVERRIDE(weight_override, weightOverride, AgentId); }
Finally, the operator and the module is registered to Atoms
#include "MyJointTransformModule.h" #include "MyJointTransformOperator.h" #include <AtomsGraph/NodeFactory.h> #include <Atoms/BehaviourModules.h> void FAtomsUnrealTestGameModule::StartupModule() { AtomsGraph::NodeFactory& manager = AtomsGraph::NodeFactory::instance(); manager.registerNode(MyJointTransformOperator::staticTypeStr(), &MyJointTransformOperator::creator); Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance(); moduleManager.registerBehaviourModule("MyJointTransform", &MyJointTransformModule::creator, Atoms::BehaviourModules::kNative); } void FAtomsUnrealTestGameModule::ShutdownModule() { // Deregister the node from the node factory AtomsGraph::NodeFactory& manager = AtomsGraph::NodeFactory::instance(); manager.deregisterNode(MyJointTransformOperator::staticTypeStr()); Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance(); moduleManager.deregisterBehaviourModule("MyJointTransform"); }