Apply clang-format to code base

This commit is contained in:
clang-format-bot 2022-09-22 21:26:05 +03:00 committed by ζeh Matt
parent f37d0be806
commit ddb0522bbf
No known key found for this signature in database
GPG key ID: 18CE582C71A225B0
2199 changed files with 118692 additions and 114392 deletions

View file

@ -9,167 +9,168 @@
namespace
{
template <typename IndexArrayType>
osg::ref_ptr<IndexArrayType> createIndexBuffer(unsigned int flags, unsigned int verts)
{
// LOD level n means every 2^n-th vertex is kept, but we currently handle LOD elsewhere.
size_t lodLevel = 0;//(flags >> (4*4));
size_t lodDeltas[4];
for (int i=0; i<4; ++i)
lodDeltas[i] = (flags >> (4*i)) & (0xf);
bool anyDeltas = (lodDeltas[Terrain::North] || lodDeltas[Terrain::South] || lodDeltas[Terrain::West] || lodDeltas[Terrain::East]);
size_t increment = static_cast<size_t>(1) << lodLevel;
assert(increment < verts);
osg::ref_ptr<IndexArrayType> indices (new IndexArrayType(osg::PrimitiveSet::TRIANGLES));
indices->reserve((verts-1)*(verts-1)*2*3 / increment);
size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1;
// If any edge needs stitching we'll skip all edges at this point,
// mainly because stitching one edge would have an effect on corners and on the adjacent edges
if (anyDeltas)
template <typename IndexArrayType>
osg::ref_ptr<IndexArrayType> createIndexBuffer(unsigned int flags, unsigned int verts)
{
colStart += increment;
colEnd -= increment;
rowEnd -= increment;
rowStart += increment;
// LOD level n means every 2^n-th vertex is kept, but we currently handle LOD elsewhere.
size_t lodLevel = 0; //(flags >> (4*4));
size_t lodDeltas[4];
for (int i = 0; i < 4; ++i)
lodDeltas[i] = (flags >> (4 * i)) & (0xf);
bool anyDeltas = (lodDeltas[Terrain::North] || lodDeltas[Terrain::South] || lodDeltas[Terrain::West]
|| lodDeltas[Terrain::East]);
size_t increment = static_cast<size_t>(1) << lodLevel;
assert(increment < verts);
osg::ref_ptr<IndexArrayType> indices(new IndexArrayType(osg::PrimitiveSet::TRIANGLES));
indices->reserve((verts - 1) * (verts - 1) * 2 * 3 / increment);
size_t rowStart = 0, colStart = 0, rowEnd = verts - 1, colEnd = verts - 1;
// If any edge needs stitching we'll skip all edges at this point,
// mainly because stitching one edge would have an effect on corners and on the adjacent edges
if (anyDeltas)
{
colStart += increment;
colEnd -= increment;
rowEnd -= increment;
rowStart += increment;
}
for (size_t row = rowStart; row < rowEnd; row += increment)
{
for (size_t col = colStart; col < colEnd; col += increment)
{
// diamond pattern
if ((row + col % 2) % 2 == 1)
{
indices->push_back(verts * (col + increment) + row);
indices->push_back(verts * (col + increment) + row + increment);
indices->push_back(verts * col + row + increment);
indices->push_back(verts * col + row);
indices->push_back(verts * (col + increment) + row);
indices->push_back(verts * (col) + row + increment);
}
else
{
indices->push_back(verts * col + row);
indices->push_back(verts * (col + increment) + row + increment);
indices->push_back(verts * col + row + increment);
indices->push_back(verts * col + row);
indices->push_back(verts * (col + increment) + row);
indices->push_back(verts * (col + increment) + row + increment);
}
}
}
size_t innerStep = increment;
if (anyDeltas)
{
// Now configure LOD transitions at the edges - this is pretty tedious,
// and some very long and boring code, but it works great
// South
size_t row = 0;
size_t outerStep = static_cast<size_t>(1) << (lodDeltas[Terrain::South] + lodLevel);
for (size_t col = 0; col < verts - 1; col += outerStep)
{
indices->push_back(verts * col + row);
indices->push_back(verts * (col + outerStep) + row);
// Make sure not to touch the right edge
if (col + outerStep == verts - 1)
indices->push_back(verts * (col + outerStep - innerStep) + row + innerStep);
else
indices->push_back(verts * (col + outerStep) + row + innerStep);
for (size_t i = 0; i < outerStep; i += innerStep)
{
// Make sure not to touch the left or right edges
if (col + i == 0 || col + i == verts - 1 - innerStep)
continue;
indices->push_back(verts * (col) + row);
indices->push_back(verts * (col + i + innerStep) + row + innerStep);
indices->push_back(verts * (col + i) + row + innerStep);
}
}
// North
row = verts - 1;
outerStep = size_t(1) << (lodDeltas[Terrain::North] + lodLevel);
for (size_t col = 0; col < verts - 1; col += outerStep)
{
indices->push_back(verts * (col + outerStep) + row);
indices->push_back(verts * col + row);
// Make sure not to touch the left edge
if (col == 0)
indices->push_back(verts * (col + innerStep) + row - innerStep);
else
indices->push_back(verts * col + row - innerStep);
for (size_t i = 0; i < outerStep; i += innerStep)
{
// Make sure not to touch the left or right edges
if (col + i == 0 || col + i == verts - 1 - innerStep)
continue;
indices->push_back(verts * (col + i) + row - innerStep);
indices->push_back(verts * (col + i + innerStep) + row - innerStep);
indices->push_back(verts * (col + outerStep) + row);
}
}
// West
size_t col = 0;
outerStep = size_t(1) << (lodDeltas[Terrain::West] + lodLevel);
for (row = 0; row < verts - 1; row += outerStep)
{
indices->push_back(verts * col + row + outerStep);
indices->push_back(verts * col + row);
// Make sure not to touch the top edge
if (row + outerStep == verts - 1)
indices->push_back(verts * (col + innerStep) + row + outerStep - innerStep);
else
indices->push_back(verts * (col + innerStep) + row + outerStep);
for (size_t i = 0; i < outerStep; i += innerStep)
{
// Make sure not to touch the top or bottom edges
if (row + i == 0 || row + i == verts - 1 - innerStep)
continue;
indices->push_back(verts * col + row);
indices->push_back(verts * (col + innerStep) + row + i);
indices->push_back(verts * (col + innerStep) + row + i + innerStep);
}
}
// East
col = verts - 1;
outerStep = size_t(1) << (lodDeltas[Terrain::East] + lodLevel);
for (row = 0; row < verts - 1; row += outerStep)
{
indices->push_back(verts * col + row);
indices->push_back(verts * col + row + outerStep);
// Make sure not to touch the bottom edge
if (row == 0)
indices->push_back(verts * (col - innerStep) + row + innerStep);
else
indices->push_back(verts * (col - innerStep) + row);
for (size_t i = 0; i < outerStep; i += innerStep)
{
// Make sure not to touch the top or bottom edges
if (row + i == 0 || row + i == verts - 1 - innerStep)
continue;
indices->push_back(verts * col + row + outerStep);
indices->push_back(verts * (col - innerStep) + row + i + innerStep);
indices->push_back(verts * (col - innerStep) + row + i);
}
}
}
return indices;
}
for (size_t row = rowStart; row < rowEnd; row += increment)
{
for (size_t col = colStart; col < colEnd; col += increment)
{
// diamond pattern
if ((row + col%2) % 2 == 1)
{
indices->push_back(verts*(col+increment)+row);
indices->push_back(verts*(col+increment)+row+increment);
indices->push_back(verts*col+row+increment);
indices->push_back(verts*col+row);
indices->push_back(verts*(col+increment)+row);
indices->push_back(verts*(col)+row+increment);
}
else
{
indices->push_back(verts*col+row);
indices->push_back(verts*(col+increment)+row+increment);
indices->push_back(verts*col+row+increment);
indices->push_back(verts*col+row);
indices->push_back(verts*(col+increment)+row);
indices->push_back(verts*(col+increment)+row+increment);
}
}
}
size_t innerStep = increment;
if (anyDeltas)
{
// Now configure LOD transitions at the edges - this is pretty tedious,
// and some very long and boring code, but it works great
// South
size_t row = 0;
size_t outerStep = static_cast<size_t>(1) << (lodDeltas[Terrain::South] + lodLevel);
for (size_t col = 0; col < verts-1; col += outerStep)
{
indices->push_back(verts*col+row);
indices->push_back(verts*(col+outerStep)+row);
// Make sure not to touch the right edge
if (col+outerStep == verts-1)
indices->push_back(verts*(col+outerStep-innerStep)+row+innerStep);
else
indices->push_back(verts*(col+outerStep)+row+innerStep);
for (size_t i = 0; i < outerStep; i += innerStep)
{
// Make sure not to touch the left or right edges
if (col+i == 0 || col+i == verts-1-innerStep)
continue;
indices->push_back(verts*(col)+row);
indices->push_back(verts*(col+i+innerStep)+row+innerStep);
indices->push_back(verts*(col+i)+row+innerStep);
}
}
// North
row = verts-1;
outerStep = size_t(1) << (lodDeltas[Terrain::North] + lodLevel);
for (size_t col = 0; col < verts-1; col += outerStep)
{
indices->push_back(verts*(col+outerStep)+row);
indices->push_back(verts*col+row);
// Make sure not to touch the left edge
if (col == 0)
indices->push_back(verts*(col+innerStep)+row-innerStep);
else
indices->push_back(verts*col+row-innerStep);
for (size_t i = 0; i < outerStep; i += innerStep)
{
// Make sure not to touch the left or right edges
if (col+i == 0 || col+i == verts-1-innerStep)
continue;
indices->push_back(verts*(col+i)+row-innerStep);
indices->push_back(verts*(col+i+innerStep)+row-innerStep);
indices->push_back(verts*(col+outerStep)+row);
}
}
// West
size_t col = 0;
outerStep = size_t(1) << (lodDeltas[Terrain::West] + lodLevel);
for (row = 0; row < verts-1; row += outerStep)
{
indices->push_back(verts*col+row+outerStep);
indices->push_back(verts*col+row);
// Make sure not to touch the top edge
if (row+outerStep == verts-1)
indices->push_back(verts*(col+innerStep)+row+outerStep-innerStep);
else
indices->push_back(verts*(col+innerStep)+row+outerStep);
for (size_t i = 0; i < outerStep; i += innerStep)
{
// Make sure not to touch the top or bottom edges
if (row+i == 0 || row+i == verts-1-innerStep)
continue;
indices->push_back(verts*col+row);
indices->push_back(verts*(col+innerStep)+row+i);
indices->push_back(verts*(col+innerStep)+row+i+innerStep);
}
}
// East
col = verts-1;
outerStep = size_t(1) << (lodDeltas[Terrain::East] + lodLevel);
for (row = 0; row < verts-1; row += outerStep)
{
indices->push_back(verts*col+row);
indices->push_back(verts*col+row+outerStep);
// Make sure not to touch the bottom edge
if (row == 0)
indices->push_back(verts*(col-innerStep)+row+innerStep);
else
indices->push_back(verts*(col-innerStep)+row);
for (size_t i = 0; i < outerStep; i += innerStep)
{
// Make sure not to touch the top or bottom edges
if (row+i == 0 || row+i == verts-1-innerStep)
continue;
indices->push_back(verts*col+row+outerStep);
indices->push_back(verts*(col-innerStep)+row+i+innerStep);
indices->push_back(verts*(col-innerStep)+row+i);
}
}
}
return indices;
}
}
@ -186,15 +187,15 @@ namespace Terrain
int vertexCount = numVerts * numVerts;
osg::ref_ptr<osg::Vec2Array> uvs (new osg::Vec2Array(osg::Array::BIND_PER_VERTEX));
osg::ref_ptr<osg::Vec2Array> uvs(new osg::Vec2Array(osg::Array::BIND_PER_VERTEX));
uvs->reserve(vertexCount);
for (unsigned int col = 0; col < numVerts; ++col)
{
for (unsigned int row = 0; row < numVerts; ++row)
{
uvs->push_back(osg::Vec2f(col / static_cast<float>(numVerts-1),
((numVerts-1) - row) / static_cast<float>(numVerts-1)));
uvs->push_back(osg::Vec2f(
col / static_cast<float>(numVerts - 1), ((numVerts - 1) - row) / static_cast<float>(numVerts - 1)));
}
}
@ -217,7 +218,7 @@ namespace Terrain
osg::ref_ptr<osg::DrawElements> buffer;
if (numVerts*numVerts <= (0xffffu))
if (numVerts * numVerts <= (0xffffu))
buffer = createIndexBuffer<osg::DrawElementsUShort>(flags, numVerts);
else
buffer = createIndexBuffer<osg::DrawElementsUInt>(flags, numVerts);
@ -241,7 +242,7 @@ namespace Terrain
}
}
void BufferCache::releaseGLObjects(osg::State *state)
void BufferCache::releaseGLObjects(osg::State* state)
{
{
std::lock_guard<std::mutex> lock(mIndexBufferMutex);

View file

@ -1,9 +1,9 @@
#ifndef COMPONENTS_TERRAIN_BUFFERCACHE_H
#define COMPONENTS_TERRAIN_BUFFERCACHE_H
#include <osg/ref_ptr>
#include <osg/Array>
#include <osg/PrimitiveSet>
#include <osg/ref_ptr>
#include <map>
#include <mutex>
@ -18,7 +18,7 @@ namespace Terrain
/// @param flags first 4*4 bits are LOD deltas on each edge, respectively (4 bits each)
/// next 4 bits are LOD level of the index buffer (LOD 0 = don't omit any vertices)
/// @note Thread safe.
osg::ref_ptr<osg::DrawElements> getIndexBuffer (unsigned int numVerts, unsigned int flags);
osg::ref_ptr<osg::DrawElements> getIndexBuffer(unsigned int numVerts, unsigned int flags);
/// @note Thread safe.
osg::ref_ptr<osg::Vec2Array> getUVBuffer(unsigned int numVerts);
@ -30,10 +30,10 @@ namespace Terrain
private:
// Index buffers are shared across terrain batches where possible. There is one index buffer for each
// combination of LOD deltas and index buffer LOD we may need.
std::map<std::pair<int, int>, osg::ref_ptr<osg::DrawElements> > mIndexBufferMap;
std::map<std::pair<int, int>, osg::ref_ptr<osg::DrawElements>> mIndexBufferMap;
std::mutex mIndexBufferMutex;
std::map<int, osg::ref_ptr<osg::Vec2Array> > mUvBufferMap;
std::map<int, osg::ref_ptr<osg::Vec2Array>> mUvBufferMap;
std::mutex mUvBufferMutex;
};

View file

@ -1,8 +1,8 @@
#include "cellborder.hpp"
#include <osg/Geometry>
#include <osg/Material>
#include <osg/PolygonMode>
#include <osg/Geometry>
#include "world.hpp"
@ -13,102 +13,100 @@
namespace Terrain
{
CellBorder::CellBorder(Terrain::World *world, osg::Group *root, int borderMask, Resource::SceneManager* sceneManager)
: mWorld(world)
, mSceneManager(sceneManager)
, mRoot(root)
, mBorderMask(borderMask)
{
}
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)
{
const int cellSize = ESM::Land::REAL_SIZE;
const int borderSegments = 40;
osg::Vec3 cellCorner = osg::Vec3(x * cellSize,y * cellSize,0);
size *= cellSize;
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
normals->push_back(osg::Vec3(0.0f,-1.0f, 0.0f));
float borderStep = size / ((float)borderSegments);
for (int i = 0; i <= 2 * borderSegments; ++i)
CellBorder::CellBorder(
Terrain::World* world, osg::Group* root, int borderMask, Resource::SceneManager* sceneManager)
: mWorld(world)
, mSceneManager(sceneManager)
, mRoot(root)
, mBorderMask(borderMask)
{
osg::Vec3f pos = i < borderSegments ?
osg::Vec3(i * borderStep,0.0f,0.0f) :
osg::Vec3(size, (i - borderSegments) * borderStep,0.0f);
pos += cellCorner;
pos += osg::Vec3f(0,0, terrain->getHeightAt(pos) + offset);
vertices->push_back(pos);
osg::Vec4f col = i % 2 == 0 ?
osg::Vec4f(0,0,0,1) :
color;
colors->push_back(col);
}
osg::ref_ptr<osg::Geometry> border = new osg::Geometry;
border->setVertexArray(vertices.get());
border->setNormalArray(normals.get());
border->setNormalBinding(osg::Geometry::BIND_OVERALL);
border->setColorArray(colors.get());
border->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
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)
{
const int cellSize = ESM::Land::REAL_SIZE;
const int borderSegments = 40;
border->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP,0,vertices->size()));
osg::Vec3 cellCorner = osg::Vec3(x * cellSize, y * cellSize, 0);
size *= cellSize;
osg::ref_ptr<osg::Group> borderGroup = new osg::Group;
borderGroup->addChild(border.get());
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array;
osg::StateSet *stateSet = borderGroup->getOrCreateStateSet();
osg::ref_ptr<osg::Material> material (new osg::Material);
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
stateSet->setAttribute(material);
normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
osg::PolygonMode* polygonmode = new osg::PolygonMode;
polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
stateSet->setAttributeAndModes(polygonmode,osg::StateAttribute::ON);
float borderStep = size / ((float)borderSegments);
sceneManager->recreateShaders(borderGroup, "debug");
borderGroup->setNodeMask(mask);
for (int i = 0; i <= 2 * borderSegments; ++i)
{
osg::Vec3f pos = i < borderSegments ? osg::Vec3(i * borderStep, 0.0f, 0.0f)
: osg::Vec3(size, (i - borderSegments) * borderStep, 0.0f);
return borderGroup;
}
pos += cellCorner;
pos += osg::Vec3f(0, 0, terrain->getHeightAt(pos) + offset);
void CellBorder::createCellBorderGeometry(int x, int y)
{
auto borderGroup = createBorderGeometry(x, y, 1.f, mWorld->getStorage(), mSceneManager, mBorderMask);
mRoot->addChild(borderGroup);
vertices->push_back(pos);
mCellBorderNodes[std::make_pair(x,y)] = borderGroup;
}
osg::Vec4f col = i % 2 == 0 ? osg::Vec4f(0, 0, 0, 1) : color;
void CellBorder::destroyCellBorderGeometry(int x, int y)
{
CellGrid::iterator it = mCellBorderNodes.find(std::make_pair(x,y));
colors->push_back(col);
}
if (it == mCellBorderNodes.end())
return;
osg::ref_ptr<osg::Geometry> border = new osg::Geometry;
border->setVertexArray(vertices.get());
border->setNormalArray(normals.get());
border->setNormalBinding(osg::Geometry::BIND_OVERALL);
border->setColorArray(colors.get());
border->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
osg::ref_ptr<osg::Node> borderNode = it->second;
mRoot->removeChild(borderNode);
border->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, vertices->size()));
mCellBorderNodes.erase(it);
}
osg::ref_ptr<osg::Group> borderGroup = new osg::Group;
borderGroup->addChild(border.get());
void CellBorder::destroyCellBorderGeometry()
{
for (const auto& v : mCellBorderNodes)
mRoot->removeChild(v.second);
mCellBorderNodes.clear();
}
osg::StateSet* stateSet = borderGroup->getOrCreateStateSet();
osg::ref_ptr<osg::Material> material(new osg::Material);
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
stateSet->setAttribute(material);
osg::PolygonMode* polygonmode = new osg::PolygonMode;
polygonmode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
stateSet->setAttributeAndModes(polygonmode, osg::StateAttribute::ON);
sceneManager->recreateShaders(borderGroup, "debug");
borderGroup->setNodeMask(mask);
return borderGroup;
}
void CellBorder::createCellBorderGeometry(int x, int y)
{
auto borderGroup = createBorderGeometry(x, y, 1.f, mWorld->getStorage(), mSceneManager, mBorderMask);
mRoot->addChild(borderGroup);
mCellBorderNodes[std::make_pair(x, y)] = borderGroup;
}
void CellBorder::destroyCellBorderGeometry(int x, int y)
{
CellGrid::iterator it = mCellBorderNodes.find(std::make_pair(x, y));
if (it == mCellBorderNodes.end())
return;
osg::ref_ptr<osg::Node> borderNode = it->second;
mRoot->removeChild(borderNode);
mCellBorderNodes.erase(it);
}
void CellBorder::destroyCellBorderGeometry()
{
for (const auto& v : mCellBorderNodes)
mRoot->removeChild(v.second);
mCellBorderNodes.clear();
}
}

