Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

tr_bsp.c (51485B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // tr_map.c
     23 
     24 #include "tr_local.h"
     25 
     26 /*
     27 
     28 Loads and prepares a map file for scene rendering.
     29 
     30 A single entry point:
     31 
     32 void RE_LoadWorldMap( const char *name );
     33 
     34 */
     35 
     36 static	world_t		s_worldData;
     37 static	byte		*fileBase;
     38 
     39 int			c_subdivisions;
     40 int			c_gridVerts;
     41 
     42 //===============================================================================
     43 
     44 static void HSVtoRGB( float h, float s, float v, float rgb[3] )
     45 {
     46 	int i;
     47 	float f;
     48 	float p, q, t;
     49 
     50 	h *= 5;
     51 
     52 	i = floor( h );
     53 	f = h - i;
     54 
     55 	p = v * ( 1 - s );
     56 	q = v * ( 1 - s * f );
     57 	t = v * ( 1 - s * ( 1 - f ) );
     58 
     59 	switch ( i )
     60 	{
     61 	case 0:
     62 		rgb[0] = v;
     63 		rgb[1] = t;
     64 		rgb[2] = p;
     65 		break;
     66 	case 1:
     67 		rgb[0] = q;
     68 		rgb[1] = v;
     69 		rgb[2] = p;
     70 		break;
     71 	case 2:
     72 		rgb[0] = p;
     73 		rgb[1] = v;
     74 		rgb[2] = t;
     75 		break;
     76 	case 3:
     77 		rgb[0] = p;
     78 		rgb[1] = q;
     79 		rgb[2] = v;
     80 		break;
     81 	case 4:
     82 		rgb[0] = t;
     83 		rgb[1] = p;
     84 		rgb[2] = v;
     85 		break;
     86 	case 5:
     87 		rgb[0] = v;
     88 		rgb[1] = p;
     89 		rgb[2] = q;
     90 		break;
     91 	}
     92 }
     93 
     94 /*
     95 ===============
     96 R_ColorShiftLightingBytes
     97 
     98 ===============
     99 */
    100 static	void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) {
    101 	int		shift, r, g, b;
    102 
    103 	// shift the color data based on overbright range
    104 	shift = r_mapOverBrightBits->integer - tr.overbrightBits;
    105 
    106 	// shift the data based on overbright range
    107 	r = in[0] << shift;
    108 	g = in[1] << shift;
    109 	b = in[2] << shift;
    110 	
    111 	// normalize by color instead of saturating to white
    112 	if ( ( r | g | b ) > 255 ) {
    113 		int		max;
    114 
    115 		max = r > g ? r : g;
    116 		max = max > b ? max : b;
    117 		r = r * 255 / max;
    118 		g = g * 255 / max;
    119 		b = b * 255 / max;
    120 	}
    121 
    122 	out[0] = r;
    123 	out[1] = g;
    124 	out[2] = b;
    125 	out[3] = in[3];
    126 }
    127 
    128 /*
    129 ===============
    130 R_LoadLightmaps
    131 
    132 ===============
    133 */
    134 #define	LIGHTMAP_SIZE	128
    135 static	void R_LoadLightmaps( lump_t *l ) {
    136 	byte		*buf, *buf_p;
    137 	int			len;
    138 	MAC_STATIC byte		image[LIGHTMAP_SIZE*LIGHTMAP_SIZE*4];
    139 	int			i, j;
    140 	float maxIntensity = 0;
    141 	double sumIntensity = 0;
    142 
    143     len = l->filelen;
    144 	if ( !len ) {
    145 		return;
    146 	}
    147 	buf = fileBase + l->fileofs;
    148 
    149 	// we are about to upload textures
    150 	R_SyncRenderThread();
    151 
    152 	// create all the lightmaps
    153 	tr.numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3);
    154 	if ( tr.numLightmaps == 1 ) {
    155 		//FIXME: HACK: maps with only one lightmap turn up fullbright for some reason.
    156 		//this avoids this, but isn't the correct solution.
    157 		tr.numLightmaps++;
    158 	}
    159 
    160 	// if we are in r_vertexLight mode, we don't need the lightmaps at all
    161 	if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) {
    162 		return;
    163 	}
    164 
    165 	for ( i = 0 ; i < tr.numLightmaps ; i++ ) {
    166 		// expand the 24 bit on-disk to 32 bit
    167 		buf_p = buf + i * LIGHTMAP_SIZE*LIGHTMAP_SIZE * 3;
    168 
    169 		if ( r_lightmap->integer == 2 )
    170 		{	// color code by intensity as development tool	(FIXME: check range)
    171 			for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ )
    172 			{
    173 				float r = buf_p[j*3+0];
    174 				float g = buf_p[j*3+1];
    175 				float b = buf_p[j*3+2];
    176 				float intensity;
    177 				float out[3];
    178 
    179 				intensity = 0.33f * r + 0.685f * g + 0.063f * b;
    180 
    181 				if ( intensity > 255 )
    182 					intensity = 1.0f;
    183 				else
    184 					intensity /= 255.0f;
    185 
    186 				if ( intensity > maxIntensity )
    187 					maxIntensity = intensity;
    188 
    189 				HSVtoRGB( intensity, 1.00, 0.50, out );
    190 
    191 				image[j*4+0] = out[0] * 255;
    192 				image[j*4+1] = out[1] * 255;
    193 				image[j*4+2] = out[2] * 255;
    194 				image[j*4+3] = 255;
    195 
    196 				sumIntensity += intensity;
    197 			}
    198 		} else {
    199 			for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) {
    200 				R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] );
    201 				image[j*4+3] = 255;
    202 			}
    203 		}
    204 		tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, 
    205 			LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP );
    206 	}
    207 
    208 	if ( r_lightmap->integer == 2 )	{
    209 		ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) );
    210 	}
    211 }
    212 
    213 
    214 /*
    215 =================
    216 RE_SetWorldVisData
    217 
    218 This is called by the clipmodel subsystem so we can share the 1.8 megs of
    219 space in big maps...
    220 =================
    221 */
    222 void		RE_SetWorldVisData( const byte *vis ) {
    223 	tr.externalVisData = vis;
    224 }
    225 
    226 
    227 /*
    228 =================
    229 R_LoadVisibility
    230 =================
    231 */
    232 static	void R_LoadVisibility( lump_t *l ) {
    233 	int		len;
    234 	byte	*buf;
    235 
    236 	len = ( s_worldData.numClusters + 63 ) & ~63;
    237 	s_worldData.novis = ri.Hunk_Alloc( len, h_low );
    238 	Com_Memset( s_worldData.novis, 0xff, len );
    239 
    240     len = l->filelen;
    241 	if ( !len ) {
    242 		return;
    243 	}
    244 	buf = fileBase + l->fileofs;
    245 
    246 	s_worldData.numClusters = LittleLong( ((int *)buf)[0] );
    247 	s_worldData.clusterBytes = LittleLong( ((int *)buf)[1] );
    248 
    249 	// CM_Load should have given us the vis data to share, so
    250 	// we don't need to allocate another copy
    251 	if ( tr.externalVisData ) {
    252 		s_worldData.vis = tr.externalVisData;
    253 	} else {
    254 		byte	*dest;
    255 
    256 		dest = ri.Hunk_Alloc( len - 8, h_low );
    257 		Com_Memcpy( dest, buf + 8, len - 8 );
    258 		s_worldData.vis = dest;
    259 	}
    260 }
    261 
    262 //===============================================================================
    263 
    264 
    265 /*
    266 ===============
    267 ShaderForShaderNum
    268 ===============
    269 */
    270 static shader_t *ShaderForShaderNum( int shaderNum, int lightmapNum ) {
    271 	shader_t	*shader;
    272 	dshader_t	*dsh;
    273 
    274 	shaderNum = LittleLong( shaderNum );
    275 	if ( shaderNum < 0 || shaderNum >= s_worldData.numShaders ) {
    276 		ri.Error( ERR_DROP, "ShaderForShaderNum: bad num %i", shaderNum );
    277 	}
    278 	dsh = &s_worldData.shaders[ shaderNum ];
    279 
    280 	if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) {
    281 		lightmapNum = LIGHTMAP_BY_VERTEX;
    282 	}
    283 
    284 	if ( r_fullbright->integer ) {
    285 		lightmapNum = LIGHTMAP_WHITEIMAGE;
    286 	}
    287 
    288 	shader = R_FindShader( dsh->shader, lightmapNum, qtrue );
    289 
    290 	// if the shader had errors, just use default shader
    291 	if ( shader->defaultShader ) {
    292 		return tr.defaultShader;
    293 	}
    294 
    295 	return shader;
    296 }
    297 
    298 /*
    299 ===============
    300 ParseFace
    301 ===============
    302 */
    303 static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes  ) {
    304 	int			i, j;
    305 	srfSurfaceFace_t	*cv;
    306 	int			numPoints, numIndexes;
    307 	int			lightmapNum;
    308 	int			sfaceSize, ofsIndexes;
    309 
    310 	lightmapNum = LittleLong( ds->lightmapNum );
    311 
    312 	// get fog volume
    313 	surf->fogIndex = LittleLong( ds->fogNum ) + 1;
    314 
    315 	// get shader value
    316 	surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum );
    317 	if ( r_singleShader->integer && !surf->shader->isSky ) {
    318 		surf->shader = tr.defaultShader;
    319 	}
    320 
    321 	numPoints = LittleLong( ds->numVerts );
    322 	if (numPoints > MAX_FACE_POINTS) {
    323 		ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numPoints);
    324     numPoints = MAX_FACE_POINTS;
    325     surf->shader = tr.defaultShader;
    326 	}
    327 
    328 	numIndexes = LittleLong( ds->numIndexes );
    329 
    330 	// create the srfSurfaceFace_t
    331 	sfaceSize = ( int ) &((srfSurfaceFace_t *)0)->points[numPoints];
    332 	ofsIndexes = sfaceSize;
    333 	sfaceSize += sizeof( int ) * numIndexes;
    334 
    335 	cv = ri.Hunk_Alloc( sfaceSize, h_low );
    336 	cv->surfaceType = SF_FACE;
    337 	cv->numPoints = numPoints;
    338 	cv->numIndices = numIndexes;
    339 	cv->ofsIndices = ofsIndexes;
    340 
    341 	verts += LittleLong( ds->firstVert );
    342 	for ( i = 0 ; i < numPoints ; i++ ) {
    343 		for ( j = 0 ; j < 3 ; j++ ) {
    344 			cv->points[i][j] = LittleFloat( verts[i].xyz[j] );
    345 		}
    346 		for ( j = 0 ; j < 2 ; j++ ) {
    347 			cv->points[i][3+j] = LittleFloat( verts[i].st[j] );
    348 			cv->points[i][5+j] = LittleFloat( verts[i].lightmap[j] );
    349 		}
    350 		R_ColorShiftLightingBytes( verts[i].color, (byte *)&cv->points[i][7] );
    351 	}
    352 
    353 	indexes += LittleLong( ds->firstIndex );
    354 	for ( i = 0 ; i < numIndexes ; i++ ) {
    355 		((int *)((byte *)cv + cv->ofsIndices ))[i] = LittleLong( indexes[ i ] );
    356 	}
    357 
    358 	// take the plane information from the lightmap vector
    359 	for ( i = 0 ; i < 3 ; i++ ) {
    360 		cv->plane.normal[i] = LittleFloat( ds->lightmapVecs[2][i] );
    361 	}
    362 	cv->plane.dist = DotProduct( cv->points[0], cv->plane.normal );
    363 	SetPlaneSignbits( &cv->plane );
    364 	cv->plane.type = PlaneTypeForNormal( cv->plane.normal );
    365 
    366 	surf->data = (surfaceType_t *)cv;
    367 }
    368 
    369 
    370 /*
    371 ===============
    372 ParseMesh
    373 ===============
    374 */
    375 static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, msurface_t *surf ) {
    376 	srfGridMesh_t	*grid;
    377 	int				i, j;
    378 	int				width, height, numPoints;
    379 	MAC_STATIC drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE];
    380 	int				lightmapNum;
    381 	vec3_t			bounds[2];
    382 	vec3_t			tmpVec;
    383 	static surfaceType_t	skipData = SF_SKIP;
    384 
    385 	lightmapNum = LittleLong( ds->lightmapNum );
    386 
    387 	// get fog volume
    388 	surf->fogIndex = LittleLong( ds->fogNum ) + 1;
    389 
    390 	// get shader value
    391 	surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum );
    392 	if ( r_singleShader->integer && !surf->shader->isSky ) {
    393 		surf->shader = tr.defaultShader;
    394 	}
    395 
    396 	// we may have a nodraw surface, because they might still need to
    397 	// be around for movement clipping
    398 	if ( s_worldData.shaders[ LittleLong( ds->shaderNum ) ].surfaceFlags & SURF_NODRAW ) {
    399 		surf->data = &skipData;
    400 		return;
    401 	}
    402 
    403 	width = LittleLong( ds->patchWidth );
    404 	height = LittleLong( ds->patchHeight );
    405 
    406 	verts += LittleLong( ds->firstVert );
    407 	numPoints = width * height;
    408 	for ( i = 0 ; i < numPoints ; i++ ) {
    409 		for ( j = 0 ; j < 3 ; j++ ) {
    410 			points[i].xyz[j] = LittleFloat( verts[i].xyz[j] );
    411 			points[i].normal[j] = LittleFloat( verts[i].normal[j] );
    412 		}
    413 		for ( j = 0 ; j < 2 ; j++ ) {
    414 			points[i].st[j] = LittleFloat( verts[i].st[j] );
    415 			points[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] );
    416 		}
    417 		R_ColorShiftLightingBytes( verts[i].color, points[i].color );
    418 	}
    419 
    420 	// pre-tesseleate
    421 	grid = R_SubdividePatchToGrid( width, height, points );
    422 	surf->data = (surfaceType_t *)grid;
    423 
    424 	// copy the level of detail origin, which is the center
    425 	// of the group of all curves that must subdivide the same
    426 	// to avoid cracking
    427 	for ( i = 0 ; i < 3 ; i++ ) {
    428 		bounds[0][i] = LittleFloat( ds->lightmapVecs[0][i] );
    429 		bounds[1][i] = LittleFloat( ds->lightmapVecs[1][i] );
    430 	}
    431 	VectorAdd( bounds[0], bounds[1], bounds[1] );
    432 	VectorScale( bounds[1], 0.5f, grid->lodOrigin );
    433 	VectorSubtract( bounds[0], grid->lodOrigin, tmpVec );
    434 	grid->lodRadius = VectorLength( tmpVec );
    435 }
    436 
    437 /*
    438 ===============
    439 ParseTriSurf
    440 ===============
    441 */
    442 static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) {
    443 	srfTriangles_t	*tri;
    444 	int				i, j;
    445 	int				numVerts, numIndexes;
    446 
    447 	// get fog volume
    448 	surf->fogIndex = LittleLong( ds->fogNum ) + 1;
    449 
    450 	// get shader
    451 	surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX );
    452 	if ( r_singleShader->integer && !surf->shader->isSky ) {
    453 		surf->shader = tr.defaultShader;
    454 	}
    455 
    456 	numVerts = LittleLong( ds->numVerts );
    457 	numIndexes = LittleLong( ds->numIndexes );
    458 
    459 	tri = ri.Hunk_Alloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) 
    460 		+ numIndexes * sizeof( tri->indexes[0] ), h_low );
    461 	tri->surfaceType = SF_TRIANGLES;
    462 	tri->numVerts = numVerts;
    463 	tri->numIndexes = numIndexes;
    464 	tri->verts = (drawVert_t *)(tri + 1);
    465 	tri->indexes = (int *)(tri->verts + tri->numVerts );
    466 
    467 	surf->data = (surfaceType_t *)tri;
    468 
    469 	// copy vertexes
    470 	ClearBounds( tri->bounds[0], tri->bounds[1] );
    471 	verts += LittleLong( ds->firstVert );
    472 	for ( i = 0 ; i < numVerts ; i++ ) {
    473 		for ( j = 0 ; j < 3 ; j++ ) {
    474 			tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] );
    475 			tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] );
    476 		}
    477 		AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] );
    478 		for ( j = 0 ; j < 2 ; j++ ) {
    479 			tri->verts[i].st[j] = LittleFloat( verts[i].st[j] );
    480 			tri->verts[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] );
    481 		}
    482 
    483 		R_ColorShiftLightingBytes( verts[i].color, tri->verts[i].color );
    484 	}
    485 
    486 	// copy indexes
    487 	indexes += LittleLong( ds->firstIndex );
    488 	for ( i = 0 ; i < numIndexes ; i++ ) {
    489 		tri->indexes[i] = LittleLong( indexes[i] );
    490 		if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) {
    491 			ri.Error( ERR_DROP, "Bad index in triangle surface" );
    492 		}
    493 	}
    494 }
    495 
    496 /*
    497 ===============
    498 ParseFlare
    499 ===============
    500 */
    501 static void ParseFlare( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) {
    502 	srfFlare_t		*flare;
    503 	int				i;
    504 
    505 	// get fog volume
    506 	surf->fogIndex = LittleLong( ds->fogNum ) + 1;
    507 
    508 	// get shader
    509 	surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX );
    510 	if ( r_singleShader->integer && !surf->shader->isSky ) {
    511 		surf->shader = tr.defaultShader;
    512 	}
    513 
    514 	flare = ri.Hunk_Alloc( sizeof( *flare ), h_low );
    515 	flare->surfaceType = SF_FLARE;
    516 
    517 	surf->data = (surfaceType_t *)flare;
    518 
    519 	for ( i = 0 ; i < 3 ; i++ ) {
    520 		flare->origin[i] = LittleFloat( ds->lightmapOrigin[i] );
    521 		flare->color[i] = LittleFloat( ds->lightmapVecs[0][i] );
    522 		flare->normal[i] = LittleFloat( ds->lightmapVecs[2][i] );
    523 	}
    524 }
    525 
    526 
    527 /*
    528 =================
    529 R_MergedWidthPoints
    530 
    531 returns true if there are grid points merged on a width edge
    532 =================
    533 */
    534 int R_MergedWidthPoints(srfGridMesh_t *grid, int offset) {
    535 	int i, j;
    536 
    537 	for (i = 1; i < grid->width-1; i++) {
    538 		for (j = i + 1; j < grid->width-1; j++) {
    539 			if ( fabs(grid->verts[i + offset].xyz[0] - grid->verts[j + offset].xyz[0]) > .1) continue;
    540 			if ( fabs(grid->verts[i + offset].xyz[1] - grid->verts[j + offset].xyz[1]) > .1) continue;
    541 			if ( fabs(grid->verts[i + offset].xyz[2] - grid->verts[j + offset].xyz[2]) > .1) continue;
    542 			return qtrue;
    543 		}
    544 	}
    545 	return qfalse;
    546 }
    547 
    548 /*
    549 =================
    550 R_MergedHeightPoints
    551 
    552 returns true if there are grid points merged on a height edge
    553 =================
    554 */
    555 int R_MergedHeightPoints(srfGridMesh_t *grid, int offset) {
    556 	int i, j;
    557 
    558 	for (i = 1; i < grid->height-1; i++) {
    559 		for (j = i + 1; j < grid->height-1; j++) {
    560 			if ( fabs(grid->verts[grid->width * i + offset].xyz[0] - grid->verts[grid->width * j + offset].xyz[0]) > .1) continue;
    561 			if ( fabs(grid->verts[grid->width * i + offset].xyz[1] - grid->verts[grid->width * j + offset].xyz[1]) > .1) continue;
    562 			if ( fabs(grid->verts[grid->width * i + offset].xyz[2] - grid->verts[grid->width * j + offset].xyz[2]) > .1) continue;
    563 			return qtrue;
    564 		}
    565 	}
    566 	return qfalse;
    567 }
    568 
    569 /*
    570 =================
    571 R_FixSharedVertexLodError_r
    572 
    573 NOTE: never sync LoD through grid edges with merged points!
    574 
    575 FIXME: write generalized version that also avoids cracks between a patch and one that meets half way?
    576 =================
    577 */
    578 void R_FixSharedVertexLodError_r( int start, srfGridMesh_t *grid1 ) {
    579 	int j, k, l, m, n, offset1, offset2, touch;
    580 	srfGridMesh_t *grid2;
    581 
    582 	for ( j = start; j < s_worldData.numsurfaces; j++ ) {
    583 		//
    584 		grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data;
    585 		// if this surface is not a grid
    586 		if ( grid2->surfaceType != SF_GRID ) continue;
    587 		// if the LOD errors are already fixed for this patch
    588 		if ( grid2->lodFixed == 2 ) continue;
    589 		// grids in the same LOD group should have the exact same lod radius
    590 		if ( grid1->lodRadius != grid2->lodRadius ) continue;
    591 		// grids in the same LOD group should have the exact same lod origin
    592 		if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue;
    593 		if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue;
    594 		if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue;
    595 		//
    596 		touch = qfalse;
    597 		for (n = 0; n < 2; n++) {
    598 			//
    599 			if (n) offset1 = (grid1->height-1) * grid1->width;
    600 			else offset1 = 0;
    601 			if (R_MergedWidthPoints(grid1, offset1)) continue;
    602 			for (k = 1; k < grid1->width-1; k++) {
    603 				for (m = 0; m < 2; m++) {
    604 
    605 					if (m) offset2 = (grid2->height-1) * grid2->width;
    606 					else offset2 = 0;
    607 					if (R_MergedWidthPoints(grid2, offset2)) continue;
    608 					for ( l = 1; l < grid2->width-1; l++) {
    609 					//
    610 						if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue;
    611 						if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue;
    612 						if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue;
    613 						// ok the points are equal and should have the same lod error
    614 						grid2->widthLodError[l] = grid1->widthLodError[k];
    615 						touch = qtrue;
    616 					}
    617 				}
    618 				for (m = 0; m < 2; m++) {
    619 
    620 					if (m) offset2 = grid2->width-1;
    621 					else offset2 = 0;
    622 					if (R_MergedHeightPoints(grid2, offset2)) continue;
    623 					for ( l = 1; l < grid2->height-1; l++) {
    624 					//
    625 						if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue;
    626 						if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue;
    627 						if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue;
    628 						// ok the points are equal and should have the same lod error
    629 						grid2->heightLodError[l] = grid1->widthLodError[k];
    630 						touch = qtrue;
    631 					}
    632 				}
    633 			}
    634 		}
    635 		for (n = 0; n < 2; n++) {
    636 			//
    637 			if (n) offset1 = grid1->width-1;
    638 			else offset1 = 0;
    639 			if (R_MergedHeightPoints(grid1, offset1)) continue;
    640 			for (k = 1; k < grid1->height-1; k++) {
    641 				for (m = 0; m < 2; m++) {
    642 
    643 					if (m) offset2 = (grid2->height-1) * grid2->width;
    644 					else offset2 = 0;
    645 					if (R_MergedWidthPoints(grid2, offset2)) continue;
    646 					for ( l = 1; l < grid2->width-1; l++) {
    647 					//
    648 						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue;
    649 						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue;
    650 						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue;
    651 						// ok the points are equal and should have the same lod error
    652 						grid2->widthLodError[l] = grid1->heightLodError[k];
    653 						touch = qtrue;
    654 					}
    655 				}
    656 				for (m = 0; m < 2; m++) {
    657 
    658 					if (m) offset2 = grid2->width-1;
    659 					else offset2 = 0;
    660 					if (R_MergedHeightPoints(grid2, offset2)) continue;
    661 					for ( l = 1; l < grid2->height-1; l++) {
    662 					//
    663 						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue;
    664 						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue;
    665 						if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue;
    666 						// ok the points are equal and should have the same lod error
    667 						grid2->heightLodError[l] = grid1->heightLodError[k];
    668 						touch = qtrue;
    669 					}
    670 				}
    671 			}
    672 		}
    673 		if (touch) {
    674 			grid2->lodFixed = 2;
    675 			R_FixSharedVertexLodError_r ( start, grid2 );
    676 			//NOTE: this would be correct but makes things really slow
    677 			//grid2->lodFixed = 1;
    678 		}
    679 	}
    680 }
    681 
    682 /*
    683 =================
    684 R_FixSharedVertexLodError
    685 
    686 This function assumes that all patches in one group are nicely stitched together for the highest LoD.
    687 If this is not the case this function will still do its job but won't fix the highest LoD cracks.
    688 =================
    689 */
    690 void R_FixSharedVertexLodError( void ) {
    691 	int i;
    692 	srfGridMesh_t *grid1;
    693 
    694 	for ( i = 0; i < s_worldData.numsurfaces; i++ ) {
    695 		//
    696 		grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data;
    697 		// if this surface is not a grid
    698 		if ( grid1->surfaceType != SF_GRID )
    699 			continue;
    700 		//
    701 		if ( grid1->lodFixed )
    702 			continue;
    703 		//
    704 		grid1->lodFixed = 2;
    705 		// recursively fix other patches in the same LOD group
    706 		R_FixSharedVertexLodError_r( i + 1, grid1);
    707 	}
    708 }
    709 
    710 
    711 /*
    712 ===============
    713 R_StitchPatches
    714 ===============
    715 */
    716 int R_StitchPatches( int grid1num, int grid2num ) {
    717 	float *v1, *v2;
    718 	srfGridMesh_t *grid1, *grid2;
    719 	int k, l, m, n, offset1, offset2, row, column;
    720 
    721 	grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data;
    722 	grid2 = (srfGridMesh_t *) s_worldData.surfaces[grid2num].data;
    723 	for (n = 0; n < 2; n++) {
    724 		//
    725 		if (n) offset1 = (grid1->height-1) * grid1->width;
    726 		else offset1 = 0;
    727 		if (R_MergedWidthPoints(grid1, offset1))
    728 			continue;
    729 		for (k = 0; k < grid1->width-2; k += 2) {
    730 
    731 			for (m = 0; m < 2; m++) {
    732 
    733 				if ( grid2->width >= MAX_GRID_SIZE )
    734 					break;
    735 				if (m) offset2 = (grid2->height-1) * grid2->width;
    736 				else offset2 = 0;
    737 				for ( l = 0; l < grid2->width-1; l++) {
    738 				//
    739 					v1 = grid1->verts[k + offset1].xyz;
    740 					v2 = grid2->verts[l + offset2].xyz;
    741 					if ( fabs(v1[0] - v2[0]) > .1)
    742 						continue;
    743 					if ( fabs(v1[1] - v2[1]) > .1)
    744 						continue;
    745 					if ( fabs(v1[2] - v2[2]) > .1)
    746 						continue;
    747 
    748 					v1 = grid1->verts[k + 2 + offset1].xyz;
    749 					v2 = grid2->verts[l + 1 + offset2].xyz;
    750 					if ( fabs(v1[0] - v2[0]) > .1)
    751 						continue;
    752 					if ( fabs(v1[1] - v2[1]) > .1)
    753 						continue;
    754 					if ( fabs(v1[2] - v2[2]) > .1)
    755 						continue;
    756 					//
    757 					v1 = grid2->verts[l + offset2].xyz;
    758 					v2 = grid2->verts[l + 1 + offset2].xyz;
    759 					if ( fabs(v1[0] - v2[0]) < .01 &&
    760 							fabs(v1[1] - v2[1]) < .01 &&
    761 							fabs(v1[2] - v2[2]) < .01)
    762 						continue;
    763 					//
    764 					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
    765 					// insert column into grid2 right after after column l
    766 					if (m) row = grid2->height-1;
    767 					else row = 0;
    768 					grid2 = R_GridInsertColumn( grid2, l+1, row,
    769 									grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]);
    770 					grid2->lodStitched = qfalse;
    771 					s_worldData.surfaces[grid2num].data = (void *) grid2;
    772 					return qtrue;
    773 				}
    774 			}
    775 			for (m = 0; m < 2; m++) {
    776 
    777 				if (grid2->height >= MAX_GRID_SIZE)
    778 					break;
    779 				if (m) offset2 = grid2->width-1;
    780 				else offset2 = 0;
    781 				for ( l = 0; l < grid2->height-1; l++) {
    782 					//
    783 					v1 = grid1->verts[k + offset1].xyz;
    784 					v2 = grid2->verts[grid2->width * l + offset2].xyz;
    785 					if ( fabs(v1[0] - v2[0]) > .1)
    786 						continue;
    787 					if ( fabs(v1[1] - v2[1]) > .1)
    788 						continue;
    789 					if ( fabs(v1[2] - v2[2]) > .1)
    790 						continue;
    791 
    792 					v1 = grid1->verts[k + 2 + offset1].xyz;
    793 					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
    794 					if ( fabs(v1[0] - v2[0]) > .1)
    795 						continue;
    796 					if ( fabs(v1[1] - v2[1]) > .1)
    797 						continue;
    798 					if ( fabs(v1[2] - v2[2]) > .1)
    799 						continue;
    800 					//
    801 					v1 = grid2->verts[grid2->width * l + offset2].xyz;
    802 					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
    803 					if ( fabs(v1[0] - v2[0]) < .01 &&
    804 							fabs(v1[1] - v2[1]) < .01 &&
    805 							fabs(v1[2] - v2[2]) < .01)
    806 						continue;
    807 					//
    808 					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
    809 					// insert row into grid2 right after after row l
    810 					if (m) column = grid2->width-1;
    811 					else column = 0;
    812 					grid2 = R_GridInsertRow( grid2, l+1, column,
    813 										grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]);
    814 					grid2->lodStitched = qfalse;
    815 					s_worldData.surfaces[grid2num].data = (void *) grid2;
    816 					return qtrue;
    817 				}
    818 			}
    819 		}
    820 	}
    821 	for (n = 0; n < 2; n++) {
    822 		//
    823 		if (n) offset1 = grid1->width-1;
    824 		else offset1 = 0;
    825 		if (R_MergedHeightPoints(grid1, offset1))
    826 			continue;
    827 		for (k = 0; k < grid1->height-2; k += 2) {
    828 			for (m = 0; m < 2; m++) {
    829 
    830 				if ( grid2->width >= MAX_GRID_SIZE )
    831 					break;
    832 				if (m) offset2 = (grid2->height-1) * grid2->width;
    833 				else offset2 = 0;
    834 				for ( l = 0; l < grid2->width-1; l++) {
    835 				//
    836 					v1 = grid1->verts[grid1->width * k + offset1].xyz;
    837 					v2 = grid2->verts[l + offset2].xyz;
    838 					if ( fabs(v1[0] - v2[0]) > .1)
    839 						continue;
    840 					if ( fabs(v1[1] - v2[1]) > .1)
    841 						continue;
    842 					if ( fabs(v1[2] - v2[2]) > .1)
    843 						continue;
    844 
    845 					v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz;
    846 					v2 = grid2->verts[l + 1 + offset2].xyz;
    847 					if ( fabs(v1[0] - v2[0]) > .1)
    848 						continue;
    849 					if ( fabs(v1[1] - v2[1]) > .1)
    850 						continue;
    851 					if ( fabs(v1[2] - v2[2]) > .1)
    852 						continue;
    853 					//
    854 					v1 = grid2->verts[l + offset2].xyz;
    855 					v2 = grid2->verts[(l + 1) + offset2].xyz;
    856 					if ( fabs(v1[0] - v2[0]) < .01 &&
    857 							fabs(v1[1] - v2[1]) < .01 &&
    858 							fabs(v1[2] - v2[2]) < .01)
    859 						continue;
    860 					//
    861 					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
    862 					// insert column into grid2 right after after column l
    863 					if (m) row = grid2->height-1;
    864 					else row = 0;
    865 					grid2 = R_GridInsertColumn( grid2, l+1, row,
    866 									grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]);
    867 					grid2->lodStitched = qfalse;
    868 					s_worldData.surfaces[grid2num].data = (void *) grid2;
    869 					return qtrue;
    870 				}
    871 			}
    872 			for (m = 0; m < 2; m++) {
    873 
    874 				if (grid2->height >= MAX_GRID_SIZE)
    875 					break;
    876 				if (m) offset2 = grid2->width-1;
    877 				else offset2 = 0;
    878 				for ( l = 0; l < grid2->height-1; l++) {
    879 				//
    880 					v1 = grid1->verts[grid1->width * k + offset1].xyz;
    881 					v2 = grid2->verts[grid2->width * l + offset2].xyz;
    882 					if ( fabs(v1[0] - v2[0]) > .1)
    883 						continue;
    884 					if ( fabs(v1[1] - v2[1]) > .1)
    885 						continue;
    886 					if ( fabs(v1[2] - v2[2]) > .1)
    887 						continue;
    888 
    889 					v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz;
    890 					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
    891 					if ( fabs(v1[0] - v2[0]) > .1)
    892 						continue;
    893 					if ( fabs(v1[1] - v2[1]) > .1)
    894 						continue;
    895 					if ( fabs(v1[2] - v2[2]) > .1)
    896 						continue;
    897 					//
    898 					v1 = grid2->verts[grid2->width * l + offset2].xyz;
    899 					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
    900 					if ( fabs(v1[0] - v2[0]) < .01 &&
    901 							fabs(v1[1] - v2[1]) < .01 &&
    902 							fabs(v1[2] - v2[2]) < .01)
    903 						continue;
    904 					//
    905 					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
    906 					// insert row into grid2 right after after row l
    907 					if (m) column = grid2->width-1;
    908 					else column = 0;
    909 					grid2 = R_GridInsertRow( grid2, l+1, column,
    910 									grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]);
    911 					grid2->lodStitched = qfalse;
    912 					s_worldData.surfaces[grid2num].data = (void *) grid2;
    913 					return qtrue;
    914 				}
    915 			}
    916 		}
    917 	}
    918 	for (n = 0; n < 2; n++) {
    919 		//
    920 		if (n) offset1 = (grid1->height-1) * grid1->width;
    921 		else offset1 = 0;
    922 		if (R_MergedWidthPoints(grid1, offset1))
    923 			continue;
    924 		for (k = grid1->width-1; k > 1; k -= 2) {
    925 
    926 			for (m = 0; m < 2; m++) {
    927 
    928 				if ( grid2->width >= MAX_GRID_SIZE )
    929 					break;
    930 				if (m) offset2 = (grid2->height-1) * grid2->width;
    931 				else offset2 = 0;
    932 				for ( l = 0; l < grid2->width-1; l++) {
    933 				//
    934 					v1 = grid1->verts[k + offset1].xyz;
    935 					v2 = grid2->verts[l + offset2].xyz;
    936 					if ( fabs(v1[0] - v2[0]) > .1)
    937 						continue;
    938 					if ( fabs(v1[1] - v2[1]) > .1)
    939 						continue;
    940 					if ( fabs(v1[2] - v2[2]) > .1)
    941 						continue;
    942 
    943 					v1 = grid1->verts[k - 2 + offset1].xyz;
    944 					v2 = grid2->verts[l + 1 + offset2].xyz;
    945 					if ( fabs(v1[0] - v2[0]) > .1)
    946 						continue;
    947 					if ( fabs(v1[1] - v2[1]) > .1)
    948 						continue;
    949 					if ( fabs(v1[2] - v2[2]) > .1)
    950 						continue;
    951 					//
    952 					v1 = grid2->verts[l + offset2].xyz;
    953 					v2 = grid2->verts[(l + 1) + offset2].xyz;
    954 					if ( fabs(v1[0] - v2[0]) < .01 &&
    955 							fabs(v1[1] - v2[1]) < .01 &&
    956 							fabs(v1[2] - v2[2]) < .01)
    957 						continue;
    958 					//
    959 					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
    960 					// insert column into grid2 right after after column l
    961 					if (m) row = grid2->height-1;
    962 					else row = 0;
    963 					grid2 = R_GridInsertColumn( grid2, l+1, row,
    964 										grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]);
    965 					grid2->lodStitched = qfalse;
    966 					s_worldData.surfaces[grid2num].data = (void *) grid2;
    967 					return qtrue;
    968 				}
    969 			}
    970 			for (m = 0; m < 2; m++) {
    971 
    972 				if (grid2->height >= MAX_GRID_SIZE)
    973 					break;
    974 				if (m) offset2 = grid2->width-1;
    975 				else offset2 = 0;
    976 				for ( l = 0; l < grid2->height-1; l++) {
    977 				//
    978 					v1 = grid1->verts[k + offset1].xyz;
    979 					v2 = grid2->verts[grid2->width * l + offset2].xyz;
    980 					if ( fabs(v1[0] - v2[0]) > .1)
    981 						continue;
    982 					if ( fabs(v1[1] - v2[1]) > .1)
    983 						continue;
    984 					if ( fabs(v1[2] - v2[2]) > .1)
    985 						continue;
    986 
    987 					v1 = grid1->verts[k - 2 + offset1].xyz;
    988 					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
    989 					if ( fabs(v1[0] - v2[0]) > .1)
    990 						continue;
    991 					if ( fabs(v1[1] - v2[1]) > .1)
    992 						continue;
    993 					if ( fabs(v1[2] - v2[2]) > .1)
    994 						continue;
    995 					//
    996 					v1 = grid2->verts[grid2->width * l + offset2].xyz;
    997 					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
    998 					if ( fabs(v1[0] - v2[0]) < .01 &&
    999 							fabs(v1[1] - v2[1]) < .01 &&
   1000 							fabs(v1[2] - v2[2]) < .01)
   1001 						continue;
   1002 					//
   1003 					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
   1004 					// insert row into grid2 right after after row l
   1005 					if (m) column = grid2->width-1;
   1006 					else column = 0;
   1007 					grid2 = R_GridInsertRow( grid2, l+1, column,
   1008 										grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]);
   1009 					if (!grid2)
   1010 						break;
   1011 					grid2->lodStitched = qfalse;
   1012 					s_worldData.surfaces[grid2num].data = (void *) grid2;
   1013 					return qtrue;
   1014 				}
   1015 			}
   1016 		}
   1017 	}
   1018 	for (n = 0; n < 2; n++) {
   1019 		//
   1020 		if (n) offset1 = grid1->width-1;
   1021 		else offset1 = 0;
   1022 		if (R_MergedHeightPoints(grid1, offset1))
   1023 			continue;
   1024 		for (k = grid1->height-1; k > 1; k -= 2) {
   1025 			for (m = 0; m < 2; m++) {
   1026 
   1027 				if ( grid2->width >= MAX_GRID_SIZE )
   1028 					break;
   1029 				if (m) offset2 = (grid2->height-1) * grid2->width;
   1030 				else offset2 = 0;
   1031 				for ( l = 0; l < grid2->width-1; l++) {
   1032 				//
   1033 					v1 = grid1->verts[grid1->width * k + offset1].xyz;
   1034 					v2 = grid2->verts[l + offset2].xyz;
   1035 					if ( fabs(v1[0] - v2[0]) > .1)
   1036 						continue;
   1037 					if ( fabs(v1[1] - v2[1]) > .1)
   1038 						continue;
   1039 					if ( fabs(v1[2] - v2[2]) > .1)
   1040 						continue;
   1041 
   1042 					v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz;
   1043 					v2 = grid2->verts[l + 1 + offset2].xyz;
   1044 					if ( fabs(v1[0] - v2[0]) > .1)
   1045 						continue;
   1046 					if ( fabs(v1[1] - v2[1]) > .1)
   1047 						continue;
   1048 					if ( fabs(v1[2] - v2[2]) > .1)
   1049 						continue;
   1050 					//
   1051 					v1 = grid2->verts[l + offset2].xyz;
   1052 					v2 = grid2->verts[(l + 1) + offset2].xyz;
   1053 					if ( fabs(v1[0] - v2[0]) < .01 &&
   1054 							fabs(v1[1] - v2[1]) < .01 &&
   1055 							fabs(v1[2] - v2[2]) < .01)
   1056 						continue;
   1057 					//
   1058 					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
   1059 					// insert column into grid2 right after after column l
   1060 					if (m) row = grid2->height-1;
   1061 					else row = 0;
   1062 					grid2 = R_GridInsertColumn( grid2, l+1, row,
   1063 										grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]);
   1064 					grid2->lodStitched = qfalse;
   1065 					s_worldData.surfaces[grid2num].data = (void *) grid2;
   1066 					return qtrue;
   1067 				}
   1068 			}
   1069 			for (m = 0; m < 2; m++) {
   1070 
   1071 				if (grid2->height >= MAX_GRID_SIZE)
   1072 					break;
   1073 				if (m) offset2 = grid2->width-1;
   1074 				else offset2 = 0;
   1075 				for ( l = 0; l < grid2->height-1; l++) {
   1076 				//
   1077 					v1 = grid1->verts[grid1->width * k + offset1].xyz;
   1078 					v2 = grid2->verts[grid2->width * l + offset2].xyz;
   1079 					if ( fabs(v1[0] - v2[0]) > .1)
   1080 						continue;
   1081 					if ( fabs(v1[1] - v2[1]) > .1)
   1082 						continue;
   1083 					if ( fabs(v1[2] - v2[2]) > .1)
   1084 						continue;
   1085 
   1086 					v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz;
   1087 					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
   1088 					if ( fabs(v1[0] - v2[0]) > .1)
   1089 						continue;
   1090 					if ( fabs(v1[1] - v2[1]) > .1)
   1091 						continue;
   1092 					if ( fabs(v1[2] - v2[2]) > .1)
   1093 						continue;
   1094 					//
   1095 					v1 = grid2->verts[grid2->width * l + offset2].xyz;
   1096 					v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz;
   1097 					if ( fabs(v1[0] - v2[0]) < .01 &&
   1098 							fabs(v1[1] - v2[1]) < .01 &&
   1099 							fabs(v1[2] - v2[2]) < .01)
   1100 						continue;
   1101 					//
   1102 					//ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" );
   1103 					// insert row into grid2 right after after row l
   1104 					if (m) column = grid2->width-1;
   1105 					else column = 0;
   1106 					grid2 = R_GridInsertRow( grid2, l+1, column,
   1107 										grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]);
   1108 					grid2->lodStitched = qfalse;
   1109 					s_worldData.surfaces[grid2num].data = (void *) grid2;
   1110 					return qtrue;
   1111 				}
   1112 			}
   1113 		}
   1114 	}
   1115 	return qfalse;
   1116 }
   1117 
   1118 /*
   1119 ===============
   1120 R_TryStitchPatch
   1121 
   1122 This function will try to stitch patches in the same LoD group together for the highest LoD.
   1123 
   1124 Only single missing vertice cracks will be fixed.
   1125 
   1126 Vertices will be joined at the patch side a crack is first found, at the other side
   1127 of the patch (on the same row or column) the vertices will not be joined and cracks
   1128 might still appear at that side.
   1129 ===============
   1130 */
   1131 int R_TryStitchingPatch( int grid1num ) {
   1132 	int j, numstitches;
   1133 	srfGridMesh_t *grid1, *grid2;
   1134 
   1135 	numstitches = 0;
   1136 	grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data;
   1137 	for ( j = 0; j < s_worldData.numsurfaces; j++ ) {
   1138 		//
   1139 		grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data;
   1140 		// if this surface is not a grid
   1141 		if ( grid2->surfaceType != SF_GRID ) continue;
   1142 		// grids in the same LOD group should have the exact same lod radius
   1143 		if ( grid1->lodRadius != grid2->lodRadius ) continue;
   1144 		// grids in the same LOD group should have the exact same lod origin
   1145 		if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue;
   1146 		if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue;
   1147 		if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue;
   1148 		//
   1149 		while (R_StitchPatches(grid1num, j))
   1150 		{
   1151 			numstitches++;
   1152 		}
   1153 	}
   1154 	return numstitches;
   1155 }
   1156 
   1157 /*
   1158 ===============
   1159 R_StitchAllPatches
   1160 ===============
   1161 */
   1162 void R_StitchAllPatches( void ) {
   1163 	int i, stitched, numstitches;
   1164 	srfGridMesh_t *grid1;
   1165 
   1166 	numstitches = 0;
   1167 	do
   1168 	{
   1169 		stitched = qfalse;
   1170 		for ( i = 0; i < s_worldData.numsurfaces; i++ ) {
   1171 			//
   1172 			grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data;
   1173 			// if this surface is not a grid
   1174 			if ( grid1->surfaceType != SF_GRID )
   1175 				continue;
   1176 			//
   1177 			if ( grid1->lodStitched )
   1178 				continue;
   1179 			//
   1180 			grid1->lodStitched = qtrue;
   1181 			stitched = qtrue;
   1182 			//
   1183 			numstitches += R_TryStitchingPatch( i );
   1184 		}
   1185 	}
   1186 	while (stitched);
   1187 	ri.Printf( PRINT_ALL, "stitched %d LoD cracks\n", numstitches );
   1188 }
   1189 
   1190 /*
   1191 ===============
   1192 R_MovePatchSurfacesToHunk
   1193 ===============
   1194 */
   1195 void R_MovePatchSurfacesToHunk(void) {
   1196 	int i, size;
   1197 	srfGridMesh_t *grid, *hunkgrid;
   1198 
   1199 	for ( i = 0; i < s_worldData.numsurfaces; i++ ) {
   1200 		//
   1201 		grid = (srfGridMesh_t *) s_worldData.surfaces[i].data;
   1202 		// if this surface is not a grid
   1203 		if ( grid->surfaceType != SF_GRID )
   1204 			continue;
   1205 		//
   1206 		size = (grid->width * grid->height - 1) * sizeof( drawVert_t ) + sizeof( *grid );
   1207 		hunkgrid = ri.Hunk_Alloc( size, h_low );
   1208 		Com_Memcpy(hunkgrid, grid, size);
   1209 
   1210 		hunkgrid->widthLodError = ri.Hunk_Alloc( grid->width * 4, h_low );
   1211 		Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 );
   1212 
   1213 		hunkgrid->heightLodError = ri.Hunk_Alloc( grid->height * 4, h_low );
   1214 		Com_Memcpy( grid->heightLodError, grid->heightLodError, grid->height * 4 );
   1215 
   1216 		R_FreeSurfaceGridMesh( grid );
   1217 
   1218 		s_worldData.surfaces[i].data = (void *) hunkgrid;
   1219 	}
   1220 }
   1221 
   1222 /*
   1223 ===============
   1224 R_LoadSurfaces
   1225 ===============
   1226 */
   1227 static	void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) {
   1228 	dsurface_t	*in;
   1229 	msurface_t	*out;
   1230 	drawVert_t	*dv;
   1231 	int			*indexes;
   1232 	int			count;
   1233 	int			numFaces, numMeshes, numTriSurfs, numFlares;
   1234 	int			i;
   1235 
   1236 	numFaces = 0;
   1237 	numMeshes = 0;
   1238 	numTriSurfs = 0;
   1239 	numFlares = 0;
   1240 
   1241 	in = (void *)(fileBase + surfs->fileofs);
   1242 	if (surfs->filelen % sizeof(*in))
   1243 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1244 	count = surfs->filelen / sizeof(*in);
   1245 
   1246 	dv = (void *)(fileBase + verts->fileofs);
   1247 	if (verts->filelen % sizeof(*dv))
   1248 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1249 
   1250 	indexes = (void *)(fileBase + indexLump->fileofs);
   1251 	if ( indexLump->filelen % sizeof(*indexes))
   1252 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1253 
   1254 	out = ri.Hunk_Alloc ( count * sizeof(*out), h_low );	
   1255 
   1256 	s_worldData.surfaces = out;
   1257 	s_worldData.numsurfaces = count;
   1258 
   1259 	for ( i = 0 ; i < count ; i++, in++, out++ ) {
   1260 		switch ( LittleLong( in->surfaceType ) ) {
   1261 		case MST_PATCH:
   1262 			ParseMesh ( in, dv, out );
   1263 			numMeshes++;
   1264 			break;
   1265 		case MST_TRIANGLE_SOUP:
   1266 			ParseTriSurf( in, dv, out, indexes );
   1267 			numTriSurfs++;
   1268 			break;
   1269 		case MST_PLANAR:
   1270 			ParseFace( in, dv, out, indexes );
   1271 			numFaces++;
   1272 			break;
   1273 		case MST_FLARE:
   1274 			ParseFlare( in, dv, out, indexes );
   1275 			numFlares++;
   1276 			break;
   1277 		default:
   1278 			ri.Error( ERR_DROP, "Bad surfaceType" );
   1279 		}
   1280 	}
   1281 
   1282 #ifdef PATCH_STITCHING
   1283 	R_StitchAllPatches();
   1284 #endif
   1285 
   1286 	R_FixSharedVertexLodError();
   1287 
   1288 #ifdef PATCH_STITCHING
   1289 	R_MovePatchSurfacesToHunk();
   1290 #endif
   1291 
   1292 	ri.Printf( PRINT_ALL, "...loaded %d faces, %i meshes, %i trisurfs, %i flares\n", 
   1293 		numFaces, numMeshes, numTriSurfs, numFlares );
   1294 }
   1295 
   1296 
   1297 
   1298 /*
   1299 =================
   1300 R_LoadSubmodels
   1301 =================
   1302 */
   1303 static	void R_LoadSubmodels( lump_t *l ) {
   1304 	dmodel_t	*in;
   1305 	bmodel_t	*out;
   1306 	int			i, j, count;
   1307 
   1308 	in = (void *)(fileBase + l->fileofs);
   1309 	if (l->filelen % sizeof(*in))
   1310 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1311 	count = l->filelen / sizeof(*in);
   1312 
   1313 	s_worldData.bmodels = out = ri.Hunk_Alloc( count * sizeof(*out), h_low );
   1314 
   1315 	for ( i=0 ; i<count ; i++, in++, out++ ) {
   1316 		model_t *model;
   1317 
   1318 		model = R_AllocModel();
   1319 
   1320 		assert( model != NULL );			// this should never happen
   1321 
   1322 		model->type = MOD_BRUSH;
   1323 		model->bmodel = out;
   1324 		Com_sprintf( model->name, sizeof( model->name ), "*%d", i );
   1325 
   1326 		for (j=0 ; j<3 ; j++) {
   1327 			out->bounds[0][j] = LittleFloat (in->mins[j]);
   1328 			out->bounds[1][j] = LittleFloat (in->maxs[j]);
   1329 		}
   1330 
   1331 		out->firstSurface = s_worldData.surfaces + LittleLong( in->firstSurface );
   1332 		out->numSurfaces = LittleLong( in->numSurfaces );
   1333 	}
   1334 }
   1335 
   1336 
   1337 
   1338 //==================================================================
   1339 
   1340 /*
   1341 =================
   1342 R_SetParent
   1343 =================
   1344 */
   1345 static	void R_SetParent (mnode_t *node, mnode_t *parent)
   1346 {
   1347 	node->parent = parent;
   1348 	if (node->contents != -1)
   1349 		return;
   1350 	R_SetParent (node->children[0], node);
   1351 	R_SetParent (node->children[1], node);
   1352 }
   1353 
   1354 /*
   1355 =================
   1356 R_LoadNodesAndLeafs
   1357 =================
   1358 */
   1359 static	void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) {
   1360 	int			i, j, p;
   1361 	dnode_t		*in;
   1362 	dleaf_t		*inLeaf;
   1363 	mnode_t 	*out;
   1364 	int			numNodes, numLeafs;
   1365 
   1366 	in = (void *)(fileBase + nodeLump->fileofs);
   1367 	if (nodeLump->filelen % sizeof(dnode_t) ||
   1368 		leafLump->filelen % sizeof(dleaf_t) ) {
   1369 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1370 	}
   1371 	numNodes = nodeLump->filelen / sizeof(dnode_t);
   1372 	numLeafs = leafLump->filelen / sizeof(dleaf_t);
   1373 
   1374 	out = ri.Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low);	
   1375 
   1376 	s_worldData.nodes = out;
   1377 	s_worldData.numnodes = numNodes + numLeafs;
   1378 	s_worldData.numDecisionNodes = numNodes;
   1379 
   1380 	// load nodes
   1381 	for ( i=0 ; i<numNodes; i++, in++, out++)
   1382 	{
   1383 		for (j=0 ; j<3 ; j++)
   1384 		{
   1385 			out->mins[j] = LittleLong (in->mins[j]);
   1386 			out->maxs[j] = LittleLong (in->maxs[j]);
   1387 		}
   1388 	
   1389 		p = LittleLong(in->planeNum);
   1390 		out->plane = s_worldData.planes + p;
   1391 
   1392 		out->contents = CONTENTS_NODE;	// differentiate from leafs
   1393 
   1394 		for (j=0 ; j<2 ; j++)
   1395 		{
   1396 			p = LittleLong (in->children[j]);
   1397 			if (p >= 0)
   1398 				out->children[j] = s_worldData.nodes + p;
   1399 			else
   1400 				out->children[j] = s_worldData.nodes + numNodes + (-1 - p);
   1401 		}
   1402 	}
   1403 	
   1404 	// load leafs
   1405 	inLeaf = (void *)(fileBase + leafLump->fileofs);
   1406 	for ( i=0 ; i<numLeafs ; i++, inLeaf++, out++)
   1407 	{
   1408 		for (j=0 ; j<3 ; j++)
   1409 		{
   1410 			out->mins[j] = LittleLong (inLeaf->mins[j]);
   1411 			out->maxs[j] = LittleLong (inLeaf->maxs[j]);
   1412 		}
   1413 
   1414 		out->cluster = LittleLong(inLeaf->cluster);
   1415 		out->area = LittleLong(inLeaf->area);
   1416 
   1417 		if ( out->cluster >= s_worldData.numClusters ) {
   1418 			s_worldData.numClusters = out->cluster + 1;
   1419 		}
   1420 
   1421 		out->firstmarksurface = s_worldData.marksurfaces +
   1422 			LittleLong(inLeaf->firstLeafSurface);
   1423 		out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces);
   1424 	}	
   1425 
   1426 	// chain decendants
   1427 	R_SetParent (s_worldData.nodes, NULL);
   1428 }
   1429 
   1430 //=============================================================================
   1431 
   1432 /*
   1433 =================
   1434 R_LoadShaders
   1435 =================
   1436 */
   1437 static	void R_LoadShaders( lump_t *l ) {	
   1438 	int		i, count;
   1439 	dshader_t	*in, *out;
   1440 	
   1441 	in = (void *)(fileBase + l->fileofs);
   1442 	if (l->filelen % sizeof(*in))
   1443 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1444 	count = l->filelen / sizeof(*in);
   1445 	out = ri.Hunk_Alloc ( count*sizeof(*out), h_low );
   1446 
   1447 	s_worldData.shaders = out;
   1448 	s_worldData.numShaders = count;
   1449 
   1450 	Com_Memcpy( out, in, count*sizeof(*out) );
   1451 
   1452 	for ( i=0 ; i<count ; i++ ) {
   1453 		out[i].surfaceFlags = LittleLong( out[i].surfaceFlags );
   1454 		out[i].contentFlags = LittleLong( out[i].contentFlags );
   1455 	}
   1456 }
   1457 
   1458 
   1459 /*
   1460 =================
   1461 R_LoadMarksurfaces
   1462 =================
   1463 */
   1464 static	void R_LoadMarksurfaces (lump_t *l)
   1465 {	
   1466 	int		i, j, count;
   1467 	int		*in;
   1468 	msurface_t **out;
   1469 	
   1470 	in = (void *)(fileBase + l->fileofs);
   1471 	if (l->filelen % sizeof(*in))
   1472 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1473 	count = l->filelen / sizeof(*in);
   1474 	out = ri.Hunk_Alloc ( count*sizeof(*out), h_low);	
   1475 
   1476 	s_worldData.marksurfaces = out;
   1477 	s_worldData.nummarksurfaces = count;
   1478 
   1479 	for ( i=0 ; i<count ; i++)
   1480 	{
   1481 		j = LittleLong(in[i]);
   1482 		out[i] = s_worldData.surfaces + j;
   1483 	}
   1484 }
   1485 
   1486 
   1487 /*
   1488 =================
   1489 R_LoadPlanes
   1490 =================
   1491 */
   1492 static	void R_LoadPlanes( lump_t *l ) {
   1493 	int			i, j;
   1494 	cplane_t	*out;
   1495 	dplane_t 	*in;
   1496 	int			count;
   1497 	int			bits;
   1498 	
   1499 	in = (void *)(fileBase + l->fileofs);
   1500 	if (l->filelen % sizeof(*in))
   1501 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1502 	count = l->filelen / sizeof(*in);
   1503 	out = ri.Hunk_Alloc ( count*2*sizeof(*out), h_low);	
   1504 	
   1505 	s_worldData.planes = out;
   1506 	s_worldData.numplanes = count;
   1507 
   1508 	for ( i=0 ; i<count ; i++, in++, out++) {
   1509 		bits = 0;
   1510 		for (j=0 ; j<3 ; j++) {
   1511 			out->normal[j] = LittleFloat (in->normal[j]);
   1512 			if (out->normal[j] < 0) {
   1513 				bits |= 1<<j;
   1514 			}
   1515 		}
   1516 
   1517 		out->dist = LittleFloat (in->dist);
   1518 		out->type = PlaneTypeForNormal( out->normal );
   1519 		out->signbits = bits;
   1520 	}
   1521 }
   1522 
   1523 /*
   1524 =================
   1525 R_LoadFogs
   1526 
   1527 =================
   1528 */
   1529 static	void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) {
   1530 	int			i;
   1531 	fog_t		*out;
   1532 	dfog_t		*fogs;
   1533 	dbrush_t 	*brushes, *brush;
   1534 	dbrushside_t	*sides;
   1535 	int			count, brushesCount, sidesCount;
   1536 	int			sideNum;
   1537 	int			planeNum;
   1538 	shader_t	*shader;
   1539 	float		d;
   1540 	int			firstSide;
   1541 
   1542 	fogs = (void *)(fileBase + l->fileofs);
   1543 	if (l->filelen % sizeof(*fogs)) {
   1544 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1545 	}
   1546 	count = l->filelen / sizeof(*fogs);
   1547 
   1548 	// create fog strucutres for them
   1549 	s_worldData.numfogs = count + 1;
   1550 	s_worldData.fogs = ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low);
   1551 	out = s_worldData.fogs + 1;
   1552 
   1553 	if ( !count ) {
   1554 		return;
   1555 	}
   1556 
   1557 	brushes = (void *)(fileBase + brushesLump->fileofs);
   1558 	if (brushesLump->filelen % sizeof(*brushes)) {
   1559 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1560 	}
   1561 	brushesCount = brushesLump->filelen / sizeof(*brushes);
   1562 
   1563 	sides = (void *)(fileBase + sidesLump->fileofs);
   1564 	if (sidesLump->filelen % sizeof(*sides)) {
   1565 		ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name);
   1566 	}
   1567 	sidesCount = sidesLump->filelen / sizeof(*sides);
   1568 
   1569 	for ( i=0 ; i<count ; i++, fogs++) {
   1570 		out->originalBrushNumber = LittleLong( fogs->brushNum );
   1571 
   1572 		if ( (unsigned)out->originalBrushNumber >= brushesCount ) {
   1573 			ri.Error( ERR_DROP, "fog brushNumber out of range" );
   1574 		}
   1575 		brush = brushes + out->originalBrushNumber;
   1576 
   1577 		firstSide = LittleLong( brush->firstSide );
   1578 
   1579 			if ( (unsigned)firstSide > sidesCount - 6 ) {
   1580 			ri.Error( ERR_DROP, "fog brush sideNumber out of range" );
   1581 		}
   1582 
   1583 		// brushes are always sorted with the axial sides first
   1584 		sideNum = firstSide + 0;
   1585 		planeNum = LittleLong( sides[ sideNum ].planeNum );
   1586 		out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist;
   1587 
   1588 		sideNum = firstSide + 1;
   1589 		planeNum = LittleLong( sides[ sideNum ].planeNum );
   1590 		out->bounds[1][0] = s_worldData.planes[ planeNum ].dist;
   1591 
   1592 		sideNum = firstSide + 2;
   1593 		planeNum = LittleLong( sides[ sideNum ].planeNum );
   1594 		out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist;
   1595 
   1596 		sideNum = firstSide + 3;
   1597 		planeNum = LittleLong( sides[ sideNum ].planeNum );
   1598 		out->bounds[1][1] = s_worldData.planes[ planeNum ].dist;
   1599 
   1600 		sideNum = firstSide + 4;
   1601 		planeNum = LittleLong( sides[ sideNum ].planeNum );
   1602 		out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist;
   1603 
   1604 		sideNum = firstSide + 5;
   1605 		planeNum = LittleLong( sides[ sideNum ].planeNum );
   1606 		out->bounds[1][2] = s_worldData.planes[ planeNum ].dist;
   1607 
   1608 		// get information from the shader for fog parameters
   1609 		shader = R_FindShader( fogs->shader, LIGHTMAP_NONE, qtrue );
   1610 
   1611 		out->parms = shader->fogParms;
   1612 
   1613 		out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight, 
   1614 			                          shader->fogParms.color[1] * tr.identityLight, 
   1615 			                          shader->fogParms.color[2] * tr.identityLight, 1.0 );
   1616 
   1617 		d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque;
   1618 		out->tcScale = 1.0f / ( d * 8 );
   1619 
   1620 		// set the gradient vector
   1621 		sideNum = LittleLong( fogs->visibleSide );
   1622 
   1623 		if ( sideNum == -1 ) {
   1624 			out->hasSurface = qfalse;
   1625 		} else {
   1626 			out->hasSurface = qtrue;
   1627 			planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum );
   1628 			VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface );
   1629 			out->surface[3] = -s_worldData.planes[ planeNum ].dist;
   1630 		}
   1631 
   1632 		out++;
   1633 	}
   1634 
   1635 }
   1636 
   1637 
   1638 /*
   1639 ================
   1640 R_LoadLightGrid
   1641 
   1642 ================
   1643 */
   1644 void R_LoadLightGrid( lump_t *l ) {
   1645 	int		i;
   1646 	vec3_t	maxs;
   1647 	int		numGridPoints;
   1648 	world_t	*w;
   1649 	float	*wMins, *wMaxs;
   1650 
   1651 	w = &s_worldData;
   1652 
   1653 	w->lightGridInverseSize[0] = 1.0f / w->lightGridSize[0];
   1654 	w->lightGridInverseSize[1] = 1.0f / w->lightGridSize[1];
   1655 	w->lightGridInverseSize[2] = 1.0f / w->lightGridSize[2];
   1656 
   1657 	wMins = w->bmodels[0].bounds[0];
   1658 	wMaxs = w->bmodels[0].bounds[1];
   1659 
   1660 	for ( i = 0 ; i < 3 ; i++ ) {
   1661 		w->lightGridOrigin[i] = w->lightGridSize[i] * ceil( wMins[i] / w->lightGridSize[i] );
   1662 		maxs[i] = w->lightGridSize[i] * floor( wMaxs[i] / w->lightGridSize[i] );
   1663 		w->lightGridBounds[i] = (maxs[i] - w->lightGridOrigin[i])/w->lightGridSize[i] + 1;
   1664 	}
   1665 
   1666 	numGridPoints = w->lightGridBounds[0] * w->lightGridBounds[1] * w->lightGridBounds[2];
   1667 
   1668 	if ( l->filelen != numGridPoints * 8 ) {
   1669 		ri.Printf( PRINT_WARNING, "WARNING: light grid mismatch\n" );
   1670 		w->lightGridData = NULL;
   1671 		return;
   1672 	}
   1673 
   1674 	w->lightGridData = ri.Hunk_Alloc( l->filelen, h_low );
   1675 	Com_Memcpy( w->lightGridData, (void *)(fileBase + l->fileofs), l->filelen );
   1676 
   1677 	// deal with overbright bits
   1678 	for ( i = 0 ; i < numGridPoints ; i++ ) {
   1679 		R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] );
   1680 		R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] );
   1681 	}
   1682 }
   1683 
   1684 /*
   1685 ================
   1686 R_LoadEntities
   1687 ================
   1688 */
   1689 void R_LoadEntities( lump_t *l ) {
   1690 	char *p, *token, *s;
   1691 	char keyname[MAX_TOKEN_CHARS];
   1692 	char value[MAX_TOKEN_CHARS];
   1693 	world_t	*w;
   1694 
   1695 	w = &s_worldData;
   1696 	w->lightGridSize[0] = 64;
   1697 	w->lightGridSize[1] = 64;
   1698 	w->lightGridSize[2] = 128;
   1699 
   1700 	p = (char *)(fileBase + l->fileofs);
   1701 
   1702 	// store for reference by the cgame
   1703 	w->entityString = ri.Hunk_Alloc( l->filelen + 1, h_low );
   1704 	strcpy( w->entityString, p );
   1705 	w->entityParsePoint = w->entityString;
   1706 
   1707 	token = COM_ParseExt( &p, qtrue );
   1708 	if (!*token || *token != '{') {
   1709 		return;
   1710 	}
   1711 
   1712 	// only parse the world spawn
   1713 	while ( 1 ) {	
   1714 		// parse key
   1715 		token = COM_ParseExt( &p, qtrue );
   1716 
   1717 		if ( !*token || *token == '}' ) {
   1718 			break;
   1719 		}
   1720 		Q_strncpyz(keyname, token, sizeof(keyname));
   1721 
   1722 		// parse value
   1723 		token = COM_ParseExt( &p, qtrue );
   1724 
   1725 		if ( !*token || *token == '}' ) {
   1726 			break;
   1727 		}
   1728 		Q_strncpyz(value, token, sizeof(value));
   1729 
   1730 		// check for remapping of shaders for vertex lighting
   1731 		s = "vertexremapshader";
   1732 		if (!Q_strncmp(keyname, s, strlen(s)) ) {
   1733 			s = strchr(value, ';');
   1734 			if (!s) {
   1735 				ri.Printf( PRINT_WARNING, "WARNING: no semi colon in vertexshaderremap '%s'\n", value );
   1736 				break;
   1737 			}
   1738 			*s++ = 0;
   1739 			if (r_vertexLight->integer) {
   1740 				R_RemapShader(value, s, "0");
   1741 			}
   1742 			continue;
   1743 		}
   1744 		// check for remapping of shaders
   1745 		s = "remapshader";
   1746 		if (!Q_strncmp(keyname, s, strlen(s)) ) {
   1747 			s = strchr(value, ';');
   1748 			if (!s) {
   1749 				ri.Printf( PRINT_WARNING, "WARNING: no semi colon in shaderremap '%s'\n", value );
   1750 				break;
   1751 			}
   1752 			*s++ = 0;
   1753 			R_RemapShader(value, s, "0");
   1754 			continue;
   1755 		}
   1756 		// check for a different grid size
   1757 		if (!Q_stricmp(keyname, "gridsize")) {
   1758 			sscanf(value, "%f %f %f", &w->lightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] );
   1759 			continue;
   1760 		}
   1761 	}
   1762 }
   1763 
   1764 /*
   1765 =================
   1766 R_GetEntityToken
   1767 =================
   1768 */
   1769 qboolean R_GetEntityToken( char *buffer, int size ) {
   1770 	const char	*s;
   1771 
   1772 	s = COM_Parse( &s_worldData.entityParsePoint );
   1773 	Q_strncpyz( buffer, s, size );
   1774 	if ( !s_worldData.entityParsePoint || !s[0] ) {
   1775 		s_worldData.entityParsePoint = s_worldData.entityString;
   1776 		return qfalse;
   1777 	} else {
   1778 		return qtrue;
   1779 	}
   1780 }
   1781 
   1782 /*
   1783 =================
   1784 RE_LoadWorldMap
   1785 
   1786 Called directly from cgame
   1787 =================
   1788 */
   1789 void RE_LoadWorldMap( const char *name ) {
   1790 	int			i;
   1791 	dheader_t	*header;
   1792 	byte		*buffer;
   1793 	byte		*startMarker;
   1794 
   1795 	if ( tr.worldMapLoaded ) {
   1796 		ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map\n" );
   1797 	}
   1798 
   1799 	// set default sun direction to be used if it isn't
   1800 	// overridden by a shader
   1801 	tr.sunDirection[0] = 0.45f;
   1802 	tr.sunDirection[1] = 0.3f;
   1803 	tr.sunDirection[2] = 0.9f;
   1804 
   1805 	VectorNormalize( tr.sunDirection );
   1806 
   1807 	tr.worldMapLoaded = qtrue;
   1808 
   1809 	// load it
   1810     ri.FS_ReadFile( name, (void **)&buffer );
   1811 	if ( !buffer ) {
   1812 		ri.Error (ERR_DROP, "RE_LoadWorldMap: %s not found", name);
   1813 	}
   1814 
   1815 	// clear tr.world so if the level fails to load, the next
   1816 	// try will not look at the partially loaded version
   1817 	tr.world = NULL;
   1818 
   1819 	Com_Memset( &s_worldData, 0, sizeof( s_worldData ) );
   1820 	Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) );
   1821 
   1822 	Q_strncpyz( s_worldData.baseName, COM_SkipPath( s_worldData.name ), sizeof( s_worldData.name ) );
   1823 	COM_StripExtension( s_worldData.baseName, s_worldData.baseName );
   1824 
   1825 	startMarker = ri.Hunk_Alloc(0, h_low);
   1826 	c_gridVerts = 0;
   1827 
   1828 	header = (dheader_t *)buffer;
   1829 	fileBase = (byte *)header;
   1830 
   1831 	i = LittleLong (header->version);
   1832 	if ( i != BSP_VERSION ) {
   1833 		ri.Error (ERR_DROP, "RE_LoadWorldMap: %s has wrong version number (%i should be %i)", 
   1834 			name, i, BSP_VERSION);
   1835 	}
   1836 
   1837 	// swap all the lumps
   1838 	for (i=0 ; i<sizeof(dheader_t)/4 ; i++) {
   1839 		((int *)header)[i] = LittleLong ( ((int *)header)[i]);
   1840 	}
   1841 
   1842 	// load into heap
   1843 	R_LoadShaders( &header->lumps[LUMP_SHADERS] );
   1844 	R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] );
   1845 	R_LoadPlanes (&header->lumps[LUMP_PLANES]);
   1846 	R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] );
   1847 	R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header->lumps[LUMP_DRAWINDEXES] );
   1848 	R_LoadMarksurfaces (&header->lumps[LUMP_LEAFSURFACES]);
   1849 	R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]);
   1850 	R_LoadSubmodels (&header->lumps[LUMP_MODELS]);
   1851 	R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] );
   1852 	R_LoadEntities( &header->lumps[LUMP_ENTITIES] );
   1853 	R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] );
   1854 
   1855 	s_worldData.dataSize = (byte *)ri.Hunk_Alloc(0, h_low) - startMarker;
   1856 
   1857 	// only set tr.world now that we know the entire level has loaded properly
   1858 	tr.world = &s_worldData;
   1859 
   1860     ri.FS_FreeFile( buffer );
   1861 }
   1862