Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Environment directed graph visualiser support #138

Merged
merged 2 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/flamegpu/visualiser/FLAMEGPU_Visualisation.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,23 @@ class FLAMEGPU_Visualisation {
void requestBufferResizes(const std::string &agent_name, const std::string &state_name, const unsigned int buffLen, bool force) {
requestBufferResizes(agent_name.c_str(), state_name.c_str(), buffLen, force);
}
/**
* Main render mutex used to prevent race conditions rendering agents
*/
void lockMutex();
void releaseMutex();
/**
* Dynamic line mutex ensures graphs are fully updated before synced to render
*/
void lockDynamicLinesMutex();
void releaseDynamicLinesMutex();
void updateAgentStateBuffer(const std::string &agent_name, const std::string &state_name, const unsigned int buffLen,
const std::map<TexBufferConfig::Function, TexBufferConfig>& core_tex_buffers, const std::multimap<TexBufferConfig::Function, CustomTexBufferConfig>& tex_buffers) {
updateAgentStateBuffer(agent_name.c_str(), state_name.c_str(), buffLen, core_tex_buffers, tex_buffers);
}
void updateDynamicLine(const std::string &graph_name) {
updateDynamicLine(graph_name.c_str());
}
/**
* Provide an environment property, so it can be displayed
*/
Expand Down Expand Up @@ -81,8 +92,11 @@ class FLAMEGPU_Visualisation {
void requestBufferResizes(const char *agent_name, const char *state_name, const unsigned int buffLen, bool force);
void updateAgentStateBuffer(const char *agent_name, const char *state_name, const unsigned int buffLen,
const std::map<TexBufferConfig::Function, TexBufferConfig>& core_tex_buffers, const std::multimap<TexBufferConfig::Function, CustomTexBufferConfig>& tex_buffers);
void updateDynamicLine(const char* graph_name);

Visualiser *vis = nullptr;
LockHolder *lock = nullptr;
LockHolder *dynamic_lines_lock = nullptr;
/**
* If non-0, limits the number of simulation steps per second, by blocking the return of releaseMutex
* Blocking whilst the mutex was held, would block visualisation updates as that also uses the mutex
Expand Down
8 changes: 8 additions & 0 deletions include/flamegpu/visualiser/config/ModelConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@
* @note Defaults to (0,0,0)
*/
float cameraTarget[3];
/**
* The initial camera roll angle in radians
*/
float cameraRoll;
/**
* The movement speed of the camera in units per millisecond, and the shift key multiplier
* When shift is pressed the movement speed is multiplied by this value
Expand Down Expand Up @@ -125,6 +129,10 @@
* Store of user defined line renderings
*/
std::list<std::shared_ptr<LineConfig>> lines;
/**
* Store of user defined graph renderings
*/
std::map<std::string, std::shared_ptr<LineConfig>> dynamic_lines;
/**
* Store of user defined UI panels
*/
Expand Down Expand Up @@ -153,7 +161,7 @@
if (*target)
free(const_cast<char*>(*target));
char *t = static_cast<char*>(malloc(src.size() + 1));
snprintf(t, src.size() + 1, "%s", src.c_str());

Check failure on line 164 in include/flamegpu/visualiser/config/ModelConfig.h

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Add #include <cstdio> for snprintf

Check failure on line 164 in include/flamegpu/visualiser/config/ModelConfig.h

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Add #include <cstdio> for snprintf
*target = t;
}
};
Expand Down
3 changes: 3 additions & 0 deletions src/flamegpu/visualiser/Draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
const unsigned int Draw::DEFAULT_INITIAL_VBO_LENGTH = 1024;
const float Draw::STORAGE_MUTLIPLIER = 2.0f;
Draw::Draw(const unsigned int &bufferLength, const glm::vec4 &initialColor, const float &initialWidth)
: tName()

Check failure on line 13 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.

Check failure on line 13 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.
, tColor(initialColor)

Check failure on line 14 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.

Check failure on line 14 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.
, tWidth(initialWidth)

Check failure on line 15 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.

Check failure on line 15 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.
, tType()

