Merge branch 'sql_update' into 'noggit-shadowlands'

Update

See merge request prophecy-rp/noggit-red!44
This commit is contained in:
Kaev
2023-07-22 07:10:37 +00:00
44 changed files with 732 additions and 149 deletions

View File

@@ -81,8 +81,10 @@ namespace mysql
{
prompt.setIcon(QMessageBox::Warning);
prompt.setText("Failed to load MySQL database, check your settings.");
prompt.setText("Failed to load MySQL database, check your settings. \nIf you did not intend to use this feature, disable it in Noggit->settings->MySQL");
prompt.setWindowTitle("Noggit Database Error");
// disable if connection is not valid
// settings.value("project/mysql/enabled") = false;
std::stringstream promptText;
promptText << "\n# ERR: " << e.what();

View File

@@ -70,7 +70,7 @@ public:
MapChunk* getChunk() { return _chunk; };
TileWater* getWaterTile() { return _water_tile; };
std::optional<MH2O_Render> Render;
float xbase, zbase;
private:
@@ -84,9 +84,11 @@ private:
void copy_height_to_layer(liquid_layer& target, glm::vec3 const& pos, float radius);
std::optional<MH2O_Render> Render;
std::vector<liquid_layer> _layers;
MapChunk* _chunk;
TileWater* _water_tile;
friend class MapView;
};

View File

@@ -24,8 +24,8 @@
#include <QImage>
#include <limits>
MapChunk::MapChunk(MapTile* maintile, BlizzardArchive::ClientFile* f, bool bigAlpha,
tile_mode mode, Noggit::NoggitRenderContext context, bool init_empty, int chunk_idx)
MapChunk::MapChunk(MapTile* maintile, BlizzardArchive::ClientFile* f, bool bigAlpha,tile_mode mode
, Noggit::NoggitRenderContext context, bool init_empty, int chunk_idx, bool load_textures)
: _mode(mode)
, mt(maintile)
, use_big_alphamap(bigAlpha)
@@ -148,6 +148,11 @@ MapChunk::MapChunk(MapTile* maintile, BlizzardArchive::ClientFile* f, bool bigAl
xbase = xbase*-1.0f + ZEROPOINT;
}
if (!load_textures)
{
this->header.nLayers = 0;
}
texture_set = std::make_unique<TextureSet>(this, f, base, maintile, bigAlpha,
!!header_flags.flags.do_not_fix_alpha_map, mode == tile_mode::uid_fix_all, _context);

View File

@@ -64,7 +64,7 @@ private:
public:
MapChunk(MapTile* mt, BlizzardArchive::ClientFile* f, bool bigAlpha, tile_mode mode, Noggit::NoggitRenderContext context
, bool init_empty = false, int chunk_idx = 0);
, bool init_empty = false, int chunk_idx = 0, bool load_textures = true);
auto getHoleMask(void) const -> unsigned { return static_cast<unsigned>(holes); }
MapTile *mt;

View File

