diff --git a/.gitignore b/.gitignore index 259148f..9793c1a 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,8 @@ *.exe *.out *.app + +# Custom +.idea/* +build/* +bindings/* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..053825d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.0) +set(PROJECT_NAME "Task-4-metaballs") +set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}) +set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}) +set(CMAKE_CXX_STANDARD 17) + +project(${PROJECT_NAME} CXX) + +# CONFIG option is important so that CMake doesnt search for modules into the default modules directory +find_package(imgui CONFIG) +find_package(glfw CONFIG) +find_package(glew CONFIG) +find_package(fmt CONFIG) +find_package(glm CONFIG) +find_package(stb CONFIG) + +add_executable( + ${PROJECT_NAME} + + src/main.cpp + src/utils/settings.h + src/utils/buffers.h + + src/utils/shader.cpp + src/elements/camera.h + + src/elements/opengl.h + src/elements/window.h + src/elements/cubemap.h + src/elements/metaballs.h + + bindings/imgui_impl_glfw.cpp + bindings/imgui_impl_opengl3.cpp + + # Shaders + assets/cubemap/shaders/vertex.glsl + assets/cubemap/shaders/fragment.glsl + + assets/metaballs/shaders/fragment.glsl + assets/metaballs/shaders/geometry.glsl + assets/metaballs/shaders/vertex.glsl +) + +add_custom_command( + TARGET ${PROJECT_NAME} + + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/assets ${PROJECT_BINARY_DIR}/assets +) + +target_compile_definitions( + ${PROJECT_NAME} + PUBLIC + IMGUI_IMPL_OPENGL_LOADER_GLEW +) + +target_link_libraries( + ${PROJECT_NAME} + + imgui::imgui + GLEW::glew_s + glfw::glfw + fmt::fmt + glm::glm + stb::stb +) diff --git a/README.md b/README.md index 985e0a8..18ab755 100644 --- a/README.md +++ b/README.md @@ -1 +1,14 @@ -# ComputerGraphics-OpenGL \ No newline at end of file +# GPU metaballs marching cubes + +### Условие +Динамические metaball'ы c отражением и преломлением + +## Запуск +Собираем проект: +1) Windows `build.bat` +2) Linux `build.sh` + +Далее запускаем `build/Task-4-metaballs` + +## Примеры +![](screenshots/metaballs.png) diff --git a/assets/cubemap/shaders/fragment.glsl b/assets/cubemap/shaders/fragment.glsl new file mode 100644 index 0000000..f4feb8e --- /dev/null +++ b/assets/cubemap/shaders/fragment.glsl @@ -0,0 +1,11 @@ +#version 430 core + +out vec4 gl_FragColor; + +in vec3 shader_texture_position; + +uniform samplerCube u_skybox; + +void main() { + gl_FragColor = texture(u_skybox, shader_texture_position); +} diff --git a/assets/cubemap/shaders/vertex.glsl b/assets/cubemap/shaders/vertex.glsl new file mode 100644 index 0000000..17e7f72 --- /dev/null +++ b/assets/cubemap/shaders/vertex.glsl @@ -0,0 +1,15 @@ +#version 430 core + +layout (location = 0) in vec3 in_position; + +out vec3 shader_texture_position; + +uniform mat4 u_view; +uniform mat4 u_projection; + +void main() { + shader_texture_position = in_position; + vec4 pos = u_projection * u_view * vec4(in_position, 1.0); + + gl_Position = pos.xyww; +} diff --git a/assets/cubemap/textures/simple/back.jpg b/assets/cubemap/textures/simple/back.jpg new file mode 100644 index 0000000..470a679 Binary files /dev/null and b/assets/cubemap/textures/simple/back.jpg differ diff --git a/assets/cubemap/textures/simple/bottom.jpg b/assets/cubemap/textures/simple/bottom.jpg new file mode 100644 index 0000000..893f394 Binary files /dev/null and b/assets/cubemap/textures/simple/bottom.jpg differ diff --git a/assets/cubemap/textures/simple/front.jpg b/assets/cubemap/textures/simple/front.jpg new file mode 100644 index 0000000..4e17b77 Binary files /dev/null and b/assets/cubemap/textures/simple/front.jpg differ diff --git a/assets/cubemap/textures/simple/left.jpg b/assets/cubemap/textures/simple/left.jpg new file mode 100644 index 0000000..5750b91 Binary files /dev/null and b/assets/cubemap/textures/simple/left.jpg differ diff --git a/assets/cubemap/textures/simple/right.jpg b/assets/cubemap/textures/simple/right.jpg new file mode 100644 index 0000000..8963037 Binary files /dev/null and b/assets/cubemap/textures/simple/right.jpg differ diff --git a/assets/cubemap/textures/simple/top.jpg b/assets/cubemap/textures/simple/top.jpg new file mode 100644 index 0000000..4db3c2a Binary files /dev/null and b/assets/cubemap/textures/simple/top.jpg differ diff --git a/assets/metaballs/shaders/fragment.glsl b/assets/metaballs/shaders/fragment.glsl new file mode 100644 index 0000000..b679ad0 --- /dev/null +++ b/assets/metaballs/shaders/fragment.glsl @@ -0,0 +1,26 @@ +#version 430 core + +uniform vec3 u_camera_pos; +uniform samplerCube u_skybox; + +float u_refraction_value = 1000; + +in vec3 shader_position; +in vec3 shader_normal; + +out vec4 gl_FragColor; + +float coefficient_refraction = 0.25f; +float coefficient_reflection = 0.75f; +float fresnel_alpha = 0.5f; + +void main() { + vec3 reflection = reflect(u_camera_pos, shader_normal); + vec3 refraction = refract(u_camera_pos, shader_normal, 1 / coefficient_refraction); + + gl_FragColor = mix( + vec4(texture(u_skybox, refraction).rgb, 1.0), + vec4(texture(u_skybox, reflection).rgb, 1.0), + 0.2f + ); +} diff --git a/assets/metaballs/shaders/geometry.glsl b/assets/metaballs/shaders/geometry.glsl new file mode 100644 index 0000000..f1aafb0 --- /dev/null +++ b/assets/metaballs/shaders/geometry.glsl @@ -0,0 +1,98 @@ +#version 430 core +layout (points) in; +layout (triangle_strip, max_vertices = 24) out; + +layout(std430, binding = 1) buffer edge_table_buffer { int edge_table[]; }; +layout(std430, binding = 2) buffer triangle_table_buffer { int triangle_table[256][16]; }; + +uniform mat4 u_model; +uniform mat4 u_view; +uniform mat4 u_projection; + +const float CUBE_SIZE = 0.05f; + +out vec3 shader_normal; + +float vertex_function(vec3 point) { + vec3 metaball_center1 = vec3(0.0f, 0.0f, 0.0f); + vec3 metaball_center2 = vec3(0.1f, -0.2f, 0.5f); + vec3 metaball_center3 = vec3(-0.5f, 0.5f, -0.5f); + + vec3 delta1 = point - metaball_center1; + vec3 delta2 = point - metaball_center2; + vec3 delta3 = point - metaball_center3; + + float radius = 0.25f; + float sum = (1 / dot(delta1, delta1)) + (1 / dot(delta2, delta2)) + (1 / dot(delta3, delta3)); + + return radius * radius * sum - 1; +} + +vec3 evaluate_normal(vec3 point) { + return normalize(vec3( + vertex_function(point + vec3(CUBE_SIZE / 8, 0, 0)) - vertex_function(point - vec3(CUBE_SIZE / 8, 0, 0)), + vertex_function(point + vec3(0, CUBE_SIZE / 8, 0)) - vertex_function(point - vec3(0, CUBE_SIZE / 8, 0)), + vertex_function(point + vec3(0, 0, CUBE_SIZE / 8)) - vertex_function(point - vec3(0, 0, CUBE_SIZE / 8)) + )); +} + +vec3 lerp(vec3 point1, vec3 point2) { + float coefficient = abs(vertex_function(point1)) / (abs(vertex_function(point1)) + abs(vertex_function(point2))); + return point1 + (point2 - point1) * coefficient; +} + +void main() { + vec3 cube[8] = { + gl_in[0].gl_Position.xyz + vec3(-CUBE_SIZE / 2, -CUBE_SIZE/ 2, -CUBE_SIZE/ 2), + gl_in[0].gl_Position.xyz + vec3(CUBE_SIZE/ 2, -CUBE_SIZE/ 2, -CUBE_SIZE/ 2), + gl_in[0].gl_Position.xyz + vec3(CUBE_SIZE/ 2, -CUBE_SIZE/ 2, CUBE_SIZE/ 2), + gl_in[0].gl_Position.xyz + vec3(-CUBE_SIZE/ 2, -CUBE_SIZE/ 2, CUBE_SIZE/ 2), + gl_in[0].gl_Position.xyz + vec3(-CUBE_SIZE/ 2, CUBE_SIZE/ 2, -CUBE_SIZE/ 2), + gl_in[0].gl_Position.xyz + vec3(CUBE_SIZE/ 2, CUBE_SIZE/ 2, -CUBE_SIZE/ 2), + gl_in[0].gl_Position.xyz + vec3(CUBE_SIZE/ 2, CUBE_SIZE/ 2, CUBE_SIZE/ 2), + gl_in[0].gl_Position.xyz + vec3(-CUBE_SIZE/ 2, CUBE_SIZE/ 2, CUBE_SIZE/ 2) + }; + + int cubeBitMaskIndex = 0; + if (vertex_function(cube[0]) < 0) cubeBitMaskIndex |= 1; + if (vertex_function(cube[1]) < 0) cubeBitMaskIndex |= 2; + if (vertex_function(cube[2]) < 0) cubeBitMaskIndex |= 4; + if (vertex_function(cube[3]) < 0) cubeBitMaskIndex |= 8; + if (vertex_function(cube[4]) < 0) cubeBitMaskIndex |= 16; + if (vertex_function(cube[5]) < 0) cubeBitMaskIndex |= 32; + if (vertex_function(cube[6]) < 0) cubeBitMaskIndex |= 64; + if (vertex_function(cube[7]) < 0) cubeBitMaskIndex |= 128; + + vec3 vertexOnEdge[12]; + int edges = edge_table[cubeBitMaskIndex]; + if (edges == 0) return; + if ((edges & 1) != 0) vertexOnEdge[0] = lerp(cube[0], cube[1]); + if ((edges & 2) != 0) vertexOnEdge[1] = lerp(cube[1], cube[2]); + if ((edges & 4) != 0) vertexOnEdge[2] = lerp(cube[2], cube[3]); + if ((edges & 8) != 0) vertexOnEdge[3] = lerp(cube[3], cube[0]); + if ((edges & 16) != 0) vertexOnEdge[4] = lerp(cube[4], cube[5]); + if ((edges & 32) != 0) vertexOnEdge[5] = lerp(cube[5], cube[6]); + if ((edges & 64) != 0) vertexOnEdge[6] = lerp(cube[6], cube[7]); + if ((edges & 128) != 0) vertexOnEdge[7] = lerp(cube[7], cube[4]); + if ((edges & 256) != 0) vertexOnEdge[8] = lerp(cube[0], cube[4]); + if ((edges & 512) != 0) vertexOnEdge[9] = lerp(cube[1], cube[5]); + if ((edges & 1024) != 0) vertexOnEdge[10] = lerp(cube[2], cube[6]); + if ((edges & 2048) != 0) vertexOnEdge[11] = lerp(cube[3], cube[7]); + + mat4 mvp = u_projection * u_view * u_model; + for (int i = 0; triangle_table[cubeBitMaskIndex][i] != -1; i += 3) { + gl_Position = mvp * vec4(vertexOnEdge[triangle_table[cubeBitMaskIndex][i]], 1); + shader_normal = evaluate_normal(vertexOnEdge[triangle_table[cubeBitMaskIndex][i]]); + EmitVertex(); + + gl_Position = mvp * vec4(vertexOnEdge[triangle_table[cubeBitMaskIndex][i + 1]], 1); + shader_normal = evaluate_normal(vertexOnEdge[triangle_table[cubeBitMaskIndex][i + 1]]); + EmitVertex(); + + gl_Position = mvp * vec4(vertexOnEdge[triangle_table[cubeBitMaskIndex][i + 2]], 1); + shader_normal = evaluate_normal(vertexOnEdge[triangle_table[cubeBitMaskIndex][i + 2]]); + EmitVertex(); + + EndPrimitive(); + } +} diff --git a/assets/metaballs/shaders/vertex.glsl b/assets/metaballs/shaders/vertex.glsl new file mode 100644 index 0000000..29a0c41 --- /dev/null +++ b/assets/metaballs/shaders/vertex.glsl @@ -0,0 +1,7 @@ +#version 430 core + +layout (location = 0) in vec3 in_position; + +void main() { + gl_Position = vec4(in_position, 1.0); +} diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..3898417 --- /dev/null +++ b/build.bat @@ -0,0 +1,9 @@ +@ECHO ON + +RMDIR /Q /S build +MKDIR build +PUSHD build + +conan install .. +cmake .. -G "Visual Studio 15 2017 Win64" +cmake --build . --config Release diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..a09a066 --- /dev/null +++ b/build.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -e +set -x + +rm -rf build +mkdir build +pushd build + +export CONAN_SYSREQUIRES_MODE=enabled +conan install .. +cmake .. -DCMAKE_BUILD_TYPE=Release +cmake --build . diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 0000000..8e1460f --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,21 @@ +[requires] +imgui/1.74 +glfw/3.3.2 +glew/2.1.0 +fmt/7.0.3 +glm/0.9.9.8 +stb/20200203 +assimp/5.0.1 + +[generators] +cmake_find_package_multi + +[options] +glew:shared=False +fmt:header_only=True + +[imports] +./res/bindings, imgui_impl_glfw.cpp -> ../bindings +./res/bindings, imgui_impl_opengl3.cpp -> ../bindings +./res/bindings, imgui_impl_glfw.h -> ../bindings +./res/bindings, imgui_impl_opengl3.h -> ../bindings diff --git a/screenshots/metaballs.png b/screenshots/metaballs.png new file mode 100644 index 0000000..75ccb0e Binary files /dev/null and b/screenshots/metaballs.png differ diff --git a/src/elements/camera.h b/src/elements/camera.h new file mode 100644 index 0000000..219331a --- /dev/null +++ b/src/elements/camera.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include +#include +#include + +class Camera { + const GLfloat MOUSE_SENSITIVITY = 45.0f; + + glm::vec3 position_{}; + + GLfloat pitch_ = 0.0f; + GLfloat yaw_ = 0.0f; + + GLfloat zoom_ = 45.0f; + +public: + explicit Camera(glm::vec3 position) { + position_ = position; + } + + [[nodiscard]] + glm::mat4 view() const { + return glm::lookAt( + position(), + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 1.0f, 0.0f) + ); + } + + void process_mouse_movement(GLfloat offset_x, GLfloat offset_y) { + yaw_ += offset_x * MOUSE_SENSITIVITY; + pitch_ += offset_y * MOUSE_SENSITIVITY; + + if (pitch_ > 89.0f) pitch_ = 89.0f; + if (pitch_ < -89.0f) pitch_ = -89.0f; + } + + void process_mouse_scroll(GLfloat offset_y) { + if (offset_y < 0) zoom_ *= 1.2; + else zoom_ /= 1.2; + } + + [[nodiscard]] + glm::vec3 position() const { + glm::vec3 pos = position_ * zoom_; + + auto rotate = glm::rotate( + glm::identity(), + glm::radians(yaw_), + glm::vec3(0.0f, 1.0f, 0.0f) + ); + pos = glm::vec3(rotate * glm::vec4(pos, 1.0f)); + + auto inverse = glm::vec3(rotate * glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + rotate = glm::rotate( + glm::identity(), + glm::radians(pitch_), + inverse + ); + + return pos = glm::vec3(rotate * glm::vec4(pos, 1.0f)); + } +}; diff --git a/src/elements/cubemap.h b/src/elements/cubemap.h new file mode 100644 index 0000000..962e311 --- /dev/null +++ b/src/elements/cubemap.h @@ -0,0 +1,139 @@ +#pragma once + +#define STB_IMAGE_IMPLEMENTATION + +#include +#include +#include +#include "../utils/shader.h" +#include "../utils/settings.h" + +class Cubemap { + Shader shader_; + GLuint texture_id_; + GLuint vao_; + GLuint vbo_; + +public: + explicit Cubemap(const Shader &shader) + : shader_(shader), + vao_(0), + vbo_(0) { + texture_id_ = load_cubemap(cube_textures); + init_buffers(vbo_, vao_); + } + + ~Cubemap() { + glDeleteVertexArrays(1, &vao_); + glDeleteBuffers(1, &vbo_); + } + + void render(glm::mat4 &view, glm::mat4 &projection) { + glDepthFunc(GL_LEQUAL); + shader_.use(); + shader_.set_uniform("u_skybox", 0); + shader_.set_uniform("u_view", glm::value_ptr(view)); + shader_.set_uniform("u_projection", glm::value_ptr(projection)); + + glBindVertexArray(vao_); + glActiveTexture(Settings::GL_TEXTURE_CUBEMAP); + glBindTexture(GL_TEXTURE_CUBE_MAP, texture_id_); + glDrawArrays(GL_TRIANGLES, 0, 36); + glBindVertexArray(0); + glDepthFunc(GL_LESS); + } + + static GLuint load_cubemap(const std::vector &maps) { + GLuint textureID; + glGenTextures(1, &textureID); + glBindTexture(GL_TEXTURE_CUBE_MAP, textureID); + + int width, height, nrComponents; + for (GLuint i = 0; i < maps.size(); i++) { + GLubyte *data = stbi_load(maps[i].c_str(), &width, &height, &nrComponents, 0); + if (data) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, + 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + stbi_image_free(data); + } else { + std::cout << "Cubemap texture failed to load at path: " << maps[i] << std::endl; + stbi_image_free(data); + } + } + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + return textureID; + } + + static void init_buffers(GLuint &skyboxVBO, GLuint &skyboxVAO) { + glGenVertexArrays(1, &skyboxVAO); + glGenBuffers(1, &skyboxVBO); + glBindVertexArray(skyboxVAO); + glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO); + glBufferData(GL_ARRAY_BUFFER, + skybox_vertices.size() * sizeof(skybox_vertices[0]), + skybox_vertices.data(), + GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr); + } + +private: + inline static const std::vector skybox_vertices = { + -20.0f, 20.0f, -20.0f, + -20.0f, -20.0f, -20.0f, + 20.0f, -20.0f, -20.0f, + 20.0f, -20.0f, -20.0f, + 20.0f, 20.0f, -20.0f, + -20.0f, 20.0f, -20.0f, + + -20.0f, -20.0f, 20.0f, + -20.0f, -20.0f, -20.0f, + -20.0f, 20.0f, -20.0f, + -20.0f, 20.0f, -20.0f, + -20.0f, 20.0f, 20.0f, + -20.0f, -20.0f, 20.0f, + + 20.0f, -20.0f, -20.0f, + 20.0f, -20.0f, 20.0f, + 20.0f, 20.0f, 20.0f, + 20.0f, 20.0f, 20.0f, + 20.0f, 20.0f, -20.0f, + 20.0f, -20.0f, -20.0f, + + -20.0f, -20.0f, 20.0f, + -20.0f, 20.0f, 20.0f, + 20.0f, 20.0f, 20.0f, + 20.0f, 20.0f, 20.0f, + 20.0f, -20.0f, 20.0f, + -20.0f, -20.0f, 20.0f, + + -20.0f, 20.0f, -20.0f, + 20.0f, 20.0f, -20.0f, + 20.0f, 20.0f, 20.0f, + 20.0f, 20.0f, 20.0f, + -20.0f, 20.0f, 20.0f, + -20.0f, 20.0f, -20.0f, + + -20.0f, -20.0f, -20.0f, + -20.0f, -20.0f, 20.0f, + 20.0f, -20.0f, -20.0f, + 20.0f, -20.0f, -20.0f, + -20.0f, -20.0f, 20.0f, + 20.0f, -20.0f, 20.0f + }; + + const std::vector cube_textures = { + "assets/cubemap/textures/simple/right.jpg", + "assets/cubemap/textures/simple/left.jpg", + "assets/cubemap/textures/simple/top.jpg", + "assets/cubemap/textures/simple/bottom.jpg", + "assets/cubemap/textures/simple/front.jpg", + "assets/cubemap/textures/simple/back.jpg" + }; +}; diff --git a/src/elements/metaballs.h b/src/elements/metaballs.h new file mode 100644 index 0000000..c5ca4db --- /dev/null +++ b/src/elements/metaballs.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include "../utils/shader.h" +#include "../utils/buffers.h" + +class Metaballs { + constexpr static int GRID_SIZE = 40; + constexpr static float GRID_MINI_CUBE_SIZE = 0.05f; + + Shader shader_; + + GLuint vao_; + GLuint vbo_; + + std::vector grid_; + +public: + explicit Metaballs(const Shader &shader) + : shader_(shader), + vao_(0), + vbo_(0), + grid_(init_grid()) { + init_metaballs(vbo_, vao_); + init_buffer_edge_table(); + init_buffer_triangle_table(); + } + + void render(glm::mat4 &model, glm::mat4 &view, glm::mat4 &projection) { + shader_.use(); + shader_.set_uniform("u_model", glm::value_ptr(model)); + shader_.set_uniform("u_view", glm::value_ptr(view)); + shader_.set_uniform("u_projection", glm::value_ptr(projection)); + shader_.set_uniform("u_camera_pos", camera.position()); + + glBindVertexArray(vao_); + glDrawArrays(GL_POINTS, 0, grid_.size() / 3); + glBindVertexArray(0); + } + +private: + static std::vector init_grid() { + std::vector grid; + + for (int i = -GRID_SIZE; i < GRID_SIZE; ++i) { + for (int j = -GRID_SIZE; j < GRID_SIZE; ++j) { + for (int k = -GRID_SIZE; k < GRID_SIZE; ++k) { + grid.push_back(float(i) * GRID_MINI_CUBE_SIZE); + grid.push_back(float(j) * GRID_MINI_CUBE_SIZE); + grid.push_back(float(k) * GRID_MINI_CUBE_SIZE); + } + } + } + + return grid; + } + + void init_metaballs(GLuint &vbo, GLuint &vao) { + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, + grid_.size() * sizeof(grid_[0]), + grid_.data(), + GL_STATIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr); + } + + static void init_buffer_edge_table() { + auto buffer_data = Buffers::TABLE_EDGES; + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, + buffer_data.size() * sizeof(buffer_data[0]), + buffer_data.data(), + GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, buffer); + } + + static void init_buffer_triangle_table() { + auto buffer_data = Buffers::TABLE_TRIANGLE; + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); + glBufferData(GL_SHADER_STORAGE_BUFFER, + buffer_data.size() * sizeof(buffer_data[0]), + buffer_data.data(), + GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, buffer); + } +}; diff --git a/src/elements/opengl.h b/src/elements/opengl.h new file mode 100644 index 0000000..07da107 --- /dev/null +++ b/src/elements/opengl.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "../utils/settings.h" + +class OpenGl { + std::string window_name_; + GLFWwindow *window_; + +public: + explicit OpenGl(std::string window_name) + : window_name_(std::move(window_name)) { + window_ = init_opengl(); + } + + ~OpenGl() { + glfwDestroyWindow(window_); + glfwTerminate(); + } + + GLFWwindow *window() { + return window_; + } + +private: + GLFWwindow *init_opengl() { + glfwInit(); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_RESIZABLE, GL_FALSE); + glfwWindowHint(GLFW_SAMPLES, 4); + + GLFWwindow *window = glfwCreateWindow( + Settings::WINDOW_WIDTH, + Settings::WINDOW_HEIGHT, + window_name_.c_str(), + nullptr, + nullptr + ); + + if (window == nullptr) { + glfwTerminate(); + throw std::runtime_error("Failed to create GLFW window"); + } + + glfwMakeContextCurrent(window); + + glfwSetKeyCallback(window, key_callback); + glfwSetScrollCallback(window, scroll_callback); + + glewExperimental = GL_TRUE; + if (glewInit() != GLEW_OK) { + throw std::runtime_error("Failed to initialize GLEW"); + } + + int frame_width, frame_height; + glfwGetFramebufferSize(window, &frame_width, &frame_height); + glViewport(0, 0, frame_width, frame_height); + glEnable(GL_DEPTH_TEST); + + return window; + } + + static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mode) { + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { + glfwSetWindowShouldClose(window, GL_TRUE); + } + } + + static void scroll_callback(GLFWwindow *window, double offset_x, double offset_y) { + camera.process_mouse_scroll(offset_y); + } +}; diff --git a/src/elements/window.h b/src/elements/window.h new file mode 100644 index 0000000..3f2aa23 --- /dev/null +++ b/src/elements/window.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include "cubemap.h" +#include "metaballs.h" +#include "../../bindings/imgui_impl_opengl3.h" +#include "../../bindings/imgui_impl_glfw.h" + +class Window { + Cubemap cubemap_; + Metaballs metaballs_; + ImGuiIO io_; + GLFWwindow *window_; + +public: + explicit Window(const Cubemap &cubemap, Metaballs metaballs, GLFWwindow *window) + : cubemap_(cubemap), + metaballs_(std::move(metaballs)), + window_(window), + io_(init_ImGui(window)) {} + + void render() { + while (!glfwWindowShouldClose(window_)) { + check_error(); + check_events(); + init_display(); + mouse_events(); + + // Create Mode, View, Projection Matrix + glm::mat4 model = glm::mat4(1); + glm::mat4 view = camera.view(); + glm::mat4 projection = glm::perspective( + glm::radians(Settings::CAMERA_DEGREES), + Settings::RATIO, + Settings::Z_NEAR, + Settings::Z_FAR + ); + + // Render elements + metaballs_.render(model, view, projection); + view = glm::mat4(glm::mat3(camera.view())); + cubemap_.render(view, projection); + + // ImGui + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + + ImGui::NewFrame(); + ImGui::Begin("Information"); + ImGui::End(); + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // Swap + glfwSwapBuffers(window_); + } + } + +private: + // Check and call events + static void check_events() { + glfwPollEvents(); + } + + // Mouse events + void mouse_events() { + int display_width, display_height; + if (!ImGui::IsAnyWindowFocused()) { + auto[delta_x, delta_y] = ImGui::GetMouseDragDelta(); + ImGui::ResetMouseDragDelta(); + + glfwGetFramebufferSize(window_, &display_width, &display_height); + camera.process_mouse_movement(delta_x / float(display_width), + delta_y / float(display_height)); + } + } + + // Error + static void check_error() { + GLenum err; + while ((err = glGetError()) != GL_NO_ERROR) { + std::cerr << gluErrorString(err) << std::endl; + } + } + + // Display + void init_display() { + int display_width, display_height; + glfwGetFramebufferSize(window_, &display_width, &display_height); + glViewport(0, 0, display_width, display_height); + glClear(GLuint(GL_COLOR_BUFFER_BIT) | GLuint(GL_DEPTH_BUFFER_BIT)); + } + + static ImGuiIO init_ImGui(GLFWwindow *window) { + const char *glsl_version = "#version 430"; + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO &io = ImGui::GetIO(); + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init(glsl_version); + ImGui::StyleColorsDark(); + + return io; + } +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6b5abf4 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,27 @@ +#include "utils/shader.h" +#include "utils/settings.h" +#include "elements/metaballs.h" +#include "elements/cubemap.h" +#include "elements/window.h" +#include "elements/opengl.h" + +int main() { + // OpenGl + auto open_gl = OpenGl(Settings::WINDOW_NAME); + + // Shader + auto cubemap_shader = Shader(Settings::CUBEMAP_SHADER_VERTEX, Settings::CUBEMAP_SHADER_FRAGMENT); + auto metaballs_shader = Shader(Settings::METABALLS_SHADER_VERTEX, + Settings::METABALLS_SHADER_GEOMETRY, + Settings::METABALLS_SHADER_FRAGMENT); + + // Elements + auto cubemap = Cubemap(cubemap_shader); + auto metaballs = Metaballs(metaballs_shader); + + // Render window + auto window = Window(cubemap, metaballs, open_gl.window()); + window.render(); + + return 0; +} diff --git a/src/utils/buffers.h b/src/utils/buffers.h new file mode 100644 index 0000000..46aa0bc --- /dev/null +++ b/src/utils/buffers.h @@ -0,0 +1,357 @@ +#pragma once + +#include +#include +#include +#include + +struct Buffers { + inline const static std::vector CUBEMAP_TEXTURES = { + "assets/cubemap/textures/simple/right.jpg", + "assets/cubemap/textures/simple/left.jpg", + "assets/cubemap/textures/simple/top.jpg", + "assets/cubemap/textures/simple/bottom.jpg", + "assets/cubemap/textures/simple/front.jpg", + "assets/cubemap/textures/simple/back.jpg" + }; + + inline const static std::vector CUBEMAP_VERTICES = { + -1.0f, 1.0f, -1.0f, + -1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + + -1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, + -1.0f, -1.0f, 1.0f, + + -1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, -1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, -1.0f, + + -1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, -1.0f, + 1.0f, -1.0f, -1.0f, + -1.0f, -1.0f, 1.0f, + 1.0f, -1.0f, 1.0f + }; + + inline const static std::array TABLE_EDGES = { + 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, + 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, + 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, + 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, + 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, + 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, + 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, + 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, + 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, + 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, + 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, + 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, + 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, + 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, + 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, + 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, + 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, + 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, + 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, + 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, + 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, + 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, + 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, + 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, + 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, + 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 + }; + + inline const static std::array, 256> TABLE_TRIANGLE = { + { + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} + } + }; +}; diff --git a/src/utils/settings.h b/src/utils/settings.h new file mode 100644 index 0000000..628f82c --- /dev/null +++ b/src/utils/settings.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "../elements/camera.h" + +auto camera = Camera(glm::vec3(0.0f, 0.0f, 0.5f)); + +struct Settings { + inline static const std::string CUBEMAP_SHADER_VERTEX = "assets/cubemap/shaders/vertex.glsl"; + inline static const std::string CUBEMAP_SHADER_FRAGMENT = "assets/cubemap/shaders/fragment.glsl"; + + inline static const std::string METABALLS_SHADER_VERTEX = "assets/metaballs/shaders/vertex.glsl"; + inline static const std::string METABALLS_SHADER_GEOMETRY = "assets/metaballs/shaders/geometry.glsl"; + inline static const std::string METABALLS_SHADER_FRAGMENT = "assets/metaballs/shaders/fragment.glsl"; + + inline static const unsigned GL_TEXTURE_CUBEMAP = GL_TEXTURE0; + + inline static const std::string WINDOW_NAME = "Task-4-metaballs"; + inline static const GLuint WINDOW_WIDTH = 800; + inline static const GLuint WINDOW_HEIGHT = 600; + inline static constexpr GLfloat RATIO = GLfloat(WINDOW_WIDTH) / GLfloat(WINDOW_HEIGHT); + inline static constexpr GLfloat CAMERA_DEGREES = 45.0f; + inline static constexpr GLfloat Z_NEAR = 0.1f; + inline static constexpr GLfloat Z_FAR = 100.0f; +}; diff --git a/src/utils/shader.cpp b/src/utils/shader.cpp new file mode 100644 index 0000000..3004e54 --- /dev/null +++ b/src/utils/shader.cpp @@ -0,0 +1,174 @@ +#include "shader.h" + +#include +#include +#include + +namespace { + std::string read_shader_code(const std::string &filename) { + std::stringstream file_stream; + try { + std::ifstream file(filename.c_str()); + file_stream << file.rdbuf(); + file.close(); + } + catch (std::exception const &e) { + std::cerr << "Error reading shader file: " << e.what() << std::endl; + } + return file_stream.str(); + } +} + +Shader::Shader(const std::string &vertex_filepath, const std::string &fragment_filepath) { + const auto vertex_code = read_shader_code(vertex_filepath); + const auto fragment_code = read_shader_code(fragment_filepath); + compile(vertex_code, fragment_code); + link(); +} + +Shader::Shader( + const std::string &vertex_filepath, + const std::string &geometry_filepath, + const std::string &fragment_filepath +) { + const auto vertex_code = read_shader_code(vertex_filepath); + const auto geometry_code = read_shader_code(geometry_filepath); + const auto fragment_code = read_shader_code(fragment_filepath); + compile(vertex_code, geometry_code, fragment_code); + link(); +} + +Shader::~Shader() = default; + +void Shader::compile(const std::string &vertex_code, const std::string &fragment_code) { + const char *vcode = vertex_code.c_str(); + vertex_id_ = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_id_, 1, &vcode, nullptr); + glCompileShader(vertex_id_); + + const char *fcode = fragment_code.c_str(); + fragment_id_ = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_id_, 1, &fcode, nullptr); + glCompileShader(fragment_id_); + check_compile_error(); +} + +void Shader::compile( + const std::string &vertex_code, + const std::string &geometry_code, + const std::string &fragment_code +) { + const char *vcode = vertex_code.c_str(); + vertex_id_ = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_id_, 1, &vcode, nullptr); + glCompileShader(vertex_id_); + + const char *gcode = geometry_code.c_str(); + geometry_id_ = std::optional(glCreateShader(GL_GEOMETRY_SHADER)); + glShaderSource(geometry_id_.value(), 1, &gcode, nullptr); + glCompileShader(geometry_id_.value()); + + const char *fcode = fragment_code.c_str(); + fragment_id_ = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_id_, 1, &fcode, nullptr); + glCompileShader(fragment_id_); + + check_compile_error(); +} + +void Shader::link() { + program_id_ = glCreateProgram(); + glAttachShader(program_id_, vertex_id_); + glAttachShader(program_id_, fragment_id_); + + if (geometry_id_.has_value()) glAttachShader(program_id_, geometry_id_.value()); + glLinkProgram(program_id_); + check_linking_error(); + + glDeleteShader(vertex_id_); + glDeleteShader(fragment_id_); + if (geometry_id_.has_value()) glDeleteShader(geometry_id_.value()); +} + +void Shader::use() const { + glUseProgram(program_id_); +} + +template<> +void Shader::set_uniform(const std::string &name, int val) { + glUniform1i(glGetUniformLocation(program_id_, name.c_str()), val); +} + +template<> +void Shader::set_uniform(const std::string &name, bool val) { + glUniform1i(glGetUniformLocation(program_id_, name.c_str()), val); +} + +template<> +void Shader::set_uniform(const std::string &name, float val) { + glUniform1f(glGetUniformLocation(program_id_, name.c_str()), val); +} + +template<> +void Shader::set_uniform(const std::string &name, float val1, float val2) { + glUniform2f(glGetUniformLocation(program_id_, name.c_str()), val1, val2); +} + +template<> +void Shader::set_uniform(const std::string &name, float val1, float val2, float val3) { + glUniform3f(glGetUniformLocation(program_id_, name.c_str()), val1, val2, val3); +} + +template<> +void Shader::set_uniform(const std::string &name, float *val) { + glUniformMatrix4fv(glGetUniformLocation(program_id_, name.c_str()), 1, GL_FALSE, val); +} + +template<> +void Shader::set_uniform(const std::string &name, glm::vec3 vec) { + set_uniform(name, vec.x, vec.y, vec.z); +} + +void Shader::check_compile_error() const { + int success; + char infoLog[1024]; + + glGetShaderiv(vertex_id_, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(vertex_id_, 1024, nullptr, infoLog); + std::cerr << "Error compiling Vertex Shader:\n" << infoLog << std::endl; + } + + if (geometry_id_.has_value()) { + glGetShaderiv(geometry_id_.value(), GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(geometry_id_.value(), 1024, nullptr, infoLog); + std::cerr << "Error compiling Geometry Shader:\n" << infoLog << std::endl; + } + } + + glGetShaderiv(fragment_id_, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(fragment_id_, 1024, nullptr, infoLog); + std::cerr << "Error compiling Fragment Shader:\n" << infoLog << std::endl; + } +} + +void Shader::check_linking_error() const { + int success; + char infoLog[1024]; + glGetProgramiv(program_id_, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(program_id_, 1024, nullptr, infoLog); + std::cerr << "Error Linking Shader Program:\n" << infoLog << std::endl; + } +} + +template +void Shader::set_uniform(const std::string &name, T val) {} + +template +void Shader::set_uniform(const std::string &name, T val1, T val2) {} + +template +void Shader::set_uniform(const std::string &name, T val1, T val2, T val3) {} diff --git a/src/utils/shader.h b/src/utils/shader.h new file mode 100644 index 0000000..890c8b5 --- /dev/null +++ b/src/utils/shader.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include +#include +#include + +class Shader { +public: + Shader(const std::string &vertex_filepath, const std::string &fragment_filepath); + + Shader(const std::string &vertex_filepath, + const std::string &geometry_filepath, + const std::string &fragment_filepath); + + ~Shader(); + + void use() const; + + template + void set_uniform(const std::string &name, T val); + + template + void set_uniform(const std::string &name, T val1, T val2); + + template + void set_uniform(const std::string &name, T val1, T val2, T val3); + +private: + void check_compile_error() const; + void check_linking_error() const; + void compile(const std::string &vertex_code, const std::string &fragment_code); + void compile(const std::string &vertex_code, const std::string &geometry_code, const std::string &fragment_code); + void link(); + +private: + GLuint vertex_id_{}; + GLuint fragment_id_{}; + GLuint program_id_{}; + + std::optional geometry_id_{}; +};