Initial commit
This commit is contained in:
parent
d01a89cd94
commit
815933c4fa
82 changed files with 6435 additions and 1 deletions
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal 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
|
83
CMake/Modules/CG1Helper.cmake
Normal file
83
CMake/Modules/CG1Helper.cmake
Normal 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
49
CMakeLists.txt
Normal 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)
|
20
README.md
20
README.md
|
@ -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
21
common/CMakeLists.txt
Normal 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
40
common/glsl/mesh.frag
Normal 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
25
common/glsl/mesh.vert
Normal 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
10
common/glsl/simple.frag
Normal 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
10
common/glsl/simple.vert
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#version 130
|
||||||
|
|
||||||
|
in vec4 position;
|
||||||
|
|
||||||
|
uniform mat4 mvp;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = mvp * position;
|
||||||
|
}
|
66
common/include/gui/AbstractViewer.h
Normal file
66
common/include/gui/AbstractViewer.h
Normal 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
104
common/include/gui/Camera.h
Normal 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
105
common/include/gui/GLBuffer.h
Normal file
105
common/include/gui/GLBuffer.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
138
common/include/gui/GLShader.h
Normal file
138
common/include/gui/GLShader.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
40
common/include/gui/GLVertexArray.h
Normal file
40
common/include/gui/GLVertexArray.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
20
common/include/gui/ShaderPool.h
Normal file
20
common/include/gui/ShaderPool.h
Normal 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;
|
||||||
|
};
|
32
common/include/gui/SliderHelper.h
Normal file
32
common/include/gui/SliderHelper.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
122
common/include/math/BoundingBox.h
Normal file
122
common/include/math/BoundingBox.h
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
20
common/include/util/GLDebug.h
Normal file
20
common/include/util/GLDebug.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
69
common/include/util/OpenMeshUtils.h
Normal file
69
common/include/util/OpenMeshUtils.h
Normal 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;
|
||||||
|
};
|
52
common/include/util/UnionFind.h
Normal file
52
common/include/util/UnionFind.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
207
common/src/gui/AbstractViewer.cpp
Normal file
207
common/src/gui/AbstractViewer.cpp
Normal 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
177
common/src/gui/Camera.cpp
Normal 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
113
common/src/gui/GLBuffer.cpp
Normal 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
254
common/src/gui/GLShader.cpp
Normal 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;
|
||||||
|
}
|
44
common/src/gui/GLVertexArray.cpp
Normal file
44
common/src/gui/GLVertexArray.cpp
Normal 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;
|
||||||
|
}
|
24
common/src/gui/ShaderPool.cpp
Normal file
24
common/src/gui/ShaderPool.cpp
Normal 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));
|
||||||
|
}
|
89
common/src/gui/SliderHelper.cpp
Normal file
89
common/src/gui/SliderHelper.cpp
Normal 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
136
common/src/util/GLDebug.cpp
Normal 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);
|
||||||
|
}
|
208
common/src/util/OpenMeshUtils.cpp
Normal file
208
common/src/util/OpenMeshUtils.cpp
Normal 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();
|
||||||
|
}
|
133
common/src/util/UnionFind.cpp
Normal file
133
common/src/util/UnionFind.cpp
Normal 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
13
exercise1/CMakeLists.txt
Normal 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})
|
24
exercise1/glsl/shader.frag
Normal file
24
exercise1/glsl/shader.frag
Normal 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 ***/
|
||||||
|
|
||||||
|
}
|
31
exercise1/glsl/shader.vert
Normal file
31
exercise1/glsl/shader.vert
Normal 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. */
|
||||||
|
}
|
42
exercise1/include/Viewer.h
Normal file
42
exercise1/include/Viewer.h
Normal 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
183
exercise1/src/Viewer.cpp
Normal 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
36
exercise1/src/main.cpp
Normal 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
14
exercise2/CMakeLists.txt
Normal 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})
|
38
exercise2/include/Primitives.h
Normal file
38
exercise2/include/Primitives.h
Normal 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);
|
11
exercise2/include/ShellExtraction.h
Normal file
11
exercise2/include/ShellExtraction.h
Normal 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);
|
12
exercise2/include/Smoothing.h
Normal file
12
exercise2/include/Smoothing.h
Normal 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);
|
11
exercise2/include/Stripification.h
Normal file
11
exercise2/include/Stripification.h
Normal 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);
|
12
exercise2/include/SurfaceArea.h
Normal file
12
exercise2/include/SurfaceArea.h
Normal 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);
|
35
exercise2/include/Viewer.h
Normal file
35
exercise2/include/Viewer.h
Normal 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;
|
||||||
|
};
|
12
exercise2/include/Volume.h
Normal file
12
exercise2/include/Volume.h
Normal 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);
|
109
exercise2/include/sample_set.h
Normal file
109
exercise2/include/sample_set.h
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
307
exercise2/src/Primitives.cpp
Normal file
307
exercise2/src/Primitives.cpp
Normal 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]);
|
||||||
|
}
|
||||||
|
}
|
17
exercise2/src/ShellExtraction.cpp
Normal file
17
exercise2/src/ShellExtraction.cpp
Normal 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;
|
||||||
|
}
|
29
exercise2/src/Smoothing.cpp
Normal file
29
exercise2/src/Smoothing.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
27
exercise2/src/Stripification.cpp
Normal file
27
exercise2/src/Stripification.cpp
Normal 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;
|
||||||
|
}
|
15
exercise2/src/SurfaceArea.cpp
Normal file
15
exercise2/src/SurfaceArea.cpp
Normal 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
255
exercise2/src/Viewer.cpp
Normal 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
15
exercise2/src/Volume.cpp
Normal 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
36
exercise2/src/main.cpp
Normal 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
16
exercise3/CMakeLists.txt
Normal 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})
|
513
exercise3/include/AABBTree.h
Normal file
513
exercise3/include/AABBTree.h
Normal 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
81
exercise3/include/Box.h
Normal 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;
|
||||||
|
|
||||||
|
};
|
51
exercise3/include/GridTraverser.h
Normal file
51
exercise3/include/GridTraverser.h
Normal 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*();
|
||||||
|
};
|
34
exercise3/include/GridUtils.h
Normal file
34
exercise3/include/GridUtils.h
Normal 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;
|
||||||
|
}
|
||||||
|
|
264
exercise3/include/HashGrid.h
Normal file
264
exercise3/include/HashGrid.h
Normal 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);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
52
exercise3/include/LineSegment.h
Normal file
52
exercise3/include/LineSegment.h
Normal 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
52
exercise3/include/Point.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
48
exercise3/include/Triangle.h
Normal file
48
exercise3/include/Triangle.h
Normal 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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
90
exercise3/include/Viewer.h
Normal file
90
exercise3/include/Viewer.h
Normal 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;
|
||||||
|
};
|
42
exercise3/src/AABBTree.cpp
Normal file
42
exercise3/src/AABBTree.cpp
Normal 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
178
exercise3/src/Box.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
63
exercise3/src/GridTraverser.cpp
Normal file
63
exercise3/src/GridTraverser.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
36
exercise3/src/HashGrid.cpp
Normal file
36
exercise3/src/HashGrid.cpp
Normal 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;
|
||||||
|
}
|
123
exercise3/src/LineSegment.cpp
Normal file
123
exercise3/src/LineSegment.cpp
Normal 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
65
exercise3/src/Point.cpp
Normal 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
180
exercise3/src/Triangle.cpp
Normal 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
287
exercise3/src/Viewer.cpp
Normal 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
38
exercise3/src/main.cpp
Normal 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
10
exercise4/CMakeLists.txt
Normal 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})
|
28
exercise4/include/Parametrization.h
Normal file
28
exercise4/include/Parametrization.h
Normal 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);
|
18
exercise4/include/Registration.h
Normal file
18
exercise4/include/Registration.h
Normal 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);
|
||||||
|
|
||||||
|
|
49
exercise4/include/Viewer.h
Normal file
49
exercise4/include/Viewer.h
Normal 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;
|
||||||
|
};
|
92
exercise4/src/Parametrization.cpp
Normal file
92
exercise4/src/Parametrization.cpp
Normal 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);
|
18
exercise4/src/Registration.cpp
Normal file
18
exercise4/src/Registration.cpp
Normal 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
267
exercise4/src/Viewer.cpp
Normal 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
38
exercise4/src/main.cpp
Normal 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
1
ext/OpenMesh
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 7b59f6e07d68f262f3beac8e0ac14f3ce72bf073
|
1
ext/nanogui
Submodule
1
ext/nanogui
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 97b15265f4459f649cb0da652ce327d0f6a815b9
|
Loading…
Reference in a new issue