optimize objects selection and rendering

This commit is contained in:
T1ti
2024-09-28 05:28:39 +02:00
parent b26dc8a331
commit bd66c22938
14 changed files with 367 additions and 344 deletions

View File

@@ -69,6 +69,7 @@
#include <noggit/tools/ScriptingTool.hpp>
#include <noggit/tools/ChunkTool.hpp>
#include <noggit/StringHash.hpp>
#include <noggit/application/NoggitApplication.hpp>
#ifdef USE_MYSQL_UID_STORAGE
#include <mysql/mysql.h>

View File

@@ -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)

View File

@@ -6,7 +6,7 @@
#include <sstream>
selected_chunk_type::selected_chunk_type(MapChunk* _chunk, std::tuple<int, int, int> _triangle, glm::vec3 _position)
selected_chunk_type::selected_chunk_type(MapChunk* _chunk, const std::tuple<int, int, int>& _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<SceneObject*> selected_objects, World* world)
selection_group::selection_group(const std::vector<SceneObject*>& 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<SceneObject*> selected_objects, Wor
// save_json();
}
selection_group::selection_group(std::vector<unsigned int> 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<unsigned int>& 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<unsigned int> _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();

View File

@@ -25,7 +25,7 @@ public:
struct selected_chunk_type : Selectable
{
selected_chunk_type(MapChunk* _chunk, std::tuple<int, int, int> _triangle, glm::vec3 _position);
selected_chunk_type(MapChunk* _chunk, const std::tuple<int, int, int>& _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<SceneObject*> selected_objects, World* world);
selection_group(std::vector<unsigned int> objects_uids, World* world);
selection_group(const std::vector<SceneObject*>& selected_objects, World* world);
selection_group(const std::vector<unsigned int>& 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<unsigned int> _members_uid; // uids
std::vector<unsigned int> _members_uid;
// std::unordered_set<unsigned int> _members_uid;
// std::vector<SceneObject*> _object_members;
std::array<glm::vec3, 2> _group_extents;

View File

@@ -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

View File

@@ -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

View File

@@ -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_type> 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<selected_object_type>(type)->which() == SceneObjectTypes::eWMO
&& static_cast<WMOInstance*>(std::get<selected_object_type>(type))->uid == id;
}) != selection.end();
{
unsigned region_visible = 0;

View File

@@ -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_type> 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<glm::vec3, 2> const& getExtents() override;
bool extentsDirty() { return _need_recalc_extents || !wmo->finishedLoading(); };
void recalcExtents() override;
void change_nameset(uint16_t name_set);
void ensureExtents() override;

View File

@@ -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<selected_object_type>(selection)->which();
if (which == eMODEL)
@@ -222,43 +223,42 @@ bool World::is_selected(selection_type selection) const
}
}
return false;
*/
auto selected_object = std::get<selected_object_type>(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<selected_object_type>(entry);
if (obj->which() == eWMO)
{
if (static_cast<WMOInstance*>(obj)->uid == uid)
{
return true;
}
}
else if (obj->which() == eMODEL)
{
if (static_cast<ModelInstance*>(obj)->uid == uid)
{
return true;
}
}
}
return false;
return selected_uids.contains(uid);
}
std::optional<selection_type> World::get_last_selected_model() const
{
ZoneScoped;
if (_current_selection.empty())
return std::optional<selection_type>();
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<selected_object_type>(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<selected_object_type>(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<selected_object_type>(entry);
size_t erased_count = selected_uids.erase(obj->uid);
if (erased_count == 0)
return;
}
std::vector<selection_type>::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<selected_object_type>(entry);
for (auto& group : _selection_groups)
{
auto obj = std::get<selected_object_type>(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)
@@ -656,13 +679,13 @@ void World::remove_from_selection(std::uint32_t uid, bool skip_group, bool updat
return;
}
}
}
void World::reset_selection()
{
ZoneScoped;
selected_uids.clear();
_current_selection.clear();
_multi_select_pivot = std::nullopt;
_selected_model_count = 0;
@@ -3044,7 +3067,6 @@ void World::range_add_to_selection(glm::vec3 const& pos, float radius, bool remo
}
else
{
if (!is_selected(obj))
add_to_selection(obj, false, false);
}
}
@@ -3667,13 +3689,6 @@ void World::select_objects_in_area(
// 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<selected_object_type>(modelInstance.value());
bool do_selection = false;
// Old code to check position point instead of bound box
@@ -3717,6 +3732,7 @@ void World::select_objects_in_area(
}
// 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;
@@ -3791,7 +3807,7 @@ void World::select_objects_in_area(
{
auto model_instance = static_cast<WMOInstance*>(obj);
if (!is_selected(obj) && !model_instance->wmo->is_hidden())
if (!model_instance->wmo->is_hidden())
{
this->add_to_selection(obj, false, false);
}
@@ -3800,7 +3816,7 @@ void World::select_objects_in_area(
{
auto model_instance = static_cast<ModelInstance*>(obj);
if (!is_selected(obj) && !model_instance->model->is_hidden())
if (!model_instance->model->is_hidden())
{
this->add_to_selection(obj, false, false);
}
@@ -3894,32 +3910,30 @@ void World::add_object_group_from_selection()
saveSelectionGroups();
}
/*
void World::remove_selection_group(selection_group* group)
{
// std::vector<selection_type>::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;
// }
// }
std::vector<selection_type>::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;
}
}
}*/
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;

View File

@@ -58,6 +58,7 @@ class World
friend class Noggit::Rendering::WorldRender;
protected:
std::unordered_set<unsigned int> selected_uids; // fast lookup
std::vector<selection_type> _current_selection;
// std::unordered_map<std::string, std::vector<ModelInstance*>> _models_by_filename;
Noggit::world_model_instances_storage _model_instance_storage;
@@ -125,7 +126,7 @@ public:
std::optional<glm::vec3> 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<selection_type> const& current_selection() const { return _current_selection; }
std::vector<selected_object_type> 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<std::string, std::vector<ModelInstance*>> 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();

View File

@@ -347,6 +347,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
tsl::robin_map<Model*, std::vector<glm::mat4x4>> models_to_draw;
std::vector<WMOInstance*> wmos_to_draw;
std::unordered_map<Model*, std::size_t> 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<ModelInstance*>(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<WMOInstance*>(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*, std::size_t> 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,7 +876,10 @@ 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)
@@ -875,9 +891,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
auto model = static_cast<ModelInstance*>(obj);
if (model->isInRenderDist(_cull_distance, camera_pos, display) && model->isInFrustum(frustum))
if (model->_rendered_last_frame)
{
bool is_selected = false;
/*

View File

@@ -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();

View File

@@ -28,7 +28,6 @@ void AddObjectInstanceToSelectionNode::compute()
SceneObject* obj = defaultPortData<ObjectInstanceData>(PortType::In, 1)->value();
if (!world->is_selected(obj))
world->add_to_selection(obj);
_out_ports[0].out_value = std::make_shared<LogicData>(true);

View File

@@ -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<selection_type>(), 0, false, display_mode::in_3D, true
, false, false, 0, false, display_mode::in_3D, true
);
auto doodads = wmo_instance.get_doodads(true);