ioq3quest/q3map/lightv.c
2005-08-26 04:48:05 +00:00

5748 lines
151 KiB
C
Executable file

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "cmdlib.h"
#include "mathlib.h"
#include "bspfile.h"
#include "imagelib.h"
#include "threads.h"
#include "mutex.h"
#include "scriplib.h"
#include "shaders.h"
#include "mesh.h"
#ifdef _WIN32
//Improve floating-point consistency.
#pragma optimize( "p", on )
#endif
#ifdef _WIN32
#include "../libs/pakstuff.h"
#endif
#define MAX_CLUSTERS 16384
#define MAX_PORTALS 32768
#define MAX_FACETS 65536
#define MAX_LIGHTS 16384
#define LIGHTMAP_SIZE 128
#define LIGHTMAP_PIXELSHIFT 0.5
//#define LIGHTMAP_PATCHSHIFT
#define PORTALFILE "PRT1"
#define ON_EPSILON 0.1
#define VectorSet(v, x, y, z) v[0] = x;v[1] = y;v[2] = z;
typedef struct
{
vec3_t normal;
float dist;
} plane_t;
#define MAX_POINTS_ON_WINDING 64
//NOTE: whenever this is overflowed parts of lightmaps might end up not being lit
#define MAX_POINTS_ON_FIXED_WINDING 48
typedef struct
{
int numpoints;
vec3_t points[MAX_POINTS_ON_FIXED_WINDING]; // variable sized
} winding_t;
typedef struct
{
plane_t plane; // normal pointing into neighbor
int leaf; // neighbor
winding_t *winding;
vec3_t origin; // for fast clip testing
float radius;
} lportal_t;
#define MAX_PORTALS_ON_LEAF 128
typedef struct lleaf_s
{
int numportals;
lportal_t *portals[MAX_PORTALS_ON_LEAF];
//
int numSurfaces;
int firstSurface;
} lleaf_t;
typedef struct lFacet_s
{
int num;
plane_t plane;
vec3_t points[4]; //
int numpoints;
float lightmapCoords[4][2];
plane_t boundaries[4]; // negative is outside the bounds
float textureMatrix[2][4]; // texture coordinates for translucency
float lightmapMatrix[2][4]; // lightmap texture coordinates
vec3_t mins;
int x, y, width, height;
} lFacet_t;
typedef struct lsurfaceTest_s
{
vec3_t mins, maxs;
vec3_t origin;
float radius;
qboolean patch; // true if this is a patch
qboolean trisoup; // true if this is a triangle soup
int numFacets;
lFacet_t *facets;
mesh_t *detailMesh; // detailed mesh with points for each lmp
shaderInfo_t *shader; // for translucency
mutex_t *mutex;
int numvolumes; // number of volumes casted at this surface
//
int always_tracelight;
int always_vlight;
} lsurfaceTest_t;
//volume types
#define VOLUME_NORMAL 0
#define VOLUME_DIRECTED 1
#define MAX_TRANSLUCENTFACETS 32
typedef struct lightvolume_s
{
int num;
int cluster; //cluster this light volume started in
plane_t endplane; //end plane
plane_t farplane; //original end plane
vec3_t points[MAX_POINTS_ON_WINDING]; //end winding points
plane_t planes[MAX_POINTS_ON_WINDING]; //volume bounding planes
int numplanes; //number of volume bounding planes
int type; //light volume type
//list with translucent surfaces the volume went through
int transFacets[MAX_TRANSLUCENTFACETS];
int transSurfaces[MAX_TRANSLUCENTFACETS];
int numtransFacets;
//clusters already tested
byte clusterTested[MAX_CLUSTERS/8];
//facets already tested
byte facetTested[MAX_FACETS/8];
int facetNum; //number of the facet blocking the light in this volume
int surfaceNum; //number of the surface blocking the light in this volume
} lightvolume_t;
//light types
#define LIGHT_POINTRADIAL 1
#define LIGHT_POINTSPOT 2
#define LIGHT_POINTFAKESURFACE 3
#define LIGHT_SURFACEDIRECTED 4
#define LIGHT_SURFACERADIAL 5
#define LIGHT_SURFACESPOT 6
//light distance attenuation types
#define LDAT_QUADRATIC 0
#define LDAT_LINEAR 1
#define LDAT_NOSCALE 2
//light angle attenuation types
#define LAAT_NORMAL 0
#define LAAT_QUADRATIC 1
#define LAAT_DOUBLEQUADRATIC 2
typedef struct vlight_s
{
vec3_t origin; //light origin, for point lights
winding_t w; //light winding, for area lights
vec4_t plane; //light winding plane
vec3_t normal; //direction of the light
int type; //light type
vec3_t color; //light color
qboolean twosided; //radiates light at both sides of the winding
int style; //light style (not used)
int atten_disttype; //light distance attenuation type
int atten_angletype; //light angle attenuation type
float atten_distscale; //distance attenuation scale
float atten_anglescale; //angle attenuation scale
float radiusByDist; //radius by distance for spot lights
float photons; //emitted photons
float intensity; //intensity
vec3_t emitColor; //full out-of-gamut value (not used)
struct shaderInfo_s *si; //shader info
int insolid; //set when light is in solid
} vlight_t;
float lightLinearScale = 1.0 / 8000;
float lightPointScale = 7500;
float lightAreaScale = 0.25;
float lightFormFactorValueScale = 3;
int lightDefaultSubdivide = 999; // vary by surface size?
vec3_t lightAmbientColor;
int portalclusters, numportals, numfaces;
lleaf_t *leafs;
lportal_t *portals;
int numvlights = 0;
vlight_t *vlights[MAX_LIGHTS];
int nostitching = 0;
int noalphashading = 0;
int nocolorshading = 0;
int nobackfaceculling = 0;
int defaulttracelight = 0;
int radiosity = 0;
int radiosity_scale;
int clustersurfaces[MAX_MAP_LEAFFACES];
int numclustersurfaces = 0;
lsurfaceTest_t *lsurfaceTest[MAX_MAP_DRAW_SURFS];
int numfacets;
float lightmappixelarea[MAX_MAP_LIGHTING/3];
float *lightFloats;//[MAX_MAP_LIGHTING];
// from polylib.c
winding_t *AllocWinding (int points);
void FreeWinding (winding_t *w);
void WindingCenter (winding_t *w, vec3_t center);
void WindingBounds (winding_t *w, vec3_t mins, vec3_t maxs);
vec_t WindingArea (winding_t *w);
winding_t *BaseWindingForPlane (vec3_t normal, vec_t dist);
void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist,
vec_t epsilon, winding_t **front, winding_t **back);
winding_t *ReverseWinding (winding_t *w);
// from light.c
extern char source[1024];
extern vec3_t surfaceOrigin[ MAX_MAP_DRAW_SURFS ];
extern int entitySurface[ MAX_MAP_DRAW_SURFS ];
extern int samplesize;
extern int novertexlighting;
extern int nogridlighting;
extern qboolean patchshadows;
extern vec3_t gridSize;
float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w );
void ColorToBytes( const float *color, byte *colorBytes );
void CountLightmaps( void );
void GridAndVertexLighting( void );
void SetEntityOrigins( void );
//#define DEBUGNET
#ifdef DEBUGNET
#include "l_net.h"
socket_t *debug_socket;
/*
=====================
DebugNet_Setup
=====================
*/
void DebugNet_Setup(void)
{
address_t address;
int i;
Net_Setup();
Net_StringToAddress("127.0.0.1:28000", &address);
for (i = 0; i < 10; i++)
{
debug_socket = Net_Connect(&address, 28005 + i);
if (debug_socket)
break;
}
}
/*
=====================
DebugNet_Shutdown
=====================
*/
void DebugNet_Shutdown(void)
{
netmessage_t msg;
if (debug_socket)
{
NMSG_Clear(&msg);
NMSG_WriteByte(&msg, 1);
Net_Send(debug_socket, &msg);
Net_Disconnect(debug_socket);
}
debug_socket = NULL;
Net_Shutdown();
}
/*
=====================
DebugNet_RemoveAllPolys
=====================
*/
void DebugNet_RemoveAllPolys(void)
{
netmessage_t msg;
if (!debug_socket)
return;
NMSG_Clear(&msg);
NMSG_WriteByte(&msg, 2); //remove all debug polys
Net_Send(debug_socket, &msg);
}
/*
====================
DebugNet_DrawWinding
=====================
*/
void DebugNet_DrawWinding(winding_t *w, int color)
{
netmessage_t msg;
int i;
if (!debug_socket)
return;
NMSG_Clear(&msg);
NMSG_WriteByte(&msg, 0); //draw a winding
NMSG_WriteByte(&msg, w->numpoints); //number of points
NMSG_WriteLong(&msg, color); //color
for (i = 0; i < w->numpoints; i++)
{
NMSG_WriteFloat(&msg, w->points[i][0]);
NMSG_WriteFloat(&msg, w->points[i][1]);
NMSG_WriteFloat(&msg, w->points[i][2]);
}
Net_Send(debug_socket, &msg);
}
/*
=====================
DebugNet_DrawLine
=====================
*/
void DebugNet_DrawLine(vec3_t p1, vec3_t p2, int color)
{
netmessage_t msg;
if (!debug_socket)
return;
NMSG_Clear(&msg);
NMSG_WriteByte(&msg, 1); //draw a line
NMSG_WriteLong(&msg, color); //color
NMSG_WriteFloat(&msg, p1[0]);
NMSG_WriteFloat(&msg, p1[1]);
NMSG_WriteFloat(&msg, p1[2]);
NMSG_WriteFloat(&msg, p2[0]);
NMSG_WriteFloat(&msg, p2[1]);
NMSG_WriteFloat(&msg, p2[2]);
Net_Send(debug_socket, &msg);
}
/*
=====================
DebugNet_DrawMesh
=====================
*/
void DebugNet_DrawMesh(mesh_t *mesh)
{
int i, j;
float dot;
drawVert_t *v1, *v2, *v3, *v4;
winding_t winding;
plane_t plane;
vec3_t d1, d2;
for ( i = 0 ; i < mesh->width - 1 ; i++ ) {
for ( j = 0 ; j < mesh->height - 1 ; j++ ) {
v1 = mesh->verts + j * mesh->width + i;
v2 = v1 + 1;
v3 = v1 + mesh->width + 1;
v4 = v1 + mesh->width;
VectorSubtract( v4->xyz, v1->xyz, d1 );
VectorSubtract( v3->xyz, v1->xyz, d2 );
CrossProduct( d2, d1, plane.normal );
if ( VectorNormalize( plane.normal, plane.normal ) != 0 )
{
plane.dist = DotProduct( v1->xyz, plane.normal );
dot = DotProduct(plane.normal, v2->xyz) - plane.dist;
if (fabs(dot) < 0.1)
{
VectorCopy(v1->xyz, winding.points[0]);
VectorCopy(v4->xyz, winding.points[1]);
VectorCopy(v3->xyz, winding.points[2]);
VectorCopy(v2->xyz, winding.points[3]);
winding.numpoints = 4;
DebugNet_DrawWinding(&winding, 2);
continue;
}
}
winding.numpoints = 3;
VectorCopy(v1->xyz, winding.points[0]);
VectorCopy(v4->xyz, winding.points[1]);
VectorCopy(v3->xyz, winding.points[2]);
DebugNet_DrawWinding(&winding, 2);
VectorCopy(v1->xyz, winding.points[0]);
VectorCopy(v3->xyz, winding.points[1]);
VectorCopy(v2->xyz, winding.points[2]);
DebugNet_DrawWinding(&winding, 2);
}
}
}
/*
=====================
VL_DrawLightVolume
=====================
*/
int VL_ChopWinding (winding_t *in, plane_t *split, float epsilon);
void VL_DrawLightVolume(vlight_t *light, lightvolume_t *volume)
{
winding_t w;
int i;
vec3_t p2, invlight;
memcpy(w.points, volume->points, volume->numplanes * sizeof(vec3_t));
w.numpoints = volume->numplanes;
DebugNet_DrawWinding(&w, 2);
if (volume->type == VOLUME_DIRECTED)
{
VectorCopy(light->normal, invlight);
VectorInverse(invlight);
for (i = 0; i < volume->numplanes; i++)
{
VectorCopy(volume->points[i], w.points[0]);
VectorCopy(volume->points[(i+1) % volume->numplanes], w.points[1]);
VectorMA(w.points[1], MAX_WORLD_COORD, invlight, w.points[2]);
VectorMA(w.points[0], MAX_WORLD_COORD, invlight, w.points[3]);
w.numpoints = 4;
DebugNet_DrawWinding(&w, 2);
VectorMA(volume->points[i], 8, volume->planes[i].normal, p2);
DebugNet_DrawLine(volume->points[i], p2, 3);
}
}
else
{
//
VectorCopy(light->origin, w.points[0]);
w.numpoints = 3;
for (i = 0; i < volume->numplanes; i++)
{
VectorCopy(volume->points[i], w.points[1]);
VectorCopy(volume->points[(i+1) % volume->numplanes], w.points[2]);
VL_ChopWinding(&w, &volume->endplane, 0);
DebugNet_DrawWinding(&w, 2);
VectorMA(volume->points[i], 8, volume->planes[i].normal, p2);
DebugNet_DrawLine(volume->points[i], p2, 3);
}
}
}
/*
=============
VL_DrawLightmapPixel
=============
*/
void VL_DrawLightmapPixel(int surfaceNum, int x, int y, int color)
{
winding_t w;
dsurface_t *ds;
mesh_t *mesh;
ds = &drawSurfaces[surfaceNum];
if (ds->surfaceType == MST_PATCH)
{
mesh = lsurfaceTest[surfaceNum]->detailMesh;
VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, w.points[0]);
VectorCopy( mesh->verts[(y+1-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, w.points[1]);
VectorCopy( mesh->verts[(y+1-ds->lightmapY)*mesh->width+x+1-ds->lightmapX].xyz, w.points[2]);
VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x+1-ds->lightmapX].xyz, w.points[3]);
w.numpoints = 4;
}
else
{
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT - ds->lightmapX, ds->lightmapVecs[0], w.points[0]);
VectorMA(w.points[0], (float) y - LIGHTMAP_PIXELSHIFT - ds->lightmapY, ds->lightmapVecs[1], w.points[0]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT - ds->lightmapX, ds->lightmapVecs[0], w.points[1]);
VectorMA(w.points[1], (float) y - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapY, ds->lightmapVecs[1], w.points[1]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapX, ds->lightmapVecs[0], w.points[2]);
VectorMA(w.points[2], (float) y - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapY, ds->lightmapVecs[1], w.points[2]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapX, ds->lightmapVecs[0], w.points[3]);
VectorMA(w.points[3], (float) y - LIGHTMAP_PIXELSHIFT - ds->lightmapY, ds->lightmapVecs[1], w.points[3]);
w.numpoints = 4;
}
DebugNet_DrawWinding(&w, color);
}
/*
============
VL_DrawPortals
============
*/
void VL_DrawPortals(void)
{
int j;
lportal_t *p;
for (j = 0; j < numportals * 2; j++)
{
p = portals + j;
DebugNet_DrawWinding(p->winding, 1);
}
}
/*
============
VL_DrawLeaf
============
*/
void VL_DrawLeaf(int cluster)
{
int i;
lleaf_t *leaf;
lportal_t *p;
leaf = &leafs[cluster];
for (i = 0; i < leaf->numportals; i++)
{
p = leaf->portals[i];
DebugNet_DrawWinding(p->winding, 1);
}
}
#endif //DEBUGNET
/*
=============
VL_SplitWinding
=============
*/
int VL_SplitWinding (winding_t *in, winding_t *back, plane_t *split, float epsilon)
{
vec_t dists[128];
int sides[128];
int counts[3];
vec_t dot;
int i, j;
vec_t *p1, *p2;
vec3_t mid;
winding_t out;
winding_t *neww;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct (in->points[i], split->normal);
dot -= split->dist;
dists[i] = dot;
if (dot > epsilon)
sides[i] = SIDE_FRONT;
else if (dot < -epsilon)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
if (!counts[SIDE_BACK])
{
if (!counts[SIDE_FRONT])
return SIDE_ON;
else
return SIDE_FRONT;
}
if (!counts[SIDE_FRONT])
{
return SIDE_BACK;
}
sides[i] = sides[0];
dists[i] = dists[0];
neww = &out;
neww->numpoints = 0;
back->numpoints = 0;
for (i=0 ; i<in->numpoints ; i++)
{
p1 = in->points[i];
if (neww->numpoints >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_SplitWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return SIDE_FRONT; // can't chop -- fall back to original
}
if (back->numpoints >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_SplitWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return SIDE_FRONT;
}
if (sides[i] == SIDE_ON)
{
VectorCopy (p1, neww->points[neww->numpoints]);
neww->numpoints++;
VectorCopy (p1, back->points[back->numpoints]);
back->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy (p1, neww->points[neww->numpoints]);
neww->numpoints++;
}
if (sides[i] == SIDE_BACK)
{
VectorCopy (p1, back->points[back->numpoints]);
back->numpoints++;
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
if (neww->numpoints >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_SplitWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return SIDE_FRONT; // can't chop -- fall back to original
}
if (back->numpoints >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_SplitWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return SIDE_FRONT; // can't chop -- fall back to original
}
// generate a split point
p2 = in->points[(i+1)%in->numpoints];
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (split->normal[j] == 1)
mid[j] = split->dist;
else if (split->normal[j] == -1)
mid[j] = -split->dist;
else
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
}
VectorCopy (mid, neww->points[neww->numpoints]);
neww->numpoints++;
VectorCopy (mid, back->points[back->numpoints]);
back->numpoints++;
}
memcpy(in, &out, sizeof(winding_t));
return SIDE_CROSS;
}
/*
=====================
VL_LinkSurfaceIntoCluster
=====================
*/
void VL_LinkSurfaceIntoCluster(int cluster, int surfaceNum)
{
lleaf_t *leaf;
int i;
leaf = &leafs[cluster];
for (i = 0; i < leaf->numSurfaces; i++)
{
if (clustersurfaces[leaf->firstSurface + i] == surfaceNum)
return;
}
for (i = numclustersurfaces; i > leaf->firstSurface + leaf->numSurfaces; i--)
clustersurfaces[i] = clustersurfaces[i-1];
for (i = 0; i < portalclusters; i++)
{
if (i == cluster)
continue;
if (leafs[i].firstSurface >= leaf->firstSurface + leaf->numSurfaces)
leafs[i].firstSurface++;
}
clustersurfaces[leaf->firstSurface + leaf->numSurfaces] = surfaceNum;
leaf->numSurfaces++;
numclustersurfaces++;
if (numclustersurfaces >= MAX_MAP_LEAFFACES)
Error("MAX_MAP_LEAFFACES");
}
/*
=====================
VL_R_LinkSurface
=====================
*/
void VL_R_LinkSurface(int nodenum, int surfaceNum, winding_t *w)
{
int leafnum, cluster, res;
dnode_t *node;
dplane_t *plane;
winding_t back;
plane_t split;
while(nodenum >= 0)
{
node = &dnodes[nodenum];
plane = &dplanes[node->planeNum];
VectorCopy(plane->normal, split.normal);
split.dist = plane->dist;
res = VL_SplitWinding (w, &back, &split, 0.1);
if (res == SIDE_FRONT)
{
nodenum = node->children[0];
}
else if (res == SIDE_BACK)
{
nodenum = node->children[1];
}
else if (res == SIDE_ON)
{
memcpy(&back, w, sizeof(winding_t));
VL_R_LinkSurface(node->children[1], surfaceNum, &back);
nodenum = node->children[0];
}
else
{
VL_R_LinkSurface(node->children[1], surfaceNum, &back);
nodenum = node->children[0];
}
}
leafnum = -nodenum - 1;
cluster = dleafs[leafnum].cluster;
if (cluster != -1)
{
VL_LinkSurfaceIntoCluster(cluster, surfaceNum);
}
}
/*
=====================
VL_LinkSurfaces
maybe link each facet seperately instead of the test surfaces?
=====================
*/
void VL_LinkSurfaces(void)
{
int i, j;
lsurfaceTest_t *test;
lFacet_t *facet;
winding_t winding;
for ( i = 0 ; i < numDrawSurfaces ; i++ )
{
test = lsurfaceTest[ i ];
if (!test)
continue;
for (j = 0; j < test->numFacets; j++)
{
facet = &test->facets[j];
memcpy(winding.points, facet->points, facet->numpoints * sizeof(vec3_t));
winding.numpoints = facet->numpoints;
VL_R_LinkSurface(0, i, &winding);
}
}
}
/*
=====================
VL_TextureMatrixFromPoints
=====================
*/
void VL_TextureMatrixFromPoints( lFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) {
int i, j;
float t;
float m[3][4];
float s;
// This is an incredibly stupid way of solving a three variable equation
for ( i = 0 ; i < 2 ; i++ ) {
m[0][0] = a->xyz[0];
m[0][1] = a->xyz[1];
m[0][2] = a->xyz[2];
m[0][3] = a->st[i];
m[1][0] = b->xyz[0];
m[1][1] = b->xyz[1];
m[1][2] = b->xyz[2];
m[1][3] = b->st[i];
m[2][0] = c->xyz[0];
m[2][1] = c->xyz[1];
m[2][2] = c->xyz[2];
m[2][3] = c->st[i];
if ( fabs(m[1][0]) > fabs(m[0][0]) && fabs(m[1][0]) > fabs(m[2][0]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[0][j];
m[0][j] = m[1][j];
m[1][j] = t;
}
} else if ( fabs(m[2][0]) > fabs(m[0][0]) && fabs(m[2][0]) > fabs(m[1][0]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[0][j];
m[0][j] = m[2][j];
m[2][j] = t;
}
}
s = 1.0 / m[0][0];
m[0][0] *= s;
m[0][1] *= s;
m[0][2] *= s;
m[0][3] *= s;
s = m[1][0];
m[1][0] -= m[0][0] * s;
m[1][1] -= m[0][1] * s;
m[1][2] -= m[0][2] * s;
m[1][3] -= m[0][3] * s;
s = m[2][0];
m[2][0] -= m[0][0] * s;
m[2][1] -= m[0][1] * s;
m[2][2] -= m[0][2] * s;
m[2][3] -= m[0][3] * s;
if ( fabs(m[2][1]) > fabs(m[1][1]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[1][j];
m[1][j] = m[2][j];
m[2][j] = t;
}
}
s = 1.0 / m[1][1];
m[1][0] *= s;
m[1][1] *= s;
m[1][2] *= s;
m[1][3] *= s;
s = m[2][1];// / m[1][1];
m[2][0] -= m[1][0] * s;
m[2][1] -= m[1][1] * s;
m[2][2] -= m[1][2] * s;
m[2][3] -= m[1][3] * s;
s = 1.0 / m[2][2];
m[2][0] *= s;
m[2][1] *= s;
m[2][2] *= s;
m[2][3] *= s;
f->textureMatrix[i][2] = m[2][3];
f->textureMatrix[i][1] = m[1][3] - f->textureMatrix[i][2] * m[1][2];
f->textureMatrix[i][0] = m[0][3] - f->textureMatrix[i][2] * m[0][2] - f->textureMatrix[i][1] * m[0][1];
f->textureMatrix[i][3] = 0;
/*
s = fabs( DotProduct( a->xyz, f->textureMatrix[i] ) - a->st[i] );
if ( s > 0.01 ) {
Error( "Bad textureMatrix" );
}
s = fabs( DotProduct( b->xyz, f->textureMatrix[i] ) - b->st[i] );
if ( s > 0.01 ) {
Error( "Bad textureMatrix" );
}
s = fabs( DotProduct( c->xyz, f->textureMatrix[i] ) - c->st[i] );
if ( s > 0.01 ) {
Error( "Bad textureMatrix" );
}
*/
}
}
/*
=====================
VL_LightmapMatrixFromPoints
=====================
*/
void VL_LightmapMatrixFromPoints( dsurface_t *dsurf, shaderInfo_t *si, lFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) {
int i, j;
float t;
float m[3][4], al, bl, cl;
float s;
int h, w, ssize;
vec3_t mins, maxs, delta, size, planeNormal;
drawVert_t *verts;
static int message;
// vertex-lit triangle model
if ( dsurf->surfaceType == MST_TRIANGLE_SOUP ) {
return;
}
if ( dsurf->lightmapNum < 0 ) {
return; // doesn't need lighting
}
VectorClear(f->mins);
if (dsurf->surfaceType != MST_PATCH)
{
ssize = samplesize;
if (si->lightmapSampleSize)
ssize = si->lightmapSampleSize;
ClearBounds( mins, maxs );
verts = &drawVerts[dsurf->firstVert];
for ( i = 0 ; i < dsurf->numVerts ; i++ ) {
AddPointToBounds( verts[i].xyz, 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 );
f->mins[i] = mins[i];
size[i] = (maxs[i] - mins[i]) / ssize + 1;
}
// the two largest axis will be the lightmap size
VectorClear(f->lightmapMatrix[0]);
f->lightmapMatrix[0][3] = 0;
VectorClear(f->lightmapMatrix[1]);
f->lightmapMatrix[1][3] = 0;
planeNormal[0] = fabs( dsurf->lightmapVecs[2][0] );
planeNormal[1] = fabs( dsurf->lightmapVecs[2][1] );
planeNormal[2] = fabs( dsurf->lightmapVecs[2][2] );
if ( planeNormal[0] >= planeNormal[1] && planeNormal[0] >= planeNormal[2] ) {
w = size[1];
h = size[2];
f->lightmapMatrix[0][1] = 1.0 / ssize;
f->lightmapMatrix[1][2] = 1.0 / ssize;
} else if ( planeNormal[1] >= planeNormal[0] && planeNormal[1] >= planeNormal[2] ) {
w = size[0];
h = size[2];
f->lightmapMatrix[0][0] = 1.0 / ssize;
f->lightmapMatrix[1][2] = 1.0 / ssize;
} else {
w = size[0];
h = size[1];
f->lightmapMatrix[0][0] = 1.0 / ssize;
f->lightmapMatrix[1][1] = 1.0 / ssize;
}
if ( w > LIGHTMAP_WIDTH ) {
VectorScale ( f->lightmapMatrix[0], (float)LIGHTMAP_SIZE/w, f->lightmapMatrix[0] );
}
if ( h > LIGHTMAP_HEIGHT ) {
VectorScale ( f->lightmapMatrix[1], (float)LIGHTMAP_SIZE/h, f->lightmapMatrix[1] );
}
VectorSubtract(a->xyz, f->mins, delta);
s = (DotProduct( delta, f->lightmapMatrix[0] ) + dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE;
if ( fabs(s - a->lightmap[0]) > 0.01 ) {
_printf( "Bad lightmapMatrix" );
}
t = (DotProduct( delta, f->lightmapMatrix[1] ) + dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE;
if ( fabs(t - a->lightmap[1]) > 0.01 ) {
_printf( "Bad lightmapMatrix" );
}
VectorSubtract(b->xyz, f->mins, delta);
s = (DotProduct( delta, f->lightmapMatrix[0] ) + dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE;
if ( fabs(s - b->lightmap[0]) > 0.01 ) {
_printf( "Bad lightmapMatrix" );
}
t = (DotProduct( delta, f->lightmapMatrix[1] ) + dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE;
if ( fabs(t - b->lightmap[1]) > 0.01 ) {
_printf( "Bad lightmapMatrix" );
}
VectorSubtract(c->xyz, f->mins, delta);
s = (DotProduct( delta, f->lightmapMatrix[0] ) + dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE;
if ( fabs(s - c->lightmap[0]) > 0.01 ) {
_printf( "Bad lightmapMatrix" );
}
t = (DotProduct( delta, f->lightmapMatrix[1] ) + dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE;
if ( fabs(t - c->lightmap[1]) > 0.01 ) {
_printf( "Bad lightmapMatrix" );
}
VectorAdd(f->mins, surfaceOrigin[dsurf - drawSurfaces], f->mins);
return;
}
// This is an incredibly stupid way of solving a three variable equation
for ( i = 0 ; i < 2 ; i++ ) {
if (i)
al = a->lightmap[i] - ((float) dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE;
else
al = a->lightmap[i] - ((float) dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE;
m[0][0] = a->xyz[0] - f->mins[0];
m[0][1] = a->xyz[1] - f->mins[1];
m[0][2] = a->xyz[2] - f->mins[2];
m[0][3] = al;
if (i)
bl = b->lightmap[i] - ((float) dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE;
else
bl = b->lightmap[i] - ((float) dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE;
m[1][0] = b->xyz[0] - f->mins[0];
m[1][1] = b->xyz[1] - f->mins[1];
m[1][2] = b->xyz[2] - f->mins[2];
m[1][3] = bl;
if (i)
cl = c->lightmap[i] - ((float) dsurf->lightmapY + 0.5) / LIGHTMAP_SIZE;
else
cl = c->lightmap[i] - ((float) dsurf->lightmapX + 0.5) / LIGHTMAP_SIZE;
m[2][0] = c->xyz[0] - f->mins[0];
m[2][1] = c->xyz[1] - f->mins[1];
m[2][2] = c->xyz[2] - f->mins[2];
m[2][3] = cl;
if ( fabs(m[1][0]) > fabs(m[0][0]) && fabs(m[1][0]) >= fabs(m[2][0]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[0][j];
m[0][j] = m[1][j];
m[1][j] = t;
}
} else if ( fabs(m[2][0]) > fabs(m[0][0]) && fabs(m[2][0]) >= fabs(m[1][0]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[0][j];
m[0][j] = m[2][j];
m[2][j] = t;
}
}
if (m[0][0])
{
s = 1.0 / m[0][0];
m[0][0] *= s;
m[0][1] *= s;
m[0][2] *= s;
m[0][3] *= s;
s = m[1][0];
m[1][0] -= m[0][0] * s;
m[1][1] -= m[0][1] * s;
m[1][2] -= m[0][2] * s;
m[1][3] -= m[0][3] * s;
s = m[2][0];
m[2][0] -= m[0][0] * s;
m[2][1] -= m[0][1] * s;
m[2][2] -= m[0][2] * s;
m[2][3] -= m[0][3] * s;
}
if ( fabs(m[2][1]) > fabs(m[1][1]) ) {
for ( j = 0 ; j < 4 ; j ++ ) {
t = m[1][j];
m[1][j] = m[2][j];
m[2][j] = t;
}
}
if (m[1][1])
{
s = 1.0 / m[1][1];
m[1][0] *= s;
m[1][1] *= s;
m[1][2] *= s;
m[1][3] *= s;
s = m[2][1];
m[2][0] -= m[1][0] * s;
m[2][1] -= m[1][1] * s;
m[2][2] -= m[1][2] * s;
m[2][3] -= m[1][3] * s;
}
if (m[2][2])
{
s = 1.0 / m[2][2];
m[2][0] *= s;
m[2][1] *= s;
m[2][2] *= s;
m[2][3] *= s;
}
f->lightmapMatrix[i][2] = m[2][3];
f->lightmapMatrix[i][1] = m[1][3] - f->lightmapMatrix[i][2] * m[1][2];
f->lightmapMatrix[i][0] = m[0][3] - f->lightmapMatrix[i][2] * m[0][2] - f->lightmapMatrix[i][1] * m[0][1];
f->lightmapMatrix[i][3] = 0;
VectorSubtract(a->xyz, f->mins, delta);
s = fabs( DotProduct( delta, f->lightmapMatrix[i] ) - al );
if ( s > 0.01 ) {
if (!message)
_printf( "Bad lightmapMatrix\n" );
message = qtrue;
}
VectorSubtract(b->xyz, f->mins, delta);
s = fabs( DotProduct( delta, f->lightmapMatrix[i] ) - bl );
if ( s > 0.01 ) {
if (!message)
_printf( "Bad lightmapMatrix\n" );
message = qtrue;
}
VectorSubtract(c->xyz, f->mins, delta);
s = fabs( DotProduct( delta, f->lightmapMatrix[i] ) - cl );
if ( s > 0.01 ) {
if (!message)
_printf( "Bad lightmapMatrix\n" );
message = qtrue;
}
VectorAdd(f->mins, surfaceOrigin[dsurf - drawSurfaces], f->mins);
}
}
/*
=============
Plane_Equal
=============
*/
#define NORMAL_EPSILON 0.0001
#define DIST_EPSILON 0.02
int Plane_Equal(plane_t *a, plane_t *b, int flip)
{
vec3_t normal;
float dist;
if (flip) {
normal[0] = - b->normal[0];
normal[1] = - b->normal[1];
normal[2] = - b->normal[2];
dist = - b->dist;
}
else {
normal[0] = b->normal[0];
normal[1] = b->normal[1];
normal[2] = b->normal[2];
dist = b->dist;
}
if (
fabs(a->normal[0] - normal[0]) < NORMAL_EPSILON
&& fabs(a->normal[1] - normal[1]) < NORMAL_EPSILON
&& fabs(a->normal[2] - normal[2]) < NORMAL_EPSILON
&& fabs(a->dist - dist) < DIST_EPSILON )
return qtrue;
return qfalse;
}
/*
=============
VL_PlaneFromPoints
=============
*/
qboolean VL_PlaneFromPoints( plane_t *plane, const vec3_t a, const vec3_t b, const vec3_t c ) {
vec3_t d1, d2;
VectorSubtract( b, a, d1 );
VectorSubtract( c, a, d2 );
CrossProduct( d2, d1, plane->normal );
if ( VectorNormalize( plane->normal, plane->normal ) == 0 ) {
return qfalse;
}
plane->dist = DotProduct( a, plane->normal );
return qtrue;
}
/*
=====================
VL_GenerateBoundaryForPoints
=====================
*/
void VL_GenerateBoundaryForPoints( plane_t *boundary, plane_t *plane, vec3_t a, vec3_t b ) {
vec3_t d1;
// make a perpendicular vector to the edge and the surface
VectorSubtract( a, b, d1 );
CrossProduct( plane->normal, d1, boundary->normal );
VectorNormalize( boundary->normal, boundary->normal );
boundary->dist = DotProduct( a, boundary->normal );
}
/*
=====================
VL_GenerateFacetFor3Points
=====================
*/
qboolean VL_GenerateFacetFor3Points( dsurface_t *dsurf, shaderInfo_t *si, lFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) {
//
vec3_t dir;
int i;
// if we can't generate a valid plane for the points, ignore the facet
if ( !VL_PlaneFromPoints( &f->plane, a->xyz, b->xyz, c->xyz ) ) {
f->numpoints = 0;
return qfalse;
}
f->num = numfacets++;
VectorAdd( a->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[0] );
VectorAdd( b->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[1] );
VectorAdd( c->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[2] );
f->lightmapCoords[0][0] = a->lightmap[0];
f->lightmapCoords[0][1] = a->lightmap[1];
f->lightmapCoords[1][0] = b->lightmap[0];
f->lightmapCoords[1][1] = b->lightmap[1];
f->lightmapCoords[2][0] = c->lightmap[0];
f->lightmapCoords[2][1] = c->lightmap[1];
VL_GenerateBoundaryForPoints( &f->boundaries[0], &f->plane, f->points[0], f->points[1] );
VL_GenerateBoundaryForPoints( &f->boundaries[1], &f->plane, f->points[1], f->points[2] );
VL_GenerateBoundaryForPoints( &f->boundaries[2], &f->plane, f->points[2], f->points[0] );
for (i = 0; i < 3; i++)
{
VectorSubtract(f->points[(i+1)%3], f->points[i], dir);
if (VectorLength(dir) < 0.1)
return qfalse;
}
VL_TextureMatrixFromPoints( f, a, b, c );
VL_LightmapMatrixFromPoints( dsurf, si, f, a, b, c );
f->numpoints = 3;
return qtrue;
}
/*
=====================
VL_GenerateFacetFor4Points
Attempts to use four points as a planar quad
=====================
*/
#define PLANAR_EPSILON 0.1
qboolean VL_GenerateFacetFor4Points( dsurface_t *dsurf, shaderInfo_t *si, lFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c, drawVert_t *d ) {
float dist;
vec3_t dir;
int i;
plane_t plane;
// if we can't generate a valid plane for the points, ignore the facet
if ( !VL_PlaneFromPoints( &f->plane, a->xyz, b->xyz, c->xyz ) ) {
f->numpoints = 0;
return qfalse;
}
// if the fourth point is also on the plane, we can make a quad facet
dist = DotProduct( d->xyz, f->plane.normal ) - f->plane.dist;
if ( fabs( dist ) > PLANAR_EPSILON ) {
f->numpoints = 0;
return qfalse;
}
VectorAdd( a->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[0] );
VectorAdd( b->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[1] );
VectorAdd( c->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[2] );
VectorAdd( d->xyz, surfaceOrigin[dsurf - drawSurfaces], f->points[3] );
for (i = 1; i < 4; i++)
{
if ( !VL_PlaneFromPoints( &plane, f->points[i], f->points[(i+1) % 4], f->points[(i+2) % 4]) ) {
f->numpoints = 0;
return qfalse;
}
if (!Plane_Equal(&f->plane, &plane, qfalse)) {
f->numpoints = 0;
return qfalse;
}
}
f->lightmapCoords[0][0] = a->lightmap[0];
f->lightmapCoords[0][1] = a->lightmap[1];
f->lightmapCoords[1][0] = b->lightmap[0];
f->lightmapCoords[1][1] = b->lightmap[1];
f->lightmapCoords[2][0] = c->lightmap[0];
f->lightmapCoords[2][1] = c->lightmap[1];
f->lightmapCoords[3][0] = d->lightmap[0];
f->lightmapCoords[3][1] = d->lightmap[1];
VL_GenerateBoundaryForPoints( &f->boundaries[0], &f->plane, f->points[0], f->points[1] );
VL_GenerateBoundaryForPoints( &f->boundaries[1], &f->plane, f->points[1], f->points[2] );
VL_GenerateBoundaryForPoints( &f->boundaries[2], &f->plane, f->points[2], f->points[3] );
VL_GenerateBoundaryForPoints( &f->boundaries[3], &f->plane, f->points[3], f->points[0] );
for (i = 0; i < 4; i++)
{
VectorSubtract(f->points[(i+1)%4], f->points[i], dir);
if (VectorLength(dir) < 0.1)
return qfalse;
}
VL_TextureMatrixFromPoints( f, a, b, c );
VL_LightmapMatrixFromPoints( dsurf, si, f, a, b, c );
f->num = numfacets++;
f->numpoints = 4;
return qtrue;
}
/*
===============
VL_SphereFromBounds
===============
*/
void VL_SphereFromBounds( vec3_t mins, vec3_t maxs, vec3_t origin, float *radius ) {
vec3_t temp;
VectorAdd( mins, maxs, origin );
VectorScale( origin, 0.5, origin );
VectorSubtract( maxs, origin, temp );
*radius = VectorLength( temp );
}
/*
====================
VL_FacetsForTriangleSurface
====================
*/
void VL_FacetsForTriangleSurface( dsurface_t *dsurf, shaderInfo_t *si, lsurfaceTest_t *test ) {
int i;
drawVert_t *v1, *v2, *v3, *v4;
int count;
int i1, i2, i3, i4, i5, i6;
test->patch = qfalse;
if (dsurf->surfaceType == MST_TRIANGLE_SOUP)
test->trisoup = qtrue;
else
test->trisoup = qfalse;
test->numFacets = dsurf->numIndexes / 3;
test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets );
test->shader = si;
count = 0;
for ( i = 0 ; i < test->numFacets ; i++ ) {
i1 = drawIndexes[ dsurf->firstIndex + i*3 ];
i2 = drawIndexes[ dsurf->firstIndex + i*3 + 1 ];
i3 = drawIndexes[ dsurf->firstIndex + i*3 + 2 ];
v1 = &drawVerts[ dsurf->firstVert + i1 ];
v2 = &drawVerts[ dsurf->firstVert + i2 ];
v3 = &drawVerts[ dsurf->firstVert + i3 ];
// try and make a quad out of two triangles
if ( i != test->numFacets - 1 ) {
i4 = drawIndexes[ dsurf->firstIndex + i*3 + 3 ];
i5 = drawIndexes[ dsurf->firstIndex + i*3 + 4 ];
i6 = drawIndexes[ dsurf->firstIndex + i*3 + 5 ];
if ( i4 == i3 && i5 == i2 ) {
v4 = &drawVerts[ dsurf->firstVert + i6 ];
if ( VL_GenerateFacetFor4Points( dsurf, si, &test->facets[count], v1, v2, v4, v3 ) ) {
count++;
i++; // skip next tri
continue;
}
}
}
if (VL_GenerateFacetFor3Points( dsurf, si, &test->facets[count], v1, v2, v3 )) {
count++;
}
}
// we may have turned some pairs into quads
test->numFacets = count;
}
/*
====================
VL_FacetsForPatch
====================
*/
void VL_FacetsForPatch( dsurface_t *dsurf, int surfaceNum, shaderInfo_t *si, lsurfaceTest_t *test ) {
int i, j, x, y;
drawVert_t *v1, *v2, *v3, *v4;
int count, ssize;
mesh_t mesh;
mesh_t *subdivided, *detailmesh, *newmesh;
int widthtable[LIGHTMAP_SIZE], heighttable[LIGHTMAP_SIZE];
mesh.width = dsurf->patchWidth;
mesh.height = dsurf->patchHeight;
mesh.verts = &drawVerts[ dsurf->firstVert ];
newmesh = SubdivideMesh( mesh, 8, 999 );
PutMeshOnCurve( *newmesh );
MakeMeshNormals( *newmesh );
subdivided = RemoveLinearMeshColumnsRows( newmesh );
FreeMesh(newmesh);
// DebugNet_RemoveAllPolys();
// DebugNet_DrawMesh(subdivided);
ssize = samplesize;
if (si->lightmapSampleSize)
ssize = si->lightmapSampleSize;
if ( dsurf->lightmapNum >= 0 ) {
detailmesh = SubdivideMeshQuads( subdivided, ssize, LIGHTMAP_SIZE, widthtable, heighttable);
test->detailMesh = detailmesh;
// DebugNet_RemoveAllPolys();
// DebugNet_DrawMesh(detailmesh);
if ( detailmesh->width != dsurf->lightmapWidth || detailmesh->height != dsurf->lightmapHeight ) {
Error( "Mesh lightmap miscount");
}
}
else {
test->detailMesh = NULL;
memset(widthtable, 0, sizeof(widthtable));
memset(heighttable, 0, sizeof(heighttable));
}
test->patch = qtrue;
test->trisoup = qfalse;
test->numFacets = ( subdivided->width - 1 ) * ( subdivided->height - 1 ) * 2;
test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets );
test->shader = si;
count = 0;
x = 0;
for ( i = 0 ; i < subdivided->width - 1 ; i++ ) {
y = 0;
for ( j = 0 ; j < subdivided->height - 1 ; j++ ) {
v1 = subdivided->verts + j * subdivided->width + i;
v2 = v1 + 1;
v3 = v1 + subdivided->width + 1;
v4 = v1 + subdivided->width;
if ( VL_GenerateFacetFor4Points( dsurf, si, &test->facets[count], v1, v4, v3, v2 ) ) {
test->facets[count].x = x;
test->facets[count].y = y;
test->facets[count].width = widthtable[i];
test->facets[count].height = heighttable[j];
count++;
} else {
if (VL_GenerateFacetFor3Points( dsurf, si, &test->facets[count], v1, v4, v3 )) {
test->facets[count].x = x;
test->facets[count].y = y;
test->facets[count].width = widthtable[i];
test->facets[count].height = heighttable[j];
count++;
}
if (VL_GenerateFacetFor3Points( dsurf, si, &test->facets[count], v1, v3, v2 )) {
test->facets[count].x = x;
test->facets[count].y = y;
test->facets[count].width = widthtable[i];
test->facets[count].height = heighttable[j];
count++;
}
}
y += heighttable[j];
}
x += widthtable[i];
}
test->numFacets = count;
FreeMesh(subdivided);
}
/*
=====================
VL_InitSurfacesForTesting
=====================
*/
void VL_InitSurfacesForTesting( void ) {
int i, j, k;
dsurface_t *dsurf;
lsurfaceTest_t *test;
shaderInfo_t *si;
lFacet_t *facet;
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
// don't light the entity surfaces with vlight
if ( entitySurface[i] )
continue;
//
dsurf = &drawSurfaces[ i ];
if ( !dsurf->numIndexes && !dsurf->patchWidth ) {
continue;
}
si = ShaderInfoForShader( dshaders[ dsurf->shaderNum].shader );
// if the surface is translucent and does not cast an alpha shadow
if ( (si->contents & CONTENTS_TRANSLUCENT) && !(si->surfaceFlags & SURF_ALPHASHADOW) ) {
// if the surface has no lightmap
if ( dsurf->lightmapNum < 0 )
continue;
}
test = malloc( sizeof( *test ) );
memset(test, 0, sizeof( *test ));
test->mutex = MutexAlloc();
test->numvolumes = 0;
if (si->forceTraceLight)
test->always_tracelight = qtrue;
else if (si->forceVLight)
test->always_vlight = qtrue;
lsurfaceTest[i] = test;
if ( dsurf->surfaceType == MST_TRIANGLE_SOUP || dsurf->surfaceType == MST_PLANAR ) {
VL_FacetsForTriangleSurface( dsurf, si, test );
} else if ( dsurf->surfaceType == MST_PATCH ) {
VL_FacetsForPatch( dsurf, i, si, test );
}
if (numfacets >= MAX_FACETS)
Error("numfacets >= MAX_FACETS (%d)", MAX_FACETS);
ClearBounds( test->mins, test->maxs );
for (j = 0; j < test->numFacets; j++)
{
facet = &test->facets[j];
for ( k = 0 ; k < facet->numpoints; k++) {
AddPointToBounds( facet->points[k], test->mins, test->maxs );
}
}
VL_SphereFromBounds( test->mins, test->maxs, test->origin, &test->radius );
}
_printf("%6d facets\n", numfacets);
_printf("linking surfaces...\n");
VL_LinkSurfaces();
}
/*
=============
VL_ChopWinding
=============
*/
int VL_ChopWinding (winding_t *in, plane_t *split, float epsilon)
{
vec_t dists[128];
int sides[128];
int counts[3];
vec_t dot;
int i, j;
vec_t *p1, *p2;
vec3_t mid;
winding_t out;
winding_t *neww;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i=0 ; i<in->numpoints ; i++)
{
dot = DotProduct (in->points[i], split->normal);
dot -= split->dist;
dists[i] = dot;
if (dot > epsilon)
sides[i] = SIDE_FRONT;
else if (dot < -epsilon)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
if (!counts[SIDE_BACK])
{
if (!counts[SIDE_FRONT])
return SIDE_ON;
else
return SIDE_FRONT;
}
if (!counts[SIDE_FRONT])
{
return SIDE_BACK;
}
sides[i] = sides[0];
dists[i] = dists[0];
neww = &out;
neww->numpoints = 0;
for (i=0 ; i<in->numpoints ; i++)
{
p1 = in->points[i];
if (neww->numpoints >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_ChopWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return SIDE_FRONT; // can't chop -- fall back to original
}
if (sides[i] == SIDE_ON)
{
VectorCopy (p1, neww->points[neww->numpoints]);
neww->numpoints++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy (p1, neww->points[neww->numpoints]);
neww->numpoints++;
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
if (neww->numpoints >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_ChopWinding -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return SIDE_FRONT; // can't chop -- fall back to original
}
// generate a split point
p2 = in->points[(i+1)%in->numpoints];
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (split->normal[j] == 1)
mid[j] = split->dist;
else if (split->normal[j] == -1)
mid[j] = -split->dist;
else
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
}
VectorCopy (mid, neww->points[neww->numpoints]);
neww->numpoints++;
}
memcpy(in, &out, sizeof(winding_t));
return SIDE_CROSS;
}
/*
=============
VL_ChopWindingWithBrush
returns all winding fragments outside the brush
=============
*/
int VL_ChopWindingWithBrush(winding_t *w, dbrush_t *brush, winding_t *outwindings, int maxout)
{
int i, res, numout;
winding_t front, back;
plane_t plane;
numout = 0;
memcpy(front.points, w->points, w->numpoints * sizeof(vec3_t));
front.numpoints = w->numpoints;
for (i = 0; i < brush->numSides; i++)
{
VectorCopy(dplanes[ dbrushsides[ brush->firstSide + i ].planeNum ].normal, plane.normal);
VectorInverse(plane.normal);
plane.dist = -dplanes[ dbrushsides[ brush->firstSide + i ].planeNum ].dist;
res = VL_SplitWinding(&front, &back, &plane, 0.1);
if (res == SIDE_BACK || res == SIDE_ON)
{
memcpy(outwindings[0].points, w->points, w->numpoints * sizeof(vec3_t));
outwindings[0].numpoints = w->numpoints;
return 1; //did not intersect
}
if (res != SIDE_FRONT)
{
if (numout >= maxout)
{
_printf("WARNING: VL_ChopWindingWithBrush: more than %d windings\n", maxout);
return 0;
}
memcpy(outwindings[numout].points, back.points, back.numpoints * sizeof(vec3_t));
outwindings[numout].numpoints = back.numpoints;
numout++;
}
}
return numout;
}
/*
=============
VL_WindingAreaOutsideBrushes
=============
*/
float VL_WindingAreaOutsideBrushes(winding_t *w, int *brushnums, int numbrushes)
{
int i, j, numwindings[2], n;
winding_t windingsbuf[2][64];
dbrush_t *brush;
float area;
memcpy(windingsbuf[0][0].points, w->points, w->numpoints * sizeof(vec3_t));
windingsbuf[0][0].numpoints = w->numpoints;
numwindings[0] = 1;
for (i = 0; i < numbrushes; i++)
{
brush = &dbrushes[brushnums[i]];
if (!(dshaders[brush->shaderNum].contentFlags & (
CONTENTS_LAVA
| CONTENTS_SLIME
| CONTENTS_WATER
| CONTENTS_FOG
| CONTENTS_AREAPORTAL
| CONTENTS_PLAYERCLIP
| CONTENTS_MONSTERCLIP
| CONTENTS_CLUSTERPORTAL
| CONTENTS_DONOTENTER
| CONTENTS_BODY
| CONTENTS_CORPSE
| CONTENTS_TRANSLUCENT
| CONTENTS_TRIGGER
| CONTENTS_NODROP) ) &&
(dshaders[brush->shaderNum].contentFlags & CONTENTS_SOLID) )
{
numwindings[!(i & 1)] = 0;
for (j = 0; j < numwindings[i&1]; j++)
{
n = VL_ChopWindingWithBrush(&windingsbuf[i&1][j], brush,
&windingsbuf[!(i&1)][numwindings[!(i&1)]],
64 - numwindings[!(i&1)]);
numwindings[!(i&1)] += n;
}
if (!numwindings[!(i&1)])
return 0;
}
else
{
for (j = 0; j < numwindings[i&1]; j++)
{
windingsbuf[!(i&1)][j] = windingsbuf[i&1][j];
}
numwindings[!(i&1)] = numwindings[i&1];
}
}
area = 0;
for (j = 0; j < numwindings[i&1]; j++)
{
area += WindingArea(&windingsbuf[i&1][j]);
}
return area;
}
/*
=============
VL_R_WindingAreaOutsideSolid
=============
*/
float VL_R_WindingAreaOutsideSolid(winding_t *w, vec3_t normal, int nodenum)
{
int leafnum, res;
float area;
dnode_t *node;
dleaf_t *leaf;
dplane_t *plane;
winding_t back;
plane_t split;
area = 0;
while(nodenum >= 0)
{
node = &dnodes[nodenum];
plane = &dplanes[node->planeNum];
VectorCopy(plane->normal, split.normal);
split.dist = plane->dist;
res = VL_SplitWinding (w, &back, &split, 0.1);
if (res == SIDE_FRONT)
{
nodenum = node->children[0];
}
else if (res == SIDE_BACK)
{
nodenum = node->children[1];
}
else if (res == SIDE_ON)
{
if (DotProduct(normal, plane->normal) > 0)
nodenum = node->children[0];
else
nodenum = node->children[1];
}
else
{
area += VL_R_WindingAreaOutsideSolid(&back, normal, node->children[1]);
nodenum = node->children[0];
}
}
leafnum = -nodenum - 1;
leaf = &dleafs[leafnum];
if (leaf->cluster != -1)
{
area += VL_WindingAreaOutsideBrushes(w, &dleafbrushes[leaf->firstLeafBrush], leaf->numLeafBrushes);
}
return area;
}
/*
=============
VL_WindingAreaOutsideSolid
=============
*/
float VL_WindingAreaOutsideSolid(winding_t *w, vec3_t normal)
{
return VL_R_WindingAreaOutsideSolid(w, normal, 0);
}
/*
=============
VL_ChopWindingWithFacet
=============
*/
float VL_ChopWindingWithFacet(winding_t *w, lFacet_t *facet)
{
int i;
for (i = 0; i < facet->numpoints; i++)
{
if (VL_ChopWinding(w, &facet->boundaries[i], 0) == SIDE_BACK)
return 0;
}
if (nostitching)
return WindingArea(w);
else
return VL_WindingAreaOutsideSolid(w, facet->plane.normal);
}
/*
=============
VL_CalcVisibleLightmapPixelArea
nice brute force ;)
=============
*/
void VL_CalcVisibleLightmapPixelArea(void)
{
int i, j, x, y, k;
dsurface_t *ds;
lsurfaceTest_t *test;
mesh_t *mesh;
winding_t w, tmpw;
float area;
_printf("calculating visible lightmap pixel area...\n");
for ( i = 0 ; i < numDrawSurfaces ; i++ )
{
test = lsurfaceTest[ i ];
if (!test)
continue;
ds = &drawSurfaces[ i ];
if ( ds->lightmapNum < 0 )
continue;
for (y = 0; y < ds->lightmapHeight; y++)
{
for (x = 0; x < ds->lightmapWidth; x++)
{
if (ds->surfaceType == MST_PATCH)
{
if (y == ds->lightmapHeight-1)
continue;
if (x == ds->lightmapWidth-1)
continue;
mesh = lsurfaceTest[i]->detailMesh;
VectorCopy( mesh->verts[y*mesh->width+x].xyz, w.points[0]);
VectorCopy( mesh->verts[(y+1)*mesh->width+x].xyz, w.points[1]);
VectorCopy( mesh->verts[(y+1)*mesh->width+x+1].xyz, w.points[2]);
VectorCopy( mesh->verts[y*mesh->width+x+1].xyz, w.points[3]);
w.numpoints = 4;
if (nostitching)
area = WindingArea(&w);
else
area = VL_WindingAreaOutsideSolid(&w, mesh->verts[y*mesh->width+x].normal);
}
else
{
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT, ds->lightmapVecs[0], w.points[0]);
VectorMA(w.points[0], (float) y - LIGHTMAP_PIXELSHIFT, ds->lightmapVecs[1], w.points[0]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT, ds->lightmapVecs[0], w.points[3]);
VectorMA(w.points[3], (float) y - LIGHTMAP_PIXELSHIFT + 1, ds->lightmapVecs[1], w.points[3]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1, ds->lightmapVecs[0], w.points[2]);
VectorMA(w.points[2], (float) y - LIGHTMAP_PIXELSHIFT + 1, ds->lightmapVecs[1], w.points[2]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1, ds->lightmapVecs[0], w.points[1]);
VectorMA(w.points[1], (float) y - LIGHTMAP_PIXELSHIFT, ds->lightmapVecs[1], w.points[1]);
w.numpoints = 4;
area = 0;
for (j = 0; j < test->numFacets; j++)
{
memcpy(&tmpw, &w, sizeof(winding_t));
area += VL_ChopWindingWithFacet(&tmpw, &test->facets[j]);
}
}
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
lightmappixelarea[k] = area;
}
}
}
}
/*
=============
VL_FindAdjacentSurface
=============
*/
int VL_FindAdjacentSurface(int surfaceNum, int facetNum, vec3_t p1, vec3_t p2, int *sNum, int *fNum, int *point)
{
int i, j, k;
lsurfaceTest_t *test;
lFacet_t *facet;
dsurface_t *ds;
float *fp1, *fp2;
vec3_t dir;
plane_t *facetplane;
// winding_t w;
facetplane = &lsurfaceTest[surfaceNum]->facets[facetNum].plane;
// DebugNet_RemoveAllPolys();
// memcpy(w.points, lsurfaceTest[surfaceNum]->facets[facetNum].points,
// lsurfaceTest[surfaceNum]->facets[facetNum].numpoints * sizeof(vec3_t));
// w.numpoints = lsurfaceTest[surfaceNum]->facets[facetNum].numpoints;
// DebugNet_DrawWinding(&w, 2);
for ( i = 0 ; i < numDrawSurfaces ; i++ )
{
if (i == surfaceNum)
continue;
test = lsurfaceTest[ i ];
if (!test)
continue;
if (test->trisoup)// || test->patch)
continue;
ds = &drawSurfaces[i];
if ( ds->lightmapNum < 0 )
continue;
//if this surface is not even near the edge
VectorSubtract(p1, test->origin, dir);
if (fabs(dir[0]) > test->radius ||
fabs(dir[1]) > test->radius ||
fabs(dir[1]) > test->radius)
{
VectorSubtract(p2, test->origin, dir);
if (fabs(dir[0]) > test->radius ||
fabs(dir[1]) > test->radius ||
fabs(dir[1]) > test->radius)
{
continue;
}
}
//
for (j = 0; j < test->numFacets; j++)
{
facet = &test->facets[j];
//
//if (!Plane_Equal(&facet->plane, facetplane, qfalse))
if (DotProduct(facet->plane.normal, facetplane->normal) < 0.9)
{
if (!test->trisoup && !test->patch)
break;
continue;
}
//
for (k = 0; k < facet->numpoints; k++)
{
fp1 = facet->points[k];
if (fabs(p2[0] - fp1[0]) < 0.1 &&
fabs(p2[1] - fp1[1]) < 0.1 &&
fabs(p2[2] - fp1[2]) < 0.1)
{
fp2 = facet->points[(k+1) % facet->numpoints];
if (fabs(p1[0] - fp2[0]) < 0.1 &&
fabs(p1[1] - fp2[1]) < 0.1 &&
fabs(p1[2] - fp2[2]) < 0.1)
{
// memcpy(w.points, facet->points, facet->numpoints * sizeof(vec3_t));
// w.numpoints = facet->numpoints;
// DebugNet_DrawWinding(&w, 1);
*sNum = i;
*fNum = j;
*point = k;
return qtrue;
}
}
/*
else if (fabs(p1[0] - fp1[0]) < 0.1 &&
fabs(p1[1] - fp1[1]) < 0.1 &&
fabs(p1[2] - fp1[2]) < 0.1)
{
fp2 = facet->points[(k+1) % facet->numpoints];
if (fabs(p2[0] - fp2[0]) < 0.1 &&
fabs(p2[1] - fp2[1]) < 0.1 &&
fabs(p2[2] - fp2[2]) < 0.1)
{
// memcpy(w.points, facet->points, facet->numpoints * sizeof(vec3_t));
// w.numpoints = facet->numpoints;
// DebugNet_DrawWinding(&w, 1);
*sNum = i;
*fNum = j;
*point = k;
return qtrue;
}
}
//*/
}
}
}
return qfalse;
}
/*
=============
VL_SmoothenLightmapEdges
this code is used to smoothen lightmaps across surface edges
=============
*/
void VL_SmoothenLightmapEdges(void)
{
int i, j, k, coords1[2][2];
float coords2[2][2];
int x1, y1, xinc1, yinc1, k1, k2;
float x2, y2, xinc2, yinc2, length;
int surfaceNum, facetNum, point;
lsurfaceTest_t *test;
lFacet_t *facet1, *facet2;
dsurface_t *ds1, *ds2;
float *p[2], s, t, *color1, *color2;
vec3_t dir, cross;
for ( i = 0 ; i < numDrawSurfaces ; i++ )
{
test = lsurfaceTest[ i ];
if (!test)
continue;
if (test->trisoup)// || test->patch)
continue;
ds1 = &drawSurfaces[i];
if ( ds1->lightmapNum < 0 )
continue;
for (j = 0; j < test->numFacets; j++)
{
facet1 = &test->facets[j];
//
for (k = 0; k < facet1->numpoints; k++)
{
p[0] = facet1->points[k];
p[1] = facet1->points[(k+1)%facet1->numpoints];
//
coords1[0][0] = facet1->lightmapCoords[k][0] * LIGHTMAP_SIZE;
coords1[0][1] = facet1->lightmapCoords[k][1] * LIGHTMAP_SIZE;
coords1[1][0] = facet1->lightmapCoords[(k+1)%facet1->numpoints][0] * LIGHTMAP_SIZE;
coords1[1][1] = facet1->lightmapCoords[(k+1)%facet1->numpoints][1] * LIGHTMAP_SIZE;
if (coords1[0][0] >= LIGHTMAP_SIZE)
coords1[0][0] = LIGHTMAP_SIZE-1;
if (coords1[0][1] >= LIGHTMAP_SIZE)
coords1[0][1] = LIGHTMAP_SIZE-1;
if (coords1[1][0] >= LIGHTMAP_SIZE)
coords1[1][0] = LIGHTMAP_SIZE-1;
if (coords1[1][1] >= LIGHTMAP_SIZE)
coords1[1][1] = LIGHTMAP_SIZE-1;
// try one row or column further because on flat faces the lightmap can
// extend beyond the edge
VectorSubtract(p[1], p[0], dir);
VectorNormalize(dir, dir);
CrossProduct(dir, facet1->plane.normal, cross);
//
if (coords1[0][0] - coords1[1][0] == 0)
{
s = DotProduct( cross, facet1->lightmapMatrix[0] );
coords1[0][0] += s < 0 ? 1 : -1;
coords1[1][0] += s < 0 ? 1 : -1;
if (coords1[0][0] < ds1->lightmapX || coords1[0][0] >= ds1->lightmapX + ds1->lightmapWidth)
{
coords1[0][0] += s < 0 ? -1 : 1;
coords1[1][0] += s < 0 ? -1 : 1;
}
length = fabs(coords1[1][1] - coords1[0][1]);
}
else if (coords1[0][1] - coords1[1][1] == 0)
{
t = DotProduct( cross, facet1->lightmapMatrix[1] );
coords1[0][1] += t < 0 ? 1 : -1;
coords1[1][1] += t < 0 ? 1 : -1;
if (coords1[0][1] < ds1->lightmapY || coords1[0][1] >= ds1->lightmapY + ds1->lightmapHeight)
{
coords1[0][1] += t < 0 ? -1 : 1;
coords1[1][1] += t < 0 ? -1 : 1;
}
length = fabs(coords1[1][0] - coords1[0][0]);
}
else
{
//the edge is not parallell to one of the lightmap axis
continue;
}
//
x1 = coords1[0][0];
y1 = coords1[0][1];
xinc1 = coords1[1][0] - coords1[0][0];
if (xinc1 < 0) xinc1 = -1;
if (xinc1 > 0) xinc1 = 1;
yinc1 = coords1[1][1] - coords1[0][1];
if (yinc1 < 0) yinc1 = -1;
if (yinc1 > 0) yinc1 = 1;
// the edge should be parallell to one of the lightmap axis
if (xinc1 != 0 && yinc1 != 0)
continue;
//
if (!VL_FindAdjacentSurface(i, j, p[0], p[1], &surfaceNum, &facetNum, &point))
continue;
//
ds2 = &drawSurfaces[surfaceNum];
facet2 = &lsurfaceTest[surfaceNum]->facets[facetNum];
coords2[0][0] = facet2->lightmapCoords[(point+1)%facet2->numpoints][0] * LIGHTMAP_SIZE;
coords2[0][1] = facet2->lightmapCoords[(point+1)%facet2->numpoints][1] * LIGHTMAP_SIZE;
coords2[1][0] = facet2->lightmapCoords[point][0] * LIGHTMAP_SIZE;
coords2[1][1] = facet2->lightmapCoords[point][1] * LIGHTMAP_SIZE;
if (coords2[0][0] >= LIGHTMAP_SIZE)
coords2[0][0] = LIGHTMAP_SIZE-1;
if (coords2[0][1] >= LIGHTMAP_SIZE)
coords2[0][1] = LIGHTMAP_SIZE-1;
if (coords2[1][0] >= LIGHTMAP_SIZE)
coords2[1][0] = LIGHTMAP_SIZE-1;
if (coords2[1][1] >= LIGHTMAP_SIZE)
coords2[1][1] = LIGHTMAP_SIZE-1;
//
x2 = coords2[0][0];
y2 = coords2[0][1];
xinc2 = coords2[1][0] - coords2[0][0];
if (length)
xinc2 = xinc2 / length;
yinc2 = coords2[1][1] - coords2[0][1];
if (length)
yinc2 = yinc2 / length;
// the edge should be parallell to one of the lightmap axis
if ((int) xinc2 != 0 && (int) yinc2 != 0)
continue;
//
while(1)
{
k1 = ( ds1->lightmapNum * LIGHTMAP_HEIGHT + y1) * LIGHTMAP_WIDTH + x1;
k2 = ( ds2->lightmapNum * LIGHTMAP_HEIGHT + ((int) y2)) * LIGHTMAP_WIDTH + ((int) x2);
color1 = lightFloats + k1*3;
color2 = lightFloats + k2*3;
if (lightmappixelarea[k1] < 0.01)
{
color1[0] = color2[0];
color1[1] = color2[1];
color1[2] = color2[2];
}
else
{
color1[0] = (float) color2[0] * 0.7 + (float) color1[0] * 0.3;
color1[1] = (float) color2[1] * 0.7 + (float) color1[1] * 0.3;
color1[2] = (float) color2[2] * 0.7 + (float) color1[2] * 0.3;
}
//
if (x1 == coords1[1][0] &&
y1 == coords1[1][1])
break;
x1 += xinc1;
y1 += yinc1;
x2 += xinc2;
y2 += yinc2;
if (x2 < ds2->lightmapX)
x2 = ds2->lightmapX;
if (x2 >= ds2->lightmapX + ds2->lightmapWidth)
x2 = ds2->lightmapX + ds2->lightmapWidth-1;
if (y2 < ds2->lightmapY)
y2 = ds2->lightmapY;
if (y2 >= ds2->lightmapY + ds2->lightmapHeight)
y2 = ds2->lightmapY + ds2->lightmapHeight-1;
}
}
}
}
}
/*
=============
VL_FixLightmapEdges
=============
*/
void VL_FixLightmapEdges(void)
{
int i, j, x, y, k, foundvalue, height, width, index;
int pos, top, bottom;
dsurface_t *ds;
lsurfaceTest_t *test;
float color[3];
float *ptr;
byte filled[(LIGHTMAP_SIZE+1) * (LIGHTMAP_SIZE+1) / 8];
float lightmap_edge_epsilon;
lightmap_edge_epsilon = 0.1 * samplesize;
for ( i = 0 ; i < numDrawSurfaces ; i++ )
{
test = lsurfaceTest[ i ];
if (!test)
continue;
ds = &drawSurfaces[ i ];
if ( ds->lightmapNum < 0 )
continue;
if (ds->surfaceType == MST_PATCH)
{
height = ds->lightmapHeight - 1;
width = ds->lightmapWidth - 1;
}
else
{
height = ds->lightmapHeight;
width = ds->lightmapWidth;
}
memset(filled, 0, sizeof(filled));
// printf("\n");
for (x = 0; x < width; x++)
{
for (y = 0; y < height; y++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
if (lightmappixelarea[k] > lightmap_edge_epsilon)
{
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x;
filled[index >> 3] |= 1 << (index & 7);
// printf("*");
}
// else
// printf("_");
}
// printf("\n");
}
for (y = 0; y < height; y++)
{
pos = -2;
for (x = 0; x < width; x++)
{
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x;
if (pos == -2)
{
if (filled[index >> 3] & (1 << (index & 7)))
pos = -1;
}
else if (pos == -1)
{
if (!(filled[index >> 3] & (1 << (index & 7))))
pos = x - 1;
}
else
{
if (filled[index >> 3] & (1 << (index & 7)))
{
bottom = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + pos;
top = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
for (j = 0; j < (x - pos + 1) / 2; j++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + pos + j + 1;
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + pos + j + 1;
filled[index >> 3] |= 1 << (index & 7);
(lightFloats + k*3)[0] = (lightFloats + top*3)[0];
(lightFloats + k*3)[1] = (lightFloats + top*3)[1];
(lightFloats + k*3)[2] = (lightFloats + top*3)[2];
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x - j - 1;
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x - j - 1;
filled[index >> 3] |= 1 << (index & 7);
(lightFloats + k*3)[0] = (lightFloats + bottom*3)[0];
(lightFloats + k*3)[1] = (lightFloats + bottom*3)[1];
(lightFloats + k*3)[2] = (lightFloats + bottom*3)[2];
}
pos = -1;
}
}
}
}
for (x = 0; x < width; x++)
{
pos = -2;
for (y = 0; y < height; y++)
{
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x;
if (pos == -2)
{
if (filled[index >> 3] & (1 << (index & 7)))
pos = -1;
}
else if (pos == -1)
{
if (!(filled[index >> 3] & (1 << (index & 7))))
pos = y - 1;
}
else
{
if (filled[index >> 3] & (1 << (index & 7)))
{
bottom = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + pos)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
top = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
for (j = 0; j < (y - pos + 1) / 2; j++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + pos + j + 1)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
index = (ds->lightmapY + pos + j + 1) * LIGHTMAP_WIDTH + ds->lightmapX + x;
filled[index >> 3] |= 1 << (index & 7);
(lightFloats + k*3)[0] = (lightFloats + top*3)[0];
(lightFloats + k*3)[1] = (lightFloats + top*3)[1];
(lightFloats + k*3)[2] = (lightFloats + top*3)[2];
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y - j - 1)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
index = (ds->lightmapY + y - j - 1) * LIGHTMAP_WIDTH + ds->lightmapX + x;
filled[index >> 3] |= 1 << (index & 7);
(lightFloats + k*3)[0] = (lightFloats + bottom*3)[0];
(lightFloats + k*3)[1] = (lightFloats + bottom*3)[1];
(lightFloats + k*3)[2] = (lightFloats + bottom*3)[2];
}
pos = -1;
}
}
}
}
for (y = 0; y < height; y++)
{
foundvalue = qfalse;
for (x = 0; x < width; x++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x;
if (foundvalue)
{
if (filled[index >> 3] & (1 << (index & 7)))
{
ptr = lightFloats + k*3;
color[0] = ptr[0];
color[1] = ptr[1];
color[2] = ptr[2];
}
else
{
ptr = lightFloats + k*3;
ptr[0] = color[0];
ptr[1] = color[1];
ptr[2] = color[2];
filled[index >> 3] |= 1 << (index & 7);
}
}
else
{
if (filled[index >> 3] & (1 << (index & 7)))
{
ptr = lightFloats + k*3;
color[0] = ptr[0];
color[1] = ptr[1];
color[2] = ptr[2];
foundvalue = qtrue;
}
}
}
foundvalue = qfalse;
for (x = width-1; x >= 0; x--)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x;
if (foundvalue)
{
if (filled[index >> 3] & (1 << (index & 7)))
{
ptr = lightFloats + k*3;
color[0] = ptr[0];
color[1] = ptr[1];
color[2] = ptr[2];
}
else
{
ptr = lightFloats + k*3;
ptr[0] = color[0];
ptr[1] = color[1];
ptr[2] = color[2];
filled[index >> 3] |= 1 << (index & 7);
}
}
else
{
if (filled[index >> 3] & (1 << (index & 7)))
{
ptr = lightFloats + k*3;
color[0] = ptr[0];
color[1] = ptr[1];
color[2] = ptr[2];
foundvalue = qtrue;
}
}
}
}
for (x = 0; x < width; x++)
{
foundvalue = qfalse;
for (y = 0; y < height; y++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x;
if (foundvalue)
{
if (filled[index >> 3] & (1 << (index & 7)))
{
ptr = lightFloats + k*3;
color[0] = ptr[0];
color[1] = ptr[1];
color[2] = ptr[2];
}
else
{
ptr = lightFloats + k*3;
ptr[0] = color[0];
ptr[1] = color[1];
ptr[2] = color[2];
filled[index >> 3] |= 1 << (index & 7);
}
}
else
{
if (filled[index >> 3] & (1 << (index & 7)))
{
ptr = lightFloats + k*3;
color[0] = ptr[0];
color[1] = ptr[1];
color[2] = ptr[2];
foundvalue = qtrue;
}
}
}
foundvalue = qfalse;
for (y = height-1; y >= 0; y--)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
index = (ds->lightmapY + y) * LIGHTMAP_WIDTH + ds->lightmapX + x;
if (foundvalue)
{
if (filled[index >> 3] & (1 << (index & 7)))
{
ptr = lightFloats + k*3;
color[0] = ptr[0];
color[1] = ptr[1];
color[2] = ptr[2];
}
else
{
ptr = lightFloats + k*3;
ptr[0] = color[0];
ptr[1] = color[1];
ptr[2] = color[2];
filled[index >> 3] |= 1 << (index & 7);
}
}
else
{
if (filled[index >> 3] & (1 << (index & 7)))
{
ptr = lightFloats + k*3;
color[0] = ptr[0];
color[1] = ptr[1];
color[2] = ptr[2];
foundvalue = qtrue;
}
}
}
}
if (ds->surfaceType == MST_PATCH)
{
x = ds->lightmapWidth-1;
for (y = 0; y < ds->lightmapHeight; y++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
ptr = lightFloats + k*3;
ptr[0] = (lightFloats + (k-1)*3)[0];
ptr[1] = (lightFloats + (k-1)*3)[1];
ptr[2] = (lightFloats + (k-1)*3)[2];
}
y = ds->lightmapHeight-1;
for (x = 0; x < ds->lightmapWidth; x++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
ptr = lightFloats + k*3;
ptr[0] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[0];
ptr[1] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[1];
ptr[2] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[2];
}
}
/*
//colored debug edges
if (ds->surfaceType == MST_PATCH)
{
x = ds->lightmapWidth-1;
for (y = 0; y < ds->lightmapHeight; y++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
ptr = lightFloats + k*3;
ptr[0] = 255;
ptr[1] = 0;
ptr[2] = 0;
}
y = ds->lightmapHeight-1;
for (x = 0; x < ds->lightmapWidth; x++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
ptr = lightFloats + k*3;
ptr[0] = 0;
ptr[1] = 255;
ptr[2] = 0;
}
}
//*/
}
//
VL_SmoothenLightmapEdges();
}
/*
=============
VL_ShiftPatchLightmaps
=============
*/
void VL_ShiftPatchLightmaps(void)
{
int i, j, x, y, k;
drawVert_t *verts;
dsurface_t *ds;
lsurfaceTest_t *test;
float *ptr;
for ( i = 0 ; i < numDrawSurfaces ; i++ )
{
test = lsurfaceTest[ i ];
if (!test)
continue;
ds = &drawSurfaces[ i ];
if ( ds->lightmapNum < 0 )
continue;
if (ds->surfaceType != MST_PATCH)
continue;
for (x = ds->lightmapWidth; x > 0; x--)
{
for (y = 0; y <= ds->lightmapHeight; y++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
ptr = lightFloats + k*3;
ptr[0] = (lightFloats + (k-1)*3)[0];
ptr[1] = (lightFloats + (k-1)*3)[1];
ptr[2] = (lightFloats + (k-1)*3)[2];
}
}
for (y = ds->lightmapHeight; y > 0; y--)
{
for (x = 0; x <= ds->lightmapWidth; x++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
ptr = lightFloats + k*3;
ptr[0] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[0];
ptr[1] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[1];
ptr[2] = (lightFloats + (k-LIGHTMAP_WIDTH)*3)[2];
}
}
verts = &drawVerts[ ds->firstVert ];
for ( j = 0 ; j < ds->patchHeight * ds->patchWidth; j++ )
{
verts[j].lightmap[0] += 0.5 / LIGHTMAP_WIDTH;
verts[j].lightmap[1] += 0.5 / LIGHTMAP_HEIGHT;
}
ds->lightmapHeight++;
ds->lightmapWidth++;
}
}
/*
=============
VL_StoreLightmap
=============
*/
void VL_StoreLightmap(void)
{
int i, x, y, k;
dsurface_t *ds;
lsurfaceTest_t *test;
float *src;
byte *dst;
_printf("storing lightmaps...\n");
//fix lightmap edges before storing them
VL_FixLightmapEdges();
//
#ifdef LIGHTMAP_PATCHSHIFT
VL_ShiftPatchLightmaps();
#endif
//
for ( i = 0 ; i < numDrawSurfaces ; i++ )
{
test = lsurfaceTest[ i ];
if (!test)
continue;
ds = &drawSurfaces[ i ];
if ( ds->lightmapNum < 0 )
continue;
for (y = 0; y < ds->lightmapHeight; y++)
{
for (x = 0; x < ds->lightmapWidth; x++)
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
VectorAdd((lightFloats + k*3), lightAmbientColor, (lightFloats + k*3));
src = &lightFloats[k*3];
dst = lightBytes + k*3;
ColorToBytes(src, dst);
}
}
}
}
/*
=============
PointInLeafnum
=============
*/
int PointInLeafnum(vec3_t point)
{
int nodenum;
vec_t dist;
dnode_t *node;
dplane_t *plane;
nodenum = 0;
while (nodenum >= 0)
{
node = &dnodes[nodenum];
plane = &dplanes[node->planeNum];
dist = DotProduct (point, plane->normal) - plane->dist;
if (dist > 0)
nodenum = node->children[0];
else
nodenum = node->children[1];
}
return -nodenum - 1;
}
/*
=============
VL_PointInLeafnum_r
=============
*/
int VL_PointInLeafnum_r(vec3_t point, int nodenum)
{
int leafnum;
vec_t dist;
dnode_t *node;
dplane_t *plane;
while (nodenum >= 0)
{
node = &dnodes[nodenum];
plane = &dplanes[node->planeNum];
dist = DotProduct (point, plane->normal) - plane->dist;
if (dist > 0.1)
{
nodenum = node->children[0];
}
else if (dist < -0.1)
{
nodenum = node->children[1];
}
else
{
leafnum = VL_PointInLeafnum_r(point, node->children[0]);
if (dleafs[leafnum].cluster != -1)
return leafnum;
nodenum = node->children[1];
}
}
leafnum = -nodenum - 1;
return leafnum;
}
/*
=============
VL_PointInLeafnum
=============
*/
int VL_PointInLeafnum(vec3_t point)
{
return VL_PointInLeafnum_r(point, 0);
}
/*
=============
VL_LightLeafnum
=============
*/
int VL_LightLeafnum(vec3_t point)
{
/*
int leafnum;
dleaf_t *leaf;
float x, y, z;
vec3_t test;
leafnum = VL_PointInLeafnum(point);
leaf = &dleafs[leafnum];
if (leaf->cluster != -1)
return leafnum;
for (z = 1; z >= -1; z -= 1)
{
for (x = 1; x >= -1; x -= 1)
{
for (y = 1; y >= -1; y -= 1)
{
VectorCopy(point, test);
test[0] += x;
test[1] += y;
test[2] += z;
leafnum = VL_PointInLeafnum(test);
leaf = &dleafs[leafnum];
if (leaf->cluster != -1)
{
VectorCopy(test, point);
return leafnum;
}
}
}
}
return leafnum;
*/
return VL_PointInLeafnum(point);
}
//#define LIGHTPOLYS
#ifdef LIGHTPOLYS
winding_t *lightwindings[MAX_MAP_DRAW_SURFS];
int numlightwindings;
/*
=============
VL_DrawLightWindings
=============
*/
void VL_DrawLightWindings(void)
{
int i;
for (i = 0; i < numlightwindings; i++)
{
#ifdef DEBUGNET
DebugNet_DrawWinding(lightwindings[i], 1);
#endif
}
}
/*
=============
VL_LightSurfaceWithVolume
=============
*/
void VL_LightSurfaceWithVolume(int surfaceNum, int facetNum, vlight_t *light, lightvolume_t *volume)
{
winding_t *w;
lsurfaceTest_t *test;
lFacet_t *facet;
int i;
test = lsurfaceTest[ surfaceNum ];
facet = &test->facets[ facetNum ];
//
w = (winding_t *) malloc(sizeof(winding_t));
memcpy(w->points, facet->points, sizeof(vec3_t) * facet->numpoints);
w->numpoints = facet->numpoints;
for (i = 0; i < volume->numplanes; i++)
{
//if totally on the back
if (VL_ChopWinding(w, &volume->planes[i], 0.01) == SIDE_BACK)
return;
}
lightwindings[numlightwindings] = w;
numlightwindings++;
if (numlightwindings >= MAX_MAP_DRAW_SURFS)
Error("MAX_LIGHTWINDINGS");
}
#else
/*
=============
VL_LightSurfaceWithVolume
=============
*/
/*
int VL_PointInsideLightVolume(vec3_t point, lightvolume_t *volume)
{
int i;
float d;
for (i = 0; i < volume->numplanes; i++)
{
d = DotProduct(volume->planes[i].normal, point) - volume->planes[i].dist;
if (d < 0) return qfalse;
}
return qtrue;
}
void VL_LightSurfaceWithVolume( int surfaceNum, int facetNum, vlight_t *light, lightvolume_t *volume )
{
dsurface_t *ds;
int i, j, k;
int numPositions;
vec3_t base, normal, color;
int sampleWidth, sampleHeight;
vec3_t lightmapOrigin, lightmapVecs[2], dir;
unsigned char *ptr;
float add, dist, angle;
mesh_t * mesh;
ds = &drawSurfaces[surfaceNum];
// vertex-lit triangle model
if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
return;
}
if ( ds->lightmapNum < 0 ) {
return; // doesn't need lighting
}
if ( ds->surfaceType == MST_PATCH ) {
mesh = lsurfaceTest[surfaceNum]->detailMesh;
} else {
VectorCopy( ds->lightmapVecs[2], normal );
VectorCopy( ds->lightmapOrigin, lightmapOrigin );
VectorCopy( ds->lightmapVecs[0], lightmapVecs[0] );
VectorCopy( ds->lightmapVecs[1], lightmapVecs[1] );
}
sampleWidth = ds->lightmapWidth;
sampleHeight = ds->lightmapHeight;
//calculate lightmap
for ( i = 0 ; i < sampleWidth; i++ ) {
for ( j = 0 ; j < sampleHeight; j++ ) {
if ( ds->patchWidth ) {
numPositions = 9;
VectorCopy( mesh->verts[j*mesh->width+i].normal, normal );
// VectorNormalize( normal, normal );
// push off of the curve a bit
VectorMA( mesh->verts[j*mesh->width+i].xyz, 1, normal, base );
// MakeNormalVectors( normal, lightmapVecs[0], lightmapVecs[1] );
} else {
numPositions = 9;
for ( k = 0 ; k < 3 ; k++ ) {
base[k] = lightmapOrigin[k] + normal[k]
+ ((float) i) * lightmapVecs[0][k]
+ ((float) j) * lightmapVecs[1][k];
}
}
VectorAdd( base, surfaceOrigin[ surfaceNum ], base );
VectorSubtract(base, light->origin, dir);
dist = VectorNormalize(dir, dir);
if ( dist < 16 ) {
dist = 16;
}
angle = 1;//DotProduct( normal, dir ); //1;
if (angle > 1)
angle = 1;
if ( light->atten_disttype == LDAT_LINEAR ) {
add = angle * light->photons * lightLinearScale - dist;
if ( add < 0 ) {
add = 0;
}
} else {
add = light->photons / ( dist * dist ) * angle;
}
if (add <= 1.0)
continue;
if (VL_PointInsideLightVolume(base, volume))
{
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + j)
* LIGHTMAP_WIDTH + ds->lightmapX + i;
ptr = lightBytes + k*3;
color[0] = (float) ptr[0] + add * light->color[0];
color[1] = (float) ptr[1] + add * light->color[1];
color[2] = (float) ptr[2] + add * light->color[2];
ColorToBytes(color, ptr);
}
}
}
}
*/
/*
=============
VL_GetFilter
FIXME: don't use a lightmap pixel origin but use the four corner points
to map part of a translucent surface onto the lightmap pixel
=============
*/
void VL_GetFilter(vlight_t *light, lightvolume_t *volume, vec3_t lmp, vec3_t filter)
{
lFacet_t *facet;
lsurfaceTest_t *test;
float d, d1, d2, frac, s, t, ns;
int i, j, is, it, b;
int x, y, u, v, numsamples, radius, color[4], largest;
byte *image;
vec3_t point, origin, total;
VectorSet(filter, 1, 1, 1);
if (noalphashading)
return;
if (volume->numtransFacets <= 0)
return;
if (light->type == LIGHT_SURFACEDIRECTED)
{
// project the light map pixel origin onto the area light source plane
d = DotProduct(lmp, light->normal) - DotProduct(light->normal, light->w.points[0]);
VectorMA(lmp, -d, light->normal, origin);
}
else
{
VectorCopy(light->origin, origin);
}
for (i = 0; i < volume->numtransFacets; i++)
{
test = lsurfaceTest[ volume->transSurfaces[i] ];
facet = &test->facets[ volume->transFacets[i] ];
// if this surface does not cast an alpha shadow
if ( !(test->shader->surfaceFlags & SURF_ALPHASHADOW) )
continue;
// if there are no texture pixel available
if ( !test->shader->pixels ) {
continue;
}
//
d1 = DotProduct( origin, facet->plane.normal) - facet->plane.dist;
d2 = DotProduct( lmp, facet->plane.normal ) - facet->plane.dist;
// this should never happen because the light volume went through the facet
if ( ( d1 < 0 ) == ( d2 < 0 ) ) {
continue;
}
// calculate the crossing point
frac = d1 / ( d1 - d2 );
for ( j = 0 ; j < 3 ; j++ ) {
point[j] = origin[j] + frac * ( lmp[j] - origin[j] );
}
s = DotProduct( point, facet->textureMatrix[0] ) + facet->textureMatrix[0][3];
t = DotProduct( point, facet->textureMatrix[1] ) + facet->textureMatrix[1][3];
if (s < 0)
s = 0;
if (t < 0)
t = 0;
s = s - floor( s );
t = t - floor( t );
is = s * test->shader->width;
it = t * test->shader->height;
//if old style alpha shading
if (nocolorshading) {
image = test->shader->pixels + 4 * ( it * test->shader->width + is );
// alpha filter
b = image[3];
// alpha test makes this a binary option
b = b < 128 ? 0 : 255;
filter[0] = filter[0] * (255-b) / 255;
filter[1] = filter[1] * (255-b) / 255;
filter[2] = filter[2] * (255-b) / 255;
}
else {
VectorClear(total);
numsamples = 0;
radius = 2;
for ( u = -radius; u <= radius; u++ )
{
x = is + u;
if ( x < 0 || x >= test->shader->width)
continue;
for ( v = -radius; v <= radius; v++ )
{
y = it + v;
if ( y < 0 || y >= test->shader->height)
continue;
image = test->shader->pixels + 4 * ( y * test->shader->width + x );
color[0] = image[0];
color[1] = image[1];
color[2] = image[2];
largest = 0;
for (j = 0; j < 3; j++)
if (image[j] > largest)
largest = image[j];
if (largest <= 0 || image[3] == 0) {
color[0] = 255;
color[1] = 255;
color[2] = 255;
largest = 255;
}
total[0] += ((float) color[0]/largest) * (255-image[3]) / 255.0;
total[1] += ((float) color[1]/largest) * (255-image[3]) / 255.0;
total[2] += ((float) color[2]/largest) * (255-image[3]) / 255.0;
numsamples++;
}
}
ns = numsamples;
//
filter[0] *= total[0] / ns;
filter[1] *= total[1] / ns;
filter[2] *= total[2] / ns;
}
}
}
/*
=============
VL_LightSurfaceWithVolume
=============
*/
void VL_LightSurfaceWithVolume( int surfaceNum, int facetNum, vlight_t *light, lightvolume_t *volume )
{
int i;
dsurface_t *ds;
lFacet_t *facet;
lsurfaceTest_t *test;
winding_t w;
vec3_t base, dir, delta, normal, filter, origin;
int min_x[LIGHTMAP_SIZE+2], max_x[LIGHTMAP_SIZE+2];
int min_y, max_y, k, x, y, n;
float *color, distscale;
float d, add, angle, dist, area, insidearea, coords[MAX_POINTS_ON_WINDING+1][2];
mesh_t *mesh;
byte polygonedges[(LIGHTMAP_SIZE+1) * (LIGHTMAP_SIZE+1) / 8];
ds = &drawSurfaces[surfaceNum];
// vertex-lit triangle model
if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
return;
}
if ( ds->lightmapNum < 0 ) {
return; // doesn't need lighting
}
test = lsurfaceTest[ surfaceNum ];
facet = &test->facets[ facetNum ];
if (defaulttracelight && !test->always_vlight)
return;
if (test->always_tracelight)
return;
memcpy(w.points, facet->points, sizeof(vec3_t) * facet->numpoints);
w.numpoints = facet->numpoints;
for (i = 0; i < volume->numplanes; i++)
{
//if totally on the back
if (VL_ChopWinding(&w, &volume->planes[i], 0.01) == SIDE_BACK)
return;
}
// only one thread at a time may write to the lightmap of this surface
MutexLock(test->mutex);
test->numvolumes++;
if (ds->surfaceType == MST_PATCH)
{
// FIXME: reduce size and don't mark all as edge
min_y = ds->lightmapY + facet->y;
max_y = ds->lightmapY + facet->y + facet->height - 1;
for (y = min_y; y <= max_y; y++)
{
min_x[y] = ds->lightmapX + facet->x;
max_x[y] = ds->lightmapX + facet->x + facet->width - 1;
for (x = min_x[y]; x <= max_x[y]; x++)
{
n = y * LIGHTMAP_SIZE + x;
polygonedges[n >> 3] |= 1 << (n & 7);
}
}
}
else
{
for (i = 0; i < w.numpoints; i++)
{
float s, t;
if (i >= MAX_POINTS_ON_WINDING)
_printf("coords overflow\n");
if (ds->surfaceType != MST_PATCH)
{
VectorSubtract(w.points[i], facet->mins, delta);
s = DotProduct( delta, facet->lightmapMatrix[0] ) + ds->lightmapX + 0.5;
t = DotProduct( delta, facet->lightmapMatrix[1] ) + ds->lightmapY + 0.5;
if (s >= LIGHTMAP_SIZE)
s = LIGHTMAP_SIZE - 0.5;
if (s < 0)
s = 0;
if (t >= LIGHTMAP_SIZE)
t = LIGHTMAP_SIZE - 0.5;
if (t < 0)
t = 0;
coords[i][0] = s;
coords[i][1] = t;
}
else
{
s = DotProduct( w.points[i], facet->lightmapMatrix[0] ) + facet->lightmapMatrix[0][3];
t = DotProduct( w.points[i], facet->lightmapMatrix[1] ) + facet->lightmapMatrix[1][3];
s = s - floor( s );
t = t - floor( t );
coords[i][0] = ds->lightmapX + s * LIGHTMAP_SIZE;// + 0.5;
coords[i][1] = ds->lightmapY + t * LIGHTMAP_SIZE;// + 0.5;
if (coords[i][0] >= LIGHTMAP_SIZE)
coords[i][0] -= LIGHTMAP_SIZE;
if (coords[i][1] >= LIGHTMAP_SIZE)
coords[i][1] -= LIGHTMAP_SIZE;
if (coords[i][0] < ds->lightmapX)
coords[i][0] = ds->lightmapX;
if (coords[i][1] < ds->lightmapY)
coords[i][1] = ds->lightmapY;
}
x = coords[i][0];
y = coords[i][1];
if (x < ds->lightmapX || x >= LIGHTMAP_SIZE)
_printf("VL_LightSurfaceWithVolume: x outside lightmap\n");
if (y < ds->lightmapY || y >= LIGHTMAP_SIZE)
_printf("VL_LightSurfaceWithVolume: y outside lightmap\n");
}
coords[i][0] = coords[0][0];
coords[i][1] = coords[0][1];
//
min_y = LIGHTMAP_SIZE;
max_y = 0;
for (i = 0; i < LIGHTMAP_SIZE; i++)
{
min_x[i] = LIGHTMAP_SIZE;
max_x[i] = 0;
}
memset(polygonedges, 0, sizeof(polygonedges));
// scan convert the polygon onto the lightmap
// for each edge it marks *every* lightmap pixel the edge goes through
// so no brasenham and no scan conversion used for texture mapping but
// more something like ray casting
// this is necesary because we need all lightmap pixels totally or partly
// inside the light volume. these lightmap pixels are only lit for the part
// that they are inside the light volume.
for (i = 0; i < w.numpoints; i++)
{
float xf, yf, dx, dy, xstep, ystep, xfrac, yfrac;
int xinc, yinc;
xf = coords[i][0];
yf = coords[i][1];
dx = coords[i+1][0] - xf;
dy = coords[i+1][1] - yf;
//
x = (int) xf;
y = (int) yf;
//
if (y < min_y)
min_y = y;
if (y > max_y)
max_y = y;
//
if (fabs(dx) > fabs(dy))
{
if (dx > 0)
{
// y fraction at integer x below fractional x
yfrac = yf + (floor(xf) - xf) * dy / dx;
xinc = 1;
}
else if (dx < 0)
{
// y fraction at integer x above fractional x
yfrac = yf + (floor(xf) + 1 - xf) * dy / dx;
xinc = -1;
}
else
{
yfrac = yf;
xinc = 0;
}
// step in y direction per 1 unit in x direction
if (dx)
ystep = dy / fabs(dx);
else
ystep = 0;
while(1)
{
if (x < ds->lightmapX || x >= LIGHTMAP_SIZE)
_printf("VL_LightSurfaceWithVolume: x outside lightmap\n");
if (y < ds->lightmapY || y >= LIGHTMAP_SIZE)
_printf("VL_LightSurfaceWithVolume: y outside lightmap\n");
//
n = y * LIGHTMAP_SIZE + x;
polygonedges[n >> 3] |= 1 << (n & 7);
if (x < min_x[y])
min_x[y] = x;
if (x > max_x[y])
max_x[y] = x;
if (x == (int) coords[i+1][0])
break;
yfrac += ystep;
if (dy > 0)
{
if (yfrac > (float) y + 1)
{
y += 1;
//
n = y * LIGHTMAP_SIZE + x;
polygonedges[n >> 3] |= 1 << (n & 7);
if (x < min_x[y])
min_x[y] = x;
if (x > max_x[y])
max_x[y] = x;
}
}
else
{
if (yfrac < (float) y)
{
y -= 1;
//
n = y * LIGHTMAP_SIZE + x;
polygonedges[n >> 3] |= 1 << (n & 7);
if (x < min_x[y])
min_x[y] = x;
if (x > max_x[y])
max_x[y] = x;
}
}
x += xinc;
}
}
else
{
if (dy > 0)
{
//x fraction at integer y below fractional y
xfrac = xf + (floor(yf) - yf) * dx / dy;
yinc = 1;
}
else if (dy < 0)
{
//x fraction at integer y above fractional y
xfrac = xf + (floor(yf) + 1 - yf) * dx / dy;
yinc = -1;
}
else
{
xfrac = xf;
yinc = 0;
}
// step in x direction per 1 unit in y direction
if (dy)
xstep = dx / fabs(dy);
else
xstep = 0;
while(1)
{
if (x < ds->lightmapX || x >= LIGHTMAP_SIZE)
_printf("VL_LightSurfaceWithVolume: x outside lightmap\n");
if (y < ds->lightmapY || y >= LIGHTMAP_SIZE)
_printf("VL_LightSurfaceWithVolume: y outside lightmap\n");
//
n = y * LIGHTMAP_SIZE + x;
polygonedges[n >> 3] |= 1 << (n & 7);
if (x < min_x[y])
min_x[y] = x;
if (x > max_x[y])
max_x[y] = x;
if (y == (int) coords[i+1][1])
break;
xfrac += xstep;
if (dx > 0)
{
if (xfrac > (float) x + 1)
{
x += 1;
//
n = y * LIGHTMAP_SIZE + x;
polygonedges[n >> 3] |= 1 << (n & 7);
if (x < min_x[y])
min_x[y] = x;
if (x > max_x[y])
max_x[y] = x;
}
}
else
{
if (xfrac < (float) x)
{
x -= 1;
//
n = y * LIGHTMAP_SIZE + x;
polygonedges[n >> 3] |= 1 << (n & 7);
if (x < min_x[y])
min_x[y] = x;
if (x > max_x[y])
max_x[y] = x;
}
}
y += yinc;
}
}
}
}
// map light onto the lightmap
for (y = min_y; y <= max_y; y++)
{
for (x = min_x[y]; x <= max_x[y]; x++)
{
if (ds->surfaceType == MST_PATCH)
{
mesh = test->detailMesh;
VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, base);
VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x-ds->lightmapX].normal, normal);
//VectorCopy(facet->plane.normal, normal);
}
else
{
VectorMA(ds->lightmapOrigin, (float) x - ds->lightmapX, ds->lightmapVecs[0], base);
VectorMA(base, (float) y - ds->lightmapY, ds->lightmapVecs[1], base);
VectorCopy(facet->plane.normal, normal);
}
if (light->type == LIGHT_POINTSPOT)
{
float distByNormal;
vec3_t pointAtDist;
float radiusAtDist;
float sampleRadius;
vec3_t distToSample;
float coneScale;
VectorSubtract( light->origin, base, dir );
distByNormal = -DotProduct( dir, light->normal );
if ( distByNormal < 0 ) {
continue;
}
VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
radiusAtDist = light->radiusByDist * distByNormal;
VectorSubtract( base, pointAtDist, distToSample );
sampleRadius = VectorLength( distToSample );
if ( sampleRadius >= radiusAtDist ) {
continue; // outside the cone
}
if ( sampleRadius <= radiusAtDist - 32 ) {
coneScale = 1.0; // fully inside
} else {
coneScale = ( radiusAtDist - sampleRadius ) / 32.0;
}
dist = VectorNormalize( dir, dir );
// clamp the distance to prevent super hot spots
if ( dist < 16 ) {
dist = 16;
}
angle = DotProduct( normal, dir );
if (angle > 1)
angle = 1;
if (angle > 0) {
if ( light->atten_angletype == LAAT_QUADRATIC ) {
angle = 1 - angle;
angle *= angle;
angle = 1 - angle;
}
else if ( light->atten_angletype == LAAT_DOUBLEQUADRATIC ) {
angle = 1 - angle;
angle *= angle * angle;
angle = 1 - angle;
}
}
if (light->atten_anglescale > 0) {
angle /= light->atten_anglescale;
if (angle > 1)
angle = 1;
}
if (light->atten_distscale > 0) {
distscale = light->atten_distscale;
}
else {
distscale = 1;
}
//
if ( light->atten_disttype == LDAT_NOSCALE ) {
add = angle * coneScale;
}
else if ( light->atten_disttype == LDAT_LINEAR ) {
add = angle * light->photons * lightLinearScale * coneScale - dist * distscale;
if ( add < 0 ) {
add = 0;
}
}
else {
add = light->photons / ( dist * dist * distscale) * angle * coneScale;
}
if (add <= 1.0)
continue;
}
else if (light->type == LIGHT_POINTFAKESURFACE)
{
// calculate the contribution
add = PointToPolygonFormFactor( base, normal, &light->w );
if ( add <= 0 ) {
if ( light->twosided ) {
add = -add;
} else {
continue;
}
}
}
else if (light->type == LIGHT_SURFACEDIRECTED)
{
//VectorCopy(light->normal, dir);
//VectorInverse(dir);
// project the light map pixel origin onto the area light source plane
d = DotProduct(base, light->normal) - DotProduct(light->normal, light->w.points[0]);
VectorMA(base, -d, light->normal, origin);
VectorSubtract(origin, base, dir);
dist = VectorNormalize(dir, dir);
if ( dist < 16 ) {
dist = 16;
}
//
angle = DotProduct( normal, dir );
if (angle > 1)
angle = 1;
if (angle > 0) {
if ( light->atten_angletype == LAAT_QUADRATIC ) {
angle = 1 - angle;
angle *= angle;
angle = 1 - angle;
}
else if ( light->atten_angletype == LAAT_DOUBLEQUADRATIC ) {
angle = 1 - angle;
angle *= angle * angle;
angle = 1 - angle;
}
}
if (light->atten_anglescale > 0) {
angle /= light->atten_anglescale;
if (angle > 1)
angle = 1;
}
if (light->atten_distscale > 0) {
distscale = light->atten_distscale;
}
else {
distscale = 1;
}
if ( light->atten_disttype == LDAT_NOSCALE ) {
add = angle;
}
else if ( light->atten_disttype == LDAT_LINEAR ) {
add = angle * light->photons * lightLinearScale - dist * distscale;
if ( add < 0 ) {
add = 0;
}
} else { //default quadratic
add = light->photons / ( dist * dist * distscale) * angle;
}
if (add <= 0)
continue;
}
else //normal radial point light
{
VectorSubtract(light->origin, base, dir);
dist = VectorNormalize(dir, dir);
if ( dist < 16 ) {
dist = 16;
}
angle = DotProduct( normal, dir );
if (angle > 1)
angle = 1;
if (angle > 0) {
if ( light->atten_angletype == LAAT_QUADRATIC ) {
angle = 1 - angle;
angle *= angle;
angle = 1 - angle;
}
else if ( light->atten_angletype == LAAT_DOUBLEQUADRATIC ) {
angle = 1 - angle;
angle *= angle * angle;
angle = 1 - angle;
}
}
if (light->atten_anglescale > 0) {
angle /= light->atten_anglescale;
if (angle > 1)
angle = 1;
}
if (light->atten_distscale > 0) {
distscale = light->atten_distscale;
}
else {
distscale = 1;
}
if ( light->atten_disttype == LDAT_NOSCALE ) {
add = angle;
}
else if ( light->atten_disttype == LDAT_LINEAR ) {
add = angle * light->photons * lightLinearScale - dist * distscale;
if ( add < 0 ) {
add = 0;
}
} else {
add = light->photons / ( dist * dist * distscale) * angle;
}
if (add <= 1.0)
continue;
}
//
k = (ds->lightmapNum * LIGHTMAP_HEIGHT + y) * LIGHTMAP_WIDTH + x;
//if on one of the edges
n = y * LIGHTMAP_SIZE + x;
if ((polygonedges[n >> 3] & (1 << (n & 7)) ))
{
// multiply 'add' by the relative area being lit of the total visible lightmap pixel area
//
// first create a winding for the lightmap pixel
if (ds->surfaceType == MST_PATCH)
{
mesh = test->detailMesh;
if (y-ds->lightmapY >= mesh->height-1)
_printf("y outside mesh\n");
if (x-ds->lightmapX >= mesh->width-1)
_printf("x outside mesh\n");
VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, w.points[0]);
VectorCopy( mesh->verts[(y+1-ds->lightmapY)*mesh->width+x-ds->lightmapX].xyz, w.points[1]);
VectorCopy( mesh->verts[(y+1-ds->lightmapY)*mesh->width+x+1-ds->lightmapX].xyz, w.points[2]);
VectorCopy( mesh->verts[(y-ds->lightmapY)*mesh->width+x+1-ds->lightmapX].xyz, w.points[3]);
w.numpoints = 4;
}
else
{
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT - ds->lightmapX, ds->lightmapVecs[0], w.points[0]);
VectorMA(w.points[0], (float) y - LIGHTMAP_PIXELSHIFT - ds->lightmapY, ds->lightmapVecs[1], w.points[0]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT - ds->lightmapX, ds->lightmapVecs[0], w.points[1]);
VectorMA(w.points[1], (float) y - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapY, ds->lightmapVecs[1], w.points[1]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapX, ds->lightmapVecs[0], w.points[2]);
VectorMA(w.points[2], (float) y - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapY, ds->lightmapVecs[1], w.points[2]);
VectorMA(ds->lightmapOrigin, (float) x - LIGHTMAP_PIXELSHIFT + 1 - ds->lightmapX, ds->lightmapVecs[0], w.points[3]);
VectorMA(w.points[3], (float) y - LIGHTMAP_PIXELSHIFT - ds->lightmapY, ds->lightmapVecs[1], w.points[3]);
w.numpoints = 4;
}
//
// take the visible area of the lightmap pixel into account
//
//area = WindingArea(&w);
area = lightmappixelarea[k];
if (area <= 0)
continue;
// chop the lightmap pixel winding with the light volume
for (i = 0; i < volume->numplanes; i++)
{
//if totally on the back
if (VL_ChopWinding(&w, &volume->planes[i], 0) == SIDE_BACK)
break;
}
// if the lightmap pixel is partly inside the light volume
if (i >= volume->numplanes)
{
insidearea = WindingArea(&w);
if (insidearea <= 0)
i = 0;
add = add * insidearea / area;
}
else
{
//DebugNet_DrawWinding(&w, 2);
continue; // this shouldn't happen
}
}
// get the light filter from all the translucent surfaces the light volume went through
VL_GetFilter(light, volume, base, filter);
//
color = &lightFloats[k*3];
color[0] += add * light->color[0] * filter[0];
color[1] += add * light->color[1] * filter[1];
color[2] += add * light->color[2] * filter[2];
}
}
MutexUnlock(test->mutex);
}
#endif
/*
=============
VL_SplitLightVolume
=============
*/
int VL_SplitLightVolume(lightvolume_t *volume, lightvolume_t *back, plane_t *split, float epsilon)
{
lightvolume_t f, b;
vec_t dists[128];
int sides[128];
int counts[3];
vec_t dot;
int i, j;
vec_t *p1, *p2;
vec3_t mid;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for (i = 0; i < volume->numplanes; i++)
{
dot = DotProduct (volume->points[i], split->normal);
dot -= split->dist;
dists[i] = dot;
if (dot > epsilon)
sides[i] = SIDE_FRONT;
else if (dot < -epsilon)
sides[i] = SIDE_BACK;
else
{
sides[i] = SIDE_ON;
}
counts[sides[i]]++;
}
if (!counts[1])
return 0; // completely on front side
if (!counts[0])
return 1; // completely on back side
sides[i] = sides[0];
dists[i] = dists[0];
f.numplanes = 0;
b.numplanes = 0;
for (i = 0; i < volume->numplanes; i++)
{
p1 = volume->points[i];
if (f.numplanes >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_SplitLightVolume -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return 0; // can't chop -- fall back to original
}
if (b.numplanes >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_SplitLightVolume -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return 0; // can't chop -- fall back to original
}
if (sides[i] == SIDE_ON)
{
VectorCopy(p1, f.points[f.numplanes]);
VectorCopy(p1, b.points[b.numplanes]);
if (sides[i+1] == SIDE_BACK)
{
f.planes[f.numplanes] = *split;
b.planes[b.numplanes] = volume->planes[i];
}
else if (sides[i+1] == SIDE_FRONT)
{
f.planes[f.numplanes] = volume->planes[i];
b.planes[b.numplanes] = *split;
VectorInverse(b.planes[b.numplanes].normal);
b.planes[b.numplanes].dist = -b.planes[b.numplanes].dist;
}
else //this shouldn't happen
{
f.planes[f.numplanes] = *split;
b.planes[b.numplanes] = *split;
VectorInverse(b.planes[b.numplanes].normal);
b.planes[b.numplanes].dist = -b.planes[b.numplanes].dist;
}
f.numplanes++;
b.numplanes++;
continue;
}
if (sides[i] == SIDE_FRONT)
{
VectorCopy (p1, f.points[f.numplanes]);
f.planes[f.numplanes] = volume->planes[i];
f.numplanes++;
}
if (sides[i] == SIDE_BACK)
{
VectorCopy (p1, b.points[b.numplanes]);
b.planes[b.numplanes] = volume->planes[i];
b.numplanes++;
}
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
continue;
if (f.numplanes >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_SplitLightVolume -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return 0; // can't chop -- fall back to original
}
if (b.numplanes >= MAX_POINTS_ON_FIXED_WINDING)
{
_printf("WARNING: VL_SplitLightVolume -> MAX_POINTS_ON_FIXED_WINDING overflowed\n");
return 0; // can't chop -- fall back to original
}
// generate a split point
p2 = volume->points[(i+1)%volume->numplanes];
dot = dists[i] / (dists[i]-dists[i+1]);
for (j=0 ; j<3 ; j++)
{ // avoid round off error when possible
if (split->normal[j] == 1)
mid[j] = split->dist;
else if (split->normal[j] == -1)
mid[j] = -split->dist;
else
mid[j] = p1[j] + dot*(p2[j]-p1[j]);
}
VectorCopy (mid, f.points[f.numplanes]);
VectorCopy(mid, b.points[b.numplanes]);
if (sides[i+1] == SIDE_BACK)
{
f.planes[f.numplanes] = *split;
b.planes[b.numplanes] = volume->planes[i];
}
else
{
f.planes[f.numplanes] = volume->planes[i];
b.planes[b.numplanes] = *split;
VectorInverse(b.planes[b.numplanes].normal);
b.planes[b.numplanes].dist = -b.planes[b.numplanes].dist;
}
f.numplanes++;
b.numplanes++;
}
memcpy(volume->points, f.points, sizeof(vec3_t) * f.numplanes);
memcpy(volume->planes, f.planes, sizeof(plane_t) * f.numplanes);
volume->numplanes = f.numplanes;
memcpy(back->points, b.points, sizeof(vec3_t) * b.numplanes);
memcpy(back->planes, b.planes, sizeof(plane_t) * b.numplanes);
back->numplanes = b.numplanes;
return 2;
}
/*
=============
VL_PlaneForEdgeToWinding
=============
*/
void VL_PlaneForEdgeToWinding(vec3_t p1, vec3_t p2, winding_t *w, int windingonfront, plane_t *plane)
{
int i, j;
float length, d;
vec3_t v1, v2;
VectorSubtract(p2, p1, v1);
for (i = 0; i < w->numpoints; i++)
{
VectorSubtract (w->points[i], p1, v2);
plane->normal[0] = v1[1]*v2[2] - v1[2]*v2[1];
plane->normal[1] = v1[2]*v2[0] - v1[0]*v2[2];
plane->normal[2] = v1[0]*v2[1] - v1[1]*v2[0];
// if points don't make a valid plane, skip it
length = plane->normal[0] * plane->normal[0]
+ plane->normal[1] * plane->normal[1]
+ plane->normal[2] * plane->normal[2];
if (length < ON_EPSILON)
continue;
length = 1/sqrt(length);
plane->normal[0] *= length;
plane->normal[1] *= length;
plane->normal[2] *= length;
plane->dist = DotProduct (w->points[i], plane->normal);
//
for (j = 0; j < w->numpoints; j++)
{
if (j == i)
continue;
d = DotProduct(w->points[j], plane->normal) - plane->dist;
if (windingonfront)
{
if (d < -ON_EPSILON)
break;
}
else
{
if (d > ON_EPSILON)
break;
}
}
if (j >= w->numpoints)
return;
}
}
/*
=============
VL_R_CastLightAtSurface
=============
*/
void VL_R_FloodLight(vlight_t *light, lightvolume_t *volume, int cluster, int firstportal);
void VL_R_CastLightAtSurface(vlight_t *light, lightvolume_t *volume)
{
lsurfaceTest_t *test;
int i, n;
// light the surface with this volume
VL_LightSurfaceWithVolume(volume->surfaceNum, volume->facetNum, light, volume);
//
test = lsurfaceTest[ volume->surfaceNum ];
// if this is not a translucent surface
if ( !(test->shader->surfaceFlags & SURF_ALPHASHADOW) && !(test->shader->contents & CONTENTS_TRANSLUCENT))
return;
//
if (volume->numtransFacets >= MAX_TRANSLUCENTFACETS)
Error("a light valume went through more than %d translucent facets", MAX_TRANSLUCENTFACETS);
//add this translucent surface to the list
volume->transSurfaces[volume->numtransFacets] = volume->surfaceNum;
volume->transFacets[volume->numtransFacets] = volume->facetNum;
volume->numtransFacets++;
//clear the tested facets except the translucent ones
memset(volume->facetTested, 0, sizeof(volume->facetTested));
for (i = 0; i < volume->numtransFacets; i++)
{
test = lsurfaceTest[ volume->transSurfaces[i] ];
n = test->facets[volume->transFacets[i]].num;
volume->facetTested[n >> 3] |= 1 << (n & 7);
}
memset(volume->clusterTested, 0, sizeof(volume->clusterTested));
volume->endplane = volume->farplane;
volume->surfaceNum = -1;
volume->facetNum = 0;
VL_R_FloodLight(light, volume, volume->cluster, 0);
if (volume->surfaceNum >= 0)
{
VL_R_CastLightAtSurface(light, volume);
}
}
/*
=============
VL_R_SplitLightVolume
=============
*/
int numvolumes = 0;
int VL_R_SplitLightVolume(vlight_t *light, lightvolume_t *volume, plane_t *split, int cluster, int firstportal)
{
lightvolume_t back;
int res;
//
res = VL_SplitLightVolume(volume, &back, split, 0.1);
// if the volume was split
if (res == 2)
{
memcpy(back.clusterTested, volume->clusterTested, sizeof(back.clusterTested));
memcpy(back.facetTested, volume->facetTested, sizeof(back.facetTested));
back.num = numvolumes++;
back.endplane = volume->endplane;
back.surfaceNum = volume->surfaceNum;
back.facetNum = volume->facetNum;
back.type = volume->type;
back.cluster = volume->cluster;
back.farplane = volume->farplane;
if (volume->numtransFacets > 0)
{
memcpy(back.transFacets, volume->transFacets, sizeof(back.transFacets));
memcpy(back.transSurfaces, volume->transSurfaces, sizeof(back.transSurfaces));
}
back.numtransFacets = volume->numtransFacets;
//
// flood the volume at the back of the split plane
VL_R_FloodLight(light, &back, cluster, firstportal);
// if the back volume hit a surface
if (back.surfaceNum >= 0)
{
VL_R_CastLightAtSurface(light, &back);
}
}
return res;
}
/*
=============
VL_R_FloodLight
=============
*/
void VL_R_FloodLight(vlight_t *light, lightvolume_t *volume, int cluster, int firstportal)
{
int i, j, k, res, surfaceNum, backfaceculled, testculled;
float d;
winding_t winding, tmpwinding;
lleaf_t *leaf;
lportal_t *p;
lsurfaceTest_t *test;
lFacet_t *facet;
vec3_t dir1, dir2;
plane_t plane;
// DebugNet_RemoveAllPolys();
// VL_DrawLightVolume(light, volume);
// if the first portal is not zero then we've checked all occluders in this leaf already
if (firstportal == 0)
{
// check all potential occluders in this leaf
for (i = 0; i < leafs[cluster].numSurfaces; i++)
{
surfaceNum = clustersurfaces[leafs[cluster].firstSurface + i];
//
test = lsurfaceTest[ surfaceNum ];
if ( !test )
continue;
//
testculled = qfalse;
// use surface as an occluder
for (j = 0; j < test->numFacets; j++)
{
// use each facet as an occluder
facet = &test->facets[j];
//
// memcpy(winding.points, facet->points, sizeof(vec3_t) * facet->numpoints);
// winding.numpoints = facet->numpoints;
// DebugNet_DrawWinding(&winding, 5);
//
// if the facet was tested already
if ( volume->facetTested[facet->num >> 3] & (1 << (facet->num & 7)) )
continue;
volume->facetTested[facet->num >> 3] |= 1 << (facet->num & 7);
// backface culling for planar surfaces
backfaceculled = qfalse;
if (!test->patch && !test->trisoup)
{
if (volume->type == VOLUME_NORMAL)
{
// facet backface culling
d = DotProduct(light->origin, facet->plane.normal) - facet->plane.dist;
if (d < 0)
{
// NOTE: this doesn't work too great because of sometimes very bad tesselation
// of surfaces that are supposed to be flat
// FIXME: to work around this problem we should make sure that all facets
// created from planar surfaces use the lightmapVecs normal vector
/*
if ( !test->shader->twoSided )
{
// skip all other facets of this surface as well because they are in the same plane
for (k = 0; k < test->numFacets; k++)
{
facet = &test->facets[k];
volume->facetTested[facet->num >> 3] |= 1 << (facet->num & 7);
}
}*/
backfaceculled = qtrue;
}
}
else
{
// FIXME: if all light source winding points are at the back of the facet
// plane then backfaceculled = qtrue
}
}
else // backface culling per facet for patches and triangle soups
{
if (volume->type == VOLUME_NORMAL)
{
// facet backface culling
d = DotProduct(light->origin, facet->plane.normal) - facet->plane.dist;
if (d < 0)
backfaceculled = qtrue;
}
else
{
// FIXME: if all light source winding points are at the back of the facet
// plane then backfaceculled = qtrue
}
}
/* chopping does this already
// check if this facet is totally or partly in front of the volume end plane
for (k = 0; k < facet->numpoints; k++)
{
d = DotProduct(volume->endplane.normal, facet->points[k]) - volume->endplane.dist;
if (d > ON_EPSILON)
break;
}
// if this facet is outside the light volume
if (k >= facet->numpoints)
continue;
*/
//
if (backfaceculled)
{
// if the facet is not two sided
if ( !nobackfaceculling && !test->shader->twoSided )
continue;
// flip the winding
for (k = 0; k < facet->numpoints; k++)
VectorCopy(facet->points[k], winding.points[facet->numpoints - k - 1]);
winding.numpoints = facet->numpoints;
}
else
{
memcpy(winding.points, facet->points, sizeof(vec3_t) * facet->numpoints);
winding.numpoints = facet->numpoints;
}
//
if (!testculled)
{
testculled = qtrue;
// fast check if the surface sphere is totally behind the volume end plane
d = DotProduct(volume->endplane.normal, test->origin) - volume->endplane.dist;
if (d < -test->radius)
{
for (k = 0; k < test->numFacets; k++)
{
facet = &test->facets[k];
volume->facetTested[facet->num >> 3] |= 1 << (facet->num & 7);
}
break;
}
for (k = 0; k < volume->numplanes; k++)
{
d = DotProduct(volume->planes[k].normal, test->origin) - volume->planes[k].dist;
if (d < - test->radius)
{
for (k = 0; k < test->numFacets; k++)
{
facet = &test->facets[k];
volume->facetTested[facet->num >> 3] |= 1 << (facet->num & 7);
}
break;
}
}
if (k < volume->numplanes)
break;
}
//NOTE: we have to chop the facet winding with the volume end plane because
// the faces in Q3 are not stitched together nicely
res = VL_ChopWinding(&winding, &volume->endplane, 0.01);
// if the facet is on or at the back of the volume end plane
if (res == SIDE_BACK || res == SIDE_ON)
continue;
// check if the facet winding is totally or partly inside the light volume
memcpy(&tmpwinding, &winding, sizeof(winding_t));
for (k = 0; k < volume->numplanes; k++)
{
res = VL_ChopWinding(&tmpwinding, &volume->planes[k], 0.01);
if (res == SIDE_BACK || res == SIDE_ON)
break;
}
// if no part of the light volume is occluded by this facet
if (k < volume->numplanes)
continue;
//
for (k = 0; k < winding.numpoints; k++)
{
if (volume->type == VOLUME_DIRECTED)
{
VectorSubtract(winding.points[(k+1) % winding.numpoints], winding.points[k], dir1);
CrossProduct(light->normal, dir1, plane.normal);
VectorNormalize(plane.normal, plane.normal);
plane.dist = DotProduct(plane.normal, winding.points[k]);
}
else
{
VectorSubtract(winding.points[(k+1) % winding.numpoints], winding.points[k], dir1);
VectorSubtract(light->origin, winding.points[k], dir2);
CrossProduct(dir1, dir2, plane.normal);
VectorNormalize(plane.normal, plane.normal);
plane.dist = DotProduct(plane.normal, winding.points[k]);
}
res = VL_R_SplitLightVolume(light, volume, &plane, cluster, 0);
if (res == 1)
break; //the facet wasn't really inside the volume
}
if (k >= winding.numpoints)
{
volume->endplane = facet->plane;
if (backfaceculled)
{
VectorInverse(volume->endplane.normal);
volume->endplane.dist = -volume->endplane.dist;
}
volume->surfaceNum = surfaceNum;
volume->facetNum = j;
}
}
}
}
// we've tested all occluders in this cluster
volume->clusterTested[cluster >> 3] |= 1 << (cluster & 7);
// flood light through the portals of the current leaf
leaf = &leafs[cluster];
for (i = firstportal; i < leaf->numportals; i++)
{
p = leaf->portals[i];
//
// memcpy(&winding, p->winding, sizeof(winding_t));
// DebugNet_DrawWinding(&winding, 5);
// if already flooded into the cluster this portal leads to
if ( volume->clusterTested[p->leaf >> 3] & (1 << (p->leaf & 7)) )
continue;
//
if (volume->type == VOLUME_NORMAL)
{
// portal backface culling
d = DotProduct(light->origin, p->plane.normal) - p->plane.dist;
if (d > 0) // portal plane normal points into neighbour cluster
continue;
}
else
{
// FIXME: if all light source winding points are at the back of this portal
// plane then there's no need to flood through
}
// check if this portal is totally or partly in front of the volume end plane
// fast check with portal sphere
d = DotProduct(volume->endplane.normal, p->origin) - volume->endplane.dist;
if (d < -p->radius)
continue;
for (j = 0; j < p->winding->numpoints; j++)
{
d = DotProduct(volume->endplane.normal, p->winding->points[j]) - volume->endplane.dist;
if (d > -0.01)
break;
}
// if this portal is totally behind the light volume end plane
if (j >= p->winding->numpoints)
continue;
//distance from point light to portal
d = DotProduct(p->plane.normal, light->origin) - p->plane.dist;
// only check if a point light is Not *on* the portal
if (volume->type != VOLUME_NORMAL || fabs(d) > 0.1)
{
// check if the portal is partly or totally inside the light volume
memcpy(&winding, p->winding, sizeof(winding_t));
for (j = 0; j < volume->numplanes; j++)
{
res = VL_ChopWinding(&winding, &volume->planes[j], 0.01);
if (res == SIDE_BACK || res == SIDE_ON)
break;
}
// if the light volume does not go through this portal at all
if (j < volume->numplanes)
continue;
}
// chop the light volume with the portal
for (k = 0; k < p->winding->numpoints; k++)
{
if (volume->type == VOLUME_DIRECTED)
{
VectorSubtract(p->winding->points[(k+1) % p->winding->numpoints], p->winding->points[k], dir1);
CrossProduct(light->normal, dir1, plane.normal);
VectorNormalize(plane.normal, plane.normal);
plane.dist = DotProduct(plane.normal, p->winding->points[k]);
}
else
{
VectorSubtract(p->winding->points[(k+1) % p->winding->numpoints], p->winding->points[k], dir1);
VectorSubtract(light->origin, p->winding->points[k], dir2);
CrossProduct(dir1, dir2, plane.normal);
VectorNormalize(plane.normal, plane.normal);
plane.dist = DotProduct(plane.normal, p->winding->points[k]);
}
res = VL_R_SplitLightVolume(light, volume, &plane, cluster, i+1);
if (res == 1)
break; //volume didn't really go through the portal
}
// if the light volume went through the portal
if (k >= p->winding->numpoints)
{
// flood through the portal
VL_R_FloodLight(light, volume, p->leaf, 0);
}
}
}
/*
=============
VL_R_FloodAreaSpotLight
=============
*/
void VL_FloodAreaSpotLight(vlight_t *light, winding_t *w, int leafnum)
{
}
/*
=============
VL_R_SubdivideAreaSpotLight
=============
*/
void VL_R_SubdivideAreaSpotLight(vlight_t *light, int nodenum, winding_t *w)
{
int leafnum, res;
dnode_t *node;
dplane_t *plane;
winding_t back;
plane_t split;
while(nodenum >= 0)
{
node = &dnodes[nodenum];
plane = &dplanes[node->planeNum];
VectorCopy(plane->normal, split.normal);
split.dist = plane->dist;
res = VL_SplitWinding (w, &back, &split, 0.1);
if (res == SIDE_FRONT)
{
nodenum = node->children[0];
}
else if (res == SIDE_BACK)
{
nodenum = node->children[1];
}
else if (res == SIDE_ON)
{
memcpy(&back, w, sizeof(winding_t));
VL_R_SubdivideAreaSpotLight(light, node->children[1], &back);
nodenum = node->children[0];
}
else
{
VL_R_SubdivideAreaSpotLight(light, node->children[1], &back);
nodenum = node->children[0];
}
}
leafnum = -nodenum - 1;
if (dleafs[leafnum].cluster != -1)
{
VL_FloodAreaSpotLight(light, w, leafnum);
}
}
/*
=============
VL_R_FloodRadialAreaLight
=============
*/
void VL_FloodRadialAreaLight(vlight_t *light, winding_t *w, int leafnum)
{
}
/*
=============
VL_R_SubdivideRadialAreaLight
=============
*/
void VL_R_SubdivideRadialAreaLight(vlight_t *light, int nodenum, winding_t *w)
{
int leafnum, res;
dnode_t *node;
dplane_t *plane;
winding_t back;
plane_t split;
while(nodenum >= 0)
{
node = &dnodes[nodenum];
plane = &dplanes[node->planeNum];
VectorCopy(plane->normal, split.normal);
split.dist = plane->dist;
res = VL_SplitWinding (w, &back, &split, 0.1);
if (res == SIDE_FRONT)
{
nodenum = node->children[0];
}
else if (res == SIDE_BACK)
{
nodenum = node->children[1];
}
else if (res == SIDE_ON)
{
memcpy(&back, w, sizeof(winding_t));
VL_R_SubdivideRadialAreaLight(light, node->children[1], &back);
nodenum = node->children[0];
}
else
{
VL_R_SubdivideRadialAreaLight(light, node->children[1], &back);
nodenum = node->children[0];
}
}
leafnum = -nodenum - 1;
if (dleafs[leafnum].cluster != -1)
{
VL_FloodRadialAreaLight(light, w, leafnum);
}
}
/*
=============
VL_R_FloodDirectedLight
=============
*/
void VL_FloodDirectedLight(vlight_t *light, winding_t *w, int leafnum)
{
int i;
float dist;
lightvolume_t volume;
vec3_t dir;
if (light->atten_disttype == LDAT_NOSCALE)
{
// light travels without decrease in intensity over distance
dist = MAX_WORLD_COORD;
}
else
{
if ( light->atten_disttype == LDAT_LINEAR )
dist = light->photons * lightLinearScale;
else
dist = sqrt(light->photons);
}
memset(&volume, 0, sizeof(lightvolume_t));
for (i = 0; i < w->numpoints; i++)
{
VectorMA(w->points[i], dist, light->normal, volume.points[i]);
VectorSubtract(w->points[(i+1)%w->numpoints], w->points[i], dir);
CrossProduct(light->normal, dir, volume.planes[i].normal);
VectorNormalize(volume.planes[i].normal, volume.planes[i].normal);
volume.planes[i].dist = DotProduct(volume.planes[i].normal, w->points[i]);
}
volume.numplanes = w->numpoints;
VectorCopy(light->normal, volume.endplane.normal);
VectorInverse(volume.endplane.normal);
volume.endplane.dist = DotProduct(volume.endplane.normal, volume.points[0]);
volume.farplane = volume.endplane;
volume.surfaceNum = -1;
volume.type = VOLUME_DIRECTED;
volume.cluster = dleafs[leafnum].cluster;
VL_R_FloodLight(light, &volume, volume.cluster, 0);
if (volume.surfaceNum >= 0)
{
VL_R_CastLightAtSurface(light, &volume);
}
}
/*
=============
VL_R_SubdivideDirectedAreaLight
=============
*/
void VL_R_SubdivideDirectedAreaLight(vlight_t *light, int nodenum, winding_t *w)
{
int leafnum, res;
dnode_t *node;
dplane_t *plane;
winding_t back;
plane_t split;
while(nodenum >= 0)
{
node = &dnodes[nodenum];
plane = &dplanes[node->planeNum];
VectorCopy(plane->normal, split.normal);
split.dist = plane->dist;
res = VL_SplitWinding (w, &back, &split, 0.1);
if (res == SIDE_FRONT)
{
nodenum = node->children[0];
}
else if (res == SIDE_BACK)
{
nodenum = node->children[1];
}
else if (res == SIDE_ON)
{
memcpy(&back, w, sizeof(winding_t));
VL_R_SubdivideDirectedAreaLight(light, node->children[1], &back);
nodenum = node->children[0];
}
else
{
VL_R_SubdivideDirectedAreaLight(light, node->children[1], &back);
nodenum = node->children[0];
}
}
leafnum = -nodenum - 1;
if (dleafs[leafnum].cluster != -1)
{
VL_FloodDirectedLight(light, w, leafnum);
}
}
/*
=============
VL_FloodLight
=============
*/
void VL_FloodLight(vlight_t *light)
{
lightvolume_t volume;
dleaf_t *leaf;
int leafnum, i, j, k, dir[2][4] = {{1, 1, -1, -1}, {1, -1, -1, 1}};
float a, step, dist, radius, windingdist;
vec3_t vec, r, p, temp;
winding_t winding;
switch(light->type)
{
case LIGHT_POINTRADIAL:
{
// source is a point
// light radiates in all directions
// creates sharp shadows
//
// create 6 volumes shining in the axis directions
// what about: 4 tetrahedrons instead?
//
if ( light->atten_disttype == LDAT_LINEAR )
dist = light->photons * lightLinearScale;
else
dist = sqrt(light->photons);
//always put the winding at a large distance to avoid epsilon issues
windingdist = MAX_WORLD_COORD;
if (dist > windingdist)
windingdist = dist;
//
leafnum = VL_LightLeafnum(light->origin);
leaf = &dleafs[leafnum];
if (leaf->cluster == -1)
{
light->insolid = qtrue;
break;
}
// for each axis
for (i = 0; i < 3; i++)
{
// for both directions on the axis
for (j = -1; j <= 1; j += 2)
{
memset(&volume, 0, sizeof(lightvolume_t));
volume.numplanes = 0;
for (k = 0; k < 4; k ++)
{
volume.points[volume.numplanes][i] = light->origin[i] + j * windingdist;
volume.points[volume.numplanes][(i+1)%3] = light->origin[(i+1)%3] + dir[0][k] * windingdist;
volume.points[volume.numplanes][(i+2)%3] = light->origin[(i+2)%3] + dir[1][k] * windingdist;
volume.numplanes++;
}
if (j >= 0)
{
VectorCopy(volume.points[0], temp);
VectorCopy(volume.points[2], volume.points[0]);
VectorCopy(temp, volume.points[2]);
}
for (k = 0; k < volume.numplanes; k++)
{
VL_PlaneFromPoints(&volume.planes[k], light->origin, volume.points[(k+1)%volume.numplanes], volume.points[k]);
}
VectorCopy(light->origin, temp);
temp[i] += (float) j * dist;
VectorClear(volume.endplane.normal);
volume.endplane.normal[i] = -j;
volume.endplane.dist = DotProduct(volume.endplane.normal, temp); //DotProduct(volume.endplane.normal, volume.points[0]);
volume.farplane = volume.endplane;
volume.cluster = leaf->cluster;
volume.surfaceNum = -1;
volume.type = VOLUME_NORMAL;
//
memset(volume.facetTested, 0, sizeof(volume.facetTested));
memset(volume.clusterTested, 0, sizeof(volume.clusterTested));
VL_R_FloodLight(light, &volume, leaf->cluster, 0);
if (volume.surfaceNum >= 0)
{
VL_R_CastLightAtSurface(light, &volume);
}
}
}
break;
}
case LIGHT_POINTSPOT:
{
// source is a point
// light is targetted
// creates sharp shadows
//
// what about using brushes to shape spot lights? that'd be pretty cool
//
if ( light->atten_disttype == LDAT_LINEAR )
dist = light->photons * lightLinearScale;
else
dist = sqrt(light->photons);
dist *= 2;
//
windingdist = 4096;
if (dist > windingdist)
windingdist = dist;
//take 8 times the cone radius because the spotlight also lights outside the cone
radius = 8 * windingdist * light->radiusByDist;
//
memset(&volume, 0, sizeof(lightvolume_t));
leafnum = VL_LightLeafnum(light->origin);
leaf = &dleafs[leafnum];
if (leaf->cluster == -1)
{
light->insolid = qtrue;
break;
}
//
VectorClear(vec);
for (i = 0; i < 3; i++)
{
if (light->normal[i] > -0.9 && light->normal[i] < 0.9)
{
vec[i] = 1;
break;
}
}
CrossProduct(light->normal, vec, r);
VectorScale(r, radius, p);
volume.numplanes = 0;
step = 45;
for (a = step / 2; a < 360 + step / 2; a += step)
{
RotatePointAroundVector(volume.points[volume.numplanes], light->normal, p, a);
VectorAdd(light->origin, volume.points[volume.numplanes], volume.points[volume.numplanes]);
VectorMA(volume.points[volume.numplanes], windingdist, light->normal, volume.points[volume.numplanes]);
volume.numplanes++;
}
for (i = 0; i < volume.numplanes; i++)
{
VL_PlaneFromPoints(&volume.planes[i], light->origin, volume.points[(i+1)%volume.numplanes], volume.points[i]);
}
VectorMA(light->origin, dist, light->normal, temp);
VectorCopy(light->normal, volume.endplane.normal);
VectorInverse(volume.endplane.normal);
volume.endplane.dist = DotProduct(volume.endplane.normal, temp);//DotProduct(volume.endplane.normal, volume.points[0]);
volume.farplane = volume.endplane;
volume.cluster = leaf->cluster;
volume.surfaceNum = -1;
volume.type = VOLUME_NORMAL;
//
memset(volume.facetTested, 0, sizeof(volume.facetTested));
memset(volume.clusterTested, 0, sizeof(volume.clusterTested));
VL_R_FloodLight(light, &volume, leaf->cluster, 0);
if (volume.surfaceNum >= 0)
{
VL_R_CastLightAtSurface(light, &volume);
}
break;
}
case LIGHT_POINTFAKESURFACE:
{
float value;
int n, axis;
vec3_t v, vecs[2];
if ( light->atten_disttype == LDAT_LINEAR )
dist = light->photons * lightLinearScale;
else
dist = sqrt(light->photons);
//always put the winding at a large distance to avoid epsilon issues
windingdist = 4096;
if (dist > windingdist)
windingdist = dist;
//
VectorMA(light->origin, 0.1, light->normal, light->origin);
//
leafnum = VL_LightLeafnum(light->origin);
leaf = &dleafs[leafnum];
if (leaf->cluster == -1)
{
light->insolid = qtrue;
break;
}
value = 0;
for (i = 0; i < 3; i++)
{
if (fabs(light->normal[i]) > value)
{
value = fabs(light->normal[i]);
axis = i;
}
}
for (i = 0; i < 2; i++)
{
VectorClear(v);
v[(axis + 1 + i) % 3] = 1;
CrossProduct(light->normal, v, vecs[i]);
}
//cast 4 volumes at the front of the surface
for (i = -1; i <= 1; i += 2)
{
for (j = -1; j <= 1; j += 2)
{
for (n = 0; n < 2; n++)
{
memset(&volume, 0, sizeof(lightvolume_t));
volume.numplanes = 3;
VectorMA(light->origin, i * windingdist, vecs[0], volume.points[(i == j) == n]);
VectorMA(light->origin, j * windingdist, vecs[1], volume.points[(i != j) == n]);
VectorMA(light->origin, windingdist, light->normal, volume.points[2]);
for (k = 0; k < volume.numplanes; k++)
{
VL_PlaneFromPoints(&volume.planes[k], light->origin, volume.points[(k+1)%volume.numplanes], volume.points[k]);
}
VL_PlaneFromPoints(&volume.endplane, volume.points[0], volume.points[1], volume.points[2]);
VectorMA(light->origin, dist, light->normal, temp);
volume.endplane.dist = DotProduct(volume.endplane.normal, temp);
if (DotProduct(light->origin, volume.endplane.normal) - volume.endplane.dist > 0)
break;
}
volume.farplane = volume.endplane;
volume.cluster = leaf->cluster;
volume.surfaceNum = -1;
volume.type = VOLUME_NORMAL;
//
memset(volume.facetTested, 0, sizeof(volume.facetTested));
memset(volume.clusterTested, 0, sizeof(volume.clusterTested));
VL_R_FloodLight(light, &volume, leaf->cluster, 0);
if (volume.surfaceNum >= 0)
{
VL_R_CastLightAtSurface(light, &volume);
}
}
}
break;
}
case LIGHT_SURFACEDIRECTED:
{
// source is an area defined by a winding
// the light is unidirectional
// creates sharp shadows
// for instance sun light or laser light
//
memcpy(&winding, &light->w, sizeof(winding_t));
VL_R_SubdivideDirectedAreaLight(light, 0, &winding);
break;
}
case LIGHT_SURFACERADIAL:
{
// source is an area defined by a winding
// the light radiates in all directions at the front of the winding plane
//
memcpy(&winding, &light->w, sizeof(winding_t));
VL_R_SubdivideRadialAreaLight(light, 0, &winding);
break;
}
case LIGHT_SURFACESPOT:
{
// source is an area defined by a winding
// light is targetted but not unidirectional
//
memcpy(&winding, &light->w, sizeof(winding_t));
VL_R_SubdivideAreaSpotLight(light, 0, &winding);
break;
}
}
}
/*
=============
VL_FloodLightThread
=============
*/
void VL_FloodLightThread(int num)
{
VL_FloodLight(vlights[num]);
}
/*
=============
VL_TestLightLeafs
=============
*/
void VL_TestLightLeafs(void)
{
int leafnum, i;
vlight_t *light;
dleaf_t *leaf;
for (i = 0; i < numvlights; i++)
{
light = vlights[i];
if (light->type != LIGHT_POINTRADIAL &&
light->type != LIGHT_POINTSPOT)
continue;
leafnum = VL_LightLeafnum(light->origin);
leaf = &dleafs[leafnum];
if (leaf->cluster == -1)
if (light->type == LIGHT_POINTRADIAL)
qprintf("light in solid at %1.1f %1.1f %1.1f\n", light->origin[0], light->origin[1], light->origin[2]);
else if (light->type == LIGHT_POINTSPOT)
qprintf("spot light in solid at %1.1f %1.1f %1.1f\n", light->origin[0], light->origin[1], light->origin[2]);
}
}
/*
=============
VL_DoForcedTraceLight
=============
*/
// from light.c
void TraceLtm( int num );
void VL_DoForcedTraceLight(int num)
{
dsurface_t *ds;
shaderInfo_t *si;
ds = &drawSurfaces[num];
if ( ds->surfaceType == MST_TRIANGLE_SOUP )
return;
if ( ds->lightmapNum < 0 )
return;
// always light entity surfaces with the old light algorithm
if ( !entitySurface[num] )
{
si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
if (defaulttracelight)
{
if (si->forceVLight)
return;
}
else
{
if (!si->forceTraceLight)
return;
}
}
TraceLtm(num);
}
/*
=============
VL_DoForcedTraceLightSurfaces
=============
*/
void VL_DoForcedTraceLightSurfaces(void)
{
_printf( "forced trace light\n" );
RunThreadsOnIndividual( numDrawSurfaces, qtrue, VL_DoForcedTraceLight );
}
float *oldLightFloats;
/*
=============
VL_SurfaceRadiosity
=============
*/
void VL_SurfaceRadiosity( int num ) {
dsurface_t *ds;
mesh_t *mesh;
shaderInfo_t *si;
lsurfaceTest_t *test;
int x, y, k;
vec3_t base, normal;
float *color, area;
vlight_t vlight;
ds = &drawSurfaces[num];
if ( ds->lightmapNum < 0 ) {
return; // doesn't have a lightmap
}
// vertex-lit triangle model
if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
return;
}
si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
test = lsurfaceTest[ num ];
if (!test) {
return;
}
for (x = 0; x < ds->lightmapWidth; x++) {
for (y = 0; y < ds->lightmapHeight; y++) {
//
k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
* LIGHTMAP_WIDTH + ds->lightmapX + x;
area = lightmappixelarea[k];
if (area <= 0)
continue;
//
if (ds->surfaceType == MST_PATCH)
{
mesh = test->detailMesh;
VectorCopy( mesh->verts[y*mesh->width+x].xyz, base);
VectorCopy( mesh->verts[y*mesh->width+x].normal, normal);
}
else
{
VectorMA(ds->lightmapOrigin, (float) x, ds->lightmapVecs[0], base);
VectorMA(base, (float) y, ds->lightmapVecs[1], base);
VectorCopy(test->facets[0].plane.normal, normal);
}
// create ligth from base
memset(&vlight, 0, sizeof(vlight_t));
color = &oldLightFloats[k*3];
// a few units away from the surface
VectorMA(base, 5, normal, vlight.origin);
ColorNormalize(color, vlight.color);
// ok this is crap
vlight.photons = VectorLength(color) * 0.05 * lightPointScale / (area * radiosity_scale);
// what about using a front facing light only ?
vlight.type = LIGHT_POINTRADIAL;
// flood the light from this lightmap pixel
VL_FloodLight(&vlight);
// only one thread at a time may write to the lightmap of this surface
MutexLock(test->mutex);
// don't light the lightmap pixel itself
lightFloats[k*3] = oldLightFloats[k*3];
lightFloats[k*3+1] = oldLightFloats[k*3+1];
lightFloats[k*3+2] = oldLightFloats[k*3+2];
//
MutexUnlock(test->mutex);
}
}
}
/*
=============
VL_Radiosity
this aint working real well but it's fun to play with.
=============
*/
void VL_Radiosity(void) {
oldLightFloats = lightFloats;
lightFloats = (float *) malloc(numLightBytes * sizeof(float));
memcpy(lightFloats, oldLightFloats, numLightBytes * sizeof(float));
_printf("%7i surfaces\n", numDrawSurfaces);
RunThreadsOnIndividual( numDrawSurfaces, qtrue, VL_SurfaceRadiosity );
free(oldLightFloats);
}
/*
=============
VL_LightWorld
=============
*/
void VL_LightWorld(void)
{
int i, numcastedvolumes, numvlightsinsolid;
float f;
// find the optional world ambient
GetVectorForKey( &entities[0], "_color", lightAmbientColor );
f = FloatForKey( &entities[0], "ambient" );
VectorScale( lightAmbientColor, f, lightAmbientColor );
/*
_printf("\r%6d lights out of %d", 0, numvlights);
for (i = 0; i < numvlights; i++)
{
_printf("\r%6d", i);
VL_FloodLight(vlights[i]);
}
_printf("\r%6d lights out of %d\n", i, numvlights);
*/
_printf("%7i lights\n", numvlights);
RunThreadsOnIndividual( numvlights, qtrue, VL_FloodLightThread );
numcastedvolumes = 0;
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
if (lsurfaceTest[i])
numcastedvolumes += lsurfaceTest[i]->numvolumes;
}
_printf("%7i light volumes casted\n", numcastedvolumes);
numvlightsinsolid = 0;
for (i = 0; i < numvlights; i++)
{
if (vlights[i]->insolid)
numvlightsinsolid++;
}
_printf("%7i lights in solid\n", numvlightsinsolid);
//
radiosity_scale = 1;
for (i = 0; i < radiosity; i++) {
VL_Radiosity();
radiosity_scale <<= 1;
}
//
VL_StoreLightmap();
// redo surfaces with the old light algorithm when needed
VL_DoForcedTraceLightSurfaces();
}
/*
=============
VL_CreateEntityLights
=============
*/
entity_t *FindTargetEntity( const char *target );
void VL_CreateEntityLights (void)
{
int i, c_entityLights;
vlight_t *dl;
entity_t *e, *e2;
const char *name;
const char *target;
vec3_t dest;
const char *_color;
float intensity;
int spawnflags;
//
c_entityLights = 0;
_printf("Creating entity lights...\n");
//
for ( i = 0 ; i < num_entities ; i++ ) {
e = &entities[i];
name = ValueForKey (e, "classname");
if (strncmp (name, "light", 5))
continue;
dl = malloc(sizeof(*dl));
memset (dl, 0, sizeof(*dl));
spawnflags = FloatForKey (e, "spawnflags");
if ( spawnflags & 1 ) {
dl->atten_disttype = LDAT_LINEAR;
}
if ( spawnflags & 2 ) {
dl->atten_disttype = LDAT_NOSCALE;
}
if ( spawnflags & 4 ) {
dl->atten_angletype = LAAT_QUADRATIC;
}
if ( spawnflags & 8 ) {
dl->atten_angletype = LAAT_DOUBLEQUADRATIC;
}
dl->atten_distscale = FloatForKey(e, "atten_distscale");
dl->atten_anglescale = FloatForKey(e, "atten_anglescale");
GetVectorForKey (e, "origin", dl->origin);
dl->style = FloatForKey (e, "_style");
if (!dl->style)
dl->style = FloatForKey (e, "style");
if (dl->style < 0)
dl->style = 0;
intensity = FloatForKey (e, "light");
if (!intensity)
intensity = FloatForKey (e, "_light");
if (!intensity)
intensity = 300;
_color = ValueForKey (e, "_color");
if (_color && _color[0])
{
sscanf (_color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2]);
ColorNormalize (dl->color, dl->color);
}
else
dl->color[0] = dl->color[1] = dl->color[2] = 1.0;
intensity = intensity * lightPointScale;
dl->photons = intensity;
dl->type = LIGHT_POINTRADIAL;
// lights with a target will be spotlights
target = ValueForKey (e, "target");
if ( target[0] ) {
float radius;
float dist;
e2 = FindTargetEntity (target);
if (!e2) {
_printf ("WARNING: light at (%i %i %i) has missing target\n",
(int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]);
} else {
GetVectorForKey (e2, "origin", dest);
VectorSubtract (dest, dl->origin, dl->normal);
dist = VectorNormalize (dl->normal, dl->normal);
radius = FloatForKey (e, "radius");
if ( !radius ) {
radius = 64;
}
if ( !dist ) {
dist = 64;
}
dl->radiusByDist = (radius + 16) / dist;
dl->type = LIGHT_POINTSPOT;
}
}
vlights[numvlights++] = dl;
c_entityLights++;
}
_printf("%7i entity lights\n", c_entityLights);
}
/*
==================
VL_SubdivideAreaLight
==================
*/
void VL_SubdivideAreaLight( shaderInfo_t *ls, winding_t *w, vec3_t normal,
float areaSubdivide, qboolean backsplash ) {
float area, value, intensity;
vlight_t *dl, *dl2;
vec3_t mins, maxs;
int axis;
winding_t *front, *back;
vec3_t planeNormal;
float planeDist;
if ( !w ) {
return;
}
WindingBounds( w, mins, maxs );
// check for subdivision
for ( axis = 0 ; axis < 3 ; axis++ ) {
if ( maxs[axis] - mins[axis] > areaSubdivide ) {
VectorClear( planeNormal );
planeNormal[axis] = 1;
planeDist = ( maxs[axis] + mins[axis] ) * 0.5;
ClipWindingEpsilon ( w, planeNormal, planeDist, ON_EPSILON, &front, &back );
VL_SubdivideAreaLight( ls, front, normal, areaSubdivide, qfalse );
VL_SubdivideAreaLight( ls, back, normal, areaSubdivide, qfalse );
FreeWinding( w );
return;
}
}
// create a light from this
area = WindingArea (w);
if ( area <= 0 || area > 20000000 ) {
return;
}
dl = malloc(sizeof(*dl));
memset (dl, 0, sizeof(*dl));
dl->type = LIGHT_POINTFAKESURFACE;
WindingCenter( w, dl->origin );
memcpy(dl->w.points, w->points, sizeof(vec3_t) * w->numpoints);
dl->w.numpoints = w->numpoints;
VectorCopy ( normal, dl->normal);
VectorCopy ( normal, dl->plane);
dl->plane[3] = DotProduct( dl->origin, normal );
value = ls->value;
intensity = value * area * lightAreaScale;
VectorAdd( dl->origin, dl->normal, dl->origin );
VectorCopy( ls->color, dl->color );
dl->photons = intensity;
// emitColor is irrespective of the area
VectorScale( ls->color, value*lightFormFactorValueScale*lightAreaScale, dl->emitColor );
//
VectorCopy(dl->emitColor, dl->color);
dl->si = ls;
if ( ls->contents & CONTENTS_FOG ) {
dl->twosided = qtrue;
}
vlights[numvlights++] = dl;
// optionally create a point backsplash light
if ( backsplash && ls->backsplashFraction > 0 ) {
dl2 = malloc(sizeof(*dl));
memset (dl2, 0, sizeof(*dl2));
dl2->type = LIGHT_POINTRADIAL;
VectorMA( dl->origin, ls->backsplashDistance, normal, dl2->origin );
VectorCopy( ls->color, dl2->color );
dl2->photons = dl->photons * ls->backsplashFraction;
dl2->si = ls;
vlights[numvlights++] = dl2;
}
}
/*
==================
VL_CreateFakeSurfaceLights
==================
*/
void VL_CreateFakeSurfaceLights( void ) {
int i, j, side;
dsurface_t *ds;
shaderInfo_t *ls;
winding_t *w;
lFacet_t *f;
vlight_t *dl;
vec3_t origin;
drawVert_t *dv;
int c_surfaceLights;
float lightSubdivide;
vec3_t normal;
c_surfaceLights = 0;
_printf ("Creating surface lights...\n");
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
// see if this surface is light emiting
ds = &drawSurfaces[i];
ls = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
if ( ls->value == 0 ) {
continue;
}
// determine how much we need to chop up the surface
if ( ls->lightSubdivide ) {
lightSubdivide = ls->lightSubdivide;
} else {
lightSubdivide = lightDefaultSubdivide;
}
c_surfaceLights++;
// an autosprite shader will become
// a point light instead of an area light
if ( ls->autosprite ) {
// autosprite geometry should only have four vertexes
if ( lsurfaceTest[i] ) {
// curve or misc_model
f = lsurfaceTest[i]->facets;
if ( lsurfaceTest[i]->numFacets != 1 || f->numpoints != 4 ) {
_printf( "WARNING: surface at (%i %i %i) has autosprite shader but isn't a quad\n",
(int)f->points[0], (int)f->points[1], (int)f->points[2] );
}
VectorAdd( f->points[0], f->points[1], origin );
VectorAdd( f->points[2], origin, origin );
VectorAdd( f->points[3], origin, origin );
VectorScale( origin, 0.25, origin );
} else {
// normal polygon
dv = &drawVerts[ ds->firstVert ];
if ( ds->numVerts != 4 ) {
_printf( "WARNING: surface at (%i %i %i) has autosprite shader but %i verts\n",
(int)dv->xyz[0], (int)dv->xyz[1], (int)dv->xyz[2] );
continue;
}
VectorAdd( dv[0].xyz, dv[1].xyz, origin );
VectorAdd( dv[2].xyz, origin, origin );
VectorAdd( dv[3].xyz, origin, origin );
VectorScale( origin, 0.25, origin );
}
dl = malloc(sizeof(*dl));
memset (dl, 0, sizeof(*dl));
VectorCopy( origin, dl->origin );
VectorCopy( ls->color, dl->color );
dl->photons = ls->value * lightPointScale;
dl->type = LIGHT_POINTRADIAL;
vlights[numvlights++] = dl;
continue;
}
// possibly create for both sides of the polygon
for ( side = 0 ; side <= ls->twoSided ; side++ ) {
// create area lights
if ( lsurfaceTest[i] ) {
// curve or misc_model
for ( j = 0 ; j < lsurfaceTest[i]->numFacets ; j++ ) {
f = lsurfaceTest[i]->facets + j;
w = AllocWinding( f->numpoints );
w->numpoints = f->numpoints;
memcpy( w->points, f->points, f->numpoints * 12 );
VectorCopy( f->plane.normal, normal );
if ( side ) {
winding_t *t;
t = w;
w = ReverseWinding( t );
FreeWinding( t );
VectorSubtract( vec3_origin, normal, normal );
}
VL_SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
}
} else {
// normal polygon
w = AllocWinding( ds->numVerts );
w->numpoints = ds->numVerts;
for ( j = 0 ; j < ds->numVerts ; j++ ) {
VectorCopy( drawVerts[ds->firstVert+j].xyz, w->points[j] );
}
VectorCopy( ds->lightmapVecs[2], normal );
if ( side ) {
winding_t *t;
t = w;
w = ReverseWinding( t );
FreeWinding( t );
VectorSubtract( vec3_origin, normal, normal );
}
VL_SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
}
}
}
_printf( "%7i light emitting surfaces\n", c_surfaceLights );
}
/*
==================
VL_WindingForBrushSide
==================
*/
winding_t *VL_WindingForBrushSide(dbrush_t *brush, int side, winding_t *w)
{
int i, res;
winding_t *tmpw;
plane_t plane;
VectorCopy(dplanes[ dbrushsides[ brush->firstSide + side ].planeNum ].normal, plane.normal);
VectorInverse(plane.normal);
plane.dist = -dplanes[ dbrushsides[ brush->firstSide + side ].planeNum ].dist;
tmpw = BaseWindingForPlane( plane.normal, plane.dist );
memcpy(w->points, tmpw->points, sizeof(vec3_t) * tmpw->numpoints);
w->numpoints = tmpw->numpoints;
for (i = 0; i < brush->numSides; i++)
{
if (i == side)
continue;
VectorCopy(dplanes[ dbrushsides[ brush->firstSide + i ].planeNum ].normal, plane.normal);
VectorInverse(plane.normal);
plane.dist = -dplanes[ dbrushsides[ brush->firstSide + i ].planeNum ].dist;
res = VL_ChopWinding(w, &plane, 0.1);
if (res == SIDE_BACK)
return NULL;
}
return w;
}
/*
==================
VL_CreateSkyLights
==================
*/
void VL_CreateSkyLights(void)
{
int i, j, c_skyLights;
dbrush_t *b;
shaderInfo_t *si;
dbrushside_t *s;
vlight_t *dl;
vec3_t sunColor, sunDir = { 0.45, 0.3, 0.9 };
float d;
VectorNormalize(sunDir, sunDir);
VectorInverse(sunDir);
c_skyLights = 0;
_printf("Creating sky lights...\n");
// find the sky shader
for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
si = ShaderInfoForShader( dshaders[ drawSurfaces[i].shaderNum ].shader );
if ( si->surfaceFlags & SURF_SKY ) {
VectorCopy( si->sunLight, sunColor );
VectorCopy( si->sunDirection, sunDir );
VectorInverse(sunDir);
break;
}
}
// find the brushes
for ( i = 0 ; i < numbrushes ; i++ ) {
b = &dbrushes[i];
for ( j = 0 ; j < b->numSides ; j++ ) {
s = &dbrushsides[ b->firstSide + j ];
if ( dshaders[ s->shaderNum ].surfaceFlags & SURF_SKY ) {
//if this surface doesn't face in the same direction as the sun
d = DotProduct(dplanes[ s->planeNum ].normal, sunDir);
if (d <= 0)
continue;
//
dl = malloc(sizeof(*dl));
memset (dl, 0, sizeof(*dl));
VectorCopy(sunColor, dl->color);
VectorCopy(sunDir, dl->normal);
VectorCopy(dplanes[ s->planeNum ].normal, dl->plane);
dl->plane[3] = dplanes[ s->planeNum ].dist;
dl->type = LIGHT_SURFACEDIRECTED;
dl->atten_disttype = LDAT_NOSCALE;
VL_WindingForBrushSide(b, j, &dl->w);
// DebugNet_DrawWinding(&dl->w, 2);
//
vlights[numvlights++] = dl;
c_skyLights++;
}
}
}
_printf("%7i light emitting sky surfaces\n", c_skyLights);
}
/*
==================
VL_SetPortalSphere
==================
*/
void VL_SetPortalSphere (lportal_t *p)
{
int i;
vec3_t total, dist;
winding_t *w;
float r, bestr;
w = p->winding;
VectorCopy (vec3_origin, total);
for (i=0 ; i<w->numpoints ; i++)
{
VectorAdd (total, w->points[i], total);
}
for (i=0 ; i<3 ; i++)
total[i] /= w->numpoints;
bestr = 0;
for (i=0 ; i<w->numpoints ; i++)
{
VectorSubtract (w->points[i], total, dist);
r = VectorLength (dist);
if (r > bestr)
bestr = r;
}
VectorCopy (total, p->origin);
p->radius = bestr;
}
/*
==================
VL_PlaneFromWinding
==================
*/
void VL_PlaneFromWinding (winding_t *w, plane_t *plane)
{
vec3_t v1, v2;
//calc plane
VectorSubtract (w->points[2], w->points[1], v1);
VectorSubtract (w->points[0], w->points[1], v2);
CrossProduct (v2, v1, plane->normal);
VectorNormalize (plane->normal, plane->normal);
plane->dist = DotProduct (w->points[0], plane->normal);
}
/*
==================
VL_AllocWinding
==================
*/
winding_t *VL_AllocWinding (int points)
{
winding_t *w;
int size;
if (points > MAX_POINTS_ON_WINDING)
Error ("NewWinding: %i points", points);
size = (int)((winding_t *)0)->points[points];
w = malloc (size);
memset (w, 0, size);
return w;
}
/*
============
VL_LoadPortals
============
*/
void VL_LoadPortals (char *name)
{
int i, j, hint;
lportal_t *p;
lleaf_t *l;
char magic[80];
FILE *f;
int numpoints;
winding_t *w;
int leafnums[2];
plane_t plane;
//
if (!strcmp(name,"-"))
f = stdin;
else
{
f = fopen(name, "r");
if (!f)
Error ("LoadPortals: couldn't read %s\n",name);
}
if (fscanf (f,"%79s\n%i\n%i\n%i\n",magic, &portalclusters, &numportals, &numfaces) != 4)
Error ("LoadPortals: failed to read header");
if (strcmp(magic, PORTALFILE))
Error ("LoadPortals: not a portal file");
_printf ("%6i portalclusters\n", portalclusters);
_printf ("%6i numportals\n", numportals);
_printf ("%6i numfaces\n", numfaces);
if (portalclusters >= MAX_CLUSTERS)
Error ("more than %d clusters in portal file\n", MAX_CLUSTERS);
// each file portal is split into two memory portals
portals = malloc(2*numportals*sizeof(lportal_t));
memset (portals, 0, 2*numportals*sizeof(lportal_t));
leafs = malloc(portalclusters*sizeof(lleaf_t));
memset (leafs, 0, portalclusters*sizeof(lleaf_t));
for (i=0, p=portals ; i<numportals ; i++)
{
if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3)
Error ("LoadPortals: reading portal %i", i);
if (numpoints > MAX_POINTS_ON_WINDING)
Error ("LoadPortals: portal %i has too many points", i);
if ( (unsigned)leafnums[0] > portalclusters
|| (unsigned)leafnums[1] > portalclusters)
Error ("LoadPortals: reading portal %i", i);
if (fscanf (f, "%i ", &hint) != 1)
Error ("LoadPortals: reading hint state");
w = p->winding = VL_AllocWinding (numpoints);
w->numpoints = numpoints;
for (j=0 ; j<numpoints ; j++)
{
double v[3];
int k;
// scanf into double, then assign to vec_t
// so we don't care what size vec_t is
if (fscanf (f, "(%lf %lf %lf ) "
, &v[0], &v[1], &v[2]) != 3)
Error ("LoadPortals: reading portal %i", i);
for (k=0 ; k<3 ; k++)
w->points[j][k] = v[k];
}
fscanf (f, "\n");
// calc plane
VL_PlaneFromWinding (w, &plane);
// create forward portal
l = &leafs[leafnums[0]];
if (l->numportals == MAX_PORTALS_ON_LEAF)
Error ("Leaf with too many portals");
l->portals[l->numportals] = p;
l->numportals++;
p->winding = w;
VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
p->plane.dist = -plane.dist;
p->leaf = leafnums[1];
VL_SetPortalSphere (p);
p++;
// create backwards portal
l = &leafs[leafnums[1]];
if (l->numportals == MAX_PORTALS_ON_LEAF)
Error ("Leaf with too many portals");
l->portals[l->numportals] = p;
l->numportals++;
p->winding = VL_AllocWinding(w->numpoints);
p->winding->numpoints = w->numpoints;
for (j=0 ; j<w->numpoints ; j++)
{
VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
}
p->plane = plane;
p->leaf = leafnums[0];
VL_SetPortalSphere (p);
p++;
}
fclose (f);
}
/*
============
VLightMain
============
*/
int VLightMain (int argc, char **argv) {
int i;
double start, end;
const char *value;
_printf ("----- VLighting ----\n");
for (i=1 ; i<argc ; i++) {
if (!strcmp(argv[i],"-v")) {
verbose = qtrue;
} else if (!strcmp(argv[i],"-threads")) {
numthreads = atoi (argv[i+1]);
_printf("num threads = %d\n", numthreads);
i++;
} else if (!strcmp(argv[i],"-area")) {
lightAreaScale *= atof(argv[i+1]);
_printf ("area light scaling at %f\n", lightAreaScale);
i++;
} else if (!strcmp(argv[i],"-point")) {
lightPointScale *= atof(argv[i+1]);
_printf ("point light scaling at %f\n", lightPointScale);
i++;
} else if (!strcmp(argv[i], "-samplesize")) {
samplesize = atoi(argv[i+1]);
if (samplesize < 1) samplesize = 1;
i++;
_printf("lightmap sample size is %dx%d units\n", samplesize, samplesize);
} else if (!strcmp(argv[i], "-novertex")) {
novertexlighting = qtrue;
_printf("no vertex lighting = true\n");
} else if (!strcmp(argv[i], "-nogrid")) {
nogridlighting = qtrue;
_printf("no grid lighting = true\n");
} else if (!strcmp(argv[i], "-nostitching")) {
nostitching = qtrue;
_printf("no stitching = true\n");
} else if (!strcmp(argv[i], "-noalphashading")) {
noalphashading = qtrue;
_printf("no alpha shading = true\n");
} else if (!strcmp(argv[i], "-nocolorshading")) {
nocolorshading = qtrue;
_printf("old style alpha shading = true\n");
} else if (!strcmp(argv[i], "-nobackfaceculling")) {
nobackfaceculling = qtrue;
_printf("no backface culling = true\n");
} else if (!strcmp(argv[i], "-tracelight")) {
defaulttracelight = qtrue;
_printf("default trace light = true\n");
} else if (!strcmp(argv[i], "-radiosity")) {
radiosity = atoi(argv[i+1]);
_printf("radiosity = %d\n", radiosity);
i++;
} else {
break;
}
}
ThreadSetDefault ();
if (i != argc - 1) {
_printf("usage: q3map -vlight [-<switch> [-<switch> ...]] <mapname>\n"
"\n"
"Switches:\n"
" v = verbose output\n"
" threads <X> = set number of threads to X\n"
" area <V> = set the area light scale to V\n"
" point <W> = set the point light scale to W\n"
" novertex = don't calculate vertex lighting\n"
" nogrid = don't calculate light grid for dynamic model lighting\n"
" nostitching = no polygon stitching before lighting\n"
" noalphashading = don't use alpha shading\n"
" nocolorshading = don't use color alpha shading\n"
" tracelight = use old light algorithm by default\n"
" samplesize <N> = set the lightmap pixel size to NxN units\n");
exit(0);
}
SetQdirFromPath (argv[i]);
#ifdef _WIN32
InitPakFile(gamedir, NULL);
#endif
strcpy (source, ExpandArg(argv[i]));
StripExtension (source);
DefaultExtension (source, ".bsp");
LoadShaderInfo();
_printf ("reading %s\n", source);
LoadBSPFile (source);
ParseEntities();
value = ValueForKey( &entities[0], "gridsize" );
if (strlen(value)) {
sscanf( value, "%f %f %f", &gridSize[0], &gridSize[1], &gridSize[2] );
_printf("grid size = {%1.1f, %1.1f, %1.1f}\n", gridSize[0], gridSize[1], gridSize[2]);
}
CountLightmaps();
StripExtension (source);
DefaultExtension (source, ".prt");
VL_LoadPortals(source);
// set surfaceOrigin
SetEntityOrigins();
// grid and vertex lighting
GridAndVertexLighting();
#ifdef DEBUGNET
DebugNet_Setup();
#endif
start = clock();
lightFloats = (float *) malloc(numLightBytes * sizeof(float));
memset(lightFloats, 0, numLightBytes * sizeof(float));
VL_InitSurfacesForTesting();
VL_CalcVisibleLightmapPixelArea();
numvlights = 0;
VL_CreateEntityLights();
VL_CreateFakeSurfaceLights();
VL_CreateSkyLights();
VL_TestLightLeafs();
VL_LightWorld();
#ifndef LIGHTPOLYS
StripExtension (source);
DefaultExtension (source, ".bsp");
_printf ("writing %s\n", source);
WriteBSPFile (source);
#endif
end = clock();
_printf ("%5.2f seconds elapsed\n", (end-start) / CLK_TCK);
#ifdef LIGHTPOLYS
VL_DrawLightWindings();
#endif
#ifdef DEBUGNET
DebugNet_Shutdown();
#endif
return 0;
}