#include "platform_utils.h" #include #include #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include #include #include #include #include #include #include #include #include #include "scene.h" #include "camera.h" #include "uniforms.h" #include "InitShader.h" #include "DebugCallback.h" #include "load_texture.h" #include "interface.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"; const std::string kColPath = "hgt/color/N02E16_col.tiff"; GLuint tex_id = 0; GLuint col_id = 0; float tessellationFactor = 32.0f; std::vector vertices; std::vector indices; } // 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.1f), far_z_(10.0f), fov_(glm::pi() / 6.0f) {} Scene::~Scene() { glDeleteProgram(shader_program_); glDeleteVertexArrays(1, &vao_); FBO::Cleanup(geo_fbo_); FBO::Cleanup(lht_fbo_); } void Scene::Init() { glewInit(); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); geo_fbo_ = FBO::GenerateFramebuffer(window_width, window_height); lht_fbo_ = FBO::GenerateFramebuffer(window_width, window_height); std::vector heightData = LoadHGT(kHGTPath, 3601, 3601); tex_id = CreateHeightmapTexture(heightData, 3601, 3601); col_id = Texture::LoadTexture(kColPath.c_str()); InitBuffers(); ReloadShader(); InitQuadBuffers(); InitUI(); CameraControls::SetPosition(activeCamera_, glm::vec3(1.4f, 1.4f, 0.0f)); CameraControls::SetRotation(activeCamera_, -glm::normalize(CameraControls::GetPosition(activeCamera_))); CameraControls::AdjustFocalLength(activeCamera_, 50.0f); // Temporary hardcode focal length CameraControls::AdjustSensorHeight(activeCamera_, 24.0f); // Temporary hardcode focal length CameraControls::AdjustNearPlane(activeCamera_, near_z_); CameraControls::AdjustFarPlane(activeCamera_, far_z_); UpdateCamera(); Uniforms::Init(); } void Scene::GenerateGrid(int divisions, std::vector& verts, std::vector& inds) { float step = 1.0f / static_cast(divisions); float halfSize = 0.5f; // Create vertices for patches for (int i = 0; i <= divisions; i++) { for (int j = 0; j <= divisions; j++) { float x = -halfSize + j * step; float y = -halfSize + i * step; Vertex vertex; vertex.position = glm::vec3(x, y, 0.0f); vertex.texCoord = glm::vec2(static_cast(j) / divisions, static_cast(i) / divisions); verts.push_back(vertex); } } // Create indices for patches for (int i = 0; i < divisions; i++) { for (int j = 0; j < divisions; j++) { int bottomLeft = i * (divisions + 1) + j; int bottomRight = bottomLeft + 1; int topLeft = bottomLeft + (divisions + 1); int topRight = topLeft + 1; inds.push_back(topLeft); inds.push_back(topRight); inds.push_back(bottomRight); inds.push_back(bottomLeft); } } } // Currently creates a test triangle and initializes its buffers void Scene::InitBuffers() { int divisions = Uniforms::GeoData.gridDensity; // Number of divisions along one axis of the grid GenerateGrid(divisions, vertices, indices); // Create and bind VAO, VBO, and EBO, and pass the data to OpenGL GLuint vao, vbo, ebo; glGenVertexArrays(1, &vao_); glGenBuffers(1, &vbo); glGenBuffers(1, &ebo); glBindVertexArray(vao_); // Bind and fill the vertex buffer glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), GL_STATIC_DRAW); // Bind and fill the element buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW); // Define vertex position attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position)); glEnableVertexAttribArray(0); // Define texture coordinate attribute glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texCoord)); glEnableVertexAttribArray(1); // Unbind VAO 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); } void Scene::InitUI() { Interface::AddWindow(windows_, "Viewport", [this]() { ImGui::Image((void*)(intptr_t)lht_fbo_.albedoTexture, ImVec2(window_width, window_height), ImVec2(0, 1), ImVec2(1, 0)); }); Interface::AddWindow(windows_, "Scene Settings", [this]() { // 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::SliderFloat("Tessellation Level", &Uniforms::GeoData.maxTessellation, 1.0f, 64.0f); ImGui::SliderInt("Patch Grid Density", &Uniforms::GeoData.gridDensity, 1, 24); ImGui::SliderFloat("Max Height", &Uniforms::GeoData.displacementScale, 0.0f, 1.0f); Window* viewport = Interface::GetWindowByName(windows_, "Viewport"); ImGui::Text("Viewport size: (%.3f, %.3f)", viewport->size.x, viewport->size.y); }); } // Allows for runtime shader updates void Scene::ReloadShader() { GLuint geo_shader = InitShader(kVertexShaderPath.c_str(), kTessellationCtrlPath.c_str(), kTessellationEvalPath.c_str(), kFragmentShaderPath.c_str()); GLuint lht_shader = InitShader(kQuadVertexPath.c_str(), kQuadFragmentPath.c_str()); if (geo_shader == -1 || lht_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 && quad_shader_program_ != -1) { glDeleteProgram(shader_program_); glDeleteProgram(quad_shader_program_); } shader_program_ = geo_shader; quad_shader_program_ = lht_shader; } } void Scene::Display(GLFWwindow* window) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); FBO::Bind(geo_fbo_); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(shader_program_); //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); Window* viewport = Interface::GetWindowByName(windows_, "Viewport"); if (viewport && viewport->size.x > 0 && viewport->size.y > 0) { OnScreenResize(viewport); } Uniforms::GeoData.PV = projection_matrix_ * CameraControls::GetViewMatrix(activeCamera_); // Projection-View matrix Uniforms::BufferSceneData(); Uniforms::GeoData.M = glm::rotate(angle_, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::scale(glm::vec3(scale_)); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex_id); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, col_id); GLint heightTextureLoc = glGetUniformLocation(shader_program_, "heightTexture"); glUniform1i(heightTextureLoc, 0); GLint colorTextureLoc = glGetUniformLocation(shader_program_, "colorTexture"); glUniform1i(colorTextureLoc, 1); GLint tessFactorLoc = glGetUniformLocation(shader_program_, "tessellationFactor"); glUniform1f(tessFactorLoc, tessellationFactor); glBindVertexArray(vao_); glPatchParameteri(GL_PATCH_VERTICES, 4); glDrawElements(GL_PATCHES, indices.size(), GL_UNSIGNED_INT, 0); //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); FBO::Bind(lht_fbo_); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(quad_shader_program_); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, geo_fbo_.albedoTexture); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, geo_fbo_.positionTexture); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, geo_fbo_.normalTexture); GLint albedoTextureLoc = glGetUniformLocation(quad_shader_program_, "albedoGbuffer"); glUniform1i(albedoTextureLoc, 0); GLint positionTextureLoc = glGetUniformLocation(quad_shader_program_, "positionGbuffer"); glUniform1i(positionTextureLoc, 1); GLint normalTextureLoc = glGetUniformLocation(quad_shader_program_, "normalGbuffer"); glUniform1i(normalTextureLoc, 2); // Render the full-screen quad glBindVertexArray(quad_vao_); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); FBO::Unbind(); //DrawGui(window); Interface::RenderWindows(windows_); glfwSwapBuffers(window); } void Scene::OnScreenResize(Window* viewport) { if (window_width != static_cast(viewport->size.x) || window_height != static_cast(viewport->size.y)) { window_width = static_cast(viewport->size.x); window_height = static_cast(viewport->size.y); UpdateCamera(); } } void Scene::UpdateViewport() { window_width = ImGui::GetWindowSize().x; window_height = ImGui::GetWindowSize().y; UpdateCamera(); } 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(CameraControls::GetFOV(activeCamera_), static_cast(window_width) / window_height, activeCamera_.nearPlane, activeCamera_.farPlane); } 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; }