Writing the behaviour module
First thing to do is create a class and hinerit from the base BehaviourModule class. With a constructor a desctructor and a static creator function used by atoms to create an isntance of this 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();
} |
Lets add some attributes to our node. In this case we need only a target position. The attributes of the modules are stored inside a MapMetadata object, and you can add new attribute using the addAttribute function. So lets add this attribute inside the constructor.このモジュールでは、Target Positionアトリビュートのみが必要になります。モジュールのアトリビュートは、MapMetadataオブジェクト、FollowTargetModule()内に格納されます。addAttribute関数を使用して新しいアトリビュートを追加できます。
アトリビュートは、常に、コンストラクタ内に追加する必要があります。
addAttributeメソッドの3番目のアトリビュートは、そのアトリビュートがエージェント毎に、オーバーライドが可能かどうかを定義します。こちらの例では、オーバーライドを可能にします。
コード ブロック | ||
---|---|---|
| ||
FollowTargetModule(): { AtomsCoreAtoms::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( |
今度は、エージェントのポーズが計算される前に、各フレームで新しいエージェントの方向を計算する必要があります。これは、initFrameメソッド内で行われます。
こちらの例では、他のメソッドをオーバーライドする必要はありません。
コード ブロック | ||
---|---|---|
| ||
void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr)
{
} |
In the init frame we need to get our targetPosition value and iterate over each agent to get the position and set the new directioninitFrame関数より、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()); } } } |
コード ブロック | ||
---|---|---|
| ||
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
The main module is now ready so we need to register it to the behaviour module factoryVisual Debuggingオプションの追加
directionベクトルを視覚的にデバッグするためのdraw関数を実装しましょう。ラインデータを格納するためにstd :: vector <AtomsCore :: Vector 3> m_drawLinesをクラスに追加します。
preDraw関数の内部では、時間が変わった場合にのみ呼び出されるため、agentseからデータを収集できます。
そしてdraw関数の中で、最後にdraw lines関数を呼び出して、いくつかのラインを引くことができます。
コード ブロック | ||
---|---|---|
| ||
extern "C" { ATOMSPLUGIN_EXPORT bool initializePlugin() { void preDraw(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 opendexr 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 the folder where the plugin is located to the ATOMS_PLUGINS_PATH environment variable. Then the module is automatically loaded when you load atomsDrawContext* 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;
}
}
|