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)
@@ -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<glm::vec2, 2>& 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<glm::vec2, 2>& 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<selected_object_type>(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<WMOInstance*>(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<ModelInstance*>(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<WMOInstance*>(obj);
if (!model_instance->wmo->is_hidden())
{
this->add_to_selection(obj, false, false);
}
}
else if (which == eMODEL)
{
auto model_instance = static_cast<ModelInstance*>(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<selection_type>::iterator position = std::find(_selection_groups.begin(), _selection_groups.end(), group);
// if (position != _selection_groups.end())
// {
// _selection_groups.erase(position);
// }
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;
// }
// }
}
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,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<selected_object_type>(selection);
if (obj->which() != eMODEL)
continue;
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;
/*
@@ -890,7 +904,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
&& std::get<selected_object_type>(type)->which() == SceneObjectTypes::eMODEL
&& static_cast<ModelInstance*>(std::get<selected_object_type>(type))->uid == id;
}) != _world->current_selection().end();*/
model->draw_box(model_view, projection, is_selected); // make optional!
}
}

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,8 +28,7 @@ void AddObjectInstanceToSelectionNode::compute()
SceneObject* obj = defaultPortData<ObjectInstanceData>(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<LogicData>(true);
_node->onDataUpdated(0);

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