From cba45bdecfcbb8bf31ef026c027a407664671cd1 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Thu, 15 Aug 2024 00:56:29 -0400 Subject: [PATCH] Can import .hgt files. TODO refactor --- imgui.ini | 18 ++--- include/scene.h | 6 ++ shaders/fragment.glsl | 23 ++++--- shaders/tessellation_ctrl.glsl | 9 +++ shaders/tessellation_eval.glsl | 12 ++++ shaders/vertex.glsl | 7 ++ source/scene.cpp | 122 ++++++++++++++++++++++++++++----- 7 files changed, 163 insertions(+), 34 deletions(-) diff --git a/imgui.ini b/imgui.ini index 87f08b5..540ceee 100644 --- a/imgui.ini +++ b/imgui.ini @@ -4,10 +4,10 @@ Size=400,400 Collapsed=0 [Window][Terrain Controls] -Pos=0,19 -Size=1280,94 +Pos=929,19 +Size=351,701 Collapsed=0 -DockId=0x00000001,0 +DockId=0x00000002,0 [Window][DockSpaceViewport_11111111] Size=1280,720 @@ -23,13 +23,13 @@ Size=1280,720 Collapsed=0 [Window][Scene Window] -Pos=0,115 -Size=1280,605 +Pos=0,19 +Size=927,701 Collapsed=0 -DockId=0x00000002,0 +DockId=0x00000001,0 [Docking][Data] -DockSpace ID=0x6F42A598 Window=0xE80F322C Pos=0,19 Size=1280,701 Split=Y Selected=0x9F2D9299 - DockNode ID=0x00000001 Parent=0x6F42A598 SizeRef=1280,94 Selected=0xF69494A7 - DockNode ID=0x00000002 Parent=0x6F42A598 SizeRef=1280,605 CentralNode=1 Selected=0x9F2D9299 +DockSpace ID=0x6F42A598 Window=0xE80F322C Pos=0,19 Size=1280,701 Split=X Selected=0x9F2D9299 + DockNode ID=0x00000001 Parent=0x6F42A598 SizeRef=927,701 CentralNode=1 Selected=0x9F2D9299 + DockNode ID=0x00000002 Parent=0x6F42A598 SizeRef=351,701 Selected=0xF69494A7 diff --git a/include/scene.h b/include/scene.h index cf87509..fe83250 100644 --- a/include/scene.h +++ b/include/scene.h @@ -3,6 +3,8 @@ #include #include +#include +#include #include "fbo.h" @@ -16,6 +18,7 @@ class Scene { void DrawGui(GLFWwindow* window); void Idle(); void ReloadShader(); + std::vector LoadHGT(const std::string& filename, int width, int height); int window_width; int window_height; @@ -42,6 +45,9 @@ class Scene { glm::mat4 projection_matrix_; void UpdateCamera(); + int16_t Scene::SwapEndian(int16_t val); + float NormalizeHeight(int16_t value, int16_t minVal, int16_t maxVal); + GLuint CreateHeightmapTexture(std::vector data, int width, int height); }; #endif // SCENE_H_ \ No newline at end of file diff --git a/shaders/fragment.glsl b/shaders/fragment.glsl index 6717873..d094014 100644 --- a/shaders/fragment.glsl +++ b/shaders/fragment.glsl @@ -1,7 +1,7 @@ #version 430 precision highp float; -layout(binding = 0) uniform sampler2D diffuse_tex; +layout(binding = 0) uniform sampler2D heightTexture; layout(location = 1) uniform float time; layout(std140, binding = 0) uniform SceneUniforms @@ -26,15 +26,12 @@ layout(std140, binding = 2) uniform MaterialUniforms float shininess; //specular exponent }; -in VertexData -{ - vec2 tex_coord; - vec3 pw; //world-space vertex position - vec3 nw; //world-space normal vector -} inData; //block is named 'inData' - in vec3 frag_position; +in VertexData { + vec2 tex_coord; +} inData; + out vec4 frag_color; //the output color for this fragment void main(void) @@ -58,6 +55,14 @@ void main(void) // vec4 specular_term = atten*ks*Ls*pow(max(0.0, dot(rw, vw)), shininess); // fragcolor = ambient_term + diffuse_term + specular_term; - frag_color = vec4(frag_position, 1.0f); + + + float height = texture(heightTexture, inData.tex_coord).r; + + //frag_color = vec4(inData.tex_coord, 0.0f, 1.0f); + + // if (height > 0.8) frag_color = vec4(vec3(1.0f-height), 1.0f); + // else frag_color = vec4(vec3(height), 1.0f); + frag_color = vec4(height, height, height, 1.0); } diff --git a/shaders/tessellation_ctrl.glsl b/shaders/tessellation_ctrl.glsl index 5aca5ac..3021988 100644 --- a/shaders/tessellation_ctrl.glsl +++ b/shaders/tessellation_ctrl.glsl @@ -2,10 +2,19 @@ layout(vertices = 4) out; // Define the number of control points per patch (e.g., 4 for a quad) +in VertexData { + vec2 tex_coord; +} inData[]; + +out VertexData { + vec2 tex_coord; +} outData[]; + void main() { // Pass through control points to the tessellation evaluation shader gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; + outData[gl_InvocationID].tex_coord = inData[gl_InvocationID].tex_coord; // Set the tessellation levels (outer and inner) if (gl_InvocationID == 0) { diff --git a/shaders/tessellation_eval.glsl b/shaders/tessellation_eval.glsl index 44b0df9..3f7898d 100644 --- a/shaders/tessellation_eval.glsl +++ b/shaders/tessellation_eval.glsl @@ -10,8 +10,16 @@ layout(std140, binding = 0) uniform SceneUniforms vec4 eye_w; //world-space eye position }; +in VertexData { + vec2 tex_coord; +} inData[]; + out vec3 frag_position; // Ensure this matches the input in fragment.glsl +out VertexData { + vec2 tex_coord; +} outData; + void main() { // Interpolate the position using the barycentric coordinates from tessellation @@ -21,5 +29,9 @@ void main() frag_position = pos; + outData.tex_coord = mix(mix(inData[0].tex_coord, inData[1].tex_coord, gl_TessCoord.x), + mix(inData[3].tex_coord, inData[2].tex_coord, gl_TessCoord.x), + gl_TessCoord.y); + gl_Position = PV*M*vec4(pos, 1.0); } diff --git a/shaders/vertex.glsl b/shaders/vertex.glsl index 5c47f8a..577af1a 100644 --- a/shaders/vertex.glsl +++ b/shaders/vertex.glsl @@ -11,8 +11,15 @@ layout(location = 0) in vec3 pos_attrib; layout(location = 1) in vec2 tex_coord_attrib; layout(location = 2) in vec3 normal_attrib; +out VertexData +{ + vec2 tex_coord; +} outData; + void main() { gl_Position = vec4(pos_attrib, 1.0); + + outData.tex_coord = tex_coord_attrib; //send tex_coord to fragment shader } diff --git a/source/scene.cpp b/source/scene.cpp index 56412d3..88e824d 100644 --- a/source/scene.cpp +++ b/source/scene.cpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include "scene.h" #include "Uniforms.h" @@ -32,6 +35,10 @@ 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 = "C:/Users/jmchr/Downloads/N02E016.SRTMGL1.hgt/N02E016.hgt"; + +GLuint tex_id = -1; + } // namespace Scene::Scene(int width, int height) @@ -46,7 +53,7 @@ Scene::Scene(int width, int height) aspect_(static_cast(width) / static_cast(height)), near_z_(0.01f), far_z_(100.0f), - fov_(glm::pi() / 4.0f) + fov_(glm::pi() / 8.0f) {} Scene::~Scene() { @@ -68,6 +75,9 @@ void Scene::Init() { 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(); @@ -80,21 +90,13 @@ void Scene::Init() { // Currently creates a test triangle and initializes its buffers void Scene::InitBuffers() { GLuint patch_vbo; - // Quad vertices in 3D space - // float patchVertices[] = { - // -0.5f, 0.0f, -0.5f, // Bottom-left - // 0.5f, 0.0f, -0.5f, // Bottom-right - // 0.5f, 0.0f, 0.5f, // Top-right - // -0.5f, 0.0f, 0.5f // Top-left - - // }; float patchVertices[] = { - -0.5f, -0.5f, 0.0f, // Bottom-left - 0.5f, -0.5f, 0.0f, // Bottom-right - 0.5f, 0.5f, 0.0f, // Top-right - -0.5f, 0.5f, 0.0f // Top-left - + // 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_); @@ -106,7 +108,11 @@ void Scene::InitBuffers() { // Position attribute glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)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); } @@ -163,13 +169,19 @@ void Scene::Display(GLFWwindow* window) { //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - view_matrix_ = glm::lookAt(glm::vec3(0.0f, 2.0f, 3.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); + 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); @@ -272,6 +284,84 @@ 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_); +} + +// 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(value - minVal) / (maxVal - minVal); +} + +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::cout << "Min Val: " << minVal << "\n"; + std::cout << "Max Val: " << maxVal << std::endl; + + 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)); + //floatData.push_back((static_cast(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; } \ No newline at end of file