diff options
Diffstat (limited to 'q3map/terrain.c')
-rwxr-xr-x | q3map/terrain.c | 2468 |
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 ); +} + |