grouping is complete, only missing json saving

This commit is contained in:
T1ti
2024-01-27 04:55:02 +01:00
parent 9622a00a9a
commit 0f42da9939
11 changed files with 313 additions and 77 deletions

View File

@@ -5931,29 +5931,85 @@ void MapView::ShowContextMenu(QPoint pos)
// TODO
QAction action_group("Group Selected Objects TODO", this);
menu->addAction(&action_group);
action_group.setEnabled(_world->has_multiple_model_selected() && true); // TODO, some "notgrouped" condition
QObject::connect(&action_snap, &QAction::triggered, [=]()
// check if all selected objects are already grouped
bool groupable = false;
if ( _world->has_multiple_model_selected())
{
// if there's no existing groups, that means it's always groupable
if (!_world->_selection_groups.size())
groupable = true;
if (!groupable)
{
_world->add_object_group();
//auto selected_objects = _world->get_selected_objects();
//for (auto selected_obj : selected_objects)
//{
//
//}
// check if there's any ungrouped object
for (auto obj : _world->get_selected_objects())
{
bool obj_ungrouped = true;
for (auto& group : _world->_selection_groups)
{
if (group.contains_object(obj))
obj_ungrouped = false;
}
if (obj_ungrouped)
{
groupable = true;
break;
}
}
}
}
action_group.setEnabled(groupable);
QObject::connect(&action_group, &QAction::triggered, [=]()
{
// remove all groups the objects are already in and create a new one
// for (auto obj : _world->get_selected_objects())
// {
// for (auto& group : _world->_selection_groups)
// {
// if (group.contains_object(obj))
// {
// group.remove_group();
// }
// }
// }
for (auto& group : _world->_selection_groups)
{
if (group.isSelected())
{
group.remove_group();
}
}
_world->add_object_group_from_selection();
});
QAction action_ungroup("Ungroup Selected Objects TODO", this);
menu->addAction(&action_ungroup);
bool group_selected = false;
for (auto& group : _world->_selection_groups)
{
if (group.isSelected())
{
group_selected = true;
break;
}
}
action_ungroup.setEnabled(group_selected);
QObject::connect(&action_ungroup, &QAction::triggered, [=]()
{
for (auto& group : _world->_selection_groups)
{
if (group.isSelected())
{
group.remove_group();
}
}
});
menu->exec(mapToGlobal(pos)); // synch
// menu->popup(mapToGlobal(pos)); // asynch
// menu->popup(mapToGlobal(pos)); // asynch, needs to be preloaded to work
};
}

View File

