From 56aab6b7c019940c582590b22a005dd570df61bf Mon Sep 17 00:00:00 2001 From: T1ti <40864460+T1ti@users.noreply.github.com> Date: Mon, 26 Aug 2024 02:54:57 +0200 Subject: [PATCH] update liquid attributes and fatigue auto updates --- src/noggit/ChunkWater.cpp | 109 ++++++++-- src/noggit/ChunkWater.hpp | 10 +- src/noggit/MapHeaders.h | 43 +++- src/noggit/Selection.cpp | 12 +- src/noggit/TabletManager.hpp | 2 + src/noggit/liquid_layer.cpp | 286 +++++++++++++++++--------- src/noggit/liquid_layer.hpp | 48 ++++- src/noggit/rendering/LiquidRender.cpp | 8 +- src/noggit/scripting/script_chunk.cpp | 39 +--- src/noggit/scripting/script_chunk.hpp | 4 +- src/noggit/ui/Water.cpp | 9 +- src/noggit/wmo_liquid.cpp | 29 +-- src/noggit/wmo_liquid.hpp | 2 +- 13 files changed, 392 insertions(+), 209 deletions(-) diff --git a/src/noggit/ChunkWater.cpp b/src/noggit/ChunkWater.cpp index 476ba217..087a7340 100755 --- a/src/noggit/ChunkWater.cpp +++ b/src/noggit/ChunkWater.cpp @@ -12,6 +12,7 @@ ChunkWater::ChunkWater(MapChunk* chunk, TileWater* water_tile, float x, float z, , zbase(z) , vmin(x, 0.f, z) , vmax(x + CHUNKSIZE, 0.f, z + CHUNKSIZE) + , vcenter((vmin + vmax) * 0.5f) , _use_mclq_green_lava(use_mclq_green_lava) , _chunk(chunk) , _water_tile(water_tile) @@ -22,7 +23,6 @@ void ChunkWater::from_mclq(std::vector& layers) { glm::vec3 pos(xbase, 0.0f, zbase); - if (!Render.has_value()) Render.emplace(); for (mclq& liquid : layers) { std::uint8_t mclq_liquid_type = 0; @@ -33,8 +33,8 @@ void ChunkWater::from_mclq(std::vector& layers) { mclq_tile const& tile = liquid.tiles[z * 8 + x]; - misc::bit_or(Render.value().fishable, x, z, tile.fishable); - misc::bit_or(Render.value().fatigue, x, z, tile.fatigue); + misc::bit_or(attributes.fishable, x, z, tile.fishable); + misc::bit_or(attributes.fatigue, x, z, tile.fatigue); if (!tile.dont_render) { @@ -45,17 +45,17 @@ void ChunkWater::from_mclq(std::vector& layers) switch (mclq_liquid_type) { - case 1: - _layers.emplace_back(this, pos, liquid, 2); + case 1: // mclq ocean + _layers.emplace_back(this, pos, liquid, LIQUID_OCEAN); break; - case 3: - _layers.emplace_back(this, pos, liquid, 4); + case 3: // mclq slime + _layers.emplace_back(this, pos, liquid, LIQUID_SLIME); break; - case 4: - _layers.emplace_back(this, pos, liquid, 1); + case 4: // mclq river + _layers.emplace_back(this, pos, liquid, LIQUID_WATER); break; - case 6: - _layers.emplace_back(this, pos, liquid, (_use_mclq_green_lava ? 15 : 3)); + case 6: // mclq magma + _layers.emplace_back(this, pos, liquid, (_use_mclq_green_lava ? LIQUID_Green_Lava : LIQUID_MAGMA)); break; default: @@ -78,17 +78,22 @@ void ChunkWater::fromFile(BlizzardArchive::ClientFile &f, size_t basePos) } //render - if (header.ofsRenderMask) + if (header.ofsAttributes) { - Render.emplace(); - f.seek(basePos + header.ofsRenderMask); - f.read(&Render.value(), sizeof(MH2O_Render)); + f.seek(basePos + header.ofsAttributes); + f.read(&attributes, sizeof(MH2O_Attributes)); + } + else + { + // when chunks don't have attributes it means everything is set. + attributes.fishable = 0xFFFFFFFFFFFFFFFF; + attributes.fatigue = 0xFFFFFFFFFFFFFFFF; } for (std::size_t k = 0; k < header.nLayers; ++k) { MH2O_Information info; - uint64_t infoMask = 0xFFFFFFFFFFFFFFFF; // default = all water + uint64_t infoMask = 0xFFFFFFFFFFFFFFFF; // default = all water. InfoMask + HeightMap combined //info f.seek(basePos + header.ofsInformation + sizeof(MH2O_Information)* k); @@ -106,6 +111,8 @@ void ChunkWater::fromFile(BlizzardArchive::ClientFile &f, size_t basePos) glm::vec3 pos(xbase, 0.0f, zbase); _water_tile->tagUpdate(); + // liquid_layer(ChunkWater* chunk, BlizzardArchive::ClientFile& f, std::size_t base_pos, glm::vec3 const& base + // ,MH2O_Information const& info, std::uint64_t infomask); _layers.emplace_back(this, f, basePos, pos, info, infoMask); } @@ -119,20 +126,23 @@ void ChunkWater::save(sExtendableArray& adt, int base_pos, int& header_pos, int& // remove empty layers cleanup(); + update_attributes(); if (hasData(0)) { header.nLayers = static_cast(_layers.size()); - if (Render.has_value()) + // fagique only for single layer ocean chunk + bool fatigue = _layers[0].has_fatigue(); + if (!fatigue) { - header.ofsRenderMask = current_pos - base_pos; - adt.Insert(current_pos, sizeof(MH2O_Render), reinterpret_cast(&Render.value())); - current_pos += sizeof(MH2O_Render); + header.ofsAttributes = current_pos - base_pos; + adt.Insert(current_pos, sizeof(MH2O_Attributes), reinterpret_cast(&attributes)); + current_pos += sizeof(MH2O_Attributes); } else { - header.ofsRenderMask = 0; + header.ofsAttributes = 0; } header.ofsInformation = current_pos - base_pos; @@ -211,6 +221,16 @@ void ChunkWater::update_layers() vmin.y = std::numeric_limits::max(); vmax.y = std::numeric_limits::lowest(); + std::uint64_t debug_fishable_old = attributes.fishable; + std::uint64_t debug_fatigue_old = attributes.fatigue; + + if (_auto_update_attributes) + { + // reset attributes + attributes.fishable = 0; + attributes.fatigue = 0; + } + for (liquid_layer& layer : _layers) { vmin.y = std::min (vmin.y, layer.min()); @@ -220,9 +240,45 @@ void ChunkWater::update_layers() extents[1].y = std::max(extents[1].y, vmax.y); _water_tile->tagUpdate(); + + if (_auto_update_attributes) + layer.update_attributes(attributes); + count++; } + // some tests to compare with blizzard + const bool run_tests = false; + if (run_tests) + { + if (attributes.fishable != debug_fishable_old && _layers.size()) + { + uint64_t x = attributes.fishable ^ debug_fishable_old; + int difference_count = 0; + + + while (x > 0) { + difference_count += x & 1; + x >>= 1; + } + + auto type = _layers.front().liquidID(); + std::vector depths; + + int zero_depth_num = 0; + int below_20_num = 0; // below 0.2 + for (auto& vert : _layers.front().getVertices()) + { + depths.push_back(vert.depth); + if (vert.depth == 0.0f) + zero_depth_num++; + if (vert.depth < 0.2f && vert.depth != 0.0f) + below_20_num++; + } + } + } + + _water_tile->tagExtents(true); vcenter = (vmin + vmax) * 0.5f; @@ -350,6 +406,17 @@ void ChunkWater::copy_height_to_layer(liquid_layer& target, glm::vec3 const& pos } } +void ChunkWater::update_attributes() +{ + attributes.fishable = 0; + attributes.fatigue = 0; + + for (liquid_layer& layer : _layers) + { + layer.update_attributes(attributes); + } +} + void ChunkWater::tagUpdate() { _water_tile->tagUpdate(); diff --git a/src/noggit/ChunkWater.hpp b/src/noggit/ChunkWater.hpp index 69464443..2b86cfce 100755 --- a/src/noggit/ChunkWater.hpp +++ b/src/noggit/ChunkWater.hpp @@ -70,10 +70,13 @@ public: MapChunk* getChunk() { return _chunk; }; TileWater* getWaterTile() { return _water_tile; }; - std::optional Render; + + MH2O_Attributes& const getAttributes() { return attributes; }; + float xbase, zbase; private: + MH2O_Attributes attributes; glm::vec3 vmin, vmax, vcenter; bool _use_mclq_green_lava; @@ -83,8 +86,9 @@ private: void copy_height_to_layer(liquid_layer& target, glm::vec3 const& pos, float radius); - - + bool _auto_update_attributes = true; + // updates attributes for all layers + void update_attributes(); std::vector _layers; MapChunk* _chunk; diff --git a/src/noggit/MapHeaders.h b/src/noggit/MapHeaders.h index f8512e84..97077db0 100755 --- a/src/noggit/MapHeaders.h +++ b/src/noggit/MapHeaders.h @@ -187,15 +187,49 @@ struct ENTRY_MCSE struct MH2O_Header{ uint32_t ofsInformation; uint32_t nLayers; - uint32_t ofsRenderMask; + uint32_t ofsAttributes; MH2O_Header() : ofsInformation(0) , nLayers(0) - , ofsRenderMask(0) + , ofsAttributes(0) {} }; +// enum for type column of liquidtype.dbc +enum liquid_basic_types +{ + liquid_basic_types_water = 0, + liquid_basic_types_ocean = 1, + liquid_basic_types_magma = 2, + liquid_basic_types_slime = 3, + + liquid_basic_types_MASK = 3, +}; + +// just liquidtype.dbc +enum liquid_types +{ + LIQUID_WATER = 1, + LIQUID_OCEAN = 2, + LIQUID_MAGMA = 3, + LIQUID_SLIME = 4, + // slow + // fast + LIQUID_WMO_Water = 13, + LIQUID_WMO_Ocean = 14, + LIQUID_Green_Lava = 15, + LIQUID_WMO_Water_Interior = 17, + LIQUID_WMO_Magma = 19, + LIQUID_WMO_Slime = 20, + + LIQUID_END_BASIC_LIQUIDS = LIQUID_WMO_Slime, + + LIQUID_FIRST_NONBASIC_LIQUID_TYPE = 21, + + LIQUID_NAXX_SLIME = LIQUID_FIRST_NONBASIC_LIQUID_TYPE, +}; + struct MH2O_Information{ uint16_t liquid_id; uint16_t liquid_vertex_format; @@ -231,11 +265,10 @@ struct mh2o_uv std::uint16_t y; }; -struct MH2O_Render +struct MH2O_Attributes { - // seems to be usable as visibility information (as per https://wowdev.wiki/ADT/v18#MH2O_chunk_.28WotLK.2B.29) std::uint64_t fishable = 0xFFFFFFFFFFFFFFFF; - std::uint64_t fatigue = 0; + std::uint64_t fatigue = 0; // should be set to max ? }; struct water_vert diff --git a/src/noggit/Selection.cpp b/src/noggit/Selection.cpp index f4565250..64de87ec 100755 --- a/src/noggit/Selection.cpp +++ b/src/noggit/Selection.cpp @@ -171,7 +171,7 @@ void selected_chunk_type::updateDetails(Noggit::Ui::detail_infos* detail_widget) { ChunkWater* waterchunk = chunk->liquid_chunk(); - MH2O_Render liquid_render = waterchunk->Render.value_or(MH2O_Render{ 0xffffffffffffffff,0xffffffffffffffff }); + MH2O_Attributes attributes = waterchunk->getAttributes(); if (waterchunk->hasData(0)) { @@ -179,11 +179,13 @@ void selected_chunk_type::updateDetails(Noggit::Ui::detail_infos* detail_widget) liquid_layer liquid = waterchunk->getLayers()->at(0); // only getting data from layer 0, maybe loop them ? int liquid_flags = liquid.getSubchunks(); - select_info << "
liquid type: " << liquid.liquidID() << " (\"" << gLiquidTypeDB.getLiquidName(liquid.liquidID()) << "\")" - << "
liquid flags: " + select_info << "
Liquid type: " << liquid.liquidID() << " (\"" << gLiquidTypeDB.getLiquidName(liquid.liquidID()) << "\")" + << "
liquid flags(center): " // getting flags from the center tile - << ((liquid_render.fishable >> (4 * 8 + 4)) & 1 ? "fishable " : "") - << ((liquid_render.fatigue >> (4 * 8 + 4)) & 1 ? "fatigue" : ""); + << ((attributes.fishable >> (4 * 8 + 4)) & 1 ? "fishable " : "") + << ((attributes.fatigue >> (4 * 8 + 4)) & 1 ? "fatigue " : "") + + << (liquid.has_fatigue() ? "
entire chunk has fatigue!" : ""); } } else diff --git a/src/noggit/TabletManager.hpp b/src/noggit/TabletManager.hpp index 8c7f1952..0ecda711 100755 --- a/src/noggit/TabletManager.hpp +++ b/src/noggit/TabletManager.hpp @@ -29,7 +29,9 @@ namespace Noggit private: TabletManager() : QObject() {} + // gets activated by MapView::tabletEvent(QTabletEvent* event) bool _is_active = false; + // 0.0 to 1.0 scale double _pressure = 0.0; }; diff --git a/src/noggit/liquid_layer.cpp b/src/noggit/liquid_layer.cpp index b546a657..f4d135fe 100755 --- a/src/noggit/liquid_layer.cpp +++ b/src/noggit/liquid_layer.cpp @@ -20,7 +20,7 @@ namespace liquid_layer::liquid_layer(ChunkWater* chunk, glm::vec3 const& base, float height, int liquid_id) : _liquid_id(liquid_id) - , _liquid_vertex_format(0) + , _liquid_vertex_format(HEIGHT_DEPTH) , _minimum(height) , _maximum(height) , _subchunks(0) @@ -28,22 +28,9 @@ liquid_layer::liquid_layer(ChunkWater* chunk, glm::vec3 const& base, float heigh , _chunk(chunk) { if (!gLiquidTypeDB.CheckIfIdExists(_liquid_id)) - _liquid_id = 1; + _liquid_id = LIQUID_WATER; - for (int z = 0; z < 9; ++z) - { - for (int x = 0; x < 9; ++x) - { - unsigned v_index = z * 9 + x; - _tex_coords[v_index] = default_uv(x, z); - _depth[v_index] = 1.0f; - _vertices[v_index] = glm::vec3( - pos.x + UNITSIZE * x - , height - , pos.z + UNITSIZE * z - ); - } - } + create_vertices(height); changeLiquidID(_liquid_id); @@ -58,7 +45,7 @@ liquid_layer::liquid_layer(ChunkWater* chunk, glm::vec3 const& base, mclq& liqui , _chunk(chunk) { if (!gLiquidTypeDB.CheckIfIdExists(_liquid_id)) - _liquid_id = 1; + _liquid_id = LIQUID_WATER; changeLiquidID(_liquid_id); @@ -77,23 +64,25 @@ liquid_layer::liquid_layer(ChunkWater* chunk, glm::vec3 const& base, mclq& liqui const unsigned v_index = z * 9 + x; mclq_vertex const& v = liquid.vertices[v_index]; - if (_liquid_vertex_format == 1) + liquid_vertex lv; + + // _liquid_vertex_format is set by changeLiquidID() + if (_liquid_vertex_format == HEIGHT_UV) { - _depth[v_index] = 1.0f; - _tex_coords[v_index] = glm::vec2(static_cast(v.magma.x) / 255.f, static_cast(v.magma.y) / 255.f); + lv.depth = 1.f; + lv.uv = { static_cast(v.magma.x) / 255.f, static_cast(v.magma.y) / 255.f }; } else { - _depth[v_index] = static_cast(v.water.depth) / 255.f; - _tex_coords[v_index] = default_uv(x, z); + lv.depth = static_cast(v.water.depth) / 255.f; + lv.uv = default_uv(x, z); } - _vertices[v_index] = glm::vec3( - pos.x + UNITSIZE * x - // sometimes there's garbage data on unused tiles that mess things up - , std::clamp(v.height, _minimum, _maximum) - , pos.z + UNITSIZE * z - ); + // sometimes there's garbage data on unused tiles that mess things up + lv.position = { pos.x + UNITSIZE * x, std::clamp(v.height, _minimum, _maximum), pos.z + UNITSIZE * z }; + + + _vertices[v_index] = lv; } } @@ -115,7 +104,7 @@ liquid_layer::liquid_layer(ChunkWater* chunk { // check if liquid id is valid or some downported maps will crash if (!gLiquidTypeDB.CheckIfIdExists(_liquid_id)) - _liquid_id = 1; + _liquid_id = LIQUID_WATER; int offset = 0; for (int z = 0; z < info.height; ++z) @@ -128,40 +117,28 @@ liquid_layer::liquid_layer(ChunkWater* chunk } // default values - for (int z = 0; z < 9; ++z) - { - for (int x = 0; x < 9; ++x) - { - const unsigned v_index = z * 9 + x; - _tex_coords[v_index] = default_uv(x, z); - _depth[v_index] = 1.0f; - _vertices[v_index] = glm::vec3( - pos.x + UNITSIZE * x - , _minimum - , pos.z + UNITSIZE * z - ); - } - } + create_vertices(_minimum); if (info.ofsHeightMap) { f.seek(base_pos + info.ofsHeightMap); - if (_liquid_vertex_format == 0 || _liquid_vertex_format == 1) + if (_liquid_vertex_format == HEIGHT_DEPTH || _liquid_vertex_format == HEIGHT_UV) { for (int z = info.yOffset; z <= info.yOffset + info.height; ++z) { for (int x = info.xOffset; x <= info.xOffset + info.width; ++x) { - int index = z * 9 + x; - f.read(&_vertices[index].y, sizeof(float)); - _vertices[index].y = std::clamp(_vertices[index].y, _minimum, _maximum); + float h; + f.read(&h, sizeof(float)); + + _vertices[z * 9 + x].position.y = std::clamp(h, _minimum, _maximum); } } } - if (_liquid_vertex_format == 1) + if (_liquid_vertex_format == HEIGHT_UV) { for (int z = info.yOffset; z <= info.yOffset + info.height; ++z) { @@ -169,7 +146,7 @@ liquid_layer::liquid_layer(ChunkWater* chunk { mh2o_uv uv; f.read(&uv, sizeof(mh2o_uv)); - _tex_coords[z * 9 + x] = + _vertices[z * 9 + x].uv = { static_cast(uv.x) / 255.f , static_cast(uv.y) / 255.f }; @@ -177,7 +154,7 @@ liquid_layer::liquid_layer(ChunkWater* chunk } } - if (_liquid_vertex_format == 0 || _liquid_vertex_format == 2) + if (_liquid_vertex_format == HEIGHT_DEPTH || _liquid_vertex_format == DEPTH) { for (int z = info.yOffset; z <= info.yOffset + info.height; ++z) { @@ -185,13 +162,16 @@ liquid_layer::liquid_layer(ChunkWater* chunk { std::uint8_t depth; f.read(&depth, sizeof(std::uint8_t)); - _depth[z * 9 + x] = static_cast(depth) / 255.f; + _vertices[z * 9 + x].depth = static_cast(depth) / 255.f; } } } } - + changeLiquidID(_liquid_id); // to update the liquid type + + if (check_fatigue()) + _fatigue_enabled = true; } liquid_layer::liquid_layer(liquid_layer&& other) @@ -199,14 +179,15 @@ liquid_layer::liquid_layer(liquid_layer&& other) , _liquid_vertex_format(other._liquid_vertex_format) , _minimum(other._minimum) , _maximum(other._maximum) + // , _center(other._center) , _subchunks(other._subchunks) , _vertices(other._vertices) - , _depth(other._depth) - , _tex_coords(other._tex_coords) - , _indices_by_lod(other._indices_by_lod) + // , _indices_by_lod(other._indices_by_lod) + , _fatigue_enabled(other._fatigue_enabled) , pos(other.pos) , _chunk(other._chunk) { + // update liquid type and vertex format changeLiquidID(_liquid_id); } @@ -217,12 +198,12 @@ liquid_layer::liquid_layer(liquid_layer const& other) , _maximum(other._maximum) , _subchunks(other._subchunks) , _vertices(other._vertices) - , _depth(other._depth) - , _tex_coords(other._tex_coords) - , _indices_by_lod(other._indices_by_lod) + // , _indices_by_lod(other._indices_by_lod) + , _fatigue_enabled(other._fatigue_enabled) , pos(other.pos) , _chunk(other._chunk) { + // update liquid type and vertex format changeLiquidID(_liquid_id); } @@ -234,12 +215,12 @@ liquid_layer& liquid_layer::operator= (liquid_layer&& other) std::swap(_maximum, other._maximum); std::swap(_subchunks, other._subchunks); std::swap(_vertices, other._vertices); - std::swap(_depth, other._depth); - std::swap(_tex_coords, other._tex_coords); + std::swap(_fatigue_enabled, other._fatigue_enabled); std::swap(pos, other.pos); - std::swap(_indices_by_lod, other._indices_by_lod); + // std::swap(_indices_by_lod, other._indices_by_lod); std::swap(_chunk, other._chunk); + // update liquid type and vertex format changeLiquidID(_liquid_id); other.changeLiquidID(other._liquid_id); @@ -248,21 +229,38 @@ liquid_layer& liquid_layer::operator= (liquid_layer&& other) liquid_layer& liquid_layer::operator=(liquid_layer const& other) { - changeLiquidID(other._liquid_id); + _liquid_vertex_format = other._liquid_vertex_format; _minimum = other._minimum; _maximum = other._maximum; _subchunks = other._subchunks; _vertices = other._vertices; - _depth = other._depth; - _tex_coords = other._tex_coords; pos = other.pos; - _indices_by_lod = other._indices_by_lod; + // _indices_by_lod = other._indices_by_lod; + _fatigue_enabled = other._fatigue_enabled; _chunk = other._chunk; + // update liquid type and vertex format + changeLiquidID(other._liquid_id); return *this; } +void liquid_layer::create_vertices(float height) +{ + int index = 0; + for (int z = 0; z < 9; ++z) + { + const float posZ = pos.z + UNITSIZE * z; + for (int x = 0; x < 9; ++x, ++index) + { + _vertices[index] = liquid_vertex( glm::vec3(pos.x + UNITSIZE * x, height, posZ) + , default_uv(x, z) + , 1.f + ); + } + } +} + void liquid_layer::save(sExtendableArray& adt, int base_pos, int& info_pos, int& current_pos) const { int min_x = 9, min_z = 9, max_x = 0, max_z = 0; @@ -325,11 +323,10 @@ void liquid_layer::save(sExtendableArray& adt, int base_pos, int& info_pos, int& } } + int vertices_count = (info.width + 1) * (info.height + 1); info.ofsHeightMap = current_pos - base_pos; - int vertices_count = (info.width + 1) * (info.height + 1); - - if (_liquid_vertex_format == 0 || _liquid_vertex_format == 1) + if (_liquid_vertex_format == HEIGHT_DEPTH || _liquid_vertex_format == HEIGHT_UV) { adt.Extend(vertices_count * sizeof(float)); @@ -337,13 +334,18 @@ void liquid_layer::save(sExtendableArray& adt, int base_pos, int& info_pos, int& { for (int x = info.xOffset; x <= info.xOffset + info.width; ++x) { - memcpy(adt.GetPointer(current_pos), &_vertices[z * 9 + x].y, sizeof(float)); + memcpy(adt.GetPointer(current_pos), &_vertices[z * 9 + x].position.y, sizeof(float)); current_pos += sizeof(float); } } } + // no heightmap/depth data for fatigue chunks + else if (_fatigue_enabled) + { + info.ofsHeightMap = 0; + } - if (_liquid_vertex_format == 1) + if (_liquid_vertex_format == HEIGHT_UV) { adt.Extend(vertices_count * sizeof(mh2o_uv)); @@ -352,8 +354,8 @@ void liquid_layer::save(sExtendableArray& adt, int base_pos, int& info_pos, int& for (int x = info.xOffset; x <= info.xOffset + info.width; ++x) { mh2o_uv uv; - uv.x = static_cast(std::min(_tex_coords[z * 9 + x].x * 255.f, 65535.f)); - uv.y = static_cast(std::min(_tex_coords[z * 9 + x].y * 255.f, 65535.f)); + uv.x = static_cast(std::min(_vertices[z * 9 + x].uv.x * 255.f, 65535.f)); + uv.y = static_cast(std::min(_vertices[z * 9 + x].uv.y * 255.f, 65535.f)); memcpy(adt.GetPointer(current_pos), &uv, sizeof(mh2o_uv)); current_pos += sizeof(mh2o_uv); @@ -361,7 +363,7 @@ void liquid_layer::save(sExtendableArray& adt, int base_pos, int& info_pos, int& } } - if (_liquid_vertex_format == 0 || _liquid_vertex_format == 2) + if (_liquid_vertex_format == HEIGHT_DEPTH || (_liquid_vertex_format == DEPTH && !_fatigue_enabled)) { adt.Extend(vertices_count * sizeof(std::uint8_t)); @@ -369,7 +371,7 @@ void liquid_layer::save(sExtendableArray& adt, int base_pos, int& info_pos, int& { for (int x = info.xOffset; x <= info.xOffset + info.width; ++x) { - std::uint8_t depth = static_cast(std::min(_depth[z * 9 + x] * 255.0f, 255.f)); + std::uint8_t depth = static_cast(std::min(_vertices[z * 9 + x].depth * 255.0f, 255.f)); memcpy(adt.GetPointer(current_pos), &depth, sizeof(std::uint8_t)); current_pos += sizeof(std::uint8_t); } @@ -388,19 +390,26 @@ void liquid_layer::changeLiquidID(int id) { DBCFile::Record lLiquidTypeRow = gLiquidTypeDB.getByID(_liquid_id); - switch (lLiquidTypeRow.getInt(LiquidTypeDB::Type)) + _liquid_type = lLiquidTypeRow.getInt(LiquidTypeDB::Type); + + switch (_liquid_type) { - case 2: // magma - case 3: // slime - _liquid_vertex_format = 1; + case liquid_basic_types_magma: + case liquid_basic_types_slime: + _liquid_vertex_format = HEIGHT_UV; + break; + case liquid_basic_types_ocean: // ocean + _liquid_vertex_format = DEPTH; break; default: - _liquid_vertex_format = 0; + _liquid_vertex_format = HEIGHT_DEPTH; break; } } - catch (...) + catch (LiquidTypeDB::NotFound) { + assert(false); + LogError << "Liquid type id " << _liquid_type << " not found in LiquidType dbc" << std::endl; } } @@ -420,10 +429,10 @@ void liquid_layer::crop(MapChunk* chunk) { int water_index = 9 * z + x, terrain_index = 17 * z + x; - if (_vertices[water_index].y < chunk->mVertices[terrain_index].y - && _vertices[water_index + 1].y < chunk->mVertices[terrain_index + 1].y - && _vertices[water_index + 9].y < chunk->mVertices[terrain_index + 17].y - && _vertices[water_index + 10].y < chunk->mVertices[terrain_index + 18].y + if ( _vertices[water_index + 0].position.y < chunk->mVertices[terrain_index + 0].y + && _vertices[water_index + 1].position.y < chunk->mVertices[terrain_index + 1].y + && _vertices[water_index + 9].position.y < chunk->mVertices[terrain_index + 17].y + && _vertices[water_index + 10].position.y < chunk->mVertices[terrain_index + 18].y ) { setSubchunk(x, z, false); @@ -490,18 +499,18 @@ void liquid_layer::paintLiquid( glm::vec3 const& cursor_pos { for (int x = 0; x < 8; ++x) { - if (misc::getShortestDist(cursor_pos, _vertices[id], UNITSIZE) <= radius) + if (misc::getShortestDist(cursor_pos, _vertices[id].position, UNITSIZE) <= radius) { if (add) { for (int index : {id, id + 1, id + 9, id + 10}) { bool no_subchunk = !hasSubchunk(x, z); - bool in_range = misc::dist(cursor_pos, _vertices[index]) <= radius; + bool in_range = misc::dist(cursor_pos, _vertices[index].position) <= radius; if (no_subchunk || (in_range && override_height)) { - _vertices[index].y = misc::angledHeight(ref, _vertices[index], angle, orientation); + _vertices[index].position.y = misc::angledHeight(ref, _vertices[index].position, angle, orientation); } if (no_subchunk || in_range) { @@ -527,12 +536,12 @@ void liquid_layer::update_min_max() _maximum = std::numeric_limits::lowest(); int x = 0, z = 0; - for (glm::vec3& v : _vertices) + for (liquid_vertex& v : _vertices) { if (hasSubchunk(std::min(x, 7), std::min(z, 7))) { - _maximum = std::max(_maximum, v.y); - _minimum = std::min(_minimum, v.y); + _maximum = std::max(_maximum, v.position.y); + _minimum = std::min(_minimum, v.position.y); } if (++x == 9) @@ -543,15 +552,19 @@ void liquid_layer::update_min_max() } // lvf = 2 means the liquid height is 0, switch to lvf 0 when that's not the case - if (_liquid_vertex_format == 2 && (!misc::float_equals(0.f, _minimum) || !misc::float_equals(0.f, _maximum))) + if (_liquid_vertex_format == DEPTH && (!misc::float_equals(0.f, _minimum) || !misc::float_equals(0.f, _maximum))) { - _liquid_vertex_format = 0; + _liquid_vertex_format = HEIGHT_DEPTH; } // use lvf 2 when possible to save space - else if (_liquid_vertex_format == 0 && misc::float_equals(0.f, _minimum) && misc::float_equals(0.f, _maximum)) + else if (_liquid_vertex_format == HEIGHT_DEPTH && misc::float_equals(0.f, _minimum) && misc::float_equals(0.f, _maximum)) { - _liquid_vertex_format = 2; + _liquid_vertex_format = DEPTH; } + + _fatigue_enabled = check_fatigue(); + // recalc all atributes instead? + // _chunk->update_layers(); } void liquid_layer::copy_subchunk_height(int x, int z, liquid_layer const& from) @@ -560,7 +573,7 @@ void liquid_layer::copy_subchunk_height(int x, int z, liquid_layer const& from) for (int index : {id, id + 1, id + 9, id + 10}) { - _vertices[index].y = from._vertices[index].y; + _vertices[index].position.y = from._vertices[index].position.y; } setSubchunk(x, z, true); @@ -568,18 +581,93 @@ void liquid_layer::copy_subchunk_height(int x, int z, liquid_layer const& from) void liquid_layer::update_vertex_opacity(int x, int z, MapChunk* chunk, float factor) { - float diff = _vertices[z * 9 + x].y - chunk->mVertices[z * 17 + x].y; - _depth[z * 9 + x] = diff < 0.0f ? 0.0f : (std::min(1.0f, std::max(0.0f, (diff + 1.0f) * factor))); + const int index = z * 9 + x; + float diff = _vertices[index].position.y - chunk->mVertices[z * 17 + x].y; + _vertices[z * 9 + x].depth = diff < 0.0f ? 0.0f : (std::min(1.0f, std::max(0.0f, (diff + 1.0f) * factor))); } int liquid_layer::get_lod_level(glm::vec3 const& camera_pos) const { - auto const& center_vertex (_vertices[5 * 9 + 4]); - auto const dist ((center_vertex - camera_pos).length()); + glm::vec3 const& center_vertex (_vertices[5 * 9 + 4].position); + // this doesn't look like it's using the right length function... + // auto const dist ((center_vertex - camera_pos).length()); + float const dist = misc::dist(center_vertex, camera_pos); return dist < 1000.f ? 0 : dist < 2000.f ? 1 : dist < 4000.f ? 2 : 3; } +// if ocean and all subchunks are at max depth +bool liquid_layer::check_fatigue() const +{ + // only oceans have fatigue + if (_liquid_type != liquid_basic_types_ocean) + { + return false; + } + + for (int z = 0; z < 8; ++z) + { + for (int x = 0; x < 8; ++x) + { + if (!(hasSubchunk(x, z) && subchunk_at_max_depth(x, z))) + { + return false; + } + } + } + + return true; +} + +void liquid_layer::update_attributes(MH2O_Attributes& attributes) +{ + if (check_fatigue()) + { + attributes.fishable = 0xFFFFFFFFFFFFFFFF; + attributes.fatigue = 0xFFFFFFFFFFFFFFFF; + + _fatigue_enabled = true; + } + else + { + _fatigue_enabled = false; + for (int z = 0; z < 8; ++z) + { + for (int x = 0; x < 8; ++x) + { + if (hasSubchunk(x, z)) + { + // todo : find out when fishable isn't set. maybe lava/slime or very shallow water ? + // Most likely when subchunk is entirely above terrain. + misc::set_bit(attributes.fishable, x, z, true); + + // only oceans have fatigue + // warning: not used by TrinityCore + if (_liquid_type == liquid_basic_types_ocean && subchunk_at_max_depth(x, z)) + { + misc::set_bit(attributes.fatigue, x, z, true); + } + } + } + } + } +} + +bool liquid_layer::subchunk_at_max_depth(int x, int z) const +{ + for (int id_z = z; id_z <= z + 1; ++id_z) + { + for (int id_x = x; id_x <= x + 1; ++id_x) + { + if (_vertices[id_x + 9 * id_z].depth < 1.f) + { + return false; + } + } + } + + return true; +} diff --git a/src/noggit/liquid_layer.hpp b/src/noggit/liquid_layer.hpp index a50674ac..aafc1437 100755 --- a/src/noggit/liquid_layer.hpp +++ b/src/noggit/liquid_layer.hpp @@ -21,6 +21,14 @@ enum LiquidLayerUpdateFlags ll_FLAGS = 0x10 }; +enum LiquidVertexFormats +{ + HEIGHT_DEPTH = 0, + HEIGHT_UV = 1, + DEPTH = 2, + HEIGHT_DEPTH_UV = 3 +}; + namespace BlizzardArchive { class ClientFile; @@ -29,6 +37,15 @@ namespace BlizzardArchive // handle liquids like oceans, lakes, rivers, slime, magma class liquid_layer { +struct liquid_vertex +{ + glm::vec3 position; + glm::vec2 uv; + float depth; + + liquid_vertex() = default; + liquid_vertex(glm::vec3 const& pos, glm::vec2 const& uv, float depth) : position(pos), uv(uv), depth(depth) {} +}; public: liquid_layer() = delete; @@ -44,19 +61,23 @@ public: void save(sExtendableArray& adt, int base_pos, int& info_pos, int& current_pos) const; + void update_attributes(MH2O_Attributes& attributes); void changeLiquidID(int id); void crop(MapChunk* chunk); void update_opacity(MapChunk* chunk, float factor); - std::array& getVertices() { return _vertices; }; - std::array& getDepth() { return _depth; }; - std::array& getTexCoords() { return _tex_coords; }; + std::array& getVertices() { return _vertices; }; + // std::array& getDepth() { return _depth; }; + // std::array& getTexCoords() { return _tex_coords; }; float min() const { return _minimum; } float max() const { return _maximum; } int liquidID() const { return _liquid_id; } + // used for fatigue calculation + bool subchunk_at_max_depth(int x, int z) const; + bool hasSubchunk(int x, int z, int size = 1) const; void setSubchunk(int x, int z, bool water); @@ -82,23 +103,34 @@ public: ChunkWater* getChunk() { return _chunk; }; + bool has_fatigue() const { return _fatigue_enabled; } private: + void create_vertices(float height); + void update_min_max(); void update_vertex_opacity(int x, int z, MapChunk* chunk, float factor); int get_lod_level(glm::vec3 const& camera_pos) const; - void set_lod_level(int lod_level); + // void set_lod_level(int lod_level); + + bool check_fatigue() const; + // gets enabled when all subchunks are at max depth and type is ocean : check_fatigue() + bool _fatigue_enabled = false; int _liquid_id; + int _liquid_type; int _liquid_vertex_format; float _minimum; float _maximum; std::uint64_t _subchunks; - std::array _vertices; - std::array _depth; - std::array _tex_coords; + // std::array _vertices; + // std::array _depth; + // std::array _tex_coords; - std::map> _indices_by_lod; + // std::vector _vertices; + std::array _vertices; + + // std::map> _indices_by_lod; private: diff --git a/src/noggit/rendering/LiquidRender.cpp b/src/noggit/rendering/LiquidRender.cpp index 823a1ad3..507368c0 100755 --- a/src/noggit/rendering/LiquidRender.cpp +++ b/src/noggit/rendering/LiquidRender.cpp @@ -161,16 +161,16 @@ void LiquidRender::updateLayerData(LiquidTextureManager* tex_manager) // fill vertex data auto& vertices = layer.getVertices(); - auto& tex_coords = layer.getTexCoords(); - auto& depth = layer.getDepth(); + // auto& tex_coords = layer.getTexCoords(); + // auto& depth = layer.getDepth(); for (int z_v = 0; z_v < 9; ++z_v) { for (int x_v = 0; x_v < 9; ++x_v) { const unsigned v_index = z_v * 9 + x_v; - glm::vec2& tex_coord = tex_coords[v_index]; - layer_params.vertex_data[n_chunks][v_index] = glm::vec4(vertices[v_index].y, depth[v_index], tex_coord.x, tex_coord.y); + glm::vec2& tex_coord = vertices[v_index].uv; + layer_params.vertex_data[n_chunks][v_index] = glm::vec4(vertices[v_index].position.y, vertices[v_index].depth, tex_coord.x, tex_coord.y); } } diff --git a/src/noggit/scripting/script_chunk.cpp b/src/noggit/scripting/script_chunk.cpp index 405d0d29..e40cdb36 100755 --- a/src/noggit/scripting/script_chunk.cpp +++ b/src/noggit/scripting/script_chunk.cpp @@ -151,37 +151,14 @@ namespace Noggit return vert(state(), _chunk, index); } - bool chunk::has_render_flags() + MH2O_Attributes& chunk::getAttributes() { - return _chunk->liquid_chunk()->Render.has_value(); - } - - MH2O_Render chunk::getRenderOrDefault() - { - std::optional render = _chunk->liquid_chunk()->Render; - if (render.has_value()) - { - return render.value(); - } - else - { - return { 0xFFFFFFFFFFFFFFFF,1 }; - } - } - - MH2O_Render& chunk::getOrCreateRender() - { - std::optional& render = _chunk->liquid_chunk()->Render; - if (!render.has_value()) - { - render.emplace(); - } - return render.value(); + return _chunk->liquid_chunk()->getAttributes(); } void chunk::set_deep_flag(std::uint32_t low, std::uint32_t high) { - getOrCreateRender().fatigue = std::uint64_t(low) | (std::uint64_t(high) << 32); + _chunk->liquid_chunk()->getAttributes().fatigue = std::uint64_t(low) | (std::uint64_t(high) << 32); } void chunk::set_deep_flag_1(std::uint32_t low) @@ -191,17 +168,17 @@ namespace Noggit std::uint32_t chunk::get_deep_flag() { - return static_cast(getRenderOrDefault().fatigue); + return static_cast(getAttributes().fatigue); } std::uint32_t chunk::get_deep_flag_high() { - return static_cast(getRenderOrDefault().fatigue >> 32); + return static_cast(getAttributes().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); + _chunk->liquid_chunk()->getAttributes().fishable = std::uint64_t(low) | (std::uint64_t(high) << 32); } void chunk::set_fishable_flag_1(std::uint32_t low) @@ -211,11 +188,11 @@ namespace Noggit std::uint32_t chunk::get_fishable_flag() { - return static_cast(getRenderOrDefault().fishable); + return static_cast(getAttributes().fishable); } std::uint32_t chunk::get_fishable_flag_high() { - return static_cast(getRenderOrDefault().fishable >> 32); + return static_cast(getAttributes().fishable >> 32); } std::shared_ptr chunk::to_selection() diff --git a/src/noggit/scripting/script_chunk.hpp b/src/noggit/scripting/script_chunk.hpp index 6ad283f6..9741aeeb 100755 --- a/src/noggit/scripting/script_chunk.hpp +++ b/src/noggit/scripting/script_chunk.hpp @@ -43,7 +43,6 @@ namespace Noggit 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(); @@ -52,8 +51,7 @@ namespace Noggit vert get_vert(int index); std::shared_ptr to_selection(); private: - MH2O_Render getRenderOrDefault(); - MH2O_Render& getOrCreateRender(); + MH2O_Attributes& getAttributes(); MapChunk* _chunk; friend class selection; }; diff --git a/src/noggit/ui/Water.cpp b/src/noggit/ui/Water.cpp index 87f58e6f..5bbd58bc 100755 --- a/src/noggit/ui/Water.cpp +++ b/src/noggit/ui/Water.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -63,7 +64,8 @@ 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) + if (liquid_id == LIQUID_WMO_Water || liquid_id == LIQUID_WMO_Ocean || liquid_id == LIQUID_WMO_Water_Interior + || liquid_id == LIQUID_WMO_Magma || liquid_id == LIQUID_WMO_Slime) continue; std::stringstream ss; @@ -81,13 +83,14 @@ namespace Noggit if (_opacity_mode == custom_opacity) return; + // other liquid types shouldn't use opacity(depth) int liquid_type = LiquidTypeDB::getLiquidType(_liquid_id); - if (liquid_type == 1) // ocean + if (liquid_type == liquid_basic_types_ocean) // ocean { ocean_button->setChecked(true); _opacity_mode = ocean_opacity; } - else + else // water. opacity doesn't matter for lava/slim { river_button->setChecked(true); _opacity_mode = river_opacity; diff --git a/src/noggit/wmo_liquid.cpp b/src/noggit/wmo_liquid.cpp index f7b54d3b..52180c19 100755 --- a/src/noggit/wmo_liquid.cpp +++ b/src/noggit/wmo_liquid.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -14,30 +15,6 @@ namespace { - - enum liquid_basic_types - { - liquid_basic_types_water = 0, - liquid_basic_types_ocean = 1, - liquid_basic_types_magma = 2, - liquid_basic_types_slime = 3, - - liquid_basic_types_MASK = 3, - }; - enum liquid_types - { - LIQUID_WMO_Water = 13, - LIQUID_WMO_Ocean = 14, - LIQUID_Green_Lava = 15, - LIQUID_WMO_Magma = 19, - LIQUID_WMO_Slime = 20, - - LIQUID_END_BASIC_LIQUIDS = 20, - LIQUID_FIRST_NONBASIC_LIQUID_TYPE = 21, - - LIQUID_NAXX_SLIME = 21, - }; - liquid_types to_wmo_liquid(int x, bool ocean) { liquid_basic_types const basic(static_cast(x & liquid_basic_types_MASK)); @@ -117,8 +94,8 @@ wmo_liquid::wmo_liquid(wmo_liquid const& other) int wmo_liquid::initGeometry(BlizzardArchive::ClientFile* f) { - LiquidVertex const* map = reinterpret_cast(f->getPointer()); - SMOLTile const* tiles = reinterpret_cast(f->getPointer() + (xtiles + 1)*(ytiles + 1) * sizeof(LiquidVertex)); + WmoLiquidVertex const* map = reinterpret_cast(f->getPointer()); + SMOLTile const* tiles = reinterpret_cast(f->getPointer() + (xtiles + 1)*(ytiles + 1) * sizeof(WmoLiquidVertex)); int last_liquid_id = 0; // generate vertices diff --git a/src/noggit/wmo_liquid.hpp b/src/noggit/wmo_liquid.hpp index b1c2c8fb..5df2fc6e 100755 --- a/src/noggit/wmo_liquid.hpp +++ b/src/noggit/wmo_liquid.hpp @@ -88,7 +88,7 @@ struct SMOMVert std::int16_t t; }; -struct LiquidVertex { +struct WmoLiquidVertex { union { SMOWVert water_vertex;