View file

@ -20,9 +20,9 @@ namespace Terrain
class CellBorder
{
public:
typedef std::map<std::pair<int, int>, osg::ref_ptr<osg::Node> > CellGrid;
typedef std::map<std::pair<int, int>, osg::ref_ptr<osg::Node>> CellGrid;
CellBorder(Terrain::World *world, osg::Group *root, int borderMask, Resource::SceneManager* sceneManager);
CellBorder(Terrain::World* world, osg::Group* root, int borderMask, Resource::SceneManager* sceneManager);
void createCellBorderGeometry(int x, int y);
void destroyCellBorderGeometry(int x, int y);
@ -32,12 +32,13 @@ namespace Terrain
*/
void destroyCellBorderGeometry();
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 });
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 });
protected:
Terrain::World *mWorld;
Terrain::World* mWorld;
Resource::SceneManager* mSceneManager;
osg::Group *mRoot;
osg::Group* mRoot;
CellGrid mCellBorderNodes;
int mBorderMask;

View file

@ -1,7 +1,7 @@
#include "chunkmanager.hpp"
#include <osg/Texture2D>
#include <osg/Material>
#include <osg/Texture2D>
#include <osgUtil/IncrementalCompileOperation>
@ -10,279 +10,304 @@
#include <components/sceneutil/lightmanager.hpp>
#include "terraindrawable.hpp"
#include "compositemaprenderer.hpp"
#include "material.hpp"
#include "storage.hpp"
#include "terraindrawable.hpp"
#include "texturemanager.hpp"
#include "compositemaprenderer.hpp"
namespace Terrain
{
ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer)
: GenericResourceManager<ChunkId>(nullptr)
, mStorage(storage)
, mSceneManager(sceneMgr)
, mTextureManager(textureManager)
, mCompositeMapRenderer(renderer)
, mNodeMask(0)
, mCompositeMapSize(512)
, mCompositeMapLevel(1.f)
, mMaxCompGeometrySize(1.f)
{
mMultiPassRoot = new osg::StateSet;
mMultiPassRoot->setRenderingHint(osg::StateSet::OPAQUE_BIN);
osg::ref_ptr<osg::Material> material (new osg::Material);
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON);
}
struct FindChunkTemplate
{
void operator() (ChunkId id, osg::Object* obj)
ChunkManager::ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
CompositeMapRenderer* renderer)
: GenericResourceManager<ChunkId>(nullptr)
, mStorage(storage)
, mSceneManager(sceneMgr)
, mTextureManager(textureManager)
, mCompositeMapRenderer(renderer)
, mNodeMask(0)
, mCompositeMapSize(512)
, mCompositeMapLevel(1.f)
, mMaxCompGeometrySize(1.f)
{
if (std::get<0>(id) == std::get<0>(mId) && std::get<1>(id) == std::get<1>(mId))
mFoundTemplate = obj;
mMultiPassRoot = new osg::StateSet;
mMultiPassRoot->setRenderingHint(osg::StateSet::OPAQUE_BIN);
osg::ref_ptr<osg::Material> material(new osg::Material);
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
mMultiPassRoot->setAttributeAndModes(material, osg::StateAttribute::ON);
}
ChunkId mId;
osg::ref_ptr<osg::Object> mFoundTemplate;
};
osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
{
// Override lod with the vertexLodMod adjusted value.
// TODO: maybe we can refactor this code by moving all vertexLodMod code into this class.
lod = static_cast<unsigned char>(lodFlags >> (4*4));
ChunkId id = std::make_tuple(center, lod, lodFlags);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);
if (obj)
return static_cast<osg::Node*>(obj.get());
else
struct FindChunkTemplate
{
FindChunkTemplate find;
find.mId = id;
mCache->call(find);
TerrainDrawable* templateGeometry = find.mFoundTemplate ? static_cast<TerrainDrawable*>(find.mFoundTemplate.get()) : nullptr;
osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile, templateGeometry);
mCache->addEntryToObjectCache(id, node.get());
return node;
}
}
void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const
{
stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize());
}
void ChunkManager::clearCache()
{
GenericResourceManager<ChunkId>::clearCache();
mBufferCache.clearCache();
}
void ChunkManager::releaseGLObjects(osg::State *state)
{
GenericResourceManager<ChunkId>::releaseGLObjects(state);
mBufferCache.releaseGLObjects(state);
}
osg::ref_ptr<osg::Texture2D> ChunkManager::createCompositeMapRTT()
{
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
texture->setTextureWidth(mCompositeMapSize);
texture->setTextureHeight(mCompositeMapSize);
texture->setInternalFormat(GL_RGB);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
return texture;
}
void ChunkManager::createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& compositeMap)
{
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);
createCompositeMapGeometry(chunkSize/2.f, chunkCenter + osg::Vec2f(chunkSize/4.f, -chunkSize/4.f), osg::Vec4f(texCoords.x() + texCoords.z()/2.f, texCoords.y()+texCoords.w()/2.f, 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.w()/2.f, texCoords.z()/2.f, texCoords.w()/2.f), compositeMap);
}
else
{
float left = texCoords.x()*2.f-1;
float top = texCoords.y()*2.f-1;
float width = texCoords.z()*2.f;
float height = texCoords.w()*2.f;
std::vector<osg::ref_ptr<osg::StateSet> > passes = createPasses(chunkSize, chunkCenter, true);
for (std::vector<osg::ref_ptr<osg::StateSet> >::iterator it = passes.begin(); it != passes.end(); ++it)
void operator()(ChunkId id, osg::Object* obj)
{
osg::ref_ptr<osg::Geometry> geom = osg::createTexturedQuadGeometry(osg::Vec3(left,top,0), osg::Vec3(width,0,0), osg::Vec3(0,height,0));
geom->setUseDisplayList(false); // don't bother making a display list for an object that is just rendered once.
geom->setUseVertexBufferObjects(false);
geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);
geom->setStateSet(*it);
compositeMap.mDrawables.emplace_back(geom);
if (std::get<0>(id) == std::get<0>(mId) && std::get<1>(id) == std::get<1>(mId))
mFoundTemplate = obj;
}
}
}
ChunkId mId;
osg::ref_ptr<osg::Object> mFoundTemplate;
};
std::vector<osg::ref_ptr<osg::StateSet> > ChunkManager::createPasses(float chunkSize, const osg::Vec2f &chunkCenter, bool forCompositeMap)
{
std::vector<LayerInfo> layerList;
std::vector<osg::ref_ptr<osg::Image> > blendmaps;
mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList);
bool useShaders = mSceneManager->getForceShaders();
if (!mSceneManager->getClampLighting())
useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between a terrain chunk with normal maps and one without normal maps
std::vector<TextureLayer> layers;
osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f& center, unsigned char lod,
unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
{
for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)
// Override lod with the vertexLodMod adjusted value.
// TODO: maybe we can refactor this code by moving all vertexLodMod code into this class.
lod = static_cast<unsigned char>(lodFlags >> (4 * 4));
ChunkId id = std::make_tuple(center, lod, lodFlags);
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);
if (obj)
return static_cast<osg::Node*>(obj.get());
else
{
TextureLayer textureLayer;
textureLayer.mParallax = it->mParallax;
textureLayer.mSpecular = it->mSpecular;
textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap);
if (!forCompositeMap && !it->mNormalMap.empty())
textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap);
if (it->requiresShaders())
useShaders = true;
layers.push_back(textureLayer);
FindChunkTemplate find;
find.mId = id;
mCache->call(find);
TerrainDrawable* templateGeometry
= find.mFoundTemplate ? static_cast<TerrainDrawable*>(find.mFoundTemplate.get()) : nullptr;
osg::ref_ptr<osg::Node> node = createChunk(size, center, lod, lodFlags, compile, templateGeometry);
mCache->addEntryToObjectCache(id, node.get());
return node;
}
}
if (forCompositeMap)
useShaders = false;
std::vector<osg::ref_ptr<osg::Texture2D> > blendmapTextures;
for (std::vector<osg::ref_ptr<osg::Image> >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)
void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
texture->setImage(*it);
stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize());
}
void ChunkManager::clearCache()
{
GenericResourceManager<ChunkId>::clearCache();
mBufferCache.clearCache();
}
void ChunkManager::releaseGLObjects(osg::State* state)
{
GenericResourceManager<ChunkId>::releaseGLObjects(state);
mBufferCache.releaseGLObjects(state);
}
osg::ref_ptr<osg::Texture2D> ChunkManager::createCompositeMapRTT()
{
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
texture->setTextureWidth(mCompositeMapSize);
texture->setTextureHeight(mCompositeMapSize);
texture->setInternalFormat(GL_RGB);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
texture->setResizeNonPowerOfTwoHint(false);
blendmapTextures.push_back(texture);
return texture;
}
float blendmapScale = mStorage->getBlendmapScale(chunkSize);
return ::Terrain::createPasses(useShaders, mSceneManager, layers, blendmapTextures, blendmapScale, blendmapScale);
}
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter, unsigned char lod, unsigned int lodFlags, bool compile, TerrainDrawable* templateGeometry)
{
osg::ref_ptr<TerrainDrawable> geometry (new TerrainDrawable);
if (!templateGeometry)
void ChunkManager::createCompositeMapGeometry(
float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& compositeMap)
{
osg::ref_ptr<osg::Vec3Array> positions (new osg::Vec3Array);
osg::ref_ptr<osg::Vec3Array> normals (new osg::Vec3Array);
osg::ref_ptr<osg::Vec4ubArray> colors (new osg::Vec4ubArray);
colors->setNormalize(true);
mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors);
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
positions->setVertexBufferObject(vbo);
normals->setVertexBufferObject(vbo);
colors->setVertexBufferObject(vbo);
geometry->setVertexArray(positions);
geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
}
else
{
// Unfortunately we need to copy vertex data because of poor coupling with VertexBufferObject.
osg::ref_ptr<osg::Array> positions = static_cast<osg::Array*>(templateGeometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL));
osg::ref_ptr<osg::Array> normals = static_cast<osg::Array*>(templateGeometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL));
osg::ref_ptr<osg::Array> colors = static_cast<osg::Array*>(templateGeometry->getColorArray()->clone(osg::CopyOp::DEEP_COPY_ALL));
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
positions->setVertexBufferObject(vbo);
normals->setVertexBufferObject(vbo);
colors->setVertexBufferObject(vbo);
geometry->setVertexArray(positions);
geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
}
geometry->setUseDisplayList(false);
geometry->setUseVertexBufferObjects(true);
if (chunkSize <= 1.f)
geometry->setLightListCallback(new SceneUtil::LightListCallback);
unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1;
geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags));
bool useCompositeMap = chunkSize >= mCompositeMapLevel;
unsigned int numUvSets = useCompositeMap ? 1 : 2;
geometry->setTexCoordArrayList(osg::Geometry::ArrayList(numUvSets, mBufferCache.getUVBuffer(numVerts)));
geometry->createClusterCullingCallback();
geometry->setStateSet(mMultiPassRoot);
if (templateGeometry)
{
if (templateGeometry->getCompositeMap())
if (chunkSize > mMaxCompGeometrySize)
{
geometry->setCompositeMap(templateGeometry->getCompositeMap());
geometry->setCompositeMapRenderer(mCompositeMapRenderer);
}
geometry->setPasses(templateGeometry->getPasses());
}
else
{
if (useCompositeMap)
{
osg::ref_ptr<CompositeMap> compositeMap = new CompositeMap;
compositeMap->mTexture = createCompositeMapRTT();
createCompositeMapGeometry(chunkSize, chunkCenter, osg::Vec4f(0,0,1,1), *compositeMap);
mCompositeMapRenderer->addCompositeMap(compositeMap.get(), false);
geometry->setCompositeMap(compositeMap);
geometry->setCompositeMapRenderer(mCompositeMapRenderer);
TextureLayer layer;
layer.mDiffuseMap = compositeMap->mTexture;
layer.mParallax = false;
layer.mSpecular = false;
geometry->setPasses(::Terrain::createPasses(mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), mSceneManager, std::vector<TextureLayer>(1, layer), std::vector<osg::ref_ptr<osg::Texture2D> >(), 1.f, 1.f));
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);
createCompositeMapGeometry(chunkSize / 2.f, chunkCenter + osg::Vec2f(chunkSize / 4.f, -chunkSize / 4.f),
osg::Vec4f(texCoords.x() + texCoords.z() / 2.f, texCoords.y() + texCoords.w() / 2.f,
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.w() / 2.f, texCoords.z() / 2.f, texCoords.w() / 2.f),
compositeMap);
}
else
{
geometry->setPasses(createPasses(chunkSize, chunkCenter, false));
float left = texCoords.x() * 2.f - 1;
float top = texCoords.y() * 2.f - 1;
float width = texCoords.z() * 2.f;
float height = texCoords.w() * 2.f;
std::vector<osg::ref_ptr<osg::StateSet>> passes = createPasses(chunkSize, chunkCenter, true);
for (std::vector<osg::ref_ptr<osg::StateSet>>::iterator it = passes.begin(); it != passes.end(); ++it)
{
osg::ref_ptr<osg::Geometry> geom = osg::createTexturedQuadGeometry(
osg::Vec3(left, top, 0), osg::Vec3(width, 0, 0), osg::Vec3(0, height, 0));
geom->setUseDisplayList(
false); // don't bother making a display list for an object that is just rendered once.
geom->setUseVertexBufferObjects(false);
geom->setTexCoordArray(1, geom->getTexCoordArray(0), osg::Array::BIND_PER_VERTEX);
geom->setStateSet(*it);
compositeMap.mDrawables.emplace_back(geom);
}
}
}
geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts);
if (!templateGeometry && compile && mSceneManager->getIncrementalCompileOperation())
std::vector<osg::ref_ptr<osg::StateSet>> ChunkManager::createPasses(
float chunkSize, const osg::Vec2f& chunkCenter, bool forCompositeMap)
{
mSceneManager->getIncrementalCompileOperation()->add(geometry);
std::vector<LayerInfo> layerList;
std::vector<osg::ref_ptr<osg::Image>> blendmaps;
mStorage->getBlendmaps(chunkSize, chunkCenter, blendmaps, layerList);
bool useShaders = mSceneManager->getForceShaders();
if (!mSceneManager->getClampLighting())
useShaders = true; // always use shaders when lighting is unclamped, this is to avoid lighting seams between
// a terrain chunk with normal maps and one without normal maps
std::vector<TextureLayer> layers;
{
for (std::vector<LayerInfo>::const_iterator it = layerList.begin(); it != layerList.end(); ++it)
{
TextureLayer textureLayer;
textureLayer.mParallax = it->mParallax;
textureLayer.mSpecular = it->mSpecular;
textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap);
if (!forCompositeMap && !it->mNormalMap.empty())
textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap);
if (it->requiresShaders())
useShaders = true;
layers.push_back(textureLayer);
}
}
if (forCompositeMap)
useShaders = false;
std::vector<osg::ref_ptr<osg::Texture2D>> blendmapTextures;
for (std::vector<osg::ref_ptr<osg::Image>>::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)
{
osg::ref_ptr<osg::Texture2D> texture(new osg::Texture2D);
texture->setImage(*it);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
texture->setResizeNonPowerOfTwoHint(false);
blendmapTextures.push_back(texture);
}
float blendmapScale = mStorage->getBlendmapScale(chunkSize);
return ::Terrain::createPasses(
useShaders, mSceneManager, layers, blendmapTextures, blendmapScale, blendmapScale);
}
geometry->setNodeMask(mNodeMask);
return geometry;
}
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f& chunkCenter, unsigned char lod,
unsigned int lodFlags, bool compile, TerrainDrawable* templateGeometry)
{
osg::ref_ptr<TerrainDrawable> geometry(new TerrainDrawable);
if (!templateGeometry)
{
osg::ref_ptr<osg::Vec3Array> positions(new osg::Vec3Array);
osg::ref_ptr<osg::Vec3Array> normals(new osg::Vec3Array);
osg::ref_ptr<osg::Vec4ubArray> colors(new osg::Vec4ubArray);
colors->setNormalize(true);
mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors);
osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject);
positions->setVertexBufferObject(vbo);
normals->setVertexBufferObject(vbo);
colors->setVertexBufferObject(vbo);
geometry->setVertexArray(positions);
geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
}
else
{
// Unfortunately we need to copy vertex data because of poor coupling with VertexBufferObject.
osg::ref_ptr<osg::Array> positions
= static_cast<osg::Array*>(templateGeometry->getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL));
osg::ref_ptr<osg::Array> normals
= static_cast<osg::Array*>(templateGeometry->getNormalArray()->clone(osg::CopyOp::DEEP_COPY_ALL));
osg::ref_ptr<osg::Array> colors
= static_cast<osg::Array*>(templateGeometry->getColorArray()->clone(osg::CopyOp::DEEP_COPY_ALL));
osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject);
positions->setVertexBufferObject(vbo);
normals->setVertexBufferObject(vbo);
colors->setVertexBufferObject(vbo);
geometry->setVertexArray(positions);
geometry->setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
}
geometry->setUseDisplayList(false);
geometry->setUseVertexBufferObjects(true);
if (chunkSize <= 1.f)
geometry->setLightListCallback(new SceneUtil::LightListCallback);
unsigned int numVerts = (mStorage->getCellVertices() - 1) * chunkSize / (1 << lod) + 1;
geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, lodFlags));
bool useCompositeMap = chunkSize >= mCompositeMapLevel;
unsigned int numUvSets = useCompositeMap ? 1 : 2;
geometry->setTexCoordArrayList(osg::Geometry::ArrayList(numUvSets, mBufferCache.getUVBuffer(numVerts)));
geometry->createClusterCullingCallback();
geometry->setStateSet(mMultiPassRoot);
if (templateGeometry)
{
if (templateGeometry->getCompositeMap())
{
geometry->setCompositeMap(templateGeometry->getCompositeMap());
geometry->setCompositeMapRenderer(mCompositeMapRenderer);
}
geometry->setPasses(templateGeometry->getPasses());
}
else
{
if (useCompositeMap)
{
osg::ref_ptr<CompositeMap> compositeMap = new CompositeMap;
compositeMap->mTexture = createCompositeMapRTT();
createCompositeMapGeometry(chunkSize, chunkCenter, osg::Vec4f(0, 0, 1, 1), *compositeMap);
mCompositeMapRenderer->addCompositeMap(compositeMap.get(), false);
geometry->setCompositeMap(compositeMap);
geometry->setCompositeMapRenderer(mCompositeMapRenderer);
TextureLayer layer;
layer.mDiffuseMap = compositeMap->mTexture;
layer.mParallax = false;
layer.mSpecular = false;
geometry->setPasses(::Terrain::createPasses(
mSceneManager->getForceShaders() || !mSceneManager->getClampLighting(), mSceneManager,
std::vector<TextureLayer>(1, layer), std::vector<osg::ref_ptr<osg::Texture2D>>(), 1.f, 1.f));
}
else
{
geometry->setPasses(createPasses(chunkSize, chunkCenter, false));
}
}
geometry->setupWaterBoundingBox(-1, chunkSize * mStorage->getCellWorldSize() / numVerts);
if (!templateGeometry && compile && mSceneManager->getIncrementalCompileOperation())
{
mSceneManager->getIncrementalCompileOperation()->add(geometry);
}
geometry->setNodeMask(mNodeMask);
return geometry;
}
}

