📄 light.c
字号:
// light.c
#include "light.h"
#ifdef _WIN32
#ifdef _TTIMOBUILD
#include "pakstuff.h"
#else
#include "../libs/pakstuff.h"
#endif
#endif
#define EXTRASCALE 2
typedef struct {
float plane[4];
vec3_t origin;
vec3_t vectors[2];
shaderInfo_t *si;
} filter_t;
#define MAX_FILTERS 1024
filter_t filters[MAX_FILTERS];
int numFilters;
extern char source[1024];
qboolean notrace;
qboolean patchshadows;
qboolean dump;
qboolean extra;
qboolean extraWide;
qboolean lightmapBorder;
qboolean noSurfaces;
int samplesize = 16; //sample size in units
int novertexlighting = 0;
int nogridlighting = 0;
// for run time tweaking of all area sources in the level
float areaScale = 0.25;
// for run time tweaking of all point sources in the level
float pointScale = 7500;
qboolean exactPointToPolygon = qtrue;
float formFactorValueScale = 3;
float linearScale = 1.0 / 8000;
light_t *lights;
int numPointLights;
int numAreaLights;
FILE *dumpFile;
int c_visible, c_occluded;
//int defaultLightSubdivide = 128; // vary by surface size?
int defaultLightSubdivide = 999; // vary by surface size?
vec3_t ambientColor;
vec3_t surfaceOrigin[ MAX_MAP_DRAW_SURFS ];
int entitySurface[ MAX_MAP_DRAW_SURFS ];
// 7,9,11 normalized to avoid being nearly coplanar with common faces
//vec3_t sunDirection = { 0.441835, 0.56807, 0.694313 };
//vec3_t sunDirection = { 0.45, 0, 0.9 };
//vec3_t sunDirection = { 0, 0, 1 };
// these are usually overrided by shader values
vec3_t sunDirection = { 0.45, 0.3, 0.9 };
vec3_t sunLight = { 100, 100, 50 };
typedef struct {
dbrush_t *b;
vec3_t bounds[2];
} skyBrush_t;
int numSkyBrushes;
skyBrush_t skyBrushes[MAX_MAP_BRUSHES];
/*
the corners of a patch mesh will always be exactly at lightmap samples.
The dimensions of the lightmap will be equal to the average length of the control
mesh in each dimension divided by 2.
The lightmap sample points should correspond to the chosen subdivision points.
*/
/*
===============================================================
SURFACE LOADING
===============================================================
*/
#define MAX_FACE_POINTS 128
/*
===============
SubdivideAreaLight
Subdivide area lights that are very large
A light that is subdivided will never backsplash, avoiding weird pools of light near edges
===============
*/
void SubdivideAreaLight( shaderInfo_t *ls, winding_t *w, vec3_t normal,
float areaSubdivide, qboolean backsplash ) {
float area, value, intensity;
light_t *dl, *dl2;
vec3_t mins, maxs;
int axis;
winding_t *front, *back;
vec3_t planeNormal;
float planeDist;
if ( !w ) {
return;
}
WindingBounds( w, mins, maxs );
// check for subdivision
for ( axis = 0 ; axis < 3 ; axis++ ) {
if ( maxs[axis] - mins[axis] > areaSubdivide ) {
VectorClear( planeNormal );
planeNormal[axis] = 1;
planeDist = ( maxs[axis] + mins[axis] ) * 0.5;
ClipWindingEpsilon ( w, planeNormal, planeDist, ON_EPSILON, &front, &back );
SubdivideAreaLight( ls, front, normal, areaSubdivide, qfalse );
SubdivideAreaLight( ls, back, normal, areaSubdivide, qfalse );
FreeWinding( w );
return;
}
}
// create a light from this
area = WindingArea (w);
if ( area <= 0 || area > 20000000 ) {
return;
}
numAreaLights++;
dl = malloc(sizeof(*dl));
memset (dl, 0, sizeof(*dl));
dl->next = lights;
lights = dl;
dl->type = emit_area;
WindingCenter( w, dl->origin );
dl->w = w;
VectorCopy ( normal, dl->normal);
dl->dist = DotProduct( dl->origin, normal );
value = ls->value;
intensity = value * area * areaScale;
VectorAdd( dl->origin, dl->normal, dl->origin );
VectorCopy( ls->color, dl->color );
dl->photons = intensity;
// emitColor is irrespective of the area
VectorScale( ls->color, value*formFactorValueScale*areaScale, dl->emitColor );
dl->si = ls;
if ( ls->contents & CONTENTS_FOG ) {
dl->twosided = qtrue;
}
// optionally create a point backsplash light
if ( backsplash && ls->backsplashFraction > 0 ) {
dl2 = malloc(sizeof(*dl));
memset (dl2, 0, sizeof(*dl2));
dl2->next = lights;
lights = dl2;
dl2->type = emit_point;
VectorMA( dl->origin, ls->backsplashDistance, normal, dl2->origin );
VectorCopy( ls->color, dl2->color );
dl2->photons = dl->photons * ls->backsplashFraction;
dl2->si = ls;
}
}
/*
===============
CountLightmaps
===============
*/
void CountLightmaps( void ) {
int count;
int i;
dsurface_t *ds;
qprintf ("--- CountLightmaps ---\n");
count = 0;
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
// see if this surface is light emiting
ds = &drawSurfaces[i];
if ( ds->lightmapNum > count ) {
count = ds->lightmapNum;
}
}
count++;
numLightBytes = count * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3;
if ( numLightBytes > MAX_MAP_LIGHTING ) {
Error("MAX_MAP_LIGHTING exceeded");
}
qprintf( "%5i drawSurfaces\n", numDrawSurfaces );
qprintf( "%5i lightmaps\n", count );
}
/*
===============
CreateSurfaceLights
This creates area lights
===============
*/
void CreateSurfaceLights( void ) {
int i, j, side;
dsurface_t *ds;
shaderInfo_t *ls;
winding_t *w;
cFacet_t *f;
light_t *dl;
vec3_t origin;
drawVert_t *dv;
int c_lightSurfaces;
float lightSubdivide;
vec3_t normal;
qprintf ("--- CreateSurfaceLights ---\n");
c_lightSurfaces = 0;
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
// see if this surface is light emiting
ds = &drawSurfaces[i];
ls = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
if ( ls->value == 0 ) {
continue;
}
// determine how much we need to chop up the surface
if ( ls->lightSubdivide ) {
lightSubdivide = ls->lightSubdivide;
} else {
lightSubdivide = defaultLightSubdivide;
}
c_lightSurfaces++;
// an autosprite shader will become
// a point light instead of an area light
if ( ls->autosprite ) {
// autosprite geometry should only have four vertexes
if ( surfaceTest[i] ) {
// curve or misc_model
f = surfaceTest[i]->facets;
if ( surfaceTest[i]->numFacets != 1 || f->numBoundaries != 4 ) {
_printf( "WARNING: surface at (%i %i %i) has autosprite shader but isn't a quad\n",
(int)f->points[0], (int)f->points[1], (int)f->points[2] );
}
VectorAdd( f->points[0], f->points[1], origin );
VectorAdd( f->points[2], origin, origin );
VectorAdd( f->points[3], origin, origin );
VectorScale( origin, 0.25, origin );
} else {
// normal polygon
dv = &drawVerts[ ds->firstVert ];
if ( ds->numVerts != 4 ) {
_printf( "WARNING: surface at (%i %i %i) has autosprite shader but %i verts\n",
(int)dv->xyz[0], (int)dv->xyz[1], (int)dv->xyz[2] );
continue;
}
VectorAdd( dv[0].xyz, dv[1].xyz, origin );
VectorAdd( dv[2].xyz, origin, origin );
VectorAdd( dv[3].xyz, origin, origin );
VectorScale( origin, 0.25, origin );
}
numPointLights++;
dl = malloc(sizeof(*dl));
memset (dl, 0, sizeof(*dl));
dl->next = lights;
lights = dl;
VectorCopy( origin, dl->origin );
VectorCopy( ls->color, dl->color );
dl->photons = ls->value * pointScale;
dl->type = emit_point;
continue;
}
// possibly create for both sides of the polygon
for ( side = 0 ; side <= ls->twoSided ; side++ ) {
// create area lights
if ( surfaceTest[i] ) {
// curve or misc_model
for ( j = 0 ; j < surfaceTest[i]->numFacets ; j++ ) {
f = surfaceTest[i]->facets + j;
w = AllocWinding( f->numBoundaries );
w->numpoints = f->numBoundaries;
memcpy( w->p, f->points, f->numBoundaries * 12 );
VectorCopy( f->surface, normal );
if ( side ) {
winding_t *t;
t = w;
w = ReverseWinding( t );
FreeWinding( t );
VectorSubtract( vec3_origin, normal, normal );
}
SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
}
} else {
// normal polygon
w = AllocWinding( ds->numVerts );
w->numpoints = ds->numVerts;
for ( j = 0 ; j < ds->numVerts ; j++ ) {
VectorCopy( drawVerts[ds->firstVert+j].xyz, w->p[j] );
}
VectorCopy( ds->lightmapVecs[2], normal );
if ( side ) {
winding_t *t;
t = w;
w = ReverseWinding( t );
FreeWinding( t );
VectorSubtract( vec3_origin, normal, normal );
}
SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
}
}
}
_printf( "%5i light emitting surfaces\n", c_lightSurfaces );
}
/*
================
FindSkyBrushes
================
*/
void FindSkyBrushes( void ) {
int i, j;
dbrush_t *b;
skyBrush_t *sb;
shaderInfo_t *si;
dbrushside_t *s;
// find the brushes
for ( i = 0 ; i < numbrushes ; i++ ) {
b = &dbrushes[i];
for ( j = 0 ; j < b->numSides ; j++ ) {
s = &dbrushsides[ b->firstSide + j ];
if ( dshaders[ s->shaderNum ].surfaceFlags & SURF_SKY ) {
sb = &skyBrushes[ numSkyBrushes ];
sb->b = b;
sb->bounds[0][0] = -dplanes[ dbrushsides[ b->firstSide + 0 ].planeNum ].dist - 1;
sb->bounds[1][0] = dplanes[ dbrushsides[ b->firstSide + 1 ].planeNum ].dist + 1;
sb->bounds[0][1] = -dplanes[ dbrushsides[ b->firstSide + 2 ].planeNum ].dist - 1;
sb->bounds[1][1] = dplanes[ dbrushsides[ b->firstSide + 3 ].planeNum ].dist + 1;
sb->bounds[0][2] = -dplanes[ dbrushsides[ b->firstSide + 4 ].planeNum ].dist - 1;
sb->bounds[1][2] = dplanes[ dbrushsides[ b->firstSide + 5 ].planeNum ].dist + 1;
numSkyBrushes++;
break;
}
}
}
// default
VectorNormalize( sunDirection, sunDirection );
// find the sky shader
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
si = ShaderInfoForShader( dshaders[ drawSurfaces[i].shaderNum ].shader );
if ( si->surfaceFlags & SURF_SKY ) {
VectorCopy( si->sunLight, sunLight );
VectorCopy( si->sunDirection, sunDirection );
break;
}
}
}
/*
=================================================================
LIGHT SETUP
=================================================================
*/
/*
==================
FindTargetEntity
==================
*/
entity_t *FindTargetEntity( const char *target ) {
int i;
const char *n;
for ( i = 0 ; i < num_entities ; i++ ) {
n = ValueForKey (&entities[i], "targetname");
if ( !strcmp (n, target) ) {
return &entities[i];
}
}
return NULL;
}
/*
=============
CreateEntityLights
=============
*/
void CreateEntityLights (void)
{
int i;
light_t *dl;
entity_t *e, *e2;
const char *name;
const char *target;
vec3_t dest;
const char *_color;
float intensity;
int spawnflags;
//
// entities
//
for ( i = 0 ; i < num_entities ; i++ ) {
e = &entities[i];
name = ValueForKey (e, "classname");
if (strncmp (name, "light", 5))
continue;
numPointLights++;
dl = malloc(sizeof(*dl));
memset (dl, 0, sizeof(*dl));
dl->next = lights;
lights = dl;
spawnflags = FloatForKey (e, "spawnflags");
if ( spawnflags & 1 ) {
dl->linearLight = qtrue;
}
GetVectorForKey (e, "origin", dl->origin);
dl->style = FloatForKey (e, "_style");
if (!dl->style)
dl->style = FloatForKey (e, "style");
if (dl->style < 0)
dl->style = 0;
intensity = FloatForKey (e, "light");
if (!intensity)
intensity = FloatForKey (e, "_light");
if (!intensity)
intensity = 300;
_color = ValueForKey (e, "_color");
if (_color && _color[0])
{
sscanf (_color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2]);
ColorNormalize (dl->color, dl->color);
}
else
dl->color[0] = dl->color[1] = dl->color[2] = 1.0;
intensity = intensity * pointScale;
dl->photons = intensity;
dl->type = emit_point;
// lights with a target will be spotlights
target = ValueForKey (e, "target");
if ( target[0] ) {
float radius;
float dist;
e2 = FindTargetEntity (target);
if (!e2) {
_printf ("WARNING: light at (%i %i %i) has missing target\n",
(int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]);
} else {
GetVectorForKey (e2, "origin", dest);
VectorSubtract (dest, dl->origin, dl->normal);
dist = VectorNormalize (dl->normal, dl->normal);
radius = FloatForKey (e, "radius");
if ( !radius ) {
radius = 64;
}
if ( !dist ) {
dist = 64;
}
dl->radiusByDist = (radius + 16) / dist;
dl->type = emit_spotlight;
}
}
}
}
//=================================================================
/*
================
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -