Terrain: geometry is now loaded in background threads.

TODO: background load layer textures and blendmaps.
"Distant land" setting has been removed for now (i.e. always enabled).
actorid
scrawl 11 years ago
parent b3fed853ae
commit 195071efc7

@ -651,6 +651,11 @@ void RenderingManager::setGlare(bool glare)
void RenderingManager::requestMap(MWWorld::CellStore* cell) void RenderingManager::requestMap(MWWorld::CellStore* cell)
{ {
// FIXME: move to other method
// TODO: probably not needed when crossing a cell border. Could delay the map render until we are loaded.
if (mTerrain)
mTerrain->syncLoad();
if (cell->getCell()->isExterior()) if (cell->getCell()->isExterior())
{ {
assert(mTerrain); assert(mTerrain);

@ -164,9 +164,9 @@ namespace MWRender
} }
void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align,
Ogre::HardwareVertexBufferSharedPtr vertexBuffer, std::vector<float>& positions,
Ogre::HardwareVertexBufferSharedPtr normalBuffer, std::vector<float>& normals,
Ogre::HardwareVertexBufferSharedPtr colourBuffer) std::vector<Ogre::uint8>& colours)
{ {
// LOD level n means every 2^n-th vertex is kept // LOD level n means every 2^n-th vertex is kept
size_t increment = 1 << lodLevel; size_t increment = 1 << lodLevel;
@ -180,11 +180,8 @@ namespace MWRender
size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1; size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1;
std::vector<uint8_t> colors; colours.resize(numVerts*numVerts*4);
colors.resize(numVerts*numVerts*4);
std::vector<float> positions;
positions.resize(numVerts*numVerts*3); positions.resize(numVerts*numVerts*3);
std::vector<float> normals;
normals.resize(numVerts*numVerts*3); normals.resize(numVerts*numVerts*3);
Ogre::Vector3 normal; Ogre::Vector3 normal;
@ -270,7 +267,7 @@ namespace MWRender
color.a = 1; color.a = 1;
Ogre::uint32 rsColor; Ogre::uint32 rsColor;
Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor); Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor);
memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32)); memcpy(&colours[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32));
++vertX; ++vertX;
} }
@ -283,10 +280,6 @@ namespace MWRender
assert(vertX_ == numVerts); // Ensure we covered whole area assert(vertX_ == numVerts); // Ensure we covered whole area
} }
assert(vertY_ == numVerts); // Ensure we covered whole area assert(vertY_ == numVerts); // Ensure we covered whole area
vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true);
normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true);
colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true);
} }
TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY, TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY,

@ -19,8 +19,8 @@ namespace MWRender
/// Get bounds of the whole terrain in cell units /// Get bounds of the whole terrain in cell units
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
/// Get the minimum and maximum heights of a terrain chunk. /// Get the minimum and maximum heights of a terrain region.
/// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree.
/// Larger chunks can simply merge AABB of children. /// Larger chunks can simply merge AABB of children.
/// @param size size of the chunk in cell units /// @param size size of the chunk in cell units
/// @param center center of the chunk in cell units /// @param center center of the chunk in cell units
@ -30,16 +30,18 @@ namespace MWRender
virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max); virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max);
/// Fill vertex buffers for a terrain chunk. /// 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.
/// @param lodLevel LOD level, 0 = most detailed /// @param lodLevel LOD level, 0 = most detailed
/// @param size size of the terrain chunk in cell units /// @param size size of the terrain chunk in cell units
/// @param center center of the chunk in cell units /// @param center center of the chunk in cell units
/// @param vertexBuffer buffer to write vertices /// @param positions buffer to write vertices
/// @param normalBuffer buffer to write vertex normals /// @param normals buffer to write vertex normals
/// @param colourBuffer buffer to write vertex colours /// @param colours buffer to write vertex colours
virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align,
Ogre::HardwareVertexBufferSharedPtr vertexBuffer, std::vector<float>& positions,
Ogre::HardwareVertexBufferSharedPtr normalBuffer, std::vector<float>& normals,
Ogre::HardwareVertexBufferSharedPtr colourBuffer); std::vector<Ogre::uint8>& colours);
/// Create textures holding layer blend values for a terrain chunk. /// 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 /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might