@@ -73,10 +73,11 @@ void ModelInstance::draw_box (glm::mat4x4 const& model_view
}
else
{
const glm::vec4 color = _grouped ? glm::vec4(0.5f, 0.5f, 1.0f, 0.5f) : glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
Noggit::Rendering::Primitives::WireBox::getInstance(_context).draw ( model_view
, projection
, transformMatrix()
, {0.5f, 0.5f, 0.5f, 1.0f}
, color
, misc::transform_model_box_coords(model->header.bounding_box_min)
, misc::transform_model_box_coords(model->header.bounding_box_max)
);

View File

@@ -72,6 +72,8 @@ public:
glm::vec3 const getServerPos() { return glm::vec3(ZEROPOINT - pos.z, ZEROPOINT - pos.x, pos.y); }
bool _grouped = false;
public:
glm::vec3 pos;
std::array<glm::vec3, 2> extents;

View File

@@ -77,20 +77,22 @@ void selected_chunk_type::updateDetails(Noggit::Ui::detail_infos* detail_widget)
detail_widget->setText(select_info.str());
}
selection_group::selection_group(std::vector<selected_object_type> selected_objects, World* world)
selection_group::selection_group(std::vector<SceneObject*> selected_objects, World* world)
: _world(world)
{
_object_count = selected_objects.size();
// _object_count = selected_objects.size();
if (!_object_count)
if (!selected_objects.size())
return;
_is_selected = true;
// default group extents to first obj
_group_extents = selected_objects.front()->getExtents();
_members_uid.reserve(selected_objects.size());
for (auto& selected_obj : selected_objects)
{
selected_obj->_grouped = true;
_members_uid.push_back(selected_obj->uid);
if (selected_obj->getExtents()[0].x < _group_extents[0].x)
@@ -109,7 +111,32 @@ selection_group::selection_group(std::vector<selected_object_type> selected_obje
}
}
bool selection_group::group_contains_object(selected_object_type object)
void selection_group::remove_member(unsigned int object_uid)
{
if (_members_uid.size() == 1)
{
remove_group();
return;
}
for (auto it = _members_uid.begin(); it != _members_uid.end(); ++it)
{
auto member_uid = *it;
std::optional<selection_type> obj = _world->get_model(member_uid);
if (!obj)
continue;
SceneObject* instance = std::get<SceneObject*>(obj.value());
if (instance->uid == object_uid)
{
_members_uid.erase(it);
instance->_grouped = false;
return;
}
}
}
bool selection_group::contains_object(SceneObject* object)
{
for (unsigned int member_uid : _members_uid)
{
@@ -134,8 +161,9 @@ void selection_group::select_group()
continue;
_world->add_to_selection(obj.value(), true);
}
_is_selected = true;
}
void selection_group::unselect_group()
@@ -143,29 +171,35 @@ 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);
/*
std::optional<selection_type> obj = _world->get_model(obj_uid);
if (!obj)
continue;
SceneObject* instance = std::get<SceneObject*>(obj.value());
if (_world->is_selected(instance))
_world->remove_from_selection(obj.value());
*/
_world->remove_from_selection(obj_uid, true);
}
_is_selected = false;
}
void selection_group::move_group()
// only remove the group, not used to delete objects in it
void selection_group::remove_group()
{
// TODO remove group from storage and json
// _world->select_objects_in_area
_world->remove_selection_group(this);
// remvoe grouped attribute
for (auto it = _members_uid.begin(); it != _members_uid.end(); ++it)
{
auto member_uid = *it;
std::optional<selection_type> obj = _world->get_model(member_uid);
if (!obj)
continue;
SceneObject* instance = std::get<SceneObject*>(obj.value());
instance->_grouped = false;
}
}
void selection_group::recalcExtents()
{
bool first_obj = true;
for (unsigned int obj_uid : _members_uid)
{
std::optional<selection_type> obj = _world->get_model(obj_uid);
@@ -174,6 +208,13 @@ void selection_group::recalcExtents()
SceneObject* instance = std::get<SceneObject*>(obj.value());
if (first_obj)
{
_group_extents = instance->getExtents();
first_obj = false;
continue;
}
// min = glm::min(min, point);
if (instance->getExtents()[0].x < _group_extents[0].x)
_group_extents[0].x = instance->extents[0].x;

View File

@@ -57,40 +57,51 @@ enum eSelectionEntryTypes
class selection_group
{
public:
selection_group(std::vector<selected_object_type> selected_objects, World* world);
selection_group(std::vector<SceneObject*> selected_objects, World* world);
// selection_group(std::vector<unsigned int> objects_uids, World* world);
// void set_selected_as_group(std::vector<selected_object_type> selection);
void remove_group();
void add_member(SceneObject* object);
void remove_member(unsigned int object_uid);
bool contains_object(SceneObject* object);
void add_member(selected_object_type object);
bool group_contains_object(selected_object_type object);
void select_group();
void unselect_group();
// void set_selected_as_group(std::vector<selected_object_type> selection);
void recalcExtents();
// void copy_group(); // create and save a new selection group from copied objects
void copy_group(); // create and save a new selection group from copied objects
void move_group();
void scale_group();
void rotate_group();
// void move_group();
// void scale_group();
// void rotate_group();
std::vector<unsigned int> const& getObjects() const { return _members_uid; }
[[nodiscard]]
std::array<glm::vec3, 2> const& getExtents() { return _group_extents; } // ensureExtents();
private:
void recalcExtents();
bool isSelected() const { return _is_selected; }
void setUnselected() { _is_selected = false; }
private:
std::vector<unsigned int> _members_uid; // uids
bool _is_selected = false;
// std::vector<SceneObject*> _object_members;
std::array<glm::vec3, 2> _group_extents;
unsigned int _object_count = 0;
// unsigned int _object_count = 0;
World* _world;
// bool _need_recalc_extents = false;
};
using selection_entry = std::pair<float, selection_type>;

View File

@@ -110,7 +110,7 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
, model_view
, projection
, _transform_mat
, is_selected
, is_selected && !_grouped
, frustum
, cull_distance
, camera
@@ -125,10 +125,10 @@ void WMOInstance::draw ( OpenGL::Scoped::use_program& wmo_shader
if (force_box || is_selected)
{
//gl.enable(GL_BLEND);
//gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
gl.enable(GL_BLEND);
gl.blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glm::vec4 color = force_box ? glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)
glm::vec4 color = force_box || _grouped ? glm::vec4(0.5f, 0.5f, 1.0f, 0.5f)
: glm::vec4(0.0f, 1.0f, 0.0f, 1.0f);
Noggit::Rendering::Primitives::WireBox::getInstance(_context).draw(model_view

View File

@@ -363,6 +363,8 @@ void World::rotate_selected_models_randomly(float minX, float maxX, float minY,
void World::rotate_selected_models_to_ground_normal(bool smoothNormals)
{
ZoneScoped;
if (!_selected_model_count)
return;
for (auto& entry : _current_selection)
{
auto type = entry.index();
@@ -500,16 +502,14 @@ void World::rotate_selected_models_to_ground_normal(bool smoothNormals)
double cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
updateTilesEntry(entry, model_update::add);
}
update_selected_model_groups();
}
void World::set_current_selection(selection_type entry)
{
ZoneScoped;
_current_selection.clear();
_current_selection.push_back(entry);
_multi_select_pivot = std::nullopt;
_selected_model_count = entry.index() != eEntry_Object ? 0 : 1;
reset_selection();
add_to_selection(entry);
}
void World::add_to_selection(selection_type entry, bool skip_group)
@@ -525,7 +525,7 @@ void World::add_to_selection(selection_type entry, bool skip_group)
auto obj = std::get<selected_object_type>(entry);
for (auto& group : _selection_groups)
{
if (group.group_contains_object(obj))
if (group.contains_object(obj))
{
// this then calls add_to_selection() with skip_group = true to avoid repetition
group.select_group();
@@ -538,7 +538,16 @@ void World::add_to_selection(selection_type entry, bool skip_group)
update_selection_pivot();
}
void World::remove_from_selection(selection_type entry)
void World::remove_selection_group(selection_group* group)
{
for (auto it = _selection_groups.begin(); it != _selection_groups.end(); ++it)
{
_selection_groups.erase(it);
return;
}
}
void World::remove_from_selection(selection_type entry, bool skip_group)
{
ZoneScoped;
std::vector<selection_type>::iterator position = std::find(_current_selection.begin(), _current_selection.end(), entry);
@@ -547,6 +556,21 @@ void World::remove_from_selection(selection_type entry)
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)
{
if (group.contains_object(obj))
{
// this then calls remove_from_selection() with skip_group = true to avoid repetition
group.unselect_group();
break;
}
}
}
}
_current_selection.erase(position);
@@ -554,7 +578,7 @@ void World::remove_from_selection(selection_type entry)
}
}
void World::remove_from_selection(std::uint32_t uid)
void World::remove_from_selection(std::uint32_t uid, bool skip_group)
{
ZoneScoped;
for (auto it = _current_selection.begin(); it != _current_selection.end(); ++it)
@@ -564,18 +588,30 @@ void World::remove_from_selection(std::uint32_t uid)
auto obj = std::get<selected_object_type>(*it);
if (obj->which() == eMODEL && static_cast<ModelInstance*>(obj)->uid == uid)
if (obj->uid == uid)
{
_current_selection.erase(it);
update_selection_pivot();
return;
}
else if (obj->which() == eWMO && static_cast<WMOInstance*>(obj)->uid == uid)
{
_current_selection.erase(it);
update_selection_pivot();
return;
_selected_model_count--;
_current_selection.erase(it);
// check if it is in a group
if (!skip_group)
{
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;
}
}
}
update_selection_pivot();
return;
}
}
}
@@ -585,11 +621,28 @@ void World::reset_selection()
_current_selection.clear();
_multi_select_pivot = std::nullopt;
_selected_model_count = 0;
for (auto& selection_group : _selection_groups)
{
selection_group.setUnselected();
}
}
void World::delete_selected_models()
{
ZoneScoped;
if (!_selected_model_count)
return;
// erase selected groups as well
for (auto& group : _selection_groups)
{
if (group.isSelected())
{
group.remove_group();
}
}
_model_instance_storage.delete_instances(_current_selection);
need_model_updates = true;
reset_selection();
@@ -626,6 +679,8 @@ glm::vec3 World::get_ground_height(glm::vec3 pos)
void World::snap_selected_models_to_the_ground()
{
ZoneScoped;
if (!_selected_model_count)
return;
for (auto& entry : _current_selection)
{
auto type = entry.index();
@@ -647,11 +702,14 @@ void World::snap_selected_models_to_the_ground()
}
update_selection_pivot();
update_selected_model_groups();
}
void World::scale_selected_models(float v, m2_scaling_type type)
{
ZoneScoped;
if (!_selected_model_count)
return;
for (auto& entry : _current_selection)
{
if (entry.index() == eEntry_Object)
@@ -692,11 +750,14 @@ void World::scale_selected_models(float v, m2_scaling_type type)
updateTilesModel(mi, model_update::add);
}
}
update_selected_model_groups();
}
void World::move_selected_models(float dx, float dy, float dz)
{
ZoneScoped;
if (!_selected_model_count)
return;
for (auto& entry : _current_selection)
{
auto type = entry.index();
@@ -721,6 +782,7 @@ void World::move_selected_models(float dx, float dy, float dz)
}
update_selection_pivot();
update_selected_model_groups();
}
void World::move_model(selection_type entry, float dx, float dy, float dz)
@@ -751,6 +813,8 @@ void World::move_model(selection_type entry, float dx, float dy, float dz)
void World::set_selected_models_pos(glm::vec3 const& pos, bool change_height)
{
ZoneScoped;
if (!_selected_model_count)
return;
// move models relative to the pivot when several are selected
if (has_multiple_model_selected())
{
@@ -787,6 +851,7 @@ void World::set_selected_models_pos(glm::vec3 const& pos, bool change_height)
}
update_selection_pivot();
update_selected_model_groups();
}
void World::set_model_pos(selection_type entry, glm::vec3 const& pos, bool change_height)
@@ -811,6 +876,9 @@ void World::set_model_pos(selection_type entry, glm::vec3 const& pos, bool chang
void World::rotate_selected_models(math::degrees rx, math::degrees ry, math::degrees rz, bool use_pivot)
{
ZoneScoped;
if (!_selected_model_count)
return;
math::degrees::vec3 dir_change(rx._, ry._, rz._);
bool has_multi_select = has_multiple_model_selected();
@@ -848,17 +916,21 @@ void World::rotate_selected_models(math::degrees rx, math::degrees ry, math::deg
updateTilesEntry(entry, model_update::add);
}
update_selected_model_groups();
}
void World::set_selected_models_rotation(math::degrees rx, math::degrees ry, math::degrees rz)
{
ZoneScoped;
if (!_selected_model_count)
return;
math::degrees::vec3 new_dir(rx._, ry._, rz._);
for (auto& entry : _current_selection)
{
auto type = entry.index();
if (type == eEntry_MapChunk)
if (type != eEntry_Object)
{
continue;
}
@@ -876,8 +948,17 @@ void World::set_selected_models_rotation(math::degrees rx, math::degrees ry, mat
updateTilesEntry(entry, model_update::add);
}
update_selected_model_groups();
}
void World::update_selected_model_groups()
{
for (auto& selection_group : _selection_groups)
{
if (selection_group.isSelected())
selection_group.recalcExtents();
}
}
MapChunk* World::getChunkAt(glm::vec3 const& pos)
{
@@ -3390,9 +3471,9 @@ void World::select_objects_in_area(
}
}
void World::add_object_group()
void World::add_object_group_from_selection()
{
// auto selected_objects = get_selected_objects();
// create group from selecetd objects
selection_group selection_group(get_selected_objects(), this);
_selection_groups.push_back(selection_group);

View File

@@ -61,9 +61,9 @@ protected:
// std::unordered_map<std::string, std::vector<ModelInstance*>> _models_by_filename;
Noggit::world_model_instances_storage _model_instance_storage;
Noggit::world_tile_update_queue _tile_update_queue;
public:
std::vector<selection_group> _selection_groups;
public:
MapIndex mapIndex;
Noggit::map_horizon horizon;
@@ -132,8 +132,8 @@ public:
// 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);
void remove_from_selection(selection_type entry);
void remove_from_selection(std::uint32_t uid);
void remove_from_selection(selection_type entry, bool skip_group = false);
void remove_from_selection(std::uint32_t uid, bool skip_group = false);
void reset_selection();
void delete_selected_models();
glm::vec3 get_ground_height(glm::vec3 pos);
@@ -165,6 +165,8 @@ public:
void rotate_selected_models_randomly(float minX, float maxX, float minY, float maxY, float minZ, float maxZ);
void set_selected_models_rotation(math::degrees rx, math::degrees ry, math::degrees rz);
void update_selected_model_groups();
// Checks the normal of the terrain on model origin and rotates to that spot.
void rotate_selected_models_to_ground_normal(bool smoothNormals);
@@ -390,8 +392,8 @@ public:
glm::vec3 camera_position
);
void add_object_group();
void delete_object_group();
void add_object_group_from_selection();
void remove_selection_group(selection_group* group);
protected:
// void update_models_by_filename();

View File

@@ -766,14 +766,46 @@ void WorldRender::draw (glm::mat4x4 const& model_view
continue;
auto model = static_cast<ModelInstance*>(obj);
if (model->isInFrustum(frustum) && model->isInRenderDist(_cull_distance, camera_pos, display))
{
model->draw_box(model_view, projection, false); // make optional!
bool is_selected = false;
/*
auto id = model->uid;
bool const is_selected = _world->current_selection().size() > 0 &&
std::find_if(_world->current_selection().begin(), _world->current_selection().end(),
[id](selection_type type)
{
return var_type(type) == typeid(selected_object_type)
&& 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!
}
}
}
}
// render selection group boxes
for (auto& selection_group : _world->_selection_groups)
{
if (!selection_group.isSelected())
continue;
glm::mat4x4 identity_mtx = glm::mat4x4{ 1 };
auto& extents = selection_group.getExtents();
Noggit::Rendering::Primitives::WireBox::getInstance(_world->_context).draw(model_view
, projection
, identity_mtx
, { 0.0f, 0.0f, 1.0f, 1.0f } // blue
, extents[0]
, extents[1]
);
}
// set anim time only once per frame
{
OpenGL::Scoped::use_program water_shader {*_liquid_program.get()};

View File

@@ -315,5 +315,7 @@ void ViewportGizmo::handleTransformGizmo(MapView* map_view
_world->updateTilesEntry(selected, model_update::add);
}
}
_world->update_selected_model_groups();
}

View File

@@ -189,6 +189,14 @@ namespace Noggit
_world->updateTilesEntry(instance.value(), model_update::remove);
auto obj = std::get<selected_object_type>(instance.value());
for (auto& selection_group : _world->_selection_groups)
{
if (selection_group.contains_object(obj))
{
selection_group.remove_member(obj->uid);
}
}
if (NOGGIT_CUR_ACTION)
{
NOGGIT_CUR_ACTION->registerObjectRemoved(obj);