Initial commit

This commit is contained in:
Nico Schertler 2018-09-06 14:35:43 +02:00
parent d01a89cd94
commit 815933c4fa
82 changed files with 6435 additions and 1 deletions

6
.gitmodules vendored Normal file
View file

@ -0,0 +1,6 @@
[submodule "ext/OpenMesh"]
path = ext/OpenMesh
url = https://www.graphics.rwth-aachen.de:9000/OpenMesh/OpenMesh.git
[submodule "ext/nanogui"]
path = ext/nanogui
url = https://github.com/NSchertler/nanogui

View file

@ -0,0 +1,83 @@
FUNCTION(PREPEND var prefix)
SET(listVar "")
FOREACH(f ${ARGN})
LIST(APPEND listVar "${prefix}/${f}")
ENDFOREACH(f)
SET(${var} "${listVar}" PARENT_SCOPE)
ENDFUNCTION(PREPEND)
FUNCTION(JOIN VALUES GLUE OUTPUT)
string (REGEX REPLACE "([^\\]|^);" "\\1${GLUE}" _TMP_STR "${VALUES}")
string (REGEX REPLACE "[\\](.)" "\\1" _TMP_STR "${_TMP_STR}") #fixes escaping
set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE)
ENDFUNCTION()
FUNCTION(ProcessGLSLFiles GLSL_FILES_VAR)
set(GLSL_FILES ${${GLSL_FILES_VAR}})
PREPEND(GLSL_FILES "${CMAKE_CURRENT_SOURCE_DIR}/glsl/" ${GLSL_FILES})
set(${GLSL_FILES_VAR} ${GLSL_FILES} PARENT_SCOPE)
source_group(glsl FILES ${GLSL_FILES} glsl.cpp glsl.h)
JOIN("${GLSL_FILES}" "," glsl_string)
set(bin2c_cmdline
-DOUTPUT_C=glsl.cpp
-DOUTPUT_H=glsl.h
"-DINPUT_FILES=${glsl_string}"
-P "${NANOGUI_DIR}/resources/bin2c.cmake")
add_custom_command(
OUTPUT glsl.cpp glsl.h
COMMAND ${CMAKE_COMMAND} ARGS ${bin2c_cmdline}
DEPENDS ${GLSL_FILES}
COMMENT "Running bin2c"
PRE_BUILD VERBATIM)
ENDFUNCTION()
FUNCTION(SetupBuildEnvironment)
# Enable folders for projects in Visual Studio
if (CMAKE_GENERATOR MATCHES "Visual Studio")
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
# Sanitize build environment for static build with C++11
if (MSVC)
add_definitions (/D "_CRT_SECURE_NO_WARNINGS")
add_definitions (/D "__TBB_NO_IMPLICIT_LINKAGE")
add_definitions (-DNOMINMAX )
add_definitions(/D_USE_MATH_DEFINES)
# Parallel build on MSVC (all targets)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:SSE2")
# Disable Eigen vectorization for Windows 32 bit builds (issues with unaligned access segfaults)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DEIGEN_DONT_ALIGN")
endif()
# Static build
set(CompilerFlags
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)
foreach(CompilerFlag ${CompilerFlags})
string(REPLACE "/MD" "/MT" ${CompilerFlag} "${${CompilerFlag}}")
endforeach()
elseif(APPLE)
# Try to auto-detect a suitable SDK
execute_process(COMMAND bash -c "xcodebuild -version -sdk | grep MacOSX | grep Path | head -n 1 | cut -f 2 -d ' '" OUTPUT_VARIABLE CMAKE_OSX_SYSROOT)
string(REGEX REPLACE "(\r?\n)+$" "" CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT}")
string(REGEX REPLACE "^.*X([0-9.]*).sdk$" "\\1" CMAKE_OSX_DEPLOYMENT_TARGET "${CMAKE_OSX_SYSROOT}")
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations -Wno-unused-result")
endif()
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} PARENT_SCOPE)
set (CMAKE_CXX_STANDARD 14 PARENT_SCOPE)
ENDFUNCTION()

49
CMakeLists.txt Normal file
View file

@ -0,0 +1,49 @@
cmake_minimum_required (VERSION 3.1)
project(CG1)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake/Modules/")
include(CG1Helper)
set(LIBS)
SetupBuildEnvironment()
# Add NanoGUI
set(NANOGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/nanogui)
set(NANOGUI_BUILD_EXAMPLE OFF CACHE BOOL " " FORCE)
set(NANOGUI_BUILD_SHARED OFF CACHE BOOL " " FORCE)
set(NANOGUI_BUILD_PYTHON OFF CACHE BOOL " " FORCE)
set(NANOGUI_USE_GLAD ON CACHE BOOL " " FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/nanogui ext_build/nanogui)
set_property(TARGET nanogui nanogui-obj glfw glfw_objects PROPERTY FOLDER "dependencies")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/ext/nanogui/include
${NANOGUI_EXTRA_INCS})
set(LIBS ${LIBS} nanogui ${NANOGUI_EXTRA_LIBS})
add_definitions(${NANOGUI_EXTRA_DEFS})
# Add OpenMesh
set(BUILD_APPS OFF CACHE BOOL " " FORCE)
set(OPENMESH_BUILD_PYTHON_BINDINGS OFF CACHE BOOL " " FORCE)
set(OPENMESH_BUILD_SHARED OFF CACHE BOOL " ")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ext/OpenMesh ext_build/OpenMesh)
if(TARGET OpenMeshCoreStatic)
set(LIBS ${LIBS} OpenMeshCoreStatic)
set_property(TARGET OpenMeshCoreStatic OpenMeshToolsStatic PROPERTY FOLDER "dependencies")
set_property(TARGET OpenMeshCore OpenMeshToolsStatic PROPERTY EXCLUDE_FROM_ALL TRUE)
else()
set(LIBS ${LIBS} OpenMeshCore)
endif()
include_directories(ext/OpenMesh/src)
set_property(TARGET OpenMeshCore OpenMeshTools PROPERTY FOLDER "dependencies")
set_property(TARGET OpenMeshTools PROPERTY EXCLUDE_FROM_ALL TRUE)
add_subdirectory(common)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common/include)
add_subdirectory(exercise1)
add_subdirectory(exercise2)
add_subdirectory(exercise3)
add_subdirectory(exercise4)

View file

@ -1,2 +1,20 @@
# cg1 # Computer Graphics 1 - Exercises
This repository contains the source code and data sets for the CG1 exercises. To download and compile the project, you need to do the following steps:
1. Clone the project to your local computer
2. Run CMake to generate a project file for your build platform. Alternatively, you can use a development environment that supports CMake natively and open the directory directly.
3. Open the project file and compile.
Under Unix, you could employ the command line like follows:
git clone https://bitbucket.org/cgvtud/cg1.git --recursive
cd cg1
mkdir build
cd build
cmake ..
make
After this approach, you find the compiled applications under `cg1/build/bin`.
On Windows / Visual Studio, you can use the CMake GUI. After cloning the repository, open the CMake GUI and set *Where is the source code* to the cloned directory. Set *Where to build the binaries* to a new subfolder `build`. Click *Configure* and select the desired Visual Studio Version. Then, click *Configure* and *Open Project* to generate and open the project in Visual studio. Finally, choose the desired startup project (Exercise1-4) and compile and run.

21
common/CMakeLists.txt Normal file
View file

@ -0,0 +1,21 @@
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR})
set(GLSL_FILES mesh.vert mesh.frag
simple.vert simple.frag)
ProcessGLSLFiles(GLSL_FILES)
add_library(CG1Common STATIC
src/gui/AbstractViewer.cpp
src/gui/Camera.cpp
src/gui/GLBuffer.cpp
src/gui/GLShader.cpp
src/gui/GLVertexArray.cpp
src/gui/SliderHelper.cpp
src/gui/ShaderPool.cpp
src/util/GLDebug.cpp
src/util/UnionFind.cpp
src/util/OpenMeshUtils.cpp
glsl.cpp)

40
common/glsl/mesh.frag Normal file
View file

@ -0,0 +1,40 @@
#version 130
in vec4 normalViewSpace;
in vec4 posViewSpace;
in vec4 vertexColor;
in vec2 vertexTexCoords;
out vec4 outColor;
uniform vec4 color;
uniform bool flatShading;
uniform bool perVertexColor;
uniform bool visualizeTexCoords;
void main()
{
vec3 normal = normalize(normalViewSpace.xyz);
if(flatShading)
normal = normalize(cross(dFdx(posViewSpace.xyz), dFdy(posViewSpace.xyz)));
vec3 toLight = normalize(-posViewSpace.xyz);
float diffuse = abs(dot(normal, toLight));
float specular = pow(diffuse, 20);
if(!gl_FrontFacing)
diffuse *= 0.5;
vec4 materialColor = perVertexColor ? vertexColor : color;
if(visualizeTexCoords)
{
int cellId = (int(20 * vertexTexCoords.x) + int(20 * vertexTexCoords.y));
if(cellId % 2 != 0)
materialColor.xyz *= 0.3;
}
outColor = diffuse * materialColor + specular * vec4(0.5, 0.5, 0.5, 0);
outColor.a = 1;
}

25
common/glsl/mesh.vert Normal file
View file

@ -0,0 +1,25 @@
#version 130
in vec4 position;
in vec4 normal;
in vec4 color;
in vec2 texCoords;
out vec4 normalViewSpace;
out vec4 posViewSpace;
out vec4 vertexColor;
out vec2 vertexTexCoords;
uniform mat4 view;
uniform mat4 proj;
void main()
{
posViewSpace = view * position;
normalViewSpace = view * normal;
vertexColor = color;
vertexTexCoords = texCoords;
gl_Position = proj * posViewSpace;
}

10
common/glsl/simple.frag Normal file
View file

@ -0,0 +1,10 @@
#version 130
out vec4 out_color;
uniform vec4 color;
void main()
{
out_color = color;
}

10
common/glsl/simple.vert Normal file
View file

@ -0,0 +1,10 @@
#version 130
in vec4 position;
uniform mat4 mvp;
void main()
{
gl_Position = mvp * position;
}

View file

@ -0,0 +1,66 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Nico Schertler
*/
#pragma once
#include <nanogui/screen.h>
#include "gui/Camera.h"
#include <chrono>
namespace nse {
namespace gui
{
//This base class provides basic functionality for 3D interaction.
class AbstractViewer : public nanogui::Screen
{
public:
AbstractViewer(const std::string& title, int width = 1280, int height = 800, int nSamples = 4);
bool scrollEvent(const Eigen::Vector2i & p, const Eigen::Vector2f & rel);
bool mouseButtonEvent(const Eigen::Vector2i & p, int button, bool down, int modifiers);
bool mouseMotionEvent(const Eigen::Vector2i & p, const Eigen::Vector2i & rel, int button, int modifiers);
virtual bool keyboardEvent(int key, int scancode, int action, int mods);
virtual bool resizeEvent(const Eigen::Vector2i&);
const Camera& camera() const { return _camera; }
Camera& camera() { return _camera; }
bool ctrlDown() const { return _ctrlDown; }
bool shiftDown() const { return _shiftDown; }
//returns depth buffer value
float get3DPosition(const Eigen::Vector2i& screenPos, Eigen::Vector4f& pos);
float get3DPosition(const Eigen::Vector2i& screenPos, Eigen::Vector3f& pos);
protected:
nanogui::Window* SetupMainWindow();
void CheckOpenGLSupport();
const int nSamples;
Camera _camera;
bool _ctrlDown;
bool _shiftDown;
std::chrono::high_resolution_clock::time_point lastClickTime;
int lastClickButton;
Eigen::Vector2i lastClickPosition;
virtual bool scrollHook(const Eigen::Vector2i & p, const Eigen::Vector2f & rel) { return false; }
virtual bool mouseButtonHook(const Eigen::Vector2i & p, int button, bool down, int modifiers) { return false; }
virtual bool mouseMotionHook(const Eigen::Vector2i & p, const Eigen::Vector2i & rel, int button, int modifiers) { return false; }
};
}
}

104
common/include/gui/Camera.h Normal file
View file

@ -0,0 +1,104 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Wenzel Jakob
@author Nico Schertler
*/
#pragma once
#include <nanogui/glutil.h>
#include <nanogui/widget.h>
#include "math/BoundingBox.h"
namespace nse {
namespace gui
{
//Provides a 3D camera with arcball interaction capabilities.
class Camera
{
public:
//Initialize the camera. parent is the widget onto which the scene is rendered and is only used to query its size.
Camera(const nanogui::Widget& parent);
//Computes view and projection matrix for the current camera state.
void ComputeCameraMatrices(
Eigen::Matrix4f &view,
Eigen::Matrix4f &proj,
float customAspectRatio = 0) const;
//Applies a zoom by scaling the scene. Positive values of amount increase object sizes.
void Zoom(float amount);
//Sets the extent of the scene, which is kept between znear/zfar.
void SetSceneExtent(const nse::math::BoundingBox<float, 3>& bbox);
//Translates and zooms the camera in a way that it shows the entire bounding box while keeping the orientation.
//The parameters are only estimated; it is not guaranteed that the bounding box actually fits in the viewport.
void FocusOnBBox(const nse::math::BoundingBox<float, 3>& bbox);
//Keeps the camera's rotation and viewing distance and sets the focus point to the provided location.
void FocusOnPoint(const Eigen::Vector3f& point);
//Rotates the camera, such that it becomes horizontally aligned
void MakeHorizontal();
//Rotates the camera around the current focus point by rotation. The rotation is expressed in the
//camera's local coordinate system.
void RotateAroundFocusPointLocal(const Eigen::Quaternionf& rotation);
//Rotates the camera around the current focus point by rotation. The rotation is expressed in the
//global coordinate system.
void RotateAroundFocusPointGlobal(const Eigen::Quaternionf& rotation);
//Forwarded mouse button event.
bool HandleMouseButton(const Eigen::Vector2i &p, int button, bool down, int modifiers);
//Forwarded mouse move event.
bool HandleMouseMove(const Eigen::Vector2i &p, const Eigen::Vector2i &rel, int button, int modifiers);
//Forwarded resize event.
void resize(const Eigen::Vector2i & s);
//Returns the point that the camera focuses on
const Eigen::Vector3f& GetFocusPoint() const;
struct CamParams
{
nanogui::Arcball arcball;
//bounding sphere of scene
Eigen::Vector3f sceneCenter;
float sceneRadius;
float fovy = 45.0f;
Eigen::Vector3f focusPoint = Eigen::Vector3f::Zero();
float viewDistance = 5;
};
//Returns the current camera parameters
const CamParams& saveParams() const { return params; }
//Restores the camera parameters that have been previously saved.
void restoreParams(const CamParams& params) { this->params = params; }
private:
const nanogui::Widget& parent;
enum InteractionMode
{
None,
Translate,
Rotate
} interactionMode = None;
CamParams params;
Eigen::Vector3f modelTranslation_start = Eigen::Vector3f::Zero();
Eigen::Vector2i translation_start; //mouse position on the screen where translation started
};
}
}

View file

@ -0,0 +1,105 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Wenzel Jakob
@author Nico Schertler
*/
#pragma once
#include <nanogui/glutil.h>
namespace nse {
namespace gui
{
enum GLBufferType
{
VertexBuffer,
IndexBuffer,
};
//Represents a generic OpenGL buffer
class GLBuffer
{
public:
GLBuffer(GLBufferType type);
~GLBuffer();
//Binds the buffer to the correct target slot.
//If the buffer is not generated yet, a new buffer is generated.
void bind();
//Calls the according OpenGL function; mostly used for SSBOs
void bindBufferBase(unsigned int base) const;
/// Upload data from an Eigen matrix into the buffer
template <typename Matrix>
GLBuffer& uploadData(const Matrix &M)
{
uint32_t compSize = sizeof(typename Matrix::Scalar);
GLuint glType = (GLuint)nanogui::detail::type_traits<typename Matrix::Scalar>::type;
bool integral = (bool)nanogui::detail::type_traits<typename Matrix::Scalar>::integral;
uploadData((uint32_t)M.size(), (int)M.rows(), compSize,
glType, integral, (const uint8_t *)M.data());
return *this;
}
template <typename Scalar, int Rows>
GLBuffer& uploadData(const std::vector<Eigen::Matrix<Scalar, Rows, 1>>& data)
{
uint32_t compSize = sizeof(Scalar);
GLuint glType = (GLuint)nanogui::detail::type_traits<Scalar>::type;
bool integral = (bool)nanogui::detail::type_traits<Scalar>::integral;
uploadData((uint32_t)data.size() * Rows, Rows, compSize,
glType, integral, (const uint8_t *)data.data());
return *this;
}
/// Download the data from the vertex buffer object into an Eigen matrix
template <typename Matrix>
void downloadData(Matrix &M)
{
uint32_t compSize = sizeof(typename Matrix::Scalar);
GLuint glType = (GLuint)nanogui::detail::type_traits<typename Matrix::Scalar>::type;
M.resize(dim, size / dim);
downloadData(M.size(), M.rows(), compSize, glType, (uint8_t *)M.data());
}
//Binds the vertex buffer to the provided attribute of the currently bound GLShader program.
void bindToAttribute(const std::string& attribute);
/// Return the size of this buffer in bytes
size_t bufferSize() const
{
return size;
}
void uploadData(uint32_t size, const void *data);
void uploadData(uint32_t size, int dim,
uint32_t compSize, GLuint glType, bool integral,
const uint8_t *data);
void downloadData(uint32_t size, int dim,
uint32_t compSize, GLuint glType, uint8_t *data);
protected:
GLBufferType type;
GLuint id;
GLuint glType;
GLuint dim;
GLuint compSize;
GLuint size;
bool integral;
};
}
}

View file

@ -0,0 +1,138 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Wenzel Jakob
@author Nico Schertler
*/
#pragma once
//Adapted nanogui shader
#include <nanogui/glutil.h>
#include <string>
namespace nse {
namespace gui
{
//Represents an OpenGL shader program
class GLShader
{
public:
/// Create an unitialized OpenGL shader program
GLShader()
: mVertexShader(0), mFragmentShader(0), mGeometryShader(0),
mProgramShader(0) { }
~GLShader();
/// Initialize the shader using the specified source strings
bool init(const std::string &name, const std::string &vertex_str,
const std::string &fragment_str,
const std::string &geometry_str = "",
bool failSilently = false);
#ifdef HAVE_TESSELLATION
bool initWithTessellation(const std::string &name, const std::string &vertex_str,
const std::string &tessellation_control_str,
const std::string &tessellation_eval_str,
const std::string &fragment_str,
const std::string &geometry_str = "",
bool failSilently = false);
#endif
/// Initialize the shader using the specified files on disk
bool initFromFiles(const std::string &name,
const std::string &vertex_fname,
const std::string &fragment_fname,
const std::string &geometry_fname = "");
/// Return the name of the shader
const std::string &name() const { return mName; }
/// Set a preprocessor definition
void define(const std::string &key, const std::string &value) { mDefinitions[key] = value; }
/// Select this shader for subsequent draw calls
void bind();
/// Return the handle of a named shader attribute (-1 if it does not exist)
GLint attrib(const std::string &name, bool warn = true) const;
/// Return the handle of a uniform attribute (-1 if it does not exist)
GLint uniform(const std::string &name, bool warn = true) const;
/// Initialize a uniform parameter with a 4x4 matrix (float)
template <typename T>
void setUniform(const std::string &name, const Eigen::Matrix<T, 4, 4> &mat, bool warn = true) {
glUniformMatrix4fv(uniform(name, warn), 1, GL_FALSE, mat.template cast<float>().data());
}
/// Initialize a uniform parameter with an integer value
template <typename T, typename std::enable_if<nanogui::detail::type_traits<T>::integral == 1, int>::type = 0>
void setUniform(const std::string &name, T value, bool warn = true) {
glUniform1i(uniform(name, warn), (int)value);
}
/// Initialize a uniform parameter with a floating point value
template <typename T, typename std::enable_if<nanogui::detail::type_traits<T>::integral == 0, int>::type = 0>
void setUniform(const std::string &name, T value, bool warn = true) {
glUniform1f(uniform(name, warn), (float)value);
}
/// Initialize a uniform parameter with a 2D vector (int)
template <typename T, typename std::enable_if<nanogui::detail::type_traits<T>::integral == 1, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 2, 1> &v, bool warn = true) {
glUniform2i(uniform(name, warn), (int)v.x(), (int)v.y());
}
/// Initialize a uniform parameter with a 2D vector (float)
template <typename T, typename std::enable_if<nanogui::detail::type_traits<T>::integral == 0, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 2, 1> &v, bool warn = true) {
glUniform2f(uniform(name, warn), (float)v.x(), (float)v.y());
}
/// Initialize a uniform parameter with a 3D vector (int)
template <typename T, typename std::enable_if<nanogui::detail::type_traits<T>::integral == 1, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 3, 1> &v, bool warn = true) {
glUniform3i(uniform(name, warn), (int)v.x(), (int)v.y(), (int)v.z());
}
/// Initialize a uniform parameter with a 3D vector (float)
template <typename T, typename std::enable_if<nanogui::detail::type_traits<T>::integral == 0, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 3, 1> &v, bool warn = true) {
glUniform3f(uniform(name, warn), (float)v.x(), (float)v.y(), (float)v.z());
}
/// Initialize a uniform parameter with a 4D vector (int)
template <typename T, typename std::enable_if<nanogui::detail::type_traits<T>::integral == 1, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 4, 1> &v, bool warn = true) {
glUniform4i(uniform(name, warn), (int)v.x(), (int)v.y(), (int)v.z(), (int)v.w());
}
/// Initialize a uniform parameter with a 4D vector (float)
template <typename T, typename std::enable_if<nanogui::detail::type_traits<T>::integral == 0, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 4, 1> &v, bool warn = true) {
glUniform4f(uniform(name, warn), (float)v.x(), (float)v.y(), (float)v.z(), (float)v.w());
}
//Returns the shader program that has been bound last.
static GLShader* currentProgram();
protected:
static GLShader* _currentProgram;
std::string mName;
GLuint mVertexShader;
GLuint mTessellationControlShader;
GLuint mTessellationEvalShader;
GLuint mFragmentShader;
GLuint mGeometryShader;
GLuint mProgramShader;
std::map<std::string, std::string> mDefinitions;
};
}
}

View file