@@ -41,6 +41,7 @@ MapTile::MapTile( int pX
, World* world
, Noggit::NoggitRenderContext context
, tile_mode mode
, bool pLoadTextures
)
: AsyncObject(pFilename)
, _renderer(this)
@@ -54,6 +55,7 @@ MapTile::MapTile( int pX
, _tile_is_being_reloaded(reloading_tile)
, mBigAlpha(pBigAlpha)
, _load_models(pLoadModels)
, _load_textures(pLoadTextures)
, _world(world)
, _context(context)
, _chunk_update_flags(ChunkUpdateFlags::VERTEX | ChunkUpdateFlags::ALPHAMAP
@@ -161,23 +163,25 @@ void MapTile::finishLoading()
// - MTEX ----------------------------------------------
theFile.seek(Header.mtex + 0x14);
theFile.read(&fourcc, 4);
theFile.read(&size, 4);
assert(fourcc == 'MTEX');
if (_load_textures)
{
char const* lCurPos = reinterpret_cast<char const*>(theFile.getPointer());
char const* lEnd = lCurPos + size;
theFile.seek(Header.mtex + 0x14);
theFile.read(&fourcc, 4);
theFile.read(&size, 4);
assert(fourcc == 'MTEX');
while (lCurPos < lEnd)
{
mTextureFilenames.push_back(BlizzardArchive::ClientData::normalizeFilenameInternal(std::string(lCurPos)));
lCurPos += strlen(lCurPos) + 1;
char const* lCurPos = reinterpret_cast<char const*>(theFile.getPointer());
char const* lEnd = lCurPos + size;
while (lCurPos < lEnd)
{
mTextureFilenames.push_back(BlizzardArchive::ClientData::normalizeFilenameInternal(std::string(lCurPos)));
lCurPos += strlen(lCurPos) + 1;
}
}
}
if (_load_models)
{
// - MMDX ----------------------------------------------
@@ -359,7 +363,7 @@ void MapTile::finishLoading()
unsigned x = nextChunk / 16;
unsigned z = nextChunk % 16;
mChunks[x][z] = std::make_unique<MapChunk> (this, &theFile, mBigAlpha, _mode, _context);
mChunks[x][z] = std::make_unique<MapChunk> (this, &theFile, mBigAlpha, _mode, _context, false, 0, _load_textures);
auto& chunk = mChunks[x][z];
_renderer.initChunkData(chunk.get());

View File

@@ -55,6 +55,7 @@ public:
, World*
, Noggit::NoggitRenderContext context
, tile_mode mode = tile_mode::edit
, bool pLoadTextures = true
);
~MapTile();
@@ -205,6 +206,7 @@ private:
std::array<float, 145 * 256 * 4> _chunk_heightmap_buffer;
bool _load_models;
bool _load_textures;
World* _world;

View File

@@ -232,16 +232,31 @@ void MapView::set_editing_mode(editing_mode mode)
_viewport_overlay_ui->gizmoBar->hide();
}
auto previous_mode = _left_sec_toolbar->getCurrentMode();
_left_sec_toolbar->setCurrentMode(this, mode);
if (context() && context()->isValid())
{
if (mode == editing_mode::holes && previous_mode != editing_mode::holes)
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_lines = true;
_world->renderer()->getTerrainParamsUniformBlock()->draw_hole_lines = true;
}
else if (previous_mode == editing_mode::holes && mode != editing_mode::holes)
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_lines = _draw_lines.get();
_world->renderer()->getTerrainParamsUniformBlock()->draw_hole_lines = _draw_hole_lines.get();
}
_world->renderer()->getTerrainParamsUniformBlock()->draw_areaid_overlay = false;
_world->renderer()->getTerrainParamsUniformBlock()->draw_impass_overlay = false;
_world->renderer()->getTerrainParamsUniformBlock()->draw_paintability_overlay = false;
_world->renderer()->getTerrainParamsUniformBlock()->draw_selection_overlay = false;
_minimap->use_selection(nullptr);
bool use_classic_ui = _settings->value("classicUI", true).toBool();
switch (mode)
{
case editing_mode::ground:
@@ -255,9 +270,20 @@ void MapView::set_editing_mode(editing_mode mode)
{
texturingTool->updateMaskImage();
}
if (_left_sec_toolbar->showUnpaintableChunk())
if (use_classic_ui)
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_paintability_overlay = true;
if (texturingTool->show_unpaintable_chunks())
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_paintability_overlay = true;
}
}
else
{
if (_left_sec_toolbar->showUnpaintableChunk())
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_paintability_overlay = true;
}
}
break;
case editing_mode::mccv:
@@ -1877,62 +1903,63 @@ void MapView::setupViewMenu()
view_menu->addAction(createTextSeparator("Drawing"));
view_menu->addSeparator();
ADD_TOGGLE (view_menu, "Doodads", Qt::Key_F1, _draw_models);
ADD_TOGGLE (view_menu, "WMOs", Qt::Key_F2, _draw_wmo);
ADD_TOGGLE (view_menu, "WMO doodads", Qt::Key_F3, _draw_wmo_doodads);
ADD_TOGGLE (view_menu, "Terrain", Qt::Key_F4, _draw_terrain);
ADD_TOGGLE (view_menu, "Water", Qt::Key_F5, _draw_water);
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_POST (view_menu, "Lines", Qt::SHIFT | Qt::Key_F1, _draw_lines,
ADD_TOGGLE_POST (view_menu, "Lines", Qt::Key_F7, _draw_lines,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_lines = _draw_lines.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
ADD_TOGGLE_POST (view_menu, "Hole lines", Qt::SHIFT | Qt::Key_F2, _draw_hole_lines,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_hole_lines = _draw_hole_lines.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
ADD_TOGGLE_POST (view_menu, "Wireframe", Qt::SHIFT | Qt::Key_F3, _draw_wireframe,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_wireframe = _draw_wireframe.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
ADD_TOGGLE_POST (view_menu, "Contours", Qt::SHIFT | Qt::Key_F4, _draw_contour,
ADD_TOGGLE_POST (view_menu, "Contours", Qt::Key_F9, _draw_contour,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_terrain_height_contour = _draw_contour.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
ADD_TOGGLE_POST(view_menu, "Climb", Qt::SHIFT | Qt::Key_F5, _draw_climb,
ADD_TOGGLE_POST (view_menu, "Wireframe", Qt::Key_F10, _draw_wireframe,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_wireframe = _draw_wireframe.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
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_POST (view_menu, "Hole lines", Qt::SHIFT | Qt::Key_F1, _draw_hole_lines,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_hole_lines = _draw_hole_lines.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
ADD_TOGGLE_POST(view_menu, "Climb", Qt::SHIFT | Qt::Key_F2, _draw_climb,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_impassible_climb = _draw_climb.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
ADD_TOGGLE_POST(view_menu, "Vertex Color", Qt::SHIFT | Qt::Key_F6, _draw_vertex_color,
ADD_TOGGLE_POST(view_menu, "Vertex Color", Qt::SHIFT | Qt::Key_F3, _draw_vertex_color,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_vertex_color = _draw_vertex_color.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
ADD_TOGGLE_POST(view_menu, "Baked Shadows", Qt::SHIFT | Qt::Key_F7, _draw_baked_shadows,
ADD_TOGGLE_POST(view_menu, "Baked Shadows", Qt::SHIFT | Qt::Key_F4, _draw_baked_shadows,
[=]
{
_world->renderer()->getTerrainParamsUniformBlock()->draw_shadows = _draw_baked_shadows.get();
_world->renderer()->markTerrainParamsUniformBlockDirty();
});
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_NS (view_menu, "Models with box", _draw_models_with_box);
@@ -2042,7 +2069,6 @@ void MapView::setupViewMenu()
{
_camera.add_to_yaw(math::degrees(180.f));
_camera_moved_since_last_draw = true;
_minimap->update();
}
);
@@ -2265,6 +2291,7 @@ void MapView::setupHotkeys()
, [&]
{
_left_sec_toolbar->nextFlattenMode(this);
flattenTool->nextFlattenMode();
}
, [&] { return terrainMode == editing_mode::flatten_blur && !NOGGIT_CUR_ACTION; }
);
@@ -2796,7 +2823,11 @@ void MapView::move_camera_with_auto_height (glm::vec3 const& pos)
makeCurrent();
OpenGL::context::scoped_setter const _ (::gl, context());
_world->mapIndex.loadTile(pos)->wait_until_loaded();
TileIndex tile_index = TileIndex(pos);
if (_world->mapIndex.hasTile(tile_index))
{
_world->mapIndex.loadTile(pos)->wait_until_loaded();
}
_camera.position = pos;
_camera.position.y = 0.0f;
@@ -2813,7 +2844,6 @@ void MapView::move_camera_with_auto_height (glm::vec3 const& pos)
_camera.position.y += 50.0f;
_minimap->update();
_camera_moved_since_last_draw = true;
}
@@ -3968,20 +3998,22 @@ void MapView::tick (float dt)
if (moving)
{
_camera.move_forward(moving, dt);
_camera_moved_since_last_draw = true;
// TODO use normalized speed (doesn't slow down when looking up)
// _camera.move_forward_normalized(moving, dt);
}
if (strafing)
{
_camera.move_horizontal(strafing, dt);
_camera_moved_since_last_draw = true;
}
// get ground z position
// can be optimized by calling this only when moving/strafing or changing camera mode.
auto ground_pos = _world.get()->get_ground_height(_camera.position);
_camera.position.y = ground_pos.y + 2;
_camera_moved_since_last_draw = true;
// hack to update camera when entering mode in void ViewToolbar::add_tool_icon()
if (_camera_moved_since_last_draw)
{
auto ground_pos = _world.get()->get_ground_height(_camera.position);
_camera.position.y = ground_pos.y + 2;
}
}
else
{
@@ -4033,7 +4065,7 @@ void MapView::tick (float dt)
}
}
_minimap->update();
// _minimap->update(); // causes massive performance issues
_world->time += this->mTimespeed * dt;
_world->animtime += dt * 1000.0f;
@@ -4485,6 +4517,13 @@ void MapView::draw_map()
doSelection(true);
}
if (_camera_moved_since_last_draw)
{
_minimap->update();
}
bool classic_ui = _settings->value("classicUI", true).toBool();
bool show_unpaintable = classic_ui ? texturingTool->show_unpaintable_chunks() : _left_sec_toolbar->showUnpaintableChunk();
_world->renderer()->draw (
model_view()
, projection()
@@ -4493,7 +4532,7 @@ void MapView::draw_map()
, terrainMode == editing_mode::mccv ? shaderTool->shaderColor() : cursor_color
, _cursorType
, radius
, _left_sec_toolbar->showUnpaintableChunk()
, show_unpaintable
, _left_sec_toolbar->drawOnlyInsideSphereLight()
, _left_sec_toolbar->drawWireframeSphereLight()
, _left_sec_toolbar->getAlphaSphereLight()
@@ -4663,7 +4702,6 @@ void MapView::keyPressEvent (QKeyEvent *event)
{
_camera.position = glm::vec3(_cursor_pos.x, _cursor_pos.y + 50, _cursor_pos.z);
_camera_moved_since_last_draw = true;
_minimap->update();
}
if (event->key() == Qt::Key_L)
@@ -4680,28 +4718,24 @@ void MapView::keyPressEvent (QKeyEvent *event)
auto next_z = cur_tile.z - 1;
_camera.position = glm::vec3((cur_tile.x * TILESIZE) + (TILESIZE / 2), _camera.position.y, (next_z * TILESIZE) + (TILESIZE / 2));
_camera_moved_since_last_draw = true;
_minimap->update();
}
else if (event->key() == Qt::Key_Down)
{
auto next_z = cur_tile.z + 1;
_camera.position = glm::vec3((cur_tile.x * TILESIZE) + (TILESIZE / 2), _camera.position.y, (next_z * TILESIZE) + (TILESIZE / 2));
_camera_moved_since_last_draw = true;
_minimap->update();
}
else if (event->key() == Qt::Key_Left)
{
auto next_x = cur_tile.x - 1;
_camera.position = glm::vec3((next_x * TILESIZE) + (TILESIZE / 2), _camera.position.y, (cur_tile.z * TILESIZE) + (TILESIZE / 2));
_camera_moved_since_last_draw = true;
_minimap->update();
}
else if (event->key() == Qt::Key_Right)
{
auto next_x = cur_tile.x + 1;
_camera.position = glm::vec3((next_x * TILESIZE) + (TILESIZE / 2), _camera.position.y, (cur_tile.z * TILESIZE) + (TILESIZE / 2));
_camera_moved_since_last_draw = true;
_minimap->update();
}
}
@@ -4849,7 +4883,6 @@ void MapView::mouseMoveEvent (QMouseEvent* event)
_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)

View File

@@ -280,6 +280,7 @@ public:
void randomizeStampRotation();
void onSettingsSave();
void updateRotationEditor() { _rotation_editor_need_update = true; };
void setCameraDirty() { _camera_moved_since_last_draw = true; };
[[nodiscard]]
Noggit::Ui::minimap_widget* getMinimapWidget() const { return _minimap; }

View File

@@ -3,6 +3,7 @@
#include <glm/gtx/quaternion.hpp>
#include <math/bounding_box.hpp>
#include <math/frustum.hpp>
#include <glm/glm.hpp>
#include <noggit/Log.h>
#include <noggit/Misc.h> // checkinside
#include <noggit/Model.h> // Model, etc.
@@ -237,6 +238,10 @@ void ModelInstance::updateDetails(Noggit::Ui::detail_infos* detail_widget)
<< "<br><b>position X/Y/Z:</b> {" << pos.x << " , " << pos.y << " , " << pos.z << "}"
<< "<br><b>rotation X/Y/Z:</b> {" << dir.x << " , " << dir.y << " , " << dir.z << "}"
<< "<br><b>scale:</b> " << scale
<< "<br><b>server position X/Y/Z: </b>{" << (ZEROPOINT - pos.z) << ", " << (ZEROPOINT - pos.x) << ", " << pos.y << "}"
<< "<br><b>server orientation: </b>" << fabs(2 * glm::pi<float>() - glm::pi<float>() / 180.0 * (float(dir.y) < 0 ? fabs(float(dir.y)) + 180.0 : fabs(float(dir.y) - 180.0)))
<< "<br><b>textures Used:</b> " << model->header.nTextures
<< "<br><b>size category:</b><span> " << size_cat;

View File

@@ -24,6 +24,31 @@ void selected_chunk_type::updateDetails(Noggit::Ui::detail_infos* detail_widget)
<< "<br><b>textures used:</b> " << chunk->texture_set->num()
<< "<br><b>textures:</b><span>";
// liquid details if the chunk has liquid data
if (chunk->mt->Water.hasData(0))
{
ChunkWater* waterchunk = chunk->liquid_chunk();
MH2O_Render liquid_render = waterchunk->Render.value_or(MH2O_Render{ 0xffffffffffffffff,0xffffffffffffffff });
if (waterchunk->hasData(0))
{
liquid_layer liquid = waterchunk->getLayers()->at(0); // only getting data from layer 0, maybe loop them ?
int liquid_flags = liquid.getSubchunks();
select_info << "\nliquid type: " << liquid.liquidID() << " (\"" << gLiquidTypeDB.getLiquidName(liquid.liquidID()) << "\")"
<< "\nliquid flags: "
// getting flags from the center tile
<< ((liquid_render.fishable >> (4 * 8 + 4)) & 1 ? "fishable " : "")
<< ((liquid_render.fatigue >> (4 * 8 + 4)) & 1 ? "fatigue" : "");
}
}
else
{
select_info << "\nno liquid data";
}
unsigned counter = 0;
for (auto& tex : *(chunk->texture_set->getTextures()))
{

View File

@@ -173,6 +173,10 @@ void WMOInstance::updateDetails(Noggit::Ui::detail_infos* detail_widget)
<< "<br><b>WMO Id: </b>" << wmo->WmoId
<< "<br><b>doodad set: </b>" << doodadset()
<< "<br><b>name set: </b>" << mNameset
<< "<br><b>server position X/Y/Z: </b>{" << (ZEROPOINT - pos.z) << ", " << (ZEROPOINT - pos.x) << ", " << pos.y << "}"
<< "<br><b>server orientation: </b>" << fabs(2 * glm::pi<float>() - glm::pi<float>() / 180.0 * (float(dir.y) < 0 ? fabs(float(dir.y)) + 180.0 : fabs(float(dir.y) - 180.0)))
<< "<br><b>textures used: </b>" << wmo->textures.size()
<< "<span>";

View File

@@ -64,7 +64,7 @@ bool World::IsEditableWorld(BlizzardDatabaseLib::Structures::BlizzardDatabaseRow
// Not using the libWDT here doubles performance. You might want to look at your lib again and improve it.
const int lFlags = *(reinterpret_cast<const int*>(lPointer + 8 + 4 + 8));
if (lFlags & 1)
return false;
return true; // filter them later
const int * lData = reinterpret_cast<const int*>(lPointer + 8 + 4 + 8 + 0x20 + 8);
for (int i = 0; i < 8192; i += 2)
@@ -76,14 +76,33 @@ bool World::IsEditableWorld(BlizzardDatabaseLib::Structures::BlizzardDatabaseRow
return false;
}
bool World::IsWMOWorld(BlizzardDatabaseLib::Structures::BlizzardDatabaseRow& record)
{
ZoneScoped;
std::string lMapName = record.Columns["Directory"].Value;
std::stringstream ssfilename;
ssfilename << "World\\Maps\\" << lMapName << "\\" << lMapName << ".wdt";
BlizzardArchive::ClientFile mf(ssfilename.str(), Noggit::Application::NoggitApplication::instance()->clientData());
const char* lPointer = reinterpret_cast<const char*>(mf.getPointer());
const int lFlags = *(reinterpret_cast<const int*>(lPointer + 8 + 4 + 8));
if (lFlags & 1)
return true;
return false;
}
World::World(const std::string& name, int map_id, Noggit::NoggitRenderContext context, bool create_empty)
: _renderer(Noggit::Rendering::WorldRender(this))
, _model_instance_storage(this)
, _tile_update_queue(this)
, mapIndex(name, map_id, this, context, create_empty)
, horizon(name, &mapIndex)
, mWmoFilename("")
, mWmoEntry(ENTRY_MODF())
, mWmoFilename(mapIndex.globalWMOName)
, mWmoEntry(mapIndex.wmoEntry)
, animtime(0)
, time(1450)
, basename(name)
@@ -357,10 +376,11 @@ void World::rotate_selected_models_to_ground_normal(bool smoothNormals)
}
});
// We shouldn't end up with empty ever.
// !\ todo We shouldn't end up with empty ever (but we do, on completely flat ground)
if (results.empty())
{
LogError << "rotate_selected_models_to_ground_normal ray intersection failed" << std::endl;
// just to avoid models disappearing when this happens
updateTilesEntry(entry, model_update::add);
continue;
}
@@ -836,15 +856,14 @@ MapChunk* World::getChunkAt(glm::vec3 const& pos)
return nullptr;
}
bool World::isInIndoorWmoGroup(std::array<glm::vec3, 2> obj_bounds)
bool World::isInIndoorWmoGroup(std::array<glm::vec3, 2> obj_bounds, glm::mat4x4 obj_transform)
{
bool is_indoor = false;
// check if model bounds is within wmo bounds then check each indor wmo group bounds
_model_instance_storage.for_each_wmo_instance([&](WMOInstance& wmo_instance)
{
auto wmo_extents = wmo_instance.getExtents();
// check if global wmo bounds intersect
if (obj_bounds[1].x >= wmo_extents[0].x
&& obj_bounds[1].y >= wmo_extents[0].y
&& obj_bounds[1].z >= wmo_extents[0].z
@@ -864,15 +883,23 @@ bool World::isInIndoorWmoGroup(std::array<glm::vec3, 2> obj_bounds)
auto& group_extents = wmo_instance.getGroupExtents().at(i);
// TODO : do a precise calculation instead of using axis aligned bounding boxes.
auto test = obj_bounds[1].x >= group_extents.first.x
bool aabb_test = obj_bounds[1].x >= group_extents.first.x
&& obj_bounds[1].y >= group_extents.first.y
&& obj_bounds[1].z >= group_extents.first.z
&& group_extents.second.x >= obj_bounds[0].x
&& group_extents.second.y >= obj_bounds[0].y
&& group_extents.second.z >= obj_bounds[0].z;
is_indoor = test;
return;
if (aabb_test) // oriented box check
{
/* TODO
if (collide_test)
{
is_indoor = true;
return;
}
*/
}
}
}
}
@@ -1797,7 +1824,10 @@ void World::remove_models_if_needed(std::vector<uint32_t> const& uids)
reset_selection();
}
update_models_by_filename();
if (uids.size())
{
update_models_by_filename();
}
}
void World::reload_tile(TileIndex const& tile)
@@ -2695,7 +2725,7 @@ void World::update_models_by_filename()
{
_models_by_filename[model_instance.model->file_key().filepath()].push_back(&model_instance);
// to make sure the transform matrix are up to date
model_instance.recalcExtents();
model_instance.ensureExtents();
});
need_model_updates = false;

View File

@@ -105,7 +105,7 @@ public:
MapChunk* getChunkAt(glm::vec3 const& pos);
bool isInIndoorWmoGroup(std::array<glm::vec3, 2> obj_bounds);
bool isInIndoorWmoGroup(std::array<glm::vec3, 2> obj_bounds, glm::mat4x4 obj_transform);
protected:
// Information about the currently selected model / WMO / triangle.
@@ -316,6 +316,8 @@ public:
static bool IsEditableWorld(BlizzardDatabaseLib::Structures::BlizzardDatabaseRow& record);
static bool IsWMOWorld(BlizzardDatabaseLib::Structures::BlizzardDatabaseRow& record);
void clearHeight(glm::vec3 const& pos);
void clearAllModelsOnADT(TileIndex const& tile);

View File

@@ -104,4 +104,6 @@ private:
private:
glm::vec3 pos;
ChunkWater* _chunk;
friend class MapView;
};