View file

@ -34,9 +34,11 @@ namespace Terrain
class ChunkManager : public Resource::GenericResourceManager<ChunkId>, public QuadTreeWorld::ChunkManager
{
public:
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager, CompositeMapRenderer* renderer);
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager,
CompositeMapRenderer* renderer);
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;
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;
void setCompositeMapSize(unsigned int size) { mCompositeMapSize = size; }
void setCompositeMapLevel(float level) { mCompositeMapLevel = level; }
@ -52,13 +54,16 @@ namespace Terrain
void releaseGLObjects(osg::State* state) override;
private:
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool compile, TerrainDrawable* templateGeometry);
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, unsigned char lod,
unsigned int lodFlags, bool compile, TerrainDrawable* templateGeometry);
osg::ref_ptr<osg::Texture2D> createCompositeMapRTT();
void createCompositeMapGeometry(float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& map);
void createCompositeMapGeometry(
float chunkSize, const osg::Vec2f& chunkCenter, const osg::Vec4f& texCoords, CompositeMap& map);
std::vector<osg::ref_ptr<osg::StateSet> > createPasses(float chunkSize, const osg::Vec2f& chunkCenter, bool forCompositeMap);
std::vector<osg::ref_ptr<osg::StateSet>> createPasses(
float chunkSize, const osg::Vec2f& chunkCenter, bool forCompositeMap);
Terrain::Storage* mStorage;
Resource::SceneManager* mSceneManager;

View file

