Factor out terrain chunk loading/caching into a new resource manager

pull/185/head
scrawl 8 years ago
parent 274690f790
commit 35d53acc65

@ -4,6 +4,7 @@
#include <string>
#include <map>
#include <memory>
#include <vector>
#include <boost/shared_ptr.hpp>

@ -914,8 +914,6 @@ namespace MWRender
if (stats->collectStats("resource"))
{
stats->setAttribute(frameNumber, "UnrefQueue", mUnrefQueue->getNumItems());
mTerrain->reportStats(frameNumber, stats);
}
}

@ -168,24 +168,20 @@ namespace MWWorld
class UpdateCacheItem : public SceneUtil::WorkItem
{
public:
UpdateCacheItem(Resource::ResourceSystem* resourceSystem, Terrain::World* terrain, double referenceTime)
UpdateCacheItem(Resource::ResourceSystem* resourceSystem, double referenceTime)
: mReferenceTime(referenceTime)
, mResourceSystem(resourceSystem)
, mTerrain(terrain)
{
}
virtual void doWork()
{
mResourceSystem->updateCache(mReferenceTime);
mTerrain->updateCache(mReferenceTime);
}
private:
double mReferenceTime;
Resource::ResourceSystem* mResourceSystem;
Terrain::World* mTerrain;
};
CellPreloader::CellPreloader(Resource::ResourceSystem* resourceSystem, Resource::BulletShapeManager* bulletShapeManager, Terrain::World* terrain, MWRender::LandManager* landManager)
@ -313,7 +309,7 @@ namespace MWWorld
if (timestamp - mLastResourceCacheUpdate > 1.0)
{
// the resource cache is cleared from the worker thread so that we're not holding up the main thread with delete operations
mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, mTerrain, timestamp), true);
mWorkQueue->addWorkItem(new UpdateCacheItem(mResourceSystem, timestamp), true);
mLastResourceCacheUpdate = timestamp;
}
}

@ -114,7 +114,7 @@ add_component_dir (translation
)
add_component_dir (terrain
storage world buffercache defs terraingrid material terraindrawable texturemanager
storage world buffercache defs terraingrid material terraindrawable texturemanager chunkmanager
)
add_component_dir (loadinglistener

@ -612,4 +612,9 @@ namespace ESMTerrain
return ESM::Land::LAND_SIZE;
}
int Storage::getBlendmapScale(float chunkSize)
{
return ESM::Land::LAND_TEXTURE_SIZE*chunkSize;
}
}

@ -99,6 +99,8 @@ namespace ESMTerrain
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
virtual int getCellVertices();
virtual int getBlendmapScale(float chunkSize);
private:
const VFS::Manager* mVFS;

