You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
7.4 KiB
D
287 lines
7.4 KiB
D
module terrain.quad;
|
|
|
|
import terrain.archive;
|
|
import terrain.bindings;
|
|
import std.stdio;
|
|
import monster.vm.dbg;
|
|
|
|
const int CELL_WIDTH = 8192;
|
|
const float SPLIT_FACTOR = 0.5;
|
|
const float UNSPLIT_FACTOR = 2.0;
|
|
|
|
class Quad
|
|
{
|
|
this(int cellX=0, int cellY=0, Quad parent = null)
|
|
{
|
|
scope auto _trc = new MTrace("Quad.this");
|
|
|
|
mCellX = cellX;
|
|
mCellY = cellY;
|
|
|
|
// Do we have a parent?
|
|
if(parent !is null)
|
|
{
|
|
mLevel = parent.mLevel-1;
|
|
|
|
// Coordinates relative to our parent
|
|
int relX = cellX - parent.mCellX;
|
|
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.
|
|
mNode = terr_createChildNode(relX*CELL_WIDTH,
|
|
relY*CELL_WIDTH,
|
|
parent.mNode);
|
|
|
|
// Get the archive data for this quad.
|
|
mInfo = g_archive.getQuad(mCellX,mCellY,mLevel);
|
|
|
|
// Set up the bounding box. Use MW coordinates all the
|
|
// way.
|
|
mBounds = terr_makeBounds(mInfo.minHeight,
|
|
mInfo.maxHeight,
|
|
mInfo.worldWidth,
|
|
mNode);
|
|
|
|
float radius = mInfo.boundingRadius;
|
|
|
|
mSplitDistance = radius * SPLIT_FACTOR;
|
|
mUnsplitDistance = radius * UNSPLIT_FACTOR;
|
|
|
|
// Square the distances
|
|
mSplitDistance *= mSplitDistance;
|
|
mUnsplitDistance *= mUnsplitDistance;
|
|
|
|
if(mLevel == 1)
|
|
{
|
|
// Create the terrain and leave it there.
|
|
buildTerrain();
|
|
isStatic = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No parent, this is the top-most quad. Get all the info from
|
|
// the archive.
|
|
mInfo = g_archive.rootQuad;
|
|
assert(mInfo);
|
|
|
|
mLevel = mInfo.level;
|
|
cellX = mCellX = mInfo.cellX;
|
|
cellY = mCellY = mInfo.cellY;
|
|
|
|
mNode = terr_createChildNode(cellX*CELL_WIDTH,
|
|
cellY*CELL_WIDTH,
|
|
null);
|
|
|
|
// Split up
|
|
split();
|
|
|
|
// The root can never be unsplit
|
|
isStatic = true;
|
|
}
|
|
|
|
assert(mLevel >= 1);
|
|
assert(mNode !is null);
|
|
|
|
// Update the terrain. This will create the mesh or children if
|
|
// necessary.
|
|
update();
|
|
}
|
|
|
|
~this()
|
|
{
|
|
scope auto _trc = new MTrace("Quad.~this");
|
|
|
|
// TODO: We might rewrite the code so that the quads are never
|
|
// actually destroyed, just 'inactivated' by hiding their scene
|
|
// node. We only call update on our children if we don't have a
|
|
// mesh ourselves.
|
|
if(hasMesh)
|
|
destroyTerrain();
|
|
else if(hasChildren)
|
|
for (size_t i = 0; i < 4; i++)
|
|
delete mChildren[i];
|
|
|
|
terr_destroyNode(mNode);
|
|
if(mBounds !is null)
|
|
terr_killBounds(mBounds);
|
|
}
|
|
|
|
// Remove the landscape for this quad, and create children.
|
|
void split()
|
|
{
|
|
scope auto _trc = new MTrace("split");
|
|
// Never split a static quad or a quad that already has children.
|
|
assert(!isStatic);
|
|
assert(!hasChildren);
|
|
assert(mLevel > 1);
|
|
|
|
if(hasMesh)
|
|
destroyTerrain();
|
|
|
|
// Find the cell width of our children
|
|
int cWidth = 1 << (mLevel-2);
|
|
|
|
// Create children
|
|
for ( size_t i = 0; i < 4; ++i )
|
|
{
|
|
if(!mInfo.hasChild[i])
|
|
continue;
|
|
|
|
// The cell coordinates for this child quad
|
|
int x = (i%2)*cWidth + mCellX;
|
|
int y = (i/2)*cWidth + mCellY;
|
|
|
|
mChildren[i] = new Quad(x,y,this);
|
|
}
|
|
hasChildren = true;
|
|
}
|
|
|
|
// Removes children and rebuilds terrain
|
|
void unsplit()
|
|
{
|
|
scope auto _trc = new MTrace("unsplit");
|
|
// 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);
|
|
|
|
for( size_t i = 0; i < 4; i++ )
|
|
{
|
|
delete mChildren[i];
|
|
mChildren[i] = null;
|
|
}
|
|
|
|
buildTerrain();
|
|
|
|
hasChildren = false;
|
|
}
|
|
|
|
// Determines whether to split or unsplit the quad, and immediately
|
|
// does it.
|
|
void update()
|
|
{
|
|
scope auto _trc = new MTrace("Quad.update()");
|
|
|
|
// Static quads don't change
|
|
if(!isStatic)
|
|
{
|
|
assert(mUnsplitDistance > mSplitDistance);
|
|
|
|
// Get (squared) camera distance. TODO: shouldn't this just
|
|
// be a simple vector difference from the mesh center?
|
|
assert(mBounds !is null);
|
|
float camDist = terr_getSqCamDist(mBounds);
|
|
|
|
// 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();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If we get here, we either had children when we entered,
|
|
// or we just performed a split.
|
|
assert(!hasMesh);
|
|
assert(hasChildren);
|
|
|
|
// If the camera is too far away, kill the children.
|
|
if(camDist > mUnsplitDistance)
|
|
{
|
|
unsplit();
|
|
return;
|
|
}
|
|
}
|
|
else if(!hasChildren)
|
|
return;
|
|
|
|
// We have children and we're happy about it. Update them too.
|
|
for(int i; i < 4; ++i)
|
|
{
|
|
Quad q = mChildren[i];
|
|
if(q !is null) q.update();
|
|
}
|
|
}
|
|
|
|
// Build the terrain for this quad
|
|
void buildTerrain()
|
|
{
|
|
scope auto _trc = new MTrace("buildTerrain");
|
|
|
|
assert(!hasMesh);
|
|
assert(!isStatic);
|
|
|
|
// Map the terrain data into memory.
|
|
assert(mInfo);
|
|
g_archive.mapQuad(mInfo);
|
|
|
|
// Create one mesh for each segment in the quad. TerrainMesh takes
|
|
// care of the loading.
|
|
meshList.length = mInfo.meshNum;
|
|
foreach(i, ref m; meshList)
|
|
{
|
|
MeshInfo *mi = g_archive.getMeshInfo(i);
|
|
m = terr_makeMesh(mNode, mi, mInfo.level, TEX_SCALE);
|
|
}
|
|
|
|
hasMesh = true;
|
|
}
|
|
|
|
void destroyTerrain()
|
|
{
|
|
scope auto _trc = new MTrace("destroyTerrain");
|
|
|
|
assert(hasMesh);
|
|
|
|
foreach(m; meshList)
|
|
terr_killMesh(m);
|
|
|
|
meshList[] = null;
|
|
hasMesh = false;
|
|
}
|
|
|
|
private:
|
|
|
|
// List of meshes, if any. The meshes are C++ objects.
|
|
MeshObj meshList[];
|
|
|
|
// Scene node. All child quads are added to this.
|
|
SceneNode mNode;
|
|
|
|
// Bounding box, transformed to world coordinates. Used to calculate
|
|
// camera distance.
|
|
Bounds mBounds;
|
|
|
|
float mSplitDistance,mUnsplitDistance;
|
|
|
|
Quad mChildren[4];
|
|
|
|
// Contains the 'level' of this node. Level 1 is the closest and
|
|
// most detailed level
|
|
int mLevel;
|
|
int mCellX, mCellY;
|
|
|
|
QuadInfo *mInfo;
|
|
|
|
bool hasMesh;
|
|
bool hasChildren;
|
|
bool isStatic; // Static quads are never split or unsplit
|
|
}
|