Program Listing for File ModelVis.cpp
↰ Return to documentation for file (src/flamegpu/visualiser/ModelVis.cpp
)
// @todo - ifdef visualisation
#include "flamegpu/visualiser/ModelVis.h"
#include <thread>
#include <utility>
#include "flamegpu/simulation/CUDASimulation.h"
#include "flamegpu/model/AgentData.h"
#include "flamegpu/visualiser/FLAMEGPU_Visualisation.h"
namespace flamegpu {
namespace visualiser {
ModelVisData::ModelVisData(const flamegpu::CUDASimulation &_model)
: modelCfg(_model.getModelDescription().name.c_str())
, autoPalette(std::make_shared<AutoPalette>(Stock::Palettes::DARK2))
, model(_model)
, modelData(_model.getModelDescription()) { }
void ModelVisData::hookVis(std::shared_ptr<visualiser::ModelVisData>& vis, std::unordered_map<std::string, std::shared_ptr<detail::CUDAEnvironmentDirectedGraphBuffers>> &map) {
for (auto [name, graph] : graphs) {
auto &graph_buffs = map.at(name);
graph_buffs->setVisualisation(vis);
graph->constructGraph(graph_buffs);
vis->rebuildEnvGraph(name);
}
}
void ModelVisData::registerEnvProperties() {
if (model.singletons && !env_registered) {
char* const host_env_origin = const_cast<char*>(static_cast<const char*>(model.singletons->environment->getHostBuffer()));
for (const auto &panel : modelCfg.panels) {
for (const auto &element : panel.second->ui_elements) {
if (auto a = dynamic_cast<EnvPropertyElement*>(element.get())) {
auto & prop = model.singletons->environment->getPropertiesMap().at(a->getName());
visualiser->registerEnvironmentProperty(a->getName(), host_env_origin + prop.offset, prop.type, prop.elements, prop.isConst);
}
}
}
env_registered = true;
}
}
void ModelVisData::updateBuffers(const unsigned int& sc) {
if (visualiser) {
bool has_agents = false;
for (auto& a : agents) {
has_agents = a.second->requestBufferResizes(visualiser, sc == 0 || sc == UINT_MAX) || has_agents;
}
// Block the sim when we first get agents, until vis has resized buffers, incase vis is being slow to init
if (has_agents && (sc == 0 || sc == UINT_MAX)) {
while (!visualiser->buffersReady()) {
// Do nothing, just spin until ready
std::this_thread::yield();
}
}
// wait for lock data->visualiser (its probably executing render loop in separate thread) This might not be 100% safe. RequestResize might need extra thread safety.
visualiser->lockMutex();
// Update step count
if (sc != UINT_MAX) {
visualiser->setStepCount(sc);
}
for (auto& a : agents) {
a.second->updateBuffers(visualiser);
}
visualiser->releaseMutex();
// Block the sim again, until vis is fully ready
if (has_agents && (sc == 0 || sc == UINT_MAX)) {
while (!visualiser->isReady()) {
// Do nothing, just spin until ready
std::this_thread::yield();
}
}
}
}
void ModelVisData::updateRandomSeed() {
if (visualiser) {
// Yolo thread safety, shouldn't matter if random seed is printed wrong for a single frame
visualiser->setRandomSeed(model.getSimulationConfig().random_seed);
}
}
void ModelVisData::buildEnvGraphs() {
for (auto [name, graph] : graphs) {
graph->constructGraph(model.directed_graph_map.at(name));
}
}
void ModelVisData::rebuildEnvGraph(const std::string &graph_name) {
graphs.at(graph_name)->constructGraph(model.directed_graph_map.at(graph_name));
}
ModelVis::ModelVis(std::shared_ptr<ModelVisData> _data, bool _isSWIG)
: isSWIG(_isSWIG)
, data(std::move(_data)) { }
void ModelVis::setAutoPalette(const Palette& palette) {
data->autoPalette = std::make_shared<AutoPalette>(palette);
}
void ModelVis::clearAutoPalette() {
data->autoPalette = nullptr;
}
AgentVis ModelVis::addAgent(const std::string &agent_name) {
// If agent exists
if (data->modelData.agents.find(agent_name) != data->modelData.agents.end()) {
// If agent is not already in vis map
auto visAgent = data->agents.find(agent_name);
if (visAgent == data->agents.end()) {
// Create new vis agent
return AgentVis(data->agents.emplace(agent_name, std::make_shared<AgentVisData>(data->model.getCUDAAgent(agent_name), data->autoPalette)).first->second);
}
return AgentVis(visAgent->second);
}
THROW exception::InvalidAgentName("Agent name '%s' was not found within the model description hierarchy, "
"in ModelVis::addAgent()\n",
agent_name.c_str());
}
AgentVis ModelVis::Agent(const std::string &agent_name) {
// If agent exists
if (data->modelData.agents.find(agent_name) != data->modelData.agents.end()) {
// If agent is already in vis map
auto visAgent = data->agents.find(agent_name);
if (visAgent != data->agents.end()) {
// Create new vis agent
return AgentVis(visAgent->second);
}
THROW exception::InvalidAgentName("Agent name '%s' has not been marked for visualisation, ModelVis::addAgent() must be called first, "
"in ModelVis::Agent()\n",
agent_name.c_str());
}
THROW exception::InvalidAgentName("Agent name '%s' was not found within the model description hierarchy, "
"in ModelVis::Agent()\n",
agent_name.c_str());
}
EnvironmentGraphVis ModelVis::addGraph(const std::string& graph_name) {
// If graph exists
auto graph_it = data->modelData.environment->directed_graphs.find(graph_name);
if (graph_it != data->modelData.environment->directed_graphs.end()) {
// If graph is not already in vis map
auto visGraph = data->graphs.find(graph_name);
if (visGraph == data->graphs.end()) {
// Create a line config for the graph
auto m = std::make_shared<LineConfig>(LineConfig::Type::Lines);
data->modelCfg.dynamic_lines.insert({std::string("graph_") + graph_name, m});
// Create new vis graph
return EnvironmentGraphVis(data->graphs.emplace(graph_name, std::make_shared<EnvironmentGraphVisData>(graph_it->second, m)).first->second);
}
return EnvironmentGraphVis(visGraph->second);
}
THROW exception::InvalidEnvGraph("Environment direct graph name '%s' was not found within the model description hierarchy, "
"in ModelVis::addGraph()\n",
graph_name.c_str());
}
EnvironmentGraphVis ModelVis::Graph(const std::string& graph_name) {
// If graph exists
if (data->modelData.environment->directed_graphs.find(graph_name) != data->modelData.environment->directed_graphs.end()) {
// If graph is already in vis map
auto visGraph = data->graphs.find(graph_name);
if (visGraph != data->graphs.end()) {
return EnvironmentGraphVis(visGraph->second);
}
THROW exception::InvalidEnvGraph("Environment direct graph name '%s' has not been marked for visualisation, ModelVis::addGraph() must be called first, "
"in ModelVis::Agent()\n",
graph_name.c_str());
}
THROW exception::InvalidEnvGraph("Environment direct graph name '%s' was not found within the model description hierarchy, "
"in ModelVis::addGraph()\n",
graph_name.c_str());
}
// Below methods are related to executing the visualiser
void ModelVis::activate() {
// Only execute if background thread is not active
if ((!data->visualiser || !data->visualiser->isRunning()) && !data->model.getSimulationConfig().console_mode) {
// Send Python status to the visualiser
data->modelCfg.isPython = isSWIG;
// Init visualiser
data->visualiser = std::make_unique<FLAMEGPU_Visualisation>(data->modelCfg); // Window resolution
data->visualiser->setRandomSeed(data->model.getSimulationConfig().random_seed);
for (auto &agent : data->agents) {
// If x and y aren't set, throw exception
if (agent.second->core_tex_buffers.find(TexBufferConfig::Position_x) == agent.second->core_tex_buffers.end() &&
agent.second->core_tex_buffers.find(TexBufferConfig::Position_y) == agent.second->core_tex_buffers.end() &&
agent.second->core_tex_buffers.find(TexBufferConfig::Position_z) == agent.second->core_tex_buffers.end() &&
agent.second->core_tex_buffers.find(TexBufferConfig::Position_xy) == agent.second->core_tex_buffers.end() &&
agent.second->core_tex_buffers.find(TexBufferConfig::Position_xyz) == agent.second->core_tex_buffers.end()) {
THROW exception::VisualisationException("Agent '%s' has not had x, y or z variables set, agent requires location to render, "
"in ModelVis::activate()\n",
agent.second->agentData->name.c_str());
}
agent.second->initBindings(data->visualiser);
}
data->env_registered = false;
data->registerEnvProperties();
data->visualiser->start();
}
}
void ModelVis::deactivate() {
if (data->visualiser && data->visualiser->isRunning()) {
data->visualiser->stop();
join();
data->visualiser.reset();
}
}
void ModelVis::join() {
if (data->visualiser) {
data->visualiser->join();
data->visualiser.reset();
}
}
bool ModelVis::isRunning() const {
return data->visualiser ? data->visualiser->isRunning() : false;
}
void ModelVis::setWindowTitle(const std::string& title) {
ModelConfig::setString(&data->modelCfg.windowTitle, title);
}
void ModelVis::setWindowDimensions(const unsigned int& width, const unsigned int& height) {
data->modelCfg.windowDimensions[0] = width;
data->modelCfg.windowDimensions[1] = height;
}
void ModelVis::setClearColor(const float& red, const float& green, const float& blue) {
data->modelCfg.clearColor[0] = red;
data->modelCfg.clearColor[1] = green;
data->modelCfg.clearColor[2] = blue;
}
void ModelVis::setFPSVisible(const bool& showFPS) {
data->modelCfg.fpsVisible = showFPS;
}
void ModelVis::setFPSColor(const float& red, const float& green, const float& blue) {
data->modelCfg.fpsColor[0] = red;
data->modelCfg.fpsColor[1] = green;
data->modelCfg.fpsColor[2] = blue;
}
void ModelVis::setInitialCameraLocation(const float &x, const float &y, const float &z) {
data->modelCfg.cameraLocation[0] = x;
data->modelCfg.cameraLocation[1] = y;
data->modelCfg.cameraLocation[2] = z;
}
void ModelVis::setInitialCameraTarget(const float &x, const float &y, const float &z) {
data->modelCfg.cameraTarget[0] = x;
data->modelCfg.cameraTarget[1] = y;
data->modelCfg.cameraTarget[2] = z;
}
void ModelVis::setInitialCameraRoll(const float &roll) {
data->modelCfg.cameraRoll = roll;
}
void ModelVis::setCameraSpeed(const float &speed, const float &shiftMultiplier) {
data->modelCfg.cameraSpeed[0] = speed;
data->modelCfg.cameraSpeed[1] = shiftMultiplier;
}
void ModelVis::setViewClips(const float &nearClip, const float &farClip) {
data->modelCfg.nearFarClip[0] = nearClip;
data->modelCfg.nearFarClip[1] = farClip;
}
void ModelVis::setOrthographic(const bool& isOrtho) {
data->modelCfg.isOrtho = isOrtho;
}
void ModelVis::setOrthographicZoomModifier(const float& zoomMod) {
data->modelCfg.orthoZoom = zoomMod;
}
void ModelVis::setStepVisible(const bool& showStep) {
data->modelCfg.stepVisible = showStep;
}
void ModelVis::setSimulationSpeed(const unsigned int& _stepsPerSecond) {
data->modelCfg.stepsPerSecond = _stepsPerSecond;
}
void ModelVis::setBeginPaused(const bool& beginPaused) {
data->modelCfg.beginPaused = beginPaused;
}
StaticModelVis ModelVis::newStaticModel(const std::string &modelPath, const std::string &texturePath) {
// Create ModelConfig::StaticModel
auto m = std::make_shared<ModelConfig::StaticModel>();
// set modelPath, texturePath
m->path = modelPath;
m->texture = texturePath;
// add to ModelConfig.staticModels
data->modelCfg.staticModels.push_back(m);
// Create return type
return StaticModelVis(data->modelCfg.staticModels.back());
}
LineVis ModelVis::newLineSketch(float r, float g, float b, float a) {
auto m = std::make_shared<LineConfig>(LineConfig::Type::Lines);
data->modelCfg.lines.push_back(m);
return LineVis(m, r, g, b, a);
}
LineVis ModelVis::newPolylineSketch(float r, float g, float b, float a) {
auto m = std::make_shared<LineConfig>(LineConfig::Type::Polyline);
data->modelCfg.lines.push_back(m);
return LineVis(m, r, g, b, a);
}
PanelVis ModelVis::newUIPanel(const std::string& panel_title) {
if (data->modelCfg.panels.find(panel_title) != data->modelCfg.panels.end()) {
THROW exception::InvalidOperation("Panel with title '%s' already exists.\n", panel_title.c_str());
}
auto m = std::make_shared<PanelConfig>(panel_title);
data->modelCfg.panels.emplace(panel_title, m);
return PanelVis(m, data->model.getModelDescription().environment);
}
} // namespace visualiser
} // namespace flamegpu