@ -0,0 +1,40 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Nico Schertler
*/
#pragma once
#include <nanogui/opengl.h>
namespace nse {
namespace gui
{
//Represents an OpenGL Vertex Array Object
class GLVertexArray
{
public:
GLVertexArray();
~GLVertexArray();
//Generates the VAO if it has not been generated yet.
void generate();
//Binds the VAO.
void bind() const;
//Binds a zero VAO.
void unbind() const;
//Returns if this VAO has been generated.
bool valid() const;
private:
GLuint vaoId;
};
}
}

View file

@ -0,0 +1,20 @@
// 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
#pragma once
#include <gui/GLShader.h>
struct ShaderPool
{
private:
static ShaderPool* _instance;
public:
static ShaderPool* Instance();
void CompileShaders();
nse::gui::GLShader meshShader;
nse::gui::GLShader simpleShader;
};

View file

@ -0,0 +1,32 @@
#pragma once
#include <nanogui/slider.h>
#include <nanogui/textbox.h>
namespace nse
{
namespace gui
{
nanogui::Slider* AddWidgetWithSlider(nanogui::Widget* parent, const std::string& caption, const std::pair<float, float>& range, float defaultValue, nanogui::Widget*& outWidget);
nanogui::Slider* AddLabeledSlider(nanogui::Widget* parent, const std::string& caption, const std::pair<float, float>& range, float defaultValue);
nanogui::Slider* AddLabeledSlider(nanogui::Widget* parent, const std::string& caption, const std::pair<float, float>& range, float defaultValue, nanogui::TextBox*& out_label);
nanogui::Slider* AddLabeledSliderWithDefaultDisplay(nanogui::Widget* parent, const std::string& caption, const std::pair<float, float>& range, float defaultValue, std::streamsize displayPrecision);
class VectorInput
{
public:
VectorInput(nanogui::Widget* parent, const std::string& prefix, const Eigen::Vector3f& min, const Eigen::Vector3f& max, const Eigen::Vector3f& current, std::function<void(const Eigen::Vector3f&)> callback);
Eigen::Vector3f Value() const;
void SetBounds(const Eigen::Vector3f& lower, const Eigen::Vector3f& upper);
void SetValue(const Eigen::Vector3f& value);
private:
nanogui::Slider *sldX, *sldY, *sldZ;
};
}
}

View file

@ -0,0 +1,122 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Nico Schertler
*/
#pragma once
#include <Eigen/Dense>
namespace nse {
namespace math
{
//Represents an n-dimensional axis-aligned bounding box. The lower limits are inclusive,
//whereas the upper limits are exclusive.
template <typename T, int DIM>
struct BoundingBox
{
Eigen::Matrix<T, DIM, 1> min, max;
//Initializes the bounding box with an empty area.
BoundingBox()
{
reset();
}
BoundingBox(const Eigen::Matrix<T, DIM, 1>& min, const Eigen::Matrix<T, DIM, 1>& max)
: min(min), max(max)
{ }
//Resets the bounding box to an empty area.
void reset()
{
min.setConstant(std::numeric_limits<float>::max());
max.setConstant(std::numeric_limits<float>::lowest());
}
//Computes the intersection of two axis-aligned bounding boxes and returns if there is an intersection.
static bool intersect(const BoundingBox<T, DIM>& bb1, const BoundingBox<T, DIM>& bb2, BoundingBox<T, DIM>& out)
{
for (int i = 0; i < DIM; ++i)
{
out.min[i] = std::max(bb1.min[i], bb2.min[i]);
if (out.min[i] > bb1.max[i] || out.min[i] > bb2.max[i])
return false;
out.max[i] = std::min(bb1.max[i], bb2.max[i]);
if (out.max[i] < bb1.min[i] || out.max[i] < bb2.min[i])
return false;
}
return true;
}
//Computes the union of two axis-aligned bounding boxes.
static void unite(const BoundingBox<T, DIM>& bb1, const BoundingBox<T, DIM>& bb2, BoundingBox<T, DIM>& out)
{
for (int i = 0; i < DIM; ++i)
{
out.min[i] = std::min(bb1.min[i], bb2.min[i]);
out.max[i] = std::max(bb1.max[i], bb2.max[i]);
}
}
//expands the bounding box to contain every point in V
void expand(const Eigen::Matrix<T, DIM, -1>& V)
{
for (int v = 0; v < V.cols(); ++v)
{
for (int i = 0; i < DIM; ++i)
{
if (V(i, v) < min(i))
min(i) = V(i, v);
if (V(i, v) >= max(i))
max(i) = nextafter(V(i, v), std::numeric_limits<T>::max());
}
}
}
//Returns if the bounding box contains the given point.
bool containsPoint(const Eigen::Matrix<T, DIM, 1>& v) const
{
for (int i = 0; i < DIM; ++i)
{
if (v(i) < min(i) || v(i) >= max(i))
return false;
}
return true;
}
//Returns the box's diagonal
Eigen::Matrix<T, DIM, 1> diagonal() const { return max - min; }
//Returns the box's center
Eigen::Matrix<T, DIM, 1> center() const { return 0.5f * (min + max); }
//Transforms the bounding box with a given affine transform and returns the axis-aligned bounding box
//of the transformed box.
BoundingBox<T, DIM> transform(const Eigen::Transform<T, DIM, Eigen::Affine>& t) const
{
//http://dev.theomader.com/transform-bounding-boxes/
Eigen::Matrix<T, DIM, 1> tMin = t.translation();
Eigen::Matrix<T, DIM, 1> tMax = t.translation();
for (int i = 0; i < DIM; ++i)
{
Eigen::Matrix<T, DIM, 1> a = t.linear().col(i) * min(i);
Eigen::Matrix<T, DIM, 1> b = t.linear().col(i) * max(i);
tMin += a.cwiseMin(b);
tMax += a.cwiseMax(b);
}
return BoundingBox<T, DIM>(tMin, tMax);
}
};
}
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <nanogui/opengl.h>
#include <unordered_set>
namespace nse
{
namespace util
{
class GLDebug
{
public:
static void IgnoreGLError(GLuint errorId);
static void SetupDebugCallback();
private:
static std::unordered_set<GLuint> ignoredIds;
};
}
}

View file

@ -0,0 +1,69 @@
#pragma once
#include <Eigen/Core>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
#include <gui/GLBuffer.h>
#include <gui/GLVertexArray.h>
#include <gui/GLShader.h>
typedef OpenMesh::PolyMesh_ArrayKernelT<> HEMesh;
//Converts an OpenMesh vector to an Eigen vector
static Eigen::Vector3f ToEigenVector(const OpenMesh::Vec3f& v)
{
return Eigen::Vector3f(v[0], v[1], v[2]);
}
//Converts an OpenMesh vector to an Eigen vector
static Eigen::Vector2f ToEigenVector(const OpenMesh::Vec2f& v)
{
return Eigen::Vector2f(v[0], v[1]);
}
//Converts an Eigen vector to an OpenMesh vector
static OpenMesh::Vec3f ToOpenMeshVector(const Eigen::Vector3f& v)
{
return OpenMesh::Vec3f(v[0], v[1], v[2]);
}
//Converts an OpenMesh vector to an Eigen vector
static Eigen::Vector4f ToEigenVector4(const OpenMesh::Vec3f& v, float w = 1)
{
return Eigen::Vector4f(v[0], v[1], v[2], w);
}
//Converts an OpenMesh vector to an Eigen vector
static Eigen::Vector4f ToEigenVector4(const OpenMesh::Vec2f& v, float z = 0, float w = 1)
{
return Eigen::Vector4f(v[0], v[1], z, w);
}
//GPU representation of a mesh with rendering capabilities.
class MeshRenderer
{
public:
MeshRenderer(const HEMesh& mesh);
//Update the underlying buffers based on the current geometry in the referenced mesh
void Update();
void UpdateWithPerFaceColor(OpenMesh::FPropHandleT<Eigen::Vector4f> colorProperty);
void Render(const Eigen::Matrix4f& view, const Eigen::Matrix4f& projection, bool flatShading = false, bool withTexCoords = false, const Eigen::Vector4f& color = Eigen::Vector4f(0.8f, 0.7f, 0.6f, 1.0f)) const;
void RenderTextureMap(const Eigen::Matrix4f& projection, const Eigen::Vector4f& color) const;
private:
void UpdateTextureMapBuffers();
const HEMesh& mesh;
nse::gui::GLBuffer positionBuffer, normalBuffer, colorBuffer, texCoordBuffer, texCoordBuffer4D;
nse::gui::GLBuffer indexBuffer, indexBufferTexCoords;
unsigned int indexCount;
unsigned int indexCountTexCoords;
nse::gui::GLVertexArray vao, vaoTexCoords;
bool hasColor = false;
};

View file

@ -0,0 +1,52 @@
#pragma once
#include <vector>
#include <cstddef>
namespace nse
{
namespace util
{
// Represents a union-find data structure (disjoint sets).
class UnionFind
{
// Node is root iff parent index == index
public:
typedef unsigned int index_t;
// Saves the entire structure to a file for later usage
void SaveToFile(const char* filename) const;
// Returns the number of entries
std::size_t size() const;
// Loads the entire structure from a file. Existing data in the structure is overridden.
void LoadFromFile(const char* filename);
// Adds an item to the structure
void AddItem();
void AddItems(std::size_t count);
void Clear();
// Finds the set representative for a given entry. Two entries are in the same set
// iff they have the same set representative.
index_t GetRepresentative(index_t index);
// Merges the sets of the two specified entries. Returns the new root.
index_t Merge(index_t i1, index_t i2);
// Merges the sets of the two specified entries, such that the specified entry will be the new root of the subtree.
//newRoot must already be the representative of itself.
void MergeWithPredefinedRoot(index_t newRoot, index_t i);
private:
void ConcreteMerge(index_t newRoot, index_t child);
std::vector<index_t> parentIndices;
std::vector<unsigned int> ranks;
};
}
}

View file

@ -0,0 +1,207 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Nico Schertler
*/
#include "gui/AbstractViewer.h"
#include <nanogui/window.h>
#include <nanogui/layout.h>
#include <cstring>
#include <iostream>
using namespace nse::gui;
AbstractViewer::AbstractViewer(const std::string& title, int width, int height, int nSamples)
: nanogui::Screen(Eigen::Vector2i(width, height), title, true, false, 8, 8, 24, 8, nSamples),
_camera(*this), _ctrlDown(false), _shiftDown(false), nSamples(nSamples)
{
}
nanogui::Window* AbstractViewer::SetupMainWindow()
{
auto mainWindow = new nanogui::Window(this, this->caption());
mainWindow->setPosition(Eigen::Vector2i(15, 15));
mainWindow->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
CheckOpenGLSupport();
return mainWindow;
}
// Check if the correct version of OpenGL is supported
void AbstractViewer::CheckOpenGLSupport()
{
char tempBuffer[255];
char* oglVersion = (char*)glGetString(GL_VERSION);
strcpy(tempBuffer, oglVersion);
char* token = strtok(tempBuffer, ". ");
std::vector<int> oglVersionNumbers;
while (token != nullptr)
{
try
{
oglVersionNumbers.push_back(std::atoi(token));
token = strtok(nullptr, ". ");
}
catch (...)
{
std::cout << "Error parsing OpenGL version string " << oglVersion << std::endl;
return;
}
}
char* glslVersion = (char*)glGetString(GL_SHADING_LANGUAGE_VERSION);
strcpy(tempBuffer, oglVersion);
token = strtok(tempBuffer, ". ");
std::vector<int> glslVersionNumbers;
while (token != nullptr)
{
try
{
glslVersionNumbers.push_back(std::atoi(token));
token = strtok(nullptr, ". ");
}
catch (...)
{
std::cout << "Error parsing GLSL version string " << glslVersion << std::endl;
return;
}
}
if (oglVersionNumbers.size() < 1 || glslVersionNumbers.size() < 2)
{
std::cout << "Error parsing OpenGL or GLSL version numbers." << std::endl;
return;
}
if (oglVersionNumbers[0] < 3 || (glslVersionNumbers[1] < 3 && glslVersionNumbers[0] == 1))
{
std::cout
<< "ERROR: YOUR VERSION OF OPENGL IS TOO OLD!" << std::endl
<< " - Needed Opengl-Version: 3.0. On this system: " << glGetString(GL_VERSION) << std::endl
<< " - Needed GLSL-Version: 1.3. On this system: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
}
}
bool AbstractViewer::keyboardEvent(int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_LEFT_CONTROL && action == 0)
_ctrlDown = false;
if (key == GLFW_KEY_LEFT_CONTROL && action == 1)
_ctrlDown = true;
if (key == GLFW_KEY_LEFT_SHIFT && action == 0)
_shiftDown = false;
if (key == GLFW_KEY_LEFT_SHIFT && action == 1)
_shiftDown = true;
return true;
}
bool AbstractViewer::scrollEvent(const Eigen::Vector2i &p, const Eigen::Vector2f &rel)
{
if (Screen::scrollEvent(p, rel))
return true;
if (scrollHook(p, rel))
return true;
if (!_ctrlDown && !_shiftDown)
{
_camera.Zoom(rel.y());
return true;
}
return false;
}
bool AbstractViewer::mouseButtonEvent(const Eigen::Vector2i &p, int button, bool down, int modifiers)
{
if (Screen::mouseButtonEvent(p, button, down, modifiers) && down)
return true;
if (mouseButtonHook(p, button, down, modifiers) && down)
return true;
auto now = std::chrono::high_resolution_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - lastClickTime).count() < 300 && !down && button == lastClickButton && (p - lastClickPosition).cwiseAbs().sum() <= 2)
{
if (button == GLFW_MOUSE_BUTTON_LEFT)
{
Eigen::Vector3f point;
auto depth = get3DPosition(p, point);
if (depth < 1)
{
_camera.FocusOnPoint(point);
glfwSetCursorPos(mGLFWWindow, width() / 2, height() / 2);
}
}
}
if (!down)
{
lastClickTime = now;
lastClickButton = button;
lastClickPosition = p;
}
return _camera.HandleMouseButton(p, button, down, modifiers);
}
bool AbstractViewer::mouseMotionEvent(const Eigen::Vector2i &p, const Eigen::Vector2i &rel,
int button, int modifiers)
{
if (Screen::mouseMotionEvent(p, rel, button, modifiers))
return true;
if (mouseMotionHook(p, rel, button, modifiers))
return true;
return _camera.HandleMouseMove(p, rel, button, modifiers);
}
bool AbstractViewer::resizeEvent(const Eigen::Vector2i & s)
{
Screen::resizeEvent(s);
_camera.resize(s);
return true;
}
float AbstractViewer::get3DPosition(const Eigen::Vector2i & screenPos, Eigen::Vector4f & pos)
{
float depth;
glReadPixels(screenPos.x(), height() - 1 - screenPos.y(), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
float ndcDepth = 2 * (depth - 0.5f);
float x = 2 * ((float)screenPos.x() / width() - 0.5f);
float y = 2 * ((float)-screenPos.y() / height() + 0.5f);
Eigen::Matrix4f view, proj;
camera().ComputeCameraMatrices(view, proj);
Eigen::Matrix4f mvp = proj * view;
Eigen::Matrix4f invMvp = mvp.inverse();
pos = invMvp * Eigen::Vector4f(x, y, ndcDepth, 1);
pos /= pos.w();
return depth;
}
float AbstractViewer::get3DPosition(const Eigen::Vector2i & screenPos, Eigen::Vector3f & pos)
{
Eigen::Vector4f pos4;
float depth = get3DPosition(screenPos, pos4);
pos.x() = pos4.x();
pos.y() = pos4.y();
pos.z() = pos4.z();
return depth;
}

177
common/src/gui/Camera.cpp Normal file
View file

@ -0,0 +1,177 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Wenzel Jakob
@author Nico Schertler
*/
#include <cmath>
#include "gui/Camera.h"
using namespace nse::gui;
Camera::Camera(const nanogui::Widget & parent)
: parent(parent)
{
params.arcball.setSize(parent.size());
}
void Camera::ComputeCameraMatrices(Eigen::Matrix4f & view, Eigen::Matrix4f & proj, float customAspectRatio) const
{
auto arcBallMatrix = params.arcball.matrix();
Eigen::Vector3f viewDirection = -arcBallMatrix.row(2).head<3>();
Eigen::Vector3f cameraPosition = params.focusPoint - viewDirection * params.viewDistance;
float depthOfSceneCenter = (params.sceneCenter - cameraPosition).dot(viewDirection);
float minZNear = 0.001f * params.sceneRadius;
float znear = std::max(minZNear, depthOfSceneCenter - params.sceneRadius);
float zfar = std::max(znear + minZNear, depthOfSceneCenter + params.sceneRadius);
float fH = std::tan(params.fovy / 360.0f * (float)M_PI) * znear;
float aspectRatio = customAspectRatio == 0 ? (float)parent.width() / parent.height() : customAspectRatio;
float fW = fH * aspectRatio;
proj = nanogui::frustum(-fW, fW, -fH, fH, znear, zfar);
view = nanogui::translate(Eigen::Vector3f(0, 0, -params.viewDistance)) * params.arcball.matrix() * nanogui::translate(-params.focusPoint);
}
void Camera::Zoom(float amount)
{
params.viewDistance = std::max(0.001f * params.sceneRadius, params.viewDistance * std::pow(0.90f, amount));
}
void Camera::SetSceneExtent(const nse::math::BoundingBox<float, 3>& bbox)
{
params.sceneCenter = 0.5f * (bbox.min + bbox.max);
params.sceneRadius = bbox.diagonal().norm() / 2.0f;
}
void Camera::FocusOnBBox(const nse::math::BoundingBox<float, 3>& bbox)
{
SetSceneExtent(bbox);
params.focusPoint = params.sceneCenter;
float fov = params.fovy * std::min(1.0f, (float)parent.width() / parent.height());
params.viewDistance = params.sceneRadius / sinf(fov / 2.0f * (float)M_PI / 180);
}
void Camera::FocusOnPoint(const Eigen::Vector3f& point)
{
params.focusPoint = point;
}
const Eigen::Vector3f& Camera::GetFocusPoint() const
{
return params.focusPoint;
}
#include <iostream>
void Camera::MakeHorizontal()
{
//the camera matrix C (=inverse view matrix)
auto matrix = params.arcball.matrix().transpose();
Eigen::Vector3f currentXAxis = matrix.col(0).head<3>();
Eigen::Vector3f currentYAxis = matrix.col(1).head<3>();
//solve for a rotation R about the z-axis, such that [C * R * currentXAxis].y = 0
//find the target of the x-axis in the plane: s * x + t * y, s.t. s^2 + t^2 = 1
float xy = currentXAxis.y();
float yy = currentYAxis.y();
float s = sqrt(1.0f / (1 + (xy * xy) / (yy * yy)));
float t = -s * xy / yy;
float angle = std::atan2(t, s);
params.arcball.state() = Eigen::AngleAxisf(-angle, Eigen::Vector3f::UnitZ()) * params.arcball.state();
}
void Camera::RotateAroundFocusPointGlobal(const Eigen::Quaternionf& rotation)
{
params.arcball.state() *= rotation.conjugate();
}
void Camera::RotateAroundFocusPointLocal(const Eigen::Quaternionf& rotation)
{
params.arcball.state() = rotation.conjugate() * params.arcball.state();
}
bool Camera::HandleMouseButton(const Eigen::Vector2i & p, int button, bool down, int modifiers)
{
if (button == GLFW_MOUSE_BUTTON_1 && modifiers == 0)
{
if (down)
{
if (interactionMode == None)
{
interactionMode = Rotate;
params.arcball.button(p, down);
return true;
}
}
else
{
if (interactionMode == Rotate)
{
params.arcball.button(p, false);
interactionMode = None;
return true;
}
}
}
else if (button == GLFW_MOUSE_BUTTON_2 ||
(button == GLFW_MOUSE_BUTTON_1 && modifiers == GLFW_MOD_SHIFT))
{
if (down)
{
if (interactionMode == None)
{
modelTranslation_start = params.focusPoint;
translation_start = p;
interactionMode = Translate;
return true;
}
}
else
{
if (interactionMode == Translate)
{
interactionMode = None;
return true;
}
}
}
return false;
}
bool Camera::HandleMouseMove(const Eigen::Vector2i & p, const Eigen::Vector2i & rel, int button, int modifiers)
{
if (interactionMode == Rotate)
{
params.arcball.motion(p);
return true;
}
else if (interactionMode == Translate)
{
Eigen::Matrix4f view, proj;
ComputeCameraMatrices(view, proj);
Eigen::Vector2f current(2.0f * p.x() / parent.height() - 1.0f, -2.0f * p.y() / parent.height() + 1.0f);
Eigen::Vector2f start(2.0f * translation_start.x() / parent.height() - 1.0f, -2.0f * translation_start.y() / parent.height() + 1.0f);
auto rel = (current - start) * params.viewDistance * tanf(params.fovy / 2 * (float)M_PI / 180);
params.focusPoint = modelTranslation_start - (rel.x() * view.block<1, 3>(0, 0).transpose() + rel.y() * view.block<1, 3>(1, 0).transpose());
return true;
}
return false;
}
void Camera::resize(const Eigen::Vector2i & s)
{
params.arcball.setSize(s);
}

113
common/src/gui/GLBuffer.cpp Normal file
View file

@ -0,0 +1,113 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Wenzel Jakob
@author Nico Schertler
*/
#include "gui/GLBuffer.h"
#include "gui/GLShader.h"
#include <iostream>
using namespace nse::gui;
const GLuint BufferTargets[] =
{
GL_ARRAY_BUFFER,
GL_ELEMENT_ARRAY_BUFFER
#ifdef HAVE_SSBO
, GL_SHADER_STORAGE_BUFFER
#endif
};
GLBuffer::GLBuffer(GLBufferType type)
: id(0), type(type)
{}
GLBuffer::~GLBuffer()
{
if (id != 0)
glDeleteBuffers(1, &id);
}
void GLBuffer::bind()
{
if (id == 0)
glGenBuffers(1, &id);
glBindBuffer(BufferTargets[type], id);
}
void GLBuffer::bindBufferBase(unsigned int base) const
{
glBindBufferBase(BufferTargets[type], base, id);
}
void GLBuffer::bindToAttribute(const std::string& attribute)
{
assert(type == VertexBuffer);
GLShader* prog = GLShader::currentProgram();
auto attribID = prog->attrib(attribute);
if (attribID >= 0)
{
glEnableVertexAttribArray(attribID);
bind();
if (integral)
glVertexAttribIPointer(attribID, dim, glType, 0, 0);
else
glVertexAttribPointer(attribID, dim, glType, GL_FALSE, 0, 0);
}
else
std::cout << "Warning: Attribute \"" << attribute << "\" not found in shader \"" << prog->name() << "\"." << std::endl;
}
void GLBuffer::uploadData(uint32_t size, int dim,
uint32_t compSize, GLuint glType, bool integral,
const uint8_t *data)
{
this->glType = glType;
this->dim = dim;
this->compSize = compSize;
this->size = size;
this->integral = integral;
size_t totalSize = (size_t)size * (size_t)compSize;
bind();
glBufferData(BufferTargets[type], totalSize, data, GL_DYNAMIC_DRAW);
}
void GLBuffer::uploadData(uint32_t size, const void *data)
{
this->glType = -1;
this->dim = -1;
this->compSize = -1;
this->size = size;
this->integral = false;
bind();
glBufferData(BufferTargets[type], size, data, GL_DYNAMIC_DRAW);
}
void GLBuffer::downloadData(uint32_t size, int /* dim */,
uint32_t compSize, GLuint /* glType */, uint8_t *data)
{
if(id == 0)
throw std::runtime_error("The specified buffer has no data to download.");
if (this->size != size || this->compSize != compSize)
throw std::runtime_error("downloadData: size mismatch!");
size_t totalSize = (size_t)size * (size_t)compSize;
bind();
glGetBufferSubData(BufferTargets[type], 0, totalSize, data);
}

254
common/src/gui/GLShader.cpp Normal file
View file

@ -0,0 +1,254 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Wenzel Jakob
@author Nico Schertler
*/
#include "gui/GLShader.h"
#include <iostream>
#include <fstream>
using namespace nse::gui;
GLShader* GLShader::_currentProgram = nullptr;
GLShader* GLShader::currentProgram()
{
return _currentProgram;
}
GLShader::~GLShader()
{
glDeleteProgram(mProgramShader); mProgramShader = 0;
glDeleteShader(mVertexShader); mVertexShader = 0;
glDeleteShader(mTessellationControlShader); mTessellationControlShader = 0;
glDeleteShader(mTessellationEvalShader); mTessellationEvalShader = 0;
glDeleteShader(mFragmentShader); mFragmentShader = 0;
glDeleteShader(mGeometryShader); mGeometryShader = 0;
}
static GLuint createShader_helper(GLint type, const std::string &name,
const std::string &defines,
std::string shader_string,
bool failSilently)
{
if (shader_string.empty())
return (GLuint)0;
if (!defines.empty())
{
if (shader_string.length() > 8 && shader_string.substr(0, 8) == "#version")
{
std::istringstream iss(shader_string);
std::ostringstream oss;
std::string line;
std::getline(iss, line);
oss << line << std::endl;
oss << defines;
while (std::getline(iss, line))
oss << line << std::endl;
shader_string = oss.str();
}
else
{
shader_string = defines + shader_string;
}
}
GLuint id = glCreateShader(type);
const char *shader_string_const = shader_string.c_str();
glShaderSource(id, 1, &shader_string_const, nullptr);
glCompileShader(id);
GLint status;
glGetShaderiv(id, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE)
{
if (failSilently)
return 0;
char buffer[512];
std::cerr << "Error while compiling ";
if (type == GL_VERTEX_SHADER)
std::cerr << "vertex shader";
else if (type == GL_FRAGMENT_SHADER)
std::cerr << "fragment shader";
else if (type == GL_GEOMETRY_SHADER)
std::cerr << "geometry shader";
#ifdef HAVE_TESSELLATION
else if (type == GL_TESS_CONTROL_SHADER)
std::cerr << "tessellation control shader";
else if (type == GL_TESS_EVALUATION_SHADER)
std::cerr << "tessellation evaluation shader";
#endif
std::cerr << " \"" << name << "\":" << std::endl;
std::cerr << shader_string << std::endl << std::endl;
glGetShaderInfoLog(id, 512, nullptr, buffer);
std::cerr << "Error: " << std::endl << buffer << std::endl;
throw std::runtime_error("Shader compilation failed!");
}
return id;
}
bool GLShader::initFromFiles (const std::string &name,
const std::string &vertex_fname,
const std::string &fragment_fname,
const std::string &geometry_fname)
{
auto file_to_string = [](const std::string &filename) -> std::string
{
if (filename.empty())
return "";
std::ifstream t(filename);
return std::string((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
};
return init(name,
file_to_string(vertex_fname),
file_to_string(fragment_fname),
file_to_string(geometry_fname));
}
bool GLShader::init(const std::string &name,
const std::string &vertex_str,
const std::string &fragment_str,
const std::string &geometry_str,
bool failSilently)
{
std::string defines;
for (auto def : mDefinitions)
defines += std::string("#define ") + def.first + std::string(" ") + def.second + "\n";
mName = name;
mVertexShader =
createShader_helper(GL_VERTEX_SHADER, name, defines, vertex_str, failSilently);
mGeometryShader =
createShader_helper(GL_GEOMETRY_SHADER, name, defines, geometry_str, failSilently);
mFragmentShader =
createShader_helper(GL_FRAGMENT_SHADER, name, defines, fragment_str, failSilently);
//only relevant for failSilently
if (!mVertexShader || !mFragmentShader)
return false;
if (!geometry_str.empty() && !mGeometryShader)
return false;
mProgramShader = glCreateProgram();
glAttachShader(mProgramShader, mVertexShader);
glAttachShader(mProgramShader, mFragmentShader);
if (mGeometryShader)
glAttachShader(mProgramShader, mGeometryShader);
glLinkProgram(mProgramShader);
GLint status;
glGetProgramiv(mProgramShader, GL_LINK_STATUS, &status);
if (status != GL_TRUE)
{
if (failSilently)
return false;
char buffer[512];
glGetProgramInfoLog(mProgramShader, 512, nullptr, buffer);
std::cerr << "Linker error (" << mName << "): " << std::endl << buffer << std::endl;
mProgramShader = 0;
throw std::runtime_error("Shader linking failed!");
}
return true;
}
#ifdef HAVE_TESSELLATION
bool GLShader::initWithTessellation(const std::string &name,
const std::string &vertex_str,
const std::string &tessellation_control_str,
const std::string &tessellation_eval_str,
const std::string &fragment_str,
const std::string &geometry_str,
bool failSilently)
{
std::string defines;
for (auto def : mDefinitions)
defines += std::string("#define ") + def.first + std::string(" ") + def.second + "\n";
mName = name;
mVertexShader =
createShader_helper(GL_VERTEX_SHADER, name, defines, vertex_str, failSilently);
#ifdef HAVE_TESSELLATION
mTessellationControlShader =
createShader_helper(GL_TESS_CONTROL_SHADER, name, defines, tessellation_control_str, failSilently);
mTessellationEvalShader =
createShader_helper(GL_TESS_EVALUATION_SHADER, name, defines, tessellation_eval_str, failSilently);
#endif
mGeometryShader =
createShader_helper(GL_GEOMETRY_SHADER, name, defines, geometry_str, failSilently);
mFragmentShader =
createShader_helper(GL_FRAGMENT_SHADER, name, defines, fragment_str, failSilently);
//only relevant for failSilently
if (!mVertexShader || !mFragmentShader || !mTessellationControlShader || !mTessellationEvalShader)
return false;
if (!geometry_str.empty() && !mGeometryShader)
return false;
mProgramShader = glCreateProgram();
glAttachShader(mProgramShader, mVertexShader);
glAttachShader(mProgramShader, mTessellationControlShader);
glAttachShader(mProgramShader, mTessellationEvalShader);
glAttachShader(mProgramShader, mFragmentShader);
if (mGeometryShader)
glAttachShader(mProgramShader, mGeometryShader);
glLinkProgram(mProgramShader);
GLint status;
glGetProgramiv(mProgramShader, GL_LINK_STATUS, &status);
if (status != GL_TRUE)
{
if (failSilently)
return false;
char buffer[512];
glGetProgramInfoLog(mProgramShader, 512, nullptr, buffer);
std::cerr << "Linker error (" << mName << "): " << std::endl << buffer << std::endl;
mProgramShader = 0;
throw std::runtime_error("Shader linking failed!");
}
return true;
}
#endif
void GLShader::bind()
{
glUseProgram(mProgramShader);
_currentProgram = this;
}
GLint GLShader::attrib(const std::string &name, bool warn) const
{
GLint id = glGetAttribLocation(mProgramShader, name.c_str());
if (id == -1 && warn)
std::cerr << mName << ": warning: did not find attrib " << name << std::endl;
return id;
}
GLint GLShader::uniform(const std::string &name, bool warn) const
{
GLint id = glGetUniformLocation(mProgramShader, name.c_str());
if (id == -1 && warn)
std::cerr << mName << ": warning: did not find uniform " << name << std::endl;
return id;
}

View file

@ -0,0 +1,44 @@
/*
This file is part of NSEssentials.
Use of this source code is granted via a BSD-style license, which can be found
in License.txt in the repository root.
@author Nico Schertler
*/
#include "gui/GLVertexArray.h"
using namespace nse::gui;
GLVertexArray::GLVertexArray()
: vaoId(0)
{
}
GLVertexArray::~GLVertexArray()
{
if (vaoId > 0)
glDeleteVertexArrays(1, &vaoId);
}
void GLVertexArray::generate()
{
if (vaoId == 0)
glGenVertexArrays(1, &vaoId);
}
void GLVertexArray::bind() const
{
glBindVertexArray(vaoId);
}
void GLVertexArray::unbind() const
{
glBindVertexArray(0);
}
bool GLVertexArray::valid() const
{
return vaoId != 0;
}

View file

@ -0,0 +1,24 @@
// 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 <gui/ShaderPool.h>
#include "glsl.h"
ShaderPool* ShaderPool::_instance(nullptr);
ShaderPool* ShaderPool::Instance()
{
if (_instance == nullptr)
{
_instance = new ShaderPool();
_instance->CompileShaders();
}
return _instance;
}
void ShaderPool::CompileShaders()
{
meshShader.init("Mesh Shader", std::string((char*)mesh_vert, mesh_vert_size), std::string((char*)mesh_frag, mesh_frag_size));
simpleShader.init("Simple Shader", std::string((char*)simple_vert, simple_vert_size), std::string((char*)simple_frag, simple_frag_size));
}

View file

@ -0,0 +1,89 @@
#include <gui/SliderHelper.h>
#include <nanogui/layout.h>
#include <nanogui/label.h>
using namespace nse::gui;
nanogui::Slider* nse::gui::AddWidgetWithSlider(nanogui::Widget* parent, const std::string& caption, const std::pair<float, float>& range, float defaultValue, nanogui::Widget*& outWidget)
{
outWidget = new nanogui::Widget(parent);
outWidget->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Horizontal, nanogui::Alignment::Middle, 0, 6));
new nanogui::Label(outWidget, caption);
auto slider = new nanogui::Slider(outWidget);
slider->setFixedWidth(100);
slider->setValue(defaultValue);
slider->setRange(range);
return slider;
}
nanogui::Slider* nse::gui::AddLabeledSlider(nanogui::Widget* parent, const std::string& caption, const std::pair<float, float>& range, float defaultValue)
{
nanogui::Widget* widget;
return AddWidgetWithSlider(parent, caption, range, defaultValue, widget);
}
nanogui::Slider* nse::gui::AddLabeledSlider(nanogui::Widget* parent, const std::string& caption, const std::pair<float, float>& range, float defaultValue, nanogui::TextBox*& out_label)
{
nanogui::Widget* widget;
auto slider = AddWidgetWithSlider(parent, caption, range, defaultValue, widget);
out_label = new nanogui::TextBox(widget);
out_label->setFixedSize(Eigen::Vector2i(60, 25));
return slider;
}
nanogui::Slider* nse::gui::AddLabeledSliderWithDefaultDisplay(nanogui::Widget* parent, const std::string& caption, const std::pair<float, float>& range, float defaultValue, std::streamsize displayPrecision)
{
nanogui::TextBox* txt;
auto sld = AddLabeledSlider(parent, caption, range, defaultValue, txt);
sld->setCallback([txt, displayPrecision](float value) {
std::stringstream ss;
ss.precision(displayPrecision);
ss << std::fixed << value;
txt->setValue(ss.str());
});
sld->callback()(sld->value());
return sld;
}
VectorInput::VectorInput(nanogui::Widget* parent, const std::string& prefix, const Eigen::Vector3f& min, const Eigen::Vector3f& max, const Eigen::Vector3f& current, std::function<void(const Eigen::Vector3f&)> callback)
{
sldX = nse::gui::AddLabeledSliderWithDefaultDisplay(parent, prefix + " X", std::make_pair(min.x(), max.x()), current.x(), 2);
sldY = nse::gui::AddLabeledSliderWithDefaultDisplay(parent, prefix + " Y", std::make_pair(min.y(), max.y()), current.y(), 2);
sldZ = nse::gui::AddLabeledSliderWithDefaultDisplay(parent, prefix + " Z", std::make_pair(min.z(), max.z()), current.z(), 2);
auto oldXCb = sldX->callback();
auto oldYCb = sldY->callback();
auto oldZCb = sldZ->callback();
auto update = [callback, this]() {
auto value = Value();
callback(value);
};
sldX->setCallback([oldXCb, update](float value) {oldXCb(value); update(); });
sldY->setCallback([oldYCb, update](float value) {oldYCb(value); update(); });
sldZ->setCallback([oldZCb, update](float value) {oldZCb(value); update(); });
}
Eigen::Vector3f VectorInput::Value() const
{
return Eigen::Vector3f(sldX->value(), sldY->value(), sldZ->value());
}
void VectorInput::SetBounds(const Eigen::Vector3f& lower, const Eigen::Vector3f& upper)
{
sldX->setRange(std::make_pair(lower.x(), upper.x()));
sldY->setRange(std::make_pair(lower.y(), upper.y()));
sldZ->setRange(std::make_pair(lower.z(), upper.z()));
}
void VectorInput::SetValue(const Eigen::Vector3f& value)
{
sldX->setValue(value.x()); sldX->callback()(sldX->value());
sldY->setValue(value.y()); sldY->callback()(sldY->value());
sldZ->setValue(value.z()); sldZ->callback()(sldZ->value());
}

136
common/src/util/GLDebug.cpp Normal file
View file

@ -0,0 +1,136 @@
#include "util/GLDebug.h"
#include <iostream>
std::unordered_set<GLuint> nse::util::GLDebug::ignoredIds;
std::string FormatDebugOutput(GLenum source, GLenum type, GLuint id, GLenum severity, const char* msg)
{
std::stringstream stringStream;
std::string sourceString;
std::string typeString;
std::string severityString;
// The AMD variant of this extension provides a less detailed classification of the error,
// which is why some arguments might be "Unknown".
switch (source) {
case GL_DEBUG_CATEGORY_API_ERROR_AMD:
case GL_DEBUG_SOURCE_API: {
sourceString = "API";
break;
}
case GL_DEBUG_CATEGORY_APPLICATION_AMD:
case GL_DEBUG_SOURCE_APPLICATION: {
sourceString = "Application";
break;
}
case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD:
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: {
sourceString = "Window System";
break;
}
case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD:
case GL_DEBUG_SOURCE_SHADER_COMPILER: {
sourceString = "Shader Compiler";
break;
}
case GL_DEBUG_SOURCE_THIRD_PARTY: {
sourceString = "Third Party";
break;
}
case GL_DEBUG_CATEGORY_OTHER_AMD:
case GL_DEBUG_SOURCE_OTHER: {
sourceString = "Other";
break;
}
default: {
sourceString = "Unknown";
break;
}
}
switch (type) {
case GL_DEBUG_TYPE_ERROR: {
typeString = "Error";
break;
}
case GL_DEBUG_CATEGORY_DEPRECATION_AMD:
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: {
typeString = "Deprecated Behavior";
break;
}
case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD:
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: {
typeString = "Undefined Behavior";
break;
}
case GL_DEBUG_TYPE_PORTABILITY_ARB: {
typeString = "Portability";
break;
}
case GL_DEBUG_CATEGORY_PERFORMANCE_AMD:
case GL_DEBUG_TYPE_PERFORMANCE: {
typeString = "Performance";
break;
}
case GL_DEBUG_CATEGORY_OTHER_AMD:
case GL_DEBUG_TYPE_OTHER: {
typeString = "Other";
break;
}
default: {
typeString = "Unknown";
break;
}
}
switch (severity) {
case GL_DEBUG_SEVERITY_HIGH: {
severityString = "High";
break;
}
case GL_DEBUG_SEVERITY_MEDIUM: {
severityString = "Medium";
break;
}
case GL_DEBUG_SEVERITY_LOW: {
severityString = "Low";
break;
}
default: {
severityString = "Unknown";
break;
}
}
stringStream << "OpenGL Error: " << msg;
stringStream << " [Source = " << sourceString;
stringStream << ", Type = " << typeString;
stringStream << ", Severity = " << severityString;
stringStream << ", ID = " << id << "]";
return stringStream.str();
}
void APIENTRY DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
const std::unordered_set<GLuint>* ignoredIds = static_cast<const std::unordered_set<GLuint>*>(userParam);
if(ignoredIds->find(id) != ignoredIds->end())
return;
std::string error = FormatDebugOutput(source, type, id, severity, message);
std::cout << error << std::endl;
}
void nse::util::GLDebug::IgnoreGLError(GLuint errorId)
{
ignoredIds.insert(errorId);
}
void nse::util::GLDebug::SetupDebugCallback()
{
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback(DebugCallback, &ignoredIds);
}