@ -1,199 +1,191 @@
#include "compositemaprenderer.hpp"
#include <osg/FrameBufferObject>
#include <osg/Texture2D>
#include <osg/RenderInfo>
#include <osg/Texture2D>
#include <algorithm>
namespace Terrain
{
CompositeMapRenderer::CompositeMapRenderer()
: mTargetFrameRate(120)
, mMinimumTimeAvailable(0.0025)
{
setSupportsDisplayList(false);
setCullingActive(false);
mFBO = new osg::FrameBufferObject;
getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
}
CompositeMapRenderer::~CompositeMapRenderer()
{
}
void CompositeMapRenderer::drawImplementation(osg::RenderInfo &renderInfo) const
{
double dt = mTimer.time_s();
dt = std::min(dt, 0.2);
mTimer.setStartTick();
double targetFrameTime = 1.0/static_cast<double>(mTargetFrameRate);
double conservativeTimeRatio(0.75);
double availableTime = std::max((targetFrameTime - dt)*conservativeTimeRatio,
mMinimumTimeAvailable);
std::lock_guard<std::mutex> lock(mMutex);
if (mImmediateCompileSet.empty() && mCompileSet.empty())
return;
while (!mImmediateCompileSet.empty())
CompositeMapRenderer::CompositeMapRenderer()
: mTargetFrameRate(120)
, mMinimumTimeAvailable(0.0025)
{
osg::ref_ptr<CompositeMap> node = *mImmediateCompileSet.begin();
mImmediateCompileSet.erase(node);
setSupportsDisplayList(false);
setCullingActive(false);
mMutex.unlock();
compile(*node, renderInfo, nullptr);
mMutex.lock();
mFBO = new osg::FrameBufferObject;
getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
}
double timeLeft = availableTime;
CompositeMapRenderer::~CompositeMapRenderer() {}
while (!mCompileSet.empty() && timeLeft > 0)
void CompositeMapRenderer::drawImplementation(osg::RenderInfo& renderInfo) const
{
osg::ref_ptr<CompositeMap> node = *mCompileSet.begin();
mCompileSet.erase(node);
double dt = mTimer.time_s();
dt = std::min(dt, 0.2);
mTimer.setStartTick();
double targetFrameTime = 1.0 / static_cast<double>(mTargetFrameRate);
double conservativeTimeRatio(0.75);
double availableTime = std::max((targetFrameTime - dt) * conservativeTimeRatio, mMinimumTimeAvailable);
mMutex.unlock();
compile(*node, renderInfo, &timeLeft);
mMutex.lock();
std::lock_guard<std::mutex> lock(mMutex);
if (node->mCompiled < node->mDrawables.size())
if (mImmediateCompileSet.empty() && mCompileSet.empty())
return;
while (!mImmediateCompileSet.empty())
{
// We did not compile the map fully.
// Place it back to queue to continue work in the next time.
mCompileSet.insert(node);
osg::ref_ptr<CompositeMap> node = *mImmediateCompileSet.begin();
mImmediateCompileSet.erase(node);
mMutex.unlock();
compile(*node, renderInfo, nullptr);
mMutex.lock();
}
}
mTimer.setStartTick();
}
void CompositeMapRenderer::compile(CompositeMap &compositeMap, osg::RenderInfo &renderInfo, double* timeLeft) const
{
// if there are no more external references we can assume the texture is no longer required
if (compositeMap.mTexture->referenceCount() <= 1)
{
compositeMap.mCompiled = compositeMap.mDrawables.size();
return;
double timeLeft = availableTime;
while (!mCompileSet.empty() && timeLeft > 0)
{
osg::ref_ptr<CompositeMap> node = *mCompileSet.begin();
mCompileSet.erase(node);
mMutex.unlock();
compile(*node, renderInfo, &timeLeft);
mMutex.lock();
if (node->mCompiled < node->mDrawables.size())
{
// We did not compile the map fully.
// Place it back to queue to continue work in the next time.
mCompileSet.insert(node);
}
}
mTimer.setStartTick();
}
osg::Timer timer;
osg::State& state = *renderInfo.getState();
osg::GLExtensions* ext = state.get<osg::GLExtensions>();
if (!mFBO)
return;
if (!ext->isFrameBufferObjectSupported)
return;
osg::FrameBufferAttachment attach (compositeMap.mTexture);
mFBO->setAttachment(osg::Camera::COLOR_BUFFER, attach);
mFBO->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
GLenum status = ext->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
void CompositeMapRenderer::compile(CompositeMap& compositeMap, osg::RenderInfo& renderInfo, double* timeLeft) const
{
// if there are no more external references we can assume the texture is no longer required
if (compositeMap.mTexture->referenceCount() <= 1)
{
compositeMap.mCompiled = compositeMap.mDrawables.size();
return;
}
osg::Timer timer;
osg::State& state = *renderInfo.getState();
osg::GLExtensions* ext = state.get<osg::GLExtensions>();
if (!mFBO)
return;
if (!ext->isFrameBufferObjectSupported)
return;
osg::FrameBufferAttachment attach(compositeMap.mTexture);
mFBO->setAttachment(osg::Camera::COLOR_BUFFER, attach);
mFBO->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
GLenum status = ext->glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0;
ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
OSG_ALWAYS << "Error attaching FBO" << std::endl;
return;
}
// inform State that Texture attribute has changed due to compiling of FBO texture
// should OSG be doing this on its own?
state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), osg::StateAttribute::TEXTURE);
for (unsigned int i = compositeMap.mCompiled; i < compositeMap.mDrawables.size(); ++i)
{
osg::Drawable* drw = compositeMap.mDrawables[i];
osg::StateSet* stateset = drw->getStateSet();
if (stateset)
renderInfo.getState()->pushStateSet(stateset);
renderInfo.getState()->apply();
glViewport(0, 0, compositeMap.mTexture->getTextureWidth(), compositeMap.mTexture->getTextureHeight());
drw->drawImplementation(renderInfo);
if (stateset)
renderInfo.getState()->popStateSet();
++compositeMap.mCompiled;
compositeMap.mDrawables[i] = nullptr;
if (timeLeft)
{
*timeLeft -= timer.time_s();
timer.setStartTick();
if (*timeLeft <= 0)
break;
}
}
if (compositeMap.mCompiled == compositeMap.mDrawables.size())
compositeMap.mDrawables = std::vector<osg::ref_ptr<osg::Drawable>>();
state.haveAppliedAttribute(osg::StateAttribute::VIEWPORT);
GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0;
ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
OSG_ALWAYS << "Error attaching FBO" << std::endl;
return;
}
// inform State that Texture attribute has changed due to compiling of FBO texture
// should OSG be doing this on its own?
state.haveAppliedTextureAttribute(state.getActiveTextureUnit(), osg::StateAttribute::TEXTURE);
for (unsigned int i=compositeMap.mCompiled; i<compositeMap.mDrawables.size(); ++i)
void CompositeMapRenderer::setMinimumTimeAvailableForCompile(double time)
{
osg::Drawable* drw = compositeMap.mDrawables[i];
osg::StateSet* stateset = drw->getStateSet();
mMinimumTimeAvailable = time;
}
if (stateset)
renderInfo.getState()->pushStateSet(stateset);
void CompositeMapRenderer::setTargetFrameRate(float framerate)
{
mTargetFrameRate = framerate;
}
renderInfo.getState()->apply();
void CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool immediate)
{
std::lock_guard<std::mutex> lock(mMutex);
if (immediate)
mImmediateCompileSet.insert(compositeMap);
else
mCompileSet.insert(compositeMap);
}
glViewport(0,0,compositeMap.mTexture->getTextureWidth(), compositeMap.mTexture->getTextureHeight());
drw->drawImplementation(renderInfo);
if (stateset)
renderInfo.getState()->popStateSet();
++compositeMap.mCompiled;
compositeMap.mDrawables[i] = nullptr;
if (timeLeft)
void CompositeMapRenderer::setImmediate(CompositeMap* compositeMap)
{
std::lock_guard<std::mutex> lock(mMutex);
CompileSet::iterator found = mCompileSet.find(compositeMap);
if (found == mCompileSet.end())
return;
else
{
*timeLeft -= timer.time_s();
timer.setStartTick();
if (*timeLeft <= 0)
break;
mImmediateCompileSet.insert(compositeMap);
mCompileSet.erase(found);
}
}
if (compositeMap.mCompiled == compositeMap.mDrawables.size())
compositeMap.mDrawables = std::vector<osg::ref_ptr<osg::Drawable>>();
state.haveAppliedAttribute(osg::StateAttribute::VIEWPORT);
GLuint fboId = state.getGraphicsContext() ? state.getGraphicsContext()->getDefaultFboId() : 0;
ext->glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId);
}
void CompositeMapRenderer::setMinimumTimeAvailableForCompile(double time)
{
mMinimumTimeAvailable = time;
}
void CompositeMapRenderer::setTargetFrameRate(float framerate)
{
mTargetFrameRate = framerate;
}
void CompositeMapRenderer::addCompositeMap(CompositeMap* compositeMap, bool immediate)
{
std::lock_guard<std::mutex> lock(mMutex);
if (immediate)
mImmediateCompileSet.insert(compositeMap);
else
mCompileSet.insert(compositeMap);
}
void CompositeMapRenderer::setImmediate(CompositeMap* compositeMap)
{
std::lock_guard<std::mutex> lock(mMutex);
CompileSet::iterator found = mCompileSet.find(compositeMap);
if (found == mCompileSet.end())
return;
else
unsigned int CompositeMapRenderer::getCompileSetSize() const
{
mImmediateCompileSet.insert(compositeMap);
mCompileSet.erase(found);
std::lock_guard<std::mutex> lock(mMutex);
return mCompileSet.size();
}
}
unsigned int CompositeMapRenderer::getCompileSetSize() const
{
std::lock_guard<std::mutex> lock(mMutex);
return mCompileSet.size();
}
CompositeMap::CompositeMap()
: mCompiled(0)
{
}
CompositeMap::~CompositeMap()
{
}
CompositeMap::CompositeMap()
: mCompiled(0)
{
}
CompositeMap::~CompositeMap() {}
}

View file

