mirror of
https://github.com/OpenMW/openmw.git
synced 2025-10-18 13:46:37 +00:00
Merge branch 'esm4-terrain' into 'master'
Initial support for ESM4 terrain See merge request OpenMW/openmw!3032
This commit is contained in:
commit
27babefee6
46 changed files with 769 additions and 380 deletions
|
@ -146,8 +146,8 @@ void CSVRender::Cell::updateLand()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mTerrain = std::make_unique<Terrain::TerrainGrid>(
|
mTerrain = std::make_unique<Terrain::TerrainGrid>(mCellNode, mCellNode, mData.getResourceSystem().get(),
|
||||||
mCellNode, mCellNode, mData.getResourceSystem().get(), mTerrainStorage, Mask_Terrain);
|
mTerrainStorage, Mask_Terrain, ESM::Cell::sDefaultWorldspaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
mTerrain->loadCell(esmLand.mX, esmLand.mY);
|
mTerrain->loadCell(esmLand.mX, esmLand.mY);
|
||||||
|
|
|
@ -28,12 +28,12 @@ namespace CSVRender
|
||||||
resetHeights();
|
resetHeights();
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)
|
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(ESM::ExteriorCellLocation cellLocation)
|
||||||
{
|
{
|
||||||
// The cell isn't guaranteed to have Land. This is because the terrain implementation
|
// The cell isn't guaranteed to have Land. This is because the terrain implementation
|
||||||
// has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
|
// has to wrap the vertices of the last row and column to the next cell, which may be a nonexisting cell
|
||||||
const int index
|
const int index = mData.getLand().searchId(
|
||||||
= mData.getLand().searchId(ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellX, cellY)));
|
ESM::RefId::stringRefId(CSMWorld::Land::createUniqueRecordId(cellLocation.mX, cellLocation.mY)));
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -82,83 +82,83 @@ namespace CSVRender
|
||||||
return &mAlteredHeight[inCellY * ESM::Land::LAND_SIZE + inCellX];
|
return &mAlteredHeight[inCellY * ESM::Land::LAND_SIZE + inCellX];
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
|
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
// not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells
|
// not needed at the moment - this returns the bounds of the whole world, but we only edit individual cells
|
||||||
throw std::runtime_error("getBounds not implemented");
|
throw std::runtime_error("getBounds not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getThisHeight(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getThisHeight(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return heightData->mHeights[col * ESM::Land::LAND_SIZE + row]
|
return heightData[col * ESM::Land::LAND_SIZE + row]
|
||||||
+ mAlteredHeight[static_cast<unsigned int>(col * ESM::Land::LAND_SIZE + row)];
|
+ mAlteredHeight[static_cast<unsigned int>(col * ESM::Land::LAND_SIZE + row)];
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getLeftHeight(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getLeftHeight(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return heightData->mHeights[(col)*ESM::Land::LAND_SIZE + row - 1]
|
return heightData[(col)*ESM::Land::LAND_SIZE + row - 1]
|
||||||
+ mAlteredHeight[static_cast<unsigned int>((col)*ESM::Land::LAND_SIZE + row - 1)];
|
+ mAlteredHeight[static_cast<unsigned int>((col)*ESM::Land::LAND_SIZE + row - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getRightHeight(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getRightHeight(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return heightData->mHeights[col * ESM::Land::LAND_SIZE + row + 1]
|
return heightData[col * ESM::Land::LAND_SIZE + row + 1]
|
||||||
+ mAlteredHeight[static_cast<unsigned int>(col * ESM::Land::LAND_SIZE + row + 1)];
|
+ mAlteredHeight[static_cast<unsigned int>(col * ESM::Land::LAND_SIZE + row + 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getUpHeight(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getUpHeight(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return heightData->mHeights[(col - 1) * ESM::Land::LAND_SIZE + row]
|
return heightData[(col - 1) * ESM::Land::LAND_SIZE + row]
|
||||||
+ mAlteredHeight[static_cast<unsigned int>((col - 1) * ESM::Land::LAND_SIZE + row)];
|
+ mAlteredHeight[static_cast<unsigned int>((col - 1) * ESM::Land::LAND_SIZE + row)];
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getDownHeight(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getDownHeight(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return heightData->mHeights[(col + 1) * ESM::Land::LAND_SIZE + row]
|
return heightData[(col + 1) * ESM::Land::LAND_SIZE + row]
|
||||||
+ mAlteredHeight[static_cast<unsigned int>((col + 1) * ESM::Land::LAND_SIZE + row)];
|
+ mAlteredHeight[static_cast<unsigned int>((col + 1) * ESM::Land::LAND_SIZE + row)];
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getHeightDifferenceToLeft(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return abs(getThisHeight(col, row, heightData) - getLeftHeight(col, row, heightData));
|
return abs(getThisHeight(col, row, heightData) - getLeftHeight(col, row, heightData));
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getHeightDifferenceToRight(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return abs(getThisHeight(col, row, heightData) - getRightHeight(col, row, heightData));
|
return abs(getThisHeight(col, row, heightData) - getRightHeight(col, row, heightData));
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getHeightDifferenceToUp(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return abs(getThisHeight(col, row, heightData) - getUpHeight(col, row, heightData));
|
return abs(getThisHeight(col, row, heightData) - getUpHeight(col, row, heightData));
|
||||||
}
|
}
|
||||||
|
|
||||||
int TerrainStorage::getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData* heightData) const
|
int TerrainStorage::getHeightDifferenceToDown(int col, int row, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData));
|
return abs(getThisHeight(col, row, heightData) - getDownHeight(col, row, heightData));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerrainStorage::leftOrUpIsOverTheLimit(
|
bool TerrainStorage::leftOrUpIsOverTheLimit(
|
||||||
int col, int row, int heightWarningLimit, const ESM::Land::LandData* heightData) const
|
int col, int row, int heightWarningLimit, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit
|
return getHeightDifferenceToLeft(col, row, heightData) >= heightWarningLimit
|
||||||
|| getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit;
|
|| getHeightDifferenceToUp(col, row, heightData) >= heightWarningLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerrainStorage::rightOrDownIsOverTheLimit(
|
bool TerrainStorage::rightOrDownIsOverTheLimit(
|
||||||
int col, int row, int heightWarningLimit, const ESM::Land::LandData* heightData) const
|
int col, int row, int heightWarningLimit, std::span<const float> heightData) const
|
||||||
{
|
{
|
||||||
return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit
|
return getHeightDifferenceToRight(col, row, heightData) >= heightWarningLimit
|
||||||
|| getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit;
|
|| getHeightDifferenceToDown(col, row, heightData) >= heightWarningLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainStorage::adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const
|
void TerrainStorage::adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const
|
||||||
{
|
{
|
||||||
// Highlight broken height changes
|
// Highlight broken height changes
|
||||||
int heightWarningLimit = 1024;
|
int heightWarningLimit = 1024;
|
||||||
if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData))
|
if (((col > 0 && row > 0) && leftOrUpIsOverTheLimit(col, row, heightWarningLimit, heightData->getHeights()))
|
||||||
|| ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1)
|
|| ((col < ESM::Land::LAND_SIZE - 1 && row < ESM::Land::LAND_SIZE - 1)
|
||||||
&& rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData)))
|
&& rightOrDownIsOverTheLimit(col, row, heightWarningLimit, heightData->getHeights())))
|
||||||
{
|
{
|
||||||
color.r() = 255;
|
color.r() = 255;
|
||||||
color.g() = 0;
|
color.g() = 0;
|
||||||
|
|
|
@ -37,26 +37,25 @@ namespace CSVRender
|
||||||
const CSMWorld::Data& mData;
|
const CSMWorld::Data& mData;
|
||||||
std::array<float, ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE> mAlteredHeight;
|
std::array<float, ESM::Land::LAND_SIZE * ESM::Land::LAND_SIZE> mAlteredHeight;
|
||||||
|
|
||||||
osg::ref_ptr<const ESMTerrain::LandObject> getLand(int cellX, int cellY) override;
|
osg::ref_ptr<const ESMTerrain::LandObject> getLand(ESM::ExteriorCellLocation cellLocation) override;
|
||||||
const ESM::LandTexture* getLandTexture(int index, short plugin) override;
|
const ESM::LandTexture* getLandTexture(int index, short plugin) override;
|
||||||
|
|
||||||
void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;
|
void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) override;
|
||||||
|
|
||||||
int getThisHeight(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getThisHeight(int col, int row, std::span<const float> heightData) const;
|
||||||
int getLeftHeight(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getLeftHeight(int col, int row, std::span<const float> heightData) const;
|
||||||
int getRightHeight(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getRightHeight(int col, int row, std::span<const float> heightData) const;
|
||||||
int getUpHeight(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getUpHeight(int col, int row, std::span<const float> heightData) const;
|
||||||
int getDownHeight(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getDownHeight(int col, int row, std::span<const float> heightData) const;
|
||||||
int getHeightDifferenceToLeft(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getHeightDifferenceToLeft(int col, int row, std::span<const float> heightData) const;
|
||||||
int getHeightDifferenceToRight(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getHeightDifferenceToRight(int col, int row, std::span<const float> heightData) const;
|
||||||
int getHeightDifferenceToUp(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getHeightDifferenceToUp(int col, int row, std::span<const float> heightData) const;
|
||||||
int getHeightDifferenceToDown(int col, int row, const ESM::Land::LandData* heightData) const;
|
int getHeightDifferenceToDown(int col, int row, std::span<const float> heightData) const;
|
||||||
bool leftOrUpIsOverTheLimit(
|
bool leftOrUpIsOverTheLimit(int col, int row, int heightWarningLimit, std::span<const float> heightData) const;
|
||||||
int col, int row, int heightWarningLimit, const ESM::Land::LandData* heightData) const;
|
|
||||||
bool rightOrDownIsOverTheLimit(
|
bool rightOrDownIsOverTheLimit(
|
||||||
int col, int row, int heightWarningLimit, const ESM::Land::LandData* heightData) const;
|
int col, int row, int heightWarningLimit, std::span<const float> heightData) const;
|
||||||
|
|
||||||
void adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const override;
|
void adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const override;
|
||||||
float getAlteredHeight(int col, int row) const override;
|
float getAlteredHeight(int col, int row) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -572,7 +572,7 @@ namespace MWBase
|
||||||
virtual void rotateWorldObject(const MWWorld::Ptr& ptr, const osg::Quat& rotate) = 0;
|
virtual void rotateWorldObject(const MWWorld::Ptr& ptr, const osg::Quat& rotate) = 0;
|
||||||
|
|
||||||
/// Return terrain height at \a worldPos position.
|
/// Return terrain height at \a worldPos position.
|
||||||
virtual float getTerrainHeightAt(const osg::Vec3f& worldPos) const = 0;
|
virtual float getTerrainHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) const = 0;
|
||||||
|
|
||||||
/// Return physical or rendering half extents of the given actor.
|
/// Return physical or rendering half extents of the given actor.
|
||||||
virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering = false) const = 0;
|
virtual osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering = false) const = 0;
|
||||||
|
|
|
@ -474,7 +474,8 @@ namespace MWMechanics
|
||||||
if (enemy.getCell()->isExterior())
|
if (enemy.getCell()->isExterior())
|
||||||
{
|
{
|
||||||
if (attackDistance < (enemyPos.pos[2]
|
if (attackDistance < (enemyPos.pos[2]
|
||||||
- MWBase::Environment::get().getWorld()->getTerrainHeightAt(enemyPos.asVec3())))
|
- MWBase::Environment::get().getWorld()->getTerrainHeightAt(
|
||||||
|
enemyPos.asVec3(), enemy.getCell()->getCell()->getWorldSpace())))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
LandManager::LandManager(int loadFlags)
|
LandManager::LandManager(int loadFlags)
|
||||||
: GenericResourceManager<std::pair<int, int>>(nullptr)
|
: GenericResourceManager<ESM::ExteriorCellLocation>(nullptr)
|
||||||
, mLoadFlags(loadFlags)
|
, mLoadFlags(loadFlags)
|
||||||
{
|
{
|
||||||
mCache = new CacheType;
|
mCache = new CacheType;
|
||||||
|
@ -20,11 +20,7 @@ namespace MWRender
|
||||||
|
|
||||||
osg::ref_ptr<ESMTerrain::LandObject> LandManager::getLand(ESM::ExteriorCellLocation cellIndex)
|
osg::ref_ptr<ESMTerrain::LandObject> LandManager::getLand(ESM::ExteriorCellLocation cellIndex)
|
||||||
{
|
{
|
||||||
if (ESM::isEsm4Ext(cellIndex.mWorldspace))
|
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(cellIndex);
|
||||||
return osg::ref_ptr<ESMTerrain::LandObject>(nullptr);
|
|
||||||
int x = cellIndex.mX;
|
|
||||||
int y = cellIndex.mY;
|
|
||||||
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(std::make_pair(x, y));
|
|
||||||
if (obj)
|
if (obj)
|
||||||
return static_cast<ESMTerrain::LandObject*>(obj.get());
|
return static_cast<ESMTerrain::LandObject*>(obj.get());
|
||||||
else
|
else
|
||||||
|
@ -32,12 +28,25 @@ namespace MWRender
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
const auto world = MWBase::Environment::get().getWorld();
|
||||||
if (!world)
|
if (!world)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
const ESM::Land* land = world->getStore().get<ESM::Land>().search(x, y);
|
|
||||||
if (!land)
|
if (ESM::isEsm4Ext(cellIndex.mWorldspace))
|
||||||
return nullptr;
|
{
|
||||||
osg::ref_ptr<ESMTerrain::LandObject> landObj(new ESMTerrain::LandObject(land, mLoadFlags));
|
const ESM4::Land* land = world->getStore().get<ESM4::Land>().search(cellIndex);
|
||||||
mCache->addEntryToObjectCache(std::make_pair(x, y), landObj.get());
|
if (!land)
|
||||||
return landObj;
|
return nullptr;
|
||||||
|
osg::ref_ptr<ESMTerrain::LandObject> landObj(new ESMTerrain::LandObject(land, mLoadFlags));
|
||||||
|
mCache->addEntryToObjectCache(cellIndex, landObj.get());
|
||||||
|
return landObj;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const ESM::Land* land = world->getStore().get<ESM::Land>().search(cellIndex.mX, cellIndex.mY);
|
||||||
|
if (!land)
|
||||||
|
return nullptr;
|
||||||
|
osg::ref_ptr<ESMTerrain::LandObject> landObj(new ESMTerrain::LandObject(land, mLoadFlags));
|
||||||
|
mCache->addEntryToObjectCache(cellIndex, landObj.get());
|
||||||
|
return landObj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace ESM
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
class LandManager : public Resource::GenericResourceManager<std::pair<int, int>>
|
class LandManager : public Resource::GenericResourceManager<ESM::ExteriorCellLocation>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LandManager(int loadFlags);
|
LandManager(int loadFlags);
|
||||||
|
|
|
@ -321,6 +321,7 @@ namespace MWRender
|
||||||
, mFieldOfViewOverride(0.f)
|
, mFieldOfViewOverride(0.f)
|
||||||
, mFieldOfView(Settings::camera().mFieldOfView)
|
, mFieldOfView(Settings::camera().mFieldOfView)
|
||||||
, mFirstPersonFieldOfView(Settings::camera().mFirstPersonFieldOfView)
|
, mFirstPersonFieldOfView(Settings::camera().mFirstPersonFieldOfView)
|
||||||
|
, mGroundCoverStore(groundcoverStore)
|
||||||
{
|
{
|
||||||
bool reverseZ = SceneUtil::AutoDepth::isReversed();
|
bool reverseZ = SceneUtil::AutoDepth::isReversed();
|
||||||
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(
|
auto lightingMethod = SceneUtil::LightManager::getLightingMethodFromString(
|
||||||
|
@ -457,46 +458,11 @@ namespace MWRender
|
||||||
|
|
||||||
mTerrainStorage = std::make_unique<TerrainStorage>(mResourceSystem, normalMapPattern, heightMapPattern,
|
mTerrainStorage = std::make_unique<TerrainStorage>(mResourceSystem, normalMapPattern, heightMapPattern,
|
||||||
useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps);
|
useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps);
|
||||||
const float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain");
|
|
||||||
|
|
||||||
bool groundcover = Settings::Manager::getBool("enabled", "Groundcover");
|
WorldspaceChunkMgr& chunkMgr = getWorldspaceChunkMgr(ESM::Cell::sDefaultWorldspaceId);
|
||||||
bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
|
mTerrain = chunkMgr.mTerrain.get();
|
||||||
if (distantTerrain || groundcover)
|
mGroundcover = chunkMgr.mGroundcover.get();
|
||||||
{
|
mObjectPaging = chunkMgr.mObjectPaging.get();
|
||||||
const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
|
|
||||||
int compMapPower = Settings::Manager::getInt("composite map level", "Terrain");
|
|
||||||
compMapPower = std::max(-3, compMapPower);
|
|
||||||
float compMapLevel = pow(2, compMapPower);
|
|
||||||
const int vertexLodMod = Settings::Manager::getInt("vertex lod mod", "Terrain");
|
|
||||||
float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain");
|
|
||||||
maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f);
|
|
||||||
bool debugChunks = Settings::Manager::getBool("debug chunks", "Terrain");
|
|
||||||
mTerrain = std::make_unique<Terrain::QuadTreeWorld>(sceneRoot, mRootNode, mResourceSystem,
|
|
||||||
mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel,
|
|
||||||
lodFactor, vertexLodMod, maxCompGeometrySize, debugChunks);
|
|
||||||
if (Settings::Manager::getBool("object paging", "Terrain"))
|
|
||||||
{
|
|
||||||
mObjectPaging = std::make_unique<ObjectPaging>(mResourceSystem->getSceneManager());
|
|
||||||
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mObjectPaging.get());
|
|
||||||
mResourceSystem->addResourceManager(mObjectPaging.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
mTerrain = std::make_unique<Terrain::TerrainGrid>(sceneRoot, mRootNode, mResourceSystem,
|
|
||||||
mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug);
|
|
||||||
|
|
||||||
mTerrain->setTargetFrameRate(Settings::cells().mTargetFramerate);
|
|
||||||
|
|
||||||
if (groundcover)
|
|
||||||
{
|
|
||||||
float density = Settings::Manager::getFloat("density", "Groundcover");
|
|
||||||
density = std::clamp(density, 0.f, 1.f);
|
|
||||||
|
|
||||||
mGroundcover = std::make_unique<Groundcover>(
|
|
||||||
mResourceSystem->getSceneManager(), density, groundcoverDistance, groundcoverStore);
|
|
||||||
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mGroundcover.get());
|
|
||||||
mResourceSystem->addResourceManager(mGroundcover.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
mStateUpdater = new StateUpdater;
|
mStateUpdater = new StateUpdater;
|
||||||
sceneRoot->addUpdateCallback(mStateUpdater);
|
sceneRoot->addUpdateCallback(mStateUpdater);
|
||||||
|
@ -633,7 +599,7 @@ namespace MWRender
|
||||||
|
|
||||||
Terrain::World* RenderingManager::getTerrain()
|
Terrain::World* RenderingManager::getTerrain()
|
||||||
{
|
{
|
||||||
return mTerrain.get();
|
return mTerrain;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::preloadCommonAssets()
|
void RenderingManager::preloadCommonAssets()
|
||||||
|
@ -771,6 +737,7 @@ namespace MWRender
|
||||||
|
|
||||||
if (store->getCell()->isExterior())
|
if (store->getCell()->isExterior())
|
||||||
{
|
{
|
||||||
|
enableTerrain(true, store->getCell()->getWorldSpace());
|
||||||
mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
|
mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -782,16 +749,28 @@ namespace MWRender
|
||||||
|
|
||||||
if (store->getCell()->isExterior())
|
if (store->getCell()->isExterior())
|
||||||
{
|
{
|
||||||
mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
|
getWorldspaceChunkMgr(store->getCell()->getWorldSpace())
|
||||||
|
.mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
|
||||||
}
|
}
|
||||||
|
|
||||||
mWater->removeCell(store);
|
mWater->removeCell(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::enableTerrain(bool enable)
|
void RenderingManager::enableTerrain(bool enable, ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
if (!enable)
|
if (!enable)
|
||||||
mWater->setCullCallback(nullptr);
|
mWater->setCullCallback(nullptr);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WorldspaceChunkMgr& newChunks = getWorldspaceChunkMgr(worldspace);
|
||||||
|
if (newChunks.mTerrain.get() != mTerrain)
|
||||||
|
{
|
||||||
|
mTerrain->enable(false);
|
||||||
|
mTerrain = newChunks.mTerrain.get();
|
||||||
|
mGroundcover = newChunks.mGroundcover.get();
|
||||||
|
mObjectPaging = newChunks.mObjectPaging.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
mTerrain->enable(enable);
|
mTerrain->enable(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1343,6 +1322,60 @@ namespace MWRender
|
||||||
mStateUpdater->setFogColor(color);
|
mStateUpdater->setFogColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderingManager::WorldspaceChunkMgr& RenderingManager::getWorldspaceChunkMgr(ESM::RefId worldspace)
|
||||||
|
{
|
||||||
|
auto existingChunkMgr = mWorldspaceChunks.find(worldspace);
|
||||||
|
if (existingChunkMgr != mWorldspaceChunks.end())
|
||||||
|
return existingChunkMgr->second;
|
||||||
|
RenderingManager::WorldspaceChunkMgr newChunkMgr;
|
||||||
|
|
||||||
|
const float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain");
|
||||||
|
bool groundcover = Settings::Manager::getBool("enabled", "Groundcover");
|
||||||
|
bool distantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
|
||||||
|
if (distantTerrain || groundcover)
|
||||||
|
{
|
||||||
|
const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
|
||||||
|
int compMapPower = Settings::Manager::getInt("composite map level", "Terrain");
|
||||||
|
compMapPower = std::max(-3, compMapPower);
|
||||||
|
float compMapLevel = pow(2, compMapPower);
|
||||||
|
const int vertexLodMod = Settings::Manager::getInt("vertex lod mod", "Terrain");
|
||||||
|
float maxCompGeometrySize = Settings::Manager::getFloat("max composite geometry size", "Terrain");
|
||||||
|
maxCompGeometrySize = std::max(maxCompGeometrySize, 1.f);
|
||||||
|
bool debugChunks = Settings::Manager::getBool("debug chunks", "Terrain");
|
||||||
|
auto quadTreeWorld = std::make_unique<Terrain::QuadTreeWorld>(mSceneRoot, mRootNode, mResourceSystem,
|
||||||
|
mTerrainStorage.get(), Mask_Terrain, Mask_PreCompile, Mask_Debug, compMapResolution, compMapLevel,
|
||||||
|
lodFactor, vertexLodMod, maxCompGeometrySize, debugChunks, worldspace);
|
||||||
|
if (Settings::Manager::getBool("object paging", "Terrain"))
|
||||||
|
{
|
||||||
|
newChunkMgr.mObjectPaging = std::make_unique<ObjectPaging>(mResourceSystem->getSceneManager());
|
||||||
|
quadTreeWorld->addChunkManager(newChunkMgr.mObjectPaging.get());
|
||||||
|
mResourceSystem->addResourceManager(newChunkMgr.mObjectPaging.get());
|
||||||
|
}
|
||||||
|
if (groundcover)
|
||||||
|
{
|
||||||
|
float groundcoverDistance
|
||||||
|
= std::max(0.f, Settings::Manager::getFloat("rendering distance", "Groundcover"));
|
||||||
|
float density = Settings::Manager::getFloat("density", "Groundcover");
|
||||||
|
density = std::clamp(density, 0.f, 1.f);
|
||||||
|
|
||||||
|
newChunkMgr.mGroundcover = std::make_unique<Groundcover>(
|
||||||
|
mResourceSystem->getSceneManager(), density, groundcoverDistance, mGroundCoverStore);
|
||||||
|
quadTreeWorld->addChunkManager(newChunkMgr.mGroundcover.get());
|
||||||
|
mResourceSystem->addResourceManager(newChunkMgr.mGroundcover.get());
|
||||||
|
}
|
||||||
|
newChunkMgr.mTerrain = std::move(quadTreeWorld);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
newChunkMgr.mTerrain = std::make_unique<Terrain::TerrainGrid>(mSceneRoot, mRootNode, mResourceSystem,
|
||||||
|
mTerrainStorage.get(), Mask_Terrain, worldspace, Mask_PreCompile, Mask_Debug);
|
||||||
|
|
||||||
|
newChunkMgr.mTerrain->setTargetFrameRate(Settings::cells().mTargetFramerate);
|
||||||
|
float distanceMult = std::cos(osg::DegreesToRadians(std::min(mFieldOfView, 140.f)) / 2.f);
|
||||||
|
newChunkMgr.mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f / distanceMult : 1.f));
|
||||||
|
|
||||||
|
return mWorldspaceChunks.emplace(worldspace, std::move(newChunkMgr)).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
void RenderingManager::reportStats() const
|
void RenderingManager::reportStats() const
|
||||||
{
|
{
|
||||||
osg::Stats* stats = mViewer->getViewerStats();
|
osg::Stats* stats = mViewer->getViewerStats();
|
||||||
|
@ -1444,9 +1477,9 @@ namespace MWRender
|
||||||
updateProjectionMatrix();
|
updateProjectionMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
float RenderingManager::getTerrainHeightAt(const osg::Vec3f& pos)
|
float RenderingManager::getTerrainHeightAt(const osg::Vec3f& pos, ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
return mTerrain->getHeightAt(pos);
|
return getWorldspaceChunkMgr(worldspace).mTerrain->getHeightAt(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::overrideFieldOfView(float val)
|
void RenderingManager::overrideFieldOfView(float val)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
|
@ -150,7 +151,7 @@ namespace MWRender
|
||||||
void addCell(const MWWorld::CellStore* store);
|
void addCell(const MWWorld::CellStore* store);
|
||||||
void removeCell(const MWWorld::CellStore* store);
|
void removeCell(const MWWorld::CellStore* store);
|
||||||
|
|
||||||
void enableTerrain(bool enable);
|
void enableTerrain(bool enable, ESM::RefId worldspace);
|
||||||
|
|
||||||
void updatePtr(const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
|
void updatePtr(const MWWorld::Ptr& old, const MWWorld::Ptr& updated);
|
||||||
|
|
||||||
|
@ -230,7 +231,7 @@ namespace MWRender
|
||||||
|
|
||||||
void setViewDistance(float distance, bool delay = false);
|
void setViewDistance(float distance, bool delay = false);
|
||||||
|
|
||||||
float getTerrainHeightAt(const osg::Vec3f& pos);
|
float getTerrainHeightAt(const osg::Vec3f& pos, ESM::RefId worldspace);
|
||||||
|
|
||||||
// camera stuff
|
// camera stuff
|
||||||
Camera* getCamera() { return mCamera.get(); }
|
Camera* getCamera() { return mCamera.get(); }
|
||||||
|
@ -282,6 +283,15 @@ namespace MWRender
|
||||||
void setFogColor(const osg::Vec4f& color);
|
void setFogColor(const osg::Vec4f& color);
|
||||||
void updateThirdPersonViewMode();
|
void updateThirdPersonViewMode();
|
||||||
|
|
||||||
|
struct WorldspaceChunkMgr
|
||||||
|
{
|
||||||
|
std::unique_ptr<Terrain::World> mTerrain;
|
||||||
|
std::unique_ptr<ObjectPaging> mObjectPaging;
|
||||||
|
std::unique_ptr<Groundcover> mGroundcover;
|
||||||
|
};
|
||||||
|
|
||||||
|
WorldspaceChunkMgr& getWorldspaceChunkMgr(ESM::RefId worldspace);
|
||||||
|
|
||||||
void reportStats() const;
|
void reportStats() const;
|
||||||
|
|
||||||
void updateNavMesh();
|
void updateNavMesh();
|
||||||
|
@ -312,10 +322,11 @@ namespace MWRender
|
||||||
std::unique_ptr<Pathgrid> mPathgrid;
|
std::unique_ptr<Pathgrid> mPathgrid;
|
||||||
std::unique_ptr<Objects> mObjects;
|
std::unique_ptr<Objects> mObjects;
|
||||||
std::unique_ptr<Water> mWater;
|
std::unique_ptr<Water> mWater;
|
||||||
std::unique_ptr<Terrain::World> mTerrain;
|
std::unordered_map<ESM::RefId, WorldspaceChunkMgr> mWorldspaceChunks;
|
||||||
|
Terrain::World* mTerrain;
|
||||||
std::unique_ptr<TerrainStorage> mTerrainStorage;
|
std::unique_ptr<TerrainStorage> mTerrainStorage;
|
||||||
std::unique_ptr<ObjectPaging> mObjectPaging;
|
ObjectPaging* mObjectPaging;
|
||||||
std::unique_ptr<Groundcover> mGroundcover;
|
Groundcover* mGroundcover;
|
||||||
std::unique_ptr<SkyManager> mSky;
|
std::unique_ptr<SkyManager> mSky;
|
||||||
std::unique_ptr<FogManager> mFog;
|
std::unique_ptr<FogManager> mFog;
|
||||||
std::unique_ptr<ScreenshotManager> mScreenshotManager;
|
std::unique_ptr<ScreenshotManager> mScreenshotManager;
|
||||||
|
@ -343,6 +354,7 @@ namespace MWRender
|
||||||
float mFirstPersonFieldOfView;
|
float mFirstPersonFieldOfView;
|
||||||
bool mUpdateProjectionMatrix = false;
|
bool mUpdateProjectionMatrix = false;
|
||||||
bool mNight = false;
|
bool mNight = false;
|
||||||
|
const MWWorld::GroundcoverStore& mGroundCoverStore;
|
||||||
|
|
||||||
void operator=(const RenderingManager&);
|
void operator=(const RenderingManager&);
|
||||||
RenderingManager(const RenderingManager&);
|
RenderingManager(const RenderingManager&);
|
||||||
|
|
|
@ -26,15 +26,33 @@ namespace MWRender
|
||||||
mResourceSystem->removeResourceManager(mLandManager.get());
|
mResourceSystem->removeResourceManager(mLandManager.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerrainStorage::hasData(int cellX, int cellY)
|
bool TerrainStorage::hasData(ESM::ExteriorCellLocation cellLocation)
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
||||||
|
|
||||||
const ESM::Land* land = esmStore.get<ESM::Land>().search(cellX, cellY);
|
if (ESM::isEsm4Ext(cellLocation.mWorldspace))
|
||||||
return land != nullptr;
|
{
|
||||||
|
return esmStore.get<ESM4::Land>().search(cellLocation) != nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return esmStore.get<ESM::Land>().search(cellLocation.mX, cellLocation.mY) != nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
|
static void BoundUnion(float& minX, float& maxX, float& minY, float& maxY, float x, float y)
|
||||||
|
{
|
||||||
|
if (x < minX)
|
||||||
|
minX = x;
|
||||||
|
if (x > maxX)
|
||||||
|
maxX = x;
|
||||||
|
if (y < minY)
|
||||||
|
minY = y;
|
||||||
|
if (y > maxY)
|
||||||
|
maxY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
minX = 0;
|
minX = 0;
|
||||||
minY = 0;
|
minY = 0;
|
||||||
|
@ -43,19 +61,25 @@ namespace MWRender
|
||||||
|
|
||||||
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
|
||||||
|
|
||||||
MWWorld::Store<ESM::Land>::iterator it = esmStore.get<ESM::Land>().begin();
|
if (ESM::isEsm4Ext(worldspace))
|
||||||
for (; it != esmStore.get<ESM::Land>().end(); ++it)
|
|
||||||
{
|
{
|
||||||
if (it->mX < minX)
|
const auto& lands = esmStore.get<ESM4::Land>().getLands();
|
||||||
minX = static_cast<float>(it->mX);
|
for (const auto& [landPos, _] : lands)
|
||||||
if (it->mX > maxX)
|
{
|
||||||
maxX = static_cast<float>(it->mX);
|
if (landPos.mWorldspace == worldspace)
|
||||||
if (it->mY < minY)
|
{
|
||||||
minY = static_cast<float>(it->mY);
|
BoundUnion(minX, maxX, minY, maxY, static_cast<float>(landPos.mX), static_cast<float>(landPos.mY));
|
||||||
if (it->mY > maxY)
|
}
|
||||||
maxY = static_cast<float>(it->mY);
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MWWorld::Store<ESM::Land>::iterator it = esmStore.get<ESM::Land>().begin();
|
||||||
|
for (; it != esmStore.get<ESM::Land>().end(); ++it)
|
||||||
|
{
|
||||||
|
BoundUnion(minX, maxX, minY, maxY, static_cast<float>(it->mX), static_cast<float>(it->mY));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// since grid coords are at cell origin, we need to add 1 cell
|
// since grid coords are at cell origin, we need to add 1 cell
|
||||||
maxX += 1;
|
maxX += 1;
|
||||||
maxY += 1;
|
maxY += 1;
|
||||||
|
@ -66,9 +90,9 @@ namespace MWRender
|
||||||
return mLandManager.get();
|
return mLandManager.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(int cellX, int cellY)
|
osg::ref_ptr<const ESMTerrain::LandObject> TerrainStorage::getLand(ESM::ExteriorCellLocation cellLocation)
|
||||||
{
|
{
|
||||||
return mLandManager->getLand(ESM::ExteriorCellLocation(cellX, cellY, ESM::Cell::sDefaultWorldspaceId));
|
return mLandManager->getLand(cellLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
|
const ESM::LandTexture* TerrainStorage::getLandTexture(int index, short plugin)
|
||||||
|
|
|
@ -21,13 +21,13 @@ namespace MWRender
|
||||||
const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
|
const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
|
||||||
~TerrainStorage();
|
~TerrainStorage();
|
||||||
|
|
||||||
osg::ref_ptr<const ESMTerrain::LandObject> getLand(int cellX, int cellY) override;
|
osg::ref_ptr<const ESMTerrain::LandObject> getLand(ESM::ExteriorCellLocation cellLocation) override;
|
||||||
const ESM::LandTexture* getLandTexture(int index, short plugin) override;
|
const ESM::LandTexture* getLandTexture(int index, short plugin) override;
|
||||||
|
|
||||||
bool hasData(int cellX, int cellY) override;
|
bool hasData(ESM::ExteriorCellLocation cellLocation) override;
|
||||||
|
|
||||||
/// Get bounds of the whole terrain in cell units
|
/// Get bounds of the whole terrain in cell units
|
||||||
void getBounds(float& minX, float& maxX, float& minY, float& maxY) override;
|
void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) override;
|
||||||
|
|
||||||
LandManager* getLandManager() const;
|
LandManager* getLandManager() const;
|
||||||
|
|
||||||
|
|
|
@ -316,7 +316,8 @@ namespace MWScript
|
||||||
{
|
{
|
||||||
float terrainHeight = -std::numeric_limits<float>::max();
|
float terrainHeight = -std::numeric_limits<float>::max();
|
||||||
if (ptr.getCell()->isExterior())
|
if (ptr.getCell()->isExterior())
|
||||||
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(curPos);
|
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(
|
||||||
|
curPos, ptr.getCell()->getCell()->getWorldSpace());
|
||||||
|
|
||||||
if (pos < terrainHeight)
|
if (pos < terrainHeight)
|
||||||
pos = terrainHeight;
|
pos = terrainHeight;
|
||||||
|
|
|
@ -53,7 +53,8 @@ namespace MWSound
|
||||||
{
|
{
|
||||||
const float terrainX = pos.x() - mSettings.mNearWaterRadius + x * step;
|
const float terrainX = pos.x() - mSettings.mNearWaterRadius + x * step;
|
||||||
const float terrainY = pos.y() - mSettings.mNearWaterRadius + y * step;
|
const float terrainY = pos.y() - mSettings.mNearWaterRadius + y * step;
|
||||||
const float height = world.getTerrainHeightAt(osg::Vec3f(terrainX, terrainY, 0.0f));
|
const float height = world.getTerrainHeightAt(
|
||||||
|
osg::Vec3f(terrainX, terrainY, 0.0f), cell.getCell()->getWorldSpace());
|
||||||
|
|
||||||
if (height < 0)
|
if (height < 0)
|
||||||
underwaterPoints++;
|
underwaterPoints++;
|
||||||
|
|
|
@ -238,26 +238,7 @@ namespace MWWorld
|
||||||
|
|
||||||
CellPreloader::~CellPreloader()
|
CellPreloader::~CellPreloader()
|
||||||
{
|
{
|
||||||
if (mTerrainPreloadItem)
|
clearAllTasks();
|
||||||
{
|
|
||||||
mTerrainPreloadItem->abort();
|
|
||||||
mTerrainPreloadItem->waitTillDone();
|
|
||||||
mTerrainPreloadItem = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mUpdateCacheItem)
|
|
||||||
{
|
|
||||||
mUpdateCacheItem->waitTillDone();
|
|
||||||
mUpdateCacheItem = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end(); ++it)
|
|
||||||
it->second.mWorkItem->abort();
|
|
||||||
|
|
||||||
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end(); ++it)
|
|
||||||
it->second.mWorkItem->waitTillDone();
|
|
||||||
|
|
||||||
mPreloadCells.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellPreloader::preload(CellStore& cell, double timestamp)
|
void CellPreloader::preload(CellStore& cell, double timestamp)
|
||||||
|
@ -458,4 +439,37 @@ namespace MWWorld
|
||||||
&& contains(mLoadedTerrainPositions, std::array{ position }, ESM::Land::REAL_SIZE);
|
&& contains(mLoadedTerrainPositions, std::array{ position }, ESM::Land::REAL_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CellPreloader::setTerrain(Terrain::World* terrain)
|
||||||
|
{
|
||||||
|
if (terrain != mTerrain)
|
||||||
|
{
|
||||||
|
clearAllTasks();
|
||||||
|
mTerrain = terrain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CellPreloader::clearAllTasks()
|
||||||
|
{
|
||||||
|
if (mTerrainPreloadItem)
|
||||||
|
{
|
||||||
|
mTerrainPreloadItem->abort();
|
||||||
|
mTerrainPreloadItem->waitTillDone();
|
||||||
|
mTerrainPreloadItem = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mUpdateCacheItem)
|
||||||
|
{
|
||||||
|
mUpdateCacheItem->waitTillDone();
|
||||||
|
mUpdateCacheItem = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end(); ++it)
|
||||||
|
it->second.mWorkItem->abort();
|
||||||
|
|
||||||
|
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end(); ++it)
|
||||||
|
it->second.mWorkItem->waitTillDone();
|
||||||
|
|
||||||
|
mPreloadCells.clear();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,8 +74,11 @@ namespace MWWorld
|
||||||
void syncTerrainLoad(Loading::Listener& listener);
|
void syncTerrainLoad(Loading::Listener& listener);
|
||||||
void abortTerrainPreloadExcept(const PositionCellGrid* exceptPos);
|
void abortTerrainPreloadExcept(const PositionCellGrid* exceptPos);
|
||||||
bool isTerrainLoaded(const CellPreloader::PositionCellGrid& position, double referenceTime) const;
|
bool isTerrainLoaded(const CellPreloader::PositionCellGrid& position, double referenceTime) const;
|
||||||
|
void setTerrain(Terrain::World* terrain);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void clearAllTasks();
|
||||||
|
|
||||||
Resource::ResourceSystem* mResourceSystem;
|
Resource::ResourceSystem* mResourceSystem;
|
||||||
Resource::BulletShapeManager* mBulletShapeManager;
|
Resource::BulletShapeManager* mBulletShapeManager;
|
||||||
Terrain::World* mTerrain;
|
Terrain::World* mTerrain;
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <components/misc/algorithm.hpp>
|
#include <components/misc/algorithm.hpp>
|
||||||
|
|
||||||
#include <components/esm4/common.hpp>
|
#include <components/esm4/common.hpp>
|
||||||
|
#include <components/esm4/loadland.hpp>
|
||||||
#include <components/esm4/loadwrld.hpp>
|
#include <components/esm4/loadwrld.hpp>
|
||||||
#include <components/esm4/reader.hpp>
|
#include <components/esm4/reader.hpp>
|
||||||
#include <components/esm4/readerutils.hpp>
|
#include <components/esm4/readerutils.hpp>
|
||||||
|
@ -447,6 +448,7 @@ namespace MWWorld
|
||||||
getWritable<ESM::Skill>().setUp();
|
getWritable<ESM::Skill>().setUp();
|
||||||
getWritable<ESM::MagicEffect>().setUp();
|
getWritable<ESM::MagicEffect>().setUp();
|
||||||
getWritable<ESM::Attribute>().setUp();
|
getWritable<ESM::Attribute>().setUp();
|
||||||
|
getWritable<ESM4::Land>().updateLandPositions(get<ESM4::Cell>());
|
||||||
getWritable<ESM4::Reference>().preprocessReferences(get<ESM4::Cell>());
|
getWritable<ESM4::Reference>().preprocessReferences(get<ESM4::Cell>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ namespace ESM4
|
||||||
struct MiscItem;
|
struct MiscItem;
|
||||||
struct Weapon;
|
struct Weapon;
|
||||||
struct World;
|
struct World;
|
||||||
|
struct Land;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
|
@ -121,7 +122,7 @@ namespace MWWorld
|
||||||
Store<ESM4::Static>, Store<ESM4::Cell>, Store<ESM4::Light>, Store<ESM4::Reference>, Store<ESM4::Activator>,
|
Store<ESM4::Static>, Store<ESM4::Cell>, Store<ESM4::Light>, Store<ESM4::Reference>, Store<ESM4::Activator>,
|
||||||
Store<ESM4::Potion>, Store<ESM4::Ammunition>, Store<ESM4::Armor>, Store<ESM4::Book>, Store<ESM4::Clothing>,
|
Store<ESM4::Potion>, Store<ESM4::Ammunition>, Store<ESM4::Armor>, Store<ESM4::Book>, Store<ESM4::Clothing>,
|
||||||
Store<ESM4::Container>, Store<ESM4::Door>, Store<ESM4::Ingredient>, Store<ESM4::MiscItem>,
|
Store<ESM4::Container>, Store<ESM4::Door>, Store<ESM4::Ingredient>, Store<ESM4::MiscItem>,
|
||||||
Store<ESM4::Weapon>, Store<ESM4::World>>;
|
Store<ESM4::Weapon>, Store<ESM4::World>, Store<ESM4::Land>>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <components/detournavigator/heightfieldshape.hpp>
|
#include <components/detournavigator/heightfieldshape.hpp>
|
||||||
#include <components/detournavigator/navigator.hpp>
|
#include <components/detournavigator/navigator.hpp>
|
||||||
#include <components/detournavigator/updateguard.hpp>
|
#include <components/detournavigator/updateguard.hpp>
|
||||||
|
#include <components/esm/esmterrain.hpp>
|
||||||
#include <components/esm/records.hpp>
|
#include <components/esm/records.hpp>
|
||||||
#include <components/esm3/loadcell.hpp>
|
#include <components/esm3/loadcell.hpp>
|
||||||
#include <components/loadinglistener/loadinglistener.hpp>
|
#include <components/loadinglistener/loadinglistener.hpp>
|
||||||
|
@ -397,15 +398,16 @@ namespace MWWorld
|
||||||
if (cellVariant.isExterior())
|
if (cellVariant.isExterior())
|
||||||
{
|
{
|
||||||
osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellIndex);
|
osg::ref_ptr<const ESMTerrain::LandObject> land = mRendering.getLandManager()->getLand(cellIndex);
|
||||||
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
|
||||||
const int verts = ESM::Land::LAND_SIZE;
|
const int verts = ESM::getLandSize(worldspace);
|
||||||
const int worldsize = ESM::Land::REAL_SIZE;
|
const int worldsize = ESM::getCellSize(worldspace);
|
||||||
|
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
mPhysics->addHeightField(
|
mPhysics->addHeightField(data->getHeights().data(), cellX, cellY, worldsize, verts,
|
||||||
data->mHeights, cellX, cellY, worldsize, verts, data->mMinHeight, data->mMaxHeight, land.get());
|
data->getMinHeight(), data->getMaxHeight(), land.get());
|
||||||
}
|
}
|
||||||
else
|
else if (!ESM::isEsm4Ext(worldspace))
|
||||||
{
|
{
|
||||||
static std::vector<float> defaultHeight;
|
static std::vector<float> defaultHeight;
|
||||||
defaultHeight.resize(verts * verts, ESM::Land::DEFAULT_HEIGHT);
|
defaultHeight.resize(verts * verts, ESM::Land::DEFAULT_HEIGHT);
|
||||||
|
@ -425,14 +427,14 @@ namespace MWWorld
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DetourNavigator::HeightfieldSurface heights;
|
DetourNavigator::HeightfieldSurface heights;
|
||||||
heights.mHeights = data->mHeights;
|
heights.mHeights = data->getHeights().data();
|
||||||
heights.mSize = static_cast<std::size_t>(ESM::Land::LAND_SIZE);
|
heights.mSize = static_cast<std::size_t>(data->getLandSize());
|
||||||
heights.mMinHeight = data->mMinHeight;
|
heights.mMinHeight = data->getMinHeight();
|
||||||
heights.mMaxHeight = data->mMaxHeight;
|
heights.mMaxHeight = data->getMaxHeight();
|
||||||
return heights;
|
return heights;
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
mNavigator.addHeightfield(cellPosition, ESM::Land::REAL_SIZE, shape, navigatorUpdateGuard);
|
mNavigator.addHeightfield(cellPosition, worldsize, shape, navigatorUpdateGuard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,7 +572,8 @@ namespace MWWorld
|
||||||
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
|
mCurrentGridCenter = osg::Vec2i(playerCellX, playerCellY);
|
||||||
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
|
osg::Vec4i newGrid = gridCenterToBounds(mCurrentGridCenter);
|
||||||
mRendering.setActiveGrid(newGrid);
|
mRendering.setActiveGrid(newGrid);
|
||||||
|
mRendering.enableTerrain(true, playerCellIndex.mWorldspace);
|
||||||
|
mPreloader->setTerrain(mRendering.getTerrain());
|
||||||
if (mRendering.pagingUnlockCache())
|
if (mRendering.pagingUnlockCache())
|
||||||
mPreloader->abortTerrainPreloadExcept(nullptr);
|
mPreloader->abortTerrainPreloadExcept(nullptr);
|
||||||
if (!mPreloader->isTerrainLoaded(std::make_pair(pos, newGrid), mRendering.getReferenceTime()))
|
if (!mPreloader->isTerrainLoaded(std::make_pair(pos, newGrid), mRendering.getReferenceTime()))
|
||||||
|
@ -784,7 +787,7 @@ namespace MWWorld
|
||||||
mHalfGridSize = cell.getCell()->isEsm4() ? Constants::ESM4CellGridRadius : Constants::CellGridRadius;
|
mHalfGridSize = cell.getCell()->isEsm4() ? Constants::ESM4CellGridRadius : Constants::CellGridRadius;
|
||||||
mCurrentCell = &cell;
|
mCurrentCell = &cell;
|
||||||
|
|
||||||
mRendering.enableTerrain(cell.isExterior());
|
mRendering.enableTerrain(cell.isExterior(), cell.getCell()->getWorldSpace());
|
||||||
|
|
||||||
MWWorld::Ptr old = mWorld.getPlayerPtr();
|
MWWorld::Ptr old = mWorld.getPlayerPtr();
|
||||||
mWorld.getPlayer().setCell(&cell);
|
mWorld.getPlayer().setCell(&cell);
|
||||||
|
|
|
@ -8,11 +8,14 @@
|
||||||
#include <components/esm/records.hpp>
|
#include <components/esm/records.hpp>
|
||||||
#include <components/esm3/esmreader.hpp>
|
#include <components/esm3/esmreader.hpp>
|
||||||
#include <components/esm3/esmwriter.hpp>
|
#include <components/esm3/esmwriter.hpp>
|
||||||
|
#include <components/esm4/loadland.hpp>
|
||||||
#include <components/esm4/loadwrld.hpp>
|
#include <components/esm4/loadwrld.hpp>
|
||||||
#include <components/loadinglistener/loadinglistener.hpp>
|
#include <components/loadinglistener/loadinglistener.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include <apps/openmw/mwworld/cell.hpp>
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwworld/cell.hpp"
|
||||||
|
#include "../mwworld/worldimp.hpp"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -1189,6 +1192,33 @@ namespace MWWorld
|
||||||
MWWorld::TypedDynamicStore<ESM4::Cell>::clearDynamic();
|
MWWorld::TypedDynamicStore<ESM4::Cell>::clearDynamic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ESM4 Land
|
||||||
|
//=========================================================================
|
||||||
|
// Needed to avoid include of ESM4::Land in header
|
||||||
|
Store<ESM4::Land>::Store() {}
|
||||||
|
|
||||||
|
void Store<ESM4::Land>::updateLandPositions(const Store<ESM4::Cell>& cells)
|
||||||
|
{
|
||||||
|
for (const auto& [id, value] : mStatic)
|
||||||
|
{
|
||||||
|
const ESM4::Cell* cell = cells.find(value.mCell);
|
||||||
|
mLands[cell->getExteriorCellLocation()] = &value;
|
||||||
|
}
|
||||||
|
for (const auto& [id, value] : mDynamic)
|
||||||
|
{
|
||||||
|
const ESM4::Cell* cell = cells.find(value.mCell);
|
||||||
|
mLands[cell->getExteriorCellLocation()] = &value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM4::Land* MWWorld::Store<ESM4::Land>::search(ESM::ExteriorCellLocation cellLocation) const
|
||||||
|
{
|
||||||
|
auto foundLand = mLands.find(cellLocation);
|
||||||
|
if (foundLand == mLands.end())
|
||||||
|
return nullptr;
|
||||||
|
return foundLand->second;
|
||||||
|
}
|
||||||
|
|
||||||
// ESM4 Reference
|
// ESM4 Reference
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
|
@ -1276,3 +1306,4 @@ template class MWWorld::TypedDynamicStore<ESM4::Reference, ESM::FormId>;
|
||||||
template class MWWorld::TypedDynamicStore<ESM4::Cell>;
|
template class MWWorld::TypedDynamicStore<ESM4::Cell>;
|
||||||
template class MWWorld::TypedDynamicStore<ESM4::Weapon>;
|
template class MWWorld::TypedDynamicStore<ESM4::Weapon>;
|
||||||
template class MWWorld::TypedDynamicStore<ESM4::World>;
|
template class MWWorld::TypedDynamicStore<ESM4::World>;
|
||||||
|
template class MWWorld::TypedDynamicStore<ESM4::Land>;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <components/esm3/loadland.hpp>
|
#include <components/esm3/loadland.hpp>
|
||||||
#include <components/esm3/loadpgrd.hpp>
|
#include <components/esm3/loadpgrd.hpp>
|
||||||
#include <components/esm4/loadcell.hpp>
|
#include <components/esm4/loadcell.hpp>
|
||||||
|
#include <components/esm4/loadland.hpp>
|
||||||
#include <components/esm4/loadrefr.hpp>
|
#include <components/esm4/loadrefr.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
|
@ -35,6 +36,11 @@ namespace ESM
|
||||||
class ESMWriter;
|
class ESMWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ESM4
|
||||||
|
{
|
||||||
|
struct Land;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Loading
|
namespace Loading
|
||||||
{
|
{
|
||||||
class Listener;
|
class Listener;
|
||||||
|
@ -298,6 +304,19 @@ namespace MWWorld
|
||||||
void clearDynamic() override;
|
void clearDynamic() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
class Store<ESM4::Land> : public TypedDynamicStore<ESM4::Land>
|
||||||
|
{
|
||||||
|
std::unordered_map<ESM::ExteriorCellLocation, const ESM4::Land*> mLands;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Store();
|
||||||
|
void updateLandPositions(const Store<ESM4::Cell>& cells);
|
||||||
|
|
||||||
|
const ESM4::Land* search(ESM::ExteriorCellLocation cellLocation) const;
|
||||||
|
const std::unordered_map<ESM::ExteriorCellLocation, const ESM4::Land*>& getLands() const { return mLands; }
|
||||||
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class Store<ESM::Land> : public DynamicStore
|
class Store<ESM::Land> : public DynamicStore
|
||||||
{
|
{
|
||||||
|
|
|
@ -1363,9 +1363,11 @@ namespace MWWorld
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptr.getCell()->isExterior() && !ptr.getCell()->getCell()->isEsm4())
|
const float terrainHeight = ptr.getCell()->isExterior()
|
||||||
pos.z() = std::max(pos.z(), getTerrainHeightAt(pos));
|
? getTerrainHeightAt(pos, ptr.getCell()->getCell()->getWorldSpace())
|
||||||
pos.z() += 20; // place slightly above terrain. will snap down to ground with code below
|
: -std::numeric_limits<float>::max();
|
||||||
|
pos.z() = std::max(pos.z(), terrainHeight)
|
||||||
|
+ 20; // place slightly above terrain. will snap down to ground with code below
|
||||||
|
|
||||||
// We still should trace down dead persistent actors - they do not use the "swimdeath" animation.
|
// We still should trace down dead persistent actors - they do not use the "swimdeath" animation.
|
||||||
bool swims = ptr.getClass().isActor() && isSwimming(ptr)
|
bool swims = ptr.getClass().isActor() && isSwimming(ptr)
|
||||||
|
@ -3607,9 +3609,9 @@ namespace MWWorld
|
||||||
return mPlayerTraveling;
|
return mPlayerTraveling;
|
||||||
}
|
}
|
||||||
|
|
||||||
float World::getTerrainHeightAt(const osg::Vec3f& worldPos) const
|
float World::getTerrainHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) const
|
||||||
{
|
{
|
||||||
return mRendering->getTerrainHeightAt(worldPos);
|
return mRendering->getTerrainHeightAt(worldPos, worldspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::Vec3f World::getHalfExtents(const ConstPtr& object, bool rendering) const
|
osg::Vec3f World::getHalfExtents(const ConstPtr& object, bool rendering) const
|
||||||
|
|
|
@ -648,7 +648,7 @@ namespace MWWorld
|
||||||
bool isPlayerTraveling() const override;
|
bool isPlayerTraveling() const override;
|
||||||
|
|
||||||
/// Return terrain height at \a worldPos position.
|
/// Return terrain height at \a worldPos position.
|
||||||
float getTerrainHeightAt(const osg::Vec3f& worldPos) const override;
|
float getTerrainHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) const override;
|
||||||
|
|
||||||
/// Return physical or rendering half extents of the given actor.
|
/// Return physical or rendering half extents of the given actor.
|
||||||
osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering = false) const override;
|
osg::Vec3f getHalfExtents(const MWWorld::ConstPtr& actor, bool rendering = false) const override;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <components/esm3/typetraits.hpp>
|
#include <components/esm3/typetraits.hpp>
|
||||||
#include <components/esm4/common.hpp>
|
#include <components/esm4/common.hpp>
|
||||||
#include <components/esm4/loadcell.hpp>
|
#include <components/esm4/loadcell.hpp>
|
||||||
|
#include <components/esm4/loadland.hpp>
|
||||||
#include <components/esm4/loadligh.hpp>
|
#include <components/esm4/loadligh.hpp>
|
||||||
#include <components/esm4/loadrefr.hpp>
|
#include <components/esm4/loadrefr.hpp>
|
||||||
#include <components/esm4/loadstat.hpp>
|
#include <components/esm4/loadstat.hpp>
|
||||||
|
|
|
@ -118,7 +118,7 @@ add_component_dir (to_utf8
|
||||||
to_utf8
|
to_utf8
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge
|
add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge esmterrain
|
||||||
formid
|
formid
|
||||||
formidrefid
|
formidrefid
|
||||||
stringrefid
|
stringrefid
|
||||||
|
|
67
components/esm/esmterrain.cpp
Normal file
67
components/esm/esmterrain.cpp
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#include <components/misc/constants.hpp>
|
||||||
|
|
||||||
|
#include "esmterrain.hpp"
|
||||||
|
|
||||||
|
ESM::LandData::LandData(const ESM::Land& land, int loadFlags)
|
||||||
|
: mLoadFlags(loadFlags)
|
||||||
|
, mSize(Constants::CellSizeInUnits)
|
||||||
|
, mLandSize(ESM::Land::LAND_SIZE)
|
||||||
|
{
|
||||||
|
ESM::Land::LandData data;
|
||||||
|
land.loadData(loadFlags, &data);
|
||||||
|
mLoadFlags = data.mDataLoaded;
|
||||||
|
std::span<const float> heights(data.mHeights);
|
||||||
|
mHeights = std::vector(heights.begin(), heights.end());
|
||||||
|
|
||||||
|
std::span<const VNML> normals(data.mNormals);
|
||||||
|
mNormals = std::vector(normals.begin(), normals.end());
|
||||||
|
|
||||||
|
std::span<const unsigned char> colors(data.mColours);
|
||||||
|
mColors = std::vector(colors.begin(), colors.end());
|
||||||
|
|
||||||
|
std::span<const uint16_t> textures(data.mTextures);
|
||||||
|
mTextures = std::vector(textures.begin(), textures.end());
|
||||||
|
|
||||||
|
mMinHeight = data.mMinHeight;
|
||||||
|
mMaxHeight = data.mMaxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::LandData::LandData(const ESM4::Land& land, int loadFlags)
|
||||||
|
: mLoadFlags(loadFlags)
|
||||||
|
, mSize(Constants::ESM4CellSizeInUnits)
|
||||||
|
, mLandSize(ESM4::Land::VERTS_PER_SIDE)
|
||||||
|
{
|
||||||
|
|
||||||
|
mMinHeight = std::numeric_limits<float>::max();
|
||||||
|
mMaxHeight = std::numeric_limits<float>::lowest();
|
||||||
|
mHeights.resize(ESM4::Land::LAND_NUM_VERTS);
|
||||||
|
mTextures.resize(ESM::Land::LAND_NUM_TEXTURES);
|
||||||
|
std::fill(mTextures.begin(), mTextures.end(), 0);
|
||||||
|
|
||||||
|
float row_offset = land.mHeightMap.heightOffset;
|
||||||
|
for (int y = 0; y < mLandSize; y++)
|
||||||
|
{
|
||||||
|
row_offset += land.mHeightMap.gradientData[y * mLandSize];
|
||||||
|
|
||||||
|
const float heightY = row_offset * ESM4::Land::HEIGHT_SCALE;
|
||||||
|
mHeights[y * mLandSize] = heightY;
|
||||||
|
mMinHeight = std::min(mMinHeight, heightY);
|
||||||
|
mMaxHeight = std::max(mMaxHeight, heightY);
|
||||||
|
|
||||||
|
float colOffset = row_offset;
|
||||||
|
for (int x = 1; x < mLandSize; x++)
|
||||||
|
{
|
||||||
|
colOffset += land.mHeightMap.gradientData[y * mLandSize + x];
|
||||||
|
const float heightX = colOffset * ESM4::Land::HEIGHT_SCALE;
|
||||||
|
mMinHeight = std::min(mMinHeight, heightX);
|
||||||
|
mMaxHeight = std::max(mMaxHeight, heightX);
|
||||||
|
mHeights[x + y * mLandSize] = heightX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<const VNML> normals(land.mVertNorm);
|
||||||
|
mNormals = std::vector(normals.begin(), normals.end());
|
||||||
|
|
||||||
|
std::span<const unsigned char> colors(land.mVertColr);
|
||||||
|
mColors = std::vector(colors.begin(), colors.end());
|
||||||
|
}
|
46
components/esm/esmterrain.hpp
Normal file
46
components/esm/esmterrain.hpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef COMPONENTS_ESM_ESMTERRAIN
|
||||||
|
#define COMPONENTS_ESM_ESMTERRAIN
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <components/esm3/loadland.hpp>
|
||||||
|
#include <components/esm4/loadland.hpp>
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class LandData
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
~LandData() = default;
|
||||||
|
LandData() = default;
|
||||||
|
LandData(const ESM::Land& Land, int loadFLags);
|
||||||
|
LandData(const ESM4::Land& Land, int loadFLags);
|
||||||
|
|
||||||
|
typedef signed char VNML;
|
||||||
|
|
||||||
|
std::span<const float> getHeights() const { return mHeights; }
|
||||||
|
std::span<const VNML> getNormals() const { return mNormals; }
|
||||||
|
std::span<const unsigned char> getColors() const { return mColors; }
|
||||||
|
std::span<const uint16_t> getTextures() const { return mTextures; }
|
||||||
|
float getSize() const { return mSize; }
|
||||||
|
float getMinHeight() const { return mMinHeight; }
|
||||||
|
float getMaxHeight() const { return mMaxHeight; }
|
||||||
|
int getLandSize() const { return mLandSize; }
|
||||||
|
|
||||||
|
int mLoadFlags;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float mMinHeight = 0.f;
|
||||||
|
float mMaxHeight = 0.f;
|
||||||
|
float mSize = 0.f;
|
||||||
|
int mLandSize = 0;
|
||||||
|
std::vector<float> mHeights;
|
||||||
|
std::vector<VNML> mNormals;
|
||||||
|
std::vector<unsigned char> mColors;
|
||||||
|
std::vector<uint16_t> mTextures;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // ! COMPNENTS_ESM_ESMTERRAIN
|
|
@ -1,4 +1,6 @@
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
|
#include <components/esm3/loadland.hpp>
|
||||||
|
#include <components/esm4/loadland.hpp>
|
||||||
|
|
||||||
osg::Vec2 ESM::indexToPosition(const ESM::ExteriorCellLocation& cellIndex, bool centre)
|
osg::Vec2 ESM::indexToPosition(const ESM::ExteriorCellLocation& cellIndex, bool centre)
|
||||||
{
|
{
|
||||||
|
@ -14,3 +16,8 @@ osg::Vec2 ESM::indexToPosition(const ESM::ExteriorCellLocation& cellIndex, bool
|
||||||
}
|
}
|
||||||
return osg::Vec2(x, y);
|
return osg::Vec2(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ESM::getLandSize(ESM::RefId worldspaceId)
|
||||||
|
{
|
||||||
|
return isEsm4Ext(worldspaceId) ? ESM4::Land::VERTS_PER_SIDE : ESM::Land::LAND_SIZE;
|
||||||
|
}
|
||||||
|
|
|
@ -51,8 +51,10 @@ namespace ESM
|
||||||
|
|
||||||
struct ExteriorCellLocation
|
struct ExteriorCellLocation
|
||||||
{
|
{
|
||||||
int mX, mY;
|
int mX = 0;
|
||||||
ESM::RefId mWorldspace;
|
int mY = 0;
|
||||||
|
ESM::RefId mWorldspace = ESM::Cell::sDefaultWorldspaceId;
|
||||||
|
ExteriorCellLocation() = default;
|
||||||
|
|
||||||
ExteriorCellLocation(int x, int y, ESM::RefId worldspace)
|
ExteriorCellLocation(int x, int y, ESM::RefId worldspace)
|
||||||
: mX(x)
|
: mX(x)
|
||||||
|
@ -83,6 +85,9 @@ namespace ESM
|
||||||
return isEsm4Ext(worldspaceId) ? Constants::ESM4CellSizeInUnits : Constants::CellSizeInUnits;
|
return isEsm4Ext(worldspaceId) ? Constants::ESM4CellSizeInUnits : Constants::CellSizeInUnits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Vertex count of a side of a land record
|
||||||
|
int getLandSize(ESM::RefId worldspaceId);
|
||||||
|
|
||||||
inline ESM::ExteriorCellLocation positionToExteriorCellLocation(
|
inline ESM::ExteriorCellLocation positionToExteriorCellLocation(
|
||||||
float x, float y, ESM::RefId worldspaceId = ESM::Cell::sDefaultWorldspaceId)
|
float x, float y, ESM::RefId worldspaceId = ESM::Cell::sDefaultWorldspaceId)
|
||||||
{
|
{
|
||||||
|
|
|
@ -87,10 +87,10 @@ namespace ESM
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
typedef signed char VNML;
|
|
||||||
|
|
||||||
struct LandData
|
struct LandData
|
||||||
{
|
{
|
||||||
|
typedef signed char VNML;
|
||||||
|
|
||||||
LandData()
|
LandData()
|
||||||
: mHeightOffset(0)
|
: mHeightOffset(0)
|
||||||
, mMinHeight(0)
|
, mMinHeight(0)
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include <osg/Plane>
|
#include <osg/Plane>
|
||||||
|
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
|
#include <components/esm/esmterrain.hpp>
|
||||||
|
#include <components/esm4/loadland.hpp>
|
||||||
#include <components/misc/resourcehelpers.hpp>
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
#include <components/misc/strings/algorithm.hpp>
|
#include <components/misc/strings/algorithm.hpp>
|
||||||
#include <components/vfs/manager.hpp>
|
#include <components/vfs/manager.hpp>
|
||||||
|
@ -16,26 +18,29 @@ namespace ESMTerrain
|
||||||
class LandCache
|
class LandCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::map<std::pair<int, int>, osg::ref_ptr<const LandObject>> Map;
|
typedef std::map<ESM::ExteriorCellLocation, osg::ref_ptr<const LandObject>> Map;
|
||||||
Map mMap;
|
Map mMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
LandObject::LandObject()
|
LandObject::LandObject()
|
||||||
: mLand(nullptr)
|
: mLand(nullptr)
|
||||||
, mLoadFlags(0)
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LandObject::LandObject(const ESM4::Land* land, int loadFlags)
|
||||||
|
: mLand(nullptr)
|
||||||
|
, mData(*land, loadFlags)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
LandObject::LandObject(const ESM::Land* land, int loadFlags)
|
LandObject::LandObject(const ESM::Land* land, int loadFlags)
|
||||||
: mLand(land)
|
: mLand(land)
|
||||||
, mLoadFlags(loadFlags)
|
, mData(*land, loadFlags)
|
||||||
{
|
{
|
||||||
mLand->loadData(mLoadFlags, &mData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LandObject::LandObject(const LandObject& copy, const osg::CopyOp& copyop)
|
LandObject::LandObject(const LandObject& copy, const osg::CopyOp& copyop)
|
||||||
: mLand(nullptr)
|
: mLand(nullptr)
|
||||||
, mLoadFlags(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +60,7 @@ namespace ESMTerrain
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Storage::getMinMaxHeights(float size, const osg::Vec2f& center, float& min, float& max)
|
bool Storage::getMinMaxHeights(float size, const osg::Vec2f& center, ESM::RefId worldspace, float& min, float& max)
|
||||||
{
|
{
|
||||||
assert(size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell");
|
assert(size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell");
|
||||||
|
|
||||||
|
@ -63,15 +68,15 @@ namespace ESMTerrain
|
||||||
|
|
||||||
int cellX = static_cast<int>(std::floor(origin.x()));
|
int cellX = static_cast<int>(std::floor(origin.x()));
|
||||||
int cellY = static_cast<int>(std::floor(origin.y()));
|
int cellY = static_cast<int>(std::floor(origin.y()));
|
||||||
|
osg::ref_ptr<const LandObject> land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace));
|
||||||
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
|
||||||
|
const int landSize = ESM::getLandSize(worldspace);
|
||||||
|
int startRow = (origin.x() - cellX) * landSize;
|
||||||
|
int startColumn = (origin.y() - cellY) * landSize;
|
||||||
|
|
||||||
int startRow = (origin.x() - cellX) * ESM::Land::LAND_SIZE;
|
int endRow = startRow + size * (landSize - 1) + 1;
|
||||||
int startColumn = (origin.y() - cellY) * ESM::Land::LAND_SIZE;
|
int endColumn = startColumn + size * (landSize - 1) + 1;
|
||||||
|
|
||||||
int endRow = startRow + size * (ESM::Land::LAND_SIZE - 1) + 1;
|
|
||||||
int endColumn = startColumn + size * (ESM::Land::LAND_SIZE - 1) + 1;
|
|
||||||
|
|
||||||
osg::ref_ptr<const LandObject> land = getLand(cellX, cellY);
|
|
||||||
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VHGT) : nullptr;
|
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
min = std::numeric_limits<float>::max();
|
min = std::numeric_limits<float>::max();
|
||||||
|
@ -80,7 +85,7 @@ namespace ESMTerrain
|
||||||
{
|
{
|
||||||
for (int col = startColumn; col < endColumn; ++col)
|
for (int col = startColumn; col < endColumn; ++col)
|
||||||
{
|
{
|
||||||
float h = data->mHeights[col * ESM::Land::LAND_SIZE + row];
|
float h = data->getHeights()[col * landSize + row];
|
||||||
if (h > max)
|
if (h > max)
|
||||||
max = h;
|
max = h;
|
||||||
if (h < min)
|
if (h < min)
|
||||||
|
@ -95,73 +100,80 @@ namespace ESMTerrain
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::fixNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache)
|
void Storage::fixNormal(
|
||||||
|
osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache)
|
||||||
{
|
{
|
||||||
while (col >= ESM::Land::LAND_SIZE - 1)
|
|
||||||
|
const int landSize = ESM::getLandSize(cellLocation.mWorldspace);
|
||||||
|
|
||||||
|
while (col >= landSize - 1)
|
||||||
{
|
{
|
||||||
++cellY;
|
++cellLocation.mY;
|
||||||
col -= ESM::Land::LAND_SIZE - 1;
|
col -= landSize - 1;
|
||||||
}
|
}
|
||||||
while (row >= ESM::Land::LAND_SIZE - 1)
|
while (row >= landSize - 1)
|
||||||
{
|
{
|
||||||
++cellX;
|
++cellLocation.mX;
|
||||||
row -= ESM::Land::LAND_SIZE - 1;
|
row -= landSize - 1;
|
||||||
}
|
}
|
||||||
while (col < 0)
|
while (col < 0)
|
||||||
{
|
{
|
||||||
--cellY;
|
--cellLocation.mY;
|
||||||
col += ESM::Land::LAND_SIZE - 1;
|
col += landSize - 1;
|
||||||
}
|
}
|
||||||
while (row < 0)
|
while (row < 0)
|
||||||
{
|
{
|
||||||
--cellX;
|
--cellLocation.mX;
|
||||||
row += ESM::Land::LAND_SIZE - 1;
|
row += landSize - 1;
|
||||||
}
|
}
|
||||||
|
const LandObject* land = getLand(cellLocation, cache);
|
||||||
const LandObject* land = getLand(cellX, cellY, cache);
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr;
|
||||||
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VNML) : nullptr;
|
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
normal.x() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3];
|
normal.x() = data->getNormals()[col * landSize * 3 + row * 3];
|
||||||
normal.y() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 1];
|
normal.y() = data->getNormals()[col * landSize * 3 + row * 3 + 1];
|
||||||
normal.z() = data->mNormals[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 2];
|
normal.z() = data->getNormals()[col * landSize * 3 + row * 3 + 2];
|
||||||
normal.normalize();
|
normal.normalize();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
normal = osg::Vec3f(0, 0, 1);
|
normal = osg::Vec3f(0, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::averageNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache)
|
void Storage::averageNormal(
|
||||||
|
osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache)
|
||||||
{
|
{
|
||||||
osg::Vec3f n1, n2, n3, n4;
|
osg::Vec3f n1, n2, n3, n4;
|
||||||
fixNormal(n1, cellX, cellY, col + 1, row, cache);
|
fixNormal(n1, cellLocation, col + 1, row, cache);
|
||||||
fixNormal(n2, cellX, cellY, col - 1, row, cache);
|
fixNormal(n2, cellLocation, col - 1, row, cache);
|
||||||
fixNormal(n3, cellX, cellY, col, row + 1, cache);
|
fixNormal(n3, cellLocation, col, row + 1, cache);
|
||||||
fixNormal(n4, cellX, cellY, col, row - 1, cache);
|
fixNormal(n4, cellLocation, col, row - 1, cache);
|
||||||
normal = (n1 + n2 + n3 + n4);
|
normal = (n1 + n2 + n3 + n4);
|
||||||
normal.normalize();
|
normal.normalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::fixColour(osg::Vec4ub& color, int cellX, int cellY, int col, int row, LandCache& cache)
|
void Storage::fixColour(
|
||||||
|
osg::Vec4ub& color, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache)
|
||||||
{
|
{
|
||||||
if (col == ESM::Land::LAND_SIZE - 1)
|
|
||||||
|
const int landSize = ESM::getLandSize(cellLocation.mWorldspace);
|
||||||
|
|
||||||
|
if (col == landSize - 1)
|
||||||
{
|
{
|
||||||
++cellY;
|
++cellLocation.mY;
|
||||||
col = 0;
|
col = 0;
|
||||||
}
|
}
|
||||||
if (row == ESM::Land::LAND_SIZE - 1)
|
if (row == landSize - 1)
|
||||||
{
|
{
|
||||||
++cellX;
|
++cellLocation.mX;
|
||||||
row = 0;
|
row = 0;
|
||||||
}
|
}
|
||||||
|
const LandObject* land = getLand(cellLocation, cache);
|
||||||
const LandObject* land = getLand(cellX, cellY, cache);
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr;
|
||||||
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VCLR) : nullptr;
|
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
color.r() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3];
|
color.r() = data->getColors()[col * landSize * 3 + row * 3];
|
||||||
color.g() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 1];
|
color.g() = data->getColors()[col * landSize * 3 + row * 3 + 1];
|
||||||
color.b() = data->mColours[col * ESM::Land::LAND_SIZE * 3 + row * 3 + 2];
|
color.b() = data->getColors()[col * landSize * 3 + row * 3 + 2];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -171,7 +183,7 @@ namespace ESMTerrain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center,
|
void Storage::fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, ESM::RefId worldspace,
|
||||||
osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
|
osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
|
||||||
osg::ref_ptr<osg::Vec4ubArray> colours)
|
osg::ref_ptr<osg::Vec4ubArray> colours)
|
||||||
{
|
{
|
||||||
|
@ -182,8 +194,10 @@ namespace ESMTerrain
|
||||||
|
|
||||||
int startCellX = static_cast<int>(std::floor(origin.x()));
|
int startCellX = static_cast<int>(std::floor(origin.x()));
|
||||||
int startCellY = static_cast<int>(std::floor(origin.y()));
|
int startCellY = static_cast<int>(std::floor(origin.y()));
|
||||||
|
const int landSize = ESM::getLandSize(worldspace);
|
||||||
|
const int LandSizeInUnits = ESM::getCellSize(worldspace);
|
||||||
|
|
||||||
size_t numVerts = static_cast<size_t>(size * (ESM::Land::LAND_SIZE - 1) / increment + 1);
|
size_t numVerts = static_cast<size_t>(size * (landSize - 1) / increment + 1);
|
||||||
|
|
||||||
positions->resize(numVerts * numVerts);
|
positions->resize(numVerts * numVerts);
|
||||||
normals->resize(numVerts * numVerts);
|
normals->resize(numVerts * numVerts);
|
||||||
|
@ -198,22 +212,24 @@ namespace ESMTerrain
|
||||||
LandCache cache;
|
LandCache cache;
|
||||||
|
|
||||||
bool alteration = useAlteration();
|
bool alteration = useAlteration();
|
||||||
|
bool validHeightDataExists = false;
|
||||||
float vertY_ = 0; // of current cell corner
|
float vertY_ = 0; // of current cell corner
|
||||||
for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY)
|
for (int cellY = startCellY; cellY < startCellY + std::ceil(size); ++cellY)
|
||||||
{
|
{
|
||||||
float vertX_ = 0; // of current cell corner
|
float vertX_ = 0; // of current cell corner
|
||||||
for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX)
|
for (int cellX = startCellX; cellX < startCellX + std::ceil(size); ++cellX)
|
||||||
{
|
{
|
||||||
const LandObject* land = getLand(cellX, cellY, cache);
|
ESM::ExteriorCellLocation cellLocation(cellX, cellY, worldspace);
|
||||||
const ESM::Land::LandData* heightData = nullptr;
|
const LandObject* land = getLand(cellLocation, cache);
|
||||||
const ESM::Land::LandData* normalData = nullptr;
|
const ESM::LandData* heightData = nullptr;
|
||||||
const ESM::Land::LandData* colourData = nullptr;
|
const ESM::LandData* normalData = nullptr;
|
||||||
|
const ESM::LandData* colourData = nullptr;
|
||||||
if (land)
|
if (land)
|
||||||
{
|
{
|
||||||
heightData = land->getData(ESM::Land::DATA_VHGT);
|
heightData = land->getData(ESM::Land::DATA_VHGT);
|
||||||
normalData = land->getData(ESM::Land::DATA_VNML);
|
normalData = land->getData(ESM::Land::DATA_VNML);
|
||||||
colourData = land->getData(ESM::Land::DATA_VCLR);
|
colourData = land->getData(ESM::Land::DATA_VCLR);
|
||||||
|
validHeightDataExists = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rowStart = 0;
|
int rowStart = 0;
|
||||||
|
@ -227,12 +243,12 @@ namespace ESMTerrain
|
||||||
rowStart += increment;
|
rowStart += increment;
|
||||||
|
|
||||||
// Only relevant for chunks smaller than (contained in) one cell
|
// Only relevant for chunks smaller than (contained in) one cell
|
||||||
rowStart += (origin.x() - startCellX) * ESM::Land::LAND_SIZE;
|
rowStart += (origin.x() - startCellX) * landSize;
|
||||||
colStart += (origin.y() - startCellY) * ESM::Land::LAND_SIZE;
|
colStart += (origin.y() - startCellY) * landSize;
|
||||||
int rowEnd = std::min(static_cast<int>(rowStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE - 1) + 1),
|
int rowEnd = std::min(
|
||||||
static_cast<int>(ESM::Land::LAND_SIZE));
|
static_cast<int>(rowStart + std::min(1.f, size) * (landSize - 1) + 1), static_cast<int>(landSize));
|
||||||
int colEnd = std::min(static_cast<int>(colStart + std::min(1.f, size) * (ESM::Land::LAND_SIZE - 1) + 1),
|
int colEnd = std::min(
|
||||||
static_cast<int>(ESM::Land::LAND_SIZE));
|
static_cast<int>(colStart + std::min(1.f, size) * (landSize - 1) + 1), static_cast<int>(landSize));
|
||||||
|
|
||||||
vertY = vertY_;
|
vertY = vertY_;
|
||||||
for (int col = colStart; col < colEnd; col += increment)
|
for (int col = colStart; col < colEnd; col += increment)
|
||||||
|
@ -240,27 +256,27 @@ namespace ESMTerrain
|
||||||
vertX = vertX_;
|
vertX = vertX_;
|
||||||
for (int row = rowStart; row < rowEnd; row += increment)
|
for (int row = rowStart; row < rowEnd; row += increment)
|
||||||
{
|
{
|
||||||
int srcArrayIndex = col * ESM::Land::LAND_SIZE * 3 + row * 3;
|
int srcArrayIndex = col * landSize * 3 + row * 3;
|
||||||
|
|
||||||
assert(row >= 0 && row < ESM::Land::LAND_SIZE);
|
assert(row >= 0 && row < landSize);
|
||||||
assert(col >= 0 && col < ESM::Land::LAND_SIZE);
|
assert(col >= 0 && col < landSize);
|
||||||
|
|
||||||
assert(vertX < numVerts);
|
assert(vertX < numVerts);
|
||||||
assert(vertY < numVerts);
|
assert(vertY < numVerts);
|
||||||
|
|
||||||
float height = defaultHeight;
|
float height = defaultHeight;
|
||||||
if (heightData)
|
if (heightData)
|
||||||
height = heightData->mHeights[col * ESM::Land::LAND_SIZE + row];
|
height = heightData->getHeights()[col * landSize + row];
|
||||||
if (alteration)
|
if (alteration)
|
||||||
height += getAlteredHeight(col, row);
|
height += getAlteredHeight(col, row);
|
||||||
(*positions)[static_cast<unsigned int>(vertX * numVerts + vertY)]
|
(*positions)[static_cast<unsigned int>(vertX * numVerts + vertY)]
|
||||||
= osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits,
|
= osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * LandSizeInUnits,
|
||||||
(vertY / float(numVerts - 1) - 0.5f) * size * Constants::CellSizeInUnits, height);
|
(vertY / float(numVerts - 1) - 0.5f) * size * LandSizeInUnits, height);
|
||||||
|
|
||||||
if (normalData)
|
if (normalData)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
normal[i] = normalData->mNormals[srcArrayIndex + i];
|
normal[i] = normalData->getNormals()[srcArrayIndex + i];
|
||||||
|
|
||||||
normal.normalize();
|
normal.normalize();
|
||||||
}
|
}
|
||||||
|
@ -268,13 +284,12 @@ namespace ESMTerrain
|
||||||
normal = osg::Vec3f(0, 0, 1);
|
normal = osg::Vec3f(0, 0, 1);
|
||||||
|
|
||||||
// Normals apparently don't connect seamlessly between cells
|
// Normals apparently don't connect seamlessly between cells
|
||||||
if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1)
|
if (col == landSize - 1 || row == landSize - 1)
|
||||||
fixNormal(normal, cellX, cellY, col, row, cache);
|
fixNormal(normal, cellLocation, col, row, cache);
|
||||||
|
|
||||||
// some corner normals appear to be complete garbage (z < 0)
|
// some corner normals appear to be complete garbage (z < 0)
|
||||||
if ((row == 0 || row == ESM::Land::LAND_SIZE - 1)
|
if ((row == 0 || row == landSize - 1) && (col == 0 || col == landSize - 1))
|
||||||
&& (col == 0 || col == ESM::Land::LAND_SIZE - 1))
|
averageNormal(normal, cellLocation, col, row, cache);
|
||||||
averageNormal(normal, cellX, cellY, col, row, cache);
|
|
||||||
|
|
||||||
assert(normal.z() > 0);
|
assert(normal.z() > 0);
|
||||||
|
|
||||||
|
@ -283,7 +298,7 @@ namespace ESMTerrain
|
||||||
if (colourData)
|
if (colourData)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
color[i] = colourData->mColours[srcArrayIndex + i];
|
color[i] = colourData->getColors()[srcArrayIndex + i];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -295,8 +310,8 @@ namespace ESMTerrain
|
||||||
adjustColor(col, row, heightData, color); // Does nothing by default, override in OpenMW-CS
|
adjustColor(col, row, heightData, color); // Does nothing by default, override in OpenMW-CS
|
||||||
|
|
||||||
// Unlike normals, colors mostly connect seamlessly between cells, but not always...
|
// Unlike normals, colors mostly connect seamlessly between cells, but not always...
|
||||||
if (col == ESM::Land::LAND_SIZE - 1 || row == ESM::Land::LAND_SIZE - 1)
|
if (col == landSize - 1 || row == landSize - 1)
|
||||||
fixColour(color, cellX, cellY, col, row, cache);
|
fixColour(color, cellLocation, col, row, cache);
|
||||||
|
|
||||||
color.a() = 255;
|
color.a() = 255;
|
||||||
|
|
||||||
|
@ -313,39 +328,50 @@ namespace ESMTerrain
|
||||||
assert(vertX_ == numVerts); // Ensure we covered whole area
|
assert(vertX_ == numVerts); // Ensure we covered whole area
|
||||||
}
|
}
|
||||||
assert(vertY_ == numVerts); // Ensure we covered whole area
|
assert(vertY_ == numVerts); // Ensure we covered whole area
|
||||||
|
|
||||||
|
if (!validHeightDataExists && ESM::isEsm4Ext(worldspace))
|
||||||
|
{
|
||||||
|
for (unsigned int iVert = 0; iVert < numVerts * numVerts; iVert++)
|
||||||
|
{
|
||||||
|
(*positions)[static_cast<unsigned int>(iVert)] = osg::Vec3f(0.f, 0.f, 0.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Storage::UniqueTextureId Storage::getVtexIndexAt(int cellX, int cellY, int x, int y, LandCache& cache)
|
Storage::UniqueTextureId Storage::getVtexIndexAt(
|
||||||
|
ESM::ExteriorCellLocation cellLocation, const LandObject* land, int x, int y, LandCache& cache)
|
||||||
{
|
{
|
||||||
// For the first/last row/column, we need to get the texture from the neighbour cell
|
// For the first/last row/column, we need to get the texture from the neighbour cell
|
||||||
// to get consistent blending at the borders
|
// to get consistent blending at the borders
|
||||||
--x;
|
--x;
|
||||||
|
ESM::ExteriorCellLocation cellLocationIn = cellLocation;
|
||||||
if (x < 0)
|
if (x < 0)
|
||||||
{
|
{
|
||||||
--cellX;
|
--cellLocation.mX;
|
||||||
x += ESM::Land::LAND_TEXTURE_SIZE;
|
x += ESM::Land::LAND_TEXTURE_SIZE;
|
||||||
}
|
}
|
||||||
while (x >= ESM::Land::LAND_TEXTURE_SIZE)
|
while (x >= ESM::Land::LAND_TEXTURE_SIZE)
|
||||||
{
|
{
|
||||||
++cellX;
|
++cellLocation.mX;
|
||||||
x -= ESM::Land::LAND_TEXTURE_SIZE;
|
x -= ESM::Land::LAND_TEXTURE_SIZE;
|
||||||
}
|
}
|
||||||
while (
|
while (
|
||||||
y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not?
|
y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not?
|
||||||
{
|
{
|
||||||
++cellY;
|
++cellLocation.mY;
|
||||||
y -= ESM::Land::LAND_TEXTURE_SIZE;
|
y -= ESM::Land::LAND_TEXTURE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cellLocation != cellLocationIn)
|
||||||
|
land = getLand(cellLocation, cache);
|
||||||
|
|
||||||
assert(x < ESM::Land::LAND_TEXTURE_SIZE);
|
assert(x < ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
assert(y < ESM::Land::LAND_TEXTURE_SIZE);
|
assert(y < ESM::Land::LAND_TEXTURE_SIZE);
|
||||||
|
|
||||||
const LandObject* land = getLand(cellX, cellY, cache);
|
const ESM::LandData* data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr;
|
||||||
|
|
||||||
const ESM::Land::LandData* data = land ? land->getData(ESM::Land::DATA_VTEX) : nullptr;
|
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
int tex = data->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
|
int tex = data->getTextures()[y * ESM::Land::LAND_TEXTURE_SIZE + x];
|
||||||
if (tex == 0)
|
if (tex == 0)
|
||||||
return std::make_pair(0, 0); // vtex 0 is always the base texture, regardless of plugin
|
return std::make_pair(0, 0); // vtex 0 is always the base texture, regardless of plugin
|
||||||
return std::make_pair(tex, land->getPlugin());
|
return std::make_pair(tex, land->getPlugin());
|
||||||
|
@ -375,7 +401,7 @@ namespace ESMTerrain
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
void Storage::getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
||||||
std::vector<Terrain::LayerInfo>& layerList)
|
std::vector<Terrain::LayerInfo>& layerList, ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize / 2.f, chunkSize / 2.f);
|
osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize / 2.f, chunkSize / 2.f);
|
||||||
int cellX = static_cast<int>(std::floor(origin.x()));
|
int cellX = static_cast<int>(std::floor(origin.x()));
|
||||||
|
@ -393,12 +419,15 @@ namespace ESMTerrain
|
||||||
|
|
||||||
LandCache cache;
|
LandCache cache;
|
||||||
std::map<UniqueTextureId, unsigned int> textureIndicesMap;
|
std::map<UniqueTextureId, unsigned int> textureIndicesMap;
|
||||||
|
ESM::ExteriorCellLocation cellLocation(cellX, cellY, worldspace);
|
||||||
|
|
||||||
|
const LandObject* land = getLand(cellLocation, cache);
|
||||||
|
|
||||||
for (int y = 0; y < blendmapSize; y++)
|
for (int y = 0; y < blendmapSize; y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < blendmapSize; x++)
|
for (int x = 0; x < blendmapSize; x++)
|
||||||
{
|
{
|
||||||
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x + rowStart, y + colStart, cache);
|
UniqueTextureId id = getVtexIndexAt(cellLocation, land, x + rowStart, y + colStart, cache);
|
||||||
std::map<UniqueTextureId, unsigned int>::iterator found = textureIndicesMap.find(id);
|
std::map<UniqueTextureId, unsigned int>::iterator found = textureIndicesMap.find(id);
|
||||||
if (found == textureIndicesMap.end())
|
if (found == textureIndicesMap.end())
|
||||||
{
|
{
|
||||||
|
@ -442,27 +471,29 @@ namespace ESMTerrain
|
||||||
blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend
|
blendmaps.clear(); // If a single texture fills the whole terrain, there is no need to blend
|
||||||
}
|
}
|
||||||
|
|
||||||
float Storage::getHeightAt(const osg::Vec3f& worldPos)
|
float Storage::getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
int cellX = static_cast<int>(std::floor(worldPos.x() / float(Constants::CellSizeInUnits)));
|
const float cellSize = ESM::getCellSize(worldspace);
|
||||||
int cellY = static_cast<int>(std::floor(worldPos.y() / float(Constants::CellSizeInUnits)));
|
int cellX = static_cast<int>(std::floor(worldPos.x() / cellSize));
|
||||||
|
int cellY = static_cast<int>(std::floor(worldPos.y() / cellSize));
|
||||||
|
|
||||||
osg::ref_ptr<const LandObject> land = getLand(cellX, cellY);
|
osg::ref_ptr<const LandObject> land = getLand(ESM::ExteriorCellLocation(cellX, cellY, worldspace));
|
||||||
if (!land)
|
if (!land)
|
||||||
return defaultHeight;
|
return ESM::isEsm4Ext(worldspace) ? std::numeric_limits<float>::lowest() : defaultHeight;
|
||||||
|
|
||||||
const ESM::Land::LandData* data = land->getData(ESM::Land::DATA_VHGT);
|
const ESM::LandData* data = land->getData(ESM::Land::DATA_VHGT);
|
||||||
if (!data)
|
if (!data)
|
||||||
return defaultHeight;
|
return defaultHeight;
|
||||||
|
const int landSize = data->getLandSize();
|
||||||
|
|
||||||
// Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition
|
// Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition
|
||||||
|
|
||||||
// Normalized position in the cell
|
// Normalized position in the cell
|
||||||
float nX = (worldPos.x() - (cellX * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits);
|
float nX = (worldPos.x() - (cellX * cellSize)) / cellSize;
|
||||||
float nY = (worldPos.y() - (cellY * Constants::CellSizeInUnits)) / float(Constants::CellSizeInUnits);
|
float nY = (worldPos.y() - (cellY * cellSize)) / cellSize;
|
||||||
|
|
||||||
// get left / bottom points (rounded down)
|
// get left / bottom points (rounded down)
|
||||||
float factor = ESM::Land::LAND_SIZE - 1.0f;
|
float factor = landSize - 1.0f;
|
||||||
float invFactor = 1.0f / factor;
|
float invFactor = 1.0f / factor;
|
||||||
|
|
||||||
int startX = static_cast<int>(nX * factor);
|
int startX = static_cast<int>(nX * factor);
|
||||||
|
@ -470,8 +501,8 @@ namespace ESMTerrain
|
||||||
int endX = startX + 1;
|
int endX = startX + 1;
|
||||||
int endY = startY + 1;
|
int endY = startY + 1;
|
||||||
|
|
||||||
endX = std::min(endX, ESM::Land::LAND_SIZE - 1);
|
endX = std::min(endX, landSize - 1);
|
||||||
endY = std::min(endY, ESM::Land::LAND_SIZE - 1);
|
endY = std::min(endY, landSize - 1);
|
||||||
|
|
||||||
// now get points in terrain space (effectively rounding them to boundaries)
|
// now get points in terrain space (effectively rounding them to boundaries)
|
||||||
float startXTS = startX * invFactor;
|
float startXTS = startX * invFactor;
|
||||||
|
@ -491,10 +522,10 @@ namespace ESMTerrain
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Build all 4 positions in normalized cell space, using point-sampled height
|
// Build all 4 positions in normalized cell space, using point-sampled height
|
||||||
osg::Vec3f v0(startXTS, startYTS, getVertexHeight(data, startX, startY) / float(Constants::CellSizeInUnits));
|
osg::Vec3f v0(startXTS, startYTS, getVertexHeight(data, startX, startY) / cellSize);
|
||||||
osg::Vec3f v1(endXTS, startYTS, getVertexHeight(data, endX, startY) / float(Constants::CellSizeInUnits));
|
osg::Vec3f v1(endXTS, startYTS, getVertexHeight(data, endX, startY) / cellSize);
|
||||||
osg::Vec3f v2(endXTS, endYTS, getVertexHeight(data, endX, endY) / float(Constants::CellSizeInUnits));
|
osg::Vec3f v2(endXTS, endYTS, getVertexHeight(data, endX, endY) / cellSize);
|
||||||
osg::Vec3f v3(startXTS, endYTS, getVertexHeight(data, startX, endY) / float(Constants::CellSizeInUnits));
|
osg::Vec3f v3(startXTS, endYTS, getVertexHeight(data, startX, endY) / cellSize);
|
||||||
// define this plane in terrain space
|
// define this plane in terrain space
|
||||||
osg::Plane plane;
|
osg::Plane plane;
|
||||||
// FIXME: deal with differing triangle alignment
|
// FIXME: deal with differing triangle alignment
|
||||||
|
@ -520,23 +551,22 @@ namespace ESMTerrain
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Solve plane equation for z
|
// Solve plane equation for z
|
||||||
return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z()
|
return (-plane.getNormal().x() * nX - plane.getNormal().y() * nY - plane[3]) / plane.getNormal().z() * cellSize;
|
||||||
* Constants::CellSizeInUnits;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const LandObject* Storage::getLand(int cellX, int cellY, LandCache& cache)
|
const LandObject* Storage::getLand(ESM::ExteriorCellLocation cellLocation, LandCache& cache)
|
||||||
{
|
{
|
||||||
LandCache::Map::iterator found = cache.mMap.find(std::make_pair(cellX, cellY));
|
LandCache::Map::iterator found = cache.mMap.find(cellLocation);
|
||||||
if (found != cache.mMap.end())
|
if (found != cache.mMap.end())
|
||||||
return found->second;
|
return found->second;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
found = cache.mMap.insert(std::make_pair(std::make_pair(cellX, cellY), getLand(cellX, cellY))).first;
|
found = cache.mMap.insert(std::make_pair(cellLocation, getLand(cellLocation))).first;
|
||||||
return found->second;
|
return found->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Storage::adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const {}
|
void Storage::adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const {}
|
||||||
|
|
||||||
float Storage::getAlteredHeight(int col, int row) const
|
float Storage::getAlteredHeight(int col, int row) const
|
||||||
{
|
{
|
||||||
|
@ -591,14 +621,14 @@ namespace ESMTerrain
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Storage::getCellWorldSize()
|
float Storage::getCellWorldSize(ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
return static_cast<float>(ESM::Land::REAL_SIZE);
|
return static_cast<float>(ESM::getCellSize(worldspace));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Storage::getCellVertices()
|
int Storage::getCellVertices(ESM::RefId worldspace)
|
||||||
{
|
{
|
||||||
return ESM::Land::LAND_SIZE;
|
return ESM::getLandSize(worldspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Storage::getBlendmapScale(float chunkSize)
|
int Storage::getBlendmapScale(float chunkSize)
|
||||||
|
|
|
@ -6,9 +6,21 @@
|
||||||
|
|
||||||
#include <components/terrain/storage.hpp>
|
#include <components/terrain/storage.hpp>
|
||||||
|
|
||||||
|
#include <components/esm/esmterrain.hpp>
|
||||||
|
#include <components/esm/util.hpp>
|
||||||
#include <components/esm3/loadland.hpp>
|
#include <components/esm3/loadland.hpp>
|
||||||
#include <components/esm3/loadltex.hpp>
|
#include <components/esm3/loadltex.hpp>
|
||||||
|
|
||||||
|
namespace ESM4
|
||||||
|
{
|
||||||
|
struct Land;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class LandData;
|
||||||
|
}
|
||||||
|
|
||||||
namespace VFS
|
namespace VFS
|
||||||
{
|
{
|
||||||
class Manager;
|
class Manager;
|
||||||
|
@ -26,24 +38,28 @@ namespace ESMTerrain
|
||||||
public:
|
public:
|
||||||
LandObject();
|
LandObject();
|
||||||
LandObject(const ESM::Land* land, int loadFlags);
|
LandObject(const ESM::Land* land, int loadFlags);
|
||||||
|
LandObject(const ESM4::Land* land, int loadFlags);
|
||||||
|
|
||||||
LandObject(const LandObject& copy, const osg::CopyOp& copyop);
|
LandObject(const LandObject& copy, const osg::CopyOp& copyop);
|
||||||
virtual ~LandObject();
|
virtual ~LandObject();
|
||||||
|
|
||||||
META_Object(ESMTerrain, LandObject)
|
META_Object(ESMTerrain, LandObject)
|
||||||
|
|
||||||
inline const ESM::Land::LandData* getData(int flags) const
|
inline const ESM::LandData* getData(int flags) const
|
||||||
{
|
{
|
||||||
if ((mData.mDataLoaded & flags) != flags)
|
if ((mData.mLoadFlags & flags) != flags)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return &mData;
|
return &mData;
|
||||||
}
|
}
|
||||||
inline int getPlugin() const { return mLand->getPlugin(); }
|
inline int getPlugin() const { return mLand->getPlugin(); }
|
||||||
|
inline int getLandSize() const { return mData.getLandSize(); }
|
||||||
|
inline int getRealSize() const { return mData.getSize(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ESM::Land* mLand;
|
const ESM::Land* mLand;
|
||||||
int mLoadFlags;
|
|
||||||
|
|
||||||
ESM::Land::LandData mData;
|
ESM::LandData mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture)
|
/// @brief Feeds data from ESM terrain records (ESM::Land, ESM::LandTexture)
|
||||||
|
@ -56,10 +72,10 @@ namespace ESMTerrain
|
||||||
const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
|
const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
|
||||||
|
|
||||||
// Not implemented in this class, because we need different Store implementations for game and editor
|
// Not implemented in this class, because we need different Store implementations for game and editor
|
||||||
virtual osg::ref_ptr<const LandObject> getLand(int cellX, int cellY) = 0;
|
virtual osg::ref_ptr<const LandObject> getLand(ESM::ExteriorCellLocation cellLocation) = 0;
|
||||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0;
|
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0;
|
||||||
/// Get bounds of the whole terrain in cell units
|
/// Get bounds of the whole terrain in cell units
|
||||||
void getBounds(float& minX, float& maxX, float& minY, float& maxY) override = 0;
|
void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) override = 0;
|
||||||
|
|
||||||
/// Get the minimum and maximum heights of a terrain region.
|
/// Get the minimum and maximum heights of a terrain region.
|
||||||
/// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree.
|
/// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree.
|
||||||
|
@ -69,7 +85,8 @@ namespace ESMTerrain
|
||||||
/// @param min min height will be stored here
|
/// @param min min height will be stored here
|
||||||
/// @param max max height will be stored here
|
/// @param max max height will be stored here
|
||||||
/// @return true if there was data available for this terrain chunk
|
/// @return true if there was data available for this terrain chunk
|
||||||
bool getMinMaxHeights(float size, const osg::Vec2f& center, float& min, float& max) override;
|
bool getMinMaxHeights(
|
||||||
|
float size, const osg::Vec2f& center, ESM::RefId worldspace, float& min, float& max) override;
|
||||||
|
|
||||||
/// Fill vertex buffers for a terrain chunk.
|
/// Fill vertex buffers for a terrain chunk.
|
||||||
/// @note May be called from background threads. Make sure to only call thread-safe functions from here!
|
/// @note May be called from background threads. Make sure to only call thread-safe functions from here!
|
||||||
|
@ -81,7 +98,7 @@ namespace ESMTerrain
|
||||||
/// @param positions buffer to write vertices
|
/// @param positions buffer to write vertices
|
||||||
/// @param normals buffer to write vertex normals
|
/// @param normals buffer to write vertex normals
|
||||||
/// @param colours buffer to write vertex colours
|
/// @param colours buffer to write vertex colours
|
||||||
void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center,
|
void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, ESM::RefId worldspace,
|
||||||
osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
|
osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
|
||||||
osg::ref_ptr<osg::Vec4ubArray> colours) override;
|
osg::ref_ptr<osg::Vec4ubArray> colours) override;
|
||||||
|
|
||||||
|
@ -94,36 +111,40 @@ namespace ESMTerrain
|
||||||
/// @param blendmaps created blendmaps will be written here
|
/// @param blendmaps created blendmaps will be written here
|
||||||
/// @param layerList names of the layer textures used will be written here
|
/// @param layerList names of the layer textures used will be written here
|
||||||
void getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
void getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
||||||
std::vector<Terrain::LayerInfo>& layerList) override;
|
std::vector<Terrain::LayerInfo>& layerList, ESM::RefId worldspace) override;
|
||||||
|
|
||||||
float getHeightAt(const osg::Vec3f& worldPos) override;
|
float getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) override;
|
||||||
|
|
||||||
/// Get the transformation factor for mapping cell units to world units.
|
/// Get the transformation factor for mapping cell units to world units.
|
||||||
float getCellWorldSize() override;
|
float getCellWorldSize(ESM::RefId worldspace) override;
|
||||||
|
|
||||||
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
|
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
|
||||||
int getCellVertices() override;
|
int getCellVertices(ESM::RefId worldspace) override;
|
||||||
|
|
||||||
int getBlendmapScale(float chunkSize) override;
|
int getBlendmapScale(float chunkSize) override;
|
||||||
|
|
||||||
float getVertexHeight(const ESM::Land::LandData* data, int x, int y)
|
float getVertexHeight(const ESM::LandData* data, int x, int y)
|
||||||
{
|
{
|
||||||
assert(x < ESM::Land::LAND_SIZE);
|
const int landSize = data->getLandSize();
|
||||||
assert(y < ESM::Land::LAND_SIZE);
|
assert(x < landSize);
|
||||||
return data->mHeights[y * ESM::Land::LAND_SIZE + x];
|
assert(y < landSize);
|
||||||
|
return data->getHeights()[y * landSize + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const VFS::Manager* mVFS;
|
const VFS::Manager* mVFS;
|
||||||
|
|
||||||
inline void fixNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);
|
inline void fixNormal(
|
||||||
inline void fixColour(osg::Vec4ub& colour, int cellX, int cellY, int col, int row, LandCache& cache);
|
osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache);
|
||||||
inline void averageNormal(osg::Vec3f& normal, int cellX, int cellY, int col, int row, LandCache& cache);
|
inline void fixColour(
|
||||||
|
osg::Vec4ub& colour, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache);
|
||||||
|
inline void averageNormal(
|
||||||
|
osg::Vec3f& normal, ESM::ExteriorCellLocation cellLocation, int col, int row, LandCache& cache);
|
||||||
|
|
||||||
inline const LandObject* getLand(int cellX, int cellY, LandCache& cache);
|
inline const LandObject* getLand(ESM::ExteriorCellLocation cellLocation, LandCache& cache);
|
||||||
|
|
||||||
virtual bool useAlteration() const { return false; }
|
virtual bool useAlteration() const { return false; }
|
||||||
virtual void adjustColor(int col, int row, const ESM::Land::LandData* heightData, osg::Vec4ub& color) const;
|
virtual void adjustColor(int col, int row, const ESM::LandData* heightData, osg::Vec4ub& color) const;
|
||||||
virtual float getAlteredHeight(int col, int row) const;
|
virtual float getAlteredHeight(int col, int row) const;
|
||||||
|
|
||||||
// Since plugins can define new texture palettes, we need to know the plugin index too
|
// Since plugins can define new texture palettes, we need to know the plugin index too
|
||||||
|
@ -131,7 +152,8 @@ namespace ESMTerrain
|
||||||
// pair <texture id, plugin id>
|
// pair <texture id, plugin id>
|
||||||
typedef std::pair<short, short> UniqueTextureId;
|
typedef std::pair<short, short> UniqueTextureId;
|
||||||
|
|
||||||
inline UniqueTextureId getVtexIndexAt(int cellX, int cellY, int x, int y, LandCache&);
|
inline UniqueTextureId getVtexIndexAt(
|
||||||
|
ESM::ExteriorCellLocation cellLocation, const LandObject* land, int x, int y, LandCache&);
|
||||||
std::string getTextureName(UniqueTextureId id);
|
std::string getTextureName(UniqueTextureId id);
|
||||||
|
|
||||||
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
#include <components/esm/defs.hpp>
|
#include <components/esm/defs.hpp>
|
||||||
#include <components/esm/refid.hpp>
|
#include <components/esm/refid.hpp>
|
||||||
|
#include <components/esm/util.hpp>
|
||||||
#include <components/esm4/reader.hpp>
|
#include <components/esm4/reader.hpp>
|
||||||
|
|
||||||
namespace ESM4
|
namespace ESM4
|
||||||
|
@ -107,7 +108,10 @@ namespace ESM4
|
||||||
int getGridX() const { return mX; }
|
int getGridX() const { return mX; }
|
||||||
int getGridY() const { return mY; }
|
int getGridY() const { return mY; }
|
||||||
bool isExterior() const { return !(mCellFlags & CELL_Interior); }
|
bool isExterior() const { return !(mCellFlags & CELL_Interior); }
|
||||||
|
ESM::ExteriorCellLocation getExteriorCellLocation() const
|
||||||
|
{
|
||||||
|
return ESM::ExteriorCellLocation(mX, mY, isExterior() ? mParent : mId);
|
||||||
|
}
|
||||||
static float sInvalidWaterLevel;
|
static float sInvalidWaterLevel;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,8 +34,10 @@
|
||||||
|
|
||||||
#include <iostream> // FIXME: debug only
|
#include <iostream> // FIXME: debug only
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
#include "reader.hpp"
|
#include "reader.hpp"
|
||||||
//#include "writer.hpp"
|
// #include "writer.hpp"
|
||||||
|
|
||||||
// overlap north
|
// overlap north
|
||||||
//
|
//
|
||||||
|
@ -54,11 +56,12 @@
|
||||||
//
|
//
|
||||||
void ESM4::Land::load(ESM4::Reader& reader)
|
void ESM4::Land::load(ESM4::Reader& reader)
|
||||||
{
|
{
|
||||||
mFormId = reader.hdr().record.getFormId();
|
ESM::FormId formId = reader.hdr().record.getFormId();
|
||||||
reader.adjustFormId(mFormId);
|
reader.adjustFormId(formId);
|
||||||
|
mId = ESM::RefId::formIdRefId(formId);
|
||||||
mFlags = reader.hdr().record.flags;
|
mFlags = reader.hdr().record.flags;
|
||||||
mDataTypes = 0;
|
mDataTypes = 0;
|
||||||
|
mCell = ESM::RefId::formIdRefId(reader.currCell());
|
||||||
TxtLayer layer;
|
TxtLayer layer;
|
||||||
std::int8_t currentAddQuad = -1; // for VTXT following ATXT
|
std::int8_t currentAddQuad = -1; // for VTXT following ATXT
|
||||||
|
|
||||||
|
@ -121,7 +124,7 @@ void ESM4::Land::load(ESM4::Reader& reader)
|
||||||
if (currentAddQuad != -1)
|
if (currentAddQuad != -1)
|
||||||
{
|
{
|
||||||
// FIXME: sometimes there are no VTXT following an ATXT? Just add a dummy one for now
|
// FIXME: sometimes there are no VTXT following an ATXT? Just add a dummy one for now
|
||||||
std::cout << "ESM4::Land VTXT empty layer " << (int)layer.texture.layerIndex << std::endl;
|
Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << (int)layer.texture.layerIndex;
|
||||||
mTextures[currentAddQuad].layers.push_back(layer);
|
mTextures[currentAddQuad].layers.push_back(layer);
|
||||||
}
|
}
|
||||||
reader.get(layer.texture);
|
reader.get(layer.texture);
|
||||||
|
@ -207,8 +210,8 @@ void ESM4::Land::load(ESM4::Reader& reader)
|
||||||
if (currentAddQuad != -1)
|
if (currentAddQuad != -1)
|
||||||
{
|
{
|
||||||
// FIXME: not sure if it happens here as well
|
// FIXME: not sure if it happens here as well
|
||||||
std::cout << "ESM4::Land VTXT empty layer " << (int)layer.texture.layerIndex << " quad "
|
Log(Debug::Verbose) << "ESM4::Land VTXT empty layer " << (int)layer.texture.layerIndex << " quad "
|
||||||
<< (int)layer.texture.quadrant << std::endl;
|
<< (int)layer.texture.quadrant;
|
||||||
mTextures[currentAddQuad].layers.push_back(layer);
|
mTextures[currentAddQuad].layers.push_back(layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@
|
||||||
|
|
||||||
#include "formid.hpp"
|
#include "formid.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/defs.hpp>
|
||||||
|
#include <components/esm/refid.hpp>
|
||||||
|
|
||||||
namespace ESM4
|
namespace ESM4
|
||||||
{
|
{
|
||||||
class Reader;
|
class Reader;
|
||||||
|
@ -109,7 +112,7 @@ namespace ESM4
|
||||||
std::vector<TxtLayer> layers;
|
std::vector<TxtLayer> layers;
|
||||||
};
|
};
|
||||||
|
|
||||||
FormId mFormId; // from the header
|
ESM::RefId mId; // from the header
|
||||||
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
|
std::uint32_t mFlags; // from the header, see enum type RecordFlag for details
|
||||||
|
|
||||||
std::uint32_t mLandFlags; // from DATA subrecord
|
std::uint32_t mLandFlags; // from DATA subrecord
|
||||||
|
@ -117,16 +120,20 @@ namespace ESM4
|
||||||
// FIXME: lazy loading not yet implemented
|
// FIXME: lazy loading not yet implemented
|
||||||
int mDataTypes; // which data types are loaded
|
int mDataTypes; // which data types are loaded
|
||||||
|
|
||||||
|
float mHeights[VERTS_PER_SIDE * VERTS_PER_SIDE]; // Float value of compressed Heightmap
|
||||||
|
float mMinHeight, mMaxHeight;
|
||||||
signed char mVertNorm[VERTS_PER_SIDE * VERTS_PER_SIDE * 3]; // from VNML subrecord
|
signed char mVertNorm[VERTS_PER_SIDE * VERTS_PER_SIDE * 3]; // from VNML subrecord
|
||||||
signed char mVertColr[VERTS_PER_SIDE * VERTS_PER_SIDE * 3]; // from VCLR subrecord
|
unsigned char mVertColr[VERTS_PER_SIDE * VERTS_PER_SIDE * 3]; // from VCLR subrecord
|
||||||
VHGT mHeightMap;
|
VHGT mHeightMap;
|
||||||
Texture mTextures[4]; // 0 = bottom left, 1 = bottom right, 2 = top left, 3 = top right
|
Texture mTextures[4]; // 0 = bottom left, 1 = bottom right, 2 = top left, 3 = top right
|
||||||
std::vector<FormId> mIds; // land texture (LTEX) formids
|
std::vector<FormId> mIds; // land texture (LTEX) formids
|
||||||
|
ESM::RefId mCell;
|
||||||
void load(Reader& reader);
|
void load(Reader& reader);
|
||||||
|
Land() = default;
|
||||||
// void save(Writer& writer) const;
|
// void save(Writer& writer) const;
|
||||||
|
|
||||||
// void blank();
|
// void blank();
|
||||||
|
static constexpr ESM::RecNameInts sRecordId = ESM::REC_LAND4;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,10 @@
|
||||||
#include "loadwrld.hpp"
|
#include "loadwrld.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
//#include <iostream> // FIXME: debug only
|
// #include <iostream> // FIXME: debug only
|
||||||
|
|
||||||
#include "reader.hpp"
|
#include "reader.hpp"
|
||||||
//#include "writer.hpp"
|
// #include "writer.hpp"
|
||||||
|
|
||||||
void ESM4::World::load(ESM4::Reader& reader)
|
void ESM4::World::load(ESM4::Reader& reader)
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace Terrain
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<osg::Group> CellBorder::createBorderGeometry(float x, float y, float size, Terrain::Storage* terrain,
|
osg::ref_ptr<osg::Group> CellBorder::createBorderGeometry(float x, float y, float size, Terrain::Storage* terrain,
|
||||||
Resource::SceneManager* sceneManager, int mask, float offset, osg::Vec4f color)
|
Resource::SceneManager* sceneManager, int mask, ESM::RefId worldspace, float offset, osg::Vec4f color)
|
||||||
{
|
{
|
||||||
const int cellSize = ESM::Land::REAL_SIZE;
|
const int cellSize = ESM::Land::REAL_SIZE;
|
||||||
const int borderSegments = 40;
|
const int borderSegments = 40;
|
||||||
|
@ -45,7 +45,7 @@ namespace Terrain
|
||||||
: osg::Vec3(size, (i - borderSegments) * borderStep, 0.0f);
|
: osg::Vec3(size, (i - borderSegments) * borderStep, 0.0f);
|
||||||
|
|
||||||
pos += cellCorner;
|
pos += cellCorner;
|
||||||
pos += osg::Vec3f(0, 0, terrain->getHeightAt(pos) + offset);
|
pos += osg::Vec3f(0, 0, terrain->getHeightAt(pos, worldspace) + offset);
|
||||||
|
|
||||||
vertices->push_back(pos);
|
vertices->push_back(pos);
|
||||||
|
|
||||||
|
@ -83,7 +83,8 @@ namespace Terrain
|
||||||
|
|
||||||
void CellBorder::createCellBorderGeometry(int x, int y)
|
void CellBorder::createCellBorderGeometry(int x, int y)
|
||||||
{
|
{
|
||||||
auto borderGroup = createBorderGeometry(x, y, 1.f, mWorld->getStorage(), mSceneManager, mBorderMask);
|
auto borderGroup = createBorderGeometry(
|
||||||
|
x, y, 1.f, mWorld->getStorage(), mSceneManager, mBorderMask, mWorld->getWorldspace());
|
||||||
mRoot->addChild(borderGroup);
|
mRoot->addChild(borderGroup);
|
||||||
|
|
||||||
mCellBorderNodes[std::make_pair(x, y)] = borderGroup;
|
mCellBorderNodes[std::make_pair(x, y)] = borderGroup;
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
|
|
||||||
|
#include <components/esm/refid.hpp>
|
||||||
|
|
||||||
namespace Resource
|
namespace Resource
|
||||||
{
|
{
|
||||||
class SceneManager;
|
class SceneManager;
|
||||||
|
@ -33,7 +35,8 @@ namespace Terrain
|
||||||
void destroyCellBorderGeometry();
|
void destroyCellBorderGeometry();
|
||||||
|
|
||||||
static osg::ref_ptr<osg::Group> createBorderGeometry(float x, float y, float size, Storage* terrain,
|
static osg::ref_ptr<osg::Group> createBorderGeometry(float x, float y, float size, Storage* terrain,
|
||||||
Resource::SceneManager* sceneManager, int mask, float offset = 10.0, osg::Vec4f color = { 1, 1, 0, 0 });
|
Resource::SceneManager* sceneManager, int mask, ESM::RefId worldspace, float offset = 10.0,
|
||||||
|
osg::Vec4f color = { 1, 1, 0, 0 });
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Terrain::World* mWorld;
|
Terrain::World* mWorld;
|
||||||
|
|
|
@ -20,8 +20,9 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
|
|
||||||
ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
|
ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
|
||||||
CompositeMapRenderer* renderer)
|
CompositeMapRenderer* renderer, ESM::RefId worldspace)
|
||||||
: GenericResourceManager<ChunkId>(nullptr)
|
: GenericResourceManager<ChunkId>(nullptr)
|
||||||
|
, QuadTreeWorld::ChunkManager(worldspace)
|
||||||
, mStorage(storage)
|
, mStorage(storage)
|
||||||
, mSceneManager(sceneMgr)
|
, mSceneManager(sceneMgr)
|
||||||
, mTextureManager(textureManager)
|
, mTextureManager(textureManager)
|
||||||
|
@ -153,7 +154,7 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
std::vector<LayerInfo> layerList;
|
std::vector<LayerInfo> layerList;
|
||||||
std::vector<osg::ref_ptr<osg::Image>> blendmaps;
|
std::vector<osg::ref_ptr<osg::Image>> blendmaps;
|
||||||
mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList);
|
mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList, mWorldspace);
|
||||||
|
|
||||||
bool useShaders = mSceneManager->getForceShaders();
|
bool useShaders = mSceneManager->getForceShaders();
|
||||||
if (!mSceneManager->getClampLighting())
|
if (!mSceneManager->getClampLighting())
|
||||||
|
@ -212,7 +213,7 @@ namespace Terrain
|
||||||
osg::ref_ptr<osg::Vec4ubArray> colors(new osg::Vec4ubArray);
|
osg::ref_ptr<osg::Vec4ubArray> colors(new osg::Vec4ubArray);
|
||||||
colors->setNormalize(true);
|
colors->setNormalize(true);
|
||||||
|
|
||||||
mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors);
|
mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, mWorldspace, positions, normals, colors);
|
||||||
|
|
||||||
osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject);
|
osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject);
|
||||||
positions->setVertexBufferObject(vbo);
|
positions->setVertexBufferObject(vbo);
|
||||||
|
@ -249,7 +250,7 @@ namespace Terrain
|
||||||
if (chunkSize <= 1.f)
|
if (chunkSize <= 1.f)
|
||||||
geometry->setLightListCallback(new SceneUtil::LightListCallback);
|
geometry->setLightListCallback(new SceneUtil::LightListCallback);
|
||||||
|
|
||||||
unsigned int numVerts = (mStorage->getCellVertices() - 1) * chunkSize / (1 << lod) + 1;
|
unsigned int numVerts = (mStorage->getCellVertices(mWorldspace) - 1) * chunkSize / (1 << lod) + 1;
|
||||||
|
|
||||||
geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags));
|
geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags));
|
||||||
|
|
||||||
|
@ -299,7 +300,7 @@ namespace Terrain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts);
|
geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize(mWorldspace) / numVerts);
|
||||||
|
|
||||||
if (!templateGeometry && compile && mSceneManager->getIncrementalCompileOperation())
|
if (!templateGeometry && compile && mSceneManager->getIncrementalCompileOperation())
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
|
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
|
||||||
CompositeMapRenderer* renderer);
|
CompositeMapRenderer* renderer, ESM::RefId worldspace);
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags,
|
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags,
|
||||||
bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;
|
bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;
|
||||||
|
|
|
@ -39,13 +39,14 @@ namespace Terrain
|
||||||
class DefaultLodCallback : public LodCallback
|
class DefaultLodCallback : public LodCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DefaultLodCallback(
|
DefaultLodCallback(float factor, float minSize, float viewDistance, const osg::Vec4i& grid, int cellSizeInUnits,
|
||||||
float factor, float minSize, float viewDistance, const osg::Vec4i& grid, float distanceModifier = 0.f)
|
float distanceModifier = 0.f)
|
||||||
: mFactor(factor)
|
: mFactor(factor)
|
||||||
, mMinSize(minSize)
|
, mMinSize(minSize)
|
||||||
, mViewDistance(viewDistance)
|
, mViewDistance(viewDistance)
|
||||||
, mActiveGrid(grid)
|
, mActiveGrid(grid)
|
||||||
, mDistanceModifier(distanceModifier)
|
, mDistanceModifier(distanceModifier)
|
||||||
|
, mCellSizeInUnits(cellSizeInUnits)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +70,8 @@ namespace Terrain
|
||||||
dist = std::max(0.f, dist + mDistanceModifier);
|
dist = std::max(0.f, dist + mDistanceModifier);
|
||||||
if (dist > mViewDistance && !activeGrid) // for Scene<->ObjectPaging sync the activegrid must remain loaded
|
if (dist > mViewDistance && !activeGrid) // for Scene<->ObjectPaging sync the activegrid must remain loaded
|
||||||
return StopTraversal;
|
return StopTraversal;
|
||||||
return getNativeLodLevel(node, mMinSize) <= convertDistanceToLodLevel(dist, mMinSize, mFactor)
|
return getNativeLodLevel(node, mMinSize)
|
||||||
|
<= convertDistanceToLodLevel(dist, mMinSize, mFactor, mCellSizeInUnits)
|
||||||
? StopTraversalAndUse
|
? StopTraversalAndUse
|
||||||
: Deeper;
|
: Deeper;
|
||||||
}
|
}
|
||||||
|
@ -77,9 +79,9 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
return Log2(static_cast<unsigned int>(node->getSize() / minSize));
|
return Log2(static_cast<unsigned int>(node->getSize() / minSize));
|
||||||
}
|
}
|
||||||
static unsigned int convertDistanceToLodLevel(float dist, float minSize, float factor)
|
static unsigned int convertDistanceToLodLevel(float dist, float minSize, float factor, int cellSize)
|
||||||
{
|
{
|
||||||
return Log2(static_cast<unsigned int>(dist / (Constants::CellSizeInUnits * minSize * factor)));
|
return Log2(static_cast<unsigned int>(dist / (cellSize * minSize * factor)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -88,6 +90,7 @@ namespace Terrain
|
||||||
float mViewDistance;
|
float mViewDistance;
|
||||||
osg::Vec4i mActiveGrid;
|
osg::Vec4i mActiveGrid;
|
||||||
float mDistanceModifier;
|
float mDistanceModifier;
|
||||||
|
int mCellSizeInUnits;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RootNode : public QuadTreeNode
|
class RootNode : public QuadTreeNode
|
||||||
|
@ -117,19 +120,20 @@ namespace Terrain
|
||||||
class QuadTreeBuilder
|
class QuadTreeBuilder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QuadTreeBuilder(Terrain::Storage* storage, float minSize)
|
QuadTreeBuilder(Terrain::Storage* storage, float minSize, ESM::RefId worldspace)
|
||||||
: mStorage(storage)
|
: mStorage(storage)
|
||||||
, mMinX(0.f)
|
, mMinX(0.f)
|
||||||
, mMaxX(0.f)
|
, mMaxX(0.f)
|
||||||
, mMinY(0.f)
|
, mMinY(0.f)
|
||||||
, mMaxY(0.f)
|
, mMaxY(0.f)
|
||||||
, mMinSize(minSize)
|
, mMinSize(minSize)
|
||||||
|
, mWorldspace(worldspace)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void build()
|
void build()
|
||||||
{
|
{
|
||||||
mStorage->getBounds(mMinX, mMaxX, mMinY, mMaxY);
|
mStorage->getBounds(mMinX, mMaxX, mMinY, mMaxY, mWorldspace);
|
||||||
|
|
||||||
int origSizeX = static_cast<int>(mMaxX - mMinX);
|
int origSizeX = static_cast<int>(mMaxX - mMinX);
|
||||||
int origSizeY = static_cast<int>(mMaxY - mMinY);
|
int origSizeY = static_cast<int>(mMaxY - mMinY);
|
||||||
|
@ -144,7 +148,7 @@ namespace Terrain
|
||||||
addChildren(mRootNode);
|
addChildren(mRootNode);
|
||||||
|
|
||||||
mRootNode->initNeighbours();
|
mRootNode->initNeighbours();
|
||||||
float cellWorldSize = mStorage->getCellWorldSize();
|
float cellWorldSize = mStorage->getCellWorldSize(mWorldspace);
|
||||||
mRootNode->setInitialBound(
|
mRootNode->setInitialBound(
|
||||||
osg::BoundingSphere(osg::BoundingBox(osg::Vec3(mMinX * cellWorldSize, mMinY * cellWorldSize, 0),
|
osg::BoundingSphere(osg::BoundingBox(osg::Vec3(mMinX * cellWorldSize, mMinY * cellWorldSize, 0),
|
||||||
osg::Vec3(mMaxX * cellWorldSize, mMaxY * cellWorldSize, 0))));
|
osg::Vec3(mMaxX * cellWorldSize, mMaxY * cellWorldSize, 0))));
|
||||||
|
@ -206,7 +210,8 @@ namespace Terrain
|
||||||
|
|
||||||
// Do not add child nodes for default cells without data.
|
// Do not add child nodes for default cells without data.
|
||||||
// size = 1 means that the single shape covers the whole cell.
|
// size = 1 means that the single shape covers the whole cell.
|
||||||
if (node->getSize() == 1 && !mStorage->hasData(center.x() - 0.5, center.y() - 0.5))
|
if (node->getSize() == 1
|
||||||
|
&& !mStorage->hasData(ESM::ExteriorCellLocation(center.x() - 0.5, center.y() - 0.5, mWorldspace)))
|
||||||
return node;
|
return node;
|
||||||
|
|
||||||
if (node->getSize() <= mMinSize)
|
if (node->getSize() <= mMinSize)
|
||||||
|
@ -216,7 +221,7 @@ namespace Terrain
|
||||||
// height data here.
|
// height data here.
|
||||||
constexpr float minZ = -std::numeric_limits<float>::max();
|
constexpr float minZ = -std::numeric_limits<float>::max();
|
||||||
constexpr float maxZ = std::numeric_limits<float>::max();
|
constexpr float maxZ = std::numeric_limits<float>::max();
|
||||||
float cellWorldSize = mStorage->getCellWorldSize();
|
float cellWorldSize = mStorage->getCellWorldSize(mWorldspace);
|
||||||
osg::BoundingBox boundingBox(
|
osg::BoundingBox boundingBox(
|
||||||
osg::Vec3f((center.x() - halfSize) * cellWorldSize, (center.y() - halfSize) * cellWorldSize, minZ),
|
osg::Vec3f((center.x() - halfSize) * cellWorldSize, (center.y() - halfSize) * cellWorldSize, minZ),
|
||||||
osg::Vec3f((center.x() + halfSize) * cellWorldSize, (center.y() + halfSize) * cellWorldSize, maxZ));
|
osg::Vec3f((center.x() + halfSize) * cellWorldSize, (center.y() + halfSize) * cellWorldSize, maxZ));
|
||||||
|
@ -239,13 +244,16 @@ namespace Terrain
|
||||||
float mMinSize;
|
float mMinSize;
|
||||||
|
|
||||||
osg::ref_ptr<RootNode> mRootNode;
|
osg::ref_ptr<RootNode> mRootNode;
|
||||||
|
ESM::RefId mWorldspace;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DebugChunkManager : public QuadTreeWorld::ChunkManager
|
class DebugChunkManager : public QuadTreeWorld::ChunkManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DebugChunkManager(Resource::SceneManager* sceneManager, Storage* storage, unsigned int nodeMask)
|
DebugChunkManager(
|
||||||
: mSceneManager(sceneManager)
|
Resource::SceneManager* sceneManager, Storage* storage, unsigned int nodeMask, ESM::RefId worldspace)
|
||||||
|
: QuadTreeWorld::ChunkManager(worldspace)
|
||||||
|
, mSceneManager(sceneManager)
|
||||||
, mStorage(storage)
|
, mStorage(storage)
|
||||||
, mNodeMask(nodeMask)
|
, mNodeMask(nodeMask)
|
||||||
{
|
{
|
||||||
|
@ -255,9 +263,9 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
osg::Vec3f center = { chunkCenter.x(), chunkCenter.y(), 0 };
|
osg::Vec3f center = { chunkCenter.x(), chunkCenter.y(), 0 };
|
||||||
auto chunkBorder = CellBorder::createBorderGeometry(center.x() - size / 2.f, center.y() - size / 2.f, size,
|
auto chunkBorder = CellBorder::createBorderGeometry(center.x() - size / 2.f, center.y() - size / 2.f, size,
|
||||||
mStorage, mSceneManager, mNodeMask, 5.f, { 1, 0, 0, 0 });
|
mStorage, mSceneManager, mNodeMask, mWorldspace, 5.f, { 1, 0, 0, 0 });
|
||||||
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;
|
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;
|
||||||
pat->setPosition(-center * Constants::CellSizeInUnits);
|
pat->setPosition(-center * ESM::getCellSize(mWorldspace));
|
||||||
pat->addChild(chunkBorder);
|
pat->addChild(chunkBorder);
|
||||||
return pat;
|
return pat;
|
||||||
}
|
}
|
||||||
|
@ -272,14 +280,14 @@ namespace Terrain
|
||||||
QuadTreeWorld::QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
QuadTreeWorld::QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
||||||
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask,
|
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask,
|
||||||
int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize,
|
int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize,
|
||||||
bool debugChunks)
|
bool debugChunks, ESM::RefId worldspace)
|
||||||
: TerrainGrid(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask)
|
: TerrainGrid(parent, compileRoot, resourceSystem, storage, nodeMask, worldspace, preCompileMask, borderMask)
|
||||||
, mViewDataMap(new ViewDataMap)
|
, mViewDataMap(new ViewDataMap)
|
||||||
, mQuadTreeBuilt(false)
|
, mQuadTreeBuilt(false)
|
||||||
, mLodFactor(lodFactor)
|
, mLodFactor(lodFactor)
|
||||||
, mVertexLodMod(vertexLodMod)
|
, mVertexLodMod(vertexLodMod)
|
||||||
, mViewDistance(std::numeric_limits<float>::max())
|
, mViewDistance(std::numeric_limits<float>::max())
|
||||||
, mMinSize(1 / 8.f)
|
, mMinSize(ESM::isEsm4Ext(worldspace) ? 1 / 4.f : 1 / 8.f)
|
||||||
, mDebugTerrainChunks(debugChunks)
|
, mDebugTerrainChunks(debugChunks)
|
||||||
{
|
{
|
||||||
mChunkManager->setCompositeMapSize(compMapResolution);
|
mChunkManager->setCompositeMapSize(compMapResolution);
|
||||||
|
@ -289,8 +297,8 @@ namespace Terrain
|
||||||
|
|
||||||
if (mDebugTerrainChunks)
|
if (mDebugTerrainChunks)
|
||||||
{
|
{
|
||||||
mDebugChunkManager
|
mDebugChunkManager = std::make_unique<DebugChunkManager>(
|
||||||
= std::make_unique<DebugChunkManager>(mResourceSystem->getSceneManager(), mStorage, borderMask);
|
mResourceSystem->getSceneManager(), mStorage, borderMask, mWorldspace);
|
||||||
addChunkManager(mDebugChunkManager.get());
|
addChunkManager(mDebugChunkManager.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -466,11 +474,12 @@ namespace Terrain
|
||||||
if (needsUpdate)
|
if (needsUpdate)
|
||||||
{
|
{
|
||||||
vd->reset();
|
vd->reset();
|
||||||
DefaultLodCallback lodCallback(mLodFactor, mMinSize, mViewDistance, mActiveGrid);
|
DefaultLodCallback lodCallback(
|
||||||
|
mLodFactor, mMinSize, mViewDistance, mActiveGrid, ESM::getCellSize(mWorldspace));
|
||||||
mRootNode->traverseNodes(vd, viewPoint, &lodCallback);
|
mRootNode->traverseNodes(vd, viewPoint, &lodCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
const float cellWorldSize = mStorage->getCellWorldSize();
|
const float cellWorldSize = ESM::getCellSize(mWorldspace);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < vd->getNumEntries(); ++i)
|
for (unsigned int i = 0; i < vd->getNumEntries(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -481,7 +490,7 @@ namespace Terrain
|
||||||
|
|
||||||
if (mHeightCullCallback && isCullVisitor)
|
if (mHeightCullCallback && isCullVisitor)
|
||||||
updateWaterCullingView(mHeightCullCallback, vd, static_cast<osgUtil::CullVisitor*>(&nv),
|
updateWaterCullingView(mHeightCullCallback, vd, static_cast<osgUtil::CullVisitor*>(&nv),
|
||||||
mStorage->getCellWorldSize(), !isGridEmpty());
|
mStorage->getCellWorldSize(mWorldspace), !isGridEmpty());
|
||||||
|
|
||||||
vd->resetChanged();
|
vd->resetChanged();
|
||||||
|
|
||||||
|
@ -499,7 +508,7 @@ namespace Terrain
|
||||||
if (mQuadTreeBuilt)
|
if (mQuadTreeBuilt)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QuadTreeBuilder builder(mStorage, mMinSize);
|
QuadTreeBuilder builder(mStorage, mMinSize, mWorldspace);
|
||||||
builder.build();
|
builder.build();
|
||||||
|
|
||||||
mRootNode = builder.getRootNode();
|
mRootNode = builder.getRootNode();
|
||||||
|
@ -529,7 +538,7 @@ namespace Terrain
|
||||||
std::atomic<bool>& abort, Loading::Reporter& reporter)
|
std::atomic<bool>& abort, Loading::Reporter& reporter)
|
||||||
{
|
{
|
||||||
ensureQuadTreeBuilt();
|
ensureQuadTreeBuilt();
|
||||||
const float cellWorldSize = mStorage->getCellWorldSize();
|
const float cellWorldSize = mStorage->getCellWorldSize(mWorldspace);
|
||||||
|
|
||||||
ViewData* vd = static_cast<ViewData*>(view);
|
ViewData* vd = static_cast<ViewData*>(view);
|
||||||
vd->setViewPoint(viewPoint);
|
vd->setViewPoint(viewPoint);
|
||||||
|
@ -544,7 +553,7 @@ namespace Terrain
|
||||||
distanceModifier = 1024;
|
distanceModifier = 1024;
|
||||||
else if (pass == 2)
|
else if (pass == 2)
|
||||||
distanceModifier = -1024;
|
distanceModifier = -1024;
|
||||||
DefaultLodCallback lodCallback(mLodFactor, mMinSize, mViewDistance, grid, distanceModifier);
|
DefaultLodCallback lodCallback(mLodFactor, mMinSize, mViewDistance, grid, cellWorldSize, distanceModifier);
|
||||||
mRootNode->traverseNodes(vd, viewPoint, &lodCallback);
|
mRootNode->traverseNodes(vd, viewPoint, &lodCallback);
|
||||||
|
|
||||||
if (pass == 0)
|
if (pass == 0)
|
||||||
|
@ -579,7 +588,7 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
// fallback behavior only for undefined cells (every other is already handled in quadtree)
|
// fallback behavior only for undefined cells (every other is already handled in quadtree)
|
||||||
float dummy;
|
float dummy;
|
||||||
if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x + 0.5, y + 0.5), dummy, dummy))
|
if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x + 0.5, y + 0.5), mWorldspace, dummy, dummy))
|
||||||
TerrainGrid::loadCell(x, y);
|
TerrainGrid::loadCell(x, y);
|
||||||
else
|
else
|
||||||
World::loadCell(x, y);
|
World::loadCell(x, y);
|
||||||
|
@ -589,7 +598,7 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
// fallback behavior only for undefined cells (every other is already handled in quadtree)
|
// fallback behavior only for undefined cells (every other is already handled in quadtree)
|
||||||
float dummy;
|
float dummy;
|
||||||
if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x + 0.5, y + 0.5), dummy, dummy))
|
if (mChunkManager && !mStorage->getMinMaxHeights(1, osg::Vec2f(x + 0.5, y + 0.5), mWorldspace, dummy, dummy))
|
||||||
TerrainGrid::unloadCell(x, y);
|
TerrainGrid::unloadCell(x, y);
|
||||||
else
|
else
|
||||||
World::unloadCell(x, y);
|
World::unloadCell(x, y);
|
||||||
|
@ -600,8 +609,9 @@ namespace Terrain
|
||||||
mChunkManagers.push_back(m);
|
mChunkManagers.push_back(m);
|
||||||
mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask() | m->getNodeMask());
|
mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask() | m->getNodeMask());
|
||||||
if (m->getViewDistance())
|
if (m->getViewDistance())
|
||||||
m->setMaxLodLevel(DefaultLodCallback::convertDistanceToLodLevel(
|
m->setMaxLodLevel(
|
||||||
m->getViewDistance() + mViewDataMap->getReuseDistance(), mMinSize, mLodFactor));
|
DefaultLodCallback::convertDistanceToLodLevel(m->getViewDistance() + mViewDataMap->getReuseDistance(),
|
||||||
|
mMinSize, ESM::getCellSize(mWorldspace), mLodFactor));
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeWorld::rebuildViews()
|
void QuadTreeWorld::rebuildViews()
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <components/esm/refid.hpp>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
class NodeVisitor;
|
class NodeVisitor;
|
||||||
|
@ -31,7 +33,7 @@ namespace Terrain
|
||||||
QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
||||||
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask,
|
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask,
|
||||||
int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize,
|
int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize,
|
||||||
bool debugChunks);
|
bool debugChunks, ESM::RefId worldspace);
|
||||||
|
|
||||||
~QuadTreeWorld();
|
~QuadTreeWorld();
|
||||||
|
|
||||||
|
@ -58,6 +60,12 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~ChunkManager() {}
|
virtual ~ChunkManager() {}
|
||||||
|
ChunkManager() = default;
|
||||||
|
ChunkManager(ESM::RefId worldspace)
|
||||||
|
: ChunkManager()
|
||||||
|
{
|
||||||
|
mWorldspace = worldspace;
|
||||||
|
}
|
||||||
virtual osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod,
|
virtual osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod,
|
||||||
unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
|
unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
|
||||||
= 0;
|
= 0;
|
||||||
|
@ -70,6 +78,9 @@ namespace Terrain
|
||||||
unsigned int getMaxLodLevel() const { return mMaxLodLevel; }
|
unsigned int getMaxLodLevel() const { return mMaxLodLevel; }
|
||||||
void setMaxLodLevel(unsigned int level) { mMaxLodLevel = level; }
|
void setMaxLodLevel(unsigned int level) { mMaxLodLevel = level; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ESM::RefId mWorldspace = ESM::RefId();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float mViewDistance = 0.f;
|
float mViewDistance = 0.f;
|
||||||
unsigned int mMaxLodLevel = ~0u;
|
unsigned int mMaxLodLevel = ~0u;
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
#include <osg/Vec3f>
|
#include <osg/Vec3f>
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
#include <components/esm/refid.hpp>
|
||||||
|
#include <components/esm/util.hpp>
|
||||||
|
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
|
@ -26,14 +29,15 @@ namespace Terrain
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// 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) = 0;
|
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY, ESM::RefId worldspace) = 0;
|
||||||
|
|
||||||
/// Return true if there is land data for this cell
|
/// Return true if there is land data for this cell
|
||||||
/// May be overriden for a faster implementation
|
/// May be overriden for a faster implementation
|
||||||
virtual bool hasData(int cellX, int cellY)
|
virtual bool hasData(ESM::ExteriorCellLocation cellLocation)
|
||||||
{
|
{
|
||||||
float dummy;
|
float dummy;
|
||||||
return getMinMaxHeights(1, osg::Vec2f(cellX + 0.5, cellY + 0.5), dummy, dummy);
|
return getMinMaxHeights(
|
||||||
|
1, osg::Vec2f(cellLocation.mX + 0.5, cellLocation.mY + 0.5), cellLocation.mWorldspace, dummy, dummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the minimum and maximum heights of a terrain region.
|
/// Get the minimum and maximum heights of a terrain region.
|
||||||
|
@ -44,7 +48,9 @@ namespace Terrain
|
||||||
/// @param min min height will be stored here
|
/// @param min min height will be stored here
|
||||||
/// @param max max height will be stored here
|
/// @param max max height will be stored here
|
||||||
/// @return true if there was data available for this terrain chunk
|
/// @return true if there was data available for this terrain chunk
|
||||||
virtual bool getMinMaxHeights(float size, const osg::Vec2f& center, float& min, float& max) = 0;
|
virtual bool getMinMaxHeights(
|
||||||
|
float size, const osg::Vec2f& center, ESM::RefId worldspace, float& min, float& max)
|
||||||
|
= 0;
|
||||||
|
|
||||||
/// Fill vertex buffers for a terrain chunk.
|
/// Fill vertex buffers for a terrain chunk.
|
||||||
/// @note May be called from background threads. Make sure to only call thread-safe functions from here!
|
/// @note May be called from background threads. Make sure to only call thread-safe functions from here!
|
||||||
|
@ -57,7 +63,7 @@ namespace Terrain
|
||||||
/// @param positions buffer to write vertices
|
/// @param positions buffer to write vertices
|
||||||
/// @param normals buffer to write vertex normals
|
/// @param normals buffer to write vertex normals
|
||||||
/// @param colours buffer to write vertex colours
|
/// @param colours buffer to write vertex colours
|
||||||
virtual void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center,
|
virtual void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center, ESM::RefId worldspace,
|
||||||
osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
|
osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
|
||||||
osg::ref_ptr<osg::Vec4ubArray> colours)
|
osg::ref_ptr<osg::Vec4ubArray> colours)
|
||||||
= 0;
|
= 0;
|
||||||
|
@ -71,17 +77,17 @@ namespace Terrain
|
||||||
/// @param chunkCenter center of the chunk in cell units
|
/// @param chunkCenter center of the chunk in cell units
|
||||||
/// @param blendmaps created blendmaps will be written here
|
/// @param blendmaps created blendmaps will be written here
|
||||||
/// @param layerList names of the layer textures used will be written here
|
/// @param layerList names of the layer textures used will be written here
|
||||||
virtual void getBlendmaps(
|
virtual void getBlendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
|
||||||
float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, std::vector<LayerInfo>& layerList)
|
std::vector<LayerInfo>& layerList, ESM::RefId worldspace)
|
||||||
= 0;
|
= 0;
|
||||||
|
|
||||||
virtual float getHeightAt(const osg::Vec3f& worldPos) = 0;
|
virtual float getHeightAt(const osg::Vec3f& worldPos, ESM::RefId worldspace) = 0;
|
||||||
|
|
||||||
/// Get the transformation factor for mapping cell units to world units.
|
/// Get the transformation factor for mapping cell units to world units.
|
||||||
virtual float getCellWorldSize() = 0;
|
virtual float getCellWorldSize(ESM::RefId worldspace) = 0;
|
||||||
|
|
||||||
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
|
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
|
||||||
virtual int getCellVertices() = 0;
|
virtual int getCellVertices(ESM::RefId worldspace) = 0;
|
||||||
|
|
||||||
virtual int getBlendmapScale(float chunkSize) = 0;
|
virtual int getBlendmapScale(float chunkSize) = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,14 +23,15 @@ namespace Terrain
|
||||||
};
|
};
|
||||||
|
|
||||||
TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
||||||
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask)
|
Storage* storage, unsigned int nodeMask, ESM::RefId worldspace, unsigned int preCompileMask,
|
||||||
: Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask)
|
unsigned int borderMask)
|
||||||
|
: Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask, worldspace)
|
||||||
, mNumSplits(4)
|
, mNumSplits(4)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TerrainGrid::TerrainGrid(osg::Group* parent, Storage* storage, unsigned int nodeMask)
|
TerrainGrid::TerrainGrid(osg::Group* parent, Storage* storage, ESM::RefId worldspace, unsigned int nodeMask)
|
||||||
: Terrain::World(parent, storage, nodeMask)
|
: Terrain::World(parent, storage, nodeMask, worldspace)
|
||||||
, mNumSplits(4)
|
, mNumSplits(4)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -73,7 +74,7 @@ namespace Terrain
|
||||||
if (!node)
|
if (!node)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const float cellWorldSize = mStorage->getCellWorldSize();
|
const float cellWorldSize = mStorage->getCellWorldSize(mWorldspace);
|
||||||
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;
|
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;
|
||||||
pat->setPosition(osg::Vec3f(chunkCenter.x() * cellWorldSize, chunkCenter.y() * cellWorldSize, 0.f));
|
pat->setPosition(osg::Vec3f(chunkCenter.x() * cellWorldSize, chunkCenter.y() * cellWorldSize, 0.f));
|
||||||
pat->addChild(node);
|
pat->addChild(node);
|
||||||
|
|
|
@ -27,8 +27,9 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
||||||
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask = ~0u, unsigned int borderMask = 0);
|
Storage* storage, unsigned int nodeMask, ESM::RefId worldspace, unsigned int preCompileMask = ~0u,
|
||||||
TerrainGrid(osg::Group* parent, Storage* storage, unsigned int nodeMask = ~0u);
|
unsigned int borderMask = 0);
|
||||||
|
TerrainGrid(osg::Group* parent, Storage* storage, ESM::RefId worldspace, unsigned int nodeMask = ~0u);
|
||||||
~TerrainGrid();
|
~TerrainGrid();
|
||||||
|
|
||||||
void cacheCell(View* view, int x, int y) override;
|
void cacheCell(View* view, int x, int y) override;
|
||||||
|
|
|
@ -16,12 +16,14 @@ namespace Terrain
|
||||||
{
|
{
|
||||||
|
|
||||||
World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
|
||||||
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask)
|
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask,
|
||||||
|
ESM::RefId worldspace)
|
||||||
: mStorage(storage)
|
: mStorage(storage)
|
||||||
, mParent(parent)
|
, mParent(parent)
|
||||||
, mResourceSystem(resourceSystem)
|
, mResourceSystem(resourceSystem)
|
||||||
, mBorderVisible(false)
|
, mBorderVisible(false)
|
||||||
, mHeightCullCallback(new HeightCullCallback)
|
, mHeightCullCallback(new HeightCullCallback)
|
||||||
|
, mWorldspace(worldspace)
|
||||||
{
|
{
|
||||||
mTerrainRoot = new osg::Group;
|
mTerrainRoot = new osg::Group;
|
||||||
mTerrainRoot->setNodeMask(nodeMask);
|
mTerrainRoot->setNodeMask(nodeMask);
|
||||||
|
@ -45,7 +47,7 @@ namespace Terrain
|
||||||
|
|
||||||
mTextureManager = std::make_unique<TextureManager>(mResourceSystem->getSceneManager());
|
mTextureManager = std::make_unique<TextureManager>(mResourceSystem->getSceneManager());
|
||||||
mChunkManager = std::make_unique<ChunkManager>(
|
mChunkManager = std::make_unique<ChunkManager>(
|
||||||
mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer);
|
mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer, mWorldspace);
|
||||||
mChunkManager->setNodeMask(nodeMask);
|
mChunkManager->setNodeMask(nodeMask);
|
||||||
mCellBorder
|
mCellBorder
|
||||||
= std::make_unique<CellBorder>(this, mTerrainRoot.get(), borderMask, mResourceSystem->getSceneManager());
|
= std::make_unique<CellBorder>(this, mTerrainRoot.get(), borderMask, mResourceSystem->getSceneManager());
|
||||||
|
@ -54,7 +56,7 @@ namespace Terrain
|
||||||
mResourceSystem->addResourceManager(mTextureManager.get());
|
mResourceSystem->addResourceManager(mTextureManager.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
World::World(osg::Group* parent, Storage* storage, unsigned int nodeMask)
|
World::World(osg::Group* parent, Storage* storage, unsigned int nodeMask, ESM::RefId worldspace)
|
||||||
: mStorage(storage)
|
: mStorage(storage)
|
||||||
, mParent(parent)
|
, mParent(parent)
|
||||||
, mCompositeMapCamera(nullptr)
|
, mCompositeMapCamera(nullptr)
|
||||||
|
@ -65,6 +67,7 @@ namespace Terrain
|
||||||
, mCellBorder(nullptr)
|
, mCellBorder(nullptr)
|
||||||
, mBorderVisible(false)
|
, mBorderVisible(false)
|
||||||
, mHeightCullCallback(nullptr)
|
, mHeightCullCallback(nullptr)
|
||||||
|
, mWorldspace(worldspace)
|
||||||
{
|
{
|
||||||
mTerrainRoot = new osg::Group;
|
mTerrainRoot = new osg::Group;
|
||||||
mTerrainRoot->setNodeMask(nodeMask);
|
mTerrainRoot->setNodeMask(nodeMask);
|
||||||
|
@ -124,7 +127,7 @@ namespace Terrain
|
||||||
|
|
||||||
float World::getHeightAt(const osg::Vec3f& worldPos)
|
float World::getHeightAt(const osg::Vec3f& worldPos)
|
||||||
{
|
{
|
||||||
return mStorage->getHeightAt(worldPos);
|
return mStorage->getHeightAt(worldPos, mWorldspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::updateTextureFiltering()
|
void World::updateTextureFiltering()
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
|
#include <components/esm/refid.hpp>
|
||||||
|
|
||||||
#include "cellborder.hpp"
|
#include "cellborder.hpp"
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
|
@ -48,8 +50,8 @@ namespace Terrain
|
||||||
/// @param nodeMask mask for the terrain root
|
/// @param nodeMask mask for the terrain root
|
||||||
/// @param preCompileMask mask for pre compiling textures
|
/// @param preCompileMask mask for pre compiling textures
|
||||||
World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage,
|
World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage,
|
||||||
unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask);
|
unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask, ESM::RefId worldspace);
|
||||||
World(osg::Group* parent, Storage* storage, unsigned int nodeMask);
|
World(osg::Group* parent, Storage* storage, unsigned int nodeMask, ESM::RefId worldspace);
|
||||||
virtual ~World();
|
virtual ~World();
|
||||||
|
|
||||||
/// See CompositeMapRenderer::setTargetFrameRate
|
/// See CompositeMapRenderer::setTargetFrameRate
|
||||||
|
@ -99,6 +101,8 @@ namespace Terrain
|
||||||
|
|
||||||
virtual void setViewDistance(float distance) {}
|
virtual void setViewDistance(float distance) {}
|
||||||
|
|
||||||
|
ESM::RefId getWorldspace() { return mWorldspace; }
|
||||||
|
|
||||||
Storage* getStorage() { return mStorage; }
|
Storage* getStorage() { return mStorage; }
|
||||||
|
|
||||||
osg::Callback* getHeightCullCallback(float highz, unsigned int mask);
|
osg::Callback* getHeightCullCallback(float highz, unsigned int mask);
|
||||||
|
@ -127,6 +131,7 @@ namespace Terrain
|
||||||
osg::ref_ptr<HeightCullCallback> mHeightCullCallback;
|
osg::ref_ptr<HeightCullCallback> mHeightCullCallback;
|
||||||
|
|
||||||
osg::Vec4i mActiveGrid;
|
osg::Vec4i mActiveGrid;
|
||||||
|
ESM::RefId mWorldspace;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue