Use more decomposition, string_view, and implicit sizes in ESM code

fix-osga-rotate-wildly
Evil Eye 1 month ago
parent 781e797810
commit 5a0aed3a78

@ -896,9 +896,6 @@ namespace EsmTool
if (const ESM::Land::LandData* data = mData.getLandData(mData.mDataTypes))
{
std::cout << " Height Offset: " << data->mHeightOffset << std::endl;
// Lots of missing members.
std::cout << " Unknown1: " << data->mUnk1 << std::endl;
std::cout << " Unknown2: " << static_cast<unsigned>(data->mUnk2) << std::endl;
}
mData.unloadData();
std::cout << " Deleted: " << mIsDeleted << std::endl;

@ -232,7 +232,7 @@ namespace ESSImport
esm.skip(4);
}
esm.getExact(nam8, 32);
esm.getT(nam8);
newcell.mFogOfWar.reserve(16 * 16);
for (int x = 0; x < 16; ++x)

@ -1,10 +1,30 @@
#include "importcellref.hpp"
#include <components/esm3/esmreader.hpp>
#include <components/misc/concepts.hpp>
#include <cstdint>
namespace ESSImport
{
template <Misc::SameAsWithoutCvref<ACDT> T>
void decompose(T&& v, const auto& f)
{
f(v.mUnknown, v.mFlags, v.mBreathMeter, v.mUnknown2, v.mDynamic, v.mUnknown3, v.mAttributes, v.mMagicEffects,
v.mUnknown4, v.mGoldPool, v.mCountDown, v.mUnknown5);
}
template <Misc::SameAsWithoutCvref<ACSC> T>
void decompose(T&& v, const auto& f)
{
f(v.mUnknown1, v.mFlags, v.mUnknown2, v.mCorpseClearCountdown, v.mUnknown3);
}
template <Misc::SameAsWithoutCvref<ANIS> T>
void decompose(T&& v, const auto& f)
{
f(v.mGroupIndex, v.mUnknown, v.mTime);
}
void CellRef::load(ESM::ESMReader& esm)
{
@ -45,14 +65,9 @@ namespace ESSImport
bool isDeleted = false;
ESM::CellRef::loadData(esm, isDeleted);
mActorData.mHasACDT
= esm.getHNOT("ACDT", mActorData.mACDT.mUnknown, mActorData.mACDT.mFlags, mActorData.mACDT.mBreathMeter,
mActorData.mACDT.mUnknown2, mActorData.mACDT.mDynamic, mActorData.mACDT.mUnknown3,
mActorData.mACDT.mAttributes, mActorData.mACDT.mMagicEffects, mActorData.mACDT.mUnknown4,
mActorData.mACDT.mGoldPool, mActorData.mACDT.mCountDown, mActorData.mACDT.mUnknown5);
mActorData.mHasACDT = esm.getOptionalComposite("ACDT", mActorData.mACDT);
mActorData.mHasACSC = esm.getHNOT("ACSC", mActorData.mACSC.mUnknown1, mActorData.mACSC.mFlags,
mActorData.mACSC.mUnknown2, mActorData.mACSC.mCorpseClearCountdown, mActorData.mACSC.mUnknown3);
mActorData.mHasACSC = esm.getOptionalComposite("ACSC", mActorData.mACSC);
if (esm.isNextSub("ACSL"))
esm.skipHSubSize(112);
@ -127,8 +142,7 @@ namespace ESSImport
if (esm.isNextSub("ND3D"))
esm.skipHSub();
mActorData.mHasANIS
= esm.getHNOT("ANIS", mActorData.mANIS.mGroupIndex, mActorData.mANIS.mUnknown, mActorData.mANIS.mTime);
mActorData.mHasANIS = esm.getOptionalComposite("ANIS", mActorData.mANIS);
if (esm.isNextSub("LVCR"))
{
@ -146,7 +160,7 @@ namespace ESSImport
// I've seen DATA *twice* on a creature record, and with the exact same content too! weird
// alarmvoi0000.ess
for (int i = 0; i < 2; ++i)
esm.getHNOT("DATA", mPos.pos, mPos.rot);
esm.getOptionalComposite("DATA", mPos);
mDeleted = 0;
if (esm.isNextSub("DELE"))

@ -135,7 +135,7 @@ namespace ESSImport
sub.mFileOffset = esm.getFileOffset();
sub.mName = esm.retSubName().toString();
sub.mData.resize(esm.getSubSize());
esm.getExact(&sub.mData[0], sub.mData.size());
esm.getExact(sub.mData.data(), sub.mData.size());
rec.mSubrecords.push_back(sub);
}
file.mRecords.push_back(rec);

@ -38,7 +38,7 @@ std::string ESM::loadLuaBinaryData(ESMReader& esm)
{
esm.getSubHeader();
data.resize(esm.getSubSize());
esm.getExact(data.data(), static_cast<int>(data.size()));
esm.getExact(data.data(), data.size());
}
return data;
}

@ -153,7 +153,7 @@ namespace ESM
{
if (isNextSub(name))
return getHString();
return "";
return {};
}
ESM::RefId ESMReader::getHNORefId(NAME name)
@ -244,21 +244,6 @@ namespace ESM
skipHString();
}
void ESMReader::getHExact(void* p, std::size_t size)
{
getSubHeader();
if (size != mCtx.leftSub)
reportSubSizeMismatch(size, mCtx.leftSub);
getExact(p, size);
}
// Read the given number of bytes from a named subrecord
void ESMReader::getHNExact(void* p, std::size_t size, NAME name)
{
getSubNameIs(name);
getHExact(p, size);
}
FormId ESMReader::getFormId(bool wide, NAME tag)
{
FormId res;
@ -316,7 +301,7 @@ namespace ESM
// reading the subrecord data anyway.
const std::size_t subNameSize = decltype(mCtx.subName)::sCapacity;
getExact(mCtx.subName.mData, static_cast<int>(subNameSize));
getExact(mCtx.subName.mData, subNameSize);
mCtx.leftRec -= static_cast<std::uint32_t>(subNameSize);
}
@ -506,7 +491,7 @@ namespace ESM
case RefIdType::Generated:
{
std::uint64_t generated{};
getExact(&generated, sizeof(std::uint64_t));
getT(generated);
return RefId::generated(generated);
}
case RefIdType::Index:
@ -514,14 +499,14 @@ namespace ESM
RecNameInts recordType{};
getExact(&recordType, sizeof(std::uint32_t));
std::uint32_t index{};
getExact(&index, sizeof(std::uint32_t));
getT(index);
return RefId::index(recordType, index);
}
case RefIdType::ESM3ExteriorCell:
{
int32_t x, y;
getExact(&x, sizeof(std::int32_t));
getExact(&y, sizeof(std::int32_t));
getT(x);
getT(y);
return RefId::esm3ExteriorCell(x, y);
}
}
@ -529,7 +514,7 @@ namespace ESM
fail("Unsupported RefIdType: " + std::to_string(static_cast<unsigned>(refIdType)));
}
[[noreturn]] void ESMReader::fail(const std::string& msg)
[[noreturn]] void ESMReader::fail(std::string_view msg)
{
std::stringstream ss;

@ -237,12 +237,6 @@ namespace ESM
void skipHRefId();
// Read the given number of bytes from a subrecord
void getHExact(void* p, std::size_t size);
// Read the given number of bytes from a named subrecord
void getHNExact(void* p, std::size_t size, NAME name);
ESM::FormId getFormId(bool wide = false, NAME tag = "FRMR");
/*************************************************************************
@ -354,7 +348,7 @@ namespace ESM
}
/// Used for error handling
[[noreturn]] void fail(const std::string& msg);
[[noreturn]] void fail(std::string_view msg);
/// Sets font encoder for ESM strings
void setEncoder(ToUTF8::Utf8Encoder* encoder) { mEncoder = encoder; }

@ -97,14 +97,14 @@ namespace ESM
mHeader.mData.type = type;
}
void ESMWriter::setAuthor(const std::string& auth)
void ESMWriter::setAuthor(std::string_view auth)
{
mHeader.mData.author.assign(auth);
mHeader.mData.author = auth;
}
void ESMWriter::setDescription(const std::string& desc)
void ESMWriter::setDescription(std::string_view desc)
{
mHeader.mData.desc.assign(desc);
mHeader.mData.desc = desc;
}
void ESMWriter::setRecordCount(int count)
@ -122,7 +122,7 @@ namespace ESM
mHeader.mMaster.clear();
}
void ESMWriter::addMaster(const std::string& name, uint64_t size)
void ESMWriter::addMaster(std::string_view name, uint64_t size)
{
Header::MasterData d;
d.name = name;
@ -208,14 +208,14 @@ namespace ESM
endRecord(NAME(name));
}
void ESMWriter::writeHNString(NAME name, const std::string& data)
void ESMWriter::writeHNString(NAME name, std::string_view data)
{
startSubRecord(name);
writeHString(data);
endRecord(name);
}
void ESMWriter::writeHNString(NAME name, const std::string& data, size_t size)
void ESMWriter::writeHNString(NAME name, std::string_view data, size_t size)
{
assert(data.size() <= size);
startSubRecord(name);
@ -278,9 +278,9 @@ namespace ESM
write(string.c_str(), string.size());
}
void ESMWriter::writeHString(const std::string& data)
void ESMWriter::writeHString(std::string_view data)
{
if (data.size() == 0)
if (data.empty())
write("\0", 1);
else
{
@ -291,7 +291,7 @@ namespace ESM
}
}
void ESMWriter::writeHCString(const std::string& data)
void ESMWriter::writeHCString(std::string_view data)
{
writeHString(data);
if (data.size() > 0 && data[data.size() - 1] != '\0')

@ -38,8 +38,8 @@ namespace ESM
void setVersion(unsigned int ver = 0x3fa66666);
void setType(int type);
void setEncoder(ToUTF8::Utf8Encoder* encoding);
void setAuthor(const std::string& author);
void setDescription(const std::string& desc);
void setAuthor(std::string_view author);
void setDescription(std::string_view desc);
void setHeader(const Header& value) { mHeader = value; }
// Set the record count for writing it in the file header
@ -54,7 +54,7 @@ namespace ESM
void clearMaster();
void addMaster(const std::string& name, uint64_t size);
void addMaster(std::string_view name, uint64_t size);
void save(std::ostream& file);
///< Start saving a file by writing the TES3 header.
@ -62,20 +62,20 @@ namespace ESM
void close();
///< \note Does not close the stream.
void writeHNString(NAME name, const std::string& data);
void writeHNString(NAME name, const std::string& data, size_t size);
void writeHNCString(NAME name, const std::string& data)
void writeHNString(NAME name, std::string_view data);
void writeHNString(NAME name, std::string_view data, size_t size);
void writeHNCString(NAME name, std::string_view data)
{
startSubRecord(name);
writeHCString(data);
endRecord(name);
}
void writeHNOString(NAME name, const std::string& data)
void writeHNOString(NAME name, std::string_view data)
{
if (!data.empty())
writeHNString(name, data);
}
void writeHNOCString(NAME name, const std::string& data)
void writeHNOCString(NAME name, std::string_view data)
{
if (!data.empty())
writeHNCString(name, data);
@ -140,6 +140,7 @@ namespace ESM
// state being discarded without any error on writing or reading it. :(
// writeHNString and friends must be used instead.
void writeHNT(NAME name, const std::string& data) = delete;
void writeHNT(NAME name, std::string_view data) = delete;
void writeT(NAME data) = delete;
@ -181,8 +182,8 @@ namespace ESM
void endRecord(NAME name);
void endRecord(uint32_t name);
void writeMaybeFixedSizeString(const std::string& data, std::size_t size);
void writeHString(const std::string& data);
void writeHCString(const std::string& data);
void writeHString(std::string_view data);
void writeHCString(std::string_view data);
void writeMaybeFixedSizeRefId(RefId value, std::size_t size);

@ -1,6 +1,7 @@
#ifndef OPENMW_COMPONENTS_ESM3_LANDRECORDDATA_H
#define OPENMW_COMPONENTS_ESM3_LANDRECORDDATA_H
#include <array>
#include <cstdint>
namespace ESM
@ -22,24 +23,20 @@ namespace ESM
// Initial reference height for the first vertex, only needed for filling mHeights
float mHeightOffset = 0;
// Height in world space for each vertex
float mHeights[sLandNumVerts];
std::array<float, sLandNumVerts> mHeights;
float mMinHeight = 0;
float mMaxHeight = 0;
// 24-bit normals, these aren't always correct though. Edge and corner normals may be garbage.
std::int8_t mNormals[sLandNumVerts * 3];
std::array<std::int8_t, 3 * sLandNumVerts> mNormals;
// 2D array of texture indices. An index can be used to look up an LandTexture,
// but to do so you must subtract 1 from the index first!
// An index of 0 indicates the default texture.
std::uint16_t mTextures[sLandNumTextures];
std::array<std::uint16_t, sLandNumTextures> mTextures;
// 24-bit RGB color for each vertex
std::uint8_t mColours[3 * sLandNumVerts];
// ???
std::uint16_t mUnk1 = 0;
std::uint8_t mUnk2 = 0;
std::array<std::uint8_t, 3 * sLandNumVerts> mColours;
int mDataLoaded = 0;
};

@ -5,7 +5,9 @@
#include <limits>
#include <utility>
#include "components/esm/defs.hpp"
#include <components/esm/defs.hpp>
#include <components/misc/concepts.hpp>
#include "esmreader.hpp"
#include "esmwriter.hpp"
@ -13,27 +15,43 @@ namespace ESM
{
namespace
{
struct VHGT
{
float mHeightOffset;
std::int8_t mHeightData[LandRecordData::sLandNumVerts];
};
template <Misc::SameAsWithoutCvref<VHGT> T>
void decompose(T&& v, const auto& f)
{
char padding[3] = { 0, 0, 0 };
f(v.mHeightOffset, v.mHeightData, padding);
}
void transposeTextureData(const std::uint16_t* in, std::uint16_t* out)
{
int readPos = 0; // bit ugly, but it works
for (int y1 = 0; y1 < 4; y1++)
for (int x1 = 0; x1 < 4; x1++)
for (int y2 = 0; y2 < 4; y2++)
for (int x2 = 0; x2 < 4; x2++)
size_t readPos = 0; // bit ugly, but it works
for (size_t y1 = 0; y1 < 4; y1++)
for (size_t x1 = 0; x1 < 4; x1++)
for (size_t y2 = 0; y2 < 4; y2++)
for (size_t x2 = 0; x2 < 4; x2++)
out[(y1 * 4 + y2) * 16 + (x1 * 4 + x2)] = in[readPos++];
}
// Loads data and marks it as loaded. Return true if data is actually loaded from reader, false otherwise
// including the case when data is already loaded.
bool condLoad(ESMReader& reader, int flags, int& targetFlags, int dataFlag, void* ptr, std::size_t size)
bool condLoad(ESMReader& reader, int flags, int& targetFlags, int dataFlag, auto& in)
{
if ((targetFlags & dataFlag) == 0 && (flags & dataFlag) != 0)
{
reader.getHExact(ptr, size);
if constexpr (std::is_same_v<std::remove_cvref_t<decltype(in)>, VHGT>)
reader.getSubComposite(in);
else
reader.getHT(in);
targetFlags |= dataFlag;
return true;
}
reader.skipHSubSize(size);
reader.skipHSub();
return false;
}
}
@ -50,11 +68,7 @@ namespace ESM
switch (esm.retSubName().toInt())
{
case fourCC("INTV"):
esm.getSubHeader();
if (esm.getSubSize() != 8)
esm.fail("Subrecord size is not equal to 8");
esm.getT(mX);
esm.getT(mY);
esm.getHT(mX, mY);
hasLocation = true;
break;
case fourCC("DATA"):
@ -94,7 +108,7 @@ namespace ESM
mDataTypes |= DATA_VHGT;
break;
case fourCC("WNAM"):
esm.getHExact(mWnam.data(), mWnam.size());
esm.getHT(mWnam);
mDataTypes |= DATA_WNAM;
break;
case fourCC("VCLR"):
@ -137,12 +151,10 @@ namespace ESM
{
VHGT offsets;
offsets.mHeightOffset = mLandData->mHeights[0] / HEIGHT_SCALE;
offsets.mUnk1 = mLandData->mUnk1;
offsets.mUnk2 = mLandData->mUnk2;
float prevY = mLandData->mHeights[0];
int number = 0; // avoid multiplication
for (int i = 0; i < LAND_SIZE; ++i)
size_t number = 0; // avoid multiplication
for (unsigned i = 0; i < LandRecordData::sLandSize; ++i)
{
float diff = (mLandData->mHeights[number] - prevY) / HEIGHT_SCALE;
offsets.mHeightData[number]
@ -151,7 +163,7 @@ namespace ESM
float prevX = prevY = mLandData->mHeights[number];
++number;
for (int j = 1; j < LAND_SIZE; ++j)
for (unsigned j = 1; j < LandRecordData::sLandSize; ++j)
{
diff = (mLandData->mHeights[number] - prevX) / HEIGHT_SCALE;
offsets.mHeightData[number]
@ -161,7 +173,7 @@ namespace ESM
++number;
}
}
esm.writeHNT("VHGT", offsets, sizeof(VHGT));
esm.writeNamedComposite("VHGT", offsets);
}
if (mDataTypes & Land::DATA_WNAM)
{
@ -169,13 +181,15 @@ namespace ESM
std::int8_t wnam[LAND_GLOBAL_MAP_LOD_SIZE];
constexpr float max = std::numeric_limits<std::int8_t>::max();
constexpr float min = std::numeric_limits<std::int8_t>::min();
constexpr float vertMult = static_cast<float>(Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT;
for (int row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row)
constexpr float vertMult
= static_cast<float>(LandRecordData::sLandSize - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT;
for (unsigned row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row)
{
for (int col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col)
for (unsigned col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col)
{
float height = mLandData->mHeights[static_cast<int>(row * vertMult) * Land::LAND_SIZE
+ static_cast<int>(col * vertMult)];
float height
= mLandData->mHeights[static_cast<size_t>(row * vertMult) * LandRecordData::sLandSize
+ static_cast<size_t>(col * vertMult)];
height /= height > 0 ? 128.f : 16.f;
height = std::clamp(height, min, max);
wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast<std::int8_t>(height);
@ -189,8 +203,8 @@ namespace ESM
}
if (mDataTypes & Land::DATA_VTEX)
{
uint16_t vtex[LAND_NUM_TEXTURES];
transposeTextureData(mLandData->mTextures, vtex);
uint16_t vtex[LandRecordData::sLandNumTextures];
transposeTextureData(mLandData->mTextures.data(), vtex);
esm.writeHNT("VTEX", vtex);
}
}
@ -200,25 +214,23 @@ namespace ESM
{
setPlugin(0);
std::fill(std::begin(mWnam), std::end(mWnam), 0);
mWnam.fill(0);
if (mLandData == nullptr)
mLandData = std::make_unique<LandData>();
mLandData->mHeightOffset = 0;
std::fill(std::begin(mLandData->mHeights), std::end(mLandData->mHeights), 0.0f);
mLandData->mHeights.fill(0);
mLandData->mMinHeight = 0;
mLandData->mMaxHeight = 0;
for (int i = 0; i < LAND_NUM_VERTS; ++i)
for (size_t i = 0; i < LandRecordData::sLandNumVerts; ++i)
{
mLandData->mNormals[i * 3 + 0] = 0;
mLandData->mNormals[i * 3 + 1] = 0;
mLandData->mNormals[i * 3 + 2] = 127;
}
std::fill(std::begin(mLandData->mTextures), std::end(mLandData->mTextures), 0);
std::fill(std::begin(mLandData->mColours), std::end(mLandData->mColours), 255);
mLandData->mUnk1 = 0;
mLandData->mUnk2 = 0;
mLandData->mTextures.fill(0);
mLandData->mColours.fill(255);
mLandData->mDataLoaded
= Land::DATA_VNML | Land::DATA_VHGT | Land::DATA_WNAM | Land::DATA_VCLR | Land::DATA_VTEX;
mDataTypes = mLandData->mDataLoaded;
@ -259,32 +271,32 @@ namespace ESM
if (reader.isNextSub("VNML"))
{
condLoad(reader, flags, data.mDataLoaded, DATA_VNML, data.mNormals, sizeof(data.mNormals));
condLoad(reader, flags, data.mDataLoaded, DATA_VNML, data.mNormals);
}
if (reader.isNextSub("VHGT"))
{
VHGT vhgt;
if (condLoad(reader, flags, data.mDataLoaded, DATA_VHGT, &vhgt, sizeof(vhgt)))
if (condLoad(reader, flags, data.mDataLoaded, DATA_VHGT, vhgt))
{
data.mMinHeight = std::numeric_limits<float>::max();
data.mMaxHeight = -std::numeric_limits<float>::max();
float rowOffset = vhgt.mHeightOffset;
for (int y = 0; y < LAND_SIZE; y++)
for (unsigned y = 0; y < LandRecordData::sLandSize; y++)
{
rowOffset += vhgt.mHeightData[y * LAND_SIZE];
rowOffset += vhgt.mHeightData[y * LandRecordData::sLandSize];
data.mHeights[y * LAND_SIZE] = rowOffset * HEIGHT_SCALE;
data.mHeights[y * LandRecordData::sLandSize] = rowOffset * HEIGHT_SCALE;
if (rowOffset * HEIGHT_SCALE > data.mMaxHeight)
data.mMaxHeight = rowOffset * HEIGHT_SCALE;
if (rowOffset * HEIGHT_SCALE < data.mMinHeight)
data.mMinHeight = rowOffset * HEIGHT_SCALE;
float colOffset = rowOffset;
for (int x = 1; x < LAND_SIZE; x++)
for (unsigned x = 1; x < LandRecordData::sLandSize; x++)
{
colOffset += vhgt.mHeightData[y * LAND_SIZE + x];
data.mHeights[x + y * LAND_SIZE] = colOffset * HEIGHT_SCALE;
colOffset += vhgt.mHeightData[y * LandRecordData::sLandSize + x];
data.mHeights[x + y * LandRecordData::sLandSize] = colOffset * HEIGHT_SCALE;
if (colOffset * HEIGHT_SCALE > data.mMaxHeight)
data.mMaxHeight = colOffset * HEIGHT_SCALE;
@ -292,8 +304,6 @@ namespace ESM
data.mMinHeight = colOffset * HEIGHT_SCALE;
}
}
data.mUnk1 = vhgt.mUnk1;
data.mUnk2 = vhgt.mUnk2;
}
}
@ -301,13 +311,13 @@ namespace ESM
reader.skipHSub();
if (reader.isNextSub("VCLR"))
condLoad(reader, flags, data.mDataLoaded, DATA_VCLR, data.mColours, 3 * LAND_NUM_VERTS);
condLoad(reader, flags, data.mDataLoaded, DATA_VCLR, data.mColours);
if (reader.isNextSub("VTEX"))
{
uint16_t vtex[LAND_NUM_TEXTURES];
if (condLoad(reader, flags, data.mDataLoaded, DATA_VTEX, vtex, sizeof(vtex)))
uint16_t vtex[LandRecordData::sLandNumTextures];
if (condLoad(reader, flags, data.mDataLoaded, DATA_VTEX, vtex))
{
transposeTextureData(vtex, data.mTextures);
transposeTextureData(vtex, data.mTextures.data());
}
}
}

@ -86,19 +86,9 @@ namespace ESM
// total number of textures per land
static constexpr int LAND_NUM_TEXTURES = LandRecordData::sLandNumTextures;
static constexpr int LAND_GLOBAL_MAP_LOD_SIZE = 81;
static constexpr unsigned LAND_GLOBAL_MAP_LOD_SIZE = 81;
static constexpr int LAND_GLOBAL_MAP_LOD_SIZE_SQRT = 9;
#pragma pack(push, 1)
struct VHGT
{
float mHeightOffset;
std::int8_t mHeightData[LAND_NUM_VERTS];
std::uint16_t mUnk1;
std::uint8_t mUnk2;
};
#pragma pack(pop)
static constexpr unsigned LAND_GLOBAL_MAP_LOD_SIZE_SQRT = 9;
using LandData = ESM::LandRecordData;
@ -128,16 +118,10 @@ namespace ESM
const LandData* getLandData(int flags) const;
/// Return land data without loading first anything. Can return a 0-pointer.
const LandData* getLandData() const
{
return mLandData.get();
}
const LandData* getLandData() const { return mLandData.get(); }
/// Return land data without loading first anything. Can return a 0-pointer.
LandData* getLandData()
{
return mLandData.get();
}
LandData* getLandData() { return mLandData.get(); }
/// \attention Must not be called on objects that aren't fully loaded.
///

Loading…
Cancel
Save