Behaviourモジュールを書く
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()); } } }
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.