Esempio n. 1
0
void	quadsquare::CreateChild(int index, const quadcornerdata& cd)
// Creates a child square at the specified index.
{
    if (Child[index] == 0) {
	quadcornerdata	q;
	SetupCornerData(&q, cd, index);
		
	Child[index] = new quadsquare(&q);
    }
}
Esempio n. 2
0
quadsquare*	quadsquare::EnableDescendant(int count, int path[], const quadcornerdata& cd)
// This function enables the descendant node 'count' generations below
// us, located by following the list of child indices in path[].
// Creates the node if necessary, and returns a pointer to it.
{
    count--;
    int	ChildIndex = path[count];

    if ((EnabledFlags & (16 << ChildIndex)) == 0) {
	EnableChild(ChildIndex, cd);
    }
	
    if (count > 0) {
	quadcornerdata	q;
	SetupCornerData(&q, cd, ChildIndex);
	return Child[ChildIndex]->EnableDescendant(count, path, q);
    } else {
	return Child[ChildIndex];
    }
}
void quadsquare::RenderAux( const quadcornerdata &cd, CLIPSTATE vis )
{
//Does the work of rendering this square.  Uses the enabled vertices only.
//Recurses as necessary.
    unsigned int whole = 2<<cd.Level;
    SphereTransformRenderlevel++;
    //If this square is outside the frustum, then don't render it.
    if (vis != GFX_TOTALLY_VISIBLE) {
        Vector min, max;
        min.i = cd.xorg;
        min.j = MinY;
        min.k = cd.zorg;
        max.i = cd.xorg+whole;
        max.j = MaxY;
        max.k = cd.zorg+whole;
        vis   = nonlinear_trans->BoxInFrustum( min, max, quadsquare::camerapos );
        if (vis == GFX_NOT_VISIBLE) {
            SphereTransformRenderlevel--;
            //This square is completely outside the view frustum.
            return;
        }
    }
    int i;

    int flags = 0;
    int mask  = 1;
    quadcornerdata q;
    for (i = 0; i < 4; i++, mask <<= 1) {
        if ( EnabledFlags&(16<<i) ) {
            SetupCornerData( &q, cd, i );
            Child[i]->RenderAux( q, vis );
        } else {
            flags |= mask;
        }
    }
    SphereTransformRenderlevel--;
    if (flags == 0)
        return;
//Local macro to make the triangle logic shorter & hopefully clearer.
    //#define tri(aa,ta,bb,tb,cc,tc) (indices[ta].q.push_back (aa), indices[ta].q.push_back (bb), indices[ta].q.push_back (cc))
#define V0 (Vertex[0].vertindex)
#define T0 ( Vertex[0].GetTex() )
#define V1 (Vertex[1].vertindex)
#define T1 ( Vertex[1].GetTex() )
#define V2 (cd.Verts[0].vertindex)
#define T2 ( cd.Verts[0].GetTex() )
#define V3 (Vertex[2].vertindex)
#define T3 ( Vertex[2].GetTex() )
#define V4 (cd.Verts[1].vertindex)
#define T4 ( cd.Verts[1].GetTex() )
#define V5 (Vertex[3].vertindex)
#define T5 ( Vertex[3].GetTex() )
#define V6 (cd.Verts[2].vertindex)
#define T6 ( cd.Verts[2].GetTex() )
#define V7 (Vertex[4].vertindex)
#define T7 ( Vertex[4].GetTex() )
#define V8 (cd.Verts[3].vertindex)
#define T8 ( cd.Verts[3].GetTex() )
    //Make the list of triangles to draw.
    if ( (EnabledFlags&1) == 0 ) {tri( V0, T0, V8, T8, V2, T2 ); } else {
        if (flags&8) tri( V0, T0, V8, T8, V1, T1 );
        if (flags&1) tri( V0, T0, V1, T1, V2, T2 );
    }
    if ( (EnabledFlags&2) == 0 ) {tri( V0, T0, V2, T2, V4, T4 ); } else {
        if (flags&1) tri( V0, T0, V2, T2, V3, T3 );
        if (flags&2) tri( V0, T0, V3, T3, V4, T4 );
    }
    if ( (EnabledFlags&4) == 0 ) {tri( V0, T0, V4, T4, V6, T6 ); } else {
        if (flags&2) tri( V0, T0, V4, T4, V5, T5 );
        if (flags&4) tri( V0, T0, V5, T5, V6, T6 );
    }
    if ( (EnabledFlags&8) == 0 ) {tri( V0, T0, V6, T6, V8, T8 ); } else {
        if (flags&4) tri( V0, T0, V6, T6, V7, T7 );
        if (flags&8) tri( V0, T0, V7, T7, V8, T8 );
    }
#undef V1
#undef V2
#undef V3
#undef V4
#undef V5
#undef V6
#undef V7
#undef V8
#undef T1
#undef T2
#undef T3
#undef T4
#undef T5
#undef t6
#undef T7
#undef T8
}
Esempio n. 4
0
void	quadsquare::UpdateAux(const quadcornerdata& cd, const float ViewerLocation[3], float CenterError, clip_result_t vis )
// Does the actual work of updating enabled states and tree growing/shrinking.
{
    BlockUpdateCount++;	//xxxxx

    check_assertion( vis != NotVisible, "Invalid visibility value" );
    if ( vis != NoClip ) {
	vis = ClipSquare( cd );

	if ( vis == NotVisible ) {
	    return;
	}
    }
	
    // Make sure error values are current.
    if (Dirty) {
	RecomputeError(cd);
    }

    int	half = 1 << cd.Level;
    int	whole = half << 1;

    // See about enabling child verts.

    // East vert.
    if ( (EnabledFlags & 1) == 0 && 
	 VertexTest(cd.xorg + whole, Vertex[1].Y, cd.zorg + half, 
		    Error[0], ViewerLocation, cd.Level, East) == true ) 
    {
	EnableEdgeVertex(0, false, cd);	
    }

    // South vert.
    if ( (EnabledFlags & 8) == 0 && 
	 VertexTest(cd.xorg + half, Vertex[4].Y, cd.zorg + whole, 
		    Error[1], ViewerLocation, cd.Level, South) == true ) 
    {
	EnableEdgeVertex(3, false, cd);	
    }

    if (cd.Level > 0) {
	if ((EnabledFlags & 32) == 0) {
	    if (BoxTest(cd.xorg, cd.zorg, half, MinY, MaxY, Error[3], ViewerLocation) == true) EnableChild(1, cd);	// nw child.er
	}
	if ((EnabledFlags & 16) == 0) {
	    if (BoxTest(cd.xorg + half, cd.zorg, half, MinY, MaxY, Error[2], ViewerLocation) == true) EnableChild(0, cd);	// ne child.
	}
	if ((EnabledFlags & 64) == 0) {
	    if (BoxTest(cd.xorg, cd.zorg + half, half, MinY, MaxY, Error[4], ViewerLocation) == true) EnableChild(2, cd);	// sw child.
	}
	if ((EnabledFlags & 128) == 0) {
	    if (BoxTest(cd.xorg + half, cd.zorg + half, half, MinY, MaxY, Error[5], ViewerLocation) == true) EnableChild(3, cd);	// se child.
	}
		
	// Recurse into child quadrants as necessary.
	quadcornerdata	q;
		
	if (EnabledFlags & 32) {
	    SetupCornerData(&q, cd, 1);
	    Child[1]->UpdateAux(q, ViewerLocation, Error[3], vis);
	}
	if (EnabledFlags & 16) {
	    SetupCornerData(&q, cd, 0);
	    Child[0]->UpdateAux(q, ViewerLocation, Error[2], vis);
	}
	if (EnabledFlags & 64) {
	    SetupCornerData(&q, cd, 2);
	    Child[2]->UpdateAux(q, ViewerLocation, Error[4], vis);
	}
	if (EnabledFlags & 128) {
	    SetupCornerData(&q, cd, 3);
	    Child[3]->UpdateAux(q, ViewerLocation, Error[5], vis);
	}
    }
	
    // Test for disabling.  East, South, and center.
    if ( (EnabledFlags & 1) && 
	 SubEnabledCount[0] == 0 && 
	 VertexTest(cd.xorg + whole, Vertex[1].Y, cd.zorg + half, 
		    Error[0], ViewerLocation, cd.Level, East) == false) 
    {
	EnabledFlags &= ~1;
	quadsquare*	s = GetNeighbor(0, cd);
	if (s) s->EnabledFlags &= ~4;
    }

    if ( (EnabledFlags & 8) && 
	 SubEnabledCount[1] == 0 && 
	 VertexTest(cd.xorg + half, Vertex[4].Y, cd.zorg + whole, 
		    Error[1], ViewerLocation, cd.Level, South) == false) 
    {
	EnabledFlags &= ~8;
	quadsquare*	s = GetNeighbor(3, cd);
	if (s) s->EnabledFlags &= ~2;
    }

    if (EnabledFlags == 0 &&
	cd.Parent != NULL &&
	BoxTest(cd.xorg, cd.zorg, whole, MinY, MaxY, CenterError, 
		ViewerLocation) == false)
    {
	// Disable ourself.
	cd.Parent->Square->NotifyChildDisable(*cd.Parent, cd.ChildIndex);	// nb: possibly deletes 'this'.
    }
}
Esempio n. 5
0
void	quadsquare::StaticCullAux(const quadcornerdata& cd, float ThresholdDetail, int TargetLevel)
// Check this node and its descendents, and remove nodes which don't contain
// necessary detail.
{
    int	i, j;
    quadcornerdata	q;

    if (cd.Level > TargetLevel) {
	// Just recurse to child nodes.
	for (j = 0; j < 4; j++) {
	    if (j < 2) i = 1 - j;
	    else i = j;

	    if (Child[i]) {
		SetupCornerData(&q, cd, i);
		Child[i]->StaticCullAux(q, ThresholdDetail, TargetLevel);
	    }
	}
	return;
    }

    // We're at the target level.  Check this node to see if it's OK to delete it.

    // Check edge vertices to see if they're necessary.
    float	size = 2 << cd.Level;	// Edge length.
    if (Child[0] == NULL && Child[3] == NULL && Error[0] * ThresholdDetail < size) {
	quadsquare*	s = GetNeighbor(0, cd);
	if (s == NULL || (s->Child[1] == NULL && s->Child[2] == NULL)) {

	    // Force vertex height to the edge value.
	    float	y = (cd.Verts[0].Y + cd.Verts[3].Y) * 0.5;
	    Vertex[1].Y = y;
	    Error[0] = 0;
			
	    // Force alias vertex to match.
	    if (s) s->Vertex[3].Y = y;
			
	    Dirty = true;
	}
    }

    if (Child[2] == NULL && Child[3] == NULL && Error[1] * ThresholdDetail < size) {
	quadsquare*	s = GetNeighbor(3, cd);
	if (s == NULL || (s->Child[0] == NULL && s->Child[1] == NULL)) {
	    float	y = (cd.Verts[2].Y + cd.Verts[3].Y) * 0.5;
	    Vertex[4].Y = y;
	    Error[1] = 0;
			
	    if (s) s->Vertex[2].Y = y;
			
	    Dirty = true;
	}
    }

    // See if we have child nodes.
    bool	StaticChildren = false;
    for (i = 0; i < 4; i++) {
	if (Child[i]) {
	    StaticChildren = true;
	    if (Child[i]->Dirty) Dirty = true;
	}
    }

    // If we have no children and no necessary edges, then see if we can delete ourself.
    if (StaticChildren == false && cd.Parent != NULL) {
	bool	NecessaryEdges = false;
	for (i = 0; i < 4; i++) {
	    // See if vertex deviates from edge between corners.
	    float	diff = fabs(Vertex[i+1].Y - (cd.Verts[i].Y + cd.Verts[(i+3)&3].Y) * 0.5);
	    if (diff > 0.00001) {
		NecessaryEdges = true;
	    }
	}

	if (!NecessaryEdges) {
	    size *= 1.414213562;	// sqrt(2), because diagonal is longer than side.
	    if (cd.Parent->Square->Error[2 + cd.ChildIndex] * ThresholdDetail < size) {
		delete cd.Parent->Square->Child[cd.ChildIndex];	// Delete this.
		cd.Parent->Square->Child[cd.ChildIndex] = 0;	// Clear the pointer.
	    }
	}
    }
}
Esempio n. 6
0
float	quadsquare::RecomputeError(const quadcornerdata& cd)
// Recomputes the error values for this tree.  Returns the
// max error.
// Also updates MinY & MaxY.
{
    int	i;
    int j;
    int t;
    int	half = 1 << cd.Level;
    int	whole = half << 1;
    float terrain_error;
	
    // Measure error of center and edge vertices.
    float	maxerror = 0;

    // Compute error of center vert.
    float	e;
    if (cd.ChildIndex & 1) {
	e = fabs(Vertex[0].Y - (cd.Verts[1].Y + cd.Verts[3].Y) * 0.5);
    } else {
	e = fabs(Vertex[0].Y - (cd.Verts[0].Y + cd.Verts[2].Y) * 0.5);
    }
    if (e > maxerror) maxerror = e;

    // Initial min/max.
    MaxY = Vertex[0].Y;
    MinY = Vertex[0].Y;

    // Check min/max of corners.
    for (i = 0; i < 4; i++) {
	float	y = cd.Verts[i].Y;
	if (y < MinY) MinY = y;
	if (y > MaxY) MaxY = y;
    }
	
    // Edge verts.
    e = fabs(Vertex[1].Y - (cd.Verts[0].Y + cd.Verts[3].Y) * 0.5);
    if (e > maxerror) maxerror = e;
    Error[0] = e;
	
    e = fabs(Vertex[4].Y - (cd.Verts[2].Y + cd.Verts[3].Y) * 0.5);
    if (e > maxerror) maxerror = e;
    Error[1] = e;

    // Terrain edge checks
    if ( cd.Level == 0 && cd.xorg <= RowSize-1 && cd.zorg <= NumRows-1 ) {

	// Check South vertex
	int x = cd.xorg + half;
	int z = cd.zorg + whole;
	int idx = x  + z * RowSize;
	bool different_terrains = false;

	terrain_error = 0.f;

	check_assertion( x >= 0, "x coordinate is negative" );
	check_assertion( z >= 0, "z coordinate is negative" );

        if ( x < RowSize && z < NumRows ) {

	    if ( x < RowSize - 1 ) {
		if ( Terrain[idx] != Terrain[idx+1] ) {
		    different_terrains = true;
		}
	    }
	    if ( z >= 1 ) {
		idx -= RowSize;
		if ( Terrain[idx] != Terrain[idx+1] ) {
		    different_terrains = true;
		}
	    }

	    if ( different_terrains ) {
		ForceSouthVert = true;
		terrain_error = TERRAIN_ERROR_SCALE * whole * whole;
	    } else {
		ForceSouthVert = false;
	    }

	    if ( terrain_error > Error[0] ) {
		Error[0] = terrain_error;
	    }
	    if ( Error[0] > maxerror ) {
		maxerror = Error[0];
	    }
	}

	// Check East vertex
	x = cd.xorg + whole;
	z = cd.zorg + half;
	idx = x  + z * RowSize;
	terrain_error = 0;
	different_terrains = false;

        if ( x < RowSize && z < NumRows ) {

	    if ( z >= 1 ) {
		if ( Terrain[idx] != Terrain[idx-RowSize] ) {
		    different_terrains = true;
		}
	    }
	    if ( z >= 1 && x < RowSize - 1 ) {
		idx += 1;
		if ( Terrain[idx] != Terrain[idx-RowSize] ) {
		    different_terrains = true;
		}
	    }

	    if ( different_terrains ) {
		ForceEastVert = true;
		terrain_error = TERRAIN_ERROR_SCALE * whole * whole;
	    } else {
		ForceEastVert = false;
	    }

	    if ( terrain_error > Error[1] ) {
		Error[1] = terrain_error;
	    }
	    if ( Error[1] > maxerror ) {
		maxerror = Error[1];
	    }
        }
    }

    // Min/max of edge verts.
    for (i = 0; i < 4; i++) {
	float	y = Vertex[1 + i].Y;
	if (y < MinY) MinY = y;
	if (y > MaxY) MaxY = y;
    }
	
    // Check child squares.
    for (i = 0; i < 4; i++) {
	quadcornerdata	q;
	if (Child[i]) {
	    SetupCornerData(&q, cd, i);
	    Error[i+2] = Child[i]->RecomputeError(q);

	    if (Child[i]->MinY < MinY) MinY = Child[i]->MinY;
	    if (Child[i]->MaxY > MaxY) MaxY = Child[i]->MaxY;
	} else {
	    // Compute difference between bilinear average at child center, and diagonal edge approximation.
	    Error[i+2] = fabs((Vertex[0].Y + cd.Verts[i].Y) - (Vertex[i+1].Y + Vertex[((i+1)&3) + 1].Y)) * 0.25;
	}
	if (Error[i+2] > maxerror) maxerror = Error[i+2];
    }

    //
    // Compute terrain_error
    //
    int terrain;

    int *terrain_count = new int[(int)NumTerrains];

    for (t=0; t<NumTerrains; t++) {
	terrain_count[t] = 0;
    }

    for (i=cd.xorg; i<=cd.xorg+whole; i++) {
	for (j=cd.zorg; j<=cd.zorg+whole; j++) {

	    if ( i < 0 || i >= RowSize ||
		 j < 0 || j >= NumRows ) 
	    {
		continue;
	    }

	    terrain = (int) Terrain[ i + RowSize*j ];
	    check_assertion( terrain >= 0 && 
			     terrain < NumTerrains,
			     "Invalid terrain type" );
	    terrain_count[ terrain ] += 1;
	}
    }

    int max_count = 0;
    int max_type = 0;
    int total = 0;
    for (t=0; t<NumTerrains; t++) {
	if ( terrain_count[t] > max_count ) {
	    max_count = terrain_count[t];
	    max_type = t;
	}
	total += terrain_count[t];
    }

    delete [] terrain_count;

    /* Calculate a terrain error that varies between 0 and 1 */
    if ( total > 0 ) {
	terrain_error = (1.0 - max_count / total);  
	if ( NumTerrains > 1 ) {
	    terrain_error *= NumTerrains / ( NumTerrains - 1.0 );
	}
    } else {
	terrain_error = 0;
    }

    /* and scale it by the square area */
    terrain_error *= whole * whole;

    /* and finally scale it so that it's comparable to height error */
    terrain_error *= TERRAIN_ERROR_SCALE;

    if ( terrain_error > maxerror ) {
	maxerror = terrain_error;
    }

    if ( terrain_error > Error[0] ) {
	Error[0] = terrain_error;
    }
    if ( terrain_error > Error[1] ) {
	Error[1] = terrain_error;
    }


    // The error  and MinY/MaxY values for this node and descendants are correct now.
    Dirty = false;
	
    return maxerror;
}
Esempio n. 7
0
float	quadsquare::GetHeight(const quadcornerdata& cd, float x, float z)
// Returns the height of the heightfield at the specified x,z coordinates.
{
    int	half = 1 << cd.Level;

    float	lx = (x - cd.xorg) / float(half);
    float	lz = (z - cd.zorg) / float(half);

    int	ix = (int) floor(lx);
    int	iz = (int) floor(lz);

    // Clamp.
    if (ix < 0) ix = 0;
    if (ix > 1) ix = 1;
    if (iz < 0) iz = 0;
    if (iz > 1) iz = 1;

    int	index = ix ^ (iz ^ 1) + (iz << 1);
    if (Child[index] && Child[index]->Static) {
	// Pass the query down to the child which contains it.
	quadcornerdata	q;
	SetupCornerData(&q, cd, index);
	return Child[index]->GetHeight(q, x, z);
    }

    // Bilinear interpolation.
    lx -= ix;
    if (lx < 0) lx = 0;
    if (lx > 1) lx = 1;
	
    lz -= iz;
    if (lx < 0) lz = 0;
    if (lz > 1) lz = 1;

    float	s00, s01, s10, s11;
    switch (index) {
    default:
    case 0:
	s00 = Vertex[2].Y;
	s01 = cd.Verts[0].Y;
	s10 = Vertex[0].Y;
	s11 = Vertex[1].Y;
	break;
    case 1:
	s00 = cd.Verts[1].Y;
	s01 = Vertex[2].Y;
	s10 = Vertex[3].Y;
	s11 = Vertex[0].Y;
	break;
    case 2:
	s00 = Vertex[3].Y;
	s01 = Vertex[0].Y;
	s10 = cd.Verts[2].Y;
	s11 = Vertex[4].Y;
	break;
    case 3:
	s00 = Vertex[0].Y;
	s01 = Vertex[1].Y;
	s10 = Vertex[4].Y;
	s11 = cd.Verts[3].Y;
	break;
    }

    return (s00 * (1-lx) + s01 * lx) * (1 - lz) + (s10 * (1-lx) + s11 * lx) * lz;
}
Esempio n. 8
0
void	quadsquare::AddHeightMap(const quadcornerdata& cd, const HeightMapInfo& hm)
// Sets the height of all samples within the specified rectangular
// region using the given array of floats.  Extends the tree to the
// level of detail defined by (1 << hm.Scale) as necessary.
{
    RowSize = hm.RowWidth;
    NumRows = hm.ZSize;

    if ( cd.Parent == NULL ) {
	if ( VertexArrayIndices != NULL ) {
	    delete VertexArrayIndices;
	}

	/* Maximum number of triangles is 2 * RowSize * NumRows 
	   This uses up a lot of space but it is a *big* performance gain.
	*/
	VertexArrayIndices = new GLuint[6 * RowSize * NumRows];
    }

    // If block is outside rectangle, then don't bother.
    int	BlockSize = 2 << cd.Level;
    if (cd.xorg > hm.XOrigin + ((hm.XSize + 2) << hm.Scale) ||
	cd.xorg + BlockSize < hm.XOrigin - (1 << hm.Scale) ||
	cd.zorg > hm.ZOrigin + ((hm.ZSize + 2) << hm.Scale) ||
	cd.zorg + BlockSize < hm.ZOrigin - (1 << hm.Scale))
    {
	// This square does not touch the given height array area; no need to modify this square or descendants.
	return;
    }

    if (cd.Parent && cd.Parent->Square) {
	cd.Parent->Square->EnableChild(cd.ChildIndex, *cd.Parent);	// causes parent edge verts to be enabled, possibly causing neighbor blocks to be created.
    }
	
    int	i;
	
    int	half = 1 << cd.Level;

    // Create and update child nodes.
    for (i = 0; i < 4; i++) {
	quadcornerdata	q;
	SetupCornerData(&q, cd, i);
				
	if (Child[i] == NULL && cd.Level > hm.Scale) {
	    // Create child node w/ current (unmodified) values for corner verts.
	    Child[i] = new quadsquare(&q);
	}
		
	// Recurse.
	if (Child[i]) {
	    Child[i]->AddHeightMap(q, hm);
	}
    }
	
    // Deviate vertex heights based on data sampled from heightmap.
    float	s[5];
    s[0] = hm.Sample(cd.xorg + half, cd.zorg + half);
    s[1] = hm.Sample(cd.xorg + half*2, cd.zorg + half);
    s[2] = hm.Sample(cd.xorg + half, cd.zorg);
    s[3] = hm.Sample(cd.xorg, cd.zorg + half);
    s[4] = hm.Sample(cd.xorg + half, cd.zorg + half*2);

    // Modify the vertex heights if necessary, and set the dirty
    // flag if any modifications occur, so that we know we need to
    // recompute error data later.
    for (i = 0; i < 5; i++) {
	if (s[i] != 0) {
	    Dirty = true;
	    Vertex[i].Y += s[i];
	}
    }

    if (!Dirty) {
	// Check to see if any child nodes are dirty, and set the dirty flag if so.
	for (i = 0; i < 4; i++) {
	    if (Child[i] && Child[i]->Dirty) {
		Dirty = true;
		break;
	    }
	}
    }

    if (Dirty) SetStatic(cd);
}
Esempio n. 9
0
void	quadsquare::RenderAux(const quadcornerdata& cd, clip_result_t vis,
			      int terrain)
