aboutsummaryrefslogtreecommitdiffstats
path: root/q3map/terrain.c
diff options
context:
space:
mode:
Diffstat (limited to 'q3map/terrain.c')
-rwxr-xr-xq3map/terrain.c1255
1 files changed, 1255 insertions, 0 deletions
diff --git a/q3map/terrain.c b/q3map/terrain.c
new file mode 100755
index 0000000..9446737
--- /dev/null
+++ b/q3map/terrain.c
@@ -0,0 +1,1255 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+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 );
+}
+