adspartan : implement option to save liquids as MCLQ
mapchunk: store pointer to the header when saving2893aadcaff7e5f8396fnoggit3 : extendable array: prevent use after extend https://github.com/wowdev/noggit3/pull/91
This commit is contained in:
@@ -6,6 +6,9 @@
|
||||
#include <noggit/MapChunk.h>
|
||||
#include <noggit/Misc.h>
|
||||
#include <ClientFile.hpp>
|
||||
#include <util/sExtendableArray.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
ChunkWater::ChunkWater(MapChunk* chunk, TileWater* water_tile, float x, float z, bool use_mclq_green_lava)
|
||||
: xbase(x)
|
||||
@@ -163,6 +166,62 @@ void ChunkWater::save(util::sExtendableArray& adt, int base_pos, int& header_pos
|
||||
header_pos += sizeof(MH2O_Header);
|
||||
}
|
||||
|
||||
void ChunkWater::save_mclq(util::sExtendableArray& adt, int mcnk_pos, int& current_pos)
|
||||
{
|
||||
// remove empty layers
|
||||
cleanup();
|
||||
update_attributes();
|
||||
|
||||
if (hasData(0))
|
||||
{
|
||||
adt.Extend(sizeof(mclq) * _layers.size() + 8);
|
||||
// size seems to be 0 in vanilla adts in the mclq chunk's header and set right in the mcnk header (layer_size * n_layer + 8)
|
||||
SetChunkHeader(adt, current_pos, 'MCLQ', 0);
|
||||
|
||||
current_pos += 8;
|
||||
|
||||
// it's possible to merge layers when they don't overlap (liquids using the same vertice, but at different height)
|
||||
// layer ordering seems to matter, having a lava layer then a river layer causes the lava layer to not render ingame
|
||||
// sorting order seems to be dependant on the flag ordering in the mcnk's header
|
||||
std::vector<std::pair<mclq, int>> mclq_layers;
|
||||
|
||||
for (liquid_layer const& layer : _layers)
|
||||
{
|
||||
switch (layer.mclq_liquid_type())
|
||||
{
|
||||
case 6: // lava
|
||||
adt.GetPointer<MapChunkHeader>(mcnk_pos + 8)->flags.flags.lq_magma = 1;
|
||||
break;
|
||||
case 3: // slime
|
||||
adt.GetPointer<MapChunkHeader>(mcnk_pos + 8)->flags.flags.lq_slime = 1;
|
||||
break;
|
||||
case 1: // ocean
|
||||
adt.GetPointer<MapChunkHeader>(mcnk_pos + 8)->flags.flags.lq_ocean = 1;
|
||||
break;
|
||||
default: // river
|
||||
adt.GetPointer<MapChunkHeader>(mcnk_pos + 8)->flags.flags.lq_river = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
mclq_layers.push_back({ layer.to_mclq(attributes), layer.mclq_flag_ordering() });
|
||||
}
|
||||
|
||||
auto cmp = [](std::pair<mclq, int> const& a, std::pair<mclq, int> const& b)
|
||||
{
|
||||
return a.second < b.second;
|
||||
};
|
||||
|
||||
// sort the layers by flag order
|
||||
std::sort(mclq_layers.begin(), mclq_layers.end(), cmp);
|
||||
|
||||
for (auto const& mclq_layer : mclq_layers)
|
||||
{
|
||||
std::memcpy(adt.GetPointer<char>(current_pos).get(), &mclq_layer.first, sizeof(mclq));
|
||||
current_pos += sizeof(mclq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ChunkWater::autoGen(MapChunk *chunk, float factor)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
void from_mclq(std::vector<mclq>& layers);
|
||||
void fromFile(BlizzardArchive::ClientFile& f, size_t basePos);
|
||||
void save(util::sExtendableArray& adt, int base_pos, int& header_pos, int& current_pos);
|
||||
void save_mclq(util::sExtendableArray& adt, int mcnk_pos, int& current_pos);
|
||||
|
||||
bool is_visible ( const float& cull_distance
|
||||
, const math::frustum& frustum
|
||||
@@ -76,6 +77,8 @@ public:
|
||||
|
||||
float xbase, zbase;
|
||||
|
||||
int layer_count() const { return _layers.size(); }
|
||||
|
||||
private:
|
||||
MH2O_Attributes attributes;
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ MapChunk::MapChunk(MapTile* maintile, BlizzardArchive::ClientFile* f, bool bigAl
|
||||
|
||||
f->read(&tmp_chunk_header, sizeof(MapChunkHeader));
|
||||
|
||||
header_flags.value = tmp_chunk_header.flags;
|
||||
header_flags.value = tmp_chunk_header.flags.value;
|
||||
areaID = tmp_chunk_header.areaid;
|
||||
|
||||
zbase = tmp_chunk_header.zpos;
|
||||
@@ -1432,7 +1432,8 @@ void MapChunk::save(util::sExtendableArray& lADTFile
|
||||
, int& lMCIN_Position
|
||||
, std::map<std::string, int> &lTextures
|
||||
, std::vector<WMOInstance*> &lObjectInstances
|
||||
, std::vector<ModelInstance*>& lModelInstances)
|
||||
, std::vector<ModelInstance*>& lModelInstances
|
||||
, bool use_mclq_liquids)
|
||||
{
|
||||
int lID;
|
||||
int lMCNK_Size = 0x80;
|
||||
@@ -1445,9 +1446,9 @@ void MapChunk::save(util::sExtendableArray& lADTFile
|
||||
// lADTFile.Insert(lCurrentPosition + 8, 0x80, reinterpret_cast<char*>(&(header)));
|
||||
auto const lMCNK_header = lADTFile.GetPointer<MapChunkHeader>(lCurrentPosition + 8);
|
||||
|
||||
header_flags.flags.do_not_fix_alpha_map = 1;
|
||||
header_flags.flags.do_not_fix_alpha_map = use_mclq_liquids ? 0 : 1;
|
||||
|
||||
lMCNK_header->flags = header_flags.value;
|
||||
lMCNK_header->flags = header_flags;
|
||||
lMCNK_header->ix = px;
|
||||
lMCNK_header->iy = py;
|
||||
lMCNK_header->zpos = zbase * -1.0f + ZEROPOINT;
|
||||
@@ -1690,7 +1691,8 @@ void MapChunk::save(util::sExtendableArray& lADTFile
|
||||
// MCSH
|
||||
if (has_shadows())
|
||||
{
|
||||
header_flags.flags.has_mcsh = 1;
|
||||
// header_flags.flags.has_mcsh = 1;
|
||||
lMCNK_header->flags.flags.has_mcsh = 1;
|
||||
|
||||
int lMCSH_Size = 0x200;
|
||||
lADTFile.Extend(8 + lMCSH_Size);
|
||||
@@ -1709,7 +1711,8 @@ void MapChunk::save(util::sExtendableArray& lADTFile
|
||||
}
|
||||
else
|
||||
{
|
||||
header_flags.flags.has_mcsh = 0;
|
||||
lMCNK_header->flags.flags.has_mcsh = 0;
|
||||
// header_flags.flags.has_mcsh = 0;
|
||||
header_ptr->ofsShadow = 0;
|
||||
header_ptr->sizeShadow = 0;
|
||||
}
|
||||
@@ -1731,10 +1734,42 @@ void MapChunk::save(util::sExtendableArray& lADTFile
|
||||
|
||||
lCurrentPosition += 8 + lMCAL_Size;
|
||||
lMCNK_Size += 8 + lMCAL_Size;
|
||||
// }
|
||||
|
||||
//! Don't write anything MCLQ related anymore...
|
||||
if (use_mclq_liquids)
|
||||
{
|
||||
auto liquids = liquid_chunk();
|
||||
|
||||
if (liquids && liquids->layer_count() > 0)
|
||||
{
|
||||
int liquids_size = 8 + liquids->layer_count() * sizeof(mclq);
|
||||
|
||||
lMCNK_Size += liquids_size;
|
||||
|
||||
header_ptr->sizeLiquid = liquids_size;
|
||||
header_ptr->ofsLiquid = lCurrentPosition - lMCNK_Position;
|
||||
|
||||
// current position updated inside
|
||||
liquids->save_mclq(lADTFile, lMCNK_Position, lCurrentPosition);
|
||||
}
|
||||
// no liquid, vanilla adt still have an empty chunk
|
||||
else
|
||||
{
|
||||
lADTFile.Extend(8);
|
||||
// size seems to be 0 in vanilla adts in the mclq chunk's header and set right in the mcnk header (layer_size * n_layer + 8)
|
||||
SetChunkHeader(lADTFile, lCurrentPosition, 'MCLQ', 0);
|
||||
|
||||
header_ptr->sizeLiquid = 8;
|
||||
header_ptr->ofsLiquid = lCurrentPosition - lMCNK_Position;
|
||||
|
||||
// When saving a tile that had MLCQ, also remove flags in MCNK
|
||||
// Client sees the flag and loads random data as if it were MCLQ, which we don<6F>t save.
|
||||
// clear MCLQ liquid flags (0x4, 0x8, 0x10, 0x20)
|
||||
header_ptr->flags.value &= 0xFFFFFFC3;
|
||||
|
||||
lCurrentPosition += 8;
|
||||
lMCNK_Size += 8;
|
||||
}
|
||||
}
|
||||
|
||||
// MCSE
|
||||
int lMCSE_Size = sizeof(ENTRY_MCSE) * sound_emitters.size();
|
||||
|
||||
@@ -201,7 +201,9 @@ public:
|
||||
, int &lMCIN_Position
|
||||
, std::map<std::string, int> &lTextures
|
||||
, std::vector<WMOInstance*> &lObjectInstances
|
||||
, std::vector<ModelInstance*>& lModelInstances);
|
||||
, std::vector<ModelInstance*>& lModelInstances
|
||||
, bool use_mclq_liquids
|
||||
);
|
||||
|
||||
// fix the gaps with the chunk to the left
|
||||
bool fixGapLeft(const MapChunk* chunk);
|
||||
|
||||
@@ -121,7 +121,7 @@ struct ENTRY_MODF
|
||||
};
|
||||
|
||||
struct MapChunkHeader {
|
||||
uint32_t flags = 0;
|
||||
mcnk_flags flags;
|
||||
uint32_t ix = 0;
|
||||
uint32_t iy = 0;
|
||||
uint32_t nLayers = 0;
|
||||
|
||||
@@ -529,6 +529,24 @@ void MapTile::getVertexInternal(float x, float z, glm::vec3* v)
|
||||
/// --- Only saving related below this line. --------------------------
|
||||
|
||||
void MapTile::saveTile(World* world)
|
||||
{
|
||||
// if we want to save a duplicate with mclq in a separate folder
|
||||
/*
|
||||
save(world, false);
|
||||
|
||||
if (NoggitSettings.value("use_mclq_liquids_export", false).toBool())
|
||||
{
|
||||
save(world, true);
|
||||
}
|
||||
*/
|
||||
|
||||
QSettings settings;
|
||||
bool use_mclq = settings.value("use_mclq_liquids_export", false).toBool();
|
||||
|
||||
save(world, use_mclq);
|
||||
}
|
||||
|
||||
void MapTile::save(World* world, bool save_using_mclq_liquids)
|
||||
{
|
||||
Log << "Saving ADT \"" << _file_key.stringRepr() << "\"." << std::endl;
|
||||
|
||||
@@ -848,14 +866,17 @@ void MapTile::saveTile(World* world)
|
||||
lCurrentPosition += 8 + lMODF_Size;
|
||||
|
||||
//MH2O
|
||||
Water.saveToFile(lADTFile, lMHDR_Position, lCurrentPosition);
|
||||
if (!save_using_mclq_liquids)
|
||||
{
|
||||
Water.saveToFile(lADTFile, lMHDR_Position, lCurrentPosition);
|
||||
}
|
||||
|
||||
// MCNK
|
||||
for (int y = 0; y < 16; ++y)
|
||||
{
|
||||
for (int x = 0; x < 16; ++x)
|
||||
{
|
||||
mChunks[y][x]->save(lADTFile, lCurrentPosition, lMCIN_Position, lTextures, lObjectInstances, lModelInstances);
|
||||
mChunks[y][x]->save(lADTFile, lCurrentPosition, lMCIN_Position, lTextures, lObjectInstances, lModelInstances, save_using_mclq_liquids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -908,6 +929,17 @@ void MapTile::saveTile(World* world)
|
||||
// begin with.
|
||||
f.setBuffer(lADTFile.data_up_to(lCurrentPosition)); // cleaning unused nulls at the end of file
|
||||
f.save();
|
||||
|
||||
// adspartan's way, save MCLQ files separately
|
||||
/*
|
||||
if (save_using_mclq_liquids)
|
||||
{
|
||||
f.save_file_to_folder(NoggitSettings.value("project/mclq_liquids_path").toString().toStdString());
|
||||
}
|
||||
else
|
||||
{
|
||||
f.save();
|
||||
}*/
|
||||
}
|
||||
|
||||
lObjectInstances.clear();
|
||||
|
||||
@@ -107,8 +107,13 @@ public:
|
||||
bool GetVertex(float x, float z, glm::vec3 *V);
|
||||
void getVertexInternal(float x, float z, glm::vec3* v);
|
||||
|
||||
void saveTile(World*);
|
||||
void CropWater();
|
||||
void saveTile(World* world);
|
||||
|
||||
private:
|
||||
void save(World* world, bool save_using_mclq_liquids);
|
||||
|
||||
public:
|
||||
|
||||
bool isTile(int pX, int pZ);
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ liquid_layer::liquid_layer(ChunkWater* chunk
|
||||
update_min_max();
|
||||
}
|
||||
|
||||
liquid_layer::liquid_layer(liquid_layer&& other)
|
||||
liquid_layer::liquid_layer(liquid_layer&& other) noexcept
|
||||
: _liquid_id(other._liquid_id)
|
||||
, _liquid_vertex_format(other._liquid_vertex_format)
|
||||
, _minimum(other._minimum)
|
||||
@@ -207,7 +207,7 @@ liquid_layer::liquid_layer(liquid_layer const& other)
|
||||
changeLiquidID(_liquid_id);
|
||||
}
|
||||
|
||||
liquid_layer& liquid_layer::operator= (liquid_layer&& other)
|
||||
liquid_layer& liquid_layer::operator= (liquid_layer&& other) noexcept
|
||||
{
|
||||
std::swap(_liquid_id, other._liquid_id);
|
||||
std::swap(_liquid_vertex_format, other._liquid_vertex_format);
|
||||
@@ -395,14 +395,20 @@ void liquid_layer::changeLiquidID(int id)
|
||||
switch (_liquid_type)
|
||||
{
|
||||
case liquid_basic_types_magma:
|
||||
_mclq_liquid_type = 6;
|
||||
_liquid_vertex_format = 1;
|
||||
break;
|
||||
case liquid_basic_types_slime:
|
||||
_mclq_liquid_type = 3;
|
||||
_liquid_vertex_format = HEIGHT_UV;
|
||||
break;
|
||||
case liquid_basic_types_ocean: // ocean
|
||||
_liquid_vertex_format = DEPTH;
|
||||
_mclq_liquid_type = 1;
|
||||
break;
|
||||
default:
|
||||
default: // river
|
||||
_liquid_vertex_format = HEIGHT_DEPTH;
|
||||
_mclq_liquid_type = 4;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -650,6 +656,62 @@ bool liquid_layer::check_fatigue() const
|
||||
return true;
|
||||
}
|
||||
|
||||
mclq liquid_layer::to_mclq(MH2O_Attributes& attributes) const
|
||||
{
|
||||
mclq mclq_data;
|
||||
|
||||
mclq_data.min_height = _minimum;
|
||||
mclq_data.max_height = _maximum;
|
||||
|
||||
for (int i = 0; i < 8 * 8; ++i)
|
||||
{
|
||||
if (hasSubchunk(i % 8, i / 8))
|
||||
{
|
||||
mclq_data.tiles[i].liquid_type = _mclq_liquid_type & 0x7;
|
||||
mclq_data.tiles[i].dont_render = 0;
|
||||
mclq_data.tiles[i].fishable = (attributes.fishable >> i) & 1;
|
||||
mclq_data.tiles[i].fatigue = (attributes.fatigue >> i) & 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mclq_data.tiles[i].liquid_type = 7;
|
||||
mclq_data.tiles[i].dont_render = 1;
|
||||
mclq_data.tiles[i].fishable = 0;
|
||||
mclq_data.tiles[i].fatigue = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 9 * 9; ++i)
|
||||
{
|
||||
mclq_data.vertices[i].height = _vertices[i].position.y;
|
||||
|
||||
// magma and slime
|
||||
if (_liquid_type == 2 || _liquid_type == 3)
|
||||
{
|
||||
mclq_data.vertices[i].magma.x = static_cast<std::uint16_t>(std::min(_vertices[i].uv.x * 255.f, 65535.f));
|
||||
mclq_data.vertices[i].magma.y = static_cast<std::uint16_t>(std::min(_vertices[i].uv.y * 255.f, 65535.f));
|
||||
}
|
||||
else
|
||||
{
|
||||
mclq_data.vertices[i].water.depth = static_cast<std::uint8_t>(std::clamp(_vertices[i].depth * 255.f, 0.f, 255.f));
|
||||
}
|
||||
}
|
||||
|
||||
return mclq_data;
|
||||
}
|
||||
|
||||
int liquid_layer::mclq_flag_ordering() const
|
||||
{
|
||||
switch (_mclq_liquid_type)
|
||||
{
|
||||
case 6: return 2; // lava
|
||||
case 3: return 3; // slime
|
||||
case 1: return 1; // ocean
|
||||
default: return 0; // river
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void liquid_layer::update_attributes(MH2O_Attributes& attributes)
|
||||
{
|
||||
if (check_fatigue())
|
||||
|
||||
@@ -54,12 +54,13 @@ public:
|
||||
liquid_layer(ChunkWater* chunk, BlizzardArchive::ClientFile& f, std::size_t base_pos, glm::vec3 const& base, MH2O_Information const& info, std::uint64_t infomask);
|
||||
|
||||
liquid_layer(liquid_layer const& other);
|
||||
liquid_layer(liquid_layer&&);
|
||||
liquid_layer(liquid_layer&&) noexcept;
|
||||
|
||||
liquid_layer& operator=(liquid_layer&&);
|
||||
liquid_layer& operator=(liquid_layer&&) noexcept;
|
||||
liquid_layer& operator=(liquid_layer const& other);
|
||||
|
||||
void save(util::sExtendableArray& adt, int base_pos, int& info_pos, int& current_pos) const;
|
||||
mclq to_mclq(MH2O_Attributes& attributes) const;
|
||||
|
||||
void update_attributes(MH2O_Attributes& attributes);
|
||||
void changeLiquidID(int id);
|
||||
@@ -75,6 +76,9 @@ public:
|
||||
float min() const { return _minimum; }
|
||||
float max() const { return _maximum; }
|
||||
int liquidID() const { return _liquid_id; }
|
||||
int mclq_liquid_type() const { return _mclq_liquid_type; }
|
||||
// order of the flag corresponding to the liquid type in the mcnk header
|
||||
int mclq_flag_ordering() const;
|
||||
|
||||
// used for fatigue calculation
|
||||
bool subchunk_at_max_depth(int x, int z) const;
|
||||
@@ -105,6 +109,7 @@ public:
|
||||
ChunkWater* getChunk() { return _chunk; };
|
||||
|
||||
bool has_fatigue() const { return _fatigue_enabled; }
|
||||
|
||||
private:
|
||||
void create_vertices(float height);
|
||||
|
||||
@@ -120,6 +125,7 @@ private:
|
||||
int _liquid_id;
|
||||
int _liquid_type;
|
||||
int _liquid_vertex_format;
|
||||
int _mclq_liquid_type;
|
||||
float _minimum;
|
||||
float _maximum;
|
||||
|
||||
|
||||
@@ -219,6 +219,7 @@ namespace Noggit
|
||||
ui->_additional_file_loading_log->setChecked(
|
||||
_settings->value("additional_file_loading_log", false).toBool());
|
||||
ui->_keyboard_locale->setCurrentText(_settings->value("keyboard_locale", "QWERTY").toString());
|
||||
ui->_use_mclq_liquids_export->setChecked(_settings->value("use_mclq_liquids_export", false).toBool());
|
||||
ui->_theme->setCurrentText(_settings->value("theme", "Dark").toString());
|
||||
|
||||
ui->assetBrowserBgCol->setColor(_settings->value("assetBrowser/background_color",
|
||||
@@ -305,6 +306,7 @@ namespace Noggit
|
||||
_settings->setValue("systemWindowFrame", ui->_systemWindowFrame->isChecked());
|
||||
_settings->setValue("nativeMenubar", ui->_nativeMenubar->isChecked());
|
||||
_settings->setValue("classicUI", ui->_classic_ui->isChecked());
|
||||
_settings->setValue("use_mclq_liquids_export", ui->_use_mclq_liquids_export->isChecked());
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
_settings->setValue ("project/mysql/enabled", ui->MySQL_box->isChecked());
|
||||
|
||||
@@ -478,7 +478,7 @@
|
||||
<number>64</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -585,6 +585,15 @@
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_32">
|
||||
<property name="leftMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<property name="leftMargin">
|
||||
@@ -768,6 +777,23 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="_use_mclq_liquids_export">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>Use MCLQ Liquids (vanilla/BC) export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
||||
Reference in New Issue
Block a user