]> icculus.org git repositories - divverent/netradiant.git/blob - tools/quake3/q3map2/lightmaps.c
fix two deluxemap bugs
[divverent/netradiant.git] / tools / quake3 / q3map2 / lightmaps.c
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 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 GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "qbsp.h"
23
24
25 /*
26
27   Lightmap allocation has to be done after all flood filling and
28   visible surface determination.
29
30 */
31
32 int                                     numSortShaders;
33 mapDrawSurface_t        *surfsOnShader[ MAX_MAP_SHADERS ];
34
35
36 int             allocated[ LIGHTMAP_WIDTH ];
37
38 int             numLightmaps = 1;
39 int             c_exactLightmap = 0;
40 int             c_planarPatch = 0;
41 int             c_nonplanarLightmap = 0;
42
43
44 void PrepareNewLightmap( void ) {
45         memset( allocated, 0, sizeof( allocated ) );
46         numLightmaps++;
47 }
48
49 /*
50 ===============
51 AllocLMBlock
52
53 returns a texture number and the position inside it
54 ===============
55 */
56 qboolean AllocLMBlock (int w, int h, int *x, int *y)
57 {
58         int             i, j;
59         int             best, best2;
60
61         best = LIGHTMAP_HEIGHT;
62
63         for ( i=0 ; i <= LIGHTMAP_WIDTH-w ; i++ ) {
64                 best2 = 0;
65
66                 for (j=0 ; j<w ; j++) {
67                         if (allocated[i+j] >= best) {
68                                 break;
69                         }
70                         if (allocated[i+j] > best2) {
71                                 best2 = allocated[i+j];
72                         }
73                 }
74                 if (j == w)     {       // this is a valid spot
75                         *x = i;
76                         *y = best = best2;
77                 }
78         }
79
80         if (best + h > LIGHTMAP_HEIGHT) {
81                 return qfalse;
82         }
83
84         for (i=0 ; i<w ; i++) {
85                 allocated[*x + i] = best + h;
86         }
87
88         return qtrue;
89 }
90
91
92 /*
93 ===================
94 AllocateLightmapForPatch
95 ===================
96 */
97 //#define LIGHTMAP_PATCHSHIFT
98
99 void AllocateLightmapForPatch( mapDrawSurface_t *ds )
100 {
101         int                     i, j, k;
102         drawVert_t      *verts;
103         int                     w, h;
104         int                     x, y;
105         float           s, t;
106         mesh_t          mesh, *subdividedMesh, *tempMesh, *newmesh;
107         int                     widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_HEIGHT], ssize;
108
109         verts = ds->verts;
110
111         mesh.width = ds->patchWidth;
112         mesh.height = ds->patchHeight;
113         mesh.verts = verts;
114         newmesh = SubdivideMesh( mesh, 8, 999 );
115
116         PutMeshOnCurve( *newmesh );
117         tempMesh = RemoveLinearMeshColumnsRows( newmesh );
118         FreeMesh(newmesh);
119         
120         /* get sample size */
121         ssize = ds->sampleSize;
122         
123         
124 #ifdef LIGHTMAP_PATCHSHIFT
125         subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH-1, widthtable, heighttable );
126 #else
127         subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH, widthtable, heighttable );
128 #endif
129
130         w = subdividedMesh->width;
131         h = subdividedMesh->height;
132
133 #ifdef LIGHTMAP_PATCHSHIFT
134         w++;
135         h++;
136 #endif
137
138         FreeMesh(subdividedMesh);
139
140         // allocate the lightmap
141         c_exactLightmap += w * h;
142
143         if ( !AllocLMBlock( w, h, &x, &y ) ) {
144                 PrepareNewLightmap();
145                 if ( !AllocLMBlock( w, h, &x, &y ) )
146                 {
147                         Error("Entity %i, brush %i: Lightmap allocation failed", 
148                                 ds->mapBrush->entitynum, ds->mapBrush->brushnum );
149                 }
150         }
151
152 #ifdef LIGHTMAP_PATCHSHIFT
153         w--;
154         h--;
155 #endif
156
157         // set the lightmap texture coordinates in the drawVerts
158         ds->lightmapNum = numLightmaps - 1;
159         ds->lightmapWidth = w;
160         ds->lightmapHeight = h;
161         ds->lightmapX = x;
162         ds->lightmapY = y;
163
164         for ( i = 0 ; i < ds->patchWidth ; i++ ) {
165                 for ( k = 0 ; k < w ; k++ ) {
166                         if ( originalWidths[k] >= i ) {
167                                 break;
168                         }
169                 }
170                 if (k >= w)
171                         k = w-1;
172                 s = x + k;
173                 for ( j = 0 ; j < ds->patchHeight ; j++ ) {
174                         for ( k = 0 ; k < h ; k++ ) {
175                                 if ( originalHeights[k] >= j ) {
176                                         break;
177                                 }
178                         }
179                         if (k >= h)
180                                 k = h-1;
181                         t = y + k;
182                         verts[i + j * ds->patchWidth].lightmap[0] = ( s + 0.5 ) / LIGHTMAP_WIDTH;
183                         verts[i + j * ds->patchWidth].lightmap[1] = ( t + 0.5 ) / LIGHTMAP_HEIGHT;
184                 }
185         }
186 }
187
188
189 /*
190 ===================
191 AllocateLightmapForSurface
192 ===================
193 */
194
195 //#define       LIGHTMAP_BLOCK  16
196
197 void AllocateLightmapForSurface( mapDrawSurface_t *ds )
198 {
199         vec3_t          mins, maxs, size, exactSize, delta;
200         int                     i;
201         drawVert_t      *verts;
202         int                     w, h;
203         int                     x, y, ssize;
204         int                     axis;
205         vec3_t          vecs[ 2 ];
206         float           s, t;
207         vec3_t          origin;
208         vec4_t          plane;
209         float           d;
210         
211         
212         /* debug code */
213         #if 0
214                 if( ds->type == SURF_META && ds->planar == qfalse )
215                         Sys_Printf( "NPMS: %3d vertexes, %s\n", ds->numVerts, ds->shaderInfo->shader );
216                 else if( ds->type == SURF_META && ds->planar == qtrue )
217                         Sys_Printf( "PMS:  %3d vertexes, %s\n", ds->numVerts, ds->shaderInfo->shader );
218         #endif
219         
220         /* ydnar: handle planar patches */
221         if( noPatchFix == qtrue || (ds->type == SURF_PATCH && ds->planeNum < 0) )
222         {
223                 AllocateLightmapForPatch( ds );
224                 return;
225         }
226         
227         /* get sample size */
228         ssize = ds->sampleSize;
229         
230         /* bound the surface */
231         ClearBounds( mins, maxs );
232         verts = ds->verts;
233         for ( i = 0 ; i < ds->numVerts ; i++ )
234                 AddPointToBounds( verts[i].xyz, mins, maxs );
235         
236         /* round to the lightmap resolution */
237         for( i = 0; i < 3; i++ )
238         {
239                 exactSize[i] = maxs[i] - mins[i];
240                 mins[i] = ssize * floor( mins[i] / ssize );
241                 maxs[i] = ssize * ceil( maxs[i] / ssize );
242                 size[i] = (maxs[i] - mins[i]) / ssize + 1;
243         }
244         
245         /* ydnar: lightmap projection axis is already stored */
246         memset( vecs, 0, sizeof( vecs ) );
247         
248         /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
249         if( ds->lightmapAxis[ 2 ] >= ds->lightmapAxis[ 0 ] && ds->lightmapAxis[ 2 ] >= ds->lightmapAxis[ 1 ] )
250         {
251                 w = size[ 0 ];
252                 h = size[ 1 ];
253                 axis = 2;
254                 vecs[ 0 ][ 0 ] = 1.0 / ssize;
255                 vecs[ 1 ][ 1 ] = 1.0 / ssize;
256         }
257         else if( ds->lightmapAxis[ 0 ] >= ds->lightmapAxis[ 1 ] && ds->lightmapAxis[ 0 ] >= ds->lightmapAxis[ 2 ] )
258         {
259                 w = size[ 1 ];
260                 h = size[ 2 ];
261                 axis = 0;
262                 vecs[ 0 ][ 1 ] = 1.0 / ssize;
263                 vecs[ 1 ][ 2 ] = 1.0 / ssize;
264         }
265         else
266         {
267                 w = size[ 0 ];
268                 h = size[ 2 ];
269                 axis = 1;
270                 vecs[ 0 ][ 0 ] = 1.0 / ssize;
271                 vecs[ 1 ][ 2 ] = 1.0 / ssize;
272         }
273         
274         /* odd check, given projection is now precalculated */
275         if( ds->lightmapAxis[ axis ] == 0 )
276                 Error( "Chose a 0 valued axis" );
277         
278         /* clamp to lightmap texture resolution */
279         if( w > LIGHTMAP_WIDTH )
280         {
281                 VectorScale ( vecs[0], (float) LIGHTMAP_WIDTH / w, vecs[0] );
282                 w = LIGHTMAP_WIDTH;
283         }
284         if( h > LIGHTMAP_HEIGHT )
285         {
286                 VectorScale ( vecs[1], (float) LIGHTMAP_HEIGHT / h, vecs[1] );
287                 h = LIGHTMAP_HEIGHT;
288         }
289         
290         
291         /* ydnar */
292         if( ds->planar == qfalse )
293                 c_nonplanarLightmap += w * h;
294         c_exactLightmap += w * h;
295         
296         
297         if( !AllocLMBlock( w, h, &x, &y ) )
298         {
299                 PrepareNewLightmap();
300                 if ( !AllocLMBlock( w, h, &x, &y ) )
301                 {
302                         Error( "Entity %i, brush %i: Lightmap allocation failed", 
303                                 ds->mapBrush->entitynum, ds->mapBrush->brushnum );
304                 }
305         }
306
307         /* set the lightmap texture coordinates in the drawVerts */
308         ds->lightmapNum = numLightmaps - 1;
309         ds->lightmapWidth = w;
310         ds->lightmapHeight = h;
311         ds->lightmapX = x;
312         ds->lightmapY = y;
313         for ( i = 0 ; i < ds->numVerts ; i++ )
314         {
315                 VectorSubtract( verts[i].xyz, mins, delta );
316                 s = DotProduct( delta, vecs[0] ) + x + 0.5;
317                 t = DotProduct( delta, vecs[1] ) + y + 0.5;
318                 verts[i].lightmap[0] = s / LIGHTMAP_WIDTH;
319                 verts[i].lightmap[1] = t / LIGHTMAP_HEIGHT;
320         }
321
322         /* calculate the world coordinates of the lightmap samples */
323         
324         /* construct a plane from the first vert and clear bounding box */
325         
326         /* project mins onto plane to get origin */
327         VectorCopy( ds->lightmapVecs[ 2 ], plane );
328         plane[ 3 ] = DotProduct( ds->verts[ 0 ].xyz, plane );
329         d = DotProduct( mins, plane ) - plane[ 3 ];
330         d /= plane[ axis ];
331         
332         //% d = DotProduct( mins, plane->normal ) - plane->dist;
333         //% d /= plane->normal[ axis ];
334         VectorCopy( mins, origin );
335         origin[ axis ] -= d;
336
337         /* project stepped lightmap blocks and subtract to get planevecs */
338         for( i = 0; i < 2; i++ )
339         {
340                 vec3_t  normalized;
341                 float   len;
342
343                 len = VectorNormalize( vecs[i], normalized );
344                 VectorScale( normalized, (1.0/len), vecs[i] );
345                 d = DotProduct( vecs[i], plane );
346                 d /= plane[ axis ];
347                 //%d = DotProduct( vecs[i], plane->normal );
348                 //%d /= plane->normal[ axis ];
349                 vecs[i][axis] -= d;
350         }
351         
352         /* store lightmap origin and vectors (fixme: make this work right) */
353         VectorCopy( origin, ds->lightmapOrigin );
354         //% VectorCopy( plane->normal, ds->lightmapVecs[ 2 ] );
355         
356         /* ydnar: lightmap vectors 0 and 1 are used for lod bounds, so don't overwrite */
357         if( ds->type == SURF_PATCH )
358                 c_planarPatch++;
359         
360         /* store lightmap vectors */
361         VectorCopy( vecs[ 0 ], ds->lightmapVecs[ 0 ] );
362         VectorCopy( vecs[ 1 ], ds->lightmapVecs[ 1 ] );
363         
364         /* ydnar: print some stats */
365         //Sys_FPrintf( SYS_VRB, "Lightmap block %3d (%3d, %3d) (%3d x %3d) emitted\n", (numLightmaps - 1), x, y, w, h );
366 }
367
368
369 /*
370 ===================
371 AllocateLightmaps
372 ===================
373 */
374 void AllocateLightmaps( entity_t *e )
375 {
376         int                                     i, j;
377         mapDrawSurface_t        *ds;
378         shaderInfo_t            *si;
379         
380         
381         /* note it */
382         Sys_FPrintf( SYS_VRB,"--- AllocateLightmaps ---\n" );
383         
384         
385         /* sort all surfaces by shader so common shaders will usually be in the same lightmap */
386         /* ydnar: this is done in two passes, because of an odd bug with lightmapped terrain */
387         numSortShaders = 0;
388         for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
389         {
390                 /* get surface and early out if possible */
391                 ds = &mapDrawSurfs[ i ];
392                 si = ds->shaderInfo;
393                 if( si->surfaceFlags & SURF_VERTEXLIT )
394                         continue;
395                 if( ds->numVerts <= 0 )
396                         continue;
397                 
398                 /* ydnar: handle brush faces and patches first */
399                 if( ds->type != SURF_FACE && ds->type != SURF_PATCH )
400                         continue;
401                 
402                 /* ydnar: this is unecessary because it should already be set */
403                 //% VectorCopy( ds->plane.normal, ds->lightmapVecs[ 2 ] );
404
405                 /* search for this shader */
406                 for( j = 0 ; j < numSortShaders; j++ )
407                 {
408                         if( ds->shaderInfo == surfsOnShader[ j ]->shaderInfo )
409                         {
410                                 ds->nextOnShader = surfsOnShader[ j ];
411                                 surfsOnShader[ j ] = ds;
412                                 break;
413                         }
414                 } 
415                 
416                 /* new shader */
417                 if( j == numSortShaders )
418                 {
419                         if( numSortShaders >= MAX_MAP_SHADERS )
420                                 Error( "MAX_MAP_SHADERS" );
421                         surfsOnShader[ j ] = ds;
422                         ds->nextOnShader = NULL;
423                         numSortShaders++;
424                 }
425         }
426         
427         /* second pass, to allocate lightmapped terrain last */
428         for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
429         {
430                 /* get surface and early out if possible */
431                 ds = &mapDrawSurfs[ i ];
432                 si = ds->shaderInfo;
433                 if( si->surfaceFlags & SURF_VERTEXLIT )
434                         continue;
435                 if( ds->numVerts <= 0 )
436                         continue;
437                 
438                 /* ydnar: this only handles metasurfaces and terrain */
439                 if( ds->type != SURF_TERRAIN && ds->type != SURF_META )
440                         continue;
441                 
442                 /* ydnar: a lightmap projection should be pre-stored for anything but excessively curved patches */
443                 if( VectorLength( ds->lightmapAxis ) <= 0 )
444                         continue;
445                 
446                 /* search for this shader */
447                 for( j = 0; j < numSortShaders; j++ )
448                 {
449                         if( ds->shaderInfo == surfsOnShader[ j ]->shaderInfo )
450                         {
451                                 ds->nextOnShader = surfsOnShader[ j ];
452                                 surfsOnShader[ j ] = ds;
453                                 break;
454                         }
455                 }
456                 
457                 /* new shader */
458                 if( j == numSortShaders )
459                 {
460                         if( numSortShaders >= MAX_MAP_SHADERS )
461                                 Error( "MAX_MAP_SHADERS" );
462                         surfsOnShader[ j ] = ds;
463                         ds->nextOnShader = NULL;
464                         numSortShaders++;
465                 }
466         }
467         
468         /* tot up shader count */
469         Sys_FPrintf( SYS_VRB, "%9d unique shaders\n", numSortShaders );
470         
471         /* for each shader, allocate lightmaps for each surface */
472         for( i = 0; i < numSortShaders; i++ )
473         {
474                 si = surfsOnShader[ i ]->shaderInfo;
475                 for( ds = surfsOnShader[ i ]; ds; ds = ds->nextOnShader )
476                 {
477                         /* ydnar: promoting pointlight above nolightmap */
478                         if( si->surfaceFlags & SURF_POINTLIGHT )
479                                 ds->lightmapNum = -3;
480                         else if( si->surfaceFlags & SURF_NOLIGHTMAP )
481                                 ds->lightmapNum = -1;
482                         else
483                                 AllocateLightmapForSurface( ds );
484                 }
485         }
486         
487         /* emit some statistics */
488         Sys_FPrintf( SYS_VRB, "%9d exact lightmap texels\n", c_exactLightmap );
489         Sys_FPrintf( SYS_VRB, "%9d block lightmap texels\n", numLightmaps * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT );
490         Sys_FPrintf( SYS_VRB, "%9d non-planar or terrain lightmap texels\n", c_nonplanarLightmap );
491         Sys_FPrintf( SYS_VRB, "%9d planar patch lightmaps\n", c_planarPatch );
492         Sys_FPrintf( SYS_VRB, "%9d lightmap textures, size: %d Kbytes\n", numLightmaps, (numLightmaps * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3) / 1024 );
493 }
494
495
496