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 9 Next »

In this section we are going to create a new behaviour module. This behaviour module is a simple follow target module.


Writing the behaviour module

Create a new class and hinerit from the base Atoms::BehaviourModule class. With a constructor a desctructor and a static creator function used by Atoms to create an isntance of this module.

class FollowTargetModule: public Atoms::BehaviourModule
{
	public:

		FollowTargetModule()
		{}

		virtual ~FollowTargetModule()
		{}

		static Atoms::BehaviourModule* creator(const std::string& parameter);
};

Atoms::BehaviourModule* FollowTargetModule::creator(const std::string& parameter)
{
	return new FollowTargetModule();
}

Add some attributes to our node. In this case we need only a target position. The attributes of the module are stored inside a MapMetadata object, you can add new attributes using the addAttribute function. Add this attribute inside the constructor.

FollowTargetModule(): Atoms::BehaviourModule()
{
	AtomsCore::Vector3Metadata targetPosition(AtomsCore::Vector3(0.0,0.0,0.0));
	// The last argumet specify if this attribute can be override per agent
	addAttribute("targetPosition", &targerPosition, true);
}

Now we need to compute the new agent direction at each frame before the agent pose is computed, so we need to override the initFrame function.

You can override other function linke the endFrame or initSimulation or agentsCreated but in this case we need only the initFrame.


void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr)
{
}

In the initFrame function get the targetPosition value and iterate over each agent to read the position and set the new direction.

void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr)
{
	// Get the target position from the moduel attribute
	AtomsCore::MapMetadata& attributeMap = attributes();
	AtomsCore::Vector3& targetPosition = attributeMap.getTypedEntry<AtomsCore::Vector3Metadata>("targetPosition")->get();

	// iterate over each agent
	for(Atoms::Agent* agent: agents)
	{
		if (!agent)
			continue;
		//get the agent position
		AtomsPtr<AtomsCore::Vector3Metadata> positionPtr = agent->metadata().getTypedEntry<AtomsCore::Vector3Metadata>("position");
		AtomsPtr<AtomsCore::Vector3Metadata> directionPtr = agent->metadata().getTypedEntry<AtomsCore::Vector3Metadata>("direction");
		if(!positionPtr || !directionPtr )
			continue;

		// compute the new direction
		directionPtr->set((targetPosition - positionPtr->get()).normalized()); 
	}
}
The target position attribute is overraidable per agent but the code to handle it isn't there yet. When you override an attribute, Atoms creates an entry using the agent group id inside the mapmetadata saved as AttributeName_override, check this map and get the value from it otherwise use the global attribute.


void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr)
{
	// Get the target position from the moduel attribute
	AtomsCore::MapMetadata& attributeMap = attributes();
	AtomsCore::Vector3& targetPosition = attributeMap.getTypedEntry<AtomsCore::Vector3Metadata>("targetPosition")->get();

	//get the override map metadata
	AtomsPtr<AtomsCore::MapMetadata> targetPositionOverride = attributeMap.getTypedEntry<AtomsCore::MapMetadata>("targetPosition_override");

	// used to store the groupId as string
	char groupIdChar[12];
	std::string groupIdStr;
	groupIdStr.reserve(12);

	// iterate over each agent
	for(Atoms::Agent* agent: agents)
	{
		if (!agent)
			continue;
		//get the agent position
		AtomsPtr<AtomsCore::Vector3Metadata> positionPtr = agent->metadata().getTypedEntry<AtomsCore::Vector3Metadata>("position");
		AtomsPtr<AtomsCore::Vector3Metadata> directionPtr = agent->metadata().getTypedEntry<AtomsCore::Vector3Metadata>("direction");
		if(!positionPtr || !directionPtr )
			continue;

		// get the agent group id
		AtomsPtr<AtomsCore::IntMetadata> groupIdPtr = agent->metadata().getTypedEntry<AtomsCore::IntMetadata>("groupId");
		if (!groupIdPtr)
			continue;

		//convert the groupId to a string since the map metadata use only strings as key
		sprintf(groupIdChar, "%d", groupIdPtr->value());
		groupIdStr = groupIdChar;

		//check if the agent is inside the override map
		AtomsPtr<AtomsCore::Vector3Metadata> overrideTargetPos = targetPositionOverride->getTypedEntry<AtomsCore::Vector3Metadata>(groupIdStr);
		if (overrideTargetPos)
		{
			// compute the new direction
			directionPtr->set((overrideTargetPos->get() - positionPtr->get()).normalized()); 
		}
		else
		{
			// compute the new direction
			directionPtr->set((targetPosition - positionPtr->get()).normalized()); 
		}
	}
}