@ -3,8 +3,8 @@
#include <osg/Drawable>
#include <set>
#include <mutex>
#include <set>
namespace osg
{
@ -21,13 +21,14 @@ namespace Terrain
public:
CompositeMap();
~CompositeMap();
std::vector<osg::ref_ptr<osg::Drawable> > mDrawables;
std::vector<osg::ref_ptr<osg::Drawable>> mDrawables;
osg::ref_ptr<osg::Texture2D> mTexture;
unsigned int mCompiled;
};
/**
* @brief The CompositeMapRenderer is responsible for updating composite map textures in a blocking or non-blocking way.
* @brief The CompositeMapRenderer is responsible for updating composite map textures in a blocking or non-blocking
* way.
*/
class CompositeMapRenderer : public osg::Drawable
{
@ -46,7 +47,7 @@ namespace Terrain
void setTargetFrameRate(float framerate);
/// Add a composite map to be rendered
void addCompositeMap(CompositeMap* map, bool immediate=false);
void addCompositeMap(CompositeMap* map, bool immediate = false);
/// Mark this composite map to be required for the current frame
void setImmediate(CompositeMap* map);
@ -58,7 +59,7 @@ namespace Terrain
double mMinimumTimeAvailable;
mutable osg::Timer mTimer;
typedef std::set<osg::ref_ptr<CompositeMap> > CompileSet;
typedef std::set<osg::ref_ptr<CompositeMap>> CompileSet;
mutable CompileSet mCompileSet;
mutable CompileSet mImmediateCompileSet;

View file

@ -1,8 +1,8 @@
#ifndef COMPONENTS_TERRAIN_HEIGHTCULL_H
#define COMPONENTS_TERRAIN_HEIGHTCULL_H
#include <osg/ref_ptr>
#include <osg/Referenced>
#include <osg/ref_ptr>
#include <limits>
@ -19,38 +19,21 @@ namespace Terrain
class HeightCullCallback : public SceneUtil::NodeCallback<HeightCullCallback>
{
public:
void setLowZ(float z)
{
mLowZ = z;
}
float getLowZ() const
{
return mLowZ;
}
void setLowZ(float z) { mLowZ = z; }
float getLowZ() const { return mLowZ; }
void setHighZ(float highZ)
{
mHighZ = highZ;
}
float getHighZ() const
{
return mHighZ;
}
void setHighZ(float highZ) { mHighZ = highZ; }
float getHighZ() const { return mHighZ; }
void setCullMask(unsigned int mask)
{
mMask = mask;
}
unsigned int getCullMask() const
{
return mMask;
}
void setCullMask(unsigned int mask) { mMask = mask; }
unsigned int getCullMask() const { return mMask; }
void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (mLowZ <= mHighZ)
traverse(node, nv);
}
private:
float mLowZ{ -std::numeric_limits<float>::max() };
float mHighZ{ std::numeric_limits<float>::max() };

View file

@ -1,17 +1,17 @@
#include "material.hpp"
#include <osg/Fog>
#include <osg/Depth>
#include <osg/TexEnvCombine>
#include <osg/Texture2D>
#include <osg/TexMat>
#include <osg/BlendFunc>
#include <osg/Capability>
#include <osg/Depth>
#include <osg/Fog>
#include <osg/TexEnvCombine>
#include <osg/TexMat>
#include <osg/Texture2D>
#include <components/stereo/stereomanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/sceneutil/depth.hpp>
#include <components/shader/shadermanager.hpp>
#include <components/stereo/stereomanager.hpp>
#include <mutex>
@ -33,13 +33,14 @@ namespace
if (texMat == mTexMatMap.end())
{
osg::Matrixf matrix;
float scale = (blendmapScale/(static_cast<float>(blendmapScale)+1.f));
float scale = (blendmapScale / (static_cast<float>(blendmapScale) + 1.f));
matrix.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f));
matrix.preMultScale(osg::Vec3f(scale, scale, 1.f));
matrix.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f));
// We need to nudge the blendmap to look like vanilla.
// This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the blendmap, apparently.
matrix.preMultTranslate(osg::Vec3f(1.0f/blendmapScale/4.0f, 1.0f/blendmapScale/4.0f, 0.f));
// This causes visible seams unless the blendmap's resolution is doubled, but Vanilla also doubles the
// blendmap, apparently.
matrix.preMultTranslate(osg::Vec3f(1.0f / blendmapScale / 4.0f, 1.0f / blendmapScale / 4.0f, 0.f));
texMat = mTexMatMap.insert(std::make_pair(blendmapScale, new osg::TexMat(matrix))).first;
}
@ -66,8 +67,10 @@ namespace
auto texMat = mTexMatMap.find(layerTileSize);
if (texMat == mTexMatMap.end())
{
texMat = mTexMatMap.insert(std::make_pair(layerTileSize,
new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize, layerTileSize, 1.f))))).first;
texMat = mTexMatMap
.insert(std::make_pair(layerTileSize,
new osg::TexMat(osg::Matrix::scale(osg::Vec3f(layerTileSize, layerTileSize, 1.f)))))
.first;
}
return texMat->second;
}
@ -196,18 +199,19 @@ namespace
namespace Terrain
{
std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Resource::SceneManager* sceneManager, const std::vector<TextureLayer> &layers,
const std::vector<osg::ref_ptr<osg::Texture2D> > &blendmaps, int blendmapScale, float layerTileSize)
std::vector<osg::ref_ptr<osg::StateSet>> createPasses(bool useShaders, Resource::SceneManager* sceneManager,
const std::vector<TextureLayer>& layers, const std::vector<osg::ref_ptr<osg::Texture2D>>& blendmaps,
int blendmapScale, float layerTileSize)
{
auto& shaderManager = sceneManager->getShaderManager();
std::vector<osg::ref_ptr<osg::StateSet> > passes;
std::vector<osg::ref_ptr<osg::StateSet>> passes;
unsigned int blendmapIndex = 0;
for (std::vector<TextureLayer>::const_iterator it = layers.begin(); it != layers.end(); ++it)
{
bool firstLayer = (it == layers.begin());
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
osg::ref_ptr<osg::StateSet> stateset(new osg::StateSet);
if (!blendmaps.empty())
{
@ -232,7 +236,8 @@ namespace Terrain
stateset->setTextureAttributeAndModes(0, it->mDiffuseMap);
if (layerTileSize != 1.f)
stateset->setTextureAttributeAndModes(0, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(
0, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);
stateset->addUniform(UniformCollection::value().mDiffuseMap);
@ -259,8 +264,10 @@ namespace Terrain
defineMap["writeNormals"] = (it == layers.end() - 1) ? "1" : "0";
Stereo::Manager::instance().shaderStereoDefines(defineMap);
osg::ref_ptr<osg::Shader> vertexShader = shaderManager.getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
osg::ref_ptr<osg::Shader> fragmentShader = shaderManager.getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
osg::ref_ptr<osg::Shader> vertexShader
= shaderManager.getShader("terrain_vertex.glsl", defineMap, osg::Shader::VERTEX);
osg::ref_ptr<osg::Shader> fragmentShader
= shaderManager.getShader("terrain_fragment.glsl", defineMap, osg::Shader::FRAGMENT);
if (!vertexShader || !fragmentShader)
{
// Try again without shader. Error already logged by above
@ -277,7 +284,8 @@ namespace Terrain
stateset->setTextureAttributeAndModes(0, tex.get());
if (layerTileSize != 1.f)
stateset->setTextureAttributeAndModes(0, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(
0, LayerTexMat::value(layerTileSize), osg::StateAttribute::ON);
// Multiply by the alpha map
if (!blendmaps.empty())
@ -290,7 +298,6 @@ namespace Terrain
stateset->setTextureAttributeAndModes(1, BlendmapTexMat::value(blendmapScale));
stateset->setTextureAttributeAndModes(1, TexEnvCombine::value(), osg::StateAttribute::ON);
}
}
passes.push_back(stateset);

View file

@ -24,9 +24,9 @@ namespace Terrain
bool mSpecular;
};
std::vector<osg::ref_ptr<osg::StateSet> > createPasses(bool useShaders, Resource::SceneManager* sceneManager,
const std::vector<TextureLayer>& layers,
const std::vector<osg::ref_ptr<osg::Texture2D> >& blendmaps, int blendmapScale, float layerTileSize);
std::vector<osg::ref_ptr<osg::StateSet>> createPasses(bool useShaders, Resource::SceneManager* sceneManager,
const std::vector<TextureLayer>& layers, const std::vector<osg::ref_ptr<osg::Texture2D>>& blendmaps,
int blendmapScale, float layerTileSize);
}

View file

@ -10,144 +10,140 @@
namespace Terrain
{
float distance(const osg::BoundingBox& box, const osg::Vec3f& v)
{
if (box.contains(v))
return 0;
else
float distance(const osg::BoundingBox& box, const osg::Vec3f& v)
{
osg::Vec3f maxDist(0,0,0);
if (v.x() < box.xMin())
maxDist.x() = box.xMin() - v.x();
else if (v.x() > box.xMax())
maxDist.x() = v.x() - box.xMax();
if (v.y() < box.yMin())
maxDist.y() = box.yMin() - v.y();
else if (v.y() > box.yMax())
maxDist.y() = v.y() - box.yMax();
if (v.z() < box.zMin())
maxDist.z() = box.zMin() - v.z();
else if (v.z() > box.zMax())
maxDist.z() = v.z() - box.zMax();
return maxDist.length();
if (box.contains(v))
return 0;
else
{
osg::Vec3f maxDist(0, 0, 0);
if (v.x() < box.xMin())
maxDist.x() = box.xMin() - v.x();
else if (v.x() > box.xMax())
maxDist.x() = v.x() - box.xMax();
if (v.y() < box.yMin())
maxDist.y() = box.yMin() - v.y();
else if (v.y() > box.yMax())
maxDist.y() = v.y() - box.yMax();
if (v.z() < box.zMin())
maxDist.z() = box.zMin() - v.z();
else if (v.z() > box.zMax())
maxDist.z() = v.z() - box.zMax();
return maxDist.length();
}
}
}
ChildDirection reflect(ChildDirection dir, Direction dir2)
{
assert(dir != Root);
const int lookupTable[4][4] =
ChildDirection reflect(ChildDirection dir, Direction dir2)
{
// NW NE SW SE
{ SW, SE, NW, NE }, // N
{ NE, NW, SE, SW }, // E
{ SW, SE, NW, NE }, // S
{ NE, NW, SE, SW } // W
};
return (ChildDirection)lookupTable[dir2][dir];
}
bool adjacent(ChildDirection dir, Direction dir2)
{
assert(dir != Root);
const bool lookupTable[4][4] =
{
// NW NE SW SE
{ true, true, false, false }, // N
{ false, true, false, true }, // E
{ false, false, true, true }, // S
{ true, false, true, false } // W
};
return lookupTable[dir2][dir];
}
QuadTreeNode* searchNeighbour (QuadTreeNode* currentNode, Direction dir)
{
if (currentNode->getDirection() == Root)
return nullptr; // Arrived at root node, the root node does not have neighbours
QuadTreeNode* nextNode;
if (adjacent(currentNode->getDirection(), dir))
nextNode = searchNeighbour(currentNode->getParent(), dir);
else
nextNode = currentNode->getParent();
if (nextNode && nextNode->getNumChildren())
return nextNode->getChild(reflect(currentNode->getDirection(), dir));
else
return nullptr;
}
QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float size, const osg::Vec2f& center)
: mParent(parent)
, mDirection(direction)
, mValidBounds(false)
, mSize(size)
, mCenter(center)
{
for (unsigned int i=0; i<4; ++i)
mNeighbours[i] = nullptr;
}
QuadTreeNode::~QuadTreeNode()
{
}
QuadTreeNode *QuadTreeNode::getNeighbour(Direction dir)
{
return mNeighbours[dir];
}
float QuadTreeNode::distance(const osg::Vec3f& v) const
{
const osg::BoundingBox& box = getBoundingBox();
return Terrain::distance(box, v);
}
void QuadTreeNode::initNeighbours()
{
for (int i=0; i<4; ++i)
mNeighbours[i] = searchNeighbour(this, (Direction)i);
for (unsigned int i=0; i<getNumChildren(); ++i)
getChild(i)->initNeighbours();
}
void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback)
{
if (!hasValidBounds())
return;
LodCallback::ReturnValue lodResult = lodCallback->isSufficientDetail(this, distance(viewPoint));
if (lodResult == LodCallback::StopTraversal)
return;
else if (lodResult == LodCallback::Deeper && getNumChildren())
{
for (unsigned int i=0; i<getNumChildren(); ++i)
getChild(i)->traverseNodes(vd, viewPoint, lodCallback);
assert(dir != Root);
const int lookupTable[4][4] = {
// NW NE SW SE
{ SW, SE, NW, NE }, // N
{ NE, NW, SE, SW }, // E
{ SW, SE, NW, NE }, // S
{ NE, NW, SE, SW } // W
};
return (ChildDirection)lookupTable[dir2][dir];
}
else
vd->add(this);
}
void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox)
{
mBoundingBox = boundingBox;
mValidBounds = boundingBox.valid();
}
bool adjacent(ChildDirection dir, Direction dir2)
{
assert(dir != Root);
const bool lookupTable[4][4] = {
// NW NE SW SE
{ true, true, false, false }, // N
{ false, true, false, true }, // E
{ false, false, true, true }, // S
{ true, false, true, false } // W
};
return lookupTable[dir2][dir];
}
const osg::BoundingBox &QuadTreeNode::getBoundingBox() const
{
return mBoundingBox;
}
QuadTreeNode* searchNeighbour(QuadTreeNode* currentNode, Direction dir)
{
if (currentNode->getDirection() == Root)
return nullptr; // Arrived at root node, the root node does not have neighbours
float QuadTreeNode::getSize() const
{
return mSize;
}
QuadTreeNode* nextNode;
if (adjacent(currentNode->getDirection(), dir))
nextNode = searchNeighbour(currentNode->getParent(), dir);
else
nextNode = currentNode->getParent();
const osg::Vec2f &QuadTreeNode::getCenter() const
{
return mCenter;
}
if (nextNode && nextNode->getNumChildren())
return nextNode->getChild(reflect(currentNode->getDirection(), dir));
else
return nullptr;
}
QuadTreeNode::QuadTreeNode(QuadTreeNode* parent, ChildDirection direction, float size, const osg::Vec2f& center)
: mParent(parent)
, mDirection(direction)
, mValidBounds(false)
, mSize(size)
, mCenter(center)
{
for (unsigned int i = 0; i < 4; ++i)
mNeighbours[i] = nullptr;
}
QuadTreeNode::~QuadTreeNode() {}
QuadTreeNode* QuadTreeNode::getNeighbour(Direction dir)
{
return mNeighbours[dir];
}
float QuadTreeNode::distance(const osg::Vec3f& v) const
{
const osg::BoundingBox& box = getBoundingBox();
return Terrain::distance(box, v);
}
void QuadTreeNode::initNeighbours()
{
for (int i = 0; i < 4; ++i)
mNeighbours[i] = searchNeighbour(this, (Direction)i);
for (unsigned int i = 0; i < getNumChildren(); ++i)
getChild(i)->initNeighbours();
}
void QuadTreeNode::traverseNodes(ViewData* vd, const osg::Vec3f& viewPoint, LodCallback* lodCallback)
{
if (!hasValidBounds())
return;
LodCallback::ReturnValue lodResult = lodCallback->isSufficientDetail(this, distance(viewPoint));
if (lodResult == LodCallback::StopTraversal)
return;
else if (lodResult == LodCallback::Deeper && getNumChildren())
{
for (unsigned int i = 0; i < getNumChildren(); ++i)
getChild(i)->traverseNodes(vd, viewPoint, lodCallback);
}
else
vd->add(this);
}
void QuadTreeNode::setBoundingBox(const osg::BoundingBox& boundingBox)
{
mBoundingBox = boundingBox;
mValidBounds = boundingBox.valid();
}
const osg::BoundingBox& QuadTreeNode::getBoundingBox() const
{
return mBoundingBox;
}
float QuadTreeNode::getSize() const
{
return mSize;
}
const osg::Vec2f& QuadTreeNode::getCenter() const
{
return mCenter;
}
}

View file

@ -29,7 +29,7 @@ namespace Terrain
StopTraversal,
StopTraversalAndUse
};
virtual ReturnValue isSufficientDetail(QuadTreeNode *node, float dist) = 0;
virtual ReturnValue isSufficientDetail(QuadTreeNode* node, float dist) = 0;
};
class ViewData;
@ -62,7 +62,7 @@ namespace Terrain
ChildDirection getDirection() { return mDirection; }
/// Get neighbour node in this direction
QuadTreeNode* getNeighbour (Direction dir);
QuadTreeNode* getNeighbour(Direction dir);
/// Initialize neighbours - do this after the quadtree is built
void initNeighbours();

File diff suppressed because it is too large Load diff

View file

@ -3,9 +3,9 @@
#include "terraingrid.hpp"
#include <mutex>
#include <memory>
#include <atomic>
#include <memory>
#include <mutex>
namespace osg
{
@ -20,14 +20,18 @@ namespace Terrain
class ViewDataMap;
class ViewData;
struct ViewDataEntry;
class DebugChunkManager;
/// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD.
class QuadTreeWorld : public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell)
class QuadTreeWorld
: public TerrainGrid // note: derived from TerrainGrid is only to render default cells (see loadCell)
{
public:
QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask, int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize, bool debugChunks);
QuadTreeWorld(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask,
int compMapResolution, float comMapLevel, float lodFactor, int vertexLodMod, float maxCompGeometrySize,
bool debugChunks);
~QuadTreeWorld();
@ -37,14 +41,15 @@ namespace Terrain
void setViewDistance(float distance) override;
void cacheCell(View *view, int x, int y) override {}
void cacheCell(View* view, int x, int y) override {}
/// @note Not thread safe.
void loadCell(int x, int y) override;
/// @note Not thread safe.
void unloadCell(int x, int y) override;
View* createView() override;
void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic<bool>& abort, Loading::Reporter& reporter) override;
void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i& cellgrid, std::atomic<bool>& abort,
Loading::Reporter& reporter) override;
void rebuildViews() override;
void reportStats(unsigned int frameNumber, osg::Stats* stats) override;
@ -52,8 +57,10 @@ namespace Terrain
class ChunkManager
{
public:
virtual ~ChunkManager(){}
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) = 0;
virtual ~ChunkManager() {}
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)
= 0;
virtual unsigned int getNodeMask() { return 0; }
void setViewDistance(float viewDistance) { mViewDistance = viewDistance; }
@ -62,6 +69,7 @@ namespace Terrain
// Automatically set by addChunkManager based on getViewDistance()
unsigned int getMaxLodLevel() const { return mMaxLodLevel; }
void setMaxLodLevel(unsigned int level) { mMaxLodLevel = level; }
private:
float mViewDistance = 0.f;
unsigned int mMaxLodLevel = ~0u;
@ -70,7 +78,8 @@ namespace Terrain
private:
void ensureQuadTreeBuilt();
void loadRenderingNode(ViewDataEntry& entry, ViewData* vd, float cellWorldSize, const osg::Vec4i &gridbounds, bool compile);
void loadRenderingNode(
ViewDataEntry& entry, ViewData* vd, float cellWorldSize, const osg::Vec4i& gridbounds, bool compile);
osg::ref_ptr<RootNode> mRootNode;

View file

@ -3,10 +3,10 @@
#include <vector>
#include <osg/Array>
#include <osg/Vec2f>
#include <osg/Vec3f>
#include <osg/ref_ptr>
#include <osg/Array>
#include "defs.hpp"
@ -33,7 +33,7 @@ namespace Terrain
virtual bool hasData(int cellX, int cellY)
{
float dummy;
return getMinMaxHeights(1, osg::Vec2f(cellX+0.5, cellY+0.5), dummy, dummy);
return getMinMaxHeights(1, osg::Vec2f(cellX + 0.5, cellY + 0.5), dummy, dummy);
}
/// Get the minimum and maximum heights of a terrain region.
@ -44,7 +44,7 @@ namespace Terrain
/// @param min min height will be stored here
/// @param max max height will be stored here
/// @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, float& min, float& max) = 0;
/// Fill vertex buffers for a terrain chunk.
/// @note May be called from background threads. Make sure to only call thread-safe functions from here!
@ -57,12 +57,12 @@ namespace Terrain
/// @param positions buffer to write vertices
/// @param normals buffer to write vertex normals
/// @param colours buffer to write vertex colours
virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center,
osg::ref_ptr<osg::Vec3Array> positions,
osg::ref_ptr<osg::Vec3Array> normals,
osg::ref_ptr<osg::Vec4ubArray> colours) = 0;
virtual void fillVertexBuffers(int lodLevel, float size, const osg::Vec2f& center,
osg::ref_ptr<osg::Vec3Array> positions, osg::ref_ptr<osg::Vec3Array> normals,
osg::ref_ptr<osg::Vec4ubArray> colours)
= 0;
typedef std::vector<osg::ref_ptr<osg::Image> > ImageVector;
typedef std::vector<osg::ref_ptr<osg::Image>> ImageVector;
/// Create textures holding layer blend values for a terrain chunk.
/// @note The terrain chunk shouldn't be larger than one cell since otherwise we might
/// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.
@ -71,10 +71,11 @@ namespace Terrain
/// @param chunkCenter center of the chunk in cell units
/// @param blendmaps created blendmaps will be written here
/// @param layerList names of the layer textures used will be written here
virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,
std::vector<LayerInfo>& layerList) = 0;
virtual void getBlendmaps(
float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, std::vector<LayerInfo>& layerList)
= 0;
virtual float getHeightAt (const osg::Vec3f& worldPos) = 0;
virtual float getHeightAt(const osg::Vec3f& worldPos) = 0;
/// Get the transformation factor for mapping cell units to world units.
virtual float getCellWorldSize() = 0;

View file

