265 lines
No EOL
6.7 KiB
C++
265 lines
No EOL
6.7 KiB
C++
#ifndef GLHELPERS_H
|
|
#define GLHELPERS_H
|
|
|
|
#include "path_glsl_files.h"
|
|
|
|
class GlHelper {
|
|
public:
|
|
GLFWwindow* window;
|
|
|
|
std::vector<std::string> all_paths;
|
|
std::vector<std::string> vertex_shader_paths;
|
|
std::vector<std::string> tess_control_shader_paths;
|
|
std::vector<std::string> tess_evaluation_shader_paths;
|
|
std::vector<std::string> fragment_shader_paths;
|
|
|
|
double time_of_last_shader_compilation = 0;
|
|
bool isFirstCompilation = true;
|
|
|
|
GlHelper(int task) {
|
|
last_time = get_seconds();
|
|
|
|
setShaderPaths(task, all_paths, vertex_shader_paths, tess_control_shader_paths, tess_evaluation_shader_paths, fragment_shader_paths);
|
|
}
|
|
|
|
// Use GLFW and GLAD to create a window
|
|
GLFWwindow* createWindow() {
|
|
// Initialize GLFW
|
|
if (!glfwInit()) {
|
|
std::cerr << "Failed to initialize GLFW" << std::endl;
|
|
return NULL;
|
|
}
|
|
|
|
// Set GLFW to create an OpenGL context (version 440 core)
|
|
glfwWindowHint(GLFW_SAMPLES, 4);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
|
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
|
GLFWwindow* window = glfwCreateWindow(width, height, "shader-pipeline", NULL, NULL);
|
|
if (!window) {
|
|
std::cerr << "Failed to create GLFW window" << std::endl;
|
|
glfwTerminate();
|
|
return NULL;
|
|
}
|
|
|
|
// Make the OpenGL context current
|
|
glfwMakeContextCurrent(window);
|
|
|
|
// Initialize GLAD
|
|
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
|
|
std::cerr << "Failed to initialized GLAD" << std::endl;
|
|
glfwTerminate();
|
|
return NULL;
|
|
}
|
|
this->window = window;
|
|
return window;
|
|
}
|
|
|
|
// Handle window rescaling and update the projection matrix accordingly
|
|
void setPerspectiveMatrixBasedOnWindowScale() {
|
|
const auto& reshape = [](
|
|
GLFWwindow* window,
|
|
int _width,
|
|
int _height)
|
|
{
|
|
::width = _width, ::height = _height;
|
|
|
|
// windows can't handle variables named near and far.
|
|
float nearVal = 0.01f;
|
|
float farVal = 100.0f;
|
|
float top = static_cast<float>(tan(35. / 360. * M_PI)) * nearVal;
|
|
float right = top * (float)::width / (float)::height;
|
|
float left = -right;
|
|
float bottom = -top;
|
|
proj.setConstant(4, 4, 0.);
|
|
proj(0, 0) = (2.0f * nearVal) / (right - left);
|
|
proj(1, 1) = (2.0f * nearVal) / (top - bottom);
|
|
proj(0, 2) = (right + left) / (right - left);
|
|
proj(1, 2) = (top + bottom) / (top - bottom);
|
|
proj(2, 2) = -(farVal + nearVal) / (farVal - nearVal);
|
|
proj(3, 2) = -1.0f;
|
|
proj(2, 3) = -(2.0f * farVal * nearVal) / (farVal - nearVal);
|
|
};
|
|
// Set up window resizing
|
|
glfwSetWindowSizeCallback(window, reshape);
|
|
{
|
|
int width_window, height_window;
|
|
glfwGetWindowSize(window, &width_window, &height_window);
|
|
reshape(window, width_window, height_window);
|
|
}
|
|
}
|
|
|
|
// Setup a VAO from a mesh
|
|
void createVAO() {
|
|
icosahedron(V, F);
|
|
mesh_to_vao(V, F, VAO);
|
|
igl::opengl::report_gl_error("mesh_to_vao");
|
|
}
|
|
|
|
// Make GLFW listen for keyboard and mouse inputs
|
|
// and change the user input state accordingly
|
|
void setKeyboardAndMouseCallbacks() {
|
|
std::cout << R"(
|
|
Usage:
|
|
[] the window can be rescaled
|
|
[Click and drag] to orbit view
|
|
[Scroll] to translate view in and out
|
|
A,a toggle animation
|
|
L,l toggle wireframe rending
|
|
Z,z reset view to look along z-axis
|
|
|
|
)";
|
|
|
|
// Close the window if user presses ESC or CTRL+C
|
|
glfwSetKeyCallback(
|
|
window,
|
|
[](GLFWwindow* window, int key, int scancode, int action, int mods)
|
|
{
|
|
if (key == 256 || (key == 67 && (mods & GLFW_MOD_CONTROL)))
|
|
{
|
|
glfwSetWindowShouldClose(window, true);
|
|
}
|
|
});
|
|
// Listen to keypresses on A, L and Z
|
|
glfwSetCharModsCallback(
|
|
window,
|
|
[](GLFWwindow* window, unsigned int codepoint, int modifier)
|
|
{
|
|
switch (codepoint)
|
|
{
|
|
case 'A':
|
|
case 'a':
|
|
is_animating ^= 1;
|
|
if (is_animating)
|
|
{
|
|
last_time = get_seconds();
|
|
}
|
|
break;
|
|
case 'L':
|
|
case 'l':
|
|
wire_frame ^= 1;
|
|
if (wire_frame) {
|
|
glDisable(GL_CULL_FACE);
|
|
}
|
|
else {
|
|
glEnable(GL_CULL_FACE);
|
|
}
|
|
break;
|
|
case 'Z':
|
|
case 'z':
|
|
view.matrix().block(0, 0, 3, 3).setIdentity();
|
|
break;
|
|
default:
|
|
std::cout << "Unrecognized key: " << (unsigned char)codepoint << std::endl;
|
|
break;
|
|
}
|
|
});
|
|
glfwSetMouseButtonCallback(
|
|
window,
|
|
[](GLFWwindow* window, int button, int action, int mods)
|
|
{
|
|
mouse_down = action == GLFW_PRESS;
|
|
});
|
|
glfwSetCursorPosCallback(
|
|
window,
|
|
[](GLFWwindow* window, double x, double y)
|
|
{
|
|
static double mouse_last_x = x;
|
|
static double mouse_last_y = y;
|
|
float dx = static_cast<float>(x - mouse_last_x);
|
|
float dy = static_cast<float>(y - mouse_last_y);
|
|
if (mouse_down)
|
|
{
|
|
// Two axis valuator with fixed up
|
|
float factor = std::abs(view.matrix()(2, 3));
|
|
view.rotate(
|
|
Eigen::AngleAxisf(
|
|
dx * factor / float(width),
|
|
Eigen::Vector3f(0, 1, 0)));
|
|
view.rotate(
|
|
Eigen::AngleAxisf(
|
|
dy * factor / float(height),
|
|
view.matrix().topLeftCorner(3, 3).inverse() * Eigen::Vector3f(1, 0, 0)));
|
|
}
|
|
mouse_last_x = x;
|
|
mouse_last_y = y;
|
|
});
|
|
glfwSetScrollCallback(window,
|
|
[](GLFWwindow* window, double xoffset, double yoffset)
|
|
{
|
|
view.matrix()(2, 3) =
|
|
std::min(std::max(view.matrix()(2, 3) + (float)yoffset, -100.0f), -2.0f);
|
|
});
|
|
}
|
|
|
|
bool glslFileChanged() {
|
|
for (std::string& path : all_paths)
|
|
{
|
|
if (last_modification_time(path) > time_of_last_shader_compilation)
|
|
{
|
|
if (isFirstCompilation) {
|
|
isFirstCompilation = false;
|
|
}
|
|
else {
|
|
std::cout << path << " has changed since last compilation attempt." << std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool compileShaderIfChanged() {
|
|
if (glslFileChanged()) {
|
|
time_of_last_shader_compilation = get_seconds();
|
|
|
|
// (re)compile shader
|
|
if (!create_shader_program_from_files(
|
|
vertex_shader_paths,
|
|
tess_control_shader_paths,
|
|
tess_evaluation_shader_paths,
|
|
fragment_shader_paths,
|
|
prog_id)) {
|
|
// failed to compile shader
|
|
glDeleteProgram(prog_id);
|
|
prog_id = 0;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Update the uniforms used in the GLSL shaders
|
|
void updateShaderUniforms() {
|
|
// select program
|
|
glUseProgram(prog_id);
|
|
// Attach uniforms
|
|
{
|
|
if (is_animating)
|
|
{
|
|
double now = get_seconds();
|
|
animation_seconds += now - last_time;
|
|
last_time = now;
|
|
}
|
|
glUniform1f(glGetUniformLocation(prog_id, "animation_seconds"), static_cast<GLfloat>(animation_seconds));
|
|
}
|
|
glUniformMatrix4fv(
|
|
glGetUniformLocation(prog_id, "proj"), 1, false, proj.data());
|
|
glUniformMatrix4fv(
|
|
glGetUniformLocation(prog_id, "view"), 1, false, view.matrix().data());
|
|
// Draw mesh as wireframe
|
|
if (wire_frame)
|
|
{
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
}
|
|
else
|
|
{
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
#endif |