Skip to content

Commit

Permalink
Add clicking to select Nodes
Browse files Browse the repository at this point in the history
See: #15
  • Loading branch information
bpe2 committed Jul 7, 2022
1 parent e4cfd5f commit 42dc9de
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 1 deletion.
2 changes: 2 additions & 0 deletions resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@
<file>shaders/model.frag</file>
<file>shaders/skybox.vert</file>
<file>shaders/skybox.frag</file>
<file>shaders/picking.frag</file>
<file>shaders/picking.vert</file>
</qresource>
</RCC>
14 changes: 14 additions & 0 deletions shaders/picking.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#version 330

uniform uint object_type;
uniform uint object_id;

out uvec3 picking_fragment;

void main() {
// The leading 1.0 is to indicate the presence of an object
// in the texture, since by default, OpenGL will, clear to 0
// so, we need some way to tell an object was rendered at this
// fragment
picking_fragment = uvec3(1.0, object_type, object_id);
}
11 changes: 11 additions & 0 deletions shaders/picking.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#version 330

layout (location = 0) in vec3 in_position;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main() {
gl_Position = projection * view * model * vec4(in_position, 1.0);
}
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ target_sources(netsimulyzer PRIVATE
group/node/Node.h group/node/Node.cpp
group/node/TrailBuffer.h group/node/TrailBuffer.cpp
render/camera/Camera.h render/camera/Camera.cpp
render/framebuffer/PickingFramebuffer.h render/framebuffer/PickingFramebuffer.cpp
render/helper/Floor.h render/helper/Floor.cpp
render/Light.h
render/material/material.h
Expand Down
92 changes: 92 additions & 0 deletions src/render/framebuffer/PickingFramebuffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* NIST-developed software is provided by NIST as a public service. You may use,
* copy and distribute copies of the software in any medium, provided that you
* keep intact this entire notice. You may improve,modify and create derivative
* works of the software or any portion of the software, and you may copy and
* distribute such modifications or works. Modified works should carry a notice
* stating that you changed the software and should note the date and nature of
* any such change. Please explicitly acknowledge the National Institute of
* Standards and Technology as the source of the software.
*
* NIST-developed software is expressly provided "AS IS." NIST MAKES NO
* WARRANTY OF ANY KIND, EXPRESS, IMPLIED, IN FACT OR ARISING BY OPERATION OF
* LAW, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY. NIST NEITHER REPRESENTS NOR WARRANTS THAT THE
* OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT
* ANY DEFECTS WILL BE CORRECTED. NIST DOES NOT WARRANT OR MAKE ANY
* REPRESENTATIONS REGARDING THE USE OF THE SOFTWARE OR THE RESULTS THEREOF,
* INCLUDING BUT NOT LIMITED TO THE CORRECTNESS, ACCURACY, RELIABILITY,
* OR USEFULNESS OF THE SOFTWARE.
*
* You are solely responsible for determining the appropriateness of using and
* distributing the software and you assume all risks associated with its use,
* including but not limited to the risks and costs of program errors,
* compliance with applicable laws, damage to or loss of data, programs or
* equipment, and the unavailability or interruption of operation. This
* software is not intended to be used in any situation where a failure could
* cause risk of injury or damage to property. The software developed by NIST
* employees is not subject to copyright protection within the United States.
*
* Author: Evan Black <[email protected]>
*/

#include "PickingFramebuffer.h"
#include <QOpenGLFunctions_3_3_Core>
namespace netsimulyzer {

void PickingFramebuffer::generate(int width, int height) {
openGl.glGenFramebuffers(1, &fbo);
bind(GL_FRAMEBUFFER);

openGl.glGenTextures(1, &idTexture);
openGl.glBindTexture(GL_TEXTURE_2D, idTexture);
openGl.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32UI, width, height, 0, GL_RGB_INTEGER, GL_UNSIGNED_INT, nullptr);
openGl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
openGl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
openGl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, idTexture, 0);

openGl.glGenTextures(1, &depthTexture);
openGl.glBindTexture(GL_TEXTURE_2D, depthTexture);
openGl.glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
openGl.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
}

// NOLINT(cppcoreguidelines-pro-type-member-init)
PickingFramebuffer::PickingFramebuffer(QOpenGLFunctions_3_3_Core &openGl, int width, int height)
: openGl(openGl) { // NOLINT(cppcoreguidelines-pro-type-member-init)
generate(width, height);
}

PickingFramebuffer::~PickingFramebuffer() {
openGl.glDeleteTextures(1, &idTexture);
openGl.glDeleteTextures(1, &depthTexture);
openGl.glDeleteFramebuffers(1, &fbo);
}

void PickingFramebuffer::bind(GLenum mode) const {
openGl.glBindFramebuffer(mode, fbo);
}

void PickingFramebuffer::unbind(GLenum mode, unsigned int defaultFbo) const {
openGl.glBindFramebuffer(mode, defaultFbo);
}

