diff --git a/OpenGL_Flightsim/OpenGL_Flightsim.vcxproj b/OpenGL_Flightsim/OpenGL_Flightsim.vcxproj index 1dd39b2..699023e 100644 --- a/OpenGL_Flightsim/OpenGL_Flightsim.vcxproj +++ b/OpenGL_Flightsim/OpenGL_Flightsim.vcxproj @@ -167,6 +167,7 @@ xcopy /y /i /s $(ProjectDir)..\lib\glew-2.2.0\bin\Release\x64\*.dll $(OutDir) + diff --git a/OpenGL_Flightsim/OpenGL_Flightsim.vcxproj.filters b/OpenGL_Flightsim/OpenGL_Flightsim.vcxproj.filters index 9c6df42..81b8bef 100644 --- a/OpenGL_Flightsim/OpenGL_Flightsim.vcxproj.filters +++ b/OpenGL_Flightsim/OpenGL_Flightsim.vcxproj.filters @@ -75,6 +75,9 @@ Source Files + + Source Files + diff --git a/OpenGL_Flightsim/src/app.cpp b/OpenGL_Flightsim/src/app.cpp index bc39cce..e44ed1a 100644 --- a/OpenGL_Flightsim/src/app.cpp +++ b/OpenGL_Flightsim/src/app.cpp @@ -112,7 +112,7 @@ void App::init() #if 1 #endif #if 1 - m_clipmap = new Clipmap(); + m_clipmap = new GeometryClipmap(); m_clipmap->visible = true; m_scene->add(m_clipmap); #endif diff --git a/OpenGL_Flightsim/src/app.h b/OpenGL_Flightsim/src/app.h index ece6078..dbd1ef6 100644 --- a/OpenGL_Flightsim/src/app.h +++ b/OpenGL_Flightsim/src/app.h @@ -60,7 +60,7 @@ class App // physics Airplane* m_flightmodel; phi::RigidBody* m_terrain; - Clipmap* m_clipmap; + GeometryClipmap* m_clipmap; void init_airplane(); void init_flightmodel(); diff --git a/OpenGL_Flightsim/src/collider.h b/OpenGL_Flightsim/src/collider.h index accf8f2..bc4bb57 100644 --- a/OpenGL_Flightsim/src/collider.h +++ b/OpenGL_Flightsim/src/collider.h @@ -35,9 +35,9 @@ struct Sphere : public Collider { }; struct Heightmap : public Collider { - const Clipmap* terrain = nullptr; + const GeometryClipmap* terrain = nullptr; - Heightmap(Clipmap* terrain_) : terrain(terrain_) {} + Heightmap(GeometryClipmap* terrain_) : terrain(terrain_) {} bool test(const phi::Transform* tf, const Collider* other, const phi::Transform* other_tf, phi::Collision* info) const override; diff --git a/OpenGL_Flightsim/src/terrain.cpp b/OpenGL_Flightsim/src/terrain.cpp new file mode 100644 index 0000000..1954d76 --- /dev/null +++ b/OpenGL_Flightsim/src/terrain.cpp @@ -0,0 +1,264 @@ +#include "terrain.h" + +TextureCLipmap::TextureCLipmap() {} + +Seam::Seam(int columns, float size) +{ + int rows = 1; + index_count = columns; + + std::vector vertices; + + int x; + for (x = 0; x < columns; x++) { + vertices.push_back({x * size, 0.0f, 0 * size}); + vertices.push_back({x * size + size / 2.0f, size, 0 * size}); + vertices.push_back({x * size + size, 0.0f, 0 * size}); + } + + vao.bind(); + vbo.buffer(&vertices[0], vertices.size() * sizeof(vertices[0])); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + vao.unbind(); +} + +void Seam::bind() { vao.bind(); } + +void Seam::unbind() { vao.unbind(); } + +void Seam::draw() +{ + bind(); + glDrawArrays(GL_TRIANGLES, 0, 3 * index_count); + unbind(); +} + +Block::Block(int width, int height, float segment_size) +{ + std::vector vertices; + std::vector indices; + + for (int y = 0; y <= width; y++) { + for (int x = 0; x <= height; x++) { + vertices.push_back({x * segment_size, 0.0f, y * segment_size}); + } + } + + for (int r = 0; r < width; r++) { + for (int c = 0; c < height + 1; c++) { + auto i0 = (r + 0) * (height + 1) + c; + indices.push_back(i0); + + auto i1 = (r + 1) * (height + 1) + c; + indices.push_back(i1); + } + indices.push_back(primitive_restart); // restart primitive + } + + index_count = indices.size(); + + assert(indices.size() > 0 && vertices.size() > 0); + + vao.bind(); + + vbo.buffer(&vertices[0], vertices.size() * sizeof(vertices[0])); + ebo.buffer(&indices[0], indices.size() * sizeof(indices[0])); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + + vao.unbind(); +} + +void Block::bind() { vao.bind(); } + +void Block::unbind() { vao.unbind(); } + +void Block::draw() +{ + bind(); + glDrawElements(GL_TRIANGLE_STRIP, index_count, GL_UNSIGNED_INT, 0); + unbind(); +} + +GeometryClipmap::GeometryClipmap(int levels_, int segments_) + : segment_size(2.0f), + levels(levels_), + segments(segments_), + terrain_size(MAX_TILE_SIZE / ZOOM_FACTOR), + shader("shaders/terrain"), + heightmap_image(PATH + "height.png"), + heightmap(heightmap_image, params), + normalmap(PATH + "normal.png", params), + terrain(PATH + "texture.png", params), + terrain_0(), + tile(segments, segments, segment_size), + col_fixup(2, segments, segment_size), + row_fixup(segments, 2, segment_size), + horizontal(2 * segments + 2, 1, segment_size), + vertical(1, 2 * segments + 2, segment_size), + center(2 * segments + 2, 2 * segments + 2, segment_size), + seam(2 * segments + 2, segment_size * 2) +{ + std::cout << "terrain_size = " << terrain_size << " m" << std::endl; + + auto fog = gfx::rgb(0x5e5e6e); + auto color = glm::vec4(fog, 1.0f); + + auto p = pixel_from_height(2561.0f); + + terrain.bind(); + terrain.set_parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + terrain.set_parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + terrain.set_parameter(GL_TEXTURE_BORDER_COLOR, glm::value_ptr(color)); + + heightmap.bind(); + heightmap.set_parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + heightmap.set_parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + heightmap.set_parameter(GL_TEXTURE_BORDER_COLOR, glm::value_ptr(glm::vec4(pixel_from_height(0.0f), 1.0f))); +} + +void GeometryClipmap::draw_self(gfx::RenderContext& context) +{ + if (context.shadow_pass) { + return; + } + auto camera_pos = context.camera->get_world_position(); + float height = camera_pos.y; + auto camera_pos_xy = glm::vec2(camera_pos.x, camera_pos.z); + + // TODO: select proper texture LOD + glm::vec2 origin = camera_pos_xy - glm::vec2(terrain_size / 2); + + shader.bind(); + shader.set_uniform("u_Heightmap", 2); + shader.set_uniform("u_Normalmap", 3); + shader.set_uniform("u_Texture_01", 4); + shader.set_uniform("u_FogColor", context.fog_color); + shader.set_uniform("u_View", context.camera->get_view_matrix()); + shader.set_uniform("u_CameraPos", context.camera->get_world_position()); + shader.set_uniform("u_Projection", context.camera->get_projection_matrix()); + shader.set_uniform("u_TerrainSize", terrain_size); + shader.set_uniform("u_Shadowmap", 5); + shader.set_uniform("u_LightSpaceMatrix", context.light_space_matrix); + + glEnable(GL_CULL_FACE); + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(primitive_restart); + + int min_level = 1; // depends on camera height + + for (int level = min_level; level <= levels; level++) { + const int rows = 5, columns = 5; + float scale = std::pow(2.0f, level); + float next_scale = std::pow(2.0f, level + 2); + float scaled_segment_size = segment_size * scale; + float tile_size = segments * scaled_segment_size; + glm::vec2 snapped = glm::floor(camera_pos_xy / next_scale) * next_scale; + auto base = calc_base(level, camera_pos_xy); + + shader.set_uniform("u_Scale", scale); + shader.set_uniform("u_SegmentSize", scaled_segment_size); + shader.set_uniform("u_Level", static_cast(level) / levels); + +#if 1 + // don't render lots of detail if we are very high up + if (tile_size * 5 < height * 2.5) { + min_level = level + 1; + continue; + } +#endif + + heightmap.bind(2); + normalmap.bind(3); + terrain.bind(4); + context.depth_map->bind(5); + +#if 1 + if (level == min_level) { + shader.set_uniform("u_Model", transform_matrix(base + glm::vec2(tile_size, tile_size), scale)); + center.draw(); + } else { // not at base level + auto prev_base = calc_base(level - 1, camera_pos_xy); + auto diff = glm::abs(base - prev_base); + + auto l_offset = glm::vec2(tile_size, tile_size); + if (diff.x == tile_size) { + l_offset.x += (2 * segments + 1) * scaled_segment_size; + } + shader.set_uniform("u_Model", transform_matrix(base + l_offset, scale)); + horizontal.draw(); + + auto v_offset = glm::vec2(tile_size, tile_size); + if (diff.y == tile_size) { + v_offset.y += (2 * segments + 1) * scaled_segment_size; + } + + shader.set_uniform("u_Model", transform_matrix(base + v_offset, scale)); + vertical.draw(); + } +#endif +#if 1 + glm::vec2 offset(0.0f); + for (int row = 0; row < rows; row++) { + offset.y = 0; + for (int column = 0; column < columns; column++) { + if (row == 0 || row == rows - 1 || column == 0 || column == columns - 1) { + auto tile_pos = base + offset; + shader.set_uniform("u_Model", transform_matrix(tile_pos, scale)); + + if ((column != 2) && (row != 2)) { + if (column == 0 && row == 0) // east + { + shader.set_uniform("u_Model", transform_matrix(tile_pos, scale)); + seam.draw(); + } else if (column == columns - 1 && row == rows - 1) // west + { + shader.set_uniform("u_Model", transform_matrix(tile_pos + glm::vec2(tile_size), scale, 180.0f)); + seam.draw(); + } else if (column == columns - 1 && row == 0) // south + { + shader.set_uniform("u_Model", transform_matrix(tile_pos + glm::vec2(0, tile_size), scale, 90.0f)); + seam.draw(); + } else if (column == 0 && row == rows - 1) // north + { + shader.set_uniform("u_Model", transform_matrix(tile_pos + glm::vec2(tile_size, 0), scale, -90.0f)); + seam.draw(); + } + + shader.set_uniform("u_Model", transform_matrix(tile_pos, scale)); + tile.draw(); + } else if (column == 2) { + col_fixup.draw(); + } else if (row == 2) { + row_fixup.draw(); + } + } + + if (column == 2) { + offset.y += 2 * scaled_segment_size; + } else { + offset.y += tile_size; + } + } + + if (row == 2) { + offset.x += 2 * scaled_segment_size; + } else { + offset.x += tile_size; + } + } +#endif + + heightmap.unbind(); + normalmap.unbind(); + terrain.unbind(); + } + + glDisable(GL_CULL_FACE); + + shader.unbind(); +} diff --git a/OpenGL_Flightsim/src/terrain.h b/OpenGL_Flightsim/src/terrain.h index 5dc15d3..5440fe8 100644 --- a/OpenGL_Flightsim/src/terrain.h +++ b/OpenGL_Flightsim/src/terrain.h @@ -12,8 +12,8 @@ const std::string PATH = "assets/textures/terrain/data/9/268/178/"; const int ZOOM_FACTOR = 1; #elif (DATA_SRC == 2) const std::string PATH = "assets/textures/terrain/data/10/536/356/"; -///const std::string PATH = "assets/textures/terrain/data/10/536/360/"; -//const std::string PATH = "assets/textures/terrain/debug/"; +/// const std::string PATH = "assets/textures/terrain/data/10/536/360/"; +// const std::string PATH = "assets/textures/terrain/debug/"; const int ZOOM_FACTOR = 2; #elif (DATA_SRC == 3) const std::string PATH = "assets/textures/terrain/data/11/1072/712/"; @@ -40,21 +40,19 @@ inline float height_from_pixel(const glm::vec3& rgb) return (pixel.r * 256.0f + pixel.g + pixel.b / 256.0f) - 32768.0f; } - inline glm::vec3 pixel_from_height(float height) { - // TODO - const float c = 32768.0f; - glm::vec3 pixel(0.0f); - - double decodedHeight = height + 32768; - int redDec = static_cast(decodedHeight / 256); - int greenDec = static_cast(decodedHeight) % 256; - //int blueDec = static_cast((decodedHeight * 256 - greenDec - redDec * 256) / 65536); - int blueDec = static_cast(((decodedHeight * 256) - (greenDec * 256) - (redDec * 65536)) / 256); + // TODO + const float c = 32768.0f; + glm::vec3 pixel(0.0f); + double decodedHeight = height + 32768; + int redDec = static_cast(decodedHeight / 256); + int greenDec = static_cast(decodedHeight) % 256; + // int blueDec = static_cast((decodedHeight * 256 - greenDec - redDec * 256) / 65536); + int blueDec = static_cast(((decodedHeight * 256) - (greenDec * 256) - (redDec * 65536)) / 256); - return pixel / 256.0f; + return pixel / 256.0f; } inline float scale(float input_val, float in_min, float in_max, float out_min, float out_max) @@ -77,39 +75,10 @@ struct Seam { gfx::gl::VertexArrayObject vao; size_t index_count; - Seam(int columns, float size) - { - int rows = 1; - index_count = columns; - - std::vector vertices; - - int x; - for (x = 0; x < columns; x++) { - vertices.push_back({x * size, 0.0f, 0 * size}); - vertices.push_back({x * size + size / 2.0f, size, 0 * size}); - vertices.push_back({x * size + size, 0.0f, 0 * size}); - } - - vao.bind(); - vbo.buffer(&vertices[0], vertices.size() * sizeof(vertices[0])); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - - vao.unbind(); - } - - void bind() { vao.bind(); } - - void unbind() { vao.unbind(); } - - void draw() - { - bind(); - glDrawArrays(GL_TRIANGLES, 0, 3 * index_count); - unbind(); - } + Seam(int columns, float size); + void bind(); + void unbind(); + void draw(); }; struct Block { @@ -118,248 +87,37 @@ struct Block { gfx::gl::VertexArrayObject vao; size_t index_count; - Block(int width, int height, float segment_size) - { - std::vector vertices; - std::vector indices; - - for (int y = 0; y <= width; y++) { - for (int x = 0; x <= height; x++) { - vertices.push_back({x * segment_size, 0.0f, y * segment_size}); - } - } - - for (int r = 0; r < width; r++) { - for (int c = 0; c < height + 1; c++) { - auto i0 = (r + 0) * (height + 1) + c; - indices.push_back(i0); - - auto i1 = (r + 1) * (height + 1) + c; - indices.push_back(i1); - } - indices.push_back(primitive_restart); // restart primitive - } - - index_count = indices.size(); - - assert(indices.size() > 0 && vertices.size() > 0); - - vao.bind(); - - vbo.buffer(&vertices[0], vertices.size() * sizeof(vertices[0])); - ebo.buffer(&indices[0], indices.size() * sizeof(indices[0])); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - - vao.unbind(); - } - - void bind() { vao.bind(); } + Block(int width, int height, float segment_size); + void bind(); + void unbind(); + void draw(); +}; - void unbind() { vao.unbind(); } +// A texture clipmap is a way of represinting a texture of arbitrary size +class TextureCLipmap +{ + public: + TextureCLipmap(); - void draw() - { - bind(); - glDrawElements(GL_TRIANGLE_STRIP, index_count, GL_UNSIGNED_INT, 0); - unbind(); - } + private: + gfx::gl::Texture m_texture; }; // Geometry Clipmap // https://developer.nvidia.com/gpugems/gpugems2/part-i-geometric-complexity/chapter-2-terrain-rendering-using-gpu-based-geometry // https://mikejsavage.co.uk/blog/geometry-clipmaps.html -class Clipmap : public gfx::Object3D +class GeometryClipmap : public gfx::Object3D { public: - Clipmap(int levels_ = 12, int segments_ = 16) - : segment_size(2.0f), - levels(levels_), - segments(segments_), - terrain_size(MAX_TILE_SIZE / ZOOM_FACTOR), - shader("shaders/terrain"), - heightmap_image(PATH + "height.png"), - heightmap(heightmap_image, params), - normalmap(PATH + "normal.png", params), - terrain(PATH + "texture.png", params), - terrain_0(), - tile(segments, segments, segment_size), - col_fixup(2, segments, segment_size), - row_fixup(segments, 2, segment_size), - horizontal(2 * segments + 2, 1, segment_size), - vertical(1, 2 * segments + 2, segment_size), - center(2 * segments + 2, 2 * segments + 2, segment_size), - seam(2 * segments + 2, segment_size * 2) - { - std::cout << "terrain_size = " << terrain_size << " m" << std::endl; - - auto fog = gfx::rgb(0x5e5e6e); - auto color = glm::vec4(fog, 1.0f); - - auto p = pixel_from_height(2561.0f); - - terrain.bind(); - terrain.set_parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - terrain.set_parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - terrain.set_parameter(GL_TEXTURE_BORDER_COLOR, glm::value_ptr(color)); - - heightmap.bind(); - heightmap.set_parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - heightmap.set_parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - heightmap.set_parameter(GL_TEXTURE_BORDER_COLOR, glm::value_ptr(glm::vec4(pixel_from_height(0.0f), 1.0f))); - - - - } + GeometryClipmap(int levels_ = 12, int segments_ = 16); + float get_terrain_height(const glm::vec2 pos) const { return sample_heightmap(heightmap_image, pos, terrain_size); } float get_terrain_size() const { return terrain_size; } - void draw_self(gfx::RenderContext& context) override - { - if (context.shadow_pass) { - return; - } - auto camera_pos = context.camera->get_world_position(); - float height = camera_pos.y; - auto camera_pos_xy = glm::vec2(camera_pos.x, camera_pos.z); - - // TODO: select proper texture LOD - glm::vec2 origin = camera_pos_xy - glm::vec2(terrain_size / 2); - - shader.bind(); - shader.set_uniform("u_Heightmap", 2); - shader.set_uniform("u_Normalmap", 3); - shader.set_uniform("u_Texture_01", 4); - shader.set_uniform("u_FogColor", context.fog_color); - shader.set_uniform("u_View", context.camera->get_view_matrix()); - shader.set_uniform("u_CameraPos", context.camera->get_world_position()); - shader.set_uniform("u_Projection", context.camera->get_projection_matrix()); - shader.set_uniform("u_TerrainSize", terrain_size); - shader.set_uniform("u_Shadowmap", 5); - shader.set_uniform("u_LightSpaceMatrix", context.light_space_matrix); - - - glEnable(GL_CULL_FACE); - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(primitive_restart); - - int min_level = 1; // depends on camera height - - for (int level = min_level; level <= levels; level++) { - const int rows = 5, columns = 5; - float scale = std::pow(2.0f, level); - float next_scale = std::pow(2.0f, level + 2); - float scaled_segment_size = segment_size * scale; - float tile_size = segments * scaled_segment_size; - glm::vec2 snapped = glm::floor(camera_pos_xy / next_scale) * next_scale; - auto base = calc_base(level, camera_pos_xy); - - shader.set_uniform("u_Scale", scale); - shader.set_uniform("u_SegmentSize", scaled_segment_size); - shader.set_uniform("u_Level", static_cast(level) / levels); - -#if 1 - // don't render lots of detail if we are very high up - if (tile_size * 5 < height * 2.5) { - min_level = level + 1; - continue; - } -#endif - - heightmap.bind(2); - normalmap.bind(3); - terrain.bind(4); - context.depth_map->bind(5); - -#if 1 - if (level == min_level) { - shader.set_uniform("u_Model", transform_matrix(base + glm::vec2(tile_size, tile_size), scale)); - center.draw(); - } else { // not at base level - auto prev_base = calc_base(level - 1, camera_pos_xy); - auto diff = glm::abs(base - prev_base); - - auto l_offset = glm::vec2(tile_size, tile_size); - if (diff.x == tile_size) { - l_offset.x += (2 * segments + 1) * scaled_segment_size; - } - shader.set_uniform("u_Model", transform_matrix(base + l_offset, scale)); - horizontal.draw(); - - auto v_offset = glm::vec2(tile_size, tile_size); - if (diff.y == tile_size) { - v_offset.y += (2 * segments + 1) * scaled_segment_size; - } - - shader.set_uniform("u_Model", transform_matrix(base + v_offset, scale)); - vertical.draw(); - } -#endif -#if 1 - glm::vec2 offset(0.0f); - for (int row = 0; row < rows; row++) { - offset.y = 0; - for (int column = 0; column < columns; column++) { - if (row == 0 || row == rows - 1 || column == 0 || column == columns - 1) { - auto tile_pos = base + offset; - shader.set_uniform("u_Model", transform_matrix(tile_pos, scale)); - - if ((column != 2) && (row != 2)) { - if (column == 0 && row == 0) // east - { - shader.set_uniform("u_Model", transform_matrix(tile_pos, scale)); - seam.draw(); - } else if (column == columns - 1 && row == rows - 1) // west - { - shader.set_uniform("u_Model", transform_matrix(tile_pos + glm::vec2(tile_size), scale, 180.0f)); - seam.draw(); - } else if (column == columns - 1 && row == 0) // south - { - shader.set_uniform("u_Model", transform_matrix(tile_pos + glm::vec2(0, tile_size), scale, 90.0f)); - seam.draw(); - } else if (column == 0 && row == rows - 1) // north - { - shader.set_uniform("u_Model", transform_matrix(tile_pos + glm::vec2(tile_size, 0), scale, -90.0f)); - seam.draw(); - } - - shader.set_uniform("u_Model", transform_matrix(tile_pos, scale)); - tile.draw(); - } else if (column == 2) { - col_fixup.draw(); - } else if (row == 2) { - row_fixup.draw(); - } - } - - if (column == 2) { - offset.y += 2 * scaled_segment_size; - } else { - offset.y += tile_size; - } - } - - if (row == 2) { - offset.x += 2 * scaled_segment_size; - } else { - offset.x += tile_size; - } - } -#endif - - heightmap.unbind(); - normalmap.unbind(); - terrain.unbind(); - } - - glDisable(GL_CULL_FACE); - - shader.unbind(); - } - + void draw_self(gfx::RenderContext& context) override; + private: const float segment_size; const int levels; @@ -370,12 +128,7 @@ class Clipmap : public gfx::Object3D gfx::Image heightmap_image; gfx::gl::Texture heightmap, normalmap, terrain, terrain_0; - Block tile; - Block center; - Block col_fixup; - Block row_fixup; - Block horizontal; - Block vertical; + Block tile, center, col_fixup, row_fixup, horizontal, vertical; Seam seam; glm::vec2 calc_base(int level, glm::vec2 camera_pos)