📄 terrain.c
字号:
#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
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();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -