Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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.

...

Code Block
languagecpp
#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;
};

Code Block
languagecpp
#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("");
}

...