View file

@ -0,0 +1,208 @@
#include "util/OpenMeshUtils.h"
#include <vector>
#include <gui/ShaderPool.h>
MeshRenderer::MeshRenderer(const HEMesh& mesh)
: mesh(mesh), indexCount(0),
positionBuffer(nse::gui::VertexBuffer), normalBuffer(nse::gui::VertexBuffer), colorBuffer(nse::gui::VertexBuffer), texCoordBuffer(nse::gui::VertexBuffer),
indexBuffer(nse::gui::IndexBuffer),
texCoordBuffer4D(nse::gui::VertexBuffer), indexBufferTexCoords(nse::gui::IndexBuffer)
{
vao.generate();
vaoTexCoords.generate();
Update();
}
//EmitVertexFunctor: void(const HEMesh::HalfedgeHandle[3]) //the to-vertices of the halfedges are the triangle corners
template <typename EmitTriangleFunctor>
void TriangulateMeshFace(HEMesh::FaceHandle f, const HEMesh& mesh, EmitTriangleFunctor&& emitTriangle)
{
OpenMesh::HalfedgeHandle base;
for (auto h : mesh.fh_range(f))
{
if (base.idx() == -1)
{
base = h;
continue;
}
auto nextH = mesh.next_halfedge_handle(h);
if (nextH == base)
break;
else
{
HEMesh::HalfedgeHandle triangle[3] = { base, h, nextH };
std::forward<EmitTriangleFunctor>(emitTriangle)(triangle);
}
}
}
void MeshRenderer::Update()
{
if (mesh.n_vertices() == 0)
return;
ShaderPool::Instance()->meshShader.bind();
vao.bind();
std::vector<Eigen::Vector4f> positions;
std::vector<Eigen::Vector4f> normals;
std::vector<Eigen::Vector2f> uvs;
positions.reserve(mesh.n_vertices());
normals.reserve(mesh.n_vertices());
if(mesh.has_vertex_texcoords2D())
uvs.reserve(mesh.n_vertices());
for (auto v : mesh.vertices())
{
positions.push_back(ToEigenVector4(mesh.point(v)));
OpenMesh::Vec3f n;
mesh.calc_vertex_normal_correct(v, n);
normals.push_back(ToEigenVector4(n, 0));
if (mesh.has_vertex_texcoords2D())
uvs.push_back(ToEigenVector(mesh.texcoord2D(v)));
}
positionBuffer.uploadData(positions).bindToAttribute("position");
normalBuffer.uploadData(normals).bindToAttribute("normal");
if (mesh.has_vertex_texcoords2D())
texCoordBuffer.uploadData(uvs).bindToAttribute("texCoords");
std::vector<uint32_t> indices;
indices.reserve(mesh.n_faces() * 3);
for (auto f : mesh.faces())
{
TriangulateMeshFace(f, mesh, [&](const HEMesh::HalfedgeHandle h[3])
{
indices.push_back(mesh.to_vertex_handle(h[0]).idx());
indices.push_back(mesh.to_vertex_handle(h[1]).idx());
indices.push_back(mesh.to_vertex_handle(h[2]).idx());
});
}
indexBuffer.uploadData(sizeof(uint32_t) * (uint32_t)indices.size(), indices.data());
indexCount = (unsigned int)indices.size();
vao.unbind();
hasColor = false;
UpdateTextureMapBuffers();
}
void MeshRenderer::UpdateWithPerFaceColor(OpenMesh::FPropHandleT<Eigen::Vector4f> colorProperty)
{
if (mesh.n_vertices() == 0)
return;
ShaderPool::Instance()->meshShader.bind();
vao.bind();
std::vector<Eigen::Vector4f> positions;
std::vector<Eigen::Vector4f> normals;
std::vector<Eigen::Vector4f> colors;
std::vector<Eigen::Vector2f> uvs;
positions.reserve(3 * mesh.n_faces());
normals.reserve(3 * mesh.n_faces());
colors.reserve(3 * mesh.n_faces());
if (mesh.has_vertex_texcoords2D())
uvs.reserve(3 * mesh.n_faces());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
for (auto f : mesh.faces())
{
TriangulateMeshFace(f, mesh, [&](const HEMesh::HalfedgeHandle h[3])
{
for (int i = 0; i < 3; ++i)
{
auto v = mesh.to_vertex_handle(h[i]);
positions.push_back(ToEigenVector4(mesh.point(v)));
OpenMesh::Vec3f n;
mesh.calc_vertex_normal_correct(v, n);
normals.push_back(ToEigenVector4(n, 0));
colors.push_back(mesh.property(colorProperty, f));
if (mesh.has_vertex_texcoords2D())
uvs.push_back(ToEigenVector(mesh.texcoord2D(v)));
}
});
}
positionBuffer.uploadData(positions).bindToAttribute("position");
normalBuffer.uploadData(normals).bindToAttribute("normal");
colorBuffer.uploadData(colors).bindToAttribute("color");
if (mesh.has_vertex_texcoords2D())
texCoordBuffer.uploadData(uvs).bindToAttribute("texCoords");
indexCount = (unsigned int)positions.size();
vao.unbind();
hasColor = true;
UpdateTextureMapBuffers();
}
void MeshRenderer::UpdateTextureMapBuffers()
{
if (!mesh.has_vertex_texcoords2D())
return;
ShaderPool::Instance()->simpleShader.bind();
vaoTexCoords.bind();
std::vector<Eigen::Vector4f> positions;
positions.reserve(mesh.n_vertices());
for (auto v : mesh.vertices())
positions.push_back(ToEigenVector4(mesh.texcoord2D(v)));
texCoordBuffer4D.uploadData(positions).bindToAttribute("position");
std::vector<uint32_t> indices;
indices.reserve(mesh.n_edges() * 2);
for (auto e : mesh.edges())
{
auto h = mesh.halfedge_handle(e, 0);
indices.push_back(mesh.from_vertex_handle(h).idx());
indices.push_back(mesh.to_vertex_handle(h).idx());
}
indexBufferTexCoords.uploadData(sizeof(uint32_t) * (uint32_t)indices.size(), indices.data());
indexCountTexCoords = (unsigned int)indices.size();
vaoTexCoords.unbind();
}
void MeshRenderer::Render(const Eigen::Matrix4f& view, const Eigen::Matrix4f& projection, bool flatShading, bool withTexCoords, const Eigen::Vector4f& color) const
{
if (indexCount == 0)
return;
auto& shader = ShaderPool::Instance()->meshShader;
shader.bind();
shader.setUniform("view", view);
shader.setUniform("proj", projection);
shader.setUniform("flatShading", flatShading ? 1 : 0);
shader.setUniform("perVertexColor", hasColor ? 1 : 0);
shader.setUniform("visualizeTexCoords", withTexCoords ? 1 : 0);
shader.setUniform("color", color);
vao.bind();
if (hasColor)
glDrawArrays(GL_TRIANGLES, 0, indexCount);
else
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
vao.unbind();
}
void MeshRenderer::RenderTextureMap(const Eigen::Matrix4f& projection, const Eigen::Vector4f& color) const
{
if (indexCountTexCoords == 0)
return;
auto& shader = ShaderPool::Instance()->simpleShader;
shader.bind();
shader.setUniform("mvp", projection);
shader.setUniform("color", color);
vaoTexCoords.bind();
glDrawElements(GL_LINES, indexCountTexCoords, GL_UNSIGNED_INT, 0);
vaoTexCoords.unbind();
}

