diff --git a/src/noggit/MapView.cpp b/src/noggit/MapView.cpp index ff4c2ceb..05796dcc 100755 --- a/src/noggit/MapView.cpp +++ b/src/noggit/MapView.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #ifdef USE_MYSQL_UID_STORAGE #include diff --git a/src/noggit/ModelInstance.cpp b/src/noggit/ModelInstance.cpp index d39efadb..99ccbc73 100755 --- a/src/noggit/ModelInstance.cpp +++ b/src/noggit/ModelInstance.cpp @@ -48,6 +48,7 @@ void ModelInstance::draw_box (glm::mat4x4 const& model_view if (is_current_selection) { + // draw collision box Noggit::Rendering::Primitives::WireBox::getInstance(_context).draw ( model_view , projection , transformMatrix() @@ -56,6 +57,7 @@ void ModelInstance::draw_box (glm::mat4x4 const& model_view , misc::transform_model_box_coords(model->header.collision_box_max) ); + // draw bounding box Noggit::Rendering::Primitives::WireBox::getInstance(_context).draw ( model_view , projection , transformMatrix() @@ -64,6 +66,7 @@ void ModelInstance::draw_box (glm::mat4x4 const& model_view , misc::transform_model_box_coords(model->header.bounding_box_max) ); + // draw extents Noggit::Rendering::Primitives::WireBox::getInstance(_context).draw ( model_view , projection , glm::mat4x4(1) diff --git a/src/noggit/Selection.cpp b/src/noggit/Selection.cpp index 759853b9..ef3e078c 100755 --- a/src/noggit/Selection.cpp +++ b/src/noggit/Selection.cpp @@ -6,7 +6,7 @@ #include -selected_chunk_type::selected_chunk_type(MapChunk* _chunk, std::tuple _triangle, glm::vec3 _position) +selected_chunk_type::selected_chunk_type(MapChunk* _chunk, const std::tuple& _triangle, const glm::vec3& _position) : chunk(_chunk) , triangle(_triangle) , position(_position) @@ -228,10 +228,10 @@ void selected_chunk_type::updateDetails(Noggit::Ui::detail_infos* detail_widget) detail_widget->setText(select_info.str()); } -selection_group::selection_group(std::vector selected_objects, World* world) +selection_group::selection_group(const std::vector& selected_objects, World* world) : _world(world) { - if (!selected_objects.size()) + if (selected_objects.empty()) return; // _is_selected = true; @@ -246,14 +246,16 @@ selection_group::selection_group(std::vector selected_objects, Wor // save_json(); } -selection_group::selection_group(std::vector objects_uids, World* world) +// only called when initializing world before objects are loaded, so can't set selected_obj->_grouped +selection_group::selection_group(const std::vector& objects_uids, World* world) : _world(world) { - if (!objects_uids.size()) + if (objects_uids.empty()) return; // _is_selected = true; _members_uid = objects_uids; + // std::unordered_set _members_uid(objects_uids.begin(), objects_uids.end()); recalcExtents(); // save_json(); @@ -314,9 +316,6 @@ void selection_group::select_group() instance->_grouped = true; // ensure grouped attribute, some models could still be unloaded when creating the group - if (_world->is_selected(instance)) - continue; - _world->add_to_selection(obj.value(), true, false); } _world->update_selection_pivot(); diff --git a/src/noggit/Selection.h b/src/noggit/Selection.h index 634de0e5..9d41c2ca 100755 --- a/src/noggit/Selection.h +++ b/src/noggit/Selection.h @@ -25,7 +25,7 @@ public: struct selected_chunk_type : Selectable { - selected_chunk_type(MapChunk* _chunk, std::tuple _triangle, glm::vec3 _position); + selected_chunk_type(MapChunk* _chunk, const std::tuple& _triangle, const glm::vec3& _position); // : chunk(_chunk) // , triangle(_triangle) // , position(_position) @@ -61,8 +61,8 @@ enum eSelectionEntryTypes class selection_group { public: - selection_group(std::vector selected_objects, World* world); - selection_group(std::vector objects_uids, World* world); + selection_group(const std::vector& selected_objects, World* world); + selection_group(const std::vector& objects_uids, World* world); // ~selection_group(); @@ -71,13 +71,11 @@ public: void remove_group(bool save = true); - void add_member(SceneObject* object); + // void add_member(SceneObject* object); void remove_member(unsigned int object_uid); bool contains_object(SceneObject* object); - - void select_group(); void unselect_group(); @@ -99,7 +97,8 @@ public: bool _is_selected = false; private: - std::vector _members_uid; // uids + std::vector _members_uid; + // std::unordered_set _members_uid; // std::vector _object_members; std::array _group_extents; diff --git a/src/noggit/Sky.cpp b/src/noggit/Sky.cpp index b505e1dd..cdc9e3cd 100755 --- a/src/noggit/Sky.cpp +++ b/src/noggit/Sky.cpp @@ -587,6 +587,7 @@ bool Skies::draw(glm::mat4x4 const& model_view , math::frustum const& frustum , const float& cull_distance , int animtime + , int time /*, bool draw_particles*/ , bool draw_skybox , OutdoorLightStats const& light_stats diff --git a/src/noggit/Sky.h b/src/noggit/Sky.h index 74d1ba81..1fc6cf6e 100755 --- a/src/noggit/Sky.h +++ b/src/noggit/Sky.h @@ -330,6 +330,7 @@ public: , math::frustum const& frustum , const float& cull_distance , int animtime + , int time /*, bool draw_particles*/ , bool draw_skybox , OutdoorLightStats const& light_stats diff --git a/src/noggit/WMOInstance.cpp b/src/noggit/WMOInstance.cpp index 38137767..7b6df352 100755 --- a/src/noggit/WMOInstance.cpp +++ b/src/noggit/WMOInstance.cpp @@ -53,15 +53,15 @@ WMOInstance::WMOInstance(BlizzardArchive::Listfile::FileKey const& file_key, Nog void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader - , glm::mat4x4 const& model_view - , glm::mat4x4 const& projection + , const glm::mat4x4 const& model_view + , const glm::mat4x4 const& projection , math::frustum const& frustum , const float& cull_distance , const glm::vec3& camera , bool force_box , bool draw_doodads , bool draw_fog - , std::vector selection + , bool is_selected , int animtime , bool world_has_skies , display_mode display @@ -76,16 +76,6 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader ensureExtents(); - const uint id = this->uid; - bool const is_selected = selection.size() > 0 && - std::find_if(selection.begin(), selection.end(), - [id](selection_type type) - { - return var_type(type) == typeid(selected_object_type) - && std::get(type)->which() == SceneObjectTypes::eWMO - && static_cast(std::get(type))->uid == id; - }) != selection.end(); - { unsigned region_visible = 0; diff --git a/src/noggit/WMOInstance.h b/src/noggit/WMOInstance.h index f6b8e5af..9ae7e994 100755 --- a/src/noggit/WMOInstance.h +++ b/src/noggit/WMOInstance.h @@ -87,15 +87,15 @@ public: } void draw ( OpenGL::Scoped::use_program& wmo_shader - , glm::mat4x4 const& model_view - , glm::mat4x4 const& projection + , const glm::mat4x4 const& model_view + , const glm::mat4x4 const& projection , math::frustum const& frustum , const float& cull_distance , const glm::vec3& camera , bool force_box , bool draw_doodads , bool draw_fog - , std::vector selection + , bool is_selected , int animtime , bool world_has_skies , display_mode display @@ -106,6 +106,7 @@ public: void intersect (math::ray const&, selection_result*, bool do_exterior = true); std::array const& getExtents() override; + bool extentsDirty() { return _need_recalc_extents || !wmo->finishedLoading(); }; void recalcExtents() override; void change_nameset(uint16_t name_set); void ensureExtents() override; diff --git a/src/noggit/World.cpp b/src/noggit/World.cpp index 3d23d535..7c857f71 100755 --- a/src/noggit/World.cpp +++ b/src/noggit/World.cpp @@ -178,12 +178,13 @@ void World::update_selection_pivot() } } -bool World::is_selected(selection_type selection) const +bool World::is_selected(selection_type selection) { ZoneScoped; if (selection.index() != eEntry_Object) return false; + /* auto which = std::get(selection)->which(); if (which == eMODEL) @@ -222,43 +223,42 @@ bool World::is_selected(selection_type selection) const } } + return false; +*/ + + auto selected_object = std::get(selection); + unsigned int uid = selected_object->uid; + + bool found = selected_uids.contains(uid); + if (!found) + return false; + + // verify object type + // probably should only be done when adding or removing objects. + /* + auto instance = getObjectInstance(uid); + if (instance == nullptr || var_type(instance) != typeid(selected_object_type)) + return false; + + if (selected_object->which() != instance->which()) + { + return false; + } + */ + return true; } bool World::is_selected(std::uint32_t uid) const { - ZoneScoped; - for (selection_type const& entry : _current_selection) - { - if (entry.index() != eEntry_Object) - continue; - - auto obj = std::get(entry); - - if (obj->which() == eWMO) - { - if (static_cast(obj)->uid == uid) - { - return true; - } - } - else if (obj->which() == eMODEL) - { - if (static_cast(obj)->uid == uid) - { - return true; - } - } - } - - return false; + return selected_uids.contains(uid); } std::optional World::get_last_selected_model() const { ZoneScoped; if (_current_selection.empty()) - return std::optional(); + return std::nullopt; auto const it ( std::find_if ( _current_selection.rbegin() @@ -561,17 +561,27 @@ void World::set_current_selection(selection_type entry) } // 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) +// now checks if model is already selected, don't need to call is_selected anymore ! +bool World::add_to_selection(selection_type entry, bool skip_group, bool update_pivot) { ZoneScoped; if (entry.index() == eEntry_Object) { - _selected_model_count++; + auto obj = std::get(entry); + + auto result = selected_uids.insert(obj->uid); + + if (!result.second) + { + // Duplicate existed + return false; + } + + _selected_model_count++; // check if it is in a group if (!skip_group) { - auto obj = std::get(entry); for (auto& group : _selection_groups) { if (group.contains_object(obj)) @@ -580,7 +590,7 @@ void World::add_to_selection(selection_type entry, bool skip_group, bool update_ _current_selection.push_back(entry); // this then calls add_to_selection() with skip_group = true to avoid repetition group.select_group(); - return; + return true; } } } @@ -589,24 +599,33 @@ void World::add_to_selection(selection_type entry, bool skip_group, bool update_ if (update_pivot) update_selection_pivot(); + + return true; } void World::remove_from_selection(selection_type entry, bool skip_group, bool update_pivot) { ZoneScoped; + if (entry.index() == eEntry_Object) + { + auto obj = std::get(entry); + size_t erased_count = selected_uids.erase(obj->uid); + if (erased_count == 0) + return; + } + std::vector::iterator position = std::find(_current_selection.begin(), _current_selection.end(), entry); if (position != _current_selection.end()) { if (entry.index() == eEntry_Object) { _selected_model_count--; - // check if it is in a group if (!skip_group) { - auto obj = std::get(entry); for (auto& group : _selection_groups) { + auto obj = std::get(entry); if (group.contains_object(obj)) { // this then calls remove_from_selection() with skip_group = true to avoid repetition @@ -626,6 +645,10 @@ void World::remove_from_selection(selection_type entry, bool skip_group, bool up void World::remove_from_selection(std::uint32_t uid, bool skip_group, bool update_pivot) { ZoneScoped; + size_t erased_count = selected_uids.erase(uid); + if (erased_count == 0) + return; + for (auto it = _current_selection.begin(); it != _current_selection.end(); ++it) { if (it->index() != eEntry_Object) @@ -635,34 +658,34 @@ void World::remove_from_selection(std::uint32_t uid, bool skip_group, bool updat if (obj->uid == uid) { - _selected_model_count--; - _current_selection.erase(it); + _selected_model_count--; + _current_selection.erase(it); - // check if it is in a group - if (!skip_group) + // check if it is in a group + if (!skip_group) + { + for (auto& group : _selection_groups) { - for (auto& group : _selection_groups) - { - if (group.contains_object(obj)) - { - // this then calls remove_from_selection() with skip_group = true to avoid repetition - group.unselect_group(); - break; - } - } + if (group.contains_object(obj)) + { + // this then calls remove_from_selection() with skip_group = true to avoid repetition + group.unselect_group(); + break; + } } - if (update_pivot) - update_selection_pivot(); - return; + } + if (update_pivot) + update_selection_pivot(); + return; } - } } void World::reset_selection() { ZoneScoped; + selected_uids.clear(); _current_selection.clear(); _multi_select_pivot = std::nullopt; _selected_model_count = 0; @@ -3038,15 +3061,14 @@ void World::range_add_to_selection(glm::vec3 const& pos, float radius, bool remo for (auto obj : objects_in_range) { - if (remove) - { - remove_from_selection(obj, false, false); - } - else - { - if (!is_selected(obj)) - add_to_selection(obj, false, false); - } + if (remove) + { + remove_from_selection(obj, false, false); + } + else + { + add_to_selection(obj, false, false); + } } update_selection_pivot(); } @@ -3587,228 +3609,222 @@ void World::notifyTileRendererOnSelectedTextureChange() } void World::select_objects_in_area( - const std::array& selection_box, - bool reset_selection, - const glm::mat4x4& view, - const glm::mat4x4& projection, - int viewport_width, - int viewport_height, - float user_depth, - const glm::vec3& camera_position) + const std::array& selection_box, + bool reset_selection, + const glm::mat4x4& view, + const glm::mat4x4& projection, + int viewport_width, + int viewport_height, + float user_depth, + const glm::vec3& camera_position) { - ZoneScoped; - - if (reset_selection) + ZoneScoped; + + if (reset_selection) + { + this->reset_selection(); + } + + 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; + + if (!tile) { - this->reset_selection(); + break; } - 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) + // some optimizations to see if the tile is in selection before iterating objects in it { - MapTile* tile = map_object.second; + // tile not in screen, skip + // frustum.intersects(tile_extents[1], tile_extents[0]) + if (!tile->_was_rendered_last_frame) + continue; - if (!tile) - { - break; - } - - // some optimizations to see if the tile is in selection before iterating objects in it - { - // tile not in screen, skip - // frustum.intersects(tile_extents[1], tile_extents[0]) - 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 + // 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; + // skip if no objects + if (tile->getObjectInstances().empty()) + continue; - bool valid = false; - auto screenBounds = misc::getAABBScreenBounds(tile->getCombinedExtents(), VPmatrix - , viewport_width, viewport_height, valid, 1.0f); + bool valid = false; + auto screenBounds = misc::getAABBScreenBounds(tile->getCombinedExtents(), VPmatrix + , 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] - , selection_box[0], selection_box[1])) - { - continue; - } - } - - for (auto& pair : tile->getObjectInstances()) - { - [[unlikely]] - if (!pair.first->finishedLoading()) - continue; - - 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; - - - bool corner_occluded = is_point_occluded_by_terrain(corner, view, VPmatrix, viewport_width, viewport_height, camera_position); - - if (!corner_occluded) - { - object_occluded = false; - break; - } - // object_occluded = true; - } - - do_selection = !object_occluded; - } - - if (!do_selection) - continue; - - auto& obj = instance; - auto which = obj->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->model->is_hidden()) - { - this->add_to_selection(obj, false, false); - } - } - } - } + // this only works if all tile points are in screen space + if (valid && !math::boxIntersects(screenBounds[0], screenBounds[1] + , selection_box[0], selection_box[1])) + { + continue; + } } + for (auto& pair : tile->getObjectInstances()) + { + [[unlikely]] + if (!pair.first->finishedLoading()) + continue; + + 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; + + 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 + // we check _rendered_last_frame because m2s that are too small on screen already don't render + 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; + + + bool corner_occluded = is_point_occluded_by_terrain(corner, view, VPmatrix, viewport_width, viewport_height, camera_position); + + if (!corner_occluded) + { + object_occluded = false; + break; + } + // object_occluded = true; + } + + do_selection = !object_occluded; + } + + if (!do_selection) + continue; + + auto& obj = instance; + auto which = obj->which(); + if (which == eWMO) + { + auto model_instance = static_cast(obj); + + if (!model_instance->wmo->is_hidden()) + { + this->add_to_selection(obj, false, false); + } + } + else if (which == eMODEL) + { + auto model_instance = static_cast(obj); + + if (!model_instance->model->is_hidden()) + { + this->add_to_selection(obj, false, false); + } + } + } + } + } + this->update_selection_pivot(); } @@ -3894,32 +3910,30 @@ void World::add_object_group_from_selection() saveSelectionGroups(); } +/* void World::remove_selection_group(selection_group* group) { - // std::vector::iterator position = std::find(_selection_groups.begin(), _selection_groups.end(), group); - // if (position != _selection_groups.end()) - // { - // _selection_groups.erase(position); - // } + std::vector::iterator position = std::find(_selection_groups.begin(), _selection_groups.end(), group); + if (position != _selection_groups.end()) + { + _selection_groups.erase(position); + } - // for (auto it = _selection_groups.begin(); it != _selection_groups.end(); ++it) - // { - // auto it_group = *it; - // if (it_group.getMembers().size() == group->getMembers().size() && it_group.getExtents() == group->getExtents()) - // // if (it_group.isSelected()) - // { - // _selection_groups.erase(it); - // saveSelectionGroups(); - // return; - // } - // } -} + for (auto it = _selection_groups.begin(); it != _selection_groups.end(); ++it) + { + auto it_group = *it; + if (it_group.getMembers().size() == group->getMembers().size() && it_group.getExtents() == group->getExtents()) + // if (it_group.isSelected()) + { + _selection_groups.erase(it); + saveSelectionGroups(); + return; + } + } +}*/ void World::clear_selection_groups() { - // _selection_groups.clear(); - - // for (auto it = _selection_groups.begin(); it != _selection_groups.end(); ++it) for (auto& group : _selection_groups) { // auto it_group = *it; diff --git a/src/noggit/World.h b/src/noggit/World.h index a1019839..4652f553 100755 --- a/src/noggit/World.h +++ b/src/noggit/World.h @@ -58,6 +58,7 @@ class World friend class Noggit::Rendering::WorldRender; protected: + std::unordered_set selected_uids; // fast lookup std::vector _current_selection; // std::unordered_map> _models_by_filename; Noggit::world_model_instances_storage _model_instance_storage; @@ -125,7 +126,7 @@ public: std::optional const& multi_select_pivot() const { return _multi_select_pivot; } // Selection related methods. - bool is_selected(selection_type selection) const; + bool is_selected(selection_type selection); bool is_selected(std::uint32_t uid) const; std::vector const& current_selection() const { return _current_selection; } std::vector const get_selected_objects() const; @@ -136,7 +137,7 @@ 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, bool update_pivot = true); + bool 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(); @@ -408,7 +409,7 @@ public: void add_object_group_from_selection(); - void remove_selection_group(selection_group* group); + // void remove_selection_group(selection_group* group); void clear_selection_groups(); diff --git a/src/noggit/rendering/WorldRender.cpp b/src/noggit/rendering/WorldRender.cpp index 13798b46..c7d74087 100755 --- a/src/noggit/rendering/WorldRender.cpp +++ b/src/noggit/rendering/WorldRender.cpp @@ -347,6 +347,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view tsl::robin_map> models_to_draw; std::vector wmos_to_draw; + std::unordered_map model_boxes_to_draw; // frame counter loop. pretty hacky but works // this is used to make sure no object is processed more than once within a frame @@ -426,10 +427,13 @@ void WorldRender::draw (glm::mat4x4 const& model_view continue; } - instance->frame = frame; - auto m2_instance = static_cast(instance); + if (!draw_hidden_models && m2_instance->model->is_hidden()) + continue; + + instance->frame = frame; + // 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*/) { @@ -437,15 +441,22 @@ void WorldRender::draw (glm::mat4x4 const& model_view { 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; } + // if (render && !draw_models_with_box /* && !m2_instance->model->is_hidden()*/) + // { + // // model box wasn't set in model draw(), add selection boxes + // if (_world->selected_uids.contains(m2_instance->uid)) + // model_boxes_to_draw.emplace(m2_instance->model, instances.size()); + // } + } } @@ -483,10 +494,13 @@ void WorldRender::draw (glm::mat4x4 const& model_view continue; } - instance->frame = frame; - auto wmo_instance = static_cast(instance); + if (!draw_hidden_models && wmo_instance->wmo->is_hidden()) + continue; + + instance->frame = frame; + // 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*/) { @@ -618,8 +632,9 @@ void WorldRender::draw (glm::mat4x4 const& model_view continue; } + bool const is_selected = _world->selected_uids.contains(instance->uid); - if (draw_hidden_models || !is_hidden) + /*if (draw_hidden_models || !is_hidden)*/ // now checking when adding instances { instance->draw(wmo_program , model_view @@ -630,7 +645,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view , is_hidden , draw_wmo_doodads , draw_fog - , _world->current_selection() + , is_selected , _world->animtime , _skies->hasSkies() , display @@ -722,8 +737,6 @@ void WorldRender::draw (glm::mat4x4 const& model_view _world->update_models_by_filename(); }*/ - std::unordered_map model_boxes_to_draw; - { if (draw_models || draw_doodads_wmo || (minimap_render && minimap_render_settings->use_filters)) { @@ -773,7 +786,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view continue; } - if (draw_hidden_models || !pair.first->is_hidden()) + /*if (draw_hidden_models || !pair.first->is_hidden())*/ // now done when building models_to_draw { pair.first->renderer()->draw( model_view , pair.second @@ -863,21 +876,22 @@ void WorldRender::draw (glm::mat4x4 const& model_view it.first->renderer()->drawBox(m2_box_shader, it.second); } } + model_boxes_to_draw.clear(); + // render selection boxes. + // TODO can try to move to m2 box shader but it requires some refactor for (auto& selection : _world->current_selection()) { if (selection.index() == eEntry_Object) { auto obj = std::get(selection); - + if (obj->which() != eMODEL) continue; - + auto model = static_cast(obj); - - - if (model->isInRenderDist(_cull_distance, camera_pos, display) && model->isInFrustum(frustum)) + if (model->_rendered_last_frame) { bool is_selected = false; /* @@ -890,7 +904,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view && std::get(type)->which() == SceneObjectTypes::eMODEL && static_cast(std::get(type))->uid == id; }) != _world->current_selection().end();*/ - + model->draw_box(model_view, projection, is_selected); // make optional! } } diff --git a/src/noggit/tools/ObjectTool.cpp b/src/noggit/tools/ObjectTool.cpp index 700404cf..c85a046a 100644 --- a/src/noggit/tools/ObjectTool.cpp +++ b/src/noggit/tools/ObjectTool.cpp @@ -196,7 +196,7 @@ namespace Noggit addMenuSeperator(menu); - addMenuItem(menu, "Select all Like Selected", "Warning : Doing actions on models overlapping unloaded tiles can cause crash", + addMenuItem(menu, "Select all Like Selected", "", world->get_selected_model_count() == 1, [=] { auto world = mapView()->getWorld(); diff --git a/src/noggit/ui/tools/NodeEditor/Nodes/World/Selection/AddObjectInstanceToSelection.cpp b/src/noggit/ui/tools/NodeEditor/Nodes/World/Selection/AddObjectInstanceToSelection.cpp index 940e5f9c..fbe37323 100755 --- a/src/noggit/ui/tools/NodeEditor/Nodes/World/Selection/AddObjectInstanceToSelection.cpp +++ b/src/noggit/ui/tools/NodeEditor/Nodes/World/Selection/AddObjectInstanceToSelection.cpp @@ -28,8 +28,7 @@ void AddObjectInstanceToSelectionNode::compute() SceneObject* obj = defaultPortData(PortType::In, 1)->value(); - if (!world->is_selected(obj)) - world->add_to_selection(obj); + world->add_to_selection(obj); _out_ports[0].out_value = std::make_shared(true); _node->onDataUpdated(0); diff --git a/src/noggit/ui/tools/PreviewRenderer/PreviewRenderer.cpp b/src/noggit/ui/tools/PreviewRenderer/PreviewRenderer.cpp index df290b45..f8b18077 100755 --- a/src/noggit/ui/tools/PreviewRenderer/PreviewRenderer.cpp +++ b/src/noggit/ui/tools/PreviewRenderer/PreviewRenderer.cpp @@ -191,7 +191,7 @@ void PreviewRenderer::draw() wmo_instance.draw( wmo_program, model_view(), projection(), frustum, culldistance, _camera.position, _draw_boxes.get(), _draw_models.get() - , false, std::vector(), 0, false, display_mode::in_3D, true + , false, false, 0, false, display_mode::in_3D, true ); auto doodads = wmo_instance.get_doodads(true);