mirror of
https://github.com/TTimo/GtkRadiant.git
synced 2024-11-10 07:11:54 +00:00
b388770c65
former commit name: initial this commit only backports changes from tools/
2391 lines
60 KiB
C
2391 lines
60 KiB
C
/* -------------------------------------------------------------------------------
|
|
|
|
Copyright (C) 1999-2007 id Software, Inc. and contributors.
|
|
For a list of contributors, see the accompanying CONTRIBUTORS file.
|
|
|
|
This file is part of GtkRadiant.
|
|
|
|
GtkRadiant is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
GtkRadiant is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GtkRadiant; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
----------------------------------------------------------------------------------
|
|
|
|
This code has been altered significantly from its original form, to support
|
|
several games based on the Quake III Arena engine, in the form of "Q3Map2."
|
|
|
|
------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
/* marker */
|
|
#define LIGHT_C
|
|
|
|
|
|
|
|
/* dependencies */
|
|
#include "q3map2.h"
|
|
|
|
|
|
|
|
/*
|
|
CreateSunLight() - ydnar
|
|
this creates a sun light
|
|
*/
|
|
|
|
static void CreateSunLight( sun_t *sun ){
|
|
int i;
|
|
float photons, d, angle, elevation, da, de;
|
|
vec3_t direction;
|
|
light_t *light;
|
|
|
|
|
|
/* dummy check */
|
|
if ( sun == NULL ) {
|
|
return;
|
|
}
|
|
|
|
/* fixup */
|
|
if ( sun->numSamples < 1 ) {
|
|
sun->numSamples = 1;
|
|
}
|
|
|
|
/* set photons */
|
|
photons = sun->photons / sun->numSamples;
|
|
|
|
/* create the right number of suns */
|
|
for ( i = 0; i < sun->numSamples; i++ )
|
|
{
|
|
/* calculate sun direction */
|
|
if ( i == 0 ) {
|
|
VectorCopy( sun->direction, direction );
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
sun->direction[ 0 ] = cos( angle ) * cos( elevation );
|
|
sun->direction[ 1 ] = sin( angle ) * cos( elevation );
|
|
sun->direction[ 2 ] = sin( elevation );
|
|
|
|
xz_dist = sqrt( x*x + z*z )
|
|
latitude = atan2( xz_dist, y ) * RADIANS
|
|
longitude = atan2( x, z ) * RADIANS
|
|
*/
|
|
|
|
d = sqrt( sun->direction[ 0 ] * sun->direction[ 0 ] + sun->direction[ 1 ] * sun->direction[ 1 ] );
|
|
angle = atan2( sun->direction[ 1 ], sun->direction[ 0 ] );
|
|
elevation = atan2( sun->direction[ 2 ], d );
|
|
|
|
/* jitter the angles (loop to keep random sample within sun->deviance steridians) */
|
|
do
|
|
{
|
|
da = ( Random() * 2.0f - 1.0f ) * sun->deviance;
|
|
de = ( Random() * 2.0f - 1.0f ) * sun->deviance;
|
|
}
|
|
while ( ( da * da + de * de ) > ( sun->deviance * sun->deviance ) );
|
|
angle += da;
|
|
elevation += de;
|
|
|
|
/* debug code */
|
|
//% Sys_Printf( "%d: Angle: %3.4f Elevation: %3.3f\n", sun->numSamples, (angle / Q_PI * 180.0f), (elevation / Q_PI * 180.0f) );
|
|
|
|
/* create new vector */
|
|
direction[ 0 ] = cos( angle ) * cos( elevation );
|
|
direction[ 1 ] = sin( angle ) * cos( elevation );
|
|
direction[ 2 ] = sin( elevation );
|
|
}
|
|
|
|
/* create a light */
|
|
numSunLights++;
|
|
light = safe_malloc( sizeof( *light ) );
|
|
memset( light, 0, sizeof( *light ) );
|
|
light->next = lights;
|
|
lights = light;
|
|
|
|
/* initialize the light */
|
|
light->flags = LIGHT_SUN_DEFAULT;
|
|
light->type = EMIT_SUN;
|
|
light->fade = 1.0f;
|
|
light->falloffTolerance = falloffTolerance;
|
|
light->filterRadius = sun->filterRadius / sun->numSamples;
|
|
light->style = noStyles ? LS_NORMAL : sun->style;
|
|
|
|
/* set the light's position out to infinity */
|
|
VectorMA( vec3_origin, ( MAX_WORLD_COORD * 8.0f ), direction, light->origin ); /* MAX_WORLD_COORD * 2.0f */
|
|
|
|
/* set the facing to be the inverse of the sun direction */
|
|
VectorScale( direction, -1.0, light->normal );
|
|
light->dist = DotProduct( light->origin, light->normal );
|
|
|
|
/* set color and photons */
|
|
VectorCopy( sun->color, light->color );
|
|
light->photons = photons * skyScale;
|
|
}
|
|
|
|
/* another sun? */
|
|
if ( sun->next != NULL ) {
|
|
CreateSunLight( sun->next );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
CreateSkyLights() - ydnar
|
|
simulates sky light with multiple suns
|
|
*/
|
|
|
|
static void CreateSkyLights( vec3_t color, float value, int iterations, float filterRadius, int style ){
|
|
int i, j, numSuns;
|
|
int angleSteps, elevationSteps;
|
|
float angle, elevation;
|
|
float angleStep, elevationStep;
|
|
float step, start;
|
|
sun_t sun;
|
|
|
|
|
|
/* dummy check */
|
|
if ( value <= 0.0f || iterations < 2 ) {
|
|
return;
|
|
}
|
|
|
|
/* calculate some stuff */
|
|
step = 2.0f / ( iterations - 1 );
|
|
start = -1.0f;
|
|
|
|
/* basic sun setup */
|
|
VectorCopy( color, sun.color );
|
|
sun.deviance = 0.0f;
|
|
sun.filterRadius = filterRadius;
|
|
sun.numSamples = 1;
|
|
sun.style = noStyles ? LS_NORMAL : style;
|
|
sun.next = NULL;
|
|
|
|
/* setup */
|
|
elevationSteps = iterations - 1;
|
|
angleSteps = elevationSteps * 4;
|
|
angle = 0.0f;
|
|
elevationStep = DEG2RAD( 90.0f / iterations ); /* skip elevation 0 */
|
|
angleStep = DEG2RAD( 360.0f / angleSteps );
|
|
|
|
/* calc individual sun brightness */
|
|
numSuns = angleSteps * elevationSteps + 1;
|
|
sun.photons = value / numSuns;
|
|
|
|
/* iterate elevation */
|
|
elevation = elevationStep * 0.5f;
|
|
angle = 0.0f;
|
|
for ( i = 0, elevation = elevationStep * 0.5f; i < elevationSteps; i++ )
|
|
{
|
|
/* iterate angle */
|
|
for ( j = 0; j < angleSteps; j++ )
|
|
{
|
|
/* create sun */
|
|
sun.direction[ 0 ] = cos( angle ) * cos( elevation );
|
|
sun.direction[ 1 ] = sin( angle ) * cos( elevation );
|
|
sun.direction[ 2 ] = sin( elevation );
|
|
CreateSunLight( &sun );
|
|
|
|
/* move */
|
|
angle += angleStep;
|
|
}
|
|
|
|
/* move */
|
|
elevation += elevationStep;
|
|
angle += angleStep / elevationSteps;
|
|
}
|
|
|
|
/* create vertical sun */
|
|
VectorSet( sun.direction, 0.0f, 0.0f, 1.0f );
|
|
CreateSunLight( &sun );
|
|
|
|
/* short circuit */
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
CreateEntityLights()
|
|
creates lights from light entities
|
|
*/
|
|
|
|
void CreateEntityLights( void ){
|
|
int i, j;
|
|
light_t *light, *light2;
|
|
entity_t *e, *e2;
|
|
const char *name;
|
|
const char *target;
|
|
const char *noradiosity;
|
|
vec3_t dest;
|
|
const char *_color;
|
|
float intensity, scale, deviance, filterRadius;
|
|
int spawnflags, flags, numSamples;
|
|
qboolean junior;
|
|
|
|
|
|
/* go throught entity list and find lights */
|
|
for ( i = 0; i < numEntities; i++ )
|
|
{
|
|
/* get entity */
|
|
e = &entities[ i ];
|
|
name = ValueForKey( e, "classname" );
|
|
|
|
/* ydnar: check for lightJunior */
|
|
if ( Q_strncasecmp( name, "lightJunior", 11 ) == 0 ) {
|
|
junior = qtrue;
|
|
}
|
|
else if ( Q_strncasecmp( name, "light", 5 ) == 0 ) {
|
|
junior = qfalse;
|
|
}
|
|
else{
|
|
continue;
|
|
}
|
|
|
|
/* neumond: skip dynamic lights */
|
|
noradiosity = ValueForKey( e, "noradiosity" );
|
|
if ( noradiosity[ 0 ] == '1' ) {
|
|
continue;
|
|
}
|
|
|
|
/* lights with target names (and therefore styles) are only parsed from BSP */
|
|
target = ValueForKey( e, "targetname" );
|
|
if ( target[ 0 ] != '\0' && i >= numBSPEntities ) {
|
|
continue;
|
|
}
|
|
|
|
/* create a light */
|
|
numPointLights++;
|
|
light = safe_malloc( sizeof( *light ) );
|
|
memset( light, 0, sizeof( *light ) );
|
|
light->next = lights;
|
|
lights = light;
|
|
|
|
/* handle spawnflags */
|
|
spawnflags = IntForKey( e, "spawnflags" );
|
|
|
|
/* ydnar: quake 3+ light behavior */
|
|
if ( wolfLight == qfalse ) {
|
|
/* set default flags */
|
|
flags = LIGHT_Q3A_DEFAULT;
|
|
|
|
/* linear attenuation? */
|
|
if ( spawnflags & 1 ) {
|
|
flags |= LIGHT_ATTEN_LINEAR;
|
|
flags &= ~LIGHT_ATTEN_ANGLE;
|
|
}
|
|
|
|
/* no angle attenuate? */
|
|
if ( spawnflags & 2 ) {
|
|
flags &= ~LIGHT_ATTEN_ANGLE;
|
|
}
|
|
}
|
|
|
|
/* ydnar: wolf light behavior */
|
|
else
|
|
{
|
|
/* set default flags */
|
|
flags = LIGHT_WOLF_DEFAULT;
|
|
|
|
/* inverse distance squared attenuation? */
|
|
if ( spawnflags & 1 ) {
|
|
flags &= ~LIGHT_ATTEN_LINEAR;
|
|
flags |= LIGHT_ATTEN_ANGLE;
|
|
}
|
|
|
|
/* angle attenuate? */
|
|
if ( spawnflags & 2 ) {
|
|
flags |= LIGHT_ATTEN_ANGLE;
|
|
}
|
|
}
|
|
|
|
/* other flags (borrowed from wolf) */
|
|
|
|
/* wolf dark light? */
|
|
if ( ( spawnflags & 4 ) || ( spawnflags & 8 ) ) {
|
|
flags |= LIGHT_DARK;
|
|
}
|
|
|
|
/* nogrid? */
|
|
if ( spawnflags & 16 ) {
|
|
flags &= ~LIGHT_GRID;
|
|
}
|
|
|
|
/* junior? */
|
|
if ( junior ) {
|
|
flags |= LIGHT_GRID;
|
|
flags &= ~LIGHT_SURFACES;
|
|
}
|
|
|
|
/* store the flags */
|
|
light->flags = flags;
|
|
|
|
/* ydnar: set fade key (from wolf) */
|
|
light->fade = 1.0f;
|
|
if ( light->flags & LIGHT_ATTEN_LINEAR ) {
|
|
light->fade = FloatForKey( e, "fade" );
|
|
if ( light->fade == 0.0f ) {
|
|
light->fade = 1.0f;
|
|
}
|
|
}
|
|
|
|
/* ydnar: set angle scaling (from vlight) */
|
|
light->angleScale = FloatForKey( e, "_anglescale" );
|
|
if ( light->angleScale != 0.0f ) {
|
|
light->flags |= LIGHT_ATTEN_ANGLE;
|
|
}
|
|
|
|
/* set origin */
|
|
GetVectorForKey( e, "origin", light->origin );
|
|
light->style = IntForKey( e, "_style" );
|
|
if ( light->style == LS_NORMAL ) {
|
|
light->style = IntForKey( e, "style" );
|
|
}
|
|
if ( light->style < LS_NORMAL || light->style >= LS_NONE ) {
|
|
Error( "Invalid lightstyle (%d) on entity %d", light->style, i );
|
|
}
|
|
|
|
if ( light->style != LS_NORMAL ) {
|
|
Sys_FPrintf( SYS_WRN, "WARNING: Styled light found targeting %s\n **", target );
|
|
}
|
|
|
|
/* set light intensity */
|
|
intensity = FloatForKey( e, "_light" );
|
|
if ( intensity == 0.0f ) {
|
|
intensity = FloatForKey( e, "light" );
|
|
}
|
|
if ( intensity == 0.0f ) {
|
|
intensity = 300.0f;
|
|
}
|
|
|
|
/* ydnar: set light scale (sof2) */
|
|
scale = FloatForKey( e, "scale" );
|
|
if ( scale == 0.0f ) {
|
|
scale = 1.0f;
|
|
}
|
|
intensity *= scale;
|
|
|
|
/* ydnar: get deviance and samples */
|
|
deviance = FloatForKey( e, "_deviance" );
|
|
if ( deviance == 0.0f ) {
|
|
deviance = FloatForKey( e, "_deviation" );
|
|
}
|
|
if ( deviance == 0.0f ) {
|
|
deviance = FloatForKey( e, "_jitter" );
|
|
}
|
|
numSamples = IntForKey( e, "_samples" );
|
|
if ( deviance < 0.0f || numSamples < 1 ) {
|
|
deviance = 0.0f;
|
|
numSamples = 1;
|
|
}
|
|
intensity /= numSamples;
|
|
|
|
/* ydnar: get filter radius */
|
|
filterRadius = FloatForKey( e, "_filterradius" );
|
|
if ( filterRadius == 0.0f ) {
|
|
filterRadius = FloatForKey( e, "_filteradius" );
|
|
}
|
|
if ( filterRadius == 0.0f ) {
|
|
filterRadius = FloatForKey( e, "_filter" );
|
|
}
|
|
if ( filterRadius < 0.0f ) {
|
|
filterRadius = 0.0f;
|
|
}
|
|
light->filterRadius = filterRadius;
|
|
|
|
/* set light color */
|
|
_color = ValueForKey( e, "_color" );
|
|
if ( _color && _color[ 0 ] ) {
|
|
sscanf( _color, "%f %f %f", &light->color[ 0 ], &light->color[ 1 ], &light->color[ 2 ] );
|
|
ColorNormalize( light->color, light->color );
|
|
}
|
|
else{
|
|
light->color[ 0 ] = light->color[ 1 ] = light->color[ 2 ] = 1.0f;
|
|
}
|
|
|
|
intensity = intensity * pointScale;
|
|
light->photons = intensity;
|
|
|
|
light->type = EMIT_POINT;
|
|
|
|
/* set falloff threshold */
|
|
light->falloffTolerance = falloffTolerance / numSamples;
|
|
|
|
/* lights with a target will be spotlights */
|
|
target = ValueForKey( e, "target" );
|
|
if ( target[ 0 ] ) {
|
|
float radius;
|
|
float dist;
|
|
sun_t sun;
|
|
const char *_sun;
|
|
|
|
|
|
/* get target */
|
|
e2 = FindTargetEntity( target );
|
|
if ( e2 == NULL ) {
|
|
Sys_FPrintf( SYS_WRN, "WARNING: light at (%i %i %i) has missing target\n",
|
|
(int) light->origin[ 0 ], (int) light->origin[ 1 ], (int) light->origin[ 2 ] );
|
|
}
|
|
else
|
|
{
|
|
/* not a point light */
|
|
numPointLights--;
|
|
numSpotLights++;
|
|
|
|
/* make a spotlight */
|
|
GetVectorForKey( e2, "origin", dest );
|
|
VectorSubtract( dest, light->origin, light->normal );
|
|
dist = VectorNormalize( light->normal, light->normal );
|
|
radius = FloatForKey( e, "radius" );
|
|
if ( !radius ) {
|
|
radius = 64;
|
|
}
|
|
if ( !dist ) {
|
|
dist = 64;
|
|
}
|
|
light->radiusByDist = ( radius + 16 ) / dist;
|
|
light->type = EMIT_SPOT;
|
|
|
|
/* ydnar: wolf mods: spotlights always use nonlinear + angle attenuation */
|
|
light->flags &= ~LIGHT_ATTEN_LINEAR;
|
|
light->flags |= LIGHT_ATTEN_ANGLE;
|
|
light->fade = 1.0f;
|
|
|
|
/* ydnar: is this a sun? */
|
|
_sun = ValueForKey( e, "_sun" );
|
|
if ( _sun[ 0 ] == '1' ) {
|
|
/* not a spot light */
|
|
numSpotLights--;
|
|
|
|
/* unlink this light */
|
|
lights = light->next;
|
|
|
|
/* make a sun */
|
|
VectorScale( light->normal, -1.0f, sun.direction );
|
|
VectorCopy( light->color, sun.color );
|
|
sun.photons = ( intensity / pointScale );
|
|
sun.deviance = deviance / 180.0f * Q_PI;
|
|
sun.numSamples = numSamples;
|
|
sun.style = noStyles ? LS_NORMAL : light->style;
|
|
sun.next = NULL;
|
|
|
|
/* make a sun light */
|
|
CreateSunLight( &sun );
|
|
|
|
/* free original light */
|
|
free( light );
|
|
light = NULL;
|
|
|
|
/* skip the rest of this love story */
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* jitter the light */
|
|
for ( j = 1; j < numSamples; j++ )
|
|
{
|
|
/* create a light */
|
|
light2 = safe_malloc( sizeof( *light ) );
|
|
memcpy( light2, light, sizeof( *light ) );
|
|
light2->next = lights;
|
|
lights = light2;
|
|
|
|
/* add to counts */
|
|
if ( light->type == EMIT_SPOT ) {
|
|
numSpotLights++;
|
|
}
|
|
else{
|
|
numPointLights++;
|
|
}
|
|
|
|
/* jitter it */
|
|
light2->origin[ 0 ] = light->origin[ 0 ] + ( Random() * 2.0f - 1.0f ) * deviance;
|
|
light2->origin[ 1 ] = light->origin[ 1 ] + ( Random() * 2.0f - 1.0f ) * deviance;
|
|
light2->origin[ 2 ] = light->origin[ 2 ] + ( Random() * 2.0f - 1.0f ) * deviance;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
CreateSurfaceLights() - ydnar
|
|
this hijacks the radiosity code to generate surface lights for first pass
|
|
*/
|
|
|
|
#define APPROX_BOUNCE 1.0f
|
|
|
|
void CreateSurfaceLights( void ){
|
|
int i;
|
|
bspDrawSurface_t *ds;
|
|
surfaceInfo_t *info;
|
|
shaderInfo_t *si;
|
|
light_t *light;
|
|
float subdivide;
|
|
vec3_t origin;
|
|
clipWork_t cw;
|
|
const char *nss;
|
|
|
|
|
|
/* get sun shader supressor */
|
|
nss = ValueForKey( &entities[ 0 ], "_noshadersun" );
|
|
|
|
/* walk the list of surfaces */
|
|
for ( i = 0; i < numBSPDrawSurfaces; i++ )
|
|
{
|
|
/* get surface and other bits */
|
|
ds = &bspDrawSurfaces[ i ];
|
|
info = &surfaceInfos[ i ];
|
|
si = info->si;
|
|
|
|
/* sunlight? */
|
|
if ( si->sun != NULL && nss[ 0 ] != '1' ) {
|
|
Sys_FPrintf( SYS_VRB, "Sun: %s\n", si->shader );
|
|
CreateSunLight( si->sun );
|
|
si->sun = NULL; /* FIXME: leak! */
|
|
}
|
|
|
|
/* sky light? */
|
|
if ( si->skyLightValue > 0.0f ) {
|
|
Sys_FPrintf( SYS_VRB, "Sky: %s\n", si->shader );
|
|
CreateSkyLights( si->color, si->skyLightValue, si->skyLightIterations, si->lightFilterRadius, si->lightStyle );
|
|
si->skyLightValue = 0.0f; /* FIXME: hack! */
|
|
}
|
|
|
|
/* try to early out */
|
|
if ( si->value <= 0 ) {
|
|
continue;
|
|
}
|
|
|
|
/* autosprite shaders become point lights */
|
|
if ( si->autosprite ) {
|
|
/* create an average xyz */
|
|
VectorAdd( info->mins, info->maxs, origin );
|
|
VectorScale( origin, 0.5f, origin );
|
|
|
|
/* create a light */
|
|
light = safe_malloc( sizeof( *light ) );
|
|
memset( light, 0, sizeof( *light ) );
|
|
light->next = lights;
|
|
lights = light;
|
|
|
|
/* set it up */
|
|
light->flags = LIGHT_Q3A_DEFAULT;
|
|
light->type = EMIT_POINT;
|
|
light->photons = si->value * pointScale;
|
|
light->fade = 1.0f;
|
|
light->si = si;
|
|
VectorCopy( origin, light->origin );
|
|
VectorCopy( si->color, light->color );
|
|
light->falloffTolerance = falloffTolerance;
|
|
light->style = si->lightStyle;
|
|
|
|
/* add to point light count and continue */
|
|
numPointLights++;
|
|
continue;
|
|
}
|
|
|
|
/* get subdivision amount */
|
|
if ( si->lightSubdivide > 0 ) {
|
|
subdivide = si->lightSubdivide;
|
|
}
|
|
else{
|
|
subdivide = defaultLightSubdivide;
|
|
}
|
|
|
|
/* switch on type */
|
|
switch ( ds->surfaceType )
|
|
{
|
|
case MST_PLANAR:
|
|
case MST_TRIANGLE_SOUP:
|
|
RadLightForTriangles( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
|
|
break;
|
|
|
|
case MST_PATCH:
|
|
RadLightForPatch( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
SetEntityOrigins()
|
|
find the offset values for inline models
|
|
*/
|
|
|
|
void SetEntityOrigins( void ){
|
|
int i, j, k, f;
|
|
entity_t *e;
|
|
vec3_t origin;
|
|
const char *key;
|
|
int modelnum;
|
|
bspModel_t *dm;
|
|
bspDrawSurface_t *ds;
|
|
|
|
|
|
/* ydnar: copy drawverts into private storage for nefarious purposes */
|
|
yDrawVerts = safe_malloc( numBSPDrawVerts * sizeof( bspDrawVert_t ) );
|
|
memcpy( yDrawVerts, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVert_t ) );
|
|
|
|
/* set the entity origins */
|
|
for ( i = 0; i < numEntities; i++ )
|
|
{
|
|
/* get entity and model */
|
|
e = &entities[ i ];
|
|
key = ValueForKey( e, "model" );
|
|
if ( key[ 0 ] != '*' ) {
|
|
continue;
|
|
}
|
|
modelnum = atoi( key + 1 );
|
|
dm = &bspModels[ modelnum ];
|
|
|
|
/* get entity origin */
|
|
key = ValueForKey( e, "origin" );
|
|
if ( key[ 0 ] == '\0' ) {
|
|
continue;
|
|
}
|
|
GetVectorForKey( e, "origin", origin );
|
|
|
|
/* set origin for all surfaces for this model */
|
|
for ( j = 0; j < dm->numBSPSurfaces; j++ )
|
|
{
|
|
/* get drawsurf */
|
|
ds = &bspDrawSurfaces[ dm->firstBSPSurface + j ];
|
|
|
|
/* set its verts */
|
|
for ( k = 0; k < ds->numVerts; k++ )
|
|
{
|
|
f = ds->firstVert + k;
|
|
VectorAdd( origin, bspDrawVerts[ f ].xyz, yDrawVerts[ f ].xyz );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
PointToPolygonFormFactor()
|
|
calculates the area over a point/normal hemisphere a winding covers
|
|
ydnar: fixme: there has to be a faster way to calculate this
|
|
without the expensive per-vert sqrts and transcendental functions
|
|
ydnar 2002-09-30: added -faster switch because only 19% deviance > 10%
|
|
between this and the approximation
|
|
*/
|
|
|
|
#define ONE_OVER_2PI 0.159154942f //% (1.0f / (2.0f * 3.141592657f))
|
|
|
|
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;
|
|
|
|
|
|
/* this is expensive */
|
|
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 ] );
|
|
|
|
/* calculcate relative area */
|
|
total = 0.0f;
|
|
for ( i = 0; i < w->numpoints; i++ )
|
|
{
|
|
/* get a triangle */
|
|
j = i + 1;
|
|
dot = DotProduct( dirs[ i ], dirs[ j ] );
|
|
|
|
/* roundoff can cause slight creep, which gives an IND from acos */
|
|
if ( dot > 1.0f ) {
|
|
dot = 1.0f;
|
|
}
|
|
else if ( dot < -1.0f ) {
|
|
dot = -1.0f;
|
|
}
|
|
|
|
/* get the angle */
|
|
angle = acos( dot );
|
|
|
|
CrossProduct( dirs[ i ], dirs[ j ], triVector );
|
|
if ( VectorNormalize( triVector, triNormal ) < 0.0001f ) {
|
|
continue;
|
|
}
|
|
|
|
facing = DotProduct( normal, triNormal );
|
|
total += facing * angle;
|
|
|
|
/* ydnar: this was throwing too many errors with radiosity + crappy maps. ignoring it. */
|
|
if ( total > 6.3f || total < -6.3f ) {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
/* now in the range of 0 to 1 over the entire incoming hemisphere */
|
|
//% total /= (2.0f * 3.141592657f);
|
|
total *= ONE_OVER_2PI;
|
|
return total;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
LightContributionTosample()
|
|
determines the amount of light reaching a sample (luxel or vertex) from a given light
|
|
*/
|
|
|
|
int LightContributionToSample( trace_t *trace ){
|
|
light_t *light;
|
|
float angle;
|
|
float add;
|
|
float dist;
|
|
|
|
|
|
/* get light */
|
|
light = trace->light;
|
|
|
|
/* clear color */
|
|
VectorClear( trace->color );
|
|
|
|
/* ydnar: early out */
|
|
if ( !( light->flags & LIGHT_SURFACES ) || light->envelope <= 0.0f ) {
|
|
return 0;
|
|
}
|
|
|
|
/* do some culling checks */
|
|
if ( light->type != EMIT_SUN ) {
|
|
/* MrE: if the light is behind the surface */
|
|
if ( trace->twoSided == qfalse ) {
|
|
if ( DotProduct( light->origin, trace->normal ) - DotProduct( trace->origin, trace->normal ) < 0.0f ) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* ydnar: test pvs */
|
|
if ( !ClusterVisible( trace->cluster, light->cluster ) ) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* exact point to polygon form factor */
|
|
if ( light->type == EMIT_AREA ) {
|
|
float factor;
|
|
float d;
|
|
vec3_t pushedOrigin;
|
|
|
|
/* project sample point into light plane */
|
|
d = DotProduct( trace->origin, light->normal ) - light->dist;
|
|
if ( d < 3.0f ) {
|
|
/* sample point behind plane? */
|
|
if ( !( light->flags & LIGHT_TWOSIDED ) && d < -1.0f ) {
|
|
return 0;
|
|
}
|
|
|
|
/* sample plane coincident? */
|
|
if ( d > -3.0f && DotProduct( trace->normal, light->normal ) > 0.9f ) {
|
|
return 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.0f && d < 8.0f ) {
|
|
VectorMA( trace->origin, ( 8.0f - d ), light->normal, pushedOrigin );
|
|
}
|
|
else{
|
|
VectorCopy( trace->origin, pushedOrigin );
|
|
}
|
|
|
|
/* get direction and distance */
|
|
VectorCopy( light->origin, trace->end );
|
|
dist = SetupTrace( trace );
|
|
if ( dist >= light->envelope ) {
|
|
return 0;
|
|
}
|
|
|
|
/* ptpff approximation */
|
|
if ( faster ) {
|
|
/* angle attenuation */
|
|
angle = DotProduct( trace->normal, trace->direction );
|
|
|
|
/* twosided lighting */
|
|
if ( trace->twoSided ) {
|
|
angle = fabs( angle );
|
|
}
|
|
|
|
/* attenuate */
|
|
angle *= -DotProduct( light->normal, trace->direction );
|
|
if ( angle == 0.0f ) {
|
|
return 0;
|
|
}
|
|
else if ( angle < 0.0f &&
|
|
( trace->twoSided || ( light->flags & LIGHT_TWOSIDED ) ) ) {
|
|
angle = -angle;
|
|
}
|
|
add = light->photons / ( dist * dist ) * angle;
|
|
}
|
|
else
|
|
{
|
|
/* calculate the contribution */
|
|
factor = PointToPolygonFormFactor( pushedOrigin, trace->normal, light->w );
|
|
if ( factor == 0.0f ) {
|
|
return 0;
|
|
}
|
|
else if ( factor < 0.0f ) {
|
|
/* twosided lighting */
|
|
if ( trace->twoSided || ( light->flags & LIGHT_TWOSIDED ) ) {
|
|
factor = -factor;
|
|
|
|
/* push light origin to other side of the plane */
|
|
VectorMA( light->origin, -2.0f, light->normal, trace->end );
|
|
dist = SetupTrace( trace );
|
|
if ( dist >= light->envelope ) {
|
|
return 0;
|
|
}
|
|
}
|
|
else{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* ydnar: moved to here */
|
|
add = factor * light->add;
|
|
}
|
|
}
|
|
|
|
/* point/spot lights */
|
|
else if ( light->type == EMIT_POINT || light->type == EMIT_SPOT ) {
|
|
/* get direction and distance */
|
|
VectorCopy( light->origin, trace->end );
|
|
dist = SetupTrace( trace );
|
|
if ( dist >= light->envelope ) {
|
|
return 0;
|
|
}
|
|
|
|
/* clamp the distance to prevent super hot spots */
|
|
if ( dist < 16.0f ) {
|
|
dist = 16.0f;
|
|
}
|
|
|
|
/* angle attenuation */
|
|
angle = ( light->flags & LIGHT_ATTEN_ANGLE ) ? DotProduct( trace->normal, trace->direction ) : 1.0f;
|
|
if ( light->angleScale != 0.0f ) {
|
|
angle /= light->angleScale;
|
|
if ( angle > 1.0f ) {
|
|
angle = 1.0f;
|
|
}
|
|
}
|
|
|
|
/* twosided lighting */
|
|
if ( trace->twoSided ) {
|
|
angle = fabs( angle );
|
|
}
|
|
|
|
/* attenuate */
|
|
if ( light->flags & LIGHT_ATTEN_LINEAR ) {
|
|
add = angle * light->photons * linearScale - ( dist * light->fade );
|
|
if ( add < 0.0f ) {
|
|
add = 0.0f;
|
|
}
|
|
}
|
|
else{
|
|
add = light->photons / ( dist * dist ) * angle;
|
|
}
|
|
|
|
/* handle spotlights */
|
|
if ( light->type == EMIT_SPOT ) {
|
|
float distByNormal, radiusAtDist, sampleRadius;
|
|
vec3_t pointAtDist, distToSample;
|
|
|
|
/* do cone calculation */
|
|
distByNormal = -DotProduct( trace->displacement, light->normal );
|
|
if ( distByNormal < 0.0f ) {
|
|
return 0;
|
|
}
|
|
VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
|
|
radiusAtDist = light->radiusByDist * distByNormal;
|
|
VectorSubtract( trace->origin, pointAtDist, distToSample );
|
|
sampleRadius = VectorLength( distToSample );
|
|
|
|
/* outside the cone */
|
|
if ( sampleRadius >= radiusAtDist ) {
|
|
return 0;
|
|
}
|
|
|
|
/* attenuate */
|
|
if ( sampleRadius > ( radiusAtDist - 32.0f ) ) {
|
|
add *= ( ( radiusAtDist - sampleRadius ) / 32.0f );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ydnar: sunlight */
|
|
else if ( light->type == EMIT_SUN ) {
|
|
/* get origin and direction */
|
|
VectorAdd( trace->origin, light->origin, trace->end );
|
|
dist = SetupTrace( trace );
|
|
|
|
/* angle attenuation */
|
|
angle = ( light->flags & LIGHT_ATTEN_ANGLE )
|
|
? DotProduct( trace->normal, trace->direction )
|
|
: 1.0f;
|
|
|
|
/* twosided lighting */
|
|
if ( trace->twoSided ) {
|
|
angle = fabs( angle );
|
|
}
|
|
|
|
/* attenuate */
|
|
add = light->photons * angle;
|
|
if ( add <= 0.0f ) {
|
|
return 0;
|
|
}
|
|
|
|
/* setup trace */
|
|
trace->testAll = qtrue;
|
|
VectorScale( light->color, add, trace->color );
|
|
|
|
/* trace to point */
|
|
if ( trace->testOcclusion && !trace->forceSunlight ) {
|
|
/* trace */
|
|
TraceLine( trace );
|
|
if ( !( trace->compileFlags & C_SKY ) || trace->opaque ) {
|
|
VectorClear( trace->color );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* return to sender */
|
|
return 1;
|
|
}
|
|
else {
|
|
Error( "Light of undefined type!" );
|
|
}
|
|
|
|
/* ydnar: changed to a variable number */
|
|
if ( add <= 0.0f || ( add <= light->falloffTolerance && ( light->flags & LIGHT_FAST_ACTUAL ) ) ) {
|
|
return 0;
|
|
}
|
|
|
|
/* setup trace */
|
|
trace->testAll = qfalse;
|
|
VectorScale( light->color, add, trace->color );
|
|
|
|
/* raytrace */
|
|
TraceLine( trace );
|
|
if ( trace->passSolid || trace->opaque ) {
|
|
VectorClear( trace->color );
|
|
return -1;
|
|
}
|
|
|
|
/* return to sender */
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
LightingAtSample()
|
|
determines the amount of light reaching a sample (luxel or vertex)
|
|
*/
|
|
|
|
void LightingAtSample( trace_t *trace, byte styles[ MAX_LIGHTMAPS ], vec3_t colors[ MAX_LIGHTMAPS ] ){
|
|
int i, lightmapNum;
|
|
|
|
|
|
/* clear colors */
|
|
for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
|
|
VectorClear( colors[ lightmapNum ] );
|
|
|
|
/* ydnar: normalmap */
|
|
if ( normalmap ) {
|
|
colors[ 0 ][ 0 ] = ( trace->normal[ 0 ] + 1.0f ) * 127.5f;
|
|
colors[ 0 ][ 1 ] = ( trace->normal[ 1 ] + 1.0f ) * 127.5f;
|
|
colors[ 0 ][ 2 ] = ( trace->normal[ 2 ] + 1.0f ) * 127.5f;
|
|
return;
|
|
}
|
|
|
|
/* ydnar: don't bounce ambient all the time */
|
|
if ( !bouncing ) {
|
|
VectorCopy( ambientColor, colors[ 0 ] );
|
|
}
|
|
|
|
/* ydnar: trace to all the list of lights pre-stored in tw */
|
|
for ( i = 0; i < trace->numLights && trace->lights[ i ] != NULL; i++ )
|
|
{
|
|
/* set light */
|
|
trace->light = trace->lights[ i ];
|
|
|
|
/* style check */
|
|
for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
|
|
{
|
|
if ( styles[ lightmapNum ] == trace->light->style ||
|
|
styles[ lightmapNum ] == LS_NONE ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* max of MAX_LIGHTMAPS (4) styles allowed to hit a sample */
|
|
if ( lightmapNum >= MAX_LIGHTMAPS ) {
|
|
continue;
|
|
}
|
|
|
|
/* sample light */
|
|
LightContributionToSample( trace );
|
|
if ( trace->color[ 0 ] == 0.0f && trace->color[ 1 ] == 0.0f && trace->color[ 2 ] == 0.0f ) {
|
|
continue;
|
|
}
|
|
|
|
/* handle negative light */
|
|
if ( trace->light->flags & LIGHT_NEGATIVE ) {
|
|
VectorScale( trace->color, -1.0f, trace->color );
|
|
}
|
|
|
|
/* set style */
|
|
styles[ lightmapNum ] = trace->light->style;
|
|
|
|
/* add it */
|
|
VectorAdd( colors[ lightmapNum ], trace->color, colors[ lightmapNum ] );
|
|
|
|
/* cheap mode */
|
|
if ( cheap &&
|
|
colors[ 0 ][ 0 ] >= 255.0f &&
|
|
colors[ 0 ][ 1 ] >= 255.0f &&
|
|
colors[ 0 ][ 2 ] >= 255.0f ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
LightContributionToPoint()
|
|
for a given light, how much light/color reaches a given point in space (with no facing)
|
|
note: this is similar to LightContributionToSample() but optimized for omnidirectional sampling
|
|
*/
|
|
|
|
int LightContributionToPoint( trace_t *trace ){
|
|
light_t *light;
|
|
float add, dist;
|
|
|
|
|
|
/* get light */
|
|
light = trace->light;
|
|
|
|
/* clear color */
|
|
VectorClear( trace->color );
|
|
|
|
/* ydnar: early out */
|
|
if ( !( light->flags & LIGHT_GRID ) || light->envelope <= 0.0f ) {
|
|
return qfalse;
|
|
}
|
|
|
|
/* is this a sun? */
|
|
if ( light->type != EMIT_SUN ) {
|
|
/* sun only? */
|
|
if ( sunOnly ) {
|
|
return qfalse;
|
|
}
|
|
|
|
/* test pvs */
|
|
if ( !ClusterVisible( trace->cluster, light->cluster ) ) {
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
/* ydnar: check origin against light's pvs envelope */
|
|
if ( trace->origin[ 0 ] > light->maxs[ 0 ] || trace->origin[ 0 ] < light->mins[ 0 ] ||
|
|
trace->origin[ 1 ] > light->maxs[ 1 ] || trace->origin[ 1 ] < light->mins[ 1 ] ||
|
|
trace->origin[ 2 ] > light->maxs[ 2 ] || trace->origin[ 2 ] < light->mins[ 2 ] ) {
|
|
gridBoundsCulled++;
|
|
return qfalse;
|
|
}
|
|
|
|
/* set light origin */
|
|
if ( light->type == EMIT_SUN ) {
|
|
VectorAdd( trace->origin, light->origin, trace->end );
|
|
}
|
|
else{
|
|
VectorCopy( light->origin, trace->end );
|
|
}
|
|
|
|
/* set direction */
|
|
dist = SetupTrace( trace );
|
|
|
|
/* test envelope */
|
|
if ( dist > light->envelope ) {
|
|
gridEnvelopeCulled++;
|
|
return qfalse;
|
|
}
|
|
|
|
/* ptpff approximation */
|
|
if ( light->type == EMIT_AREA && faster ) {
|
|
/* clamp the distance to prevent super hot spots */
|
|
if ( dist < 16.0f ) {
|
|
dist = 16.0f;
|
|
}
|
|
|
|
/* attenuate */
|
|
add = light->photons / ( dist * dist );
|
|
}
|
|
|
|
/* exact point to polygon form factor */
|
|
else if ( light->type == EMIT_AREA ) {
|
|
float factor, d;
|
|
vec3_t pushedOrigin;
|
|
|
|
|
|
/* see if the point is behind the light */
|
|
d = DotProduct( trace->origin, light->normal ) - light->dist;
|
|
if ( !( light->flags & LIGHT_TWOSIDED ) && d < -1.0f ) {
|
|
return qfalse;
|
|
}
|
|
|
|
/* 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.0f && d < 8.0f ) {
|
|
VectorMA( trace->origin, ( 8.0f - d ), light->normal, pushedOrigin );
|
|
}
|
|
else{
|
|
VectorCopy( trace->origin, pushedOrigin );
|
|
}
|
|
|
|
/* calculate the contribution (ydnar 2002-10-21: [bug 642] bad normal calc) */
|
|
factor = PointToPolygonFormFactor( pushedOrigin, trace->direction, light->w );
|
|
if ( factor == 0.0f ) {
|
|
return qfalse;
|
|
}
|
|
else if ( factor < 0.0f ) {
|
|
if ( light->flags & LIGHT_TWOSIDED ) {
|
|
factor = -factor;
|
|
}
|
|
else{
|
|
return qfalse;
|
|
}
|
|
}
|
|
|
|
/* ydnar: moved to here */
|
|
add = factor * light->add;
|
|
}
|
|
|
|
/* point/spot lights */
|
|
else if ( light->type == EMIT_POINT || light->type == EMIT_SPOT ) {
|
|
/* clamp the distance to prevent super hot spots */
|
|
if ( dist < 16.0f ) {
|
|
dist = 16.0f;
|
|
}
|
|
|
|
/* attenuate */
|
|
if ( light->flags & LIGHT_ATTEN_LINEAR ) {
|
|
add = light->photons * linearScale - ( dist * light->fade );
|
|
if ( add < 0.0f ) {
|
|
add = 0.0f;
|
|
}
|
|
}
|
|
else{
|
|
add = light->photons / ( dist * dist );
|
|
}
|
|
|
|
/* handle spotlights */
|
|
if ( light->type == EMIT_SPOT ) {
|
|
float distByNormal, radiusAtDist, sampleRadius;
|
|
vec3_t pointAtDist, distToSample;
|
|
|
|
|
|
/* do cone calculation */
|
|
distByNormal = -DotProduct( trace->displacement, light->normal );
|
|
if ( distByNormal < 0.0f ) {
|
|
return qfalse;
|
|
}
|
|
VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
|
|
radiusAtDist = light->radiusByDist * distByNormal;
|
|
VectorSubtract( trace->origin, pointAtDist, distToSample );
|
|
sampleRadius = VectorLength( distToSample );
|
|
|
|
/* outside the cone */
|
|
if ( sampleRadius >= radiusAtDist ) {
|
|
return qfalse;
|
|
}
|
|
|
|
/* attenuate */
|
|
if ( sampleRadius > ( radiusAtDist - 32.0f ) ) {
|
|
add *= ( ( radiusAtDist - sampleRadius ) / 32.0f );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ydnar: sunlight */
|
|
else if ( light->type == EMIT_SUN ) {
|
|
/* attenuate */
|
|
add = light->photons;
|
|
if ( add <= 0.0f ) {
|
|
return qfalse;
|
|
}
|
|
|
|
/* setup trace */
|
|
trace->testAll = qtrue;
|
|
VectorScale( light->color, add, trace->color );
|
|
|
|
/* trace to point */
|
|
if ( trace->testOcclusion && !trace->forceSunlight ) {
|
|
/* trace */
|
|
TraceLine( trace );
|
|
if ( !( trace->compileFlags & C_SKY ) || trace->opaque ) {
|
|
VectorClear( trace->color );
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* return to sender */
|
|
return qtrue;
|
|
}
|
|
|
|
/* unknown light type */
|
|
else{
|
|
return qfalse;
|
|
}
|
|
|
|
/* ydnar: changed to a variable number */
|
|
if ( add <= 0.0f || ( add <= light->falloffTolerance && ( light->flags & LIGHT_FAST_ACTUAL ) ) ) {
|
|
return qfalse;
|
|
}
|
|
|
|
/* setup trace */
|
|
trace->testAll = qfalse;
|
|
VectorScale( light->color, add, trace->color );
|
|
|
|
/* trace */
|
|
TraceLine( trace );
|
|
if ( trace->passSolid ) {
|
|
VectorClear( trace->color );
|
|
return qfalse;
|
|
}
|
|
|
|
/* we have a valid sample */
|
|
return qtrue;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
TraceGrid()
|
|
grid samples are for quickly determining the lighting
|
|
of dynamically placed entities in the world
|
|
*/
|
|
|
|
#define MAX_CONTRIBUTIONS 1024
|
|
|
|
typedef struct
|
|
{
|
|
vec3_t dir;
|
|
vec3_t color;
|
|
int style;
|
|
}
|
|
contribution_t;
|
|
|
|
void TraceGrid( int num ){
|
|
int i, j, x, y, z, mod, step, numCon, numStyles;
|
|
float d;
|
|
vec3_t baseOrigin, cheapColor, color;
|
|
rawGridPoint_t *gp;
|
|
bspGridPoint_t *bgp;
|
|
contribution_t contributions[ MAX_CONTRIBUTIONS ];
|
|
trace_t trace;
|
|
|
|
/* get grid points */
|
|
gp = &rawGridPoints[ num ];
|
|
bgp = &bspGridPoints[ num ];
|
|
|
|
/* get grid origin */
|
|
mod = num;
|
|
z = mod / ( gridBounds[ 0 ] * gridBounds[ 1 ] );
|
|
mod -= z * ( gridBounds[ 0 ] * gridBounds[ 1 ] );
|
|
y = mod / gridBounds[ 0 ];
|
|
mod -= y * gridBounds[ 0 ];
|
|
x = mod;
|
|
|
|
trace.origin[ 0 ] = gridMins[ 0 ] + x * gridSize[ 0 ];
|
|
trace.origin[ 1 ] = gridMins[ 1 ] + y * gridSize[ 1 ];
|
|
trace.origin[ 2 ] = gridMins[ 2 ] + z * gridSize[ 2 ];
|
|
|
|
/* set inhibit sphere */
|
|
if ( gridSize[ 0 ] > gridSize[ 1 ] && gridSize[ 0 ] > gridSize[ 2 ] ) {
|
|
trace.inhibitRadius = gridSize[ 0 ] * 0.5f;
|
|
}
|
|
else if ( gridSize[ 1 ] > gridSize[ 0 ] && gridSize[ 1 ] > gridSize[ 2 ] ) {
|
|
trace.inhibitRadius = gridSize[ 1 ] * 0.5f;
|
|
}
|
|
else{
|
|
trace.inhibitRadius = gridSize[ 2 ] * 0.5f;
|
|
}
|
|
|
|
/* find point cluster */
|
|
trace.cluster = ClusterForPointExt( trace.origin, GRID_EPSILON );
|
|
if ( trace.cluster < 0 ) {
|
|
/* try to nudge the origin around to find a valid point */
|
|
VectorCopy( trace.origin, baseOrigin );
|
|
for ( step = 9; step <= 18; step += 9 )
|
|
{
|
|
for ( i = 0; i < 8; i++ )
|
|
{
|
|
VectorCopy( baseOrigin, trace.origin );
|
|
if ( i & 1 ) {
|
|
trace.origin[ 0 ] += step;
|
|
}
|
|
else{
|
|
trace.origin[ 0 ] -= step;
|
|
}
|
|
|
|
if ( i & 2 ) {
|
|
trace.origin[ 1 ] += step;
|
|
}
|
|
else{
|
|
trace.origin[ 1 ] -= step;
|
|
}
|
|
|
|
if ( i & 4 ) {
|
|
trace.origin[ 2 ] += step;
|
|
}
|
|
else{
|
|
trace.origin[ 2 ] -= step;
|
|
}
|
|
|
|
/* ydnar: changed to find cluster num */
|
|
trace.cluster = ClusterForPointExt( trace.origin, VERTEX_EPSILON );
|
|
if ( trace.cluster >= 0 ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( i != 8 ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* can't find a valid point at all */
|
|
if ( step > 18 ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* setup trace */
|
|
trace.testOcclusion = !noTrace;
|
|
trace.forceSunlight = qfalse;
|
|
trace.recvShadows = WORLDSPAWN_RECV_SHADOWS;
|
|
trace.numSurfaces = 0;
|
|
trace.surfaces = NULL;
|
|
trace.numLights = 0;
|
|
trace.lights = NULL;
|
|
|
|
/* clear */
|
|
numCon = 0;
|
|
VectorClear( cheapColor );
|
|
|
|
/* trace to all the lights, find the major light direction, and divide the
|
|
total light between that along the direction and the remaining in the ambient */
|
|
for ( trace.light = lights; trace.light != NULL; trace.light = trace.light->next )
|
|
{
|
|
float addSize;
|
|
|
|
|
|
/* sample light */
|
|
if ( !LightContributionToPoint( &trace ) ) {
|
|
continue;
|
|
}
|
|
|
|
/* handle negative light */
|
|
if ( trace.light->flags & LIGHT_NEGATIVE ) {
|
|
VectorScale( trace.color, -1.0f, trace.color );
|
|
}
|
|
|
|
/* add a contribution */
|
|
VectorCopy( trace.color, contributions[ numCon ].color );
|
|
VectorCopy( trace.direction, contributions[ numCon ].dir );
|
|
contributions[ numCon ].style = trace.light->style;
|
|
numCon++;
|
|
|
|
/* push average direction around */
|
|
addSize = VectorLength( trace.color );
|
|
VectorMA( gp->dir, addSize, trace.direction, gp->dir );
|
|
|
|
/* stop after a while */
|
|
if ( numCon >= ( MAX_CONTRIBUTIONS - 1 ) ) {
|
|
break;
|
|
}
|
|
|
|
/* ydnar: cheap mode */
|
|
VectorAdd( cheapColor, trace.color, cheapColor );
|
|
if ( cheapgrid && cheapColor[ 0 ] >= 255.0f && cheapColor[ 1 ] >= 255.0f && cheapColor[ 2 ] >= 255.0f ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/////// Floodlighting for point //////////////////
|
|
//do our floodlight ambient occlusion loop, and add a single contribution based on the brightest dir
|
|
if ( floodlighty ) {
|
|
int q;
|
|
float addSize,f;
|
|
vec3_t col,dir;
|
|
col[0] = col[1] = col[2] = floodlightIntensity;
|
|
dir[0] = dir[1] = 0;
|
|
dir[2] = 1;
|
|
|
|
trace.testOcclusion = qtrue;
|
|
trace.forceSunlight = qfalse;
|
|
trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
|
|
trace.testAll = qtrue;
|
|
|
|
for ( q = 0; q < 2; q++ )
|
|
{
|
|
if ( q == 0 ) { //upper hemisphere
|
|
trace.normal[0] = 0;
|
|
trace.normal[1] = 0;
|
|
trace.normal[2] = 1;
|
|
}
|
|
else //lower hemisphere
|
|
{
|
|
trace.normal[0] = 0;
|
|
trace.normal[1] = 0;
|
|
trace.normal[2] = -1;
|
|
}
|
|
|
|
f = FloodLightForSample( &trace );
|
|
|
|
contributions[ numCon ].color[0] = col[0] * f;
|
|
contributions[ numCon ].color[1] = col[1] * f;
|
|
contributions[ numCon ].color[2] = col[2] * f;
|
|
|
|
contributions[ numCon ].dir[0] = dir[0];
|
|
contributions[ numCon ].dir[1] = dir[1];
|
|
contributions[ numCon ].dir[2] = dir[2];
|
|
|
|
contributions[ numCon ].style = 0;
|
|
numCon++;
|
|
/* push average direction around */
|
|
addSize = VectorLength( col );
|
|
VectorMA( gp->dir, addSize, dir, gp->dir );
|
|
}
|
|
}
|
|
/////////////////////
|
|
|
|
/* normalize to get primary light direction */
|
|
VectorNormalize( gp->dir, gp->dir );
|
|
|
|
/* now that we have identified the primary light direction,
|
|
go back and separate all the light into directed and ambient */
|
|
numStyles = 1;
|
|
for ( i = 0; i < numCon; i++ )
|
|
{
|
|
/* get relative directed strength */
|
|
d = DotProduct( contributions[ i ].dir, gp->dir );
|
|
if ( d < 0.0f ) {
|
|
d = 0.0f;
|
|
}
|
|
|
|
/* find appropriate style */
|
|
for ( j = 0; j < numStyles; j++ )
|
|
{
|
|
if ( gp->styles[ j ] == contributions[ i ].style ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* style not found? */
|
|
if ( j >= numStyles ) {
|
|
/* add a new style */
|
|
if ( numStyles < MAX_LIGHTMAPS ) {
|
|
gp->styles[ numStyles ] = contributions[ i ].style;
|
|
bgp->styles[ numStyles ] = contributions[ i ].style;
|
|
numStyles++;
|
|
//% Sys_Printf( "(%d, %d) ", num, contributions[ i ].style );
|
|
}
|
|
|
|
/* fallback */
|
|
else{
|
|
j = 0;
|
|
}
|
|
}
|
|
|
|
/* add the directed color */
|
|
VectorMA( gp->directed[ j ], d, contributions[ i ].color, gp->directed[ j ] );
|
|
|
|
/* ambient light will be at 1/4 the value of directed light */
|
|
/* (ydnar: nuke this in favor of more dramatic lighting?) */
|
|
d = 0.25f * ( 1.0f - d );
|
|
VectorMA( gp->ambient[ j ], d, contributions[ i ].color, gp->ambient[ j ] );
|
|
}
|
|
|
|
|
|
/* store off sample */
|
|
for ( i = 0; i < MAX_LIGHTMAPS; i++ )
|
|
{
|
|
/* do some fudging to keep the ambient from being too low (2003-07-05: 0.25 -> 0.125) */
|
|
if ( !bouncing ) {
|
|
VectorMA( gp->ambient[ i ], 0.125f, gp->directed[ i ], gp->ambient[ i ] );
|
|
}
|
|
|
|
/* set minimum light and copy off to bytes */
|
|
VectorCopy( gp->ambient[ i ], color );
|
|
for ( j = 0; j < 3; j++ )
|
|
if ( color[ j ] < minGridLight[ j ] ) {
|
|
color[ j ] = minGridLight[ j ];
|
|
}
|
|
|
|
ColorToBytes( color, bgp->ambient[ i ], 1.0f );
|
|
ColorToBytes( gp->directed[ i ], bgp->directed[ i ], 1.0f );
|
|
}
|
|
|
|
/* debug code */
|
|
#if 0
|
|
//% Sys_FPrintf( SYS_VRB, "%10d %10d %10d ", &gp->ambient[ 0 ][ 0 ], &gp->ambient[ 0 ][ 1 ], &gp->ambient[ 0 ][ 2 ] );
|
|
Sys_FPrintf( SYS_VRB, "%9d Amb: (%03.1f %03.1f %03.1f) Dir: (%03.1f %03.1f %03.1f)\n",
|
|
num,
|
|
gp->ambient[ 0 ][ 0 ], gp->ambient[ 0 ][ 1 ], gp->ambient[ 0 ][ 2 ],
|
|
gp->directed[ 0 ][ 0 ], gp->directed[ 0 ][ 1 ], gp->directed[ 0 ][ 2 ] );
|
|
#endif
|
|
|
|
/* store direction */
|
|
if ( !bouncing ) {
|
|
NormalToLatLong( gp->dir, bgp->latLong );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
SetupGrid()
|
|
calculates the size of the lightgrid and allocates memory
|
|
*/
|
|
|
|
void SetupGrid( void ){
|
|
int i, j;
|
|
vec3_t maxs, oldGridSize;
|
|
const char *value;
|
|
char temp[ 64 ];
|
|
|
|
|
|
/* don't do this if not grid lighting */
|
|
if ( noGridLighting ) {
|
|
return;
|
|
}
|
|
|
|
/* ydnar: set grid size */
|
|
value = ValueForKey( &entities[ 0 ], "gridsize" );
|
|
if ( value[ 0 ] != '\0' ) {
|
|
sscanf( value, "%f %f %f", &gridSize[ 0 ], &gridSize[ 1 ], &gridSize[ 2 ] );
|
|
}
|
|
|
|
/* quantize it */
|
|
VectorCopy( gridSize, oldGridSize );
|
|
for ( i = 0; i < 3; i++ )
|
|
gridSize[ i ] = gridSize[ i ] >= 8.0f ? floor( gridSize[ i ] ) : 8.0f;
|
|
|
|
/* ydnar: increase gridSize until grid count is smaller than max allowed */
|
|
numRawGridPoints = MAX_MAP_LIGHTGRID + 1;
|
|
j = 0;
|
|
while ( numRawGridPoints > MAX_MAP_LIGHTGRID )
|
|
{
|
|
/* get world bounds */
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
gridMins[ i ] = gridSize[ i ] * ceil( bspModels[ 0 ].mins[ i ] / gridSize[ i ] );
|
|
maxs[ i ] = gridSize[ i ] * floor( bspModels[ 0 ].maxs[ i ] / gridSize[ i ] );
|
|
gridBounds[ i ] = ( maxs[ i ] - gridMins[ i ] ) / gridSize[ i ] + 1;
|
|
}
|
|
|
|
/* set grid size */
|
|
numRawGridPoints = gridBounds[ 0 ] * gridBounds[ 1 ] * gridBounds[ 2 ];
|
|
|
|
/* increase grid size a bit */
|
|
if ( numRawGridPoints > MAX_MAP_LIGHTGRID ) {
|
|
gridSize[ j++ % 3 ] += 16.0f;
|
|
}
|
|
}
|
|
|
|
/* print it */
|
|
Sys_Printf( "Grid size = { %1.0f, %1.0f, %1.0f }\n", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
|
|
|
|
/* different? */
|
|
if ( !VectorCompare( gridSize, oldGridSize ) ) {
|
|
sprintf( temp, "%.0f %.0f %.0f", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );
|
|
SetKeyValue( &entities[ 0 ], "gridsize", (const char*) temp );
|
|
Sys_FPrintf( SYS_VRB, "Storing adjusted grid size\n" );
|
|
}
|
|
|
|
/* 2nd variable. fixme: is this silly? */
|
|
numBSPGridPoints = numRawGridPoints;
|
|
|
|
/* allocate lightgrid */
|
|
rawGridPoints = safe_malloc( numRawGridPoints * sizeof( *rawGridPoints ) );
|
|
memset( rawGridPoints, 0, numRawGridPoints * sizeof( *rawGridPoints ) );
|
|
|
|
if ( bspGridPoints != NULL ) {
|
|
free( bspGridPoints );
|
|
}
|
|
bspGridPoints = safe_malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
|
|
memset( bspGridPoints, 0, numBSPGridPoints * sizeof( *bspGridPoints ) );
|
|
|
|
/* clear lightgrid */
|
|
for ( i = 0; i < numRawGridPoints; i++ )
|
|
{
|
|
VectorCopy( ambientColor, rawGridPoints[ i ].ambient[ j ] );
|
|
rawGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
|
|
bspGridPoints[ i ].styles[ 0 ] = LS_NORMAL;
|
|
for ( j = 1; j < MAX_LIGHTMAPS; j++ )
|
|
{
|
|
rawGridPoints[ i ].styles[ j ] = LS_NONE;
|
|
bspGridPoints[ i ].styles[ j ] = LS_NONE;
|
|
}
|
|
}
|
|
|
|
/* note it */
|
|
Sys_Printf( "%9d grid points\n", numRawGridPoints );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
LightWorld()
|
|
does what it says...
|
|
*/
|
|
|
|
void LightWorld( void ){
|
|
vec3_t color;
|
|
float f;
|
|
int b, bt;
|
|
qboolean minVertex, minGrid;
|
|
const char *value;
|
|
|
|
|
|
/* ydnar: smooth normals */
|
|
if ( shade ) {
|
|
Sys_Printf( "--- SmoothNormals ---\n" );
|
|
SmoothNormals();
|
|
}
|
|
|
|
/* determine the number of grid points */
|
|
Sys_Printf( "--- SetupGrid ---\n" );
|
|
SetupGrid();
|
|
|
|
/* find the optional minimum lighting values */
|
|
GetVectorForKey( &entities[ 0 ], "_color", color );
|
|
if ( VectorLength( color ) == 0.0f ) {
|
|
VectorSet( color, 1.0, 1.0, 1.0 );
|
|
}
|
|
|
|
/* ambient */
|
|
f = FloatForKey( &entities[ 0 ], "_ambient" );
|
|
if ( f == 0.0f ) {
|
|
f = FloatForKey( &entities[ 0 ], "ambient" );
|
|
}
|
|
VectorScale( color, f, ambientColor );
|
|
|
|
/* minvertexlight */
|
|
minVertex = qfalse;
|
|
value = ValueForKey( &entities[ 0 ], "_minvertexlight" );
|
|
if ( value[ 0 ] != '\0' ) {
|
|
minVertex = qtrue;
|
|
f = atof( value );
|
|
VectorScale( color, f, minVertexLight );
|
|
}
|
|
|
|
/* mingridlight */
|
|
minGrid = qfalse;
|
|
value = ValueForKey( &entities[ 0 ], "_mingridlight" );
|
|
if ( value[ 0 ] != '\0' ) {
|
|
minGrid = qtrue;
|
|
f = atof( value );
|
|
VectorScale( color, f, minGridLight );
|
|
}
|
|
|
|
/* minlight */
|
|
value = ValueForKey( &entities[ 0 ], "_minlight" );
|
|
if ( value[ 0 ] != '\0' ) {
|
|
f = atof( value );
|
|
VectorScale( color, f, minLight );
|
|
if ( minVertex == qfalse ) {
|
|
VectorScale( color, f, minVertexLight );
|
|
}
|
|
if ( minGrid == qfalse ) {
|
|
VectorScale( color, f, minGridLight );
|
|
}
|
|
}
|
|
|
|
/* create world lights */
|
|
Sys_FPrintf( SYS_VRB, "--- CreateLights ---\n" );
|
|
CreateEntityLights();
|
|
CreateSurfaceLights();
|
|
Sys_Printf( "%9d point lights\n", numPointLights );
|
|
Sys_Printf( "%9d spotlights\n", numSpotLights );
|
|
Sys_Printf( "%9d diffuse (area) lights\n", numDiffuseLights );
|
|
Sys_Printf( "%9d sun/sky lights\n", numSunLights );
|
|
|
|
/* calculate lightgrid */
|
|
if ( !noGridLighting ) {
|
|
/* ydnar: set up light envelopes */
|
|
SetupEnvelopes( qtrue, fastgrid );
|
|
|
|
Sys_Printf( "--- TraceGrid ---\n" );
|
|
RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
|
|
Sys_Printf( "%d x %d x %d = %d grid\n",
|
|
gridBounds[ 0 ], gridBounds[ 1 ], gridBounds[ 2 ], numBSPGridPoints );
|
|
|
|
/* ydnar: emit statistics on light culling */
|
|
Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
|
|
Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
|
|
}
|
|
|
|
/* slight optimization to remove a sqrt */
|
|
subdivideThreshold *= subdivideThreshold;
|
|
|
|
/* map the world luxels */
|
|
Sys_Printf( "--- MapRawLightmap ---\n" );
|
|
RunThreadsOnIndividual( numRawLightmaps, qtrue, MapRawLightmap );
|
|
Sys_Printf( "%9d luxels\n", numLuxels );
|
|
Sys_Printf( "%9d luxels mapped\n", numLuxelsMapped );
|
|
Sys_Printf( "%9d luxels occluded\n", numLuxelsOccluded );
|
|
|
|
/* dirty them up */
|
|
if ( dirty ) {
|
|
Sys_Printf( "--- DirtyRawLightmap ---\n" );
|
|
RunThreadsOnIndividual( numRawLightmaps, qtrue, DirtyRawLightmap );
|
|
}
|
|
|
|
/* floodlight them up */
|
|
if ( floodlighty ) {
|
|
Sys_Printf( "--- FloodlightRawLightmap ---\n" );
|
|
RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
|
|
}
|
|
|
|
/* ydnar: set up light envelopes */
|
|
SetupEnvelopes( qfalse, fast );
|
|
|
|
/* light up my world */
|
|
lightsPlaneCulled = 0;
|
|
lightsEnvelopeCulled = 0;
|
|
lightsBoundsCulled = 0;
|
|
lightsClusterCulled = 0;
|
|
|
|
Sys_Printf( "--- IlluminateRawLightmap ---\n" );
|
|
RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );
|
|
Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );
|
|
|
|
StitchSurfaceLightmaps();
|
|
|
|
Sys_Printf( "--- IlluminateVertexes ---\n" );
|
|
RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );
|
|
Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
|
|
|
|
/* ydnar: emit statistics on light culling */
|
|
Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );
|
|
Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );
|
|
Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );
|
|
Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );
|
|
|
|
/* radiosity */
|
|
b = 1;
|
|
bt = bounce;
|
|
while ( bounce > 0 )
|
|
{
|
|
/* store off the bsp between bounces */
|
|
StoreSurfaceLightmaps();
|
|
Sys_Printf( "Writing %s\n", source );
|
|
WriteBSPFile( source );
|
|
|
|
/* note it */
|
|
Sys_Printf( "\n--- Radiosity (bounce %d of %d) ---\n", b, bt );
|
|
|
|
/* flag bouncing */
|
|
bouncing = qtrue;
|
|
VectorClear( ambientColor );
|
|
floodlighty = qfalse;
|
|
|
|
/* generate diffuse lights */
|
|
RadFreeLights();
|
|
RadCreateDiffuseLights();
|
|
|
|
/* setup light envelopes */
|
|
SetupEnvelopes( qfalse, fastbounce );
|
|
if ( numLights == 0 ) {
|
|
Sys_Printf( "No diffuse light to calculate, ending radiosity.\n" );
|
|
return;
|
|
}
|
|
|
|
/* add to lightgrid */
|
|
if ( bouncegrid ) {
|
|
gridEnvelopeCulled = 0;
|
|
gridBoundsCulled = 0;
|
|
|
|
Sys_Printf( "--- BounceGrid ---\n" );
|
|
RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );
|
|
Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );
|
|
Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );
|
|
}
|
|
|
|
/* light up my world */
|
|
lightsPlaneCulled = 0;
|
|
lightsEnvelopeCulled = 0;
|
|
lightsBoundsCulled = 0;
|
|
lightsClusterCulled = 0;
|
|
|
|
Sys_Printf( "--- IlluminateRawLightmap ---\n" );
|
|
RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );
|
|
Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );
|
|
Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
|
|
|
|
StitchSurfaceLightmaps();
|
|
|
|
Sys_Printf( "--- IlluminateVertexes ---\n" );
|
|
RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );
|
|
Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );
|
|
|
|
/* ydnar: emit statistics on light culling */
|
|
Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );
|
|
Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );
|
|
Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );
|
|
Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );
|
|
|
|
/* interate */
|
|
bounce--;
|
|
b++;
|
|
}
|
|
/* ydnar: store off lightmaps */
|
|
StoreSurfaceLightmaps();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
LightMain()
|
|
main routine for light processing
|
|
*/
|
|
|
|
int LightMain( int argc, char **argv ){
|
|
int i;
|
|
float f;
|
|
char mapSource[ 1024 ];
|
|
const char *value;
|
|
|
|
|
|
/* note it */
|
|
Sys_Printf( "--- Light ---\n" );
|
|
|
|
/* set standard game flags */
|
|
wolfLight = game->wolfLight;
|
|
lmCustomSize = game->lightmapSize;
|
|
lightmapGamma = game->lightmapGamma;
|
|
lightmapCompensate = game->lightmapCompensate;
|
|
|
|
/* process commandline arguments */
|
|
for ( i = 1; i < ( argc - 1 ); i++ )
|
|
{
|
|
/* lightsource scaling */
|
|
if ( !strcmp( argv[ i ], "-point" ) || !strcmp( argv[ i ], "-pointscale" ) ) {
|
|
f = atof( argv[ i + 1 ] );
|
|
pointScale *= f;
|
|
Sys_Printf( "Point (entity) light scaled by %f to %f\n", f, pointScale );
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-area" ) || !strcmp( argv[ i ], "-areascale" ) ) {
|
|
f = atof( argv[ i + 1 ] );
|
|
areaScale *= f;
|
|
Sys_Printf( "Area (shader) light scaled by %f to %f\n", f, areaScale );
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-sky" ) || !strcmp( argv[ i ], "-skyscale" ) ) {
|
|
f = atof( argv[ i + 1 ] );
|
|
skyScale *= f;
|
|
Sys_Printf( "Sky/sun light scaled by %f to %f\n", f, skyScale );
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-bouncescale" ) ) {
|
|
f = atof( argv[ i + 1 ] );
|
|
bounceScale *= f;
|
|
Sys_Printf( "Bounce (radiosity) light scaled by %f to %f\n", f, bounceScale );
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-scale" ) ) {
|
|
f = atof( argv[ i + 1 ] );
|
|
pointScale *= f;
|
|
areaScale *= f;
|
|
skyScale *= f;
|
|
bounceScale *= f;
|
|
Sys_Printf( "All light scaled by %f\n", f );
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-gamma" ) ) {
|
|
f = atof( argv[ i + 1 ] );
|
|
lightmapGamma = f;
|
|
Sys_Printf( "Lighting gamma set to %f\n", lightmapGamma );
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-exposure" ) ) {
|
|
f = atof( argv[ i + 1 ] );
|
|
lightmapExposure = f;
|
|
Sys_Printf( "Lighting exposure set to %f\n", lightmapExposure );
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-compensate" ) ) {
|
|
f = atof( argv[ i + 1 ] );
|
|
if ( f <= 0.0f ) {
|
|
f = 1.0f;
|
|
}
|
|
lightmapCompensate = f;
|
|
Sys_Printf( "Lighting compensation set to 1/%f\n", lightmapCompensate );
|
|
i++;
|
|
}
|
|
|
|
/* ydnar switches */
|
|
else if ( !strcmp( argv[ i ], "-bounce" ) ) {
|
|
bounce = atoi( argv[ i + 1 ] );
|
|
if ( bounce < 0 ) {
|
|
bounce = 0;
|
|
}
|
|
else if ( bounce > 0 ) {
|
|
Sys_Printf( "Radiosity enabled with %d bounce(s)\n", bounce );
|
|
}
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-supersample" ) || !strcmp( argv[ i ], "-super" ) ) {
|
|
superSample = atoi( argv[ i + 1 ] );
|
|
if ( superSample < 1 ) {
|
|
superSample = 1;
|
|
}
|
|
else if ( superSample > 1 ) {
|
|
Sys_Printf( "Ordered-grid supersampling enabled with %d sample(s) per lightmap texel\n", ( superSample * superSample ) );
|
|
}
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-samples" ) ) {
|
|
lightSamples = atoi( argv[ i + 1 ] );
|
|
if ( lightSamples < 1 ) {
|
|
lightSamples = 1;
|
|
}
|
|
else if ( lightSamples > 1 ) {
|
|
Sys_Printf( "Adaptive supersampling enabled with %d sample(s) per lightmap texel\n", lightSamples );
|
|
}
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-filter" ) ) {
|
|
filter = qtrue;
|
|
Sys_Printf( "Lightmap filtering enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-dark" ) ) {
|
|
dark = qtrue;
|
|
Sys_Printf( "Dark lightmap seams enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-shadeangle" ) ) {
|
|
shadeAngleDegrees = atof( argv[ i + 1 ] );
|
|
if ( shadeAngleDegrees < 0.0f ) {
|
|
shadeAngleDegrees = 0.0f;
|
|
}
|
|
else if ( shadeAngleDegrees > 0.0f ) {
|
|
shade = qtrue;
|
|
Sys_Printf( "Phong shading enabled with a breaking angle of %f degrees\n", shadeAngleDegrees );
|
|
}
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-thresh" ) ) {
|
|
subdivideThreshold = atof( argv[ i + 1 ] );
|
|
if ( subdivideThreshold < 0 ) {
|
|
subdivideThreshold = DEFAULT_SUBDIVIDE_THRESHOLD;
|
|
}
|
|
else{
|
|
Sys_Printf( "Subdivision threshold set at %.3f\n", subdivideThreshold );
|
|
}
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-approx" ) ) {
|
|
approximateTolerance = atoi( argv[ i + 1 ] );
|
|
if ( approximateTolerance < 0 ) {
|
|
approximateTolerance = 0;
|
|
}
|
|
else if ( approximateTolerance > 0 ) {
|
|
Sys_Printf( "Approximating lightmaps within a byte tolerance of %d\n", approximateTolerance );
|
|
}
|
|
i++;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-deluxe" ) || !strcmp( argv[ i ], "-deluxemap" ) ) {
|
|
deluxemap = qtrue;
|
|
Sys_Printf( "Generating deluxemaps for average light direction\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-external" ) ) {
|
|
externalLightmaps = qtrue;
|
|
Sys_Printf( "Storing all lightmaps externally\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-lightmapsize" ) ) {
|
|
lmCustomSize = atoi( argv[ i + 1 ] );
|
|
|
|
/* must be a power of 2 and greater than 2 */
|
|
if ( ( ( lmCustomSize - 1 ) & lmCustomSize ) || lmCustomSize < 2 ) {
|
|
Sys_FPrintf( SYS_WRN, "WARNING: Lightmap size must be a power of 2, greater or equal to 2 pixels.\n" );
|
|
lmCustomSize = game->lightmapSize;
|
|
}
|
|
i++;
|
|
Sys_Printf( "Default lightmap size set to %d x %d pixels\n", lmCustomSize, lmCustomSize );
|
|
|
|
/* enable external lightmaps */
|
|
if ( lmCustomSize != game->lightmapSize ) {
|
|
externalLightmaps = qtrue;
|
|
Sys_Printf( "Storing all lightmaps externally\n" );
|
|
}
|
|
}
|
|
|
|
/* ydnar: add this to suppress warnings */
|
|
else if ( !strcmp( argv[ i ], "-custinfoparms" ) ) {
|
|
Sys_Printf( "Custom info parms enabled\n" );
|
|
useCustomInfoParms = qtrue;
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-wolf" ) ) {
|
|
/* -game should already be set */
|
|
wolfLight = qtrue;
|
|
Sys_Printf( "Enabling Wolf lighting model (linear default)\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-q3" ) ) {
|
|
/* -game should already be set */
|
|
wolfLight = qfalse;
|
|
Sys_Printf( "Enabling Quake 3 lighting model (nonlinear default)\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-sunonly" ) ) {
|
|
sunOnly = qtrue;
|
|
Sys_Printf( "Only computing sunlight\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-bounceonly" ) ) {
|
|
bounceOnly = qtrue;
|
|
Sys_Printf( "Storing bounced light (radiosity) only\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-nocollapse" ) ) {
|
|
noCollapse = qtrue;
|
|
Sys_Printf( "Identical lightmap collapsing disabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-shade" ) ) {
|
|
shade = qtrue;
|
|
Sys_Printf( "Phong shading enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-bouncegrid" ) ) {
|
|
bouncegrid = qtrue;
|
|
if ( bounce > 0 ) {
|
|
Sys_Printf( "Grid lighting with radiosity enabled\n" );
|
|
}
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-smooth" ) ) {
|
|
lightSamples = EXTRA_SCALE;
|
|
Sys_Printf( "The -smooth argument is deprecated, use \"-samples 2\" instead\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-fast" ) ) {
|
|
fast = qtrue;
|
|
fastgrid = qtrue;
|
|
fastbounce = qtrue;
|
|
Sys_Printf( "Fast mode enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-faster" ) ) {
|
|
faster = qtrue;
|
|
fast = qtrue;
|
|
fastgrid = qtrue;
|
|
fastbounce = qtrue;
|
|
Sys_Printf( "Faster mode enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-fastgrid" ) ) {
|
|
fastgrid = qtrue;
|
|
Sys_Printf( "Fast grid lighting enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-fastbounce" ) ) {
|
|
fastbounce = qtrue;
|
|
Sys_Printf( "Fast bounce mode enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-cheap" ) ) {
|
|
cheap = qtrue;
|
|
cheapgrid = qtrue;
|
|
Sys_Printf( "Cheap mode enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-cheapgrid" ) ) {
|
|
cheapgrid = qtrue;
|
|
Sys_Printf( "Cheap grid mode enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-normalmap" ) ) {
|
|
normalmap = qtrue;
|
|
Sys_Printf( "Storing normal map instead of lightmap\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-trisoup" ) ) {
|
|
trisoup = qtrue;
|
|
Sys_Printf( "Converting brush faces to triangle soup\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-debug" ) ) {
|
|
debug = qtrue;
|
|
Sys_Printf( "Lightmap debugging enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-debugsurfaces" ) || !strcmp( argv[ i ], "-debugsurface" ) ) {
|
|
debugSurfaces = qtrue;
|
|
Sys_Printf( "Lightmap surface debugging enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-debugunused" ) ) {
|
|
debugUnused = qtrue;
|
|
Sys_Printf( "Unused luxel debugging enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-debugaxis" ) ) {
|
|
debugAxis = qtrue;
|
|
Sys_Printf( "Lightmap axis debugging enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-debugcluster" ) ) {
|
|
debugCluster = qtrue;
|
|
Sys_Printf( "Luxel cluster debugging enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-debugorigin" ) ) {
|
|
debugOrigin = qtrue;
|
|
Sys_Printf( "Luxel origin debugging enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-debugdeluxe" ) ) {
|
|
deluxemap = qtrue;
|
|
debugDeluxemap = qtrue;
|
|
Sys_Printf( "Deluxemap debugging enabled\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-export" ) ) {
|
|
exportLightmaps = qtrue;
|
|
Sys_Printf( "Exporting lightmaps\n" );
|
|
}
|
|
|
|
else if ( !strcmp( argv[ i ], "-notrace" ) ) {
|
|
noTrace = qtrue;
|
|
Sys_Printf( "Shadow occlusion disabled\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-patchshadows" ) ) {
|
|
patchShadows = qtrue;
|
|
Sys_Printf( "Patch shadow casting enabled\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-extra" ) ) {
|
|
superSample = EXTRA_SCALE; /* ydnar */
|
|
Sys_Printf( "The -extra argument is deprecated, use \"-super 2\" instead\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-extrawide" ) ) {
|
|
superSample = EXTRAWIDE_SCALE; /* ydnar */
|
|
filter = qtrue; /* ydnar */
|
|
Sys_Printf( "The -extrawide argument is deprecated, use \"-filter [-super 2]\" instead\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-samplesize" ) ) {
|
|
sampleSize = atoi( argv[ i + 1 ] );
|
|
if ( sampleSize < 1 ) {
|
|
sampleSize = 1;
|
|
}
|
|
i++;
|
|
Sys_Printf( "Default lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-novertex" ) ) {
|
|
noVertexLighting = qtrue;
|
|
Sys_Printf( "Disabling vertex lighting\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-nogrid" ) ) {
|
|
noGridLighting = qtrue;
|
|
Sys_Printf( "Disabling grid lighting\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-border" ) ) {
|
|
lightmapBorder = qtrue;
|
|
Sys_Printf( "Adding debug border to lightmaps\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-nosurf" ) ) {
|
|
noSurfaces = qtrue;
|
|
Sys_Printf( "Not tracing against surfaces\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-dump" ) ) {
|
|
dump = qtrue;
|
|
Sys_Printf( "Dumping radiosity lights into numbered prefabs\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-lomem" ) ) {
|
|
loMem = qtrue;
|
|
Sys_Printf( "Enabling low-memory (potentially slower) lighting mode\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-nostyle" ) || !strcmp( argv[ i ], "-nostyles" ) ) {
|
|
noStyles = qtrue;
|
|
Sys_Printf( "Disabling lightstyles\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-cpma" ) ) {
|
|
cpmaHack = qtrue;
|
|
Sys_Printf( "Enabling Challenge Pro Mode Asstacular Vertex Lighting Mode (tm)\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-floodlight" ) ) {
|
|
floodlighty = qtrue;
|
|
Sys_Printf( "FloodLighting enabled\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-debugnormals" ) ) {
|
|
debugnormals = qtrue;
|
|
Sys_Printf( "DebugNormals enabled\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-lowquality" ) ) {
|
|
floodlight_lowquality = qtrue;
|
|
Sys_Printf( "Low Quality FloodLighting enabled\n" );
|
|
}
|
|
|
|
/* r7: dirtmapping */
|
|
else if ( !strcmp( argv[ i ], "-dirty" ) ) {
|
|
dirty = qtrue;
|
|
Sys_Printf( "Dirtmapping enabled\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-dirtdebug" ) || !strcmp( argv[ i ], "-debugdirt" ) ) {
|
|
dirtDebug = qtrue;
|
|
Sys_Printf( "Dirtmap debugging enabled\n" );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-dirtmode" ) ) {
|
|
dirtMode = atoi( argv[ i + 1 ] );
|
|
if ( dirtMode != 0 && dirtMode != 1 ) {
|
|
dirtMode = 0;
|
|
}
|
|
if ( dirtMode == 1 ) {
|
|
Sys_Printf( "Enabling randomized dirtmapping\n" );
|
|
}
|
|
else{
|
|
Sys_Printf( "Enabling ordered dir mapping\n" );
|
|
}
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-dirtdepth" ) ) {
|
|
dirtDepth = atof( argv[ i + 1 ] );
|
|
if ( dirtDepth <= 0.0f ) {
|
|
dirtDepth = 128.0f;
|
|
}
|
|
Sys_Printf( "Dirtmapping depth set to %.1f\n", dirtDepth );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-dirtscale" ) ) {
|
|
dirtScale = atof( argv[ i + 1 ] );
|
|
if ( dirtScale <= 0.0f ) {
|
|
dirtScale = 1.0f;
|
|
}
|
|
Sys_Printf( "Dirtmapping scale set to %.1f\n", dirtScale );
|
|
}
|
|
else if ( !strcmp( argv[ i ], "-dirtgain" ) ) {
|
|
dirtGain = atof( argv[ i + 1 ] );
|
|
if ( dirtGain <= 0.0f ) {
|
|
dirtGain = 1.0f;
|
|
}
|
|
Sys_Printf( "Dirtmapping gain set to %.1f\n", dirtGain );
|
|
}
|
|
|
|
/* unhandled args */
|
|
else{
|
|
Sys_FPrintf( SYS_WRN, "WARNING: Unknown argument \"%s\"\n", argv[ i ] );
|
|
}
|
|
|
|
}
|
|
|
|
/* clean up map name */
|
|
strcpy( source, ExpandArg( argv[ i ] ) );
|
|
StripExtension( source );
|
|
DefaultExtension( source, ".bsp" );
|
|
strcpy( mapSource, ExpandArg( argv[ i ] ) );
|
|
StripExtension( mapSource );
|
|
DefaultExtension( mapSource, ".map" );
|
|
|
|
/* ydnar: set default sample size */
|
|
SetDefaultSampleSize( sampleSize );
|
|
|
|
/* ydnar: handle shaders */
|
|
BeginMapShaderFile( source );
|
|
LoadShaderInfo();
|
|
|
|
/* note loading */
|
|
Sys_Printf( "Loading %s\n", source );
|
|
|
|
/* ydnar: load surface file */
|
|
LoadSurfaceExtraFile( source );
|
|
|
|
/* load bsp file */
|
|
LoadBSPFile( source );
|
|
|
|
/* parse bsp entities */
|
|
ParseEntities();
|
|
|
|
/* load map file */
|
|
value = ValueForKey( &entities[ 0 ], "_keepLights" );
|
|
if ( value[ 0 ] != '1' ) {
|
|
LoadMapFile( mapSource, qtrue );
|
|
}
|
|
|
|
/* set the entity/model origins and init yDrawVerts */
|
|
SetEntityOrigins();
|
|
|
|
/* ydnar: set up optimization */
|
|
SetupBrushes();
|
|
SetupDirt();
|
|
SetupFloodLight();
|
|
SetupSurfaceLightmaps();
|
|
|
|
/* initialize the surface facet tracing */
|
|
SetupTraceNodes();
|
|
|
|
/* light the world */
|
|
LightWorld();
|
|
|
|
/* write out the bsp */
|
|
UnparseEntities();
|
|
Sys_Printf( "Writing %s\n", source );
|
|
WriteBSPFile( source );
|
|
|
|
/* ydnar: export lightmaps */
|
|
if ( exportLightmaps && !externalLightmaps ) {
|
|
ExportLightmaps();
|
|
}
|
|
|
|
/* return to sender */
|
|
return 0;
|
|
}
|