@ -621,6 +621,11 @@ namespace Resource
mIncrementalCompileOperation = ico;
}
osgUtil::IncrementalCompileOperation *SceneManager::getIncrementalCompileOperation()
{
return mIncrementalCompileOperation.get();
}
Resource::ImageManager* SceneManager::getImageManager()
{
return mImageManager;

@ -121,6 +121,8 @@ namespace Resource
/// Set up an IncrementalCompileOperation for background compiling of loaded scenes.
void setIncrementalCompileOperation(osgUtil::IncrementalCompileOperation* ico);
osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation();
Resource::ImageManager* getImageManager();
/// @param mask The node mask to apply to loaded particle system nodes.

@ -259,7 +259,7 @@ void StatsHandler::setUpScene(osgViewer::ViewerBase *viewer)
_resourceStatsChildNum = _switch->getNumChildren();
_switch->addChild(group, false);
const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Cell", "Terrain Texture", "Land", "", "UnrefQueue"};
const char* statNames[] = {"Compiling", "WorkQueue", "WorkThread", "", "Texture", "StateSet", "Node", "Node Instance", "Shape", "Shape Instance", "Image", "Nif", "Keyframe", "Terrain Chunk", "Terrain Texture", "Land", "", "UnrefQueue"};
int numLines = sizeof(statNames) / sizeof(statNames[0]);

@ -0,0 +1,180 @@
#include "chunkmanager.hpp"
#include <sstream>
#include <osg/Texture2D>
#include <osgUtil/IncrementalCompileOperation>
#include <components/resource/objectcache.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include "terraindrawable.hpp"
#include "material.hpp"
#include "storage.hpp"
#include "texturemanager.hpp"
namespace
{
class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback
{
public:
StaticBoundingBoxCallback(const osg::BoundingBox& bounds)
: mBoundingBox(bounds)
{
}
virtual osg::BoundingBox computeBound(const osg::Drawable&) const
{
return mBoundingBox;
}
private:
osg::BoundingBox mBoundingBox;
};
}
namespace Terrain
{
ChunkManager::ChunkManager(Storage *storage, Resource::SceneManager *sceneMgr, TextureManager* textureManager)
: ResourceManager(NULL)
, mStorage(storage)
, mSceneManager(sceneMgr)
, mTextureManager(textureManager)
, mShaderManager(NULL)
{
}
osg::ref_ptr<osg::Node> ChunkManager::getChunk(float size, const osg::Vec2f &center)
{
std::ostringstream stream;
stream << size << " " << center.x() << " " << center.y();
std::string id = stream.str();
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(id);
if (obj)
return obj->asNode();
else
{
osg::ref_ptr<osg::Node> node = createChunk(size, center);
mCache->addEntryToObjectCache(id, node.get());
return node;
}
}
void ChunkManager::setShaderManager(Shader::ShaderManager *shaderManager)
{
mShaderManager = shaderManager;
}
void ChunkManager::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
stats->setAttribute(frameNumber, "Terrain Chunk", mCache->getCacheSize());
}
osg::ref_ptr<osg::Node> ChunkManager::createChunk(float chunkSize, const osg::Vec2f &chunkCenter)
{
float minH, maxH;
if (!mStorage->getMinMaxHeights(chunkSize, chunkCenter, minH, maxH))
return NULL; // no terrain defined
osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize();
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> transform (new SceneUtil::PositionAttitudeTransform);
transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f));
osg::ref_ptr<osg::Vec3Array> positions (new osg::Vec3Array);
osg::ref_ptr<osg::Vec3Array> normals (new osg::Vec3Array);
osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array);
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
positions->setVertexBufferObject(vbo);
normals->setVertexBufferObject(vbo);
colors->setVertexBufferObject(vbo);
unsigned int lod = 0;
mStorage->fillVertexBuffers(lod, chunkSize, chunkCenter, positions, normals, colors);
osg::ref_ptr<TerrainDrawable> geometry (new TerrainDrawable);
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);
unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize / (1 << lod) + 1;
geometry->addPrimitiveSet(mBufferCache.getIndexBuffer(numVerts, 0));
// we already know the bounding box, so no need to let OSG compute it.
osg::Vec3f min(-0.5f*mStorage->getCellWorldSize()*chunkSize,
-0.5f*mStorage->getCellWorldSize()*chunkSize,
minH);
osg::Vec3f max (0.5f*mStorage->getCellWorldSize()*chunkSize,
0.5f*mStorage->getCellWorldSize()*chunkSize,
maxH);
osg::BoundingBox bounds(min, max);
geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds));
std::vector<LayerInfo> layerList;
std::vector<osg::ref_ptr<osg::Image> > blendmaps;
mStorage->getBlendmaps(chunkSize, chunkCenter, false, 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 (!it->mNormalMap.empty())
textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap);
if (it->requiresShaders())
useShaders = true;
layers.push_back(textureLayer);
}
}
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);
}
// use texture coordinates for both texture units, the layer texture and blend texture
for (unsigned int i=0; i<2; ++i)
geometry->setTexCoordArray(i, mBufferCache.getUVBuffer(numVerts));
float blendmapScale = mStorage->getBlendmapScale(chunkSize);
geometry->setPasses(createPasses(mShaderManager ? useShaders : false, mSceneManager->getForcePerPixelLighting(),
mSceneManager->getClampLighting(), mShaderManager, layers, blendmapTextures, blendmapScale, blendmapScale));
transform->addChild(geometry);
if (mSceneManager->getIncrementalCompileOperation())
{
mSceneManager->getIncrementalCompileOperation()->add(geometry);
}
return transform;
}
}

