major clean up and rework of minimap generator. it is now much faster and never skips to load an asset. it should not stall the process either.

UX improvements for dock widgets and minimap selector widget. The long requested ADT coord promt is now back.
This commit is contained in:
Skarn
2021-11-17 21:13:03 +03:00
parent 484fc38d82
commit 58c4d0fb02
26 changed files with 563 additions and 272 deletions

View File

@@ -11,21 +11,7 @@
bool AsyncLoader::is_loading()
{
std::lock_guard<std::mutex> const lock (_guard);
if (_currently_loading.empty())
{
return false;
}
else
{
for (auto async_obj : _currently_loading)
{
if ((!async_obj->loading_failed() && async_obj->filename.length())) // the filename check is a hack
{
return true;
}
}
}
return false;
return !_currently_loading.empty();
}
void AsyncLoader::process()

View File

@@ -81,4 +81,5 @@ public:
}
virtual void finishLoading() = 0;
virtual void waitForChildrenLoaded() = 0;
};

View File

@@ -223,8 +223,8 @@ MPQFile::MPQFile(std::string const& filename)
return;
}
LogError << "File '" << filename << "' does not exist." << std::endl;
//throw std::invalid_argument ("File '" + filename + "' does not exist.");
//LogError << "File '" << filename << "' does not exist." << std::endl;
throw std::invalid_argument ("File '" + filename + "' does not exist.");
}
MPQFile::~MPQFile()

View File

@@ -32,6 +32,7 @@ public:
bool openFile(const std::string& filename, HANDLE* fileHandle) const;
void finishLoading();
virtual void waitForChildrenLoaded() override {};
static bool allFinishedLoading();
static void allFinishLoading();

View File