Check failure on line 16 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.

Check failure on line 16 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.
, shaders(std::make_shared<Shaders>(Stock::Shaders::COLOR_NOSHADE))

Check failure on line 17 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.

Check failure on line 17 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.
, vertices(GL_FLOAT, 3, sizeof(float))

Check failure on line 18 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.

Check failure on line 18 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.
, colors(GL_FLOAT, 4, sizeof(float))

Check failure on line 19 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.

Check failure on line 19 in src/flamegpu/visualiser/Draw.cpp

View workflow job for this annotation

GitHub Actions / cpplint (11.0, ubuntu-20.04)

Do not indent within a namespace.
, vboLen(bufferLength == 0 ? DEFAULT_INITIAL_VBO_LENGTH : bufferLength)
, vboOffset(0)
, requiredLength(0) {
Expand Down Expand Up @@ -91,6 +91,9 @@
requiredLength += tState.count;
stateDirectory.insert({ tName, std::move(tState) });
}
bool Draw::has(const std::string &name) {
return stateDirectory.find(name) != stateDirectory.end();
}
Draw::State Draw::_save(bool isTemporary) {
if (tType == Type::Lines && tVertices.size() % 2 != 0) {
THROW VisAssert("Draw::_save(): Line drawings require an even number of vertices.\n");
Expand Down
4 changes: 4 additions & 0 deletions src/flamegpu/visualiser/Draw.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ class Draw : Renderable {
* @throw Throws a runtime exception if called whilst the draw state is not open
*/
void save(bool replaceExisting = false);
/**
* Check whether a drawing with the name exists.
**/
bool has(const std::string &name);
/**
* Renders a saved drawstate
* @param name The name of the draw state to render
Expand Down
14 changes: 14 additions & 0 deletions src/flamegpu/visualiser/FLAMEGPU_Visualisation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ FLAMEGPU_Visualisation::~FLAMEGPU_Visualisation() {
delete vis;
if (lock)
delete lock;
if (dynamic_lines_lock)
delete dynamic_lines_lock;
}
void FLAMEGPU_Visualisation::addAgentState(const char *agent_name, const char *state_name, const AgentStateConfig &vc,
const std::map<TexBufferConfig::Function, TexBufferConfig>& core_tex_buffers, const std::multimap<TexBufferConfig::Function, CustomTexBufferConfig>& tex_buffers) {
Expand All @@ -40,6 +42,9 @@ void FLAMEGPU_Visualisation::updateAgentStateBuffer(const char *agent_name, cons
void FLAMEGPU_Visualisation::registerEnvironmentProperty(const std::string& property_name, void* ptr, const std::type_index type, const unsigned int elements, const bool is_const) {
vis->registerEnvironmentProperty(property_name, ptr, type, elements, is_const);
}
void FLAMEGPU_Visualisation::updateDynamicLine(const char *graph_name) {
vis->updateDynamicLine(graph_name);
}
void FLAMEGPU_Visualisation::setStepCount(const unsigned int stepCount) {
vis->setStepCount(stepCount);
}
Expand Down Expand Up @@ -89,6 +94,15 @@ void FLAMEGPU_Visualisation::releaseMutex() {
}
}
}
void FLAMEGPU_Visualisation::lockDynamicLinesMutex() {
dynamic_lines_lock = new LockHolder(vis->getDynamicLineMutex());
}
void FLAMEGPU_Visualisation::releaseDynamicLinesMutex() {
if (dynamic_lines_lock) {
delete dynamic_lines_lock;
dynamic_lines_lock = nullptr;
}
}

} // namespace visualiser
} // namespace flamegpu
75 changes: 49 additions & 26 deletions src/flamegpu/visualiser/Visualiser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,28 @@ Visualiser::RenderInfo::RenderInfo(const AgentStateConfig& vc,
entity->loadKeyFrameModel(vc.model_pathB);
}
}

void addLine(std::shared_ptr<Draw> &lines, const std::shared_ptr<LineConfig> &line, const std::string &name, bool replace) {
// Check it's valid
if (line->lineType == LineConfig::Type::Polyline) {
if (line->vertices.size() < 6 || line->vertices.size() % 3 != 0 || line->colors.size() % 4 != 0 || (line->colors.size() / 4) * 3 != line->vertices.size()) {
THROW SketchError("Polyline sketch contains invalid number of vertices (%d/3) or colours (%d/4).\n", line->vertices.size(), line->colors.size());
}
} else if (line->lineType == LineConfig::Type::Lines) {
if (line->vertices.size() < 6 || line->vertices.size() % 6 != 0 || line->colors.size() % 4 != 0 || (line->colors.size() / 4) * 3 != line->vertices.size()) {
THROW SketchError("Lines sketch contains invalid number of vertices (%d/3) or colours (%d/4).\n", line->vertices.size(), line->colors.size());
}
}
// Convert to Draw
lines->begin(line->lineType == LineConfig::Type::Polyline ? Draw::Type::Polyline : Draw::Type::Lines, name);
for (size_t i = 0; i < line->vertices.size() / 3; ++i) {
lines->color(*reinterpret_cast<const glm::vec4*>(&line->colors[i * 4]));
lines->vertex(*reinterpret_cast<const glm::vec3*>(&line->vertices[i * 3]));
}
lines->save(replace);
}
Visualiser::Visualiser(const ModelConfig& modelcfg)
: hud(std::make_shared<HUD>(modelcfg.windowDimensions[0], modelcfg.windowDimensions[1]))
, camera(std::make_shared<NoClipCamera>(*reinterpret_cast<const glm::vec3*>(&modelcfg.cameraLocation[0]), *reinterpret_cast<const glm::vec3*>(&modelcfg.cameraTarget[0])))
, camera(std::make_shared<NoClipCamera>(*reinterpret_cast<const glm::vec3*>(&modelcfg.cameraLocation[0]), *reinterpret_cast<const glm::vec3*>(&modelcfg.cameraTarget[0]), modelcfg.cameraRoll))
, isInitialised(false)
, continueRender(false)
, buffersAllocated(false)
Expand Down Expand Up @@ -149,9 +167,12 @@ Visualiser::Visualiser(const ModelConfig& modelcfg)
imguiPanel = std::make_shared<ImGuiPanel>(modelcfg.panels, *this);
hud->add(imguiPanel, HUD::AnchorV::North, HUD::AnchorH::West, glm::ivec2(0, 0), INT_MAX-2);
}
lines = std::make_shared<Draw>();
lines->setViewMatPtr(camera->getViewMatPtr());
lines->setProjectionMatPtr(&this->projMat);
lines_static = std::make_shared<Draw>();
lines_static->setViewMatPtr(camera->getViewMatPtr());
lines_static->setProjectionMatPtr(&this->projMat);
lines_dynamic = std::make_shared<Draw>();
lines_dynamic->setViewMatPtr(camera->getViewMatPtr());
lines_dynamic->setProjectionMatPtr(&this->projMat);
// Process static models
for (auto &sm : modelcfg.staticModels) {
std::shared_ptr<Entity> entity;
Expand Down Expand Up @@ -186,23 +207,7 @@ Visualiser::Visualiser(const ModelConfig& modelcfg)
}
// Process lines
for (auto &line : modelcfg.lines) {
// Check it's valid
if (line->lineType == LineConfig::Type::Polyline) {
if (line->vertices.size() < 6 || line->vertices.size() % 3 != 0 || line->colors.size() % 4 != 0 || (line->colors.size() / 4) * 3 != line->vertices.size()) {
THROW SketchError("Polyline sketch contains invalid number of vertices (%d/3) or colours (%d/4).\n", line->vertices.size(), line->colors.size());
}
} else if (line->lineType == LineConfig::Type::Lines) {
if (line->vertices.size() < 6 || line->vertices.size() % 6 != 0 || line->colors.size() % 4 != 0 || (line->colors.size() / 4) * 3 != line->vertices.size()) {
THROW SketchError("Lines sketch contains invalid number of vertices (%d/3) or colours (%d/4).\n", line->vertices.size(), line->colors.size());
}
}
// Convert to Draw
lines->begin(line->lineType == LineConfig::Type::Polyline ? Draw::Type::Polyline : Draw::Type::Lines, std::to_string(totalLines++));
for (size_t i = 0; i < line->vertices.size() / 3; ++i) {
lines->color(*reinterpret_cast<const glm::vec4*>(&line->colors[i * 4]));
lines->vertex(*reinterpret_cast<const glm::vec3*>(&line->vertices[i * 3]));
}
lines->save();
addLine(lines_static, line, std::to_string(totalLines++), false);
}
// Default lighting, single point light attached to camera
// Maybe in future let user specify lights instead of this
Expand Down Expand Up @@ -445,12 +450,23 @@ void Visualiser::render() {
hud->remove(splashScreen);
splashScreen.reset();
}
// Update dynamic lines if they have changed
{
auto lock = std::lock_guard<std::mutex>(lines_dynamic_mutex);
for (auto &name : lines_dynamic_updates)
addLine(lines_dynamic, modelConfig.dynamic_lines.at(name), name, true);
lines_dynamic_updates.clear();
}
// Render lines last, as they may contain alpha
if (renderLines) {
GL_CALL(glEnable(GL_BLEND));
GL_CALL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
for (unsigned int i = 0; i < totalLines; ++i)
lines->render(std::to_string(i));
lines_static->render(std::to_string(i));
for (const auto &[name, _] : modelConfig.dynamic_lines)
// Dynamic lines may begin uninitialised
if (lines_dynamic->has(name))
lines_dynamic->render(name);
GL_CALL(glDisable(GL_BLEND));
}
GL_CALL(glViewport(0, 0, windowDims.x, windowDims.y));
Expand Down Expand Up @@ -804,7 +820,8 @@ void Visualiser::deallocateGLObjects() {
for (auto &as : agentStates) {
as.second.entity.reset();
}
this->lines.reset();
this->lines_static.reset();
this->lines_dynamic.reset();
render_buffer.reset();
screenshot_buffer.reset();

Expand Down Expand Up @@ -896,8 +913,10 @@ void Visualiser::handleKeypress(SDL_Keycode keycode, int /*x*/, int /*y*/) {
break;
case SDLK_F5:
// Reload all shaders
if (this->lines)
this->lines->reload();
if (this->lines_static)
this->lines_static->reload();
if (this->lines_dynamic)
this->lines_dynamic->reload();
for (auto& as : this->agentStates)
as.second.entity->reload();
for (auto& sm : this->staticModels)
Expand Down Expand Up @@ -1241,6 +1260,10 @@ void Visualiser::setWindowIcon() {
if (surface)
SDL_SetWindowIcon(window, surface.get());
}
void Visualiser::updateDynamicLine(const std::string& name) {
// Store a list of dynamic line updates, as they must occur in render thread
lines_dynamic_updates.insert(name);
}

} // namespace visualiser
} // namespace flamegpu
17 changes: 15 additions & 2 deletions src/flamegpu/visualiser/Visualiser.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <unordered_map>
#include <utility>
#include <map>
#include <set>
#include <cstdint>
#define GLM_FORCE_NO_CTOR_INIT
#include <glm/glm.hpp>
Expand Down Expand Up @@ -67,7 +68,7 @@ class Visualiser : public ViewportExt {

public:
explicit Visualiser(const ModelConfig &modelcfg);
~Visualiser();
~Visualiser() override;
/**
* Starts the render loop running
*/
Expand Down Expand Up @@ -128,6 +129,11 @@ class Visualiser : public ViewportExt {
* Provide the env_cache ptr for the specified environment property, for visualisation
*/
void registerEnvironmentProperty(const std::string& property_name, void* ptr, std::type_index type, unsigned int elements, bool is_const);
/**
* Update the dynamic_lines with the corresponding name
* @note getDynamicLineMutex() should be locked before this is called
*/
void updateDynamicLine(const std::string &name);

private:
void run();
Expand Down Expand Up @@ -242,6 +248,11 @@ class Visualiser : public ViewportExt {
* @see updateAgentStateBuffer(const std::string &, const std::string &, const unsigned int, float *, float *, float *, float *)
*/
std::mutex &getRenderBufferMutex() { return render_buffer_mutex; }
/**
* This must be locked when changes to dynamic_lines are performed
* e.g. during graph updates
*/
std::mutex &getDynamicLineMutex() { return lines_dynamic_mutex; }
/**
* Sets the value to be rendered to the HUD step counter (if enabled)
* @param stepCount The step value to be displayed
Expand Down Expand Up @@ -358,7 +369,9 @@ class Visualiser : public ViewportExt {
/**
* User defined lines to be rendered
*/
std::shared_ptr<Draw> lines;
std::shared_ptr<Draw> lines_static, lines_dynamic;
std::set<std::string> lines_dynamic_updates;
std::mutex lines_dynamic_mutex;
/**
* Provides a simple default lighting configuration located at the camera using the old fixed function pipeline methods
*/
Expand Down
5 changes: 2 additions & 3 deletions src/flamegpu/visualiser/camera/NoClipCamera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ NoClipCamera::NoClipCamera(const glm::vec3 &eye)
: NoClipCamera(eye, glm::vec3(0, 0, 0)) {}
// Initialiser list written to remove any references to member variables
// Because member variables are initialised via initaliser lists in the order they are declared in the class declaration (rather than the order of the initialiser list)
NoClipCamera::NoClipCamera(const glm::vec3 &eye, const glm::vec3 &target)
NoClipCamera::NoClipCamera(const glm::vec3 &eye, const glm::vec3 &target, const float _roll)
: Camera(eye)
, pureUp(0.0f, 1.0f, 0.0f)
, look(normalize(target - eye))
Expand All @@ -27,8 +27,7 @@ NoClipCamera::NoClipCamera(const glm::vec3 &eye, const glm::vec3 &target)
// this->look = target - eye; // Look is the direction from eye to target
// this->right = cross(look, pureUp); // Right is perpendicular to look and pureUp
// this->up = cross(right, look); // Up is perpendicular to right and look

this->updateViews();
this->roll(_roll);
}
NoClipCamera::~NoClipCamera() {
}
Expand Down
3 changes: 2 additions & 1 deletion src/flamegpu/visualiser/camera/NoClipCamera.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ class NoClipCamera : public Camera {
* Initialises the camera located at eye directed at target
* @param eye The coordinates the camera is located
* @param target The coordinates the camera is directed towards
* @param _roll The camera roll in radians
*/
NoClipCamera(const glm::vec3 &eye, const glm::vec3 &target);
NoClipCamera(const glm::vec3 &eye, const glm::vec3 &target, float _roll = 0);
/**
* Default destructor
*/
Expand Down
3 changes: 3 additions & 0 deletions src/flamegpu/visualiser/config/ModelConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ModelConfig::ModelConfig(const char* _windowTitle)
, fpsColor{1, 1, 1}
, cameraLocation{1.5f, 1.5f, 1.5f}
, cameraTarget{0, 0, 0}
, cameraRoll(0)
, cameraSpeed{0.05f, 5}
, nearFarClip{0.05f, 5000}
, stepVisible(true)
Expand All @@ -39,13 +40,15 @@ ModelConfig &ModelConfig::operator=(const ModelConfig &other) {
memcpy(fpsColor, other.fpsColor, sizeof(fpsColor));
memcpy(cameraLocation, other.cameraLocation, sizeof(cameraLocation));
memcpy(cameraTarget, other.cameraTarget, sizeof(cameraTarget));
cameraRoll = other.cameraRoll;
memcpy(cameraSpeed, other.cameraSpeed, sizeof(cameraSpeed));
memcpy(nearFarClip, other.nearFarClip, sizeof(nearFarClip));
stepVisible = other.stepVisible;
beginPaused = other.beginPaused;
isPython = other.isPython;
isOrtho = other.isOrtho;
orthoZoom = other.orthoZoom;
dynamic_lines = other.dynamic_lines; // Here because they are probably empty at construction, so addLine() can't be used
// staticModels
// lines
// panels
Expand Down
Loading