vhlt/hlrad/lerp.cpp
2016-09-21 00:07:53 +03:00

3382 lines
92 KiB
C++

#include "qrad.h"
#ifdef HLRAD_LOCALTRIANGULATION
#include <vector>
#include <algorithm>
#endif
int g_lerp_enabled = DEFAULT_LERP_ENABLED;
#ifdef HLRAD_LOCALTRIANGULATION
struct interpolation_t
{
struct Point
{
int patchnum;
vec_t weight;
};
bool isbiased;
vec_t totalweight;
std::vector< Point > points;
};
struct localtriangulation_t
{
struct Wedge
{
enum eShape
{
eTriangular,
eConvex,
eConcave,
#ifdef HLRAD_BILINEARINTERPOLATION
eSquareLeft,
eSquareRight,
#endif
};
eShape shape;
int leftpatchnum;
vec3_t leftspot;
vec3_t leftdirection;
// right side equals to the left side of the next wedge
vec3_t wedgenormal; // for custom usage
};
struct HullPoint
{
vec3_t spot;
vec3_t direction;
};
dplane_t plane;
Winding winding;
vec3_t center; // center is on the plane
vec3_t normal;
int patchnum;
std::vector< int > neighborfaces; // including the face itself
std::vector< Wedge > sortedwedges; // in clockwise order (same as Winding)
std::vector< HullPoint > sortedhullpoints; // in clockwise order (same as Winding)
};
struct facetriangulation_t
{
struct Wall
{
vec3_t points[2];
vec3_t direction;
vec3_t normal;
};
int facenum;
std::vector< int > neighbors; // including the face itself
std::vector< Wall > walls;
std::vector< localtriangulation_t * > localtriangulations;
std::vector< int > usedpatches;
};
facetriangulation_t *g_facetriangulations[MAX_MAP_FACES];
static bool CalcAdaptedSpot (const localtriangulation_t *lt, const vec3_t position, int surface, vec3_t spot)
// If the surface formed by the face and its neighbor faces is not flat, the surface should be unfolded onto the face plane
// CalcAdaptedSpot calculates the coordinate of the unfolded spot on the face plane from the original position on the surface
// CalcAdaptedSpot(center) = {0,0,0}
// CalcAdaptedSpot(position on the face plane) = position - center
// Param position: must include g_face_offset
{
int i;
vec_t dot;
vec3_t surfacespot;
vec_t dist;
vec_t dist2;
vec3_t phongnormal;
vec_t frac;
vec3_t middle;
vec3_t v;
for (i = 0; i < (int)lt->neighborfaces.size (); i++)
{
if (lt->neighborfaces[i] == surface)
{
break;
}
}
if (i == (int)lt->neighborfaces.size ())
{
VectorClear (spot);
return false;
}
VectorSubtract (position, lt->center, surfacespot);
dot = DotProduct (surfacespot, lt->normal);
VectorMA (surfacespot, -dot, lt->normal, spot);
// use phong normal instead of face normal, because phong normal is a continuous function
GetPhongNormal (surface, position, phongnormal);
dot = DotProduct (spot, phongnormal);
if (fabs (dot) > ON_EPSILON)
{
frac = DotProduct (surfacespot, phongnormal) / dot;
frac = qmax (0, qmin (frac, 1)); // to correct some extreme cases
}
else
{
frac = 0;
}
VectorScale (spot, frac, middle);
dist = VectorLength (spot);
VectorSubtract (surfacespot, middle, v);
dist2 = VectorLength (middle) + VectorLength (v);
if (dist > ON_EPSILON && fabs (dist2 - dist) > ON_EPSILON)
{
VectorScale (spot, dist2 / dist, spot);
}
return true;
}
static vec_t GetAngle (const vec3_t leftdirection, const vec3_t rightdirection, const vec3_t normal)
{
vec_t angle;
vec3_t v;
CrossProduct (rightdirection, leftdirection, v);
angle = atan2 (DotProduct (v, normal), DotProduct (rightdirection, leftdirection));
return angle;
}
static vec_t GetAngleDiff (vec_t angle, vec_t base)
{
vec_t diff;
diff = angle - base;
if (diff < 0)
{
diff += 2 * Q_PI;
}
return diff;
}
static vec_t GetFrac (const vec3_t leftspot, const vec3_t rightspot, const vec3_t direction, const vec3_t normal)
{
vec3_t v;
vec_t dot1;
vec_t dot2;
vec_t frac;
CrossProduct (direction, normal, v);
dot1 = DotProduct (leftspot, v);
dot2 = DotProduct (rightspot, v);
// dot1 <= 0 < dot2
if (dot1 >= -NORMAL_EPSILON)
{
if (g_drawlerp && dot1 > ON_EPSILON)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 1.\n");
}
frac = 0.0;
}
else if (dot2 <= NORMAL_EPSILON)
{
if (g_drawlerp && dot2 < -ON_EPSILON)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 2.\n");
}
frac = 1.0;
}
else
{
frac = dot1 / (dot1 - dot2);
frac = qmax (0, qmin (frac, 1));
}
return frac;
}
static vec_t GetDirection (const vec3_t spot, const vec3_t normal, vec3_t direction_out)
{
vec_t dot;
dot = DotProduct (spot, normal);
VectorMA (spot, -dot, normal, direction_out);
return VectorNormalize (direction_out);
}
static bool CalcWeight (const localtriangulation_t *lt, const vec3_t spot, vec_t *weight_out)
// It returns true when the point is inside the hull region (with boundary), even if weight = 0.
{
vec3_t direction;
const localtriangulation_t::HullPoint *hp1;
const localtriangulation_t::HullPoint *hp2;
bool istoofar;
vec_t ratio;
int i;
int j;
vec_t angle;
std::vector< vec_t > angles;
vec_t frac;
vec_t len;
vec_t dist;
if (GetDirection (spot, lt->normal, direction) <= 2 * ON_EPSILON)
{
*weight_out = 1.0;
return true;
}
if ((int)lt->sortedhullpoints.size () == 0)
{
*weight_out = 0.0;
return false;
}
angles.resize ((int)lt->sortedhullpoints.size ());
for (i = 0; i < (int)lt->sortedhullpoints.size (); i++)
{
angle = GetAngle (lt->sortedhullpoints[i].direction, direction, lt->normal);
angles[i] = GetAngleDiff (angle, 0);
}
j = 0;
for (i = 1; i < (int)lt->sortedhullpoints.size (); i++)
{
if (angles[i] < angles[j])
{
j = i;
}
}
hp1 = &lt->sortedhullpoints[j];
hp2 = &lt->sortedhullpoints[(j + 1) % (int)lt->sortedhullpoints.size ()];
frac = GetFrac (hp1->spot, hp2->spot, direction, lt->normal);
len = (1 - frac) * DotProduct (hp1->spot, direction) + frac * DotProduct (hp2->spot, direction);
dist = DotProduct (spot, direction);
if (len <= ON_EPSILON / 4 || dist > len + 2 * ON_EPSILON)
{
istoofar = true;
ratio = 1.0;
}
else if (dist >= len - ON_EPSILON)
{
istoofar = false; // if we change this "false" to "true", we will see many places turned "green" in "-drawlerp" mode
ratio = 1.0; // in order to prevent excessively small weight
}
else
{
istoofar = false;
ratio = dist / len;
ratio = qmax (0, qmin (ratio, 1));
}
*weight_out = 1 - ratio;
return !istoofar;
}
#ifdef HLRAD_BILINEARINTERPOLATION
static void CalcInterpolation_Square (const localtriangulation_t *lt, int i, const vec3_t spot, interpolation_t *interp)
{
const localtriangulation_t::Wedge *w1;
const localtriangulation_t::Wedge *w2;
const localtriangulation_t::Wedge *w3;
vec_t weights[4];
vec_t dot1;
vec_t dot2;
vec_t dot;
vec3_t normal1;
vec3_t normal2;
vec3_t normal;
vec_t frac;
vec_t frac_near;
vec_t frac_far;
vec_t ratio;
vec3_t mid_far;
vec3_t mid_near;
vec3_t test;
w1 = &lt->sortedwedges[i];
w2 = &lt->sortedwedges[(i + 1) % (int)lt->sortedwedges.size ()];
w3 = &lt->sortedwedges[(i + 2) % (int)lt->sortedwedges.size ()];
if (w1->shape != localtriangulation_t::Wedge::eSquareLeft || w2->shape != localtriangulation_t::Wedge::eSquareRight)
{
Error ("CalcInterpolation_Square: internal error: not square.");
}
weights[0] = 0.0;
weights[1] = 0.0;
weights[2] = 0.0;
weights[3] = 0.0;
// find mid_near on (o,p3), mid_far on (p1,p2), spot on (mid_near,mid_far)
CrossProduct (w1->leftdirection, lt->normal, normal1);
VectorNormalize (normal1);
CrossProduct (w2->wedgenormal, lt->normal, normal2);
VectorNormalize (normal2);
dot1 = DotProduct (spot, normal1) - 0;
dot2 = DotProduct (spot, normal2) - DotProduct (w3->leftspot, normal2);
if (dot1 <= NORMAL_EPSILON)
{
frac = 0.0;
}
else if (dot2 <= NORMAL_EPSILON)
{
frac = 1.0;
}
else
{
frac = dot1 / (dot1 + dot2);
frac = qmax (0, qmin (frac, 1));
}
dot1 = DotProduct (w3->leftspot, normal1) - 0;
dot2 = 0 - DotProduct (w3->leftspot, normal2);
if (dot1 <= NORMAL_EPSILON)
{
frac_near = 1.0;
}
else if (dot2 <= NORMAL_EPSILON)
{
frac_near = 0.0;
}
else
{
frac_near = (frac * dot2) / ((1 - frac) * dot1 + frac * dot2);
}
VectorScale (w3->leftspot, frac_near, mid_near);
dot1 = DotProduct (w2->leftspot, normal1) - 0;
dot2 = DotProduct (w1->leftspot, normal2) - DotProduct (w3->leftspot, normal2);
if (dot1 <= NORMAL_EPSILON)
{
frac_far = 1.0;
}
else if (dot2 <= NORMAL_EPSILON)
{
frac_far = 0.0;
}
else
{
frac_far = (frac * dot2) / ((1 - frac) * dot1 + frac * dot2);
}
VectorScale (w1->leftspot, 1 - frac_far, mid_far);
VectorMA (mid_far, frac_far, w2->leftspot, mid_far);
CrossProduct (lt->normal, w3->leftdirection, normal);
VectorNormalize (normal);
dot = DotProduct (spot, normal) - 0;
dot1 = (1 - frac_far) * DotProduct (w1->leftspot, normal) + frac_far * DotProduct (w2->leftspot, normal) - 0;
if (dot <= NORMAL_EPSILON)
{
ratio = 0.0;
}
else if (dot >= dot1)
{
ratio = 1.0;
}
else
{
ratio = dot / dot1;
ratio = qmax (0, qmin (ratio, 1));
}
VectorScale (mid_near, 1 - ratio, test);
VectorMA (test, ratio, mid_far, test);
VectorSubtract (test, spot, test);
if (g_drawlerp && VectorLength (test) > 4 * ON_EPSILON)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 12.\n");
}
weights[0] += 0.5 * (1 - ratio) * (1 - frac_near);
weights[3] += 0.5 * (1 - ratio) * frac_near;
weights[1] += 0.5 * ratio * (1 - frac_far);
weights[2] += 0.5 * ratio * frac_far;
// find mid_near on (o,p1), mid_far on (p2,p3), spot on (mid_near,mid_far)
CrossProduct (lt->normal, w3->leftdirection, normal1);
VectorNormalize (normal1);
CrossProduct (w1->wedgenormal, lt->normal, normal2);
VectorNormalize (normal2);
dot1 = DotProduct (spot, normal1) - 0;
dot2 = DotProduct (spot, normal2) - DotProduct (w1->leftspot, normal2);
if (dot1 <= NORMAL_EPSILON)
{
frac = 0.0;
}
else if (dot2 <= NORMAL_EPSILON)
{
frac = 1.0;
}
else
{
frac = dot1 / (dot1 + dot2);
frac = qmax (0, qmin (frac, 1));
}
dot1 = DotProduct (w1->leftspot, normal1) - 0;
dot2 = 0 - DotProduct (w1->leftspot, normal2);
if (dot1 <= NORMAL_EPSILON)
{
frac_near = 1.0;
}
else if (dot2 <= NORMAL_EPSILON)
{
frac_near = 0.0;
}
else
{
frac_near = (frac * dot2) / ((1 - frac) * dot1 + frac * dot2);
}
VectorScale (w1->leftspot, frac_near, mid_near);
dot1 = DotProduct (w2->leftspot, normal1) - 0;
dot2 = DotProduct (w3->leftspot, normal2) - DotProduct (w1->leftspot, normal2);
if (dot1 <= NORMAL_EPSILON)
{
frac_far = 1.0;
}
else if (dot2 <= NORMAL_EPSILON)
{
frac_far = 0.0;
}
else
{
frac_far = (frac * dot2) / ((1 - frac) * dot1 + frac * dot2);
}
VectorScale (w3->leftspot, 1 - frac_far, mid_far);
VectorMA (mid_far, frac_far, w2->leftspot, mid_far);
CrossProduct (w1->leftdirection, lt->normal, normal);
VectorNormalize (normal);
dot = DotProduct (spot, normal) - 0;
dot1 = (1 - frac_far) * DotProduct (w3->leftspot, normal) + frac_far * DotProduct (w2->leftspot, normal) - 0;
if (dot <= NORMAL_EPSILON)
{
ratio = 0.0;
}
else if (dot >= dot1)
{
ratio = 1.0;
}
else
{
ratio = dot / dot1;
ratio = qmax (0, qmin (ratio, 1));
}
VectorScale (mid_near, 1 - ratio, test);
VectorMA (test, ratio, mid_far, test);
VectorSubtract (test, spot, test);
if (g_drawlerp && VectorLength (test) > 4 * ON_EPSILON)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 13.\n");
}
weights[0] += 0.5 * (1 - ratio) * (1 - frac_near);
weights[1] += 0.5 * (1 - ratio) * frac_near;
weights[3] += 0.5 * ratio * (1 - frac_far);
weights[2] += 0.5 * ratio * frac_far;
interp->isbiased = false;
interp->totalweight = 1.0;
interp->points.resize (4);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = weights[0];
interp->points[1].patchnum = w1->leftpatchnum;
interp->points[1].weight = weights[1];
interp->points[2].patchnum = w2->leftpatchnum;
interp->points[2].weight = weights[2];
interp->points[3].patchnum = w3->leftpatchnum;
interp->points[3].weight = weights[3];
}
#endif
static void CalcInterpolation (const localtriangulation_t *lt, const vec3_t spot, interpolation_t *interp)
// The interpolation function is defined over the entire plane, so CalcInterpolation never fails.
{
vec3_t direction;
const localtriangulation_t::Wedge *w;
const localtriangulation_t::Wedge *wnext;
int i;
int j;
vec_t angle;
std::vector< vec_t > angles;
if (GetDirection (spot, lt->normal, direction) <= 2 * ON_EPSILON)
{
// spot happens to be at the center
interp->isbiased = false;
interp->totalweight = 1.0;
interp->points.resize (1);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = 1.0;
return;
}
if ((int)lt->sortedwedges.size () == 0) // this local triangulation only has center patch
{
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (1);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = 1.0;
return;
}
// Find the wedge with minimum non-negative angle (counterclockwise) pass the spot
angles.resize ((int)lt->sortedwedges.size ());
for (i = 0; i < (int)lt->sortedwedges.size (); i++)
{
angle = GetAngle (lt->sortedwedges[i].leftdirection, direction, lt->normal);
angles[i] = GetAngleDiff (angle, 0);
}
j = 0;
for (i = 1; i < (int)lt->sortedwedges.size (); i++)
{
if (angles[i] < angles[j])
{
j = i;
}
}
w = &lt->sortedwedges[j];
wnext = &lt->sortedwedges[(j + 1) % (int)lt->sortedwedges.size ()];
// Different wedge types have different interpolation methods
switch (w->shape)
{
#ifdef HLRAD_BILINEARINTERPOLATION
case localtriangulation_t::Wedge::eSquareLeft:
case localtriangulation_t::Wedge::eSquareRight:
#endif
case localtriangulation_t::Wedge::eTriangular:
// w->wedgenormal is undefined
{
vec_t frac;
vec_t len;
vec_t dist;
bool istoofar;
vec_t ratio;
frac = GetFrac (w->leftspot, wnext->leftspot, direction, lt->normal);
len = (1 - frac) * DotProduct (w->leftspot, direction) + frac * DotProduct (wnext->leftspot, direction);
dist = DotProduct (spot, direction);
if (len <= ON_EPSILON / 4 || dist > len + 2 * ON_EPSILON)
{
istoofar = true;
ratio = 1.0;
}
else if (dist >= len - ON_EPSILON)
{
istoofar = false;
ratio = 1.0;
}
else
{
istoofar = false;
ratio = dist / len;
ratio = qmax (0, qmin (ratio, 1));
}
if (istoofar)
{
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (2);
interp->points[0].patchnum = w->leftpatchnum;
interp->points[0].weight = 1 - frac;
interp->points[1].patchnum = wnext->leftpatchnum;
interp->points[1].weight = frac;
}
#ifdef HLRAD_BILINEARINTERPOLATION
else if (w->shape == localtriangulation_t::Wedge::eSquareLeft)
{
i = w - &lt->sortedwedges[0];
CalcInterpolation_Square (lt, i, spot, interp);
}
else if (w->shape == localtriangulation_t::Wedge::eSquareRight)
{
i = w - &lt->sortedwedges[0];
i = (i - 1 + (int)lt->sortedwedges.size ()) % (int)lt->sortedwedges.size ();
CalcInterpolation_Square (lt, i, spot, interp);
}
#endif
else
{
interp->isbiased = false;
interp->totalweight = 1.0;
interp->points.resize (3);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = 1 - ratio;
interp->points[1].patchnum = w->leftpatchnum;
interp->points[1].weight = ratio * (1 - frac);
interp->points[2].patchnum = wnext->leftpatchnum;
interp->points[2].weight = ratio * frac;
}
}
break;
case localtriangulation_t::Wedge::eConvex:
// w->wedgenormal is the unit vector pointing from w->leftspot to wnext->leftspot
{
vec_t dot;
vec_t dot1;
vec_t dot2;
vec_t frac;
dot1 = DotProduct (w->leftspot, w->wedgenormal) - DotProduct (spot, w->wedgenormal);
dot2 = DotProduct (wnext->leftspot, w->wedgenormal) - DotProduct (spot, w->wedgenormal);
dot = 0 - DotProduct (spot, w->wedgenormal);
// for eConvex type: dot1 < dot < dot2
if (g_drawlerp && (dot1 > dot || dot > dot2))
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 3.\n");
}
if (dot1 >= -NORMAL_EPSILON) // 0 <= dot1 < dot < dot2
{
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (1);
interp->points[0].patchnum = w->leftpatchnum;
interp->points[0].weight = 1.0;
}
else if (dot2 <= NORMAL_EPSILON) // dot1 < dot < dot2 <= 0
{
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (1);
interp->points[0].patchnum = wnext->leftpatchnum;
interp->points[0].weight = 1.0;
}
else if (dot > 0) // dot1 < 0 < dot < dot2
{
frac = dot1 / (dot1 - dot);
frac = qmax (0, qmin (frac, 1));
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (2);
interp->points[0].patchnum = w->leftpatchnum;
interp->points[0].weight = 1 - frac;
interp->points[1].patchnum = lt->patchnum;
interp->points[1].weight = frac;
}
else // dot1 < dot <= 0 < dot2
{
frac = dot / (dot - dot2);
frac = qmax (0, qmin (frac, 1));
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (2);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = 1 - frac;
interp->points[1].patchnum = wnext->leftpatchnum;
interp->points[1].weight = frac;
}
}
break;
case localtriangulation_t::Wedge::eConcave:
{
vec_t len;
vec_t dist;
vec_t ratio;
if (DotProduct (spot, w->wedgenormal) < 0) // the spot is closer to the left edge than the right edge
{
len = DotProduct (w->leftspot, w->leftdirection);
dist = DotProduct (spot, w->leftdirection);
if (g_drawlerp && len <= ON_EPSILON)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 4.\n");
}
if (dist <= NORMAL_EPSILON)
{
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (1);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = 1.0;
}
else if (dist >= len)
{
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (1);
interp->points[0].patchnum = w->leftpatchnum;
interp->points[0].weight = 1.0;
}
else
{
ratio = dist / len;
ratio = qmax (0, qmin (ratio, 1));
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (2);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = 1 - ratio;
interp->points[1].patchnum = w->leftpatchnum;
interp->points[1].weight = ratio;
}
}
else // the spot is closer to the right edge than the left edge
{
len = DotProduct (wnext->leftspot, wnext->leftdirection);
dist = DotProduct (spot, wnext->leftdirection);
if (g_drawlerp && len <= ON_EPSILON)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 5.\n");
}
if (dist <= NORMAL_EPSILON)
{
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (1);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = 1.0;
}
else if (dist >= len)
{
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (1);
interp->points[0].patchnum = wnext->leftpatchnum;
interp->points[0].weight = 1.0;
}
else
{
ratio = dist / len;
ratio = qmax (0, qmin (ratio, 1));
interp->isbiased = true;
interp->totalweight = 1.0;
interp->points.resize (2);
interp->points[0].patchnum = lt->patchnum;
interp->points[0].weight = 1 - ratio;
interp->points[1].patchnum = wnext->leftpatchnum;
interp->points[1].weight = ratio;
}
}
}
break;
default:
Error ("CalcInterpolation: internal error: invalid wedge type.");
break;
}
}
static void ApplyInterpolation (const interpolation_t *interp, int numstyles, const int *styles, vec3_t *outs
#ifdef ZHLT_XASH
, vec3_t *outs_direction
#endif
)
{
int i;
int j;
for (j = 0; j < numstyles; j++)
{
VectorClear (outs[j]);
#ifdef ZHLT_XASH
VectorClear (outs_direction[j]);
#endif
}
if (interp->totalweight <= 0)
{
return;
}
for (i = 0; i < (int)interp->points.size (); i++)
{
for (j = 0; j < numstyles; j++)
{
#ifdef ZHLT_XASH
const vec3_t *b_direction;
#endif
const vec3_t *b = GetTotalLight (&g_patches[interp->points[i].patchnum], styles[j]
#ifdef ZHLT_XASH
, b_direction
#endif
);
VectorMA (outs[j], interp->points[i].weight / interp->totalweight, *b, outs[j]);
#ifdef ZHLT_XASH
VectorMA (outs_direction[j], interp->points[i].weight / interp->totalweight, *b_direction, outs_direction[j]);
#endif
}
}
}
// =====================================================================================
// InterpolateSampleLight
// =====================================================================================
void InterpolateSampleLight (const vec3_t position, int surface, int numstyles, const int *styles, vec3_t *outs
#ifdef ZHLT_XASH
, vec3_t *outs_direction
#endif
)
{
try
{
const facetriangulation_t *ft;
interpolation_t *maininterp;
std::vector< vec_t > localweights;
std::vector< interpolation_t * > localinterps;
int i;
int j;
int n;
const facetriangulation_t *ft2;
const localtriangulation_t *lt;
vec3_t spot;
vec_t weight;
interpolation_t *interp;
const localtriangulation_t *best;
vec3_t v;
vec_t dist;
vec_t bestdist;
vec_t dot;
if (surface < 0 || surface >= g_numfaces)
{
Error ("InterpolateSampleLight: internal error: surface number out of range.");
}
ft = g_facetriangulations[surface];
maininterp = new interpolation_t;
maininterp->points.reserve (64);
// Calculate local interpolations and their weights
localweights.resize (0);
localinterps.resize (0);
if (g_lerp_enabled)
{
for (i = 0; i < (int)ft->neighbors.size (); i++) // for this face and each of its neighbors
{
ft2 = g_facetriangulations[ft->neighbors[i]];
for (j = 0; j < (int)ft2->localtriangulations.size (); j++) // for each patch on that face
{
lt = ft2->localtriangulations[j];
if (!CalcAdaptedSpot (lt, position, surface, spot))
{
if (g_drawlerp && ft2 == ft)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 6.\n");
}
continue;
}
if (!CalcWeight (lt, spot, &weight))
{
continue;
}
interp = new interpolation_t;
#ifdef HLRAD_BILINEARINTERPOLATION
interp->points.reserve (4);
#else
interp->points.reserve (3);
#endif
CalcInterpolation (lt, spot, interp);
localweights.push_back (weight);
localinterps.push_back (interp);
}
}
}
// Combine into one interpolation
maininterp->isbiased = false;
maininterp->totalweight = 0;
maininterp->points.resize (0);
for (i = 0; i < (int)localinterps.size (); i++)
{
if (localinterps[i]->isbiased)
{
maininterp->isbiased = true;
}
for (j = 0; j < (int)localinterps[i]->points.size (); j++)
{
weight = localinterps[i]->points[j].weight * localweights[i];
if (g_patches[localinterps[i]->points[j].patchnum].flags == ePatchFlagOutside)
{
weight *= 0.01;
}
n = (int)maininterp->points.size ();
maininterp->points.resize (n + 1);
maininterp->points[n].patchnum = localinterps[i]->points[j].patchnum;
maininterp->points[n].weight = weight;
maininterp->totalweight += weight;
}
}
if (maininterp->totalweight > 0)
{
ApplyInterpolation (maininterp, numstyles, styles, outs
#ifdef ZHLT_XASH
, outs_direction
#endif
);
if (g_drawlerp)
{
for (j = 0; j < numstyles; j++)
{
// white or yellow
outs[j][0] = 100;
outs[j][1] = 100;
outs[j][2] = (maininterp->isbiased? 0: 100);
}
}
}
else
{
// try again, don't multiply localweights[i] (which equals to 0)
maininterp->isbiased = false;
maininterp->totalweight = 0;
maininterp->points.resize (0);
for (i = 0; i < (int)localinterps.size (); i++)
{
if (localinterps[i]->isbiased)
{
maininterp->isbiased = true;
}
for (j = 0; j < (int)localinterps[i]->points.size (); j++)
{
weight = localinterps[i]->points[j].weight;
if (g_patches[localinterps[i]->points[j].patchnum].flags == ePatchFlagOutside)
{
weight *= 0.01;
}
n = (int)maininterp->points.size ();
maininterp->points.resize (n + 1);
maininterp->points[n].patchnum = localinterps[i]->points[j].patchnum;
maininterp->points[n].weight = weight;
maininterp->totalweight += weight;
}
}
if (maininterp->totalweight > 0)
{
ApplyInterpolation (maininterp, numstyles, styles, outs
#ifdef ZHLT_XASH
, outs_direction
#endif
);
if (g_drawlerp)
{
for (j = 0; j < numstyles; j++)
{
// red
outs[j][0] = 100;
outs[j][1] = 0;
outs[j][2] = (maininterp->isbiased? 0: 100);
}
}
}
else
{
// worst case, simply use the nearest patch
best = NULL;
for (i = 0; i < (int)ft->localtriangulations.size (); i++)
{
lt = ft->localtriangulations[i];
VectorCopy (position, v);
snap_to_winding (lt->winding, lt->plane, v);
VectorSubtract (v, position, v);
dist = VectorLength (v);
if (best == NULL || dist < bestdist - ON_EPSILON)
{
best = lt;
bestdist = dist;
}
}
if (best)
{
lt = best;
VectorSubtract (position, lt->center, spot);
dot = DotProduct (spot, lt->normal);
VectorMA (spot, -dot, lt->normal, spot);
CalcInterpolation (lt, spot, maininterp);
maininterp->totalweight = 0;
for (j = 0; j < (int)maininterp->points.size (); j++)
{
if (g_patches[maininterp->points[j].patchnum].flags == ePatchFlagOutside)
{
maininterp->points[j].weight *= 0.01;
}
maininterp->totalweight += maininterp->points[j].weight;
}
ApplyInterpolation (maininterp, numstyles, styles, outs
#ifdef ZHLT_XASH
, outs_direction
#endif
);
if (g_drawlerp)
{
for (j = 0; j < numstyles; j++)
{
// green
outs[j][0] = 0;
outs[j][1] = 100;
outs[j][2] = (maininterp->isbiased? 0: 100);
}
}
}
else
{
maininterp->isbiased = true;
maininterp->totalweight = 0;
maininterp->points.resize (0);
ApplyInterpolation (maininterp, numstyles, styles, outs
#ifdef ZHLT_XASH
, outs_direction
#endif
);
if (g_drawlerp)
{
for (j = 0; j < numstyles; j++)
{
// black
outs[j][0] = 0;
outs[j][1] = 0;
outs[j][2] = 0;
}
}
}
}
}
delete maininterp;
for (i = 0; i < (int)localinterps.size (); i++)
{
delete localinterps[i];
}
}
catch (std::bad_alloc)
{
hlassume (false, assume_NoMemory);
}
}
static bool TestLineSegmentIntersectWall (const facetriangulation_t *facetrian, const vec3_t p1, const vec3_t p2)
{
int i;
const facetriangulation_t::Wall *wall;
vec_t front;
vec_t back;
vec_t dot1;
vec_t dot2;
vec_t dot;
vec_t bottom;
vec_t top;
vec_t frac;
for (i = 0; i < (int)facetrian->walls.size (); i++)
{
wall = &facetrian->walls[i];
bottom = DotProduct (wall->points[0], wall->direction);
top = DotProduct (wall->points[1], wall->direction);
front = DotProduct (p1, wall->normal) - DotProduct (wall->points[0], wall->normal);
back = DotProduct (p2, wall->normal) - DotProduct (wall->points[0], wall->normal);
if (front > ON_EPSILON && back > ON_EPSILON || front < -ON_EPSILON && back < -ON_EPSILON)
{
continue;
}
dot1 = DotProduct (p1, wall->direction);
dot2 = DotProduct (p2, wall->direction);
if (fabs (front) <= 2 * ON_EPSILON && fabs (back) <= 2 * ON_EPSILON)
{
top = qmin (top, qmax (dot1, dot2));
bottom = qmax (bottom, qmin (dot1, dot2));
}
else
{
frac = front / (front - back);
frac = qmax (0, qmin (frac, 1));
dot = dot1 + frac * (dot2 - dot1);
top = qmin (top, dot);
bottom = qmax (bottom, dot);
}
if (top - bottom >= -ON_EPSILON)
{
return true;
}
}
return false;
}
#ifdef HLRAD_FARPATCH_FIX
static bool TestFarPatch (const localtriangulation_t *lt, const vec3_t p2, const Winding &p2winding)
{
int i;
vec3_t v;
vec_t dist;
vec_t size1;
vec_t size2;
size1 = 0;
for (i = 0; i < lt->winding.m_NumPoints; i++)
{
VectorSubtract (lt->winding.m_Points[i], lt->center, v);
dist = VectorLength (v);
if (dist > size1)
{
size1 = dist;
}
}
size2 = 0;
for (i = 0; i < p2winding.m_NumPoints; i++)
{
VectorSubtract (p2winding.m_Points[i], p2, v);
dist = VectorLength (v);
if (dist > size2)
{
size2 = dist;
}
}
VectorSubtract (p2, lt->center, v);
dist = VectorLength (v);
return dist > 1.4 * (size1 + size2);
}
#endif
#define TRIANGLE_SHAPE_THRESHOLD (115.0*Q_PI/180)
// If one of the angles in a triangle exceeds this threshold, the most distant point will be removed or the triangle will break into a convex-type wedge.
static void GatherPatches (localtriangulation_t *lt, const facetriangulation_t *facetrian)
{
int i;
int facenum2;
const dplane_t *dp2;
const patch_t *patch2;
int patchnum2;
vec3_t v;
localtriangulation_t::Wedge point;
std::vector< localtriangulation_t::Wedge > points;
std::vector< std::pair< vec_t, int > > angles;
vec_t angle;
if (!g_lerp_enabled)
{
lt->sortedwedges.resize (0);
return;
}
points.resize (0);
for (i = 0; i < (int)lt->neighborfaces.size (); i++)
{
facenum2 = lt->neighborfaces[i];
dp2 = getPlaneFromFaceNumber (facenum2);
for (patch2 = g_face_patches[facenum2]; patch2; patch2 = patch2->next)
{
patchnum2 = patch2 - g_patches;
point.leftpatchnum = patchnum2;
VectorMA (patch2->origin, -PATCH_HUNT_OFFSET, dp2->normal, v);
// Do permission tests using the original position of the patch
if (patchnum2 == lt->patchnum || point_in_winding (lt->winding, lt->plane, v))
{
continue;
}
if (facenum2 != facetrian->facenum && TestLineSegmentIntersectWall (facetrian, lt->center, v))
{
continue;
}
#ifdef HLRAD_FARPATCH_FIX
if (TestFarPatch (lt, v, *patch2->winding))
{
continue;
}
#endif
// Store the adapted position of the patch
if (!CalcAdaptedSpot (lt, v, facenum2, point.leftspot))
{
continue;
}
if (GetDirection (point.leftspot, lt->normal, point.leftdirection) <= 2 * ON_EPSILON)
{
continue;
}
points.push_back (point);
}
}
// Sort the patches into clockwise order
angles.resize ((int)points.size ());
for (i = 0; i < (int)points.size (); i++)
{
angle = GetAngle (points[0].leftdirection, points[i].leftdirection, lt->normal);
if (i == 0)
{
if (g_drawlerp && fabs (angle) > NORMAL_EPSILON)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 7.\n");
}
angle = 0.0;
}
angles[i].first = GetAngleDiff (angle, 0);
angles[i].second = i;
}
std::sort (angles.begin (), angles.end ());
lt->sortedwedges.resize ((int)points.size ());
for (i = 0; i < (int)points.size (); i++)
{
lt->sortedwedges[i] = points[angles[i].second];
}
}
static void PurgePatches (localtriangulation_t *lt)
{
std::vector< localtriangulation_t::Wedge > points;
int i;
int cur;
std::vector< int > next;
std::vector< int > prev;
std::vector< int > valid;
std::vector< std::pair< vec_t, int > > dists;
vec_t angle;
vec3_t normal;
vec3_t v;
points.swap (lt->sortedwedges);
lt->sortedwedges.resize (0);
next.resize ((int)points.size ());
prev.resize ((int)points.size ());
valid.resize ((int)points.size ());
dists.resize ((int)points.size ());
for (i = 0; i < (int)points.size (); i++)
{
next[i] = (i + 1) % (int)points.size ();
prev[i] = (i - 1 + (int)points.size ()) % (int)points.size ();
valid[i] = 1;
dists[i].first = DotProduct (points[i].leftspot, points[i].leftdirection);
dists[i].second = i;
}
std::sort (dists.begin (), dists.end ());
for (i = 0; i < (int)points.size (); i++)
{
cur = dists[i].second;
if (valid[cur] == 0)
{
continue;
}
valid[cur] = 2; // mark current patch as final
CrossProduct (points[cur].leftdirection, lt->normal, normal);
VectorNormalize (normal);
VectorScale (normal, cos (TRIANGLE_SHAPE_THRESHOLD), v);
VectorMA (v, sin (TRIANGLE_SHAPE_THRESHOLD), points[cur].leftdirection, v);
while (next[cur] != cur && valid[next[cur]] != 2)
{
angle = GetAngle (points[cur].leftdirection, points[next[cur]].leftdirection, lt->normal);
if (fabs (angle) <= (1.0*Q_PI/180) ||
GetAngleDiff (angle, 0) <= Q_PI + NORMAL_EPSILON
&& DotProduct (points[next[cur]].leftspot, v) >= DotProduct (points[cur].leftspot, v) - ON_EPSILON / 2)
{
// remove next patch
valid[next[cur]] = 0;
next[cur] = next[next[cur]];
prev[next[cur]] = cur;
continue;
}
// the triangle is good
break;
}
CrossProduct (lt->normal, points[cur].leftdirection, normal);
VectorNormalize (normal);
VectorScale (normal, cos (TRIANGLE_SHAPE_THRESHOLD), v);
VectorMA (v, sin (TRIANGLE_SHAPE_THRESHOLD), points[cur].leftdirection, v);
while (prev[cur] != cur && valid[prev[cur]] != 2)
{
angle = GetAngle (points[prev[cur]].leftdirection, points[cur].leftdirection, lt->normal);
if (fabs (angle) <= (1.0*Q_PI/180) ||
GetAngleDiff (angle, 0) <= Q_PI + NORMAL_EPSILON
&& DotProduct (points[prev[cur]].leftspot, v) >= DotProduct (points[cur].leftspot, v) - ON_EPSILON / 2)
{
// remove previous patch
valid[prev[cur]] = 0;
prev[cur] = prev[prev[cur]];
next[prev[cur]] = cur;
continue;
}
// the triangle is good
break;
}
}
for (i = 0; i < (int)points.size (); i++)
{
if (valid[i] == 2)
{
lt->sortedwedges.push_back (points[i]);
}
}
}
static void PlaceHullPoints (localtriangulation_t *lt)
{
int i;
int j;
int n;
vec3_t v;
vec_t dot;
vec_t angle;
localtriangulation_t::HullPoint hp;
std::vector< localtriangulation_t::HullPoint > spots;
std::vector< std::pair< vec_t, int > > angles;
const localtriangulation_t::Wedge *w;
const localtriangulation_t::Wedge *wnext;
std::vector< localtriangulation_t::HullPoint > arc_spots;
std::vector< vec_t > arc_angles;
std::vector< int > next;
std::vector< int > prev;
vec_t frac;
vec_t len;
vec_t dist;
spots.reserve (lt->winding.m_NumPoints);
spots.resize (0);
for (i = 0; i < (int)lt->winding.m_NumPoints; i++)
{
VectorSubtract (lt->winding.m_Points[i], lt->center, v);
dot = DotProduct (v, lt->normal);
VectorMA (v, -dot, lt->normal, hp.spot);
if (!GetDirection (hp.spot, lt->normal, hp.direction))
{
continue;
}
spots.push_back (hp);
}
if ((int)lt->sortedwedges.size () == 0)
{
angles.resize ((int)spots.size ());
for (i = 0; i < (int)spots.size (); i++)
{
angle = GetAngle (spots[0].direction, spots[i].direction, lt->normal);
if (i == 0)
{
angle = 0.0;
}
angles[i].first = GetAngleDiff (angle, 0);
angles[i].second = i;
}
std::sort (angles.begin (), angles.end ());
lt->sortedhullpoints.resize (0);
for (i = 0; i < (int)spots.size (); i++)
{
if (g_drawlerp && angles[i].second != i)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 8.\n");
}
lt->sortedhullpoints.push_back (spots[angles[i].second]);
}
return;
}
lt->sortedhullpoints.resize (0);
for (i = 0; i < (int)lt->sortedwedges.size (); i++)
{
w = &lt->sortedwedges[i];
wnext = &lt->sortedwedges[(i + 1) % (int)lt->sortedwedges.size ()];
angles.resize ((int)spots.size ());
for (j = 0; j < (int)spots.size (); j++)
{
angle = GetAngle (w->leftdirection, spots[j].direction, lt->normal);
angles[j].first = GetAngleDiff (angle, 0);
angles[j].second = j;
}
std::sort (angles.begin (), angles.end ());
angle = GetAngle (w->leftdirection, wnext->leftdirection, lt->normal);
if ((int)lt->sortedwedges.size () == 1)
{
angle = 2 * Q_PI;
}
else
{
angle = GetAngleDiff (angle, 0);
}
arc_spots.resize ((int)spots.size () + 2);
arc_angles.resize ((int)spots.size () + 2);
next.resize ((int)spots.size () + 2);
prev.resize ((int)spots.size () + 2);
VectorCopy (w->leftspot, arc_spots[0].spot);
VectorCopy (w->leftdirection, arc_spots[0].direction);
arc_angles[0] = 0;
next[0] = 1;
prev[0] = -1;
n = 1;
for (j = 0; j < (int)spots.size (); j++)
{
if (NORMAL_EPSILON <= angles[j].first && angles[j].first <= angle - NORMAL_EPSILON)
{
arc_spots[n] = spots[angles[j].second];
arc_angles[n] = angles[j].first;
next[n] = n + 1;
prev[n] = n - 1;
n++;
}
}
VectorCopy (wnext->leftspot, arc_spots[n].spot);
VectorCopy (wnext->leftdirection, arc_spots[n].direction);
arc_angles[n] = angle;
next[n] = -1;
prev[n] = n - 1;
n++;
for (j = 1; next[j] != -1; j = next[j])
{
while (prev[j] != -1)
{
if (arc_angles[next[j]] - arc_angles[prev[j]] <= Q_PI + NORMAL_EPSILON)
{
frac = GetFrac (arc_spots[prev[j]].spot, arc_spots[next[j]].spot, arc_spots[j].direction, lt->normal);
len = (1 - frac) * DotProduct (arc_spots[prev[j]].spot, arc_spots[j].direction)
+ frac * DotProduct (arc_spots[next[j]].spot, arc_spots[j].direction);
dist = DotProduct (arc_spots[j].spot, arc_spots[j].direction);
if (dist <= len + NORMAL_EPSILON)
{
j = prev[j];
next[j] = next[next[j]];
prev[next[j]] = j;
continue;
}
}
break;
}
}
for (j = 0; next[j] != -1; j = next[j])
{
lt->sortedhullpoints.push_back (arc_spots[j]);
}
}
}
#ifdef HLRAD_BILINEARINTERPOLATION
static bool TryMakeSquare (localtriangulation_t *lt, int i)
{
localtriangulation_t::Wedge *w1;
localtriangulation_t::Wedge *w2;
localtriangulation_t::Wedge *w3;
vec3_t v;
vec3_t dir1;
vec3_t dir2;
vec_t angle;
w1 = &lt->sortedwedges[i];
w2 = &lt->sortedwedges[(i + 1) % (int)lt->sortedwedges.size ()];
w3 = &lt->sortedwedges[(i + 2) % (int)lt->sortedwedges.size ()];
// (o, p1, p2) and (o, p2, p3) must be triangles and not in a square
if (w1->shape != localtriangulation_t::Wedge::eTriangular || w2->shape != localtriangulation_t::Wedge::eTriangular)
{
return false;
}
// (o, p1, p3) must be a triangle
angle = GetAngle (w1->leftdirection, w3->leftdirection, lt->normal);
angle = GetAngleDiff (angle, 0);
if (angle >= TRIANGLE_SHAPE_THRESHOLD)
{
return false;
}
// (p2, p1, p3) must be a triangle
VectorSubtract (w1->leftspot, w2->leftspot, v);
if (!GetDirection (v, lt->normal, dir1))
{
return false;
}
VectorSubtract (w3->leftspot, w2->leftspot, v);
if (!GetDirection (v, lt->normal, dir2))
{
return false;
}
angle = GetAngle (dir2, dir1, lt->normal);
angle = GetAngleDiff (angle, 0);
if (angle >= TRIANGLE_SHAPE_THRESHOLD)
{
return false;
}
w1->shape = localtriangulation_t::Wedge::eSquareLeft;
VectorSubtract (vec3_origin, dir1, w1->wedgenormal);
w2->shape = localtriangulation_t::Wedge::eSquareRight;
VectorCopy (dir2, w2->wedgenormal);
return true;
}
static void FindSquares (localtriangulation_t *lt)
{
int i;
localtriangulation_t::Wedge *w;
std::vector< std::pair< vec_t, int > > dists;
if ((int)lt->sortedwedges.size () <= 2)
{
return;
}
dists.resize ((int)lt->sortedwedges.size ());
for (i = 0; i < (int)lt->sortedwedges.size (); i++)
{
w = &lt->sortedwedges[i];
dists[i].first = DotProduct (w->leftspot, w->leftdirection);
dists[i].second = i;
}
std::sort (dists.begin (), dists.end ());
for (i = 0; i < (int)lt->sortedwedges.size (); i++)
{
TryMakeSquare (lt, dists[i].second);
TryMakeSquare (lt, (dists[i].second - 2 + (int)lt->sortedwedges.size ()) % (int)lt->sortedwedges.size ());
}
}
#endif
static localtriangulation_t *CreateLocalTriangulation (const facetriangulation_t *facetrian, int patchnum)
{
localtriangulation_t *lt;
int i;
const patch_t *patch;
vec_t dot;
int facenum;
localtriangulation_t::Wedge *w;
localtriangulation_t::Wedge *wnext;
vec_t angle;
vec_t total;
vec3_t v;
vec3_t normal;
facenum = facetrian->facenum;
patch = &g_patches[patchnum];
lt = new localtriangulation_t;
// Fill basic information for this local triangulation
lt->plane = *getPlaneFromFaceNumber (facenum);
lt->plane.dist += DotProduct (g_face_offset[facenum], lt->plane.normal);
lt->winding = *patch->winding;
VectorMA (patch->origin, -PATCH_HUNT_OFFSET, lt->plane.normal, lt->center);
dot = DotProduct (lt->center, lt->plane.normal) - lt->plane.dist;
VectorMA (lt->center, -dot, lt->plane.normal, lt->center);
if (!point_in_winding_noedge (lt->winding, lt->plane, lt->center, DEFAULT_EDGE_WIDTH))
{
snap_to_winding_noedge (lt->winding, lt->plane, lt->center, DEFAULT_EDGE_WIDTH, 4 * DEFAULT_EDGE_WIDTH);
}
VectorCopy (lt->plane.normal, lt->normal);
lt->patchnum = patchnum;
lt->neighborfaces = facetrian->neighbors;
// Gather all patches from nearby faces
GatherPatches (lt, facetrian);
// Remove distant patches
PurgePatches (lt);
// Calculate wedge types
total = 0.0;
for (i = 0; i < (int)lt->sortedwedges.size (); i++)
{
w = &lt->sortedwedges[i];
wnext = &lt->sortedwedges[(i + 1) % (int)lt->sortedwedges.size ()];
angle = GetAngle (w->leftdirection, wnext->leftdirection, lt->normal);
if (g_drawlerp && ((int)lt->sortedwedges.size () >= 2 && fabs (angle) <= (0.9*Q_PI/180)))
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 9.\n");
}
angle = GetAngleDiff (angle, 0);
if ((int)lt->sortedwedges.size () == 1)
{
angle = 2 * Q_PI;
}
total += angle;
if (angle <= Q_PI + NORMAL_EPSILON)
{
if (angle < TRIANGLE_SHAPE_THRESHOLD)
{
w->shape = localtriangulation_t::Wedge::eTriangular;
VectorClear (w->wedgenormal);
}
else
{
w->shape = localtriangulation_t::Wedge::eConvex;
VectorSubtract (wnext->leftspot, w->leftspot, v);
GetDirection (v, lt->normal, w->wedgenormal);
}
}
else
{
w->shape = localtriangulation_t::Wedge::eConcave;
VectorAdd (wnext->leftdirection, w->leftdirection, v);
CrossProduct (lt->normal, v, normal);
VectorSubtract (wnext->leftdirection, w->leftdirection, v);
VectorAdd (normal, v, normal);
GetDirection (normal, lt->normal, w->wedgenormal);
if (g_drawlerp && VectorLength (w->wedgenormal) == 0)
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 10.\n");
}
}
}
if (g_drawlerp && ((int)lt->sortedwedges.size () > 0 && fabs (total - 2 * Q_PI) > 10 * NORMAL_EPSILON))
{
Developer (DEVELOPER_LEVEL_SPAM, "Debug: triangulation: internal error 11.\n");
}
#ifdef HLRAD_BILINEARINTERPOLATION
FindSquares (lt);
#endif
// Calculate hull points
PlaceHullPoints (lt);
return lt;
}
static void FreeLocalTriangulation (localtriangulation_t *lt)
{
delete lt;
}
static void FindNeighbors (facetriangulation_t *facetrian)
{
int i;
int j;
int e;
const edgeshare_t *es;
int side;
const facelist_t *fl;
int facenum;
int facenum2;
const dface_t *f;
const dface_t *f2;
const dplane_t *dp;
const dplane_t *dp2;
facenum = facetrian->facenum;
f = &g_dfaces[facenum];
dp = getPlaneFromFace (f);
facetrian->neighbors.resize (0);
facetrian->neighbors.push_back (facenum);
for (i = 0; i < f->numedges; i++)
{
e = g_dsurfedges[f->firstedge + i];
es = &g_edgeshare[abs (e)];
if (!es->smooth)
{
continue;
}
f2 = es->faces[e > 0? 1: 0];
facenum2 = f2 - g_dfaces;
dp2 = getPlaneFromFace (f2);
if (DotProduct (dp->normal, dp2->normal) < -NORMAL_EPSILON)
{
continue;
}
for (j = 0; j < (int)facetrian->neighbors.size (); j++)
{
if (facetrian->neighbors[j] == facenum2)
{
break;
}
}
if (j == (int)facetrian->neighbors.size ())
{
facetrian->neighbors.push_back (facenum2);
}
}
for (i = 0; i < f->numedges; i++)
{
e = g_dsurfedges[f->firstedge + i];
es = &g_edgeshare[abs (e)];
if (!es->smooth)
{
continue;
}
for (side = 0; side < 2; side++)
{
for (fl = es->vertex_facelist[side]; fl; fl = fl->next)
{
f2 = fl->face;
facenum2 = f2 - g_dfaces;
dp2 = getPlaneFromFace (f2);
if (DotProduct (dp->normal, dp2->normal) < -NORMAL_EPSILON)
{
continue;
}
for (j = 0; j < (int)facetrian->neighbors.size (); j++)
{
if (facetrian->neighbors[j] == facenum2)
{
break;
}
}
if (j == (int)facetrian->neighbors.size ())
{
facetrian->neighbors.push_back (facenum2);
}
}
}
}
}
static void BuildWalls (facetriangulation_t *facetrian)
{
int i;
int j;
int facenum;
int facenum2;
const dface_t *f;
const dface_t *f2;
const dplane_t *dp;
const dplane_t *dp2;
int e;
const edgeshare_t *es;
vec_t dot;
facenum = facetrian->facenum;
f = &g_dfaces[facenum];
dp = getPlaneFromFace (f);
facetrian->walls.resize (0);
for (i = 0; i < (int)facetrian->neighbors.size (); i++)
{
facenum2 = facetrian->neighbors[i];
f2 = &g_dfaces[facenum2];
dp2 = getPlaneFromFace (f2);
if (DotProduct (dp->normal, dp2->normal) <= 0.1)
{
continue;
}
for (j = 0; j < f2->numedges; j++)
{
e = g_dsurfedges[f2->firstedge + j];
es = &g_edgeshare[abs (e)];
if (!es->smooth)
{
facetriangulation_t::Wall wall;
VectorAdd (g_dvertexes[g_dedges[abs(e)].v[0]].point, g_face_offset[facenum], wall.points[0]);
VectorAdd (g_dvertexes[g_dedges[abs(e)].v[1]].point, g_face_offset[facenum], wall.points[1]);
VectorSubtract (wall.points[1], wall.points[0], wall.direction);
dot = DotProduct (wall.direction, dp->normal);
VectorMA (wall.direction, -dot, dp->normal, wall.direction);
if (VectorNormalize (wall.direction))
{
CrossProduct (wall.direction, dp->normal, wall.normal);
VectorNormalize (wall.normal);
facetrian->walls.push_back (wall);
}
}
}
}
}
static void CollectUsedPatches (facetriangulation_t *facetrian)
{
int i;
int j;
int k;
int patchnum;
const localtriangulation_t *lt;
const localtriangulation_t::Wedge *w;
facetrian->usedpatches.resize (0);
for (i = 0; i < (int)facetrian->localtriangulations.size (); i++)
{
lt = facetrian->localtriangulations[i];
patchnum = lt->patchnum;
for (k = 0; k < (int)facetrian->usedpatches.size (); k++)
{
if (facetrian->usedpatches[k] == patchnum)
{
break;
}
}
if (k == (int)facetrian->usedpatches.size ())
{
facetrian->usedpatches.push_back (patchnum);
}
for (j = 0; j < (int)lt->sortedwedges.size (); j++)
{
w = &lt->sortedwedges[j];
patchnum = w->leftpatchnum;
for (k = 0; k < (int)facetrian->usedpatches.size (); k++)
{
if (facetrian->usedpatches[k] == patchnum)
{
break;
}
}
if (k == (int)facetrian->usedpatches.size ())
{
facetrian->usedpatches.push_back (patchnum);
}
}
}
}
// =====================================================================================
// CreateTriangulations
// =====================================================================================
void CreateTriangulations (int facenum)
{
try
{
facetriangulation_t *facetrian;
int patchnum;
const patch_t *patch;
localtriangulation_t *lt;
g_facetriangulations[facenum] = new facetriangulation_t;
facetrian = g_facetriangulations[facenum];
facetrian->facenum = facenum;
// Find neighbors
FindNeighbors (facetrian);
// Build walls
BuildWalls (facetrian);
// Create local triangulation around each patch
facetrian->localtriangulations.resize (0);
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
{
patchnum = patch - g_patches;
lt = CreateLocalTriangulation (facetrian, patchnum);
facetrian->localtriangulations.push_back (lt);
}
// Collect used patches
CollectUsedPatches (facetrian);
}
catch (std::bad_alloc)
{
hlassume (false, assume_NoMemory);
}
}
// =====================================================================================
// GetTriangulationPatches
// =====================================================================================
void GetTriangulationPatches (int facenum, int *numpatches, const int **patches)
{
const facetriangulation_t *facetrian;
facetrian = g_facetriangulations[facenum];
*numpatches = (int)facetrian->usedpatches.size ();
*patches = facetrian->usedpatches.data ();
}
// =====================================================================================
// FreeTriangulations
// =====================================================================================
void FreeTriangulations ()
{
try
{
int i;
int j;
facetriangulation_t *facetrian;
for (i = 0; i < g_numfaces; i++)
{
facetrian = g_facetriangulations[i];
for (j = 0; j < (int)facetrian->localtriangulations.size (); j++)
{
FreeLocalTriangulation (facetrian->localtriangulations[j]);
}
delete facetrian;
g_facetriangulations[i] = NULL;
}
}
catch (std::bad_alloc)
{
hlassume (false, assume_NoMemory);
}
}
#else
#ifdef HLRAD_LERP_VL
static bool LerpTriangle(const lerpTriangulation_t* trian, const vec3_t point, vec3_t result,
#ifdef ZHLT_XASH
vec3_t &result_direction,
#endif
int pt1, int pt2, int pt3, int style);
static bool LerpEdge(const lerpTriangulation_t* trian, const vec3_t point, vec3_t result,
#ifdef ZHLT_XASH
vec3_t &result_direction,
#endif
int pt1, int pt2, int style);
static bool LerpNearest(const lerpTriangulation_t* trian, const vec3_t point, vec3_t result,
#ifdef ZHLT_XASH
vec3_t &result_direction,
#endif
int pt1, int style);
#define LERP_EPSILON 0.5
#endif
#ifndef HLRAD_LERP_VL
// =====================================================================================
// TestWallIntersectTri
// Returns true if wall polygon intersects patch polygon
// =====================================================================================
static bool TestWallIntersectTri(const lerpTriangulation_t* const trian, const vec3_t p1, const vec3_t p2, const vec3_t p3)
{
#ifdef HLRAD_LERP_VL
int x;
const lerpWall_t* wall;
const vec_t* normal = trian->plane->normal;
{
vec3_t d1, d2, n;
VectorSubtract (p3, p2, d1);
VectorSubtract (p1, p2, d2);
CrossProduct (d1, d2, n);
if (DotProduct (n, normal) < 0)
{
const vec_t* tmp;
tmp = p2;
p2 = p3;
p3 = tmp;
}
}
for (x = 0, wall = trian->walls; x < trian->numwalls; x++, wall++)
{
if (point_in_tri (wall->vertex0, trian->plane, p1, p2, p3))
{
return true;
}
if (point_in_tri (wall->vertex1, trian->plane, p1, p2, p3))
{
return true;
}
}
return false;
#else
int x;
const lerpWall_t* wall = trian->walls;
dplane_t plane;
plane_from_points(p1, p2, p3, &plane);
// Try first 'vertical' side
// Since we test each of the 3 segments from patch against wall, only one side of wall needs testing inside
// patch (since they either dont intersect at all at this point, or both line segments intersect inside)
for (x = 0; x < trian->numwalls; x++, wall++)
{
vec3_t point;
// Try side A
if (intersect_linesegment_plane(&plane, wall->vertex[0], wall->vertex[3], point))
{
if (point_in_tri(point, &plane, p1, p2, p3))
{
#if 0
Verbose
("Wall side A point @ (%4.3f %4.3f %4.3f) inside patch (%4.3f %4.3f %4.3f) (%4.3f %4.3f %4.3f) (%4.3f %4.3f %4.3f)\n",
point[0], point[1], point[2], p1[0], p1[1], p1[2], p2[0], p2[1], p2[2], p3[0], p3[1], p3[2]);
#endif
return true;
}
}
}
return false;
#endif
}
#endif
// =====================================================================================
// TestLineSegmentIntersectWall
// Returns true if line would hit the 'wall' (to fix light streaking)
// =====================================================================================
static bool TestLineSegmentIntersectWall(const lerpTriangulation_t* const trian, const vec3_t p1, const vec3_t p2
#ifdef HLRAD_LERP_VL
, vec_t epsilon = ON_EPSILON
#endif
)
{
int x;
const lerpWall_t* wall = trian->walls;
for (x = 0; x < trian->numwalls; x++, wall++)
{
#ifdef HLRAD_LERP_VL
vec_t front, back, frac;
vec3_t mid;
front = DotProduct (p1, wall->plane.normal) - wall->plane.dist;
back = DotProduct (p2, wall->plane.normal) - wall->plane.dist;
if (fabs (front) <= epsilon && fabs (back) <= epsilon)
{
if (DotProduct (p1, wall->increment) < DotProduct (wall->vertex0, wall->increment) - epsilon &&
DotProduct (p2, wall->increment) < DotProduct (wall->vertex0, wall->increment) - epsilon )
{
continue;
}
if (DotProduct (p1, wall->increment) > DotProduct (wall->vertex1, wall->increment) + epsilon &&
DotProduct (p2, wall->increment) > DotProduct (wall->vertex1, wall->increment) + epsilon )
{
continue;
}
return true;
}
if (front > 0.9 * epsilon && back > 0.9 * epsilon || front < -0.9 * epsilon && back < -0.9 * epsilon)
{
continue;
}
frac = front / (front - back);
frac = qmax (0, qmin (frac, 1));
mid[0] = p1[0] + (p2[0] - p1[0]) * frac;
mid[1] = p1[1] + (p2[1] - p1[1]) * frac;
mid[2] = p1[2] + (p2[2] - p1[2]) * frac;
if (DotProduct (mid, wall->increment) < DotProduct (wall->vertex0, wall->increment) - epsilon ||
DotProduct (mid, wall->increment) > DotProduct (wall->vertex1, wall->increment) + epsilon )
{
continue;
}
return true;
#else
vec3_t point;
if (intersect_linesegment_plane(&wall->plane, p1, p2, point))
{
if (point_in_wall(wall, point))
{
#if 0
Verbose
("Tested point @ (%4.3f %4.3f %4.3f) blocks segment from (%4.3f %4.3f %4.3f) to (%4.3f %4.3f %4.3f) intersects wall\n",
point[0], point[1], point[2], p1[0], p1[1], p1[2], p2[0], p2[1], p2[2]);
#endif
return true;
}
}
#endif
}
return false;
}
#ifndef HLRAD_LERP_VL
// =====================================================================================
// TestTriIntersectWall
// Returns true if line would hit the 'wall' (to fix light streaking)
// =====================================================================================
static bool TestTriIntersectWall(const lerpTriangulation_t* trian, const vec3_t p1, const vec3_t p2,
const vec3_t p3)
{
if (TestLineSegmentIntersectWall(trian, p1, p2) || TestLineSegmentIntersectWall(trian, p1, p3)
|| TestLineSegmentIntersectWall(trian, p2, p3))
{
return true;
}
return false;
}
#endif
// =====================================================================================
// LerpTriangle
// pt1 must be closest point
// =====================================================================================
#ifdef HLRAD_LERP_VL
static bool LerpTriangle(const lerpTriangulation_t* trian, const vec3_t point, vec3_t result,
#ifdef ZHLT_XASH
vec3_t &result_direction,
#endif
int pt1, int pt2, int pt3, int style)
{
patch_t *p1;
patch_t *p2;
patch_t *p3;
const vec_t *o1;
const vec_t *o2;
const vec_t *o3;
vec3_t v;
p1 = trian->points[pt1];
p2 = trian->points[pt2];
p3 = trian->points[pt3];
#ifdef HLRAD_LERP_TEXNORMAL
o1 = trian->points_pos[pt1];
o2 = trian->points_pos[pt2];
o3 = trian->points_pos[pt3];
#else
o1 = p1->origin;
o2 = p2->origin;
o3 = p3->origin;
#endif
dplane_t edge12, edge13, edge23;
VectorSubtract (o2, o1, v);
CrossProduct (trian->plane->normal, v, edge12.normal);
VectorNormalize (edge12.normal);
edge12.dist = DotProduct (o1, edge12.normal);
VectorSubtract (o3, o1, v);
CrossProduct (trian->plane->normal, v, edge13.normal);
VectorNormalize (edge13.normal);
edge13.dist = DotProduct (o1, edge13.normal);
VectorSubtract (o3, o2, v);
CrossProduct (trian->plane->normal, v, edge23.normal);
VectorNormalize (edge23.normal);
edge23.dist = DotProduct (o2, edge23.normal);
vec_t dist12 = DotProduct (point, edge12.normal) - edge12.dist;
vec_t dist13 = DotProduct (point, edge13.normal) - edge13.dist;
vec_t dist23 = DotProduct (point, edge23.normal) - edge23.dist;
// the point could be on the edge.
if (fabs (dist12) < LERP_EPSILON)
{
if (LerpEdge (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt2, style))
{
return true;
}
}
if (fabs (dist13) < LERP_EPSILON)
{
if (LerpEdge (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt3, style))
{
return true;
}
}
if (fabs (dist23) < LERP_EPSILON)
{
if (LerpEdge (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt2, pt3, style))
{
return true;
}
}
// now that the point is not on the edge, we should check the shape of the triangle and make sure that the point is precisely inside the triangle.
vec_t maxdist12 = DotProduct (o3, edge12.normal) - edge12.dist;
vec_t maxdist13 = DotProduct (o2, edge13.normal) - edge13.dist;
vec_t maxdist23 = DotProduct (o1, edge23.normal) - edge23.dist;
if (fabs (maxdist12) <= LERP_EPSILON || fabs (maxdist13) <= LERP_EPSILON || fabs (maxdist23) <= LERP_EPSILON)
{
return false;
}
if (dist12 / maxdist12 < 0 || dist12 / maxdist12 > 1 ||
dist13 / maxdist13 < 0 || dist13 / maxdist13 > 1 ||
dist23 / maxdist23 < 0 || dist23 / maxdist23 > 1 )
{
return false;
}
vec3_t testpoint;
VectorScale (p1->origin, 1 - dist13 / maxdist13 - dist12 / maxdist12, testpoint);
VectorMA (testpoint, dist13 / maxdist13, p2->origin, testpoint);
VectorMA (testpoint, dist12 / maxdist12, p3->origin, testpoint);
if (TestLineSegmentIntersectWall (trian, testpoint, p1->origin) ||
TestLineSegmentIntersectWall (trian, testpoint, p2->origin) ||
TestLineSegmentIntersectWall (trian, testpoint, p3->origin) ||
TestLineSegmentIntersectWall (trian, p2->origin, p1->origin) ||
TestLineSegmentIntersectWall (trian, p3->origin, p2->origin) ||
TestLineSegmentIntersectWall (trian, p1->origin, p3->origin) )
{
return false;
}
const vec3_t *l1, *l2, *l3;
#ifdef ZHLT_XASH
const vec3_t *l1_d, *l2_d, *l3_d;
#endif
l1 = GetTotalLight(p1, style
#ifdef ZHLT_XASH
, l1_d
#endif
);
l2 = GetTotalLight(p2, style
#ifdef ZHLT_XASH
, l2_d
#endif
);
l3 = GetTotalLight(p3, style
#ifdef ZHLT_XASH
, l3_d
#endif
);
VectorScale (*l1, 1 - dist13 / maxdist13 - dist12 / maxdist12, result);
VectorMA (result, dist13 / maxdist13, *l2, result);
VectorMA (result, dist12 / maxdist12, *l3, result);
#ifdef ZHLT_XASH
VectorScale (*l1_d, 1 - dist13 / maxdist13 - dist12 / maxdist12, result_direction);
VectorMA (result_direction, dist13 / maxdist13, *l2_d, result_direction);
VectorMA (result_direction, dist12 / maxdist12, *l3_d, result_direction);
#endif
return true;
}
#else
#ifdef ZHLT_TEXLIGHT
static void LerpTriangle(const lerpTriangulation_t* const trian, const vec3_t point, vec3_t result, const unsigned pt1, const unsigned pt2, const unsigned pt3, int style) //LRC
#else
static void LerpTriangle(const lerpTriangulation_t* const trian, const vec3_t point, vec3_t result, const unsigned pt1, const unsigned pt2, const unsigned pt3)
#endif
{
patch_t* p1;
patch_t* p2;
patch_t* p3;
vec3_t base;
vec3_t d1;
vec3_t d2;
vec_t x;
vec_t y;
vec_t y1;
vec_t x2;
vec3_t v;
dplane_t ep1;
dplane_t ep2;
p1 = trian->points[pt1];
p2 = trian->points[pt2];
p3 = trian->points[pt3];
#ifdef ZHLT_TEXLIGHT
VectorCopy(*GetTotalLight(p1, style), base); //LRC
VectorSubtract(*GetTotalLight(p2, style), base, d1); //LRC
VectorSubtract(*GetTotalLight(p3, style), base, d2); //LRC
#else
VectorCopy(p1->totallight, base);
VectorSubtract(p2->totallight, base, d1);
VectorSubtract(p3->totallight, base, d2);
#endif
// Get edge normals
VectorSubtract(p1->origin, p2->origin, v);
VectorNormalize(v);
CrossProduct(v, trian->plane->normal, ep1.normal);
ep1.dist = DotProduct(p1->origin, ep1.normal);
VectorSubtract(p1->origin, p3->origin, v);
VectorNormalize(v);
CrossProduct(v, trian->plane->normal, ep2.normal);
ep2.dist = DotProduct(p1->origin, ep2.normal);
x = DotProduct(point, ep1.normal) - ep1.dist;
y = DotProduct(point, ep2.normal) - ep2.dist;
y1 = DotProduct(p2->origin, ep2.normal) - ep2.dist;
x2 = DotProduct(p3->origin, ep1.normal) - ep1.dist;
VectorCopy(base, result);
if (fabs(x2) >= ON_EPSILON)
{
int i;
for (i = 0; i < 3; i++)
{
result[i] += x * d2[i] / x2;
}
}
if (fabs(y1) >= ON_EPSILON)
{
int i;
for (i = 0; i < 3; i++)
{
result[i] += y * d1[i] / y1;
}
}
}
#endif
// =====================================================================================
// LerpNearest
// =====================================================================================
#ifdef HLRAD_LERP_VL
static bool LerpNearest(const lerpTriangulation_t* trian, const vec3_t point, vec3_t result,
#ifdef ZHLT_XASH
vec3_t &result_direction,
#endif
int pt1, int style)
{
patch_t *patch;
patch = trian->points[pt1];
const vec3_t *l;
#ifdef ZHLT_XASH
const vec3_t *l_d;
#endif
l = GetTotalLight(patch, style
#ifdef ZHLT_XASH
, l_d
#endif
);
VectorCopy (*l, result);
#ifdef ZHLT_XASH
VectorCopy (*l_d, result_direction);
#endif
return true;
}
#else
#ifdef ZHLT_TEXLIGHT
static void LerpNearest(const lerpTriangulation_t* const trian, vec3_t result, int style) //LRC
#else
static void LerpNearest(const lerpTriangulation_t* const trian, vec3_t result)
#endif
{
unsigned x;
unsigned numpoints = trian->numpoints;
patch_t* patch;
// Find nearest in original face
for (x = 0; x < numpoints; x++)
{
patch = trian->points[trian->dists[x].patch];
if (patch->faceNumber == trian->facenum)
{
#ifdef ZHLT_TEXLIGHT
VectorCopy(*GetTotalLight(patch, style), result); //LRC
#else
VectorCopy(patch->totallight, result);
#endif
return;
}
}
// If none in nearest face, settle for nearest
if (numpoints)
{
#ifdef ZHLT_TEXLIGHT
VectorCopy(*GetTotalLight(trian->points[trian->dists[0].patch], style), result); //LRC
#else
VectorCopy(trian->points[trian->dists[0].patch]->totallight, result);
#endif
}
else
{
VectorClear(result);
}
}
#endif
// =====================================================================================
// LerpEdge
// =====================================================================================
#ifdef HLRAD_LERP_VL
static bool LerpEdge(const lerpTriangulation_t* trian, const vec3_t point, vec3_t result,
#ifdef ZHLT_XASH
vec3_t &result_direction,
#endif
int pt1, int pt2, int style)
{
patch_t *p1;
patch_t *p2;
const vec_t *o1;
const vec_t *o2;
vec3_t increment;
vec3_t normal;
vec_t x;
vec_t x1;
vec3_t base;
vec3_t d;
#ifdef ZHLT_XASH
vec3_t base_direction;
vec3_t d_direction;
#endif
p1 = trian->points[pt1];
p2 = trian->points[pt2];
#ifdef HLRAD_LERP_TEXNORMAL
o1 = trian->points_pos[pt1];
o2 = trian->points_pos[pt2];
#else
o1 = p1->origin;
o2 = p2->origin;
#endif
if (TestLineSegmentIntersectWall(trian, p1->origin, p2->origin))
return false;
VectorSubtract (o2, o1, increment);
CrossProduct (trian->plane->normal, increment, normal);
CrossProduct (normal, trian->plane->normal, increment);
VectorNormalize (increment);
x = DotProduct (o2, increment) - DotProduct (o1, increment);
x1 = DotProduct (point, increment) - DotProduct (o1, increment);
if (x1 < -LERP_EPSILON || x1 > x + LERP_EPSILON)
{
return false;
}
const vec3_t *l;
#ifdef ZHLT_XASH
const vec3_t *l_d;
#endif
l = GetTotalLight(p1, style
#ifdef ZHLT_XASH
, l_d
#endif
);
VectorCopy (*l, base);
#ifdef ZHLT_XASH
VectorCopy (*l_d, base_direction);
#endif
l = GetTotalLight(p2, style
#ifdef ZHLT_XASH
, l_d
#endif
);
VectorSubtract (*l, base, d);
VectorCopy (base, result);
#ifdef ZHLT_XASH
VectorSubtract (*l_d, base_direction, d_direction);
VectorCopy (base_direction, result_direction);
#endif
if (x > LERP_EPSILON)
{
vec_t frac = x1 / x;
if (frac < 0) frac = 0;
if (frac > 1) frac = 1;
VectorMA (result, frac, d, result);
#ifdef ZHLT_XASH
VectorMA (result_direction, frac, d_direction, result_direction);
#endif
}
return true;
}
#else
#ifdef ZHLT_TEXLIGHT
static bool LerpEdge(const lerpTriangulation_t* const trian, const vec3_t point, vec3_t result, int style) //LRC
#else
static bool LerpEdge(const lerpTriangulation_t* const trian, const vec3_t point, vec3_t result)
#endif
{
patch_t* p1;
patch_t* p2;
#ifndef HLRAD_LERP_VL
patch_t* p3;
#endif
vec3_t v1;
vec3_t v2;
vec_t d;
#ifdef HLRAD_LERP_VL
p1 = trian->points[pt1];
p2 = trian->points[pt2];
#else
p1 = trian->points[trian->dists[0].patch];
p2 = trian->points[trian->dists[1].patch];
p3 = trian->points[trian->dists[2].patch];
#endif
#ifndef HLRAD_LERP_FIX
VectorSubtract(point, p1->origin, v2);
VectorNormalize(v2);
#endif
// Try nearest and 2
if (!TestLineSegmentIntersectWall(trian, p1->origin, p2->origin))
{
#ifdef HLRAD_LERP_FIX
vec_t total_length, length1, length2;
VectorSubtract (p2->origin, p1->origin, v1);
CrossProduct (trian->plane->normal, v1, v2);
CrossProduct (v2, trian->plane->normal, v1);
length1 = DotProduct (v1, point) - DotProduct (v1, p1->origin);
length2 = DotProduct (v1, p2->origin) - DotProduct (v1, point);
total_length = DotProduct (v1, p2->origin) - DotProduct (v1, p1->origin);
if (total_length > 0 && length1 >= 0 && length2 >= 0)
{
int i;
#else
VectorSubtract(p2->origin, p1->origin, v1);
VectorNormalize(v1);
d = DotProduct(v2, v1);
if (d >= ON_EPSILON)
{
int i;
vec_t length1;
vec_t length2;
vec3_t segment;
vec_t total_length;
VectorSubtract(point, p1->origin, segment);
length1 = VectorLength(segment);
VectorSubtract(point, p2->origin, segment);
length2 = VectorLength(segment);
total_length = length1 + length2;
#endif
for (i = 0; i < 3; i++)
{
#ifdef ZHLT_TEXLIGHT
#ifdef HLRAD_LERP_FIX
result[i] = (((*GetTotalLight(p1, style))[i] * length2) + ((*GetTotalLight(p2, style))[i] * length1)) / total_length; //LRC
#else
result[i] = (((*GetTotalLight(p1, style))[i] * length2) + ((*GetTotalLight(p1, style))[i] * length1)) / total_length; //LRC
#endif
#else
result[i] = ((p1->totallight[i] * length2) + (p2->totallight[i] * length1)) / total_length;
#endif
}
return true;
}
}
#ifndef HLRAD_LERP_VL
// Try nearest and 3
if (!TestLineSegmentIntersectWall(trian, p1->origin, p3->origin))
{
#ifdef HLRAD_LERP_FIX
vec_t total_length, length1, length2;
VectorSubtract (p3->origin, p1->origin, v1);
CrossProduct (trian->plane->normal, v1, v2);
CrossProduct (v2, trian->plane->normal, v1);
length1 = DotProduct (v1, point) - DotProduct (v1, p1->origin);
length2 = DotProduct (v1, p3->origin) - DotProduct (v1, point);
total_length = DotProduct (v1, p3->origin) - DotProduct (v1, p1->origin);
if (total_length > 0 && length1 >= 0 && length2 >= 0)
{
int i;
#else
VectorSubtract(p3->origin, p1->origin, v1);
VectorNormalize(v1);
d = DotProduct(v2, v1);
if (d >= ON_EPSILON)
{
int i;
vec_t length1;
vec_t length2;
vec3_t segment;
vec_t total_length;
VectorSubtract(point, p1->origin, segment);
length1 = VectorLength(segment);
VectorSubtract(point, p3->origin, segment);
length2 = VectorLength(segment);
total_length = length1 + length2;
#endif
for (i = 0; i < 3; i++)
{
#ifdef ZHLT_TEXLIGHT
result[i] = (((*GetTotalLight(p1, style))[i] * length2) + ((*GetTotalLight(p3, style))[i] * length1)) / total_length; //LRC
#else
result[i] = ((p1->totallight[i] * length2) + (p3->totallight[i] * length1)) / total_length;
#endif
}
return true;
}
}
#endif
return false;
}
#endif
// =====================================================================================
//
// SampleTriangulation
//
// =====================================================================================
// =====================================================================================
// dist_sorter
// =====================================================================================
static int CDECL dist_sorter(const void* p1, const void* p2)
{
lerpDist_t* dist1 = (lerpDist_t*) p1;
lerpDist_t* dist2 = (lerpDist_t*) p2;
#ifdef HLRAD_LERP_VL
if (dist1->invalid < dist2->invalid)
return -1;
if (dist2->invalid < dist1->invalid)
return 1;
if (dist1->dist + ON_EPSILON < dist2->dist)
return -1;
if (dist2->dist + ON_EPSILON < dist1->dist)
return 1;
if (dist1->pos[0] + ON_EPSILON < dist2->pos[0])
return -1;
if (dist2->pos[0] + ON_EPSILON < dist1->pos[0])
return 1;
if (dist1->pos[1] + ON_EPSILON < dist2->pos[1])
return -1;
if (dist2->pos[1] + ON_EPSILON < dist1->pos[1])
return 1;
if (dist1->pos[2] + ON_EPSILON < dist2->pos[2])
return -1;
if (dist2->pos[2] + ON_EPSILON < dist1->pos[2])
return 1;
return 0;
#else
if (dist1->dist < dist2->dist)
{
return -1;
}
else if (dist1->dist > dist2->dist)
{
return 1;
}
else
{
return 0;
}
#endif
}
// =====================================================================================
// FindDists
// =====================================================================================
static void FindDists(const lerpTriangulation_t* const trian, const vec3_t point)
{
unsigned x;
unsigned numpoints = trian->numpoints;
patch_t** patch = trian->points;
lerpDist_t* dists = trian->dists;
vec3_t delta;
#ifdef HLRAD_LERP_VL
vec3_t testpoint;
{
VectorCopy (point, testpoint);
Winding *w = new Winding (*trian->face);
int i;
for (i = 0; i < w->m_NumPoints; i++)
{
VectorAdd (w->m_Points[i], g_face_offset[trian->facenum], w->m_Points[i]);
}
if (!point_in_winding_noedge (*w, *trian->plane, testpoint, DEFAULT_EDGE_WIDTH))
{
snap_to_winding_noedge (*w, *trian->plane, testpoint, DEFAULT_EDGE_WIDTH, 4 * DEFAULT_EDGE_WIDTH);
}
delete w;
if (!TestLineSegmentIntersectWall (trian, point, testpoint, 2 * ON_EPSILON))
{
VectorCopy (point, testpoint);
}
}
#endif
for (x = 0; x < numpoints; x++, patch++, dists++)
{
#ifdef HLRAD_LERP_TEXNORMAL
VectorSubtract (trian->points_pos[x], point, delta);
#else
VectorSubtract((*patch)->origin, point, delta);
#endif
#ifdef HLRAD_LERP_VL
vec3_t normal;
CrossProduct (trian->plane->normal, delta, normal);
CrossProduct (normal, trian->plane->normal, delta);
#endif
dists->dist = VectorLength(delta);
dists->patch = x;
#ifdef HLRAD_LERP_VL
dists->invalid = TestLineSegmentIntersectWall (trian, testpoint, (*patch)->origin);
VectorCopy ((*patch)->origin, dists->pos);
#endif
}
qsort((void*)trian->dists, (size_t) numpoints, sizeof(lerpDist_t), dist_sorter);
}
// =====================================================================================
// SampleTriangulation
// =====================================================================================
#ifdef ZHLT_TEXLIGHT
#ifdef HLRAD_LERP_VL
void SampleTriangulation(const lerpTriangulation_t* const trian, const vec3_t point, vec3_t result,
#ifdef ZHLT_XASH
vec3_t &result_direction,
#endif
int style)
#else
void SampleTriangulation(const lerpTriangulation_t* const trian, vec3_t point, vec3_t result, int style) //LRC
#endif
#else
void SampleTriangulation(const lerpTriangulation_t* const trian, vec3_t point, vec3_t result)
#endif
{
FindDists(trian, point);
#ifdef HLRAD_LERP_VL
VectorClear(result);
#ifdef ZHLT_XASH
VectorClear (result_direction);
#endif
if (trian->numpoints >= 3 && trian->dists[2].invalid <= 0 && g_lerp_enabled)
{
int pt1 = trian->dists[0].patch;
int pt2 = trian->dists[1].patch;
int pt3 = trian->dists[2].patch;
if (LerpTriangle (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt2, pt3, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 100, result[1] = 100, result[2] = 100;
#endif
return;
}
#ifdef HLRAD_LERP_TRY5POINTS
if (trian->numpoints >= 4 && trian->dists[3].invalid <= 0)
{
int pt4 = trian->dists[3].patch;
if (LerpTriangle (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt2, pt4, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 100, result[1] = 100, result[2] = 0;
return;
#endif
}
if (LerpTriangle (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt3, pt4, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 100, result[1] = 0, result[2] = 100;
#endif
return;
}
if (trian->numpoints >= 5 && trian->dists[4].invalid <= 0)
{
int pt5 = trian->dists[4].patch;
if (LerpTriangle (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt2, pt5, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 100, result[1] = 0, result[2] = 0;
#endif
return;
}
if (LerpTriangle (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt3, pt5, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 100, result[1] = 0, result[2] = 0;
#endif
return;
}
if (LerpTriangle (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt4, pt5, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 100, result[1] = 0, result[2] = 0;
#endif
return;
}
}
}
#endif
}
if (trian->numpoints >= 2 && trian->dists[1].invalid <= 0 && g_lerp_enabled)
{
int pt1 = trian->dists[0].patch;
int pt2 = trian->dists[1].patch;
if (LerpEdge (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt2, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 0, result[1] = 100, result[2] = 100;
#endif
return;
}
if (trian->numpoints >= 3 && trian->dists[2].invalid <= 0 )
{
int pt3 = trian->dists[2].patch;
if (LerpEdge (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, pt3, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 0, result[1] = 100, result[2] = 0;
#endif
return;
}
}
}
if (trian->numpoints >= 1)
{
int pt1 = trian->dists[0].patch;
if (LerpNearest (trian, point, result,
#ifdef ZHLT_XASH
result_direction,
#endif
pt1, style))
{
#ifdef HLRAD_DEBUG_DRAWPOINTS
if (g_drawlerp)
result[0] = 0, result[1] = 0, result[2] = 100;
#endif
return;
}
}
#else
if ((trian->numpoints > 3) && (g_lerp_enabled))
{
unsigned pt1;
unsigned pt2;
unsigned pt3;
vec_t* p1;
vec_t* p2;
vec_t* p3;
dplane_t plane;
pt1 = trian->dists[0].patch;
pt2 = trian->dists[1].patch;
pt3 = trian->dists[2].patch;
p1 = trian->points[pt1]->origin;
p2 = trian->points[pt2]->origin;
p3 = trian->points[pt3]->origin;
plane_from_points(p1, p2, p3, &plane);
SnapToPlane(&plane, point, 0.0);
if (point_in_tri(point, &plane, p1, p2, p3))
{ // TODO check edges/tri for blocking by wall
if (!TestWallIntersectTri(trian, p1, p2, p3) && !TestTriIntersectWall(trian, p1, p2, p3))
{
#ifdef ZHLT_TEXLIGHT
LerpTriangle(trian, point, result, pt1, pt2, pt3, style); //LRC
#else
LerpTriangle(trian, point, result, pt1, pt2, pt3);
#endif
return;
}
}
else
{
#ifdef ZHLT_TEXLIGHT
if (LerpEdge(trian, point, result, style)) //LRC
#else
if (LerpEdge(trian, point, result))
#endif
{
return;
}
}
}
#ifdef ZHLT_TEXLIGHT
LerpNearest(trian, result, style); //LRC
#else
LerpNearest(trian, result);
#endif
#endif
}
// =====================================================================================
// AddPatchToTriangulation
// =====================================================================================
static void AddPatchToTriangulation(lerpTriangulation_t* trian, patch_t* patch)
{
#ifdef HLRAD_PATCHBLACK_FIX
if (patch->flags != ePatchFlagOutside)
#else
if (!(patch->flags & ePatchFlagOutside))
#endif
{
int pnum = trian->numpoints;
if (pnum >= trian->maxpoints)
{
trian->points = (patch_t**)realloc(trian->points, sizeof(patch_t*) * (trian->maxpoints + DEFAULT_MAX_LERP_POINTS));
hlassume(trian->points != NULL, assume_NoMemory);
memset(trian->points + trian->maxpoints, 0, sizeof(patch_t*) * DEFAULT_MAX_LERP_POINTS); // clear the new block
#ifdef HLRAD_LERP_TEXNORMAL
trian->points_pos = (vec3_t *)realloc(trian->points_pos, sizeof (vec3_t) * (trian->maxpoints + DEFAULT_MAX_LERP_POINTS));
hlassume (trian->points_pos != NULL, assume_NoMemory);
memset (trian->points_pos + trian->maxpoints, 0, sizeof (vec3_t) * DEFAULT_MAX_LERP_POINTS);
#endif
trian->maxpoints += DEFAULT_MAX_LERP_POINTS;
}
trian->points[pnum] = patch;
#ifdef HLRAD_LERP_TEXNORMAL
VectorCopy (patch->origin, trian->points_pos[pnum]);
if (patch->faceNumber != trian->facenum)
{
vec3_t snapdir;
dplane_t p1 = *trian->plane;
p1.dist += DotProduct (p1.normal, g_face_offset[trian->facenum]);
dplane_t p2 = *getPlaneFromFaceNumber (patch->faceNumber);
p2.dist += DotProduct (p2.normal, g_face_offset[patch->faceNumber]);
#ifdef HLRAD_GROWSAMPLE
// we have abandoned the texnormal approach, since the lighting should not be affected by the texnormal: it should only rely on the s,t coordinate of vertices, which doesn't include texnormal information
VectorAdd (p1.normal, p2.normal, snapdir);
if (!VectorNormalize (snapdir)) // normal2 = -normal1
{
// skip this patch
return;
}
#else
VectorCopy (p1.normal, snapdir);
if (!GetIntertexnormal (patch->faceNumber, trian->facenum, snapdir))
{
Warning ("AddPatchToTriangulation: internal error 1.");
}
#endif
VectorMA (trian->points_pos[pnum], -PATCH_HUNT_OFFSET, p2.normal, trian->points_pos[pnum]);
vec_t dist = (DotProduct (trian->points_pos[pnum], p1.normal) - p1.dist) / DotProduct (snapdir, p1.normal);
VectorMA (trian->points_pos[pnum], -dist, snapdir, trian->points_pos[pnum]);
}
#endif
trian->numpoints++;
}
}
// =====================================================================================
// CreateWalls
// =====================================================================================
#ifdef HLRAD_LERP_VL
static void AddWall (lerpTriangulation_t *trian, const vec_t *v1, const vec_t *v2) // v1 & v2 should be without offset
{
int facenum = trian->facenum;
const dplane_t* p = trian->plane;
vec3_t delta;
vec3_t p0;
vec3_t p1;
vec3_t normal;
lerpWall_t *wall;
if (trian->numwalls >= trian->maxwalls)
{
trian->walls =
(lerpWall_t*)realloc(trian->walls, sizeof(lerpWall_t) * (trian->maxwalls + DEFAULT_MAX_LERP_WALLS));
hlassume(trian->walls != NULL, assume_NoMemory);
memset(trian->walls + trian->maxwalls, 0, sizeof(lerpWall_t) * DEFAULT_MAX_LERP_WALLS); // clear the new block
trian->maxwalls += DEFAULT_MAX_LERP_WALLS;
}
wall = &trian->walls[trian->numwalls];
trian->numwalls++;
VectorAdd (v1, g_face_offset[facenum], p0);
VectorAdd (v2, g_face_offset[facenum], p1);
VectorSubtract (p1, p0, delta);
CrossProduct (p->normal, delta, normal);
if (VectorNormalize (normal) == 0.0)
{
trian->numwalls--;
return;
}
VectorCopy (normal, wall->plane.normal);
wall->plane.dist = DotProduct (normal, p0);
CrossProduct (normal, p->normal, wall->increment);
VectorCopy (p0, wall->vertex0);
VectorCopy (p1, wall->vertex1);
}
#endif
#ifndef HLRAD_LERP_FACELIST
static void CreateWalls(lerpTriangulation_t* trian, const dface_t* const face)
{
#ifdef HLRAD_LERP_VL
const dplane_t* p = getPlaneFromFace(face);
int facenum = face - g_dfaces;
int x;
for (x = 0; x < face->numedges; x++)
{
edgeshare_t* es;
dface_t* f2;
int edgenum = g_dsurfedges[face->firstedge + x];
if (edgenum > 0)
{
es = &g_edgeshare[edgenum];
f2 = es->faces[1];
}
else
{
es = &g_edgeshare[-edgenum];
f2 = es->faces[0];
}
if (!es->smooth)
{
AddWall (trian, g_dvertexes[g_dedges[abs(edgenum)].v[0]].point, g_dvertexes[g_dedges[abs(edgenum)].v[1]].point);
}
else
{
int facenum2 = f2 - g_dfaces;
int x2;
for (x2 = 0; x2 < f2->numedges; x2++)
{
edgeshare_t *es2;
dface_t *f3;
int edgenum2 = g_dsurfedges[f2->firstedge + x2];
if (edgenum2 > 0)
{
es2 = &g_edgeshare[edgenum2];
f3 = es2->faces[1];
}
else
{
es2 = &g_edgeshare[-edgenum2];
f3 = es2->faces[0];
}
if (!es2->smooth)
{
AddWall (trian, g_dvertexes[g_dedges[abs(edgenum2)].v[0]].point, g_dvertexes[g_dedges[abs(edgenum2)].v[1]].point);
}
}
}
}
#else
const dplane_t* p = getPlaneFromFace(face);
int facenum = face - g_dfaces;
int x;
for (x = 0; x < face->numedges; x++)
{
edgeshare_t* es;
dface_t* f2;
int edgenum = g_dsurfedges[face->firstedge + x];
if (edgenum > 0)
{
es = &g_edgeshare[edgenum];
f2 = es->faces[1];
}
else
{
es = &g_edgeshare[-edgenum];
f2 = es->faces[0];
}
// Build Wall for non-coplanar neighbhors
#ifdef HLRAD_GetPhongNormal_VL
if (f2 && !es->smooth)
#else
if (f2 && !es->coplanar && VectorCompare(vec3_origin, es->interface_normal))
#endif
{
const dplane_t* plane = getPlaneFromFace(f2);
// if plane isn't facing us, ignore it
#ifdef HLRAD_DPLANEOFFSET_MISCFIX
if (DotProduct(plane->normal, g_face_centroids[facenum]) < plane->dist + DotProduct(plane->normal, g_face_offset[facenum]))
#else
if (DotProduct(plane->normal, g_face_centroids[facenum]) < plane->dist)
#endif
{
continue;
}
{
vec3_t delta;
vec3_t p0;
vec3_t p1;
lerpWall_t* wall;
if (trian->numwalls >= trian->maxwalls)
{
trian->walls =
(lerpWall_t*)realloc(trian->walls, sizeof(lerpWall_t) * (trian->maxwalls + DEFAULT_MAX_LERP_WALLS));
hlassume(trian->walls != NULL, assume_NoMemory);
memset(trian->walls + trian->maxwalls, 0, sizeof(lerpWall_t) * DEFAULT_MAX_LERP_WALLS); // clear the new block
trian->maxwalls += DEFAULT_MAX_LERP_WALLS;
}
wall = &trian->walls[trian->numwalls];
trian->numwalls++;
VectorScale(p->normal, 10000.0, delta);
VectorCopy(g_dvertexes[g_dedges[abs(edgenum)].v[0]].point, p0);
VectorCopy(g_dvertexes[g_dedges[abs(edgenum)].v[1]].point, p1);
// Adjust for origin-based models
// technically we should use the other faces g_face_offset entries
// If they are nonzero, it has to be from the same model with
// the same offset, so we are cool
VectorAdd(p0, g_face_offset[facenum], p0);
VectorAdd(p1, g_face_offset[facenum], p1);
VectorAdd(p0, delta, wall->vertex[0]);
VectorAdd(p1, delta, wall->vertex[1]);
VectorSubtract(p1, delta, wall->vertex[2]);
VectorSubtract(p0, delta, wall->vertex[3]);
{
vec3_t delta1;
vec3_t delta2;
vec3_t normal;
vec_t dist;
VectorSubtract(wall->vertex[2], wall->vertex[1], delta1);
VectorSubtract(wall->vertex[0], wall->vertex[1], delta2);
CrossProduct(delta1, delta2, normal);
VectorNormalize(normal);
dist = DotProduct(normal, p0);
VectorCopy(normal, wall->plane.normal);
wall->plane.dist = dist;
}
}
}
}
#endif
}
#endif
// =====================================================================================
// AllocTriangulation
// =====================================================================================
static lerpTriangulation_t* AllocTriangulation()
{
lerpTriangulation_t* trian = (lerpTriangulation_t*)calloc(1, sizeof(lerpTriangulation_t));
#ifdef HLRAD_HLASSUMENOMEMORY
hlassume (trian != NULL, assume_NoMemory);
#endif
trian->maxpoints = DEFAULT_MAX_LERP_POINTS;
trian->maxwalls = DEFAULT_MAX_LERP_WALLS;
trian->points = (patch_t**)calloc(DEFAULT_MAX_LERP_POINTS, sizeof(patch_t*));
#ifdef HLRAD_LERP_TEXNORMAL
trian->points_pos = (vec3_t *)calloc (DEFAULT_MAX_LERP_POINTS, sizeof(vec3_t));
hlassume (trian->points_pos != NULL, assume_NoMemory);
#endif
trian->walls = (lerpWall_t*)calloc(DEFAULT_MAX_LERP_WALLS, sizeof(lerpWall_t));
hlassume(trian->points != NULL, assume_NoMemory);
hlassume(trian->walls != NULL, assume_NoMemory);
return trian;
}
// =====================================================================================
// FreeTriangulation
// =====================================================================================
void FreeTriangulation(lerpTriangulation_t* trian)
{
free(trian->dists);
free(trian->points);
#ifdef HLRAD_LERP_TEXNORMAL
free(trian->points_pos);
#endif
free(trian->walls);
#ifdef HLRAD_LERP_FACELIST
for (facelist_t *next; trian->allfaces; trian->allfaces = next)
{
next = trian->allfaces->next;
free (trian->allfaces);
}
#endif
free(trian);
}
#ifdef HLRAD_LERP_FACELIST
void AddFaceToTrian (facelist_t **faces, dface_t *face)
{
for (; *faces; faces = &(*faces)->next)
{
if ((*faces)->face == face)
{
return;
}
}
*faces = (facelist_t *)malloc (sizeof (facelist_t));
hlassume (*faces != NULL, assume_NoMemory);
(*faces)->face = face;
(*faces)->next = NULL;
}
void FindFaces (lerpTriangulation_t *trian)
{
int i, j;
AddFaceToTrian (&trian->allfaces, &g_dfaces[trian->facenum]);
for (j = 0; j < trian->face->numedges; j++)
{
int edgenum = g_dsurfedges[trian->face->firstedge + j];
edgeshare_t *es = &g_edgeshare[abs (edgenum)];
dface_t *f2;
if (!es->smooth)
{
continue;
}
if (edgenum > 0)
{
f2 = es->faces[1];
}
else
{
f2 = es->faces[0];
}
AddFaceToTrian (&trian->allfaces, f2);
}
for (j = 0; j < trian->face->numedges; j++)
{
int edgenum = g_dsurfedges[trian->face->firstedge + j];
edgeshare_t *es = &g_edgeshare[abs (edgenum)];
if (!es->smooth)
{
continue;
}
for (i = 0; i < 2; i++)
{
facelist_t *fl;
for (fl = es->vertex_facelist[i]; fl; fl = fl->next)
{
dface_t *f2 = fl->face;
AddFaceToTrian (&trian->allfaces, f2);
}
}
}
}
#endif
// =====================================================================================
// CreateTriangulation
// =====================================================================================
lerpTriangulation_t* CreateTriangulation(const unsigned int facenum)
{
const dface_t* f = g_dfaces + facenum;
const dplane_t* p = getPlaneFromFace(f);
lerpTriangulation_t* trian = AllocTriangulation();
patch_t* patch;
unsigned int j;
dface_t* f2;
trian->facenum = facenum;
trian->plane = p;
trian->face = f;
#ifdef HLRAD_LERP_FACELIST
FindFaces (trian);
facelist_t *fl;
for (fl = trian->allfaces; fl; fl = fl->next)
{
f2 = fl->face;
int facenum2 = fl->face - g_dfaces;
for (patch = g_face_patches[facenum2]; patch; patch = patch->next)
{
AddPatchToTriangulation (trian, patch);
}
for (j = 0; j < f2->numedges; j++)
{
int edgenum = g_dsurfedges[f2->firstedge + j];
edgeshare_t *es = &g_edgeshare[abs(edgenum)];
if (!es->smooth)
{
AddWall (trian, g_dvertexes[g_dedges[abs(edgenum)].v[0]].point, g_dvertexes[g_dedges[abs(edgenum)].v[1]].point);
}
}
}
#else
for (patch = g_face_patches[facenum]; patch; patch = patch->next)
{
AddPatchToTriangulation(trian, patch);
}
CreateWalls(trian, f);
for (j = 0; j < f->numedges; j++)
{
edgeshare_t* es;
int edgenum = g_dsurfedges[f->firstedge + j];
if (edgenum > 0)
{
es = &g_edgeshare[edgenum];
f2 = es->faces[1];
}
else
{
es = &g_edgeshare[-edgenum];
f2 = es->faces[0];
}
#ifdef HLRAD_GetPhongNormal_VL
if (!es->smooth)
#else
if (!es->coplanar && VectorCompare(vec3_origin, es->interface_normal))
#endif
{
continue;
}
for (patch = g_face_patches[f2 - g_dfaces]; patch; patch = patch->next)
{
AddPatchToTriangulation(trian, patch);
}
}
#endif
trian->dists = (lerpDist_t*)calloc(trian->numpoints, sizeof(lerpDist_t));
#ifdef HLRAD_HULLU
//Get rid off error that seems to happen with some opaque faces (when opaque face have all edges 'out' of map)
if(trian->numpoints != 0) // this line should be removed. --vluzacn
#endif
hlassume(trian->dists != NULL, assume_NoMemory);
return trian;
}
#endif