If you want imporve the performance of the module you can add tbb to parallelize the main for loop.

void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr)
{
	// Get the target position from the moduel attribute
	AtomsCore::MapMetadata& attributeMap = attributes();
	AtomsCore::Vector3& targetPosition = attributeMap.getTypedEntry<AtomsCore::Vector3Metadata>("targetPosition")->get();

	//get the override map metadata
	AtomsPtr<AtomsCore::MapMetadata> targetPositionOverride = attributeMap.getTypedEntry<AtomsCore::MapMetadata>("targetPosition_override");



	// iterate over each agent
	tbb::parallel_for(tbb::blocked_range<unsigned int>(0, agents.size()),
		[&](const tbb::blocked_range<size_t>& r)
	{
		// used to store the groupId as string
		char groupIdChar[12];
		std::string groupIdStr;
		groupIdStr.reserve(12);

		for (size_t i = r.begin(); i < r.end(); i++)
		{
			Atoms::Agent* agent = agents[i];

			if (!agent)
				continue;
			//get the agent position
			AtomsPtr<AtomsCore::Vector3Metadata> positionPtr = agent->metadata().getTypedEntry<AtomsCore::Vector3Metadata>("position");
			AtomsPtr<AtomsCore::Vector3Metadata> directionPtr = agent->metadata().getTypedEntry<AtomsCore::Vector3Metadata>("direction");
			if(!positionPtr || !directionPtr )
				continue;

			// get the agent group id
			AtomsPtr<AtomsCore::IntMetadata> groupIdPtr = agent->metadata().getTypedEntry<AtomsCore::IntMetadata>("groupId");
			if (!groupIdPtr)
				continue;
	
			//convert the groupId to a string since the map metadata use only strings as key
			sprintf(groupIdChar, "%d", groupIdPtr->value());
			groupIdStr = groupIdChar;
	
			//check if the agent is inside the override map
			AtomsPtr<AtomsCore::Vector3Metadata> overrideTargetPos = targetPositionOverride->getTypedEntry<AtomsCore::Vector3Metadata>(groupIdStr);
			if (overrideTargetPos)
			{
				// compute the new direction
				directionPtr->set((overrideTargetPos->get() - positionPtr->get()).normalized()); 
			}
			else
			{
				// compute the new direction
				directionPtr->set((targetPosition - positionPtr->get()).normalized()); 
			}
		}
	});
}

Register the behaviour module

Now the behaviour module must be added to the behaviour module factory.

extern "C"
{
	ATOMSPLUGIN_EXPORT bool initializePlugin()
	{
		Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance();
moduleManager.registerBehaviourModule("SimpleFollowTarget", &FollowTargetModule::creator, Atoms::BehaviourModules::kNative);
		return true;
	}

	ATOMSPLUGIN_EXPORT bool unitializePlugin()
	{
		Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance();
		moduleManager.deregisterBehaviourModule("SimpleFollowTarget");
		return true;
	}
}

Compile the behaviour module

Windows:

In visual studio create a dll projects, add the atoms, tbb and openexr includes and add BUILD_ATOMSPLUGIN to the preprocessor definitions.

Linux:

Compile as a shared object adding the atoms, tbb and opendexr includes and BUILD_ATOMSPLUGIN to the preprocessor definitions.



Use the behaviour module

