diff --git a/src/noggit/MapView.cpp b/src/noggit/MapView.cpp index f439add8..f4cb0a8a 100755 --- a/src/noggit/MapView.cpp +++ b/src/noggit/MapView.cpp @@ -817,6 +817,9 @@ void MapView::setupObjectEditorUi() /* Additional tools */ + /* Area selection */ + _area_selection = new QRubberBand(QRubberBand::Rectangle, this); + /* Object Palette */ _object_palette = new Noggit::Ui::ObjectPalette(this, this); _object_palette->hide(); @@ -4910,6 +4913,12 @@ void MapView::mouseMoveEvent (QMouseEvent* event) } + if (leftMouse && terrainMode == editing_mode::object && _display_mode == display_mode::in_3D && !ImGuizmo::IsUsing()) + { + _needs_redraw = true; + _area_selection->setGeometry(QRect(_drag_start_pos, event->pos()).normalized()); + } + if (_display_mode == display_mode::in_2D && leftMouse && _mod_alt_down && _mod_shift_down) { strafing = ((relative_movement.dx() / XSENS) / -1) * 5.0f; @@ -4978,18 +4987,15 @@ void MapView::mousePressEvent(QMouseEvent* event) break; } - if (leftMouse) + if (leftMouse && ((terrainMode == editing_mode::object || terrainMode == editing_mode::minimap) && !_mod_ctrl_down)) { - if ((terrainMode == editing_mode::object || terrainMode == editing_mode::minimap) && !_mod_ctrl_down) - { - doSelection(false); - } - else - { - doSelection(true); - } + _drag_start_pos = event->pos(); + _needs_redraw = true; + _area_selection->setGeometry(QRect(_drag_start_pos, QSize())); + _area_selection->show(); } - else if (rightMouse) + + if (rightMouse) { look = true; } @@ -5075,6 +5081,32 @@ void MapView::mouseReleaseEvent (QMouseEvent* event) strafing = 0; moving = 0; } + + if ((terrainMode == editing_mode::object || terrainMode == editing_mode::minimap) && !_mod_ctrl_down) + { + auto drag_end_pos = event->pos(); + + if (_drag_start_pos != drag_end_pos && !ImGuizmo::IsUsing()) + { + const std::array selection_box + { + glm::vec2(std::min(_drag_start_pos.x(), drag_end_pos.x()), std::min(_drag_start_pos.y(), drag_end_pos.y())), + glm::vec2(std::max(_drag_start_pos.x(), drag_end_pos.x()), std::max(_drag_start_pos.y(), drag_end_pos.y())) + }; + _world->select_objects_in_area(selection_box, !_mod_shift_down, model_view(), projection(), width(), height(), objectEditor->drag_selection_depth(), _camera.position); + } + else // Do normal selection when we just clicked + { + doSelection(false); + } + + _area_selection->hide(); + } + else + { + doSelection(true); + } + break; case Qt::RightButton: diff --git a/src/noggit/MapView.h b/src/noggit/MapView.h index 06516a1c..e1ec9e7d 100755 --- a/src/noggit/MapView.h +++ b/src/noggit/MapView.h @@ -126,6 +126,7 @@ private: float moving, strafing, updown, mousedir, turn, lookat; CursorType _cursorType; glm::vec3 _cursor_pos; + QPoint _drag_start_pos; float _cursorRotation; bool look, freelook; bool ui_hidden = false; @@ -449,6 +450,8 @@ private: OpenGL::Scoped::deferred_upload_buffers<2> _buffers; + QRubberBand* _area_selection; + public: private: diff --git a/src/noggit/Misc.cpp b/src/noggit/Misc.cpp index d4ba7e28..0cc2d8fc 100755 --- a/src/noggit/Misc.cpp +++ b/src/noggit/Misc.cpp @@ -21,6 +21,12 @@ namespace misc point.x <= extents[1].x && point.z <= extents[1].z; } + bool pointInside(glm::vec2 point, std::array const& extents) + { + return point.x >= extents[0].x && point.y >= extents[0].y && + point.x <= extents[1].x && point.y <= extents[1].y; + } + void minmax(glm::vec3* a, glm::vec3* b) { if (a->x > b->x) diff --git a/src/noggit/Misc.h b/src/noggit/Misc.h index b0300fed..9677fca6 100755 --- a/src/noggit/Misc.h +++ b/src/noggit/Misc.h @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -55,6 +56,7 @@ namespace misc bool deg_vec3d_equals(math::degrees::vec3 const& v1, math::degrees::vec3 const& v2); bool pointInside(glm::vec3 point, std::array const& extents); + bool pointInside(glm::vec2 point, std::array const& extents); void minmax(glm::vec3* a, glm::vec3* b); inline int rounded_int_div(int value, int div) diff --git a/src/noggit/World.cpp b/src/noggit/World.cpp index 6a47b568..a66fe2d3 100755 --- a/src/noggit/World.cpp +++ b/src/noggit/World.cpp @@ -2976,3 +2976,72 @@ void World::notifyTileRendererOnSelectedTextureChange() } } +void World::select_objects_in_area( + const std::array selection_box, + bool reset_selection, + glm::mat4x4 view, + glm::mat4x4 projection, + int viewport_width, + int viewport_height, + float user_depth, + glm::vec3 camera_position) +{ + ZoneScoped; + + if (reset_selection) + { + this->reset_selection(); + } + + for (auto& map_object : _loaded_tiles_buffer) + { + MapTile* tile = map_object.second; + + if (!tile) + { + break; + } + + for (auto& pair : tile->getObjectInstances()) + { + if (pair.second[0]->which() == eMODEL) + { + for (auto& instance : pair.second) + { + auto model = instance->transformMatrix(); + glm::mat4 VPmatrix = projection * view; + glm::vec4 screenPos = VPmatrix * glm::vec4(instance->pos, 1.0f); + screenPos.x /= screenPos.w; + screenPos.y /= screenPos.w; + + screenPos.x = (screenPos.x + 1.0f) / 2.0f; + screenPos.y = (screenPos.y + 1.0f) / 2.0f; + screenPos.y = 1 - screenPos.y; + + screenPos.x *= viewport_width; + screenPos.y *= viewport_height; + + auto depth = glm::distance(camera_position, instance->pos); + if (depth <= user_depth) + { + const glm::vec2 screenPos2D = glm::vec2(screenPos); + if (misc::pointInside(screenPos2D, selection_box)) + { + auto uid = instance->uid; + auto modelInstance = _model_instance_storage.get_instance(uid); + if (modelInstance && modelInstance.value().index() == eEntry_Object) { + auto obj = std::get(modelInstance.value()); + auto model_instance = static_cast(obj); + + if (!is_selected(obj) && !model_instance->model->is_hidden()) + { + this->add_to_selection(obj); + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/noggit/World.h b/src/noggit/World.h index bbc0b01c..1a26da4b 100755 --- a/src/noggit/World.h +++ b/src/noggit/World.h @@ -356,6 +356,17 @@ public: unsigned getNumLoadedTiles() const { return _n_loaded_tiles; }; unsigned getNumRenderedTiles() const { return _n_rendered_tiles; }; + void select_objects_in_area( + const std::array selection_box, + bool reset_selection, + glm::mat4x4 view, + glm::mat4x4 projection, + int viewport_width, + int viewport_height, + float user_depth, + glm::vec3 camera_position + ); + protected: void update_models_by_filename(); diff --git a/src/noggit/ui/ObjectEditor.cpp b/src/noggit/ui/ObjectEditor.cpp index f8601293..05316d57 100755 --- a/src/noggit/ui/ObjectEditor.cpp +++ b/src/noggit/ui/ObjectEditor.cpp @@ -73,10 +73,26 @@ namespace Noggit _radius_slider->setRange (0, 100); _radius_slider->setSliderPosition (_radius); - radius_layout->addRow (_radius_slider); + radius_layout->addRow(_radius_slider); radius_layout->addRow(_radius_spin); layout->addWidget(radius_group); + QGroupBox* drag_selection_depth_group = new QGroupBox("Drag Selection Depth"); + auto drag_selection_depth_layout = new QFormLayout(drag_selection_depth_group); + + _drag_selection_depth_spin = new QDoubleSpinBox(this); + _drag_selection_depth_spin->setRange(1.0f, 3000.0f); + _drag_selection_depth_spin->setDecimals(2); + _drag_selection_depth_spin->setValue(_drag_selection_depth); + + _drag_selection_depth_slider = new QSlider(Qt::Orientation::Horizontal, this); + _drag_selection_depth_slider->setRange(1.0f, 3000.0f); + _drag_selection_depth_slider->setSliderPosition(_drag_selection_depth); + + drag_selection_depth_layout->addRow(_drag_selection_depth_slider); + drag_selection_depth_layout->addRow(_drag_selection_depth_spin); + layout->addWidget(drag_selection_depth_group); + auto *copyBox = new ExpanderWidget( this); copyBox->setExpanderTitle("Copy options"); copyBox->setExpanded(_settings->value ("object_editor/copy_options", false).toBool()); @@ -224,7 +240,7 @@ namespace Noggit multi_select_movement_layout->addRow(multi_select_movement_cb); multi_select_movement_layout->addRow(object_median_pivot_point); - auto *selectionOptionsBox = new ExpanderWidget( this); + auto *selectionOptionsBox = new ExpanderWidget(this); selectionOptionsBox->setExpanderTitle("Movement Options"); selectionOptionsBox->setExpanded(_settings->value ("object_editor/movement_options", false).toBool()); @@ -347,6 +363,23 @@ namespace Noggit } ); + connect(_drag_selection_depth_spin, qOverload(&QDoubleSpinBox::valueChanged) + , [&] (double v) + { + _drag_selection_depth = v; + QSignalBlocker const blocker(_drag_selection_depth_slider); + _drag_selection_depth_slider->setSliderPosition((int)std::round(v)); + } + ); + connect(_drag_selection_depth_slider, &QSlider::valueChanged + , [&](int v) + { + _drag_selection_depth = v; + QSignalBlocker const blocker(_drag_selection_depth_spin); + _drag_selection_depth_spin->setValue(v); + } + ); + connect ( rotRangeStart, qOverload (&QDoubleSpinBox::valueChanged) , [=] (double v) { diff --git a/src/noggit/ui/ObjectEditor.h b/src/noggit/ui/ObjectEditor.h index 858e7202..e97668de 100755 --- a/src/noggit/ui/ObjectEditor.h +++ b/src/noggit/ui/ObjectEditor.h @@ -77,6 +77,8 @@ namespace Noggit float brushRadius() const { return _radius; } + float drag_selection_depth() const { return _drag_selection_depth; } + model_import *modelImport; rotation_editor* rotationEditor; helper_models* helper_models_widget; @@ -84,11 +86,14 @@ namespace Noggit private: float _radius = 0.01f; + float _drag_selection_depth = 100.0f; MapView* _map_view; QSlider* _radius_slider; QDoubleSpinBox* _radius_spin; + QSlider* _drag_selection_depth_slider; + QDoubleSpinBox* _drag_selection_depth_spin; QSettings* _settings;