@ -10,159 +10,156 @@
namespace Terrain
{
TerrainDrawable::TerrainDrawable()
{
TerrainDrawable::TerrainDrawable() {}
}
TerrainDrawable::~TerrainDrawable() {}
TerrainDrawable::~TerrainDrawable()
{
}
TerrainDrawable::TerrainDrawable(const TerrainDrawable &copy, const osg::CopyOp &copyop)
: osg::Geometry(copy, copyop)
, mPasses(copy.mPasses)
, mLightListCallback(copy.mLightListCallback)
{
}
void TerrainDrawable::accept(osg::NodeVisitor &nv)
{
if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)
TerrainDrawable::TerrainDrawable(const TerrainDrawable& copy, const osg::CopyOp& copyop)
: osg::Geometry(copy, copyop)
, mPasses(copy.mPasses)
, mLightListCallback(copy.mLightListCallback)
{
osg::Geometry::accept(nv);
}
else if (nv.validNodeMask(*this))
{
nv.pushOntoNodePath(this);
cull(static_cast<osgUtil::CullVisitor*>(&nv));
nv.popFromNodePath();
}
}
inline float distance(const osg::Vec3& coord,const osg::Matrix& matrix)
{
return -((float)coord[0]*(float)matrix(0,2)+(float)coord[1]*(float)matrix(1,2)+(float)coord[2]*(float)matrix(2,2)+matrix(3,2));
}
//canot use ClusterCullingCallback::cull: viewpoint != eyepoint
// !osgfixpotential!
bool clusterCull(osg::ClusterCullingCallback* cb, const osg::Vec3f& eyePoint, bool shadowcam)
{
float _deviation = cb->getDeviation();
const osg::Vec3& _controlPoint = cb->getControlPoint();
osg::Vec3 _normal = cb->getNormal();
if (shadowcam) _normal = _normal * -1; //inverting for shadowcam frontfaceculing
float _radius = cb->getRadius();
if (_deviation<=-1.0f) return false;
osg::Vec3 eye_cp = eyePoint - _controlPoint;
float radius = eye_cp.length();
if (radius<_radius) return false;
float deviation = (eye_cp * _normal)/radius;
return deviation < _deviation;
}
void TerrainDrawable::cull(osgUtil::CullVisitor *cv)
{
const osg::BoundingBox& bb = getBoundingBox();
if (_cullingActive && cv->isCulled(getBoundingBox()))
return;
bool shadowcam = cv->getCurrentCamera()->getName() == "ShadowCamera";
if (cv->getCullingMode() & osg::CullStack::CLUSTER_CULLING && clusterCull(mClusterCullingCallback, cv->getEyePoint(), shadowcam))
return;
osg::RefMatrix& matrix = *cv->getModelViewMatrix();
if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR && bb.valid())
{
if (!cv->updateCalculatedNearFar(matrix, *this, false))
return;
}
float depth = bb.valid() ? distance(bb.center(),matrix) : 0.0f;
if (osg::isNaN(depth))
return;
if (shadowcam)
void TerrainDrawable::accept(osg::NodeVisitor& nv)
{
cv->addDrawableAndDepth(this, &matrix, depth);
return;
if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)
{
osg::Geometry::accept(nv);
}
else if (nv.validNodeMask(*this))
{
nv.pushOntoNodePath(this);
cull(static_cast<osgUtil::CullVisitor*>(&nv));
nv.popFromNodePath();
}
}
if (mCompositeMap && mCompositeMapRenderer)
inline float distance(const osg::Vec3& coord, const osg::Matrix& matrix)
{
mCompositeMapRenderer->setImmediate(mCompositeMap);
mCompositeMapRenderer = nullptr;
return -((float)coord[0] * (float)matrix(0, 2) + (float)coord[1] * (float)matrix(1, 2)
+ (float)coord[2] * (float)matrix(2, 2) + matrix(3, 2));
}
bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv);
osg::StateSet* stateset = getStateSet();
if (stateset)
cv->pushStateSet(stateset);
for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it)
// canot use ClusterCullingCallback::cull: viewpoint != eyepoint
// !osgfixpotential!
bool clusterCull(osg::ClusterCullingCallback* cb, const osg::Vec3f& eyePoint, bool shadowcam)
{
cv->pushStateSet(*it);
cv->addDrawableAndDepth(this, &matrix, depth);
cv->popStateSet();
float _deviation = cb->getDeviation();
const osg::Vec3& _controlPoint = cb->getControlPoint();
osg::Vec3 _normal = cb->getNormal();
if (shadowcam)
_normal = _normal * -1; // inverting for shadowcam frontfaceculing
float _radius = cb->getRadius();
if (_deviation <= -1.0f)
return false;
osg::Vec3 eye_cp = eyePoint - _controlPoint;
float radius = eye_cp.length();
if (radius < _radius)
return false;
float deviation = (eye_cp * _normal) / radius;
return deviation < _deviation;
}
if (stateset)
cv->popStateSet();
if (pushedLight)
cv->popStateSet();
}
void TerrainDrawable::createClusterCullingCallback()
{
mClusterCullingCallback = new osg::ClusterCullingCallback(this);
}
void TerrainDrawable::setPasses(const TerrainDrawable::PassVector &passes)
{
mPasses = passes;
}
void TerrainDrawable::setLightListCallback(SceneUtil::LightListCallback *lightListCallback)
{
mLightListCallback = lightListCallback;
}
void TerrainDrawable::setupWaterBoundingBox(float waterheight, float margin)
{
osg::Vec3Array* vertices = static_cast<osg::Vec3Array*>(getVertexArray());
for (unsigned int i=0; i<vertices->size(); ++i)
{
const osg::Vec3f& vertex = (*vertices)[i];
if (vertex.z() <= waterheight)
mWaterBoundingBox.expandBy(vertex);
}
if (mWaterBoundingBox.valid())
void TerrainDrawable::cull(osgUtil::CullVisitor* cv)
{
const osg::BoundingBox& bb = getBoundingBox();
mWaterBoundingBox.xMin() = std::max(bb.xMin(), mWaterBoundingBox.xMin() - margin);
mWaterBoundingBox.yMin() = std::max(bb.yMin(), mWaterBoundingBox.yMin() - margin);
mWaterBoundingBox.xMax() = std::min(bb.xMax(), mWaterBoundingBox.xMax() + margin);
mWaterBoundingBox.xMax() = std::min(bb.xMax(), mWaterBoundingBox.xMax() + margin);
}
}
void TerrainDrawable::compileGLObjects(osg::RenderInfo &renderInfo) const
{
for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it)
if (_cullingActive && cv->isCulled(getBoundingBox()))
return;
bool shadowcam = cv->getCurrentCamera()->getName() == "ShadowCamera";
if (cv->getCullingMode() & osg::CullStack::CLUSTER_CULLING
&& clusterCull(mClusterCullingCallback, cv->getEyePoint(), shadowcam))
return;
osg::RefMatrix& matrix = *cv->getModelViewMatrix();
if (cv->getComputeNearFarMode() != osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR && bb.valid())
{
if (!cv->updateCalculatedNearFar(matrix, *this, false))
return;
}
float depth = bb.valid() ? distance(bb.center(), matrix) : 0.0f;
if (osg::isNaN(depth))
return;
if (shadowcam)
{
cv->addDrawableAndDepth(this, &matrix, depth);
return;
}
if (mCompositeMap && mCompositeMapRenderer)
{
mCompositeMapRenderer->setImmediate(mCompositeMap);
mCompositeMapRenderer = nullptr;
}
bool pushedLight = mLightListCallback && mLightListCallback->pushLightState(this, cv);
osg::StateSet* stateset = getStateSet();
if (stateset)
cv->pushStateSet(stateset);
for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it)
{
cv->pushStateSet(*it);
cv->addDrawableAndDepth(this, &matrix, depth);
cv->popStateSet();
}
if (stateset)
cv->popStateSet();
if (pushedLight)
cv->popStateSet();
}
void TerrainDrawable::createClusterCullingCallback()
{
osg::StateSet* stateset = *it;
stateset->compileGLObjects(*renderInfo.getState());
mClusterCullingCallback = new osg::ClusterCullingCallback(this);
}
osg::Geometry::compileGLObjects(renderInfo);
}
void TerrainDrawable::setPasses(const TerrainDrawable::PassVector& passes)
{
mPasses = passes;
}
void TerrainDrawable::setLightListCallback(SceneUtil::LightListCallback* lightListCallback)
{
mLightListCallback = lightListCallback;
}
void TerrainDrawable::setupWaterBoundingBox(float waterheight, float margin)
{
osg::Vec3Array* vertices = static_cast<osg::Vec3Array*>(getVertexArray());
for (unsigned int i = 0; i < vertices->size(); ++i)
{
const osg::Vec3f& vertex = (*vertices)[i];
if (vertex.z() <= waterheight)
mWaterBoundingBox.expandBy(vertex);
}
if (mWaterBoundingBox.valid())
{
const osg::BoundingBox& bb = getBoundingBox();
mWaterBoundingBox.xMin() = std::max(bb.xMin(), mWaterBoundingBox.xMin() - margin);
mWaterBoundingBox.yMin() = std::max(bb.yMin(), mWaterBoundingBox.yMin() - margin);
mWaterBoundingBox.xMax() = std::min(bb.xMax(), mWaterBoundingBox.xMax() + margin);
mWaterBoundingBox.xMax() = std::min(bb.xMax(), mWaterBoundingBox.xMax() + margin);
}
}
void TerrainDrawable::compileGLObjects(osg::RenderInfo& renderInfo) const
{
for (PassVector::const_iterator it = mPasses.begin(); it != mPasses.end(); ++it)
{
osg::StateSet* stateset = *it;
stateset->compileGLObjects(*renderInfo.getState());
}
osg::Geometry::compileGLObjects(renderInfo);
}
}

View file

@ -30,9 +30,12 @@ namespace Terrain
class TerrainDrawable : public osg::Geometry
{
public:
osg::Object* cloneType() const override { return new TerrainDrawable (); }
osg::Object* clone(const osg::CopyOp& copyop) const override { return new TerrainDrawable (*this,copyop); }
bool isSameKindAs(const osg::Object* obj) const override { return dynamic_cast<const TerrainDrawable *>(obj)!=nullptr; }
osg::Object* cloneType() const override { return new TerrainDrawable(); }
osg::Object* clone(const osg::CopyOp& copyop) const override { return new TerrainDrawable(*this, copyop); }
bool isSameKindAs(const osg::Object* obj) const override
{
return dynamic_cast<const TerrainDrawable*>(obj) != nullptr;
}
const char* className() const override { return "TerrainDrawable"; }
const char* libraryName() const override { return "Terrain"; }
@ -40,12 +43,12 @@ namespace Terrain
~TerrainDrawable(); // has to be defined in the cpp file because we only forward declared some members.
TerrainDrawable(const TerrainDrawable& copy, const osg::CopyOp& copyop);
void accept(osg::NodeVisitor &nv) override;
void accept(osg::NodeVisitor& nv) override;
void cull(osgUtil::CullVisitor* cv);
typedef std::vector<osg::ref_ptr<osg::StateSet> > PassVector;
void setPasses (const PassVector& passes);
const PassVector& getPasses() const { return mPasses; }
typedef std::vector<osg::ref_ptr<osg::StateSet>> PassVector;
void setPasses(const PassVector& passes);
const PassVector& getPasses() const { return mPasses; }
void setLightListCallback(SceneUtil::LightListCallback* lightListCallback);
@ -73,5 +76,4 @@ namespace Terrain
}
#endif

View file

@ -2,131 +2,135 @@
#include <memory>
#include <osg/Group>
#include <osg/ComputeBoundsVisitor>
#include <osg/Group>
#include <components/sceneutil/positionattitudetransform.hpp>
#include "chunkmanager.hpp"
#include "compositemaprenderer.hpp"
#include "view.hpp"
#include "storage.hpp"
#include "heightcull.hpp"
#include "storage.hpp"
#include "view.hpp"
#include <components/sceneutil/positionattitudetransform.hpp>
namespace Terrain
{
class MyView : public View
{
public:
osg::ref_ptr<osg::Node> mLoaded;
void reset() override {}
};
TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask)
: Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask)
, mNumSplits(4)
{
}
TerrainGrid::TerrainGrid(osg::Group* parent, Storage* storage, unsigned int nodeMask)
: Terrain::World(parent, storage, nodeMask)
, mNumSplits(4)
{
}
TerrainGrid::~TerrainGrid()
{
while (!mGrid.empty())
class MyView : public View
{
TerrainGrid::unloadCell(mGrid.begin()->first.first, mGrid.begin()->first.second);
}
}
public:
osg::ref_ptr<osg::Node> mLoaded;
void TerrainGrid::cacheCell(View* view, int x, int y)
{
osg::Vec2f center(x+0.5f, y+0.5f);
static_cast<MyView*>(view)->mLoaded = buildTerrain(nullptr, 1.f, center);
}
void reset() override {}
};
osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter)
{
if (chunkSize * mNumSplits > 1.f)
TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask)
: Terrain::World(parent, compileRoot, resourceSystem, storage, nodeMask, preCompileMask, borderMask)
, mNumSplits(4)
{
// keep splitting
osg::ref_ptr<osg::Group> group (new osg::Group);
if (parent)
parent->addChild(group);
float newChunkSize = chunkSize/2.f;
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize/2.f, newChunkSize/2.f));
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize/2.f, -newChunkSize/2.f));
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize/2.f, newChunkSize/2.f));
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize/2.f, -newChunkSize/2.f));
return group;
}
else
TerrainGrid::TerrainGrid(osg::Group* parent, Storage* storage, unsigned int nodeMask)
: Terrain::World(parent, storage, nodeMask)
, mNumSplits(4)
{
osg::ref_ptr<osg::Node> node = mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f(), true);
if (!node)
return nullptr;
const float cellWorldSize = mStorage->getCellWorldSize();
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;
pat->setPosition(osg::Vec3f(chunkCenter.x()*cellWorldSize, chunkCenter.y()*cellWorldSize, 0.f));
pat->addChild(node);
if (parent)
parent->addChild(pat);
return pat;
}
}
void TerrainGrid::loadCell(int x, int y)
{
if (mGrid.find(std::make_pair(x, y)) != mGrid.end())
return; // already loaded
TerrainGrid::~TerrainGrid()
{
while (!mGrid.empty())
{
TerrainGrid::unloadCell(mGrid.begin()->first.first, mGrid.begin()->first.second);
}
}
osg::Vec2f center(x+0.5f, y+0.5f);
osg::ref_ptr<osg::Node> terrainNode = buildTerrain(nullptr, 1.f, center);
if (!terrainNode)
return; // no terrain defined
void TerrainGrid::cacheCell(View* view, int x, int y)
{
osg::Vec2f center(x + 0.5f, y + 0.5f);
static_cast<MyView*>(view)->mLoaded = buildTerrain(nullptr, 1.f, center);
}
TerrainGrid::World::loadCell(x,y);
osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain(
osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter)
{
if (chunkSize * mNumSplits > 1.f)
{
// keep splitting
osg::ref_ptr<osg::Group> group(new osg::Group);
if (parent)
parent->addChild(group);
mTerrainRoot->addChild(terrainNode);
float newChunkSize = chunkSize / 2.f;
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize / 2.f, newChunkSize / 2.f));
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(newChunkSize / 2.f, -newChunkSize / 2.f));
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize / 2.f, newChunkSize / 2.f));
buildTerrain(group, newChunkSize, chunkCenter + osg::Vec2f(-newChunkSize / 2.f, -newChunkSize / 2.f));
return group;
}
else
{
osg::ref_ptr<osg::Node> node
= mChunkManager->getChunk(chunkSize, chunkCenter, 0, 0, false, osg::Vec3f(), true);
if (!node)
return nullptr;
mGrid[std::make_pair(x,y)] = terrainNode;
updateWaterCulling();
}
const float cellWorldSize = mStorage->getCellWorldSize();
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> pat = new SceneUtil::PositionAttitudeTransform;
pat->setPosition(osg::Vec3f(chunkCenter.x() * cellWorldSize, chunkCenter.y() * cellWorldSize, 0.f));
pat->addChild(node);
if (parent)
parent->addChild(pat);
return pat;
}
}
void TerrainGrid::unloadCell(int x, int y)
{
CellBorder::CellGrid::iterator it = mGrid.find(std::make_pair(x,y));
if (it == mGrid.end())
return;
void TerrainGrid::loadCell(int x, int y)
{
if (mGrid.find(std::make_pair(x, y)) != mGrid.end())
return; // already loaded
Terrain::World::unloadCell(x,y);
osg::Vec2f center(x + 0.5f, y + 0.5f);
osg::ref_ptr<osg::Node> terrainNode = buildTerrain(nullptr, 1.f, center);
if (!terrainNode)
return; // no terrain defined
osg::ref_ptr<osg::Node> terrainNode = it->second;
mTerrainRoot->removeChild(terrainNode);
TerrainGrid::World::loadCell(x, y);
mGrid.erase(it);
updateWaterCulling();
}
mTerrainRoot->addChild(terrainNode);
void TerrainGrid::updateWaterCulling()
{
if (!mHeightCullCallback) return;
mGrid[std::make_pair(x, y)] = terrainNode;
updateWaterCulling();
}
osg::ComputeBoundsVisitor computeBoundsVisitor;
mTerrainRoot->accept(computeBoundsVisitor);
float lowZ = computeBoundsVisitor.getBoundingBox()._min.z();
mHeightCullCallback->setLowZ(lowZ);
}
void TerrainGrid::unloadCell(int x, int y)
{
CellBorder::CellGrid::iterator it = mGrid.find(std::make_pair(x, y));
if (it == mGrid.end())
return;
View *TerrainGrid::createView()
{
return new MyView;
}
Terrain::World::unloadCell(x, y);
osg::ref_ptr<osg::Node> terrainNode = it->second;
mTerrainRoot->removeChild(terrainNode);
mGrid.erase(it);
updateWaterCulling();
}
void TerrainGrid::updateWaterCulling()
{
if (!mHeightCullCallback)
return;
osg::ComputeBoundsVisitor computeBoundsVisitor;
mTerrainRoot->accept(computeBoundsVisitor);
float lowZ = computeBoundsVisitor.getBoundingBox()._min.z();
mHeightCullCallback->setLowZ(lowZ);
}
View* TerrainGrid::createView()
{
return new MyView;
}
}

