比較バージョン

キー

  • この行は追加されました。
  • この行は削除されました。
  • 書式設定が変更されました。
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 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).新しいクラスを作成して、ベースから継承します。

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


コード ブロック
languagecpp
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();
}

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.

コード ブロック
languagecpp
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. This will be done inside the initFrame method.

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


コード ブロック
languagecpp
void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr)
{
}

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

コード ブロック
languagecpp
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 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.

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

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


コード ブロック
languagecpp
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メソッド内で行われます。
こちらの例では、他のメソッドをオーバーライドする必要はありません。



コード ブロック
languagecpp
void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr)
{
}


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

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


コード ブロック
languagecpp
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アトリビュートを使用してください。



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


コード ブロック
languagecpp
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()); 
			}
		}
	});
}

Add visual debugging options

Lets implement a the draw function to visually debug the direction vector. Add a std::vector<AtomsCore::Vector3> m_drawLines to the class to store the lines data.

Inside the preDraw function you can collect the data form the agentse because it is called only if the time changes.

Then inside the draw function you can finally call the draw lines function to draw some lines.

}


Visual Debuggingオプションの追加

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


コード ブロック
languagecpp
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);
}

Register the behaviour module

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


Behaviour モジュールを登録する

BehaviourモジュールをBehaviour Module Factoryに追加する必要があります。


コード ブロック
languagecpp
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

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

Windows:In

visual studio create a dll projects, add the atoms, tbb and openexr includes and add BUILD_ATOMSPLUGIN to the preprocessor definitions.Visual Studioでdllプロジェクトを作成し、atom、tbb、openexr includeを追加して、BUILD_ATOMSPLUGINをプリプロセッサ定義に追加します。


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 codeAtoms、tbbおよびopendexr includesとBUILD_ATOMSPLUGINをプリプロセッサ定義に追加して、共有オブジェクトとしてコンパイルします。


Behaviour モジュールを使用する

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


最終コード

コード ブロック
languagecpp
#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;
	}
}