aboutsummaryrefslogtreecommitdiffstats
path: root/q3map/lightv.c
diff options
context:
space:
mode:
Diffstat (limited to 'q3map/lightv.c')
-rwxr-xr-xq3map/lightv.c5748
1 files changed, 5748 insertions, 0 deletions
diff --git a/q3map/lightv.c b/q3map/lightv.c
new file mode 100755
index 0000000..dddecb7
--- /dev/null
+++ b/q3map/lightv.c
@@ -0,0 +1,5748 @@
+/*
+===========================================================================
+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;
+}