Files
noggit-red/src/noggit/tools/ObjectTool.cpp
2024-09-12 21:28:04 +00:00

969 lines
40 KiB
C++

// This file is part of Noggit3, licensed under GNU General Public License (version 3).
#include "ObjectTool.hpp"
#include <noggit/ActionManager.hpp>
#include <noggit/MapView.h>
#include <noggit/Input.hpp>
#include <noggit/ui/ObjectEditor.h>
#include <noggit/ui/tools/AssetBrowser/Ui/AssetBrowser.hpp>
#include <noggit/ui/ModelImport.h>
#include <noggit/ui/RotationEditor.h>
#include <noggit/ui/HelperModels.h>
#include <noggit/ui/tools/ToolPanel/ToolPanel.hpp>
#include <noggit/ui/object_palette.hpp>
#include <noggit/ui/windows/noggitWindow/NoggitWindow.hpp>
namespace Noggit
{
ObjectTool::ObjectTool(MapView* mapView)
: Tool{ mapView }
{
setupHotkeys();
}
ObjectTool::~ObjectTool()
{
delete _objectEditor;
}
unsigned int ObjectTool::actionModality() const
{
unsigned int actionModality = 0;
if (_moveObject)
actionModality |= Noggit::ActionModalityControllers::eMMB;
if (_keys)
actionModality |= Noggit::ActionModalityControllers::eSCALE;
if (_keyr)
actionModality |= Noggit::ActionModalityControllers::eROTATE;
return actionModality;
}
char const* ObjectTool::name() const
{
return "Object Editor";
}
editing_mode ObjectTool::editingMode() const
{
return editing_mode::object;
}
Ui::FontNoggit::Icons ObjectTool::icon() const
{
return Ui::FontNoggit::TOOL_OBJECT_EDITOR;
}
void ObjectTool::setupUi(Ui::Tools::ToolPanel* toolPanel)
{
auto mv = mapView();
// initialize some saved defaults
_object_paste_params.rotate_on_terrain = mv->settings()->value("paste_params/rotate_on_terrain", true).toBool();
/* Tool */
_objectEditor = new Noggit::Ui::object_editor(mv
, mv->getWorld()
, &_move_model_to_cursor_position
, &_snap_multi_selection_to_ground
, &_use_median_pivot_point
, &_object_paste_params
, &_rotate_along_ground
, &_rotate_along_ground_smooth
, &_rotate_along_ground_random
, &_move_model_snap_to_objects
, mv
);
toolPanel->registerTool(this, _objectEditor);
/* Additional tools */
/* Area selection */
_area_selection = new QRubberBand(QRubberBand::Rectangle, mv);
/* Object Palette */
_object_palette = new Noggit::Ui::ObjectPalette(mv, mv->project(), mv);
_object_palette->hide();
// Dock
_object_palette_dock = new QDockWidget("Object Palette", mv);
_object_palette_dock->setFeatures(QDockWidget::DockWidgetMovable
| QDockWidget::DockWidgetFloatable
| QDockWidget::DockWidgetClosable
);
_object_palette_dock->setWidget(_object_palette);
_object_palette_dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
mv->mainWindow()->addDockWidget(Qt::BottomDockWidgetArea, _object_palette_dock);
_object_palette_dock->hide();
// End Dock
QObject::connect(_object_palette_dock, &QDockWidget::visibilityChanged,
[=](bool visible)
{
if (mv->isUiHidden())
return;
mv->settings()->setValue("map_view/object_palette", visible);
mv->settings()->sync();
});
QObject::connect(mapView(), &MapView::rotationChanged, [=] {
updateRotationEditor();
});
QObject::connect(_objectEditor, &Ui::object_editor::objectPaletteBtnPressed, [=] {
_object_palette_dock->setVisible(_object_palette_dock->isHidden());
});
QObject::connect(mapView(), &MapView::selectionUpdated, [=](auto) {
_objectEditor->update_selection_ui(mapView()->getWorld());
});
using AssetBrowser = Noggit::Ui::Tools::AssetBrowser::Ui::AssetBrowserWidget;
QObject::connect(mapView()->getAssetBrowserWidget(), &AssetBrowser::selectionChanged, [=](std::string const& path) {
if (_objectEditor->isVisible()) _objectEditor->copy(path);
});
QObject::connect(_object_palette, &Ui::ObjectPalette::selected, [=](std::string str) {
_objectEditor->copy(str);
});
}
ToolDrawParameters ObjectTool::drawParameters() const
{
return
{
.radius = _objectEditor->brushRadius(),
};
}
float ObjectTool::brushRadius() const
{
return _objectEditor->brushRadius();
}
bool ObjectTool::useMultiselectionPivot() const
{
return _use_median_pivot_point.get();
}
bool ObjectTool::useMedianPivotPoint() const
{
return _use_median_pivot_point.get();
}
void ObjectTool::registerMenuItems(QMenu* menu)
{
addMenuTitle(menu, name());
addMenuItem(menu, "Last M2 from WMV", QKeySequence{ "Shift+V" }, [this] { _objectEditor->import_last_model_from_wmv(eMODEL); });
addMenuItem(menu, "Last WMO from WMV", QKeySequence{ "Alt+V" }, [this] { _objectEditor->import_last_model_from_wmv(eWMO); });
addMenuItem(menu, "Helper models", [this] {_objectEditor->helper_models_widget->show(); });
}
void ObjectTool::registerContextMenuItems(QMenu* menu)
{
auto world = mapView()->getWorld();
addMenuTitle(menu, name());
bool has_selected_objects = world->get_selected_model_count();
bool has_copied_objects = _objectEditor->clipboardSize();
addMenuItem(menu, "Copy Object(s)", QKeySequence::Copy, has_selected_objects, [=] { _objectEditor->copy_current_selection(world); });
addMenuItem(menu, "Paste Object(s)", QKeySequence::Paste, has_copied_objects, [=] {
auto mv = mapView();
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_ADDED);
_objectEditor->pasteObject(mv->cursorPosition(), mv->getCamera()->position, world, &_object_paste_params);
NOGGIT_ACTION_MGR->endAction();
});
addMenuItem(menu, "Delete Object(s)", QKeySequence::Delete, has_selected_objects, [=] {
auto mv = mapView();
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_REMOVED);
mv->DeleteSelectedObjects();
NOGGIT_ACTION_MGR->endAction();
});
addMenuItem(menu, "Duplicate Object(s)", { "CTRL+B" }, has_copied_objects, [=] {
auto mv = mapView();
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_ADDED);
_objectEditor->copy_current_selection(world);
_objectEditor->pasteObject(mv->cursorPosition(), mv->getCamera()->position, world, &_object_paste_params);
NOGGIT_ACTION_MGR->endAction();
});
addMenuSeperator(menu);
addMenuItem(menu, "Select all Like Selected", "Warning : Doing actions on models overlapping unloaded tiles can cause crash",
world->get_selected_model_count() == 1, [=] {
auto world = mapView()->getWorld();
auto last_entry = world->get_last_selected_model();
if (last_entry)
{
if (!last_entry.value().index() == eEntry_Object)
return;
auto obj = std::get<selected_object_type>(last_entry.value());
auto model_name = obj->instance_model()->file_key().filepath();
// auto models = world->get_models_by_filename()[model_name];
// if changing this, make sure to check for duplicate instead // if (!world->is_selected(instance))
world->reset_selection();
if (obj->which() == eMODEL)
{
world->getModelInstanceStorage().for_each_m2_instance([&](ModelInstance& model_instance)
{
if (model_instance.instance_model()->file_key().filepath() == model_name)
{
world->add_to_selection(&model_instance);
}
});
}
else if (obj->which() == eWMO)
world->getModelInstanceStorage().for_each_wmo_instance([&](WMOInstance& wmo_instance)
{
if (wmo_instance.instance_model()->file_key().filepath() == model_name)
{
// objects_to_select.push_back(wmo_instance.uid);
world->add_to_selection(&wmo_instance);
}
});
// for (auto uid_it = objects_to_select.begin(); uid_it != objects_to_select.end(); uid_it++)
// {
// auto instance = world->getObjectInstance(*uid_it);
// // if (!world->is_selected(instance))
// world->add_to_selection(instance);
// }
}
});
addMenuItem(menu, "Hide Selected Objects", Qt::Key_H, has_selected_objects, [=] {
if (world->has_selection())
{
for (auto& obj : world->get_selected_objects())
{
if (obj->which() == eMODEL)
static_cast<ModelInstance*>(obj)->model->hide();
else if (obj->which() == eWMO)
static_cast<WMOInstance*>(obj)->wmo->hide();
}
}
});
addMenuItem(menu, "Hide Unselected Objects (NOT IMPLEMENTED)", [] {});
// QAction action_2("Show Hidden", this);
addMenuItem(menu, "Add Object To Palette", QKeySequence::UnknownKey, world->get_selected_model_count(),
[=] {
auto last_entry = world->get_last_selected_model();
if (last_entry)
{
if (!last_entry.value().index() == eEntry_Object)
return;
_object_palette_dock->setVisible(true);
auto obj = std::get<selected_object_type>(last_entry.value());
auto model_name = obj->instance_model()->file_key().filepath();
_object_palette->addObjectByFilename(model_name.c_str());
}
});
addMenuSeperator(menu);
// allow replacing all selected?
addMenuItem(menu, "Replace Models (By Clipboard)", "Replace the currently selected objects by the object in the clipboard (There must only be one!). M2s can only be replaced by m2s",
has_selected_objects && _objectEditor->clipboardSize() == 1, [=] {
auto mv = mapView();
mv->makeCurrent();
OpenGL::context::scoped_setter const _(::gl, mv->context());
if (mv->get_editing_mode() != editing_mode::object && NOGGIT_CUR_ACTION)
return;
if (!_objectEditor->clipboardSize())
return;
// verify this
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_ADDED | Noggit::ActionFlags::eOBJECTS_REMOVED);
// get the model to replace by
auto replace_select = _objectEditor->getClipboard().front();
auto replacement_obj = std::get<selected_object_type>(replace_select);
auto& replace_path = replacement_obj->instance_model()->file_key().filepath();
std::vector<SceneObject*> objects_to_delete;
// iterate selection (objects to replace)
std::vector<selected_object_type> selected_objects = world->get_selected_objects();
for (SceneObject* old_obj : selected_objects)
{
if (old_obj->instance_model()->file_key().filepath() == replace_path)
continue;
math::degrees::vec3 source_rot(math::degrees(0)._, math::degrees(0)._, math::degrees(0)._);
source_rot = old_obj->dir;
float source_scale = old_obj->scale;
glm::vec3 source_pos = old_obj->pos;
// world->deleteInstance(old_obj->uid);
objects_to_delete.emplace_back(old_obj);
if (replacement_obj->which() == eWMO)
{
// auto replace_wmo = static_cast<WMOInstance*>(replacement_obj);
// auto source_wmo = static_cast<WMOInstance*>(old_obj);
auto new_obj = world->addWMOAndGetInstance(replace_path, source_pos, source_rot, true);
new_obj->wmo->wait_until_loaded();
new_obj->wmo->waitForChildrenLoaded();
new_obj->recalcExtents();
}
else if (replacement_obj->which() == eMODEL)
{
// auto replace_m2 = static_cast<ModelInstance*>(replacement_obj);
// auto source_m2 = static_cast<ModelInstance*>(source_obj);
// Just swapping model
// Issue : doesn't work with actions
// world->updateTilesEntry(entry, model_update::remove);
// source_m2->model = scoped_model_reference(replace_path, _context);
// source_m2->recalcExtents();
// world->updateTilesEntry(entry, model_update::add);
auto new_obj = world->addM2AndGetInstance(replace_path
, source_pos
, source_scale
, source_rot
, &_object_paste_params
, true
, true
);
new_obj->model->wait_until_loaded();
new_obj->model->waitForChildrenLoaded();
new_obj->recalcExtents();
}
}
// this would also delete models that got skipped
// world->delete_selected_models();
world->deleteObjects(objects_to_delete, true);
world->reset_selection();
NOGGIT_ACTION_MGR->endAction();
});
addMenuItem(menu, "Snap Selected To Ground", Qt::Key_PageDown, has_selected_objects, [=] {
NOGGIT_ACTION_MGR->beginAction(mapView(), Noggit::ActionFlags::eOBJECTS_TRANSFORMED);
mapView()->snap_selected_models_to_the_ground();
NOGGIT_ACTION_MGR->endAction();
});
addMenuItem(menu, "Save objects coords(to file)", QKeySequence::UnknownKey, has_selected_objects, [=] {
if (world->has_selection() && world->get_selected_model_count())
{
std::stringstream obj_data;
for (auto& obj : world->get_selected_objects())
{
obj_data << "\"Object : " << obj->instance_model()->file_key().filepath() << "(UID :" << obj->uid << ")\"," << std::endl;
obj_data << "\"Scale : " << obj->scale << "\"," << std::endl;
// coords string in ts-wow format
obj_data << "\"Coords(server): {map:" << world->getMapID() << ",x:" << (ZEROPOINT - obj->pos.z) << ",y:" << (ZEROPOINT - obj->pos.x)
<< ",z:" << obj->pos.y << ",o:";
float server_rot = 2 * glm::pi<float>() - glm::pi<float>() / 180.0 * (float(obj->dir.y) < 0 ? fabs(float(obj->dir.y)) + 180.0 : fabs(float(obj->dir.y) - 180.0));
// float server_rot = glm::radians(obj->dir.y) + glm::radians(180.f);
obj_data << server_rot << "}\"," << std::endl;
/// converting db gobject rotation to noggit. Keep commented for later usage
/*
glm::quat test_db_quat = glm::quat(1.0, 1.0, 1.0, 1.0);
test_db_quat.x = 0.607692, test_db_quat.y = -0.361538, test_db_quat.z = 0.607693, test_db_quat.w = 0.361539;
glm::vec3 rot_euler = glm::eulerAngles(test_db_quat);
glm::vec3 rot_degrees = glm::degrees(rot_euler);
rot_degrees = glm::vec3(rot_degrees.y, rot_degrees.z - 180.f, rot_degrees.x); // final noggit coords
*/
glm::quat rot_quat = glm::quat(glm::vec3(glm::radians(obj->dir.z), glm::radians(obj->dir.x), server_rot));
auto normalized_quat = glm::normalize(rot_quat);
obj_data << "\"Rotation (server quaternion): {x:" << normalized_quat.x << ",y:" << normalized_quat.y << ",z:" << normalized_quat.z
<< ",w:" << normalized_quat.w << "}\"," << std::endl << "\n";
}
std::ofstream f("saved_objects_data.txt", std::ios_base::app);
f << "\"Saved " << world->get_selected_model_count() << " objects at : " << QDateTime::currentDateTime().toString("dd MMMM yyyy hh:mm:ss").toStdString() << "\"" << std::endl;
f << obj_data.str();
f.close();
}});
addMenuSeperator(menu);
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)
{
// 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;
}
}
}
}
// TODO
addMenuItem(menu, "Group Selected Objects", QKeySequence::UnknownKey, groupable, [=] {
// 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();
});
bool group_selected = false;
for (auto& group : world->_selection_groups)
{
if (group.isSelected())
{
group_selected = true;
break;
}
}
addMenuItem(menu, "Ungroup Selected Objects", QKeySequence::UnknownKey, group_selected, [=] {
world->clear_selection_groups();
});
}
void ObjectTool::onSelected()
{
_object_palette_dock->setVisible(!mapView()->isUiHidden() && mapView()->settings()->value("map_view/object_palette", false).toBool());
}
void ObjectTool::onDeselected()
{
QSignalBlocker blocker{ _object_palette_dock };
_objectEditor->modelImport->hide();
_objectEditor->rotationEditor->hide();
_object_palette_dock->hide();
_moveObject = false;
}
void ObjectTool::onTick(float deltaTime, TickParameters const& params)
{
unsigned action_modality = 0;
float numpad_moveratio = 0.001f;
if (mapView()->getWorld()->has_selection())
{
auto mv = mapView();
auto world = mv->getWorld();
// reset numpad_moveratio when no numpad key is pressed
if (!(_keyx != 0 || _keyy != 0 || _keyz != 0 || _keyr != 0 || _keys != 0))
{
numpad_moveratio = 0.5f;
}
else // Set move scale and rotate for numpad keys
{
if (params.mod_ctrl_down && params.mod_shift_down)
{
numpad_moveratio += 0.5f;
}
else if (params.mod_shift_down)
{
numpad_moveratio += 0.05f;
}
else if (params.mod_ctrl_down)
{
numpad_moveratio += 0.005f;
}
}
if (_keys != 0.f)
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eSCALE);
world->scale_selected_models(_keys * numpad_moveratio / 50.f, World::m2_scaling_type::add);
updateRotationEditor();
}
if (_keyr != 0.f)
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eROTATE);
world->rotate_selected_models(math::degrees(0.f)
, math::degrees(_keyr * numpad_moveratio * 5.f)
, math::degrees(0.f)
, _use_median_pivot_point.get()
);
updateRotationEditor();
}
if (_moveObject)
{
if (params.mod_alt_down)
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eALT
| Noggit::ActionModalityControllers::eMMB);
world->scale_selected_models(std::pow(2.f, _mv * 4.f), World::m2_scaling_type::mult);
}
else if (params.mod_shift_down)
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eSHIFT
| Noggit::ActionModalityControllers::eMMB);
world->move_selected_models(0.f, _mv * 80.f, 0.f);
}
else if (params.mod_ctrl_down)
{
// do nothing
}
else
{
bool snapped = false;
bool snapped_to_object = false;
if (world->has_multiple_model_selected())
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eMMB);
world->set_selected_models_pos(mv->cursorPosition(), false);
if (_snap_multi_selection_to_ground.get())
{
mv->snap_selected_models_to_the_ground();
snapped = true;
}
}
else
{
if (!_move_model_to_cursor_position.get())
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eMMB);
if ((_mh <= 0.01f && _mh >= -0.01f) && (_mv <= 0.01f && _mv >= -0.01f))
{
glm::vec3 _vec = (_mh * params.dirUp + _mv * params.dirRight);
world->move_selected_models(_vec * 500.f);
}
}
else
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eMMB);
if (_move_model_to_cursor_position.get() || _move_model_snap_to_objects.get())
{
selection_result results(mv->intersect_result(false));
if (!results.empty())
{
for (auto result = results.begin(); result != results.end(); result++)
{
auto const& hit(result->second);
bool is_selected_model = false;
// if a terrain is found first use that (terrain cursor pos position updated on move already)
if (hit.index() == eEntry_MapChunk && _move_model_to_cursor_position.get())
{
break;
}
if (hit.index() == eEntry_Object && _move_model_snap_to_objects.get())
{
auto obj_hit = std::get<selected_object_type>(hit);
auto obj_hit_type = obj_hit->which();
// don't snap to animated models
if (obj_hit_type == eMODEL)
{
auto m2_model_hit = static_cast<ModelInstance*>(obj_hit);
if (m2_model_hit->model->animated_mesh())
continue;
}
// find and ignore current object/selected models or it will keep snaping to itself
for (auto& entry : world->current_selection())
{
auto type = entry.index();
if (type == eEntry_Object)
{
auto& selection_obj = std::get<selected_object_type>(entry);
if (selection_obj->uid == obj_hit->uid)
{
is_selected_model = true;
break;
}
}
}
if (is_selected_model)
continue;
auto hit_pos = mv->intersect_ray().position(result->first);
mv->cursorPosition(hit_pos);
snapped_to_object = true;
// TODO : rotate objects to objects normal
// if (_rotate_doodads_along_doodads.get())
// world->rotate_selected_models_to_object_normal(_rotate_along_ground_smooth.get(), obj_hit, hit_pos, glm::transpose(model_view()), _rotate_doodads_along_wmos.get());
break;
}
}
}
world->set_selected_models_pos(mv->cursorPosition(), false);
snapped = true;
}
}
}
if (snapped && _rotate_along_ground.get())
{
if (!snapped_to_object)
world->rotate_selected_models_to_ground_normal(_rotate_along_ground_smooth.get());
if (_rotate_along_ground_random.get())
{
float minX = 0, maxX = 0, minY = 0, maxY = 0, minZ = 0, maxZ = 0;
if (mv->settings()->value("model/random_rotation", false).toBool())
{
minY = _object_paste_params.minRotation;
maxY = _object_paste_params.maxRotation;
}
if (mv->settings()->value("model/random_tilt", false).toBool())
{
minX = _object_paste_params.minTilt;
maxX = _object_paste_params.maxTilt;
minZ = minX;
maxZ = maxX;
}
world->rotate_selected_models_randomly(
minX,
maxX,
minY,
maxY,
minZ,
maxZ);
if (mv->settings()->value("model/random_size", false).toBool())
{
float min = _object_paste_params.minScale;
float max = _object_paste_params.maxScale;
world->scale_selected_models(misc::randfloat(min, max), World::m2_scaling_type::set);
}
}
}
}
updateRotationEditor();
}
/* TODO: Numpad for action system
if (_keyx != 0.f || _keyy != 0.f || _keyz != 0.f)
{
world->move_selected_models(_keyx * numpad_moveratio, _keyy * numpad_moveratio, _keyz * numpad_moveratio);
updateRotationEditor();
}
*/
if (mv->isRotatingCamera())
{
if (params.mod_ctrl_down) // X
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eCTRL
| Noggit::ActionModalityControllers::eRMB);
world->rotate_selected_models(math::degrees(_rh + _rv)
, math::degrees(0.f)
, math::degrees(0.f)
, _use_median_pivot_point.get()
);
}
if (params.mod_shift_down) // Y
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eSHIFT
| Noggit::ActionModalityControllers::eRMB);
world->rotate_selected_models(math::degrees(0.f)
, math::degrees(_rh + _rv)
, math::degrees(0.f)
, _use_median_pivot_point.get()
);
}
if (params.mod_alt_down) // Z
{
NOGGIT_ACTION_MGR->beginAction(mv, Noggit::ActionFlags::eOBJECTS_TRANSFORMED,
Noggit::ActionModalityControllers::eALT
| Noggit::ActionModalityControllers::eRMB);
world->rotate_selected_models(math::degrees(0.f)
, math::degrees(0.f)
, math::degrees(_rh + _rv)
, _use_median_pivot_point.get()
);
}
updateRotationEditor();
}
}
_mh = 0;
_mv = 0;
_rh = 0;
_rv = 0;
}
void ObjectTool::onMousePress(MousePressParameters const& params)
{
if (params.button == Qt::MouseButton::LeftButton && !params.mod_ctrl_down)
{
_area_selection->setGeometry(QRect(_drag_start_pos, QSize()));
_area_selection->show();
_drag_start_pos = params.mouse_position;
mapView()->invalidate();
}
if (params.button == Qt::MouseButton::MiddleButton)
{
_moveObject = true;
}
}
void ObjectTool::onMouseRelease(MouseReleaseParameters const& params)
{
if (params.button == Qt::MouseButton::MiddleButton)
{
_moveObject = false;
return;
}
if (params.button != Qt::MouseButton::LeftButton || params.mod_ctrl_down)
{
return;
}
auto drag_end_pos = params.mouse_position;
if (_drag_start_pos != drag_end_pos && !ImGuizmo::IsUsing())
{
const std::array<glm::vec2, 2> 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);
mapView()->selectObjects(selection_box, 3000.0f);
}
else // Do normal selection when we just clicked
{
mapView()->doSelection(false);
}
_area_selection->hide();
}
void ObjectTool::onMouseMove(MouseMoveParameters const& params)
{
auto mapView = this->mapView();
if (_moveObject)
{
_mh = -mapView->aspect_ratio() * params.relative_movement.dx() / static_cast<float>(mapView->width());
_mv = -params.relative_movement.dy() / static_cast<float>(mapView->height());
}
else
{
_mh = 0.0f;
_mv = 0.0f;
}
if (params.mod_shift_down || params.mod_ctrl_down || params.mod_alt_down || params.mod_space_down)
{
_rh = params.relative_movement.dx() / XSENS * 5.0f;
_rv = params.relative_movement.dy() / YSENS * 5.0f;
}
if (params.left_mouse)
{
if (params.mod_alt_down && !params.mod_shift_down && !params.mod_ctrl_down)
{
_objectEditor->changeRadius(params.relative_movement.dx() / XSENS);
}
if (!params.mod_alt_down && params.displayMode == display_mode::in_3D && !ImGuizmo::IsUsing())
{
_area_selection->setGeometry(QRect(_drag_start_pos, params.mouse_position).normalized());
mapView->invalidate();
}
if (params.mod_shift_down || params.mod_ctrl_down)
{
mapView->doSelection(false, true);
}
}
}
void ObjectTool::hidePopups()
{
_objectEditor->modelImport->hide();
_objectEditor->rotationEditor->hide();
_objectEditor->helper_models_widget->hide();
_object_palette_dock->hide();
}
void ObjectTool::onFocusLost()
{
_keyx = 0;
_keyz = 0;
_keyy = 0;
_keyr = 0;
_keys = 0;
_moveObject = false;
}
void ObjectTool::setupHotkeys()
{
auto mapView = this->mapView();
addHotkey("copySelection"_hash, Hotkey{
.onPress = [=] { _objectEditor->copy_current_selection(mapView->getWorld()); },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object && !NOGGIT_CUR_ACTION; },
});
addHotkey("paste"_hash, Hotkey{
.onPress = [=] {
NOGGIT_ACTION_MGR->beginAction(mapView, Noggit::ActionFlags::eOBJECTS_ADDED);
_objectEditor->pasteObject(mapView->cursorPosition(), mapView->getCamera()->position, mapView->getWorld(), &_object_paste_params);
NOGGIT_ACTION_MGR->endAction();
},
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object && !NOGGIT_CUR_ACTION; },
});
addHotkey("importM2FromWmv"_hash, Hotkey{
.onPress = [=] { _objectEditor->import_last_model_from_wmv(eMODEL); },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object && !NOGGIT_CUR_ACTION; },
});
addHotkey("importWmoFromWmv"_hash, Hotkey{
.onPress = [=] { _objectEditor->import_last_model_from_wmv(eWMO); },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object && !NOGGIT_CUR_ACTION; },
});
addHotkey("duplacteSelection"_hash, Hotkey{
.onPress = [=] {
NOGGIT_ACTION_MGR->beginAction(mapView, Noggit::ActionFlags::eOBJECTS_ADDED);
_objectEditor->copy_current_selection(mapView->getWorld());
_objectEditor->pasteObject(mapView->cursorPosition(), mapView->getCamera()->position, mapView->getWorld(), &_object_paste_params);
NOGGIT_ACTION_MGR->endAction();
},
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object && !NOGGIT_CUR_ACTION; },
});
addHotkey("togglePasteMode"_hash, Hotkey{
.onPress = [=] { _objectEditor->togglePasteMode(); },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("moveSelectedDown"_hash, Hotkey{
.onPress = [=] { _keyx = 1; },
.onRelease = [=] { _keyx = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("moveSelectedUp"_hash, Hotkey{
.onPress = [=] { _keyx = -1; },
.onRelease = [=] { _keyx = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("moveSelectedLeft"_hash, Hotkey{
.onPress = [=] { _keyz = 1; },
.onRelease = [=] { _keyz = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("moveSelectedRight"_hash, Hotkey{
.onPress = [=] { _keyz = -1; },
.onRelease = [=] { _keyz = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("rotateSelectedPitchCcw"_hash, Hotkey{
.onPress = [=] { _keyy = 1; },
.onRelease = [=] { _keyy = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("rotateSelectedPitchCw"_hash, Hotkey{
.onPress = [=] { _keyy = -1; },
.onRelease = [=] { _keyy = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("rotateSelectedYawCcw"_hash, Hotkey{
.onPress = [=] { _keyr = 1; },
.onRelease = [=] {_keyr = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("rotateSelectedYawCw"_hash, Hotkey{
.onPress = [=] {_keyr = -1; },
.onRelease = [=] {_keyr = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("increaseSelectedScale"_hash, Hotkey{
.onPress = [=] { _keys = 1; },
.onRelease = [=] { _keys = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
addHotkey("decreaseSelectedScale"_hash, Hotkey{
.onPress = [=] { _keys = -1; },
.onRelease = [=] { _keys = 0; },
.condition = [=] { return mapView->get_editing_mode() == editing_mode::object; },
});
}
void ObjectTool::updateRotationEditor()
{
_objectEditor->rotationEditor->updateValues(mapView()->getWorld());
}
}