1 /*
2 Copyright (C) 2003-2006 Andrey Nazarov
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 /*
22 * gl_surf.c -- surface post-processing code
23 *
24 */
25 #include "gl_local.h"
26
27 lightmapBuilder_t lm;
28
29 static cvar_t *gl_coloredlightmaps;
30 static cvar_t *gl_brightness;
31
32 cvar_t *gl_modulate_hack;
33
34 /*
35 =============================================================================
36
37 LIGHTMAP TEXTURES BUILDING
38
39 =============================================================================
40 */
41
42 static float colorScale, brightness;
43
LM_AllocBlock(int w,int h,int * s,int * t)44 static qboolean LM_AllocBlock( int w, int h, int *s, int *t ) {
45 int i, j;
46 int x, y, maxInuse, minInuse;
47
48 x = 0; y = LM_BLOCK_HEIGHT;
49 minInuse = LM_BLOCK_HEIGHT;
50 for( i = 0; i < LM_BLOCK_WIDTH - w; i++ ) {
51 maxInuse = 0;
52 for( j = 0; j < w; j++ ) {
53 if( lm.inuse[ i + j ] >= minInuse ) {
54 break;
55 }
56 if( maxInuse < lm.inuse[ i + j ] ) {
57 maxInuse = lm.inuse[ i + j ];
58 }
59 }
60 if( j == w ) {
61 x = i;
62 y = minInuse = maxInuse;
63 }
64 }
65
66 if( y + h > LM_BLOCK_HEIGHT ) {
67 return qfalse;
68 }
69
70 for( i = 0; i < w; i++ ) {
71 lm.inuse[ x + i ] = y + h;
72 }
73
74 *s = x;
75 *t = y;
76 return qtrue;
77 }
78
LM_InitBlock(void)79 static void LM_InitBlock( void ) {
80 int i;
81
82 for( i = 0; i < LM_BLOCK_WIDTH; i++ ) {
83 lm.inuse[i] = 0;
84 }
85 }
86
LM_UploadBlock(void)87 static void LM_UploadBlock( void ) {
88 /* lightmap images would be automatically freed
89 * by R_FreeUnusedImages on next level load */
90 GL_SelectTMU( 1 );
91 lm.lightmaps[lm.numMaps] = R_CreateImage( va( "*lightmap%d", lm.numMaps ),
92 lm.buffer, LM_BLOCK_WIDTH, LM_BLOCK_HEIGHT,
93 it_lightmap, if_auto );
94 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
95 qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
96 lm.numMaps++;
97 GL_SelectTMU( 0 );
98 }
99
LM_BuildSurfaceLightmap(bspSurface_t * surf)100 static int LM_BuildSurfaceLightmap( bspSurface_t *surf ) {
101 byte *ptr, *dst, *src;
102 int i, j;
103 bspPoly_t *poly;
104 vec_t *vert;
105 int smax, tmax, s, t;
106
107 smax = ( surf->extents[0] >> 4 ) + 1;
108 tmax = ( surf->extents[1] >> 4 ) + 1;
109
110 if( !LM_AllocBlock( smax, tmax, &s, &t ) ) {
111 LM_UploadBlock();
112 if( lm.numMaps == LM_MAX_LIGHTMAPS ) {
113 Com_EPrintf( "LM_MAX_LIGHTMAPS exceeded\n" );
114 return -1;
115 }
116 LM_InitBlock();
117 if( !LM_AllocBlock( smax, tmax, &s, &t ) ) {
118 Com_EPrintf( "LM_AllocBlock( %d, %d ) failed\n",
119 smax, tmax );
120 return -1;
121 }
122 }
123
124 src = surf->lightmap;
125 dst = &lm.buffer[ ( t * LM_BLOCK_WIDTH + s ) << 2 ];
126
127 for( i = 0; i < tmax; i++ ) {
128 ptr = dst;
129 for( j = 0; j < smax; j++ ) {
130 float r, g, b, min, max, mid;
131
132 r = src[0];
133 g = src[1];
134 b = src[2];
135
136 if( colorScale != 1.0f ) {
137 min = max = r;
138 if ( g < min ) min = g;
139 if ( b < min ) min = b;
140 if ( g > max ) max = g;
141 if ( b > max ) max = b;
142 mid = 0.5 * ( min + max );
143 r = mid + ( r - mid ) * colorScale;
144 g = mid + ( g - mid ) * colorScale;
145 b = mid + ( b - mid ) * colorScale;
146 }
147
148 if( !gl_modulate_hack->integer ) {
149 r *= gl_modulate->value;
150 g *= gl_modulate->value;
151 b *= gl_modulate->value;
152 }
153
154 max = g;
155 if( r > max ) {
156 max = r;
157 }
158 if( b > max ) {
159 max = b;
160 }
161
162 if( max > 255 ) {
163 r *= 255.0f / max;
164 g *= 255.0f / max;
165 b *= 255.0f / max;
166 }
167
168 //atu brightness adjustments
169 brightness = 255.0f * gl_brightness->value;
170 r += brightness;
171 g += brightness;
172 b += brightness;
173 if ( r > 255.0f ) r = 255.0f;
174 else if ( r < 0.0f ) r = 0.0f;
175 if ( g > 255.0f ) g = 255.0f;
176 else if ( g < 0.0f ) g = 0.0f;
177 if ( b > 255.0f ) b = 255.0f;
178 else if ( b < 0.0f ) b = 0.0f;
179
180 src[0] = r;
181 src[1] = g;
182 src[2] = b;
183
184 ptr[0] = src[0];
185 ptr[1] = src[1];
186 ptr[2] = src[2];
187 ptr[3] = 255;
188
189 src += 3; ptr += 4;
190 }
191 dst += LM_BLOCK_WIDTH * 4;
192 }
193
194 surf->lightmapnum = lm.numMaps + 1;
195
196 s = ( s << 4 ) + 8;
197 t = ( t << 4 ) + 8;
198
199 poly = surf->polys;
200 vert = poly->vertices;
201 for( i = 0; i < poly->numVerts; i++ ) {
202 vert[5] = vert[3] - surf->texturemins[0] + s;
203 vert[6] = vert[4] - surf->texturemins[1] + t;
204 vert[5] /= LM_BLOCK_WIDTH * 16;
205 vert[6] /= LM_BLOCK_HEIGHT * 16;
206 vert += VERTEX_SIZE;
207 }
208
209 return 0;
210 }
211
212 /*
213 =============================================================================
214
215 POLYGONS BUILDING
216
217 =============================================================================
218 */
219
GL_CalcSurfaceExtents(bspSurface_t * surf)220 static void GL_CalcSurfaceExtents( bspSurface_t *surf ) {
221 vec2_t mins, maxs;
222 int bmins[2], bmaxs[2];
223 int i;
224 bspPoly_t *poly = surf->polys;
225 vec_t *vert;
226
227 mins[0] = mins[1] = 99999;
228 maxs[0] = maxs[1] = -99999;
229
230 vert = poly->vertices;
231 for( i = 0; i < poly->numVerts; i++ ) {
232 if( mins[0] > vert[3] ) mins[0] = vert[3];
233 if( maxs[0] < vert[3] ) maxs[0] = vert[3];
234
235 if( mins[1] > vert[4] ) mins[1] = vert[4];
236 if( maxs[1] < vert[4] ) maxs[1] = vert[4];
237
238 vert += VERTEX_SIZE;
239 }
240
241 bmins[0] = floor( mins[0] / 16 );
242 bmins[1] = floor( mins[1] / 16 );
243 bmaxs[0] = ceil( maxs[0] / 16 );
244 bmaxs[1] = ceil( maxs[1] / 16 );
245
246 surf->texturemins[0] = bmins[0] << 4;
247 surf->texturemins[1] = bmins[1] << 4;
248
249 surf->extents[0] = ( bmaxs[0] - bmins[0] ) << 4;
250 surf->extents[1] = ( bmaxs[1] - bmins[1] ) << 4;
251
252 }
253
254 #define NOLIGHT_MASK \
255 (SURF_SKY|SURF_WARP|SURF_TRANS33|SURF_TRANS66)
256
GL_BuildSurfacePoly(bspSurface_t * surf)257 static int GL_BuildSurfacePoly( bspSurface_t *surf ) {
258 int *src_surfedge;
259 dvertex_t *src_vert;
260 dedge_t *src_edge;
261 vec_t *dst_vert;
262 bspTexinfo_t *texinfo;
263 bspPoly_t *poly;
264 int i;
265 int index, vertIndex;
266 int numEdges = surf->numSurfEdges;
267
268 poly = sys.HunkAlloc( &r_world.pool, sizeof( *poly ) +
269 sizeof( vec_t ) * VERTEX_SIZE * ( numEdges - 1 ) );
270 poly->next = NULL;
271 poly->numVerts = numEdges;
272 poly->numIndices = ( numEdges - 2 ) * 3;
273 surf->polys = poly;
274
275 texinfo = surf->texinfo;
276 if( !surf->lightmap || ( texinfo->flags & NOLIGHT_MASK ) ||
277 gl_fullbright->integer )
278 {
279 surf->type = DSURF_NOLM;
280 } else {
281 surf->type = DSURF_POLY;
282 }
283
284 src_surfedge = surf->firstSurfEdge;
285 dst_vert = poly->vertices;
286 for( i = 0; i < numEdges; i++ ) {
287 index = *src_surfedge++;
288
289 vertIndex = 0;
290 if( index < 0 ) {
291 index = -index;
292 vertIndex = 1;
293 }
294
295 if( index >= r_world.numEdges ) {
296 printf( "LoadFace: bad edge index" );
297 return -1;
298 }
299
300 src_edge = r_world.edges + index;
301 src_vert = r_world.vertices + src_edge->v[vertIndex];
302
303 VectorCopy( src_vert->point, dst_vert );
304
305 /* texture coordinates */
306 dst_vert[3] = DotProduct( dst_vert, texinfo->axis[0] ) +
307 texinfo->offset[0];
308 dst_vert[4] = DotProduct( dst_vert, texinfo->axis[1] ) +
309 texinfo->offset[1];
310
311 /* lightmap coordinates */
312 dst_vert[5] = 0;
313 dst_vert[6] = 0;
314
315 dst_vert += VERTEX_SIZE;
316 }
317
318 return 0;
319
320 }
321
322 #define SUBDIVIDE_SIZE 64
323 #define SUBDIVIDE_VERTS 64
324
325 static bspSurface_t *warpsurf;
326
BoundPolygon(int numVerts,const vec_t * verts,vec3_t mins,vec3_t maxs)327 static void BoundPolygon( int numVerts, const vec_t *verts,
328 vec3_t mins, vec3_t maxs )
329 {
330 ClearBounds( mins, maxs );
331
332 while( numVerts-- ) {
333 AddPointToBounds( verts, mins, maxs );
334 verts += 3;
335 }
336 }
337
338
SubdividePolygon_r(int numVerts,vec_t * verts)339 static void SubdividePolygon_r( int numVerts, vec_t *verts ) {
340 int i, j;
341 vec3_t front[SUBDIVIDE_VERTS];
342 vec3_t back[SUBDIVIDE_VERTS];
343 vec_t dist[SUBDIVIDE_VERTS];
344 vec3_t mins, maxs;
345 int f, b;
346 vec_t mid, frac, scale, *v;
347 bspPoly_t *poly;
348 vec3_t total;
349 vec_t total_s, total_t;
350 bspTexinfo_t *texinfo;
351
352 if( numVerts > SUBDIVIDE_VERTS - 4 ) {
353 Com_Error( ERR_DROP, "SubdividePolygon_r: numVerts = %d", numVerts );
354 }
355
356 BoundPolygon( numVerts, verts, mins, maxs );
357
358 for( i = 0; i < 3; i++ ) {
359 mid = ( mins[i] + maxs[i] ) * 0.5f;
360 mid = SUBDIVIDE_SIZE * floor( mid / SUBDIVIDE_SIZE + 0.5f );
361 if( mid - mins[i] < 8 ) {
362 continue;
363 }
364 if( maxs[i] - mid < 8 ) {
365 continue;
366 }
367
368 v = verts + i;
369 for( j = 0; j < numVerts; j++, v += 3 ) {
370 dist[j] = *v - mid;
371 }
372
373 dist[j] = dist[0];
374 VectorCopy( verts, v - i );
375
376 f = b = 0;
377 v = verts;
378 for( j = 0; j < numVerts; j++, v += 3 ) {
379 if( dist[j] >= 0 ) {
380 VectorCopy( v, front[f] ); f++;
381 }
382 if( dist[j] <= 0 ) {
383 VectorCopy( v, back[b] ); b++;
384 }
385 if( dist[j] == 0 || dist[j+1] == 0 ) {
386 continue;
387 }
388 if( ( dist[j] > 0 ) != ( dist[j+1] > 0 ) ) {
389 frac = dist[j] / ( dist[j] - dist[j+1] );
390 front[f][0] = back[b][0] = v[0] + frac * ( v[3+0] - v[0] );
391 front[f][1] = back[b][1] = v[1] + frac * ( v[3+1] - v[1] );
392 front[f][2] = back[b][2] = v[2] + frac * ( v[3+2] - v[2] );
393 f++; b++;
394 }
395 }
396
397 SubdividePolygon_r( f, front[0] );
398 SubdividePolygon_r( b, back[0] );
399 return;
400 }
401
402 poly = sys.HunkAlloc( &r_world.pool, sizeof( *poly ) +
403 sizeof( vec_t ) * VERTEX_SIZE * numVerts );
404 poly->next = warpsurf->polys;
405 poly->numVerts = numVerts + 1;
406 poly->numIndices = numVerts * 3;
407 warpsurf->polys = poly;
408
409 texinfo = warpsurf->texinfo;
410 VectorClear( total );
411 total_s = total_t = 0;
412 v = poly->vertices + VERTEX_SIZE;
413 for( i = 0; i < numVerts; i++ ) {
414 VectorCopy( verts, v );
415 v[3] = DotProduct( verts, texinfo->axis[0] );
416 v[4] = DotProduct( verts, texinfo->axis[1] );
417 total_s += v[3];
418 total_t += v[4];
419 VectorAdd( total, verts, total );
420 verts += 3; v += VERTEX_SIZE;
421 }
422
423 /* middle point */
424 v = poly->vertices;
425 scale = 1.0f / numVerts;
426 VectorScale( total, scale, v );
427 v[3] = total_s * scale;
428 v[4] = total_t * scale;
429
430 }
431
GL_BuildSurfaceWarpPolys(bspSurface_t * surf)432 static int GL_BuildSurfaceWarpPolys( bspSurface_t *surf ) {
433 int *src_surfedge;
434 dvertex_t *src_vert;
435 dedge_t *src_edge;
436 vec_t *dst_vert;
437 int i;
438 int index, vertIndex;
439 int numEdges = surf->numSurfEdges;
440 vec3_t verts[SUBDIVIDE_VERTS];
441
442 surf->polys = NULL;
443 surf->type = DSURF_WARP;
444 warpsurf = surf;
445
446 src_surfedge = surf->firstSurfEdge;
447 dst_vert = verts[0];
448 for( i = 0; i < numEdges; i++ ) {
449 index = *src_surfedge++;
450
451 vertIndex = 0;
452 if( index < 0 ) {
453 index = -index;
454 vertIndex = 1;
455 }
456
457 if( index >= r_world.numEdges ) {
458 printf( "LoadFace: bad edge index" );
459 return -1;
460 }
461
462 src_edge = r_world.edges + index;
463 src_vert = r_world.vertices + src_edge->v[vertIndex];
464
465 VectorCopy( src_vert->point, dst_vert );
466
467 dst_vert += 3;
468 }
469
470 SubdividePolygon_r( numEdges, verts[0] );
471
472 return 0;
473
474 }
475
GL_NormalizeSurfaceTexcoords(bspSurface_t * surf)476 static void GL_NormalizeSurfaceTexcoords( bspSurface_t *surf ) {
477 bspTexinfo_t *texinfo = surf->texinfo;
478 bspPoly_t *poly = surf->polys;
479 vec_t *vert;
480 tcoord_t *tc;
481 int i;
482 vec2_t scale;
483
484 surf->normalizedTC = sys.HunkAlloc( &r_world.pool, sizeof( tcoord_t ) * poly->numVerts );
485
486 scale[0] = 1.0f / texinfo->image->width;
487 scale[1] = 1.0f / texinfo->image->height;
488
489 tc = surf->normalizedTC;
490 vert = poly->vertices;
491 for( i = 0; i < poly->numVerts; i++ ) {
492 vert[3] *= scale[0];
493 vert[4] *= scale[1];
494 tc->st[0] = DotProduct( vert, texinfo->normalizedAxis[0] );
495 tc->st[1] = DotProduct( vert, texinfo->normalizedAxis[1] );
496
497 vert += VERTEX_SIZE; tc++;
498 }
499 }
500
GL_PostProcessSurface(bspSurface_t * surf)501 int GL_PostProcessSurface( bspSurface_t *surf ) {
502 bspTexinfo_t *texinfo = surf->texinfo;
503
504 if( texinfo->flags & SURF_SKY ) {
505 }
506 if( ( texinfo->flags & SURF_WARP ) && gl_subdivide->integer ) {
507 if( GL_BuildSurfaceWarpPolys( surf ) ) {
508 return -1;
509 }
510 } else {
511 if( GL_BuildSurfacePoly( surf ) ) {
512 return -1;
513 }
514 GL_CalcSurfaceExtents( surf );
515 }
516
517 if( surf->type == DSURF_POLY ) {
518 if( LM_BuildSurfaceLightmap( surf ) ) {
519 return -1;
520 }
521 }
522
523 if( !( texinfo->flags & (SURF_SKY|SURF_WARP) ) ) {
524 GL_NormalizeSurfaceTexcoords( surf );
525 }
526
527 if( ( texinfo->flags & SURF_WARP ) && !gl_subdivide->integer ) {
528 GL_NormalizeSurfaceTexcoords( surf );
529 }
530
531 return 0;
532 }
533
GL_BeginPostProcessing(void)534 void GL_BeginPostProcessing( void ) {
535 lm.numMaps = 0;
536 LM_InitBlock();
537
538 gl_coloredlightmaps = cvar.Get( "gl_coloredlightmaps", "1",
539 CVAR_ARCHIVE|CVAR_LATCHED );
540 gl_brightness = cvar.Get( "gl_brightness", "0",
541 CVAR_ARCHIVE|CVAR_LATCHED );
542 gl_modulate_hack = cvar.Get( "gl_modulate_hack", "0", CVAR_LATCHED );
543
544 if( gl_coloredlightmaps->value < 0 ) {
545 cvar.Set( "gl_coloredlightmaps", "0" );
546 } else if( gl_coloredlightmaps->value > 1 ) {
547 cvar.Set( "gl_coloredlightmaps", "1" );
548 }
549
550 if( gl_brightness->value < -1 ) {
551 cvar.Set( "gl_brightness", "-1" );
552 } else if( gl_brightness->value > 1 ) {
553 cvar.Set( "gl_brightness", "1" );
554 }
555
556 brightness = gl_brightness->value;
557 colorScale = gl_coloredlightmaps->value;
558 }
559
GL_EndPostProcessing(void)560 void GL_EndPostProcessing( void ) {
561 int i;
562
563 for( i = 0; i < LM_BLOCK_WIDTH; i++ ) {
564 if( lm.inuse[i] ) {
565 LM_UploadBlock();
566 break;
567 }
568 }
569
570 Com_DPrintf( "GL_EndPostProcessing: %d lightmaps built\n", lm.numMaps );
571 }
572
573
574