📄 light.c
字号:
SetEntityOrigins
Find the offset values for inline models
================
*/
void SetEntityOrigins( void ) {
int i, j;
entity_t *e;
vec3_t origin;
const char *key;
int modelnum;
dmodel_t *dm;
for ( i=0 ; i < num_entities ; i++ ) {
e = &entities[i];
key = ValueForKey (e, "model");
if ( key[0] != '*' ) {
continue;
}
modelnum = atoi( key + 1 );
dm = &dmodels[ modelnum ];
// set entity surface to true for all surfaces for this model
for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
entitySurface[ dm->firstSurface + j ] = qtrue;
}
key = ValueForKey (e, "origin");
if ( !key[0] ) {
continue;
}
GetVectorForKey ( e, "origin", origin );
// set origin for all surfaces for this model
for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
VectorCopy( origin, surfaceOrigin[ dm->firstSurface + j ] );
}
}
}
/*
=================================================================
=================================================================
*/
#define MAX_POINTS_ON_WINDINGS 64
/*
================
PointToPolygonFormFactor
================
*/
float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w ) {
vec3_t triVector, triNormal;
int i, j;
vec3_t dirs[MAX_POINTS_ON_WINDING];
float total;
float dot, angle, facing;
for ( i = 0 ; i < w->numpoints ; i++ ) {
VectorSubtract( w->p[i], point, dirs[i] );
VectorNormalize( dirs[i], dirs[i] );
}
// duplicate first vertex to avoid mod operation
VectorCopy( dirs[0], dirs[i] );
total = 0;
for ( i = 0 ; i < w->numpoints ; i++ ) {
j = i+1;
dot = DotProduct( dirs[i], dirs[j] );
// roundoff can cause slight creep, which gives an IND from acos
if ( dot > 1.0 ) {
dot = 1.0;
} else if ( dot < -1.0 ) {
dot = -1.0;
}
angle = acos( dot );
CrossProduct( dirs[i], dirs[j], triVector );
if ( VectorNormalize( triVector, triNormal ) < 0.0001 ) {
continue;
}
facing = DotProduct( normal, triNormal );
total += facing * angle;
if ( total > 6.3 || total < -6.3 ) {
static qboolean printed;
if ( !printed ) {
printed = qtrue;
_printf( "WARNING: bad PointToPolygonFormFactor: %f at %1.1f %1.1f %1.1f from %1.1f %1.1f %1.1f\n", total,
w->p[i][0], w->p[i][1], w->p[i][2], point[0], point[1], point[2]);
}
return 0;
}
}
total /= 2*3.141592657; // now in the range of 0 to 1 over the entire incoming hemisphere
return total;
}
/*
================
FilterTrace
Returns 0 to 1.0 filter fractions for the given trace
================
*/
void FilterTrace( const vec3_t start, const vec3_t end, vec3_t filter ) {
float d1, d2;
filter_t *f;
int filterNum;
vec3_t point;
float frac;
int i;
float s, t;
int u, v;
int x, y;
byte *pixel;
float radius;
float len;
vec3_t total;
filter[0] = 1.0;
filter[1] = 1.0;
filter[2] = 1.0;
for ( filterNum = 0 ; filterNum < numFilters ; filterNum++ ) {
f = &filters[ filterNum ];
// see if the plane is crossed
d1 = DotProduct( start, f->plane ) - f->plane[3];
d2 = DotProduct( end, f->plane ) - f->plane[3];
if ( ( d1 < 0 ) == ( d2 < 0 ) ) {
continue;
}
// calculate the crossing point
frac = d1 / ( d1 - d2 );
for ( i = 0 ; i < 3 ; i++ ) {
point[i] = start[i] + frac * ( end[i] - start[i] );
}
VectorSubtract( point, f->origin, point );
s = DotProduct( point, f->vectors[0] );
t = 1.0 - DotProduct( point, f->vectors[1] );
if ( s < 0 || s >= 1.0 || t < 0 || t >= 1.0 ) {
continue;
}
// decide the filter size
radius = 10 * frac;
len = VectorLength( f->vectors[0] );
if ( !len ) {
continue;
}
radius = radius * len * f->si->width;
// look up the filter, taking multiple samples
VectorClear( total );
for ( u = -1 ; u <= 1 ; u++ ) {
for ( v = -1 ; v <=1 ; v++ ) {
x = s * f->si->width + u * radius;
if ( x < 0 ) {
x = 0;
}
if ( x >= f->si->width ) {
x = f->si->width - 1;
}
y = t * f->si->height + v * radius;
if ( y < 0 ) {
y = 0;
}
if ( y >= f->si->height ) {
y = f->si->height - 1;
}
pixel = f->si->pixels + ( y * f->si->width + x ) * 4;
total[0] += pixel[0];
total[1] += pixel[1];
total[2] += pixel[2];
}
}
filter[0] *= total[0]/(255.0*9);
filter[1] *= total[1]/(255.0*9);
filter[2] *= total[2]/(255.0*9);
}
}
/*
================
SunToPoint
Returns an amount of light to add at the point
================
*/
int c_sunHit, c_sunMiss;
void SunToPoint( const vec3_t origin, traceWork_t *tw, vec3_t addLight ) {
int i;
trace_t trace;
skyBrush_t *b;
vec3_t end;
if ( !numSkyBrushes ) {
VectorClear( addLight );
return;
}
VectorMA( origin, MAX_WORLD_COORD * 2, sunDirection, end );
TraceLine( origin, end, &trace, qtrue, tw );
// see if trace.hit is inside a sky brush
for ( i = 0 ; i < numSkyBrushes ; i++) {
b = &skyBrushes[ i ];
// this assumes that sky brushes are axial...
if ( trace.hit[0] < b->bounds[0][0]
|| trace.hit[0] > b->bounds[1][0]
|| trace.hit[1] < b->bounds[0][1]
|| trace.hit[1] > b->bounds[1][1]
|| trace.hit[2] < b->bounds[0][2]
|| trace.hit[2] > b->bounds[1][2] ) {
continue;
}
// trace again to get intermediate filters
TraceLine( origin, trace.hit, &trace, qtrue, tw );
// we hit the sky, so add sunlight
if ( numthreads == 1 ) {
c_sunHit++;
}
addLight[0] = trace.filter[0] * sunLight[0];
addLight[1] = trace.filter[1] * sunLight[1];
addLight[2] = trace.filter[2] * sunLight[2];
return;
}
if ( numthreads == 1 ) {
c_sunMiss++;
}
VectorClear( addLight );
}
/*
================
SunToPlane
================
*/
void SunToPlane( const vec3_t origin, const vec3_t normal, vec3_t color, traceWork_t *tw ) {
float angle;
vec3_t sunColor;
if ( !numSkyBrushes ) {
return;
}
angle = DotProduct( normal, sunDirection );
if ( angle <= 0 ) {
return; // facing away
}
SunToPoint( origin, tw, sunColor );
VectorMA( color, angle, sunColor, color );
}
/*
================
LightingAtSample
================
*/
void LightingAtSample( vec3_t origin, vec3_t normal, vec3_t color,
qboolean testOcclusion, qboolean forceSunLight, traceWork_t *tw ) {
light_t *light;
trace_t trace;
float angle;
float add;
float dist;
vec3_t dir;
VectorCopy( ambientColor, color );
// trace to all the lights
for ( light = lights ; light ; light = light->next ) {
//MrE: if the light is behind the surface
if ( DotProduct(light->origin, normal) - DotProduct(normal, origin) < 0 )
continue;
// testing exact PTPFF
if ( exactPointToPolygon && light->type == emit_area ) {
float factor;
float d;
vec3_t pushedOrigin;
// see if the point is behind the light
d = DotProduct( origin, light->normal ) - light->dist;
if ( !light->twosided ) {
if ( d < -1 ) {
continue; // point is behind light
}
}
// test occlusion and find light filters
// clip the line, tracing from the surface towards the light
if ( !notrace && testOcclusion ) {
TraceLine( origin, light->origin, &trace, qfalse, tw );
// other light rays must not hit anything
if ( trace.passSolid ) {
continue;
}
} else {
trace.filter[0] = 1.0;
trace.filter[1] = 1.0;
trace.filter[2] = 1.0;
}
// nudge the point so that it is clearly forward of the light
// so that surfaces meeting a light emiter don't get black edges
if ( d > -8 && d < 8 ) {
VectorMA( origin, (8-d), light->normal, pushedOrigin );
} else {
VectorCopy( origin, pushedOrigin );
}
// calculate the contribution
factor = PointToPolygonFormFactor( pushedOrigin, normal, light->w );
if ( factor <= 0 ) {
if ( light->twosided ) {
factor = -factor;
} else {
continue;
}
}
color[0] += factor * light->emitColor[0] * trace.filter[0];
color[1] += factor * light->emitColor[1] * trace.filter[1];
color[2] += factor * light->emitColor[2] * trace.filter[2];
continue;
}
// calculate the amount of light at this sample
if ( light->type == emit_point ) {
VectorSubtract( light->origin, origin, dir );
dist = VectorNormalize( dir, dir );
// clamp the distance to prevent super hot spots
if ( dist < 16 ) {
dist = 16;
}
angle = DotProduct( normal, dir );
if ( light->linearLight ) {
add = angle * light->photons * linearScale - dist;
if ( add < 0 ) {
add = 0;
}
} else {
add = light->photons / ( dist * dist ) * angle;
}
} else if ( light->type == emit_spotlight ) {
float distByNormal;
vec3_t pointAtDist;
float radiusAtDist;
float sampleRadius;
vec3_t distToSample;
float coneScale;
VectorSubtract( light->origin, origin, dir );
distByNormal = -DotProduct( dir, light->normal );
if ( distByNormal < 0 ) {
continue;
}
VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
radiusAtDist = light->radiusByDist * distByNormal;
VectorSubtract( origin, pointAtDist, distToSample );
sampleRadius = VectorLength( distToSample );
if ( sampleRadius >= radiusAtDist ) {
continue; // outside the cone
}
if ( sampleRadius <= radiusAtDist - 32 ) {
coneScale = 1.0; // fully inside
} else {
coneScale = ( radiusAtDist - sampleRadius ) / 32.0;
}
dist = VectorNormalize( dir, dir );
// clamp the distance to prevent super hot spots
if ( dist < 16 ) {
dist = 16;
}
angle = DotProduct( normal, dir );
add = light->photons / ( dist * dist ) * angle * coneScale;
} else if ( light->type == emit_area ) {
VectorSubtract( light->origin, origin, dir );
dist = VectorNormalize( dir, dir );
// clamp the distance to prevent super hot spots
if ( dist < 16 ) {
dist = 16;
}
angle = DotProduct( normal, dir );
if ( angle <= 0 ) {
continue;
}
angle *= -DotProduct( light->normal, dir );
if ( angle <= 0 ) {
continue;
}
if ( light->linearLight ) {
add = angle * light->photons * linearScale - dist;
if ( add < 0 ) {
add = 0;
}
} else {
add = light->photons / ( dist * dist ) * angle;
}
}
if ( add <= 1.0 ) {
continue;
}
// clip the line, tracing from the surface towards the light
if ( !notrace && testOcclusion ) {
TraceLine( origin, light->origin, &trace, qfalse, tw );
// other light rays must not hit anything
if ( trace.passSolid ) {
continue;
}
} else {
trace.filter[0] = 1;
trace.filter[1] = 1;
trace.filter[2] = 1;
}
// add the result
color[0] += add * light->color[0] * trace.filter[0];
color[1] += add * light->color[1] * trace.filter[1];
color[2] += add * light->color[2] * trace.filter[2];
}
//
// trace directly to the sun
//
if ( testOcclusion || forceSunLight ) {
SunToPlane( origin, normal, color, tw );
}
}
/*
=============
PrintOccluded
For debugging
=============
*/
void PrintOccluded( byte occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE],
int width, int height ) {
int i, j;
_printf( "\n" );
for ( i = 0 ; i < height ; i++ ) {
for ( j = 0 ; j < width ; j++ ) {
_printf("%i", (int)occluded[j][i] );
}
_printf( "\n" );
}
}
/*
=============
VertexLighting
Vertex lighting will completely ignore occlusion, because
shadows would not be resolvable anyway.
=============
*/
void VertexLighting( dsurface_t *ds, qboolean testOcclusion, qboolean forceSunLight, float scale, traceWork_t *tw ) {
int i, j;
drawVert_t *dv;
vec3_t sample, normal;
float max;
VectorCopy( ds->lightmapVecs[2], normal );
// generate vertex lighting
for ( i = 0 ; i < ds->numVerts ; i++ ) {
dv = &drawVerts[ ds->firstVert + i ];
if ( ds->patchWidth ) {
LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
}
else if (ds->surfaceType == MST_TRIANGLE_SOUP) {
LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
}
else {
LightingAtSample( dv->xyz, normal, sample, testOcclusion, forceSunLight, tw );
}
if (scale >= 0)
VectorScale(sample, scale, sample);
// clamp with color normalization
max = sample[0];
if ( sample[1] > max ) {
max = sample[1];
}
if ( sample[2] > max ) {
max = sample[2];
}
if ( max > 255 ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -