Dirtmapping (ambient occlusion) from q3map2
This commit is contained in:
parent
02196e2efa
commit
741d341332
|
|
@ -39,6 +39,8 @@ typedef vec_t vec3_t[3];
|
|||
|
||||
#define Q_PI 3.14159265358979323846
|
||||
|
||||
#define DEG2RAD( a ) ( ( a ) * ( ( 2 * Q_PI ) / 360.0 ) )
|
||||
|
||||
extern const vec3_t vec3_origin;
|
||||
|
||||
#define EQUAL_EPSILON 0.001
|
||||
|
|
@ -91,12 +93,31 @@ VectorInverse(vec3_t v)
|
|||
v[2] = -v[2];
|
||||
}
|
||||
|
||||
static inline void
|
||||
VectorSet(vec3_t out, vec_t x, vec_t y, vec_t z)
|
||||
{
|
||||
out[0] = x;
|
||||
out[1] = y;
|
||||
out[2] = z;
|
||||
}
|
||||
|
||||
static inline vec_t
|
||||
Q_rint(vec_t in)
|
||||
{
|
||||
return (vec_t)(floor(in + 0.5));
|
||||
}
|
||||
|
||||
/*
|
||||
Random()
|
||||
returns a pseudorandom number between 0 and 1
|
||||
*/
|
||||
|
||||
static inline vec_t
|
||||
Random( void )
|
||||
{
|
||||
return (vec_t) rand() / RAND_MAX;
|
||||
}
|
||||
|
||||
static inline void
|
||||
VectorMA(const vec3_t va, vec_t scale, const vec3_t vb, vec3_t vc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -68,6 +68,17 @@ typedef struct entity_s {
|
|||
float anglescale;
|
||||
int style;
|
||||
|
||||
/* worldspawn only */
|
||||
vec_t dirtdepth;
|
||||
int dirtmode;
|
||||
int sunlight_dirt;
|
||||
int minlight_dirt;
|
||||
|
||||
/* worldspawn, light entities */
|
||||
vec_t dirtscale;
|
||||
vec_t dirtgain;
|
||||
int dirt;
|
||||
|
||||
char target[MAX_ENT_VALUE];
|
||||
char targetname[MAX_ENT_VALUE];
|
||||
struct epair_s *epairs;
|
||||
|
|
|
|||
|
|
@ -104,6 +104,24 @@ extern lightsample_t minlight;
|
|||
extern lightsample_t sunlight;
|
||||
extern vec3_t sunvec;
|
||||
|
||||
/* dirt */
|
||||
|
||||
extern qboolean dirty; // should any dirtmapping take place?
|
||||
extern qboolean dirtDebug;
|
||||
extern int dirtMode;
|
||||
extern float dirtDepth;
|
||||
extern float dirtScale;
|
||||
extern float dirtGain;
|
||||
|
||||
extern qboolean globalDirt; // apply dirt to all lights (unless they override it)?
|
||||
extern qboolean sunlightDirt; // apply dirt to sunlight?
|
||||
extern qboolean minlightDirt; // apply dirt to minlight?
|
||||
|
||||
extern qboolean dirtModeSetOnCmdline;
|
||||
extern qboolean dirtDepthSetOnCmdline;
|
||||
extern qboolean dirtScaleSetOnCmdline;
|
||||
extern qboolean dirtGainSetOnCmdline;
|
||||
|
||||
/*
|
||||
* Return space for the lightmap and colourmap at the same time so it can
|
||||
* be done in a thread-safe manner.
|
||||
|
|
@ -116,4 +134,6 @@ extern byte *lit_filebase;
|
|||
extern int oversample;
|
||||
extern qboolean write_litfile;
|
||||
|
||||
void SetupDirt();
|
||||
|
||||
#endif /* __LIGHT_LIGHT_H__ */
|
||||
|
|
|
|||
|
|
@ -417,6 +417,26 @@ LoadEntities(const bsp2_t *bsp)
|
|||
normalize_color_format(minlight.color);
|
||||
} else if (!strcmp(key, "_anglesense") || !strcmp(key, "_anglescale"))
|
||||
entity->anglescale = atof(com_token);
|
||||
else if (!strcmp(key, "_dirtdepth"))
|
||||
entity->dirtdepth = atof(com_token);
|
||||
else if (!strcmp(key, "_dirtmode"))
|
||||
entity->dirtmode = atoi(com_token);
|
||||
else if (!strcmp(key, "_sunlight_dirt"))
|
||||
entity->sunlight_dirt = atoi(com_token);
|
||||
else if (!strcmp(key, "_minlight_dirt"))
|
||||
entity->minlight_dirt = atoi(com_token);
|
||||
else if (!strcmp(key, "_dirtscale"))
|
||||
entity->dirtscale = atof(com_token);
|
||||
else if (!strcmp(key, "_dirtgain"))
|
||||
entity->dirtgain = atof(com_token);
|
||||
else if (!strcmp(key, "_dirt")) {
|
||||
entity->dirt = atoi(com_token);
|
||||
if (entity->dirt == 1 && !dirty) {
|
||||
logprint("entity with \"_dirt\" \"1\" detected, enabling "
|
||||
"dirtmapping.\n");
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -445,6 +465,54 @@ LoadEntities(const bsp2_t *bsp)
|
|||
}
|
||||
if (entity->anglescale >= 0 && entity->anglescale <= 1.0)
|
||||
sun_anglescale = entity->anglescale;
|
||||
|
||||
if (entity->dirtdepth && !dirtDepthSetOnCmdline) {
|
||||
dirtDepth = entity->dirtdepth;
|
||||
logprint("Using dirtdepth value %f from worldspawn.\n",
|
||||
dirtDepth);
|
||||
}
|
||||
if (entity->dirtmode && !dirtModeSetOnCmdline) {
|
||||
dirtMode = entity->dirtmode;
|
||||
logprint("Using dirtmode value %i from worldspawn.\n",
|
||||
dirtMode);
|
||||
}
|
||||
if (entity->dirtscale && !dirtScaleSetOnCmdline) {
|
||||
dirtScale = entity->dirtscale;
|
||||
logprint("Using dirtscale value %f from worldspawn.\n",
|
||||
dirtScale);
|
||||
}
|
||||
if (entity->dirtgain && !dirtGainSetOnCmdline) {
|
||||
dirtGain = entity->dirtgain;
|
||||
logprint("Using dirtgain value %f from worldspawn.\n",
|
||||
dirtGain);
|
||||
}
|
||||
if (entity->dirt == 1) {
|
||||
globalDirt = true;
|
||||
dirty = true;
|
||||
logprint("Global dirtmapping enabled in worldspawn.\n");
|
||||
}
|
||||
|
||||
if (entity->sunlight_dirt == 1) {
|
||||
sunlightDirt = true;
|
||||
dirty = true;
|
||||
logprint("Sunlight dirtmapping enabled in worldspawn.\n");
|
||||
} else if (entity->sunlight_dirt == -1) {
|
||||
sunlightDirt = false;
|
||||
logprint("Sunlight dirtmapping disabled in worldspawn.\n");
|
||||
} else {
|
||||
sunlightDirt = globalDirt;
|
||||
}
|
||||
|
||||
if (entity->minlight_dirt == 1) {
|
||||
minlightDirt = true;
|
||||
dirty = true;
|
||||
logprint("Minlight dirtmapping enabled in worldspawn.\n");
|
||||
} else if (entity->minlight_dirt == -1) {
|
||||
minlightDirt = false;
|
||||
logprint("Minlight dirtmapping disabled in worldspawn.\n");
|
||||
} else {
|
||||
minlightDirt = globalDirt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,24 @@ lightsample_t minlight = { 0, { 255, 255, 255 } };
|
|||
lightsample_t sunlight = { 0, { 255, 255, 255 } };
|
||||
vec3_t sunvec = { 0, 0, 16384 }; /* defaults to straight down */
|
||||
|
||||
/* dirt */
|
||||
qboolean dirty = false;
|
||||
qboolean dirtDebug = false;
|
||||
int dirtMode = 0;
|
||||
float dirtDepth = 128.0f;
|
||||
float dirtScale = 1.0f;
|
||||
float dirtGain = 1.0f;
|
||||
|
||||
qboolean globalDirt = false;
|
||||
qboolean sunlightDirt = false;
|
||||
qboolean minlightDirt = false;
|
||||
|
||||
qboolean dirtSetOnCmdline = false;
|
||||
qboolean dirtModeSetOnCmdline = false;
|
||||
qboolean dirtDepthSetOnCmdline = false;
|
||||
qboolean dirtScaleSetOnCmdline = false;
|
||||
qboolean dirtGainSetOnCmdline = false;
|
||||
|
||||
byte *filebase; // start of lightmap data
|
||||
static byte *file_p; // start of free space after data
|
||||
static byte *file_end; // end of free space for lightmap data
|
||||
|
|
@ -257,6 +275,50 @@ main(int argc, const char **argv)
|
|||
anglescale = sun_anglescale = atoi(argv[++i]);
|
||||
else
|
||||
Error("-anglesense requires a numeric argument (0.0 - 1.0)");
|
||||
} else if ( !strcmp( argv[ i ], "-dirty" ) ) {
|
||||
dirty = true;
|
||||
globalDirt = true;
|
||||
sunlightDirt = true;
|
||||
minlightDirt = true;
|
||||
logprint( "Dirtmapping enabled globally\n" );
|
||||
} else if ( !strcmp( argv[ i ], "-dirtdebug" ) || !strcmp( argv[ i ], "-debugdirt" ) ) {
|
||||
dirty = true;
|
||||
globalDirt = true;
|
||||
dirtDebug = true;
|
||||
logprint( "Dirtmap debugging enabled\n" );
|
||||
} else if ( !strcmp( argv[ i ], "-dirtmode" ) ) {
|
||||
dirtModeSetOnCmdline = true;
|
||||
dirtMode = atoi( argv[ ++i ] );
|
||||
if ( dirtMode != 0 && dirtMode != 1 ) {
|
||||
dirtMode = 0;
|
||||
}
|
||||
if ( dirtMode == 1 ) {
|
||||
logprint( "Enabling randomized dirtmapping\n" );
|
||||
}
|
||||
else{
|
||||
logprint( "Enabling ordered dirtmapping\n" );
|
||||
}
|
||||
} else if ( !strcmp( argv[ i ], "-dirtdepth" ) ) {
|
||||
dirtDepthSetOnCmdline = true;
|
||||
dirtDepth = atof( argv[ ++i ] );
|
||||
if ( dirtDepth <= 0.0f ) {
|
||||
dirtDepth = 128.0f;
|
||||
}
|
||||
logprint( "Dirtmapping depth set to %.1f\n", dirtDepth );
|
||||
} else if ( !strcmp( argv[ i ], "-dirtscale" ) ) {
|
||||
dirtScaleSetOnCmdline = true;
|
||||
dirtScale = atof( argv[ ++i ] );
|
||||
if ( dirtScale <= 0.0f ) {
|
||||
dirtScale = 1.0f;
|
||||
}
|
||||
logprint( "Dirtmapping scale set to %.1f\n", dirtScale );
|
||||
} else if ( !strcmp( argv[ i ], "-dirtgain" ) ) {
|
||||
dirtGainSetOnCmdline = true;
|
||||
dirtGain = atof( argv[ ++i ] );
|
||||
if ( dirtGain <= 0.0f ) {
|
||||
dirtGain = 1.0f;
|
||||
}
|
||||
logprint( "Dirtmapping gain set to %.1f\n", dirtGain );
|
||||
} else if (argv[i][0] == '-')
|
||||
Error("Unknown option \"%s\"", argv[i]);
|
||||
else
|
||||
|
|
@ -267,6 +329,7 @@ main(int argc, const char **argv)
|
|||
printf("usage: light [-threads num] [-extra|-extra4]\n"
|
||||
" [-light num] [-addmin] [-anglescale|-anglesense]\n"
|
||||
" [-dist n] [-range n] [-gate n] [-lit]\n"
|
||||
" [-dirty] [-dirtdebug] [-dirtmode n] [-dirtdepth n] [-dirtscale n] [-dirtgain n]\n"
|
||||
" [-soft [n]] bspfile\n");
|
||||
exit(1);
|
||||
}
|
||||
|
|
@ -301,6 +364,10 @@ main(int argc, const char **argv)
|
|||
ConvertBSPFormat(BSP2VERSION, &bspdata);
|
||||
|
||||
LoadEntities(bsp);
|
||||
|
||||
if (dirty)
|
||||
SetupDirt();
|
||||
|
||||
MakeTnodes(bsp);
|
||||
modelinfo = malloc(bsp->nummodels * sizeof(*modelinfo));
|
||||
FindModelInfo(bsp);
|
||||
|
|
|
|||
332
light/ltface.c
332
light/ltface.c
|
|
@ -158,6 +158,12 @@ typedef struct {
|
|||
|
||||
int numpoints;
|
||||
vec3_t points[SINGLEMAP];
|
||||
|
||||
/*
|
||||
raw ambient occlusion amount per sample point, 0-1, where 1 is
|
||||
fully occluded. dirtgain/dirtscale are not applied yet
|
||||
*/
|
||||
vec_t occlusion[SINGLEMAP];
|
||||
} lightsurf_t;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -705,6 +711,69 @@ Light_ClampMin(lightsample_t *sample, const vec_t light, const vec3_t color)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ============
|
||||
* Dirt_GetScaleFactor
|
||||
*
|
||||
* returns scale factor for dirt/ambient occlusion
|
||||
* ============
|
||||
*/
|
||||
static inline vec_t
|
||||
Dirt_GetScaleFactor(vec_t occlusion, const entity_t *entity)
|
||||
{
|
||||
vec_t light_dirtgain = dirtGain;
|
||||
vec_t light_dirtscale = dirtScale;
|
||||
vec_t outDirt;
|
||||
qboolean usedirt;
|
||||
|
||||
/* is dirt processing disabled entirely? */
|
||||
if (!dirty)
|
||||
return 1.0f;
|
||||
|
||||
/* should this light be affected by dirt? */
|
||||
if (entity && entity->dirt == -1) {
|
||||
usedirt = false;
|
||||
} else if (entity && entity->dirt == 1) {
|
||||
usedirt = true;
|
||||
} else {
|
||||
usedirt = globalDirt;
|
||||
}
|
||||
|
||||
/* if not, quit */
|
||||
if (!usedirt)
|
||||
return 1.0;
|
||||
|
||||
/* override the global scale and gain values with the light-specific
|
||||
values, if present */
|
||||
if (entity) {
|
||||
if (entity->dirtgain)
|
||||
light_dirtgain = entity->dirtgain;
|
||||
if (entity->dirtscale)
|
||||
light_dirtscale = entity->dirtscale;
|
||||
}
|
||||
|
||||
/* early out */
|
||||
if ( occlusion <= 0.0f ) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/* apply gain (does this even do much? heh) */
|
||||
outDirt = pow( occlusion, light_dirtgain );
|
||||
if ( outDirt > 1.0f ) {
|
||||
outDirt = 1.0f;
|
||||
}
|
||||
|
||||
/* apply scale */
|
||||
outDirt *= light_dirtscale;
|
||||
if ( outDirt > 1.0f ) {
|
||||
outDirt = 1.0f;
|
||||
}
|
||||
|
||||
/* return to sender */
|
||||
return 1.0f - outDirt;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ================
|
||||
* LightFace_Entity
|
||||
|
|
@ -773,6 +842,8 @@ LightFace_Entity(const entity_t *entity, const lightsample_t *light,
|
|||
|
||||
angle = (1.0 - entity->anglescale) + entity->anglescale * angle;
|
||||
add = GetLightValue(light, entity, dist) * angle * spotscale;
|
||||
add *= Dirt_GetScaleFactor(lightsurf->occlusion[i], entity);
|
||||
|
||||
Light_Add(sample, add, light->color);
|
||||
|
||||
/* Check if we really hit, ignore tiny lights */
|
||||
|
|
@ -822,9 +893,13 @@ LightFace_Sky(const lightsample_t *light, const vec3_t vector,
|
|||
sample = lightmap->samples;
|
||||
surfpoint = lightsurf->points[0];
|
||||
for (i = 0; i < lightsurf->numpoints; i++, sample++, surfpoint += 3) {
|
||||
vec_t value;
|
||||
if (!TestSky(surfpoint, vector, shadowself))
|
||||
continue;
|
||||
Light_Add(sample, angle * light->light, light->color);
|
||||
value = angle * light->light;
|
||||
if (sunlightDirt)
|
||||
value *= Dirt_GetScaleFactor(lightsurf->occlusion[i], NULL);
|
||||
Light_Add(sample, value, light->color);
|
||||
if (!hit && sample->light >= 1)
|
||||
hit = true;
|
||||
}
|
||||
|
|
@ -857,10 +932,13 @@ LightFace_Min(const lightsample_t *light,
|
|||
hit = false;
|
||||
sample = lightmap->samples;
|
||||
for (i = 0; i < lightsurf->numpoints; i++, sample++) {
|
||||
vec_t value = light->light;
|
||||
if (minlightDirt)
|
||||
value *= Dirt_GetScaleFactor(lightsurf->occlusion[i], NULL);
|
||||
if (addminlight)
|
||||
Light_Add(sample, light->light, light->color);
|
||||
Light_Add(sample, value, light->color);
|
||||
else
|
||||
Light_ClampMin(sample, light->light, light->color);
|
||||
Light_ClampMin(sample, value, light->color);
|
||||
if (!hit && sample->light >= 1)
|
||||
hit = true;
|
||||
}
|
||||
|
|
@ -875,13 +953,15 @@ LightFace_Min(const lightsample_t *light,
|
|||
surfpoint = lightsurf->points[0];
|
||||
for (j = 0; j < lightsurf->numpoints; j++, sample++, surfpoint += 3) {
|
||||
if (addminlight || sample->light < entity->light.light) {
|
||||
vec_t value = entity->light.light;
|
||||
trace = TestLight(entity->origin, surfpoint, shadowself);
|
||||
if (!trace)
|
||||
continue;
|
||||
value *= Dirt_GetScaleFactor(lightsurf->occlusion[j], entity);
|
||||
if (addminlight)
|
||||
Light_Add(sample, entity->light.light, entity->light.color);
|
||||
Light_Add(sample, value, entity->light.color);
|
||||
else
|
||||
Light_ClampMin(sample, entity->light.light, entity->light.color);
|
||||
Light_ClampMin(sample, value, entity->light.color);
|
||||
}
|
||||
if (!hit && sample->light >= 1)
|
||||
hit = true;
|
||||
|
|
@ -892,6 +972,240 @@ LightFace_Min(const lightsample_t *light,
|
|||
Lightmap_Save(lightmaps, lightsurf, lightmap, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* =============
|
||||
* LightFace_DirtDebug
|
||||
* =============
|
||||
*/
|
||||
static void
|
||||
LightFace_DirtDebug(const lightsurf_t *lightsurf, lightmap_t *lightmaps)
|
||||
{
|
||||
int i;
|
||||
lightsample_t *sample;
|
||||
lightmap_t *lightmap;
|
||||
|
||||
/* use a style 0 light map */
|
||||
lightmap = Lightmap_ForStyle(lightmaps, 0);
|
||||
|
||||
/* Overwrite each point with the dirt value for that sample... */
|
||||
sample = lightmap->samples;
|
||||
for (i = 0; i < lightsurf->numpoints; i++, sample++) {
|
||||
sample->light = 255 * Dirt_GetScaleFactor(lightsurf->occlusion[i], NULL);
|
||||
VectorSet(sample->color, sample->light, sample->light, sample->light);
|
||||
}
|
||||
|
||||
Lightmap_Save(lightmaps, lightsurf, lightmap, 0);
|
||||
}
|
||||
|
||||
/* Dirtmapping borrowed from q3map2, originally by RaP7oR */
|
||||
|
||||
#define DIRT_CONE_ANGLE 88 /* degrees */
|
||||
#define DIRT_NUM_ANGLE_STEPS 16
|
||||
#define DIRT_NUM_ELEVATION_STEPS 3
|
||||
#define DIRT_NUM_VECTORS ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
|
||||
|
||||
static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
|
||||
static int numDirtVectors = 0;
|
||||
|
||||
/*
|
||||
* ============
|
||||
* SetupDirt
|
||||
*
|
||||
* sets up dirtmap (ambient occlusion)
|
||||
* ============
|
||||
*/
|
||||
void SetupDirt( void ) {
|
||||
int i, j;
|
||||
float angle, elevation, angleStep, elevationStep;
|
||||
|
||||
/* note it */
|
||||
logprint("--- SetupDirt ---\n" );
|
||||
|
||||
/* calculate angular steps */
|
||||
angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
|
||||
elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
|
||||
|
||||
/* iterate angle */
|
||||
angle = 0.0f;
|
||||
for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep ) {
|
||||
/* iterate elevation */
|
||||
for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep ) {
|
||||
dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
|
||||
dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
|
||||
dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
|
||||
numDirtVectors++;
|
||||
}
|
||||
}
|
||||
|
||||
/* emit some statistics */
|
||||
logprint("%9d dirtmap vectors\n", numDirtVectors );
|
||||
}
|
||||
|
||||
/*
|
||||
* ============
|
||||
* DirtTrace
|
||||
*
|
||||
* returns true if the trace from start to stop hits something solid,
|
||||
* or if it started in the void.
|
||||
* ============
|
||||
*/
|
||||
qboolean
|
||||
DirtTrace(const vec3_t start, const vec3_t stop, const dmodel_t *self, vec3_t hitpoint_out)
|
||||
{
|
||||
const dmodel_t *const *model;
|
||||
const int traceflags = TRACE_HIT_SOLID | TRACE_HIT_SKY;
|
||||
int result = TRACE_HIT_NONE;
|
||||
tracepoint_t hitpoint;
|
||||
|
||||
if (self) {
|
||||
result = TraceLine(self, traceflags, start, stop, &hitpoint);
|
||||
if (result == -TRACE_HIT_SOLID) {
|
||||
/* We started in the void, which ideally wouldn't happen,
|
||||
but does (say on e1m1). Return the start point as the hitpoint,
|
||||
which will make fully black dirt.
|
||||
*/
|
||||
VectorCopy(start, hitpoint_out);
|
||||
return true;
|
||||
} else if (result == TRACE_HIT_SOLID) {
|
||||
VectorCopy(hitpoint.point, hitpoint_out);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check against the list of global shadow casters */
|
||||
for (model = tracelist; *model; model++) {
|
||||
result = TraceLine(*model, traceflags, start, stop, &hitpoint);
|
||||
if (result == -TRACE_HIT_SOLID) {
|
||||
VectorCopy(start, hitpoint_out);
|
||||
return true;
|
||||
} else if (result == TRACE_HIT_SOLID) {
|
||||
VectorCopy(hitpoint.point, hitpoint_out);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* ============
|
||||
* DirtForSample
|
||||
* ============
|
||||
*/
|
||||
static vec_t
|
||||
DirtForSample(const dmodel_t *model, const vec3_t origin, const vec3_t normal){
|
||||
int i;
|
||||
float gatherDirt, angle, elevation, ooDepth;
|
||||
vec3_t worldUp, myUp, myRt, temp, direction, displacement;
|
||||
vec3_t traceEnd, traceHitpoint;
|
||||
|
||||
/* dummy check */
|
||||
if ( !dirty ) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/* setup */
|
||||
gatherDirt = 0.0f;
|
||||
ooDepth = 1.0f / dirtDepth;
|
||||
|
||||
/* check if the normal is aligned to the world-up */
|
||||
if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f ) {
|
||||
if ( normal[ 2 ] == 1.0f ) {
|
||||
VectorSet( myRt, 1.0f, 0.0f, 0.0f );
|
||||
VectorSet( myUp, 0.0f, 1.0f, 0.0f );
|
||||
} else if ( normal[ 2 ] == -1.0f ) {
|
||||
VectorSet( myRt, -1.0f, 0.0f, 0.0f );
|
||||
VectorSet( myUp, 0.0f, 1.0f, 0.0f );
|
||||
}
|
||||
} else {
|
||||
VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
|
||||
CrossProduct( normal, worldUp, myRt );
|
||||
VectorNormalize( myRt );
|
||||
CrossProduct( myRt, normal, myUp );
|
||||
VectorNormalize( myUp );
|
||||
}
|
||||
|
||||
/* 1 = random mode, 0 (well everything else) = non-random mode */
|
||||
if ( dirtMode == 1 ) {
|
||||
/* iterate */
|
||||
for ( i = 0; i < numDirtVectors; i++ ) {
|
||||
/* get random vector */
|
||||
angle = Random() * DEG2RAD( 360.0f );
|
||||
elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
|
||||
temp[ 0 ] = cos( angle ) * sin( elevation );
|
||||
temp[ 1 ] = sin( angle ) * sin( elevation );
|
||||
temp[ 2 ] = cos( elevation );
|
||||
|
||||
/* transform into tangent space */
|
||||
direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
|
||||
direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
|
||||
direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
|
||||
|
||||
/* set endpoint */
|
||||
VectorMA( origin, dirtDepth, direction, traceEnd );
|
||||
|
||||
/* trace */
|
||||
if (DirtTrace(origin, traceEnd, model, traceHitpoint)) {
|
||||
VectorSubtract( traceHitpoint, origin, displacement );
|
||||
gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* iterate through ordered vectors */
|
||||
for ( i = 0; i < numDirtVectors; i++ ) {
|
||||
/* transform vector into tangent space */
|
||||
direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
|
||||
direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
|
||||
direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
|
||||
|
||||
/* set endpoint */
|
||||
VectorMA( origin, dirtDepth, direction, traceEnd );
|
||||
|
||||
/* trace */
|
||||
if (DirtTrace(origin, traceEnd, model, traceHitpoint)) {
|
||||
VectorSubtract( traceHitpoint, origin, displacement );
|
||||
gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* direct ray */
|
||||
VectorMA( origin, dirtDepth, normal, traceEnd );
|
||||
|
||||
/* trace */
|
||||
if (DirtTrace(origin, traceEnd, model, traceHitpoint)) {
|
||||
VectorSubtract( traceHitpoint, origin, displacement );
|
||||
gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
|
||||
}
|
||||
|
||||
/* save gatherDirt, the rest of the scaling of the dirt value is done
|
||||
per-light */
|
||||
|
||||
return gatherDirt / ( numDirtVectors + 1 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ============
|
||||
* LightFace_CalculateDirt
|
||||
* ============
|
||||
*/
|
||||
static void
|
||||
LightFace_CalculateDirt(lightsurf_t *lightsurf)
|
||||
{
|
||||
const modelinfo_t *modelinfo = lightsurf->modelinfo;
|
||||
const plane_t *plane = &lightsurf->plane;
|
||||
const vec_t *surfpoint;
|
||||
int i;
|
||||
|
||||
/* Check each point... */
|
||||
surfpoint = lightsurf->points[0];
|
||||
for (i = 0; i < lightsurf->numpoints; i++, surfpoint += 3) {
|
||||
lightsurf->occlusion[i] = DirtForSample(modelinfo->model, surfpoint, plane->normal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
WriteLightmaps(bsp2_dface_t *face, const lightsurf_t *lightsurf,
|
||||
const lightmap_t *lightmaps)
|
||||
|
|
@ -992,6 +1306,10 @@ LightFace(bsp2_dface_t *face, const modelinfo_t *modelinfo,
|
|||
Lightsurf_Init(modelinfo, face, bsp, &lightsurf);
|
||||
Lightmaps_Init(lightmaps, MAXLIGHTMAPS + 1);
|
||||
|
||||
/* calculate dirt (ambient occlusion) but don't use it yet */
|
||||
if (dirty)
|
||||
LightFace_CalculateDirt(&lightsurf);
|
||||
|
||||
/*
|
||||
* The lighting procedure is: cast all positive lights, fix
|
||||
* minlight levels, then cast all negative lights. Finally, we
|
||||
|
|
@ -1024,6 +1342,10 @@ LightFace(bsp2_dface_t *face, const modelinfo_t *modelinfo,
|
|||
if (sunlight.light < 0)
|
||||
LightFace_Sky(&sunlight, sunvec, &lightsurf, lightmaps);
|
||||
|
||||
/* replace lightmaps with AO for debugging */
|
||||
if (dirtDebug)
|
||||
LightFace_DirtDebug(&lightsurf, lightmaps);
|
||||
|
||||
/* Fix any negative values */
|
||||
for (i = 0; i < MAXLIGHTMAPS; i++) {
|
||||
if (lightmaps[i].style == 255)
|
||||
|
|
|
|||
45
man/light.1
45
man/light.1
|
|
@ -62,6 +62,15 @@ surface (more detailed explanation in the "_anglescale" key below).
|
|||
Force generation of a .lit file, even if your map does not have any coloured
|
||||
lights. By default, light will automatically generate the .lit file when
|
||||
needed.
|
||||
.IP "\fB\-dirty\fP"
|
||||
Globally enables dirtmapping (ambient occlusion), overriding the "_dirt" worldspawn
|
||||
key. See "_dirt" for more details.
|
||||
.IP "\fB\-dirtdebug\fP"
|
||||
Implies "-dirty", and renders just the dirtmap against a fullbright background,
|
||||
ignoring all lights in the map. Useful for previewing and turning the dirt settings.
|
||||
.IP "\fB\-dirtmode n\fP | \fB\-dirtdepth n\fP | \fB\-dirtscale n\fP | \fB\-dirtgain n\fP"
|
||||
Fine-tune the dirtmapping, overriding the corresponding worldspawn keys. See the
|
||||
worldspawn keys below.
|
||||
|
||||
.SH "MODEL ENTITY KEYS"
|
||||
|
||||
|
|
@ -93,6 +102,33 @@ sunlight. RGB component values are between 0 and 255 (between 0 and 1 is also
|
|||
accepted). Default is white light
|
||||
("255 255 255").
|
||||
|
||||
.IP "\fB""_dirt"" ""n""\fP"
|
||||
1 enables dirtmapping (ambient occlusion) on all lights, borrowed from q3map2. This adds shadows
|
||||
to corners and crevices. You can override the global setting for specific lights with the
|
||||
"_dirt" light entitiy key or "_sunlight_nodirt" and "_minlight_nodirt" worldspawn keys.
|
||||
Default is no dirtmapping (-1).
|
||||
|
||||
.IP "\fB""_sunlight_dirt"" ""n""\fP"
|
||||
1 enables dirtmapping (ambient occlusion) on sunlight, -1 to disable (making it illuminate the dirtmapping shadows). Default is to use the value of "_dirt".
|
||||
|
||||
.IP "\fB""_minlight_dirt"" ""n""\fP"
|
||||
1 enables dirtmapping (ambient occlusion) on minlight, -1 to disable. Default is to use the value of "_dirt".
|
||||
|
||||
.IP "\fB""_dirtmode"" ""n""\fP"
|
||||
Choose between ordered (0, default) and randomized (1) dirtmapping.
|
||||
|
||||
.IP "\fB""_dirtdepth"" ""n""\fP"
|
||||
Maximum depth of occlusion checking for dirtmapping, default 128.
|
||||
|
||||
.IP "\fB""_dirtscale"" ""n""\fP"
|
||||
Scale factor used in dirt calculations, default 1. Lower values (e.g. 0.5) make
|
||||
the dirt fainter, 2.0 would create much darker shadows.
|
||||
|
||||
.IP "\fB""_dirtgain"" ""n""\fP"
|
||||
Exponent used in dirt calculation, default 1. Lower values (e.g. 0.5) make the
|
||||
shadows darker and stretch further away from corners.
|
||||
|
||||
|
||||
.SS "Model Entity Keys"
|
||||
|
||||
.PP
|
||||
|
|
@ -188,6 +224,15 @@ on a surface has on the brightness of the surface. \fIn\fP must be between 0.0
|
|||
and 1.0. Smaller values mean less attenuation, with zero meaning that angle of
|
||||
incidence has no effect at all on the brightness. Default 0.5.
|
||||
|
||||
.IP "\fB""_dirtscale"" ""n""\fP | \fB""_dirtgain"" ""n""\fP"
|
||||
Override the global "_dirtscale" or "_dirtgain" settings to change how this
|
||||
light is affected by dirtmapping (ambient occlusion). See descriptions of these
|
||||
keys in the worldspawn section.
|
||||
|
||||
.IP "\fB""_dirt"" ""n""\fP"
|
||||
Overrides the worldspawn setting of "_dirt" for this particular light. -1 to disable dirtmapping (ambient occlusion) for this light, making it illuminate the dirtmapping shadows. 1 to enable ambient occlusion for this light. Default is to defer to the worldspawn setting.
|
||||
|
||||
|
||||
.SH AUTHOR
|
||||
Written by Kevin Shanahan (aka Tyrann)
|
||||
.br
|
||||
|
|
|
|||
Loading…
Reference in New Issue