update liquid attributes and fatigue auto updates

This commit is contained in:
T1ti
2024-08-26 02:54:57 +02:00
parent 15bb0f7fba
commit 56aab6b7c0
13 changed files with 392 additions and 209 deletions

View File

@@ -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<mclq>& 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<mclq>& 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<mclq>& 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<std::uint32_t>(_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<char*>(&Render.value()));
current_pos += sizeof(MH2O_Render);
header.ofsAttributes = current_pos - base_pos;
adt.Insert(current_pos, sizeof(MH2O_Attributes), reinterpret_cast<char*>(&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<float>::max();
vmax.y = std::numeric_limits<float>::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<float> 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();

View File

@@ -70,10 +70,13 @@ public:
MapChunk* getChunk() { return _chunk; };
TileWater* getWaterTile() { return _water_tile; };
std::optional<MH2O_Render> 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<liquid_layer> _layers;
MapChunk* _chunk;

View File

@@ -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

View File

@@ -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 << "<br><b>liquid type</b>: " << liquid.liquidID() << " (\"" << gLiquidTypeDB.getLiquidName(liquid.liquidID()) << "\")"
<< "<br><b>liquid flags</b>: "
select_info << "<br><b>Liquid type</b>: " << liquid.liquidID() << " (\"" << gLiquidTypeDB.getLiquidName(liquid.liquidID()) << "\")"
<< "<br><b>liquid flags(center)</b>: "
// 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() ? "<br><b>entire chunk has fatigue!</b>" : "");
}
}
else

View File

@@ -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;
};

View File

@@ -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<float>(v.magma.x) / 255.f, static_cast<float>(v.magma.y) / 255.f);
lv.depth = 1.f;
lv.uv = { static_cast<float>(v.magma.x) / 255.f, static_cast<float>(v.magma.y) / 255.f };
}
else
{
_depth[v_index] = static_cast<float>(v.water.depth) / 255.f;
_tex_coords[v_index] = default_uv(x, z);
lv.depth = static_cast<float>(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<float>(uv.x) / 255.f
, static_cast<float>(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<float>(depth) / 255.f;
_vertices[z * 9 + x].depth = static_cast<float>(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<char>(current_pos), &_vertices[z * 9 + x].y, sizeof(float));
memcpy(adt.GetPointer<char>(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::uint16_t>(std::min(_tex_coords[z * 9 + x].x * 255.f, 65535.f));
uv.y = static_cast<std::uint16_t>(std::min(_tex_coords[z * 9 + x].y * 255.f, 65535.f));
uv.x = static_cast<std::uint16_t>(std::min(_vertices[z * 9 + x].uv.x * 255.f, 65535.f));
uv.y = static_cast<std::uint16_t>(std::min(_vertices[z * 9 + x].uv.y * 255.f, 65535.f));
memcpy(adt.GetPointer<char>(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::uint8_t>(std::min(_depth[z * 9 + x] * 255.0f, 255.f));
std::uint8_t depth = static_cast<std::uint8_t>(std::min(_vertices[z * 9 + x].depth * 255.0f, 255.f));
memcpy(adt.GetPointer<char>(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<float>::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;
}

View File

@@ -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<glm::vec3, 9 * 9>& getVertices() { return _vertices; };
std::array<float, 9 * 9>& getDepth() { return _depth; };
std::array<glm::vec2, 9 * 9>& getTexCoords() { return _tex_coords; };
std::array<liquid_vertex, 9 * 9>& getVertices() { return _vertices; };
// std::array<float, 9 * 9>& getDepth() { return _depth; };
// std::array<glm::vec2, 9 * 9>& 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<glm::vec3, 9 * 9> _vertices;
std::array<float, 9 * 9> _depth;
std::array<glm::vec2, 9 * 9> _tex_coords;
// std::array<glm::vec3, 9 * 9> _vertices;
// std::array<float, 9 * 9> _depth;
// std::array<glm::vec2, 9 * 9> _tex_coords;
std::map<int, std::vector<std::uint16_t>> _indices_by_lod;
// std::vector<liquid_vertex> _vertices;
std::array<liquid_vertex, 9 * 9> _vertices;
// std::map<int, std::vector<liquid_indice>> _indices_by_lod;
private:

View File

@@ -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);
}
}

View File

@@ -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<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();
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<std::uint32_t>(getRenderOrDefault().fatigue);
return static_cast<std::uint32_t>(getAttributes().fatigue);
}
std::uint32_t chunk::get_deep_flag_high()
{
return static_cast<std::uint32_t>(getRenderOrDefault().fatigue >> 32);
return static_cast<std::uint32_t>(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<std::uint32_t>(getRenderOrDefault().fishable);
return static_cast<std::uint32_t>(getAttributes().fishable);
}
std::uint32_t chunk::get_fishable_flag_high()
{
return static_cast<std::uint32_t>(getRenderOrDefault().fishable >> 32);
return static_cast<std::uint32_t>(getAttributes().fishable >> 32);
}
std::shared_ptr<selection> chunk::to_selection()

View File

@@ -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<selection> to_selection();
private:
MH2O_Render getRenderOrDefault();
MH2O_Render& getOrCreateRender();
MH2O_Attributes& getAttributes();
MapChunk* _chunk;
friend class selection;
};

View File

@@ -6,6 +6,7 @@
#include <noggit/World.h>
#include <noggit/ui/pushbutton.hpp>
#include <noggit/ui/Water.h>
#include <noggit/MapHeaders.h>
#include <util/qt/overload.hpp>
#include <QtWidgets/QButtonGroup>
@@ -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;

View File

@@ -5,6 +5,7 @@
#include <noggit/World.h>
#include <noggit/wmo_liquid.hpp>
#include <noggit/application/NoggitApplication.hpp>
#include <noggit/MapHeaders.h>
#include <opengl/context.hpp>
#include <opengl/context.inl>
#include <opengl/shader.hpp>
@@ -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<liquid_basic_types>(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<LiquidVertex const*>(f->getPointer());
SMOLTile const* tiles = reinterpret_cast<SMOLTile const*>(f->getPointer() + (xtiles + 1)*(ytiles + 1) * sizeof(LiquidVertex));
WmoLiquidVertex const* map = reinterpret_cast<WmoLiquidVertex const*>(f->getPointer());
SMOLTile const* tiles = reinterpret_cast<SMOLTile const*>(f->getPointer() + (xtiles + 1)*(ytiles + 1) * sizeof(WmoLiquidVertex));
int last_liquid_id = 0;
// generate vertices

View File

@@ -88,7 +88,7 @@ struct SMOMVert
std::int16_t t;
};
struct LiquidVertex {
struct WmoLiquidVertex {
union
{
SMOWVert water_vertex;