diff --git a/.gitignore b/.gitignore index 567609b..0ca3f46 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ build/ +*.log +*.synctex.gz +*.aux diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b9edc23 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch Exercise 2", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/bin/Exercise2", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "launchCompleteCommand": "exec-run", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 9e26dfe..f09e1a3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1 +1,6 @@ -{} \ No newline at end of file +{ + "files.associations": { + "bitset": "cpp", + "chrono": "cpp" + } +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d9fca8c..7c9477c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,3 +48,4 @@ add_subdirectory(exercise1) add_subdirectory(exercise2) add_subdirectory(exercise3) add_subdirectory(exercise4) +add_subdirectory(exercise5) diff --git a/common/include/gui/Camera.h b/common/include/gui/Camera.h index 20dd595..8cb7b34 100644 --- a/common/include/gui/Camera.h +++ b/common/include/gui/Camera.h @@ -33,7 +33,7 @@ namespace nse { Eigen::Matrix4f &proj, float customAspectRatio = 0) const; - //Applies a zoom by scaling the scene. Positive values of amount increase object sizes. + //Applies a zoom by changing the view distance. Positive values of amount decrease the distance. void Zoom(float amount); //Sets the extent of the scene, which is kept between znear/zfar. @@ -64,6 +64,8 @@ namespace nse { //Forwarded resize event. void resize(const Eigen::Vector2i & s); + void FixClippingPlanes(float znear, float zfar); + //Returns the point that the camera focuses on const Eigen::Vector3f& GetFocusPoint() const; @@ -99,6 +101,8 @@ namespace nse { Eigen::Vector3f modelTranslation_start = Eigen::Vector3f::Zero(); Eigen::Vector2i translation_start; //mouse position on the screen where translation started + + float fixedZNear, fixedZFar; }; } } \ No newline at end of file diff --git a/common/src/gui/Camera.cpp b/common/src/gui/Camera.cpp index 77eff96..1f14a58 100644 --- a/common/src/gui/Camera.cpp +++ b/common/src/gui/Camera.cpp @@ -14,7 +14,7 @@ using namespace nse::gui; Camera::Camera(const nanogui::Widget & parent) - : parent(parent) + : parent(parent), fixedZNear(std::numeric_limits::quiet_NaN()) { params.arcball.setSize(parent.size()); } @@ -27,8 +27,17 @@ void Camera::ComputeCameraMatrices(Eigen::Matrix4f & view, Eigen::Matrix4f & pro 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 znear, zfar; + if (std::isnan(fixedZNear)) + { + znear = std::max(minZNear, depthOfSceneCenter - params.sceneRadius); + zfar = std::max(znear + minZNear, depthOfSceneCenter + params.sceneRadius); + } + else + { + znear = fixedZNear; + zfar = fixedZFar; + } float fH = std::tan(params.fovy / 360.0f * (float)M_PI) * znear; float aspectRatio = customAspectRatio == 0 ? (float)parent.width() / parent.height() : customAspectRatio; @@ -46,6 +55,7 @@ void Camera::Zoom(float amount) void Camera::SetSceneExtent(const nse::math::BoundingBox& bbox) { + fixedZNear = std::numeric_limits::quiet_NaN(); params.sceneCenter = 0.5f * (bbox.min + bbox.max); params.sceneRadius = bbox.diagonal().norm() / 2.0f; } @@ -174,4 +184,10 @@ bool Camera::HandleMouseMove(const Eigen::Vector2i & p, const Eigen::Vector2i & void Camera::resize(const Eigen::Vector2i & s) { params.arcball.setSize(s); +} + +void Camera::FixClippingPlanes(float znear, float zfar) +{ + fixedZNear = znear; + fixedZFar = zfar; } \ No newline at end of file diff --git a/common/src/util/GLDebug.cpp b/common/src/util/GLDebug.cpp index dd8699e..d4213de 100644 --- a/common/src/util/GLDebug.cpp +++ b/common/src/util/GLDebug.cpp @@ -132,5 +132,8 @@ void nse::util::GLDebug::SetupDebugCallback() { glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback(DebugCallback, &ignoredIds); + if (glDebugMessageCallback == nullptr) + std::cout << "Cannot set up an OpenGL debug callback. Perhaps your OpenGL version is too old." << std::endl; + else + glDebugMessageCallback(DebugCallback, &ignoredIds); } \ No newline at end of file diff --git a/exercise1/Theorie.pdf b/exercise1/Theorie.pdf new file mode 100644 index 0000000..eb21719 Binary files /dev/null and b/exercise1/Theorie.pdf differ diff --git a/exercise1/Theorie.tex b/exercise1/Theorie.tex new file mode 100644 index 0000000..8096b96 --- /dev/null +++ b/exercise1/Theorie.tex @@ -0,0 +1,151 @@ +\documentclass[12pt, a4paper]{article} + +%packages +\usepackage[ngerman]{babel} +\usepackage[utf8x]{inputenc} +%Formel packages +\usepackage{amsmath} +\usepackage{amsthm} +\usepackage{amsbsy} +\usepackage{amssymb} + + +\usepackage{enumerate} + + +\begin{document} + +\section*{Computer Graphics - Excercise 1} +\subsection*{1.1.1} +\subsection*{a)} +\begin{itemize} +\item Matrix Multiplication $\widehat{=}$ transformation +\item Inverse of a transformation $\tilde{T}^{-1}$ \\ +\begin{align*} + \Rightarrow && \tilde{T}*\tilde{T}^{-1} &= \tilde{T}^{-1}*\tilde{T} = \tilde{E} && \left |\ *\tilde{p} \right. \\ + &&\tilde{T}^{-1}*\tilde{T}*\tilde{p} &= \tilde{E}*\tilde{p} && \left |\ \tilde{T}*\tilde{p}=\tilde{p'} \right. \\ + \Rightarrow && \tilde{E}*\tilde{p} = \tilde{p}&, \hspace{1em} \tilde{T}^{-1}*\tilde{p'} = \tilde{p} \\ +\end{align*} +\end{itemize} +\subsection*{b)} + +For $R_1 \times R_2$\\ +\begin{gather*} +\begin{pmatrix} a & b & 0 \\ c&d&0\\ 0&0&1 \\ \end{pmatrix} \times \begin{pmatrix} e & f & 0 \\ g& h& 0\\ 0&0&1\\ \end{pmatrix} =\begin{pmatrix} a \times e + b \times g& a \times f + b \times h & 0 \\ c \times e + d \times g& c \times f + d \times h & 0\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} +For $R_2 \times R_1$\\ +\begin{gather*} +\begin{pmatrix} e & f & 0 \\ g& h& 0\\ 0&0&1\\ \end{pmatrix} \times \begin{pmatrix} a & b & 0 \\ c&d&0\\ 0&0&1 \\ \end{pmatrix} =\begin{pmatrix} a \times e + c \times f& b \times e + d \times f & 0 \\ a \times g + c \times h& b \times g + d \times h & 0\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} + +$\Rightarrow R_1 \times R_2 \neq R_2 \times R_1 \Rightarrow$ does not commute\\ + +\newpage +\hspace{-2em} +For $T_1 \times T_2$\\ +\begin{gather*} +\begin{pmatrix} 1 & 0 & a \\ 0&1&b\\ 0&0&1 \\ \end{pmatrix} \times \begin{pmatrix} 1 & 0 & c \\ 0& 1& d\\ 0&0&1\\ \end{pmatrix} =\begin{pmatrix} 1& 0 & a+c \\ 0& 1 & b+d\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} +\hspace{-0.5em} +For $T_2 \times T_1$\\ +\begin{gather*} +\begin{pmatrix} 1 & 0 & c \\ 0&1&d\\ 0&0&1 \\ \end{pmatrix} \times \begin{pmatrix} 1 & 0 & a \\ 0& 1& b\\ 0&0&1\\ \end{pmatrix} =\begin{pmatrix} 1& 0 & a+c \\ 0& 1 & b+d\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} + +$\Rightarrow T_1 \times T_2 = T_2 \times T_1 \Rightarrow$ does commute\\ +\\ +\hspace{-1.5em} +For $S_1 \times S_2$\\ +\begin{gather*} +\begin{pmatrix} a & 0 & 0 \\ 0&b&0\\ 0&0&1 \\ \end{pmatrix} \times \begin{pmatrix} c & 0 & 0 \\ 0& d& 0\\ 0&0&1\\ \end{pmatrix} =\begin{pmatrix} a \times c & 0 & 0 \\ b \times d& 0 & 0\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} +For $S_2 \times S_1$\\ +\begin{gather*} +\begin{pmatrix} c & 0& 0 \\ 0& d& 0\\ 0&0&1\\ \end{pmatrix} \times \begin{pmatrix} a & 0 & 0 \\ 0&b&0\\ 0&0&1 \\ \end{pmatrix} =\begin{pmatrix} a \times c & 0 & 0 \\ b \times d& 0 & 0\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} + +$\Rightarrow S_1 \times S_2 = S_2 \times S_1 \Rightarrow$ does commute\\ +\\ +\hspace{-1.5em} +For $R \times T$\\ +\begin{gather*} +\begin{pmatrix} a & b& 0 \\ c&d&0\\ 0&0&1 \\ \end{pmatrix} \times \begin{pmatrix} 1 & 0 & e \\ 0& 1& f\\ 0&0&1\\ \end{pmatrix} =\begin{pmatrix} a& b & a \times e + b \times f \\ c& d & c\times e + d \times f\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} +For $T \times R$\\ +\begin{gather*} +\begin{pmatrix} 1 & 0 & e \\ 0& 1& f\\ 0&0&1\\ \end{pmatrix} \times \begin{pmatrix} a & b& 0 \\ c&d&0\\ 0&0&1 \\ \end{pmatrix} =\begin{pmatrix} a& b & e \\ c& d & f\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} + +$\Rightarrow R \times T \neq T \times R \Rightarrow$ does not commute\\ +\\ +\hspace{-1.5em} +For $R \times S$\\ +\begin{gather*} +\begin{pmatrix} a & b& 0 \\ c&d&0\\ 0&0&1 \\ \end{pmatrix} \times \begin{pmatrix} e & 0 & 0 \\ 0& f& 0\\ 0&0&1\\ \end{pmatrix} =\begin{pmatrix} a \times e& b \times f & 0 \\ c \times f & d \times f & 0\times e + d \times f\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} +For $S \times R$\\ +\begin{gather*} +\begin{pmatrix} e & 0 & 0 \\ 0& f& 0\\ 0&0&1\\ \end{pmatrix} \times \begin{pmatrix} a & b& 0 \\ c&d&0\\ 0&0&1 \\ \end{pmatrix} =\begin{pmatrix} a \times e& b \times f & 0 \\ c \times f & d \times f & 0\times e + d \times f\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} + +$\Rightarrow R \times S = S \times R \Rightarrow$ does commute\\ +\\ +\hspace{-1.5em} +For $S \times T$\\ +\begin{gather*} +\begin{pmatrix} 1 & 0 & a \\ 0&0&b\\ 0&0&1 \\ \end{pmatrix} \times \begin{pmatrix} c & 0 & 0 \\ 0& d& 0\\ 0&0&1\\ \end{pmatrix} =\begin{pmatrix} c & 0 & a \\ 0& d & b\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} +For $T \times S$\\ +\begin{gather*} +\begin{pmatrix} c & 0 & 0 \\ 0& d& 0\\ 0&0&1\\ \end{pmatrix} \times \begin{pmatrix} 1 & 0 & a \\ 0&0&b\\ 0&0&1 \\ \end{pmatrix} =\begin{pmatrix} c & 0 & a \times c \\ d& 0 & b \times d\\ 0&0&1 \\ \end{pmatrix} +\end{gather*} + +$\Rightarrow S \times T \neq T \times S \Rightarrow$ does not commute\\ + + +\subsection*{1.1.2} +\subsection*{a)} +\begin{gather*} +\begin{pmatrix} 1 & 0 & 0 \\ 0&0&1\\ 0&1&0 \\ \end{pmatrix} \times \begin{pmatrix} x_1 & x_2 & \cdots & x_n \\ y_1&y_2&\cdots&y_n\\ z_1&z_2&\cdots&z_n \\ \end{pmatrix} =\begin{pmatrix} x_1 & x_2 & \cdots & x_n \\ z_1&z_2&\cdots&z_n\\ y_1&y_2&\cdots&y_n \\ \end{pmatrix} +\end{gather*} + +\subsection*{b)} +\begin{gather*} +\begin{pmatrix} x_1 & x_2 & \cdots & x_n \\ y_1&y_2&\cdots&y_n\\ z_1&z_2&\cdots&z_n \\ \end{pmatrix} \times \begin{pmatrix} 1 \\ 1 \\ \vdots \\ 1 \\ \end{pmatrix} = +\begin{pmatrix} \sum\limits_{i=1} x_i \\ \sum\limits_{i=1} y_i \\ \sum\limits_{i=1} z_i \end{pmatrix} +\end{gather*} + +\subsection*{c)} +\begin{gather*} + \tilde{M} = \begin{pmatrix} M_{11} & M_{12} & M_{13} & t_x \\ M_{21}&M_{22}& M_{23}&t_y\\ M_{31}&M_{32}&M_{33}&t_z \\ p_x&p_y&p_z&1 \\ \end{pmatrix}; +e = \begin{pmatrix} 5 \\ 10 \\ 5 \\\end{pmatrix}= t; +p = \begin{pmatrix} 0 \\ 0 \\ 0 \\ \end{pmatrix} +\end{gather*} +\begin{gather*} +z_\phi = \frac {\begin{pmatrix} 0\\0\\1\\ \end{pmatrix} \times p_x} {1 \times |p_x|} \Rightarrow \begin{pmatrix} cos(z_\phi) & -sin(z_\phi) & 0 \\ sin(z_\phi)&cos(z_\phi)&0\\ 0&0&1 \\ \end{pmatrix}=R_z\\ +y_\phi = \frac {\begin{pmatrix} 0\\1\\0\\ \end{pmatrix} \times p_x} {1 \times |p_x|} \Rightarrow \begin{pmatrix} cos(y_\phi) & 0 & sin(y_\phi) \\ 0 &1 &0\\ -sin(z_\phi)&0&cos(y_\phi) \\ \end{pmatrix}=R_y\\ +x_\phi = \frac {\begin{pmatrix} 1\\0\\0\\ \end{pmatrix} \times p_x} {1 \times |p_x|} \Rightarrow \begin{pmatrix} 0 & 0 & 0 \\ 0 &cos(x_\phi)&-sin(x_\phi)\\ 0&sin(x_\phi)&cos(x_\phi) \\ \end{pmatrix}=R_x\\ +M = R_x \times R_y \times R_z \\ +\end{gather*} +\newpage +Berechnung für: \\ +$p_1 = (-1, -1, 1); p_2 = (2, 1, -2); p_3 = (2, 1, -3); p_4 = (-1, -2, 1); p_5 = (3, -1, 0)$ +\begin{gather*} +\Rightarrow Vector =\begin{pmatrix} +p_1 \times \tilde{M_1}\\ +p_2 \times \tilde{M_2}\\ +p_3 \times \tilde{M_3}\\ +p_4 \times \tilde{M_4}\\ +p_5 \times \tilde{M_5}\\ \end{pmatrix} += + \begin{pmatrix} +6.323746006808568\\ +2.672777625063053\\ +1.6528549605644152\\ +6.432049954608068\\ +4.731031538265404\\ \end{pmatrix}\\ +\Rightarrow z_{far} = p_4 = 6.432049954608068; z_{near} = p_3 = 1.6528549605644152\\ +\end{gather*} + +\end{document} + diff --git a/exercise1/glsl/shader.vert b/exercise1/glsl/shader.vert index 9e11959..d736bef 100644 --- a/exercise1/glsl/shader.vert +++ b/exercise1/glsl/shader.vert @@ -18,4 +18,9 @@ void main() gl_Position = proj * view * in_position; fragment_color = in_color; +<<<<<<< HEAD +======= + + julia_position = julia_positions[gl_VertexID % 3]; +>>>>>>> 8b4276f6ab9ed1d7d6e1909f36944eaa9a8964da } \ No newline at end of file diff --git a/exercise1/include/Viewer.h b/exercise1/include/Viewer.h index 650663d..2ee7070 100644 --- a/exercise1/include/Viewer.h +++ b/exercise1/include/Viewer.h @@ -1,5 +1,5 @@ -// This source code is property of the Computer Graphics and Visualization -// chair of the TU Dresden. Do not distribute! +// 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 @@ -8,31 +8,41 @@ class Viewer : public nse::gui::AbstractViewer { -public: + public: Viewer(); void drawContents(); -private: + 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::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 + nanogui::Slider *sldJuliaCX; //Seed for the Julia fractal + nanogui::Slider *sldJuliaCY; + nanogui::Slider *sldJuliaZoom; //Zoom factor for the Julia fractal +<<<<<<< HEAD +======= + nanogui::Slider *point1X; + nanogui::Slider *point1Y; + nanogui::Slider *point2X; + nanogui::Slider *point2Y; + nanogui::Slider *point3X; + nanogui::Slider *point3Y; + +>>>>>>> 8b4276f6ab9ed1d7d6e1909f36944eaa9a8964da // 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 + 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 GLint view_uniform_id; @@ -40,7 +50,13 @@ private: GLint julia_m_id; GLint julia_c_id; +<<<<<<< HEAD // Read, Compile and link the shader codes to a shader program +======= + GLint julia_pos_id; + + // Read, Compile and link the shader codes to a shader program +>>>>>>> 8b4276f6ab9ed1d7d6e1909f36944eaa9a8964da void CreateShaders(); // Create and define the vertex array and add a number of vertex buffers void CreateVertexBuffers(); diff --git a/exercise1/src/Viewer.cpp b/exercise1/src/Viewer.cpp index 6343e3e..8a5b4d7 100644 --- a/exercise1/src/Viewer.cpp +++ b/exercise1/src/Viewer.cpp @@ -50,6 +50,16 @@ void Viewer::SetupGUI() 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); +<<<<<<< HEAD +======= + point1X = AddLabeledSliderWithDefaultDisplay(mainWindow, "Point1.X", std::make_pair(-3.0f, 3.0f), -0.8f, 2); + point1Y = AddLabeledSliderWithDefaultDisplay(mainWindow, "Point1.Y", std::make_pair(-3.0f, 3.0f), 0.6f, 2); + point2X = AddLabeledSliderWithDefaultDisplay(mainWindow, "Point2.X", std::make_pair(-3.0f, 3.0f), 0.5f, 2); + point2Y = AddLabeledSliderWithDefaultDisplay(mainWindow, "Point2.Y", std::make_pair(-3.0f, 3.0f), -1.0f, 2); + point3X = AddLabeledSliderWithDefaultDisplay(mainWindow, "Point3.X", std::make_pair(-3.0f, 3.0f), 0.8f, 2); + point3Y = AddLabeledSliderWithDefaultDisplay(mainWindow, "Point3.Y", std::make_pair(-3.0f, 3.0f), 1.0f, 2); + +>>>>>>> 8b4276f6ab9ed1d7d6e1909f36944eaa9a8964da performLayout(); } diff --git a/exercise2/CMakeLists.txt b/exercise2/CMakeLists.txt index 6c9fb4d..0df7f7e 100644 --- a/exercise2/CMakeLists.txt +++ b/exercise2/CMakeLists.txt @@ -1,14 +1,31 @@ +set(GLSL_FILES sky.vert sky.frag + terrain.vert terrain.frag) + +ProcessGLSLFiles(GLSL_FILES) + +set(TEXTURE_FILES grass.jpg rock.jpg roadColor.jpg roadNormals.jpg roadSpecular.jpg alpha.jpg) +PREPEND(TEXTURE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/resources/" ${TEXTURE_FILES}) +JOIN("${TEXTURE_FILES}" "," texture_string) +set(bin2c_cmdline + -DOUTPUT_C=textures.cpp + -DOUTPUT_H=textures.h + "-DINPUT_FILES=${texture_string}" + -P "${NANOGUI_DIR}/resources/bin2c.cmake") + + add_custom_command( + OUTPUT textures.cpp textures.h + COMMAND ${CMAKE_COMMAND} ARGS ${bin2c_cmdline} + DEPENDS ${TEXTURE_FILES} + COMMENT "Running bin2c" + PRE_BUILD VERBATIM) + include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include ) add_executable(Exercise2 MACOSX_BUNDLE + glsl.cpp + textures.cpp 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) + ${GLSL_FILES}) target_link_libraries(Exercise2 CG1Common ${LIBS}) \ No newline at end of file diff --git a/exercise2/glsl/sky.frag b/exercise2/glsl/sky.frag new file mode 100644 index 0000000..cb1baed --- /dev/null +++ b/exercise2/glsl/sky.frag @@ -0,0 +1,21 @@ +#version 330 +// 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 + +out vec4 color; + +in vec4 clipPos; + +const vec4 horizon = vec4(0.85, 0.85, 0.8, 1.0); +const vec4 floor = vec4(0.1, 0.1, 0.1, 1.0); +const vec4 sky = vec4(0.5, 0.6, 0.8, 1.0); + +void main() +{ + float h = normalize(clipPos.xyz).y; + if(h < 0) + color = mix(horizon, floor, pow(-h, 0.5)); + else + color = mix(horizon, sky, pow(h, 0.9)); +} diff --git a/exercise2/glsl/sky.vert b/exercise2/glsl/sky.vert new file mode 100644 index 0000000..883a534 --- /dev/null +++ b/exercise2/glsl/sky.vert @@ -0,0 +1,19 @@ +#version 330 +// 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 + +uniform mat4 mvp; + +out vec4 clipPos; + +void main() +{ + clipPos = vec4( float((gl_VertexID << 1) & 2) - 1.0, + float((gl_VertexID + 1) & 2) - 1.0, + float( gl_VertexID & 2) - 1.0, + 1.0); + + gl_Position = mvp * clipPos; + gl_Position.z = 0.5 * gl_Position.w; +} diff --git a/exercise2/glsl/terrain.frag b/exercise2/glsl/terrain.frag new file mode 100644 index 0000000..7beb1bc --- /dev/null +++ b/exercise2/glsl/terrain.frag @@ -0,0 +1,57 @@ +#version 330 +// 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 + +in vec3 n; +in vec2 uv; +in vec2 road_uv; + +out vec4 color; + +uniform vec3 cameraPos; + +uniform sampler2D grass; +uniform sampler2D rock; +uniform sampler2D alpha; +uniform sampler2D road; +uniform sampler2D specular_road; + +uniform sampler2D background; +uniform vec2 screenSize; + +const vec3 dirToLight = normalize(vec3(1, 3, 1)); + +//Calculates the visible surface color based on the Blinn-Phong illumination model +vec4 calculateLighting(vec4 materialColor, float specularIntensity, vec3 normalizedNormal, vec3 directionToViewer) +{ + vec4 color = materialColor; + vec3 h = normalize(dirToLight + directionToViewer); + color.xyz *= 0.9 * max(dot(normalizedNormal, dirToLight), 0) + 0.1; + color.xyz += specularIntensity * pow(max(dot(h, normalizedNormal), 0), 50); + return color; +} + +vec4 getBackgroundColor() +{ + return texture(background, gl_FragCoord.xy / screenSize); +} + +void main() +{ + //surface geometry + //vec3 n = vec3(0, 1, 0); + vec3 dirToViewer = vec3(0, 1, 0); + + //material properties + //color = vec4(0.6, 0.6, 0.6, 1); + color = mix(texture(grass, uv), texture(rock, uv), dot(n, dirToViewer)); + color = mix(color, texture(road, road_uv), texture(alpha, uv).x); + + float specular = texture(specular_road, road_uv).x; + + //Calculate light + color = calculateLighting(color, specular, n, dirToViewer); + + +} diff --git a/exercise2/glsl/terrain.vert b/exercise2/glsl/terrain.vert new file mode 100644 index 0000000..adf3242 --- /dev/null +++ b/exercise2/glsl/terrain.vert @@ -0,0 +1,130 @@ +#version 330 +// 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 + +in vec4 position; + +out vec3 n; +out vec2 uv; +out vec2 road_uv; + +uniform mat4 mvp; + +//Returns the height of the procedural terrain at a given xz position +float getTerrainHeight(vec2 p); + +vec3 calculate_normal(vec4 terrain_position); +vec4 offset_point(vec4 base, vec2 offset); + +const vec2 offsets[6] = vec2[6]( + vec2(0.0, 0.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0), + vec2(1.0, 1.0) +); + +void main() +{ + vec4 terrain_position = vec4(position.x, getTerrainHeight(vec2(position.x, position.z)), position.z, position.w); + n = calculate_normal(position); + //n = vec3(0.0, 1.0, 0.0); + + uv = vec2(terrain_position.x / 255.0, terrain_position.z / 255.0); + road_uv = offsets[gl_VertexID % 6]; + + gl_Position = mvp * terrain_position; +} + +//source: https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 +float rand(vec2 c) +{ + return 2 * fract(sin(dot(c.xy ,vec2(12.9898,78.233))) * 43758.5453) - 1; +} + +float perlinNoise(vec2 p ) +{ + vec2 ij = floor(p); + vec2 xy = p - ij; + //xy = 3.*xy*xy-2.*xy*xy*xy; + xy = .5*(1.-cos(3.1415926 * xy)); + float a = rand((ij+vec2(0.,0.))); + float b = rand((ij+vec2(1.,0.))); + float c = rand((ij+vec2(0.,1.))); + float d = rand((ij+vec2(1.,1.))); + float x1 = mix(a, b, xy.x); + float x2 = mix(c, d, xy.x); + return mix(x1, x2, xy.y); +} + +//based on https://www.seedofandromeda.com/blogs/58-procedural-heightmap-terrain-generation +float getTerrainHeight(vec2 p) +{ + float total = 0.0; + float maxAmplitude = 0.0; + float amplitude = 1.0; + float frequency = 0.02; + for (int i = 0; i < 11; i++) + { + total += ((1.0 - abs(perlinNoise(p * frequency))) * 2.0 - 1.0) * amplitude; + frequency *= 2.0; + maxAmplitude += amplitude; + amplitude *= 0.45; + } + return 15 * total / maxAmplitude; +} + +vec4 offset_point(vec4 base, vec2 offset) { + float y = getTerrainHeight(vec2(base.x, base.z) + offset); + return vec4(base.x + offset.x, y, base.z + offset.y, base.w); +} + +// calculate the position of the first vertex in this square +vec4 pos_to_base(vec4 pos, int v_id) { + return vec4(pos.x - offsets[v_id].x, pos.y, pos.z - offsets[v_id].y, pos.w); +} + +// calculates the normal of terrain_position +// it generates the complete triangle based on the gl_VertexID and then normal math +vec3 calculate_normal(vec4 terrain_position) { + int offset_index = gl_VertexID % 6; + + vec4 base_vertex = pos_to_base(terrain_position, offset_index); + + // the other 2 points from the triangle + int points_index = 0; + vec4 points[2]; + + int iterator_offset = 0; + + // second triangle offset + if (offset_index > 2) { + iterator_offset = 3; + } + + for (int i = iterator_offset; i < (3 + iterator_offset); i++) { + // skip for current vertex + if (i == offset_index) { + continue; + } + + points[points_index] = offset_point(base_vertex, offsets[i]); + points_index++; + } + + // create connection vectors + vec3 p1 = (points[0] - terrain_position).xyz; + vec3 p2 = (points[1] - terrain_position).xyz; + + // calculate normal + vec3 normal = normalize(cross(p1, p2)); + + // naively assume that a normal always has to look upwards + if (normal.y < 0.0) { + return -normal; + } else { + return normal; + } +} \ No newline at end of file diff --git a/exercise2/include/Viewer.h b/exercise2/include/Viewer.h index 29d694b..fa88fb0 100644 --- a/exercise2/include/Viewer.h +++ b/exercise2/include/Viewer.h @@ -1,35 +1,50 @@ -// This source code is property of the Computer Graphics and Visualization -// chair of the TU Dresden. Do not distribute! +// 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 -#include +#include +#include +#include class Viewer : public nse::gui::AbstractViewer { -public: + public: Viewer(); + void LoadShaders(); + void CreateGeometry(); + void drawContents(); + bool resizeEvent(const Eigen::Vector2i &); -private: - void SetupGUI(); - void MeshUpdated(bool initNewMesh = false); + private: + void RenderSky(); - void ColorMeshFromIds(); + Eigen::Matrix4f view, proj; - bool hasColors = false; + nse::gui::GLShader skyShader; + nse::gui::GLVertexArray emptyVAO; - nanogui::ComboBox* shadingBtn; - unsigned int smoothingIterations; - nanogui::Slider* sldSmoothingStrength; - unsigned int stripificationTrials; + nse::gui::GLShader terrainShader; + nse::gui::GLVertexArray terrainVAO; + nse::gui::GLBuffer terrainPositions; + nse::gui::GLBuffer terrainIndices; - HEMesh polymesh; - MeshRenderer renderer; + nse::gui::GLVertexArray referenceVAO; + nse::gui::GLBuffer referenceVB, referenceIB; - OpenMesh::FPropHandleT faceIdProperty; - OpenMesh::FPropHandleT faceColorProperty; + GLuint grassTexture, rockTexture, roadColorTexture, roadNormalMap, roadSpecularMap, alphaMap; + + GLint grass_texture_location; + GLint rock_texture_location; + GLint alpha_texture_location; + GLint road_texture_location; + GLint road_specular_location; + + nse::gui::GLBuffer offsetBuffer; + + GLuint backgroundFBO, backgroundTexture; }; diff --git a/exercise2/resources/alpha.jpg b/exercise2/resources/alpha.jpg new file mode 100644 index 0000000..938c697 Binary files /dev/null and b/exercise2/resources/alpha.jpg differ diff --git a/exercise2/resources/grass.jpg b/exercise2/resources/grass.jpg new file mode 100644 index 0000000..eb972b6 Binary files /dev/null and b/exercise2/resources/grass.jpg differ diff --git a/exercise2/resources/roadColor.jpg b/exercise2/resources/roadColor.jpg new file mode 100644 index 0000000..4c7a7d2 Binary files /dev/null and b/exercise2/resources/roadColor.jpg differ diff --git a/exercise2/resources/roadNormals.jpg b/exercise2/resources/roadNormals.jpg new file mode 100644 index 0000000..204fa93 Binary files /dev/null and b/exercise2/resources/roadNormals.jpg differ diff --git a/exercise2/resources/roadSpecular.jpg b/exercise2/resources/roadSpecular.jpg new file mode 100644 index 0000000..67b3b02 Binary files /dev/null and b/exercise2/resources/roadSpecular.jpg differ diff --git a/exercise2/resources/rock.jpg b/exercise2/resources/rock.jpg new file mode 100644 index 0000000..002e01f Binary files /dev/null and b/exercise2/resources/rock.jpg differ diff --git a/exercise2/src/Viewer.cpp b/exercise2/src/Viewer.cpp index dd3b377..b15807e 100644 --- a/exercise2/src/Viewer.cpp +++ b/exercise2/src/Viewer.cpp @@ -1,255 +1,377 @@ -// This source code is property of the Computer Graphics and Visualization -// chair of the TU Dresden. Do not distribute! +// 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 -#include -#include -#include -#include #include -#include - -#include - -#include +#include #include -#include "Primitives.h" -#include "SurfaceArea.h" -#include "Volume.h" -#include "ShellExtraction.h" -#include "Smoothing.h" -#include "Stripification.h" +#include -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 }, +#include -}; +#include "glsl.h" +#include "textures.h" + +#include +#include + +constexpr bool REFERENCE = true; +constexpr uint32_t PATCH_SIZE = 256; //number of vertices along one side of the terrain patch Viewer::Viewer() : AbstractViewer("CG1 Exercise 2"), - renderer(polymesh) -{ - SetupGUI(); + terrainPositions(nse::gui::VertexBuffer), terrainIndices(nse::gui::IndexBuffer), + offsetBuffer(nse::gui::VertexBuffer), + referenceVB(nse::gui::VertexBuffer), referenceIB(nse::gui::IndexBuffer) +{ + LoadShaders(); + CreateGeometry(); - polymesh.add_property(faceIdProperty); - polymesh.add_property(faceColorProperty); + grass_texture_location = terrainShader.uniform("grass"); + rock_texture_location = terrainShader.uniform("rock"); + alpha_texture_location = terrainShader.uniform("alpha"); + road_texture_location = terrainShader.uniform("road"); + road_specular_location = terrainShader.uniform("specular_road"); + + //Create a texture and framebuffer for the background + glGenFramebuffers(1, &backgroundFBO); + glGenTextures(1, &backgroundTexture); + + //Align camera to view a reasonable part of the terrain + camera().SetSceneExtent(nse::math::BoundingBox(Eigen::Vector3f(0, 0, 0), Eigen::Vector3f(PATCH_SIZE - 1, 0, PATCH_SIZE - 1))); + camera().FocusOnPoint(0.5f * Eigen::Vector3f(PATCH_SIZE - 1, 15, PATCH_SIZE - 1)); + camera().Zoom(-30); + camera().RotateAroundFocusPointLocal(Eigen::AngleAxisf(-0.5f, Eigen::Vector3f::UnitY()) * Eigen::AngleAxisf(-0.05f, Eigen::Vector3f::UnitX())); + camera().FixClippingPlanes(0.1, 1000); } -void Viewer::SetupGUI() +bool Viewer::resizeEvent(const Eigen::Vector2i &) { - auto mainWindow = SetupMainWindow(); + //Re-generate the texture and FBO for the background + glBindFramebuffer(GL_FRAMEBUFFER, backgroundFBO); + glBindTexture(GL_TEXTURE_2D, backgroundTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width(), height(), 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, backgroundTexture, 0); + auto fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (fboStatus != GL_FRAMEBUFFER_COMPLETE) + std::cout << "Warning: Background framebuffer is not complete: " << fboStatus << std::endl; + glBindFramebuffer(GL_FRAMEBUFFER, 0); - auto loadFileBtn = new nanogui::Button(mainWindow, "Load Mesh"); - loadFileBtn->setCallback([this]() { - std::vector> 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(); + return false; } -void Viewer::ColorMeshFromIds() +inline std::string loadShaderText(const char *path) { - //Set face colors - for (auto f : polymesh.faces()) + std::ifstream is(path); + std::string s_save; + + if (is.is_open()) { - auto shell = polymesh.property(faceIdProperty, f); - if (shell < 0) - polymesh.property(faceColorProperty, f) = Eigen::Vector4f(0, 0, 0, 1); - else + s_save.assign(std::istreambuf_iterator(is), std::istreambuf_iterator()); + } + else + { + std::cout << "could not open " << path << std::endl; + } + + is.close(); + return s_save; +} + +void Viewer::LoadShaders() +{ + std::string sky_vs = loadShaderText("exercise2/glsl/sky.vert"); + std::string sky_fs = loadShaderText("exercise2/glsl/sky.frag"); + + std::string terrain_vs = loadShaderText("exercise2/glsl/terrain.vert"); + std::string terrain_fs = loadShaderText("exercise2/glsl/terrain.frag"); + + skyShader.init("Sky Shader", sky_vs, sky_fs); + terrainShader.init("Terrain Shader", terrain_vs, terrain_fs); +} + +GLuint CreateTexture(const unsigned char *fileData, size_t fileLength, bool repeat = true) +{ + GLuint textureName; + int textureWidth, textureHeight, textureChannels; + auto pixelData = stbi_load_from_memory(fileData, fileLength, &textureWidth, &textureHeight, &textureChannels, 3); + + GLenum provided_format; + + if (textureChannels == 1) + { + provided_format = GL_R; + } + else if (textureChannels == 2) + { + provided_format = GL_RG; + } + else if (textureChannels == 3) + { + provided_format = GL_RGB; + } + else if (textureChannels == 4) + { + provided_format = GL_RGBA; + } + + glGenTextures(1, &textureName); + glBindTexture(GL_TEXTURE_2D, textureName); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, provided_format, GL_UNSIGNED_BYTE, pixelData); + + glGenerateMipmap(GL_TEXTURE_2D); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); + + if (repeat) + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + else + { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + stbi_image_free(pixelData); + return textureName; +} + +std::string vec4_to_str(Eigen::Vector4f &v) +{ + std::stringstream ss; + + ss << "(" << v.x() << ", " + << v.y() << ", " + << v.z() << ", " + << v.w() << ")"; + + //std::cout << ss.str() << std::endl; + + return ss.str(); +} + +void Viewer::CreateGeometry() +{ + //empty VAO for sky + emptyVAO.generate(); + + //terrain VAO + terrainVAO.generate(); + terrainVAO.bind(); + + std::vector positions; + std::vector indices; + + /*Generate positions and indices for a terrain patch with a + single triangle strip */ + + for (int i = 0; i < PATCH_SIZE; i++) + { + for (int j = 0; j < PATCH_SIZE; j++) { - auto& color = segmentColors[shell % segmentColorCount]; - polymesh.property(faceColorProperty, f) = Eigen::Vector4f(color[0], color[1], color[2], 1); + positions.emplace_back((float)j, 0.0f, (float)i, 1.0f); } } - hasColors = true; - MeshUpdated(); + + // full x direction, we skip x = 255 inside the loop + // decrease y direction by 1 + int count = (PATCH_SIZE) * (PATCH_SIZE - 1); + + for (int k = 0; k < count; k++) + { + + // skip creating triangles on last x in row + /* + int end_of_x = k % (PATCH_SIZE); + + if (end_of_x == 0) + { + continue; + } + */ + + indices.emplace_back(k); + indices.emplace_back(k + PATCH_SIZE); + //indices.emplace_back(k + 1); + //indices.emplace_back(k + 1 + PATCH_SIZE); + } + + terrainShader.bind(); + terrainPositions.uploadData(positions).bindToAttribute("position"); + terrainIndices.uploadData(indices.size() * sizeof(uint32_t), indices.data()); + + referenceVAO.generate(); + referenceVAO.bind(); + + std::vector ref_pos; + std::vector ref_ind; + + for (int i = 0; i < PATCH_SIZE; i++) + { + for (int j = 0; j < PATCH_SIZE; j++) + { + ref_pos.emplace_back((float)j, 0.0f, (float)i, 1.0f); + } + } + + int index_width = PATCH_SIZE - 1; + + for (int i = 0; i < index_width; i++) + { + for (int j = 0; j < index_width; j++) + { + int p1 = i + (PATCH_SIZE * j); + int p2 = i + (PATCH_SIZE * j) + 1; + int p3 = i + (PATCH_SIZE * (j + 1)); + int p4 = i + (PATCH_SIZE * (j + 1)) + 1; + + ref_ind.emplace_back(p1); + ref_ind.emplace_back(p3); + ref_ind.emplace_back(p2); + + ref_ind.emplace_back(p3); + ref_ind.emplace_back(p2); + ref_ind.emplace_back(p4); + } + } + + referenceVB.uploadData(ref_pos) + .bindToAttribute("position"); + referenceIB.uploadData(ref_ind.size() * sizeof(uint32_t), ref_ind.data()); + + //textures + grassTexture = CreateTexture((unsigned char *)grass_jpg, grass_jpg_size); + rockTexture = CreateTexture((unsigned char *)rock_jpg, rock_jpg_size); + roadColorTexture = CreateTexture((unsigned char *)roadcolor_jpg, roadcolor_jpg_size); + roadNormalMap = CreateTexture((unsigned char *)roadnormals_jpg, roadnormals_jpg_size); + roadSpecularMap = CreateTexture((unsigned char *)roadspecular_jpg, roadspecular_jpg_size); + alphaMap = CreateTexture((unsigned char *)alpha_jpg, alpha_jpg_size, false); } -void Viewer::MeshUpdated(bool initNewMesh) +void Viewer::RenderSky() { - if (initNewMesh) - { - hasColors = false; + Eigen::Matrix4f skyView = view; + for (int i = 0; i < 3; ++i) + skyView.col(i).normalize(); + skyView.col(3).head<3>().setZero(); + Eigen::Matrix4f skyMvp = proj * skyView; + glDepthMask(GL_FALSE); + glEnable(GL_DEPTH_CLAMP); + emptyVAO.bind(); + skyShader.bind(); + skyShader.setUniform("mvp", skyMvp); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 6); + glDisable(GL_DEPTH_CLAMP); + glDepthMask(GL_TRUE); - //calculate the bounding box of the mesh - nse::math::BoundingBox bbox; - for (auto v : polymesh.vertices()) - bbox.expand(ToEigenVector(polymesh.point(v))); - camera().FocusOnBBox(bbox); - } + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, backgroundFBO); + glBlitFramebuffer(0, 0, width(), height(), 0, 0, width(), height(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +} - if (hasColors) - renderer.UpdateWithPerFaceColor(faceColorProperty); - else - renderer.Update(); +void CalculateViewFrustum(const Eigen::Matrix4f &mvp, Eigen::Vector4f *frustumPlanes, nse::math::BoundingBox &bbox) +{ + frustumPlanes[0] = (mvp.row(3) + mvp.row(0)).transpose(); + frustumPlanes[1] = (mvp.row(3) - mvp.row(0)).transpose(); + frustumPlanes[2] = (mvp.row(3) + mvp.row(1)).transpose(); + frustumPlanes[3] = (mvp.row(3) - mvp.row(1)).transpose(); + frustumPlanes[4] = (mvp.row(3) + mvp.row(2)).transpose(); + frustumPlanes[5] = (mvp.row(3) - mvp.row(2)).transpose(); + + Eigen::Matrix4f invMvp = mvp.inverse(); + bbox.reset(); + for (int x = -1; x <= 1; x += 2) + for (int y = -1; y <= 1; y += 2) + for (int z = -1; z <= 1; z += 2) + { + Eigen::Vector4f corner = invMvp * Eigen::Vector4f(x, y, z, 1); + corner /= corner.w(); + bbox.expand(corner.head<3>()); + } +} + +bool IsBoxCompletelyBehindPlane(const Eigen::Vector3f &boxMin, const Eigen::Vector3f &boxMax, const Eigen::Vector4f &plane) +{ + return plane.dot(Eigen::Vector4f(boxMin.x(), boxMin.y(), boxMin.z(), 1)) < 0 && + plane.dot(Eigen::Vector4f(boxMin.x(), boxMin.y(), boxMax.z(), 1)) < 0 && + plane.dot(Eigen::Vector4f(boxMin.x(), boxMax.y(), boxMin.z(), 1)) < 0 && + plane.dot(Eigen::Vector4f(boxMin.x(), boxMax.y(), boxMin.z(), 1)) < 0 && + plane.dot(Eigen::Vector4f(boxMax.x(), boxMin.y(), boxMin.z(), 1)) < 0 && + plane.dot(Eigen::Vector4f(boxMax.x(), boxMin.y(), boxMax.z(), 1)) < 0 && + plane.dot(Eigen::Vector4f(boxMax.x(), boxMax.y(), boxMin.z(), 1)) < 0 && + plane.dot(Eigen::Vector4f(boxMax.x(), boxMax.y(), boxMin.z(), 1)) < 0; } void Viewer::drawContents() { - glEnable(GL_DEPTH_TEST); - - Eigen::Matrix4f view, proj; camera().ComputeCameraMatrices(view, proj); - renderer.Render(view, proj, shadingBtn->selectedIndex() == 1); + Eigen::Matrix4f mvp = proj * view; + Eigen::Vector3f cameraPosition = view.inverse().col(3).head<3>(); + int visiblePatches = 0; + + RenderSky(); + + //render terrain + glEnable(GL_DEPTH_TEST); + + if (REFERENCE) + { + referenceVAO.bind(); + } + else + { + terrainVAO.bind(); + } + + terrainShader.bind(); + + terrainShader.setUniform("screenSize", Eigen::Vector2f(width(), height()), false); + terrainShader.setUniform("mvp", mvp); + terrainShader.setUniform("cameraPos", cameraPosition, false); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, grassTexture); + glUniform1i(grass_texture_location, 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, rockTexture); + glUniform1i(rock_texture_location, 1); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, alphaMap); + glUniform1i(alpha_texture_location, 2); + + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, roadColorTexture); + glUniform1i(road_texture_location, 3); + + glActiveTexture(GL_TEXTURE4); + glBindTexture(GL_TEXTURE_2D, roadSpecularMap); + glUniform1i(road_specular_location, 4); + + if (REFERENCE) + { + glDrawElements(GL_TRIANGLES, (PATCH_SIZE - 1) * (PATCH_SIZE - 1) * 6, GL_UNSIGNED_INT, 0); + } + else + { + glPrimitiveRestartIndex(PATCH_SIZE * 2); + glDrawElements(GL_TRIANGLE_STRIP, (PATCH_SIZE - 1) * PATCH_SIZE * 2, GL_UNSIGNED_INT, 0); + } + + //Render text + nvgBeginFrame(mNVGContext, width(), height(), mPixelRatio); + std::string text = "Patches visible: " + std::to_string(visiblePatches); + nvgText(mNVGContext, 10, 20, text.c_str(), nullptr); + nvgEndFrame(mNVGContext); } \ No newline at end of file diff --git a/exercise3/CMakeLists.txt b/exercise3/CMakeLists.txt index 4d0a509..f83db3f 100644 --- a/exercise3/CMakeLists.txt +++ b/exercise3/CMakeLists.txt @@ -3,14 +3,12 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/inc 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 - ) + 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(Exercise3 CG1Common ${LIBS}) \ No newline at end of file diff --git a/exercise2/include/Primitives.h b/exercise3/include/Primitives.h similarity index 100% rename from exercise2/include/Primitives.h rename to exercise3/include/Primitives.h diff --git a/exercise2/include/ShellExtraction.h b/exercise3/include/ShellExtraction.h similarity index 100% rename from exercise2/include/ShellExtraction.h rename to exercise3/include/ShellExtraction.h diff --git a/exercise2/include/Smoothing.h b/exercise3/include/Smoothing.h similarity index 100% rename from exercise2/include/Smoothing.h rename to exercise3/include/Smoothing.h diff --git a/exercise2/include/Stripification.h b/exercise3/include/Stripification.h similarity index 100% rename from exercise2/include/Stripification.h rename to exercise3/include/Stripification.h diff --git a/exercise2/include/SurfaceArea.h b/exercise3/include/SurfaceArea.h similarity index 100% rename from exercise2/include/SurfaceArea.h rename to exercise3/include/SurfaceArea.h diff --git a/exercise3/include/Viewer.h b/exercise3/include/Viewer.h index fcda12f..29d694b 100644 --- a/exercise3/include/Viewer.h +++ b/exercise3/include/Viewer.h @@ -5,17 +5,8 @@ #pragma once #include -#include #include -#include "AABBTree.h" -#include "HashGrid.h" -#include "Point.h" -#include "LineSegment.h" -#include "Triangle.h" - -#include - class Viewer : public nse::gui::AbstractViewer { public: @@ -24,67 +15,21 @@ public: void drawContents(); private: - - enum PrimitiveType - { - Vertex, Edge, Tri - }; - void SetupGUI(); - void MeshUpdated(); + void MeshUpdated(bool initNewMesh = false); - void FindClosestPoint(const Eigen::Vector3f& p); - void BuildGridVBO(); - void BuildRayVBOs(); + void ColorMeshFromIds(); - template - void BuildGridVBO(const Grid& grid) - { - std::vector 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& positions); + bool hasColors = false; nanogui::ComboBox* shadingBtn; - nanogui::CheckBox* chkRenderMesh; - nanogui::CheckBox* chkRenderGrid; - nanogui::CheckBox* chkRenderRay; - int raySteps; + unsigned int smoothingIterations; + nanogui::Slider* sldSmoothingStrength; + unsigned int stripificationTrials; - nse::gui::VectorInput* sldQuery, *sldRayOrigin, *sldRayDir; - nanogui::ComboBox* cmbPrimitiveType; - HEMesh polymesh; - float bboxMaxLength; MeshRenderer renderer; - - AABBTree vertexTree; - AABBTree edgeTree; - AABBTree triangleTree; - - HashGrid vertexGrid; - HashGrid edgeGrid; - HashGrid 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; + OpenMesh::FPropHandleT faceIdProperty; + OpenMesh::FPropHandleT faceColorProperty; }; diff --git a/exercise2/include/Volume.h b/exercise3/include/Volume.h similarity index 100% rename from exercise2/include/Volume.h rename to exercise3/include/Volume.h diff --git a/exercise2/include/sample_set.h b/exercise3/include/sample_set.h similarity index 100% rename from exercise2/include/sample_set.h rename to exercise3/include/sample_set.h diff --git a/exercise2/src/Primitives.cpp b/exercise3/src/Primitives.cpp similarity index 100% rename from exercise2/src/Primitives.cpp rename to exercise3/src/Primitives.cpp diff --git a/exercise2/src/ShellExtraction.cpp b/exercise3/src/ShellExtraction.cpp similarity index 100% rename from exercise2/src/ShellExtraction.cpp rename to exercise3/src/ShellExtraction.cpp diff --git a/exercise2/src/Smoothing.cpp b/exercise3/src/Smoothing.cpp similarity index 100% rename from exercise2/src/Smoothing.cpp rename to exercise3/src/Smoothing.cpp diff --git a/exercise2/src/Stripification.cpp b/exercise3/src/Stripification.cpp similarity index 100% rename from exercise2/src/Stripification.cpp rename to exercise3/src/Stripification.cpp diff --git a/exercise2/src/SurfaceArea.cpp b/exercise3/src/SurfaceArea.cpp similarity index 100% rename from exercise2/src/SurfaceArea.cpp rename to exercise3/src/SurfaceArea.cpp diff --git a/exercise3/src/Viewer.cpp b/exercise3/src/Viewer.cpp index e0f22e7..dd3b377 100644 --- a/exercise3/src/Viewer.cpp +++ b/exercise3/src/Viewer.cpp @@ -8,33 +8,49 @@ #include #include #include +#include #include #include -#include - #include +#include + #include -#include +#include "Primitives.h" +#include "SurfaceArea.h" +#include "Volume.h" +#include "ShellExtraction.h" +#include "Smoothing.h" +#include "Stripification.h" -#include -#include "GridTraverser.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 3"), - renderer(polymesh), - closestPositions(nse::gui::VertexBuffer), - gridPositions(nse::gui::VertexBuffer), - rayPositions(nse::gui::VertexBuffer), rayCellsPositions(nse::gui::VertexBuffer) + : AbstractViewer("CG1 Exercise 2"), + renderer(polymesh) { SetupGUI(); - closestVAO.generate(); - gridVAO.generate(); - rayVAO.generate(); - rayCellsVAO.generate(); + polymesh.add_property(faceIdProperty); + polymesh.add_property(faceColorProperty); } void Viewer::SetupGUI() @@ -55,233 +71,185 @@ void Viewer::SetupGUI() "The specified file could not be loaded"); } else - MeshUpdated(); + MeshUpdated(true); } - }); - - 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); + 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::FindClosestPoint(const Eigen::Vector3f& p) +void Viewer::ColorMeshFromIds() { - if (polymesh.vertices_empty()) - return; - Eigen::Vector3f closest; - auto timeStart = std::chrono::high_resolution_clock::now(); - switch (cmbPrimitiveType->selectedIndex()) + //Set face colors + for (auto f : polymesh.faces()) { - case Vertex: - closest = vertexTree.ClosestPoint(p); - break; - case Edge: - closest = edgeTree.ClosestPoint(p); - break; - case Tri: - closest = triangleTree.ClosestPoint(p); - break; + 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 bbox; + for (auto v : polymesh.vertices()) + bbox.expand(ToEigenVector(polymesh.point(v))); + camera().FocusOnBBox(bbox); } - auto timeEnd = std::chrono::high_resolution_clock::now(); - std::cout << std::fixed << "Closest point query took " << std::chrono::duration_cast(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 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 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& 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); + if (hasColors) + renderer.UpdateWithPerFaceColor(faceColorProperty); + else + renderer.Update(); } void Viewer::drawContents() { glEnable(GL_DEPTH_TEST); - if (!polymesh.vertices_empty()) - { - Eigen::Matrix4f view, proj; - camera().ComputeCameraMatrices(view, proj); - Eigen::Matrix4f mvp = proj * view; + Eigen::Matrix4f view, proj; + camera().ComputeCameraMatrices(view, proj); - 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(); - } - } + renderer.Render(view, proj, shadingBtn->selectedIndex() == 1); } \ No newline at end of file diff --git a/exercise2/src/Volume.cpp b/exercise3/src/Volume.cpp similarity index 100% rename from exercise2/src/Volume.cpp rename to exercise3/src/Volume.cpp diff --git a/exercise3/src/main.cpp b/exercise3/src/main.cpp index e94f898..e01821b 100644 --- a/exercise3/src/main.cpp +++ b/exercise3/src/main.cpp @@ -10,11 +10,9 @@ int main(int argc, char* argv[]) { - std::cout.imbue(std::locale("")); - nanogui::init(); - { + { nanogui::ref viewer = new Viewer(); viewer->setVisible(true); diff --git a/exercise4/CMakeLists.txt b/exercise4/CMakeLists.txt index dc3fd08..dc2f1b1 100644 --- a/exercise4/CMakeLists.txt +++ b/exercise4/CMakeLists.txt @@ -3,8 +3,14 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/inc 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 + 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(Exercise4 CG1Common ${LIBS}) \ No newline at end of file diff --git a/exercise3/include/AABBTree.h b/exercise4/include/AABBTree.h similarity index 100% rename from exercise3/include/AABBTree.h rename to exercise4/include/AABBTree.h diff --git a/exercise3/include/Box.h b/exercise4/include/Box.h similarity index 100% rename from exercise3/include/Box.h rename to exercise4/include/Box.h diff --git a/exercise3/include/GridTraverser.h b/exercise4/include/GridTraverser.h similarity index 100% rename from exercise3/include/GridTraverser.h rename to exercise4/include/GridTraverser.h diff --git a/exercise3/include/GridUtils.h b/exercise4/include/GridUtils.h similarity index 100% rename from exercise3/include/GridUtils.h rename to exercise4/include/GridUtils.h diff --git a/exercise3/include/HashGrid.h b/exercise4/include/HashGrid.h similarity index 100% rename from exercise3/include/HashGrid.h rename to exercise4/include/HashGrid.h diff --git a/exercise3/include/LineSegment.h b/exercise4/include/LineSegment.h similarity index 100% rename from exercise3/include/LineSegment.h rename to exercise4/include/LineSegment.h diff --git a/exercise3/include/Point.h b/exercise4/include/Point.h similarity index 100% rename from exercise3/include/Point.h rename to exercise4/include/Point.h diff --git a/exercise3/include/Triangle.h b/exercise4/include/Triangle.h similarity index 100% rename from exercise3/include/Triangle.h rename to exercise4/include/Triangle.h diff --git a/exercise4/include/Viewer.h b/exercise4/include/Viewer.h index 00dcbad..fcda12f 100644 --- a/exercise4/include/Viewer.h +++ b/exercise4/include/Viewer.h @@ -8,8 +8,13 @@ #include #include -#include "Registration.h" -#include +#include "AABBTree.h" +#include "HashGrid.h" +#include "Point.h" +#include "LineSegment.h" +#include "Triangle.h" + +#include class Viewer : public nse::gui::AbstractViewer { @@ -18,32 +23,68 @@ public: void drawContents(); - virtual bool resizeEvent(const Eigen::Vector2i&); +private: -private: + enum PrimitiveType + { + Vertex, Edge, Tri + }; void SetupGUI(); - void MeshUpdated(); - void BuildCorrVBOs(); + void MeshUpdated(); - nse::math::BoundingBox meshBbox, expandedBbox; + void FindClosestPoint(const Eigen::Vector3f& p); + void BuildGridVBO(); + void BuildRayVBOs(); + + template + void BuildGridVBO(const Grid& grid) + { + std::vector 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& positions); nanogui::ComboBox* shadingBtn; - nanogui::CheckBox* chkRenderTextureMap; - nanogui::CheckBox* chkRenderSecondMesh; + nanogui::CheckBox* chkRenderMesh; + nanogui::CheckBox* chkRenderGrid; + nanogui::CheckBox* chkRenderRay; + int raySteps; + + nse::gui::VectorInput* sldQuery, *sldRayOrigin, *sldRayDir; + nanogui::ComboBox* cmbPrimitiveType; HEMesh polymesh; - MeshRenderer renderer; + float bboxMaxLength; + MeshRenderer renderer; + + AABBTree vertexTree; + AABBTree edgeTree; + AABBTree triangleTree; + + HashGrid vertexGrid; + HashGrid edgeGrid; + HashGrid triangleGrid; - bool hasParametrization = false; - Eigen::Matrix4f texMapProjectionMatrix; + nse::gui::GLBuffer closestPositions; + nse::gui::GLVertexArray closestVAO; - Eigen::Affine3f secondMeshTransform; + nse::gui::GLBuffer gridPositions; + nse::gui::GLVertexArray gridVAO; + GLuint gridIndices; - std::mt19937 rnd; - std::vector correspondences; - - size_t corrCount; - nse::gui::GLBuffer corrPositions; - nse::gui::GLVertexArray corrVAO; + nse::gui::GLBuffer rayPositions, rayCellsPositions; + nse::gui::GLVertexArray rayVAO, rayCellsVAO; + GLuint rayCellsIndices; }; diff --git a/exercise3/src/AABBTree.cpp b/exercise4/src/AABBTree.cpp similarity index 100% rename from exercise3/src/AABBTree.cpp rename to exercise4/src/AABBTree.cpp diff --git a/exercise3/src/Box.cpp b/exercise4/src/Box.cpp similarity index 100% rename from exercise3/src/Box.cpp rename to exercise4/src/Box.cpp diff --git a/exercise3/src/GridTraverser.cpp b/exercise4/src/GridTraverser.cpp similarity index 100% rename from exercise3/src/GridTraverser.cpp rename to exercise4/src/GridTraverser.cpp diff --git a/exercise3/src/HashGrid.cpp b/exercise4/src/HashGrid.cpp similarity index 100% rename from exercise3/src/HashGrid.cpp rename to exercise4/src/HashGrid.cpp diff --git a/exercise3/src/LineSegment.cpp b/exercise4/src/LineSegment.cpp similarity index 100% rename from exercise3/src/LineSegment.cpp rename to exercise4/src/LineSegment.cpp diff --git a/exercise3/src/Point.cpp b/exercise4/src/Point.cpp similarity index 100% rename from exercise3/src/Point.cpp rename to exercise4/src/Point.cpp diff --git a/exercise3/src/Triangle.cpp b/exercise4/src/Triangle.cpp similarity index 100% rename from exercise3/src/Triangle.cpp rename to exercise4/src/Triangle.cpp diff --git a/exercise4/src/Viewer.cpp b/exercise4/src/Viewer.cpp index 92ba141..e0f22e7 100644 --- a/exercise4/src/Viewer.cpp +++ b/exercise4/src/Viewer.cpp @@ -13,23 +13,28 @@ #include +#include + #include -#include "Parametrization.h" -#include "Registration.h" +#include #include +#include "GridTraverser.h" Viewer::Viewer() - : AbstractViewer("CG1 Exercise 4"), + : AbstractViewer("CG1 Exercise 3"), renderer(polymesh), - corrPositions(nse::gui::VertexBuffer) + closestPositions(nse::gui::VertexBuffer), + gridPositions(nse::gui::VertexBuffer), + rayPositions(nse::gui::VertexBuffer), rayCellsPositions(nse::gui::VertexBuffer) { - polymesh.request_vertex_texcoords2D(); - - corrVAO.generate(); - SetupGUI(); + + closestVAO.generate(); + gridVAO.generate(); + rayVAO.generate(); + rayCellsVAO.generate(); } void Viewer::SetupGUI() @@ -52,184 +57,179 @@ void Viewer::SetupGUI() 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(); +} - 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)); +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(timeEnd - timeStart).count() << " microseconds." << std::endl; - 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(polymesh); - break; - case 1: - success = ComputeParametrizationOfTopologicalDisk(polymesh); - break; - case 2: - success = ComputeParametrizationOfTopologicalDisk(polymesh); - break; - case 3: - success = ComputeParametrizationOfTopologicalDisk(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 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 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 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(); + 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 - meshBbox.reset(); + nse::math::BoundingBox bbox; for (auto v : polymesh.vertices()) - meshBbox.expand(ToEigenVector(polymesh.point(v))); + bbox.expand(ToEigenVector(polymesh.point(v))); + camera().FocusOnBBox(bbox); + bboxMaxLength = bbox.diagonal().maxCoeff(); - 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(); + polymesh.triangulate(); - hasParametrization = false; + 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(); - BuildCorrVBOs(); } -void Viewer::BuildCorrVBOs() +void Viewer::BuildGridVBO() { - std::vector pos; - pos.reserve(correspondences.size() * 2); - for (auto& c : correspondences) + switch (cmbPrimitiveType->selectedIndex()) { - 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)); + 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 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); } - corrVAO.bind(); - ShaderPool::Instance()->simpleShader.bind(); - corrPositions.uploadData(pos).bindToAttribute("position"); - corrVAO.unbind(); + rayCellsVAO.bind(); + rayCellsPositions.uploadData(cellPositions).bindToAttribute("position"); + rayCellsVAO.unbind(); + rayCellsIndices = (GLuint)cellPositions.size(); } -bool Viewer::resizeEvent(const Eigen::Vector2i& size) +void Viewer::AddBoxVertices(const Box & box, std::vector& positions) { - AbstractViewer::resizeEvent(size); + 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); - 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; + 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() @@ -242,26 +242,46 @@ void Viewer::drawContents() camera().ComputeCameraMatrices(view, proj); Eigen::Matrix4f mvp = proj * view; - renderer.Render(view, proj, shadingBtn->selectedIndex() == 1, hasParametrization); + if(chkRenderMesh->checked()) + renderer.Render(view, proj, shadingBtn->selectedIndex() == 1); - if (chkRenderSecondMesh->checked()) + 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()) { - 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(); - } + gridVAO.bind(); + ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(0.2f, 0.2f, 0.2f, 1)); + glDrawArrays(GL_LINES, 0, gridIndices); + gridVAO.unbind(); } - if (hasParametrization && chkRenderTextureMap->checked()) + if (chkRenderRay->checked()) { - glDisable(GL_DEPTH_TEST); - renderer.RenderTextureMap(texMapProjectionMatrix, Eigen::Vector4f(1, 1, 1, 1)); + //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(); } } } \ No newline at end of file diff --git a/exercise5/CMakeLists.txt b/exercise5/CMakeLists.txt new file mode 100644 index 0000000..c2e52b1 --- /dev/null +++ b/exercise5/CMakeLists.txt @@ -0,0 +1,10 @@ +include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include ) + +add_executable(Exercise5 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(Exercise5 CG1Common ${LIBS}) \ No newline at end of file diff --git a/exercise4/include/Parametrization.h b/exercise5/include/Parametrization.h similarity index 100% rename from exercise4/include/Parametrization.h rename to exercise5/include/Parametrization.h diff --git a/exercise4/include/Registration.h b/exercise5/include/Registration.h similarity index 100% rename from exercise4/include/Registration.h rename to exercise5/include/Registration.h diff --git a/exercise5/include/Viewer.h b/exercise5/include/Viewer.h new file mode 100644 index 0000000..00dcbad --- /dev/null +++ b/exercise5/include/Viewer.h @@ -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 +#include +#include + +#include "Registration.h" +#include + +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 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 correspondences; + + size_t corrCount; + nse::gui::GLBuffer corrPositions; + nse::gui::GLVertexArray corrVAO; +}; diff --git a/exercise4/src/Parametrization.cpp b/exercise5/src/Parametrization.cpp similarity index 100% rename from exercise4/src/Parametrization.cpp rename to exercise5/src/Parametrization.cpp diff --git a/exercise4/src/Registration.cpp b/exercise5/src/Registration.cpp similarity index 100% rename from exercise4/src/Registration.cpp rename to exercise5/src/Registration.cpp diff --git a/exercise5/src/Viewer.cpp b/exercise5/src/Viewer.cpp new file mode 100644 index 0000000..92ba141 --- /dev/null +++ b/exercise5/src/Viewer.cpp @@ -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 +#include +#include +#include +#include +#include + +#include + +#include + +#include "Parametrization.h" +#include "Registration.h" + +#include + +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> 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(polymesh); + break; + case 1: + success = ComputeParametrizationOfTopologicalDisk(polymesh); + break; + case 2: + success = ComputeParametrizationOfTopologicalDisk(polymesh); + break; + case 3: + success = ComputeParametrizationOfTopologicalDisk(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 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 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 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 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)); + } + } +} \ No newline at end of file diff --git a/exercise5/src/main.cpp b/exercise5/src/main.cpp new file mode 100644 index 0000000..e94f898 --- /dev/null +++ b/exercise5/src/main.cpp @@ -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 + +#include + +#include "Viewer.h" + +int main(int argc, char* argv[]) +{ + std::cout.imbue(std::locale("")); + + nanogui::init(); + + { + nanogui::ref 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; +} \ No newline at end of file diff --git a/solution/linux/Exercise2 b/solution/linux/Exercise2 index 969b19b..369df2c 100644 Binary files a/solution/linux/Exercise2 and b/solution/linux/Exercise2 differ diff --git a/solution/linux/Exercise3 b/solution/linux/Exercise3 index a0169c8..969b19b 100644 Binary files a/solution/linux/Exercise3 and b/solution/linux/Exercise3 differ diff --git a/solution/linux/Exercise4 b/solution/linux/Exercise4 index b278ca2..a0169c8 100644 Binary files a/solution/linux/Exercise4 and b/solution/linux/Exercise4 differ diff --git a/solution/linux/Exercise5 b/solution/linux/Exercise5 new file mode 100644 index 0000000..b278ca2 Binary files /dev/null and b/solution/linux/Exercise5 differ diff --git a/solution/win/Exercise2.exe b/solution/win/Exercise2.exe index 1c44ab9..03e057f 100644 Binary files a/solution/win/Exercise2.exe and b/solution/win/Exercise2.exe differ diff --git a/solution/win/Exercise3.exe b/solution/win/Exercise3.exe index 0e182f3..1c44ab9 100644 Binary files a/solution/win/Exercise3.exe and b/solution/win/Exercise3.exe differ diff --git a/solution/win/Exercise4.exe b/solution/win/Exercise4.exe index 4c4c4b1..0e182f3 100644 Binary files a/solution/win/Exercise4.exe and b/solution/win/Exercise4.exe differ diff --git a/solution/win/Exercise5.exe b/solution/win/Exercise5.exe new file mode 100644 index 0000000..4c4c4b1 Binary files /dev/null and b/solution/win/Exercise5.exe differ