Add to the ATOMS_PLUGINS_PATH environment variable, the folder where the plugin is located. Then the module is automatically loaded when you load atoms.


Final code

#pragma once
#include <Atoms/BehaviourModule.h>
#include <Atoms/Agent.h>
#include <Atoms/AgentGroup.h>
#include <AtomsUtils/Logger.h>
#include <Atoms/GlobalNames.h>
#include <AtomsCore/Metadata/IntMetadata.h>
#include <AtomsCore/Metadata/Vector3Metadata.h>
#include <tbb/parallel_for.h>
#include <algorithm>

class FollowTargetModule : public Atoms::BehaviourModule
{
public:

	FollowTargetModule(): Atoms::BehaviourModule()
	{
		AtomsCore::Vector3Metadata targetPosition(AtomsCore::Vector3(0.0,0.0,0.0));
		// The last argumet specify if this attribute can be override per agent
		addAttribute("targetPosition", &targerPosition, true);
	}

	~FollowTargetModule()
	{
	}

	void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr)
	{
		// Get the target position from the moduel attribute
		AtomsCore::MapMetadata& attributeMap = attributes();
		AtomsCore::Vector3& targetPosition = attributeMap.getTypedEntry<AtomsCore::Vector3Metadata>("targetPosition")->get();

		//get the override map metadata
		AtomsPtr<AtomsCore::MapMetadata> targetPositionOverride = attributeMap.getTypedEntry<AtomsCore::MapMetadata>("targetPosition_override");



		// iterate over each agent
		tbb::parallel_for(tbb::blocked_range<unsigned int>(0, agents.size()),
			[&](const tbb::blocked_range<size_t>& r)
		{
			// used to store the groupId as string
			char groupIdChar[12];
			std::string groupIdStr;
			groupIdStr.reserve(12);

			for (size_t i = r.begin(); i < r.end(); i++)
			{
				Atoms::Agent* agent = agents[i];

				if (!agent)
					continue;
				//get the agent position
				AtomsPtr<AtomsCore::Vector3Metadata> positionPtr = agent->metadata().getTypedEntry<AtomsCore::Vector3Metadata>("position");
				AtomsPtr<AtomsCore::Vector3Metadata> directionPtr = agent->metadata().getTypedEntry<AtomsCore::Vector3Metadata>("direction");
				if(!positionPtr || !directionPtr )
					continue;

				// get the agent group id
				AtomsPtr<AtomsCore::IntMetadata> groupIdPtr = agent->metadata().getTypedEntry<AtomsCore::IntMetadata>("groupId");
				if (!groupIdPtr)
					continue;
		
				//convert the groupId to a string since the map metadata use only strings as key
				sprintf(groupIdChar, "%d", groupIdPtr->value());
				groupIdStr = groupIdChar;
		
				//check if the agent is inside the override map
				AtomsPtr<AtomsCore::Vector3Metadata> overrideTargetPos = targetPositionOverride->getTypedEntry<AtomsCore::Vector3Metadata>(groupIdStr);
				if (overrideTargetPos)
				{
					// compute the new direction
					directionPtr->set((overrideTargetPos->get() - positionPtr->get()).normalized()); 
				}
				else
				{
					// compute the new direction
					directionPtr->set((targetPosition - positionPtr->get()).normalized()); 
				}
			}
		});
	}

	static Atoms::BehaviourModule* creator(const std::string& parameter);
};

Atoms::BehaviourModule* FollowTargetModule::creator(const std::string& parameter)
{
	return new FollowTargetModule();
}

extern "C"
{
	ATOMSPLUGIN_EXPORT bool initializePlugin()
	{
		Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance();
moduleManager.registerBehaviourModule("SimpleFollowTarget", &FollowTargetModule::creator, Atoms::BehaviourModules::kNative);
		return true;
	}

	ATOMSPLUGIN_EXPORT bool unitializePlugin()
	{
		Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance();
		moduleManager.deregisterBehaviourModule("SimpleFollowTarget");
		return true;
	}
}
  • No labels