View file

@ -0,0 +1,133 @@
#include "util/UnionFind.h"
#include <stdio.h>
#include <cstdint>
#include <cassert>
#include <stdexcept>
using namespace nse::util;
void UnionFind::SaveToFile(const char* filename) const
{
FILE* file = fopen(filename, "wb");
uint64_t entries = size();
fwrite(&entries, sizeof(uint64_t), 1, file);
fwrite(&parentIndices[0], sizeof(index_t), parentIndices.size(), file);
fwrite(&ranks[0], sizeof(unsigned int), ranks.size(), file);
fclose(file);
}
std::size_t UnionFind::size() const { return parentIndices.size(); }
// Loads the entire structure from a file. Existing data in the structure is overridden.
void UnionFind::LoadFromFile(const char* filename)
{
uint64_t entries;
FILE* file = fopen(filename, "rb");
if(fread(&entries, sizeof(uint64_t), 1, file) != 1)
throw std::runtime_error("Cannot read enough data from file");
parentIndices.resize(entries);
ranks.resize(entries);
if(fread(&parentIndices[0], sizeof(index_t), entries, file) != entries)
throw std::runtime_error("Cannot read enough data from file");
if(fread(&ranks[0], sizeof(unsigned int), entries, file) != entries)
throw std::runtime_error("Cannot read enough data from file");
fclose(file);
}
// Adds an item to the structure
void UnionFind::AddItem()
{
parentIndices.push_back((index_t)parentIndices.size());
ranks.push_back(0);
}
void UnionFind::AddItems(std::size_t count)
{
auto oldCount = parentIndices.size();
parentIndices.resize(parentIndices.size() + count);
for (index_t i = static_cast<index_t>(oldCount); i < parentIndices.size(); ++i)
parentIndices[i] = i;
ranks.resize(ranks.size() + count);
}
void UnionFind::Clear()
{
parentIndices.clear();
ranks.clear();
}
// Finds the set representative for a given entry. Two entries are in the same set
// iff they have the same set representative.
UnionFind::index_t UnionFind::GetRepresentative(index_t index)
{
//Find the root
index_t current = index;
while (parentIndices[current] != current)
current = parentIndices[current];
//Path compression
index_t root = current;
current = index;
while (parentIndices[current] != current)
{
index_t i = current;
current = parentIndices[current];
parentIndices[i] = root;
}
return root;
}
// Merges the sets of the two specified entries.
UnionFind::index_t UnionFind::Merge(index_t i1, index_t i2)
{
index_t rep1 = GetRepresentative(i1);
index_t rep2 = GetRepresentative(i2);
if (rep1 == rep2)
return rep1;
//Union by rank
unsigned int rank1 = ranks[rep1];
unsigned int rank2 = ranks[rep2];
if (rank1 < rank2)
{
ConcreteMerge(rep2, rep1);
return rep2;
}
else if (rank2 < rank1)
{
ConcreteMerge(rep1, rep2);
return rep1;
}
else
{
ConcreteMerge(rep1, rep2);
++ranks[rep1];
return rep1;
}
}
void UnionFind::MergeWithPredefinedRoot(index_t newRoot, index_t i)
{
assert(GetRepresentative(newRoot) == newRoot);
index_t rep2 = GetRepresentative(i);
if (newRoot == rep2)
return;
unsigned int rank1 = ranks[newRoot];
unsigned int rank2 = ranks[rep2];
if (rank1 == rank2)
++ranks[newRoot];
ConcreteMerge(newRoot, i);
}
void UnionFind::ConcreteMerge(index_t newRoot, index_t child)
{
parentIndices[child] = newRoot;
}

13
exercise1/CMakeLists.txt Normal file
View file

@ -0,0 +1,13 @@
set(GLSL_FILES shader.vert shader.frag)
ProcessGLSLFiles(GLSL_FILES)
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include )
add_executable(Exercise1 MACOSX_BUNDLE
glsl.cpp
src/main.cpp
src/Viewer.cpp include/Viewer.h
${GLSL_FILES})
target_link_libraries(Exercise1 CG1Common ${LIBS})

View file

@ -0,0 +1,24 @@
// 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
#version 130
in vec4 fragment_color;
out vec4 color;
void main(void)
{
color = fragment_color * 0.5 + vec4(0.5);
/**** Begin of tasks ***
- 1.2.5
Implement the pseudo-code for calculating the julia fractal at a point.
To get the point you can declare a new "in" variable which contains the
position and just use the X- and Y- value.
*** End of tasks ***/
}

View file

@ -0,0 +1,31 @@
// 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
#version 130
in vec4 in_position;
out vec4 fragment_color;
void main(void)
{
gl_Position = in_position;
fragment_color = in_position;
/* - 1.2.2 (b)
* Declare a new "in" variable with the name "in_color". Instead of setting
* "fragment_color" to the position, set it to "in_color. */
/* - 1.2.4 (a)
* Declare two new "uniform" variables with the type "mat4" (above the main function)
* that store the modelview and projection matrix. To apply the transformations
* multiply the value of "in_position" before setting "gl_Position". */
/* - 1.2.5
* The algorithm to calculate the julia fractal needs a position as input.
* Declare another "out" variable and set it to the untransformed input
* position. */
}

View file

@ -0,0 +1,42 @@
// 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
#pragma once
#include <gui/AbstractViewer.h>
class Viewer : public nse::gui::AbstractViewer
{
public:
Viewer();
void drawContents();
private:
void SetupGUI();
Eigen::Matrix4f modelViewMatrix, projectionMatrix;
//GUI Elements for the various options
nanogui::CheckBox* chkHasFaceCulling; //Shall back face culling be activated?
nanogui::CheckBox* chkHasDepthTesting; //Shall depth testing be activated?
nanogui::Slider* sldJuliaCX; //Seed for the Julia fractal
nanogui::Slider* sldJuliaCY;
nanogui::Slider* sldJuliaZoom; //Zoom factor for the Julia fractal
// The following variables hold OpenGL object IDs
GLuint vertex_shader_id, // ID of the vertex shader
fragment_shader_id, // ID of the fragment shader
program_id, // ID of the shader program
vertex_array_id, // ID of the vertex array
position_buffer_id, // ID of the position buffer
color_buffer_id, // ID of the color buffer
uv_map_buffer_id; // ID of the uv_map
// Read, Compile and link the shader codes to a shader program
void CreateShaders();
// Create and define the vertex array and add a number of vertex buffers
void CreateVertexBuffers();
};

183
exercise1/src/Viewer.cpp Normal file
View file

@ -0,0 +1,183 @@
// 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/layout.h>
#include <nanogui/checkbox.h>
#include <gui/SliderHelper.h>
#include <iostream>
#include "glsl.h"
Viewer::Viewer()
: AbstractViewer("CG1 Exercise 1")
{
SetupGUI();
CreateShaders();
CreateVertexBuffers();
modelViewMatrix.setIdentity();
projectionMatrix.setIdentity();
camera().FocusOnBBox(nse::math::BoundingBox<float, 3>(Eigen::Vector3f(-1, -1, -1), Eigen::Vector3f(1, 1, 1)));
}
void Viewer::SetupGUI()
{
auto mainWindow = SetupMainWindow();
//Create GUI elements for the various options
chkHasDepthTesting = new nanogui::CheckBox(mainWindow, "Perform Depth Testing");
chkHasDepthTesting->setChecked(true);
chkHasFaceCulling = new nanogui::CheckBox(mainWindow, "Perform backface Culling");
chkHasFaceCulling->setChecked(true);
sldJuliaCX = nse::gui::AddLabeledSliderWithDefaultDisplay(mainWindow, "JuliaC.X", std::make_pair(-1.0f, 1.0f), 0.45f, 2);
sldJuliaCY = nse::gui::AddLabeledSliderWithDefaultDisplay(mainWindow, "JuliaC.Y", std::make_pair(-1.0f, 1.0f), -0.3f, 2);
sldJuliaZoom = nse::gui::AddLabeledSliderWithDefaultDisplay(mainWindow, "Julia Zoom", std::make_pair(0.01f, 10.0f), 1.0f, 2);
performLayout();
}
// Create and define the vertex array and add a number of vertex buffers
void Viewer::CreateVertexBuffers()
{
/*** Begin of task 1.2.3 ***
Fill the positions-array and your color array with 12 rows, each
containing 4 entries, to define a tetrahedron. */
// Define 3 vertices for one face
GLfloat positions[] = {
0, 1, 0, 1,
-1, -1, 0, 1,
1, -1, 0, 1
};
// Generate the vertex array
glGenVertexArrays(1, &vertex_array_id);
glBindVertexArray(vertex_array_id);
// Generate a position buffer to be appended to the vertex array
glGenBuffers(1, &position_buffer_id);
// Bind the buffer for subsequent settings
glBindBuffer(GL_ARRAY_BUFFER, position_buffer_id);
// Supply the position data
glBufferData(GL_ARRAY_BUFFER, sizeof(positions), positions, GL_STATIC_DRAW);
// The buffer shall now be linked to the shader attribute
// "in_position". First, get the location of this attribute in
// the shader program
GLuint vid = glGetAttribLocation(program_id, "in_position");
// Enable this vertex attribute array
glEnableVertexAttribArray(vid);
// Set the format of the data to match the type of "in_position"
glVertexAttribPointer(vid, 4, GL_FLOAT, GL_FALSE, 0, 0);
/*** Begin of task 1.2.2 (a) ***
Create another buffer that will store color information. This works nearly
similar to the code above that creates the position buffer. Store the buffer
id into the variable "color_buffer_id" and bind the color buffer to the
shader variable "in_color".
/*** End of task 1.2.2 (a) ***/
// Unbind the vertex array to leave OpenGL in a clean state
glBindVertexArray(0);
}
//Checks if the given shader has been compiled successfully. Otherwise, prints an
//error message and throws an exception.
// shaderId - the id of the shader object
// name - a human readable name for the shader that is printed together with the error
void CheckShaderCompileStatus(GLuint shaderId, std::string name)
{
GLint status;
glGetShaderiv(shaderId, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE)
{
char buffer[512];
std::cerr << "Error while compiling shader \"" << name << "\":" << std::endl;
glGetShaderInfoLog(shaderId, 512, nullptr, buffer);
std::cerr << "Error: " << std::endl << buffer << std::endl;
throw std::runtime_error("Shader compilation failed!");
}
}
// Read, Compile and link the shader codes to a shader program
void Viewer::CreateShaders()
{
std::string vs((char*)shader_vert, shader_vert_size);
const char *vertex_content = vs.c_str();
std::string fs((char*)shader_frag, shader_frag_size);
const char *fragment_content = fs.c_str();
/*** Begin of task 1.2.1 ***
Use the appropriate OpenGL commands to create a shader object for
the vertex shader, set the source code and let it compile. Store the
ID of this shader object in the variable "vertex_shader_id". Repeat
for the fragment shader. Store the ID in the variable "fragment_shader_id.
Finally, create a shader program with its handle stored in "program_id",
attach both shader objects and link them. For error checking, you can
use the method "CheckShaderCompileStatus()" after the call to glCompileShader().
*/
/*** End of task 1.2.1 ***/
}
void Viewer::drawContents()
{
Eigen::Vector2f juliaC(sldJuliaCX->value(), sldJuliaCY->value());
float juliaZoom = sldJuliaZoom->value();
//Get the transform matrices
camera().ComputeCameraMatrices(modelViewMatrix, projectionMatrix);
// If has_faceculling is set then enable backface culling
// and disable it otherwise
if (chkHasFaceCulling->checked())
glEnable(GL_CULL_FACE);
else
glDisable(GL_CULL_FACE);
// If has_depthtesting is set then enable depth testing
// and disable it otherwise
if (chkHasDepthTesting->checked())
glEnable(GL_DEPTH_TEST);
else
glDisable(GL_DEPTH_TEST);
// Activate the shader program
glUseProgram(program_id);
/*** Begin of task 1.2.4 (b) ***
Set the shader variables for the modelview and projection matrix.
First, find the location of these variables using glGetUniformLocation and
then set them with the command glUniformMatrix4fv.
*/
// Bind the vertex array
glBindVertexArray(vertex_array_id);
// Draw the bound vertex array. Start at element 0 and draw 3 vertices
glDrawArrays(GL_TRIANGLES, 0, 3);
/*** End of task 1.2.4 (b) ***/
// Unbind the vertex array
glBindVertexArray(0);
// Deactivate the shader program
glUseProgram(0);
}

36
exercise1/src/main.cpp Normal file
View file

@ -0,0 +1,36 @@
// 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 <iostream>
#include <util/GLDebug.h>
#include "Viewer.h"
int main(int argc, char* argv[])
{
nanogui::init();
{
nanogui::ref<Viewer> viewer = new Viewer();
viewer->setVisible(true);
nse::util::GLDebug::SetupDebugCallback();
nse::util::GLDebug::IgnoreGLError(131185); //buffer usage info
try
{
nanogui::mainloop();
}
catch (std::runtime_error& e)
{
std::cerr << e.what() << std::endl;
}
}
nanogui::shutdown();
return 0;
}

14
exercise2/CMakeLists.txt Normal file
View file

@ -0,0 +1,14 @@
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include )
add_executable(Exercise2 MACOSX_BUNDLE
src/main.cpp
src/Viewer.cpp include/Viewer.h
src/Primitives.cpp include/Primitives.h
src/SurfaceArea.cpp include/SurfaceArea.h
src/Volume.cpp include/Volume.h
src/ShellExtraction.cpp include/ShellExtraction.h
src/Smoothing.cpp include/Smoothing.h
src/Stripification.cpp include/Stripification.h
include/sample_set.h)
target_link_libraries(Exercise2 CG1Common ${LIBS})

View file

@ -0,0 +1,38 @@
// 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
#pragma once
#include "util/OpenMeshUtils.h"
//create a mesh containing a single quad
void CreateQuad(HEMesh& m);
//create a unit coube mesh
void CreateCube(HEMesh& m);
//create a regular tetraeder mesh
void CreateTetrahedron(HEMesh& m, float a = 1);
//create a disc mesh
void CreateDisk(HEMesh& m, float radius, int slices);
//create a cylinder mesh
void CreateCylinder(HEMesh& m, float radius, float height, int stacks, int slices);
//create a sphere mesh
void CreateSphere(HEMesh& m, float radius, int slices, int stacks);
//create a torus mesh
void CreateTorus(HEMesh& m, float r, float R, int nsides, int rings);
//creates an icosaeder mesh in m
// radius is the radius of the circum sphere
void CreateIcosahedron(HEMesh& m, float radius);
//creates an octaeder mesh
// radius is the radius of the circum sphere
void CreateOctahedron(HEMesh& m, float radius);
//create a unit arrow
void CreateUnitArrow(HEMesh& m, float stem_radius = 0.04, float head_radius = 0.1, float stem_height = 0.8, int slices = 30, int stem_stacks = 1);

View file

@ -0,0 +1,11 @@
// 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
#pragma once
#include "util/OpenMeshUtils.h"
//Finds the connected components in the mesh. Writes the non-negative shell index into perFaceShellIndex
//and returns the number of shells.
unsigned int ExtractShells(HEMesh& m, OpenMesh::FPropHandleT<int> perFaceShellIndex);

View file

@ -0,0 +1,12 @@
// 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
#pragma once
#include "util/OpenMeshUtils.h"
//Updates the vertex positions by Laplacian smoothing
void SmoothUniformLaplacian(HEMesh& m, float lambda, unsigned int iterations);
void AddNoise(HEMesh& m);

View file

@ -0,0 +1,11 @@
// 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
#pragma once
#include "util/OpenMeshUtils.h"
//Extracts triangle strips from the mesh, writes the Strip Id of each face in
//perFaceStripIdProperty, and returns the number of strips.
unsigned int ExtractTriStrips(HEMesh& m, OpenMesh::FPropHandleT<int> perFaceStripIdProperty, unsigned int nTrials);

View file

@ -0,0 +1,12 @@
// 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
#pragma once
#include "util/OpenMeshUtils.h"
//computes the surface area of the halfedge mesh m by computing and summing up the
//areas of all polygonal faces
//method is not restricted to triangle meshes
float ComputeSurfaceArea(const HEMesh& m);

View file

@ -0,0 +1,35 @@
// 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
#pragma once
#include <gui/AbstractViewer.h>
#include <util/OpenMeshUtils.h>
class Viewer : public nse::gui::AbstractViewer
{
public:
Viewer();
void drawContents();
private:
void SetupGUI();
void MeshUpdated(bool initNewMesh = false);
void ColorMeshFromIds();
bool hasColors = false;
nanogui::ComboBox* shadingBtn;
unsigned int smoothingIterations;
nanogui::Slider* sldSmoothingStrength;
unsigned int stripificationTrials;
HEMesh polymesh;
MeshRenderer renderer;
OpenMesh::FPropHandleT<int> faceIdProperty;
OpenMesh::FPropHandleT<Eigen::Vector4f> faceColorProperty;
};

View file

@ -0,0 +1,12 @@
// 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
#pragma once
#include "util/OpenMeshUtils.h"
//computes the surface area of the halfedge mesh m by computing and summing up the
//areas of all polygonal faces
//method is not restricted to triangle meshes
float ComputeVolume(const HEMesh& m);

View file

@ -0,0 +1,109 @@
//
// This source code is property of the Computer Graphics and Visualization
// chair of the TU Dresden. Do not distribute!
// Copyright (C) 2015-2017 CGV TU Dresden - All Rights Reserved
//
#pragma once
#include <vector>
#include <unordered_map>
#include <random>
/*
Usage:
//C++0x Random Engine Mersenne Twister
std::mt19937 eng;
//a set data structure with amortize constant time insertion, removal, and uniform random sampling from its elements
sample_set<int> my_set;
my_set.reserve(3);
my_set.insert(1);
my_set.insert(2);
my_set.insert(6);
int rand_elem1 = my_set.sample(eng);//1,2 or 6
my_set.remove(2);
int rand_elem2 = my_set.sample(eng); //1 or 6
*/
template <typename T>
struct sample_set
{
typedef int element_index;
std::vector<T> elements;
std::unordered_map<T,element_index> index_lut;
//create empty set
sample_set(){}
//reserve memory for n elements
void reserve(size_t n)
{
elements.reserve(n);
index_lut.rehash(n);
}
//insert element elem into set
void insert(const T& elem)
{
//guard against duplicates
if (index_lut.find(elem) == index_lut.end())
{
element_index idx = (element_index)elements.size();
elements.push_back(elem);
index_lut[elem]=idx;
}
}
//remove element elem from set
bool remove(const T& elem)
{
auto it = index_lut.find(elem);
if(it == index_lut.end())
{
return false;
}
int i = it->second;
std::swap(elements[i],elements.back());
elements.pop_back();
index_lut.erase(it);
if(unsigned(i) < elements.size())
{
index_lut[elements[i]]=i;
}
return true;
}
//draw a sample from set
template <typename Engine>
const T& sample(Engine& eng)
{
int b = (element_index)(elements.size()-1);
std::uniform_int_distribution<int> uniform_dist(0,b);
element_index idx = uniform_dist(eng);
return elements[idx];
}
//returns number of elements in set
size_t size() const
{
return elements.size();
}
//returns true if set is empty
bool empty() const
{
return elements.empty();
}
};

View file

