Writing a layout module
Atoms has several layout modules but you can create new modules with c++. This example shows how create a layout module for generating agents along a input curve.
This example is made of two parts:
- the layout generator responsible for creating the agents
- a behaviour module responsible for creating the layout generator and attaching it to the agent group
Writing the curve layout generator
The layout generator object generates the description of new agents created at every frame. A generator can be static or dynamic. A static generator creates the agent descriptions only at the first frame, while a dynamic one can generate new agents at every frame. In this example our generator is static, since the agents are created only at the first frame along the curve. If you want to create a dynamic generator just use the setAsStaticgenerator(false) function inside the constructor.
The generate function creates a vector of AgentInitData objects. This object defines the position, direction, agent type, groupId of the new agents. The agent group uses this information to create thee new agents.
#include <Atoms/LayoutGenerators/LayoutGenerator.h> #include <AtomsUtils/Curve.h> #include <Atoms/AgentTypes.h> class CurveLayoutGenerator : public LayoutGenerator { public: CurveLayoutGenerator(); virtual ~CurveLayoutGenerator(); // the generator function virtual std::vector<AgentInitData> generate(double time); inline void setCurve(AtomsUtils::Curve& curve) { m_curve = curve; } inline void setDirection(AtomsCore::Vector3& dir) { m_direction = dir; } inline void setUpVector(AtomsCore::Vector3& dir) { m_upVector = dir; } inline void setNumberOfAgents(size_t value) { m_numberOfAgents = value; } inline void setDirectionType(short value) { m_directionType = value; } inline void setInvertDirection(short value) { m_invertDirection = value; } inline void setAgentTypeOverride(const std::map<int, std::string>& value) { m_agentTypeOverride = value; } inline void setDirectionOverride(const std::map<int, AtomsCore::Vector3>& value) { m_directionOverride = value; } private: // Input curve AtomsUtils::Curve m_curve; // Agent direction AtomsCore::Vector3 m_direction; // Up vector AtomsCore::Vector3 m_upVector; // Agent direction override std::map<int, AtomsCore::Vector3> m_directionOverride; // Agent type override std::map<int, std::string> m_agentTypeOverride; // Number of agents along the curve size_t m_numberOfAgents; // Direction type: 0 - tangent, 1 - normal, 2 - custom short m_directionType; // Invert the direction bool m_invertDirection; }; CurveLayoutGenerator::CurveLayoutGenerator(): LayoutGenerator(), m_direction(1.0,0.0,0.0), m_upVector(0.0,1.0,0.0), m_numberOfAgents(0), m_directionType(0), m_invertDirection(false) { setAsStaticGenerator(true); } CurveLayoutGenerator::~CurveLayoutGenerator() { } // the generator function since is a static generator this function is called once std::vector<AgentInitData> CurveLayoutGenerator::generate(double time) { // Check if the curve is a valid if ((m_curve.cvs().size() < 2) || (m_numberOfAgents < 1)) return std::vector<AgentInitData>(); Atoms::AgentTypes &agTypes = Atoms::AgentTypes::instance(); // initialize the vector std::vector<AgentInitData> data(m_numberOfAgents); // curve parameter step float curveStep = m_curve.maxU() / static_cast<float>(std::max(static_cast<size_t>(1), m_numberOfAgents - 1)); for (size_t i = 0; i < m_numberOfAgents; i++) { float param = static_cast<float>(i) * curveStep; AgentInitData& agentData = data[i]; // set the agent position agentData.position = m_curve.pointOnCurve(param); // set the agent group id, this must be unique agentData.groupId = i; // set the agent type checking if there is any override auto atIt = m_agentTypeOverride.find(i); if (atIt != m_agentTypeOverride.end()) { agentData.agentType = agTypes.agentType(atIt->second); } else { agentData.agentType = agentType(); } // set the agent direction checking if there is any override auto dIt = m_directionOverride.find(i); if (dIt != m_directionOverride.end()) { agentData.direction = dIt->second; } else { switch (m_directionType) { case 0: // use the tangent { agentData.direction = m_curve.tangentOnCurve(param); break; } case 1: // use the normal { agentData.direction = m_curve.tangentOnCurve(param); agentData.direction = agentData.direction.cross(m_upVector); break; } default: { agentData.direction = m_direction; break; } } } if (m_invertDirection) { agentData.direction *= -1.0; } } return std::move(data); }
Writing the curve layout module
To use the curve layout generator a new behaviour modules is needed. This behaviour module exposes the generator attributes to the user and sends all the data to the generator itself.
#include <Atoms/BehaviourModule.h> #include <Atoms/AgentTypes.h> #include <Atoms/AgentGroup.h> #include <AtomsCore/Metadata/IntMetadata.h> #include <AtomsCore/Metadata/Vector3ArrayMetadata.h> #include <AtomsCore/Metadata/StringMetadata.h> #include <AtomsCore/Metadata/CurveMetadata.h> #include <AtomsCore/Metadata/BoolMetadata.h> class CurveGeneratorModule : public Atoms::BehaviourModule { public: CurveGeneratorModule(); ~CurveGeneratorModule(); virtual void initSimulation(Atoms::AgentGroup* agentGroup = nullptr); static Atoms::BehaviourModule* creator(const std::string& parameter); }; CurveGeneratorModule::CurveGeneratorModule() : Atoms::BehaviourModule() { AtomsCore::MapMetadata& metadata = attributes(); AtomsCore::StringMetadata atype("man"); addAttribute("agentType", &atype, true); AtomsCore::CurveMetadata curve; metadata.addEntry("curve", &curve); AtomsCore::IntMetadata numAgents(10); metadata.addEntry("numAgents", &numAgents); AtomsCore::IntMetadata dirType(0); metadata.addEntry("directionType", &dirType); AtomsCore::BoolMetadata invDir(false); metadata.addEntry("invertDirection", &invDir); AtomsCore::Vector3Metadata dir(AtomsCore::Vector3(1.0, 0.0, 0.0)); addAttribute("direction", &dir, true); AtomsCore::Vector3Metadata up(AtomsCore::Vector3(0.0, 1.0, 0.0)); addAttribute("upVector", &up, false); } CurveGeneratorModule::~CurveGeneratorModule() {} void CurveGeneratorModule::initSimulation(Atoms::AgentGroup* agentGroup) { // Get the module attributes AtomsCore::MapMetadata& metadata = attributes(); AtomsPtr<AtomsCore::CurveMetadata> curvePtr = metadata.getTypedEntry<AtomsCore::CurveMetadata>("curve"); AtomsPtr<AtomsCore::IntMetadata> numAgentsPtr = metadata.getTypedEntry<AtomsCore::IntMetadata>("numAgents"); AtomsPtr<AtomsCore::IntMetadata> directionTypePtr = metadata.getTypedEntry<AtomsCore::IntMetadata>("directionType"); AtomsPtr<AtomsCore::BoolMetadata> invertDirectionPtr = metadata.getTypedEntry<AtomsCore::BoolMetadata>("invertDirection"); AtomsPtr<AtomsCore::Vector3Metadata> directionPtr = metadata.getTypedEntry<AtomsCore::Vector3Metadata>("direction"); AtomsPtr<AtomsCore::Vector3Metadata> upPtr = metadata.getTypedEntry<AtomsCore::Vector3Metadata>("upVector"); AtomsPtr<AtomsCore::StringMetadata> agentTypePtr = metadata.getTypedEntry<AtomsCore::StringMetadata>("agentType"); // Check if the agent type is valid Atoms::AgentTypePtr aType = Atoms::AgentTypes::instance().agentType(agentTypePtr->value()); if (!aType) { return; } // Get all agent type overrides AtomsPtr<AtomsCore::MapMetadata> atypeOverride = metadata.getTypedEntry<AtomsCore::MapMetadata>("agentType_override"); std::map<int, std::string> aTypesOverride; for (auto it = atypeOverride->begin(); it != atypeOverride->end(); it++) { if (!it->second) continue; if (it->second->typeId() != AtomsCore::StringMetadata::staticTypeId()) continue; auto value = std::static_pointer_cast<AtomsCore::StringMetadata>(it->second); if (!value) continue; Atoms::AgentTypePtr aType = Atoms::AgentTypes::instance().agentType(value->get()); if (!aType) { continue; } aTypesOverride[std::stoi(it->first)] = value->get(); } // Get all agent direction overrides AtomsPtr<AtomsCore::MapMetadata> directionOverride = metadata.getTypedEntry<AtomsCore::MapMetadata>("direction_override"); std::map<int, AtomsCore::Vector3> dirOverride; for (auto it = directionOverride->begin(); it != directionOverride->end(); it++) { if (!it->second) continue; if (it->second->typeId() != AtomsCore::Vector3Metadata::staticTypeId()) continue; auto value = std::static_pointer_cast<AtomsCore::Vector3Metadata>(it->second); if (value) dirOverride[std::stoi(it->first)] = value->get(); } // Create the layout generator and set all the attributes Atoms::CurveLayoutGenerator* curveLayout = new Atoms::CurveLayoutGenerator(); curveLayout->setCurve(curvePtr->get()); curveLayout->setNumberOfAgents(numAgentsPtr->value()); curveLayout->setDirectionType(directionTypePtr->value()); curveLayout->setInvertDirection(invertDirectionPtr->value()); curveLayout->setDirection(directionPtr->value()); curveLayout->setUpVector(upPtr->value()); curveLayout->setAgentTypeOverride(aTypesOverride); curveLayout->setDirectionOverride(dirOverride); Atoms::LayoutGeneratorPtr generator(curveLayout); generator->setAgentType(aType); agentGroup->setLayoutGenerator(generator); } Atoms::BehaviourModule* CurveGeneratorModule::creator(const std::string& parameter) { return new CurveGeneratorModule(); } extern "C" { ATOMSPLUGIN_EXPORT bool initializePlugin() { return true; } ATOMSPLUGIN_EXPORT bool unitializePlugin() { return true; } ATOMSPLUGIN_EXPORT bool initializeScene() { Atoms::BehaviourModules& moduleManager = Atoms::BehaviourModules::instance(); moduleManager.registerBehaviourModule("SimpleCurveLayout", &CurveGeneratorModule::creator, Atoms::BehaviourModules::kNative); return true; } ATOMSPLUGIN_EXPORT bool unitializeScene() { return true; } }
Copyright © 2017, Toolchefs LTD.