2009-05-15 07:04:50 +00:00
|
|
|
/**
|
2009-05-16 17:58:08 +00:00
|
|
|
* defines an area of Landscape
|
2009-05-15 07:04:50 +00:00
|
|
|
*
|
2009-05-16 17:58:08 +00:00
|
|
|
* A quad can either hold a mesh, or 4 other sub quads The functions
|
|
|
|
* split and unsplit either break the current quad into smaller quads,
|
|
|
|
* or alternatively remove the lower quads and create the terrain mesh
|
|
|
|
* on the the current (now the lowest) level
|
2009-05-15 07:04:50 +00:00
|
|
|
*/
|
2009-05-16 17:58:08 +00:00
|
|
|
/* Previously for MeshInterface:
|
|
|
|
* Interface between the quad and the terrain renderble classes, to the
|
|
|
|
* quad it looks like this rendereds a single mesh for the quad. This
|
|
|
|
* may not be the case.
|
|
|
|
*
|
|
|
|
* It also could allow several optimizations (e.g. multiple splits)
|
|
|
|
*/
|
2009-06-01 09:16:41 +00:00
|
|
|
|
2009-05-15 07:04:50 +00:00
|
|
|
class Quad
|
|
|
|
{
|
2009-05-16 17:58:08 +00:00
|
|
|
typedef std::list<TerrainMesh*> MeshList;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
Quad(int cellX=0, int cellY=0, Quad* parent = NULL)
|
|
|
|
: mCellX(cellX),
|
|
|
|
mCellY(cellY)
|
2009-05-15 07:04:50 +00:00
|
|
|
{
|
2009-06-01 09:16:41 +00:00
|
|
|
RTRACE("Quad");
|
|
|
|
|
2009-05-15 07:04:50 +00:00
|
|
|
memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN);
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
hasMesh = false;
|
|
|
|
hasChildren = false;
|
|
|
|
isStatic = false;
|
|
|
|
|
|
|
|
// Do we have a parent?
|
|
|
|
if(parent != NULL)
|
|
|
|
{
|
|
|
|
mLevel = parent->mLevel-1;
|
|
|
|
|
|
|
|
if(mLevel == 1)
|
2009-05-15 07:04:50 +00:00
|
|
|
{
|
2009-06-01 09:16:41 +00:00
|
|
|
// Create the terrain and leave it there.
|
2009-05-15 07:04:50 +00:00
|
|
|
buildTerrain();
|
2009-06-01 09:16:41 +00:00
|
|
|
isStatic = true;
|
2009-05-15 07:04:50 +00:00
|
|
|
}
|
2009-06-01 09:16:41 +00:00
|
|
|
|
|
|
|
// Coordinates relative to our parent
|
|
|
|
const int relX = cellX - parent->mCellX;
|
|
|
|
const int relY = cellY - parent->mCellY;
|
|
|
|
|
|
|
|
// The coordinates give the top left corner of the quad, or our
|
|
|
|
// relative coordinates within that should always be positive.
|
|
|
|
assert(relX >= 0);
|
|
|
|
assert(relY >= 0);
|
|
|
|
|
|
|
|
// Create a child scene node. The scene node position is given in
|
|
|
|
// world units, ie. CELL_WIDTH units per cell.
|
|
|
|
const Ogre::Vector3 pos(relX * CELL_WIDTH,
|
|
|
|
relY * CELL_WIDTH,
|
|
|
|
0);
|
|
|
|
mSceneNode = parent->mSceneNode->createChildSceneNode(pos);
|
|
|
|
|
|
|
|
// Get the archive data for this quad.
|
|
|
|
mInfo = g_archive.getQuad(mCellX,mCellY,mLevel);
|
2009-05-15 07:04:50 +00:00
|
|
|
}
|
|
|
|
else
|
2009-06-01 09:16:41 +00:00
|
|
|
{
|
|
|
|
// No parent, this is the top-most quad. Get all the info from
|
|
|
|
// the archive.
|
|
|
|
mInfo = g_archive.rootQuad;
|
|
|
|
|
|
|
|
mLevel = mInfo->level;
|
|
|
|
cellX = mCellX = mInfo->cellX;
|
|
|
|
cellY = mCellY = mInfo->cellY;
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
const Ogre::Vector3 pos(cellX * CELL_WIDTH,
|
|
|
|
cellY * CELL_WIDTH,
|
|
|
|
0);
|
|
|
|
mSceneNode = g_rootTerrainNode->
|
|
|
|
createChildSceneNode(pos);
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Split up
|
2009-05-15 07:04:50 +00:00
|
|
|
split();
|
2009-06-01 09:16:41 +00:00
|
|
|
|
|
|
|
// The root can never be unsplit
|
|
|
|
isStatic = true;
|
2009-05-15 07:04:50 +00:00
|
|
|
}
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
assert(mLevel >= 1);
|
|
|
|
assert(mSceneNode != NULL);
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Set up the bounding box. Use MW coordinates all the way
|
|
|
|
mBounds.setExtents(0,0,mInfo->minHeight,
|
|
|
|
mInfo->worldWidth,mInfo->worldWidth,
|
|
|
|
mInfo->maxHeight);
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Transform the box to world coordinates, so it can be compared
|
|
|
|
// with the camera later.
|
|
|
|
mBounds.transformAffine(mSceneNode->_getFullTransform());
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
const float radius = mInfo->boundingRadius;
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
mSplitDistance = radius * SPLIT_FACTOR;
|
|
|
|
mUnsplitDistance = radius * UNSPLIT_FACTOR;
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Square the distances
|
|
|
|
mSplitDistance *= mSplitDistance;
|
|
|
|
mUnsplitDistance *= mUnsplitDistance;
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Update the terrain. This will create the mesh or children if
|
|
|
|
// necessary.
|
|
|
|
update();
|
2009-05-15 07:04:50 +00:00
|
|
|
}
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
~Quad()
|
2009-05-15 07:04:50 +00:00
|
|
|
{
|
2009-06-01 09:16:41 +00:00
|
|
|
RTRACE("~Quad");
|
|
|
|
if(hasMesh)
|
|
|
|
destroyTerrain();
|
|
|
|
else if(hasChildren)
|
|
|
|
for (size_t i = 0; i < NUM_CHILDREN; i++)
|
|
|
|
delete mChildren[i];
|
|
|
|
|
|
|
|
mSceneNode->removeAndDestroyAllChildren();
|
|
|
|
mSceneMgr->destroySceneNode(mSceneNode);
|
2009-05-15 07:04:50 +00:00
|
|
|
}
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Remove the landscape for this quad, and create children.
|
|
|
|
void split()
|
2009-05-15 07:04:50 +00:00
|
|
|
{
|
2009-06-01 09:16:41 +00:00
|
|
|
RTRACE("split");
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Never split a static quad or a quad that already has children.
|
|
|
|
assert(!isStatic);
|
|
|
|
assert(!hasChildren);
|
|
|
|
assert(mLevel > 1);
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
if(hasMesh)
|
|
|
|
destroyTerrain();
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Find the cell width of our children
|
|
|
|
int cWidth = 1 << (mLevel-2);
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Create children
|
|
|
|
for ( size_t i = 0; i < NUM_CHILDREN; ++i )
|
|
|
|
{
|
|
|
|
if(!mInfo->hasChild[i])
|
|
|
|
continue;
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// The cell coordinates for this child quad
|
|
|
|
int x = (i%2)*cWidth + mCellX;
|
|
|
|
int y = (i/2)*cWidth + mCellY;
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
mChildren[i] = new Quad(x,y,this);
|
|
|
|
}
|
|
|
|
hasChildren = true;
|
2009-05-15 07:04:50 +00:00
|
|
|
}
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Removes children and rebuilds terrain
|
|
|
|
void unsplit()
|
2009-05-15 07:04:50 +00:00
|
|
|
{
|
2009-06-01 09:16:41 +00:00
|
|
|
RTRACE("unsplit");
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Never unsplit the root quad
|
|
|
|
assert(mLevel < g_archive.rootQuad->level);
|
|
|
|
// Never unsplit a static or quad that isn't split.
|
|
|
|
assert(!isStatic);
|
|
|
|
assert(hasChildren);
|
|
|
|
assert(!hasMesh);
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
for( size_t i = 0; i < NUM_CHILDREN; i++ )
|
|
|
|
{
|
|
|
|
delete mChildren[i];
|
|
|
|
mChildren[i] = NULL;
|
|
|
|
}
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
buildTerrain();
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
hasChildren = false;
|
2009-05-15 07:04:50 +00:00
|
|
|
}
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Determines whether to split or unsplit the quad, and immediately
|
|
|
|
// does it.
|
|
|
|
void update()
|
|
|
|
{
|
|
|
|
RTRACE("Quad::update");
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Static quads don't change
|
|
|
|
if(isStatic)
|
|
|
|
return;
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
assert(mUnsplitDistance > mSplitDistance);
|
|
|
|
|
|
|
|
// Get (squared) camera distance. TODO: shouldn't this just be a
|
|
|
|
// simple vector difference from the mesh center?
|
|
|
|
float camDist;
|
|
|
|
{
|
|
|
|
const Ogre::Vector3 cpos = mCamera->getDerivedPosition();
|
|
|
|
Ogre::Vector3 diff(0, 0, 0);
|
|
|
|
diff.makeFloor(cpos - mBounds.getMinimum() );
|
|
|
|
diff.makeCeil(cpos - mBounds.getMaximum() );
|
|
|
|
camDist = diff.squaredLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
// No children?
|
|
|
|
if(!hasChildren)
|
|
|
|
{
|
|
|
|
// If we're close, split now.
|
|
|
|
if(camDist < mSplitDistance)
|
|
|
|
split();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We're not close, and don't have any children. Should we
|
|
|
|
// built terrain?
|
|
|
|
if(!hasMesh)
|
|
|
|
buildTerrain();
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// If we get here, we either had children when we entered, or we
|
|
|
|
// just performed a split.
|
|
|
|
assert(!hasMesh);
|
|
|
|
assert(hasChildren);
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// If the camera is too far away, kill the children.
|
|
|
|
if( camDist > mUnsplitDistance )
|
2009-05-16 17:58:08 +00:00
|
|
|
{
|
|
|
|
unsplit();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// We have children and we're happy about it. Update them too.
|
|
|
|
for (size_t i = 0; i < NUM_CHILDREN; ++i)
|
2009-05-16 17:58:08 +00:00
|
|
|
{
|
2009-06-01 09:16:41 +00:00
|
|
|
Quad *q = mChildren[i];
|
|
|
|
if(q != NULL) q->update();
|
2009-05-16 17:58:08 +00:00
|
|
|
}
|
2009-06-01 09:16:41 +00:00
|
|
|
}
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Build the terrain for this quad
|
|
|
|
void buildTerrain()
|
|
|
|
{
|
|
|
|
RTRACE("buildTerrain");
|
|
|
|
assert(!hasMesh);
|
|
|
|
assert(!isStatic);
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Map the terrain data into memory.
|
|
|
|
g_archive.mapQuad(mInfo);
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Create one mesh for each segment in the quad. TerrainMesh takes
|
|
|
|
// care of the loading.
|
|
|
|
for(int i=0; i < mInfo->meshNum; i++)
|
|
|
|
mMeshList.push_back(new TerrainMesh(i, mSceneNode));
|
2009-05-16 17:58:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-06-01 09:16:41 +00:00
|
|
|
* @brief destroys the terrain.
|
2009-05-16 17:58:08 +00:00
|
|
|
*/
|
2009-06-01 09:16:41 +00:00
|
|
|
void destroyTerrain()
|
|
|
|
{
|
|
|
|
RTRACE("destroyTerrain");
|
|
|
|
assert(hasMesh);
|
|
|
|
|
2009-05-16 17:58:08 +00:00
|
|
|
for ( MeshList::iterator itr = mMeshList.begin();
|
2009-06-01 09:16:41 +00:00
|
|
|
itr != mMeshList.end(); ++itr )
|
|
|
|
delete *itr;
|
|
|
|
|
|
|
|
mMeshList.clear();
|
2009-05-16 17:58:08 +00:00
|
|
|
}
|
2009-05-15 07:04:50 +00:00
|
|
|
|
|
|
|
private:
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// List of meshes, if any
|
2009-05-16 17:58:08 +00:00
|
|
|
MeshList mMeshList;
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Scene node. All child quads are added to this.
|
|
|
|
SceneNode* mSceneNode;
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Bounding box, transformed to world coordinates. Used to calculate
|
|
|
|
// camera distance.
|
2009-05-16 17:58:08 +00:00
|
|
|
Ogre::AxisAlignedBox mBounds;
|
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
Ogre::Real mSplitDistance,mUnsplitDistance;
|
2009-05-16 17:58:08 +00:00
|
|
|
|
2009-05-15 07:04:50 +00:00
|
|
|
static const size_t NUM_CHILDREN = 4;
|
|
|
|
|
2009-05-16 17:58:08 +00:00
|
|
|
Quad* mChildren[NUM_CHILDREN]; ///optionaly the children. Should be
|
|
|
|
///0 if not exist
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
// Contains the 'level' of this node. Level 1 is the closest and
|
|
|
|
// most detailed level
|
|
|
|
int mLevel;
|
|
|
|
int mCellX, mCellY;
|
|
|
|
|
|
|
|
QuadInfo *mInfo;
|
2009-05-15 07:04:50 +00:00
|
|
|
|
2009-06-01 09:16:41 +00:00
|
|
|
bool hasMesh;
|
|
|
|
bool hasChildren;
|
|
|
|
bool isStatic; // Static quads are never split or unsplit
|
2009-05-15 07:04:50 +00:00
|
|
|
};
|