// Does the work of rendering this square.  Uses the enabled vertices only.
// Recurses as necessary.
{
    int	half = 1 << cd.Level;
    int	whole = 2 << cd.Level;
	

    // If this square is outside the frustum, then don't render it.
    if (vis != NoClip) {

	vis = ClipSquare( cd );

	if (vis == NotVisible ) {
	    // This square is completely outside the view frustum.
	    return;
	}
	// else vis is either NoClip or SomeClip.  If it's NoClip, then child
	// squares won't have to bother with the frustum check.
    }
	
    int	i;

    int	flags = 0;
    int	mask = 1;
    quadcornerdata	q;
    for (i = 0; i < 4; i++, mask <<= 1) {
	if (EnabledFlags & (16 << i)) {
	    SetupCornerData(&q, cd, i);
	    Child[i]->RenderAux(q, vis, terrain);
	} else {
	    flags |= mask;
	}
    }

    if (flags == 0) return;

    // Init vertex data.  Here's a diagram of what's going on.
    // Yes, this diagram is f****d, but that's because things are mirrored 
    // in the z axis for us (z coords are actually -z coords).
    //
    // (top of course) 
    //        N                
    //    +---+---+ (xorg, zorg)
    //   2|  3   4|        
    //    |(1) (2)|
    // E  +   +   +  W
    //   1|  0   5|
    //    |(8) (4)|
    //    +---+---+
    //   8   7   6
    //        S
    // (bottom of course)
    //
    // Values in parens are bitmask values for the corresponding child squares
    //

    InitVert(0, cd.xorg + half, cd.zorg + half);
    InitVert(1, cd.xorg + whole, cd.zorg + half);
    InitVert(2, cd.xorg + whole, cd.zorg);
    InitVert(3, cd.xorg + half, cd.zorg);
    InitVert(4, cd.xorg, cd.zorg);
    InitVert(5, cd.xorg, cd.zorg + half);
    InitVert(6, cd.xorg, cd.zorg + whole);
    InitVert(7, cd.xorg + half, cd.zorg + whole);
    InitVert(8, cd.xorg + whole, cd.zorg + whole);


    // Make the list of triangles to draw.
#define make_tri_list(tri_func) \
    if ((EnabledFlags & 1) == 0 ) { \
	tri_func(0, 2, 8, terrain); \
    } else { \
	if (flags & 8) tri_func(0, 1, 8, terrain); \
	if (flags & 1) tri_func(0, 2, 1, terrain); \
    } \
    if ((EnabledFlags & 2) == 0 ) {  \
	tri_func(0, 4, 2, terrain);  \
    } else { \
	if (flags & 1) tri_func(0, 3, 2, terrain); \
	if (flags & 2) tri_func(0, 4, 3, terrain); \
    } \
    if ((EnabledFlags & 4) == 0 ) { \
	tri_func(0, 6, 4, terrain); \
    } else { \
	if (flags & 2) tri_func(0, 5, 4, terrain); \
	if (flags & 4) tri_func(0, 6, 5, terrain); \
    } \
    if ((EnabledFlags & 8) == 0 ) { \
	tri_func(0, 8, 6, terrain); \
    } else { \
	if (flags & 4) tri_func(0, 7, 6, terrain); \
	if (flags & 8) tri_func(0, 8, 7, terrain); \
    }

    if ( terrain == -1 ) {
	make_tri_list(MakeSpecialTri);
    } else if ( getparam_terrain_blending() ) {
	make_tri_list(MakeTri);
    } else {
	make_tri_list(MakeNoBlendTri);
    }
}