@ -0,0 +1,307 @@
// 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 "Primitives.h"
#include "util/OpenMeshUtils.h"
void CreateQuad(HEMesh& mesh)
{
mesh.clear();
std::vector<OpenMesh::VertexHandle> vhandles;
vhandles.push_back(mesh.add_vertex(OpenMesh::Vec3f(-0.5f, -0.5f, 0)));
vhandles.push_back(mesh.add_vertex(OpenMesh::Vec3f( 0.5f, -0.5f, 0)));
vhandles.push_back(mesh.add_vertex(OpenMesh::Vec3f( 0.5f, 0.5f, 0)));
vhandles.push_back(mesh.add_vertex(OpenMesh::Vec3f(-0.5f, 0.5f, 0)));
mesh.add_face(vhandles);
}
void CreateCube(HEMesh& mesh)
{
mesh.clear();
/* Task 2.2.1 */
}
void CreateTetrahedron(HEMesh& mesh, float a)
{
mesh.clear();
std::vector<OpenMesh::VertexHandle> vhandles(4);
vhandles[0] = mesh.add_vertex(OpenMesh::Vec3f(sqrt(3.0f)*a / 3.0f, 0, 0));
vhandles[1] = mesh.add_vertex(OpenMesh::Vec3f(-sqrt(3.0f)*a / 6.0f, -a / 2.0f, 0));
vhandles[2] = mesh.add_vertex(OpenMesh::Vec3f(-sqrt(3.0f)*a / 6.0f, a / 2.0f, 0));
vhandles[3] = mesh.add_vertex(OpenMesh::Vec3f(0, 0, sqrt(6.0f)*a / 3.0f));
mesh.add_face(vhandles[0], vhandles[1], vhandles[2]);
mesh.add_face(vhandles[0], vhandles[2], vhandles[3]);
mesh.add_face(vhandles[0], vhandles[3], vhandles[1]);
mesh.add_face(vhandles[3], vhandles[2], vhandles[1]);
}
void CreateDisk(HEMesh& mesh, float radius, int slices)
{
mesh.clear();
std::vector<OpenMesh::VertexHandle> vhandles(slices + 1);
vhandles[0] = mesh.add_vertex(OpenMesh::Vec3f(0, 0, 0));
for (int i = 0; i < slices; i++)
{
float angle = -i * 2 * 3.14159f / slices;
vhandles[i + 1] = mesh.add_vertex(OpenMesh::Vec3f(sin(angle)*radius, cos(angle)*radius, 0));
}
for (int i = 0; i < slices; i++)
mesh.add_face(vhandles[0], vhandles[1 + i%slices], vhandles[1 + (1 + i) % slices]);
}
//create a cylinder mesh
void CreateCylinder(HEMesh& mesh, float radius, float height, int stacks, int slices)
{
assert(slices >= 3 && stacks >= 1);
mesh.clear();
int n = 2 + slices*(stacks + 1);
std::vector<OpenMesh::VertexHandle> vhandles(n);
vhandles[0] = mesh.add_vertex(OpenMesh::Vec3f(0.0f, height, 0.0f));
int k = 1;
for (int i = 0; i < stacks + 1; i++)
{
float h = (stacks - i)*height / (stacks);
for (int j = 0; j < slices; j++)
{
float angle2 = j*2.0f*3.14159f / (float)(slices);
vhandles[k] = mesh.add_vertex(OpenMesh::Vec3f(cos(angle2)*radius, h, sin(angle2)*radius));
k++;
}
}
vhandles[k] = mesh.add_vertex(OpenMesh::Vec3f(0.0f, 0.0f, 0.0f));
for (int i = 0; i < slices; i++)
{
mesh.add_face(vhandles[0], vhandles[1 + (1 + i) % slices], vhandles[1 + i%slices]);
for (int j = 0; j < stacks; j++)
{
int a, b, c, d;
a = 1 + j*slices + (i) % slices;
b = 1 + j*slices + (1 + i) % slices;
c = 1 + (j + 1)*slices + (1 + i) % slices;
d = 1 + (j + 1)*slices + (i) % slices;
mesh.add_face(vhandles[a], vhandles[b], vhandles[c], vhandles[d]);
}
mesh.add_face(vhandles[vhandles.size() - 1],
vhandles[1 + (stacks)*slices + (i) % slices],
vhandles[1 + (stacks)*slices + (1 + i) % slices]);
}
}
//create a sphere mesh
void CreateSphere(HEMesh& mesh, float radius, int slices, int stacks)
{
assert(slices >= 3 && stacks >= 3);
mesh.clear();
int n = slices*(stacks - 1) + 2;
std::vector<OpenMesh::VertexHandle> vhandles(n);
vhandles[0] = mesh.add_vertex(OpenMesh::Vec3f(0.0f, radius, 0.0f));
int k = 1;
for (int i = 1; i < stacks; i++)
{
float angle1 = 3.14159f / 2.0f - i*3.14159f / (float)stacks;
float r = cos(angle1)*radius;
float height = sin(angle1)*radius;
for (int j = 0; j < slices; j++)
{
float angle2 = j*2.0f*3.14159f / (float)(slices);
vhandles[k] = mesh.add_vertex(OpenMesh::Vec3f(cos(angle2)*r, height, sin(angle2)*r));
k++;
}
}
vhandles[k] = mesh.add_vertex(OpenMesh::Vec3f(0.0f, -radius, 0.0f));
for (int i = 0; i < slices; i++)
{
mesh.add_face(vhandles[0], vhandles[1 + (1 + i) % slices], vhandles[1 + i%slices]);
for (int j = 0; j < stacks - 2; j++)
{
int a, b, c, d;
a = 1 + j*slices + (i) % slices;
b = 1 + j*slices + (1 + i) % slices;
c = 1 + (j + 1)*slices + (1 + i) % slices;
d = 1 + (j + 1)*slices + (i) % slices;
mesh.add_face(vhandles[a], vhandles[b], vhandles[c], vhandles[d]);
}
mesh.add_face(vhandles[1 + slices*(stacks - 1)],
vhandles[1 + (stacks - 2)*slices + (i) % slices],
vhandles[1 + (stacks - 2)*slices + (1 + i) % slices]);
}
}
//create a torus mesh
void CreateTorus(HEMesh& mesh, float r, float R, int nsides, int rings)
{
assert(nsides >= 3 && rings >= 3);
mesh.clear();
int n = rings*nsides;
std::vector<OpenMesh::VertexHandle> vhandles(n);
int k = 0;
for (int i = 0; i < rings; i++)
{
float angle1 = (float)(i*2.0*3.14159 / (rings));
OpenMesh::Vec3f center(cos(angle1)*R, 0.0f, sin(angle1)*R);
OpenMesh::Vec3f t1(cos(angle1), 0.0, sin(angle1));
OpenMesh::Vec3f t2(0.0f, 1.0f, 0.0f);
for (int j = 0; j < nsides; j++)
{
float angle2 = (float)(j*2.0*3.14159 / (nsides));
vhandles[k] = mesh.add_vertex(center + (float)(sin(angle2)*r)*t1 + (float)(cos(angle2)*r)*t2);
k++;
}
}
for (int i = 0; i < rings; i++)
{
for (int j = 0; j < nsides; j++)
{
int a, b, c, d;
a = (i + 1) % (rings)*(nsides)+j;
b = (i + 1) % (rings)*(nsides)+(j + 1) % (nsides);
c = i*(nsides)+(j + 1) % (nsides);
d = i*(nsides)+j;
mesh.add_face(
vhandles[a], vhandles[b],
vhandles[c], vhandles[d]);
}
}
}
//creates an icosahedron mesh in m
// radius is the radius of the circum sphere
void CreateIcosahedron(HEMesh& mesh, float radius)
{
mesh.clear();
float a = (float)(radius*4.0 / sqrt(10.0 + 2.0*sqrt(5.0)));
float h = (float)cos(2.0*asin(a / (2.0*radius)))*radius;
float r2 = (float)sqrt(radius*radius - h*h);
std::vector<OpenMesh::VertexHandle> vhandles(12);
int k = 0;
vhandles[k++] = mesh.add_vertex(OpenMesh::Vec3f(0, radius, 0));
for (int i = 0; i < 5; i++)
vhandles[k++] = mesh.add_vertex(OpenMesh::Vec3f((float)cos(i*72.0*3.14159 / 180.0)*r2, h, -(float)sin(i*72.0*3.14159 / 180.0)*r2));
for (int i = 0; i < 5; i++)
vhandles[k++] = mesh.add_vertex(OpenMesh::Vec3f((float)cos(36.0*3.14159 / 180.0 + i*72.0*3.14159 / 180.0)*r2, -h, -(float)sin(36.0*3.14159 / 180.0 + i*72.0*3.14159 / 180.0)*r2));
vhandles[k] = mesh.add_vertex(OpenMesh::Vec3f(0, -radius, 0));
for (int i = 0; i < 5; i++)
{
mesh.add_face(vhandles[0], vhandles[i + 1], vhandles[(i + 1) % 5 + 1]);
mesh.add_face(vhandles[11], vhandles[(i + 1) % 5 + 6], vhandles[i + 6]);
mesh.add_face(vhandles[i + 1], vhandles[i + 6], vhandles[(i + 1) % 5 + 1]);
mesh.add_face(vhandles[(i + 1) % 5 + 1], vhandles[i + 6], vhandles[(i + 1) % 5 + 6]);
}
}
//creates an octaeder mesh
// radius is the radius of the circum sphere
void CreateOctahedron(HEMesh& mesh, float radius)
{
mesh.clear();
std::vector<OpenMesh::VertexHandle> vhandles(6);
int k = 0;
vhandles[k++] = mesh.add_vertex(OpenMesh::Vec3f(0, radius, 0));
for (int i = 0; i < 4; i++)
vhandles[k++] = mesh.add_vertex(OpenMesh::Vec3f((float)cos(i*3.14159 / 2.0)*radius, 0, -(float)sin(i*3.14159 / 2.0)*radius));
vhandles[k++] = mesh.add_vertex(OpenMesh::Vec3f(0, -radius, 0));
for (int i = 0; i < 4; i++)
{
mesh.add_face(vhandles[0], vhandles[i + 1], vhandles[(i + 1) % 4 + 1]);
mesh.add_face(vhandles[5], vhandles[(i + 1) % 4 + 1], vhandles[i + 1]);
}
}
//create a unit arrow
void CreateUnitArrow(HEMesh& mesh, float stem_radius, float head_radius, float stem_height, int slices, int stem_stacks)
{
assert(slices >= 3 && stem_stacks >= 1 && stem_height <= 1 && stem_height >= 0);
float head_height = (float)1 - stem_height;
mesh.clear();
int n = 2 + slices*(stem_stacks + 2);
std::vector<OpenMesh::VertexHandle> vhandles(n);
float height = stem_height + head_height;
vhandles[0] = mesh.add_vertex(OpenMesh::Vec3f(0.0f, height, 0.0f));
int k = 1;
for (int j = 0; j < slices; j++)
{
float angle2 = j*2.0f*3.14159f / (float)(slices);
vhandles[k] = mesh.add_vertex(OpenMesh::Vec3f(cos(angle2)*head_radius, stem_height, sin(angle2)*head_radius));
k++;
}
for (int i = 0; i < stem_stacks + 1; i++)
{
float h = (stem_stacks - i)*stem_height / (stem_stacks);
for (int j = 0; j < slices; j++)
{
float angle2 = j*2.0f*3.14159f / (float)(slices);
vhandles[k] = mesh.add_vertex(OpenMesh::Vec3f(cos(angle2)*stem_radius, h, sin(angle2)*stem_radius));
k++;
}
}
vhandles[k] = mesh.add_vertex(OpenMesh::Vec3f(0.0f, 0.0f, 0.0f));
for (int i = 0; i < slices; i++)
{
mesh.add_face(vhandles[0], vhandles[1 + (1 + i) % slices], vhandles[1 + i%slices]);
for (int j = 0; j < stem_stacks + 1; j++)
{
int a, b, c, d;
a = 1 + j*slices + (i) % slices;
b = 1 + j*slices + (1 + i) % slices;
c = 1 + (j + 1)*slices + (1 + i) % slices;
d = 1 + (j + 1)*slices + (i) % slices;
mesh.add_face(vhandles[a], vhandles[b], vhandles[c], vhandles[d]);
}
mesh.add_face(vhandles[vhandles.size() - 1],
vhandles[1 + (stem_stacks + 1)*slices + (i) % slices],
vhandles[1 + (stem_stacks + 1)*slices + (1 + i) % slices]);
}
}

View file

@ -0,0 +1,17 @@
// 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 "ShellExtraction.h"
#include <queue>
unsigned int ExtractShells(HEMesh& m, OpenMesh::FPropHandleT<int> perFaceShellIndex)
{
//reset the shell indices to -1 for every face
for (auto f : m.faces())
m.property(perFaceShellIndex, f) = -1;
/*Task 2.2.3*/
return 0;
}

View file

@ -0,0 +1,29 @@
// 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 "Smoothing.h"
#include <random>
void SmoothUniformLaplacian(HEMesh& m, float lambda, unsigned int iterations)
{
/*Task 2.2.4*/
}
void AddNoise(HEMesh& m)
{
std::mt19937 rnd;
std::normal_distribution<float> dist;
for (auto v : m.vertices())
{
OpenMesh::Vec3f n;
m.calc_vertex_normal_correct(v, n); //normal scales with area
float areaScale = n.norm();
float lengthScale = sqrt(areaScale);
n = lengthScale / areaScale * n;
m.point(v) += 0.1f * dist(rnd) * n;
}
}

View file

@ -0,0 +1,27 @@
// 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 "Stripification.h"
#include <random>
#include "sample_set.h"
unsigned int ExtractTriStrips(HEMesh& mesh, OpenMesh::FPropHandleT<int> perFaceStripIdProperty, unsigned int nTrials)
{
//prepare random engine
std::mt19937 eng;
//initialize strip index to -1 for each face
for (auto f : mesh.faces())
mesh.property(perFaceStripIdProperty, f) = -1;
int nStrips = 0;
/*Task 2.2.5*/
return nStrips;
}

View file

@ -0,0 +1,15 @@
// 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 "SurfaceArea.h"
#include <iostream>
float ComputeSurfaceArea(const HEMesh& m)
{
float area = 0;
/* Task 2.2.2 */
std::cout << "Area computation is not implemented." << std::endl;
return area;
}

255
exercise2/src/Viewer.cpp Normal file
View file

@ -0,0 +1,255 @@
// 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/popupbutton.h>
#include <nanogui/layout.h>
#include <nanogui/combobox.h>
#include <iostream>
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <gui/SliderHelper.h>
#include "Primitives.h"
#include "SurfaceArea.h"
#include "Volume.h"
#include "ShellExtraction.h"
#include "Smoothing.h"
#include "Stripification.h"
const int segmentColorCount = 12;
const float segmentColors[segmentColorCount][3] =
{
{ 0.651f, 0.808f, 0.890f },
{ 0.122f, 0.471f, 0.706f },
{ 0.698f, 0.875f, 0.541f },
{ 0.200f, 0.627f, 0.173f },
{ 0.984f, 0.604f, 0.600f },
{ 0.890f, 0.102f, 0.110f },
{ 0.992f, 0.749f, 0.435f },
{ 1.000f, 0.498f, 0.000f },
{ 0.792f, 0.698f, 0.839f },
{ 0.416f, 0.239f, 0.604f },
{ 1.000f, 1.000f, 0.600f },
{ 0.694f, 0.349f, 0.157f },
};
Viewer::Viewer()
: AbstractViewer("CG1 Exercise 2"),
renderer(polymesh)
{
SetupGUI();
polymesh.add_property(faceIdProperty);
polymesh.add_property(faceColorProperty);
}
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(true);
}
});
auto primitiveBtn = new nanogui::PopupButton(mainWindow, "Create Primitive");
primitiveBtn->popup()->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
auto quadBtn = new nanogui::Button(primitiveBtn->popup(), "Quad");
quadBtn->setCallback([this]() { CreateQuad(polymesh); MeshUpdated(true); });
auto diskBtn = new nanogui::Button(primitiveBtn->popup(), "Disk");
diskBtn->setCallback([this]() { CreateDisk(polymesh, 1, 20); MeshUpdated(true); });
auto tetBtn = new nanogui::Button(primitiveBtn->popup(), "Tetrahedron");
tetBtn->setCallback([this]() { CreateTetrahedron(polymesh); MeshUpdated(true); });
auto octaBtn = new nanogui::Button(primitiveBtn->popup(), "Octahedron");
octaBtn->setCallback([this]() { CreateOctahedron(polymesh, 1); MeshUpdated(true); });
auto cubeBtn = new nanogui::Button(primitiveBtn->popup(), "Cube");
cubeBtn->setCallback([this]() { CreateCube(polymesh); MeshUpdated(true); });
auto icoBtn = new nanogui::Button(primitiveBtn->popup(), "Icosahedron");
icoBtn->setCallback([this]() { CreateIcosahedron(polymesh, 1); MeshUpdated(true); });
auto cylBtn = new nanogui::Button(primitiveBtn->popup(), "Cylinder");
cylBtn->setCallback([this]() { CreateCylinder(polymesh, 0.3f, 1, 20, 10); MeshUpdated(true); });
auto sphereBtn = new nanogui::Button(primitiveBtn->popup(), "Sphere");
sphereBtn->setCallback([this]() { CreateSphere(polymesh, 1, 20, 20); MeshUpdated(true); });
auto torusBtn = new nanogui::Button(primitiveBtn->popup(), "Torus");
torusBtn->setCallback([this]() { CreateTorus(polymesh, 0.4f, 1, 20, 20); MeshUpdated(true); });
auto arrowBtn = new nanogui::Button(primitiveBtn->popup(), "Arrow");
arrowBtn->setCallback([this]() { CreateUnitArrow(polymesh); MeshUpdated(true); });
auto calcAreaBtn = new nanogui::Button(mainWindow, "Calculate Mesh Area");
calcAreaBtn->setCallback([this]() {
auto area = ComputeSurfaceArea(polymesh);
std::stringstream ss;
ss << "The mesh has an area of " << area << ".";
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Information, "Surface Area",
ss.str());
});
auto calcVolBtn = new nanogui::Button(mainWindow, "Calculate Mesh Volume");
calcVolBtn->setCallback([this]() {
//Triangulate the mesh if it is not a triangle mesh
for (auto f : polymesh.faces())
{
if (polymesh.valence(f) > 3)
{
std::cout << "Triangulating mesh." << std::endl;
polymesh.triangulate();
MeshUpdated();
break;
}
}
auto vol = ComputeVolume(polymesh);
std::stringstream ss;
ss << "The mesh has a volume of " << vol << ".";
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Information, "Volume",
ss.str());
});
auto extractShellsBtn = new nanogui::Button(mainWindow, "Extract Shells");
extractShellsBtn->setCallback([this]() {
auto count = ExtractShells(polymesh, faceIdProperty);
std::stringstream ss;
ss << "The mesh has " << count << " shells.";
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Information, "Shell Extraction",
ss.str());
ColorMeshFromIds();
});
auto noiseBtn = new nanogui::Button(mainWindow, "Add Noise");
noiseBtn->setCallback([this]() { AddNoise(polymesh); MeshUpdated(); });
nanogui::TextBox* txtSmoothingIterations;
auto sldSmoothingIterations = nse::gui::AddLabeledSlider(mainWindow, "Smoothing Iterations", std::make_pair(1, 100), 20, txtSmoothingIterations);
sldSmoothingIterations->setCallback([this, txtSmoothingIterations](float value)
{
smoothingIterations = (unsigned int)std::round(value);
txtSmoothingIterations->setValue(std::to_string(smoothingIterations));
});
sldSmoothingIterations->callback()(sldSmoothingIterations->value());
sldSmoothingStrength = nse::gui::AddLabeledSliderWithDefaultDisplay(mainWindow, "Smoothing Strength", std::make_pair(0.0f, 1.0f), 0.1f, 2);
auto smoothBtn = new nanogui::Button(mainWindow, "Laplacian Smoothing");
smoothBtn->setCallback([this]() {
SmoothUniformLaplacian(polymesh, sldSmoothingStrength->value(), smoothingIterations);
MeshUpdated();
});
nanogui::TextBox* txtStripificationTrials;
auto sldStripificationTrials = nse::gui::AddLabeledSlider(mainWindow, "Stripification Trials", std::make_pair(1, 50), 20, txtStripificationTrials);
sldStripificationTrials->setCallback([this, txtStripificationTrials](float value)
{
stripificationTrials = (unsigned int)std::round(value);
txtStripificationTrials->setValue(std::to_string(stripificationTrials));
});
sldStripificationTrials->callback()(sldStripificationTrials->value());
auto stripifyBtn = new nanogui::Button(mainWindow, "Extract Triangle Strips");
stripifyBtn->setCallback([this]() {
//Triangulate the mesh if it is not a triangle mesh
for (auto f : polymesh.faces())
{
if (polymesh.valence(f) > 3)
{
std::cout << "Triangulating mesh." << std::endl;
polymesh.triangulate();
MeshUpdated();
break;
}
}
auto count = ExtractTriStrips(polymesh, faceIdProperty, stripificationTrials);
std::stringstream ss;
ss << "The mesh has " << count << " triangle strips.";
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Information, "Shell Extraction",
ss.str());
ColorMeshFromIds();
});
shadingBtn = new nanogui::ComboBox(mainWindow, { "Smooth Shading", "Flat Shading" });
performLayout();
}
void Viewer::ColorMeshFromIds()
{
//Set face colors
for (auto f : polymesh.faces())
{
auto shell = polymesh.property(faceIdProperty, f);
if (shell < 0)
polymesh.property(faceColorProperty, f) = Eigen::Vector4f(0, 0, 0, 1);
else
{
auto& color = segmentColors[shell % segmentColorCount];
polymesh.property(faceColorProperty, f) = Eigen::Vector4f(color[0], color[1], color[2], 1);
}
}
hasColors = true;
MeshUpdated();
}
void Viewer::MeshUpdated(bool initNewMesh)
{
if (initNewMesh)
{
hasColors = false;
//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);
}
if (hasColors)
renderer.UpdateWithPerFaceColor(faceColorProperty);
else
renderer.Update();
}
void Viewer::drawContents()
{
glEnable(GL_DEPTH_TEST);
Eigen::Matrix4f view, proj;
camera().ComputeCameraMatrices(view, proj);
renderer.Render(view, proj, shadingBtn->selectedIndex() == 1);
}

15
exercise2/src/Volume.cpp Normal file
View file

@ -0,0 +1,15 @@
// 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 "Volume.h"
#include <iostream>
float ComputeVolume(const HEMesh& m)
{
float vol = 0;
/*Task 2.2.2*/
std::cout << "Volume calculation is not implemented." << std::endl;
return vol;
}

36
exercise2/src/main.cpp Normal file
View file

@ -0,0 +1,36 @@
// 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 <iostream>
#include <util/GLDebug.h>
#include "Viewer.h"
int main(int argc, char* argv[])
{
nanogui::init();
{
nanogui::ref<Viewer> viewer = new Viewer();
viewer->setVisible(true);
nse::util::GLDebug::SetupDebugCallback();
nse::util::GLDebug::IgnoreGLError(131185); //buffer usage info
try
{
nanogui::mainloop();
}
catch (std::runtime_error& e)
{
std::cerr << e.what() << std::endl;
}
}
nanogui::shutdown();
return 0;
}

16
exercise3/CMakeLists.txt Normal file
View file

@ -0,0 +1,16 @@
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include )
add_executable(Exercise3 MACOSX_BUNDLE
src/main.cpp
src/Viewer.cpp include/Viewer.h
src/AABBTree.cpp include/AABBTree.h
src/Box.cpp include/Box.h
src/LineSegment.cpp include/LineSegment.h
src/Point.cpp include/Point.h
src/Triangle.cpp include/Triangle.h
include/GridUtils.h
src/HashGrid.cpp include/HashGrid.h
src/GridTraverser.cpp include/GridTraverser.h
)
target_link_libraries(Exercise3 CG1Common ${LIBS})

