aboutsummaryrefslogtreecommitdiffstats
path: root/q3map/terrain.c
diff options
context:
space:
mode:
Diffstat (limited to 'q3map/terrain.c')
-rwxr-xr-xq3map/terrain.c2468
1 files changed, 1234 insertions, 1234 deletions
diff --git a/q3map/terrain.c b/q3map/terrain.c
index 9446737..129ac08 100755
--- a/q3map/terrain.c
+++ b/q3map/terrain.c
@@ -19,1237 +19,1237 @@ along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
-#include "qbsp.h"
-#include <assert.h>
-
-#define SURF_WIDTH 2048
-#define SURF_HEIGHT 2048
-
-#define GROW_VERTS 512
-#define GROW_INDICES 512
-#define GROW_SURFACES 128
-
-#define VectorSet(v, x, y, z) v[0] = x;v[1] = y;v[2] = z;
-
-void QuakeTextureVecs( plane_t *plane, vec_t shift[2], vec_t rotate, vec_t scale[2], vec_t mappingVecs[2][4] );
-
-typedef struct {
- shaderInfo_t *shader;
- int x, y;
-
- int maxVerts;
- int numVerts;
- drawVert_t *verts;
-
- int maxIndexes;
- int numIndexes;
- int *indexes;
-} terrainSurf_t;
-
-static terrainSurf_t *surfaces = NULL;
-static terrainSurf_t *lastSurface = NULL;
-static int numsurfaces = 0;
-static int maxsurfaces = 0;
-
-/*
-================
-ShaderForLayer
-================
-*/
-shaderInfo_t *ShaderForLayer( int minlayer, int maxlayer, const char *shadername ) {
- char shader[ MAX_QPATH ];
-
- if ( minlayer == maxlayer ) {
- sprintf( shader, "textures/%s_%d", shadername, maxlayer );
- } else {
- sprintf( shader, "textures/%s_%dto%d", shadername, minlayer, maxlayer );
- }
-
- return ShaderInfoForShader( shader );
-}
-
-/*
-================
-CompareVert
-================
-*/
-qboolean CompareVert( drawVert_t *v1, drawVert_t *v2, qboolean checkst ) {
- int i;
-
- for( i = 0; i < 3; i++ ) {
- if ( floor( v1->xyz[ i ] + 0.1 ) != floor( v2->xyz[ i ] + 0.1 ) ) {
- return qfalse;
- }
- if ( checkst && ( ( v1->st[ 0 ] != v2->st[ 0 ] ) || ( v1->st[ 1 ] != v2->st[ 1 ] ) ) ) {
- return qfalse;
- }
- }
-
- return qtrue;
-}
-
-/*
-================
-LoadAlphaMap
-================
-*/
-byte *LoadAlphaMap( int *num_layers, int *alphawidth, int *alphaheight ) {
- int *alphamap32;
- byte *alphamap;
- const char *alphamapname;
- char ext[ 128 ];
- int width;
- int height;
- int layers;
- int size;
- int i;
-
- assert( alphawidth );
- assert( alphaheight );
- assert( num_layers );
-
- layers = atoi( ValueForKey( mapent, "layers" ) );
- if ( layers < 1 ) {
- Error ("SetTerrainTextures: invalid value for 'layers' (%d)", layers );
- }
-
- alphamapname = ValueForKey( mapent, "alphamap" );
- if ( !alphamapname[ 0 ] ) {
- Error ("LoadAlphaMap: No alphamap specified on terrain" );
- }
-
- ExtractFileExtension( alphamapname, ext);
- if ( !Q_stricmp( ext, "tga" ) ) {
- Load32BitImage( ExpandGamePath( alphamapname ), &alphamap32, &width, &height );
-
- size = width * height;
- alphamap = malloc( size );
- for( i = 0; i < size; i++ ) {
- alphamap[ i ] = ( ( alphamap32[ i ] & 0xff ) * layers ) / 256;
- if ( alphamap[ i ] >= layers ) {
- alphamap[ i ] = layers - 1;
- }
- }
- } else {
- Load256Image( ExpandGamePath( alphamapname ), &alphamap, NULL, &width, &height );
- size = width * height;
- for( i = 0; i < size; i++ ) {
- if ( alphamap[ i ] >= layers ) {
- alphamap[ i ] = layers - 1;
- }
- }
- }
-
- if ( ( width < 2 ) || ( height < 2 ) ) {
- Error ("LoadAlphaMap: alphamap width/height must be at least 2x2." );
- }
-
- *num_layers = layers;
- *alphawidth = width;
- *alphaheight = height;
-
- return alphamap;
-}
-
-/*
-================
-CalcTerrainSize
-================
-*/
-void CalcTerrainSize( vec3_t mins, vec3_t maxs, vec3_t size ) {
- bspbrush_t *brush;
- int i;
- const char *key;
-
- // calculate the size of the terrain
- ClearBounds( mins, maxs );
- for( brush = mapent->brushes; brush != NULL; brush = brush->next ) {
- AddPointToBounds( brush->mins, mins, maxs );
- AddPointToBounds( brush->maxs, mins, maxs );
- }
-
- key = ValueForKey( mapent, "min" );
- if ( key[ 0 ] ) {
- GetVectorForKey( mapent, "min", mins );
- }
-
- key = ValueForKey( mapent, "max" );
- if ( key[ 0 ] ) {
- GetVectorForKey( mapent, "max", maxs );
- }
-
- for( i = 0; i < 3; i++ ) {
- mins[ i ] = floor( mins[ i ] + 0.1 );
- maxs[ i ] = floor( maxs[ i ] + 0.1 );
- }
-
- VectorSubtract( maxs, mins, size );
-
- if ( ( size[ 0 ] <= 0 ) || ( size[ 1 ] <= 0 ) ) {
- Error ("CalcTerrainSize: Invalid terrain size: %fx%f", size[ 0 ], size[ 1 ] );
- }
-}
-
-/*
-==================
-IsTriangleDegenerate
-
-Returns qtrue if all three points are collinear or backwards
-===================
-*/
-#define COLINEAR_AREA 10
-static qboolean IsTriangleDegenerate( drawVert_t *points, int a, int b, int c ) {
- vec3_t v1, v2, v3;
- float d;
-
- VectorSubtract( points[b].xyz, points[a].xyz, v1 );
- VectorSubtract( points[c].xyz, points[a].xyz, v2 );
- CrossProduct( v1, v2, v3 );
- d = VectorLength( v3 );
-
- // assume all very small or backwards triangles will cause problems
- if ( d < COLINEAR_AREA ) {
- return qtrue;
- }
-
- return qfalse;
-}
-
-/*
-===============
-SideAsTriFan
-
-The surface can't be represented as a single tristrip without
-leaving a degenerate triangle (and therefore a crack), so add
-a point in the middle and create (points-1) triangles in fan order
-===============
-*/
-static void SideAsTriFan( terrainSurf_t *surf, int *index, int num ) {
- int i;
- int colorSum[4];
- drawVert_t *mid, *v;
-
- // make sure we have enough space for a new vert
- if ( surf->numVerts >= surf->maxVerts ) {
- surf->maxVerts += GROW_VERTS;
- surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
- }
-
- // create a new point in the center of the face
- mid = &surf->verts[ surf->numVerts ];
- surf->numVerts++;
-
- colorSum[0] = colorSum[1] = colorSum[2] = colorSum[3] = 0;
-
- for (i = 0 ; i < num; i++ ) {
- v = &surf->verts[ index[ i ] ];
- VectorAdd( mid->xyz, v->xyz, mid->xyz );
- mid->st[0] += v->st[0];
- mid->st[1] += v->st[1];
- mid->lightmap[0] += v->lightmap[0];
- mid->lightmap[1] += v->lightmap[1];
-
- colorSum[0] += v->color[0];
- colorSum[1] += v->color[1];
- colorSum[2] += v->color[2];
- colorSum[3] += v->color[3];
- }
-
- mid->xyz[0] /= num;
- mid->xyz[1] /= num;
- mid->xyz[2] /= num;
-
- mid->st[0] /= num;
- mid->st[1] /= num;
-
- mid->lightmap[0] /= num;
- mid->lightmap[1] /= num;
-
- mid->color[0] = colorSum[0] / num;
- mid->color[1] = colorSum[1] / num;
- mid->color[2] = colorSum[2] / num;
- mid->color[3] = colorSum[3] / num;
-
- // fill in indices in trifan order
- if ( surf->numIndexes + num * 3 > surf->maxIndexes ) {
- surf->maxIndexes = surf->numIndexes + num * 3;
- surf->indexes = realloc( surf->indexes, surf->maxIndexes * sizeof( *surf->indexes ) );
- }
-
-
- for ( i = 0 ; i < num; i++ ) {
- surf->indexes[ surf->numIndexes++ ] = surf->numVerts - 1;
- surf->indexes[ surf->numIndexes++ ] = index[ i ];
- surf->indexes[ surf->numIndexes++ ] = index[ (i+1) % ( surf->numVerts - 1 ) ];
- }
-}
-/*
-================
-SideAsTristrip
-
-Try to create indices that make (points-2) triangles in tristrip order
-================
-*/
-#define MAX_INDICES 1024
-static void SideAsTristrip( terrainSurf_t *surf, int *index, int num ) {
- int i;
- int rotate;
- int numIndices;
- int ni;
- int a, b, c;
- int indices[ MAX_INDICES ];
-
- // determine the triangle strip order
- numIndices = ( num - 2 ) * 3;
- if ( numIndices > MAX_INDICES ) {
- Error( "MAX_INDICES exceeded for surface" );
- }
-
- // try all possible orderings of the points looking
- // for a strip order that isn't degenerate
- for ( rotate = 0 ; rotate < num; rotate++ ) {
- for ( ni = 0, i = 0 ; i < num - 2 - i ; i++ ) {
- a = index[ ( num - 1 - i + rotate ) % num ];
- b = index[ ( i + rotate ) % num ];
- c = index[ ( num - 2 - i + rotate ) % num ];
-
- if ( IsTriangleDegenerate( surf->verts, a, b, c ) ) {
- break;
- }
- indices[ni++] = a;
- indices[ni++] = b;
- indices[ni++] = c;
-
- if ( i + 1 != num - 1 - i ) {
- a = index[ ( num - 2 - i + rotate ) % num ];
- b = index[ ( i + rotate ) % num ];
- c = index[ ( i + 1 + rotate ) % num ];
-
- if ( IsTriangleDegenerate( surf->verts, a, b, c ) ) {
- break;
- }
- indices[ni++] = a;
- indices[ni++] = b;
- indices[ni++] = c;
- }
- }
- if ( ni == numIndices ) {
- break; // got it done without degenerate triangles
- }
- }
-
- // if any triangle in the strip is degenerate,
- // render from a centered fan point instead
- if ( ni < numIndices ) {
- SideAsTriFan( surf, index, num );
- return;
- }
-
- // a normal tristrip
- if ( surf->numIndexes + ni > surf->maxIndexes ) {
- surf->maxIndexes = surf->numIndexes + ni;
- surf->indexes = realloc( surf->indexes, surf->maxIndexes * sizeof( *surf->indexes ) );
- }
-
- memcpy( surf->indexes + surf->numIndexes, indices, ni * sizeof( *surf->indexes ) );
- surf->numIndexes += ni;
-}
-
-/*
-================
-CreateTerrainSurface
-================
-*/
-void CreateTerrainSurface( terrainSurf_t *surf, shaderInfo_t *shader ) {
- int i, j, k;
- drawVert_t *out;
- drawVert_t *in;
- mapDrawSurface_t *newsurf;
-
- newsurf = AllocDrawSurf();
-
- newsurf->miscModel = qtrue;
- newsurf->shaderInfo = shader;
- newsurf->lightmapNum = -1;
- newsurf->fogNum = -1;
- newsurf->numIndexes = surf->numIndexes;
- newsurf->numVerts = surf->numVerts;
-
- // copy the indices
- newsurf->indexes = malloc( surf->numIndexes * sizeof( *newsurf->indexes ) );
- memcpy( newsurf->indexes, surf->indexes, surf->numIndexes * sizeof( *newsurf->indexes ) );
-
- // allocate the vertices
- newsurf->verts = malloc( surf->numVerts * sizeof( *newsurf->verts ) );
- memset( newsurf->verts, 0, surf->numVerts * sizeof( *newsurf->verts ) );
-
- // calculate the surface verts
- out = newsurf->verts;
- for( i = 0; i < newsurf->numVerts; i++, out++ ) {
- VectorCopy( surf->verts[ i ].xyz, out->xyz );
-
- // set the texture coordinates
- out->st[ 0 ] = surf->verts[ i ].st[ 0 ];
- out->st[ 1 ] = surf->verts[ i ].st[ 1 ];
-
- // the colors will be set by the lighting pass
- out->color[0] = 255;
- out->color[1] = 255;
- out->color[2] = 255;
- out->color[3] = surf->verts[ i ].color[ 3 ];
-
- // calculate the vertex normal
- VectorClear( out->normal );
- for( j = 0; j < numsurfaces; j++ ) {
- in = surfaces[ j ].verts;
- for( k = 0; k < surfaces[ j ].numVerts; k++, in++ ) {
- if ( CompareVert( out, in, qfalse ) ) {
- VectorAdd( out->normal, in->normal, out->normal );
- }
- }
- }
-
- VectorNormalize( out->normal, out->normal );
- }
-}
-
-/*
-================
-EmitTerrainVerts
-================
-*/
-void EmitTerrainVerts( side_t *side, terrainSurf_t *surf, int maxlayer, int alpha[ MAX_POINTS_ON_WINDING ], qboolean projecttexture ) {
- int i;
- int j;
- drawVert_t *vert;
- int *indices;
- int numindices;
- int maxindices;
- int xyplane;
- vec3_t xynorm = { 0, 0, 1 };
- vec_t shift[ 2 ] = { 0, 0 };
- vec_t scale[ 2 ] = { 0.5, 0.5 };
- float vecs[ 2 ][ 4 ];
- static int numtimes = 0;
-
- numtimes++;
-
- if ( !surf->verts ) {
- surf->numVerts = 0;
- surf->maxVerts = GROW_VERTS;
- surf->verts = malloc( surf->maxVerts * sizeof( *surf->verts ) );
-
- surf->numIndexes = 0;
- surf->maxIndexes = GROW_INDICES;
- surf->indexes = malloc( surf->maxIndexes * sizeof( *surf->indexes ) );
- }
-
- // calculate the texture coordinate vectors
- xyplane = FindFloatPlane( xynorm, 0 );
- QuakeTextureVecs( &mapplanes[ xyplane ], shift, 0, scale, vecs );
-
- // emit the vertexes
- numindices = 0;
- maxindices = surf->maxIndexes;
- indices = malloc ( maxindices * sizeof( *indices ) );
-
- for ( i = 0; i < side->winding->numpoints; i++ ) {
- vert = &surf->verts[ surf->numVerts ];
-
- // set the final alpha value--0 for texture 1, 255 for texture 2
- if ( alpha[ i ] < maxlayer ) {
- vert->color[3] = 0;
- } else {
- vert->color[3] = 255;
- }
-
- vert->xyz[ 0 ] = floor( side->winding->p[ i ][ 0 ] + 0.1f );
- vert->xyz[ 1 ] = floor( side->winding->p[ i ][ 1 ] + 0.1f );
- vert->xyz[ 2 ] = floor( side->winding->p[ i ][ 2 ] + 0.1f );
-
- // set the texture coordinates
- if ( projecttexture ) {
- vert->st[0] = ( vecs[0][3] + DotProduct( vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
- vert->st[1] = ( vecs[1][3] + DotProduct( vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
- } else {
- vert->st[0] = ( side->vecs[0][3] + DotProduct( side->vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
- vert->st[1] = ( side->vecs[1][3] + DotProduct( side->vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
- }
-
- VectorCopy( mapplanes[ side->planenum ].normal, vert->normal );
-
- for( j = 0; j < surf->numVerts; j++ ) {
- if ( CompareVert( vert, &surf->verts[ j ], qtrue ) ) {
- break;
- }
- }
-
- if ( numindices >= maxindices ) {
- maxindices += GROW_INDICES;
- indices = realloc( indices, maxindices * sizeof( *indices ) );
- }
-
- if ( j != surf->numVerts ) {
- indices[ numindices++ ] = j;
- } else {
- indices[ numindices++ ] = surf->numVerts;
- surf->numVerts++;
- if ( surf->numVerts >= surf->maxVerts ) {
- surf->maxVerts += GROW_VERTS;
- surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
- }
- }
- }
-
- SideAsTristrip( surf, indices, numindices );
-
- free( indices );
-}
-
-/*
-================
-SurfaceForShader
-================
-*/
-terrainSurf_t *SurfaceForShader( shaderInfo_t *shader, int x, int y ) {
- int i;
-
- if ( lastSurface && ( lastSurface->shader == shader ) && ( lastSurface->x == x ) && ( lastSurface->y == y ) ) {
- return lastSurface;
- }
-
- lastSurface = surfaces;
- for( i = 0; i < numsurfaces; i++, lastSurface++ ) {
- if ( ( lastSurface->shader == shader ) && ( lastSurface->x == x ) && ( lastSurface->y == y ) ) {
- return lastSurface;
- }
- }
-
- if ( numsurfaces >= maxsurfaces ) {
- maxsurfaces += GROW_SURFACES;
- surfaces = realloc( surfaces, maxsurfaces * sizeof( *surfaces ) );
- memset( surfaces + numsurfaces + 1, 0, ( maxsurfaces - numsurfaces - 1 ) * sizeof( *surfaces ) );
- }
-
- lastSurface= &surfaces[ numsurfaces++ ];
- lastSurface->shader = shader;
- lastSurface->x = x;
- lastSurface->y = y;
-
- return lastSurface;
-}
-
-/*
-================
-SetTerrainTextures
-================
-*/
-void SetTerrainTextures( void ) {
- int i;
- int x, y;
- int layer;
- int minlayer, maxlayer;
- float s, t;
- float min_s, min_t;
- int alpha[ MAX_POINTS_ON_WINDING ];
- shaderInfo_t *si, *terrainShader;
- bspbrush_t *brush;
- side_t *side;
- const char *shadername;
- vec3_t mins, maxs;
- vec3_t size;
- int surfwidth, surfheight, surfsize;
- terrainSurf_t *surf;
- byte *alphamap;
- int alphawidth, alphaheight;
- int num_layers;
- extern qboolean onlyents;
-
- if ( onlyents ) {
- return;
- }
-
- shadername = ValueForKey( mapent, "shader" );
- if ( !shadername[ 0 ] ) {
- Error ("SetTerrainTextures: shader not specified" );
- }
-
- alphamap = LoadAlphaMap( &num_layers, &alphawidth, &alphaheight );
-
- mapent->firstDrawSurf = numMapDrawSurfs;
-
- // calculate the size of the terrain
- CalcTerrainSize( mins, maxs, size );
-
- surfwidth = ( size[ 0 ] + SURF_WIDTH - 1 ) / SURF_WIDTH;
- surfheight = ( size[ 1 ] + SURF_HEIGHT - 1 ) / SURF_HEIGHT;
- surfsize = surfwidth * surfheight;
-
- lastSurface = NULL;
- numsurfaces = 0;
- maxsurfaces = 0;
- for( i = num_layers; i > 0; i-- ) {
- maxsurfaces += i * surfsize;
- }
- surfaces = malloc( maxsurfaces * sizeof( *surfaces ) );
- memset( surfaces, 0, maxsurfaces * sizeof( *surfaces ) );
-
- terrainShader = ShaderInfoForShader( "textures/common/terrain" );
-
- for( brush = mapent->brushes; brush != NULL; brush = brush->next ) {
- // only create surfaces for sides marked as terrain
- for( side = brush->sides; side < &brush->sides[ brush->numsides ]; side++ ) {
- if ( !side->shaderInfo ) {
- continue;
- }
-
- if ( ( ( side->surfaceFlags | side->shaderInfo->surfaceFlags ) & SURF_NODRAW ) && !strstr( side->shaderInfo->shader, "terrain" ) ) {
- continue;
- }
-
- minlayer = num_layers;
- maxlayer = 0;
-
- // project each point of the winding onto the alphamap to determine which
- // textures to blend
- min_s = 1.0;
- min_t = 1.0;
- for( i = 0; i < side->winding->numpoints; i++ ) {
- s = floor( side->winding->p[ i ][ 0 ] + 0.1f - mins[ 0 ] ) / size[ 0 ];
- t = floor( side->winding->p[ i ][ 1 ] + 0.1f - mins[ 0 ] ) / size[ 1 ];
-
- if ( s < 0 ) {
- s = 0;
- }
-
- if ( t < 0 ) {
- t = 0;
- }
-
- if ( s >= 1.0 ) {
- s = 1.0;
- }
-
- if ( t >= 1.0 ) {
- t = 1.0;
- }
-
- if ( s < min_s ) {
- min_s = s;
- }
-
- if ( t < min_t ) {
- min_t = t;
- }
-
- x = ( alphawidth - 1 ) * s;
- y = ( alphaheight - 1 ) * t;
-
- layer = alphamap[ x + y * alphawidth ];
- if ( layer < minlayer ) {
- minlayer = layer;
- }
-
- if ( layer > maxlayer ) {
- maxlayer = layer;
- }
-
- alpha[ i ] = layer;
- }
-
- x = min_s * surfwidth;
- if ( x >= surfwidth ) {
- x = surfwidth - 1;
- }
-
- y = min_t * surfheight;
- if ( y >= surfheight ) {
- y = surfheight - 1;
- }
-
- if ( strstr( side->shaderInfo->shader, "terrain" ) ) {
- si = ShaderForLayer( minlayer, maxlayer, shadername );
- if ( showseams ) {
- for( i = 0; i < side->winding->numpoints; i++ ) {
- if ( ( alpha[ i ] != minlayer ) && ( alpha[ i ] != maxlayer ) ) {
- si = ShaderInfoForShader( "textures/common/white" );
- break;
- }
- }
- }
- surf = SurfaceForShader( si, x, y );
- EmitTerrainVerts( side, surf, maxlayer, alpha, qtrue );
- } else {
- si = side->shaderInfo;
- side->shaderInfo = terrainShader;
- surf = SurfaceForShader( si, x, y );
- EmitTerrainVerts( side, surf, maxlayer, alpha, qfalse );
- }
- }
- }
-
- // create the final surfaces
- for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
- if ( surf->numVerts ) {
- CreateTerrainSurface( surf, surf->shader );
- }
- }
-
- //
- // clean up any allocated memory
- //
- for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
- if ( surf->verts ) {
- free( surf->verts );
- free( surf->indexes );
- }
- }
- free( alphamap );
- free( surfaces );
-
- surfaces = NULL;
- lastSurface = NULL;
- numsurfaces = 0;
- maxsurfaces = 0;
-}
-
-/*****************************************************************************
-
- New terrain code
-
-******************************************************************************/
-
-typedef struct terrainFace_s {
- shaderInfo_t *shaderInfo;
- //texdef_t texdef;
-
- float vecs[ 2 ][ 4 ]; // texture coordinate mapping
-} terrainFace_t;
-
-typedef struct terrainVert_s {
- vec3_t xyz;
- terrainFace_t tri;
-} terrainVert_t;
-
-typedef struct terrainMesh_s {
- float scale_x;
- float scale_y;
- vec3_t origin;
-
- int width, height;
- terrainVert_t *map;
-} terrainMesh_t;
-
-terrainVert_t *Terrain_GetVert( terrainMesh_t *pm, int x, int y ) {
- return &pm->map[ x + y * pm->width ];
-}
-
-void Terrain_GetTriangles( terrainMesh_t *pm, int x, int y, terrainVert_t **verts ) {
- if ( ( x + y ) & 1 ) {
- // first tri
- verts[ 0 ] = Terrain_GetVert( pm, x, y );
- verts[ 1 ] = Terrain_GetVert( pm, x, y + 1 );
- verts[ 2 ] = Terrain_GetVert( pm, x + 1, y + 1 );
-
- // second tri
- verts[ 3 ] = verts[ 2 ];
- verts[ 4 ] = Terrain_GetVert( pm, x + 1, y );
- verts[ 5 ] = verts[ 0 ];
- } else {
- // first tri
- verts[ 0 ] = Terrain_GetVert( pm, x, y );
- verts[ 1 ] = Terrain_GetVert( pm, x, y + 1 );
- verts[ 2 ] = Terrain_GetVert( pm, x + 1, y );
-
- // second tri
- verts[ 3 ] = verts[ 2 ];
- verts[ 4 ] = verts[ 1 ];
- verts[ 5 ] = Terrain_GetVert( pm, x + 1, y + 1 );
- }
-}
-
-/*
-================
-EmitTerrainVerts2
-================
-*/
-void EmitTerrainVerts2( terrainSurf_t *surf, terrainVert_t **verts, int alpha[ 3 ] ) {
- int i;
- int j;
- drawVert_t *vert;
- int *indices;
- int numindices;
- int maxindices;
- int xyplane;
- vec3_t xynorm = { 0, 0, 1 };
- vec_t shift[ 2 ] = { 0, 0 };
- vec_t scale[ 2 ] = { 0.5, 0.5 };
- float vecs[ 2 ][ 4 ];
- vec4_t plane;
-
- if ( !surf->verts ) {
- surf->numVerts = 0;
- surf->maxVerts = GROW_VERTS;
- surf->verts = malloc( surf->maxVerts * sizeof( *surf->verts ) );
-
- surf->numIndexes = 0;
- surf->maxIndexes = GROW_INDICES;
- surf->indexes = malloc( surf->maxIndexes * sizeof( *surf->indexes ) );
- }
-
- // calculate the texture coordinate vectors
- xyplane = FindFloatPlane( xynorm, 0 );
- QuakeTextureVecs( &mapplanes[ xyplane ], shift, 0, scale, vecs );
-
- // emit the vertexes
- numindices = 0;
- maxindices = surf->maxIndexes;
- assert( maxindices >= 0 );
- indices = malloc ( maxindices * sizeof( *indices ) );
-
- PlaneFromPoints( plane, verts[ 0 ]->xyz, verts[ 1 ]->xyz, verts[ 2 ]->xyz );
-
- for ( i = 0; i < 3; i++ ) {
- vert = &surf->verts[ surf->numVerts ];
-
- if ( alpha[ i ] ) {
- vert->color[3] = 255;
- } else {
- vert->color[3] = 0;
- }
-
- vert->xyz[ 0 ] = floor( verts[ i ]->xyz[ 0 ] + 0.1f );
- vert->xyz[ 1 ] = floor( verts[ i ]->xyz[ 1 ] + 0.1f );
- vert->xyz[ 2 ] = floor( verts[ i ]->xyz[ 2 ] + 0.1f );
-
- // set the texture coordinates
- vert->st[0] = ( vecs[0][3] + DotProduct( vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
- vert->st[1] = ( vecs[1][3] + DotProduct( vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
-
- VectorCopy( plane, vert->normal );
-
- for( j = 0; j < surf->numVerts; j++ ) {
- if ( CompareVert( vert, &surf->verts[ j ], qtrue ) ) {
- break;
- }
- }
-
- if ( numindices >= maxindices ) {
- maxindices += GROW_INDICES;
- indices = realloc( indices, maxindices * sizeof( *indices ) );
- }
-
- if ( j != surf->numVerts ) {
- indices[ numindices++ ] = j;
- } else {
- indices[ numindices++ ] = surf->numVerts;
- surf->numVerts++;
- if ( surf->numVerts >= surf->maxVerts ) {
- surf->maxVerts += GROW_VERTS;
- surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
- }
- }
- }
-
- SideAsTristrip( surf, indices, numindices );
-
- free( indices );
-}
-
-int MapPlaneFromPoints( vec3_t p0, vec3_t p1, vec3_t p2 );
-void QuakeTextureVecs( plane_t *plane, vec_t shift[2], vec_t rotate, vec_t scale[2], vec_t mappingVecs[2][4] );
-qboolean RemoveDuplicateBrushPlanes( bspbrush_t *b );
-void SetBrushContents( bspbrush_t *b );
-
-void AddBrushSide( vec3_t v1, vec3_t v2, vec3_t v3, shaderInfo_t *terrainShader ) {
- side_t *side;
- int planenum;
-
- side = &buildBrush->sides[ buildBrush->numsides ];
- memset( side, 0, sizeof( *side ) );
- buildBrush->numsides++;
-
- side->shaderInfo = terrainShader;
-
- // find the plane number
- planenum = MapPlaneFromPoints( v1, v2, v3 );
- side->planenum = planenum;
-}
-
-void MakeBrushFromTriangle( vec3_t v1, vec3_t v2, vec3_t v3, shaderInfo_t *terrainShader ) {
- bspbrush_t *b;
- vec3_t d1;
- vec3_t d2;
- vec3_t d3;
-
- VectorSet( d1, v1[ 0 ], v1[ 1 ], MIN_WORLD_COORD + 10 ); //FIXME
- VectorSet( d2, v2[ 0 ], v2[ 1 ], MIN_WORLD_COORD + 10 );
- VectorSet( d3, v3[ 0 ], v3[ 1 ], MIN_WORLD_COORD + 10 );
-
- buildBrush->numsides = 0;
- buildBrush->detail = qfalse;
-
- AddBrushSide( v1, v2, v3, terrainShader );
- AddBrushSide( v1, d1, v2, terrainShader );
- AddBrushSide( v2, d2, v3, terrainShader );
- AddBrushSide( v3, d3, v1, terrainShader );
- AddBrushSide( d3, d2, d1, terrainShader );
-
- buildBrush->portalareas[0] = -1;
- buildBrush->portalareas[1] = -1;
- buildBrush->entitynum = num_entities-1;
- buildBrush->brushnum = entitySourceBrushes;
-
- // if there are mirrored planes, the entire brush is invalid
- if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
- return;
- }
-
- // get the content for the entire brush
- SetBrushContents( buildBrush );
- buildBrush->contents |= CONTENTS_DETAIL;
-
- b = FinishBrush();
- if ( !b ) {
- return;
- }
-}
-
-void MakeTerrainIntoBrushes( terrainMesh_t *tm ) {
- int index[ 6 ];
- int y;
- int x;
- terrainVert_t *verts;
- shaderInfo_t *terrainShader;
-
- terrainShader = ShaderInfoForShader( "textures/common/terrain" );
-
- verts = tm->map;
- for( y = 0; y < tm->height - 1; y++ ) {
- for( x = 0; x < tm->width - 1; x++ ) {
- if ( ( x + y ) & 1 ) {
- // first tri
- index[ 0 ] = x + y * tm->width;
- index[ 1 ] = x + ( y + 1 ) * tm->width;
- index[ 2 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
- index[ 3 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
- index[ 4 ] = ( x + 1 ) + y * tm->width;
- index[ 5 ] = x + y * tm->width;
- } else {
- // first tri
- index[ 0 ] = x + y * tm->width;
- index[ 1 ] = x + ( y + 1 ) * tm->width;
- index[ 2 ] = ( x + 1 ) + y * tm->width;
- index[ 3 ] = ( x + 1 ) + y * tm->width;
- index[ 4 ] = x + ( y + 1 ) * tm->width;
- index[ 5 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
- }
-
- MakeBrushFromTriangle( verts[ index[ 0 ] ].xyz, verts[ index[ 1 ] ].xyz, verts[ index[ 2 ] ].xyz, terrainShader );
- MakeBrushFromTriangle( verts[ index[ 3 ] ].xyz, verts[ index[ 4 ] ].xyz, verts[ index[ 5 ] ].xyz, terrainShader );
- }
- }
-}
-
-void Terrain_ParseFace( terrainFace_t *face ) {
- shaderInfo_t *si;
- vec_t shift[ 2 ];
- vec_t rotate;
- vec_t scale[ 2 ];
- char name[ MAX_QPATH ];
- char shader[ MAX_QPATH ];
- plane_t p;
-
- // read the texturedef
- GetToken( qfalse );
- strcpy( name, token );
-
- GetToken( qfalse );
- shift[ 0 ] = atof(token);
- GetToken( qfalse );
- shift[ 1 ] = atof( token );
- GetToken( qfalse );
- rotate = atof( token );
- GetToken( qfalse );
- scale[ 0 ] = atof( token );
- GetToken( qfalse );
- scale[ 1 ] = atof( token );
-
- // find default flags and values
- sprintf( shader, "textures/%s", name );
- si = ShaderInfoForShader( shader );
- face->shaderInfo = si;
- //face->texdef = si->texdef;
-
- // skip over old contents
- GetToken( qfalse );
-
- // skip over old flags
- GetToken( qfalse );
-
- // skip over old value
- GetToken( qfalse );
-
- //Surface_Parse( &face->texdef );
- //Surface_BuildTexdef( &face->texdef );
-
- // make a fake horizontal plane
- VectorSet( p.normal, 0, 0, 1 );
- p.dist = 0;
- p.type = PlaneTypeForNormal( p.normal );
-
- QuakeTextureVecs( &p, shift, rotate, scale, face->vecs );
-}
-
-#define MAX_TERRAIN_TEXTURES 128
-static int numtextures = 0;;
-static shaderInfo_t *textures[ MAX_TERRAIN_TEXTURES ];
-
-void Terrain_AddTexture( shaderInfo_t *texture ) {
- int i;
-
- if ( !texture ) {
- return;
- }
-
- for( i = 0; i < numtextures; i++ ) {
- if ( textures[ i ] == texture ) {
- return;
- }
- }
-
- if ( numtextures >= MAX_TERRAIN_TEXTURES ) {
- Error( "Too many textures on terrain" );
- return;
- }
-
- textures[ numtextures++ ] = texture;
-}
-
-int LayerForShader( shaderInfo_t *shader ) {
- int i;
- int l;
-
- l = strlen( shader->shader );
- for( i = l - 1; i >= 0; i-- ) {
- if ( shader->shader[ i ] == '_' ) {
- return atoi( &shader->shader[ i + 1 ] );
- break;
- }
- }
-
- return 0;
-}
-
-/*
-=================
-ParseTerrain
-
-Creates a mapDrawSurface_t from the terrain text
-=================
-*/
-
-void ParseTerrain( void ) {
- int i, j;
- int x, y;
- int x1, y1;
- terrainMesh_t t;
- int index;
- terrainVert_t *verts[ 6 ];
- int num_layers;
- int layer, minlayer, maxlayer;
- int alpha[ 6 ];
- shaderInfo_t *si, *terrainShader;
- int surfwidth, surfheight, surfsize;
- terrainSurf_t *surf;
- char shadername[ MAX_QPATH ];
-
- mapent->firstDrawSurf = numMapDrawSurfs;
-
- memset( &t, 0, sizeof( t ) );
-
- MatchToken( "{" );
-
- // get width
- GetToken( qtrue );
- t.width = atoi( token );
-
- // get height
- GetToken( qfalse );
- t.height = atoi( token );
-
- // get scale_x
- GetToken( qfalse );
- t.scale_x = atof( token );
-
- // get scale_y
- GetToken( qfalse );
- t.scale_y = atof( token );
-
- // get origin
- GetToken( qtrue );
- t.origin[ 0 ] = atof( token );
- GetToken( qfalse );
- t.origin[ 1 ] = atof( token );
- GetToken( qfalse );
- t.origin[ 2 ] = atof( token );
-
- t.map = malloc( t.width * t.height * sizeof( t.map[ 0 ] ) );
-
- if ( t.width <= 0 || t.height <= 0 ) {
- Error( "ParseTerrain: bad size" );
- }
-
- numtextures = 0;
- index = 0;
- for ( i = 0; i < t.height; i++ ) {
- for( j = 0; j < t.width; j++, index++ ) {
- // get height
- GetToken( qtrue );
- t.map[ index ].xyz[ 0 ] = t.origin[ 0 ] + t.scale_x * ( float )j;
- t.map[ index ].xyz[ 1 ] = t.origin[ 1 ] + t.scale_y * ( float )i;
- t.map[ index ].xyz[ 2 ] = t.origin[ 2 ] + atof( token );
-
- Terrain_ParseFace( &t.map[ index ].tri );
- Terrain_AddTexture( t.map[ index ].tri.shaderInfo );
- }
- }
-
- MatchToken( "}" );
- MatchToken( "}" );
-
- MakeTerrainIntoBrushes( &t );
-
- surfwidth = ( ( t.scale_x * t.width ) + SURF_WIDTH - 1 ) / SURF_WIDTH;
- surfheight = ( ( t.scale_y * t.height ) + SURF_HEIGHT - 1 ) / SURF_HEIGHT;
- surfsize = surfwidth * surfheight;
-
- //FIXME
- num_layers = 0;
- for( i = 0; i < numtextures; i++ ) {
- layer = LayerForShader( textures[ i ] ) + 1;
- if ( layer > num_layers ) {
- num_layers = layer;
- }
- }
- num_layers = 4;
-
- memset( alpha, 0, sizeof( alpha ) );
-
- lastSurface = NULL;
- numsurfaces = 0;
- maxsurfaces = 0;
- for( i = num_layers; i > 0; i-- ) {
- maxsurfaces += i * surfsize;
- }
-
- surfaces = malloc( maxsurfaces * sizeof( *surfaces ) );
- memset( surfaces, 0, maxsurfaces * sizeof( *surfaces ) );
-
- terrainShader = ShaderInfoForShader( "textures/common/terrain" );
-
- // get the shadername
- if ( Q_strncasecmp( textures[ 0 ]->shader, "textures/", 9 ) == 0 ) {
- strcpy( shadername, &textures[ 0 ]->shader[ 9 ] );
- } else {
- strcpy( shadername, textures[ 0 ]->shader );
- }
- j = strlen( shadername );
- for( i = j - 1; i >= 0; i-- ) {
- if ( shadername[ i ] == '_' ) {
- shadername[ i ] = 0;
- break;
- }
- }
-
- for( y = 0; y < t.height - 1; y++ ) {
- for( x = 0; x < t.width - 1; x++ ) {
- Terrain_GetTriangles( &t, x, y, verts );
-
- x1 = ( ( float )x / ( float )( t.width - 1 ) ) * surfwidth;
- if ( x1 >= surfwidth ) {
- x1 = surfwidth - 1;
- }
-
- y1 = ( ( float )y / ( float )( t.height - 1 ) ) * surfheight;
- if ( y1 >= surfheight ) {
- y1 = surfheight - 1;
- }
-
- maxlayer = minlayer = LayerForShader( verts[ 0 ]->tri.shaderInfo );
- for( i = 0; i < 3; i++ ) {
- layer = LayerForShader( verts[ i ]->tri.shaderInfo );
- if ( layer < minlayer ) {
- minlayer = layer;
- }
- if ( layer > maxlayer ) {
- maxlayer = layer;
- }
- }
-
- for( i = 0; i < 3; i++ ) {
- layer = LayerForShader( verts[ i ]->tri.shaderInfo );
- if ( layer > minlayer ) {
- alpha[ i ] = 1.0f;
- } else {
- alpha[ i ] = 0.0f;
- }
- }
-
- si = ShaderForLayer( minlayer, maxlayer, shadername );
- surf = SurfaceForShader( si, x1, y1 );
- EmitTerrainVerts2( surf, &verts[ 0 ], &alpha[ 0 ] );
-
- // second triangle
- maxlayer = minlayer = LayerForShader( verts[ 3 ]->tri.shaderInfo );
- for( i = 3; i < 6; i++ ) {
- layer = LayerForShader( verts[ i ]->tri.shaderInfo );
- if ( layer < minlayer ) {
- minlayer = layer;
- }
- if ( layer > maxlayer ) {
- maxlayer = layer;
- }
- }
-
- for( i = 3; i < 6; i++ ) {
- layer = LayerForShader( verts[ i ]->tri.shaderInfo );
- if ( layer > minlayer ) {
- alpha[ i ] = 1.0f;
- } else {
- alpha[ i ] = 0.0f;
- }
- }
-
- si = ShaderForLayer( minlayer, maxlayer, shadername );
- surf = SurfaceForShader( si, x1, y1 );
- EmitTerrainVerts2( surf, &verts[ 3 ], &alpha[ 3 ] );
- }
- }
-
- // create the final surfaces
- for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
- if ( surf->numVerts ) {
- CreateTerrainSurface( surf, surf->shader );
- }
- }
-
- //
- // clean up any allocated memory
- //
- for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
- if ( surf->verts ) {
- free( surf->verts );
- free( surf->indexes );
- }
- }
- free( surfaces );
-
- surfaces = NULL;
- lastSurface = NULL;
- numsurfaces = 0;
- maxsurfaces = 0;
-
- free( t.map );
-}
-
+#include "qbsp.h"
+#include <assert.h>
+
+#define SURF_WIDTH 2048
+#define SURF_HEIGHT 2048
+
+#define GROW_VERTS 512
+#define GROW_INDICES 512
+#define GROW_SURFACES 128
+
+#define VectorSet(v, x, y, z) v[0] = x;v[1] = y;v[2] = z;
+
+void QuakeTextureVecs( plane_t *plane, vec_t shift[2], vec_t rotate, vec_t scale[2], vec_t mappingVecs[2][4] );
+
+typedef struct {
+ shaderInfo_t *shader;
+ int x, y;
+
+ int maxVerts;
+ int numVerts;
+ drawVert_t *verts;
+
+ int maxIndexes;
+ int numIndexes;
+ int *indexes;
+} terrainSurf_t;
+
+static terrainSurf_t *surfaces = NULL;
+static terrainSurf_t *lastSurface = NULL;
+static int numsurfaces = 0;
+static int maxsurfaces = 0;
+
+/*
+================
+ShaderForLayer
+================
+*/
+shaderInfo_t *ShaderForLayer( int minlayer, int maxlayer, const char *shadername ) {
+ char shader[ MAX_QPATH ];
+
+ if ( minlayer == maxlayer ) {
+ sprintf( shader, "textures/%s_%d", shadername, maxlayer );
+ } else {
+ sprintf( shader, "textures/%s_%dto%d", shadername, minlayer, maxlayer );
+ }
+
+ return ShaderInfoForShader( shader );
+}
+
+/*
+================
+CompareVert
+================
+*/
+qboolean CompareVert( drawVert_t *v1, drawVert_t *v2, qboolean checkst ) {
+ int i;
+
+ for( i = 0; i < 3; i++ ) {
+ if ( floor( v1->xyz[ i ] + 0.1 ) != floor( v2->xyz[ i ] + 0.1 ) ) {
+ return qfalse;
+ }
+ if ( checkst && ( ( v1->st[ 0 ] != v2->st[ 0 ] ) || ( v1->st[ 1 ] != v2->st[ 1 ] ) ) ) {
+ return qfalse;
+ }
+ }
+
+ return qtrue;
+}
+
+/*
+================
+LoadAlphaMap
+================
+*/
+byte *LoadAlphaMap( int *num_layers, int *alphawidth, int *alphaheight ) {
+ int *alphamap32;
+ byte *alphamap;
+ const char *alphamapname;
+ char ext[ 128 ];
+ int width;
+ int height;
+ int layers;
+ int size;
+ int i;
+
+ assert( alphawidth );
+ assert( alphaheight );
+ assert( num_layers );
+
+ layers = atoi( ValueForKey( mapent, "layers" ) );
+ if ( layers < 1 ) {
+ Error ("SetTerrainTextures: invalid value for 'layers' (%d)", layers );
+ }
+
+ alphamapname = ValueForKey( mapent, "alphamap" );
+ if ( !alphamapname[ 0 ] ) {
+ Error ("LoadAlphaMap: No alphamap specified on terrain" );
+ }
+
+ ExtractFileExtension( alphamapname, ext);
+ if ( !Q_stricmp( ext, "tga" ) ) {
+ Load32BitImage( ExpandGamePath( alphamapname ), &alphamap32, &width, &height );
+
+ size = width * height;
+ alphamap = malloc( size );
+ for( i = 0; i < size; i++ ) {
+ alphamap[ i ] = ( ( alphamap32[ i ] & 0xff ) * layers ) / 256;
+ if ( alphamap[ i ] >= layers ) {
+ alphamap[ i ] = layers - 1;
+ }
+ }
+ } else {
+ Load256Image( ExpandGamePath( alphamapname ), &alphamap, NULL, &width, &height );
+ size = width * height;
+ for( i = 0; i < size; i++ ) {
+ if ( alphamap[ i ] >= layers ) {
+ alphamap[ i ] = layers - 1;
+ }
+ }
+ }
+
+ if ( ( width < 2 ) || ( height < 2 ) ) {
+ Error ("LoadAlphaMap: alphamap width/height must be at least 2x2." );
+ }
+
+ *num_layers = layers;
+ *alphawidth = width;
+ *alphaheight = height;
+
+ return alphamap;
+}
+
+/*
+================
+CalcTerrainSize
+================
+*/
+void CalcTerrainSize( vec3_t mins, vec3_t maxs, vec3_t size ) {
+ bspbrush_t *brush;
+ int i;
+ const char *key;
+
+ // calculate the size of the terrain
+ ClearBounds( mins, maxs );
+ for( brush = mapent->brushes; brush != NULL; brush = brush->next ) {
+ AddPointToBounds( brush->mins, mins, maxs );
+ AddPointToBounds( brush->maxs, mins, maxs );
+ }
+
+ key = ValueForKey( mapent, "min" );
+ if ( key[ 0 ] ) {
+ GetVectorForKey( mapent, "min", mins );
+ }
+
+ key = ValueForKey( mapent, "max" );
+ if ( key[ 0 ] ) {
+ GetVectorForKey( mapent, "max", maxs );
+ }
+
+ for( i = 0; i < 3; i++ ) {
+ mins[ i ] = floor( mins[ i ] + 0.1 );
+ maxs[ i ] = floor( maxs[ i ] + 0.1 );
+ }
+
+ VectorSubtract( maxs, mins, size );
+
+ if ( ( size[ 0 ] <= 0 ) || ( size[ 1 ] <= 0 ) ) {
+ Error ("CalcTerrainSize: Invalid terrain size: %fx%f", size[ 0 ], size[ 1 ] );
+ }
+}
+
+/*
+==================
+IsTriangleDegenerate
+
+Returns qtrue if all three points are collinear or backwards
+===================
+*/
+#define COLINEAR_AREA 10
+static qboolean IsTriangleDegenerate( drawVert_t *points, int a, int b, int c ) {
+ vec3_t v1, v2, v3;
+ float d;
+
+ VectorSubtract( points[b].xyz, points[a].xyz, v1 );
+ VectorSubtract( points[c].xyz, points[a].xyz, v2 );
+ CrossProduct( v1, v2, v3 );
+ d = VectorLength( v3 );
+
+ // assume all very small or backwards triangles will cause problems
+ if ( d < COLINEAR_AREA ) {
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+===============
+SideAsTriFan
+
+The surface can't be represented as a single tristrip without
+leaving a degenerate triangle (and therefore a crack), so add
+a point in the middle and create (points-1) triangles in fan order
+===============
+*/
+static void SideAsTriFan( terrainSurf_t *surf, int *index, int num ) {
+ int i;
+ int colorSum[4];
+ drawVert_t *mid, *v;
+
+ // make sure we have enough space for a new vert
+ if ( surf->numVerts >= surf->maxVerts ) {
+ surf->maxVerts += GROW_VERTS;
+ surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
+ }
+
+ // create a new point in the center of the face
+ mid = &surf->verts[ surf->numVerts ];
+ surf->numVerts++;
+
+ colorSum[0] = colorSum[1] = colorSum[2] = colorSum[3] = 0;
+
+ for (i = 0 ; i < num; i++ ) {
+ v = &surf->verts[ index[ i ] ];
+ VectorAdd( mid->xyz, v->xyz, mid->xyz );
+ mid->st[0] += v->st[0];
+ mid->st[1] += v->st[1];
+ mid->lightmap[0] += v->lightmap[0];
+ mid->lightmap[1] += v->lightmap[1];
+
+ colorSum[0] += v->color[0];
+ colorSum[1] += v->color[1];
+ colorSum[2] += v->color[2];
+ colorSum[3] += v->color[3];
+ }
+
+ mid->xyz[0] /= num;
+ mid->xyz[1] /= num;
+ mid->xyz[2] /= num;
+
+ mid->st[0] /= num;
+ mid->st[1] /= num;
+
+ mid->lightmap[0] /= num;
+ mid->lightmap[1] /= num;
+
+ mid->color[0] = colorSum[0] / num;
+ mid->color[1] = colorSum[1] / num;
+ mid->color[2] = colorSum[2] / num;
+ mid->color[3] = colorSum[3] / num;
+
+ // fill in indices in trifan order
+ if ( surf->numIndexes + num * 3 > surf->maxIndexes ) {
+ surf->maxIndexes = surf->numIndexes + num * 3;
+ surf->indexes = realloc( surf->indexes, surf->maxIndexes * sizeof( *surf->indexes ) );
+ }
+
+
+ for ( i = 0 ; i < num; i++ ) {
+ surf->indexes[ surf->numIndexes++ ] = surf->numVerts - 1;
+ surf->indexes[ surf->numIndexes++ ] = index[ i ];
+ surf->indexes[ surf->numIndexes++ ] = index[ (i+1) % ( surf->numVerts - 1 ) ];
+ }
+}
+/*
+================
+SideAsTristrip
+
+Try to create indices that make (points-2) triangles in tristrip order
+================
+*/
+#define MAX_INDICES 1024
+static void SideAsTristrip( terrainSurf_t *surf, int *index, int num ) {
+ int i;
+ int rotate;
+ int numIndices;
+ int ni;
+ int a, b, c;
+ int indices[ MAX_INDICES ];
+
+ // determine the triangle strip order
+ numIndices = ( num - 2 ) * 3;
+ if ( numIndices > MAX_INDICES ) {
+ Error( "MAX_INDICES exceeded for surface" );
+ }
+
+ // try all possible orderings of the points looking
+ // for a strip order that isn't degenerate
+ for ( rotate = 0 ; rotate < num; rotate++ ) {
+ for ( ni = 0, i = 0 ; i < num - 2 - i ; i++ ) {
+ a = index[ ( num - 1 - i + rotate ) % num ];
+ b = index[ ( i + rotate ) % num ];
+ c = index[ ( num - 2 - i + rotate ) % num ];
+
+ if ( IsTriangleDegenerate( surf->verts, a, b, c ) ) {
+ break;
+ }
+ indices[ni++] = a;
+ indices[ni++] = b;
+ indices[ni++] = c;
+
+ if ( i + 1 != num - 1 - i ) {
+ a = index[ ( num - 2 - i + rotate ) % num ];
+ b = index[ ( i + rotate ) % num ];
+ c = index[ ( i + 1 + rotate ) % num ];
+
+ if ( IsTriangleDegenerate( surf->verts, a, b, c ) ) {
+ break;
+ }
+ indices[ni++] = a;
+ indices[ni++] = b;
+ indices[ni++] = c;
+ }
+ }
+ if ( ni == numIndices ) {
+ break; // got it done without degenerate triangles
+ }
+ }
+
+ // if any triangle in the strip is degenerate,
+ // render from a centered fan point instead
+ if ( ni < numIndices ) {
+ SideAsTriFan( surf, index, num );
+ return;
+ }
+
+ // a normal tristrip
+ if ( surf->numIndexes + ni > surf->maxIndexes ) {
+ surf->maxIndexes = surf->numIndexes + ni;
+ surf->indexes = realloc( surf->indexes, surf->maxIndexes * sizeof( *surf->indexes ) );
+ }
+
+ memcpy( surf->indexes + surf->numIndexes, indices, ni * sizeof( *surf->indexes ) );
+ surf->numIndexes += ni;
+}
+
+/*
+================
+CreateTerrainSurface
+================
+*/
+void CreateTerrainSurface( terrainSurf_t *surf, shaderInfo_t *shader ) {
+ int i, j, k;
+ drawVert_t *out;
+ drawVert_t *in;
+ mapDrawSurface_t *newsurf;
+
+ newsurf = AllocDrawSurf();
+
+ newsurf->miscModel = qtrue;
+ newsurf->shaderInfo = shader;
+ newsurf->lightmapNum = -1;
+ newsurf->fogNum = -1;
+ newsurf->numIndexes = surf->numIndexes;
+ newsurf->numVerts = surf->numVerts;
+
+ // copy the indices
+ newsurf->indexes = malloc( surf->numIndexes * sizeof( *newsurf->indexes ) );
+ memcpy( newsurf->indexes, surf->indexes, surf->numIndexes * sizeof( *newsurf->indexes ) );
+
+ // allocate the vertices
+ newsurf->verts = malloc( surf->numVerts * sizeof( *newsurf->verts ) );
+ memset( newsurf->verts, 0, surf->numVerts * sizeof( *newsurf->verts ) );
+
+ // calculate the surface verts
+ out = newsurf->verts;
+ for( i = 0; i < newsurf->numVerts; i++, out++ ) {
+ VectorCopy( surf->verts[ i ].xyz, out->xyz );
+
+ // set the texture coordinates
+ out->st[ 0 ] = surf->verts[ i ].st[ 0 ];
+ out->st[ 1 ] = surf->verts[ i ].st[ 1 ];
+
+ // the colors will be set by the lighting pass
+ out->color[0] = 255;
+ out->color[1] = 255;
+ out->color[2] = 255;
+ out->color[3] = surf->verts[ i ].color[ 3 ];
+
+ // calculate the vertex normal
+ VectorClear( out->normal );
+ for( j = 0; j < numsurfaces; j++ ) {
+ in = surfaces[ j ].verts;
+ for( k = 0; k < surfaces[ j ].numVerts; k++, in++ ) {
+ if ( CompareVert( out, in, qfalse ) ) {
+ VectorAdd( out->normal, in->normal, out->normal );
+ }
+ }
+ }
+
+ VectorNormalize( out->normal, out->normal );
+ }
+}
+
+/*
+================
+EmitTerrainVerts
+================
+*/
+void EmitTerrainVerts( side_t *side, terrainSurf_t *surf, int maxlayer, int alpha[ MAX_POINTS_ON_WINDING ], qboolean projecttexture ) {
+ int i;
+ int j;
+ drawVert_t *vert;
+ int *indices;
+ int numindices;
+ int maxindices;
+ int xyplane;
+ vec3_t xynorm = { 0, 0, 1 };
+ vec_t shift[ 2 ] = { 0, 0 };
+ vec_t scale[ 2 ] = { 0.5, 0.5 };
+ float vecs[ 2 ][ 4 ];
+ static int numtimes = 0;
+
+ numtimes++;
+
+ if ( !surf->verts ) {
+ surf->numVerts = 0;
+ surf->maxVerts = GROW_VERTS;
+ surf->verts = malloc( surf->maxVerts * sizeof( *surf->verts ) );
+
+ surf->numIndexes = 0;
+ surf->maxIndexes = GROW_INDICES;
+ surf->indexes = malloc( surf->maxIndexes * sizeof( *surf->indexes ) );
+ }
+
+ // calculate the texture coordinate vectors
+ xyplane = FindFloatPlane( xynorm, 0 );
+ QuakeTextureVecs( &mapplanes[ xyplane ], shift, 0, scale, vecs );
+
+ // emit the vertexes
+ numindices = 0;
+ maxindices = surf->maxIndexes;
+ indices = malloc ( maxindices * sizeof( *indices ) );
+
+ for ( i = 0; i < side->winding->numpoints; i++ ) {
+ vert = &surf->verts[ surf->numVerts ];
+
+ // set the final alpha value--0 for texture 1, 255 for texture 2
+ if ( alpha[ i ] < maxlayer ) {
+ vert->color[3] = 0;
+ } else {
+ vert->color[3] = 255;
+ }
+
+ vert->xyz[ 0 ] = floor( side->winding->p[ i ][ 0 ] + 0.1f );
+ vert->xyz[ 1 ] = floor( side->winding->p[ i ][ 1 ] + 0.1f );
+ vert->xyz[ 2 ] = floor( side->winding->p[ i ][ 2 ] + 0.1f );
+
+ // set the texture coordinates
+ if ( projecttexture ) {
+ vert->st[0] = ( vecs[0][3] + DotProduct( vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
+ vert->st[1] = ( vecs[1][3] + DotProduct( vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
+ } else {
+ vert->st[0] = ( side->vecs[0][3] + DotProduct( side->vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
+ vert->st[1] = ( side->vecs[1][3] + DotProduct( side->vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
+ }
+
+ VectorCopy( mapplanes[ side->planenum ].normal, vert->normal );
+
+ for( j = 0; j < surf->numVerts; j++ ) {
+ if ( CompareVert( vert, &surf->verts[ j ], qtrue ) ) {
+ break;
+ }
+ }
+
+ if ( numindices >= maxindices ) {
+ maxindices += GROW_INDICES;
+ indices = realloc( indices, maxindices * sizeof( *indices ) );
+ }
+
+ if ( j != surf->numVerts ) {
+ indices[ numindices++ ] = j;
+ } else {
+ indices[ numindices++ ] = surf->numVerts;
+ surf->numVerts++;
+ if ( surf->numVerts >= surf->maxVerts ) {
+ surf->maxVerts += GROW_VERTS;
+ surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
+ }
+ }
+ }
+
+ SideAsTristrip( surf, indices, numindices );
+
+ free( indices );
+}
+
+/*
+================
+SurfaceForShader
+================
+*/
+terrainSurf_t *SurfaceForShader( shaderInfo_t *shader, int x, int y ) {
+ int i;
+
+ if ( lastSurface && ( lastSurface->shader == shader ) && ( lastSurface->x == x ) && ( lastSurface->y == y ) ) {
+ return lastSurface;
+ }
+
+ lastSurface = surfaces;
+ for( i = 0; i < numsurfaces; i++, lastSurface++ ) {
+ if ( ( lastSurface->shader == shader ) && ( lastSurface->x == x ) && ( lastSurface->y == y ) ) {
+ return lastSurface;
+ }
+ }
+
+ if ( numsurfaces >= maxsurfaces ) {
+ maxsurfaces += GROW_SURFACES;
+ surfaces = realloc( surfaces, maxsurfaces * sizeof( *surfaces ) );
+ memset( surfaces + numsurfaces + 1, 0, ( maxsurfaces - numsurfaces - 1 ) * sizeof( *surfaces ) );
+ }
+
+ lastSurface= &surfaces[ numsurfaces++ ];
+ lastSurface->shader = shader;
+ lastSurface->x = x;
+ lastSurface->y = y;
+
+ return lastSurface;
+}
+
+/*
+================
+SetTerrainTextures
+================
+*/
+void SetTerrainTextures( void ) {
+ int i;
+ int x, y;
+ int layer;
+ int minlayer, maxlayer;
+ float s, t;
+ float min_s, min_t;
+ int alpha[ MAX_POINTS_ON_WINDING ];
+ shaderInfo_t *si, *terrainShader;
+ bspbrush_t *brush;
+ side_t *side;
+ const char *shadername;
+ vec3_t mins, maxs;
+ vec3_t size;
+ int surfwidth, surfheight, surfsize;
+ terrainSurf_t *surf;
+ byte *alphamap;
+ int alphawidth, alphaheight;
+ int num_layers;
+ extern qboolean onlyents;
+
+ if ( onlyents ) {
+ return;
+ }
+
+ shadername = ValueForKey( mapent, "shader" );
+ if ( !shadername[ 0 ] ) {
+ Error ("SetTerrainTextures: shader not specified" );
+ }
+
+ alphamap = LoadAlphaMap( &num_layers, &alphawidth, &alphaheight );
+
+ mapent->firstDrawSurf = numMapDrawSurfs;
+
+ // calculate the size of the terrain
+ CalcTerrainSize( mins, maxs, size );
+
+ surfwidth = ( size[ 0 ] + SURF_WIDTH - 1 ) / SURF_WIDTH;
+ surfheight = ( size[ 1 ] + SURF_HEIGHT - 1 ) / SURF_HEIGHT;
+ surfsize = surfwidth * surfheight;
+
+ lastSurface = NULL;
+ numsurfaces = 0;
+ maxsurfaces = 0;
+ for( i = num_layers; i > 0; i-- ) {
+ maxsurfaces += i * surfsize;
+ }
+ surfaces = malloc( maxsurfaces * sizeof( *surfaces ) );
+ memset( surfaces, 0, maxsurfaces * sizeof( *surfaces ) );
+
+ terrainShader = ShaderInfoForShader( "textures/common/terrain" );
+
+ for( brush = mapent->brushes; brush != NULL; brush = brush->next ) {
+ // only create surfaces for sides marked as terrain
+ for( side = brush->sides; side < &brush->sides[ brush->numsides ]; side++ ) {
+ if ( !side->shaderInfo ) {
+ continue;
+ }
+
+ if ( ( ( side->surfaceFlags | side->shaderInfo->surfaceFlags ) & SURF_NODRAW ) && !strstr( side->shaderInfo->shader, "terrain" ) ) {
+ continue;
+ }
+
+ minlayer = num_layers;
+ maxlayer = 0;
+
+ // project each point of the winding onto the alphamap to determine which
+ // textures to blend
+ min_s = 1.0;
+ min_t = 1.0;
+ for( i = 0; i < side->winding->numpoints; i++ ) {
+ s = floor( side->winding->p[ i ][ 0 ] + 0.1f - mins[ 0 ] ) / size[ 0 ];
+ t = floor( side->winding->p[ i ][ 1 ] + 0.1f - mins[ 0 ] ) / size[ 1 ];
+
+ if ( s < 0 ) {
+ s = 0;
+ }
+
+ if ( t < 0 ) {
+ t = 0;
+ }
+
+ if ( s >= 1.0 ) {
+ s = 1.0;
+ }
+
+ if ( t >= 1.0 ) {
+ t = 1.0;
+ }
+
+ if ( s < min_s ) {
+ min_s = s;
+ }
+
+ if ( t < min_t ) {
+ min_t = t;
+ }
+
+ x = ( alphawidth - 1 ) * s;
+ y = ( alphaheight - 1 ) * t;
+
+ layer = alphamap[ x + y * alphawidth ];
+ if ( layer < minlayer ) {
+ minlayer = layer;
+ }
+
+ if ( layer > maxlayer ) {
+ maxlayer = layer;
+ }
+
+ alpha[ i ] = layer;
+ }
+
+ x = min_s * surfwidth;
+ if ( x >= surfwidth ) {
+ x = surfwidth - 1;
+ }
+
+ y = min_t * surfheight;
+ if ( y >= surfheight ) {
+ y = surfheight - 1;
+ }
+
+ if ( strstr( side->shaderInfo->shader, "terrain" ) ) {
+ si = ShaderForLayer( minlayer, maxlayer, shadername );
+ if ( showseams ) {
+ for( i = 0; i < side->winding->numpoints; i++ ) {
+ if ( ( alpha[ i ] != minlayer ) && ( alpha[ i ] != maxlayer ) ) {
+ si = ShaderInfoForShader( "textures/common/white" );
+ break;
+ }
+ }
+ }
+ surf = SurfaceForShader( si, x, y );
+ EmitTerrainVerts( side, surf, maxlayer, alpha, qtrue );
+ } else {
+ si = side->shaderInfo;
+ side->shaderInfo = terrainShader;
+ surf = SurfaceForShader( si, x, y );
+ EmitTerrainVerts( side, surf, maxlayer, alpha, qfalse );
+ }
+ }
+ }
+
+ // create the final surfaces
+ for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
+ if ( surf->numVerts ) {
+ CreateTerrainSurface( surf, surf->shader );
+ }
+ }
+
+ //
+ // clean up any allocated memory
+ //
+ for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
+ if ( surf->verts ) {
+ free( surf->verts );
+ free( surf->indexes );
+ }
+ }
+ free( alphamap );
+ free( surfaces );
+
+ surfaces = NULL;
+ lastSurface = NULL;
+ numsurfaces = 0;
+ maxsurfaces = 0;
+}
+
+/*****************************************************************************
+
+ New terrain code
+
+******************************************************************************/
+
+typedef struct terrainFace_s {
+ shaderInfo_t *shaderInfo;
+ //texdef_t texdef;
+
+ float vecs[ 2 ][ 4 ]; // texture coordinate mapping
+} terrainFace_t;
+
+typedef struct terrainVert_s {
+ vec3_t xyz;
+ terrainFace_t tri;
+} terrainVert_t;
+
+typedef struct terrainMesh_s {
+ float scale_x;
+ float scale_y;
+ vec3_t origin;
+
+ int width, height;
+ terrainVert_t *map;
+} terrainMesh_t;
+
+terrainVert_t *Terrain_GetVert( terrainMesh_t *pm, int x, int y ) {
+ return &pm->map[ x + y * pm->width ];
+}
+
+void Terrain_GetTriangles( terrainMesh_t *pm, int x, int y, terrainVert_t **verts ) {
+ if ( ( x + y ) & 1 ) {
+ // first tri
+ verts[ 0 ] = Terrain_GetVert( pm, x, y );
+ verts[ 1 ] = Terrain_GetVert( pm, x, y + 1 );
+ verts[ 2 ] = Terrain_GetVert( pm, x + 1, y + 1 );
+
+ // second tri
+ verts[ 3 ] = verts[ 2 ];
+ verts[ 4 ] = Terrain_GetVert( pm, x + 1, y );
+ verts[ 5 ] = verts[ 0 ];
+ } else {
+ // first tri
+ verts[ 0 ] = Terrain_GetVert( pm, x, y );
+ verts[ 1 ] = Terrain_GetVert( pm, x, y + 1 );
+ verts[ 2 ] = Terrain_GetVert( pm, x + 1, y );
+
+ // second tri
+ verts[ 3 ] = verts[ 2 ];
+ verts[ 4 ] = verts[ 1 ];
+ verts[ 5 ] = Terrain_GetVert( pm, x + 1, y + 1 );
+ }
+}
+
+/*
+================
+EmitTerrainVerts2
+================
+*/
+void EmitTerrainVerts2( terrainSurf_t *surf, terrainVert_t **verts, int alpha[ 3 ] ) {
+ int i;
+ int j;
+ drawVert_t *vert;
+ int *indices;
+ int numindices;
+ int maxindices;
+ int xyplane;
+ vec3_t xynorm = { 0, 0, 1 };
+ vec_t shift[ 2 ] = { 0, 0 };
+ vec_t scale[ 2 ] = { 0.5, 0.5 };
+ float vecs[ 2 ][ 4 ];
+ vec4_t plane;
+
+ if ( !surf->verts ) {
+ surf->numVerts = 0;
+ surf->maxVerts = GROW_VERTS;
+ surf->verts = malloc( surf->maxVerts * sizeof( *surf->verts ) );
+
+ surf->numIndexes = 0;
+ surf->maxIndexes = GROW_INDICES;
+ surf->indexes = malloc( surf->maxIndexes * sizeof( *surf->indexes ) );
+ }
+
+ // calculate the texture coordinate vectors
+ xyplane = FindFloatPlane( xynorm, 0 );
+ QuakeTextureVecs( &mapplanes[ xyplane ], shift, 0, scale, vecs );
+
+ // emit the vertexes
+ numindices = 0;
+ maxindices = surf->maxIndexes;
+ assert( maxindices >= 0 );
+ indices = malloc ( maxindices * sizeof( *indices ) );
+
+ PlaneFromPoints( plane, verts[ 0 ]->xyz, verts[ 1 ]->xyz, verts[ 2 ]->xyz );
+
+ for ( i = 0; i < 3; i++ ) {
+ vert = &surf->verts[ surf->numVerts ];
+
+ if ( alpha[ i ] ) {
+ vert->color[3] = 255;
+ } else {
+ vert->color[3] = 0;
+ }
+
+ vert->xyz[ 0 ] = floor( verts[ i ]->xyz[ 0 ] + 0.1f );
+ vert->xyz[ 1 ] = floor( verts[ i ]->xyz[ 1 ] + 0.1f );
+ vert->xyz[ 2 ] = floor( verts[ i ]->xyz[ 2 ] + 0.1f );
+
+ // set the texture coordinates
+ vert->st[0] = ( vecs[0][3] + DotProduct( vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
+ vert->st[1] = ( vecs[1][3] + DotProduct( vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
+
+ VectorCopy( plane, vert->normal );
+
+ for( j = 0; j < surf->numVerts; j++ ) {
+ if ( CompareVert( vert, &surf->verts[ j ], qtrue ) ) {
+ break;
+ }
+ }
+
+ if ( numindices >= maxindices ) {
+ maxindices += GROW_INDICES;
+ indices = realloc( indices, maxindices * sizeof( *indices ) );
+ }
+
+ if ( j != surf->numVerts ) {
+ indices[ numindices++ ] = j;
+ } else {
+ indices[ numindices++ ] = surf->numVerts;
+ surf->numVerts++;
+ if ( surf->numVerts >= surf->maxVerts ) {
+ surf->maxVerts += GROW_VERTS;
+ surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
+ }
+ }
+ }
+
+ SideAsTristrip( surf, indices, numindices );
+
+ free( indices );
+}
+
+int MapPlaneFromPoints( vec3_t p0, vec3_t p1, vec3_t p2 );
+void QuakeTextureVecs( plane_t *plane, vec_t shift[2], vec_t rotate, vec_t scale[2], vec_t mappingVecs[2][4] );
+qboolean RemoveDuplicateBrushPlanes( bspbrush_t *b );
+void SetBrushContents( bspbrush_t *b );
+
+void AddBrushSide( vec3_t v1, vec3_t v2, vec3_t v3, shaderInfo_t *terrainShader ) {
+ side_t *side;
+ int planenum;
+
+ side = &buildBrush->sides[ buildBrush->numsides ];
+ memset( side, 0, sizeof( *side ) );
+ buildBrush->numsides++;
+
+ side->shaderInfo = terrainShader;
+
+ // find the plane number
+ planenum = MapPlaneFromPoints( v1, v2, v3 );
+ side->planenum = planenum;
+}
+
+void MakeBrushFromTriangle( vec3_t v1, vec3_t v2, vec3_t v3, shaderInfo_t *terrainShader ) {
+ bspbrush_t *b;
+ vec3_t d1;
+ vec3_t d2;
+ vec3_t d3;
+
+ VectorSet( d1, v1[ 0 ], v1[ 1 ], MIN_WORLD_COORD + 10 ); //FIXME
+ VectorSet( d2, v2[ 0 ], v2[ 1 ], MIN_WORLD_COORD + 10 );
+ VectorSet( d3, v3[ 0 ], v3[ 1 ], MIN_WORLD_COORD + 10 );
+
+ buildBrush->numsides = 0;
+ buildBrush->detail = qfalse;
+
+ AddBrushSide( v1, v2, v3, terrainShader );
+ AddBrushSide( v1, d1, v2, terrainShader );
+ AddBrushSide( v2, d2, v3, terrainShader );
+ AddBrushSide( v3, d3, v1, terrainShader );
+ AddBrushSide( d3, d2, d1, terrainShader );
+
+ buildBrush->portalareas[0] = -1;
+ buildBrush->portalareas[1] = -1;
+ buildBrush->entitynum = num_entities-1;
+ buildBrush->brushnum = entitySourceBrushes;
+
+ // if there are mirrored planes, the entire brush is invalid
+ if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
+ return;
+ }
+
+ // get the content for the entire brush
+ SetBrushContents( buildBrush );
+ buildBrush->contents |= CONTENTS_DETAIL;
+
+ b = FinishBrush();
+ if ( !b ) {
+ return;
+ }
+}
+
+void MakeTerrainIntoBrushes( terrainMesh_t *tm ) {
+ int index[ 6 ];
+ int y;
+ int x;
+ terrainVert_t *verts;
+ shaderInfo_t *terrainShader;
+
+ terrainShader = ShaderInfoForShader( "textures/common/terrain" );
+
+ verts = tm->map;
+ for( y = 0; y < tm->height - 1; y++ ) {
+ for( x = 0; x < tm->width - 1; x++ ) {
+ if ( ( x + y ) & 1 ) {
+ // first tri
+ index[ 0 ] = x + y * tm->width;
+ index[ 1 ] = x + ( y + 1 ) * tm->width;
+ index[ 2 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
+ index[ 3 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
+ index[ 4 ] = ( x + 1 ) + y * tm->width;
+ index[ 5 ] = x + y * tm->width;
+ } else {
+ // first tri
+ index[ 0 ] = x + y * tm->width;
+ index[ 1 ] = x + ( y + 1 ) * tm->width;
+ index[ 2 ] = ( x + 1 ) + y * tm->width;
+ index[ 3 ] = ( x + 1 ) + y * tm->width;
+ index[ 4 ] = x + ( y + 1 ) * tm->width;
+ index[ 5 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
+ }
+
+ MakeBrushFromTriangle( verts[ index[ 0 ] ].xyz, verts[ index[ 1 ] ].xyz, verts[ index[ 2 ] ].xyz, terrainShader );
+ MakeBrushFromTriangle( verts[ index[ 3 ] ].xyz, verts[ index[ 4 ] ].xyz, verts[ index[ 5 ] ].xyz, terrainShader );
+ }
+ }
+}
+
+void Terrain_ParseFace( terrainFace_t *face ) {
+ shaderInfo_t *si;
+ vec_t shift[ 2 ];
+ vec_t rotate;
+ vec_t scale[ 2 ];
+ char name[ MAX_QPATH ];
+ char shader[ MAX_QPATH ];
+ plane_t p;
+
+ // read the texturedef
+ GetToken( qfalse );
+ strcpy( name, token );
+
+ GetToken( qfalse );
+ shift[ 0 ] = atof(token);
+ GetToken( qfalse );
+ shift[ 1 ] = atof( token );
+ GetToken( qfalse );
+ rotate = atof( token );
+ GetToken( qfalse );
+ scale[ 0 ] = atof( token );
+ GetToken( qfalse );
+ scale[ 1 ] = atof( token );
+
+ // find default flags and values
+ sprintf( shader, "textures/%s", name );
+ si = ShaderInfoForShader( shader );
+ face->shaderInfo = si;
+ //face->texdef = si->texdef;
+
+ // skip over old contents
+ GetToken( qfalse );
+
+ // skip over old flags
+ GetToken( qfalse );
+
+ // skip over old value
+ GetToken( qfalse );
+
+ //Surface_Parse( &face->texdef );
+ //Surface_BuildTexdef( &face->texdef );
+
+ // make a fake horizontal plane
+ VectorSet( p.normal, 0, 0, 1 );
+ p.dist = 0;
+ p.type = PlaneTypeForNormal( p.normal );
+
+ QuakeTextureVecs( &p, shift, rotate, scale, face->vecs );
+}
+
+#define MAX_TERRAIN_TEXTURES 128
+static int numtextures = 0;;
+static shaderInfo_t *textures[ MAX_TERRAIN_TEXTURES ];
+
+void Terrain_AddTexture( shaderInfo_t *texture ) {
+ int i;
+
+ if ( !texture ) {
+ return;
+ }
+
+ for( i = 0; i < numtextures; i++ ) {
+ if ( textures[ i ] == texture ) {
+ return;
+ }
+ }
+
+ if ( numtextures >= MAX_TERRAIN_TEXTURES ) {
+ Error( "Too many textures on terrain" );
+ return;
+ }
+
+ textures[ numtextures++ ] = texture;
+}
+
+int LayerForShader( shaderInfo_t *shader ) {
+ int i;
+ int l;
+
+ l = strlen( shader->shader );
+ for( i = l - 1; i >= 0; i-- ) {
+ if ( shader->shader[ i ] == '_' ) {
+ return atoi( &shader->shader[ i + 1 ] );
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+=================
+ParseTerrain
+
+Creates a mapDrawSurface_t from the terrain text
+=================
+*/
+
+void ParseTerrain( void ) {
+ int i, j;
+ int x, y;
+ int x1, y1;
+ terrainMesh_t t;
+ int index;
+ terrainVert_t *verts[ 6 ];
+ int num_layers;
+ int layer, minlayer, maxlayer;
+ int alpha[ 6 ];
+ shaderInfo_t *si, *terrainShader;
+ int surfwidth, surfheight, surfsize;
+ terrainSurf_t *surf;
+ char shadername[ MAX_QPATH ];
+
+ mapent->firstDrawSurf = numMapDrawSurfs;
+
+ memset( &t, 0, sizeof( t ) );
+
+ MatchToken( "{" );
+
+ // get width
+ GetToken( qtrue );
+ t.width = atoi( token );
+
+ // get height
+ GetToken( qfalse );
+ t.height = atoi( token );
+
+ // get scale_x
+ GetToken( qfalse );
+ t.scale_x = atof( token );
+
+ // get scale_y
+ GetToken( qfalse );
+ t.scale_y = atof( token );
+
+ // get origin
+ GetToken( qtrue );
+ t.origin[ 0 ] = atof( token );
+ GetToken( qfalse );
+ t.origin[ 1 ] = atof( token );
+ GetToken( qfalse );
+ t.origin[ 2 ] = atof( token );
+
+ t.map = malloc( t.width * t.height * sizeof( t.map[ 0 ] ) );
+
+ if ( t.width <= 0 || t.height <= 0 ) {
+ Error( "ParseTerrain: bad size" );
+ }
+
+ numtextures = 0;
+ index = 0;
+ for ( i = 0; i < t.height; i++ ) {
+ for( j = 0; j < t.width; j++, index++ ) {
+ // get height
+ GetToken( qtrue );
+ t.map[ index ].xyz[ 0 ] = t.origin[ 0 ] + t.scale_x * ( float )j;
+ t.map[ index ].xyz[ 1 ] = t.origin[ 1 ] + t.scale_y * ( float )i;
+ t.map[ index ].xyz[ 2 ] = t.origin[ 2 ] + atof( token );
+
+ Terrain_ParseFace( &t.map[ index ].tri );
+ Terrain_AddTexture( t.map[ index ].tri.shaderInfo );
+ }
+ }
+
+ MatchToken( "}" );
+ MatchToken( "}" );
+
+ MakeTerrainIntoBrushes( &t );
+
+ surfwidth = ( ( t.scale_x * t.width ) + SURF_WIDTH - 1 ) / SURF_WIDTH;
+ surfheight = ( ( t.scale_y * t.height ) + SURF_HEIGHT - 1 ) / SURF_HEIGHT;
+ surfsize = surfwidth * surfheight;
+
+ //FIXME
+ num_layers = 0;
+ for( i = 0; i < numtextures; i++ ) {
+ layer = LayerForShader( textures[ i ] ) + 1;
+ if ( layer > num_layers ) {
+ num_layers = layer;
+ }
+ }
+ num_layers = 4;
+
+ memset( alpha, 0, sizeof( alpha ) );
+
+ lastSurface = NULL;
+ numsurfaces = 0;
+ maxsurfaces = 0;
+ for( i = num_layers; i > 0; i-- ) {
+ maxsurfaces += i * surfsize;
+ }
+
+ surfaces = malloc( maxsurfaces * sizeof( *surfaces ) );
+ memset( surfaces, 0, maxsurfaces * sizeof( *surfaces ) );
+
+ terrainShader = ShaderInfoForShader( "textures/common/terrain" );
+
+ // get the shadername
+ if ( Q_strncasecmp( textures[ 0 ]->shader, "textures/", 9 ) == 0 ) {
+ strcpy( shadername, &textures[ 0 ]->shader[ 9 ] );
+ } else {
+ strcpy( shadername, textures[ 0 ]->shader );
+ }
+ j = strlen( shadername );
+ for( i = j - 1; i >= 0; i-- ) {
+ if ( shadername[ i ] == '_' ) {
+ shadername[ i ] = 0;
+ break;
+ }
+ }
+
+ for( y = 0; y < t.height - 1; y++ ) {
+ for( x = 0; x < t.width - 1; x++ ) {
+ Terrain_GetTriangles( &t, x, y, verts );
+
+ x1 = ( ( float )x / ( float )( t.width - 1 ) ) * surfwidth;
+ if ( x1 >= surfwidth ) {
+ x1 = surfwidth - 1;
+ }
+
+ y1 = ( ( float )y / ( float )( t.height - 1 ) ) * surfheight;
+ if ( y1 >= surfheight ) {
+ y1 = surfheight - 1;
+ }
+
+ maxlayer = minlayer = LayerForShader( verts[ 0 ]->tri.shaderInfo );
+ for( i = 0; i < 3; i++ ) {
+ layer = LayerForShader( verts[ i ]->tri.shaderInfo );
+ if ( layer < minlayer ) {
+ minlayer = layer;
+ }
+ if ( layer > maxlayer ) {
+ maxlayer = layer;
+ }
+ }
+
+ for( i = 0; i < 3; i++ ) {
+ layer = LayerForShader( verts[ i ]->tri.shaderInfo );
+ if ( layer > minlayer ) {
+ alpha[ i ] = 1.0f;
+ } else {
+ alpha[ i ] = 0.0f;
+ }
+ }
+
+ si = ShaderForLayer( minlayer, maxlayer, shadername );
+ surf = SurfaceForShader( si, x1, y1 );
+ EmitTerrainVerts2( surf, &verts[ 0 ], &alpha[ 0 ] );
+
+ // second triangle
+ maxlayer = minlayer = LayerForShader( verts[ 3 ]->tri.shaderInfo );
+ for( i = 3; i < 6; i++ ) {
+ layer = LayerForShader( verts[ i ]->tri.shaderInfo );
+ if ( layer < minlayer ) {
+ minlayer = layer;
+ }
+ if ( layer > maxlayer ) {
+ maxlayer = layer;
+ }
+ }
+
+ for( i = 3; i < 6; i++ ) {
+ layer = LayerForShader( verts[ i ]->tri.shaderInfo );
+ if ( layer > minlayer ) {
+ alpha[ i ] = 1.0f;
+ } else {
+ alpha[ i ] = 0.0f;
+ }
+ }
+
+ si = ShaderForLayer( minlayer, maxlayer, shadername );
+ surf = SurfaceForShader( si, x1, y1 );
+ EmitTerrainVerts2( surf, &verts[ 3 ], &alpha[ 3 ] );
+ }
+ }
+
+ // create the final surfaces
+ for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
+ if ( surf->numVerts ) {
+ CreateTerrainSurface( surf, surf->shader );
+ }
+ }
+
+ //
+ // clean up any allocated memory
+ //
+ for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
+ if ( surf->verts ) {
+ free( surf->verts );
+ free( surf->indexes );
+ }
+ }
+ free( surfaces );
+
+ surfaces = NULL;
+ lastSurface = NULL;
+ numsurfaces = 0;
+ maxsurfaces = 0;
+
+ free( t.map );
+}
+