terravisor/source/scene.cpp

367 lines
11 KiB
C++
Raw Normal View History

2024-08-10 19:12:21 -04:00
#include <GL/glew.h>
#include <GLFW/glfw3.h>
2024-08-12 21:40:28 -04:00
#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>
2024-08-11 21:20:17 -04:00
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
2024-08-10 19:12:21 -04:00
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
2024-08-15 00:56:29 -04:00
#include <vector>
#include <cstdint>
#include <algorithm>
2024-08-10 19:12:21 -04:00
2024-08-12 17:20:14 -04:00
#include "scene.h"
2024-08-12 21:40:28 -04:00
#include "Uniforms.h"
#include "InitShader.h" //Functions for loading shaders from text files
#include "DebugCallback.h"
#include "PlatformUtils.h"
2024-08-12 21:40:28 -04:00
2024-08-13 16:25:56 -04:00
namespace {
2024-08-12 21:40:28 -04:00
2024-08-13 16:25:56 -04:00
const std::string kVertexShaderPath = "shaders/vertex.glsl";
const std::string kTessellationCtrlPath = "shaders/tessellation_ctrl.glsl";
const std::string kTessellationEvalPath = "shaders/tessellation_eval.glsl";
2024-08-13 16:25:56 -04:00
const std::string kFragmentShaderPath = "shaders/fragment.glsl";
2024-08-12 21:40:28 -04:00
const std::string kQuadVertexPath = "shaders/quad_vertex.glsl";
const std::string kQuadFragmentPath = "shaders/quad_fragment.glsl";
2024-08-15 00:56:29 -04:00
const std::string kHGTPath = "C:/Users/jmchr/Downloads/N02E016.SRTMGL1.hgt/N02E016.hgt";
GLuint tex_id = -1;
2024-08-13 16:25:56 -04:00
} // namespace
2024-08-12 21:40:28 -04:00
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.01f),
far_z_(100.0f),
2024-08-15 00:56:29 -04:00
fov_(glm::pi<float>() / 8.0f)
{}
2024-08-12 21:40:28 -04:00
2024-08-13 16:25:56 -04:00
Scene::~Scene() {
glDeleteProgram(shader_program_);
glDeleteVertexArrays(1, &vao_);
fbo_.~FBO();
post_fbo_.~FBO();
2024-08-13 16:25:56 -04:00
}
void Scene::Init() {
glewInit();
// GL_DEPTH_TEST causes artifacts in meshes made up of multiple triangles
// disabling for now
//glEnable(GL_DEPTH_TEST);
2024-08-13 16:25:56 -04:00
glEnable(GL_CULL_FACE);
fbo_.Init(window_width, window_height);
post_fbo_.Init(window_width, window_height);
2024-08-15 00:56:29 -04:00
std::vector<int16_t> heightData = LoadHGT(kHGTPath, 3601, 3601);
tex_id = CreateHeightmapTexture(heightData, 3601, 3601);
2024-08-13 16:25:56 -04:00
InitBuffers();
ReloadShader();
InitQuadBuffers();
quad_shader_program_ = InitShader(kQuadVertexPath.c_str(), kQuadFragmentPath.c_str());
2024-08-13 16:25:56 -04:00
UpdateCamera();
Uniforms::Init();
}
// Currently creates a test triangle and initializes its buffers
2024-08-13 16:25:56 -04:00
void Scene::InitBuffers() {
GLuint patch_vbo;
float patchVertices[] = {
2024-08-15 00:56:29 -04:00
// 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
2024-08-13 16:25:56 -04:00
};
glGenVertexArrays(1, &vao_);
glGenBuffers(1, &patch_vbo);
2024-08-13 16:25:56 -04:00
glBindVertexArray(vao_);
glBindBuffer(GL_ARRAY_BUFFER, patch_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(patchVertices), &patchVertices, GL_STATIC_DRAW);
// Position attribute
2024-08-13 16:25:56 -04:00
glEnableVertexAttribArray(0);
2024-08-15 00:56:29 -04:00
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);
2024-08-13 16:25:56 -04:00
}
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
2024-08-13 16:25:56 -04:00
void Scene::ReloadShader() {
GLuint new_shader = InitShader(kVertexShaderPath.c_str(), kTessellationCtrlPath.c_str(), kTessellationEvalPath.c_str(), kFragmentShaderPath.c_str());
2024-08-13 16:25:56 -04:00
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;
2024-08-12 21:40:28 -04:00
}
}
void Scene::Display(GLFWwindow* window) {
2024-08-13 16:25:56 -04:00
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2024-08-12 21:40:28 -04:00
fbo_.Bind();
glUseProgram(shader_program_);
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
2024-08-15 00:56:29 -04:00
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));
2024-08-13 16:25:56 -04:00
Uniforms::SceneData.PV = projection_matrix_ * view_matrix_; // Projection-View matrix
2024-08-12 21:40:28 -04:00
Uniforms::BufferSceneData();
glm::mat4 model_matrix = glm::rotate(angle_, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::scale(glm::vec3(scale_));
2024-08-13 16:25:56 -04:00
glUniformMatrix4fv(Uniforms::UniformLocs::M, 1, false, glm::value_ptr(model_matrix));
2024-08-12 21:40:28 -04:00
2024-08-15 00:56:29 -04:00
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex_id);
GLint heightTextureLoc = glGetUniformLocation(shader_program_, "heightTexture");
glUniform1i(heightTextureLoc, 0);
2024-08-13 16:25:56 -04:00
glBindVertexArray(vao_);
glPatchParameteri(GL_PATCH_VERTICES, 4);
glDrawArrays(GL_PATCHES, 0, 4);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
2024-08-12 21:40:28 -04:00
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);
2024-08-13 16:25:56 -04:00
DrawGui(window);
2024-08-12 21:40:28 -04:00
glfwSwapBuffers(window);
}
2024-08-10 19:12:21 -04:00
2024-08-13 16:25:56 -04:00
void Scene::DrawGui(GLFWwindow* window) {
2024-08-11 21:20:17 -04:00
// 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
2024-08-13 16:25:56 -04:00
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) {
2024-08-11 21:20:17 -04:00
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
}
2024-08-13 16:25:56 -04:00
if (ImGui::BeginMenuBar()) {
if (ImGui::BeginMenu("Options")) {
2024-08-11 21:20:17 -04:00
ImGui::Text("Nothing Here Yet, Check Back Later!");
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
ImGui::End();
// Draw Gui
2024-08-11 21:20:17 -04:00
ImGui::Begin("Terrain Controls");
2024-08-13 16:25:56 -04:00
if (ImGui::Button("Quit")) {
2024-08-11 21:20:17 -04:00
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();
2024-08-11 21:20:17 -04:00
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
2024-08-10 19:12:21 -04:00
}
2024-08-12 21:40:28 -04:00
void Scene::Idle() {
2024-08-13 16:25:56 -04:00
2024-08-10 19:12:21 -04:00
}
2024-08-15 00:56:29 -04:00
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;
}
2024-08-13 16:25:56 -04:00
void Scene::UpdateCamera() {
projection_matrix_ = glm::perspective(fov_, aspect_, near_z_, far_z_);
2024-08-15 00:56:29 -04:00
}
// HGT files are 16bit integers whereas OpenGL prefers floating point values for color data
float Scene::NormalizeHeight(int16_t value, int16_t minVal, int16_t maxVal) {
return static_cast<float>(value - minVal) / (maxVal - minVal);
}
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::cout << "Min Val: " << minVal << "\n";
std::cout << "Max Val: " << maxVal << std::endl;
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));
//floatData.push_back((static_cast<float>(d) / 32767.0f + 1.0f) / 2.0f);
}
minVal = *std::min_element(data.begin(), data.end());
maxVal = *std::max_element(data.begin(), data.end());
std::cout << "Min Val: " << minVal << "\n";
std::cout << "Max Val: " << maxVal << std::endl;
float minFloatVal = *std::min_element(floatData.begin(), floatData.end());
float maxFloatVal = *std::max_element(floatData.begin(), floatData.end());
std::cout << "Min Float Val: " << minFloatVal << "\n";
std::cout << "Max Float Val: " << maxFloatVal << std::endl;
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;
}