View File

@@ -217,6 +217,35 @@ map_horizon::map_horizon(const std::string& basename, const MapIndex * const ind
set_minimap(index);
}
void map_horizon::update_minimap_tile(int y, int x, bool has_data = false )
{
if (_tiles[y][x])
{
//! \todo There also is a second heightmap appended which has additional 16*16 pixels.
//! \todo There also is MAHO giving holes into this heightmap.
for (int j(0); j < 16; ++j)
{
for (int i(0); i < 16; ++i)
{
//! \todo R and B are inverted here
_qt_minimap.setPixel(x * 16 + i, y * 16 + j, color_for_height(_tiles[y][x]->height_17[j][i]));
}
}
}
// the adt exist but there's no data in the wdl
else if (has_data)
{
for (int j(0); j < 16; ++j)
{
for (int i(0); i < 16; ++i)
{
_qt_minimap.setPixel(x * 16 + i, y * 16 + j, color(200, 100, 25));
}
}
}
}
void map_horizon::set_minimap(const MapIndex* const index)
{
_qt_minimap = QImage(16 * 64, 16 * 64, QImage::Format_ARGB32);
@@ -226,31 +255,20 @@ void map_horizon::set_minimap(const MapIndex* const index)
{
for (int x(0); x < 64; ++x)
{
if (_tiles[y][x])
{
//! \todo There also is a second heightmap appended which has additional 16*16 pixels.
//! \todo There also is MAHO giving holes into this heightmap.
update_minimap_tile(y, x, index->hasTile(TileIndex(x, y)));
}
}
}
for (int j(0); j < 16; ++j)
{
for (int i(0); i < 16; ++i)
{
//! \todo R and B are inverted here
_qt_minimap.setPixel(x * 16 + i, y * 16 + j, color_for_height(_tiles[y][x]->height_17[j][i]));
}
}
}
// the adt exist but there's no data in the wdl
else if (index->hasTile(TileIndex(x, y)))
{
for (int j(0); j < 16; ++j)
{
for (int i(0); i < 16; ++i)
{
_qt_minimap.setPixel(x * 16 + i, y * 16 + j, color(200, 100, 25));
}
}
}
void map_horizon::remove_horizon_tile(int y, int x)
{
_tiles[y][x].reset();
for (int j(0); j < 16; ++j)
{
for (int i(0); i < 16; ++i)
{
_qt_minimap.setPixel(x * 16 + i, y * 16 + j, color(255, 25, 25));
}
}
}
@@ -278,6 +296,9 @@ int16_t map_horizon::getWdlheight(MapTile* tile, float x, float y)
// truncate and clamp the float value
auto chunk = tile->getChunk(cx, cy);
// float height = heights[cy * 16 + cx][17 * (row / 2) + (inner ? 9 : 0) + col];
if (!chunk)
return 0.0f;
float height = chunk->getHeightmap()[17 * (row / 2) + (inner ? 9 : 0) + col].y;
return std::min(std::max(static_cast<int16_t>(height), static_cast<int16_t>(SHRT_MIN)), static_cast<int16_t>(SHRT_MAX));
}
@@ -318,6 +339,8 @@ void map_horizon::update_horizon_tile(MapTile* mTile)
for (int j = 0; j < 16; ++j)
{
auto chunk = mTile->getChunk(j, i);
if (!chunk)
continue;
// the ordering seems to be : short array = Y axis, flags values = X axis and the values are for a whole chunk.
std::bitset<16> holeBits(chunk->getHoleMask());
@@ -327,6 +350,8 @@ void map_horizon::update_horizon_tile(MapTile* mTile)
}
_tiles[tile_index.z][tile_index.x].get()->holes[i] = static_cast<int16_t>(wdlHoleMask.to_ulong());
}
update_minimap_tile(tile_index.z, tile_index.x, true);
}
void map_horizon::save_wdl(World* world, bool regenerate)
@@ -405,7 +430,9 @@ void map_horizon::save_wdl(World* world, bool regenerate)
if (!horizon_tile || regenerate)
{
bool unload = !world->mapIndex.tileLoaded(index) && !world->mapIndex.tileAwaitingLoading(index);
MapTile* mTile = world->mapIndex.loadTile(index);
MapTile* mTile = world->mapIndex.loadTile(index, false, false, false);
auto nloadedtiles = world->mapIndex.getNLoadedTiles();
if (mTile)
mTile->wait_until_loaded();

View File

@@ -80,8 +80,12 @@ public:
map_horizon(const std::string& basename, const MapIndex * const index);
void update_minimap_tile(int y, int x, bool has_data);
void set_minimap(const MapIndex* const index);
void remove_horizon_tile(int y, int x);
Noggit::map_horizon_tile* get_horizon_tile(int y, int x);
QImage _qt_minimap;

View File

@@ -140,7 +140,7 @@ MapIndex::MapIndex (const std::string &pBasename, int map_id, World* world,
//! \bug MODF reads wrong. The assertion fails every time. Somehow, it keeps being MWMO. Or are there two blocks?
//! \nofuckingbug on eof read returns just without doing sth to the var and some wdts have a MWMO without having a MODF so only checking for eof above is not enough
mHasAGlobalWMO = false;
// mHasAGlobalWMO = false;
// - MWMO ----------------------------------------------
@@ -360,7 +360,7 @@ void MapIndex::setFlag(bool to, glm::vec3 const& pos, uint32_t flag)
}
}
MapTile* MapIndex::loadTile(const TileIndex& tile, bool reloading)
MapTile* MapIndex::loadTile(const TileIndex& tile, bool reloading, bool load_models, bool load_textures)
{
if (!hasTile(tile))
{
@@ -382,7 +382,7 @@ MapTile* MapIndex::loadTile(const TileIndex& tile, bool reloading)
}
mTiles[tile.z][tile.x].tile = std::make_unique<MapTile> (static_cast<int>(tile.x), static_cast<int>(tile.z), filename.str(),
mBigAlpha, true, use_mclq_green_lava(), reloading, _world, _context);
mBigAlpha, load_models, use_mclq_green_lava(), reloading, _world, _context, tile_mode::edit, load_textures);
MapTile* adt = mTiles[tile.z][tile.x].tile.get();
@@ -1162,6 +1162,8 @@ void MapIndex::addTile(const TileIndex& tile)
mTiles[tile.z][tile.x].flags |= 0x1;
mTiles[tile.z][tile.x].tile->changed = true;
_world->horizon.update_horizon_tile(mTiles[tile.z][tile.x].tile.get());
changed = true;
}
@@ -1177,6 +1179,8 @@ void MapIndex::removeTile(const TileIndex &tile)
mTiles[tile.z][tile.x].tile->changed = true;
mTiles[tile.z][tile.x].onDisc = false;
_world->horizon.remove_horizon_tile(tile.z, tile.x);
changed = true;
}

