diff --git a/CMakeLists.txt b/CMakeLists.txt index 43d40c2ef..d13437214 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,7 +174,7 @@ if (${OGRE_VERSION} VERSION_LESS "1.9") message(FATAL_ERROR "OpenMW requires Ogre 1.9 or later, please install the latest stable version from http://ogre3d.org") endif() -find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgGA osgAnimation osgParticle osgQt osgUtil) +find_package(OpenSceneGraph 3.2.0 REQUIRED osgDB osgViewer osgGA osgAnimation osgParticle osgQt osgUtil osgFX) include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS}) find_package(MyGUI REQUIRED) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 064e96150..9772a68fa 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -488,6 +488,11 @@ Resource::ResourceSystem* CSMWorld::Data::getResourceSystem() return &mResourceSystem; } +const Resource::ResourceSystem* CSMWorld::Data::getResourceSystem() const +{ + return &mResourceSystem; +} + const CSMWorld::IdCollection& CSMWorld::Data::getGlobals() const { return mGlobals; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 93cd46d86..a41946687 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -140,6 +140,8 @@ namespace CSMWorld Resource::ResourceSystem* getResourceSystem(); + const Resource::ResourceSystem* getResourceSystem() const; + const IdCollection& getGlobals() const; IdCollection& getGlobals(); diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index 343d0d2e6..063413248 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -64,7 +64,6 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st addObjects (0, rows-1); - /* const CSMWorld::IdCollection& land = mData.getLand(); int landIndex = land.searchId(mId); if (landIndex != -1) @@ -72,27 +71,18 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, osg::Group* rootNode, const std::st const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get(); if(esmLand && esmLand->mDataTypes&ESM::Land::DATA_VHGT) { - mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true, - Terrain::Align_XY)); + mTerrain.reset(new Terrain::TerrainGrid(mCellNode, data.getResourceSystem(), NULL, new TerrainStorage(mData), Element_Terrain<<1)); mTerrain->loadCell(esmLand->mX, esmLand->mY); - //float verts = ESM::Land::LAND_SIZE; - //float worldsize = ESM::Land::REAL_SIZE; mX = esmLand->mX; mY = esmLand->mY; - //mPhysics->addHeightField(sceneManager, - // esmLand->mLandData->mHeights, mX, mY, 0, worldsize / (verts-1), verts); } } - */ } CSVRender::Cell::~Cell() { - //if (mTerrain.get()) - // mPhysics->removeHeightField(mSceneMgr, mX, mY); - for (std::map::iterator iter (mObjects.begin()); iter!=mObjects.end(); ++iter) delete iter->second; @@ -221,11 +211,3 @@ bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int return addObjects (start, end); } - -float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const -{ - if(mTerrain.get() != NULL) - return mTerrain->getHeightAt(pos); - else - return -std::numeric_limits::max(); -} diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp index 259ab1779..f4272b887 100644 --- a/apps/opencs/view/render/cell.hpp +++ b/apps/opencs/view/render/cell.hpp @@ -75,8 +75,6 @@ namespace CSVRender /// \return Did this call result in a modification of the visual representation of /// this cell? bool referenceAdded (const QModelIndex& parent, int start, int end); - - float getTerrainHeightAt(const Ogre::Vector3 &pos) const; }; } diff --git a/apps/opencs/view/render/terrainstorage.cpp b/apps/opencs/view/render/terrainstorage.cpp index a14eea5dd..fe302cef1 100644 --- a/apps/opencs/view/render/terrainstorage.cpp +++ b/apps/opencs/view/render/terrainstorage.cpp @@ -4,7 +4,8 @@ namespace CSVRender { TerrainStorage::TerrainStorage(const CSMWorld::Data &data) - : mData(data) + : ESMTerrain::Storage(data.getResourceSystem()->getVFS()) + , mData(data) { } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index bb71cb6c9..ef0c4e135 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -22,8 +22,8 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender actors objects renderingmanager animation rotatecontroller sky npcanimation vismask creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation - bulletdebugdraw globalmap characterpreview camera localmap water -# occlusionquery shadows ripplesimulation terrainstorage + bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage +# occlusionquery shadows ripplesimulation ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwrender/localmap.cpp b/apps/openmw/mwrender/localmap.cpp index 3003e4736..16957a585 100644 --- a/apps/openmw/mwrender/localmap.cpp +++ b/apps/openmw/mwrender/localmap.cpp @@ -157,7 +157,7 @@ osg::ref_ptr LocalMap::createOrthographicCamera(float x, float y, f camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); camera->setRenderOrder(osg::Camera::PRE_RENDER); - camera->setCullMask(MWRender::Mask_Scene|MWRender::Mask_Water); + camera->setCullMask(Mask_Scene|Mask_Water|Mask_Terrain); camera->setNodeMask(Mask_RenderToTexture); osg::ref_ptr stateset = new osg::StateSet; @@ -309,7 +309,7 @@ void LocalMap::requestExteriorMap(MWWorld::CellStore* cell) void LocalMap::requestInteriorMap(MWWorld::CellStore* cell) { osg::ComputeBoundsVisitor computeBoundsVisitor; - computeBoundsVisitor.setTraversalMask(Mask_Scene); + computeBoundsVisitor.setTraversalMask(Mask_Scene|Mask_Terrain); mSceneRoot->accept(computeBoundsVisitor); osg::BoundingBox bounds = computeBoundsVisitor.getBoundingBox(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index a708bbe88..6205c3ad8 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include #include "sky.hpp" @@ -35,6 +37,7 @@ #include "pathgrid.hpp" #include "camera.hpp" #include "water.hpp" +#include "terrainstorage.hpp" namespace MWRender { @@ -132,7 +135,10 @@ namespace MWRender mEffectManager.reset(new EffectManager(lightRoot, mResourceSystem)); - mWater.reset(new Water(lightRoot, mResourceSystem)); + mWater.reset(new Water(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation())); + + mTerrain.reset(new Terrain::TerrainGrid(lightRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), + new TerrainStorage(mResourceSystem->getVFS(), false), Mask_Terrain)); mCamera.reset(new Camera(mViewer->getCamera())); @@ -236,12 +242,18 @@ namespace MWRender mPathgrid->addCell(store); mWater->changeCell(store); + + if (store->getCell()->isExterior()) + mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY()); } void RenderingManager::removeCell(const MWWorld::CellStore *store) { mPathgrid->removeCell(store); mObjects->removeCell(store); + + if (store->getCell()->isExterior()) + mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY()); } void RenderingManager::setSkyEnabled(bool enabled) @@ -482,7 +494,6 @@ namespace MWRender void RenderingManager::clear() { - //mLocalMap->clear(); notifyWorldSpaceChanged(); } @@ -595,6 +606,11 @@ namespace MWRender return mNearClip; } + float RenderingManager::getTerrainHeightAt(const osg::Vec3f &pos) + { + return mTerrain->getHeightAt(pos); + } + bool RenderingManager::vanityRotateCamera(const float *rot) { if(!mCamera->isVanityOrPreviewModeEnabled()) diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index d90a75c86..c22d3c7bf 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -32,6 +32,11 @@ namespace ESM struct Cell; } +namespace Terrain +{ + class World; +} + namespace MWRender { @@ -119,6 +124,8 @@ namespace MWRender float getNearClipDistance() const; + float getTerrainHeightAt(const osg::Vec3f& pos); + // camera stuff bool vanityRotateCamera(const float *rot); void setCameraDistance(float dist, bool adjust, bool override); @@ -147,6 +154,7 @@ namespace MWRender std::auto_ptr mPathgrid; std::auto_ptr mObjects; std::auto_ptr mWater; + std::auto_ptr mTerrain; std::auto_ptr mSky; std::auto_ptr mEffectManager; std::auto_ptr mPlayerAnimation; diff --git a/apps/openmw/mwrender/terrainstorage.cpp b/apps/openmw/mwrender/terrainstorage.cpp index 8ad2ea321..269e7f99f 100644 --- a/apps/openmw/mwrender/terrainstorage.cpp +++ b/apps/openmw/mwrender/terrainstorage.cpp @@ -9,7 +9,8 @@ namespace MWRender { - TerrainStorage::TerrainStorage(bool preload) + TerrainStorage::TerrainStorage(const VFS::Manager* vfs, bool preload) + : ESMTerrain::Storage(vfs) { if (preload) { diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index e6f4a04ad..93531a552 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -16,7 +16,7 @@ namespace MWRender ///@param preload Preload all Land records at startup? If using the multithreaded terrain component, this /// should be set to "true" in order to avoid race conditions. - TerrainStorage(bool preload); + TerrainStorage(const VFS::Manager* vfs, bool preload); /// Get bounds of the whole terrain in cell units virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); diff --git a/apps/openmw/mwrender/vismask.hpp b/apps/openmw/mwrender/vismask.hpp index 3a0336f82..b794ac24a 100644 --- a/apps/openmw/mwrender/vismask.hpp +++ b/apps/openmw/mwrender/vismask.hpp @@ -16,13 +16,14 @@ namespace MWRender Mask_Player = (1<<4), Mask_Sky = (1<<5), Mask_Water = (1<<6), + Mask_Terrain = (1<<7), // top level masks - Mask_Scene = (1<<7), - Mask_GUI = (1<<8), + Mask_Scene = (1<<8), + Mask_GUI = (1<<9), // Set on cameras within the main scene graph - Mask_RenderToTexture = (1<<9) + Mask_RenderToTexture = (1<<10) // reserved: (1<<16) for SceneUtil::Mask_Lit }; diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index 2dd843e4e..c2149358b 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include #include @@ -105,7 +107,7 @@ namespace MWRender // -------------------------------------------------------------------------------------------------------------------------------- -Water::Water(osg::Group *parent, Resource::ResourceSystem *resourceSystem) +Water::Water(osg::Group *parent, Resource::ResourceSystem *resourceSystem, osgUtil::IncrementalCompileOperation *ico) : mParent(parent) , mResourceSystem(resourceSystem) , mEnabled(true) @@ -118,6 +120,9 @@ Water::Water(osg::Group *parent, Resource::ResourceSystem *resourceSystem) geode->addDrawable(waterGeom); geode->setNodeMask(Mask_Water); + if (ico) + ico->add(geode); + createWaterStateSet(mResourceSystem, geode); mWaterNode = new osg::PositionAttitudeTransform; diff --git a/apps/openmw/mwrender/water.hpp b/apps/openmw/mwrender/water.hpp index 45b4b38a6..d389392ba 100644 --- a/apps/openmw/mwrender/water.hpp +++ b/apps/openmw/mwrender/water.hpp @@ -11,6 +11,11 @@ namespace osg class PositionAttitudeTransform; } +namespace osgUtil +{ + class IncrementalCompileOperation; +} + namespace Resource { class ResourceSystem; @@ -27,6 +32,7 @@ namespace MWRender osg::ref_ptr mParent; osg::ref_ptr mWaterNode; Resource::ResourceSystem* mResourceSystem; + osg::ref_ptr mIncrementalCompileOperation; bool mEnabled; bool mToggled; @@ -36,7 +42,7 @@ namespace MWRender void updateVisible(); public: - Water(osg::Group* parent, Resource::ResourceSystem* resourceSystem); + Water(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico); ~Water(); void setEnabled(bool enabled); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 799f82015..e676977de 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1298,7 +1298,9 @@ namespace MWWorld return; } - float terrainHeight = -FLT_MAX;//mRendering->getTerrainHeightAt(Ogre::Vector3(pos.pos)); + float terrainHeight = -std::numeric_limits::max(); + if (isCellExterior()) + terrainHeight = mRendering->getTerrainHeightAt(pos.asVec3()); if (pos.pos[2] < terrainHeight) pos.pos[2] = terrainHeight; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 6a5b7f59a..51cb55aee 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -104,10 +104,8 @@ add_component_dir (translation translation ) -#add_definitions(-DTERRAIN_USE_SHADER=1) -add_definitions(-DTERRAIN_USE_SHADER=0) add_component_dir (terrain - quadtreenode chunk world defaultworld terraingrid storage material buffercache defs + storage world buffercache defs terraingrid material ) add_component_dir (loadinglistener diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index e510616af..edaeadb03 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -80,7 +80,7 @@ struct Land VNML mNormals[LAND_NUM_VERTS * 3]; uint16_t mTextures[LAND_NUM_TEXTURES]; - char mColours[3 * LAND_NUM_VERTS]; + unsigned char mColours[3 * LAND_NUM_VERTS]; int mDataTypes; // low-LOD heightmap (used for rendering the global map) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 5a69fd27a..0795a3ffc 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -1,34 +1,38 @@ #include "storage.hpp" -#include -#include -#include -#include -#include -#include -#include +#include + +#include +#include + +#include #include -#include #include +#include namespace ESMTerrain { - bool Storage::getMinMaxHeights(float size, const Ogre::Vector2 ¢er, float &min, float &max) + Storage::Storage(const VFS::Manager *vfs) + : mVFS(vfs) + { + } + + bool Storage::getMinMaxHeights(float size, const osg::Vec2f ¢er, float &min, float &max) { assert (size <= 1 && "Storage::getMinMaxHeights, chunk size should be <= 1 cell"); /// \todo investigate if min/max heights should be stored at load time in ESM::Land instead - Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); + osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); - assert(origin.x == (int) origin.x); - assert(origin.y == (int) origin.y); + assert(origin.x == (int) origin.x()); + assert(origin.y == (int) origin.y()); - int cellX = static_cast(origin.x); - int cellY = static_cast(origin.y); + int cellX = static_cast(origin.x()); + int cellY = static_cast(origin.y()); const ESM::Land* land = getLand(cellX, cellY); if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) @@ -50,7 +54,7 @@ namespace ESMTerrain return true; } - void Storage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row) + void Storage::fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row) { while (col >= ESM::Land::LAND_SIZE-1) { @@ -75,27 +79,27 @@ namespace ESMTerrain ESM::Land* land = getLand(cellX, cellY); if (land && land->mDataTypes&ESM::Land::DATA_VNML) { - normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalise(); + normal.x() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalize(); } else - normal = Ogre::Vector3(0,0,1); + normal = osg::Vec3f(0,0,1); } - void Storage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row) + void Storage::averageNormal(osg::Vec3f &normal, int cellX, int cellY, int col, int row) { - Ogre::Vector3 n1,n2,n3,n4; + osg::Vec3f n1,n2,n3,n4; fixNormal(n1, cellX, cellY, col+1, row); fixNormal(n2, cellX, cellY, col-1, row); fixNormal(n3, cellX, cellY, col, row+1); fixNormal(n4, cellX, cellY, col, row-1); normal = (n1+n2+n3+n4); - normal.normalise(); + normal.normalize(); } - void Storage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row) + void Storage::fixColour (osg::Vec4f& color, int cellX, int cellY, int col, int row) { if (col == ESM::Land::LAND_SIZE-1) { @@ -110,42 +114,42 @@ namespace ESMTerrain ESM::Land* land = getLand(cellX, cellY); if (land && land->mDataTypes&ESM::Land::DATA_VCLR) { - color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; - color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; - color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + color.r() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; + color.g() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; + color.b() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; } else { - color.r = 1; - color.g = 1; - color.b = 1; + color.r() = 1; + color.g() = 1; + color.b() = 1; } } - void Storage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, - std::vector& positions, - std::vector& normals, - std::vector& colours) + void Storage::fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, + osg::ref_ptr positions, + osg::ref_ptr normals, + osg::ref_ptr colours) { // LOD level n means every 2^n-th vertex is kept size_t increment = 1 << lodLevel; - Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f); - assert(origin.x == (int) origin.x); - assert(origin.y == (int) origin.y); + osg::Vec2f origin = center - osg::Vec2f(size/2.f, size/2.f); + assert(origin.x() == (int) origin.x()); + assert(origin.y() == (int) origin.y()); - int startX = static_cast(origin.x); - int startY = static_cast(origin.y); + int startX = static_cast(origin.x()); + int startY = static_cast(origin.y()); size_t numVerts = static_cast(size*(ESM::Land::LAND_SIZE - 1) / increment + 1); - colours.resize(numVerts*numVerts*4); - positions.resize(numVerts*numVerts*3); - normals.resize(numVerts*numVerts*3); + positions->resize(numVerts*numVerts); + normals->resize(numVerts*numVerts); + colours->resize(numVerts*numVerts); - Ogre::Vector3 normal; - Ogre::ColourValue color; + osg::Vec3f normal; + osg::Vec4f color; float vertY = 0; float vertX = 0; @@ -175,22 +179,24 @@ namespace ESMTerrain vertX = vertX_; for (int row=rowStart; row(vertX*numVerts * 3 + vertY * 3)] = ((vertX / float(numVerts - 1) - 0.5f) * size * 8192); - positions[static_cast(vertX*numVerts * 3 + vertY * 3 + 1)] = ((vertY / float(numVerts - 1) - 0.5f) * size * 8192); + float height = -2048; if (land) - positions[static_cast(vertX*numVerts * 3 + vertY * 3 + 2)] = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE + row]; - else - positions[static_cast(vertX*numVerts * 3 + vertY * 3 + 2)] = -2048; + height = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE + row]; + + (*positions)[static_cast(vertX*numVerts + vertY)] + = osg::Vec3f((vertX / float(numVerts - 1) - 0.5f) * size * 8192, + (vertY / float(numVerts - 1) - 0.5f) * size * 8192, + height); if (land && land->mDataTypes&ESM::Land::DATA_VNML) { - normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; - normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; - normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; - normal.normalise(); + normal.x() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3]; + normal.y() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1]; + normal.z() = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2]; + normal.normalize(); } else - normal = Ogre::Vector3(0,0,1); + normal = osg::Vec3f(0,0,1); // Normals apparently don't connect seamlessly between cells if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) @@ -200,33 +206,30 @@ namespace ESMTerrain if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1)) averageNormal(normal, cellX, cellY, col, row); - assert(normal.z > 0); + assert(normal.z() > 0); - normals[static_cast(vertX*numVerts * 3 + vertY * 3)] = normal.x; - normals[static_cast(vertX*numVerts * 3 + vertY * 3 + 1)] = normal.y; - normals[static_cast(vertX*numVerts * 3 + vertY * 3 + 2)] = normal.z; + (*normals)[static_cast(vertX*numVerts + vertY)] = normal; if (land && land->mDataTypes&ESM::Land::DATA_VCLR) { - color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; - color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; - color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; + color.r() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f; + color.g() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f; + color.b() = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f; } else { - color.r = 1; - color.g = 1; - color.b = 1; + color.r() = 1; + color.g() = 1; + color.b() = 1; } // Unlike normals, colors mostly connect seamlessly between cells, but not always... if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1) fixColour(color, cellX, cellY, col, row); - color.a = 1; - Ogre::uint32 rsColor; - Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); - memcpy(&colours[static_cast(vertX*numVerts * 4 + vertY * 4)], &rsColor, sizeof(Ogre::uint32)); + color.a() = 1; + + (*colours)[static_cast(vertX*numVerts + vertY)] = color; ++vertX; } @@ -281,39 +284,22 @@ namespace ESMTerrain // NB: All vtex ids are +1 compared to the ltex ids const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second); - //TODO this is needed due to MWs messed up texture handling - assert(0 && "no vfs here yet"); - std::string texture = ltex->mTexture; //Misc::ResourceHelpers::correctTexturePath(ltex->mTexture); + // this is needed due to MWs messed up texture handling + std::string texture = Misc::ResourceHelpers::correctTexturePath(ltex->mTexture, mVFS); return texture; } - void Storage::getBlendmaps (const std::vector& nodes, std::vector& out, bool pack) - { - for (std::vector::const_iterator it = nodes.begin(); it != nodes.end(); ++it) - { - out.push_back(Terrain::LayerCollection()); - out.back().mTarget = *it; - getBlendmapsImpl(static_cast((*it)->getSize()), (*it)->getCenter(), pack, out.back().mBlendmaps, out.back().mLayers); - } - } - - void Storage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter, - bool pack, std::vector &blendmaps, std::vector &layerList) - { - getBlendmapsImpl(chunkSize, chunkCenter, pack, blendmaps, layerList); - } - - void Storage::getBlendmapsImpl(float chunkSize, const Ogre::Vector2 &chunkCenter, - bool pack, std::vector &blendmaps, std::vector &layerList) + void Storage::getBlendmaps(float chunkSize, const osg::Vec2f &chunkCenter, + bool pack, ImageVector &blendmaps, std::vector &layerList) { // TODO - blending isn't completely right yet; the blending radius appears to be // different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap // and interpolate the rest of the cell by hand? :/ - Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f); - int cellX = static_cast(origin.x); - int cellY = static_cast(origin.y); + osg::Vec2f origin = chunkCenter - osg::Vec2f(chunkSize/2.f, chunkSize/2.f); + int cellX = static_cast(origin.x()); + int cellY = static_cast(origin.y()); // Save the used texture indices so we know the total number of textures // and number of required blend maps @@ -353,11 +339,11 @@ namespace ESMTerrain for (int i=0; i image (new osg::Image); + image->allocateImage(blendmapSize, blendmapSize, 1, format, GL_UNSIGNED_BYTE); + unsigned char* pData = image->data(); for (int y=0; y(std::floor(worldPos.x / 8192.f)); - int cellY = static_cast(std::floor(worldPos.y / 8192.f)); + int cellX = static_cast(std::floor(worldPos.x() / 8192.f)); + int cellY = static_cast(std::floor(worldPos.y() / 8192.f)); ESM::Land* land = getLand(cellX, cellY); if (!land || !(land->mDataTypes&ESM::Land::DATA_VHGT)) @@ -390,8 +377,8 @@ namespace ESMTerrain // Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition // Normalized position in the cell - float nX = (worldPos.x - (cellX * 8192))/8192.f; - float nY = (worldPos.y - (cellY * 8192))/8192.f; + float nX = (worldPos.x() - (cellX * 8192))/8192.f; + float nY = (worldPos.y() - (cellY * 8192))/8192.f; // get left / bottom points (rounded down) float factor = ESM::Land::LAND_SIZE - 1.0f; @@ -423,22 +410,23 @@ namespace ESMTerrain */ // Build all 4 positions in normalized cell space, using point-sampled height - Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); - Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); - Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); - Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); + osg::Vec3f v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f); + osg::Vec3f v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f); + osg::Vec3f v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f); + osg::Vec3f v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f); // define this plane in terrain space - Ogre::Plane plane; - // (At the moment, all rows have the same triangle alignment) + osg::Plane plane; + // FIXME: deal with differing triangle alignment if (true) { // odd row bool secondTri = ((1.0 - yParam) > xParam); if (secondTri) - plane.redefine(v0, v1, v3); + plane = osg::Plane(v0, v1, v3); else - plane.redefine(v1, v2, v3); + plane = osg::Plane(v1, v2, v3); } + /* else { // even row @@ -448,11 +436,12 @@ namespace ESMTerrain else plane.redefine(v0, v1, v2); } + */ // Solve plane equation for z - return (-plane.normal.x * nX - -plane.normal.y * nY - - plane.d) / plane.normal.z * 8192; + return (-plane.getNormal().x() * nX + -plane.getNormal().y() * nY + - plane[3]) / plane.getNormal().z() * 8192; } @@ -477,7 +466,7 @@ namespace ESMTerrain std::string texture_ = texture; boost::replace_last(texture_, ".", "_nh."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texture_)) + if (mVFS->exists(texture_)) { info.mNormalMap = texture_; info.mParallax = true; @@ -486,24 +475,18 @@ namespace ESMTerrain { texture_ = texture; boost::replace_last(texture_, ".", "_n."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texture_)) + if (mVFS->exists(texture_)) info.mNormalMap = texture_; } texture_ = texture; boost::replace_last(texture_, ".", "_diffusespec."); - if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texture_)) + if (mVFS->exists(texture_)) { info.mDiffuseMap = texture_; info.mSpecular = true; } - // This wasn't cached, so the textures are probably not loaded either. - // Background load them so they are hopefully already loaded once we need them! - Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mDiffuseMap, "General"); - if (!info.mNormalMap.empty()) - Ogre::ResourceBackgroundQueue::getSingleton().load("Texture", info.mNormalMap, "General"); - mLayerInfoMap[texture] = info; return info; diff --git a/components/esmterrain/storage.hpp b/components/esmterrain/storage.hpp index e184cbc4c..8f4a3aa92 100644 --- a/components/esmterrain/storage.hpp +++ b/components/esmterrain/storage.hpp @@ -6,6 +6,11 @@ #include #include +namespace VFS +{ + class Manager; +} + namespace ESMTerrain { @@ -20,6 +25,7 @@ namespace ESMTerrain virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0; public: + Storage(const VFS::Manager* vfs); // Not implemented in this class, because we need different Store implementations for game and editor /// Get bounds of the whole terrain in cell units @@ -33,11 +39,10 @@ namespace ESMTerrain /// @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 Ogre::Vector2& center, float& min, float& max); + virtual bool getMinMaxHeights (float size, const osg::Vec2f& center, float& min, float& max); /// Fill vertex buffers for a terrain chunk. /// @note May be called from background threads. Make sure to only call thread-safe functions from here! - /// @note returned colors need to be in render-system specific format! Use RenderSystem::convertColourValue. /// @note Vertices should be written in row-major order (a row is defined as parallel to the x-axis). /// The specified positions should be in local space, i.e. relative to the center of the terrain chunk. /// @param lodLevel LOD level, 0 = most detailed @@ -46,10 +51,10 @@ namespace ESMTerrain /// @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 Ogre::Vector2& center, Terrain::Alignment align, - std::vector& positions, - std::vector& normals, - std::vector& colours); + virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, + osg::ref_ptr positions, + osg::ref_ptr normals, + osg::ref_ptr colours); /// 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 @@ -62,23 +67,11 @@ namespace ESMTerrain /// can utilize packing, FFP can't. /// @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 Ogre::Vector2& chunkCenter, bool pack, - std::vector& blendmaps, + virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, bool pack, + ImageVector& blendmaps, std::vector& layerList); - /// Retrieve pixel data for textures holding layer blend values for terrain chunks and layer texture information. - /// This variant is provided to eliminate the overhead of virtual function calls when retrieving a large number of blendmaps at once. - /// @note The terrain chunks 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. - /// @note May be called from background threads. - /// @param nodes A collection of nodes for which to retrieve the aforementioned data - /// @param out Output vector - /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - - /// otherwise, each texture contains blend values for one layer only. Shader-based rendering - /// can utilize packing, FFP can't. - virtual void getBlendmaps (const std::vector& nodes, std::vector& out, bool pack); - - virtual float getHeightAt (const Ogre::Vector3& worldPos); + virtual float getHeightAt (const osg::Vec3f& worldPos); virtual Terrain::LayerInfo getDefaultLayer(); @@ -89,9 +82,11 @@ namespace ESMTerrain virtual int getCellVertices(); private: - void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); - void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row); - void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row); + const VFS::Manager* mVFS; + + void fixNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row); + void fixColour (osg::Vec4f& colour, int cellX, int cellY, int col, int row); + void averageNormal (osg::Vec3f& normal, int cellX, int cellY, int col, int row); float getVertexHeight (const ESM::Land* land, int x, int y); @@ -107,11 +102,6 @@ namespace ESMTerrain std::map mLayerInfoMap; Terrain::LayerInfo getLayerInfo(const std::string& texture); - - // Non-virtual - void getBlendmapsImpl (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack, - std::vector& blendmaps, - std::vector& layerList); }; } diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp index 1b000fabb..7dc93f1d6 100644 --- a/components/terrain/buffercache.cpp +++ b/components/terrain/buffercache.cpp @@ -21,15 +21,17 @@ */ #include "buffercache.hpp" -#include +#include + +#include #include "defs.hpp" namespace { -template -Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigned int verts, Ogre::HardwareIndexBuffer::IndexType type) +template +osg::ref_ptr createIndexBuffer(unsigned int flags, unsigned int verts) { // LOD level n means every 2^n-th vertex is kept size_t lodLevel = (flags >> (4*4)); @@ -42,8 +44,9 @@ Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigne size_t increment = 1 << lodLevel; assert(increment < verts); - std::vector indices; - indices.reserve((verts-1)*(verts-1)*2*3 / increment); + + osg::ref_ptr 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, @@ -62,23 +65,23 @@ Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigne // 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+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); + 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+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); + indices->push_back(verts*col+row); + indices->push_back(verts*(col+increment)+row); + indices->push_back(verts*(col+increment)+row+increment); } } } @@ -94,22 +97,22 @@ Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigne size_t outerStep = 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); + 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); + indices->push_back(verts*(col+outerStep-innerStep)+row+innerStep); else - indices.push_back(verts*(col+outerStep)+row+innerStep); + 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); + indices->push_back(verts*(col)+row); + indices->push_back(verts*(col+i+innerStep)+row+innerStep); + indices->push_back(verts*(col+i)+row+innerStep); } } @@ -118,22 +121,22 @@ Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigne 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); + 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); + indices->push_back(verts*(col+innerStep)+row-innerStep); else - indices.push_back(verts*col+row-innerStep); + 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); + indices->push_back(verts*(col+i)+row-innerStep); + indices->push_back(verts*(col+i+innerStep)+row-innerStep); + indices->push_back(verts*(col+outerStep)+row); } } @@ -142,22 +145,22 @@ Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigne outerStep = size_t(1) << (lodDeltas[Terrain::West] + lodLevel); for (size_t row = 0; row < verts-1; row += outerStep) { - indices.push_back(verts*col+row+outerStep); - indices.push_back(verts*col+row); + 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); + indices->push_back(verts*(col+innerStep)+row+outerStep-innerStep); else - indices.push_back(verts*(col+innerStep)+row+outerStep); + 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); + indices->push_back(verts*col+row); + indices->push_back(verts*(col+innerStep)+row+i); + indices->push_back(verts*(col+innerStep)+row+i+innerStep); } } @@ -166,31 +169,27 @@ Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigne outerStep = size_t(1) << (lodDeltas[Terrain::East] + lodLevel); for (size_t row = 0; row < verts-1; row += outerStep) { - indices.push_back(verts*col+row); - indices.push_back(verts*col+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); + indices->push_back(verts*(col-innerStep)+row+innerStep); else - indices.push_back(verts*(col-innerStep)+row); + 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); + indices->push_back(verts*col+row+outerStep); + indices->push_back(verts*(col-innerStep)+row+i+innerStep); + indices->push_back(verts*(col-innerStep)+row+i); } } } - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(type, - indices.size(), Ogre::HardwareBuffer::HBU_STATIC); - buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); - return buffer; + return indices; } } @@ -198,7 +197,7 @@ Ogre::HardwareIndexBufferSharedPtr createIndexBuffer(unsigned int flags, unsigne namespace Terrain { - Ogre::HardwareVertexBufferSharedPtr BufferCache::getUVBuffer() + osg::ref_ptr BufferCache::getUVBuffer() { if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end()) { @@ -207,30 +206,23 @@ namespace Terrain int vertexCount = mNumVerts * mNumVerts; - std::vector uvs; - uvs.reserve(vertexCount*2); + osg::ref_ptr uvs (new osg::Vec2Array); + uvs->reserve(vertexCount); for (unsigned int col = 0; col < mNumVerts; ++col) { for (unsigned int row = 0; row < mNumVerts; ++row) { - uvs.push_back(col / static_cast(mNumVerts-1)); // U - uvs.push_back(row / static_cast(mNumVerts-1)); // V + uvs->push_back(osg::Vec2f(col / static_cast(mNumVerts-1), + row / static_cast(mNumVerts-1))); } } - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareVertexBufferSharedPtr buffer = mgr->createVertexBuffer( - Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2), - vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - - buffer->writeData(0, buffer->getSizeInBytes(), &uvs[0], true); - - mUvBufferMap[mNumVerts] = buffer; - return buffer; + mUvBufferMap[mNumVerts] = uvs; + return uvs; } - Ogre::HardwareIndexBufferSharedPtr BufferCache::getIndexBuffer(unsigned int flags) + osg::ref_ptr BufferCache::getIndexBuffer(unsigned int flags) { unsigned int verts = mNumVerts; @@ -239,11 +231,12 @@ namespace Terrain return mIndexBufferMap[flags]; } - Ogre::HardwareIndexBufferSharedPtr buffer; + osg::ref_ptr buffer; + if (verts*verts > (0xffffu)) - buffer = createIndexBuffer(flags, verts, Ogre::HardwareIndexBuffer::IT_32BIT); + buffer = createIndexBuffer(flags, verts); else - buffer = createIndexBuffer(flags, verts, Ogre::HardwareIndexBuffer::IT_16BIT); + buffer = createIndexBuffer(flags, verts); mIndexBufferMap[flags] = buffer; return buffer; diff --git a/components/terrain/buffercache.hpp b/components/terrain/buffercache.hpp index 887f0822e..575e9bca2 100644 --- a/components/terrain/buffercache.hpp +++ b/components/terrain/buffercache.hpp @@ -22,8 +22,8 @@ #ifndef COMPONENTS_TERRAIN_BUFFERCACHE_H #define COMPONENTS_TERRAIN_BUFFERCACHE_H -#include -#include +#include +#include #include @@ -38,16 +38,18 @@ 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) - Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (unsigned int flags); + osg::ref_ptr getIndexBuffer (unsigned int flags); - Ogre::HardwareVertexBufferSharedPtr getUVBuffer (); + osg::ref_ptr getUVBuffer(); + + // TODO: add releaseGLObjects() for our vertex/element buffer objects 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 mIndexBufferMap; + std::map > mIndexBufferMap; - std::map mUvBufferMap; + std::map > mUvBufferMap; unsigned int mNumVerts; }; diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp deleted file mode 100644 index e3bae1173..000000000 --- a/components/terrain/chunk.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2015 scrawl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "chunk.hpp" - -#include -#include -#include -#include -#include - -namespace Terrain -{ - - Chunk::Chunk(Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, - const std::vector& positions, const std::vector& normals, const std::vector& colours) - : mBounds(bounds) - , mOwnMaterial(false) - { - mVertexData = OGRE_NEW Ogre::VertexData; - mVertexData->vertexStart = 0; - mVertexData->vertexCount = positions.size()/3; - - // Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc) - Ogre::VertexDeclaration* vertexDecl = mVertexData->vertexDeclaration; - - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - size_t nextBuffer = 0; - - // Positions - vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); - Ogre::HardwareVertexBufferSharedPtr vertexBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - - // Normals - vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); - Ogre::HardwareVertexBufferSharedPtr normalBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), - mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - - - // UV texture coordinates - vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT2, - Ogre::VES_TEXTURE_COORDINATES, 0); - - // Colours - vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); - Ogre::HardwareVertexBufferSharedPtr colourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), - mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); - - vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true); - normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true); - colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colours[0], true); - - mVertexData->vertexBufferBinding->setBinding(0, vertexBuffer); - mVertexData->vertexBufferBinding->setBinding(1, normalBuffer); - mVertexData->vertexBufferBinding->setBinding(2, uvBuffer); - mVertexData->vertexBufferBinding->setBinding(3, colourBuffer); - - // Assign a default material in case terrain material fails to be created - mMaterial = Ogre::MaterialManager::getSingleton().getByName("BaseWhite"); - - mIndexData = OGRE_NEW Ogre::IndexData(); - mIndexData->indexStart = 0; - } - - void Chunk::setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer) - { - mIndexData->indexBuffer = buffer; - mIndexData->indexCount = buffer->getNumIndexes(); - } - - Chunk::~Chunk() - { - if (!mMaterial.isNull() && mOwnMaterial) - { -#if TERRAIN_USE_SHADER - sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName()); -#endif - Ogre::MaterialManager::getSingleton().remove(mMaterial->getName()); - } - OGRE_DELETE mVertexData; - OGRE_DELETE mIndexData; - } - - void Chunk::setMaterial(const Ogre::MaterialPtr &material, bool own) - { - // Clean up the previous material, if we own it - if (!mMaterial.isNull() && mOwnMaterial) - { -#if TERRAIN_USE_SHADER - sh::Factory::getInstance().destroyMaterialInstance(mMaterial->getName()); -#endif - Ogre::MaterialManager::getSingleton().remove(mMaterial->getName()); - } - - mMaterial = material; - mOwnMaterial = own; - } - - const Ogre::AxisAlignedBox& Chunk::getBoundingBox(void) const - { - return mBounds; - } - - Ogre::Real Chunk::getBoundingRadius(void) const - { - return mBounds.getHalfSize().length(); - } - - void Chunk::_updateRenderQueue(Ogre::RenderQueue* queue) - { - queue->addRenderable(this, mRenderQueueID); - } - - void Chunk::visitRenderables(Ogre::Renderable::Visitor* visitor, - bool debugRenderables) - { - visitor->visit(this, 0, false); - } - - const Ogre::MaterialPtr& Chunk::getMaterial(void) const - { - return mMaterial; - } - - void Chunk::getRenderOperation(Ogre::RenderOperation& op) - { - assert (!mIndexData->indexBuffer.isNull() && "Trying to render, but no index buffer set!"); - assert(!mMaterial.isNull() && "Trying to render, but no material set!"); - op.useIndexes = true; - op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; - op.vertexData = mVertexData; - op.indexData = mIndexData; - } - - void Chunk::getWorldTransforms(Ogre::Matrix4* xform) const - { - *xform = getParentSceneNode()->_getFullTransform(); - } - - Ogre::Real Chunk::getSquaredViewDepth(const Ogre::Camera* cam) const - { - return getParentSceneNode()->getSquaredViewDepth(cam); - } - - const Ogre::LightList& Chunk::getLights(void) const - { - return queryLights(); - } - -} diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp deleted file mode 100644 index 22b4f26ef..000000000 --- a/components/terrain/chunk.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2015 scrawl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef COMPONENTS_TERRAIN_TERRAINBATCH_H -#define COMPONENTS_TERRAIN_TERRAINBATCH_H - -#include -#include - -namespace Terrain -{ - - /** - * @brief A movable object representing a chunk of terrain. - */ - class Chunk : public Ogre::Renderable, public Ogre::MovableObject - { - public: - Chunk (Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, - const std::vector& positions, - const std::vector& normals, - const std::vector& colours); - - virtual ~Chunk(); - - /// @param own Should we take ownership of the material? - void setMaterial (const Ogre::MaterialPtr& material, bool own=true); - - void setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer); - - // Inherited from MovableObject - virtual const Ogre::String& getMovableType(void) const { static Ogre::String t = "MW_TERRAIN"; return t; } - virtual const Ogre::AxisAlignedBox& getBoundingBox(void) const; - virtual Ogre::Real getBoundingRadius(void) const; - virtual void _updateRenderQueue(Ogre::RenderQueue* queue); - virtual void visitRenderables(Renderable::Visitor* visitor, - bool debugRenderables = false); - - // Inherited from Renderable - virtual const Ogre::MaterialPtr& getMaterial(void) const; - virtual void getRenderOperation(Ogre::RenderOperation& op); - virtual void getWorldTransforms(Ogre::Matrix4* xform) const; - virtual Ogre::Real getSquaredViewDepth(const Ogre::Camera* cam) const; - virtual const Ogre::LightList& getLights(void) const; - - private: - Ogre::AxisAlignedBox mBounds; - Ogre::MaterialPtr mMaterial; - bool mOwnMaterial; // Should we remove mMaterial on destruction? - - Ogre::VertexData* mVertexData; - Ogre::IndexData* mIndexData; - }; - -} - -#endif diff --git a/components/terrain/defaultworld.cpp b/components/terrain/defaultworld.cpp deleted file mode 100644 index 7bc73ddda..000000000 --- a/components/terrain/defaultworld.cpp +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (c) 2015 scrawl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "defaultworld.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include "storage.hpp" -#include "quadtreenode.hpp" - -namespace -{ - - bool isPowerOfTwo(int x) - { - return ( (x > 0) && ((x & (x - 1)) == 0) ); - } - - int nextPowerOfTwo (int v) - { - if (isPowerOfTwo(v)) return v; - int depth=0; - while(v) - { - v >>= 1; - depth++; - } - return 1 << depth; - } - - Terrain::QuadTreeNode* findNode (const Ogre::Vector2& center, Terrain::QuadTreeNode* node) - { - if (center == node->getCenter()) - return node; - - if (center.x > node->getCenter().x && center.y > node->getCenter().y) - return findNode(center, node->getChild(Terrain::NE)); - else if (center.x > node->getCenter().x && center.y < node->getCenter().y) - return findNode(center, node->getChild(Terrain::SE)); - else if (center.x < node->getCenter().x && center.y > node->getCenter().y) - return findNode(center, node->getChild(Terrain::NW)); - else //if (center.x < node->getCenter().x && center.y < node->getCenter().y) - return findNode(center, node->getChild(Terrain::SW)); - } - -} - -namespace Terrain -{ - - const Ogre::uint REQ_ID_CHUNK = 1; - const Ogre::uint REQ_ID_LAYERS = 2; - - DefaultWorld::DefaultWorld(Ogre::SceneManager* sceneMgr, - Storage* storage, int visibilityFlags, bool shaders, Alignment align, float minBatchSize, float maxBatchSize) - : World(sceneMgr, storage, visibilityFlags, shaders, align) - , mWorkQueueChannel(0) - , mVisible(true) - , mChunksLoading(0) - , mMinX(0) - , mMaxX(0) - , mMinY(0) - , mMaxY(0) - , mMinBatchSize(minBatchSize) - , mMaxBatchSize(maxBatchSize) - , mLayerLoadPending(true) - { -#if TERRAIN_USE_SHADER == 0 - if (mShaders) - std::cerr << "Compiled Terrain without shader support, disabling..." << std::endl; - mShaders = false; -#endif - - mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); - - /// \todo make composite map size configurable - Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a"); - mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual( - "terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, 128, 128, 0, Ogre::PF_A8B8G8R8, Ogre::TU_RENDERTARGET); - mCompositeMapRenderTarget = mCompositeMapRenderTexture->getBuffer()->getRenderTarget(); - mCompositeMapRenderTarget->setAutoUpdated(false); - mCompositeMapRenderTarget->addViewport(compositeMapCam); - - storage->getBounds(mMinX, mMaxX, mMinY, mMaxY); - - int origSizeX = static_cast(mMaxX - mMinX); - int origSizeY = static_cast(mMaxY - mMinY); - - // Dividing a quad tree only works well for powers of two, so round up to the nearest one - int size = nextPowerOfTwo(std::max(origSizeX, origSizeY)); - - // Adjust the center according to the new size - float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f; - float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; - - mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - - // While building the quadtree, remember leaf nodes since we need to load their layers - LayersRequestData data; - data.mPack = getShadersEnabled(); - - mRootNode = new QuadTreeNode(this, Root, static_cast(size), Ogre::Vector2(centerX, centerY), NULL); - buildQuadTree(mRootNode, data.mNodes); - //loadingListener->indicateProgress(); - mRootNode->initAabb(); - //loadingListener->indicateProgress(); - mRootNode->initNeighbours(); - //loadingListener->indicateProgress(); - - Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); - mWorkQueueChannel = wq->getChannel("LargeTerrain"); - wq->addRequestHandler(mWorkQueueChannel, this); - wq->addResponseHandler(mWorkQueueChannel, this); - - // Start loading layers in the background (for leaf nodes) - wq->addRequest(mWorkQueueChannel, REQ_ID_LAYERS, Ogre::Any(data)); - } - - DefaultWorld::~DefaultWorld() - { - Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue(); - wq->removeRequestHandler(mWorkQueueChannel, this); - wq->removeResponseHandler(mWorkQueueChannel, this); - - delete mRootNode; - } - - void DefaultWorld::buildQuadTree(QuadTreeNode *node, std::vector& leafs) - { - float halfSize = node->getSize()/2.f; - - if (node->getSize() <= mMinBatchSize) - { - // We arrived at a leaf - float minZ,maxZ; - Ogre::Vector2 center = node->getCenter(); - float cellWorldSize = getStorage()->getCellWorldSize(); - if (mStorage->getMinMaxHeights(static_cast(node->getSize()), center, minZ, maxZ)) - { - Ogre::AxisAlignedBox bounds(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), - Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ)); - convertBounds(bounds); - node->setBoundingBox(bounds); - leafs.push_back(node); - } - else - node->markAsDummy(); // no data available for this node, skip it - return; - } - - if (node->getCenter().x - halfSize > mMaxX - || node->getCenter().x + halfSize < mMinX - || node->getCenter().y - halfSize > mMaxY - || node->getCenter().y + halfSize < mMinY ) - // Out of bounds of the actual terrain - this will happen because - // we rounded the size up to the next power of two - { - node->markAsDummy(); - return; - } - - // Not a leaf, create its children - node->createChild(SW, halfSize, node->getCenter() - halfSize/2.f); - node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -halfSize/2.f)); - node->createChild(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f)); - node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f); - buildQuadTree(node->getChild(SW), leafs); - buildQuadTree(node->getChild(SE), leafs); - buildQuadTree(node->getChild(NW), leafs); - buildQuadTree(node->getChild(NE), leafs); - - // if all children are dummy, we are also dummy - for (int i=0; i<4; ++i) - { - if (!node->getChild((ChildDirection)i)->isDummy()) - return; - } - node->markAsDummy(); - } - - void DefaultWorld::update(const Ogre::Vector3& cameraPos) - { - if (!mVisible) - return; - mRootNode->update(cameraPos); - mRootNode->updateIndexBuffers(); - } - - Ogre::AxisAlignedBox DefaultWorld::getWorldBoundingBox (const Ogre::Vector2& center) - { - if (center.x > mMaxX - || center.x < mMinX - || center.y > mMaxY - || center.y < mMinY) - return Ogre::AxisAlignedBox::BOX_NULL; - QuadTreeNode* node = findNode(center, mRootNode); - return node->getWorldBoundingBox(); - } - - void DefaultWorld::renderCompositeMap(Ogre::TexturePtr target) - { - mCompositeMapRenderTarget->update(); - target->getBuffer()->blit(mCompositeMapRenderTexture->getBuffer()); - } - - void DefaultWorld::clearCompositeMapSceneManager() - { - mCompositeMapSceneMgr->destroyAllManualObjects(); - mCompositeMapSceneMgr->clearScene(); - } - - void DefaultWorld::applyMaterials(bool shadows, bool splitShadows) - { - mShadows = shadows; - mSplitShadows = splitShadows; - mRootNode->applyMaterials(); - } - - void DefaultWorld::setVisible(bool visible) - { - if (visible && !mVisible) - mSceneMgr->getRootSceneNode()->addChild(mRootSceneNode); - else if (!visible && mVisible) - mSceneMgr->getRootSceneNode()->removeChild(mRootSceneNode); - - mVisible = visible; - } - - bool DefaultWorld::getVisible() - { - return mVisible; - } - - void DefaultWorld::syncLoad() - { - while (mChunksLoading || mLayerLoadPending) - { - OGRE_THREAD_SLEEP(0); - Ogre::Root::getSingleton().getWorkQueue()->processResponses(); - } - } - - Ogre::WorkQueue::Response* DefaultWorld::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ) - { - if (req->getType() == REQ_ID_CHUNK) - { - const LoadRequestData data = Ogre::any_cast(req->getData()); - - QuadTreeNode* node = data.mNode; - - LoadResponseData* responseData = new LoadResponseData(); - - getStorage()->fillVertexBuffers(node->getNativeLodLevel(), static_cast(node->getSize()), node->getCenter(), getAlign(), - responseData->mPositions, responseData->mNormals, responseData->mColours); - - return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); - } - else // REQ_ID_LAYERS - { - const LayersRequestData data = Ogre::any_cast(req->getData()); - - LayersResponseData* responseData = new LayersResponseData(); - - getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack); - - return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData)); - } - } - - void DefaultWorld::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ) - { - assert(res->succeeded() && "Response failure not handled"); - - if (res->getRequest()->getType() == REQ_ID_CHUNK) - { - LoadResponseData* data = Ogre::any_cast(res->getData()); - - const LoadRequestData requestData = Ogre::any_cast(res->getRequest()->getData()); - - requestData.mNode->load(*data); - - delete data; - - --mChunksLoading; - } - else // REQ_ID_LAYERS - { - LayersResponseData* data = Ogre::any_cast(res->getData()); - - for (std::vector::iterator it = data->mLayerCollections.begin(); it != data->mLayerCollections.end(); ++it) - { - it->mTarget->loadLayers(*it); - } - - delete data; - - mRootNode->loadMaterials(); - - mLayerLoadPending = false; - } - } - - void DefaultWorld::queueLoad(QuadTreeNode *node) - { - LoadRequestData data; - data.mNode = node; - - Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, REQ_ID_CHUNK, Ogre::Any(data)); - ++mChunksLoading; - } -} diff --git a/components/terrain/defaultworld.hpp b/components/terrain/defaultworld.hpp deleted file mode 100644 index 4caef8462..000000000 --- a/components/terrain/defaultworld.hpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2015 scrawl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef COMPONENTS_TERRAIN_H -#define COMPONENTS_TERRAIN_H - -#include -#include -#include - -#include "world.hpp" - -namespace Ogre -{ - class Camera; -} - -namespace Terrain -{ - - class QuadTreeNode; - class Storage; - - /** - * @brief A quadtree-based terrain implementation suitable for large data sets. \n - * Near cells are rendered with alpha splatting, distant cells are merged - * together in batches and have their layers pre-rendered onto a composite map. \n - * Cracks at LOD transitions are avoided using stitching. - * @note Multiple cameras are not supported yet - */ - class DefaultWorld : public World, public Ogre::WorkQueue::RequestHandler, public Ogre::WorkQueue::ResponseHandler - { - public: - /// @note takes ownership of \a storage - /// @param sceneMgr scene manager to use - /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) - /// @param visbilityFlags visibility flags for the created meshes - /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually - /// faster so this is just here for compatibility. - /// @param align The align of the terrain, see Alignment enum - /// @param minBatchSize Minimum size of a terrain batch along one side (in cell units). Used for building the quad tree. - /// @param maxBatchSize Maximum size of a terrain batch along one side (in cell units). Used when traversing the quad tree. - DefaultWorld(Ogre::SceneManager* sceneMgr, - Storage* storage, int visibilityFlags, bool shaders, Alignment align, float minBatchSize, float maxBatchSize); - ~DefaultWorld(); - - /// Update chunk LODs according to this camera position - /// @note Calling this method might lead to composite textures being rendered, so it is best - /// not to call it when render commands are still queued, since that would cause a flush. - virtual void update (const Ogre::Vector3& cameraPos); - - /// Get the world bounding box of a chunk of terrain centered at \a center - virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center); - - Ogre::SceneNode* getRootSceneNode() { return mRootSceneNode; } - - /// Show or hide the whole terrain - /// @note this setting will be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden - virtual void setVisible(bool visible); - virtual bool getVisible(); - - /// Recreate materials used by terrain chunks. This should be called whenever settings of - /// the material factory are changed. (Relying on the factory to update those materials is not - /// enough, since turning a feature on/off can change the number of texture units available for layer/blend - /// textures, and to properly respond to this we may need to change the structure of the material, such as - /// adding or removing passes. This can only be achieved by a full rebuild.) - virtual void applyMaterials(bool shadows, bool splitShadows); - - int getMaxBatchSize() { return static_cast(mMaxBatchSize); } - - /// Wait until all background loading is complete. - void syncLoad(); - - private: - // Called from a background worker thread - virtual Ogre::WorkQueue::Response* handleRequest(const Ogre::WorkQueue::Request* req, const Ogre::WorkQueue* srcQ); - // Called from the main thread - virtual void handleResponse(const Ogre::WorkQueue::Response* res, const Ogre::WorkQueue* srcQ); - Ogre::uint16 mWorkQueueChannel; - - bool mVisible; - - QuadTreeNode* mRootNode; - Ogre::SceneNode* mRootSceneNode; - - /// The number of chunks currently loading in a background thread. If 0, we have finished loading! - int mChunksLoading; - - Ogre::SceneManager* mCompositeMapSceneMgr; - - /// Bounds in cell units - float mMinX, mMaxX, mMinY, mMaxY; - - /// Minimum size of a terrain batch along one side (in cell units) - float mMinBatchSize; - /// Maximum size of a terrain batch along one side (in cell units) - float mMaxBatchSize; - - void buildQuadTree(QuadTreeNode* node, std::vector& leafs); - - // Are layers for leaf nodes loaded? This is done once at startup (but in a background thread) - bool mLayerLoadPending; - - public: - // ----INTERNAL---- - Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; } - - bool areLayersLoaded() { return !mLayerLoadPending; } - - // Delete all quads - void clearCompositeMapSceneManager(); - void renderCompositeMap (Ogre::TexturePtr target); - - // Adds a WorkQueue request to load a chunk for this node in the background. - void queueLoad (QuadTreeNode* node); - - private: - Ogre::RenderTarget* mCompositeMapRenderTarget; - Ogre::TexturePtr mCompositeMapRenderTexture; - }; - - struct LoadRequestData - { - QuadTreeNode* mNode; - - friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r) - { return o; } - }; - - struct LoadResponseData - { - std::vector mPositions; - std::vector mNormals; - std::vector mColours; - - friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r) - { return o; } - }; - - struct LayersRequestData - { - std::vector mNodes; - bool mPack; - - friend std::ostream& operator<<(std::ostream& o, const LayersRequestData& r) - { return o; } - }; - - struct LayersResponseData - { - std::vector mLayerCollections; - - friend std::ostream& operator<<(std::ostream& o, const LayersResponseData& r) - { return o; } - }; - -} - -#endif diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp index 6d173d136..7b40ad479 100644 --- a/components/terrain/defs.hpp +++ b/components/terrain/defs.hpp @@ -22,41 +22,10 @@ #ifndef COMPONENTS_TERRAIN_DEFS_HPP #define COMPONENTS_TERRAIN_DEFS_HPP +#include + namespace Terrain { - class QuadTreeNode; - - /// The alignment of the terrain - enum Alignment - { - /// Terrain is in the X/Z plane - Align_XZ = 0, - /// Terrain is in the X/Y plane - Align_XY = 1, - /// Terrain is in the Y/Z plane. - /// UNTESTED - use at own risk. - /// Besides, X as up axis? What is wrong with you? ;) - Align_YZ = 2 - }; - - inline void convertPosition(Alignment align, float &x, float &y, float &z) - { - switch (align) - { - case Align_XY: - return; - case Align_XZ: - std::swap(y, z); - // This is since -Z should be going *into* the screen - // If not doing this, we'd get wrong vertex winding - z *= -1; - return; - case Align_YZ: - std::swap(x, y); - std::swap(y, z); - return; - } - } enum Direction { @@ -74,13 +43,6 @@ namespace Terrain bool mSpecular; // Specular info in diffuse map alpha channel? }; - struct LayerCollection - { - QuadTreeNode* mTarget; - // Since we can't create a texture from a different thread, this only holds the raw texel data - std::vector mBlendmaps; - std::vector mLayers; - }; } #endif diff --git a/components/terrain/material.cpp b/components/terrain/material.cpp index 4d2318aa6..17acd940b 100644 --- a/components/terrain/material.cpp +++ b/components/terrain/material.cpp @@ -21,353 +21,86 @@ */ #include "material.hpp" -#include -#include -#include +#include -#include +#include +#include +#include +#include +#include -#if TERRAIN_USE_SHADER -#include -#endif - -namespace -{ - -int getBlendmapIndexForLayer (int layerIndex) -{ - return static_cast(std::floor((layerIndex - 1) / 4.f)); -} - -std::string getBlendmapComponentForLayer (int layerIndex) -{ - int n = (layerIndex-1)%4; - if (n == 0) - return "x"; - if (n == 1) - return "y"; - if (n == 2) - return "z"; - else - return "w"; -} - -} +#include namespace Terrain { - MaterialGenerator::MaterialGenerator() - : mShaders(true) - , mShadows(false) - , mSplitShadows(false) - , mNormalMapping(true) - , mParallaxMapping(true) - { - - } - - Ogre::MaterialPtr MaterialGenerator::generate() - { - assert(!mLayerList.empty() && "Can't create material with no layers"); - - return create(false, false); - } - - Ogre::MaterialPtr MaterialGenerator::generateForCompositeMapRTT() - { - assert(!mLayerList.empty() && "Can't create material with no layers"); - - return create(true, false); - } - - Ogre::MaterialPtr MaterialGenerator::generateForCompositeMap() + FixedFunctionTechnique::FixedFunctionTechnique(const std::vector >& layers, + const std::vector >& blendmaps) { - return create(false, true); - } - - Ogre::MaterialPtr MaterialGenerator::create(bool renderCompositeMap, bool displayCompositeMap) - { - assert(!renderCompositeMap || !displayCompositeMap); - - static int count = 0; - std::stringstream name; - name << "terrain/mat" << count++; - - if (!mShaders) + bool firstLayer = true; + int i=0; + for (std::vector >::const_iterator it = layers.begin(); it != layers.end(); ++it) { - Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(name.str(), - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - Ogre::Technique* technique = mat->getTechnique(0); - technique->removeAllPasses(); + osg::ref_ptr stateset (new osg::StateSet); - if (displayCompositeMap) + if (!firstLayer) { - Ogre::Pass* pass = technique->createPass(); - pass->setVertexColourTracking(Ogre::TVC_AMBIENT|Ogre::TVC_DIFFUSE); - pass->createTextureUnitState(mCompositeMap)->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON); + osg::ref_ptr depth (new osg::Depth); + depth->setFunction(osg::Depth::EQUAL); + stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); } - else - { - assert(mLayerList.size() == mBlendmapList.size()+1); - std::vector::iterator blend = mBlendmapList.begin(); - for (std::vector::iterator layer = mLayerList.begin(); layer != mLayerList.end(); ++layer) - { - Ogre::Pass* pass = technique->createPass(); - pass->setLightingEnabled(false); - pass->setVertexColourTracking(Ogre::TVC_NONE); - // TODO: How to handle fog? - pass->setFog(true, Ogre::FOG_NONE); - - bool first = (layer == mLayerList.begin()); - - Ogre::TextureUnitState* tus; - if (!first) - { - pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); - pass->setDepthFunction(Ogre::CMPF_EQUAL); - - tus = pass->createTextureUnitState((*blend)->getName()); - tus->setAlphaOperation(Ogre::LBX_BLEND_TEXTURE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setIsAlpha(true); - tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); - - float scale = (16/(16.f+1.f)); - tus->setTextureScale(1.f/scale,1.f/scale); - } - - // Add the actual layer texture on top of the alpha map. - tus = pass->createTextureUnitState(layer->mDiffuseMap); - if (!first) - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_CURRENT); - - tus->setTextureScale(1/16.f,1/16.f); - - if (!first) - ++blend; - } - - if (!renderCompositeMap) - { - Ogre::Pass* lightingPass = technique->createPass(); - lightingPass->setSceneBlending(Ogre::SBT_MODULATE); - lightingPass->setVertexColourTracking(Ogre::TVC_AMBIENT|Ogre::TVC_DIFFUSE); - lightingPass->setFog(true, Ogre::FOG_NONE); - } - } - - return mat; - } -#if TERRAIN_USE_SHADER - else - { - sh::MaterialInstance* material = sh::Factory::getInstance().createMaterialInstance (name.str()); - material->setProperty ("allow_fixed_function", sh::makeProperty(new sh::BooleanValue(false))); - - if (displayCompositeMap) + int texunit = 0; + if(!firstLayer) { - sh::MaterialInstancePass* p = material->createPass (); + osg::ref_ptr blendmap = blendmaps.at(i++); - p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); - p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); - p->mShaderProperties.setProperty ("is_first_pass", sh::makeProperty(new sh::BooleanValue(true))); - p->mShaderProperties.setProperty ("render_composite_map", sh::makeProperty(new sh::BooleanValue(false))); - p->mShaderProperties.setProperty ("display_composite_map", sh::makeProperty(new sh::BooleanValue(true))); - p->mShaderProperties.setProperty ("num_layers", sh::makeProperty (new sh::StringValue("0"))); - p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty (new sh::StringValue("0"))); - p->mShaderProperties.setProperty ("normal_map_enabled", sh::makeProperty (new sh::BooleanValue(false))); - p->mShaderProperties.setProperty ("parallax_enabled", sh::makeProperty (new sh::BooleanValue(false))); - p->mShaderProperties.setProperty ("normal_maps", - sh::makeProperty (new sh::IntValue(0))); + stateset->setTextureAttributeAndModes(texunit, blendmap.get()); - sh::MaterialInstanceTextureUnit* tex = p->createTextureUnit ("compositeMap"); - tex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(mCompositeMap))); - tex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); + // This is to map corner vertices directly to the center of a blendmap texel. + osg::Matrixf texMat; + float scale = (16/(16.f+1.f)); + texMat.preMultTranslate(osg::Vec3f(0.5f, 0.5f, 0.f)); + texMat.preMultScale(osg::Vec3f(scale, scale, 1.f)); + texMat.preMultTranslate(osg::Vec3f(-0.5f, -0.5f, 0.f)); - // shadow. TODO: repeated, put in function - if (mShadows) - { - for (int i = 0; i < (mSplitShadows ? 3 : 1); ++i) - { - sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); - shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); - } - } - p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty (new sh::StringValue( - Ogre::StringConverter::toString(1)))); + stateset->setTextureAttributeAndModes(texunit, new osg::TexMat(texMat)); - p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(0))); + ++texunit; } - else - { - - bool shadows = mShadows && !renderCompositeMap; - - int layerOffset = 0; - while (layerOffset < (int)mLayerList.size()) - { - int blendmapOffset = (layerOffset == 0) ? 1 : 0; // the first layer of the first pass is the base layer and does not need a blend map - - // Check how many layers we can fit in this pass - int numLayersInThisPass = 0; - int numBlendTextures = 0; - std::vector blendTextures; - int remainingTextureUnits = OGRE_MAX_TEXTURE_LAYERS; - if (shadows) - remainingTextureUnits -= (mSplitShadows ? 3 : 1); - while (remainingTextureUnits && layerOffset + numLayersInThisPass < (int)mLayerList.size()) - { - int layerIndex = numLayersInThisPass + layerOffset; - int neededTextureUnits=0; - int neededBlendTextures=0; + // Add the actual layer texture multiplied by the alpha map. + osg::ref_ptr tex = *it; + stateset->setTextureAttributeAndModes(texunit, tex.get()); - if (layerIndex != 0) - { - std::string blendTextureName = mBlendmapList[getBlendmapIndexForLayer(layerIndex)]->getName(); - if (std::find(blendTextures.begin(), blendTextures.end(), blendTextureName) == blendTextures.end()) - { - blendTextures.push_back(blendTextureName); - ++neededBlendTextures; - ++neededTextureUnits; // blend texture - } - } - ++neededTextureUnits; // layer texture + osg::ref_ptr texMat (new osg::TexMat); + float scale = 16.f; + texMat->setMatrix(osg::Matrix::scale(osg::Vec3f(scale,scale,1.f))); + stateset->setTextureAttributeAndModes(texunit, texMat, osg::StateAttribute::ON); - // Check if this layer has a normal map - if (mNormalMapping && !mLayerList[layerIndex].mNormalMap.empty() && !renderCompositeMap) - ++neededTextureUnits; // normal map - if (neededTextureUnits <= remainingTextureUnits) - { - // We can fit another! - remainingTextureUnits -= neededTextureUnits; - numBlendTextures += neededBlendTextures; - ++numLayersInThisPass; - } - else - break; // We're full - } + firstLayer = false; + addPass(stateset); + } + } - sh::MaterialInstancePass* p = material->createPass (); - p->setProperty ("vertex_program", sh::makeProperty(new sh::StringValue("terrain_vertex"))); - p->setProperty ("fragment_program", sh::makeProperty(new sh::StringValue("terrain_fragment"))); - if (layerOffset != 0) - { - p->setProperty ("scene_blend", sh::makeProperty(new sh::StringValue("alpha_blend"))); - // Only write if depth is equal to the depth value written by the previous pass. - p->setProperty ("depth_func", sh::makeProperty(new sh::StringValue("equal"))); - } - - p->mShaderProperties.setProperty ("render_composite_map", sh::makeProperty(new sh::BooleanValue(renderCompositeMap))); - p->mShaderProperties.setProperty ("display_composite_map", sh::makeProperty(new sh::BooleanValue(displayCompositeMap))); - - p->mShaderProperties.setProperty ("num_layers", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numLayersInThisPass)))); - p->mShaderProperties.setProperty ("num_blendmaps", sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(numBlendTextures)))); - p->mShaderProperties.setProperty ("normal_map_enabled", - sh::makeProperty (new sh::BooleanValue(false))); - - // blend maps - // the index of the first blend map used in this pass - int blendmapStart; - if (mLayerList.size() == 1) // special case. if there's only one layer, we don't need blend maps at all - blendmapStart = 0; - else - blendmapStart = getBlendmapIndexForLayer(layerOffset+blendmapOffset); - for (int i = 0; i < numBlendTextures; ++i) - { - sh::MaterialInstanceTextureUnit* blendTex = p->createTextureUnit ("blendMap" + Ogre::StringConverter::toString(i)); - blendTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(mBlendmapList[blendmapStart+i]->getName()))); - blendTex->setProperty ("tex_address_mode", sh::makeProperty (new sh::StringValue("clamp"))); - } - - // layer maps - bool anyNormalMaps = false; - bool anyParallax = false; - size_t normalMaps = 0; - for (int i = 0; i < numLayersInThisPass; ++i) - { - const LayerInfo& layer = mLayerList[layerOffset+i]; - // diffuse map - sh::MaterialInstanceTextureUnit* diffuseTex = p->createTextureUnit ("diffuseMap" + Ogre::StringConverter::toString(i)); - diffuseTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(layer.mDiffuseMap))); - - // normal map (optional) - bool useNormalMap = mNormalMapping && !mLayerList[layerOffset+i].mNormalMap.empty() && !renderCompositeMap; - bool useParallax = useNormalMap && mParallaxMapping && layer.mParallax; - bool useSpecular = layer.mSpecular; - if (useNormalMap) - { - anyNormalMaps = true; - anyParallax = anyParallax || useParallax; - sh::MaterialInstanceTextureUnit* normalTex = p->createTextureUnit ("normalMap" + Ogre::StringConverter::toString(i)); - normalTex->setProperty ("direct_texture", sh::makeProperty (new sh::StringValue(layer.mNormalMap))); - } - p->mShaderProperties.setProperty ("use_normal_map_" + Ogre::StringConverter::toString(i), - sh::makeProperty (new sh::BooleanValue(useNormalMap))); - p->mShaderProperties.setProperty ("use_parallax_" + Ogre::StringConverter::toString(i), - sh::makeProperty (new sh::BooleanValue(useParallax))); - p->mShaderProperties.setProperty ("use_specular_" + Ogre::StringConverter::toString(i), - sh::makeProperty (new sh::BooleanValue(useSpecular))); - boost::hash_combine(normalMaps, useNormalMap); - boost::hash_combine(normalMaps, useNormalMap && layer.mParallax); - boost::hash_combine(normalMaps, useSpecular); - - if (i+layerOffset > 0) - { - int blendTextureIndex = getBlendmapIndexForLayer(layerOffset+i); - std::string blendTextureComponent = getBlendmapComponentForLayer(layerOffset+i); - p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), - sh::makeProperty (new sh::StringValue(Ogre::StringConverter::toString(blendTextureIndex-blendmapStart) + "." + blendTextureComponent))); - } - else - { - // just to make it shut up about blendmap_component_0 not existing in the first pass. - // it might be retrieved, but will never survive the preprocessing step. - p->mShaderProperties.setProperty ("blendmap_component_" + Ogre::StringConverter::toString(i), - sh::makeProperty (new sh::StringValue(""))); - } - } - p->mShaderProperties.setProperty ("normal_map_enabled", - sh::makeProperty (new sh::BooleanValue(anyNormalMaps))); - p->mShaderProperties.setProperty ("parallax_enabled", - sh::makeProperty (new sh::BooleanValue(anyParallax))); - // Since the permutation handler can't handle dynamic property names, - // combine normal map settings for all layers into one value - p->mShaderProperties.setProperty ("normal_maps", - sh::makeProperty (new sh::IntValue(normalMaps))); - - // shadow - if (shadows) - { - for (int i = 0; i < (mSplitShadows ? 3 : 1); ++i) - { - sh::MaterialInstanceTextureUnit* shadowTex = p->createTextureUnit ("shadowMap" + Ogre::StringConverter::toString(i)); - shadowTex->setProperty ("content_type", sh::makeProperty (new sh::StringValue("shadow"))); - } - } - p->mShaderProperties.setProperty ("shadowtexture_offset", sh::makeProperty (new sh::StringValue( - Ogre::StringConverter::toString(numBlendTextures + numLayersInThisPass)))); + Effect::Effect(const std::vector > &layers, const std::vector > &blendmaps) + : mLayers(layers) + , mBlendmaps(blendmaps) + { + osg::ref_ptr material (new osg::Material); + material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE); + getOrCreateStateSet()->setAttributeAndModes(material, osg::StateAttribute::ON); - // Make sure the pass index is fed to the permutation handler, because blendmap components may be different - p->mShaderProperties.setProperty ("pass_index", sh::makeProperty(new sh::IntValue(layerOffset))); + selectTechnique(0); + } - assert ((int)p->mTexUnits.size() == OGRE_MAX_TEXTURE_LAYERS - remainingTextureUnits); + bool Effect::define_techniques() + { + addTechnique(new FixedFunctionTechnique(mLayers, mBlendmaps)); - layerOffset += numLayersInThisPass; - } - } - } -#endif - return Ogre::MaterialManager::getSingleton().getByName(name.str()); + return true; } } diff --git a/components/terrain/material.hpp b/components/terrain/material.hpp index b79df9f48..47c5142c9 100644 --- a/components/terrain/material.hpp +++ b/components/terrain/material.hpp @@ -22,51 +22,55 @@ #ifndef COMPONENTS_TERRAIN_MATERIAL_H #define COMPONENTS_TERRAIN_MATERIAL_H -#include +#include +#include -#include "storage.hpp" +#include "defs.hpp" + +namespace osg +{ + class Texture2D; +} namespace Terrain { - class MaterialGenerator + class FixedFunctionTechnique : public osgFX::Technique { public: - MaterialGenerator (); - - void setLayerList (const std::vector& layerList) { mLayerList = layerList; } - bool hasLayers() { return mLayerList.size() > 0; } - void setBlendmapList (const std::vector& blendmapList) { mBlendmapList = blendmapList; } - const std::vector& getBlendmapList() { return mBlendmapList; } - void setCompositeMap (const std::string& name) { mCompositeMap = name; } + FixedFunctionTechnique( + const std::vector >& layers, + const std::vector >& blendmaps); - void enableShaders(bool shaders) { mShaders = shaders; } - void enableShadows(bool shadows) { mShadows = shadows; } - void enableNormalMapping(bool normalMapping) { mNormalMapping = normalMapping; } - void enableParallaxMapping(bool parallaxMapping) { mParallaxMapping = parallaxMapping; } - void enableSplitShadows(bool splitShadows) { mSplitShadows = splitShadows; } + protected: + virtual void define_passes() {} + }; - /// Creates a material suitable for displaying a chunk of terrain using alpha-blending. - Ogre::MaterialPtr generate (); + class Effect : public osgFX::Effect + { + public: + Effect( + const std::vector >& layers, + const std::vector >& blendmaps); - /// Creates a material suitable for displaying a chunk of terrain using a ready-made composite map. - Ogre::MaterialPtr generateForCompositeMap (); + virtual bool define_techniques(); - /// Creates a material suitable for rendering composite maps, i.e. for "baking" several layer textures - /// into one. The main difference compared to a normal material is that no shading is applied at this point. - Ogre::MaterialPtr generateForCompositeMapRTT (); + virtual const char *effectName() const + { + return NULL; + } + virtual const char *effectDescription() const + { + return NULL; + } + virtual const char *effectAuthor() const + { + return NULL; + } private: - Ogre::MaterialPtr create (bool renderCompositeMap, bool displayCompositeMap); - - std::vector mLayerList; - std::vector mBlendmapList; - std::string mCompositeMap; - bool mShaders; - bool mShadows; - bool mSplitShadows; - bool mNormalMapping; - bool mParallaxMapping; + std::vector > mLayers; + std::vector > mBlendmaps; }; } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp deleted file mode 100644 index 89e5e34a3..000000000 --- a/components/terrain/quadtreenode.cpp +++ /dev/null @@ -1,611 +0,0 @@ -/* - * Copyright (c) 2015 scrawl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#include "quadtreenode.hpp" - -#include -#include -#include -#include -#include - -#include "defaultworld.hpp" -#include "chunk.hpp" -#include "storage.hpp" -#include "buffercache.hpp" -#include "material.hpp" - -using namespace Terrain; - -namespace -{ - int Log2( int n ) - { - assert(n > 0); - int targetlevel = 0; - while (n >>= 1) ++targetlevel; - return targetlevel; - } - - // Utility functions for neighbour finding algorithm - ChildDirection reflect(ChildDirection dir, Direction dir2) - { - 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]; - } - - 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]; - } - - // Algorithm described by Hanan Samet - 'Neighbour Finding in Quadtrees' - // http://www.cs.umd.edu/~hjs/pubs/SametPRIP81.pdf - QuadTreeNode* searchNeighbourRecursive (QuadTreeNode* currentNode, Direction dir) - { - if (!currentNode->getParent()) - return NULL; // Arrived at root node, the root node does not have neighbours - - QuadTreeNode* nextNode; - if (adjacent(currentNode->getDirection(), dir)) - nextNode = searchNeighbourRecursive(currentNode->getParent(), dir); - else - nextNode = currentNode->getParent(); - - if (nextNode && nextNode->hasChildren()) - return nextNode->getChild(reflect(currentNode->getDirection(), dir)); - else - return NULL; - } - - // Create a 2D quad - void makeQuad(Ogre::SceneManager* sceneMgr, float left, float top, float right, float bottom, Ogre::MaterialPtr material) - { - Ogre::ManualObject* manual = sceneMgr->createManualObject(); - - // Use identity view/projection matrices to get a 2d quad - manual->setUseIdentityProjection(true); - manual->setUseIdentityView(true); - - manual->begin(material->getName()); - - float normLeft = left*2-1; - float normTop = top*2-1; - float normRight = right*2-1; - float normBottom = bottom*2-1; - - manual->position(normLeft, normTop, 0.0); - manual->textureCoord(0, 1); - manual->position(normRight, normTop, 0.0); - manual->textureCoord(1, 1); - manual->position(normRight, normBottom, 0.0); - manual->textureCoord(1, 0); - manual->position(normLeft, normBottom, 0.0); - manual->textureCoord(0, 0); - - manual->quad(0,1,2,3); - - manual->end(); - - Ogre::AxisAlignedBox aabInf; - aabInf.setInfinite(); - manual->setBoundingBox(aabInf); - - sceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(manual); - } -} - -QuadTreeNode::QuadTreeNode(DefaultWorld* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent) - : mMaterialGenerator(NULL) - , mLoadState(LS_Unloaded) - , mIsDummy(false) - , mSize(size) - , mLodLevel(Log2(static_cast(mSize))) - , mBounds(Ogre::AxisAlignedBox::BOX_NULL) - , mWorldBounds(Ogre::AxisAlignedBox::BOX_NULL) - , mDirection(dir) - , mCenter(center) - , mSceneNode(NULL) - , mParent(parent) - , mChunk(NULL) - , mTerrain(terrain) -{ - mBounds.setNull(); - for (int i=0; i<4; ++i) - mChildren[i] = NULL; - for (int i=0; i<4; ++i) - mNeighbours[i] = NULL; - - if (mDirection == Root) - mSceneNode = mTerrain->getRootSceneNode(); - else - mSceneNode = mTerrain->getSceneManager()->createSceneNode(); - Ogre::Vector2 pos (0,0); - if (mParent) - pos = mParent->getCenter(); - pos = mCenter - pos; - float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); - - Ogre::Vector3 sceneNodePos (pos.x*cellWorldSize, pos.y*cellWorldSize, 0); - mTerrain->convertPosition(sceneNodePos); - - mSceneNode->setPosition(sceneNodePos); - - mMaterialGenerator = new MaterialGenerator(); - mMaterialGenerator->enableShaders(mTerrain->getShadersEnabled()); -} - -void QuadTreeNode::createChild(ChildDirection id, float size, const Ogre::Vector2 ¢er) -{ - mChildren[id] = new QuadTreeNode(mTerrain, id, size, center, this); -} - -QuadTreeNode::~QuadTreeNode() -{ - for (int i=0; i<4; ++i) - delete mChildren[i]; - delete mChunk; - delete mMaterialGenerator; -} - -QuadTreeNode* QuadTreeNode::getNeighbour(Direction dir) -{ - return mNeighbours[static_cast(dir)]; -} - -void QuadTreeNode::initNeighbours() -{ - for (int i=0; i<4; ++i) - mNeighbours[i] = searchNeighbourRecursive(this, (Direction)i); - - if (hasChildren()) - for (int i=0; i<4; ++i) - mChildren[i]->initNeighbours(); -} - -void QuadTreeNode::initAabb() -{ - float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); - if (hasChildren()) - { - for (int i=0; i<4; ++i) - { - mChildren[i]->initAabb(); - mBounds.merge(mChildren[i]->getBoundingBox()); - } - float minH, maxH; - switch (mTerrain->getAlign()) - { - case Terrain::Align_XY: - minH = mBounds.getMinimum().z; - maxH = mBounds.getMaximum().z; - break; - case Terrain::Align_XZ: - minH = mBounds.getMinimum().y; - maxH = mBounds.getMaximum().y; - break; - case Terrain::Align_YZ: - minH = mBounds.getMinimum().x; - maxH = mBounds.getMaximum().x; - break; - } - Ogre::Vector3 min(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, minH); - Ogre::Vector3 max(Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, maxH)); - mBounds = Ogre::AxisAlignedBox (min, max); - mTerrain->convertBounds(mBounds); - } - Ogre::Vector3 offset(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0); - mTerrain->convertPosition(offset); - mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + offset, - mBounds.getMaximum() + offset); -} - -void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box) -{ - mBounds = box; -} - -const Ogre::AxisAlignedBox& QuadTreeNode::getBoundingBox() -{ - return mBounds; -} - -const Ogre::AxisAlignedBox& QuadTreeNode::getWorldBoundingBox() -{ - return mWorldBounds; -} - -bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos) -{ - if (isDummy()) - return true; - - if (mBounds.isNull()) - return true; - - float dist = mWorldBounds.distance(cameraPos); - - // Make sure our scene node is attached - if (!mSceneNode->isInSceneGraph()) - { - mParent->getSceneNode()->addChild(mSceneNode); - } - - // Simple LOD selection - /// \todo use error metrics? - size_t wantedLod = 0; - float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); - - if (dist > cellWorldSize*64) - wantedLod = 6; - else if (dist > cellWorldSize*32) - wantedLod = 5; - else if (dist > cellWorldSize*12) - wantedLod = 4; - else if (dist > cellWorldSize*5) - wantedLod = 3; - else if (dist > cellWorldSize*2) - wantedLod = 2; - else if (dist > cellWorldSize * 1.42) // < sqrt2 so the 3x3 grid around player is always highest lod - wantedLod = 1; - - bool wantToDisplay = mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod; - - if (wantToDisplay) - { - // Wanted LOD is small enough to render this node in one chunk - if (mLoadState == LS_Unloaded) - { - mLoadState = LS_Loading; - mTerrain->queueLoad(this); - return false; - } - - if (mLoadState == LS_Loaded) - { - // Additional (index buffer) LOD is currently disabled. - // This is due to a problem with the LOD selection when a node splits. - // After splitting, the distance is measured from the children's bounding boxes, which are possibly - // further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD - // than the original node. - // In short, we'd sometimes get a switch to a lesser detail when actually moving closer. - // This wouldn't be so bad, but unfortunately it also breaks LOD edge connections if a neighbour - // node hasn't split yet, and has a higher LOD than our node's child: - // ----- ----- ------------ - // | LOD | LOD | | - // | 1 | 1 | | - // |-----|-----| LOD 0 | - // | LOD | LOD | | - // | 0 | 0 | | - // ----- ----- ------------ - // To prevent this, nodes of the same size need to always select the same LOD, which is basically what we're - // doing here. - // But this "solution" does increase triangle overhead, so eventually we need to find a more clever way. - //mChunk->setAdditionalLod(wantedLod - mLodLevel); - - if (!mChunk->getVisible() && hasChildren()) - { - for (int i=0; i<4; ++i) - mChildren[i]->unload(true); - } - mChunk->setVisible(true); - - return true; - } - return false; // LS_Loading - } - - // We do not want to display this node - delegate to children if they are already loaded - if (!wantToDisplay && hasChildren()) - { - if (mChunk) - { - // Are children already loaded? - bool childrenLoaded = true; - for (int i=0; i<4; ++i) - if (!mChildren[i]->update(cameraPos)) - childrenLoaded = false; - - if (!childrenLoaded) - { - mChunk->setVisible(true); - // Make sure child scene nodes are detached until all children are loaded - mSceneNode->removeAllChildren(); - } - else - { - // Delegation went well, we can unload now - unload(); - - for (int i=0; i<4; ++i) - { - if (!mChildren[i]->getSceneNode()->isInSceneGraph()) - mSceneNode->addChild(mChildren[i]->getSceneNode()); - } - } - return true; - } - else - { - bool success = true; - for (int i=0; i<4; ++i) - success = mChildren[i]->update(cameraPos) & success; - return success; - } - } - return false; -} - -void QuadTreeNode::load(const LoadResponseData &data) -{ - assert (!mChunk); - - mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data.mPositions, data.mNormals, data.mColours); - mChunk->setVisibilityFlags(mTerrain->getVisibilityFlags()); - mChunk->setCastShadows(true); - mSceneNode->attachObject(mChunk); - - mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); - mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); - - if (mTerrain->areLayersLoaded()) - { - if (mSize == 1) - { - mChunk->setMaterial(mMaterialGenerator->generate()); - } - else - { - ensureCompositeMap(); - mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); - mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap()); - } - } - // else: will be loaded in loadMaterials() after background thread has finished loading layers - mChunk->setVisible(false); - - mLoadState = LS_Loaded; -} - -void QuadTreeNode::unload(bool recursive) -{ - if (mChunk) - { - mSceneNode->detachObject(mChunk); - - delete mChunk; - mChunk = NULL; - - if (!mCompositeMap.isNull()) - { - Ogre::TextureManager::getSingleton().remove(mCompositeMap->getName()); - mCompositeMap.setNull(); - } - - // Do *not* set this when we are still loading! - mLoadState = LS_Unloaded; - } - - if (recursive && hasChildren()) - { - for (int i=0; i<4; ++i) - mChildren[i]->unload(true); - } -} - -void QuadTreeNode::updateIndexBuffers() -{ - if (hasChunk()) - { - // Fetch a suitable index buffer (which may be shared) - size_t ourLod = getActualLodLevel(); - - unsigned int flags = 0; - - for (int i=0; i<4; ++i) - { - QuadTreeNode* neighbour = getNeighbour((Direction)i); - - // If the neighbour isn't currently rendering itself, - // go up until we find one. NOTE: We don't need to go down, - // because in that case neighbour's detail would be higher than - // our detail and the neighbour would handle stitching by itself. - while (neighbour && !neighbour->hasChunk()) - neighbour = neighbour->getParent(); - size_t lod = 0; - if (neighbour) - lod = neighbour->getActualLodLevel(); - if (lod <= ourLod) // We only need to worry about neighbours less detailed than we are - - lod = 0; // neighbours with more detail will do the stitching themselves - // Use 4 bits for each LOD delta - if (lod > 0) - { - assert (lod - ourLod < (1 << 4)); - flags |= static_cast(lod - ourLod) << (4*i); - } - } - flags |= 0 /*((int)mAdditionalLod)*/ << (4*4); - - mChunk->setIndexBuffer(mTerrain->getBufferCache().getIndexBuffer(flags)); - } - else if (hasChildren()) - { - for (int i=0; i<4; ++i) - mChildren[i]->updateIndexBuffers(); - } -} - -bool QuadTreeNode::hasChunk() -{ - return mSceneNode->isInSceneGraph() && mChunk && mChunk->getVisible(); -} - -size_t QuadTreeNode::getActualLodLevel() -{ - assert(hasChunk() && "Can't get actual LOD level if this node has no render chunk"); - return mLodLevel /* + mChunk->getAdditionalLod() */; -} - -void QuadTreeNode::loadLayers(const LayerCollection& collection) -{ - assert (!mMaterialGenerator->hasLayers()); - - std::vector blendTextures; - for (std::vector::const_iterator it = collection.mBlendmaps.begin(); it != collection.mBlendmaps.end(); ++it) - { - // TODO: clean up blend textures on destruction - static int count=0; - Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/" - + Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, it->getWidth(), it->getHeight(), 0, it->format); - - Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(it->data, it->getWidth()*it->getHeight()*Ogre::PixelUtil::getNumElemBytes(it->format), true)); - map->loadRawData(stream, it->getWidth(), it->getHeight(), it->format); - blendTextures.push_back(map); - } - - mMaterialGenerator->setLayerList(collection.mLayers); - mMaterialGenerator->setBlendmapList(blendTextures); -} - -void QuadTreeNode::loadMaterials() -{ - if (isDummy()) - return; - - // Load children first since we depend on them when creating a composite map - if (hasChildren()) - { - for (int i=0; i<4; ++i) - mChildren[i]->loadMaterials(); - } - - if (mChunk) - { - if (mSize == 1) - { - mChunk->setMaterial(mMaterialGenerator->generate()); - } - else - { - ensureCompositeMap(); - mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); - mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap()); - } - } -} - -void QuadTreeNode::prepareForCompositeMap(Ogre::TRect area) -{ - Ogre::SceneManager* sceneMgr = mTerrain->getCompositeMapSceneManager(); - - if (mIsDummy) - { - // TODO - store this default material somewhere instead of creating one for each empty cell - MaterialGenerator matGen; - matGen.enableShaders(mTerrain->getShadersEnabled()); - std::vector layer; - layer.push_back(mTerrain->getStorage()->getDefaultLayer()); - matGen.setLayerList(layer); - makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, matGen.generateForCompositeMapRTT()); - return; - } - if (mSize > 1) - { - assert(hasChildren()); - - // 0,0 -------- 1,0 - // | | | - // |-----|------| - // | | | - // 0,1 -------- 1,1 - - float halfW = area.width()/2.f; - float halfH = area.height()/2.f; - mChildren[NW]->prepareForCompositeMap(Ogre::TRect(area.left, area.top, area.right-halfW, area.bottom-halfH)); - mChildren[NE]->prepareForCompositeMap(Ogre::TRect(area.left+halfW, area.top, area.right, area.bottom-halfH)); - mChildren[SW]->prepareForCompositeMap(Ogre::TRect(area.left, area.top+halfH, area.right-halfW, area.bottom)); - mChildren[SE]->prepareForCompositeMap(Ogre::TRect(area.left+halfW, area.top+halfH, area.right, area.bottom)); - } - else - { - // TODO: when to destroy? - Ogre::MaterialPtr material = mMaterialGenerator->generateForCompositeMapRTT(); - makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, material); - } -} - -void QuadTreeNode::ensureCompositeMap() -{ - if (!mCompositeMap.isNull()) - return; - - static int i=0; - std::stringstream name; - name << "terrain/comp" << i++; - - const int size = 128; - mCompositeMap = Ogre::TextureManager::getSingleton().createManual( - name.str(), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, size, size, Ogre::MIP_DEFAULT, Ogre::PF_A8B8G8R8); - - // Create quads for each cell - prepareForCompositeMap(Ogre::TRect(0,0,1,1)); - - mTerrain->renderCompositeMap(mCompositeMap); - - mTerrain->clearCompositeMapSceneManager(); - -} - -void QuadTreeNode::applyMaterials() -{ - if (mChunk) - { - mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled()); - mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled()); - if (mSize <= 1) - mChunk->setMaterial(mMaterialGenerator->generate()); - else - mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap()); - } - if (hasChildren()) - for (int i=0; i<4; ++i) - mChildren[i]->applyMaterials(); -} diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp deleted file mode 100644 index e44b64600..000000000 --- a/components/terrain/quadtreenode.hpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2015 scrawl - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef COMPONENTS_TERRAIN_QUADTREENODE_H -#define COMPONENTS_TERRAIN_QUADTREENODE_H - -#include -#include -#include - -#include "defs.hpp" - -namespace Ogre -{ - class Rectangle2D; -} - -namespace Terrain -{ - class DefaultWorld; - class Chunk; - class MaterialGenerator; - struct LoadResponseData; - - enum ChildDirection - { - NW = 0, - NE = 1, - SW = 2, - SE = 3, - Root - }; - - enum LoadState - { - LS_Unloaded, - LS_Loading, - LS_Loaded - }; - - /** - * @brief A node in the quad tree for our terrain. Depending on LOD, - * a node can either choose to render itself in one batch (merging its children), - * or delegate the render process to its children, rendering each child in at least one batch. - */ - class QuadTreeNode - { - public: - /// @param terrain - /// @param dir relative to parent, or Root if we are the root node - /// @param size size (in *cell* units!) - /// @param center center (in *cell* units!) - /// @param parent parent node - QuadTreeNode (DefaultWorld* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent); - ~QuadTreeNode(); - - /// Rebuild all materials - void applyMaterials(); - - /// Initialize neighbours - do this after the quadtree is built - void initNeighbours(); - /// Initialize bounding boxes of non-leafs by merging children bounding boxes. - /// Do this after the quadtree is built - note that leaf bounding boxes - /// need to be set first via setBoundingBox! - void initAabb(); - - /// @note takes ownership of \a child - void createChild (ChildDirection id, float size, const Ogre::Vector2& center); - - /// Mark this node as a dummy node. This can happen if the terrain size isn't a power of two. - /// For the QuadTree to work, we need to round the size up to a power of two, which means we'll - /// end up with empty nodes that don't actually render anything. - void markAsDummy() { mIsDummy = true; } - bool isDummy() { return mIsDummy; } - - QuadTreeNode* getParent() { return mParent; } - - Ogre::SceneNode* getSceneNode() { return mSceneNode; } - - int getSize() { return static_cast(mSize); } - Ogre::Vector2 getCenter() { return mCenter; } - - bool hasChildren() { return mChildren[0] != 0; } - QuadTreeNode* getChild(ChildDirection dir) { return mChildren[dir]; } - - /// Get neighbour node in this direction - QuadTreeNode* getNeighbour (Direction dir); - - /// Returns our direction relative to the parent node, or Root if we are the root node. - ChildDirection getDirection() { return mDirection; } - - /// Set bounding box in local coordinates. Should be done at load time for leaf nodes. - /// Other nodes can merge AABB of child nodes. - void setBoundingBox (const Ogre::AxisAlignedBox& box); - - /// Get bounding box in local coordinates - const Ogre::AxisAlignedBox& getBoundingBox(); - - const Ogre::AxisAlignedBox& getWorldBoundingBox(); - - DefaultWorld* getTerrain() { return mTerrain; } - - /// Adjust LODs for the given camera position, possibly splitting up chunks or merging them. - /// @return Did we (or all of our children) choose to render? - bool update (const Ogre::Vector3& cameraPos); - - /// Adjust index buffers of chunks to stitch together chunks of different LOD, so that cracks are avoided. - /// Call after QuadTreeNode::update! - void updateIndexBuffers(); - - /// Destroy chunks rendered by this node *and* its children (if param is true) - void destroyChunks(bool children); - - /// Get the effective LOD level if this node was rendered in one chunk - /// with Storage::getCellVertices^2 vertices - size_t getNativeLodLevel() { return mLodLevel; } - - /// Get the effective current LOD level used by the chunk rendering this node - size_t getActualLodLevel(); - - /// Is this node currently configured to render itself? - bool hasChunk(); - - /// Add a textured quad to a specific 2d area in the composite map scenemanager. - /// Only nodes with size <= 1 can be rendered with alpha blending, so larger nodes will simply - /// call this method on their children. - /// @note Do not call this before World::areLayersLoaded() == true - /// @param area area in image space to put the quad - void prepareForCompositeMap(Ogre::TRect area); - - /// Create a chunk for this node from the given data. - void load (const LoadResponseData& data); - void unload(bool recursive=false); - void loadLayers (const LayerCollection& collection); - /// This is recursive! Call it once on the root node after all leafs have loaded layers. - void loadMaterials(); - - LoadState getLoadState() { return mLoadState; } - - private: - // Stored here for convenience in case we need layer list again - MaterialGenerator* mMaterialGenerator; - - LoadState mLoadState; - - bool mIsDummy; - float mSize; - size_t mLodLevel; // LOD if we were to render this node in one chunk - Ogre::AxisAlignedBox mBounds; - Ogre::AxisAlignedBox mWorldBounds; - ChildDirection mDirection; - Ogre::Vector2 mCenter; - - Ogre::SceneNode* mSceneNode; - - QuadTreeNode* mParent; - QuadTreeNode* mChildren[4]; - QuadTreeNode* mNeighbours[4]; - - Chunk* mChunk; - - DefaultWorld* mTerrain; - - Ogre::TexturePtr mCompositeMap; - - void ensureCompositeMap(); - }; - -} - -#endif diff --git a/components/terrain/storage.cpp b/components/terrain/storage.cpp index 14009127d..857713a82 100644 --- a/components/terrain/storage.cpp +++ b/components/terrain/storage.cpp @@ -19,3 +19,5 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + +#include "storage.hpp" diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 7846e91c6..a302c8f8c 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -22,10 +22,20 @@ #ifndef COMPONENTS_TERRAIN_STORAGE_H #define COMPONENTS_TERRAIN_STORAGE_H -#include +#include + +#include +#include +#include +#include #include "defs.hpp" +namespace osg +{ + class Image; +} + namespace Terrain { /// We keep storage of terrain data abstract here since we need different implementations for game and editor @@ -46,7 +56,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 Ogre::Vector2& 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! @@ -59,11 +69,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 Ogre::Vector2& center, Terrain::Alignment align, - std::vector& positions, - std::vector& normals, - std::vector& colours) = 0; + virtual void fillVertexBuffers (int lodLevel, float size, const osg::Vec2f& center, + osg::ref_ptr positions, + osg::ref_ptr normals, + osg::ref_ptr colours) = 0; + typedef std::vector > 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. @@ -75,23 +86,11 @@ namespace Terrain /// can utilize packing, FFP can't. /// @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 Ogre::Vector2& chunkCenter, bool pack, - std::vector& blendmaps, + virtual void getBlendmaps (float chunkSize, const osg::Vec2f& chunkCenter, bool pack, + ImageVector& blendmaps, std::vector& layerList) = 0; - /// Retrieve pixel data for textures holding layer blend values for terrain chunks and layer texture information. - /// This variant is provided to eliminate the overhead of virtual function calls when retrieving a large number of blendmaps at once. - /// @note The terrain chunks 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. - /// @note May be called from background threads. Make sure to only call thread-safe functions from here! - /// @param nodes A collection of nodes for which to retrieve the aforementioned data - /// @param out Output vector - /// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) - - /// otherwise, each texture contains blend values for one layer only. Shader-based rendering - /// can utilize packing, FFP can't. - virtual void getBlendmaps (const std::vector& nodes, std::vector& out, bool pack) = 0; - - virtual float getHeightAt (const Ogre::Vector3& worldPos) = 0; + virtual float getHeightAt (const osg::Vec3f& worldPos) = 0; virtual LayerInfo getDefaultLayer() = 0; diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index bb99ca23e..e99126a2a 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -19,22 +19,54 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #include "terraingrid.hpp" -#include -#include -#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include + +#include -#include "chunk.hpp" +#include "material.hpp" +#include "storage.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 { -TerrainGrid::TerrainGrid(Ogre::SceneManager *sceneMgr, Terrain::Storage *storage, int visibilityFlags, bool shaders, Terrain::Alignment align) - : Terrain::World(sceneMgr, storage, visibilityFlags, shaders, align) - , mVisible(true) +TerrainGrid::TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, + Storage* storage, int nodeMask) + : Terrain::World(parent, resourceSystem, ico, storage, nodeMask) { - mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); } TerrainGrid::~TerrainGrid() @@ -43,143 +75,118 @@ TerrainGrid::~TerrainGrid() { unloadCell(mGrid.begin()->first.first, mGrid.begin()->first.second); } - - mSceneMgr->destroySceneNode(mRootNode); } -void TerrainGrid::update(const Ogre::Vector3 &cameraPos) +class GridElement { -} +public: + osg::ref_ptr mNode; +}; void TerrainGrid::loadCell(int x, int y) { if (mGrid.find(std::make_pair(x, y)) != mGrid.end()) return; // already loaded - Ogre::Vector2 center(x+0.5f, y+0.5f); + osg::Vec2f center(x+0.5f, y+0.5f); float minH, maxH; if (!mStorage->getMinMaxHeights(1, center, minH, maxH)) return; // no terrain defined - Ogre::Vector3 min (-0.5f*mStorage->getCellWorldSize(), - -0.5f*mStorage->getCellWorldSize(), - minH); - Ogre::Vector3 max (0.5f*mStorage->getCellWorldSize(), - 0.5f*mStorage->getCellWorldSize(), - maxH); - - Ogre::AxisAlignedBox bounds(min, max); + std::auto_ptr element (new GridElement); - GridElement element; + osg::Vec2f worldCenter = center*mStorage->getCellWorldSize(); + element->mNode = new osg::PositionAttitudeTransform; + element->mNode->setPosition(osg::Vec3f(worldCenter.x(), worldCenter.y(), 0.f)); + mTerrainRoot->addChild(element->mNode); - Ogre::Vector2 worldCenter = center*mStorage->getCellWorldSize(); - element.mSceneNode = mRootNode->createChildSceneNode(Ogre::Vector3(worldCenter.x, worldCenter.y, 0)); + osg::ref_ptr positions (new osg::Vec3Array); + osg::ref_ptr normals (new osg::Vec3Array); + osg::ref_ptr colors (new osg::Vec4Array); - std::vector positions; - std::vector normals; - std::vector colours; - mStorage->fillVertexBuffers(0, 1, center, mAlign, positions, normals, colours); + mStorage->fillVertexBuffers(0, 1, center, positions, normals, colors); - element.mChunk = new Terrain::Chunk(mCache.getUVBuffer(), bounds, positions, normals, colours); - element.mChunk->setIndexBuffer(mCache.getIndexBuffer(0)); - element.mChunk->setVisibilityFlags(mVisibilityFlags); - element.mChunk->setCastShadows(true); + osg::ref_ptr geometry (new osg::Geometry); + 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); - std::vector blendmaps; - std::vector layerList; - mStorage->getBlendmaps(1, center, mShaders, blendmaps, layerList); + geometry->addPrimitiveSet(mCache.getIndexBuffer(0)); - element.mMaterialGenerator.setLayerList(layerList); - - // upload blendmaps to GPU - std::vector blendTextures; - for (std::vector::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) - { - static int count=0; - Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/" - + Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, it->getWidth(), it->getHeight(), 0, it->format); - - Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(it->data, it->getWidth()*it->getHeight()*Ogre::PixelUtil::getNumElemBytes(it->format), true)); - map->loadRawData(stream, it->getWidth(), it->getHeight(), it->format); - blendTextures.push_back(map); - } + // we already know the bounding box, so no need to let OSG compute it. + osg::Vec3f min(-0.5f*mStorage->getCellWorldSize(), + -0.5f*mStorage->getCellWorldSize(), + minH); + osg::Vec3f max (0.5f*mStorage->getCellWorldSize(), + 0.5f*mStorage->getCellWorldSize(), + maxH); + osg::BoundingBox bounds(min, max); + geometry->setComputeBoundingBoxCallback(new StaticBoundingBoxCallback(bounds)); - element.mMaterialGenerator.setBlendmapList(blendTextures); + osg::ref_ptr geode (new osg::Geode); + geode->addDrawable(geometry); - element.mSceneNode->attachObject(element.mChunk); - updateMaterial(element); + std::vector layerList; + std::vector > blendmaps; + mStorage->getBlendmaps(1.f, center, false, blendmaps, layerList); - mGrid[std::make_pair(x,y)] = element; -} + // For compiling textures, I don't think the osgFX::Effect does it correctly + osg::ref_ptr textureCompileDummy (new osg::Node); -void TerrainGrid::updateMaterial(GridElement &element) -{ - element.mMaterialGenerator.enableShadows(getShadowsEnabled()); - element.mMaterialGenerator.enableSplitShadows(getSplitShadowsEnabled()); - element.mChunk->setMaterial(element.mMaterialGenerator.generate()); -} + std::vector > layerTextures; + for (std::vector::const_iterator it = layerList.begin(); it != layerList.end(); ++it) + { + layerTextures.push_back(mResourceSystem->getTextureManager()->getTexture2D(it->mDiffuseMap, osg::Texture::REPEAT, osg::Texture::REPEAT)); + textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(0, layerTextures.back()); + } -void TerrainGrid::unloadCell(int x, int y) -{ - Grid::iterator it = mGrid.find(std::make_pair(x,y)); - if (it == mGrid.end()) - return; + std::vector > blendmapTextures; + for (std::vector >::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) + { + osg::ref_ptr 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->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + texture->setResizeNonPowerOfTwoHint(false); + blendmapTextures.push_back(texture); + + textureCompileDummy->getOrCreateStateSet()->setTextureAttributeAndModes(0, layerTextures.back()); + } - GridElement& element = it->second; - delete element.mChunk; - element.mChunk = NULL; + for (unsigned int i=0; isetTexCoordArray(i, mCache.getUVBuffer()); - const std::vector& blendmaps = element.mMaterialGenerator.getBlendmapList(); - for (std::vector::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it) - Ogre::TextureManager::getSingleton().remove((*it)->getName()); + osg::ref_ptr effect (new Terrain::Effect(layerTextures, blendmapTextures)); - mSceneMgr->destroySceneNode(element.mSceneNode); - element.mSceneNode = NULL; + effect->addCullCallback(new SceneUtil::LightListCallback); - mGrid.erase(it); -} + effect->addChild(geode); + element->mNode->addChild(effect); -void TerrainGrid::applyMaterials(bool shadows, bool splitShadows) -{ - mShadows = shadows; - mSplitShadows = splitShadows; - for (Grid::iterator it = mGrid.begin(); it != mGrid.end(); ++it) + if (mIncrementalCompileOperation) { - updateMaterial(it->second); + mIncrementalCompileOperation->add(geode); + mIncrementalCompileOperation->add(textureCompileDummy); } -} -bool TerrainGrid::getVisible() -{ - return mVisible; -} - -void TerrainGrid::setVisible(bool visible) -{ - mVisible = visible; - mRootNode->setVisible(visible); + mGrid[std::make_pair(x,y)] = element.release(); } -Ogre::AxisAlignedBox TerrainGrid::getWorldBoundingBox (const Ogre::Vector2& center) +void TerrainGrid::unloadCell(int x, int y) { - int cellX = static_cast(std::floor(center.x)); - int cellY = static_cast(std::floor(center.y)); - - Grid::iterator it = mGrid.find(std::make_pair(cellX, cellY)); + Grid::iterator it = mGrid.find(std::make_pair(x,y)); if (it == mGrid.end()) - return Ogre::AxisAlignedBox::BOX_NULL; - - Terrain::Chunk* chunk = it->second.mChunk; - Ogre::SceneNode* node = it->second.mSceneNode; - Ogre::AxisAlignedBox box = chunk->getBoundingBox(); - box = Ogre::AxisAlignedBox(box.getMinimum() + node->getPosition(), box.getMaximum() + node->getPosition()); - return box; -} + return; -void TerrainGrid::syncLoad() -{ + GridElement* element = it->second; + mTerrainRoot->removeChild(element->mNode); + delete element; + mGrid.erase(it); } } diff --git a/components/terrain/terraingrid.hpp b/components/terrain/terraingrid.hpp index 97ef6d14d..3a6d71793 100644 --- a/components/terrain/terraingrid.hpp +++ b/components/terrain/terraingrid.hpp @@ -27,64 +27,23 @@ namespace Terrain { - class Chunk; - struct GridElement - { - Ogre::SceneNode* mSceneNode; - - Terrain::MaterialGenerator mMaterialGenerator; - - Terrain::Chunk* mChunk; - }; + class GridElement; /// @brief Simple terrain implementation that loads cells in a grid, with no LOD class TerrainGrid : public Terrain::World { public: - /// @note takes ownership of \a storage - /// @param sceneMgr scene manager to use - /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) - /// @param visbilityFlags visibility flags for the created meshes - /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually - /// faster so this is just here for compatibility. - /// @param align The align of the terrain, see Alignment enum - TerrainGrid(Ogre::SceneManager* sceneMgr, - Terrain::Storage* storage, int visibilityFlags, bool shaders, Terrain::Alignment align); + TerrainGrid(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, + Storage* storage, int nodeMask); ~TerrainGrid(); - /// Update chunk LODs according to this camera position - virtual void update (const Ogre::Vector3& cameraPos); - virtual void loadCell(int x, int y); virtual void unloadCell(int x, int y); - /// Get the world bounding box of a chunk of terrain centered at \a center - virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center); - - /// Show or hide the whole terrain - /// @note this setting may be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden - virtual void setVisible(bool visible); - virtual bool getVisible(); - - /// Recreate materials used by terrain chunks. This should be called whenever settings of - /// the material factory are changed. (Relying on the factory to update those materials is not - /// enough, since turning a feature on/off can change the number of texture units available for layer/blend - /// textures, and to properly respond to this we may need to change the structure of the material, such as - /// adding or removing passes. This can only be achieved by a full rebuild.) - virtual void applyMaterials(bool shadows, bool splitShadows); - - /// Wait until all background loading is complete. - virtual void syncLoad(); - private: - void updateMaterial (GridElement& element); - - typedef std::map, GridElement> Grid; + typedef std::map, GridElement*> Grid; Grid mGrid; - - Ogre::SceneNode* mRootNode; - bool mVisible; }; } diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 3baaaed44..1cfcc80ac 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -21,63 +21,39 @@ */ #include "world.hpp" -#include +#include +#include #include "storage.hpp" namespace Terrain { -World::World(Ogre::SceneManager* sceneMgr, - Storage* storage, int visibilityFlags, bool shaders, Alignment align) - : mShaders(shaders) - , mShadows(false) - , mSplitShadows(false) - , mAlign(align) - , mStorage(storage) - , mVisibilityFlags(visibilityFlags) - , mSceneMgr(sceneMgr) +World::World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, + Storage* storage, int nodeMask) + : mStorage(storage) , mCache(storage->getCellVertices()) + , mParent(parent) + , mResourceSystem(resourceSystem) + , mIncrementalCompileOperation(ico) { + mTerrainRoot = new osg::Group; + mTerrainRoot->setNodeMask(nodeMask); + mTerrainRoot->getOrCreateStateSet()->setRenderingHint(osg::StateSet::OPAQUE_BIN); + + mParent->addChild(mTerrainRoot); } World::~World() { + mParent->removeChild(mTerrainRoot); + delete mStorage; } -float World::getHeightAt(const Ogre::Vector3 &worldPos) +float World::getHeightAt(const osg::Vec3f &worldPos) { return mStorage->getHeightAt(worldPos); } -void World::convertPosition(float &x, float &y, float &z) -{ - Terrain::convertPosition(mAlign, x, y, z); -} - -void World::convertPosition(Ogre::Vector3 &pos) -{ - convertPosition(pos.x, pos.y, pos.z); -} - -void World::convertBounds(Ogre::AxisAlignedBox& bounds) -{ - switch (mAlign) - { - case Align_XY: - return; - case Align_XZ: - convertPosition(bounds.getMinimum()); - convertPosition(bounds.getMaximum()); - // Because we changed sign of Z - std::swap(bounds.getMinimum().z, bounds.getMaximum().z); - return; - case Align_YZ: - convertPosition(bounds.getMinimum()); - convertPosition(bounds.getMaximum()); - return; - } -} - } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 3e63b4c93..70ec30410 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -22,14 +22,24 @@ #ifndef COMPONENTS_TERRAIN_WORLD_H #define COMPONENTS_TERRAIN_WORLD_H -#include +#include #include "defs.hpp" #include "buffercache.hpp" -namespace Ogre +namespace osg { - class SceneManager; + class Group; +} + +namespace osgUtil +{ + class IncrementalCompileOperation; +} + +namespace Resource +{ + class ResourceSystem; } namespace Terrain @@ -44,79 +54,31 @@ namespace Terrain { public: /// @note takes ownership of \a storage - /// @param sceneMgr scene manager to use /// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..) - /// @param visbilityFlags visibility flags for the created meshes - /// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually - /// faster so this is just here for compatibility. - /// @param align The align of the terrain, see Alignment enum - World(Ogre::SceneManager* sceneMgr, - Storage* storage, int visiblityFlags, bool shaders, Alignment align); + /// @param nodeMask mask for the terrain root + World(osg::Group* parent, Resource::ResourceSystem* resourceSystem, osgUtil::IncrementalCompileOperation* ico, + Storage* storage, int nodeMask); virtual ~World(); - bool getShadersEnabled() { return mShaders; } - bool getShadowsEnabled() { return mShadows; } - bool getSplitShadowsEnabled() { return mSplitShadows; } - - float getHeightAt (const Ogre::Vector3& worldPos); - - /// Update chunk LODs according to this camera position - /// @note Calling this method might lead to composite textures being rendered, so it is best - /// not to call it when render commands are still queued, since that would cause a flush. - virtual void update (const Ogre::Vector3& cameraPos) = 0; + float getHeightAt (const osg::Vec3f& worldPos); // This is only a hint and may be ignored by the implementation. virtual void loadCell(int x, int y) {} virtual void unloadCell(int x, int y) {} - /// Get the world bounding box of a chunk of terrain centered at \a center - virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center) = 0; - - Ogre::SceneManager* getSceneManager() { return mSceneMgr; } - Storage* getStorage() { return mStorage; } - /// Show or hide the whole terrain - /// @note this setting may be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden - virtual void setVisible(bool visible) = 0; - virtual bool getVisible() = 0; - - /// Recreate materials used by terrain chunks. This should be called whenever settings of - /// the material factory are changed. (Relying on the factory to update those materials is not - /// enough, since turning a feature on/off can change the number of texture units available for layer/blend - /// textures, and to properly respond to this we may need to change the structure of the material, such as - /// adding or removing passes. This can only be achieved by a full rebuild.) - virtual void applyMaterials(bool shadows, bool splitShadows) = 0; - - int getVisibilityFlags() { return mVisibilityFlags; } - - Alignment getAlign() { return mAlign; } - - /// Wait until all background loading is complete. - virtual void syncLoad() {} - protected: - bool mShaders; - bool mShadows; - bool mSplitShadows; - Alignment mAlign; - Storage* mStorage; - int mVisibilityFlags; - - Ogre::SceneManager* mSceneMgr; - BufferCache mCache; - public: - // ----INTERNAL---- - BufferCache& getBufferCache() { return mCache; } + osg::ref_ptr mParent; + osg::ref_ptr mTerrainRoot; + + Resource::ResourceSystem* mResourceSystem; - // Convert the given position from Z-up align, i.e. Align_XY to the wanted align set in mAlign - void convertPosition (float& x, float& y, float& z); - void convertPosition (Ogre::Vector3& pos); - void convertBounds (Ogre::AxisAlignedBox& bounds); + osg::ref_ptr mIncrementalCompileOperation; }; }