2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\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
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
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
27 Lightmap allocation has to be done after all flood filling and
\r
28 visible surface determination.
\r
33 mapDrawSurface_t *surfsOnShader[ MAX_MAP_SHADERS ];
\r
36 int allocated[ LIGHTMAP_WIDTH ];
\r
38 int numLightmaps = 1;
\r
39 int c_exactLightmap = 0;
\r
40 int c_planarPatch = 0;
\r
41 int c_nonplanarLightmap = 0;
\r
44 void PrepareNewLightmap( void ) {
\r
45 memset( allocated, 0, sizeof( allocated ) );
\r
53 returns a texture number and the position inside it
\r
56 qboolean AllocLMBlock (int w, int h, int *x, int *y)
\r
61 best = LIGHTMAP_HEIGHT;
\r
63 for ( i=0 ; i <= LIGHTMAP_WIDTH-w ; i++ ) {
\r
66 for (j=0 ; j<w ; j++) {
\r
67 if (allocated[i+j] >= best) {
\r
70 if (allocated[i+j] > best2) {
\r
71 best2 = allocated[i+j];
\r
74 if (j == w) { // this is a valid spot
\r
80 if (best + h > LIGHTMAP_HEIGHT) {
\r
84 for (i=0 ; i<w ; i++) {
\r
85 allocated[*x + i] = best + h;
\r
94 AllocateLightmapForPatch
\r
97 //#define LIGHTMAP_PATCHSHIFT
\r
99 void AllocateLightmapForPatch( mapDrawSurface_t *ds )
\r
106 mesh_t mesh, *subdividedMesh, *tempMesh, *newmesh;
\r
107 int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_HEIGHT], ssize;
\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
116 PutMeshOnCurve( *newmesh );
\r
117 tempMesh = RemoveLinearMeshColumnsRows( newmesh );
\r
120 /* get sample size */
\r
121 ssize = ds->sampleSize;
\r
124 #ifdef LIGHTMAP_PATCHSHIFT
\r
125 subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH-1, widthtable, heighttable );
\r
127 subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH, widthtable, heighttable );
\r
130 w = subdividedMesh->width;
\r
131 h = subdividedMesh->height;
\r
133 #ifdef LIGHTMAP_PATCHSHIFT
\r
138 FreeMesh(subdividedMesh);
\r
140 // allocate the lightmap
\r
141 c_exactLightmap += w * h;
\r
143 if ( !AllocLMBlock( w, h, &x, &y ) ) {
\r
144 PrepareNewLightmap();
\r
145 if ( !AllocLMBlock( w, h, &x, &y ) )
\r
147 Error("Entity %i, brush %i: Lightmap allocation failed",
\r
148 ds->mapBrush->entitynum, ds->mapBrush->brushnum );
\r
152 #ifdef LIGHTMAP_PATCHSHIFT
\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
164 for ( i = 0 ; i < ds->patchWidth ; i++ ) {
\r
165 for ( k = 0 ; k < w ; k++ ) {
\r
166 if ( originalWidths[k] >= i ) {
\r
173 for ( j = 0 ; j < ds->patchHeight ; j++ ) {
\r
174 for ( k = 0 ; k < h ; k++ ) {
\r
175 if ( originalHeights[k] >= j ) {
\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
190 ===================
\r
191 AllocateLightmapForSurface
\r
192 ===================
\r
195 //#define LIGHTMAP_BLOCK 16
\r
197 void AllocateLightmapForSurface( mapDrawSurface_t *ds )
\r
199 vec3_t mins, maxs, size, exactSize, delta;
\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
220 /* ydnar: handle planar patches */
\r
221 if( noPatchFix == qtrue || (ds->type == SURF_PATCH && ds->planeNum < 0) )
\r
223 AllocateLightmapForPatch( ds );
\r
227 /* get sample size */
\r
228 ssize = ds->sampleSize;
\r
230 /* bound the surface */
\r
231 ClearBounds( mins, maxs );
\r
233 for ( i = 0 ; i < ds->numVerts ; i++ )
\r
234 AddPointToBounds( verts[i].xyz, mins, maxs );
\r
236 /* round to the lightmap resolution */
\r
237 for( i = 0; i < 3; i++ )
\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
245 /* ydnar: lightmap projection axis is already stored */
\r
246 memset( vecs, 0, sizeof( vecs ) );
\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
254 vecs[ 0 ][ 0 ] = 1.0 / ssize;
\r
255 vecs[ 1 ][ 1 ] = 1.0 / ssize;
\r
257 else if( ds->lightmapAxis[ 0 ] >= ds->lightmapAxis[ 1 ] && ds->lightmapAxis[ 0 ] >= ds->lightmapAxis[ 2 ] )
\r
262 vecs[ 0 ][ 1 ] = 1.0 / ssize;
\r
263 vecs[ 1 ][ 2 ] = 1.0 / ssize;
\r
270 vecs[ 0 ][ 0 ] = 1.0 / ssize;
\r
271 vecs[ 1 ][ 2 ] = 1.0 / ssize;
\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
278 /* clamp to lightmap texture resolution */
\r
279 if( w > LIGHTMAP_WIDTH )
\r
281 VectorScale ( vecs[0], (float) LIGHTMAP_WIDTH / w, vecs[0] );
\r
282 w = LIGHTMAP_WIDTH;
\r
284 if( h > LIGHTMAP_HEIGHT )
\r
286 VectorScale ( vecs[1], (float) LIGHTMAP_HEIGHT / h, vecs[1] );
\r
287 h = LIGHTMAP_HEIGHT;
\r
292 if( ds->planar == qfalse )
\r
293 c_nonplanarLightmap += w * h;
\r
294 c_exactLightmap += w * h;
\r
297 if( !AllocLMBlock( w, h, &x, &y ) )
\r
299 PrepareNewLightmap();
\r
300 if ( !AllocLMBlock( w, h, &x, &y ) )
\r
302 Error( "Entity %i, brush %i: Lightmap allocation failed",
\r
303 ds->mapBrush->entitynum, ds->mapBrush->brushnum );
\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
313 for ( i = 0 ; i < ds->numVerts ; i++ )
\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
322 /* calculate the world coordinates of the lightmap samples */
\r
324 /* construct a plane from the first vert and clear bounding box */
\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
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
337 /* project stepped lightmap blocks and subtract to get planevecs */
\r
338 for( i = 0; i < 2; i++ )
\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
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
356 /* ydnar: lightmap vectors 0 and 1 are used for lod bounds, so don't overwrite */
\r
357 if( ds->type == SURF_PATCH )
\r
360 /* store lightmap vectors */
\r
361 VectorCopy( vecs[ 0 ], ds->lightmapVecs[ 0 ] );
\r
362 VectorCopy( vecs[ 1 ], ds->lightmapVecs[ 1 ] );
\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
370 ===================
\r
372 ===================
\r
374 void AllocateLightmaps( entity_t *e )
\r
377 mapDrawSurface_t *ds;
\r
382 Sys_FPrintf( SYS_VRB,"--- AllocateLightmaps ---\n" );
\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
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
395 if( ds->numVerts <= 0 )
\r
398 /* ydnar: handle brush faces and patches first */
\r
399 if( ds->type != SURF_FACE && ds->type != SURF_PATCH )
\r
402 /* ydnar: this is unecessary because it should already be set */
\r
403 //% VectorCopy( ds->plane.normal, ds->lightmapVecs[ 2 ] );
\r
405 /* search for this shader */
\r
406 for( j = 0 ; j < numSortShaders; j++ )
\r
408 if( ds->shaderInfo == surfsOnShader[ j ]->shaderInfo )
\r
410 ds->nextOnShader = surfsOnShader[ j ];
\r
411 surfsOnShader[ j ] = ds;
\r
417 if( j == numSortShaders )
\r
419 if( numSortShaders >= MAX_MAP_SHADERS )
\r
420 Error( "MAX_MAP_SHADERS" );
\r
421 surfsOnShader[ j ] = ds;
\r
422 ds->nextOnShader = NULL;
\r
427 /* second pass, to allocate lightmapped terrain last */
\r
428 for( i = e->firstDrawSurf; i < numMapDrawSurfs; i++ )
\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
435 if( ds->numVerts <= 0 )
\r
438 /* ydnar: this only handles metasurfaces and terrain */
\r
439 if( ds->type != SURF_TERRAIN && ds->type != SURF_META )
\r
442 /* ydnar: a lightmap projection should be pre-stored for anything but excessively curved patches */
\r
443 if( VectorLength( ds->lightmapAxis ) <= 0 )
\r
446 /* search for this shader */
\r
447 for( j = 0; j < numSortShaders; j++ )
\r
449 if( ds->shaderInfo == surfsOnShader[ j ]->shaderInfo )
\r
451 ds->nextOnShader = surfsOnShader[ j ];
\r
452 surfsOnShader[ j ] = ds;
\r
458 if( j == numSortShaders )
\r
460 if( numSortShaders >= MAX_MAP_SHADERS )
\r
461 Error( "MAX_MAP_SHADERS" );
\r
462 surfsOnShader[ j ] = ds;
\r
463 ds->nextOnShader = NULL;
\r
468 /* tot up shader count */
\r
469 Sys_FPrintf( SYS_VRB, "%9d unique shaders\n", numSortShaders );
\r
471 /* for each shader, allocate lightmaps for each surface */
\r
472 for( i = 0; i < numSortShaders; i++ )
\r
474 si = surfsOnShader[ i ]->shaderInfo;
\r
475 for( ds = surfsOnShader[ i ]; ds; ds = ds->nextOnShader )
\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
483 AllocateLightmapForSurface( ds );
\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