2020-08-31 18:50:41 +02:00
/***
*
* Copyright ( c ) 1996 - 2002 , Valve LLC . All rights reserved .
*
* This product contains software technology licensed from Id
* Software , Inc . ( " Id Technology " ) . Id Technology ( c ) 1996 Id Software , Inc .
* All Rights Reserved .
*
* * * */
// lerp.c
# include "qrad.h"
# include "utlarray.h"
# define TRIANGLE_SHAPE_THRESHOLD DEG2RAD( 115.0 )
struct interpolation_t
{
struct Point
{
int patchnum ;
vec_t weight ;
} ;
bool isbiased ;
vec_t totalweight ;
CUtlArray < Point > points ;
} ;
// replace std::pair
struct pair_t
{
vec_t first ;
int second ;
} ;
struct localtrian_t
{
struct Wedge
{
enum eShape
{
eTriangular ,
eConvex ,
eConcave ,
eSquareLeft ,
eSquareRight ,
} ;
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_t * winding ;
vec3_t center ; // center is on the plane
vec3_t normal ;
int patchnum ;
CUtlArray < int > neighborfaces ; // including the face itself
CUtlArray < Wedge > sortedwedges ; // in clockwise order (same as Winding)
CUtlArray < 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 ;
CUtlArray < int > neighbors ; // including the face itself
CUtlArray < Wall > walls ;
CUtlArray < localtrian_t * > localtriangulations ;
CUtlArray < int > usedpatches ;
} ;
// replace std::sort
int __cdecl LessThan ( const pair_t * s0 , const pair_t * s1 )
{
if ( s0 - > first > s1 - > first )
return 1 ;
if ( s0 - > first < s1 - > first )
return - 1 ;
if ( s0 - > second > s1 - > second )
return 1 ;
if ( s0 - > second < s1 - > second )
return - 1 ;
return 0 ;
}
facetriangulation_t * g_facetriangulations [ MAX_MAP_FACES ] ;
bool g_drawlerp = false ;
// 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
static bool CalcAdaptedSpot ( const localtrian_t * lt , const vec3_t position , int surface , vec3_t spot )
{
vec3_t surfacespot ;
vec_t dist , dist2 ;
vec3_t phongnormal ;
vec_t dot , frac ;
vec3_t v , middle ;
int i ;
for ( i = 0 ; i < lt - > neighborfaces . Count ( ) ; i + + )
{
if ( lt - > neighborfaces [ i ] = = surface )
break ;
}
if ( i = = lt - > neighborfaces . Count ( ) )
{
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 = bound ( 0 , 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 = angle - base ;
if ( diff < 0 )
diff + = 2 * M_PI ;
return diff ;
}
static vec_t GetFrac ( const vec3_t leftspot , const vec3_t rightspot , const vec3_t direction , const vec3_t normal )
{
vec_t dot1 ;
vec_t dot2 ;
vec_t frac ;
vec3_t v ;
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 )
MsgDev ( D_REPORT , " Debug: triangulation: internal error 1. \n " ) ;
frac = 0.0 ;
}
else if ( dot2 < = NORMAL_EPSILON )
{
if ( g_drawlerp & & dot2 < - ON_EPSILON )
MsgDev ( D_REPORT , " Debug: triangulation: internal error 2. \n " ) ;
frac = 1.0 ;
}
else
{
frac = dot1 / ( dot1 - dot2 ) ;
frac = bound ( 0 , frac , 1 ) ;
}
return frac ;
}
static vec_t GetDirection ( const vec3_t spot , const vec3_t normal , vec3_t direction_out )
{
vec_t dot = DotProduct ( spot , normal ) ;
VectorMA ( spot , - dot , normal , direction_out ) ;
return VectorNormalize ( direction_out ) ;
}
// It returns true when the point is inside the hull region (with boundary), even if weight = 0.
static bool CalcWeight ( const localtrian_t * lt , const vec3_t spot , vec_t * weight_out )
{
vec_t angle , len ;
vec_t frac , dist ;
vec3_t direction ;
bool istoofar ;
CUtlArray < vec_t > angles ;
vec_t ratio ;
const localtrian_t : : HullPoint * hp1 ;
const localtrian_t : : HullPoint * hp2 ;
int i , j ;
if ( GetDirection ( spot , lt - > normal , direction ) < = 2 * ON_EPSILON )
{
* weight_out = 1.0 ;
return true ;
}
if ( lt - > sortedhullpoints . Count ( ) = = 0 )
{
* weight_out = 0.0 ;
return false ;
}
angles . SetCount ( lt - > sortedhullpoints . Count ( ) ) ;
for ( i = 0 ; i < lt - > sortedhullpoints . Count ( ) ; i + + )
{
angle = GetAngle ( lt - > sortedhullpoints [ i ] . direction , direction , lt - > normal ) ;
angles [ i ] = GetAngleDiff ( angle , 0 ) ;
}
for ( i = 1 , j = 0 ; i < lt - > sortedhullpoints . Count ( ) ; i + + )
{
if ( angles [ i ] < angles [ j ] )
j = i ;
}
hp1 = & lt - > sortedhullpoints [ j ] ;
hp2 = & lt - > sortedhullpoints [ ( j + 1 ) % lt - > sortedhullpoints . Count ( ) ] ;
frac = GetFrac ( hp1 - > spot , hp2 - > spot , direction , lt - > normal ) ;
len = ( 1.0 - 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 = bound ( 0 , ratio , 1 ) ;
}
* weight_out = 1 - ratio ;
return ! istoofar ;
}
static void CalcInterpolation_Square ( const localtrian_t * lt , int i , const vec3_t spot , interpolation_t * interp )
{
vec_t frac , frac_near , frac_far ;
vec3_t normal , normal1 , normal2 ;
vec3_t test , mid_far , mid_near ;
vec_t dot , dot1 , dot2 , ratio ;
vec_t weights [ 4 ] ;
const localtrian_t : : Wedge * w1 ;
const localtrian_t : : Wedge * w2 ;
const localtrian_t : : Wedge * w3 ;
w1 = & lt - > sortedwedges [ i ] ;
w2 = & lt - > sortedwedges [ ( i + 1 ) % lt - > sortedwedges . Count ( ) ] ;
w3 = & lt - > sortedwedges [ ( i + 2 ) % lt - > sortedwedges . Count ( ) ] ;
if ( w1 - > shape ! = localtrian_t : : Wedge : : eSquareLeft | | w2 - > shape ! = localtrian_t : : Wedge : : eSquareRight )
COM_FatalError ( " CalcInterpolation_Square: internal error: not square. \n " ) ;
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 = bound ( 0 , 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.0 - 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.0 - 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.0 - 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 = bound ( 0 , ratio , 1 ) ;
}
VectorScale ( mid_near , 1.0 - ratio , test ) ;
VectorMA ( test , ratio , mid_far , test ) ;
VectorSubtract ( test , spot , test ) ;
if ( g_drawlerp & & VectorLength ( test ) > 4 * ON_EPSILON )
MsgDev ( D_REPORT , " Debug: triangulation: internal error 12. \n " ) ;
weights [ 0 ] + = 0.5 * ( 1.0 - ratio ) * ( 1.0 - frac_near ) ;
weights [ 3 ] + = 0.5 * ( 1.0 - ratio ) * frac_near ;
weights [ 1 ] + = 0.5 * ratio * ( 1.0 - 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 = bound ( 0 , 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.0 - 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.0 - frac ) * dot1 + frac * dot2 ) ;
}
VectorScale ( w3 - > leftspot , 1.0 - 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.0 - 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 = bound ( 0 , ratio , 1 ) ;
}
VectorScale ( mid_near , 1.0 - ratio , test ) ;
VectorMA ( test , ratio , mid_far , test ) ;
VectorSubtract ( test , spot , test ) ;
if ( g_drawlerp & & VectorLength ( test ) > 4 * ON_EPSILON )
MsgDev ( D_REPORT , " 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 . SetCount ( 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 ] ;
}
// The interpolation function is defined over the entire plane, so CalcInterpolation never fails.
static void CalcInterpolation ( const localtrian_t * lt , const vec3_t spot , interpolation_t * interp )
{
vec_t frac , len , dist ;
vec_t dot , dot1 , dot2 ;
vec_t angle , ratio ;
const localtrian_t : : Wedge * w , * wnext ;
vec3_t direction ;
bool istoofar ;
CUtlArray < vec_t > angles ;
int i , j ;
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 . SetCount ( 1 ) ;
interp - > points [ 0 ] . patchnum = lt - > patchnum ;
interp - > points [ 0 ] . weight = 1.0 ;
return ;
}
if ( lt - > sortedwedges . Count ( ) = = 0 ) // this local triangulation only has center patch
{
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 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 . SetCount ( lt - > sortedwedges . Count ( ) ) ;
for ( i = 0 ; i < lt - > sortedwedges . Count ( ) ; i + + )
{
angle = GetAngle ( lt - > sortedwedges [ i ] . leftdirection , direction , lt - > normal ) ;
angles [ i ] = GetAngleDiff ( angle , 0 ) ;
}
for ( i = 1 , j = 0 ; i < lt - > sortedwedges . Count ( ) ; i + + )
{
if ( angles [ i ] < angles [ j ] )
j = i ;
}
w = & lt - > sortedwedges [ j ] ;
wnext = & lt - > sortedwedges [ ( j + 1 ) % lt - > sortedwedges . Count ( ) ] ;
// Different wedge types have different interpolation methods
switch ( w - > shape )
{
case localtrian_t : : Wedge : : eSquareLeft :
case localtrian_t : : Wedge : : eSquareRight :
case localtrian_t : : Wedge : : eTriangular :
// w->wedgenormal is undefined
frac = GetFrac ( w - > leftspot , wnext - > leftspot , direction , lt - > normal ) ;
len = ( 1.0 - 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 = bound ( 0 , ratio , 1 ) ;
}
if ( istoofar )
{
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 2 ) ;
interp - > points [ 0 ] . patchnum = w - > leftpatchnum ;
interp - > points [ 0 ] . weight = 1.0 - frac ;
interp - > points [ 1 ] . patchnum = wnext - > leftpatchnum ;
interp - > points [ 1 ] . weight = frac ;
}
else if ( w - > shape = = localtrian_t : : Wedge : : eSquareLeft )
{
i = w - & lt - > sortedwedges [ 0 ] ;
CalcInterpolation_Square ( lt , i , spot , interp ) ;
}
else if ( w - > shape = = localtrian_t : : Wedge : : eSquareRight )
{
i = w - & lt - > sortedwedges [ 0 ] ;
i = ( i - 1 + lt - > sortedwedges . Count ( ) ) % lt - > sortedwedges . Count ( ) ;
CalcInterpolation_Square ( lt , i , spot , interp ) ;
}
else
{
interp - > isbiased = false ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 3 ) ;
interp - > points [ 0 ] . patchnum = lt - > patchnum ;
interp - > points [ 0 ] . weight = 1.0 - ratio ;
interp - > points [ 1 ] . patchnum = w - > leftpatchnum ;
interp - > points [ 1 ] . weight = ratio * ( 1.0 - frac ) ;
interp - > points [ 2 ] . patchnum = wnext - > leftpatchnum ;
interp - > points [ 2 ] . weight = ratio * frac ;
}
break ;
case localtrian_t : : Wedge : : eConvex :
// w->wedgenormal is the unit vector pointing from w->leftspot to wnext->leftspot
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 ) )
{
MsgDev ( D_REPORT , " Debug: triangulation: internal error 3. \n " ) ;
}
if ( dot1 > = - NORMAL_EPSILON ) // 0 <= dot1 < dot < dot2
{
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 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 . SetCount ( 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 = bound ( 0 , frac , 1 ) ;
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 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 = bound ( 0 , frac , 1 ) ;
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 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 localtrian_t : : Wedge : : eConcave :
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 )
{
MsgDev ( D_REPORT , " Debug: triangulation: internal error 4. \n " ) ;
}
if ( dist < = NORMAL_EPSILON )
{
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 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 . SetCount ( 1 ) ;
interp - > points [ 0 ] . patchnum = w - > leftpatchnum ;
interp - > points [ 0 ] . weight = 1.0 ;
}
else
{
ratio = dist / len ;
ratio = bound ( 0 , ratio , 1 ) ;
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 2 ) ;
interp - > points [ 0 ] . patchnum = lt - > patchnum ;
interp - > points [ 0 ] . weight = 1.0 - 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 )
{
MsgDev ( D_REPORT , " Debug: triangulation: internal error 5. \n " ) ;
}
if ( dist < = NORMAL_EPSILON )
{
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 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 . SetCount ( 1 ) ;
interp - > points [ 0 ] . patchnum = wnext - > leftpatchnum ;
interp - > points [ 0 ] . weight = 1.0 ;
}
else
{
ratio = dist / len ;
ratio = bound ( 0 , ratio , 1 ) ;
interp - > isbiased = true ;
interp - > totalweight = 1.0 ;
interp - > points . SetCount ( 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 :
COM_FatalError ( " CalcInterpolation: internal error: invalid wedge type \n " ) ;
break ;
}
}
static void ApplyInterpolation ( const interpolation_t * interp , int numstyles , const int * styles , vec3_t * outs , vec3_t * outs_dir = NULL )
{
int i , j ;
for ( j = 0 ; j < numstyles ; j + + )
{
if ( outs_dir ! = NULL )
VectorClear ( outs_dir [ j ] ) ;
VectorClear ( outs [ j ] ) ;
}
if ( interp - > totalweight < = 0 )
return ;
for ( i = 0 ; i < interp - > points . Count ( ) ; i + + )
{
vec_t lerp = interp - > points [ i ] . weight / interp - > totalweight ;
for ( j = 0 ; j < numstyles ; j + + )
{
const vec_t * b = GetTotalLight ( & g_patches [ interp - > points [ i ] . patchnum ] , styles [ j ] ) ;
VectorMA ( outs [ j ] , lerp , b , outs [ j ] ) ;
if ( ! outs_dir ) continue ;
const vec_t * d = GetTotalDirection ( & g_patches [ interp - > points [ i ] . patchnum ] , styles [ j ] ) ;
VectorMA ( outs_dir [ j ] , lerp , d , outs_dir [ j ] ) ;
}
}
}
// =====================================================================================
// InterpolateSampleLight
// =====================================================================================
void InterpolateSampleLight ( const vec3_t position , int surface , int numstyles , const int * styles , vec3_t * outs , vec3_t * outs_dir )
{
vec_t dot , bestdist ;
vec_t weight , dist ;
CUtlArray < vec_t > localweights ;
CUtlArray < interpolation_t * > localinterps ;
interpolation_t * maininterp ;
interpolation_t * interp ;
vec3_t v , spot ;
const localtrian_t * best ;
const facetriangulation_t * ft1 ;
const facetriangulation_t * ft2 ;
const localtrian_t * lt ;
int i , j , n ;
if ( surface < 0 | | surface > = g_numfaces )
COM_FatalError ( " InterpolateSampleLight: surface number out of range. \n " ) ;
ft1 = g_facetriangulations [ surface ] ;
maininterp = new interpolation_t ;
maininterp - > points . EnsureCount ( 64 ) ;
// Calculate local interpolations and their weights
localweights . Purge ( ) ;
localinterps . Purge ( ) ;
if ( g_lerp_enabled )
{
for ( i = 0 ; i < ft1 - > neighbors . Count ( ) ; i + + ) // for this face and each of its neighbors
{
ft2 = g_facetriangulations [ ft1 - > neighbors [ i ] ] ;
for ( j = 0 ; j < ft2 - > localtriangulations . Count ( ) ; j + + ) // for each patch on that face
{
lt = ft2 - > localtriangulations [ j ] ;
if ( ! CalcAdaptedSpot ( lt , position , surface , spot ) )
{
if ( g_drawlerp & & ft2 = = ft1 )
{
MsgDev ( D_REPORT , " Debug: triangulation: internal error 6. \n " ) ;
}
continue ;
}
if ( ! CalcWeight ( lt , spot , & weight ) )
continue ;
interp = new interpolation_t ;
interp - > points . EnsureCount ( 4 ) ;
CalcInterpolation ( lt , spot , interp ) ;
localweights . AddToTail ( weight ) ;
localinterps . AddToTail ( interp ) ;
}
}
}
// combine into one interpolation
maininterp - > isbiased = false ;
maininterp - > totalweight = 0 ;
maininterp - > points . Purge ( ) ;
for ( i = 0 ; i < localinterps . Count ( ) ; i + + )
{
if ( localinterps [ i ] - > isbiased )
{
maininterp - > isbiased = true ;
}
for ( j = 0 ; j < localinterps [ i ] - > points . Count ( ) ; j + + )
{
weight = localinterps [ i ] - > points [ j ] . weight * localweights [ i ] ;
if ( FBitSet ( g_patches [ localinterps [ i ] - > points [ j ] . patchnum ] . flags , PATCH_OUTSIDE ) )
{
weight * = 0.01 ;
}
n = maininterp - > points . AddToTail ( ) ;
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 , outs_dir ) ;
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 . Purge ( ) ;
for ( i = 0 ; i < localinterps . Count ( ) ; i + + )
{
if ( localinterps [ i ] - > isbiased )
{
maininterp - > isbiased = true ;
}
for ( j = 0 ; j < localinterps [ i ] - > points . Count ( ) ; j + + )
{
weight = localinterps [ i ] - > points [ j ] . weight ;
if ( FBitSet ( g_patches [ localinterps [ i ] - > points [ j ] . patchnum ] . flags , PATCH_OUTSIDE ) )
{
weight * = 0.01 ;
}
n = maininterp - > points . AddToTail ( ) ;
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 , outs_dir ) ;
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 < ft1 - > localtriangulations . Count ( ) ; i + + )
{
lt = ft1 - > localtriangulations [ i ] ;
VectorCopy ( position , v ) ;
WindingSnapPoint ( lt - > winding , lt - > plane . normal , 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 < maininterp - > points . Count ( ) ; j + + )
{
if ( FBitSet ( g_patches [ maininterp - > points [ j ] . patchnum ] . flags , PATCH_OUTSIDE ) )
{
maininterp - > points [ j ] . weight * = 0.01 ;
}
maininterp - > totalweight + = maininterp - > points [ j ] . weight ;
}
ApplyInterpolation ( maininterp , numstyles , styles , outs , outs_dir ) ;
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 . Purge ( ) ;
ApplyInterpolation ( maininterp , numstyles , styles , outs , outs_dir ) ;
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 < localinterps . Count ( ) ; i + + )
delete localinterps [ i ] ;
}
static bool TestLineSegmentIntersectWall ( const facetriangulation_t * facetrian , const vec3_t p1 , const vec3_t p2 )
{
vec_t frac , top , bottom ;
vec_t dot , dot1 , dot2 ;
vec_t front , back ;
const facetriangulation_t : : Wall * wall ;
for ( int i = 0 ; i < facetrian - > walls . Count ( ) ; 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 = Q_min ( top , Q_max ( dot1 , dot2 ) ) ;
bottom = Q_max ( bottom , Q_min ( dot1 , dot2 ) ) ;
}
else
{
frac = front / ( front - back ) ;
frac = bound ( 0 , frac , 1 ) ;
dot = dot1 + frac * ( dot2 - dot1 ) ;
top = Q_min ( top , dot ) ;
bottom = Q_max ( bottom , dot ) ;
}
if ( top - bottom > = - ON_EPSILON )
return true ;
}
return false ;
}
static bool TestFarPatch ( const localtrian_t * lt , const vec3_t p2 , const winding_t * p2winding )
{
vec_t size1 , size2 ;
vec_t dist ;
vec3_t v ;
int i ;
for ( i = 0 , size1 = 0 ; i < lt - > winding - > numpoints ; i + + )
{
VectorSubtract ( lt - > winding - > p [ i ] , lt - > center , v ) ;
dist = VectorLength ( v ) ;
if ( dist > size1 )
size1 = dist ;
}
for ( i = 0 , size2 = 0 ; i < p2winding - > numpoints ; i + + )
{
VectorSubtract ( p2winding - > p [ 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 ) ;
}
// if one of the angles in a triangle exceeds this threshold, the most distant point will be removed or the triangle will break into convex-type wedge.
static void GatherPatches ( localtrian_t * lt , const facetriangulation_t * facetrian )
{
int patchnum2 ;
int facenum2 ;
const patch_t * patch2 ;
CUtlArray < localtrian_t : : Wedge > points ;
CUtlArray < pair_t > angles ;
localtrian_t : : Wedge point ;
vec_t angle ;
const dplane_t * dp2 ;
vec3_t v ;
int i ;
points . Purge ( ) ;
for ( i = 0 ; i < lt - > neighborfaces . Count ( ) ; i + + )
{
facenum2 = lt - > neighborfaces [ i ] ;
dp2 = GetPlaneFromFace ( facenum2 ) ;
for ( patch2 = g_face_patches [ facenum2 ] ; patch2 ! = NULL ; patch2 = patch2 - > next )
{
patchnum2 = patch2 - g_patches ;
point . leftpatchnum = patchnum2 ;
VectorMA ( patch2 - > origin , - DEFAULT_HUNT_OFFSET , dp2 - > normal , v ) ;
// Do permission tests using the original position of the patch
if ( patchnum2 = = lt - > patchnum | | PointInWindingEpsilon ( lt - > winding , lt - > plane . normal , v ) )
continue ;
if ( facenum2 ! = facetrian - > facenum & & TestLineSegmentIntersectWall ( facetrian , lt - > center , v ) )
continue ;
if ( TestFarPatch ( lt , v , patch2 - > winding ) )
continue ;
// 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 . AddToTail ( point ) ;
}
}
// Sort the patches into clockwise order
angles . SetCount ( points . Count ( ) ) ;
for ( i = 0 ; i < points . Count ( ) ; i + + )
{
angle = GetAngle ( points [ 0 ] . leftdirection , points [ i ] . leftdirection , lt - > normal ) ;
if ( i = = 0 )
{
if ( g_drawlerp & & fabs ( angle ) > NORMAL_EPSILON )
{
MsgDev ( D_REPORT , " Debug: triangulation: internal error 7. \n " ) ;
}
angle = 0.0 ;
}
angles [ i ] . first = GetAngleDiff ( angle , 0 ) ;
angles [ i ] . second = i ;
}
angles . Sort ( LessThan ) ;
lt - > sortedwedges . SetCount ( points . Count ( ) ) ;
for ( i = 0 ; i < points . Count ( ) ; i + + )
{
lt - > sortedwedges [ i ] = points [ angles [ i ] . second ] ;
}
}
static void PurgePatches ( localtrian_t * lt )
{
CUtlArray < localtrian_t : : Wedge > points ;
int i , cur ;
CUtlArray < int > next ;
CUtlArray < int > prev ;
CUtlArray < int > valid ;
CUtlArray < pair_t > dists ;
vec_t angle ;
vec3_t normal ;
vec3_t v ;
points . Swap ( lt - > sortedwedges ) ;
lt - > sortedwedges . Purge ( ) ;
next . SetCount ( points . Count ( ) ) ;
prev . SetCount ( points . Count ( ) ) ;
valid . SetCount ( points . Count ( ) ) ;
dists . SetCount ( points . Count ( ) ) ;
for ( i = 0 ; i < points . Count ( ) ; i + + )
{
next [ i ] = ( i + 1 ) % points . Count ( ) ;
prev [ i ] = ( i - 1 + points . Count ( ) ) % points . Count ( ) ;
valid [ i ] = 1 ;
dists [ i ] . first = DotProduct ( points [ i ] . leftspot , points [ i ] . leftdirection ) ;
dists [ i ] . second = i ;
}
dists . Sort ( LessThan ) ;
for ( i = 0 ; i < points . Count ( ) ; i + + )
{
vec_t sangle , cangle ;
cur = dists [ i ] . second ;
if ( valid [ cur ] = = 0 )
continue ;
valid [ cur ] = 2 ; // mark current patch as final
SinCos ( TRIANGLE_SHAPE_THRESHOLD , & sangle , & cangle ) ;
CrossProduct ( points [ cur ] . leftdirection , lt - > normal , normal ) ;
VectorNormalize ( normal ) ;
VectorScale ( normal , cangle , v ) ;
VectorMA ( v , sangle , 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 ) < = DEG2RAD ( 1.0 ) | | GetAngleDiff ( angle , 0 ) < = M_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 , cangle , v ) ;
VectorMA ( v , sangle , 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 ) < = DEG2RAD ( 1.0 ) | | GetAngleDiff ( angle , 0 ) < = M_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 < points . Count ( ) ; i + + )
{
if ( valid [ i ] = = 2 )
{
lt - > sortedwedges . AddToTail ( points [ i ] ) ;
}
}
}
static void PlaceHullPoints ( localtrian_t * lt )
{
int i , j , n ;
vec_t dot , angle ;
localtrian_t : : HullPoint hp ;
CUtlArray < localtrian_t : : HullPoint > spots ;
CUtlArray < pair_t > angles ;
const localtrian_t : : Wedge * w ;
const localtrian_t : : Wedge * wnext ;
CUtlArray < localtrian_t : : HullPoint > arc_spots ;
CUtlArray < vec_t > arc_angles ;
CUtlArray < int > next ;
CUtlArray < int > prev ;
vec_t frac ;
vec_t len ;
vec_t dist ;
vec3_t v ;
// spots.reserve( lt->winding->numpoints );
spots . Purge ( ) ;
for ( i = 0 ; i < lt - > winding - > numpoints ; i + + )
{
VectorSubtract ( lt - > winding - > p [ 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 . AddToTail ( hp ) ;
}
if ( lt - > sortedwedges . Count ( ) = = 0 )
{
angles . SetCount ( spots . Count ( ) ) ;
for ( i = 0 ; i < spots . Count ( ) ; 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 ;
}
angles . Sort ( LessThan ) ;
lt - > sortedhullpoints . Purge ( ) ;
for ( i = 0 ; i < spots . Count ( ) ; i + + )
{
if ( g_drawlerp & & angles [ i ] . second ! = i )
MsgDev ( D_REPORT , " Debug: triangulation: internal error 8. \n " ) ;
lt - > sortedhullpoints . AddToTail ( spots [ angles [ i ] . second ] ) ;
}
return ;
}
lt - > sortedhullpoints . Purge ( ) ;
for ( i = 0 ; i < lt - > sortedwedges . Count ( ) ; i + + )
{
w = & lt - > sortedwedges [ i ] ;
wnext = & lt - > sortedwedges [ ( i + 1 ) % lt - > sortedwedges . Count ( ) ] ;
angles . SetCount ( spots . Count ( ) ) ;
for ( j = 0 ; j < spots . Count ( ) ; j + + )
{
angle = GetAngle ( w - > leftdirection , spots [ j ] . direction , lt - > normal ) ;
angles [ j ] . first = GetAngleDiff ( angle , 0 ) ;
angles [ j ] . second = j ;
}
angles . Sort ( LessThan ) ;
angle = GetAngle ( w - > leftdirection , wnext - > leftdirection , lt - > normal ) ;
if ( lt - > sortedwedges . Count ( ) = = 1 )
{
angle = M_PI2 ;
}
else
{
angle = GetAngleDiff ( angle , 0 ) ;
}
arc_spots . SetCount ( spots . Count ( ) + 2 ) ;
arc_angles . SetCount ( spots . Count ( ) + 2 ) ;
next . SetCount ( spots . Count ( ) + 2 ) ;
prev . SetCount ( spots . Count ( ) + 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 < spots . Count ( ) ; 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 ] ] < = M_PI + NORMAL_EPSILON )
{
frac = GetFrac ( arc_spots [ prev [ j ] ] . spot , arc_spots [ next [ j ] ] . spot , arc_spots [ j ] . direction , lt - > normal ) ;
len = ( 1.0 - 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 . AddToTail ( arc_spots [ j ] ) ;
}
}
}
static bool TryMakeSquare ( localtrian_t * lt , int i )
{
localtrian_t : : Wedge * w1 ;
localtrian_t : : Wedge * w2 ;
localtrian_t : : Wedge * w3 ;
vec3_t dir1 ;
vec3_t dir2 ;
vec_t angle ;
vec3_t v ;
w1 = & lt - > sortedwedges [ i ] ;
w2 = & lt - > sortedwedges [ ( i + 1 ) % lt - > sortedwedges . Count ( ) ] ;
w3 = & lt - > sortedwedges [ ( i + 2 ) % lt - > sortedwedges . Count ( ) ] ;
// (o, p1, p2) and (o, p2, p3) must be triangles and not in a square
if ( w1 - > shape ! = localtrian_t : : Wedge : : eTriangular | | w2 - > shape ! = localtrian_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 = localtrian_t : : Wedge : : eSquareLeft ;
VectorNegate ( dir1 , w1 - > wedgenormal ) ;
w2 - > shape = localtrian_t : : Wedge : : eSquareRight ;
VectorCopy ( dir2 , w2 - > wedgenormal ) ;
return true ;
}
static void FindSquares ( localtrian_t * lt )
{
CUtlArray < pair_t > dists ;
localtrian_t : : Wedge * w ;
int i ;
if ( lt - > sortedwedges . Count ( ) < = 2 )
return ;
dists . SetCount ( lt - > sortedwedges . Count ( ) ) ;
for ( i = 0 ; i < lt - > sortedwedges . Count ( ) ; i + + )
{
w = & lt - > sortedwedges [ i ] ;
dists [ i ] . first = DotProduct ( w - > leftspot , w - > leftdirection ) ;
dists [ i ] . second = i ;
}
dists . Sort ( LessThan ) ;
for ( i = 0 ; i < lt - > sortedwedges . Count ( ) ; i + + )
{
TryMakeSquare ( lt , dists [ i ] . second ) ;
TryMakeSquare ( lt , ( dists [ i ] . second - 2 + lt - > sortedwedges . Count ( ) ) % lt - > sortedwedges . Count ( ) ) ;
}
}
static localtrian_t * CreateLocalTriangulation ( const facetriangulation_t * facetrian , int patchnum )
{
localtrian_t * lt ;
const patch_t * patch ;
int facenum ;
localtrian_t : : Wedge * w ;
localtrian_t : : Wedge * wnext ;
vec_t angle ;
vec_t total ;
vec3_t normal ;
vec_t dot ;
vec3_t v ;
facenum = facetrian - > facenum ;
patch = & g_patches [ patchnum ] ;
lt = new localtrian_t ;
// Fill basic information for this local triangulation
lt - > plane = * GetPlaneFromFace ( facenum ) ;
lt - > plane . dist + = DotProduct ( g_face_offset [ facenum ] , lt - > plane . normal ) ;
lt - > winding = patch - > winding ;
VectorMA ( patch - > origin , - DEFAULT_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 ( ! PointInWindingEpsilon ( lt - > winding , lt - > plane . normal , lt - > center , ON_EPSILON , DEFAULT_EDGE_WIDTH ) )
{
WindingSnapPointEpsilon ( lt - > winding , lt - > plane . normal , 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 ( int i = 0 ; i < lt - > sortedwedges . Count ( ) ; i + + )
{
w = & lt - > sortedwedges [ i ] ;
wnext = & lt - > sortedwedges [ ( i + 1 ) % lt - > sortedwedges . Count ( ) ] ;
angle = GetAngle ( w - > leftdirection , wnext - > leftdirection , lt - > normal ) ;
if ( g_drawlerp & & ( lt - > sortedwedges . Count ( ) > = 2 & & fabs ( angle ) < = DEG2RAD ( 0.9 ) ) )
{
MsgDev ( D_REPORT , " Debug: triangulation: internal error 9. \n " ) ;
}
angle = GetAngleDiff ( angle , 0 ) ;
if ( lt - > sortedwedges . Count ( ) = = 1 )
{
angle = M_PI2 ;
}
total + = angle ;
if ( angle < = M_PI + NORMAL_EPSILON )
{
if ( angle < TRIANGLE_SHAPE_THRESHOLD )
{
w - > shape = localtrian_t : : Wedge : : eTriangular ;
VectorClear ( w - > wedgenormal ) ;
}
else
{
w - > shape = localtrian_t : : Wedge : : eConvex ;
VectorSubtract ( wnext - > leftspot , w - > leftspot , v ) ;
GetDirection ( v , lt - > normal , w - > wedgenormal ) ;
}
}
else
{
w - > shape = localtrian_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 )
{
MsgDev ( D_REPORT , " Debug: triangulation: internal error 10. \n " ) ;
}
}
}
if ( g_drawlerp & & ( lt - > sortedwedges . Count ( ) > 0 & & fabs ( total - M_PI2 ) > 10 * NORMAL_EPSILON ) )
{
MsgDev ( D_REPORT , " Debug: triangulation: internal error 11. \n " ) ;
}
FindSquares ( lt ) ;
// Calculate hull points
PlaceHullPoints ( lt ) ;
return lt ;
}
static void FindNeighbors ( facetriangulation_t * facetrian )
{
int i , j , e ;
int facenum1 ;
int facenum2 ;
const dplane_t * dp1 ;
const dplane_t * dp2 ;
const edgeshare_t * es ;
const dface_t * f1 ;
const dface_t * f2 ;
facenum1 = facetrian - > facenum ;
f1 = & g_dfaces [ facenum1 ] ;
dp1 = GetPlaneFromFace ( f1 ) ;
facetrian - > neighbors . Purge ( ) ;
facetrian - > neighbors . AddToTail ( facenum1 ) ;
for ( i = 0 ; i < f1 - > numedges ; i + + )
{
e = g_dsurfedges [ f1 - > 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 ( dp1 - > normal , dp2 - > normal ) < - NORMAL_EPSILON )
continue ;
for ( j = 0 ; j < facetrian - > neighbors . Count ( ) ; j + + )
{
if ( facetrian - > neighbors [ j ] = = facenum2 )
break ;
}
if ( j = = facetrian - > neighbors . Count ( ) )
{
facetrian - > neighbors . AddToTail ( facenum2 ) ;
}
}
}
static void BuildWalls ( facetriangulation_t * facetrian )
{
int i , j , e ;
int facenum ;
int facenum2 ;
const dface_t * f ;
const dface_t * f2 ;
const dplane_t * dp ;
const dplane_t * dp2 ;
const edgeshare_t * es ;
vec_t dot ;
facenum = facetrian - > facenum ;
f = & g_dfaces [ facenum ] ;
dp = GetPlaneFromFace ( f ) ;
facetrian - > walls . Purge ( ) ;
for ( i = 0 ; i < facetrian - > neighbors . Count ( ) ; 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 . AddToTail ( wall ) ;
}
}
}
}
}
static void CollectUsedPatches ( facetriangulation_t * facetrian )
{
int i , j , k ;
int patchnum ;
const localtrian_t * lt ;
const localtrian_t : : Wedge * w ;
facetrian - > usedpatches . Purge ( ) ;
for ( i = 0 ; i < facetrian - > localtriangulations . Count ( ) ; i + + )
{
lt = facetrian - > localtriangulations [ i ] ;
patchnum = lt - > patchnum ;
for ( k = 0 ; k < facetrian - > usedpatches . Count ( ) ; k + + )
{
if ( facetrian - > usedpatches [ k ] = = patchnum )
break ;
}
if ( k = = facetrian - > usedpatches . Count ( ) )
facetrian - > usedpatches . AddToTail ( patchnum ) ;
for ( j = 0 ; j < lt - > sortedwedges . Count ( ) ; j + + )
{
w = & lt - > sortedwedges [ j ] ;
patchnum = w - > leftpatchnum ;
for ( k = 0 ; k < facetrian - > usedpatches . Count ( ) ; k + + )
{
if ( facetrian - > usedpatches [ k ] = = patchnum )
break ;
}
if ( k = = facetrian - > usedpatches . Count ( ) )
facetrian - > usedpatches . AddToTail ( patchnum ) ;
}
}
}
// =====================================================================================
// CreateTriangulations
// =====================================================================================
void CreateTriangulations ( int facenum , int threadnum )
{
facetriangulation_t * facetrian ;
int patchnum ;
const patch_t * patch ;
localtrian_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 . Purge ( ) ;
for ( patch = g_face_patches [ facenum ] ; patch ; patch = patch - > next )
{
patchnum = patch - g_patches ;
lt = CreateLocalTriangulation ( facetrian , patchnum ) ;
facetrian - > localtriangulations . AddToTail ( lt ) ;
}
// Collect used patches
CollectUsedPatches ( facetrian ) ;
}
// =====================================================================================
// GetTriangulationPatches
// =====================================================================================
void GetTriangulationPatches ( int facenum , int * numpatches , const int * * patches )
{
const facetriangulation_t * facetrian ;
if ( g_numbounce < = 0 & & ! g_fastmode )
{
* numpatches = 0 ;
* patches = NULL ;
}
else
{
facetrian = g_facetriangulations [ facenum ] ;
* numpatches = facetrian - > usedpatches . Count ( ) ;
* patches = facetrian - > usedpatches . Base ( ) ;
}
}
// =====================================================================================
// FreeTriangulations
// =====================================================================================
void FreeTriangulations ( void )
{
facetriangulation_t * facetrian ;
for ( int i = 0 ; i < g_numfaces ; i + + )
{
facetrian = g_facetriangulations [ i ] ;
for ( int j = 0 ; j < facetrian - > localtriangulations . Count ( ) ; j + + )
{
delete facetrian - > localtriangulations [ j ] ;
}
g_facetriangulations [ i ] = NULL ;
delete facetrian ;
}
2020-08-31 00:15:53 +02:00
}