forked from teamnwah/openmw-tes3coop
Refactor non-distant land terrain path to a grid based implementation (Fixes #1562)
This commit is contained in:
parent
4949aa1fbb
commit
e25fa6c157
17 changed files with 825 additions and 495 deletions
|
@ -15,7 +15,7 @@ add_openmw_dir (mwrender
|
|||
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
|
||||
actors objects renderinginterface localmap occlusionquery water shadows
|
||||
characterpreview globalmap videoplayer ripplesimulation refraction
|
||||
terrainstorage renderconst effectmanager weaponanimation
|
||||
terrainstorage renderconst effectmanager weaponanimation terraingrid
|
||||
)
|
||||
|
||||
add_openmw_dir (mwinput
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <openengine/bullet/physic.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/terrain/world.hpp>
|
||||
#include <components/terrain/defaultworld.hpp>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
@ -45,6 +45,7 @@
|
|||
#include "globalmap.hpp"
|
||||
#include "terrainstorage.hpp"
|
||||
#include "effectmanager.hpp"
|
||||
#include "terraingrid.hpp"
|
||||
|
||||
using namespace MWRender;
|
||||
using namespace Ogre;
|
||||
|
@ -223,6 +224,9 @@ MWRender::Camera* RenderingManager::getCamera() const
|
|||
|
||||
void RenderingManager::removeCell (MWWorld::CellStore *store)
|
||||
{
|
||||
if (store->isExterior())
|
||||
mTerrain->unloadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
|
||||
|
||||
mLocalMap->saveFogOfWar(store);
|
||||
mObjects->removeCell(store);
|
||||
mActors->removeCell(store);
|
||||
|
@ -241,6 +245,9 @@ bool RenderingManager::toggleWater()
|
|||
|
||||
void RenderingManager::cellAdded (MWWorld::CellStore *store)
|
||||
{
|
||||
if (store->isExterior())
|
||||
mTerrain->loadCell(store->getCell()->getGridX(), store->getCell()->getGridY());
|
||||
|
||||
mObjects->buildStaticGeometry (*store);
|
||||
sh::Factory::getInstance().unloadUnreferencedMaterials();
|
||||
mDebugging->cellAdded(store);
|
||||
|
@ -1039,9 +1046,12 @@ void RenderingManager::enableTerrain(bool enable)
|
|||
{
|
||||
if (!mTerrain)
|
||||
{
|
||||
mTerrain = new Terrain::World(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
||||
Settings::Manager::getBool("distant land", "Terrain"),
|
||||
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
|
||||
if (Settings::Manager::getBool("distant land", "Terrain"))
|
||||
mTerrain = new Terrain::DefaultWorld(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
||||
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY, 1, 64);
|
||||
else
|
||||
mTerrain = new MWRender::TerrainGrid(mRendering.getScene(), new MWRender::TerrainStorage(), RV_Terrain,
|
||||
Settings::Manager::getBool("shader", "Terrain"), Terrain::Align_XY);
|
||||
mTerrain->applyMaterials(Settings::Manager::getBool("enabled", "Shadows"),
|
||||
Settings::Manager::getBool("split", "Shadows"));
|
||||
mTerrain->update(mRendering.getCamera()->getRealPosition());
|
||||
|
|
165
apps/openmw/mwrender/terraingrid.cpp
Normal file
165
apps/openmw/mwrender/terraingrid.cpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
#include "terraingrid.hpp"
|
||||
|
||||
#include <OgreSceneManager.h>
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreAxisAlignedBox.h>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include <components/terrain/chunk.hpp>
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
TerrainGrid::TerrainGrid(Ogre::SceneManager *sceneMgr, Terrain::Storage *storage, int visibilityFlags, bool shaders, Terrain::Alignment align)
|
||||
: Terrain::World(sceneMgr, storage, visibilityFlags, shaders, align)
|
||||
, mVisible(true)
|
||||
{
|
||||
mRootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
}
|
||||
|
||||
TerrainGrid::~TerrainGrid()
|
||||
{
|
||||
while (!mGrid.empty())
|
||||
{
|
||||
unloadCell(mGrid.begin()->first.first, mGrid.begin()->first.second);
|
||||
}
|
||||
|
||||
mSceneMgr->destroySceneNode(mRootNode);
|
||||
}
|
||||
|
||||
void TerrainGrid::update(const Ogre::Vector3 &cameraPos)
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainGrid::loadCell(int x, int y)
|
||||
{
|
||||
if (mGrid.find(std::make_pair(x, y)) != mGrid.end())
|
||||
return; // already loaded
|
||||
|
||||
Ogre::Vector2 center(x+0.5, y+0.5);
|
||||
float minH, maxH;
|
||||
if (!mStorage->getMinMaxHeights(1, center, minH, maxH))
|
||||
return; // no terrain defined
|
||||
|
||||
Ogre::Vector3 min (-0.5*mStorage->getCellWorldSize(),
|
||||
-0.5*mStorage->getCellWorldSize(),
|
||||
minH);
|
||||
Ogre::Vector3 max (0.5*mStorage->getCellWorldSize(),
|
||||
0.5*mStorage->getCellWorldSize(),
|
||||
maxH);
|
||||
|
||||
Ogre::AxisAlignedBox bounds(min, max);
|
||||
|
||||
GridElement element;
|
||||
|
||||
Ogre::Vector2 worldCenter = center*mStorage->getCellWorldSize();
|
||||
element.mSceneNode = mRootNode->createChildSceneNode(Ogre::Vector3(worldCenter.x, worldCenter.y, 0));
|
||||
|
||||
std::vector<float> positions;
|
||||
std::vector<float> normals;
|
||||
std::vector<Ogre::uint8> colours;
|
||||
mStorage->fillVertexBuffers(0, 1, center, mAlign, positions, normals, colours);
|
||||
|
||||
element.mChunk = new Terrain::Chunk(mCache.getUVBuffer(), bounds, positions, normals, colours);
|
||||
element.mChunk->setIndexBuffer(mCache.getIndexBuffer(0));
|
||||
|
||||
std::vector<Ogre::PixelBox> blendmaps;
|
||||
std::vector<Terrain::LayerInfo> layerList;
|
||||
mStorage->getBlendmaps(1, center, mShaders, blendmaps, layerList);
|
||||
|
||||
element.mMaterialGenerator.setLayerList(layerList);
|
||||
|
||||
// upload blendmaps to GPU
|
||||
std::vector<Ogre::TexturePtr> blendTextures;
|
||||
for (std::vector<Ogre::PixelBox>::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)
|
||||
{
|
||||
static int count=0;
|
||||
Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/"
|
||||
+ Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
Ogre::TEX_TYPE_2D, it->getWidth(), it->getHeight(), 0, it->format);
|
||||
|
||||
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(it->data, it->getWidth()*it->getHeight()*Ogre::PixelUtil::getNumElemBytes(it->format), true));
|
||||
map->loadRawData(stream, it->getWidth(), it->getHeight(), it->format);
|
||||
blendTextures.push_back(map);
|
||||
}
|
||||
|
||||
element.mMaterialGenerator.setBlendmapList(blendTextures);
|
||||
|
||||
element.mSceneNode->attachObject(element.mChunk);
|
||||
updateMaterial(element);
|
||||
|
||||
mGrid[std::make_pair(x,y)] = element;
|
||||
}
|
||||
|
||||
void TerrainGrid::updateMaterial(GridElement &element)
|
||||
{
|
||||
element.mMaterialGenerator.enableShadows(getShadowsEnabled());
|
||||
element.mMaterialGenerator.enableSplitShadows(getSplitShadowsEnabled());
|
||||
element.mChunk->setMaterial(element.mMaterialGenerator.generate());
|
||||
}
|
||||
|
||||
void TerrainGrid::unloadCell(int x, int y)
|
||||
{
|
||||
Grid::iterator it = mGrid.find(std::make_pair(x,y));
|
||||
if (it == mGrid.end())
|
||||
return;
|
||||
|
||||
GridElement& element = it->second;
|
||||
delete element.mChunk;
|
||||
element.mChunk = NULL;
|
||||
|
||||
const std::vector<Ogre::TexturePtr>& blendmaps = element.mMaterialGenerator.getBlendmapList();
|
||||
for (std::vector<Ogre::TexturePtr>::const_iterator it = blendmaps.begin(); it != blendmaps.end(); ++it)
|
||||
Ogre::TextureManager::getSingleton().remove((*it)->getName());
|
||||
|
||||
mSceneMgr->destroySceneNode(element.mSceneNode);
|
||||
element.mSceneNode = NULL;
|
||||
|
||||
mGrid.erase(it);
|
||||
}
|
||||
|
||||
void TerrainGrid::applyMaterials(bool shadows, bool splitShadows)
|
||||
{
|
||||
mShadows = shadows;
|
||||
mSplitShadows = splitShadows;
|
||||
for (Grid::iterator it = mGrid.begin(); it != mGrid.end(); ++it)
|
||||
{
|
||||
updateMaterial(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
bool TerrainGrid::getVisible()
|
||||
{
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
void TerrainGrid::setVisible(bool visible)
|
||||
{
|
||||
mVisible = visible;
|
||||
mRootNode->setVisible(visible);
|
||||
}
|
||||
|
||||
Ogre::AxisAlignedBox TerrainGrid::getWorldBoundingBox (const Ogre::Vector2& center)
|
||||
{
|
||||
int cellX, cellY;
|
||||
MWBase::Environment::get().getWorld()->positionToIndex(center.x, center.y, cellX, cellY);
|
||||
|
||||
Grid::iterator it = mGrid.find(std::make_pair(cellX, cellY));
|
||||
if (it == mGrid.end())
|
||||
return Ogre::AxisAlignedBox::BOX_NULL;
|
||||
|
||||
Terrain::Chunk* chunk = it->second.mChunk;
|
||||
Ogre::SceneNode* node = it->second.mSceneNode;
|
||||
Ogre::AxisAlignedBox box = chunk->getBoundingBox();
|
||||
box = Ogre::AxisAlignedBox(box.getMinimum() + node->getPosition(), box.getMaximum() + node->getPosition());
|
||||
return box;
|
||||
}
|
||||
|
||||
void TerrainGrid::syncLoad()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
75
apps/openmw/mwrender/terraingrid.hpp
Normal file
75
apps/openmw/mwrender/terraingrid.hpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef OPENMW_MWRENDER_TERRAINGRID_H
|
||||
#define OPENMW_MWRENDER_TERRAINGRID_H
|
||||
|
||||
#include <components/terrain/world.hpp>
|
||||
#include <components/terrain/material.hpp>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
class Chunk;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
struct GridElement
|
||||
{
|
||||
Ogre::SceneNode* mSceneNode;
|
||||
|
||||
Terrain::MaterialGenerator mMaterialGenerator;
|
||||
|
||||
Terrain::Chunk* mChunk;
|
||||
};
|
||||
|
||||
/// @brief Simple terrain implementation that loads cells in a grid, with no LOD
|
||||
class TerrainGrid : public Terrain::World
|
||||
{
|
||||
public:
|
||||
/// @note takes ownership of \a storage
|
||||
/// @param sceneMgr scene manager to use
|
||||
/// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..)
|
||||
/// @param visbilityFlags visibility flags for the created meshes
|
||||
/// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually
|
||||
/// faster so this is just here for compatibility.
|
||||
/// @param align The align of the terrain, see Alignment enum
|
||||
TerrainGrid(Ogre::SceneManager* sceneMgr,
|
||||
Terrain::Storage* storage, int visibilityFlags, bool shaders, Terrain::Alignment align);
|
||||
~TerrainGrid();
|
||||
|
||||
/// Update chunk LODs according to this camera position
|
||||
virtual void update (const Ogre::Vector3& cameraPos);
|
||||
|
||||
virtual void loadCell(int x, int y);
|
||||
virtual void unloadCell(int x, int y);
|
||||
|
||||
/// Get the world bounding box of a chunk of terrain centered at \a center
|
||||
virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center);
|
||||
|
||||
/// Show or hide the whole terrain
|
||||
/// @note this setting may be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden
|
||||
virtual void setVisible(bool visible);
|
||||
virtual bool getVisible();
|
||||
|
||||
/// Recreate materials used by terrain chunks. This should be called whenever settings of
|
||||
/// the material factory are changed. (Relying on the factory to update those materials is not
|
||||
/// enough, since turning a feature on/off can change the number of texture units available for layer/blend
|
||||
/// textures, and to properly respond to this we may need to change the structure of the material, such as
|
||||
/// adding or removing passes. This can only be achieved by a full rebuild.)
|
||||
virtual void applyMaterials(bool shadows, bool splitShadows);
|
||||
|
||||
/// Wait until all background loading is complete.
|
||||
virtual void syncLoad();
|
||||
|
||||
private:
|
||||
void updateMaterial (GridElement& element);
|
||||
|
||||
typedef std::map<std::pair<int, int>, GridElement> Grid;
|
||||
Grid mGrid;
|
||||
|
||||
Ogre::SceneNode* mRootNode;
|
||||
bool mVisible;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -46,7 +46,7 @@ namespace MWRender
|
|||
/// Create textures holding layer blend values for a terrain chunk.
|
||||
/// @note The terrain chunk shouldn't be larger than one cell since otherwise we might
|
||||
/// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.
|
||||
/// @note May be called from *one* background thread.
|
||||
/// @note May be called from background threads.
|
||||
/// @param chunkSize size of the terrain 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) -
|
||||
|
@ -62,7 +62,7 @@ namespace MWRender
|
|||
/// 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.
|
||||
/// @note May be called from background threads.
|
||||
/// @param nodes A collection of nodes for which to retrieve the aforementioned data
|
||||
/// @param out Output vector
|
||||
/// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
|
||||
|
|
|
@ -72,7 +72,7 @@ add_component_dir (translation
|
|||
|
||||
add_definitions(-DTERRAIN_USE_SHADER=1)
|
||||
add_component_dir (terrain
|
||||
quadtreenode chunk world storage material buffercache defs
|
||||
quadtreenode chunk world defaultworld storage material buffercache defs backgroundloader
|
||||
)
|
||||
|
||||
add_component_dir (loadinglistener
|
||||
|
|
0
components/terrain/backgroundloader.cpp
Normal file
0
components/terrain/backgroundloader.cpp
Normal file
|
@ -8,19 +8,17 @@
|
|||
|
||||
#include <extern/shiny/Main/Factory.hpp>
|
||||
|
||||
|
||||
#include "world.hpp" // FIXME: for LoadResponseData, move to backgroundloader.hpp
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
|
||||
Chunk::Chunk(Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data)
|
||||
Chunk::Chunk(Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds,
|
||||
const std::vector<float>& positions, const std::vector<float>& normals, const std::vector<Ogre::uint8>& colours)
|
||||
: mBounds(bounds)
|
||||
, mOwnMaterial(false)
|
||||
{
|
||||
mVertexData = OGRE_NEW Ogre::VertexData;
|
||||
mVertexData->vertexStart = 0;
|
||||
mVertexData->vertexCount = data.mPositions.size()/3;
|
||||
mVertexData->vertexCount = positions.size()/3;
|
||||
|
||||
// Set up the vertex declaration, which specifies the info for each vertex (normals, colors, UVs, etc)
|
||||
Ogre::VertexDeclaration* vertexDecl = mVertexData->vertexDeclaration;
|
||||
|
@ -48,9 +46,9 @@ namespace Terrain
|
|||
Ogre::HardwareVertexBufferSharedPtr colourBuffer = mgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR),
|
||||
mVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC);
|
||||
|
||||
vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &data.mPositions[0], true);
|
||||
normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &data.mNormals[0], true);
|
||||
colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &data.mColours[0], true);
|
||||
vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true);
|
||||
normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true);
|
||||
colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colours[0], true);
|
||||
|
||||
mVertexData->vertexBufferBinding->setBinding(0, vertexBuffer);
|
||||
mVertexData->vertexBufferBinding->setBinding(1, normalBuffer);
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
namespace Terrain
|
||||
{
|
||||
|
||||
class BufferCache;
|
||||
struct LoadResponseData;
|
||||
|
||||
/**
|
||||
* @brief Renders a chunk of terrain, either using alpha splatting or a composite map.
|
||||
* @brief A movable object representing a chunk of terrain.
|
||||
*/
|
||||
class Chunk : public Ogre::Renderable, public Ogre::MovableObject
|
||||
{
|
||||
public:
|
||||
Chunk (Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds, const LoadResponseData& data);
|
||||
Chunk (Ogre::HardwareVertexBufferSharedPtr uvBuffer, const Ogre::AxisAlignedBox& bounds,
|
||||
const std::vector<float>& positions,
|
||||
const std::vector<float>& normals,
|
||||
const std::vector<Ogre::uint8>& colours);
|
||||
|
||||
virtual ~Chunk();
|
||||
|
||||
|
|
315
components/terrain/defaultworld.cpp
Normal file
315
components/terrain/defaultworld.cpp
Normal file
|
@ -0,0 +1,315 @@
|
|||
#include "defaultworld.hpp"
|
||||
|
||||
#include <OgreAxisAlignedBox.h>
|
||||
#include <OgreCamera.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreRenderTexture.h>
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreRoot.h>
|
||||
|
||||
#include "storage.hpp"
|
||||
#include "quadtreenode.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool isPowerOfTwo(int x)
|
||||
{
|
||||
return ( (x > 0) && ((x & (x - 1)) == 0) );
|
||||
}
|
||||
|
||||
int nextPowerOfTwo (int v)
|
||||
{
|
||||
if (isPowerOfTwo(v)) return v;
|
||||
int depth=0;
|
||||
while(v)
|
||||
{
|
||||
v >>= 1;
|
||||
depth++;
|
||||
}
|
||||
return 1 << depth;
|
||||
}
|
||||
|
||||
Terrain::QuadTreeNode* findNode (const Ogre::Vector2& center, Terrain::QuadTreeNode* node)
|
||||
{
|
||||
if (center == node->getCenter())
|
||||
return node;
|
||||
|
||||
if (center.x > node->getCenter().x && center.y > node->getCenter().y)
|
||||
return findNode(center, node->getChild(Terrain::NE));
|
||||
else if (center.x > node->getCenter().x && center.y < node->getCenter().y)
|
||||
return findNode(center, node->getChild(Terrain::SE));
|
||||
else if (center.x < node->getCenter().x && center.y > node->getCenter().y)
|
||||
return findNode(center, node->getChild(Terrain::NW));
|
||||
else //if (center.x < node->getCenter().x && center.y < node->getCenter().y)
|
||||
return findNode(center, node->getChild(Terrain::SW));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
|
||||
const Ogre::uint REQ_ID_CHUNK = 1;
|
||||
const Ogre::uint REQ_ID_LAYERS = 2;
|
||||
|
||||
DefaultWorld::DefaultWorld(Ogre::SceneManager* sceneMgr,
|
||||
Storage* storage, int visibilityFlags, bool shaders, Alignment align, float minBatchSize, float maxBatchSize)
|
||||
: World(sceneMgr, storage, visibilityFlags, shaders, align)
|
||||
, mMinBatchSize(minBatchSize)
|
||||
, mMaxBatchSize(maxBatchSize)
|
||||
, mVisible(true)
|
||||
, mMaxX(0)
|
||||
, mMinX(0)
|
||||
, mMaxY(0)
|
||||
, mMinY(0)
|
||||
, mChunksLoading(0)
|
||||
, mWorkQueueChannel(0)
|
||||
, mLayerLoadPending(true)
|
||||
{
|
||||
#if TERRAIN_USE_SHADER == 0
|
||||
if (mShaders)
|
||||
std::cerr << "Compiled Terrain without shader support, disabling..." << std::endl;
|
||||
mShaders = false;
|
||||
#endif
|
||||
|
||||
mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
|
||||
|
||||
/// \todo make composite map size configurable
|
||||
Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a");
|
||||
mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual(
|
||||
"terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
Ogre::TEX_TYPE_2D, 128, 128, 0, Ogre::PF_A8B8G8R8, Ogre::TU_RENDERTARGET);
|
||||
mCompositeMapRenderTarget = mCompositeMapRenderTexture->getBuffer()->getRenderTarget();
|
||||
mCompositeMapRenderTarget->setAutoUpdated(false);
|
||||
mCompositeMapRenderTarget->addViewport(compositeMapCam);
|
||||
|
||||
storage->getBounds(mMinX, mMaxX, mMinY, mMaxY);
|
||||
|
||||
int origSizeX = mMaxX-mMinX;
|
||||
int origSizeY = mMaxY-mMinY;
|
||||
|
||||
// Dividing a quad tree only works well for powers of two, so round up to the nearest one
|
||||
int size = nextPowerOfTwo(std::max(origSizeX, origSizeY));
|
||||
|
||||
// Adjust the center according to the new size
|
||||
float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f;
|
||||
float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f;
|
||||
|
||||
mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
|
||||
// While building the quadtree, remember leaf nodes since we need to load their layers
|
||||
LayersRequestData data;
|
||||
data.mPack = getShadersEnabled();
|
||||
|
||||
mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL);
|
||||
buildQuadTree(mRootNode, data.mNodes);
|
||||
//loadingListener->indicateProgress();
|
||||
mRootNode->initAabb();
|
||||
//loadingListener->indicateProgress();
|
||||
mRootNode->initNeighbours();
|
||||
//loadingListener->indicateProgress();
|
||||
|
||||
Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue();
|
||||
mWorkQueueChannel = wq->getChannel("LargeTerrain");
|
||||
wq->addRequestHandler(mWorkQueueChannel, this);
|
||||
wq->addResponseHandler(mWorkQueueChannel, this);
|
||||
|
||||
// Start loading layers in the background (for leaf nodes)
|
||||
wq->addRequest(mWorkQueueChannel, REQ_ID_LAYERS, Ogre::Any(data));
|
||||
}
|
||||
|
||||
DefaultWorld::~DefaultWorld()
|
||||
{
|
||||
Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue();
|
||||
wq->removeRequestHandler(mWorkQueueChannel, this);
|
||||
wq->removeResponseHandler(mWorkQueueChannel, this);
|
||||
|
||||
delete mRootNode;
|
||||
}
|
||||
|
||||
void DefaultWorld::buildQuadTree(QuadTreeNode *node, std::vector<QuadTreeNode*>& leafs)
|
||||
{
|
||||
float halfSize = node->getSize()/2.f;
|
||||
|
||||
if (node->getSize() <= mMinBatchSize)
|
||||
{
|
||||
// We arrived at a leaf
|
||||
float minZ,maxZ;
|
||||
Ogre::Vector2 center = node->getCenter();
|
||||
float cellWorldSize = getStorage()->getCellWorldSize();
|
||||
if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ))
|
||||
{
|
||||
Ogre::AxisAlignedBox bounds(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ),
|
||||
Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ));
|
||||
convertBounds(bounds);
|
||||
node->setBoundingBox(bounds);
|
||||
leafs.push_back(node);
|
||||
}
|
||||
else
|
||||
node->markAsDummy(); // no data available for this node, skip it
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->getCenter().x - halfSize > mMaxX
|
||||
|| node->getCenter().x + halfSize < mMinX
|
||||
|| node->getCenter().y - halfSize > mMaxY
|
||||
|| node->getCenter().y + halfSize < mMinY )
|
||||
// Out of bounds of the actual terrain - this will happen because
|
||||
// we rounded the size up to the next power of two
|
||||
{
|
||||
node->markAsDummy();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not a leaf, create its children
|
||||
node->createChild(SW, halfSize, node->getCenter() - halfSize/2.f);
|
||||
node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -halfSize/2.f));
|
||||
node->createChild(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f));
|
||||
node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f);
|
||||
buildQuadTree(node->getChild(SW), leafs);
|
||||
buildQuadTree(node->getChild(SE), leafs);
|
||||
buildQuadTree(node->getChild(NW), leafs);
|
||||
buildQuadTree(node->getChild(NE), leafs);
|
||||
|
||||
// if all children are dummy, we are also dummy
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
if (!node->getChild((ChildDirection)i)->isDummy())
|
||||
return;
|
||||
}
|
||||
node->markAsDummy();
|
||||
}
|
||||
|
||||
void DefaultWorld::update(const Ogre::Vector3& cameraPos)
|
||||
{
|
||||
if (!mVisible)
|
||||
return;
|
||||
mRootNode->update(cameraPos);
|
||||
mRootNode->updateIndexBuffers();
|
||||
}
|
||||
|
||||
Ogre::AxisAlignedBox DefaultWorld::getWorldBoundingBox (const Ogre::Vector2& center)
|
||||
{
|
||||
if (center.x > mMaxX
|
||||
|| center.x < mMinX
|
||||
|| center.y > mMaxY
|
||||
|| center.y < mMinY)
|
||||
return Ogre::AxisAlignedBox::BOX_NULL;
|
||||
QuadTreeNode* node = findNode(center, mRootNode);
|
||||
return node->getWorldBoundingBox();
|
||||
}
|
||||
|
||||
void DefaultWorld::renderCompositeMap(Ogre::TexturePtr target)
|
||||
{
|
||||
mCompositeMapRenderTarget->update();
|
||||
target->getBuffer()->blit(mCompositeMapRenderTexture->getBuffer());
|
||||
}
|
||||
|
||||
void DefaultWorld::clearCompositeMapSceneManager()
|
||||
{
|
||||
mCompositeMapSceneMgr->destroyAllManualObjects();
|
||||
mCompositeMapSceneMgr->clearScene();
|
||||
}
|
||||
|
||||
void DefaultWorld::applyMaterials(bool shadows, bool splitShadows)
|
||||
{
|
||||
mShadows = shadows;
|
||||
mSplitShadows = splitShadows;
|
||||
mRootNode->applyMaterials();
|
||||
}
|
||||
|
||||
void DefaultWorld::setVisible(bool visible)
|
||||
{
|
||||
if (visible && !mVisible)
|
||||
mSceneMgr->getRootSceneNode()->addChild(mRootSceneNode);
|
||||
else if (!visible && mVisible)
|
||||
mSceneMgr->getRootSceneNode()->removeChild(mRootSceneNode);
|
||||
|
||||
mVisible = visible;
|
||||
}
|
||||
|
||||
bool DefaultWorld::getVisible()
|
||||
{
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
void DefaultWorld::syncLoad()
|
||||
{
|
||||
while (mChunksLoading || mLayerLoadPending)
|
||||
{
|
||||
OGRE_THREAD_SLEEP(0);
|
||||
Ogre::Root::getSingleton().getWorkQueue()->processResponses();
|
||||
}
|
||||
}
|
||||
|
||||
Ogre::WorkQueue::Response* DefaultWorld::handleRequest(const Ogre::WorkQueue::Request *req, const Ogre::WorkQueue *srcQ)
|
||||
{
|
||||
if (req->getType() == REQ_ID_CHUNK)
|
||||
{
|
||||
const LoadRequestData data = Ogre::any_cast<LoadRequestData>(req->getData());
|
||||
|
||||
QuadTreeNode* node = data.mNode;
|
||||
|
||||
LoadResponseData* responseData = new LoadResponseData();
|
||||
|
||||
getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(),
|
||||
responseData->mPositions, responseData->mNormals, responseData->mColours);
|
||||
|
||||
return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData));
|
||||
}
|
||||
else // REQ_ID_LAYERS
|
||||
{
|
||||
const LayersRequestData data = Ogre::any_cast<LayersRequestData>(req->getData());
|
||||
|
||||
LayersResponseData* responseData = new LayersResponseData();
|
||||
|
||||
getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack);
|
||||
|
||||
return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData));
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultWorld::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ)
|
||||
{
|
||||
assert(res->succeeded() && "Response failure not handled");
|
||||
|
||||
if (res->getRequest()->getType() == REQ_ID_CHUNK)
|
||||
{
|
||||
LoadResponseData* data = Ogre::any_cast<LoadResponseData*>(res->getData());
|
||||
|
||||
const LoadRequestData requestData = Ogre::any_cast<LoadRequestData>(res->getRequest()->getData());
|
||||
|
||||
requestData.mNode->load(*data);
|
||||
|
||||
delete data;
|
||||
|
||||
--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);
|
||||
}
|
||||
|
||||
delete data;
|
||||
|
||||
mRootNode->loadMaterials();
|
||||
|
||||
mLayerLoadPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultWorld::queueLoad(QuadTreeNode *node)
|
||||
{
|
||||
LoadRequestData data;
|
||||
data.mNode = node;
|
||||
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, REQ_ID_CHUNK, Ogre::Any(data));
|
||||
++mChunksLoading;
|
||||
}
|
||||
}
|
156
components/terrain/defaultworld.hpp
Normal file
156
components/terrain/defaultworld.hpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
#ifndef COMPONENTS_TERRAIN_H
|
||||
#define COMPONENTS_TERRAIN_H
|
||||
|
||||
#include <OgreAxisAlignedBox.h>
|
||||
#include <OgreTexture.h>
|
||||
#include <OgreWorkQueue.h>
|
||||
|
||||
#include "world.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Camera;
|
||||
}
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
|
||||
class QuadTreeNode;
|
||||
class Storage;
|
||||
|
||||
/**
|
||||
* @brief A quadtree-based terrain implementation suitable for large data sets. \n
|
||||
* Near cells are rendered with alpha splatting, distant cells are merged
|
||||
* together in batches and have their layers pre-rendered onto a composite map. \n
|
||||
* Cracks at LOD transitions are avoided using stitching.
|
||||
* @note Multiple cameras are not supported yet
|
||||
*/
|
||||
class DefaultWorld : public World, public Ogre::WorkQueue::RequestHandler, public Ogre::WorkQueue::ResponseHandler
|
||||
{
|
||||
public:
|
||||
/// @note takes ownership of \a storage
|
||||
/// @param sceneMgr scene manager to use
|
||||
/// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..)
|
||||
/// @param visbilityFlags visibility flags for the created meshes
|
||||
/// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually
|
||||
/// faster so this is just here for compatibility.
|
||||
/// @param align The align of the terrain, see Alignment enum
|
||||
/// @param minBatchSize Minimum size of a terrain batch along one side (in cell units). Used for building the quad tree.
|
||||
/// @param maxBatchSize Maximum size of a terrain batch along one side (in cell units). Used when traversing the quad tree.
|
||||
DefaultWorld(Ogre::SceneManager* sceneMgr,
|
||||
Storage* storage, int visibilityFlags, bool shaders, Alignment align, float minBatchSize, float maxBatchSize);
|
||||
~DefaultWorld();
|
||||
|
||||
/// Update chunk LODs according to this camera position
|
||||
/// @note Calling this method might lead to composite textures being rendered, so it is best
|
||||
/// not to call it when render commands are still queued, since that would cause a flush.
|
||||
virtual void update (const Ogre::Vector3& cameraPos);
|
||||
|
||||
/// Get the world bounding box of a chunk of terrain centered at \a center
|
||||
virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center);
|
||||
|
||||
Ogre::SceneNode* getRootSceneNode() { return mRootSceneNode; }
|
||||
|
||||
/// Show or hide the whole terrain
|
||||
/// @note this setting will be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden
|
||||
virtual void setVisible(bool visible);
|
||||
virtual bool getVisible();
|
||||
|
||||
/// Recreate materials used by terrain chunks. This should be called whenever settings of
|
||||
/// the material factory are changed. (Relying on the factory to update those materials is not
|
||||
/// enough, since turning a feature on/off can change the number of texture units available for layer/blend
|
||||
/// textures, and to properly respond to this we may need to change the structure of the material, such as
|
||||
/// adding or removing passes. This can only be achieved by a full rebuild.)
|
||||
virtual void applyMaterials(bool shadows, bool splitShadows);
|
||||
|
||||
int getMaxBatchSize() { return mMaxBatchSize; }
|
||||
|
||||
/// Wait until all background loading is complete.
|
||||
void syncLoad();
|
||||
|
||||
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 mVisible;
|
||||
|
||||
QuadTreeNode* mRootNode;
|
||||
Ogre::SceneNode* mRootSceneNode;
|
||||
|
||||
/// The number of chunks currently loading in a background thread. If 0, we have finished loading!
|
||||
int mChunksLoading;
|
||||
|
||||
Ogre::SceneManager* mCompositeMapSceneMgr;
|
||||
|
||||
/// Bounds in cell units
|
||||
float mMinX, mMaxX, mMinY, mMaxY;
|
||||
|
||||
/// Minimum size of a terrain batch along one side (in cell units)
|
||||
float mMinBatchSize;
|
||||
/// Maximum size of a terrain batch along one side (in cell units)
|
||||
float mMaxBatchSize;
|
||||
|
||||
void buildQuadTree(QuadTreeNode* node, std::vector<QuadTreeNode*>& leafs);
|
||||
|
||||
// Are layers for leaf nodes loaded? This is done once at startup (but in a background thread)
|
||||
bool mLayerLoadPending;
|
||||
|
||||
public:
|
||||
// ----INTERNAL----
|
||||
Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; }
|
||||
|
||||
bool areLayersLoaded() { return !mLayerLoadPending; }
|
||||
|
||||
// Delete all quads
|
||||
void clearCompositeMapSceneManager();
|
||||
void renderCompositeMap (Ogre::TexturePtr target);
|
||||
|
||||
// Adds a WorkQueue request to load a chunk for this node in the background.
|
||||
void queueLoad (QuadTreeNode* node);
|
||||
|
||||
private:
|
||||
Ogre::RenderTarget* mCompositeMapRenderTarget;
|
||||
Ogre::TexturePtr mCompositeMapRenderTexture;
|
||||
};
|
||||
|
||||
struct LoadRequestData
|
||||
{
|
||||
QuadTreeNode* mNode;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r)
|
||||
{ return o; }
|
||||
};
|
||||
|
||||
struct LoadResponseData
|
||||
{
|
||||
std::vector<float> mPositions;
|
||||
std::vector<float> mNormals;
|
||||
std::vector<Ogre::uint8> mColours;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r)
|
||||
{ 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
|
|
@ -36,8 +36,8 @@ std::string getBlendmapComponentForLayer (int layerIndex)
|
|||
namespace Terrain
|
||||
{
|
||||
|
||||
MaterialGenerator::MaterialGenerator(bool shaders)
|
||||
: mShaders(shaders)
|
||||
MaterialGenerator::MaterialGenerator()
|
||||
: mShaders(true)
|
||||
, mShadows(false)
|
||||
, mSplitShadows(false)
|
||||
, mNormalMapping(true)
|
||||
|
|
|
@ -11,11 +11,7 @@ namespace Terrain
|
|||
class MaterialGenerator
|
||||
{
|
||||
public:
|
||||
/// @param layerList layer textures
|
||||
/// @param blendmapList blend textures
|
||||
/// @param shaders Whether to use shaders. With a shader, blendmap packing can be used (4 channels instead of one),
|
||||
/// so if this parameter is true, then the supplied blend maps are expected to be packed.
|
||||
MaterialGenerator (bool shaders);
|
||||
MaterialGenerator ();
|
||||
|
||||
void setLayerList (const std::vector<LayerInfo>& layerList) { mLayerList = layerList; }
|
||||
bool hasLayers() { return mLayerList.size(); }
|
||||
|
@ -23,6 +19,7 @@ namespace Terrain
|
|||
const std::vector<Ogre::TexturePtr>& getBlendmapList() { return mBlendmapList; }
|
||||
void setCompositeMap (const std::string& name) { mCompositeMap = name; }
|
||||
|
||||
void enableShaders(bool shaders) { mShaders = shaders; }
|
||||
void enableShadows(bool shadows) { mShadows = shadows; }
|
||||
void enableNormalMapping(bool normalMapping) { mNormalMapping = normalMapping; }
|
||||
void enableParallaxMapping(bool parallaxMapping) { mParallaxMapping = parallaxMapping; }
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <OgreMaterialManager.h>
|
||||
#include <OgreTextureManager.h>
|
||||
|
||||
#include "world.hpp"
|
||||
#include "defaultworld.hpp"
|
||||
#include "chunk.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "buffercache.hpp"
|
||||
|
@ -142,7 +142,7 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent)
|
||||
QuadTreeNode::QuadTreeNode(DefaultWorld* terrain, ChildDirection dir, float size, const Ogre::Vector2 ¢er, QuadTreeNode* parent)
|
||||
: mMaterialGenerator(NULL)
|
||||
, mIsDummy(false)
|
||||
, mSize(size)
|
||||
|
@ -178,7 +178,8 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const
|
|||
|
||||
mSceneNode->setPosition(sceneNodePos);
|
||||
|
||||
mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled());
|
||||
mMaterialGenerator = new MaterialGenerator();
|
||||
mMaterialGenerator->enableShaders(mTerrain->getShadersEnabled());
|
||||
}
|
||||
|
||||
void QuadTreeNode::createChild(ChildDirection id, float size, const Ogre::Vector2 ¢er)
|
||||
|
@ -386,11 +387,9 @@ void QuadTreeNode::load(const LoadResponseData &data)
|
|||
{
|
||||
assert (!mChunk);
|
||||
|
||||
mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data);
|
||||
mChunk->setVisibilityFlags(mTerrain->getVisiblityFlags());
|
||||
mChunk = new Chunk(mTerrain->getBufferCache().getUVBuffer(), mBounds, data.mPositions, data.mNormals, data.mColours);
|
||||
mChunk->setVisibilityFlags(mTerrain->getVisibilityFlags());
|
||||
mChunk->setCastShadows(true);
|
||||
if (!mTerrain->getDistantLandEnabled())
|
||||
mChunk->setRenderingDistance(8192);
|
||||
mSceneNode->attachObject(mChunk);
|
||||
|
||||
mMaterialGenerator->enableShadows(mTerrain->getShadowsEnabled());
|
||||
|
@ -550,7 +549,8 @@ void QuadTreeNode::prepareForCompositeMap(Ogre::TRect<float> area)
|
|||
if (mIsDummy)
|
||||
{
|
||||
// TODO - store this default material somewhere instead of creating one for each empty cell
|
||||
MaterialGenerator matGen(mTerrain->getShadersEnabled());
|
||||
MaterialGenerator matGen;
|
||||
matGen.enableShaders(mTerrain->getShadersEnabled());
|
||||
std::vector<LayerInfo> layer;
|
||||
layer.push_back(mTerrain->getStorage()->getDefaultLayer());
|
||||
matGen.setLayerList(layer);
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace Ogre
|
|||
|
||||
namespace Terrain
|
||||
{
|
||||
class World;
|
||||
class DefaultWorld;
|
||||
class Chunk;
|
||||
class MaterialGenerator;
|
||||
struct LoadResponseData;
|
||||
|
@ -48,7 +48,7 @@ namespace Terrain
|
|||
/// @param size size (in *cell* units!)
|
||||
/// @param center center (in *cell* units!)
|
||||
/// @param parent parent node
|
||||
QuadTreeNode (World* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent);
|
||||
QuadTreeNode (DefaultWorld* terrain, ChildDirection dir, float size, const Ogre::Vector2& center, QuadTreeNode* parent);
|
||||
~QuadTreeNode();
|
||||
|
||||
/// Rebuild all materials
|
||||
|
@ -95,7 +95,7 @@ namespace Terrain
|
|||
|
||||
const Ogre::AxisAlignedBox& getWorldBoundingBox();
|
||||
|
||||
World* getTerrain() { return mTerrain; }
|
||||
DefaultWorld* getTerrain() { return mTerrain; }
|
||||
|
||||
/// Adjust LODs for the given camera position, possibly splitting up chunks or merging them.
|
||||
/// @param force Always choose to render this node, even if not the perfect LOD.
|
||||
|
@ -158,7 +158,7 @@ namespace Terrain
|
|||
|
||||
Chunk* mChunk;
|
||||
|
||||
World* mTerrain;
|
||||
DefaultWorld* mTerrain;
|
||||
|
||||
Ogre::TexturePtr mCompositeMap;
|
||||
|
||||
|
|
|
@ -1,356 +1,60 @@
|
|||
#include "world.hpp"
|
||||
|
||||
#include <OgreAxisAlignedBox.h>
|
||||
#include <OgreCamera.h>
|
||||
#include <OgreHardwarePixelBuffer.h>
|
||||
#include <OgreTextureManager.h>
|
||||
#include <OgreRenderTexture.h>
|
||||
#include <OgreSceneNode.h>
|
||||
#include <OgreRoot.h>
|
||||
|
||||
#include "storage.hpp"
|
||||
#include "quadtreenode.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
bool isPowerOfTwo(int x)
|
||||
{
|
||||
return ( (x > 0) && ((x & (x - 1)) == 0) );
|
||||
}
|
||||
|
||||
int nextPowerOfTwo (int v)
|
||||
{
|
||||
if (isPowerOfTwo(v)) return v;
|
||||
int depth=0;
|
||||
while(v)
|
||||
{
|
||||
v >>= 1;
|
||||
depth++;
|
||||
}
|
||||
return 1 << depth;
|
||||
}
|
||||
|
||||
Terrain::QuadTreeNode* findNode (const Ogre::Vector2& center, Terrain::QuadTreeNode* node)
|
||||
{
|
||||
if (center == node->getCenter())
|
||||
return node;
|
||||
|
||||
if (center.x > node->getCenter().x && center.y > node->getCenter().y)
|
||||
return findNode(center, node->getChild(Terrain::NE));
|
||||
else if (center.x > node->getCenter().x && center.y < node->getCenter().y)
|
||||
return findNode(center, node->getChild(Terrain::SE));
|
||||
else if (center.x < node->getCenter().x && center.y > node->getCenter().y)
|
||||
return findNode(center, node->getChild(Terrain::NW));
|
||||
else //if (center.x < node->getCenter().x && center.y < node->getCenter().y)
|
||||
return findNode(center, node->getChild(Terrain::SW));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
|
||||
const Ogre::uint REQ_ID_CHUNK = 1;
|
||||
const Ogre::uint REQ_ID_LAYERS = 2;
|
||||
World::World(Ogre::SceneManager* sceneMgr,
|
||||
Storage* storage, int visibilityFlags, bool shaders, Alignment align)
|
||||
: mStorage(storage)
|
||||
, mSceneMgr(sceneMgr)
|
||||
, mVisibilityFlags(visibilityFlags)
|
||||
, mShaders(shaders)
|
||||
, mAlign(align)
|
||||
, mCache(storage->getCellVertices())
|
||||
{
|
||||
}
|
||||
|
||||
World::World(Ogre::SceneManager* sceneMgr,
|
||||
Storage* storage, int visibilityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize)
|
||||
: mStorage(storage)
|
||||
, mMinBatchSize(minBatchSize)
|
||||
, mMaxBatchSize(maxBatchSize)
|
||||
, mSceneMgr(sceneMgr)
|
||||
, mVisibilityFlags(visibilityFlags)
|
||||
, mDistantLand(distantLand)
|
||||
, mShaders(shaders)
|
||||
, mVisible(true)
|
||||
, mAlign(align)
|
||||
, mMaxX(0)
|
||||
, mMinX(0)
|
||||
, mMaxY(0)
|
||||
, mMinY(0)
|
||||
, mChunksLoading(0)
|
||||
, mWorkQueueChannel(0)
|
||||
, mCache(storage->getCellVertices())
|
||||
, mLayerLoadPending(true)
|
||||
World::~World()
|
||||
{
|
||||
delete mStorage;
|
||||
}
|
||||
|
||||
float World::getHeightAt(const Ogre::Vector3 &worldPos)
|
||||
{
|
||||
return mStorage->getHeightAt(worldPos);
|
||||
}
|
||||
|
||||
void World::convertPosition(float &x, float &y, float &z)
|
||||
{
|
||||
Terrain::convertPosition(mAlign, x, y, z);
|
||||
}
|
||||
|
||||
void World::convertPosition(Ogre::Vector3 &pos)
|
||||
{
|
||||
convertPosition(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
void World::convertBounds(Ogre::AxisAlignedBox& bounds)
|
||||
{
|
||||
switch (mAlign)
|
||||
{
|
||||
#if TERRAIN_USE_SHADER == 0
|
||||
if (mShaders)
|
||||
std::cerr << "Compiled Terrain without shader support, disabling..." << std::endl;
|
||||
mShaders = false;
|
||||
#endif
|
||||
|
||||
mCompositeMapSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC);
|
||||
|
||||
/// \todo make composite map size configurable
|
||||
Ogre::Camera* compositeMapCam = mCompositeMapSceneMgr->createCamera("a");
|
||||
mCompositeMapRenderTexture = Ogre::TextureManager::getSingleton().createManual(
|
||||
"terrain/comp/rt", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
|
||||
Ogre::TEX_TYPE_2D, 128, 128, 0, Ogre::PF_A8B8G8R8, Ogre::TU_RENDERTARGET);
|
||||
mCompositeMapRenderTarget = mCompositeMapRenderTexture->getBuffer()->getRenderTarget();
|
||||
mCompositeMapRenderTarget->setAutoUpdated(false);
|
||||
mCompositeMapRenderTarget->addViewport(compositeMapCam);
|
||||
|
||||
storage->getBounds(mMinX, mMaxX, mMinY, mMaxY);
|
||||
|
||||
int origSizeX = mMaxX-mMinX;
|
||||
int origSizeY = mMaxY-mMinY;
|
||||
|
||||
// Dividing a quad tree only works well for powers of two, so round up to the nearest one
|
||||
int size = nextPowerOfTwo(std::max(origSizeX, origSizeY));
|
||||
|
||||
// Adjust the center according to the new size
|
||||
float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f;
|
||||
float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f;
|
||||
|
||||
mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
|
||||
|
||||
// While building the quadtree, remember leaf nodes since we need to load their layers
|
||||
LayersRequestData data;
|
||||
data.mPack = getShadersEnabled();
|
||||
|
||||
mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL);
|
||||
buildQuadTree(mRootNode, data.mNodes);
|
||||
//loadingListener->indicateProgress();
|
||||
mRootNode->initAabb();
|
||||
//loadingListener->indicateProgress();
|
||||
mRootNode->initNeighbours();
|
||||
//loadingListener->indicateProgress();
|
||||
|
||||
Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue();
|
||||
mWorkQueueChannel = wq->getChannel("LargeTerrain");
|
||||
wq->addRequestHandler(mWorkQueueChannel, this);
|
||||
wq->addResponseHandler(mWorkQueueChannel, this);
|
||||
|
||||
// Start loading layers in the background (for leaf nodes)
|
||||
wq->addRequest(mWorkQueueChannel, REQ_ID_LAYERS, Ogre::Any(data));
|
||||
}
|
||||
|
||||
World::~World()
|
||||
{
|
||||
Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue();
|
||||
wq->removeRequestHandler(mWorkQueueChannel, this);
|
||||
wq->removeResponseHandler(mWorkQueueChannel, this);
|
||||
|
||||
delete mRootNode;
|
||||
delete mStorage;
|
||||
}
|
||||
|
||||
void World::buildQuadTree(QuadTreeNode *node, std::vector<QuadTreeNode*>& leafs)
|
||||
{
|
||||
float halfSize = node->getSize()/2.f;
|
||||
|
||||
if (node->getSize() <= mMinBatchSize)
|
||||
{
|
||||
// We arrived at a leaf
|
||||
float minZ,maxZ;
|
||||
Ogre::Vector2 center = node->getCenter();
|
||||
float cellWorldSize = getStorage()->getCellWorldSize();
|
||||
if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ))
|
||||
{
|
||||
Ogre::AxisAlignedBox bounds(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ),
|
||||
Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ));
|
||||
convertBounds(bounds);
|
||||
node->setBoundingBox(bounds);
|
||||
leafs.push_back(node);
|
||||
}
|
||||
else
|
||||
node->markAsDummy(); // no data available for this node, skip it
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->getCenter().x - halfSize > mMaxX
|
||||
|| node->getCenter().x + halfSize < mMinX
|
||||
|| node->getCenter().y - halfSize > mMaxY
|
||||
|| node->getCenter().y + halfSize < mMinY )
|
||||
// Out of bounds of the actual terrain - this will happen because
|
||||
// we rounded the size up to the next power of two
|
||||
{
|
||||
node->markAsDummy();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not a leaf, create its children
|
||||
node->createChild(SW, halfSize, node->getCenter() - halfSize/2.f);
|
||||
node->createChild(SE, halfSize, node->getCenter() + Ogre::Vector2(halfSize/2.f, -halfSize/2.f));
|
||||
node->createChild(NW, halfSize, node->getCenter() + Ogre::Vector2(-halfSize/2.f, halfSize/2.f));
|
||||
node->createChild(NE, halfSize, node->getCenter() + halfSize/2.f);
|
||||
buildQuadTree(node->getChild(SW), leafs);
|
||||
buildQuadTree(node->getChild(SE), leafs);
|
||||
buildQuadTree(node->getChild(NW), leafs);
|
||||
buildQuadTree(node->getChild(NE), leafs);
|
||||
|
||||
// if all children are dummy, we are also dummy
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
if (!node->getChild((ChildDirection)i)->isDummy())
|
||||
return;
|
||||
}
|
||||
node->markAsDummy();
|
||||
}
|
||||
|
||||
void World::update(const Ogre::Vector3& cameraPos)
|
||||
{
|
||||
if (!mVisible)
|
||||
return;
|
||||
mRootNode->update(cameraPos);
|
||||
mRootNode->updateIndexBuffers();
|
||||
}
|
||||
|
||||
Ogre::AxisAlignedBox World::getWorldBoundingBox (const Ogre::Vector2& center)
|
||||
{
|
||||
if (center.x > mMaxX
|
||||
|| center.x < mMinX
|
||||
|| center.y > mMaxY
|
||||
|| center.y < mMinY)
|
||||
return Ogre::AxisAlignedBox::BOX_NULL;
|
||||
QuadTreeNode* node = findNode(center, mRootNode);
|
||||
return node->getWorldBoundingBox();
|
||||
}
|
||||
|
||||
void World::renderCompositeMap(Ogre::TexturePtr target)
|
||||
{
|
||||
mCompositeMapRenderTarget->update();
|
||||
target->getBuffer()->blit(mCompositeMapRenderTexture->getBuffer());
|
||||
}
|
||||
|
||||
void World::clearCompositeMapSceneManager()
|
||||
{
|
||||
mCompositeMapSceneMgr->destroyAllManualObjects();
|
||||
mCompositeMapSceneMgr->clearScene();
|
||||
}
|
||||
|
||||
float World::getHeightAt(const Ogre::Vector3 &worldPos)
|
||||
{
|
||||
return mStorage->getHeightAt(worldPos);
|
||||
}
|
||||
|
||||
void World::applyMaterials(bool shadows, bool splitShadows)
|
||||
{
|
||||
mShadows = shadows;
|
||||
mSplitShadows = splitShadows;
|
||||
mRootNode->applyMaterials();
|
||||
}
|
||||
|
||||
void World::setVisible(bool visible)
|
||||
{
|
||||
if (visible && !mVisible)
|
||||
mSceneMgr->getRootSceneNode()->addChild(mRootSceneNode);
|
||||
else if (!visible && mVisible)
|
||||
mSceneMgr->getRootSceneNode()->removeChild(mRootSceneNode);
|
||||
|
||||
mVisible = visible;
|
||||
}
|
||||
|
||||
bool World::getVisible()
|
||||
{
|
||||
return mVisible;
|
||||
}
|
||||
|
||||
void World::convertPosition(float &x, float &y, float &z)
|
||||
{
|
||||
Terrain::convertPosition(mAlign, x, y, z);
|
||||
}
|
||||
|
||||
void World::convertPosition(Ogre::Vector3 &pos)
|
||||
{
|
||||
convertPosition(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
void World::convertBounds(Ogre::AxisAlignedBox& bounds)
|
||||
{
|
||||
switch (mAlign)
|
||||
{
|
||||
case Align_XY:
|
||||
return;
|
||||
case Align_XZ:
|
||||
convertPosition(bounds.getMinimum());
|
||||
convertPosition(bounds.getMaximum());
|
||||
// Because we changed sign of Z
|
||||
std::swap(bounds.getMinimum().z, bounds.getMaximum().z);
|
||||
return;
|
||||
case Align_YZ:
|
||||
convertPosition(bounds.getMinimum());
|
||||
convertPosition(bounds.getMaximum());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void World::syncLoad()
|
||||
{
|
||||
while (mChunksLoading || mLayerLoadPending)
|
||||
{
|
||||
OGRE_THREAD_SLEEP(0);
|
||||
Ogre::Root::getSingleton().getWorkQueue()->processResponses();
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
QuadTreeNode* node = data.mNode;
|
||||
|
||||
LoadResponseData* responseData = new LoadResponseData();
|
||||
|
||||
getStorage()->fillVertexBuffers(node->getNativeLodLevel(), node->getSize(), node->getCenter(), getAlign(),
|
||||
responseData->mPositions, responseData->mNormals, responseData->mColours);
|
||||
|
||||
return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData));
|
||||
}
|
||||
else // REQ_ID_LAYERS
|
||||
{
|
||||
const LayersRequestData data = Ogre::any_cast<LayersRequestData>(req->getData());
|
||||
|
||||
LayersResponseData* responseData = new LayersResponseData();
|
||||
|
||||
getStorage()->getBlendmaps(data.mNodes, responseData->mLayerCollections, data.mPack);
|
||||
|
||||
return OGRE_NEW Ogre::WorkQueue::Response(req, true, Ogre::Any(responseData));
|
||||
}
|
||||
}
|
||||
|
||||
void World::handleResponse(const Ogre::WorkQueue::Response *res, const Ogre::WorkQueue *srcQ)
|
||||
{
|
||||
assert(res->succeeded() && "Response failure not handled");
|
||||
|
||||
if (res->getRequest()->getType() == REQ_ID_CHUNK)
|
||||
{
|
||||
LoadResponseData* data = Ogre::any_cast<LoadResponseData*>(res->getData());
|
||||
|
||||
const LoadRequestData requestData = Ogre::any_cast<LoadRequestData>(res->getRequest()->getData());
|
||||
|
||||
requestData.mNode->load(*data);
|
||||
|
||||
delete data;
|
||||
|
||||
--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);
|
||||
}
|
||||
|
||||
delete data;
|
||||
|
||||
mRootNode->loadMaterials();
|
||||
|
||||
mLayerLoadPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
void World::queueLoad(QuadTreeNode *node)
|
||||
{
|
||||
LoadRequestData data;
|
||||
data.mNode = node;
|
||||
|
||||
Ogre::Root::getSingleton().getWorkQueue()->addRequest(mWorkQueueChannel, REQ_ID_CHUNK, Ogre::Any(data));
|
||||
++mChunksLoading;
|
||||
case Align_XY:
|
||||
return;
|
||||
case Align_XZ:
|
||||
convertPosition(bounds.getMinimum());
|
||||
convertPosition(bounds.getMaximum());
|
||||
// Because we changed sign of Z
|
||||
std::swap(bounds.getMinimum().z, bounds.getMaximum().z);
|
||||
return;
|
||||
case Align_YZ:
|
||||
convertPosition(bounds.getMinimum());
|
||||
convertPosition(bounds.getMaximum());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,50 +1,38 @@
|
|||
#ifndef COMPONENTS_TERRAIN_H
|
||||
#define COMPONENTS_TERRAIN_H
|
||||
#ifndef COMPONENTS_TERRAIN_WORLD_H
|
||||
#define COMPONENTS_TERRAIN_WORLD_H
|
||||
|
||||
#include <OgreAxisAlignedBox.h>
|
||||
#include <OgreTexture.h>
|
||||
#include <OgreWorkQueue.h>
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include "defs.hpp"
|
||||
#include "buffercache.hpp"
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class Camera;
|
||||
class SceneManager;
|
||||
}
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
|
||||
class QuadTreeNode;
|
||||
class Storage;
|
||||
|
||||
/**
|
||||
* @brief A quadtree-based terrain implementation suitable for large data sets. \n
|
||||
* Near cells are rendered with alpha splatting, distant cells are merged
|
||||
* together in batches and have their layers pre-rendered onto a composite map. \n
|
||||
* Cracks at LOD transitions are avoided using stitching.
|
||||
* @note Multiple cameras are not supported yet
|
||||
* @brief The basic interface for a terrain world. How the terrain chunks are paged and displayed
|
||||
* is up to the implementation.
|
||||
*/
|
||||
class World : public Ogre::WorkQueue::RequestHandler, public Ogre::WorkQueue::ResponseHandler
|
||||
class World
|
||||
{
|
||||
public:
|
||||
/// @note takes ownership of \a storage
|
||||
/// @param sceneMgr scene manager to use
|
||||
/// @param storage Storage instance to get terrain data from (heights, normals, colors, textures..)
|
||||
/// @param visbilityFlags visibility flags for the created meshes
|
||||
/// @param distantLand Whether to draw all of the terrain, or only a 3x3 grid around the camera.
|
||||
/// This is a temporary option until it can be streamlined.
|
||||
/// @param shaders Whether to use splatting shader, or multi-pass fixed function splatting. Shader is usually
|
||||
/// faster so this is just here for compatibility.
|
||||
/// @param align The align of the terrain, see Alignment enum
|
||||
/// @param minBatchSize Minimum size of a terrain batch along one side (in cell units). Used for building the quad tree.
|
||||
/// @param maxBatchSize Maximum size of a terrain batch along one side (in cell units). Used when traversing the quad tree.
|
||||
World(Ogre::SceneManager* sceneMgr,
|
||||
Storage* storage, int visiblityFlags, bool distantLand, bool shaders, Alignment align, float minBatchSize, float maxBatchSize);
|
||||
~World();
|
||||
Storage* storage, int visiblityFlags, bool shaders, Alignment align);
|
||||
virtual ~World();
|
||||
|
||||
bool getDistantLandEnabled() { return mDistantLand; }
|
||||
bool getShadersEnabled() { return mShaders; }
|
||||
bool getShadowsEnabled() { return mShadows; }
|
||||
bool getSplitShadowsEnabled() { return mSplitShadows; }
|
||||
|
@ -54,138 +42,60 @@ namespace Terrain
|
|||
/// Update chunk LODs according to this camera position
|
||||
/// @note Calling this method might lead to composite textures being rendered, so it is best
|
||||
/// not to call it when render commands are still queued, since that would cause a flush.
|
||||
void update (const Ogre::Vector3& cameraPos);
|
||||
virtual void update (const Ogre::Vector3& cameraPos) = 0;
|
||||
|
||||
// This is only a hint and may be ignored by the implementation.
|
||||
virtual void loadCell(int x, int y) {}
|
||||
virtual void unloadCell(int x, int y) {}
|
||||
|
||||
/// Get the world bounding box of a chunk of terrain centered at \a center
|
||||
Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center);
|
||||
virtual Ogre::AxisAlignedBox getWorldBoundingBox (const Ogre::Vector2& center) = 0;
|
||||
|
||||
Ogre::SceneManager* getSceneManager() { return mSceneMgr; }
|
||||
|
||||
Ogre::SceneNode* getRootSceneNode() { return mRootSceneNode; }
|
||||
|
||||
Storage* getStorage() { return mStorage; }
|
||||
|
||||
/// Show or hide the whole terrain
|
||||
/// @note this setting will be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden
|
||||
void setVisible(bool visible);
|
||||
bool getVisible();
|
||||
/// @note this setting may be invalidated once you call Terrain::update, so do not call it while the terrain should be hidden
|
||||
virtual void setVisible(bool visible) = 0;
|
||||
virtual bool getVisible() = 0;
|
||||
|
||||
/// Recreate materials used by terrain chunks. This should be called whenever settings of
|
||||
/// the material factory are changed. (Relying on the factory to update those materials is not
|
||||
/// enough, since turning a feature on/off can change the number of texture units available for layer/blend
|
||||
/// textures, and to properly respond to this we may need to change the structure of the material, such as
|
||||
/// adding or removing passes. This can only be achieved by a full rebuild.)
|
||||
void applyMaterials(bool shadows, bool splitShadows);
|
||||
virtual void applyMaterials(bool shadows, bool splitShadows) = 0;
|
||||
|
||||
int getVisiblityFlags() { return mVisibilityFlags; }
|
||||
|
||||
int getMaxBatchSize() { return mMaxBatchSize; }
|
||||
|
||||
void enableSplattingShader(bool enabled);
|
||||
int getVisibilityFlags() { return mVisibilityFlags; }
|
||||
|
||||
Alignment getAlign() { return mAlign; }
|
||||
|
||||
/// Wait until all background loading is complete.
|
||||
void syncLoad();
|
||||
virtual void syncLoad() {}
|
||||
|
||||
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;
|
||||
protected:
|
||||
bool mShaders;
|
||||
bool mShadows;
|
||||
bool mSplitShadows;
|
||||
bool mVisible;
|
||||
Alignment mAlign;
|
||||
|
||||
QuadTreeNode* mRootNode;
|
||||
Ogre::SceneNode* mRootSceneNode;
|
||||
Storage* mStorage;
|
||||
|
||||
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* mCompositeMapSceneMgr;
|
||||
|
||||
/// Bounds in cell units
|
||||
float mMinX, mMaxX, mMinY, mMaxY;
|
||||
|
||||
/// Minimum size of a terrain batch along one side (in cell units)
|
||||
float mMinBatchSize;
|
||||
/// Maximum size of a terrain batch along one side (in cell units)
|
||||
float mMaxBatchSize;
|
||||
|
||||
void buildQuadTree(QuadTreeNode* node, std::vector<QuadTreeNode*>& leafs);
|
||||
|
||||
BufferCache mCache;
|
||||
|
||||
// Are layers for leaf nodes loaded? This is done once at startup (but in a background thread)
|
||||
bool mLayerLoadPending;
|
||||
|
||||
public:
|
||||
// ----INTERNAL----
|
||||
Ogre::SceneManager* getCompositeMapSceneManager() { return mCompositeMapSceneMgr; }
|
||||
BufferCache& getBufferCache() { return mCache; }
|
||||
|
||||
bool areLayersLoaded() { return !mLayerLoadPending; }
|
||||
|
||||
// Delete all quads
|
||||
void clearCompositeMapSceneManager();
|
||||
void renderCompositeMap (Ogre::TexturePtr target);
|
||||
|
||||
// Convert the given position from Z-up align, i.e. Align_XY to the wanted align set in mAlign
|
||||
void convertPosition (float& x, float& y, float& z);
|
||||
void convertPosition (Ogre::Vector3& pos);
|
||||
void convertBounds (Ogre::AxisAlignedBox& bounds);
|
||||
|
||||
// Adds a WorkQueue request to load a chunk for this node in the background.
|
||||
void queueLoad (QuadTreeNode* node);
|
||||
|
||||
private:
|
||||
Ogre::RenderTarget* mCompositeMapRenderTarget;
|
||||
Ogre::TexturePtr mCompositeMapRenderTexture;
|
||||
};
|
||||
|
||||
struct LoadRequestData
|
||||
{
|
||||
QuadTreeNode* mNode;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& o, const LoadRequestData& r)
|
||||
{ return o; }
|
||||
};
|
||||
|
||||
struct LoadResponseData
|
||||
{
|
||||
std::vector<float> mPositions;
|
||||
std::vector<float> mNormals;
|
||||
std::vector<Ogre::uint8> mColours;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& o, const LoadResponseData& r)
|
||||
{ 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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue