lightmap.c

Go to the documentation of this file.
00001 
00005 /*
00006 Copyright (C) 1997-2001 Id Software, Inc.
00007 
00008 This program is free software; you can redistribute it and/or
00009 modify it under the terms of the GNU General Public License
00010 as published by the Free Software Foundation; either version 2
00011 of the License, or (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016 
00017 See the GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00023 */
00024 
00025 #include "lighting.h"
00026 #include "bsp.h"
00027 
00028 #define sun_angles          config.sun_angles[config.compile_for_day]
00029 #define sun_normal          config.sun_normal[config.compile_for_day]
00030 #define sun_color           config.sun_color[config.compile_for_day]
00031 #define sun_intensity       config.sun_intensity[config.compile_for_day]
00032 #define sun_ambient_color   config.sun_ambient_color[config.compile_for_day]
00033 
00034 vec3_t face_offset[MAX_MAP_FACES];      
00037 typedef struct {
00038     vec_t   facedist;
00039     vec3_t  facenormal;
00040 
00041     int     numsurfpt;
00042     vec3_t  *surfpt;
00043 
00044     vec3_t  modelorg;       
00046     vec3_t  texorg;
00047     vec3_t  worldtotex[2];  
00048     vec3_t  textoworld[2];  
00050     int     texmins[2], texsize[2];
00051     dBspSurface_t   *face;
00052 } lightinfo_t;
00053 
00055 typedef struct extents_s {
00056     vec3_t mins, maxs;
00057     vec3_t center;
00058     vec2_t stmins, stmaxs;
00059 } extents_t;
00060 
00061 static extents_t face_extents[MAX_MAP_FACES];
00062 
00068 static void BuildFaceExtents (void)
00069 {
00070     int k;
00071 
00072     for (k = 0; k < curTile->numfaces; k++) {
00073         const dBspSurface_t *s = &curTile->faces[k];
00074         const dBspTexinfo_t *tex = &curTile->texinfo[s->texinfo];
00075 
00076         float *mins = face_extents[s - curTile->faces].mins;
00077         float *maxs = face_extents[s - curTile->faces].maxs;
00078 
00079         float *center = face_extents[s - curTile->faces].center;
00080 
00081         float *stmins = face_extents[s - curTile->faces].stmins;
00082         float *stmaxs = face_extents[s - curTile->faces].stmaxs;
00083         int i;
00084 
00085         VectorSet(mins, 999999, 999999, 999999);
00086         VectorSet(maxs, -999999, -999999, -999999);
00087 
00088         stmins[0] = stmins[1] = 999999;
00089         stmaxs[0] = stmaxs[1] = -999999;
00090 
00091         for (i = 0; i < s->numedges; i++) {
00092             const int e = curTile->surfedges[s->firstedge + i];
00093             const dBspVertex_t *v;
00094             int j;
00095 
00096             if (e >= 0)
00097                 v = curTile->vertexes + curTile->edges[e].v[0];
00098             else
00099                 v = curTile->vertexes + curTile->edges[-e].v[1];
00100 
00101             for (j = 0; j < 3; j++) {  /* calculate mins, maxs */
00102                 if (v->point[j] > maxs[j])
00103                     maxs[j] = v->point[j];
00104                 if (v->point[j] < mins[j])
00105                     mins[j] = v->point[j];
00106             }
00107 
00108             for (j = 0; j < 2; j++) {  /* calculate stmins, stmaxs */
00109                 const float val = DotProduct(v->point, tex->vecs[j]) + tex->vecs[j][3];
00110                 if (val < stmins[j])
00111                     stmins[j] = val;
00112                 if (val > stmaxs[j])
00113                     stmaxs[j] = val;
00114             }
00115         }
00116 
00117         for (i = 0; i < 3; i++)
00118             center[i] = (mins[i] + maxs[i]) / 2.0f;
00119     }
00120 }
00121 
00125 static void CalcLightinfoExtents (lightinfo_t *l)
00126 {
00127     const dBspSurface_t *s;
00128     float *stmins, *stmaxs;
00129     vec2_t lm_mins, lm_maxs;
00130     int i;
00131 
00132     s = l->face;
00133 
00134     stmins = face_extents[s - curTile->faces].stmins;
00135     stmaxs = face_extents[s - curTile->faces].stmaxs;
00136 
00137     for (i = 0; i < 2; i++) {
00138         lm_mins[i] = floor(stmins[i] / (1 << config.lightquant));
00139         lm_maxs[i] = ceil(stmaxs[i] / (1 << config.lightquant));
00140 
00141         l->texmins[i] = lm_mins[i];
00142         l->texsize[i] = lm_maxs[i] - lm_mins[i];
00143     }
00144 
00145     if (l->texsize[0] * l->texsize[1] > MAX_MAP_LIGHTMAP)
00146         Sys_Error("Surface too large to light (%dx%d)", l->texsize[0], l->texsize[1]);
00147 }
00148 
00152 static void CalcLightinfoVectors (lightinfo_t *l)
00153 {
00154     const dBspTexinfo_t *tex;
00155     int i;
00156     vec3_t texnormal;
00157     vec_t distscale, dist;
00158 
00159     tex = &curTile->texinfo[l->face->texinfo];
00160 
00161     /* convert from float to double */
00162     for (i = 0; i < 2; i++)
00163         VectorCopy(tex->vecs[i], l->worldtotex[i]);
00164 
00165     /* calculate a normal to the texture axis.  points can be moved along this
00166      * without changing their S/T */
00167     texnormal[0] = tex->vecs[1][1] * tex->vecs[0][2]
00168                     - tex->vecs[1][2] * tex->vecs[0][1];
00169     texnormal[1] = tex->vecs[1][2] * tex->vecs[0][0]
00170                     - tex->vecs[1][0] * tex->vecs[0][2];
00171     texnormal[2] = tex->vecs[1][0] * tex->vecs[0][1]
00172                     - tex->vecs[1][1] * tex->vecs[0][0];
00173     VectorNormalize(texnormal);
00174 
00175     /* flip it towards plane normal */
00176     distscale = DotProduct(texnormal, l->facenormal);
00177     if (!distscale) {
00178         Verb_Printf(VERB_EXTRA, "WARNING: Texture axis perpendicular to face\n");
00179         distscale = 1.0;
00180     }
00181     if (distscale < 0.0) {
00182         distscale = -distscale;
00183         VectorSubtract(vec3_origin, texnormal, texnormal);
00184     }
00185 
00186     /* distscale is the ratio of the distance along the texture normal to
00187      * the distance along the plane normal */
00188     distscale = 1.0 / distscale;
00189 
00190     for (i = 0; i < 2; i++) {
00191         const vec_t len = VectorLength(l->worldtotex[i]);
00192         const vec_t distance = DotProduct(l->worldtotex[i], l->facenormal) * distscale;
00193         VectorMA(l->worldtotex[i], -distance, texnormal, l->textoworld[i]);
00194         VectorScale(l->textoworld[i], (1.0f / len) * (1.0f / len), l->textoworld[i]);
00195     }
00196 
00197     /* calculate texorg on the texture plane */
00198     for (i = 0; i < 3; i++)
00199         l->texorg[i] =
00200             -tex->vecs[0][3] * l->textoworld[0][i] -
00201             tex->vecs[1][3] * l->textoworld[1][i];
00202 
00203     /* project back to the face plane */
00204     dist = DotProduct(l->texorg, l->facenormal) - l->facedist - 1;
00205     dist *= distscale;
00206     VectorMA(l->texorg, -dist, texnormal, l->texorg);
00207 
00208     /* compensate for org'd bmodels */
00209     VectorAdd(l->texorg, l->modelorg, l->texorg);
00210 
00211     /* total sample count */
00212     l->numsurfpt = (l->texsize[0] + 1) * (l->texsize[1] + 1);
00213     l->surfpt = Mem_Alloc(l->numsurfpt * sizeof(vec3_t));
00214     if (!l->surfpt)
00215         Sys_Error("Surface too large to light ("UFO_SIZE_T")", l->numsurfpt * sizeof(*l->surfpt));
00216 }
00217 
00222 static void CalcPoints (lightinfo_t *l, float sofs, float tofs)
00223 {
00224     int s, t, j;
00225     int w, h, step;
00226     vec_t starts, startt;
00227     vec_t *surf;
00228 
00229     /* fill in surforg
00230      * the points are biased towards the center of the surfaces
00231      * to help avoid edge cases just inside walls */
00232     surf = l->surfpt[0];
00233 
00234     h = l->texsize[1] + 1;
00235     w = l->texsize[0] + 1;
00236 
00237     step = 1 << config.lightquant;
00238     starts = l->texmins[0] * step;
00239     startt = l->texmins[1] * step;
00240 
00241     for (t = 0; t < h; t++) {
00242         for (s = 0; s < w; s++, surf += 3) {
00243             const vec_t us = starts + (s + sofs) * step;
00244             const vec_t ut = startt + (t + tofs) * step;
00245 
00246             /* calculate texture point */
00247             for (j = 0; j < 3; j++)
00248                 surf[j] = l->texorg[j] + l->textoworld[0][j] * us +
00249                         l->textoworld[1][j] * ut;
00250         }
00251     }
00252 }
00253 
00255 typedef struct facelight_s {
00256     int numsamples;
00257     float *origins;
00258     float *samples;     
00259     float *directions;  
00260 } facelight_t;
00261 
00262 static facelight_t facelight[LIGHTMAP_MAX][MAX_MAP_FACES];
00263 
00265 typedef enum {
00266     emit_surface,       
00267     emit_point,         
00268     emit_spotlight      
00269 } emittype_t;
00270 
00272 typedef struct light_s {
00273     struct light_s *next;   
00274     emittype_t  type;       
00276     float       intensity;  
00277     vec3_t      origin;     
00278     vec3_t      color;      
00279     vec3_t      normal;     
00280     float       stopdot;    
00281 } light_t;
00282 
00283 static light_t *lights[LIGHTMAP_MAX];
00284 static int numlights[LIGHTMAP_MAX];
00285 
00290 void BuildLights (void)
00291 {
00292     int i;
00293     light_t *l;
00294 
00295     /* surfaces */
00296     for (i = 0; i < MAX_MAP_FACES; i++) {
00297         const patch_t *p = face_patches[i];
00298         while (p) {  /* iterate subdivided patches */
00299             if (VectorCompare(p->light, vec3_origin))
00300                 continue;
00301 
00302             numlights[config.compile_for_day]++;
00303             l = Mem_Alloc(sizeof(*l));
00304 
00305             VectorCopy(p->origin, l->origin);
00306 
00307             l->next = lights[config.compile_for_day];
00308             lights[config.compile_for_day] = l;
00309 
00310             l->type = emit_surface;
00311 
00312             l->intensity = ColorNormalize(p->light, l->color);
00313             l->intensity *= p->area * config.surface_scale;
00314 
00315             p = p->next;
00316         }
00317     }
00318 
00319     /* entities (skip the world) */
00320     for (i = 1; i < num_entities; i++) {
00321         float intensity;
00322         const char *color;
00323         const char *target;
00324         const entity_t *e = &entities[i];
00325         const char *name = ValueForKey(e, "classname");
00326         if (strncmp(name, "light", 5))
00327             continue;
00328 
00329         /* remove those lights that are only for the night version */
00330         if (config.compile_for_day) {
00331             const int spawnflags = atoi(ValueForKey(e, "spawnflags"));
00332             if (!(spawnflags & 1))  /* day */
00333                 continue;
00334         }
00335 
00336         numlights[config.compile_for_day]++;
00337         l = Mem_Alloc(sizeof(*l));
00338 
00339         GetVectorForKey(e, "origin", l->origin);
00340 
00341         /* link in */
00342         l->next = lights[config.compile_for_day];
00343         lights[config.compile_for_day] = l;
00344 
00345         intensity = FloatForKey(e, "light");
00346         if (!intensity)
00347             intensity = 300.0;
00348         color = ValueForKey(e, "_color");
00349         if (color && color[0] != '\0'){
00350             sscanf(color, "%f %f %f", &l->color[0], &l->color[1], &l->color[2]);
00351             ColorNormalize(l->color, l->color);
00352         } else
00353             l->color[0] = l->color[1] = l->color[2] = 1.0;
00354         l->intensity = intensity * config.entity_scale;
00355         l->type = emit_point;
00356 
00357         target = ValueForKey(e, "target");
00358         if (target[0] != '\0' || !strcmp(name, "light_spot")) {
00359             l->type = emit_spotlight;
00360             l->stopdot = FloatForKey(e, "_cone");
00361             if (!l->stopdot)
00362                 l->stopdot = 10;
00363             l->stopdot = cos(l->stopdot * torad);
00364             if (target[0] != '\0') {    /* point towards target */
00365                 entity_t *e2 = FindTargetEntity(target);
00366                 if (!e2)
00367                     Com_Printf("WARNING: light at (%i %i %i) has missing target '%s' - e.g. create an info_null that has a 'targetname' set to '%s'\n",
00368                         (int)l->origin[0], (int)l->origin[1], (int)l->origin[2], target, target);
00369                 else {
00370                     vec3_t dest;
00371                     GetVectorForKey(e2, "origin", dest);
00372                     VectorSubtract(dest, l->origin, l->normal);
00373                     VectorNormalize(l->normal);
00374                 }
00375             } else {    /* point down angle */
00376                 const float angle = FloatForKey(e, "angle");
00377                 if (angle == ANGLE_UP) {
00378                     l->normal[0] = l->normal[1] = 0.0;
00379                     l->normal[2] = 1.0;
00380                 } else if (angle == ANGLE_DOWN) {
00381                     l->normal[0] = l->normal[1] = 0.0;
00382                     l->normal[2] = -1.0;
00383                 } else {
00384                     l->normal[2] = 0;
00385                     l->normal[0] = cos(angle * torad);
00386                     l->normal[1] = sin(angle * torad);
00387                 }
00388             }
00389         }
00390     }
00391 
00392     /* handle worldspawn light settings */
00393     {
00394         const entity_t *e = &entities[0];
00395         const char *ambient, *light, *angles, *color;
00396         float f;
00397         int i;
00398 
00399         if (config.compile_for_day) {
00400             ambient = ValueForKey(e, "ambient_day");
00401             light = ValueForKey(e, "light_day");
00402             angles = ValueForKey(e, "angles_day");
00403             color = ValueForKey(e, "color_day");
00404         } else {
00405             ambient = ValueForKey(e, "ambient_night");
00406             light = ValueForKey(e, "light_night");
00407             angles = ValueForKey(e, "angles_night");
00408             color = ValueForKey(e, "color_night");
00409         }
00410 
00411         if (light[0] != '\0')
00412             sun_intensity = atoi(light);
00413 
00414         if (angles[0] != '\0') {
00415             VectorClear(sun_angles);
00416             sscanf(angles, "%f %f", &sun_angles[0], &sun_angles[1]);
00417             AngleVectors(sun_angles, sun_normal, NULL, NULL);
00418         }
00419 
00420         if (color[0] != '\0') {
00421             GetVectorFromString(color, sun_color);
00422             ColorNormalize(sun_color, sun_color);
00423         }
00424 
00425         if (ambient[0] != '\0')
00426             GetVectorFromString(ambient, sun_ambient_color);
00427 
00428         /* optionally pull brightness from worldspawn */
00429         f = FloatForKey(e, "brightness");
00430         if (f > 0.0)
00431             config.brightness = f;
00432 
00433         /* saturation as well */
00434         f = FloatForKey(e, "saturation");
00435         if (f > 0.0)
00436             config.saturation = f;
00437         else
00438             Verb_Printf(VERB_EXTRA, "Invalid saturation setting (%f) in worldspawn found\n", f);
00439 
00440         f = FloatForKey(e, "contrast");
00441         if (f > 0.0)
00442             config.contrast = f;
00443         else
00444             Verb_Printf(VERB_EXTRA, "Invalid contrast setting (%f) in worldspawn found\n", f);
00445 
00446         /* lightmap resolution downscale (e.g. 4 = 1 << 4) */
00447         i = atoi(ValueForKey(e, "quant"));
00448         if (i >= 1 && i <= 6)
00449             config.lightquant = i;
00450         else
00451             Verb_Printf(VERB_EXTRA, "Invalid quant setting (%i) in worldspawn found\n", i);
00452     }
00453 
00454     Verb_Printf(VERB_EXTRA, "light settings:\n * intensity: %i\n * sun_angles: pitch %f yaw %f\n * sun_color: %f:%f:%f\n * sun_ambient_color: %f:%f:%f\n",
00455         sun_intensity, sun_angles[0], sun_angles[1], sun_color[0], sun_color[1], sun_color[2], sun_ambient_color[0], sun_ambient_color[1], sun_ambient_color[2]);
00456     Verb_Printf(VERB_NORMAL, "%i direct lights for %s lightmap\n", numlights[config.compile_for_day], (config.compile_for_day ? "day" : "night"));
00457 }
00458 
00467 static qboolean TR_TestLineSingleTile (const vec3_t start, const vec3_t stop, int *headhint)
00468 {
00469     int i;
00470     static int shared_lastthead = 0;
00471     int lastthead = *headhint;
00472 
00473     if (!lastthead) {
00474         lastthead = shared_lastthead;
00475         *headhint = lastthead;
00476     }
00477 
00478     assert(mapTiles.numTiles == 1);
00479 
00480     /* ufo2map does many traces to the same endpoint.
00481      * Often an occluding node will be found in the same thead
00482      * as the last trace, so test that one first. */
00483     if (curTile->theadlevel[lastthead] <= LEVEL_LASTLIGHTBLOCKING
00484         && TR_TestLine_r(curTile, curTile->thead[lastthead], start, stop))
00485         return qtrue;
00486 
00487     for (i = 0; i < curTile->numtheads; i++) {
00488         const int level = curTile->theadlevel[i];
00489         if (i == lastthead)
00490             continue;
00491         if (level > LEVEL_LASTLIGHTBLOCKING)
00492             continue;
00493         if (TR_TestLine_r(curTile, curTile->thead[i], start, stop)) {
00494             shared_lastthead = *headhint = i;
00495             return qtrue;
00496         }
00497     }
00498     return qfalse;
00499 }
00500 
00504 static void GatherSampleSunlight (const vec3_t pos, const vec3_t normal, float *sample, float *direction, float scale, int *headhint)
00505 {
00506     vec3_t delta;
00507     float dot, light;
00508 
00509     if (!sun_intensity)
00510         return;
00511 
00512     dot = DotProduct(sun_normal, normal);
00513     if (dot <= 0.001)
00514         return; /* wrong direction */
00515 
00516     /* don't use only 512 (which would be the 8 level max unit) but a
00517      * higher value - because the light angle is not fixed at 90 degree */
00518     VectorMA(pos, 8192, sun_normal, delta);
00519 
00520     if (TR_TestLineSingleTile(pos, delta, headhint))
00521         return; /* occluded */
00522 
00523     light = sun_intensity * dot * scale;
00524 
00525     /* add some light to it */
00526     VectorMA(sample, light, sun_color, sample);
00527 
00528     /* and accumulate the direction */
00529     VectorMix(normal, sun_normal, light / sun_intensity, delta);
00530     VectorMA(direction, light * scale, delta, direction);
00531 }
00532 
00533 
00541 static void GatherSampleLight (vec3_t pos, const vec3_t normal, float *sample, float *direction, float scale, int *headhints)
00542 {
00543     light_t *l;
00544     vec3_t delta;
00545     float dot, dot2;
00546     float dist;
00547     int *headhint;
00548 
00549     for (l = lights[config.compile_for_day], headhint = headhints; l; l = l->next, headhint++) {
00550         float light = 0.0;
00551 
00552         /* Com_Printf("Looking with next hint.\n"); */
00553 
00554         VectorSubtract(l->origin, pos, delta);
00555         dist = VectorNormalize(delta);
00556 
00557         dot = DotProduct(delta, normal);
00558         if (dot <= 0.001)
00559             continue;   /* behind sample surface */
00560 
00561         switch (l->type) {
00562         case emit_point:
00563             /* linear falloff */
00564             light = (l->intensity - dist) * dot;
00565             break;
00566 
00567         case emit_surface:
00568             /* exponential falloff */
00569             light = (l->intensity / (dist * dist)) * dot;
00570             break;
00571 
00572         case emit_spotlight:
00573             /* linear falloff with cone */
00574             dot2 = -DotProduct(delta, l->normal);
00575             if (dot2 > l->stopdot) {
00576                 /* inside the cone */
00577                 light = (l->intensity - dist) * dot;
00578             } else {
00579                 /* outside the cone */
00580                 light = (l->intensity * 0.5 - dist) * dot;
00581             }
00582             break;
00583         default:
00584             Sys_Error("Bad l->type");
00585         }
00586 
00587         if (light <= 0.5)  /* almost no light */
00588             continue;
00589 
00590         if (TR_TestLineSingleTile(pos, l->origin, headhint))
00591             continue;   /* occluded */
00592 
00593         /* add some light to it */
00594         VectorMA(sample, light * scale, l->color, sample);
00595 
00596         /* and add some direction */
00597         VectorMix(normal, delta, 2.0 * light / l->intensity, delta);
00598         VectorMA(direction, light * scale, delta, direction);
00599     }
00600 
00601     /* Com_Printf("Looking with last hint.\n"); */
00602     GatherSampleSunlight(pos, normal, sample, direction, scale, headhint);
00603 }
00604 
00605 #define SAMPLE_NUDGE 0.25
00606 
00611 static inline void NudgeSamplePosition (const vec3_t in, const vec3_t normal, const vec3_t center, vec3_t out)
00612 {
00613     vec3_t dir;
00614 
00615     VectorCopy(in, out);
00616 
00617     /* move into the level using the normal and surface center */
00618     VectorSubtract(out, center, dir);
00619     VectorNormalize(dir);
00620 
00621     VectorMA(out, SAMPLE_NUDGE, dir, out);
00622     VectorMA(out, SAMPLE_NUDGE, normal, out);
00623 }
00624 
00625 #define MAX_VERT_FACES 256
00626 
00633 static void FacesWithVert (int vert, int *faces, int *nfaces)
00634 {
00635     int i, j, k;
00636 
00637     k = 0;
00638     for (i = 0; i < curTile->numfaces; i++) {
00639         const dBspSurface_t *face = &curTile->faces[i];
00640 
00641         if (!(curTile->texinfo[face->texinfo].surfaceFlags & SURF_PHONG))
00642             continue;
00643 
00644         for (j = 0; j < face->numedges; j++) {
00645             const int e = curTile->surfedges[face->firstedge + j];
00646             const int v = e >= 0 ? curTile->edges[e].v[0] : curTile->edges[-e].v[1];
00647 
00648             /* face references vert */
00649             if (v == vert) {
00650                 faces[k++] = i;
00651                 if (k == MAX_VERT_FACES)
00652                     Sys_Error("MAX_VERT_FACES");
00653                 break;
00654             }
00655         }
00656     }
00657     *nfaces = k;
00658 }
00659 
00665 void BuildVertexNormals (void)
00666 {
00667     int vert_faces[MAX_VERT_FACES];
00668     int num_vert_faces;
00669     vec3_t norm, delta;
00670     float scale;
00671     int i, j;
00672 
00673     BuildFaceExtents();
00674 
00675     for (i = 0; i < curTile->numvertexes; i++) {
00676         VectorClear(curTile->normals[i].normal);
00677 
00678         FacesWithVert(i, vert_faces, &num_vert_faces);
00679         if (!num_vert_faces)  /* rely on plane normal only */
00680             continue;
00681 
00682         for (j = 0; j < num_vert_faces; j++) {
00683             const dBspSurface_t *face = &curTile->faces[vert_faces[j]];
00684             const dBspPlane_t *plane = &curTile->planes[face->planenum];
00685 
00686             /* scale the contribution of each face based on size */
00687             VectorSubtract(face_extents[vert_faces[j]].maxs, face_extents[vert_faces[j]].mins, delta);
00688             scale = VectorLength(delta);
00689 
00690             if (face->side)
00691                 VectorScale(plane->normal, -scale, norm);
00692             else
00693                 VectorScale(plane->normal, scale, norm);
00694 
00695             VectorAdd(curTile->normals[i].normal, norm, curTile->normals[i].normal);
00696         }
00697         VectorNormalize(curTile->normals[i].normal);
00698     }
00699 }
00700 
00701 
00706 static void SampleNormal (const lightinfo_t *l, const vec3_t pos, vec3_t normal)
00707 {
00708     vec3_t temp;
00709     float dist[MAX_VERT_FACES];
00710     float nearest;
00711     int i, v, nearv;
00712 
00713     nearest = 9999.0;
00714     nearv = 0;
00715 
00716     /* calculate the distance to each vertex */
00717     for (i = 0; i < l->face->numedges; i++) {  /* find nearest and farthest verts */
00718         const int e = curTile->surfedges[l->face->firstedge + i];
00719         if (e >= 0)
00720             v = curTile->edges[e].v[0];
00721         else
00722             v = curTile->edges[-e].v[1];
00723 
00724         VectorSubtract(pos, curTile->vertexes[v].point, temp);
00725         dist[i] = VectorLength(temp);
00726         if (dist[i] < nearest) {
00727             nearest = dist[i];
00728             nearv = v;
00729         }
00730     }
00731     VectorCopy(curTile->normals[nearv].normal, normal);
00732 }
00733 
00734 
00735 #define MAX_SAMPLES 5
00736 static const float sampleofs[MAX_SAMPLES][2] = {
00737     {0.0, 0.0}, {-0.125, -0.125}, {0.125, -0.125}, {0.125, 0.125}, {-0.125, 0.125}
00738 };
00739 
00744 void BuildFacelights (unsigned int facenum)
00745 {
00746     dBspSurface_t *face;
00747     dBspPlane_t *plane;
00748     dBspTexinfo_t *tex;
00749     float *center;
00750     float *sdir, *tdir;
00751     vec3_t normal, binormal;
00752     vec4_t tangent;
00753     lightinfo_t l[MAX_SAMPLES];
00754     float scale;
00755     int i, j, numsamples;
00756     facelight_t *fl;
00757     int *headhints;
00758 
00759     if (facenum >= MAX_MAP_FACES) {
00760         Com_Printf("MAX_MAP_FACES hit\n");
00761         return;
00762     }
00763 
00764     face = &curTile->faces[facenum];
00765     plane = &curTile->planes[face->planenum];
00766     tex = &curTile->texinfo[face->texinfo];
00767 
00768     if (tex->surfaceFlags & SURF_WARP)
00769         return;     /* non-lit texture */
00770 
00771     sdir = tex->vecs[0];
00772     tdir = tex->vecs[1];
00773 
00774     /* lighting -extra antialiasing */
00775     if (config.extrasamples)
00776         numsamples = MAX_SAMPLES;
00777     else
00778         numsamples = 1;
00779 
00780     memset(l, 0, sizeof(l));
00781 
00782     scale = 1.0 / numsamples; /* each sample contributes this much */
00783 
00784     for (i = 0; i < numsamples; i++) {
00785         l[i].face = face;
00786         l[i].facedist = plane->dist;
00787         VectorCopy(plane->normal, l[i].facenormal);
00788         /* negate the normal and dist */
00789         if (face->side) {
00790             VectorNegate(l[i].facenormal, l[i].facenormal);
00791             l[i].facedist = -l[i].facedist;
00792         }
00793 
00794         /* get the origin offset for rotating bmodels */
00795         VectorCopy(face_offset[facenum], l[i].modelorg);
00796 
00797         /* calculate lightmap texture mins and maxs */
00798         CalcLightinfoExtents(&l[i]);
00799 
00800         /* and the lightmap texture vectors */
00801         CalcLightinfoVectors(&l[i]);
00802 
00803         /* now generate all of the sample points */
00804         CalcPoints(&l[i], sampleofs[i][0], sampleofs[i][1]);
00805     }
00806 
00807     fl = &facelight[config.compile_for_day][facenum];
00808     fl->numsamples = l[0].numsurfpt;
00809 
00810     fl->origins = Mem_Alloc(fl->numsamples * sizeof(vec3_t));
00811     memcpy(fl->origins, l[0].surfpt, fl->numsamples * sizeof(vec3_t));
00812     fl->samples = Mem_Alloc(fl->numsamples * sizeof(vec3_t));
00813     fl->directions = Mem_Alloc(fl->numsamples * sizeof(vec3_t));
00814 
00815     center = face_extents[facenum].center;  /* center of the face */
00816 
00817     /* Also setup the hints.  Each hint is specific to each light source, including sunlight. */
00818     headhints = Mem_Alloc((numlights[config.compile_for_day] + 1) * sizeof(int));
00819 
00820     /* calculate light for each sample */
00821     for (i = 0; i < fl->numsamples; i++) {
00822         float *sample = fl->samples + i * 3;            /* accumulate lighting here */
00823         float *direction = fl->directions + i * 3;      /* accumulate direction here */
00824 
00825         for (j = 0; j < numsamples; j++) {  /* with antialiasing */
00826             vec3_t pos;
00827 
00828             if (tex->surfaceFlags & SURF_PHONG)
00829                 /* interpolated normal */
00830                 SampleNormal(&l[0], l[j].surfpt[i], normal);
00831             else
00832                 /* or just plane normal */
00833                 VectorCopy(l[0].facenormal, normal);
00834 
00835             NudgeSamplePosition(l[j].surfpt[i], normal, center, pos);
00836 
00837             GatherSampleLight(pos, normal, sample, direction, scale, headhints);
00838         }
00839         if (!VectorCompare(direction, vec3_origin)) {
00840             vec3_t dir;
00841 
00842             /* normalize it */
00843             VectorNormalize(direction);
00844 
00845             /* finalize the lighting direction for the sample */
00846             TangentVectors(normal, sdir, tdir, tangent, binormal);
00847 
00848             dir[0] = DotProduct(direction, tangent);
00849             dir[1] = DotProduct(direction, binormal);
00850             dir[2] = DotProduct(direction, normal);
00851 
00852             VectorCopy(dir, direction);
00853         }
00854     }
00855 
00856     /* Free the hints. */
00857     Mem_Free(headhints);
00858 
00859     for (i = 0; i < l[0].numsurfpt; i++) {  /* pad them */
00860         float *direction = fl->directions + i * 3;
00861         if (VectorCompare(direction, vec3_origin))
00862             VectorSet(direction, 0.0, 0.0, 1.0);
00863     }
00864 
00865     /* free the sample positions for the face */
00866     for (i = 0; i < numsamples; i++)
00867         Mem_Free(l[i].surfpt);
00868 }
00869 
00870 static void WriteTGA24 (const char *filename, const byte * data, int width, int height, int offset)
00871 {
00872     int i, c;
00873     byte *buffer;
00874     qFILE file;
00875 
00876     /* allocate a buffer and set it up */
00877     buffer = Mem_Alloc(width * height * 3 + 18);
00878     memset(buffer, 0, 18);
00879     buffer[2] = 2;
00880     buffer[12] = width & 255;
00881     buffer[13] = width >> 8;
00882     buffer[14] = height & 255;
00883     buffer[15] = height >> 8;
00884     buffer[16] = 24;
00885 
00886     /* swap rgb to bgr */
00887     c = (width * height * 3) + 18;
00888     for (i = 18; i < c; i += 6) {
00889         buffer[i] = data[i - 18 + offset + 2];  /* blue */
00890         buffer[i + 1] = data[i - 18 + offset + 1];  /* green */
00891         buffer[i + 2] = data[i - 18 + offset + 0];  /* red */
00892     }
00893 
00894     /* write it and free the buffer */
00895     if (FS_OpenFile(filename, &file, FILE_WRITE) > 0)
00896         Sys_Error("Unable to open %s for writing", filename);
00897 
00898     FS_Write(buffer, c, &file);
00899 
00900     /* close the file */
00901     FS_CloseFile(&file);
00902     Mem_Free(buffer);
00903 }
00904 
00912 static void CalcTextureSize (const dBspSurface_t *s, vec2_t texsize, int scale)
00913 {
00914     const float *stmins = face_extents[s - curTile->faces].stmins;
00915     const float *stmaxs = face_extents[s - curTile->faces].stmaxs;
00916     int i;
00917 
00918     for (i = 0; i < 2; i++) {
00919         const float mins = floor(stmins[i] / scale);
00920         const float maxs = ceil(stmaxs[i] / scale);
00921 
00922         texsize[i] = maxs - mins;
00923     }
00924 }
00925 
00932 static void ExportLightmap (const char *path, const char *name, qboolean day)
00933 {
00934     int i;
00935     const int lightmapIndex = day ? 1 : 0;
00936     const byte *bspLightBytes = curTile->lightdata[lightmapIndex];
00937     const byte quant = *bspLightBytes;
00938     const int scale = 1 << quant;
00939 
00940     for (i = 0; i < curTile->numfaces; i++) {
00941         const dBspSurface_t *face = &curTile->faces[i];
00942         const byte *lightmap = bspLightBytes + face->lightofs[lightmapIndex];
00943         vec2_t texSize;
00944 
00945         CalcTextureSize(face, texSize, scale);
00946 
00947         /* write a tga image out */
00948         if (texSize[0] && texSize[1]) {
00949             char filename[MAX_QPATH];
00950             Com_sprintf(filename, sizeof(filename), "%s/%s_lightmap_%04d%c.tga", path, name, i, day ? 'd' : 'n');
00951             Com_Printf("Writing %s (%.0fx%.0f)\n", filename, texSize[0], texSize[1]);
00952             WriteTGA24(filename, lightmap, texSize[0], texSize[1], 0);
00953             Com_sprintf(filename, sizeof(filename), "%s/%s_direction_%04d%c.tga", path, name, i, day ? 'd' : 'n');
00954             Com_Printf("Writing %s (%.0fx%.0f)\n", filename, texSize[0], texSize[1]);
00955             WriteTGA24(filename, lightmap, texSize[0], texSize[1], 3);
00956         }
00957     }
00958 }
00959 
00965 void ExportLightmaps (const char *bspFileName)
00966 {
00967     char path[MAX_QPATH], lightmapName[MAX_QPATH];
00968     const char *fileName = Com_SkipPath(bspFileName);
00969 
00970     Com_FilePath(bspFileName, path);
00971     Com_StripExtension(fileName, lightmapName, sizeof(lightmapName));
00972 
00973     /* note it */
00974     Com_Printf("--- ExportLightmaps ---\n");
00975 
00976     BuildFaceExtents();
00977 
00978     ExportLightmap(path, lightmapName, qtrue);
00979     ExportLightmap(path, lightmapName, qfalse);
00980 }
00981 
00982 static const vec3_t luminosity = {0.2125, 0.7154, 0.0721};
00983 
00989 void FinalLightFace (unsigned int facenum)
00990 {
00991     dBspSurface_t *f;
00992     int j, k;
00993     vec3_t dir, intensity;
00994     facelight_t *fl;
00995     float max, d;
00996     byte *dest;
00997 
00998     f = &curTile->faces[facenum];
00999     fl = &facelight[config.compile_for_day][facenum];
01000 
01001     /* none-lit texture */
01002     if (curTile->texinfo[f->texinfo].surfaceFlags & SURF_WARP)
01003         return;
01004 
01005     ThreadLock();
01006 
01007     f->lightofs[config.compile_for_day] = curTile->lightdatasize[config.compile_for_day];
01008     curTile->lightdatasize[config.compile_for_day] += fl->numsamples * 3;
01009     /* account for light direction data as well */
01010     curTile->lightdatasize[config.compile_for_day] += fl->numsamples * 3;
01011 
01012     if (curTile->lightdatasize[config.compile_for_day] > MAX_MAP_LIGHTING)
01013         Sys_Error("MAX_MAP_LIGHTING (%i exceeded %i) - try to reduce the brush size (%s)",
01014             curTile->lightdatasize[config.compile_for_day], MAX_MAP_LIGHTING,
01015             curTile->texinfo[f->texinfo].texture);
01016 
01017     ThreadUnlock();
01018 
01019     /* write it out */
01020     dest = &curTile->lightdata[config.compile_for_day][f->lightofs[config.compile_for_day]];
01021 
01022     for (j = 0; j < fl->numsamples; j++) {
01023         vec3_t temp;
01024 
01025         /* start with raw sample data */
01026         VectorCopy((fl->samples + j * 3), temp);
01027 
01028         /* convert to float */
01029         VectorScale(temp, 1.0 / 255.0, temp);
01030 
01031         /* add an ambient term if desired */
01032         VectorAdd(temp, sun_ambient_color, temp);
01033 
01034         /* apply global scale factor */
01035         VectorScale(temp, config.brightness, temp);
01036 
01037         max = 0.0;
01038 
01039         /* find the brightest component */
01040         for (k = 0; k < 3; k++) {
01041             /* enforcing positive values */
01042             if (temp[k] < 0.0)
01043                 temp[k] = 0.0;
01044 
01045             if (temp[k] > max)
01046                 max = temp[k];
01047         }
01048 
01049         if (max > 255.0)  /* clamp without changing hue */
01050             VectorScale(temp, 255.0 / max, temp);
01051 
01052         for (k = 0; k < 3; k++) {  /* apply contrast */
01053             temp[k] -= 0.5;  /* normalize to -0.5 through 0.5 */
01054 
01055             temp[k] *= config.contrast;  /* scale */
01056 
01057             temp[k] += 0.5;
01058 
01059             if (temp[k] > 1.0)  /* clamp */
01060                 temp[k] = 1.0;
01061             else if (temp[k] < 0)
01062                 temp[k] = 0;
01063         }
01064 
01065         /* apply saturation */
01066         d = DotProduct(temp, luminosity);
01067 
01068         VectorSet(intensity, d, d, d);
01069         VectorMix(intensity, temp, config.saturation, temp);
01070 
01071         for (k = 0; k < 3; k++) {
01072             temp[k] *= 255.0;  /* back to byte */
01073 
01074             if (temp[k] > 255.0)  /* clamp */
01075                 temp[k] = 255.0;
01076             else if (temp[k] < 0.0)
01077                 temp[k] = 0.0;
01078 
01079             *dest++ = (byte)temp[k];
01080         }
01081 
01082         /* also write the directional data */
01083         VectorCopy((fl->directions + j * 3), dir);
01084         for (k = 0; k < 3; k++)
01085             *dest++ = (byte)((dir[k] + 1.0f) * 127.0f);
01086     }
01087 }

Generated by  doxygen 1.6.2