#include "qrad.h" edgeshare_t g_edgeshare[MAX_MAP_EDGES]; vec3_t g_face_centroids[MAX_MAP_EDGES]; bool g_sky_lighting_fix = DEFAULT_SKY_LIGHTING_FIX; bool g_light_remove = DEFAULT_OBS_LIGHT_REMOVAL; extern bool g_warned_direct; #define TEXTURE_STEP 16.0 // ===================================================================================== // PairEdges // ===================================================================================== void PairEdges() { int i, j, k; dface_t* f; edgeshare_t* e; memset(&g_edgeshare, 0, sizeof(g_edgeshare)); f = g_dfaces; for (i = 0; i < g_numfaces; i++, f++) { for (j = 0; j < f->numedges; j++) { k = g_dsurfedges[f->firstedge + j]; if (k < 0) { e = &g_edgeshare[-k]; hlassert(e->faces[1] == NULL); e->faces[1] = f; } else { e = &g_edgeshare[k]; hlassert(e->faces[0] == NULL); e->faces[0] = f; } if (e->faces[0] && e->faces[1]) { // determine if coplanar if (e->faces[0]->planenum == e->faces[1]->planenum) { e->coplanar = true; } else { // see if they fall into a "smoothing group" based on angle of the normals vec3_t normals[2]; VectorCopy(getPlaneFromFace(e->faces[0])->normal, normals[0]); VectorCopy(getPlaneFromFace(e->faces[1])->normal, normals[1]); e->cos_normals_angle = DotProduct(normals[0], normals[1]); if (e->cos_normals_angle > (1.0 - NORMAL_EPSILON)) { e->coplanar = true; } else if (g_smoothing_threshold > 0.0) { if (e->cos_normals_angle >= g_smoothing_threshold) { VectorAdd(normals[0], normals[1], e->interface_normal); VectorNormalize(e->interface_normal); } } } } } } } #define MAX_SINGLEMAP (18*18*4) typedef struct { vec_t* light; vec_t facedist; vec3_t facenormal; int numsurfpt; vec3_t surfpt[MAX_SINGLEMAP]; vec3_t texorg; vec3_t worldtotex[2]; // s = (world - texorg) . worldtotex[0] vec3_t textoworld[2]; // world = texorg + s * textoworld[0] vec_t exactmins[2], exactmaxs[2]; int texmins[2], texsize[2]; int lightstyles[256]; int surfnum; dface_t* face; } lightinfo_t; // ===================================================================================== // TextureNameFromFace // ===================================================================================== static const char* TextureNameFromFace(const dface_t* const f) { texinfo_t* tx; miptex_t* mt; int ofs; // // check for light emited by texture // tx = &g_texinfo[f->texinfo]; ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[tx->miptex]; mt = (miptex_t*)((byte*) g_dtexdata + ofs); return mt->name; } // ===================================================================================== // CalcFaceExtents // Fills in s->texmins[] and s->texsize[] // also sets exactmins[] and exactmaxs[] // ===================================================================================== static void CalcFaceExtents(lightinfo_t* l) { const int facenum = l->surfnum; dface_t* s; vec_t mins[2], maxs[2], val; int i, j, e; dvertex_t* v; texinfo_t* tex; s = l->face; mins[0] = mins[1] = 999999; maxs[0] = maxs[1] = -99999; tex = &g_texinfo[s->texinfo]; for (i = 0; i < s->numedges; i++) { e = g_dsurfedges[s->firstedge + i]; if (e >= 0) { v = g_dvertexes + g_dedges[e].v[0]; } else { v = g_dvertexes + g_dedges[-e].v[1]; } for (j = 0; j < 2; j++) { val = v->point[0] * tex->vecs[j][0] + v->point[1] * tex->vecs[j][1] + v->point[2] * tex->vecs[j][2] + tex->vecs[j][3]; if (val < mins[j]) { mins[j] = val; } if (val > maxs[j]) { maxs[j] = val; } } } for (i = 0; i < 2; i++) { l->exactmins[i] = mins[i]; l->exactmaxs[i] = maxs[i]; mins[i] = floor(mins[i] / 16.0); maxs[i] = ceil(maxs[i] / 16.0); l->texmins[i] = mins[i]; l->texsize[i] = maxs[i] - mins[i]; } if (!(tex->flags & TEX_SPECIAL)) { if ((l->texsize[0] > 16) || (l->texsize[1] > 16)) { ThreadLock(); PrintOnce("\nfor Face %d (texture %s) at ", s - g_dfaces, TextureNameFromFace(s)); for (i = 0; i < s->numedges; i++) { e = g_dsurfedges[s->firstedge + i]; if (e >= 0) { v = g_dvertexes + g_dedges[e].v[0]; } else { v = g_dvertexes + g_dedges[-e].v[1]; } VectorAdd(v->point, g_face_offset[facenum], v->point); Log("(%4.3f %4.3f %4.3f) ", v->point[0], v->point[1], v->point[2]); } Log("\n"); Error( "Bad surface extents (%d x %d)\nCheck the file ZHLTProblems.html for a detailed explanation of this problem", l->texsize[0], l->texsize[1]); } } } // ===================================================================================== // CalcFaceVectors // Fills in texorg, worldtotex. and textoworld // ===================================================================================== static void CalcFaceVectors(lightinfo_t* l) { texinfo_t* tex; int i, j; vec3_t texnormal; vec_t distscale; vec_t dist, len; tex = &g_texinfo[l->face->texinfo]; // convert from float to double for (i = 0; i < 2; i++) { for (j = 0; j < 3; j++) { l->worldtotex[i][j] = tex->vecs[i][j]; } } // calculate a normal to the texture axis. points can be moved along this // without changing their S/T CrossProduct(tex->vecs[1], tex->vecs[0], texnormal); VectorNormalize(texnormal); // flip it towards plane normal distscale = DotProduct(texnormal, l->facenormal); if (distscale == 0.0) { const unsigned facenum = l->face - g_dfaces; ThreadLock(); Log("Malformed face (%d) normal @ \n", facenum); Winding* w = new Winding(*l->face); { const unsigned numpoints = w->m_NumPoints; unsigned x; for (x=0; xm_Points[x], g_face_offset[facenum], w->m_Points[x]); } } w->Print(); delete w; ThreadUnlock(); hlassume(false, assume_MalformedTextureFace); } if (distscale < 0) { distscale = -distscale; VectorSubtract(vec3_origin, texnormal, texnormal); } // distscale is the ratio of the distance along the texture normal to // the distance along the plane normal distscale = 1.0 / distscale; for (i = 0; i < 2; i++) { len = (float)VectorLength(l->worldtotex[i]); dist = DotProduct(l->worldtotex[i], l->facenormal); dist *= distscale; VectorMA(l->worldtotex[i], -dist, texnormal, l->textoworld[i]); VectorScale(l->textoworld[i], (1 / len) * (1 / len), l->textoworld[i]); } // calculate texorg on the texture plane for (i = 0; i < 3; i++) { l->texorg[i] = -tex->vecs[0][3] * l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i]; } // project back to the face plane dist = DotProduct(l->texorg, l->facenormal) - l->facedist - DEFAULT_HUNT_OFFSET; dist *= distscale; VectorMA(l->texorg, -dist, texnormal, l->texorg); } // ===================================================================================== // SetSurfFromST // ===================================================================================== static void SetSurfFromST(const lightinfo_t* const l, vec_t* surf, const vec_t s, const vec_t t) { const int facenum = l->surfnum; int j; for (j = 0; j < 3; j++) { surf[j] = l->texorg[j] + l->textoworld[0][j] * s + l->textoworld[1][j] * t; } // Adjust for origin-based models VectorAdd(surf, g_face_offset[facenum], surf); } // ===================================================================================== // FindSurfaceMidpoint // ===================================================================================== static dleaf_t* FindSurfaceMidpoint(const lightinfo_t* const l, vec_t* midpoint) { int s, t; int w, h; vec_t starts, startt; vec_t us, ut; vec3_t broken_midpoint; vec3_t surface_midpoint; int inside_point_count; dleaf_t* last_valid_leaf = NULL; dleaf_t* leaf_mid; const int facenum = l->surfnum; const dface_t* f = g_dfaces + facenum; const dplane_t* p = getPlaneFromFace(f); const vec_t* face_delta = g_face_offset[facenum]; h = l->texsize[1] + 1; w = l->texsize[0] + 1; starts = (float)l->texmins[0] * 16; startt = (float)l->texmins[1] * 16; // General case inside_point_count = 0; VectorClear(surface_midpoint); for (t = 0; t < h; t++) { for (s = 0; s < w; s++) { us = starts + s * TEXTURE_STEP; ut = startt + t * TEXTURE_STEP; SetSurfFromST(l, midpoint, us, ut); if ((leaf_mid = PointInLeaf(midpoint)) != g_dleafs) { if ((leaf_mid->contents != CONTENTS_SKY) && (leaf_mid->contents != CONTENTS_SOLID)) { last_valid_leaf = leaf_mid; inside_point_count++; VectorAdd(surface_midpoint, midpoint, surface_midpoint); } } } } if (inside_point_count > 1) { vec_t tmp = 1.0 / inside_point_count; VectorScale(surface_midpoint, tmp, midpoint); //Verbose("Trying general at (%4.3f %4.3f %4.3f) %d\n", surface_midpoint[0], surface_midpoint[1], surface_midpoint[2], inside_point_count); if ( (leaf_mid = HuntForWorld(midpoint, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET))) { //Verbose("general method succeeded at (%4.3f %4.3f %4.3f)\n", midpoint[0], midpoint[1], midpoint[2]); return leaf_mid; } //Verbose("Tried general , failed at (%4.3f %4.3f %4.3f)\n", midpoint[0], midpoint[1], midpoint[2]); } else if (inside_point_count == 1) { //Verbose("Returning single point from general\n"); VectorCopy(surface_midpoint, midpoint); return last_valid_leaf; } else { //Verbose("general failed (no points)\n"); } // Try harder inside_point_count = 0; VectorClear(surface_midpoint); for (t = 0; t < h; t++) { for (s = 0; s < w; s++) { us = starts + s * TEXTURE_STEP; ut = startt + t * TEXTURE_STEP; SetSurfFromST(l, midpoint, us, ut); leaf_mid = HuntForWorld(midpoint, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET); if (leaf_mid != g_dleafs) { last_valid_leaf = leaf_mid; inside_point_count++; VectorAdd(surface_midpoint, midpoint, surface_midpoint); } } } if (inside_point_count > 1) { vec_t tmp = 1.0 / inside_point_count; VectorScale(surface_midpoint, tmp, midpoint); if ( (leaf_mid = HuntForWorld(midpoint, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET))) { //Verbose("best method succeeded at (%4.3f %4.3f %4.3f)\n", midpoint[0], midpoint[1], midpoint[2]); return leaf_mid; } //Verbose("Tried best, failed at (%4.3f %4.3f %4.3f)\n", midpoint[0], midpoint[1], midpoint[2]); } else if (inside_point_count == 1) { //Verbose("Returning single point from best\n"); VectorCopy(surface_midpoint, midpoint); return last_valid_leaf; } else { //Verbose("best failed (no points)\n"); } // Original broken code { vec_t mids = (l->exactmaxs[0] + l->exactmins[0]) / 2; vec_t midt = (l->exactmaxs[1] + l->exactmins[1]) / 2; SetSurfFromST(l, midpoint, mids, midt); if ((leaf_mid = PointInLeaf(midpoint)) != g_dleafs) { if ((leaf_mid->contents != CONTENTS_SKY) && (leaf_mid->contents != CONTENTS_SOLID)) { return leaf_mid; } } VectorCopy(midpoint, broken_midpoint); //Verbose("Tried original method, failed at (%4.3f %4.3f %4.3f)\n", midpoint[0], midpoint[1], midpoint[2]); } VectorCopy(broken_midpoint, midpoint); return HuntForWorld(midpoint, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET); } // ===================================================================================== // SimpleNudge // Return vec_t in point only valid when function returns true // Use negative scales to push away from center instead // ===================================================================================== static bool SimpleNudge(vec_t* const point, const lightinfo_t* const l, vec_t* const s, vec_t* const t, const vec_t delta) { const int facenum = l->surfnum; const dface_t* f = g_dfaces + facenum; const dplane_t* p = getPlaneFromFace(f); const vec_t* face_delta = g_face_offset[facenum]; const int h = l->texsize[1] + 1; const int w = l->texsize[0] + 1; const vec_t half_w = (vec_t)(w - 1) / 2.0; const vec_t half_h = (vec_t)(h - 1) / 2.0; const vec_t s_vec = *s; const vec_t t_vec = *t; vec_t s1; vec_t t1; if (s_vec > half_w) { s1 = s_vec - delta; } else { s1 = s_vec + delta; } SetSurfFromST(l, point, s1, t_vec); if (HuntForWorld(point, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET)) { *s = s1; return true; } if (t_vec > half_h) { t1 = t_vec - delta; } else { t1 = t_vec + delta; } SetSurfFromST(l, point, s_vec, t1); if (HuntForWorld(point, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET)) { *t = t1; return true; } return false; } typedef enum { LightOutside, // Not lit LightShifted, // used HuntForWorld on 100% dark face LightShiftedInside, // moved to neighbhor on 2nd cleanup pass LightNormal, // Normally lit with no movement LightPulledInside, // Pulled inside by bleed code adjustments LightSimpleNudge, // A simple nudge 1/3 or 2/3 towards center along S or T axist LightSimpleNudgeEmbedded // A nudge either 1 full unit in each of S and T axis, or 1/3 or 2/3 AWAY from center } light_flag_t; // ===================================================================================== // CalcPoints // For each texture aligned grid point, back project onto the plane // to get the world xyz value of the sample point // ===================================================================================== static void CalcPoints(lightinfo_t* l) { const int facenum = l->surfnum; const dface_t* f = g_dfaces + facenum; const dplane_t* p = getPlaneFromFace(f); const vec_t* face_delta = g_face_offset[facenum]; const eModelLightmodes lightmode = g_face_lightmode[facenum]; const vec_t mids = (l->exactmaxs[0] + l->exactmins[0]) / 2; const vec_t midt = (l->exactmaxs[1] + l->exactmins[1]) / 2; const int h = l->texsize[1] + 1; const int w = l->texsize[0] + 1; const vec_t starts = (l->texmins[0] * 16); const vec_t startt = (l->texmins[1] * 16); light_flag_t LuxelFlags[MAX_SINGLEMAP]; light_flag_t* pLuxelFlags; vec_t us, ut; vec_t* surf; vec3_t surface_midpoint; dleaf_t* leaf_mid; dleaf_t* leaf_surf; int s, t; int i; l->numsurfpt = w * h; memset(LuxelFlags, 0, sizeof(LuxelFlags)); leaf_mid = FindSurfaceMidpoint(l, surface_midpoint); #if 0 if (!leaf_mid) { Developer(DEVELOPER_LEVEL_FLUFF, "CalcPoints [face %d] (%4.3f %4.3f %4.3f) midpoint outside world\n", facenum, surface_midpoint[0], surface_midpoint[1], surface_midpoint[2]); } else { Developer(DEVELOPER_LEVEL_FLUFF, "FindSurfaceMidpoint [face %d] @ (%4.3f %4.3f %4.3f)\n", facenum, surface_midpoint[0], surface_midpoint[1], surface_midpoint[2]); } #endif // First pass, light normally, and pull any faces toward the center for bleed adjustment surf = l->surfpt[0]; pLuxelFlags = LuxelFlags; for (t = 0; t < h; t++) { for (s = 0; s < w; s++, surf += 3, pLuxelFlags++) { vec_t original_s = us = starts + s * TEXTURE_STEP; vec_t original_t = ut = startt + t * TEXTURE_STEP; SetSurfFromST(l, surf, us, ut); leaf_surf = HuntForWorld(surf, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET); if (!leaf_surf) { // At first try a 1/3 and 2/3 distance to nearest in each S and T axis towards the face midpoint if (SimpleNudge(surf, l, &us, &ut, TEXTURE_STEP * (1.0 / 3.0))) { *pLuxelFlags = LightSimpleNudge; } else if (SimpleNudge(surf, l, &us, &ut, -TEXTURE_STEP * (1.0 / 3.0))) { *pLuxelFlags = LightSimpleNudge; } else if (SimpleNudge(surf, l, &us, &ut, TEXTURE_STEP * (2.0 / 3.0))) { *pLuxelFlags = LightSimpleNudge; } else if (SimpleNudge(surf, l, &us, &ut, -TEXTURE_STEP * (2.0 / 3.0))) { *pLuxelFlags = LightSimpleNudge; } // Next, if this is a model flagged with the 'Embedded' mode, try away from the facemid too else if (lightmode & eModelLightmodeEmbedded) { SetSurfFromST(l, surf, us, ut); if (SimpleNudge(surf, l, &us, &ut, TEXTURE_STEP)) { *pLuxelFlags = LightSimpleNudgeEmbedded; continue; } if (SimpleNudge(surf, l, &us, &ut, -TEXTURE_STEP)) { *pLuxelFlags = LightSimpleNudgeEmbedded; continue; } SetSurfFromST(l, surf, original_s, original_t); *pLuxelFlags = LightOutside; continue; } } if (!(lightmode & eModelLightmodeEmbedded)) { // Pull the sample points towards the facemid if visibility is blocked // and the facemid is inside the world int nudge_divisor = max(max(w, h), 4); int max_nudge = nudge_divisor + 1; bool nudge_succeeded = false; vec_t nudge_s = (mids - us) / (vec_t)nudge_divisor; vec_t nudge_t = (midt - ut) / (vec_t)nudge_divisor; vec3_t transparency; // if a line can be traced from surf to facemid, the point is good for (i = 0; i < max_nudge; i++) { // Make sure we are "in the world"(Not the zero leaf) if (leaf_mid) { SetSurfFromST(l, surf, us, ut); leaf_surf = HuntForWorld(surf, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET); if (leaf_surf) { if (TestLine(surface_midpoint, surf) == CONTENTS_EMPTY) { if (lightmode & eModelLightmodeConcave) { #ifdef HLRAD_HULLU //removed reset of transparency - TestSegmentAgainstOpaqueList already resets to 1,1,1 int blocking_facenum = TestSegmentAgainstOpaqueList(surface_midpoint, surf, transparency); #else int blocking_facenum = TestSegmentAgainstOpaqueList(surface_midpoint, surf); #endif if(blocking_facenum > -1 && blocking_facenum != facenum) { //Log("SDF::4\n"); us += nudge_s; ut += nudge_t; continue; // Try nudge again, we hit an opaque face } } if (i) { *pLuxelFlags = LightPulledInside; } else { *pLuxelFlags = LightNormal; } nudge_succeeded = true; break; } } } else { leaf_surf = PointInLeaf(surf); if (leaf_surf != g_dleafs) { if ((leaf_surf->contents != CONTENTS_SKY) && (leaf_surf->contents != CONTENTS_SOLID)) { *pLuxelFlags = LightNormal; nudge_succeeded = true; break; } } } us += nudge_s; ut += nudge_t; } if (!nudge_succeeded) { SetSurfFromST(l, surf, original_s, original_t); *pLuxelFlags = LightOutside; } } } } // 2nd Pass, find units that are not lit and try to move them one half or unit worth // in each direction and see if that is lit. // This handles 1 x N lightmaps which are all dark everywhere and have no frame of refernece // for a good center or directly lit areas surf = l->surfpt[0]; pLuxelFlags = LuxelFlags; #if 0 Developer(DEVELOPER_LEVEL_SPAM, "w (%d) h (%d) dim (%d) leafmid (%4.3f %4.3f %4.3f) plane normal (%4.3f) (%4.3f) (%4.3f) dist (%f)\n", w, h, w * h, surface_midpoint[0], surface_midpoint[1], surface_midpoint[2], p->normal[0], p->normal[1], p->normal[2], p->dist); #endif { int total_dark = 0; int total_adjusted = 0; for (t = 0; t < h; t++) { for (s = 0; s < w; s++, surf += 3, pLuxelFlags++) { if (!*pLuxelFlags) { #if 0 Developer(DEVELOPER_LEVEL_FLUFF, "Dark (%d %d) (%4.3f %4.3f %4.3f)\n", s, t, surf[0], surf[1], surf[2]); #endif total_dark++; if (HuntForWorld(surf, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET)) { #if 0 Developer(DEVELOPER_LEVEL_FLUFF, "Shifted %d %d to (%4.3f %4.3f %4.3f)\n", s, t, surf[0], surf[1], surf[2]); #endif *pLuxelFlags = LightShifted; total_adjusted++; } else if (HuntForWorld(surf, face_delta, p, 101, 0.5, DEFAULT_HUNT_OFFSET)) { #if 0 Developer(DEVELOPER_LEVEL_FLUFF, "Shifted %d %d to (%4.3f %4.3f %4.3f)\n", s, t, surf[0], surf[1], surf[2]); #endif *pLuxelFlags = LightShifted; total_adjusted++; } } } } #if 0 if (total_dark) { Developer(DEVELOPER_LEVEL_FLUFF, "Pass 2 : %d dark, %d corrected\n", total_dark, total_adjusted); } #endif } // 3rd Pass, find units that are not lit and move them towards neighbhors who are // Currently finds the first lit neighbhor and uses its data surf = l->surfpt[0]; pLuxelFlags = LuxelFlags; { int total_dark = 0; int total_adjusted = 0; for (t = 0; t < h; t++) { for (s = 0; s < w; s++, surf += 3, pLuxelFlags++) { if (!*pLuxelFlags) { int x_min = max(0, s - 1); int x_max = min(w, s + 1); int y_min = max(0, t - 1); int y_max = min(t, t + 1); int x, y; #if 0 Developer(DEVELOPER_LEVEL_FLUFF, "Point outside (%d %d) (%4.3f %4.3f %4.3f)\n", s, t, surf[0], surf[1], surf[2]); #endif total_dark++; for (x = x_min; x < x_max; x++) { for (y = y_min; y < y_max; y++) { if (*pLuxelFlags >= LightNormal) { dleaf_t* leaf; vec_t* other_surf = l->surfpt[0]; other_surf += ((y * w) + x) * 3; leaf = PointInLeaf(other_surf); if ((leaf->contents != CONTENTS_SKY && leaf->contents != CONTENTS_SOLID)) { *pLuxelFlags = LightShiftedInside; #if 0 Developer(DEVELOPER_LEVEL_MESSAGE, "Nudged (%d %d) (%4.3f %4.3f %4.3f) to (%d %d) (%4.3f %4.3f %4.3f) \n", s, t, surf[0], surf[1], surf[2], x, y, other_surf[0], other_surf[1], other_surf[2]); #endif VectorCopy(other_surf, surf); total_adjusted++; goto found_it; } } } } } found_it:; } } #if 0 if (total_dark) { Developer(DEVELOPER_LEVEL_FLUFF, "Pass 2 : %d dark, %d corrected\n", total_dark, total_adjusted); } #endif } } //============================================================== typedef struct { vec3_t pos; vec3_t light; } sample_t; typedef struct { int numsamples; sample_t* samples[MAXLIGHTMAPS]; } facelight_t; static directlight_t* directlights[MAX_MAP_LEAFS]; static facelight_t facelight[MAX_MAP_FACES]; static int numdlights; #define DIRECT_SCALE 0.1f // ===================================================================================== // CreateDirectLights // ===================================================================================== void CreateDirectLights() { unsigned i; patch_t* p; directlight_t* dl; dleaf_t* leaf; int leafnum; entity_t* e; entity_t* e2; const char* name; const char* target; float angle; vec3_t dest; // AJM: coplaner lighting vec3_t temp_normal; numdlights = 0; // // surfaces // for (i = 0, p = g_patches; i < g_num_patches; i++, p++) { #ifdef ZHLT_TEXLIGHT if (VectorAvg(p->baselight) >= g_dlight_threshold) //LRC #else if (VectorAvg(p->totallight) >= g_dlight_threshold) #endif { numdlights++; dl = (directlight_t*)calloc(1, sizeof(directlight_t)); VectorCopy(p->origin, dl->origin); leaf = PointInLeaf(dl->origin); leafnum = leaf - g_dleafs; dl->next = directlights[leafnum]; directlights[leafnum] = dl; #ifdef ZHLT_TEXLIGHT dl->style = p->emitstyle; //LRC #endif dl->type = emit_surface; VectorCopy(getPlaneFromFaceNumber(p->faceNumber)->normal, dl->normal); #ifdef ZHLT_TEXLIGHT VectorCopy(p->baselight, dl->intensity); //LRC #else VectorCopy(p->totallight, dl->intensity); #endif VectorScale(dl->intensity, p->area, dl->intensity); VectorScale(dl->intensity, DIRECT_SCALE, dl->intensity); // -------------------------------------------------------------- // Changes by Adam Foster - afoster@compsoc.man.ac.uk // mazemaster's l33t backwards lighting (I still haven't a clue // what it's supposed to be for) :-) #ifdef HLRAD_WHOME if (g_softlight_hack[0] || g_softlight_hack[1] || g_softlight_hack[2]) { numdlights++; dl = (directlight_t *) calloc(1, sizeof(directlight_t)); VectorCopy(p->origin, dl->origin); leaf = PointInLeaf(dl->origin); leafnum = leaf - g_dleafs; dl->next = directlights[leafnum]; directlights[leafnum] = dl; dl->type = emit_surface; VectorCopy(getPlaneFromFaceNumber(p->faceNumber)->normal, dl->normal); VectorScale(dl->normal, g_softlight_hack_distance, temp_normal); VectorAdd(dl->origin, temp_normal, dl->origin); VectorScale(dl->normal, -1, dl->normal); #ifdef ZHLT_TEXLIGHT VectorCopy(p->baselight, dl->intensity); //LRC #else VectorCopy(p->totallight, dl->intensity); #endif VectorScale(dl->intensity, p->area, dl->intensity); VectorScale(dl->intensity, DIRECT_SCALE, dl->intensity); dl->intensity[0] *= g_softlight_hack[0]; dl->intensity[1] *= g_softlight_hack[1]; dl->intensity[2] *= g_softlight_hack[2]; } #endif // -------------------------------------------------------------- } #ifdef ZHLT_TEXLIGHT //LRC VectorClear(p->totallight[0]); #else VectorClear(p->totallight); #endif } // // entities // for (i = 0; i < (unsigned)g_numentities; i++) { const char* pLight; double r, g, b, scaler; float l1; int argCnt; e = &g_entities[i]; name = ValueForKey(e, "classname"); if (strncmp(name, "light", 5)) continue; numdlights++; dl = (directlight_t*)calloc(1, sizeof(directlight_t)); GetVectorForKey(e, "origin", dl->origin); leaf = PointInLeaf(dl->origin); leafnum = leaf - g_dleafs; dl->next = directlights[leafnum]; directlights[leafnum] = dl; dl->style = IntForKey(e, "style"); #ifdef ZHLT_TEXLIGHT if (dl->style < 0) dl->style = -dl->style; //LRC #endif pLight = ValueForKey(e, "_light"); // scanf into doubles, then assign, so it is vec_t size independent r = g = b = scaler = 0; argCnt = sscanf_s(pLight, "%lf %lf %lf %lf", &r, &g, &b, &scaler); dl->intensity[0] = (float)r; if (argCnt == 1) { // The R,G,B values are all equal. dl->intensity[1] = dl->intensity[2] = (float)r; } else if (argCnt == 3 || argCnt == 4) { // Save the other two G,B values. dl->intensity[1] = (float)g; dl->intensity[2] = (float)b; // Did we also get an "intensity" scaler value too? if (argCnt == 4) { // Scale the normalized 0-255 R,G,B values by the intensity scaler dl->intensity[0] = dl->intensity[0] / 255 * (float)scaler; dl->intensity[1] = dl->intensity[1] / 255 * (float)scaler; dl->intensity[2] = dl->intensity[2] / 255 * (float)scaler; } } else { Log("light at (%f,%f,%f) has bad or missing '_light' value : '%s'\n", dl->origin[0], dl->origin[1], dl->origin[2], pLight); continue; } dl->fade = FloatForKey(e, "_fade"); if (dl->fade == 0.0) { dl->fade = g_fade; } dl->falloff = IntForKey(e, "_falloff"); if (dl->falloff == 0) { dl->falloff = g_falloff; } target = ValueForKey(e, "target"); if (!strcmp(name, "light_spot") || !strcmp(name, "light_environment") || target[0]) { if (!VectorAvg(dl->intensity)) { VectorFill(dl->intensity, 500); } dl->type = emit_spotlight; dl->stopdot = FloatForKey(e, "_cone"); if (!dl->stopdot) { dl->stopdot = 10; } dl->stopdot2 = FloatForKey(e, "_cone2"); if (!dl->stopdot2) { dl->stopdot2 = dl->stopdot; } if (dl->stopdot2 < dl->stopdot) { dl->stopdot2 = dl->stopdot; } dl->stopdot2 = (float)cos(dl->stopdot2 / 180 * Q_PI); dl->stopdot = (float)cos(dl->stopdot / 180 * Q_PI); if (target[0]) { // point towards target e2 = FindTargetEntity(target); if (!e2) { Warning("light at (%i %i %i) has missing target", (int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]); } else { GetVectorForKey(e2, "origin", dest); VectorSubtract(dest, dl->origin, dl->normal); VectorNormalize(dl->normal); } } else { // point down angle vec3_t vAngles; GetVectorForKey(e, "angles", vAngles); angle = (float)FloatForKey(e, "angle"); if (angle == ANGLE_UP) { dl->normal[0] = dl->normal[1] = 0; dl->normal[2] = 1; } else if (angle == ANGLE_DOWN) { dl->normal[0] = dl->normal[1] = 0; dl->normal[2] = -1; } else { // if we don't have a specific "angle" use the "angles" YAW if (!angle) { angle = vAngles[1]; } dl->normal[2] = 0; dl->normal[0] = (float)cos(angle / 180 * Q_PI); dl->normal[1] = (float)sin(angle / 180 * Q_PI); } angle = FloatForKey(e, "pitch"); if (!angle) { // if we don't have a specific "pitch" use the "angles" PITCH angle = vAngles[0]; } dl->normal[2] = (float)sin(angle / 180 * Q_PI); dl->normal[0] *= (float)cos(angle / 180 * Q_PI); dl->normal[1] *= (float)cos(angle / 180 * Q_PI); } if (FloatForKey(e, "_sky") || !strcmp(name, "light_environment")) { // ----------------------------------------------------------------------------------- // Changes by Adam Foster - afoster@compsoc.man.ac.uk // diffuse lighting hack - most of the following code nicked from earlier // need to get diffuse intensity from new _diffuse_light key // // What does _sky do for spotlights, anyway? // ----------------------------------------------------------------------------------- #ifdef HLRAD_WHOME pLight = ValueForKey(e, "_diffuse_light"); r = g = b = scaler = 0; argCnt = sscanf_s(pLight, "%lf %lf %lf %lf", &r, &g, &b, &scaler); dl->diffuse_intensity[0] = (float)r; if (argCnt == 1) { // The R,G,B values are all equal. dl->diffuse_intensity[1] = dl->diffuse_intensity[2] = (float)r; } else if (argCnt == 3 || argCnt == 4) { // Save the other two G,B values. dl->diffuse_intensity[1] = (float)g; dl->diffuse_intensity[2] = (float)b; // Did we also get an "intensity" scaler value too? if (argCnt == 4) { // Scale the normalized 0-255 R,G,B values by the intensity scaler dl->diffuse_intensity[0] = dl->diffuse_intensity[0] / 255 * (float)scaler; dl->diffuse_intensity[1] = dl->diffuse_intensity[1] / 255 * (float)scaler; dl->diffuse_intensity[2] = dl->diffuse_intensity[2] / 255 * (float)scaler; } } else { // backwards compatibility with maps without _diffuse_light dl->diffuse_intensity[0] = dl->intensity[0]; dl->diffuse_intensity[1] = dl->intensity[1]; dl->diffuse_intensity[2] = dl->intensity[2]; } #endif // ----------------------------------------------------------------------------------- dl->type = emit_skylight; dl->stopdot2 = FloatForKey(e, "_sky"); // hack stopdot2 to a sky key number } } else { if (!VectorAvg(dl->intensity)) VectorFill(dl->intensity, 300); dl->type = emit_point; } if (dl->type != emit_skylight) { l1 = max(dl->intensity[0], max(dl->intensity[1], dl->intensity[2])); l1 = l1 * l1 / 10; dl->intensity[0] *= l1; dl->intensity[1] *= l1; dl->intensity[2] *= l1; } } hlassume(numdlights, assume_NoLights); Log("Direct lights: %i\n", numdlights); } // ===================================================================================== // DeleteDirectLights // ===================================================================================== void DeleteDirectLights(int * lNumAddress, int * lListAddress) { int l; directlight_t* dl; for (l = 0; l < g_numleafs; l++) { dl = directlights[l]; while (dl) { directlights[l] = dl->next; free(dl); dl = directlights[l]; } } //SILENCER--> //Strip light entities if(g_light_remove) { int i = 0; for (i = 0; i < *(lNumAddress); i++) { MarkEntityAsRedundant(&g_entities[*(lListAddress + i)]); } UnparseEntities(); Log("Obsolete light entities removed: %i\n", i); } else { Log("Removal of obsolete lights disabled. Skipping.\n"); } //<--SILENCER } // ===================================================================================== // GatherSampleLight // ===================================================================================== #define NUMVERTEXNORMALS 162 double r_avertexnormals[NUMVERTEXNORMALS][3] = { #include "../common/anorms.h" }; static void GatherSampleLight(const int input_facenum, const vec3_t pos, const byte* const pvs, const vec3_t normal, vec3_t* sample, byte* styles) { int i; directlight_t* l; vec3_t add; vec3_t delta; #ifdef HLRAD_FASTMATH2 const float LIGHT_EPSILON = ON_EPSILON; float dot; #else float dot, dot2; #endif float dist; float ratio; #ifdef HLRAD_OPACITY // AJM float l_opacity; #endif int style_index; directlight_t* sky_used = NULL; vec3_t transparency; for (i = 1; i < g_numleafs; i++) { l = directlights[i]; if (l) { if (((l->type == emit_skylight) && (g_sky_lighting_fix)) || (pvs[(i - 1) >> 3] & (1 << ((i - 1) & 7)))) { for (; l; l = l->next) { // skylights work fundamentally differently than normal lights if (l->type == emit_skylight) { // only allow one of each sky type to hit any given point if (sky_used) { continue; } sky_used = l; // make sure the angle is okay dot = -DotProduct(normal, l->normal); if (dot <= ON_EPSILON / 10) { continue; } // search back to see if we can hit a sky brush VectorScale(l->normal, -10000, delta); VectorAdd(pos, delta, delta); if (TestLine(pos, delta) != CONTENTS_SKY) { continue; // occluded } #ifdef HLRAD_HULLU //removed reset of transparency - TestSegmentAgainstOpaqueList already resets to 1,1,1 int facenum = TestSegmentAgainstOpaqueList(pos, delta, transparency); #else int facenum = TestSegmentAgainstOpaqueList(pos, delta); #endif if(facenum > -1 && facenum != input_facenum) { continue; } VectorScale(l->intensity, dot, add); #ifdef HLRAD_HULLU VectorMultiply(add, transparency, add); #endif } #ifdef HLRAD_FASTMATH2 else { VectorSubtract(l->origin,pos,delta); dist = min(1,VectorNormalize(delta)); //VectorNormalize returns old length dot = DotProduct(delta,normal); if(dot <= LIGHT_EPSILON) { continue; } //behind surface //calculate numberator factor caused by light type switch(l->type) { case emit_point: ratio = 1; break; case emit_surface: ratio = -DotProduct(delta,l->normal); if(ratio <= LIGHT_EPSILON) { continue; } //behind light surface break; case emit_spotlight: ratio = -DotProduct(delta,l->normal); if(ratio <= l->stopdot2) { continue; } //outside penumbra if(ratio <= l->stopdot) //between umbra and penumbra - scale it { ratio *= (ratio - l->stopdot2) / (l->stopdot - l->stopdot2); } break; default: hlassume(false, assume_BadLightType); break; } //primitive ratio (all types) ratio *= dot; ratio /= dist; //calculate denominator factor caused by light type switch(l->type) { case emit_point: case emit_spotlight: ratio /= l->fade; if(l->falloff == 2) { ratio /= dist; } break; case emit_surface: ratio /= g_fade; if(g_falloff == 2) { ratio /= dist; } break; } VectorScale(l->intensity, ratio, add); } #else else { float denominator; VectorSubtract(l->origin, pos, delta); dist = VectorNormalize(delta); dot = DotProduct(delta, normal); // if (dot <= 0.0) // continue; if (dot <= ON_EPSILON / 10) { continue; // behind sample surface } if (dist < 1.0) { dist = 1.0; } // Variable power falloff (1 = inverse linear, 2 = inverse square denominator = dist * l->fade; if (l->falloff == 2) { denominator *= dist; } switch (l->type) { case emit_point: { // Variable power falloff (1 = inverse linear, 2 = inverse square vec_t denominator = dist * l->fade; if (l->falloff == 2) { denominator *= dist; } ratio = dot / denominator; VectorScale(l->intensity, ratio, add); break; } case emit_surface: { dot2 = -DotProduct(delta, l->normal); if (dot2 <= ON_EPSILON / 10) { continue; // behind light surface } // Variable power falloff (1 = inverse linear, 2 = inverse square vec_t denominator = dist * g_fade; if (g_falloff == 2) { denominator *= dist; } ratio = dot * dot2 / denominator; VectorScale(l->intensity, ratio, add); break; } case emit_spotlight: { dot2 = -DotProduct(delta, l->normal); if (dot2 <= l->stopdot2) { continue; // outside light cone } // Variable power falloff (1 = inverse linear, 2 = inverse square vec_t denominator = dist * l->fade; if (l->falloff == 2) { denominator *= dist; } ratio = dot * dot2 / denominator; if (dot2 <= l->stopdot) { ratio *= (dot2 - l->stopdot2) / (l->stopdot - l->stopdot2); } VectorScale(l->intensity, ratio, add); break; } default: { hlassume(false, assume_BadLightType); break; } } } #endif if (VectorMaximum(add) > (l->style ? g_coring : 0)) { #ifdef HLRAD_HULLU vec3_t transparency = {1.0,1.0,1.0}; #endif if (l->type != emit_skylight && TestLine(pos, l->origin) != CONTENTS_EMPTY) { continue; // occluded } if (l->type != emit_skylight) { // Don't test from light_environment entities to face, the special sky code occludes correctly #ifdef HLRAD_HULLU int facenum = TestSegmentAgainstOpaqueList(pos, delta, transparency); #else int facenum = TestSegmentAgainstOpaqueList(pos, delta); #endif if(facenum > -1 && facenum != input_facenum) { continue; } } #ifdef HLRAD_OPACITY //VectorScale(add, l_opacity, add); #endif for (style_index = 0; style_index < MAXLIGHTMAPS; style_index++) { if (styles[style_index] == l->style || styles[style_index] == 255) { break; } } if (style_index == MAXLIGHTMAPS) { Warning("Too many direct light styles on a face(%f,%f,%f)", pos[0], pos[1], pos[2]); continue; } if (styles[style_index] == 255) { styles[style_index] = l->style; } #ifdef HLRAD_HULLU VectorMultiply(add,transparency,add); #endif VectorAdd(sample[style_index], add, sample[style_index]); } } } } } if (sky_used && g_indirect_sun != 0.0) { vec3_t total; int j; vec3_t sky_intensity; // ----------------------------------------------------------------------------------- // Changes by Adam Foster - afoster@compsoc.man.ac.uk // Instead of using intensity from sky_used->intensity, get it from the new sky_used->diffuse_intensity #ifdef HLRAD_WHOME VectorScale(sky_used->diffuse_intensity, g_indirect_sun / (NUMVERTEXNORMALS * 2), sky_intensity); #else VectorScale(sky_used->intensity, g_indirect_sun / (NUMVERTEXNORMALS * 2), sky_intensity); #endif // That should be it. Who knows - it might actually work! // AJM: It DOES actually work. Havent you ever heard of beta testing.... // ----------------------------------------------------------------------------------- total[0] = total[1] = total[2] = 0.0; for (j = 0; j < NUMVERTEXNORMALS; j++) { // make sure the angle is okay dot = -DotProduct(normal, r_avertexnormals[j]); if (dot <= ON_EPSILON / 10) { continue; } // search back to see if we can hit a sky brush VectorScale(r_avertexnormals[j], -10000, delta); VectorAdd(pos, delta, delta); if (TestLine(pos, delta) != CONTENTS_SKY) { continue; // occluded } VectorScale(sky_intensity, dot, add); VectorAdd(total, add, total); } if (VectorMaximum(total) > 0) { for (style_index = 0; style_index < MAXLIGHTMAPS; style_index++) { if (styles[style_index] == sky_used->style || styles[style_index] == 255) { break; } } if (style_index == MAXLIGHTMAPS) { Warning("Too many direct light styles on a face(%f,%f,%f)", pos[0], pos[1], pos[2]); return; } if (styles[style_index] == 255) { styles[style_index] = sky_used->style; } VectorAdd(sample[style_index], total, sample[style_index]); } } } // ===================================================================================== // AddSampleToPatch // Take the sample's collected light and add it back into the apropriate patch for the radiosity pass. // ===================================================================================== #ifdef ZHLT_TEXLIGHT static void AddSampleToPatch(const sample_t* const s, const int facenum, int style) //LRC #else static void AddSampleToPatch(const sample_t* const s, const int facenum) #endif { patch_t* patch; BoundingBox bounds; int i; if (g_numbounce == 0) { return; } for (patch = g_face_patches[facenum]; patch; patch = patch->next) { // see if the point is in this patch (roughly) patch->winding->getBounds(bounds); for (i = 0; i < 3; i++) { if (bounds.m_Mins[i] > s->pos[i] + 16) { goto nextpatch; } if (bounds.m_Maxs[i] < s->pos[i] - 16) { goto nextpatch; } } // add the sample to the patch #ifdef ZHLT_TEXLIGHT //LRC: for (i = 0; i < MAXLIGHTMAPS && patch->totalstyle[i] != 255; i++) { if (patch->totalstyle[i] == style) break; } if (i == MAXLIGHTMAPS) { if(!g_warned_direct || g_verbose) { Warning("Too many light styles on a face(%f,%f,%f)",patch->origin[0],patch->origin[1],patch->origin[2]); g_warned_direct = true; } } else { if (patch->totalstyle[i] == 255) { patch->totalstyle[i] = style; } patch->samples[i]++; VectorAdd(patch->samplelight[i], s->light, patch->samplelight[i]); } //LRC (ends) #else patch->samples++; VectorAdd(patch->samplelight, s->light, patch->samplelight); #endif //return; nextpatch:; } // don't worry if some samples don't find a patch } // ===================================================================================== // GetPhongNormal // ===================================================================================== void GetPhongNormal(int facenum, vec3_t spot, vec3_t phongnormal) { int j; const dface_t* f = g_dfaces + facenum; const dplane_t* p = getPlaneFromFace(f); vec3_t facenormal; VectorCopy(p->normal, facenormal); VectorCopy(facenormal, phongnormal); if (g_smoothing_threshold > 0.0) { // Calculate modified point normal for surface // Use the edge normals iff they are defined. Bend the surface towards the edge normal(s) // Crude first attempt: find nearest edge normal and do a simple interpolation with facenormal. // Second attempt: find edge points+center that bound the point and do a three-point triangulation(baricentric) // Better third attempt: generate the point normals for all vertices and do baricentric triangulation. for (j = 0; j < f->numedges; j++) { vec3_t p1; vec3_t p2; vec3_t v1; vec3_t v2; vec3_t vspot; unsigned prev_edge; unsigned next_edge; int e; int e1; int e2; edgeshare_t* es; edgeshare_t* es1; edgeshare_t* es2; float a1; float a2; float aa; float bb; float ab; if (j) { prev_edge = f->firstedge + ((j - 1) % f->numedges); } else { prev_edge = f->firstedge + f->numedges - 1; } if ((j + 1) != f->numedges) { next_edge = f->firstedge + ((j + 1) % f->numedges); } else { next_edge = f->firstedge; } e = g_dsurfedges[f->firstedge + j]; e1 = g_dsurfedges[prev_edge]; e2 = g_dsurfedges[next_edge]; es = &g_edgeshare[abs(e)]; es1 = &g_edgeshare[abs(e1)]; es2 = &g_edgeshare[abs(e2)]; if ( (es->coplanar && es1->coplanar && es2->coplanar) || (VectorCompare(es->interface_normal, vec3_origin) && VectorCompare(es1->interface_normal, vec3_origin) && VectorCompare(es2->interface_normal, vec3_origin))) { continue; } if (e > 0) { VectorCopy(g_dvertexes[g_dedges[e].v[0]].point, p1); VectorCopy(g_dvertexes[g_dedges[e].v[1]].point, p2); } else { VectorCopy(g_dvertexes[g_dedges[-e].v[1]].point, p1); VectorCopy(g_dvertexes[g_dedges[-e].v[0]].point, p2); } // Adjust for origin-based models VectorAdd(p1, g_face_offset[facenum], p1); VectorAdd(p2, g_face_offset[facenum], p2); // Build vectors from the middle of the face to the edge vertexes and the sample pos. VectorSubtract(p1, g_face_centroids[facenum], v1); VectorSubtract(p2, g_face_centroids[facenum], v2); VectorSubtract(spot, g_face_centroids[facenum], vspot); aa = DotProduct(v1, v1); bb = DotProduct(v2, v2); ab = DotProduct(v1, v2); a1 = (bb * DotProduct(v1, vspot) - ab * DotProduct(vspot, v2)) / (aa * bb - ab * ab); a2 = (DotProduct(vspot, v2) - a1 * ab) / bb; // Test center to sample vector for inclusion between center to vertex vectors (Use dot product of vectors) if (a1 >= 0.0 && a2 >= 0.0) { // calculate distance from edge to pos vec3_t n1, n2; vec3_t temp; VectorAdd(es->interface_normal, es1->interface_normal, n1); if (VectorCompare(n1, vec3_origin)) { VectorCopy(facenormal, n1); } VectorNormalize(n1); VectorAdd(es->interface_normal, es2->interface_normal, n2); if (VectorCompare(n2, vec3_origin)) { VectorCopy(facenormal, n2); } VectorNormalize(n2); // Interpolate between the center and edge normals based on sample position VectorScale(facenormal, 1.0 - a1 - a2, phongnormal); VectorScale(n1, a1, temp); VectorAdd(phongnormal, temp, phongnormal); VectorScale(n2, a2, temp); VectorAdd(phongnormal, temp, phongnormal); VectorNormalize(phongnormal); break; } } } } const vec3_t s_circuscolors[] = { {100000.0, 100000.0, 100000.0}, // white {100000.0, 0.0, 0.0 }, // red {0.0, 100000.0, 0.0 }, // green {0.0, 0.0, 100000.0}, // blue {0.0, 100000.0, 100000.0}, // cyan {100000.0, 0.0, 100000.0}, // magenta {100000.0, 100000.0, 0.0 } // yellow }; // ===================================================================================== // BuildFacelights // ===================================================================================== void BuildFacelights(const int facenum) { dface_t* f; vec3_t sampled[MAXLIGHTMAPS]; lightinfo_t l; int i; int j; int k; sample_t* s; vec_t* spot; patch_t* patch; const dplane_t* plane; byte pvs[(MAX_MAP_LEAFS + 7) / 8]; int thisoffset = -1, lastoffset = -1; int lightmapwidth; int lightmapheight; int size; #ifdef HLRAD_HULLU bool b_transparency_loss = false; vec_t light_left_for_facelight = 1.0; #endif f = &g_dfaces[facenum]; // // some surfaces don't need lightmaps // f->lightofs = -1; for (j = 0; j < MAXLIGHTMAPS; j++) { f->styles[j] = 255; } if (g_texinfo[f->texinfo].flags & TEX_SPECIAL) { return; // non-lit texture } f->styles[0] = 0; // Everyone gets the style zero map. memset(&l, 0, sizeof(l)); l.surfnum = facenum; l.face = f; // // get transparency loss (part of light go through transparency faces.. reduce facelight on these) // #ifdef HLRAD_HULLU for(unsigned int m = 0; m < g_opaque_face_count; m++) { opaqueList_t* opaque = &g_opaque_face_list[m]; if(opaque->facenum == facenum && opaque->transparency) { vec_t transparency = opaque->transparency; b_transparency_loss = true; light_left_for_facelight = 1.0 - transparency; if( light_left_for_facelight < 0.0 ) light_left_for_facelight = 0.0; if( light_left_for_facelight > 1.0 ) light_left_for_facelight = 1.0; break; } } #endif // // rotate plane // plane = getPlaneFromFace(f); VectorCopy(plane->normal, l.facenormal); l.facedist = plane->dist; CalcFaceVectors(&l); CalcFaceExtents(&l); CalcPoints(&l); lightmapwidth = l.texsize[0] + 1; lightmapheight = l.texsize[1] + 1; size = lightmapwidth * lightmapheight; hlassume(size <= MAX_SINGLEMAP, assume_MAX_SINGLEMAP); facelight[facenum].numsamples = l.numsurfpt; for (k = 0; k < MAXLIGHTMAPS; k++) { facelight[facenum].samples[k] = (sample_t*)calloc(l.numsurfpt, sizeof(sample_t)); } spot = l.surfpt[0]; for (i = 0; i < l.numsurfpt; i++, spot += 3) { vec3_t pointnormal = { 0, 0, 0 }; for (k = 0; k < MAXLIGHTMAPS; k++) { VectorCopy(spot, facelight[facenum].samples[k][i].pos); } // get the PVS for the pos to limit the number of checks if (!g_visdatasize) { memset(pvs, 255, (g_numleafs + 7) / 8); lastoffset = -1; } else { dleaf_t* leaf = PointInLeaf(spot); thisoffset = leaf->visofs; if (i == 0 || thisoffset != lastoffset) { hlassert(thisoffset != -1); DecompressVis(&g_dvisdata[leaf->visofs], pvs, sizeof(pvs)); } lastoffset = thisoffset; } memset(sampled, 0, sizeof(sampled)); // If we are doing "extra" samples, oversample the direct light around the point. if (g_extra) { int weighting[3][3] = { {5, 9, 5}, {9, 16, 9}, {5, 9, 5} }; vec3_t pos; int s, t, subsamples = 0; for (t = -1; t <= 1; t++) { for (s = -1; s <= 1; s++) { int subsample = i + t * lightmapwidth + s; int sample_s = i % lightmapwidth; int sample_t = i / lightmapwidth; if ((0 <= s + sample_s) && (s + sample_s < lightmapwidth) && (0 <= t + sample_t)&&(t + sample_t styles); for (j = 0; j < MAXLIGHTMAPS && (f->styles[j] != 255); j++) { VectorScale(subsampled[j], weighting[s + 1][t + 1], subsampled[j]); VectorAdd(sampled[j], subsampled[j], sampled[j]); } subsamples += weighting[s + 1][t + 1]; } } } for (j = 0; j < MAXLIGHTMAPS && (f->styles[j] != 255); j++) { VectorScale(sampled[j], 1.0 / subsamples, sampled[j]); } } else { GetPhongNormal(facenum, spot, pointnormal); GatherSampleLight(facenum, spot, pvs, pointnormal, sampled, f->styles); } for (j = 0; j < MAXLIGHTMAPS && (f->styles[j] != 255); j++) { VectorCopy(sampled[j], facelight[facenum].samples[j][i].light); #ifdef HLRAD_HULLU if(b_transparency_loss) { VectorScale(facelight[facenum].samples[j][i].light, light_left_for_facelight, facelight[facenum].samples[j][i].light); } #endif #ifdef ZHLT_TEXLIGHT AddSampleToPatch(&facelight[facenum].samples[j][i], facenum, f->styles[j]); //LRC #else if (f->styles[j] == 0) { AddSampleToPatch(&facelight[facenum].samples[j][i], facenum); } #endif } } // average up the direct light on each patch for radiosity if (g_numbounce > 0) { for (patch = g_face_patches[facenum]; patch; patch = patch->next) { #ifdef ZHLT_TEXLIGHT //LRC: unsigned istyle; for (istyle = 0; istyle < MAXLIGHTMAPS && patch->totalstyle[istyle] != 255; istyle++) { if (patch->samples[istyle]) { vec3_t v; // BUGBUG: Use a weighted average instead? VectorScale(patch->samplelight[istyle], (1.0f / patch->samples[istyle]), v); VectorAdd(patch->totallight[istyle], v, patch->totallight[istyle]); VectorAdd(patch->directlight[istyle], v, patch->directlight[istyle]); } } //LRC (ends) #else if (patch->samples) { vec3_t v; // BUGBUG: Use a weighted average instead? VectorScale(patch->samplelight, (1.0f / patch->samples), v); VectorAdd(patch->totallight, v, patch->totallight); VectorAdd(patch->directlight, v, patch->directlight); } #endif } } // add an ambient term if desired if (g_ambient[0] || g_ambient[1] || g_ambient[2]) { for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++) { if (f->styles[j] == 0) { s = facelight[facenum].samples[j]; for (i = 0; i < l.numsurfpt; i++, s++) { VectorAdd(s->light, g_ambient, s->light); } break; } } } // add circus lighting for finding black lightmaps if (g_circus) { for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++) { if (f->styles[j] == 0) { int amt = 7; s = facelight[facenum].samples[j]; while ((l.numsurfpt % amt) == 0) { amt--; } if (amt < 2) { amt = 7; } for (i = 0; i < l.numsurfpt; i++, s++) { if ((s->light[0] == 0) && (s->light[1] == 0) && (s->light[2] == 0)) { VectorAdd(s->light, s_circuscolors[i % amt], s->light); } } break; } } } // light from dlight_threshold and above is sent out, but the // texture itself should still be full bright // if( VectorAvg( face_patches[facenum]->baselight ) >= dlight_threshold) // Now all lighted surfaces glow { #ifdef ZHLT_TEXLIGHT //LRC: if (g_face_patches[facenum]) { for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++) { if (f->styles[j] == g_face_patches[facenum]->emitstyle) //LRC { break; } } if (j == MAXLIGHTMAPS) { if(!g_warned_direct || g_verbose) { Warning("Too many light styles on a face(%f,%f,%f)",g_face_patches[facenum]->origin[0],g_face_patches[facenum]->origin[1],g_face_patches[facenum]->origin[2]); g_warned_direct = true; } } else { if (f->styles[j] == 255) { f->styles[j] = g_face_patches[facenum]->emitstyle; } s = facelight[facenum].samples[j]; for (i = 0; i < l.numsurfpt; i++, s++) { VectorAdd(s->light, g_face_patches[facenum]->baselight, s->light); } } } //LRC (ends) #else for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++) { if (f->styles[j] == 0) { if (g_face_patches[facenum]) { s = facelight[facenum].samples[j]; for (i = 0; i < l.numsurfpt; i++, s++) { VectorAdd(s->light, g_face_patches[facenum]->baselight, s->light); } break; } } } #endif } } // ===================================================================================== // PrecompLightmapOffsets // ===================================================================================== void PrecompLightmapOffsets() { int facenum; dface_t* f; facelight_t* fl; int lightstyles; #ifdef ZHLT_TEXLIGHT int i; //LRC patch_t* patch; //LRC #endif g_lightdatasize = 0; for (facenum = 0; facenum < g_numfaces; facenum++) { f = &g_dfaces[facenum]; fl = &facelight[facenum]; if (g_texinfo[f->texinfo].flags & TEX_SPECIAL) { continue; // non-lit texture } #ifdef ZHLT_TEXLIGHT //LRC - find all the patch lightstyles, and add them to the ones used by this face patch = g_face_patches[facenum]; if (patch) { for (i = 0; i < MAXLIGHTMAPS && patch->totalstyle[i] != 255; i++) { for (lightstyles = 0; lightstyles < MAXLIGHTMAPS && f->styles[lightstyles] != 255; lightstyles++) { if (f->styles[lightstyles] == patch->totalstyle[i]) break; } if (lightstyles == MAXLIGHTMAPS) { if(!g_warned_direct || g_verbose) { Warning("Too many direct light styles on a face(%f,%f,%f)",patch->origin[0],patch->origin[1],patch->origin[2]); g_warned_direct = true; } } else if (f->styles[lightstyles] == 255) { f->styles[lightstyles] = patch->totalstyle[i]; // Log("Face acquires new lightstyle %d at offset %d\n", f->styles[lightstyles], lightstyles); } } } //LRC (ends) #endif for (lightstyles = 0; lightstyles < MAXLIGHTMAPS; lightstyles++) { if (f->styles[lightstyles] == 255) { break; } } if (!lightstyles) { continue; } f->lightofs = g_lightdatasize; g_lightdatasize += fl->numsamples * 3 * lightstyles; } } // ===================================================================================== // FinalLightFace // Add the indirect lighting on top of the direct lighting and save into final map format // ===================================================================================== void FinalLightFace(const int facenum) { int i, j, k; vec3_t lb, v; facelight_t* fl; sample_t* samp; float minlight; int lightstyles; dface_t* f; lerpTriangulation_t* trian = NULL; // ------------------------------------------------------------------------ // Changes by Adam Foster - afoster@compsoc.man.ac.uk #ifdef HLRAD_WHOME float temp_rand; #endif // ------------------------------------------------------------------------ f = &g_dfaces[facenum]; fl = &facelight[facenum]; if (g_texinfo[f->texinfo].flags & TEX_SPECIAL) { return; // non-lit texture } for (lightstyles = 0; lightstyles < MAXLIGHTMAPS; lightstyles++) { if (f->styles[lightstyles] == 255) { break; } } if (!lightstyles) { return; } // // set up the triangulation // if (g_numbounce) { trian = CreateTriangulation(facenum); } // // sample the triangulation // minlight = FloatForKey(g_face_entity[facenum], "_minlight") * 128; hlassume(f->lightofs + fl->numsamples*lightstyles*3 < g_max_map_lightdata, assume_MAX_MAP_LIGHTING); for (k = 0; k < lightstyles; k++) { samp = fl->samples[k]; for (j = 0; j < fl->numsamples; j++, samp++) { // Should be a VectorCopy, but we scale by 2 to compensate for an earlier lighting flaw // Specifically, the directlight contribution was included in the bounced light AND the directlight // Since many of the levels were built with this assumption, this "fudge factor" compensates for it. VectorScale(samp->light, g_direct_scale, lb); #ifdef ZHLT_TEXLIGHT if (g_numbounce)//LRC && (k == 0)) { SampleTriangulation(trian, samp->pos, v, f->styles[k]); //LRC #else if (g_numbounce && (k == 0)) { SampleTriangulation(trian, samp->pos, v); #endif if (isPointFinite(v)) { VectorAdd(lb, v, lb); } else { Warning("point (%4.3f %4.3f %4.3f) infinite v (%4.3f %4.3f %4.3f)\n", samp->pos[0], samp->pos[1], samp->pos[2], v[0], v[1], v[2]); } } // ------------------------------------------------------------------------ // Changes by Adam Foster - afoster@compsoc.man.ac.uk // colour lightscale - code was originally: #ifdef HLRAD_WHOME lb[0] *= g_colour_lightscale[0]; lb[1] *= g_colour_lightscale[1]; lb[2] *= g_colour_lightscale[2]; #else VectorScale(lb, g_lightscale, lb); #endif // ------------------------------------------------------------------------ // clip from the bottom first for (i = 0; i < 3; i++) { if (lb[i] < minlight) { lb[i] = minlight; } } // clip from the top { vec_t max = VectorMaximum(lb); if (max > g_maxlight) { vec_t scale = g_maxlight / max; lb[0] *= scale; lb[1] *= scale; lb[2] *= scale; } } // ------------------------------------------------------------------------ // Changes by Adam Foster - afoster@compsoc.man.ac.uk #ifdef HLRAD_WHOME // AJM: your code is formatted really wierd, and i cant understand a damn thing. // so i reformatted it into a somewhat readable "normal" fashion. :P // colour gamma - code was originally: // if (g_qgamma != 1.0) { // for (i = 0; i < 3; i++) { // lb[i] = (float) pow(lb[i] / 256.0f, g_qgamma) * 256.0f; // } // } if ( g_colour_qgamma[0] != 1.0 ) lb[0] = (float) pow(lb[0] / 256.0f, g_colour_qgamma[0]) * 256.0f; if ( g_colour_qgamma[1] != 1.0 ) lb[1] = (float) pow(lb[1] / 256.0f, g_colour_qgamma[1]) * 256.0f; if ( g_colour_qgamma[2] != 1.0 ) lb[2] = (float) pow(lb[2] / 256.0f, g_colour_qgamma[2]) * 256.0f; // Two different ways of adding noise to the lightmap - colour jitter // (red, green and blue channels are independent), and mono jitter // (monochromatic noise). For simulating dithering, on the cheap. :) // Tends to create seams between adjacent polygons, so not ideal. // Got really weird results when it was set to limit values to 256.0f - it // was as if r, g or b could wrap, going close to zero. if (g_colour_jitter_hack[0] || g_colour_jitter_hack[1] || g_colour_jitter_hack[2]) { for (i = 0; i < 3; i++) { lb[i] += g_colour_jitter_hack[i] * ((float)rand() / RAND_MAX - 0.5); if (lb[i] < 0.0f) { lb[i] = 0.0f; } else if (lb[i] > 255.0f) { lb[i] = 255.0f; } } } if (g_jitter_hack[0] || g_jitter_hack[1] || g_jitter_hack[2]) { temp_rand = (float)rand() / RAND_MAX - 0.5; for (i = 0; i < 3; i++) { lb[i] += g_jitter_hack[i] * temp_rand; if (lb[i] < 0.0f) { lb[i] = 0.0f; } else if (lb[i] > 255.0f) { lb[i] = 255.0f; } } } #else if (g_qgamma != 1.0) { for (i = 0; i < 3; i++) { lb[i] = (float) pow(lb[i] / 256.0f, g_qgamma) * 256.0f; } } #endif // ------------------------------------------------------------------------ { unsigned char* colors = &g_dlightdata[f->lightofs + k * fl->numsamples * 3 + j * 3]; colors[0] = (unsigned char)lb[0]; colors[1] = (unsigned char)lb[1]; colors[2] = (unsigned char)lb[2]; } } } if (g_numbounce) { FreeTriangulation(trian); } } #ifdef ZHLT_TEXLIGHT //LRC vec3_t totallight_default = { 0, 0, 0 }; //LRC - utility for getting the right totallight value from a patch vec3_t* GetTotalLight(patch_t* patch, int style) { int i; for (i = 0; i < MAXLIGHTMAPS && patch->totalstyle[i] != 255; i++) { if (patch->totalstyle[i] == style) return &(patch->totallight[i]); } return &totallight_default; } #endif