forked from teamnwah/openmw-tes3coop
Terrain refactoring, reduce game startup time and memory usage
This commit is contained in:
parent
62a32220ff
commit
e712b0353b
9 changed files with 58 additions and 48 deletions
|
@ -814,7 +814,6 @@ void Record<ESM::Land>::print()
|
||||||
{
|
{
|
||||||
std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl;
|
std::cout << " Coordinates: (" << mData.mX << "," << mData.mY << ")" << std::endl;
|
||||||
std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl;
|
std::cout << " Flags: " << landFlags(mData.mFlags) << std::endl;
|
||||||
std::cout << " HasData: " << mData.mHasData << std::endl;
|
|
||||||
std::cout << " DataTypes: " << mData.mDataTypes << std::endl;
|
std::cout << " DataTypes: " << mData.mDataTypes << std::endl;
|
||||||
|
|
||||||
// Seems like this should done with reference counting in the
|
// Seems like this should done with reference counting in the
|
||||||
|
|
|
@ -76,7 +76,7 @@ namespace MWRender
|
||||||
|
|
||||||
if (land)
|
if (land)
|
||||||
{
|
{
|
||||||
int mask = ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX | ESM::Land::DATA_WNAM;
|
int mask = ESM::Land::DATA_WNAM;
|
||||||
if (!land->isDataLoaded(mask))
|
if (!land->isDataLoaded(mask))
|
||||||
land->loadData(mask);
|
land->loadData(mask);
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,8 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadingListener->increaseProgress();
|
loadingListener->increaseProgress();
|
||||||
|
if (land)
|
||||||
|
land->unloadData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1036,10 +1036,10 @@ void RenderingManager::enableTerrain(bool enable)
|
||||||
if (!mTerrain)
|
if (!mTerrain)
|
||||||
{
|
{
|
||||||
if (Settings::Manager::getBool("distant land", "Terrain"))
|
if (Settings::Manager::getBool("distant land", "Terrain"))
|
||||||
mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(true), RV_Terrain,
|
||||||
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
|
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
|
||||||
else
|
else
|
||||||
mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
mTerrain = new Terrain::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(false), RV_Terrain,
|
||||||
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY);
|
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY);
|
||||||
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
|
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
|
||||||
Settings::Manager::getBool("split", "Shadows"));
|
Settings::Manager::getBool("split", "Shadows"));
|
||||||
|
|
|
@ -9,6 +9,22 @@
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
|
TerrainStorage::TerrainStorage(bool preload)
|
||||||
|
{
|
||||||
|
if (preload)
|
||||||
|
{
|
||||||
|
const MWWorld::ESMStore &esmStore =
|
||||||
|
MWBase::Environment::get().getWorld()->getStore();
|
||||||
|
|
||||||
|
MWWorld::Store<ESM::Land>::iterator it = esmStore.get<ESM::Land>().begin();
|
||||||
|
for (; it != esmStore.get<ESM::Land>().end(); ++it)
|
||||||
|
{
|
||||||
|
ESM::Land* land = const_cast<ESM::Land*>(&*it); // TODO: fix store interface
|
||||||
|
land->loadData(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
|
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
|
||||||
{
|
{
|
||||||
minX = 0, minY = 0, maxX = 0, maxY = 0;
|
minX = 0, minY = 0, maxX = 0, maxY = 0;
|
||||||
|
@ -39,6 +55,12 @@ namespace MWRender
|
||||||
const MWWorld::ESMStore &esmStore =
|
const MWWorld::ESMStore &esmStore =
|
||||||
MWBase::Environment::get().getWorld()->getStore();
|
MWBase::Environment::get().getWorld()->getStore();
|
||||||
ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY);
|
ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY);
|
||||||
|
if (!land)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX;
|
||||||
|
if (!land->isDataLoaded(flags))
|
||||||
|
land->loadData(flags);
|
||||||
return land;
|
return land;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,10 @@ namespace MWRender
|
||||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
|
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
///@param preload Preload all Land records at startup? If using the multithreaded terrain component, this
|
||||||
|
/// should be set to "true" in order to avoid race conditions.
|
||||||
|
TerrainStorage(bool preload);
|
||||||
|
|
||||||
/// Get bounds of the whole terrain in cell units
|
/// Get bounds of the whole terrain in cell units
|
||||||
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
|
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
|
||||||
};
|
};
|
||||||
|
|
|
@ -231,6 +231,11 @@ namespace MWWorld
|
||||||
cell->getCell()->getGridY()
|
cell->getCell()->getGridY()
|
||||||
);
|
);
|
||||||
if (land) {
|
if (land) {
|
||||||
|
// Actually only VHGT is needed here, but we'll need the rest for rendering anyway.
|
||||||
|
// Load everything now to reduce IO overhead.
|
||||||
|
const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX;
|
||||||
|
if (!land->isDataLoaded(flags))
|
||||||
|
land->loadData(flags);
|
||||||
mPhysics->addHeightField (
|
mPhysics->addHeightField (
|
||||||
land->mLandData->mHeights,
|
land->mLandData->mHeights,
|
||||||
cell->getCell()->getGridX(),
|
cell->getCell()->getGridX(),
|
||||||
|
|
|
@ -72,7 +72,6 @@ Land::Land()
|
||||||
, mDataLoaded(false)
|
, mDataLoaded(false)
|
||||||
, mLandData(NULL)
|
, mLandData(NULL)
|
||||||
, mPlugin(0)
|
, mPlugin(0)
|
||||||
, mHasData(false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,8 +96,6 @@ void Land::load(ESMReader &esm)
|
||||||
// Store the file position
|
// Store the file position
|
||||||
mContext = esm.getContext();
|
mContext = esm.getContext();
|
||||||
|
|
||||||
mHasData = false;
|
|
||||||
|
|
||||||
// Skip these here. Load the actual data when the cell is loaded.
|
// Skip these here. Load the actual data when the cell is loaded.
|
||||||
if (esm.isNextSub("VNML"))
|
if (esm.isNextSub("VNML"))
|
||||||
{
|
{
|
||||||
|
@ -126,10 +123,6 @@ void Land::load(ESMReader &esm)
|
||||||
mDataTypes |= DATA_VTEX;
|
mDataTypes |= DATA_VTEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need all three of VNML, VHGT and VTEX in order to use the
|
|
||||||
// landscape. (Though Morrowind seems to accept terrain without VTEX/VCLR entries)
|
|
||||||
mHasData = mDataTypes & (DATA_VNML|DATA_VHGT|DATA_WNAM);
|
|
||||||
|
|
||||||
mDataLoaded = 0;
|
mDataLoaded = 0;
|
||||||
mLandData = NULL;
|
mLandData = NULL;
|
||||||
}
|
}
|
||||||
|
@ -144,13 +137,12 @@ void Land::save(ESMWriter &esm) const
|
||||||
esm.writeHNT("DATA", mFlags);
|
esm.writeHNT("DATA", mFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \todo remove memory allocation when only defaults needed
|
|
||||||
void Land::loadData(int flags)
|
void Land::loadData(int flags)
|
||||||
{
|
{
|
||||||
// Try to load only available data
|
// Try to load only available data
|
||||||
int actual = flags & mDataTypes;
|
flags = flags & mDataTypes;
|
||||||
// Return if all required data is loaded
|
// Return if all required data is loaded
|
||||||
if (flags == 0 || (actual != 0 && (mDataLoaded & actual) == actual)) {
|
if ((mDataLoaded & flags) == flags) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Create storage if nothing is loaded
|
// Create storage if nothing is loaded
|
||||||
|
@ -160,15 +152,13 @@ void Land::loadData(int flags)
|
||||||
}
|
}
|
||||||
mEsm->restoreContext(mContext);
|
mEsm->restoreContext(mContext);
|
||||||
|
|
||||||
memset(mLandData->mNormals, 0, sizeof(mLandData->mNormals));
|
|
||||||
|
|
||||||
if (mEsm->isNextSub("VNML")) {
|
if (mEsm->isNextSub("VNML")) {
|
||||||
condLoad(actual, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals));
|
condLoad(flags, DATA_VNML, mLandData->mNormals, sizeof(mLandData->mNormals));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mEsm->isNextSub("VHGT")) {
|
if (mEsm->isNextSub("VHGT")) {
|
||||||
static VHGT vhgt;
|
static VHGT vhgt;
|
||||||
if (condLoad(actual, DATA_VHGT, &vhgt, sizeof(vhgt))) {
|
if (condLoad(flags, DATA_VHGT, &vhgt, sizeof(vhgt))) {
|
||||||
float rowOffset = vhgt.mHeightOffset;
|
float rowOffset = vhgt.mHeightOffset;
|
||||||
for (int y = 0; y < LAND_SIZE; y++) {
|
for (int y = 0; y < LAND_SIZE; y++) {
|
||||||
rowOffset += vhgt.mHeightData[y * LAND_SIZE];
|
rowOffset += vhgt.mHeightData[y * LAND_SIZE];
|
||||||
|
@ -184,30 +174,18 @@ void Land::loadData(int flags)
|
||||||
mLandData->mUnk1 = vhgt.mUnk1;
|
mLandData->mUnk1 = vhgt.mUnk1;
|
||||||
mLandData->mUnk2 = vhgt.mUnk2;
|
mLandData->mUnk2 = vhgt.mUnk2;
|
||||||
}
|
}
|
||||||
} else if ((flags & DATA_VHGT) && (mDataLoaded & DATA_VHGT) == 0) {
|
|
||||||
for (int i = 0; i < LAND_NUM_VERTS; ++i) {
|
|
||||||
mLandData->mHeights[i] = -256.0f * HEIGHT_SCALE;
|
|
||||||
}
|
|
||||||
mDataLoaded |= DATA_VHGT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mEsm->isNextSub("WNAM")) {
|
if (mEsm->isNextSub("WNAM")) {
|
||||||
condLoad(actual, DATA_WNAM, mLandData->mWnam, 81);
|
condLoad(flags, DATA_WNAM, mLandData->mWnam, 81);
|
||||||
}
|
|
||||||
if (mEsm->isNextSub("VCLR")) {
|
|
||||||
mLandData->mUsingColours = true;
|
|
||||||
condLoad(actual, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
|
|
||||||
} else {
|
|
||||||
mLandData->mUsingColours = false;
|
|
||||||
}
|
}
|
||||||
|
if (mEsm->isNextSub("VCLR"))
|
||||||
|
condLoad(flags, DATA_VCLR, mLandData->mColours, 3 * LAND_NUM_VERTS);
|
||||||
if (mEsm->isNextSub("VTEX")) {
|
if (mEsm->isNextSub("VTEX")) {
|
||||||
static uint16_t vtex[LAND_NUM_TEXTURES];
|
static uint16_t vtex[LAND_NUM_TEXTURES];
|
||||||
if (condLoad(actual, DATA_VTEX, vtex, sizeof(vtex))) {
|
if (condLoad(flags, DATA_VTEX, vtex, sizeof(vtex))) {
|
||||||
LandData::transposeTextureData(vtex, mLandData->mTextures);
|
LandData::transposeTextureData(vtex, mLandData->mTextures);
|
||||||
}
|
}
|
||||||
} else if ((flags & DATA_VTEX) && (mDataLoaded & DATA_VTEX) == 0) {
|
|
||||||
memset(mLandData->mTextures, 0, sizeof(mLandData->mTextures));
|
|
||||||
mDataLoaded |= DATA_VTEX;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,4 +210,9 @@ bool Land::condLoad(int flags, int dataFlag, void *ptr, unsigned int size)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Land::isDataLoaded(int flags) const
|
||||||
|
{
|
||||||
|
return (mDataLoaded & flags) == (flags & mDataTypes);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ struct Land
|
||||||
ESMReader* mEsm;
|
ESMReader* mEsm;
|
||||||
ESM_Context mContext;
|
ESM_Context mContext;
|
||||||
|
|
||||||
bool mHasData;
|
|
||||||
int mDataTypes;
|
int mDataTypes;
|
||||||
int mDataLoaded;
|
int mDataLoaded;
|
||||||
|
|
||||||
|
@ -81,7 +80,6 @@ struct Land
|
||||||
VNML mNormals[LAND_NUM_VERTS * 3];
|
VNML mNormals[LAND_NUM_VERTS * 3];
|
||||||
uint16_t mTextures[LAND_NUM_TEXTURES];
|
uint16_t mTextures[LAND_NUM_TEXTURES];
|
||||||
|
|
||||||
bool mUsingColours;
|
|
||||||
char mColours[3 * LAND_NUM_VERTS];
|
char mColours[3 * LAND_NUM_VERTS];
|
||||||
int mDataTypes;
|
int mDataTypes;
|
||||||
|
|
||||||
|
@ -113,10 +111,8 @@ struct Land
|
||||||
void unloadData();
|
void unloadData();
|
||||||
|
|
||||||
/// Check if given data type is loaded
|
/// Check if given data type is loaded
|
||||||
/// \todo reimplement this
|
/// @note We only check data types that *can* be loaded (present in mDataTypes)
|
||||||
bool isDataLoaded(int flags) {
|
bool isDataLoaded(int flags) const;
|
||||||
return (mDataLoaded & flags) == flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Land(const Land& land);
|
Land(const Land& land);
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace ESMTerrain
|
||||||
int cellY = origin.y;
|
int cellY = origin.y;
|
||||||
|
|
||||||
const ESM::Land* land = getLand(cellX, cellY);
|
const ESM::Land* land = getLand(cellX, cellY);
|
||||||
if (!land)
|
if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
min = std::numeric_limits<float>::max();
|
min = std::numeric_limits<float>::max();
|
||||||
|
@ -73,7 +73,7 @@ namespace ESMTerrain
|
||||||
row += ESM::Land::LAND_SIZE-1;
|
row += ESM::Land::LAND_SIZE-1;
|
||||||
}
|
}
|
||||||
ESM::Land* land = getLand(cellX, cellY);
|
ESM::Land* land = getLand(cellX, cellY);
|
||||||
if (land && land->mHasData)
|
if (land && land->mDataTypes&ESM::Land::DATA_VNML)
|
||||||
{
|
{
|
||||||
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
|
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
|
||||||
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
||||||
|
@ -108,7 +108,7 @@ namespace ESMTerrain
|
||||||
row = 0;
|
row = 0;
|
||||||
}
|
}
|
||||||
ESM::Land* land = getLand(cellX, cellY);
|
ESM::Land* land = getLand(cellX, cellY);
|
||||||
if (land && land->mLandData->mUsingColours)
|
if (land && land->mDataTypes&ESM::Land::DATA_VCLR)
|
||||||
{
|
{
|
||||||
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
|
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
|
||||||
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
|
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
|
||||||
|
@ -157,9 +157,8 @@ namespace ESMTerrain
|
||||||
for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX)
|
for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX)
|
||||||
{
|
{
|
||||||
ESM::Land* land = getLand(cellX, cellY);
|
ESM::Land* land = getLand(cellX, cellY);
|
||||||
if (land && !land->mHasData)
|
if (land && !(land->mDataTypes&ESM::Land::DATA_VHGT))
|
||||||
land = NULL;
|
land = NULL;
|
||||||
bool hasColors = land && land->mLandData->mUsingColours;
|
|
||||||
|
|
||||||
int rowStart = 0;
|
int rowStart = 0;
|
||||||
int colStart = 0;
|
int colStart = 0;
|
||||||
|
@ -183,7 +182,7 @@ namespace ESMTerrain
|
||||||
else
|
else
|
||||||
positions[vertX*numVerts*3 + vertY*3 + 2] = -2048;
|
positions[vertX*numVerts*3 + vertY*3 + 2] = -2048;
|
||||||
|
|
||||||
if (land)
|
if (land && land->mDataTypes&ESM::Land::DATA_VNML)
|
||||||
{
|
{
|
||||||
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
|
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
|
||||||
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
|
||||||
|
@ -207,7 +206,7 @@ namespace ESMTerrain
|
||||||
normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
|
normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
|
||||||
normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
|
normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
|
||||||
|
|
||||||
if (hasColors)
|
if (land && land->mDataTypes&ESM::Land::DATA_VCLR)
|
||||||
{
|
{
|
||||||
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
|
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
|
||||||
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
|
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
|
||||||
|
@ -263,7 +262,7 @@ namespace ESMTerrain
|
||||||
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
|
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
|
|
||||||
ESM::Land* land = getLand(cellX, cellY);
|
ESM::Land* land = getLand(cellX, cellY);
|
||||||
if (land)
|
if (land && (land->mDataTypes&ESM::Land::DATA_VTEX))
|
||||||
{
|
{
|
||||||
int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
|
int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
|
||||||
if (tex == 0)
|
if (tex == 0)
|
||||||
|
|
Loading…
Reference in a new issue