Show terrain.c syntax highlighted
/*
===========================================================================
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), (unsigned **)&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 = 0;
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 );
}
See more files for this project here