@ -388,6 +388,8 @@ namespace MWWorld
typedef std::vector<ESM::LandTexture>::const_iterator iterator; typedef std::vector<ESM::LandTexture>::const_iterator iterator;
// Must be threadsafe! Called from terrain background loading threads.
// Not a big deal here, since ESM::LandTexture can never be modified or inserted/erased
const ESM::LandTexture *search(size_t index, size_t plugin) const { const ESM::LandTexture *search(size_t index, size_t plugin) const {
assert(plugin < mStatic.size()); assert(plugin < mStatic.size());
const LandTextureList &ltexl = mStatic[plugin]; const LandTextureList &ltexl = mStatic[plugin];
@ -487,6 +489,8 @@ namespace MWWorld
return iterator(mStatic.end()); return iterator(mStatic.end());
} }
// Must be threadsafe! Called from terrain background loading threads.
// Not a big deal here, since ESM::Land can never be modified or inserted/erased
ESM::Land *search(int x, int y) const { ESM::Land *search(int x, int y) const {
ESM::Land land; ESM::Land land;
land.mX = x, land.mY = y; land.mX = x, land.mY = y;

@ -10,9 +10,9 @@
namespace Terrain namespace Terrain
{ {
Chunk::Chunk(QuadTreeNode* node, short lodLevel) Chunk::Chunk(QuadTreeNode* node, const LoadResponseData& data)
: mNode(node) : mNode(node)
, mVertexLod(lodLevel) , mVertexLod(node->getNativeLodLevel())
, mAdditionalLod(0) , mAdditionalLod(0)
{ {
mVertexData = OGRE_NEW Ogre::VertexData; mVertexData = OGRE_NEW Ogre::VertexData;
@ -20,6 +20,8 @@ namespace Terrain
unsigned int verts = mNode->getTerrain()->getStorage()->getCellVertices(); unsigned int verts = mNode->getTerrain()->getStorage()->getCellVertices();
size_t lodLevel = mNode->getNativeLodLevel();
// Set the total number of vertices // Set the total number of vertices
size_t numVertsOneSide = mNode->getSize() * (verts-1); size_t numVertsOneSide = mNode->getSize() * (verts-1);
numVertsOneSide /= 1 << lodLevel; numVertsOneSide /= 1 << lodLevel;
@ -52,8 +54,9 @@ namespace Terrain
mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR), mColourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR),
mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC); mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);
mNode->getTerrain()->getStorage()->fillVertexBuffers(lodLevel, mNode->getSize(), mNode->getCenter(), mNode->getTerrain()->getAlign(), mVertexBuffer->writeData(0, mVertexBuffer->getSizeInBytes(), &data.mPositions[0], true);
mVertexBuffer, mNormalBuffer, mColourBuffer); mNormalBuffer->writeData(0, mNormalBuffer->getSizeInBytes(), &data.mNormals[0], true);
mColourBuffer->writeData(0, mColourBuffer->getSizeInBytes(), &data.mColours[0], true);
mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer); mVertexData->vertexBufferBinding->setBinding(0, mVertexBuffer);
mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer); mVertexData->vertexBufferBinding->setBinding(1, mNormalBuffer);