PickingFramebuffer::PixelInfo PickingFramebuffer::read(int x, int y) const {
bind(GL_READ_FRAMEBUFFER);
openGl.glReadBuffer(GL_COLOR_ATTACHMENT0);

PixelInfo pixelInfo; // NOLINT(cppcoreguidelines-pro-type-member-init)
openGl.glReadPixels(x, y, 1, 1, GL_RGB_INTEGER, GL_UNSIGNED_INT, &pixelInfo);

return pixelInfo;
}

void PickingFramebuffer::resize(int width, int height) {
openGl.glDeleteTextures(1, &idTexture);
openGl.glDeleteTextures(1, &depthTexture);
openGl.glDeleteFramebuffers(1, &fbo);
generate(width, height);
}

} // namespace netsimulyzer
72 changes: 72 additions & 0 deletions src/render/framebuffer/PickingFramebuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* NIST-developed software is provided by NIST as a public service. You may use,
* copy and distribute copies of the software in any medium, provided that you
* keep intact this entire notice. You may improve,modify and create derivative
* works of the software or any portion of the software, and you may copy and
* distribute such modifications or works. Modified works should carry a notice
* stating that you changed the software and should note the date and nature of
* any such change. Please explicitly acknowledge the National Institute of
* Standards and Technology as the source of the software.
*
* NIST-developed software is expressly provided "AS IS." NIST MAKES NO
* WARRANTY OF ANY KIND, EXPRESS, IMPLIED, IN FACT OR ARISING BY OPERATION OF
* LAW, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
* AND DATA ACCURACY. NIST NEITHER REPRESENTS NOR WARRANTS THAT THE
* OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE, OR THAT
* ANY DEFECTS WILL BE CORRECTED. NIST DOES NOT WARRANT OR MAKE ANY
* REPRESENTATIONS REGARDING THE USE OF THE SOFTWARE OR THE RESULTS THEREOF,
* INCLUDING BUT NOT LIMITED TO THE CORRECTNESS, ACCURACY, RELIABILITY,
* OR USEFULNESS OF THE SOFTWARE.
*
* You are solely responsible for determining the appropriateness of using and
* distributing the software and you assume all risks associated with its use,
* including but not limited to the risks and costs of program errors,
* compliance with applicable laws, damage to or loss of data, programs or
* equipment, and the unavailability or interruption of operation. This
* software is not intended to be used in any situation where a failure could
* cause risk of injury or damage to property. The software developed by NIST
* employees is not subject to copyright protection within the United States.
*
* Author: Evan Black <[email protected]>
*/
#pragma once
#include <QOpenGLFunctions_3_3_Core>

namespace netsimulyzer {

class PickingFramebuffer {
QOpenGLFunctions_3_3_Core &openGl;
unsigned int fbo;
unsigned int idTexture;
unsigned int depthTexture;

void generate(int width, int height);

public:
struct PixelInfo {
unsigned int object;
unsigned int type;
unsigned int id;
};

PickingFramebuffer(QOpenGLFunctions_3_3_Core &openGl, int width, int height);
~PickingFramebuffer();

// Disallow copying
PickingFramebuffer(const PickingFramebuffer &) = delete;
PickingFramebuffer &operator=(const PickingFramebuffer &) = delete;

void bind(GLenum mode) const;
void unbind(GLenum mode, unsigned int defaultFbo) const;

[[nodiscard]] PixelInfo read(int x, int y) const;

inline unsigned int getIds() {
return idTexture;
}

void resize(int width, int height);
};

} // namespace netsimulyzer
8 changes: 8 additions & 0 deletions src/render/model/ModelCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,14 @@ void ModelRenderInfo::clear() {
meshes.clear();
}

std::vector<Mesh> &ModelRenderInfo::getMeshes() {
return meshes;
}

std::vector<Mesh> &ModelRenderInfo::getTransparentMeshes() {
return transparentMeshes;
}

ModelCache::ModelCache(TextureCache &textureCache) : textureCache(textureCache) {
}

Expand Down
2 changes: 2 additions & 0 deletions src/render/model/ModelCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ class ModelRenderInfo : protected QOpenGLFunctions_3_3_Core {

void render(Shader &s, const Model &model);
void renderTransparent(Shader &s, const Model &model);
std::vector<Mesh>& getMeshes();
std::vector<Mesh>& getTransparentMeshes();
void clear();
};

Expand Down
22 changes: 22 additions & 0 deletions src/render/renderer/Renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ void Renderer::init() {

initShader(modelShader, ":shader/shaders/model.vert", ":shader/shaders/model.frag");
initShader(skyBoxShader, ":shader/shaders/skybox.vert", ":shader/shaders/skybox.frag");
initShader(pickingShader, ":/shader/shaders/picking.vert", ":/shader/shaders/picking.frag");
}

