Behaviourモジュールを書く

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

新しいクラスを作成して、ベースから継承します。

Atoms :: Behaviourモジュールクラス。コンストラクタ、デストラクタ、およびスタティッククリエータ関数(こちらのモジュールのインスタンスを作成するために、Atomsが必要とする)を定義します。

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();
}

このモジュールでは、Target Positionアトリビュートのみが必要になります。モジュールのアトリビュートは、MapMetadataオブジェクト、FollowTargetModule()内に格納されます。addAttribute関数を使用して新しいアトリビュートを追加できます。

アトリビュートは、常に、コンストラクタ内に追加する必要があります。
addAttributeメソッドの3番目のアトリビュートは、そのアトリビュートがエージェント毎に、オーバーライドが可能かどうかを定義します。こちらの例では、オーバーライドを可能にします。

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);
}

今度は、エージェントのポーズが計算される前に、各フレームで新しいエージェントの方向を計算する必要があります。これは、initFrameメソッド内で行われます。
こちらの例では、他のメソッドをオーバーライドする必要はありません。


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

initFrame関数より、targetPosition値を取得し、各エージェントを繰り返し処理します。

位置を読み取り、新しい方向を設定します。

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()); 
	}
}

target positionアトリビュートは、エージェント毎にオーバーライドを実施することができますが、それを処理するコードはまだありません。アトリビュートを上書きすると、 "AttributeName_override"という名前の新しいmapmetadataが作成されます。このmapmetadataのキーは、エージェントグループIDですが、ユーザーが自分の3Dアプリケーション内からオーバーライドを指定した場合にのみ表示されます。


エージェントにオーバライドがあるかどうかを把握するためには、メタデータのマップを確認する必要があります。エージェントにオーバライドがない場合は、globalアトリビュートを使用してください。


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()); 
		}
	}
}
モジュールのパフォーマンスを向上させたい場合は、main for loopを並列化するために「tbb」を追加することができます。
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()); 
			}
		}
	});
}

Visual Debuggingオプションの追加

directionベクトルを視覚的にデバッグするためのdraw関数を実装しましょう。ラインデータを格納するためにstd :: vector <AtomsCore :: Vector 3> m_drawLinesをクラスに追加します。
preDraw関数の内部では、時間が変わった場合にのみ呼び出されるため、agentseからデータを収集できます。
そしてdraw関数の中で、最後にdraw lines関数を呼び出して、いくつかのラインを引くことができます。

void preDraw(Atoms::DrawContext* context, const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup)
{
	// The pre draw function is called only on time or attribute change,
	// so it's safe to collect the data from the agents here
	m_drawLines.resize(agents.size()*2);
	for (size_t i = 0; i < agents.size(); i++) 
	{
		Atoms::Agent* agent = agents[i];
		 
		//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;
		
		m_drawLines[i*2] = positionPtr->get();
		m_drawLines[i*2 + 1] = positionPtr->get() + directionPtr0>get();
	}
}

void draw(Atoms::DrawContext* context, const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup) 
{
	// the draw function is called when the dcc update the viewports
	context->lines(m_drawLines);
}

Behaviour モジュールを登録する

Behaviourモジュールを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;
	}
}

Behaviour モジュールをコンパイルする

Windows:

Visual Studioでdllプロジェクトを作成し、atom、tbb、openexr includeを追加して、BUILD_ATOMSPLUGINをプリプロセッサ定義に追加します。


Linux:

Atoms、tbbおよびopendexr includesとBUILD_ATOMSPLUGINをプリプロセッサ定義に追加して、共有オブジェクトとしてコンパイルします。


Behaviour モジュールを使用する

ATOMS_PLUGINS_PATH環境変数に、プラグインが配置されているフォルダーを追加します。その後、Atomsをロードするとモジュールが自動的にロードされます。


最終コード

#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()); 
				}
			}
		});
	}

	void preDraw(Atoms::DrawContext* context, const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup)
	{
		 // The pre draw function is called only when the time is changed or some attribute is changed so
		// we can collect the data from the agents here
		m_drawLines.resize(agents.size()*2);
		for (size_t i = 0; i < agents.size(); i++) 
		{
			Atoms::Agent* agent = agents[i];

			//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;

			m_drawLines[i*2] = positionPtr->get();
			m_drawLines[i*2 + 1] = positionPtr->get() + directionPtr0>get();
		}
	}

	void draw(Atoms::DrawContext* context, const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup) 
	{
		// the draw function is called when the dcc update the viewports
		context->lines(m_drawLines);
	}

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

private:

	std::vector<AtomsCore::Vector3> m_drawLines;
};

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;
	}
}

Copyright © 2017, Toolchefs LTD.