@ -0,0 +1,59 @@
#ifndef OPENMW_COMPONENTS_TERRAIN_CHUNKMANAGER_H
#define OPENMW_COMPONENTS_TERRAIN_CHUNKMANAGER_H
#include <components/resource/resourcemanager.hpp>
#include "buffercache.hpp"
namespace osg
{
class Geometry;
}
namespace SceneUtil
{
class PositionAttitudeTransform;
}
namespace Resource
{
class SceneManager;
}
namespace Shader
{
class ShaderManager;
}
namespace Terrain
{
class TextureManager;
class Storage;
/// @brief Handles loading and caching of terrain chunks
class ChunkManager : public Resource::ResourceManager
{
public:
ChunkManager(Storage* storage, Resource::SceneManager* sceneMgr, TextureManager* textureManager);
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center);
// Optional
void setShaderManager(Shader::ShaderManager* shaderManager);
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats);
private:
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center);
Terrain::Storage* mStorage;
Resource::SceneManager* mSceneManager;
TextureManager* mTextureManager;
Shader::ShaderManager* mShaderManager;
BufferCache mBufferCache;
};
}
#endif

@ -78,6 +78,8 @@ namespace Terrain
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
virtual int getCellVertices() = 0;
virtual int getBlendmapScale(float chunkSize) = 0;
};
}