View file

@ -0,0 +1,513 @@
// 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
#pragma once
#include <queue>
#include <utility>
#include <util/OpenMeshUtils.h>
#include "Box.h"
#include "Triangle.h"
#include "LineSegment.h"
#include "Point.h"
/**
* Axis aligned bounding volume hierachy data structure.
*/
template <typename Primitive>
class AABBTree
{
public:
typedef std::vector<Primitive> primitive_list;
//iterator type pointing inside the primitive list
typedef typename primitive_list::iterator PrimitiveIterator;
//const iterator type pointing inside the primitive list
typedef typename primitive_list::const_iterator const_primitive_iterator;
//abstract base class defining the common interface of all aabb tree node
class AABBNode
{
protected:
//storage of bounding box assosiated with aabb_node
Box bounds;
public:
AABBNode() {
}
AABBNode(const Box& b): bounds(b) {
}
//returns the bounding box of the node
Box GetBounds() const
{
return bounds;
}
virtual int NumPrimitives() const = 0;
//this method must be implemented to return true for a leaf node and false for a non_lef node
virtual bool IsLeaf() const = 0;
//virtual destructor
virtual ~AABBNode() {}
};
///a class representing a leaf node of an aabb tree (non split node)
class AABBLeafNode: public AABBNode
{
//internal storage to the range (begin and end pointer) of the primitives associated with the current leaf node
PrimitiveIterator primitivesBegin, primitivesEnd;
public:
//construct a leaf node from
AABBLeafNode(const PrimitiveIterator& primitivesBegin,
const PrimitiveIterator& primitivesEnd,
const Box& b):
primitivesBegin(primitivesBegin),primitivesEnd(primitivesEnd), AABBNode(b)
{
}
//return always true because its a leaf node
bool IsLeaf() const
{
return true;
}
//returns the number primitives assosiated with the current leaf
int NumPrimitives() const
{
return (int)(primitivesEnd-primitivesBegin);
}
const_primitive_iterator begin() const
{
return primitivesBegin;
}
const_primitive_iterator end() const
{
return primitivesEnd;
}
};
///a class representing a split node of an aabb tree (non leaf node)
class AABBSplitNode: public AABBNode
{
//child pointers
AABBNode* children[2];
public:
//default constructor
AABBSplitNode()
{
children[0] = children[1] = nullptr;
}
//construct a split node from given Left and right child pointers and given bounding box b of the node
AABBSplitNode(AABBNode* left_child, AABBNode* right_child,const Box& b): AABBNode(b)
{
children[0] = left_child;
children[1] = right_child;
}
//destructor of node, recursively deleted whole subtree
~AABBSplitNode()
{
if(Left() != nullptr)
delete Left();
if(Right() != nullptr)
delete Right();
}
//returns always false because its a split node
bool IsLeaf() const
{
return false;
}
//returns a pointer to the left child node
AABBNode* Left()
{
return children[0];
}
//returns a pointer to the right child node
AABBNode* Right()
{
return children[1];
}
//returns a const pointer to the left child node
const AABBNode* Left() const
{
return children[0];
}
//returns a const pointer to the right child node
const AABBNode* Right() const
{
return children[1];
}
//counts the number of primitives of the subtree
int NumPrimitives() const
{
return Left()->NumPrimitives() + Right()->NumPrimitives();
}
};
private:
//search entry used internally for nearest and k nearest primitive queries
struct SearchEntry
{
//squared distance to node from query point
float sqrDistance;
//node
const AABBNode* node;
//constructor
SearchEntry(float sqrDistance, const AABBNode* node)
: sqrDistance(sqrDistance), node(node)
{ }
//search entry a < b means a.sqr_distance > b. sqr_distance
bool operator<(const SearchEntry& e) const
{
return sqrDistance > e.sqrDistance;
}
};
//result entry for nearest and k nearest primitive queries
struct ResultEntry
{
//squared distance from query point to primitive
float sqrDistance;
//pointer to primitive
const Primitive* prim;
//default constructor
ResultEntry()
: sqrDistance(std::numeric_limits<float>::infinity()), prim(nullptr)
{ }
//constructor
ResultEntry(float sqrDistance, const Primitive* p)
: sqrDistance(sqrDistance), prim(p)
{ }
//result_entry are sorted by their sqr_distance using this less than operator
bool operator<(const ResultEntry& e) const
{
return sqrDistance < e.sqrDistance;
}
};
//list of all primitives in the tree
primitive_list primitives;
//maximum allowed tree depth to stop tree construction
int maxDepth;
//minimal number of primitives to stop tree construction
int minSize;
//pointer to the root node of the tree
AABBNode *root;
//a flag indicating if the tree is constructed
bool completed;
public:
//returns a pointer to the root node of the tree
AABBNode* Root()
{
assert(IsCompleted());
return root;
}
//returns a const pointer to the root node of the tree
const AABBNode* Root() const
{
assert(IsCompleted());
return root;
}
//constructor of aabb tree
//default maximal tree depth is 20
//default minimal size of a node not to be further subdivided in the cnstruction process is two
AABBTree(int maxDepth=20, int minSize=2):
maxDepth(maxDepth),minSize(minSize),root(nullptr),completed(false)
{
}
//copy constructor
AABBTree(const AABBTree& other)
{
primitives = other.primitives;
maxDepth = other.maxDepth;
minSize = other.minSize;
root = CopyTree(other.primitives,other.root);
completed = other.completed;
}
//move constructor
AABBTree(AABBTree&& other):root(nullptr),completed(false)
{
*this = std::move(other);
}
//copy assignment operator
AABBTree& operator=(const AABBTree& other)
{
if(this != &other)
{
if(root != nullptr)
delete root;
primitives = other.primitives;
maxDepth = other.maxDepth;
minSize = other.minSize;
root = CopyTree(other.primitives,other.root);
completed = other.completed;
}
return *this;
}
//move assign operator
AABBTree& operator=(AABBTree&& other)
{
if(this != &other)
{
std::swap(primitives,other.primitives);
std::swap(maxDepth, other.maxDepth);
std::swap(minSize, other.minSize);
std::swap(root,other.root) ;
std::swap(completed, other.completed);
}
return *this;
}
//remove all primitives from tree
void Clear()
{
primitives.clear();
if(root != nullptr)
{
delete root;
root = nullptr;
}
completed = false;
}
//returns true if tree is empty
bool Empty() const
{
return primitives.Empty();
}
//insert a primitive into internal primitive list
//this method do not construct the tree!
//call the method Complete, after insertion of all primitives
void Insert(const Primitive& p)
{
primitives.push_back(p);
completed=false;
}
//construct the tree from all prior inserted primitives
void Complete()
{
//if tree already constructed -> delete tree
if(root != nullptr)
delete root;
//compute bounding box over all primitives using helper function
Box bounds = ComputeBounds(primitives.begin(),primitives.end());
//initial call to the recursive tree construction method over the whole range of primitives
root = Build(primitives.begin(),primitives.end(),bounds,0);
//set completed flag to true
completed=true;
}
//returns true if the tree can be used for queries
//if the tree is not completed call the method complete()
bool IsCompleted() const
{
return completed;
}
//closest primitive computation via linear search
ResultEntry ClosestPrimitiveLinearSearch(const Eigen::Vector3f& q) const
{
ResultEntry best;
auto pend = primitives.end();
for(auto pit = primitives.begin(); pit != pend; ++pit)
{
float dist = pit->SqrDistance(q);
if(dist < best.sqrDistance)
{
best.sqrDistance = dist;
best.prim = &(*pit);
}
}
return best;
}
//computes the k nearest neighbor primitives via linear search
std::vector<ResultEntry> ClosestKPrimitivesLinearSearch(size_t k, const Eigen::Vector3f& q) const
{
std::priority_queue<ResultEntry> k_best;
Primitive best_p;
auto pend = primitives.end();
for(auto pit = primitives.begin(); pit != pend; ++pit)
{
float dist = pit->SqrDistance(q);
if(k_best.size() < k )
{
k_best.push(ResultEntry(dist,*pit));
return;
}
if(k_best.top().SqrDistance > dist)
{
k_best.pop();
k_best.push(ResultEntry(dist,*pit));
}
}
std::vector<ResultEntry> result(k_best.size());
auto rend = result.end();
for(auto rit = result.begin(); rit != rend; ++rit)
{
*rit = k_best.top();
k_best.pop();
}
return result;
}
//closest k primitive computation
std::vector<ResultEntry> ClosestKPrimitives(size_t k,const Eigen::Vector3f& q) const
{
//student begin
return ClosestKPrimitivesLinearSearch(k,q);
//student end
}
//returns the closest primitive and its squared distance to the point q
ResultEntry ClosestPrimitive(const Eigen::Vector3f& q) const
{
assert(IsCompleted());
if(root == nullptr)
return ResultEntry();
/* Task 3.2.1 */
return ClosestPrimitiveLinearSearch(q);
}
//return the closest point position on the closest primitive in the tree with respect to the query point q
Eigen::Vector3f ClosestPoint(const Eigen::Vector3f& p) const
{
ResultEntry r = ClosestPrimitive(p);
return r.prim->ClosestPoint(p);
}
//return the squared distance between point p and the nearest primitive in the tree
float SqrDistance(const Eigen::Vector3f& p) const
{
ResultEntry r = ClosestPrimitive(p);
return r.SqrDistance;
}
//return the euclidean distance between point p and the nearest primitive in the tree
float Distance(const Eigen::Vector3f& p) const
{
return sqrt(SqrDistance(p));
}
protected:
//helper function to copy a subtree
AABBNode* CopyTree(const primitive_list& other_primitives,AABBNode* node)
{
if(node == nullptr)
return nullptr;
if(node->IsLeaf())
{
AABBLeafNode* leaf = (AABBLeafNode*)node;
return new AABBLeafNode(primitives.begin()+(leaf->primitives.begin()-other_primitives.begin()),
primitives.begin()+(leaf->primitives.end()-other_primitives.begin()));
}
else
{
AABBSplitNode* split = (AABBSplitNode*)node;
return new AABBSplitNode(CopyTree(other_primitives,split->Left()),
CopyTree(other_primitives,split->Right()));
}
}
//helper function to compute an axis aligned bounding box over the range of primitives [begin,end)
Box ComputeBounds(const_primitive_iterator begin,
const_primitive_iterator end)
{
Box bounds;
for(auto pit = begin; pit != end; ++pit)
bounds.Insert(pit->ComputeBounds());
return bounds;
}
//recursive tree construction initially called from method complete()
//build an aabb (sub)-tree over the range of primitives [begin,end),
//the current bounding box is given by bounds and the current tree depth is given by the parameter depth
//if depth >= max_depth or the number of primitives (end-begin) <= min_size a leaf node is constructed
//otherwise split node is created
// to create a split node the range of primitives [begin,end) must be splitted and reordered into two
//sub ranges [begin,mid) and [mid,end),
//therefore sort the range of primitives [begin,end) along the largest bounding box extent by its reference
//point returned by the method ReferencePoint()
//then choose the median element as mid
// the STL routine std::nth_element would be very useful here , you only have to provide a ordering predicate
//compute the boundg boxed of the two resulting sub ranges and recursivly call build on the two subranges
//the resulting subtree are used as children of the resulting split node.
AABBNode* Build(PrimitiveIterator begin, PrimitiveIterator end, Box& bounds, int depth)
{
if(depth >= maxDepth || end-begin <= minSize)
{
return new AABBLeafNode(begin,end,bounds);
}
Eigen::Vector3f e = bounds.Extents();
int axis = 0;
float max_extent = e[0];
if(max_extent < e[1])
{
axis = 1;
max_extent = e[1];
}
if(max_extent < e[2])
{
axis = 2;
max_extent = e[2];
}
PrimitiveIterator mid= begin + (end-begin)/2;
std::nth_element(begin,mid,end,[&axis](const Primitive& a, const Primitive& b)
{ return a.ReferencePoint()[axis] < b.ReferencePoint()[axis];});
Box lbounds = ComputeBounds(begin,mid);
Box rbounds = ComputeBounds(mid,end);
return new AABBSplitNode(Build(begin,mid,lbounds,depth+1),Build(mid,end,rbounds,depth+1),bounds);
}
};
//helper function to construct an aabb tree data structure from the triangle faces of the halfedge mesh m
void BuildAABBTreeFromTriangles(const HEMesh& m, AABBTree<Triangle >& tree);
//helper function to construct an aabb tree data structure from the vertices of the halfedge mesh m
void BuildAABBTreeFromVertices(const HEMesh& m, AABBTree<Point>& tree);
//helper function to construct an aabb tree data structure from the edges of the halfedge mesh m
void BuildAABBTreeFromEdges(const HEMesh& m, AABBTree<LineSegment>& tree);

81
exercise3/include/Box.h Normal file
View file

@ -0,0 +1,81 @@
// 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
#pragma once
#include <Eigen/Core>
class Box
{
//internal storage for lower and upper corner points of the box
std::pair<Eigen::Vector3f, Eigen::Vector3f> bounds;
public:
//creates an empty box like the method Clear
Box();
//construct a box with the gven lower and upper corner points
Box(const Eigen::Vector3f& lbound, const Eigen::Vector3f& ubound);
//returns the corner point which is the lower bound of the box in all dimensions
Eigen::Vector3f& LowerBound();
//returns the corner point which is the lower bound of the box in all dimensions
const Eigen::Vector3f& LowerBound() const;
//returns the corner point which is the upper bound of the box in all dimensions
Eigen::Vector3f& UpperBound();
//returns the corner point which is the upper bound of the box in all dimensions
const Eigen::Vector3f& UpperBound() const;
//returns a vector containing the extents of the box in all dimensions
Eigen::Vector3f Extents() const;
//returns a vector containing the extents of the box in all dimensions divided by 2
Eigen::Vector3f HalfExtents() const;
//returns the center of the box
Eigen::Vector3f Center() const;
//returns the surface area of the box
float SurfaceArea() const;
//returns the volume of the box
float Volume() const;
//returns the box radius for a given direction vector a
//if the box is centered at the origin
//then the projection of the box on direction a is contained within the Interval [-r,r]
float Radius(const Eigen::Vector3f& a) const;
//returns true if the box b overlaps with the current one
bool Overlaps(const Box& b) const;
//returns true if the point p is inside this box
bool IsInside(const Eigen::Vector3f& p) const;
//returns true if box b is inside this box
bool IsInside(const Box& b) const;
//creates a box which goes from [+infinity,- infinity] in al dimensions
void Clear();
//enlarges the box such that the point p is inside afterwards
void Insert(const Eigen::Vector3f& p);
//enlarges the box such that box b is inside afterwards
void Insert(const Box& b);
//returns the point on or inside the box with the smallest distance to p
Eigen::Vector3f ClosestPoint(const Eigen::Vector3f& p) const;
//returns the squared distance between p and the box
float SqrDistance(const Eigen::Vector3f& p) const;
//returns the euclidean distance between p and the box
float Distance(const Eigen::Vector3f& p) const;
};

View file

@ -0,0 +1,51 @@
// 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
#pragma once
#include <array>
#include <Eigen/Core>
class GridTraverser
{
//ray origin and direction
Eigen::Vector3f orig,dir;
//grid cell extents
Eigen::Vector3f cellExtents;
//current cell index
Eigen::Vector3i current;
/* you can additional attributes for incremental calculation here */
public:
//default constructor
GridTraverser();
//constructs a grid traverser for a given ray with origin o, and ray direction d for a grid with cell extents ce
GridTraverser(const Eigen::Vector3f& o, const Eigen::Vector3f&d, const Eigen::Vector3f ce);
//accessor of ray origin
Eigen::Vector3f& Origin();
//const accessor of ray origin
const Eigen::Vector3f& Origin() const;
//accessor of ray direction
Eigen::Vector3f& Direction();
//const accessor of ray direction
const Eigen::Vector3f& Direction() const;
//set cell extents
void SetCellExtents(const Eigen::Vector3f& cellExtent);
//init at origin cell
void Init();
//step to next cell along ray direction
void operator++(int);
//return current cell index
Eigen::Vector3i operator*();
};

View file

@ -0,0 +1,34 @@
// 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
#pragma once
#include <array>
#include <Eigen/Core>
//converts 3d floating point position pos into 3d integer grid cell index
inline Eigen::Vector3i PositionToCellIndex(const Eigen::Vector3f& pos, const Eigen::Vector3f& cellExtents)
{
Eigen::Vector3i idx;
for(int d = 0; d < 3; ++d)
{
idx[d] = int(pos[d]/cellExtents[d]);
if (pos[d] < 0)
--idx[d];
}
return idx;
}
//returns true if the two Interval [lb1,ub2] and [lb2,ub2] overlap
inline bool OverlapIntervals(float lb1, float ub1, float lb2, float ub2)
{
if(lb1 > ub1)
std::swap(lb1,ub1);
if(lb2 > ub2)
std::swap(lb2,ub2);
if(ub1 < lb2|| lb1 >ub2)
return false;
return true;
}

View file

@ -0,0 +1,264 @@
// 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
#pragma once
#include <unordered_map>
#include <array>
#include <vector>
#include "Box.h"
#include "GridUtils.h"
#include "Triangle.h"
#include "Point.h"
#include "LineSegment.h"
template <typename Primitive >
class HashGrid
{
public:
//hash function
struct GridHashFunc
{
size_t operator()(const Eigen::Vector3i &idx ) const
{
static const int p1 = 131071;
static const int p2 = 524287;
static const int p3 = 8191;
return idx[0] * p1 + idx[1] * p2 + idx[2] * p3;
}
};
//type of internal hash map
typedef std::unordered_map<Eigen::Vector3i,std::vector<Primitive>,GridHashFunc> CellHashMapType;
private:
//internal hash map storing the data of each non empty grid cell
//it is a map with a 3 dimensional cell index index as a key and a std::vector<Primitive> as value type
CellHashMapType cellHashMap;
//internal extents of a cell
Eigen::Vector3f cellExtents;
public:
//constructor for hash grid with uniform cell extent
//initial size is used to preallocate memory for the internal unordered map
HashGrid(const float cellExtent=0.01,const int initialSize=1): cellHashMap(initialSize)
{
cellExtents[0] =cellExtents[1] =cellExtents[2] = cellExtent;
}
//constructor for hash grid with non uniform cell extents
//initial size is used to preallocate memory for the internal unordered map
HashGrid(const Eigen::Vector3f& cellExtents,const int initialSize): cellHashMap(initialSize),cellExtents(cellExtents)
{
}
//resize hash map with at least count buckets
void ReHash(const int count)
{
cellHashMap.rehash(count);
}
//converts a position to a grid index
Eigen::Vector3i PositionToIndex(const Eigen::Vector3f& pos) const
{
return PositionToCellIndex(pos, cellExtents) ;
}
//return the center position of a cell specified by its cell key
Eigen::Vector3f CellCenter(const Eigen::Vector3i& idx) const
{
Eigen::Vector3f p;
for(int d = 0; d < 3; ++d)
p[d] = (idx[d] + 0.5f)*cellExtents[d];
return p;
}
//return the center position of a cell containing give position pos
Eigen::Vector3f CellCenter(const Eigen::Vector3f& pos) const
{
return CellCenter(PositionToIndex(pos));
}
//return the min corner position of a cell specified by its cell key
Eigen::Vector3f CellMinPosition(const Eigen::Vector3i& key) const
{
Eigen::Vector3f p;
for(int d = 0; d < 3; ++d)
p[d] = key[d]*cellExtents[d];
return p;
}
//return the min corner position of a cell containing the point pos
Eigen::Vector3f CellMinPosition(const Eigen::Vector3f& pos) const
{
return CellMinPosition(PositionToIndex(pos));
}
//return the max corner position of a cell specified by its cell key
Eigen::Vector3f CellMaxPosition(const Eigen::Vector3i& idx) const
{
Eigen::Vector3f p;
for(int d = 0; d < 3; ++d)
p[d] = (idx[d]+1)*cellExtents[d];
return p;
}
//return the max corner position of a cell containing the point pos
Eigen::Vector3f CellMaxPosition(const Eigen::Vector3f& pos) const
{
return CellMaxPosition(PositionToIndex(pos));
}
//returns bounding box of cell with index idx
Box CellBounds(const Eigen::Vector3i& idx) const
{
return Box(CellMinPosition(idx),CellMaxPosition(idx));
}
//returns the bounding box of cell containing the point pos
Box CellBounds(const Eigen::Vector3f& pos) const
{
Eigen::Vector3i idx = PositionToIndex(pos);
return Box(CellMinPosition(idx),CellMaxPosition(idx));
}
//returns the extents of a grid cell
Eigen::Vector3f CellExtents() const
{
return cellExtents;
}
//returns volume of a grid cell
float CellVolume() const
{
float vol = 0;
for(int d = 0; d < 3; ++d)
vol *= cellExtents[d];
return vol;
}
//removes all non empty cells from the hash grid
bool Empty(const Eigen::Vector3i& idx) const
{
auto it = cellHashMap.find(idx);
if(it == cellHashMap.end())
return true;
return false;
}
//inserts primitive p into all overlapping hash grid cells
//the primitive must implement a method "box compute_bounds()" which returns an axis aligned bounding box
//and a method "bool overlaps(const box& b)" which returns true if the primitive overlaps the given box b
void Insert(const Primitive& p)
{
Box b = p.ComputeBounds();
Eigen::Vector3f lb = b.LowerBound();
Eigen::Vector3f ub = b.UpperBound();
if(lb[0] > ub[0])
return;
if(lb[1] > ub[1])
return;
if(lb[2] > ub[2])
return;
Eigen::Vector3i lb_idx = PositionToIndex(lb);
Eigen::Vector3i ub_idx = PositionToIndex(ub);
Eigen::Vector3i idx;
for(idx[0] = lb_idx[0]; idx[0] <=ub_idx[0]; ++idx[0])
for(idx[1] = lb_idx[1]; idx[1] <=ub_idx[1]; ++idx[1])
for(idx[2] = lb_idx[2]; idx[2] <=ub_idx[2]; ++idx[2])
if(p.Overlaps(CellBounds(idx)))
cellHashMap[idx].push_back(p);
}
//remove all cells from hash grid
void Clear()
{
cellHashMap.clear();
}
//returns true if hashgrid contains no cells
bool Empty() const
{
return cellHashMap.empty();
}
//returns the number of non empty cells
size_t NumCells() const
{
return cellHashMap.size();
}
//iterator pointing to the first cell within the hashgrid
typename CellHashMapType::iterator NonEmptyCellsBegin()
{
return cellHashMap.begin();
}
//iterator pointing behind the last cell within the hashgrid
typename CellHashMapType::iterator NonEmptyCellsEnd()
{
return cellHashMap.end();
}
//const iterator pointing to the first cell within the hashgrid
typename CellHashMapType::const_iterator NonEmptyCellsBegin() const
{
return cellHashMap.begin();
}
//const iterator pointing behind the last cell within the hashgrid
typename CellHashMapType::const_iterator NonEmptyCellsEnd() const
{
return cellHashMap.end();
}
//iterator pointing to the first primitive stored in the cell idx
typename std::vector<Primitive>::iterator PrimitivesBegin(const Eigen::Vector3i& idx)
{
assert(!Empty(idx));
return cellHashMap[idx].begin();
}
//iterator pointing after the last primitive stored in the cell idx
typename std::vector<Primitive>::iterator PrimitivesEnd(const Eigen::Vector3i& idx)
{
assert(!Empty(idx));
return cellHashMap[idx].end();
}
//const iterator pointing to the first primitive stored in the cell idx
typename std::vector<Primitive>::const_iterator PrimitivesBegin(const Eigen::Vector3i& idx) const
{
assert(!Empty(idx));
return cellHashMap[idx].cbegin();
}
//const iterator pointing after the last primitive stored in the cell idx
typename std::vector<Primitive>::const_iterator PrimitivesEnd(const Eigen::Vector3i& idx) const
{
assert(!Empty(idx));
return cellHashMap[idx].cend();
}
};
//helper function to construct a hashgrid data structure from the triangle faces of the halfedge mesh m
void BuildHashGridFromTriangles(const HEMesh& m, HashGrid<Triangle>& grid, const Eigen::Vector3f& cellSize);
//helper function to construct a hashgrid data structure from the vertices of the halfedge mesh m
void BuildHashGridFromVertices(const HEMesh& m, HashGrid<Point>& grid, const Eigen::Vector3f& cellSize);
//helper function to construct a hashgrid data structure from the edges of the halfedge mesh m
void BuildHashGridFromEdges(const HEMesh& m, HashGrid<LineSegment >& grid, const Eigen::Vector3f& cellSize);