View File

@@ -194,7 +194,7 @@ public:
void create_empty_wdl();
void enterTile(const TileIndex& tile);
MapTile *loadTile(const TileIndex& tile, bool reloading = false);
MapTile *loadTile(const TileIndex& tile, bool reloading = false, bool load_models = true, bool load_textures = true);
void update_model_tile(const TileIndex& tile, model_update type, SceneObject* instance);
@@ -273,10 +273,9 @@ private:
public:
int const _map_id;
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> _minimap_md5translate;
private:
std::string globalWMOName;
ENTRY_MODF wmoEntry;
private:
int _last_unload_time;
int _unload_interval;
int _unload_dist;
@@ -295,7 +294,6 @@ private:
uint32_t highestGUID;
ENTRY_MODF wmoEntry;
MPHD mphd;
// Holding all MapTiles there can be in a World.

View File

@@ -229,7 +229,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
_cull_distance= draw_fog ? _skies->fog_distance_end() : _view_distance;
// Draw verylowres heightmap
if (draw_fog && draw_terrain)
if (!_world->mapIndex.hasAGlobalWMO() && draw_fog && draw_terrain)
{
ZoneScopedN("World::draw() : Draw horizon");
_horizon_render->draw (model_view, projection, &_world->mapIndex, _skies->color_set[FOG_COLOR], _cull_distance, frustum, camera_pos, display);
@@ -457,6 +457,19 @@ void WorldRender::draw (glm::mat4x4 const& model_view
wmo_program.uniform("camera", glm::vec3(camera_pos.x, camera_pos.y, camera_pos.z));
// make this check per WMO or global WMO with tiles may not work
bool disable_cull = false;
if (_world->mapIndex.hasAGlobalWMO() && !wmos_to_draw.size())
{
auto global_wmo = _world->_model_instance_storage.get_wmo_instance(_world->mWmoEntry.uniqueID);
if (global_wmo.has_value())
{
wmos_to_draw.push_back(global_wmo.value());
disable_cull = true;
}
}
for (auto& instance: wmos_to_draw)
{
@@ -519,7 +532,7 @@ void WorldRender::draw (glm::mat4x4 const& model_view
, _world->animtime
, _skies->hasSkies()
, display
, false
, disable_cull
, draw_wmo_exterior
);

View File

@@ -11,6 +11,7 @@
#include <noggit/MapHeaders.h>
#include <noggit/MapView.h>
#include <noggit/World.h>
#include <noggit/ChunkWater.hpp>
#include <noggit/ContextObject.hpp>
namespace Noggit
@@ -150,6 +151,73 @@ namespace Noggit
return vert(state(), _chunk, index);
}
bool chunk::has_render_flags()
{
return _chunk->liquid_chunk()->Render.has_value();
}
MH2O_Render chunk::getRenderOrDefault()
{
std::optional<MH2O_Render> render = _chunk->liquid_chunk()->Render;
if (render.has_value())
{
return render.value();
}
else
{
return { 0xFFFFFFFFFFFFFFFF,1 };
}
}
MH2O_Render& chunk::getOrCreateRender()
{
std::optional<MH2O_Render>& render = _chunk->liquid_chunk()->Render;
if (!render.has_value())
{
render.emplace();
}
return render.value();
}
void chunk::set_deep_flag(std::uint32_t low, std::uint32_t high)
{
getOrCreateRender().fatigue = std::uint64_t(low) | (std::uint64_t(high) << 32);
}
void chunk::set_deep_flag_1(std::uint32_t low)
{
set_deep_flag(low, 0);
}
std::uint32_t chunk::get_deep_flag()
{
return static_cast<std::uint32_t>(getRenderOrDefault().fatigue);
}
std::uint32_t chunk::get_deep_flag_high()
{
return static_cast<std::uint32_t>(getRenderOrDefault().fatigue >> 32);
}
void chunk::set_fishable_flag(std::uint32_t low, std::uint32_t high)
{
getOrCreateRender().fishable = std::uint64_t(low) | (std::uint64_t(high) << 32);
}
void chunk::set_fishable_flag_1(std::uint32_t low)
{
set_fishable_flag(low, 0);
}
std::uint32_t chunk::get_fishable_flag()
{
return static_cast<std::uint32_t>(getRenderOrDefault().fishable);
}
std::uint32_t chunk::get_fishable_flag_high()
{
return static_cast<std::uint32_t>(getRenderOrDefault().fishable >> 32);
}
std::shared_ptr<selection> chunk::to_selection()
{
return std::make_shared<selection>(state(), "chunk#to_selection", _chunk->vmin,_chunk->vmax);
@@ -185,6 +253,18 @@ namespace Noggit
, "to_selection", &chunk::to_selection
, "get_tex", &chunk::get_tex
, "get_vert", &chunk::get_vert
, "set_deep_flag", sol::overload(
&chunk::set_deep_flag
, &chunk::set_deep_flag_1
)
, "get_deep_flag", &chunk::get_deep_flag
, "get_deep_flag_high", &chunk::get_deep_flag_high
, "set_fishable_flag", sol::overload(
&chunk::set_fishable_flag
, &chunk::set_fishable_flag_1
)
, "get_fishable_flag", &chunk::get_fishable_flag
, "get_fishable_flag_high", &chunk::get_fishable_flag_high
);
}
} // namespace Scripting