@ -3,47 +3,12 @@
#include <memory>
#include <osg/Material>
#include <osg/Geometry>
#include <osg/Group>
#include <osgUtil/IncrementalCompileOperation>
#include <OpenThreads/ScopedLock>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/imagemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/sceneutil/positionattitudetransform.hpp>
#include <components/sceneutil/unrefqueue.hpp>
#include <components/esm/loadland.hpp>
#include "material.hpp"
#include "storage.hpp"
#include "terraindrawable.hpp"
#include "texturemanager.hpp"
namespace
{
class StaticBoundingBoxCallback : public osg::Drawable::ComputeBoundingBoxCallback
{
public:
StaticBoundingBoxCallback(const osg::BoundingBox& bounds)
: mBoundingBox(bounds)
{
}
virtual osg::BoundingBox computeBound(const osg::Drawable&) const
{
return mBoundingBox;
}
private:
osg::BoundingBox mBoundingBox;
};
}
#include "chunkmanager.hpp"
namespace Terrain
{
@ -57,6 +22,8 @@ TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceS
osg::ref_ptr<osg::Material> material (new osg::Material);
material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);
mTerrainRoot->getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON);
mChunkManager->setShaderManager(mShaderManager);
}
TerrainGrid::~TerrainGrid()
@ -69,17 +36,8 @@ TerrainGrid::~TerrainGrid()
osg::ref_ptr<osg::Node> TerrainGrid::cacheCell(int x, int y)
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
Grid::iterator found = mGridCache.find(std::make_pair(x,y));
if (found != mGridCache.end())
return found->second;
}
osg::ref_ptr<osg::Node> node = buildTerrain(NULL, 1.f, osg::Vec2f(x+0.5, y+0.5));
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
mGridCache.insert(std::make_pair(std::make_pair(x,y), node));
return node;
osg::Vec2f center(x+0.5f, y+0.5f);
return buildTerrain(NULL, 1.f, center);
}
osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter)
@ -100,106 +58,13 @@ osg::ref_ptr<osg::Node> TerrainGrid::buildTerrain (osg::Group* parent, float chu
}
else
{
float minH, maxH;
if (!mStorage->getMinMaxHeights(chunkSize, chunkCenter, minH, maxH))
return NULL; // no terrain defined
osg::Vec2f worldCenter = chunkCenter*mStorage->getCellWorldSize();
osg::ref_ptr<SceneUtil::PositionAttitudeTransform> transform (new SceneUtil::PositionAttitudeTransform);
transform->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f));
osg::ref_ptr<osg::Node> node = mChunkManager->getChunk(chunkSize, chunkCenter);
if (!node)
return NULL;
if (parent)
parent->addChild(transform);
osg::ref_ptr<osg::Vec3Array> positions (new osg::Vec3Array);
osg::ref_ptr<osg::Vec3Array> normals (new osg::Vec3Array);
osg::ref_ptr<osg::Vec4Array> colors (new osg::Vec4Array);
osg::ref_ptr<osg::VertexBufferObject> vbo (new osg::VertexBufferObject);
positions->setVertexBufferObject(vbo);
normals->setVertexBufferObject(vbo);
colors->setVertexBufferObject(vbo);
mStorage->fillVertexBuffers(0, chunkSize, chunkCenter, positions, normals, colors);
osg::ref_ptr<TerrainDrawable> geometry (new TerrainDrawable);
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);
unsigned int numVerts = (mStorage->getCellVertices()-1) * chunkSize + 1;
geometry->addPrimitiveSet(mCache.getIndexBuffer(numVerts, 0));
// we already know the bounding box, so no need to let OSG compute it.
osg::Vec3f min(-0.5f*mStorage->getCellWorldSize()*chunkSize,
-0.5f*mStorage->getCellWorldSize()*chunkSize,
minH);
osg::Vec3f max (0.5f*mStorage->getCellWorldSize()*chunkSize,
0.5f*mStorage->getCellWorldSize()*chunkSize,
maxH);
osg::BoundingBox bounds(min, max);
geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds));
std::vector<LayerInfo> layerList;
std::vector<osg::ref_ptr<osg::Image> > blendmaps;
mStorage->getBlendmaps(chunkSize, chunkCenter, false, blendmaps, layerList);
bool useShaders = mResourceSystem->getSceneManager()->getForceShaders();
if (!mResourceSystem->getSceneManager()->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;
parent->addChild(node);
textureLayer.mDiffuseMap = mTextureManager->getTexture(it->mDiffuseMap);
if (!it->mNormalMap.empty())
{
textureLayer.mNormalMap = mTextureManager->getTexture(it->mNormalMap);
}
if (it->requiresShaders())
useShaders = true;
layers.push_back(textureLayer);
}
}
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);
}
// use texture coordinates for both texture units, the layer texture and blend texture
for (unsigned int i=0; i<2; ++i)
geometry->setTexCoordArray(i, mCache.getUVBuffer(numVerts));
float blendmapScale = ESM::Land::LAND_TEXTURE_SIZE*chunkSize;
geometry->setPasses(createPasses(mShaderManager ? useShaders : false, mResourceSystem->getSceneManager()->getForcePerPixelLighting(),
mResourceSystem->getSceneManager()->getClampLighting(), mShaderManager, layers, blendmapTextures, blendmapScale, blendmapScale));
transform->addChild(geometry);
if (mIncrementalCompileOperation)
{
mIncrementalCompileOperation->add(geometry);
}
return transform;
return node;
}
}
@ -208,27 +73,10 @@ void TerrainGrid::loadCell(int x, int y)
if (mGrid.find(std::make_pair(x, y)) != mGrid.end())
return; // already loaded
// try to get it from the cache
osg::ref_ptr<osg::Node> terrainNode;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
Grid::const_iterator found = mGridCache.find(std::make_pair(x,y));
if (found != mGridCache.end())
{
terrainNode = found->second;
if (!terrainNode)
return; // no terrain defined
}
}
// didn't find in cache, build it
osg::Vec2f center(x+0.5f, y+0.5f);
osg::ref_ptr<osg::Node> terrainNode = buildTerrain(NULL, 1.f, center);
if (!terrainNode)
{
osg::Vec2f center(x+0.5f, y+0.5f);
terrainNode = buildTerrain(NULL, 1.f, center);
if (!terrainNode)
return; // no terrain defined
}
return; // no terrain defined
mTerrainRoot->addChild(terrainNode);
@ -250,31 +98,9 @@ void TerrainGrid::unloadCell(int x, int y)
mGrid.erase(it);
}
void TerrainGrid::updateCache(double referenceTime)
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
for (Grid::iterator it = mGridCache.begin(); it != mGridCache.end();)
{
if (it->second->referenceCount() <= 1)
mGridCache.erase(it++);
else
++it;
}
}
}
void TerrainGrid::updateTextureFiltering()
{
mTextureManager->updateTextureFiltering();
}
void TerrainGrid::reportStats(unsigned int frameNumber, osg::Stats *stats)
{
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(mGridCacheMutex);
stats->setAttribute(frameNumber, "Terrain Cell", mGridCache.size());
}
}
}

