Atoms Crowd  7.0.0
Writing a behaviour module

Create a new class and inherit from the base Atoms::BehaviourModule class. Define a a constructor, a destructor and a static creator function (needed by Atoms to create an instance 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();
}
Behaviour module.
Definition: BehaviourModule.h:32

For this module we'll need only a target position attribute. The attributes of the module are stored inside a MapMetadata object, you can add new attributes using the addAttribute function. Attributes should always be added inside the constructor.

The third attribute of the addAttribute method defines if the attribute is overridable per agent or not. In this example we'll make it overridable.

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);
}
AtomsMath::Vector3 Vector3
Vector3 class.
Definition: AtomsMath.h:57
Atoms namespace.
Definition: Agent.h:29

Now we need to compute the new agent direction at each frame before the agent pose is computed. This will be done inside the initFrame method.

For this example we won't need to override any other method.

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

In the initFrame function get the targetPosition value, iterate over each agent to read the position and set their 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());
}
}
Agent.
Definition: Agent.h:44
MapMetadata class.
Definition: MapMetadata.h:24
AtomsPtr< T > getTypedEntry(const Key &key)
Get a typed entry.
Definition: MapMetadata.impl.h:4

The target position attribute is overridable per agent, but the code to handle it isn't there yet. When you override an attribute, a new map metadata named "AttributeName_override" is created. The keys of this map metadata are the agent group ids, but they will be present only when the user specifies an override from inside his 3D application.

In order to know if an agent has an override, please check the metadata map. If it's not there, please 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 improve 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()
{
moduleManager.registerBehaviourModule("SimpleFollowTarget", &FollowTargetModule::creator, Atoms::BehaviourModules::kNative);
return true;
}
ATOMSPLUGIN_EXPORT bool unitializePlugin()
{
moduleManager.deregisterBehaviourModule("SimpleFollowTarget");
return true;
}
}
Behaviour module factory.
Definition: BehaviourModules.h:22
bool deregisterBehaviourModule(const std::string &name)
Deregisters a Behaviour module type from the factory.
static BehaviourModules & instance()
Singleton access.
bool registerBehaviourModule(const std::string &moduleName, creatorFn func, unsigned int moduleType=kNative, bool replace=false, const std::string category="")
Registers a behaviour module in the factory.

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

#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::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()
{
moduleManager.registerBehaviourModule("SimpleFollowTarget", &FollowTargetModule::creator, Atoms::BehaviourModules::kNative);
return true;
}
ATOMSPLUGIN_EXPORT bool initializeScenePlugin()
{
moduleManager.registerBehaviourModule("SimpleFollowTarget", &FollowTargetModule::creator, Atoms::BehaviourModules::kNative);
return true;
}
ATOMSPLUGIN_EXPORT bool unitializeScenePlugin()
{
moduleManager.deregisterBehaviourModule("SimpleFollowTarget");
return true;
}
ATOMSPLUGIN_EXPORT bool unitializePlugin()
{
moduleManager.deregisterBehaviourModule("SimpleFollowTarget");
return true;
}
}
virtual AtomsCore::MapMetadata & attributes()
Returns the module attributes map.
Definition: BehaviourModule.h:124
virtual void initFrame(const std::vector< Agent * > &agents, AgentGroup *agentGroup=nullptr)
Init frame.
virtual void addAttribute(const std::string &attributeName, AtomsPtr< AtomsCore::Metadata > &metadata, bool perAgent=false)
Add an attribute to the module, if the perAgent flag is set to true an extra attribute is added with ...