View file

@ -0,0 +1,52 @@
// 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
#pragma once
#include "Box.h"
#include "GridUtils.h"
#include <util/OpenMeshUtils.h>
/*
a 3d line segment primitive which can be used with the AABBTree and the HashGrid data structure
*/
class LineSegment
{
//internal storage of start point of line segment
Eigen::Vector3f v0;
//internal storage of end point of line segment
Eigen::Vector3f v1;
//internal storage for an edge handle
//this edge handle can be used to optionally identify the edge in a halfedge mesh data structure instance
OpenMesh::EdgeHandle h;
public:
//default constructor
LineSegment();
//constructs a line segment by the two end points v0, v1 without using the edge handle
LineSegment(const Eigen::Vector3f& v0, const Eigen::Vector3f& v1);
//construct a line segment from the edge e of the halfedge mesh m
LineSegment(const HEMesh& m,const OpenMesh::EdgeHandle& e);
//returns an axis aligned bounding box of the line segment
Box ComputeBounds() const;
//returns true if the line segment overlaps the given box b
bool Overlaps(const Box& b) const;
//returns the point with smallest distance topoint p which lies on the line segment
Eigen::Vector3f ClosestPoint(const Eigen::Vector3f& p) const;
//returns the squared distance between point p and the line segment
float SqrDistance(const Eigen::Vector3f& p) const;
//returns the euclidean distance between point p and the line segment
float Distance(const Eigen::Vector3f& p) const;
//returns a reference point which is on the line segment and is used to sort the primitive in the AABB tree construction
Eigen::Vector3f ReferencePoint() const;
};

52
exercise3/include/Point.h Normal file
View file

@ -0,0 +1,52 @@
// 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
#pragma once
#include <util/OpenMeshUtils.h>
#include "Box.h"
class Point
{
//internal storage of point position
Eigen::Vector3f v0;
//internal storage for a vertex handle
//this vertex handle can be used to optionally identify the vertex in a halfedge mesh data structure instance
OpenMesh::VertexHandle h;
public:
//default constructor
Point();
//construct a point with given point position v0
Point(const Eigen::Vector3f& v0);
//construct a point from vertex v of giben halfedge mesh m
Point(const HEMesh &m, const OpenMesh::VertexHandle& v);
//returns axis aligned bounding box of point
Box ComputeBounds() const;
//returns true if point overlap with box b
bool Overlaps(const Box& b) const;
//returns the point position
Eigen::Vector3f ClosestPoint(const Eigen::Vector3f& p) const;
//returns the squared distance between the query point p and the current point
float SqrDistance(const Eigen::Vector3f& p) const;
//returns the euclidean distance between the query point p and the current point
float Distance(const Eigen::Vector3f& p) const;
//returns a the position of the point as a reference point which is used to sort the primitive in the AABB tree construction
Eigen::Vector3f ReferencePoint() const;
};

View file

@ -0,0 +1,48 @@
// 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
#pragma once
#include "Box.h"
#include "util/OpenMeshUtils.h"
/*
a triangle primitive which can be used with the AABBTree and the HashGrid data structure
*/
class Triangle
{
//internal storage of the first vertex position of the triangle
Eigen::Vector3f v0;
//internal storage of the second vertex position of the triangle
Eigen::Vector3f v1;
//internal storage of the third vertex position of the triangle
Eigen::Vector3f v2;
//internal storage of the optional face handle to identify the originating face in a half edge mesh instance
OpenMesh::FaceHandle h;
public:
//default constructor
Triangle();
//constructs a triangle using the vertex positions v0,v1 and v2
Triangle(const Eigen::Vector3f& v0, const Eigen::Vector3f& v1,const Eigen::Vector3f& v2);
//constructs a triangle from the face f of the given halfedge mesh m
Triangle(const HEMesh&m, const OpenMesh::FaceHandle& f);
//returns the axis aligned bounding box of the triangle
Box ComputeBounds() const;
//returns true if the triangle overlaps the given box b
bool Overlaps(const Box& b) const;
//returns the barycentric coordinates of the point with thesmallest distance to point p which lies on the triangle
void ClosestPointBarycentric(const Eigen::Vector3f& p, float& l0, float& l1, float& l2) const;
//returns the point with smallest distance to point p which lies on the triangle
Eigen::Vector3f ClosestPoint(const Eigen::Vector3f& p) const;
//returns the squared distance between point p and the triangle
float SqrDistance(const Eigen::Vector3f& p) const;
//returns the euclidean distance between point p and the triangle
float Distance(const Eigen::Vector3f& p) const;
//returns a reference point which is on the triangle and is used to sort the primitive in the AABB tree construction
Eigen::Vector3f ReferencePoint() const;
};

View file

@ -0,0 +1,90 @@
// 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
#pragma once
#include <gui/AbstractViewer.h>
#include <gui/SliderHelper.h>
#include <util/OpenMeshUtils.h>
#include "AABBTree.h"
#include "HashGrid.h"
#include "Point.h"
#include "LineSegment.h"
#include "Triangle.h"
#include <gui/ShaderPool.h>
class Viewer : public nse::gui::AbstractViewer
{
public:
Viewer();
void drawContents();
private:
enum PrimitiveType
{
Vertex, Edge, Tri
};
void SetupGUI();
void MeshUpdated();
void FindClosestPoint(const Eigen::Vector3f& p);
void BuildGridVBO();
void BuildRayVBOs();
template <typename Grid>
void BuildGridVBO(const Grid& grid)
{
std::vector<Eigen::Vector4f> positions;
for (auto it = grid.NonEmptyCellsBegin(); it != grid.NonEmptyCellsEnd(); ++it)
{
auto box = grid.CellBounds(it->first);
AddBoxVertices(box, positions);
}
ShaderPool::Instance()->simpleShader.bind();
gridVAO.bind();
gridPositions.uploadData(positions).bindToAttribute("position");
gridVAO.unbind();
gridIndices = (GLuint)positions.size();
}
void AddBoxVertices(const Box& box, std::vector<Eigen::Vector4f>& positions);
nanogui::ComboBox* shadingBtn;
nanogui::CheckBox* chkRenderMesh;
nanogui::CheckBox* chkRenderGrid;
nanogui::CheckBox* chkRenderRay;
int raySteps;
nse::gui::VectorInput* sldQuery, *sldRayOrigin, *sldRayDir;
nanogui::ComboBox* cmbPrimitiveType;
HEMesh polymesh;
float bboxMaxLength;
MeshRenderer renderer;
AABBTree<Point> vertexTree;
AABBTree<LineSegment> edgeTree;
AABBTree<Triangle> triangleTree;
HashGrid<Point> vertexGrid;
HashGrid<LineSegment> edgeGrid;
HashGrid<Triangle> triangleGrid;
nse::gui::GLBuffer closestPositions;
nse::gui::GLVertexArray closestVAO;
nse::gui::GLBuffer gridPositions;
nse::gui::GLVertexArray gridVAO;
GLuint gridIndices;
nse::gui::GLBuffer rayPositions, rayCellsPositions;
nse::gui::GLVertexArray rayVAO, rayCellsVAO;
GLuint rayCellsIndices;
};

View file

@ -0,0 +1,42 @@
// 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 "AABBTree.h"
#include <iostream>
void BuildAABBTreeFromTriangles(const HEMesh& m, AABBTree<Triangle >& tree)
{
std::cout << "Building AABB tree from triangles .." << std::endl;
tree.Clear();
auto fend = m.faces_end();
for(auto fit = m.faces_begin(); fit != fend; ++fit)
tree.Insert(Triangle(m,*fit));
tree.Complete();
std::cout << "Done." << std::endl;
}
void BuildAABBTreeFromVertices(const HEMesh& m, AABBTree<Point>& tree)
{
std::cout << "Building AABB tree from vertices .." << std::endl;
tree.Clear();
auto vend = m.vertices_end();
for(auto vit = m.vertices_begin(); vit != vend; ++vit)
tree.Insert(Point(m,*vit));
tree.Complete();
std::cout << "Done." << std::endl;
}
void BuildAABBTreeFromEdges(const HEMesh& m, AABBTree<LineSegment>& tree)
{
std::cout << "Building AABB tree from edges .." << std::endl;
tree.Clear();
auto eend = m.edges_end();
for(auto eit = m.edges_begin(); eit != eend; ++eit)
tree.Insert(LineSegment(m,*eit));
tree.Complete();
std::cout << "Done." << std::endl;
}

178
exercise3/src/Box.cpp Normal file
View file

@ -0,0 +1,178 @@
// 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 "Box.h"
#include "GridUtils.h"
#include <limits>
//creates an empty box like the method Clear
Box::Box()
{
Clear();
}
//construct a box with the gven lower and upper corner points
Box::Box(const Eigen::Vector3f& lbound, const Eigen::Vector3f& ubound)
{
bounds = std::make_pair(lbound,ubound);
}
//returns the corner point which is the lower bound of the box in all dimensions
Eigen::Vector3f& Box::LowerBound()
{
return bounds.first;
}
//returns the corner point which is the lower bound of the box in all dimensions
const Eigen::Vector3f& Box::LowerBound() const
{
return bounds.first;
}
//returns the corner point which is the upper bound of the box in all dimensions
Eigen::Vector3f& Box::UpperBound()
{
return bounds.second;
}
//returns the corner point which is the upper bound of the box in all dimensions
const Eigen::Vector3f& Box::UpperBound() const
{
return bounds.second;
}
//returns a vector containing the extents of the box in all dimensions
Eigen::Vector3f Box::Extents() const
{
return UpperBound()-LowerBound();
}
//returns a vector containing the extents of the box in all dimensions divided by 2
Eigen::Vector3f Box::HalfExtents() const
{
return Extents()/2.0f;
}
//returns the center of the box
Eigen::Vector3f Box::Center() const
{
return (LowerBound()+UpperBound())/2.0f;
}
//returns the surface area of the box
float Box::SurfaceArea() const
{
Eigen::Vector3f e = Extents();
return 2.0f*(e[0]*e[1] + e[1]*e[2] + e[0]*e[2]);
}
//returns the volume of the box
float Box::Volume() const
{
Eigen::Vector3f e = Extents();
return e[0] + e[1]+ e[2];
}
//returns the box radius for a given direction vector a
//if the box is centered at the origin
//then the projection of the box on direction a is contained within the Interval [-r,r]
float Box::Radius(const Eigen::Vector3f& a) const
{
Eigen::Vector3f h = HalfExtents();
return h[0]*std::abs(a[0]) + h[1]*std::abs(a[1]) + h[2]*std::abs(a[2]);
}
//returns true if the box b overlaps with the current one
bool Box:: Overlaps(const Box& b) const
{
Eigen::Vector3f lb1 = LowerBound();
Eigen::Vector3f ub1 = UpperBound();
Eigen::Vector3f lb2 = b.LowerBound();
Eigen::Vector3f ub2 = b.UpperBound();
if(!OverlapIntervals(lb1[0], ub1[0],lb2[0], ub2[0]))
return false;
if(!OverlapIntervals(lb1[1], ub1[1],lb2[1], ub2[1]))
return false;
if(!OverlapIntervals(lb1[2], ub1[2],lb2[2], ub2[2]))
return false;
return true;
}
//returns true if the point p is inside this box
bool Box::IsInside(const Eigen::Vector3f& p) const
{
const Eigen::Vector3f& lbound = LowerBound();
const Eigen::Vector3f& ubound = UpperBound();
if(p[0] < lbound[0] || p[0] > ubound[0] ||
p[1] < lbound[1] || p[1] > ubound[1] ||
p[2] < lbound[2] || p[2] > ubound[2])
return false;
return true;
}
//returns true if box b is inside this box
bool Box::IsInside(const Box& b) const
{
return IsInside(b.LowerBound()) && IsInside(b.UpperBound());
}
//creates a box which goes from [+infinity,- infinity] in al dimensions
void Box::Clear()
{
const float infty = std::numeric_limits<float>::infinity();
const Eigen::Vector3f vec_infty = Eigen::Vector3f(infty,infty,infty);
bounds = std::make_pair(vec_infty,-vec_infty);
}
//enlarges the box such that the point p is inside afterwards
void Box::Insert(const Eigen::Vector3f& p)
{
Eigen::Vector3f& lbound = LowerBound();
Eigen::Vector3f& ubound = UpperBound();
lbound[0] = (std::min)(p[0],lbound[0]);
lbound[1] = (std::min)(p[1],lbound[1]);
lbound[2] = (std::min)(p[2],lbound[2]);
ubound[0] = (std::max)(p[0],ubound[0]);
ubound[1] = (std::max)(p[1],ubound[1]);
ubound[2] = (std::max)(p[2],ubound[2]);
}
//enlarges the box such that box b is inside afterwards
void Box::Insert(const Box& b)
{
Insert(b.LowerBound());
Insert(b.UpperBound());
}
//returns the point on or inside the box with the smallest distance to p
Eigen::Vector3f Box::ClosestPoint(const Eigen::Vector3f& p) const
{
Eigen::Vector3f q;
const Eigen::Vector3f& lbound = LowerBound();
const Eigen::Vector3f& ubound = UpperBound();
q[0] = std::min(std::max(p[0],lbound[0]),ubound[0]);
q[1] = std::min(std::max(p[1],lbound[1]),ubound[1]);
q[2] = std::min(std::max(p[2],lbound[2]),ubound[2]);
return q;
}
//returns the squared distance between p and the box
float Box::SqrDistance(const Eigen::Vector3f& p) const
{
Eigen::Vector3f d = p-ClosestPoint(p);
return d.dot(d);
}
//returns the euclidean distance between p and the box
float Box::Distance(const Eigen::Vector3f& p) const
{
return sqrt(SqrDistance(p));
}

View file

@ -0,0 +1,63 @@
// 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 "GridTraverser.h"
#include "GridUtils.h"
GridTraverser::GridTraverser()
{ }
GridTraverser::GridTraverser(const Eigen::Vector3f& o, const Eigen::Vector3f&d, const Eigen::Vector3f cell_extents)
: orig(o), dir(d), cellExtents(cell_extents)
{
dir.normalize();
Init();
}
Eigen::Vector3f& GridTraverser::Origin()
{
return orig;
}
const Eigen::Vector3f& GridTraverser::Origin() const
{
return orig;
}
Eigen::Vector3f& GridTraverser::Direction()
{
return dir;
}
const Eigen::Vector3f& GridTraverser::Direction() const
{
return dir;
}
void GridTraverser::SetCellExtents(const Eigen::Vector3f& cellExtent)
{
this->cellExtents = cellExtent;
Init();
}
void GridTraverser::Init()
{
current = PositionToCellIndex(orig, cellExtents);
/* Task 3.2.2 */
//you can add some precalculation code here
}
void GridTraverser::operator++(int)
{
/* Task 3.2.2 */
//traverse one step along the ray
//update the cell index stored in attribute "current"
}
Eigen::Vector3i GridTraverser::operator*()
{
return current;
}

View file

@ -0,0 +1,36 @@
// 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 "HashGrid.h"
#include <iostream>
void BuildHashGridFromTriangles(const HEMesh& m, HashGrid<Triangle>& grid, const Eigen::Vector3f& cellSize)
{
std::cout << "Building hash grid from triangles .." << std::endl;
grid = HashGrid<Triangle>(cellSize, 1);
auto fend = m.faces_end();
for(auto fit = m.faces_begin(); fit != fend; ++fit)
grid.Insert(Triangle(m,*fit));
std::cout << "Done (using " << grid.NumCells() << " cells)." << std::endl;
}
void BuildHashGridFromVertices(const HEMesh& m, HashGrid<Point>& grid, const Eigen::Vector3f& cellSize)
{
std::cout << "Building hash grid from vertices .." << std::endl;
grid = HashGrid<Point>(cellSize, 1);
auto vend = m.vertices_end();
for(auto vit = m.vertices_begin(); vit != vend; ++vit)
grid.Insert(Point(m,*vit));
std::cout << "Done (using " << grid.NumCells() << " cells)." << std::endl;
}
void BuildHashGridFromEdges(const HEMesh& m, HashGrid<LineSegment >& grid, const Eigen::Vector3f& cellSize)
{
std::cout << "Building hash grid from edges .." << std::endl;
grid = HashGrid<LineSegment>(cellSize, 1);
auto eend = m.edges_end();
for(auto eit = m.edges_begin(); eit != eend; ++eit)
grid.Insert(LineSegment(m,*eit));
std::cout << "Done (using " << grid.NumCells() << " cells)." << std::endl;
}

View file

@ -0,0 +1,123 @@
// 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 "LineSegment.h"
#include "GridUtils.h"
//default constructor
LineSegment::LineSegment()
{
}
//constructs a line segment by the two end points v0, v1 without using the edge handle
LineSegment::LineSegment(const Eigen::Vector3f& v0, const Eigen::Vector3f& v1):v0(v0),v1(v1)
{
}
//construct a line segment from the edge e of the halfedge mesh m
LineSegment::LineSegment(const HEMesh& m,const OpenMesh::EdgeHandle& e):h(e)
{
auto h = m.halfedge_handle(e, 0);
v0 = ToEigenVector(m.point(m.from_vertex_handle(h)));
v1 = ToEigenVector(m.point(m.to_vertex_handle(h)));
}
//returns an axis aligned bounding box of the line segment
Box LineSegment::ComputeBounds() const
{
Box b;
b.Insert(v0);
b.Insert(v1);
return b;
}
//returns true if the line segment overlaps the given box b
bool LineSegment::Overlaps(const Box& b) const
{
Box aabb = ComputeBounds();
if(!b.Overlaps(aabb))
return false;
Eigen::Vector3f o = b.Center();
Eigen::Vector3f u1 = v0-o;
Eigen::Vector3f u2 = v1-o;
Eigen::Vector3f d1 = v1-v0;
d1.normalize();
float r = b.Radius(d1);
float lb = u1.dot(d1);
float ub = u2.dot(d1);
if(lb > ub)
std::swap(lb,ub);
if(lb > r || ub < -r)
return false;
Eigen::Vector3f e1(1,0,0);
Eigen::Vector3f d2= d1.cross(e1);
r = b.Radius(d2);
lb = u1.dot(d2);
ub = u2.dot(d2);
if(lb > ub)
std::swap(lb,ub);
if(!OverlapIntervals(-r,r,lb,ub))
return false;
Eigen::Vector3f e2(0,1,0);
Eigen::Vector3f d3 = d1.cross(e2);
r = b.Radius(d3);
lb = u1.dot(d3);
ub = u2.dot(d3);
if(lb > ub)
std::swap(lb,ub);
if(!OverlapIntervals(-r,r,lb,ub))
return false;
Eigen::Vector3f e3(0,0,1);
Eigen::Vector3f d4 = d1.cross(e3);
r = b.Radius(d4);
lb = u1.dot(d4);
ub = u2.dot(d4);
if(!OverlapIntervals(-r,r,lb,ub))
return false;
return true;
}
//returns the point with smallest distance topoint p which lies on the line segment
Eigen::Vector3f LineSegment::ClosestPoint(const Eigen::Vector3f& p) const
{
//the two endpoints of the line segment are v0,v1
/* Task 3.2.1 */
return Eigen::Vector3f(0,0,0);
}
//returns the squared distance between point p and the line segment
float LineSegment::SqrDistance(const Eigen::Vector3f& p) const
{
Eigen::Vector3f d = p-ClosestPoint(p);
return d.squaredNorm();
}
//returns the euclidean distance between point p and the line segment
float LineSegment::Distance(const Eigen::Vector3f& p) const
{
return sqrt(SqrDistance(p));
}
//returns a reference point which is on the line segment and is used to sort the primitive in the AABB tree construction
Eigen::Vector3f LineSegment::ReferencePoint() const
{
return 0.5f*(v0 + v1);
}

65
exercise3/src/Point.cpp Normal file
View file