View File

@@ -4,6 +4,7 @@
#include <noggit/scripting/script_vert.hpp>
#include <noggit/scripting/script_object.hpp>
#include <noggit/MapChunk.h>
#include <cstdint>
namespace Noggit
{
@@ -32,6 +33,18 @@ namespace Noggit
void apply_heightmap();
void apply_vertex_color();
void apply_all();
void set_deep_flag(std::uint32_t low, std::uint32_t high);
void set_deep_flag_1(std::uint32_t low);
std::uint32_t get_deep_flag();
std::uint32_t get_deep_flag_high();
void set_fishable_flag(std::uint32_t low, std::uint32_t high);
void set_fishable_flag_1(std::uint32_t low);
std::uint32_t get_fishable_flag();
std::uint32_t get_fishable_flag_high();
bool has_render_flags();
void set_impassable(bool add);
int get_area_id();
void set_area_id(int value);
@@ -39,6 +52,8 @@ namespace Noggit
vert get_vert(int index);
std::shared_ptr<selection> to_selection();
private:
MH2O_Render getRenderOrDefault();
MH2O_Render& getOrCreateRender();
MapChunk* _chunk;
friend class selection;
};

View File

@@ -4,6 +4,7 @@
#include <noggit/World.h>
#include <util/qt/overload.hpp>
#include <QtCore/QSettings>
#include <QtWidgets/QFormLayout>
#include <QtWidgets/QGridLayout>
@@ -78,6 +79,29 @@ namespace Noggit
layout->addWidget(settings_group);
QGroupBox* flatten_blur_group = new QGroupBox("Flatten/Blur", this);
flatten_blur_group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
auto flatten_blur_layout = new QGridLayout(flatten_blur_group);
flatten_blur_layout->addWidget(_lock_up_checkbox = new QCheckBox(this), 0, 0);
flatten_blur_layout->addWidget(_lock_down_checkbox = new QCheckBox(this), 0, 1);
_lock_up_checkbox->setChecked(_flatten_mode.raise);
_lock_up_checkbox->setText("Raise");
_lock_up_checkbox->setToolTip("Raise the terrain when using the tool");
_lock_down_checkbox->setChecked(_flatten_mode.lower);
_lock_down_checkbox->setText("Lower");
_lock_down_checkbox->setToolTip("Lower the terrain when using the tool");
QSettings settings;
bool use_classic_ui = settings.value("classicUI", true).toBool();
if (use_classic_ui)
flatten_blur_group->show();
else
flatten_blur_group->hide();
layout->addWidget(flatten_blur_group);
QGroupBox* flatten_only_group = new QGroupBox("Flatten only", this);
auto flatten_only_layout = new QVBoxLayout(flatten_only_group);
@@ -142,6 +166,20 @@ namespace Noggit
}
);
connect(_lock_up_checkbox, &QCheckBox::stateChanged
, [&](int state)
{
_flatten_mode.raise = state;
}
);
connect(_lock_down_checkbox, &QCheckBox::stateChanged
, [&](int state)
{
_flatten_mode.lower = state;
}
);
connect ( _angle_slider, &QSlider::valueChanged
, [&] (int v)
{
@@ -230,6 +268,16 @@ namespace Noggit
}
}
void flatten_blur_tool::nextFlattenMode()
{
_flatten_mode.next();
QSignalBlocker const up_lock(_lock_up_checkbox);
QSignalBlocker const down_lock(_lock_down_checkbox);
_lock_up_checkbox->setChecked(_flatten_mode.raise);
_lock_down_checkbox->setChecked(_flatten_mode.lower);
}
void flatten_blur_tool::nextFlattenType()
{
_flatten_type = ( ++_flatten_type ) % eFlattenType_Count;

View File

@@ -27,6 +27,7 @@ namespace Noggit
void blur (World* world, glm::vec3 const& cursor_pos, float dt);
void nextFlattenType();
void nextFlattenMode();
void toggleFlattenAngle();
void toggleFlattenLock();
void lockPos (glm::vec3 const& cursor_pos);

View File

@@ -641,7 +641,8 @@ namespace Noggit
new_obj->recalcExtents();
// check if pos is valid (not in an interior) wmo group
bool is_indoor = world->isInIndoorWmoGroup(new_obj->extents);
// bool is_indoor = world->isInIndoorWmoGroup(new_obj->extents, new_obj->transformMatrix());
bool is_indoor = false; // TODO Disabled the indoor check until non axis aligned boxes check is implemented
if (is_indoor)
{
QMessageBox::warning

View File

@@ -62,6 +62,10 @@ namespace Noggit
{
int liquid_id = i->getInt(LiquidTypeDB::ID);
// filter WMO liquids
if (liquid_id == 13 || liquid_id == 14 || liquid_id == 17 || liquid_id == 19 || liquid_id == 20)
continue;
std::stringstream ss;
ss << liquid_id << "-" << LiquidTypeDB::getLiquidName(liquid_id);
waterType->addItem (QString::fromUtf8(ss.str().c_str()), QVariant (liquid_id));

View File

@@ -116,8 +116,18 @@ namespace Noggit
}
//! \todo Only redraw stuff as told in event.
void minimap_widget::paintEvent (QPaintEvent*)
// called by _minimap->update()
// \todo : massive performance drop after clicking the minimap once until moving cursor out of frame, paintEvent gets called repeatidly
void minimap_widget::paintEvent (QPaintEvent* paint_event)
{
/*
auto rectangle = paint_event->rect();
auto left = rectangle.left();
auto rectop = rectangle.top();
auto recwidth = rectangle.width();
auto recheight = rectangle.height();
*/
//! \note Only take multiples of 1.0 pixels per tile.
const int smaller_side ((qMin (rect().width(), rect().height()) / 64) * 64);
const QRect drawing_rect (0, 0, smaller_side, smaller_side);
@@ -248,7 +258,33 @@ namespace Noggit
painter.drawLine (camera_vector);
}
}
if (_world->mapIndex.hasAGlobalWMO())
{
painter.setPen(QColor::fromRgbF(1.0f, 0.0f, 0.0f, 1.f));
auto extents = _world->mWmoEntry.extents;
// WMOInstance inst(_world->mWmoFilename, &_world->mWmoEntry, _world->_context);
float pos = tile_size * 64 / 2; // TODO : convert wmo pos
float min_point_x = pos + (extents[0][0] / TILESIZE * tile_size); // extents[min][x]
float min_point_y = pos + (extents[0][1] / TILESIZE * tile_size); // extents[min][y]
float max_point_x = pos + (extents[1][0] / TILESIZE * tile_size);
float max_point_y = pos + (extents[1][1] / TILESIZE * tile_size);
// tile_size = 14 | max size = 896
float width = max_point_x - min_point_x;
float height = max_point_y - min_point_y;
painter.drawRect(QRectF(min_point_x
, min_point_y
, width // width
, height // height
)
);
}
}
else
{
//! \todo Draw something so user realizes this will become the minimap.
@@ -283,7 +319,7 @@ namespace Noggit
QPoint tile = locateTile(event);
if (!world()->mapIndex.hasTile (TileIndex (tile.x(), tile.y())))
if (!world()->mapIndex.hasTile (TileIndex (tile.x(), tile.y())) && !_world->mapIndex.hasAGlobalWMO())
{
event->ignore();
return;
@@ -351,7 +387,7 @@ namespace Noggit
emit tile_clicked(tile);
}
update();
// update();
}
}
}

