From 765cd026b11b0bea15062cf99eb5c675ec309479 Mon Sep 17 00:00:00 2001 From: Skarn Date: Tue, 20 Oct 2020 13:02:32 +0300 Subject: [PATCH] implement test minimap rendering. ugly as fuck, WIP --- src/noggit/MapTile.cpp | 6 +- src/noggit/MapView.cpp | 11 + src/noggit/TextureManager.cpp | 5 + src/noggit/TextureManager.h | 4 +- src/noggit/World.cpp | 375 +++++++++++++++++++++++++++++++++- src/noggit/World.h | 17 +- 6 files changed, 412 insertions(+), 6 deletions(-) diff --git a/src/noggit/MapTile.cpp b/src/noggit/MapTile.cpp index a229ae10..9ecefecb 100644 --- a/src/noggit/MapTile.cpp +++ b/src/noggit/MapTile.cpp @@ -24,6 +24,7 @@ #include #include #include +#include MapTile::MapTile( int pX , int pZ @@ -321,12 +322,15 @@ bool MapTile::isTile(int pX, int pZ) float MapTile::getMaxHeight() { - float maxHeight = -99999.0f; + float maxHeight = std::numeric_limits::min(); + for (int nextChunk = 0; nextChunk < 256; ++nextChunk) { maxHeight = std::max(mChunks[nextChunk / 16][nextChunk % 16]->vmax.y, maxHeight); } + return maxHeight; + } void MapTile::convert_alphamap(bool to_big_alpha) diff --git a/src/noggit/MapView.cpp b/src/noggit/MapView.cpp index 63d3d164..29d6d6ef 100644 --- a/src/noggit/MapView.cpp +++ b/src/noggit/MapView.cpp @@ -62,6 +62,8 @@ #include #include #include +#include +#include static const float XSENS = 15.0f; @@ -1529,6 +1531,15 @@ void MapView::initializeGL() void MapView::paintGL() { opengl::context::scoped_setter const _ (::gl, context()); + + if (Saving) + { + // std::this_thread::sleep_for(std::chrono::seconds {10}); + _world->saveMinimap(512, 512, tile_index (_camera.position)); + Saving = false; + return; + } + const qreal now(_startup_time.elapsed() / 1000.0); _last_frame_durations.emplace_back (now - _last_update); diff --git a/src/noggit/TextureManager.cpp b/src/noggit/TextureManager.cpp index 524b9dc9..47d4c5c3 100644 --- a/src/noggit/TextureManager.cpp +++ b/src/noggit/TextureManager.cpp @@ -418,6 +418,11 @@ namespace noggit return &(_cache[curEntry] = std::move(result)); } + + BLPRenderer::~BLPRenderer() + { + opengl::context::scoped_setter const context_set (::gl, &_context); + } } scoped_blp_texture_reference::scoped_blp_texture_reference (std::string const& filename) diff --git a/src/noggit/TextureManager.h b/src/noggit/TextureManager.h index c7df2af9..6f8f2d6c 100644 --- a/src/noggit/TextureManager.h +++ b/src/noggit/TextureManager.h @@ -38,7 +38,7 @@ struct blp_texture : public opengl::texture, AsyncObject virtual async_priority loading_priority() const { - return async_priority::low; + return async_priority::high; } private: @@ -115,6 +115,8 @@ namespace noggit return instance; } + ~BLPRenderer(); + QPixmap* render_blp_to_pixmap ( std::string const& blp_filename, int width = -1, int height = -1); }; diff --git a/src/noggit/World.cpp b/src/noggit/World.cpp index 99c4cd30..b71795fb 100644 --- a/src/noggit/World.cpp +++ b/src/noggit/World.cpp @@ -3,6 +3,7 @@ #include #include +#include #include // brush #include #include @@ -503,7 +504,6 @@ void World::rotate_selected_models(math::degrees rx, math::degrees ry, math::deg math::vector_3d rot_result = math::matrix_4x4(math::matrix_4x4::rotation_xyz, {rx, ry, rz}) * diff_pos; pos += rot_result - diff_pos; - dir += dir_change; } else { @@ -1758,9 +1758,349 @@ void World::convert_alphamap(bool to_big_alpha) mapIndex.save(); } -void World::saveMap (int, int) +void World::drawMinimap ( MapTile *tile + , math::matrix_4x4 const& model_view + , math::matrix_4x4 const& projection + , math::vector_3d const& camera_pos +) { - throw std::runtime_error("minimap saving not implemented"); + if (!_display_initialized) + { + initDisplay(); + _display_initialized = true; + } + + math::matrix_4x4 const mvp(model_view * projection); + math::frustum const frustum(mvp); + + if (!_m2_program_mini) + { + _m2_program_mini.reset + (new opengl::program + {{GL_VERTEX_SHADER, opengl::shader::src_from_qrc("m2_vs")}, + {GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("m2_fs")} + } + ); + } + if (!_m2_instanced_program_mini) + { + _m2_instanced_program_mini.reset + (new opengl::program + {{GL_VERTEX_SHADER, opengl::shader::src_from_qrc("m2_vs", {"instanced"})}, + {GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("m2_fs")} + } + ); + } + + if (!_mcnk_program_mini) + { + _mcnk_program_mini.reset + (new opengl::program + {{GL_VERTEX_SHADER, opengl::shader::src_from_qrc("terrain_vs")}, + {GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("terrain_fs")} + } + ); + } + + if (!_liquid_render_mini) + { + _liquid_render_mini.emplace(); + } + if (!_wmo_program_mini) + { + _wmo_program_mini.reset + (new opengl::program + {{GL_VERTEX_SHADER, opengl::shader::src_from_qrc("wmo_vs")}, + {GL_FRAGMENT_SHADER, opengl::shader::src_from_qrc("wmo_fs")} + } + ); + } + + int daytime = static_cast(time) % 2880; + + skies->update_sky_colors(camera_pos, daytime); + outdoorLightStats = ol->getLightStats(daytime); + + math::vector_3d light_dir = outdoorLightStats.dayDir; + light_dir = {-light_dir.y, -light_dir.z, -light_dir.x}; + // todo: figure out why I need to use a different light vector for the terrain + math::vector_3d terrain_light_dir = {-light_dir.z, light_dir.y, -light_dir.x}; + + math::vector_3d diffuse_color(skies->color_set[LIGHT_GLOBAL_DIFFUSE] * outdoorLightStats.dayIntensity); + math::vector_3d ambient_color(skies->color_set[LIGHT_GLOBAL_AMBIENT] * outdoorLightStats.ambientIntensity); + + culldistance = _view_distance; + + gl.enable(GL_DEPTH_TEST); + gl.depthFunc(GL_LEQUAL); // less z-fighting artifacts this way, I think + gl.enable(GL_BLEND); + gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // draw terrain + { + opengl::scoped::use_program mcnk_shader{*_mcnk_program_mini.get()}; + + mcnk_shader.uniform("model_view", model_view); + mcnk_shader.uniform("projection", projection); + + mcnk_shader.uniform("draw_lines", 0); + mcnk_shader.uniform("draw_hole_lines", 0); + mcnk_shader.uniform("draw_areaid_overlay", 0); + mcnk_shader.uniform("draw_terrain_height_contour", 0); + + mcnk_shader.uniform("draw_impassible_flag", 0); + + mcnk_shader.uniform("draw_wireframe", 0); + mcnk_shader.uniform("wireframe_type", _settings->value("wireframe/type", 0).toInt()); + mcnk_shader.uniform("wireframe_radius", _settings->value("wireframe/radius", 1.5f).toFloat()); + mcnk_shader.uniform("wireframe_width", _settings->value("wireframe/width", 1.f).toFloat()); + + // !\ todo store the color somewhere ? + QColor c = _settings->value("wireframe/color").value(); + math::vector_4d wireframe_color(c.redF(), c.greenF(), c.blueF(), c.alphaF()); + mcnk_shader.uniform("wireframe_color", wireframe_color); + + mcnk_shader.uniform("draw_fog", 0); + mcnk_shader.uniform("fog_color", math::vector_4d(skies->color_set[FOG_COLOR], 1)); + // !\ todo use light dbcs values + mcnk_shader.uniform("fog_end", fogdistance); + mcnk_shader.uniform("fog_start", 0.5f); + mcnk_shader.uniform("camera", camera_pos); + + mcnk_shader.uniform("light_dir", terrain_light_dir); + mcnk_shader.uniform("diffuse_color", diffuse_color); + mcnk_shader.uniform("ambient_color", ambient_color); + + mcnk_shader.uniform("draw_cursor_circle", 0); + + mcnk_shader.uniform("alphamap", 0); + mcnk_shader.uniform("tex0", 1); + mcnk_shader.uniform("tex1", 2); + mcnk_shader.uniform("tex2", 3); + mcnk_shader.uniform("tex3", 4); + mcnk_shader.uniform("shadow_map", 5); + + mcnk_shader.uniform("tex_anim_0", math::vector_2d()); + mcnk_shader.uniform("tex_anim_1", math::vector_2d()); + mcnk_shader.uniform("tex_anim_2", math::vector_2d()); + mcnk_shader.uniform("tex_anim_3", math::vector_2d()); + + std::map area_id_colors; + + tile->draw(frustum, mcnk_shader, detailtexcoords, culldistance, camera_pos, true, false, + false, false, false, area_id_colors, animtime, + display_mode::in_2D + ); + + tile_index m_tile = tile_index (camera_pos); + m_tile.z -= 1; + + bool unload = !mapIndex.tileLoaded(m_tile) && !mapIndex.tileAwaitingLoading(m_tile); + MapTile* mTile = mapIndex.loadTile(m_tile); + + if (mTile) + { + mTile->wait_until_loaded(); + + mTile->draw(frustum, mcnk_shader, detailtexcoords, culldistance, camera_pos, true, false, + false, false, false, area_id_colors, animtime, + display_mode::in_2D + ); + } + + gl.bindVertexArray(0); + gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + // M2s / models + { + + if (need_model_updates) + { + update_models_by_filename(); + } + + std::unordered_map model_boxes_to_draw; + std::unordered_map model_with_particles; + + { + opengl::scoped::use_program m2_shader{*_m2_instanced_program_mini.get()}; + + m2_shader.uniform("model_view", model_view); + m2_shader.uniform("projection", projection); + m2_shader.uniform("tex1", 0); + m2_shader.uniform("tex2", 1); + + m2_shader.uniform("draw_fog", 0); + + m2_shader.uniform("light_dir", light_dir); + m2_shader.uniform("diffuse_color", diffuse_color); + m2_shader.uniform("ambient_color", ambient_color); + + for (auto &it : _models_by_filename) + { + it.second[0]->model->wait_until_loaded(); + it.second[0]->model->draw(model_view, it.second, m2_shader, frustum, culldistance, camera_pos, false, + animtime, false, false, model_with_particles, + model_boxes_to_draw, display_mode::in_2D + ); + } + + } + } + + // Setup liquid lighting + { + opengl::scoped::use_program water_shader{_liquid_render_mini->shader_program()}; + water_shader.uniform("animtime", static_cast(animtime) / 2880.f); + + water_shader.uniform("model_view", model_view); + water_shader.uniform("projection", projection); + + math::vector_4d ocean_color_light(skies->color_set[OCEAN_COLOR_LIGHT], skies->ocean_shallow_alpha()); + math::vector_4d ocean_color_dark(skies->color_set[OCEAN_COLOR_DARK], skies->ocean_deep_alpha()); + math::vector_4d river_color_light(skies->color_set[RIVER_COLOR_LIGHT], skies->river_shallow_alpha()); + math::vector_4d river_color_dark(skies->color_set[RIVER_COLOR_DARK], skies->river_deep_alpha()); + + water_shader.uniform("ocean_color_light", ocean_color_light); + water_shader.uniform("ocean_color_dark", ocean_color_dark); + water_shader.uniform("river_color_light", river_color_light); + water_shader.uniform("river_color_dark", river_color_dark); + water_shader.uniform("use_transform", 1); + + } + + // WMOs / map objects + { + opengl::scoped::use_program wmo_program{*_wmo_program_mini.get()}; + + wmo_program.uniform("model_view", model_view); + wmo_program.uniform("projection", projection); + wmo_program.uniform("tex1", 0); + wmo_program.uniform("tex2", 1); + + wmo_program.uniform("draw_fog", 0); + + wmo_program.uniform("exterior_light_dir", light_dir); + wmo_program.uniform("exterior_diffuse_color", diffuse_color); + wmo_program.uniform("exterior_ambient_color", ambient_color); + + _model_instance_storage.for_each_wmo_instance([&](WMOInstance &wmo) + { + wmo.wmo->wait_until_loaded(); + wmo.draw(wmo_program, model_view, projection, frustum, + culldistance, camera_pos, false, false, + false, _liquid_render.get(), current_selection(), + animtime, skies->hasSkies(), display_mode::in_2D + ); + + }); + + gl.enable(GL_BLEND); + gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl.enable(GL_CULL_FACE); + } + + // Liquids + { + _liquid_render_mini->force_texture_update(); + + // draw the water on both sides + opengl::scoped::bool_setter const cull; + + opengl::scoped::use_program water_shader{_liquid_render_mini->shader_program()}; + + water_shader.uniform("use_transform", 0); + + tile->drawWater(frustum, culldistance, camera_pos, true, _liquid_render.get(), water_shader, animtime, + -1, display_mode::in_2D + ); + + gl.bindVertexArray(0); + gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } +} + + +void World::saveMinimap (int width, int height) +{ + for (size_t z = 0; z < 64; z++) + { + for (size_t x = 0; x < 64; x++) + { + tile_index tile(x, z); + + bool unload = !mapIndex.tileLoaded(tile) && !mapIndex.tileAwaitingLoading(tile); + MapTile* mTile = mapIndex.loadTile(tile); + + if (mTile) + { + mTile->wait_until_loaded(); + + //drawMinimap(mTile); + + if (unload) + { + mapIndex.unloadTile(tile); + } + } + } + } + +} + +void World::saveMinimap(int width, int height, tile_index const& tile_idx) +{ + // Setup framebuffer + QOpenGLFramebufferObjectFormat fmt; + fmt.setSamples(1); + fmt.setInternalTextureFormat(GL_RGBA8); + fmt.setAttachment(QOpenGLFramebufferObject::Depth); + + QOpenGLFramebufferObject pixel_buffer(width, height, fmt); + pixel_buffer.bind(); + + gl.viewport(0, 0, width, height); + gl.clearColor(.0f, .0f, .0f, 1.f); + gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Load tile + bool unload = !mapIndex.tileLoaded(tile_idx) && !mapIndex.tileAwaitingLoading(tile_idx); + MapTile* mTile = mapIndex.loadTile(tile_idx); + + if (mTile) + { + mTile->wait_until_loaded(); + wait_for_all_tile_updates(); + + float max_height = getMaxTileHeight(tile_idx); + + // setup view matrices + auto projection = math::ortho( + -TILESIZE / 2.0f, + TILESIZE / 2.0f, + -TILESIZE / 2.0f, + TILESIZE / 2.0f, + 5.f, + 10000.0f + ); + + auto look_at = math::look_at(math::vector_3d(TILESIZE * tile_idx.x + TILESIZE / 2.0f, max_height + 10.0f, TILESIZE * tile_idx.z + TILESIZE / 2.0f), + math::vector_3d(TILESIZE * tile_idx.x + TILESIZE / 2.0f, max_height + 9.0f, TILESIZE * tile_idx.z + TILESIZE / 2.0 - 0.005f), + math::vector_3d(0.f,1.f, 0.f)); + + drawMinimap(mTile, look_at.transposed(), projection.transposed(), math::vector_3d(TILESIZE * tile_idx.x + TILESIZE / 2.0f, max_height + 15.0f, TILESIZE * tile_idx.z + TILESIZE / 2.0f)); + + QImage image = pixel_buffer.toImage(); + image.save("/Users/sshumakov/Desktop/test_minimap.png"); + + if (unload) + { + mapIndex.unloadTile(tile_idx); + } + } + + pixel_buffer.release(); } void World::deleteModelInstance(int pUniqueID) @@ -2344,3 +2684,32 @@ void World::range_add_to_selection(math::vector_3d const& pos, float radius, boo }); } + +float World::getMaxTileHeight(const tile_index& tile) +{ + + MapTile* m_tile = mapIndex.getTile(tile); + + float max_height = m_tile->getMaxHeight(); + + std::vector* uids = m_tile->get_uids(); + + for (uint32_t uid : *uids) + { + auto instance = _model_instance_storage.get_instance(uid); + + if (instance.get().which() == eEntry_WMO) + { + auto wmo = boost::get(instance.get()); + max_height = std::max(max_height, std::max(wmo->extents[0].y, wmo->extents[1].y)); + } + else + { + auto model = boost::get(instance.get()); + max_height = std::max(max_height, std::max(model->extents()[0].y, model->extents()[1].y)); + } + } + + + return max_height; +} diff --git a/src/noggit/World.h b/src/noggit/World.h index 067dd774..8e7422d4 100644 --- a/src/noggit/World.h +++ b/src/noggit/World.h @@ -260,7 +260,13 @@ public: void updateTilesModel(ModelInstance* m2, model_update type); void wait_for_all_tile_updates(); - void saveMap (int width, int height); + void saveMinimap (int width, int height); + void saveMinimap (int width, int height, tile_index const& tile_idx); + void drawMinimap ( MapTile *tile + , math::matrix_4x4 const& model_view + , math::matrix_4x4 const& projection + , math::vector_3d const& camera_pos + ); void deleteModelInstance(int pUniqueID); void deleteWMOInstance(int pUniqueID); @@ -312,6 +318,8 @@ public: void updateVertexCenter(); void clearVertexSelection(); + float getMaxTileHeight(const tile_index& tile); + math::vector_3d const& vertexCenter(); void recalc_norms (MapChunk*) const; @@ -348,9 +356,16 @@ private: std::unique_ptr _m2_box_program; std::unique_ptr _wmo_program; + // Minimap programs. Ugly, but those can't be shared between contexts, so we compile twice + std::unique_ptr _mcnk_program_mini; + std::unique_ptr _m2_program_mini; + std::unique_ptr _m2_instanced_program_mini; + std::unique_ptr _wmo_program_mini; + noggit::cursor_render _cursor_render; opengl::primitives::sphere _sphere_render; opengl::primitives::square _square_render; boost::optional _liquid_render = boost::none; + boost::optional _liquid_render_mini = boost::none; };