@ -0,0 +1,65 @@
// 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 "Point.h"
#include "GridUtils.h"
//default constructor
Point::Point(){}
//construct a point with given point position v0
Point::Point(const Eigen::Vector3f& v0):v0(v0)
{
}
//construct a point from vertex v of giben halfedge mesh m
Point::Point(const HEMesh &m, const OpenMesh::VertexHandle& v):h(v)
{
v0 = ToEigenVector(m.point(v));
}
//returns axis aligned bounding box of point
Box Point::ComputeBounds() const
{
Box b;
b.Insert(v0);
return b;
}
//returns true if point overlap with box b
bool Point::Overlaps(const Box& b) const
{
Eigen::Vector3f lb = b.LowerBound();
Eigen::Vector3f ub = b.UpperBound();
return
(v0[0] >= lb[0] && v0[0] <= ub[0]&&
v0[1] >= lb[1] && v0[1] <= ub[1] &&
v0[2] >= lb[2] && v0[2] <= ub[2]);
}
//returns the point position
Eigen::Vector3f Point::ClosestPoint(const Eigen::Vector3f& p) const
{
return v0;
}
//returns the squared distance between the query point p and the current point
float Point::SqrDistance(const Eigen::Vector3f& p) const
{
Eigen::Vector3f d = p-ClosestPoint(p);
return d.squaredNorm();
}
//returns the euclidean distance between the query point p and the current point
float Point::Distance(const Eigen::Vector3f& p) const
{
return sqrt(SqrDistance(p));
}
//returns a the position of the point as a reference point which is used to sort the primitive in the AABB tree construction
Eigen::Vector3f Point::ReferencePoint() const
{
return v0;
}

180
exercise3/src/Triangle.cpp Normal file
View file

@ -0,0 +1,180 @@
// 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 "Triangle.h"
#include "GridUtils.h"
#include <tuple>
//default constructor
Triangle::Triangle()
{
}
//constructs a triangle using the vertex positions v0,v1 and v2
Triangle::Triangle(const Eigen::Vector3f& v0, const Eigen::Vector3f& v1,const Eigen::Vector3f& v2): v0(v0),v1(v1),v2(v2)
{
}
//constructs a triangle from the face f of the given halfedge mesh m
Triangle::Triangle(const HEMesh&m, const OpenMesh::FaceHandle& f):h(f)
{
OpenMesh::HalfedgeHandle he = m.halfedge_handle(f);
v0 = ToEigenVector(m.point(m.from_vertex_handle(he)));
he = m.next_halfedge_handle(he);
v1 = ToEigenVector(m.point(m.from_vertex_handle(he)));
he = m.next_halfedge_handle(he);
v2 = ToEigenVector(m.point(m.from_vertex_handle(he)));
}
//returns the smallest axis aligned bounding box of the triangle
Box Triangle::ComputeBounds() const
{
/* Task 3.2.2 */
Box b;
return b;
}
//returns true if the triangle overlaps the given box b
bool Triangle::Overlaps(const Box& b) const
{
/* Task 3.2.2 */
//carefully look at the interface of the box class, there might be a lot of useful helper functions
return true;
}
//returns the barycentric coordinates of the point with the smallest distance to point p which lies on the triangle
void Triangle::ClosestPointBarycentric(const Eigen::Vector3f& p, float& l0, float& l1, float& l2) const
{
Eigen::Vector3f edge0 = v1 - v0;
Eigen::Vector3f edge1 = v2 - v0;
Eigen::Vector3f v = v0 - p;
float a = edge0.dot( edge0 );
float b = edge0.dot( edge1 );
float c = edge1.dot( edge1 );
float d = edge0.dot( v );
float e = edge1.dot( v );
float det = a*c - b*b;
float s = b*e - c*d;
float t = b*d - a*e;
if ( s + t < det )
{
if ( s < 0.f )
{
if ( t < 0.f )
{
if ( d < 0.f )
{
s=-d/a;
s=std::min(std::max(s,0.0f),1.0f);
t = 0.f;
}
else
{
s = 0.f;
t = -e/c;
t = std::min(std::max(t,0.0f),1.0f);
}
}
else
{
s = 0.f;
t = -e/c;
t = std::min(std::max(t,0.0f),1.0f);
}
}
else if ( t < 0.f )
{
s = -d/a;
s=std::min(std::max(s,0.0f),1.0f);
t = 0.f;
}
else
{
float invDet = 1.f / det;
s *= invDet;
t *= invDet;
}
}
else
{
if ( s < 0.f )
{
float tmp0 = b+d;
float tmp1 = c+e;
if ( tmp1 > tmp0 )
{
float numer = tmp1 - tmp0;
float denom = a-2*b+c;
s = numer/denom;
s=std::min(std::max(s,0.0f),1.0f);
t = 1-s;
}
else
{
t = -e/c;
t=std::min(std::max(t,0.0f),1.0f);
s = 0.f;
}
}
else if ( t < 0.f )
{
if ( a+d > b+e )
{
float numer = c+e-b-d;
float denom = a-2*b+c;
s = numer/denom;
s=std::min(std::max(s,0.0f),1.0f);
t = 1-s;
}
else
{
s = -e/c;
s=std::min(std::max(s,0.0f),1.0f);
t = 0.f;
}
}
else
{
float numer = c+e-b-d;
float denom = a-2*b+c;
s = numer/denom;
s=std::min(std::max(s,0.0f),1.0f);
t = 1.f - s;
}
}
l0 = 1-s-t;
l1 = s;
l2 = t;
}
//returns the point with smallest distance to point p which lies on the triangle
Eigen::Vector3f Triangle::ClosestPoint(const Eigen::Vector3f& p) const
{
float l0,l1,l2;
ClosestPointBarycentric(p,l0,l1,l2);
return l0*v0 + l1*v1 +l2* v2;
}
//returns the squared distance between point p and the triangle
float Triangle::SqrDistance(const Eigen::Vector3f& p) const
{
Eigen::Vector3f d = p-ClosestPoint(p);
return d.squaredNorm();
}
//returns the euclidean distance between point p and the triangle
float Triangle::Distance(const Eigen::Vector3f& p) const
{
return sqrt(SqrDistance(p));
}
//returns a reference point which is on the triangle and is used to sort the primitive in the AABB tree construction
Eigen::Vector3f Triangle::ReferencePoint() const
{
return (v0+v1+v2)/3.0f;
}

287
exercise3/src/Viewer.cpp Normal file
View file

@ -0,0 +1,287 @@
// 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();
}
}
}

38
exercise3/src/main.cpp Normal file
View file

@ -0,0 +1,38 @@
// 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 <iostream>
#include <util/GLDebug.h>
#include "Viewer.h"
int main(int argc, char* argv[])
{
std::cout.imbue(std::locale(""));
nanogui::init();
{
nanogui::ref<Viewer> viewer = new Viewer();
viewer->setVisible(true);
nse::util::GLDebug::SetupDebugCallback();
nse::util::GLDebug::IgnoreGLError(131185); //buffer usage info
try
{
nanogui::mainloop();
}
catch (std::runtime_error& e)
{
std::cerr << e.what() << std::endl;
}
}
nanogui::shutdown();
return 0;
}

10
exercise4/CMakeLists.txt Normal file
View file

@ -0,0 +1,10 @@
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include )
add_executable(Exercise4 MACOSX_BUNDLE
src/main.cpp
src/Viewer.cpp include/Viewer.h
src/Parametrization.cpp include/Parametrization.h
src/Registration.cpp include/Registration.h
)
target_link_libraries(Exercise4 CG1Common ${LIBS})

View file

@ -0,0 +1,28 @@
// 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
#pragma once
#include <util/OpenMeshUtils.h>
//returns true if mesh is a topological disk
bool IsTopologicalDisk(const HEMesh& m, OpenMesh::HalfedgeHandle& outBoundary);
//different possible weights
enum WeightType { CONSTANT_WEIGHT, EDGE_LENGTH_WEIGHT, INV_EDGE_LENGTH_WEIGHT, COTAN_WEIGHT};
//compute weight w_ij for a given halfedge, where vertex i is the origin and vertex j is the target of halfedge h
template <WeightType wtype>
float Weight(HEMesh& m, OpenMesh::HalfedgeHandle h);
template<> float Weight<CONSTANT_WEIGHT>(HEMesh& m, OpenMesh::HalfedgeHandle h);
template<> float Weight<EDGE_LENGTH_WEIGHT>(HEMesh& m, OpenMesh::HalfedgeHandle h);
template<> float Weight<INV_EDGE_LENGTH_WEIGHT>(HEMesh& m, OpenMesh::HalfedgeHandle h);
template<> float Weight<COTAN_WEIGHT>(HEMesh& m, OpenMesh::HalfedgeHandle h);
//computes a parametrization (textcoords for each vertex)
//texcoords of boundary vertices are placed on a circle with center (0.5,0.5) and radius 1 intexture space
//texcoords of inner vertices are placed in the weighted mean of its one ring neighbors texcoords
//these constraints can be stacked into a sparse linear equation system which can be solved in a least squares sense
template <WeightType wtype>
bool ComputeParametrizationOfTopologicalDisk(HEMesh& mesh);

View file

@ -0,0 +1,18 @@
// 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
#pragma once
#include <util/OpenMeshUtils.h>
#include <vector>
#include <Eigen/Core>
//a point to point correspondence
// the first point is on mesh_A the second in mesh_B
typedef std::pair<Eigen::Vector3f, Eigen::Vector3f> correspondence;
//compute a rigid transformation (R,t) that maps the correspondences
//to each other in a least-squares sense (map second entry to first entry)
Eigen::Affine3f CalculateRigidRegistration(std::vector<correspondence>& correspondences);

View file

@ -0,0 +1,49 @@
// 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
#pragma once
#include <gui/AbstractViewer.h>
#include <gui/SliderHelper.h>
#include <util/OpenMeshUtils.h>
#include "Registration.h"
#include <random>
class Viewer : public nse::gui::AbstractViewer
{
public:
Viewer();
void drawContents();
virtual bool resizeEvent(const Eigen::Vector2i&);
private:
void SetupGUI();
void MeshUpdated();
void BuildCorrVBOs();
nse::math::BoundingBox<float, 3> meshBbox, expandedBbox;
nanogui::ComboBox* shadingBtn;
nanogui::CheckBox* chkRenderTextureMap;
nanogui::CheckBox* chkRenderSecondMesh;
HEMesh polymesh;
MeshRenderer renderer;
bool hasParametrization = false;
Eigen::Matrix4f texMapProjectionMatrix;
Eigen::Affine3f secondMeshTransform;
std::mt19937 rnd;
std::vector<correspondence> correspondences;
size_t corrCount;
nse::gui::GLBuffer corrPositions;
nse::gui::GLVertexArray corrVAO;
};

View file

@ -0,0 +1,92 @@
// 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 "Parametrization.h"
#include <queue>
#include <Eigen/Sparse>
#include <Eigen/SparseLU>
#include <iostream>
bool IsTopologicalDisk(const HEMesh& m, OpenMesh::HalfedgeHandle& outBoundary)
{
std::vector<bool> vertexVisited(m.n_vertices());
std::queue<HEMesh::VertexHandle> vQueue;
vQueue.push(m.vertex_handle(0));
size_t visitedVertices = 0;
size_t visitedBoundaries = 0;
//check if we can reach all vertices from the first vertex by region growing
while (!vQueue.empty())
{
auto v = vQueue.front();
vQueue.pop();
if (vertexVisited[v.idx()])
continue;
vertexVisited[v.idx()] = true;
++visitedVertices;
//visit all the vertices along the boundary
if (m.is_boundary(v))
{
++visitedBoundaries;
auto h = m.halfedge_handle(v); //this will always be a boundary halfedge
outBoundary = h;
auto nextV = m.to_vertex_handle(h);
while (!vertexVisited[nextV.idx()])
{
vertexVisited[nextV.idx()] = true;
++visitedVertices;
vQueue.push(nextV);
h = m.next_halfedge_handle(h);
nextV = m.to_vertex_handle(h);
}
}
for (auto vn : m.vv_range(v))
vQueue.push(vn);
}
return visitedVertices == m.n_vertices() && visitedBoundaries == 1;
}
template<> float Weight<CONSTANT_WEIGHT>(HEMesh& m, OpenMesh::HalfedgeHandle h)
{
return 0;
}
template<> float Weight<EDGE_LENGTH_WEIGHT>(HEMesh& m, OpenMesh::HalfedgeHandle h)
{
return 0;
}
template<> float Weight<INV_EDGE_LENGTH_WEIGHT>(HEMesh& m, OpenMesh::HalfedgeHandle h)
{
return 0;
}
template<> float Weight<COTAN_WEIGHT>(HEMesh& m, OpenMesh::HalfedgeHandle h)
{
return 0;
}
template <WeightType wtype>
bool ComputeParametrizationOfTopologicalDisk(HEMesh& mesh)
{
OpenMesh::HalfedgeHandle boundary;
if (!IsTopologicalDisk(mesh, boundary))
{
std::cout << "This mesh is not a topological disk." << std::endl;
return false;
}
/* Task 4.2.1 */
return true;
}
template bool ComputeParametrizationOfTopologicalDisk<CONSTANT_WEIGHT>(HEMesh& mesh);
template bool ComputeParametrizationOfTopologicalDisk<EDGE_LENGTH_WEIGHT>(HEMesh& mesh);
template bool ComputeParametrizationOfTopologicalDisk<INV_EDGE_LENGTH_WEIGHT>(HEMesh& mesh);
template bool ComputeParametrizationOfTopologicalDisk<COTAN_WEIGHT>(HEMesh& mesh);

View file

@ -0,0 +1,18 @@
// 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 "Registration.h"
Eigen::Affine3f CalculateRigidRegistration(std::vector<correspondence>& correspondences)
{
//transform to compute
Eigen::Affine3f T = Eigen::Affine3f(Eigen::Matrix3f::Identity());
if(correspondences.size() < 3)
return T;
/* Task 4.2.2 */
return T;
}

267
exercise4/src/Viewer.cpp Normal file
View file

@ -0,0 +1,267 @@
// 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 <gui/SliderHelper.h>
#include "Parametrization.h"
#include "Registration.h"
#include <gui/ShaderPool.h>
Viewer::Viewer()
: AbstractViewer("CG1 Exercise 4"),
renderer(polymesh),
corrPositions(nse::gui::VertexBuffer)
{
polymesh.request_vertex_texcoords2D();
corrVAO.generate();
SetupGUI();
}
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();
}
});
shadingBtn = new nanogui::ComboBox(mainWindow, { "Smooth Shading", "Flat Shading" });
performLayout();
auto paramWindow = new nanogui::Window(this, "Parametrization");
paramWindow->setPosition(Eigen::Vector2i(mainWindow->position().x(), mainWindow->position().y() + mainWindow->size().y() + 15));
paramWindow->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
auto cmbWeightType = new nanogui::ComboBox(paramWindow, { "Constant Weight", "Edge Length Weight", "Inverse Edge Length Weight", "Cotan Weight" });
auto parametrizeBtn = new nanogui::Button(paramWindow, "Calculate Parametrization");
parametrizeBtn->setCallback([this, cmbWeightType]() {
if (polymesh.n_vertices() == 0)
{
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Parametrization", "Please load a mesh for parametrization.");
}
else
{
bool success;
switch (cmbWeightType->selectedIndex())
{
case 0:
success = ComputeParametrizationOfTopologicalDisk<CONSTANT_WEIGHT>(polymesh);
break;
case 1:
success = ComputeParametrizationOfTopologicalDisk<EDGE_LENGTH_WEIGHT>(polymesh);
break;
case 2:
success = ComputeParametrizationOfTopologicalDisk<INV_EDGE_LENGTH_WEIGHT>(polymesh);
break;
case 3:
success = ComputeParametrizationOfTopologicalDisk<COTAN_WEIGHT>(polymesh);
break;
}
if (success)
{
renderer.Update();
hasParametrization = true;
}
else
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Parametrization", "Parametrization failed.");
}
});
chkRenderTextureMap = new nanogui::CheckBox(paramWindow, "Render Texture Map");
performLayout();
auto registrationWindow = new nanogui::Window(this, "Registration");
registrationWindow->setPosition(Eigen::Vector2i(paramWindow->position().x(), paramWindow->position().y() + paramWindow->size().y() + 15));
registrationWindow->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
auto rotateBtn = new nanogui::Button(registrationWindow, "Random Rotation");
rotateBtn->setCallback([this]() {
secondMeshTransform = Eigen::Affine3f(Eigen::Translation3f(3 * meshBbox.diagonal().x(), 0, 0));
std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
Eigen::Quaternionf q(dist(rnd), dist(rnd), dist(rnd), dist(rnd));
q.normalize();
secondMeshTransform *= q;
correspondences.clear();
BuildCorrVBOs();
});
corrCount = 10;
nanogui::TextBox* txtCorrCount;
auto sldCorrCount = nse::gui::AddLabeledSlider(registrationWindow, "Correspondence Count", std::make_pair(1.0f, 50.0f), (float)corrCount, txtCorrCount);
sldCorrCount->setCallback([this, txtCorrCount](float value) {
corrCount = (int)std::round(value);
txtCorrCount->setValue(std::to_string(corrCount));
});
sldCorrCount->callback()(sldCorrCount->value());
auto autoCorrs = new nanogui::Button(registrationWindow, "Automatic Correspondences");
autoCorrs->setCallback([this]() {
if (corrCount > polymesh.n_vertices())
return;
std::uniform_int_distribution<int> dist(0, (int)polymesh.n_vertices());
correspondences.clear();
for (int i = 0; i < corrCount; ++i)
{
auto v = polymesh.vertex_handle(dist(rnd));
auto p = ToEigenVector(polymesh.point(v));
correspondences.push_back(std::make_pair(p, secondMeshTransform * p));
}
BuildCorrVBOs();
});
auto distortCorrespondencesBtn = new nanogui::Button(registrationWindow, "Distort Correspondences");
distortCorrespondencesBtn->setCallback([this]() {
std::normal_distribution<float> dist(0.0f, meshBbox.diagonal().norm() * 0.01f);
for (auto& c : correspondences)
c.first += Eigen::Vector3f(dist(rnd), dist(rnd), dist(rnd));
BuildCorrVBOs();
});
auto registerBtn = new nanogui::Button(registrationWindow, "Register");
registerBtn->setCallback([this]() {
auto T = CalculateRigidRegistration(correspondences);
secondMeshTransform = T * secondMeshTransform;
for (auto& c : correspondences)
c.second = T * c.second;
BuildCorrVBOs();
});
chkRenderSecondMesh = new nanogui::CheckBox(registrationWindow, "Render Second Mesh", [this](bool checked) {
if (checked)
camera().FocusOnBBox(expandedBbox);
else
camera().FocusOnBBox(meshBbox);
});
performLayout();
auto maxWidth = std::max(mainWindow->size().x(), std::max(paramWindow->size().x(), registrationWindow->size().x()));
mainWindow->setFixedWidth(maxWidth);
paramWindow->setFixedWidth(maxWidth);
registrationWindow->setFixedWidth(maxWidth);
performLayout();
}
void Viewer::MeshUpdated()
{
//calculate the bounding Box of the mesh
meshBbox.reset();
for (auto v : polymesh.vertices())
meshBbox.expand(ToEigenVector(polymesh.point(v)));
expandedBbox = meshBbox;
secondMeshTransform = Eigen::Affine3f(Eigen::Translation3f(3 * meshBbox.diagonal().x(), 0, 0));
expandedBbox.max.x() += 3 * meshBbox.diagonal().x();
if(chkRenderSecondMesh->checked())
camera().FocusOnBBox(expandedBbox);
else
camera().FocusOnBBox(meshBbox);
polymesh.triangulate();
correspondences.clear();
hasParametrization = false;
renderer.Update();
BuildCorrVBOs();
}
void Viewer::BuildCorrVBOs()
{
std::vector<Eigen::Vector4f> pos;
pos.reserve(correspondences.size() * 2);
for (auto& c : correspondences)
{
pos.push_back(Eigen::Vector4f(c.first.x(), c.first.y(), c.first.z(), 1.0f));
pos.push_back(Eigen::Vector4f(c.second.x(), c.second.y(), c.second.z(), 1.0f));
}
corrVAO.bind();
ShaderPool::Instance()->simpleShader.bind();
corrPositions.uploadData(pos).bindToAttribute("position");
corrVAO.unbind();
}
bool Viewer::resizeEvent(const Eigen::Vector2i& size)
{
AbstractViewer::resizeEvent(size);
float ratio = (float)size.x() / size.y();
Eigen::Affine3f proj = Eigen::Translation3f(1.0f - 0.1f / ratio - 1.8f / ratio, -0.9f, 0.0f) * Eigen::AlignedScaling3f(1.8f / ratio, 1.8f, 1.0f);
texMapProjectionMatrix = proj.matrix();
return true;
}
void Viewer::drawContents()
{
glEnable(GL_DEPTH_TEST);
if (!polymesh.vertices_empty())
{
Eigen::Matrix4f view, proj;
camera().ComputeCameraMatrices(view, proj);
Eigen::Matrix4f mvp = proj * view;
renderer.Render(view, proj, shadingBtn->selectedIndex() == 1, hasParametrization);
if (chkRenderSecondMesh->checked())
{
renderer.Render(view * secondMeshTransform.matrix(), proj, shadingBtn->selectedIndex() == 1, false, Eigen::Vector4f(0.2f, 0.3f, 0.4f, 1.0f));
if (correspondences.size() > 0)
{
corrVAO.bind();
ShaderPool::Instance()->simpleShader.bind();
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(1, 1, 1, 1));
ShaderPool::Instance()->simpleShader.setUniform("mvp", mvp);
glDrawArrays(GL_LINES, 0, (GLsizei)correspondences.size() * 2);
corrVAO.unbind();
}
}
if (hasParametrization && chkRenderTextureMap->checked())
{
glDisable(GL_DEPTH_TEST);
renderer.RenderTextureMap(texMapProjectionMatrix, Eigen::Vector4f(1, 1, 1, 1));
}
}
}

38
exercise4/src/main.cpp Normal file
View file

@ -0,0 +1,38 @@
// 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 <iostream>
#include <util/GLDebug.h>
#include "Viewer.h"
int main(int argc, char* argv[])
{
std::cout.imbue(std::locale(""));
nanogui::init();
{
nanogui::ref<Viewer> viewer = new Viewer();
viewer->setVisible(true);
nse::util::GLDebug::SetupDebugCallback();
nse::util::GLDebug::IgnoreGLError(131185); //buffer usage info
try
{
nanogui::mainloop();
}
catch (std::runtime_error& e)
{
std::cerr << e.what() << std::endl;
}
}
nanogui::shutdown();
return 0;
}

1
ext/OpenMesh Submodule

@ -0,0 +1 @@
Subproject commit 7b59f6e07d68f262f3beac8e0ac14f3ce72bf073

1
ext/nanogui Submodule

@ -0,0 +1 @@
Subproject commit 97b15265f4459f649cb0da652ce327d0f6a815b9