conflict
This commit is contained in:
commit
3cd3b20600
76 changed files with 1720 additions and 756 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,4 @@
|
||||||
build/
|
build/
|
||||||
|
*.log
|
||||||
|
*.synctex.gz
|
||||||
|
*.aux
|
||||||
|
|
29
.vscode/launch.json
vendored
Normal file
29
.vscode/launch.json
vendored
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
|
@ -1 +1,6 @@
|
||||||
{}
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"bitset": "cpp",
|
||||||
|
"chrono": "cpp"
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,3 +48,4 @@ add_subdirectory(exercise1)
|
||||||
add_subdirectory(exercise2)
|
add_subdirectory(exercise2)
|
||||||
add_subdirectory(exercise3)
|
add_subdirectory(exercise3)
|
||||||
add_subdirectory(exercise4)
|
add_subdirectory(exercise4)
|
||||||
|
add_subdirectory(exercise5)
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace nse {
|
||||||
Eigen::Matrix4f &proj,
|
Eigen::Matrix4f &proj,
|
||||||
float customAspectRatio = 0) const;
|
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);
|
void Zoom(float amount);
|
||||||
|
|
||||||
//Sets the extent of the scene, which is kept between znear/zfar.
|
//Sets the extent of the scene, which is kept between znear/zfar.
|
||||||
|
@ -64,6 +64,8 @@ namespace nse {
|
||||||
//Forwarded resize event.
|
//Forwarded resize event.
|
||||||
void resize(const Eigen::Vector2i & s);
|
void resize(const Eigen::Vector2i & s);
|
||||||
|
|
||||||
|
void FixClippingPlanes(float znear, float zfar);
|
||||||
|
|
||||||
//Returns the point that the camera focuses on
|
//Returns the point that the camera focuses on
|
||||||
const Eigen::Vector3f& GetFocusPoint() const;
|
const Eigen::Vector3f& GetFocusPoint() const;
|
||||||
|
|
||||||
|
@ -99,6 +101,8 @@ namespace nse {
|
||||||
|
|
||||||
Eigen::Vector3f modelTranslation_start = Eigen::Vector3f::Zero();
|
Eigen::Vector3f modelTranslation_start = Eigen::Vector3f::Zero();
|
||||||
Eigen::Vector2i translation_start; //mouse position on the screen where translation started
|
Eigen::Vector2i translation_start; //mouse position on the screen where translation started
|
||||||
|
|
||||||
|
float fixedZNear, fixedZFar;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
using namespace nse::gui;
|
using namespace nse::gui;
|
||||||
|
|
||||||
Camera::Camera(const nanogui::Widget & parent)
|
Camera::Camera(const nanogui::Widget & parent)
|
||||||
: parent(parent)
|
: parent(parent), fixedZNear(std::numeric_limits<float>::quiet_NaN())
|
||||||
{
|
{
|
||||||
params.arcball.setSize(parent.size());
|
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 depthOfSceneCenter = (params.sceneCenter - cameraPosition).dot(viewDirection);
|
||||||
float minZNear = 0.001f * params.sceneRadius;
|
float minZNear = 0.001f * params.sceneRadius;
|
||||||
float znear = std::max(minZNear, depthOfSceneCenter - params.sceneRadius);
|
float znear, zfar;
|
||||||
float zfar = std::max(znear + minZNear, depthOfSceneCenter + params.sceneRadius);
|
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 fH = std::tan(params.fovy / 360.0f * (float)M_PI) * znear;
|
||||||
float aspectRatio = customAspectRatio == 0 ? (float)parent.width() / parent.height() : customAspectRatio;
|
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<float, 3>& bbox)
|
void Camera::SetSceneExtent(const nse::math::BoundingBox<float, 3>& bbox)
|
||||||
{
|
{
|
||||||
|
fixedZNear = std::numeric_limits<float>::quiet_NaN();
|
||||||
params.sceneCenter = 0.5f * (bbox.min + bbox.max);
|
params.sceneCenter = 0.5f * (bbox.min + bbox.max);
|
||||||
params.sceneRadius = bbox.diagonal().norm() / 2.0f;
|
params.sceneRadius = bbox.diagonal().norm() / 2.0f;
|
||||||
}
|
}
|
||||||
|
@ -175,3 +185,9 @@ void Camera::resize(const Eigen::Vector2i & s)
|
||||||
{
|
{
|
||||||
params.arcball.setSize(s);
|
params.arcball.setSize(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Camera::FixClippingPlanes(float znear, float zfar)
|
||||||
|
{
|
||||||
|
fixedZNear = znear;
|
||||||
|
fixedZFar = zfar;
|
||||||
|
}
|
|
@ -132,5 +132,8 @@ void nse::util::GLDebug::SetupDebugCallback()
|
||||||
{
|
{
|
||||||
glEnable(GL_DEBUG_OUTPUT);
|
glEnable(GL_DEBUG_OUTPUT);
|
||||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
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);
|
||||||
}
|
}
|
BIN
exercise1/Theorie.pdf
Normal file
BIN
exercise1/Theorie.pdf
Normal file
Binary file not shown.
151
exercise1/Theorie.tex
Normal file
151
exercise1/Theorie.tex
Normal file
|
@ -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}
|
||||||
|
|
|
@ -18,4 +18,9 @@ void main()
|
||||||
gl_Position = proj * view * in_position;
|
gl_Position = proj * view * in_position;
|
||||||
|
|
||||||
fragment_color = in_color;
|
fragment_color = in_color;
|
||||||
|
<<<<<<< HEAD
|
||||||
|
=======
|
||||||
|
|
||||||
|
julia_position = julia_positions[gl_VertexID % 3];
|
||||||
|
>>>>>>> 8b4276f6ab9ed1d7d6e1909f36944eaa9a8964da
|
||||||
}
|
}
|
|
@ -8,31 +8,41 @@
|
||||||
|
|
||||||
class Viewer : public nse::gui::AbstractViewer
|
class Viewer : public nse::gui::AbstractViewer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Viewer();
|
Viewer();
|
||||||
|
|
||||||
void drawContents();
|
void drawContents();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupGUI();
|
void SetupGUI();
|
||||||
|
|
||||||
Eigen::Matrix4f modelViewMatrix, projectionMatrix;
|
Eigen::Matrix4f modelViewMatrix, projectionMatrix;
|
||||||
|
|
||||||
//GUI Elements for the various options
|
//GUI Elements for the various options
|
||||||
nanogui::CheckBox* chkHasFaceCulling; //Shall back face culling be activated?
|
nanogui::CheckBox *chkHasFaceCulling; //Shall back face culling be activated?
|
||||||
nanogui::CheckBox* chkHasDepthTesting; //Shall depth testing be activated?
|
nanogui::CheckBox *chkHasDepthTesting; //Shall depth testing be activated?
|
||||||
|
|
||||||
nanogui::Slider* sldJuliaCX; //Seed for the Julia fractal
|
nanogui::Slider *sldJuliaCX; //Seed for the Julia fractal
|
||||||
nanogui::Slider* sldJuliaCY;
|
nanogui::Slider *sldJuliaCY;
|
||||||
nanogui::Slider* sldJuliaZoom; //Zoom factor for the Julia fractal
|
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
|
// The following variables hold OpenGL object IDs
|
||||||
GLuint vertex_shader_id, // ID of the vertex shader
|
GLuint vertex_shader_id, // ID of the vertex shader
|
||||||
fragment_shader_id, // ID of the fragment shader
|
fragment_shader_id, // ID of the fragment shader
|
||||||
program_id, // ID of the shader program
|
program_id, // ID of the shader program
|
||||||
vertex_array_id, // ID of the vertex array
|
vertex_array_id, // ID of the vertex array
|
||||||
position_buffer_id, // ID of the position buffer
|
position_buffer_id, // ID of the position buffer
|
||||||
color_buffer_id, // ID of the color buffer
|
color_buffer_id, // ID of the color buffer
|
||||||
uv_map_buffer_id; // ID of the uv_map
|
uv_map_buffer_id; // ID of the uv_map
|
||||||
|
|
||||||
GLint view_uniform_id;
|
GLint view_uniform_id;
|
||||||
|
@ -40,7 +50,13 @@ private:
|
||||||
GLint julia_m_id;
|
GLint julia_m_id;
|
||||||
GLint julia_c_id;
|
GLint julia_c_id;
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
// Read, Compile and link the shader codes to a shader program
|
// 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();
|
void CreateShaders();
|
||||||
// Create and define the vertex array and add a number of vertex buffers
|
// Create and define the vertex array and add a number of vertex buffers
|
||||||
void CreateVertexBuffers();
|
void CreateVertexBuffers();
|
||||||
|
|
|
@ -50,6 +50,16 @@ void Viewer::SetupGUI()
|
||||||
sldJuliaCY = nse::gui::AddLabeledSliderWithDefaultDisplay(mainWindow, "JuliaC.Y", std::make_pair(-1.0f, 1.0f), -0.3f, 2);
|
sldJuliaCY = nse::gui::AddLabeledSliderWithDefaultDisplay(mainWindow, "JuliaC.Y", std::make_pair(-1.0f, 1.0f), -0.3f, 2);
|
||||||
sldJuliaZoom = nse::gui::AddLabeledSliderWithDefaultDisplay(mainWindow, "Julia Zoom", std::make_pair(0.01f, 10.0f), 1.0f, 2);
|
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();
|
performLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 )
|
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include )
|
||||||
|
|
||||||
add_executable(Exercise2 MACOSX_BUNDLE
|
add_executable(Exercise2 MACOSX_BUNDLE
|
||||||
|
glsl.cpp
|
||||||
|
textures.cpp
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/Viewer.cpp include/Viewer.h
|
src/Viewer.cpp include/Viewer.h
|
||||||
src/Primitives.cpp include/Primitives.h
|
${GLSL_FILES})
|
||||||
src/SurfaceArea.cpp include/SurfaceArea.h
|
|
||||||
src/Volume.cpp include/Volume.h
|
|
||||||
src/ShellExtraction.cpp include/ShellExtraction.h
|
|
||||||
src/Smoothing.cpp include/Smoothing.h
|
|
||||||
src/Stripification.cpp include/Stripification.h
|
|
||||||
include/sample_set.h)
|
|
||||||
|
|
||||||
target_link_libraries(Exercise2 CG1Common ${LIBS})
|
target_link_libraries(Exercise2 CG1Common ${LIBS})
|
21
exercise2/glsl/sky.frag
Normal file
21
exercise2/glsl/sky.frag
Normal file
|
@ -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));
|
||||||
|
}
|
19
exercise2/glsl/sky.vert
Normal file
19
exercise2/glsl/sky.vert
Normal file
|
@ -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;
|
||||||
|
}
|
57
exercise2/glsl/terrain.frag
Normal file
57
exercise2/glsl/terrain.frag
Normal file
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
130
exercise2/glsl/terrain.vert
Normal file
130
exercise2/glsl/terrain.vert
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,31 +5,46 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gui/AbstractViewer.h>
|
#include <gui/AbstractViewer.h>
|
||||||
#include <util/OpenMeshUtils.h>
|
#include <gui/GLShader.h>
|
||||||
|
#include <gui/GLBuffer.h>
|
||||||
|
#include <gui/GLVertexArray.h>
|
||||||
|
|
||||||
class Viewer : public nse::gui::AbstractViewer
|
class Viewer : public nse::gui::AbstractViewer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Viewer();
|
Viewer();
|
||||||
|
|
||||||
|
void LoadShaders();
|
||||||
|
void CreateGeometry();
|
||||||
|
|
||||||
void drawContents();
|
void drawContents();
|
||||||
|
bool resizeEvent(const Eigen::Vector2i &);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupGUI();
|
void RenderSky();
|
||||||
void MeshUpdated(bool initNewMesh = false);
|
|
||||||
|
|
||||||
void ColorMeshFromIds();
|
Eigen::Matrix4f view, proj;
|
||||||
|
|
||||||
bool hasColors = false;
|
nse::gui::GLShader skyShader;
|
||||||
|
nse::gui::GLVertexArray emptyVAO;
|
||||||
|
|
||||||
nanogui::ComboBox* shadingBtn;
|
nse::gui::GLShader terrainShader;
|
||||||
unsigned int smoothingIterations;
|
nse::gui::GLVertexArray terrainVAO;
|
||||||
nanogui::Slider* sldSmoothingStrength;
|
nse::gui::GLBuffer terrainPositions;
|
||||||
unsigned int stripificationTrials;
|
nse::gui::GLBuffer terrainIndices;
|
||||||
|
|
||||||
HEMesh polymesh;
|
nse::gui::GLVertexArray referenceVAO;
|
||||||
MeshRenderer renderer;
|
nse::gui::GLBuffer referenceVB, referenceIB;
|
||||||
|
|
||||||
OpenMesh::FPropHandleT<int> faceIdProperty;
|
GLuint grassTexture, rockTexture, roadColorTexture, roadNormalMap, roadSpecularMap, alphaMap;
|
||||||
OpenMesh::FPropHandleT<Eigen::Vector4f> faceColorProperty;
|
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
|
|
BIN
exercise2/resources/alpha.jpg
Normal file
BIN
exercise2/resources/alpha.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
BIN
exercise2/resources/grass.jpg
Normal file
BIN
exercise2/resources/grass.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1,016 KiB |
BIN
exercise2/resources/roadColor.jpg
Normal file
BIN
exercise2/resources/roadColor.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 403 KiB |
BIN
exercise2/resources/roadNormals.jpg
Normal file
BIN
exercise2/resources/roadNormals.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 672 KiB |
BIN
exercise2/resources/roadSpecular.jpg
Normal file
BIN
exercise2/resources/roadSpecular.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 572 KiB |
BIN
exercise2/resources/rock.jpg
Normal file
BIN
exercise2/resources/rock.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
|
@ -5,251 +5,373 @@
|
||||||
#include "Viewer.h"
|
#include "Viewer.h"
|
||||||
|
|
||||||
#include <nanogui/window.h>
|
#include <nanogui/window.h>
|
||||||
#include <nanogui/button.h>
|
|
||||||
#include <nanogui/checkbox.h>
|
|
||||||
#include <nanogui/messagedialog.h>
|
|
||||||
#include <nanogui/popupbutton.h>
|
|
||||||
#include <nanogui/layout.h>
|
#include <nanogui/layout.h>
|
||||||
#include <nanogui/combobox.h>
|
#include <nanogui/checkbox.h>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <OpenMesh/Core/IO/MeshIO.hh>
|
|
||||||
|
|
||||||
#include <gui/SliderHelper.h>
|
#include <gui/SliderHelper.h>
|
||||||
|
|
||||||
#include "Primitives.h"
|
#include <iostream>
|
||||||
#include "SurfaceArea.h"
|
|
||||||
#include "Volume.h"
|
|
||||||
#include "ShellExtraction.h"
|
|
||||||
#include "Smoothing.h"
|
|
||||||
#include "Stripification.h"
|
|
||||||
|
|
||||||
const int segmentColorCount = 12;
|
#include <stb_image.h>
|
||||||
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 "glsl.h"
|
||||||
|
#include "textures.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
constexpr bool REFERENCE = true;
|
||||||
|
constexpr uint32_t PATCH_SIZE = 256; //number of vertices along one side of the terrain patch
|
||||||
|
|
||||||
Viewer::Viewer()
|
Viewer::Viewer()
|
||||||
: AbstractViewer("CG1 Exercise 2"),
|
: AbstractViewer("CG1 Exercise 2"),
|
||||||
renderer(polymesh)
|
terrainPositions(nse::gui::VertexBuffer), terrainIndices(nse::gui::IndexBuffer),
|
||||||
|
offsetBuffer(nse::gui::VertexBuffer),
|
||||||
|
referenceVB(nse::gui::VertexBuffer), referenceIB(nse::gui::IndexBuffer)
|
||||||
{
|
{
|
||||||
SetupGUI();
|
LoadShaders();
|
||||||
|
CreateGeometry();
|
||||||
|
|
||||||
polymesh.add_property(faceIdProperty);
|
grass_texture_location = terrainShader.uniform("grass");
|
||||||
polymesh.add_property(faceColorProperty);
|
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<float, 3>(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");
|
return false;
|
||||||
loadFileBtn->setCallback([this]() {
|
|
||||||
std::vector<std::pair<std::string, std::string>> fileTypes;
|
|
||||||
fileTypes.push_back(std::make_pair("obj", "OBJ File"));
|
|
||||||
auto file = nanogui::file_dialog(fileTypes, false);
|
|
||||||
if (!file.empty())
|
|
||||||
{
|
|
||||||
polymesh.clear();
|
|
||||||
if (!OpenMesh::IO::read_mesh(polymesh, file))
|
|
||||||
{
|
|
||||||
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Load Mesh",
|
|
||||||
"The specified file could not be loaded");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
MeshUpdated(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
auto primitiveBtn = new nanogui::PopupButton(mainWindow, "Create Primitive");
|
|
||||||
primitiveBtn->popup()->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
|
|
||||||
|
|
||||||
auto quadBtn = new nanogui::Button(primitiveBtn->popup(), "Quad");
|
|
||||||
quadBtn->setCallback([this]() { CreateQuad(polymesh); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto diskBtn = new nanogui::Button(primitiveBtn->popup(), "Disk");
|
|
||||||
diskBtn->setCallback([this]() { CreateDisk(polymesh, 1, 20); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto tetBtn = new nanogui::Button(primitiveBtn->popup(), "Tetrahedron");
|
|
||||||
tetBtn->setCallback([this]() { CreateTetrahedron(polymesh); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto octaBtn = new nanogui::Button(primitiveBtn->popup(), "Octahedron");
|
|
||||||
octaBtn->setCallback([this]() { CreateOctahedron(polymesh, 1); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto cubeBtn = new nanogui::Button(primitiveBtn->popup(), "Cube");
|
|
||||||
cubeBtn->setCallback([this]() { CreateCube(polymesh); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto icoBtn = new nanogui::Button(primitiveBtn->popup(), "Icosahedron");
|
|
||||||
icoBtn->setCallback([this]() { CreateIcosahedron(polymesh, 1); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto cylBtn = new nanogui::Button(primitiveBtn->popup(), "Cylinder");
|
|
||||||
cylBtn->setCallback([this]() { CreateCylinder(polymesh, 0.3f, 1, 20, 10); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto sphereBtn = new nanogui::Button(primitiveBtn->popup(), "Sphere");
|
|
||||||
sphereBtn->setCallback([this]() { CreateSphere(polymesh, 1, 20, 20); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto torusBtn = new nanogui::Button(primitiveBtn->popup(), "Torus");
|
|
||||||
torusBtn->setCallback([this]() { CreateTorus(polymesh, 0.4f, 1, 20, 20); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto arrowBtn = new nanogui::Button(primitiveBtn->popup(), "Arrow");
|
|
||||||
arrowBtn->setCallback([this]() { CreateUnitArrow(polymesh); MeshUpdated(true); });
|
|
||||||
|
|
||||||
auto calcAreaBtn = new nanogui::Button(mainWindow, "Calculate Mesh Area");
|
|
||||||
calcAreaBtn->setCallback([this]() {
|
|
||||||
auto area = ComputeSurfaceArea(polymesh);
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "The mesh has an area of " << area << ".";
|
|
||||||
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Information, "Surface Area",
|
|
||||||
ss.str());
|
|
||||||
});
|
|
||||||
|
|
||||||
auto calcVolBtn = new nanogui::Button(mainWindow, "Calculate Mesh Volume");
|
|
||||||
calcVolBtn->setCallback([this]() {
|
|
||||||
//Triangulate the mesh if it is not a triangle mesh
|
|
||||||
for (auto f : polymesh.faces())
|
|
||||||
{
|
|
||||||
if (polymesh.valence(f) > 3)
|
|
||||||
{
|
|
||||||
std::cout << "Triangulating mesh." << std::endl;
|
|
||||||
polymesh.triangulate();
|
|
||||||
MeshUpdated();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto vol = ComputeVolume(polymesh);
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "The mesh has a volume of " << vol << ".";
|
|
||||||
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Information, "Volume",
|
|
||||||
ss.str());
|
|
||||||
});
|
|
||||||
|
|
||||||
auto extractShellsBtn = new nanogui::Button(mainWindow, "Extract Shells");
|
|
||||||
extractShellsBtn->setCallback([this]() {
|
|
||||||
auto count = ExtractShells(polymesh, faceIdProperty);
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "The mesh has " << count << " shells.";
|
|
||||||
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Information, "Shell Extraction",
|
|
||||||
ss.str());
|
|
||||||
|
|
||||||
ColorMeshFromIds();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto noiseBtn = new nanogui::Button(mainWindow, "Add Noise");
|
|
||||||
noiseBtn->setCallback([this]() { AddNoise(polymesh); MeshUpdated(); });
|
|
||||||
|
|
||||||
nanogui::TextBox* txtSmoothingIterations;
|
|
||||||
auto sldSmoothingIterations = nse::gui::AddLabeledSlider(mainWindow, "Smoothing Iterations", std::make_pair(1, 100), 20, txtSmoothingIterations);
|
|
||||||
sldSmoothingIterations->setCallback([this, txtSmoothingIterations](float value)
|
|
||||||
{
|
|
||||||
smoothingIterations = (unsigned int)std::round(value);
|
|
||||||
txtSmoothingIterations->setValue(std::to_string(smoothingIterations));
|
|
||||||
});
|
|
||||||
sldSmoothingIterations->callback()(sldSmoothingIterations->value());
|
|
||||||
|
|
||||||
sldSmoothingStrength = nse::gui::AddLabeledSliderWithDefaultDisplay(mainWindow, "Smoothing Strength", std::make_pair(0.0f, 1.0f), 0.1f, 2);
|
|
||||||
|
|
||||||
|
|
||||||
auto smoothBtn = new nanogui::Button(mainWindow, "Laplacian Smoothing");
|
|
||||||
smoothBtn->setCallback([this]() {
|
|
||||||
SmoothUniformLaplacian(polymesh, sldSmoothingStrength->value(), smoothingIterations);
|
|
||||||
MeshUpdated();
|
|
||||||
});
|
|
||||||
|
|
||||||
nanogui::TextBox* txtStripificationTrials;
|
|
||||||
auto sldStripificationTrials = nse::gui::AddLabeledSlider(mainWindow, "Stripification Trials", std::make_pair(1, 50), 20, txtStripificationTrials);
|
|
||||||
sldStripificationTrials->setCallback([this, txtStripificationTrials](float value)
|
|
||||||
{
|
|
||||||
stripificationTrials = (unsigned int)std::round(value);
|
|
||||||
txtStripificationTrials->setValue(std::to_string(stripificationTrials));
|
|
||||||
});
|
|
||||||
sldStripificationTrials->callback()(sldStripificationTrials->value());
|
|
||||||
|
|
||||||
auto stripifyBtn = new nanogui::Button(mainWindow, "Extract Triangle Strips");
|
|
||||||
stripifyBtn->setCallback([this]() {
|
|
||||||
//Triangulate the mesh if it is not a triangle mesh
|
|
||||||
for (auto f : polymesh.faces())
|
|
||||||
{
|
|
||||||
if (polymesh.valence(f) > 3)
|
|
||||||
{
|
|
||||||
std::cout << "Triangulating mesh." << std::endl;
|
|
||||||
polymesh.triangulate();
|
|
||||||
MeshUpdated();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto count = ExtractTriStrips(polymesh, faceIdProperty, stripificationTrials);
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "The mesh has " << count << " triangle strips.";
|
|
||||||
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Information, "Shell Extraction",
|
|
||||||
ss.str());
|
|
||||||
|
|
||||||
ColorMeshFromIds();
|
|
||||||
});
|
|
||||||
|
|
||||||
shadingBtn = new nanogui::ComboBox(mainWindow, { "Smooth Shading", "Flat Shading" });
|
|
||||||
|
|
||||||
performLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::ColorMeshFromIds()
|
inline std::string loadShaderText(const char *path)
|
||||||
{
|
{
|
||||||
//Set face colors
|
std::ifstream is(path);
|
||||||
for (auto f : polymesh.faces())
|
std::string s_save;
|
||||||
|
|
||||||
|
if (is.is_open())
|
||||||
{
|
{
|
||||||
auto shell = polymesh.property(faceIdProperty, f);
|
s_save.assign(std::istreambuf_iterator<char>(is), std::istreambuf_iterator<char>());
|
||||||
if (shell < 0)
|
|
||||||
polymesh.property(faceColorProperty, f) = Eigen::Vector4f(0, 0, 0, 1);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto& color = segmentColors[shell % segmentColorCount];
|
|
||||||
polymesh.property(faceColorProperty, f) = Eigen::Vector4f(color[0], color[1], color[2], 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hasColors = true;
|
|
||||||
MeshUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::MeshUpdated(bool initNewMesh)
|
|
||||||
{
|
|
||||||
if (initNewMesh)
|
|
||||||
{
|
|
||||||
hasColors = false;
|
|
||||||
|
|
||||||
//calculate the bounding box of the mesh
|
|
||||||
nse::math::BoundingBox<float, 3> bbox;
|
|
||||||
for (auto v : polymesh.vertices())
|
|
||||||
bbox.expand(ToEigenVector(polymesh.point(v)));
|
|
||||||
camera().FocusOnBBox(bbox);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasColors)
|
|
||||||
renderer.UpdateWithPerFaceColor(faceColorProperty);
|
|
||||||
else
|
else
|
||||||
renderer.Update();
|
{
|
||||||
|
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<Eigen::Vector4f> positions;
|
||||||
|
std::vector<uint32_t> 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++)
|
||||||
|
{
|
||||||
|
positions.emplace_back((float)j, 0.0f, (float)i, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<Eigen::Vector4f> ref_pos;
|
||||||
|
std::vector<uint32_t> 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::RenderSky()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalculateViewFrustum(const Eigen::Matrix4f &mvp, Eigen::Vector4f *frustumPlanes, nse::math::BoundingBox<float, 3> &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()
|
void Viewer::drawContents()
|
||||||
{
|
{
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
|
|
||||||
Eigen::Matrix4f view, proj;
|
|
||||||
camera().ComputeCameraMatrices(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);
|
||||||
}
|
}
|
|
@ -3,14 +3,12 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/inc
|
||||||
add_executable(Exercise3 MACOSX_BUNDLE
|
add_executable(Exercise3 MACOSX_BUNDLE
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/Viewer.cpp include/Viewer.h
|
src/Viewer.cpp include/Viewer.h
|
||||||
src/AABBTree.cpp include/AABBTree.h
|
src/Primitives.cpp include/Primitives.h
|
||||||
src/Box.cpp include/Box.h
|
src/SurfaceArea.cpp include/SurfaceArea.h
|
||||||
src/LineSegment.cpp include/LineSegment.h
|
src/Volume.cpp include/Volume.h
|
||||||
src/Point.cpp include/Point.h
|
src/ShellExtraction.cpp include/ShellExtraction.h
|
||||||
src/Triangle.cpp include/Triangle.h
|
src/Smoothing.cpp include/Smoothing.h
|
||||||
include/GridUtils.h
|
src/Stripification.cpp include/Stripification.h
|
||||||
src/HashGrid.cpp include/HashGrid.h
|
include/sample_set.h)
|
||||||
src/GridTraverser.cpp include/GridTraverser.h
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(Exercise3 CG1Common ${LIBS})
|
target_link_libraries(Exercise3 CG1Common ${LIBS})
|
|
@ -5,17 +5,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gui/AbstractViewer.h>
|
#include <gui/AbstractViewer.h>
|
||||||
#include <gui/SliderHelper.h>
|
|
||||||
#include <util/OpenMeshUtils.h>
|
#include <util/OpenMeshUtils.h>
|
||||||
|
|
||||||
#include "AABBTree.h"
|
|
||||||
#include "HashGrid.h"
|
|
||||||
#include "Point.h"
|
|
||||||
#include "LineSegment.h"
|
|
||||||
#include "Triangle.h"
|
|
||||||
|
|
||||||
#include <gui/ShaderPool.h>
|
|
||||||
|
|
||||||
class Viewer : public nse::gui::AbstractViewer
|
class Viewer : public nse::gui::AbstractViewer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -24,67 +15,21 @@ public:
|
||||||
void drawContents();
|
void drawContents();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
enum PrimitiveType
|
|
||||||
{
|
|
||||||
Vertex, Edge, Tri
|
|
||||||
};
|
|
||||||
|
|
||||||
void SetupGUI();
|
void SetupGUI();
|
||||||
void MeshUpdated();
|
void MeshUpdated(bool initNewMesh = false);
|
||||||
|
|
||||||
void FindClosestPoint(const Eigen::Vector3f& p);
|
void ColorMeshFromIds();
|
||||||
void BuildGridVBO();
|
|
||||||
void BuildRayVBOs();
|
|
||||||
|
|
||||||
template <typename Grid>
|
bool hasColors = false;
|
||||||
void BuildGridVBO(const Grid& grid)
|
|
||||||
{
|
|
||||||
std::vector<Eigen::Vector4f> positions;
|
|
||||||
for (auto it = grid.NonEmptyCellsBegin(); it != grid.NonEmptyCellsEnd(); ++it)
|
|
||||||
{
|
|
||||||
auto box = grid.CellBounds(it->first);
|
|
||||||
AddBoxVertices(box, positions);
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderPool::Instance()->simpleShader.bind();
|
|
||||||
gridVAO.bind();
|
|
||||||
gridPositions.uploadData(positions).bindToAttribute("position");
|
|
||||||
gridVAO.unbind();
|
|
||||||
gridIndices = (GLuint)positions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddBoxVertices(const Box& box, std::vector<Eigen::Vector4f>& positions);
|
|
||||||
|
|
||||||
nanogui::ComboBox* shadingBtn;
|
nanogui::ComboBox* shadingBtn;
|
||||||
nanogui::CheckBox* chkRenderMesh;
|
unsigned int smoothingIterations;
|
||||||
nanogui::CheckBox* chkRenderGrid;
|
nanogui::Slider* sldSmoothingStrength;
|
||||||
nanogui::CheckBox* chkRenderRay;
|
unsigned int stripificationTrials;
|
||||||
int raySteps;
|
|
||||||
|
|
||||||
nse::gui::VectorInput* sldQuery, *sldRayOrigin, *sldRayDir;
|
|
||||||
nanogui::ComboBox* cmbPrimitiveType;
|
|
||||||
|
|
||||||
HEMesh polymesh;
|
HEMesh polymesh;
|
||||||
float bboxMaxLength;
|
|
||||||
MeshRenderer renderer;
|
MeshRenderer renderer;
|
||||||
|
|
||||||
AABBTree<Point> vertexTree;
|
OpenMesh::FPropHandleT<int> faceIdProperty;
|
||||||
AABBTree<LineSegment> edgeTree;
|
OpenMesh::FPropHandleT<Eigen::Vector4f> faceColorProperty;
|
||||||
AABBTree<Triangle> triangleTree;
|
|
||||||
|
|
||||||
HashGrid<Point> vertexGrid;
|
|
||||||
HashGrid<LineSegment> edgeGrid;
|
|
||||||
HashGrid<Triangle> triangleGrid;
|
|
||||||
|
|
||||||
nse::gui::GLBuffer closestPositions;
|
|
||||||
nse::gui::GLVertexArray closestVAO;
|
|
||||||
|
|
||||||
nse::gui::GLBuffer gridPositions;
|
|
||||||
nse::gui::GLVertexArray gridVAO;
|
|
||||||
GLuint gridIndices;
|
|
||||||
|
|
||||||
nse::gui::GLBuffer rayPositions, rayCellsPositions;
|
|
||||||
nse::gui::GLVertexArray rayVAO, rayCellsVAO;
|
|
||||||
GLuint rayCellsIndices;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,33 +8,49 @@
|
||||||
#include <nanogui/button.h>
|
#include <nanogui/button.h>
|
||||||
#include <nanogui/checkbox.h>
|
#include <nanogui/checkbox.h>
|
||||||
#include <nanogui/messagedialog.h>
|
#include <nanogui/messagedialog.h>
|
||||||
|
#include <nanogui/popupbutton.h>
|
||||||
#include <nanogui/layout.h>
|
#include <nanogui/layout.h>
|
||||||
#include <nanogui/combobox.h>
|
#include <nanogui/combobox.h>
|
||||||
|
|
||||||
#include <OpenMesh/Core/IO/MeshIO.hh>
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <OpenMesh/Core/IO/MeshIO.hh>
|
||||||
|
|
||||||
#include <gui/SliderHelper.h>
|
#include <gui/SliderHelper.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include "Primitives.h"
|
||||||
|
#include "SurfaceArea.h"
|
||||||
|
#include "Volume.h"
|
||||||
|
#include "ShellExtraction.h"
|
||||||
|
#include "Smoothing.h"
|
||||||
|
#include "Stripification.h"
|
||||||
|
|
||||||
#include <gui/ShaderPool.h>
|
const int segmentColorCount = 12;
|
||||||
#include "GridTraverser.h"
|
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()
|
Viewer::Viewer()
|
||||||
: AbstractViewer("CG1 Exercise 3"),
|
: AbstractViewer("CG1 Exercise 2"),
|
||||||
renderer(polymesh),
|
renderer(polymesh)
|
||||||
closestPositions(nse::gui::VertexBuffer),
|
|
||||||
gridPositions(nse::gui::VertexBuffer),
|
|
||||||
rayPositions(nse::gui::VertexBuffer), rayCellsPositions(nse::gui::VertexBuffer)
|
|
||||||
{
|
{
|
||||||
SetupGUI();
|
SetupGUI();
|
||||||
|
|
||||||
closestVAO.generate();
|
polymesh.add_property(faceIdProperty);
|
||||||
gridVAO.generate();
|
polymesh.add_property(faceColorProperty);
|
||||||
rayVAO.generate();
|
|
||||||
rayCellsVAO.generate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::SetupGUI()
|
void Viewer::SetupGUI()
|
||||||
|
@ -55,233 +71,185 @@ void Viewer::SetupGUI()
|
||||||
"The specified file could not be loaded");
|
"The specified file could not be loaded");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
MeshUpdated();
|
MeshUpdated(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cmbPrimitiveType = new nanogui::ComboBox(mainWindow, { "Use Vertices", "Use Edges", "Use Triangles" });
|
auto primitiveBtn = new nanogui::PopupButton(mainWindow, "Create Primitive");
|
||||||
cmbPrimitiveType->setCallback([this](int) { FindClosestPoint(sldQuery->Value()); BuildGridVBO(); });
|
primitiveBtn->popup()->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
|
||||||
|
|
||||||
sldQuery = new nse::gui::VectorInput(mainWindow, "Query", Eigen::Vector3f::Zero(), Eigen::Vector3f::Zero(), Eigen::Vector3f::Zero(), [this](const Eigen::Vector3f& p) { FindClosestPoint(p); });
|
auto quadBtn = new nanogui::Button(primitiveBtn->popup(), "Quad");
|
||||||
|
quadBtn->setCallback([this]() { CreateQuad(polymesh); MeshUpdated(true); });
|
||||||
|
|
||||||
sldRayOrigin = new nse::gui::VectorInput(mainWindow, "Ray Origin", Eigen::Vector3f::Zero(), Eigen::Vector3f::Zero(), Eigen::Vector3f::Zero(), [this](const Eigen::Vector3f& p) { BuildRayVBOs(); });
|
auto diskBtn = new nanogui::Button(primitiveBtn->popup(), "Disk");
|
||||||
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(); });
|
diskBtn->setCallback([this]() { CreateDisk(polymesh, 1, 20); MeshUpdated(true); });
|
||||||
nanogui::TextBox* txtRaySteps;
|
|
||||||
auto sldRaySteps = nse::gui::AddLabeledSlider(mainWindow, "Ray Steps", std::make_pair(1, 200), 80, txtRaySteps);
|
auto tetBtn = new nanogui::Button(primitiveBtn->popup(), "Tetrahedron");
|
||||||
sldRaySteps->setCallback([this, txtRaySteps](float value) {
|
tetBtn->setCallback([this]() { CreateTetrahedron(polymesh); MeshUpdated(true); });
|
||||||
raySteps = (int)std::round(value);
|
|
||||||
txtRaySteps->setValue(std::to_string(raySteps));
|
auto octaBtn = new nanogui::Button(primitiveBtn->popup(), "Octahedron");
|
||||||
BuildRayVBOs();
|
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());
|
||||||
});
|
});
|
||||||
sldRaySteps->callback()(sldRaySteps->value());
|
|
||||||
|
|
||||||
chkRenderMesh = new nanogui::CheckBox(mainWindow, "Render Mesh"); chkRenderMesh->setChecked(true);
|
auto calcVolBtn = new nanogui::Button(mainWindow, "Calculate Mesh Volume");
|
||||||
chkRenderGrid = new nanogui::CheckBox(mainWindow, "Render Non-Empty Grid Cells"); chkRenderGrid->setChecked(false);
|
calcVolBtn->setCallback([this]() {
|
||||||
chkRenderRay = new nanogui::CheckBox(mainWindow, "Render Ray"); chkRenderRay->setChecked(false);
|
//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" });
|
shadingBtn = new nanogui::ComboBox(mainWindow, { "Smooth Shading", "Flat Shading" });
|
||||||
|
|
||||||
performLayout();
|
performLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::FindClosestPoint(const Eigen::Vector3f& p)
|
void Viewer::ColorMeshFromIds()
|
||||||
{
|
{
|
||||||
if (polymesh.vertices_empty())
|
//Set face colors
|
||||||
return;
|
for (auto f : polymesh.faces())
|
||||||
Eigen::Vector3f closest;
|
|
||||||
auto timeStart = std::chrono::high_resolution_clock::now();
|
|
||||||
switch (cmbPrimitiveType->selectedIndex())
|
|
||||||
{
|
{
|
||||||
case Vertex:
|
auto shell = polymesh.property(faceIdProperty, f);
|
||||||
closest = vertexTree.ClosestPoint(p);
|
if (shell < 0)
|
||||||
break;
|
polymesh.property(faceColorProperty, f) = Eigen::Vector4f(0, 0, 0, 1);
|
||||||
case Edge:
|
else
|
||||||
closest = edgeTree.ClosestPoint(p);
|
{
|
||||||
break;
|
auto& color = segmentColors[shell % segmentColorCount];
|
||||||
case Tri:
|
polymesh.property(faceColorProperty, f) = Eigen::Vector4f(color[0], color[1], color[2], 1);
|
||||||
closest = triangleTree.ClosestPoint(p);
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
auto timeEnd = std::chrono::high_resolution_clock::now();
|
hasColors = true;
|
||||||
std::cout << std::fixed << "Closest point query took " << std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeStart).count() << " microseconds." << std::endl;
|
MeshUpdated();
|
||||||
|
|
||||||
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()
|
void Viewer::MeshUpdated(bool initNewMesh)
|
||||||
{
|
{
|
||||||
//calculate the bounding Box of the mesh
|
if (initNewMesh)
|
||||||
nse::math::BoundingBox<float, 3> bbox;
|
|
||||||
for (auto v : polymesh.vertices())
|
|
||||||
bbox.expand(ToEigenVector(polymesh.point(v)));
|
|
||||||
camera().FocusOnBBox(bbox);
|
|
||||||
bboxMaxLength = bbox.diagonal().maxCoeff();
|
|
||||||
|
|
||||||
polymesh.triangulate();
|
|
||||||
|
|
||||||
BuildAABBTreeFromVertices(polymesh, vertexTree);
|
|
||||||
BuildAABBTreeFromEdges(polymesh, edgeTree);
|
|
||||||
BuildAABBTreeFromTriangles(polymesh, triangleTree);
|
|
||||||
|
|
||||||
Eigen::Vector3f cellSize = Eigen::Vector3f::Constant(bbox.diagonal().maxCoeff() / 50);
|
|
||||||
BuildHashGridFromVertices(polymesh, vertexGrid, cellSize);
|
|
||||||
BuildHashGridFromEdges(polymesh, edgeGrid, cellSize);
|
|
||||||
BuildHashGridFromTriangles(polymesh, triangleGrid, cellSize);
|
|
||||||
|
|
||||||
sldQuery->SetBounds(bbox.min, bbox.max);
|
|
||||||
sldQuery->SetValue(bbox.max);
|
|
||||||
FindClosestPoint(bbox.max);
|
|
||||||
|
|
||||||
sldRayOrigin->SetBounds(bbox.min, bbox.max);
|
|
||||||
sldRayOrigin->SetValue(bbox.min);
|
|
||||||
sldRayDir->SetValue(bbox.diagonal().normalized());
|
|
||||||
|
|
||||||
BuildGridVBO();
|
|
||||||
|
|
||||||
renderer.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::BuildGridVBO()
|
|
||||||
{
|
|
||||||
switch (cmbPrimitiveType->selectedIndex())
|
|
||||||
{
|
{
|
||||||
case Vertex:
|
hasColors = false;
|
||||||
BuildGridVBO(vertexGrid);
|
|
||||||
break;
|
|
||||||
case Edge:
|
|
||||||
BuildGridVBO(edgeGrid);
|
|
||||||
break;
|
|
||||||
case Tri:
|
|
||||||
BuildGridVBO(triangleGrid);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::BuildRayVBOs()
|
//calculate the bounding box of the mesh
|
||||||
{
|
nse::math::BoundingBox<float, 3> bbox;
|
||||||
if (polymesh.vertices_empty())
|
for (auto v : polymesh.vertices())
|
||||||
return;
|
bbox.expand(ToEigenVector(polymesh.point(v)));
|
||||||
|
camera().FocusOnBBox(bbox);
|
||||||
//Ray line indicator
|
|
||||||
Eigen::Matrix4Xf rayPoints(4, 2);
|
|
||||||
rayPoints.block<3, 1>(0, 0) = sldRayOrigin->Value();
|
|
||||||
rayPoints.block<3, 1>(0, 1) = sldRayOrigin->Value() + sldRayDir->Value().normalized() * 0.2f * bboxMaxLength;
|
|
||||||
rayPoints.row(3).setConstant(1);
|
|
||||||
|
|
||||||
rayVAO.bind();
|
|
||||||
ShaderPool::Instance()->simpleShader.bind();
|
|
||||||
rayPositions.uploadData(rayPoints).bindToAttribute("position");
|
|
||||||
rayVAO.unbind();
|
|
||||||
|
|
||||||
//Ray cells
|
|
||||||
std::vector<Eigen::Vector4f> cellPositions;
|
|
||||||
GridTraverser trav(sldRayOrigin->Value(), sldRayDir->Value(), vertexGrid.CellExtents());
|
|
||||||
for (int i = 0; i < raySteps; ++i, trav++)
|
|
||||||
{
|
|
||||||
auto bounds = vertexGrid.CellBounds(*trav);
|
|
||||||
AddBoxVertices(bounds, cellPositions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rayCellsVAO.bind();
|
if (hasColors)
|
||||||
rayCellsPositions.uploadData(cellPositions).bindToAttribute("position");
|
renderer.UpdateWithPerFaceColor(faceColorProperty);
|
||||||
rayCellsVAO.unbind();
|
else
|
||||||
rayCellsIndices = (GLuint)cellPositions.size();
|
renderer.Update();
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::AddBoxVertices(const Box & box, std::vector<Eigen::Vector4f>& positions)
|
|
||||||
{
|
|
||||||
auto& lb = box.LowerBound();
|
|
||||||
auto& ub = box.UpperBound();
|
|
||||||
Eigen::Vector4f o; o << lb, 1.0f;
|
|
||||||
Eigen::Vector4f x; x << ub.x() - lb.x(), 0, 0, 0;
|
|
||||||
Eigen::Vector4f y; y << 0, ub.y() - lb.y(), 0, 0;
|
|
||||||
Eigen::Vector4f z; z << 0, 0, ub.z() - lb.z(), 0;
|
|
||||||
positions.push_back(o);
|
|
||||||
positions.push_back(o + x);
|
|
||||||
positions.push_back(o + x);
|
|
||||||
positions.push_back(o + x + y);
|
|
||||||
positions.push_back(o + x + y);
|
|
||||||
positions.push_back(o + y);
|
|
||||||
positions.push_back(o + y);
|
|
||||||
positions.push_back(o);
|
|
||||||
|
|
||||||
positions.push_back(o + z);
|
|
||||||
positions.push_back(o + z + x);
|
|
||||||
positions.push_back(o + z + x);
|
|
||||||
positions.push_back(o + z + x + y);
|
|
||||||
positions.push_back(o + z + x + y);
|
|
||||||
positions.push_back(o + z + y);
|
|
||||||
positions.push_back(o + z + y);
|
|
||||||
positions.push_back(o + z);
|
|
||||||
|
|
||||||
positions.push_back(o);
|
|
||||||
positions.push_back(o + z);
|
|
||||||
positions.push_back(o + x);
|
|
||||||
positions.push_back(o + x + z);
|
|
||||||
positions.push_back(o + y);
|
|
||||||
positions.push_back(o + y + z);
|
|
||||||
positions.push_back(o + x + y);
|
|
||||||
positions.push_back(o + x + y + z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::drawContents()
|
void Viewer::drawContents()
|
||||||
{
|
{
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
if (!polymesh.vertices_empty())
|
Eigen::Matrix4f view, proj;
|
||||||
{
|
camera().ComputeCameraMatrices(view, proj);
|
||||||
Eigen::Matrix4f view, proj;
|
|
||||||
camera().ComputeCameraMatrices(view, proj);
|
|
||||||
Eigen::Matrix4f mvp = proj * view;
|
|
||||||
|
|
||||||
if(chkRenderMesh->checked())
|
renderer.Render(view, proj, shadingBtn->selectedIndex() == 1);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
std::cout.imbue(std::locale(""));
|
|
||||||
|
|
||||||
nanogui::init();
|
nanogui::init();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,8 +3,14 @@ include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/inc
|
||||||
add_executable(Exercise4 MACOSX_BUNDLE
|
add_executable(Exercise4 MACOSX_BUNDLE
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/Viewer.cpp include/Viewer.h
|
src/Viewer.cpp include/Viewer.h
|
||||||
src/Parametrization.cpp include/Parametrization.h
|
src/AABBTree.cpp include/AABBTree.h
|
||||||
src/Registration.cpp include/Registration.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})
|
target_link_libraries(Exercise4 CG1Common ${LIBS})
|
|
@ -8,8 +8,13 @@
|
||||||
#include <gui/SliderHelper.h>
|
#include <gui/SliderHelper.h>
|
||||||
#include <util/OpenMeshUtils.h>
|
#include <util/OpenMeshUtils.h>
|
||||||
|
|
||||||
#include "Registration.h"
|
#include "AABBTree.h"
|
||||||
#include <random>
|
#include "HashGrid.h"
|
||||||
|
#include "Point.h"
|
||||||
|
#include "LineSegment.h"
|
||||||
|
#include "Triangle.h"
|
||||||
|
|
||||||
|
#include <gui/ShaderPool.h>
|
||||||
|
|
||||||
class Viewer : public nse::gui::AbstractViewer
|
class Viewer : public nse::gui::AbstractViewer
|
||||||
{
|
{
|
||||||
|
@ -18,32 +23,68 @@ public:
|
||||||
|
|
||||||
void drawContents();
|
void drawContents();
|
||||||
|
|
||||||
virtual bool resizeEvent(const Eigen::Vector2i&);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
enum PrimitiveType
|
||||||
|
{
|
||||||
|
Vertex, Edge, Tri
|
||||||
|
};
|
||||||
|
|
||||||
void SetupGUI();
|
void SetupGUI();
|
||||||
void MeshUpdated();
|
void MeshUpdated();
|
||||||
void BuildCorrVBOs();
|
|
||||||
|
|
||||||
nse::math::BoundingBox<float, 3> meshBbox, expandedBbox;
|
void FindClosestPoint(const Eigen::Vector3f& p);
|
||||||
|
void BuildGridVBO();
|
||||||
|
void BuildRayVBOs();
|
||||||
|
|
||||||
|
template <typename Grid>
|
||||||
|
void BuildGridVBO(const Grid& grid)
|
||||||
|
{
|
||||||
|
std::vector<Eigen::Vector4f> positions;
|
||||||
|
for (auto it = grid.NonEmptyCellsBegin(); it != grid.NonEmptyCellsEnd(); ++it)
|
||||||
|
{
|
||||||
|
auto box = grid.CellBounds(it->first);
|
||||||
|
AddBoxVertices(box, positions);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderPool::Instance()->simpleShader.bind();
|
||||||
|
gridVAO.bind();
|
||||||
|
gridPositions.uploadData(positions).bindToAttribute("position");
|
||||||
|
gridVAO.unbind();
|
||||||
|
gridIndices = (GLuint)positions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddBoxVertices(const Box& box, std::vector<Eigen::Vector4f>& positions);
|
||||||
|
|
||||||
nanogui::ComboBox* shadingBtn;
|
nanogui::ComboBox* shadingBtn;
|
||||||
nanogui::CheckBox* chkRenderTextureMap;
|
nanogui::CheckBox* chkRenderMesh;
|
||||||
nanogui::CheckBox* chkRenderSecondMesh;
|
nanogui::CheckBox* chkRenderGrid;
|
||||||
|
nanogui::CheckBox* chkRenderRay;
|
||||||
|
int raySteps;
|
||||||
|
|
||||||
|
nse::gui::VectorInput* sldQuery, *sldRayOrigin, *sldRayDir;
|
||||||
|
nanogui::ComboBox* cmbPrimitiveType;
|
||||||
|
|
||||||
HEMesh polymesh;
|
HEMesh polymesh;
|
||||||
|
float bboxMaxLength;
|
||||||
MeshRenderer renderer;
|
MeshRenderer renderer;
|
||||||
|
|
||||||
bool hasParametrization = false;
|
AABBTree<Point> vertexTree;
|
||||||
Eigen::Matrix4f texMapProjectionMatrix;
|
AABBTree<LineSegment> edgeTree;
|
||||||
|
AABBTree<Triangle> triangleTree;
|
||||||
|
|
||||||
Eigen::Affine3f secondMeshTransform;
|
HashGrid<Point> vertexGrid;
|
||||||
|
HashGrid<LineSegment> edgeGrid;
|
||||||
|
HashGrid<Triangle> triangleGrid;
|
||||||
|
|
||||||
std::mt19937 rnd;
|
nse::gui::GLBuffer closestPositions;
|
||||||
std::vector<correspondence> correspondences;
|
nse::gui::GLVertexArray closestVAO;
|
||||||
|
|
||||||
size_t corrCount;
|
nse::gui::GLBuffer gridPositions;
|
||||||
nse::gui::GLBuffer corrPositions;
|
nse::gui::GLVertexArray gridVAO;
|
||||||
nse::gui::GLVertexArray corrVAO;
|
GLuint gridIndices;
|
||||||
|
|
||||||
|
nse::gui::GLBuffer rayPositions, rayCellsPositions;
|
||||||
|
nse::gui::GLVertexArray rayVAO, rayCellsVAO;
|
||||||
|
GLuint rayCellsIndices;
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,23 +13,28 @@
|
||||||
|
|
||||||
#include <OpenMesh/Core/IO/MeshIO.hh>
|
#include <OpenMesh/Core/IO/MeshIO.hh>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <gui/SliderHelper.h>
|
#include <gui/SliderHelper.h>
|
||||||
|
|
||||||
#include "Parametrization.h"
|
#include <chrono>
|
||||||
#include "Registration.h"
|
|
||||||
|
|
||||||
#include <gui/ShaderPool.h>
|
#include <gui/ShaderPool.h>
|
||||||
|
#include "GridTraverser.h"
|
||||||
|
|
||||||
Viewer::Viewer()
|
Viewer::Viewer()
|
||||||
: AbstractViewer("CG1 Exercise 4"),
|
: AbstractViewer("CG1 Exercise 3"),
|
||||||
renderer(polymesh),
|
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();
|
SetupGUI();
|
||||||
|
|
||||||
|
closestVAO.generate();
|
||||||
|
gridVAO.generate();
|
||||||
|
rayVAO.generate();
|
||||||
|
rayCellsVAO.generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::SetupGUI()
|
void Viewer::SetupGUI()
|
||||||
|
@ -54,182 +59,177 @@ void Viewer::SetupGUI()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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" });
|
shadingBtn = new nanogui::ComboBox(mainWindow, { "Smooth Shading", "Flat Shading" });
|
||||||
|
|
||||||
performLayout();
|
performLayout();
|
||||||
|
}
|
||||||
|
|
||||||
auto paramWindow = new nanogui::Window(this, "Parametrization");
|
void Viewer::FindClosestPoint(const Eigen::Vector3f& p)
|
||||||
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));
|
if (polymesh.vertices_empty())
|
||||||
|
return;
|
||||||
|
Eigen::Vector3f closest;
|
||||||
|
auto timeStart = std::chrono::high_resolution_clock::now();
|
||||||
|
switch (cmbPrimitiveType->selectedIndex())
|
||||||
|
{
|
||||||
|
case Vertex:
|
||||||
|
closest = vertexTree.ClosestPoint(p);
|
||||||
|
break;
|
||||||
|
case Edge:
|
||||||
|
closest = edgeTree.ClosestPoint(p);
|
||||||
|
break;
|
||||||
|
case Tri:
|
||||||
|
closest = triangleTree.ClosestPoint(p);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto timeEnd = std::chrono::high_resolution_clock::now();
|
||||||
|
std::cout << std::fixed << "Closest point query took " << std::chrono::duration_cast<std::chrono::microseconds>(timeEnd - timeStart).count() << " microseconds." << std::endl;
|
||||||
|
|
||||||
auto cmbWeightType = new nanogui::ComboBox(paramWindow, { "Constant Weight", "Edge Length Weight", "Inverse Edge Length Weight", "Cotan Weight" });
|
Eigen::Matrix4Xf points(4, 2);
|
||||||
|
points.block<3, 1>(0, 0) = p;
|
||||||
|
points.block<3, 1>(0, 1) = closest;
|
||||||
|
points.row(3).setConstant(1);
|
||||||
|
|
||||||
auto parametrizeBtn = new nanogui::Button(paramWindow, "Calculate Parametrization");
|
closestVAO.bind();
|
||||||
parametrizeBtn->setCallback([this, cmbWeightType]() {
|
ShaderPool::Instance()->simpleShader.bind();
|
||||||
if (polymesh.n_vertices() == 0)
|
closestPositions.uploadData(points).bindToAttribute("position");
|
||||||
{
|
closestVAO.unbind();
|
||||||
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Parametrization", "Please load a mesh for parametrization.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bool success;
|
|
||||||
switch (cmbWeightType->selectedIndex())
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
success = ComputeParametrizationOfTopologicalDisk<CONSTANT_WEIGHT>(polymesh);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
success = ComputeParametrizationOfTopologicalDisk<EDGE_LENGTH_WEIGHT>(polymesh);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
success = ComputeParametrizationOfTopologicalDisk<INV_EDGE_LENGTH_WEIGHT>(polymesh);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
success = ComputeParametrizationOfTopologicalDisk<COTAN_WEIGHT>(polymesh);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
renderer.Update();
|
|
||||||
hasParametrization = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Parametrization", "Parametrization failed.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
chkRenderTextureMap = new nanogui::CheckBox(paramWindow, "Render Texture Map");
|
|
||||||
|
|
||||||
performLayout();
|
|
||||||
|
|
||||||
auto registrationWindow = new nanogui::Window(this, "Registration");
|
|
||||||
registrationWindow->setPosition(Eigen::Vector2i(paramWindow->position().x(), paramWindow->position().y() + paramWindow->size().y() + 15));
|
|
||||||
registrationWindow->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
|
|
||||||
|
|
||||||
auto rotateBtn = new nanogui::Button(registrationWindow, "Random Rotation");
|
|
||||||
rotateBtn->setCallback([this]() {
|
|
||||||
secondMeshTransform = Eigen::Affine3f(Eigen::Translation3f(3 * meshBbox.diagonal().x(), 0, 0));
|
|
||||||
std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
|
|
||||||
Eigen::Quaternionf q(dist(rnd), dist(rnd), dist(rnd), dist(rnd));
|
|
||||||
q.normalize();
|
|
||||||
secondMeshTransform *= q;
|
|
||||||
|
|
||||||
correspondences.clear();
|
|
||||||
BuildCorrVBOs();
|
|
||||||
});
|
|
||||||
|
|
||||||
corrCount = 10;
|
|
||||||
nanogui::TextBox* txtCorrCount;
|
|
||||||
auto sldCorrCount = nse::gui::AddLabeledSlider(registrationWindow, "Correspondence Count", std::make_pair(1.0f, 50.0f), (float)corrCount, txtCorrCount);
|
|
||||||
sldCorrCount->setCallback([this, txtCorrCount](float value) {
|
|
||||||
corrCount = (int)std::round(value);
|
|
||||||
txtCorrCount->setValue(std::to_string(corrCount));
|
|
||||||
});
|
|
||||||
sldCorrCount->callback()(sldCorrCount->value());
|
|
||||||
|
|
||||||
auto autoCorrs = new nanogui::Button(registrationWindow, "Automatic Correspondences");
|
|
||||||
autoCorrs->setCallback([this]() {
|
|
||||||
if (corrCount > polymesh.n_vertices())
|
|
||||||
return;
|
|
||||||
std::uniform_int_distribution<int> dist(0, (int)polymesh.n_vertices());
|
|
||||||
|
|
||||||
correspondences.clear();
|
|
||||||
|
|
||||||
for (int i = 0; i < corrCount; ++i)
|
|
||||||
{
|
|
||||||
auto v = polymesh.vertex_handle(dist(rnd));
|
|
||||||
auto p = ToEigenVector(polymesh.point(v));
|
|
||||||
correspondences.push_back(std::make_pair(p, secondMeshTransform * p));
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildCorrVBOs();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto distortCorrespondencesBtn = new nanogui::Button(registrationWindow, "Distort Correspondences");
|
|
||||||
distortCorrespondencesBtn->setCallback([this]() {
|
|
||||||
std::normal_distribution<float> dist(0.0f, meshBbox.diagonal().norm() * 0.01f);
|
|
||||||
for (auto& c : correspondences)
|
|
||||||
c.first += Eigen::Vector3f(dist(rnd), dist(rnd), dist(rnd));
|
|
||||||
BuildCorrVBOs();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto registerBtn = new nanogui::Button(registrationWindow, "Register");
|
|
||||||
registerBtn->setCallback([this]() {
|
|
||||||
auto T = CalculateRigidRegistration(correspondences);
|
|
||||||
secondMeshTransform = T * secondMeshTransform;
|
|
||||||
|
|
||||||
for (auto& c : correspondences)
|
|
||||||
c.second = T * c.second;
|
|
||||||
BuildCorrVBOs();
|
|
||||||
});
|
|
||||||
|
|
||||||
chkRenderSecondMesh = new nanogui::CheckBox(registrationWindow, "Render Second Mesh", [this](bool checked) {
|
|
||||||
if (checked)
|
|
||||||
camera().FocusOnBBox(expandedBbox);
|
|
||||||
else
|
|
||||||
camera().FocusOnBBox(meshBbox);
|
|
||||||
});
|
|
||||||
|
|
||||||
performLayout();
|
|
||||||
|
|
||||||
auto maxWidth = std::max(mainWindow->size().x(), std::max(paramWindow->size().x(), registrationWindow->size().x()));
|
|
||||||
mainWindow->setFixedWidth(maxWidth);
|
|
||||||
paramWindow->setFixedWidth(maxWidth);
|
|
||||||
registrationWindow->setFixedWidth(maxWidth);
|
|
||||||
|
|
||||||
performLayout();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::MeshUpdated()
|
void Viewer::MeshUpdated()
|
||||||
{
|
{
|
||||||
//calculate the bounding Box of the mesh
|
//calculate the bounding Box of the mesh
|
||||||
meshBbox.reset();
|
nse::math::BoundingBox<float, 3> bbox;
|
||||||
for (auto v : polymesh.vertices())
|
for (auto v : polymesh.vertices())
|
||||||
meshBbox.expand(ToEigenVector(polymesh.point(v)));
|
bbox.expand(ToEigenVector(polymesh.point(v)));
|
||||||
|
camera().FocusOnBBox(bbox);
|
||||||
expandedBbox = meshBbox;
|
bboxMaxLength = bbox.diagonal().maxCoeff();
|
||||||
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();
|
polymesh.triangulate();
|
||||||
correspondences.clear();
|
|
||||||
|
|
||||||
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();
|
renderer.Update();
|
||||||
BuildCorrVBOs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::BuildCorrVBOs()
|
void Viewer::BuildGridVBO()
|
||||||
{
|
{
|
||||||
std::vector<Eigen::Vector4f> pos;
|
switch (cmbPrimitiveType->selectedIndex())
|
||||||
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));
|
case Vertex:
|
||||||
pos.push_back(Eigen::Vector4f(c.second.x(), c.second.y(), c.second.z(), 1.0f));
|
BuildGridVBO(vertexGrid);
|
||||||
|
break;
|
||||||
|
case Edge:
|
||||||
|
BuildGridVBO(edgeGrid);
|
||||||
|
break;
|
||||||
|
case Tri:
|
||||||
|
BuildGridVBO(triangleGrid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::BuildRayVBOs()
|
||||||
|
{
|
||||||
|
if (polymesh.vertices_empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Ray line indicator
|
||||||
|
Eigen::Matrix4Xf rayPoints(4, 2);
|
||||||
|
rayPoints.block<3, 1>(0, 0) = sldRayOrigin->Value();
|
||||||
|
rayPoints.block<3, 1>(0, 1) = sldRayOrigin->Value() + sldRayDir->Value().normalized() * 0.2f * bboxMaxLength;
|
||||||
|
rayPoints.row(3).setConstant(1);
|
||||||
|
|
||||||
|
rayVAO.bind();
|
||||||
|
ShaderPool::Instance()->simpleShader.bind();
|
||||||
|
rayPositions.uploadData(rayPoints).bindToAttribute("position");
|
||||||
|
rayVAO.unbind();
|
||||||
|
|
||||||
|
//Ray cells
|
||||||
|
std::vector<Eigen::Vector4f> cellPositions;
|
||||||
|
GridTraverser trav(sldRayOrigin->Value(), sldRayDir->Value(), vertexGrid.CellExtents());
|
||||||
|
for (int i = 0; i < raySteps; ++i, trav++)
|
||||||
|
{
|
||||||
|
auto bounds = vertexGrid.CellBounds(*trav);
|
||||||
|
AddBoxVertices(bounds, cellPositions);
|
||||||
}
|
}
|
||||||
|
|
||||||
corrVAO.bind();
|
rayCellsVAO.bind();
|
||||||
ShaderPool::Instance()->simpleShader.bind();
|
rayCellsPositions.uploadData(cellPositions).bindToAttribute("position");
|
||||||
corrPositions.uploadData(pos).bindToAttribute("position");
|
rayCellsVAO.unbind();
|
||||||
corrVAO.unbind();
|
rayCellsIndices = (GLuint)cellPositions.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Viewer::resizeEvent(const Eigen::Vector2i& size)
|
void Viewer::AddBoxVertices(const Box & box, std::vector<Eigen::Vector4f>& 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();
|
positions.push_back(o + z);
|
||||||
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);
|
positions.push_back(o + z + x);
|
||||||
texMapProjectionMatrix = proj.matrix();
|
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);
|
||||||
|
|
||||||
return true;
|
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()
|
void Viewer::drawContents()
|
||||||
|
@ -242,26 +242,46 @@ void Viewer::drawContents()
|
||||||
camera().ComputeCameraMatrices(view, proj);
|
camera().ComputeCameraMatrices(view, proj);
|
||||||
Eigen::Matrix4f mvp = proj * view;
|
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));
|
gridVAO.bind();
|
||||||
if (correspondences.size() > 0)
|
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(0.2f, 0.2f, 0.2f, 1));
|
||||||
{
|
glDrawArrays(GL_LINES, 0, gridIndices);
|
||||||
corrVAO.bind();
|
gridVAO.unbind();
|
||||||
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())
|
if (chkRenderRay->checked())
|
||||||
{
|
{
|
||||||
glDisable(GL_DEPTH_TEST);
|
//Draw line for ray
|
||||||
renderer.RenderTextureMap(texMapProjectionMatrix, Eigen::Vector4f(1, 1, 1, 1));
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
10
exercise5/CMakeLists.txt
Normal file
10
exercise5/CMakeLists.txt
Normal file
|
@ -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})
|
49
exercise5/include/Viewer.h
Normal file
49
exercise5/include/Viewer.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
// This source code is property of the Computer Graphics and Visualization
|
||||||
|
// chair of the TU Dresden. Do not distribute!
|
||||||
|
// Copyright (C) CGV TU Dresden - All Rights Reserved
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gui/AbstractViewer.h>
|
||||||
|
#include <gui/SliderHelper.h>
|
||||||
|
#include <util/OpenMeshUtils.h>
|
||||||
|
|
||||||
|
#include "Registration.h"
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
class Viewer : public nse::gui::AbstractViewer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Viewer();
|
||||||
|
|
||||||
|
void drawContents();
|
||||||
|
|
||||||
|
virtual bool resizeEvent(const Eigen::Vector2i&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void SetupGUI();
|
||||||
|
void MeshUpdated();
|
||||||
|
void BuildCorrVBOs();
|
||||||
|
|
||||||
|
nse::math::BoundingBox<float, 3> meshBbox, expandedBbox;
|
||||||
|
|
||||||
|
nanogui::ComboBox* shadingBtn;
|
||||||
|
nanogui::CheckBox* chkRenderTextureMap;
|
||||||
|
nanogui::CheckBox* chkRenderSecondMesh;
|
||||||
|
|
||||||
|
HEMesh polymesh;
|
||||||
|
MeshRenderer renderer;
|
||||||
|
|
||||||
|
bool hasParametrization = false;
|
||||||
|
Eigen::Matrix4f texMapProjectionMatrix;
|
||||||
|
|
||||||
|
Eigen::Affine3f secondMeshTransform;
|
||||||
|
|
||||||
|
std::mt19937 rnd;
|
||||||
|
std::vector<correspondence> correspondences;
|
||||||
|
|
||||||
|
size_t corrCount;
|
||||||
|
nse::gui::GLBuffer corrPositions;
|
||||||
|
nse::gui::GLVertexArray corrVAO;
|
||||||
|
};
|
267
exercise5/src/Viewer.cpp
Normal file
267
exercise5/src/Viewer.cpp
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
// This source code is property of the Computer Graphics and Visualization
|
||||||
|
// chair of the TU Dresden. Do not distribute!
|
||||||
|
// Copyright (C) CGV TU Dresden - All Rights Reserved
|
||||||
|
|
||||||
|
#include "Viewer.h"
|
||||||
|
|
||||||
|
#include <nanogui/window.h>
|
||||||
|
#include <nanogui/button.h>
|
||||||
|
#include <nanogui/checkbox.h>
|
||||||
|
#include <nanogui/messagedialog.h>
|
||||||
|
#include <nanogui/layout.h>
|
||||||
|
#include <nanogui/combobox.h>
|
||||||
|
|
||||||
|
#include <OpenMesh/Core/IO/MeshIO.hh>
|
||||||
|
|
||||||
|
#include <gui/SliderHelper.h>
|
||||||
|
|
||||||
|
#include "Parametrization.h"
|
||||||
|
#include "Registration.h"
|
||||||
|
|
||||||
|
#include <gui/ShaderPool.h>
|
||||||
|
|
||||||
|
Viewer::Viewer()
|
||||||
|
: AbstractViewer("CG1 Exercise 4"),
|
||||||
|
renderer(polymesh),
|
||||||
|
corrPositions(nse::gui::VertexBuffer)
|
||||||
|
{
|
||||||
|
polymesh.request_vertex_texcoords2D();
|
||||||
|
|
||||||
|
corrVAO.generate();
|
||||||
|
|
||||||
|
SetupGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::SetupGUI()
|
||||||
|
{
|
||||||
|
auto mainWindow = SetupMainWindow();
|
||||||
|
|
||||||
|
auto loadFileBtn = new nanogui::Button(mainWindow, "Load Mesh");
|
||||||
|
loadFileBtn->setCallback([this]() {
|
||||||
|
std::vector<std::pair<std::string, std::string>> fileTypes;
|
||||||
|
fileTypes.push_back(std::make_pair("obj", "OBJ File"));
|
||||||
|
auto file = nanogui::file_dialog(fileTypes, false);
|
||||||
|
if (!file.empty())
|
||||||
|
{
|
||||||
|
polymesh.clear();
|
||||||
|
if (!OpenMesh::IO::read_mesh(polymesh, file))
|
||||||
|
{
|
||||||
|
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Load Mesh",
|
||||||
|
"The specified file could not be loaded");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MeshUpdated();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
shadingBtn = new nanogui::ComboBox(mainWindow, { "Smooth Shading", "Flat Shading" });
|
||||||
|
|
||||||
|
performLayout();
|
||||||
|
|
||||||
|
auto paramWindow = new nanogui::Window(this, "Parametrization");
|
||||||
|
paramWindow->setPosition(Eigen::Vector2i(mainWindow->position().x(), mainWindow->position().y() + mainWindow->size().y() + 15));
|
||||||
|
paramWindow->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
|
||||||
|
|
||||||
|
auto cmbWeightType = new nanogui::ComboBox(paramWindow, { "Constant Weight", "Edge Length Weight", "Inverse Edge Length Weight", "Cotan Weight" });
|
||||||
|
|
||||||
|
auto parametrizeBtn = new nanogui::Button(paramWindow, "Calculate Parametrization");
|
||||||
|
parametrizeBtn->setCallback([this, cmbWeightType]() {
|
||||||
|
if (polymesh.n_vertices() == 0)
|
||||||
|
{
|
||||||
|
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Parametrization", "Please load a mesh for parametrization.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
switch (cmbWeightType->selectedIndex())
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
success = ComputeParametrizationOfTopologicalDisk<CONSTANT_WEIGHT>(polymesh);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
success = ComputeParametrizationOfTopologicalDisk<EDGE_LENGTH_WEIGHT>(polymesh);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
success = ComputeParametrizationOfTopologicalDisk<INV_EDGE_LENGTH_WEIGHT>(polymesh);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
success = ComputeParametrizationOfTopologicalDisk<COTAN_WEIGHT>(polymesh);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
renderer.Update();
|
||||||
|
hasParametrization = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
new nanogui::MessageDialog(this, nanogui::MessageDialog::Type::Warning, "Parametrization", "Parametrization failed.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
chkRenderTextureMap = new nanogui::CheckBox(paramWindow, "Render Texture Map");
|
||||||
|
|
||||||
|
performLayout();
|
||||||
|
|
||||||
|
auto registrationWindow = new nanogui::Window(this, "Registration");
|
||||||
|
registrationWindow->setPosition(Eigen::Vector2i(paramWindow->position().x(), paramWindow->position().y() + paramWindow->size().y() + 15));
|
||||||
|
registrationWindow->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 4, 4));
|
||||||
|
|
||||||
|
auto rotateBtn = new nanogui::Button(registrationWindow, "Random Rotation");
|
||||||
|
rotateBtn->setCallback([this]() {
|
||||||
|
secondMeshTransform = Eigen::Affine3f(Eigen::Translation3f(3 * meshBbox.diagonal().x(), 0, 0));
|
||||||
|
std::uniform_real_distribution<float> dist(-1.0f, 1.0f);
|
||||||
|
Eigen::Quaternionf q(dist(rnd), dist(rnd), dist(rnd), dist(rnd));
|
||||||
|
q.normalize();
|
||||||
|
secondMeshTransform *= q;
|
||||||
|
|
||||||
|
correspondences.clear();
|
||||||
|
BuildCorrVBOs();
|
||||||
|
});
|
||||||
|
|
||||||
|
corrCount = 10;
|
||||||
|
nanogui::TextBox* txtCorrCount;
|
||||||
|
auto sldCorrCount = nse::gui::AddLabeledSlider(registrationWindow, "Correspondence Count", std::make_pair(1.0f, 50.0f), (float)corrCount, txtCorrCount);
|
||||||
|
sldCorrCount->setCallback([this, txtCorrCount](float value) {
|
||||||
|
corrCount = (int)std::round(value);
|
||||||
|
txtCorrCount->setValue(std::to_string(corrCount));
|
||||||
|
});
|
||||||
|
sldCorrCount->callback()(sldCorrCount->value());
|
||||||
|
|
||||||
|
auto autoCorrs = new nanogui::Button(registrationWindow, "Automatic Correspondences");
|
||||||
|
autoCorrs->setCallback([this]() {
|
||||||
|
if (corrCount > polymesh.n_vertices())
|
||||||
|
return;
|
||||||
|
std::uniform_int_distribution<int> dist(0, (int)polymesh.n_vertices());
|
||||||
|
|
||||||
|
correspondences.clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < corrCount; ++i)
|
||||||
|
{
|
||||||
|
auto v = polymesh.vertex_handle(dist(rnd));
|
||||||
|
auto p = ToEigenVector(polymesh.point(v));
|
||||||
|
correspondences.push_back(std::make_pair(p, secondMeshTransform * p));
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildCorrVBOs();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto distortCorrespondencesBtn = new nanogui::Button(registrationWindow, "Distort Correspondences");
|
||||||
|
distortCorrespondencesBtn->setCallback([this]() {
|
||||||
|
std::normal_distribution<float> dist(0.0f, meshBbox.diagonal().norm() * 0.01f);
|
||||||
|
for (auto& c : correspondences)
|
||||||
|
c.first += Eigen::Vector3f(dist(rnd), dist(rnd), dist(rnd));
|
||||||
|
BuildCorrVBOs();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto registerBtn = new nanogui::Button(registrationWindow, "Register");
|
||||||
|
registerBtn->setCallback([this]() {
|
||||||
|
auto T = CalculateRigidRegistration(correspondences);
|
||||||
|
secondMeshTransform = T * secondMeshTransform;
|
||||||
|
|
||||||
|
for (auto& c : correspondences)
|
||||||
|
c.second = T * c.second;
|
||||||
|
BuildCorrVBOs();
|
||||||
|
});
|
||||||
|
|
||||||
|
chkRenderSecondMesh = new nanogui::CheckBox(registrationWindow, "Render Second Mesh", [this](bool checked) {
|
||||||
|
if (checked)
|
||||||
|
camera().FocusOnBBox(expandedBbox);
|
||||||
|
else
|
||||||
|
camera().FocusOnBBox(meshBbox);
|
||||||
|
});
|
||||||
|
|
||||||
|
performLayout();
|
||||||
|
|
||||||
|
auto maxWidth = std::max(mainWindow->size().x(), std::max(paramWindow->size().x(), registrationWindow->size().x()));
|
||||||
|
mainWindow->setFixedWidth(maxWidth);
|
||||||
|
paramWindow->setFixedWidth(maxWidth);
|
||||||
|
registrationWindow->setFixedWidth(maxWidth);
|
||||||
|
|
||||||
|
performLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::MeshUpdated()
|
||||||
|
{
|
||||||
|
//calculate the bounding Box of the mesh
|
||||||
|
meshBbox.reset();
|
||||||
|
for (auto v : polymesh.vertices())
|
||||||
|
meshBbox.expand(ToEigenVector(polymesh.point(v)));
|
||||||
|
|
||||||
|
expandedBbox = meshBbox;
|
||||||
|
secondMeshTransform = Eigen::Affine3f(Eigen::Translation3f(3 * meshBbox.diagonal().x(), 0, 0));
|
||||||
|
expandedBbox.max.x() += 3 * meshBbox.diagonal().x();
|
||||||
|
|
||||||
|
if(chkRenderSecondMesh->checked())
|
||||||
|
camera().FocusOnBBox(expandedBbox);
|
||||||
|
else
|
||||||
|
camera().FocusOnBBox(meshBbox);
|
||||||
|
|
||||||
|
polymesh.triangulate();
|
||||||
|
correspondences.clear();
|
||||||
|
|
||||||
|
hasParametrization = false;
|
||||||
|
|
||||||
|
renderer.Update();
|
||||||
|
BuildCorrVBOs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::BuildCorrVBOs()
|
||||||
|
{
|
||||||
|
std::vector<Eigen::Vector4f> pos;
|
||||||
|
pos.reserve(correspondences.size() * 2);
|
||||||
|
for (auto& c : correspondences)
|
||||||
|
{
|
||||||
|
pos.push_back(Eigen::Vector4f(c.first.x(), c.first.y(), c.first.z(), 1.0f));
|
||||||
|
pos.push_back(Eigen::Vector4f(c.second.x(), c.second.y(), c.second.z(), 1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
corrVAO.bind();
|
||||||
|
ShaderPool::Instance()->simpleShader.bind();
|
||||||
|
corrPositions.uploadData(pos).bindToAttribute("position");
|
||||||
|
corrVAO.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Viewer::resizeEvent(const Eigen::Vector2i& size)
|
||||||
|
{
|
||||||
|
AbstractViewer::resizeEvent(size);
|
||||||
|
|
||||||
|
float ratio = (float)size.x() / size.y();
|
||||||
|
Eigen::Affine3f proj = Eigen::Translation3f(1.0f - 0.1f / ratio - 1.8f / ratio, -0.9f, 0.0f) * Eigen::AlignedScaling3f(1.8f / ratio, 1.8f, 1.0f);
|
||||||
|
texMapProjectionMatrix = proj.matrix();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Viewer::drawContents()
|
||||||
|
{
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
if (!polymesh.vertices_empty())
|
||||||
|
{
|
||||||
|
Eigen::Matrix4f view, proj;
|
||||||
|
camera().ComputeCameraMatrices(view, proj);
|
||||||
|
Eigen::Matrix4f mvp = proj * view;
|
||||||
|
|
||||||
|
renderer.Render(view, proj, shadingBtn->selectedIndex() == 1, hasParametrization);
|
||||||
|
|
||||||
|
if (chkRenderSecondMesh->checked())
|
||||||
|
{
|
||||||
|
renderer.Render(view * secondMeshTransform.matrix(), proj, shadingBtn->selectedIndex() == 1, false, Eigen::Vector4f(0.2f, 0.3f, 0.4f, 1.0f));
|
||||||
|
if (correspondences.size() > 0)
|
||||||
|
{
|
||||||
|
corrVAO.bind();
|
||||||
|
ShaderPool::Instance()->simpleShader.bind();
|
||||||
|
ShaderPool::Instance()->simpleShader.setUniform("color", Eigen::Vector4f(1, 1, 1, 1));
|
||||||
|
ShaderPool::Instance()->simpleShader.setUniform("mvp", mvp);
|
||||||
|
glDrawArrays(GL_LINES, 0, (GLsizei)correspondences.size() * 2);
|
||||||
|
corrVAO.unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasParametrization && chkRenderTextureMap->checked())
|
||||||
|
{
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
renderer.RenderTextureMap(texMapProjectionMatrix, Eigen::Vector4f(1, 1, 1, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
exercise5/src/main.cpp
Normal file
38
exercise5/src/main.cpp
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// This source code is property of the Computer Graphics and Visualization
|
||||||
|
// chair of the TU Dresden. Do not distribute!
|
||||||
|
// Copyright (C) CGV TU Dresden - All Rights Reserved
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <util/GLDebug.h>
|
||||||
|
|
||||||
|
#include "Viewer.h"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
std::cout.imbue(std::locale(""));
|
||||||
|
|
||||||
|
nanogui::init();
|
||||||
|
|
||||||
|
{
|
||||||
|
nanogui::ref<Viewer> viewer = new Viewer();
|
||||||
|
viewer->setVisible(true);
|
||||||
|
|
||||||
|
nse::util::GLDebug::SetupDebugCallback();
|
||||||
|
nse::util::GLDebug::IgnoreGLError(131185); //buffer usage info
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
nanogui::mainloop();
|
||||||
|
}
|
||||||
|
catch (std::runtime_error& e)
|
||||||
|
{
|
||||||
|
std::cerr << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
nanogui::shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
solution/linux/Exercise5
Normal file
BIN
solution/linux/Exercise5
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
solution/win/Exercise5.exe
Normal file
BIN
solution/win/Exercise5.exe
Normal file
Binary file not shown.
Loading…
Reference in a new issue