3071 lines
94 KiB
C++
3071 lines
94 KiB
C++
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
|
|
|
|
#include <math/projection.hpp>
|
|
#include <noggit/Brush.h> // brush
|
|
#include <noggit/DBC.h>
|
|
#include <noggit/Log.h>
|
|
#include <noggit/MapChunk.h>
|
|
#include <noggit/MapView.h>
|
|
#include <noggit/Misc.h>
|
|
#include <noggit/ModelManager.h> // ModelManager
|
|
#include <noggit/TextureManager.h> // TextureManager, Texture
|
|
#include <noggit/WMOInstance.h> // WMOInstance
|
|
#include <noggit/World.h>
|
|
#include <noggit/map_index.hpp>
|
|
#include <noggit/uid_storage.hpp>
|
|
#include <noggit/ui/CurrentTexture.h>
|
|
#include <noggit/ui/CursorSwitcher.h> // cursor_switcher
|
|
#include <noggit/ui/DetailInfos.h> // detailInfos
|
|
#include <noggit/ui/FlattenTool.hpp>
|
|
#include <noggit/ui/Help.h>
|
|
#include <noggit/ui/HelperModels.h>
|
|
#include <noggit/ui/ModelImport.h>
|
|
#include <noggit/ui/ObjectEditor.h>
|
|
#include <noggit/ui/RotationEditor.h>
|
|
#include <noggit/ui/TexturePicker.h>
|
|
#include <noggit/ui/TexturingGUI.h>
|
|
#include <noggit/ui/Toolbar.h> // noggit::ui::toolbar
|
|
#include <noggit/ui/Water.h>
|
|
#include <noggit/ui/ZoneIDBrowser.h>
|
|
#include <noggit/ui/main_window.hpp>
|
|
#include <noggit/ui/minimap_widget.hpp>
|
|
#include <noggit/ui/shader_tool.hpp>
|
|
#include <noggit/ui/terrain_tool.hpp>
|
|
#include <noggit/ui/texture_swapper.hpp>
|
|
#include <noggit/ui/texturing_tool.hpp>
|
|
#include <noggit/ui/hole_tool.hpp>
|
|
#include <noggit/ui/texture_palette_small.hpp>
|
|
#include <opengl/scoped.hpp>
|
|
|
|
#include "revision.h"
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
|
|
#include <QtCore/QTimer>
|
|
#include <QtGui/QKeyEvent>
|
|
#include <QtGui/QMouseEvent>
|
|
#include <QtWidgets/QApplication>
|
|
#include <QtWidgets/QMenuBar>
|
|
#include <QtWidgets/QMessageBox>
|
|
#include <QtWidgets/QPushButton>
|
|
#include <QtWidgets/QStatusBar>
|
|
#include <QWidgetAction>
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <regex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
|
|
static const float XSENS = 15.0f;
|
|
static const float YSENS = 15.0f;
|
|
|
|
void MapView::set_editing_mode (editing_mode mode)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
|
|
objectEditor->modelImport->hide();
|
|
objectEditor->rotationEditor->hide();
|
|
TexturePalette->hide();
|
|
TexturePicker->hide();
|
|
_texture_palette_dock->hide();
|
|
|
|
MoveObj = false;
|
|
_world->reset_selection();
|
|
_rotation_editor_need_update = true;
|
|
|
|
if (!ui_hidden)
|
|
{
|
|
setToolPropertyWidgetVisibility(mode);
|
|
}
|
|
|
|
terrainMode = mode;
|
|
_toolbar->check_tool (mode);
|
|
|
|
this->activateWindow();
|
|
}
|
|
|
|
void MapView::setToolPropertyWidgetVisibility(editing_mode mode)
|
|
{
|
|
for (auto dock : _tool_properties_docks)
|
|
{
|
|
dock->hide();
|
|
}
|
|
|
|
switch (mode)
|
|
{
|
|
case editing_mode::ground:
|
|
_terrain_tool_dock->setVisible(!ui_hidden);
|
|
break;
|
|
case editing_mode::flatten_blur:
|
|
_flatten_blur_dock->setVisible(!ui_hidden);
|
|
break;
|
|
case editing_mode::paint:
|
|
_texturing_dock->setVisible(!ui_hidden);
|
|
break;
|
|
case editing_mode::areaid:
|
|
_areaid_editor_dock->setVisible(!ui_hidden);
|
|
break;
|
|
case editing_mode::water:
|
|
_water_editor_dock->setVisible(!ui_hidden);
|
|
break;
|
|
case editing_mode::mccv:
|
|
_vertex_shading_dock->setVisible(!ui_hidden);
|
|
break;
|
|
case editing_mode::object:
|
|
_object_editor_dock->setVisible(!ui_hidden);
|
|
break;
|
|
case editing_mode::holes:
|
|
_hole_tool_dock->setVisible(!ui_hidden);
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
void MapView::ResetSelectedObjectRotation()
|
|
{
|
|
for (auto& selection : _world->current_selection())
|
|
{
|
|
if (selection.which() == eEntry_WMO)
|
|
{
|
|
WMOInstance* wmo = boost::get<selected_wmo_type>(selection);
|
|
_world->updateTilesWMO(wmo, model_update::remove);
|
|
wmo->resetDirection();
|
|
_world->updateTilesWMO(wmo, model_update::add);
|
|
}
|
|
else if (selection.which() == eEntry_Model)
|
|
{
|
|
ModelInstance* m2 = boost::get<selected_model_type>(selection);
|
|
_world->updateTilesModel(m2, model_update::remove);
|
|
m2->resetDirection();
|
|
m2->recalcExtents();
|
|
_world->updateTilesModel(m2, model_update::add);
|
|
}
|
|
}
|
|
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
void MapView::snap_selected_models_to_the_ground()
|
|
{
|
|
_world->snap_selected_models_to_the_ground();
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
|
|
void MapView::DeleteSelectedObject()
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
|
|
_world->delete_selected_models();
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
|
|
void MapView::changeZoneIDValue (int set)
|
|
{
|
|
_selected_area_id = set;
|
|
}
|
|
|
|
|
|
QWidgetAction* MapView::createTextSeparator(const QString& text)
|
|
{
|
|
auto* pLabel = new QLabel(text);
|
|
//pLabel->setMinimumWidth(this->minimumWidth() - 4);
|
|
pLabel->setAlignment(Qt::AlignCenter);
|
|
auto* separator = new QWidgetAction(this);
|
|
separator->setDefaultWidget(pLabel);
|
|
return separator;
|
|
}
|
|
|
|
|
|
void MapView::createGUI()
|
|
{
|
|
// create tool widgets
|
|
|
|
_terrain_tool_dock = new QDockWidget("Raise / Lower", this);
|
|
terrainTool = new noggit::ui::terrain_tool(_terrain_tool_dock);
|
|
_terrain_tool_dock->setWidget(terrainTool);
|
|
_tool_properties_docks.insert(_terrain_tool_dock);
|
|
|
|
connect(terrainTool
|
|
, &noggit::ui::terrain_tool::updateVertices
|
|
, [this](int vertex_mode, math::degrees const& angle, math::degrees const& orientation)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
|
|
_world->orientVertices(vertex_mode == eVertexMode_Mouse
|
|
? _cursor_pos
|
|
: _world->vertexCenter()
|
|
, angle
|
|
, orientation
|
|
);
|
|
}
|
|
);
|
|
|
|
_flatten_blur_dock = new QDockWidget("Flatten / Blur", this);
|
|
flattenTool = new noggit::ui::flatten_blur_tool(_flatten_blur_dock);
|
|
_flatten_blur_dock->setWidget(flattenTool);
|
|
_tool_properties_docks.insert(_flatten_blur_dock);
|
|
|
|
_texturing_dock = new QDockWidget("Texturing", this);
|
|
texturingTool = new noggit::ui::texturing_tool(&_camera.position, _world.get(), &_show_texture_palette_small_window, _texturing_dock);
|
|
_texturing_dock->setWidget(texturingTool);
|
|
_tool_properties_docks.insert(_texturing_dock);
|
|
|
|
_hole_tool_dock = new QDockWidget("Holes", this);
|
|
holeTool = new noggit::ui::hole_tool(_hole_tool_dock);
|
|
_hole_tool_dock->setWidget(holeTool);
|
|
_tool_properties_docks.insert(_hole_tool_dock);
|
|
|
|
_areaid_editor_dock = new QDockWidget("Area ID", this);
|
|
ZoneIDBrowser = new noggit::ui::zone_id_browser(_areaid_editor_dock);
|
|
_areaid_editor_dock->setWidget(ZoneIDBrowser);
|
|
_tool_properties_docks.insert(_areaid_editor_dock);
|
|
|
|
_water_editor_dock = new QDockWidget("Water", this);
|
|
guiWater = new noggit::ui::water(&_displayed_water_layer, &_display_all_water_layers, _water_editor_dock);
|
|
_water_editor_dock->setWidget(guiWater);
|
|
_tool_properties_docks.insert(_water_editor_dock);
|
|
|
|
|
|
connect(guiWater, &noggit::ui::water::regenerate_water_opacity
|
|
, [this](float factor)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
|
|
_world->autoGenWaterTrans(_camera.position, factor);
|
|
}
|
|
);
|
|
|
|
connect(guiWater, &noggit::ui::water::crop_water
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
|
|
_world->CropWaterADT(_camera.position);
|
|
}
|
|
);
|
|
|
|
_vertex_shading_dock = new QDockWidget("Vertex Shading", this);
|
|
shaderTool = new noggit::ui::shader_tool(shader_color, _vertex_shading_dock);
|
|
_vertex_shading_dock->setWidget(shaderTool);
|
|
_tool_properties_docks.insert(_vertex_shading_dock);
|
|
|
|
_object_editor_dock = new QDockWidget("Object", this);
|
|
objectEditor = new noggit::ui::object_editor(this
|
|
, _world.get()
|
|
, &_move_model_to_cursor_position
|
|
, &_snap_multi_selection_to_ground
|
|
, &_use_median_pivot_point
|
|
, &_object_paste_params
|
|
, _object_editor_dock
|
|
);
|
|
_object_editor_dock->setWidget(objectEditor);
|
|
_tool_properties_docks.insert(_object_editor_dock);
|
|
|
|
|
|
for (auto widget : _tool_properties_docks)
|
|
{
|
|
connect(this, &QObject::destroyed, widget, &QObject::deleteLater);
|
|
}
|
|
|
|
|
|
TexturePalette = new noggit::ui::tileset_chooser(this);
|
|
|
|
connect( texturingTool->texture_swap_tool()->texture_display()
|
|
, &noggit::ui::current_texture::texture_dropped
|
|
, [=] (std::string const& filename)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
|
|
texturingTool->texture_swap_tool()->set_texture(filename);
|
|
}
|
|
);
|
|
|
|
connect( texturingTool->_current_texture
|
|
, &noggit::ui::current_texture::texture_dropped
|
|
, [=] (std::string const& filename)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
|
|
noggit::ui::selected_texture::set(filename);
|
|
}
|
|
);
|
|
connect(texturingTool->_current_texture, &noggit::ui::current_texture::clicked
|
|
, [=]
|
|
{
|
|
TexturePalette->setVisible(!TexturePalette->isVisible());
|
|
}
|
|
);
|
|
connect(TexturePalette, &noggit::ui::tileset_chooser::selected
|
|
, [=](std::string const& filename)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
|
|
noggit::ui::selected_texture::set(filename);
|
|
texturingTool->_current_texture->set_texture(filename);
|
|
}
|
|
);
|
|
|
|
connect(this, &QObject::destroyed, TexturePalette, &QObject::deleteLater);
|
|
|
|
_texture_palette_small = new noggit::ui::texture_palette_small (this);
|
|
_texture_palette_small->hide();
|
|
|
|
connect(_texture_palette_small, &noggit::ui::texture_palette_small::selected
|
|
, [=](std::string const& filename)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
|
|
noggit::ui::selected_texture::set(filename);
|
|
texturingTool->_current_texture->set_texture(filename);
|
|
}
|
|
);
|
|
connect(this, &QObject::destroyed, _texture_palette_small, &QObject::deleteLater);
|
|
|
|
guidetailInfos = new noggit::ui::detail_infos(this);
|
|
guidetailInfos->hide();
|
|
connect(this, &QObject::destroyed, guidetailInfos, &QObject::deleteLater);
|
|
|
|
_keybindings = new noggit::ui::help(this);
|
|
_keybindings->hide();
|
|
connect(this, &QObject::destroyed, _keybindings, &QObject::deleteLater);
|
|
|
|
TexturePicker = new noggit::ui::texture_picker(texturingTool->_current_texture, this);
|
|
TexturePicker->hide();
|
|
connect(this, &QObject::destroyed, TexturePicker, &QObject::deleteLater);
|
|
|
|
connect( TexturePicker
|
|
, &noggit::ui::texture_picker::set_texture
|
|
, [=] (scoped_blp_texture_reference texture)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
noggit::ui::selected_texture::set(std::move(texture));
|
|
}
|
|
);
|
|
connect(TexturePicker, &noggit::ui::texture_picker::shift_left
|
|
, [=]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
TexturePicker->shiftSelectedTextureLeft();
|
|
}
|
|
);
|
|
connect(TexturePicker, &noggit::ui::texture_picker::shift_right
|
|
, [=]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
TexturePicker->shiftSelectedTextureRight();
|
|
}
|
|
);
|
|
|
|
ZoneIDBrowser->setMapID(_world->getMapID());
|
|
connect(ZoneIDBrowser, &noggit::ui::zone_id_browser::selected
|
|
, [this](int area_id) { changeZoneIDValue(area_id); }
|
|
);
|
|
|
|
terrainTool->storeCursorPos(&_cursor_pos);
|
|
|
|
|
|
for (auto dock : _tool_properties_docks)
|
|
{
|
|
_main_window->addDockWidget(Qt::RightDockWidgetArea, dock);
|
|
dock->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
|
dock->setAllowedAreas(Qt::RightDockWidgetArea);
|
|
|
|
connect(dock, &QDockWidget::topLevelChanged
|
|
, this
|
|
, [=]
|
|
{
|
|
dock->adjustSize();
|
|
|
|
for (auto dock_ : _tool_properties_docks)
|
|
{
|
|
if (dock->isFloating() != dock_->isFloating())
|
|
dock_->setFloating(dock->isFloating());
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
if (_settings->value("undock_tool_properties/enabled", 1).toBool())
|
|
{
|
|
for (auto dock : _tool_properties_docks)
|
|
{
|
|
dock->setFloating(true);
|
|
dock->move(_main_window->geometry().topRight().x() - dock->rect().width() - 20, _main_window->geometry().topRight().y() + 40);
|
|
}
|
|
}
|
|
|
|
// create quick access texture palette dock
|
|
|
|
_texture_palette_dock->setFeatures(QDockWidget::DockWidgetMovable
|
|
| QDockWidget::DockWidgetFloatable
|
|
| QDockWidget::DockWidgetClosable
|
|
);
|
|
|
|
_texture_palette_dock->setWidget(_texture_palette_small);
|
|
_texture_palette_dock->setWindowTitle("Quick Access Texture Palette");
|
|
_texture_palette_dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
|
|
_texture_palette_dock->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum));
|
|
|
|
_main_window->addDockWidget(Qt::BottomDockWidgetArea, _texture_palette_dock);
|
|
|
|
if (_settings->value("undock_small_texture_palette/enabled", 1).toBool())
|
|
{
|
|
_texture_palette_dock->setFloating(true);
|
|
_texture_palette_dock->move(_main_window->geometry().bottomLeft().x() + 50, _main_window->geometry().bottomLeft().y() - 200);
|
|
}
|
|
|
|
connect(this, &QObject::destroyed, _texture_palette_dock, &QObject::deleteLater);
|
|
|
|
// create toolbar
|
|
|
|
_toolbar = new noggit::ui::toolbar([this] (editing_mode mode) { set_editing_mode (mode); });
|
|
_main_window->addToolBar(Qt::LeftToolBarArea, _toolbar);
|
|
connect (this, &QObject::destroyed, _toolbar, &QObject::deleteLater);
|
|
|
|
auto file_menu (_main_window->menuBar()->addMenu ("Editor"));
|
|
connect (this, &QObject::destroyed, file_menu, &QObject::deleteLater);
|
|
|
|
auto edit_menu (_main_window->menuBar()->addMenu ("Edit"));
|
|
connect (this, &QObject::destroyed, edit_menu, &QObject::deleteLater);
|
|
|
|
auto assist_menu (_main_window->menuBar()->addMenu ("Assist"));
|
|
connect (this, &QObject::destroyed, assist_menu, &QObject::deleteLater);
|
|
|
|
auto view_menu (_main_window->menuBar()->addMenu ("View"));
|
|
connect (this, &QObject::destroyed, view_menu, &QObject::deleteLater);
|
|
|
|
auto help_menu (_main_window->menuBar()->addMenu ("Help"));
|
|
connect (this, &QObject::destroyed, help_menu, &QObject::deleteLater);
|
|
|
|
#define ADD_ACTION(menu, name, shortcut, on_action) \
|
|
{ \
|
|
auto action (menu->addAction (name)); \
|
|
action->setShortcut (QKeySequence (shortcut)); \
|
|
connect (action, &QAction::triggered, on_action); \
|
|
}
|
|
|
|
#define ADD_ACTION_NS(menu, name, on_action) \
|
|
{ \
|
|
auto action (menu->addAction (name)); \
|
|
connect (action, &QAction::triggered, on_action); \
|
|
}
|
|
|
|
#define ADD_TOGGLE(menu_, name_, shortcut_, property_) \
|
|
do \
|
|
{ \
|
|
QAction* action (new QAction (name_, this)); \
|
|
action->setShortcut (QKeySequence (shortcut_)); \
|
|
action->setCheckable (true); \
|
|
action->setChecked (property_.get()); \
|
|
menu_->addAction (action); \
|
|
connect ( action, &QAction::toggled \
|
|
, &property_, &noggit::bool_toggle_property::set \
|
|
); \
|
|
connect ( &property_, &noggit::bool_toggle_property::changed \
|
|
, action, &QAction::setChecked \
|
|
); \
|
|
} \
|
|
while (false)
|
|
|
|
#define ADD_TOGGLE_NS(menu_, name_, property_) \
|
|
do \
|
|
{ \
|
|
QAction* action (new QAction (name_, this)); \
|
|
action->setCheckable (true); \
|
|
action->setChecked (property_.get()); \
|
|
menu_->addAction (action); \
|
|
connect ( action, &QAction::toggled \
|
|
, &property_, &noggit::bool_toggle_property::set \
|
|
); \
|
|
connect ( &property_, &noggit::bool_toggle_property::changed \
|
|
, action, &QAction::setChecked \
|
|
); \
|
|
} \
|
|
while (false)
|
|
|
|
|
|
|
|
ADD_ACTION (file_menu, "Save current tile", "Ctrl+Shift+S", [this] { save(save_mode::current); });
|
|
ADD_ACTION (file_menu, "Save changed tiles", QKeySequence::Save, [this] { save(save_mode::changed); });
|
|
ADD_ACTION (file_menu, "Save all tiles", "Ctrl+Shift+A", [this] { save(save_mode::all); });
|
|
|
|
ADD_ACTION ( file_menu
|
|
, "Reload tile"
|
|
, "Shift+J"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->reload_tile (_camera.position);
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
);
|
|
|
|
file_menu->addSeparator();
|
|
ADD_ACTION_NS (file_menu, "Force uid check on next opening", [this] { _force_uid_check = true; });
|
|
file_menu->addSeparator();
|
|
|
|
|
|
edit_menu->addSeparator();
|
|
edit_menu->addAction(createTextSeparator("Selected object"));
|
|
edit_menu->addSeparator();
|
|
ADD_ACTION (edit_menu, "Delete", Qt::Key_Delete, [this] { DeleteSelectedObject(); });
|
|
ADD_ACTION (edit_menu, "Reset rotation", "Ctrl+R", [this] { ResetSelectedObjectRotation(); });
|
|
ADD_ACTION (edit_menu, "Set to ground", Qt::Key_PageDown, [this] { snap_selected_models_to_the_ground(); });
|
|
|
|
|
|
edit_menu->addSeparator();
|
|
edit_menu->addAction(createTextSeparator("Options"));
|
|
edit_menu->addSeparator();
|
|
ADD_TOGGLE_NS (edit_menu, "Locked cursor mode", _locked_cursor_mode);
|
|
|
|
|
|
assist_menu->addSeparator();
|
|
assist_menu->addAction(createTextSeparator("Model"));
|
|
assist_menu->addSeparator();
|
|
ADD_ACTION (assist_menu, "Last M2 from WMV", "Shift+V", [this] { objectEditor->import_last_model_from_wmv(eEntry_Model); });
|
|
ADD_ACTION (assist_menu, "Last WMO from WMV", "Alt+V", [this] { objectEditor->import_last_model_from_wmv(eEntry_WMO); });
|
|
ADD_ACTION_NS (assist_menu, "Helper models", [this] { objectEditor->helper_models_widget->show(); });
|
|
|
|
assist_menu->addSeparator();
|
|
assist_menu->addAction(createTextSeparator("Current ADT"));
|
|
assist_menu->addSeparator();
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Set Area ID"
|
|
, [this]
|
|
{
|
|
if (_selected_area_id != -1)
|
|
{
|
|
_world->setAreaID(_camera.position, _selected_area_id, true);
|
|
}
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Clear height map"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->clearHeight(_camera.position);
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Remove texture duplicates"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->removeTexDuplicateOnADT(_camera.position);
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Clear textures"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->clearTextures(_camera.position);
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Clear textures + set base"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->setBaseTexture(_camera.position);
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Clear shadows"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->clear_shadows(_camera.position);
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Clear models"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->clearAllModelsOnADT(_camera.position);
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Clear duplicate models"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->delete_duplicate_model_and_wmo_instances();
|
|
}
|
|
);
|
|
|
|
assist_menu->addSeparator();
|
|
assist_menu->addAction(createTextSeparator("Loaded ADTs"));
|
|
assist_menu->addSeparator();
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Fix gaps"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->fixAllGaps();
|
|
}
|
|
);
|
|
|
|
assist_menu->addSeparator();
|
|
assist_menu->addAction(createTextSeparator("Global"));
|
|
assist_menu->addSeparator();
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Map to big alpha"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->convert_alphamap(true);
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( assist_menu
|
|
, "Map to old alpha"
|
|
, [this]
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
_world->convert_alphamap(false);
|
|
}
|
|
);
|
|
|
|
view_menu->addSeparator();
|
|
view_menu->addAction(createTextSeparator("Drawing"));
|
|
view_menu->addSeparator();
|
|
ADD_TOGGLE (view_menu, "Doodads", Qt::Key_F1, _draw_models);
|
|
ADD_TOGGLE (view_menu, "WMO doodads", Qt::Key_F2, _draw_wmo_doodads);
|
|
ADD_TOGGLE (view_menu, "Terrain", Qt::Key_F3, _draw_terrain);
|
|
ADD_TOGGLE (view_menu, "Water", Qt::Key_F4, _draw_water);
|
|
ADD_TOGGLE (view_menu, "WMOs", Qt::Key_F6, _draw_wmo);
|
|
ADD_TOGGLE (view_menu, "Lines", Qt::Key_F7, _draw_lines);
|
|
ADD_TOGGLE (view_menu, "Map contour infos", Qt::Key_F9, _draw_contour);
|
|
ADD_TOGGLE (view_menu, "Wireframe", Qt::Key_F10, _draw_wireframe);
|
|
ADD_TOGGLE (view_menu, "Toggle Animation", Qt::Key_F11, _draw_model_animations);
|
|
ADD_TOGGLE (view_menu, "Draw fog", Qt::Key_F12, _draw_fog);
|
|
ADD_TOGGLE_NS (view_menu, "Flight Bounds", _draw_mfbo);
|
|
ADD_TOGGLE (view_menu, "Hole lines always on", "Shift+F7", _draw_hole_lines);
|
|
ADD_TOGGLE_NS (view_menu, "Models with box", _draw_models_with_box);
|
|
//! \todo space+h in object mode
|
|
ADD_TOGGLE_NS (view_menu, "Draw hidden models", _draw_hidden_models);
|
|
|
|
view_menu->addSeparator();
|
|
view_menu->addAction(createTextSeparator("Minimap"));
|
|
view_menu->addSeparator();
|
|
|
|
ADD_TOGGLE (view_menu, "Show", Qt::Key_M, _show_minimap_window);
|
|
connect ( &_show_minimap_window, &noggit::bool_toggle_property::changed
|
|
, _minimap_dock, [this]
|
|
{
|
|
if (!ui_hidden)
|
|
_minimap_dock->setVisible(_show_minimap_window.get());
|
|
}
|
|
);
|
|
connect ( _minimap_dock, &QDockWidget::visibilityChanged
|
|
, &_show_minimap_window, &noggit::bool_toggle_property::set
|
|
);
|
|
|
|
ADD_TOGGLE_NS(view_menu, "Show ADT borders", _show_minimap_borders);
|
|
connect ( &_show_minimap_borders, &noggit::bool_toggle_property::changed
|
|
, [this]
|
|
{
|
|
_minimap->draw_boundaries(_show_minimap_borders.get());
|
|
}
|
|
);
|
|
|
|
ADD_TOGGLE_NS(view_menu, "Show light zones", _show_minimap_skies);
|
|
connect ( &_show_minimap_skies, &noggit::bool_toggle_property::changed
|
|
, [this]
|
|
{
|
|
_minimap->draw_skies(_show_minimap_skies.get());
|
|
}
|
|
);
|
|
|
|
view_menu->addSeparator();
|
|
view_menu->addAction(createTextSeparator("Windows"));
|
|
view_menu->addSeparator();
|
|
|
|
|
|
auto hide_widgets = [=] {
|
|
|
|
QWidget* widget_list[] =
|
|
{
|
|
TexturePalette,
|
|
TexturePicker,
|
|
guidetailInfos,
|
|
_cursor_switcher.get(),
|
|
_keybindings,
|
|
_minimap_dock,
|
|
objectEditor->modelImport,
|
|
objectEditor->rotationEditor,
|
|
objectEditor->helper_models_widget,
|
|
_texture_palette_small
|
|
};
|
|
|
|
|
|
if (_main_window->displayed_widgets.empty())
|
|
{
|
|
for (auto widget : widget_list)
|
|
if (widget->isVisible())
|
|
{
|
|
_main_window->displayed_widgets.emplace(widget);
|
|
widget->hide();
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
for (auto widget : _main_window->displayed_widgets)
|
|
widget->show();
|
|
|
|
_main_window->displayed_widgets.clear();
|
|
}
|
|
|
|
|
|
_main_window->statusBar()->setVisible(ui_hidden);
|
|
_toolbar->setVisible(ui_hidden);
|
|
|
|
ui_hidden = !ui_hidden;
|
|
|
|
setToolPropertyWidgetVisibility(terrainMode);
|
|
|
|
};
|
|
|
|
ADD_ACTION(view_menu, "Toggle UI", Qt::Key_Tab, hide_widgets);
|
|
|
|
|
|
ADD_TOGGLE (view_menu, "Detail infos", Qt::Key_F8, _show_detail_info_window);
|
|
connect ( &_show_detail_info_window, &noggit::bool_toggle_property::changed
|
|
, guidetailInfos, [this]
|
|
{
|
|
if (!ui_hidden)
|
|
guidetailInfos->setVisible(_show_detail_info_window.get());
|
|
}
|
|
);
|
|
connect ( guidetailInfos, &noggit::ui::widget::visibilityChanged
|
|
, &_show_detail_info_window, &noggit::bool_toggle_property::set
|
|
);
|
|
ADD_TOGGLE (view_menu, "Cursor switcher", "Ctrl+Alt+C", _show_cursor_switcher_window);
|
|
connect ( &_show_cursor_switcher_window, &noggit::bool_toggle_property::changed
|
|
, _cursor_switcher.get(), &QWidget::setVisible
|
|
);
|
|
connect ( _cursor_switcher.get(), &noggit::ui::widget::visibilityChanged
|
|
, &_show_cursor_switcher_window, &noggit::bool_toggle_property::set
|
|
);
|
|
ADD_TOGGLE (view_menu, "Texture palette", Qt::Key_X, _show_texture_palette_window);
|
|
connect ( &_show_texture_palette_window, &noggit::bool_toggle_property::changed
|
|
, TexturePalette, [this]
|
|
{
|
|
if (terrainMode == editing_mode::paint && !ui_hidden)
|
|
{
|
|
TexturePalette->setVisible(_show_texture_palette_window.get());
|
|
}
|
|
else
|
|
{
|
|
_show_texture_palette_window.set(false);
|
|
}
|
|
}
|
|
);
|
|
connect ( TexturePalette, &noggit::ui::widget::visibilityChanged
|
|
, &_show_texture_palette_window, &noggit::bool_toggle_property::set
|
|
);
|
|
|
|
ADD_TOGGLE_NS(view_menu, "Small texture palette", _show_texture_palette_small_window);
|
|
|
|
addHotkey( Qt::Key_H
|
|
, MOD_none
|
|
, [this] { _show_texture_palette_small_window.toggle(); }
|
|
, [this] { return terrainMode == editing_mode::paint; }
|
|
);
|
|
|
|
connect(&_show_texture_palette_small_window, &noggit::bool_toggle_property::changed
|
|
, _texture_palette_dock, [this]
|
|
{
|
|
if (terrainMode == editing_mode::paint && !ui_hidden)
|
|
{
|
|
_texture_palette_dock->setVisible(_show_texture_palette_small_window.get());
|
|
}
|
|
else
|
|
{
|
|
_show_texture_palette_small_window.set(false);
|
|
}
|
|
}
|
|
);
|
|
connect(_texture_palette_dock, &QDockWidget::visibilityChanged
|
|
, &_show_texture_palette_small_window, &noggit::bool_toggle_property::set
|
|
);
|
|
|
|
addHotkey ( Qt::Key_F1
|
|
, MOD_shift
|
|
, [this]
|
|
{
|
|
if (alloff)
|
|
{
|
|
alloff_models = _draw_models.get();
|
|
alloff_doodads = _draw_wmo_doodads.get();
|
|
alloff_contour = _draw_contour.get();
|
|
alloff_wmo = _draw_wmo.get();
|
|
alloff_fog = _draw_fog.get();
|
|
alloff_terrain = _draw_terrain.get();
|
|
|
|
_draw_models.set (false);
|
|
_draw_wmo_doodads.set (false);
|
|
_draw_contour.set (true);
|
|
_draw_wmo.set (false);
|
|
_draw_terrain.set (true);
|
|
_draw_fog.set (false);
|
|
}
|
|
else
|
|
{
|
|
_draw_models.set (alloff_models);
|
|
_draw_wmo_doodads.set (alloff_doodads);
|
|
_draw_contour.set (alloff_contour);
|
|
_draw_wmo.set (alloff_wmo);
|
|
_draw_terrain.set (alloff_terrain);
|
|
_draw_fog.set (alloff_fog);
|
|
}
|
|
alloff = !alloff;
|
|
}
|
|
);
|
|
|
|
ADD_TOGGLE (help_menu, "Key Bindings", "Ctrl+F1", _show_keybindings_window);
|
|
connect ( &_show_keybindings_window, &noggit::bool_toggle_property::changed
|
|
, _keybindings, &QWidget::setVisible
|
|
);
|
|
connect ( _keybindings, &noggit::ui::widget::visibilityChanged
|
|
, &_show_keybindings_window, &noggit::bool_toggle_property::set
|
|
);
|
|
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
ADD_ACTION_NS ( help_menu
|
|
, "Forum"
|
|
, []
|
|
{
|
|
ShellExecute ( nullptr
|
|
, "open"
|
|
, "http://www.modcraft.io/index.php?board=48.0"
|
|
, nullptr
|
|
, nullptr
|
|
, SW_SHOWNORMAL
|
|
);
|
|
}
|
|
);
|
|
ADD_ACTION_NS ( help_menu
|
|
, "Homepage"
|
|
, []
|
|
{
|
|
ShellExecute ( nullptr
|
|
, "open"
|
|
, "https://bitbucket.org/berndloerwald/noggit3/"
|
|
, nullptr
|
|
, nullptr
|
|
, SW_SHOWNORMAL
|
|
);
|
|
}
|
|
);
|
|
|
|
ADD_ACTION_NS ( help_menu
|
|
, "Discord"
|
|
, []
|
|
{
|
|
ShellExecute ( nullptr
|
|
, "open"
|
|
, "https://discord.gg/UbdFHyM"
|
|
, nullptr
|
|
, nullptr
|
|
, SW_SHOWNORMAL
|
|
);
|
|
}
|
|
);
|
|
#endif
|
|
|
|
ADD_ACTION ( file_menu
|
|
, "Add bookmark"
|
|
, Qt::Key_F5
|
|
, [this]
|
|
{
|
|
std::ofstream f ("bookmarks.txt", std::ios_base::app);
|
|
f << _world->getMapID() << " "
|
|
<< _camera.position.x << " "
|
|
<< _camera.position.y << " "
|
|
<< _camera.position.z << " "
|
|
<< _camera.yaw()._ << " "
|
|
<< _camera.pitch()._ << " "
|
|
<< _world->getAreaID (_camera.position) << std::endl;
|
|
}
|
|
);
|
|
|
|
ADD_ACTION (view_menu, "Increase time speed", Qt::Key_N, [this] { mTimespeed += 90.0f; });
|
|
ADD_ACTION (view_menu, "Decrease time speed", Qt::Key_B, [this] { mTimespeed = std::max (0.0f, mTimespeed - 90.0f); });
|
|
ADD_ACTION (view_menu, "Pause time", Qt::Key_J, [this] { mTimespeed = 0.0f; });
|
|
|
|
addHotkey ( Qt::Key_C
|
|
, MOD_ctrl
|
|
, [this]
|
|
{
|
|
objectEditor->copy_current_selection(_world.get());
|
|
}
|
|
, [this] { return terrainMode == editing_mode::object; }
|
|
);
|
|
addHotkey ( Qt::Key_C
|
|
, MOD_none
|
|
, [this]
|
|
{
|
|
objectEditor->copy_current_selection(_world.get());
|
|
}
|
|
, [this] { return terrainMode == editing_mode::object; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_C
|
|
, MOD_shift
|
|
, [this]
|
|
{
|
|
do
|
|
{
|
|
cursor_type.set ((cursor_type.get() + 1) % static_cast<unsigned int>(cursor_mode::mode_count));
|
|
} while (cursor_type.get() == static_cast<unsigned int>(cursor_mode::unused)); // hack to not get the unused cursor type
|
|
}
|
|
, [this] { return terrainMode != editing_mode::object; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_V
|
|
, MOD_ctrl
|
|
, [this] { objectEditor->pasteObject (_cursor_pos, _camera.position, _world.get(), &_object_paste_params); }
|
|
, [this] { return terrainMode == editing_mode::object; }
|
|
);
|
|
addHotkey ( Qt::Key_V
|
|
, MOD_none
|
|
, [this] { objectEditor->pasteObject (_cursor_pos, _camera.position, _world.get(), &_object_paste_params); }
|
|
, [this] { return terrainMode == editing_mode::object; }
|
|
);
|
|
addHotkey ( Qt::Key_V
|
|
, MOD_shift
|
|
, [this] { objectEditor->import_last_model_from_wmv(eEntry_Model); }
|
|
, [this] { return terrainMode == editing_mode::object; }
|
|
);
|
|
addHotkey ( Qt::Key_V
|
|
, MOD_alt
|
|
, [this] { objectEditor->import_last_model_from_wmv(eEntry_WMO); }
|
|
, [this] { return terrainMode == editing_mode::object; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_C
|
|
, MOD_none
|
|
, [this] { _world->clearVertexSelection(); }
|
|
, [this] { return terrainMode == editing_mode::ground; }
|
|
);
|
|
|
|
addHotkey( Qt::Key_B
|
|
, MOD_ctrl
|
|
, [this]
|
|
{
|
|
objectEditor->copy_current_selection(_world.get());
|
|
objectEditor->pasteObject(_cursor_pos, _camera.position, _world.get(), &_object_paste_params);
|
|
}
|
|
, [this] { return terrainMode == editing_mode::object; }
|
|
);
|
|
|
|
ADD_ACTION (view_menu, "Invert mouse", "I", [this] { mousedir *= -1.f; });
|
|
|
|
ADD_ACTION (view_menu, "Decrease camera speed", Qt::Key_O, [this] { _camera.move_speed *= 0.5f; });
|
|
ADD_ACTION (view_menu, "Increase camera speed", Qt::Key_P, [this] { _camera.move_speed *= 2.0f; });
|
|
|
|
ADD_ACTION (file_menu, "Save minimaps", "Ctrl+Shift+P", [this] { Saving = true; });
|
|
|
|
ADD_ACTION ( view_menu
|
|
, "Turn camera around 180°"
|
|
, "Shift+R"
|
|
, [this]
|
|
{
|
|
_camera.add_to_yaw(math::degrees(180.f));
|
|
_camera_moved_since_last_draw = true;
|
|
_minimap->update();
|
|
}
|
|
);
|
|
|
|
ADD_ACTION ( file_menu
|
|
, "Write coordinates to port.txt"
|
|
, Qt::Key_G
|
|
, [this]
|
|
{
|
|
std::ofstream f("ports.txt", std::ios_base::app);
|
|
f << "Map: " << gAreaDB.getAreaName(_world->getAreaID (_camera.position)) << " on ADT " << std::floor(_camera.position.x / TILESIZE) << " " << std::floor(_camera.position.z / TILESIZE) << std::endl;
|
|
f << "Trinity:" << std::endl << ".go " << (ZEROPOINT - _camera.position.z) << " " << (ZEROPOINT - _camera.position.x) << " " << _camera.position.y << " " << _world->getMapID() << std::endl;
|
|
f << "ArcEmu:" << std::endl << ".worldport " << _world->getMapID() << " " << (ZEROPOINT - _camera.position.z) << " " << (ZEROPOINT - _camera.position.x) << " " << _camera.position.y << " " << std::endl << std::endl;
|
|
f.close();
|
|
}
|
|
);
|
|
|
|
addHotkey ( Qt::Key_Y
|
|
, MOD_none
|
|
, [this] { terrainTool->nextType(); }
|
|
, [this] { return terrainMode == editing_mode::ground; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_Y
|
|
, MOD_none
|
|
, [this] { flattenTool->nextFlattenType(); }
|
|
, [this] { return terrainMode == editing_mode::flatten_blur; }
|
|
);
|
|
|
|
ADD_ACTION ( view_menu
|
|
, "Toggle tile mode"
|
|
, Qt::Key_U
|
|
, [this]
|
|
{
|
|
if (_display_mode == display_mode::in_2D)
|
|
{
|
|
_display_mode = display_mode::in_3D;
|
|
set_editing_mode (saveterrainMode);
|
|
}
|
|
else
|
|
{
|
|
_display_mode = display_mode::in_2D;
|
|
saveterrainMode = terrainMode;
|
|
set_editing_mode (editing_mode::paint);
|
|
}
|
|
}
|
|
);
|
|
|
|
addHotkey ( Qt::Key_T
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
flattenTool->toggleFlattenAngle();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::flatten_blur; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_T
|
|
, MOD_space
|
|
, [&]
|
|
{
|
|
flattenTool->nextFlattenMode();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::flatten_blur; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_T
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
texturingTool->toggle_tool();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::paint; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_T
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
_world->setHoleADT (_camera.position, false);
|
|
}
|
|
, [&] { return terrainMode == editing_mode::holes; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_T
|
|
, MOD_alt
|
|
, [&]
|
|
{
|
|
_world->setHoleADT (_camera.position, true);
|
|
}
|
|
, [&] { return terrainMode == editing_mode::holes; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_T
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
guiWater->toggle_angled_mode();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::water; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_T
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
objectEditor->togglePasteMode();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::object; }
|
|
);
|
|
|
|
|
|
addHotkey ( Qt::Key_H
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
if (_world->has_selection())
|
|
{
|
|
for (auto& selection : _world->current_selection())
|
|
{
|
|
if (selection.which() == eEntry_Model)
|
|
{
|
|
boost::get<selected_model_type>(selection)->model->toggle_visibility();
|
|
}
|
|
else if (selection.which() == eEntry_WMO)
|
|
{
|
|
boost::get<selected_wmo_type>(selection)->wmo->toggle_visibility();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
, [&] { return terrainMode == editing_mode::object; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_H
|
|
, MOD_space
|
|
, [&]
|
|
{
|
|
_draw_hidden_models.toggle();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::object; }
|
|
);
|
|
|
|
addHotkey(Qt::Key_R
|
|
, MOD_space
|
|
, [&]
|
|
{
|
|
texturingTool->toggle_brush_level_min_max();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::paint; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_H
|
|
, MOD_shift
|
|
, [&]
|
|
{
|
|
ModelManager::clear_hidden_models();
|
|
WMOManager::clear_hidden_wmos();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::object; }
|
|
);
|
|
|
|
addHotkey ( Qt::Key_F
|
|
, MOD_space
|
|
, [&]
|
|
{
|
|
terrainTool->flattenVertices (_world.get());
|
|
}
|
|
, [&] { return terrainMode == editing_mode::ground; }
|
|
);
|
|
addHotkey ( Qt::Key_F
|
|
, MOD_space
|
|
, [&]
|
|
{
|
|
flattenTool->toggleFlattenLock();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::flatten_blur; }
|
|
);
|
|
addHotkey ( Qt::Key_F
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
flattenTool->lockPos (_cursor_pos);
|
|
}
|
|
, [&] { return terrainMode == editing_mode::flatten_blur; }
|
|
);
|
|
addHotkey ( Qt::Key_F
|
|
, MOD_space
|
|
, [&]
|
|
{
|
|
guiWater->toggle_lock();
|
|
}
|
|
, [&] { return terrainMode == editing_mode::water; }
|
|
);
|
|
addHotkey( Qt::Key_F
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
guiWater->lockPos(_cursor_pos);
|
|
}
|
|
, [&] { return terrainMode == editing_mode::water; }
|
|
);
|
|
addHotkey ( Qt::Key_F
|
|
, MOD_none
|
|
, [&]
|
|
{
|
|
_world->set_selected_models_pos(_cursor_pos);
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
, [&] { return terrainMode == editing_mode::object; }
|
|
);
|
|
|
|
addHotkey (Qt::Key_Plus, MOD_alt, [this] { terrainTool->changeRadius(0.01f); }, [this] { return terrainMode == editing_mode::ground; });
|
|
|
|
addHotkey (Qt::Key_Plus, MOD_alt, [this] { flattenTool->changeRadius(0.01f); }, [this] { return terrainMode == editing_mode::flatten_blur; });
|
|
|
|
addHotkey ( Qt::Key_Plus
|
|
, MOD_alt
|
|
, [&]
|
|
{
|
|
texturingTool->change_radius(0.1f);
|
|
}
|
|
, [this] { return terrainMode == editing_mode::paint; }
|
|
);
|
|
|
|
addHotkey (Qt::Key_Plus, MOD_shift, [this] { _world->fogdistance += 60.0f; });
|
|
|
|
addHotkey (Qt::Key_Minus, MOD_alt, [this] { terrainTool->changeRadius(-0.01f); }, [this] { return terrainMode == editing_mode::ground; });
|
|
|
|
addHotkey (Qt::Key_Minus, MOD_alt, [this] { flattenTool->changeRadius(-0.01f); }, [this] { return terrainMode == editing_mode::flatten_blur; });
|
|
|
|
addHotkey ( Qt::Key_Minus
|
|
, MOD_alt
|
|
, [&]
|
|
{
|
|
texturingTool->change_radius(-0.1f);
|
|
}
|
|
, [this] { return terrainMode == editing_mode::paint; }
|
|
);
|
|
|
|
addHotkey (Qt::Key_Minus, MOD_shift, [this] { _world->fogdistance -= 60.0f; });
|
|
|
|
addHotkey (Qt::Key_1, MOD_shift, [this] { _camera.move_speed = 15.0f; });
|
|
addHotkey (Qt::Key_2, MOD_shift, [this] { _camera.move_speed = 50.0f; });
|
|
addHotkey (Qt::Key_3, MOD_shift, [this] { _camera.move_speed = 200.0f; });
|
|
addHotkey (Qt::Key_4, MOD_shift, [this] { _camera.move_speed = 800.0f; });
|
|
addHotkey (Qt::Key_1, MOD_alt, [this] { texturingTool->set_brush_level(0.0f); });
|
|
addHotkey (Qt::Key_2, MOD_alt, [this] { texturingTool->set_brush_level(255.0f* 0.25f); });
|
|
addHotkey (Qt::Key_3, MOD_alt, [this] { texturingTool->set_brush_level(255.0f* 0.5f); });
|
|
addHotkey (Qt::Key_4, MOD_alt, [this] { texturingTool->set_brush_level(255.0f* 0.75f); });
|
|
addHotkey (Qt::Key_5, MOD_alt, [this] { texturingTool->set_brush_level(255.0f); });
|
|
|
|
addHotkey(Qt::Key_1, MOD_none, [this] { set_editing_mode(editing_mode::ground); }, [this] { return !_mod_num_down; });
|
|
addHotkey (Qt::Key_2, MOD_none, [this] { set_editing_mode (editing_mode::flatten_blur); }, [this] { return !_mod_num_down; });
|
|
addHotkey (Qt::Key_3, MOD_none, [this] { set_editing_mode (editing_mode::paint); }, [this] { return !_mod_num_down; });
|
|
addHotkey (Qt::Key_4, MOD_none, [this] { set_editing_mode (editing_mode::holes); }, [this] { return !_mod_num_down; });
|
|
addHotkey (Qt::Key_5, MOD_none, [this] { set_editing_mode (editing_mode::areaid); }, [this] { return !_mod_num_down; });
|
|
addHotkey (Qt::Key_6, MOD_none, [this] { set_editing_mode (editing_mode::flags); }, [this] { return !_mod_num_down; });
|
|
addHotkey (Qt::Key_7, MOD_none, [this] { set_editing_mode (editing_mode::water); }, [this] { return !_mod_num_down; });
|
|
addHotkey (Qt::Key_8, MOD_none, [this] { set_editing_mode (editing_mode::mccv); }, [this] { return !_mod_num_down; });
|
|
addHotkey (Qt::Key_9, MOD_none, [this] { set_editing_mode (editing_mode::object); }, [this] { return !_mod_num_down; });
|
|
|
|
addHotkey(Qt::Key_0, MOD_ctrl, [this] { change_selected_wmo_doodadset(0); });
|
|
addHotkey(Qt::Key_1, MOD_ctrl, [this] { change_selected_wmo_doodadset(1); });
|
|
addHotkey(Qt::Key_2, MOD_ctrl, [this] { change_selected_wmo_doodadset(2); });
|
|
addHotkey(Qt::Key_3, MOD_ctrl, [this] { change_selected_wmo_doodadset(3); });
|
|
addHotkey(Qt::Key_4, MOD_ctrl, [this] { change_selected_wmo_doodadset(4); });
|
|
addHotkey(Qt::Key_5, MOD_ctrl, [this] { change_selected_wmo_doodadset(5); });
|
|
addHotkey(Qt::Key_6, MOD_ctrl, [this] { change_selected_wmo_doodadset(6); });
|
|
addHotkey(Qt::Key_7, MOD_ctrl, [this] { change_selected_wmo_doodadset(7); });
|
|
addHotkey(Qt::Key_8, MOD_ctrl, [this] { change_selected_wmo_doodadset(8); });
|
|
addHotkey(Qt::Key_9, MOD_ctrl, [this] { change_selected_wmo_doodadset(9); });
|
|
|
|
connect(_main_window, &noggit::ui::main_window::exit_prompt_opened, this, &MapView::on_exit_prompt);
|
|
}
|
|
|
|
void MapView::on_exit_prompt()
|
|
{
|
|
// hide all popups
|
|
_cursor_switcher->hide();
|
|
_keybindings->hide();
|
|
_minimap_dock->hide();
|
|
_texture_palette_small->hide();
|
|
objectEditor->helper_models_widget->hide();
|
|
objectEditor->modelImport->hide();
|
|
objectEditor->rotationEditor->hide();
|
|
guidetailInfos->hide();
|
|
TexturePicker->hide();
|
|
TexturePalette->hide();
|
|
}
|
|
|
|
MapView::MapView( math::degrees camera_yaw0
|
|
, math::degrees camera_pitch0
|
|
, math::vector_3d camera_pos
|
|
, noggit::ui::main_window* main_window
|
|
, std::unique_ptr<World> world
|
|
, uid_fix_mode uid_fix
|
|
, bool from_bookmark
|
|
)
|
|
: _camera (camera_pos, camera_yaw0, camera_pitch0)
|
|
, mTimespeed(0.0f)
|
|
, _uid_fix (uid_fix)
|
|
, _from_bookmark (from_bookmark)
|
|
, _settings (new QSettings (this))
|
|
, cursor_color (1.f, 1.f, 1.f, 1.f)
|
|
, shader_color (1.f, 1.f, 1.f, 1.f)
|
|
, cursor_type (static_cast<unsigned int>(cursor_mode::terrain))
|
|
, _main_window (main_window)
|
|
, _world (std::move (world))
|
|
, _status_position (new QLabel (this))
|
|
, _status_selection (new QLabel (this))
|
|
, _status_area (new QLabel (this))
|
|
, _status_time (new QLabel (this))
|
|
, _status_fps (new QLabel (this))
|
|
, _minimap (new noggit::ui::minimap_widget (nullptr))
|
|
, _minimap_dock (new QDockWidget ("Minimap", this))
|
|
, _texture_palette_dock(new QDockWidget(this))
|
|
{
|
|
_main_window->setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
|
|
_main_window->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
|
|
_main_window->setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
|
|
_main_window->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
|
|
|
|
_main_window->statusBar()->addWidget (_status_position);
|
|
connect ( this
|
|
, &QObject::destroyed
|
|
, _main_window
|
|
, [=] { _main_window->statusBar()->removeWidget (_status_position); }
|
|
);
|
|
_main_window->statusBar()->addWidget (_status_selection);
|
|
connect ( this
|
|
, &QObject::destroyed
|
|
, _main_window
|
|
, [=] { _main_window->statusBar()->removeWidget (_status_selection); }
|
|
);
|
|
_main_window->statusBar()->addWidget (_status_area);
|
|
connect ( this
|
|
, &QObject::destroyed
|
|
, _main_window
|
|
, [=] { _main_window->statusBar()->removeWidget (_status_area); }
|
|
);
|
|
_main_window->statusBar()->addWidget (_status_time);
|
|
connect ( this
|
|
, &QObject::destroyed
|
|
, _main_window
|
|
, [=] { _main_window->statusBar()->removeWidget (_status_time); }
|
|
);
|
|
_main_window->statusBar()->addWidget (_status_fps);
|
|
connect ( this
|
|
, &QObject::destroyed
|
|
, _main_window
|
|
, [=] { _main_window->statusBar()->removeWidget (_status_fps); }
|
|
);
|
|
|
|
_minimap->world (_world.get());
|
|
_minimap->camera (&_camera);
|
|
_minimap->draw_boundaries (_show_minimap_borders.get());
|
|
_minimap->draw_skies (_show_minimap_skies.get());
|
|
|
|
connect ( _minimap, &noggit::ui::minimap_widget::map_clicked
|
|
, [this] (math::vector_3d const& pos)
|
|
{
|
|
move_camera_with_auto_height (pos);
|
|
}
|
|
);
|
|
|
|
_minimap_dock->setFeatures ( QDockWidget::DockWidgetMovable
|
|
| QDockWidget::DockWidgetFloatable
|
|
| QDockWidget::DockWidgetClosable
|
|
);
|
|
_minimap_dock->setWidget(_minimap);
|
|
_main_window->addDockWidget (Qt::RightDockWidgetArea, _minimap_dock);
|
|
_minimap_dock->setVisible (false);
|
|
_minimap_dock->setFloating(true);
|
|
_minimap_dock->move(_main_window->rect().center() - _minimap->rect().center());
|
|
|
|
|
|
connect(this, &QObject::destroyed, _minimap_dock, &QObject::deleteLater);
|
|
connect(this, &QObject::destroyed, _minimap, &QObject::deleteLater);
|
|
|
|
setWindowTitle ("Noggit Studio - " STRPRODUCTVER);
|
|
|
|
cursor_type.set (_settings->value ("cursor/default_type", static_cast<unsigned int>(cursor_mode::terrain)).toUInt());
|
|
|
|
cursor_color.x = _settings->value ("cursor/color/r", 1).toFloat();
|
|
cursor_color.y = _settings->value ("cursor/color/g", 1).toFloat();
|
|
cursor_color.z = _settings->value ("cursor/color/b", 1).toFloat();
|
|
cursor_color.w = _settings->value ("cursor/color/a", 1).toFloat();
|
|
|
|
connect(&cursor_type, &noggit::unsigned_int_property::changed, [&] (unsigned int type)
|
|
{
|
|
_settings->setValue("cursor/default_type", type);
|
|
});
|
|
|
|
if (cursor_type.get() == static_cast<unsigned int>(cursor_mode::unused))
|
|
{
|
|
cursor_type.set(static_cast<unsigned int>(cursor_mode::terrain));
|
|
}
|
|
|
|
_cursor_switcher.reset(new noggit::ui::cursor_switcher (this, cursor_color, cursor_type));
|
|
|
|
setFocusPolicy (Qt::StrongFocus);
|
|
setMouseTracking (true);
|
|
|
|
moving = strafing = updown = lookat = turn = 0.0f;
|
|
|
|
freelook = false;
|
|
|
|
mousedir = -1.0f;
|
|
|
|
look = false;
|
|
_display_mode = display_mode::in_3D;
|
|
|
|
_tablet_active = true;
|
|
|
|
_startup_time.start();
|
|
_update_every_event_loop.start (0);
|
|
connect (&_update_every_event_loop, &QTimer::timeout, [this] { update(); });
|
|
}
|
|
|
|
void MapView::tabletEvent(QTabletEvent* event)
|
|
{
|
|
_tablet_pressure = event->pressure();
|
|
event->setAccepted(true);
|
|
|
|
}
|
|
|
|
void MapView::move_camera_with_auto_height (math::vector_3d const& pos)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
|
|
_world->mapIndex.loadTile(pos)->wait_until_loaded();
|
|
|
|
_camera.position = pos;
|
|
_camera.position.y = 0.0f;
|
|
|
|
_world->GetVertex (pos.x, pos.z, &_camera.position);
|
|
|
|
// min elevation according to https://wowdev.wiki/AreaTable.dbc
|
|
//! \ todo use the current area's MinElevation
|
|
if (_camera.position.y < -5000.0f)
|
|
{
|
|
//! \todo use the height of a model/wmo of the tile (or the map) ?
|
|
_camera.position.y = 0.0f;
|
|
}
|
|
|
|
_camera.position.y += 50.0f;
|
|
|
|
_minimap->update();
|
|
}
|
|
|
|
void MapView::on_uid_fix_fail()
|
|
{
|
|
emit uid_fix_failed();
|
|
|
|
_uid_fix_failed = true;
|
|
deleteLater();
|
|
}
|
|
|
|
void MapView::initializeGL()
|
|
{
|
|
bool uid_warning = false;
|
|
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
gl.viewport(0.0f, 0.0f, width(), height());
|
|
|
|
gl.clearColor (0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
if (_uid_fix == uid_fix_mode::max_uid)
|
|
{
|
|
_world->mapIndex.searchMaxUID();
|
|
}
|
|
else if (_uid_fix == uid_fix_mode::fix_all_fail_on_model_loading_error)
|
|
{
|
|
auto result = _world->mapIndex.fixUIDs (_world.get(), true);
|
|
|
|
if (result == uid_fix_status::failed)
|
|
{
|
|
on_uid_fix_fail();
|
|
return;
|
|
}
|
|
}
|
|
else if (_uid_fix == uid_fix_mode::fix_all_fuckporting_edition)
|
|
{
|
|
auto result = _world->mapIndex.fixUIDs (_world.get(), false);
|
|
|
|
uid_warning = result == uid_fix_status::done_with_errors;
|
|
}
|
|
|
|
_uid_fix = uid_fix_mode::none;
|
|
|
|
createGUI();
|
|
|
|
set_editing_mode (editing_mode::ground);
|
|
|
|
if (!_from_bookmark)
|
|
{
|
|
move_camera_with_auto_height (_camera.position);
|
|
}
|
|
|
|
if (uid_warning)
|
|
{
|
|
QMessageBox::warning
|
|
( nullptr
|
|
, "UID Warning"
|
|
, "Some models were missing or couldn't be loaded. "
|
|
"This will lead to culling (visibility) errors in game\n"
|
|
"It is recommanded to fix those models (listed in the log file) and run the uid fix all again."
|
|
, QMessageBox::Ok
|
|
);
|
|
}
|
|
}
|
|
|
|
void MapView::paintGL()
|
|
{
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
const qreal now(_startup_time.elapsed() / 1000.0);
|
|
|
|
_last_frame_durations.emplace_back (now - _last_update);
|
|
|
|
tick (now - _last_update);
|
|
_last_update = now;
|
|
|
|
gl.clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
draw_map();
|
|
|
|
if (_world->uid_duplicates_found() && !_uid_duplicate_warning_shown)
|
|
{
|
|
_uid_duplicate_warning_shown = true;
|
|
|
|
QMessageBox::critical( this
|
|
, "UID ALREADY IN USE"
|
|
, "Please enable 'Always check for max UID', mysql uid store or synchronize your "
|
|
"uid.ini file if you're sharing the map between several mappers.\n\n"
|
|
"Use 'Editor > Force uid check on next opening' to fix the issue."
|
|
);
|
|
}
|
|
}
|
|
|
|
void MapView::resizeGL (int width, int height)
|
|
{
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
gl.viewport(0.0f, 0.0f, width, height);
|
|
}
|
|
|
|
|
|
MapView::~MapView()
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
|
|
// when the uid fix fail the UI isn't created
|
|
if (!_uid_fix_failed)
|
|
{
|
|
delete TexturePicker; // explicitly delete this here to avoid opengl context related crash
|
|
delete objectEditor;
|
|
delete texturingTool;
|
|
}
|
|
|
|
if (_force_uid_check)
|
|
{
|
|
uid_storage::remove_uid_for_map(_world->getMapID());
|
|
}
|
|
|
|
_world.reset();
|
|
|
|
AsyncLoader::instance().reset_object_fail();
|
|
|
|
noggit::ui::selected_texture::texture.reset();
|
|
|
|
ModelManager::report();
|
|
TextureManager::report();
|
|
WMOManager::report();
|
|
}
|
|
|
|
void MapView::tick (float dt)
|
|
{
|
|
_mod_shift_down = QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
|
_mod_ctrl_down = QApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
|
|
_mod_alt_down = QApplication::keyboardModifiers().testFlag(Qt::AltModifier);
|
|
_mod_num_down = QApplication::keyboardModifiers().testFlag(Qt::KeypadModifier);
|
|
|
|
|
|
// start unloading tiles
|
|
_world->mapIndex.enterTile (tile_index (_camera.position));
|
|
_world->mapIndex.unloadTiles (tile_index (_camera.position));
|
|
|
|
dt = std::min(dt, 1.0f);
|
|
|
|
if (_locked_cursor_mode.get())
|
|
{
|
|
switch (terrainMode)
|
|
{
|
|
case editing_mode::areaid:
|
|
case editing_mode::flags:
|
|
case editing_mode::holes:
|
|
case editing_mode::object:
|
|
update_cursor_pos();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
update_cursor_pos();
|
|
}
|
|
|
|
if (_tablet_active && _settings->value ("tablet/enabled", false).toBool())
|
|
{
|
|
switch (terrainMode)
|
|
{
|
|
case editing_mode::ground:
|
|
terrainTool->setSpeed(_tablet_pressure * 10.0f);
|
|
case editing_mode::flatten_blur:
|
|
flattenTool->setSpeed(_tablet_pressure * 10.0f);
|
|
break;
|
|
case editing_mode::paint:
|
|
texturingTool->set_pressure(_tablet_pressure);
|
|
break;
|
|
}
|
|
}
|
|
|
|
math::degrees yaw (-_camera.yaw()._);
|
|
|
|
math::vector_3d dir(1.0f, 0.0f, 0.0f);
|
|
math::vector_3d dirUp(1.0f, 0.0f, 0.0f);
|
|
math::vector_3d dirRight(0.0f, 0.0f, 1.0f);
|
|
math::rotate(0.0f, 0.0f, &dir.x, &dir.y, _camera.pitch());
|
|
math::rotate(0.0f, 0.0f, &dir.x, &dir.z, yaw);
|
|
|
|
if (_mod_ctrl_down)
|
|
{
|
|
dirUp.x = 0.0f;
|
|
dirUp.y = 1.0f;
|
|
math::rotate(0.0f, 0.0f, &dirUp.x, &dirUp.y, _camera.pitch());
|
|
math::rotate(0.0f, 0.0f, &dirRight.x, &dirRight.y, _camera.pitch());
|
|
math::rotate(0.0f, 0.0f, &dirUp.x, &dirUp.z, yaw);
|
|
math::rotate(0.0f, 0.0f, &dirRight.x, &dirRight.z,yaw);
|
|
}
|
|
else if(!_mod_shift_down)
|
|
{
|
|
math::rotate(0.0f, 0.0f, &dirUp.x, &dirUp.z, yaw);
|
|
math::rotate(0.0f, 0.0f, &dirRight.x, &dirRight.z, yaw);
|
|
}
|
|
|
|
auto currentSelection = _world->current_selection();
|
|
if (_world->has_selection())
|
|
{
|
|
// update rotation editor if the selection has changed
|
|
if (lastSelected != currentSelection)
|
|
{
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
if (terrainMode == editing_mode::object)
|
|
{
|
|
// reset numpad_moveratio when no numpad key is pressed
|
|
if (!(keyx != 0 || keyy != 0 || keyz != 0 || keyr != 0 || keys != 0))
|
|
{
|
|
numpad_moveratio = 0.001f;
|
|
}
|
|
else // Set move scale and rotate for numpad keys
|
|
{
|
|
if (_mod_ctrl_down && _mod_shift_down)
|
|
{
|
|
numpad_moveratio += 0.1f;
|
|
}
|
|
else if (_mod_shift_down)
|
|
{
|
|
numpad_moveratio += 0.01f;
|
|
}
|
|
else if (_mod_ctrl_down)
|
|
{
|
|
numpad_moveratio += 0.0005f;
|
|
}
|
|
}
|
|
|
|
if (keys != 0.f)
|
|
{
|
|
_world->scale_selected_models(keys*numpad_moveratio / 50.f, World::m2_scaling_type::add);
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
if (keyr != 0.f)
|
|
{
|
|
_world->rotate_selected_models( math::degrees(0.f)
|
|
, math::degrees(keyr * numpad_moveratio * 5.f)
|
|
, math::degrees(0.f)
|
|
, _use_median_pivot_point.get()
|
|
);
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
if (MoveObj)
|
|
{
|
|
if (_mod_alt_down)
|
|
{
|
|
_world->scale_selected_models(std::pow(2.f, mv*4.f), World::m2_scaling_type::mult);
|
|
}
|
|
else if (_mod_shift_down)
|
|
{
|
|
_world->move_selected_models(0.f, mv*80.f, 0.f);
|
|
}
|
|
else
|
|
{
|
|
if (_world->has_multiple_model_selected())
|
|
{
|
|
_world->set_selected_models_pos(_cursor_pos, false);
|
|
|
|
if (_snap_multi_selection_to_ground.get())
|
|
{
|
|
snap_selected_models_to_the_ground();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!_move_model_to_cursor_position.get())
|
|
{
|
|
_world->move_selected_models((mv * dirUp - mh * dirRight)*80.f);
|
|
}
|
|
else
|
|
{
|
|
_world->set_selected_models_pos(_cursor_pos, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
if (keyx != 0.f || keyy != 0.f || keyz != 0.f)
|
|
{
|
|
_world->move_selected_models(keyx * numpad_moveratio, keyy * numpad_moveratio, keyz * numpad_moveratio);
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
if (look)
|
|
{
|
|
if (_mod_ctrl_down) // X
|
|
{
|
|
_world->rotate_selected_models( math::degrees(rh + rv)
|
|
, math::degrees(0.f)
|
|
, math::degrees(0.f)
|
|
, _use_median_pivot_point.get()
|
|
);
|
|
}
|
|
if (_mod_shift_down) // Y
|
|
{
|
|
_world->rotate_selected_models( math::degrees(0.f)
|
|
, math::degrees(rh + rv)
|
|
, math::degrees(0.f)
|
|
, _use_median_pivot_point.get()
|
|
);
|
|
}
|
|
if (_mod_alt_down) // Z
|
|
{
|
|
_world->rotate_selected_models( math::degrees(0.f)
|
|
, math::degrees(0.f)
|
|
, math::degrees(rh + rv)
|
|
, _use_median_pivot_point.get()
|
|
);
|
|
}
|
|
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
}
|
|
|
|
for (auto& selection : currentSelection)
|
|
{
|
|
if (leftMouse && selection.which() == eEntry_MapChunk)
|
|
{
|
|
bool underMap = _world->isUnderMap(_cursor_pos);
|
|
|
|
switch (terrainMode)
|
|
{
|
|
case editing_mode::ground:
|
|
if (_display_mode == display_mode::in_3D && !underMap)
|
|
{
|
|
if (_mod_shift_down)
|
|
{
|
|
terrainTool->changeTerrain(_world.get(), _cursor_pos, 7.5f * dt);
|
|
}
|
|
else if (_mod_ctrl_down)
|
|
{
|
|
terrainTool->changeTerrain(_world.get(), _cursor_pos, -7.5f * dt);
|
|
}
|
|
}
|
|
break;
|
|
case editing_mode::flatten_blur:
|
|
if (_display_mode == display_mode::in_3D && !underMap)
|
|
{
|
|
if (_mod_shift_down)
|
|
{
|
|
flattenTool->flatten(_world.get(), _cursor_pos, dt);
|
|
}
|
|
else if (_mod_ctrl_down)
|
|
{
|
|
flattenTool->blur(_world.get(), _cursor_pos, dt);
|
|
}
|
|
}
|
|
break;
|
|
case editing_mode::paint:
|
|
if (_mod_shift_down && _mod_ctrl_down && _mod_alt_down)
|
|
{
|
|
// clear chunk texture
|
|
if (!underMap)
|
|
{
|
|
_world->eraseTextures(_cursor_pos);
|
|
}
|
|
}
|
|
else if (_mod_ctrl_down && !ui_hidden)
|
|
{
|
|
// Pick texture
|
|
TexturePicker->getTextures(selection);
|
|
}
|
|
else if (_mod_shift_down && !!noggit::ui::selected_texture::get())
|
|
{
|
|
if ((_display_mode == display_mode::in_3D && !underMap) || _display_mode == display_mode::in_2D)
|
|
{
|
|
texturingTool->paint(_world.get(), _cursor_pos, dt, *noggit::ui::selected_texture::get());
|
|
}
|
|
}
|
|
break;
|
|
|
|
case editing_mode::holes:
|
|
// no undermap check here, else it's impossible to remove holes
|
|
if (_mod_shift_down)
|
|
{
|
|
_world->setHole(_cursor_pos, holeTool->brushRadius(),_mod_alt_down, false);
|
|
}
|
|
else if (_mod_ctrl_down && !underMap)
|
|
{
|
|
_world->setHole(_cursor_pos, holeTool->brushRadius(), _mod_alt_down, true);
|
|
}
|
|
break;
|
|
case editing_mode::areaid:
|
|
if (!underMap)
|
|
{
|
|
if (_mod_shift_down)
|
|
{
|
|
// draw the selected AreaId on current selected chunk
|
|
_world->setAreaID(_cursor_pos, _selected_area_id, false, ZoneIDBrowser->brushRadius());
|
|
}
|
|
else if (_mod_ctrl_down)
|
|
{
|
|
// pick areaID from chunk
|
|
MapChunk* chnk(boost::get<selected_chunk_type>(selection).chunk);
|
|
int newID = chnk->getAreaID();
|
|
_selected_area_id = newID;
|
|
ZoneIDBrowser->setZoneID(newID);
|
|
}
|
|
}
|
|
break;
|
|
case editing_mode::flags:
|
|
if (!underMap)
|
|
{
|
|
// todo: replace this
|
|
if (_mod_shift_down)
|
|
{
|
|
_world->mapIndex.setFlag(true, _cursor_pos, 0x2);
|
|
}
|
|
else if (_mod_ctrl_down)
|
|
{
|
|
_world->mapIndex.setFlag(false, _cursor_pos, 0x2);
|
|
}
|
|
}
|
|
break;
|
|
case editing_mode::water:
|
|
if (_display_mode == display_mode::in_3D && !underMap)
|
|
{
|
|
if (_mod_shift_down)
|
|
{
|
|
guiWater->paintLiquid(_world.get(), _cursor_pos, true);
|
|
}
|
|
else if (_mod_ctrl_down)
|
|
{
|
|
guiWater->paintLiquid(_world.get(), _cursor_pos, false);
|
|
}
|
|
}
|
|
break;
|
|
case editing_mode::mccv:
|
|
if (!underMap)
|
|
{
|
|
if (_mod_shift_down)
|
|
{
|
|
shaderTool->changeShader(_world.get(), _cursor_pos, dt, true);
|
|
}
|
|
if (_mod_ctrl_down)
|
|
{
|
|
shaderTool->changeShader(_world.get(), _cursor_pos, dt, false);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mh = 0;
|
|
mv = 0;
|
|
rh = 0;
|
|
rv = 0;
|
|
|
|
if (_display_mode != display_mode::in_2D)
|
|
{
|
|
if (turn)
|
|
{
|
|
_camera.add_to_yaw(math::degrees(turn));
|
|
_camera_moved_since_last_draw = true;
|
|
}
|
|
if (lookat)
|
|
{
|
|
_camera.add_to_pitch(math::degrees(lookat));
|
|
_camera_moved_since_last_draw = true;
|
|
}
|
|
if (moving)
|
|
{
|
|
_camera.move_forward(moving, dt);
|
|
_camera_moved_since_last_draw = true;
|
|
}
|
|
if (strafing)
|
|
{
|
|
_camera.move_horizontal(strafing, dt);
|
|
_camera_moved_since_last_draw = true;
|
|
}
|
|
if (updown)
|
|
{
|
|
_camera.move_vertical(updown, dt);
|
|
_camera_moved_since_last_draw = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//! \todo this is total bullshit. there should be a seperate view and camera class for tilemode
|
|
if (moving)
|
|
{
|
|
_camera.position.z -= dt * _camera.move_speed * moving;
|
|
_camera_moved_since_last_draw = true;
|
|
}
|
|
if (strafing)
|
|
{
|
|
_camera.position.x += dt * _camera.move_speed * strafing;
|
|
_camera_moved_since_last_draw = true;
|
|
}
|
|
if (updown)
|
|
{
|
|
_2d_zoom *= pow(2.0f, dt * updown * 4.0f);
|
|
_2d_zoom = std::max(0.01f, _2d_zoom);
|
|
_camera_moved_since_last_draw = true;
|
|
}
|
|
}
|
|
|
|
_minimap->update();
|
|
|
|
_world->time += this->mTimespeed * dt;
|
|
_world->animtime += dt * 1000.0f;
|
|
|
|
if (_draw_model_animations.get())
|
|
{
|
|
_world->update_models_emitters(dt);
|
|
}
|
|
|
|
if (_world->has_selection())
|
|
{
|
|
lastSelected = currentSelection;
|
|
}
|
|
|
|
if (_rotation_editor_need_update)
|
|
{
|
|
objectEditor->rotationEditor->updateValues(_world.get());
|
|
_rotation_editor_need_update = false;
|
|
}
|
|
|
|
QString status;
|
|
status += ( QString ("tile: %1 %2")
|
|
. arg (std::floor (_camera.position.x / TILESIZE))
|
|
. arg (std::floor (_camera.position.z / TILESIZE))
|
|
);
|
|
status += ( QString ("; coordinates client: (%1, %2, %3), server: (%4, %5, %6)")
|
|
. arg (_camera.position.x)
|
|
. arg (_camera.position.z)
|
|
. arg (_camera.position.y)
|
|
. arg (ZEROPOINT - _camera.position.z)
|
|
. arg (ZEROPOINT - _camera.position.x)
|
|
. arg (_camera.position.y)
|
|
);
|
|
|
|
_status_position->setText (status);
|
|
|
|
if (currentSelection.size() > 0)
|
|
{
|
|
_status_selection->setText ("");
|
|
}
|
|
else if (currentSelection.size() == 1)
|
|
{
|
|
switch (currentSelection.begin()->which())
|
|
{
|
|
case eEntry_Model:
|
|
{
|
|
auto instance(boost::get<selected_model_type>(*currentSelection.begin()));
|
|
_status_selection->setText
|
|
( QString ("%1: %2")
|
|
. arg (instance->uid)
|
|
. arg (QString::fromStdString (instance->model->filename))
|
|
);
|
|
break;
|
|
}
|
|
case eEntry_WMO:
|
|
{
|
|
auto instance(boost::get<selected_wmo_type>(*currentSelection.begin()));
|
|
_status_selection->setText
|
|
( QString ("%1: %2")
|
|
. arg (instance->mUniqueID)
|
|
. arg (QString::fromStdString (instance->wmo->filename))
|
|
);
|
|
break;
|
|
}
|
|
case eEntry_MapChunk:
|
|
{
|
|
auto chunk(boost::get<selected_chunk_type>(*currentSelection.begin()).chunk);
|
|
_status_selection->setText
|
|
(QString ("%1, %2").arg (chunk->px).arg (chunk->py));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
_status_area->setText
|
|
(QString::fromStdString (gAreaDB.getAreaName (_world->getAreaID (_camera.position))));
|
|
|
|
{
|
|
int time ((static_cast<int>(_world->time) % 2880) / 2);
|
|
std::stringstream timestrs;
|
|
timestrs << "Time: " << (time / 60) << ":" << std::setfill ('0')
|
|
<< std::setw (2) << (time % 60);
|
|
|
|
|
|
if (_tablet_active && _settings->value ("tablet/enabled", false).toBool())
|
|
{
|
|
timestrs << ", Pres: " << _tablet_pressure;
|
|
}
|
|
|
|
_status_time->setText (QString::fromStdString (timestrs.str()));
|
|
}
|
|
|
|
_last_fps_update += dt;
|
|
|
|
// update fps every sec
|
|
if (_last_fps_update > 1.f && !_last_frame_durations.empty())
|
|
{
|
|
auto avg_frame_duration
|
|
( std::accumulate ( _last_frame_durations.begin()
|
|
, _last_frame_durations.end()
|
|
, 0.
|
|
)
|
|
/ qreal (_last_frame_durations.size())
|
|
);
|
|
_status_fps->setText ( "FPS: " + QString::number (int (1. / avg_frame_duration))
|
|
+ " - Average frame time: " + QString::number(avg_frame_duration*1000.0) + "ms"
|
|
);
|
|
|
|
_last_frame_durations.clear();
|
|
_last_fps_update = 0.f;
|
|
}
|
|
|
|
guiWater->updatePos (_camera.position);
|
|
|
|
|
|
if (guidetailInfos->isVisible())
|
|
{
|
|
if(currentSelection.size() > 0)
|
|
{
|
|
std::stringstream select_info;
|
|
auto lastSelection = currentSelection.back();
|
|
|
|
switch (lastSelection.which())
|
|
{
|
|
case eEntry_Model:
|
|
{
|
|
auto instance(boost::get<selected_model_type>(lastSelection));
|
|
select_info << "filename: " << instance->model->filename
|
|
<< "\nunique ID: " << instance->uid
|
|
<< "\nposition X/Y/Z: " << instance->pos.x << " / " << instance->pos.y << " / " << instance->pos.z
|
|
<< "\nrotation X/Y/Z: " << instance->dir.x << " / " << instance->dir.y << " / " << instance->dir.z
|
|
<< "\nscale: " << instance->scale
|
|
<< "\ntextures Used: " << instance->model->header.nTextures
|
|
<< "\nsize category: " << instance->size_cat;
|
|
|
|
for (unsigned int j = 0; j < std::min(instance->model->header.nTextures, 6U); j++)
|
|
{
|
|
select_info << "\n " << (j + 1) << ": " << instance->model->_textures[j]->filename;
|
|
}
|
|
if (instance->model->header.nTextures > 25)
|
|
{
|
|
select_info << "\n and more.";
|
|
}
|
|
|
|
select_info << "\n";
|
|
break;
|
|
}
|
|
case eEntry_WMO:
|
|
{
|
|
auto instance(boost::get<selected_wmo_type>(lastSelection));
|
|
select_info << "filename: " << instance->wmo->filename
|
|
<< "\nunique ID: " << instance->mUniqueID
|
|
<< "\nposition X/Y/Z: " << instance->pos.x << " / " << instance->pos.y << " / " << instance->pos.z
|
|
<< "\nrotation X/Y/Z: " << instance->dir.x << " / " << instance->dir.y << " / " << instance->dir.z
|
|
<< "\ndoodad set: " << instance->doodadset()
|
|
<< "\ntextures used: " << instance->wmo->textures.size();
|
|
|
|
|
|
const unsigned int texture_count (std::min((unsigned int)(instance->wmo->textures.size()), 8U));
|
|
for (unsigned int j = 0; j < texture_count; j++)
|
|
{
|
|
select_info << "\n " << (j + 1) << ": " << instance->wmo->textures[j]->filename;
|
|
}
|
|
if (instance->wmo->textures.size() > 25)
|
|
{
|
|
select_info << "\n and more.";
|
|
}
|
|
|
|
select_info << "\n";
|
|
break;
|
|
}
|
|
case eEntry_MapChunk:
|
|
{
|
|
auto chunk(boost::get<selected_chunk_type>(lastSelection).chunk);
|
|
mcnk_flags const& flags = chunk->header_flags;
|
|
|
|
select_info << "MCNK " << chunk->px << ", " << chunk->py << " (" << chunk->py * 16 + chunk->px
|
|
<< ") of tile (" << chunk->mt->index.x << " " << chunk->mt->index.z << ")"
|
|
<< "\narea ID: " << chunk->getAreaID() << " (\"" << gAreaDB.getAreaName(chunk->getAreaID()) << "\")"
|
|
<< "\nflags: "
|
|
<< (flags.flags.has_mcsh ? "shadows " : "")
|
|
<< (flags.flags.impass ? "impassable " : "")
|
|
<< (flags.flags.lq_river ? "river " : "")
|
|
<< (flags.flags.lq_ocean ? "ocean " : "")
|
|
<< (flags.flags.lq_magma ? "lava" : "")
|
|
<< (flags.flags.lq_slime ? "slime" : "")
|
|
<< "\ntextures used: " << chunk->texture_set->num();
|
|
|
|
//! \todo get a list of textures and their flags as well as detail doodads.
|
|
|
|
select_info << "\n";
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
guidetailInfos->setText(select_info.str());
|
|
}
|
|
else
|
|
{
|
|
guidetailInfos->setText("");
|
|
}
|
|
}
|
|
}
|
|
|
|
math::vector_4d MapView::normalized_device_coords (int x, int y) const
|
|
{
|
|
return {2.0f * x / width() - 1.0f, 1.0f - 2.0f * y / height(), 0.0f, 1.0f};
|
|
}
|
|
|
|
float MapView::aspect_ratio() const
|
|
{
|
|
return float (width()) / float (height());
|
|
}
|
|
|
|
math::ray MapView::intersect_ray() const
|
|
{
|
|
float mx = _last_mouse_pos.x(), mz = _last_mouse_pos.y();
|
|
|
|
if (_display_mode == display_mode::in_3D)
|
|
{
|
|
// during rendering we multiply perspective * view
|
|
// so we need the same order here and then invert.
|
|
math::vector_3d const pos
|
|
(
|
|
( ( projection()
|
|
* model_view()
|
|
).inverted()
|
|
* normalized_device_coords (mx, mz)
|
|
).xyz_normalized_by_w()
|
|
);
|
|
|
|
return { _camera.position, pos - _camera.position };
|
|
}
|
|
else
|
|
{
|
|
math::vector_3d const pos
|
|
( _camera.position.x - (width() * 0.5f - mx) * _2d_zoom
|
|
, _camera.position.y
|
|
, _camera.position.z - (height() * 0.5f - mz) * _2d_zoom
|
|
);
|
|
|
|
return { pos, math::vector_3d(0.f, -1.f, 0.f) };
|
|
}
|
|
}
|
|
|
|
selection_result MapView::intersect_result(bool terrain_only)
|
|
{
|
|
selection_result results
|
|
( _world->intersect
|
|
( model_view().transposed()
|
|
, intersect_ray()
|
|
, terrain_only
|
|
, terrainMode == editing_mode::object
|
|
, _draw_terrain.get()
|
|
, _draw_wmo.get()
|
|
, _draw_models.get()
|
|
, _draw_hidden_models.get()
|
|
)
|
|
);
|
|
|
|
std::sort ( results.begin()
|
|
, results.end()
|
|
, [](selection_entry const& lhs, selection_entry const& rhs)
|
|
{
|
|
return lhs.first < rhs.first;
|
|
}
|
|
);
|
|
|
|
return results;
|
|
}
|
|
|
|
void MapView::doSelection (bool selectTerrainOnly, bool mouseMove)
|
|
{
|
|
selection_result results(intersect_result(selectTerrainOnly));
|
|
|
|
if (results.empty())
|
|
{
|
|
_world->reset_selection();
|
|
}
|
|
else
|
|
{
|
|
auto const& hit (results.front().second);
|
|
|
|
if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier))
|
|
{
|
|
if (hit.which() == eEntry_Model || hit.which() == eEntry_WMO)
|
|
{
|
|
if (!_world->is_selected(hit))
|
|
{
|
|
_world->add_to_selection(hit);
|
|
}
|
|
else if (!mouseMove)
|
|
{
|
|
_world->remove_from_selection(hit);
|
|
}
|
|
}
|
|
else if (hit.which() == eEntry_MapChunk)
|
|
{
|
|
_world->range_add_to_selection(_cursor_pos, objectEditor->brushRadius(), false);
|
|
}
|
|
|
|
}
|
|
else if (QGuiApplication::queryKeyboardModifiers().testFlag(Qt::ControlModifier))
|
|
{
|
|
if (hit.which() == eEntry_MapChunk)
|
|
{
|
|
_world->range_add_to_selection(_cursor_pos, objectEditor->brushRadius(), true);
|
|
}
|
|
}
|
|
else if (!_mod_space_down && !_mod_alt_down)
|
|
{
|
|
_world->reset_selection();
|
|
_world->add_to_selection(hit);
|
|
}
|
|
|
|
_cursor_pos = hit.which() == eEntry_Model ? boost::get<selected_model_type>(hit)->pos
|
|
: hit.which() == eEntry_WMO ? boost::get<selected_wmo_type>(hit)->pos
|
|
: hit.which() == eEntry_MapChunk ? boost::get<selected_chunk_type>(hit).position
|
|
: throw std::logic_error("bad variant");
|
|
}
|
|
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
void MapView::update_cursor_pos()
|
|
{
|
|
selection_result results (intersect_result (true));
|
|
|
|
if (!results.empty())
|
|
{
|
|
auto const& hit(results.front().second);
|
|
// hit cannot be something else than a chunk
|
|
_cursor_pos = boost::get<selected_chunk_type>(hit).position;
|
|
}
|
|
}
|
|
|
|
math::matrix_4x4 MapView::model_view() const
|
|
{
|
|
if (_display_mode == display_mode::in_2D)
|
|
{
|
|
math::vector_3d eye = _camera.position;
|
|
math::vector_3d target = eye;
|
|
target.y -= 1.f;
|
|
target.z -= 0.001f;
|
|
|
|
return math::look_at(eye, target, {0.f,1.f, 0.f});
|
|
}
|
|
else
|
|
{
|
|
return _camera.look_at_matrix();
|
|
}
|
|
}
|
|
math::matrix_4x4 MapView::projection() const
|
|
{
|
|
float far_z = _settings->value("farZ", 2048).toFloat();
|
|
|
|
if (_display_mode == display_mode::in_2D)
|
|
{
|
|
float half_width = width() * 0.5f * _2d_zoom;
|
|
float half_height = height() * 0.5f * _2d_zoom;
|
|
|
|
return math::ortho(-half_width, half_width, -half_height, half_height, -1.f, far_z);
|
|
}
|
|
else
|
|
{
|
|
return math::perspective(_camera.fov(), aspect_ratio(), 1.f, far_z);
|
|
}
|
|
}
|
|
|
|
void MapView::draw_map()
|
|
{
|
|
//! \ todo: make the current tool return the radius
|
|
float radius = 0.0f, inner_radius = 0.0f, angle = 0.0f, orientation = 0.0f;
|
|
math::vector_3d ref_pos;
|
|
bool angled_mode = false, use_ref_pos = false;
|
|
|
|
switch (terrainMode)
|
|
{
|
|
case editing_mode::ground:
|
|
radius = terrainTool->brushRadius();
|
|
inner_radius = terrainTool->innerRadius();
|
|
break;
|
|
case editing_mode::flatten_blur:
|
|
radius = flattenTool->brushRadius();
|
|
angle = flattenTool->angle();
|
|
orientation = flattenTool->orientation();
|
|
ref_pos = flattenTool->ref_pos();
|
|
angled_mode = flattenTool->angled_mode();
|
|
use_ref_pos = flattenTool->use_ref_pos();
|
|
break;
|
|
case editing_mode::paint:
|
|
radius = texturingTool->brush_radius();
|
|
inner_radius = texturingTool->hardness();
|
|
break;
|
|
case editing_mode::water:
|
|
radius = guiWater->brushRadius();
|
|
angle = guiWater->angle();
|
|
orientation = guiWater->orientation();
|
|
ref_pos = guiWater->ref_pos();
|
|
angled_mode = guiWater->angled_mode();
|
|
use_ref_pos = guiWater->use_ref_pos();
|
|
break;
|
|
case editing_mode::mccv:
|
|
radius = shaderTool->brushRadius();
|
|
break;
|
|
case editing_mode::areaid:
|
|
radius = ZoneIDBrowser->brushRadius();
|
|
break;
|
|
case editing_mode::holes:
|
|
radius = holeTool->brushRadius();
|
|
break;
|
|
case editing_mode::object:
|
|
radius = objectEditor->brushRadius();
|
|
break;
|
|
}
|
|
|
|
//! \note Select terrain below mouse, if no item selected or the item is map.
|
|
if (!(_world->has_selection()
|
|
|| _locked_cursor_mode.get()))
|
|
{
|
|
doSelection(true);
|
|
}
|
|
|
|
_world->draw ( model_view().transposed()
|
|
, projection().transposed()
|
|
, _cursor_pos
|
|
, terrainMode == editing_mode::mccv ? shader_color : cursor_color
|
|
, cursor_type.get()
|
|
, radius
|
|
, texturingTool->show_unpaintable_chunks()
|
|
, _draw_contour.get()
|
|
, inner_radius
|
|
, ref_pos
|
|
, angle
|
|
, orientation
|
|
, use_ref_pos
|
|
, angled_mode
|
|
, terrainMode == editing_mode::paint
|
|
, terrainMode == editing_mode::flags
|
|
, terrainMode == editing_mode::areaid
|
|
, terrainMode
|
|
, _camera.position
|
|
, _camera_moved_since_last_draw
|
|
, _draw_mfbo.get()
|
|
, _draw_wireframe.get()
|
|
, _draw_lines.get()
|
|
, _draw_terrain.get()
|
|
, _draw_wmo.get()
|
|
, _draw_water.get()
|
|
, _draw_wmo_doodads.get()
|
|
, _draw_models.get()
|
|
, _draw_model_animations.get()
|
|
, _draw_hole_lines.get() || terrainMode == editing_mode::holes
|
|
, _draw_models_with_box.get()
|
|
, _draw_hidden_models.get()
|
|
, _area_id_colors
|
|
, _draw_fog.get()
|
|
, terrainTool->_edit_type
|
|
, _display_all_water_layers.get() ? -1 : _displayed_water_layer.get()
|
|
, _display_mode
|
|
);
|
|
|
|
// reset after each world::draw call
|
|
_camera_moved_since_last_draw = false;
|
|
}
|
|
|
|
void MapView::keyPressEvent (QKeyEvent *event)
|
|
{
|
|
size_t const modifier
|
|
( ((event->modifiers() & Qt::ShiftModifier) ? MOD_shift : 0)
|
|
| ((event->modifiers() & Qt::ControlModifier) ? MOD_ctrl : 0)
|
|
| ((event->modifiers() & Qt::AltModifier) ? MOD_alt : 0)
|
|
| ((event->modifiers() & Qt::MetaModifier) ? MOD_meta : 0)
|
|
| ((event->modifiers() & Qt::KeypadModifier) ? MOD_num : 0)
|
|
| (_mod_space_down ? MOD_space : 0)
|
|
);
|
|
|
|
for (auto&& hotkey : hotkeys)
|
|
{
|
|
if (event->key() == hotkey.key && modifier == hotkey.modifiers && hotkey.condition())
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
|
|
hotkey.function();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Space)
|
|
_mod_space_down = true;
|
|
|
|
|
|
// movement
|
|
if (event->key() == Qt::Key_W)
|
|
{
|
|
moving = 1.0f;
|
|
}
|
|
if (event->key() == Qt::Key_S)
|
|
{
|
|
moving = -1.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Up)
|
|
{
|
|
lookat = 0.75f;
|
|
}
|
|
if (event->key() == Qt::Key_Down)
|
|
{
|
|
lookat = -0.75f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Right)
|
|
{
|
|
turn = 0.75f;
|
|
}
|
|
if (event->key() == Qt::Key_Left)
|
|
{
|
|
turn = -0.75f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_D)
|
|
{
|
|
strafing = 1.0f;
|
|
}
|
|
if (event->key() == Qt::Key_A)
|
|
{
|
|
strafing = -1.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Q)
|
|
{
|
|
updown = 1.0f;
|
|
}
|
|
if (event->key() == Qt::Key_E)
|
|
{
|
|
updown = -1.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_2 && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyx = 1;
|
|
}
|
|
if (event->key() == Qt::Key_8 && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyx = -1;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_4 && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyz = 1;
|
|
}
|
|
if (event->key() == Qt::Key_6 && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyz = -1;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_3 && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyy = 1;
|
|
}
|
|
if (event->key() == Qt::Key_1 && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyy = -1;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_7 && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyr = 1;
|
|
}
|
|
if (event->key() == Qt::Key_9 && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyr = -1;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Plus)
|
|
{
|
|
keys = 1;
|
|
|
|
switch (terrainMode)
|
|
{
|
|
case editing_mode::mccv:
|
|
{
|
|
shaderTool->addColorToPalette();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (event->key() == Qt::Key_Minus)
|
|
{
|
|
keys = -1;
|
|
}
|
|
if (event->key() == Qt::Key_Home)
|
|
{
|
|
_camera.position = math::vector_3d(_cursor_pos.x, _cursor_pos.y + 50, _cursor_pos.z); ;
|
|
_minimap->update();
|
|
}
|
|
|
|
if (event->key() == Qt::Key_L)
|
|
{
|
|
freelook = true;
|
|
}
|
|
}
|
|
|
|
void MapView::keyReleaseEvent (QKeyEvent* event)
|
|
{
|
|
if (event->key() == Qt::Key_Space)
|
|
_mod_space_down = false;
|
|
|
|
// movement
|
|
if (event->key() == Qt::Key_W || event->key() == Qt::Key_S)
|
|
{
|
|
moving = 0.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down)
|
|
{
|
|
lookat = 0.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Right || event->key() == Qt::Key_Left)
|
|
{
|
|
turn = 0.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_D || event->key() == Qt::Key_A)
|
|
{
|
|
strafing = 0.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Q || event->key() == Qt::Key_E)
|
|
{
|
|
updown = 0.0f;
|
|
}
|
|
|
|
|
|
if ((event->key() == Qt::Key_2 || event->key() == Qt::Key_8) && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyx = 0.0f;
|
|
}
|
|
|
|
if ((event->key() == Qt::Key_4 || event->key() == Qt::Key_6) && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyz = 0.0f;
|
|
}
|
|
|
|
if ((event->key() == Qt::Key_3 || event->key() == Qt::Key_1) && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyy = 0.0f;
|
|
}
|
|
|
|
if ((event->key() == Qt::Key_7 || event->key() == Qt::Key_9) && event->modifiers() & Qt::KeypadModifier)
|
|
{
|
|
keyr = 0.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_Plus || event->key() == Qt::Key_Minus)
|
|
{
|
|
keys = 0.0f;
|
|
}
|
|
|
|
if (event->key() == Qt::Key_L || event->key() == Qt::Key_Minus)
|
|
{
|
|
freelook = false;
|
|
}
|
|
|
|
}
|
|
|
|
void MapView::focusOutEvent (QFocusEvent*)
|
|
{
|
|
_mod_alt_down = false;
|
|
_mod_ctrl_down = false;
|
|
_mod_shift_down = false;
|
|
_mod_space_down = false;
|
|
_mod_num_down = false;
|
|
|
|
moving = 0.0f;
|
|
lookat = 0.0f;
|
|
turn = 0.0f;
|
|
strafing = 0.0f;
|
|
updown = 0.0f;
|
|
|
|
keyx = 0;
|
|
keyz = 0;
|
|
keyy = 0;
|
|
keyr = 0;
|
|
keys = 0;
|
|
|
|
leftMouse = false;
|
|
rightMouse = false;
|
|
MoveObj = false;
|
|
look = false;
|
|
freelook = false;
|
|
}
|
|
|
|
void MapView::mouseMoveEvent (QMouseEvent* event)
|
|
{
|
|
//! \todo: move the function call requiring a context in tick ?
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
QLineF const relative_movement (_last_mouse_pos, event->pos());
|
|
|
|
if ((look || freelook) && !(_mod_shift_down || _mod_ctrl_down || _mod_alt_down || _mod_space_down))
|
|
{
|
|
_camera.add_to_yaw(math::degrees(relative_movement.dx() / XSENS));
|
|
_camera.add_to_pitch(math::degrees(mousedir * relative_movement.dy() / YSENS));
|
|
_camera_moved_since_last_draw = true;
|
|
_minimap->update();
|
|
}
|
|
|
|
if (MoveObj)
|
|
{
|
|
mh = -aspect_ratio()*relative_movement.dx() / static_cast<float>(width());
|
|
mv = -relative_movement.dy() / static_cast<float>(height());
|
|
}
|
|
else
|
|
{
|
|
mh = 0.0f;
|
|
mv = 0.0f;
|
|
}
|
|
|
|
if (_mod_shift_down || _mod_ctrl_down || _mod_alt_down || _mod_space_down)
|
|
{
|
|
rh = relative_movement.dx() / XSENS * 5.0f;
|
|
rv = relative_movement.dy() / YSENS * 5.0f;
|
|
}
|
|
|
|
if (rightMouse && _mod_alt_down)
|
|
{
|
|
if (terrainMode == editing_mode::ground)
|
|
{
|
|
terrainTool->changeInnerRadius(relative_movement.dx() / 100.0f);
|
|
}
|
|
if (terrainMode == editing_mode::paint)
|
|
{
|
|
texturingTool->change_hardness(relative_movement.dx() / 300.0f);
|
|
}
|
|
}
|
|
|
|
if (rightMouse && _mod_shift_down)
|
|
{
|
|
if (terrainMode == editing_mode::ground)
|
|
{
|
|
terrainTool->moveVertices (_world.get(), -relative_movement.dy() / YSENS);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (rightMouse && _mod_space_down)
|
|
{
|
|
terrainTool->setOrientRelativeTo (_world.get(), _cursor_pos);
|
|
}
|
|
|
|
if (leftMouse && _mod_alt_down)
|
|
{
|
|
switch (terrainMode)
|
|
{
|
|
case editing_mode::ground:
|
|
terrainTool->changeRadius(relative_movement.dx() / XSENS);
|
|
break;
|
|
case editing_mode::flatten_blur:
|
|
flattenTool->changeRadius(relative_movement.dx() / XSENS);
|
|
break;
|
|
case editing_mode::paint:
|
|
texturingTool->change_radius(relative_movement.dx() / XSENS);
|
|
break;
|
|
case editing_mode::water:
|
|
guiWater->changeRadius(relative_movement.dx() / XSENS);
|
|
break;
|
|
case editing_mode::mccv:
|
|
shaderTool->changeRadius(relative_movement.dx() / XSENS);
|
|
break;
|
|
case editing_mode::areaid:
|
|
ZoneIDBrowser->changeRadius(relative_movement.dx() / XSENS);
|
|
break;
|
|
case editing_mode::holes:
|
|
holeTool->changeRadius(relative_movement.dx() / XSENS);
|
|
break;
|
|
case editing_mode::object:
|
|
objectEditor->changeRadius(relative_movement.dx() / XSENS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (leftMouse && _mod_space_down)
|
|
{
|
|
switch (terrainMode)
|
|
{
|
|
case editing_mode::ground:
|
|
terrainTool->changeSpeed(relative_movement.dx() / 30.0f);
|
|
break;
|
|
case editing_mode::flatten_blur:
|
|
flattenTool->changeSpeed(relative_movement.dx() / 30.0f);
|
|
break;
|
|
case editing_mode::paint:
|
|
texturingTool->change_pressure(relative_movement.dx() / 300.0f);
|
|
break;
|
|
case editing_mode::mccv:
|
|
shaderTool->changeSpeed(relative_movement.dx() / XSENS);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (leftMouse && (_mod_shift_down || _mod_ctrl_down))
|
|
{
|
|
if (terrainMode == editing_mode::object)
|
|
{
|
|
doSelection(false, true); // Required for radius selection in Object mode
|
|
}
|
|
}
|
|
|
|
if (_display_mode == display_mode::in_2D && leftMouse && _mod_alt_down && _mod_shift_down)
|
|
{
|
|
strafing = ((relative_movement.dx() / XSENS) / -1) * 5.0f;
|
|
moving = (relative_movement.dy() / YSENS) * 5.0f;
|
|
}
|
|
|
|
if (_display_mode == display_mode::in_2D && rightMouse && _mod_shift_down)
|
|
{
|
|
updown = (relative_movement.dy() / YSENS);
|
|
}
|
|
|
|
_last_mouse_pos = event->pos();
|
|
}
|
|
|
|
void MapView::selectModel(std::string const& model)
|
|
{
|
|
if (boost::ends_with (model, ".m2"))
|
|
{
|
|
ModelInstance mi(model);
|
|
_world->set_current_selection(boost::get<selected_model_type>(&mi));
|
|
|
|
}
|
|
else if (boost::ends_with (model, ".wmo"))
|
|
{
|
|
WMOInstance wi(model);
|
|
_world->set_current_selection(boost::get<selected_wmo_type>(&wi));
|
|
}
|
|
|
|
objectEditor->copy_current_selection(_world.get());
|
|
_rotation_editor_need_update = true;
|
|
}
|
|
|
|
void MapView::change_selected_wmo_doodadset(int set)
|
|
{
|
|
for (auto& selection : _world->current_selection())
|
|
{
|
|
if (selection.which() == eEntry_WMO)
|
|
{
|
|
auto wmo = boost::get<selected_wmo_type>(selection);
|
|
wmo->change_doodadset(set);
|
|
_world->updateTilesWMO(wmo, model_update::none);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MapView::mousePressEvent(QMouseEvent* event)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _(::gl, context());
|
|
|
|
switch (event->button())
|
|
{
|
|
case Qt::LeftButton:
|
|
leftMouse = true;
|
|
break;
|
|
|
|
case Qt::RightButton:
|
|
rightMouse = true;
|
|
break;
|
|
|
|
case Qt::MiddleButton:
|
|
if (_world->has_selection())
|
|
{
|
|
MoveObj = true;
|
|
}
|
|
|
|
if(terrainMode == editing_mode::mccv)
|
|
{
|
|
shaderTool->pickColor(_world.get(), _cursor_pos);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (leftMouse)
|
|
{
|
|
if (!(terrainMode == editing_mode::mccv && _mod_ctrl_down))
|
|
{
|
|
doSelection(false);
|
|
}
|
|
}
|
|
else if (rightMouse)
|
|
{
|
|
look = true;
|
|
}
|
|
}
|
|
|
|
void MapView::wheelEvent (QWheelEvent* event)
|
|
{
|
|
//! \todo: move the function call requiring a context in tick ?
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
|
|
auto&& delta_for_range
|
|
( [&] (float range)
|
|
{
|
|
//! \note / 8.f for degrees, / 40.f for smoothness
|
|
return (_mod_ctrl_down ? 0.01f : 0.1f)
|
|
* range
|
|
// alt = horizontal delta
|
|
* (_mod_alt_down ? event->angleDelta().x() : event->angleDelta().y())
|
|
/ 320.f
|
|
;
|
|
}
|
|
);
|
|
|
|
if (terrainMode == editing_mode::ground)
|
|
{
|
|
if (_mod_shift_down)
|
|
{
|
|
terrainTool->changeAngle (delta_for_range (178.f));
|
|
}
|
|
else if (_mod_alt_down)
|
|
{
|
|
terrainTool->changeOrientation (delta_for_range (360.f));
|
|
}
|
|
}
|
|
else if (terrainMode == editing_mode::paint)
|
|
{
|
|
if (_mod_space_down)
|
|
{
|
|
texturingTool->change_brush_level (delta_for_range (255.f));
|
|
}
|
|
else if (_mod_alt_down)
|
|
{
|
|
texturingTool->change_spray_size (delta_for_range (39.f));
|
|
}
|
|
else if (_mod_shift_down)
|
|
{
|
|
texturingTool->change_spray_pressure (delta_for_range (10.f));
|
|
}
|
|
}
|
|
else if (terrainMode == editing_mode::flatten_blur)
|
|
{
|
|
if (_mod_alt_down)
|
|
{
|
|
flattenTool->changeOrientation (delta_for_range (360.f));
|
|
}
|
|
else if (_mod_shift_down)
|
|
{
|
|
flattenTool->changeAngle (delta_for_range (89.f));
|
|
}
|
|
else if (_mod_space_down)
|
|
{
|
|
//! \note not actual range
|
|
flattenTool->changeHeight (delta_for_range (40.f));
|
|
}
|
|
}
|
|
else if (terrainMode == editing_mode::water)
|
|
{
|
|
if (_mod_alt_down)
|
|
{
|
|
guiWater->changeOrientation (delta_for_range (360.f));
|
|
}
|
|
else if (_mod_shift_down)
|
|
{
|
|
guiWater->changeAngle (delta_for_range (89.f));
|
|
}
|
|
else if (_mod_space_down)
|
|
{
|
|
//! \note not actual range
|
|
guiWater->change_height (delta_for_range (40.f));
|
|
}
|
|
}
|
|
}
|
|
|
|
void MapView::mouseReleaseEvent (QMouseEvent* event)
|
|
{
|
|
switch (event->button())
|
|
{
|
|
case Qt::LeftButton:
|
|
leftMouse = false;
|
|
|
|
if (_display_mode == display_mode::in_2D)
|
|
{
|
|
strafing = 0;
|
|
moving = 0;
|
|
}
|
|
break;
|
|
|
|
case Qt::RightButton:
|
|
rightMouse = false;
|
|
|
|
look = false;
|
|
|
|
if (_display_mode == display_mode::in_2D)
|
|
updown = 0;
|
|
|
|
break;
|
|
|
|
case Qt::MiddleButton:
|
|
MoveObj = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MapView::save(save_mode mode)
|
|
{
|
|
bool save = true;
|
|
|
|
if (AsyncLoader::instance().important_object_failed_loading())
|
|
{
|
|
save = false;
|
|
QPushButton *yes, *no;
|
|
|
|
QMessageBox first_warning;
|
|
first_warning.setIcon(QMessageBox::Critical);
|
|
first_warning.setWindowIcon(QIcon (":/icon"));
|
|
first_warning.setWindowTitle("Some models couldn't be loaded");
|
|
first_warning.setText("Error:\nSome models could not be loaded and saving will cause collision and culling issues, would you still like to save ?");
|
|
// roles are swapped to force the user to pay attention and both are "accept" roles so that escape does nothing
|
|
no = first_warning.addButton("No", QMessageBox::ButtonRole::AcceptRole);
|
|
yes = first_warning.addButton("Yes", QMessageBox::ButtonRole::YesRole);
|
|
first_warning.setDefaultButton(no);
|
|
|
|
first_warning.exec();
|
|
|
|
if (first_warning.clickedButton() == yes)
|
|
{
|
|
QMessageBox second_warning;
|
|
second_warning.setIcon(QMessageBox::Warning);
|
|
second_warning.setWindowIcon(QIcon (":/icon"));
|
|
second_warning.setWindowTitle("Are you sure ?");
|
|
second_warning.setText( "If you save you will have to save again all the adt containing the defective/missing models once you've fixed said models to correct all the issues.\n"
|
|
"By clicking yes you accept to bear all the consequences of your action and forfeit the right to complain to the developers about any culling and collision issues.\n\n"
|
|
"So... do you REALLY want to save ?"
|
|
);
|
|
no = second_warning.addButton("No", QMessageBox::ButtonRole::YesRole);
|
|
yes = second_warning.addButton("Yes", QMessageBox::ButtonRole::AcceptRole);
|
|
second_warning.setDefaultButton(no);
|
|
|
|
second_warning.exec();
|
|
|
|
if (second_warning.clickedButton() == yes)
|
|
{
|
|
save = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( mode == save_mode::current
|
|
&& save
|
|
&& (QMessageBox::warning
|
|
(nullptr
|
|
, "Save current map tile only"
|
|
, "This can cause a collision bug when placing objects between two ADT borders!\n\n"
|
|
"We recommend you to use the normal save function rather than "
|
|
"this one to get the collisions right."
|
|
, QMessageBox::Save | QMessageBox::Cancel
|
|
, QMessageBox::Cancel
|
|
) == QMessageBox::Cancel
|
|
)
|
|
)
|
|
{
|
|
save = false;
|
|
}
|
|
|
|
if (save)
|
|
{
|
|
makeCurrent();
|
|
opengl::context::scoped_setter const _ (::gl, context());
|
|
|
|
switch (mode)
|
|
{
|
|
case save_mode::current: _world->mapIndex.saveTile(tile_index(_camera.position), _world.get()); break;
|
|
case save_mode::changed: _world->mapIndex.saveChanged(_world.get()); break;
|
|
case save_mode::all: _world->mapIndex.saveall(_world.get()); break;
|
|
}
|
|
|
|
AsyncLoader::instance().reset_object_fail();
|
|
|
|
|
|
_main_window->statusBar()->showMessage("Map saved", 2000);
|
|
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::warning
|
|
( nullptr
|
|
, "Map NOT saved"
|
|
, "The map wasn NOT saved, don't forget to save before leaving"
|
|
, QMessageBox::Ok
|
|
);
|
|
}
|
|
}
|
|
|
|
void MapView::addHotkey(Qt::Key key, size_t modifiers, std::function<void()> function, std::function<bool()> condition)
|
|
{
|
|
hotkeys.emplace_front (key, modifiers, function, condition);
|
|
}
|