diff options
| author | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 | 
|---|---|---|
| committer | zakk <zakk@edf5b092-35ff-0310-97b2-ce42778d08ea> | 2005-08-26 17:39:27 +0000 | 
| commit | 6bf20c78f5b69d40bcc4931df93d29198435ab67 (patch) | |
| tree | e3eda937a05d7db42de725b7013bd0344b987f34 /code/renderer/tr_bsp.c | |
| parent | 872d4d7f55af706737ffb361bb76ad13e7496770 (diff) | |
| download | ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.tar.gz ioquake3-aero-6bf20c78f5b69d40bcc4931df93d29198435ab67.zip | |
newlines fixed
git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea
Diffstat (limited to 'code/renderer/tr_bsp.c')
| -rwxr-xr-x | code/renderer/tr_bsp.c | 3724 | 
1 files changed, 1862 insertions, 1862 deletions
| diff --git a/code/renderer/tr_bsp.c b/code/renderer/tr_bsp.c index 5aefb24..1f5db42 100755 --- a/code/renderer/tr_bsp.c +++ b/code/renderer/tr_bsp.c @@ -1,1862 +1,1862 @@ -/*
 -===========================================================================
 -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
 -===========================================================================
 -*/
 -// tr_map.c
 -
 -#include "tr_local.h"
 -
 -/*
 -
 -Loads and prepares a map file for scene rendering.
 -
 -A single entry point:
 -
 -void RE_LoadWorldMap( const char *name );
 -
 -*/
 -
 -static	world_t		s_worldData;
 -static	byte		*fileBase;
 -
 -int			c_subdivisions;
 -int			c_gridVerts;
 -
 -//===============================================================================
 -
 -static void HSVtoRGB( float h, float s, float v, float rgb[3] )
 -{
 -	int i;
 -	float f;
 -	float p, q, t;
 -
 -	h *= 5;
 -
 -	i = floor( h );
 -	f = h - i;
 -
 -	p = v * ( 1 - s );
 -	q = v * ( 1 - s * f );
 -	t = v * ( 1 - s * ( 1 - f ) );
 -
 -	switch ( i )
 -	{
 -	case 0:
 -		rgb[0] = v;
 -		rgb[1] = t;
 -		rgb[2] = p;
 -		break;
 -	case 1:
 -		rgb[0] = q;
 -		rgb[1] = v;
 -		rgb[2] = p;
 -		break;
 -	case 2:
 -		rgb[0] = p;
 -		rgb[1] = v;
 -		rgb[2] = t;
 -		break;
 -	case 3:
 -		rgb[0] = p;
 -		rgb[1] = q;
 -		rgb[2] = v;
 -		break;
 -	case 4:
 -		rgb[0] = t;
 -		rgb[1] = p;
 -		rgb[2] = v;
 -		break;
 -	case 5:
 -		rgb[0] = v;
 -		rgb[1] = p;
 -		rgb[2] = q;
 -		break;
 -	}
 -}
 -
 -/*
 -===============
 -R_ColorShiftLightingBytes
 -
 -===============
 -*/
 -static	void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) {
 -	int		shift, r, g, b;
 -
 -	// shift the color data based on overbright range
 -	shift = r_mapOverBrightBits->integer - tr.overbrightBits;
 -
 -	// shift the data based on overbright range
 -	r = in[0] << shift;
 -	g = in[1] << shift;
 -	b = in[2] << shift;
 -	
 -	// normalize by color instead of saturating to white
 -	if ( ( r | g | b ) > 255 ) {
 -		int		max;
 -
 -		max = r > g ? r : g;
 -		max = max > b ? max : b;
 -		r = r * 255 / max;
 -		g = g * 255 / max;
 -		b = b * 255 / max;
 -	}
 -
 -	out[0] = r;
 -	out[1] = g;
 -	out[2] = b;
 -	out[3] = in[3];
 -}
 -
 -/*
 -===============
 -R_LoadLightmaps
 -
 -===============
 -*/
 -#define	LIGHTMAP_SIZE	128
 -static	void R_LoadLightmaps( lump_t *l ) {
 -	byte		*buf, *buf_p;
 -	int			len;
 -	MAC_STATIC byte		image[LIGHTMAP_SIZE*LIGHTMAP_SIZE*4];
 -	int			i, j;
 -	float maxIntensity = 0;
 -	double sumIntensity = 0;
 -
 -    len = l->filelen;
 -	if ( !len ) {
 -		return;
 -	}
 -	buf = fileBase + l->fileofs;
 -
 -	// we are about to upload textures
 -	R_SyncRenderThread();
 -
 -	// create all the lightmaps
 -	tr.numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3);
 -	if ( tr.numLightmaps == 1 ) {
 -		//FIXME: HACK: maps with only one lightmap turn up fullbright for some reason.
 -		//this avoids this, but isn't the correct solution.
 -		tr.numLightmaps++;
 -	}
 -
 -	// if we are in r_vertexLight mode, we don't need the lightmaps at all
 -	if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) {
 -		return;
 -	}
 -
 -	for ( i = 0 ; i < tr.numLightmaps ; i++ ) {
 -		// expand the 24 bit on-disk to 32 bit
 -		buf_p = buf + i * LIGHTMAP_SIZE*LIGHTMAP_SIZE * 3;
 -
 -		if ( r_lightmap->integer == 2 )
 -		{	// color code by intensity as development tool	(FIXME: check range)
 -			for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ )
 -			{
 -				float r = buf_p[j*3+0];
 -				float g = buf_p[j*3+1];
 -				float b = buf_p[j*3+2];
 -				float intensity;
 -				float out[3];
 -
 -				intensity = 0.33f * r + 0.685f * g + 0.063f * b;
 -
 -				if ( intensity > 255 )
 -					intensity = 1.0f;
 -				else
 -					intensity /= 255.0f;
 -
 -				if ( intensity > maxIntensity )
 -					maxIntensity = intensity;
 -
 -				HSVtoRGB( intensity, 1.00, 0.50, out );
 -
 -				image[j*4+0] = out[0] * 255;
 -				image[j*4+1] = out[1] * 255;
 -				image[j*4+2] = out[2] * 255;
 -				image[j*4+3] = 255;
 -
 -				sumIntensity += intensity;
 -			}
 -		} else {
 -			for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) {
 -				R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] );
 -				image[j*4+3] = 255;
 -			}
 -		}
 -		tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, 
 -			LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP );
 -	}
 -
 -	if ( r_lightmap->integer == 2 )	{
 -		ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) );
 -	}
 -}
 -
 -
 -/*
 -=================
 -RE_SetWorldVisData
 -
 -This is called by the clipmodel subsystem so we can share the 1.8 megs of
 -space in big maps...
 -=================
 -*/
 -void		RE_SetWorldVisData( const byte *vis ) {
 -	tr.externalVisData = vis;
 -}
 -
 -
 -/*
 -=================
 -R_LoadVisibility
 -=================
 -*/
 -static	void R_LoadVisibility( lump_t *l ) {
 -	int		len;
 -	byte	*buf;
 -
 -	len = ( s_worldData.numClusters + 63 ) & ~63;
 -	s_worldData.novis = ri.Hunk_Alloc( len, h_low );
 -	Com_Memset( s_worldData.novis, 0xff, len );
 -
 -    len = l->filelen;
 -	if ( !len ) {
 -		return;
 -	}
 -	buf = fileBase + l->fileofs;
 -
 -	s_worldData.numClusters = LittleLong( ((int *)buf)[0] );
 -	s_worldData.clusterBytes = LittleLong( ((int *)buf)[1] );
 -
 -	// CM_Load should have given us the vis data to share, so
 -	// we don't need to allocate another copy
 -	if ( tr.externalVisData ) {
 -		s_worldData.vis = tr.externalVisData;
 -	} else {
 -		byte	*dest;
 -
 -		dest = ri.Hunk_Alloc( len - 8, h_low );
 -		Com_Memcpy( dest, buf + 8, len - 8 );
 -		s_worldData.vis = dest;
 -	}
 -}
 -
 -//===============================================================================
 -
 -
 -/*
 -===============
 -ShaderForShaderNum
 -===============
 -*/
 -static shader_t *ShaderForShaderNum( int shaderNum, int lightmapNum ) {
 -	shader_t	*shader;
 -	dshader_t	*dsh;
 -
 -	shaderNum = LittleLong( shaderNum );
 -	if ( shaderNum < 0 || shaderNum >= s_worldData.numShaders ) {
 -		ri.Error( ERR_DROP, "ShaderForShaderNum: bad num %i", shaderNum );
 -	}
 -	dsh = &s_worldData.shaders[ shaderNum ];
 -
 -	if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) {
 -		lightmapNum = LIGHTMAP_BY_VERTEX;
 -	}
 -
 -	if ( r_fullbright->integer ) {
 -		lightmapNum = LIGHTMAP_WHITEIMAGE;
 -	}
 -
 -	shader = R_FindShader( dsh->shader, lightmapNum, qtrue );
 -
 -	// if the shader had errors, just use default shader
 -	if ( shader->defaultShader ) {
 -		return tr.defaultShader;
 -	}
 -
 -	return shader;
 -}
 -
 -/*
 -===============
 -ParseFace
 -===============
 -*/
 -static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes  ) {
 -	int			i, j;
 -	srfSurfaceFace_t	*cv;
 -	int			numPoints, numIndexes;
 -	int			lightmapNum;
 -	int			sfaceSize, ofsIndexes;
 -
 -	lightmapNum = LittleLong( ds->lightmapNum );
 -
 -	// get fog volume
 -	surf->fogIndex = LittleLong( ds->fogNum ) + 1;
 -
 -	// get shader value
 -	surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum );
 -	if ( r_singleShader->integer && !surf->shader->isSky ) {
 -		surf->shader = tr.defaultShader;
 -	}
 -
 -	numPoints = LittleLong( ds->numVerts );
 -	if (numPoints > MAX_FACE_POINTS) {
 -		ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numPoints);
 -    numPoints = MAX_FACE_POINTS;
 -    surf->shader = tr.defaultShader;
 -	}
 -
 -	numIndexes = LittleLong( ds->numIndexes );
 -
 -	// create the srfSurfaceFace_t
 -	sfaceSize = ( int ) &((srfSurfaceFace_t *)0)->points[numPoints];
 -	ofsIndexes = sfaceSize;
 -	sfaceSize += sizeof( int ) * numIndexes;
 -
 -	cv = ri.Hunk_Alloc( sfaceSize, h_low );
 -	cv->surfaceType = SF_FACE;
 -	cv->numPoints = numPoints;
 -	cv->numIndices = numIndexes;
 -	cv->ofsIndices = ofsIndexes;
 -
 -	verts += LittleLong( ds->firstVert );
 -	for ( i = 0 ; i < numPoints ; i++ ) {
 -		for ( j = 0 ; j < 3 ; j++ ) {
 -			cv->points[i][j] = LittleFloat( verts[i].xyz[j] );
 -		}
 -		for ( j = 0 ; j < 2 ; j++ ) {
 -			cv->points[i][3+j] = LittleFloat( verts[i].st[j] );
 -			cv->points[i][5+j] = LittleFloat( verts[i].lightmap[j] );
 -		}
 -		R_ColorShiftLightingBytes( verts[i].color, (byte *)&cv->points[i][7] );
 -	}
 -
 -	indexes += LittleLong( ds->firstIndex );
 -	for ( i = 0 ; i < numIndexes ; i++ ) {
 -		((int *)((byte *)cv + cv->ofsIndices ))[i] = LittleLong( indexes[ i ] );
 -	}
 -
 -	// take the plane information from the lightmap vector
 -	for ( i = 0 ; i < 3 ; i++ ) {
 -		cv->plane.normal[i] = LittleFloat( ds->lightmapVecs[2][i] );
 -	}
 -	cv->plane.dist = DotProduct( cv->points[0], cv->plane.normal );
 -	SetPlaneSignbits( &cv->plane );
 -	cv->plane.type = PlaneTypeForNormal( cv->plane.normal );
 -
 -	surf->data = (surfaceType_t *)cv;
 -}
 -
 -
 -/*
 -===============
 -ParseMesh
 -===============
 -*/
 -static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, msurface_t *surf ) {
 -	srfGridMesh_t	*grid;
 -	int				i, j;
 -	int				width, height, numPoints;
 -	MAC_STATIC drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE];
 -	int				lightmapNum;
 -	vec3_t			bounds[2];
 -	vec3_t			tmpVec;
 -	static surfaceType_t	skipData = SF_SKIP;
 -
 -	lightmapNum = LittleLong( ds->lightmapNum );
 -
 -	// get fog volume
 -	surf->fogIndex = LittleLong( ds->fogNum ) + 1;
 -
 -	// get shader value
 -	surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum );
 -	if ( r_singleShader->integer && !surf->shader->isSky ) {
 -		surf->shader = tr.defaultShader;
 -	}
 -
 -	// we may have a nodraw surface, because they might still need to
 -	// be around for movement clipping
 -	if ( s_worldData.shaders[ LittleLong( ds->shaderNum ) ].surfaceFlags & SURF_NODRAW ) {
 -		surf->data = &skipData;
 -		return;
 -	}
 -
 -	width = LittleLong( ds->patchWidth );
 -	height = LittleLong( ds->patchHeight );
 -
 -	verts += LittleLong( ds->firstVert );
 -	numPoints = width * height;
 -	for ( i = 0 ; i < numPoints ; i++ ) {
 -		for ( j = 0 ; j < 3 ; j++ ) {
 -			points[i].xyz[j] = LittleFloat( verts[i].xyz[j] );
 -			points[i].normal[j] = LittleFloat( verts[i].normal[j] );
 -		}
 -		for ( j = 0 ; j < 2 ; j++ ) {
 -			points[i].st[j] = LittleFloat( verts[i].st[j] );
 -			points[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] );
 -		}
 -		R_ColorShiftLightingBytes( verts[i].color, points[i].color );
 -	}
 -
 -	// pre-tesseleate
 -	grid = R_SubdividePatchToGrid( width, height, points );
 -	surf->data = (surfaceType_t *)grid;
 -
 -	// copy the level of detail origin, which is the center
 -	// of the group of all curves that must subdivide the same
 -	// to avoid cracking
 -	for ( i = 0 ; i < 3 ; i++ ) {
 -		bounds[0][i] = LittleFloat( ds->lightmapVecs[0][i] );
 -		bounds[1][i] = LittleFloat( ds->lightmapVecs[1][i] );
 -	}
 -	VectorAdd( bounds[0], bounds[1], bounds[1] );
 -	VectorScale( bounds[1], 0.5f, grid->lodOrigin );
 -	VectorSubtract( bounds[0], grid->lodOrigin, tmpVec );
 -	grid->lodRadius = VectorLength( tmpVec );
 -}
 -
 -/*
 -===============
 -ParseTriSurf
 -===============
 -*/
 -static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) {
 -	srfTriangles_t	*tri;
 -	int				i, j;
 -	int				numVerts, numIndexes;
 -
 -	// get fog volume
 -	surf->fogIndex = LittleLong( ds->fogNum ) + 1;
 -
 -	// get shader
 -	surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX );
 -	if ( r_singleShader->integer && !surf->shader->isSky ) {
 -		surf->shader = tr.defaultShader;
 -	}
 -
 -	numVerts = LittleLong( ds->numVerts );
 -	numIndexes = LittleLong( ds->numIndexes );
 -
 -	tri = ri.Hunk_Alloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) 
 -		+ numIndexes * sizeof( tri->indexes[0] ), h_low );
 -	tri->surfaceType = SF_TRIANGLES;
 -	tri->numVerts = numVerts;
 -	tri->numIndexes = numIndexes;
 -	tri->verts = (drawVert_t *)(tri + 1);
 -	tri->indexes = (int *)(tri->verts + tri->numVerts );
 -
 -	surf->data = (surfaceType_t *)tri;
 -
 -	// copy vertexes
 -	ClearBounds( tri->bounds[0], tri->bounds[1] );
 -	verts += LittleLong( ds->firstVert );
 -	for ( i = 0 ; i < numVerts ; i++ ) {
 -		for ( j = 0 ; j < 3 ; j++ ) {
 -			tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] );
 -			tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] );
 -		}
 -		AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] );
 -		for ( j = 0 ; j < 2 ; j++ ) {
 -			tri->verts[i].st[j] = LittleFloat( verts[i].st[j] );
 -			tri->verts[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] );
 -		}
 -
 -		R_ColorShiftLightingBytes( verts[i].color, tri->verts[i].color );
 -	}
 -
 -	// copy indexes
 -	indexes += LittleLong( ds->firstIndex );
 -	for ( i = 0 ; i < numIndexes ; i++ ) {
 -		tri->indexes[i] = LittleLong( indexes[i] );
 -		if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) {
 -			ri.Error( ERR_DROP, "Bad index in triangle surface" );
 -		}
 -	}
 -}
 -
 -/*
 -===============
 -ParseFlare
 -===============
 -*/
 -static void ParseFlare( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) {
 -	srfFlare_t		*flare;
 -	int				i;
 -
 -	// get fog volume
 -	surf->fogIndex = LittleLong( ds->fogNum ) + 1;
 -
 -	// get shader
 -	surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX );
 -	if ( r_singleShader->integer && !surf->shader->isSky ) {
 -		surf->shader = tr.defaultShader;
 -	}
 -
 -	flare = ri.Hunk_Alloc( sizeof( *flare ), h_low );
 -	flare->surfaceType = SF_FLARE;
 -
 -	surf->data = (surfaceType_t *)flare;
 -
 -	for ( i = 0 ; i < 3 ; i++ ) {
 -		flare->origin[i] = LittleFloat( ds->lightmapOrigin[i] );
 -		flare->color[i] = LittleFloat( ds->lightmapVecs[0][i] );
 -		flare->normal[i] = LittleFloat( ds->lightmapVecs[2][i] );
 -	}
 -}
 -
 -
 -/*
 -=================
 -R_MergedWidthPoints
 -
 -returns true if there are grid points merged on a width edge
 -=================
 -*/
 -int R_MergedWidthPoints(srfGridMesh_t *grid, int offset) {
 -	int i, j;
 -
 -	for (i = 1; i < grid->width-1; i++) {
 -		for (j = i + 1; j < grid->width-1; j++) {
 -			if ( fabs(grid->verts[i + offset].xyz[0] - grid->verts[j + offset].xyz[0]) > .1) continue;
 -			if ( fabs(grid->verts[i + offset].xyz[1] - grid->verts[j + offset].xyz[1]) > .1) continue;
 -			if ( fabs(grid->verts[i + offset].xyz[2] - grid->verts[j + offset].xyz[2]) > .1) continue;
 -			return qtrue;
 -		}
 -	}
 -	return qfalse;
 -}
 -
 -/*
 -=================
 -R_MergedHeightPoints
 -
 -returns true if there are grid points merged on a height edge
 -=================
 -*/
 -int R_MergedHeightPoints(srfGridMesh_t *grid, int offset) {
 -	int i, j;
 -
 -	for (i = 1; i < grid->height-1; i++) {
 -		for (j = i + 1; j < grid->height-1; j++) {
 -			if ( fabs(grid->verts[grid->width * i + offset].xyz[0] - grid->verts[grid->width * j + offset].xyz[0]) > .1) continue;
 -			if ( fabs(grid->verts[grid->width * i + offset].xyz[1] - grid->verts[grid->width * j + offset].xyz[1]) > .1) continue;
 -			if ( fabs(grid->verts[grid->width * i + offset].xyz[2] - grid->verts[grid->width * j + offset].xyz[2]) > .1) continue;
 -			return qtrue;
 -		}
 -	}
 -	return qfalse;
 -}
 -
 -/*
 -=================
 -R_FixSharedVertexLodError_r
 -
 -NOTE: never sync LoD through grid edges with merged points!
 -
 -FIXME: write generalized version that also avoids cracks between a patch and one that meets half way?
 -=================
 -*/
 -void R_FixSharedVertexLodError_r( int start, srfGridMesh_t *grid1 ) {
 -	int j, k, l, m, n, offset1, offset2, touch;
 -	srfGridMesh_t *grid2;
 -
 -	for ( j = start; j < s_worldData.numsurfaces; j++ ) {
 -		//
 -		grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data;
 -		// if this surface is not a grid
 -		if ( grid2->surfaceType != SF_GRID ) continue;
 -		// if the LOD errors are already fixed for this patch
 -		if ( grid2->lodFixed == 2 ) continue;
 -		// grids in the same LOD group should have the exact same lod radius
 -		if ( grid1->lodRadius != grid2->lodRadius ) continue;
 -		// grids in the same LOD group should have the exact same lod origin
 -		if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue;
 -		if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue;
 -		if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue;
 -		//
 -		touch = qfalse;
 -		for (n = 0; n < 2; n++) {
 -			//
 -			if (n) offset1 = (grid1->height-1) * grid1->width;
 -			else offset1 = 0;
 -			if (R_MergedWidthPoints(grid1, offset1)) continue;
 -			for (k = 1; k < grid1->width-1; k++) {
 -				for (m = 0; m < 2; m++) {
 -
 -					if (m) offset2 = (grid2->height-1) * grid2->width;
 -					else offset2 = 0;
 -					if (R_MergedWidthPoints(grid2, offset2)) continue;
 -					for ( l = 1; l < grid2->width-1; l++) {
 -					//
 -						if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue;
 -						if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue;
 -						if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue;
 -						// ok the points are equal and should have the same lod error
 -						grid2->widthLodError[l] = grid1->widthLodError[k];
 -						touch = qtrue;
 -					}
 -				}
 -				for (m = 0; m < 2; m++) {
 -
 -					if (m) offset2 = grid2->width-1;
 -					else offset2 = 0;
 -					if (R_MergedHeightPoints(grid2, offset2)) continue;
 -					for ( l = 1; l < grid2->height-1; l++) {
 -					//
 -						if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue;
 -						if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue;
 -						if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue;
 -						// ok the points are equal and should have the same lod error
 -						grid2->heightLodError[l] = grid1->widthLodError[k];
 -						touch = qtrue;
 -					}
 -				}
 -			}
 -		}
 -		for (n = 0; n < 2; n++) {
 -			//
 -			if (n) offset1 = grid1->width-1;
 -			else offset1 = 0;
 -			if (R_MergedHeightPoints(grid1, offset1)) continue;
 -			for (k = 1; k < grid1->height-1; k++) {
 -				for (m = 0; m < 2; m++) {
 -
 -					if (m) offset2 = (grid2->height-1) * grid2->width;
 -					else offset2 = 0;
 -					if (R_MergedWidthPoints(grid2, offset2)) continue;
 -					for ( l = 1; l < grid2->width-1; l++) {
 -					//
 -						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue;
 -						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue;
 -						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue;
 -						// ok the points are equal and should have the same lod error
 -						grid2->widthLodError[l] = grid1->heightLodError[k];
 -						touch = qtrue;
 -					}
 -				}
 -				for (m = 0; m < 2; m++) {
 -
 -					if (m) offset2 = grid2->width-1;
 -					else offset2 = 0;
 -					if (R_MergedHeightPoints(grid2, offset2)) continue;
 -					for ( l = 1; l < grid2->height-1; l++) {
 -					//
 -						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue;
 -						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue;
 -						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue;
 -						// ok the points are equal and should have the same lod error
 -						grid2->heightLodError[l] = grid1->heightLodError[k];
 -						touch = qtrue;
 -					}
 -				}
 -			}
 -		}
 -		if (touch) {
 -			grid2->lodFixed = 2;
 -			R_FixSharedVertexLodError_r ( start, grid2 );
 -			//NOTE: this would be correct but makes things really slow
 -			//grid2->lodFixed = 1;
 -		}
 -	}
 -}
 -
 -/*
 -=================
 -R_FixSharedVertexLodError
 -
 -This function assumes that all patches in one group are nicely stitched together for the highest LoD.
 -If this is not the case this function will still do its job but won't fix the highest LoD cracks.
 -=================
 -*/
 -void R_FixSharedVertexLodError( void ) {
 -	int i;
 -	srfGridMesh_t *grid1;
 -
 -	for ( i = 0; i < s_worldData.numsurfaces; i++ ) {
 -		//
 -		grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data;
 -		// if this surface is not a grid
 -		if ( grid1->surfaceType != SF_GRID )
 -			continue;
 -		//
 -		if ( grid1->lodFixed )
 -			continue;
 -		//
 -		grid1->lodFixed = 2;
 -		// recursively fix other patches in the same LOD group
 -		R_FixSharedVertexLodError_r( i + 1, grid1);
 -	}
 -}
 -
 -
 -/*
 -===============
 -R_StitchPatches
 -===============
 -*/
 -int R_StitchPatches( int grid1num, int grid2num ) {
 -	float *v1, *v2;
 -	srfGridMesh_t *grid1, *grid2;
 -	int k, l, m, n, offset1, offset2, row, column;
 -
 -	grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data;
 -	grid2 = (srfGridMesh_t *) s_worldData.surfaces[grid2num].data;
 -	for (n = 0; n < 2; n++) {
 -		//
 -		if (n) offset1 = (grid1->height-1) * grid1->width;
 -		else offset1 = 0;
 -		if (R_MergedWidthPoints(grid1, offset1))
 -			continue;
 -		for (k = 0; k < grid1->width-2; k += 2) {
 -
 -			for (m = 0; m < 2; m++) {
 -
 -				if ( grid2->width >= MAX_GRID_SIZE )
 -					break;
 -				if (m) offset2 = (grid2->height-1) * grid2->width;
 -				else offset2 = 0;
 -				for ( l = 0; l < grid2->width-1; l++) {
 -				//
 -					v1 = grid1->verts[k + offset1].xyz;
 -					v2 = grid2->verts[l + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -
 -					v1 = grid1->verts[k + 2 + offset1].xyz;
 -					v2 = grid2->verts[l + 1 + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -					//
 -					v1 = grid2->verts[l + offset2].xyz;
 -					v2 = grid2->verts[l + 1 + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) < .01 &&
 -							fabs(v1[1] - v2[1]) < .01 &&
 -							fabs(v1[2] - v2[2]) < .01)
 -						continue;
 -					//
 -					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
 -					// insert column into grid2 right after after column l
 -					if (m) row = grid2->height-1;
 -					else row = 0;
 -					grid2 = R_GridInsertColumn( grid2, l+1, row,
 -									grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]);
 -					grid2->lodStitched = qfalse;
 -					s_worldData.surfaces[grid2num].data = (void *) grid2;
 -					return qtrue;
 -				}
 -			}
 -			for (m = 0; m < 2; m++) {
 -
 -				if (grid2->height >= MAX_GRID_SIZE)
 -					break;
 -				if (m) offset2 = grid2->width-1;
 -				else offset2 = 0;
 -				for ( l = 0; l < grid2->height-1; l++) {
 -					//
 -					v1 = grid1->verts[k + offset1].xyz;
 -					v2 = grid2->verts[grid2->width * l + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -
 -					v1 = grid1->verts[k + 2 + offset1].xyz;
 -					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -					//
 -					v1 = grid2->verts[grid2->width * l + offset2].xyz;
 -					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) < .01 &&
 -							fabs(v1[1] - v2[1]) < .01 &&
 -							fabs(v1[2] - v2[2]) < .01)
 -						continue;
 -					//
 -					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
 -					// insert row into grid2 right after after row l
 -					if (m) column = grid2->width-1;
 -					else column = 0;
 -					grid2 = R_GridInsertRow( grid2, l+1, column,
 -										grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]);
 -					grid2->lodStitched = qfalse;
 -					s_worldData.surfaces[grid2num].data = (void *) grid2;
 -					return qtrue;
 -				}
 -			}
 -		}
 -	}
 -	for (n = 0; n < 2; n++) {
 -		//
 -		if (n) offset1 = grid1->width-1;
 -		else offset1 = 0;
 -		if (R_MergedHeightPoints(grid1, offset1))
 -			continue;
 -		for (k = 0; k < grid1->height-2; k += 2) {
 -			for (m = 0; m < 2; m++) {
 -
 -				if ( grid2->width >= MAX_GRID_SIZE )
 -					break;
 -				if (m) offset2 = (grid2->height-1) * grid2->width;
 -				else offset2 = 0;
 -				for ( l = 0; l < grid2->width-1; l++) {
 -				//
 -					v1 = grid1->verts[grid1->width * k + offset1].xyz;
 -					v2 = grid2->verts[l + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -
 -					v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz;
 -					v2 = grid2->verts[l + 1 + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -					//
 -					v1 = grid2->verts[l + offset2].xyz;
 -					v2 = grid2->verts[(l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) < .01 &&
 -							fabs(v1[1] - v2[1]) < .01 &&
 -							fabs(v1[2] - v2[2]) < .01)
 -						continue;
 -					//
 -					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
 -					// insert column into grid2 right after after column l
 -					if (m) row = grid2->height-1;
 -					else row = 0;
 -					grid2 = R_GridInsertColumn( grid2, l+1, row,
 -									grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]);
 -					grid2->lodStitched = qfalse;
 -					s_worldData.surfaces[grid2num].data = (void *) grid2;
 -					return qtrue;
 -				}
 -			}
 -			for (m = 0; m < 2; m++) {
 -
 -				if (grid2->height >= MAX_GRID_SIZE)
 -					break;
 -				if (m) offset2 = grid2->width-1;
 -				else offset2 = 0;
 -				for ( l = 0; l < grid2->height-1; l++) {
 -				//
 -					v1 = grid1->verts[grid1->width * k + offset1].xyz;
 -					v2 = grid2->verts[grid2->width * l + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -
 -					v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz;
 -					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -					//
 -					v1 = grid2->verts[grid2->width * l + offset2].xyz;
 -					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) < .01 &&
 -							fabs(v1[1] - v2[1]) < .01 &&
 -							fabs(v1[2] - v2[2]) < .01)
 -						continue;
 -					//
 -					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
 -					// insert row into grid2 right after after row l
 -					if (m) column = grid2->width-1;
 -					else column = 0;
 -					grid2 = R_GridInsertRow( grid2, l+1, column,
 -									grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]);
 -					grid2->lodStitched = qfalse;
 -					s_worldData.surfaces[grid2num].data = (void *) grid2;
 -					return qtrue;
 -				}
 -			}
 -		}
 -	}
 -	for (n = 0; n < 2; n++) {
 -		//
 -		if (n) offset1 = (grid1->height-1) * grid1->width;
 -		else offset1 = 0;
 -		if (R_MergedWidthPoints(grid1, offset1))
 -			continue;
 -		for (k = grid1->width-1; k > 1; k -= 2) {
 -
 -			for (m = 0; m < 2; m++) {
 -
 -				if ( grid2->width >= MAX_GRID_SIZE )
 -					break;
 -				if (m) offset2 = (grid2->height-1) * grid2->width;
 -				else offset2 = 0;
 -				for ( l = 0; l < grid2->width-1; l++) {
 -				//
 -					v1 = grid1->verts[k + offset1].xyz;
 -					v2 = grid2->verts[l + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -
 -					v1 = grid1->verts[k - 2 + offset1].xyz;
 -					v2 = grid2->verts[l + 1 + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -					//
 -					v1 = grid2->verts[l + offset2].xyz;
 -					v2 = grid2->verts[(l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) < .01 &&
 -							fabs(v1[1] - v2[1]) < .01 &&
 -							fabs(v1[2] - v2[2]) < .01)
 -						continue;
 -					//
 -					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
 -					// insert column into grid2 right after after column l
 -					if (m) row = grid2->height-1;
 -					else row = 0;
 -					grid2 = R_GridInsertColumn( grid2, l+1, row,
 -										grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]);
 -					grid2->lodStitched = qfalse;
 -					s_worldData.surfaces[grid2num].data = (void *) grid2;
 -					return qtrue;
 -				}
 -			}
 -			for (m = 0; m < 2; m++) {
 -
 -				if (grid2->height >= MAX_GRID_SIZE)
 -					break;
 -				if (m) offset2 = grid2->width-1;
 -				else offset2 = 0;
 -				for ( l = 0; l < grid2->height-1; l++) {
 -				//
 -					v1 = grid1->verts[k + offset1].xyz;
 -					v2 = grid2->verts[grid2->width * l + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -
 -					v1 = grid1->verts[k - 2 + offset1].xyz;
 -					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -					//
 -					v1 = grid2->verts[grid2->width * l + offset2].xyz;
 -					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) < .01 &&
 -							fabs(v1[1] - v2[1]) < .01 &&
 -							fabs(v1[2] - v2[2]) < .01)
 -						continue;
 -					//
 -					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
 -					// insert row into grid2 right after after row l
 -					if (m) column = grid2->width-1;
 -					else column = 0;
 -					grid2 = R_GridInsertRow( grid2, l+1, column,
 -										grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]);
 -					if (!grid2)
 -						break;
 -					grid2->lodStitched = qfalse;
 -					s_worldData.surfaces[grid2num].data = (void *) grid2;
 -					return qtrue;
 -				}
 -			}
 -		}
 -	}
 -	for (n = 0; n < 2; n++) {
 -		//
 -		if (n) offset1 = grid1->width-1;
 -		else offset1 = 0;
 -		if (R_MergedHeightPoints(grid1, offset1))
 -			continue;
 -		for (k = grid1->height-1; k > 1; k -= 2) {
 -			for (m = 0; m < 2; m++) {
 -
 -				if ( grid2->width >= MAX_GRID_SIZE )
 -					break;
 -				if (m) offset2 = (grid2->height-1) * grid2->width;
 -				else offset2 = 0;
 -				for ( l = 0; l < grid2->width-1; l++) {
 -				//
 -					v1 = grid1->verts[grid1->width * k + offset1].xyz;
 -					v2 = grid2->verts[l + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -
 -					v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz;
 -					v2 = grid2->verts[l + 1 + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -					//
 -					v1 = grid2->verts[l + offset2].xyz;
 -					v2 = grid2->verts[(l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) < .01 &&
 -							fabs(v1[1] - v2[1]) < .01 &&
 -							fabs(v1[2] - v2[2]) < .01)
 -						continue;
 -					//
 -					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
 -					// insert column into grid2 right after after column l
 -					if (m) row = grid2->height-1;
 -					else row = 0;
 -					grid2 = R_GridInsertColumn( grid2, l+1, row,
 -										grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]);
 -					grid2->lodStitched = qfalse;
 -					s_worldData.surfaces[grid2num].data = (void *) grid2;
 -					return qtrue;
 -				}
 -			}
 -			for (m = 0; m < 2; m++) {
 -
 -				if (grid2->height >= MAX_GRID_SIZE)
 -					break;
 -				if (m) offset2 = grid2->width-1;
 -				else offset2 = 0;
 -				for ( l = 0; l < grid2->height-1; l++) {
 -				//
 -					v1 = grid1->verts[grid1->width * k + offset1].xyz;
 -					v2 = grid2->verts[grid2->width * l + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -
 -					v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz;
 -					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) > .1)
 -						continue;
 -					if ( fabs(v1[1] - v2[1]) > .1)
 -						continue;
 -					if ( fabs(v1[2] - v2[2]) > .1)
 -						continue;
 -					//
 -					v1 = grid2->verts[grid2->width * l + offset2].xyz;
 -					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
 -					if ( fabs(v1[0] - v2[0]) < .01 &&
 -							fabs(v1[1] - v2[1]) < .01 &&
 -							fabs(v1[2] - v2[2]) < .01)
 -						continue;
 -					//
 -					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
 -					// insert row into grid2 right after after row l
 -					if (m) column = grid2->width-1;
 -					else column = 0;
 -					grid2 = R_GridInsertRow( grid2, l+1, column,
 -										grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]);
 -					grid2->lodStitched = qfalse;
 -					s_worldData.surfaces[grid2num].data = (void *) grid2;
 -					return qtrue;
 -				}
 -			}
 -		}
 -	}
 -	return qfalse;
 -}
 -
 -/*
 -===============
 -R_TryStitchPatch
 -
 -This function will try to stitch patches in the same LoD group together for the highest LoD.
 -
 -Only single missing vertice cracks will be fixed.
 -
 -Vertices will be joined at the patch side a crack is first found, at the other side
 -of the patch (on the same row or column) the vertices will not be joined and cracks
 -might still appear at that side.
 -===============
 -*/
 -int R_TryStitchingPatch( int grid1num ) {
 -	int j, numstitches;
 -	srfGridMesh_t *grid1, *grid2;
 -
 -	numstitches = 0;
 -	grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data;
 -	for ( j = 0; j < s_worldData.numsurfaces; j++ ) {
 -		//
 -		grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data;
 -		// if this surface is not a grid
 -		if ( grid2->surfaceType != SF_GRID ) continue;
 -		// grids in the same LOD group should have the exact same lod radius
 -		if ( grid1->lodRadius != grid2->lodRadius ) continue;
 -		// grids in the same LOD group should have the exact same lod origin
 -		if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue;
 -		if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue;
 -		if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue;
 -		//
 -		while (R_StitchPatches(grid1num, j))
 -		{
 -			numstitches++;
 -		}
 -	}
 -	return numstitches;
 -}
 -
 -/*
 -===============
 -R_StitchAllPatches
 -===============
 -*/
 -void R_StitchAllPatches( void ) {
 -	int i, stitched, numstitches;
 -	srfGridMesh_t *grid1;
 -
 -	numstitches = 0;
 -	do
 -	{
 -		stitched = qfalse;
 -		for ( i = 0; i < s_worldData.numsurfaces; i++ ) {
 -			//
 -			grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data;
 -			// if this surface is not a grid
 -			if ( grid1->surfaceType != SF_GRID )
 -				continue;
 -			//
 -			if ( grid1->lodStitched )
 -				continue;
 -			//
 -			grid1->lodStitched = qtrue;
 -			stitched = qtrue;
 -			//
 -			numstitches += R_TryStitchingPatch( i );
 -		}
 -	}
 -	while (stitched);
 -	ri.Printf( PRINT_ALL, "stitched %d LoD cracks\n", numstitches );
 -}
 -
 -/*
 -===============
 -R_MovePatchSurfacesToHunk
 -===============
 -*/
 -void R_MovePatchSurfacesToHunk(void) {
 -	int i, size;
 -	srfGridMesh_t *grid, *hunkgrid;
 -
 -	for ( i = 0; i < s_worldData.numsurfaces; i++ ) {
 -		//
 -		grid = (srfGridMesh_t *) s_worldData.surfaces[i].data;
 -		// if this surface is not a grid
 -		if ( grid->surfaceType != SF_GRID )
 -			continue;
 -		//
 -		size = (grid->width * grid->height - 1) * sizeof( drawVert_t ) + sizeof( *grid );
 -		hunkgrid = ri.Hunk_Alloc( size, h_low );
 -		Com_Memcpy(hunkgrid, grid, size);
 -
 -		hunkgrid->widthLodError = ri.Hunk_Alloc( grid->width * 4, h_low );
 -		Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 );
 -
 -		hunkgrid->heightLodError = ri.Hunk_Alloc( grid->height * 4, h_low );
 -		Com_Memcpy( grid->heightLodError, grid->heightLodError, grid->height * 4 );
 -
 -		R_FreeSurfaceGridMesh( grid );
 -
 -		s_worldData.surfaces[i].data = (void *) hunkgrid;
 -	}
 -}
 -
 -/*
 -===============
 -R_LoadSurfaces
 -===============
 -*/
 -static	void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) {
 -	dsurface_t	*in;
 -	msurface_t	*out;
 -	drawVert_t	*dv;
 -	int			*indexes;
 -	int			count;
 -	int			numFaces, numMeshes, numTriSurfs, numFlares;
 -	int			i;
 -
 -	numFaces = 0;
 -	numMeshes = 0;
 -	numTriSurfs = 0;
 -	numFlares = 0;
 -
 -	in = (void *)(fileBase + surfs->fileofs);
 -	if (surfs->filelen % sizeof(*in))
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	count = surfs->filelen / sizeof(*in);
 -
 -	dv = (void *)(fileBase + verts->fileofs);
 -	if (verts->filelen % sizeof(*dv))
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -
 -	indexes = (void *)(fileBase + indexLump->fileofs);
 -	if ( indexLump->filelen % sizeof(*indexes))
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -
 -	out = ri.Hunk_Alloc ( count * sizeof(*out), h_low );	
 -
 -	s_worldData.surfaces = out;
 -	s_worldData.numsurfaces = count;
 -
 -	for ( i = 0 ; i < count ; i++, in++, out++ ) {
 -		switch ( LittleLong( in->surfaceType ) ) {
 -		case MST_PATCH:
 -			ParseMesh ( in, dv, out );
 -			numMeshes++;
 -			break;
 -		case MST_TRIANGLE_SOUP:
 -			ParseTriSurf( in, dv, out, indexes );
 -			numTriSurfs++;
 -			break;
 -		case MST_PLANAR:
 -			ParseFace( in, dv, out, indexes );
 -			numFaces++;
 -			break;
 -		case MST_FLARE:
 -			ParseFlare( in, dv, out, indexes );
 -			numFlares++;
 -			break;
 -		default:
 -			ri.Error( ERR_DROP, "Bad surfaceType" );
 -		}
 -	}
 -
 -#ifdef PATCH_STITCHING
 -	R_StitchAllPatches();
 -#endif
 -
 -	R_FixSharedVertexLodError();
 -
 -#ifdef PATCH_STITCHING
 -	R_MovePatchSurfacesToHunk();
 -#endif
 -
 -	ri.Printf( PRINT_ALL, "...loaded %d faces, %i meshes, %i trisurfs, %i flares\n", 
 -		numFaces, numMeshes, numTriSurfs, numFlares );
 -}
 -
 -
 -
 -/*
 -=================
 -R_LoadSubmodels
 -=================
 -*/
 -static	void R_LoadSubmodels( lump_t *l ) {
 -	dmodel_t	*in;
 -	bmodel_t	*out;
 -	int			i, j, count;
 -
 -	in = (void *)(fileBase + l->fileofs);
 -	if (l->filelen % sizeof(*in))
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	count = l->filelen / sizeof(*in);
 -
 -	s_worldData.bmodels = out = ri.Hunk_Alloc( count * sizeof(*out), h_low );
 -
 -	for ( i=0 ; i<count ; i++, in++, out++ ) {
 -		model_t *model;
 -
 -		model = R_AllocModel();
 -
 -		assert( model != NULL );			// this should never happen
 -
 -		model->type = MOD_BRUSH;
 -		model->bmodel = out;
 -		Com_sprintf( model->name, sizeof( model->name ), "*%d", i );
 -
 -		for (j=0 ; j<3 ; j++) {
 -			out->bounds[0][j] = LittleFloat (in->mins[j]);
 -			out->bounds[1][j] = LittleFloat (in->maxs[j]);
 -		}
 -
 -		out->firstSurface = s_worldData.surfaces + LittleLong( in->firstSurface );
 -		out->numSurfaces = LittleLong( in->numSurfaces );
 -	}
 -}
 -
 -
 -
 -//==================================================================
 -
 -/*
 -=================
 -R_SetParent
 -=================
 -*/
 -static	void R_SetParent (mnode_t *node, mnode_t *parent)
 -{
 -	node->parent = parent;
 -	if (node->contents != -1)
 -		return;
 -	R_SetParent (node->children[0], node);
 -	R_SetParent (node->children[1], node);
 -}
 -
 -/*
 -=================
 -R_LoadNodesAndLeafs
 -=================
 -*/
 -static	void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) {
 -	int			i, j, p;
 -	dnode_t		*in;
 -	dleaf_t		*inLeaf;
 -	mnode_t 	*out;
 -	int			numNodes, numLeafs;
 -
 -	in = (void *)(fileBase + nodeLump->fileofs);
 -	if (nodeLump->filelen % sizeof(dnode_t) ||
 -		leafLump->filelen % sizeof(dleaf_t) ) {
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	}
 -	numNodes = nodeLump->filelen / sizeof(dnode_t);
 -	numLeafs = leafLump->filelen / sizeof(dleaf_t);
 -
 -	out = ri.Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low);	
 -
 -	s_worldData.nodes = out;
 -	s_worldData.numnodes = numNodes + numLeafs;
 -	s_worldData.numDecisionNodes = numNodes;
 -
 -	// load nodes
 -	for ( i=0 ; i<numNodes; i++, in++, out++)
 -	{
 -		for (j=0 ; j<3 ; j++)
 -		{
 -			out->mins[j] = LittleLong (in->mins[j]);
 -			out->maxs[j] = LittleLong (in->maxs[j]);
 -		}
 -	
 -		p = LittleLong(in->planeNum);
 -		out->plane = s_worldData.planes + p;
 -
 -		out->contents = CONTENTS_NODE;	// differentiate from leafs
 -
 -		for (j=0 ; j<2 ; j++)
 -		{
 -			p = LittleLong (in->children[j]);
 -			if (p >= 0)
 -				out->children[j] = s_worldData.nodes + p;
 -			else
 -				out->children[j] = s_worldData.nodes + numNodes + (-1 - p);
 -		}
 -	}
 -	
 -	// load leafs
 -	inLeaf = (void *)(fileBase + leafLump->fileofs);
 -	for ( i=0 ; i<numLeafs ; i++, inLeaf++, out++)
 -	{
 -		for (j=0 ; j<3 ; j++)
 -		{
 -			out->mins[j] = LittleLong (inLeaf->mins[j]);
 -			out->maxs[j] = LittleLong (inLeaf->maxs[j]);
 -		}
 -
 -		out->cluster = LittleLong(inLeaf->cluster);
 -		out->area = LittleLong(inLeaf->area);
 -
 -		if ( out->cluster >= s_worldData.numClusters ) {
 -			s_worldData.numClusters = out->cluster + 1;
 -		}
 -
 -		out->firstmarksurface = s_worldData.marksurfaces +
 -			LittleLong(inLeaf->firstLeafSurface);
 -		out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces);
 -	}	
 -
 -	// chain decendants
 -	R_SetParent (s_worldData.nodes, NULL);
 -}
 -
 -//=============================================================================
 -
 -/*
 -=================
 -R_LoadShaders
 -=================
 -*/
 -static	void R_LoadShaders( lump_t *l ) {	
 -	int		i, count;
 -	dshader_t	*in, *out;
 -	
 -	in = (void *)(fileBase + l->fileofs);
 -	if (l->filelen % sizeof(*in))
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	count = l->filelen / sizeof(*in);
 -	out = ri.Hunk_Alloc ( count*sizeof(*out), h_low );
 -
 -	s_worldData.shaders = out;
 -	s_worldData.numShaders = count;
 -
 -	Com_Memcpy( out, in, count*sizeof(*out) );
 -
 -	for ( i=0 ; i<count ; i++ ) {
 -		out[i].surfaceFlags = LittleLong( out[i].surfaceFlags );
 -		out[i].contentFlags = LittleLong( out[i].contentFlags );
 -	}
 -}
 -
 -
 -/*
 -=================
 -R_LoadMarksurfaces
 -=================
 -*/
 -static	void R_LoadMarksurfaces (lump_t *l)
 -{	
 -	int		i, j, count;
 -	int		*in;
 -	msurface_t **out;
 -	
 -	in = (void *)(fileBase + l->fileofs);
 -	if (l->filelen % sizeof(*in))
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	count = l->filelen / sizeof(*in);
 -	out = ri.Hunk_Alloc ( count*sizeof(*out), h_low);	
 -
 -	s_worldData.marksurfaces = out;
 -	s_worldData.nummarksurfaces = count;
 -
 -	for ( i=0 ; i<count ; i++)
 -	{
 -		j = LittleLong(in[i]);
 -		out[i] = s_worldData.surfaces + j;
 -	}
 -}
 -
 -
 -/*
 -=================
 -R_LoadPlanes
 -=================
 -*/
 -static	void R_LoadPlanes( lump_t *l ) {
 -	int			i, j;
 -	cplane_t	*out;
 -	dplane_t 	*in;
 -	int			count;
 -	int			bits;
 -	
 -	in = (void *)(fileBase + l->fileofs);
 -	if (l->filelen % sizeof(*in))
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	count = l->filelen / sizeof(*in);
 -	out = ri.Hunk_Alloc ( count*2*sizeof(*out), h_low);	
 -	
 -	s_worldData.planes = out;
 -	s_worldData.numplanes = count;
 -
 -	for ( i=0 ; i<count ; i++, in++, out++) {
 -		bits = 0;
 -		for (j=0 ; j<3 ; j++) {
 -			out->normal[j] = LittleFloat (in->normal[j]);
 -			if (out->normal[j] < 0) {
 -				bits |= 1<<j;
 -			}
 -		}
 -
 -		out->dist = LittleFloat (in->dist);
 -		out->type = PlaneTypeForNormal( out->normal );
 -		out->signbits = bits;
 -	}
 -}
 -
 -/*
 -=================
 -R_LoadFogs
 -
 -=================
 -*/
 -static	void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) {
 -	int			i;
 -	fog_t		*out;
 -	dfog_t		*fogs;
 -	dbrush_t 	*brushes, *brush;
 -	dbrushside_t	*sides;
 -	int			count, brushesCount, sidesCount;
 -	int			sideNum;
 -	int			planeNum;
 -	shader_t	*shader;
 -	float		d;
 -	int			firstSide;
 -
 -	fogs = (void *)(fileBase + l->fileofs);
 -	if (l->filelen % sizeof(*fogs)) {
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	}
 -	count = l->filelen / sizeof(*fogs);
 -
 -	// create fog strucutres for them
 -	s_worldData.numfogs = count + 1;
 -	s_worldData.fogs = ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low);
 -	out = s_worldData.fogs + 1;
 -
 -	if ( !count ) {
 -		return;
 -	}
 -
 -	brushes = (void *)(fileBase + brushesLump->fileofs);
 -	if (brushesLump->filelen % sizeof(*brushes)) {
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	}
 -	brushesCount = brushesLump->filelen / sizeof(*brushes);
 -
 -	sides = (void *)(fileBase + sidesLump->fileofs);
 -	if (sidesLump->filelen % sizeof(*sides)) {
 -		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
 -	}
 -	sidesCount = sidesLump->filelen / sizeof(*sides);
 -
 -	for ( i=0 ; i<count ; i++, fogs++) {
 -		out->originalBrushNumber = LittleLong( fogs->brushNum );
 -
 -		if ( (unsigned)out->originalBrushNumber >= brushesCount ) {
 -			ri.Error( ERR_DROP, "fog brushNumber out of range" );
 -		}
 -		brush = brushes + out->originalBrushNumber;
 -
 -		firstSide = LittleLong( brush->firstSide );
 -
 -			if ( (unsigned)firstSide > sidesCount - 6 ) {
 -			ri.Error( ERR_DROP, "fog brush sideNumber out of range" );
 -		}
 -
 -		// brushes are always sorted with the axial sides first
 -		sideNum = firstSide + 0;
 -		planeNum = LittleLong( sides[ sideNum ].planeNum );
 -		out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist;
 -
 -		sideNum = firstSide + 1;
 -		planeNum = LittleLong( sides[ sideNum ].planeNum );
 -		out->bounds[1][0] = s_worldData.planes[ planeNum ].dist;
 -
 -		sideNum = firstSide + 2;
 -		planeNum = LittleLong( sides[ sideNum ].planeNum );
 -		out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist;
 -
 -		sideNum = firstSide + 3;
 -		planeNum = LittleLong( sides[ sideNum ].planeNum );
 -		out->bounds[1][1] = s_worldData.planes[ planeNum ].dist;
 -
 -		sideNum = firstSide + 4;
 -		planeNum = LittleLong( sides[ sideNum ].planeNum );
 -		out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist;
 -
 -		sideNum = firstSide + 5;
 -		planeNum = LittleLong( sides[ sideNum ].planeNum );
 -		out->bounds[1][2] = s_worldData.planes[ planeNum ].dist;
 -
 -		// get information from the shader for fog parameters
 -		shader = R_FindShader( fogs->shader, LIGHTMAP_NONE, qtrue );
 -
 -		out->parms = shader->fogParms;
 -
 -		out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight, 
 -			                          shader->fogParms.color[1] * tr.identityLight, 
 -			                          shader->fogParms.color[2] * tr.identityLight, 1.0 );
 -
 -		d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque;
 -		out->tcScale = 1.0f / ( d * 8 );
 -
 -		// set the gradient vector
 -		sideNum = LittleLong( fogs->visibleSide );
 -
 -		if ( sideNum == -1 ) {
 -			out->hasSurface = qfalse;
 -		} else {
 -			out->hasSurface = qtrue;
 -			planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum );
 -			VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface );
 -			out->surface[3] = -s_worldData.planes[ planeNum ].dist;
 -		}
 -
 -		out++;
 -	}
 -
 -}
 -
 -
 -/*
 -================
 -R_LoadLightGrid
 -
 -================
 -*/
 -void R_LoadLightGrid( lump_t *l ) {
 -	int		i;
 -	vec3_t	maxs;
 -	int		numGridPoints;
 -	world_t	*w;
 -	float	*wMins, *wMaxs;
 -
 -	w = &s_worldData;
 -
 -	w->lightGridInverseSize[0] = 1.0f / w->lightGridSize[0];
 -	w->lightGridInverseSize[1] = 1.0f / w->lightGridSize[1];
 -	w->lightGridInverseSize[2] = 1.0f / w->lightGridSize[2];
 -
 -	wMins = w->bmodels[0].bounds[0];
 -	wMaxs = w->bmodels[0].bounds[1];
 -
 -	for ( i = 0 ; i < 3 ; i++ ) {
 -		w->lightGridOrigin[i] = w->lightGridSize[i] * ceil( wMins[i] / w->lightGridSize[i] );
 -		maxs[i] = w->lightGridSize[i] * floor( wMaxs[i] / w->lightGridSize[i] );
 -		w->lightGridBounds[i] = (maxs[i] - w->lightGridOrigin[i])/w->lightGridSize[i] + 1;
 -	}
 -
 -	numGridPoints = w->lightGridBounds[0] * w->lightGridBounds[1] * w->lightGridBounds[2];
 -
 -	if ( l->filelen != numGridPoints * 8 ) {
 -		ri.Printf( PRINT_WARNING, "WARNING: light grid mismatch\n" );
 -		w->lightGridData = NULL;
 -		return;
 -	}
 -
 -	w->lightGridData = ri.Hunk_Alloc( l->filelen, h_low );
 -	Com_Memcpy( w->lightGridData, (void *)(fileBase + l->fileofs), l->filelen );
 -
 -	// deal with overbright bits
 -	for ( i = 0 ; i < numGridPoints ; i++ ) {
 -		R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] );
 -		R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] );
 -	}
 -}
 -
 -/*
 -================
 -R_LoadEntities
 -================
 -*/
 -void R_LoadEntities( lump_t *l ) {
 -	char *p, *token, *s;
 -	char keyname[MAX_TOKEN_CHARS];
 -	char value[MAX_TOKEN_CHARS];
 -	world_t	*w;
 -
 -	w = &s_worldData;
 -	w->lightGridSize[0] = 64;
 -	w->lightGridSize[1] = 64;
 -	w->lightGridSize[2] = 128;
 -
 -	p = (char *)(fileBase + l->fileofs);
 -
 -	// store for reference by the cgame
 -	w->entityString = ri.Hunk_Alloc( l->filelen + 1, h_low );
 -	strcpy( w->entityString, p );
 -	w->entityParsePoint = w->entityString;
 -
 -	token = COM_ParseExt( &p, qtrue );
 -	if (!*token || *token != '{') {
 -		return;
 -	}
 -
 -	// only parse the world spawn
 -	while ( 1 ) {	
 -		// parse key
 -		token = COM_ParseExt( &p, qtrue );
 -
 -		if ( !*token || *token == '}' ) {
 -			break;
 -		}
 -		Q_strncpyz(keyname, token, sizeof(keyname));
 -
 -		// parse value
 -		token = COM_ParseExt( &p, qtrue );
 -
 -		if ( !*token || *token == '}' ) {
 -			break;
 -		}
 -		Q_strncpyz(value, token, sizeof(value));
 -
 -		// check for remapping of shaders for vertex lighting
 -		s = "vertexremapshader";
 -		if (!Q_strncmp(keyname, s, strlen(s)) ) {
 -			s = strchr(value, ';');
 -			if (!s) {
 -				ri.Printf( PRINT_WARNING, "WARNING: no semi colon in vertexshaderremap '%s'\n", value );
 -				break;
 -			}
 -			*s++ = 0;
 -			if (r_vertexLight->integer) {
 -				R_RemapShader(value, s, "0");
 -			}
 -			continue;
 -		}
 -		// check for remapping of shaders
 -		s = "remapshader";
 -		if (!Q_strncmp(keyname, s, strlen(s)) ) {
 -			s = strchr(value, ';');
 -			if (!s) {
 -				ri.Printf( PRINT_WARNING, "WARNING: no semi colon in shaderremap '%s'\n", value );
 -				break;
 -			}
 -			*s++ = 0;
 -			R_RemapShader(value, s, "0");
 -			continue;
 -		}
 -		// check for a different grid size
 -		if (!Q_stricmp(keyname, "gridsize")) {
 -			sscanf(value, "%f %f %f", &w->lightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] );
 -			continue;
 -		}
 -	}
 -}
 -
 -/*
 -=================
 -R_GetEntityToken
 -=================
 -*/
 -qboolean R_GetEntityToken( char *buffer, int size ) {
 -	const char	*s;
 -
 -	s = COM_Parse( &s_worldData.entityParsePoint );
 -	Q_strncpyz( buffer, s, size );
 -	if ( !s_worldData.entityParsePoint || !s[0] ) {
 -		s_worldData.entityParsePoint = s_worldData.entityString;
 -		return qfalse;
 -	} else {
 -		return qtrue;
 -	}
 -}
 -
 -/*
 -=================
 -RE_LoadWorldMap
 -
 -Called directly from cgame
 -=================
 -*/
 -void RE_LoadWorldMap( const char *name ) {
 -	int			i;
 -	dheader_t	*header;
 -	byte		*buffer;
 -	byte		*startMarker;
 -
 -	if ( tr.worldMapLoaded ) {
 -		ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map\n" );
 -	}
 -
 -	// set default sun direction to be used if it isn't
 -	// overridden by a shader
 -	tr.sunDirection[0] = 0.45f;
 -	tr.sunDirection[1] = 0.3f;
 -	tr.sunDirection[2] = 0.9f;
 -
 -	VectorNormalize( tr.sunDirection );
 -
 -	tr.worldMapLoaded = qtrue;
 -
 -	// load it
 -    ri.FS_ReadFile( name, (void **)&buffer );
 -	if ( !buffer ) {
 -		ri.Error (ERR_DROP, "RE_LoadWorldMap: %s not found", name);
 -	}
 -
 -	// clear tr.world so if the level fails to load, the next
 -	// try will not look at the partially loaded version
 -	tr.world = NULL;
 -
 -	Com_Memset( &s_worldData, 0, sizeof( s_worldData ) );
 -	Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) );
 -
 -	Q_strncpyz( s_worldData.baseName, COM_SkipPath( s_worldData.name ), sizeof( s_worldData.name ) );
 -	COM_StripExtension( s_worldData.baseName, s_worldData.baseName );
 -
 -	startMarker = ri.Hunk_Alloc(0, h_low);
 -	c_gridVerts = 0;
 -
 -	header = (dheader_t *)buffer;
 -	fileBase = (byte *)header;
 -
 -	i = LittleLong (header->version);
 -	if ( i != BSP_VERSION ) {
 -		ri.Error (ERR_DROP, "RE_LoadWorldMap: %s has wrong version number (%i should be %i)", 
 -			name, i, BSP_VERSION);
 -	}
 -
 -	// swap all the lumps
 -	for (i=0 ; i<sizeof(dheader_t)/4 ; i++) {
 -		((int *)header)[i] = LittleLong ( ((int *)header)[i]);
 -	}
 -
 -	// load into heap
 -	R_LoadShaders( &header->lumps[LUMP_SHADERS] );
 -	R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] );
 -	R_LoadPlanes (&header->lumps[LUMP_PLANES]);
 -	R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] );
 -	R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header->lumps[LUMP_DRAWINDEXES] );
 -	R_LoadMarksurfaces (&header->lumps[LUMP_LEAFSURFACES]);
 -	R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]);
 -	R_LoadSubmodels (&header->lumps[LUMP_MODELS]);
 -	R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] );
 -	R_LoadEntities( &header->lumps[LUMP_ENTITIES] );
 -	R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] );
 -
 -	s_worldData.dataSize = (byte *)ri.Hunk_Alloc(0, h_low) - startMarker;
 -
 -	// only set tr.world now that we know the entire level has loaded properly
 -	tr.world = &s_worldData;
 -
 -    ri.FS_FreeFile( buffer );
 -}
 -
 +/* +=========================================================================== +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 +=========================================================================== +*/ +// tr_map.c + +#include "tr_local.h" + +/* + +Loads and prepares a map file for scene rendering. + +A single entry point: + +void RE_LoadWorldMap( const char *name ); + +*/ + +static	world_t		s_worldData; +static	byte		*fileBase; + +int			c_subdivisions; +int			c_gridVerts; + +//=============================================================================== + +static void HSVtoRGB( float h, float s, float v, float rgb[3] ) +{ +	int i; +	float f; +	float p, q, t; + +	h *= 5; + +	i = floor( h ); +	f = h - i; + +	p = v * ( 1 - s ); +	q = v * ( 1 - s * f ); +	t = v * ( 1 - s * ( 1 - f ) ); + +	switch ( i ) +	{ +	case 0: +		rgb[0] = v; +		rgb[1] = t; +		rgb[2] = p; +		break; +	case 1: +		rgb[0] = q; +		rgb[1] = v; +		rgb[2] = p; +		break; +	case 2: +		rgb[0] = p; +		rgb[1] = v; +		rgb[2] = t; +		break; +	case 3: +		rgb[0] = p; +		rgb[1] = q; +		rgb[2] = v; +		break; +	case 4: +		rgb[0] = t; +		rgb[1] = p; +		rgb[2] = v; +		break; +	case 5: +		rgb[0] = v; +		rgb[1] = p; +		rgb[2] = q; +		break; +	} +} + +/* +=============== +R_ColorShiftLightingBytes + +=============== +*/ +static	void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) { +	int		shift, r, g, b; + +	// shift the color data based on overbright range +	shift = r_mapOverBrightBits->integer - tr.overbrightBits; + +	// shift the data based on overbright range +	r = in[0] << shift; +	g = in[1] << shift; +	b = in[2] << shift; +	 +	// normalize by color instead of saturating to white +	if ( ( r | g | b ) > 255 ) { +		int		max; + +		max = r > g ? r : g; +		max = max > b ? max : b; +		r = r * 255 / max; +		g = g * 255 / max; +		b = b * 255 / max; +	} + +	out[0] = r; +	out[1] = g; +	out[2] = b; +	out[3] = in[3]; +} + +/* +=============== +R_LoadLightmaps + +=============== +*/ +#define	LIGHTMAP_SIZE	128 +static	void R_LoadLightmaps( lump_t *l ) { +	byte		*buf, *buf_p; +	int			len; +	MAC_STATIC byte		image[LIGHTMAP_SIZE*LIGHTMAP_SIZE*4]; +	int			i, j; +	float maxIntensity = 0; +	double sumIntensity = 0; + +    len = l->filelen; +	if ( !len ) { +		return; +	} +	buf = fileBase + l->fileofs; + +	// we are about to upload textures +	R_SyncRenderThread(); + +	// create all the lightmaps +	tr.numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3); +	if ( tr.numLightmaps == 1 ) { +		//FIXME: HACK: maps with only one lightmap turn up fullbright for some reason. +		//this avoids this, but isn't the correct solution. +		tr.numLightmaps++; +	} + +	// if we are in r_vertexLight mode, we don't need the lightmaps at all +	if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { +		return; +	} + +	for ( i = 0 ; i < tr.numLightmaps ; i++ ) { +		// expand the 24 bit on-disk to 32 bit +		buf_p = buf + i * LIGHTMAP_SIZE*LIGHTMAP_SIZE * 3; + +		if ( r_lightmap->integer == 2 ) +		{	// color code by intensity as development tool	(FIXME: check range) +			for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) +			{ +				float r = buf_p[j*3+0]; +				float g = buf_p[j*3+1]; +				float b = buf_p[j*3+2]; +				float intensity; +				float out[3]; + +				intensity = 0.33f * r + 0.685f * g + 0.063f * b; + +				if ( intensity > 255 ) +					intensity = 1.0f; +				else +					intensity /= 255.0f; + +				if ( intensity > maxIntensity ) +					maxIntensity = intensity; + +				HSVtoRGB( intensity, 1.00, 0.50, out ); + +				image[j*4+0] = out[0] * 255; +				image[j*4+1] = out[1] * 255; +				image[j*4+2] = out[2] * 255; +				image[j*4+3] = 255; + +				sumIntensity += intensity; +			} +		} else { +			for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) { +				R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] ); +				image[j*4+3] = 255; +			} +		} +		tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image,  +			LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP ); +	} + +	if ( r_lightmap->integer == 2 )	{ +		ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) ); +	} +} + + +/* +================= +RE_SetWorldVisData + +This is called by the clipmodel subsystem so we can share the 1.8 megs of +space in big maps... +================= +*/ +void		RE_SetWorldVisData( const byte *vis ) { +	tr.externalVisData = vis; +} + + +/* +================= +R_LoadVisibility +================= +*/ +static	void R_LoadVisibility( lump_t *l ) { +	int		len; +	byte	*buf; + +	len = ( s_worldData.numClusters + 63 ) & ~63; +	s_worldData.novis = ri.Hunk_Alloc( len, h_low ); +	Com_Memset( s_worldData.novis, 0xff, len ); + +    len = l->filelen; +	if ( !len ) { +		return; +	} +	buf = fileBase + l->fileofs; + +	s_worldData.numClusters = LittleLong( ((int *)buf)[0] ); +	s_worldData.clusterBytes = LittleLong( ((int *)buf)[1] ); + +	// CM_Load should have given us the vis data to share, so +	// we don't need to allocate another copy +	if ( tr.externalVisData ) { +		s_worldData.vis = tr.externalVisData; +	} else { +		byte	*dest; + +		dest = ri.Hunk_Alloc( len - 8, h_low ); +		Com_Memcpy( dest, buf + 8, len - 8 ); +		s_worldData.vis = dest; +	} +} + +//=============================================================================== + + +/* +=============== +ShaderForShaderNum +=============== +*/ +static shader_t *ShaderForShaderNum( int shaderNum, int lightmapNum ) { +	shader_t	*shader; +	dshader_t	*dsh; + +	shaderNum = LittleLong( shaderNum ); +	if ( shaderNum < 0 || shaderNum >= s_worldData.numShaders ) { +		ri.Error( ERR_DROP, "ShaderForShaderNum: bad num %i", shaderNum ); +	} +	dsh = &s_worldData.shaders[ shaderNum ]; + +	if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { +		lightmapNum = LIGHTMAP_BY_VERTEX; +	} + +	if ( r_fullbright->integer ) { +		lightmapNum = LIGHTMAP_WHITEIMAGE; +	} + +	shader = R_FindShader( dsh->shader, lightmapNum, qtrue ); + +	// if the shader had errors, just use default shader +	if ( shader->defaultShader ) { +		return tr.defaultShader; +	} + +	return shader; +} + +/* +=============== +ParseFace +=============== +*/ +static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes  ) { +	int			i, j; +	srfSurfaceFace_t	*cv; +	int			numPoints, numIndexes; +	int			lightmapNum; +	int			sfaceSize, ofsIndexes; + +	lightmapNum = LittleLong( ds->lightmapNum ); + +	// get fog volume +	surf->fogIndex = LittleLong( ds->fogNum ) + 1; + +	// get shader value +	surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); +	if ( r_singleShader->integer && !surf->shader->isSky ) { +		surf->shader = tr.defaultShader; +	} + +	numPoints = LittleLong( ds->numVerts ); +	if (numPoints > MAX_FACE_POINTS) { +		ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numPoints); +    numPoints = MAX_FACE_POINTS; +    surf->shader = tr.defaultShader; +	} + +	numIndexes = LittleLong( ds->numIndexes ); + +	// create the srfSurfaceFace_t +	sfaceSize = ( int ) &((srfSurfaceFace_t *)0)->points[numPoints]; +	ofsIndexes = sfaceSize; +	sfaceSize += sizeof( int ) * numIndexes; + +	cv = ri.Hunk_Alloc( sfaceSize, h_low ); +	cv->surfaceType = SF_FACE; +	cv->numPoints = numPoints; +	cv->numIndices = numIndexes; +	cv->ofsIndices = ofsIndexes; + +	verts += LittleLong( ds->firstVert ); +	for ( i = 0 ; i < numPoints ; i++ ) { +		for ( j = 0 ; j < 3 ; j++ ) { +			cv->points[i][j] = LittleFloat( verts[i].xyz[j] ); +		} +		for ( j = 0 ; j < 2 ; j++ ) { +			cv->points[i][3+j] = LittleFloat( verts[i].st[j] ); +			cv->points[i][5+j] = LittleFloat( verts[i].lightmap[j] ); +		} +		R_ColorShiftLightingBytes( verts[i].color, (byte *)&cv->points[i][7] ); +	} + +	indexes += LittleLong( ds->firstIndex ); +	for ( i = 0 ; i < numIndexes ; i++ ) { +		((int *)((byte *)cv + cv->ofsIndices ))[i] = LittleLong( indexes[ i ] ); +	} + +	// take the plane information from the lightmap vector +	for ( i = 0 ; i < 3 ; i++ ) { +		cv->plane.normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); +	} +	cv->plane.dist = DotProduct( cv->points[0], cv->plane.normal ); +	SetPlaneSignbits( &cv->plane ); +	cv->plane.type = PlaneTypeForNormal( cv->plane.normal ); + +	surf->data = (surfaceType_t *)cv; +} + + +/* +=============== +ParseMesh +=============== +*/ +static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, msurface_t *surf ) { +	srfGridMesh_t	*grid; +	int				i, j; +	int				width, height, numPoints; +	MAC_STATIC drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE]; +	int				lightmapNum; +	vec3_t			bounds[2]; +	vec3_t			tmpVec; +	static surfaceType_t	skipData = SF_SKIP; + +	lightmapNum = LittleLong( ds->lightmapNum ); + +	// get fog volume +	surf->fogIndex = LittleLong( ds->fogNum ) + 1; + +	// get shader value +	surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); +	if ( r_singleShader->integer && !surf->shader->isSky ) { +		surf->shader = tr.defaultShader; +	} + +	// we may have a nodraw surface, because they might still need to +	// be around for movement clipping +	if ( s_worldData.shaders[ LittleLong( ds->shaderNum ) ].surfaceFlags & SURF_NODRAW ) { +		surf->data = &skipData; +		return; +	} + +	width = LittleLong( ds->patchWidth ); +	height = LittleLong( ds->patchHeight ); + +	verts += LittleLong( ds->firstVert ); +	numPoints = width * height; +	for ( i = 0 ; i < numPoints ; i++ ) { +		for ( j = 0 ; j < 3 ; j++ ) { +			points[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); +			points[i].normal[j] = LittleFloat( verts[i].normal[j] ); +		} +		for ( j = 0 ; j < 2 ; j++ ) { +			points[i].st[j] = LittleFloat( verts[i].st[j] ); +			points[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); +		} +		R_ColorShiftLightingBytes( verts[i].color, points[i].color ); +	} + +	// pre-tesseleate +	grid = R_SubdividePatchToGrid( width, height, points ); +	surf->data = (surfaceType_t *)grid; + +	// copy the level of detail origin, which is the center +	// of the group of all curves that must subdivide the same +	// to avoid cracking +	for ( i = 0 ; i < 3 ; i++ ) { +		bounds[0][i] = LittleFloat( ds->lightmapVecs[0][i] ); +		bounds[1][i] = LittleFloat( ds->lightmapVecs[1][i] ); +	} +	VectorAdd( bounds[0], bounds[1], bounds[1] ); +	VectorScale( bounds[1], 0.5f, grid->lodOrigin ); +	VectorSubtract( bounds[0], grid->lodOrigin, tmpVec ); +	grid->lodRadius = VectorLength( tmpVec ); +} + +/* +=============== +ParseTriSurf +=============== +*/ +static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { +	srfTriangles_t	*tri; +	int				i, j; +	int				numVerts, numIndexes; + +	// get fog volume +	surf->fogIndex = LittleLong( ds->fogNum ) + 1; + +	// get shader +	surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); +	if ( r_singleShader->integer && !surf->shader->isSky ) { +		surf->shader = tr.defaultShader; +	} + +	numVerts = LittleLong( ds->numVerts ); +	numIndexes = LittleLong( ds->numIndexes ); + +	tri = ri.Hunk_Alloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] )  +		+ numIndexes * sizeof( tri->indexes[0] ), h_low ); +	tri->surfaceType = SF_TRIANGLES; +	tri->numVerts = numVerts; +	tri->numIndexes = numIndexes; +	tri->verts = (drawVert_t *)(tri + 1); +	tri->indexes = (int *)(tri->verts + tri->numVerts ); + +	surf->data = (surfaceType_t *)tri; + +	// copy vertexes +	ClearBounds( tri->bounds[0], tri->bounds[1] ); +	verts += LittleLong( ds->firstVert ); +	for ( i = 0 ; i < numVerts ; i++ ) { +		for ( j = 0 ; j < 3 ; j++ ) { +			tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); +			tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] ); +		} +		AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] ); +		for ( j = 0 ; j < 2 ; j++ ) { +			tri->verts[i].st[j] = LittleFloat( verts[i].st[j] ); +			tri->verts[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); +		} + +		R_ColorShiftLightingBytes( verts[i].color, tri->verts[i].color ); +	} + +	// copy indexes +	indexes += LittleLong( ds->firstIndex ); +	for ( i = 0 ; i < numIndexes ; i++ ) { +		tri->indexes[i] = LittleLong( indexes[i] ); +		if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) { +			ri.Error( ERR_DROP, "Bad index in triangle surface" ); +		} +	} +} + +/* +=============== +ParseFlare +=============== +*/ +static void ParseFlare( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { +	srfFlare_t		*flare; +	int				i; + +	// get fog volume +	surf->fogIndex = LittleLong( ds->fogNum ) + 1; + +	// get shader +	surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); +	if ( r_singleShader->integer && !surf->shader->isSky ) { +		surf->shader = tr.defaultShader; +	} + +	flare = ri.Hunk_Alloc( sizeof( *flare ), h_low ); +	flare->surfaceType = SF_FLARE; + +	surf->data = (surfaceType_t *)flare; + +	for ( i = 0 ; i < 3 ; i++ ) { +		flare->origin[i] = LittleFloat( ds->lightmapOrigin[i] ); +		flare->color[i] = LittleFloat( ds->lightmapVecs[0][i] ); +		flare->normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); +	} +} + + +/* +================= +R_MergedWidthPoints + +returns true if there are grid points merged on a width edge +================= +*/ +int R_MergedWidthPoints(srfGridMesh_t *grid, int offset) { +	int i, j; + +	for (i = 1; i < grid->width-1; i++) { +		for (j = i + 1; j < grid->width-1; j++) { +			if ( fabs(grid->verts[i + offset].xyz[0] - grid->verts[j + offset].xyz[0]) > .1) continue; +			if ( fabs(grid->verts[i + offset].xyz[1] - grid->verts[j + offset].xyz[1]) > .1) continue; +			if ( fabs(grid->verts[i + offset].xyz[2] - grid->verts[j + offset].xyz[2]) > .1) continue; +			return qtrue; +		} +	} +	return qfalse; +} + +/* +================= +R_MergedHeightPoints + +returns true if there are grid points merged on a height edge +================= +*/ +int R_MergedHeightPoints(srfGridMesh_t *grid, int offset) { +	int i, j; + +	for (i = 1; i < grid->height-1; i++) { +		for (j = i + 1; j < grid->height-1; j++) { +			if ( fabs(grid->verts[grid->width * i + offset].xyz[0] - grid->verts[grid->width * j + offset].xyz[0]) > .1) continue; +			if ( fabs(grid->verts[grid->width * i + offset].xyz[1] - grid->verts[grid->width * j + offset].xyz[1]) > .1) continue; +			if ( fabs(grid->verts[grid->width * i + offset].xyz[2] - grid->verts[grid->width * j + offset].xyz[2]) > .1) continue; +			return qtrue; +		} +	} +	return qfalse; +} + +/* +================= +R_FixSharedVertexLodError_r + +NOTE: never sync LoD through grid edges with merged points! + +FIXME: write generalized version that also avoids cracks between a patch and one that meets half way? +================= +*/ +void R_FixSharedVertexLodError_r( int start, srfGridMesh_t *grid1 ) { +	int j, k, l, m, n, offset1, offset2, touch; +	srfGridMesh_t *grid2; + +	for ( j = start; j < s_worldData.numsurfaces; j++ ) { +		// +		grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; +		// if this surface is not a grid +		if ( grid2->surfaceType != SF_GRID ) continue; +		// if the LOD errors are already fixed for this patch +		if ( grid2->lodFixed == 2 ) continue; +		// grids in the same LOD group should have the exact same lod radius +		if ( grid1->lodRadius != grid2->lodRadius ) continue; +		// grids in the same LOD group should have the exact same lod origin +		if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; +		if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; +		if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; +		// +		touch = qfalse; +		for (n = 0; n < 2; n++) { +			// +			if (n) offset1 = (grid1->height-1) * grid1->width; +			else offset1 = 0; +			if (R_MergedWidthPoints(grid1, offset1)) continue; +			for (k = 1; k < grid1->width-1; k++) { +				for (m = 0; m < 2; m++) { + +					if (m) offset2 = (grid2->height-1) * grid2->width; +					else offset2 = 0; +					if (R_MergedWidthPoints(grid2, offset2)) continue; +					for ( l = 1; l < grid2->width-1; l++) { +					// +						if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; +						if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; +						if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; +						// ok the points are equal and should have the same lod error +						grid2->widthLodError[l] = grid1->widthLodError[k]; +						touch = qtrue; +					} +				} +				for (m = 0; m < 2; m++) { + +					if (m) offset2 = grid2->width-1; +					else offset2 = 0; +					if (R_MergedHeightPoints(grid2, offset2)) continue; +					for ( l = 1; l < grid2->height-1; l++) { +					// +						if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; +						if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; +						if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; +						// ok the points are equal and should have the same lod error +						grid2->heightLodError[l] = grid1->widthLodError[k]; +						touch = qtrue; +					} +				} +			} +		} +		for (n = 0; n < 2; n++) { +			// +			if (n) offset1 = grid1->width-1; +			else offset1 = 0; +			if (R_MergedHeightPoints(grid1, offset1)) continue; +			for (k = 1; k < grid1->height-1; k++) { +				for (m = 0; m < 2; m++) { + +					if (m) offset2 = (grid2->height-1) * grid2->width; +					else offset2 = 0; +					if (R_MergedWidthPoints(grid2, offset2)) continue; +					for ( l = 1; l < grid2->width-1; l++) { +					// +						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; +						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; +						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; +						// ok the points are equal and should have the same lod error +						grid2->widthLodError[l] = grid1->heightLodError[k]; +						touch = qtrue; +					} +				} +				for (m = 0; m < 2; m++) { + +					if (m) offset2 = grid2->width-1; +					else offset2 = 0; +					if (R_MergedHeightPoints(grid2, offset2)) continue; +					for ( l = 1; l < grid2->height-1; l++) { +					// +						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; +						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; +						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; +						// ok the points are equal and should have the same lod error +						grid2->heightLodError[l] = grid1->heightLodError[k]; +						touch = qtrue; +					} +				} +			} +		} +		if (touch) { +			grid2->lodFixed = 2; +			R_FixSharedVertexLodError_r ( start, grid2 ); +			//NOTE: this would be correct but makes things really slow +			//grid2->lodFixed = 1; +		} +	} +} + +/* +================= +R_FixSharedVertexLodError + +This function assumes that all patches in one group are nicely stitched together for the highest LoD. +If this is not the case this function will still do its job but won't fix the highest LoD cracks. +================= +*/ +void R_FixSharedVertexLodError( void ) { +	int i; +	srfGridMesh_t *grid1; + +	for ( i = 0; i < s_worldData.numsurfaces; i++ ) { +		// +		grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; +		// if this surface is not a grid +		if ( grid1->surfaceType != SF_GRID ) +			continue; +		// +		if ( grid1->lodFixed ) +			continue; +		// +		grid1->lodFixed = 2; +		// recursively fix other patches in the same LOD group +		R_FixSharedVertexLodError_r( i + 1, grid1); +	} +} + + +/* +=============== +R_StitchPatches +=============== +*/ +int R_StitchPatches( int grid1num, int grid2num ) { +	float *v1, *v2; +	srfGridMesh_t *grid1, *grid2; +	int k, l, m, n, offset1, offset2, row, column; + +	grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; +	grid2 = (srfGridMesh_t *) s_worldData.surfaces[grid2num].data; +	for (n = 0; n < 2; n++) { +		// +		if (n) offset1 = (grid1->height-1) * grid1->width; +		else offset1 = 0; +		if (R_MergedWidthPoints(grid1, offset1)) +			continue; +		for (k = 0; k < grid1->width-2; k += 2) { + +			for (m = 0; m < 2; m++) { + +				if ( grid2->width >= MAX_GRID_SIZE ) +					break; +				if (m) offset2 = (grid2->height-1) * grid2->width; +				else offset2 = 0; +				for ( l = 0; l < grid2->width-1; l++) { +				// +					v1 = grid1->verts[k + offset1].xyz; +					v2 = grid2->verts[l + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; + +					v1 = grid1->verts[k + 2 + offset1].xyz; +					v2 = grid2->verts[l + 1 + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; +					// +					v1 = grid2->verts[l + offset2].xyz; +					v2 = grid2->verts[l + 1 + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) < .01 && +							fabs(v1[1] - v2[1]) < .01 && +							fabs(v1[2] - v2[2]) < .01) +						continue; +					// +					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); +					// insert column into grid2 right after after column l +					if (m) row = grid2->height-1; +					else row = 0; +					grid2 = R_GridInsertColumn( grid2, l+1, row, +									grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); +					grid2->lodStitched = qfalse; +					s_worldData.surfaces[grid2num].data = (void *) grid2; +					return qtrue; +				} +			} +			for (m = 0; m < 2; m++) { + +				if (grid2->height >= MAX_GRID_SIZE) +					break; +				if (m) offset2 = grid2->width-1; +				else offset2 = 0; +				for ( l = 0; l < grid2->height-1; l++) { +					// +					v1 = grid1->verts[k + offset1].xyz; +					v2 = grid2->verts[grid2->width * l + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; + +					v1 = grid1->verts[k + 2 + offset1].xyz; +					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; +					// +					v1 = grid2->verts[grid2->width * l + offset2].xyz; +					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) < .01 && +							fabs(v1[1] - v2[1]) < .01 && +							fabs(v1[2] - v2[2]) < .01) +						continue; +					// +					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); +					// insert row into grid2 right after after row l +					if (m) column = grid2->width-1; +					else column = 0; +					grid2 = R_GridInsertRow( grid2, l+1, column, +										grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); +					grid2->lodStitched = qfalse; +					s_worldData.surfaces[grid2num].data = (void *) grid2; +					return qtrue; +				} +			} +		} +	} +	for (n = 0; n < 2; n++) { +		// +		if (n) offset1 = grid1->width-1; +		else offset1 = 0; +		if (R_MergedHeightPoints(grid1, offset1)) +			continue; +		for (k = 0; k < grid1->height-2; k += 2) { +			for (m = 0; m < 2; m++) { + +				if ( grid2->width >= MAX_GRID_SIZE ) +					break; +				if (m) offset2 = (grid2->height-1) * grid2->width; +				else offset2 = 0; +				for ( l = 0; l < grid2->width-1; l++) { +				// +					v1 = grid1->verts[grid1->width * k + offset1].xyz; +					v2 = grid2->verts[l + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; + +					v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; +					v2 = grid2->verts[l + 1 + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; +					// +					v1 = grid2->verts[l + offset2].xyz; +					v2 = grid2->verts[(l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) < .01 && +							fabs(v1[1] - v2[1]) < .01 && +							fabs(v1[2] - v2[2]) < .01) +						continue; +					// +					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); +					// insert column into grid2 right after after column l +					if (m) row = grid2->height-1; +					else row = 0; +					grid2 = R_GridInsertColumn( grid2, l+1, row, +									grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); +					grid2->lodStitched = qfalse; +					s_worldData.surfaces[grid2num].data = (void *) grid2; +					return qtrue; +				} +			} +			for (m = 0; m < 2; m++) { + +				if (grid2->height >= MAX_GRID_SIZE) +					break; +				if (m) offset2 = grid2->width-1; +				else offset2 = 0; +				for ( l = 0; l < grid2->height-1; l++) { +				// +					v1 = grid1->verts[grid1->width * k + offset1].xyz; +					v2 = grid2->verts[grid2->width * l + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; + +					v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; +					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; +					// +					v1 = grid2->verts[grid2->width * l + offset2].xyz; +					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) < .01 && +							fabs(v1[1] - v2[1]) < .01 && +							fabs(v1[2] - v2[2]) < .01) +						continue; +					// +					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); +					// insert row into grid2 right after after row l +					if (m) column = grid2->width-1; +					else column = 0; +					grid2 = R_GridInsertRow( grid2, l+1, column, +									grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); +					grid2->lodStitched = qfalse; +					s_worldData.surfaces[grid2num].data = (void *) grid2; +					return qtrue; +				} +			} +		} +	} +	for (n = 0; n < 2; n++) { +		// +		if (n) offset1 = (grid1->height-1) * grid1->width; +		else offset1 = 0; +		if (R_MergedWidthPoints(grid1, offset1)) +			continue; +		for (k = grid1->width-1; k > 1; k -= 2) { + +			for (m = 0; m < 2; m++) { + +				if ( grid2->width >= MAX_GRID_SIZE ) +					break; +				if (m) offset2 = (grid2->height-1) * grid2->width; +				else offset2 = 0; +				for ( l = 0; l < grid2->width-1; l++) { +				// +					v1 = grid1->verts[k + offset1].xyz; +					v2 = grid2->verts[l + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; + +					v1 = grid1->verts[k - 2 + offset1].xyz; +					v2 = grid2->verts[l + 1 + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; +					// +					v1 = grid2->verts[l + offset2].xyz; +					v2 = grid2->verts[(l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) < .01 && +							fabs(v1[1] - v2[1]) < .01 && +							fabs(v1[2] - v2[2]) < .01) +						continue; +					// +					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); +					// insert column into grid2 right after after column l +					if (m) row = grid2->height-1; +					else row = 0; +					grid2 = R_GridInsertColumn( grid2, l+1, row, +										grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); +					grid2->lodStitched = qfalse; +					s_worldData.surfaces[grid2num].data = (void *) grid2; +					return qtrue; +				} +			} +			for (m = 0; m < 2; m++) { + +				if (grid2->height >= MAX_GRID_SIZE) +					break; +				if (m) offset2 = grid2->width-1; +				else offset2 = 0; +				for ( l = 0; l < grid2->height-1; l++) { +				// +					v1 = grid1->verts[k + offset1].xyz; +					v2 = grid2->verts[grid2->width * l + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; + +					v1 = grid1->verts[k - 2 + offset1].xyz; +					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; +					// +					v1 = grid2->verts[grid2->width * l + offset2].xyz; +					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) < .01 && +							fabs(v1[1] - v2[1]) < .01 && +							fabs(v1[2] - v2[2]) < .01) +						continue; +					// +					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); +					// insert row into grid2 right after after row l +					if (m) column = grid2->width-1; +					else column = 0; +					grid2 = R_GridInsertRow( grid2, l+1, column, +										grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); +					if (!grid2) +						break; +					grid2->lodStitched = qfalse; +					s_worldData.surfaces[grid2num].data = (void *) grid2; +					return qtrue; +				} +			} +		} +	} +	for (n = 0; n < 2; n++) { +		// +		if (n) offset1 = grid1->width-1; +		else offset1 = 0; +		if (R_MergedHeightPoints(grid1, offset1)) +			continue; +		for (k = grid1->height-1; k > 1; k -= 2) { +			for (m = 0; m < 2; m++) { + +				if ( grid2->width >= MAX_GRID_SIZE ) +					break; +				if (m) offset2 = (grid2->height-1) * grid2->width; +				else offset2 = 0; +				for ( l = 0; l < grid2->width-1; l++) { +				// +					v1 = grid1->verts[grid1->width * k + offset1].xyz; +					v2 = grid2->verts[l + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; + +					v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; +					v2 = grid2->verts[l + 1 + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; +					// +					v1 = grid2->verts[l + offset2].xyz; +					v2 = grid2->verts[(l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) < .01 && +							fabs(v1[1] - v2[1]) < .01 && +							fabs(v1[2] - v2[2]) < .01) +						continue; +					// +					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); +					// insert column into grid2 right after after column l +					if (m) row = grid2->height-1; +					else row = 0; +					grid2 = R_GridInsertColumn( grid2, l+1, row, +										grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); +					grid2->lodStitched = qfalse; +					s_worldData.surfaces[grid2num].data = (void *) grid2; +					return qtrue; +				} +			} +			for (m = 0; m < 2; m++) { + +				if (grid2->height >= MAX_GRID_SIZE) +					break; +				if (m) offset2 = grid2->width-1; +				else offset2 = 0; +				for ( l = 0; l < grid2->height-1; l++) { +				// +					v1 = grid1->verts[grid1->width * k + offset1].xyz; +					v2 = grid2->verts[grid2->width * l + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; + +					v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; +					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) > .1) +						continue; +					if ( fabs(v1[1] - v2[1]) > .1) +						continue; +					if ( fabs(v1[2] - v2[2]) > .1) +						continue; +					// +					v1 = grid2->verts[grid2->width * l + offset2].xyz; +					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; +					if ( fabs(v1[0] - v2[0]) < .01 && +							fabs(v1[1] - v2[1]) < .01 && +							fabs(v1[2] - v2[2]) < .01) +						continue; +					// +					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); +					// insert row into grid2 right after after row l +					if (m) column = grid2->width-1; +					else column = 0; +					grid2 = R_GridInsertRow( grid2, l+1, column, +										grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); +					grid2->lodStitched = qfalse; +					s_worldData.surfaces[grid2num].data = (void *) grid2; +					return qtrue; +				} +			} +		} +	} +	return qfalse; +} + +/* +=============== +R_TryStitchPatch + +This function will try to stitch patches in the same LoD group together for the highest LoD. + +Only single missing vertice cracks will be fixed. + +Vertices will be joined at the patch side a crack is first found, at the other side +of the patch (on the same row or column) the vertices will not be joined and cracks +might still appear at that side. +=============== +*/ +int R_TryStitchingPatch( int grid1num ) { +	int j, numstitches; +	srfGridMesh_t *grid1, *grid2; + +	numstitches = 0; +	grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; +	for ( j = 0; j < s_worldData.numsurfaces; j++ ) { +		// +		grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; +		// if this surface is not a grid +		if ( grid2->surfaceType != SF_GRID ) continue; +		// grids in the same LOD group should have the exact same lod radius +		if ( grid1->lodRadius != grid2->lodRadius ) continue; +		// grids in the same LOD group should have the exact same lod origin +		if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; +		if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; +		if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; +		// +		while (R_StitchPatches(grid1num, j)) +		{ +			numstitches++; +		} +	} +	return numstitches; +} + +/* +=============== +R_StitchAllPatches +=============== +*/ +void R_StitchAllPatches( void ) { +	int i, stitched, numstitches; +	srfGridMesh_t *grid1; + +	numstitches = 0; +	do +	{ +		stitched = qfalse; +		for ( i = 0; i < s_worldData.numsurfaces; i++ ) { +			// +			grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; +			// if this surface is not a grid +			if ( grid1->surfaceType != SF_GRID ) +				continue; +			// +			if ( grid1->lodStitched ) +				continue; +			// +			grid1->lodStitched = qtrue; +			stitched = qtrue; +			// +			numstitches += R_TryStitchingPatch( i ); +		} +	} +	while (stitched); +	ri.Printf( PRINT_ALL, "stitched %d LoD cracks\n", numstitches ); +} + +/* +=============== +R_MovePatchSurfacesToHunk +=============== +*/ +void R_MovePatchSurfacesToHunk(void) { +	int i, size; +	srfGridMesh_t *grid, *hunkgrid; + +	for ( i = 0; i < s_worldData.numsurfaces; i++ ) { +		// +		grid = (srfGridMesh_t *) s_worldData.surfaces[i].data; +		// if this surface is not a grid +		if ( grid->surfaceType != SF_GRID ) +			continue; +		// +		size = (grid->width * grid->height - 1) * sizeof( drawVert_t ) + sizeof( *grid ); +		hunkgrid = ri.Hunk_Alloc( size, h_low ); +		Com_Memcpy(hunkgrid, grid, size); + +		hunkgrid->widthLodError = ri.Hunk_Alloc( grid->width * 4, h_low ); +		Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 ); + +		hunkgrid->heightLodError = ri.Hunk_Alloc( grid->height * 4, h_low ); +		Com_Memcpy( grid->heightLodError, grid->heightLodError, grid->height * 4 ); + +		R_FreeSurfaceGridMesh( grid ); + +		s_worldData.surfaces[i].data = (void *) hunkgrid; +	} +} + +/* +=============== +R_LoadSurfaces +=============== +*/ +static	void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) { +	dsurface_t	*in; +	msurface_t	*out; +	drawVert_t	*dv; +	int			*indexes; +	int			count; +	int			numFaces, numMeshes, numTriSurfs, numFlares; +	int			i; + +	numFaces = 0; +	numMeshes = 0; +	numTriSurfs = 0; +	numFlares = 0; + +	in = (void *)(fileBase + surfs->fileofs); +	if (surfs->filelen % sizeof(*in)) +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	count = surfs->filelen / sizeof(*in); + +	dv = (void *)(fileBase + verts->fileofs); +	if (verts->filelen % sizeof(*dv)) +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + +	indexes = (void *)(fileBase + indexLump->fileofs); +	if ( indexLump->filelen % sizeof(*indexes)) +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + +	out = ri.Hunk_Alloc ( count * sizeof(*out), h_low );	 + +	s_worldData.surfaces = out; +	s_worldData.numsurfaces = count; + +	for ( i = 0 ; i < count ; i++, in++, out++ ) { +		switch ( LittleLong( in->surfaceType ) ) { +		case MST_PATCH: +			ParseMesh ( in, dv, out ); +			numMeshes++; +			break; +		case MST_TRIANGLE_SOUP: +			ParseTriSurf( in, dv, out, indexes ); +			numTriSurfs++; +			break; +		case MST_PLANAR: +			ParseFace( in, dv, out, indexes ); +			numFaces++; +			break; +		case MST_FLARE: +			ParseFlare( in, dv, out, indexes ); +			numFlares++; +			break; +		default: +			ri.Error( ERR_DROP, "Bad surfaceType" ); +		} +	} + +#ifdef PATCH_STITCHING +	R_StitchAllPatches(); +#endif + +	R_FixSharedVertexLodError(); + +#ifdef PATCH_STITCHING +	R_MovePatchSurfacesToHunk(); +#endif + +	ri.Printf( PRINT_ALL, "...loaded %d faces, %i meshes, %i trisurfs, %i flares\n",  +		numFaces, numMeshes, numTriSurfs, numFlares ); +} + + + +/* +================= +R_LoadSubmodels +================= +*/ +static	void R_LoadSubmodels( lump_t *l ) { +	dmodel_t	*in; +	bmodel_t	*out; +	int			i, j, count; + +	in = (void *)(fileBase + l->fileofs); +	if (l->filelen % sizeof(*in)) +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	count = l->filelen / sizeof(*in); + +	s_worldData.bmodels = out = ri.Hunk_Alloc( count * sizeof(*out), h_low ); + +	for ( i=0 ; i<count ; i++, in++, out++ ) { +		model_t *model; + +		model = R_AllocModel(); + +		assert( model != NULL );			// this should never happen + +		model->type = MOD_BRUSH; +		model->bmodel = out; +		Com_sprintf( model->name, sizeof( model->name ), "*%d", i ); + +		for (j=0 ; j<3 ; j++) { +			out->bounds[0][j] = LittleFloat (in->mins[j]); +			out->bounds[1][j] = LittleFloat (in->maxs[j]); +		} + +		out->firstSurface = s_worldData.surfaces + LittleLong( in->firstSurface ); +		out->numSurfaces = LittleLong( in->numSurfaces ); +	} +} + + + +//================================================================== + +/* +================= +R_SetParent +================= +*/ +static	void R_SetParent (mnode_t *node, mnode_t *parent) +{ +	node->parent = parent; +	if (node->contents != -1) +		return; +	R_SetParent (node->children[0], node); +	R_SetParent (node->children[1], node); +} + +/* +================= +R_LoadNodesAndLeafs +================= +*/ +static	void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { +	int			i, j, p; +	dnode_t		*in; +	dleaf_t		*inLeaf; +	mnode_t 	*out; +	int			numNodes, numLeafs; + +	in = (void *)(fileBase + nodeLump->fileofs); +	if (nodeLump->filelen % sizeof(dnode_t) || +		leafLump->filelen % sizeof(dleaf_t) ) { +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	} +	numNodes = nodeLump->filelen / sizeof(dnode_t); +	numLeafs = leafLump->filelen / sizeof(dleaf_t); + +	out = ri.Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low);	 + +	s_worldData.nodes = out; +	s_worldData.numnodes = numNodes + numLeafs; +	s_worldData.numDecisionNodes = numNodes; + +	// load nodes +	for ( i=0 ; i<numNodes; i++, in++, out++) +	{ +		for (j=0 ; j<3 ; j++) +		{ +			out->mins[j] = LittleLong (in->mins[j]); +			out->maxs[j] = LittleLong (in->maxs[j]); +		} +	 +		p = LittleLong(in->planeNum); +		out->plane = s_worldData.planes + p; + +		out->contents = CONTENTS_NODE;	// differentiate from leafs + +		for (j=0 ; j<2 ; j++) +		{ +			p = LittleLong (in->children[j]); +			if (p >= 0) +				out->children[j] = s_worldData.nodes + p; +			else +				out->children[j] = s_worldData.nodes + numNodes + (-1 - p); +		} +	} +	 +	// load leafs +	inLeaf = (void *)(fileBase + leafLump->fileofs); +	for ( i=0 ; i<numLeafs ; i++, inLeaf++, out++) +	{ +		for (j=0 ; j<3 ; j++) +		{ +			out->mins[j] = LittleLong (inLeaf->mins[j]); +			out->maxs[j] = LittleLong (inLeaf->maxs[j]); +		} + +		out->cluster = LittleLong(inLeaf->cluster); +		out->area = LittleLong(inLeaf->area); + +		if ( out->cluster >= s_worldData.numClusters ) { +			s_worldData.numClusters = out->cluster + 1; +		} + +		out->firstmarksurface = s_worldData.marksurfaces + +			LittleLong(inLeaf->firstLeafSurface); +		out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces); +	}	 + +	// chain decendants +	R_SetParent (s_worldData.nodes, NULL); +} + +//============================================================================= + +/* +================= +R_LoadShaders +================= +*/ +static	void R_LoadShaders( lump_t *l ) {	 +	int		i, count; +	dshader_t	*in, *out; +	 +	in = (void *)(fileBase + l->fileofs); +	if (l->filelen % sizeof(*in)) +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	count = l->filelen / sizeof(*in); +	out = ri.Hunk_Alloc ( count*sizeof(*out), h_low ); + +	s_worldData.shaders = out; +	s_worldData.numShaders = count; + +	Com_Memcpy( out, in, count*sizeof(*out) ); + +	for ( i=0 ; i<count ; i++ ) { +		out[i].surfaceFlags = LittleLong( out[i].surfaceFlags ); +		out[i].contentFlags = LittleLong( out[i].contentFlags ); +	} +} + + +/* +================= +R_LoadMarksurfaces +================= +*/ +static	void R_LoadMarksurfaces (lump_t *l) +{	 +	int		i, j, count; +	int		*in; +	msurface_t **out; +	 +	in = (void *)(fileBase + l->fileofs); +	if (l->filelen % sizeof(*in)) +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	count = l->filelen / sizeof(*in); +	out = ri.Hunk_Alloc ( count*sizeof(*out), h_low);	 + +	s_worldData.marksurfaces = out; +	s_worldData.nummarksurfaces = count; + +	for ( i=0 ; i<count ; i++) +	{ +		j = LittleLong(in[i]); +		out[i] = s_worldData.surfaces + j; +	} +} + + +/* +================= +R_LoadPlanes +================= +*/ +static	void R_LoadPlanes( lump_t *l ) { +	int			i, j; +	cplane_t	*out; +	dplane_t 	*in; +	int			count; +	int			bits; +	 +	in = (void *)(fileBase + l->fileofs); +	if (l->filelen % sizeof(*in)) +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	count = l->filelen / sizeof(*in); +	out = ri.Hunk_Alloc ( count*2*sizeof(*out), h_low);	 +	 +	s_worldData.planes = out; +	s_worldData.numplanes = count; + +	for ( i=0 ; i<count ; i++, in++, out++) { +		bits = 0; +		for (j=0 ; j<3 ; j++) { +			out->normal[j] = LittleFloat (in->normal[j]); +			if (out->normal[j] < 0) { +				bits |= 1<<j; +			} +		} + +		out->dist = LittleFloat (in->dist); +		out->type = PlaneTypeForNormal( out->normal ); +		out->signbits = bits; +	} +} + +/* +================= +R_LoadFogs + +================= +*/ +static	void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) { +	int			i; +	fog_t		*out; +	dfog_t		*fogs; +	dbrush_t 	*brushes, *brush; +	dbrushside_t	*sides; +	int			count, brushesCount, sidesCount; +	int			sideNum; +	int			planeNum; +	shader_t	*shader; +	float		d; +	int			firstSide; + +	fogs = (void *)(fileBase + l->fileofs); +	if (l->filelen % sizeof(*fogs)) { +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	} +	count = l->filelen / sizeof(*fogs); + +	// create fog strucutres for them +	s_worldData.numfogs = count + 1; +	s_worldData.fogs = ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low); +	out = s_worldData.fogs + 1; + +	if ( !count ) { +		return; +	} + +	brushes = (void *)(fileBase + brushesLump->fileofs); +	if (brushesLump->filelen % sizeof(*brushes)) { +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	} +	brushesCount = brushesLump->filelen / sizeof(*brushes); + +	sides = (void *)(fileBase + sidesLump->fileofs); +	if (sidesLump->filelen % sizeof(*sides)) { +		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); +	} +	sidesCount = sidesLump->filelen / sizeof(*sides); + +	for ( i=0 ; i<count ; i++, fogs++) { +		out->originalBrushNumber = LittleLong( fogs->brushNum ); + +		if ( (unsigned)out->originalBrushNumber >= brushesCount ) { +			ri.Error( ERR_DROP, "fog brushNumber out of range" ); +		} +		brush = brushes + out->originalBrushNumber; + +		firstSide = LittleLong( brush->firstSide ); + +			if ( (unsigned)firstSide > sidesCount - 6 ) { +			ri.Error( ERR_DROP, "fog brush sideNumber out of range" ); +		} + +		// brushes are always sorted with the axial sides first +		sideNum = firstSide + 0; +		planeNum = LittleLong( sides[ sideNum ].planeNum ); +		out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist; + +		sideNum = firstSide + 1; +		planeNum = LittleLong( sides[ sideNum ].planeNum ); +		out->bounds[1][0] = s_worldData.planes[ planeNum ].dist; + +		sideNum = firstSide + 2; +		planeNum = LittleLong( sides[ sideNum ].planeNum ); +		out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist; + +		sideNum = firstSide + 3; +		planeNum = LittleLong( sides[ sideNum ].planeNum ); +		out->bounds[1][1] = s_worldData.planes[ planeNum ].dist; + +		sideNum = firstSide + 4; +		planeNum = LittleLong( sides[ sideNum ].planeNum ); +		out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist; + +		sideNum = firstSide + 5; +		planeNum = LittleLong( sides[ sideNum ].planeNum ); +		out->bounds[1][2] = s_worldData.planes[ planeNum ].dist; + +		// get information from the shader for fog parameters +		shader = R_FindShader( fogs->shader, LIGHTMAP_NONE, qtrue ); + +		out->parms = shader->fogParms; + +		out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight,  +			                          shader->fogParms.color[1] * tr.identityLight,  +			                          shader->fogParms.color[2] * tr.identityLight, 1.0 ); + +		d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque; +		out->tcScale = 1.0f / ( d * 8 ); + +		// set the gradient vector +		sideNum = LittleLong( fogs->visibleSide ); + +		if ( sideNum == -1 ) { +			out->hasSurface = qfalse; +		} else { +			out->hasSurface = qtrue; +			planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); +			VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); +			out->surface[3] = -s_worldData.planes[ planeNum ].dist; +		} + +		out++; +	} + +} + + +/* +================ +R_LoadLightGrid + +================ +*/ +void R_LoadLightGrid( lump_t *l ) { +	int		i; +	vec3_t	maxs; +	int		numGridPoints; +	world_t	*w; +	float	*wMins, *wMaxs; + +	w = &s_worldData; + +	w->lightGridInverseSize[0] = 1.0f / w->lightGridSize[0]; +	w->lightGridInverseSize[1] = 1.0f / w->lightGridSize[1]; +	w->lightGridInverseSize[2] = 1.0f / w->lightGridSize[2]; + +	wMins = w->bmodels[0].bounds[0]; +	wMaxs = w->bmodels[0].bounds[1]; + +	for ( i = 0 ; i < 3 ; i++ ) { +		w->lightGridOrigin[i] = w->lightGridSize[i] * ceil( wMins[i] / w->lightGridSize[i] ); +		maxs[i] = w->lightGridSize[i] * floor( wMaxs[i] / w->lightGridSize[i] ); +		w->lightGridBounds[i] = (maxs[i] - w->lightGridOrigin[i])/w->lightGridSize[i] + 1; +	} + +	numGridPoints = w->lightGridBounds[0] * w->lightGridBounds[1] * w->lightGridBounds[2]; + +	if ( l->filelen != numGridPoints * 8 ) { +		ri.Printf( PRINT_WARNING, "WARNING: light grid mismatch\n" ); +		w->lightGridData = NULL; +		return; +	} + +	w->lightGridData = ri.Hunk_Alloc( l->filelen, h_low ); +	Com_Memcpy( w->lightGridData, (void *)(fileBase + l->fileofs), l->filelen ); + +	// deal with overbright bits +	for ( i = 0 ; i < numGridPoints ; i++ ) { +		R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] ); +		R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] ); +	} +} + +/* +================ +R_LoadEntities +================ +*/ +void R_LoadEntities( lump_t *l ) { +	char *p, *token, *s; +	char keyname[MAX_TOKEN_CHARS]; +	char value[MAX_TOKEN_CHARS]; +	world_t	*w; + +	w = &s_worldData; +	w->lightGridSize[0] = 64; +	w->lightGridSize[1] = 64; +	w->lightGridSize[2] = 128; + +	p = (char *)(fileBase + l->fileofs); + +	// store for reference by the cgame +	w->entityString = ri.Hunk_Alloc( l->filelen + 1, h_low ); +	strcpy( w->entityString, p ); +	w->entityParsePoint = w->entityString; + +	token = COM_ParseExt( &p, qtrue ); +	if (!*token || *token != '{') { +		return; +	} + +	// only parse the world spawn +	while ( 1 ) {	 +		// parse key +		token = COM_ParseExt( &p, qtrue ); + +		if ( !*token || *token == '}' ) { +			break; +		} +		Q_strncpyz(keyname, token, sizeof(keyname)); + +		// parse value +		token = COM_ParseExt( &p, qtrue ); + +		if ( !*token || *token == '}' ) { +			break; +		} +		Q_strncpyz(value, token, sizeof(value)); + +		// check for remapping of shaders for vertex lighting +		s = "vertexremapshader"; +		if (!Q_strncmp(keyname, s, strlen(s)) ) { +			s = strchr(value, ';'); +			if (!s) { +				ri.Printf( PRINT_WARNING, "WARNING: no semi colon in vertexshaderremap '%s'\n", value ); +				break; +			} +			*s++ = 0; +			if (r_vertexLight->integer) { +				R_RemapShader(value, s, "0"); +			} +			continue; +		} +		// check for remapping of shaders +		s = "remapshader"; +		if (!Q_strncmp(keyname, s, strlen(s)) ) { +			s = strchr(value, ';'); +			if (!s) { +				ri.Printf( PRINT_WARNING, "WARNING: no semi colon in shaderremap '%s'\n", value ); +				break; +			} +			*s++ = 0; +			R_RemapShader(value, s, "0"); +			continue; +		} +		// check for a different grid size +		if (!Q_stricmp(keyname, "gridsize")) { +			sscanf(value, "%f %f %f", &w->lightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] ); +			continue; +		} +	} +} + +/* +================= +R_GetEntityToken +================= +*/ +qboolean R_GetEntityToken( char *buffer, int size ) { +	const char	*s; + +	s = COM_Parse( &s_worldData.entityParsePoint ); +	Q_strncpyz( buffer, s, size ); +	if ( !s_worldData.entityParsePoint || !s[0] ) { +		s_worldData.entityParsePoint = s_worldData.entityString; +		return qfalse; +	} else { +		return qtrue; +	} +} + +/* +================= +RE_LoadWorldMap + +Called directly from cgame +================= +*/ +void RE_LoadWorldMap( const char *name ) { +	int			i; +	dheader_t	*header; +	byte		*buffer; +	byte		*startMarker; + +	if ( tr.worldMapLoaded ) { +		ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map\n" ); +	} + +	// set default sun direction to be used if it isn't +	// overridden by a shader +	tr.sunDirection[0] = 0.45f; +	tr.sunDirection[1] = 0.3f; +	tr.sunDirection[2] = 0.9f; + +	VectorNormalize( tr.sunDirection ); + +	tr.worldMapLoaded = qtrue; + +	// load it +    ri.FS_ReadFile( name, (void **)&buffer ); +	if ( !buffer ) { +		ri.Error (ERR_DROP, "RE_LoadWorldMap: %s not found", name); +	} + +	// clear tr.world so if the level fails to load, the next +	// try will not look at the partially loaded version +	tr.world = NULL; + +	Com_Memset( &s_worldData, 0, sizeof( s_worldData ) ); +	Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) ); + +	Q_strncpyz( s_worldData.baseName, COM_SkipPath( s_worldData.name ), sizeof( s_worldData.name ) ); +	COM_StripExtension( s_worldData.baseName, s_worldData.baseName ); + +	startMarker = ri.Hunk_Alloc(0, h_low); +	c_gridVerts = 0; + +	header = (dheader_t *)buffer; +	fileBase = (byte *)header; + +	i = LittleLong (header->version); +	if ( i != BSP_VERSION ) { +		ri.Error (ERR_DROP, "RE_LoadWorldMap: %s has wrong version number (%i should be %i)",  +			name, i, BSP_VERSION); +	} + +	// swap all the lumps +	for (i=0 ; i<sizeof(dheader_t)/4 ; i++) { +		((int *)header)[i] = LittleLong ( ((int *)header)[i]); +	} + +	// load into heap +	R_LoadShaders( &header->lumps[LUMP_SHADERS] ); +	R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] ); +	R_LoadPlanes (&header->lumps[LUMP_PLANES]); +	R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] ); +	R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header->lumps[LUMP_DRAWINDEXES] ); +	R_LoadMarksurfaces (&header->lumps[LUMP_LEAFSURFACES]); +	R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]); +	R_LoadSubmodels (&header->lumps[LUMP_MODELS]); +	R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] ); +	R_LoadEntities( &header->lumps[LUMP_ENTITIES] ); +	R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] ); + +	s_worldData.dataSize = (byte *)ri.Hunk_Alloc(0, h_low) - startMarker; + +	// only set tr.world now that we know the entire level has loaded properly +	tr.world = &s_worldData; + +    ri.FS_FreeFile( buffer ); +} + | 
