mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 22:15:32 +00:00
Terrain: background load blendmaps & layer textures. Refactor QuadTree update.
This commit is contained in:
parent
4328e08162
commit
1d926816b5
13 changed files with 296 additions and 143 deletions
|
@ -651,13 +651,20 @@ void RenderingManager::setGlare(bool glare)
|
||||||
mSkyManager->setGlare(glare);
|
mSkyManager->setGlare(glare);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderingManager::updateTerrain()
|
||||||
|
{
|
||||||
|
if (mTerrain)
|
||||||
|
{
|
||||||
|
// Avoid updating with dims.getCenter for each cell. Player position should be good enough
|
||||||
|
mTerrain->update(mRendering.getCamera()->getRealPosition());
|
||||||
|
mTerrain->syncLoad();
|
||||||
|
// need to update again so the chunks that were just loaded can be made visible
|
||||||
|
mTerrain->update(mRendering.getCamera()->getRealPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@ -666,9 +673,6 @@ void RenderingManager::requestMap(MWWorld::CellStore* cell)
|
||||||
Ogre::Vector2 center (cell->getCell()->getGridX() + 0.5, cell->getCell()->getGridY() + 0.5);
|
Ogre::Vector2 center (cell->getCell()->getGridX() + 0.5, cell->getCell()->getGridY() + 0.5);
|
||||||
dims.merge(mTerrain->getWorldBoundingBox(center));
|
dims.merge(mTerrain->getWorldBoundingBox(center));
|
||||||
|
|
||||||
if (dims.isFinite())
|
|
||||||
mTerrain->update(dims.getCenter());
|
|
||||||
|
|
||||||
mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z);
|
mLocalMap->requestMap(cell, dims.getMinimum().z, dims.getMaximum().z);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1059,8 +1063,7 @@ void RenderingManager::enableTerrain(bool enable)
|
||||||
}
|
}
|
||||||
mTerrain->setVisible(true);
|
mTerrain->setVisible(true);
|
||||||
}
|
}
|
||||||
else
|
else if (mTerrain)
|
||||||
if (mTerrain)
|
|
||||||
mTerrain->setVisible(false);
|
mTerrain->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,6 +180,10 @@ public:
|
||||||
void removeWaterRippleEmitter (const MWWorld::Ptr& ptr);
|
void removeWaterRippleEmitter (const MWWorld::Ptr& ptr);
|
||||||
void updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);
|
void updateWaterRippleEmitterPtr (const MWWorld::Ptr& old, const MWWorld::Ptr& ptr);
|
||||||
|
|
||||||
|
void updateTerrain ();
|
||||||
|
///< update the terrain according to the player position. Usually done automatically, but should be done manually
|
||||||
|
/// before calling requestMap
|
||||||
|
|
||||||
void requestMap (MWWorld::CellStore* cell);
|
void requestMap (MWWorld::CellStore* cell);
|
||||||
///< request the local map for a cell
|
///< request the local map for a cell
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <OgreStringConverter.h>
|
#include <OgreStringConverter.h>
|
||||||
#include <OgreRenderSystem.h>
|
#include <OgreRenderSystem.h>
|
||||||
#include <OgreResourceGroupManager.h>
|
#include <OgreResourceGroupManager.h>
|
||||||
|
#include <OgreResourceBackgroundQueue.h>
|
||||||
#include <OgreRoot.h>
|
#include <OgreRoot.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
@ -13,6 +14,8 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
#include <components/terrain/quadtreenode.hpp>
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -329,8 +332,24 @@ namespace MWRender
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TerrainStorage::getBlendmaps (const std::vector<Terrain::QuadTreeNode*>& nodes, std::vector<Terrain::LayerCollection>& out, bool pack)
|
||||||
|
{
|
||||||
|
for (std::vector<Terrain::QuadTreeNode*>::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
|
||||||
|
{
|
||||||
|
out.push_back(Terrain::LayerCollection());
|
||||||
|
out.back().mTarget = *it;
|
||||||
|
getBlendmapsImpl((*it)->getSize(), (*it)->getCenter(), pack, out.back().mBlendmaps, out.back().mLayers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter,
|
void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter,
|
||||||
bool pack, std::vector<Ogre::TexturePtr> &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
|
bool pack, std::vector<Ogre::PixelBox> &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
|
||||||
|
{
|
||||||
|
getBlendmapsImpl(chunkSize, chunkCenter, pack, blendmaps, layerList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TerrainStorage::getBlendmapsImpl(float chunkSize, const Ogre::Vector2 &chunkCenter,
|
||||||
|
bool pack, std::vector<Ogre::PixelBox> &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
|
||||||
{
|
{
|
||||||
// TODO - blending isn't completely right yet; the blending radius appears to be
|
// 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
|
// different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap
|
||||||
|
@ -375,16 +394,14 @@ namespace MWRender
|
||||||
|
|
||||||
// Second iteration - create and fill in the blend maps
|
// Second iteration - create and fill in the blend maps
|
||||||
const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1;
|
const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1;
|
||||||
std::vector<Ogre::uchar> data;
|
|
||||||
data.resize(blendmapSize * blendmapSize * channels, 0);
|
|
||||||
|
|
||||||
for (int i=0; i<numBlendmaps; ++i)
|
for (int i=0; i<numBlendmaps; ++i)
|
||||||
{
|
{
|
||||||
Ogre::PixelFormat format = pack ? Ogre::PF_A8B8G8R8 : Ogre::PF_A8;
|
Ogre::PixelFormat format = pack ? Ogre::PF_A8B8G8R8 : Ogre::PF_A8;
|
||||||
static int count=0;
|
|
||||||
Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/"
|
Ogre::uchar* pData =
|
||||||
+ Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
OGRE_ALLOC_T(Ogre::uchar, blendmapSize*blendmapSize*channels, Ogre::MEMCATEGORY_GENERAL);
|
||||||
Ogre::TEX_TYPE_2D, blendmapSize, blendmapSize, 0, format);
|
memset(pData, 0, blendmapSize*blendmapSize*channels);
|
||||||
|
|
||||||
for (int y=0; y<blendmapSize; ++y)
|
for (int y=0; y<blendmapSize; ++y)
|
||||||
{
|
{
|
||||||
|
@ -396,16 +413,12 @@ namespace MWRender
|
||||||
int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
|
int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
|
||||||
|
|
||||||
if (blendIndex == i)
|
if (blendIndex == i)
|
||||||
data[y*blendmapSize*channels + x*channels + channel] = 255;
|
pData[y*blendmapSize*channels + x*channels + channel] = 255;
|
||||||
else
|
else
|
||||||
data[y*blendmapSize*channels + x*channels + channel] = 0;
|
pData[y*blendmapSize*channels + x*channels + channel] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
blendmaps.push_back(Ogre::PixelBox(blendmapSize, blendmapSize, 1, format, pData));
|
||||||
// All done, upload to GPU
|
|
||||||
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
|
|
||||||
map->loadRawData(stream, blendmapSize, blendmapSize, format);
|
|
||||||
blendmaps.push_back(map);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,6 +540,12 @@ namespace MWRender
|
||||||
info.mSpecular = true;
|
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;
|
mLayerInfoMap[texture] = info;
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
|
|
|
@ -46,6 +46,7 @@ namespace MWRender
|
||||||
/// 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
|
||||||
/// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.
|
/// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.
|
||||||
|
/// @note May be called from *one* background thread.
|
||||||
/// @param chunkSize size of the terrain chunk in cell units
|
/// @param chunkSize size of the terrain chunk in cell units
|
||||||
/// @param chunkCenter center of the chunk in cell units
|
/// @param chunkCenter center of the chunk in cell units
|
||||||
/// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
|
/// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
|
||||||
|
@ -54,9 +55,21 @@ namespace MWRender
|
||||||
/// @param blendmaps created blendmaps will be written here
|
/// @param blendmaps created blendmaps will be written here
|
||||||
/// @param layerList names of the layer textures used 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,
|
virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack,
|
||||||
std::vector<Ogre::TexturePtr>& blendmaps,
|
std::vector<Ogre::PixelBox>& blendmaps,
|
||||||
std::vector<Terrain::LayerInfo>& layerList);
|
std::vector<Terrain::LayerInfo>& 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 *one* background thread.
|
||||||
|
/// @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<Terrain::QuadTreeNode*>& nodes, std::vector<Terrain::LayerCollection>& out, bool pack);
|
||||||
|
|
||||||
virtual float getHeightAt (const Ogre::Vector3& worldPos);
|
virtual float getHeightAt (const Ogre::Vector3& worldPos);
|
||||||
|
|
||||||
virtual Terrain::LayerInfo getDefaultLayer();
|
virtual Terrain::LayerInfo getDefaultLayer();
|
||||||
|
@ -86,6 +99,11 @@ namespace MWRender
|
||||||
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
||||||
|
|
||||||
Terrain::LayerInfo getLayerInfo(const std::string& texture);
|
Terrain::LayerInfo getLayerInfo(const std::string& texture);
|
||||||
|
|
||||||
|
// Non-virtual
|
||||||
|
void getBlendmapsImpl (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack,
|
||||||
|
std::vector<Ogre::PixelBox>& blendmaps,
|
||||||
|
std::vector<Terrain::LayerInfo>& layerList);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,10 +195,12 @@ namespace MWWorld
|
||||||
mechMgr->updateCell(old, player);
|
mechMgr->updateCell(old, player);
|
||||||
mechMgr->watchActor(player);
|
mechMgr->watchActor(player);
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
|
mRendering.updateTerrain();
|
||||||
|
|
||||||
for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active)
|
for (CellStoreCollection::iterator active = mActiveCells.begin(); active!=mActiveCells.end(); ++active)
|
||||||
mRendering.requestMap(*active);
|
mRendering.requestMap(*active);
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::changeToVoid()
|
void Scene::changeToVoid()
|
||||||
|
|
|
@ -110,6 +110,7 @@ namespace Terrain
|
||||||
void Chunk::getRenderOperation(Ogre::RenderOperation& op)
|
void Chunk::getRenderOperation(Ogre::RenderOperation& op)
|
||||||
{
|
{
|
||||||
assert (!mIndexData->indexBuffer.isNull() && "Trying to render, but no index buffer set!");
|
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.useIndexes = true;
|
||||||
op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST;
|
op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST;
|
||||||
op.vertexData = mVertexData;
|
op.vertexData = mVertexData;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
namespace Terrain
|
namespace Terrain
|
||||||
{
|
{
|
||||||
|
class QuadTreeNode;
|
||||||
|
|
||||||
/// The alignment of the terrain
|
/// The alignment of the terrain
|
||||||
enum Alignment
|
enum Alignment
|
||||||
|
@ -51,6 +52,14 @@ namespace Terrain
|
||||||
bool mParallax; // Height info in normal map alpha channel?
|
bool mParallax; // Height info in normal map alpha channel?
|
||||||
bool mSpecular; // Specular info in diffuse map alpha channel?
|
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<Ogre::PixelBox> mBlendmaps;
|
||||||
|
std::vector<LayerInfo> mLayers;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -48,11 +48,15 @@ namespace Terrain
|
||||||
|
|
||||||
Ogre::MaterialPtr MaterialGenerator::generate(Ogre::MaterialPtr mat)
|
Ogre::MaterialPtr MaterialGenerator::generate(Ogre::MaterialPtr mat)
|
||||||
{
|
{
|
||||||
|
assert(!mLayerList.empty() && "Can't create material with no layers");
|
||||||
|
|
||||||
return create(mat, false, false);
|
return create(mat, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ogre::MaterialPtr MaterialGenerator::generateForCompositeMapRTT(Ogre::MaterialPtr mat)
|
Ogre::MaterialPtr MaterialGenerator::generateForCompositeMapRTT(Ogre::MaterialPtr mat)
|
||||||
{
|
{
|
||||||
|
assert(!mLayerList.empty() && "Can't create material with no layers");
|
||||||
|
|
||||||
return create(mat, true, false);
|
return create(mat, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,6 @@ namespace
|
||||||
|
|
||||||
QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent)
|
QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent)
|
||||||
: mMaterialGenerator(NULL)
|
: mMaterialGenerator(NULL)
|
||||||
, mIsActive(false)
|
|
||||||
, mIsDummy(false)
|
, mIsDummy(false)
|
||||||
, mSize(size)
|
, mSize(size)
|
||||||
, mLodLevel(Log2(mSize))
|
, mLodLevel(Log2(mSize))
|
||||||
|
@ -262,11 +261,13 @@ const Ogre::AxisAlignedBox& QuadTreeNode::getWorldBoundingBox()
|
||||||
return mWorldBounds;
|
return mWorldBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
bool QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
||||||
{
|
{
|
||||||
const Ogre::AxisAlignedBox& bounds = getBoundingBox();
|
if (isDummy())
|
||||||
if (bounds.isNull())
|
return true;
|
||||||
return;
|
|
||||||
|
if (mBounds.isNull())
|
||||||
|
return true;
|
||||||
|
|
||||||
float dist = distance(mWorldBounds, cameraPos);
|
float dist = distance(mWorldBounds, cameraPos);
|
||||||
|
|
||||||
|
@ -291,11 +292,9 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
||||||
wantedLod = 3;
|
wantedLod = 3;
|
||||||
else if (dist > cellWorldSize*2)
|
else if (dist > cellWorldSize*2)
|
||||||
wantedLod = 2;
|
wantedLod = 2;
|
||||||
else if (dist > cellWorldSize)
|
else if (dist > cellWorldSize * 1.42) // < sqrt2 so the 3x3 grid around player is always highest lod
|
||||||
wantedLod = 1;
|
wantedLod = 1;
|
||||||
|
|
||||||
mIsActive = true;
|
|
||||||
|
|
||||||
bool wantToDisplay = mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod;
|
bool wantToDisplay = mSize <= mTerrain->getMaxBatchSize() && mLodLevel <= wantedLod;
|
||||||
|
|
||||||
if (wantToDisplay)
|
if (wantToDisplay)
|
||||||
|
@ -305,6 +304,7 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
||||||
{
|
{
|
||||||
mLoadState = LS_Loading;
|
mLoadState = LS_Loading;
|
||||||
mTerrain->queueLoad(this);
|
mTerrain->queueLoad(this);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mLoadState == LS_Loaded)
|
if (mLoadState == LS_Loaded)
|
||||||
|
@ -331,47 +331,60 @@ void QuadTreeNode::update(const Ogre::Vector3 &cameraPos)
|
||||||
|
|
||||||
if (!mChunk->getVisible() && hasChildren())
|
if (!mChunk->getVisible() && hasChildren())
|
||||||
{
|
{
|
||||||
// Make sure child scene nodes are detached
|
for (int i=0; i<4; ++i)
|
||||||
mSceneNode->removeAllChildren();
|
mChildren[i]->unload(true);
|
||||||
|
|
||||||
// TODO: unload
|
|
||||||
//for (int i=0; i<4; ++i)
|
|
||||||
// mChildren[i]->unload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mChunk->setVisible(true);
|
mChunk->setVisible(true);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false; // LS_Loading
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wantToDisplay && mLoadState != LS_Loaded)
|
// We do not want to display this node - delegate to children if they are already loaded
|
||||||
|
if (!wantToDisplay && hasChildren())
|
||||||
{
|
{
|
||||||
// We want to display, but aren't ready yet. Perhaps our child nodes are ready?
|
|
||||||
// TODO: this doesn't check child-child nodes...
|
|
||||||
if (hasChildren())
|
|
||||||
{
|
|
||||||
for (int i=0; i<4; ++i)
|
|
||||||
if (mChildren[i]->hasChunk())
|
|
||||||
mChildren[i]->update(cameraPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!wantToDisplay)
|
|
||||||
{
|
|
||||||
// We do not want to display this node - delegate to children
|
|
||||||
if (mChunk)
|
if (mChunk)
|
||||||
mChunk->setVisible(false);
|
|
||||||
if (hasChildren())
|
|
||||||
{
|
{
|
||||||
|
// Are children already loaded?
|
||||||
|
bool childrenLoaded = true;
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
mChildren[i]->update(cameraPos);
|
if (!mChildren[i]->update(cameraPos))
|
||||||
|
childrenLoaded = false;
|
||||||
|
|
||||||
|
if (!childrenLoaded)
|
||||||
|
{
|
||||||
|
// 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)
|
void QuadTreeNode::load(const LoadResponseData &data)
|
||||||
{
|
{
|
||||||
assert (!mChunk);
|
assert (!mChunk);
|
||||||
|
|
||||||
std::cout << "loading " << std::endl;
|
|
||||||
mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data);
|
mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data);
|
||||||
mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags());
|
mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags());
|
||||||
mChunk->setCastShadows(true);
|
mChunk->setCastShadows(true);
|
||||||
|
@ -380,9 +393,10 @@ void QuadTreeNode::load(const LoadResponseData &data)
|
||||||
mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
|
mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
|
||||||
mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled());
|
mMaterialGenerator->enableSplitShadows(mTerrain->getSplitShadowsEnabled());
|
||||||
|
|
||||||
|
if (mTerrain->areLayersLoaded())
|
||||||
|
{
|
||||||
if (mSize == 1)
|
if (mSize == 1)
|
||||||
{
|
{
|
||||||
ensureLayerInfo();
|
|
||||||
mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial()));
|
mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -391,41 +405,39 @@ void QuadTreeNode::load(const LoadResponseData &data)
|
||||||
mMaterialGenerator->setCompositeMap(mCompositeMap->getName());
|
mMaterialGenerator->setCompositeMap(mCompositeMap->getName());
|
||||||
mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial()));
|
mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// else: will be loaded in loadMaterials() after background thread has finished loading layers
|
||||||
mChunk->setVisible(false);
|
mChunk->setVisible(false);
|
||||||
|
|
||||||
mLoadState = LS_Loaded;
|
mLoadState = LS_Loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeNode::unload()
|
void QuadTreeNode::unload(bool recursive)
|
||||||
{
|
{
|
||||||
if (mChunk)
|
if (mChunk)
|
||||||
{
|
{
|
||||||
Ogre::MaterialManager::getSingleton().remove(mChunk->getMaterial()->getName());
|
|
||||||
mSceneNode->detachObject(mChunk);
|
mSceneNode->detachObject(mChunk);
|
||||||
|
|
||||||
delete mChunk;
|
delete mChunk;
|
||||||
mChunk = NULL;
|
mChunk = NULL;
|
||||||
// destroy blendmaps
|
|
||||||
if (mMaterialGenerator)
|
|
||||||
{
|
|
||||||
const std::vector<Ogre::TexturePtr>& list = mMaterialGenerator->getBlendmapList();
|
|
||||||
for (std::vector<Ogre::TexturePtr>::const_iterator it = list.begin(); it != list.end(); ++it)
|
|
||||||
Ogre::TextureManager::getSingleton().remove((*it)->getName());
|
|
||||||
mMaterialGenerator->setBlendmapList(std::vector<Ogre::TexturePtr>());
|
|
||||||
mMaterialGenerator->setLayerList(std::vector<LayerInfo>());
|
|
||||||
mMaterialGenerator->setCompositeMap("");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mCompositeMap.isNull())
|
if (!mCompositeMap.isNull())
|
||||||
{
|
{
|
||||||
Ogre::TextureManager::getSingleton().remove(mCompositeMap->getName());
|
Ogre::TextureManager::getSingleton().remove(mCompositeMap->getName());
|
||||||
mCompositeMap.setNull();
|
mCompositeMap.setNull();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Do *not* set this when we are still loading!
|
||||||
mLoadState = LS_Unloaded;
|
mLoadState = LS_Unloaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (recursive && hasChildren())
|
||||||
|
{
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
mChildren[i]->unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void QuadTreeNode::updateIndexBuffers()
|
void QuadTreeNode::updateIndexBuffers()
|
||||||
{
|
{
|
||||||
if (hasChunk())
|
if (hasChunk())
|
||||||
|
@ -479,17 +491,53 @@ size_t QuadTreeNode::getActualLodLevel()
|
||||||
return mLodLevel /* + mChunk->getAdditionalLod() */;
|
return mLodLevel /* + mChunk->getAdditionalLod() */;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeNode::ensureLayerInfo()
|
void QuadTreeNode::loadLayers(const LayerCollection& collection)
|
||||||
{
|
{
|
||||||
if (mMaterialGenerator->hasLayers())
|
assert (!mMaterialGenerator->hasLayers());
|
||||||
|
|
||||||
|
std::vector<Ogre::TexturePtr> blendTextures;
|
||||||
|
for (std::vector<Ogre::PixelBox>::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;
|
return;
|
||||||
|
|
||||||
std::vector<Ogre::TexturePtr> blendmaps;
|
// Load children first since we depend on them when creating a composite map
|
||||||
std::vector<LayerInfo> layerList;
|
if (hasChildren())
|
||||||
mTerrain->getStorage()->getBlendmaps(mSize, mCenter, mTerrain->getShadersEnabled(), blendmaps, layerList);
|
{
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
mChildren[i]->loadMaterials();
|
||||||
|
}
|
||||||
|
|
||||||
mMaterialGenerator->setLayerList(layerList);
|
if (mChunk)
|
||||||
mMaterialGenerator->setBlendmapList(blendmaps);
|
{
|
||||||
|
if (mSize == 1)
|
||||||
|
{
|
||||||
|
mChunk->setMaterial(mMaterialGenerator->generate(mChunk->getMaterial()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ensureCompositeMap();
|
||||||
|
mMaterialGenerator->setCompositeMap(mCompositeMap->getName());
|
||||||
|
mChunk->setMaterial(mMaterialGenerator->generateForCompositeMap(mChunk->getMaterial()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
|
void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
|
||||||
|
@ -525,8 +573,7 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ensureLayerInfo();
|
// TODO: when to destroy?
|
||||||
|
|
||||||
Ogre::MaterialPtr material = mMaterialGenerator->generateForCompositeMapRTT(Ogre::MaterialPtr());
|
Ogre::MaterialPtr material = mMaterialGenerator->generateForCompositeMapRTT(Ogre::MaterialPtr());
|
||||||
makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, material);
|
makeQuad(sceneMgr, area.left, area.top, area.right, area.bottom, material);
|
||||||
}
|
}
|
||||||
|
@ -570,13 +617,3 @@ void QuadTreeNode::applyMaterials()
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
mChildren[i]->applyMaterials();
|
mChildren[i]->applyMaterials();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeNode::setVisible(bool visible)
|
|
||||||
{
|
|
||||||
if (!visible && mChunk)
|
|
||||||
mChunk->setVisible(false);
|
|
||||||
|
|
||||||
if (hasChildren())
|
|
||||||
for (int i=0; i<4; ++i)
|
|
||||||
mChildren[i]->setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
|
@ -51,8 +51,6 @@ namespace Terrain
|
||||||
QuadTreeNode (World* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent);
|
QuadTreeNode (World* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent);
|
||||||
~QuadTreeNode();
|
~QuadTreeNode();
|
||||||
|
|
||||||
void setVisible(bool visible);
|
|
||||||
|
|
||||||
/// Rebuild all materials
|
/// Rebuild all materials
|
||||||
void applyMaterials();
|
void applyMaterials();
|
||||||
|
|
||||||
|
@ -100,7 +98,9 @@ namespace Terrain
|
||||||
World* getTerrain() { return mTerrain; }
|
World* getTerrain() { return mTerrain; }
|
||||||
|
|
||||||
/// Adjust LODs for the given camera position, possibly splitting up chunks or merging them.
|
/// Adjust LODs for the given camera position, possibly splitting up chunks or merging them.
|
||||||
void update (const Ogre::Vector3& cameraPos);
|
/// @param force Always choose to render this node, even if not the perfect LOD.
|
||||||
|
/// @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.
|
/// Adjust index buffers of chunks to stitch together chunks of different LOD, so that cracks are avoided.
|
||||||
/// Call after QuadTreeNode::update!
|
/// Call after QuadTreeNode::update!
|
||||||
|
@ -122,13 +122,17 @@ namespace Terrain
|
||||||
/// Add a textured quad to a specific 2d area in the composite map scenemanager.
|
/// 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
|
/// Only nodes with size <= 1 can be rendered with alpha blending, so larger nodes will simply
|
||||||
/// call this method on their children.
|
/// 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
|
/// @param area area in image space to put the quad
|
||||||
/// @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.
|
/// Create a chunk for this node from the given data.
|
||||||
void load (const LoadResponseData& data);
|
void load (const LoadResponseData& data);
|
||||||
void unload();
|
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; }
|
LoadState getLoadState() { return mLoadState; }
|
||||||
|
|
||||||
|
@ -138,10 +142,6 @@ namespace Terrain
|
||||||
|
|
||||||
LoadState mLoadState;
|
LoadState mLoadState;
|
||||||
|
|
||||||
/// 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)
|
|
||||||
bool mIsActive;
|
|
||||||
|
|
||||||
bool mIsDummy;
|
bool mIsDummy;
|
||||||
float mSize;
|
float mSize;
|
||||||
size_t mLodLevel; // LOD if we were to render this node in one chunk
|
size_t mLodLevel; // LOD if we were to render this node in one chunk
|
||||||
|
@ -162,7 +162,6 @@ namespace Terrain
|
||||||
|
|
||||||
Ogre::TexturePtr mCompositeMap;
|
Ogre::TexturePtr mCompositeMap;
|
||||||
|
|
||||||
void ensureLayerInfo();
|
|
||||||
void ensureCompositeMap();
|
void ensureCompositeMap();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ namespace Terrain
|
||||||
/// 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
|
||||||
/// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.
|
/// 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 chunkSize size of the terrain chunk in cell units
|
/// @param chunkSize size of the terrain chunk in cell units
|
||||||
/// @param chunkCenter center of the chunk in cell units
|
/// @param chunkCenter center of the chunk in cell units
|
||||||
/// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
|
/// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
|
||||||
|
@ -52,9 +53,21 @@ namespace Terrain
|
||||||
/// @param blendmaps created blendmaps will be written here
|
/// @param blendmaps created blendmaps will be written here
|
||||||
/// @param layerList names of the layer textures used 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,
|
virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack,
|
||||||
std::vector<Ogre::TexturePtr>& blendmaps,
|
std::vector<Ogre::PixelBox>& blendmaps,
|
||||||
std::vector<LayerInfo>& layerList) = 0;
|
std::vector<LayerInfo>& 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<QuadTreeNode*>& nodes, std::vector<LayerCollection>& out, bool pack) = 0;
|
||||||
|
|
||||||
virtual float getHeightAt (const Ogre::Vector3& worldPos) = 0;
|
virtual float getHeightAt (const Ogre::Vector3& worldPos) = 0;
|
||||||
|
|
||||||
virtual LayerInfo getDefaultLayer() = 0;
|
virtual LayerInfo getDefaultLayer() = 0;
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <OgreRenderTexture.h>
|
#include <OgreRenderTexture.h>
|
||||||
#include <OgreSceneNode.h>
|
#include <OgreSceneNode.h>
|
||||||
#include <OgreRoot.h>
|
#include <OgreRoot.h>
|
||||||
#include <OgreTimer.h> // TEMP
|
|
||||||
|
|
||||||
#include "storage.hpp"
|
#include "storage.hpp"
|
||||||
#include "quadtreenode.hpp"
|
#include "quadtreenode.hpp"
|
||||||
|
@ -52,6 +51,9 @@ namespace
|
||||||
namespace Terrain
|
namespace Terrain
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const Ogre::uint REQ_ID_CHUNK = 1;
|
||||||
|
const Ogre::uint REQ_ID_LAYERS = 2;
|
||||||
|
|
||||||
World::World(Ogre::SceneManager* sceneMgr,
|
World::World(Ogre::SceneManager* sceneMgr,
|
||||||
Storage* storage, int visibilityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize)
|
Storage* storage, int visibilityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize)
|
||||||
: mStorage(storage)
|
: mStorage(storage)
|
||||||
|
@ -70,6 +72,7 @@ namespace Terrain
|
||||||
, mChunksLoading(0)
|
, mChunksLoading(0)
|
||||||
, mWorkQueueChannel(0)
|
, mWorkQueueChannel(0)
|
||||||
, mCache(storage->getCellVertices())
|
, mCache(storage->getCellVertices())
|
||||||
|
, mLayerLoadPending(true)
|
||||||
{
|
{
|
||||||
#if TERRAIN_USE_SHADER == 0
|
#if TERRAIN_USE_SHADER == 0
|
||||||
if (mShaders)
|
if (mShaders)
|
||||||
|
@ -102,8 +105,12 @@ namespace Terrain
|
||||||
|
|
||||||
mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
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, size, Ogre::Vector2(centerX, centerY), NULL);
|
mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL);
|
||||||
buildQuadTree(mRootNode);
|
buildQuadTree(mRootNode, data.mNodes);
|
||||||
//loadingListener->indicateProgress();
|
//loadingListener->indicateProgress();
|
||||||
mRootNode->initAabb();
|
mRootNode->initAabb();
|
||||||
//loadingListener->indicateProgress();
|
//loadingListener->indicateProgress();
|
||||||
|
@ -114,6 +121,9 @@ namespace Terrain
|
||||||
mWorkQueueChannel = wq->getChannel("LargeTerrain");
|
mWorkQueueChannel = wq->getChannel("LargeTerrain");
|
||||||
wq->addRequestHandler(mWorkQueueChannel, this);
|
wq->addRequestHandler(mWorkQueueChannel, this);
|
||||||
wq->addResponseHandler(mWorkQueueChannel, this);
|
wq->addResponseHandler(mWorkQueueChannel, this);
|
||||||
|
|
||||||
|
// Start loading layers in the background (for leaf nodes)
|
||||||
|
wq->addRequest(mWorkQueueChannel, REQ_ID_LAYERS, Ogre::Any(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
World::~World()
|
World::~World()
|
||||||
|
@ -126,7 +136,7 @@ namespace Terrain
|
||||||
delete mStorage;
|
delete mStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::buildQuadTree(QuadTreeNode *node)
|
void World::buildQuadTree(QuadTreeNode *node, std::vector<QuadTreeNode*>& leafs)
|
||||||
{
|
{
|
||||||
float halfSize = node->getSize()/2.f;
|
float halfSize = node->getSize()/2.f;
|
||||||
|
|
||||||
|
@ -142,6 +152,7 @@ namespace Terrain
|
||||||
Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ));
|
Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ));
|
||||||
convertBounds(bounds);
|
convertBounds(bounds);
|
||||||
node->setBoundingBox(bounds);
|
node->setBoundingBox(bounds);
|
||||||
|
leafs.push_back(node);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
node->markAsDummy(); // no data available for this node, skip it
|
node->markAsDummy(); // no data available for this node, skip it
|
||||||
|
@ -164,10 +175,10 @@ namespace Terrain
|
||||||
node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -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(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f));
|
||||||
node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f);
|
node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f);
|
||||||
buildQuadTree(node->getChild(SW));
|
buildQuadTree(node->getChild(SW), leafs);
|
||||||
buildQuadTree(node->getChild(SE));
|
buildQuadTree(node->getChild(SE), leafs);
|
||||||
buildQuadTree(node->getChild(NW));
|
buildQuadTree(node->getChild(NW), leafs);
|
||||||
buildQuadTree(node->getChild(NE));
|
buildQuadTree(node->getChild(NE), leafs);
|
||||||
|
|
||||||
// if all children are dummy, we are also dummy
|
// if all children are dummy, we are also dummy
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
|
@ -267,7 +278,7 @@ namespace Terrain
|
||||||
|
|
||||||
void World::syncLoad()
|
void World::syncLoad()
|
||||||
{
|
{
|
||||||
while (mChunksLoading)
|
while (mChunksLoading || mLayerLoadPending)
|
||||||
{
|
{
|
||||||
OGRE_THREAD_SLEEP(0);
|
OGRE_THREAD_SLEEP(0);
|
||||||
Ogre::Root::getSingleton().getWorkQueue()->processResponses();
|
Ogre::Root::getSingleton().getWorkQueue()->processResponses();
|
||||||
|
@ -275,6 +286,8 @@ namespace Terrain
|
||||||
}
|
}
|
||||||
|
|
||||||
Ogre::WorkQueue::Response* World::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ)
|
Ogre::WorkQueue::Response* World::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ)
|
||||||
|
{
|
||||||
|
if (req->getType() == REQ_ID_CHUNK)
|
||||||
{
|
{
|
||||||
const LoadRequestData data = Ogre::any_cast<LoadRequestData>(req->getData());
|
const LoadRequestData data = Ogre::any_cast<LoadRequestData>(req->getData());
|
||||||
|
|
||||||
|
@ -282,21 +295,28 @@ namespace Terrain
|
||||||
|
|
||||||
LoadResponseData* responseData = new LoadResponseData();
|
LoadResponseData* responseData = new LoadResponseData();
|
||||||
|
|
||||||
Ogre::Timer timer;
|
|
||||||
getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(),
|
getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(),
|
||||||
responseData->mPositions, responseData->mNormals, responseData->mColours);
|
responseData->mPositions, responseData->mNormals, responseData->mColours);
|
||||||
|
|
||||||
std::cout << "THREAD" << std::endl;
|
return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData));
|
||||||
|
}
|
||||||
|
else // REQ_ID_LAYERS
|
||||||
|
{
|
||||||
|
const LayersRequestData data = Ogre::any_cast<LayersRequestData>(req->getData());
|
||||||
|
|
||||||
responseData->time = timer.getMicroseconds();
|
LayersResponseData* responseData = new LayersResponseData();
|
||||||
|
|
||||||
|
getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack);
|
||||||
|
|
||||||
return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData));
|
return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void World::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ)
|
void World::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ)
|
||||||
{
|
{
|
||||||
static unsigned long time = 0;
|
assert(res->succeeded() && "Response failure not handled");
|
||||||
if (res->succeeded())
|
|
||||||
|
if (res->getRequest()->getType() == REQ_ID_CHUNK)
|
||||||
{
|
{
|
||||||
LoadResponseData* data = Ogre::any_cast<LoadResponseData*>(res->getData());
|
LoadResponseData* data = Ogre::any_cast<LoadResponseData*>(res->getData());
|
||||||
|
|
||||||
|
@ -304,23 +324,31 @@ namespace Terrain
|
||||||
|
|
||||||
requestData.mNode->load(*data);
|
requestData.mNode->load(*data);
|
||||||
|
|
||||||
time += data->time;
|
|
||||||
|
|
||||||
delete data;
|
delete data;
|
||||||
|
|
||||||
std::cout << "RESPONSE, reqs took ms" << time/1000.f << std::endl;
|
|
||||||
}
|
|
||||||
--mChunksLoading;
|
--mChunksLoading;
|
||||||
}
|
}
|
||||||
|
else // REQ_ID_LAYERS
|
||||||
|
{
|
||||||
|
LayersResponseData* data = Ogre::any_cast<LayersResponseData*>(res->getData());
|
||||||
|
|
||||||
|
for (std::vector<LayerCollection>::iterator it = data->mLayerCollections.begin(); it != data->mLayerCollections.end(); ++it)
|
||||||
|
{
|
||||||
|
it->mTarget->loadLayers(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
mRootNode->loadMaterials();
|
||||||
|
|
||||||
|
mLayerLoadPending = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void World::queueLoad(QuadTreeNode *node)
|
void World::queueLoad(QuadTreeNode *node)
|
||||||
{
|
{
|
||||||
LoadRequestData data;
|
LoadRequestData data;
|
||||||
data.mNode = node;
|
data.mNode = node;
|
||||||
data.mPack = getShadersEnabled();
|
|
||||||
|
|
||||||
const Ogre::uint16 loadRequestId = 1;
|
Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, REQ_ID_CHUNK, Ogre::Any(data));
|
||||||
Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, loadRequestId, Ogre::Any(data));
|
|
||||||
++mChunksLoading;
|
++mChunksLoading;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,15 +122,20 @@ namespace Terrain
|
||||||
/// Maximum size of a terrain batch along one side (in cell units)
|
/// Maximum size of a terrain batch along one side (in cell units)
|
||||||
float mMaxBatchSize;
|
float mMaxBatchSize;
|
||||||
|
|
||||||
void buildQuadTree(QuadTreeNode* node);
|
void buildQuadTree(QuadTreeNode* node, std::vector<QuadTreeNode*>& leafs);
|
||||||
|
|
||||||
BufferCache mCache;
|
BufferCache mCache;
|
||||||
|
|
||||||
|
// Are layers for leaf nodes loaded? This is done once at startup (but in a background thread)
|
||||||
|
bool mLayerLoadPending;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// ----INTERNAL----
|
// ----INTERNAL----
|
||||||
Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; }
|
Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; }
|
||||||
BufferCache& getBufferCache() { return mCache; }
|
BufferCache& getBufferCache() { return mCache; }
|
||||||
|
|
||||||
|
bool areLayersLoaded() { return !mLayerLoadPending; }
|
||||||
|
|
||||||
// Delete all quads
|
// Delete all quads
|
||||||
void clearCompositeMapSceneManager();
|
void clearCompositeMapSceneManager();
|
||||||
void renderCompositeMap (Ogre::TexturePtr target);
|
void renderCompositeMap (Ogre::TexturePtr target);
|
||||||
|
@ -151,7 +156,6 @@ namespace Terrain
|
||||||
struct LoadRequestData
|
struct LoadRequestData
|
||||||
{
|
{
|
||||||
QuadTreeNode* mNode;
|
QuadTreeNode* mNode;
|
||||||
bool mPack;
|
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r)
|
friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r)
|
||||||
{ return o; }
|
{ return o; }
|
||||||
|
@ -162,16 +166,28 @@ namespace Terrain
|
||||||
std::vector<float> mPositions;
|
std::vector<float> mPositions;
|
||||||
std::vector<float> mNormals;
|
std::vector<float> mNormals;
|
||||||
std::vector<Ogre::uint8> mColours;
|
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)
|
friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r)
|
||||||
{ return o; }
|
{ return o; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LayersRequestData
|
||||||
|
{
|
||||||
|
std::vector<QuadTreeNode*> mNodes;
|
||||||
|
bool mPack;
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& o, const LayersRequestData& r)
|
||||||
|
{ return o; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LayersResponseData
|
||||||
|
{
|
||||||
|
std::vector<LayerCollection> mLayerCollections;
|
||||||
|
|
||||||
|
friend std::ostream& operator<<(std::ostream& o, const LayersResponseData& r)
|
||||||
|
{ return o; }
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue