mirror of
https://github.com/nzp-team/vhlt.git
synced 2024-11-25 05:11:28 +00:00
9179 lines
275 KiB
C++
9179 lines
275 KiB
C++
#include "qrad.h"
|
|
|
|
edgeshare_t g_edgeshare[MAX_MAP_EDGES];
|
|
vec3_t g_face_centroids[MAX_MAP_EDGES]; // BUG: should this be [MAX_MAP_FACES]?
|
|
bool g_sky_lighting_fix = DEFAULT_SKY_LIGHTING_FIX;
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_SMOOTH_TEXNORMAL
|
|
vec3_t g_face_texnormals[MAX_MAP_FACES];
|
|
#endif
|
|
#endif
|
|
|
|
//#define TEXTURE_STEP 16.0
|
|
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_SMOOTH_TEXNORMAL
|
|
bool GetIntertexnormal (int facenum1, int facenum2, vec_t *out)
|
|
{
|
|
vec3_t normal;
|
|
const dplane_t *p1 = getPlaneFromFaceNumber (facenum1);
|
|
const dplane_t *p2 = getPlaneFromFaceNumber (facenum2);
|
|
VectorAdd (g_face_texnormals[facenum1], g_face_texnormals[facenum2], normal);
|
|
if (!VectorNormalize (normal)
|
|
|| DotProduct (normal, p1->normal) <= NORMAL_EPSILON
|
|
|| DotProduct (normal, p2->normal) <= NORMAL_EPSILON
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
if (out)
|
|
{
|
|
VectorCopy (normal, out);
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
#endif
|
|
// =====================================================================================
|
|
// PairEdges
|
|
// =====================================================================================
|
|
#ifdef HLRAD_SMOOTH_FACELIST
|
|
typedef struct
|
|
{
|
|
int numclipplanes;
|
|
dplane_t *clipplanes;
|
|
}
|
|
intersecttest_t;
|
|
bool TestFaceIntersect (intersecttest_t *t, int facenum)
|
|
{
|
|
dface_t *f2 = &g_dfaces[facenum];
|
|
Winding *w = new Winding (*f2);
|
|
int k;
|
|
for (k = 0; k < w->m_NumPoints; k++)
|
|
{
|
|
VectorAdd (w->m_Points[k], g_face_offset[facenum], w->m_Points[k]);
|
|
}
|
|
for (k = 0; k < t->numclipplanes; k++)
|
|
{
|
|
if (!w->Clip (t->clipplanes[k], false
|
|
#ifdef ZHLT_WINDING_EPSILON
|
|
, ON_EPSILON*4
|
|
#endif
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
bool intersect = w->m_NumPoints > 0;
|
|
delete w;
|
|
return intersect;
|
|
}
|
|
intersecttest_t *CreateIntersectTest (const dplane_t *p, int facenum)
|
|
{
|
|
dface_t *f = &g_dfaces[facenum];
|
|
intersecttest_t *t;
|
|
t = (intersecttest_t *)malloc (sizeof (intersecttest_t));
|
|
hlassume (t != NULL, assume_NoMemory);
|
|
t->clipplanes = (dplane_t *)malloc (f->numedges * sizeof (dplane_t));
|
|
hlassume (t->clipplanes != NULL, assume_NoMemory);
|
|
t->numclipplanes = 0;
|
|
int j;
|
|
for (j = 0; j < f->numedges; j++)
|
|
{
|
|
// should we use winding instead?
|
|
int edgenum = g_dsurfedges[f->firstedge + j];
|
|
{
|
|
vec3_t v0, v1;
|
|
vec3_t dir, normal;
|
|
if (edgenum < 0)
|
|
{
|
|
VectorCopy (g_dvertexes[g_dedges[-edgenum].v[1]].point, v0);
|
|
VectorCopy (g_dvertexes[g_dedges[-edgenum].v[0]].point, v1);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (g_dvertexes[g_dedges[edgenum].v[0]].point, v0);
|
|
VectorCopy (g_dvertexes[g_dedges[edgenum].v[1]].point, v1);
|
|
}
|
|
VectorAdd (v0, g_face_offset[facenum], v0);
|
|
VectorAdd (v1, g_face_offset[facenum], v1);
|
|
VectorSubtract (v1, v0, dir);
|
|
CrossProduct (dir, p->normal, normal); // facing inward
|
|
if (!VectorNormalize (normal))
|
|
{
|
|
continue;
|
|
}
|
|
VectorCopy (normal, t->clipplanes[t->numclipplanes].normal);
|
|
t->clipplanes[t->numclipplanes].dist = DotProduct (v0, normal);
|
|
t->numclipplanes++;
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
void FreeIntersectTest (intersecttest_t *t)
|
|
{
|
|
free (t->clipplanes);
|
|
free (t);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
void AddFaceForVertexNormal_printerror (const int edgeabs, const int edgeend, dface_t *const f)
|
|
{
|
|
if (DEVELOPER_LEVEL_WARNING <= g_developer)
|
|
{
|
|
int i, e;
|
|
Log ("AddFaceForVertexNormal - bad face:\n");
|
|
Log (" edgeabs=%d edgeend=%d\n", edgeabs, edgeend);
|
|
for (i = 0; i < f->numedges; i++)
|
|
{
|
|
e = g_dsurfedges[f->firstedge + i];
|
|
edgeshare_t *es = &g_edgeshare[abs(e)];
|
|
int v0 = g_dedges[abs(e)].v[0], v1 = g_dedges[abs(e)].v[1];
|
|
Log (" e=%d v0=%d(%f,%f,%f) v1=%d(%f,%f,%f) share0=%d share1=%d\n", e,
|
|
v0, g_dvertexes[v0].point[0], g_dvertexes[v0].point[1], g_dvertexes[v0].point[2],
|
|
v1, g_dvertexes[v1].point[0], g_dvertexes[v1].point[1], g_dvertexes[v1].point[2],
|
|
(es->faces[0]==NULL? -1: es->faces[0]-g_dfaces), (es->faces[1]==NULL? -1: es->faces[1]-g_dfaces));
|
|
}
|
|
}
|
|
}
|
|
int AddFaceForVertexNormal (const int edgeabs, int &edgeabsnext, const int edgeend, int &edgeendnext, dface_t *const f, dface_t *&fnext, vec_t &angle, vec3_t &normal)
|
|
// Must guarantee these faces will form a loop or a chain, otherwise will result in endless loop.
|
|
//
|
|
// e[end]/enext[endnext]
|
|
// *
|
|
// |\.
|
|
// |a\ fnext
|
|
// | \,
|
|
// | f \.
|
|
// | \.
|
|
// e enext
|
|
//
|
|
{
|
|
VectorCopy(getPlaneFromFace(f)->normal, normal);
|
|
int vnum = g_dedges[edgeabs].v[edgeend];
|
|
int iedge, iedgenext, edge, edgenext;
|
|
int i, e, count1, count2;
|
|
vec_t dot;
|
|
for (count1 = count2 = 0, i = 0; i < f->numedges; i++)
|
|
{
|
|
e = g_dsurfedges[f->firstedge + i];
|
|
if (g_dedges[abs(e)].v[0] == g_dedges[abs(e)].v[1])
|
|
continue;
|
|
if (abs(e) == edgeabs)
|
|
{
|
|
iedge = i;
|
|
edge = e;
|
|
count1 ++;
|
|
}
|
|
else if (g_dedges[abs(e)].v[0] == vnum || g_dedges[abs(e)].v[1] == vnum)
|
|
{
|
|
iedgenext = i;
|
|
edgenext = e;
|
|
count2 ++;
|
|
}
|
|
}
|
|
if (count1 != 1 || count2 != 1)
|
|
{
|
|
AddFaceForVertexNormal_printerror (edgeabs, edgeend, f);
|
|
return -1;
|
|
}
|
|
int vnum11, vnum12, vnum21, vnum22;
|
|
vec3_t vec1, vec2;
|
|
vnum11 = g_dedges[abs(edge)].v[edge>0?0:1];
|
|
vnum12 = g_dedges[abs(edge)].v[edge>0?1:0];
|
|
vnum21 = g_dedges[abs(edgenext)].v[edgenext>0?0:1];
|
|
vnum22 = g_dedges[abs(edgenext)].v[edgenext>0?1:0];
|
|
if (vnum == vnum12 && vnum == vnum21 && vnum != vnum11 && vnum != vnum22)
|
|
{
|
|
VectorSubtract(g_dvertexes[vnum11].point, g_dvertexes[vnum].point, vec1);
|
|
VectorSubtract(g_dvertexes[vnum22].point, g_dvertexes[vnum].point, vec2);
|
|
edgeabsnext = abs(edgenext);
|
|
edgeendnext = edgenext>0?0:1;
|
|
}
|
|
else if (vnum == vnum11 && vnum == vnum22 && vnum != vnum12 && vnum != vnum21)
|
|
{
|
|
VectorSubtract(g_dvertexes[vnum12].point, g_dvertexes[vnum].point, vec1);
|
|
VectorSubtract(g_dvertexes[vnum21].point, g_dvertexes[vnum].point, vec2);
|
|
edgeabsnext = abs(edgenext);
|
|
edgeendnext = edgenext>0?1:0;
|
|
}
|
|
else
|
|
{
|
|
AddFaceForVertexNormal_printerror (edgeabs, edgeend, f);
|
|
return -1;
|
|
}
|
|
VectorNormalize(vec1);
|
|
VectorNormalize(vec2);
|
|
dot = DotProduct(vec1,vec2);
|
|
dot = dot>1? 1: dot<-1? -1: dot;
|
|
angle = acos(dot);
|
|
edgeshare_t *es = &g_edgeshare[edgeabsnext];
|
|
if (!(es->faces[0] && es->faces[1]))
|
|
return 1;
|
|
if (es->faces[0] == f && es->faces[1] != f)
|
|
fnext = es->faces[1];
|
|
else if (es->faces[1] == f && es->faces[0] != f)
|
|
fnext = es->faces[0];
|
|
else
|
|
{
|
|
AddFaceForVertexNormal_printerror (edgeabs, edgeend, f);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
|
|
static bool TranslateTexToTex (int facenum, int edgenum, int facenum2, matrix_t &m, matrix_t &m_inverse)
|
|
// This function creates a matrix that can translate texture coords in face1 into texture coords in face2.
|
|
// It keeps all points in the common edge invariant. For example, if there is a point in the edge, and in the texture of face1, its (s,t)=(16,0), and in face2, its (s,t)=(128,64), then we must let matrix*(16,0,0)=(128,64,0)
|
|
{
|
|
matrix_t worldtotex;
|
|
matrix_t worldtotex2;
|
|
dedge_t *e;
|
|
int i;
|
|
dvertex_t *vert[2];
|
|
vec3_t face_vert[2];
|
|
vec3_t face2_vert[2];
|
|
vec3_t face_axis[2];
|
|
vec3_t face2_axis[2];
|
|
const vec3_t v_up = {0, 0, 1};
|
|
vec_t len;
|
|
vec_t len2;
|
|
matrix_t edgetotex, edgetotex2;
|
|
matrix_t inv, inv2;
|
|
|
|
TranslateWorldToTex (facenum, worldtotex);
|
|
TranslateWorldToTex (facenum2, worldtotex2);
|
|
|
|
e = &g_dedges[edgenum];
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
vert[i] = &g_dvertexes[e->v[i]];
|
|
ApplyMatrix (worldtotex, vert[i]->point, face_vert[i]);
|
|
face_vert[i][2] = 0; // this value is naturally close to 0 assuming that the edge is on the face plane, but let's make this more explicit.
|
|
ApplyMatrix (worldtotex2, vert[i]->point, face2_vert[i]);
|
|
face2_vert[i][2] = 0;
|
|
}
|
|
|
|
VectorSubtract (face_vert[1], face_vert[0], face_axis[0]);
|
|
len = VectorLength (face_axis[0]);
|
|
CrossProduct (v_up, face_axis[0], face_axis[1]);
|
|
if (CalcMatrixSign (worldtotex) < 0.0) // the three vectors s, t, facenormal are in reverse order
|
|
{
|
|
VectorInverse (face_axis[1]);
|
|
}
|
|
|
|
VectorSubtract (face2_vert[1], face2_vert[0], face2_axis[0]);
|
|
len2 = VectorLength (face2_axis[0]);
|
|
CrossProduct (v_up, face2_axis[0], face2_axis[1]);
|
|
if (CalcMatrixSign (worldtotex2) < 0.0)
|
|
{
|
|
VectorInverse (face2_axis[1]);
|
|
}
|
|
|
|
VectorCopy (face_axis[0], edgetotex.v[0]); // / v[0][0] v[1][0] \ is a rotation (possibly with a reflection by the edge)
|
|
VectorCopy (face_axis[1], edgetotex.v[1]); // \ v[0][1] v[1][1] /
|
|
VectorScale (v_up, len, edgetotex.v[2]); // encode the length into the 3rd value of the matrix
|
|
VectorCopy (face_vert[0], edgetotex.v[3]); // map (0,0) into the origin point
|
|
|
|
VectorCopy (face2_axis[0], edgetotex2.v[0]);
|
|
VectorCopy (face2_axis[1], edgetotex2.v[1]);
|
|
VectorScale (v_up, len2, edgetotex2.v[2]);
|
|
VectorCopy (face2_vert[0], edgetotex2.v[3]);
|
|
|
|
if (!InvertMatrix (edgetotex, inv) || !InvertMatrix (edgetotex2, inv2))
|
|
{
|
|
return false;
|
|
}
|
|
MultiplyMatrix (edgetotex2, inv, m);
|
|
MultiplyMatrix (edgetotex, inv2, m_inverse);
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
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++)
|
|
{
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_SMOOTH_TEXNORMAL
|
|
{
|
|
const dplane_t *fp = getPlaneFromFace (f);
|
|
vec3_t texnormal;
|
|
const texinfo_t *tex = &g_texinfo[f->texinfo];
|
|
CrossProduct (tex->vecs[1], tex->vecs[0], texnormal);
|
|
VectorNormalize (texnormal);
|
|
if (DotProduct (texnormal, fp->normal) < 0)
|
|
{
|
|
VectorSubtract (vec3_origin, texnormal, texnormal);
|
|
}
|
|
VectorCopy (texnormal, g_face_texnormals[i]);
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_EDGESHARE_NOSPECIAL
|
|
if (g_texinfo[f->texinfo].flags & TEX_SPECIAL)
|
|
{
|
|
// special textures don't have lightmaps
|
|
continue;
|
|
}
|
|
#endif
|
|
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
|
|
#ifdef HLRAD_PairEdges_FACESIDE_FIX
|
|
&& e->faces[0]->side == e->faces[1]->side
|
|
#endif
|
|
) {
|
|
e->coplanar = true;
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
VectorCopy(getPlaneFromFace(e->faces[0])->normal, e->interface_normal);
|
|
e->cos_normals_angle = 1.0;
|
|
#endif
|
|
} 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]);
|
|
|
|
#ifdef HLRAD_CUSTOMSMOOTH
|
|
vec_t smoothvalue;
|
|
int m0 = g_texinfo[e->faces[0]->texinfo].miptex;
|
|
int m1 = g_texinfo[e->faces[1]->texinfo].miptex;
|
|
smoothvalue = qmax (g_smoothvalues[m0], g_smoothvalues[m1]);
|
|
if (m0 != m1)
|
|
{
|
|
smoothvalue = qmax (smoothvalue, g_smoothing_threshold_2);
|
|
}
|
|
if (smoothvalue >= 1.0 - NORMAL_EPSILON)
|
|
{
|
|
smoothvalue = 2.0;
|
|
}
|
|
#endif
|
|
if (e->cos_normals_angle > (1.0 - NORMAL_EPSILON))
|
|
{
|
|
e->coplanar = true;
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
VectorCopy(getPlaneFromFace(e->faces[0])->normal, e->interface_normal);
|
|
e->cos_normals_angle = 1.0;
|
|
#endif
|
|
}
|
|
#ifdef HLRAD_CUSTOMSMOOTH
|
|
else if (e->cos_normals_angle >= qmax (smoothvalue - NORMAL_EPSILON, NORMAL_EPSILON))
|
|
{
|
|
#else
|
|
else if (g_smoothing_threshold > 0.0)
|
|
{
|
|
if (e->cos_normals_angle >= g_smoothing_threshold)
|
|
#endif
|
|
{
|
|
VectorAdd(normals[0], normals[1], e->interface_normal);
|
|
VectorNormalize(e->interface_normal);
|
|
}
|
|
}
|
|
}
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (!VectorCompare (g_translucenttextures[g_texinfo[e->faces[0]->texinfo].miptex], g_translucenttextures[g_texinfo[e->faces[1]->texinfo].miptex]))
|
|
{
|
|
e->coplanar = false;
|
|
VectorClear (e->interface_normal);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
{
|
|
int miptex0, miptex1;
|
|
miptex0 = g_texinfo[e->faces[0]->texinfo].miptex;
|
|
miptex1 = g_texinfo[e->faces[1]->texinfo].miptex;
|
|
if (fabs (g_lightingconeinfo[miptex0][0] - g_lightingconeinfo[miptex1][0]) > NORMAL_EPSILON ||
|
|
fabs (g_lightingconeinfo[miptex0][1] - g_lightingconeinfo[miptex1][1]) > NORMAL_EPSILON )
|
|
{
|
|
e->coplanar = false;
|
|
VectorClear (e->interface_normal);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
if (!VectorCompare(e->interface_normal, vec3_origin))
|
|
{
|
|
e->smooth = true;
|
|
}
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_SMOOTH_TEXNORMAL
|
|
if (!GetIntertexnormal (e->faces[0] - g_dfaces, e->faces[1] - g_dfaces))
|
|
{
|
|
e->coplanar = false;
|
|
VectorClear (e->interface_normal);
|
|
e->smooth = false;
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
if (e->smooth)
|
|
{
|
|
// compute the matrix in advance
|
|
if (!TranslateTexToTex (e->faces[0] - g_dfaces, abs (k), e->faces[1] - g_dfaces, e->textotex[0], e->textotex[1]))
|
|
{
|
|
e->smooth = false;
|
|
e->coplanar = false;
|
|
VectorClear (e->interface_normal);
|
|
|
|
dvertex_t *dv = &g_dvertexes[g_dedges[abs(k)].v[0]];
|
|
Developer (DEVELOPER_LEVEL_MEGASPAM, "TranslateTexToTex failed on face %d and %d @(%f,%f,%f)", (int)(e->faces[0] - g_dfaces), (int)(e->faces[1] - g_dfaces), dv->point[0], dv->point[1], dv->point[2]);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
{
|
|
int edgeabs, edgeabsnext;
|
|
int edgeend, edgeendnext;
|
|
int d;
|
|
dface_t *f, *fcurrent, *fnext;
|
|
vec_t angle, angles;
|
|
vec3_t normal, normals;
|
|
vec3_t edgenormal;
|
|
int r, count;
|
|
for (edgeabs = 0; edgeabs < MAX_MAP_EDGES; edgeabs++)
|
|
{
|
|
e = &g_edgeshare[edgeabs];
|
|
if (!e->smooth)
|
|
continue;
|
|
VectorCopy(e->interface_normal, edgenormal);
|
|
if (g_dedges[edgeabs].v[0] == g_dedges[edgeabs].v[1])
|
|
{
|
|
vec3_t errorpos;
|
|
VectorCopy (g_dvertexes[g_dedges[edgeabs].v[0]].point, errorpos);
|
|
VectorAdd (errorpos, g_face_offset[e->faces[0] - g_dfaces], errorpos);
|
|
Developer (DEVELOPER_LEVEL_WARNING, "PairEdges: invalid edge at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);
|
|
VectorCopy(edgenormal, e->vertex_normal[0]);
|
|
VectorCopy(edgenormal, e->vertex_normal[1]);
|
|
}
|
|
else
|
|
{
|
|
const dplane_t *p0 = getPlaneFromFace (e->faces[0]);
|
|
const dplane_t *p1 = getPlaneFromFace (e->faces[1]);
|
|
#ifdef HLRAD_SMOOTH_FACELIST
|
|
intersecttest_t *test0 = CreateIntersectTest (p0, e->faces[0] - g_dfaces);
|
|
intersecttest_t *test1 = CreateIntersectTest (p1, e->faces[1] - g_dfaces);
|
|
#endif
|
|
for (edgeend = 0; edgeend < 2; edgeend++)
|
|
{
|
|
vec3_t errorpos;
|
|
VectorCopy (g_dvertexes[g_dedges[edgeabs].v[edgeend]].point, errorpos);
|
|
VectorAdd (errorpos, g_face_offset[e->faces[0] - g_dfaces], errorpos);
|
|
angles = 0;
|
|
VectorClear (normals);
|
|
|
|
for (d = 0; d < 2; d++)
|
|
{
|
|
f = e->faces[d];
|
|
count = 0, fnext = f, edgeabsnext = edgeabs, edgeendnext = edgeend;
|
|
while (1)
|
|
{
|
|
fcurrent = fnext;
|
|
r = AddFaceForVertexNormal (edgeabsnext, edgeabsnext, edgeendnext, edgeendnext, fcurrent, fnext, angle, normal);
|
|
count++;
|
|
if (r == -1)
|
|
{
|
|
Developer (DEVELOPER_LEVEL_WARNING, "PairEdges: face edges mislink at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);
|
|
break;
|
|
}
|
|
if (count >= 100)
|
|
{
|
|
Developer (DEVELOPER_LEVEL_WARNING, "PairEdges: faces mislink at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);
|
|
break;
|
|
}
|
|
if (DotProduct (normal, p0->normal) <= NORMAL_EPSILON || DotProduct(normal, p1->normal) <= NORMAL_EPSILON)
|
|
break;
|
|
#ifdef HLRAD_CUSTOMSMOOTH
|
|
vec_t smoothvalue;
|
|
int m0 = g_texinfo[f->texinfo].miptex;
|
|
int m1 = g_texinfo[fcurrent->texinfo].miptex;
|
|
smoothvalue = qmax (g_smoothvalues[m0], g_smoothvalues[m1]);
|
|
if (m0 != m1)
|
|
{
|
|
smoothvalue = qmax (smoothvalue, g_smoothing_threshold_2);
|
|
}
|
|
if (smoothvalue >= 1.0 - NORMAL_EPSILON)
|
|
{
|
|
smoothvalue = 2.0;
|
|
}
|
|
if (DotProduct (edgenormal, normal) < qmax (smoothvalue - NORMAL_EPSILON, NORMAL_EPSILON))
|
|
#else
|
|
if (DotProduct (edgenormal, normal) + NORMAL_EPSILON < g_smoothing_threshold)
|
|
#endif
|
|
break;
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_SMOOTH_TEXNORMAL
|
|
if (!GetIntertexnormal (fcurrent - g_dfaces, e->faces[0] - g_dfaces) || !GetIntertexnormal (fcurrent - g_dfaces, e->faces[1] - g_dfaces))
|
|
break;
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_SMOOTH_FACELIST
|
|
if (fcurrent != e->faces[0] && fcurrent != e->faces[1] &&
|
|
(TestFaceIntersect (test0, fcurrent - g_dfaces) || TestFaceIntersect (test1, fcurrent - g_dfaces)))
|
|
{
|
|
Developer (DEVELOPER_LEVEL_WARNING, "Overlapping faces around corner (%f,%f,%f)\n", errorpos[0], errorpos[1], errorpos[2]);
|
|
break;
|
|
}
|
|
#endif
|
|
angles += angle;
|
|
VectorMA(normals, angle, normal, normals);
|
|
#ifdef HLRAD_SMOOTH_FACELIST
|
|
{
|
|
bool in = false;
|
|
if (fcurrent == e->faces[0] || fcurrent == e->faces[1])
|
|
{
|
|
in = true;
|
|
}
|
|
for (facelist_t *l = e->vertex_facelist[edgeend]; l; l = l->next)
|
|
{
|
|
if (fcurrent == l->face)
|
|
{
|
|
in = true;
|
|
}
|
|
}
|
|
if (!in)
|
|
{
|
|
facelist_t *l = (facelist_t *)malloc (sizeof (facelist_t));
|
|
hlassume (l != NULL, assume_NoMemory);
|
|
l->face = fcurrent;
|
|
l->next = e->vertex_facelist[edgeend];
|
|
e->vertex_facelist[edgeend] = l;
|
|
}
|
|
}
|
|
#endif
|
|
if (r != 0 || fnext == f)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (angles < NORMAL_EPSILON)
|
|
{
|
|
VectorCopy(edgenormal, e->vertex_normal[edgeend]);
|
|
Developer (DEVELOPER_LEVEL_WARNING, "PairEdges: no valid faces at (%f,%f,%f)", errorpos[0], errorpos[1], errorpos[2]);
|
|
}
|
|
else
|
|
{
|
|
VectorNormalize(normals);
|
|
VectorCopy(normals, e->vertex_normal[edgeend]);
|
|
}
|
|
}
|
|
#ifdef HLRAD_SMOOTH_FACELIST
|
|
FreeIntersectTest (test0);
|
|
FreeIntersectTest (test1);
|
|
#endif
|
|
}
|
|
if (e->coplanar)
|
|
{
|
|
if (!VectorCompare (e->vertex_normal[0], e->interface_normal) || !VectorCompare (e->vertex_normal[1], e->interface_normal))
|
|
{
|
|
e->coplanar = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#define MAX_SINGLEMAP ((MAX_SURFACE_EXTENT+1)*(MAX_SURFACE_EXTENT+1)) //#define MAX_SINGLEMAP (18*18*4) //--vluzacn
|
|
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
typedef enum
|
|
{
|
|
WALLFLAG_NONE = 0,
|
|
WALLFLAG_NUDGED = 0x1,
|
|
WALLFLAG_BLOCKED = 0x2, // this only happens when the entire face and its surroundings are covered by solid or opaque entities
|
|
WALLFLAG_SHADOWED = 0x4,
|
|
}
|
|
wallflag_t;
|
|
|
|
#endif
|
|
typedef struct
|
|
{
|
|
vec_t* light;
|
|
vec_t facedist;
|
|
vec3_t facenormal;
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
bool translucent_b;
|
|
vec3_t translucent_v;
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
int miptex;
|
|
#endif
|
|
|
|
int numsurfpt;
|
|
vec3_t surfpt[MAX_SINGLEMAP];
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
vec3_t* surfpt_position; //[MAX_SINGLEMAP] // surfpt_position[] are valid positions for light tracing, while surfpt[] are positions for getting phong normal and doing patch interpolation
|
|
int* surfpt_surface; //[MAX_SINGLEMAP] // the face that owns this position
|
|
#endif
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
bool surfpt_lightoutside[MAX_SINGLEMAP];
|
|
#endif
|
|
|
|
vec3_t texorg;
|
|
vec3_t worldtotex[2]; // s = (world - texorg) . worldtotex[0]
|
|
vec3_t textoworld[2]; // world = texorg + s * textoworld[0]
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
vec3_t texnormal;
|
|
#endif
|
|
|
|
vec_t exactmins[2], exactmaxs[2];
|
|
|
|
int texmins[2], texsize[2];
|
|
int lightstyles[256];
|
|
int surfnum;
|
|
dface_t* face;
|
|
#ifdef HLRAD_BLUR
|
|
int lmcache_density; // shared by both s and t direction
|
|
int lmcache_offset; // shared by both s and t direction
|
|
int lmcache_side;
|
|
#ifdef HLRAD_AUTOCORING
|
|
vec3_t (*lmcache)[ALLSTYLES]; // lm: short for lightmap // don't forget to free!
|
|
#ifdef ZHLT_XASH
|
|
vec3_t (*lmcache_direction)[ALLSTYLES];
|
|
#endif
|
|
#else
|
|
vec3_t (*lmcache)[MAXLIGHTMAPS];
|
|
#endif
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
vec3_t *lmcache_normal; // record the phong normals
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
int *lmcache_wallflags; // wallflag_t
|
|
#endif
|
|
int lmcachewidth;
|
|
int lmcacheheight;
|
|
#endif
|
|
}
|
|
lightinfo_t;
|
|
#ifdef HLRAD_MDL_LIGHT_HACK
|
|
#ifndef HLRAD_MDL_LIGHT_HACK_NEW
|
|
typedef struct
|
|
{
|
|
vec3_t texorg;
|
|
vec3_t offset;
|
|
vec3_t textoworld[2];
|
|
vec3_t worldtotex[2];
|
|
int texmins[2], texsize[2];
|
|
}
|
|
facesampleinfo_t;
|
|
static facesampleinfo_t facesampleinfo[MAX_MAP_FACES];
|
|
#endif
|
|
#endif
|
|
|
|
// =====================================================================================
|
|
// 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;
|
|
float mins[2], maxs[2], val; //vec_t mins[2], maxs[2], val; //vluzacn
|
|
int i, j, e;
|
|
dvertex_t* v;
|
|
texinfo_t* tex;
|
|
|
|
s = l->face;
|
|
|
|
#ifdef ZHLT_64BIT_FIX
|
|
mins[0] = mins[1] = 99999999;
|
|
maxs[0] = maxs[1] = -99999999;
|
|
#else
|
|
mins[0] = mins[1] = 999999;
|
|
maxs[0] = maxs[1] = -99999; // a little small, but same with Goldsrc. --vluzacn
|
|
#endif
|
|
|
|
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];
|
|
|
|
#ifndef ZHLT_64BIT_FIX
|
|
mins[i] = floor(mins[i] / TEXTURE_STEP); //mins[i] = floor(mins[i] / 16.0); //--vluzacn
|
|
maxs[i] = ceil(maxs[i] / TEXTURE_STEP); //maxs[i] = ceil(maxs[i] / 16.0); //--vluzacn
|
|
|
|
l->texmins[i] = mins[i];
|
|
l->texsize[i] = maxs[i] - mins[i];
|
|
#endif
|
|
}
|
|
#ifdef ZHLT_64BIT_FIX
|
|
int bmins[2];
|
|
int bmaxs[2];
|
|
GetFaceExtents (l->surfnum, bmins, bmaxs);
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
mins[i] = bmins[i];
|
|
maxs[i] = bmaxs[i];
|
|
l->texmins[i] = bmins[i];
|
|
l->texsize[i] = bmaxs[i] - bmins[i];
|
|
}
|
|
#endif
|
|
|
|
if (!(tex->flags & TEX_SPECIAL))
|
|
{
|
|
if ((l->texsize[0] > MAX_SURFACE_EXTENT) || (l->texsize[1] > MAX_SURFACE_EXTENT)
|
|
|| l->texsize[0] < 0 || l->texsize[1] < 0 //--vluzacn
|
|
)
|
|
{
|
|
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];
|
|
}
|
|
#ifdef HLRAD_OVERWRITEVERTEX_FIX
|
|
vec3_t pos;
|
|
VectorAdd (v->point, g_face_offset[facenum], pos);
|
|
Log ("(%4.3f %4.3f %4.3f) ", pos[0], pos[1], pos[2]);
|
|
#else
|
|
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]);
|
|
#endif
|
|
}
|
|
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]);
|
|
}
|
|
}
|
|
#ifdef HLRAD_BLUR
|
|
// allocate sample light cache
|
|
{
|
|
if (g_extra
|
|
#ifdef HLRAD_FASTMODE
|
|
&& !g_fastmode
|
|
#endif
|
|
)
|
|
{
|
|
l->lmcache_density = 3;
|
|
}
|
|
else
|
|
{
|
|
l->lmcache_density = 1;
|
|
}
|
|
l->lmcache_side = (int)ceil ((0.5 * g_blur * l->lmcache_density - 0.5) * (1 - NORMAL_EPSILON));
|
|
l->lmcache_offset = l->lmcache_side;
|
|
l->lmcachewidth = l->texsize[0] * l->lmcache_density + 1 + 2 * l->lmcache_side;
|
|
l->lmcacheheight = l->texsize[1] * l->lmcache_density + 1 + 2 * l->lmcache_side;
|
|
#ifdef HLRAD_AUTOCORING
|
|
l->lmcache = (vec3_t (*)[ALLSTYLES])malloc (l->lmcachewidth * l->lmcacheheight * sizeof (vec3_t [ALLSTYLES]));
|
|
#ifdef ZHLT_XASH
|
|
l->lmcache_direction = (vec3_t (*)[ALLSTYLES])malloc (l->lmcachewidth * l->lmcacheheight * sizeof (vec3_t [ALLSTYLES]));
|
|
#endif
|
|
#else
|
|
l->lmcache = (vec3_t (*)[MAXLIGHTMAPS])malloc (l->lmcachewidth * l->lmcacheheight * sizeof (vec3_t [MAXLIGHTMAPS]));
|
|
#endif
|
|
hlassume (l->lmcache != NULL, assume_NoMemory);
|
|
#ifdef ZHLT_XASH
|
|
hlassume (l->lmcache_direction != NULL, assume_NoMemory);
|
|
#endif
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
l->lmcache_normal = (vec3_t *)malloc (l->lmcachewidth * l->lmcacheheight * sizeof (vec3_t));
|
|
hlassume (l->lmcache_normal != NULL, assume_NoMemory);
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
l->lmcache_wallflags = (int *)malloc (l->lmcachewidth * l->lmcacheheight * sizeof (int));
|
|
hlassume (l->lmcache_wallflags != NULL, assume_NoMemory);
|
|
#endif
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
l->surfpt_position = (vec3_t *)malloc (MAX_SINGLEMAP * sizeof (vec3_t));
|
|
l->surfpt_surface = (int *)malloc (MAX_SINGLEMAP * sizeof (int));
|
|
hlassume (l->surfpt_position != NULL && l->surfpt_surface != NULL, assume_NoMemory);
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// =====================================================================================
|
|
// 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; x<numpoints; x++)
|
|
{
|
|
VectorAdd(w->m_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;
|
|
|
|
#ifdef ZHLT_FREETEXTUREAXIS
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
CrossProduct (l->worldtotex[!i], l->facenormal, l->textoworld[i]);
|
|
len = DotProduct (l->textoworld[i], l->worldtotex[i]);
|
|
VectorScale (l->textoworld[i], 1 / len, l->textoworld[i]);
|
|
}
|
|
#else
|
|
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]);
|
|
}
|
|
#endif
|
|
|
|
// 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
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
dist = DotProduct(l->texorg, l->facenormal) - l->facedist;
|
|
#else
|
|
dist = DotProduct(l->texorg, l->facenormal) - l->facedist - DEFAULT_HUNT_OFFSET;
|
|
#endif
|
|
dist *= distscale;
|
|
VectorMA(l->texorg, -dist, texnormal, l->texorg);
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
VectorCopy (texnormal, l->texnormal);
|
|
#endif
|
|
|
|
}
|
|
|
|
// =====================================================================================
|
|
// 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);
|
|
}
|
|
|
|
#ifndef HLRAD_CalcPoints_NEW
|
|
// =====================================================================================
|
|
// 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] * TEXTURE_STEP; //starts = (float)l->texmins[0] * 16; //--vluzacn
|
|
startt = (float)l->texmins[1] * TEXTURE_STEP; //startt = (float)l->texmins[1] * 16; //--vluzacn
|
|
|
|
// 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;
|
|
}
|
|
#endif
|
|
|
|
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
|
|
#ifndef HLRAD_NUDGE_VL
|
|
LightSimpleNudgeEmbedded // A nudge either 1 full unit in each of S and T axis, or 1/3 or 2/3 AWAY from center
|
|
#endif
|
|
}
|
|
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
|
|
// =====================================================================================
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
static int PointInFace(const lightinfo_t *l, const vec_t* point)
|
|
{
|
|
int facenum = l->surfnum;
|
|
const dface_t* f = &g_dfaces[facenum];
|
|
Winding *w;
|
|
dplane_t plane;
|
|
VectorCopy (l->texnormal, plane.normal);
|
|
const dplane_t *p = &plane;
|
|
vec3_t new_point;
|
|
VectorSubtract (point, g_face_offset[facenum], new_point);
|
|
w = new Winding (*f);
|
|
if (point_in_winding (*w, *p, new_point))
|
|
{
|
|
delete w;
|
|
return facenum;
|
|
}
|
|
delete w;
|
|
|
|
int j;
|
|
for (j = 0; j < f->numedges; j++)
|
|
{
|
|
int e;
|
|
edgeshare_t *es;
|
|
dface_t* f2;
|
|
e = g_dsurfedges[f->firstedge + j];
|
|
es = &g_edgeshare[abs(e)];
|
|
if (!es->smooth)
|
|
continue;
|
|
f2 = es->faces[!(e<0)];
|
|
const dplane_t *p2 = getPlaneFromFace (f2);
|
|
if (DotProduct (p->normal, p2->normal) < NORMAL_EPSILON)
|
|
continue;
|
|
w = new Winding (*f2);
|
|
if (point_in_winding (*w, *p, new_point))
|
|
{
|
|
delete w;
|
|
return f2 - g_dfaces;
|
|
}
|
|
delete w;
|
|
}
|
|
#ifdef HLRAD_SMOOTH_FACELIST
|
|
for (j = 0; j < f->numedges; j++)
|
|
{
|
|
int e;
|
|
edgeshare_t *es;
|
|
dface_t* f2;
|
|
e = g_dsurfedges[f->firstedge + j];
|
|
es = &g_edgeshare[abs(e)];
|
|
if (!es->smooth)
|
|
continue;
|
|
for (int edgeend = 0; edgeend < 2; edgeend++)
|
|
{
|
|
for (facelist_t *l = es->vertex_facelist[edgeend]; l; l = l->next)
|
|
{
|
|
f2 = l->face;
|
|
const dplane_t *p2 = getPlaneFromFace (f2);
|
|
if (DotProduct (p->normal, p2->normal) < NORMAL_EPSILON)
|
|
continue;
|
|
w = new Winding (*f2);
|
|
if (point_in_winding (*w, *p, new_point))
|
|
{
|
|
delete w;
|
|
return f2 - g_dfaces;
|
|
}
|
|
delete w;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return facenum;
|
|
}
|
|
#endif
|
|
static void SetSTFromSurf(const lightinfo_t* const l, const vec_t* surf, vec_t& s, vec_t& t)
|
|
{
|
|
const int facenum = l->surfnum;
|
|
int j;
|
|
|
|
s = t = 0;
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
s += (surf[j] - g_face_offset[facenum][j] - l->texorg[j]) * l->worldtotex[0][j];
|
|
t += (surf[j] - g_face_offset[facenum][j] - l->texorg[j]) * l->worldtotex[1][j];
|
|
}
|
|
}
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
|
|
typedef struct
|
|
{
|
|
int edgenum; // g_dedges index
|
|
int edgeside;
|
|
int nextfacenum; // where to grow
|
|
bool tried;
|
|
|
|
vec3_t point1; // start point
|
|
vec3_t point2; // end point
|
|
vec3_t direction; // normalized; from point1 to point2
|
|
|
|
bool noseam;
|
|
vec_t distance; // distance from origin
|
|
vec_t distancereduction;
|
|
vec_t flippedangle;
|
|
|
|
vec_t ratio; // if ratio != 1, seam is unavoidable
|
|
matrix_t prevtonext;
|
|
matrix_t nexttoprev;
|
|
}
|
|
samplefragedge_t;
|
|
|
|
typedef struct
|
|
{
|
|
dplane_t planes[4];
|
|
}
|
|
samplefragrect_t;
|
|
|
|
typedef struct samplefrag_s
|
|
{
|
|
samplefrag_s *next; // since this is a node in a list
|
|
samplefrag_s *parentfrag; // where it grew from
|
|
samplefragedge_t *parentedge;
|
|
int facenum; // facenum
|
|
|
|
vec_t flippedangle; // copied from parent edge
|
|
bool noseam; // copied from parent edge
|
|
|
|
matrix_t coordtomycoord; // v[2][2] > 0, v[2][0] = v[2][1] = v[0][2] = v[1][2] = 0.0
|
|
matrix_t mycoordtocoord;
|
|
|
|
vec3_t origin; // original s,t
|
|
vec3_t myorigin; // relative to the texture coordinate on that face
|
|
samplefragrect_t rect; // original rectangle that forms the boundary
|
|
samplefragrect_t myrect; // relative to the texture coordinate on that face
|
|
|
|
Winding *winding; // a fragment of the original rectangle in the texture coordinate plane; windings of different frags should not overlap
|
|
dplane_t windingplane; // normal = (0,0,1) or (0,0,-1); if this normal is wrong, point_in_winding() will never return true
|
|
Winding *mywinding; // relative to the texture coordinate on that face
|
|
dplane_t mywindingplane;
|
|
|
|
int numedges; // # of candicates for the next growth
|
|
samplefragedge_t *edges; // candicates for the next growth
|
|
}
|
|
samplefrag_t;
|
|
|
|
typedef struct
|
|
{
|
|
int maxsize;
|
|
int size;
|
|
samplefrag_t *head;
|
|
}
|
|
samplefraginfo_t;
|
|
|
|
void ChopFrag (samplefrag_t *frag)
|
|
// fill winding, windingplane, mywinding, mywindingplane, numedges, edges
|
|
{
|
|
// get the shape of the fragment by clipping the face using the boundaries
|
|
dface_t *f;
|
|
Winding *facewinding;
|
|
matrix_t worldtotex;
|
|
const vec3_t v_up = {0, 0, 1};
|
|
|
|
f = &g_dfaces[frag->facenum];
|
|
facewinding = new Winding (*f);
|
|
|
|
TranslateWorldToTex (frag->facenum, worldtotex);
|
|
frag->mywinding = new Winding (facewinding->m_NumPoints);
|
|
for (int x = 0; x < facewinding->m_NumPoints; x++)
|
|
{
|
|
ApplyMatrix (worldtotex, facewinding->m_Points[x], frag->mywinding->m_Points[x]);
|
|
frag->mywinding->m_Points[x][2] = 0.0;
|
|
}
|
|
frag->mywinding->RemoveColinearPoints ();
|
|
VectorCopy (v_up, frag->mywindingplane.normal); // this is the same as applying the worldtotex matrix to the faceplane
|
|
if (CalcMatrixSign (worldtotex) < 0.0)
|
|
{
|
|
frag->mywindingplane.normal[2] *= -1;
|
|
}
|
|
frag->mywindingplane.dist = 0.0;
|
|
|
|
for (int x = 0; x < 4 && frag->mywinding->m_NumPoints > 0; x++)
|
|
{
|
|
frag->mywinding->Clip (frag->myrect.planes[x], false);
|
|
}
|
|
|
|
frag->winding = new Winding (frag->mywinding->m_NumPoints);
|
|
for (int x = 0; x < frag->mywinding->m_NumPoints; x++)
|
|
{
|
|
ApplyMatrix (frag->mycoordtocoord, frag->mywinding->m_Points[x], frag->winding->m_Points[x]);
|
|
}
|
|
frag->winding->RemoveColinearPoints ();
|
|
VectorCopy (frag->mywindingplane.normal, frag->windingplane.normal);
|
|
if (CalcMatrixSign (frag->mycoordtocoord) < 0.0)
|
|
{
|
|
frag->windingplane.normal[2] *= -1;
|
|
}
|
|
frag->windingplane.dist = 0.0;
|
|
|
|
delete facewinding;
|
|
|
|
// find the edges where the fragment can grow in the future
|
|
frag->numedges = 0;
|
|
frag->edges = (samplefragedge_t *)malloc (f->numedges * sizeof (samplefragedge_t));
|
|
hlassume (frag->edges != NULL, assume_NoMemory);
|
|
for (int i = 0; i < f->numedges; i++)
|
|
{
|
|
samplefragedge_t *e;
|
|
edgeshare_t *es;
|
|
dedge_t *de;
|
|
dvertex_t *dv1;
|
|
dvertex_t *dv2;
|
|
vec_t frac1, frac2;
|
|
vec_t edgelen;
|
|
vec_t dot, dot1, dot2;
|
|
vec3_t tmp, v, normal;
|
|
const matrix_t *m;
|
|
const matrix_t *m_inverse;
|
|
|
|
e = &frag->edges[frag->numedges];
|
|
|
|
// some basic info
|
|
e->edgenum = abs (g_dsurfedges[f->firstedge + i]);
|
|
e->edgeside = (g_dsurfedges[f->firstedge + i] < 0? 1: 0);
|
|
es = &g_edgeshare[e->edgenum];
|
|
if (!es->smooth)
|
|
{
|
|
continue;
|
|
}
|
|
if (es->faces[e->edgeside] - g_dfaces != frag->facenum)
|
|
{
|
|
Error ("internal error 1 in GrowSingleSampleFrag");
|
|
}
|
|
m = &es->textotex[e->edgeside];
|
|
m_inverse = &es->textotex[1-e->edgeside];
|
|
e->nextfacenum = es->faces[1-e->edgeside] - g_dfaces;
|
|
if (e->nextfacenum == frag->facenum)
|
|
{
|
|
continue; // an invalid edge (usually very short)
|
|
}
|
|
e->tried = false; // because the frag hasn't been linked into the list yet
|
|
|
|
// translate the edge points from world to the texture plane of the original frag
|
|
// so the distances are able to be compared among edges from different frags
|
|
de = &g_dedges[e->edgenum];
|
|
dv1 = &g_dvertexes[de->v[e->edgeside]];
|
|
dv2 = &g_dvertexes[de->v[1-e->edgeside]];
|
|
ApplyMatrix (worldtotex, dv1->point, tmp);
|
|
ApplyMatrix (frag->mycoordtocoord, tmp, e->point1);
|
|
e->point1[2] = 0.0;
|
|
ApplyMatrix (worldtotex, dv2->point, tmp);
|
|
ApplyMatrix (frag->mycoordtocoord, tmp, e->point2);
|
|
e->point2[2] = 0.0;
|
|
VectorSubtract (e->point2, e->point1, e->direction);
|
|
edgelen = VectorNormalize (e->direction);
|
|
if (edgelen <= ON_EPSILON)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// clip the edge
|
|
frac1 = 0;
|
|
frac2 = 1;
|
|
for (int x = 0; x < 4; x++)
|
|
{
|
|
vec_t dot1;
|
|
vec_t dot2;
|
|
|
|
dot1 = DotProduct (e->point1, frag->rect.planes[x].normal) - frag->rect.planes[x].dist;
|
|
dot2 = DotProduct (e->point2, frag->rect.planes[x].normal) - frag->rect.planes[x].dist;
|
|
if (dot1 <= ON_EPSILON && dot2 <= ON_EPSILON)
|
|
{
|
|
frac1 = 1;
|
|
frac2 = 0;
|
|
}
|
|
else if (dot1 < 0)
|
|
{
|
|
frac1 = qmax (frac1, dot1 / (dot1 - dot2));
|
|
}
|
|
else if (dot2 < 0)
|
|
{
|
|
frac2 = qmin (frac2, dot1 / (dot1 - dot2));
|
|
}
|
|
}
|
|
if (edgelen * (frac2 - frac1) <= ON_EPSILON)
|
|
{
|
|
continue;
|
|
}
|
|
VectorMA (e->point1, edgelen * frac2, e->direction, e->point2);
|
|
VectorMA (e->point1, edgelen * frac1, e->direction, e->point1);
|
|
|
|
// calculate the distance, etc., which are used to determine its priority
|
|
e->noseam = frag->noseam;
|
|
dot = DotProduct (frag->origin, e->direction);
|
|
dot1 = DotProduct (e->point1, e->direction);
|
|
dot2 = DotProduct (e->point2, e->direction);
|
|
dot = qmax (dot1, qmin (dot, dot2));
|
|
VectorMA (e->point1, dot - dot1, e->direction, v);
|
|
VectorSubtract (v, frag->origin, v);
|
|
e->distance = VectorLength (v);
|
|
CrossProduct (e->direction, frag->windingplane.normal, normal);
|
|
VectorNormalize (normal); // points inward
|
|
e->distancereduction = DotProduct (v, normal);
|
|
e->flippedangle = frag->flippedangle + acos (qmin (es->cos_normals_angle, 1.0));
|
|
|
|
// calculate the matrix
|
|
e->ratio = (*m_inverse).v[2][2];
|
|
if (e->ratio <= NORMAL_EPSILON || (1 / e->ratio) <= NORMAL_EPSILON)
|
|
{
|
|
Developer (DEVELOPER_LEVEL_SPAM, "TranslateTexToTex failed on face %d and %d @(%f,%f,%f)", frag->facenum, e->nextfacenum, dv1->point[0], dv1->point[1], dv1->point[2]);
|
|
continue;
|
|
}
|
|
|
|
if (fabs (e->ratio - 1) < 0.005)
|
|
{
|
|
e->prevtonext = *m;
|
|
e->nexttoprev = *m_inverse;
|
|
}
|
|
else
|
|
{
|
|
e->noseam = false;
|
|
e->prevtonext = *m;
|
|
e->nexttoprev = *m_inverse;
|
|
}
|
|
|
|
frag->numedges++;
|
|
}
|
|
}
|
|
|
|
static samplefrag_t *GrowSingleFrag (const samplefraginfo_t *info, samplefrag_t *parent, samplefragedge_t *edge)
|
|
{
|
|
samplefrag_t *frag;
|
|
bool overlap;
|
|
int numclipplanes;
|
|
dplane_t *clipplanes;
|
|
|
|
frag = (samplefrag_t *)malloc (sizeof (samplefrag_t));
|
|
hlassume (frag != NULL, assume_NoMemory);
|
|
|
|
// some basic info
|
|
frag->next = NULL;
|
|
frag->parentfrag = parent;
|
|
frag->parentedge = edge;
|
|
frag->facenum = edge->nextfacenum;
|
|
|
|
frag->flippedangle = edge->flippedangle;
|
|
frag->noseam = edge->noseam;
|
|
|
|
// calculate the matrix
|
|
MultiplyMatrix (edge->prevtonext, parent->coordtomycoord, frag->coordtomycoord);
|
|
MultiplyMatrix (parent->mycoordtocoord, edge->nexttoprev, frag->mycoordtocoord);
|
|
|
|
// fill in origin
|
|
VectorCopy (parent->origin, frag->origin);
|
|
ApplyMatrix (frag->coordtomycoord, frag->origin, frag->myorigin);
|
|
|
|
// fill in boundaries
|
|
frag->rect = parent->rect;
|
|
for (int x = 0; x < 4; x++)
|
|
{
|
|
// since a plane's parameters are in the dual coordinate space, we translate the original absolute plane into this relative plane by multiplying the inverse matrix
|
|
ApplyMatrixOnPlane (frag->mycoordtocoord, frag->rect.planes[x].normal, frag->rect.planes[x].dist, frag->myrect.planes[x].normal, frag->myrect.planes[x].dist);
|
|
double len = VectorLength (frag->myrect.planes[x].normal);
|
|
if (!len)
|
|
{
|
|
Developer (DEVELOPER_LEVEL_MEGASPAM, "couldn't translate sample boundaries on face %d", frag->facenum);
|
|
free (frag);
|
|
return NULL;
|
|
}
|
|
VectorScale (frag->myrect.planes[x].normal, 1 / len, frag->myrect.planes[x].normal);
|
|
frag->myrect.planes[x].dist /= len;
|
|
}
|
|
|
|
// chop windings and edges
|
|
ChopFrag (frag);
|
|
|
|
if (frag->winding->m_NumPoints == 0 || frag->mywinding->m_NumPoints == 0)
|
|
{
|
|
// empty
|
|
delete frag->mywinding;
|
|
delete frag->winding;
|
|
free (frag->edges);
|
|
free (frag);
|
|
return NULL;
|
|
}
|
|
|
|
// do overlap test
|
|
|
|
overlap = false;
|
|
clipplanes = (dplane_t *)malloc (frag->winding->m_NumPoints * sizeof (dplane_t));
|
|
hlassume (clipplanes != NULL, assume_NoMemory);
|
|
numclipplanes = 0;
|
|
for (int x = 0; x < frag->winding->m_NumPoints; x++)
|
|
{
|
|
vec3_t v;
|
|
VectorSubtract (frag->winding->m_Points[(x + 1) % frag->winding->m_NumPoints], frag->winding->m_Points[x], v);
|
|
CrossProduct (v, frag->windingplane.normal, clipplanes[numclipplanes].normal);
|
|
if (!VectorNormalize (clipplanes[numclipplanes].normal))
|
|
{
|
|
continue;
|
|
}
|
|
clipplanes[numclipplanes].dist = DotProduct (frag->winding->m_Points[x], clipplanes[numclipplanes].normal);
|
|
numclipplanes++;
|
|
}
|
|
for (samplefrag_t *f2 = info->head; f2 && !overlap; f2 = f2->next)
|
|
{
|
|
Winding *w = new Winding (*f2->winding);
|
|
for (int x = 0; x < numclipplanes && w->m_NumPoints > 0; x++)
|
|
{
|
|
w->Clip (clipplanes[x], false
|
|
#ifdef ZHLT_WINDING_EPSILON
|
|
, 4 * ON_EPSILON
|
|
#endif
|
|
);
|
|
}
|
|
if (w->m_NumPoints > 0)
|
|
{
|
|
overlap = true;
|
|
}
|
|
delete w;
|
|
}
|
|
free (clipplanes);
|
|
if (overlap)
|
|
{
|
|
// in the original texture plane, this fragment overlaps with some existing fragments
|
|
delete frag->mywinding;
|
|
delete frag->winding;
|
|
free (frag->edges);
|
|
free (frag);
|
|
return NULL;
|
|
}
|
|
|
|
return frag;
|
|
}
|
|
|
|
static bool FindBestEdge (samplefraginfo_t *info, samplefrag_t *&bestfrag, samplefragedge_t *&bestedge)
|
|
{
|
|
samplefrag_t *f;
|
|
samplefragedge_t *e;
|
|
bool found;
|
|
|
|
found = false;
|
|
|
|
for (f = info->head; f; f = f->next)
|
|
{
|
|
for (e = f->edges; e < f->edges + f->numedges; e++)
|
|
{
|
|
if (e->tried)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool better;
|
|
|
|
if (!found)
|
|
{
|
|
better = true;
|
|
}
|
|
else if ((e->flippedangle < Q_PI + NORMAL_EPSILON) != (bestedge->flippedangle < Q_PI + NORMAL_EPSILON))
|
|
{
|
|
better = ((e->flippedangle < Q_PI + NORMAL_EPSILON) && !(bestedge->flippedangle < Q_PI + NORMAL_EPSILON));
|
|
}
|
|
else if (e->noseam != bestedge->noseam)
|
|
{
|
|
better = (e->noseam && !bestedge->noseam);
|
|
}
|
|
else if (fabs (e->distance - bestedge->distance) > ON_EPSILON)
|
|
{
|
|
better = (e->distance < bestedge->distance);
|
|
}
|
|
else if (fabs (e->distancereduction - bestedge->distancereduction) > ON_EPSILON)
|
|
{
|
|
better = (e->distancereduction > bestedge->distancereduction);
|
|
}
|
|
else
|
|
{
|
|
better = e->edgenum < bestedge->edgenum;
|
|
}
|
|
|
|
if (better)
|
|
{
|
|
found = true;
|
|
bestfrag = f;
|
|
bestedge = e;
|
|
}
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
static samplefraginfo_t *CreateSampleFrag (int facenum, vec_t s, vec_t t,
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
const vec_t square[2][2],
|
|
#else
|
|
vec_t reach,
|
|
#endif
|
|
int maxsize)
|
|
{
|
|
samplefraginfo_t *info;
|
|
const vec3_t v_s = {1, 0, 0};
|
|
const vec3_t v_t = {0, 1, 0};
|
|
|
|
info = (samplefraginfo_t *)malloc (sizeof (samplefraginfo_t));
|
|
hlassume (info != NULL, assume_NoMemory);
|
|
info->maxsize = maxsize;
|
|
info->size = 1;
|
|
info->head = (samplefrag_t *)malloc (sizeof (samplefrag_t));
|
|
hlassume (info->head != NULL, assume_NoMemory);
|
|
|
|
info->head->next = NULL;
|
|
info->head->parentfrag = NULL;
|
|
info->head->parentedge = NULL;
|
|
info->head->facenum = facenum;
|
|
|
|
info->head->flippedangle = 0.0;
|
|
info->head->noseam = true;
|
|
|
|
MatrixForScale (vec3_origin, 1.0, info->head->coordtomycoord);
|
|
MatrixForScale (vec3_origin, 1.0, info->head->mycoordtocoord);
|
|
|
|
info->head->origin[0] = s;
|
|
info->head->origin[1] = t;
|
|
info->head->origin[2] = 0.0;
|
|
VectorCopy (info->head->origin, info->head->myorigin);
|
|
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
VectorScale (v_s, 1, info->head->rect.planes[0].normal); info->head->rect.planes[0].dist = square[0][0]; // smin
|
|
VectorScale (v_s, -1, info->head->rect.planes[1].normal); info->head->rect.planes[1].dist = -square[1][0]; // smax
|
|
VectorScale (v_t, 1, info->head->rect.planes[2].normal); info->head->rect.planes[2].dist = square[0][1]; // tmin
|
|
VectorScale (v_t, -1, info->head->rect.planes[3].normal); info->head->rect.planes[3].dist = -square[1][1]; // tmax
|
|
#else
|
|
VectorScale (v_s, 1, info->head->rect.planes[0].normal); info->head->rect.planes[0].dist = (s - reach);
|
|
VectorScale (v_s, -1, info->head->rect.planes[1].normal); info->head->rect.planes[1].dist = -(s + reach);
|
|
VectorScale (v_t, 1, info->head->rect.planes[2].normal); info->head->rect.planes[2].dist = (t - reach);
|
|
VectorScale (v_t, -1, info->head->rect.planes[3].normal); info->head->rect.planes[3].dist = -(t + reach);
|
|
#endif
|
|
info->head->myrect = info->head->rect;
|
|
|
|
ChopFrag (info->head);
|
|
|
|
if (info->head->winding->m_NumPoints == 0 || info->head->mywinding->m_NumPoints == 0)
|
|
{
|
|
// empty
|
|
delete info->head->mywinding;
|
|
delete info->head->winding;
|
|
free (info->head->edges);
|
|
free (info->head);
|
|
info->head = NULL;
|
|
info->size = 0;
|
|
}
|
|
else
|
|
{
|
|
// prune edges
|
|
for (samplefragedge_t *e = info->head->edges; e < info->head->edges + info->head->numedges; e++)
|
|
{
|
|
if (e->nextfacenum == info->head->facenum)
|
|
{
|
|
e->tried = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (info->size < info->maxsize)
|
|
{
|
|
samplefrag_t *bestfrag;
|
|
samplefragedge_t *bestedge;
|
|
samplefrag_t *newfrag;
|
|
|
|
if (!FindBestEdge (info, bestfrag, bestedge))
|
|
{
|
|
break;
|
|
}
|
|
|
|
newfrag = GrowSingleFrag (info, bestfrag, bestedge);
|
|
bestedge->tried = true;
|
|
|
|
if (newfrag)
|
|
{
|
|
newfrag->next = info->head;
|
|
info->head = newfrag;
|
|
info->size++;
|
|
|
|
for (samplefrag_t *f = info->head; f; f = f->next)
|
|
{
|
|
for (samplefragedge_t *e = newfrag->edges; e < newfrag->edges + newfrag->numedges; e++)
|
|
{
|
|
if (e->nextfacenum == f->facenum)
|
|
{
|
|
e->tried = true;
|
|
}
|
|
}
|
|
}
|
|
for (samplefrag_t *f = info->head; f; f = f->next)
|
|
{
|
|
for (samplefragedge_t *e = f->edges; e < f->edges + f->numedges; e++)
|
|
{
|
|
if (e->nextfacenum == newfrag->facenum)
|
|
{
|
|
e->tried = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
static bool IsFragEmpty (samplefraginfo_t *fraginfo)
|
|
{
|
|
return (fraginfo->size == 0);
|
|
}
|
|
|
|
static void DeleteSampleFrag (samplefraginfo_t *fraginfo)
|
|
{
|
|
while (fraginfo->head)
|
|
{
|
|
samplefrag_t *f;
|
|
|
|
f = fraginfo->head;
|
|
fraginfo->head = f->next;
|
|
delete f->mywinding;
|
|
delete f->winding;
|
|
free (f->edges);
|
|
free (f);
|
|
}
|
|
free (fraginfo);
|
|
}
|
|
|
|
#endif
|
|
static light_flag_t SetSampleFromST(vec_t* const point,
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
vec_t* const position, // a valid world position for light tracing
|
|
int* const surface, // the face used for phong normal and patch interpolation
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
bool *nudged,
|
|
#endif
|
|
const lightinfo_t* const l, const vec_t original_s, const vec_t original_t,
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
const vec_t square[2][2], // {smin, tmin}, {smax, tmax}
|
|
#else
|
|
const vec_t reach, // the size of the square that grows
|
|
#endif
|
|
#endif
|
|
eModelLightmodes lightmode)
|
|
{
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
light_flag_t LuxelFlag;
|
|
int facenum;
|
|
dface_t *face;
|
|
const dplane_t* faceplane;
|
|
samplefraginfo_t *fraginfo;
|
|
samplefrag_t *f;
|
|
|
|
facenum = l->surfnum;
|
|
face = l->face;
|
|
faceplane = getPlaneFromFace (face);
|
|
|
|
fraginfo = CreateSampleFrag (facenum, original_s, original_t,
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
square,
|
|
#else
|
|
reach,
|
|
#endif
|
|
100);
|
|
|
|
bool found;
|
|
samplefrag_t *bestfrag;
|
|
vec3_t bestpos;
|
|
vec_t bests, bestt;
|
|
vec_t best_dist;
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
bool best_nudged;
|
|
#endif
|
|
|
|
found = false;
|
|
for (f = fraginfo->head; f; f = f->next)
|
|
{
|
|
vec3_t pos;
|
|
vec_t s, t;
|
|
vec_t dist;
|
|
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
bool nudged_one;
|
|
#endif
|
|
if (!FindNearestPosition (f->facenum, f->mywinding, f->mywindingplane, f->myorigin[0], f->myorigin[1], pos, &s, &t, &dist
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
, &nudged_one
|
|
#endif
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bool better;
|
|
|
|
if (!found)
|
|
{
|
|
better = true;
|
|
}
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
else if (nudged_one != best_nudged)
|
|
{
|
|
better = !nudged_one;
|
|
}
|
|
#endif
|
|
else if (fabs (dist - best_dist) > 2 * ON_EPSILON)
|
|
{
|
|
better = (dist < best_dist);
|
|
}
|
|
else if (f->noseam != bestfrag->noseam)
|
|
{
|
|
better = (f->noseam && !bestfrag->noseam);
|
|
}
|
|
else
|
|
{
|
|
better = (f->facenum < bestfrag->facenum);
|
|
}
|
|
|
|
if (better)
|
|
{
|
|
found = true;
|
|
bestfrag = f;
|
|
VectorCopy (pos, bestpos);
|
|
bests = s;
|
|
bestt = t;
|
|
best_dist = dist;
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
best_nudged = nudged_one;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
matrix_t worldtotex, textoworld;
|
|
vec3_t tex;
|
|
|
|
TranslateWorldToTex (bestfrag->facenum, worldtotex);
|
|
if (!InvertMatrix (worldtotex, textoworld))
|
|
{
|
|
const unsigned facenum = bestfrag->facenum;
|
|
ThreadLock ();
|
|
Log ("Malformed face (%d) normal @ \n", facenum);
|
|
Winding* w = new Winding (g_dfaces[facenum]);
|
|
for (int x = 0; x < w->m_NumPoints; x++)
|
|
{
|
|
VectorAdd (w->m_Points[x], g_face_offset[facenum], w->m_Points[x]);
|
|
}
|
|
w->Print ();
|
|
delete w;
|
|
ThreadUnlock ();
|
|
hlassume (false, assume_MalformedTextureFace);
|
|
}
|
|
|
|
// point
|
|
tex[0] = bests;
|
|
tex[1] = bestt;
|
|
tex[2] = 0.0;
|
|
{vec3_t v; ApplyMatrix (textoworld, tex, v); VectorCopy (v, point);}
|
|
VectorAdd (point, g_face_offset[bestfrag->facenum], point);
|
|
// position
|
|
VectorCopy (bestpos, position);
|
|
// surface
|
|
*surface = bestfrag->facenum;
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
// whether nudged to fit
|
|
*nudged = best_nudged;
|
|
#endif
|
|
// returned value
|
|
LuxelFlag = LightNormal;
|
|
}
|
|
else
|
|
{
|
|
SetSurfFromST (l, point, original_s, original_t);
|
|
VectorMA (point, DEFAULT_HUNT_OFFSET, faceplane->normal, position);
|
|
*surface = facenum;
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
*nudged = true;
|
|
#endif
|
|
LuxelFlag = LightOutside;
|
|
}
|
|
|
|
DeleteSampleFrag (fraginfo);
|
|
|
|
return LuxelFlag;
|
|
|
|
#else
|
|
light_flag_t LuxelFlag = LightOutside;
|
|
int huntsize = 3;
|
|
vec_t huntscale = 0.2;
|
|
vec_t width = DEFAULT_EDGE_WIDTH;
|
|
|
|
int facenum = l->surfnum;
|
|
const vec_t* face_delta = g_face_offset[facenum];
|
|
|
|
const dface_t* f = &g_dfaces[facenum];
|
|
const dplane_t* p = getPlaneFromFace (f);
|
|
Winding *wd = new Winding (*f);
|
|
{
|
|
int j;
|
|
for (j = 0; j < wd->m_NumPoints; j++)
|
|
{
|
|
VectorAdd (wd->m_Points[j], face_delta, wd->m_Points[j]);
|
|
}
|
|
}
|
|
|
|
const vec_t* face_centroid = g_face_centroids[facenum];
|
|
vec_t mids, midt;
|
|
SetSTFromSurf (l, face_centroid, mids, midt);
|
|
|
|
vec3_t surf_original;
|
|
dleaf_t* leaf_original;
|
|
SetSurfFromST (l, surf_original, original_s, original_t);
|
|
leaf_original = HuntForWorld (surf_original, face_delta, p, 1, 0.0, DEFAULT_HUNT_OFFSET);
|
|
|
|
int facenum_tosnap = PointInFace (l, surf_original);
|
|
const dface_t* f_tosnap = &g_dfaces[facenum_tosnap];
|
|
const dplane_t* p_tosnap = getPlaneFromFace (f_tosnap);
|
|
#ifdef HLRAD_SMOOTH_TEXNORMAL
|
|
vec3_t snapdir;
|
|
if (!GetIntertexnormal (facenum, facenum_tosnap, snapdir))
|
|
{
|
|
facenum_tosnap = facenum;
|
|
f_tosnap = f;
|
|
p_tosnap = p;
|
|
}
|
|
#endif
|
|
|
|
vec3_t surf_direct;
|
|
dleaf_t* leaf_direct;
|
|
VectorCopy (surf_original, surf_direct);
|
|
{
|
|
vec_t dist;
|
|
vec_t scale;
|
|
#ifdef HLRAD_SMOOTH_TEXNORMAL
|
|
scale = DotProduct (snapdir, p_tosnap->normal);
|
|
#else
|
|
scale = DotProduct (l->texnormal, p_tosnap->normal);
|
|
#endif
|
|
dist = DotProduct (surf_direct, p_tosnap->normal) - DotProduct (face_delta, p_tosnap->normal) - p_tosnap->dist - DEFAULT_HUNT_OFFSET;
|
|
#ifdef HLRAD_SMOOTH_TEXNORMAL
|
|
VectorMA (surf_direct, - dist / scale, snapdir, surf_direct);
|
|
#else
|
|
VectorMA (surf_direct, - dist / scale, l->texnormal, surf_direct);
|
|
#endif
|
|
}
|
|
leaf_direct = HuntForWorld (surf_direct, face_delta, p_tosnap, huntsize, huntscale, DEFAULT_HUNT_OFFSET);
|
|
|
|
if (LuxelFlag == LightOutside)
|
|
{
|
|
if (leaf_direct && point_in_winding_noedge (*wd, *p, surf_direct, width))
|
|
{
|
|
LuxelFlag = LightNormal;
|
|
VectorCopy (surf_direct, point);
|
|
}
|
|
}
|
|
|
|
if (LuxelFlag == LightOutside)
|
|
{
|
|
bool blocked_direct;
|
|
bool blocked_inwinding;
|
|
bool blocked_inwinding_noedge;
|
|
vec3_t surf_inwinding;
|
|
vec3_t surf_inwinding_noedge;
|
|
dleaf_t*leaf_inwinding;
|
|
dleaf_t*leaf_inwinding_noedge;
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency = { 1.0, 1.0, 1.0 };
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
int opaquestyle;
|
|
#endif
|
|
{
|
|
blocked_direct = (leaf_direct == NULL);
|
|
if (!point_in_winding (*wd, *p, surf_original))
|
|
{
|
|
VectorCopy (surf_original, surf_inwinding);
|
|
snap_to_winding (*wd, *p, surf_inwinding);
|
|
leaf_inwinding = HuntForWorld (surf_inwinding, face_delta, p, huntsize, huntscale, DEFAULT_HUNT_OFFSET);
|
|
if ( blocked_direct
|
|
|| !leaf_inwinding
|
|
|| TestLine (surf_direct, surf_inwinding) != CONTENTS_EMPTY
|
|
|| TestSegmentAgainstOpaqueList (surf_direct, surf_inwinding
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
, opaquestyle
|
|
#endif
|
|
) == true
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
|| opaquestyle != -1
|
|
#endif
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
|| l->translucent_b
|
|
#endif
|
|
)
|
|
{
|
|
blocked_direct = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (surf_original, surf_inwinding);
|
|
leaf_inwinding = leaf_original;
|
|
}
|
|
blocked_inwinding = (leaf_inwinding == NULL);
|
|
if (!point_in_winding_noedge (*wd, *p, surf_inwinding, width))
|
|
{
|
|
VectorCopy (surf_inwinding, surf_inwinding_noedge);
|
|
snap_to_winding_noedge (*wd, *p, surf_inwinding_noedge, width, 4 * width);
|
|
leaf_inwinding_noedge = HuntForWorld (surf_inwinding_noedge, face_delta, p, huntsize, huntscale, DEFAULT_HUNT_OFFSET);
|
|
if ( blocked_inwinding
|
|
|| !leaf_inwinding_noedge
|
|
|| TestLine (surf_inwinding, surf_inwinding_noedge) != CONTENTS_EMPTY
|
|
|| TestSegmentAgainstOpaqueList (surf_inwinding, surf_inwinding_noedge
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
, opaquestyle
|
|
#endif
|
|
) == true
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
|| opaquestyle != -1
|
|
#endif
|
|
)
|
|
{
|
|
blocked_inwinding = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VectorCopy (surf_inwinding, surf_inwinding_noedge);
|
|
leaf_inwinding_noedge = leaf_inwinding;
|
|
}
|
|
blocked_inwinding_noedge = (leaf_inwinding_noedge == NULL);
|
|
if (blocked_inwinding_noedge == true)
|
|
{
|
|
blocked_inwinding = true;
|
|
}
|
|
if (blocked_inwinding == true)
|
|
{
|
|
blocked_direct = true;
|
|
}
|
|
}
|
|
if (!blocked_direct)
|
|
{
|
|
LuxelFlag = LightNormal;
|
|
VectorCopy (surf_direct, point);
|
|
}
|
|
else if (!blocked_inwinding)
|
|
{
|
|
LuxelFlag = LightPulledInside;
|
|
VectorCopy (surf_inwinding, point);
|
|
}
|
|
else if (!blocked_inwinding_noedge)
|
|
{
|
|
LuxelFlag = LightPulledInside;
|
|
VectorCopy (surf_inwinding_noedge, point);
|
|
}
|
|
}
|
|
|
|
if (LuxelFlag == LightOutside)
|
|
{
|
|
// this part is very slow
|
|
const int numnudges = 13;
|
|
vec_t nudgelist[numnudges][2] = {{0,0},{0.6,0},{0,0.6},{-0.6,0},{0,-0.6},{1.1,1.1},{1.1,-1.1},{-1.1,1.1},{-1.1,-1.1},{1.6,0},{0,1.6},{-1.6,0},{0,-1.6}};
|
|
vec_t nudgescale_s, nudgescale_t;
|
|
nudgescale_s = original_s <= mids? TEXTURE_STEP: -TEXTURE_STEP;
|
|
nudgescale_t = original_t <= midt? TEXTURE_STEP: -TEXTURE_STEP;
|
|
int i;
|
|
for (i = 0; i < numnudges; i++)
|
|
{
|
|
vec_t s1 = original_s + nudgelist[i][0] * nudgescale_s;
|
|
vec_t t1 = original_t + nudgelist[i][1] * nudgescale_t;
|
|
vec3_t surf;
|
|
SetSurfFromST(l, surf, s1, t1);
|
|
if (point_in_winding (*wd, *p, surf) && HuntForWorld (surf, face_delta, p, 2, 0.5, DEFAULT_HUNT_OFFSET) && point_in_winding (*wd, *p, surf))
|
|
{
|
|
LuxelFlag = LightSimpleNudge;
|
|
VectorCopy (surf, point);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (LuxelFlag == LightOutside)
|
|
{
|
|
VectorCopy (surf_original, point);
|
|
}
|
|
|
|
delete wd;
|
|
return LuxelFlag;
|
|
#endif
|
|
}
|
|
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 int h = l->texsize[1] + 1;
|
|
const int w = l->texsize[0] + 1;
|
|
const vec_t starts = l->texmins[0] * TEXTURE_STEP;
|
|
const vec_t startt = l->texmins[1] * TEXTURE_STEP;
|
|
light_flag_t LuxelFlags[MAX_SINGLEMAP];
|
|
light_flag_t* pLuxelFlags;
|
|
vec_t us, ut;
|
|
vec_t* surf;
|
|
int s, t;
|
|
l->numsurfpt = w * h;
|
|
for (t = 0; t < h; t++)
|
|
{
|
|
for (s = 0; s < w; s++)
|
|
{
|
|
surf = l->surfpt[s+w*t];
|
|
pLuxelFlags = &LuxelFlags[s+w*t];
|
|
us = starts + s * TEXTURE_STEP;
|
|
ut = startt + t * TEXTURE_STEP;
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
vec_t square[2][2];
|
|
square[0][0] = us - TEXTURE_STEP;
|
|
square[0][1] = ut - TEXTURE_STEP;
|
|
square[1][0] = us + TEXTURE_STEP;
|
|
square[1][1] = ut + TEXTURE_STEP;
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
bool nudged;
|
|
#endif
|
|
*pLuxelFlags = SetSampleFromST (surf,
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
l->surfpt_position[s+w*t], &l->surfpt_surface[s+w*t],
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
&nudged,
|
|
#endif
|
|
l, us, ut,
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
square,
|
|
#else
|
|
TEXTURE_STEP,
|
|
#endif
|
|
#endif
|
|
lightmode);
|
|
}
|
|
}
|
|
{
|
|
int i, n;
|
|
int s_other, t_other;
|
|
light_flag_t* pLuxelFlags_other;
|
|
vec_t* surf_other;
|
|
bool adjusted;
|
|
for (i = 0; i < h + w; i++)
|
|
{ // propagate valid light samples
|
|
adjusted = false;
|
|
for (t = 0; t < h; t++)
|
|
{
|
|
for (s = 0; s < w; s++)
|
|
{
|
|
surf = l->surfpt[s+w*t];
|
|
pLuxelFlags = &LuxelFlags[s+w*t];
|
|
if (*pLuxelFlags != LightOutside)
|
|
continue;
|
|
for (n = 0; n < 4; n++)
|
|
{
|
|
switch (n)
|
|
{
|
|
case 0: s_other = s + 1; t_other = t; break;
|
|
case 1: s_other = s - 1; t_other = t; break;
|
|
case 2: s_other = s; t_other = t + 1; break;
|
|
case 3: s_other = s; t_other = t - 1; break;
|
|
}
|
|
if (t_other < 0 || t_other >= h || s_other < 0 || s_other >= w)
|
|
continue;
|
|
surf_other = l->surfpt[s_other+w*t_other];
|
|
pLuxelFlags_other = &LuxelFlags[s_other+w*t_other];
|
|
if (*pLuxelFlags_other != LightOutside && *pLuxelFlags_other != LightShifted)
|
|
{
|
|
*pLuxelFlags = LightShifted;
|
|
VectorCopy (surf_other, surf);
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
VectorCopy (l->surfpt_position[s_other+w*t_other], l->surfpt_position[s+w*t]);
|
|
l->surfpt_surface[s+w*t] = l->surfpt_surface[s_other+w*t_other];
|
|
#endif
|
|
adjusted = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (t = 0; t < h; t++)
|
|
{
|
|
for (s = 0; s < w; s++)
|
|
{
|
|
pLuxelFlags = &LuxelFlags[s+w*t];
|
|
if (*pLuxelFlags == LightShifted)
|
|
{
|
|
*pLuxelFlags = LightShiftedInside;
|
|
}
|
|
}
|
|
}
|
|
if (!adjusted)
|
|
break;
|
|
}
|
|
}
|
|
for (int i = 0; i < MAX_SINGLEMAP; i++)
|
|
{
|
|
l->surfpt_lightoutside[i] = (LuxelFlags[i] == LightOutside);
|
|
}
|
|
}
|
|
#else /*HLRAD_CalcPoints_NEW*/
|
|
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];
|
|
|
|
#ifdef HLRAD_NUDGE_VL
|
|
vec_t mids, midt;
|
|
{
|
|
// use winding center instead
|
|
vec3_t surf;
|
|
VectorSubtract (g_face_centroids[facenum], g_face_offset[facenum], surf);
|
|
VectorSubtract (surf, l->texorg, surf);
|
|
mids = DotProduct (surf, l->worldtotex[0]);
|
|
midt = DotProduct (surf, l->worldtotex[1]);
|
|
}
|
|
#else
|
|
const vec_t mids = (l->exactmaxs[0] + l->exactmins[0]) / 2;
|
|
const vec_t midt = (l->exactmaxs[1] + l->exactmins[1]) / 2;
|
|
#endif
|
|
|
|
const int h = l->texsize[1] + 1;
|
|
const int w = l->texsize[0] + 1;
|
|
|
|
const vec_t starts = (l->texmins[0] * TEXTURE_STEP); //const vec_t starts = (l->texmins[0] * 16); //--vluzacn
|
|
const vec_t startt = (l->texmins[1] * TEXTURE_STEP); //const vec_t startt = (l->texmins[1] * 16); //--vluzacn
|
|
|
|
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;
|
|
}
|
|
#ifdef HLRAD_NUDGE_VL
|
|
else if (SimpleNudge(surf, l, &us, &ut, TEXTURE_STEP))
|
|
{
|
|
*pLuxelFlags = LightSimpleNudge;
|
|
}
|
|
else if (SimpleNudge(surf, l, &us, &ut, -TEXTURE_STEP))
|
|
{
|
|
*pLuxelFlags = LightSimpleNudge;
|
|
}
|
|
#else
|
|
// 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;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef HLRAD_NUDGE_VL
|
|
if (!(lightmode & eModelLightmodeEmbedded))
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_NUDGE_VL
|
|
// HLRAD_NUDGE_VL: only pull when light is blocked AND point is outside face.
|
|
vec3_t surf_nopull;
|
|
vec_t us_nopull = us, ut_nopull = ut;
|
|
Winding *wd = new Winding (*f);
|
|
int j;
|
|
for (j = 0; j < wd->m_NumPoints; j++)
|
|
{
|
|
VectorAdd (wd->m_Points[j], face_delta, wd->m_Points[j]);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_SNAPTOWINDING
|
|
bool nudge_succeeded = false;
|
|
SetSurfFromST(l, surf, us, ut);
|
|
leaf_surf = HuntForWorld(surf, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET);
|
|
if (leaf_surf && point_in_winding_noedge (*wd, *p, surf, 1.0))
|
|
{
|
|
*pLuxelFlags = LightNormal;
|
|
nudge_succeeded = true;
|
|
}
|
|
else
|
|
{
|
|
SetSurfFromST(l, surf, us, ut);
|
|
snap_to_winding (*wd, *p, surf);
|
|
if (lightmode & eModelLightmodeConcave)
|
|
{
|
|
VectorScale (surf, 0.99, surf);
|
|
VectorMA (surf, 0.01, g_face_centroids[facenum], surf);
|
|
}
|
|
leaf_surf = HuntForWorld(surf, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE, DEFAULT_HUNT_OFFSET);
|
|
if (leaf_surf)
|
|
{
|
|
*pLuxelFlags = LightPulledInside;
|
|
nudge_succeeded = true;
|
|
}
|
|
}
|
|
|
|
#else
|
|
// Pull the sample points towards the facemid if visibility is blocked
|
|
// and the facemid is inside the world
|
|
#ifdef HLRAD_NUDGE_SMALLSTEP
|
|
int nudge_divisor = 4 * qmax(qmax(w, h), 4);
|
|
#else
|
|
int nudge_divisor = qmax(qmax(w, h), 4);
|
|
#endif
|
|
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;
|
|
|
|
// 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)
|
|
#ifndef HLRAD_NUDGE_VL
|
|
if (leaf_mid)
|
|
{
|
|
#endif
|
|
SetSurfFromST(l, surf, us, ut);
|
|
leaf_surf =
|
|
HuntForWorld(surf, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE,
|
|
DEFAULT_HUNT_OFFSET);
|
|
|
|
if (leaf_surf)
|
|
{
|
|
#ifdef HLRAD_NUDGE_VL
|
|
if (point_in_winding_noedge (*wd, *p, surf, 1.0))
|
|
{
|
|
#else
|
|
if (TestLine(surface_midpoint, surf) == CONTENTS_EMPTY)
|
|
{
|
|
if (lightmode & eModelLightmodeConcave)
|
|
{
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency = { 1.0, 1.0, 1.0 };
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
int opaquestyle;
|
|
#endif
|
|
if (TestSegmentAgainstOpaqueList(surface_midpoint, surf
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
, opaquestyle
|
|
#endif
|
|
)
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
|| opaquestyle != -1
|
|
#endif
|
|
)
|
|
{
|
|
Log("SDF::4\n");
|
|
us += nudge_s;
|
|
ut += nudge_t;
|
|
continue; // Try nudge again, we hit an opaque face
|
|
}
|
|
}
|
|
#endif
|
|
if (i)
|
|
{
|
|
*pLuxelFlags = LightPulledInside;
|
|
}
|
|
else
|
|
{
|
|
*pLuxelFlags = LightNormal;
|
|
}
|
|
nudge_succeeded = true;
|
|
break;
|
|
}
|
|
}
|
|
#ifndef HLRAD_NUDGE_VL
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
us += nudge_s;
|
|
ut += nudge_t;
|
|
}
|
|
#endif /*HLRAD_SNAPTOWINDING*/
|
|
|
|
if (!nudge_succeeded)
|
|
{
|
|
SetSurfFromST(l, surf, original_s, original_t);
|
|
*pLuxelFlags = LightOutside;
|
|
}
|
|
#ifdef HLRAD_NUDGE_VL
|
|
delete wd;
|
|
if (*pLuxelFlags == LightPulledInside)
|
|
{
|
|
SetSurfFromST(l, surf_nopull, us_nopull, ut_nopull);
|
|
leaf_surf =
|
|
HuntForWorld(surf_nopull, face_delta, p, DEFAULT_HUNT_SIZE, DEFAULT_HUNT_SCALE,
|
|
DEFAULT_HUNT_OFFSET);
|
|
if (leaf_surf)
|
|
{
|
|
if (TestLine(surf, surf_nopull) == CONTENTS_EMPTY)
|
|
{
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency = { 1.0, 1.0, 1.0 };
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
int opaquestyle;
|
|
#endif
|
|
if (!TestSegmentAgainstOpaqueList(surf, surf_nopull
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
, opaquestyle
|
|
#endif
|
|
)
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
&& opaquestyle == -1
|
|
#endif
|
|
)
|
|
{
|
|
*pLuxelFlags = LightNormal;
|
|
VectorCopy (surf_nopull, surf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 = qmax(0, s - 1);
|
|
int x_max = qmin(w, s + 1);
|
|
int y_min = qmax(0, t - 1);
|
|
int y_max = qmin(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
|
|
}
|
|
}
|
|
#endif /*HLRAD_CalcPoints_NEW*/
|
|
|
|
//==============================================================
|
|
|
|
typedef struct
|
|
{
|
|
vec3_t pos;
|
|
vec3_t light;
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
int surface; // this sample can grow into another face
|
|
#endif
|
|
#ifdef ZHLT_XASH
|
|
// this increases the maximum (at 100% AllocBlock, 4 light styles) possible usage of memory of all light samples from 100MB to 200MB
|
|
vec3_t light_direction; // sum of light direction * light contribution (rgb averaged)
|
|
vec3_t normal; // phong normal
|
|
#endif
|
|
}
|
|
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;
|
|
|
|
#ifndef HLRAD_REFLECTIVITY
|
|
#define DIRECT_SCALE 0.1f
|
|
#endif
|
|
|
|
// =====================================================================================
|
|
// 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;
|
|
|
|
#ifndef HLRAD_CUSTOMTEXLIGHT
|
|
// AJM: coplaner lighting
|
|
vec3_t temp_normal;
|
|
#endif
|
|
|
|
numdlights = 0;
|
|
#ifdef HLRAD_STYLEREPORT
|
|
int styleused[ALLSTYLES];
|
|
memset (styleused, 0, ALLSTYLES * sizeof(styleused[0]));
|
|
styleused[0] = true;
|
|
int numstyles = 1;
|
|
#endif
|
|
|
|
//
|
|
// surfaces
|
|
//
|
|
for (i = 0, p = g_patches; i < g_num_patches; i++, p++)
|
|
{
|
|
#ifdef ZHLT_TEXLIGHT
|
|
#ifdef HLRAD_STYLEREPORT
|
|
if (p->emitstyle >= 0 && p->emitstyle < ALLSTYLES)
|
|
{
|
|
if (styleused[p->emitstyle] == false)
|
|
{
|
|
styleused[p->emitstyle] = true;
|
|
numstyles++;
|
|
}
|
|
}
|
|
#endif
|
|
if (
|
|
#ifdef HLRAD_REFLECTIVITY
|
|
DotProduct (p->baselight, p->texturereflectivity) / 3
|
|
#else
|
|
VectorAvg(p->baselight)
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTTHRESHOLD_FIX
|
|
> 0.0
|
|
#else
|
|
>= g_dlight_threshold
|
|
#endif
|
|
#ifdef HLRAD_CUSTOMTEXLIGHT
|
|
&& !(g_face_texlights[p->faceNumber]
|
|
&& *ValueForKey (g_face_texlights[p->faceNumber], "_scale")
|
|
&& FloatForKey (g_face_texlights[p->faceNumber], "_scale") <= 0)
|
|
#endif
|
|
) //LRC
|
|
#else
|
|
if (
|
|
#ifdef HLRAD_REFLECTIVITY
|
|
DotProduct (p->totallight, p->texturereflectivity) / 3
|
|
#else
|
|
VectorAvg(p->totallight)
|
|
#endif
|
|
>= g_dlight_threshold
|
|
)
|
|
#endif
|
|
{
|
|
numdlights++;
|
|
dl = (directlight_t*)calloc(1, sizeof(directlight_t));
|
|
#ifdef HLRAD_HLASSUMENOMEMORY
|
|
hlassume (dl != NULL, assume_NoMemory);
|
|
#endif
|
|
|
|
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
|
|
#ifdef HLRAD_GatherPatchLight
|
|
dl->topatch = false;
|
|
#ifdef HLRAD_TEXLIGHTTHRESHOLD_FIX
|
|
if (!p->emitmode)
|
|
{
|
|
dl->topatch = true;
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_FASTMODE
|
|
if (g_fastmode)
|
|
{
|
|
dl->topatch = true;
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHT_SPOTS_FIX
|
|
dl->patch_area = p->area;
|
|
#ifdef HLRAD_ACCURATEBOUNCE_TEXLIGHT
|
|
dl->patch_emitter_range = p->emitter_range;
|
|
dl->patch = p;
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
dl->texlightgap = g_texlightgap;
|
|
#ifdef HLRAD_CUSTOMTEXLIGHT
|
|
if (g_face_texlights[p->faceNumber] && *ValueForKey (g_face_texlights[p->faceNumber], "_texlightgap"))
|
|
{
|
|
dl->texlightgap = FloatForKey (g_face_texlights[p->faceNumber], "_texlightgap");
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_CUSTOMTEXLIGHT
|
|
dl->stopdot = 0.0;
|
|
dl->stopdot2 = 0.0;
|
|
if (g_face_texlights[p->faceNumber])
|
|
{
|
|
if (*ValueForKey (g_face_texlights[p->faceNumber], "_cone"))
|
|
{
|
|
dl->stopdot = FloatForKey (g_face_texlights[p->faceNumber], "_cone");
|
|
dl->stopdot = dl->stopdot >= 90? 0: (float)cos (dl->stopdot / 180 * Q_PI);
|
|
}
|
|
if (*ValueForKey (g_face_texlights[p->faceNumber], "_cone2"))
|
|
{
|
|
dl->stopdot2 = FloatForKey (g_face_texlights[p->faceNumber], "_cone2");
|
|
dl->stopdot2 = dl->stopdot2 >= 90? 0: (float)cos (dl->stopdot2 / 180 * Q_PI);
|
|
}
|
|
if (dl->stopdot2 > dl->stopdot)
|
|
dl->stopdot2 = dl->stopdot;
|
|
}
|
|
#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
|
|
#ifdef HLRAD_CUSTOMTEXLIGHT
|
|
if (g_face_texlights[p->faceNumber])
|
|
{
|
|
if (*ValueForKey (g_face_texlights[p->faceNumber], "_scale"))
|
|
{
|
|
vec_t scale = FloatForKey (g_face_texlights[p->faceNumber], "_scale");
|
|
VectorScale (dl->intensity, scale, dl->intensity);
|
|
}
|
|
}
|
|
#endif
|
|
VectorScale(dl->intensity, p->area, dl->intensity);
|
|
#ifdef HLRAD_ACCURATEBOUNCE_REDUCEAREA
|
|
VectorScale (dl->intensity, p->exposure, dl->intensity);
|
|
#endif
|
|
#ifdef HLRAD_REFLECTIVITY
|
|
VectorScale (dl->intensity, 1.0 / Q_PI, dl->intensity);
|
|
VectorMultiply (dl->intensity, p->texturereflectivity, dl->intensity);
|
|
#else
|
|
VectorScale(dl->intensity, DIRECT_SCALE, dl->intensity);
|
|
#endif
|
|
|
|
#ifdef HLRAD_WATERBACKFACE_FIX
|
|
dface_t *f = &g_dfaces[p->faceNumber];
|
|
if (g_face_entity[p->faceNumber] - g_entities != 0 && !strncasecmp (GetTextureByNumber (f->texinfo), "!", 1))
|
|
{
|
|
directlight_t *dl2;
|
|
numdlights++;
|
|
dl2 = (directlight_t *)calloc (1, sizeof (directlight_t));
|
|
hlassume (dl2 != NULL, assume_NoMemory);
|
|
*dl2 = *dl;
|
|
VectorMA (dl->origin, -2, dl->normal, dl2->origin);
|
|
VectorSubtract (vec3_origin, dl->normal, dl2->normal);
|
|
leaf = PointInLeaf (dl2->origin);
|
|
leafnum = leaf - g_dleafs;
|
|
dl2->next = directlights[leafnum];
|
|
directlights[leafnum] = dl2;
|
|
}
|
|
#endif
|
|
#ifndef HLRAD_CUSTOMTEXLIGHT // no softlight hack
|
|
// --------------------------------------------------------------
|
|
// 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));
|
|
#ifdef HLRAD_HLASSUMENOMEMORY
|
|
hlassume (dl != NULL, assume_NoMemory);
|
|
#endif
|
|
|
|
VectorCopy(p->origin, dl->origin);
|
|
|
|
leaf = PointInLeaf(dl->origin);
|
|
leafnum = leaf - g_dleafs;
|
|
|
|
dl->next = directlights[leafnum];
|
|
directlights[leafnum] = dl;
|
|
|
|
#ifdef HLRAD_GatherPatchLight
|
|
dl->topatch = false;
|
|
#ifdef HLRAD_TEXLIGHTTHRESHOLD_FIX
|
|
if (!p->emitmode)
|
|
{
|
|
dl->topatch = true;
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_FASTMODE
|
|
if (g_fastmode)
|
|
{
|
|
dl->topatch = true;
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHT_SPOTS_FIX
|
|
dl->patch_area = p->area;
|
|
#ifdef HLRAD_ACCURATEBOUNCE_TEXLIGHT
|
|
dl->patch_emitter_range = p->emitter_range;
|
|
dl->patch = p;
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
dl->texlightgap = 0;
|
|
#endif
|
|
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);
|
|
#ifdef HLRAD_ACCURATEBOUNCE_REDUCEAREA
|
|
VectorScale (dl->intensity, p->exposure, dl->intensity);
|
|
#endif
|
|
#ifdef HLRAD_REFLECTIVITY
|
|
VectorScale (dl->intensity, 1.0 / Q_PI, dl->intensity);
|
|
VectorMultiply (dl->intensity, p->texturereflectivity, dl->intensity);
|
|
#else
|
|
VectorScale(dl->intensity, DIRECT_SCALE, dl->intensity);
|
|
#endif
|
|
|
|
dl->intensity[0] *= g_softlight_hack[0];
|
|
dl->intensity[1] *= g_softlight_hack[1];
|
|
dl->intensity[2] *= g_softlight_hack[2];
|
|
}
|
|
|
|
#endif
|
|
// --------------------------------------------------------------
|
|
#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;
|
|
#ifdef HLRAD_STYLE_CORING
|
|
{
|
|
int style = IntForKey (e, "style");
|
|
#ifdef ZHLT_TEXLIGHT
|
|
if (style < 0)
|
|
{
|
|
style = -style;
|
|
}
|
|
#endif
|
|
style = (unsigned char)style;
|
|
if (style > 0 && style < ALLSTYLES && *ValueForKey (e, "zhlt_stylecoring"))
|
|
{
|
|
g_corings[style] = FloatForKey (e, "zhlt_stylecoring");
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
if (!strcmp (name, "light_shadow")
|
|
#ifdef HLRAD_BOUNCE_STYLE
|
|
|| !strcmp (name, "light_bounce")
|
|
#endif
|
|
)
|
|
{
|
|
#ifdef HLRAD_STYLEREPORT
|
|
int style = IntForKey (e, "style");
|
|
#ifdef ZHLT_TEXLIGHT
|
|
if (style < 0)
|
|
{
|
|
style = -style;
|
|
}
|
|
#endif
|
|
style = (unsigned char)style;
|
|
if (style >= 0 && style < ALLSTYLES)
|
|
{
|
|
if (styleused[style] == false)
|
|
{
|
|
styleused[style] = true;
|
|
numstyles++;
|
|
}
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_CUSTOMTEXLIGHT
|
|
if (!strcmp (name, "light_surface"))
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
numdlights++;
|
|
dl = (directlight_t*)calloc(1, sizeof(directlight_t));
|
|
#ifdef HLRAD_HLASSUMENOMEMORY
|
|
hlassume (dl != NULL, assume_NoMemory);
|
|
#endif
|
|
|
|
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
|
|
#ifdef HLRAD_STYLE_CORING
|
|
dl->style = (unsigned char)dl->style;
|
|
if (dl->style >= ALLSTYLES)
|
|
{
|
|
Error ("invalid light style: style (%d) >= ALLSTYLES (%d)", dl->style, ALLSTYLES);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_STYLEREPORT
|
|
if (dl->style >= 0 && dl->style < ALLSTYLES)
|
|
{
|
|
if (styleused[dl->style] == false)
|
|
{
|
|
styleused[dl->style] = true;
|
|
numstyles++;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_GatherPatchLight
|
|
dl->topatch = false;
|
|
if (IntForKey (e, "_fast") == 1)
|
|
{
|
|
dl->topatch = true;
|
|
}
|
|
#ifdef HLRAD_FASTMODE
|
|
if (g_fastmode)
|
|
{
|
|
dl->topatch = true;
|
|
}
|
|
#endif
|
|
#endif
|
|
pLight = ValueForKey(e, "_light");
|
|
// scanf into doubles, then assign, so it is vec_t size independent
|
|
r = g = b = scaler = 0;
|
|
argCnt = sscanf(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;
|
|
}
|
|
|
|
#ifndef HLRAD_ARG_MISC
|
|
dl->falloff = IntForKey(e, "_falloff");
|
|
if (dl->falloff == 0)
|
|
{
|
|
dl->falloff = g_falloff;
|
|
}
|
|
#endif
|
|
|
|
target = ValueForKey(e, "target");
|
|
|
|
if (!strcmp(name, "light_spot") || !strcmp(name, "light_environment") || target[0])
|
|
{
|
|
if (!VectorAvg(dl->intensity))
|
|
{
|
|
#ifndef HLRAD_ALLOWZEROBRIGHTNESS
|
|
VectorFill(dl->intensity, 500);
|
|
#endif
|
|
}
|
|
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 (!FindTargetEntity(target)) //--vluzacn
|
|
{
|
|
Warning("light at (%i %i %i) has missing target",
|
|
(int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]);
|
|
target = "";
|
|
}
|
|
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(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
|
|
// -----------------------------------------------------------------------------------
|
|
#ifdef HLRAD_SUNDIFFUSE
|
|
pLight = ValueForKey(e, "_diffuse_light2");
|
|
r = g = b = scaler = 0;
|
|
argCnt = sscanf(pLight, "%lf %lf %lf %lf", &r, &g, &b, &scaler);
|
|
dl->diffuse_intensity2[0] = (float)r;
|
|
if (argCnt == 1)
|
|
{
|
|
// The R,G,B values are all equal.
|
|
dl->diffuse_intensity2[1] = dl->diffuse_intensity2[2] = (float)r;
|
|
}
|
|
else if (argCnt == 3 || argCnt == 4)
|
|
{
|
|
// Save the other two G,B values.
|
|
dl->diffuse_intensity2[1] = (float)g;
|
|
dl->diffuse_intensity2[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_intensity2[0] = dl->diffuse_intensity2[0] / 255 * (float)scaler;
|
|
dl->diffuse_intensity2[1] = dl->diffuse_intensity2[1] / 255 * (float)scaler;
|
|
dl->diffuse_intensity2[2] = dl->diffuse_intensity2[2] / 255 * (float)scaler;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dl->diffuse_intensity2[0] = dl->diffuse_intensity[0];
|
|
dl->diffuse_intensity2[1] = dl->diffuse_intensity[1];
|
|
dl->diffuse_intensity2[2] = dl->diffuse_intensity[2];
|
|
}
|
|
#endif
|
|
|
|
dl->type = emit_skylight;
|
|
dl->stopdot2 = FloatForKey(e, "_sky"); // hack stopdot2 to a sky key number
|
|
#ifdef HLRAD_SUNSPREAD
|
|
dl->sunspreadangle = FloatForKey (e, "_spread");
|
|
if (!g_allow_spread)
|
|
{
|
|
dl->sunspreadangle = 0;
|
|
}
|
|
if (dl->sunspreadangle < 0.0 || dl->sunspreadangle > 180)
|
|
{
|
|
Error ("Invalid spread angle '%s'. Please use a number between 0 and 180.\n", ValueForKey (e, "_spread"));
|
|
}
|
|
if (dl->sunspreadangle > 0.0)
|
|
{
|
|
int i;
|
|
vec_t testangle = dl->sunspreadangle;
|
|
if (dl->sunspreadangle < SUNSPREAD_THRESHOLD)
|
|
{
|
|
testangle = SUNSPREAD_THRESHOLD; // We will later centralize all the normals we have collected.
|
|
}
|
|
{
|
|
vec_t totalweight = 0;
|
|
int count;
|
|
vec_t testdot = cos (testangle * (Q_PI / 180.0));
|
|
for (count = 0, i = 0; i < g_numskynormals[SUNSPREAD_SKYLEVEL]; i++)
|
|
{
|
|
vec3_t &testnormal = g_skynormals[SUNSPREAD_SKYLEVEL][i];
|
|
vec_t dot = DotProduct (dl->normal, testnormal);
|
|
if (dot >= testdot - NORMAL_EPSILON)
|
|
{
|
|
totalweight += qmax (0, dot - testdot) * g_skynormalsizes[SUNSPREAD_SKYLEVEL][i]; // This is not the right formula when dl->sunspreadangle < SUNSPREAD_THRESHOLD, but it gives almost the same result as the right one.
|
|
count++;
|
|
}
|
|
}
|
|
if (count <= 10 || totalweight <= NORMAL_EPSILON)
|
|
{
|
|
Error ("collect spread normals: internal error: can not collect enough normals.");
|
|
}
|
|
dl->numsunnormals = count;
|
|
dl->sunnormals = (vec3_t *)malloc (count * sizeof (vec3_t));
|
|
dl->sunnormalweights = (vec_t *)malloc (count * sizeof (vec_t));
|
|
hlassume (dl->sunnormals != NULL, assume_NoMemory);
|
|
hlassume (dl->sunnormalweights != NULL, assume_NoMemory);
|
|
for (count = 0, i = 0; i < g_numskynormals[SUNSPREAD_SKYLEVEL]; i++)
|
|
{
|
|
vec3_t &testnormal = g_skynormals[SUNSPREAD_SKYLEVEL][i];
|
|
vec_t dot = DotProduct (dl->normal, testnormal);
|
|
if (dot >= testdot - NORMAL_EPSILON)
|
|
{
|
|
if (count >= dl->numsunnormals)
|
|
{
|
|
Error ("collect spread normals: internal error.");
|
|
}
|
|
VectorCopy (testnormal, dl->sunnormals[count]);
|
|
dl->sunnormalweights[count] = qmax (0, dot - testdot) * g_skynormalsizes[SUNSPREAD_SKYLEVEL][i] / totalweight;
|
|
count++;
|
|
}
|
|
}
|
|
if (count != dl->numsunnormals)
|
|
{
|
|
Error ("collect spread normals: internal error.");
|
|
}
|
|
}
|
|
if (dl->sunspreadangle < SUNSPREAD_THRESHOLD)
|
|
{
|
|
for (i = 0; i < dl->numsunnormals; i++)
|
|
{
|
|
vec3_t tmp;
|
|
VectorScale (dl->sunnormals[i], 1 / DotProduct (dl->sunnormals[i], dl->normal), tmp);
|
|
VectorSubtract (tmp, dl->normal, tmp);
|
|
VectorMA (dl->normal, dl->sunspreadangle / SUNSPREAD_THRESHOLD, tmp, dl->sunnormals[i]);
|
|
VectorNormalize (dl->sunnormals[i]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dl->numsunnormals = 1;
|
|
dl->sunnormals = (vec3_t *)malloc (sizeof (vec3_t));
|
|
dl->sunnormalweights = (vec_t *)malloc (sizeof (vec_t));
|
|
hlassume (dl->sunnormals != NULL, assume_NoMemory);
|
|
hlassume (dl->sunnormalweights != NULL, assume_NoMemory);
|
|
VectorCopy (dl->normal, dl->sunnormals[0]);
|
|
dl->sunnormalweights[0] = 1.0;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!VectorAvg(dl->intensity))
|
|
{
|
|
#ifndef HLRAD_ALLOWZEROBRIGHTNESS
|
|
VectorFill(dl->intensity, 300);
|
|
#endif
|
|
}
|
|
dl->type = emit_point;
|
|
}
|
|
|
|
if (dl->type != emit_skylight)
|
|
{
|
|
//why? --vluzacn
|
|
l1 = qmax(dl->intensity[0], qmax(dl->intensity[1], dl->intensity[2]));
|
|
l1 = l1 * l1 / 10;
|
|
|
|
dl->intensity[0] *= l1;
|
|
dl->intensity[1] *= l1;
|
|
dl->intensity[2] *= l1;
|
|
}
|
|
}
|
|
|
|
#ifndef HLRAD_ALLOWZEROBRIGHTNESS
|
|
hlassume(numdlights, assume_NoLights);
|
|
#endif
|
|
#ifdef HLRAD_GatherPatchLight
|
|
int countnormallights = 0, countfastlights = 0;
|
|
{
|
|
int l;
|
|
#ifdef HLRAD_VIS_FIX
|
|
for (l = 0; l < 1 + g_dmodels[0].visleafs; l++)
|
|
#else
|
|
for (l = 0; l < g_numleafs; l++)
|
|
#endif
|
|
{
|
|
for (dl = directlights[l]; dl; dl = dl->next)
|
|
{
|
|
switch (dl->type)
|
|
{
|
|
case emit_surface:
|
|
case emit_point:
|
|
case emit_spotlight:
|
|
if (!VectorCompare (dl->intensity, vec3_origin))
|
|
{
|
|
if (dl->topatch)
|
|
{
|
|
countfastlights++;
|
|
}
|
|
else
|
|
{
|
|
countnormallights++;
|
|
}
|
|
}
|
|
break;
|
|
case emit_skylight:
|
|
if (!VectorCompare (dl->intensity, vec3_origin))
|
|
{
|
|
if (dl->topatch)
|
|
{
|
|
countfastlights++;
|
|
#ifdef HLRAD_SUNSPREAD
|
|
if (dl->sunspreadangle > 0.0)
|
|
{
|
|
countfastlights--;
|
|
countfastlights += dl->numsunnormals;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
countnormallights++;
|
|
#ifdef HLRAD_SUNSPREAD
|
|
if (dl->sunspreadangle > 0.0)
|
|
{
|
|
countnormallights--;
|
|
countnormallights += dl->numsunnormals;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef HLRAD_WHOME
|
|
if (g_indirect_sun > 0 && !VectorCompare (dl->diffuse_intensity, vec3_origin))
|
|
#else
|
|
if (g_indirect_sun > 0 && !VectorCompare (dl->intensity, vec3_origin))
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_SOFTSKY
|
|
if (g_softsky)
|
|
{
|
|
countfastlights += g_numskynormals[SKYLEVEL_SOFTSKYON];
|
|
}
|
|
else
|
|
{
|
|
#ifdef HLRAD_FASTMODE
|
|
countfastlights += g_numskynormals[SKYLEVEL_SOFTSKYOFF];
|
|
#else
|
|
countnormallights += g_numskynormals[SKYLEVEL_SOFTSKYOFF];
|
|
#endif
|
|
}
|
|
#else
|
|
countnormallights += 162; //NUMVERTEXNORMALS
|
|
#endif
|
|
}
|
|
break;
|
|
default:
|
|
hlassume(false, assume_BadLightType);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Log("%i direct lights and %i fast direct lights\n", countnormallights, countfastlights);
|
|
#else
|
|
Log("%i direct lights\n", numdlights);
|
|
#endif
|
|
#ifdef HLRAD_STYLEREPORT
|
|
Log("%i light styles\n", numstyles);
|
|
#endif
|
|
#ifdef HLRAD_SKYFIX_FIX
|
|
// move all emit_skylight to leaf 0 (the solid leaf)
|
|
if (g_sky_lighting_fix)
|
|
{
|
|
directlight_t *skylights = NULL;
|
|
int l;
|
|
#ifdef HLRAD_VIS_FIX
|
|
for (l = 0; l < 1 + g_dmodels[0].visleafs; l++)
|
|
#else
|
|
for (l = 0; l < g_numleafs; l++)
|
|
#endif
|
|
{
|
|
directlight_t **pdl;
|
|
for (dl = directlights[l], pdl = &directlights[l]; dl; dl = *pdl)
|
|
{
|
|
if (dl->type == emit_skylight)
|
|
{
|
|
*pdl = dl->next;
|
|
dl->next = skylights;
|
|
skylights = dl;
|
|
}
|
|
else
|
|
{
|
|
pdl = &dl->next;
|
|
}
|
|
}
|
|
}
|
|
while ((dl = directlights[0]) != NULL)
|
|
{
|
|
// since they are in leaf 0, they won't emit a light anyway
|
|
directlights[0] = dl->next;
|
|
free(dl);
|
|
}
|
|
directlights[0] = skylights;
|
|
}
|
|
#endif
|
|
#ifdef ZHLT_ENTITY_INFOSUNLIGHT
|
|
#ifdef HLRAD_MULTISKYLIGHT
|
|
if (g_sky_lighting_fix)
|
|
{
|
|
int countlightenvironment = 0;
|
|
int countinfosunlight = 0;
|
|
for (int i = 0; i < g_numentities; i++)
|
|
{
|
|
entity_t *e = &g_entities[i];
|
|
const char *classname = ValueForKey (e, "classname");
|
|
if (!strcmp (classname, "light_environment"))
|
|
{
|
|
countlightenvironment++;
|
|
}
|
|
if (!strcmp (classname, "info_sunlight"))
|
|
{
|
|
countinfosunlight++;
|
|
}
|
|
}
|
|
if (countlightenvironment > 1 && countinfosunlight == 0)
|
|
{
|
|
// because the map is lit by more than one light_environments, but the game can only recognize one of them when setting sv_skycolor and sv_skyvec.
|
|
Warning ("More than one light_environments are in use. Add entity info_sunlight to clarify the sunlight's brightness for in-game model(.mdl) rendering.");
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
// =====================================================================================
|
|
// DeleteDirectLights
|
|
// =====================================================================================
|
|
void DeleteDirectLights()
|
|
{
|
|
int l;
|
|
directlight_t* dl;
|
|
|
|
#ifdef HLRAD_VIS_FIX
|
|
for (l = 0; l < 1 + g_dmodels[0].visleafs; l++)
|
|
#else
|
|
for (l = 0; l < g_numleafs; l++)
|
|
#endif
|
|
{
|
|
dl = directlights[l];
|
|
while (dl)
|
|
{
|
|
directlights[l] = dl->next;
|
|
free(dl);
|
|
dl = directlights[l];
|
|
}
|
|
}
|
|
|
|
// AJM: todo: strip light entities out at this point
|
|
// vluzacn: hlvis and hlrad must not modify entity data, because the following procedures are supposed to produce the same bsp file:
|
|
// 1> hlcsg -> hlbsp -> hlvis -> hlrad (a normal compile)
|
|
// 2) hlcsg -> hlbsp -> hlvis -> hlrad -> hlcsg -onlyents
|
|
// 3) hlcsg -> hlbsp -> hlvis -> hlrad -> hlcsg -onlyents -> hlrad
|
|
}
|
|
|
|
// =====================================================================================
|
|
// GatherSampleLight
|
|
// =====================================================================================
|
|
#ifndef HLRAD_SOFTSKY
|
|
#define NUMVERTEXNORMALS 162
|
|
double r_avertexnormals[NUMVERTEXNORMALS][3] = {
|
|
//#include "../common/anorms.h"
|
|
#include "anorms.h" //--vluzacn
|
|
};
|
|
#endif
|
|
#ifdef HLRAD_SOFTSKY
|
|
int g_numskynormals[SKYLEVELMAX+1];
|
|
vec3_t *g_skynormals[SKYLEVELMAX+1];
|
|
vec_t *g_skynormalsizes[SKYLEVELMAX+1];
|
|
typedef double point_t[3];
|
|
typedef struct {int point[2]; bool divided; int child[2];} edge_t;
|
|
typedef struct {int edge[3]; int dir[3];} triangle_t;
|
|
void CopyToSkynormals (int skylevel, int numpoints, point_t *points, int numedges, edge_t *edges, int numtriangles, triangle_t *triangles)
|
|
{
|
|
hlassume (numpoints == (1 << (2 * skylevel)) + 2, assume_first);
|
|
hlassume (numedges == (1 << (2 * skylevel)) * 4 - 4 , assume_first);
|
|
hlassume (numtriangles == (1 << (2 * skylevel)) * 2, assume_first);
|
|
g_numskynormals[skylevel] = numpoints;
|
|
g_skynormals[skylevel] = (vec3_t *)malloc (numpoints * sizeof (vec3_t));
|
|
g_skynormalsizes[skylevel] = (vec_t *)malloc (numpoints * sizeof (vec_t));
|
|
hlassume (g_skynormals[skylevel] != NULL, assume_NoMemory);
|
|
hlassume (g_skynormalsizes[skylevel] != NULL, assume_NoMemory);
|
|
int j, k;
|
|
for (j = 0; j < numpoints; j++)
|
|
{
|
|
VectorCopy (points[j], g_skynormals[skylevel][j]);
|
|
g_skynormalsizes[skylevel][j] = 0;
|
|
}
|
|
double totalsize = 0;
|
|
for (j = 0; j < numtriangles; j++)
|
|
{
|
|
int pt[3];
|
|
for (k = 0; k < 3; k++)
|
|
{
|
|
pt[k] = edges[triangles[j].edge[k]].point[triangles[j].dir[k]];
|
|
}
|
|
double currentsize;
|
|
double tmp[3];
|
|
CrossProduct (points[pt[0]], points[pt[1]], tmp);
|
|
currentsize = DotProduct (tmp, points[pt[2]]);
|
|
hlassume (currentsize > 0, assume_first);
|
|
g_skynormalsizes[skylevel][pt[0]] += currentsize / 3.0;
|
|
g_skynormalsizes[skylevel][pt[1]] += currentsize / 3.0;
|
|
g_skynormalsizes[skylevel][pt[2]] += currentsize / 3.0;
|
|
totalsize += currentsize;
|
|
}
|
|
for (j = 0; j < numpoints; j++)
|
|
{
|
|
g_skynormalsizes[skylevel][j] /= totalsize;
|
|
}
|
|
#if 0
|
|
printf ("g_numskynormals[%i]=%i\n", skylevel, g_numskynormals[skylevel]);
|
|
for (j = 0; j < numpoints; j += (numpoints / 20 + 1))
|
|
{
|
|
printf ("g_skynormals[%i][%i]=%1.3f,%1.3f,%1.3f g_skynormalsizes[%i][%i]=%f\n",
|
|
skylevel, j, g_skynormals[skylevel][j][0], g_skynormals[skylevel][j][1], g_skynormals[skylevel][j][2],
|
|
skylevel, j, g_skynormalsizes[skylevel][j]);
|
|
}
|
|
#endif
|
|
}
|
|
void BuildDiffuseNormals ()
|
|
{
|
|
int i, j, k;
|
|
g_numskynormals[0] = 0;
|
|
g_skynormals[0] = NULL; //don't use this
|
|
g_skynormalsizes[0] = NULL;
|
|
int numpoints = 6;
|
|
point_t *points = (point_t *)malloc (((1 << (2 * SKYLEVELMAX)) + 2) * sizeof (point_t));
|
|
hlassume (points != NULL, assume_NoMemory);
|
|
points[0][0] = 1, points[0][1] = 0, points[0][2] = 0;
|
|
points[1][0] = -1,points[1][1] = 0, points[1][2] = 0;
|
|
points[2][0] = 0, points[2][1] = 1, points[2][2] = 0;
|
|
points[3][0] = 0, points[3][1] = -1,points[3][2] = 0;
|
|
points[4][0] = 0, points[4][1] = 0, points[4][2] = 1;
|
|
points[5][0] = 0, points[5][1] = 0, points[5][2] = -1;
|
|
int numedges = 12;
|
|
edge_t *edges = (edge_t *)malloc (((1 << (2 * SKYLEVELMAX)) * 4 - 4) * sizeof (edge_t));
|
|
hlassume (edges != NULL, assume_NoMemory);
|
|
edges[0].point[0] = 0, edges[0].point[1] = 2, edges[0].divided = false;
|
|
edges[1].point[0] = 2, edges[1].point[1] = 1, edges[1].divided = false;
|
|
edges[2].point[0] = 1, edges[2].point[1] = 3, edges[2].divided = false;
|
|
edges[3].point[0] = 3, edges[3].point[1] = 0, edges[3].divided = false;
|
|
edges[4].point[0] = 2, edges[4].point[1] = 4, edges[4].divided = false;
|
|
edges[5].point[0] = 4, edges[5].point[1] = 3, edges[5].divided = false;
|
|
edges[6].point[0] = 3, edges[6].point[1] = 5, edges[6].divided = false;
|
|
edges[7].point[0] = 5, edges[7].point[1] = 2, edges[7].divided = false;
|
|
edges[8].point[0] = 4, edges[8].point[1] = 0, edges[8].divided = false;
|
|
edges[9].point[0] = 0, edges[9].point[1] = 5, edges[9].divided = false;
|
|
edges[10].point[0] = 5, edges[10].point[1] = 1, edges[10].divided = false;
|
|
edges[11].point[0] = 1, edges[11].point[1] = 4, edges[11].divided = false;
|
|
int numtriangles = 8;
|
|
triangle_t *triangles = (triangle_t *)malloc (((1 << (2 * SKYLEVELMAX)) * 2) * sizeof (triangle_t));
|
|
hlassume (triangles != NULL, assume_NoMemory);
|
|
triangles[0].edge[0] = 0, triangles[0].dir[0] = 0, triangles[0].edge[1] = 4, triangles[0].dir[1] = 0, triangles[0].edge[2] = 8, triangles[0].dir[2] = 0;
|
|
triangles[1].edge[0] = 1, triangles[1].dir[0] = 0, triangles[1].edge[1] = 11, triangles[1].dir[1] = 0, triangles[1].edge[2] = 4, triangles[1].dir[2] = 1;
|
|
triangles[2].edge[0] = 2, triangles[2].dir[0] = 0, triangles[2].edge[1] = 5, triangles[2].dir[1] = 1, triangles[2].edge[2] = 11, triangles[2].dir[2] = 1;
|
|
triangles[3].edge[0] = 3, triangles[3].dir[0] = 0, triangles[3].edge[1] = 8, triangles[3].dir[1] = 1, triangles[3].edge[2] = 5, triangles[3].dir[2] = 0;
|
|
triangles[4].edge[0] = 0, triangles[4].dir[0] = 1, triangles[4].edge[1] = 9, triangles[4].dir[1] = 0, triangles[4].edge[2] = 7, triangles[4].dir[2] = 0;
|
|
triangles[5].edge[0] = 1, triangles[5].dir[0] = 1, triangles[5].edge[1] = 7, triangles[5].dir[1] = 1, triangles[5].edge[2] = 10, triangles[5].dir[2] = 0;
|
|
triangles[6].edge[0] = 2, triangles[6].dir[0] = 1, triangles[6].edge[1] = 10, triangles[6].dir[1] = 1, triangles[6].edge[2] = 6, triangles[6].dir[2] = 1;
|
|
triangles[7].edge[0] = 3, triangles[7].dir[0] = 1, triangles[7].edge[1] = 6, triangles[7].dir[1] = 0, triangles[7].edge[2] = 9, triangles[7].dir[2] = 1;
|
|
CopyToSkynormals (1, numpoints, points, numedges, edges, numtriangles, triangles);
|
|
for (i = 1; i < SKYLEVELMAX; i++)
|
|
{
|
|
int oldnumedges = numedges;
|
|
for (j = 0; j < oldnumedges; j++)
|
|
{
|
|
if (!edges[j].divided)
|
|
{
|
|
hlassume (numpoints < (1 << (2 * SKYLEVELMAX)) + 2, assume_first);
|
|
point_t mid;
|
|
double len;
|
|
VectorAdd (points[edges[j].point[0]], points[edges[j].point[1]], mid);
|
|
len = sqrt (DotProduct (mid, mid));
|
|
hlassume (len > 0.2, assume_first);
|
|
VectorScale (mid, 1 / len, mid);
|
|
int p2 = numpoints;
|
|
VectorCopy (mid, points[numpoints]);
|
|
numpoints++;
|
|
hlassume (numedges < (1 << (2 * SKYLEVELMAX)) * 4 - 4, assume_first);
|
|
edges[j].child[0] = numedges;
|
|
edges[numedges].divided = false;
|
|
edges[numedges].point[0] = edges[j].point[0];
|
|
edges[numedges].point[1] = p2;
|
|
numedges++;
|
|
hlassume (numedges < (1 << (2 * SKYLEVELMAX)) * 4 - 4, assume_first);
|
|
edges[j].child[1] = numedges;
|
|
edges[numedges].divided = false;
|
|
edges[numedges].point[0] = p2;
|
|
edges[numedges].point[1] = edges[j].point[1];
|
|
numedges++;
|
|
edges[j].divided = true;
|
|
}
|
|
}
|
|
int oldnumtriangles = numtriangles;
|
|
for (j = 0; j < oldnumtriangles; j++)
|
|
{
|
|
int mid[3];
|
|
for (k = 0; k < 3; k++)
|
|
{
|
|
hlassume (numtriangles < (1 << (2 * SKYLEVELMAX)) * 2, assume_first);
|
|
mid[k] = edges[edges[triangles[j].edge[k]].child[0]].point[1];
|
|
triangles[numtriangles].edge[0] = edges[triangles[j].edge[k]].child[1 - triangles[j].dir[k]];
|
|
triangles[numtriangles].dir[0] = triangles[j].dir[k];
|
|
triangles[numtriangles].edge[1] = edges[triangles[j].edge[(k+1)%3]].child[triangles[j].dir[(k+1)%3]];
|
|
triangles[numtriangles].dir[1] = triangles[j].dir[(k+1)%3];
|
|
triangles[numtriangles].edge[2] = numedges + k;
|
|
triangles[numtriangles].dir[2] = 1;
|
|
numtriangles++;
|
|
}
|
|
for (k = 0; k < 3; k++)
|
|
{
|
|
hlassume (numedges < (1 << (2 * SKYLEVELMAX)) * 4 - 4, assume_first);
|
|
triangles[j].edge[k] = numedges;
|
|
triangles[j].dir[k] = 0;
|
|
edges[numedges].divided = false;
|
|
edges[numedges].point[0] = mid[k];
|
|
edges[numedges].point[1] = mid[(k+1)%3];
|
|
numedges++;
|
|
}
|
|
}
|
|
CopyToSkynormals (i + 1, numpoints, points, numedges, edges, numtriangles, triangles);
|
|
}
|
|
free (points);
|
|
free (edges);
|
|
free (triangles);
|
|
}
|
|
#endif
|
|
static void GatherSampleLight(const vec3_t pos, const byte* const pvs, const vec3_t normal, vec3_t* sample
|
|
#ifdef ZHLT_XASH
|
|
, vec3_t* sample_direction
|
|
#endif
|
|
, byte* styles
|
|
#ifdef HLRAD_GatherPatchLight
|
|
, int step
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, int miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, int texlightgap_surfacenum
|
|
#endif
|
|
)
|
|
{
|
|
int i;
|
|
directlight_t* l;
|
|
#ifndef HLRAD_OPAQUE_STYLE
|
|
vec3_t add;
|
|
#ifdef ZHLT_XASH
|
|
vec3_t add_direction;
|
|
#endif
|
|
#endif // now we always add the light into the total brightness of this sample immediately after each TestLine, because each TestLine may result in different style.
|
|
vec3_t delta;
|
|
float dot, dot2;
|
|
float dist;
|
|
float ratio;
|
|
#ifdef HLRAD_OPACITY // AJM
|
|
float l_opacity;
|
|
#endif
|
|
int style_index;
|
|
#ifdef HLRAD_GatherPatchLight
|
|
int step_match;
|
|
#endif
|
|
#ifdef HLRAD_MULTISKYLIGHT
|
|
bool sky_used = false;
|
|
#else
|
|
directlight_t* sky_used = NULL;
|
|
#endif
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
vec3_t testline_origin;
|
|
#endif
|
|
#ifdef HLRAD_STYLE_CORING
|
|
vec3_t adds[ALLSTYLES];
|
|
#ifdef ZHLT_XASH
|
|
vec3_t adds_direction[ALLSTYLES];
|
|
#endif
|
|
int style;
|
|
memset (adds, 0, ALLSTYLES * sizeof(vec3_t));
|
|
#ifdef ZHLT_XASH
|
|
memset (adds_direction, 0, ALLSTYLES * sizeof (vec3_t));
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
bool lighting_diversify;
|
|
vec_t lighting_power;
|
|
vec_t lighting_scale;
|
|
lighting_power = g_lightingconeinfo[miptex][0];
|
|
lighting_scale = g_lightingconeinfo[miptex][1];
|
|
lighting_diversify = (lighting_power != 1.0 || lighting_scale != 1.0);
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
vec3_t texlightgap_textoworld[2];
|
|
// calculates textoworld
|
|
{
|
|
dface_t *f = &g_dfaces[texlightgap_surfacenum];
|
|
const dplane_t *dp = getPlaneFromFace (f);
|
|
texinfo_t *tex = &g_texinfo[f->texinfo];
|
|
int x;
|
|
vec_t len;
|
|
|
|
for (x = 0; x < 2; x++)
|
|
{
|
|
CrossProduct (tex->vecs[1 - x], dp->normal, texlightgap_textoworld[x]);
|
|
len = DotProduct (texlightgap_textoworld[x], tex->vecs[x]);
|
|
if (fabs (len) < NORMAL_EPSILON)
|
|
{
|
|
VectorClear (texlightgap_textoworld[x]);
|
|
}
|
|
else
|
|
{
|
|
VectorScale (texlightgap_textoworld[x], 1 / len, texlightgap_textoworld[x]);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef HLRAD_SKYFIX_FIX
|
|
#ifdef HLRAD_VIS_FIX
|
|
for (i = 0; i < 1 + g_dmodels[0].visleafs; i++)
|
|
#else
|
|
for (i = 0; i < g_numleafs; i++)
|
|
#endif
|
|
#else
|
|
#ifdef HLRAD_VIS_FIX
|
|
for (i = 1; i < 1 + g_dmodels[0].visleafs; i++)
|
|
#else
|
|
for (i = 1; i < g_numleafs; i++)
|
|
#endif
|
|
#endif
|
|
{
|
|
l = directlights[i];
|
|
#ifdef HLRAD_SKYFIX_FIX
|
|
if (l)
|
|
{
|
|
if (i == 0? g_sky_lighting_fix: pvs[(i - 1) >> 3] & (1 << ((i - 1) & 7)))
|
|
{
|
|
for (; l; l = l->next)
|
|
{
|
|
#else
|
|
if (l)
|
|
{
|
|
if (((l->type == emit_skylight) && (g_sky_lighting_fix)) || (pvs[(i - 1) >> 3] & (1 << ((i - 1) & 7))))
|
|
{
|
|
for (; l; l = l->next)
|
|
{
|
|
#endif
|
|
// skylights work fundamentally differently than normal lights
|
|
if (l->type == emit_skylight)
|
|
{
|
|
#ifdef HLRAD_MULTISKYLIGHT
|
|
if (!g_sky_lighting_fix)
|
|
{
|
|
if (sky_used)
|
|
{
|
|
continue;
|
|
}
|
|
sky_used = true;
|
|
}
|
|
#ifndef HLRAD_OPAQUE_STYLE
|
|
VectorClear (add);
|
|
#endif
|
|
do // add sun light
|
|
{
|
|
#ifdef HLRAD_GatherPatchLight
|
|
// check step
|
|
step_match = (int)l->topatch;
|
|
if (step != step_match)
|
|
continue;
|
|
#endif
|
|
#ifdef HLRAD_ALLOWZEROBRIGHTNESS
|
|
// check intensity
|
|
if (!(l->intensity[0] || l->intensity[1] || l->intensity[2]))
|
|
continue;
|
|
#endif
|
|
#ifdef HLRAD_SUNSPREAD
|
|
// loop over the normals
|
|
for (int j = 0; j < l->numsunnormals; j++)
|
|
{
|
|
#endif
|
|
// make sure the angle is okay
|
|
#ifdef HLRAD_SUNSPREAD
|
|
dot = -DotProduct (normal, l->sunnormals[j]);
|
|
#else
|
|
dot = -DotProduct (normal, l->normal);
|
|
#endif
|
|
if (dot <= NORMAL_EPSILON) //ON_EPSILON / 10 //--vluzacn
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// search back to see if we can hit a sky brush
|
|
#ifdef HLRAD_SUNSPREAD
|
|
#ifdef ZHLT_LARGERANGE
|
|
VectorScale (l->sunnormals[j], -BOGUS_RANGE, delta);
|
|
#else
|
|
VectorScale (l->sunnormals[j], -10000, delta);
|
|
#endif
|
|
#else
|
|
#ifdef ZHLT_LARGERANGE
|
|
VectorScale (l->normal, -BOGUS_RANGE, delta);
|
|
#else
|
|
VectorScale (l->normal, -10000, delta);
|
|
#endif
|
|
#endif
|
|
VectorAdd(pos, delta, delta);
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
vec3_t skyhit;
|
|
VectorCopy (delta, skyhit);
|
|
#endif
|
|
if (TestLine(pos, delta
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
, skyhit
|
|
#endif
|
|
) != CONTENTS_SKY)
|
|
{
|
|
continue; // occluded
|
|
}
|
|
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency;
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
int opaquestyle;
|
|
#endif
|
|
if (TestSegmentAgainstOpaqueList(pos,
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
skyhit
|
|
#else
|
|
delta
|
|
#endif
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
, opaquestyle
|
|
#endif
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifdef ZHLT_XASH
|
|
vec3_t direction;
|
|
#ifdef HLRAD_SUNSPREAD
|
|
VectorCopy (l->sunnormals[j], direction);
|
|
#else
|
|
VectorCopy (l->normal, direction);
|
|
#endif
|
|
#endif
|
|
vec3_t add_one;
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
if (lighting_diversify)
|
|
{
|
|
dot = lighting_scale * pow (dot, lighting_power);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_SUNSPREAD
|
|
VectorScale (l->intensity, dot * l->sunnormalweights[j], add_one);
|
|
#else
|
|
VectorScale(l->intensity, dot, add_one);
|
|
#endif
|
|
#ifdef HLRAD_HULLU
|
|
VectorMultiply(add_one, transparency, add_one);
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
// add to the total brightness of this sample
|
|
style = l->style;
|
|
if (opaquestyle != -1)
|
|
{
|
|
if (style == 0 || style == opaquestyle)
|
|
style = opaquestyle;
|
|
else
|
|
continue; // dynamic light of other styles hits this toggleable opaque entity, then it completely vanishes.
|
|
}
|
|
VectorAdd (adds[style], add_one, adds[style]);
|
|
#ifdef ZHLT_XASH
|
|
vec_t avg = VectorAvg (add_one);
|
|
VectorMA (adds_direction[style], avg, direction, adds_direction[style]);
|
|
#endif
|
|
#else
|
|
// add to the contribution of this light
|
|
VectorAdd (add, add_one, add);
|
|
#ifdef ZHLT_XASH
|
|
vec_t avg = VectorAvg (add_one);
|
|
VectorMA (add_direction, avg, direction, add_direction);
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_SUNSPREAD
|
|
} // (loop over the normals)
|
|
#endif
|
|
}
|
|
while (0);
|
|
do // add sky light
|
|
{
|
|
#ifdef HLRAD_GatherPatchLight
|
|
// check step
|
|
step_match = 0;
|
|
#ifdef HLRAD_SOFTSKY
|
|
if (g_softsky)
|
|
step_match = 1;
|
|
#endif
|
|
#ifdef HLRAD_FASTMODE
|
|
if (g_fastmode)
|
|
step_match = 1;
|
|
#endif
|
|
if (step != step_match)
|
|
continue;
|
|
#endif
|
|
// check intensity
|
|
if (g_indirect_sun <= 0.0 ||
|
|
VectorCompare (
|
|
#ifdef HLRAD_WHOME
|
|
l->diffuse_intensity,
|
|
#else
|
|
l->intensity,
|
|
#endif
|
|
vec3_origin)
|
|
#ifdef HLRAD_SUNDIFFUSE
|
|
&& VectorCompare (l->diffuse_intensity2, vec3_origin)
|
|
#endif
|
|
)
|
|
continue;
|
|
|
|
vec3_t sky_intensity;
|
|
#ifndef HLRAD_SUNDIFFUSE
|
|
#ifndef HLRAD_SOFTSKY
|
|
#ifdef HLRAD_WHOME
|
|
VectorScale (l->diffuse_intensity, g_indirect_sun / (NUMVERTEXNORMALS * 2), sky_intensity);
|
|
#else
|
|
VectorScale (l->intensity, g_indirect_sun / (NUMVERTEXNORMALS * 2), sky_intensity);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
// loop over the normals
|
|
#ifdef HLRAD_SOFTSKY
|
|
vec3_t *skynormals = g_skynormals[g_softsky?SKYLEVEL_SOFTSKYON:SKYLEVEL_SOFTSKYOFF];
|
|
vec_t *skyweights = g_skynormalsizes[g_softsky?SKYLEVEL_SOFTSKYON:SKYLEVEL_SOFTSKYOFF];
|
|
for (int j = 0; j < g_numskynormals[g_softsky?SKYLEVEL_SOFTSKYON:SKYLEVEL_SOFTSKYOFF]; j++)
|
|
#else
|
|
for (int j = 0; j < NUMVERTEXNORMALS; j++)
|
|
#endif
|
|
{
|
|
// make sure the angle is okay
|
|
#ifdef HLRAD_SOFTSKY
|
|
dot = -DotProduct (normal, skynormals[j]);
|
|
#else
|
|
dot = -DotProduct (normal, r_avertexnormals[j]);
|
|
#endif
|
|
if (dot <= NORMAL_EPSILON) //ON_EPSILON / 10 //--vluzacn
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// search back to see if we can hit a sky brush
|
|
#ifdef HLRAD_SOFTSKY
|
|
#ifdef ZHLT_LARGERANGE
|
|
VectorScale (skynormals[j], -BOGUS_RANGE, delta);
|
|
#else
|
|
VectorScale (skynormals[j], -10000, delta);
|
|
#endif
|
|
#else
|
|
#ifdef ZHLT_LARGERANGE
|
|
VectorScale (r_avertexnormals[j], -BOGUS_RANGE, delta);
|
|
#else
|
|
VectorScale (r_avertexnormals[j], -10000, delta);
|
|
#endif
|
|
#endif
|
|
VectorAdd(pos, delta, delta);
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
vec3_t skyhit;
|
|
VectorCopy (delta, skyhit);
|
|
#endif
|
|
if (TestLine(pos, delta
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
, skyhit
|
|
#endif
|
|
) != CONTENTS_SKY)
|
|
{
|
|
continue; // occluded
|
|
}
|
|
|
|
#ifdef HLRAD_OPAQUE_DIFFUSE_FIX
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency;
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
int opaquestyle;
|
|
#endif
|
|
if (TestSegmentAgainstOpaqueList(pos,
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
skyhit
|
|
#else
|
|
delta
|
|
#endif
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
, opaquestyle
|
|
#endif
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
#endif /*HLRAD_OPAQUE_DIFFUSE_FIX*/
|
|
|
|
#ifdef ZHLT_XASH
|
|
vec3_t direction;
|
|
#ifdef HLRAD_SOFTSKY
|
|
VectorCopy (skynormals[j], direction);
|
|
#else
|
|
VectorCopy (r_avertexnormals[j], direction);
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_SUNDIFFUSE
|
|
#ifdef HLRAD_SOFTSKY
|
|
vec_t factor = qmin (qmax (0.0, (1 - DotProduct (l->normal, skynormals[j])) / 2), 1.0); // how far this piece of sky has deviated from the sun
|
|
#else
|
|
vec_t factor = qmin (qmax (0.0, (1 - DotProduct (l->normal, r_avertexnormals[j])) / 2), 1.0); // how far this piece of sky has deviated from the sun
|
|
#endif
|
|
VectorScale (l->diffuse_intensity, 1 - factor, sky_intensity);
|
|
VectorMA (sky_intensity, factor, l->diffuse_intensity2, sky_intensity);
|
|
#ifdef HLRAD_SOFTSKY
|
|
VectorScale (sky_intensity, skyweights[j] * g_indirect_sun / 2, sky_intensity);
|
|
#else
|
|
VectorScale (sky_intensity, g_indirect_sun / (NUMVERTEXNORMALS * 2), sky_intensity);
|
|
#endif
|
|
#else
|
|
#ifdef HLRAD_SOFTSKY
|
|
#ifdef HLRAD_WHOME
|
|
VectorScale (l->diffuse_intensity, skyweights[j] * g_indirect_sun / 2, sky_intensity);
|
|
#else
|
|
VectorScale (l->intensity, skyweights[j] * g_indirect_sun / 2, sky_intensity);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
vec3_t add_one;
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
if (lighting_diversify)
|
|
{
|
|
dot = lighting_scale * pow (dot, lighting_power);
|
|
}
|
|
#endif
|
|
VectorScale(sky_intensity, dot, add_one);
|
|
#ifdef HLRAD_OPAQUE_DIFFUSE_FIX
|
|
#ifdef HLRAD_HULLU
|
|
VectorMultiply(add_one, transparency, add_one);
|
|
#endif
|
|
#endif /*HLRAD_OPAQUE_DIFFUSE_FIX*/
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
// add to the total brightness of this sample
|
|
style = l->style;
|
|
if (opaquestyle != -1)
|
|
{
|
|
if (style == 0 || style == opaquestyle)
|
|
style = opaquestyle;
|
|
else
|
|
continue; // dynamic light of other styles hits this toggleable opaque entity, then it completely vanishes.
|
|
}
|
|
VectorAdd (adds[style], add_one, adds[style]);
|
|
#ifdef ZHLT_XASH
|
|
vec_t avg = VectorAvg (add_one);
|
|
VectorMA (adds_direction[style], avg, direction, adds_direction[style]);
|
|
#endif
|
|
#else
|
|
// add to the contribution of this light
|
|
VectorAdd(add, add_one, add);
|
|
#ifdef ZHLT_XASH
|
|
vec_t avg = VectorAvg (add_one);
|
|
VectorMA (add_direction, avg, direction, add_direction);
|
|
#endif
|
|
#endif
|
|
} // (loop over the normals)
|
|
|
|
}
|
|
while (0);
|
|
|
|
#else /*HLRAD_MULTISKYLIGHT*/
|
|
// 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 <= NORMAL_EPSILON) //ON_EPSILON / 10 //--vluzacn
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// search back to see if we can hit a sky brush
|
|
#ifdef ZHLT_LARGERANGE
|
|
VectorScale(l->normal, -BOGUS_RANGE, delta);
|
|
#else
|
|
VectorScale(l->normal, -10000, delta);
|
|
#endif
|
|
VectorAdd(pos, delta, delta);
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
vec3_t skyhit;
|
|
VectorCopy (delta, skyhit);
|
|
#endif
|
|
if (TestLine(pos, delta
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
, skyhit
|
|
#endif
|
|
) != CONTENTS_SKY)
|
|
{
|
|
continue; // occluded
|
|
}
|
|
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency = {1.0,1.0,1.0};
|
|
#endif
|
|
if (TestSegmentAgainstOpaqueList(pos,
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
skyhit
|
|
#else
|
|
delta
|
|
#endif
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
if (lighting_diversify)
|
|
{
|
|
dot = lighting_scale * pow (dot, lighting_power);
|
|
}
|
|
#endif
|
|
VectorScale(l->intensity, dot, add);
|
|
#ifdef HLRAD_HULLU
|
|
VectorMultiply(add, transparency, add);
|
|
#endif
|
|
|
|
#endif /*HLRAD_MULTISKYLIGHT*/
|
|
}
|
|
else // not emit_skylight
|
|
{
|
|
#ifdef HLRAD_GatherPatchLight
|
|
step_match = (int)l->topatch;
|
|
if (step != step_match)
|
|
continue;
|
|
#endif
|
|
#ifdef HLRAD_ALLOWZEROBRIGHTNESS
|
|
if (!(l->intensity[0] || l->intensity[1] || l->intensity[2]))
|
|
continue;
|
|
#endif
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
VectorCopy (l->origin, testline_origin);
|
|
#endif
|
|
float denominator;
|
|
|
|
VectorSubtract(l->origin, pos, delta);
|
|
#ifdef HLRAD_ACCURATEBOUNCE_TEXLIGHT
|
|
if (l->type == emit_surface)
|
|
{
|
|
// move emitter back to its plane
|
|
VectorMA (delta, -PATCH_HUNT_OFFSET, l->normal, delta);
|
|
}
|
|
#endif
|
|
dist = VectorNormalize(delta);
|
|
dot = DotProduct(delta, normal);
|
|
// if (dot <= 0.0)
|
|
// continue;
|
|
#ifndef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
if (dot <= NORMAL_EPSILON) //ON_EPSILON / 10 //--vluzacn
|
|
{
|
|
continue; // behind sample surface
|
|
}
|
|
#endif
|
|
|
|
if (dist < 1.0)
|
|
{
|
|
dist = 1.0;
|
|
}
|
|
|
|
#ifdef HLRAD_ARG_MISC
|
|
denominator = dist * dist * l->fade;
|
|
#else
|
|
// Variable power falloff (1 = inverse linear, 2 = inverse square
|
|
denominator = dist * l->fade;
|
|
if (l->falloff == 2)
|
|
{
|
|
denominator *= dist;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
vec3_t add;
|
|
#endif
|
|
#ifdef ZHLT_XASH
|
|
vec3_t direction;
|
|
VectorSubtract (vec3_origin, delta, direction);
|
|
#ifdef HLRAD_ACCURATEBOUNCE_TEXLIGHT
|
|
if ((-dot) > 0)
|
|
{
|
|
// reflect the direction back (this is not ideal!)
|
|
VectorMA (direction, -(-dot) * 2, normal, direction);
|
|
}
|
|
#endif
|
|
#endif
|
|
switch (l->type)
|
|
{
|
|
case emit_point:
|
|
{
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
if (dot <= NORMAL_EPSILON)
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_ARG_MISC
|
|
vec_t denominator = dist * dist * l->fade;
|
|
#else
|
|
// Variable power falloff (1 = inverse linear, 2 = inverse square
|
|
vec_t denominator = dist * l->fade;
|
|
|
|
if (l->falloff == 2)
|
|
{
|
|
denominator *= dist;
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
if (lighting_diversify)
|
|
{
|
|
dot = lighting_scale * pow (dot, lighting_power);
|
|
}
|
|
#endif
|
|
ratio = dot / denominator;
|
|
VectorScale(l->intensity, ratio, add);
|
|
break;
|
|
}
|
|
|
|
case emit_surface:
|
|
{
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
bool light_behind_surface = false;
|
|
if (dot <= NORMAL_EPSILON)
|
|
{
|
|
light_behind_surface = true;
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
if (lighting_diversify
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
&& !light_behind_surface
|
|
#endif
|
|
)
|
|
{
|
|
dot = lighting_scale * pow (dot, lighting_power);
|
|
}
|
|
#endif
|
|
dot2 = -DotProduct(delta, l->normal);
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
// discard the texlight if the spot is too close to the texlight plane
|
|
if (l->texlightgap > 0)
|
|
{
|
|
vec_t test;
|
|
|
|
test = dot2 * dist; // distance from spot to texlight plane;
|
|
test -= l->texlightgap * fabs (DotProduct (l->normal, texlightgap_textoworld[0])); // maximum distance reduction if the spot is allowed to shift l->texlightgap pixels along s axis
|
|
test -= l->texlightgap * fabs (DotProduct (l->normal, texlightgap_textoworld[1])); // maximum distance reduction if the spot is allowed to shift l->texlightgap pixels along t axis
|
|
if (test < -ON_EPSILON)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_CUSTOMTEXLIGHT
|
|
#ifdef HLRAD_ACCURATEBOUNCE_TEXLIGHT
|
|
if (dot2 * dist <= MINIMUM_PATCH_DISTANCE)
|
|
{
|
|
continue;
|
|
}
|
|
vec_t range = l->patch_emitter_range;
|
|
if (l->stopdot > 0.0) // stopdot2 > 0.0 or stopdot > 0.0
|
|
{
|
|
vec_t range_scale;
|
|
range_scale = 1 - l->stopdot2 * l->stopdot2;
|
|
range_scale = 1 / sqrt (qmax (NORMAL_EPSILON, range_scale));
|
|
// range_scale = 1 / sin (cone2)
|
|
range_scale = qmin (range_scale, 2); // restrict this to 2, because skylevel has limit.
|
|
range *= range_scale; // because smaller cones are more likely to create the ugly grid effect.
|
|
|
|
if (dot2 <= l->stopdot2 + NORMAL_EPSILON)
|
|
{
|
|
if (dist >= range) // use the old method, which will merely give 0 in this case
|
|
{
|
|
continue;
|
|
}
|
|
ratio = 0.0;
|
|
}
|
|
else if (dot2 <= l->stopdot)
|
|
{
|
|
ratio = dot * dot2 * (dot2 - l->stopdot2) / (dist * dist * (l->stopdot - l->stopdot2));
|
|
}
|
|
else
|
|
{
|
|
ratio = dot * dot2 / (dist * dist);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ratio = dot * dot2 / (dist * dist);
|
|
}
|
|
#else
|
|
if (dot2 <= l->stopdot2 + NORMAL_EPSILON)
|
|
{
|
|
continue;
|
|
}
|
|
if (dot2 <= l->stopdot)
|
|
{
|
|
ratio = dot * dot2 * (dot2 - l->stopdot2) / (dist * dist * (l->stopdot - l->stopdot2));
|
|
}
|
|
else
|
|
{
|
|
ratio = dot * dot2 / (dist * dist);
|
|
}
|
|
#endif
|
|
#else
|
|
#ifdef HLRAD_ACCURATEBOUNCE_TEXLIGHT
|
|
if (dot2 * dist <= MINIMUM_PATCH_DISTANCE)
|
|
{
|
|
continue;
|
|
}
|
|
vec_t range = l->patch_emitter_range;
|
|
ratio = dot * dot2 / (dist * dist * g_fade);
|
|
#else
|
|
if (dot2 <= NORMAL_EPSILON) //ON_EPSILON / 10 //--vluzacn
|
|
{
|
|
continue; // behind light surface
|
|
}
|
|
|
|
#ifdef HLRAD_ARG_MISC
|
|
vec_t denominator = dist * dist * g_fade;
|
|
#else
|
|
// Variable power falloff (1 = inverse linear, 2 = inverse square
|
|
vec_t denominator = dist * g_fade;
|
|
if (g_falloff == 2)
|
|
{
|
|
denominator *= dist;
|
|
}
|
|
#endif
|
|
ratio = dot * dot2 / denominator;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef HLRAD_TEXLIGHT_SPOTS_FIX
|
|
// analogous to the one in MakeScales
|
|
// 0.4f is tested to be able to fully eliminate bright spots
|
|
if (ratio * l->patch_area > 0.4f)
|
|
{
|
|
ratio = 0.4f / l->patch_area;
|
|
}
|
|
#ifdef HLRAD_ACCURATEBOUNCE_TEXLIGHT
|
|
if (dist < range - ON_EPSILON)
|
|
{ // do things slow
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
if (light_behind_surface)
|
|
{
|
|
dot = 0.0;
|
|
ratio = 0.0;
|
|
}
|
|
GetAlternateOrigin (pos, normal, l->patch, testline_origin);
|
|
#endif
|
|
vec_t sightarea;
|
|
int skylevel = l->patch->emitter_skylevel;
|
|
#ifdef HLRAD_CUSTOMTEXLIGHT
|
|
if (l->stopdot > 0.0) // stopdot2 > 0.0 or stopdot > 0.0
|
|
{
|
|
const vec_t *emitnormal = getPlaneFromFaceNumber (l->patch->faceNumber)->normal;
|
|
if (l->stopdot2 >= 0.8) // about 37deg
|
|
{
|
|
skylevel += 1; // because the range is larger
|
|
}
|
|
sightarea = CalcSightArea_SpotLight (pos, normal, l->patch->winding, emitnormal, l->stopdot, l->stopdot2, skylevel
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, lighting_power, lighting_scale
|
|
#endif
|
|
); // because we have doubled the range
|
|
}
|
|
else
|
|
{
|
|
sightarea = CalcSightArea (pos, normal, l->patch->winding, skylevel
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, lighting_power, lighting_scale
|
|
#endif
|
|
);
|
|
}
|
|
#else
|
|
sightarea = CalcSightArea (pos, normal, l->patch->winding, skylevel
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, lighting_power, lighting_scale
|
|
#endif
|
|
);
|
|
#endif
|
|
|
|
vec_t frac = dist / range;
|
|
frac = (frac - 0.5) * 2; // make a smooth transition between the two methods
|
|
frac = qmax (0, qmin (frac, 1));
|
|
|
|
vec_t ratio2 = (sightarea / l->patch_area); // because l->patch->area has been multiplied into l->intensity
|
|
#ifndef HLRAD_CUSTOMTEXLIGHT
|
|
// Variable power falloff (1 = inverse linear, 2 = inverse square
|
|
ratio2 /= g_fade;
|
|
#endif
|
|
ratio = frac * ratio + (1 - frac) * ratio2;
|
|
}
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
else
|
|
{
|
|
if (light_behind_surface)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
VectorScale(l->intensity, ratio, add);
|
|
break;
|
|
}
|
|
|
|
case emit_spotlight:
|
|
{
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
if (dot <= NORMAL_EPSILON)
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
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;
|
|
#ifndef HLRAD_ARG_MISC
|
|
if (l->falloff == 2)
|
|
#endif
|
|
{
|
|
denominator *= dist;
|
|
}
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
if (lighting_diversify)
|
|
{
|
|
dot = lighting_scale * pow (dot, lighting_power);
|
|
}
|
|
#endif
|
|
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;
|
|
}
|
|
}
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
if (TestLine (pos,
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
testline_origin
|
|
#else
|
|
l->origin
|
|
#endif
|
|
) != CONTENTS_EMPTY)
|
|
{
|
|
continue;
|
|
}
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency;
|
|
#endif
|
|
int opaquestyle;
|
|
if (TestSegmentAgainstOpaqueList (pos,
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
testline_origin
|
|
#else
|
|
l->origin
|
|
#endif
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
, opaquestyle))
|
|
{
|
|
continue;
|
|
}
|
|
#ifdef HLRAD_HULLU
|
|
VectorMultiply (add, transparency, add);
|
|
#endif
|
|
// add to the total brightness of this sample
|
|
style = l->style;
|
|
if (opaquestyle != -1)
|
|
{
|
|
if (style == 0 || style == opaquestyle)
|
|
style = opaquestyle;
|
|
else
|
|
continue; // dynamic light of other styles hits this toggleable opaque entity, then it completely vanishes.
|
|
}
|
|
VectorAdd (adds[style], add, adds[style]);
|
|
#ifdef ZHLT_XASH
|
|
vec_t avg = VectorAvg (add);
|
|
VectorMA (adds_direction[style], avg, direction, adds_direction[style]);
|
|
#endif
|
|
#else
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (direcion, add_direction); // we'll scale it later
|
|
#endif
|
|
#endif
|
|
} // end emit_skylight
|
|
|
|
#ifndef HLRAD_OPAQUE_STYLE
|
|
#ifndef HLRAD_STYLE_CORING
|
|
if (VectorMaximum(add) > (l->style ? g_coring : 0))
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency = {1.0,1.0,1.0};
|
|
#endif
|
|
|
|
if (l->type != emit_skylight && TestLine(pos,
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
testline_origin
|
|
#else
|
|
l->origin
|
|
#endif
|
|
) != CONTENTS_EMPTY)
|
|
{
|
|
continue; // occluded
|
|
}
|
|
|
|
if (l->type != emit_skylight)
|
|
{ // Don't test from light_environment entities to face, the special sky code occludes correctly
|
|
if (TestSegmentAgainstOpaqueList(pos,
|
|
#ifdef HLRAD_ACCURATEBOUNCE_ALTERNATEORIGIN
|
|
testline_origin
|
|
#else
|
|
l->origin
|
|
#endif
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
#ifdef HLRAD_OPAQUE_STYLE
|
|
, opaquestyle
|
|
#endif
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
#ifdef HLRAD_OPACITY
|
|
//VectorScale(add, l_opacity, add);
|
|
#endif
|
|
|
|
#ifdef HLRAD_STYLE_CORING
|
|
#ifdef HLRAD_HULLU
|
|
if (l->type != emit_skylight)
|
|
{
|
|
VectorMultiply (add, transparency, add);
|
|
}
|
|
#endif
|
|
#ifdef ZHLT_XASH
|
|
if (l->type != emit_skylight)
|
|
{
|
|
vec3_t avg = VectorAvg (add);
|
|
VectorScale (add_direction, avg, add_direction);
|
|
}
|
|
#endif
|
|
VectorAdd (adds[l->style], add, adds[l->style]);
|
|
#ifdef ZHLT_XASH
|
|
VectorAdd (adds_direction[l->style], add_direction, adds_direction[l->style]);
|
|
#endif
|
|
#else
|
|
for (style_index = 0; style_index < MAXLIGHTMAPS; style_index++)
|
|
{
|
|
if (styles[style_index] == l->style || styles[style_index] == 255)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (style_index == MAXLIGHTMAPS)
|
|
{
|
|
#ifdef HLRAD_READABLE_EXCEEDSTYLEWARNING
|
|
if (++stylewarningcount >= stylewarningnext)
|
|
{
|
|
stylewarningnext = stylewarningcount * 2;
|
|
Warning("Too many direct light styles on a face(%f,%f,%f)", pos[0], pos[1], pos[2]);
|
|
Warning(" total %d warnings for too many styles", stylewarningcount);
|
|
}
|
|
#else
|
|
Warning("Too many direct light styles on a face(%f,%f,%f)", pos[0], pos[1], pos[2]);
|
|
#endif
|
|
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]);
|
|
#endif
|
|
}
|
|
#endif /*#ifndef HLRAD_OPAQUE_STYLE*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef HLRAD_MULTISKYLIGHT
|
|
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 <= NORMAL_EPSILON) //ON_EPSILON / 10 //--vluzacn
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// search back to see if we can hit a sky brush
|
|
#ifdef ZHLT_LARGERANGE
|
|
VectorScale(r_avertexnormals[j], -BOGUS_RANGE, delta);
|
|
#else
|
|
VectorScale(r_avertexnormals[j], -10000, delta);
|
|
#endif
|
|
VectorAdd(pos, delta, delta);
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
vec3_t skyhit;
|
|
VectorCopy (delta, skyhit);
|
|
#endif
|
|
if (TestLine(pos, delta
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
, skyhit
|
|
#endif
|
|
) != CONTENTS_SKY)
|
|
{
|
|
continue; // occluded
|
|
}
|
|
|
|
#ifdef HLRAD_OPAQUE_DIFFUSE_FIX
|
|
#ifdef HLRAD_HULLU
|
|
vec3_t transparency = {1.0,1.0,1.0};
|
|
#endif
|
|
if (TestSegmentAgainstOpaqueList(pos,
|
|
#ifdef HLRAD_OPAQUEINSKY_FIX
|
|
skyhit
|
|
#else
|
|
delta
|
|
#endif
|
|
#ifdef HLRAD_HULLU
|
|
, transparency
|
|
#endif
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
#endif /*HLRAD_OPAQUE_DIFFUSE_FIX*/
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
if (lighting_diversify)
|
|
{
|
|
dot = lighting_scale * pow (dot, lighting_power);
|
|
}
|
|
#endif
|
|
VectorScale(sky_intensity, dot, add);
|
|
#ifdef HLRAD_OPAQUE_DIFFUSE_FIX
|
|
#ifdef HLRAD_HULLU
|
|
VectorMultiply(add, transparency, add);
|
|
#endif
|
|
#endif /*HLRAD_OPAQUE_DIFFUSE_FIX*/
|
|
VectorAdd(total, add, total);
|
|
}
|
|
if (VectorMaximum(total) > 0)
|
|
{
|
|
#ifdef HLRAD_STYLE_CORING
|
|
VectorAdd (adds[sky_used->style], total, adds[sky_used->style]);
|
|
#else
|
|
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)
|
|
{
|
|
#ifdef HLRAD_READABLE_EXCEEDSTYLEWARNING
|
|
if (++stylewarningcount >= stylewarningnext)
|
|
{
|
|
stylewarningnext = stylewarningcount * 2;
|
|
Warning("Too many direct light styles on a face(%f,%f,%f)\n", pos[0], pos[1], pos[2]);
|
|
Warning(" total %d warnings for too many styles", stylewarningcount);
|
|
}
|
|
#else
|
|
Warning("Too many direct light styles on a face(%f,%f,%f)\n", pos[0], pos[1], pos[2]);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (styles[style_index] == 255)
|
|
{
|
|
styles[style_index] = sky_used->style;
|
|
}
|
|
|
|
VectorAdd(sample[style_index], total, sample[style_index]);
|
|
#endif
|
|
}
|
|
}
|
|
#endif /*HLRAD_MULTISKYLIGHT*/
|
|
#ifdef HLRAD_STYLE_CORING
|
|
for (style = 0; style < ALLSTYLES; ++style)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
if (VectorMaximum(adds[style]) > g_corings[style] * 0.1)
|
|
#else
|
|
if (VectorMaximum(adds[style]) > g_corings[style])
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (style_index = 0; style_index < ALLSTYLES; style_index++)
|
|
#else
|
|
for (style_index = 0; style_index < MAXLIGHTMAPS; style_index++)
|
|
#endif
|
|
{
|
|
if (styles[style_index] == style || styles[style_index] == 255)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef HLRAD_AUTOCORING
|
|
if (style_index == ALLSTYLES) // shouldn't happen
|
|
#else
|
|
if (style_index == MAXLIGHTMAPS)
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_READABLE_EXCEEDSTYLEWARNING
|
|
if (++stylewarningcount >= stylewarningnext)
|
|
{
|
|
stylewarningnext = stylewarningcount * 2;
|
|
Warning("Too many direct light styles on a face(%f,%f,%f)", pos[0], pos[1], pos[2]);
|
|
Warning(" total %d warnings for too many styles", stylewarningcount);
|
|
}
|
|
#else
|
|
Warning("Too many direct light styles on a face(%f,%f,%f)", pos[0], pos[1], pos[2]);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (styles[style_index] == 255)
|
|
{
|
|
styles[style_index] = style;
|
|
}
|
|
|
|
VectorAdd(sample[style_index], adds[style], sample[style_index]);
|
|
#ifdef ZHLT_XASH
|
|
VectorAdd (sample_direction[style_index], adds_direction[style], sample_direction[style_index]);
|
|
#endif
|
|
}
|
|
#ifdef HLRAD_AUTOCORING
|
|
else
|
|
{
|
|
if (VectorMaximum (adds[style]) > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
ThreadLock ();
|
|
if (VectorMaximum (adds[style]) > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = VectorMaximum (adds[style]);
|
|
VectorCopy (pos, g_maxdiscardedpos);
|
|
}
|
|
ThreadUnlock ();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// =====================================================================================
|
|
// AddSampleToPatch
|
|
// Take the sample's collected light and add it back into the apropriate patch for the radiosity pass.
|
|
// =====================================================================================
|
|
#ifdef HLRAD_ACCURATEBOUNCE_SAMPLELIGHT
|
|
static void AddSamplesToPatches (const sample_t **samples, const unsigned char *styles, int facenum, const lightinfo_t *l)
|
|
{
|
|
#ifndef HLRAD_GatherPatchLight
|
|
if (g_numbounce == 0)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
patch_t *patch;
|
|
int i, j, m, k;
|
|
int numtexwindings;
|
|
Winding **texwindings;
|
|
|
|
numtexwindings = 0;
|
|
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
|
|
{
|
|
numtexwindings++;
|
|
}
|
|
texwindings = (Winding **)malloc (numtexwindings * sizeof (Winding *));
|
|
hlassume (texwindings != NULL, assume_NoMemory);
|
|
|
|
// translate world winding into winding in s,t plane
|
|
for (j = 0, patch = g_face_patches[facenum]; j < numtexwindings; j++, patch = patch->next)
|
|
{
|
|
Winding *w = new Winding (patch->winding->m_NumPoints);
|
|
for (int x = 0; x < w->m_NumPoints; x++)
|
|
{
|
|
vec_t s, t;
|
|
SetSTFromSurf (l, patch->winding->m_Points[x], s, t);
|
|
w->m_Points[x][0] = s;
|
|
w->m_Points[x][1] = t;
|
|
w->m_Points[x][2] = 0.0;
|
|
}
|
|
w->RemoveColinearPoints ();
|
|
texwindings[j] = w;
|
|
}
|
|
|
|
for (i = 0; i < l->numsurfpt; i++)
|
|
{
|
|
// prepare clip planes
|
|
vec_t s_vec, t_vec;
|
|
s_vec = l->texmins[0] * TEXTURE_STEP + (i % (l->texsize[0] + 1)) * TEXTURE_STEP;
|
|
t_vec = l->texmins[1] * TEXTURE_STEP + (i / (l->texsize[0] + 1)) * TEXTURE_STEP;
|
|
|
|
dplane_t clipplanes[4];
|
|
VectorClear (clipplanes[0].normal);
|
|
clipplanes[0].normal[0] = 1;
|
|
clipplanes[0].dist = s_vec - 0.5 * TEXTURE_STEP;
|
|
VectorClear (clipplanes[1].normal);
|
|
clipplanes[1].normal[0] = -1;
|
|
clipplanes[1].dist = -(s_vec + 0.5 * TEXTURE_STEP);
|
|
VectorClear (clipplanes[2].normal);
|
|
clipplanes[2].normal[1] = 1;
|
|
clipplanes[2].dist = t_vec - 0.5 * TEXTURE_STEP;
|
|
VectorClear (clipplanes[3].normal);
|
|
clipplanes[3].normal[1] = -1;
|
|
clipplanes[3].dist = -(t_vec + 0.5 * TEXTURE_STEP);
|
|
|
|
// clip each patch
|
|
for (j = 0, patch = g_face_patches[facenum]; j < numtexwindings; j++, patch = patch->next)
|
|
{
|
|
Winding *w = new Winding (*texwindings[j]);
|
|
for (k = 0; k < 4; k++)
|
|
{
|
|
if (w->m_NumPoints)
|
|
{
|
|
w->Clip (clipplanes[k], false);
|
|
}
|
|
}
|
|
if (w->m_NumPoints)
|
|
{
|
|
// add sample to patch
|
|
vec_t area = w->getArea () / (TEXTURE_STEP * TEXTURE_STEP);
|
|
patch->samples += area;
|
|
for (m = 0; m < ALLSTYLES && styles[m] != 255; m++)
|
|
{
|
|
int style = styles[m];
|
|
const sample_t *s = &samples[m][i];
|
|
for (k = 0; k < ALLSTYLES && patch->totalstyle_all[k] != 255; k++)
|
|
{
|
|
if (patch->totalstyle_all[k] == style)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (k == ALLSTYLES)
|
|
{
|
|
#ifdef HLRAD_READABLE_EXCEEDSTYLEWARNING
|
|
if (++stylewarningcount >= stylewarningnext)
|
|
{
|
|
stylewarningnext = stylewarningcount * 2;
|
|
Warning("Too many direct light styles on a face(?,?,?)\n");
|
|
Warning(" total %d warnings for too many styles", stylewarningcount);
|
|
}
|
|
#else
|
|
Warning("Too many direct light styles on a face(?,?,?)\n");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (patch->totalstyle_all[k] == 255)
|
|
{
|
|
patch->totalstyle_all[k] = style;
|
|
}
|
|
VectorMA (patch->samplelight_all[k], area, s->light, patch->samplelight_all[k]);
|
|
#ifdef ZHLT_XASH
|
|
VectorMA (patch->samplelight_all_direction[k], area, s->light_direction, patch->samplelight_all_direction[k]);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
delete w;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < numtexwindings; j++)
|
|
{
|
|
delete texwindings[j];
|
|
}
|
|
free (texwindings);
|
|
}
|
|
#else
|
|
#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;
|
|
|
|
#ifndef HLRAD_GatherPatchLight
|
|
if (g_numbounce == 0)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
}
|
|
#ifdef HLRAD_AUTOCORING
|
|
if (style == 0)
|
|
{
|
|
patch->samples++;
|
|
}
|
|
#endif
|
|
|
|
// add the sample to the patch
|
|
#ifdef ZHLT_TEXLIGHT
|
|
//LRC:
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (i = 0; i < ALLSTYLES && patch->totalstyle_all[i] != 255; i++)
|
|
{
|
|
if (patch->totalstyle_all[i] == style)
|
|
break;
|
|
}
|
|
if (i == ALLSTYLES) // shouldn't happen
|
|
#else
|
|
for (i = 0; i < MAXLIGHTMAPS && patch->totalstyle[i] != 255; i++)
|
|
{
|
|
if (patch->totalstyle[i] == style)
|
|
break;
|
|
}
|
|
if (i == MAXLIGHTMAPS)
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_READABLE_EXCEEDSTYLEWARNING
|
|
if (++stylewarningcount >= stylewarningnext)
|
|
{
|
|
stylewarningnext = stylewarningcount * 2;
|
|
Warning("Too many direct light styles on a face(?,?,?)\n");
|
|
Warning(" total %d warnings for too many styles", stylewarningcount);
|
|
}
|
|
#else
|
|
Warning("Too many direct light styles on a face(?,?,?)\n");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
if (patch->totalstyle_all[i] == 255)
|
|
{
|
|
patch->totalstyle_all[i] = style;
|
|
}
|
|
VectorAdd(patch->samplelight_all[i], s->light, patch->samplelight_all[i]);
|
|
#ifdef ZHLT_XASH
|
|
VectorAdd (patch->samplelight_all_direction[i], s->light_direction, patch->samplelight_all_direction[i]);
|
|
#endif
|
|
#else
|
|
if (patch->totalstyle[i] == 255)
|
|
{
|
|
patch->totalstyle[i] = style;
|
|
}
|
|
|
|
patch->samples[i]++;
|
|
VectorAdd(patch->samplelight[i], s->light, patch->samplelight[i]);
|
|
#endif
|
|
}
|
|
//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
|
|
}
|
|
#endif
|
|
|
|
// =====================================================================================
|
|
// GetPhongNormal
|
|
// =====================================================================================
|
|
void GetPhongNormal(int facenum, const vec3_t spot, vec3_t phongnormal)
|
|
{
|
|
int j;
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
int s; // split every edge into two parts
|
|
#endif
|
|
const dface_t* f = g_dfaces + facenum;
|
|
const dplane_t* p = getPlaneFromFace(f);
|
|
vec3_t facenormal;
|
|
|
|
VectorCopy(p->normal, facenormal);
|
|
VectorCopy(facenormal, phongnormal);
|
|
|
|
#ifndef HLRAD_CUSTOMSMOOTH
|
|
if (g_smoothing_threshold > 0.0)
|
|
#endif
|
|
{
|
|
// 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)
|
|
{
|
|
#ifdef HLRAD_NEGATIVEDIVIDEND_MISCFIX
|
|
prev_edge = f->firstedge + ((j + f->numedges - 1) % f->numedges);
|
|
#else
|
|
prev_edge = f->firstedge + ((j - 1) % f->numedges);
|
|
#endif
|
|
}
|
|
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)];
|
|
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
if ((!es->smooth || es->coplanar) && (!es1->smooth || es1->coplanar) && (!es2->smooth || es2->coplanar))
|
|
#else
|
|
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)))
|
|
#endif
|
|
{
|
|
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);
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
for (s = 0; s < 2; s++)
|
|
{
|
|
vec3_t s1, s2;
|
|
if (s == 0)
|
|
{
|
|
VectorCopy(p1, s1);
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(p2, s1);
|
|
}
|
|
|
|
VectorAdd(p1,p2,s2); // edge center
|
|
VectorScale(s2,0.5,s2);
|
|
|
|
VectorSubtract(s1, g_face_centroids[facenum], v1);
|
|
VectorSubtract(s2, g_face_centroids[facenum], v2);
|
|
#else
|
|
|
|
// 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);
|
|
#endif
|
|
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)
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
if (a1 >= -0.01 && a2 >= -0.01)
|
|
#else
|
|
if (a1 >= 0.0 && a2 >= 0.0)
|
|
#endif
|
|
{
|
|
// calculate distance from edge to pos
|
|
vec3_t n1, n2;
|
|
vec3_t temp;
|
|
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
if (es->smooth)
|
|
if (s == 0)
|
|
{VectorCopy(es->vertex_normal[e>0?0:1], n1);}
|
|
else
|
|
{VectorCopy(es->vertex_normal[e>0?1:0], n1);}
|
|
else if (s == 0 && es1->smooth)
|
|
{VectorCopy(es1->vertex_normal[e1>0?1:0], n1);}
|
|
else if (s == 1 && es2->smooth)
|
|
{VectorCopy(es2->vertex_normal[e2>0?0:1], n1);}
|
|
else
|
|
{VectorCopy(facenormal, n1);}
|
|
|
|
if (es->smooth)
|
|
{VectorCopy(es->interface_normal, n2);}
|
|
else
|
|
{VectorCopy(facenormal, n2);}
|
|
#else
|
|
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);
|
|
#endif
|
|
|
|
// 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;
|
|
}
|
|
#ifdef HLRAD_GetPhongNormal_VL
|
|
} // s=0,1
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
// =====================================================================================
|
|
#ifdef HLRAD_BLUR
|
|
void CalcLightmap (lightinfo_t *l, byte *styles)
|
|
{
|
|
int facenum;
|
|
int i, j;
|
|
byte pvs[(MAX_MAP_LEAFS + 7) / 8];
|
|
int lastoffset;
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
byte pvs2[(MAX_MAP_LEAFS + 7) / 8];
|
|
int lastoffset2;
|
|
#endif
|
|
|
|
facenum = l->surfnum;
|
|
#ifdef HLRAD_AUTOCORING
|
|
memset (l->lmcache, 0, l->lmcachewidth * l->lmcacheheight * sizeof (vec3_t [ALLSTYLES]));
|
|
#ifdef ZHLT_XASH
|
|
memset (l->lmcache_direction, 0, l->lmcachewidth * l->lmcacheheight * sizeof (vec3_t [ALLSTYLES]));
|
|
#endif
|
|
#else
|
|
memset (l->lmcache, 0, l->lmcachewidth * l->lmcacheheight * sizeof (vec3_t [MAXLIGHTMAPS]));
|
|
#endif
|
|
|
|
// for each sample whose light we need to calculate
|
|
for (i = 0; i < l->lmcachewidth * l->lmcacheheight; i++)
|
|
{
|
|
vec_t s, t;
|
|
vec_t s_vec, t_vec;
|
|
int nearest_s, nearest_t;
|
|
vec3_t spot;
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
vec_t square[2][2]; // the max possible range in which this sample point affects the lighting on a face
|
|
#else
|
|
vec_t reach; // the max possible range in which a sample point affects the lighting on a face
|
|
#endif
|
|
vec3_t surfpt; // the point on the surface (with no HUNT_OFFSET applied), used for getting phong normal and doing patch interpolation
|
|
int surface;
|
|
#endif
|
|
vec3_t pointnormal;
|
|
bool blocked;
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
vec3_t spot2;
|
|
vec3_t pointnormal2;
|
|
#endif
|
|
vec3_t *sampled;
|
|
#ifdef ZHLT_XASH
|
|
vec3_t *sampled_direction;
|
|
#endif
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
vec3_t *normal_out;
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
bool nudged;
|
|
int *wallflags_out;
|
|
#endif
|
|
|
|
// prepare input parameter and output parameter
|
|
{
|
|
s = ((i % l->lmcachewidth) - l->lmcache_offset) / (vec_t)l->lmcache_density;
|
|
t = ((i / l->lmcachewidth) - l->lmcache_offset) / (vec_t)l->lmcache_density;
|
|
s_vec = l->texmins[0] * TEXTURE_STEP + s * TEXTURE_STEP;
|
|
t_vec = l->texmins[1] * TEXTURE_STEP + t * TEXTURE_STEP;
|
|
nearest_s = qmax (0, qmin ((int)floor (s + 0.5), l->texsize[0]));
|
|
nearest_t = qmax (0, qmin ((int)floor (t + 0.5), l->texsize[1]));
|
|
sampled = l->lmcache[i];
|
|
#ifdef ZHLT_XASH
|
|
sampled_direction = l->lmcache_direction[i];
|
|
#endif
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
normal_out = &l->lmcache_normal[i];
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
wallflags_out = &l->lmcache_wallflags[i];
|
|
#endif
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
//
|
|
// The following graph illustrates the range in which a sample point can affect the lighting of a face when g_blur = 1.5 and g_extra = on
|
|
// X : the sample point. They are placed on every TEXTURE_STEP/lmcache_density (=16.0/3) texture pixels. We calculate light for each sample point, which is the main time sink.
|
|
// + : the lightmap pixel. They are placed on every TEXTURE_STEP (=16.0) texture pixels, which is hard coded inside the GoldSrc engine. Their brightness are averaged from the sample points in a square with size g_blur*TEXTURE_STEP.
|
|
// o : indicates that this lightmap pixel is affected by the sample point 'X'. The higher g_blur, the more 'o'.
|
|
// |/ / / | : indicates that the brightness of this area is affected by the lightmap pixels 'o' and hence by the sample point 'X'. This is because the engine uses bilinear interpolation to display the lightmap.
|
|
//
|
|
// ==============================================================================================================================================
|
|
// || + + + + + + || + + + + + + || + + + + + + || + + + + + + ||
|
|
// || || || || ||
|
|
// || || || || ||
|
|
// || + +-----+-----+ + + || + +-----+-----+-----+ + || + +-----+-----+-----+ + || + + +-----+-----+ + ||
|
|
// || | / / / / / | || | / / / / / / / / | || | / / / / / / / / | || | / / / / / | ||
|
|
// || |/ / / / / /| || |/ / / / / / / / /| || |/ / / / / / / / /| || |/ / / / / /| ||
|
|
// || + + / / X / / + + + || + + / / o X / o / / + + || + + / / o / X o / / + + || + + + / / X / / + + ||
|
|
// || |/ / / / / /| || |/ / / / / / / / /| || |/ / / / / / / / /| || |/ / / / / /| ||
|
|
// || | / / / / / | || | / / / / / / / / | || | / / / / / / / / | || | / / / / / | ||
|
|
// || + +-----+-----+ + + || + +-----+-----+-----+ + || + +-----+-----+-----+ + || + + +-----+-----+ + ||
|
|
// || || || || ||
|
|
// || || || || ||
|
|
// || + + + + + + || + + + + + + || + + + + + + || + + + + + + ||
|
|
// ==============================================================================================================================================
|
|
// || + + + + + + || + + + + + + || + + + + + + || + + + + + + ||
|
|
// || || || || ||
|
|
// || || || || ||
|
|
// || + +-----+-----+ + + || + +-----+-----+-----+ + || + +-----+-----+-----+ + || + + +-----+-----+ + ||
|
|
// || | / / / / / | || | / / / / / / / / | || | / / / / / / / / | || | / / / / / | ||
|
|
// || |/ / / / / /| || |/ / / / / / / / /| || |/ / / / / / / / /| || |/ / / / / /| ||
|
|
// || + + / / o / / + + + || + + / / o / / o / / + + || + + / / o / / o / / + + || + + + / / o / / + + ||
|
|
// || |/ / /X/ / /| || |/ / / /X/ / / / /| || |/ / / / /X/ / / /| || |/ / /X/ / /| ||
|
|
// || | / / / / / | || | / / / / / / / / | || | / / / / / / / / | || | / / / / / | ||
|
|
// || + +/ / /o/ / /+ + + || + +/ / /o/ / /o/ / /+ + || + +/ / /o/ / /o/ / /+ + || + + +/ / /o/ / /+ + ||
|
|
// || | / / / / / | || | / / / / / / / / | || | / / / / / / / / | || | / / / / / | ||
|
|
// || |/ / / / / /| || |/ / / / / / / / /| || |/ / / / / / / / /| || |/ / / / / /| ||
|
|
// || + +-----+-----+ + + || + +-----+-----+-----+ + || + +-----+-----+-----+ + || + + +-----+-----+ + ||
|
|
// ==============================================================================================================================================
|
|
//
|
|
square[0][0] = l->texmins[0] * TEXTURE_STEP + ceil (s - (l->lmcache_side + 0.5) / (vec_t)l->lmcache_density) * TEXTURE_STEP - TEXTURE_STEP;
|
|
square[0][1] = l->texmins[1] * TEXTURE_STEP + ceil (t - (l->lmcache_side + 0.5) / (vec_t)l->lmcache_density) * TEXTURE_STEP - TEXTURE_STEP;
|
|
square[1][0] = l->texmins[0] * TEXTURE_STEP + floor (s + (l->lmcache_side + 0.5) / (vec_t)l->lmcache_density) * TEXTURE_STEP + TEXTURE_STEP;
|
|
square[1][1] = l->texmins[1] * TEXTURE_STEP + floor (t + (l->lmcache_side + 0.5) / (vec_t)l->lmcache_density) * TEXTURE_STEP + TEXTURE_STEP;
|
|
#else
|
|
reach = (0.5 / (vec_t)l->lmcache_density) * TEXTURE_STEP + 0.5 * g_blur * TEXTURE_STEP + TEXTURE_STEP;
|
|
#endif
|
|
#endif
|
|
}
|
|
// find world's position for the sample
|
|
{
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
if (i == (nearest_s * l->lmcache_density + l->lmcache_offset)
|
|
+ l->lmcachewidth * (nearest_t * l->lmcache_density + l->lmcache_offset)) // almost always true when compiled with no '-extra'
|
|
{
|
|
j = nearest_s + (l->texsize[0] + 1) * nearest_t;
|
|
VectorCopy (l->surfpt[j], spot);
|
|
blocked = l->surfpt_lightoutside[j];
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
blocked = false;
|
|
if (SetSampleFromST (
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
surfpt, spot, &surface,
|
|
#else
|
|
spot,
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
&nudged,
|
|
#endif
|
|
l, s_vec, t_vec,
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_BLUR_MINIMALSQUARE
|
|
square,
|
|
#else
|
|
reach,
|
|
#endif
|
|
#endif
|
|
g_face_lightmode[facenum]) == LightOutside)
|
|
{
|
|
j = nearest_s + (l->texsize[0] + 1) * nearest_t;
|
|
if (l->surfpt_lightoutside[j])
|
|
{
|
|
blocked = true;
|
|
}
|
|
else
|
|
{
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
// the area this light sample has effect on is completely covered by solid, so take whatever valid position.
|
|
VectorCopy (l->surfpt[j], surfpt);
|
|
VectorCopy (l->surfpt_position[j], spot);
|
|
surface = l->surfpt_surface[j];
|
|
#else
|
|
VectorCopy(l->surfpt[j], spot);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (l->translucent_b)
|
|
{
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
const dplane_t *surfaceplane = getPlaneFromFaceNumber (surface);
|
|
Winding *surfacewinding = new Winding (g_dfaces[surface]);
|
|
|
|
VectorCopy (spot, spot2);
|
|
for (int x = 0; x < surfacewinding->m_NumPoints; x++)
|
|
{
|
|
VectorAdd (surfacewinding->m_Points[x], g_face_offset[surface], surfacewinding->m_Points[x]);
|
|
}
|
|
if (!point_in_winding_noedge (*surfacewinding, *surfaceplane, spot2, 0.2))
|
|
{
|
|
snap_to_winding_noedge (*surfacewinding, *surfaceplane, spot2, 0.2, 4 * 0.2);
|
|
}
|
|
VectorMA (spot2, -(g_translucentdepth + 2 * DEFAULT_HUNT_OFFSET), surfaceplane->normal, spot2);
|
|
|
|
delete surfacewinding;
|
|
#else
|
|
vec3_t delta;
|
|
VectorSubtract (g_face_centroids[facenum], spot, delta);
|
|
VectorNormalize (delta);
|
|
VectorMA (spot, 0.2, delta, spot2);
|
|
VectorMA (spot2, -(g_translucentdepth + 2*DEFAULT_HUNT_OFFSET), l->facenormal, spot2);
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
*wallflags_out = WALLFLAG_NONE;
|
|
if (blocked)
|
|
{
|
|
*wallflags_out |= (WALLFLAG_BLOCKED | WALLFLAG_NUDGED);
|
|
}
|
|
if (nudged)
|
|
{
|
|
*wallflags_out |= WALLFLAG_NUDGED;
|
|
}
|
|
#endif
|
|
}
|
|
// calculate normal for the sample
|
|
{
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
GetPhongNormal (surface, surfpt, pointnormal);
|
|
#else
|
|
#ifdef HLRAD_PHONG_FROMORIGINAL
|
|
vec3_t pos_original;
|
|
SetSurfFromST (l, pos_original, s_vec, t_vec);
|
|
{
|
|
// adjust sample's offset to 0
|
|
vec_t scale;
|
|
scale = DotProduct (l->texnormal, l->facenormal);
|
|
VectorMA (pos_original, - DEFAULT_HUNT_OFFSET / scale, l->texnormal, pos_original);
|
|
}
|
|
GetPhongNormal(facenum, pos_original, pointnormal);
|
|
#else
|
|
GetPhongNormal(facenum, spot, pointnormal);
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (l->translucent_b)
|
|
{
|
|
VectorSubtract (vec3_origin, pointnormal, pointnormal2);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
VectorCopy (pointnormal, *normal_out);
|
|
#endif
|
|
}
|
|
// calculate visibility for the sample
|
|
{
|
|
if (!g_visdatasize)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs, 255, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs, 255, (g_numleafs + 7) / 8);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dleaf_t *leaf = PointInLeaf(spot);
|
|
int thisoffset = leaf->visofs;
|
|
if (i == 0 || thisoffset != lastoffset)
|
|
{
|
|
#ifdef HLRAD_VIS_FIX
|
|
if (thisoffset == -1)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs, 0, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset (pvs, 0, (g_numleafs + 7) / 8);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
DecompressVis(&g_dvisdata[leaf->visofs], pvs, sizeof(pvs));
|
|
}
|
|
#else
|
|
hlassert(thisoffset != -1);
|
|
DecompressVis(&g_dvisdata[leaf->visofs], pvs, sizeof(pvs));
|
|
#endif
|
|
}
|
|
lastoffset = thisoffset;
|
|
}
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (l->translucent_b)
|
|
{
|
|
if (!g_visdatasize)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs2, 255, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs2, 255, (g_numleafs + 7) / 8);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dleaf_t *leaf2 = PointInLeaf(spot2);
|
|
int thisoffset2 = leaf2->visofs;
|
|
if (i == 0 || thisoffset2 != lastoffset2)
|
|
{
|
|
#ifdef HLRAD_VIS_FIX
|
|
if (thisoffset2 == -1)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs2, 0, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs2, 0, (g_numleafs + 7) / 8);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
DecompressVis(&g_dvisdata[leaf2->visofs], pvs2, sizeof(pvs2));
|
|
}
|
|
#else
|
|
hlassert(thisoffset2 != -1);
|
|
DecompressVis(&g_dvisdata[leaf2->visofs], pvs2, sizeof(pvs2));
|
|
#endif
|
|
}
|
|
lastoffset2 = thisoffset2;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
// gather light
|
|
{
|
|
if (!blocked)
|
|
{
|
|
GatherSampleLight(spot, pvs, pointnormal, sampled
|
|
#ifdef ZHLT_XASH
|
|
, sampled_direction
|
|
#endif
|
|
, styles
|
|
#ifdef HLRAD_GatherPatchLight
|
|
, 0
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l->miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
, surface
|
|
#else
|
|
, facenum
|
|
#endif
|
|
#endif
|
|
);
|
|
}
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (l->translucent_b)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
vec3_t sampled2[ALLSTYLES];
|
|
memset (sampled2, 0, ALLSTYLES * sizeof (vec3_t));
|
|
#ifdef ZHLT_XASH
|
|
vec3_t sampled2_direction[ALLSTYLES];
|
|
memset (sampled2_direction, 0, ALLSTYLES * sizeof (vec3_t));
|
|
#endif
|
|
#else
|
|
vec3_t sampled2[MAXLIGHTMAPS];
|
|
memset (sampled2, 0, MAXLIGHTMAPS * sizeof (vec3_t));
|
|
#endif
|
|
if (!blocked)
|
|
{
|
|
GatherSampleLight(spot2, pvs2, pointnormal2, sampled2
|
|
#ifdef ZHLT_XASH
|
|
, sampled2_direction
|
|
#endif
|
|
, styles
|
|
#ifdef HLRAD_GatherPatchLight
|
|
, 0
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l->miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
, surface
|
|
#else
|
|
, facenum
|
|
#endif
|
|
#endif
|
|
);
|
|
}
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && styles[j] != 255; j++)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && styles[j] != 255; j++)
|
|
#endif
|
|
{
|
|
#ifdef ZHLT_XASH
|
|
// reflect the direction back
|
|
vec_t dot = DotProduct (sampled2_direction[j], pointnormal);
|
|
VectorMA (sampled2_direction[j], -dot * 2, pointnormal, sampled2_direction[j]);
|
|
vec_t front = 0, back = 0;
|
|
// calculate the change of brightness and adjust the direction. This is not totally right when the translucent value is not grayscale,
|
|
// but at least it still preserves the condition that VectorLength(light_direction) <= VectorAvg(light).
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
front += ((1.0 - l->translucent_v[x]) * sampled[j][x]) / 3;
|
|
back += (l->translucent_v[x] * sampled2[j][x]) / 3;
|
|
}
|
|
front = fabs (VectorAvg (sampled[j])) > NORMAL_EPSILON? front / VectorAvg (sampled[j]): 0;
|
|
back = fabs (VectorAvg (sampled2[j])) > NORMAL_EPSILON? back / VectorAvg (sampled2[j]): 0;
|
|
#endif
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
sampled[j][x] = (1.0 - l->translucent_v[x]) * sampled[j][x] + l->translucent_v[x] * sampled2[j][x];
|
|
#ifdef ZHLT_XASH
|
|
sampled_direction[j][x] = front * sampled_direction[j][x] + back * sampled2_direction[j][x];
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
if (g_drawnudge)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && styles[j] != 255; j++)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && styles[j] != 255; j++)
|
|
#endif
|
|
{
|
|
if (blocked && styles[j] == 0)
|
|
{
|
|
sampled[j][0] = 200;
|
|
sampled[j][1] = 0;
|
|
sampled[j][2] = 0;
|
|
}
|
|
else if (nudged && styles[j] == 0) // we assume style 0 is always present
|
|
{
|
|
VectorFill (sampled[j], 100);
|
|
}
|
|
else
|
|
{
|
|
VectorClear (sampled[j]);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
void BuildFacelights(const int facenum)
|
|
{
|
|
dface_t* f;
|
|
#ifdef HLRAD_AUTOCORING
|
|
unsigned char f_styles[ALLSTYLES];
|
|
sample_t *fl_samples[ALLSTYLES];
|
|
#ifndef HLRAD_BLUR
|
|
vec3_t sampled[ALLSTYLES];
|
|
#ifdef ZHLT_XASH
|
|
vec3_t sampled_direction[ALLSTYLES];
|
|
vec3_t sampled_normal;
|
|
#endif
|
|
#endif
|
|
#else
|
|
#ifndef HLRAD_BLUR
|
|
vec3_t sampled[MAXLIGHTMAPS];
|
|
#endif
|
|
#endif
|
|
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_TRANSLUCENT
|
|
vec3_t spot2, normal2;
|
|
vec3_t delta;
|
|
byte pvs2[(MAX_MAP_LEAFS + 7) / 8];
|
|
int thisoffset2 = -1, lastoffset2 = -1;
|
|
#endif
|
|
|
|
#ifndef HLRAD_TRANCPARENCYLOSS_FIX
|
|
#ifdef HLRAD_HULLU
|
|
bool b_transparency_loss = false;
|
|
vec_t light_left_for_facelight = 1.0;
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
int *sample_wallflags;
|
|
#endif
|
|
|
|
f = &g_dfaces[facenum];
|
|
|
|
//
|
|
// some surfaces don't need lightmaps
|
|
//
|
|
f->lightofs = -1;
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
{
|
|
f_styles[j] = 255;
|
|
}
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS; j++)
|
|
{
|
|
f->styles[j] = 255;
|
|
}
|
|
#endif
|
|
|
|
if (g_texinfo[f->texinfo].flags & TEX_SPECIAL)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < MAXLIGHTMAPS; j++)
|
|
{
|
|
f->styles[j] = 255;
|
|
}
|
|
#endif
|
|
return; // non-lit texture
|
|
}
|
|
|
|
#ifdef HLRAD_AUTOCORING
|
|
f_styles[0] = 0;
|
|
#else
|
|
f->styles[0] = 0; // Everyone gets the style zero map.
|
|
#endif
|
|
#ifdef HLRAD_STYLE_CORING
|
|
#ifdef ZHLT_TEXLIGHT
|
|
if (g_face_patches[facenum] && g_face_patches[facenum]->emitstyle)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
f_styles[1] = g_face_patches[facenum]->emitstyle;
|
|
#else
|
|
f->styles[1] = g_face_patches[facenum]->emitstyle;
|
|
#endif
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
memset(&l, 0, sizeof(l));
|
|
|
|
l.surfnum = facenum;
|
|
l.face = f;
|
|
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
VectorCopy (g_translucenttextures[g_texinfo[f->texinfo].miptex], l.translucent_v);
|
|
l.translucent_b = !VectorCompare (l.translucent_v, vec3_origin);
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
l.miptex = g_texinfo[f->texinfo].miptex;
|
|
#endif
|
|
#ifndef HLRAD_TRANCPARENCYLOSS_FIX
|
|
//
|
|
// get transparency loss (part of light go through transparency faces.. reduce facelight on these)
|
|
//
|
|
#ifndef HLRAD_OPAQUE_NODE
|
|
#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 = VectorAvg (opaque->transparency_scale); //vec_t transparency = opaque->transparency; //--vluzacn
|
|
|
|
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
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// rotate plane
|
|
//
|
|
plane = getPlaneFromFace(f);
|
|
VectorCopy(plane->normal, l.facenormal);
|
|
l.facedist = plane->dist;
|
|
|
|
CalcFaceVectors(&l);
|
|
CalcFaceExtents(&l);
|
|
CalcPoints(&l);
|
|
#ifdef HLRAD_BLUR
|
|
CalcLightmap (&l
|
|
#ifdef HLRAD_AUTOCORING
|
|
, f_styles
|
|
#else
|
|
, f->styles
|
|
#endif
|
|
);
|
|
#endif
|
|
#ifdef HLRAD_MDL_LIGHT_HACK
|
|
#ifndef HLRAD_MDL_LIGHT_HACK_NEW
|
|
VectorCopy (g_face_offset[facenum], facesampleinfo[facenum].offset);
|
|
for (i=0; i<2; ++i)
|
|
{
|
|
facesampleinfo[facenum].texmins[i] = l.texmins[i];
|
|
facesampleinfo[facenum].texsize[i] = l.texsize[i];
|
|
VectorCopy (l.textoworld[i], facesampleinfo[facenum].textoworld[i]);
|
|
VectorCopy (l.worldtotex[i], facesampleinfo[facenum].worldtotex[i]);
|
|
}
|
|
VectorCopy (l.texorg, facesampleinfo[facenum].texorg);
|
|
#endif
|
|
#endif
|
|
|
|
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;
|
|
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (k = 0; k < ALLSTYLES; k++)
|
|
{
|
|
fl_samples[k] = (sample_t *)calloc (l.numsurfpt, sizeof(sample_t));
|
|
hlassume (fl_samples[k] != NULL, assume_NoMemory);
|
|
}
|
|
#else
|
|
for (k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
facelight[facenum].samples[k] = (sample_t*)calloc(l.numsurfpt, sizeof(sample_t));
|
|
#ifdef HLRAD_HLASSUMENOMEMORY
|
|
hlassume (facelight[facenum].samples[k] != NULL, assume_NoMemory);
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
|
|
{
|
|
hlassume (patch->totalstyle_all = (unsigned char *)malloc (ALLSTYLES * sizeof (unsigned char)), assume_NoMemory);
|
|
hlassume (patch->samplelight_all = (vec3_t *)malloc (ALLSTYLES * sizeof (vec3_t)), assume_NoMemory);
|
|
hlassume (patch->totallight_all = (vec3_t *)malloc (ALLSTYLES * sizeof (vec3_t)), assume_NoMemory);
|
|
hlassume (patch->directlight_all = (vec3_t *)malloc (ALLSTYLES * sizeof (vec3_t)), assume_NoMemory);
|
|
#ifdef ZHLT_XASH
|
|
hlassume (patch->samplelight_all_direction = (vec3_t *)malloc (ALLSTYLES * sizeof (vec3_t)), assume_NoMemory);
|
|
hlassume (patch->totallight_all_direction = (vec3_t *)malloc (ALLSTYLES * sizeof (vec3_t)), assume_NoMemory);
|
|
hlassume (patch->directlight_all_direction = (vec3_t *)malloc (ALLSTYLES * sizeof (vec3_t)), assume_NoMemory);
|
|
#endif
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
{
|
|
patch->totalstyle_all[j] = 255;
|
|
VectorClear (patch->samplelight_all[j]);
|
|
VectorClear (patch->totallight_all[j]);
|
|
VectorClear (patch->directlight_all[j]);
|
|
#ifdef ZHLT_XASH
|
|
VectorClear (patch->samplelight_all_direction[j]);
|
|
VectorClear (patch->totallight_all_direction[j]);
|
|
VectorClear (patch->directlight_all_direction[j]);
|
|
#endif
|
|
}
|
|
patch->totalstyle_all[0] = 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
sample_wallflags = (int *)malloc ((2 * l.lmcache_side + 1) * (2 * l.lmcache_side + 1) * sizeof (int));
|
|
#endif
|
|
spot = l.surfpt[0];
|
|
for (i = 0; i < l.numsurfpt; i++, spot += 3)
|
|
{
|
|
#ifndef HLRAD_BLUR
|
|
vec3_t pointnormal = { 0, 0, 0 };
|
|
#endif
|
|
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
#ifdef HLRAD_LERP_TEXNORMAL
|
|
vec3_t spot_original;
|
|
{
|
|
vec_t s_vec = l.texmins[0] * TEXTURE_STEP + (i % lightmapwidth) * TEXTURE_STEP;
|
|
vec_t t_vec = l.texmins[1] * TEXTURE_STEP + (i / lightmapwidth) * TEXTURE_STEP;
|
|
SetSurfFromST (&l, spot_original, s_vec, t_vec);
|
|
{
|
|
// adjust sample's offset to 0
|
|
vec_t scale;
|
|
scale = DotProduct (l.texnormal, l.facenormal);
|
|
VectorMA (spot_original, - DEFAULT_HUNT_OFFSET / scale, l.texnormal, spot_original);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (k = 0; k < ALLSTYLES; k++)
|
|
{
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
VectorCopy(spot, fl_samples[k][i].pos);
|
|
fl_samples[k][i].surface = l.surfpt_surface[i];
|
|
#else
|
|
#ifdef HLRAD_LERP_TEXNORMAL
|
|
VectorCopy(spot_original, fl_samples[k][i].pos);
|
|
#else
|
|
VectorCopy(spot, fl_samples[k][i].pos);
|
|
#endif
|
|
#endif
|
|
}
|
|
#else
|
|
for (k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
VectorCopy(spot, facelight[facenum].samples[k][i].pos);
|
|
facelight[facenum].samples[k][i].surface = l.surfpt_surface[i];
|
|
#else
|
|
#ifdef HLRAD_LERP_TEXNORMAL
|
|
VectorCopy(spot_original, facelight[facenum].samples[k][i].pos);
|
|
#else
|
|
VectorCopy(spot, facelight[facenum].samples[k][i].pos);
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef HLRAD_BLUR
|
|
int s, t, pos;
|
|
int s_center, t_center;
|
|
vec_t sizehalf;
|
|
vec_t weighting, subsamples;
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
vec3_t centernormal;
|
|
vec_t weighting_correction;
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
int pass;
|
|
#endif
|
|
s_center = (i % lightmapwidth) * l.lmcache_density + l.lmcache_offset;
|
|
t_center = (i / lightmapwidth) * l.lmcache_density + l.lmcache_offset;
|
|
sizehalf = 0.5 * g_blur * l.lmcache_density;
|
|
subsamples = 0.0;
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
VectorCopy (l.lmcache_normal[s_center + l.lmcachewidth * t_center], centernormal);
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
if (g_bleedfix && !g_drawnudge)
|
|
{
|
|
int s_origin = s_center;
|
|
int t_origin = t_center;
|
|
for (s = s_center - l.lmcache_side; s <= s_center + l.lmcache_side; s++)
|
|
{
|
|
for (t = t_center - l.lmcache_side; t <= t_center + l.lmcache_side; t++)
|
|
{
|
|
int *pwallflags = &sample_wallflags[(s - s_center + l.lmcache_side) + (2 * l.lmcache_side + 1) * (t - t_center + l.lmcache_side)];
|
|
*pwallflags = l.lmcache_wallflags[s + l.lmcachewidth * t];
|
|
}
|
|
}
|
|
// project the "shadow" from the origin point
|
|
for (s = s_center - l.lmcache_side; s <= s_center + l.lmcache_side; s++)
|
|
{
|
|
for (t = t_center - l.lmcache_side; t <= t_center + l.lmcache_side; t++)
|
|
{
|
|
int *pwallflags = &sample_wallflags[(s - s_center + l.lmcache_side) + (2 * l.lmcache_side + 1) * (t - t_center + l.lmcache_side)];
|
|
int coord[2] = {s - s_origin, t - t_origin};
|
|
int axis = abs(coord[0]) >= abs(coord[1])? 0: 1;
|
|
int sign = coord[axis] >= 0? 1: -1;
|
|
bool blocked1 = false;
|
|
bool blocked2 = false;
|
|
for (int dist = 1; dist < abs (coord[axis]); dist++)
|
|
{
|
|
int test1[2];
|
|
int test2[2];
|
|
test1[axis] = test2[axis] = sign * dist;
|
|
double intercept = (double)coord[1-axis] * (double)test1[axis] / (double)coord[axis];
|
|
test1[1-axis] = (int)floor (intercept + 0.01);
|
|
test2[1-axis] = (int)ceil (intercept - 0.01);
|
|
if (abs (test1[0] + s_origin - s_center) > l.lmcache_side || abs (test1[1] + t_origin - t_center) > l.lmcache_side ||
|
|
abs (test2[0] + s_origin - s_center) > l.lmcache_side || abs (test2[1] + t_origin - t_center) > l.lmcache_side )
|
|
{
|
|
Warning ("HLRAD_AVOIDWALLBLEED: internal error. Contact vluzacn@163.com concerning this issue.");
|
|
continue;
|
|
}
|
|
int wallflags1 = sample_wallflags[(test1[0] + s_origin - s_center + l.lmcache_side) + (2 * l.lmcache_side + 1) * (test1[1] + t_origin - t_center + l.lmcache_side)];
|
|
int wallflags2 = sample_wallflags[(test2[0] + s_origin - s_center + l.lmcache_side) + (2 * l.lmcache_side + 1) * (test2[1] + t_origin - t_center + l.lmcache_side)];
|
|
if (wallflags1 & WALLFLAG_NUDGED)
|
|
{
|
|
blocked1 = true;
|
|
}
|
|
if (wallflags2 & WALLFLAG_NUDGED)
|
|
{
|
|
blocked2 = true;
|
|
}
|
|
}
|
|
if (blocked1 && blocked2)
|
|
{
|
|
*pwallflags |= WALLFLAG_SHADOWED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
for (pass = 0; pass < 2; pass++)
|
|
{
|
|
#endif
|
|
for (s = s_center - l.lmcache_side; s <= s_center + l.lmcache_side; s++)
|
|
{
|
|
for (t = t_center - l.lmcache_side; t <= t_center + l.lmcache_side; t++)
|
|
{
|
|
weighting = (qmin (0.5, sizehalf - (s - s_center)) - qmax (-0.5, -sizehalf - (s - s_center)))
|
|
* (qmin (0.5, sizehalf - (t - t_center)) - qmax (-0.5, -sizehalf - (t - t_center)));
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
if (g_bleedfix && !g_drawnudge)
|
|
{
|
|
int wallflags = sample_wallflags[(s - s_center + l.lmcache_side) + (2 * l.lmcache_side + 1) * (t - t_center + l.lmcache_side)];
|
|
if (wallflags & (WALLFLAG_BLOCKED | WALLFLAG_SHADOWED))
|
|
{
|
|
continue;
|
|
}
|
|
if (wallflags & WALLFLAG_NUDGED)
|
|
{
|
|
if (pass == 0)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pos = s + l.lmcachewidth * t;
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
// when blur distance (g_blur) is large, the subsample can be very far from the original lightmap sample (aligned with interval TEXTURE_STEP (16.0))
|
|
// in some cases such as a thin cylinder, the subsample can even grow into the opposite side
|
|
// as a result, when exposed to a directional light, the light on the cylinder may "leak" into the opposite dark side
|
|
// this correction limits the effect of blur distance when the normal changes very fast
|
|
// this correction will not break the smoothness that HLRAD_GROWSAMPLE ensures
|
|
weighting_correction = DotProduct (l.lmcache_normal[pos], centernormal);
|
|
weighting_correction = (weighting_correction > 0)? weighting_correction * weighting_correction: 0;
|
|
weighting = weighting * weighting_correction;
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
VectorMA (fl_samples[j][i].light, weighting, l.lmcache[pos][j], fl_samples[j][i].light);
|
|
#ifdef ZHLT_XASH
|
|
VectorMA (fl_samples[j][i].light_direction, weighting, l.lmcache_direction[pos][j], fl_samples[j][i].light_direction);
|
|
#endif
|
|
}
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++)
|
|
{
|
|
VectorAdd (facelight[facenum].samples[j][i].light, weighting, l.lmcache[pos][j], facelight[facenum].samples[j][i].light);
|
|
}
|
|
#endif
|
|
subsamples += weighting;
|
|
}
|
|
}
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
if (subsamples > NORMAL_EPSILON)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
subsamples = 0.0;
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
VectorClear (fl_samples[j][i].light);
|
|
#ifdef ZHLT_XASH
|
|
VectorClear (fl_samples[j][i].light_direction);
|
|
#endif
|
|
}
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++)
|
|
{
|
|
VectorClear (facelight[facenum].samples[j][i].light);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef ZHLT_XASH
|
|
for (int k = 0; k < ALLSTYLES; k++)
|
|
{
|
|
VectorClear (fl_samples[k][i].normal);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
if (subsamples > 0)
|
|
#else
|
|
if (subsamples > NORMAL_EPSILON)
|
|
#endif
|
|
{
|
|
#ifdef ZHLT_XASH
|
|
for (int k = 0; k < ALLSTYLES; k++) // fill 'sample.normal' for all 64 styles, just like what we did on 'sample.pos'
|
|
{
|
|
VectorCopy (centernormal, fl_samples[k][i].normal);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
VectorScale (fl_samples[j][i].light, 1.0 / subsamples, fl_samples[j][i].light);
|
|
#ifdef ZHLT_XASH
|
|
VectorScale (fl_samples[j][i].light_direction, 1.0 / subsamples, fl_samples[j][i].light_direction);
|
|
#endif
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++)
|
|
{
|
|
VectorScale (facelight[facenum].samples[j][i].light, 1.0 / subsamples, facelight[facenum].samples[j][i].light);
|
|
#endif
|
|
#ifndef HLRAD_ACCURATEBOUNCE_SAMPLELIGHT
|
|
#ifdef ZHLT_TEXLIGHT
|
|
#ifdef HLRAD_AUTOCORING
|
|
AddSampleToPatch (&fl_samples[j][i], facenum, f_styles[j]); //LRC
|
|
#else
|
|
AddSampleToPatch(&facelight[facenum].samples[j][i], facenum, f->styles[j]); //LRC
|
|
#endif
|
|
#else
|
|
if (f->styles[j] == 0)
|
|
{
|
|
AddSampleToPatch(&facelight[facenum].samples[j][i], facenum);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
#else
|
|
// get the PVS for the pos to limit the number of checks
|
|
if (!g_visdatasize)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs, 255, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs, 255, (g_numleafs + 7) / 8);
|
|
#endif
|
|
lastoffset = -1;
|
|
}
|
|
else
|
|
{
|
|
dleaf_t* leaf = PointInLeaf(spot);
|
|
|
|
thisoffset = leaf->visofs;
|
|
if (i == 0 || thisoffset != lastoffset)
|
|
{
|
|
#ifdef HLRAD_VIS_FIX
|
|
if (thisoffset == -1)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs, 0, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset (pvs, 0, (g_numleafs + 7) / 8);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
DecompressVis(&g_dvisdata[leaf->visofs], pvs, sizeof(pvs));
|
|
}
|
|
#else
|
|
hlassert(thisoffset != -1);
|
|
DecompressVis(&g_dvisdata[leaf->visofs], pvs, sizeof(pvs));
|
|
#endif
|
|
}
|
|
lastoffset = thisoffset;
|
|
}
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (l.translucent_b)
|
|
{
|
|
VectorSubtract (g_face_centroids[facenum], spot, delta);
|
|
VectorNormalize (delta);
|
|
VectorMA (spot, 0.2, delta, spot2);
|
|
VectorMA (spot2, -(g_translucentdepth + 2*DEFAULT_HUNT_OFFSET), l.facenormal, spot2);
|
|
if (!g_visdatasize)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs2, 255, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs2, 255, (g_numleafs + 7) / 8);
|
|
#endif
|
|
lastoffset2 = -1;
|
|
}
|
|
else
|
|
{
|
|
dleaf_t* leaf2 = PointInLeaf(spot2);
|
|
|
|
thisoffset2 = leaf2->visofs;
|
|
if (i == 0 || thisoffset2 != lastoffset2)
|
|
{
|
|
#ifdef HLRAD_VIS_FIX
|
|
if (thisoffset2 == -1)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs2, 0, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs2, 0, (g_numleafs + 7) / 8);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
DecompressVis(&g_dvisdata[leaf2->visofs], pvs2, sizeof(pvs2));
|
|
}
|
|
#else
|
|
hlassert(thisoffset2 != -1);
|
|
DecompressVis(&g_dvisdata[leaf2->visofs], pvs2, sizeof(pvs2));
|
|
#endif
|
|
}
|
|
lastoffset2 = thisoffset2;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
memset(sampled, 0, sizeof(sampled));
|
|
#ifdef ZHLT_XASH
|
|
memset (sampled_direction, 0, sizeof (sampled_direction));
|
|
VectorClear (sampled_normal);
|
|
#endif
|
|
|
|
// If we are doing "extra" samples, oversample the direct light around the point.
|
|
if (g_extra
|
|
#ifdef HLRAD_FASTMODE
|
|
&& !g_fastmode
|
|
#endif
|
|
)
|
|
{
|
|
#ifdef HLRAD_WEIGHT_FIX
|
|
int weighting[3][3] = { {1, 1, 1}, {1, 1, 1}, {1, 1, 1} }; // because we are using 1/3 dist not 1/2
|
|
#else
|
|
int weighting[3][3] = { {5, 9, 5}, {9, 16, 9}, {5, 9, 5} };
|
|
#endif
|
|
vec3_t pos;
|
|
int s, t, subsamples = 0;
|
|
|
|
for (t = -1; t <= 1; t++)
|
|
{
|
|
for (s = -1; s <= 1; s++)
|
|
{
|
|
#ifndef HLRAD_CalcPoints_NEW
|
|
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 <lightmapheight))
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
vec3_t subsampled[ALLSTYLES];
|
|
#ifdef ZHLT_XASH
|
|
vec3_t subsampled_direction[ALLSTYLES];
|
|
#endif
|
|
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
#else
|
|
vec3_t subsampled[MAXLIGHTMAPS];
|
|
|
|
for (j = 0; j < MAXLIGHTMAPS; j++)
|
|
#endif
|
|
{
|
|
VectorClear(subsampled[j]);
|
|
#ifdef ZHLT_XASH
|
|
VectorClear (subsampled_direction[j]);
|
|
#endif
|
|
}
|
|
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
vec_t s_vec = l.texmins[0] * TEXTURE_STEP + (i % lightmapwidth + s * 1.0/3.0) * TEXTURE_STEP;
|
|
vec_t t_vec = l.texmins[1] * TEXTURE_STEP + (i / lightmapwidth + t * 1.0/3.0) * TEXTURE_STEP;
|
|
bool blocked = false;
|
|
if (SetSampleFromST (pos, &l, s_vec, t_vec, g_face_lightmode[facenum]) == LightOutside)
|
|
{
|
|
if (l.surfpt_lightoutside[i])
|
|
{
|
|
blocked = true;
|
|
}
|
|
else
|
|
{
|
|
VectorCopy(l.surfpt[i], pos);
|
|
}
|
|
}
|
|
#else
|
|
// Calculate the point one third of the way toward the "subsample point"
|
|
VectorCopy(l.surfpt[i], pos);
|
|
VectorAdd(pos, l.surfpt[i], pos);
|
|
VectorAdd(pos, l.surfpt[subsample], pos);
|
|
VectorScale(pos, 1.0 / 3.0, pos);
|
|
#endif
|
|
|
|
#ifdef HLRAD_PHONG_FROMORIGINAL
|
|
// this will generate smoother light for cylinders partially embedded in solid,
|
|
vec3_t pos_original;
|
|
SetSurfFromST (&l, pos_original, s_vec, t_vec);
|
|
{
|
|
// adjust sample's offset to 0
|
|
vec_t scale;
|
|
scale = DotProduct (l.texnormal, l.facenormal);
|
|
VectorMA (pos_original, - DEFAULT_HUNT_OFFSET / scale, l.texnormal, pos_original);
|
|
}
|
|
GetPhongNormal(facenum, pos_original, pointnormal);
|
|
#else
|
|
GetPhongNormal(facenum, pos, pointnormal);
|
|
#endif
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
if (!blocked)
|
|
{
|
|
#endif
|
|
GatherSampleLight(pos, pvs, pointnormal, subsampled,
|
|
#ifdef ZHLT_XASH
|
|
subsampled_direction,
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
f_styles
|
|
#else
|
|
f->styles
|
|
#endif
|
|
#ifdef HLRAD_GatherPatchLight
|
|
, 0
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l.miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, facenum
|
|
#endif
|
|
);
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (l.translucent_b)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
vec3_t subsampled2[ALLSTYLES];
|
|
#ifdef ZHLT_XASH
|
|
vec3_t subsampled2_direction[ALLSTYLES];
|
|
#endif
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
#else
|
|
vec3_t subsampled2[MAXLIGHTMAPS];
|
|
for (j = 0; j < MAXLIGHTMAPS; j++)
|
|
#endif
|
|
{
|
|
VectorFill(subsampled2[j], 0);
|
|
#ifdef ZHLT_XASH
|
|
VectorFill (subsampled2_direction[j], 0);
|
|
#endif
|
|
}
|
|
VectorSubtract (g_face_centroids[facenum], pos, delta);
|
|
VectorNormalize (delta);
|
|
VectorMA (pos, 0.2, delta, spot2);
|
|
VectorMA (spot2, -(g_translucentdepth + 2*DEFAULT_HUNT_OFFSET), l.facenormal, spot2);
|
|
VectorSubtract (vec3_origin, pointnormal, normal2);
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
if (!blocked)
|
|
{
|
|
#endif
|
|
GatherSampleLight(spot2, pvs2, normal2, subsampled2,
|
|
#ifdef ZHLT_XASH
|
|
subsampled2_direction,
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
f_styles
|
|
#else
|
|
f->styles
|
|
#endif
|
|
#ifdef HLRAD_GatherPatchLight
|
|
, 0
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l.miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, facenum
|
|
#endif
|
|
);
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && (f->styles[j] != 255); j++)
|
|
#endif
|
|
{
|
|
#ifdef ZHLT_XASH
|
|
// reflect the direction back
|
|
vec_t dot = DotProduct (subsampled2_direction[j], pointnormal);
|
|
VectorMA (subsampled2_direction[j], -dot * 2, pointnormal, subsampled2_direction[j]);
|
|
vec_t front = 0, back = 0;
|
|
// calculate the change of brightness and adjust the direction. This is not totally right when the translucent value is not grayscale,
|
|
// but at least it still preserves the condition that VectorLength(light_direction) <= VectorAvg(light).
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
front += ((1.0 - l.translucent_v[x]) * subsampled[j][x]) / 3;
|
|
back += (l.translucent_v[x] * subsampled2[j][x]) / 3;
|
|
}
|
|
front = fabs (VectorAvg (subsampled[j])) > NORMAL_EPSILON? front / VectorAvg (subsampled[j]): 0;
|
|
back = fabs (VectorAvg (subsampled2[j])) > NORMAL_EPSILON? back / VectorAvg (subsampled2[j]): 0;
|
|
#endif
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
subsampled[j][x] = (1.0 - l.translucent_v[x]) * subsampled[j][x] + l.translucent_v[x] * subsampled2[j][x];
|
|
#ifdef ZHLT_XASH
|
|
subsampled_direction[j][x] = front * subsampled_direction[j][x] + back * subsampled2_direction[j][x];
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && (f->styles[j] != 255); j++)
|
|
#endif
|
|
{
|
|
VectorScale(subsampled[j], weighting[s + 1][t + 1], subsampled[j]);
|
|
VectorAdd(sampled[j], subsampled[j], sampled[j]);
|
|
#ifdef ZHLT_XASH
|
|
VectorScale (subsampled_direction[j], weighting[s + 1][t + 1], subsampled_direction[j]);
|
|
VectorAdd (sampled_direction[j], subsampled_direction[j], sampled_direction[j]);
|
|
#endif
|
|
}
|
|
#ifdef ZHLT_XASH
|
|
VectorMA (sampled_normal, weighting[s + 1][t + 1], pointnormal, sampled_normal);
|
|
#endif
|
|
subsamples += weighting[s + 1][t + 1];
|
|
}
|
|
}
|
|
}
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && (f->styles[j] != 255); j++)
|
|
#endif
|
|
{
|
|
VectorScale(sampled[j], 1.0 / subsamples, sampled[j]);
|
|
#ifdef ZHLT_XASH
|
|
VectorScale (sampled_direction[j], 1.0 / subsamples, sampled_direction[j]);
|
|
#endif
|
|
}
|
|
#ifdef ZHLT_XASH
|
|
VectorScale (sampled_normal, 1.0 / subsamples, sampled_normal);
|
|
if (!VectorNormalize (sampled_normal))
|
|
{
|
|
VectorCopy (l.facenormal, sampled_normal);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef HLRAD_PHONG_FROMORIGINAL
|
|
vec_t s_vec = l.texmins[0] * TEXTURE_STEP + (i % lightmapwidth) * TEXTURE_STEP;
|
|
vec_t t_vec = l.texmins[1] * TEXTURE_STEP + (i / lightmapwidth) * TEXTURE_STEP;
|
|
// this will generate smoother light for cylinders partially embedded in solid,
|
|
vec3_t pos_original;
|
|
SetSurfFromST (&l, pos_original, s_vec, t_vec);
|
|
{
|
|
// adjust sample's offset to 0
|
|
vec_t scale;
|
|
scale = DotProduct (l.texnormal, l.facenormal);
|
|
VectorMA (pos_original, - DEFAULT_HUNT_OFFSET / scale, l.texnormal, pos_original);
|
|
}
|
|
GetPhongNormal(facenum, pos_original, pointnormal);
|
|
#else
|
|
GetPhongNormal(facenum, spot, pointnormal);
|
|
#endif
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
bool blocked = l.surfpt_lightoutside[i];
|
|
if (!blocked)
|
|
{
|
|
#endif
|
|
GatherSampleLight(spot, pvs, pointnormal, sampled,
|
|
#ifdef ZHLT_XASH
|
|
sampled_direction,
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
f_styles
|
|
#else
|
|
f->styles
|
|
#endif
|
|
#ifdef HLRAD_GatherPatchLight
|
|
, 0
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l.miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, facenum
|
|
#endif
|
|
);
|
|
#ifdef HLRAD_CalcPoints_NEW
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (l.translucent_b)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
vec3_t sampled2[ALLSTYLES];
|
|
#ifdef ZHLT_XASH
|
|
vec3_t sampled2_direction[ALLSTYLES];
|
|
#endif
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
#else
|
|
vec3_t sampled2[MAXLIGHTMAPS];
|
|
for (j = 0; j < MAXLIGHTMAPS; j++)
|
|
#endif
|
|
{
|
|
VectorFill(sampled2[j], 0);
|
|
#ifdef ZHLT_XASH
|
|
VectorFill (sampled2_direction[j], 0);
|
|
#endif
|
|
}
|
|
VectorSubtract (vec3_origin, pointnormal, normal2);
|
|
GatherSampleLight(spot2, pvs2, normal2, sampled2,
|
|
#ifdef ZHLT_XASH
|
|
sampled2_direction,
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
f_styles
|
|
#else
|
|
f->styles
|
|
#endif
|
|
#ifdef HLRAD_GatherPatchLight
|
|
, 0
|
|
#endif
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l.miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, facenum
|
|
#endif
|
|
);
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && (f->styles[j] != 255); j++)
|
|
#endif
|
|
{
|
|
#ifdef ZHLT_XASH
|
|
vec_t dot = DotProduct (sampled2_direction[j], pointnormal);
|
|
VectorMA (sampled2_direction[j], -dot * 2, pointnormal, sampled2_direction[j]);
|
|
vec_t front = 0, back = 0;
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
front += ((1.0 - l.translucent_v[x]) * sampled[j][x]) / 3;
|
|
back += (l.translucent_v[x] * sampled2[j][x]) / 3;
|
|
}
|
|
front = fabs (VectorAvg (sampled[j])) > NORMAL_EPSILON? front / VectorAvg (sampled[j]): 0;
|
|
back = fabs (VectorAvg (sampled2[j])) > NORMAL_EPSILON? back / VectorAvg (sampled2[j]): 0;
|
|
#endif
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
sampled[j][x] = (1.0 - l.translucent_v[x]) * sampled[j][x] + l.translucent_v[x] * sampled2[j][x];
|
|
#ifdef ZHLT_XASH
|
|
sampled_direction[j][x] = front * sampled_direction[j][x] + back * sampled2_direction[j][x];
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (pointnormal, sampled_normal);
|
|
#endif
|
|
}
|
|
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && (f->styles[j] != 255); j++)
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
VectorCopy (sampled[j], fl_samples[j][i].light);
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (sampled_direction[j], fl_samples[j][i].light_direction);
|
|
#endif
|
|
#else
|
|
VectorCopy(sampled[j], facelight[facenum].samples[j][i].light);
|
|
#endif
|
|
|
|
#ifndef HLRAD_TRANCPARENCYLOSS_FIX
|
|
#ifdef HLRAD_HULLU
|
|
if(b_transparency_loss)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
VectorScale (fl_samples[j][i].light, light_left_for_facelight, fl_samples[j][i].light);
|
|
#ifdef ZHLT_XASH
|
|
VectorScale (fl_samples[j][i].light_direction, light_left_for_facelight, fl_samples[j][i].light_direction);
|
|
#endif
|
|
#else
|
|
VectorScale(facelight[facenum].samples[j][i].light, light_left_for_facelight, facelight[facenum].samples[j][i].light);
|
|
#endif
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef HLRAD_ACCURATEBOUNCE_SAMPLELIGHT
|
|
#ifdef ZHLT_TEXLIGHT
|
|
#ifdef HLRAD_AUTOCORING
|
|
AddSampleToPatch (&fl_samples[j][i], facenum, f_styles[j]); //LRC
|
|
#else
|
|
AddSampleToPatch(&facelight[facenum].samples[j][i], facenum, f->styles[j]); //LRC
|
|
#endif
|
|
#else
|
|
if (f->styles[j] == 0)
|
|
{
|
|
AddSampleToPatch(&facelight[facenum].samples[j][i], facenum);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
#ifdef ZHLT_XASH
|
|
for (k = 0; k < ALLSTYLES; k++) // fill 'sample.normal' for all 64 styles, just like what we did on 'sample.pos'
|
|
{
|
|
VectorCopy (sampled_normal, fl_samples[k][i].normal);
|
|
}
|
|
#endif
|
|
#endif // ifndef HLRAD_BLUR
|
|
} // end of i loop
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
free (sample_wallflags);
|
|
#endif
|
|
|
|
// average up the direct light on each patch for radiosity
|
|
#ifdef HLRAD_ACCURATEBOUNCE_SAMPLELIGHT
|
|
AddSamplesToPatches ((const sample_t **)fl_samples, f_styles, facenum, &l);
|
|
#endif
|
|
#ifndef HLRAD_GatherPatchLight
|
|
if (g_numbounce > 0)
|
|
#endif
|
|
{
|
|
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
|
|
{
|
|
#ifdef ZHLT_TEXLIGHT
|
|
//LRC:
|
|
unsigned istyle;
|
|
#ifdef HLRAD_AUTOCORING
|
|
#ifdef HLRAD_ACCURATEBOUNCE_SAMPLELIGHT
|
|
if (patch->samples <= ON_EPSILON * ON_EPSILON)
|
|
patch->samples = 0.0;
|
|
#endif
|
|
if (patch->samples)
|
|
{
|
|
for (istyle = 0; istyle < ALLSTYLES && patch->totalstyle_all[istyle] != 255; istyle++)
|
|
{
|
|
vec3_t v;
|
|
VectorScale (patch->samplelight_all[istyle], 1.0f / patch->samples, v);
|
|
VectorAdd (patch->directlight_all[istyle], v, patch->directlight_all[istyle]);
|
|
#ifdef ZHLT_XASH
|
|
VectorScale (patch->samplelight_all_direction[istyle], 1.0f / patch->samples, v);
|
|
VectorAdd (patch->directlight_all_direction[istyle], v, patch->directlight_all_direction[istyle]);
|
|
#endif
|
|
}
|
|
}
|
|
#else
|
|
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]);
|
|
}
|
|
}
|
|
#endif
|
|
//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
|
|
}
|
|
}
|
|
#ifdef HLRAD_GatherPatchLight
|
|
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
|
|
{
|
|
// get the PVS for the pos to limit the number of checks
|
|
if (!g_visdatasize)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs, 255, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs, 255, (g_numleafs + 7) / 8);
|
|
#endif
|
|
lastoffset = -1;
|
|
}
|
|
else
|
|
{
|
|
dleaf_t* leaf = PointInLeaf(patch->origin);
|
|
|
|
thisoffset = leaf->visofs;
|
|
#ifdef HLRAD_BLUR
|
|
if (patch == g_face_patches[facenum] || thisoffset != lastoffset)
|
|
#else
|
|
if (l.numsurfpt == 0 || thisoffset != lastoffset)
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_VIS_FIX
|
|
if (thisoffset == -1)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs, 0, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs, 0, (g_numleafs + 7) / 8);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
DecompressVis(&g_dvisdata[leaf->visofs], pvs, sizeof(pvs));
|
|
}
|
|
#else
|
|
hlassert(thisoffset != -1);
|
|
DecompressVis(&g_dvisdata[leaf->visofs], pvs, sizeof(pvs));
|
|
#endif
|
|
}
|
|
lastoffset = thisoffset;
|
|
}
|
|
#ifdef HLRAD_TRANSLUCENT
|
|
if (l.translucent_b)
|
|
{
|
|
if (!g_visdatasize)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs2, 255, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs2, 255, (g_numleafs + 7) / 8);
|
|
#endif
|
|
lastoffset2 = -1;
|
|
}
|
|
else
|
|
{
|
|
VectorMA (patch->origin, -(g_translucentdepth+2*PATCH_HUNT_OFFSET), l.facenormal, spot2);
|
|
dleaf_t* leaf2 = PointInLeaf(spot2);
|
|
|
|
thisoffset2 = leaf2->visofs;
|
|
if (l.numsurfpt == 0 || thisoffset2 != lastoffset2)
|
|
{
|
|
#ifdef HLRAD_VIS_FIX
|
|
if (thisoffset2 == -1)
|
|
{
|
|
#ifdef ZHLT_DecompressVis_FIX
|
|
memset (pvs2, 0, (g_dmodels[0].visleafs + 7) / 8);
|
|
#else
|
|
memset(pvs2, 0, (g_numleafs + 7) / 8);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
DecompressVis(&g_dvisdata[leaf2->visofs], pvs2, sizeof(pvs2));
|
|
}
|
|
#else
|
|
hlassert(thisoffset2 != -1);
|
|
DecompressVis(&g_dvisdata[leaf2->visofs], pvs2, sizeof(pvs2));
|
|
#endif
|
|
}
|
|
lastoffset2 = thisoffset2;
|
|
}
|
|
#ifdef HLRAD_AUTOCORING
|
|
vec3_t frontsampled[ALLSTYLES], backsampled[ALLSTYLES];
|
|
#ifdef ZHLT_XASH
|
|
vec3_t frontsampled_direction[ALLSTYLES], backsampled_direction[ALLSTYLES];
|
|
#endif
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
#else
|
|
vec3_t frontsampled[MAXLIGHTMAPS], backsampled[MAXLIGHTMAPS];
|
|
for (j = 0; j < MAXLIGHTMAPS; j++)
|
|
#endif
|
|
{
|
|
VectorClear (frontsampled[j]);
|
|
VectorClear (backsampled[j]);
|
|
#ifdef ZHLT_XASH
|
|
VectorClear (frontsampled_direction[j]);
|
|
VectorClear (backsampled_direction[j]);
|
|
#endif
|
|
}
|
|
VectorSubtract (vec3_origin, l.facenormal, normal2);
|
|
GatherSampleLight (patch->origin, pvs, l.facenormal, frontsampled,
|
|
#ifdef ZHLT_XASH
|
|
frontsampled_direction,
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
patch->totalstyle_all
|
|
#else
|
|
patch->totalstyle
|
|
#endif
|
|
, 1
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l.miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, facenum
|
|
#endif
|
|
);
|
|
GatherSampleLight (spot2, pvs2, normal2, backsampled,
|
|
#ifdef ZHLT_XASH
|
|
backsampled_direction,
|
|
#endif
|
|
#ifdef HLRAD_AUTOCORING
|
|
patch->totalstyle_all
|
|
#else
|
|
patch->totalstyle
|
|
#endif
|
|
, 1
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l.miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, facenum
|
|
#endif
|
|
);
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && patch->totalstyle_all[j] != 255; j++)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && (patch->totalstyle[j] != 255); j++)
|
|
#endif
|
|
{
|
|
#ifdef ZHLT_XASH
|
|
vec_t dot = DotProduct (backsampled_direction[j], l.facenormal);
|
|
VectorMA (backsampled_direction[j], -dot * 2, l.facenormal, backsampled_direction[j]);
|
|
vec_t front = 0, back = 0;
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
front += ((1.0 - l.translucent_v[x]) * frontsampled[j][x]) / 3;
|
|
back += (l.translucent_v[x] * backsampled[j][x]) / 3;
|
|
}
|
|
front = fabs (VectorAvg (frontsampled[j])) > NORMAL_EPSILON? front / VectorAvg (frontsampled[j]): 0;
|
|
back = fabs (VectorAvg (backsampled[j])) > NORMAL_EPSILON? back / VectorAvg (backsampled[j]): 0;
|
|
#endif
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
patch->totallight_all[j][x] += (1.0 - l.translucent_v[x]) * frontsampled[j][x] + l.translucent_v[x] * backsampled[j][x];
|
|
#ifdef ZHLT_XASH
|
|
patch->totallight_all_direction[j][x] += front * frontsampled_direction[j][x] + back * backsampled_direction[j][x];
|
|
#endif
|
|
#else
|
|
patch->totallight[j][x] += (1.0 - l.translucent_v[x]) * frontsampled[j][x] + l.translucent_v[x] * backsampled[j][x];
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GatherSampleLight (patch->origin, pvs, l.facenormal,
|
|
#ifdef HLRAD_AUTOCORING
|
|
patch->totallight_all,
|
|
#ifdef ZHLT_XASH
|
|
patch->totallight_all_direction,
|
|
#endif
|
|
patch->totalstyle_all
|
|
#else
|
|
patch->totallight, patch->totalstyle
|
|
#endif
|
|
, 1
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l.miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, facenum
|
|
#endif
|
|
);
|
|
}
|
|
#else
|
|
GatherSampleLight (patch->origin, pvs, l.facenormal,
|
|
#ifdef HLRAD_AUTOCORING
|
|
patch->totallight_all,
|
|
#ifdef ZHLT_XASH
|
|
patch->totallight_all_direction,
|
|
#endif
|
|
patch->totalstyle_all
|
|
#else
|
|
patch->totallight, patch->totalstyle
|
|
#endif
|
|
, 1
|
|
#ifdef HLRAD_DIVERSE_LIGHTING
|
|
, l.miptex
|
|
#endif
|
|
#ifdef HLRAD_TEXLIGHTGAP
|
|
, facenum
|
|
#endif
|
|
);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
// add an ambient term if desired
|
|
if (g_ambient[0] || g_ambient[1] || g_ambient[2])
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
if (f_styles[j] == 0)
|
|
{
|
|
s = fl_samples[j];
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++)
|
|
{
|
|
if (f->styles[j] == 0)
|
|
{
|
|
s = facelight[facenum].samples[j];
|
|
#endif
|
|
for (i = 0; i < l.numsurfpt; i++, s++)
|
|
{
|
|
VectorAdd(s->light, g_ambient, s->light);
|
|
#ifdef ZHLT_XASH
|
|
vec_t avg = VectorAvg (g_ambient);
|
|
VectorMA (s->light_direction, -DIFFUSE_DIRECTION_SCALE * avg, s->normal, s->light_direction);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// add circus lighting for finding black lightmaps
|
|
if (g_circus)
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
if (f_styles[j] == 0)
|
|
{
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++)
|
|
{
|
|
if (f->styles[j] == 0)
|
|
{
|
|
#endif
|
|
int amt = 7;
|
|
|
|
#ifdef HLRAD_AUTOCORING
|
|
s = fl_samples[j];
|
|
#else
|
|
s = facelight[facenum].samples[j];
|
|
#endif
|
|
|
|
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])
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
if (f_styles[j] == g_face_patches[facenum]->emitstyle)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (j == ALLSTYLES)
|
|
#else
|
|
for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++)
|
|
{
|
|
if (f->styles[j] == g_face_patches[facenum]->emitstyle) //LRC
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j == MAXLIGHTMAPS)
|
|
#endif
|
|
{
|
|
#ifdef HLRAD_READABLE_EXCEEDSTYLEWARNING
|
|
if (++stylewarningcount >= stylewarningnext)
|
|
{
|
|
stylewarningnext = stylewarningcount * 2;
|
|
Warning("Too many direct light styles on a face(?,?,?)");
|
|
Warning(" total %d warnings for too many styles", stylewarningcount);
|
|
}
|
|
#else
|
|
Warning("Too many direct light styles on a face(?,?,?)");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
if (f_styles[j] == 255)
|
|
{
|
|
f_styles[j] = g_face_patches[facenum]->emitstyle;
|
|
}
|
|
|
|
s = fl_samples[j];
|
|
#else
|
|
if (f->styles[j] == 255)
|
|
{
|
|
f->styles[j] = g_face_patches[facenum]->emitstyle;
|
|
}
|
|
|
|
s = facelight[facenum].samples[j];
|
|
#endif
|
|
for (i = 0; i < l.numsurfpt; i++, s++)
|
|
{
|
|
VectorAdd(s->light, g_face_patches[facenum]->baselight, s->light);
|
|
#ifdef ZHLT_XASH
|
|
vec_t avg = VectorAvg (g_face_patches[facenum]->baselight);
|
|
VectorMA (s->light_direction, -DIFFUSE_DIRECTION_SCALE * avg, s->normal, s->light_direction);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
//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
|
|
}
|
|
#ifdef HLRAD_AUTOCORING
|
|
// samples
|
|
{
|
|
facelight_t *fl = &facelight[facenum];
|
|
vec_t maxlights[ALLSTYLES];
|
|
for (j = 0; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
maxlights[j] = 0;
|
|
for (i = 0; i < fl->numsamples; i++)
|
|
{
|
|
vec_t b = VectorMaximum (fl_samples[j][i].light);
|
|
maxlights[j] = qmax (maxlights[j], b);
|
|
}
|
|
if (maxlights[j] <= g_corings[f_styles[j]] * 0.1) // light is too dim, discard this style to reduce RAM usage
|
|
{
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
ThreadLock ();
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = maxlights[j];
|
|
VectorCopy (g_face_centroids[facenum], g_maxdiscardedpos);
|
|
}
|
|
ThreadUnlock ();
|
|
}
|
|
maxlights[j] = 0;
|
|
}
|
|
}
|
|
for (k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
int bestindex = -1;
|
|
if (k == 0)
|
|
{
|
|
bestindex = 0;
|
|
}
|
|
else
|
|
{
|
|
vec_t bestmaxlight = 0;
|
|
for (j = 1; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
if (maxlights[j] > bestmaxlight + NORMAL_EPSILON)
|
|
{
|
|
bestmaxlight = maxlights[j];
|
|
bestindex = j;
|
|
}
|
|
}
|
|
}
|
|
if (bestindex != -1)
|
|
{
|
|
maxlights[bestindex] = 0;
|
|
f->styles[k] = f_styles[bestindex];
|
|
fl->samples[k] = (sample_t *)malloc (fl->numsamples * sizeof (sample_t));
|
|
hlassume (fl->samples[k] != NULL, assume_NoMemory);
|
|
memcpy (fl->samples[k], fl_samples[bestindex], fl->numsamples * sizeof (sample_t));
|
|
}
|
|
else
|
|
{
|
|
f->styles[k] = 255;
|
|
fl->samples[k] = NULL;
|
|
}
|
|
}
|
|
for (j = 1; j < ALLSTYLES && f_styles[j] != 255; j++)
|
|
{
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
ThreadLock ();
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = maxlights[j];
|
|
VectorCopy (g_face_centroids[facenum], g_maxdiscardedpos);
|
|
}
|
|
ThreadUnlock ();
|
|
}
|
|
}
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
{
|
|
free (fl_samples[j]);
|
|
}
|
|
}
|
|
// patches
|
|
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
|
|
{
|
|
vec_t maxlights[ALLSTYLES];
|
|
for (j = 0; j < ALLSTYLES && patch->totalstyle_all[j] != 255; j++)
|
|
{
|
|
maxlights[j] = VectorMaximum (patch->totallight_all[j]);
|
|
}
|
|
for (k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
int bestindex = -1;
|
|
if (k == 0)
|
|
{
|
|
bestindex = 0;
|
|
}
|
|
else
|
|
{
|
|
vec_t bestmaxlight = 0;
|
|
for (j = 1; j < ALLSTYLES && patch->totalstyle_all[j] != 255; j++)
|
|
{
|
|
if (maxlights[j] > bestmaxlight + NORMAL_EPSILON)
|
|
{
|
|
bestmaxlight = maxlights[j];
|
|
bestindex = j;
|
|
}
|
|
}
|
|
}
|
|
if (bestindex != -1)
|
|
{
|
|
maxlights[bestindex] = 0;
|
|
patch->totalstyle[k] = patch->totalstyle_all[bestindex];
|
|
VectorCopy (patch->totallight_all[bestindex], patch->totallight[k]);
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (patch->totallight_all_direction[bestindex], patch->totallight_direction[k]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
patch->totalstyle[k] = 255;
|
|
}
|
|
}
|
|
for (j = 1; j < ALLSTYLES && patch->totalstyle_all[j] != 255; j++)
|
|
{
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
ThreadLock ();
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = maxlights[j];
|
|
VectorCopy (patch->origin, g_maxdiscardedpos);
|
|
}
|
|
ThreadUnlock ();
|
|
}
|
|
}
|
|
for (j = 0; j < ALLSTYLES && patch->totalstyle_all[j] != 255; j++)
|
|
{
|
|
maxlights[j] = VectorMaximum (patch->directlight_all[j]);
|
|
}
|
|
for (k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
int bestindex = -1;
|
|
if (k == 0)
|
|
{
|
|
bestindex = 0;
|
|
}
|
|
else
|
|
{
|
|
vec_t bestmaxlight = 0;
|
|
for (j = 1; j < ALLSTYLES && patch->totalstyle_all[j] != 255; j++)
|
|
{
|
|
if (maxlights[j] > bestmaxlight + NORMAL_EPSILON)
|
|
{
|
|
bestmaxlight = maxlights[j];
|
|
bestindex = j;
|
|
}
|
|
}
|
|
}
|
|
if (bestindex != -1)
|
|
{
|
|
maxlights[bestindex] = 0;
|
|
patch->directstyle[k] = patch->totalstyle_all[bestindex];
|
|
VectorCopy (patch->directlight_all[bestindex], patch->directlight[k]);
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (patch->directlight_all_direction[bestindex], patch->directlight_direction[k]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
patch->directstyle[k] = 255;
|
|
}
|
|
}
|
|
for (j = 1; j < ALLSTYLES && patch->totalstyle_all[j] != 255; j++)
|
|
{
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
ThreadLock ();
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = maxlights[j];
|
|
VectorCopy (patch->origin, g_maxdiscardedpos);
|
|
}
|
|
ThreadUnlock ();
|
|
}
|
|
}
|
|
free (patch->totalstyle_all);
|
|
patch->totalstyle_all = NULL;
|
|
free (patch->samplelight_all);
|
|
patch->samplelight_all = NULL;
|
|
free (patch->totallight_all);
|
|
patch->totallight_all = NULL;
|
|
free (patch->directlight_all);
|
|
patch->directlight_all = NULL;
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_BLUR
|
|
free (l.lmcache);
|
|
#ifdef ZHLT_XASH
|
|
free (l.lmcache_direction);
|
|
#endif
|
|
#ifdef HLRAD_AVOIDNORMALFLIP
|
|
free (l.lmcache_normal);
|
|
#endif
|
|
#ifdef HLRAD_AVOIDWALLBLEED
|
|
free (l.lmcache_wallflags);
|
|
#endif
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
free (l.surfpt_position);
|
|
free (l.surfpt_surface);
|
|
#endif
|
|
#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;
|
|
#ifdef ZHLT_XASH
|
|
g_dlitdatasize = 0;
|
|
#endif
|
|
|
|
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 HLRAD_ENTSTRIPRAD
|
|
#ifndef HLRAD_REDUCELIGHTMAP
|
|
if (IntForKey (g_face_entity[facenum], "zhlt_striprad"))
|
|
{
|
|
continue;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef HLRAD_AUTOCORING
|
|
{
|
|
int i, j, k;
|
|
vec_t maxlights[ALLSTYLES];
|
|
{
|
|
vec3_t maxlights1[ALLSTYLES];
|
|
vec3_t maxlights2[ALLSTYLES];
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
{
|
|
VectorClear (maxlights1[j]);
|
|
VectorClear (maxlights2[j]);
|
|
}
|
|
for (k = 0; k < MAXLIGHTMAPS && f->styles[k] != 255; k++)
|
|
{
|
|
for (i = 0; i < fl->numsamples; i++)
|
|
{
|
|
VectorCompareMaximum (maxlights1[f->styles[k]], fl->samples[k][i].light, maxlights1[f->styles[k]]);
|
|
}
|
|
}
|
|
#ifdef HLRAD_LOCALTRIANGULATION
|
|
int numpatches;
|
|
const int *patches;
|
|
GetTriangulationPatches (facenum, &numpatches, &patches); // collect patches and their neighbors
|
|
|
|
for (i = 0; i < numpatches; i++)
|
|
{
|
|
patch = &g_patches[patches[i]];
|
|
#else
|
|
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
|
|
{
|
|
#endif
|
|
for (k = 0; k < MAXLIGHTMAPS && patch->totalstyle[k] != 255; k++)
|
|
{
|
|
VectorCompareMaximum (maxlights2[patch->totalstyle[k]], patch->totallight[k], maxlights2[patch->totalstyle[k]]);
|
|
}
|
|
}
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
{
|
|
vec3_t v;
|
|
VectorAdd (maxlights1[j], maxlights2[j], v);
|
|
maxlights[j] = VectorMaximum (v);
|
|
if (maxlights[j] <= g_corings[j] * 0.01)
|
|
{
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = maxlights[j];
|
|
VectorCopy (g_face_centroids[facenum], g_maxdiscardedpos);
|
|
}
|
|
maxlights[j] = 0;
|
|
}
|
|
}
|
|
}
|
|
unsigned char oldstyles[MAXLIGHTMAPS];
|
|
sample_t *oldsamples[MAXLIGHTMAPS];
|
|
for (k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
oldstyles[k] = f->styles[k];
|
|
oldsamples[k] = fl->samples[k];
|
|
}
|
|
for (k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
unsigned char beststyle = 255;
|
|
if (k == 0)
|
|
{
|
|
beststyle = 0;
|
|
}
|
|
else
|
|
{
|
|
vec_t bestmaxlight = 0;
|
|
for (j = 1; j < ALLSTYLES; j++)
|
|
{
|
|
if (maxlights[j] > bestmaxlight + NORMAL_EPSILON)
|
|
{
|
|
bestmaxlight = maxlights[j];
|
|
beststyle = j;
|
|
}
|
|
}
|
|
}
|
|
if (beststyle != 255)
|
|
{
|
|
maxlights[beststyle] = 0;
|
|
f->styles[k] = beststyle;
|
|
fl->samples[k] = (sample_t *)malloc (fl->numsamples * sizeof (sample_t));
|
|
hlassume (fl->samples[k] != NULL, assume_NoMemory);
|
|
for (i = 0; i < MAXLIGHTMAPS && oldstyles[i] != 255; i++)
|
|
{
|
|
if (oldstyles[i] == f->styles[k])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (i < MAXLIGHTMAPS && oldstyles[i] != 255)
|
|
{
|
|
memcpy (fl->samples[k], oldsamples[i], fl->numsamples * sizeof (sample_t));
|
|
}
|
|
else
|
|
{
|
|
memcpy (fl->samples[k], oldsamples[0], fl->numsamples * sizeof (sample_t)); // copy 'sample.pos' from style 0 to the new style - because 'sample.pos' is actually the same for all styles! (why did we decide to store it in many places?)
|
|
for (j = 0; j < fl->numsamples; j++)
|
|
{
|
|
VectorClear (fl->samples[k][j].light);
|
|
#ifdef ZHLT_XASH
|
|
VectorClear (fl->samples[k][j].light_direction);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f->styles[k] = 255;
|
|
fl->samples[k] = NULL;
|
|
}
|
|
}
|
|
for (j = 1; j < ALLSTYLES; j++)
|
|
{
|
|
if (maxlights[j] > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = maxlights[j];
|
|
VectorCopy (g_face_centroids[facenum], g_maxdiscardedpos);
|
|
}
|
|
}
|
|
for (k = 0; k < MAXLIGHTMAPS && oldstyles[k] != 255; k++)
|
|
{
|
|
free (oldsamples[k]);
|
|
}
|
|
}
|
|
#else
|
|
#ifdef ZHLT_TEXLIGHT
|
|
//LRC - find all the patch lightstyles, and add them to the ones used by this face
|
|
#ifdef HLRAD_STYLE_CORING
|
|
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
|
|
#else
|
|
patch = g_face_patches[facenum];
|
|
if (patch)
|
|
#endif
|
|
{
|
|
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)
|
|
{
|
|
#ifdef HLRAD_READABLE_EXCEEDSTYLEWARNING
|
|
if (++stylewarningcount >= stylewarningnext)
|
|
{
|
|
stylewarningnext = stylewarningcount * 2;
|
|
Warning("Too many direct light styles on a face(?,?,?)\n");
|
|
Warning(" total %d warnings for too many styles", stylewarningcount);
|
|
}
|
|
#else
|
|
Warning("Too many direct light styles on a face(?,?,?)\n");
|
|
#endif
|
|
}
|
|
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
|
|
#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;
|
|
hlassume (g_lightdatasize <= g_max_map_lightdata, assume_MAX_MAP_LIGHTING); //lightdata
|
|
#ifdef ZHLT_XASH
|
|
g_dlitdatasize += fl->numsamples * 3 * lightstyles;
|
|
hlassume (g_dlitdatasize < g_max_map_dlitdata, assume_MAX_MAP_LIGHTING);
|
|
#endif
|
|
|
|
}
|
|
}
|
|
#ifdef HLRAD_REDUCELIGHTMAP
|
|
void ReduceLightmap ()
|
|
{
|
|
byte *oldlightdata = (byte *)malloc (g_lightdatasize);
|
|
hlassume (oldlightdata != NULL, assume_NoMemory);
|
|
memcpy (oldlightdata, g_dlightdata, g_lightdatasize);
|
|
#ifdef ZHLT_XASH
|
|
if (g_dlitdatasize != g_lightdatasize)
|
|
{
|
|
Error ("g_dlitdatasize != g_lightdatasize");
|
|
}
|
|
byte *olddlitdata = (byte *)malloc (g_dlitdatasize);
|
|
hlassume (olddlitdata != NULL, assume_NoMemory);
|
|
memcpy (olddlitdata, g_ddlitdata, g_dlitdatasize);
|
|
g_dlitdatasize = 0;
|
|
#endif
|
|
g_lightdatasize = 0;
|
|
|
|
int facenum;
|
|
for (facenum = 0; facenum < g_numfaces; facenum++)
|
|
{
|
|
dface_t *f = &g_dfaces[facenum];
|
|
facelight_t *fl = &facelight[facenum];
|
|
if (g_texinfo[f->texinfo].flags & TEX_SPECIAL)
|
|
{
|
|
continue; // non-lit texture
|
|
}
|
|
#ifdef HLRAD_ENTSTRIPRAD
|
|
// just need to zero the lightmap so that it won't contribute to lightdata size
|
|
if (IntForKey (g_face_entity[facenum], "zhlt_striprad"))
|
|
{
|
|
f->lightofs = g_lightdatasize;
|
|
for (int k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
f->styles[k] = 255;
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
#if 0 //debug. --vluzacn
|
|
const char *lightmapcolor = ValueForKey (g_face_entity[facenum], "zhlt_rad");
|
|
if (*lightmapcolor)
|
|
{
|
|
hlassume (MAXLIGHTMAPS == 4, assume_first);
|
|
int styles[4], values[4][3];
|
|
if (sscanf (lightmapcolor, "%d=%d,%d,%d %d=%d,%d,%d %d=%d,%d,%d %d=%d,%d,%d"
|
|
, &styles[0], &values[0][0], &values[0][1], &values[0][2]
|
|
, &styles[1], &values[1][0], &values[1][1], &values[1][2]
|
|
, &styles[2], &values[2][0], &values[2][1], &values[2][2]
|
|
, &styles[3], &values[3][0], &values[3][1], &values[3][2]
|
|
) != 16)
|
|
{
|
|
Error ("Bad value for 'zhlt_rad'.");
|
|
}
|
|
f->lightofs = g_lightdatasize;
|
|
int i, k;
|
|
for (k = 0; k < 4; k++)
|
|
{
|
|
f->styles[k] = 255;
|
|
}
|
|
for (k = 0; k < 4 && styles[k] != 255; k++)
|
|
{
|
|
f->styles[k] = styles[k];
|
|
hlassume (g_lightdatasize + fl->numsamples * 3 <= g_max_map_lightdata, assume_MAX_MAP_LIGHTING);
|
|
#ifdef ZHLT_XASH
|
|
hlassume (g_dlitdatasize + fl->numsamples * 3 <= g_max_map_dlitdata, assume_MAX_MAP_LIGHTING);
|
|
#endif
|
|
for (i = 0; i < fl->numsamples; i++)
|
|
{
|
|
VectorCopy (values[k], (byte *)&g_dlightdata[g_lightdatasize + i * 3]);
|
|
#ifdef ZHLT_XASH
|
|
VectorFill ((byte *)&g_ddlitdata[g_lightdatasize + i * 3], 128);
|
|
#endif
|
|
}
|
|
g_lightdatasize += fl->numsamples * 3;
|
|
#ifdef ZHLT_XASH
|
|
g_dlitdatasize += fl->numsamples * 3;
|
|
#endif
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
if (f->lightofs == -1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int i, k;
|
|
int oldofs;
|
|
unsigned char oldstyles[MAXLIGHTMAPS];
|
|
oldofs = f->lightofs;
|
|
f->lightofs = g_lightdatasize;
|
|
for (k = 0; k < MAXLIGHTMAPS; k++)
|
|
{
|
|
oldstyles[k] = f->styles[k];
|
|
f->styles[k] = 255;
|
|
}
|
|
int numstyles = 0;
|
|
for (k = 0; k < MAXLIGHTMAPS && oldstyles[k] != 255; k++)
|
|
{
|
|
unsigned char maxb = 0;
|
|
for (i = 0; i < fl->numsamples; i++)
|
|
{
|
|
unsigned char *v = &oldlightdata[oldofs + fl->numsamples * 3 * k + i * 3];
|
|
maxb = qmax (maxb, VectorMaximum (v));
|
|
}
|
|
if (maxb <= 0) // black
|
|
{
|
|
continue;
|
|
}
|
|
f->styles[numstyles] = oldstyles[k];
|
|
hlassume (g_lightdatasize + fl->numsamples * 3 * (numstyles + 1) <= g_max_map_lightdata, assume_MAX_MAP_LIGHTING);
|
|
memcpy (&g_dlightdata[f->lightofs + fl->numsamples * 3 * numstyles], &oldlightdata[oldofs + fl->numsamples * 3 * k], fl->numsamples * 3);
|
|
#ifdef ZHLT_XASH
|
|
hlassume (g_dlitdatasize + fl->numsamples * 3 * (numstyles + 1) <= g_max_map_dlitdata, assume_MAX_MAP_LIGHTING);
|
|
memcpy (&g_ddlitdata[f->lightofs + fl->numsamples * 3 * numstyles], &olddlitdata[oldofs + fl->numsamples * 3 * k], fl->numsamples * 3);
|
|
#endif
|
|
numstyles++;
|
|
}
|
|
g_lightdatasize += fl->numsamples * 3 * numstyles;
|
|
#ifdef ZHLT_XASH
|
|
g_dlitdatasize += fl->numsamples * 3 * numstyles;
|
|
#endif
|
|
}
|
|
free (oldlightdata);
|
|
#ifdef ZHLT_XASH
|
|
free (olddlitdata);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#ifdef HLRAD_MDL_LIGHT_HACK
|
|
|
|
// Change the sample light right under a mdl file entity's origin.
|
|
// Use this when "mdl" in shadow has incorrect brightness.
|
|
|
|
const int MLH_MAXFACECOUNT = 16;
|
|
const int MLH_MAXSAMPLECOUNT = 4;
|
|
const vec_t MLH_LEFT = 0;
|
|
const vec_t MLH_RIGHT = 1;
|
|
|
|
typedef struct
|
|
{
|
|
vec3_t origin;
|
|
vec3_t floor;
|
|
struct
|
|
{
|
|
int num;
|
|
struct
|
|
{
|
|
bool exist;
|
|
int seq;
|
|
}
|
|
style[ALLSTYLES];
|
|
struct
|
|
{
|
|
int num;
|
|
vec3_t pos;
|
|
unsigned char* (style[ALLSTYLES]);
|
|
}
|
|
sample[MLH_MAXSAMPLECOUNT];
|
|
int samplecount;
|
|
}
|
|
face[MLH_MAXFACECOUNT];
|
|
int facecount;
|
|
} mdllight_t;
|
|
|
|
#ifdef HLRAD_MDL_LIGHT_HACK_NEW
|
|
int MLH_AddFace (mdllight_t *ml, int facenum)
|
|
{
|
|
dface_t *f = &g_dfaces[facenum];
|
|
int i, j;
|
|
for (i = 0; i < ml->facecount; i++)
|
|
{
|
|
if (ml->face[i].num == facenum)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
if (ml->facecount >= MLH_MAXFACECOUNT)
|
|
{
|
|
return -1;
|
|
}
|
|
i = ml->facecount;
|
|
ml->facecount++;
|
|
ml->face[i].num = facenum;
|
|
ml->face[i].samplecount = 0;
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
{
|
|
ml->face[i].style[j].exist = false;
|
|
}
|
|
for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++)
|
|
{
|
|
ml->face[i].style[f->styles[j]].exist = true;
|
|
ml->face[i].style[f->styles[j]].seq = j;
|
|
}
|
|
return i;
|
|
}
|
|
void MLH_AddSample (mdllight_t *ml, int facenum, int w, int h, int s, int t, const vec3_t pos)
|
|
{
|
|
dface_t *f = &g_dfaces[facenum];
|
|
int i, j;
|
|
int r = MLH_AddFace (ml, facenum);
|
|
if (r == -1)
|
|
{
|
|
return;
|
|
}
|
|
int size = w * h;
|
|
int num = s + w * t;
|
|
for (i = 0; i < ml->face[r].samplecount; i++)
|
|
{
|
|
if (ml->face[r].sample[i].num == num)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (ml->face[r].samplecount >= MLH_MAXSAMPLECOUNT)
|
|
{
|
|
return;
|
|
}
|
|
i = ml->face[r].samplecount;
|
|
ml->face[r].samplecount++;
|
|
ml->face[r].sample[i].num = num;
|
|
VectorCopy (pos, ml->face[r].sample[i].pos);
|
|
for (j = 0; j < ALLSTYLES; j++)
|
|
{
|
|
if (ml->face[r].style[j].exist)
|
|
{
|
|
ml->face[r].sample[i].style[j] = &g_dlightdata[f->lightofs + (num + size * ml->face[r].style[j].seq) * 3];
|
|
}
|
|
}
|
|
}
|
|
void MLH_CalcExtents (const dface_t *f, int *texturemins, int *extents)
|
|
{
|
|
#ifdef ZHLT_64BIT_FIX
|
|
int bmins[2];
|
|
int bmaxs[2];
|
|
int i;
|
|
|
|
GetFaceExtents (f - g_dfaces, bmins, bmaxs);
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
texturemins[i] = bmins[i] * TEXTURE_STEP;
|
|
extents[i] = (bmaxs[i] - bmins[i]) * TEXTURE_STEP;
|
|
}
|
|
#else
|
|
float mins[2], maxs[2];
|
|
int bmins[2], bmaxs[2];
|
|
texinfo_t *tex;
|
|
tex = &g_texinfo[f->texinfo];
|
|
mins[0] = mins[1] = 999999;
|
|
maxs[0] = maxs[1] = -99999;
|
|
int i;
|
|
for (i = 0; i < f->numedges; i++)
|
|
{
|
|
int e;
|
|
dvertex_t *v;
|
|
int j;
|
|
e = g_dsurfedges[f->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++)
|
|
{
|
|
float 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++)
|
|
{
|
|
bmins[i] = floor (mins[i] / TEXTURE_STEP);
|
|
bmaxs[i] = ceil (maxs[i] / TEXTURE_STEP);
|
|
texturemins[i] = bmins[i] * TEXTURE_STEP;
|
|
extents[i] = (bmaxs[i] - bmins[i]) * TEXTURE_STEP;
|
|
}
|
|
#endif
|
|
}
|
|
void MLH_GetSamples_r (mdllight_t *ml, int nodenum, const float *start, const float *end)
|
|
{
|
|
if (nodenum < 0)
|
|
return;
|
|
dnode_t *node = &g_dnodes[nodenum];
|
|
dplane_t *plane;
|
|
float front, back, frac;
|
|
float mid[3];
|
|
int side;
|
|
plane = &g_dplanes[node->planenum];
|
|
front = DotProduct (start, plane->normal) - plane->dist;
|
|
back = DotProduct (end, plane->normal) - plane->dist;
|
|
side = front < 0;
|
|
if ((back < 0) == side)
|
|
{
|
|
MLH_GetSamples_r (ml, node->children[side], start, end);
|
|
return;
|
|
}
|
|
frac = front / (front - back);
|
|
mid[0] = start[0] + (end[0] - start[0]) * frac;
|
|
mid[1] = start[1] + (end[1] - start[1]) * frac;
|
|
mid[2] = start[2] + (end[2] - start[2]) * frac;
|
|
MLH_GetSamples_r (ml, node->children[side], start, mid);
|
|
if (ml->facecount > 0)
|
|
{
|
|
return;
|
|
}
|
|
{
|
|
int i;
|
|
for (i = 0; i < node->numfaces; i++)
|
|
{
|
|
dface_t *f = &g_dfaces[node->firstface + i];
|
|
texinfo_t *tex = &g_texinfo[f->texinfo];
|
|
const char *texname = GetTextureByNumber (f->texinfo);
|
|
if (!strncmp (texname, "sky", 3))
|
|
{
|
|
continue;
|
|
}
|
|
if (f->lightofs == -1)
|
|
{
|
|
continue;
|
|
}
|
|
int s = (int)(DotProduct (mid, tex->vecs[0]) + tex->vecs[0][3]);
|
|
int t = (int)(DotProduct (mid, tex->vecs[1]) + tex->vecs[1][3]);
|
|
int texturemins[2], extents[2];
|
|
MLH_CalcExtents (f, texturemins, extents);
|
|
if (s < texturemins[0] || t < texturemins[1])
|
|
{
|
|
continue;
|
|
}
|
|
int ds = s - texturemins[0];
|
|
int dt = t - texturemins[1];
|
|
if (ds > extents[0] || dt > extents[1])
|
|
{
|
|
continue;
|
|
}
|
|
ds >>= 4;
|
|
dt >>= 4;
|
|
MLH_AddSample (ml, node->firstface + i, extents[0] / TEXTURE_STEP + 1, extents[1] / TEXTURE_STEP + 1, ds, dt, mid);
|
|
break;
|
|
}
|
|
}
|
|
if (ml->facecount > 0)
|
|
{
|
|
VectorCopy (mid, ml->floor);
|
|
return;
|
|
}
|
|
MLH_GetSamples_r (ml, node->children[!side], mid, end);
|
|
}
|
|
void MLH_mdllightCreate (mdllight_t *ml)
|
|
{
|
|
// code from Quake
|
|
float p[3];
|
|
float end[3];
|
|
ml->facecount = 0;
|
|
VectorCopy (ml->origin, ml->floor);
|
|
VectorCopy (ml->origin, p);
|
|
VectorCopy (ml->origin, end);
|
|
end[2] -= 2048;
|
|
MLH_GetSamples_r (ml, 0, p, end);
|
|
}
|
|
#else
|
|
void MLH_mdllightCreate (mdllight_t *ml)
|
|
{
|
|
int i, j, k;
|
|
vec_t height, minheight = BOGUS_RANGE;
|
|
ml->facecount = 0;
|
|
for (i = 0; i < g_numfaces; ++i)
|
|
{
|
|
if (strcasecmp (ValueForKey (g_face_entity[i], "classname"), "worldspawn"))
|
|
continue;
|
|
const dface_t *f = &g_dfaces[i];
|
|
const dplane_t *p = getPlaneFromFace(f);
|
|
Winding *w=new Winding (*f);
|
|
for (j = 0;j < w->m_NumPoints; j++)
|
|
{
|
|
VectorAdd(w->m_Points[j], g_face_offset[i], w->m_Points[j]);
|
|
}
|
|
vec3_t delta , sect;
|
|
VectorCopy (ml->origin, delta);
|
|
delta[2] -= BOGUS_RANGE;
|
|
if (intersect_linesegment_plane(p, ml->origin, delta, sect) && point_in_winding (*w, *p, sect))
|
|
{
|
|
height = ml->origin[2] - sect[2];
|
|
if (height >= 0 && height <= minheight)
|
|
minheight = height;
|
|
}
|
|
delete w;
|
|
}
|
|
VectorCopy (ml->origin, ml->floor);
|
|
ml->floor[2] -= minheight;
|
|
for (i = 0; i < g_numfaces; ++i)
|
|
{
|
|
if (strcasecmp (ValueForKey (g_face_entity[i], "classname"), "worldspawn"))
|
|
continue;
|
|
const dface_t *f = &g_dfaces[i];
|
|
const dplane_t *p = getPlaneFromFace(f);
|
|
Winding *w=new Winding (*f);
|
|
if (g_texinfo[f->texinfo].flags & TEX_SPECIAL)
|
|
{
|
|
continue; // non-lit texture
|
|
}
|
|
for (j = 0;j < w->m_NumPoints; j++)
|
|
{
|
|
VectorAdd(w->m_Points[j], g_face_offset[i], w->m_Points[j]);
|
|
}
|
|
vec3_t delta , sect;
|
|
VectorCopy (ml->origin, delta);
|
|
delta[2] -= BOGUS_RANGE;
|
|
if (intersect_linesegment_plane(p, ml->origin, delta, sect) && VectorCompare (sect, ml->floor))
|
|
{
|
|
bool inlightmap = false;
|
|
{
|
|
vec3_t v;
|
|
facesampleinfo_t *info = &facesampleinfo[i];
|
|
int w = info->texsize[0] + 1;
|
|
int h = info->texsize[1] + 1;
|
|
vec_t vs, vt;
|
|
int s1, s2, t1, t2, s, t;
|
|
VectorCopy (ml->floor, v);
|
|
VectorSubtract (v, info->offset, v);
|
|
VectorSubtract (v, info->texorg, v);
|
|
vs = DotProduct (v, info->worldtotex[0]);
|
|
vt = DotProduct (v, info->worldtotex[1]);
|
|
s1 = (int)floor((vs-MLH_LEFT)/TEXTURE_STEP) - info->texmins[0];
|
|
s2 = (int)floor((vs+MLH_RIGHT)/TEXTURE_STEP) - info->texmins[0];
|
|
t1 = (int)floor((vt-MLH_LEFT)/TEXTURE_STEP) - info->texmins[1];
|
|
t2 = (int)floor((vt+MLH_RIGHT)/TEXTURE_STEP) - info->texmins[1];
|
|
for (s=s1; s<=s2; ++s)
|
|
for (t=t1; t<=t2; ++t)
|
|
if (s>=0 && s<w && t>=0 && t<h)
|
|
inlightmap = true;
|
|
}
|
|
if (inlightmap && ml->facecount < MLH_MAXFACECOUNT)
|
|
{
|
|
ml->face[ml->facecount].num = i;
|
|
ml->facecount++;
|
|
}
|
|
}
|
|
delete w;
|
|
}
|
|
for (i = 0; i < ml->facecount; ++i)
|
|
{
|
|
const dface_t *f = &g_dfaces[ml->face[i].num];
|
|
for (j = 0; j < ALLSTYLES; ++j)
|
|
ml->face[i].style[j].exist = false;
|
|
for (j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; ++j)
|
|
{
|
|
ml->face[i].style[f->styles[j]].exist = true;
|
|
ml->face[i].style[f->styles[j]].seq = j;
|
|
}
|
|
ml->face[i].samplecount = 0;
|
|
if (j == 0)
|
|
continue;
|
|
|
|
const facelight_t *fl=&facelight[ml->face[i].num];
|
|
{
|
|
vec3_t v;
|
|
facesampleinfo_t *info = &facesampleinfo[ml->face[i].num];
|
|
int w = info->texsize[0] + 1;
|
|
int h = info->texsize[1] + 1;
|
|
vec_t vs, vt;
|
|
int s1, s2, t1, t2, s, t;
|
|
VectorCopy (ml->floor, v);
|
|
VectorSubtract (v, info->offset, v);
|
|
VectorSubtract (v, info->texorg, v);
|
|
vs = DotProduct (v, info->worldtotex[0]);
|
|
vt = DotProduct (v, info->worldtotex[1]);
|
|
s1 = (int)floor((vs-MLH_LEFT)/TEXTURE_STEP) - info->texmins[0];
|
|
s2 = (int)floor((vs+MLH_RIGHT)/TEXTURE_STEP) - info->texmins[0];
|
|
t1 = (int)floor((vt-MLH_LEFT)/TEXTURE_STEP) - info->texmins[1];
|
|
t2 = (int)floor((vt+MLH_RIGHT)/TEXTURE_STEP) - info->texmins[1];
|
|
for (s=s1; s<=s2; ++s)
|
|
for (t=t1; t<=t2; ++t)
|
|
if (s>=0 && s<w && t>=0 && t<h)
|
|
if (ml->face[i].samplecount < MLH_MAXSAMPLECOUNT)
|
|
{
|
|
ml->face[i].sample[ml->face[i].samplecount].num = s + t * w;
|
|
VectorAdd (info->offset, info->texorg, v);
|
|
vs = TEXTURE_STEP * (s + info->texmins[0]);
|
|
vt = TEXTURE_STEP * (t + info->texmins[1]);
|
|
VectorMA (v, vs, info->textoworld[0], v);
|
|
VectorMA (v, vt, info->textoworld[1], v);
|
|
VectorCopy (v, ml->face[i].sample[ml->face[i].samplecount].pos);
|
|
ml->face[i].samplecount++;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < ml->face[i].samplecount; ++j)
|
|
{
|
|
for (k = 0; k < ALLSTYLES; ++k)
|
|
if (ml->face[i].style[k].exist)
|
|
{
|
|
ml->face[i].sample[j].style[k] =
|
|
&g_dlightdata[f->lightofs + ml->face[i].style[k].seq * fl->numsamples * 3 + ml->face[i].sample[j].num * 3];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int MLH_CopyLight (const vec3_t from, const vec3_t to)
|
|
{
|
|
int i, j, k, count = 0;
|
|
mdllight_t mlfrom, mlto;
|
|
VectorCopy (from, mlfrom.origin);
|
|
VectorCopy (to, mlto.origin);
|
|
MLH_mdllightCreate (&mlfrom);
|
|
MLH_mdllightCreate (&mlto);
|
|
if (mlfrom.facecount == 0 || mlfrom.face[0].samplecount == 0)
|
|
return -1;
|
|
for (i = 0; i < mlto.facecount; ++i)
|
|
for (j = 0; j < mlto.face[i].samplecount; ++j, ++count)
|
|
for (k = 0; k < ALLSTYLES; ++k)
|
|
if (mlto.face[i].style[k].exist && mlfrom.face[0].style[k].exist)
|
|
{
|
|
VectorCopy (mlfrom.face[0].sample[0].style[k],mlto.face[i].sample[j].style[k]);
|
|
Developer (DEVELOPER_LEVEL_SPAM, "Mdl Light Hack: face (%d) sample (%d) style (%d) position (%f,%f,%f)\n",
|
|
mlto.face[i].num, mlto.face[i].sample[j].num, k,
|
|
mlto.face[i].sample[j].pos[0], mlto.face[i].sample[j].pos[1], mlto.face[i].sample[j].pos[2]);
|
|
}
|
|
Developer (DEVELOPER_LEVEL_MESSAGE, "Mdl Light Hack: %d sample light copied from (%f,%f,%f) to (%f,%f,%f)\n",
|
|
count, mlfrom.floor[0], mlfrom.floor[1], mlfrom.floor[2], mlto.floor[0], mlto.floor[1], mlto.floor[2]);
|
|
return count;
|
|
}
|
|
|
|
void MdlLightHack ()
|
|
{
|
|
int ient;
|
|
entity_t *ent1, *ent2;
|
|
vec3_t origin1, origin2;
|
|
const char *target;
|
|
#ifndef HLRAD_MDL_LIGHT_HACK_NEW
|
|
double start, end;
|
|
#endif
|
|
int used = 0, countent = 0, countsample = 0, r;
|
|
#ifndef HLRAD_MDL_LIGHT_HACK_NEW
|
|
start = I_FloatTime();
|
|
#endif
|
|
for (ient = 0; ient < g_numentities; ++ient)
|
|
{
|
|
ent1 = &g_entities[ient];
|
|
target = ValueForKey (ent1, "zhlt_copylight");
|
|
if (!strcmp (target, ""))
|
|
continue;
|
|
used = 1;
|
|
ent2 = FindTargetEntity (target);
|
|
if (ent2 == NULL)
|
|
{
|
|
Warning ("target entity '%s' not found", target);
|
|
continue;
|
|
}
|
|
GetVectorForKey (ent1, "origin", origin1);
|
|
GetVectorForKey (ent2, "origin", origin2);
|
|
r = MLH_CopyLight (origin2, origin1);
|
|
if (r < 0)
|
|
Warning ("can not copy light from (%f,%f,%f)", origin2[0], origin2[1], origin2[2]);
|
|
else
|
|
{
|
|
countent += 1;
|
|
countsample += r;
|
|
}
|
|
}
|
|
#ifndef HLRAD_MDL_LIGHT_HACK_NEW
|
|
end = I_FloatTime();
|
|
#endif
|
|
if (used)
|
|
#ifdef HLRAD_MDL_LIGHT_HACK_NEW
|
|
Log ("Adjust mdl light: modified %d samples for %d entities\n", countsample, countent);
|
|
#else
|
|
Log("Mdl Light Hack: %d entities %d samples (%.2f seconds)\n", countent, countsample, end - start);
|
|
#endif
|
|
}
|
|
#endif /*HLRAD_MDL_LIGHT_HACK*/
|
|
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
|
|
typedef struct facelightlist_s
|
|
{
|
|
int facenum;
|
|
facelightlist_s *next;
|
|
}
|
|
facelightlist_t;
|
|
|
|
static facelightlist_t *g_dependentfacelights[MAX_MAP_FACES];
|
|
|
|
// =====================================================================================
|
|
// CreateFacelightDependencyList
|
|
// =====================================================================================
|
|
void CreateFacelightDependencyList ()
|
|
{
|
|
int facenum;
|
|
dface_t *f;
|
|
facelight_t *fl;
|
|
int i;
|
|
int k;
|
|
int surface;
|
|
facelightlist_t *item;
|
|
|
|
for (i = 0; i < MAX_MAP_FACES; i++)
|
|
{
|
|
g_dependentfacelights[i] = NULL;
|
|
}
|
|
|
|
// for each face
|
|
for (facenum = 0; facenum < g_numfaces; facenum++)
|
|
{
|
|
f = &g_dfaces[facenum];
|
|
fl = &facelight[facenum];
|
|
if (g_texinfo[f->texinfo].flags & TEX_SPECIAL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (k = 0; k < MAXLIGHTMAPS && f->styles[k] != 255; k++)
|
|
{
|
|
for (i = 0; i < fl->numsamples; i++)
|
|
{
|
|
surface = fl->samples[k][i].surface; // that surface contains at least one sample from this face
|
|
if (0 <= surface && surface < g_numfaces)
|
|
{
|
|
// insert this face into the dependency list of that surface
|
|
for (item = g_dependentfacelights[surface]; item != NULL; item = item->next)
|
|
{
|
|
if (item->facenum == facenum)
|
|
break;
|
|
}
|
|
if (item)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
item = (facelightlist_t *)malloc (sizeof (facelightlist_t));
|
|
hlassume (item != NULL, assume_NoMemory);
|
|
item->facenum = facenum;
|
|
item->next = g_dependentfacelights[surface];
|
|
g_dependentfacelights[surface] = item;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// =====================================================================================
|
|
// FreeFacelightDependencyList
|
|
// =====================================================================================
|
|
void FreeFacelightDependencyList ()
|
|
{
|
|
int i;
|
|
facelightlist_t *item;
|
|
|
|
for (i = 0; i < MAX_MAP_FACES; i++)
|
|
{
|
|
while (g_dependentfacelights[i])
|
|
{
|
|
item = g_dependentfacelights[i];
|
|
g_dependentfacelights[i] = item->next;
|
|
free (item);
|
|
}
|
|
}
|
|
}
|
|
|
|
// =====================================================================================
|
|
// ScaleDirectLights
|
|
// =====================================================================================
|
|
void ScaleDirectLights ()
|
|
{
|
|
int facenum;
|
|
dface_t *f;
|
|
facelight_t *fl;
|
|
int i;
|
|
int k;
|
|
sample_t *samp;
|
|
|
|
for (facenum = 0; facenum < g_numfaces; facenum++)
|
|
{
|
|
f = &g_dfaces[facenum];
|
|
|
|
if (g_texinfo[f->texinfo].flags & TEX_SPECIAL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
fl = &facelight[facenum];
|
|
|
|
for (k = 0; k < MAXLIGHTMAPS && f->styles[k] != 255; k++)
|
|
{
|
|
for (i = 0; i < fl->numsamples; i++)
|
|
{
|
|
samp = &fl->samples[k][i];
|
|
VectorScale (samp->light, g_direct_scale, samp->light);
|
|
#ifdef ZHLT_XASH
|
|
VectorScale (samp->light_direction, g_direct_scale, samp->light_direction);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// =====================================================================================
|
|
// AddPatchLights
|
|
// This function is run multithreaded
|
|
// =====================================================================================
|
|
void AddPatchLights (int facenum)
|
|
{
|
|
dface_t *f;
|
|
#ifndef HLRAD_LOCALTRIANGULATION
|
|
lerpTriangulation_t *trian;
|
|
#endif
|
|
facelightlist_t *item;
|
|
dface_t *f_other;
|
|
facelight_t *fl_other;
|
|
int k;
|
|
int i;
|
|
sample_t *samp;
|
|
|
|
f = &g_dfaces[facenum];
|
|
|
|
if (g_texinfo[f->texinfo].flags & TEX_SPECIAL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#ifndef HLRAD_LOCALTRIANGULATION
|
|
trian = CreateTriangulation (facenum);
|
|
#endif
|
|
|
|
for (item = g_dependentfacelights[facenum]; item != NULL; item = item->next)
|
|
{
|
|
f_other = &g_dfaces[item->facenum];
|
|
fl_other = &facelight[item->facenum];
|
|
for (k = 0; k < MAXLIGHTMAPS && f_other->styles[k] != 255; k++)
|
|
{
|
|
for (i = 0; i < fl_other->numsamples; i++)
|
|
{
|
|
samp = &fl_other->samples[k][i];
|
|
if (samp->surface != facenum)
|
|
{ // the sample is not in this surface
|
|
continue;
|
|
}
|
|
|
|
{
|
|
vec3_t v;
|
|
#ifdef ZHLT_XASH
|
|
vec3_t v_direction;
|
|
#endif
|
|
|
|
#ifdef HLRAD_LOCALTRIANGULATION
|
|
int style = f_other->styles[k];
|
|
InterpolateSampleLight (samp->pos, samp->surface, 1, &style, &v
|
|
#ifdef ZHLT_XASH
|
|
, &v_direction
|
|
#endif
|
|
);
|
|
#else
|
|
SampleTriangulation (trian, samp->pos, v,
|
|
#ifdef ZHLT_XASH
|
|
v_direction,
|
|
#endif
|
|
f_other->styles[k]); //LRC
|
|
#endif
|
|
|
|
#ifdef HLRAD_STYLE_CORING
|
|
VectorAdd (samp->light, v, v);
|
|
#ifdef ZHLT_XASH
|
|
VectorAdd (samp->light_direction, v_direction, v_direction);
|
|
#endif
|
|
if (VectorMaximum (v) >= g_corings[f_other->styles[k]])
|
|
{
|
|
VectorCopy (v, samp->light);
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (v_direction, samp->light_direction);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef HLRAD_AUTOCORING
|
|
if (VectorMaximum (v) > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
ThreadLock ();
|
|
if (VectorMaximum (v) > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = VectorMaximum (v);
|
|
VectorCopy (samp->pos, g_maxdiscardedpos);
|
|
}
|
|
ThreadUnlock ();
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
VectorAdd (samp->light, v, samp->light);
|
|
#endif
|
|
}
|
|
} // loop samples
|
|
}
|
|
}
|
|
|
|
#ifndef HLRAD_LOCALTRIANGULATION
|
|
FreeTriangulation (trian);
|
|
#endif
|
|
}
|
|
|
|
#endif /*HLRAD_GROWSAMPLE*/
|
|
// =====================================================================================
|
|
// FinalLightFace
|
|
// Add the indirect lighting on top of the direct lighting and save into final map format
|
|
// =====================================================================================
|
|
void FinalLightFace(const int facenum)
|
|
{
|
|
#ifdef HLRAD_DEBUG_DRAWPOINTS
|
|
if (facenum == 0 && g_drawsample)
|
|
{
|
|
char name[_MAX_PATH+20];
|
|
sprintf (name, "%s_sample.pts", g_Mapname);
|
|
Log ("Writing '%s' ...\n", name);
|
|
FILE *f;
|
|
f = fopen(name, "w");
|
|
if (f)
|
|
{
|
|
const int pos_count = 15;
|
|
const vec3_t pos[pos_count] = {{0,0,0},{1,0,0},{0,1,0},{-1,0,0},{0,-1,0},{1,0,0},{0,0,1},{-1,0,0},{0,0,-1},{0,-1,0},{0,0,1},{0,1,0},{0,0,-1},{1,0,0},{0,0,0}};
|
|
int i, j, k;
|
|
vec3_t v, dist;
|
|
for (i = 0; i < g_numfaces; ++i)
|
|
{
|
|
const facelight_t *fl=&facelight[i];
|
|
for (j = 0; j < fl->numsamples; ++j)
|
|
{
|
|
VectorCopy (fl->samples[0][j].pos, v);
|
|
VectorSubtract (v, g_drawsample_origin, dist);
|
|
if (DotProduct (dist, dist) < g_drawsample_radius * g_drawsample_radius)
|
|
{
|
|
for (k = 0; k < pos_count; ++k)
|
|
fprintf (f, "%g %g %g\n", v[0]+pos[k][0], v[1]+pos[k][1], v[2]+pos[k][2]);
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
Log ("OK.\n");
|
|
}
|
|
else
|
|
Log ("Error.\n");
|
|
}
|
|
#endif
|
|
int i, j, k;
|
|
vec3_t lb, v;
|
|
facelight_t* fl;
|
|
sample_t* samp;
|
|
float minlight;
|
|
int lightstyles;
|
|
dface_t* f;
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
lerpTriangulation_t* trian = NULL;
|
|
#endif
|
|
#ifdef HLRAD_FinalLightFace_VL
|
|
vec3_t *original_basiclight;
|
|
int (*final_basiclight)[3];
|
|
int lbi[3];
|
|
#ifdef ZHLT_XASH
|
|
vec3_t *original_basicdirection;
|
|
vec3_t *final_basicdirection;
|
|
vec3_t direction;
|
|
vec3_t directionnormals[3];
|
|
vec3_t debug_original_light;
|
|
vec3_t debug_final_light;
|
|
vec3_t debug_original_direction;
|
|
vec3_t debug_final_direction;
|
|
#endif
|
|
#endif
|
|
|
|
// ------------------------------------------------------------------------
|
|
// 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
|
|
}
|
|
|
|
#ifdef HLRAD_ENTSTRIPRAD
|
|
#ifndef HLRAD_REDUCELIGHTMAP
|
|
if (IntForKey (g_face_entity[facenum], "zhlt_striprad"))
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
for (lightstyles = 0; lightstyles < MAXLIGHTMAPS; lightstyles++)
|
|
{
|
|
if (f->styles[lightstyles] == 255)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!lightstyles)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// set up the triangulation
|
|
//
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
#ifndef HLRAD_GatherPatchLight
|
|
if (g_numbounce)
|
|
#endif
|
|
{
|
|
trian = CreateTriangulation(facenum);
|
|
}
|
|
#endif
|
|
//
|
|
// sample the triangulation
|
|
//
|
|
minlight = FloatForKey(g_face_entity[facenum], "_minlight") * 128;
|
|
|
|
#ifdef HLRAD_FinalLightFace_VL
|
|
original_basiclight = (vec3_t *)calloc (fl->numsamples, sizeof(vec3_t));
|
|
final_basiclight = (int (*)[3])calloc (fl->numsamples, sizeof(int [3]));
|
|
hlassume (original_basiclight != NULL, assume_NoMemory);
|
|
hlassume (final_basiclight != NULL, assume_NoMemory);
|
|
#ifdef ZHLT_XASH
|
|
original_basicdirection = (vec3_t *)calloc (fl->numsamples, sizeof(vec3_t));
|
|
final_basicdirection = (vec3_t *)calloc (fl->numsamples, sizeof (vec3_t));
|
|
hlassume (original_basicdirection != NULL, assume_NoMemory);
|
|
hlassume (final_basicdirection != NULL, assume_NoMemory);
|
|
#endif
|
|
#endif
|
|
for (k = 0; k < lightstyles; k++)
|
|
{
|
|
samp = fl->samples[k];
|
|
for (j = 0; j < fl->numsamples; j++, samp++)
|
|
{
|
|
#ifdef ZHLT_XASH
|
|
{
|
|
|
|
VectorCopy (samp->normal, directionnormals[2]);
|
|
|
|
vec3_t texdirections[2];
|
|
const vec3_t &facenormal = getPlaneFromFace (f)->normal;
|
|
texinfo_t *tx = &g_texinfo[f->texinfo];
|
|
for (int side = 0; side < 2; side++)
|
|
{
|
|
CrossProduct (facenormal, tx->vecs[!side], texdirections[side]);
|
|
VectorNormalize (texdirections[side]);
|
|
if (DotProduct (texdirections[side], tx->vecs[side]) < 0)
|
|
{
|
|
VectorSubtract (vec3_origin, texdirections[side], texdirections[side]);
|
|
}
|
|
}
|
|
|
|
for (int side = 0; side < 2; side++)
|
|
{
|
|
vec_t dot;
|
|
dot = DotProduct (texdirections[side], samp->normal);
|
|
VectorMA (texdirections[side], -dot, samp->normal, directionnormals[side]);
|
|
VectorNormalize (directionnormals[side]);
|
|
}
|
|
VectorSubtract (vec3_origin, directionnormals[1], directionnormals[1]);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_GROWSAMPLE
|
|
VectorCopy (samp->light, lb);
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (samp->light_direction, direction);
|
|
#endif
|
|
#else
|
|
// 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.
|
|
|
|
// Default direct_scale has been changed from 2 to 1 and default scale has been changed from 1 to 2. --vluzacn
|
|
VectorScale(samp->light, g_direct_scale, lb);
|
|
#ifdef ZHLT_XASH
|
|
VectorScale (samp->light_direction, g_direct_scale, direction);
|
|
vec3_t v_direction;
|
|
#endif
|
|
|
|
#ifdef ZHLT_TEXLIGHT
|
|
#ifndef HLRAD_GatherPatchLight
|
|
if (g_numbounce)//LRC && (k == 0))
|
|
#endif
|
|
{
|
|
SampleTriangulation(trian, samp->pos, v,
|
|
#ifdef ZHLT_XASH
|
|
v_direction,
|
|
#endif
|
|
f->styles[k]); //LRC
|
|
#else
|
|
if (
|
|
#ifndef HLRAD_GatherPatchLight
|
|
g_numbounce &&
|
|
#endif
|
|
(k == 0))
|
|
{
|
|
SampleTriangulation(trian, samp->pos, v);
|
|
#endif
|
|
|
|
if (isPointFinite(v))
|
|
{
|
|
#ifdef HLRAD_STYLE_CORING
|
|
VectorAdd (lb, v, v);
|
|
#ifdef ZHLT_XASH
|
|
VectorAdd (direction, v_direction, v_direction);
|
|
#endif
|
|
if (VectorMaximum (v) >= g_corings[f->styles[k]])
|
|
{
|
|
VectorCopy (v, lb);
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (v_direction, direction);
|
|
#endif
|
|
}
|
|
#ifdef HLRAD_AUTOCORING
|
|
else
|
|
{
|
|
if (VectorMaximum (v) > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
ThreadLock ();
|
|
if (VectorMaximum (v) > g_maxdiscardedlight + NORMAL_EPSILON)
|
|
{
|
|
g_maxdiscardedlight = VectorMaximum (v);
|
|
VectorCopy (samp->pos, g_maxdiscardedpos);
|
|
}
|
|
ThreadUnlock ();
|
|
}
|
|
}
|
|
#endif
|
|
#else
|
|
VectorAdd(lb, v, lb);
|
|
#endif
|
|
}
|
|
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]);
|
|
}
|
|
|
|
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_FinalLightFace_VL
|
|
if (f->styles[0] != 0)
|
|
{
|
|
Warning ("wrong f->styles[0]");
|
|
}
|
|
VectorCompareMaximum (lb, vec3_origin, lb);
|
|
if (k == 0)
|
|
{
|
|
VectorCopy (lb, original_basiclight[j]);
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (direction, original_basicdirection[j]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
VectorAdd (lb, original_basiclight[j], lb);
|
|
#ifdef ZHLT_XASH
|
|
VectorAdd (direction, original_basicdirection[j], direction);
|
|
#endif
|
|
}
|
|
#ifdef ZHLT_XASH
|
|
{
|
|
VectorCopy (lb, debug_original_light);
|
|
VectorCopy (direction, debug_original_direction);
|
|
// get the real direction
|
|
// this is what the direction should be after style 0 and this style are added together
|
|
vec_t avg = VectorAvg (lb);
|
|
if (avg > NORMAL_EPSILON)
|
|
{
|
|
VectorScale (direction, 1 / avg, direction);
|
|
}
|
|
else
|
|
{
|
|
VectorClear (direction);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
// ------------------------------------------------------------------------
|
|
// Changes by Adam Foster - afoster@compsoc.man.ac.uk
|
|
// colour lightscale:
|
|
#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;
|
|
}
|
|
}
|
|
|
|
#ifndef HLRAD_FinalLightFace_VL
|
|
// 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;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ------------------------------------------------------------------------
|
|
// 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
|
|
|
|
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.
|
|
|
|
#ifndef HLRAD_FinalLightFace_VL
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#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
|
|
|
|
#ifdef HLRAD_PRESERVELIGHTMAPCOLOR
|
|
// clip from the top
|
|
{
|
|
vec_t max = VectorMaximum (lb);
|
|
if (g_limitthreshold >= 0 && max > g_limitthreshold)
|
|
{
|
|
if (!g_drawoverload)
|
|
{
|
|
VectorScale (lb, g_limitthreshold / max, lb);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_drawoverload)
|
|
{
|
|
VectorScale (lb, 0.1, lb); // darken good points
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_MINLIGHT
|
|
for (i = 0; i < 3; ++i)
|
|
if (lb[i] < g_minlight)
|
|
lb[i] = g_minlight;
|
|
#endif
|
|
// ------------------------------------------------------------------------
|
|
#ifdef HLRAD_FinalLightFace_VL
|
|
for (i = 0; i < 3; ++i)
|
|
{
|
|
lbi[i] = (int) floor (lb[i] + 0.5);
|
|
if (lbi[i] < 0) lbi[i] = 0;
|
|
}
|
|
#ifdef ZHLT_XASH
|
|
{
|
|
vec_t avg = (lbi[0] + lbi[1] + lbi[2]) / 3.0;
|
|
VectorScale (direction, avg, direction);
|
|
VectorCopy (lbi, debug_final_light);
|
|
VectorCopy (direction, debug_final_direction);
|
|
}
|
|
#endif
|
|
if (k == 0)
|
|
{
|
|
VectorCopy (lbi, final_basiclight[j]);
|
|
#ifdef ZHLT_XASH
|
|
VectorCopy (direction, final_basicdirection[j]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
VectorSubtract (lbi, final_basiclight[j], lbi);
|
|
#ifdef ZHLT_XASH
|
|
VectorSubtract (direction, final_basicdirection[j], direction);
|
|
#endif
|
|
}
|
|
#ifdef ZHLT_XASH
|
|
{
|
|
// because the direction will be multiplied with the brightness when styles are added together, now divide the direction by the brightness
|
|
vec_t avg = (lbi[0] + lbi[1] + lbi[2]) / 3.0;
|
|
avg = qmax (1, avg);
|
|
VectorScale (direction, 1 / avg, direction);
|
|
}
|
|
#endif
|
|
#ifdef HLRAD_WHOME
|
|
if (k == 0)
|
|
{
|
|
if (g_colour_jitter_hack[0] || g_colour_jitter_hack[1] || g_colour_jitter_hack[2])
|
|
for (i = 0; i < 3; i++)
|
|
lbi[i] += g_colour_jitter_hack[i] * ((float)rand() / RAND_MAX - 0.5);
|
|
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++)
|
|
lbi[i] += g_jitter_hack[i] * temp_rand;
|
|
}
|
|
}
|
|
#endif
|
|
for (i = 0; i < 3; ++i)
|
|
{
|
|
if (lbi[i] < 0) lbi[i] = 0;
|
|
if (lbi[i] > 255) lbi[i] = 255;
|
|
}
|
|
{
|
|
unsigned char* colors = &g_dlightdata[f->lightofs + k * fl->numsamples * 3 + j * 3];
|
|
|
|
colors[0] = (unsigned char)lbi[0];
|
|
colors[1] = (unsigned char)lbi[1];
|
|
colors[2] = (unsigned char)lbi[2];
|
|
}
|
|
#ifdef ZHLT_XASH
|
|
{
|
|
vec3_t v;
|
|
VectorScale (direction, g_directionscale, v); // the scale is calculated such that length(v) < 1
|
|
if (DotProduct (v, v) > 1 + NORMAL_EPSILON)
|
|
{
|
|
#if 0
|
|
{
|
|
ThreadLock ();
|
|
printf ("facenum = %d sample = %d styleindex = %d\n", facenum, j, k);
|
|
printf ("original_basiclight = %f %f %f\n", (vec_t)original_basiclight[j][0], (vec_t)original_basiclight[j][1], (vec_t)original_basiclight[j][2]);
|
|
printf ("original_basicdirection = %f %f %f\n", (vec_t)original_basicdirection[j][0], (vec_t)original_basicdirection[j][1], (vec_t)original_basicdirection[j][2]);
|
|
printf ("final_basiclight = %f %f %f\n", (vec_t)final_basiclight[j][0], (vec_t)final_basiclight[j][1], (vec_t)final_basiclight[j][2]);
|
|
printf ("final_basicdirection = %f %f %f\n", (vec_t)final_basicdirection[j][0], (vec_t)final_basicdirection[j][1], (vec_t)final_basicdirection[j][2]);
|
|
printf ("debug_original_light = %f %f %f\n", (vec_t)debug_original_light[0], (vec_t)debug_original_light[1], (vec_t)debug_original_light[2]);
|
|
printf ("debug_original_direction = %f %f %f\n", (vec_t)debug_original_direction[0], (vec_t)debug_original_direction[1], (vec_t)debug_original_direction[2]);
|
|
printf ("debug_final_light = %f %f %f\n", (vec_t)debug_final_light[0], (vec_t)debug_final_light[1], (vec_t)debug_final_light[2]);
|
|
printf ("debug_final_direction = %f %f %f\n", (vec_t)debug_final_direction[0], (vec_t)debug_final_direction[1], (vec_t)debug_final_direction[2]);
|
|
ThreadUnlock ();
|
|
}
|
|
#endif
|
|
VectorNormalize (v);
|
|
}
|
|
VectorSubtract (vec3_origin, v, v); // let the direction point from face sample to light source
|
|
unsigned char *dots = &g_ddlitdata[f->lightofs + k * fl->numsamples * 3 + j * 3];
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
int i;
|
|
i = DotProduct (v, directionnormals[x]) * 128 + 128;
|
|
i = qmax (0, qmin (i, 255));
|
|
dots[x] = (unsigned char)i;
|
|
}
|
|
}
|
|
#endif
|
|
#else
|
|
{
|
|
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];
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef HLRAD_FinalLightFace_VL
|
|
free (original_basiclight);
|
|
free (final_basiclight);
|
|
#ifdef ZHLT_XASH
|
|
free (original_basicdirection);
|
|
free (final_basicdirection);
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef HLRAD_GROWSAMPLE
|
|
#ifndef HLRAD_GatherPatchLight
|
|
if (g_numbounce)
|
|
#endif
|
|
{
|
|
FreeTriangulation(trian);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef ZHLT_TEXLIGHT
|
|
//LRC
|
|
vec3_t totallight_default = { 0, 0, 0 };
|
|
#ifdef ZHLT_XASH
|
|
vec3_t totallight_default_direction = { 0, 0, 0 };
|
|
#endif
|
|
|
|
//LRC - utility for getting the right totallight value from a patch
|
|
vec3_t* GetTotalLight(patch_t* patch, int style
|
|
#ifdef ZHLT_XASH
|
|
, const vec3_t *&direction_out
|
|
#endif
|
|
)
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAXLIGHTMAPS && patch->totalstyle[i] != 255; i++)
|
|
{
|
|
if (patch->totalstyle[i] == style)
|
|
{
|
|
#ifdef ZHLT_XASH
|
|
direction_out = &(patch->totallight_direction[i]);
|
|
#endif
|
|
return &(patch->totallight[i]);
|
|
}
|
|
}
|
|
#ifdef ZHLT_XASH
|
|
direction_out = &totallight_default_direction;
|
|
#endif
|
|
return &totallight_default;
|
|
}
|
|
|
|
#endif
|