@@ -602,6 +602,15 @@ void MapChunk::updateVerticesData()
update_intersect_points();
}
void MapChunk::recalcExtents()
{
for (int i(0); i < mapbufsize; ++i)
{
vmin.y = std::min(vmin.y, mVertices[i].y);
vmax.y = std::max(vmax.y, mVertices[i].y);
}
}
math::vector_3d MapChunk::getNeighborVertex(int i, unsigned dir)
{
// i - vertex index

View File

@@ -132,6 +132,7 @@ public:
bool hasColors() const { return hasMCCV; };
void updateVerticesData();
void recalcExtents();
void recalcNorms();
void updateNormalsData();
math::vector_3d getNeighborVertex(int i, unsigned dir);

View File

@@ -78,8 +78,32 @@ MapTile::~MapTile()
_world->remove_models_if_needed(uids);
}
void MapTile::waitForChildrenLoaded()
{
for (auto& instance : object_instances)
{
instance.first->wait_until_loaded();
instance.first->waitForChildrenLoaded();
}
for (int i = 0; i < 16; ++i)
{
for (int j = 0; j < 16; ++j)
{
for (int k = 0; k < mChunks[i][j].get()->texture_set->num(); ++k)
{
(*mChunks[i][j].get()->texture_set->getTextures())[k].get()->wait_until_loaded();
}
}
}
}
void MapTile::finishLoading()
{
if (finished)
return;
MPQFile theFile(filename);
Log << "Opening tile " << index.x << ", " << index.z << " (\"" << filename << "\") from " << (theFile.isExternal() ? "disk" : "MPQ") << "." << std::endl;
@@ -369,6 +393,20 @@ float MapTile::getMinHeight()
return _extents[0].y;
}
void MapTile::forceRecalcExtents()
{
for (int i = 0; i < 16; ++i)
{
for (int j = 0; j < 16; ++j)
{
MapChunk* chunk = mChunks[i][j].get();
chunk->recalcExtents();
_extents[0].y = std::min(_extents[0].y, chunk->getMinHeight());
_extents[1].y = std::max(_extents[1].y, chunk->getMaxHeight());
}
}
}
void MapTile::convert_alphamap(bool to_big_alpha)
{
mBigAlpha = true;

View File

@@ -52,6 +52,7 @@ public:
~MapTile();
void finishLoading();
void waitForChildrenLoaded();
//! \todo on destruction, unload ModelInstances and WMOInstances on this tile:
// a) either keep up the information what tiles the instances are on at all times
@@ -70,6 +71,7 @@ public:
//! \brief Get the maximum height of terrain on this map tile.
float getMaxHeight();
float getMinHeight();
void forceRecalcExtents();
void convert_alphamap(bool to_big_alpha);

View File

@@ -74,6 +74,7 @@
#include <QDateTime>
#include <QCursor>
#include <QFileDialog>
#include <QProgressDialog>
#include <algorithm>
#include <cmath>
@@ -230,6 +231,7 @@ void MapView::set_editing_mode (editing_mode mode)
_world->getTerrainParamsUniformBlock()->draw_impass_overlay = false;
_world->getTerrainParamsUniformBlock()->draw_paintability_overlay = false;
_world->getTerrainParamsUniformBlock()->draw_selection_overlay = false;
_minimap->use_selection(nullptr);
switch (mode)
{
@@ -269,6 +271,7 @@ void MapView::set_editing_mode (editing_mode mode)
break;
case editing_mode::minimap:
_world->getTerrainParamsUniformBlock()->draw_selection_overlay = true;
_minimap->use_selection(minimapTool->getSelectedTiles());
break;
default:
break;
@@ -548,6 +551,7 @@ void MapView::setupTexturePainterUi()
_texture_browser_dock->setFeatures(QDockWidget::DockWidgetMovable
| QDockWidget::DockWidgetFloatable
| QDockWidget::DockWidgetClosable);
_texture_browser_dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea);
_main_window->addDockWidget(Qt::BottomDockWidgetArea, _texture_browser_dock);
_texture_browser_dock->hide();
@@ -670,6 +674,7 @@ void MapView::setupTexturePainterUi()
| QDockWidget::DockWidgetFloatable
| QDockWidget::DockWidgetClosable);
_main_window->addDockWidget(Qt::BottomDockWidgetArea, _texture_picker_dock);
_texture_picker_dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
_texture_picker_dock->setFloating(true);
_texture_picker_dock->hide();
connect(this, &QObject::destroyed, _texture_picker_dock, &QObject::deleteLater);
@@ -837,6 +842,7 @@ void MapView::setupNodeEditor()
auto _node_editor = new noggit::Red::NodeEditor::Ui::NodeEditorWidget(this);
_node_editor_dock = new QDockWidget("Node editor", this);
_node_editor_dock->setWidget(_node_editor);
_node_editor_dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea);
_main_window->addDockWidget(Qt::LeftDockWidgetArea, _node_editor_dock);
_node_editor_dock->setFeatures(QDockWidget::DockWidgetMovable
@@ -913,6 +919,9 @@ void MapView::setupDetailInfos()
| QDockWidget::DockWidgetFloatable
| QDockWidget::DockWidgetClosable);
_detail_infos_dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea | Qt::LeftDockWidgetArea);
_main_window->addDockWidget(Qt::BottomDockWidgetArea, _detail_infos_dock);
_detail_infos_dock->setFloating(true);
_detail_infos_dock->hide();
@@ -1011,8 +1020,6 @@ void MapView::setupFileMenu()
}
);
ADD_ACTION (file_menu, "Save minimaps", "Ctrl+Shift+P", [this] { saving_minimap = true; });
ADD_ACTION ( file_menu
, "Write coordinates to port.txt"
, Qt::Key_G
@@ -2324,11 +2331,15 @@ void MapView::setupMinimap()
{
_minimap = new noggit::ui::minimap_widget(this);
_minimap_dock = new QDockWidget("Minimap", this);
_minimap_dock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
_minimap_dock->setFixedSize(_minimap->sizeHint());
_minimap_dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::BottomDockWidgetArea | Qt::TopDockWidgetArea);
_minimap->world (_world.get());
_minimap->camera (&_camera);
_minimap->draw_boundaries (_show_minimap_borders.get());
_minimap->draw_skies (_show_minimap_skies.get());
_minimap->set_resizeable(true);
connect ( _minimap, &noggit::ui::minimap_widget::map_clicked
, [this] (math::vector_3d const& pos)
@@ -2341,8 +2352,12 @@ void MapView::setupMinimap()
| QDockWidget::DockWidgetFloatable
| QDockWidget::DockWidgetClosable
);
_minimap_dock->setWidget(_minimap);
_main_window->addDockWidget (Qt::RightDockWidgetArea, _minimap_dock);
auto minimap_scroll_area = new QScrollArea(_minimap_dock);
minimap_scroll_area->setWidget(_minimap);
minimap_scroll_area->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
_minimap_dock->setWidget(minimap_scroll_area);
_main_window->addDockWidget (Qt::LeftDockWidgetArea, _minimap_dock);
_minimap_dock->setVisible (false);
_minimap_dock->setFloating(true);
_minimap_dock->move(_main_window->rect().center() - _minimap->rect().center());
@@ -2679,12 +2694,14 @@ void MapView::initializeGL()
void MapView::saveMinimap(MinimapRenderSettings* settings)
{
// This convoluted logic is necessary here if we want to draw minimaps in the same OpenGL context.
// And we do, to avoid loading geometry twice. Even though, offscreen one in the background would be nice.
// The idea is, if rendering fails due to unfinished loading, we skip to the next frame until we are able to render.
opengl::context::scoped_setter const _ (::gl, context());
bool mmap_render_success = false;
static QProgressBar* progress;
static QPushButton* cancel_btn;
switch (settings->export_mode)
{
case MinimapGenMode::CURRENT_ADT:
@@ -2693,121 +2710,240 @@ void MapView::saveMinimap(MinimapRenderSettings* settings)
if (_world->mapIndex.hasTile(tile))
{
mmap_render_success = _world->saveMinimap(tile, settings);
}
else
{
saving_minimap = false;
mmap_render_success = _world->saveMinimap(tile, settings, _mmap_combined_image);
}
if (mmap_render_success)
{
saving_minimap = false;
_world->mapIndex.saveMinimapMD5translate();
}
saving_minimap = false;
break;
}
case MinimapGenMode::MAP:
{
tile_index tile = tile_index(mmap_render_index / 64, mmap_render_index % 64);
if (_world->mapIndex.hasTile(tile))
// init progress
if (!_mmap_async_index)
{
mmap_render_success = _world->saveMinimap(tile, settings);
progress = new QProgressBar(nullptr);
progress->setMinimum(0);
progress->setMaximum(_world->mapIndex.getNumExistingTiles());
_main_window->statusBar()->addPermanentWidget(progress);
if (mmap_render_success)
cancel_btn = new QPushButton(nullptr);
cancel_btn->setText("Cancel");
connect(cancel_btn, &QPushButton::clicked,
[=, this]
{
_mmap_async_index = 0;
_mmap_render_index = 0;
saving_minimap = false;
progress->deleteLater();
cancel_btn->deleteLater();
_mmap_combined_image.reset();
});
_main_window->statusBar()->addPermanentWidget(cancel_btn);
connect(this, &MapView::updateProgress, progress, &QProgressBar::setValue);
// setup combined image if necessary
if (settings->combined_minimap)
{
mmap_render_index++;
_mmap_combined_image.emplace(8192, 8192, QImage::Format_RGBA8888);
_mmap_combined_image->fill(Qt::black);
}
}
if (!saving_minimap)
return;
if (_mmap_async_index < 4096 && _mmap_render_index < progress->maximum())
{
tile_index tile = tile_index(_mmap_async_index / 64, _mmap_async_index % 64);
if (_world->mapIndex.hasTile(tile))
{
opengl::context::scoped_setter const _(::gl, context());
makeCurrent();
mmap_render_success = _world->saveMinimap(tile, settings, _mmap_combined_image);
_mmap_render_index++;
emit updateProgress(_mmap_render_index);
if (!mmap_render_success)
{
LogError << "Minimap rendered incorrectly for tile: " << tile.x << "_" << tile.z << std::endl;
}
}
_mmap_async_index++;
}
else
{
do
{
mmap_render_index++;
tile.x = mmap_render_index / 64;
tile.z = mmap_render_index % 64;
} while (!_world->mapIndex.hasTile(tile) && mmap_render_index != 4095 );
}
if (mmap_render_success && mmap_render_index >= 4095)
{
_mmap_async_index = 0;
_mmap_render_index = 0;
saving_minimap = false;
mmap_render_index = 0;
mmap_render_success = false;
progress->deleteLater();
cancel_btn->deleteLater();
_world->mapIndex.saveMinimapMD5translate();
// save combined minimap
if (settings->combined_minimap)
{
QString image_path = QString(std::string(_world->basename + "_combined_minimap.png").c_str());
QSettings app_settings;
QString str = app_settings.value("project/path").toString();
if (!(str.endsWith('\\') || str.endsWith('/')))
{
str += "/";
}
QDir dir(str + "/textures/minimap/");
if (!dir.exists())
dir.mkpath(".");
_mmap_combined_image->save(dir.filePath(image_path));
_mmap_combined_image.reset();
}
}
//_main_window->statusBar()->showMessage("Minimap rendering done.", 2000);
break;
}
}
case MinimapGenMode::SELECTED_ADTS:
{
auto selected_tiles = minimapTool->getSelectedTiles();
while (mmap_render_index < 4096)
// init progress
if (!_mmap_async_index)
{
progress = new QProgressBar(nullptr);
progress->setMinimum(0);
bool is_selected = selected_tiles->at(mmap_render_index);
unsigned n_selected_tiles = 0;
if (is_selected)
for (int i = 0; i < 4096; ++i)
{
tile_index tile = tile_index(mmap_render_index / 64, mmap_render_index % 64);
if (selected_tiles->at(i))
n_selected_tiles++;
}
progress->setMaximum(n_selected_tiles);
_main_window->statusBar()->addPermanentWidget(progress);
cancel_btn = new QPushButton(nullptr);
cancel_btn->setText("Cancel");
connect(cancel_btn, &QPushButton::clicked,
[=, this]
{
_mmap_async_index = 0;
_mmap_render_index = 0;
saving_minimap = false;
progress->deleteLater();
cancel_btn->deleteLater();
_mmap_combined_image.reset();
});
_main_window->statusBar()->addPermanentWidget(cancel_btn);
connect(this, &MapView::updateProgress, progress, &QProgressBar::setValue);
// setup combined image if necessary
if (settings->combined_minimap)
{
_mmap_combined_image.emplace(8192, 8192, QImage::Format_RGBA8888);
_mmap_combined_image->fill(Qt::black);
}
}
if (!saving_minimap)
return;
if (_mmap_async_index < 4096 && _mmap_render_index < progress->maximum())
{
if (selected_tiles->at(_mmap_async_index))
{
tile_index tile = tile_index(_mmap_async_index / 64, _mmap_async_index % 64);
if (_world->mapIndex.hasTile(tile))
{
mmap_render_success = _world->saveMinimap(tile, settings);
mmap_render_success = _world->saveMinimap(tile, settings, _mmap_combined_image);
_mmap_render_index++;
if (mmap_render_success)
emit updateProgress(_mmap_render_index);
if (!mmap_render_success)
{
mmap_render_index++;
break;
}
else
{
break;
LogError << "Minimap rendered incorrectly for tile: " << tile.x << "_" << tile.z << std::endl;
}
}
else
{
mmap_render_index++;
}
}
else
{
mmap_render_index++;
}
_mmap_async_index++;
}
if (mmap_render_success && mmap_render_index >= 4095)
else
{
_mmap_async_index = 0;
_mmap_render_index = 0;
saving_minimap = false;
mmap_render_index = 0;
mmap_render_success = false;
progress->deleteLater();
cancel_btn->deleteLater();
_world->mapIndex.saveMinimapMD5translate();
// save combined minimap
if (settings->combined_minimap)
{
QString image_path = QString(std::string(_world->basename + "_combined_minimap.png").c_str());
QSettings app_settings;
QString str = app_settings.value("project/path").toString();
if (!(str.endsWith('\\') || str.endsWith('/')))
{
str += "/";
}
QDir dir(str + "/textures/minimap/");
if (!dir.exists())
dir.mkpath(".");
_mmap_combined_image->save(dir.filePath(image_path));
_mmap_combined_image.reset();
}
}
break;
}
}
minimapTool->progressUpdate(mmap_render_index);
//minimapTool->progressUpdate(0);
}
void MapView::paintGL()
{
ZoneScoped;
static bool lock = false;
if (lock)
return;
if (!_needs_redraw)
return;
else
_needs_redraw = false;
ZoneScoped;
if (!_gl_initialized)
{
initializeGL();
@@ -2819,36 +2955,36 @@ void MapView::paintGL()
return;
}
opengl::context::scoped_setter const _ (::gl, context());
if (saving_minimap)
{
saveMinimap(minimapTool->getMinimapRenderSettings());
_main_window->setEnabled(false);
}
const qreal now(_startup_time.elapsed() / 1000.0);
_last_frame_durations.emplace_back (now - _last_update);
// minimap rendering
if (saving_minimap)
{
opengl::context::scoped_setter const _(::gl, context());
makeCurrent();
_camera_moved_since_last_draw = true;
lock = true;
saveMinimap(minimapTool->getMinimapRenderSettings());
lock = false;
return;
}
opengl::context::scoped_setter const _(::gl, context());
makeCurrent();
gl.clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
draw_map();
if (!saving_minimap)
{
draw_map();
tick (now - _last_update);
_main_window->setEnabled(true);
}
_last_update = now;
if (saving_minimap)
{
gl.clear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
if (_gizmo_on.get() && _world->has_selection())
{

View File

@@ -42,6 +42,7 @@
#include <unordered_set>
#include <thread>
#include <array>
#include <optional>
#include <ui_MapViewOverlay.h>
@@ -182,9 +183,6 @@ private:
bool _rotation_editor_need_update = false;
bool mmap_render_success = false;
int mmap_render_index = 0;
// Vars for the ground editing toggle mode store the status of some
// view settings when the ground editing mode is switched on to
// restore them if switch back again
@@ -224,6 +222,7 @@ signals:
void uid_fix_failed();
void resized();
void saved();
void updateProgress(int value);
public slots:
void on_exit_prompt();
@@ -253,6 +252,7 @@ public:
void randomizeShaderRotation();
void randomizeStampRotation();
void onSettingsSave();
noggit::ui::minimap_widget* getMinimapWidget() const { return _minimap; }
void set_editing_mode (editing_mode);
editing_mode get_editing_mode() { return terrainMode; };
@@ -400,6 +400,10 @@ private:
bool _destroying = false;
bool _needs_redraw = false;
unsigned _mmap_async_index = 0;
unsigned _mmap_render_index = 0;
std::optional<QImage> _mmap_combined_image;
opengl::scoped::deferred_upload_buffers<2> _buffers;
public:

View File

@@ -79,6 +79,19 @@ void Model::finishLoading()
_state_changed.notify_all();
}
void Model::waitForChildrenLoaded()
{
for (auto& tex : _textures)
{
tex.get()->wait_until_loaded();
}
for (auto& pair : _replaceTextures)
{
pair.second.get()->wait_until_loaded();
}
}
bool Model::isAnimated(const MPQFile& f)
{

View File

@@ -253,6 +253,7 @@ public:
void updateEmitters(float dt);
virtual void finishLoading();
virtual void waitForChildrenLoaded() override;
bool is_hidden() const { return _hidden; }
void toggle_visibility() { _hidden = !_hidden; }

View File

@@ -222,7 +222,6 @@ math::vector_3d* ModelInstance::getExtents()
return &extents[0];
}
wmo_doodad_instance::wmo_doodad_instance(std::string const& filename, MPQFile* f, noggit::NoggitRenderContext context)
: ModelInstance(filename, context)
{
@@ -280,3 +279,4 @@ void wmo_doodad_instance::update_transform_matrix_wmo(WMOInstance* wmo)
_need_matrix_update = false;
}

View File

@@ -162,6 +162,7 @@ public:
[[nodiscard]]
virtual bool isWMODoodad() const override { return true; };
protected:
// to avoid redefining recalcExtents
void updateTransformMatrix() override { }

View File

@@ -46,6 +46,7 @@ struct blp_texture : public AsyncObject
{
blp_texture (std::string const& filename, noggit::NoggitRenderContext context);
void finishLoading();
virtual void waitForChildrenLoaded() override {};
void loadFromUncompressedData(BLPHeader const* lHeader, char const* lData);
void loadFromCompressedData(BLPHeader const* lHeader, char const* lData);

View File

@@ -334,6 +334,20 @@ void WMO::finishLoading ()
_state_changed.notify_all();
}
void WMO::waitForChildrenLoaded()
{
for (auto& tex : textures)
{
tex.get()->wait_until_loaded();
}
for (auto& doodad : modelis)
{
doodad.model->wait_until_loaded();
doodad.model->waitForChildrenLoaded();
}
}
void WMO::draw ( opengl::scoped::use_program& wmo_shader
, math::matrix_4x4 const& model_view
, math::matrix_4x4 const& projection
@@ -728,9 +742,9 @@ void WMOGroup::upload()
}
else
{
draw_call->samplers[draw_call->n_used_samplers] = _render_batches[batch_counter].tex_array1;
_render_batches[batch_counter].tex_array1 = draw_call->n_used_samplers;
draw_call->n_used_samplers++;
//draw_call->samplers[draw_call->n_used_samplers] = _render_batches[batch_counter].tex_array1;
//_render_batches[batch_counter].tex_array1 = draw_call->n_used_samplers;
//draw_call->n_used_samplers++;
}
}
}
@@ -891,7 +905,15 @@ void WMOGroup::load()
f.read (&header, sizeof (wmo_group_header));
WMOFog &wf = wmo->fogs[header.fogs[0]];
unsigned fog_index = header.fogs[0];
// downport hack
if (fog_index >= wmo->fogs.size())
{
fog_index = 0;
}
WMOFog &wf = wmo->fogs[fog_index];
if (wf.r2 <= 0) fog = -1; // default outdoor fog..?
else fog = header.fogs[0];
@@ -1319,6 +1341,12 @@ void WMOGroup::load()
for (auto doodad : _doodad_ref)
{
if (doodad >= wmo->modelis.size())
{
continue;
LogError << "The WMO file currently loaded is potentially corrupt. Non-existing doodad referenced." << std::endl;
}
lenmin = 999999.0f * 999999.0f;
ModelInstance& mi = wmo->modelis[doodad];
for (unsigned int j = 0; j < wmo->lights.size(); j++)

View File

@@ -328,6 +328,8 @@ public:
void finishLoading();
virtual void waitForChildrenLoaded() override;
void unload();
std::map<uint32_t, std::vector<wmo_doodad_instance>> doodads_per_group(uint16_t doodadset) const;

View File

@@ -106,28 +106,28 @@ bool World::IsEditableWorld(int pMapId)
}
World::World(const std::string& name, int map_id, noggit::NoggitRenderContext context, bool create_empty)
: _model_instance_storage(this)
, _tile_update_queue(this)
, mapIndex (name, map_id, this, context, create_empty)
, horizon(name, &mapIndex)
, mWmoFilename("")
, mWmoEntry(ENTRY_MODF())
, ol(nullptr)
, animtime(0)
, time(1450)
, basename(name)
, fogdistance(777.0f)
, culldistance(fogdistance)
, skies(nullptr)
, outdoorLightStats(OutdoorLightStats())
, _current_selection()
, _settings (new QSettings())
, _view_distance(_settings->value ("view_distance", 1000.f).toFloat())
, _context(context)
, _liquid_texture_manager(context)
: _model_instance_storage(this)
, _tile_update_queue(this)
, mapIndex(name, map_id, this, context, create_empty)
, horizon(name, &mapIndex)
, mWmoFilename("")
, mWmoEntry(ENTRY_MODF())
, ol(nullptr)
, animtime(0)
, time(1450)
, basename(name)
, fogdistance(777.0f)
, culldistance(fogdistance)
, skies(nullptr)
, outdoorLightStats(OutdoorLightStats())
, _current_selection()
, _settings(new QSettings())
, _view_distance(_settings->value("view_distance", 1000.f).toFloat())
, _context(context)
, _liquid_texture_manager(context)
{
LogDebug << "Loading world \"" << name << "\"." << std::endl;
_loaded_tiles_buffer[0] = nullptr;
_loaded_tiles_buffer[0] = std::make_pair<std::pair<int, int>, MapTile*>(std::make_pair(0, 0), nullptr);
}
void World::update_selection_pivot()
@@ -1093,11 +1093,25 @@ void World::draw ( math::matrix_4x4 const& model_view
tile->recalcObjectInstanceExtents();
tile->recalcCombinedExtents();
if (minimap_render)
{
auto& tile_extents = tile->getCombinedExtents();
tile->calcCamDist(camera_pos);
tile->tile_frustum_culled = false;
tile->objects_frustum_cull_test = 2;
tile->tile_occluded = false;
_loaded_tiles_buffer[tile_counter] = std::make_pair(std::make_pair(tile->index.x, tile->index.z), tile);
tile_counter++;
_n_loaded_tiles++;
continue;
}
auto& tile_extents = tile->getCombinedExtents();
if (frustum.intersects(tile_extents[1], tile_extents[0]) || tile->getChunkUpdateFlags())
{
tile->calcCamDist(camera_pos);
_loaded_tiles_buffer[tile_counter] = tile;
_loaded_tiles_buffer[tile_counter] = std::make_pair(std::make_pair(tile->index.x, tile->index.z), tile);
tile->objects_frustum_cull_test = 1;
if (frustum.contains(tile_extents[0]) && frustum.contains(tile_extents[1]))
@@ -1126,26 +1140,26 @@ void World::draw ( math::matrix_4x4 const& model_view
}
auto buf_end = _loaded_tiles_buffer.begin() + tile_counter;
_loaded_tiles_buffer[tile_counter] = nullptr;
_loaded_tiles_buffer[tile_counter] = std::make_pair<std::pair<int, int>, MapTile*>(std::make_pair<int, int>(0, 0), nullptr);
// It is always import to sort tiles __front to back__.
// Otherwise selection would not work. Overdraw overhead is gonna occur as well.
// TODO: perhaps parallel sort?
std::sort(_loaded_tiles_buffer.begin(), buf_end,
[](MapTile* a, MapTile* b) -> bool
[](std::pair<std::pair<int, int>, MapTile*>& a, std::pair<std::pair<int, int>, MapTile*>& b) -> bool
{
if (!a)
if (!a.second)
{
return false;
}
if (!b)
if (!b.second)
{
return true;
}
return a->camDist() < b->camDist();
return a.second->camDist() < b.second->camDist();
});
// only draw the sky in 3D
@@ -1247,13 +1261,18 @@ void World::draw ( math::matrix_4x4 const& model_view
gl.bindVertexArray(_mapchunk_vao);
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, _mapchunk_index);
for (MapTile* tile : _loaded_tiles_buffer)
for (auto& pair : _loaded_tiles_buffer)
{
MapTile* tile = pair.second;
if (!tile)
{
break;
}
if (minimap_render)
tile->tile_occluded = false;
if (tile->tile_occluded && !tile->getChunkUpdateFlags() && !tile->tile_occlusion_cull_override)
continue;
@@ -1319,13 +1338,18 @@ void World::draw ( math::matrix_4x4 const& model_view
frame++;
}
for (MapTile* tile : _loaded_tiles_buffer)
for (auto& pair : _loaded_tiles_buffer)
{
MapTile* tile = pair.second;
if (!tile)
{
break;
}
if (minimap_render)
tile->tile_occluded = false;
if (tile->tile_occluded && !tile->getChunkUpdateFlags() && !tile->tile_occlusion_cull_override)
continue;
@@ -1454,8 +1478,10 @@ void World::draw ( math::matrix_4x4 const& model_view
gl.disable(GL_CULL_FACE); // TODO: figure out why indices are bad and we need this
math::matrix_4x4 identity_mtx = math::matrix_4x4{math::matrix_4x4::unit};
for (MapTile* tile : _loaded_tiles_buffer)
for (auto& pair : _loaded_tiles_buffer)
{
MapTile* tile = pair.second;
if (!tile)
{
break;
@@ -1475,8 +1501,10 @@ void World::draw ( math::matrix_4x4 const& model_view
if (draw_occlusion_boxes)
{
for (MapTile* tile : _loaded_tiles_buffer)
for (auto& pair : _loaded_tiles_buffer)
{
MapTile* tile = pair.second;
if (!tile)
{
break;
@@ -1709,8 +1737,10 @@ void World::draw ( math::matrix_4x4 const& model_view
water_shader.uniform ("use_transform", 0);
for (MapTile* tile : _loaded_tiles_buffer)
for (auto& pair : _loaded_tiles_buffer)
{
MapTile* tile = pair.second;
if (!tile)
break;
@@ -1826,12 +1856,21 @@ selection_result World::intersect ( math::matrix_4x4 const& model_view
{
ZoneScopedN("World::intersect() : intersect terrain");
for (auto tile : _loaded_tiles_buffer)
for (auto& pair : _loaded_tiles_buffer)
{
MapTile* tile = pair.second;
if (!tile)
break;
if (!tile->finishedLoading()) // TODO: crash
tile_index index{ static_cast<std::size_t>(pair.first.first)
, static_cast<std::size_t>(pair.first.second) };
// handle tiles that got unloaded mid-frame to avoid illegal access
if (!mapIndex.tileLoaded(index) || mapIndex.tileAwaitingLoading(index))
continue;
if (!tile->finishedLoading())
continue;
if (tile->intersect(ray, &results))
@@ -2332,12 +2371,26 @@ void World::drawMinimap ( MapTile *tile
tile_index m_tile = tile_index(camera_pos);
m_tile.z -= 1;
bool unload = !mapIndex.has_unsaved_changes(m_tile);
MapTile* mTile = mapIndex.loadTile(m_tile);
if (mTile)
{
mTile->wait_until_loaded();
mTile->waitForChildrenLoaded();
}
draw(model_view, projection, math::vector_3d{}, 0, math::vector_4d{},
CursorType::NONE, 0.f, false, 0.f, math::vector_3d{}, 0.f, 0.f, false, false, false, editing_mode::minimap, camera_pos, true, false, true, settings->draw_wmo, settings->draw_water, false, settings->draw_m2, false, false, true, settings, false, eTerrainType::eTerrainType_Linear, 0, display_mode::in_3D, false, true);
if (unload)
{
mapIndex.unloadTile(m_tile);
}
/*
if (!_display_initialized)
@@ -2673,12 +2726,12 @@ void World::drawMinimap ( MapTile *tile
*/
}
bool World::saveMinimap(tile_index const& tile_idx, MinimapRenderSettings* settings)
bool World::saveMinimap(tile_index const& tile_idx, MinimapRenderSettings* settings, std::optional<QImage>& combined_image)
{
ZoneScoped;
// Setup framebuffer
QOpenGLFramebufferObjectFormat fmt;
fmt.setSamples(1);
fmt.setSamples(0);
fmt.setInternalTextureFormat(GL_RGBA8);
fmt.setAttachment(QOpenGLFramebufferObject::Depth);
@@ -2691,19 +2744,32 @@ bool World::saveMinimap(tile_index const& tile_idx, MinimapRenderSettings* setti
// Load tile
bool unload = !mapIndex.has_unsaved_changes(tile_idx);
MapTile* mTile = mapIndex.loadTile(tile_idx);
if (!mapIndex.tileLoaded(tile_idx) && !mapIndex.tileAwaitingLoading(tile_idx))
{
MapTile* tile = mapIndex.loadTile(tile_idx);
tile->wait_until_loaded();
wait_for_all_tile_updates();
tile->waitForChildrenLoaded();
}
MapTile* mTile = mapIndex.getTile(tile_idx);
if (mTile)
{
mTile->wait_until_loaded();
wait_for_all_tile_updates();
unsigned counter = 0;
constexpr unsigned TIMEOUT = 5000;
if (AsyncLoader::instance().is_loading())
while (AsyncLoader::instance().is_loading() || !mTile->finishedLoading())
{
//return false;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
counter++;
if (counter >= TIMEOUT)
break;
}
float max_height = getMaxTileHeight(tile_idx);
float max_height = std::max(getMaxTileHeight(tile_idx), 200.f);
// setup view matrices
auto projection = math::ortho(
@@ -2711,14 +2777,17 @@ bool World::saveMinimap(tile_index const& tile_idx, MinimapRenderSettings* setti
TILESIZE / 2.0f,
-TILESIZE / 2.0f,
TILESIZE / 2.0f,
5.f,
0.f,
100000.0f
);
auto look_at = math::look_at(math::vector_3d(TILESIZE * tile_idx.x + TILESIZE / 2.0f, max_height + 10.0f, TILESIZE * tile_idx.z + TILESIZE / 2.0f),
math::vector_3d(TILESIZE * tile_idx.x + TILESIZE / 2.0f, max_height + 9.0f, TILESIZE * tile_idx.z + TILESIZE / 2.0 - 0.005f),
math::vector_3d(TILESIZE * tile_idx.x + TILESIZE / 2.0f, max_height + 5.0f, TILESIZE * tile_idx.z + TILESIZE / 2.0 - 0.005f),
math::vector_3d(0.f,1.f, 0.f));
glFinish();
drawMinimap(mTile
, look_at.transposed()
, projection.transposed()
@@ -2729,10 +2798,13 @@ bool World::saveMinimap(tile_index const& tile_idx, MinimapRenderSettings* setti
// Clearing alpha from image
gl.colorMask(false, false, false, true);
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.colorMask(true, true, true, true);
assert(pixel_buffer.isValid() && pixel_buffer.isBound());
QImage image = pixel_buffer.toImage();
image = image.convertToFormat(QImage::Format_RGBA8888);
QSettings app_settings;
@@ -2776,24 +2848,9 @@ bool World::saveMinimap(tile_index const& tile_idx, MinimapRenderSettings* setti
}
// Write combined file
if (settings->combined_minimap)
if (settings->combined_minimap && combined_image.has_value())
{
QString image_path = QString(std::string(basename + "_combined_minimap.png").c_str());
QImage combined_image;
if (dir.exists(image_path))
{
combined_image = QImage(dir.filePath(image_path));
if (combined_image.width() != 8192 | combined_image.height() != 8192)
{
combined_image = QImage(8192, 8192, QImage::Format_RGBA8888);
}
}
else
{
combined_image = QImage(8192, 8192, QImage::Format_RGBA8888);
}
QImage& combined_image = combined_image;
QImage scaled_image = image.scaled(128, 128, Qt::KeepAspectRatio);
@@ -2805,22 +2862,18 @@ bool World::saveMinimap(tile_index const& tile_idx, MinimapRenderSettings* setti
}
}
combined_image.save(dir.filePath(image_path));
}
// Register in md5translate.trs
std::string map_name = gMapDB.getMapName(mapIndex._map_id);
std::string tilename_left = (boost::format("%s\\map_%d_%02d.blp") % map_name % tile_idx.x % tile_idx.z).str();
std::string map_name = gMapDB.getByID(mapIndex._map_id).getString(MapDB::InternalName);
std::string tilename_left = (boost::format("%s\\map%02d_%02d.blp") % map_name % tile_idx.x % tile_idx.z).str();
mapIndex._minimap_md5translate[map_name][tilename_left] = tex_name;
/*
if (unload)
{
mapIndex.unloadTile(tile_idx);
}
*/
}
pixel_buffer.release();
@@ -3944,7 +3997,7 @@ void World::range_add_to_selection(math::vector_3d const& pos, float radius, boo
}
}
}
});
}
@@ -3953,6 +4006,7 @@ float World::getMaxTileHeight(const tile_index& tile)
ZoneScoped;
MapTile* m_tile = mapIndex.getTile(tile);
m_tile->forceRecalcExtents();
float max_height = m_tile->getMaxHeight();
std::vector<uint32_t>* uids = m_tile->get_uids();
@@ -3964,6 +4018,7 @@ float World::getMaxTileHeight(const tile_index& tile)
if (instance.get().which() == eEntry_Object)
{
auto obj = boost::get<selected_object_type>(instance.get());
obj->ensureExtents();
max_height = std::max(max_height, std::max(obj->extents[0].y, obj->extents[1].y));
}
}

View File

@@ -332,7 +332,7 @@ public:
void updateTilesModel(ModelInstance* m2, model_update type);
void wait_for_all_tile_updates();
bool saveMinimap (tile_index const& tile_idx, MinimapRenderSettings* settings);
bool saveMinimap (tile_index const& tile_idx, MinimapRenderSettings* settings, std::optional<QImage>& combined_image);
void drawMinimap ( MapTile *tile
, math::matrix_4x4 const& model_view
, math::matrix_4x4 const& projection
@@ -491,7 +491,7 @@ private:
LiquidTextureManager _liquid_texture_manager;
std::array<MapTile*, 64 * 64> _loaded_tiles_buffer;
std::array<std::pair<std::pair<int, int>, MapTile*>, 64 * 64 > _loaded_tiles_buffer;
bool _need_terrain_params_ubo_update = false;

View File

@@ -382,6 +382,7 @@ int main(int argc, char *argv[])
QApplication::setStyle(QStyleFactory::create("Fusion"));
//QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication qapp (argc, argv);
qapp.setApplicationName ("Noggit");
qapp.setOrganizationName ("Noggit");

View File

@@ -1163,6 +1163,25 @@ void MapIndex::removeTile(const tile_index &tile)
changed = true;
}
unsigned MapIndex::getNumExistingTiles()
{
if (_n_existing_tiles >= 0)
return _n_existing_tiles;
_n_existing_tiles = 0;
for (int i = 0; i < 4096; ++i)
{
tile_index index(i / 64, i % 64);
if (hasTile(index))
{
_n_existing_tiles++;
}
}
return _n_existing_tiles;
}
void MapIndex::set_basename(const std::string &pBasename)
{
basename = pBasename;

View File

@@ -228,6 +228,8 @@ public:
void addTile(const tile_index& tile);
void removeTile(const tile_index& tile);
unsigned getNumExistingTiles();
// todo: find out how wow choose to use the green lava in outland
inline bool use_mclq_green_lava() const
{
@@ -260,6 +262,7 @@ private:
int _unload_interval;
int _unload_dist;
unsigned _n_loaded_tiles = 0; // to be loaded, not necessarily already loaded
int _n_existing_tiles = -1;
// Is the WDT telling us to use a different alphamap structure.
bool mBigAlpha;

View File

@@ -20,7 +20,6 @@
#include <QWheelEvent>
#include <QApplication>
#include <QComboBox>
#include <QProgressBar>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
@@ -130,10 +129,6 @@ namespace noggit
draw_elements_box_layout->addWidget(combined_minimap, 3, 1);
_progress_bar = new QProgressBar(this);
_progress_bar->setRange(0, 4096);
generate_layout->addRow(_progress_bar);
// Filter
auto filter_widget = new QWidget(this);
filter_widget->setContentsMargins(0, 0, 0, 0);
@@ -351,22 +346,7 @@ namespace noggit
// Minimap
auto scroll_minimap = new QScrollArea(this);
scroll_minimap->setFixedSize(226, 226);
_minimap_widget = new minimap_widget(this);
layout->addWidget(scroll_minimap);
scroll_minimap->setAlignment(Qt::AlignCenter);
scroll_minimap->setWidget(_minimap_widget);
scroll_minimap->setWidgetResizable(true);
_minimap_widget->world(world);
_minimap_widget->draw_boundaries(true);
_minimap_widget->use_selection(&_render_settings.selected_tiles);
_minimap_widget->camera(mapView->getCamera());
scroll_minimap->ensureWidgetVisible(_minimap_widget, 0, 0);
_minimap_widget = mapView->getMinimapWidget();
loadFiltersFromJSON();
@@ -859,51 +839,6 @@ namespace noggit
mapView->initMinimapSave();
});
// Selection
QObject::connect
( _minimap_widget, &minimap_widget::tile_clicked
, [this, world] (QPoint tile)
{
if (QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier))
{
int x = tile.x() - 1;
int y = tile.y() - 1;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
if (world->mapIndex.hasTile(tile_index(x + i, y + j)))
{
_render_settings.selected_tiles[64 * (x + i) + (y + j)]
= !QApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
}
}
}
}
else
{
if (world->mapIndex.hasTile(tile_index(tile.x(), tile.y())))
{
_render_settings.selected_tiles[64 * tile.x() + tile.y()]
= !QApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
}
}
update();
}
);
QObject::connect
( _minimap_widget, &minimap_widget::reset_selection
, [this, world] ()
{
_render_settings.selected_tiles.fill(false);
}
);
}
void MinimapCreator::changeRadius(float change)
@@ -916,31 +851,6 @@ namespace noggit
return QSize(width(), height());
}
void MinimapCreator::wheelEvent(QWheelEvent* event)
{
if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier))
{
const int degrees = event->angleDelta().y() / 8;
int steps = degrees / 15;
auto base_size = _minimap_widget->width();
if (steps > 0)
{
auto new_size = std::max(512, base_size + 64);
_minimap_widget->setFixedSize(new_size, new_size);
}
else
{
auto new_size = std::min(4096, base_size - 64);
_minimap_widget->setFixedSize(new_size, new_size);
}
event->ignore();
}
}
void MinimapCreator::includeM2Model(std::string filename, float size_cat)
{

View File

@@ -7,9 +7,9 @@
#include <QSlider>
#include <QDoubleSpinBox>
#include <QSpinBox>
#include <QProgressBar>
#include <QLineEdit>
#include <QListWidget>
#include <QApplication>
#include <qt-color-widgets/color_selector.hpp>
#include <boost/optional.hpp>
@@ -89,9 +89,6 @@ namespace noggit
MinimapRenderSettings* getMinimapRenderSettings() { return &_render_settings; };
QSize sizeHint() const override;
void wheelEvent(QWheelEvent* event) override;
void progressUpdate(int value) { _progress_bar->setValue(value); };
void includeM2Model(std::string filename, float size_cat = 0.0f);
void unincludeM2Model(std::string filename);
@@ -112,7 +109,6 @@ namespace noggit
QSlider* _radius_slider;
QDoubleSpinBox* _radius_spin;
minimap_widget* _minimap_widget;
QProgressBar* _progress_bar;
QListWidget* _m2_model_filter_include;
QListWidget* _m2_instance_filter_include;
QListWidget* _wmo_model_filter_exclude;

View File

@@ -23,16 +23,96 @@ namespace noggit
, _world (nullptr)
, _camera (nullptr)
, _draw_skies (false)
, _selected_tiles(nullptr)
, _resizeable(false)
{
setSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred);
//setMouseTracking(true);
setMouseTracking(true);
setMaximumSize(QSize(1024, 1024));
setMinimumSize(QSize(128, 128));
connect(this, &minimap_widget::tile_clicked
, [this](QPoint tile)
{
if (!_selected_tiles)
return;
if (QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier))
{
int x = tile.x() - 1;
int y = tile.y() - 1;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 3; ++j)
{
if (_world->mapIndex.hasTile(tile_index(x + i, y + j)))
{
(*_selected_tiles)[64 * (x + i) + (y + j)]
= !QApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
}
}
}
}
else
{
if (_world->mapIndex.hasTile(tile_index(tile.x(), tile.y())))
{
(*_selected_tiles)[64 * tile.x() + tile.y()]
= !QApplication::keyboardModifiers().testFlag(Qt::ControlModifier);
}
}
update();
}
);
connect(this, &minimap_widget::reset_selection
, [this]()
{
if (!_selected_tiles)
return;
_selected_tiles->fill(false);
}
);
}
void minimap_widget::wheelEvent(QWheelEvent* event)
{
if (!_resizeable)
return;
if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier))
{
const int degrees = event->angleDelta().y() / 8;
int steps = degrees / 15;
auto base_size = width();
if (steps > 0)
{
auto new_size = std::min(std::max(128, base_size + 64), 4096);
setFixedSize(new_size, new_size);
}
else
{
auto new_size = std::max(std::min(4096, base_size - 64), 128);
setFixedSize(new_size, new_size);
}
event->ignore();
}
}
QSize minimap_widget::sizeHint() const
{
return QSize (1024, 1024);
return QSize (512, 512);
}
//! \todo Only redraw stuff as told in event.
@@ -233,7 +313,7 @@ namespace noggit
void minimap_widget::mouseMoveEvent(QMouseEvent* event)
{
if (world())
if (_world)
{
QPoint tile = locateTile(event);

View File

@@ -41,10 +41,11 @@ namespace noggit
inline const bool& draw_boundaries() const { return _draw_boundaries; }
inline const std::array<bool, 4096>* use_selection (std::array<bool, 4096>* selection_)
{ _use_selection = true; _selected_tiles = selection_; update(); return _selected_tiles; }
{ _use_selection = selection_; _selected_tiles = selection_; update(); return _selected_tiles; }
inline const std::array<bool, 4096>* selection() const { return _selected_tiles; }
inline void camera (noggit::camera* camera) { _camera = camera; }
void set_resizeable(bool state) { _resizeable = state; };
protected:
virtual void paintEvent (QPaintEvent*) override;
@@ -52,6 +53,7 @@ namespace noggit
virtual void mouseMoveEvent(QMouseEvent*) override;
virtual void mousePressEvent(QMouseEvent* event) override;
virtual void mouseReleaseEvent(QMouseEvent* event) override;
virtual void wheelEvent(QWheelEvent* event) override;
QPoint locateTile(QMouseEvent* event);
@@ -68,6 +70,7 @@ namespace noggit
bool _draw_skies;
bool _draw_camera;
bool _draw_boundaries;
bool _resizeable;
bool _use_selection = false;
bool _is_selecting = false;