@ -8,6 +8,7 @@ namespace Terrain
{ {
class QuadTreeNode; class QuadTreeNode;
struct LoadResponseData;
/** /**
* @brief Renders a chunk of terrain, either using alpha splatting or a composite map. * @brief Renders a chunk of terrain, either using alpha splatting or a composite map.
@ -15,8 +16,8 @@ namespace Terrain
class Chunk : public Ogre::Renderable, public Ogre::MovableObject class Chunk : public Ogre::Renderable, public Ogre::MovableObject
{ {
public: public:
/// @param lodLevel LOD level for the vertex buffer. Chunk (QuadTreeNode* node, const LoadResponseData& data);
Chunk (QuadTreeNode* node, short lodLevel);
virtual ~Chunk(); virtual ~Chunk();
void setMaterial (const Ogre::MaterialPtr& material); void setMaterial (const Ogre::MaterialPtr& material);

@ -36,6 +36,13 @@ namespace Terrain
} }
} }
struct LayerInfo
{
std::string mDiffuseMap;
std::string mNormalMap;
bool mParallax; // Height info in normal map alpha channel?
bool mSpecular; // Specular info in diffuse map alpha channel?
};
} }
#endif #endif

@ -153,6 +153,7 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const
, mParent(parent) , mParent(parent)
, mTerrain(terrain) , mTerrain(terrain)
, mChunk(NULL) , mChunk(NULL)
, mLoadState(LS_Unloaded)
{ {
mBounds.setNull(); mBounds.setNull();
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
@ -266,8 +267,6 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
float dist = distance(mWorldBounds, cameraPos); float dist = distance(mWorldBounds, cameraPos);
bool distantLand = mTerrain->getDistantLandEnabled();
// Make sure our scene node is attached // Make sure our scene node is attached
if (!mSceneNode->isInSceneGraph()) if (!mSceneNode->isInSceneGraph())
{ {
@ -292,100 +291,110 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
else if (dist > cellWorldSize) else if (dist > cellWorldSize)
wantedLod = 1; wantedLod = 1;
bool hadChunk = hasChunk();
if (!distantLand && dist > 8192*2)
{
if (mIsActive)
{
destroyChunks(true);
mIsActive = false;
}
return;
}
mIsActive = true; mIsActive = true;
if (mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod) bool wantToDisplay = mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod;
if (wantToDisplay)
{ {
// Wanted LOD is small enough to render this node in one chunk // Wanted LOD is small enough to render this node in one chunk
if (!mChunk) if (mLoadState == LS_Unloaded)
{ {
mChunk = new Chunk(this, mLodLevel); mLoadState = LS_Loading;
mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags()); mTerrain->queueLoad(this);
mChunk->setCastShadows(true); }
mSceneNode->attachObject(mChunk);
mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled());
if (mSize == 1) if (mLoadState == LS_Loaded)
{ {
ensureLayerInfo(); // Additional (index buffer) LOD is currently disabled.
mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial())); // 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
else // 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())
{ {
ensureCompositeMap(); // Make sure child scene nodes are detached
mMaterialGenerator->setCompositeMap(mCompositeMap->getName()); mSceneNode->removeAllChildren();
mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial()));
// TODO: unload
//for (int i=0; i<4; ++i)
// mChildren[i]->unload();
} }
mChunk->setVisible(true);
} }
}
// Additional (index buffer) LOD is currently disabled. if (wantToDisplay && mLoadState != LS_Loaded)
// 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 // We want to display, but aren't ready yet. Perhaps our child nodes are ready?
// further away than the original node's bounding box, possibly causing a child to switch to a *lower* LOD // TODO: this doesn't check child-child nodes...
// than the original node. if (hasChildren())
// 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);
mChunk->setVisible(true);
if (!hadChunk && hasChildren())
{ {
// Make sure child scene nodes are detached for (int i=0; i<4; ++i)
mSceneNode->removeAllChildren(); if (mChildren[i]->hasChunk())
mChildren[i]->update(cameraPos);
// If distant land is enabled, keep the chunks around in case we need them again,
// otherwise, prefer low memory usage
if (!distantLand)
for (int i=0; i<4; ++i)
mChildren[i]->destroyChunks(true);
} }
} }
else if (!wantToDisplay)
{ {
// Wanted LOD is too detailed to be rendered in one chunk, // We do not want to display this node - delegate to children
// so split it up by delegating to child nodes if (mChunk)
if (hadChunk) mChunk->setVisible(false);
if (hasChildren())
{ {
// If distant land is enabled, keep the chunks around in case we need them again, for (int i=0; i<4; ++i)
// otherwise, prefer low memory usage mChildren[i]->update(cameraPos);
if (!distantLand)
destroyChunks(false);
else if (mChunk)
mChunk->setVisible(false);
} }
assert(hasChildren() && "Leaf node's LOD needs to be 0");
for (int i=0; i<4; ++i)
mChildren[i]->update(cameraPos);
} }
} }
void QuadTreeNode::destroyChunks(bool children) void QuadTreeNode::load(const LoadResponseData &data)
{
assert (!mChunk);
std::cout << "loading " << std::endl;
mChunk = new Chunk(this, data);
mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags());
mChunk->setCastShadows(true);
mSceneNode->attachObject(mChunk);
mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled());
if (mSize == 1)
{
ensureLayerInfo();
mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial()));
}
else
{
ensureCompositeMap();
mMaterialGenerator->setCompositeMap(mCompositeMap->getName());
mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial()));
}
mChunk->setVisible(false);
mLoadState = LS_Loaded;
}
void QuadTreeNode::unload()
{ {
if (mChunk) if (mChunk)
{ {
@ -411,9 +420,7 @@ void QuadTreeNode::destroyChunks(bool children)
mCompositeMap.setNull(); mCompositeMap.setNull();
} }
} }
else if (children && hasChildren()) mLoadState = LS_Unloaded;
for (int i=0; i<4; ++i)
mChildren[i]->destroyChunks(true);
} }
void QuadTreeNode::updateIndexBuffers() void QuadTreeNode::updateIndexBuffers()

@ -15,6 +15,7 @@ namespace Terrain
class World; class World;
class Chunk; class Chunk;
class MaterialGenerator; class MaterialGenerator;
struct LoadResponseData;
enum Direction enum Direction
{ {
@ -33,6 +34,13 @@ namespace Terrain
Root Root
}; };
enum LoadState
{
LS_Unloaded,
LS_Loading,
LS_Loaded
};
/** /**
* @brief A node in the quad tree for our terrain. Depending on LOD, * @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), * a node can either choose to render itself in one batch (merging its children),
@ -124,10 +132,18 @@ namespace Terrain
/// @param quads collect quads here so they can be deleted later /// @param quads collect quads here so they can be deleted later
void prepareForCompositeMap(Ogre::TRect<float> area); void prepareForCompositeMap(Ogre::TRect<float> area);
/// Create a chunk for this node from the given data.
void load (const LoadResponseData& data);
void unload();
LoadState getLoadState() { return mLoadState; }
private: private:
// Stored here for convenience in case we need layer list again // Stored here for convenience in case we need layer list again
MaterialGenerator* mMaterialGenerator; MaterialGenerator* mMaterialGenerator;
LoadState mLoadState;
/// Is this node (or any of its child nodes) currently configured to render itself? /// Is this node (or any of its child nodes) currently configured to render itself?
/// (only relevant when distant land is disabled, otherwise whole terrain is always rendered) /// (only relevant when distant land is disabled, otherwise whole terrain is always rendered)
bool mIsActive; bool mIsActive;

@ -7,15 +7,6 @@
namespace Terrain namespace Terrain
{ {
struct LayerInfo
{
std::string mDiffuseMap;
std::string mNormalMap;
bool mParallax; // Height info in normal map alpha channel?
bool mSpecular; // Specular info in diffuse map alpha channel?
};
/// We keep storage of terrain data abstract here since we need different implementations for game and editor /// We keep storage of terrain data abstract here since we need different implementations for game and editor
class Storage class Storage
{ {
@ -26,8 +17,8 @@ namespace Terrain
/// Get bounds of the whole terrain in cell units /// Get bounds of the whole terrain in cell units
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0;
/// Get the minimum and maximum heights of a terrain chunk. /// Get the minimum and maximum heights of a terrain region.
/// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. /// @note Will only be called for chunks with size = minBatchSize, i.e. leafs of the quad tree.
/// Larger chunks can simply merge AABB of children. /// Larger chunks can simply merge AABB of children.
/// @param size size of the chunk in cell units /// @param size size of the chunk in cell units
/// @param center center of the chunk in cell units /// @param center center of the chunk in cell units
@ -37,16 +28,18 @@ namespace Terrain
virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max) = 0; virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max) = 0;
/// Fill vertex buffers for a terrain chunk. /// 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.
/// @param lodLevel LOD level, 0 = most detailed /// @param lodLevel LOD level, 0 = most detailed
/// @param size size of the terrain chunk in cell units /// @param size size of the terrain chunk in cell units
/// @param center center of the chunk in cell units /// @param center center of the chunk in cell units
/// @param vertexBuffer buffer to write vertices /// @param positions buffer to write vertices
/// @param normalBuffer buffer to write vertex normals /// @param normals buffer to write vertex normals
/// @param colourBuffer buffer to write vertex colours /// @param colours buffer to write vertex colours
virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align, virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center, Terrain::Alignment align,
Ogre::HardwareVertexBufferSharedPtr vertexBuffer, std::vector<float>& positions,
Ogre::HardwareVertexBufferSharedPtr normalBuffer, std::vector<float>& normals,
Ogre::HardwareVertexBufferSharedPtr colourBuffer) = 0; std::vector<Ogre::uint8>& colours) = 0;
/// Create textures holding layer blend values for a terrain chunk. /// 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 /// @note The terrain chunk shouldn't be larger than one cell since otherwise we might

@ -64,6 +64,8 @@ namespace Terrain
, mMinX(0) , mMinX(0)
, mMaxY(0) , mMaxY(0)
, mMinY(0) , mMinY(0)
, mChunksLoading(0)
, mWorkQueueChannel(0)
{ {
#if TERRAIN_USE_SHADER == 0 #if TERRAIN_USE_SHADER == 0
if (mShaders) if (mShaders)
@ -103,10 +105,19 @@ namespace Terrain
//loadingListener->indicateProgress(); //loadingListener->indicateProgress();
mRootNode->initNeighbours(); mRootNode->initNeighbours();
//loadingListener->indicateProgress(); //loadingListener->indicateProgress();
Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue();
mWorkQueueChannel = wq->getChannel("LargeTerrain");
wq->addRequestHandler(mWorkQueueChannel, this);
wq->addResponseHandler(mWorkQueueChannel, this);
} }
World::~World() World::~World()
{ {
Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue();
wq->removeRequestHandler(mWorkQueueChannel, this);
wq->removeResponseHandler(mWorkQueueChannel, this);
delete mRootNode; delete mRootNode;
delete mStorage; delete mStorage;
} }
@ -445,4 +456,62 @@ namespace Terrain
} }
} }
void World::syncLoad()
{
while (mChunksLoading)
{
OGRE_THREAD_SLEEP(0);
Ogre::Root::getSingleton().getWorkQueue()->processResponses();
}
}
Ogre::WorkQueue::Response* World::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ)
{
const LoadRequestData data = Ogre::any_cast<LoadRequestData>(req->getData());
QuadTreeNode* node = data.mNode;
LoadResponseData* responseData = new LoadResponseData();
Ogre::Timer timer;
getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(),
responseData->mPositions, responseData->mNormals, responseData->mColours);
std::cout << "THREAD" << std::endl;
responseData->time = timer.getMicroseconds();
return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData));
}
void World::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ)
{
static unsigned long time = 0;
if (res->succeeded())
{
LoadResponseData* data = Ogre::any_cast<LoadResponseData*>(res->getData());
const LoadRequestData requestData = Ogre::any_cast<LoadRequestData>(res->getRequest()->getData());
requestData.mNode->load(*data);
time += data->time;
delete data;
std::cout << "RESPONSE, reqs took ms" << time/1000.f << std::endl;
}
--mChunksLoading;
}
void World::queueLoad(QuadTreeNode *node)
{
LoadRequestData data;
data.mNode = node;
data.mPack = getShadersEnabled();
const Ogre::uint16 loadRequestId = 1;
Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, loadRequestId, Ogre::Any(data));
++mChunksLoading;
}
} }

@ -5,6 +5,7 @@
#include <OgreHardwareVertexBuffer.h> #include <OgreHardwareVertexBuffer.h>
#include <OgreAxisAlignedBox.h> #include <OgreAxisAlignedBox.h>
#include <OgreTexture.h> #include <OgreTexture.h>
#include <OgreWorkQueue.h>
#include "defs.hpp" #include "defs.hpp"
@ -26,7 +27,7 @@ namespace Terrain
* Cracks at LOD transitions are avoided using stitching. * Cracks at LOD transitions are avoided using stitching.
* @note Multiple cameras are not supported yet * @note Multiple cameras are not supported yet
*/ */
class World class World : public Ogre::WorkQueue::RequestHandler, public Ogre::WorkQueue::ResponseHandler
{ {
public: public:
/// @note takes ownership of \a storage /// @note takes ownership of \a storage
@ -85,7 +86,16 @@ namespace Terrain
Alignment getAlign() { return mAlign; } Alignment getAlign() { return mAlign; }
/// Wait until all background loading is complete.
void syncLoad();
private: private:
// Called from a background worker thread
Ogre::WorkQueue::Response* handleRequest(const Ogre::WorkQueue::Request* req, const Ogre::WorkQueue* srcQ);
// Called from the main thread
void handleResponse(const Ogre::WorkQueue::Response* res, const Ogre::WorkQueue* srcQ);
Ogre::uint16 mWorkQueueChannel;
bool mDistantLand; bool mDistantLand;
bool mShaders; bool mShaders;
bool mShadows; bool mShadows;
@ -99,6 +109,9 @@ namespace Terrain
int mVisibilityFlags; int mVisibilityFlags;
/// The number of chunks currently loading in a background thread. If 0, we have finished loading!
int mChunksLoading;
Ogre::SceneManager* mSceneMgr; Ogre::SceneManager* mSceneMgr;
Ogre::SceneManager* mCompositeMapSceneMgr; Ogre::SceneManager* mCompositeMapSceneMgr;
@ -141,6 +154,9 @@ namespace Terrain
void convertPosition (Ogre::Vector3& pos); void convertPosition (Ogre::Vector3& pos);
void convertBounds (Ogre::AxisAlignedBox& bounds); void convertBounds (Ogre::AxisAlignedBox& bounds);
// Adds a WorkQueue request to load a chunk for this node in the background.
void queueLoad (QuadTreeNode* node);
private: private:
// Index buffers are shared across terrain batches where possible. There is one index buffer for each // 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. // combination of LOD deltas and index buffer LOD we may need.
@ -152,6 +168,30 @@ namespace Terrain
Ogre::TexturePtr mCompositeMapRenderTexture; Ogre::TexturePtr mCompositeMapRenderTexture;
}; };
struct LoadRequestData
{
QuadTreeNode* mNode;
bool mPack;
friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r)
{ return o; }
};
struct LoadResponseData
{
std::vector<float> mPositions;
std::vector<float> mNormals;
std::vector<Ogre::uint8> mColours;
// Since we can't create a texture from a different thread, this only holds the raw texel data
std::vector< std::vector<Ogre::uint8> > mBlendmaps;
std::vector<Terrain::LayerInfo> mLayerList;
unsigned long time;
friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r)
{ return o; }
};
} }
#endif #endif

Loading…
Cancel
Save