View file

@ -26,8 +26,9 @@ namespace Terrain
class TerrainGrid : public Terrain::World
{
public:
TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask=~0u, unsigned int borderMask=0);
TerrainGrid(osg::Group* parent, Storage* storage, unsigned int nodeMask=~0u);
TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask = ~0u, unsigned int borderMask = 0);
TerrainGrid(osg::Group* parent, Storage* storage, unsigned int nodeMask = ~0u);
~TerrainGrid();
void cacheCell(View* view, int x, int y) override;
@ -44,7 +45,7 @@ namespace Terrain
bool isGridEmpty() const { return mGrid.empty(); }
private:
osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter);
osg::ref_ptr<osg::Node> buildTerrain(osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter);
void updateWaterCulling();
// split each ESM::Cell into mNumSplits*mNumSplits terrain chunks

View file

@ -3,62 +3,60 @@
#include <osg/Stats>
#include <osg/Texture2D>
#include <components/resource/scenemanager.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/resource/objectcache.hpp>
#include <components/resource/scenemanager.hpp>
namespace Terrain
{
TextureManager::TextureManager(Resource::SceneManager *sceneMgr)
: ResourceManager(sceneMgr->getVFS())
, mSceneManager(sceneMgr)
{
}
struct UpdateTextureFilteringFunctor
{
UpdateTextureFilteringFunctor(Resource::SceneManager* sceneMgr)
: mSceneManager(sceneMgr)
TextureManager::TextureManager(Resource::SceneManager* sceneMgr)
: ResourceManager(sceneMgr->getVFS())
, mSceneManager(sceneMgr)
{
}
Resource::SceneManager* mSceneManager;
void operator()(std::string, osg::Object* obj)
struct UpdateTextureFilteringFunctor
{
mSceneManager->applyFilterSettings(static_cast<osg::Texture2D*>(obj));
}
};
UpdateTextureFilteringFunctor(Resource::SceneManager* sceneMgr)
: mSceneManager(sceneMgr)
{
}
Resource::SceneManager* mSceneManager;
void TextureManager::updateTextureFiltering()
{
UpdateTextureFilteringFunctor f(mSceneManager);
mCache->call(f);
}
void operator()(std::string, osg::Object* obj)
{
mSceneManager->applyFilterSettings(static_cast<osg::Texture2D*>(obj));
}
};
osg::ref_ptr<osg::Texture2D> TextureManager::getTexture(const std::string &name)
{
// don't bother with case folding, since there is only one way of referring to terrain textures we can assume the case is always the same
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(name);
if (obj)
return static_cast<osg::Texture2D*>(obj.get());
else
void TextureManager::updateTextureFiltering()
{
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D(mSceneManager->getImageManager()->getImage(name)));
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
mSceneManager->applyFilterSettings(texture);
mCache->addEntryToObjectCache(name, texture.get());
return texture;
UpdateTextureFilteringFunctor f(mSceneManager);
mCache->call(f);
}
}
void TextureManager::reportStats(unsigned int frameNumber, osg::Stats *stats) const
{
stats->setAttribute(frameNumber, "Terrain Texture", mCache->getCacheSize());
}
osg::ref_ptr<osg::Texture2D> TextureManager::getTexture(const std::string& name)
{
// don't bother with case folding, since there is only one way of referring to terrain textures we can assume
// the case is always the same
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(name);
if (obj)
return static_cast<osg::Texture2D*>(obj.get());
else
{
osg::ref_ptr<osg::Texture2D> texture(new osg::Texture2D(mSceneManager->getImageManager()->getImage(name)));
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
mSceneManager->applyFilterSettings(texture);
mCache->addEntryToObjectCache(name, texture.get());
return texture;
}
}
void TextureManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
stats->setAttribute(frameNumber, "Terrain Texture", mCache->getCacheSize());
}
}

View file

@ -31,7 +31,6 @@ namespace Terrain
private:
Resource::SceneManager* mSceneManager;
};
}

View file

@ -5,202 +5,203 @@
namespace Terrain
{
ViewData::ViewData()
: mNumEntries(0)
, mLastUsageTimeStamp(0.0)
, mChanged(false)
, mHasViewPoint(false)
, mWorldUpdateRevision(0)
{
}
ViewData::ViewData()
: mNumEntries(0)
, mLastUsageTimeStamp(0.0)
, mChanged(false)
, mHasViewPoint(false)
, mWorldUpdateRevision(0)
{
}
ViewData::~ViewData()
{
}
ViewData::~ViewData() {}
void ViewData::copyFrom(const ViewData& other)
{
mNumEntries = other.mNumEntries;
mEntries = other.mEntries;
mChanged = other.mChanged;
mHasViewPoint = other.mHasViewPoint;
mViewPoint = other.mViewPoint;
mActiveGrid = other.mActiveGrid;
mWorldUpdateRevision = other.mWorldUpdateRevision;
}
void ViewData::copyFrom(const ViewData& other)
{
mNumEntries = other.mNumEntries;
mEntries = other.mEntries;
mChanged = other.mChanged;
mHasViewPoint = other.mHasViewPoint;
mViewPoint = other.mViewPoint;
mActiveGrid = other.mActiveGrid;
mWorldUpdateRevision = other.mWorldUpdateRevision;
}
void ViewData::add(QuadTreeNode *node)
{
unsigned int index = mNumEntries++;
void ViewData::add(QuadTreeNode* node)
{
unsigned int index = mNumEntries++;
if (index+1 > mEntries.size())
mEntries.resize(index+1);
if (index + 1 > mEntries.size())
mEntries.resize(index + 1);
ViewDataEntry& entry = mEntries[index];
if (entry.set(node))
mChanged = true;
}
ViewDataEntry& entry = mEntries[index];
if (entry.set(node))
mChanged = true;
}
void ViewData::setViewPoint(const osg::Vec3f &viewPoint)
{
mViewPoint = viewPoint;
mHasViewPoint = true;
}
void ViewData::setViewPoint(const osg::Vec3f& viewPoint)
{
mViewPoint = viewPoint;
mHasViewPoint = true;
}
// NOTE: As a performance optimisation, we cache mRenderingNodes from previous frames here.
// If this cache becomes invalid (e.g. through mWorldUpdateRevision), we need to use clear() instead of reset().
void ViewData::reset()
{
// clear any unused entries
for (unsigned int i=mNumEntries; i<mEntries.size(); ++i)
mEntries[i].set(nullptr);
// NOTE: As a performance optimisation, we cache mRenderingNodes from previous frames here.
// If this cache becomes invalid (e.g. through mWorldUpdateRevision), we need to use clear() instead of reset().
void ViewData::reset()
{
// clear any unused entries
for (unsigned int i = mNumEntries; i < mEntries.size(); ++i)
mEntries[i].set(nullptr);
// reset index for next frame
mNumEntries = 0;
mChanged = false;
}
// reset index for next frame
mNumEntries = 0;
mChanged = false;
}
void ViewData::clear()
{
for (unsigned int i=0; i<mEntries.size(); ++i)
mEntries[i].set(nullptr);
mNumEntries = 0;
mLastUsageTimeStamp = 0;
mChanged = false;
mHasViewPoint = false;
}
void ViewData::clear()
{
for (unsigned int i = 0; i < mEntries.size(); ++i)
mEntries[i].set(nullptr);
mNumEntries = 0;
mLastUsageTimeStamp = 0;
mChanged = false;
mHasViewPoint = false;
}
bool ViewData::suitableToUse(const osg::Vec4i &activeGrid) const
{
return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries();
}
bool ViewData::suitableToUse(const osg::Vec4i& activeGrid) const
{
return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries();
}
bool ViewData::contains(QuadTreeNode *node) const
{
for (unsigned int i=0; i<mNumEntries; ++i)
if (mEntries[i].mNode == node)
return true;
return false;
}
ViewDataEntry::ViewDataEntry()
: mNode(nullptr)
, mLodFlags(0)
{
}
bool ViewDataEntry::set(QuadTreeNode *node)
{
if (node == mNode)
bool ViewData::contains(QuadTreeNode* node) const
{
for (unsigned int i = 0; i < mNumEntries; ++i)
if (mEntries[i].mNode == node)
return true;
return false;
else
{
mNode = node;
// clear cached data
mRenderingNode = nullptr;
return true;
}
}
ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate)
{
ViewerMap::const_iterator found = mViewers.find(viewer);
ViewData* vd = nullptr;
if (found == mViewers.end())
ViewDataEntry::ViewDataEntry()
: mNode(nullptr)
, mLodFlags(0)
{
vd = createOrReuseView();
mViewers[viewer] = vd;
}
else
vd = found->second;
needsUpdate = false;
if (!(vd->suitableToUse(activeGrid) && (vd->getViewPoint()-viewPoint).length2() < mReuseDistance*mReuseDistance && vd->getWorldUpdateRevision() >= mWorldUpdateRevision))
bool ViewDataEntry::set(QuadTreeNode* node)
{
float shortestDist = viewer ? mReuseDistance*mReuseDistance : std::numeric_limits<float>::max();
const ViewData* mostSuitableView = nullptr;
for (const ViewData* other : mUsedViews)
if (node == mNode)
return false;
else
{
if (other->suitableToUse(activeGrid) && other->getWorldUpdateRevision() >= mWorldUpdateRevision)
mNode = node;
// clear cached data
mRenderingNode = nullptr;
return true;
}
}
ViewData* ViewDataMap::getViewData(
osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i& activeGrid, bool& needsUpdate)
{
ViewerMap::const_iterator found = mViewers.find(viewer);
ViewData* vd = nullptr;
if (found == mViewers.end())
{
vd = createOrReuseView();
mViewers[viewer] = vd;
}
else
vd = found->second;
needsUpdate = false;
if (!(vd->suitableToUse(activeGrid)
&& (vd->getViewPoint() - viewPoint).length2() < mReuseDistance * mReuseDistance
&& vd->getWorldUpdateRevision() >= mWorldUpdateRevision))
{
float shortestDist = viewer ? mReuseDistance * mReuseDistance : std::numeric_limits<float>::max();
const ViewData* mostSuitableView = nullptr;
for (const ViewData* other : mUsedViews)
{
float dist = (viewPoint-other->getViewPoint()).length2();
if (dist < shortestDist)
if (other->suitableToUse(activeGrid) && other->getWorldUpdateRevision() >= mWorldUpdateRevision)
{
shortestDist = dist;
mostSuitableView = other;
float dist = (viewPoint - other->getViewPoint()).length2();
if (dist < shortestDist)
{
shortestDist = dist;
mostSuitableView = other;
}
}
}
}
if (mostSuitableView && mostSuitableView != vd)
{
vd->copyFrom(*mostSuitableView);
return vd;
}
else if (!mostSuitableView)
{
if (vd->getWorldUpdateRevision() != mWorldUpdateRevision)
if (mostSuitableView && mostSuitableView != vd)
{
vd->setWorldUpdateRevision(mWorldUpdateRevision);
vd->clear();
vd->copyFrom(*mostSuitableView);
return vd;
}
else if (!mostSuitableView)
{
if (vd->getWorldUpdateRevision() != mWorldUpdateRevision)
{
vd->setWorldUpdateRevision(mWorldUpdateRevision);
vd->clear();
}
vd->setViewPoint(viewPoint);
vd->setActiveGrid(activeGrid);
needsUpdate = true;
}
vd->setViewPoint(viewPoint);
vd->setActiveGrid(activeGrid);
needsUpdate = true;
}
return vd;
}
return vd;
}
ViewData *ViewDataMap::createOrReuseView()
{
ViewData* vd = nullptr;
if (mUnusedViews.size())
ViewData* ViewDataMap::createOrReuseView()
{
vd = mUnusedViews.front();
mUnusedViews.pop_front();
}
else
{
mViewVector.emplace_back();
vd = &mViewVector.back();
}
mUsedViews.push_back(vd);
vd->setWorldUpdateRevision(mWorldUpdateRevision);
return vd;
}
ViewData *ViewDataMap::createIndependentView() const
{
ViewData* vd = new ViewData;
vd->setWorldUpdateRevision(mWorldUpdateRevision);
return vd;
}
void ViewDataMap::clearUnusedViews(double referenceTime)
{
for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end(); )
{
if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
mViewers.erase(it++);
else
++it;
}
for (std::deque<ViewData*>::iterator it = mUsedViews.begin(); it != mUsedViews.end(); )
{
if ((*it)->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
ViewData* vd = nullptr;
if (mUnusedViews.size())
{
(*it)->clear();
mUnusedViews.push_back(*it);
it = mUsedViews.erase(it);
vd = mUnusedViews.front();
mUnusedViews.pop_front();
}
else
++it;
{
mViewVector.emplace_back();
vd = &mViewVector.back();
}
mUsedViews.push_back(vd);
vd->setWorldUpdateRevision(mWorldUpdateRevision);
return vd;
}
}
void ViewDataMap::rebuildViews()
{
++mWorldUpdateRevision;
}
ViewData* ViewDataMap::createIndependentView() const
{
ViewData* vd = new ViewData;
vd->setWorldUpdateRevision(mWorldUpdateRevision);
return vd;
}
void ViewDataMap::clearUnusedViews(double referenceTime)
{
for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end();)
{
if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
mViewers.erase(it++);
else
++it;
}
for (std::deque<ViewData*>::iterator it = mUsedViews.begin(); it != mUsedViews.end();)
{
if ((*it)->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
{
(*it)->clear();
mUnusedViews.push_back(*it);
it = mUsedViews.erase(it);
}
else
++it;
}
}
void ViewDataMap::rebuildViews()
{
++mWorldUpdateRevision;
}
}

