From 97c3efb3ba8dbcdc7ac503ecbdba8163830bb4b1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 26 Feb 2014 19:23:42 +0100 Subject: [PATCH] Terrain: decoupled Chunk from QuadTreeNode. --- components/CMakeLists.txt | 2 +- components/terrain/buffercache.cpp | 200 ++++++++++++++++++++++++++++ components/terrain/buffercache.hpp | 36 +++++ components/terrain/chunk.cpp | 76 ++--------- components/terrain/chunk.hpp | 17 +-- components/terrain/defs.hpp | 8 ++ components/terrain/quadtreenode.cpp | 39 +++++- components/terrain/quadtreenode.hpp | 10 +- components/terrain/world.cpp | 197 +-------------------------- components/terrain/world.hpp | 28 +--- 10 files changed, 304 insertions(+), 309 deletions(-) create mode 100644 components/terrain/buffercache.cpp create mode 100644 components/terrain/buffercache.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 938ef2e4d..0a99c9528 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -75,7 +75,7 @@ add_component_dir (translation add_definitions(-DTERRAIN_USE_SHADER=1) add_component_dir (terrain - quadtreenode chunk world storage material + quadtreenode chunk world storage material buffercache compositemap defs ) add_component_dir (loadinglistener diff --git a/components/terrain/buffercache.cpp b/components/terrain/buffercache.cpp new file mode 100644 index 000000000..f693c0e40 --- /dev/null +++ b/components/terrain/buffercache.cpp @@ -0,0 +1,200 @@ +#include "buffercache.hpp" + +#include + +#include "defs.hpp" + +namespace Terrain +{ + + Ogre::HardwareVertexBufferSharedPtr BufferCache::getUVBuffer() + { + if (mUvBufferMap.find(mNumVerts) != mUvBufferMap.end()) + { + return mUvBufferMap[mNumVerts]; + } + + int vertexCount = mNumVerts * mNumVerts; + + std::vector uvs; + uvs.reserve(vertexCount*2); + + 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 + } + } + + 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; + } + + Ogre::HardwareIndexBufferSharedPtr BufferCache::getIndexBuffer(int flags) + { + unsigned int verts = mNumVerts; + + if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) + { + return mIndexBufferMap[flags]; + } + + // LOD level n means every 2^n-th vertex is kept + size_t lodLevel = (flags >> (4*4)); + + size_t lodDeltas[4]; + for (int i=0; i<4; ++i) + lodDeltas[i] = (flags >> (4*i)) & (0xf); + + bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); + + size_t increment = 1 << lodLevel; + assert(increment < verts); + std::vector indices; + indices.reserve((verts-1)*(verts-1)*2*3 / increment); + + size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; + // If any edge needs stitching we'll skip all edges at this point, + // mainly because stitching one edge would have an effect on corners and on the adjacent edges + if (anyDeltas) + { + colStart += increment; + colEnd -= increment; + rowEnd -= increment; + rowStart += increment; + } + for (size_t row = rowStart; row < rowEnd; row += increment) + { + for (size_t col = colStart; col < colEnd; col += increment) + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row+increment); + indices.push_back(verts*col+row+increment); + + indices.push_back(verts*col+row); + indices.push_back(verts*(col+increment)+row); + indices.push_back(verts*(col+increment)+row+increment); + } + } + + size_t innerStep = increment; + if (anyDeltas) + { + // Now configure LOD transitions at the edges - this is pretty tedious, + // and some very long and boring code, but it works great + + // South + size_t row = 0; + size_t outerStep = 1 << (lodDeltas[South] + lodLevel); + for (size_t col = 0; col < verts-1; col += outerStep) + { + indices.push_back(verts*col+row); + indices.push_back(verts*(col+outerStep)+row); + // Make sure not to touch the right edge + if (col+outerStep == verts-1) + indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); + else + indices.push_back(verts*(col+outerStep)+row+innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == verts-1-innerStep) + continue; + indices.push_back(verts*(col)+row); + indices.push_back(verts*(col+i+innerStep)+row+innerStep); + indices.push_back(verts*(col+i)+row+innerStep); + } + } + + // North + row = verts-1; + outerStep = 1 << (lodDeltas[North] + lodLevel); + for (size_t col = 0; col < verts-1; col += outerStep) + { + indices.push_back(verts*(col+outerStep)+row); + indices.push_back(verts*col+row); + // Make sure not to touch the left edge + if (col == 0) + indices.push_back(verts*(col+innerStep)+row-innerStep); + else + indices.push_back(verts*col+row-innerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the left or right edges + if (col+i == 0 || col+i == verts-1-innerStep) + continue; + indices.push_back(verts*(col+i)+row-innerStep); + indices.push_back(verts*(col+i+innerStep)+row-innerStep); + indices.push_back(verts*(col+outerStep)+row); + } + } + + // West + size_t col = 0; + outerStep = 1 << (lodDeltas[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); + // Make sure not to touch the top edge + if (row+outerStep == verts-1) + indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); + else + indices.push_back(verts*(col+innerStep)+row+outerStep); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == verts-1-innerStep) + continue; + indices.push_back(verts*col+row); + indices.push_back(verts*(col+innerStep)+row+i); + indices.push_back(verts*(col+innerStep)+row+i+innerStep); + } + } + + // East + col = verts-1; + outerStep = 1 << (lodDeltas[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); + // Make sure not to touch the bottom edge + if (row == 0) + indices.push_back(verts*(col-innerStep)+row+innerStep); + else + indices.push_back(verts*(col-innerStep)+row); + + for (size_t i = 0; i < outerStep; i += innerStep) + { + // Make sure not to touch the top or bottom edges + if (row+i == 0 || row+i == verts-1-innerStep) + continue; + indices.push_back(verts*col+row+outerStep); + indices.push_back(verts*(col-innerStep)+row+i+innerStep); + indices.push_back(verts*(col-innerStep)+row+i); + } + } + } + + Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); + Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, + indices.size(), Ogre::HardwareBuffer::HBU_STATIC); + buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); + mIndexBufferMap[flags] = buffer; + return buffer; + } + +} diff --git a/components/terrain/buffercache.hpp b/components/terrain/buffercache.hpp new file mode 100644 index 000000000..f0aea9bfd --- /dev/null +++ b/components/terrain/buffercache.hpp @@ -0,0 +1,36 @@ +#ifndef COMPONENTS_TERRAIN_BUFFERCACHE_H +#define COMPONENTS_TERRAIN_BUFFERCACHE_H + +#include +#include + +#include + +namespace Terrain +{ + + /// @brief Implements creation and caching of vertex buffers for terrain chunks. + class BufferCache + { + public: + BufferCache(unsigned int numVerts) : mNumVerts(numVerts) {} + + /// @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 (int flags); + + Ogre::HardwareVertexBufferSharedPtr getUVBuffer (); + + 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 mUvBufferMap; + + unsigned int mNumVerts; + }; + +} + +#endif diff --git a/components/terrain/chunk.cpp b/components/terrain/chunk.cpp index 6f2b9ff73..cf6c99cf4 100644 --- a/components/terrain/chunk.cpp +++ b/components/terrain/chunk.cpp @@ -4,31 +4,17 @@ #include #include -#include "quadtreenode.hpp" -#include "world.hpp" -#include "storage.hpp" +#include "world.hpp" // FIXME: for LoadResponseData, move to backgroundloader.hpp namespace Terrain { - Chunk::Chunk(QuadTreeNode* node, const LoadResponseData& data) - : mNode(node) - , mVertexLod(node->getNativeLodLevel()) - , mAdditionalLod(0) + Chunk::Chunk(Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data) + : mBounds(bounds) { mVertexData = OGRE_NEW Ogre::VertexData; mVertexData->vertexStart = 0; - - unsigned int verts = mNode->getTerrain()->getStorage()->getCellVertices(); - - size_t lodLevel = mNode->getNativeLodLevel(); - - // Set the total number of vertices - size_t numVertsOneSide = mNode->getSize() * (verts-1); - numVertsOneSide /= 1 << lodLevel; - numVertsOneSide += 1; - assert(numVertsOneSide == verts); - mVertexData->vertexCount = numVertsOneSide * numVertsOneSide; + mVertexData->vertexCount = data.mPositions.size()/3; // Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc) Ogre::VertexDeclaration* vertexDecl = mVertexData->vertexDeclaration; @@ -40,15 +26,16 @@ namespace Terrain vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION); mVertexBuffer = 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); mNormalBuffer = 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); - Ogre::HardwareVertexBufferSharedPtr uvBuf = mNode->getTerrain()->getVertexBuffer(numVertsOneSide); // Colours vertexDecl->addElement(nextBuffer++, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE); @@ -61,54 +48,17 @@ namespace Terrain mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer); - mVertexData->vertexBufferBinding->setBinding(2, uvBuf); + mVertexData->vertexBufferBinding->setBinding(2, uvBuffer); mVertexData->vertexBufferBinding->setBinding(3, mColourBuffer); mIndexData = OGRE_NEW Ogre::IndexData(); mIndexData->indexStart = 0; } - - - void Chunk::updateIndexBuffer() + void Chunk::setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer) { - // Fetch a suitable index buffer (which may be shared) - size_t ourLod = mVertexLod + mAdditionalLod; - - int flags = 0; - - for (int i=0; i<4; ++i) - { - QuadTreeNode* neighbour = mNode->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 |= int(lod - ourLod) << (4*i); - } - } - - flags |= ((int)mAdditionalLod) << (4*4); - - size_t numIndices; - mIndexBuffer = mNode->getTerrain()->getIndexBuffer(flags, numIndices); - mIndexData->indexCount = numIndices; - mIndexData->indexBuffer = mIndexBuffer; + mIndexData->indexBuffer = buffer; + mIndexData->indexCount = buffer->getNumIndexes(); } Chunk::~Chunk() @@ -124,12 +74,12 @@ namespace Terrain const Ogre::AxisAlignedBox& Chunk::getBoundingBox(void) const { - return mNode->getBoundingBox(); + return mBounds; } Ogre::Real Chunk::getBoundingRadius(void) const { - return mNode->getBoundingBox().getHalfSize().length(); + return mBounds.getHalfSize().length(); } void Chunk::_updateRenderQueue(Ogre::RenderQueue* queue) @@ -150,7 +100,7 @@ namespace Terrain void Chunk::getRenderOperation(Ogre::RenderOperation& op) { - assert (!mIndexBuffer.isNull() && "Trying to render, but no index buffer set!"); + assert (!mIndexData->indexBuffer.isNull() && "Trying to render, but no index buffer set!"); op.useIndexes = true; op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; op.vertexData = mVertexData; diff --git a/components/terrain/chunk.hpp b/components/terrain/chunk.hpp index a6477df28..d198de264 100644 --- a/components/terrain/chunk.hpp +++ b/components/terrain/chunk.hpp @@ -7,7 +7,7 @@ namespace Terrain { - class QuadTreeNode; + class BufferCache; struct LoadResponseData; /** @@ -16,18 +16,13 @@ namespace Terrain class Chunk : public Ogre::Renderable, public Ogre::MovableObject { public: - Chunk (QuadTreeNode* node, const LoadResponseData& data); + Chunk (Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data); virtual ~Chunk(); void setMaterial (const Ogre::MaterialPtr& material); - /// Set additional LOD applied on top of vertex LOD. \n - /// This is achieved by changing the index buffer to omit vertices. - void setAdditionalLod (size_t lod) { mAdditionalLod = lod; } - size_t getAdditionalLod() { return mAdditionalLod; } - - void updateIndexBuffer(); + void setIndexBuffer(Ogre::HardwareIndexBufferSharedPtr buffer); // Inherited from MovableObject virtual const Ogre::String& getMovableType(void) const { static Ogre::String t = "MW_TERRAIN"; return t; } @@ -45,18 +40,14 @@ namespace Terrain virtual const Ogre::LightList& getLights(void) const; private: - QuadTreeNode* mNode; + Ogre::AxisAlignedBox mBounds; Ogre::MaterialPtr mMaterial; - size_t mVertexLod; - size_t mAdditionalLod; - Ogre::VertexData* mVertexData; Ogre::IndexData* mIndexData; Ogre::HardwareVertexBufferSharedPtr mVertexBuffer; Ogre::HardwareVertexBufferSharedPtr mNormalBuffer; Ogre::HardwareVertexBufferSharedPtr mColourBuffer; - Ogre::HardwareIndexBufferSharedPtr mIndexBuffer; }; } diff --git a/components/terrain/defs.hpp b/components/terrain/defs.hpp index c14608e76..1a081c969 100644 --- a/components/terrain/defs.hpp +++ b/components/terrain/defs.hpp @@ -36,6 +36,14 @@ namespace Terrain } } + enum Direction + { + North = 0, + East = 1, + South = 2, + West = 3 + }; + struct LayerInfo { std::string mDiffuseMap; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 1c8553967..913970606 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -9,7 +9,7 @@ #include "world.hpp" #include "chunk.hpp" #include "storage.hpp" - +#include "buffercache.hpp" #include "material.hpp" using namespace Terrain; @@ -372,7 +372,7 @@ void QuadTreeNode::load(const LoadResponseData &data) assert (!mChunk); std::cout << "loading " << std::endl; - mChunk = new Chunk(this, data); + mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data); mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); mChunk->setCastShadows(true); mSceneNode->attachObject(mChunk); @@ -429,7 +429,38 @@ void QuadTreeNode::unload() void QuadTreeNode::updateIndexBuffers() { if (hasChunk()) - mChunk->updateIndexBuffer(); + { + // Fetch a suitable index buffer (which may be shared) + size_t ourLod = getActualLodLevel(); + + 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 |= int(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) @@ -445,7 +476,7 @@ bool QuadTreeNode::hasChunk() size_t QuadTreeNode::getActualLodLevel() { assert(hasChunk() && "Can't get actual LOD level if this node has no render chunk"); - return mLodLevel + mChunk->getAdditionalLod(); + return mLodLevel /* + mChunk->getAdditionalLod() */; } void QuadTreeNode::ensureLayerInfo() diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 0bba1024c..134bc2d34 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -5,6 +5,8 @@ #include #include +#include "defs.hpp" + namespace Ogre { class Rectangle2D; @@ -17,14 +19,6 @@ namespace Terrain class MaterialGenerator; struct LoadResponseData; - enum Direction - { - North = 0, - East = 1, - South = 2, - West = 3 - }; - enum ChildDirection { NW = 0, diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index 4d4616511..69748a721 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -70,6 +69,7 @@ namespace Terrain , mMinY(0) , mChunksLoading(0) , mWorkQueueChannel(0) + , mCache(storage->getCellVertices()) { #if TERRAIN_USE_SHADER == 0 if (mShaders) @@ -197,201 +197,6 @@ namespace Terrain return node->getWorldBoundingBox(); } - Ogre::HardwareVertexBufferSharedPtr World::getVertexBuffer(int numVertsOneSide) - { - if (mUvBufferMap.find(numVertsOneSide) != mUvBufferMap.end()) - { - return mUvBufferMap[numVertsOneSide]; - } - - int vertexCount = numVertsOneSide * numVertsOneSide; - - std::vector uvs; - uvs.reserve(vertexCount*2); - - for (int col = 0; col < numVertsOneSide; ++col) - { - for (int row = 0; row < numVertsOneSide; ++row) - { - uvs.push_back(col / static_cast(numVertsOneSide-1)); // U - uvs.push_back(row / static_cast(numVertsOneSide-1)); // V - } - } - - 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[numVertsOneSide] = buffer; - return buffer; - } - - Ogre::HardwareIndexBufferSharedPtr World::getIndexBuffer(int flags, size_t& numIndices) - { - unsigned int verts = mStorage->getCellVertices(); - - if (mIndexBufferMap.find(flags) != mIndexBufferMap.end()) - { - numIndices = mIndexBufferMap[flags]->getNumIndexes(); - return mIndexBufferMap[flags]; - } - - // LOD level n means every 2^n-th vertex is kept - size_t lodLevel = (flags >> (4*4)); - - size_t lodDeltas[4]; - for (int i=0; i<4; ++i) - lodDeltas[i] = (flags >> (4*i)) & (0xf); - - bool anyDeltas = (lodDeltas[North] || lodDeltas[South] || lodDeltas[West] || lodDeltas[East]); - - size_t increment = 1 << lodLevel; - assert(increment < verts); - std::vector indices; - indices.reserve((verts-1)*(verts-1)*2*3 / increment); - - size_t rowStart = 0, colStart = 0, rowEnd = verts-1, colEnd = verts-1; - // If any edge needs stitching we'll skip all edges at this point, - // mainly because stitching one edge would have an effect on corners and on the adjacent edges - if (anyDeltas) - { - colStart += increment; - colEnd -= increment; - rowEnd -= increment; - rowStart += increment; - } - for (size_t row = rowStart; row < rowEnd; row += increment) - { - for (size_t col = colStart; col < colEnd; col += increment) - { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row+increment); - indices.push_back(verts*col+row+increment); - - indices.push_back(verts*col+row); - indices.push_back(verts*(col+increment)+row); - indices.push_back(verts*(col+increment)+row+increment); - } - } - - size_t innerStep = increment; - if (anyDeltas) - { - // Now configure LOD transitions at the edges - this is pretty tedious, - // and some very long and boring code, but it works great - - // South - size_t row = 0; - size_t outerStep = 1 << (lodDeltas[South] + lodLevel); - for (size_t col = 0; col < verts-1; col += outerStep) - { - indices.push_back(verts*col+row); - indices.push_back(verts*(col+outerStep)+row); - // Make sure not to touch the right edge - if (col+outerStep == verts-1) - indices.push_back(verts*(col+outerStep-innerStep)+row+innerStep); - else - indices.push_back(verts*(col+outerStep)+row+innerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == verts-1-innerStep) - continue; - indices.push_back(verts*(col)+row); - indices.push_back(verts*(col+i+innerStep)+row+innerStep); - indices.push_back(verts*(col+i)+row+innerStep); - } - } - - // North - row = verts-1; - outerStep = 1 << (lodDeltas[North] + lodLevel); - for (size_t col = 0; col < verts-1; col += outerStep) - { - indices.push_back(verts*(col+outerStep)+row); - indices.push_back(verts*col+row); - // Make sure not to touch the left edge - if (col == 0) - indices.push_back(verts*(col+innerStep)+row-innerStep); - else - indices.push_back(verts*col+row-innerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the left or right edges - if (col+i == 0 || col+i == verts-1-innerStep) - continue; - indices.push_back(verts*(col+i)+row-innerStep); - indices.push_back(verts*(col+i+innerStep)+row-innerStep); - indices.push_back(verts*(col+outerStep)+row); - } - } - - // West - size_t col = 0; - outerStep = 1 << (lodDeltas[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); - // Make sure not to touch the top edge - if (row+outerStep == verts-1) - indices.push_back(verts*(col+innerStep)+row+outerStep-innerStep); - else - indices.push_back(verts*(col+innerStep)+row+outerStep); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == verts-1-innerStep) - continue; - indices.push_back(verts*col+row); - indices.push_back(verts*(col+innerStep)+row+i); - indices.push_back(verts*(col+innerStep)+row+i+innerStep); - } - } - - // East - col = verts-1; - outerStep = 1 << (lodDeltas[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); - // Make sure not to touch the bottom edge - if (row == 0) - indices.push_back(verts*(col-innerStep)+row+innerStep); - else - indices.push_back(verts*(col-innerStep)+row); - - for (size_t i = 0; i < outerStep; i += innerStep) - { - // Make sure not to touch the top or bottom edges - if (row+i == 0 || row+i == verts-1-innerStep) - continue; - indices.push_back(verts*col+row+outerStep); - indices.push_back(verts*(col-innerStep)+row+i+innerStep); - indices.push_back(verts*(col-innerStep)+row+i); - } - } - } - - - - numIndices = indices.size(); - - Ogre::HardwareBufferManager* mgr = Ogre::HardwareBufferManager::getSingletonPtr(); - Ogre::HardwareIndexBufferSharedPtr buffer = mgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, - numIndices, Ogre::HardwareBuffer::HBU_STATIC); - buffer->writeData(0, buffer->getSizeInBytes(), &indices[0], true); - mIndexBufferMap[flags] = buffer; - return buffer; - } - void World::renderCompositeMap(Ogre::TexturePtr target) { mCompositeMapRenderTarget->update(); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index d552bccb0..354738744 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -1,13 +1,12 @@ #ifndef COMPONENTS_TERRAIN_H #define COMPONENTS_TERRAIN_H -#include -#include #include #include #include #include "defs.hpp" +#include "buffercache.hpp" namespace Ogre { @@ -125,25 +124,12 @@ namespace Terrain void buildQuadTree(QuadTreeNode* node); + BufferCache mCache; + public: // ----INTERNAL---- - - enum IndexBufferFlags - { - IBF_North = 1 << 0, - IBF_East = 1 << 1, - IBF_South = 1 << 2, - IBF_West = 1 << 3 - }; - - /// @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) - /// @param numIndices number of indices that were used will be written here - Ogre::HardwareIndexBufferSharedPtr getIndexBuffer (int flags, size_t& numIndices); - - Ogre::HardwareVertexBufferSharedPtr getVertexBuffer (int numVertsOneSide); - Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; } + BufferCache& getBufferCache() { return mCache; } // Delete all quads void clearCompositeMapSceneManager(); @@ -158,12 +144,6 @@ namespace Terrain void queueLoad (QuadTreeNode* node); 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 mUvBufferMap; - Ogre::RenderTarget* mCompositeMapRenderTarget; Ogre::TexturePtr mCompositeMapRenderTexture; };