381 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			381 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
#include "platform_utils.h"
 | 
						|
 | 
						|
#include <GL/glew.h>
 | 
						|
#include <GLFW/glfw3.h>
 | 
						|
 | 
						|
#define GLM_ENABLE_EXPERIMENTAL
 | 
						|
#include <glm/glm.hpp>
 | 
						|
#include <glm/gtc/matrix_transform.hpp>
 | 
						|
#include <glm/gtx/transform.hpp>
 | 
						|
#include <glm/gtc/type_ptr.hpp>
 | 
						|
 | 
						|
#include <iostream>
 | 
						|
#include <fstream>
 | 
						|
#include <sstream>
 | 
						|
#include <string>
 | 
						|
#include <vector>
 | 
						|
#include <cstdint>
 | 
						|
#include <algorithm>
 | 
						|
 | 
						|
#include <FreeImage.h>
 | 
						|
 | 
						|
#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<Vertex> vertices;
 | 
						|
std::vector<GLuint> 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<float>() / 2.0f), 
 | 
						|
    scale_(1.0f), 
 | 
						|
    aspect_(static_cast<float>(width) / static_cast<float>(height)), 
 | 
						|
    near_z_(0.1f), 
 | 
						|
    far_z_(10.0f), 
 | 
						|
    fov_(glm::pi<float>() / 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<int16_t> 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<Vertex>& verts, std::vector<GLuint>& inds) {
 | 
						|
    float step = 1.0f / static_cast<float>(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<float>(j) / divisions, static_cast<float>(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<int>(viewport->size.x) || window_height != static_cast<int>(viewport->size.y)) {
 | 
						|
        window_width = static_cast<int>(viewport->size.x);
 | 
						|
        window_height = static_cast<int>(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<int16_t> Scene::LoadHGT(const std::string& filename, int width, int height) {
 | 
						|
    std::ifstream file(filename, std::ios::binary);
 | 
						|
    std::vector<int16_t> elevationData;
 | 
						|
 | 
						|
    if (file) {
 | 
						|
        for (int i = 0; i < width * height; ++i) {
 | 
						|
            int16_t value;
 | 
						|
            file.read(reinterpret_cast<char*>(&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<float>(window_width) / window_height, activeCamera_.nearPlane, activeCamera_.farPlane);
 | 
						|
}
 | 
						|
 | 
						|
GLuint Scene::CreateHeightmapTexture(std::vector<int16_t> 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<float> floatData;
 | 
						|
    for (int16_t &d : data) {
 | 
						|
        // Normalize the value between 0 and 1 based on the min/max range
 | 
						|
        d = static_cast<int16_t>(255 * (d - minVal) / (maxVal - minVal));
 | 
						|
 | 
						|
        floatData.push_back(static_cast<float>(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;
 | 
						|
} |