287 lines
No EOL
8.8 KiB
C++
287 lines
No EOL
8.8 KiB
C++
// This source code is property of the Computer Graphics and Visualization
|
|
// chair of the TU Dresden. Do not distribute!
|
|
// Copyright (C) CGV TU Dresden - All Rights Reserved
|
|
|
|
#include "Viewer.h"
|
|
|
|
#include <nanogui/window.h>
|
|
#include <nanogui/button.h>
|
|
#include <nanogui/checkbox.h>
|
|
#include <nanogui/messagedialog.h>
|
|
#include <nanogui/layout.h>
|
|
#include <nanogui/combobox.h>
|
|
|
|
#include <OpenMesh/Core/IO/MeshIO.hh>
|
|
|
|
#include <iostream>
|
|
|
|
#include <gui/SliderHelper.h>
|
|
|
|
#include <chrono>
|
|
|
|
#include <gui/ShaderPool.h>
|
|
#include "GridTraverser.h"
|
|
|
|
Viewer::Viewer()
|
|
: AbstractViewer("CG1 Exercise 3"),
|
|
renderer(polymesh),
|
|
closestPositions(nse::gui::VertexBuffer),
|
|
gridPositions(nse::gui::VertexBuffer),
|
|
rayPositions(nse::gui::VertexBuffer), rayCellsPositions(nse::gui::VertexBuffer)
|
|
{
|
|
SetupGUI();
|
|
|
|
closestVAO.generate();
|
|
gridVAO.generate();
|
|
rayVAO.generate();
|
|
rayCellsVAO.generate();
|
|
}
|
|
|
|
void Viewer::SetupGUI()
|
|
{
|
|
auto mainWindow = SetupMainWindow();
|
|
|
|
auto loadFileBtn = new nanogui::Button(mainWindow, "Load Mesh");
|
|
loadFileBtn->setCallback([this]() {
|
|
std::vector<std::pair<std::string, std::string>> fileTypes;
|
|
fileTypes.push_back(std::make_pair("obj", "OBJ File"));
|
|
auto file = nanogui::file_dialog(fileTypes, false);
|
|
if (!file.empty())
|
|
{
|
|
polymesh.clear();
|
|
if (!OpenMesh::IO::read_mesh(polymesh, file))
|
|
{
|
|
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Load Mesh",
|
|
"The specified file could not be loaded");
|
|
}
|
|
else
|
|
MeshUpdated();
|
|
}
|
|
});
|
|
|
|
cmbPrimitiveType = new nanogui::ComboBox(mainWindow, { "Use Vertices", "Use Edges", "Use Triangles" });
|
|
cmbPrimitiveType->setCallback([this](int) { FindClosestPoint(sldQuery->Value()); BuildGridVBO(); });
|
|
|
|
sldQuery = new nse::gui::VectorInput(mainWindow, "Query", Eigen::Vector3f::Zero(), Eigen::Vector3f::Zero(), Eigen::Vector3f::Zero(), [this](const Eigen::Vector3f& p) { FindClosestPoint(p); });
|
|
|
|
sldRayOrigin = new nse::gui::VectorInput(mainWindow, "Ray Origin", Eigen::Vector3f::Zero(), Eigen::Vector3f::Zero(), Eigen::Vector3f::Zero(), [this](const Eigen::Vector3f& p) { BuildRayVBOs(); });
|
|
sldRayDir = new nse::gui::VectorInput(mainWindow, "Ray Direction", Eigen::Vector3f::Constant(-1), Eigen::Vector3f::Constant(1), Eigen::Vector3f::Zero(), [this](const Eigen::Vector3f& p) { BuildRayVBOs(); });
|
|
nanogui::TextBox* txtRaySteps;
|
|
auto sldRaySteps = nse::gui::AddLabeledSlider(mainWindow, "Ray Steps", std::make_pair(1, 200), 80, txtRaySteps);
|
|
sldRaySteps->setCallback([this, txtRaySteps](float value) {
|
|
raySteps = (int)std::round(value);
|
|
txtRaySteps->setValue(std::to_string(raySteps));
|
|
BuildRayVBOs();
|
|
});
|
|
sldRaySteps->callback()(sldRaySteps->value());
|
|
|
|
chkRenderMesh = new nanogui::CheckBox(mainWindow, "Render Mesh"); chkRenderMesh->setChecked(true);
|
|
chkRenderGrid = new nanogui::CheckBox(mainWindow, "Render Non-Empty Grid Cells"); chkRenderGrid->setChecked(false);
|
|
chkRenderRay = new nanogui::CheckBox(mainWindow, "Render Ray"); chkRenderRay->setChecked(false);
|
|
|
|
shadingBtn = new nanogui::ComboBox(mainWindow, { "Smooth Shading", "Flat Shading" });
|
|
|
|
performLayout();
|
|
}
|
|
|
|
void Viewer::FindClosestPoint(const Eigen::Vector3f& p)
|
|
{
|
|
if (polymesh.vertices_empty())
|
|
return;
|
|
Eigen::Vector3f closest;
|
|
auto timeStart = std::chrono::high_resolution_clock::now();
|
|
switch (cmbPrimitiveType->selectedIndex())
|
|
{
|
|
case Vertex:
|
|
closest = vertexTree.ClosestPoint(p);
|
|
break;
|
|
case Edge:
|
|
closest = edgeTree.ClosestPoint(p);
|
|
break;
|
|
case Tri:
|
|
closest = triangleTree.ClosestPoint(p);
|
|
break;
|
|
}
|
|
auto timeEnd = std::chrono::high_resolution_clock::now();
|
|
std::cout << std::fixed << "Closest point query took " << std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeStart).count() << " microseconds." << std::endl;
|
|
|
|
Eigen::Matrix4Xf points(4, 2);
|
|
points.block<3, 1>(0, 0) = p;
|
|
points.block<3, 1>(0, 1) = closest;
|
|
points.row(3).setConstant(1);
|
|
|
|
closestVAO.bind();
|
|
ShaderPool::Instance()->simpleShader.bind();
|
|
closestPositions.uploadData(points).bindToAttribute("position");
|
|
closestVAO.unbind();
|
|
}
|
|
|
|
void Viewer::MeshUpdated()
|
|
{
|
|
//calculate the bounding Box of the mesh
|
|
nse::math::BoundingBox<float, 3> bbox;
|
|
for (auto v : polymesh.vertices())
|
|
bbox.expand(ToEigenVector(polymesh.point(v)));
|
|
camera().FocusOnBBox(bbox);
|
|
bboxMaxLength = bbox.diagonal().maxCoeff();
|
|
|
|
polymesh.triangulate();
|
|
|
|
BuildAABBTreeFromVertices(polymesh, vertexTree);
|
|
BuildAABBTreeFromEdges(polymesh, edgeTree);
|
|
BuildAABBTreeFromTriangles(polymesh, triangleTree);
|
|
|
|
Eigen::Vector3f cellSize = Eigen::Vector3f::Constant(bbox.diagonal().maxCoeff() / 50);
|
|
BuildHashGridFromVertices(polymesh, vertexGrid, cellSize);
|
|
BuildHashGridFromEdges(polymesh, edgeGrid, cellSize);
|
|
BuildHashGridFromTriangles(polymesh, triangleGrid, cellSize);
|
|
|
|
sldQuery->SetBounds(bbox.min, bbox.max);
|
|
sldQuery->SetValue(bbox.max);
|
|
FindClosestPoint(bbox.max);
|
|
|
|
sldRayOrigin->SetBounds(bbox.min, bbox.max);
|
|
sldRayOrigin->SetValue(bbox.min);
|
|
sldRayDir->SetValue(bbox.diagonal().normalized());
|
|
|
|
BuildGridVBO();
|
|
|
|
renderer.Update();
|
|
}
|
|
|
|
void Viewer::BuildGridVBO()
|
|
{
|
|
switch (cmbPrimitiveType->selectedIndex())
|
|
{
|
|
case Vertex:
|
|
BuildGridVBO(vertexGrid);
|
|
break;
|
|
case Edge:
|
|
BuildGridVBO(edgeGrid);
|
|
break;
|
|
case Tri:
|
|
BuildGridVBO(triangleGrid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Viewer::BuildRayVBOs()
|
|
{
|
|
if (polymesh.vertices_empty())
|
|
return;
|
|
|
|
//Ray line indicator
|
|
Eigen::Matrix4Xf rayPoints(4, 2);
|
|
rayPoints.block<3, 1>(0, 0) = sldRayOrigin->Value();
|
|
rayPoints.block<3, 1>(0, 1) = sldRayOrigin->Value() + sldRayDir->Value().normalized() * 0.2f * bboxMaxLength;
|
|
rayPoints.row(3).setConstant(1);
|
|
|
|
rayVAO.bind();
|
|
ShaderPool::Instance()->simpleShader.bind();
|
|
rayPositions.uploadData(rayPoints).bindToAttribute("position");
|
|
rayVAO.unbind();
|
|
|
|
//Ray cells
|
|
std::vector<Eigen::Vector4f> cellPositions;
|
|
GridTraverser trav(sldRayOrigin->Value(), sldRayDir->Value(), vertexGrid.CellExtents());
|
|
for (int i = 0; i < raySteps; ++i, trav++)
|
|
{
|
|
auto bounds = vertexGrid.CellBounds(*trav);
|
|
AddBoxVertices(bounds, cellPositions);
|
|
}
|
|
|
|
rayCellsVAO.bind();
|
|
rayCellsPositions.uploadData(cellPositions).bindToAttribute("position");
|
|
rayCellsVAO.unbind();
|
|
rayCellsIndices = (GLuint)cellPositions.size();
|
|
}
|
|
|
|
void Viewer::AddBoxVertices(const Box & box, std::vector<Eigen::Vector4f>& positions)
|
|
{
|
|
auto& lb = box.LowerBound();
|
|
auto& ub = box.UpperBound();
|
|
Eigen::Vector4f o; o << lb, 1.0f;
|
|
Eigen::Vector4f x; x << ub.x() - lb.x(), 0, 0, 0;
|
|
Eigen::Vector4f y; y << 0, ub.y() - lb.y(), 0, 0;
|
|
Eigen::Vector4f z; z << 0, 0, ub.z() - lb.z(), 0;
|
|
positions.push_back(o);
|
|
positions.push_back(o + x);
|
|
positions.push_back(o + x);
|
|
positions.push_back(o + x + y);
|
|
positions.push_back(o + x + y);
|
|
positions.push_back(o + y);
|
|
positions.push_back(o + y);
|
|
positions.push_back(o);
|
|
|
|
positions.push_back(o + z);
|
|
positions.push_back(o + z + x);
|
|
positions.push_back(o + z + x);
|
|
positions.push_back(o + z + x + y);
|
|
positions.push_back(o + z + x + y);
|
|
positions.push_back(o + z + y);
|
|
positions.push_back(o + z + y);
|
|
positions.push_back(o + z);
|
|
|
|
positions.push_back(o);
|
|
positions.push_back(o + z);
|
|
positions.push_back(o + x);
|
|
positions.push_back(o + x + z);
|
|
positions.push_back(o + y);
|
|
positions.push_back(o + y + z);
|
|
positions.push_back(o + x + y);
|
|
positions.push_back(o + x + y + z);
|
|
}
|
|
|
|
void Viewer::drawContents()
|
|
{
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
if (!polymesh.vertices_empty())
|
|
{
|
|
Eigen::Matrix4f view, proj;
|
|
camera().ComputeCameraMatrices(view, proj);
|
|
Eigen::Matrix4f mvp = proj * view;
|
|
|
|
if(chkRenderMesh->checked())
|
|
renderer.Render(view, proj, shadingBtn->selectedIndex() == 1);
|
|
|
|
ShaderPool::Instance()->simpleShader.bind();
|
|
ShaderPool::Instance()->simpleShader.setUniform("mvp", mvp);
|
|
|
|
//Draw line between query point and its closest position
|
|
closestVAO.bind();
|
|
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(1, 1, 1, 1));
|
|
glDrawArrays(GL_LINES, 0, 2);
|
|
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(1, 0, 0, 1));
|
|
glPointSize(3.0f);
|
|
glDrawArrays(GL_POINTS, 0, 2);
|
|
closestVAO.unbind();
|
|
|
|
//Draw non-empty grid cells
|
|
if (gridIndices > 0 && chkRenderGrid->checked())
|
|
{
|
|
gridVAO.bind();
|
|
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(0.2f, 0.2f, 0.2f, 1));
|
|
glDrawArrays(GL_LINES, 0, gridIndices);
|
|
gridVAO.unbind();
|
|
}
|
|
|
|
if (chkRenderRay->checked())
|
|
{
|
|
//Draw line for ray
|
|
rayVAO.bind();
|
|
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(1, 1, 1, 1));
|
|
glDrawArrays(GL_LINES, 0, 2);
|
|
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(0, 1, 0, 1));
|
|
glPointSize(3.0f);
|
|
glDrawArrays(GL_POINTS, 0, 1);
|
|
rayVAO.unbind();
|
|
|
|
//Draw ray cells
|
|
rayCellsVAO.bind();
|
|
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(0.0f, 0.8f, 0.0f, 1));
|
|
glDrawArrays(GL_LINES, 0, rayCellsIndices);
|
|
rayCellsVAO.unbind();
|
|
}
|
|
}
|
|
} |