#include #include #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scene.h" #include "Uniforms.h" #include "InitShader.h" //Functions for loading shaders from text files #include "DebugCallback.h" #include "PlatformUtils.h" namespace { const std::string kVertexShaderPath = "shaders/vertex.glsl"; const std::string kTessellationCtrlPath = "shaders/tessellation_ctrl.glsl"; const std::string kTessellationEvalPath = "shaders/tessellation_eval.glsl"; const std::string kFragmentShaderPath = "shaders/fragment.glsl"; const std::string kQuadVertexPath = "shaders/quad_vertex.glsl"; const std::string kQuadFragmentPath = "shaders/quad_fragment.glsl"; const std::string kHGTPath = "hgt/N02E016.hgt"; GLuint tex_id = -1; } // namespace Scene::Scene(int width, int height) : window_width(width), window_height(height), shader_program_(-1), quad_shader_program_(-1), vao_(-1), quad_vao_(-1), angle_(-glm::pi() / 2.0f), scale_(1.0f), aspect_(static_cast(width) / static_cast(height)), near_z_(0.01f), far_z_(100.0f), fov_(glm::pi() / 8.0f) {} Scene::~Scene() { glDeleteProgram(shader_program_); glDeleteVertexArrays(1, &vao_); fbo_.~FBO(); post_fbo_.~FBO(); } void Scene::Init() { glewInit(); // GL_DEPTH_TEST causes artifacts in meshes made up of multiple triangles // disabling for now //glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); fbo_.Init(window_width, window_height); post_fbo_.Init(window_width, window_height); std::vector heightData = LoadHGT(kHGTPath, 3601, 3601); tex_id = CreateHeightmapTexture(heightData, 3601, 3601); InitBuffers(); ReloadShader(); InitQuadBuffers(); quad_shader_program_ = InitShader(kQuadVertexPath.c_str(), kQuadFragmentPath.c_str()); UpdateCamera(); Uniforms::Init(); } // Currently creates a test triangle and initializes its buffers void Scene::InitBuffers() { GLuint patch_vbo; float patchVertices[] = { // Positions // Texture Coords -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // Bottom-left 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // Bottom-right 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // Top-right -0.5f, 0.5f, 0.0f, 0.0f, 1.0f // Top-left }; glGenVertexArrays(1, &vao_); glGenBuffers(1, &patch_vbo); glBindVertexArray(vao_); glBindBuffer(GL_ARRAY_BUFFER, patch_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(patchVertices), &patchVertices, GL_STATIC_DRAW); // Position attribute glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); // Texture coordinate attribute glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glBindVertexArray(0); } void Scene::InitQuadBuffers() { GLuint quad_vbo; // Full-screen quad vertices (position and texture coordinates) float quadVertices[] = { // positions // texCoords -1.0f, 1.0f, 0.0f, 1.0f, // Top-left -1.0f, -1.0f, 0.0f, 0.0f, // Bottom-left 1.0f, 1.0f, 1.0f, 1.0f, // Top-right 1.0f, -1.0f, 1.0f, 0.0f // Bottom-right }; glGenVertexArrays(1, &quad_vao_); glGenBuffers(1, &quad_vbo); glBindVertexArray(quad_vao_); glBindBuffer(GL_ARRAY_BUFFER, quad_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); glBindVertexArray(0); } // Allows for runtime shader updates void Scene::ReloadShader() { GLuint new_shader = InitShader(kVertexShaderPath.c_str(), kTessellationCtrlPath.c_str(), kTessellationEvalPath.c_str(), kFragmentShaderPath.c_str()); if (new_shader == -1) { DEBUG_BREAK(); glClearColor(1.0f, 0.0f, 1.0f, 0.0f); } else { glClearColor(0.35f, 0.35f, 0.35f, 0.0f); if (shader_program_ != -1) { glDeleteProgram(shader_program_); } shader_program_ = new_shader; } } void Scene::Display(GLFWwindow* window) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); fbo_.Bind(); glUseProgram(shader_program_); //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); view_matrix_ = glm::lookAt(glm::vec3(1.2f, 1.2f, 1.2f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); Uniforms::SceneData.PV = projection_matrix_ * view_matrix_; // Projection-View matrix Uniforms::BufferSceneData(); glm::mat4 model_matrix = glm::rotate(angle_, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::scale(glm::vec3(scale_)); glUniformMatrix4fv(Uniforms::UniformLocs::M, 1, false, glm::value_ptr(model_matrix)); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex_id); GLint heightTextureLoc = glGetUniformLocation(shader_program_, "heightTexture"); glUniform1i(heightTextureLoc, 0); glBindVertexArray(vao_); glPatchParameteri(GL_PATCH_VERTICES, 4); glDrawArrays(GL_PATCHES, 0, 4); //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); fbo_.Unbind(); post_fbo_.Bind(); glDisable(GL_DEPTH_TEST); glUseProgram(quad_shader_program_); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, fbo_.GetColorTexture()); GLint screenTextureLoc = glGetUniformLocation(quad_shader_program_, "screenTexture"); glUniform1i(screenTextureLoc, 0); // Texture unit 0 // Render the full-screen quad glBindVertexArray(quad_vao_); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); post_fbo_.Unbind(); glEnable(GL_DEPTH_TEST); DrawGui(window); glfwSwapBuffers(window); } void Scene::DrawGui(GLFWwindow* window) { // Begin ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); // Enable docking ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; ImGui::NewFrame(); static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_PassthruCentralNode; // Add menu bar ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; // Style windows const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->WorkPos); ImGui::SetNextWindowSize(viewport->WorkSize); ImGui::SetNextWindowViewport(viewport->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; window_flags |= ImGuiWindowFlags_NoBackground; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("TerraVisor", nullptr, window_flags); ImGui::PopStyleVar(3); // Submit the DockSpace if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); } if (ImGui::BeginMenuBar()) { if (ImGui::BeginMenu("Options")) { ImGui::Text("Nothing Here Yet, Check Back Later!"); ImGui::EndMenu(); } ImGui::EndMenuBar(); } ImGui::End(); // Draw Gui ImGui::Begin("Terrain Controls"); if (ImGui::Button("Quit")) { glfwSetWindowShouldClose(window, GLFW_TRUE); } ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::End(); // Draw FBO to ImGui window ImGui::Begin("Scene Window"); ImVec2 windowSize = ImGui::GetContentRegionAvail(); ImGui::Image((void*)(intptr_t)post_fbo_.GetColorTexture(), windowSize, ImVec2(0, 1), ImVec2(1, 0)); ImGui::End(); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } void Scene::Idle() { } int16_t Scene::SwapEndian(int16_t val) { return (val << 8) | ((val >> 8) & 0xFF); } std::vector Scene::LoadHGT(const std::string& filename, int width, int height) { std::ifstream file(filename, std::ios::binary); std::vector elevationData; if (file) { for (int i = 0; i < width * height; ++i) { int16_t value; file.read(reinterpret_cast(&value), sizeof(int16_t)); // Handle endianness value = SwapEndian(value); if (value == -32768) { value = -32767; // Clamp no-data values } elevationData.push_back(value); } } else { std::cerr << "Failed to open HGT file!" << std::endl; } return elevationData; } void Scene::UpdateCamera() { projection_matrix_ = glm::perspective(fov_, aspect_, near_z_, far_z_); } GLuint Scene::CreateHeightmapTexture(std::vector data, int width, int height) { int16_t minVal = *std::min_element(data.begin(), data.end()); int16_t maxVal = *std::max_element(data.begin(), data.end()); std::vector floatData; for (int16_t &d : data) { // Normalize the value between 0 and 1 based on the min/max range d = static_cast(255 * (d - minVal) / (maxVal - minVal)); floatData.push_back(static_cast(d / 255.0f)); } GLuint textureID; glGenTextures(1, &textureID); glBindTexture(GL_TEXTURE_2D, textureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, width, height, 0, GL_RED, GL_FLOAT, floatData.data()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, 0); return textureID; }