@ -1,6 +1,8 @@
#ifndef COMPONENTS_TERRAIN_TERRAINGRID_H
#define COMPONENTS_TERRAIN_TERRAINGRID_H
#include <map>
#include <osg/Vec2f>
#include "world.hpp"
@ -41,16 +43,10 @@ namespace Terrain
/// @note Not thread safe.
virtual void unloadCell(int x, int y);
/// Clear cached objects that are no longer referenced
/// @note Thread safe.
void updateCache(double referenceTime);
/// Apply the scene manager's texture filtering settings to all cached textures.
/// @note Thread safe.
void updateTextureFiltering();
void reportStats(unsigned int frameNumber, osg::Stats *stats);
private:
osg::ref_ptr<osg::Node> buildTerrain (osg::Group* parent, float chunkSize, const osg::Vec2f& chunkCenter);
@ -60,11 +56,6 @@ namespace Terrain
typedef std::map<std::pair<int, int>, osg::ref_ptr<osg::Node> > Grid;
Grid mGrid;
Grid mGridCache;
OpenThreads::Mutex mGridCacheMutex;
BufferCache mCache;
osg::ref_ptr<SceneUtil::UnrefQueue> mUnrefQueue;
Shader::ShaderManager* mShaderManager;

@ -7,6 +7,7 @@
#include "storage.hpp"
#include "texturemanager.hpp"
#include "chunkmanager.hpp"
namespace Terrain
{
@ -26,13 +27,16 @@ World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUt
mParent->addChild(mTerrainRoot);
mTextureManager.reset(new TextureManager(mResourceSystem->getSceneManager()));
mResourceSystem->addResourceManager(mTextureManager.get());
mChunkManager.reset(new ChunkManager(mStorage, mResourceSystem->getSceneManager(), mTextureManager.get()));
mResourceSystem->addResourceManager(mChunkManager.get());
}
World::~World()
{
mResourceSystem->removeResourceManager(mTextureManager.get());
mResourceSystem->removeResourceManager(mChunkManager.get());
mParent->removeChild(mTerrainRoot);

@ -2,16 +2,17 @@
#define COMPONENTS_TERRAIN_WORLD_H
#include <osg/ref_ptr>
#include <osg/Vec3f>
#include <memory>
#include "defs.hpp"
#include "buffercache.hpp"
namespace osg
{
class Group;
class Stats;
class Node;
}
namespace osgUtil
@ -29,6 +30,7 @@ namespace Terrain
class Storage;
class TextureManager;
class ChunkManager;
/**
* @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed
@ -46,10 +48,6 @@ namespace Terrain
virtual void updateTextureFiltering() {}
virtual void updateCache(double referenceTime) {}
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}
float getHeightAt (const osg::Vec3f& worldPos);
virtual osg::ref_ptr<osg::Node> cacheCell(int x, int y) {return NULL;}
@ -71,6 +69,7 @@ namespace Terrain
osg::ref_ptr<osgUtil::IncrementalCompileOperation> mIncrementalCompileOperation;
std::auto_ptr<TextureManager> mTextureManager;
std::auto_ptr<ChunkManager> mChunkManager;
};
}

Loading…
Cancel
Save