View File

@@ -49,7 +49,7 @@ namespace Noggit
void set_resizeable(bool state) { _resizeable = state; };
protected:
virtual void paintEvent (QPaintEvent*) override;
virtual void paintEvent (QPaintEvent* paint_event) override;
virtual void mouseDoubleClickEvent (QMouseEvent*) override;
virtual void mouseMoveEvent(QMouseEvent*) override;
virtual void mousePressEvent(QMouseEvent* event) override;

View File

@@ -31,6 +31,7 @@ namespace Noggit
)
: QWidget(parent)
, _brush_level(255)
, _show_unpaintable_chunks(false)
, _spray_size(1.0f)
, _spray_pressure(2.0f)
, _anim_prop(true)
@@ -106,6 +107,16 @@ namespace Noggit
_brush_level_spin->setSingleStep(5);
slider_layout_right->addWidget(_brush_level_spin);
_show_unpaintable_chunks_cb = new QCheckBox("Show unpaintable chunks", tool_widget);
_show_unpaintable_chunks_cb->setChecked(false);
tool_layout->addWidget(_show_unpaintable_chunks_cb);
connect(_show_unpaintable_chunks_cb, &QCheckBox::toggled, [=](bool checked)
{
_map_view->getWorld()->renderer()->getTerrainParamsUniformBlock()->draw_paintability_overlay = checked;
_map_view->getWorld()->renderer()->markTerrainParamsUniformBlockDirty();
});
// spray
_spray_mode_group = new QGroupBox("Spray", tool_widget);
_spray_mode_group->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
@@ -235,6 +246,13 @@ namespace Noggit
}
);
connect(_show_unpaintable_chunks_cb, &QCheckBox::stateChanged
, [&](int state)
{
_show_unpaintable_chunks = state;
}
);
connect ( _spray_size_spin, qOverload<double> (&QDoubleSpinBox::valueChanged)
, [&] (double v)
{
@@ -474,6 +492,11 @@ namespace Noggit
}
}
bool texturing_tool::show_unpaintable_chunks() const
{
return _show_unpaintable_chunks && _texturing_mode == texturing_mode::paint;
}
void texturing_tool::paint (World* world, glm::vec3 const& pos, float dt, scoped_blp_texture_reference texture)
{
if (TabletManager::instance()->isActive())
@@ -579,6 +602,7 @@ namespace Noggit
json["radius"] = _radius_slider->rawValue();
json["brush_level"] = _brush_level_spin->value();
json["texturing_mode"] = static_cast<int>(_texturing_mode);
json["show_unpaintable_chunks"] = _show_unpaintable_chunks_cb->isChecked();
json["anim"] = _anim_prop.get();
json["anim_speed"] = static_cast<int>(_anim_speed_prop.get());
@@ -613,6 +637,7 @@ namespace Noggit
_brush_level_spin->setValue(json["brush_level"].toInt());
tabs->setCurrentIndex(json["texturing_mode"].toInt());
_show_unpaintable_chunks_cb->setChecked(json["show_unpaintable_chunks"].toBool());
_anim_prop.set(json["anim"].toBool());
_anim_speed_prop.set(json["anim_speed"].toInt());

View File

@@ -45,6 +45,7 @@ namespace Noggit
float brush_radius() const;
float hardness() const;
bool show_unpaintable_chunks() const;
void set_brush_level (float level);
@@ -105,6 +106,7 @@ namespace Noggit
Brush _spray_brush;
int _brush_level;
bool _show_unpaintable_chunks;
float _spray_size;
float _spray_pressure;
@@ -123,6 +125,8 @@ namespace Noggit
Noggit::Ui::Tools::UiCommon::ExtendedSlider* _pressure_slider;
QSpinBox* _brush_level_spin;
QCheckBox* _show_unpaintable_chunks_cb;
QGroupBox* _spray_mode_group;
QWidget* _spray_content;
QCheckBox* _inner_radius_cb;

View File

@@ -37,9 +37,13 @@ MapCreationWizard::MapCreationWizard(std::shared_ptr<Project::NoggitProject> pro
auto layout = new QHBoxLayout(this);
// Left side
auto layout_left = new QHBoxLayout(this);
auto layout_left = new QVBoxLayout(this);
layout->addLayout(layout_left);
auto info_label = new QLabel("Left Click on the grid to add Tiles, Ctrl+click to erase, Shift+Click to add 3x3.");
info_label->setWindowIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::info));
layout_left->addWidget(info_label);
auto scroll_minimap = new QScrollArea(this);
_minimap_widget = new Noggit::Ui::minimap_widget(this);
@@ -224,6 +228,7 @@ MapCreationWizard::MapCreationWizard(std::shared_ptr<Project::NoggitProject> pro
_map_settings->setLayout(difficulty_settings_layout);
_difficulty_type = new QComboBox(_map_settings);
_difficulty_type->setDisabled(true);
difficulty_settings_layout->addRow("Difficulty Index", _difficulty_type);
@@ -586,6 +591,23 @@ void MapCreationWizard::saveCurrentEntry()
prompt.exec();
return;
}
// prompt the user if no tile was select
if (!_world->mapIndex.getNumExistingTiles()) // _world->mapIndex.hasAGlobalWMO()
{
QMessageBox prompt;
prompt.setIcon(QMessageBox::Warning);
prompt.setWindowTitle("Empty map");
prompt.setText(std::string("Warning : Your map is empty.").c_str());
prompt.setInformativeText("You didn't set any tile. To add tiles to your map click on the squares in the grid.\nDo you still want to save the empty map ?");
prompt.setStandardButtons(QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No);
prompt.setDefaultButton(QMessageBox::No);
prompt.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint);
bool answer = prompt.exec() == QMessageBox::StandardButton::Yes;
if (!answer)
{
return;
}
}
// Create WDT empty file for new map
std::stringstream filename;
@@ -710,6 +732,19 @@ void MapCreationWizard::addNewMap()
void MapCreationWizard::removeMap()
{
QMessageBox prompt;
prompt.setIcon(QMessageBox::Warning);
prompt.setWindowTitle("Remove Map");
prompt.setText(std::string("Are you sure you want to remove this map ?").c_str());
prompt.setStandardButtons(QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No);
prompt.setDefaultButton(QMessageBox::No);
prompt.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowStaysOnTopHint);
bool answer = prompt.exec() == QMessageBox::StandardButton::Yes;
if (!answer)
{
return;
}
if (!_is_new_record && _cur_map_id >= 0)
{
gMapDB.removeRecord(_cur_map_id);

View File

@@ -87,7 +87,7 @@ namespace Noggit
void wheelEvent(QWheelEvent *event) override;
void destroyFakeWorld() { if(_world) delete _world; _world = nullptr; _minimap_widget->world (nullptr); };
void addNewMap();
signals:
void map_dbc_updated();
@@ -148,7 +148,6 @@ namespace Noggit
void saveCurrentEntry();
void discardChanges();
void addNewMap();
void removeMap();
};

View File