View file

@ -1,8 +1,8 @@
#ifndef OPENMW_COMPONENTS_TERRAIN_VIEWDATA_H
#define OPENMW_COMPONENTS_TERRAIN_VIEWDATA_H
#include <vector>
#include <deque>
#include <vector>
#include <osg/Node>
@ -60,8 +60,16 @@ namespace Terrain
void setViewPoint(const osg::Vec3f& viewPoint);
const osg::Vec3f& getViewPoint() const { return mViewPoint; }
void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} }
const osg::Vec4i &getActiveGrid() const { return mActiveGrid;}
void setActiveGrid(const osg::Vec4i& grid)
{
if (grid != mActiveGrid)
{
mActiveGrid = grid;
mEntries.clear();
mNumEntries = 0;
}
}
const osg::Vec4i& getActiveGrid() const { return mActiveGrid; }
unsigned int getWorldUpdateRevision() const { return mWorldUpdateRevision; }
void setWorldUpdateRevision(int updateRevision) { mWorldUpdateRevision = updateRevision; }
@ -81,13 +89,18 @@ namespace Terrain
{
public:
ViewDataMap()
: mReuseDistance(150) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused.
// this value also serves as a threshold for when a newly loaded LOD gets unloaded again so that if you hover around an LOD transition point the LODs won't keep loading and unloading all the time.
: mReuseDistance(
150) // large value should be safe because the visibility of each node is still updated individually for
// each camera even if the base view was reused. this value also serves as a threshold for when a
// newly loaded LOD gets unloaded again so that if you hover around an LOD transition point the
// LODs won't keep loading and unloading all the time.
, mExpiryDelay(1.f)
, mWorldUpdateRevision(0)
{}
{
}
ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i &activeGrid, bool& needsUpdate);
ViewData* getViewData(
osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i& activeGrid, bool& needsUpdate);
ViewData* createOrReuseView();
ViewData* createIndependentView() const;

View file

@ -1,148 +1,152 @@
#include "world.hpp"
#include <osg/Group>
#include <osg/Camera>
#include <osg/Group>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include "storage.hpp"
#include "texturemanager.hpp"
#include "chunkmanager.hpp"
#include "compositemaprenderer.hpp"
#include "heightcull.hpp"
#include "storage.hpp"
#include "texturemanager.hpp"
namespace Terrain
{
World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask)
: mStorage(storage)
, mParent(parent)
, mResourceSystem(resourceSystem)
, mBorderVisible(false)
, mHeightCullCallback(new HeightCullCallback)
{
mTerrainRoot = new osg::Group;
mTerrainRoot->setNodeMask(nodeMask);
mTerrainRoot->setName("Terrain Root");
osg::ref_ptr<osg::Camera> compositeCam = new osg::Camera;
compositeCam->setRenderOrder(osg::Camera::PRE_RENDER, -1);
compositeCam->setProjectionMatrix(osg::Matrix::identity());
compositeCam->setViewMatrix(osg::Matrix::identity());
compositeCam->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
compositeCam->setClearMask(0);
compositeCam->setNodeMask(preCompileMask);
mCompositeMapCamera = compositeCam;
compileRoot->addChild(compositeCam);
mCompositeMapRenderer = new CompositeMapRenderer;
compositeCam->addChild(mCompositeMapRenderer);
mParent->addChild(mTerrainRoot);
mTextureManager = std::make_unique<TextureManager>(mResourceSystem->getSceneManager());
mChunkManager = std::make_unique<ChunkManager>(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer);
mChunkManager->setNodeMask(nodeMask);
mCellBorder = std::make_unique<CellBorder>(this,mTerrainRoot.get(),borderMask,mResourceSystem->getSceneManager());
mResourceSystem->addResourceManager(mChunkManager.get());
mResourceSystem->addResourceManager(mTextureManager.get());
}
World::World(osg::Group* parent, Storage* storage, unsigned int nodeMask)
: mStorage(storage)
, mParent(parent)
, mCompositeMapCamera(nullptr)
, mCompositeMapRenderer(nullptr)
, mResourceSystem(nullptr)
, mTextureManager(nullptr)
, mChunkManager(nullptr)
, mCellBorder(nullptr)
, mBorderVisible(false)
, mHeightCullCallback(nullptr)
{
mTerrainRoot = new osg::Group;
mTerrainRoot->setNodeMask(nodeMask);
mParent->addChild(mTerrainRoot);
}
World::~World()
{
if (mResourceSystem && mChunkManager)
mResourceSystem->removeResourceManager(mChunkManager.get());
if (mResourceSystem && mTextureManager)
mResourceSystem->removeResourceManager(mTextureManager.get());
mParent->removeChild(mTerrainRoot);
if (mCompositeMapCamera && mCompositeMapRenderer)
World::World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem,
Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask)
: mStorage(storage)
, mParent(parent)
, mResourceSystem(resourceSystem)
, mBorderVisible(false)
, mHeightCullCallback(new HeightCullCallback)
{
mCompositeMapCamera->removeChild(mCompositeMapRenderer);
mCompositeMapCamera->getParent(0)->removeChild(mCompositeMapCamera);
mTerrainRoot = new osg::Group;
mTerrainRoot->setNodeMask(nodeMask);
mTerrainRoot->setName("Terrain Root");
osg::ref_ptr<osg::Camera> compositeCam = new osg::Camera;
compositeCam->setRenderOrder(osg::Camera::PRE_RENDER, -1);
compositeCam->setProjectionMatrix(osg::Matrix::identity());
compositeCam->setViewMatrix(osg::Matrix::identity());
compositeCam->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
compositeCam->setClearMask(0);
compositeCam->setNodeMask(preCompileMask);
mCompositeMapCamera = compositeCam;
compileRoot->addChild(compositeCam);
mCompositeMapRenderer = new CompositeMapRenderer;
compositeCam->addChild(mCompositeMapRenderer);
mParent->addChild(mTerrainRoot);
mTextureManager = std::make_unique<TextureManager>(mResourceSystem->getSceneManager());
mChunkManager = std::make_unique<ChunkManager>(
mStorage, mResourceSystem->getSceneManager(), mTextureManager.get(), mCompositeMapRenderer);
mChunkManager->setNodeMask(nodeMask);
mCellBorder
= std::make_unique<CellBorder>(this, mTerrainRoot.get(), borderMask, mResourceSystem->getSceneManager());
mResourceSystem->addResourceManager(mChunkManager.get());
mResourceSystem->addResourceManager(mTextureManager.get());
}
}
void World::setBordersVisible(bool visible)
{
mBorderVisible = visible;
if (visible)
World::World(osg::Group* parent, Storage* storage, unsigned int nodeMask)
: mStorage(storage)
, mParent(parent)
, mCompositeMapCamera(nullptr)
, mCompositeMapRenderer(nullptr)
, mResourceSystem(nullptr)
, mTextureManager(nullptr)
, mChunkManager(nullptr)
, mCellBorder(nullptr)
, mBorderVisible(false)
, mHeightCullCallback(nullptr)
{
for (std::set<std::pair<int,int>>::iterator it = mLoadedCells.begin(); it != mLoadedCells.end(); ++it)
mCellBorder->createCellBorderGeometry(it->first,it->second);
mTerrainRoot = new osg::Group;
mTerrainRoot->setNodeMask(nodeMask);
mParent->addChild(mTerrainRoot);
}
else
mCellBorder->destroyCellBorderGeometry();
}
void World::loadCell(int x, int y)
{
if (mBorderVisible)
mCellBorder->createCellBorderGeometry(x,y);
World::~World()
{
if (mResourceSystem && mChunkManager)
mResourceSystem->removeResourceManager(mChunkManager.get());
if (mResourceSystem && mTextureManager)
mResourceSystem->removeResourceManager(mTextureManager.get());
mLoadedCells.insert(std::pair<int,int>(x,y));
}
mParent->removeChild(mTerrainRoot);
void World::unloadCell(int x, int y)
{
if (mBorderVisible)
mCellBorder->destroyCellBorderGeometry(x,y);
if (mCompositeMapCamera && mCompositeMapRenderer)
{
mCompositeMapCamera->removeChild(mCompositeMapRenderer);
mCompositeMapCamera->getParent(0)->removeChild(mCompositeMapCamera);
}
}
mLoadedCells.erase(std::pair<int,int>(x,y));
}
void World::setBordersVisible(bool visible)
{
mBorderVisible = visible;
void World::setTargetFrameRate(float rate)
{
mCompositeMapRenderer->setTargetFrameRate(rate);
}
if (visible)
{
for (std::set<std::pair<int, int>>::iterator it = mLoadedCells.begin(); it != mLoadedCells.end(); ++it)
mCellBorder->createCellBorderGeometry(it->first, it->second);
}
else
mCellBorder->destroyCellBorderGeometry();
}
float World::getHeightAt(const osg::Vec3f &worldPos)
{
return mStorage->getHeightAt(worldPos);
}
void World::loadCell(int x, int y)
{
if (mBorderVisible)
mCellBorder->createCellBorderGeometry(x, y);
void World::updateTextureFiltering()
{
if (mTextureManager)
mTextureManager->updateTextureFiltering();
}
mLoadedCells.insert(std::pair<int, int>(x, y));
}
void World::clearAssociatedCaches()
{
if (mChunkManager)
mChunkManager->clearCache();
}
void World::unloadCell(int x, int y)
{
if (mBorderVisible)
mCellBorder->destroyCellBorderGeometry(x, y);
osg::Callback* World::getHeightCullCallback(float highz, unsigned int mask)
{
if (!mHeightCullCallback) return nullptr;
mLoadedCells.erase(std::pair<int, int>(x, y));
}
mHeightCullCallback->setHighZ(highz);
mHeightCullCallback->setCullMask(mask);
return mHeightCullCallback;
}
void World::setTargetFrameRate(float rate)
{
mCompositeMapRenderer->setTargetFrameRate(rate);
}
float World::getHeightAt(const osg::Vec3f& worldPos)
{
return mStorage->getHeightAt(worldPos);
}
void World::updateTextureFiltering()
{
if (mTextureManager)
mTextureManager->updateTextureFiltering();
}
void World::clearAssociatedCaches()
{
if (mChunkManager)
mChunkManager->clearCache();
}
osg::Callback* World::getHeightCullCallback(float highz, unsigned int mask)
{
if (!mHeightCullCallback)
return nullptr;
mHeightCullCallback->setHighZ(highz);
mHeightCullCallback->setCullMask(mask);
return mHeightCullCallback;
}
}

View file

@ -1,9 +1,9 @@
#ifndef COMPONENTS_TERRAIN_WORLD_H
#define COMPONENTS_TERRAIN_WORLD_H
#include <osg/ref_ptr>
#include <osg/Referenced>
#include <osg/Vec3f>
#include <osg/ref_ptr>
#include <memory>
#include <set>
@ -37,7 +37,7 @@ namespace Terrain
class CompositeMapRenderer;
class View;
class HeightCullCallback;
/**
* @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed
* is up to the implementation.
@ -49,7 +49,8 @@ namespace Terrain
/// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..)
/// @param nodeMask mask for the terrain root
/// @param preCompileMask mask for pre compiling textures
World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask);
World(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage,
unsigned int nodeMask, unsigned int preCompileMask, unsigned int borderMask);
World(osg::Group* parent, Storage* storage, unsigned int nodeMask);
virtual ~World();
@ -60,7 +61,7 @@ namespace Terrain
/// @note Thread safe.
void updateTextureFiltering();
float getHeightAt (const osg::Vec3f& worldPos);
float getHeightAt(const osg::Vec3f& worldPos);
/// Clears the cached land and landtexture data.
/// @note Thread safe.
@ -89,7 +90,10 @@ namespace Terrain
/// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads.
virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic<bool>& abort, Loading::Reporter& reporter) {}
virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i& cellgrid,
std::atomic<bool>& abort, Loading::Reporter& reporter)
{
}
virtual void rebuildViews() {}
@ -101,7 +105,7 @@ namespace Terrain
osg::Callback* getHeightCullCallback(float highz, unsigned int mask);
void setActiveGrid(const osg::Vec4i &grid) { mActiveGrid = grid; }
void setActiveGrid(const osg::Vec4i& grid) { mActiveGrid = grid; }
protected:
Storage* mStorage;
@ -121,7 +125,7 @@ namespace Terrain
bool mBorderVisible;
std::set<std::pair<int,int>> mLoadedCells;
std::set<std::pair<int, int>> mLoadedCells;
osg::ref_ptr<HeightCullCallback> mHeightCullCallback;
osg::Vec4i mActiveGrid;