mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-03 16:19:41 +00:00
Merge pull request #2190 from akortunov/terrain
Make Distant Terrain configurable
This commit is contained in:
commit
cd70354f34
8 changed files with 186 additions and 35 deletions
|
@ -47,6 +47,7 @@
|
|||
Feature #4812: Support NiSwitchNode
|
||||
Feature #4836: Daytime node switch
|
||||
Feature #4887: Add openmw command option to set initial random seed
|
||||
Feature #4890: Make Distant Terrain configurable
|
||||
Task #4686: Upgrade media decoder to a more current FFmpeg API
|
||||
|
||||
0.45.0
|
||||
|
|
|
@ -283,12 +283,29 @@ namespace MWRender
|
|||
|
||||
mDistantFog = Settings::Manager::getBool("use distant fog", "Fog");
|
||||
mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
|
||||
mTerrainStorage = new TerrainStorage(mResourceSystem, Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getString("normal height map pattern", "Shaders"),
|
||||
Settings::Manager::getBool("auto use terrain normal maps", "Shaders"), Settings::Manager::getString("terrain specular map pattern", "Shaders"),
|
||||
Settings::Manager::getBool("auto use terrain specular maps", "Shaders"));
|
||||
|
||||
const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders");
|
||||
const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders");
|
||||
const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders");
|
||||
const bool useTerrainNormalMaps = Settings::Manager::getBool("auto use terrain normal maps", "Shaders");
|
||||
const bool useTerrainSpecularMaps = Settings::Manager::getBool("auto use terrain specular maps", "Shaders");
|
||||
|
||||
mTerrainStorage = new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps);
|
||||
|
||||
if (mDistantTerrain)
|
||||
mTerrain.reset(new Terrain::QuadTreeWorld(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug));
|
||||
{
|
||||
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 float lodFactor = Settings::Manager::getFloat("lod factor", "Terrain");
|
||||
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);
|
||||
mTerrain.reset(new Terrain::QuadTreeWorld(
|
||||
sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug,
|
||||
compMapResolution, compMapLevel, lodFactor, vertexLodMod, maxCompGeometrySize));
|
||||
}
|
||||
else
|
||||
mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug));
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, T
|
|||
, mTextureManager(textureManager)
|
||||
, mCompositeMapRenderer(renderer)
|
||||
, mCompositeMapSize(512)
|
||||
, mCompositeMapLevel(1.f)
|
||||
, mMaxCompGeometrySize(1.f)
|
||||
, mCullingActive(true)
|
||||
{
|
||||
|
||||
|
@ -68,11 +70,6 @@ void ChunkManager::releaseGLObjects(osg::State *state)
|
|||
mBufferCache.releaseGLObjects(state);
|
||||
}
|
||||
|
||||
void ChunkManager::setCullingActive(bool active)
|
||||
{
|
||||
mCullingActive = active;
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> ChunkManager::createCompositeMapRTT()
|
||||
{
|
||||
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
|
||||
|
@ -89,7 +86,7 @@ osg::ref_ptr<osg::Texture2D> ChunkManager::createCompositeMapRTT()
|
|||
|
||||
void ChunkManager::createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& compositeMap)
|
||||
{
|
||||
if (chunkSize > 1.f)
|
||||
if (chunkSize > mMaxCompGeometrySize)
|
||||
{
|
||||
createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMap);
|
||||
createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(-chunkSize/4.f, chunkSize/4.f), osg::Vec4f(texCoords.x(), texCoords.y(), texCoords.z()/2.f, texCoords.w()/2.f), compositeMap);
|
||||
|
@ -199,7 +196,7 @@ osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Ve
|
|||
|
||||
geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags));
|
||||
|
||||
bool useCompositeMap = chunkSize >= 1.f;
|
||||
bool useCompositeMap = chunkSize >= mCompositeMapLevel;
|
||||
unsigned int numUvSets = useCompositeMap ? 1 : 2;
|
||||
|
||||
for (unsigned int i=0; i<numUvSets; ++i)
|
||||
|
|
|
@ -32,14 +32,17 @@ namespace Terrain
|
|||
|
||||
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags);
|
||||
|
||||
void setCullingActive(bool active) { mCullingActive = active; }
|
||||
void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; }
|
||||
void setCompositeMapLevel(float level) { mCompositeMapLevel = level; }
|
||||
void setMaxCompositeGeometrySize(float maxCompGeometrySize) { mMaxCompGeometrySize = maxCompGeometrySize; }
|
||||
|
||||
void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
|
||||
|
||||
void clearCache() override;
|
||||
|
||||
void releaseGLObjects(osg::State* state) override;
|
||||
|
||||
void setCullingActive(bool active);
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags);
|
||||
|
||||
|
@ -56,6 +59,8 @@ namespace Terrain
|
|||
BufferCache mBufferCache;
|
||||
|
||||
unsigned int mCompositeMapSize;
|
||||
float mCompositeMapLevel;
|
||||
float mMaxCompGeometrySize;
|
||||
|
||||
bool mCullingActive;
|
||||
};
|
||||
|
|
|
@ -75,8 +75,9 @@ namespace Terrain
|
|||
class DefaultLodCallback : public LodCallback
|
||||
{
|
||||
public:
|
||||
DefaultLodCallback(float minSize)
|
||||
: mMinSize(minSize)
|
||||
DefaultLodCallback(float factor, float minSize)
|
||||
: mFactor(factor)
|
||||
, mMinSize(minSize)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -84,12 +85,13 @@ public:
|
|||
{
|
||||
float dist = distanceToBox(node->getBoundingBox(), eyePoint);
|
||||
int nativeLodLevel = Log2(static_cast<unsigned int>(node->getSize()/mMinSize));
|
||||
int lodLevel = Log2(static_cast<unsigned int>(dist/(Constants::CellSizeInUnits*mMinSize)));
|
||||
int lodLevel = Log2(static_cast<unsigned int>(dist/(Constants::CellSizeInUnits*mMinSize*mFactor)));
|
||||
|
||||
return nativeLodLevel <= lodLevel;
|
||||
}
|
||||
|
||||
private:
|
||||
float mFactor;
|
||||
float mMinSize;
|
||||
};
|
||||
|
||||
|
@ -123,8 +125,9 @@ private:
|
|||
class QuadTreeBuilder
|
||||
{
|
||||
public:
|
||||
QuadTreeBuilder(Terrain::Storage* storage, ViewDataMap* viewDataMap, float minSize)
|
||||
QuadTreeBuilder(Terrain::Storage* storage, ViewDataMap* viewDataMap, float lodFactor, float minSize)
|
||||
: mStorage(storage)
|
||||
, mLodFactor(lodFactor)
|
||||
, mMinX(0.f), mMaxX(0.f), mMinY(0.f), mMaxY(0.f)
|
||||
, mMinSize(minSize)
|
||||
, mViewDataMap(viewDataMap)
|
||||
|
@ -146,7 +149,7 @@ public:
|
|||
|
||||
mRootNode = new RootNode(size, osg::Vec2f(centerX, centerY));
|
||||
mRootNode->setViewDataMap(mViewDataMap);
|
||||
mRootNode->setLodCallback(new DefaultLodCallback(mMinSize));
|
||||
mRootNode->setLodCallback(new DefaultLodCallback(mLodFactor, mMinSize));
|
||||
addChildren(mRootNode);
|
||||
|
||||
mRootNode->initNeighbours();
|
||||
|
@ -224,6 +227,7 @@ public:
|
|||
private:
|
||||
Terrain::Storage* mStorage;
|
||||
|
||||
float mLodFactor;
|
||||
float mMinX, mMaxX, mMinY, mMaxY;
|
||||
float mMinSize;
|
||||
ViewDataMap* mViewDataMap;
|
||||
|
@ -231,13 +235,19 @@ private:
|
|||
osg::ref_ptr<RootNode> mRootNode;
|
||||
};
|
||||
|
||||
QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask, int borderMask)
|
||||
QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resource::ResourceSystem *resourceSystem, Storage *storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float compMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize)
|
||||
: World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask)
|
||||
, mViewDataMap(new ViewDataMap)
|
||||
, mQuadTreeBuilt(false)
|
||||
, mLodFactor(lodFactor)
|
||||
, mVertexLodMod(vertexLodMod)
|
||||
{
|
||||
// No need for culling on the Drawable / Transform level as the quad tree performs the culling already.
|
||||
mChunkManager->setCullingActive(false);
|
||||
|
||||
mChunkManager->setCompositeMapSize(compMapResolution);
|
||||
mChunkManager->setCompositeMapLevel(compMapLevel);
|
||||
mChunkManager->setMaxCompositeGeometrySize(maxCompGeometrySize);
|
||||
}
|
||||
|
||||
QuadTreeWorld::~QuadTreeWorld()
|
||||
|
@ -287,7 +297,30 @@ void traverseToCell(QuadTreeNode* node, ViewData* vd, int cellX, int cellY)
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int getLodFlags(QuadTreeNode* node, int ourLod, ViewData* vd)
|
||||
/// get the level of vertex detail to render this node at, expressed relative to the native resolution of the data set.
|
||||
unsigned int getVertexLod(QuadTreeNode* node, int vertexLodMod)
|
||||
{
|
||||
int lod = Log2(int(node->getSize()));
|
||||
if (vertexLodMod > 0)
|
||||
{
|
||||
lod = std::max(0, lod-vertexLodMod);
|
||||
}
|
||||
else if (vertexLodMod < 0)
|
||||
{
|
||||
float size = node->getSize();
|
||||
// Stop to simplify at this level since with size = 1 the node already covers the whole cell and has getCellVertices() vertices.
|
||||
while (size < 1)
|
||||
{
|
||||
size *= 2;
|
||||
vertexLodMod = std::min(0, vertexLodMod+1);
|
||||
}
|
||||
lod += std::abs(vertexLodMod);
|
||||
}
|
||||
return lod;
|
||||
}
|
||||
|
||||
/// get the flags to use for stitching in the index buffer so that chunks of different LOD connect seamlessly
|
||||
unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, ViewData* vd)
|
||||
{
|
||||
unsigned int lodFlags = 0;
|
||||
for (unsigned int i=0; i<4; ++i)
|
||||
|
@ -302,7 +335,7 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, ViewData* vd)
|
|||
neighbour = neighbour->getParent();
|
||||
int lod = 0;
|
||||
if (neighbour)
|
||||
lod = Log2(int(neighbour->getSize()));
|
||||
lod = getVertexLod(neighbour, vertexLodMod);
|
||||
|
||||
if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are -
|
||||
lod = 0; // neighbours with more detail will do the stitching themselves
|
||||
|
@ -315,13 +348,17 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, ViewData* vd)
|
|||
return lodFlags;
|
||||
}
|
||||
|
||||
void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunkManager)
|
||||
void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, ChunkManager* chunkManager)
|
||||
{
|
||||
if (!vd->hasChanged() && entry.mRenderingNode)
|
||||
return;
|
||||
|
||||
int ourLod = getVertexLod(entry.mNode, vertexLodMod);
|
||||
|
||||
if (vd->hasChanged())
|
||||
{
|
||||
// have to recompute the lodFlags in case a neighbour has changed LOD.
|
||||
int ourLod = Log2(int(entry.mNode->getSize()));
|
||||
unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vd);
|
||||
unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vertexLodMod, vd);
|
||||
if (lodFlags != entry.mLodFlags)
|
||||
{
|
||||
entry.mRenderingNode = nullptr;
|
||||
|
@ -330,11 +367,8 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, ChunkManager* chunk
|
|||
}
|
||||
|
||||
if (!entry.mRenderingNode)
|
||||
{
|
||||
int ourLod = Log2(int(entry.mNode->getSize()));
|
||||
entry.mRenderingNode = chunkManager->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags);
|
||||
}
|
||||
}
|
||||
|
||||
void QuadTreeWorld::accept(osg::NodeVisitor &nv)
|
||||
{
|
||||
|
@ -378,7 +412,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
|
|||
{
|
||||
ViewData::Entry& entry = vd->getEntry(i);
|
||||
|
||||
loadRenderingNode(entry, vd, mChunkManager.get());
|
||||
loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get());
|
||||
|
||||
if (entry.mVisible)
|
||||
{
|
||||
|
@ -404,7 +438,7 @@ void QuadTreeWorld::ensureQuadTreeBuilt()
|
|||
return;
|
||||
|
||||
const float minSize = 1/8.f;
|
||||
QuadTreeBuilder builder(mStorage, mViewDataMap.get(), minSize);
|
||||
QuadTreeBuilder builder(mStorage, mViewDataMap.get(), mLodFactor, minSize);
|
||||
builder.build();
|
||||
|
||||
mRootNode = builder.getRootNode();
|
||||
|
@ -435,7 +469,7 @@ void QuadTreeWorld::cacheCell(View *view, int x, int y)
|
|||
for (unsigned int i=0; i<vd->getNumEntries(); ++i)
|
||||
{
|
||||
ViewData::Entry& entry = vd->getEntry(i);
|
||||
loadRenderingNode(entry, vd, mChunkManager.get());
|
||||
loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,7 +488,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint)
|
|||
for (unsigned int i=0; i<vd->getNumEntries(); ++i)
|
||||
{
|
||||
ViewData::Entry& entry = vd->getEntry(i);
|
||||
loadRenderingNode(entry, vd, mChunkManager.get());
|
||||
loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,8 @@ namespace Terrain
|
|||
class QuadTreeWorld : public Terrain::World
|
||||
{
|
||||
public:
|
||||
QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask=~0, int borderMask=0);
|
||||
QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask, int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize);
|
||||
|
||||
~QuadTreeWorld();
|
||||
|
||||
void accept(osg::NodeVisitor& nv);
|
||||
|
@ -44,6 +45,8 @@ namespace Terrain
|
|||
|
||||
OpenThreads::Mutex mQuadTreeMutex;
|
||||
bool mQuadTreeBuilt;
|
||||
float mLodFactor;
|
||||
int mVertexLodMod;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -23,3 +23,81 @@ will still be controlled by cell preloading settings.
|
|||
The distant terrain engine is currently considered experimental
|
||||
and may receive updates and/or further configuration options in the future.
|
||||
The glaring omission of non-terrain objects in the distance somewhat limits this setting's usefulness.
|
||||
|
||||
vertex lod mod
|
||||
--------------
|
||||
|
||||
:Type: integer
|
||||
:Range: any
|
||||
:Default: 0
|
||||
|
||||
Controls only the Vertex LOD of the terrain. The amount of terrain chunks and the detail of composite maps is left unchanged.
|
||||
|
||||
Must be changed in increments of 1. Each increment will double (for positive values) or halve (for negative values) the number of vertices rendered.
|
||||
For example: -2 means 4x reduced detail, +3 means 8x increased detail.
|
||||
|
||||
Note this setting will typically not affect near terrain. When set to increase detail, the detail of near terrain can not be increased
|
||||
because the detail is simply not there in the data files, and when set to reduce detail,
|
||||
the detail of near terrain will not be reduced because it was already less detailed than the far terrain (in view relative terms) to begin with.
|
||||
|
||||
lod factor
|
||||
----------
|
||||
|
||||
:Type: float
|
||||
:Range: >0
|
||||
:Default: 1.0
|
||||
|
||||
Controls the level of detail if distant terrain is enabled.
|
||||
Higher values increase detail at the cost of performance, lower values reduce detail but increase performance.
|
||||
|
||||
Note: it also changes how the Quad Tree is split.
|
||||
Increasing detail with this setting results in the visible terrain being divided into more chunks,
|
||||
where as reducing detail with this setting would reduce the number of chunks.
|
||||
|
||||
Fewer terrain chunks is faster for rendering, but on the other hand a larger proportion of the entire terrain
|
||||
must be rebuilt when LOD levels change as the camera moves.
|
||||
This could result in frame drops if moving across the map at high speed.
|
||||
|
||||
For this reason, it is not recommended to change this setting if you want to change the LOD.
|
||||
If you want to do that, first try using the 'vertex lod mod' setting to configure the detail of the terrain outlines
|
||||
to your liking and then use 'composite map resolution' to configure the texture detail to your liking.
|
||||
But these settings can only be changed in multiples of two, so you may want to adjust 'lod factor' afterwards for even more fine-tuning.
|
||||
|
||||
composite map level
|
||||
-------------------
|
||||
|
||||
:Type: integer
|
||||
:Range: >= -3
|
||||
:Default: 0
|
||||
|
||||
Controls at which minimum size (in 2^value cell units) terrain chunks will start to use a composite map instead of the high-detail textures.
|
||||
With value -3 composite maps are used everywhere.
|
||||
|
||||
A composite map is a pre-rendered texture that contains all the texture layers combined.
|
||||
Note that resolution of composite maps is currently always fixed at 'composite map resolution',
|
||||
regardless of the resolution of the underlying terrain textures.
|
||||
If high-detail texture replacers are used, probably it is worth to increase 'composite map resolution' setting value.
|
||||
|
||||
composite map resolution
|
||||
------------------------
|
||||
|
||||
:Type: integer
|
||||
:Range: >0
|
||||
:Default: 512
|
||||
|
||||
Controls the resolution of composite maps. Larger values result in increased detail,
|
||||
but may take longer to prepare and thus could result in longer loading times and an increased chance of frame drops during play.
|
||||
As with most other texture resolution settings, it's most efficient to use values that are powers of two.
|
||||
|
||||
An easy way to observe changes to loading time is to load a save in an interior next to an exterior door
|
||||
(so it will start preloding terrain) and watch how long it takes for the 'Composite' counter on the F4 panel to fall to zero.
|
||||
|
||||
max composite geometry size
|
||||
---------------------------
|
||||
|
||||
:Type: float
|
||||
:Range: >=1.0
|
||||
:Default: 4.0
|
||||
|
||||
Controls the maximum size of simple composite geometry chunk in cell units. With small values there will more draw calls and small textures,
|
||||
but higher values create more overdraw (not every texture layer is used everywhere).
|
||||
|
|
|
@ -90,6 +90,22 @@ pointers cache size = 40
|
|||
# If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells
|
||||
distant terrain = false
|
||||
|
||||
# Controls how the Quad Tree is split. This affects Vertex LOD, Texture LOD and load times. Values > 1 increase detail, values < 1 reduce detail.
|
||||
lod factor = 1.0
|
||||
|
||||
# Controls only the Vertex LOD. Change in increments of 1, each change doubles (or halves) the number of vertices. Values > 0 increase detail, values < 0 reduce detail.
|
||||
vertex lod mod = 0
|
||||
|
||||
# Controls when the distant terrain will flip to composited textures instead of high-detail textures, should be >= -3.
|
||||
# Higher value is more detailed textures.
|
||||
composite map level = 0
|
||||
|
||||
# Controls the resolution of composite maps.
|
||||
composite map resolution = 512
|
||||
|
||||
# Controls the maximum size of composite geometry, should be >= 1.0. With low values there will be many small chunks, with high values - lesser count of bigger chunks.
|
||||
max composite geometry size = 4.0
|
||||
|
||||
[Fog]
|
||||
|
||||
# If true, use extended fog parameters for distant terrain not controlled by
|
||||
|
|
Loading…
Reference in a new issue