aboutsummaryrefslogtreecommitdiffstats
path: root/q3map/lightmaps.c
diff options
context:
space:
mode:
Diffstat (limited to 'q3map/lightmaps.c')
-rwxr-xr-xq3map/lightmaps.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/q3map/lightmaps.c b/q3map/lightmaps.c
new file mode 100755
index 0000000..95064e5
--- /dev/null
+++ b/q3map/lightmaps.c
@@ -0,0 +1,395 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 2 of the License,
+or (at your option) any later version.
+
+Quake III Arena source code is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Foobar; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+#include "qbsp.h"
+
+
+/*
+
+ Lightmap allocation has to be done after all flood filling and
+ visible surface determination.
+
+*/
+
+int numSortShaders;
+mapDrawSurface_t *surfsOnShader[MAX_MAP_SHADERS];
+
+
+int allocated[LIGHTMAP_WIDTH];
+
+int numLightmaps = 1;
+int c_exactLightmap;
+
+
+void PrepareNewLightmap( void ) {
+ memset( allocated, 0, sizeof( allocated ) );
+ numLightmaps++;
+}
+
+/*
+===============
+AllocLMBlock
+
+returns a texture number and the position inside it
+===============
+*/
+qboolean AllocLMBlock (int w, int h, int *x, int *y)
+{
+ int i, j;
+ int best, best2;
+
+ best = LIGHTMAP_HEIGHT;
+
+ for ( i=0 ; i <= LIGHTMAP_WIDTH-w ; i++ ) {
+ best2 = 0;
+
+ for (j=0 ; j<w ; j++) {
+ if (allocated[i+j] >= best) {
+ break;
+ }
+ if (allocated[i+j] > best2) {
+ best2 = allocated[i+j];
+ }
+ }
+ if (j == w) { // this is a valid spot
+ *x = i;
+ *y = best = best2;
+ }
+ }
+
+ if (best + h > LIGHTMAP_HEIGHT) {
+ return qfalse;
+ }
+
+ for (i=0 ; i<w ; i++) {
+ allocated[*x + i] = best + h;
+ }
+
+ return qtrue;
+}
+
+
+/*
+===================
+AllocateLightmapForPatch
+===================
+*/
+//#define LIGHTMAP_PATCHSHIFT
+
+void AllocateLightmapForPatch( mapDrawSurface_t *ds ) {
+ int i, j, k;
+ drawVert_t *verts;
+ int w, h;
+ int x, y;
+ float s, t;
+ mesh_t mesh, *subdividedMesh, *tempMesh, *newmesh;
+ int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_HEIGHT], ssize;
+
+ verts = ds->verts;
+
+ mesh.width = ds->patchWidth;
+ mesh.height = ds->patchHeight;
+ mesh.verts = verts;
+ newmesh = SubdivideMesh( mesh, 8, 999 );
+
+ PutMeshOnCurve( *newmesh );
+ tempMesh = RemoveLinearMeshColumnsRows( newmesh );
+ FreeMesh(newmesh);
+
+ ssize = samplesize;
+ if (ds->shaderInfo->lightmapSampleSize)
+ ssize = ds->shaderInfo->lightmapSampleSize;
+
+#ifdef LIGHTMAP_PATCHSHIFT
+ subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH-1, widthtable, heighttable);
+#else
+ subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH, widthtable, heighttable);
+#endif
+
+ w = subdividedMesh->width;
+ h = subdividedMesh->height;
+
+#ifdef LIGHTMAP_PATCHSHIFT
+ w++;
+ h++;
+#endif
+
+ FreeMesh(subdividedMesh);
+
+ // allocate the lightmap
+ c_exactLightmap += w * h;
+
+ if ( !AllocLMBlock( w, h, &x, &y ) ) {
+ PrepareNewLightmap();
+ if ( !AllocLMBlock( w, h, &x, &y ) ) {
+ Error("Entity %i, brush %i: Lightmap allocation failed",
+ ds->mapBrush->entitynum, ds->mapBrush->brushnum );
+ }
+ }
+
+#ifdef LIGHTMAP_PATCHSHIFT
+ w--;
+ h--;
+#endif
+
+ // set the lightmap texture coordinates in the drawVerts
+ ds->lightmapNum = numLightmaps - 1;
+ ds->lightmapWidth = w;
+ ds->lightmapHeight = h;
+ ds->lightmapX = x;
+ ds->lightmapY = y;
+
+ for ( i = 0 ; i < ds->patchWidth ; i++ ) {
+ for ( k = 0 ; k < w ; k++ ) {
+ if ( originalWidths[k] >= i ) {
+ break;
+ }
+ }
+ if (k >= w)
+ k = w-1;
+ s = x + k;
+ for ( j = 0 ; j < ds->patchHeight ; j++ ) {
+ for ( k = 0 ; k < h ; k++ ) {
+ if ( originalHeights[k] >= j ) {
+ break;
+ }
+ }
+ if (k >= h)
+ k = h-1;
+ t = y + k;
+ verts[i + j * ds->patchWidth].lightmap[0] = ( s + 0.5 ) / LIGHTMAP_WIDTH;
+ verts[i + j * ds->patchWidth].lightmap[1] = ( t + 0.5 ) / LIGHTMAP_HEIGHT;
+ }
+ }
+}
+
+
+/*
+===================
+AllocateLightmapForSurface
+===================
+*/
+//#define LIGHTMAP_BLOCK 16
+void AllocateLightmapForSurface( mapDrawSurface_t *ds ) {
+ vec3_t mins, maxs, size, exactSize, delta;
+ int i;
+ drawVert_t *verts;
+ int w, h;
+ int x, y, ssize;
+ int axis;
+ vec3_t vecs[2];
+ float s, t;
+ vec3_t origin;
+ plane_t *plane;
+ float d;
+ vec3_t planeNormal;
+
+ if ( ds->patch ) {
+ AllocateLightmapForPatch( ds );
+ return;
+ }
+
+ ssize = samplesize;
+ if (ds->shaderInfo->lightmapSampleSize)
+ ssize = ds->shaderInfo->lightmapSampleSize;
+
+ plane = &mapplanes[ ds->side->planenum ];
+
+ // bound the surface
+ ClearBounds( mins, maxs );
+ verts = ds->verts;
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ AddPointToBounds( verts[i].xyz, mins, maxs );
+ }
+
+ // round to the lightmap resolution
+ for ( i = 0 ; i < 3 ; i++ ) {
+ exactSize[i] = maxs[i] - mins[i];
+ mins[i] = ssize * floor( mins[i] / ssize );
+ maxs[i] = ssize * ceil( maxs[i] / ssize );
+ size[i] = (maxs[i] - mins[i]) / ssize + 1;
+ }
+
+ // the two largest axis will be the lightmap size
+ memset( vecs, 0, sizeof( vecs ) );
+
+ planeNormal[0] = fabs( plane->normal[0] );
+ planeNormal[1] = fabs( plane->normal[1] );
+ planeNormal[2] = fabs( plane->normal[2] );
+
+ if ( planeNormal[0] >= planeNormal[1] && planeNormal[0] >= planeNormal[2] ) {
+ w = size[1];
+ h = size[2];
+ axis = 0;
+ vecs[0][1] = 1.0 / ssize;
+ vecs[1][2] = 1.0 / ssize;
+ } else if ( planeNormal[1] >= planeNormal[0] && planeNormal[1] >= planeNormal[2] ) {
+ w = size[0];
+ h = size[2];
+ axis = 1;
+ vecs[0][0] = 1.0 / ssize;
+ vecs[1][2] = 1.0 / ssize;
+ } else {
+ w = size[0];
+ h = size[1];
+ axis = 2;
+ vecs[0][0] = 1.0 / ssize;
+ vecs[1][1] = 1.0 / ssize;
+ }
+
+ if ( !plane->normal[axis] ) {
+ Error( "Chose a 0 valued axis" );
+ }
+
+ if ( w > LIGHTMAP_WIDTH ) {
+ VectorScale ( vecs[0], (float)LIGHTMAP_WIDTH/w, vecs[0] );
+ w = LIGHTMAP_WIDTH;
+ }
+
+ if ( h > LIGHTMAP_HEIGHT ) {
+ VectorScale ( vecs[1], (float)LIGHTMAP_HEIGHT/h, vecs[1] );
+ h = LIGHTMAP_HEIGHT;
+ }
+
+ c_exactLightmap += w * h;
+
+ if ( !AllocLMBlock( w, h, &x, &y ) ) {
+ PrepareNewLightmap();
+ if ( !AllocLMBlock( w, h, &x, &y ) ) {
+ Error("Entity %i, brush %i: Lightmap allocation failed",
+ ds->mapBrush->entitynum, ds->mapBrush->brushnum );
+ }
+ }
+
+ // set the lightmap texture coordinates in the drawVerts
+ ds->lightmapNum = numLightmaps - 1;
+ ds->lightmapWidth = w;
+ ds->lightmapHeight = h;
+ ds->lightmapX = x;
+ ds->lightmapY = y;
+
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ VectorSubtract( verts[i].xyz, mins, delta );
+ s = DotProduct( delta, vecs[0] ) + x + 0.5;
+ t = DotProduct( delta, vecs[1] ) + y + 0.5;
+ verts[i].lightmap[0] = s / LIGHTMAP_WIDTH;
+ verts[i].lightmap[1] = t / LIGHTMAP_HEIGHT;
+ }
+
+ // calculate the world coordinates of the lightmap samples
+
+ // project mins onto plane to get origin
+ d = DotProduct( mins, plane->normal ) - plane->dist;
+ d /= plane->normal[ axis ];
+ VectorCopy( mins, origin );
+ origin[axis] -= d;
+
+ // project stepped lightmap blocks and subtract to get planevecs
+ for ( i = 0 ; i < 2 ; i++ ) {
+ vec3_t normalized;
+ float len;
+
+ len = VectorNormalize( vecs[i], normalized );
+ VectorScale( normalized, (1.0/len), vecs[i] );
+ d = DotProduct( vecs[i], plane->normal );
+ d /= plane->normal[ axis ];
+ vecs[i][axis] -= d;
+ }
+
+ VectorCopy( origin, ds->lightmapOrigin );
+ VectorCopy( vecs[0], ds->lightmapVecs[0] );
+ VectorCopy( vecs[1], ds->lightmapVecs[1] );
+ VectorCopy( plane->normal, ds->lightmapVecs[2] );
+}
+
+/*
+===================
+AllocateLightmaps
+===================
+*/
+void AllocateLightmaps( entity_t *e ) {
+ int i, j;
+ mapDrawSurface_t *ds;
+ shaderInfo_t *si;
+
+ qprintf ("--- AllocateLightmaps ---\n");
+
+
+ // sort all surfaces by shader so common shaders will usually
+ // be in the same lightmap
+ numSortShaders = 0;
+
+ for ( i = e->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
+ ds = &mapDrawSurfs[i];
+ if ( !ds->numVerts ) {
+ continue; // leftover from a surface subdivision
+ }
+ if ( ds->miscModel ) {
+ continue;
+ }
+ if ( !ds->patch ) {
+ VectorCopy( mapplanes[ds->side->planenum].normal, ds->lightmapVecs[2] );
+ }
+
+ // search for this shader
+ for ( j = 0 ; j < numSortShaders ; j++ ) {
+ if ( ds->shaderInfo == surfsOnShader[j]->shaderInfo ) {
+ ds->nextOnShader = surfsOnShader[j];
+ surfsOnShader[j] = ds;
+ break;
+ }
+ }
+ if ( j == numSortShaders ) {
+ if ( numSortShaders >= MAX_MAP_SHADERS ) {
+ Error( "MAX_MAP_SHADERS" );
+ }
+ surfsOnShader[j] = ds;
+ numSortShaders++;
+ }
+ }
+ qprintf( "%5i unique shaders\n", numSortShaders );
+
+ // for each shader, allocate lightmaps for each surface
+
+// numLightmaps = 0;
+// PrepareNewLightmap();
+
+ for ( i = 0 ; i < numSortShaders ; i++ ) {
+ si = surfsOnShader[i]->shaderInfo;
+
+ for ( ds = surfsOnShader[i] ; ds ; ds = ds->nextOnShader ) {
+ // some surfaces don't need lightmaps allocated for them
+ if ( si->surfaceFlags & SURF_NOLIGHTMAP ) {
+ ds->lightmapNum = -1;
+ } else if ( si->surfaceFlags & SURF_POINTLIGHT ) {
+ ds->lightmapNum = -3;
+ } else {
+ AllocateLightmapForSurface( ds );
+ }
+ }
+ }
+
+ qprintf( "%7i exact lightmap texels\n", c_exactLightmap );
+ qprintf( "%7i block lightmap texels\n", numLightmaps * LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT );
+}
+
+
+