forked from a1batross/Paranoia2_original
1424 lines
38 KiB
C++
1424 lines
38 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
|
*
|
|
* This product contains software technology licensed from Id
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
****/
|
|
|
|
#include "qrad.h"
|
|
#include "model_trace.h"
|
|
#include "..\..\engine\studio.h"
|
|
|
|
#ifdef HLRAD_LIGHTMAPMODELS
|
|
|
|
#define TRI_BORDER 0.4f // FIXME: this is too much
|
|
|
|
typedef struct
|
|
{
|
|
int modelnum : 10;
|
|
int facenum : 22;
|
|
} faceremap_t;
|
|
|
|
typedef struct
|
|
{
|
|
float *point;
|
|
float *coord;
|
|
} trivert_t;
|
|
|
|
static entity_t *g_modellight[MAX_MAP_MODELS];
|
|
static int g_modellight_modnum;
|
|
static faceremap_t *g_modellight_indexes;
|
|
static uint g_modellight_numindexes;
|
|
static float g_nudge[2][9] = {{ 0, -1, 0, 1, -1, 1, -1, 0, 1 }, { 0, -1, -1, -1, 0, 0, 1, 1, 1 }};
|
|
static float g_studio_blur;
|
|
|
|
static void CalcLightmapAxis( tmesh_t *mesh, lface_t *face, trivert_t *a, trivert_t *b, trivert_t *c )
|
|
{
|
|
int ssize = face->texture_step;
|
|
vec3_t mins, maxs, size;
|
|
vec3_t planeNormal;
|
|
int i, axis;
|
|
float d;
|
|
|
|
ClearBounds( mins, maxs );
|
|
AddPointToBounds( a->point, mins, maxs );
|
|
AddPointToBounds( b->point, mins, maxs );
|
|
AddPointToBounds( c->point, mins, maxs );
|
|
|
|
// round to the lightmap resolution
|
|
for( i = 0; i < 3; i++ )
|
|
{
|
|
mins[i] = ssize * floor( mins[i] / ssize );
|
|
maxs[i] = ssize * ceil( maxs[i] / ssize );
|
|
size[i] = (maxs[i] - mins[i]) / ssize;
|
|
}
|
|
|
|
// the two largest axis will be the lightmap size
|
|
planeNormal[0] = fabs( face->normal[0] );
|
|
planeNormal[1] = fabs( face->normal[1] );
|
|
planeNormal[2] = fabs( face->normal[2] );
|
|
|
|
if( planeNormal[0] >= planeNormal[1] && planeNormal[0] >= planeNormal[2] )
|
|
{
|
|
face->extents[0] = size[1];
|
|
face->extents[1] = size[2];
|
|
face->lmvecs[0][1] = 1.0 / ssize;
|
|
face->lmvecs[1][2] = 1.0 / ssize;
|
|
axis = 0;
|
|
}
|
|
else if( planeNormal[1] >= planeNormal[0] && planeNormal[1] >= planeNormal[2] )
|
|
{
|
|
face->extents[0] = size[0];
|
|
face->extents[1] = size[2];
|
|
face->lmvecs[0][0] = 1.0 / ssize;
|
|
face->lmvecs[1][2] = 1.0 / ssize;
|
|
axis = 1;
|
|
}
|
|
else
|
|
{
|
|
face->extents[0] = size[0];
|
|
face->extents[1] = size[1];
|
|
face->lmvecs[0][0] = 1.0 / ssize;
|
|
face->lmvecs[1][1] = 1.0 / ssize;
|
|
axis = 2;
|
|
}
|
|
|
|
if( !face->normal[axis] )
|
|
COM_FatalError( "Chose a 0 valued axis\n" );
|
|
|
|
if( face->extents[0] > ( MAX_STUDIO_LIGHTMAP_SIZE - 1 ))
|
|
{
|
|
VectorScale( face->lmvecs[0], (float)(MAX_STUDIO_LIGHTMAP_SIZE - 1.0f) / face->extents[0], face->lmvecs[0] );
|
|
face->extents[0] = MAX_STUDIO_LIGHTMAP_SIZE - 1;
|
|
}
|
|
|
|
if( face->extents[1] > ( MAX_STUDIO_LIGHTMAP_SIZE - 1 ))
|
|
{
|
|
VectorScale( face->lmvecs[1], (float)(MAX_STUDIO_LIGHTMAP_SIZE - 1.0f) / face->extents[1], face->lmvecs[1] );
|
|
face->extents[1] = MAX_STUDIO_LIGHTMAP_SIZE - 1;
|
|
}
|
|
|
|
// calculate the world coordinates of the lightmap samples
|
|
|
|
// project mins onto plane to get origin
|
|
d = DotProduct( mins, face->normal ) - DotProduct( face->normal, a->point );
|
|
d /= face->normal[axis];
|
|
VectorCopy( mins, face->origin );
|
|
face->origin[axis] -= d;
|
|
|
|
// project stepped lightmap blocks and subtract to get planevecs
|
|
for( i = 0; i < 2 ; i++ )
|
|
{
|
|
vec3_t normalized;
|
|
float len;
|
|
|
|
len = VectorNormalizeLength2( face->lmvecs[i], normalized );
|
|
VectorScale( normalized, (1.0 / len), face->lmvecs[i] );
|
|
d = DotProduct( face->lmvecs[i], face->normal );
|
|
d /= face->normal[axis];
|
|
face->lmvecs[i][axis] -= d;
|
|
}
|
|
|
|
}
|
|
|
|
// we need to compute tangent vectors
|
|
void CalcTriangleVectors( int modelnum, int threadnum = -1 )
|
|
{
|
|
entity_t *mapent = g_modellight[modelnum];
|
|
tmesh_t *mesh;
|
|
trivert_t v[3];
|
|
|
|
// sanity check
|
|
if( !mapent || !mapent->cache )
|
|
return;
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
if( !mesh->verts || mesh->numverts <= 0 )
|
|
return;
|
|
|
|
if( !mesh->faces || mesh->numfaces <= 0 )
|
|
return;
|
|
|
|
// build the smoothed normals
|
|
if( !FBitSet( mesh->flags, FMESH_DONT_SMOOTH ) && g_smoothing_threshold )
|
|
{
|
|
vec3_t *normals = (vec3_t *)Mem_Alloc( mesh->numverts * sizeof( vec3_t ));
|
|
|
|
for( int hashSize = 1; hashSize < mesh->numverts; hashSize <<= 1 );
|
|
hashSize = hashSize >> 2;
|
|
|
|
// build a map from vertex to a list of triangles that share the vert.
|
|
CUtlArray<CIntVector> vertHashMap;
|
|
|
|
vertHashMap.AddMultipleToTail( hashSize );
|
|
|
|
for( int vertID = 0; vertID < mesh->numverts; vertID++ )
|
|
{
|
|
tvert_t *tv = &mesh->verts[vertID];
|
|
uint hash = VertexHashKey( tv->point, hashSize );
|
|
vertHashMap[hash].AddToTail( vertID );
|
|
}
|
|
|
|
for( int hashID = 0; hashID < hashSize; hashID++ )
|
|
{
|
|
for( int i = 0; i < vertHashMap[hashID].Size(); i++ )
|
|
{
|
|
int vertID = vertHashMap[hashID][i];
|
|
tvert_t *tv0 = &mesh->verts[vertID];
|
|
|
|
for( int j = 0; j < vertHashMap[hashID].Size(); j++ )
|
|
{
|
|
tvert_t *tv1 = &mesh->verts[vertHashMap[hashID][j]];
|
|
|
|
if( !VectorCompareEpsilon( tv0->point, tv1->point, ON_EPSILON ))
|
|
continue;
|
|
|
|
if( DotProduct( tv0->normal, tv1->normal ) >= g_smoothing_threshold )
|
|
VectorAdd( normals[vertID], tv1->normal, normals[vertID] );
|
|
}
|
|
}
|
|
}
|
|
|
|
// copy smoothed normals back
|
|
for( int j = 0; j < mesh->numverts; j++ )
|
|
{
|
|
VectorCopy( normals[j], mesh->verts[j].normal );
|
|
VectorNormalize2( mesh->verts[j].normal );
|
|
}
|
|
|
|
Mem_Free( normals );
|
|
}
|
|
|
|
for( int triID = 0; triID < mesh->numfaces; triID++ )
|
|
{
|
|
tface_t *face = &mesh->faces[triID];
|
|
|
|
if( !face->light ) continue;
|
|
|
|
// get positions and UV from three points
|
|
v[0].point = (float *)&mesh->verts[face->a].point;
|
|
v[0].coord = (float *)&mesh->verts[face->a].st;
|
|
v[1].point = (float *)&mesh->verts[face->b].point;
|
|
v[1].coord = (float *)&mesh->verts[face->b].st;
|
|
v[2].point = (float *)&mesh->verts[face->c].point;
|
|
v[2].coord = (float *)&mesh->verts[face->c].st;
|
|
face->light->texture_step = mesh->texture_step;
|
|
|
|
if( VectorIsNull( face->light->normal ))
|
|
continue;
|
|
|
|
CalcLightmapAxis( mesh, face->light, &v[0], &v[1], &v[2] );
|
|
}
|
|
}
|
|
|
|
bool GetTrianglePhongNormal( lightinfo_t *l, const vec3_t spot, vec3_t phongnormal )
|
|
{
|
|
tface_t *face = l->tface;
|
|
tvert_t *a = &l->mesh->verts[face->a];
|
|
tvert_t *b = &l->mesh->verts[face->b];
|
|
tvert_t *c = &l->mesh->verts[face->c];
|
|
vec3_t edge1, edge2, p0, p1;
|
|
float uu, uv, vv, wu, wv;
|
|
float d1, d2, d, frac;
|
|
vec3_t q, n, p;
|
|
float u, v, w;
|
|
|
|
// setup fallback
|
|
VectorCopy( l->tface->light->normal, phongnormal );
|
|
#if 0
|
|
VectorSubtract( b->point, a->point, edge1 );
|
|
VectorSubtract( c->point, a->point, edge2 );
|
|
CrossProduct( edge2, edge1, n );
|
|
VectorMA( spot, DEFAULT_HUNT_OFFSET, n, p0 );
|
|
VectorMA( spot,-DEFAULT_HUNT_OFFSET, n, p1 );
|
|
|
|
VectorSubtract( p1, p0, p );
|
|
VectorSubtract( p0, a->point, q );
|
|
|
|
d1 = -DotProduct( n, q );
|
|
d2 = DotProduct( n, p );
|
|
|
|
if( fabs( d2 ) < FRAC_EPSILON )
|
|
return false; // parallel with plane
|
|
|
|
// get intersect point of ray with triangle plane
|
|
frac = d1 / d2;
|
|
if( frac < -0.001f ) return false;
|
|
|
|
// calculate the impact point
|
|
VectorLerp( p0, frac, p1, p );
|
|
|
|
// does p lie inside triangle?
|
|
uu = DotProduct( edge1, edge1 );
|
|
uv = DotProduct( edge1, edge2 );
|
|
vv = DotProduct( edge2, edge2 );
|
|
|
|
VectorSubtract( p, a->point, q );
|
|
wu = DotProduct( q, edge1 );
|
|
wv = DotProduct( q, edge2 );
|
|
d = uv * uv - uu * vv;
|
|
|
|
// get and test parametric coords
|
|
u = (uv * wv - vv * wu) / d;
|
|
if( u < -TRI_BORDER || u > ( 1.0f + TRI_BORDER ))
|
|
return false; // p is outside
|
|
|
|
v = (uv * wu - uu * wv) / d;
|
|
if( v < -TRI_BORDER || (u + v) > ( 1.0f + TRI_BORDER ))
|
|
return false; // p is outside
|
|
// calculate w parameter
|
|
w = 1.0f - ( u + v );
|
|
#else
|
|
// now, check 3 edges
|
|
float hitc1 = spot[face->pcoord0] + ( -phongnormal[face->pcoord0] );
|
|
float hitc2 = spot[face->pcoord1] + ( -phongnormal[face->pcoord1] );
|
|
|
|
// do barycentric coordinate check
|
|
v = face->edge1[0] * hitc1 + face->edge1[1] * hitc2 + face->edge1[2];
|
|
if( v < -TRI_BORDER ) return false;
|
|
|
|
w = face->edge2[0] * hitc1 + face->edge2[1] * hitc2 + face->edge2[2];
|
|
if( w < -TRI_BORDER ) return false;
|
|
|
|
u = v + w;
|
|
if( u > 1.0f + TRI_BORDER ) return false;
|
|
|
|
// calculate w parameter
|
|
u = 1.0f - u;
|
|
#endif
|
|
if( g_smoothing_threshold > 0.0 )
|
|
{
|
|
u = bound( 0.0f, u, 1.0f );
|
|
v = bound( 0.0f, v, 1.0f );
|
|
w = bound( 0.0f, w, 1.0f );
|
|
|
|
// calculate st from uvw (barycentric) coordinates
|
|
phongnormal[0] = w * a->normal[0] + u * b->normal[0] + v * c->normal[0];
|
|
phongnormal[1] = w * a->normal[1] + u * b->normal[1] + v * c->normal[1];
|
|
phongnormal[2] = w * a->normal[2] + u * b->normal[2] + v * c->normal[2];
|
|
// VectorNormalize2( phongnormal );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CalcTriangleExtents
|
|
|
|
Fills in s->texmins[] and s->texsize[]
|
|
also sets exactmins[] and exactmaxs[]
|
|
================
|
|
*/
|
|
void CalcTriangleExtents( lightinfo_t *l )
|
|
{
|
|
l->texsize[0] = l->tface->light->extents[0];
|
|
l->texsize[1] = l->tface->light->extents[1];
|
|
|
|
if( l->texsize[0] * l->texsize[1] > ( MAX_SINGLEMAP_MODEL / 3 ))
|
|
COM_FatalError( "surface to large to map %d > %d\n", l->texsize[0] * l->texsize[1], ( MAX_SINGLEMAP_MODEL / 3 ));
|
|
|
|
if( l->texsize[0] < 0 || l->texsize[1] < 0 )
|
|
COM_FatalError( "negative extents\n" );
|
|
|
|
l->lmcache_density = 1;
|
|
l->lmcache_side = (int)ceil(( 0.5 * g_studio_blur * l->lmcache_density - 0.5 ) * ( 1.0 - NORMAL_EPSILON ));
|
|
l->lmcache_offset = l->lmcache_side;
|
|
l->lmcachewidth = l->texsize[0] * l->lmcache_density + 1 + 2 * l->lmcache_side;
|
|
l->lmcacheheight = l->texsize[1] * l->lmcache_density + 1 + 2 * l->lmcache_side;
|
|
|
|
l->light = (vec3_t (*)[MAXLIGHTMAPS])Mem_Alloc( l->lmcachewidth * l->lmcacheheight * sizeof( vec3_t[MAXLIGHTMAPS] ));
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
l->deluxe = (vec3_t (*)[MAXLIGHTMAPS])Mem_Alloc( l->lmcachewidth * l->lmcacheheight * sizeof( vec3_t[MAXLIGHTMAPS] ));
|
|
l->normals = (vec3_t *)Mem_Alloc( l->lmcachewidth * l->lmcacheheight * sizeof( vec3_t ));
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
l->shadow = (vec_t (*)[MAXLIGHTMAPS])Mem_Alloc( l->lmcachewidth * l->lmcacheheight * sizeof( vec_t[MAXLIGHTMAPS] ));
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CalcPoints
|
|
|
|
For each texture aligned grid point, back project onto the plane
|
|
to get the world xyz value of the sample point
|
|
=================
|
|
*/
|
|
void CalcTrianglePoints( lightinfo_t *l, float sofs, float tofs )
|
|
{
|
|
int h = l->texsize[1] + 1;
|
|
int w = l->texsize[0] + 1;
|
|
lface_t *face = l->tface->light;
|
|
int s, t, i, j;
|
|
dleaf_t *leaf;
|
|
|
|
l->surfpt = (surfpt_t *)Mem_Alloc( w * h * sizeof( surfpt_t ));
|
|
l->numsurfpt = w * h;
|
|
|
|
for( t = 0; t < h; t++ )
|
|
{
|
|
for( s = 0; s < w; s++ )
|
|
{
|
|
surfpt_t *surf = &l->surfpt[s+w*t];
|
|
|
|
// calculate texture point
|
|
for( j = 0; j < 3; j++ )
|
|
surf->point[j] = face->origin[j] + face->normal[j] + s * face->lmvecs[0][j] + t * face->lmvecs[1][j];
|
|
|
|
// we may need to slightly nudge the sample point
|
|
// if directly on a wall
|
|
for( i = 0; i < 9; i++ )
|
|
{
|
|
// calculate texture point
|
|
for( j = 0; j < 3; j++ )
|
|
surf->position[j] = surf->point[j]
|
|
+ ( g_nudge[0][i] / 16.0f ) * face->lmvecs[0][j]
|
|
+ ( g_nudge[1][i] / 16.0f ) * face->lmvecs[1][j];
|
|
|
|
leaf = PointInLeaf( surf->position );
|
|
|
|
if( leaf->contents != CONTENTS_SOLID )
|
|
{
|
|
// if( TestLine( -1, face->origin, surf->position ) == CONTENTS_EMPTY )
|
|
break; // got it
|
|
}
|
|
}
|
|
|
|
if( i == 9 ) surf->occluded = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
InitLightinfo
|
|
=============
|
|
*/
|
|
void InitModelLightinfo( lightinfo_t *l, entity_t *mapent, tmesh_t *mesh, tface_t *tf )
|
|
{
|
|
memset( l, 0, sizeof( *l ));
|
|
l->mapent = mapent;
|
|
l->mesh = mesh;
|
|
l->tface = tf;
|
|
|
|
CalcTriangleExtents( l );
|
|
}
|
|
|
|
static void CalcModelLightmap( int thread, lightinfo_t *l, facelight_t *fl )
|
|
{
|
|
vec_t density = (vec_t)l->lmcache_density;
|
|
vec_t texture_step = l->tface->light->texture_step;
|
|
int w = l->texsize[0] + 1;
|
|
int h = l->texsize[1] + 1;
|
|
lface_t *f = l->tface->light;
|
|
byte *vislight = NULL;
|
|
int i, j, count;
|
|
vec3_t acolor, adelux;
|
|
float ashadow;
|
|
|
|
#ifdef HLRAD_COMPUTE_VISLIGHTMATRIX
|
|
vislight = l->mesh->vislight;
|
|
#endif
|
|
ASSERT( l->numsurfpt > 0 );
|
|
|
|
// allocate light samples
|
|
fl->samples = (sample_t *)Mem_Alloc( l->numsurfpt * sizeof( sample_t ));
|
|
fl->numsamples = l->numsurfpt;
|
|
|
|
// stats
|
|
g_direct_luxels[thread] += fl->numsamples;
|
|
|
|
// copy surf points from lightinfo with offset 0,0
|
|
for( i = 0; i < fl->numsamples; i++ )
|
|
{
|
|
VectorCopy( l->surfpt[i].point, fl->samples[i].pos );
|
|
fl->samples[i].occluded = l->surfpt[i].occluded;
|
|
fl->samples[i].surface = l->surfpt[i].surface;
|
|
}
|
|
|
|
// for each sample whose light we need to calculate
|
|
for( i = 0; i < l->lmcachewidth * l->lmcacheheight; i++ )
|
|
{
|
|
vec3_t pointnormal;
|
|
vec3_t spot, surfpt;
|
|
bool blocked;
|
|
|
|
// prepare input parameter and output parameter
|
|
vec_t s = ((i % l->lmcachewidth) - l->lmcache_offset) / (vec_t)l->lmcache_density;
|
|
vec_t t = ((i / l->lmcachewidth) - l->lmcache_offset) / (vec_t)l->lmcache_density;
|
|
int nearest_s = Q_max( 0, Q_min((int)floor( s + 0.5 ), l->texsize[0] ));
|
|
int nearest_t = Q_max( 0, Q_min((int)floor( t + 0.5 ), l->texsize[1] ));
|
|
|
|
j = nearest_s + w * nearest_t;
|
|
|
|
VectorCopy( l->surfpt[j].position, surfpt );
|
|
VectorMA( surfpt, DEFAULT_HUNT_OFFSET, f->normal, spot );
|
|
|
|
// calculate normal for the sample
|
|
if( !GetTrianglePhongNormal( l, surfpt, pointnormal ))
|
|
l->surfpt[j].occluded = true;
|
|
|
|
// find world's position for the sample
|
|
blocked = l->surfpt[j].occluded;
|
|
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorCopy( pointnormal, l->normals[i] );
|
|
#endif
|
|
if( blocked ) continue;
|
|
|
|
// calculate visibility for the sample
|
|
int leaf = PointInLeaf( spot ) - g_dleafs;
|
|
|
|
// gather light
|
|
#if defined( HLRAD_DELUXEMAPPING ) && defined( HLRAD_SHADOWMAPPING )
|
|
GatherSampleLight( thread, -1, spot, leaf, pointnormal, l->light[i], l->deluxe[i], l->shadow[i], f->styles, vislight, 0, l->mapent );
|
|
GatherSampleLight( thread, -1, spot, leaf, pointnormal, l->light[i], l->deluxe[i], l->shadow[i], f->styles, vislight, 1, l->mapent );
|
|
#elif defined( HLRAD_DELUXEMAPPING )
|
|
GatherSampleLight( thread, -1, spot, leaf, pointnormal, l->light[i], l->deluxe[i], NULL, f->styles, vislight, 0, l->mapent );
|
|
GatherSampleLight( thread, -1, spot, leaf, pointnormal, l->light[i], l->deluxe[i], NULL, f->styles, vislight, 1, l->mapent );
|
|
#else
|
|
GatherSampleLight( thread, -1, spot, leaf, pointnormal, l->light[i], NULL, NULL, f->styles, vislight, 0, l->mapent );
|
|
GatherSampleLight( thread, -1, spot, leaf, pointnormal, l->light[i], NULL, NULL, f->styles, vislight, 1, l->mapent );
|
|
#endif
|
|
}
|
|
|
|
for( i = 0; i < fl->numsamples; i++ )
|
|
{
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
vec_t weighting_correction;
|
|
vec3_t centernormal;
|
|
#endif
|
|
int s_center, t_center;
|
|
vec_t weighting, subsamples;
|
|
int s, t, pos;
|
|
vec_t sizehalf;
|
|
|
|
s_center = (i % w) * l->lmcache_density + l->lmcache_offset;
|
|
t_center = (i / w) * l->lmcache_density + l->lmcache_offset;
|
|
sizehalf = 0.5 * g_studio_blur * l->lmcache_density;
|
|
subsamples = 0.0;
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorCopy( l->normals[s_center + l->lmcachewidth * t_center], centernormal );
|
|
#endif
|
|
for( s = s_center - l->lmcache_side; s <= s_center + l->lmcache_side; s++ )
|
|
{
|
|
for( t = t_center - l->lmcache_side; t <= t_center + l->lmcache_side; t++ )
|
|
{
|
|
weighting = (Q_min( 0.5, sizehalf - ( s - s_center )) - Q_max( -0.5, -sizehalf - ( s - s_center )));
|
|
weighting *=(Q_min( 0.5, sizehalf - ( t - t_center )) - Q_max( -0.5, -sizehalf - ( t - t_center )));
|
|
|
|
pos = s + l->lmcachewidth * t;
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
// when blur distance (g_blur) is large, the subsample can be very far from the original lightmap sample
|
|
// in some cases such as a thin cylinder, the subsample can even grow into the opposite side
|
|
// as a result, when exposed to a directional light, the light on the cylinder may "leak" into
|
|
// the opposite dark side this correction limits the effect of blur distance when the normal changes very fast
|
|
// this correction will not break the smoothness that HLRAD_GROWSAMPLE ensures
|
|
weighting_correction = DotProduct( l->normals[pos], centernormal );
|
|
weighting_correction = (weighting_correction > 0) ? weighting_correction * weighting_correction : 0;
|
|
weighting = weighting * weighting_correction;
|
|
#endif
|
|
for( j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
|
|
{
|
|
VectorMA( fl->samples[i].light[j], weighting, l->light[pos][j], fl->samples[i].light[j] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorMA( fl->samples[i].deluxe[j], weighting, l->deluxe[pos][j], fl->samples[i].deluxe[j] );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
fl->samples[i].shadow[j] += l->shadow[pos][j] * weighting;
|
|
#endif
|
|
#endif
|
|
}
|
|
subsamples += weighting;
|
|
}
|
|
}
|
|
|
|
if( subsamples > NORMAL_EPSILON )
|
|
{
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorCopy( centernormal, fl->samples[i].normal );
|
|
#endif
|
|
for( j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
|
|
{
|
|
VectorScale( fl->samples[i].light[j], (1.0 / subsamples), fl->samples[i].light[j] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorScale( fl->samples[i].deluxe[j], (1.0 / subsamples), fl->samples[i].deluxe[j] );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
fl->samples[i].shadow[j] *= (1.0 / subsamples);
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
// multiply light by shadow to prevent blur artifacts
|
|
for( i = 0; i < fl->numsamples; i++ )
|
|
{
|
|
for( j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
|
|
{
|
|
// VectorScale( fl->samples[i].light[j], fl->samples[i].shadow[j], fl->samples[i].light[j] );
|
|
// VectorScale( fl->samples[i].deluxe[j], fl->samples[i].shadow[j], fl->samples[i].deluxe[j] );
|
|
}
|
|
}
|
|
|
|
// output occlusion shouldn't be blured
|
|
for( i = 0; i < fl->numsamples; i++ )
|
|
{
|
|
for( j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
|
|
{
|
|
int s = (i % w) + l->lmcache_side;
|
|
int t = (i / w) + l->lmcache_side;
|
|
int pos = s + l->lmcachewidth * t;
|
|
fl->samples[i].shadow[j] = l->shadow[pos][j];
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
// calculate average values for occluded samples
|
|
for( int k = 0; k < MAXLIGHTMAPS && f->styles[k] != 255; k++ )
|
|
{
|
|
for( i = 0; i < w; i++ )
|
|
{
|
|
for( j = 0; j < h; j++ )
|
|
{
|
|
int pos0 = i+w*j;
|
|
if( !l->surfpt[pos0].occluded )
|
|
continue;
|
|
|
|
int step = 1; // 3x3
|
|
|
|
// scan all surrounding samples
|
|
VectorClear( acolor );
|
|
VectorClear( adelux );
|
|
ashadow = 0.0f;
|
|
count = 0;
|
|
|
|
for( int x = -step; x <= step; x++ )
|
|
{
|
|
for( int y = -step; y <= step; y++ )
|
|
{
|
|
if( i + x < 0 || i + x >= w )
|
|
continue;
|
|
|
|
if( j + y < 0 || j + y >= h )
|
|
continue;
|
|
|
|
int pos1 = (i+x)+w*(j+y);
|
|
if( l->surfpt[pos1].occluded )
|
|
continue;
|
|
|
|
VectorAdd( fl->samples[pos1].light[k], acolor, acolor );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorAdd( fl->samples[pos1].deluxe[k], adelux, adelux );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
ashadow += fl->samples[pos1].shadow[k];
|
|
#endif
|
|
#endif
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if( !count ) continue;
|
|
VectorScale( acolor, 1.0f / count, fl->samples[pos0].light[k] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorScale( adelux, 1.0f / count, fl->samples[pos0].deluxe[k] );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
fl->samples[pos0].shadow[k] = ashadow * (1.0f / count);
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
Mem_Free( l->light );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
Mem_Free( l->normals );
|
|
Mem_Free( l->deluxe );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
Mem_Free( l->shadow );
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void BuildModelLightmaps( int indexnum, int thread = -1 )
|
|
{
|
|
int modelnum = g_modellight_indexes[indexnum].modelnum;
|
|
int facenum = g_modellight_indexes[indexnum].facenum;
|
|
entity_t *mapent = g_modellight[modelnum];
|
|
entity_t *ignoreent = NULL;
|
|
byte *vislight = NULL;
|
|
tmesh_t *mesh;
|
|
int i, j;
|
|
sample_t *s;
|
|
lightinfo_t l;
|
|
|
|
// sanity check
|
|
if( !mapent || !mapent->cache )
|
|
return;
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
if( !mesh->verts || mesh->numverts <= 0 )
|
|
return;
|
|
|
|
if( !mesh->faces || mesh->numfaces <= 0 )
|
|
return;
|
|
|
|
if( !FBitSet( mesh->flags, FMESH_SELF_SHADOW ))
|
|
ignoreent = mapent;
|
|
|
|
// lightmaps onto models it's more compilcated than vertexlighting...
|
|
if( !mesh->faces[facenum].light ) return;
|
|
lface_t *f = mesh->faces[facenum].light;
|
|
tface_t *tf = &mesh->faces[facenum];
|
|
facelight_t *fl = &f->facelight;
|
|
f->lightofs = -1;
|
|
|
|
if( VectorIsNull( f->normal ))
|
|
return;
|
|
|
|
InitModelLightinfo( &l, ignoreent, mesh, tf );
|
|
CalcTrianglePoints( &l, 0.0f, 0.0f );
|
|
CalcModelLightmap( thread, &l, fl );
|
|
|
|
Mem_Free( l.surfpt );
|
|
|
|
// add an ambient term if desired
|
|
if( g_ambient[0] || g_ambient[1] || g_ambient[2] )
|
|
{
|
|
for( j = 0; j < MAXLIGHTMAPS && f->styles[j] == 255; j++ );
|
|
if( j == MAXLIGHTMAPS ) f->styles[0] = 0; // adding style
|
|
|
|
for( j = 0; j < MAXLIGHTMAPS && f->styles[j] != 255; j++ )
|
|
{
|
|
if( f->styles[j] == 0 )
|
|
{
|
|
s = fl->samples;
|
|
for( i = 0; i < fl->numsamples; i++, s++ )
|
|
{
|
|
VectorAdd( s->light[j], g_ambient, s->light[j] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
vec_t avg = VectorAvg( g_ambient );
|
|
VectorMA( s->deluxe[j], -DIFFUSE_DIRECTION_SCALE * avg, s->normal, s->deluxe[j] );
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
CalcModelSampleSize
|
|
============
|
|
*/
|
|
void CalcModelSampleSize( void )
|
|
{
|
|
facelight_t *fl;
|
|
tface_t *f;
|
|
size_t samples_total_size = 0;
|
|
tmesh_t *mesh;
|
|
|
|
for( int i = 0; i < g_modellight_numindexes; i++ )
|
|
{
|
|
int modelnum = g_modellight_indexes[i].modelnum;
|
|
int facenum = g_modellight_indexes[i].facenum;
|
|
entity_t *mapent = g_modellight[modelnum];
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
f = &mesh->faces[facenum];
|
|
if( !f->light ) continue;
|
|
fl = &f->light->facelight;
|
|
|
|
samples_total_size += fl->numsamples * sizeof( sample_t );
|
|
}
|
|
|
|
Msg( "total studiolight data: %s\n", Q_memprint( samples_total_size ));
|
|
}
|
|
|
|
/*
|
|
============
|
|
PrecompModelLightmapOffsets
|
|
============
|
|
*/
|
|
void PrecompModelLightmapOffsets( void )
|
|
{
|
|
int lightstyles;
|
|
tmesh_t *mesh;
|
|
facelight_t *fl;
|
|
lface_t *f;
|
|
|
|
for( int l = 0; l < g_modellight_numindexes; l++ )
|
|
{
|
|
int modelnum = g_modellight_indexes[l].modelnum;
|
|
int facenum = g_modellight_indexes[l].facenum;
|
|
entity_t *mapent = g_modellight[modelnum];
|
|
vec3_t maxlights1[MAXSTYLES];
|
|
vec3_t maxlights2[MAXSTYLES];
|
|
vec_t maxlights[MAXSTYLES];
|
|
int i, j, k;
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
f = mesh->faces[facenum].light;
|
|
if( !f ) continue;
|
|
fl = &f->facelight;
|
|
|
|
for( j = 0; j < MAXSTYLES; j++ )
|
|
{
|
|
VectorClear( maxlights1[j] );
|
|
VectorClear( maxlights2[j] );
|
|
}
|
|
|
|
for( k = 0; k < MAXLIGHTMAPS && f->styles[k] != 255; k++ )
|
|
{
|
|
for( i = 0; i < fl->numsamples; i++ )
|
|
{
|
|
VectorCompareMax( maxlights1[f->styles[k]], fl->samples[i].light[k], maxlights1[f->styles[k]] );
|
|
}
|
|
}
|
|
#ifdef LATER
|
|
int numpatches;
|
|
const int *patches;
|
|
|
|
GetTriangulationPatches( facenum, &numpatches, &patches ); // collect patches and their neighbors
|
|
|
|
for( i = 0; i < numpatches; i++ )
|
|
{
|
|
patch_t *patch = &g_patches[patches[i]];
|
|
|
|
for( k = 0; k < MAXLIGHTMAPS && patch->totalstyle[k] != 255; k++ )
|
|
{
|
|
VectorCompareMax( maxlights2[patch->totalstyle[k]], patch->totallight[k], maxlights2[patch->totalstyle[k]] );
|
|
}
|
|
}
|
|
#endif
|
|
for( j = 0; j < MAXSTYLES; j++ )
|
|
{
|
|
vec3_t v;
|
|
|
|
VectorAdd( maxlights1[j], maxlights2[j], v );
|
|
maxlights[j] = VectorMaximum( v );
|
|
|
|
if( maxlights[j] <= EQUAL_EPSILON )
|
|
maxlights[j] = 0;
|
|
}
|
|
|
|
byte oldstyles[MAXLIGHTMAPS];
|
|
sample_t *oldsamples = (sample_t *)Mem_Alloc( sizeof( sample_t ) * fl->numsamples );
|
|
|
|
for( k = 0; k < MAXLIGHTMAPS; k++ )
|
|
oldstyles[k] = f->styles[k];
|
|
|
|
// make backup and clear the source
|
|
for( k = 0; k < fl->numsamples; k++ )
|
|
{
|
|
for( j = 0; j < MAXLIGHTMAPS; j++ )
|
|
{
|
|
VectorCopy( fl->samples[k].light[j], oldsamples[k].light[j] );
|
|
VectorClear( fl->samples[k].light[j] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorCopy( fl->samples[k].deluxe[j], oldsamples[k].deluxe[j] );
|
|
VectorClear( fl->samples[k].deluxe[j] );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
oldsamples[k].shadow[j] = fl->samples[k].shadow[j];
|
|
fl->samples[k].shadow[j] = 0.0f;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
for( k = 0; k < MAXLIGHTMAPS; k++ )
|
|
{
|
|
byte beststyle = 255;
|
|
|
|
if( k == 0 )
|
|
{
|
|
beststyle = 0;
|
|
}
|
|
else
|
|
{
|
|
vec_t bestmaxlight = 0;
|
|
|
|
for( j = 1; j < MAXSTYLES; j++ )
|
|
{
|
|
if( maxlights[j] > bestmaxlight + NORMAL_EPSILON )
|
|
{
|
|
bestmaxlight = maxlights[j];
|
|
beststyle = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( beststyle != 255 )
|
|
{
|
|
maxlights[beststyle] = 0;
|
|
f->styles[k] = beststyle;
|
|
|
|
for( i = 0; i < MAXLIGHTMAPS && oldstyles[i] != 255; i++ )
|
|
{
|
|
if( oldstyles[i] == f->styles[k] )
|
|
break;
|
|
}
|
|
|
|
if( i < MAXLIGHTMAPS && oldstyles[i] != 255 )
|
|
{
|
|
for( j = 0; j < fl->numsamples; j++ )
|
|
{
|
|
VectorCopy( oldsamples[j].light[i], fl->samples[j].light[k] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorCopy( oldsamples[j].deluxe[i], fl->samples[j].deluxe[k] );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
fl->samples[j].shadow[k] = oldsamples[j].shadow[i];
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( j = 0; j < fl->numsamples; j++ )
|
|
{
|
|
VectorClear( fl->samples[j].light[k] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorClear( fl->samples[j].deluxe[k] );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
fl->samples[j].shadow[k] = 0.0f;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f->styles[k] = 255;
|
|
|
|
for( j = 0; j < fl->numsamples; j++ )
|
|
{
|
|
VectorClear( fl->samples[j].light[k] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorClear( fl->samples[j].deluxe[k] );
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
fl->samples[j].shadow[k] = 0.0f;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
Mem_Free( oldsamples );
|
|
|
|
for( lightstyles = 0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
|
|
{
|
|
if( f->styles[lightstyles] == 255 )
|
|
break; // end if styles
|
|
}
|
|
|
|
if( !lightstyles ) continue;
|
|
|
|
f->lightofs = g_lightdatasize;
|
|
g_lightdatasize += fl->numsamples * 3 * lightstyles;
|
|
}
|
|
}
|
|
|
|
/*
|
|
============
|
|
ScaleModelDirectLights
|
|
============
|
|
*/
|
|
void ScaleModelDirectLights( void )
|
|
{
|
|
tmesh_t *mesh;
|
|
sample_t *samp;
|
|
facelight_t *fl;
|
|
lface_t *f;
|
|
|
|
for( int i = 0; i < g_modellight_numindexes; i++ )
|
|
{
|
|
int modelnum = g_modellight_indexes[i].modelnum;
|
|
int facenum = g_modellight_indexes[i].facenum;
|
|
entity_t *mapent = g_modellight[modelnum];
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
f = mesh->faces[facenum].light;
|
|
if( !f ) continue;
|
|
fl = &f->facelight;
|
|
|
|
for( int k = 0; k < MAXLIGHTMAPS && f->styles[k] != 255; k++ )
|
|
{
|
|
for( int i = 0; i < fl->numsamples; i++ )
|
|
{
|
|
samp = &fl->samples[i];
|
|
VectorScale( samp->light[k], g_direct_scale, samp->light[k] );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorScale( samp->deluxe[k], g_direct_scale, samp->deluxe[k] );
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FinalModelLightFace( int indexnum, int threadnum )
|
|
{
|
|
int modelnum = g_modellight_indexes[indexnum].modelnum;
|
|
int facenum = g_modellight_indexes[indexnum].facenum;
|
|
entity_t *mapent = g_modellight[modelnum];
|
|
int lightstyles;
|
|
float minlight;
|
|
int i, j, k;
|
|
sample_t *samp;
|
|
tmesh_t *mesh;
|
|
facelight_t *fl;
|
|
lface_t *f;
|
|
vec3_t lb;
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
f = mesh->faces[facenum].light;
|
|
if( !f ) return;
|
|
fl = &f->facelight;
|
|
|
|
for( lightstyles = 0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
|
|
{
|
|
if( f->styles[lightstyles] == 255 )
|
|
break;
|
|
}
|
|
|
|
if( !lightstyles ) return;
|
|
|
|
// wrote styles into mesh to determine completely black models
|
|
for( i = 0; i < MAXLIGHTMAPS && f->styles[i] != 255; i++ )
|
|
{
|
|
for( k = 0; k < MAXLIGHTMAPS; k++ )
|
|
{
|
|
if( mesh->styles[k] == f->styles[i] || mesh->styles[k] == 255 )
|
|
break;
|
|
}
|
|
|
|
// for our purpoces obviously overflow doesn't matter
|
|
if( k == MAXLIGHTMAPS )
|
|
continue;
|
|
|
|
// allocate a new one
|
|
if( mesh->styles[k] == 255 )
|
|
mesh->styles[k] = f->styles[i];
|
|
}
|
|
|
|
minlight = FloatForKey( mapent, "_minlight" );
|
|
if( minlight < 1.0 ) minlight *= 128.0f; // GoldSrc
|
|
else minlight *= 0.5f; // Quake
|
|
|
|
if( g_lightbalance )
|
|
minlight *= g_direct_scale;
|
|
if( g_numbounce > 0 ) minlight = 0.0f; // ignore for radiosity
|
|
|
|
for( k = 0; k < lightstyles; k++ )
|
|
{
|
|
samp = fl->samples;
|
|
|
|
for( j = 0; j < fl->numsamples; j++, samp++ )
|
|
{
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
vec3_t directionnormals[3];
|
|
vec3_t texdirections[2];
|
|
vec3_t direction;
|
|
int side;
|
|
vec3_t v;
|
|
|
|
VectorCopy( samp->normal, directionnormals[2] );
|
|
|
|
for( side = 0; side < 2; side++ )
|
|
{
|
|
CrossProduct( f->normal, f->lmvecs[!side], texdirections[side] );
|
|
VectorNormalize( texdirections[side] );
|
|
if( DotProduct( texdirections[side], f->lmvecs[side]) < 0.0f )
|
|
VectorNegate( texdirections[side], texdirections[side] );
|
|
}
|
|
|
|
for( side = 0; side < 2; side++ )
|
|
{
|
|
vec_t dot = DotProduct( texdirections[side], samp->normal );
|
|
VectorMA( texdirections[side], -dot, samp->normal, directionnormals[side] );
|
|
VectorNormalize( directionnormals[side] );
|
|
}
|
|
#endif
|
|
VectorCopy( samp->light[k], lb );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
VectorCopy( samp->deluxe[k], direction );
|
|
vec_t avg = VectorAvg( lb );
|
|
VectorScale( direction, 1.0 / Q_max( 1.0, avg ), direction );
|
|
#endif
|
|
// clip from the bottom first
|
|
lb[0] = Q_max( lb[0], minlight );
|
|
lb[1] = Q_max( lb[1], minlight );
|
|
lb[2] = Q_max( lb[2], minlight );
|
|
|
|
// clip from the top
|
|
if( lb[0] > g_maxlight || lb[1] > g_maxlight || lb[2] > g_maxlight )
|
|
{
|
|
// find max value and scale the whole color down;
|
|
float max = VectorMax( lb );
|
|
|
|
for( i = 0; i < 3; i++ )
|
|
lb[i] = ( lb[i] * g_maxlight ) / max;
|
|
}
|
|
|
|
// do gamma adjust
|
|
lb[0] = (float)pow( lb[0] / 256.0f, g_gamma ) * 256.0f;
|
|
lb[1] = (float)pow( lb[1] / 256.0f, g_gamma ) * 256.0f;
|
|
lb[2] = (float)pow( lb[2] / 256.0f, g_gamma ) * 256.0f;
|
|
|
|
#ifdef HLRAD_RIGHTROUND // when you go down, when you go down down!
|
|
g_dlightdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 0] = Q_rint( lb[0] );
|
|
g_dlightdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 1] = Q_rint( lb[1] );
|
|
g_dlightdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 2] = Q_rint( lb[2] );
|
|
#else
|
|
g_dlightdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 0] = (byte)lb[0];
|
|
g_dlightdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 1] = (byte)lb[1];
|
|
g_dlightdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 2] = (byte)lb[2];
|
|
#endif
|
|
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
if( g_deluxdatasize )
|
|
{
|
|
VectorScale( direction, 0.225, v ); // the scale is calculated such that length( v ) < 1
|
|
|
|
if( DotProduct( v, v ) > ( 1.0 - NORMAL_EPSILON ))
|
|
VectorNormalize( v );
|
|
|
|
VectorNegate( v, v ); // let the direction point from face sample to light source
|
|
|
|
for( int x = 0; x < 3; x++ )
|
|
{
|
|
lb[x] = DotProduct( v, directionnormals[x] ) * 127.0f + 128.0f;
|
|
lb[x] = bound( 0, lb[x], 255.0 );
|
|
}
|
|
|
|
g_ddeluxdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 0] = (byte)lb[0];
|
|
g_ddeluxdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 1] = (byte)lb[1];
|
|
g_ddeluxdata[f->lightofs + k * fl->numsamples * 3 + j * 3 + 2] = (byte)lb[2];
|
|
}
|
|
#ifdef HLRAD_SHADOWMAPPING
|
|
if( g_shadowdatasize )
|
|
g_dshadowdata[(f->lightofs / 3) + k * fl->numsamples + j] = (byte)samp->shadow[k] * 255;
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void FreeModelFaceLights( void )
|
|
{
|
|
facelight_t *fl;
|
|
lface_t *f;
|
|
tmesh_t *mesh;
|
|
|
|
for( int i = 0; i < g_modellight_numindexes; i++ )
|
|
{
|
|
int modelnum = g_modellight_indexes[i].modelnum;
|
|
int facenum = g_modellight_indexes[i].facenum;
|
|
entity_t *mapent = g_modellight[modelnum];
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
f = mesh->faces[facenum].light;
|
|
if( !f ) continue;
|
|
fl = &f->facelight;
|
|
Mem_Free( fl->samples );
|
|
}
|
|
}
|
|
|
|
void ReduceModelLightmap( byte *oldlightdata, byte *olddeluxdata, byte *oldshadowdata )
|
|
{
|
|
facelight_t *fl;
|
|
lface_t *f;
|
|
tmesh_t *mesh;
|
|
|
|
// first clearing old lightstyles
|
|
for( int i = 0; i < g_modellight_modnum; i++ )
|
|
{
|
|
entity_t *mapent = g_modellight[i];
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
mesh->styles[0] = 255;
|
|
mesh->styles[1] = 255;
|
|
mesh->styles[2] = 255;
|
|
mesh->styles[3] = 255;
|
|
}
|
|
|
|
for( int index = 0; index < g_modellight_numindexes; index++ )
|
|
{
|
|
int modelnum = g_modellight_indexes[index].modelnum;
|
|
int facenum = g_modellight_indexes[index].facenum;
|
|
entity_t *mapent = g_modellight[modelnum];
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
f = mesh->faces[facenum].light;
|
|
if( !f ) continue;
|
|
fl = &f->facelight;
|
|
|
|
if( f->lightofs == -1 )
|
|
continue;
|
|
|
|
byte oldstyles[MAXLIGHTMAPS];
|
|
int numstyles = 0;
|
|
int i, k, oldofs;
|
|
|
|
oldofs = f->lightofs;
|
|
f->lightofs = g_lightdatasize;
|
|
|
|
for( k = 0; k < MAXLIGHTMAPS; k++ )
|
|
{
|
|
oldstyles[k] = f->styles[k];
|
|
f->styles[k] = 255;
|
|
}
|
|
|
|
for( k = 0; k < MAXLIGHTMAPS && oldstyles[k] != 255; k++ )
|
|
{
|
|
int count = fl->numsamples;
|
|
byte maxb = 0;
|
|
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
byte *v = &oldlightdata[oldofs + count * 3 * k + i * 3];
|
|
maxb = Q_max( maxb, VectorMaximum( v ));
|
|
}
|
|
|
|
if( maxb <= 0 ) // black
|
|
continue;
|
|
|
|
f->styles[numstyles] = oldstyles[k];
|
|
memcpy( &g_dlightdata[f->lightofs + count * 3 * numstyles], &oldlightdata[oldofs + count * 3 * k], count * 3 );
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
if( g_ddeluxdata != NULL )
|
|
memcpy( &g_ddeluxdata[f->lightofs + count * 3 * numstyles], &olddeluxdata[oldofs + count * 3 * k], count * 3 );
|
|
if( g_dshadowdata != NULL )
|
|
memcpy( &g_dshadowdata[(f->lightofs/3) + count * numstyles], &oldshadowdata[(oldofs / 3) + count * k], count );
|
|
#endif
|
|
numstyles++;
|
|
}
|
|
g_lightdatasize += fl->numsamples * 3 * numstyles;
|
|
#ifdef HLRAD_DELUXEMAPPING
|
|
if( g_ddeluxdata != NULL )
|
|
g_deluxdatasize += fl->numsamples * 3 * numstyles;
|
|
|
|
if( g_dshadowdata != NULL )
|
|
g_shadowdatasize += fl->numsamples * numstyles;
|
|
#endif
|
|
// wrote styles into mesh to determine completely black models
|
|
for( i = 0; i < MAXLIGHTMAPS && f->styles[i] != 255; i++ )
|
|
{
|
|
for( k = 0; k < MAXLIGHTMAPS; k++ )
|
|
{
|
|
if( mesh->styles[k] == f->styles[i] || mesh->styles[k] == 255 )
|
|
break;
|
|
}
|
|
|
|
// for our purpoces obviously overflow doesn't matter
|
|
if( k == MAXLIGHTMAPS )
|
|
continue;
|
|
|
|
// allocate a new one
|
|
if( mesh->styles[k] == 255 )
|
|
mesh->styles[k] = f->styles[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GenerateLightCacheNumbers( void )
|
|
{
|
|
char string[32];
|
|
tmesh_t *mesh;
|
|
int i, j;
|
|
|
|
for( i = 1; i < g_numentities; i++ )
|
|
{
|
|
entity_t *mapent = &g_entities[i];
|
|
|
|
// no cache - no lighting
|
|
if( !mapent->cache ) continue;
|
|
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
|
|
if( mapent->modtype != mod_alias && mapent->modtype != mod_studio )
|
|
continue;
|
|
|
|
if( !mesh->verts || mesh->numverts <= 0 )
|
|
continue;
|
|
|
|
if( !mesh->faces || mesh->numfaces <= 0 )
|
|
continue;
|
|
|
|
if( !FBitSet( mesh->flags, FMESH_MODEL_LIGHTMAPS ))
|
|
continue;
|
|
|
|
mesh->texture_step = TEXTURE_STEP;
|
|
|
|
// check texturestep
|
|
int texture_step = Q_max( 0, IntForKey( mapent, "zhlt_texturestep" ));
|
|
|
|
// check bounds
|
|
if( texture_step >= MIN_STUDIO_TEXTURE_STEP && texture_step <= MAX_STUDIO_TEXTURE_STEP )
|
|
mesh->texture_step = texture_step;
|
|
|
|
short lightid = g_modellight_modnum++;
|
|
// at this point we have valid target for model lightmaps
|
|
Q_snprintf( string, sizeof( string ), "%i", lightid + 1 );
|
|
SetKeyValue( mapent, "flight_cache", string );
|
|
g_modellight[lightid] = mapent;
|
|
}
|
|
|
|
g_modellight_numindexes = 0;
|
|
g_studio_blur = g_blur;
|
|
|
|
// generate remapping table for more effective CPU utilize
|
|
for( i = 0; i < g_modellight_modnum; i++ )
|
|
{
|
|
entity_t *mapent = g_modellight[i];
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
g_modellight_numindexes += mesh->numfaces;
|
|
}
|
|
|
|
g_modellight_indexes = (faceremap_t *)Mem_Alloc( g_modellight_numindexes * sizeof( faceremap_t ));
|
|
uint curIndex = 0;
|
|
|
|
for( i = 0; i < g_modellight_modnum; i++ )
|
|
{
|
|
entity_t *mapent = g_modellight[i];
|
|
mesh = (tmesh_t *)mapent->cache;
|
|
|
|
// encode model as lowpart and facenum as highpart
|
|
for( j = 0; j < mesh->numfaces; j++ )
|
|
{
|
|
g_modellight_indexes[curIndex].modelnum = i;
|
|
g_modellight_indexes[curIndex].facenum = j;
|
|
curIndex++;
|
|
}
|
|
ASSERT( curIndex <= g_modellight_numindexes );
|
|
}
|
|
}
|
|
|
|
static int ModelSize( tmesh_t *mesh )
|
|
{
|
|
if( !mesh ) return 0;
|
|
|
|
if( !mesh->faces || mesh->numfaces <= 0 )
|
|
return 0;
|
|
|
|
for( int lightstyles = 0; lightstyles < MAXLIGHTMAPS; lightstyles++ )
|
|
{
|
|
if( mesh->styles[lightstyles] == 255 )
|
|
break;
|
|
}
|
|
|
|
// model is valid but completely not lighted by direct
|
|
if( !lightstyles ) return 0;
|
|
|
|
return sizeof( dmodelfacelight_t ) - ( sizeof( dfacelight_t ) * 3 ) + sizeof( dfacelight_t ) * mesh->numfaces + ((g_numworldlights + 7) / 8);
|
|
}
|
|
|
|
static int WriteModelLight( tmesh_t *mesh, byte *out )
|
|
{
|
|
int size = ModelSize( mesh );
|
|
dmodelfacelight_t *dml;
|
|
|
|
if( !size ) return 0;
|
|
|
|
dml = (dmodelfacelight_t *)out;
|
|
out += sizeof( dmodelfacelight_t ) - ( sizeof( dfacelight_t ) * 3 ) + sizeof( dfacelight_t ) * mesh->numfaces;
|
|
|
|
dml->modelCRC = mesh->modelCRC;
|
|
dml->numfaces = mesh->numfaces;
|
|
dml->texture_step = mesh->texture_step;
|
|
|
|
memcpy( dml->styles, mesh->styles, sizeof( dml->styles ));
|
|
memcpy( dml->submodels, mesh->fsubmodels, sizeof( dml->submodels ));
|
|
memcpy( out, mesh->vislight, ((g_numworldlights + 7) / 8));
|
|
VectorCopy( mesh->origin, dml->origin );
|
|
VectorCopy( mesh->angles, dml->angles );
|
|
VectorCopy( mesh->scale, dml->scale );
|
|
|
|
// faces could be store now
|
|
for( int i = 0; i < mesh->numfaces; i++ )
|
|
{
|
|
memcpy( dml->faces[i].styles, mesh->faces[i].light->styles, sizeof( dml->styles ));
|
|
dml->faces[i].lightofs = mesh->faces[i].light->lightofs;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
void WriteModelLighting( void )
|
|
{
|
|
int totaldatasize = ( sizeof( int ) * 3 ) + ( sizeof( int ) * g_modellight_modnum );
|
|
int i, len;
|
|
byte *data;
|
|
dvlightlump_t *l;
|
|
|
|
for( i = 0; i < g_modellight_modnum; i++ )
|
|
{
|
|
entity_t *mapent = g_modellight[i];
|
|
|
|
// sanity check
|
|
if( !mapent ) continue;
|
|
|
|
len = ModelSize( (tmesh_t *)mapent->cache );
|
|
totaldatasize += len;
|
|
}
|
|
|
|
Msg( "total modellight data: %s\n", Q_memprint( totaldatasize ));
|
|
g_dflightdata = (byte *)Mem_Realloc( g_dflightdata, totaldatasize );
|
|
|
|
// now setup to get the miptex data (or just the headers if using -wadtextures) from the wadfile
|
|
l = (dvlightlump_t *)g_dflightdata;
|
|
data = (byte *)&l->dataofs[g_modellight_modnum];
|
|
|
|
l->ident = FLIGHTIDENT;
|
|
l->version = FLIGHT_VERSION;
|
|
l->nummodels = g_modellight_modnum;
|
|
|
|
for( i = 0; i < g_modellight_modnum; i++ )
|
|
{
|
|
entity_t *mapent = g_modellight[i];
|
|
|
|
l->dataofs[i] = data - (byte *)l;
|
|
len = WriteModelLight( (tmesh_t *)mapent->cache, data );
|
|
if( !len ) l->dataofs[i] = -1; // completely black model
|
|
data += len;
|
|
}
|
|
|
|
g_flightdatasize = data - g_dflightdata;
|
|
|
|
if( totaldatasize != g_flightdatasize )
|
|
COM_FatalError( "WriteModelLighting: memory corrupted\n" );
|
|
|
|
// vertex cache acesss
|
|
// const char *id = ValueForKey( ent, "flight_cache" );
|
|
// int cacheID = atoi( id ) - 1;
|
|
// if( cacheID < 0 || cacheID > num_map_entities ) return; // bad cache num
|
|
// if( l->dataofs[cacheID] == -1 ) return; // cache missed
|
|
// otherwise it's valid
|
|
}
|
|
|
|
void BuildModelLightmaps( void )
|
|
{
|
|
GenerateLightCacheNumbers();
|
|
|
|
// new code is very fast, so no reason to show progress
|
|
RunThreadsOnIndividual( g_modellight_modnum, false, CalcTriangleVectors );
|
|
|
|
if( !g_modellight_numindexes ) return;
|
|
|
|
RunThreadsOnIndividual( g_modellight_numindexes, true, BuildModelLightmaps );
|
|
}
|
|
|
|
void FinalModelLightFace( void )
|
|
{
|
|
if( !g_modellight_numindexes ) return;
|
|
|
|
RunThreadsOnIndividual( g_modellight_numindexes, true, FinalModelLightFace );
|
|
}
|
|
|
|
#endif |