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