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