void Renderer::setPerspective(const glm::mat4 &perspective) {
Expand All @@ -88,6 +89,7 @@ void Renderer::setPerspective(const glm::mat4 &perspective) {
gridShader.uniform("projection", perspective);
modelShader.uniform("projection", perspective);
skyBoxShader.uniform("projection", perspective);
pickingShader.uniform("projection", perspective);
}

void Renderer::setPointLightCount(unsigned int count) {
Expand Down Expand Up @@ -539,6 +541,8 @@ void Renderer::use(const Camera &cam) {
auto noTranslationView = cam.view_matrix();
noTranslationView[3] = {0.0f, 0.0f, 0.0f, 1.0f};
skyBoxShader.uniform("view", noTranslationView);

pickingShader.uniform("view", cam.view_matrix());
}

void Renderer::render(const DirectionalLight &light) {
Expand Down Expand Up @@ -712,4 +716,22 @@ void Renderer::render(const std::vector<WiredLink> &wiredLinks) {
glDisable(GL_LINE_SMOOTH);
}

void Renderer::renderPickingNode(unsigned int nodeId, const Model &m) {
auto &model = modelCache.get(m.getModelId());

pickingShader.uniform("model", m.getModelMatrix());
pickingShader.uniform("object_id", nodeId);
pickingShader.uniform("object_type", 1u);

pickingShader.bind();
auto &meshes = model.getMeshes();
for (auto &mesh : meshes) {
mesh.render();
}
auto &transparentMeshes = model.getTransparentMeshes();
for (auto &mesh : transparentMeshes) {
mesh.render();
}
}

} // namespace netsimulyzer
3 changes: 3 additions & 0 deletions src/render/renderer/Renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class Renderer : protected QOpenGLFunctions_3_3_Core {
Shader gridShader;
Shader modelShader;
Shader skyBoxShader;
Shader pickingShader;

void initShader(Shader &s, const QString &vertexPath, const QString &fragmentPath);

Expand Down Expand Up @@ -89,6 +90,8 @@ class Renderer : protected QOpenGLFunctions_3_3_Core {
void startTransparent();
void endTransparent();

void renderPickingNode(unsigned int nodeId, const Model &m);

void use(const Camera &cam);
void render(const DirectionalLight &light);
void render(const PointLight &light);
Expand Down
38 changes: 37 additions & 1 deletion src/window/scene/SceneWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ void SceneWidget::initializeGL() {

updatePerspective();

// picking FBO
pickingFbo = std::make_unique<PickingFramebuffer>(openGl, width(), height());
pickingFbo->unbind(GL_FRAMEBUFFER, defaultFramebufferObject());

// Cheap hack to get Qt to repaint at a reasonable rate
// Seems to only work with the old connect syntax
QObject::connect(&timer, SIGNAL(timeout()), this, SLOT(update()));
Expand All @@ -225,9 +229,25 @@ void SceneWidget::paintGL() {
handleUndoEvents();
}

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // NOLINT(hicpp-signed-bitwise)
// Picking
pickingFbo->bind(GL_FRAMEBUFFER);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

for (auto &[key, node] : nodes) {
if (!node.visible())
continue;
renderer.renderPickingNode(node.getNs3Model().id, node.getModel());
}

glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
// end Picking

camera.move(static_cast<float>(frameTimer.elapsed()));
renderer.use(camera);

glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (renderSkybox)
renderer.render(*skyBox);

Expand Down Expand Up @@ -318,6 +338,7 @@ void SceneWidget::paintGL() {
void SceneWidget::resizeGL(int w, int h) {
updatePerspective();
glViewport(0, 0, w, h);
pickingFbo->resize(w, h);
}

void SceneWidget::keyPressEvent(QKeyEvent *event) {
Expand All @@ -332,6 +353,21 @@ void SceneWidget::keyReleaseEvent(QKeyEvent *event) {

void SceneWidget::mousePressEvent(QMouseEvent *event) {
QWidget::mousePressEvent(event);

makeCurrent();

// OpenGL starts from the bottom left,
// Qt Starts at the top left,
// so adjust the Y coordinate accordingly
const auto selected = pickingFbo->read(event->x(), height() - event->y());
pickingFbo->unbind(GL_READ_FRAMEBUFFER, defaultFramebufferObject());
doneCurrent();

if (selected.object && selected.type == 1u) {
emit nodeSelected(selected.id);
return;
}

if (!camera.mouseControlsEnabled())
return;

Expand Down
4 changes: 4 additions & 0 deletions src/window/scene/SceneWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "../../settings/SettingsManager.h"
#include "../../util/undo-events.h"
#include "src/group/link/WiredLink.h"
#include "src/render/framebuffer/PickingFramebuffer.h"
#include "src/render/helper/CoordinateGrid.h"
#include "src/render/helper/SkyBox.h"
#include <QApplication>
Expand Down Expand Up @@ -93,6 +94,8 @@ class SceneWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core {
bool renderBuildingOutlines = settings.get<bool>(SettingsManager::Key::RenderBuildingOutlines).value();
bool renderMotionTrails = settings.get<bool>(SettingsManager::Key::RenderMotionTrails).value();

std::unique_ptr<PickingFramebuffer> pickingFbo;

DirectionalLight mainLight;
std::unique_ptr<SkyBox> skyBox;
std::unique_ptr<Floor> floor;
Expand Down Expand Up @@ -242,5 +245,6 @@ class SceneWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core {
void timeChanged(parser::nanoseconds simulationTime, parser::nanoseconds increment);
void paused();
void playing();
void nodeSelected(unsigned int nodeId);
};
} // namespace netsimulyzer

0 comments on commit 42dc9de

Please sign in to comment.