Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Current »

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");
}
  • No labels