@@ -4,6 +4,7 @@
#include <noggit/ui/tools/ActionHistoryNavigator/ActionHistoryNavigator.hpp>
#include <noggit/ui/FontAwesome.hpp>
#include <QSlider>
#include <QtCore/QSettings>
using namespace Noggit::Ui;
using namespace Noggit::Ui::Tools::ViewToolbar::Ui;
@@ -135,6 +136,7 @@ ViewToolbar::ViewToolbar(MapView *mapView, ViewToolbar *tb)
// add_tool_icon(mapView, &mapView->_game_mode_camera, tr("Tile view"), FontNoggit::VIEW_MODE_2D, tb);
addSeparator();
add_tool_icon(mapView, &mapView->_show_minimap_window, tr("Show Minimap"),FontNoggit::TOOL_MINIMAP_EDITOR, tb);
add_tool_icon(mapView, &mapView->_show_detail_info_window, tr("Details info"), FontNoggit::INFO, tb);
// TODO : will open a panel with time controls, or use 2n toolbar
@@ -326,6 +328,9 @@ void ViewToolbar::setCurrentMode(MapView* mapView, editing_mode mode)
mapView->getLeftSecondaryToolbar()->hide();
current_mode = mode;
QSettings settings;
bool use_classic_ui = settings.value("classicUI", false).toBool();
switch (current_mode)
{
case editing_mode::ground:
@@ -334,14 +339,20 @@ void ViewToolbar::setCurrentMode(MapView* mapView, editing_mode mode)
if (_flatten_secondary_tool.size() > 0)
{
setupWidget(_flatten_secondary_tool);
mapView->getLeftSecondaryToolbar()->show();
if (!use_classic_ui)
mapView->getLeftSecondaryToolbar()->show();
else
mapView->getLeftSecondaryToolbar()->hide();
}
break;
case editing_mode::paint:
if (_texture_secondary_tool.size() > 0)
{
setupWidget(_texture_secondary_tool);
mapView->getLeftSecondaryToolbar()->show();
if (!use_classic_ui)
mapView->getLeftSecondaryToolbar()->show();
else
mapView->getLeftSecondaryToolbar()->hide();
}
break;
case editing_mode::object:
@@ -387,7 +398,16 @@ void ViewToolbar::add_tool_icon(MapView* mapView,
}
});
connect (view_state, &Noggit::BoolToggleProperty::changed, [action, view_state] () {
connect (view_state, &Noggit::BoolToggleProperty::changed, [action, view_state, mapView] () {
if (action->text() == "Game view" && view_state->get())
{
// hack, manually update camera when switch to game_view
mapView->setCameraDirty();
auto ground_pos = mapView->getWorld()->get_ground_height(mapView->getCamera()->position);
mapView->getCamera()->position.y = ground_pos.y + 2;
}
action->setChecked(view_state->get());
});

View File

@@ -26,6 +26,8 @@ namespace Noggit
void setCurrentMode(MapView* mapView, editing_mode mode);
editing_mode getCurrentMode() const { return current_mode; }
/*secondary top tool*/
QVector<QWidgetAction*> _climb_secondary_tool;
QVector<QWidgetAction*> _time_secondary_tool;

View File

@@ -126,12 +126,17 @@ namespace Noggit::Ui::Windows
#ifdef USE_MYSQL_UID_STORAGE
bool use_mysql = settings.value("project/mysql/enabled", false).toBool();
mysql::testConnection(true);
bool valid_conn = false;
if (use_mysql)
{
valid_conn = mysql::testConnection(true);
}
if ((use_mysql && mysql::hasMaxUIDStoredDB(_world->getMapID()))
if ((valid_conn && mysql::hasMaxUIDStoredDB(_world->getMapID()))
|| uid_storage::hasMaxUIDStored(_world->getMapID())
)
{
_world->mapIndex.loadMaxUID();
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix_mode::none, from_bookmark);
}
@@ -168,6 +173,28 @@ namespace Noggit::Ui::Windows
bool from_bookmark
)
{
if (_world->mapIndex.hasAGlobalWMO())
{
// enter at mdoel's position
// pos = glm::vec3(_world->mWmoEntry[0], _world->mWmoEntry.pos[1], _world->mWmoEntry.pos[2]);
// better, enter at model's max extent, facing toward min extent
auto min_extent = glm::vec3(_world->mWmoEntry.extents[0][0], _world->mWmoEntry.extents[0][1], _world->mWmoEntry.extents[0][2]);
auto max_extent = glm::vec3(_world->mWmoEntry.extents[1][0], _world->mWmoEntry.extents[1][1] * 2, _world->mWmoEntry.extents[1][2]);
float dx = min_extent.x - max_extent.x;
float dy = min_extent.z - max_extent.z; // flipping z and y works better for some reason
float dz = min_extent.y - max_extent.y;
pos = max_extent;
camera_yaw = math::degrees(math::radians(std::atan2(dx, dy)));
float distance = std::sqrt(dx * dx + dy * dy + dz * dz);
camera_pitch = math::degrees(math::radians(std::asin(dz / distance)));
}
_map_creation_wizard->destroyFakeWorld();
_map_view = (new MapView(camera_yaw, camera_pitch, pos, this, _project, std::move(_world), uid_fix, from_bookmark));
connect(_map_view, &MapView::uid_fix_failed, [this]()
@@ -182,7 +209,7 @@ namespace Noggit::Ui::Windows
}
void NoggitWindow::applyFilterSearch(const QString &name, int type, int expansion)
void NoggitWindow::applyFilterSearch(const QString &name, int type, int expansion, bool wmo_maps)
{
for (int i = 0; i < _continents_table->count(); ++i)
{
@@ -210,6 +237,11 @@ namespace Noggit::Ui::Windows
{
item_widget->setHidden(true);
}
if (!(widget->wmo_map() == wmo_maps))
{
item_widget->setHidden(true);
}
}
}
@@ -255,6 +287,10 @@ namespace Noggit::Ui::Windows
QTabWidget* entry_points_tabs(new QTabWidget(widget));
//entry_points_tabs->addTab(_continents_table, "Maps");
auto add_btn = new QPushButton("Add New Map", this);
add_btn->setIcon(Noggit::Ui::FontAwesomeIcon(Noggit::Ui::FontAwesome::plus));
add_btn->setAccessibleName("map_wizard_add_button");
/* set-up widget for seaching etc... through _continents_table */
{
QWidget* _first_tab = new QWidget(this);
@@ -289,31 +325,39 @@ namespace Noggit::Ui::Windows
_combo_exp_search->addItem(QIcon(":/icon-shadow"), tr("Shadowlands"));
_combo_exp_search->setCurrentIndex(0);
QObject::connect(_line_edit_search, QOverload<const QString&>::of(&QLineEdit::textChanged), [this, _combo_search, _combo_exp_search](const QString &name)
QCheckBox* _wmo_maps_search = new QCheckBox("Display WMO maps (No terrain)", this);
QObject::connect(_line_edit_search, QOverload<const QString&>::of(&QLineEdit::textChanged), [this, _combo_search, _combo_exp_search, _wmo_maps_search](const QString &name)
{
applyFilterSearch(name, _combo_search->currentIndex(), _combo_exp_search->currentIndex());
applyFilterSearch(name, _combo_search->currentIndex(), _combo_exp_search->currentIndex(), _wmo_maps_search->isChecked());
});
QObject::connect(_combo_search, QOverload<int>::of(&QComboBox::currentIndexChanged), [this, _line_edit_search, _combo_exp_search](int index)
QObject::connect(_combo_search, QOverload<int>::of(&QComboBox::currentIndexChanged), [this, _line_edit_search, _combo_exp_search, _wmo_maps_search](int index)
{
applyFilterSearch(_line_edit_search->text(), index, _combo_exp_search->currentIndex());
applyFilterSearch(_line_edit_search->text(), index, _combo_exp_search->currentIndex(), _wmo_maps_search->isChecked());
});
QObject::connect(_combo_exp_search, QOverload<int>::of(&QComboBox::currentIndexChanged), [this, _line_edit_search, _combo_search](int index)
QObject::connect(_combo_exp_search, QOverload<int>::of(&QComboBox::currentIndexChanged), [this, _line_edit_search, _combo_search, _wmo_maps_search](int index)
{
applyFilterSearch(_line_edit_search->text(), _combo_search->currentIndex(), index);
applyFilterSearch(_line_edit_search->text(), _combo_search->currentIndex(), index, _wmo_maps_search->isChecked());
});
QObject::connect(_wmo_maps_search, &QCheckBox::stateChanged, [this, _line_edit_search, _combo_search, _combo_exp_search](bool b)
{
applyFilterSearch(_line_edit_search->text(), _combo_search->currentIndex(), _combo_exp_search->currentIndex(), b);
});
QFormLayout* _group_layout = new QFormLayout();
_group_layout->addRow(tr("Name : "), _line_edit_search);
_group_layout->addRow(tr("Type : "), _combo_search);
_group_layout->addRow(tr("Expansion : "), _combo_exp_search);
_group_layout->addRow( _wmo_maps_search);
_group_search->setLayout(_group_layout);
_first_tab_layout->addWidget(_group_search);
_first_tab_layout->addSpacing(12);
_first_tab_layout->addSpacing(5);
_first_tab_layout->addWidget(_continents_table);
_first_tab_layout->addWidget(add_btn);
entry_points_tabs->addTab(_first_tab, tr("Maps"));
}
@@ -369,18 +413,21 @@ namespace Noggit::Ui::Windows
QObject::connect(_minimap, &minimap_widget::map_clicked, [this](::glm::vec3 const& pos)
{
check_uid_then_enter_map(pos, math::degrees(30.f), math::degrees(90.f));
if (_world->mapIndex.hasAGlobalWMO()) // skip uid check
enterMapAt(pos, math::degrees(30.f), math::degrees(90.f), uid_fix_mode::none, false);
else
check_uid_then_enter_map(pos, math::degrees(30.f), math::degrees(90.f));
}
);
auto right_side = new QTabWidget(this);
_right_side = new QTabWidget(this);
auto minimap_holder = new QScrollArea(this);
minimap_holder->setWidgetResizable(true);
minimap_holder->setAlignment(Qt::AlignCenter);
minimap_holder->setWidget(_minimap);
right_side->addTab(minimap_holder, "Enter map");
_right_side->addTab(minimap_holder, "Enter map");
minimap_holder->setAccessibleName("main_menu_minimap_holder");
_map_creation_wizard = new Noggit::Ui::Tools::MapCreationWizard::Ui::MapCreationWizard(_project, this);
@@ -392,9 +439,16 @@ namespace Noggit::Ui::Windows
}
);
right_side->addTab(_map_creation_wizard, "Edit map");
_right_side->addTab(_map_creation_wizard, "Edit map");
layout->addWidget(right_side);
layout->addWidget(_right_side);
connect(add_btn, &QPushButton::clicked
, [&]()
{
_right_side->setCurrentIndex(1);
_map_creation_wizard->addNewMap();
});
//setCentralWidget (_stack_widget);

View File

@@ -86,8 +86,9 @@ namespace Noggit::Ui::Windows
QListWidget* _continents_table;
QString _filter_name;
QTabWidget* _right_side;
void applyFilterSearch(const QString& name, int type, int expansion);
void applyFilterSearch(const QString& name, int type, int expansion, bool wmo_maps);
std::unique_ptr<World> _world;

View File

@@ -42,6 +42,8 @@ void BuildMapListComponent::buildMapList(Noggit::Ui::Windows::NoggitWindow* pare
if (map_list_data.map_type_id < 0 || map_list_data.map_type_id > 5 || !World::IsEditableWorld(record))
continue;
map_list_data.wmo_map = (World::IsWMOWorld(record));
auto project_pinned_maps = parent->_project->PinnedMaps;
auto pinned_map_found = std::find_if(std::begin(project_pinned_maps), std::end(project_pinned_maps),

View File

@@ -18,6 +18,7 @@ namespace Noggit::Ui::Widget
int map_type_id;
int expansion_id;
bool pinned;
bool wmo_map;
};
class MapListItem : public QWidget
@@ -40,6 +41,7 @@ namespace Noggit::Ui::Widget
int id() { return _map_data.map_id; };
int type() { return _map_data.map_type_id; };
int expansion() { return _map_data.expansion_id; };
bool wmo_map() { return _map_data.wmo_map; };
private:
QString toCamelCase(const QString& s);

View File

@@ -378,11 +378,6 @@
<string>Wrath Of The Lich King</string>
</property>
</item>
<item>
<property name="text">
<string>Shadowlands</string>
</property>
</item>
</widget>
</item>
<item>

View File

@@ -211,7 +211,7 @@ void NoggitProjectSelectionWindow::handleContextMenuProjectListItemForget(std::s
prompt.setWindowTitle("Forget Project");
prompt.setIcon(QMessageBox::Warning);
prompt.setWindowFlags(Qt::WindowStaysOnTopHint);
prompt.setText("Data on the disk will not be removed. Continue?.");
prompt.setText("Data on the disk will not be removed, this action will only hide the project. Continue?.");
prompt.addButton("Accept", QMessageBox::AcceptRole);
prompt.setDefaultButton(prompt.addButton("Cancel", QMessageBox::RejectRole));
prompt.setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowTitleHint);

View File

@@ -4,6 +4,7 @@
#include <QList>
#include <filesystem>
#include <QDesktopServices>
using namespace Noggit::Ui::Component;
@@ -72,7 +73,27 @@ void RecentProjectsComponent::buildRecentProjectsList(Noggit::Ui::Windows::Noggi
});
context_menu.addAction(&action_2);
/////
QAction action_3("Open Project Directory", project_list_item);
action_3.setIcon(FontAwesomeIcon(FontAwesome::folderopen).pixmap(QSize(16, 16)));
QObject::connect(&action_3, &QAction::triggered, [=]()
{
openDirectory(project_data.project_directory.toStdString());
});
context_menu.addAction(&action_3);
/////
QAction action_4("Open WoW Client Directory", project_list_item);
action_4.setIcon(FontAwesomeIcon(FontAwesome::gamepad).pixmap(QSize(16, 16)));
QObject::connect(&action_4, &QAction::triggered, [=]()
{
openDirectory(project->ClientPath);
});
context_menu.addAction(&action_4);
/////
context_menu.exec(project_list_item->mapToGlobal(pos));
});
@@ -84,6 +105,38 @@ void RecentProjectsComponent::buildRecentProjectsList(Noggit::Ui::Windows::Noggi
settings.endArray();
}
void RecentProjectsComponent::openDirectory(std::string const& directory_path)
{
if (!std::filesystem::exists(directory_path) || !std::filesystem::is_directory(directory_path))
return;
auto path = QString(directory_path.c_str());
QFileInfo info(path);
#if defined(Q_OS_WIN)
QStringList args;
if (!info.isDir())
args << "/select,";
args << QDir::toNativeSeparators(path);
if (QProcess::startDetached("explorer", args))
return;
#elif defined(Q_OS_MAC)
QStringList args;
args << "-e";
args << "tell application \"Finder\"";
args << "-e";
args << "activate";
args << "-e";
args << "select POSIX file \"" + path + "\"";
args << "-e";
args << "end tell";
args << "-e";
args << "return";
if (!QProcess::execute("/usr/bin/osascript", args))
return;
#endif
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
}
void RecentProjectsComponent::registerProjectChange(std::string const& project_path)
{
QSettings settings;

View File

@@ -18,7 +18,8 @@ namespace Noggit::Ui::Component
static void buildRecentProjectsList(Noggit::Ui::Windows::NoggitProjectSelectionWindow* parent);
static void registerProjectChange(std::string const& project_path);
static void registerProjectRemove(std::string const& project_path);
private:
static void openDirectory(std::string const& directory_path);
};
}

