diff --git a/src/noggit/MapTile.h b/src/noggit/MapTile.h index 30a14346..bfce7db4 100755 --- a/src/noggit/MapTile.h +++ b/src/noggit/MapTile.h @@ -100,6 +100,7 @@ public: std::atomic changed; + bool _was_rendered_last_frame = false; bool intersect (math::ray const&, selection_result*); @@ -218,7 +219,6 @@ private: bool _load_textures; World* _world; - Noggit::Rendering::TileRender _renderer; Noggit::Rendering::FlightBoundsRender _fl_bounds_render; diff --git a/src/noggit/MapView.cpp b/src/noggit/MapView.cpp index c3efa8b4..ff4c2ceb 100755 --- a/src/noggit/MapView.cpp +++ b/src/noggit/MapView.cpp @@ -3459,7 +3459,8 @@ glm::mat4x4 MapView::model_view(bool use_debug_cam) const glm::mat4x4 MapView::projection() const { - float far_z = _settings->value("view_distance", 2000.f).toFloat() + 1.f; + // float far_z = _settings->value("view_distance", 2000.f).toFloat() + 1.f; + float far_z = _world->renderer()->_view_distance + 1.0f; if (_display_mode == display_mode::in_2D) { @@ -4011,7 +4012,7 @@ void MapView::mouseReleaseEvent (QMouseEvent* event) glm::vec2(std::max(_drag_start_pos.x(), drag_end_pos.x()), std::max(_drag_start_pos.y(), drag_end_pos.y())) }; // _world->select_objects_in_area(selection_box, !_mod_shift_down, model_view(), projection(), width(), height(), objectEditor->drag_selection_depth(), _camera.position); - _world->select_objects_in_area(selection_box, !_mod_shift_down, model_view(), projection(), width(), height(), 3000.0f, _camera.position); + _world->select_objects_in_area(selection_box, !_mod_shift_down, model_view(), projection(), width(), height(), 50000.0f, _camera.position); } else // Do normal selection when we just clicked { @@ -4063,7 +4064,10 @@ void MapView::save(save_mode mode) first_warning.setIcon(QMessageBox::Critical); first_warning.setWindowIcon(QIcon (":/icon")); first_warning.setWindowTitle("Some models couldn't be loaded"); - first_warning.setText("Error:\nSome models could not be loaded and saving will cause collision and culling issues, would you still like to save ?"); + first_warning.setText("Error:\nSome models could not be loaded and saving will cause collision and culling issues," + " this is most likely caused by missing or corrupted models." + "\nCheck the log file for the list of model errors and fix them." + "\nWould you still like to save ?"); // roles are swapped to force the user to pay attention and both are "accept" roles so that escape does nothing no = first_warning.addButton("No", QMessageBox::ButtonRole::AcceptRole); yes = first_warning.addButton("Yes", QMessageBox::ButtonRole::YesRole); @@ -4222,7 +4226,7 @@ void MapView::onSettingsSave() _world->renderer()->markTerrainParamsUniformBlockDirty(); - _world->renderer()->setViewDistance(_settings->value("view_distance", 2000.f).toFloat()); + _world->renderer()->_view_distance = _settings->value("view_distance", 2000.f).toFloat(); _world.get()->mapIndex.setLoadingRadius(_settings->value("loading_radius", 2).toInt()); _world.get()->mapIndex.setUnloadDistance(_settings->value("unload_dist", 5).toInt()); diff --git a/src/noggit/Misc.cpp b/src/noggit/Misc.cpp index fc59b4f4..8c021670 100755 --- a/src/noggit/Misc.cpp +++ b/src/noggit/Misc.cpp @@ -55,6 +55,12 @@ namespace misc clipSpacePos.x = (ndcX + 1.0f) * 0.5f * viewport_width; clipSpacePos.y = (1.0f - (ndcY + 1.0f) * 0.5f) * viewport_height; + // from MapView::normalized_device_coords + // x 2.0f * x / viewport_width - 1.0f + // y 1.0f - 2.0f * y / viewport_height + // z 0.0f + // w 1.0f + valid = true; return clipSpacePos; } diff --git a/src/noggit/Model.h b/src/noggit/Model.h index 23ca2bd9..61e4ec5c 100755 --- a/src/noggit/Model.h +++ b/src/noggit/Model.h @@ -11,13 +11,13 @@ #include #include #include +#include #include #include #include #include #include -#include -#include +#include class Bone; class Model; @@ -188,6 +188,8 @@ public: [[nodiscard]] Noggit::Rendering::ModelRender* renderer() { return &_renderer; } + uint32_t get_anim_lenght(int16_t anim_id) { return _animation_length[anim_id]; } + // =============================== // Toggles // =============================== @@ -276,5 +278,6 @@ private: Noggit::Rendering::ModelRender _renderer; bool _hidden = false; + }; diff --git a/src/noggit/ModelInstance.h b/src/noggit/ModelInstance.h index dcbe0771..6e2482f2 100755 --- a/src/noggit/ModelInstance.h +++ b/src/noggit/ModelInstance.h @@ -90,6 +90,8 @@ public: bool isInFrustum(math::frustum const& frustum); bool isInRenderDist(const float& cull_distance, const glm::vec3& camera, display_mode display); + bool extentsDirty() { return _need_recalc_extents || !model->finishedLoading(); }; + [[nodiscard]] virtual glm::vec3 const& get_pos() const { return pos; } diff --git a/src/noggit/SceneObject.hpp b/src/noggit/SceneObject.hpp index 9f5b3428..1787c1da 100755 --- a/src/noggit/SceneObject.hpp +++ b/src/noggit/SceneObject.hpp @@ -86,6 +86,9 @@ public: unsigned int uid; int frame; + // Note : First, need to check if the tile that contained it was rendered too + bool _rendered_last_frame = false; + protected: SceneObjectTypes _type; diff --git a/src/noggit/Selection.cpp b/src/noggit/Selection.cpp index 64de87ec..759853b9 100755 --- a/src/noggit/Selection.cpp +++ b/src/noggit/Selection.cpp @@ -317,8 +317,9 @@ void selection_group::select_group() if (_world->is_selected(instance)) continue; - _world->add_to_selection(obj.value(), true); + _world->add_to_selection(obj.value(), true, false); } + _world->update_selection_pivot(); _is_selected = true; } @@ -328,9 +329,9 @@ void selection_group::unselect_group() for (unsigned int obj_uid : _members_uid) { // don't need to check if it's not selected - _world->remove_from_selection(obj_uid, true); + _world->remove_from_selection(obj_uid, true, false); } - + _world->update_selection_pivot(); _is_selected = false; } diff --git a/src/noggit/Selection.h b/src/noggit/Selection.h index c13ca0b5..634de0e5 100755 --- a/src/noggit/Selection.h +++ b/src/noggit/Selection.h @@ -111,5 +111,5 @@ private: // bool _need_recalc_extents = false; }; -using selection_entry = std::pair; +using selection_entry = std::pair; // float = hit distance using selection_result = std::vector; \ No newline at end of file diff --git a/src/noggit/Tool.hpp b/src/noggit/Tool.hpp index f031da7f..92d81e42 100644 --- a/src/noggit/Tool.hpp +++ b/src/noggit/Tool.hpp @@ -35,6 +35,7 @@ namespace Noggit { display_mode displayMode = display_mode::in_3D; bool underMap = false; + bool camera_moved_since_last_draw = false; bool left_mouse = false; bool right_mouse = false; diff --git a/src/noggit/World.cpp b/src/noggit/World.cpp index 45571a3b..3c9c1717 100755 --- a/src/noggit/World.cpp +++ b/src/noggit/World.cpp @@ -558,7 +558,8 @@ void World::set_current_selection(selection_type entry) add_to_selection(entry); } -void World::add_to_selection(selection_type entry, bool skip_group) +// updating pivot is expensive, in mass selection situation, it should only be updated once after operation is done +void World::add_to_selection(selection_type entry, bool skip_group, bool update_pivot) { ZoneScoped; if (entry.index() == eEntry_Object) @@ -583,10 +584,12 @@ void World::add_to_selection(selection_type entry, bool skip_group) } } _current_selection.push_back(entry); - update_selection_pivot(); + + if (update_pivot) + update_selection_pivot(); } -void World::remove_from_selection(selection_type entry, bool skip_group) +void World::remove_from_selection(selection_type entry, bool skip_group, bool update_pivot) { ZoneScoped; std::vector::iterator position = std::find(_current_selection.begin(), _current_selection.end(), entry); @@ -613,11 +616,12 @@ void World::remove_from_selection(selection_type entry, bool skip_group) } _current_selection.erase(position); - update_selection_pivot(); + if (update_pivot) + update_selection_pivot(); } } -void World::remove_from_selection(std::uint32_t uid, bool skip_group) +void World::remove_from_selection(std::uint32_t uid, bool skip_group, bool update_pivot) { ZoneScoped; for (auto it = _current_selection.begin(); it != _current_selection.end(); ++it) @@ -645,8 +649,8 @@ void World::remove_from_selection(std::uint32_t uid, bool skip_group) } } } - - update_selection_pivot(); + if (update_pivot) + update_selection_pivot(); return; } @@ -2041,6 +2045,10 @@ void World::remove_models_if_needed(std::vector const& uids) { reset_selection(); } + else + { + update_selection_pivot(); + } /* if (uids.size()) { @@ -3030,14 +3038,15 @@ void World::range_add_to_selection(glm::vec3 const& pos, float radius, bool remo { if (remove) { - remove_from_selection(obj); + remove_from_selection(obj, false, false); } else { if (!is_selected(obj)) - add_to_selection(obj); + add_to_selection(obj, false, false); } } + update_selection_pivot(); } float World::getMaxTileHeight(const TileIndex& tile) @@ -3576,14 +3585,14 @@ void World::notifyTileRendererOnSelectedTextureChange() } void World::select_objects_in_area( - const std::array selection_box, + const std::array& selection_box, bool reset_selection, - glm::mat4x4 view, - glm::mat4x4 projection, + const glm::mat4x4& view, + const glm::mat4x4& projection, int viewport_width, int viewport_height, float user_depth, - glm::vec3 camera_position) + const glm::vec3& camera_position) { ZoneScoped; @@ -3594,6 +3603,15 @@ void World::select_objects_in_area( glm::mat4 VPmatrix = projection * view; + constexpr int max_position_raycast_processing = 10000; + constexpr int max_bounds_raycast_processing = 5000; // when selecting large amount of objects, avoid doing complex ray calculations to not freeze + constexpr float bounds_check_scale = 0.8f; // size of the bounding box to use when interesecting withs election rectangle + constexpr float obj_raycast_min_size = 30.0f; // screen size rectangle lenght in pixels + + int processed_obj_count = 0; + // int debug_count_obj_min_size = 0; + // int debug_count_obj_min_size_not = 0; + for (auto& map_object : _loaded_tiles_buffer) { MapTile* tile = map_object.second; @@ -3607,17 +3625,19 @@ void World::select_objects_in_area( { // tile not in screen, skip // frustum.intersects(tile_extents[1], tile_extents[0]) - if (tile->renderer()->isFrustumCulled()) + if (!tile->_was_rendered_last_frame) continue; + // check if tile combined extents are within selection rectangle + // note very useful because cases where a tile is fully rendered and not selected are very rare + // skip if no objects if (tile->getObjectInstances().empty()) continue; - // check if tile combined extents are within selection rectangle bool valid = false; auto screenBounds = misc::getAABBScreenBounds(tile->getCombinedExtents(), VPmatrix - , viewport_width, viewport_height, valid); + , viewport_width, viewport_height, valid, 1.0f); // this only works if all tile points are in screen space if (valid && !math::boxIntersects(screenBounds[0], screenBounds[1] @@ -3629,81 +3649,235 @@ void World::select_objects_in_area( for (auto& pair : tile->getObjectInstances()) { - auto objectType = pair.second[0]->which(); - if (objectType == eMODEL || objectType == eWMO) - { - for (auto& instance : pair.second) - { - // Old code to check position point instead of bound box - glm::vec4 screenPos = VPmatrix * glm::vec4(instance->pos, 1.0f); + [[unlikely]] + if (!pair.first->finishedLoading()) + continue; - // if screenPos.w < 0.0f, object is behind camera - // check object bounding radius instead to compare the object's size, if it clips with the camera. - if (screenPos.w < -instance->getBoundingRadius()) + auto objectType = pair.second[0]->which(); + + [[unlikely]] + if (!(objectType == eMODEL || objectType == eWMO)) + continue; + + for (auto& instance : pair.second) + { + // problem : M2s have additional sized based culling with >isInRenderDist() + // if (!instance->_rendered_last_frame) + // continue; + + // unsigned int uid = instance->uid; + // auto modelInstance = _model_instance_storage.get_instance(uid); + // [[unlikely]] + // if (!modelInstance || !modelInstance.value().index() == eEntry_Object) + // continue; + // auto obj = std::get(modelInstance.value()); + + bool do_selection = false; + + // Old code to check position point instead of bound box + { + glm::vec4 screenPos = VPmatrix * glm::vec4(instance->pos, 1.0f); + + // if screenPos.w < 0.0f, object is behind camera + // check object bounding radius instead to compare the object's size, if it clips with the camera. + if (screenPos.w < -instance->getBoundingRadius()) + continue; + + screenPos.x /= screenPos.w; + screenPos.y /= screenPos.w; + + // Convert normalized device coordinates (NDC) to screen coordinates + screenPos.x = (screenPos.x + 1.0f) * 0.5f * viewport_width; + screenPos.y = (1.0f - (screenPos.y + 1.0f) * 0.5f) * viewport_height; + + + float distance = glm::distance(camera_position, instance->pos); + if (distance > user_depth) + continue; + + // check if position(origin) point is within rectangle first because it is much cheaper + { + const glm::vec2 screenPos2D = glm::vec2(screenPos); + if (misc::pointInside(screenPos2D, selection_box)) + { + processed_obj_count++; + + // check if point is occluded by terrain + if (processed_obj_count < max_position_raycast_processing + && !is_point_occluded_by_terrain(instance->pos, view, VPmatrix, viewport_width, viewport_height, camera_position)) + { + do_selection = true; + } + // else + // bool debug_breakpoint = true; + } + } + } + + // if it's not, check again if bounding box is within selection + if (!do_selection && instance->_rendered_last_frame && (processed_obj_count < max_bounds_raycast_processing) ) + { + bool valid = false; + auto screenBounds = misc::getAABBScreenBounds(instance->getExtents(), VPmatrix + , viewport_width, viewport_height, valid, 0.75f); + + if (valid && math::boxIntersects(screenBounds[0], screenBounds[1] + , selection_box[0], selection_box[1])) + { + // do_selection = true; + } + else + continue; + + // Optimization : Only do raycast bounds checks for object that take enough screen space + if (!do_selection) + { + if (glm::distance(screenBounds[0], screenBounds[1]) < obj_raycast_min_size) + { + // debug_count_obj_min_size++; + continue; + } + else + { + // debug_count_obj_min_size_not++; + } + } + } + + // Occlusion test on object's corners (that are in selection box) + // uses ray casting, very expensive + if (!do_selection) + { + math::aabb obj_aabb(instance->getExtents()[0], instance->getExtents()[1]); + auto obj_aabb_corners = obj_aabb.all_corners(); + + // int required_num_unoccluded_corers = 6; // require 6 of 8 corners to not be occluded + bool object_occluded = true; + + for (const auto& corner : obj_aabb_corners) + { + // TODO : only need to do max top left and max top right in 2d instead of all corners? + + // only process points that are within selection rectangle + bool point_valid = false; + auto point_screen_pos = misc::projectPointToScreen(corner, VPmatrix, viewport_width, viewport_height, point_valid); + if (!point_valid) + continue; + if (!misc::pointInside(point_screen_pos, selection_box)) continue; - screenPos.x /= screenPos.w; - screenPos.y /= screenPos.w; - // Convert normalized device coordinates (NDC) to screen coordinates - screenPos.x = (screenPos.x + 1.0f) * 0.5f * viewport_width; - screenPos.y = (1.0f - (screenPos.y + 1.0f) * 0.5f) * viewport_height; - - - float depth = glm::distance(camera_position, instance->pos); - if (depth <= user_depth) + bool corner_occluded = is_point_occluded_by_terrain(corner, view, VPmatrix, viewport_width, viewport_height, camera_position); + + if (!corner_occluded) { - bool do_selection = false; + object_occluded = false; + break; + } + // object_occluded = true; + } + + do_selection = !object_occluded; + } - // check if position(origin) point is within rectangle first because it is much cheaper - const glm::vec2 screenPos2D = glm::vec2(screenPos); - if (misc::pointInside(screenPos2D, selection_box)) - do_selection = true; + if (!do_selection) + continue; - // if it's not, check again if bounding box is within selection - if (!do_selection) - { - bool valid = false; - auto screenBounds = misc::getAABBScreenBounds(instance->getExtents(), VPmatrix - , viewport_width, viewport_height, valid, 0.5f); - - if (valid && math::boxIntersects(screenBounds[0], screenBounds[1] - , selection_box[0], selection_box[1])) - do_selection = true; - } + auto& obj = instance; + auto which = obj->which(); + if (which == eWMO) + { + auto model_instance = static_cast(obj); - if (do_selection) - { - unsigned int uid = instance->uid; - auto modelInstance = _model_instance_storage.get_instance(uid); - if (modelInstance && modelInstance.value().index() == eEntry_Object) { - auto obj = std::get(modelInstance.value()); - auto which = std::get(modelInstance.value())->which(); - if (which == eWMO) - { - auto model_instance = static_cast(obj); + if (!is_selected(obj) && !model_instance->wmo->is_hidden()) + { + this->add_to_selection(obj, false, false); + } + } + else if (which == eMODEL) + { + auto model_instance = static_cast(obj); - if (!is_selected(obj) && !model_instance->wmo->is_hidden()) - { - this->add_to_selection(obj); - } - } - else if (which == eMODEL) - { - auto model_instance = static_cast(obj); - - if (!is_selected(obj) && !model_instance->model->is_hidden()) - { - this->add_to_selection(obj); - } - } - } - } + if (!is_selected(obj) && !model_instance->model->is_hidden()) + { + this->add_to_selection(obj, false, false); } } } } } + + this->update_selection_pivot(); +} + + +bool World::is_point_occluded_by_terrain(const glm::vec3& point, + const glm::mat4x4& view, + const glm::mat4& VPmatrix, + float viewport_width, + float viewport_height, + const glm::vec3& camera_position) +{ + /* + bool point_valid = false; + auto point_screen_pos = misc::projectPointToScreen(point, VPmatrix, viewport_width, viewport_height, point_valid); + + if (!point_valid) + { + return true; + }*/ + + math::ray ray(camera_position, point - camera_position); // 3d display mode only. + + // intersect only terrain with a ray to object's position + selection_result terrain_intersect_results + (intersect + (glm::transpose(view) + , ray + , true + , false + , true + , false + , false + , false + , false + ) + ); + + float distance = glm::distance(camera_position, point); + + // bool point_occluded = false; + for (const auto& terrain_hit : terrain_intersect_results) + { + // if terrain hit is further, skip + if (terrain_hit.first + 10.0f > distance) // add some leeway, skip hits that are too close, especially for the terrain at object's origin + continue; + + return true; + + auto const& hitChunkInfo = std::get(terrain_hit.second); + + /* + // check if terrain hit is higher than the object's corner in 2D screen space + bool point_valid = false; + auto terrain_hit_screen_pos = misc::projectPointToScreen(hitChunkInfo.position, VPmatrix, viewport_width, viewport_height, point_valid); + + if (!point_valid) + { + continue; + } + + // if any terrain intersection point is above point, it means point is occluded + if (point_screen_pos.y > terrain_hit_screen_pos.y) + { + return true; + } + */ + + } + + // no terrain intersection point above point + return false; } void World::add_object_group_from_selection() diff --git a/src/noggit/World.h b/src/noggit/World.h index d54a73f3..a1019839 100755 --- a/src/noggit/World.h +++ b/src/noggit/World.h @@ -136,9 +136,9 @@ public: // Unused in Red, models are now iterated by adt because of the occlusion check // std::unordered_map> get_models_by_filename() const& { return _models_by_filename; } void set_current_selection(selection_type entry); - void add_to_selection(selection_type entry, bool skip_group = false); - void remove_from_selection(selection_type entry, bool skip_group = false); - void remove_from_selection(std::uint32_t uid, bool skip_group = false); + void add_to_selection(selection_type entry, bool skip_group = false, bool update_pivot = true); + void remove_from_selection(selection_type entry, bool skip_group = false, bool update_pivot = true); + void remove_from_selection(std::uint32_t uid, bool skip_group = false, bool update_pivot = true); void reset_selection(); void delete_selected_models(); glm::vec3 get_ground_height(glm::vec3 pos); @@ -396,21 +396,25 @@ public: unsigned getNumRenderedObjects() const { return _n_rendered_objects; }; void select_objects_in_area( - const std::array selection_box, + const std::array& selection_box, bool reset_selection, - glm::mat4x4 view, - glm::mat4x4 projection, + const glm::mat4x4& view, + const glm::mat4x4& projection, int viewport_width, int viewport_height, float user_depth, - glm::vec3 camera_position + const glm::vec3& camera_position ); + void add_object_group_from_selection(); void remove_selection_group(selection_group* group); void clear_selection_groups(); +private: + bool is_point_occluded_by_terrain(const glm::vec3& point, const glm::mat4x4& view, const glm::mat4& VPmatrix, float viewport_width, float viewport_height, const glm::vec3& camera_position); + protected: // void update_models_by_filename(); diff --git a/src/noggit/rendering/WorldRender.cpp b/src/noggit/rendering/WorldRender.cpp index 531e51e5..13798b46 100755 --- a/src/noggit/rendering/WorldRender.cpp +++ b/src/noggit/rendering/WorldRender.cpp @@ -107,7 +107,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view unsigned tile_counter = 0; for (MapTile* tile : _world->mapIndex.loaded_tiles()) { - tile->recalcCombinedExtents(); + tile->_was_rendered_last_frame = false; if (minimap_render) { @@ -226,6 +226,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view , frustum , _cull_distance , _world->animtime + , _world->time , draw_skybox , _outdoor_light_stats ); @@ -304,6 +305,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view ); _world->_n_rendered_tiles++; + tile->_was_rendered_last_frame = true; } @@ -383,10 +385,20 @@ void WorldRender::draw (glm::mat4x4 const& model_view // TODO: subject to potential generalization for (auto& pair : tile->getObjectInstances()) { + if (!pair.first->finishedLoading()) + continue; + if (pair.second[0]->which() == eMODEL) { if (!draw_models && !(minimap_render && minimap_render_settings->use_filters)) + { + // can optimize this with a tile.rendered_m2s_lastframe or just check if models are enabled + for (auto& instance : pair.second) + { + instance->_rendered_last_frame = false; + } continue; + } auto& instances = models_to_draw[reinterpret_cast(pair.first)]; @@ -405,9 +417,12 @@ void WorldRender::draw (glm::mat4x4 const& model_view for (auto& instance : pair.second) { + instance->_rendered_last_frame = false; + // do not render twice the cross-referenced objects twice if (instance->frame == frame) { + instance->_rendered_last_frame = true; continue; } @@ -415,9 +430,20 @@ void WorldRender::draw (glm::mat4x4 const& model_view auto m2_instance = static_cast(instance); - if ((tile->renderer()->objectsFrustumCullTest() > 1 || m2_instance->isInFrustum(frustum)) && m2_instance->isInRenderDist(_cull_distance, camera_pos, display)) + // experimental : if camera and object haven't moved/changed since last frame, we don't need to do frustum culling again + if (!camera_moved && !m2_instance->extentsDirty()/* && not_moved*/) + { + if (m2_instance->_rendered_last_frame) + { + instances.push_back(m2_instance->transformMatrix()); + m2_instance->_rendered_last_frame = true; + continue; // skip visibility checks + } + } + if (m2_instance->isInRenderDist(_cull_distance, camera_pos, display) && (tile->renderer()->objectsFrustumCullTest() > 1 || m2_instance->isInFrustum(frustum))) { instances.push_back(m2_instance->transformMatrix()); + m2_instance->_rendered_last_frame = true; } } @@ -426,7 +452,13 @@ void WorldRender::draw (glm::mat4x4 const& model_view else if (pair.second[0]->which() == eWMO) { if (!draw_wmo) - continue; + { + for (auto& instance : pair.second) + { + instance->_rendered_last_frame = false; + } + continue; + } // memory allocation heuristic. all objects will pass if tile is entirely in frustum. // otherwise we only allocate for a half @@ -442,9 +474,12 @@ void WorldRender::draw (glm::mat4x4 const& model_view for (auto& instance : pair.second) { + instance->_rendered_last_frame = false; + // do not render twice the cross-referenced objects twice if (instance->frame == frame) { + instance->_rendered_last_frame = true; continue; } @@ -452,9 +487,20 @@ void WorldRender::draw (glm::mat4x4 const& model_view auto wmo_instance = static_cast(instance); + // experimental : if camera and object haven't moved/changed since last frame, we don't need to do frustum culling again + if (!camera_moved && !wmo_instance->extentsDirty()/* && not_moved*/) + { + if (wmo_instance->_rendered_last_frame) + { + wmos_to_draw.push_back(wmo_instance); + wmo_instance->_rendered_last_frame = true; + continue; // skip visibility checks + } + } if (tile->renderer()->objectsFrustumCullTest() > 1 || frustum.intersects(wmo_instance->getExtents()[1], wmo_instance->getExtents()[0])) { wmos_to_draw.push_back(wmo_instance); + wmo_instance->_rendered_last_frame = true; if (draw_wmo_doodads) { @@ -831,7 +877,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view - if (model->isInFrustum(frustum) && model->isInRenderDist(_cull_distance, camera_pos, display)) + if (model->isInRenderDist(_cull_distance, camera_pos, display) && model->isInFrustum(frustum)) { bool is_selected = false; /* diff --git a/src/noggit/rendering/WorldRender.hpp b/src/noggit/rendering/WorldRender.hpp index d47aa193..8438ebe2 100755 --- a/src/noggit/rendering/WorldRender.hpp +++ b/src/noggit/rendering/WorldRender.hpp @@ -86,7 +86,7 @@ namespace Noggit::Rendering [[nodiscard]] std::unique_ptr& skies() { return _skies; }; - void setViewDistance(float distance) { _view_distance = distance; }; + float _view_distance; private: @@ -109,7 +109,6 @@ namespace Noggit::Rendering World* _world; float _cull_distance; - float _view_distance; // shaders std::unique_ptr _mcnk_program;; @@ -134,6 +133,7 @@ namespace Noggit::Rendering Noggit::Rendering::Primitives::Sphere _sphere_render; Noggit::Rendering::Primitives::Square _square_render; Noggit::Rendering::Primitives::Line _line_render; + Noggit::Rendering::Primitives::WireBox _wirebox_render; // buffers OpenGL::Scoped::deferred_upload_buffers<8> _buffers; diff --git a/src/noggit/tools/ObjectTool.cpp b/src/noggit/tools/ObjectTool.cpp index f5028e22..700404cf 100644 --- a/src/noggit/tools/ObjectTool.cpp +++ b/src/noggit/tools/ObjectTool.cpp @@ -219,7 +219,7 @@ namespace Noggit { if (model_instance.instance_model()->file_key().filepath() == model_name) { - world->add_to_selection(&model_instance); + world->add_to_selection(&model_instance, false, false); } }); } @@ -229,16 +229,11 @@ namespace Noggit if (wmo_instance.instance_model()->file_key().filepath() == model_name) { // objects_to_select.push_back(wmo_instance.uid); - world->add_to_selection(&wmo_instance); + world->add_to_selection(&wmo_instance, false, false); } }); - // for (auto uid_it = objects_to_select.begin(); uid_it != objects_to_select.end(); uid_it++) - // { - // auto instance = world->getObjectInstance(*uid_it); - // // if (!world->is_selected(instance)) - // world->add_to_selection(instance); - // } + world->update_selection_pivot(); } }); @@ -782,7 +777,7 @@ namespace Noggit QPoint drag_end_pos = params.mouse_position; int drag_distance_threshold = QApplication::startDragDistance(); // QApplication::startDragDistance() is 10 /*_drag_start_pos != drag_end_pos*/ - if (!ImGuizmo::IsUsing() && (_drag_start_pos - drag_end_pos).manhattanLength() > drag_distance_threshold && _area_selection->isVisible()) + if (!ImGuizmo::IsUsing() && (_drag_start_pos - drag_end_pos).manhattanLength() > drag_distance_threshold && _area_selection->isVisible() ) { const std::array selection_box { @@ -790,7 +785,7 @@ namespace Noggit glm::vec2(std::max(_drag_start_pos.x(), drag_end_pos.x()), std::max(_drag_start_pos.y(), drag_end_pos.y())) }; // _world->select_objects_in_area(selection_box, !_mod_shift_down, model_view(), projection(), width(), height(), objectEditor->drag_selection_depth(), _camera.position); - mapView()->selectObjects(selection_box, 3000.0f); + mapView()->selectObjects(selection_box, 10000.0f); } else // Do normal selection when we just clicked, or selection box is too small { @@ -846,7 +841,7 @@ namespace Noggit } } - if (params.mod_shift_down || params.mod_ctrl_down) + if (_area_selection->isHidden() && (params.mod_shift_down || params.mod_ctrl_down) ) { mapView->doSelection(false, true); } diff --git a/src/noggit/world_model_instances_storage.cpp b/src/noggit/world_model_instances_storage.cpp index 73267ce3..d86ee618 100755 --- a/src/noggit/world_model_instances_storage.cpp +++ b/src/noggit/world_model_instances_storage.cpp @@ -197,7 +197,7 @@ namespace Noggit if (--_instance_count_per_uid.at(uid) == 0) { - _world->remove_from_selection(uid); + _world->remove_from_selection(uid, false, false); _instance_count_per_uid.erase(uid); _m2s.erase(uid);