View File

@@ -194,6 +194,7 @@ namespace Noggit
ui->_uid_cb->setChecked(_settings->value("uid_startup_check", true).toBool());
ui->_systemWindowFrame->setChecked(_settings->value("systemWindowFrame", true).toBool());
ui->_nativeMenubar->setChecked(_settings->value("nativeMenubar", true).toBool());
ui->_classic_ui->setChecked(_settings->value("classicUI", false).toBool());
ui->_additional_file_loading_log->setChecked(
_settings->value("additional_file_loading_log", false).toBool());
ui->_keyboard_locale->setCurrentText(_settings->value("keyboard_locale", "QWERTY").toString());
@@ -278,6 +279,7 @@ namespace Noggit
_settings->setValue("keyboard_locale", ui->_keyboard_locale->currentText());
_settings->setValue("systemWindowFrame", ui->_systemWindowFrame->isChecked());
_settings->setValue("nativeMenubar", ui->_nativeMenubar->isChecked());
_settings->setValue("classicUI", ui->_classic_ui->isChecked());
#ifdef USE_MYSQL_UID_STORAGE
_settings->setValue ("project/mysql/enabled", ui->MySQL_box->isChecked());

View File

@@ -274,7 +274,7 @@
<item>
<widget class="QRadioButton" name="_systemWindowFrame">
<property name="toolTip">
<string>Turns on default system window frame instead of Noggit's styled one. Requires restart.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Turns on default system window frame instead of Noggit's styled one. Requires restart.&lt;/p&gt;&lt;p&gt;Warning: Can cause crash.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>System window frame</string>
@@ -318,6 +318,46 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="_classic_ui">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Disable the new secondary toolbars and moves the settings back to the tool panels.&lt;/p&gt;&lt;p&gt; Requires restart.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Classic UI</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_23">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>