aboutsummaryrefslogtreecommitdiffstats
path: root/q3map
diff options
context:
space:
mode:
Diffstat (limited to 'q3map')
-rwxr-xr-xq3map/brush.c861
-rwxr-xr-xq3map/brush_primit.c52
-rwxr-xr-xq3map/bsp.c605
-rwxr-xr-xq3map/facebsp.c379
-rwxr-xr-xq3map/fog.c554
-rwxr-xr-xq3map/gldraw.c232
-rwxr-xr-xq3map/glfile.c148
-rwxr-xr-xq3map/leakfile.c100
-rwxr-xr-xq3map/light.c2149
-rwxr-xr-xq3map/light.h151
-rwxr-xr-xq3map/light_trace.c944
-rwxr-xr-xq3map/lightmaps.c395
-rwxr-xr-xq3map/lightv.c5748
-rwxr-xr-xq3map/makefile148
-rwxr-xr-xq3map/map.c1251
-rwxr-xr-xq3map/mesh.c682
-rwxr-xr-xq3map/mesh.h48
-rwxr-xr-xq3map/misc_model.c472
-rwxr-xr-xq3map/nodraw.c47
-rwxr-xr-xq3map/patch.c286
-rwxr-xr-xq3map/portals.c843
-rwxr-xr-xq3map/prtfile.c272
-rwxr-xr-xq3map/q3map.sln56
-rwxr-xr-xq3map/q3map.vcproj1606
-rwxr-xr-xq3map/qbsp.h455
-rwxr-xr-xq3map/shaders.c608
-rwxr-xr-xq3map/shaders.h71
-rwxr-xr-xq3map/soundv.c5742
-rwxr-xr-xq3map/surface.c1158
-rwxr-xr-xq3map/terrain.c1255
-rwxr-xr-xq3map/tjunction.c551
-rwxr-xr-xq3map/tree.c146
-rwxr-xr-xq3map/vis.c1197
-rwxr-xr-xq3map/vis.h162
-rwxr-xr-xq3map/visflow.c1657
-rwxr-xr-xq3map/writebsp.c418
36 files changed, 31449 insertions, 0 deletions
diff --git a/q3map/brush.c b/q3map/brush.c
new file mode 100755
index 0000000..e373e17
--- /dev/null
+++ b/q3map/brush.c
@@ -0,0 +1,861 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+int c_active_brushes;
+
+int c_nodes;
+
+// if a brush just barely pokes onto the other side,
+// let it slide by without chopping
+#define PLANESIDE_EPSILON 0.001
+//0.1
+
+
+
+
+/*
+================
+CountBrushList
+================
+*/
+int CountBrushList (bspbrush_t *brushes)
+{
+ int c;
+
+ c = 0;
+ for ( ; brushes ; brushes = brushes->next)
+ c++;
+ return c;
+}
+
+
+/*
+================
+AllocBrush
+================
+*/
+bspbrush_t *AllocBrush (int numsides)
+{
+ bspbrush_t *bb;
+ int c;
+
+ c = (int)&(((bspbrush_t *)0)->sides[numsides]);
+ bb = malloc(c);
+ memset (bb, 0, c);
+ if (numthreads == 1)
+ c_active_brushes++;
+ return bb;
+}
+
+/*
+================
+FreeBrush
+================
+*/
+void FreeBrush (bspbrush_t *brushes)
+{
+ int i;
+
+ for (i=0 ; i<brushes->numsides ; i++)
+ if (brushes->sides[i].winding)
+ FreeWinding(brushes->sides[i].winding);
+ free (brushes);
+ if (numthreads == 1)
+ c_active_brushes--;
+}
+
+
+/*
+================
+FreeBrushList
+================
+*/
+void FreeBrushList (bspbrush_t *brushes)
+{
+ bspbrush_t *next;
+
+ for ( ; brushes ; brushes = next)
+ {
+ next = brushes->next;
+
+ FreeBrush (brushes);
+ }
+}
+
+/*
+==================
+CopyBrush
+
+Duplicates the brush, the sides, and the windings
+==================
+*/
+bspbrush_t *CopyBrush (bspbrush_t *brush)
+{
+ bspbrush_t *newbrush;
+ int size;
+ int i;
+
+ size = (int)&(((bspbrush_t *)0)->sides[brush->numsides]);
+
+ newbrush = AllocBrush (brush->numsides);
+ memcpy (newbrush, brush, size);
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ if (brush->sides[i].winding)
+ newbrush->sides[i].winding = CopyWinding (brush->sides[i].winding);
+ }
+
+ return newbrush;
+}
+
+
+/*
+================
+DrawBrushList
+================
+*/
+void DrawBrushList (bspbrush_t *brush)
+{
+ int i;
+ side_t *s;
+
+ GLS_BeginScene ();
+ for ( ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ if (!s->winding)
+ continue;
+ GLS_Winding (s->winding, 0);
+ }
+ }
+ GLS_EndScene ();
+}
+
+
+
+/*
+================
+WriteBrushList
+================
+*/
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis)
+{
+ int i;
+ side_t *s;
+ FILE *f;
+
+ qprintf ("writing %s\n", name);
+ f = SafeOpenWrite (name);
+
+ for ( ; brush ; brush=brush->next)
+ {
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ if (!s->winding)
+ continue;
+ if (onlyvis && !s->visible)
+ continue;
+ OutputWinding (brush->sides[i].winding, f);
+ }
+ }
+
+ fclose (f);
+}
+
+
+/*
+=============
+PrintBrush
+=============
+*/
+void PrintBrush (bspbrush_t *brush)
+{
+ int i;
+
+ _printf ("brush: %p\n", brush);
+ for (i=0;i<brush->numsides ; i++)
+ {
+ pw(brush->sides[i].winding);
+ _printf ("\n");
+ }
+}
+
+/*
+==================
+BoundBrush
+
+Sets the mins/maxs based on the windings
+returns false if the brush doesn't enclose a valid volume
+==================
+*/
+qboolean BoundBrush (bspbrush_t *brush)
+{
+ int i, j;
+ winding_t *w;
+
+ ClearBounds (brush->mins, brush->maxs);
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ AddPointToBounds (w->p[j], brush->mins, brush->maxs);
+ }
+
+ for (i=0 ; i<3 ; i++) {
+ if (brush->mins[i] < MIN_WORLD_COORD || brush->maxs[i] > MAX_WORLD_COORD
+ || brush->mins[i] >= brush->maxs[i] ) {
+ return qfalse;
+ }
+ }
+
+ return qtrue;
+}
+
+/*
+==================
+CreateBrushWindings
+
+makes basewindigs for sides and mins / maxs for the brush
+returns false if the brush doesn't enclose a valid volume
+==================
+*/
+qboolean CreateBrushWindings (bspbrush_t *brush)
+{
+ int i, j;
+ winding_t *w;
+ side_t *side;
+ plane_t *plane;
+
+ for ( i = 0; i < brush->numsides; i++ )
+ {
+ side = &brush->sides[i];
+ // don't create a winding for a bevel
+ if ( side->bevel ) {
+ continue;
+ }
+ plane = &mapplanes[side->planenum];
+ w = BaseWindingForPlane (plane->normal, plane->dist);
+ for ( j = 0; j < brush->numsides && w; j++ )
+ {
+ if (i == j)
+ continue;
+ if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) )
+ continue; // back side clipaway
+ if (brush->sides[j].bevel)
+ continue;
+ if (brush->sides[j].backSide)
+ continue;
+ plane = &mapplanes[brush->sides[j].planenum^1];
+ ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
+ }
+ // free any existing winding
+ if ( side->winding ) {
+ FreeWinding( side->winding );
+ }
+ side->winding = w;
+ }
+
+ return BoundBrush (brush);
+}
+
+/*
+==================
+BrushFromBounds
+
+Creates a new axial brush
+==================
+*/
+bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs)
+{
+ bspbrush_t *b;
+ int i;
+ vec3_t normal;
+ vec_t dist;
+
+ b = AllocBrush (6);
+ b->numsides = 6;
+ for (i=0 ; i<3 ; i++)
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ dist = maxs[i];
+ b->sides[i].planenum = FindFloatPlane (normal, dist);
+
+ normal[i] = -1;
+ dist = -mins[i];
+ b->sides[3+i].planenum = FindFloatPlane (normal, dist);
+ }
+
+ CreateBrushWindings (b);
+
+ return b;
+}
+
+/*
+==================
+BrushVolume
+
+==================
+*/
+vec_t BrushVolume (bspbrush_t *brush)
+{
+ int i;
+ winding_t *w;
+ vec3_t corner;
+ vec_t d, area, volume;
+ plane_t *plane;
+
+ if (!brush)
+ return 0;
+
+ // grab the first valid point as the corner
+
+ w = NULL;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (w)
+ break;
+ }
+ if (!w)
+ return 0;
+ VectorCopy (w->p[0], corner);
+
+ // make tetrahedrons to all other faces
+
+ volume = 0;
+ for ( ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ plane = &mapplanes[brush->sides[i].planenum];
+ d = -(DotProduct (corner, plane->normal) - plane->dist);
+ area = WindingArea (w);
+ volume += d*area;
+ }
+
+ volume /= 3;
+ return volume;
+}
+
+
+/*
+==================
+WriteBspBrushMap
+==================
+*/
+void WriteBspBrushMap (char *name, bspbrush_t *list)
+{
+ FILE *f;
+ side_t *s;
+ int i;
+ winding_t *w;
+
+ _printf ("writing %s\n", name);
+ f = fopen (name, "wb");
+ if (!f)
+ Error ("Can't write %s\b", name);
+
+ fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
+
+ for ( ; list ; list=list->next )
+ {
+ fprintf (f, "{\n");
+ for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
+ {
+ w = BaseWindingForPlane (mapplanes[s->planenum].normal, mapplanes[s->planenum].dist);
+
+ fprintf (f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2]);
+ fprintf (f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2]);
+
+ fprintf (f, "notexture 0 0 0 1 1\n" );
+ FreeWinding (w);
+ }
+ fprintf (f, "}\n");
+ }
+ fprintf (f, "}\n");
+
+ fclose (f);
+
+}
+
+
+//=====================================================================================
+
+/*
+====================
+FilterBrushIntoTree_r
+
+====================
+*/
+int FilterBrushIntoTree_r( bspbrush_t *b, node_t *node ) {
+ bspbrush_t *front, *back;
+ int c;
+
+ if ( !b ) {
+ return 0;
+ }
+
+ // add it to the leaf list
+ if ( node->planenum == PLANENUM_LEAF ) {
+ b->next = node->brushlist;
+ node->brushlist = b;
+
+ // classify the leaf by the structural brush
+ if ( !b->detail ) {
+ if ( b->opaque ) {
+ node->opaque = qtrue;
+ node->areaportal = qfalse;
+ } else if ( b->contents & CONTENTS_AREAPORTAL ) {
+ if ( !node->opaque ) {
+ node->areaportal = qtrue;
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ // split it by the node plane
+ SplitBrush ( b, node->planenum, &front, &back );
+ FreeBrush( b );
+
+ c = 0;
+ c += FilterBrushIntoTree_r( front, node->children[0] );
+ c += FilterBrushIntoTree_r( back, node->children[1] );
+
+ return c;
+}
+
+/*
+=====================
+FilterDetailBrushesIntoTree
+
+Fragment all the detail brushes into the structural leafs
+=====================
+*/
+void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree ) {
+ bspbrush_t *b, *newb;
+ int r;
+ int c_unique, c_clusters;
+ int i;
+
+ qprintf( "----- FilterDetailBrushesIntoTree -----\n");
+
+ c_unique = 0;
+ c_clusters = 0;
+ for ( b = e->brushes ; b ; b = b->next ) {
+ if ( !b->detail ) {
+ continue;
+ }
+ c_unique++;
+ newb = CopyBrush( b );
+ r = FilterBrushIntoTree_r( newb, tree->headnode );
+ c_clusters += r;
+
+ // mark all sides as visible so drawsurfs are created
+ if ( r ) {
+ for ( i = 0 ; i < b->numsides ; i++ ) {
+ if ( b->sides[i].winding ) {
+ b->sides[i].visible = qtrue;
+ }
+ }
+ }
+ }
+
+ qprintf( "%5i detail brushes\n", c_unique );
+ qprintf( "%5i cluster references\n", c_clusters );
+}
+
+/*
+=====================
+FilterStructuralBrushesIntoTree
+
+Mark the leafs as opaque and areaportals
+=====================
+*/
+void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree ) {
+ bspbrush_t *b, *newb;
+ int r;
+ int c_unique, c_clusters;
+ int i;
+
+ qprintf( "----- FilterStructuralBrushesIntoTree -----\n");
+
+ c_unique = 0;
+ c_clusters = 0;
+ for ( b = e->brushes ; b ; b = b->next ) {
+ if ( b->detail ) {
+ continue;
+ }
+ c_unique++;
+ newb = CopyBrush( b );
+ r = FilterBrushIntoTree_r( newb, tree->headnode );
+ c_clusters += r;
+
+ // mark all sides as visible so drawsurfs are created
+ if ( r ) {
+ for ( i = 0 ; i < b->numsides ; i++ ) {
+ if ( b->sides[i].winding ) {
+ b->sides[i].visible = qtrue;
+ }
+ }
+ }
+ }
+
+ qprintf( "%5i structural brushes\n", c_unique );
+ qprintf( "%5i cluster references\n", c_clusters );
+}
+
+
+
+/*
+================
+AllocTree
+================
+*/
+tree_t *AllocTree (void)
+{
+ tree_t *tree;
+
+ tree = malloc(sizeof(*tree));
+ memset (tree, 0, sizeof(*tree));
+ ClearBounds (tree->mins, tree->maxs);
+
+ return tree;
+}
+
+/*
+================
+AllocNode
+================
+*/
+node_t *AllocNode (void)
+{
+ node_t *node;
+
+ node = malloc(sizeof(*node));
+ memset (node, 0, sizeof(*node));
+
+ return node;
+}
+
+
+/*
+================
+WindingIsTiny
+
+Returns true if the winding would be crunched out of
+existance by the vertex snapping.
+================
+*/
+#define EDGE_LENGTH 0.2
+qboolean WindingIsTiny (winding_t *w)
+{
+/*
+ if (WindingArea (w) < 1)
+ return qtrue;
+ return qfalse;
+*/
+ int i, j;
+ vec_t len;
+ vec3_t delta;
+ int edges;
+
+ edges = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ j = i == w->numpoints - 1 ? 0 : i+1;
+ VectorSubtract (w->p[j], w->p[i], delta);
+ len = VectorLength (delta);
+ if (len > EDGE_LENGTH)
+ {
+ if (++edges == 3)
+ return qfalse;
+ }
+ }
+ return qtrue;
+}
+
+/*
+================
+WindingIsHuge
+
+Returns true if the winding still has one of the points
+from basewinding for plane
+================
+*/
+qboolean WindingIsHuge (winding_t *w)
+{
+ int i, j;
+
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ if (w->p[i][j] <= MIN_WORLD_COORD || w->p[i][j] >= MAX_WORLD_COORD)
+ return qtrue;
+ }
+ return qfalse;
+}
+
+//============================================================
+
+/*
+==================
+BrushMostlyOnSide
+
+==================
+*/
+int BrushMostlyOnSide (bspbrush_t *brush, plane_t *plane)
+{
+ int i, j;
+ winding_t *w;
+ vec_t d, max;
+ int side;
+
+ max = 0;
+ side = PSIDE_FRONT;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > max)
+ {
+ max = d;
+ side = PSIDE_FRONT;
+ }
+ if (-d > max)
+ {
+ max = -d;
+ side = PSIDE_BACK;
+ }
+ }
+ }
+ return side;
+}
+
+/*
+================
+SplitBrush
+
+Generates two new brushes, leaving the original
+unchanged
+================
+*/
+void SplitBrush (bspbrush_t *brush, int planenum,
+ bspbrush_t **front, bspbrush_t **back)
+{
+ bspbrush_t *b[2];
+ int i, j;
+ winding_t *w, *cw[2], *midwinding;
+ plane_t *plane, *plane2;
+ side_t *s, *cs;
+ float d, d_front, d_back;
+
+ *front = *back = NULL;
+ plane = &mapplanes[planenum];
+
+ // check all points
+ d_front = d_back = 0;
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ w = brush->sides[i].winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ d = DotProduct (w->p[j], plane->normal) - plane->dist;
+ if (d > 0 && d > d_front)
+ d_front = d;
+ if (d < 0 && d < d_back)
+ d_back = d;
+ }
+ }
+ if (d_front < 0.1) // PLANESIDE_EPSILON)
+ { // only on back
+ *back = CopyBrush (brush);
+ return;
+ }
+ if (d_back > -0.1) // PLANESIDE_EPSILON)
+ { // only on front
+ *front = CopyBrush (brush);
+ return;
+ }
+
+ // create a new winding from the split plane
+
+ w = BaseWindingForPlane (plane->normal, plane->dist);
+ for (i=0 ; i<brush->numsides && w ; i++)
+ {
+ if ( brush->sides[i].backSide ) {
+ continue; // fake back-sided polygons never split
+ }
+ plane2 = &mapplanes[brush->sides[i].planenum ^ 1];
+ ChopWindingInPlace (&w, plane2->normal, plane2->dist, 0); // PLANESIDE_EPSILON);
+ }
+
+ if (!w || WindingIsTiny (w) )
+ { // the brush isn't really split
+ int side;
+
+ side = BrushMostlyOnSide (brush, plane);
+ if (side == PSIDE_FRONT)
+ *front = CopyBrush (brush);
+ if (side == PSIDE_BACK)
+ *back = CopyBrush (brush);
+ return;
+ }
+
+ if (WindingIsHuge (w))
+ {
+ qprintf ("WARNING: huge winding\n");
+ }
+
+ midwinding = w;
+
+ // split it for real
+
+ for (i=0 ; i<2 ; i++)
+ {
+ b[i] = AllocBrush (brush->numsides+1);
+ memcpy( b[i], brush, sizeof( bspbrush_t ) - sizeof( brush->sides ) );
+ b[i]->numsides = 0;
+ b[i]->next = NULL;
+ b[i]->original = brush->original;
+ }
+
+ // split all the current windings
+
+ for (i=0 ; i<brush->numsides ; i++)
+ {
+ s = &brush->sides[i];
+ w = s->winding;
+ if (!w)
+ continue;
+ ClipWindingEpsilon (w, plane->normal, plane->dist,
+ 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1]);
+ for (j=0 ; j<2 ; j++)
+ {
+ if (!cw[j])
+ continue;
+/*
+ if (WindingIsTiny (cw[j]))
+ {
+ FreeWinding (cw[j]);
+ continue;
+ }
+*/
+ cs = &b[j]->sides[b[j]->numsides];
+ b[j]->numsides++;
+ *cs = *s;
+ cs->winding = cw[j];
+ }
+ }
+
+
+ // see if we have valid polygons on both sides
+
+ for (i=0 ; i<2 ; i++)
+ {
+ BoundBrush (b[i]);
+ for (j=0 ; j<3 ; j++)
+ {
+ if (b[i]->mins[j] < MIN_WORLD_COORD || b[i]->maxs[j] > MAX_WORLD_COORD)
+ {
+ qprintf ("bogus brush after clip\n");
+ break;
+ }
+ }
+
+ if (b[i]->numsides < 3 || j < 3)
+ {
+ FreeBrush (b[i]);
+ b[i] = NULL;
+ }
+ }
+
+ if ( !(b[0] && b[1]) )
+ {
+ if (!b[0] && !b[1])
+ qprintf ("split removed brush\n");
+ else
+ qprintf ("split not on both sides\n");
+ if (b[0])
+ {
+ FreeBrush (b[0]);
+ *front = CopyBrush (brush);
+ }
+ if (b[1])
+ {
+ FreeBrush (b[1]);
+ *back = CopyBrush (brush);
+ }
+ return;
+ }
+
+ // add the midwinding to both sides
+ for (i=0 ; i<2 ; i++)
+ {
+ cs = &b[i]->sides[b[i]->numsides];
+ b[i]->numsides++;
+
+ cs->planenum = planenum^i^1;
+ cs->shaderInfo = NULL;
+ if (i==0)
+ cs->winding = CopyWinding (midwinding);
+ else
+ cs->winding = midwinding;
+ }
+
+{
+ vec_t v1;
+ int i;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ v1 = BrushVolume (b[i]);
+ if (v1 < 1.0)
+ {
+ FreeBrush (b[i]);
+ b[i] = NULL;
+// qprintf ("tiny volume after clip\n");
+ }
+ }
+}
+
+ *front = b[0];
+ *back = b[1];
+}
diff --git a/q3map/brush_primit.c b/q3map/brush_primit.c
new file mode 100755
index 0000000..5ec44cc
--- /dev/null
+++ b/q3map/brush_primit.c
@@ -0,0 +1,52 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+// global flag
+int g_bBrushPrimit;
+
+// NOTE : ComputeAxisBase here and in editor code must always BE THE SAME !
+// WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0
+// rotation by (0,RotY,RotZ) assigns X to normal
+void ComputeAxisBase(vec3_t normal,vec3_t texX,vec3_t texY)
+{
+ vec_t RotY,RotZ;
+ // do some cleaning
+ if (fabs(normal[0])<1e-6)
+ normal[0]=0.0f;
+ if (fabs(normal[1])<1e-6)
+ normal[1]=0.0f;
+ if (fabs(normal[2])<1e-6)
+ normal[2]=0.0f;
+ // compute the two rotations around Y and Z to rotate X to normal
+ RotY=-atan2(normal[2],sqrt(normal[1]*normal[1]+normal[0]*normal[0]));
+ RotZ=atan2(normal[1],normal[0]);
+ // rotate (0,1,0) and (0,0,1) to compute texX and texY
+ texX[0]=-sin(RotZ);
+ texX[1]=cos(RotZ);
+ texX[2]=0;
+ // the texY vector is along -Z ( T texture coorinates axis )
+ texY[0]=-sin(RotY)*cos(RotZ);
+ texY[1]=-sin(RotY)*sin(RotZ);
+ texY[2]=-cos(RotY);
+}
diff --git a/q3map/bsp.c b/q3map/bsp.c
new file mode 100755
index 0000000..12d66aa
--- /dev/null
+++ b/q3map/bsp.c
@@ -0,0 +1,605 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+#ifdef _WIN32
+#ifdef _TTIMOBUILD
+#include "pakstuff.h"
+#else
+#include "../libs/pakstuff.h"
+#endif
+extern HWND hwndOut;
+#endif
+
+char source[1024];
+char tempsource[1024];
+char name[1024];
+
+vec_t microvolume = 1.0;
+qboolean glview;
+qboolean nodetail;
+qboolean fulldetail;
+qboolean onlyents;
+qboolean onlytextures;
+qboolean nowater;
+qboolean nofill;
+qboolean noopt;
+qboolean leaktest;
+qboolean verboseentities;
+qboolean noCurveBrushes;
+qboolean fakemap;
+qboolean notjunc;
+qboolean nomerge;
+qboolean nofog;
+qboolean nosubdivide;
+qboolean testExpand;
+qboolean showseams;
+
+char outbase[32];
+
+int entity_num;
+
+/*
+============
+ProcessWorldModel
+
+============
+*/
+void ProcessWorldModel( void ) {
+ entity_t *e;
+ tree_t *tree;
+ bspface_t *faces;
+ qboolean leaked;
+
+ BeginModel();
+
+ e = &entities[0];
+ e->firstDrawSurf = 0;//numMapDrawSurfs;
+
+ // check for patches with adjacent edges that need to LOD together
+ PatchMapDrawSurfs( e );
+
+ // build an initial bsp tree using all of the sides
+ // of all of the structural brushes
+ faces = MakeStructuralBspFaceList ( entities[0].brushes );
+ tree = FaceBSP( faces );
+ MakeTreePortals (tree);
+ FilterStructuralBrushesIntoTree( e, tree );
+
+ // see if the bsp is completely enclosed
+ if ( FloodEntities (tree) ) {
+ // rebuild a better bsp tree using only the
+ // sides that are visible from the inside
+ FillOutside (tree->headnode);
+
+ // chop the sides to the convex hull of
+ // their visible fragments, giving us the smallest
+ // polygons
+ ClipSidesIntoTree( e, tree );
+
+ faces = MakeVisibleBspFaceList( entities[0].brushes );
+ FreeTree (tree);
+ tree = FaceBSP( faces );
+ MakeTreePortals( tree );
+ FilterStructuralBrushesIntoTree( e, tree );
+ leaked = qfalse;
+ } else {
+ _printf ("**********************\n");
+ _printf ("******* leaked *******\n");
+ _printf ("**********************\n");
+ LeakFile (tree);
+ if ( leaktest ) {
+ _printf ("--- MAP LEAKED, ABORTING LEAKTEST ---\n");
+ exit (0);
+ }
+ leaked = qtrue;
+
+ // chop the sides to the convex hull of
+ // their visible fragments, giving us the smallest
+ // polygons
+ ClipSidesIntoTree( e, tree );
+ }
+
+ // save out information for visibility processing
+ NumberClusters( tree );
+ if ( !leaked ) {
+ WritePortalFile( tree );
+ }
+ if ( glview ) {
+ // dump the portals for debugging
+ WriteGLView( tree, source );
+ }
+ FloodAreas (tree);
+
+ // add references to the detail brushes
+ FilterDetailBrushesIntoTree( e, tree );
+
+ // create drawsurfs for triangle models
+ AddTriangleModels( tree );
+
+ // drawsurfs that cross fog boundaries will need to
+ // be split along the bound
+ if ( !nofog ) {
+ FogDrawSurfs(); // may fragment drawsurfs
+ }
+
+ // subdivide each drawsurf as required by shader tesselation
+ if ( !nosubdivide ) {
+ SubdivideDrawSurfs( e, tree );
+ }
+
+ // merge together all common shaders on the same plane and remove
+ // all colinear points, so extra tjunctions won't be generated
+ if ( !nomerge ) {
+ MergeSides( e, tree ); // !@# testing
+ }
+
+ // add in any vertexes required to fix tjunctions
+ if ( !notjunc ) {
+ FixTJunctions( e );
+ }
+
+ // allocate lightmaps for faces and patches
+ AllocateLightmaps( e );
+
+ // add references to the final drawsurfs in the apropriate clusters
+ FilterDrawsurfsIntoTree( e, tree );
+
+ EndModel( tree->headnode );
+
+ FreeTree (tree);
+}
+
+/*
+============
+ProcessSubModel
+
+============
+*/
+void ProcessSubModel( void ) {
+ entity_t *e;
+ tree_t *tree;
+ bspbrush_t *b, *bc;
+ node_t *node;
+
+ BeginModel ();
+
+ e = &entities[entity_num];
+ e->firstDrawSurf = numMapDrawSurfs;
+
+ PatchMapDrawSurfs( e );
+
+ // just put all the brushes in an empty leaf
+ // FIXME: patches?
+ node = AllocNode();
+ node->planenum = PLANENUM_LEAF;
+ for ( b = e->brushes ; b ; b = b->next ) {
+ bc = CopyBrush( b );
+ bc->next = node->brushlist;
+ node->brushlist = bc;
+ }
+
+ tree = AllocTree();
+ tree->headnode = node;
+
+ ClipSidesIntoTree( e, tree );
+
+ // subdivide each drawsurf as required by shader tesselation or fog
+ if ( !nosubdivide ) {
+ SubdivideDrawSurfs( e, tree );
+ }
+
+ // merge together all common shaders on the same plane and remove
+ // all colinear points, so extra tjunctions won't be generated
+ if ( !nomerge ) {
+ MergeSides( e, tree ); // !@# testing
+ }
+
+ // add in any vertexes required to fix tjunctions
+ if ( !notjunc ) {
+ FixTJunctions( e );
+ }
+
+ // allocate lightmaps for faces and patches
+ AllocateLightmaps( e );
+
+ // add references to the final drawsurfs in the apropriate clusters
+ FilterDrawsurfsIntoTree( e, tree );
+
+ EndModel ( node );
+
+ FreeTree( tree );
+}
+
+
+/*
+============
+ProcessModels
+============
+*/
+void ProcessModels (void)
+{
+ qboolean oldVerbose;
+ entity_t *entity;
+
+ oldVerbose = verbose;
+
+ BeginBSPFile ();
+
+ for ( entity_num=0 ; entity_num< num_entities ; entity_num++ ) {
+ entity = &entities[entity_num];
+
+ if ( !entity->brushes && !entity->patches ) {
+ continue;
+ }
+
+ qprintf ("############### model %i ###############\n", nummodels);
+ if (entity_num == 0)
+ ProcessWorldModel ();
+ else
+ ProcessSubModel ();
+
+ if (!verboseentities)
+ verbose = qfalse; // don't bother printing submodels
+ }
+
+ verbose = oldVerbose;
+}
+
+/*
+============
+Bspinfo
+============
+*/
+void Bspinfo( int count, char **fileNames ) {
+ int i;
+ char source[1024];
+ int size;
+ FILE *f;
+
+ if ( count < 1 ) {
+ _printf( "No files to dump info for.\n");
+ return;
+ }
+
+ for ( i = 0 ; i < count ; i++ ) {
+ _printf ("---------------------\n");
+ strcpy (source, fileNames[ i ] );
+ DefaultExtension (source, ".bsp");
+ f = fopen (source, "rb");
+ if (f)
+ {
+ size = Q_filelength (f);
+ fclose (f);
+ }
+ else
+ size = 0;
+ _printf ("%s: %i\n", source, size);
+
+ LoadBSPFile (source);
+ PrintBSPFileSizes ();
+ _printf ("---------------------\n");
+ }
+}
+
+
+/*
+============
+OnlyEnts
+============
+*/
+void OnlyEnts( void ) {
+ char out[1024];
+
+ sprintf (out, "%s.bsp", source);
+ LoadBSPFile (out);
+ num_entities = 0;
+
+ LoadMapFile (name);
+ SetModelNumbers ();
+ SetLightStyles ();
+
+ UnparseEntities ();
+
+ WriteBSPFile (out);
+}
+
+
+/*
+============
+OnlyTextures
+============
+*/
+void OnlyTextures( void ) { // FIXME!!!
+ char out[1024];
+ int i;
+
+ Error( "-onlytextures isn't working now..." );
+
+ sprintf (out, "%s.bsp", source);
+
+ LoadMapFile (name);
+
+ LoadBSPFile (out);
+
+ // replace all the drawsurface shader names
+ for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
+ }
+
+ WriteBSPFile (out);
+}
+
+
+/*
+============
+main
+============
+*/
+int LightMain( int argc, char **argv );
+int VLightMain (int argc, char **argv);
+int VSoundMain (int argc, char **argv);
+int VisMain( int argc, char **argv );
+
+int main (int argc, char **argv) {
+ int i;
+ double start, end;
+ char path[1024];
+
+ _printf ("Q3Map v1.0s (c) 1999 Id Software Inc.\n");
+
+ if ( argc < 2 ) {
+ Error ("usage: q3map [options] mapfile");
+ }
+
+ // check for general program options
+ if (!strcmp(argv[1], "-info")) {
+ Bspinfo( argc - 2, argv + 2 );
+ return 0;
+ }
+ if (!strcmp(argv[1], "-light")) {
+ LightMain( argc - 1, argv + 1 );
+ return 0;
+ }
+ if (!strcmp(argv[1], "-vlight")) {
+ VLightMain( argc - 1, argv + 1 );
+ return 0;
+ }
+ if (!strcmp(argv[1], "-vsound")) {
+ VSoundMain( argc - 1, argv + 1 );
+ return 0;
+ }
+ if (!strcmp(argv[1], "-vis")) {
+ VisMain( argc - 1, argv + 1 );
+ return 0;
+ }
+
+ // do a bsp if nothing else was specified
+
+ _printf ("---- q3map ----\n");
+
+ tempsource[0] = '\0';
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i],"-tempname"))
+ {
+ strcpy(tempsource, argv[++i]);
+ }
+ else if (!strcmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-glview"))
+ {
+ glview = qtrue;
+ }
+ else if (!strcmp(argv[i], "-v"))
+ {
+ _printf ("verbose = true\n");
+ verbose = qtrue;
+ }
+ else if (!strcmp(argv[i], "-draw"))
+ {
+ _printf ("drawflag = true\n");
+ drawflag = qtrue;
+ }
+ else if (!strcmp(argv[i], "-nowater"))
+ {
+ _printf ("nowater = true\n");
+ nowater = qtrue;
+ }
+ else if (!strcmp(argv[i], "-noopt"))
+ {
+ _printf ("noopt = true\n");
+ noopt = qtrue;
+ }
+ else if (!strcmp(argv[i], "-nofill"))
+ {
+ _printf ("nofill = true\n");
+ nofill = qtrue;
+ }
+ else if (!strcmp(argv[i], "-nodetail"))
+ {
+ _printf ("nodetail = true\n");
+ nodetail = qtrue;
+ }
+ else if (!strcmp(argv[i], "-fulldetail"))
+ {
+ _printf ("fulldetail = true\n");
+ fulldetail = qtrue;
+ }
+ else if (!strcmp(argv[i], "-onlyents"))
+ {
+ _printf ("onlyents = true\n");
+ onlyents = qtrue;
+ }
+ else if (!strcmp(argv[i], "-onlytextures"))
+ {
+ _printf ("onlytextures = true\n"); // FIXME: make work again!
+ onlytextures = qtrue;
+ }
+ else if (!strcmp(argv[i], "-micro"))
+ {
+ microvolume = atof(argv[i+1]);
+ _printf ("microvolume = %f\n", microvolume);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-nofog"))
+ {
+ _printf ("nofog = true\n");
+ nofog = qtrue;
+ }
+ else if (!strcmp(argv[i], "-nosubdivide"))
+ {
+ _printf ("nosubdivide = true\n");
+ nosubdivide = qtrue;
+ }
+ else if (!strcmp(argv[i], "-leaktest"))
+ {
+ _printf ("leaktest = true\n");
+ leaktest = qtrue;
+ }
+ else if (!strcmp(argv[i], "-verboseentities"))
+ {
+ _printf ("verboseentities = true\n");
+ verboseentities = qtrue;
+ }
+ else if (!strcmp(argv[i], "-nocurves"))
+ {
+ noCurveBrushes = qtrue;
+ _printf ("no curve brushes\n");
+ }
+ else if (!strcmp(argv[i], "-notjunc"))
+ {
+ notjunc = qtrue;
+ _printf ("no tjunction fixing\n");
+ }
+ else if (!strcmp(argv[i], "-expand"))
+ {
+ testExpand = qtrue;
+ _printf ("Writing expanded.map.\n");
+ }
+ else if (!strcmp(argv[i], "-showseams"))
+ {
+ showseams = qtrue;
+ _printf ("Showing seams on terrain.\n");
+ }
+ else if (!strcmp (argv[i],"-tmpout"))
+ {
+ strcpy (outbase, "/tmp");
+ }
+ else if (!strcmp (argv[i],"-fakemap"))
+ {
+ fakemap = qtrue;
+ _printf( "will generate fakemap.map\n");
+ }
+ 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 (argv[i][0] == '-')
+ Error ("Unknown option \"%s\"", argv[i]);
+ else
+ break;
+ }
+
+ if (i != argc - 1)
+ Error ("usage: q3map [options] mapfile");
+
+ start = I_FloatTime ();
+
+ ThreadSetDefault ();
+ //numthreads = 1; // multiple threads aren't helping because of heavy malloc use
+ SetQdirFromPath (argv[i]);
+
+#ifdef _WIN32
+ InitPakFile(gamedir, NULL);
+#endif
+
+ strcpy (source, ExpandArg (argv[i]));
+ StripExtension (source);
+
+ // delete portal and line files
+ sprintf (path, "%s.prt", source);
+ remove (path);
+ sprintf (path, "%s.lin", source);
+ remove (path);
+
+ strcpy (name, ExpandArg (argv[i]));
+ if ( strcmp(name + strlen(name) - 4, ".reg" ) ) {
+ // if we are doing a full map, delete the last saved region map
+ sprintf (path, "%s.reg", source);
+ remove (path);
+
+ DefaultExtension (name, ".map"); // might be .reg
+ }
+
+ //
+ // if onlyents, just grab the entites and resave
+ //
+ if ( onlyents ) {
+ OnlyEnts();
+ return 0;
+ }
+
+ //
+ // if onlytextures, just grab the textures and resave
+ //
+ if ( onlytextures ) {
+ OnlyTextures();
+ return 0;
+ }
+
+ //
+ // start from scratch
+ //
+ LoadShaderInfo();
+
+ // load original file from temp spot in case it was renamed by the editor on the way in
+ if (strlen(tempsource) > 0) {
+ LoadMapFile (tempsource);
+ } else {
+ LoadMapFile (name);
+ }
+
+ SetModelNumbers ();
+ SetLightStyles ();
+
+ ProcessModels ();
+
+ EndBSPFile();
+
+ end = I_FloatTime ();
+ _printf ("%5.0f seconds elapsed\n", end-start);
+
+ // remove temp name if appropriate
+ if (strlen(tempsource) > 0) {
+ remove(tempsource);
+ }
+
+ return 0;
+}
+
diff --git a/q3map/facebsp.c b/q3map/facebsp.c
new file mode 100755
index 0000000..d1b00b4
--- /dev/null
+++ b/q3map/facebsp.c
@@ -0,0 +1,379 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+int c_faceLeafs;
+
+
+/*
+================
+AllocBspFace
+================
+*/
+bspface_t *AllocBspFace( void ) {
+ bspface_t *f;
+
+ f = malloc(sizeof(*f));
+ memset( f, 0, sizeof(*f) );
+
+ return f;
+}
+
+/*
+================
+FreeBspFace
+================
+*/
+void FreeBspFace( bspface_t *f ) {
+ if ( f->w ) {
+ FreeWinding( f->w );
+ }
+ free( f );
+}
+
+
+/*
+================
+SelectSplitPlaneNum
+================
+*/
+int hintsplit;
+
+#define BLOCK_SIZE 1024
+int SelectSplitPlaneNum( node_t *node, bspface_t *list ) {
+ bspface_t *split;
+ bspface_t *check;
+ bspface_t *bestSplit;
+ int splits, facing, front, back;
+ int side;
+ plane_t *plane;
+ int value, bestValue;
+ int i;
+ vec3_t normal;
+ float dist;
+ int planenum;
+
+ hintsplit = qfalse;
+ // if it is crossing a 1k block boundary, force a split
+ for ( i = 0 ; i < 2 ; i++ ) {
+ dist = BLOCK_SIZE * ( floor( node->mins[i] / BLOCK_SIZE ) + 1 );
+ if ( node->maxs[i] > dist ) {
+ VectorClear( normal );
+ normal[i] = 1;
+ planenum = FindFloatPlane( normal, dist );
+ return planenum;
+ }
+ }
+
+ // pick one of the face planes
+ bestValue = -99999;
+ bestSplit = list;
+
+ for ( split = list ; split ; split = split->next ) {
+ split->checked = qfalse;
+ }
+
+ for ( split = list ; split ; split = split->next ) {
+ if ( split->checked ) {
+ continue;
+ }
+ plane = &mapplanes[ split->planenum ];
+ splits = 0;
+ facing = 0;
+ front = 0;
+ back = 0;
+ for ( check = list ; check ; check = check->next ) {
+ if ( check->planenum == split->planenum ) {
+ facing++;
+ check->checked = qtrue; // won't need to test this plane again
+ continue;
+ }
+ side = WindingOnPlaneSide( check->w, plane->normal, plane->dist );
+ if ( side == SIDE_CROSS ) {
+ splits++;
+ } else if ( side == SIDE_FRONT ) {
+ front++;
+ } else if ( side == SIDE_BACK ) {
+ back++;
+ }
+ }
+ value = 5*facing - 5*splits; // - abs(front-back);
+ if ( plane->type < 3 ) {
+ value+=5; // axial is better
+ }
+ value += split->priority; // prioritize hints higher
+
+ if ( value > bestValue ) {
+ bestValue = value;
+ bestSplit = split;
+ }
+ }
+
+ if ( bestValue == -99999 ) {
+ return -1;
+ }
+
+ if (bestSplit->hint)
+ hintsplit = qtrue;
+
+ return bestSplit->planenum;
+}
+
+int CountFaceList( bspface_t *list ) {
+ int c;
+ c = 0;
+ for ( ; list ; list = list->next ) {
+ c++;
+ }
+ return c;
+}
+
+/*
+================
+BuildFaceTree_r
+================
+*/
+void BuildFaceTree_r( node_t *node, bspface_t *list ) {
+ bspface_t *split;
+ bspface_t *next;
+ int side;
+ plane_t *plane;
+ bspface_t *newFace;
+ bspface_t *childLists[2];
+ winding_t *frontWinding, *backWinding;
+ int i;
+ int splitPlaneNum;
+
+ i = CountFaceList( list );
+
+ splitPlaneNum = SelectSplitPlaneNum( node, list );
+ // if we don't have any more faces, this is a node
+ if ( splitPlaneNum == -1 ) {
+ node->planenum = PLANENUM_LEAF;
+ c_faceLeafs++;
+ return;
+ }
+
+ // partition the list
+ node->planenum = splitPlaneNum;
+ node->hint = hintsplit;
+ plane = &mapplanes[ splitPlaneNum ];
+ childLists[0] = NULL;
+ childLists[1] = NULL;
+ for ( split = list ; split ; split = next ) {
+ next = split->next;
+
+ if ( split->planenum == node->planenum ) {
+ FreeBspFace( split );
+ continue;
+ }
+
+ side = WindingOnPlaneSide( split->w, plane->normal, plane->dist );
+
+ if ( side == SIDE_CROSS ) {
+ ClipWindingEpsilon( split->w, plane->normal, plane->dist, CLIP_EPSILON * 2,
+ &frontWinding, &backWinding );
+ if ( frontWinding ) {
+ newFace = AllocBspFace();
+ newFace->w = frontWinding;
+ newFace->next = childLists[0];
+ newFace->planenum = split->planenum;
+ newFace->priority = split->priority;
+ newFace->hint = split->hint;
+ childLists[0] = newFace;
+ }
+ if ( backWinding ) {
+ newFace = AllocBspFace();
+ newFace->w = backWinding;
+ newFace->next = childLists[1];
+ newFace->planenum = split->planenum;
+ newFace->priority = split->priority;
+ newFace->hint = split->hint;
+ childLists[1] = newFace;
+ }
+ FreeBspFace( split );
+ } else if ( side == SIDE_FRONT ) {
+ split->next = childLists[0];
+ childLists[0] = split;
+ } else if ( side == SIDE_BACK ) {
+ split->next = childLists[1];
+ childLists[1] = split;
+ }
+ }
+
+
+ // recursively process children
+ for ( i = 0 ; i < 2 ; i++ ) {
+ node->children[i] = AllocNode();
+ node->children[i]->parent = node;
+ VectorCopy( node->mins, node->children[i]->mins );
+ VectorCopy( node->maxs, node->children[i]->maxs );
+ }
+
+ for ( i = 0 ; i < 3 ; i++ ) {
+ if ( plane->normal[i] == 1 ) {
+ node->children[0]->mins[i] = plane->dist;
+ node->children[1]->maxs[i] = plane->dist;
+ break;
+ }
+ }
+
+ for ( i = 0 ; i < 2 ; i++ ) {
+ BuildFaceTree_r ( node->children[i], childLists[i]);
+ }
+}
+
+
+/*
+================
+FaceBSP
+
+List will be freed before returning
+================
+*/
+tree_t *FaceBSP( bspface_t *list ) {
+ tree_t *tree;
+ bspface_t *face;
+ int i;
+ int count;
+
+ qprintf( "--- FaceBSP ---\n" );
+
+ tree = AllocTree ();
+
+ count = 0;
+ for ( face = list ; face ; face = face->next ) {
+ count++;
+ for ( i = 0 ; i < face->w->numpoints ; i++ ) {
+ AddPointToBounds( face->w->p[i], tree->mins, tree->maxs);
+ }
+ }
+ qprintf( "%5i faces\n", count );
+
+ tree->headnode = AllocNode();
+ VectorCopy( tree->mins, tree->headnode->mins );
+ VectorCopy( tree->maxs, tree->headnode->maxs );
+ c_faceLeafs = 0;
+
+ BuildFaceTree_r ( tree->headnode, list );
+
+ qprintf( "%5i leafs\n", c_faceLeafs );
+
+ return tree;
+}
+
+
+/*
+=================
+BspFaceForPortal
+=================
+*/
+bspface_t *BspFaceForPortal( portal_t *p ) {
+ bspface_t *f;
+
+ f = AllocBspFace();
+ f->w = CopyWinding( p->winding );
+ f->planenum = p->onnode->planenum & ~1;
+
+ return f;
+}
+
+
+
+/*
+=================
+MakeStructuralBspFaceList
+=================
+*/
+bspface_t *MakeStructuralBspFaceList( bspbrush_t *list ) {
+ bspbrush_t *b;
+ int i;
+ side_t *s;
+ winding_t *w;
+ bspface_t *f, *flist;
+
+ flist = NULL;
+ for ( b = list ; b ; b = b->next ) {
+ if ( b->detail ) {
+ continue;
+ }
+ for ( i = 0 ; i < b->numsides ; i++ ) {
+ s = &b->sides[i];
+ w = s->winding;
+ if ( !w ) {
+ continue;
+ }
+ f = AllocBspFace();
+ f->w = CopyWinding( w );
+ f->planenum = s->planenum & ~1;
+ f->next = flist;
+ if (s->surfaceFlags & SURF_HINT) {
+ //f->priority = HINT_PRIORITY;
+ f->hint = qtrue;
+ }
+ flist = f;
+ }
+ }
+
+ return flist;
+}
+
+/*
+=================
+MakeVisibleBspFaceList
+=================
+*/
+bspface_t *MakeVisibleBspFaceList( bspbrush_t *list ) {
+ bspbrush_t *b;
+ int i;
+ side_t *s;
+ winding_t *w;
+ bspface_t *f, *flist;
+
+ flist = NULL;
+ for ( b = list ; b ; b = b->next ) {
+ if ( b->detail ) {
+ continue;
+ }
+ for ( i = 0 ; i < b->numsides ; i++ ) {
+ s = &b->sides[i];
+ w = s->visibleHull;
+ if ( !w ) {
+ continue;
+ }
+ f = AllocBspFace();
+ f->w = CopyWinding( w );
+ f->planenum = s->planenum & ~1;
+ f->next = flist;
+ if (s->surfaceFlags & SURF_HINT) {
+ //f->priority = HINT_PRIORITY;
+ f->hint = qtrue;
+ }
+ flist = f;
+ }
+ }
+
+ return flist;
+}
+
diff --git a/q3map/fog.c b/q3map/fog.c
new file mode 100755
index 0000000..cf4060a
--- /dev/null
+++ b/q3map/fog.c
@@ -0,0 +1,554 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+int c_fogFragment;
+int c_fogPatchFragments;
+
+/*
+====================
+DrawSurfToMesh
+====================
+*/
+mesh_t *DrawSurfToMesh( mapDrawSurface_t *ds ) {
+ mesh_t *m;
+
+ m = malloc( sizeof( *m ) );
+ m->width = ds->patchWidth;
+ m->height = ds->patchHeight;
+ m->verts = malloc( sizeof(m->verts[0]) * m->width * m->height );
+ memcpy( m->verts, ds->verts, sizeof(m->verts[0]) * m->width * m->height );
+
+ return m;
+}
+
+
+/*
+====================
+SplitMeshByPlane
+====================
+*/
+void SplitMeshByPlane( mesh_t *in, vec3_t normal, float dist, mesh_t **front, mesh_t **back ) {
+ int w, h, split;
+ float d[MAX_PATCH_SIZE][MAX_PATCH_SIZE];
+ drawVert_t *dv, *v1, *v2;
+ int c_front, c_back, c_on;
+ mesh_t *f, *b;
+ int i;
+ float frac;
+ int frontAprox, backAprox;
+
+ for ( i = 0 ; i < 2 ; i++ ) {
+ dv = in->verts;
+ c_front = 0;
+ c_back = 0;
+ c_on = 0;
+ for ( h = 0 ; h < in->height ; h++ ) {
+ for ( w = 0 ; w < in->width ; w++, dv++ ) {
+ d[h][w] = DotProduct( dv->xyz, normal ) - dist;
+ if ( d[h][w] > ON_EPSILON ) {
+ c_front++;
+ } else if ( d[h][w] < -ON_EPSILON ) {
+ c_back++;
+ } else {
+ c_on++;
+ }
+ }
+ }
+
+ *front = NULL;
+ *back = NULL;
+
+ if ( !c_front ) {
+ *back = in;
+ return;
+ }
+ if ( !c_back ) {
+ *front = in;
+ return;
+ }
+
+ // find a split point
+ split = -1;
+ for ( w = 0 ; w < in->width -1 ; w++ ) {
+ if ( ( d[0][w] < 0 ) != ( d[0][w+1] < 0 ) ) {
+ if ( split == -1 ) {
+ split = w;
+ break;
+ }
+ }
+ }
+
+ if ( split == -1 ) {
+ if ( i == 1 ) {
+ qprintf( "No crossing points in patch\n");
+ *front = in;
+ return;
+ }
+
+ in = TransposeMesh( in );
+ InvertMesh( in );
+ continue;
+ }
+
+ // make sure the split point stays the same for all other rows
+ for ( h = 1 ; h < in->height ; h++ ) {
+ for ( w = 0 ; w < in->width -1 ; w++ ) {
+ if ( ( d[h][w] < 0 ) != ( d[h][w+1] < 0 ) ) {
+ if ( w != split ) {
+ _printf( "multiple crossing points for patch -- can't clip\n");
+ *front = in;
+ return;
+ }
+ }
+ }
+ if ( ( d[h][split] < 0 ) == ( d[h][split+1] < 0 ) ) {
+ _printf( "differing crossing points for patch -- can't clip\n");
+ *front = in;
+ return;
+ }
+ }
+
+ break;
+ }
+
+
+ // create two new meshes
+ f = malloc( sizeof( *f ) );
+ f->width = split + 2;
+ if ( ! (f->width & 1) ) {
+ f->width++;
+ frontAprox = 1;
+ } else {
+ frontAprox = 0;
+ }
+ if ( f->width > MAX_PATCH_SIZE ) {
+ Error( "MAX_PATCH_SIZE after split");
+ }
+ f->height = in->height;
+ f->verts = malloc( sizeof(f->verts[0]) * f->width * f->height );
+
+ b = malloc( sizeof( *b ) );
+ b->width = in->width - split;
+ if ( ! (b->width & 1) ) {
+ b->width++;
+ backAprox = 1;
+ } else {
+ backAprox = 0;
+ }
+ if ( b->width > MAX_PATCH_SIZE ) {
+ Error( "MAX_PATCH_SIZE after split");
+ }
+ b->height = in->height;
+ b->verts = malloc( sizeof(b->verts[0]) * b->width * b->height );
+
+ if ( d[0][0] > 0 ) {
+ *front = f;
+ *back = b;
+ } else {
+ *front = b;
+ *back = f;
+ }
+
+ // distribute the points
+ for ( w = 0 ; w < in->width ; w++ ) {
+ for ( h = 0 ; h < in->height ; h++ ) {
+ if ( w <= split ) {
+ f->verts[ h * f->width + w ] = in->verts[ h * in->width + w ];
+ } else {
+ b->verts[ h * b->width + w - split + backAprox ] = in->verts[ h * in->width + w ];
+ }
+ }
+ }
+
+ // clip the crossing line
+ for ( h = 0 ; h < in->height ; h++ ) {
+ dv = &f->verts[ h * f->width + split + 1 ];
+ v1 = &in->verts[ h * in->width + split ];
+ v2 = &in->verts[ h * in->width + split + 1 ];
+ frac = d[h][split] / ( d[h][split] - d[h][split+1] );
+ for ( i = 0 ; i < 10 ; i++ ) {
+ dv->xyz[i] = v1->xyz[i] + frac * ( v2->xyz[i] - v1->xyz[i] );
+ }
+ dv->xyz[10] = 0;//set all 4 colors to 0
+ if ( frontAprox ) {
+ f->verts[ h * f->width + split + 2 ] = *dv;
+ }
+ b->verts[ h * b->width ] = *dv;
+ if ( backAprox ) {
+ b->verts[ h * b->width + 1 ] = *dv;
+ }
+ }
+
+ /*
+PrintMesh( in );
+_printf("\n");
+PrintMesh( f );
+_printf("\n");
+PrintMesh( b );
+_printf("\n");
+ */
+
+ FreeMesh( in );
+}
+
+
+/*
+====================
+ChopPatchByBrush
+====================
+*/
+qboolean ChopPatchByBrush( mapDrawSurface_t *ds, bspbrush_t *b ) {
+ int i, j;
+ side_t *s;
+ plane_t *plane;
+ mesh_t *outside[MAX_BRUSH_SIDES];
+ int numOutside;
+ mesh_t *m, *front, *back;
+ mapDrawSurface_t *newds;
+
+ m = DrawSurfToMesh( ds );
+ numOutside = 0;
+
+ // only split by the top and bottom planes to avoid
+ // some messy patch clipping issues
+
+ for ( i = 4 ; i <= 5 ; i++ ) {
+ s = &b->sides[ i ];
+ plane = &mapplanes[ s->planenum ];
+
+ SplitMeshByPlane( m, plane->normal, plane->dist, &front, &back );
+
+ if ( !back ) {
+ // nothing actually contained inside
+ for ( j = 0 ; j < numOutside ; j++ ) {
+ FreeMesh( outside[j] );
+ }
+ return qfalse;
+ }
+ m = back;
+
+ if ( front ) {
+ if ( numOutside == MAX_BRUSH_SIDES ) {
+ Error( "MAX_BRUSH_SIDES" );
+ }
+ outside[ numOutside ] = front;
+ numOutside++;
+ }
+ }
+
+ // all of outside fragments become seperate drawsurfs
+ c_fogPatchFragments += numOutside;
+ for ( i = 0 ; i < numOutside ; i++ ) {
+ newds = DrawSurfaceForMesh( outside[ i ] );
+ newds->shaderInfo = ds->shaderInfo;
+ FreeMesh( outside[ i ] );
+ }
+
+ // replace ds with m
+ ds->patchWidth = m->width;
+ ds->patchHeight = m->height;
+ ds->numVerts = m->width * m->height;
+ free( ds->verts );
+ ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
+ memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
+
+ FreeMesh( m );
+
+ return qtrue;
+}
+
+//===============================================================================
+
+/*
+====================
+WindingFromDrawSurf
+====================
+*/
+winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds ) {
+ winding_t *w;
+ int i;
+
+ w = AllocWinding( ds->numVerts );
+ w->numpoints = ds->numVerts;
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ VectorCopy( ds->verts[i].xyz, w->p[i] );
+ }
+ return w;
+}
+
+/*
+====================
+ChopFaceByBrush
+
+There may be a fragment contained in the brush
+====================
+*/
+qboolean ChopFaceByBrush( mapDrawSurface_t *ds, bspbrush_t *b ) {
+ int i, j;
+ side_t *s;
+ plane_t *plane;
+ winding_t *w;
+ winding_t *front, *back;
+ winding_t *outside[MAX_BRUSH_SIDES];
+ int numOutside;
+ mapDrawSurface_t *newds;
+ drawVert_t *dv;
+ shaderInfo_t *si;
+ float mins[2];
+
+ // brush primitive :
+ // axis base
+ vec3_t texX,texY;
+ vec_t x,y;
+
+ w = WindingFromDrawSurf( ds );
+ numOutside = 0;
+
+ for ( i = 0 ; i < b->numsides ; i++ ) {
+ s = &b->sides[ i ];
+ if ( s->backSide ) {
+ continue;
+ }
+ plane = &mapplanes[ s->planenum ];
+
+ // handle coplanar outfacing (don't fog)
+ if ( ds->side->planenum == s->planenum ) {
+ return qfalse;
+ }
+
+ // handle coplanar infacing (keep inside)
+ if ( ( ds->side->planenum ^ 1 ) == s->planenum ) {
+ continue;
+ }
+
+ // general case
+ ClipWindingEpsilon( w, plane->normal, plane->dist, ON_EPSILON,
+ &front, &back );
+ FreeWinding( w );
+ if ( !back ) {
+ // nothing actually contained inside
+ for ( j = 0 ; j < numOutside ; j++ ) {
+ FreeWinding( outside[j] );
+ }
+ return qfalse;
+ }
+ if ( front ) {
+ if ( numOutside == MAX_BRUSH_SIDES ) {
+ Error( "MAX_BRUSH_SIDES" );
+ }
+ outside[ numOutside ] = front;
+ numOutside++;
+ }
+ w = back;
+ }
+
+ // all of outside fragments become seperate drawsurfs
+ // linked to the same side
+ c_fogFragment += numOutside;
+ s = ds->side;
+
+ for ( i = 0 ; i < numOutside ; i++ ) {
+ newds = DrawSurfaceForSide( ds->mapBrush, s, outside[i] );
+ FreeWinding( outside[i] );
+ }
+
+
+ // replace ds->verts with the verts for w
+ ds->numVerts = w->numpoints;
+ free( ds->verts );
+
+ ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
+ memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );
+
+ si = s->shaderInfo;
+
+ mins[0] = 9999;
+ mins[1] = 9999;
+
+ // compute s/t coordinates from brush primitive texture matrix
+ // compute axis base
+ ComputeAxisBase( mapplanes[s->planenum].normal, texX, texY );
+
+ for ( j = 0 ; j < w->numpoints ; j++ ) {
+ dv = ds->verts + j;
+ VectorCopy( w->p[j], dv->xyz );
+
+ if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
+ {
+ // calculate texture s/t
+ dv->st[0] = s->vecs[0][3] + DotProduct( s->vecs[0], dv->xyz );
+ dv->st[1] = s->vecs[1][3] + DotProduct( s->vecs[1], dv->xyz );
+ dv->st[0] /= si->width;
+ dv->st[1] /= si->height;
+ }
+ else
+ {
+ // calculate texture s/t from brush primitive texture matrix
+ x = DotProduct( dv->xyz, texX );
+ y = DotProduct( dv->xyz, texY );
+ dv->st[0]=s->texMat[0][0]*x+s->texMat[0][1]*y+s->texMat[0][2];
+ dv->st[1]=s->texMat[1][0]*x+s->texMat[1][1]*y+s->texMat[1][2];
+ }
+
+ if ( dv->st[0] < mins[0] ) {
+ mins[0] = dv->st[0];
+ }
+ if ( dv->st[1] < mins[1] ) {
+ mins[1] = dv->st[1];
+ }
+
+ // copy normal
+ VectorCopy ( mapplanes[s->planenum].normal, dv->normal );
+ }
+
+ // adjust the texture coordinates to be as close to 0 as possible
+ if ( !si->globalTexture ) {
+ mins[0] = floor( mins[0] );
+ mins[1] = floor( mins[1] );
+ for ( i = 0 ; i < w->numpoints ; i++ ) {
+ dv = ds->verts + i;
+ dv->st[0] -= mins[0];
+ dv->st[1] -= mins[1];
+ }
+ }
+
+ return qtrue;
+}
+
+//===============================================================================
+
+
+/*
+=====================
+FogDrawSurfs
+
+Call after the surface list has been pruned,
+before tjunction fixing
+before lightmap allocation
+=====================
+*/
+void FogDrawSurfs( void ) {
+ int i, j, k;
+ mapDrawSurface_t *ds;
+ bspbrush_t *b;
+ vec3_t mins, maxs;
+ int c_fogged;
+ int numBaseDrawSurfs;
+ dfog_t *fog;
+
+ qprintf("----- FogDrawsurfs -----\n");
+
+ c_fogged = 0;
+ c_fogFragment = 0;
+
+ // find all fog brushes
+ for ( b = entities[0].brushes ; b ; b = b->next ) {
+ if ( !(b->contents & CONTENTS_FOG) ) {
+ continue;
+ }
+
+ if ( numFogs == MAX_MAP_FOGS ) {
+ Error( "MAX_MAP_FOGS" );
+ }
+ fog = &dfogs[numFogs];
+ numFogs++;
+ fog->brushNum = b->outputNumber;
+
+ // find a side with a valid shaderInfo
+ // non-axial fog columns may have bevel planes that need to be skipped
+ for ( i = 0 ; i < b->numsides ; i++ ) {
+ if ( b->sides[i].shaderInfo && (b->sides[i].shaderInfo->contents & CONTENTS_FOG) ) {
+ strcpy( fog->shader, b->sides[i].shaderInfo->shader );
+ break;
+ }
+ }
+ if ( i == b->numsides ) {
+ continue; // shouldn't happen
+ }
+
+ fog->visibleSide = -1;
+
+ // clip each surface into this, but don't clip any of
+ // the resulting fragments to the same brush
+ numBaseDrawSurfs = numMapDrawSurfs;
+ for ( i = 0 ; i < numBaseDrawSurfs ; i++ ) {
+ ds = &mapDrawSurfs[i];
+
+ // bound the drawsurf
+ ClearBounds( mins, maxs );
+ for ( j = 0 ; j < ds->numVerts ; j++ ) {
+ AddPointToBounds( ds->verts[j].xyz, mins, maxs );
+ }
+
+ // check against the fog brush
+ for ( k = 0 ; k < 3 ; k++ ) {
+ if ( mins[k] > b->maxs[k] ) {
+ break;
+ }
+ if ( maxs[k] < b->mins[k] ) {
+ break;
+ }
+ }
+ if ( k < 3 ) {
+ continue; // bboxes don't intersect
+ }
+
+ if ( ds->mapBrush == b ) {
+ int s;
+
+ s = ds->side - b->sides;
+ if ( s <= 6 ) { // not one of the reversed inside faces
+ // this is a visible fog plane
+ if ( fog->visibleSide != -1 ) {
+ _printf( "WARNING: fog brush %i has multiple visible sides\n", b->brushnum );
+ }
+ fog->visibleSide = s;
+ }
+ }
+
+ if ( ds->miscModel ) {
+ // we could write splitting code for trimodels if we wanted to...
+ c_fogged++;
+ ds->fogNum = numFogs - 1;
+ } else if ( ds->patch ) {
+ if ( ChopPatchByBrush( ds, b ) ) {
+ c_fogged++;
+ ds->fogNum = numFogs - 1;
+ }
+ } else {
+ if ( ChopFaceByBrush( ds, b ) ) {
+ c_fogged++;
+ ds->fogNum = numFogs - 1;
+ }
+ }
+ }
+ }
+
+ // split the drawsurfs by the fog brushes
+
+ qprintf( "%5i fogs\n", numFogs );
+ qprintf( "%5i fog polygon fragments\n", c_fogFragment );
+ qprintf( "%5i fog patch fragments\n", c_fogPatchFragments );
+ qprintf( "%5i fogged drawsurfs\n", c_fogged );
+}
diff --git a/q3map/gldraw.c b/q3map/gldraw.c
new file mode 100755
index 0000000..bf2ceba
--- /dev/null
+++ b/q3map/gldraw.c
@@ -0,0 +1,232 @@
+/*
+===========================================================================
+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 <windows.h>
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glaux.h>
+
+#include "qbsp.h"
+
+// can't use the glvertex3fv functions, because the vec3_t fields
+// could be either floats or doubles, depending on DOUBLEVEC_T
+
+qboolean drawflag;
+vec3_t draw_mins, draw_maxs;
+
+
+#define WIN_SIZE 512
+
+void InitWindow (void)
+{
+ auxInitDisplayMode (AUX_SINGLE | AUX_RGB);
+ auxInitPosition (0, 0, WIN_SIZE, WIN_SIZE);
+ auxInitWindow ("qcsg");
+}
+
+void Draw_ClearWindow (void)
+{
+ static int init;
+ int w, h, g;
+ vec_t mx, my;
+
+ if (!drawflag)
+ return;
+
+ if (!init)
+ {
+ init = qtrue;
+ InitWindow ();
+ }
+
+ glClearColor (1,0.8,0.8,0);
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ w = (draw_maxs[0] - draw_mins[0]);
+ h = (draw_maxs[1] - draw_mins[1]);
+
+ mx = draw_mins[0] + w/2;
+ my = draw_mins[1] + h/2;
+
+ g = w > h ? w : h;
+
+ glLoadIdentity ();
+ gluPerspective (90, 1, 2, 16384);
+ gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
+
+ glColor3f (0,0,0);
+// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+ glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+ glDisable (GL_DEPTH_TEST);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+#if 0
+ glColor4f (1,0,0,0.5);
+ glBegin (GL_POLYGON);
+
+ glVertex3f (0, 500, 0);
+ glVertex3f (0, 900, 0);
+ glVertex3f (0, 900, 100);
+ glVertex3f (0, 500, 100);
+
+ glEnd ();
+#endif
+
+ glFlush ();
+
+}
+
+void Draw_SetRed (void)
+{
+ if (!drawflag)
+ return;
+
+ glColor3f (1,0,0);
+}
+
+void Draw_SetGrey (void)
+{
+ if (!drawflag)
+ return;
+
+ glColor3f (0.5,0.5,0.5);
+}
+
+void Draw_SetBlack (void)
+{
+ if (!drawflag)
+ return;
+
+ glColor3f (0,0,0);
+}
+
+void DrawWinding (winding_t *w)
+{
+ int i;
+
+ if (!drawflag)
+ return;
+
+ glColor4f (0,0,0,0.5);
+ glBegin (GL_LINE_LOOP);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+ glEnd ();
+
+ glColor4f (0,1,0,0.3);
+ glBegin (GL_POLYGON);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+ glEnd ();
+
+ glFlush ();
+}
+
+void DrawAuxWinding (winding_t *w)
+{
+ int i;
+
+ if (!drawflag)
+ return;
+
+ glColor4f (0,0,0,0.5);
+ glBegin (GL_LINE_LOOP);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+ glEnd ();
+
+ glColor4f (1,0,0,0.3);
+ glBegin (GL_POLYGON);
+ for (i=0 ; i<w->numpoints ; i++)
+ glVertex3f (w->p[i][0],w->p[i][1],w->p[i][2] );
+ glEnd ();
+
+ glFlush ();
+}
+
+//============================================================
+
+#define GLSERV_PORT 25001
+
+qboolean wins_init;
+int draw_socket;
+
+void GLS_BeginScene (void)
+{
+ WSADATA winsockdata;
+ WORD wVersionRequested;
+ struct sockaddr_in address;
+ int r;
+
+ if (!wins_init)
+ {
+ wins_init = qtrue;
+
+ wVersionRequested = MAKEWORD(1, 1);
+
+ r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
+
+ if (r)
+ Error ("Winsock initialization failed.");
+
+ }
+
+ // connect a socket to the server
+
+ draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (draw_socket == -1)
+ Error ("draw_socket failed");
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ address.sin_port = GLSERV_PORT;
+ r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
+ if (r == -1)
+ {
+ closesocket (draw_socket);
+ draw_socket = 0;
+ }
+}
+
+void GLS_Winding (winding_t *w, int code)
+{
+ byte buf[1024];
+ int i, j;
+
+ if (!draw_socket)
+ return;
+
+ ((int *)buf)[0] = w->numpoints;
+ ((int *)buf)[1] = code;
+ for (i=0 ; i<w->numpoints ; i++)
+ for (j=0 ; j<3 ; j++)
+ ((float *)buf)[2+i*3+j] = w->p[i][j];
+
+ send (draw_socket, buf, w->numpoints*12+8, 0);
+}
+
+void GLS_EndScene (void)
+{
+ closesocket (draw_socket);
+ draw_socket = 0;
+}
diff --git a/q3map/glfile.c b/q3map/glfile.c
new file mode 100755
index 0000000..8bb10c6
--- /dev/null
+++ b/q3map/glfile.c
@@ -0,0 +1,148 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+int c_glfaces;
+
+int PortalVisibleSides (portal_t *p)
+{
+ int fcon, bcon;
+
+ if (!p->onnode)
+ return 0; // outside
+
+ fcon = p->nodes[0]->opaque;
+ bcon = p->nodes[1]->opaque;
+
+ // same contents never create a face
+ if (fcon == bcon)
+ return 0;
+
+ if (!fcon)
+ return 1;
+ if (!bcon)
+ return 2;
+ return 0;
+}
+
+void OutputWinding (winding_t *w, FILE *glview)
+{
+ static int level = 128;
+ vec_t light;
+ int i;
+
+ fprintf (glview, "%i\n", w->numpoints);
+ level+=28;
+ light = (level&255)/255.0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (glview, "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
+ w->p[i][0],
+ w->p[i][1],
+ w->p[i][2],
+ light,
+ light,
+ light);
+ }
+ fprintf (glview, "\n");
+}
+
+/*
+=============
+OutputPortal
+=============
+*/
+void OutputPortal (portal_t *p, FILE *glview)
+{
+ winding_t *w;
+ int sides;
+
+ sides = PortalVisibleSides (p);
+ if (!sides)
+ return;
+
+ c_glfaces++;
+
+ w = p->winding;
+
+ if (sides == 2) // back side
+ w = ReverseWinding (w);
+
+ OutputWinding (w, glview);
+
+ if (sides == 2)
+ FreeWinding(w);
+}
+
+/*
+=============
+WriteGLView_r
+=============
+*/
+void WriteGLView_r (node_t *node, FILE *glview)
+{
+ portal_t *p, *nextp;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ WriteGLView_r (node->children[0], glview);
+ WriteGLView_r (node->children[1], glview);
+ return;
+ }
+
+ // write all the portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ if (p->nodes[0] == node)
+ {
+ OutputPortal (p, glview);
+ nextp = p->next[0];
+ }
+ else
+ nextp = p->next[1];
+ }
+}
+
+/*
+=============
+WriteGLView
+=============
+*/
+void WriteGLView (tree_t *tree, char *source)
+{
+ char name[1024];
+ FILE *glview;
+
+ c_glfaces = 0;
+ sprintf (name, "%s%s.gl",outbase, source);
+ _printf ("Writing %s\n", name);
+
+ glview = fopen (name, "w");
+ if (!glview)
+ Error ("Couldn't open %s", name);
+ WriteGLView_r (tree->headnode, glview);
+ fclose (glview);
+
+ _printf ("%5i c_glfaces\n", c_glfaces);
+}
+
diff --git a/q3map/leakfile.c b/q3map/leakfile.c
new file mode 100755
index 0000000..101e32d
--- /dev/null
+++ b/q3map/leakfile.c
@@ -0,0 +1,100 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+/*
+==============================================================================
+
+LEAF FILE GENERATION
+
+Save out name.line for qe3 to read
+==============================================================================
+*/
+
+
+/*
+=============
+LeakFile
+
+Finds the shortest possible chain of portals
+that leads from the outside leaf to a specifically
+occupied leaf
+=============
+*/
+void LeakFile (tree_t *tree)
+{
+ vec3_t mid;
+ FILE *linefile;
+ char filename[1024];
+ node_t *node;
+ int count;
+
+ if (!tree->outside_node.occupied)
+ return;
+
+ qprintf ("--- LeakFile ---\n");
+
+ //
+ // write the points to the file
+ //
+ sprintf (filename, "%s.lin", source);
+ linefile = fopen (filename, "w");
+ if (!linefile)
+ Error ("Couldn't open %s\n", filename);
+
+ count = 0;
+ node = &tree->outside_node;
+ while (node->occupied > 1)
+ {
+ int next;
+ portal_t *p, *nextportal;
+ node_t *nextnode;
+ int s;
+
+ // find the best portal exit
+ next = node->occupied;
+ for (p=node->portals ; p ; p = p->next[!s])
+ {
+ s = (p->nodes[0] == node);
+ if (p->nodes[s]->occupied
+ && p->nodes[s]->occupied < next)
+ {
+ nextportal = p;
+ nextnode = p->nodes[s];
+ next = nextnode->occupied;
+ }
+ }
+ node = nextnode;
+ WindingCenter (nextportal->winding, mid);
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ count++;
+ }
+ // add the occupant center
+ GetVectorForKey (node->occupant, "origin", mid);
+
+ fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
+ qprintf ("%5i point linefile\n", count+1);
+
+ fclose (linefile);
+}
+
diff --git a/q3map/light.c b/q3map/light.c
new file mode 100755
index 0000000..a3d368b
--- /dev/null
+++ b/q3map/light.c
@@ -0,0 +1,2149 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+// light.c
+
+#include "light.h"
+#ifdef _WIN32
+#ifdef _TTIMOBUILD
+#include "pakstuff.h"
+#else
+#include "../libs/pakstuff.h"
+#endif
+#endif
+
+
+#define EXTRASCALE 2
+
+typedef struct {
+ float plane[4];
+ vec3_t origin;
+ vec3_t vectors[2];
+ shaderInfo_t *si;
+} filter_t;
+
+#define MAX_FILTERS 1024
+filter_t filters[MAX_FILTERS];
+int numFilters;
+
+extern char source[1024];
+
+qboolean notrace;
+qboolean patchshadows;
+qboolean dump;
+qboolean extra;
+qboolean extraWide;
+qboolean lightmapBorder;
+
+qboolean noSurfaces;
+
+int samplesize = 16; //sample size in units
+int novertexlighting = 0;
+int nogridlighting = 0;
+
+// for run time tweaking of all area sources in the level
+float areaScale = 0.25;
+
+// for run time tweaking of all point sources in the level
+float pointScale = 7500;
+
+qboolean exactPointToPolygon = qtrue;
+
+float formFactorValueScale = 3;
+
+float linearScale = 1.0 / 8000;
+
+light_t *lights;
+int numPointLights;
+int numAreaLights;
+
+FILE *dumpFile;
+
+int c_visible, c_occluded;
+
+//int defaultLightSubdivide = 128; // vary by surface size?
+int defaultLightSubdivide = 999; // vary by surface size?
+
+vec3_t ambientColor;
+
+vec3_t surfaceOrigin[ MAX_MAP_DRAW_SURFS ];
+int entitySurface[ MAX_MAP_DRAW_SURFS ];
+
+// 7,9,11 normalized to avoid being nearly coplanar with common faces
+//vec3_t sunDirection = { 0.441835, 0.56807, 0.694313 };
+//vec3_t sunDirection = { 0.45, 0, 0.9 };
+//vec3_t sunDirection = { 0, 0, 1 };
+
+// these are usually overrided by shader values
+vec3_t sunDirection = { 0.45, 0.3, 0.9 };
+vec3_t sunLight = { 100, 100, 50 };
+
+
+
+typedef struct {
+ dbrush_t *b;
+ vec3_t bounds[2];
+} skyBrush_t;
+
+int numSkyBrushes;
+skyBrush_t skyBrushes[MAX_MAP_BRUSHES];
+
+
+/*
+
+the corners of a patch mesh will always be exactly at lightmap samples.
+The dimensions of the lightmap will be equal to the average length of the control
+mesh in each dimension divided by 2.
+The lightmap sample points should correspond to the chosen subdivision points.
+
+*/
+
+/*
+===============================================================
+
+SURFACE LOADING
+
+===============================================================
+*/
+
+#define MAX_FACE_POINTS 128
+
+/*
+===============
+SubdivideAreaLight
+
+Subdivide area lights that are very large
+A light that is subdivided will never backsplash, avoiding weird pools of light near edges
+===============
+*/
+void SubdivideAreaLight( shaderInfo_t *ls, winding_t *w, vec3_t normal,
+ float areaSubdivide, qboolean backsplash ) {
+ float area, value, intensity;
+ light_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 );
+ SubdivideAreaLight( ls, front, normal, areaSubdivide, qfalse );
+ SubdivideAreaLight( ls, back, normal, areaSubdivide, qfalse );
+ FreeWinding( w );
+ return;
+ }
+ }
+
+ // create a light from this
+ area = WindingArea (w);
+ if ( area <= 0 || area > 20000000 ) {
+ return;
+ }
+
+ numAreaLights++;
+ dl = malloc(sizeof(*dl));
+ memset (dl, 0, sizeof(*dl));
+ dl->next = lights;
+ lights = dl;
+ dl->type = emit_area;
+
+ WindingCenter( w, dl->origin );
+ dl->w = w;
+ VectorCopy ( normal, dl->normal);
+ dl->dist = DotProduct( dl->origin, normal );
+
+ value = ls->value;
+ intensity = value * area * areaScale;
+ 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*formFactorValueScale*areaScale, dl->emitColor );
+
+ dl->si = ls;
+
+ if ( ls->contents & CONTENTS_FOG ) {
+ dl->twosided = qtrue;
+ }
+
+ // optionally create a point backsplash light
+ if ( backsplash && ls->backsplashFraction > 0 ) {
+ dl2 = malloc(sizeof(*dl));
+ memset (dl2, 0, sizeof(*dl2));
+ dl2->next = lights;
+ lights = dl2;
+ dl2->type = emit_point;
+
+ VectorMA( dl->origin, ls->backsplashDistance, normal, dl2->origin );
+
+ VectorCopy( ls->color, dl2->color );
+
+ dl2->photons = dl->photons * ls->backsplashFraction;
+ dl2->si = ls;
+ }
+}
+
+
+/*
+===============
+CountLightmaps
+===============
+*/
+void CountLightmaps( void ) {
+ int count;
+ int i;
+ dsurface_t *ds;
+
+ qprintf ("--- CountLightmaps ---\n");
+ count = 0;
+ for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
+ // see if this surface is light emiting
+ ds = &drawSurfaces[i];
+ if ( ds->lightmapNum > count ) {
+ count = ds->lightmapNum;
+ }
+ }
+
+ count++;
+ numLightBytes = count * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3;
+ if ( numLightBytes > MAX_MAP_LIGHTING ) {
+ Error("MAX_MAP_LIGHTING exceeded");
+ }
+
+ qprintf( "%5i drawSurfaces\n", numDrawSurfaces );
+ qprintf( "%5i lightmaps\n", count );
+}
+
+/*
+===============
+CreateSurfaceLights
+
+This creates area lights
+===============
+*/
+void CreateSurfaceLights( void ) {
+ int i, j, side;
+ dsurface_t *ds;
+ shaderInfo_t *ls;
+ winding_t *w;
+ cFacet_t *f;
+ light_t *dl;
+ vec3_t origin;
+ drawVert_t *dv;
+ int c_lightSurfaces;
+ float lightSubdivide;
+ vec3_t normal;
+
+ qprintf ("--- CreateSurfaceLights ---\n");
+ c_lightSurfaces = 0;
+
+ 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 = defaultLightSubdivide;
+ }
+
+ c_lightSurfaces++;
+
+ // an autosprite shader will become
+ // a point light instead of an area light
+ if ( ls->autosprite ) {
+ // autosprite geometry should only have four vertexes
+ if ( surfaceTest[i] ) {
+ // curve or misc_model
+ f = surfaceTest[i]->facets;
+ if ( surfaceTest[i]->numFacets != 1 || f->numBoundaries != 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 );
+ }
+
+
+ numPointLights++;
+ dl = malloc(sizeof(*dl));
+ memset (dl, 0, sizeof(*dl));
+ dl->next = lights;
+ lights = dl;
+
+ VectorCopy( origin, dl->origin );
+ VectorCopy( ls->color, dl->color );
+ dl->photons = ls->value * pointScale;
+ dl->type = emit_point;
+ continue;
+ }
+
+ // possibly create for both sides of the polygon
+ for ( side = 0 ; side <= ls->twoSided ; side++ ) {
+ // create area lights
+ if ( surfaceTest[i] ) {
+ // curve or misc_model
+ for ( j = 0 ; j < surfaceTest[i]->numFacets ; j++ ) {
+ f = surfaceTest[i]->facets + j;
+ w = AllocWinding( f->numBoundaries );
+ w->numpoints = f->numBoundaries;
+ memcpy( w->p, f->points, f->numBoundaries * 12 );
+
+ VectorCopy( f->surface, normal );
+ if ( side ) {
+ winding_t *t;
+
+ t = w;
+ w = ReverseWinding( t );
+ FreeWinding( t );
+ VectorSubtract( vec3_origin, normal, normal );
+ }
+ 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->p[j] );
+ }
+ VectorCopy( ds->lightmapVecs[2], normal );
+ if ( side ) {
+ winding_t *t;
+
+ t = w;
+ w = ReverseWinding( t );
+ FreeWinding( t );
+ VectorSubtract( vec3_origin, normal, normal );
+ }
+ SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
+ }
+ }
+ }
+
+ _printf( "%5i light emitting surfaces\n", c_lightSurfaces );
+}
+
+
+
+/*
+================
+FindSkyBrushes
+================
+*/
+void FindSkyBrushes( void ) {
+ int i, j;
+ dbrush_t *b;
+ skyBrush_t *sb;
+ shaderInfo_t *si;
+ dbrushside_t *s;
+
+ // 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 ) {
+ sb = &skyBrushes[ numSkyBrushes ];
+ sb->b = b;
+ sb->bounds[0][0] = -dplanes[ dbrushsides[ b->firstSide + 0 ].planeNum ].dist - 1;
+ sb->bounds[1][0] = dplanes[ dbrushsides[ b->firstSide + 1 ].planeNum ].dist + 1;
+ sb->bounds[0][1] = -dplanes[ dbrushsides[ b->firstSide + 2 ].planeNum ].dist - 1;
+ sb->bounds[1][1] = dplanes[ dbrushsides[ b->firstSide + 3 ].planeNum ].dist + 1;
+ sb->bounds[0][2] = -dplanes[ dbrushsides[ b->firstSide + 4 ].planeNum ].dist - 1;
+ sb->bounds[1][2] = dplanes[ dbrushsides[ b->firstSide + 5 ].planeNum ].dist + 1;
+ numSkyBrushes++;
+ break;
+ }
+ }
+ }
+
+ // default
+ VectorNormalize( sunDirection, sunDirection );
+
+ // 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, sunLight );
+ VectorCopy( si->sunDirection, sunDirection );
+ break;
+ }
+ }
+}
+
+/*
+=================================================================
+
+ LIGHT SETUP
+
+=================================================================
+*/
+
+/*
+==================
+FindTargetEntity
+==================
+*/
+entity_t *FindTargetEntity( const char *target ) {
+ int i;
+ const char *n;
+
+ for ( i = 0 ; i < num_entities ; i++ ) {
+ n = ValueForKey (&entities[i], "targetname");
+ if ( !strcmp (n, target) ) {
+ return &entities[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/*
+=============
+CreateEntityLights
+=============
+*/
+void CreateEntityLights (void)
+{
+ int i;
+ light_t *dl;
+ entity_t *e, *e2;
+ const char *name;
+ const char *target;
+ vec3_t dest;
+ const char *_color;
+ float intensity;
+ int spawnflags;
+
+ //
+ // entities
+ //
+ for ( i = 0 ; i < num_entities ; i++ ) {
+ e = &entities[i];
+ name = ValueForKey (e, "classname");
+ if (strncmp (name, "light", 5))
+ continue;
+
+ numPointLights++;
+ dl = malloc(sizeof(*dl));
+ memset (dl, 0, sizeof(*dl));
+ dl->next = lights;
+ lights = dl;
+
+ spawnflags = FloatForKey (e, "spawnflags");
+ if ( spawnflags & 1 ) {
+ dl->linearLight = qtrue;
+ }
+
+ 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 * pointScale;
+ dl->photons = intensity;
+
+ dl->type = emit_point;
+
+ // 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 = emit_spotlight;
+ }
+ }
+ }
+}
+
+//=================================================================
+
+/*
+================
+SetEntityOrigins
+
+Find the offset values for inline models
+================
+*/
+void SetEntityOrigins( void ) {
+ int i, j;
+ entity_t *e;
+ vec3_t origin;
+ const char *key;
+ int modelnum;
+ dmodel_t *dm;
+
+ for ( i=0 ; i < num_entities ; i++ ) {
+ e = &entities[i];
+ key = ValueForKey (e, "model");
+ if ( key[0] != '*' ) {
+ continue;
+ }
+ modelnum = atoi( key + 1 );
+ dm = &dmodels[ modelnum ];
+
+ // set entity surface to true for all surfaces for this model
+ for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
+ entitySurface[ dm->firstSurface + j ] = qtrue;
+ }
+
+ key = ValueForKey (e, "origin");
+ if ( !key[0] ) {
+ continue;
+ }
+ GetVectorForKey ( e, "origin", origin );
+
+ // set origin for all surfaces for this model
+ for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
+ VectorCopy( origin, surfaceOrigin[ dm->firstSurface + j ] );
+ }
+ }
+}
+
+
+/*
+=================================================================
+
+
+=================================================================
+*/
+
+#define MAX_POINTS_ON_WINDINGS 64
+
+/*
+================
+PointToPolygonFormFactor
+================
+*/
+float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w ) {
+ vec3_t triVector, triNormal;
+ int i, j;
+ vec3_t dirs[MAX_POINTS_ON_WINDING];
+ float total;
+ float dot, angle, facing;
+
+ for ( i = 0 ; i < w->numpoints ; i++ ) {
+ VectorSubtract( w->p[i], point, dirs[i] );
+ VectorNormalize( dirs[i], dirs[i] );
+ }
+
+ // duplicate first vertex to avoid mod operation
+ VectorCopy( dirs[0], dirs[i] );
+
+ total = 0;
+ for ( i = 0 ; i < w->numpoints ; i++ ) {
+ j = i+1;
+ dot = DotProduct( dirs[i], dirs[j] );
+
+ // roundoff can cause slight creep, which gives an IND from acos
+ if ( dot > 1.0 ) {
+ dot = 1.0;
+ } else if ( dot < -1.0 ) {
+ dot = -1.0;
+ }
+
+ angle = acos( dot );
+ CrossProduct( dirs[i], dirs[j], triVector );
+ if ( VectorNormalize( triVector, triNormal ) < 0.0001 ) {
+ continue;
+ }
+ facing = DotProduct( normal, triNormal );
+ total += facing * angle;
+
+ if ( total > 6.3 || total < -6.3 ) {
+ static qboolean printed;
+
+ if ( !printed ) {
+ printed = qtrue;
+ _printf( "WARNING: bad PointToPolygonFormFactor: %f at %1.1f %1.1f %1.1f from %1.1f %1.1f %1.1f\n", total,
+ w->p[i][0], w->p[i][1], w->p[i][2], point[0], point[1], point[2]);
+ }
+ return 0;
+ }
+
+ }
+
+ total /= 2*3.141592657; // now in the range of 0 to 1 over the entire incoming hemisphere
+
+ return total;
+}
+
+
+/*
+================
+FilterTrace
+
+Returns 0 to 1.0 filter fractions for the given trace
+================
+*/
+void FilterTrace( const vec3_t start, const vec3_t end, vec3_t filter ) {
+ float d1, d2;
+ filter_t *f;
+ int filterNum;
+ vec3_t point;
+ float frac;
+ int i;
+ float s, t;
+ int u, v;
+ int x, y;
+ byte *pixel;
+ float radius;
+ float len;
+ vec3_t total;
+
+ filter[0] = 1.0;
+ filter[1] = 1.0;
+ filter[2] = 1.0;
+
+ for ( filterNum = 0 ; filterNum < numFilters ; filterNum++ ) {
+ f = &filters[ filterNum ];
+
+ // see if the plane is crossed
+ d1 = DotProduct( start, f->plane ) - f->plane[3];
+ d2 = DotProduct( end, f->plane ) - f->plane[3];
+
+ if ( ( d1 < 0 ) == ( d2 < 0 ) ) {
+ continue;
+ }
+
+ // calculate the crossing point
+ frac = d1 / ( d1 - d2 );
+
+ for ( i = 0 ; i < 3 ; i++ ) {
+ point[i] = start[i] + frac * ( end[i] - start[i] );
+ }
+
+ VectorSubtract( point, f->origin, point );
+
+ s = DotProduct( point, f->vectors[0] );
+ t = 1.0 - DotProduct( point, f->vectors[1] );
+ if ( s < 0 || s >= 1.0 || t < 0 || t >= 1.0 ) {
+ continue;
+ }
+
+ // decide the filter size
+ radius = 10 * frac;
+ len = VectorLength( f->vectors[0] );
+ if ( !len ) {
+ continue;
+ }
+ radius = radius * len * f->si->width;
+
+ // look up the filter, taking multiple samples
+ VectorClear( total );
+ for ( u = -1 ; u <= 1 ; u++ ) {
+ for ( v = -1 ; v <=1 ; v++ ) {
+ x = s * f->si->width + u * radius;
+ if ( x < 0 ) {
+ x = 0;
+ }
+ if ( x >= f->si->width ) {
+ x = f->si->width - 1;
+ }
+ y = t * f->si->height + v * radius;
+ if ( y < 0 ) {
+ y = 0;
+ }
+ if ( y >= f->si->height ) {
+ y = f->si->height - 1;
+ }
+
+ pixel = f->si->pixels + ( y * f->si->width + x ) * 4;
+ total[0] += pixel[0];
+ total[1] += pixel[1];
+ total[2] += pixel[2];
+ }
+ }
+
+ filter[0] *= total[0]/(255.0*9);
+ filter[1] *= total[1]/(255.0*9);
+ filter[2] *= total[2]/(255.0*9);
+ }
+
+}
+
+/*
+================
+SunToPoint
+
+Returns an amount of light to add at the point
+================
+*/
+int c_sunHit, c_sunMiss;
+void SunToPoint( const vec3_t origin, traceWork_t *tw, vec3_t addLight ) {
+ int i;
+ trace_t trace;
+ skyBrush_t *b;
+ vec3_t end;
+
+ if ( !numSkyBrushes ) {
+ VectorClear( addLight );
+ return;
+ }
+
+ VectorMA( origin, MAX_WORLD_COORD * 2, sunDirection, end );
+
+ TraceLine( origin, end, &trace, qtrue, tw );
+
+ // see if trace.hit is inside a sky brush
+ for ( i = 0 ; i < numSkyBrushes ; i++) {
+ b = &skyBrushes[ i ];
+
+ // this assumes that sky brushes are axial...
+ if ( trace.hit[0] < b->bounds[0][0]
+ || trace.hit[0] > b->bounds[1][0]
+ || trace.hit[1] < b->bounds[0][1]
+ || trace.hit[1] > b->bounds[1][1]
+ || trace.hit[2] < b->bounds[0][2]
+ || trace.hit[2] > b->bounds[1][2] ) {
+ continue;
+ }
+
+
+ // trace again to get intermediate filters
+ TraceLine( origin, trace.hit, &trace, qtrue, tw );
+
+ // we hit the sky, so add sunlight
+ if ( numthreads == 1 ) {
+ c_sunHit++;
+ }
+ addLight[0] = trace.filter[0] * sunLight[0];
+ addLight[1] = trace.filter[1] * sunLight[1];
+ addLight[2] = trace.filter[2] * sunLight[2];
+
+ return;
+ }
+
+ if ( numthreads == 1 ) {
+ c_sunMiss++;
+ }
+
+ VectorClear( addLight );
+}
+
+/*
+================
+SunToPlane
+================
+*/
+void SunToPlane( const vec3_t origin, const vec3_t normal, vec3_t color, traceWork_t *tw ) {
+ float angle;
+ vec3_t sunColor;
+
+ if ( !numSkyBrushes ) {
+ return;
+ }
+
+ angle = DotProduct( normal, sunDirection );
+ if ( angle <= 0 ) {
+ return; // facing away
+ }
+
+ SunToPoint( origin, tw, sunColor );
+ VectorMA( color, angle, sunColor, color );
+}
+
+/*
+================
+LightingAtSample
+================
+*/
+void LightingAtSample( vec3_t origin, vec3_t normal, vec3_t color,
+ qboolean testOcclusion, qboolean forceSunLight, traceWork_t *tw ) {
+ light_t *light;
+ trace_t trace;
+ float angle;
+ float add;
+ float dist;
+ vec3_t dir;
+
+ VectorCopy( ambientColor, color );
+
+ // trace to all the lights
+ for ( light = lights ; light ; light = light->next ) {
+
+ //MrE: if the light is behind the surface
+ if ( DotProduct(light->origin, normal) - DotProduct(normal, origin) < 0 )
+ continue;
+ // testing exact PTPFF
+ if ( exactPointToPolygon && light->type == emit_area ) {
+ float factor;
+ float d;
+ vec3_t pushedOrigin;
+
+ // see if the point is behind the light
+ d = DotProduct( origin, light->normal ) - light->dist;
+ if ( !light->twosided ) {
+ if ( d < -1 ) {
+ continue; // point is behind light
+ }
+ }
+
+ // test occlusion and find light filters
+ // clip the line, tracing from the surface towards the light
+ if ( !notrace && testOcclusion ) {
+ TraceLine( origin, light->origin, &trace, qfalse, tw );
+
+ // other light rays must not hit anything
+ if ( trace.passSolid ) {
+ continue;
+ }
+ } else {
+ trace.filter[0] = 1.0;
+ trace.filter[1] = 1.0;
+ trace.filter[2] = 1.0;
+ }
+
+ // nudge the point so that it is clearly forward of the light
+ // so that surfaces meeting a light emiter don't get black edges
+ if ( d > -8 && d < 8 ) {
+ VectorMA( origin, (8-d), light->normal, pushedOrigin );
+ } else {
+ VectorCopy( origin, pushedOrigin );
+ }
+
+ // calculate the contribution
+ factor = PointToPolygonFormFactor( pushedOrigin, normal, light->w );
+ if ( factor <= 0 ) {
+ if ( light->twosided ) {
+ factor = -factor;
+ } else {
+ continue;
+ }
+ }
+ color[0] += factor * light->emitColor[0] * trace.filter[0];
+ color[1] += factor * light->emitColor[1] * trace.filter[1];
+ color[2] += factor * light->emitColor[2] * trace.filter[2];
+
+ continue;
+ }
+
+ // calculate the amount of light at this sample
+ if ( light->type == emit_point ) {
+ VectorSubtract( light->origin, origin, dir );
+ dist = VectorNormalize( dir, dir );
+ // clamp the distance to prevent super hot spots
+ if ( dist < 16 ) {
+ dist = 16;
+ }
+ angle = DotProduct( normal, dir );
+ if ( light->linearLight ) {
+ add = angle * light->photons * linearScale - dist;
+ if ( add < 0 ) {
+ add = 0;
+ }
+ } else {
+ add = light->photons / ( dist * dist ) * angle;
+ }
+ } else if ( light->type == emit_spotlight ) {
+ float distByNormal;
+ vec3_t pointAtDist;
+ float radiusAtDist;
+ float sampleRadius;
+ vec3_t distToSample;
+ float coneScale;
+
+ VectorSubtract( light->origin, origin, dir );
+
+ distByNormal = -DotProduct( dir, light->normal );
+ if ( distByNormal < 0 ) {
+ continue;
+ }
+ VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
+ radiusAtDist = light->radiusByDist * distByNormal;
+
+ VectorSubtract( origin, 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 );
+ add = light->photons / ( dist * dist ) * angle * coneScale;
+
+ } else if ( light->type == emit_area ) {
+ VectorSubtract( light->origin, origin, dir );
+ dist = VectorNormalize( dir, dir );
+ // clamp the distance to prevent super hot spots
+ if ( dist < 16 ) {
+ dist = 16;
+ }
+ angle = DotProduct( normal, dir );
+ if ( angle <= 0 ) {
+ continue;
+ }
+ angle *= -DotProduct( light->normal, dir );
+ if ( angle <= 0 ) {
+ continue;
+ }
+
+ if ( light->linearLight ) {
+ add = angle * light->photons * linearScale - dist;
+ if ( add < 0 ) {
+ add = 0;
+ }
+ } else {
+ add = light->photons / ( dist * dist ) * angle;
+ }
+ }
+
+ if ( add <= 1.0 ) {
+ continue;
+ }
+
+ // clip the line, tracing from the surface towards the light
+ if ( !notrace && testOcclusion ) {
+ TraceLine( origin, light->origin, &trace, qfalse, tw );
+
+ // other light rays must not hit anything
+ if ( trace.passSolid ) {
+ continue;
+ }
+ } else {
+ trace.filter[0] = 1;
+ trace.filter[1] = 1;
+ trace.filter[2] = 1;
+ }
+
+ // add the result
+ color[0] += add * light->color[0] * trace.filter[0];
+ color[1] += add * light->color[1] * trace.filter[1];
+ color[2] += add * light->color[2] * trace.filter[2];
+ }
+
+ //
+ // trace directly to the sun
+ //
+ if ( testOcclusion || forceSunLight ) {
+ SunToPlane( origin, normal, color, tw );
+ }
+}
+
+/*
+=============
+PrintOccluded
+
+For debugging
+=============
+*/
+void PrintOccluded( byte occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE],
+ int width, int height ) {
+ int i, j;
+
+ _printf( "\n" );
+
+ for ( i = 0 ; i < height ; i++ ) {
+ for ( j = 0 ; j < width ; j++ ) {
+ _printf("%i", (int)occluded[j][i] );
+ }
+ _printf( "\n" );
+ }
+}
+
+
+/*
+=============
+VertexLighting
+
+Vertex lighting will completely ignore occlusion, because
+shadows would not be resolvable anyway.
+=============
+*/
+void VertexLighting( dsurface_t *ds, qboolean testOcclusion, qboolean forceSunLight, float scale, traceWork_t *tw ) {
+ int i, j;
+ drawVert_t *dv;
+ vec3_t sample, normal;
+ float max;
+
+ VectorCopy( ds->lightmapVecs[2], normal );
+
+ // generate vertex lighting
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ dv = &drawVerts[ ds->firstVert + i ];
+
+ if ( ds->patchWidth ) {
+ LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
+ }
+ else if (ds->surfaceType == MST_TRIANGLE_SOUP) {
+ LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
+ }
+ else {
+ LightingAtSample( dv->xyz, normal, sample, testOcclusion, forceSunLight, tw );
+ }
+
+ if (scale >= 0)
+ VectorScale(sample, scale, sample);
+ // clamp with color normalization
+ max = sample[0];
+ if ( sample[1] > max ) {
+ max = sample[1];
+ }
+ if ( sample[2] > max ) {
+ max = sample[2];
+ }
+ if ( max > 255 ) {
+ VectorScale( sample, 255/max, sample );
+ }
+
+ // save the sample
+ for ( j = 0 ; j < 3 ; j++ ) {
+ if ( sample[j] > 255 ) {
+ sample[j] = 255;
+ }
+ dv->color[j] = sample[j];
+ }
+
+ // Don't bother writing alpha since it will already be set to 255,
+ // plus we don't want to write over alpha generated by SetTerrainTextures
+ //dv->color[3] = 255;
+ }
+}
+
+
+/*
+=================
+LinearSubdivideMesh
+
+For extra lighting, just midpoint one of the axis.
+The edges are clamped at the original edges.
+=================
+*/
+mesh_t *LinearSubdivideMesh( mesh_t *in ) {
+ int i, j;
+ mesh_t *out;
+ drawVert_t *v1, *v2, *vout;
+
+ out = malloc( sizeof( *out ) );
+
+ out->width = in->width * 2;
+ out->height = in->height;
+ out->verts = malloc( out->width * out->height * sizeof(*out->verts) );
+ for ( j = 0 ; j < in->height ; j++ ) {
+ out->verts[ j * out->width + 0 ] = in->verts[ j * in->width + 0 ];
+ out->verts[ j * out->width + out->width - 1 ] = in->verts[ j * in->width + in->width - 1 ];
+ for ( i = 1 ; i < out->width - 1 ; i+= 2 ) {
+ v1 = in->verts + j * in->width + (i >> 1);
+ v2 = v1 + 1;
+ vout = out->verts + j * out->width + i;
+
+ vout->xyz[0] = 0.75 * v1->xyz[0] + 0.25 * v2->xyz[0];
+ vout->xyz[1] = 0.75 * v1->xyz[1] + 0.25 * v2->xyz[1];
+ vout->xyz[2] = 0.75 * v1->xyz[2] + 0.25 * v2->xyz[2];
+
+ vout->normal[0] = 0.75 * v1->normal[0] + 0.25 * v2->normal[0];
+ vout->normal[1] = 0.75 * v1->normal[1] + 0.25 * v2->normal[1];
+ vout->normal[2] = 0.75 * v1->normal[2] + 0.25 * v2->normal[2];
+
+ VectorNormalize( vout->normal, vout->normal );
+
+ vout++;
+
+ vout->xyz[0] = 0.25 * v1->xyz[0] + 0.75 * v2->xyz[0];
+ vout->xyz[1] = 0.25 * v1->xyz[1] + 0.75 * v2->xyz[1];
+ vout->xyz[2] = 0.25 * v1->xyz[2] + 0.75 * v2->xyz[2];
+
+ vout->normal[0] = 0.25 * v1->normal[0] + 0.75 * v2->normal[0];
+ vout->normal[1] = 0.25 * v1->normal[1] + 0.75 * v2->normal[1];
+ vout->normal[2] = 0.25 * v1->normal[2] + 0.75 * v2->normal[2];
+
+ VectorNormalize( vout->normal, vout->normal );
+
+ }
+ }
+
+ FreeMesh( in );
+
+ return out;
+}
+
+/*
+==============
+ColorToBytes
+==============
+*/
+void ColorToBytes( const float *color, byte *colorBytes ) {
+ float max;
+ vec3_t sample;
+
+ VectorCopy( color, sample );
+
+ // clamp with color normalization
+ max = sample[0];
+ if ( sample[1] > max ) {
+ max = sample[1];
+ }
+ if ( sample[2] > max ) {
+ max = sample[2];
+ }
+ if ( max > 255 ) {
+ VectorScale( sample, 255/max, sample );
+ }
+ colorBytes[ 0 ] = sample[0];
+ colorBytes[ 1 ] = sample[1];
+ colorBytes[ 2 ] = sample[2];
+}
+
+
+
+/*
+=============
+TraceLtm
+=============
+*/
+void TraceLtm( int num ) {
+ dsurface_t *ds;
+ int i, j, k;
+ int x, y;
+ int position, numPositions;
+ vec3_t base, origin, normal;
+ byte occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE];
+ vec3_t color[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE];
+ traceWork_t tw;
+ vec3_t average;
+ int count;
+ mesh_t srcMesh, *mesh, *subdivided;
+ shaderInfo_t *si;
+ static float nudge[2][9] = {
+ { 0, -1, 0, 1, -1, 1, -1, 0, 1 },
+ { 0, -1, -1, -1, 0, 0, 1, 1, 1 }
+ };
+ int sampleWidth, sampleHeight, ssize;
+ vec3_t lightmapOrigin, lightmapVecs[2];
+ int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_WIDTH];
+
+ ds = &drawSurfaces[num];
+ si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
+
+ // vertex-lit triangle model
+ if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
+ VertexLighting( ds, !si->noVertexShadows, si->forceSunLight, 1.0, &tw );
+ return;
+ }
+
+ if ( ds->lightmapNum == -1 ) {
+ return; // doesn't need lighting at all
+ }
+
+ if (!novertexlighting) {
+ // calculate the vertex lighting for gouraud shade mode
+ VertexLighting( ds, si->vertexShadows, si->forceSunLight, si->vertexScale, &tw );
+ }
+
+ if ( ds->lightmapNum < 0 ) {
+ return; // doesn't need lightmap lighting
+ }
+
+ si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
+ ssize = samplesize;
+ if (si->lightmapSampleSize)
+ ssize = si->lightmapSampleSize;
+
+ if (si->patchShadows)
+ tw.patchshadows = qtrue;
+ else
+ tw.patchshadows = patchshadows;
+
+ if ( ds->surfaceType == MST_PATCH ) {
+ srcMesh.width = ds->patchWidth;
+ srcMesh.height = ds->patchHeight;
+ srcMesh.verts = drawVerts + ds->firstVert;
+ mesh = SubdivideMesh( srcMesh, 8, 999 );
+ PutMeshOnCurve( *mesh );
+ MakeMeshNormals( *mesh );
+
+ subdivided = RemoveLinearMeshColumnsRows( mesh );
+ FreeMesh(mesh);
+
+ mesh = SubdivideMeshQuads( subdivided, ssize, LIGHTMAP_WIDTH, widthtable, heighttable);
+ if ( mesh->width != ds->lightmapWidth || mesh->height != ds->lightmapHeight ) {
+ Error( "Mesh lightmap miscount");
+ }
+
+ if ( extra ) {
+ mesh_t *mp;
+
+ // chop it up for more light samples (leaking memory...)
+ mp = mesh;//CopyMesh( mesh );
+ mp = LinearSubdivideMesh( mp );
+ mp = TransposeMesh( mp );
+ mp = LinearSubdivideMesh( mp );
+ mp = TransposeMesh( mp );
+
+ mesh = mp;
+ }
+ } else {
+ VectorCopy( ds->lightmapVecs[2], normal );
+
+ if ( !extra ) {
+ VectorCopy( ds->lightmapOrigin, lightmapOrigin );
+ VectorCopy( ds->lightmapVecs[0], lightmapVecs[0] );
+ VectorCopy( ds->lightmapVecs[1], lightmapVecs[1] );
+ } else {
+ // sample at a closer spacing for antialiasing
+ VectorCopy( ds->lightmapOrigin, lightmapOrigin );
+ VectorScale( ds->lightmapVecs[0], 0.5, lightmapVecs[0] );
+ VectorScale( ds->lightmapVecs[1], 0.5, lightmapVecs[1] );
+ VectorMA( lightmapOrigin, -0.5, lightmapVecs[0], lightmapOrigin );
+ VectorMA( lightmapOrigin, -0.5, lightmapVecs[1], lightmapOrigin );
+ }
+ }
+
+ if ( extra ) {
+ sampleWidth = ds->lightmapWidth * 2;
+ sampleHeight = ds->lightmapHeight * 2;
+ } else {
+ sampleWidth = ds->lightmapWidth;
+ sampleHeight = ds->lightmapHeight;
+ }
+
+ memset ( color, 0, sizeof( color ) );
+
+ // determine which samples are occluded
+ memset ( occluded, 0, sizeof( occluded ) );
+ 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]
+ + i * lightmapVecs[0][k]
+ + j * lightmapVecs[1][k];
+ }
+ }
+ VectorAdd( base, surfaceOrigin[ num ], base );
+
+ // we may need to slightly nudge the sample point
+ // if directly on a wall
+ for ( position = 0 ; position < numPositions ; position++ ) {
+ // calculate lightmap sample position
+ for ( k = 0 ; k < 3 ; k++ ) {
+ origin[k] = base[k] +
+ + ( nudge[0][position]/16 ) * lightmapVecs[0][k]
+ + ( nudge[1][position]/16 ) * lightmapVecs[1][k];
+ }
+
+ if ( notrace ) {
+ break;
+ }
+ if ( !PointInSolid( origin ) ) {
+ break;
+ }
+ }
+
+ // if none of the nudges worked, this sample is occluded
+ if ( position == numPositions ) {
+ occluded[i][j] = qtrue;
+ if ( numthreads == 1 ) {
+ c_occluded++;
+ }
+ continue;
+ }
+
+ if ( numthreads == 1 ) {
+ c_visible++;
+ }
+ occluded[i][j] = qfalse;
+ LightingAtSample( origin, normal, color[i][j], qtrue, qfalse, &tw );
+ }
+ }
+
+ if ( dump ) {
+ PrintOccluded( occluded, sampleWidth, sampleHeight );
+ }
+
+ // calculate average values for occluded samples
+ for ( i = 0 ; i < sampleWidth ; i++ ) {
+ for ( j = 0 ; j < sampleHeight ; j++ ) {
+ if ( !occluded[i][j] ) {
+ continue;
+ }
+ // scan all surrounding samples
+ count = 0;
+ VectorClear( average );
+ for ( x = -1 ; x <= 1; x++ ) {
+ for ( y = -1 ; y <= 1 ; y++ ) {
+ if ( i + x < 0 || i + x >= sampleWidth ) {
+ continue;
+ }
+ if ( j + y < 0 || j + y >= sampleHeight ) {
+ continue;
+ }
+ if ( occluded[i+x][j+y] ) {
+ continue;
+ }
+ count++;
+ VectorAdd( color[i+x][j+y], average, average );
+ }
+ }
+ if ( count ) {
+ VectorScale( average, 1.0/count, color[i][j] );
+ }
+ }
+ }
+
+ // average together the values if we are extra sampling
+ if ( ds->lightmapWidth != sampleWidth ) {
+ for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
+ for ( j = 0 ; j < ds->lightmapHeight ; j++ ) {
+ for ( k = 0 ; k < 3 ; k++ ) {
+ float value, coverage;
+
+ value = color[i*2][j*2][k] + color[i*2][j*2+1][k] +
+ color[i*2+1][j*2][k] + color[i*2+1][j*2+1][k];
+ coverage = 4;
+ if ( extraWide ) {
+ // wider than box filter
+ if ( i > 0 ) {
+ value += color[i*2-1][j*2][k] + color[i*2-1][j*2+1][k];
+ value += color[i*2-2][j*2][k] + color[i*2-2][j*2+1][k];
+ coverage += 4;
+ }
+ if ( i < ds->lightmapWidth - 1 ) {
+ value += color[i*2+2][j*2][k] + color[i*2+2][j*2+1][k];
+ value += color[i*2+3][j*2][k] + color[i*2+3][j*2+1][k];
+ coverage += 4;
+ }
+ if ( j > 0 ) {
+ value += color[i*2][j*2-1][k] + color[i*2+1][j*2-1][k];
+ value += color[i*2][j*2-2][k] + color[i*2+1][j*2-2][k];
+ coverage += 4;
+ }
+ if ( j < ds->lightmapHeight - 1 ) {
+ value += color[i*2][j*2+2][k] + color[i*2+1][j*2+2][k];
+ value += color[i*2][j*2+3][k] + color[i*2+1][j*2+3][k];
+ coverage += 2;
+ }
+ }
+
+ color[i][j][k] = value / coverage;
+ }
+ }
+ }
+ }
+
+ // optionally create a debugging border around the lightmap
+ if ( lightmapBorder ) {
+ for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
+ color[i][0][0] = 255;
+ color[i][0][1] = 0;
+ color[i][0][2] = 0;
+
+ color[i][ds->lightmapHeight-1][0] = 255;
+ color[i][ds->lightmapHeight-1][1] = 0;
+ color[i][ds->lightmapHeight-1][2] = 0;
+ }
+ for ( i = 0 ; i < ds->lightmapHeight ; i++ ) {
+ color[0][i][0] = 255;
+ color[0][i][1] = 0;
+ color[0][i][2] = 0;
+
+ color[ds->lightmapWidth-1][i][0] = 255;
+ color[ds->lightmapWidth-1][i][1] = 0;
+ color[ds->lightmapWidth-1][i][2] = 0;
+ }
+ }
+
+ // clamp the colors to bytes and store off
+ for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
+ for ( j = 0 ; j < ds->lightmapHeight ; j++ ) {
+ k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + j)
+ * LIGHTMAP_WIDTH + ds->lightmapX + i;
+
+ ColorToBytes( color[i][j], lightBytes + k*3 );
+ }
+ }
+
+ if (ds->surfaceType == MST_PATCH)
+ {
+ FreeMesh(mesh);
+ }
+}
+
+
+//=============================================================================
+
+vec3_t gridMins;
+vec3_t gridSize = { 64, 64, 128 };
+int gridBounds[3];
+
+
+/*
+========================
+LightContributionToPoint
+========================
+*/
+qboolean LightContributionToPoint( const light_t *light, const vec3_t origin,
+ vec3_t color, traceWork_t *tw ) {
+ trace_t trace;
+ float add;
+
+ add = 0;
+
+ VectorClear( color );
+
+ // testing exact PTPFF
+ if ( exactPointToPolygon && light->type == emit_area ) {
+ float factor;
+ float d;
+ vec3_t normal;
+
+ // see if the point is behind the light
+ d = DotProduct( origin, light->normal ) - light->dist;
+ if ( !light->twosided ) {
+ if ( d < 1 ) {
+ return qfalse; // point is behind light
+ }
+ }
+
+ // test occlusion
+ // clip the line, tracing from the surface towards the light
+ TraceLine( origin, light->origin, &trace, qfalse, tw );
+ if ( trace.passSolid ) {
+ return qfalse;
+ }
+
+ // calculate the contribution
+ VectorSubtract( light->origin, origin, normal );
+ if ( VectorNormalize( normal, normal ) == 0 ) {
+ return qfalse;
+ }
+ factor = PointToPolygonFormFactor( origin, normal, light->w );
+ if ( factor <= 0 ) {
+ if ( light->twosided ) {
+ factor = -factor;
+ } else {
+ return qfalse;
+ }
+ }
+ VectorScale( light->emitColor, factor, color );
+ return qtrue;
+ }
+
+ // calculate the amount of light at this sample
+ if ( light->type == emit_point || light->type == emit_spotlight ) {
+ vec3_t dir;
+ float dist;
+
+ VectorSubtract( light->origin, origin, dir );
+ dist = VectorLength( dir );
+ // clamp the distance to prevent super hot spots
+ if ( dist < 16 ) {
+ dist = 16;
+ }
+ if ( light->linearLight ) {
+ add = light->photons * linearScale - dist;
+ if ( add < 0 ) {
+ add = 0;
+ }
+ } else {
+ add = light->photons / ( dist * dist );
+ }
+ } else {
+ return qfalse;
+ }
+
+ if ( add <= 1.0 ) {
+ return qfalse;
+ }
+
+ // clip the line, tracing from the surface towards the light
+ TraceLine( origin, light->origin, &trace, qfalse, tw );
+
+ // other light rays must not hit anything
+ if ( trace.passSolid ) {
+ return qfalse;
+ }
+
+ // add the result
+ color[0] = add * light->color[0];
+ color[1] = add * light->color[1];
+ color[2] = add * light->color[2];
+
+ return qtrue;
+}
+
+typedef struct {
+ vec3_t dir;
+ vec3_t color;
+} contribution_t;
+
+/*
+=============
+TraceGrid
+
+Grid samples are foe quickly determining the lighting
+of dynamically placed entities in the world
+=============
+*/
+#define MAX_CONTRIBUTIONS 1024
+void TraceGrid( int num ) {
+ int x, y, z;
+ vec3_t origin;
+ light_t *light;
+ vec3_t color;
+ int mod;
+ vec3_t directedColor;
+ vec3_t summedDir;
+ contribution_t contributions[MAX_CONTRIBUTIONS];
+ int numCon;
+ int i;
+ traceWork_t tw;
+ float addSize;
+
+ mod = num;
+ z = mod / ( gridBounds[0] * gridBounds[1] );
+ mod -= z * ( gridBounds[0] * gridBounds[1] );
+
+ y = mod / gridBounds[0];
+ mod -= y * gridBounds[0];
+
+ x = mod;
+
+ origin[0] = gridMins[0] + x * gridSize[0];
+ origin[1] = gridMins[1] + y * gridSize[1];
+ origin[2] = gridMins[2] + z * gridSize[2];
+
+ if ( PointInSolid( origin ) ) {
+ vec3_t baseOrigin;
+ int step;
+
+ VectorCopy( origin, baseOrigin );
+
+ // try to nudge the origin around to find a valid point
+ for ( step = 9 ; step <= 18 ; step += 9 ) {
+ for ( i = 0 ; i < 8 ; i++ ) {
+ VectorCopy( baseOrigin, origin );
+ if ( i & 1 ) {
+ origin[0] += step;
+ } else {
+ origin[0] -= step;
+ }
+ if ( i & 2 ) {
+ origin[1] += step;
+ } else {
+ origin[1] -= step;
+ }
+ if ( i & 4 ) {
+ origin[2] += step;
+ } else {
+ origin[2] -= step;
+ }
+
+ if ( !PointInSolid( origin ) ) {
+ break;
+ }
+ }
+ if ( i != 8 ) {
+ break;
+ }
+ }
+ if ( step > 18 ) {
+ // can't find a valid point at all
+ for ( i = 0 ; i < 8 ; i++ ) {
+ gridData[ num*8 + i ] = 0;
+ }
+ return;
+ }
+ }
+
+ VectorClear( summedDir );
+
+ // trace to all the lights
+
+ // find the major light direction, and divide the
+ // total light between that along the direction and
+ // the remaining in the ambient
+ numCon = 0;
+ for ( light = lights ; light ; light = light->next ) {
+ vec3_t add;
+ vec3_t dir;
+ float addSize;
+
+ if ( !LightContributionToPoint( light, origin, add, &tw ) ) {
+ continue;
+ }
+
+ VectorSubtract( light->origin, origin, dir );
+ VectorNormalize( dir, dir );
+
+ VectorCopy( add, contributions[numCon].color );
+ VectorCopy( dir, contributions[numCon].dir );
+ numCon++;
+
+ addSize = VectorLength( add );
+ VectorMA( summedDir, addSize, dir, summedDir );
+
+ if ( numCon == MAX_CONTRIBUTIONS-1 ) {
+ break;
+ }
+ }
+
+ //
+ // trace directly to the sun
+ //
+ SunToPoint( origin, &tw, color );
+ addSize = VectorLength( color );
+ if ( addSize > 0 ) {
+ VectorCopy( color, contributions[numCon].color );
+ VectorCopy( sunDirection, contributions[numCon].dir );
+ VectorMA( summedDir, addSize, sunDirection, summedDir );
+ numCon++;
+ }
+
+
+ // now that we have identified the primary light direction,
+ // go back and seperate all the light into directed and ambient
+ VectorNormalize( summedDir, summedDir );
+ VectorCopy( ambientColor, color );
+ VectorClear( directedColor );
+
+ for ( i = 0 ; i < numCon ; i++ ) {
+ float d;
+
+ d = DotProduct( contributions[i].dir, summedDir );
+ if ( d < 0 ) {
+ d = 0;
+ }
+
+ VectorMA( directedColor, d, contributions[i].color, directedColor );
+
+ // the ambient light will be at 1/4 the value of directed light
+ d = 0.25 * ( 1.0 - d );
+ VectorMA( color, d, contributions[i].color, color );
+ }
+
+ // now do some fudging to keep the ambient from being too low
+ VectorMA( color, 0.25, directedColor, color );
+
+ //
+ // save the resulting value out
+ //
+ ColorToBytes( color, gridData + num*8 );
+ ColorToBytes( directedColor, gridData + num*8 + 3 );
+
+ VectorNormalize( summedDir, summedDir );
+ NormalToLatLong( summedDir, gridData + num*8 + 6);
+}
+
+
+/*
+=============
+SetupGrid
+=============
+*/
+void SetupGrid( void ) {
+ int i;
+ vec3_t maxs;
+
+ for ( i = 0 ; i < 3 ; i++ ) {
+ gridMins[i] = gridSize[i] * ceil( dmodels[0].mins[i] / gridSize[i] );
+ maxs[i] = gridSize[i] * floor( dmodels[0].maxs[i] / gridSize[i] );
+ gridBounds[i] = (maxs[i] - gridMins[i])/gridSize[i] + 1;
+ }
+
+ numGridPoints = gridBounds[0] * gridBounds[1] * gridBounds[2];
+ if (numGridPoints * 8 >= MAX_MAP_LIGHTGRID)
+ Error("MAX_MAP_LIGHTGRID");
+ qprintf( "%5i gridPoints\n", numGridPoints );
+}
+
+//=============================================================================
+
+/*
+=============
+RemoveLightsInSolid
+=============
+*/
+void RemoveLightsInSolid(void)
+{
+ light_t *light, *prev;
+ int numsolid = 0;
+
+ prev = NULL;
+ for ( light = lights ; light ; ) {
+ if (PointInSolid(light->origin))
+ {
+ if (prev) prev->next = light->next;
+ else lights = light->next;
+ if (light->w)
+ FreeWinding(light->w);
+ free(light);
+ numsolid++;
+ if (prev)
+ light = prev->next;
+ else
+ light = lights;
+ }
+ else
+ {
+ prev = light;
+ light = light->next;
+ }
+ }
+ _printf (" %7i lights in solid\n", numsolid);
+}
+
+/*
+=============
+LightWorld
+=============
+*/
+void LightWorld (void) {
+ float f;
+
+ // determine the number of grid points
+ SetupGrid();
+
+ // find the optional world ambient
+ GetVectorForKey( &entities[0], "_color", ambientColor );
+ f = FloatForKey( &entities[0], "ambient" );
+ VectorScale( ambientColor, f, ambientColor );
+
+ // create lights out of patches and lights
+ qprintf ("--- CreateLights ---\n");
+ CreateEntityLights ();
+ qprintf ("%i point lights\n", numPointLights);
+ qprintf ("%i area lights\n", numAreaLights);
+
+ if (!nogridlighting) {
+ qprintf ("--- TraceGrid ---\n");
+ RunThreadsOnIndividual( numGridPoints, qtrue, TraceGrid );
+ qprintf( "%i x %i x %i = %i grid\n", gridBounds[0], gridBounds[1],
+ gridBounds[2], numGridPoints);
+ }
+
+ qprintf ("--- TraceLtm ---\n");
+ RunThreadsOnIndividual( numDrawSurfaces, qtrue, TraceLtm );
+ qprintf( "%5i visible samples\n", c_visible );
+ qprintf( "%5i occluded samples\n", c_occluded );
+}
+
+/*
+========
+CreateFilters
+
+EXPERIMENTAL, UNUSED
+
+Look for transparent light filter surfaces.
+
+This will only work for flat 3*3 patches that exactly hold one copy of the texture.
+========
+*/
+#define PLANAR_PATCH_EPSILON 0.1
+void CreateFilters( void ) {
+ int i;
+ filter_t *f;
+ dsurface_t *ds;
+ shaderInfo_t *si;
+ drawVert_t *v1, *v2, *v3;
+ vec3_t d1, d2;
+ int vertNum;
+
+ numFilters = 0;
+
+ return;
+
+ for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
+ ds = &drawSurfaces[i];
+ if ( !ds->patchWidth ) {
+ continue;
+ }
+ si = ShaderInfoForShader( dshaders[ ds->shaderNum ].shader );
+/*
+ if ( !(si->surfaceFlags & SURF_LIGHTFILTER) ) {
+ continue;
+ }
+*/
+
+ // we have a filter patch
+ v1 = &drawVerts[ ds->firstVert ];
+
+ if ( ds->patchWidth != 3 || ds->patchHeight != 3 ) {
+ _printf("WARNING: patch at %i %i %i has SURF_LIGHTFILTER but isn't a 3 by 3\n",
+ v1->xyz[0], v1->xyz[1], v1->xyz[2] );
+ continue;
+ }
+
+ if ( numFilters == MAX_FILTERS ) {
+ Error( "MAX_FILTERS" );
+ }
+ f = &filters[ numFilters ];
+ numFilters++;
+
+ v2 = &drawVerts[ ds->firstVert + 2 ];
+ v3 = &drawVerts[ ds->firstVert + 6 ];
+
+ VectorSubtract( v2->xyz, v1->xyz, d1 );
+ VectorSubtract( v3->xyz, v1->xyz, d2 );
+ VectorNormalize( d1, d1 );
+ VectorNormalize( d2, d2 );
+ CrossProduct( d1, d2, f->plane );
+ f->plane[3] = DotProduct( v1->xyz, f->plane );
+
+ // make sure all the control points are on the plane
+ for ( vertNum = 0 ; vertNum < ds->numVerts ; vertNum++ ) {
+ float d;
+
+ d = DotProduct( drawVerts[ ds->firstVert + vertNum ].xyz, f->plane ) - f->plane[3];
+ if ( fabs( d ) > PLANAR_PATCH_EPSILON ) {
+ break;
+ }
+ }
+ if ( vertNum != ds->numVerts ) {
+ numFilters--;
+ _printf("WARNING: patch at %i %i %i has SURF_LIGHTFILTER but isn't flat\n",
+ v1->xyz[0], v1->xyz[1], v1->xyz[2] );
+ continue;
+ }
+ }
+
+ f = &filters[0];
+ numFilters = 1;
+
+ f->plane[0] = 1;
+ f->plane[1] = 0;
+ f->plane[2] = 0;
+ f->plane[3] = 448;
+
+ f->origin[0] = 448;
+ f->origin[1] = 192;
+ f->origin[2] = 0;
+
+ f->vectors[0][0] = 0;
+ f->vectors[0][1] = -1.0 / 128;
+ f->vectors[0][2] = 0;
+
+ f->vectors[1][0] = 0;
+ f->vectors[1][1] = 0;
+ f->vectors[1][2] = 1.0 / 128;
+
+ f->si = ShaderInfoForShader( "textures/hell/blocks11ct" );
+}
+
+/*
+=============
+VertexLightingThread
+=============
+*/
+void VertexLightingThread(int num) {
+ dsurface_t *ds;
+ traceWork_t tw;
+ shaderInfo_t *si;
+
+ ds = &drawSurfaces[num];
+
+ // vertex-lit triangle model
+ if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
+ return;
+ }
+
+ if (novertexlighting)
+ return;
+
+ if ( ds->lightmapNum == -1 ) {
+ return; // doesn't need lighting at all
+ }
+
+ si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
+
+ // calculate the vertex lighting for gouraud shade mode
+ VertexLighting( ds, si->vertexShadows, si->forceSunLight, si->vertexScale, &tw );
+}
+
+/*
+=============
+TriSoupLightingThread
+=============
+*/
+void TriSoupLightingThread(int num) {
+ dsurface_t *ds;
+ traceWork_t tw;
+ shaderInfo_t *si;
+
+ ds = &drawSurfaces[num];
+ si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
+
+ // vertex-lit triangle model
+ if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
+ VertexLighting( ds, !si->noVertexShadows, si->forceSunLight, 1.0, &tw );
+ }
+}
+
+/*
+=============
+GridAndVertexLighting
+=============
+*/
+void GridAndVertexLighting(void) {
+ SetupGrid();
+
+ FindSkyBrushes();
+ CreateFilters();
+ InitTrace();
+ CreateEntityLights ();
+ CreateSurfaceLights();
+
+ if (!nogridlighting) {
+ _printf ("--- TraceGrid ---\n");
+ RunThreadsOnIndividual( numGridPoints, qtrue, TraceGrid );
+ }
+
+ if (!novertexlighting) {
+ _printf ("--- Vertex Lighting ---\n");
+ RunThreadsOnIndividual( numDrawSurfaces, qtrue, VertexLightingThread );
+ }
+
+ _printf("--- Model Lighting ---\n");
+ RunThreadsOnIndividual( numDrawSurfaces, qtrue, TriSoupLightingThread );
+}
+
+/*
+========
+LightMain
+
+========
+*/
+int LightMain (int argc, char **argv) {
+ int i;
+ double start, end;
+ const char *value;
+
+ _printf ("----- Lighting ----\n");
+
+ verbose = qfalse;
+
+ for (i=1 ; i<argc ; i++) {
+ if (!strcmp(argv[i],"-tempname"))
+ {
+ i++;
+ } else if (!strcmp(argv[i],"-v")) {
+ verbose = qtrue;
+ } else if (!strcmp(argv[i],"-threads")) {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ } else if (!strcmp(argv[i],"-area")) {
+ areaScale *= atof(argv[i+1]);
+ _printf ("area light scaling at %f\n", areaScale);
+ i++;
+ } else if (!strcmp(argv[i],"-point")) {
+ pointScale *= atof(argv[i+1]);
+ _printf ("point light scaling at %f\n", pointScale);
+ i++;
+ } else if (!strcmp(argv[i],"-notrace")) {
+ notrace = qtrue;
+ _printf ("No occlusion tracing\n");
+ } else if (!strcmp(argv[i],"-patchshadows")) {
+ patchshadows = qtrue;
+ _printf ("Patch shadow casting enabled\n");
+ } else if (!strcmp(argv[i],"-extra")) {
+ extra = qtrue;
+ _printf ("Extra detail tracing\n");
+ } else if (!strcmp(argv[i],"-extrawide")) {
+ extra = qtrue;
+ extraWide = qtrue;
+ _printf ("Extra wide detail tracing\n");
+ } 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],"-border")) {
+ lightmapBorder = qtrue;
+ _printf ("Adding debug border to lightmaps\n");
+ } else if (!strcmp(argv[i],"-nosurf")) {
+ noSurfaces = qtrue;
+ _printf ("Not tracing against surfaces\n" );
+ } else if (!strcmp(argv[i],"-dump")) {
+ dump = qtrue;
+ _printf ("Dumping occlusion maps\n");
+ } else {
+ break;
+ }
+ }
+
+ ThreadSetDefault ();
+
+ if (i != argc - 1) {
+ _printf("usage: q3map -light [-<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"
+ " notrace = don't cast any shadows\n"
+ " extra = enable super sampling for anti-aliasing\n"
+ " extrawide = same as extra but smoothen more\n"
+ " nogrid = don't calculate light grid for dynamic model lighting\n"
+ " novertex = don't calculate vertex lighting\n"
+ " samplesize <N> = set the lightmap pixel size to NxN units\n");
+ exit(0);
+ }
+
+ start = I_FloatTime ();
+
+ 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);
+
+ FindSkyBrushes();
+
+ 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]);
+ }
+
+ CreateFilters();
+
+ InitTrace();
+
+ SetEntityOrigins();
+
+ CountLightmaps();
+
+ CreateSurfaceLights();
+
+ LightWorld();
+
+ _printf ("writing %s\n", source);
+ WriteBSPFile (source);
+
+ end = I_FloatTime ();
+ _printf ("%5.0f seconds elapsed\n", end-start);
+
+ return 0;
+}
+
diff --git a/q3map/light.h b/q3map/light.h
new file mode 100755
index 0000000..5faa24a
--- /dev/null
+++ b/q3map/light.h
@@ -0,0 +1,151 @@
+/*
+===========================================================================
+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 "polylib.h"
+#include "imagelib.h"
+#include "threads.h"
+#include "scriplib.h"
+
+#include "shaders.h"
+#include "mesh.h"
+
+
+
+typedef enum
+{
+ emit_point,
+ emit_area,
+ emit_spotlight,
+ emit_sun
+} emittype_t;
+
+#define MAX_LIGHT_EDGES 8
+typedef struct light_s
+{
+ struct light_s *next;
+ emittype_t type;
+ struct shaderInfo_s *si;
+
+ vec3_t origin;
+ vec3_t normal; // for surfaces, spotlights, and suns
+ float dist; // plane location along normal
+
+ qboolean linearLight;
+ int photons;
+ int style;
+ vec3_t color;
+ float radiusByDist; // for spotlights
+
+ qboolean twosided; // fog lights both sides
+
+ winding_t *w;
+ vec3_t emitColor; // full out-of-gamut value
+} light_t;
+
+
+extern float lightscale;
+extern float ambient;
+extern float maxlight;
+extern float direct_scale;
+extern float entity_scale;
+
+extern qboolean noSurfaces;
+
+//===============================================================
+
+// light_trace.c
+
+// a facet is a subdivided element of a patch aproximation or model
+typedef struct cFacet_s {
+ float surface[4];
+ int numBoundaries; // either 3 or 4, anything less is degenerate
+ float boundaries[4][4]; // positive is outside the bounds
+
+ vec3_t points[4]; // needed for area light subdivision
+
+ float textureMatrix[2][4]; // compute texture coordinates at point of impact for translucency
+} cFacet_t;
+
+typedef struct {
+ vec3_t mins, maxs;
+ vec3_t origin;
+ float radius;
+
+ qboolean patch;
+
+ int numFacets;
+ cFacet_t *facets;
+
+ shaderInfo_t *shader; // for translucency
+} surfaceTest_t;
+
+
+typedef struct {
+ vec3_t filter; // starts out 1.0, 1.0, 1.0, may be reduced if
+ // transparent surfaces are crossed
+
+ vec3_t hit; // the impact point of a completely opaque surface
+ float hitFraction; // 0 = at start, 1.0 = at end
+ qboolean passSolid;
+} trace_t;
+
+extern surfaceTest_t *surfaceTest[MAX_MAP_DRAW_SURFS];
+
+void InitTrace( void );
+
+// traceWork_t is only a parameter to crutch up poor large local allocations on
+// winNT and macOS. It should be allocated in the worker function, but never
+// looked at.
+typedef struct {
+ vec3_t start, end;
+ int numOpenLeafs;
+ int openLeafNumbers[MAX_MAP_LEAFS];
+ trace_t *trace;
+ int patchshadows;
+} traceWork_t;
+
+void TraceLine( const vec3_t start, const vec3_t stop, trace_t *trace,
+ qboolean testAll, traceWork_t *tw );
+qboolean PointInSolid( vec3_t start );
+
+//===============================================================
+
+//===============================================================
+
+
+typedef struct {
+ int textureNum;
+ int x, y, width, height;
+
+ // for patches
+ qboolean patch;
+ mesh_t mesh;
+
+ // for faces
+ vec3_t origin;
+ vec3_t vecs[3];
+} lightmap_t;
+
+
diff --git a/q3map/light_trace.c b/q3map/light_trace.c
new file mode 100755
index 0000000..f8a51e7
--- /dev/null
+++ b/q3map/light_trace.c
@@ -0,0 +1,944 @@
+/*
+===========================================================================
+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 "light.h"
+
+
+
+#define CURVE_FACET_ERROR 8
+
+int c_totalTrace;
+int c_cullTrace, c_testTrace;
+int c_testFacets;
+
+surfaceTest_t *surfaceTest[MAX_MAP_DRAW_SURFS];
+
+/*
+=====================
+CM_GenerateBoundaryForPoints
+=====================
+*/
+void CM_GenerateBoundaryForPoints( float boundary[4], float plane[4], vec3_t a, vec3_t b ) {
+ vec3_t d1;
+
+ // amke a perpendicular vector to the edge and the surface
+ VectorSubtract( b, a, d1 );
+ CrossProduct( plane, d1, boundary );
+ VectorNormalize( boundary, boundary );
+ boundary[3] = DotProduct( a, boundary );
+}
+
+/*
+=====================
+TextureMatrixFromPoints
+=====================
+*/
+void TextureMatrixFromPoints( cFacet_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[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" );
+ }
+*/
+ }
+}
+
+/*
+=====================
+CM_GenerateFacetFor3Points
+=====================
+*/
+qboolean CM_GenerateFacetFor3Points( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c ) {
+ // if we can't generate a valid plane for the points, ignore the facet
+ if ( !PlaneFromPoints( f->surface, a->xyz, b->xyz, c->xyz ) ) {
+ f->numBoundaries = 0;
+ return qfalse;
+ }
+
+ // make boundaries
+ f->numBoundaries = 3;
+
+ CM_GenerateBoundaryForPoints( f->boundaries[0], f->surface, a->xyz, b->xyz );
+ CM_GenerateBoundaryForPoints( f->boundaries[1], f->surface, b->xyz, c->xyz );
+ CM_GenerateBoundaryForPoints( f->boundaries[2], f->surface, c->xyz, a->xyz );
+
+ VectorCopy( a->xyz, f->points[0] );
+ VectorCopy( b->xyz, f->points[1] );
+ VectorCopy( c->xyz, f->points[2] );
+
+ TextureMatrixFromPoints( f, a, b, c );
+
+ return qtrue;
+}
+
+/*
+=====================
+CM_GenerateFacetFor4Points
+
+Attempts to use four points as a planar quad
+=====================
+*/
+#define PLANAR_EPSILON 0.1
+qboolean CM_GenerateFacetFor4Points( cFacet_t *f, drawVert_t *a, drawVert_t *b, drawVert_t *c, drawVert_t *d ) {
+ float dist;
+ int i;
+ vec4_t plane;
+
+ // if we can't generate a valid plane for the points, ignore the facet
+ if ( !PlaneFromPoints( f->surface, a->xyz, b->xyz, c->xyz ) ) {
+ f->numBoundaries = 0;
+ return qfalse;
+ }
+
+ // if the fourth point is also on the plane, we can make a quad facet
+ dist = DotProduct( d->xyz, f->surface ) - f->surface[3];
+ if ( fabs( dist ) > PLANAR_EPSILON ) {
+ f->numBoundaries = 0;
+ return qfalse;
+ }
+
+ // make boundaries
+ f->numBoundaries = 4;
+
+ CM_GenerateBoundaryForPoints( f->boundaries[0], f->surface, a->xyz, b->xyz );
+ CM_GenerateBoundaryForPoints( f->boundaries[1], f->surface, b->xyz, c->xyz );
+ CM_GenerateBoundaryForPoints( f->boundaries[2], f->surface, c->xyz, d->xyz );
+ CM_GenerateBoundaryForPoints( f->boundaries[3], f->surface, d->xyz, a->xyz );
+
+ VectorCopy( a->xyz, f->points[0] );
+ VectorCopy( b->xyz, f->points[1] );
+ VectorCopy( c->xyz, f->points[2] );
+ VectorCopy( d->xyz, f->points[3] );
+
+ for (i = 1; i < 4; i++)
+ {
+ if ( !PlaneFromPoints( plane, f->points[i], f->points[(i+1) % 4], f->points[(i+2) % 4]) ) {
+ f->numBoundaries = 0;
+ return qfalse;
+ }
+
+ if (DotProduct(f->surface, plane) < 0.9) {
+ f->numBoundaries = 0;
+ return qfalse;
+ }
+ }
+
+ TextureMatrixFromPoints( f, a, b, c );
+
+ return qtrue;
+}
+
+
+
+
+/*
+===============
+SphereFromBounds
+===============
+*/
+void 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 );
+}
+
+
+/*
+====================
+FacetsForTriangleSurface
+====================
+*/
+void FacetsForTriangleSurface( dsurface_t *dsurf, shaderInfo_t *si, surfaceTest_t *test ) {
+ int i;
+ drawVert_t *v1, *v2, *v3, *v4;
+ int count;
+ int i1, i2, i3, i4, i5, i6;
+
+ test->patch = 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 ( CM_GenerateFacetFor4Points( &test->facets[count], v1, v2, v4, v3 ) ) {
+ count++;
+ i++; // skip next tri
+ continue;
+ }
+ }
+ }
+
+ if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v2, v3 ))
+ count++;
+ }
+
+ // we may have turned some pairs into quads
+ test->numFacets = count;
+}
+
+/*
+====================
+FacetsForPatch
+====================
+*/
+void FacetsForPatch( dsurface_t *dsurf, shaderInfo_t *si, surfaceTest_t *test ) {
+ int i, j;
+ drawVert_t *v1, *v2, *v3, *v4;
+ int count;
+ mesh_t srcMesh, *subdivided, *mesh;
+
+ srcMesh.width = dsurf->patchWidth;
+ srcMesh.height = dsurf->patchHeight;
+ srcMesh.verts = &drawVerts[ dsurf->firstVert ];
+
+ //subdivided = SubdivideMesh( mesh, CURVE_FACET_ERROR, 9999 );
+ mesh = SubdivideMesh( srcMesh, 8, 999 );
+ PutMeshOnCurve( *mesh );
+ MakeMeshNormals( *mesh );
+
+ subdivided = RemoveLinearMeshColumnsRows( mesh );
+ FreeMesh(mesh);
+
+ test->patch = qtrue;
+ test->numFacets = ( subdivided->width - 1 ) * ( subdivided->height - 1 ) * 2;
+ test->facets = malloc( sizeof( test->facets[0] ) * test->numFacets );
+ test->shader = si;
+
+ count = 0;
+ for ( i = 0 ; i < subdivided->width - 1 ; i++ ) {
+ 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 ( CM_GenerateFacetFor4Points( &test->facets[count], v1, v4, v3, v2 ) ) {
+ count++;
+ } else {
+ if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v4, v3 ))
+ count++;
+ if (CM_GenerateFacetFor3Points( &test->facets[count], v1, v3, v2 ))
+ count++;
+ }
+ }
+ }
+ test->numFacets = count;
+ FreeMesh(subdivided);
+}
+
+
+/*
+=====================
+InitSurfacesForTesting
+
+Builds structures to speed the ray tracing against surfaces
+=====================
+*/
+void InitSurfacesForTesting( void ) {
+
+ int i, j;
+ dsurface_t *dsurf;
+ surfaceTest_t *test;
+ drawVert_t *dvert;
+ shaderInfo_t *si;
+
+ for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
+ dsurf = &drawSurfaces[ i ];
+ if ( !dsurf->numIndexes && !dsurf->patchWidth ) {
+ continue;
+ }
+
+ // don't make surfaces for transparent objects
+ // because we want light to pass through them
+ si = ShaderInfoForShader( dshaders[ dsurf->shaderNum].shader );
+ if ( (si->contents & CONTENTS_TRANSLUCENT) && !(si->surfaceFlags & SURF_ALPHASHADOW) ) {
+ continue;
+ }
+
+ test = malloc( sizeof( *test ) );
+ surfaceTest[i] = test;
+ ClearBounds( test->mins, test->maxs );
+
+ dvert = &drawVerts[ dsurf->firstVert ];
+ for ( j = 0 ; j < dsurf->numVerts ; j++, dvert++ ) {
+ AddPointToBounds( dvert->xyz, test->mins, test->maxs );
+ }
+
+ SphereFromBounds( test->mins, test->maxs, test->origin, &test->radius );
+
+ if ( dsurf->surfaceType == MST_TRIANGLE_SOUP || dsurf->surfaceType == MST_PLANAR ) {
+ FacetsForTriangleSurface( dsurf, si, test );
+ } else if ( dsurf->surfaceType == MST_PATCH ) {
+ FacetsForPatch( dsurf, si, test );
+ }
+ }
+}
+
+
+/*
+=====================
+GenerateBoundaryForPoints
+=====================
+*/
+void GenerateBoundaryForPoints( float boundary[4], float plane[4], vec3_t a, vec3_t b ) {
+ vec3_t d1;
+
+ // amke a perpendicular vector to the edge and the surface
+ VectorSubtract( b, a, d1 );
+ CrossProduct( plane, d1, boundary );
+ VectorNormalize( boundary, boundary );
+ boundary[3] = DotProduct( a, boundary );
+}
+
+
+/*
+=================
+SetFacetFilter
+
+Given a point on a facet, determine the color filter
+for light passing through
+=================
+*/
+void SetFacetFilter( traceWork_t *tr, shaderInfo_t *shader, cFacet_t *facet, vec3_t point ) {
+ float s, t;
+ int is, it;
+ byte *image;
+ int b;
+
+ // most surfaces are completely opaque
+ if ( !(shader->surfaceFlags & SURF_ALPHASHADOW) ) {
+ VectorClear( tr->trace->filter );
+ return;
+ }
+
+ s = DotProduct( point, facet->textureMatrix[0] ) + facet->textureMatrix[0][3];
+ t = DotProduct( point, facet->textureMatrix[1] ) + facet->textureMatrix[1][3];
+
+ if ( !shader->pixels ) {
+ // assume completely solid
+ VectorClear( point );
+ return;
+ }
+
+ s = s - floor( s );
+ t = t - floor( t );
+
+ is = s * shader->width;
+ it = t * shader->height;
+
+ image = shader->pixels + 4 * ( it * shader->width + is );
+
+ // alpha filter
+ b = image[3];
+
+ // alpha test makes this a binary option
+ b = b < 128 ? 0 : 255;
+
+ tr->trace->filter[0] = tr->trace->filter[0] * (255-b) / 255;
+ tr->trace->filter[1] = tr->trace->filter[1] * (255-b) / 255;
+ tr->trace->filter[2] = tr->trace->filter[2] * (255-b) / 255;
+}
+
+
+/*
+====================
+TraceAgainstFacet
+
+Shader is needed for translucent surfaces
+====================
+*/
+void TraceAgainstFacet( traceWork_t *tr, shaderInfo_t *shader, cFacet_t *facet ) {
+ int j;
+ float d1, d2, d, f;
+ vec3_t point;
+ float dist;
+
+ // ignore degenerate facets
+ if ( facet->numBoundaries < 3 ) {
+ return;
+ }
+
+ dist = facet->surface[3];
+
+ // compare the trace endpoints against the facet plane
+ d1 = DotProduct( tr->start, facet->surface ) - dist;
+ if ( d1 > -1 && d1 < 1 ) {
+ return; // don't self intersect
+ }
+ d2 = DotProduct( tr->end, facet->surface ) - dist;
+ if ( d2 > -1 && d2 < 1 ) {
+ return; // don't self intersect
+ }
+
+ // calculate the intersection fraction
+ f = ( d1 - ON_EPSILON ) / ( d1 - d2 );
+ if ( f <= 0 ) {
+ return;
+ }
+ if ( f >= tr->trace->hitFraction ) {
+ return; // we have hit something earlier
+ }
+
+ // calculate the intersection point
+ for ( j = 0 ; j < 3 ; j++ ) {
+ point[j] = tr->start[j] + f * ( tr->end[j] - tr->start[j] );
+ }
+
+ // check the point against the facet boundaries
+ for ( j = 0 ; j < facet->numBoundaries ; j++ ) {
+ // adjust the plane distance apropriately for mins/maxs
+ dist = facet->boundaries[j][3];
+
+ d = DotProduct( point, facet->boundaries[j] );
+ if ( d > dist + ON_EPSILON ) {
+ break; // outside the bounds
+ }
+ }
+
+ if ( j != facet->numBoundaries ) {
+ return; // we are outside the bounds of the facet
+ }
+
+ // we hit this facet
+
+ // if this is a transparent surface, calculate filter value
+ if ( shader->surfaceFlags & SURF_ALPHASHADOW ) {
+ SetFacetFilter( tr, shader, facet, point );
+ } else {
+ // completely opaque
+ VectorClear( tr->trace->filter );
+ tr->trace->hitFraction = f;
+ }
+
+// VectorCopy( facet->surface, tr->trace->plane.normal );
+// tr->trace->plane.dist = facet->surface[3];
+}
+
+
+/*
+===============================================================
+
+ LINE TRACING
+
+===============================================================
+*/
+
+
+#define TRACE_ON_EPSILON 0.1
+
+typedef struct tnode_s
+{
+ int type;
+ vec3_t normal;
+ float dist;
+ int children[2];
+ int planeNum;
+} tnode_t;
+
+#define MAX_TNODES (MAX_MAP_NODES*4)
+tnode_t *tnodes, *tnode_p;
+
+/*
+==============
+MakeTnode
+
+Converts the disk node structure into the efficient tracing structure
+==============
+*/
+void MakeTnode (int nodenum)
+{
+ tnode_t *t;
+ dplane_t *plane;
+ int i;
+ dnode_t *node;
+ int leafNum;
+
+ t = tnode_p++;
+
+ node = dnodes + nodenum;
+ plane = dplanes + node->planeNum;
+
+ t->planeNum = node->planeNum;
+ t->type = PlaneTypeForNormal( plane->normal );
+ VectorCopy (plane->normal, t->normal);
+ t->dist = plane->dist;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i] < 0) {
+ leafNum = -node->children[i] - 1;
+ if ( dleafs[leafNum].cluster == -1 ) {
+ // solid
+ t->children[i] = leafNum | ( 1 << 31 ) | ( 1 << 30 );
+ } else {
+ t->children[i] = leafNum | ( 1 << 31 );
+ }
+ } else {
+ t->children[i] = tnode_p - tnodes;
+ MakeTnode (node->children[i]);
+ }
+ }
+
+}
+
+/*
+=============
+InitTrace
+
+Loads the node structure out of a .bsp file to be used for light occlusion
+=============
+*/
+void InitTrace( void ) {
+ // 32 byte align the structs
+ tnodes = malloc( (MAX_TNODES+1) * sizeof(tnode_t));
+ tnodes = (tnode_t *)(((int)tnodes + 31)&~31);
+ tnode_p = tnodes;
+
+ MakeTnode (0);
+
+ InitSurfacesForTesting();
+}
+
+
+/*
+===================
+PointInSolid
+===================
+*/
+qboolean PointInSolid_r( vec3_t start, int node ) {
+ tnode_t *tnode;
+ float front;
+
+ while ( !(node & (1<<31) ) ) {
+ tnode = &tnodes[node];
+ switch (tnode->type) {
+ case PLANE_X:
+ front = start[0] - tnode->dist;
+ break;
+ case PLANE_Y:
+ front = start[1] - tnode->dist;
+ break;
+ case PLANE_Z:
+ front = start[2] - tnode->dist;
+ break;
+ default:
+ front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
+ break;
+ }
+
+ if ( front == 0 ) {
+ // exactly on node, must check both sides
+ return (qboolean) ( PointInSolid_r( start, tnode->children[0] )
+ | PointInSolid_r( start, tnode->children[1] ) );
+ }
+
+ if ( front > 0 ) {
+ node = tnode->children[0];
+ } else {
+ node = tnode->children[1];
+ }
+ }
+
+ if ( node & ( 1 << 30 ) ) {
+ return qtrue;
+ }
+ return qfalse;
+}
+
+/*
+=============
+PointInSolid
+
+=============
+*/
+qboolean PointInSolid( vec3_t start ) {
+ return PointInSolid_r( start, 0 );
+}
+
+
+/*
+=============
+TraceLine_r
+
+Returns qtrue if something is hit and tracing can stop
+=============
+*/
+int TraceLine_r( int node, const vec3_t start, const vec3_t stop, traceWork_t *tw ) {
+ tnode_t *tnode;
+ float front, back;
+ vec3_t mid;
+ float frac;
+ int side;
+ int r;
+
+ if (node & (1<<31)) {
+ if (node & ( 1 << 30 ) ) {
+ VectorCopy (start, tw->trace->hit);
+ tw->trace->passSolid = qtrue;
+ return qtrue;
+ } else {
+ // save the node off for more exact testing
+ if ( tw->numOpenLeafs == MAX_MAP_LEAFS ) {
+ return qfalse;
+ }
+ tw->openLeafNumbers[ tw->numOpenLeafs ] = node & ~(3 << 30);
+ tw->numOpenLeafs++;
+ return qfalse;
+ }
+ }
+
+ tnode = &tnodes[node];
+ switch (tnode->type) {
+ case PLANE_X:
+ front = start[0] - tnode->dist;
+ back = stop[0] - tnode->dist;
+ break;
+ case PLANE_Y:
+ front = start[1] - tnode->dist;
+ back = stop[1] - tnode->dist;
+ break;
+ case PLANE_Z:
+ front = start[2] - tnode->dist;
+ back = stop[2] - tnode->dist;
+ break;
+ default:
+ front = (start[0]*tnode->normal[0] + start[1]*tnode->normal[1] + start[2]*tnode->normal[2]) - tnode->dist;
+ back = (stop[0]*tnode->normal[0] + stop[1]*tnode->normal[1] + stop[2]*tnode->normal[2]) - tnode->dist;
+ break;
+ }
+
+ if (front >= -TRACE_ON_EPSILON && back >= -TRACE_ON_EPSILON) {
+ return TraceLine_r (tnode->children[0], start, stop, tw);
+ }
+
+ if (front < TRACE_ON_EPSILON && back < TRACE_ON_EPSILON) {
+ return TraceLine_r (tnode->children[1], start, stop, tw);
+ }
+
+ side = front < 0;
+
+ frac = front / (front-back);
+
+ mid[0] = start[0] + (stop[0] - start[0])*frac;
+ mid[1] = start[1] + (stop[1] - start[1])*frac;
+ mid[2] = start[2] + (stop[2] - start[2])*frac;
+
+ r = TraceLine_r (tnode->children[side], start, mid, tw);
+
+ if (r) {
+ return r;
+ }
+
+// trace->planeNum = tnode->planeNum;
+ return TraceLine_r (tnode->children[!side], mid, stop, tw);
+}
+
+//==========================================================================================
+
+
+/*
+================
+SphereCull
+================
+*/
+qboolean SphereCull( vec3_t start, vec3_t stop, vec3_t origin, float radius ) {
+ vec3_t v;
+ float d;
+ vec3_t dir;
+ float len;
+ vec3_t on;
+
+ VectorSubtract( stop, start, dir );
+ len = VectorNormalize( dir, dir );
+
+ VectorSubtract( origin, start, v );
+ d = DotProduct( v, dir );
+ if ( d > len + radius ) {
+ return qtrue; // too far ahead
+ }
+ if ( d < -radius ) {
+ return qtrue; // too far behind
+ }
+ VectorMA( start, d, dir, on );
+
+ VectorSubtract( on, origin, v );
+
+ len = VectorLength( v );
+
+ if ( len > radius ) {
+ return qtrue; // too far to the side
+ }
+
+ return qfalse; // must be traced against
+}
+
+/*
+================
+TraceAgainstSurface
+================
+*/
+void TraceAgainstSurface( traceWork_t *tw, surfaceTest_t *surf ) {
+ int i;
+
+ // if surfaces are trans
+ if ( SphereCull( tw->start, tw->end, surf->origin, surf->radius ) ) {
+ if ( numthreads == 1 ) {
+ c_cullTrace++;
+ }
+ return;
+ }
+
+ if ( numthreads == 1 ) {
+ c_testTrace++;
+ c_testFacets += surf->numFacets;
+ }
+
+ /*
+ // MrE: backface culling
+ if (!surf->patch && surf->numFacets) {
+ // if the surface does not cast an alpha shadow
+ if ( !(surf->shader->surfaceFlags & SURF_ALPHASHADOW) ) {
+ vec3_t vec;
+ VectorSubtract(tw->end, tw->start, vec);
+ if (DotProduct(vec, surf->facets->surface) > 0)
+ return;
+ }
+ }
+ */
+
+ // test against each facet
+ for ( i = 0 ; i < surf->numFacets ; i++ ) {
+ TraceAgainstFacet( tw, surf->shader, surf->facets + i );
+ }
+}
+
+/*
+=============
+TraceLine
+
+Follow the trace just through the solid leafs first, and only
+if it passes that, trace against the objects inside the empty leafs
+Returns qtrue if the trace hit any
+
+traceWork_t is only a parameter to crutch up poor large local allocations on
+winNT and macOS. It should be allocated in the worker function, but never
+looked at.
+
+leave testAll false if all you care about is if it hit anything at all.
+if you need to know the exact first point of impact (for a sun trace), set
+testAll to true
+=============
+*/
+extern qboolean patchshadows;
+
+void TraceLine( const vec3_t start, const vec3_t stop, trace_t *trace, qboolean testAll, traceWork_t *tw ) {
+ int r;
+ int i, j;
+ dleaf_t *leaf;
+ float oldHitFrac;
+ surfaceTest_t *test;
+ int surfaceNum;
+ byte surfaceTested[MAX_MAP_DRAW_SURFS/8];
+ ;
+
+ if ( numthreads == 1 ) {
+ c_totalTrace++;
+ }
+
+ // assume all light gets through, unless the ray crosses
+ // a translucent surface
+ trace->filter[0] = 1.0;
+ trace->filter[1] = 1.0;
+ trace->filter[2] = 1.0;
+
+ VectorCopy( start, tw->start );
+ VectorCopy( stop, tw->end );
+ tw->trace = trace;
+
+ tw->numOpenLeafs = 0;
+
+ trace->passSolid = qfalse;
+ trace->hitFraction = 1.0;
+
+ r = TraceLine_r( 0, start, stop, tw );
+
+ // if we hit a solid leaf, stop without testing the leaf
+ // surfaces. Note that the plane and endpoint might not
+ // be the first solid intersection along the ray.
+ if ( r && !testAll ) {
+ return;
+ }
+
+ if ( noSurfaces ) {
+ return;
+ }
+
+ memset( surfaceTested, 0, (numDrawSurfaces+7)/8 );
+ oldHitFrac = trace->hitFraction;
+
+ for ( i = 0 ; i < tw->numOpenLeafs ; i++ ) {
+ leaf = &dleafs[ tw->openLeafNumbers[ i ] ];
+ for ( j = 0 ; j < leaf->numLeafSurfaces ; j++ ) {
+ surfaceNum = dleafsurfaces[ leaf->firstLeafSurface + j ];
+
+ // make sure we don't test the same ray against a surface more than once
+ if ( surfaceTested[ surfaceNum>>3 ] & ( 1 << ( surfaceNum & 7) ) ) {
+ continue;
+ }
+ surfaceTested[ surfaceNum>>3 ] |= ( 1 << ( surfaceNum & 7 ) );
+
+ test = surfaceTest[ surfaceNum ];
+ if ( !test ) {
+ continue;
+ }
+ //
+ if ( !tw->patchshadows && test->patch ) {
+ continue;
+ }
+ TraceAgainstSurface( tw, test );
+ }
+
+ // if the trace is now solid, we can't possibly hit anything closer
+ if ( trace->hitFraction < oldHitFrac ) {
+ trace->passSolid = qtrue;
+ break;
+ }
+ }
+
+ for ( i = 0 ; i < 3 ; i++ ) {
+ trace->hit[i] = start[i] + ( stop[i] - start[i] ) * trace->hitFraction;
+ }
+}
+
diff --git a/q3map/lightmaps.c b/q3map/lightmaps.c
new file mode 100755
index 0000000..95064e5
--- /dev/null
+++ b/q3map/lightmaps.c
@@ -0,0 +1,395 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+/*
+
+ Lightmap allocation has to be done after all flood filling and
+ visible surface determination.
+
+*/
+
+int numSortShaders;
+mapDrawSurface_t *surfsOnShader[MAX_MAP_SHADERS];
+
+
+int allocated[LIGHTMAP_WIDTH];
+
+int numLightmaps = 1;
+int c_exactLightmap;
+
+
+void PrepareNewLightmap( void ) {
+ memset( allocated, 0, sizeof( allocated ) );
+ numLightmaps++;
+}
+
+/*
+===============
+AllocLMBlock
+
+returns a texture number and the position inside it
+===============
+*/
+qboolean AllocLMBlock (int w, int h, int *x, int *y)
+{
+ int i, j;
+ int best, best2;
+
+ best = LIGHTMAP_HEIGHT;
+
+ for ( i=0 ; i <= LIGHTMAP_WIDTH-w ; i++ ) {
+ best2 = 0;
+
+ for (j=0 ; j<w ; j++) {
+ if (allocated[i+j] >= best) {
+ break;
+ }
+ if (allocated[i+j] > best2) {
+ best2 = allocated[i+j];
+ }
+ }
+ if (j == w) { // this is a valid spot
+ *x = i;
+ *y = best = best2;
+ }
+ }
+
+ if (best + h > LIGHTMAP_HEIGHT) {
+ return qfalse;
+ }
+
+ for (i=0 ; i<w ; i++) {
+ allocated[*x + i] = best + h;
+ }
+
+ return qtrue;
+}
+
+
+/*
+===================
+AllocateLightmapForPatch
+===================
+*/
+//#define LIGHTMAP_PATCHSHIFT
+
+void AllocateLightmapForPatch( mapDrawSurface_t *ds ) {
+ int i, j, k;
+ drawVert_t *verts;
+ int w, h;
+ int x, y;
+ float s, t;
+ mesh_t mesh, *subdividedMesh, *tempMesh, *newmesh;
+ int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_HEIGHT], ssize;
+
+ verts = ds->verts;
+
+ mesh.width = ds->patchWidth;
+ mesh.height = ds->patchHeight;
+ mesh.verts = verts;
+ newmesh = SubdivideMesh( mesh, 8, 999 );
+
+ PutMeshOnCurve( *newmesh );
+ tempMesh = RemoveLinearMeshColumnsRows( newmesh );
+ FreeMesh(newmesh);
+
+ ssize = samplesize;
+ if (ds->shaderInfo->lightmapSampleSize)
+ ssize = ds->shaderInfo->lightmapSampleSize;
+
+#ifdef LIGHTMAP_PATCHSHIFT
+ subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH-1, widthtable, heighttable);
+#else
+ subdividedMesh = SubdivideMeshQuads( tempMesh, ssize, LIGHTMAP_WIDTH, widthtable, heighttable);
+#endif
+
+ w = subdividedMesh->width;
+ h = subdividedMesh->height;
+
+#ifdef LIGHTMAP_PATCHSHIFT
+ w++;
+ h++;
+#endif
+
+ FreeMesh(subdividedMesh);
+
+ // allocate the lightmap
+ c_exactLightmap += w * h;
+
+ if ( !AllocLMBlock( w, h, &x, &y ) ) {
+ PrepareNewLightmap();
+ if ( !AllocLMBlock( w, h, &x, &y ) ) {
+ Error("Entity %i, brush %i: Lightmap allocation failed",
+ ds->mapBrush->entitynum, ds->mapBrush->brushnum );
+ }
+ }
+
+#ifdef LIGHTMAP_PATCHSHIFT
+ w--;
+ h--;
+#endif
+
+ // set the lightmap texture coordinates in the drawVerts
+ ds->lightmapNum = numLightmaps - 1;
+ ds->lightmapWidth = w;
+ ds->lightmapHeight = h;
+ ds->lightmapX = x;
+ ds->lightmapY = y;
+
+ for ( i = 0 ; i < ds->patchWidth ; i++ ) {
+ for ( k = 0 ; k < w ; k++ ) {
+ if ( originalWidths[k] >= i ) {
+ break;
+ }
+ }
+ if (k >= w)
+ k = w-1;
+ s = x + k;
+ for ( j = 0 ; j < ds->patchHeight ; j++ ) {
+ for ( k = 0 ; k < h ; k++ ) {
+ if ( originalHeights[k] >= j ) {
+ break;
+ }
+ }
+ if (k >= h)
+ k = h-1;
+ t = y + k;
+ verts[i + j * ds->patchWidth].lightmap[0] = ( s + 0.5 ) / LIGHTMAP_WIDTH;
+ verts[i + j * ds->patchWidth].lightmap[1] = ( t + 0.5 ) / LIGHTMAP_HEIGHT;
+ }
+ }
+}
+
+
+/*
+===================
+AllocateLightmapForSurface
+===================
+*/
+//#define LIGHTMAP_BLOCK 16
+void AllocateLightmapForSurface( mapDrawSurface_t *ds ) {
+ vec3_t mins, maxs, size, exactSize, delta;
+ int i;
+ drawVert_t *verts;
+ int w, h;
+ int x, y, ssize;
+ int axis;
+ vec3_t vecs[2];
+ float s, t;
+ vec3_t origin;
+ plane_t *plane;
+ float d;
+ vec3_t planeNormal;
+
+ if ( ds->patch ) {
+ AllocateLightmapForPatch( ds );
+ return;
+ }
+
+ ssize = samplesize;
+ if (ds->shaderInfo->lightmapSampleSize)
+ ssize = ds->shaderInfo->lightmapSampleSize;
+
+ plane = &mapplanes[ ds->side->planenum ];
+
+ // bound the surface
+ ClearBounds( mins, maxs );
+ verts = ds->verts;
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ AddPointToBounds( verts[i].xyz, mins, maxs );
+ }
+
+ // round to the lightmap resolution
+ for ( i = 0 ; i < 3 ; i++ ) {
+ exactSize[i] = maxs[i] - mins[i];
+ mins[i] = ssize * floor( mins[i] / ssize );
+ maxs[i] = ssize * ceil( maxs[i] / ssize );
+ size[i] = (maxs[i] - mins[i]) / ssize + 1;
+ }
+
+ // the two largest axis will be the lightmap size
+ memset( vecs, 0, sizeof( vecs ) );
+
+ planeNormal[0] = fabs( plane->normal[0] );
+ planeNormal[1] = fabs( plane->normal[1] );
+ planeNormal[2] = fabs( plane->normal[2] );
+
+ if ( planeNormal[0] >= planeNormal[1] && planeNormal[0] >= planeNormal[2] ) {
+ w = size[1];
+ h = size[2];
+ axis = 0;
+ vecs[0][1] = 1.0 / ssize;
+ vecs[1][2] = 1.0 / ssize;
+ } else if ( planeNormal[1] >= planeNormal[0] && planeNormal[1] >= planeNormal[2] ) {
+ w = size[0];
+ h = size[2];
+ axis = 1;
+ vecs[0][0] = 1.0 / ssize;
+ vecs[1][2] = 1.0 / ssize;
+ } else {
+ w = size[0];
+ h = size[1];
+ axis = 2;
+ vecs[0][0] = 1.0 / ssize;
+ vecs[1][1] = 1.0 / ssize;
+ }
+
+ if ( !plane->normal[axis] ) {
+ Error( "Chose a 0 valued axis" );
+ }
+
+ if ( w > LIGHTMAP_WIDTH ) {
+ VectorScale ( vecs[0], (float)LIGHTMAP_WIDTH/w, vecs[0] );
+ w = LIGHTMAP_WIDTH;
+ }
+
+ if ( h > LIGHTMAP_HEIGHT ) {
+ VectorScale ( vecs[1], (float)LIGHTMAP_HEIGHT/h, vecs[1] );
+ h = LIGHTMAP_HEIGHT;
+ }
+
+ c_exactLightmap += w * h;
+
+ if ( !AllocLMBlock( w, h, &x, &y ) ) {
+ PrepareNewLightmap();
+ if ( !AllocLMBlock( w, h, &x, &y ) ) {
+ Error("Entity %i, brush %i: Lightmap allocation failed",
+ ds->mapBrush->entitynum, ds->mapBrush->brushnum );
+ }
+ }
+
+ // set the lightmap texture coordinates in the drawVerts
+ ds->lightmapNum = numLightmaps - 1;
+ ds->lightmapWidth = w;
+ ds->lightmapHeight = h;
+ ds->lightmapX = x;
+ ds->lightmapY = y;
+
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ VectorSubtract( verts[i].xyz, mins, delta );
+ s = DotProduct( delta, vecs[0] ) + x + 0.5;
+ t = DotProduct( delta, vecs[1] ) + y + 0.5;
+ verts[i].lightmap[0] = s / LIGHTMAP_WIDTH;
+ verts[i].lightmap[1] = t / LIGHTMAP_HEIGHT;
+ }
+
+ // calculate the world coordinates of the lightmap samples
+
+ // project mins onto plane to get origin
+ d = DotProduct( mins, plane->normal ) - plane->dist;
+ d /= plane->normal[ axis ];
+ VectorCopy( mins, origin );
+ origin[axis] -= d;
+
+ // project stepped lightmap blocks and subtract to get planevecs
+ for ( i = 0 ; i < 2 ; i++ ) {
+ vec3_t normalized;
+ float len;
+
+ len = VectorNormalize( vecs[i], normalized );
+ VectorScale( normalized, (1.0/len), vecs[i] );
+ d = DotProduct( vecs[i], plane->normal );
+ d /= plane->normal[ axis ];
+ vecs[i][axis] -= d;
+ }
+
+ VectorCopy( origin, ds->lightmapOrigin );
+ VectorCopy( vecs[0], ds->lightmapVecs[0] );
+ VectorCopy( vecs[1], ds->lightmapVecs[1] );
+ VectorCopy( plane->normal, ds->lightmapVecs[2] );
+}
+
+/*
+===================
+AllocateLightmaps
+===================
+*/
+void AllocateLightmaps( entity_t *e ) {
+ int i, j;
+ mapDrawSurface_t *ds;
+ shaderInfo_t *si;
+
+ qprintf ("--- AllocateLightmaps ---\n");
+
+
+ // sort all surfaces by shader so common shaders will usually
+ // be in the same lightmap
+ numSortShaders = 0;
+
+ for ( i = e->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
+ ds = &mapDrawSurfs[i];
+ if ( !ds->numVerts ) {
+ continue; // leftover from a surface subdivision
+ }
+ if ( ds->miscModel ) {
+ continue;
+ }
+ if ( !ds->patch ) {
+ VectorCopy( mapplanes[ds->side->planenum].normal, ds->lightmapVecs[2] );
+ }
+
+ // search for this shader
+ for ( j = 0 ; j < numSortShaders ; j++ ) {
+ if ( ds->shaderInfo == surfsOnShader[j]->shaderInfo ) {
+ ds->nextOnShader = surfsOnShader[j];
+ surfsOnShader[j] = ds;
+ break;
+ }
+ }
+ if ( j == numSortShaders ) {
+ if ( numSortShaders >= MAX_MAP_SHADERS ) {
+ Error( "MAX_MAP_SHADERS" );
+ }
+ surfsOnShader[j] = ds;
+ numSortShaders++;
+ }
+ }
+ qprintf( "%5i unique shaders\n", numSortShaders );
+
+ // for each shader, allocate lightmaps for each surface
+
+// numLightmaps = 0;
+// PrepareNewLightmap();
+
+ for ( i = 0 ; i < numSortShaders ; i++ ) {
+ si = surfsOnShader[i]->shaderInfo;
+
+ for ( ds = surfsOnShader[i] ; ds ; ds = ds->nextOnShader ) {
+ // some surfaces don't need lightmaps allocated for them
+ if ( si->surfaceFlags & SURF_NOLIGHTMAP ) {
+ ds->lightmapNum = -1;
+ } else if ( si->surfaceFlags & SURF_POINTLIGHT ) {
+ ds->lightmapNum = -3;
+ } else {
+ AllocateLightmapForSurface( ds );
+ }
+ }
+ }
+
+ qprintf( "%7i exact lightmap texels\n", c_exactLightmap );
+ qprintf( "%7i block lightmap texels\n", numLightmaps * LIGHTMAP_WIDTH*LIGHTMAP_HEIGHT );
+}
+
+
+
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;
+}
diff --git a/q3map/makefile b/q3map/makefile
new file mode 100755
index 0000000..15c6a0b
--- /dev/null
+++ b/q3map/makefile
@@ -0,0 +1,148 @@
+
+CFLAGS = -c
+LDFLAGS =
+ODIR = /q3/q3map
+
+EXEBASE = q3map
+EXE = $(ODIR)/$(EXEBASE)
+all: $(EXE)
+
+_irix:
+ make "CFLAGS = -c -Ofast=ip27 -OPT:IEEE_arithmetic=3 -I../common -Xcpluscomm " "LDFLAGS = -Ofast=ip27 -OPT:IEEE_arithmetic=3 -g"
+
+_irixdebug:
+ make "CFLAGS = -c -O2 -g -I../common -Xcpluscomm" "LDFLAGS = -g"
+
+_irixinst:
+ make "_irix"
+ make "install"
+
+clean:
+ rm -f $(ODIR)/*.o $(EXE)
+
+install:
+ cp $(EXE) /quake3_bin
+ chmod 0777 /quake3_bin/$(EXEBASE)
+
+installtest:
+ cp $(EXE) /quake3_bin/$(EXEBASE)_test
+ chmod 0777 /quake3_bin/$(EXEBASE)_test
+
+
+FILES = $(ODIR)/fog.o $(ODIR)/brush.o $(ODIR)/tjunction.o $(ODIR)/vis.o $(ODIR)/visflow.o \
+$(ODIR)/light.o $(ODIR)/lightmaps.o $(ODIR)/bspfile.o \
+$(ODIR)/cmdlib.o $(ODIR)/patch.o $(ODIR)/mesh.o $(ODIR)/nodraw.o $(ODIR)/glfile.o \
+$(ODIR)/leakfile.o $(ODIR)/map.o $(ODIR)/mathlib.o $(ODIR)/polylib.o $(ODIR)/aselib.o \
+$(ODIR)/imagelib.o $(ODIR)/portals.o $(ODIR)/prtfile.o $(ODIR)/bsp.o $(ODIR)/surface.o \
+$(ODIR)/scriplib.o $(ODIR)/shaders.o $(ODIR)/threads.o $(ODIR)/tree.o \
+$(ODIR)/writebsp.o $(ODIR)/facebsp.o $(ODIR)/misc_model.o $(ODIR)/light_trace.o
+
+$(EXE) : $(FILES)
+ cc -o $(EXE) $(LDFLAGS) $(FILES) -lm
+
+$(ODIR)/surface.o : surface.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/fog.o : fog.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/brush.o : brush.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/tjunction.o : tjunction.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/lightmaps.o : lightmaps.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/brushbsp.o : brushbsp.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/facebsp.o : facebsp.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/patch.o : patch.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mesh.o : mesh.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/misc_model.o : misc_model.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/nodraw.o : nodraw.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/glfile.o : glfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/leakfile.o : leakfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/map.o : map.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/portals.o : portals.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/prtfile.o : prtfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bsp.o : bsp.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/tree.o : tree.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/shaders.o : shaders.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/writebsp.o : writebsp.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/csg.o : csg.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+
+$(ODIR)/vis.o : vis.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/visflow.o : visflow.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+
+$(ODIR)/light.o : light.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/light_trace.o : light_trace.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+$(ODIR)/cmdlib.o : ../common/cmdlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/mathlib.o : ../common/mathlib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/polylib.o : ../common/polylib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/aselib.o : ../common/aselib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/imagelib.o : ../common/imagelib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/scriplib.o : ../common/scriplib.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/threads.o : ../common/threads.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+$(ODIR)/bspfile.o : ../common/bspfile.c
+ cc $(CFLAGS) -E $? | tr -d '\015' > /tmp/temp.i
+ cc $(CFLAGS) -o $@ /tmp/temp.i
+
+
diff --git a/q3map/map.c b/q3map/map.c
new file mode 100755
index 0000000..e275e34
--- /dev/null
+++ b/q3map/map.c
@@ -0,0 +1,1251 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+// map.c
+
+#include "qbsp.h"
+
+
+int entitySourceBrushes; // to track editor brush numbers
+
+int numMapPatches;
+
+// undefine to make plane finding use linear sort
+#define USE_HASHING
+#define PLANE_HASHES 1024
+plane_t *planehash[PLANE_HASHES];
+
+plane_t mapplanes[MAX_MAP_PLANES];
+int nummapplanes;
+
+// as brushes and patches are read in, the shaders are stored out in order
+// here, so -onlytextures can just copy them out over the existing shaders
+// in the drawSurfaces
+char mapIndexedShaders[MAX_MAP_BRUSHSIDES][MAX_QPATH];
+int numMapIndexedShaders;
+
+vec3_t map_mins, map_maxs;
+
+entity_t *mapent;
+
+
+
+int c_boxbevels;
+int c_edgebevels;
+
+int c_areaportals;
+int c_detail;
+int c_structural;
+
+// brushes are parsed into a temporary array of sides,
+// which will have the bevels added and duplicates
+// removed before the final brush is allocated
+bspbrush_t *buildBrush;
+
+
+void TestExpandBrushes (void);
+void SetTerrainTextures( void );
+void ParseTerrain( void );
+
+
+/*
+=============================================================================
+
+PLANE FINDING
+
+=============================================================================
+*/
+
+
+/*
+================
+PlaneEqual
+================
+*/
+#define NORMAL_EPSILON 0.00001
+#define DIST_EPSILON 0.01
+qboolean PlaneEqual (plane_t *p, vec3_t normal, vec_t dist)
+{
+#if 1
+ if (
+ fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
+ && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
+ && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
+ && fabs(p->dist - dist) < DIST_EPSILON )
+ return qtrue;
+#else
+ if (p->normal[0] == normal[0]
+ && p->normal[1] == normal[1]
+ && p->normal[2] == normal[2]
+ && p->dist == dist)
+ return qtrue;
+#endif
+ return qfalse;
+}
+
+/*
+================
+AddPlaneToHash
+================
+*/
+void AddPlaneToHash (plane_t *p)
+{
+ int hash;
+
+ hash = (int)fabs(p->dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ p->hash_chain = planehash[hash];
+ planehash[hash] = p;
+}
+
+/*
+================
+CreateNewFloatPlane
+================
+*/
+int CreateNewFloatPlane (vec3_t normal, vec_t dist)
+{
+ plane_t *p, temp;
+
+ if (VectorLength(normal) < 0.5)
+ {
+ _printf( "FloatPlane: bad normal\n");
+ return -1;
+ }
+
+ // create a new plane
+ if (nummapplanes+2 > MAX_MAP_PLANES)
+ Error ("MAX_MAP_PLANES");
+
+ p = &mapplanes[nummapplanes];
+ VectorCopy (normal, p->normal);
+ p->dist = dist;
+ p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
+
+ VectorSubtract (vec3_origin, normal, (p+1)->normal);
+ (p+1)->dist = -dist;
+
+ nummapplanes += 2;
+
+ // allways put axial planes facing positive first
+ if (p->type < 3)
+ {
+ if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
+ {
+ // flip order
+ temp = *p;
+ *p = *(p+1);
+ *(p+1) = temp;
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 1;
+ }
+ }
+
+ AddPlaneToHash (p);
+ AddPlaneToHash (p+1);
+ return nummapplanes - 2;
+}
+
+/*
+==============
+SnapVector
+==============
+*/
+void SnapVector (vec3_t normal)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = 1;
+ break;
+ }
+ if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
+ {
+ VectorClear (normal);
+ normal[i] = -1;
+ break;
+ }
+ }
+}
+
+/*
+==============
+SnapPlane
+==============
+*/
+void SnapPlane (vec3_t normal, vec_t *dist)
+{
+ SnapVector (normal);
+
+ if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
+ *dist = Q_rint(*dist);
+}
+
+/*
+=============
+FindFloatPlane
+
+=============
+*/
+#ifndef USE_HASHING
+int FindFloatPlane (vec3_t normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+
+ SnapPlane (normal, &dist);
+ for (i=0, p=mapplanes ; i<nummapplanes ; i++, p++)
+ {
+ if (PlaneEqual (p, normal, dist))
+ return i;
+ }
+
+ return CreateNewFloatPlane (normal, dist);
+}
+#else
+int FindFloatPlane (vec3_t normal, vec_t dist)
+{
+ int i;
+ plane_t *p;
+ int hash, h;
+
+ SnapPlane (normal, &dist);
+ hash = (int)fabs(dist) / 8;
+ hash &= (PLANE_HASHES-1);
+
+ // search the border bins as well
+ for (i=-1 ; i<=1 ; i++)
+ {
+ h = (hash+i)&(PLANE_HASHES-1);
+ for (p = planehash[h] ; p ; p=p->hash_chain)
+ {
+ if (PlaneEqual (p, normal, dist))
+ return p-mapplanes;
+ }
+ }
+
+ return CreateNewFloatPlane (normal, dist);
+}
+#endif
+
+/*
+================
+MapPlaneFromPoints
+================
+*/
+int MapPlaneFromPoints (vec3_t p0, vec3_t p1, vec3_t p2) {
+ vec3_t t1, t2, normal;
+ vec_t dist;
+
+ VectorSubtract (p0, p1, t1);
+ VectorSubtract (p2, p1, t2);
+ CrossProduct (t1, t2, normal);
+ VectorNormalize (normal, normal);
+
+ dist = DotProduct (p0, normal);
+
+ return FindFloatPlane (normal, dist);
+}
+
+
+//====================================================================
+
+/*
+===========
+SetBrushContents
+
+The contents on all sides of a brush should be the same
+Sets contentsShader, contents, opaque, and detail
+===========
+*/
+void SetBrushContents( bspbrush_t *b ) {
+ int contents, c2;
+ side_t *s;
+ int i;
+ qboolean mixed;
+ int allFlags;
+
+ s = &b->sides[0];
+ contents = s->contents;
+ b->contentShader = s->shaderInfo;
+ mixed = qfalse;
+
+ allFlags = 0;
+
+ for ( i=1 ; i<b->numsides ; i++, s++ ) {
+ s = &b->sides[i];
+
+ if ( !s->shaderInfo ) {
+ continue;
+ }
+
+ c2 = s->contents;
+ if (c2 != contents) {
+ mixed = qtrue;
+ }
+
+ allFlags |= s->surfaceFlags;
+ }
+
+ if ( mixed ) {
+ qprintf ("Entity %i, Brush %i: mixed face contents\n"
+ , b->entitynum, b->brushnum);
+ }
+
+ if ( ( contents & CONTENTS_DETAIL ) && ( contents & CONTENTS_STRUCTURAL ) ) {
+ _printf ("Entity %i, Brush %i: mixed CONTENTS_DETAIL and CONTENTS_STRUCTURAL\n"
+ , num_entities-1, entitySourceBrushes );
+ contents &= ~CONTENTS_DETAIL;
+ }
+
+ // the fulldetail flag will cause detail brushes to be
+ // treated like normal brushes
+ if ( fulldetail ) {
+ contents &= ~CONTENTS_DETAIL;
+ }
+
+ // all translucent brushes that aren't specirically made structural will
+ // be detail
+ if ( ( contents & CONTENTS_TRANSLUCENT ) && !( contents & CONTENTS_STRUCTURAL ) ) {
+ contents |= CONTENTS_DETAIL;
+ }
+
+ if ( contents & CONTENTS_DETAIL ) {
+ c_detail++;
+ b->detail = qtrue;
+ } else {
+ c_structural++;
+ b->detail = qfalse;
+ }
+
+ if ( contents & CONTENTS_TRANSLUCENT ) {
+ b->opaque = qfalse;
+ } else {
+ b->opaque = qtrue;
+ }
+
+ if ( contents & CONTENTS_AREAPORTAL ) {
+ c_areaportals++;
+ }
+
+ b->contents = contents;
+}
+
+
+//============================================================================
+
+/*
+=================
+AddBrushBevels
+
+Adds any additional planes necessary to allow the brush being
+built to be expanded against axial bounding boxes
+=================
+*/
+void AddBrushBevels( void ) {
+ int axis, dir;
+ int i, order;
+ side_t sidetemp;
+ side_t *s;
+ vec3_t normal;
+ float dist;
+
+ //
+ // add the axial planes
+ //
+ order = 0;
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2, order++)
+ {
+ // see if the plane is allready present
+ for ( i=0, s=buildBrush->sides ; i < buildBrush->numsides ; i++,s++ ) {
+ if (mapplanes[s->planenum].normal[axis] == dir)
+ break;
+ }
+
+ if (i == buildBrush->numsides )
+ { // add a new side
+ if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
+ Error( "MAX_BUILD_SIDES" );
+ }
+ memset( s, 0, sizeof( *s ) );
+ buildBrush->numsides++;
+ VectorClear (normal);
+ normal[axis] = dir;
+ if (dir == 1)
+ dist = buildBrush->maxs[axis];
+ else
+ dist = -buildBrush->mins[axis];
+ s->planenum = FindFloatPlane (normal, dist);
+ s->contents = buildBrush->sides[0].contents;
+ s->bevel = qtrue;
+ c_boxbevels++;
+ }
+
+ // if the plane is not in it canonical order, swap it
+ if (i != order)
+ {
+ sidetemp = buildBrush->sides[order];
+ buildBrush->sides[order] = buildBrush->sides[i];
+ buildBrush->sides[i] = sidetemp;
+ }
+ }
+ }
+
+ //
+ // add the edge bevels
+ //
+ if ( buildBrush->numsides == 6 ) {
+ return; // pure axial
+ } else {
+ int j, k, l;
+ float d;
+ winding_t *w, *w2;
+ side_t *s2;
+ vec3_t vec, vec2;
+
+ // test the non-axial plane edges
+ // this code tends to cause some problems...
+ for (i=6 ; i<buildBrush->numsides ; i++)
+ {
+ s = buildBrush->sides + i;
+ w = s->winding;
+ if (!w)
+ continue;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ k = (j+1)%w->numpoints;
+ VectorSubtract (w->p[j], w->p[k], vec);
+ if (VectorNormalize (vec, vec) < 0.5)
+ continue;
+ SnapVector (vec);
+ for (k=0 ; k<3 ; k++)
+ if ( vec[k] == -1 || vec[k] == 1)
+ break; // axial
+ if (k != 3)
+ continue; // only test non-axial edges
+
+ // try the six possible slanted axials from this edge
+ for (axis=0 ; axis <3 ; axis++)
+ {
+ for (dir=-1 ; dir <= 1 ; dir+=2)
+ {
+ // construct a plane
+ VectorClear (vec2);
+ vec2[axis] = dir;
+ CrossProduct (vec, vec2, normal);
+ if (VectorNormalize (normal, normal) < 0.5)
+ continue;
+ dist = DotProduct (w->p[j], normal);
+
+ // if all the points on all the sides are
+ // behind this plane, it is a proper edge bevel
+ for (k=0 ; k < buildBrush->numsides ; k++)
+ {
+ // if this plane has allready been used, skip it
+ if (PlaneEqual (&mapplanes[buildBrush->sides[k].planenum]
+ , normal, dist) )
+ break;
+
+ w2 = buildBrush->sides[k].winding;
+ if (!w2)
+ continue;
+ for (l=0 ; l<w2->numpoints ; l++)
+ {
+ d = DotProduct (w2->p[l], normal) - dist;
+ if (d > 0.1)
+ break; // point in front
+ }
+ if (l != w2->numpoints)
+ break;
+ }
+
+ if (k != buildBrush->numsides)
+ continue; // wasn't part of the outer hull
+ // add this plane
+ if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
+ Error( "MAX_BUILD_SIDES" );
+ }
+
+ s2 = &buildBrush->sides[buildBrush->numsides];
+ buildBrush->numsides++;
+ memset( s2, 0, sizeof( *s2 ) );
+
+ s2->planenum = FindFloatPlane (normal, dist);
+ s2->contents = buildBrush->sides[0].contents;
+ s2->bevel = qtrue;
+ c_edgebevels++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+===============
+AddBackSides
+
+fog volumes need to have inside faces created
+===============
+*/
+void AddBackSides( void ) {
+/*
+ bspbrush_t *b;
+ int i, originalSides;
+ side_t *s;
+ side_t *newSide;
+
+ b = buildBrush;
+ originalSides = b->numsides;
+ for ( i = 0 ; i < originalSides ; i++ ) {
+ s = &b->sides[i];
+ if ( !s->shaderInfo ) {
+ continue;
+ }
+ if ( !(s->shaderInfo->contents & CONTENTS_FOG) ) {
+ continue;
+ }
+
+ // duplicate the up-facing side
+ if ( mapplanes[ s->planenum ].normal[2] == 1 ) {
+ newSide = &b->sides[ b->numsides ];
+ b->numsides++;
+
+ *newSide = *s;
+ newSide->backSide = qtrue;
+ newSide->planenum = s->planenum ^ 1; // opposite side
+ }
+ }
+*/
+}
+
+/*
+===============
+FinishBrush
+
+Produces a final brush based on the buildBrush->sides array
+and links it to the current entity
+===============
+*/
+bspbrush_t *FinishBrush( void ) {
+ bspbrush_t *b;
+
+ // liquids may need to have extra sides created for back sides
+ AddBackSides();
+
+ // create windings for sides and bounds for brush
+ if ( !CreateBrushWindings( buildBrush ) ) {
+ // don't keep this brush
+ return NULL;
+ }
+
+ // brushes that will not be visible at all are forced to be detail
+ if ( buildBrush->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) )
+ {
+ buildBrush->detail = qtrue;
+ c_detail++;
+ }
+
+ //
+ // origin brushes are removed, but they set
+ // the rotation origin for the rest of the brushes
+ // in the entity. After the entire entity is parsed,
+ // the planenums and texinfos will be adjusted for
+ // the origin brush
+ //
+ if ( buildBrush->contents & CONTENTS_ORIGIN )
+ {
+ char string[32];
+ vec3_t origin;
+
+ if (num_entities == 1) {
+ _printf ("Entity %i, Brush %i: origin brushes not allowed in world\n"
+ , num_entities - 1, entitySourceBrushes);
+ return NULL;
+ }
+
+ VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
+ VectorScale (origin, 0.5, origin);
+
+ sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
+ SetKeyValue (&entities[num_entities - 1], "origin", string);
+
+ VectorCopy (origin, entities[num_entities - 1].origin);
+
+ // don't keep this brush
+ return NULL;
+ }
+
+ if ( buildBrush->contents & CONTENTS_AREAPORTAL ) {
+ if (num_entities != 1) {
+ _printf ("Entity %i, Brush %i: areaportals only allowed in world\n"
+ , num_entities - 1, entitySourceBrushes);
+ return NULL;
+ }
+ }
+
+ AddBrushBevels ();
+
+ // keep it
+ b = CopyBrush( buildBrush );
+
+ b->entitynum = num_entities-1;
+ b->brushnum = entitySourceBrushes;
+
+ b->original = b;
+
+ b->next = mapent->brushes;
+ mapent->brushes = b;
+
+ return b;
+}
+
+//======================================================================
+
+
+/*
+==================
+textureAxisFromPlane
+==================
+*/
+vec3_t baseaxis[18] =
+{
+{0,0,1}, {1,0,0}, {0,-1,0}, // floor
+{0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
+{1,0,0}, {0,1,0}, {0,0,-1}, // west wall
+{-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
+{0,1,0}, {1,0,0}, {0,0,-1}, // south wall
+{0,-1,0}, {1,0,0}, {0,0,-1} // north wall
+};
+
+void TextureAxisFromPlane(plane_t *pln, vec3_t xv, vec3_t yv)
+{
+ int bestaxis;
+ vec_t dot,best;
+ int i;
+
+ best = 0;
+ bestaxis = 0;
+
+ for (i=0 ; i<6 ; i++)
+ {
+ dot = DotProduct (pln->normal, baseaxis[i*3]);
+ if (dot > best)
+ {
+ best = dot;
+ bestaxis = i;
+ }
+ }
+
+ VectorCopy (baseaxis[bestaxis*3+1], xv);
+ VectorCopy (baseaxis[bestaxis*3+2], yv);
+}
+
+
+
+/*
+=================
+QuakeTextureVecs
+
+Creates world-to-texture mapping vecs for crappy quake plane arrangements
+=================
+*/
+void QuakeTextureVecs( plane_t *plane, vec_t shift[2], vec_t rotate, vec_t scale[2],
+ vec_t mappingVecs[2][4] ) {
+
+ vec3_t vecs[2];
+ int sv, tv;
+ vec_t ang, sinv, cosv;
+ vec_t ns, nt;
+ int i, j;
+
+ TextureAxisFromPlane(plane, vecs[0], vecs[1]);
+
+ if (!scale[0])
+ scale[0] = 1;
+ if (!scale[1])
+ scale[1] = 1;
+
+ // rotate axis
+ if (rotate == 0)
+ { sinv = 0 ; cosv = 1; }
+ else if (rotate == 90)
+ { sinv = 1 ; cosv = 0; }
+ else if (rotate == 180)
+ { sinv = 0 ; cosv = -1; }
+ else if (rotate == 270)
+ { sinv = -1 ; cosv = 0; }
+ else
+ {
+ ang = rotate / 180 * Q_PI;
+ sinv = sin(ang);
+ cosv = cos(ang);
+ }
+
+ if (vecs[0][0])
+ sv = 0;
+ else if (vecs[0][1])
+ sv = 1;
+ else
+ sv = 2;
+
+ if (vecs[1][0])
+ tv = 0;
+ else if (vecs[1][1])
+ tv = 1;
+ else
+ tv = 2;
+
+ for (i=0 ; i<2 ; i++) {
+ ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
+ nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
+ vecs[i][sv] = ns;
+ vecs[i][tv] = nt;
+ }
+
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ mappingVecs[i][j] = vecs[i][j] / scale[i];
+
+ mappingVecs[0][3] = shift[0];
+ mappingVecs[1][3] = shift[1];
+}
+
+//======================================================================
+
+/*
+=================
+ParseRawBrush
+
+Just parses the sides into buildBrush->sides[], nothing else.
+no validation, back plane removal, etc.
+
+Timo - 08/26/99
+added brush epairs parsing ( ignoring actually )
+Timo - 08/04/99
+added exclusive brush primitive parsing
+Timo - 08/08/99
+support for old brush format back in
+NOTE : it would be "cleaner" to have seperate functions to parse between old and new brushes
+=================
+*/
+void ParseRawBrush( ) {
+ side_t *side;
+ vec3_t planepts[3];
+ int planenum;
+ shaderInfo_t *si;
+ // old brushes
+ vec_t shift[2];
+ vec_t rotate;
+ vec_t scale[2];
+ char name[MAX_QPATH];
+ char shader[MAX_QPATH];
+ int flags;
+
+ buildBrush->numsides = 0;
+ buildBrush->detail = qfalse;
+
+ if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
+ MatchToken( "{" );
+
+ do
+ {
+ if (!GetToken (qtrue))
+ break;
+ if (!strcmp (token, "}") )
+ break;
+ //Timo : brush primitive : here we may have to jump over brush epairs ( only used in editor )
+ if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
+ {
+ do
+ {
+ if (strcmp (token, "(") )
+ GetToken( qfalse );
+ else
+ break;
+ GetToken( qtrue );
+ } while (1);
+ }
+ UnGetToken();
+
+ if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
+ Error( "MAX_BUILD_SIDES" );
+ }
+
+ side = &buildBrush->sides[ buildBrush->numsides ];
+ memset( side, 0, sizeof( *side ) );
+ buildBrush->numsides++;
+
+ // read the three point plane definition
+ Parse1DMatrix( 3, planepts[0] );
+ Parse1DMatrix( 3, planepts[1] );
+ Parse1DMatrix( 3, planepts[2] );
+
+ if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
+ // read the texture matrix
+ Parse2DMatrix( 2, 3, (float *)side->texMat );
+
+ // read the texturedef
+ GetToken (qfalse);
+ strcpy (name, token);
+
+ // save the shader name for retexturing
+ if ( numMapIndexedShaders == MAX_MAP_BRUSHSIDES ) {
+ Error( "MAX_MAP_BRUSHSIDES" );
+ }
+ strcpy( mapIndexedShaders[numMapIndexedShaders], name );
+ numMapIndexedShaders++;
+
+ if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
+ {
+ GetToken (qfalse);
+ shift[0] = atoi(token);
+ GetToken (qfalse);
+ shift[1] = atoi(token);
+ GetToken (qfalse);
+ rotate = atoi(token);
+ GetToken (qfalse);
+ scale[0] = atof(token);
+ GetToken (qfalse);
+ scale[1] = atof(token);
+ }
+
+ // find default flags and values
+ sprintf( shader, "textures/%s", name );
+ si = ShaderInfoForShader( shader );
+ side->shaderInfo = si;
+ side->surfaceFlags = si->surfaceFlags;
+ side->value = si->value;
+ side->contents = si->contents;
+
+ // allow override of default flags and values
+ // in Q3, the only thing you can override is DETAIL
+ if (TokenAvailable())
+ {
+ GetToken (qfalse);
+// side->contents = atoi(token);
+ flags = atoi(token);
+ if ( flags & CONTENTS_DETAIL ) {
+ side->contents |= CONTENTS_DETAIL;
+ }
+
+ GetToken (qfalse);
+// td.flags = atoi(token);
+
+ GetToken (qfalse);
+// td.value = atoi(token);
+ }
+
+
+ // find the plane number
+ planenum = MapPlaneFromPoints (planepts[0], planepts[1], planepts[2]);
+ side->planenum = planenum;
+
+ if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
+ // get the texture mapping for this texturedef / plane combination
+ QuakeTextureVecs( &mapplanes[planenum], shift, rotate, scale, side->vecs );
+
+ } while (1);
+
+ if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
+ {
+ UnGetToken();
+ MatchToken( "}" );
+ MatchToken( "}" );
+ }
+}
+
+/*
+=================
+RemoveDuplicateBrushPlanes
+
+Returns false if the brush has a mirrored set of planes,
+meaning it encloses no volume.
+Also removes planes without any normal
+=================
+*/
+qboolean RemoveDuplicateBrushPlanes( bspbrush_t * b ) {
+ int i, j, k;
+ side_t *sides;
+
+ sides = b->sides;
+
+ for ( i = 1 ; i < b->numsides ; i++ ) {
+
+ // check for a degenerate plane
+ if ( sides[i].planenum == -1) {
+ _printf ("Entity %i, Brush %i: degenerate plane\n"
+ , b->entitynum, b->brushnum);
+ // remove it
+ for ( k = i + 1 ; k < b->numsides ; k++ ) {
+ sides[k-1] = sides[k];
+ }
+ b->numsides--;
+ i--;
+ continue;
+ }
+
+ // check for duplication and mirroring
+ for ( j = 0 ; j < i ; j++ ) {
+ if ( sides[i].planenum == sides[j].planenum ) {
+ _printf ("Entity %i, Brush %i: duplicate plane\n"
+ , b->entitynum, b->brushnum);
+ // remove the second duplicate
+ for ( k = i + 1 ; k < b->numsides ; k++ ) {
+ sides[k-1] = sides[k];
+ }
+ b->numsides--;
+ i--;
+ break;
+ }
+
+ if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
+ // mirror plane, brush is invalid
+ _printf ("Entity %i, Brush %i: mirrored plane\n"
+ , b->entitynum, b->brushnum);
+ return qfalse;
+ }
+ }
+ }
+ return qtrue;
+}
+
+
+/*
+=================
+ParseBrush
+
+ qboolean parameter to true -> parse new brush primitive format ( else use old format )
+=================
+*/
+void ParseBrush (void) {
+ bspbrush_t *b;
+
+ ParseRawBrush();
+
+ buildBrush->portalareas[0] = -1;
+ buildBrush->portalareas[1] = -1;
+ buildBrush->entitynum = num_entities-1;
+ buildBrush->brushnum = entitySourceBrushes;
+
+ // if there are mirrored planes, the entire brush is invalid
+ if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
+ return;
+ }
+
+ // get the content for the entire brush
+ SetBrushContents( buildBrush );
+
+ // allow detail brushes to be removed
+ if (nodetail && (buildBrush->contents & CONTENTS_DETAIL) ) {
+ FreeBrush( buildBrush );
+ return;
+ }
+
+ // allow water brushes to be removed
+ if (nowater && (buildBrush->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) {
+ FreeBrush( buildBrush );
+ return;
+ }
+
+ b = FinishBrush( );
+ if ( !b ) {
+ return;
+ }
+}
+
+
+/*
+================
+MoveBrushesToWorld
+
+Takes all of the brushes from the current entity and
+adds them to the world's brush list.
+
+Used by func_group
+================
+*/
+void MoveBrushesToWorld (entity_t *mapent) {
+ bspbrush_t *b, *next;
+ parseMesh_t *pm;
+
+ // move brushes
+ for ( b = mapent->brushes ; b ; b = next ) {
+ next = b->next;
+
+ b->next = entities[0].brushes;
+ entities[0].brushes = b;
+ }
+ mapent->brushes = NULL;
+
+ // move patches
+ if ( mapent->patches ) {
+
+ for ( pm = mapent->patches ; pm->next ; pm = pm->next ) {
+ }
+
+ pm->next = entities[0].patches;
+ entities[0].patches = mapent->patches;
+
+ mapent->patches = NULL;
+ }
+}
+
+
+/*
+================
+AdjustBrushesForOrigin
+================
+*/
+void AdjustBrushesForOrigin( entity_t *ent ) {
+ bspbrush_t *b;
+ int i;
+ side_t *s;
+ vec_t newdist;
+ parseMesh_t *p;
+
+ for ( b = ent->brushes ; b ; b = b->next ) {
+ for (i=0 ; i<b->numsides ; i++) {
+ s = &b->sides[i];
+ newdist = mapplanes[s->planenum].dist -
+ DotProduct (mapplanes[s->planenum].normal, ent->origin);
+ s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist);
+ }
+ CreateBrushWindings(b);
+ }
+
+ for ( p = ent->patches ; p ; p = p->next ) {
+ for ( i = 0 ; i < p->mesh.width*p->mesh.height ; i++ ) {
+ VectorSubtract( p->mesh.verts[i].xyz, ent->origin, p->mesh.verts[i].xyz );
+ }
+ }
+
+}
+
+/*
+================
+ParseMapEntity
+================
+*/
+qboolean ParseMapEntity (void) {
+ epair_t *e;
+
+ if (!GetToken (qtrue))
+ return qfalse;
+
+ if (strcmp (token, "{") )
+ {
+ Error ("ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...", token, scriptline, entities[num_entities].origin[0], entities[num_entities].origin[1], entities[num_entities].origin[2]);
+ }
+
+ if (num_entities == MAX_MAP_ENTITIES)
+ Error ("num_entities == MAX_MAP_ENTITIES");
+
+ entitySourceBrushes = 0;
+
+ mapent = &entities[num_entities];
+ num_entities++;
+ memset (mapent, 0, sizeof(*mapent));
+
+ do
+ {
+ if (!GetToken (qtrue))
+ Error ("ParseEntity: EOF without closing brace");
+ if (!strcmp (token, "}") )
+ break;
+
+ if (!strcmp (token, "{") ) {
+ // parse a brush or patch
+ if (!GetToken (qtrue))
+ break;
+ if ( !strcmp( token, "patchDef2" ) ) {
+ numMapPatches++;
+ ParsePatch();
+ } else if ( !strcmp( token, "terrainDef" ) ) {
+ ParseTerrain();
+ } else if ( !strcmp( token, "brushDef" ) ) {
+ if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
+ Error("old brush format not allowed in new brush format map");
+ g_bBrushPrimit=BPRIMIT_NEWBRUSHES;
+ // parse brush primitive
+ ParseBrush();
+ }
+ else
+ {
+ if (g_bBrushPrimit==BPRIMIT_NEWBRUSHES)
+ Error("new brush format not allowed in old brush format map");
+ g_bBrushPrimit=BPRIMIT_OLDBRUSHES;
+ // parse old brush format
+ UnGetToken();
+ ParseBrush();
+ }
+ entitySourceBrushes++;
+ }
+ else
+ {
+ // parse a key / value pair
+ e = ParseEpair ();
+ e->next = mapent->epairs;
+ mapent->epairs = e;
+ }
+ } while (1);
+
+ GetVectorForKey (mapent, "origin", mapent->origin);
+
+ //
+ // if there was an origin brush, offset all of the planes and texinfo
+ // for all the brushes in the entity
+ if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) {
+ AdjustBrushesForOrigin( mapent );
+ }
+
+ // group_info entities are just for editor grouping
+ // ignored
+ // FIXME: leak!
+ if (!strcmp("group_info", ValueForKey (mapent, "classname")))
+ {
+ num_entities--;
+ return qtrue;
+ }
+
+ // group entities are just for editor convenience
+ // toss all brushes into the world entity
+ if (!strcmp ("func_group", ValueForKey (mapent, "classname")))
+ {
+ if ( !strcmp ("1", ValueForKey (mapent, "terrain"))) {
+ SetTerrainTextures();
+ }
+ MoveBrushesToWorld (mapent);
+ num_entities--;
+ return qtrue;
+ }
+
+ return qtrue;
+}
+
+//===================================================================
+
+
+/*
+================
+LoadMapFile
+================
+*/
+void LoadMapFile (char *filename) {
+ bspbrush_t *b;
+
+ qprintf ("--- LoadMapFile ---\n");
+ _printf ("Loading map file %s\n", filename);
+
+ LoadScriptFile (filename);
+
+ num_entities = 0;
+ numMapDrawSurfs = 0;
+ c_detail = 0;
+
+ g_bBrushPrimit = BPRIMIT_UNDEFINED;
+
+ // allocate a very large temporary brush for building
+ // the brushes as they are loaded
+ buildBrush = AllocBrush( MAX_BUILD_SIDES );
+
+ while (ParseMapEntity ())
+ {
+ }
+
+ ClearBounds (map_mins, map_maxs);
+ for ( b = entities[0].brushes ; b ; b=b->next ) {
+ AddPointToBounds( b->mins, map_mins, map_maxs );
+ AddPointToBounds( b->maxs, map_mins, map_maxs );
+ }
+
+ qprintf ("%5i total world brushes\n", CountBrushList( entities[0].brushes ) );
+ qprintf ("%5i detail brushes\n", c_detail );
+ qprintf ("%5i patches\n", numMapPatches);
+ qprintf ("%5i boxbevels\n", c_boxbevels);
+ qprintf ("%5i edgebevels\n", c_edgebevels);
+ qprintf ("%5i entities\n", num_entities);
+ qprintf ("%5i planes\n", nummapplanes);
+ qprintf ("%5i areaportals\n", c_areaportals);
+ qprintf ("size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
+ map_maxs[0],map_maxs[1],map_maxs[2]);
+
+ if ( fakemap ) {
+ WriteBspBrushMap ("fakemap.map", entities[0].brushes );
+ }
+
+ if ( testExpand ) {
+ TestExpandBrushes ();
+ }
+}
+
+
+//====================================================================
+
+
+/*
+================
+TestExpandBrushes
+
+Expands all the brush planes and saves a new map out to
+allow visual inspection of the clipping bevels
+================
+*/
+void TestExpandBrushes( void ) {
+ side_t *s;
+ int i, j;
+ bspbrush_t *brush, *list, *copy;
+ vec_t dist;
+ plane_t *plane;
+
+ list = NULL;
+
+ for ( brush = entities[0].brushes ; brush ; brush = brush->next ) {
+ copy = CopyBrush( brush );
+ copy->next = list;
+ list = copy;
+
+ // expand all the planes
+ for ( i=0 ; i<brush->numsides ; i++ ) {
+ s = brush->sides + i;
+ plane = &mapplanes[s->planenum];
+ dist = plane->dist;
+ for (j=0 ; j<3 ; j++) {
+ dist += fabs( 16 * plane->normal[j] );
+ }
+ s->planenum = FindFloatPlane( plane->normal, dist );
+ }
+
+ }
+
+ WriteBspBrushMap ( "expanded.map", entities[0].brushes );
+
+ Error ("can't proceed after expanding brushes");
+}
diff --git a/q3map/mesh.c b/q3map/mesh.c
new file mode 100755
index 0000000..c49c76b
--- /dev/null
+++ b/q3map/mesh.c
@@ -0,0 +1,682 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+/*
+===============================================================
+
+MESH SUBDIVISION
+
+===============================================================
+*/
+
+
+int originalWidths[MAX_EXPANDED_AXIS];
+int originalHeights[MAX_EXPANDED_AXIS];
+
+int neighbors[8][2] = {
+ {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
+};
+
+/*
+============
+LerpDrawVert
+============
+*/
+void LerpDrawVert( drawVert_t *a, drawVert_t *b, drawVert_t *out ) {
+ out->xyz[0] = 0.5 * (a->xyz[0] + b->xyz[0]);
+ out->xyz[1] = 0.5 * (a->xyz[1] + b->xyz[1]);
+ out->xyz[2] = 0.5 * (a->xyz[2] + b->xyz[2]);
+
+ out->st[0] = 0.5 * (a->st[0] + b->st[0]);
+ out->st[1] = 0.5 * (a->st[1] + b->st[1]);
+
+ out->lightmap[0] = 0.5 * (a->lightmap[0] + b->lightmap[0]);
+ out->lightmap[1] = 0.5 * (a->lightmap[1] + b->lightmap[1]);
+
+ out->color[0] = (a->color[0] + b->color[0]) >> 1;
+ out->color[1] = (a->color[1] + b->color[1]) >> 1;
+ out->color[2] = (a->color[2] + b->color[2]) >> 1;
+ out->color[3] = (a->color[3] + b->color[3]) >> 1;
+}
+
+
+void FreeMesh( mesh_t *m ) {
+ free( m->verts );
+ free( m );
+}
+
+void PrintMesh( mesh_t *m ) {
+ int i, j;
+
+ for ( i = 0 ; i < m->height ; i++ ) {
+ for ( j = 0 ; j < m->width ; j++ ) {
+ _printf("(%5.2f %5.2f %5.2f) "
+ , m->verts[i*m->width+j].xyz[0]
+ , m->verts[i*m->width+j].xyz[1]
+ , m->verts[i*m->width+j].xyz[2] );
+ }
+ _printf("\n");
+ }
+}
+
+
+mesh_t *CopyMesh( mesh_t *mesh ) {
+ mesh_t *out;
+ int size;
+
+ out = malloc( sizeof( *out ) );
+ out->width = mesh->width;
+ out->height = mesh->height;
+
+ size = out->width * out->height * sizeof( *out->verts );
+ out->verts = malloc( size );
+ memcpy( out->verts, mesh->verts, size );
+
+ return out;
+}
+
+
+/*
+=================
+TransposeMesh
+
+Returns a transposed copy of the mesh, freeing the original
+=================
+*/
+mesh_t *TransposeMesh( mesh_t *in ) {
+ int w, h;
+ mesh_t *out;
+
+ out = malloc( sizeof( *out ) );
+ out->width = in->height;
+ out->height = in->width;
+ out->verts = malloc( out->width * out->height * sizeof( drawVert_t ) );
+
+ for ( h = 0 ; h < in->height ; h++ ) {
+ for ( w = 0 ; w < in->width ; w++ ) {
+ out->verts[ w * in->height + h ] = in->verts[ h * in->width + w ];
+ }
+ }
+
+ FreeMesh( in );
+
+ return out;
+}
+
+void InvertMesh( mesh_t *in ) {
+ int w, h;
+ drawVert_t temp;
+
+ for ( h = 0 ; h < in->height ; h++ ) {
+ for ( w = 0 ; w < in->width / 2 ; w++ ) {
+ temp = in->verts[ h * in->width + w ];
+ in->verts[ h * in->width + w ] = in->verts[ h * in->width + in->width - 1 - w ];
+ in->verts[ h * in->width + in->width - 1 - w ] = temp;
+ }
+ }
+}
+
+/*
+=================
+MakeMeshNormals
+
+=================
+*/
+void MakeMeshNormals( mesh_t in ) {
+ int i, j, k, dist;
+ vec3_t normal;
+ vec3_t sum;
+ int count;
+ vec3_t base;
+ vec3_t delta;
+ int x, y;
+ drawVert_t *dv;
+ vec3_t around[8], temp;
+ qboolean good[8];
+ qboolean wrapWidth, wrapHeight;
+ float len;
+
+ wrapWidth = qfalse;
+ for ( i = 0 ; i < in.height ; i++ ) {
+ VectorSubtract( in.verts[i*in.width].xyz,
+ in.verts[i*in.width+in.width-1].xyz, delta );
+ len = VectorLength( delta );
+ if ( len > 1.0 ) {
+ break;
+ }
+ }
+ if ( i == in.height ) {
+ wrapWidth = qtrue;
+ }
+
+ wrapHeight = qfalse;
+ for ( i = 0 ; i < in.width ; i++ ) {
+ VectorSubtract( in.verts[i].xyz,
+ in.verts[i + (in.height-1)*in.width].xyz, delta );
+ len = VectorLength( delta );
+ if ( len > 1.0 ) {
+ break;
+ }
+ }
+ if ( i == in.width) {
+ wrapHeight = qtrue;
+ }
+
+
+ for ( i = 0 ; i < in.width ; i++ ) {
+ for ( j = 0 ; j < in.height ; j++ ) {
+ count = 0;
+ dv = &in.verts[j*in.width+i];
+ VectorCopy( dv->xyz, base );
+ for ( k = 0 ; k < 8 ; k++ ) {
+ VectorClear( around[k] );
+ good[k] = qfalse;
+
+ for ( dist = 1 ; dist <= 3 ; dist++ ) {
+ x = i + neighbors[k][0] * dist;
+ y = j + neighbors[k][1] * dist;
+ if ( wrapWidth ) {
+ if ( x < 0 ) {
+ x = in.width - 1 + x;
+ } else if ( x >= in.width ) {
+ x = 1 + x - in.width;
+ }
+ }
+ if ( wrapHeight ) {
+ if ( y < 0 ) {
+ y = in.height - 1 + y;
+ } else if ( y >= in.height ) {
+ y = 1 + y - in.height;
+ }
+ }
+
+ if ( x < 0 || x >= in.width || y < 0 || y >= in.height ) {
+ break; // edge of patch
+ }
+ VectorSubtract( in.verts[y*in.width+x].xyz, base, temp );
+ if ( VectorNormalize( temp, temp ) == 0 ) {
+ continue; // degenerate edge, get more dist
+ } else {
+ good[k] = qtrue;
+ VectorCopy( temp, around[k] );
+ break; // good edge
+ }
+ }
+ }
+
+ VectorClear( sum );
+ for ( k = 0 ; k < 8 ; k++ ) {
+ if ( !good[k] || !good[(k+1)&7] ) {
+ continue; // didn't get two points
+ }
+ CrossProduct( around[(k+1)&7], around[k], normal );
+ if ( VectorNormalize( normal, normal ) == 0 ) {
+ continue;
+ }
+ VectorAdd( normal, sum, sum );
+ count++;
+ }
+ if ( count == 0 ) {
+//_printf("bad normal\n");
+ count = 1;
+ }
+ VectorNormalize( sum, dv->normal );
+ }
+ }
+}
+
+/*
+=================
+PutMeshOnCurve
+
+Drops the aproximating points onto the curve
+=================
+*/
+void PutMeshOnCurve( mesh_t in ) {
+ int i, j, l;
+ float prev, next;
+
+ // put all the aproximating points on the curve
+ for ( i = 0 ; i < in.width ; i++ ) {
+ for ( j = 1 ; j < in.height ; j += 2 ) {
+ for ( l = 0 ; l < 3 ; l++ ) {
+ prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j+1)*in.width+i].xyz[l] ) * 0.5;
+ next = ( in.verts[j*in.width+i].xyz[l] + in.verts[(j-1)*in.width+i].xyz[l] ) * 0.5;
+ in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
+ }
+ }
+ }
+
+ for ( j = 0 ; j < in.height ; j++ ) {
+ for ( i = 1 ; i < in.width ; i += 2 ) {
+ for ( l = 0 ; l < 3 ; l++ ) {
+ prev = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i+1].xyz[l] ) * 0.5;
+ next = ( in.verts[j*in.width+i].xyz[l] + in.verts[j*in.width+i-1].xyz[l] ) * 0.5;
+ in.verts[j*in.width+i].xyz[l] = ( prev + next ) * 0.5;
+ }
+ }
+ }
+}
+
+
+/*
+=================
+SubdivideMesh
+
+=================
+*/
+mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength ) {
+ int i, j, k, l;
+ drawVert_t prev, next, mid;
+ vec3_t prevxyz, nextxyz, midxyz;
+ vec3_t delta;
+ float len;
+ mesh_t out;
+ drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
+
+ out.width = in.width;
+ out.height = in.height;
+
+ for ( i = 0 ; i < in.width ; i++ ) {
+ for ( j = 0 ; j < in.height ; j++ ) {
+ expand[j][i] = in.verts[j*in.width+i];
+ }
+ }
+
+ for ( i = 0 ; i < in.height ; i++ ) {
+ originalHeights[i] = i;
+ }
+ for ( i = 0 ; i < in.width ; i++ ) {
+ originalWidths[i] = i;
+ }
+
+ // horizontal subdivisions
+ for ( j = 0 ; j + 2 < out.width ; j += 2 ) {
+ // check subdivided midpoints against control points
+ for ( i = 0 ; i < out.height ; i++ ) {
+ for ( l = 0 ; l < 3 ; l++ ) {
+ prevxyz[l] = expand[i][j+1].xyz[l] - expand[i][j].xyz[l];
+ nextxyz[l] = expand[i][j+2].xyz[l] - expand[i][j+1].xyz[l];
+ midxyz[l] = (expand[i][j].xyz[l] + expand[i][j+1].xyz[l] * 2
+ + expand[i][j+2].xyz[l] ) * 0.25;
+ }
+
+ // if the span length is too long, force a subdivision
+ if ( VectorLength( prevxyz ) > minLength
+ || VectorLength( nextxyz ) > minLength ) {
+ break;
+ }
+
+ // see if this midpoint is off far enough to subdivide
+ VectorSubtract( expand[i][j+1].xyz, midxyz, delta );
+ len = VectorLength( delta );
+ if ( len > maxError ) {
+ break;
+ }
+ }
+
+ if ( out.width + 2 >= MAX_EXPANDED_AXIS ) {
+ break; // can't subdivide any more
+ }
+
+ if ( i == out.height ) {
+ continue; // didn't need subdivision
+ }
+
+ // insert two columns and replace the peak
+ out.width += 2;
+
+ for ( k = out.width - 1 ; k > j + 3 ; k-- ) {
+ originalWidths[k] = originalWidths[k-2];
+ }
+ originalWidths[j+3] = originalWidths[j+1];
+ originalWidths[j+2] = originalWidths[j+1];
+ originalWidths[j+1] = originalWidths[j];
+
+ for ( i = 0 ; i < out.height ; i++ ) {
+ LerpDrawVert( &expand[i][j], &expand[i][j+1], &prev );
+ LerpDrawVert( &expand[i][j+1], &expand[i][j+2], &next );
+ LerpDrawVert( &prev, &next, &mid );
+
+ for ( k = out.width - 1 ; k > j + 3 ; k-- ) {
+ expand[i][k] = expand[i][k-2];
+ }
+ expand[i][j + 1] = prev;
+ expand[i][j + 2] = mid;
+ expand[i][j + 3] = next;
+ }
+
+ // back up and recheck this set again, it may need more subdivision
+ j -= 2;
+
+ }
+
+ // vertical subdivisions
+ for ( j = 0 ; j + 2 < out.height ; j += 2 ) {
+ // check subdivided midpoints against control points
+ for ( i = 0 ; i < out.width ; i++ ) {
+ for ( l = 0 ; l < 3 ; l++ ) {
+ prevxyz[l] = expand[j+1][i].xyz[l] - expand[j][i].xyz[l];
+ nextxyz[l] = expand[j+2][i].xyz[l] - expand[j+1][i].xyz[l];
+ midxyz[l] = (expand[j][i].xyz[l] + expand[j+1][i].xyz[l] * 2
+ + expand[j+2][i].xyz[l] ) * 0.25;
+ }
+
+ // if the span length is too long, force a subdivision
+ if ( VectorLength( prevxyz ) > minLength
+ || VectorLength( nextxyz ) > minLength ) {
+ break;
+ }
+ // see if this midpoint is off far enough to subdivide
+ VectorSubtract( expand[j+1][i].xyz, midxyz, delta );
+ len = VectorLength( delta );
+ if ( len > maxError ) {
+ break;
+ }
+ }
+
+ if ( out.height + 2 >= MAX_EXPANDED_AXIS ) {
+ break; // can't subdivide any more
+ }
+
+ if ( i == out.width ) {
+ continue; // didn't need subdivision
+ }
+
+ // insert two columns and replace the peak
+ out.height += 2;
+
+ for ( k = out.height - 1 ; k > j + 3 ; k-- ) {
+ originalHeights[k] = originalHeights[k-2];
+ }
+ originalHeights[j+3] = originalHeights[j+1];
+ originalHeights[j+2] = originalHeights[j+1];
+ originalHeights[j+1] = originalHeights[j];
+
+ for ( i = 0 ; i < out.width ; i++ ) {
+ LerpDrawVert( &expand[j][i], &expand[j+1][i], &prev );
+ LerpDrawVert( &expand[j+1][i], &expand[j+2][i], &next );
+ LerpDrawVert( &prev, &next, &mid );
+
+ for ( k = out.height - 1 ; k > j + 3 ; k-- ) {
+ expand[k][i] = expand[k-2][i];
+ }
+ expand[j+1][i] = prev;
+ expand[j+2][i] = mid;
+ expand[j+3][i] = next;
+ }
+
+ // back up and recheck this set again, it may need more subdivision
+ j -= 2;
+
+ }
+
+ // collapse the verts
+
+ out.verts = &expand[0][0];
+ for ( i = 1 ; i < out.height ; i++ ) {
+ memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
+ }
+
+ return CopyMesh(&out);
+}
+
+/*
+================
+ProjectPointOntoVector
+================
+*/
+void ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_t vProj )
+{
+ vec3_t pVec, vec;
+
+ VectorSubtract( point, vStart, pVec );
+ VectorSubtract( vEnd, vStart, vec );
+ VectorNormalize( vec, vec );
+ // project onto the directional vector for this segment
+ VectorMA( vStart, DotProduct( pVec, vec ), vec, vProj );
+}
+
+/*
+================
+RemoveLinearMeshColumsRows
+================
+*/
+mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in ) {
+ int i, j, k;
+ float len, maxLength;
+ vec3_t proj, dir;
+ mesh_t out;
+ drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
+
+ out.width = in->width;
+ out.height = in->height;
+
+ for ( i = 0 ; i < in->width ; i++ ) {
+ for ( j = 0 ; j < in->height ; j++ ) {
+ expand[j][i] = in->verts[j*in->width+i];
+ }
+ }
+
+ for ( j = 1 ; j < out.width - 1; j++ ) {
+ maxLength = 0;
+ for ( i = 0 ; i < out.height ; i++ ) {
+ ProjectPointOntoVector(expand[i][j].xyz, expand[i][j-1].xyz, expand[i][j+1].xyz, proj);
+ VectorSubtract(expand[i][j].xyz, proj, dir);
+ len = VectorLength(dir);
+ if (len > maxLength) {
+ maxLength = len;
+ }
+ }
+ if (maxLength < 0.1)
+ {
+ out.width--;
+ for ( i = 0 ; i < out.height ; i++ ) {
+ for (k = j; k < out.width; k++) {
+ expand[i][k] = expand[i][k+1];
+ }
+ }
+ for (k = j; k < out.width; k++) {
+ originalWidths[k] = originalWidths[k+1];
+ }
+ j--;
+ }
+ }
+ for ( j = 1 ; j < out.height - 1; j++ ) {
+ maxLength = 0;
+ for ( i = 0 ; i < out.width ; i++ ) {
+ ProjectPointOntoVector(expand[j][i].xyz, expand[j-1][i].xyz, expand[j+1][i].xyz, proj);
+ VectorSubtract(expand[j][i].xyz, proj, dir);
+ len = VectorLength(dir);
+ if (len > maxLength) {
+ maxLength = len;
+ }
+ }
+ if (maxLength < 0.1)
+ {
+ out.height--;
+ for ( i = 0 ; i < out.width ; i++ ) {
+ for (k = j; k < out.height; k++) {
+ expand[k][i] = expand[k+1][i];
+ }
+ }
+ for (k = j; k < out.height; k++) {
+ originalHeights[k] = originalHeights[k+1];
+ }
+ j--;
+ }
+ }
+ // collapse the verts
+ out.verts = &expand[0][0];
+ for ( i = 1 ; i < out.height ; i++ ) {
+ memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
+ }
+
+ return CopyMesh(&out);
+}
+
+/*
+============
+LerpDrawVertAmount
+============
+*/
+void LerpDrawVertAmount( drawVert_t *a, drawVert_t *b, float amount, drawVert_t *out ) {
+ out->xyz[0] = a->xyz[0] + amount * (b->xyz[0] - a->xyz[0]);
+ out->xyz[1] = a->xyz[1] + amount * (b->xyz[1] - a->xyz[1]);
+ out->xyz[2] = a->xyz[2] + amount * (b->xyz[2] - a->xyz[2]);
+
+ out->st[0] = a->st[0] + amount * (b->st[0] - a->st[0]);
+ out->st[1] = a->st[1] + amount * (b->st[1] - a->st[1]);
+
+ out->lightmap[0] = a->lightmap[0] + amount * (b->lightmap[0] - a->lightmap[0]);
+ out->lightmap[1] = a->lightmap[1] + amount * (b->lightmap[1] - a->lightmap[1]);
+
+ out->color[0] = a->color[0] + amount * (b->color[0] - a->color[0]);
+ out->color[1] = a->color[1] + amount * (b->color[1] - a->color[1]);
+ out->color[2] = a->color[2] + amount * (b->color[2] - a->color[2]);
+ out->color[3] = a->color[3] + amount * (b->color[3] - a->color[3]);
+
+ out->normal[0] = a->normal[0] + amount * (b->normal[0] - a->normal[0]);
+ out->normal[1] = a->normal[1] + amount * (b->normal[1] - a->normal[1]);
+ out->normal[2] = a->normal[2] + amount * (b->normal[2] - a->normal[2]);
+ VectorNormalize(out->normal, out->normal);
+}
+
+/*
+=================
+SubdivideMeshQuads
+=================
+*/
+mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int widthtable[], int heighttable[]) {
+ int i, j, k, w, h, maxsubdivisions, subdivisions;
+ vec3_t dir;
+ float length, maxLength, amount;
+ mesh_t out;
+ drawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS];
+
+ out.width = in->width;
+ out.height = in->height;
+
+ for ( i = 0 ; i < in->width ; i++ ) {
+ for ( j = 0 ; j < in->height ; j++ ) {
+ expand[j][i] = in->verts[j*in->width+i];
+ }
+ }
+
+ if (maxsize > MAX_EXPANDED_AXIS)
+ Error("SubdivideMeshQuads: maxsize > MAX_EXPANDED_AXIS");
+
+ // horizontal subdivisions
+
+ maxsubdivisions = (maxsize - in->width) / (in->width - 1);
+
+ for ( w = 0, j = 0 ; w < in->width - 1; w++, j += subdivisions + 1) {
+ maxLength = 0;
+ for ( i = 0 ; i < out.height ; i++ ) {
+ VectorSubtract(expand[i][j+1].xyz, expand[i][j].xyz, dir);
+ length = VectorLength( dir );
+ if (length > maxLength) {
+ maxLength = length;
+ }
+ }
+
+ subdivisions = (int) (maxLength / minLength);
+ if (subdivisions > maxsubdivisions)
+ subdivisions = maxsubdivisions;
+
+ widthtable[w] = subdivisions + 1;
+ if (subdivisions <= 0)
+ continue;
+
+ out.width += subdivisions;
+
+ for ( k = out.width - 1; k >= j + subdivisions; k-- ) {
+ originalWidths[k] = originalWidths[k-subdivisions];
+ }
+ for (k = 1; k <= subdivisions; k++) {
+ originalWidths[j+k] = originalWidths[j];
+ }
+
+ for ( i = 0 ; i < out.height ; i++ ) {
+ for ( k = out.width - 1 ; k > j + subdivisions; k-- ) {
+ expand[i][k] = expand[i][k-subdivisions];
+ }
+ for (k = 1; k <= subdivisions; k++)
+ {
+ amount = (float) k / (subdivisions + 1);
+ LerpDrawVertAmount(&expand[i][j], &expand[i][j+subdivisions+1], amount, &expand[i][j+k]);
+ }
+ }
+ }
+
+ maxsubdivisions = (maxsize - in->height) / (in->height - 1);
+
+ for ( h = 0, j = 0 ; h < in->height - 1; h++, j += subdivisions + 1) {
+ maxLength = 0;
+ for ( i = 0 ; i < out.width ; i++ ) {
+ VectorSubtract(expand[j+1][i].xyz, expand[j][i].xyz, dir);
+ length = VectorLength( dir );
+ if (length > maxLength) {
+ maxLength = length;
+ }
+ }
+
+ subdivisions = (int) (maxLength / minLength);
+ if (subdivisions > maxsubdivisions)
+ subdivisions = maxsubdivisions;
+
+ heighttable[h] = subdivisions + 1;
+ if (subdivisions <= 0)
+ continue;
+
+ out.height += subdivisions;
+
+ for ( k = out.height - 1; k >= j + subdivisions; k-- ) {
+ originalHeights[k] = originalHeights[k-subdivisions];
+ }
+ for (k = 1; k <= subdivisions; k++) {
+ originalHeights[j+k] = originalHeights[j];
+ }
+
+ for ( i = 0 ; i < out.width ; i++ ) {
+ for ( k = out.height - 1 ; k > j + subdivisions; k-- ) {
+ expand[k][i] = expand[k-subdivisions][i];
+ }
+ for (k = 1; k <= subdivisions; k++)
+ {
+ amount = (float) k / (subdivisions + 1);
+ LerpDrawVertAmount(&expand[j][i], &expand[j+subdivisions+1][i], amount, &expand[j+k][i]);
+ }
+ }
+ }
+
+ // collapse the verts
+ out.verts = &expand[0][0];
+ for ( i = 1 ; i < out.height ; i++ ) {
+ memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(drawVert_t) );
+ }
+
+ return CopyMesh(&out);
+}
diff --git a/q3map/mesh.h b/q3map/mesh.h
new file mode 100755
index 0000000..06f5684
--- /dev/null
+++ b/q3map/mesh.h
@@ -0,0 +1,48 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+
+// mesh.h
+
+
+typedef struct {
+ int width, height;
+ drawVert_t *verts;
+} mesh_t;
+
+#define MAX_EXPANDED_AXIS 128
+
+extern int originalWidths[MAX_EXPANDED_AXIS];
+extern int originalHeights[MAX_EXPANDED_AXIS];
+
+void FreeMesh( mesh_t *m );
+mesh_t *CopyMesh( mesh_t *mesh );
+void PrintMesh( mesh_t *m );
+mesh_t *TransposeMesh( mesh_t *in );
+void InvertMesh( mesh_t *m );
+mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength );
+mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int widthtable[], int heighttable[]);
+mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in );
+void MakeMeshNormals( mesh_t in );
+void PutMeshOnCurve( mesh_t in );
+
+
+void MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up);
diff --git a/q3map/misc_model.c b/q3map/misc_model.c
new file mode 100755
index 0000000..c83e7e7
--- /dev/null
+++ b/q3map/misc_model.c
@@ -0,0 +1,472 @@
+/*
+===========================================================================
+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 "qbsp.h"
+#include "aselib.h"
+#ifdef _WIN32
+#ifdef _TTIMOBUILD
+#include "pakstuff.h"
+#else
+#include "../libs/pakstuff.h"
+#endif
+#endif
+
+
+typedef struct {
+ char modelName[1024];
+ md3Header_t *header;
+} loadedModel_t;
+
+int c_triangleModels;
+int c_triangleSurfaces;
+int c_triangleVertexes;
+int c_triangleIndexes;
+
+
+#define MAX_LOADED_MODELS 1024
+loadedModel_t loadedModels[MAX_LOADED_MODELS];
+int numLoadedModels;
+
+/*
+=================
+R_LoadMD3
+=================
+*/
+#define LL(x) x=LittleLong(x)
+md3Header_t *R_LoadMD3( const char *mod_name ) {
+ int i, j;
+ md3Header_t *md3;
+ md3Frame_t *frame;
+ md3Surface_t *surf;
+ md3Triangle_t *tri;
+ md3St_t *st;
+ md3XyzNormal_t *xyz;
+ int version;
+ char filename[1024];
+ int len;
+
+ sprintf( filename, "%s%s", gamedir, mod_name );
+ len = TryLoadFile( filename, (void **)&md3 );
+#ifdef _WIN32
+ if ( len <= 0 ) {
+ len = PakLoadAnyFile(filename, (void **)&md3);
+ }
+#endif
+ if ( len <= 0 ) {
+ return NULL;
+ }
+
+ version = LittleLong (md3->version);
+ if (version != MD3_VERSION) {
+ _printf( "R_LoadMD3: %s has wrong version (%i should be %i)\n",
+ mod_name, version, MD3_VERSION);
+ return NULL;
+ }
+
+ LL(md3->ident);
+ LL(md3->version);
+ LL(md3->numFrames);
+ LL(md3->numTags);
+ LL(md3->numSurfaces);
+ LL(md3->numSkins);
+ LL(md3->ofsFrames);
+ LL(md3->ofsTags);
+ LL(md3->ofsSurfaces);
+ LL(md3->ofsEnd);
+
+ if ( md3->numFrames < 1 ) {
+ _printf( "R_LoadMD3: %s has no frames\n", mod_name );
+ return NULL;
+ }
+
+ // we don't need to swap tags in the renderer, they aren't used
+
+ // swap all the frames
+ frame = (md3Frame_t *) ( (byte *)md3 + md3->ofsFrames );
+ for ( i = 0 ; i < md3->numFrames ; i++, frame++) {
+ frame->radius = LittleFloat( frame->radius );
+ for ( j = 0 ; j < 3 ; j++ ) {
+ frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] );
+ frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] );
+ frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] );
+ }
+ }
+
+ // swap all the surfaces
+ surf = (md3Surface_t *) ( (byte *)md3 + md3->ofsSurfaces );
+ for ( i = 0 ; i < md3->numSurfaces ; i++) {
+
+ LL(surf->ident);
+ LL(surf->flags);
+ LL(surf->numFrames);
+ LL(surf->numShaders);
+ LL(surf->numTriangles);
+ LL(surf->ofsTriangles);
+ LL(surf->numVerts);
+ LL(surf->ofsShaders);
+ LL(surf->ofsSt);
+ LL(surf->ofsXyzNormals);
+ LL(surf->ofsEnd);
+
+ if ( surf->numVerts > SHADER_MAX_VERTEXES ) {
+ Error ("R_LoadMD3: %s has more than %i verts on a surface (%i)",
+ mod_name, SHADER_MAX_VERTEXES, surf->numVerts );
+ }
+ if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) {
+ Error ("R_LoadMD3: %s has more than %i triangles on a surface (%i)",
+ mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles );
+ }
+
+ // swap all the triangles
+ tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
+ for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
+ LL(tri->indexes[0]);
+ LL(tri->indexes[1]);
+ LL(tri->indexes[2]);
+ }
+
+ // swap all the ST
+ st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
+ for ( j = 0 ; j < surf->numVerts ; j++, st++ ) {
+ st->st[0] = LittleFloat( st->st[0] );
+ st->st[1] = LittleFloat( st->st[1] );
+ }
+
+ // swap all the XyzNormals
+ xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
+ for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ )
+ {
+ xyz->xyz[0] = LittleShort( xyz->xyz[0] );
+ xyz->xyz[1] = LittleShort( xyz->xyz[1] );
+ xyz->xyz[2] = LittleShort( xyz->xyz[2] );
+
+ xyz->normal = LittleShort( xyz->normal );
+ }
+
+
+ // find the next surface
+ surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
+ }
+
+ return md3;
+}
+
+
+/*
+================
+LoadModel
+================
+*/
+md3Header_t *LoadModel( const char *modelName ) {
+ int i;
+ loadedModel_t *lm;
+
+ // see if we already have it loaded
+ for ( i = 0, lm = loadedModels ; i < numLoadedModels ; i++, lm++ ) {
+ if ( !strcmp( modelName, lm->modelName ) ) {
+ return lm->header;
+ }
+ }
+
+ // load it
+ if ( numLoadedModels == MAX_LOADED_MODELS ) {
+ Error( "MAX_LOADED_MODELS" );
+ }
+ numLoadedModels++;
+
+ strcpy( lm->modelName, modelName );
+
+ lm->header = R_LoadMD3( modelName );
+
+ return lm->header;
+}
+
+/*
+============
+InsertMD3Model
+
+Convert a model entity to raw geometry surfaces and insert it in the tree
+============
+*/
+void InsertMD3Model( const char *modelName, vec3_t origin, float angle, tree_t *tree ) {
+ int i, j;
+ md3Header_t *md3;
+ md3Surface_t *surf;
+ md3Shader_t *shader;
+ md3Triangle_t *tri;
+ md3St_t *st;
+ md3XyzNormal_t *xyz;
+ drawVert_t *outv;
+ float lat, lng;
+ float angleCos, angleSin;
+ mapDrawSurface_t *out;
+ vec3_t temp;
+
+ angle = angle / 180 * Q_PI;
+ angleCos = cos( angle );
+ angleSin = sin( angle );
+
+ // load the model
+ md3 = LoadModel( modelName );
+ if ( !md3 ) {
+ return;
+ }
+
+ // each md3 surface will become a new bsp surface
+
+ c_triangleModels++;
+ c_triangleSurfaces += md3->numSurfaces;
+
+ // expand, translate, and rotate the vertexes
+ // swap all the surfaces
+ surf = (md3Surface_t *) ( (byte *)md3 + md3->ofsSurfaces );
+ for ( i = 0 ; i < md3->numSurfaces ; i++) {
+ // allocate a surface
+ out = AllocDrawSurf();
+ out->miscModel = qtrue;
+
+ shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders );
+
+ out->shaderInfo = ShaderInfoForShader( shader->name );
+
+ out->numVerts = surf->numVerts;
+ out->verts = malloc( out->numVerts * sizeof( out->verts[0] ) );
+
+ out->numIndexes = surf->numTriangles * 3;
+ out->indexes = malloc( out->numIndexes * sizeof( out->indexes[0] ) );
+
+ out->lightmapNum = -1;
+ out->fogNum = -1;
+
+ // emit the indexes
+ c_triangleIndexes += surf->numTriangles * 3;
+ tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles );
+ for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) {
+ out->indexes[j*3+0] = tri->indexes[0];
+ out->indexes[j*3+1] = tri->indexes[1];
+ out->indexes[j*3+2] = tri->indexes[2];
+ }
+
+ // emit the vertexes
+ st = (md3St_t *) ( (byte *)surf + surf->ofsSt );
+ xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals );
+
+ c_triangleVertexes += surf->numVerts;
+ for ( j = 0 ; j < surf->numVerts ; j++, st++, xyz++ ) {
+ outv = &out->verts[ j ];
+
+ outv->st[0] = st->st[0];
+ outv->st[1] = st->st[1];
+
+ outv->lightmap[0] = 0;
+ outv->lightmap[1] = 0;
+
+ // the colors will be set by the lighting pass
+ outv->color[0] = 255;
+ outv->color[1] = 255;
+ outv->color[2] = 255;
+ outv->color[3] = 255;
+
+ outv->xyz[0] = origin[0] + MD3_XYZ_SCALE * ( xyz->xyz[0] * angleCos - xyz->xyz[1] * angleSin );
+ outv->xyz[1] = origin[1] + MD3_XYZ_SCALE * ( xyz->xyz[0] * angleSin + xyz->xyz[1] * angleCos );
+ outv->xyz[2] = origin[2] + MD3_XYZ_SCALE * ( xyz->xyz[2] );
+
+ // decode the lat/lng normal to a 3 float normal
+ lat = ( xyz->normal >> 8 ) & 0xff;
+ lng = ( xyz->normal & 0xff );
+ lat *= Q_PI/128;
+ lng *= Q_PI/128;
+
+ temp[0] = cos(lat) * sin(lng);
+ temp[1] = sin(lat) * sin(lng);
+ temp[2] = cos(lng);
+
+ // rotate the normal
+ outv->normal[0] = temp[0] * angleCos - temp[1] * angleSin;
+ outv->normal[1] = temp[0] * angleSin + temp[1] * angleCos;
+ outv->normal[2] = temp[2];
+ }
+
+ // find the next surface
+ surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd );
+ }
+
+}
+
+//==============================================================================
+
+
+/*
+============
+InsertASEModel
+
+Convert a model entity to raw geometry surfaces and insert it in the tree
+============
+*/
+void InsertASEModel( const char *modelName, vec3_t origin, float angle, tree_t *tree ) {
+ int i, j;
+ drawVert_t *outv;
+ float angleCos, angleSin;
+ mapDrawSurface_t *out;
+ int numSurfaces;
+ const char *name;
+ polyset_t *pset;
+ int numFrames;
+ char filename[1024];
+
+ sprintf( filename, "%s%s", gamedir, modelName );
+
+ angle = angle / 180 * Q_PI;
+ angleCos = cos( angle );
+ angleSin = sin( angle );
+
+ // load the model
+ ASE_Load( filename, qfalse, qfalse );
+
+ // each ase surface will become a new bsp surface
+ numSurfaces = ASE_GetNumSurfaces();
+
+ c_triangleModels++;
+ c_triangleSurfaces += numSurfaces;
+
+ // expand, translate, and rotate the vertexes
+ // swap all the surfaces
+ for ( i = 0 ; i < numSurfaces ; i++) {
+ name = ASE_GetSurfaceName( i );
+
+ pset = ASE_GetSurfaceAnimation( i, &numFrames, -1, -1, -1 );
+ if ( !name || !pset ) {
+ continue;
+ }
+
+ // allocate a surface
+ out = AllocDrawSurf();
+ out->miscModel = qtrue;
+
+ out->shaderInfo = ShaderInfoForShader( pset->materialname );
+
+ out->numVerts = 3 * pset->numtriangles;
+ out->verts = malloc( out->numVerts * sizeof( out->verts[0] ) );
+
+ out->numIndexes = 3 * pset->numtriangles;
+ out->indexes = malloc( out->numIndexes * sizeof( out->indexes[0] ) );
+
+ out->lightmapNum = -1;
+ out->fogNum = -1;
+
+ // emit the indexes
+ c_triangleIndexes += out->numIndexes;
+ for ( j = 0 ; j < out->numIndexes ; j++ ) {
+ out->indexes[j] = j;
+ }
+
+ // emit the vertexes
+ c_triangleVertexes += out->numVerts;
+ for ( j = 0 ; j < out->numVerts ; j++ ) {
+ int index;
+ triangle_t *tri;
+
+ index = j % 3;
+ tri = &pset->triangles[ j / 3 ];
+
+ outv = &out->verts[ j ];
+
+ outv->st[0] = tri->texcoords[index][0];
+ outv->st[1] = tri->texcoords[index][1];
+
+ outv->lightmap[0] = 0;
+ outv->lightmap[1] = 0;
+
+ // the colors will be set by the lighting pass
+ outv->color[0] = 255;
+ outv->color[1] = 255;
+ outv->color[2] = 255;
+ outv->color[3] = 255;
+
+ outv->xyz[0] = origin[0] + tri->verts[index][0];
+ outv->xyz[1] = origin[1] + tri->verts[index][1];
+ outv->xyz[2] = origin[2] + tri->verts[index][2];
+
+ // rotate the normal
+ outv->normal[0] = tri->normals[index][0];
+ outv->normal[1] = tri->normals[index][1];
+ outv->normal[2] = tri->normals[index][2];
+ }
+ }
+
+}
+
+
+//==============================================================================
+
+
+
+/*
+=====================
+AddTriangleModels
+=====================
+*/
+void AddTriangleModels( tree_t *tree ) {
+ int entity_num;
+ entity_t *entity;
+
+ qprintf("----- AddTriangleModels -----\n");
+
+ for ( entity_num=1 ; entity_num< num_entities ; entity_num++ ) {
+ entity = &entities[entity_num];
+
+ // convert misc_models into raw geometry
+ if ( !Q_stricmp( "misc_model", ValueForKey( entity, "classname" ) ) ) {
+ const char *model;
+ vec3_t origin;
+ float angle;
+
+ // get the angle for rotation FIXME: support full matrix positioning
+ angle = FloatForKey( entity, "angle" );
+
+ GetVectorForKey( entity, "origin", origin );
+
+ model = ValueForKey( entity, "model" );
+ if ( !model[0] ) {
+ _printf("WARNING: misc_model at %i %i %i without a model key\n", (int)origin[0],
+ (int)origin[1], (int)origin[2] );
+ continue;
+ }
+ if ( strstr( model, ".md3" ) || strstr( model, ".MD3" ) ) {
+ InsertMD3Model( model, origin, angle, tree );
+ continue;
+ }
+ if ( strstr( model, ".ase" ) || strstr( model, ".ASE" ) ) {
+ InsertASEModel( model, origin, angle, tree );
+ continue;
+ }
+ _printf( "Unknown misc_model type: %s\n", model );
+ continue;
+ }
+ }
+
+ qprintf( "%5i triangle models\n", c_triangleModels );
+ qprintf( "%5i triangle surfaces\n", c_triangleSurfaces );
+ qprintf( "%5i triangle vertexes\n", c_triangleVertexes );
+ qprintf( "%5i triangle indexes\n", c_triangleIndexes );
+}
+
diff --git a/q3map/nodraw.c b/q3map/nodraw.c
new file mode 100755
index 0000000..9931f27
--- /dev/null
+++ b/q3map/nodraw.c
@@ -0,0 +1,47 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+vec3_t draw_mins, draw_maxs;
+qboolean drawflag;
+
+void Draw_ClearWindow (void)
+{
+}
+
+//============================================================
+
+#define GLSERV_PORT 25001
+
+
+void GLS_BeginScene (void)
+{
+}
+
+void GLS_Winding (winding_t *w, int code)
+{
+}
+
+void GLS_EndScene (void)
+{
+}
diff --git a/q3map/patch.c b/q3map/patch.c
new file mode 100755
index 0000000..5dbd354
--- /dev/null
+++ b/q3map/patch.c
@@ -0,0 +1,286 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+void PrintCtrl( vec3_t ctrl[9] ) {
+ int i, j;
+
+ for ( i = 0 ; i < 3 ; i++ ) {
+ for ( j = 0 ; j < 3 ; j++ ) {
+ _printf("(%5.2f %5.2f %5.2f) ", ctrl[i*3+j][0], ctrl[i*3+j][1], ctrl[i*3+j][2] );
+ }
+ _printf("\n");
+ }
+}
+
+/*
+================
+DrawSurfaceForMesh
+================
+*/
+mapDrawSurface_t *DrawSurfaceForMesh( mesh_t *m ) {
+ mapDrawSurface_t *ds;
+ int i, j;
+ mesh_t *copy;
+
+ // to make valid normals for patches with degenerate edges,
+ // we need to make a copy of the mesh and put the aproximating
+ // points onto the curve
+ copy = CopyMesh( m );
+ PutMeshOnCurve( *copy );
+ MakeMeshNormals( *copy );
+ for ( j = 0 ; j < m->width ; j++ ) {
+ for ( i = 0 ; i < m->height ; i++ ) {
+ VectorCopy( copy->verts[i*m->width+j].normal, m->verts[i*m->width+j].normal );
+ }
+ }
+ FreeMesh( copy );
+
+ ds = AllocDrawSurf();
+ ds->mapBrush = NULL;
+ ds->side = NULL;
+
+ ds->patch = qtrue;
+ ds->patchWidth = m->width;
+ ds->patchHeight = m->height;
+ ds->numVerts = ds->patchWidth * ds->patchHeight;
+ ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
+ memcpy( ds->verts, m->verts, ds->numVerts * sizeof( *ds->verts ) );
+
+ ds->lightmapNum = -1;
+ ds->fogNum = -1;
+
+ return ds;
+}
+
+/*
+=================
+ParsePatch
+
+Creates a mapDrawSurface_t from the patch text
+=================
+*/
+void ParsePatch( void ) {
+ vec_t info[5];
+ int i, j;
+ parseMesh_t *pm;
+ char texture[MAX_QPATH];
+ char shader[MAX_QPATH];
+ mesh_t m;
+ drawVert_t *verts;
+ epair_t *ep;
+
+ MatchToken( "{" );
+
+ // get texture
+ GetToken (qtrue);
+ strcpy( texture, token );
+
+ // save the shader name for retexturing
+ if ( numMapIndexedShaders == MAX_MAP_BRUSHSIDES ) {
+ Error( "MAX_MAP_BRUSHSIDES" );
+ }
+ strcpy( mapIndexedShaders[numMapIndexedShaders], texture );
+ numMapIndexedShaders++;
+
+
+ Parse1DMatrix( 5, info );
+ m.width = info[0];
+ m.height = info[1];
+ m.verts = verts = malloc( m.width * m.height * sizeof( m.verts[0] ) );
+
+ if ( m.width < 0 || m.width > MAX_PATCH_SIZE
+ || m.height < 0 || m.height > MAX_PATCH_SIZE ) {
+ Error("ParsePatch: bad size");
+ }
+
+ MatchToken( "(" );
+ for ( j = 0 ; j < m.width ; j++ ) {
+ MatchToken( "(" );
+ for ( i = 0 ; i < m.height ; i++ ) {
+ Parse1DMatrix( 5, verts[i*m.width+j].xyz );
+ }
+ MatchToken( ")" );
+ }
+ MatchToken( ")" );
+
+ // if brush primitives format, we may have some epairs to ignore here
+ GetToken(qtrue);
+ if (g_bBrushPrimit!=BPRIMIT_OLDBRUSHES && strcmp(token,"}"))
+ {
+ // NOTE: we leak that!
+ ep = ParseEpair();
+ }
+ else
+ UnGetToken();
+
+ MatchToken( "}" );
+ MatchToken( "}" );
+
+ if ( noCurveBrushes ) {
+ return;
+ }
+
+ // find default flags and values
+ pm = malloc( sizeof( *pm ) );
+ memset( pm, 0, sizeof( *pm ) );
+
+ sprintf( shader, "textures/%s", texture );
+ pm->shaderInfo = ShaderInfoForShader( shader );
+ pm->mesh = m;
+
+ // link to the entity
+ pm->next = mapent->patches;
+ mapent->patches = pm;
+}
+
+
+void GrowGroup_r( int patchNum, int patchCount, const byte *bordering, byte *group ) {
+ int i;
+ const byte *row;
+
+ if ( group[patchNum] ) {
+ return;
+ }
+ group[patchNum] = 1;
+ row = bordering + patchNum * patchCount;
+ for ( i = 0 ; i < patchCount ; i++ ) {
+ if ( row[i] ) {
+ GrowGroup_r( i, patchCount, bordering, group );
+ }
+ }
+}
+
+
+/*
+=====================
+PatchMapDrawSurfs
+
+Any patches that share an edge need to choose their
+level of detail as a unit, otherwise the edges would
+pull apart.
+=====================
+*/
+void PatchMapDrawSurfs( entity_t *e ) {
+ parseMesh_t *pm;
+ parseMesh_t *check, *scan;
+ mapDrawSurface_t *ds;
+ int patchCount, groupCount;
+ int i, j, k, l, c1, c2;
+ drawVert_t *v1, *v2;
+ vec3_t bounds[2];
+ byte *bordering;
+ parseMesh_t *meshes[MAX_MAP_DRAW_SURFS];
+ qboolean grouped[MAX_MAP_DRAW_SURFS];
+ byte group[MAX_MAP_DRAW_SURFS];
+
+ qprintf( "----- PatchMapDrawSurfs -----\n" );
+
+ patchCount = 0;
+ for ( pm = e->patches ; pm ; pm = pm->next ) {
+ meshes[patchCount] = pm;
+ patchCount++;
+ }
+
+ if ( !patchCount ) {
+ return;
+ }
+ bordering = malloc( patchCount * patchCount );
+ memset( bordering, 0, patchCount * patchCount );
+
+ // build the bordering matrix
+ for ( k = 0 ; k < patchCount ; k++ ) {
+ bordering[k*patchCount+k] = 1;
+
+ for ( l = k+1 ; l < patchCount ; l++ ) {
+ check = meshes[k];
+ scan = meshes[l];
+ c1 = scan->mesh.width * scan->mesh.height;
+ v1 = scan->mesh.verts;
+
+ for ( i = 0 ; i < c1 ; i++, v1++ ) {
+ c2 = check->mesh.width * check->mesh.height;
+ v2 = check->mesh.verts;
+ for ( j = 0 ; j < c2 ; j++, v2++ ) {
+ if ( fabs( v1->xyz[0] - v2->xyz[0] ) < 1.0
+ && fabs( v1->xyz[1] - v2->xyz[1] ) < 1.0
+ && fabs( v1->xyz[2] - v2->xyz[2] ) < 1.0 ) {
+ break;
+ }
+ }
+ if ( j != c2 ) {
+ break;
+ }
+ }
+ if ( i != c1 ) {
+ // we have a connection
+ bordering[k*patchCount+l] =
+ bordering[l*patchCount+k] = 1;
+ } else {
+ // no connection
+ bordering[k*patchCount+l] =
+ bordering[l*patchCount+k] = 0;
+ }
+
+ }
+ }
+
+ // build groups
+ memset( grouped, 0, sizeof(grouped) );
+ groupCount = 0;
+ for ( i = 0 ; i < patchCount ; i++ ) {
+ if ( !grouped[i] ) {
+ groupCount++;
+ }
+
+ // recursively find all patches that belong in the same group
+ memset( group, 0, patchCount );
+ GrowGroup_r( i, patchCount, bordering, group );
+
+ // bound them
+ ClearBounds( bounds[0], bounds[1] );
+ for ( j = 0 ; j < patchCount ; j++ ) {
+ if ( group[j] ) {
+ grouped[j] = qtrue;
+ scan = meshes[j];
+ c1 = scan->mesh.width * scan->mesh.height;
+ v1 = scan->mesh.verts;
+ for ( k = 0 ; k < c1 ; k++, v1++ ) {
+ AddPointToBounds( v1->xyz, bounds[0], bounds[1] );
+ }
+ }
+ }
+
+ // create drawsurf
+ scan = meshes[i];
+ scan->grouped = qtrue;
+ ds = DrawSurfaceForMesh( &scan->mesh );
+ ds->shaderInfo = scan->shaderInfo;
+ VectorCopy( bounds[0], ds->lightmapVecs[0] );
+ VectorCopy( bounds[1], ds->lightmapVecs[1] );
+ }
+
+ qprintf( "%5i patches\n", patchCount );
+ qprintf( "%5i patch LOD groups\n", groupCount );
+}
+
diff --git a/q3map/portals.c b/q3map/portals.c
new file mode 100755
index 0000000..dee708f
--- /dev/null
+++ b/q3map/portals.c
@@ -0,0 +1,843 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+int c_active_portals;
+int c_peak_portals;
+int c_boundary;
+int c_boundary_sides;
+
+/*
+===========
+AllocPortal
+===========
+*/
+portal_t *AllocPortal (void)
+{
+ portal_t *p;
+
+ if (numthreads == 1)
+ c_active_portals++;
+ if (c_active_portals > c_peak_portals)
+ c_peak_portals = c_active_portals;
+
+ p = malloc (sizeof(portal_t));
+ memset (p, 0, sizeof(portal_t));
+
+ return p;
+}
+
+void FreePortal (portal_t *p)
+{
+ if (p->winding)
+ FreeWinding (p->winding);
+ if (numthreads == 1)
+ c_active_portals--;
+ free (p);
+}
+
+//==============================================================
+
+/*
+=============
+Portal_Passable
+
+Returns true if the portal has non-opaque leafs on both sides
+=============
+*/
+qboolean Portal_Passable(portal_t *p) {
+ if (!p->onnode) {
+ return qfalse; // to global outsideleaf
+ }
+
+ if (p->nodes[0]->planenum != PLANENUM_LEAF
+ || p->nodes[1]->planenum != PLANENUM_LEAF) {
+ Error ("Portal_EntityFlood: not a leaf");
+ }
+
+ if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) {
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+
+//=============================================================================
+
+int c_tinyportals;
+
+/*
+=============
+AddPortalToNodes
+=============
+*/
+void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
+{
+ if (p->nodes[0] || p->nodes[1])
+ Error ("AddPortalToNode: allready included");
+
+ p->nodes[0] = front;
+ p->next[0] = front->portals;
+ front->portals = p;
+
+ p->nodes[1] = back;
+ p->next[1] = back->portals;
+ back->portals = p;
+}
+
+
+/*
+=============
+RemovePortalFromNode
+=============
+*/
+void RemovePortalFromNode (portal_t *portal, node_t *l)
+{
+ portal_t **pp, *t;
+
+// remove reference to the current portal
+ pp = &l->portals;
+ while (1)
+ {
+ t = *pp;
+ if (!t)
+ Error ("RemovePortalFromNode: portal not in leaf");
+
+ if ( t == portal )
+ break;
+
+ if (t->nodes[0] == l)
+ pp = &t->next[0];
+ else if (t->nodes[1] == l)
+ pp = &t->next[1];
+ else
+ Error ("RemovePortalFromNode: portal not bounding leaf");
+ }
+
+ if (portal->nodes[0] == l)
+ {
+ *pp = portal->next[0];
+ portal->nodes[0] = NULL;
+ }
+ else if (portal->nodes[1] == l)
+ {
+ *pp = portal->next[1];
+ portal->nodes[1] = NULL;
+ }
+}
+
+//============================================================================
+
+void PrintPortal (portal_t *p)
+{
+ int i;
+ winding_t *w;
+
+ w = p->winding;
+ for (i=0 ; i<w->numpoints ; i++)
+ _printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
+ , w->p[i][1], w->p[i][2]);
+}
+
+/*
+================
+MakeHeadnodePortals
+
+The created portals will face the global outside_node
+================
+*/
+#define SIDESPACE 8
+void MakeHeadnodePortals (tree_t *tree)
+{
+ vec3_t bounds[2];
+ int i, j, n;
+ portal_t *p, *portals[6];
+ plane_t bplanes[6], *pl;
+ node_t *node;
+
+ node = tree->headnode;
+
+// pad with some space so there will never be null volume leafs
+ for (i=0 ; i<3 ; i++)
+ {
+ bounds[0][i] = tree->mins[i] - SIDESPACE;
+ bounds[1][i] = tree->maxs[i] + SIDESPACE;
+ if ( bounds[0][i] >= bounds[1][i] ) {
+ Error( "Backwards tree volume" );
+ }
+ }
+
+ tree->outside_node.planenum = PLANENUM_LEAF;
+ tree->outside_node.brushlist = NULL;
+ tree->outside_node.portals = NULL;
+ tree->outside_node.opaque = qfalse;
+
+ for (i=0 ; i<3 ; i++)
+ for (j=0 ; j<2 ; j++)
+ {
+ n = j*3 + i;
+
+ p = AllocPortal ();
+ portals[n] = p;
+
+ pl = &bplanes[n];
+ memset (pl, 0, sizeof(*pl));
+ if (j)
+ {
+ pl->normal[i] = -1;
+ pl->dist = -bounds[j][i];
+ }
+ else
+ {
+ pl->normal[i] = 1;
+ pl->dist = bounds[j][i];
+ }
+ p->plane = *pl;
+ p->winding = BaseWindingForPlane (pl->normal, pl->dist);
+ AddPortalToNodes (p, node, &tree->outside_node);
+ }
+
+// clip the basewindings by all the other planes
+ for (i=0 ; i<6 ; i++)
+ {
+ for (j=0 ; j<6 ; j++)
+ {
+ if (j == i)
+ continue;
+ ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
+ }
+ }
+}
+
+//===================================================
+
+
+/*
+================
+BaseWindingForNode
+================
+*/
+#define BASE_WINDING_EPSILON 0.001
+#define SPLIT_WINDING_EPSILON 0.001
+
+winding_t *BaseWindingForNode (node_t *node)
+{
+ winding_t *w;
+ node_t *n;
+ plane_t *plane;
+ vec3_t normal;
+ vec_t dist;
+
+ w = BaseWindingForPlane (mapplanes[node->planenum].normal
+ , mapplanes[node->planenum].dist);
+
+ // clip by all the parents
+ for (n=node->parent ; n && w ; )
+ {
+ plane = &mapplanes[n->planenum];
+
+ if (n->children[0] == node)
+ { // take front
+ ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
+ }
+ else
+ { // take back
+ VectorSubtract (vec3_origin, plane->normal, normal);
+ dist = -plane->dist;
+ ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
+ }
+ node = n;
+ n = n->parent;
+ }
+
+ return w;
+}
+
+//============================================================
+
+/*
+==================
+MakeNodePortal
+
+create the new portal by taking the full plane winding for the cutting plane
+and clipping it by all of parents of this node
+==================
+*/
+void MakeNodePortal (node_t *node)
+{
+ portal_t *new_portal, *p;
+ winding_t *w;
+ vec3_t normal;
+ float dist;
+ int side;
+
+ w = BaseWindingForNode (node);
+
+ // clip the portal by all the other portals in the node
+ for (p = node->portals ; p && w; p = p->next[side])
+ {
+ if (p->nodes[0] == node)
+ {
+ side = 0;
+ VectorCopy (p->plane.normal, normal);
+ dist = p->plane.dist;
+ }
+ else if (p->nodes[1] == node)
+ {
+ side = 1;
+ VectorSubtract (vec3_origin, p->plane.normal, normal);
+ dist = -p->plane.dist;
+ }
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+
+ ChopWindingInPlace (&w, normal, dist, CLIP_EPSILON);
+ }
+
+ if (!w)
+ {
+ return;
+ }
+
+ if (WindingIsTiny (w))
+ {
+ c_tinyportals++;
+ FreeWinding (w);
+ return;
+ }
+
+ new_portal = AllocPortal ();
+ new_portal->plane = mapplanes[node->planenum];
+ new_portal->onnode = node;
+ new_portal->winding = w;
+ new_portal->hint = node->hint;
+ AddPortalToNodes (new_portal, node->children[0], node->children[1]);
+}
+
+
+/*
+==============
+SplitNodePortals
+
+Move or split the portals that bound node so that the node's
+children have portals instead of node.
+==============
+*/
+void SplitNodePortals (node_t *node)
+{
+ portal_t *p, *next_portal, *new_portal;
+ node_t *f, *b, *other_node;
+ int side;
+ plane_t *plane;
+ winding_t *frontwinding, *backwinding;
+
+ plane = &mapplanes[node->planenum];
+ f = node->children[0];
+ b = node->children[1];
+
+ for (p = node->portals ; p ; p = next_portal)
+ {
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ side = 1;
+ else
+ Error ("SplitNodePortals: mislinked portal");
+ next_portal = p->next[side];
+
+ other_node = p->nodes[!side];
+ RemovePortalFromNode (p, p->nodes[0]);
+ RemovePortalFromNode (p, p->nodes[1]);
+
+//
+// cut the portal into two portals, one on each side of the cut plane
+//
+ ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
+ SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
+
+ if (frontwinding && WindingIsTiny(frontwinding))
+ {
+ if (!f->tinyportals)
+ VectorCopy(frontwinding->p[0], f->referencepoint);
+ f->tinyportals++;
+ if (!other_node->tinyportals)
+ VectorCopy(frontwinding->p[0], other_node->referencepoint);
+ other_node->tinyportals++;
+
+ FreeWinding (frontwinding);
+ frontwinding = NULL;
+ c_tinyportals++;
+ }
+
+ if (backwinding && WindingIsTiny(backwinding))
+ {
+ if (!b->tinyportals)
+ VectorCopy(backwinding->p[0], b->referencepoint);
+ b->tinyportals++;
+ if (!other_node->tinyportals)
+ VectorCopy(backwinding->p[0], other_node->referencepoint);
+ other_node->tinyportals++;
+
+ FreeWinding (backwinding);
+ backwinding = NULL;
+ c_tinyportals++;
+ }
+
+ if (!frontwinding && !backwinding)
+ { // tiny windings on both sides
+ continue;
+ }
+
+ if (!frontwinding)
+ {
+ FreeWinding (backwinding);
+ if (side == 0)
+ AddPortalToNodes (p, b, other_node);
+ else
+ AddPortalToNodes (p, other_node, b);
+ continue;
+ }
+ if (!backwinding)
+ {
+ FreeWinding (frontwinding);
+ if (side == 0)
+ AddPortalToNodes (p, f, other_node);
+ else
+ AddPortalToNodes (p, other_node, f);
+ continue;
+ }
+
+ // the winding is split
+ new_portal = AllocPortal ();
+ *new_portal = *p;
+ new_portal->winding = backwinding;
+ FreeWinding (p->winding);
+ p->winding = frontwinding;
+
+ if (side == 0)
+ {
+ AddPortalToNodes (p, f, other_node);
+ AddPortalToNodes (new_portal, b, other_node);
+ }
+ else
+ {
+ AddPortalToNodes (p, other_node, f);
+ AddPortalToNodes (new_portal, other_node, b);
+ }
+ }
+
+ node->portals = NULL;
+}
+
+
+/*
+================
+CalcNodeBounds
+================
+*/
+void CalcNodeBounds (node_t *node)
+{
+ portal_t *p;
+ int s;
+ int i;
+
+ // calc mins/maxs for both leafs and nodes
+ ClearBounds (node->mins, node->maxs);
+ for (p = node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+ for (i=0 ; i<p->winding->numpoints ; i++)
+ AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
+ }
+}
+
+/*
+==================
+MakeTreePortals_r
+==================
+*/
+void MakeTreePortals_r (node_t *node)
+{
+ int i;
+
+ CalcNodeBounds (node);
+ if (node->mins[0] >= node->maxs[0])
+ {
+ _printf ("WARNING: node without a volume\n");
+ _printf("node has %d tiny portals\n", node->tinyportals);
+ _printf("node reference point %1.2f %1.2f %1.2f\n", node->referencepoint[0],
+ node->referencepoint[1],
+ node->referencepoint[2]);
+ }
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (node->mins[i] < MIN_WORLD_COORD || node->maxs[i] > MAX_WORLD_COORD)
+ {
+ _printf ("WARNING: node with unbounded volume\n");
+ break;
+ }
+ }
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ MakeNodePortal (node);
+ SplitNodePortals (node);
+
+ MakeTreePortals_r (node->children[0]);
+ MakeTreePortals_r (node->children[1]);
+}
+
+/*
+==================
+MakeTreePortals
+==================
+*/
+void MakeTreePortals (tree_t *tree)
+{
+ qprintf( "----- MakeTreePortals -----\n");
+ MakeHeadnodePortals (tree);
+ MakeTreePortals_r (tree->headnode);
+ qprintf("%6d tiny portals\n", c_tinyportals);
+}
+
+/*
+=========================================================
+
+FLOOD ENTITIES
+
+=========================================================
+*/
+
+int c_floodedleafs;
+
+/*
+=============
+FloodPortals_r
+=============
+*/
+void FloodPortals_r (node_t *node, int dist) {
+ portal_t *p;
+ int s;
+
+ if ( node->occupied ) {
+ return;
+ }
+
+ if ( node->opaque ) {
+ return;
+ }
+
+ c_floodedleafs++;
+ node->occupied = dist;
+
+ for (p=node->portals ; p ; p = p->next[s]) {
+ s = (p->nodes[1] == node);
+ FloodPortals_r (p->nodes[!s], dist+1);
+ }
+}
+
+/*
+=============
+PlaceOccupant
+=============
+*/
+qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant)
+{
+ node_t *node;
+ vec_t d;
+ plane_t *plane;
+
+ // find the leaf to start in
+ node = headnode;
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &mapplanes[node->planenum];
+ d = DotProduct (origin, plane->normal) - plane->dist;
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ if ( node->opaque )
+ return qfalse;
+ node->occupant = occupant;
+
+ FloodPortals_r (node, 1);
+
+ return qtrue;
+}
+
+/*
+=============
+FloodEntities
+
+Marks all nodes that can be reached by entites
+=============
+*/
+qboolean FloodEntities( tree_t *tree ) {
+ int i;
+ vec3_t origin;
+ const char *cl;
+ qboolean inside;
+ node_t *headnode;
+
+ headnode = tree->headnode;
+ qprintf ("--- FloodEntities ---\n");
+ inside = qfalse;
+ tree->outside_node.occupied = 0;
+
+ c_floodedleafs = 0;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ GetVectorForKey (&entities[i], "origin", origin);
+ if (VectorCompare(origin, vec3_origin))
+ continue;
+
+ cl = ValueForKey (&entities[i], "classname");
+
+ origin[2] += 1; // so objects on floor are ok
+
+ if (PlaceOccupant (headnode, origin, &entities[i]))
+ inside = qtrue;
+ }
+
+ qprintf("%5i flooded leafs\n", c_floodedleafs );
+
+ if (!inside)
+ {
+ qprintf ("no entities in open -- no filling\n");
+ }
+ else if (tree->outside_node.occupied)
+ {
+ qprintf ("entity reached from outside -- no filling\n");
+ }
+
+ return (qboolean)(inside && !tree->outside_node.occupied);
+}
+
+/*
+=========================================================
+
+FLOOD AREAS
+
+=========================================================
+*/
+
+int c_areas;
+
+/*
+=============
+FloodAreas_r
+=============
+*/
+void FloodAreas_r (node_t *node)
+{
+ portal_t *p;
+ int s;
+ bspbrush_t *b;
+
+ if ( node->areaportal ) {
+ //
+ if ( node->area == -1 ) {
+ node->area = c_areas;
+ }
+ // this node is part of an area portal brush
+ b = node->brushlist->original;
+
+ // if the current area has allready touched this
+ // portal, we are done
+ if (b->portalareas[0] == c_areas || b->portalareas[1] == c_areas)
+ return;
+
+ // note the current area as bounding the portal
+ if (b->portalareas[1] != -1)
+ {
+ _printf ("WARNING: areaportal brush %i touches > 2 areas\n", b->brushnum );
+ return;
+ }
+ if (b->portalareas[0] != -1) {
+ b->portalareas[1] = c_areas;
+ } else {
+ b->portalareas[0] = c_areas;
+ }
+
+ return;
+ }
+
+ if (node->area != -1) {
+ return; // allready got it
+ }
+ if ( node->cluster == -1 ) {
+ return;
+ }
+
+ node->area = c_areas;
+
+ for (p=node->portals ; p ; p = p->next[s])
+ {
+ s = (p->nodes[1] == node);
+
+ if ( !Portal_Passable(p) )
+ continue;
+
+ FloodAreas_r (p->nodes[!s]);
+ }
+}
+
+
+/*
+=============
+FindAreas_r
+
+Just decend the tree, and for each node that hasn't had an
+area set, flood fill out from there
+=============
+*/
+void FindAreas_r (node_t *node)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FindAreas_r (node->children[0]);
+ FindAreas_r (node->children[1]);
+ return;
+ }
+
+ if (node->opaque)
+ return;
+
+ if (node->areaportal)
+ return;
+
+ if (node->area != -1)
+ return; // allready got it
+
+ FloodAreas_r (node);
+ c_areas++;
+}
+
+/*
+=============
+CheckAreas_r
+=============
+*/
+void CheckAreas_r (node_t *node)
+{
+ bspbrush_t *b;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ CheckAreas_r (node->children[0]);
+ CheckAreas_r (node->children[1]);
+ return;
+ }
+
+ if (node->opaque)
+ return;
+
+ if (node->cluster != -1)
+ if (node->area == -1)
+ _printf("WARNING: cluster %d has area set to -1\n", node->cluster);
+ if (node->areaportal)
+ {
+ b = node->brushlist->original;
+
+ // check if the areaportal touches two areas
+ if (b->portalareas[0] == -1 || b->portalareas[1] == -1)
+ _printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushnum);
+ }
+}
+
+/*
+=============
+FloodAreas
+
+Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
+=============
+*/
+void FloodAreas (tree_t *tree)
+{
+ qprintf ("--- FloodAreas ---\n");
+ FindAreas_r( tree->headnode );
+
+ // check for areaportal brushes that don't touch two areas
+ CheckAreas_r( tree->headnode );
+
+ qprintf ("%5i areas\n", c_areas);
+}
+
+//======================================================
+
+int c_outside;
+int c_inside;
+int c_solid;
+
+void FillOutside_r (node_t *node)
+{
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FillOutside_r (node->children[0]);
+ FillOutside_r (node->children[1]);
+ return;
+ }
+
+ // anything not reachable by an entity
+ // can be filled away
+ if (!node->occupied) {
+ if ( !node->opaque ) {
+ c_outside++;
+ node->opaque = qtrue;
+ } else {
+ c_solid++;
+ }
+ } else {
+ c_inside++;
+ }
+
+}
+
+/*
+=============
+FillOutside
+
+Fill all nodes that can't be reached by entities
+=============
+*/
+void FillOutside (node_t *headnode)
+{
+ c_outside = 0;
+ c_inside = 0;
+ c_solid = 0;
+ qprintf ("--- FillOutside ---\n");
+ FillOutside_r (headnode);
+ qprintf ("%5i solid leafs\n", c_solid);
+ qprintf ("%5i leafs filled\n", c_outside);
+ qprintf ("%5i inside leafs\n", c_inside);
+}
+
+
+//==============================================================
+
diff --git a/q3map/prtfile.c b/q3map/prtfile.c
new file mode 100755
index 0000000..902ae05
--- /dev/null
+++ b/q3map/prtfile.c
@@ -0,0 +1,272 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+/*
+==============================================================================
+
+PORTAL FILE GENERATION
+
+Save out name.prt for qvis to read
+==============================================================================
+*/
+
+
+#define PORTALFILE "PRT1"
+
+FILE *pf;
+int num_visclusters; // clusters the player can be in
+int num_visportals;
+int num_solidfaces;
+
+void WriteFloat (FILE *f, vec_t v)
+{
+ if ( fabs(v - Q_rint(v)) < 0.001 )
+ fprintf (f,"%i ",(int)Q_rint(v));
+ else
+ fprintf (f,"%f ",v);
+}
+
+/*
+=================
+WritePortalFile_r
+=================
+*/
+void WritePortalFile_r (node_t *node)
+{
+ int i, s;
+ portal_t *p;
+ winding_t *w;
+ vec3_t normal;
+ vec_t dist;
+
+ // decision node
+ if (node->planenum != PLANENUM_LEAF) {
+ WritePortalFile_r (node->children[0]);
+ WritePortalFile_r (node->children[1]);
+ return;
+ }
+
+ if (node->opaque) {
+ return;
+ }
+
+ for (p = node->portals ; p ; p=p->next[s])
+ {
+ w = p->winding;
+ s = (p->nodes[1] == node);
+ if (w && p->nodes[0] == node)
+ {
+ if (!Portal_Passable(p))
+ continue;
+ // write out to the file
+
+ // sometimes planes get turned around when they are very near
+ // the changeover point between different axis. interpret the
+ // plane the same way vis will, and flip the side orders if needed
+ // FIXME: is this still relevent?
+ WindingPlane (w, normal, &dist);
+ if ( DotProduct (p->plane.normal, normal) < 0.99 )
+ { // backwards...
+ fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
+ }
+ else
+ fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
+ if (p->hint)
+ fprintf (pf, "1 ");
+ else
+ fprintf (pf, "0 ");
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pf,"(");
+ WriteFloat (pf, w->p[i][0]);
+ WriteFloat (pf, w->p[i][1]);
+ WriteFloat (pf, w->p[i][2]);
+ fprintf (pf,") ");
+ }
+ fprintf (pf,"\n");
+ }
+ }
+
+}
+
+/*
+=================
+WriteFaceFile_r
+=================
+*/
+void WriteFaceFile_r (node_t *node)
+{
+ int i, s;
+ portal_t *p;
+ winding_t *w;
+
+ // decision node
+ if (node->planenum != PLANENUM_LEAF) {
+ WriteFaceFile_r (node->children[0]);
+ WriteFaceFile_r (node->children[1]);
+ return;
+ }
+
+ if (node->opaque) {
+ return;
+ }
+
+ for (p = node->portals ; p ; p=p->next[s])
+ {
+ w = p->winding;
+ s = (p->nodes[1] == node);
+ if (w)
+ {
+ if (Portal_Passable(p))
+ continue;
+ // write out to the file
+
+ if (p->nodes[0] == node)
+ {
+ fprintf (pf,"%i %i ",w->numpoints, p->nodes[0]->cluster);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pf,"(");
+ WriteFloat (pf, w->p[i][0]);
+ WriteFloat (pf, w->p[i][1]);
+ WriteFloat (pf, w->p[i][2]);
+ fprintf (pf,") ");
+ }
+ fprintf (pf,"\n");
+ }
+ else
+ {
+ fprintf (pf,"%i %i ",w->numpoints, p->nodes[1]->cluster);
+ for (i = w->numpoints-1; i >= 0; i--)
+ {
+ fprintf (pf,"(");
+ WriteFloat (pf, w->p[i][0]);
+ WriteFloat (pf, w->p[i][1]);
+ WriteFloat (pf, w->p[i][2]);
+ fprintf (pf,") ");
+ }
+ fprintf (pf,"\n");
+ }
+ }
+ }
+}
+
+/*
+================
+NumberLeafs_r
+================
+*/
+void NumberLeafs_r (node_t *node)
+{
+ portal_t *p;
+
+ if ( node->planenum != PLANENUM_LEAF ) {
+ // decision node
+ node->cluster = -99;
+ NumberLeafs_r (node->children[0]);
+ NumberLeafs_r (node->children[1]);
+ return;
+ }
+
+ node->area = -1;
+
+ if ( node->opaque ) {
+ // solid block, viewpoint never inside
+ node->cluster = -1;
+ return;
+ }
+
+ node->cluster = num_visclusters;
+ num_visclusters++;
+
+ // count the portals
+ for (p = node->portals ; p ; )
+ {
+ if (p->nodes[0] == node) // only write out from first leaf
+ {
+ if (Portal_Passable(p))
+ num_visportals++;
+ else
+ num_solidfaces++;
+ p = p->next[0];
+ }
+ else
+ {
+ if (!Portal_Passable(p))
+ num_solidfaces++;
+ p = p->next[1];
+ }
+ }
+}
+
+
+/*
+================
+NumberClusters
+================
+*/
+void NumberClusters(tree_t *tree) {
+ num_visclusters = 0;
+ num_visportals = 0;
+ num_solidfaces = 0;
+
+ qprintf ("--- NumberClusters ---\n");
+
+ // set the cluster field in every leaf and count the total number of portals
+ NumberLeafs_r (tree->headnode);
+
+ qprintf ("%5i visclusters\n", num_visclusters);
+ qprintf ("%5i visportals\n", num_visportals);
+ qprintf ("%5i solidfaces\n", num_solidfaces);
+}
+
+/*
+================
+WritePortalFile
+================
+*/
+void WritePortalFile (tree_t *tree)
+{
+ char filename[1024];
+
+ qprintf ("--- WritePortalFile ---\n");
+
+ // write the file
+ sprintf (filename, "%s.prt", source);
+ _printf ("writing %s\n", filename);
+ pf = fopen (filename, "w");
+ if (!pf)
+ Error ("Error opening %s", filename);
+
+ fprintf (pf, "%s\n", PORTALFILE);
+ fprintf (pf, "%i\n", num_visclusters);
+ fprintf (pf, "%i\n", num_visportals);
+ fprintf (pf, "%i\n", num_solidfaces);
+
+ WritePortalFile_r(tree->headnode);
+ WriteFaceFile_r(tree->headnode);
+
+ fclose (pf);
+}
+
diff --git a/q3map/q3map.sln b/q3map/q3map.sln
new file mode 100755
index 0000000..6e4bb3c
--- /dev/null
+++ b/q3map/q3map.sln
@@ -0,0 +1,56 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpeg6", "..\libs\jpeg6\jpeg6.vcproj", "{A862AD26-94DD-4618-A814-F6AACA0B2FE3}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "..\libs\pak\pak.vcproj", "{F2ACC9D7-D628-4624-864F-87FE58787625}"
+ ProjectSection(ProjectDependencies) = postProject
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "q3map", "q3map.vcproj", "{F1162C55-66E7-4486-B1F3-071CFAA78332}"
+ ProjectSection(ProjectDependencies) = postProject
+ {F2ACC9D7-D628-4624-864F-87FE58787625} = {F2ACC9D7-D628-4624-864F-87FE58787625}
+ {A862AD26-94DD-4618-A814-F6AACA0B2FE3} = {A862AD26-94DD-4618-A814-F6AACA0B2FE3}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SourceCodeControl) = preSolution
+ SccNumberOfProjects = 3
+ SccProjectUniqueName0 = ..\\libs\\jpeg6\\jpeg6.vcproj
+ SccProjectName0 = \u0022$/source/q3radiant\u0022,\u0020FEFAAAAA
+ SccLocalPath0 = ..\\q3radiant
+ SccProvider0 = MSSCCI:Perforce\u0020SCM
+ SccProjectFilePathRelativizedFromConnection0 = ..\\libs\\jpeg6\\
+ SccProjectUniqueName1 = ..\\libs\\pak\\pak.vcproj
+ SccProjectName1 = \u0022$/source/q3radiant\u0022,\u0020FEFAAAAA
+ SccLocalPath1 = ..\\q3radiant
+ SccProvider1 = MSSCCI:Perforce\u0020SCM
+ SccProjectFilePathRelativizedFromConnection1 = ..\\libs\\pak\\
+ SccProjectUniqueName2 = q3map.vcproj
+ SccProjectName2 = \u0022$/source/q3map\u0022,\u0020PADAAAAA
+ SccLocalPath2 = .
+ SccProvider2 = MSSCCI:Perforce\u0020SCM
+ EndGlobalSection
+ GlobalSection(SolutionConfiguration) = preSolution
+ Debug = Debug
+ Release = Release
+ EndGlobalSection
+ GlobalSection(ProjectConfiguration) = postSolution
+ {A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Debug.ActiveCfg = Debug|Win32
+ {A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Debug.Build.0 = Debug|Win32
+ {A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Release.ActiveCfg = Release|Win32
+ {A862AD26-94DD-4618-A814-F6AACA0B2FE3}.Release.Build.0 = Release|Win32
+ {F2ACC9D7-D628-4624-864F-87FE58787625}.Debug.ActiveCfg = Debug|Win32
+ {F2ACC9D7-D628-4624-864F-87FE58787625}.Debug.Build.0 = Debug|Win32
+ {F2ACC9D7-D628-4624-864F-87FE58787625}.Release.ActiveCfg = Release|Win32
+ {F2ACC9D7-D628-4624-864F-87FE58787625}.Release.Build.0 = Release|Win32
+ {F1162C55-66E7-4486-B1F3-071CFAA78332}.Debug.ActiveCfg = Debug|Win32
+ {F1162C55-66E7-4486-B1F3-071CFAA78332}.Debug.Build.0 = Debug|Win32
+ {F1162C55-66E7-4486-B1F3-071CFAA78332}.Release.ActiveCfg = Release|Win32
+ {F1162C55-66E7-4486-B1F3-071CFAA78332}.Release.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ EndGlobalSection
+ GlobalSection(ExtensibilityAddIns) = postSolution
+ EndGlobalSection
+EndGlobal
diff --git a/q3map/q3map.vcproj b/q3map/q3map.vcproj
new file mode 100755
index 0000000..64ad4f8
--- /dev/null
+++ b/q3map/q3map.vcproj
@@ -0,0 +1,1606 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="q3map"
+ SccProjectName="&quot;$/source/q3map&quot;, PADAAAAA"
+ SccLocalPath=".">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\Debug"
+ IntermediateDirectory=".\Debug"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../common,c:\quake3\common,c:\quake3\libs"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Debug/q3map.pch"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glaux.lib glu32.lib wsock32.lib opengl32.lib odbc32.lib odbccp32.lib ..\libs\pakd.lib ..\libs\jpeg6d.lib"
+ OutputFile=".\Debug/q3map.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/q3map.pdb"
+ SubSystem="1"
+ StackReserveSize="4194304"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Debug/q3map.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\Release"
+ IntermediateDirectory=".\Release"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories="../common,c:\quake3\common,c:\quake3\libs"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ StringPooling="TRUE"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Release/q3map.pch"
+ AssemblerListingLocation=".\Release/"
+ ObjectFile=".\Release/"
+ ProgramDataBaseFileName=".\Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="3"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glaux.lib glu32.lib wsock32.lib opengl32.lib odbc32.lib odbccp32.lib ..\libs\pak.lib ..\libs\jpeg6.lib"
+ OutputFile=".\Release/q3map.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Release/q3map.pdb"
+ SubSystem="1"
+ StackReserveSize="4194304"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\Release/q3map.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="DebugTTimo|Win32"
+ OutputDirectory=".\q3map___Win32_DebugTTimo"
+ IntermediateDirectory=".\q3map___Win32_DebugTTimo"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../common,../../Libs,c:\quake3\common,c:\quake3\libs"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_TTIMOBUILD"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\q3map___Win32_DebugTTimo/q3map.pch"
+ AssemblerListingLocation=".\q3map___Win32_DebugTTimo/"
+ ObjectFile=".\q3map___Win32_DebugTTimo/"
+ ProgramDataBaseFileName=".\q3map___Win32_DebugTTimo/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glaux.lib glu32.lib wsock32.lib opengl32.lib odbc32.lib odbccp32.lib pakd.lib jpeg6d.lib"
+ OutputFile=".\q3map___Win32_DebugTTimo/q3map.exe"
+ LinkIncremental="2"
+ SuppressStartupBanner="TRUE"
+ AdditionalLibraryDirectories="../../Libs"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\q3map___Win32_DebugTTimo/q3map.pdb"
+ SubSystem="1"
+ StackReserveSize="4194304"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\q3map___Win32_DebugTTimo/q3map.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="ReleaseTTimo|Win32"
+ OutputDirectory=".\q3map___Win32_ReleaseTTimo"
+ IntermediateDirectory=".\q3map___Win32_ReleaseTTimo"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories="../common,../../Libs,c:\quake3\common,c:\quake3\libs"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_TTIMOBUILD"
+ StringPooling="TRUE"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\q3map___Win32_ReleaseTTimo/q3map.pch"
+ AssemblerListingLocation=".\q3map___Win32_ReleaseTTimo/"
+ ObjectFile=".\q3map___Win32_ReleaseTTimo/"
+ ProgramDataBaseFileName=".\q3map___Win32_ReleaseTTimo/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="3"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="glaux.lib glu32.lib wsock32.lib opengl32.lib odbc32.lib odbccp32.lib pak.lib jpeg6.lib"
+ OutputFile=".\q3map___Win32_ReleaseTTimo/q3map.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ AdditionalLibraryDirectories="../../Libs"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\q3map___Win32_ReleaseTTimo/q3map.pdb"
+ SubSystem="1"
+ StackReserveSize="4194304"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\q3map___Win32_ReleaseTTimo/q3map.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\common\aselib.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="brush.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="brush_primit.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="bsp.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\common\bspfile.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\common\cmdlib.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="facebsp.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="fog.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="gldraw.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="glfile.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\common\imagelib.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="leakfile.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="light.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="light_trace.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="lightmaps.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="lightv.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="map.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\common\mathlib.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="mesh.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="misc_model.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\common\mutex.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="patch.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\common\polylib.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="portals.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="prtfile.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\common\scriplib.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="shaders.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="soundv.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="surface.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="terrain.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\common\threads.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="tjunction.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="tree.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="vis.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="visflow.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="writebsp.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="DebugTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="ReleaseTTimo|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/q3map/qbsp.h b/q3map/qbsp.h
new file mode 100755
index 0000000..0890713
--- /dev/null
+++ b/q3map/qbsp.h
@@ -0,0 +1,455 @@
+/*
+===========================================================================
+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 "scriplib.h"
+#include "polylib.h"
+#include "imagelib.h"
+#include "threads.h"
+#include "bspfile.h"
+#include "shaders.h"
+#include "mesh.h"
+
+
+#define MAX_PATCH_SIZE 32
+
+#define CLIP_EPSILON 0.1
+#define PLANENUM_LEAF -1
+
+#define HINT_PRIORITY 1000
+
+typedef struct parseMesh_s {
+ struct parseMesh_s *next;
+ mesh_t mesh;
+ shaderInfo_t *shaderInfo;
+
+ qboolean grouped; // used during shared edge grouping
+ struct parseMesh_s *groupChain;
+} parseMesh_t;
+
+typedef struct bspface_s {
+ struct bspface_s *next;
+ int planenum;
+ int priority; // added to value calculation
+ qboolean checked;
+ qboolean hint;
+ winding_t *w;
+} bspface_t;
+
+typedef struct plane_s {
+ vec3_t normal;
+ vec_t dist;
+ int type;
+ struct plane_s *hash_chain;
+} plane_t;
+
+typedef struct side_s {
+ int planenum;
+
+ float texMat[2][3]; // brush primitive texture matrix
+ // for old brush coordinates mode
+ float vecs[2][4]; // texture coordinate mapping
+
+ winding_t *winding;
+ winding_t *visibleHull; // convex hull of all visible fragments
+
+ struct shaderInfo_s *shaderInfo;
+
+ int contents; // from shaderInfo
+ int surfaceFlags; // from shaderInfo
+ int value; // from shaderInfo
+
+ qboolean visible; // choose visble planes first
+ qboolean bevel; // don't ever use for bsp splitting, and don't bother
+ // making windings for it
+ qboolean backSide; // generated side for a q3map_backShader
+} side_t;
+
+
+#define MAX_BRUSH_SIDES 1024
+
+typedef struct bspbrush_s {
+ struct bspbrush_s *next;
+
+ int entitynum; // editor numbering
+ int brushnum; // editor numbering
+
+ struct shaderInfo_s *contentShader;
+
+ int contents;
+ qboolean detail;
+ qboolean opaque;
+ int outputNumber; // set when the brush is written to the file list
+
+ int portalareas[2];
+
+ struct bspbrush_s *original; // chopped up brushes will reference the originals
+
+ vec3_t mins, maxs;
+ int numsides;
+ side_t sides[6]; // variably sized
+} bspbrush_t;
+
+
+
+typedef struct drawsurf_s {
+ shaderInfo_t *shaderInfo;
+
+ bspbrush_t *mapBrush; // not valid for patches
+ side_t *side; // not valid for patches
+
+ struct drawsurf_s *nextOnShader; // when sorting by shader for lightmaps
+
+ int fogNum; // set by FogDrawSurfs
+
+ int lightmapNum; // -1 = no lightmap
+ int lightmapX, lightmapY;
+ int lightmapWidth, lightmapHeight;
+
+ int numVerts;
+ drawVert_t *verts;
+
+ int numIndexes;
+ int *indexes;
+
+ // for faces only
+ int planeNum;
+
+ vec3_t lightmapOrigin; // also used for flares
+ vec3_t lightmapVecs[3]; // also used for flares
+
+ // for patches only
+ qboolean patch;
+ int patchWidth;
+ int patchHeight;
+
+ // for misc_models only
+ qboolean miscModel;
+
+ qboolean flareSurface;
+} mapDrawSurface_t;
+
+typedef struct drawSurfRef_s {
+ struct drawSurfRef_s *nextRef;
+ int outputNumber;
+} drawSurfRef_t;
+
+typedef struct node_s {
+ // both leafs and nodes
+ int planenum; // -1 = leaf node
+ struct node_s *parent;
+ vec3_t mins, maxs; // valid after portalization
+ bspbrush_t *volume; // one for each leaf/node
+
+ // nodes only
+ side_t *side; // the side that created the node
+ struct node_s *children[2];
+ qboolean hint;
+ int tinyportals;
+ vec3_t referencepoint;
+
+ // leafs only
+ qboolean opaque; // view can never be inside
+ qboolean areaportal;
+ int cluster; // for portalfile writing
+ int area; // for areaportals
+ bspbrush_t *brushlist; // fragments of all brushes in this leaf
+ drawSurfRef_t *drawSurfReferences; // references to patches pushed down
+
+ int occupied; // 1 or greater can reach entity
+ entity_t *occupant; // for leak file testing
+
+ struct portal_s *portals; // also on nodes during construction
+} node_t;
+
+typedef struct portal_s {
+ plane_t plane;
+ node_t *onnode; // NULL = outside box
+ node_t *nodes[2]; // [0] = front side of plane
+ struct portal_s *next[2];
+ winding_t *winding;
+
+ qboolean sidefound; // false if ->side hasn't been checked
+ qboolean hint;
+ side_t *side; // NULL = non-visible
+} portal_t;
+
+typedef struct {
+ node_t *headnode;
+ node_t outside_node;
+ vec3_t mins, maxs;
+} tree_t;
+
+extern int entity_num;
+
+
+extern qboolean noprune;
+extern qboolean nodetail;
+extern qboolean fulldetail;
+extern qboolean nowater;
+extern qboolean noCurveBrushes;
+extern qboolean fakemap;
+extern qboolean coplanar;
+extern qboolean nofog;
+extern qboolean testExpand;
+extern qboolean showseams;
+
+extern vec_t microvolume;
+
+extern char outbase[32];
+extern char source[1024];
+
+extern int samplesize; //sample size in units
+extern int novertexlighting;
+extern int nogridlighting;
+
+//=============================================================================
+
+// brush.c
+
+int CountBrushList (bspbrush_t *brushes);
+bspbrush_t *AllocBrush (int numsides);
+void FreeBrush (bspbrush_t *brushes);
+void FreeBrushList (bspbrush_t *brushes);
+bspbrush_t *CopyBrush (bspbrush_t *brush);
+void DrawBrushList (bspbrush_t *brush);
+void WriteBrushList (char *name, bspbrush_t *brush, qboolean onlyvis);
+void PrintBrush (bspbrush_t *brush);
+qboolean BoundBrush (bspbrush_t *brush);
+qboolean CreateBrushWindings (bspbrush_t *brush);
+bspbrush_t *BrushFromBounds (vec3_t mins, vec3_t maxs);
+vec_t BrushVolume (bspbrush_t *brush);
+void WriteBspBrushMap (char *name, bspbrush_t *list);
+
+void FilterDetailBrushesIntoTree( entity_t *e, tree_t *tree );
+void FilterStructuralBrushesIntoTree( entity_t *e, tree_t *tree );
+
+//=============================================================================
+
+// map.c
+
+extern int entitySourceBrushes;
+
+// mapplanes[ num^1 ] will always be the mirror or mapplanes[ num ]
+// nummapplanes will always be even
+extern plane_t mapplanes[MAX_MAP_PLANES];
+extern int nummapplanes;
+
+extern vec3_t map_mins, map_maxs;
+
+extern char mapIndexedShaders[MAX_MAP_BRUSHSIDES][MAX_QPATH];
+extern int numMapIndexedShaders;
+
+extern entity_t *mapent;
+
+#define MAX_BUILD_SIDES 300
+extern bspbrush_t *buildBrush;
+
+
+void LoadMapFile (char *filename);
+int FindFloatPlane (vec3_t normal, vec_t dist);
+int PlaneTypeForNormal (vec3_t normal);
+bspbrush_t *FinishBrush( void );
+mapDrawSurface_t *AllocDrawSurf( void );
+mapDrawSurface_t *DrawSurfaceForSide( bspbrush_t *b, side_t *s, winding_t *w );
+
+//=============================================================================
+
+//=============================================================================
+
+// draw.c
+
+extern vec3_t draw_mins, draw_maxs;
+extern qboolean drawflag;
+
+void Draw_ClearWindow (void);
+void DrawWinding (winding_t *w);
+
+void GLS_BeginScene (void);
+void GLS_Winding (winding_t *w, int code);
+void GLS_EndScene (void);
+
+//=============================================================================
+
+// csg
+
+bspbrush_t *MakeBspBrushList ( bspbrush_t *brushes, vec3_t clipmins, vec3_t clipmaxs);
+
+//=============================================================================
+
+// brushbsp
+
+#define PSIDE_FRONT 1
+#define PSIDE_BACK 2
+#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
+#define PSIDE_FACING 4
+
+int BoxOnPlaneSide (vec3_t mins, vec3_t maxs, plane_t *plane);
+qboolean WindingIsTiny (winding_t *w);
+
+void SplitBrush (bspbrush_t *brush, int planenum,
+ bspbrush_t **front, bspbrush_t **back);
+
+tree_t *AllocTree (void);
+node_t *AllocNode (void);
+
+tree_t *BrushBSP (bspbrush_t *brushlist, vec3_t mins, vec3_t maxs);
+
+//=============================================================================
+
+// portals.c
+
+void MakeHeadnodePortals (tree_t *tree);
+void MakeNodePortal (node_t *node);
+void SplitNodePortals (node_t *node);
+
+qboolean Portal_Passable(portal_t *p);
+
+qboolean FloodEntities (tree_t *tree);
+void FillOutside (node_t *headnode);
+void FloodAreas (tree_t *tree);
+bspface_t *VisibleFaces(entity_t *e, tree_t *tree);
+void FreePortal (portal_t *p);
+
+void MakeTreePortals (tree_t *tree);
+
+//=============================================================================
+
+// glfile.c
+
+void OutputWinding( winding_t *w, FILE *glview );
+void WriteGLView( tree_t *tree, char *source );
+
+//=============================================================================
+
+// leakfile.c
+
+void LeakFile( tree_t *tree );
+
+//=============================================================================
+
+// prtfile.c
+
+void NumberClusters( tree_t *tree );
+void WritePortalFile( tree_t *tree );
+
+//=============================================================================
+
+// writebsp.c
+
+void SetModelNumbers (void);
+void SetLightStyles (void);
+
+int EmitShader( const char *shader );
+
+void BeginBSPFile (void);
+void EndBSPFile (void);
+
+void BeginModel (void);
+void EndModel( node_t *headnode );
+
+
+//=============================================================================
+
+// tree.c
+
+void FreeTree (tree_t *tree);
+void FreeTree_r (node_t *node);
+void PrintTree_r (node_t *node, int depth);
+void FreeTreePortals_r (node_t *node);
+
+//=============================================================================
+
+// patch.c
+
+extern int numMapPatches;
+
+mapDrawSurface_t *DrawSurfaceForMesh( mesh_t *m );
+void ParsePatch( void );
+mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength );
+void PatchMapDrawSurfs( entity_t *e );
+
+//=============================================================================
+
+// lightmap.c
+
+void AllocateLightmaps( entity_t *e );
+
+//=============================================================================
+
+// tjunction.c
+
+void FixTJunctions( entity_t *e );
+
+
+//=============================================================================
+
+// fog.c
+
+void FogDrawSurfs( void );
+winding_t *WindingFromDrawSurf( mapDrawSurface_t *ds );
+
+//=============================================================================
+
+// facebsp.c
+
+bspface_t *BspFaceForPortal( portal_t *p );
+bspface_t *MakeStructuralBspFaceList( bspbrush_t *list );
+bspface_t *MakeVisibleBspFaceList( bspbrush_t *list );
+tree_t *FaceBSP( bspface_t *list );
+
+//=============================================================================
+
+// misc_model.c
+
+extern int c_triangleModels;
+extern int c_triangleSurfaces;
+extern int c_triangleVertexes;
+extern int c_triangleIndexes;
+
+void AddTriangleModels( tree_t *tree );
+
+//=============================================================================
+
+// surface.c
+
+extern mapDrawSurface_t mapDrawSurfs[MAX_MAP_DRAW_SURFS];
+extern int numMapDrawSurfs;
+
+mapDrawSurface_t *AllocDrawSurf( void );
+void MergeSides( entity_t *e, tree_t *tree );
+void SubdivideDrawSurfs( entity_t *e, tree_t *tree );
+void MakeDrawSurfaces( bspbrush_t *b );
+void ClipSidesIntoTree( entity_t *e, tree_t *tree );
+void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree );
+
+//==============================================================================
+
+// brush_primit.c
+
+#define BPRIMIT_UNDEFINED 0
+#define BPRIMIT_OLDBRUSHES 1
+#define BPRIMIT_NEWBRUSHES 2
+extern int g_bBrushPrimit;
+
+void ComputeAxisBase( vec3_t normal, vec3_t texX, vec3_t texY);
diff --git a/q3map/shaders.c b/q3map/shaders.c
new file mode 100755
index 0000000..1efc513
--- /dev/null
+++ b/q3map/shaders.c
@@ -0,0 +1,608 @@
+/*
+===========================================================================
+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 <string.h>
+#include <math.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "imagelib.h"
+#include "scriplib.h"
+
+#ifdef _TTIMOBUILD
+#include "../common/qfiles.h"
+#include "../common/surfaceflags.h"
+#else
+#include "../code/qcommon/qfiles.h"
+#include "../code/game/surfaceflags.h"
+#endif
+
+#include "shaders.h"
+#ifdef _WIN32
+
+#ifdef _TTIMOBUILD
+#include "pakstuff.h"
+#include "jpeglib.h"
+#else
+#include "../libs/pakstuff.h"
+#include "../libs/jpeglib.h"
+#endif
+
+#endif
+
+
+// 5% backsplash by default
+#define DEFAULT_BACKSPLASH_FRACTION 0.05
+#define DEFAULT_BACKSPLASH_DISTANCE 24
+
+
+#define MAX_SURFACE_INFO 4096
+
+shaderInfo_t defaultInfo;
+shaderInfo_t shaderInfo[MAX_SURFACE_INFO];
+int numShaderInfo;
+
+
+typedef struct {
+ char *name;
+ int clearSolid, surfaceFlags, contents;
+} infoParm_t;
+
+infoParm_t infoParms[] = {
+ // server relevant contents
+ {"water", 1, 0, CONTENTS_WATER },
+ {"slime", 1, 0, CONTENTS_SLIME }, // mildly damaging
+ {"lava", 1, 0, CONTENTS_LAVA }, // very damaging
+ {"playerclip", 1, 0, CONTENTS_PLAYERCLIP },
+ {"monsterclip", 1, 0, CONTENTS_MONSTERCLIP },
+ {"nodrop", 1, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc)
+ {"nonsolid", 1, SURF_NONSOLID, 0}, // clears the solid flag
+
+ // utility relevant attributes
+ {"origin", 1, 0, CONTENTS_ORIGIN }, // center of rotating brushes
+ {"trans", 0, 0, CONTENTS_TRANSLUCENT }, // don't eat contained surfaces
+ {"detail", 0, 0, CONTENTS_DETAIL }, // don't include in structural bsp
+ {"structural", 0, 0, CONTENTS_STRUCTURAL }, // force into structural bsp even if trnas
+ {"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas
+ {"clusterportal",1, 0, CONTENTS_CLUSTERPORTAL },// for bots
+ {"donotenter", 1, 0, CONTENTS_DONOTENTER }, // for bots
+ {"botclip", 1, 0, CONTENTS_BOTCLIP }, // for bots
+ {"nobotclip", 0, 0, CONTENTS_NOBOTCLIP }, // don't use for bot clipping
+
+ {"fog", 1, 0, CONTENTS_FOG}, // carves surfaces entering
+ {"sky", 0, SURF_SKY, 0 }, // emit light from an environment map
+ {"lightfilter", 0, SURF_LIGHTFILTER, 0 }, // filter light going through it
+ {"alphashadow", 0, SURF_ALPHASHADOW, 0 }, // test light on a per-pixel basis
+ {"hint", 0, SURF_HINT, 0 }, // use as a primary splitter
+
+ // server attributes
+ {"slick", 0, SURF_SLICK, 0 },
+ {"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks
+ {"nomarks", 0, SURF_NOMARKS, 0 }, // don't make impact marks, but still explode
+ {"ladder", 0, SURF_LADDER, 0 },
+ {"nodamage", 0, SURF_NODAMAGE, 0 },
+ {"metalsteps", 0, SURF_METALSTEPS,0 },
+ {"flesh", 0, SURF_FLESH, 0 },
+ {"nosteps", 0, SURF_NOSTEPS, 0 },
+
+ // drawsurf attributes
+ {"nodraw", 0, SURF_NODRAW, 0 }, // don't generate a drawsurface (or a lightmap)
+ {"pointlight", 0, SURF_POINTLIGHT, 0 }, // sample lighting at vertexes
+ {"nolightmap", 0, SURF_NOLIGHTMAP,0 }, // don't generate a lightmap
+ {"nodlight", 0, SURF_NODLIGHT, 0 }, // don't ever add dynamic lights
+ {"dust", 0, SURF_DUST, 0} // leave dust trail when walking on this surface
+};
+
+
+/*
+===============
+LoadShaderImage
+===============
+*/
+
+byte* LoadImageFile(char *filename, qboolean *bTGA)
+{
+ byte *buffer = NULL;
+ int nLen = 0;
+ *bTGA = qtrue;
+ if (FileExists(filename))
+ {
+ LoadFileBlock(filename, &buffer);
+ }
+#ifdef _WIN32
+ else
+ {
+ PakLoadAnyFile(filename, &buffer);
+ }
+#endif
+ if ( buffer == NULL)
+ {
+ nLen = strlen(filename);
+ filename[nLen-3] = 'j';
+ filename[nLen-2] = 'p';
+ filename[nLen-1] = 'g';
+ if (FileExists(filename))
+ {
+ LoadFileBlock(filename, &buffer);
+ }
+#ifdef _WIN32
+ else
+ {
+ PakLoadAnyFile(filename, &buffer);
+ }
+#endif
+ if ( buffer )
+ {
+ *bTGA = qfalse;
+ }
+ }
+ return buffer;
+}
+
+/*
+===============
+LoadShaderImage
+===============
+*/
+static void LoadShaderImage( shaderInfo_t *si ) {
+ char filename[1024];
+ int i, count;
+ float color[4];
+ byte *buffer;
+ qboolean bTGA = qtrue;
+
+ // look for the lightimage if it is specified
+ if ( si->lightimage[0] ) {
+ sprintf( filename, "%s%s", gamedir, si->lightimage );
+ DefaultExtension( filename, ".tga" );
+ buffer = LoadImageFile(filename, &bTGA);
+ if ( buffer != NULL) {
+ goto loadTga;
+ }
+ }
+
+ // look for the editorimage if it is specified
+ if ( si->editorimage[0] ) {
+ sprintf( filename, "%s%s", gamedir, si->editorimage );
+ DefaultExtension( filename, ".tga" );
+ buffer = LoadImageFile(filename, &bTGA);
+ if ( buffer != NULL) {
+ goto loadTga;
+ }
+ }
+
+ // just try the shader name with a .tga
+ // on unix, we have case sensitivity problems...
+ sprintf( filename, "%s%s.tga", gamedir, si->shader );
+ buffer = LoadImageFile(filename, &bTGA);
+ if ( buffer != NULL) {
+ goto loadTga;
+ }
+
+ sprintf( filename, "%s%s.TGA", gamedir, si->shader );
+ buffer = LoadImageFile(filename, &bTGA);
+ if ( buffer != NULL) {
+ goto loadTga;
+ }
+
+ // couldn't load anything
+ _printf("WARNING: Couldn't find image for shader %s\n", si->shader );
+
+ si->color[0] = 1;
+ si->color[1] = 1;
+ si->color[2] = 1;
+ si->width = 64;
+ si->height = 64;
+ si->pixels = malloc( si->width * si->height * 4 );
+ memset ( si->pixels, 255, si->width * si->height * 4 );
+ return;
+
+ // load the image to get dimensions and color
+loadTga:
+ if ( bTGA) {
+ LoadTGABuffer( buffer, &si->pixels, &si->width, &si->height );
+ }
+ else {
+#ifdef _WIN32
+ LoadJPGBuff(buffer, &si->pixels, &si->width, &si->height );
+#endif
+ }
+
+ free(buffer);
+
+ count = si->width * si->height;
+
+ VectorClear( color );
+ color[ 3 ] = 0;
+ for ( i = 0 ; i < count ; i++ ) {
+ color[0] += si->pixels[ i * 4 + 0 ];
+ color[1] += si->pixels[ i * 4 + 1 ];
+ color[2] += si->pixels[ i * 4 + 2 ];
+ color[3] += si->pixels[ i * 4 + 3 ];
+ }
+ ColorNormalize( color, si->color );
+ VectorScale( color, 1.0/count, si->averageColor );
+}
+
+/*
+===============
+AllocShaderInfo
+===============
+*/
+static shaderInfo_t *AllocShaderInfo( void ) {
+ shaderInfo_t *si;
+
+ if ( numShaderInfo == MAX_SURFACE_INFO ) {
+ Error( "MAX_SURFACE_INFO" );
+ }
+ si = &shaderInfo[ numShaderInfo ];
+ numShaderInfo++;
+
+ // set defaults
+
+ si->contents = CONTENTS_SOLID;
+
+ si->backsplashFraction = DEFAULT_BACKSPLASH_FRACTION;
+ si->backsplashDistance = DEFAULT_BACKSPLASH_DISTANCE;
+
+ si->lightmapSampleSize = 0;
+ si->forceTraceLight = qfalse;
+ si->forceVLight = qfalse;
+ si->patchShadows = qfalse;
+ si->vertexShadows = qfalse;
+ si->noVertexShadows = qfalse;
+ si->forceSunLight = qfalse;
+ si->vertexScale = 1.0;
+ si->notjunc = qfalse;
+
+ return si;
+}
+
+/*
+===============
+ShaderInfoForShader
+===============
+*/
+shaderInfo_t *ShaderInfoForShader( const char *shaderName ) {
+ int i;
+ shaderInfo_t *si;
+ char shader[MAX_QPATH];
+
+ // strip off extension
+ strcpy( shader, shaderName );
+ StripExtension( shader );
+
+ // search for it
+ for ( i = 0 ; i < numShaderInfo ; i++ ) {
+ si = &shaderInfo[ i ];
+ if ( !Q_stricmp( shader, si->shader ) ) {
+ if ( !si->width ) {
+ LoadShaderImage( si );
+ }
+ return si;
+ }
+ }
+
+ si = AllocShaderInfo();
+ strcpy( si->shader, shader );
+
+ LoadShaderImage( si );
+
+ return si;
+}
+
+/*
+===============
+ParseShaderFile
+===============
+*/
+static void ParseShaderFile( const char *filename ) {
+ int i;
+ int numInfoParms = sizeof(infoParms) / sizeof(infoParms[0]);
+ shaderInfo_t *si;
+
+// qprintf( "shaderFile: %s\n", filename );
+ LoadScriptFile( filename );
+ while ( 1 ) {
+ if ( !GetToken( qtrue ) ) {
+ break;
+ }
+
+ si = AllocShaderInfo();
+ strcpy( si->shader, token );
+ MatchToken( "{" );
+ while ( 1 ) {
+ if ( !GetToken( qtrue ) ) {
+ break;
+ }
+ if ( !strcmp( token, "}" ) ) {
+ break;
+ }
+
+ // skip internal braced sections
+ if ( !strcmp( token, "{" ) ) {
+ si->hasPasses = qtrue;
+ while ( 1 ) {
+ if ( !GetToken( qtrue ) ) {
+ break;
+ }
+ if ( !strcmp( token, "}" ) ) {
+ break;
+ }
+ }
+ continue;
+ }
+
+ if ( !Q_stricmp( token, "surfaceparm" ) ) {
+ GetToken( qfalse );
+ for ( i = 0 ; i < numInfoParms ; i++ ) {
+ if ( !Q_stricmp( token, infoParms[i].name ) ) {
+ si->surfaceFlags |= infoParms[i].surfaceFlags;
+ si->contents |= infoParms[i].contents;
+ if ( infoParms[i].clearSolid ) {
+ si->contents &= ~CONTENTS_SOLID;
+ }
+ break;
+ }
+ }
+ if ( i == numInfoParms ) {
+ // we will silently ignore all tokens beginning with qer,
+ // which are QuakeEdRadient parameters
+ if ( Q_strncasecmp( token, "qer", 3 ) ) {
+ _printf( "Unknown surfaceparm: \"%s\"\n", token );
+ }
+ }
+ continue;
+ }
+
+
+ // qer_editorimage <image>
+ if ( !Q_stricmp( token, "qer_editorimage" ) ) {
+ GetToken( qfalse );
+ strcpy( si->editorimage, token );
+ DefaultExtension( si->editorimage, ".tga" );
+ continue;
+ }
+
+ // q3map_lightimage <image>
+ if ( !Q_stricmp( token, "q3map_lightimage" ) ) {
+ GetToken( qfalse );
+ strcpy( si->lightimage, token );
+ DefaultExtension( si->lightimage, ".tga" );
+ continue;
+ }
+
+ // q3map_surfacelight <value>
+ if ( !Q_stricmp( token, "q3map_surfacelight" ) ) {
+ GetToken( qfalse );
+ si->value = atoi( token );
+ continue;
+ }
+
+ // q3map_lightsubdivide <value>
+ if ( !Q_stricmp( token, "q3map_lightsubdivide" ) ) {
+ GetToken( qfalse );
+ si->lightSubdivide = atoi( token );
+ continue;
+ }
+
+ // q3map_lightmapsamplesize <value>
+ if ( !Q_stricmp( token, "q3map_lightmapsamplesize" ) ) {
+ GetToken( qfalse );
+ si->lightmapSampleSize = atoi( token );
+ continue;
+ }
+
+ // q3map_tracelight
+ if ( !Q_stricmp( token, "q3map_tracelight" ) ) {
+ si->forceTraceLight = qtrue;
+ continue;
+ }
+
+ // q3map_vlight
+ if ( !Q_stricmp( token, "q3map_vlight" ) ) {
+ si->forceVLight = qtrue;
+ continue;
+ }
+
+ // q3map_patchshadows
+ if ( !Q_stricmp( token, "q3map_patchshadows" ) ) {
+ si->patchShadows = qtrue;
+ continue;
+ }
+
+ // q3map_vertexshadows
+ if ( !Q_stricmp( token, "q3map_vertexshadows" ) ) {
+ si->vertexShadows = qtrue;
+ continue;
+ }
+
+ // q3map_novertexshadows
+ if ( !Q_stricmp( token, "q3map_novertexshadows" ) ) {
+ si->noVertexShadows = qtrue;
+ continue;
+ }
+
+ // q3map_forcesunlight
+ if ( !Q_stricmp( token, "q3map_forcesunlight" ) ) {
+ si->forceSunLight = qtrue;
+ continue;
+ }
+
+ // q3map_vertexscale
+ if ( !Q_stricmp( token, "q3map_vertexscale" ) ) {
+ GetToken( qfalse );
+ si->vertexScale = atof(token);
+ continue;
+ }
+
+ // q3map_notjunc
+ if ( !Q_stricmp( token, "q3map_notjunc" ) ) {
+ si->notjunc = qtrue;
+ continue;
+ }
+
+ // q3map_globaltexture
+ if ( !Q_stricmp( token, "q3map_globaltexture" ) ) {
+ si->globalTexture = qtrue;
+ continue;
+ }
+
+ // q3map_backsplash <percent> <distance>
+ if ( !Q_stricmp( token, "q3map_backsplash" ) ) {
+ GetToken( qfalse );
+ si->backsplashFraction = atof( token ) * 0.01;
+ GetToken( qfalse );
+ si->backsplashDistance = atof( token );
+ continue;
+ }
+
+ // q3map_backshader <shader>
+ if ( !Q_stricmp( token, "q3map_backshader" ) ) {
+ GetToken( qfalse );
+ strcpy( si->backShader, token );
+ continue;
+ }
+
+ // q3map_flare <shader>
+ if ( !Q_stricmp( token, "q3map_flare" ) ) {
+ GetToken( qfalse );
+ strcpy( si->flareShader, token );
+ continue;
+ }
+
+ // light <value>
+ // old style flare specification
+ if ( !Q_stricmp( token, "light" ) ) {
+ GetToken( qfalse );
+ strcpy( si->flareShader, "flareshader" );
+ continue;
+ }
+
+ // q3map_sun <red> <green> <blue> <intensity> <degrees> <elivation>
+ // color will be normalized, so it doesn't matter what range you use
+ // intensity falls off with angle but not distance 100 is a fairly bright sun
+ // degree of 0 = from the east, 90 = north, etc. altitude of 0 = sunrise/set, 90 = noon
+ if ( !Q_stricmp( token, "q3map_sun" ) ) {
+ float a, b;
+
+ GetToken( qfalse );
+ si->sunLight[0] = atof( token );
+ GetToken( qfalse );
+ si->sunLight[1] = atof( token );
+ GetToken( qfalse );
+ si->sunLight[2] = atof( token );
+
+ VectorNormalize( si->sunLight, si->sunLight);
+
+ GetToken( qfalse );
+ a = atof( token );
+ VectorScale( si->sunLight, a, si->sunLight);
+
+ GetToken( qfalse );
+ a = atof( token );
+ a = a / 180 * Q_PI;
+
+ GetToken( qfalse );
+ b = atof( token );
+ b = b / 180 * Q_PI;
+
+ si->sunDirection[0] = cos( a ) * cos( b );
+ si->sunDirection[1] = sin( a ) * cos( b );
+ si->sunDirection[2] = sin( b );
+
+ si->surfaceFlags |= SURF_SKY;
+ continue;
+ }
+
+ // tesssize is used to force liquid surfaces to subdivide
+ if ( !Q_stricmp( token, "tesssize" ) ) {
+ GetToken( qfalse );
+ si->subdivisions = atof( token );
+ continue;
+ }
+
+ // cull none will set twoSided
+ if ( !Q_stricmp( token, "cull" ) ) {
+ GetToken( qfalse );
+ if ( !Q_stricmp( token, "none" ) ) {
+ si->twoSided = qtrue;
+ }
+ continue;
+ }
+
+
+ // deformVertexes autosprite[2]
+ // we catch this so autosprited surfaces become point
+ // lights instead of area lights
+ if ( !Q_stricmp( token, "deformVertexes" ) ) {
+ GetToken( qfalse );
+ if ( !Q_strncasecmp( token, "autosprite", 10 ) ) {
+ si->autosprite = qtrue;
+ si->contents = CONTENTS_DETAIL;
+ }
+ continue;
+ }
+
+
+ // ignore all other tokens on the line
+
+ while ( TokenAvailable() ) {
+ GetToken( qfalse );
+ }
+ }
+ }
+}
+
+/*
+===============
+LoadShaderInfo
+===============
+*/
+#define MAX_SHADER_FILES 64
+void LoadShaderInfo( void ) {
+ char filename[1024];
+ int i;
+ char *shaderFiles[MAX_SHADER_FILES];
+ int numShaderFiles;
+
+ sprintf( filename, "%sscripts/shaderlist.txt", gamedir );
+ LoadScriptFile( filename );
+
+ numShaderFiles = 0;
+ while ( 1 ) {
+ if ( !GetToken( qtrue ) ) {
+ break;
+ }
+ shaderFiles[numShaderFiles] = malloc(MAX_OS_PATH);
+ strcpy( shaderFiles[ numShaderFiles ], token );
+ numShaderFiles++;
+ }
+
+ for ( i = 0 ; i < numShaderFiles ; i++ ) {
+ sprintf( filename, "%sscripts/%s.shader", gamedir, shaderFiles[i] );
+ ParseShaderFile( filename );
+ free(shaderFiles[i]);
+ }
+
+ qprintf( "%5i shaderInfo\n", numShaderInfo);
+}
+
diff --git a/q3map/shaders.h b/q3map/shaders.h
new file mode 100755
index 0000000..fc187c6
--- /dev/null
+++ b/q3map/shaders.h
@@ -0,0 +1,71 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+
+
+typedef struct shaderInfo_s {
+ char shader[MAX_QPATH];
+ int surfaceFlags;
+ int contents;
+ int value;
+
+ char backShader[MAX_QPATH]; // for surfaces that generate different front and back passes
+ char flareShader[MAX_QPATH]; // for light flares
+
+ float subdivisions; // from a "tesssize xxx"
+ float backsplashFraction; // floating point value, usually 0.05
+ float backsplashDistance; // default 16
+ float lightSubdivide; // default 120
+ int lightmapSampleSize; // lightmap sample size
+
+ qboolean hasPasses; // false if the shader doesn't define any rendering passes
+
+ qboolean globalTexture; // don't normalize texture repeats
+
+ qboolean twoSided; // cull none
+ qboolean autosprite; // autosprite shaders will become point lights
+ // instead of area lights
+ qboolean lightFilter; // light rays that cross surfaces of this type
+ // should test against the filter image
+ qboolean forceTraceLight; // always use -light for this surface
+ qboolean forceVLight; // always use -vlight for this surface
+ qboolean patchShadows; // have patches casting shadows when using -light for this surface
+ qboolean vertexShadows; // shadows will be casted at this surface even when vertex lit
+ qboolean noVertexShadows; // no shadows will be casted at this surface in vertex lighting
+ qboolean forceSunLight; // force sun light at this surface even tho we might not calculate shadows in vertex lighting
+ qboolean notjunc; // don't use this surface for tjunction fixing
+ float vertexScale; // vertex light scale
+
+ char editorimage[MAX_QPATH]; // use this image to generate texture coordinates
+ char lightimage[MAX_QPATH]; // use this image to generate color / averageColor
+ vec3_t color; // colorNormalized
+ vec3_t averageColor;
+
+ int width, height;
+ byte *pixels;
+
+ vec3_t sunLight;
+ vec3_t sunDirection;
+} shaderInfo_t;
+
+void LoadShaderInfo( void );
+shaderInfo_t *ShaderInfoForShader( const char *shader );
+
diff --git a/q3map/soundv.c b/q3map/soundv.c
new file mode 100755
index 0000000..1883c52
--- /dev/null
+++ b/q3map/soundv.c
@@ -0,0 +1,5742 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+/*****************************************************************************
+ * name: soundv.c
+ *****************************************************************************/
+
+#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_vsound;
+} 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 vsound_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
+} vsound_t;
+
+static float lightLinearScale = 1.0 / 8000;
+static float lightPointScale = 7500;
+static float lightAreaScale = 0.25;
+static float lightFormFactorValueScale = 3;
+static int lightDefaultSubdivide = 999; // vary by surface size?
+static vec3_t lightAmbientColor;
+
+static int portalclusters, numportals, numfaces;
+static lleaf_t *leafs;
+static lportal_t *portals;
+static int numvsounds = 0;
+static vsound_t *vsounds[MAX_LIGHTS];
+static int nostitching = 0;
+static int noalphashading = 0;
+static int nocolorshading = 0;
+static int nobackfaceculling = 0;
+static int defaulttracelight = 0;
+static int radiosity = 0;
+static int radiosity_scale;
+
+static int clustersurfaces[MAX_MAP_LEAFFACES];
+static int numclustersurfaces = 0;
+static lsurfaceTest_t *lsurfaceTest[MAX_MAP_DRAW_SURFS];
+static int numfacets;
+static float lightmappixelarea[MAX_MAP_LIGHTING/3];
+static 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 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);
+ }
+ }
+}
+
+/*
+=====================
+VS_DrawLightVolume
+=====================
+*/
+int VS_ChopWinding (winding_t *in, plane_t *split, float epsilon);
+
+void VS_DrawLightVolume(vsound_t *light, lightvolume_t *volume)
+{
+ winding_t w;
+ int i;
+ vec3_t p2, invsound;
+
+ 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, invsound);
+ VectorInverse(invsound);
+ 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, invsound, w.points[2]);
+ VectorMA(w.points[0], MAX_WORLD_COORD, invsound, 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]);
+ VS_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);
+ }
+ }
+}
+
+/*
+=============
+VS_DrawLightmapPixel
+=============
+*/
+void VS_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);
+}
+
+/*
+============
+VS_DrawPortals
+============
+*/
+void VS_DrawPortals(void)
+{
+ int j;
+ lportal_t *p;
+
+ for (j = 0; j < numportals * 2; j++)
+ {
+ p = portals + j;
+ DebugNet_DrawWinding(p->winding, 1);
+ }
+}
+
+/*
+============
+VS_DrawLeaf
+============
+*/
+void VS_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
+
+/*
+=============
+VS_SplitWinding
+=============
+*/
+int VS_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: VS_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: VS_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: VS_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: VS_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;
+}
+
+/*
+=====================
+VS_LinkSurfaceIntoCluster
+=====================
+*/
+void VS_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");
+}
+
+/*
+=====================
+VS_R_LinkSurface
+=====================
+*/
+void VS_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 = VS_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));
+ VS_R_LinkSurface(node->children[1], surfaceNum, &back);
+ nodenum = node->children[0];
+ }
+ else
+ {
+ VS_R_LinkSurface(node->children[1], surfaceNum, &back);
+ nodenum = node->children[0];
+ }
+ }
+ leafnum = -nodenum - 1;
+ cluster = dleafs[leafnum].cluster;
+ if (cluster != -1)
+ {
+ VS_LinkSurfaceIntoCluster(cluster, surfaceNum);
+ }
+}
+
+/*
+=====================
+VS_LinkSurfaces
+
+maybe link each facet seperately instead of the test surfaces?
+=====================
+*/
+void VS_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;
+ VS_R_LinkSurface(0, i, &winding);
+ }
+ }
+}
+
+/*
+=====================
+VS_TextureMatrixFromPoints
+=====================
+*/
+void VS_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" );
+ }
+*/
+ }
+}
+
+/*
+=====================
+VS_LightmapMatrixFromPoints
+=====================
+*/
+void VS_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
+
+static 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;
+}
+
+/*
+=============
+VS_PlaneFromPoints
+=============
+*/
+qboolean VS_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;
+}
+
+/*
+=====================
+VS_GenerateBoundaryForPoints
+=====================
+*/
+void VS_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 );
+}
+
+/*
+=====================
+VS_GenerateFacetFor3Points
+=====================
+*/
+qboolean VS_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 ( !VS_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];
+
+ VS_GenerateBoundaryForPoints( &f->boundaries[0], &f->plane, f->points[0], f->points[1] );
+ VS_GenerateBoundaryForPoints( &f->boundaries[1], &f->plane, f->points[1], f->points[2] );
+ VS_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;
+ }
+
+ VS_TextureMatrixFromPoints( f, a, b, c );
+ VS_LightmapMatrixFromPoints( dsurf, si, f, a, b, c );
+
+ f->numpoints = 3;
+
+ return qtrue;
+}
+
+/*
+=====================
+VS_GenerateFacetFor4Points
+
+Attempts to use four points as a planar quad
+=====================
+*/
+#define PLANAR_EPSILON 0.1
+qboolean VS_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 ( !VS_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 ( !VS_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];
+
+ VS_GenerateBoundaryForPoints( &f->boundaries[0], &f->plane, f->points[0], f->points[1] );
+ VS_GenerateBoundaryForPoints( &f->boundaries[1], &f->plane, f->points[1], f->points[2] );
+ VS_GenerateBoundaryForPoints( &f->boundaries[2], &f->plane, f->points[2], f->points[3] );
+ VS_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;
+ }
+
+ VS_TextureMatrixFromPoints( f, a, b, c );
+ VS_LightmapMatrixFromPoints( dsurf, si, f, a, b, c );
+
+ f->num = numfacets++;
+ f->numpoints = 4;
+
+ return qtrue;
+}
+
+/*
+===============
+VS_SphereFromBounds
+===============
+*/
+void VS_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 );
+}
+
+/*
+====================
+VS_FacetsForTriangleSurface
+====================
+*/
+void VS_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 ( VS_GenerateFacetFor4Points( dsurf, si, &test->facets[count], v1, v2, v4, v3 ) ) {
+ count++;
+ i++; // skip next tri
+ continue;
+ }
+ }
+ }
+
+ if (VS_GenerateFacetFor3Points( dsurf, si, &test->facets[count], v1, v2, v3 )) {
+ count++;
+ }
+ }
+
+ // we may have turned some pairs into quads
+ test->numFacets = count;
+}
+
+/*
+====================
+VS_FacetsForPatch
+====================
+*/
+void VS_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 ( VS_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 (VS_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 (VS_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);
+}
+
+/*
+=====================
+VS_InitSurfacesForTesting
+=====================
+*/
+void VS_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 vsound
+ 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_vsound = qtrue;
+ lsurfaceTest[i] = test;
+
+ if ( dsurf->surfaceType == MST_TRIANGLE_SOUP || dsurf->surfaceType == MST_PLANAR ) {
+ VS_FacetsForTriangleSurface( dsurf, si, test );
+ } else if ( dsurf->surfaceType == MST_PATCH ) {
+ VS_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 );
+ }
+ }
+ VS_SphereFromBounds( test->mins, test->maxs, test->origin, &test->radius );
+ }
+ _printf("%6d facets\n", numfacets);
+ _printf("linking surfaces...\n");
+ VS_LinkSurfaces();
+}
+
+/*
+=============
+VS_ChopWinding
+=============
+*/
+int VS_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: VS_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: VS_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;
+}
+
+/*
+=============
+VS_ChopWindingWithBrush
+
+ returns all winding fragments outside the brush
+=============
+*/
+int VS_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 = VS_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: VS_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;
+}
+
+/*
+=============
+VS_WindingAreaOutsideBrushes
+=============
+*/
+float VS_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 = VS_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;
+}
+
+/*
+=============
+VS_R_WindingAreaOutsideSolid
+=============
+*/
+float VS_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 = VS_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 += VS_R_WindingAreaOutsideSolid(&back, normal, node->children[1]);
+ nodenum = node->children[0];
+ }
+ }
+ leafnum = -nodenum - 1;
+ leaf = &dleafs[leafnum];
+ if (leaf->cluster != -1)
+ {
+ area += VS_WindingAreaOutsideBrushes(w, &dleafbrushes[leaf->firstLeafBrush], leaf->numLeafBrushes);
+ }
+ return area;
+}
+
+/*
+=============
+VS_WindingAreaOutsideSolid
+=============
+*/
+float VS_WindingAreaOutsideSolid(winding_t *w, vec3_t normal)
+{
+ return VS_R_WindingAreaOutsideSolid(w, normal, 0);
+}
+
+/*
+=============
+VS_ChopWindingWithFacet
+=============
+*/
+float VS_ChopWindingWithFacet(winding_t *w, lFacet_t *facet)
+{
+ int i;
+
+ for (i = 0; i < facet->numpoints; i++)
+ {
+ if (VS_ChopWinding(w, &facet->boundaries[i], 0) == SIDE_BACK)
+ return 0;
+ }
+ if (nostitching)
+ return WindingArea(w);
+ else
+ return VS_WindingAreaOutsideSolid(w, facet->plane.normal);
+}
+
+/*
+=============
+VS_CalcVisibleLightmapPixelArea
+
+nice brute force ;)
+=============
+*/
+void VS_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 = VS_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 += VS_ChopWindingWithFacet(&tmpw, &test->facets[j]);
+ }
+ }
+ k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + y)
+ * LIGHTMAP_WIDTH + ds->lightmapX + x;
+ lightmappixelarea[k] = area;
+ }
+ }
+ }
+}
+
+/*
+=============
+VS_FindAdjacentSurface
+=============
+*/
+int VS_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;
+}
+
+/*
+=============
+VS_SmoothenLightmapEdges
+
+this code is used to smoothen lightmaps across surface edges
+=============
+*/
+void VS_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 (!VS_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;
+ }
+ }
+ }
+ }
+}
+
+/*
+=============
+VS_FixLightmapEdges
+=============
+*/
+void VS_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;
+ }
+ }
+ //*/
+ }
+ //
+ VS_SmoothenLightmapEdges();
+}
+
+/*
+=============
+VS_ShiftPatchLightmaps
+=============
+*/
+void VS_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++;
+ }
+}
+
+/*
+=============
+VS_StoreLightmap
+=============
+*/
+void VS_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
+ VS_FixLightmapEdges();
+ //
+#ifdef LIGHTMAP_PATCHSHIFT
+ VS_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
+=============
+*/
+static 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;
+}
+
+/*
+=============
+VS_PointInLeafnum_r
+=============
+*/
+int VS_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 = VS_PointInLeafnum_r(point, node->children[0]);
+ if (dleafs[leafnum].cluster != -1)
+ return leafnum;
+ nodenum = node->children[1];
+ }
+ }
+
+ leafnum = -nodenum - 1;
+ return leafnum;
+}
+
+/*
+=============
+VS_PointInLeafnum
+=============
+*/
+int VS_PointInLeafnum(vec3_t point)
+{
+ return VS_PointInLeafnum_r(point, 0);
+}
+
+/*
+=============
+VS_LightLeafnum
+=============
+*/
+int VS_LightLeafnum(vec3_t point)
+{
+ /*
+ int leafnum;
+ dleaf_t *leaf;
+ float x, y, z;
+ vec3_t test;
+
+ leafnum = VS_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 = VS_PointInLeafnum(test);
+ leaf = &dleafs[leafnum];
+ if (leaf->cluster != -1)
+ {
+ VectorCopy(test, point);
+ return leafnum;
+ }
+ }
+ }
+ }
+ return leafnum;
+ */
+ return VS_PointInLeafnum(point);
+}
+
+//#define LIGHTPOLYS
+
+#ifdef LIGHTPOLYS
+
+winding_t *lightwindings[MAX_MAP_DRAW_SURFS];
+int numlightwindings;
+
+/*
+=============
+VS_DrawLightWindings
+=============
+*/
+void VS_DrawLightWindings(void)
+{
+ int i;
+ for (i = 0; i < numlightwindings; i++)
+ {
+#ifdef DEBUGNET
+ DebugNet_DrawWinding(lightwindings[i], 1);
+#endif
+ }
+}
+
+/*
+=============
+VS_LightSurfaceWithVolume
+=============
+*/
+void VS_LightSurfaceWithVolume(int surfaceNum, int facetNum, vsound_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 (VS_ChopWinding(w, &volume->planes[i], 0.01) == SIDE_BACK)
+ return;
+ }
+ lightwindings[numlightwindings] = w;
+ numlightwindings++;
+ if (numlightwindings >= MAX_MAP_DRAW_SURFS)
+ Error("MAX_LIGHTWINDINGS");
+}
+
+#else
+
+/*
+=============
+VS_LightSurfaceWithVolume
+=============
+*/
+/*
+int VS_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 VS_LightSurfaceWithVolume( int surfaceNum, int facetNum, vsound_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 (VS_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);
+ }
+ }
+ }
+}
+*/
+
+/*
+=============
+VS_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 VS_GetFilter(vsound_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;
+ }
+ }
+}
+
+/*
+=============
+VS_LightSurfaceWithVolume
+=============
+*/
+void VS_LightSurfaceWithVolume( int surfaceNum, int facetNum, vsound_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_vsound)
+ 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 (VS_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("VS_LightSurfaceWithVolume: x outside lightmap\n");
+ if (y < ds->lightmapY || y >= LIGHTMAP_SIZE)
+ _printf("VS_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("VS_LightSurfaceWithVolume: x outside lightmap\n");
+ if (y < ds->lightmapY || y >= LIGHTMAP_SIZE)
+ _printf("VS_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("VS_LightSurfaceWithVolume: x outside lightmap\n");
+ if (y < ds->lightmapY || y >= LIGHTMAP_SIZE)
+ _printf("VS_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 (VS_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
+ VS_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
+
+/*
+=============
+VS_SplitLightVolume
+=============
+*/
+int VS_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: VS_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: VS_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: VS_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: VS_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;
+}
+
+/*
+=============
+VS_PlaneForEdgeToWinding
+=============
+*/
+void VS_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;
+ }
+}
+
+/*
+=============
+VS_R_CastLightAtSurface
+=============
+*/
+void VS_R_FloodLight(vsound_t *light, lightvolume_t *volume, int cluster, int firstportal);
+
+void VS_R_CastLightAtSurface(vsound_t *light, lightvolume_t *volume)
+{
+ lsurfaceTest_t *test;
+ int i, n;
+
+ // light the surface with this volume
+ VS_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;
+ VS_R_FloodLight(light, volume, volume->cluster, 0);
+ if (volume->surfaceNum >= 0)
+ {
+ VS_R_CastLightAtSurface(light, volume);
+ }
+}
+
+/*
+=============
+VS_R_SplitLightVolume
+=============
+*/
+static int numvolumes = 0;
+
+int VS_R_SplitLightVolume(vsound_t *light, lightvolume_t *volume, plane_t *split, int cluster, int firstportal)
+{
+ lightvolume_t back;
+ int res;
+
+ //
+ res = VS_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
+ VS_R_FloodLight(light, &back, cluster, firstportal);
+ // if the back volume hit a surface
+ if (back.surfaceNum >= 0)
+ {
+ VS_R_CastLightAtSurface(light, &back);
+ }
+ }
+ return res;
+}
+
+/*
+=============
+VS_R_FloodLight
+=============
+*/
+void VS_R_FloodLight(vsound_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();
+ // VS_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 = VS_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 = VS_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 = VS_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 = VS_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 = VS_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
+ VS_R_FloodLight(light, volume, p->leaf, 0);
+ }
+ }
+}
+
+/*
+=============
+VS_R_FloodAreaSpotLight
+=============
+*/
+void VS_FloodAreaSpotLight(vsound_t *light, winding_t *w, int leafnum)
+{
+}
+
+/*
+=============
+VS_R_SubdivideAreaSpotLight
+=============
+*/
+void VS_R_SubdivideAreaSpotLight(vsound_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 = VS_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));
+ VS_R_SubdivideAreaSpotLight(light, node->children[1], &back);
+ nodenum = node->children[0];
+ }
+ else
+ {
+ VS_R_SubdivideAreaSpotLight(light, node->children[1], &back);
+ nodenum = node->children[0];
+ }
+ }
+ leafnum = -nodenum - 1;
+ if (dleafs[leafnum].cluster != -1)
+ {
+ VS_FloodAreaSpotLight(light, w, leafnum);
+ }
+}
+
+/*
+=============
+VS_R_FloodRadialAreaLight
+=============
+*/
+void VS_FloodRadialAreaLight(vsound_t *light, winding_t *w, int leafnum)
+{
+}
+
+/*
+=============
+VS_R_SubdivideRadialAreaLight
+=============
+*/
+void VS_R_SubdivideRadialAreaLight(vsound_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 = VS_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));
+ VS_R_SubdivideRadialAreaLight(light, node->children[1], &back);
+ nodenum = node->children[0];
+ }
+ else
+ {
+ VS_R_SubdivideRadialAreaLight(light, node->children[1], &back);
+ nodenum = node->children[0];
+ }
+ }
+ leafnum = -nodenum - 1;
+ if (dleafs[leafnum].cluster != -1)
+ {
+ VS_FloodRadialAreaLight(light, w, leafnum);
+ }
+}
+
+/*
+=============
+VS_R_FloodDirectedLight
+=============
+*/
+void VS_FloodDirectedLight(vsound_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;
+ VS_R_FloodLight(light, &volume, volume.cluster, 0);
+ if (volume.surfaceNum >= 0)
+ {
+ VS_R_CastLightAtSurface(light, &volume);
+ }
+}
+
+/*
+=============
+VS_R_SubdivideDirectedAreaLight
+=============
+*/
+void VS_R_SubdivideDirectedAreaLight(vsound_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 = VS_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));
+ VS_R_SubdivideDirectedAreaLight(light, node->children[1], &back);
+ nodenum = node->children[0];
+ }
+ else
+ {
+ VS_R_SubdivideDirectedAreaLight(light, node->children[1], &back);
+ nodenum = node->children[0];
+ }
+ }
+ leafnum = -nodenum - 1;
+ if (dleafs[leafnum].cluster != -1)
+ {
+ VS_FloodDirectedLight(light, w, leafnum);
+ }
+}
+
+/*
+=============
+VS_FloodLight
+=============
+*/
+void VS_FloodLight(vsound_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 = VS_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++)
+ {
+ VS_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));
+ VS_R_FloodLight(light, &volume, leaf->cluster, 0);
+ if (volume.surfaceNum >= 0)
+ {
+ VS_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 = VS_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++)
+ {
+ VS_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));
+ VS_R_FloodLight(light, &volume, leaf->cluster, 0);
+ if (volume.surfaceNum >= 0)
+ {
+ VS_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 = VS_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++)
+ {
+ VS_PlaneFromPoints(&volume.planes[k], light->origin, volume.points[(k+1)%volume.numplanes], volume.points[k]);
+ }
+ VS_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));
+
+ VS_R_FloodLight(light, &volume, leaf->cluster, 0);
+ if (volume.surfaceNum >= 0)
+ {
+ VS_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));
+ VS_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));
+ VS_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));
+ VS_R_SubdivideAreaSpotLight(light, 0, &winding);
+ break;
+ }
+ }
+}
+
+/*
+=============
+VS_FloodLightThread
+=============
+*/
+void VS_FloodLightThread(int num)
+{
+ VS_FloodLight(vsounds[num]);
+}
+
+/*
+=============
+VS_TestLightLeafs
+=============
+*/
+void VS_TestLightLeafs(void)
+{
+ int leafnum, i;
+ vsound_t *light;
+ dleaf_t *leaf;
+
+ for (i = 0; i < numvsounds; i++)
+ {
+ light = vsounds[i];
+ if (light->type != LIGHT_POINTRADIAL &&
+ light->type != LIGHT_POINTSPOT)
+ continue;
+ leafnum = VS_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]);
+ }
+}
+
+
+/*
+=============
+VS_DoForcedTraceLight
+=============
+*/
+// from light.c
+void TraceLtm( int num );
+
+void VS_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);
+}
+
+/*
+=============
+VS_DoForcedTraceLightSurfaces
+=============
+*/
+void VS_DoForcedTraceLightSurfaces(void)
+{
+ _printf( "forced trace light\n" );
+ RunThreadsOnIndividual( numDrawSurfaces, qtrue, VS_DoForcedTraceLight );
+}
+
+float *oldLightFloats;
+
+/*
+=============
+VS_SurfaceRadiosity
+=============
+*/
+void VS_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;
+ vsound_t vsound;
+
+ 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(&vsound, 0, sizeof(vsound_t));
+ color = &oldLightFloats[k*3];
+ // a few units away from the surface
+ VectorMA(base, 5, normal, vsound.origin);
+ ColorNormalize(color, vsound.color);
+ // ok this is crap
+ vsound.photons = VectorLength(color) * 0.05 * lightPointScale / (area * radiosity_scale);
+ // what about using a front facing light only ?
+ vsound.type = LIGHT_POINTRADIAL;
+ // flood the light from this lightmap pixel
+ VS_FloodLight(&vsound);
+ // 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);
+ }
+ }
+}
+
+/*
+=============
+VS_Radiosity
+
+this aint working real well but it's fun to play with.
+=============
+*/
+void VS_Radiosity(void) {
+
+ oldLightFloats = lightFloats;
+ lightFloats = (float *) malloc(numLightBytes * sizeof(float));
+ memcpy(lightFloats, oldLightFloats, numLightBytes * sizeof(float));
+ _printf("%7i surfaces\n", numDrawSurfaces);
+ RunThreadsOnIndividual( numDrawSurfaces, qtrue, VS_SurfaceRadiosity );
+ free(oldLightFloats);
+}
+
+/*
+=============
+VS_LightWorld
+=============
+*/
+void VS_LightWorld(void)
+{
+ int i, numcastedvolumes, numvsoundsinsolid;
+ 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, numvsounds);
+ for (i = 0; i < numvsounds; i++)
+ {
+ _printf("\r%6d", i);
+ VS_FloodLight(vsounds[i]);
+ }
+ _printf("\r%6d lights out of %d\n", i, numvsounds);
+ */
+ _printf("%7i lights\n", numvsounds);
+ RunThreadsOnIndividual( numvsounds, qtrue, VS_FloodLightThread );
+
+ numcastedvolumes = 0;
+ for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
+ if (lsurfaceTest[i])
+ numcastedvolumes += lsurfaceTest[i]->numvolumes;
+ }
+ _printf("%7i light volumes casted\n", numcastedvolumes);
+ numvsoundsinsolid = 0;
+ for (i = 0; i < numvsounds; i++)
+ {
+ if (vsounds[i]->insolid)
+ numvsoundsinsolid++;
+ }
+ _printf("%7i lights in solid\n", numvsoundsinsolid);
+ //
+ radiosity_scale = 1;
+ for (i = 0; i < radiosity; i++) {
+ VS_Radiosity();
+ radiosity_scale <<= 1;
+ }
+ //
+ VS_StoreLightmap();
+ // redo surfaces with the old light algorithm when needed
+ VS_DoForcedTraceLightSurfaces();
+}
+
+/*
+=============
+VS_CreateEntitySpeakers
+=============
+*/
+entity_t *FindTargetEntity( const char *target );
+
+void VS_CreateEntitySpeakers (void)
+{
+ int i, c_entityLights;
+ vsound_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, "speaker", 7))
+ 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;
+ }
+ }
+ vsounds[numvsounds++] = dl;
+ c_entityLights++;
+ }
+ _printf("%7i entity lights\n", c_entityLights);
+}
+
+/*
+==================
+VS_SubdivideAreaLight
+==================
+*/
+void VS_SubdivideAreaLight( shaderInfo_t *ls, winding_t *w, vec3_t normal,
+ float areaSubdivide, qboolean backsplash ) {
+ float area, value, intensity;
+ vsound_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 );
+ VS_SubdivideAreaLight( ls, front, normal, areaSubdivide, qfalse );
+ VS_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;
+ }
+
+ vsounds[numvsounds++] = 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;
+
+ vsounds[numvsounds++] = dl2;
+ }
+}
+
+/*
+==================
+VS_CreateFakeSurfaceLights
+==================
+*/
+void VS_CreateFakeSurfaceLights( void ) {
+ int i, j, side;
+ dsurface_t *ds;
+ shaderInfo_t *ls;
+ winding_t *w;
+ lFacet_t *f;
+ vsound_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;
+ vsounds[numvsounds++] = 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 );
+ }
+ VS_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 );
+ }
+ VS_SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
+ }
+ }
+ }
+
+ _printf( "%7i light emitting surfaces\n", c_surfaceLights );
+}
+
+
+/*
+==================
+VS_WindingForBrushSide
+==================
+*/
+winding_t *VS_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 = VS_ChopWinding(w, &plane, 0.1);
+ if (res == SIDE_BACK)
+ return NULL;
+ }
+ return w;
+}
+
+/*
+==================
+VS_CreateSkyLights
+==================
+*/
+void VS_CreateSkyLights(void)
+{
+ int i, j, c_skyLights;
+ dbrush_t *b;
+ shaderInfo_t *si;
+ dbrushside_t *s;
+ vsound_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;
+ VS_WindingForBrushSide(b, j, &dl->w);
+// DebugNet_DrawWinding(&dl->w, 2);
+ //
+ vsounds[numvsounds++] = dl;
+ c_skyLights++;
+ }
+ }
+ }
+ _printf("%7i light emitting sky surfaces\n", c_skyLights);
+}
+
+/*
+==================
+VS_SetPortalSphere
+==================
+*/
+void VS_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;
+}
+
+/*
+==================
+VS_PlaneFromWinding
+==================
+*/
+void VS_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);
+}
+
+/*
+==================
+VS_AllocWinding
+==================
+*/
+winding_t *VS_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;
+}
+
+/*
+============
+VS_LoadPortals
+============
+*/
+void VS_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 = VS_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
+ VS_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];
+ VS_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 = VS_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];
+ VS_SetPortalSphere (p);
+ p++;
+
+ }
+
+ fclose (f);
+}
+
+/*
+============
+VLightMain
+============
+*/
+int VSoundMain (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], "-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 -vsound [-<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");
+
+ VS_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));
+
+ VS_InitSurfacesForTesting();
+
+ VS_CalcVisibleLightmapPixelArea();
+
+ numvsounds = 0;
+ VS_CreateEntitySpeakers();
+ VS_CreateFakeSurfaceLights();
+ VS_CreateSkyLights();
+
+ VS_TestLightLeafs();
+
+ VS_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
+ VS_DrawLightWindings();
+#endif
+
+#ifdef DEBUGNET
+ DebugNet_Shutdown();
+#endif
+ return 0;
+}
diff --git a/q3map/surface.c b/q3map/surface.c
new file mode 100755
index 0000000..462449d
--- /dev/null
+++ b/q3map/surface.c
@@ -0,0 +1,1158 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+mapDrawSurface_t mapDrawSurfs[MAX_MAP_DRAW_SURFS];
+int numMapDrawSurfs;
+
+/*
+=============================================================================
+
+DRAWSURF CONSTRUCTION
+
+=============================================================================
+*/
+
+/*
+=================
+AllocDrawSurf
+=================
+*/
+mapDrawSurface_t *AllocDrawSurf( void ) {
+ mapDrawSurface_t *ds;
+
+ if ( numMapDrawSurfs >= MAX_MAP_DRAW_SURFS ) {
+ Error( "MAX_MAP_DRAW_SURFS");
+ }
+ ds = &mapDrawSurfs[ numMapDrawSurfs ];
+ numMapDrawSurfs++;
+
+ return ds;
+}
+
+/*
+=================
+DrawSurfaceForSide
+=================
+*/
+#define SNAP_FLOAT_TO_INT 8
+#define SNAP_INT_TO_FLOAT (1.0/SNAP_FLOAT_TO_INT)
+
+mapDrawSurface_t *DrawSurfaceForSide( bspbrush_t *b, side_t *s, winding_t *w ) {
+ mapDrawSurface_t *ds;
+ int i, j;
+ shaderInfo_t *si;
+ drawVert_t *dv;
+ float mins[2], maxs[2];
+
+ // brush primitive :
+ // axis base
+ vec3_t texX,texY;
+ vec_t x,y;
+
+ if ( w->numpoints > 64 ) {
+ Error( "DrawSurfaceForSide: w->numpoints = %i", w->numpoints );
+ }
+
+ si = s->shaderInfo;
+
+ ds = AllocDrawSurf();
+
+ ds->shaderInfo = si;
+ ds->mapBrush = b;
+ ds->side = s;
+ ds->fogNum = -1;
+ ds->numVerts = w->numpoints;
+ ds->verts = malloc( ds->numVerts * sizeof( *ds->verts ) );
+ memset( ds->verts, 0, ds->numVerts * sizeof( *ds->verts ) );
+
+ mins[0] = mins[1] = 99999;
+ maxs[0] = maxs[1] = -99999;
+
+ // compute s/t coordinates from brush primitive texture matrix
+ // compute axis base
+ ComputeAxisBase( mapplanes[s->planenum].normal, texX, texY );
+
+ for ( j = 0 ; j < w->numpoints ; j++ ) {
+ dv = ds->verts + j;
+
+ // round the xyz to a given precision
+ for ( i = 0 ; i < 3 ; i++ ) {
+ dv->xyz[i] = SNAP_INT_TO_FLOAT * floor( w->p[j][i] * SNAP_FLOAT_TO_INT + 0.5 );
+ }
+
+ if (g_bBrushPrimit==BPRIMIT_OLDBRUSHES)
+ {
+ // calculate texture s/t
+ dv->st[0] = s->vecs[0][3] + DotProduct( s->vecs[0], dv->xyz );
+ dv->st[1] = s->vecs[1][3] + DotProduct( s->vecs[1], dv->xyz );
+ dv->st[0] /= si->width;
+ dv->st[1] /= si->height;
+ }
+ else
+ {
+ // calculate texture s/t from brush primitive texture matrix
+ x = DotProduct( dv->xyz, texX );
+ y = DotProduct( dv->xyz, texY );
+ dv->st[0]=s->texMat[0][0]*x+s->texMat[0][1]*y+s->texMat[0][2];
+ dv->st[1]=s->texMat[1][0]*x+s->texMat[1][1]*y+s->texMat[1][2];
+ }
+
+ for ( i = 0 ; i < 2 ; i++ ) {
+ if ( dv->st[i] < mins[i] ) {
+ mins[i] = dv->st[i];
+ }
+ if ( dv->st[i] > maxs[i] ) {
+ maxs[i] = dv->st[i];
+ }
+ }
+
+ // copy normal
+ VectorCopy ( mapplanes[s->planenum].normal, dv->normal );
+ }
+
+ // adjust the texture coordinates to be as close to 0 as possible
+ if ( !si->globalTexture ) {
+ mins[0] = floor( mins[0] );
+ mins[1] = floor( mins[1] );
+ for ( i = 0 ; i < w->numpoints ; i++ ) {
+ dv = ds->verts + i;
+ dv->st[0] -= mins[0];
+ dv->st[1] -= mins[1];
+ }
+ }
+
+ return ds;
+}
+
+
+//=========================================================================
+
+
+
+
+typedef struct {
+ int planenum;
+ shaderInfo_t *shaderInfo;
+ int count;
+} sideRef_t;
+
+#define MAX_SIDE_REFS MAX_MAP_PLANES
+
+sideRef_t sideRefs[MAX_SIDE_REFS];
+int numSideRefs;
+
+void AddSideRef( side_t *side ) {
+ int i;
+
+ for ( i = 0 ; i < numSideRefs ; i++ ) {
+ if ( side->planenum == sideRefs[i].planenum
+ && side->shaderInfo == sideRefs[i].shaderInfo ) {
+ sideRefs[i].count++;
+ return;
+ }
+ }
+
+ if ( numSideRefs == MAX_SIDE_REFS ) {
+ Error( "MAX_SIDE_REFS" );
+ }
+
+ sideRefs[i].planenum = side->planenum;
+ sideRefs[i].shaderInfo = side->shaderInfo;
+ sideRefs[i].count++;
+ numSideRefs++;
+}
+
+
+/*
+=====================
+MergeSides
+
+=====================
+*/
+void MergeSides( entity_t *e, tree_t *tree ) {
+ int i;
+
+ qprintf( "----- MergeSides -----\n");
+
+ for ( i = e->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
+// AddSideRef( side );
+ }
+
+ qprintf( "%5i siderefs\n", numSideRefs );
+}
+
+//=====================================================================
+
+/*
+===================
+SubdivideDrawSurf
+===================
+*/
+void SubdivideDrawSurf( mapDrawSurface_t *ds, winding_t *w, float subdivisions ) {
+ int i;
+ int axis;
+ vec3_t bounds[2];
+ const float epsilon = 0.1;
+ int subFloor, subCeil;
+ winding_t *frontWinding, *backWinding;
+ mapDrawSurface_t *newds;
+
+ if ( !w ) {
+ return;
+ }
+ if ( w->numpoints < 3 ) {
+ Error( "SubdivideDrawSurf: Bad w->numpoints" );
+ }
+
+ ClearBounds( bounds[0], bounds[1] );
+ for ( i = 0 ; i < w->numpoints ; i++ ) {
+ AddPointToBounds( w->p[i], bounds[0], bounds[1] );
+ }
+
+ for ( axis = 0 ; axis < 3 ; axis++ ) {
+ vec3_t planePoint = { 0, 0, 0 };
+ vec3_t planeNormal = { 0, 0, 0 };
+ float d;
+
+ subFloor = floor( bounds[0][axis] / subdivisions ) * subdivisions;
+ subCeil = ceil( bounds[1][axis] / subdivisions ) * subdivisions;
+
+ planePoint[axis] = subFloor + subdivisions;
+ planeNormal[axis] = -1;
+
+ d = DotProduct( planePoint, planeNormal );
+
+ // subdivide if necessary
+ if ( subCeil - subFloor > subdivisions ) {
+ // gotta clip polygon into two polygons
+ ClipWindingEpsilon( w, planeNormal, d, epsilon, &frontWinding, &backWinding );
+
+ // the clip may not produce two polygons if it was epsilon close
+ if ( !frontWinding ) {
+ w = backWinding;
+ } else if ( !backWinding ) {
+ w = frontWinding;
+ } else {
+ SubdivideDrawSurf( ds, frontWinding, subdivisions );
+ SubdivideDrawSurf( ds, backWinding, subdivisions );
+
+ return;
+ }
+ }
+ }
+
+ // emit this polygon
+ newds = DrawSurfaceForSide( ds->mapBrush, ds->side, w );
+ newds->fogNum = ds->fogNum;
+}
+
+
+/*
+=====================
+SubdivideDrawSurfs
+
+Chop up surfaces that have subdivision attributes
+=====================
+*/
+void SubdivideDrawSurfs( entity_t *e, tree_t *tree ) {
+ int i;
+ mapDrawSurface_t *ds;
+ int numBaseDrawSurfs;
+ winding_t *w;
+ float subdivision;
+ shaderInfo_t *si;
+
+ qprintf( "----- SubdivideDrawSurfs -----\n");
+ numBaseDrawSurfs = numMapDrawSurfs;
+ for ( i = e->firstDrawSurf ; i < numBaseDrawSurfs ; i++ ) {
+ ds = &mapDrawSurfs[i];
+
+ // only subdivide brush sides, not patches or misc_models
+ if ( !ds->side ) {
+ continue;
+ }
+
+ // check subdivision for shader
+ si = ds->side->shaderInfo;
+ if ( !si ) {
+ continue;
+ }
+
+ if (ds->shaderInfo->autosprite || si->autosprite) {
+ continue;
+ }
+
+ subdivision = si->subdivisions;
+ if ( !subdivision ) {
+ continue;
+ }
+
+ w = WindingFromDrawSurf( ds );
+ ds->numVerts = 0; // remove this reference
+ SubdivideDrawSurf( ds, w, subdivision );
+ }
+
+}
+
+
+//===================================================================================
+
+/*
+====================
+ClipSideIntoTree_r
+
+Adds non-opaque leaf fragments to the convex hull
+====================
+*/
+void ClipSideIntoTree_r( winding_t *w, side_t *side, node_t *node ) {
+ plane_t *plane;
+ winding_t *front, *back;
+
+ if ( !w ) {
+ return;
+ }
+
+ if ( node->planenum != PLANENUM_LEAF ) {
+ if ( side->planenum == node->planenum ) {
+ ClipSideIntoTree_r( w, side, node->children[0] );
+ return;
+ }
+ if ( side->planenum == ( node->planenum ^ 1) ) {
+ ClipSideIntoTree_r( w, side, node->children[1] );
+ return;
+ }
+
+ plane = &mapplanes[ node->planenum ];
+ ClipWindingEpsilon ( w, plane->normal, plane->dist,
+ ON_EPSILON, &front, &back );
+ FreeWinding( w );
+
+ ClipSideIntoTree_r( front, side, node->children[0] );
+ ClipSideIntoTree_r( back, side, node->children[1] );
+
+ return;
+ }
+
+ // if opaque leaf, don't add
+ if ( !node->opaque ) {
+ AddWindingToConvexHull( w, &side->visibleHull, mapplanes[ side->planenum ].normal );
+ }
+
+ FreeWinding( w );
+ return;
+}
+
+
+/*
+=====================
+ClipSidesIntoTree
+
+Creates side->visibleHull for all visible sides
+
+The drawsurf for a side will consist of the convex hull of
+all points in non-opaque clusters, which allows overlaps
+to be trimmed off automatically.
+=====================
+*/
+void ClipSidesIntoTree( entity_t *e, tree_t *tree ) {
+ bspbrush_t *b;
+ int i;
+ winding_t *w;
+ side_t *side, *newSide;
+ shaderInfo_t *si;
+
+ qprintf( "----- ClipSidesIntoTree -----\n");
+
+ for ( b = e->brushes ; b ; b = b->next ) {
+ for ( i = 0 ; i < b->numsides ; i++ ) {
+ side = &b->sides[i];
+ if ( !side->winding) {
+ continue;
+ }
+ w = CopyWinding( side->winding );
+ side->visibleHull = NULL;
+ ClipSideIntoTree_r( w, side, tree->headnode );
+
+ w = side->visibleHull;
+ if ( !w ) {
+ continue;
+ }
+ si = side->shaderInfo;
+ if ( !si ) {
+ continue;
+ }
+ // don't create faces for non-visible sides
+ if ( si->surfaceFlags & SURF_NODRAW ) {
+ continue;
+ }
+
+ // always use the original quad winding for auto sprites
+ if ( side->shaderInfo->autosprite ) {
+ w = side->winding;
+ }
+ //
+ if ( side->bevel ) {
+ Error( "monkey tried to create draw surface for brush bevel" );
+ }
+ // save this winding as a visible surface
+ DrawSurfaceForSide( b, side, w );
+
+ // make a back side for it if needed
+ if ( !(si->contents & CONTENTS_FOG) ) {
+ continue;
+ }
+
+ // duplicate the up-facing side
+ w = ReverseWinding( w );
+
+ newSide = malloc( sizeof( *side ) );
+ *newSide = *side;
+ newSide->visibleHull = w;
+ newSide->planenum ^= 1;
+
+ // save this winding as a visible surface
+ DrawSurfaceForSide( b, newSide, w );
+
+ }
+ }
+}
+
+/*
+===================================================================================
+
+ FILTER REFERENCES DOWN THE TREE
+
+===================================================================================
+*/
+
+/*
+====================
+FilterDrawSurfIntoTree
+
+Place a reference to the given drawsurf in every leaf it contacts
+We assume that the point mesh aproximation to the curve will get a
+reference into all the leafs we need.
+====================
+*/
+int FilterMapDrawSurfIntoTree( vec3_t point, mapDrawSurface_t *ds, node_t *node ) {
+ drawSurfRef_t *dsr;
+ float d;
+ plane_t *plane;
+ int c;
+
+ if ( node->planenum != PLANENUM_LEAF ) {
+ plane = &mapplanes[ node->planenum ];
+ d = DotProduct( point, plane->normal ) - plane->dist;
+ c = 0;
+ if ( d >= -ON_EPSILON ) {
+ c += FilterMapDrawSurfIntoTree( point, ds, node->children[0] );
+ }
+ if ( d <= ON_EPSILON ) {
+ c += FilterMapDrawSurfIntoTree( point, ds, node->children[1] );
+ }
+ return c;
+ }
+
+ // if opaque leaf, don't add
+ if ( node->opaque ) {
+ return 0;
+ }
+
+ // add the drawsurf if it hasn't been already
+ for ( dsr = node->drawSurfReferences ; dsr ; dsr = dsr->nextRef ) {
+ if ( dsr->outputNumber == numDrawSurfaces ) {
+ return 0; // already referenced
+ }
+ }
+
+ dsr = malloc( sizeof( *dsr ) );
+ dsr->outputNumber = numDrawSurfaces;
+ dsr->nextRef = node->drawSurfReferences;
+ node->drawSurfReferences = dsr;
+ return 1;
+}
+
+/*
+====================
+FilterDrawSurfIntoTree_r
+
+Place a reference to the given drawsurf in every leaf it is in
+====================
+*/
+int FilterMapDrawSurfIntoTree_r( winding_t *w, mapDrawSurface_t *ds, node_t *node ) {
+ drawSurfRef_t *dsr;
+ plane_t *plane;
+ int total;
+ winding_t *front, *back;
+
+ if ( node->planenum != PLANENUM_LEAF ) {
+ plane = &mapplanes[ node->planenum ];
+ ClipWindingEpsilon ( w, plane->normal, plane->dist,
+ ON_EPSILON, &front, &back );
+
+ total = 0;
+ if ( front ) {
+ total += FilterMapDrawSurfIntoTree_r( front, ds, node->children[0] );
+ }
+ if ( back ) {
+ total += FilterMapDrawSurfIntoTree_r( back, ds, node->children[1] );
+ }
+
+ FreeWinding( w );
+ return total;
+ }
+
+ // if opaque leaf, don't add
+ if ( node->opaque ) {
+ return 0;
+ }
+
+ // add the drawsurf if it hasn't been already
+ for ( dsr = node->drawSurfReferences ; dsr ; dsr = dsr->nextRef ) {
+ if ( dsr->outputNumber == numDrawSurfaces ) {
+ return 0; // already referenced
+ }
+ }
+
+ dsr = malloc( sizeof( *dsr ) );
+ dsr->outputNumber = numDrawSurfaces;
+ dsr->nextRef = node->drawSurfReferences;
+ node->drawSurfReferences = dsr;
+ return 1;
+}
+
+/*
+====================
+FilterSideIntoTree_r
+
+Place a reference to the given drawsurf in every leaf it contacts
+====================
+*/
+int FilterSideIntoTree_r( winding_t *w, side_t *side, mapDrawSurface_t *ds, node_t *node ) {
+ drawSurfRef_t *dsr;
+ plane_t *plane;
+ winding_t *front, *back;
+ int total;
+
+ if ( !w ) {
+ return 0;
+ }
+
+ if ( node->planenum != PLANENUM_LEAF ) {
+ if ( side->planenum == node->planenum ) {
+ return FilterSideIntoTree_r( w, side, ds, node->children[0] );
+ }
+ if ( side->planenum == ( node->planenum ^ 1) ) {
+ return FilterSideIntoTree_r( w, side, ds, node->children[1] );
+ }
+
+ plane = &mapplanes[ node->planenum ];
+ ClipWindingEpsilon ( w, plane->normal, plane->dist,
+ ON_EPSILON, &front, &back );
+
+ total = FilterSideIntoTree_r( front, side, ds, node->children[0] );
+ total += FilterSideIntoTree_r( back, side, ds, node->children[1] );
+
+ FreeWinding( w );
+ return total;
+ }
+
+ // if opaque leaf, don't add
+ if ( node->opaque ) {
+ return 0;
+ }
+
+ dsr = malloc( sizeof( *dsr ) );
+ dsr->outputNumber = numDrawSurfaces;
+ dsr->nextRef = node->drawSurfReferences;
+ node->drawSurfReferences = dsr;
+
+ FreeWinding( w );
+ return 1;
+}
+
+
+/*
+=====================
+FilterFaceIntoTree
+=====================
+*/
+int FilterFaceIntoTree( mapDrawSurface_t *ds, tree_t *tree ) {
+ int l;
+ winding_t *w;
+
+ w = WindingFromDrawSurf( ds );
+ l = FilterSideIntoTree_r( w, ds->side, ds, tree->headnode );
+
+ return l;
+}
+
+
+
+/*
+=====================
+FilterPatchSurfIntoTree
+=====================
+*/
+#define SUBDIVISION_LIMIT 8.0
+int FilterPatchSurfIntoTree( mapDrawSurface_t *ds, tree_t *tree ) {
+ int i, j;
+ int l;
+ mesh_t baseMesh, *subdividedMesh;
+ winding_t *w;
+
+ baseMesh.width = ds->patchWidth;
+ baseMesh.height = ds->patchHeight;
+ baseMesh.verts = ds->verts;
+ subdividedMesh = SubdivideMesh( baseMesh, SUBDIVISION_LIMIT, 32 );
+
+ l = 0;
+ for (i = 0; i < subdividedMesh->width-1; i++) {
+ for (j = 0; j < subdividedMesh->height-1; j++) {
+ w = AllocWinding(3);
+ VectorCopy(subdividedMesh->verts[j * subdividedMesh->width + i].xyz, w->p[0]);
+ VectorCopy(subdividedMesh->verts[j * subdividedMesh->width + i + 1].xyz, w->p[1]);
+ VectorCopy(subdividedMesh->verts[(j+1) * subdividedMesh->width + i].xyz, w->p[2]);
+ w->numpoints = 3;
+ l += FilterMapDrawSurfIntoTree_r( w, ds, tree->headnode );
+ w = AllocWinding(3);
+ VectorCopy(subdividedMesh->verts[j * subdividedMesh->width + i + 1].xyz, w->p[0]);
+ VectorCopy(subdividedMesh->verts[(j+1) * subdividedMesh->width + i + 1].xyz, w->p[1]);
+ VectorCopy(subdividedMesh->verts[(j+1) * subdividedMesh->width + i].xyz, w->p[2]);
+ w->numpoints = 3;
+ l += FilterMapDrawSurfIntoTree_r( w, ds, tree->headnode );
+ }
+ }
+
+ // also use the old point filtering into the tree
+ for ( i = 0 ; i < subdividedMesh->width * subdividedMesh->height ; i++ ) {
+ l += FilterMapDrawSurfIntoTree( subdividedMesh->verts[i].xyz, ds, tree->headnode );
+ }
+
+ free(subdividedMesh);
+
+ return l;
+}
+
+
+/*
+=====================
+FilterMiscModelSurfIntoTree
+=====================
+*/
+int FilterMiscModelSurfIntoTree( mapDrawSurface_t *ds, tree_t *tree ) {
+ int i;
+ int l;
+ winding_t *w;
+
+ l = 0;
+ for (i = 0; i < ds->numIndexes-2; i++) {
+ w = AllocWinding(3);
+ VectorCopy(ds->verts[ds->indexes[i]].xyz, w->p[0]);
+ VectorCopy(ds->verts[ds->indexes[i+1]].xyz, w->p[1]);
+ VectorCopy(ds->verts[ds->indexes[i+2]].xyz, w->p[2]);
+ w->numpoints = 3;
+ l += FilterMapDrawSurfIntoTree_r( w, ds, tree->headnode );
+ }
+
+ // also use the old point filtering into the tree
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ l += FilterMapDrawSurfIntoTree( ds->verts[i].xyz, ds, tree->headnode );
+ }
+
+ return l;
+}
+
+/*
+=====================
+FilterFlareSurfIntoTree
+=====================
+*/
+int FilterFlareSurfIntoTree( mapDrawSurface_t *ds, tree_t *tree ) {
+ return FilterMapDrawSurfIntoTree( ds->lightmapOrigin, ds, tree->headnode );
+}
+
+
+//======================================================================
+
+int c_stripSurfaces, c_fanSurfaces;
+
+/*
+==================
+IsTriangleDegenerate
+
+Returns qtrue if all three points are collinear or backwards
+===================
+*/
+#define COLINEAR_AREA 10
+static qboolean IsTriangleDegenerate( drawVert_t *points, int a, int b, int c ) {
+ vec3_t v1, v2, v3;
+ float d;
+
+ VectorSubtract( points[b].xyz, points[a].xyz, v1 );
+ VectorSubtract( points[c].xyz, points[a].xyz, v2 );
+ CrossProduct( v1, v2, v3 );
+ d = VectorLength( v3 );
+
+ // assume all very small or backwards triangles will cause problems
+ if ( d < COLINEAR_AREA ) {
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+===============
+SurfaceAsTriFan
+
+The surface can't be represented as a single tristrip without
+leaving a degenerate triangle (and therefore a crack), so add
+a point in the middle and create (points-1) triangles in fan order
+===============
+*/
+static void SurfaceAsTriFan( dsurface_t *ds ) {
+ int i;
+ int colorSum[4];
+ drawVert_t *mid, *v;
+
+ // create a new point in the center of the face
+ if ( numDrawVerts == MAX_MAP_DRAW_VERTS ) {
+ Error( "MAX_MAP_DRAW_VERTS" );
+ }
+ mid = &drawVerts[ numDrawVerts ];
+ numDrawVerts++;
+
+ colorSum[0] = colorSum[1] = colorSum[2] = colorSum[3] = 0;
+
+ v = drawVerts + ds->firstVert;
+ for (i = 0 ; i < ds->numVerts ; i++, v++ ) {
+ VectorAdd( mid->xyz, v->xyz, mid->xyz );
+ mid->st[0] += v->st[0];
+ mid->st[1] += v->st[1];
+ mid->lightmap[0] += v->lightmap[0];
+ mid->lightmap[1] += v->lightmap[1];
+
+ colorSum[0] += v->color[0];
+ colorSum[1] += v->color[1];
+ colorSum[2] += v->color[2];
+ colorSum[3] += v->color[3];
+ }
+
+ mid->xyz[0] /= ds->numVerts;
+ mid->xyz[1] /= ds->numVerts;
+ mid->xyz[2] /= ds->numVerts;
+
+ mid->st[0] /= ds->numVerts;
+ mid->st[1] /= ds->numVerts;
+
+ mid->lightmap[0] /= ds->numVerts;
+ mid->lightmap[1] /= ds->numVerts;
+
+ mid->color[0] = colorSum[0] / ds->numVerts;
+ mid->color[1] = colorSum[1] / ds->numVerts;
+ mid->color[2] = colorSum[2] / ds->numVerts;
+ mid->color[3] = colorSum[3] / ds->numVerts;
+
+ VectorCopy((drawVerts+ds->firstVert)->normal, mid->normal );
+
+ // fill in indices in trifan order
+ if ( numDrawIndexes + ds->numVerts*3 > MAX_MAP_DRAW_INDEXES ) {
+ Error( "MAX_MAP_DRAWINDEXES" );
+ }
+ ds->firstIndex = numDrawIndexes;
+ ds->numIndexes = ds->numVerts*3;
+
+ //FIXME
+ // should be: for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ // set a break point and test this in a map
+ //for ( i = 0 ; i < ds->numVerts*3 ; i++ ) {
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ drawIndexes[numDrawIndexes++] = ds->numVerts;
+ drawIndexes[numDrawIndexes++] = i;
+ drawIndexes[numDrawIndexes++] = (i+1) % ds->numVerts;
+ }
+
+ ds->numVerts++;
+}
+
+
+/*
+================
+SurfaceAsTristrip
+
+Try to create indices that make (points-2) triangles in tristrip order
+================
+*/
+#define MAX_INDICES 1024
+static void SurfaceAsTristrip( dsurface_t *ds ) {
+ int i;
+ int rotate;
+ int numIndices;
+ int ni;
+ int a, b, c;
+ int indices[MAX_INDICES];
+
+ // determine the triangle strip order
+ numIndices = ( ds->numVerts - 2 ) * 3;
+ if ( numIndices > MAX_INDICES ) {
+ Error( "MAX_INDICES exceeded for surface" );
+ }
+
+ // try all possible orderings of the points looking
+ // for a strip order that isn't degenerate
+ for ( rotate = 0 ; rotate < ds->numVerts ; rotate++ ) {
+ for ( ni = 0, i = 0 ; i < ds->numVerts - 2 - i ; i++ ) {
+ a = ( ds->numVerts - 1 - i + rotate ) % ds->numVerts;
+ b = ( i + rotate ) % ds->numVerts;
+ c = ( ds->numVerts - 2 - i + rotate ) % ds->numVerts;
+
+ if ( IsTriangleDegenerate( drawVerts + ds->firstVert, a, b, c ) ) {
+ break;
+ }
+ indices[ni++] = a;
+ indices[ni++] = b;
+ indices[ni++] = c;
+
+ if ( i + 1 != ds->numVerts - 1 - i ) {
+ a = ( ds->numVerts - 2 - i + rotate ) % ds->numVerts;
+ b = ( i + rotate ) % ds->numVerts;
+ c = ( i + 1 + rotate ) % ds->numVerts;
+
+ if ( IsTriangleDegenerate( drawVerts + ds->firstVert, a, b, c ) ) {
+ break;
+ }
+ indices[ni++] = a;
+ indices[ni++] = b;
+ indices[ni++] = c;
+ }
+ }
+ if ( ni == numIndices ) {
+ break; // got it done without degenerate triangles
+ }
+ }
+
+ // if any triangle in the strip is degenerate,
+ // render from a centered fan point instead
+ if ( ni < numIndices ) {
+ c_fanSurfaces++;
+ SurfaceAsTriFan( ds );
+ return;
+ }
+
+ // a normal tristrip
+ c_stripSurfaces++;
+
+ if ( numDrawIndexes + ni > MAX_MAP_DRAW_INDEXES ) {
+ Error( "MAX_MAP_DRAW_INDEXES" );
+ }
+ ds->firstIndex = numDrawIndexes;
+ ds->numIndexes = ni;
+
+ memcpy( drawIndexes + numDrawIndexes, indices, ni * sizeof(int) );
+ numDrawIndexes += ni;
+}
+
+/*
+===============
+EmitPlanarSurf
+===============
+*/
+void EmitPlanarSurf( mapDrawSurface_t *ds ) {
+ int j;
+ dsurface_t *out;
+ drawVert_t *outv;
+
+ if ( numDrawSurfaces == MAX_MAP_DRAW_SURFS ) {
+ Error( "MAX_MAP_DRAW_SURFS" );
+ }
+ out = &drawSurfaces[ numDrawSurfaces ];
+ numDrawSurfaces++;
+
+ out->surfaceType = MST_PLANAR;
+ out->shaderNum = EmitShader( ds->shaderInfo->shader );
+ out->firstVert = numDrawVerts;
+ out->numVerts = ds->numVerts;
+ out->fogNum = ds->fogNum;
+ out->lightmapNum = ds->lightmapNum;
+ out->lightmapX = ds->lightmapX;
+ out->lightmapY = ds->lightmapY;
+ out->lightmapWidth = ds->lightmapWidth;
+ out->lightmapHeight = ds->lightmapHeight;
+
+ VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );
+ VectorCopy( ds->lightmapVecs[0], out->lightmapVecs[0] );
+ VectorCopy( ds->lightmapVecs[1], out->lightmapVecs[1] );
+ VectorCopy( ds->lightmapVecs[2], out->lightmapVecs[2] );
+
+ for ( j = 0 ; j < ds->numVerts ; j++ ) {
+ if ( numDrawVerts == MAX_MAP_DRAW_VERTS ) {
+ Error( "MAX_MAP_DRAW_VERTS" );
+ }
+ outv = &drawVerts[ numDrawVerts ];
+ numDrawVerts++;
+ memcpy( outv, &ds->verts[ j ], sizeof( *outv ) );
+ outv->color[0] = 255;
+ outv->color[1] = 255;
+ outv->color[2] = 255;
+ outv->color[3] = 255;
+ }
+
+ // create the indexes
+ SurfaceAsTristrip( out );
+}
+
+
+/*
+===============
+EmitPatchSurf
+===============
+*/
+void EmitPatchSurf( mapDrawSurface_t *ds ) {
+ int j;
+ dsurface_t *out;
+ drawVert_t *outv;
+
+ if ( numDrawSurfaces == MAX_MAP_DRAW_SURFS ) {
+ Error( "MAX_MAP_DRAW_SURFS" );
+ }
+ out = &drawSurfaces[ numDrawSurfaces ];
+ numDrawSurfaces++;
+
+ out->surfaceType = MST_PATCH;
+ out->shaderNum = EmitShader( ds->shaderInfo->shader );
+ out->firstVert = numDrawVerts;
+ out->numVerts = ds->numVerts;
+ out->firstIndex = numDrawIndexes;
+ out->numIndexes = ds->numIndexes;
+ out->patchWidth = ds->patchWidth;
+ out->patchHeight = ds->patchHeight;
+ out->fogNum = ds->fogNum;
+ out->lightmapNum = ds->lightmapNum;
+ out->lightmapX = ds->lightmapX;
+ out->lightmapY = ds->lightmapY;
+ out->lightmapWidth = ds->lightmapWidth;
+ out->lightmapHeight = ds->lightmapHeight;
+
+ VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );
+ VectorCopy( ds->lightmapVecs[0], out->lightmapVecs[0] );
+ VectorCopy( ds->lightmapVecs[1], out->lightmapVecs[1] );
+ VectorCopy( ds->lightmapVecs[2], out->lightmapVecs[2] );
+
+ for ( j = 0 ; j < ds->numVerts ; j++ ) {
+ if ( numDrawVerts == MAX_MAP_DRAW_VERTS ) {
+ Error( "MAX_MAP_DRAW_VERTS" );
+ }
+ outv = &drawVerts[ numDrawVerts ];
+ numDrawVerts++;
+ memcpy( outv, &ds->verts[ j ], sizeof( *outv ) );
+ outv->color[0] = 255;
+ outv->color[1] = 255;
+ outv->color[2] = 255;
+ outv->color[3] = 255;
+ }
+
+ for ( j = 0 ; j < ds->numIndexes ; j++ ) {
+ if ( numDrawIndexes == MAX_MAP_DRAW_INDEXES ) {
+ Error( "MAX_MAP_DRAW_INDEXES" );
+ }
+ drawIndexes[ numDrawIndexes ] = ds->indexes[ j ];
+ numDrawIndexes++;
+ }
+}
+
+/*
+===============
+EmitFlareSurf
+===============
+*/
+void EmitFlareSurf( mapDrawSurface_t *ds ) {
+ dsurface_t *out;
+
+ if ( numDrawSurfaces == MAX_MAP_DRAW_SURFS ) {
+ Error( "MAX_MAP_DRAW_SURFS" );
+ }
+ out = &drawSurfaces[ numDrawSurfaces ];
+ numDrawSurfaces++;
+
+ out->surfaceType = MST_FLARE;
+ out->shaderNum = EmitShader( ds->shaderInfo->shader );
+ out->fogNum = ds->fogNum;
+
+ VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );
+ VectorCopy( ds->lightmapVecs[0], out->lightmapVecs[0] ); // color
+ VectorCopy( ds->lightmapVecs[2], out->lightmapVecs[2] );
+}
+
+
+/*
+===============
+EmitModelSurf
+===============
+*/
+void EmitModelSurf( mapDrawSurface_t *ds ) {
+ int j;
+ dsurface_t *out;
+ drawVert_t *outv;
+
+ if ( numDrawSurfaces == MAX_MAP_DRAW_SURFS ) {
+ Error( "MAX_MAP_DRAW_SURFS" );
+ }
+ out = &drawSurfaces[ numDrawSurfaces ];
+ numDrawSurfaces++;
+
+ out->surfaceType = MST_TRIANGLE_SOUP;
+ out->shaderNum = EmitShader( ds->shaderInfo->shader );
+ out->firstVert = numDrawVerts;
+ out->numVerts = ds->numVerts;
+ out->firstIndex = numDrawIndexes;
+ out->numIndexes = ds->numIndexes;
+ out->patchWidth = ds->patchWidth;
+ out->patchHeight = ds->patchHeight;
+ out->fogNum = ds->fogNum;
+ out->lightmapNum = ds->lightmapNum;
+ out->lightmapX = ds->lightmapX;
+ out->lightmapY = ds->lightmapY;
+ out->lightmapWidth = ds->lightmapWidth;
+ out->lightmapHeight = ds->lightmapHeight;
+
+ VectorCopy( ds->lightmapOrigin, out->lightmapOrigin );
+ VectorCopy( ds->lightmapVecs[0], out->lightmapVecs[0] );
+ VectorCopy( ds->lightmapVecs[1], out->lightmapVecs[1] );
+ VectorCopy( ds->lightmapVecs[2], out->lightmapVecs[2] );
+
+ for ( j = 0 ; j < ds->numVerts ; j++ ) {
+ if ( numDrawVerts == MAX_MAP_DRAW_VERTS ) {
+ Error( "MAX_MAP_DRAW_VERTS" );
+ }
+ outv = &drawVerts[ numDrawVerts ];
+ numDrawVerts++;
+ memcpy( outv, &ds->verts[ j ], sizeof( *outv ) );
+ outv->color[0] = 255;
+ outv->color[1] = 255;
+ outv->color[2] = 255;
+ }
+
+ for ( j = 0 ; j < ds->numIndexes ; j++ ) {
+ if ( numDrawIndexes == MAX_MAP_DRAW_INDEXES ) {
+ Error( "MAX_MAP_DRAW_INDEXES" );
+ }
+ drawIndexes[ numDrawIndexes ] = ds->indexes[ j ];
+ numDrawIndexes++;
+ }
+}
+
+//======================================================================
+
+/*
+==================
+CreateFlareSurface
+
+Light flares from surface lights become
+==================
+*/
+void CreateFlareSurface( mapDrawSurface_t *faceDs ) {
+ mapDrawSurface_t *ds;
+ int i;
+
+ ds = AllocDrawSurf();
+
+ if ( faceDs->shaderInfo->flareShader[0] ) {
+ ds->shaderInfo = ShaderInfoForShader( faceDs->shaderInfo->flareShader );
+ } else {
+ ds->shaderInfo = ShaderInfoForShader( "flareshader" );
+ }
+ ds->flareSurface = qtrue;
+ VectorCopy( faceDs->lightmapVecs[2], ds->lightmapVecs[2] );
+
+ // find midpoint
+ VectorClear( ds->lightmapOrigin );
+ for ( i = 0 ; i < faceDs->numVerts ; i++ ) {
+ VectorAdd( ds->lightmapOrigin, faceDs->verts[i].xyz, ds->lightmapOrigin );
+ }
+ VectorScale( ds->lightmapOrigin, 1.0/faceDs->numVerts, ds->lightmapOrigin );
+
+ VectorMA( ds->lightmapOrigin, 2, ds->lightmapVecs[2], ds->lightmapOrigin );
+
+ VectorCopy( faceDs->shaderInfo->color, ds->lightmapVecs[0] );
+
+ // FIXME: fog
+}
+
+/*
+=====================
+FilterDrawsurfsIntoTree
+
+Upon completion, all drawsurfs that actually generate a reference
+will have been emited to the bspfile arrays, and the references
+will have valid final indexes
+=====================
+*/
+void FilterDrawsurfsIntoTree( entity_t *e, tree_t *tree ) {
+ int i;
+ mapDrawSurface_t *ds;
+ int refs;
+ int c_surfs, c_refs;
+
+ qprintf( "----- FilterDrawsurfsIntoTree -----\n");
+
+ c_surfs = 0;
+ c_refs = 0;
+ for ( i = e->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
+ ds = &mapDrawSurfs[i];
+
+ if ( !ds->numVerts && !ds->flareSurface ) {
+ continue;
+ }
+ if ( ds->miscModel ) {
+ refs = FilterMiscModelSurfIntoTree( ds, tree );
+ EmitModelSurf( ds );
+ } else if ( ds->patch ) {
+ refs = FilterPatchSurfIntoTree( ds, tree );
+ EmitPatchSurf( ds );
+ } else if ( ds->flareSurface ) {
+ refs = FilterFlareSurfIntoTree( ds, tree );
+ EmitFlareSurf( ds );
+ } else {
+ refs = FilterFaceIntoTree( ds, tree );
+// if ( ds->shaderInfo->value >= 1000 ) { // ds->shaderInfo->flareShader[0] ) {
+ if ( ds->shaderInfo->flareShader[0] ) {
+ CreateFlareSurface( ds );
+ }
+ EmitPlanarSurf( ds );
+ }
+ if ( refs > 0 ) {
+ c_surfs++;
+ c_refs += refs;
+ }
+ }
+ qprintf( "%5i emited drawsurfs\n", c_surfs );
+ qprintf( "%5i references\n", c_refs );
+ qprintf( "%5i stripfaces\n", c_stripSurfaces );
+ qprintf( "%5i fanfaces\n", c_fanSurfaces );
+}
+
+
+
diff --git a/q3map/terrain.c b/q3map/terrain.c
new file mode 100755
index 0000000..9446737
--- /dev/null
+++ b/q3map/terrain.c
@@ -0,0 +1,1255 @@
+/*
+===========================================================================
+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 "qbsp.h"
+#include <assert.h>
+
+#define SURF_WIDTH 2048
+#define SURF_HEIGHT 2048
+
+#define GROW_VERTS 512
+#define GROW_INDICES 512
+#define GROW_SURFACES 128
+
+#define VectorSet(v, x, y, z) v[0] = x;v[1] = y;v[2] = z;
+
+void QuakeTextureVecs( plane_t *plane, vec_t shift[2], vec_t rotate, vec_t scale[2], vec_t mappingVecs[2][4] );
+
+typedef struct {
+ shaderInfo_t *shader;
+ int x, y;
+
+ int maxVerts;
+ int numVerts;
+ drawVert_t *verts;
+
+ int maxIndexes;
+ int numIndexes;
+ int *indexes;
+} terrainSurf_t;
+
+static terrainSurf_t *surfaces = NULL;
+static terrainSurf_t *lastSurface = NULL;
+static int numsurfaces = 0;
+static int maxsurfaces = 0;
+
+/*
+================
+ShaderForLayer
+================
+*/
+shaderInfo_t *ShaderForLayer( int minlayer, int maxlayer, const char *shadername ) {
+ char shader[ MAX_QPATH ];
+
+ if ( minlayer == maxlayer ) {
+ sprintf( shader, "textures/%s_%d", shadername, maxlayer );
+ } else {
+ sprintf( shader, "textures/%s_%dto%d", shadername, minlayer, maxlayer );
+ }
+
+ return ShaderInfoForShader( shader );
+}
+
+/*
+================
+CompareVert
+================
+*/
+qboolean CompareVert( drawVert_t *v1, drawVert_t *v2, qboolean checkst ) {
+ int i;
+
+ for( i = 0; i < 3; i++ ) {
+ if ( floor( v1->xyz[ i ] + 0.1 ) != floor( v2->xyz[ i ] + 0.1 ) ) {
+ return qfalse;
+ }
+ if ( checkst && ( ( v1->st[ 0 ] != v2->st[ 0 ] ) || ( v1->st[ 1 ] != v2->st[ 1 ] ) ) ) {
+ return qfalse;
+ }
+ }
+
+ return qtrue;
+}
+
+/*
+================
+LoadAlphaMap
+================
+*/
+byte *LoadAlphaMap( int *num_layers, int *alphawidth, int *alphaheight ) {
+ int *alphamap32;
+ byte *alphamap;
+ const char *alphamapname;
+ char ext[ 128 ];
+ int width;
+ int height;
+ int layers;
+ int size;
+ int i;
+
+ assert( alphawidth );
+ assert( alphaheight );
+ assert( num_layers );
+
+ layers = atoi( ValueForKey( mapent, "layers" ) );
+ if ( layers < 1 ) {
+ Error ("SetTerrainTextures: invalid value for 'layers' (%d)", layers );
+ }
+
+ alphamapname = ValueForKey( mapent, "alphamap" );
+ if ( !alphamapname[ 0 ] ) {
+ Error ("LoadAlphaMap: No alphamap specified on terrain" );
+ }
+
+ ExtractFileExtension( alphamapname, ext);
+ if ( !Q_stricmp( ext, "tga" ) ) {
+ Load32BitImage( ExpandGamePath( alphamapname ), &alphamap32, &width, &height );
+
+ size = width * height;
+ alphamap = malloc( size );
+ for( i = 0; i < size; i++ ) {
+ alphamap[ i ] = ( ( alphamap32[ i ] & 0xff ) * layers ) / 256;
+ if ( alphamap[ i ] >= layers ) {
+ alphamap[ i ] = layers - 1;
+ }
+ }
+ } else {
+ Load256Image( ExpandGamePath( alphamapname ), &alphamap, NULL, &width, &height );
+ size = width * height;
+ for( i = 0; i < size; i++ ) {
+ if ( alphamap[ i ] >= layers ) {
+ alphamap[ i ] = layers - 1;
+ }
+ }
+ }
+
+ if ( ( width < 2 ) || ( height < 2 ) ) {
+ Error ("LoadAlphaMap: alphamap width/height must be at least 2x2." );
+ }
+
+ *num_layers = layers;
+ *alphawidth = width;
+ *alphaheight = height;
+
+ return alphamap;
+}
+
+/*
+================
+CalcTerrainSize
+================
+*/
+void CalcTerrainSize( vec3_t mins, vec3_t maxs, vec3_t size ) {
+ bspbrush_t *brush;
+ int i;
+ const char *key;
+
+ // calculate the size of the terrain
+ ClearBounds( mins, maxs );
+ for( brush = mapent->brushes; brush != NULL; brush = brush->next ) {
+ AddPointToBounds( brush->mins, mins, maxs );
+ AddPointToBounds( brush->maxs, mins, maxs );
+ }
+
+ key = ValueForKey( mapent, "min" );
+ if ( key[ 0 ] ) {
+ GetVectorForKey( mapent, "min", mins );
+ }
+
+ key = ValueForKey( mapent, "max" );
+ if ( key[ 0 ] ) {
+ GetVectorForKey( mapent, "max", maxs );
+ }
+
+ for( i = 0; i < 3; i++ ) {
+ mins[ i ] = floor( mins[ i ] + 0.1 );
+ maxs[ i ] = floor( maxs[ i ] + 0.1 );
+ }
+
+ VectorSubtract( maxs, mins, size );
+
+ if ( ( size[ 0 ] <= 0 ) || ( size[ 1 ] <= 0 ) ) {
+ Error ("CalcTerrainSize: Invalid terrain size: %fx%f", size[ 0 ], size[ 1 ] );
+ }
+}
+
+/*
+==================
+IsTriangleDegenerate
+
+Returns qtrue if all three points are collinear or backwards
+===================
+*/
+#define COLINEAR_AREA 10
+static qboolean IsTriangleDegenerate( drawVert_t *points, int a, int b, int c ) {
+ vec3_t v1, v2, v3;
+ float d;
+
+ VectorSubtract( points[b].xyz, points[a].xyz, v1 );
+ VectorSubtract( points[c].xyz, points[a].xyz, v2 );
+ CrossProduct( v1, v2, v3 );
+ d = VectorLength( v3 );
+
+ // assume all very small or backwards triangles will cause problems
+ if ( d < COLINEAR_AREA ) {
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+===============
+SideAsTriFan
+
+The surface can't be represented as a single tristrip without
+leaving a degenerate triangle (and therefore a crack), so add
+a point in the middle and create (points-1) triangles in fan order
+===============
+*/
+static void SideAsTriFan( terrainSurf_t *surf, int *index, int num ) {
+ int i;
+ int colorSum[4];
+ drawVert_t *mid, *v;
+
+ // make sure we have enough space for a new vert
+ if ( surf->numVerts >= surf->maxVerts ) {
+ surf->maxVerts += GROW_VERTS;
+ surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
+ }
+
+ // create a new point in the center of the face
+ mid = &surf->verts[ surf->numVerts ];
+ surf->numVerts++;
+
+ colorSum[0] = colorSum[1] = colorSum[2] = colorSum[3] = 0;
+
+ for (i = 0 ; i < num; i++ ) {
+ v = &surf->verts[ index[ i ] ];
+ VectorAdd( mid->xyz, v->xyz, mid->xyz );
+ mid->st[0] += v->st[0];
+ mid->st[1] += v->st[1];
+ mid->lightmap[0] += v->lightmap[0];
+ mid->lightmap[1] += v->lightmap[1];
+
+ colorSum[0] += v->color[0];
+ colorSum[1] += v->color[1];
+ colorSum[2] += v->color[2];
+ colorSum[3] += v->color[3];
+ }
+
+ mid->xyz[0] /= num;
+ mid->xyz[1] /= num;
+ mid->xyz[2] /= num;
+
+ mid->st[0] /= num;
+ mid->st[1] /= num;
+
+ mid->lightmap[0] /= num;
+ mid->lightmap[1] /= num;
+
+ mid->color[0] = colorSum[0] / num;
+ mid->color[1] = colorSum[1] / num;
+ mid->color[2] = colorSum[2] / num;
+ mid->color[3] = colorSum[3] / num;
+
+ // fill in indices in trifan order
+ if ( surf->numIndexes + num * 3 > surf->maxIndexes ) {
+ surf->maxIndexes = surf->numIndexes + num * 3;
+ surf->indexes = realloc( surf->indexes, surf->maxIndexes * sizeof( *surf->indexes ) );
+ }
+
+
+ for ( i = 0 ; i < num; i++ ) {
+ surf->indexes[ surf->numIndexes++ ] = surf->numVerts - 1;
+ surf->indexes[ surf->numIndexes++ ] = index[ i ];
+ surf->indexes[ surf->numIndexes++ ] = index[ (i+1) % ( surf->numVerts - 1 ) ];
+ }
+}
+/*
+================
+SideAsTristrip
+
+Try to create indices that make (points-2) triangles in tristrip order
+================
+*/
+#define MAX_INDICES 1024
+static void SideAsTristrip( terrainSurf_t *surf, int *index, int num ) {
+ int i;
+ int rotate;
+ int numIndices;
+ int ni;
+ int a, b, c;
+ int indices[ MAX_INDICES ];
+
+ // determine the triangle strip order
+ numIndices = ( num - 2 ) * 3;
+ if ( numIndices > MAX_INDICES ) {
+ Error( "MAX_INDICES exceeded for surface" );
+ }
+
+ // try all possible orderings of the points looking
+ // for a strip order that isn't degenerate
+ for ( rotate = 0 ; rotate < num; rotate++ ) {
+ for ( ni = 0, i = 0 ; i < num - 2 - i ; i++ ) {
+ a = index[ ( num - 1 - i + rotate ) % num ];
+ b = index[ ( i + rotate ) % num ];
+ c = index[ ( num - 2 - i + rotate ) % num ];
+
+ if ( IsTriangleDegenerate( surf->verts, a, b, c ) ) {
+ break;
+ }
+ indices[ni++] = a;
+ indices[ni++] = b;
+ indices[ni++] = c;
+
+ if ( i + 1 != num - 1 - i ) {
+ a = index[ ( num - 2 - i + rotate ) % num ];
+ b = index[ ( i + rotate ) % num ];
+ c = index[ ( i + 1 + rotate ) % num ];
+
+ if ( IsTriangleDegenerate( surf->verts, a, b, c ) ) {
+ break;
+ }
+ indices[ni++] = a;
+ indices[ni++] = b;
+ indices[ni++] = c;
+ }
+ }
+ if ( ni == numIndices ) {
+ break; // got it done without degenerate triangles
+ }
+ }
+
+ // if any triangle in the strip is degenerate,
+ // render from a centered fan point instead
+ if ( ni < numIndices ) {
+ SideAsTriFan( surf, index, num );
+ return;
+ }
+
+ // a normal tristrip
+ if ( surf->numIndexes + ni > surf->maxIndexes ) {
+ surf->maxIndexes = surf->numIndexes + ni;
+ surf->indexes = realloc( surf->indexes, surf->maxIndexes * sizeof( *surf->indexes ) );
+ }
+
+ memcpy( surf->indexes + surf->numIndexes, indices, ni * sizeof( *surf->indexes ) );
+ surf->numIndexes += ni;
+}
+
+/*
+================
+CreateTerrainSurface
+================
+*/
+void CreateTerrainSurface( terrainSurf_t *surf, shaderInfo_t *shader ) {
+ int i, j, k;
+ drawVert_t *out;
+ drawVert_t *in;
+ mapDrawSurface_t *newsurf;
+
+ newsurf = AllocDrawSurf();
+
+ newsurf->miscModel = qtrue;
+ newsurf->shaderInfo = shader;
+ newsurf->lightmapNum = -1;
+ newsurf->fogNum = -1;
+ newsurf->numIndexes = surf->numIndexes;
+ newsurf->numVerts = surf->numVerts;
+
+ // copy the indices
+ newsurf->indexes = malloc( surf->numIndexes * sizeof( *newsurf->indexes ) );
+ memcpy( newsurf->indexes, surf->indexes, surf->numIndexes * sizeof( *newsurf->indexes ) );
+
+ // allocate the vertices
+ newsurf->verts = malloc( surf->numVerts * sizeof( *newsurf->verts ) );
+ memset( newsurf->verts, 0, surf->numVerts * sizeof( *newsurf->verts ) );
+
+ // calculate the surface verts
+ out = newsurf->verts;
+ for( i = 0; i < newsurf->numVerts; i++, out++ ) {
+ VectorCopy( surf->verts[ i ].xyz, out->xyz );
+
+ // set the texture coordinates
+ out->st[ 0 ] = surf->verts[ i ].st[ 0 ];
+ out->st[ 1 ] = surf->verts[ i ].st[ 1 ];
+
+ // the colors will be set by the lighting pass
+ out->color[0] = 255;
+ out->color[1] = 255;
+ out->color[2] = 255;
+ out->color[3] = surf->verts[ i ].color[ 3 ];
+
+ // calculate the vertex normal
+ VectorClear( out->normal );
+ for( j = 0; j < numsurfaces; j++ ) {
+ in = surfaces[ j ].verts;
+ for( k = 0; k < surfaces[ j ].numVerts; k++, in++ ) {
+ if ( CompareVert( out, in, qfalse ) ) {
+ VectorAdd( out->normal, in->normal, out->normal );
+ }
+ }
+ }
+
+ VectorNormalize( out->normal, out->normal );
+ }
+}
+
+/*
+================
+EmitTerrainVerts
+================
+*/
+void EmitTerrainVerts( side_t *side, terrainSurf_t *surf, int maxlayer, int alpha[ MAX_POINTS_ON_WINDING ], qboolean projecttexture ) {
+ int i;
+ int j;
+ drawVert_t *vert;
+ int *indices;
+ int numindices;
+ int maxindices;
+ int xyplane;
+ vec3_t xynorm = { 0, 0, 1 };
+ vec_t shift[ 2 ] = { 0, 0 };
+ vec_t scale[ 2 ] = { 0.5, 0.5 };
+ float vecs[ 2 ][ 4 ];
+ static int numtimes = 0;
+
+ numtimes++;
+
+ if ( !surf->verts ) {
+ surf->numVerts = 0;
+ surf->maxVerts = GROW_VERTS;
+ surf->verts = malloc( surf->maxVerts * sizeof( *surf->verts ) );
+
+ surf->numIndexes = 0;
+ surf->maxIndexes = GROW_INDICES;
+ surf->indexes = malloc( surf->maxIndexes * sizeof( *surf->indexes ) );
+ }
+
+ // calculate the texture coordinate vectors
+ xyplane = FindFloatPlane( xynorm, 0 );
+ QuakeTextureVecs( &mapplanes[ xyplane ], shift, 0, scale, vecs );
+
+ // emit the vertexes
+ numindices = 0;
+ maxindices = surf->maxIndexes;
+ indices = malloc ( maxindices * sizeof( *indices ) );
+
+ for ( i = 0; i < side->winding->numpoints; i++ ) {
+ vert = &surf->verts[ surf->numVerts ];
+
+ // set the final alpha value--0 for texture 1, 255 for texture 2
+ if ( alpha[ i ] < maxlayer ) {
+ vert->color[3] = 0;
+ } else {
+ vert->color[3] = 255;
+ }
+
+ vert->xyz[ 0 ] = floor( side->winding->p[ i ][ 0 ] + 0.1f );
+ vert->xyz[ 1 ] = floor( side->winding->p[ i ][ 1 ] + 0.1f );
+ vert->xyz[ 2 ] = floor( side->winding->p[ i ][ 2 ] + 0.1f );
+
+ // set the texture coordinates
+ if ( projecttexture ) {
+ vert->st[0] = ( vecs[0][3] + DotProduct( vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
+ vert->st[1] = ( vecs[1][3] + DotProduct( vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
+ } else {
+ vert->st[0] = ( side->vecs[0][3] + DotProduct( side->vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
+ vert->st[1] = ( side->vecs[1][3] + DotProduct( side->vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
+ }
+
+ VectorCopy( mapplanes[ side->planenum ].normal, vert->normal );
+
+ for( j = 0; j < surf->numVerts; j++ ) {
+ if ( CompareVert( vert, &surf->verts[ j ], qtrue ) ) {
+ break;
+ }
+ }
+
+ if ( numindices >= maxindices ) {
+ maxindices += GROW_INDICES;
+ indices = realloc( indices, maxindices * sizeof( *indices ) );
+ }
+
+ if ( j != surf->numVerts ) {
+ indices[ numindices++ ] = j;
+ } else {
+ indices[ numindices++ ] = surf->numVerts;
+ surf->numVerts++;
+ if ( surf->numVerts >= surf->maxVerts ) {
+ surf->maxVerts += GROW_VERTS;
+ surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
+ }
+ }
+ }
+
+ SideAsTristrip( surf, indices, numindices );
+
+ free( indices );
+}
+
+/*
+================
+SurfaceForShader
+================
+*/
+terrainSurf_t *SurfaceForShader( shaderInfo_t *shader, int x, int y ) {
+ int i;
+
+ if ( lastSurface && ( lastSurface->shader == shader ) && ( lastSurface->x == x ) && ( lastSurface->y == y ) ) {
+ return lastSurface;
+ }
+
+ lastSurface = surfaces;
+ for( i = 0; i < numsurfaces; i++, lastSurface++ ) {
+ if ( ( lastSurface->shader == shader ) && ( lastSurface->x == x ) && ( lastSurface->y == y ) ) {
+ return lastSurface;
+ }
+ }
+
+ if ( numsurfaces >= maxsurfaces ) {
+ maxsurfaces += GROW_SURFACES;
+ surfaces = realloc( surfaces, maxsurfaces * sizeof( *surfaces ) );
+ memset( surfaces + numsurfaces + 1, 0, ( maxsurfaces - numsurfaces - 1 ) * sizeof( *surfaces ) );
+ }
+
+ lastSurface= &surfaces[ numsurfaces++ ];
+ lastSurface->shader = shader;
+ lastSurface->x = x;
+ lastSurface->y = y;
+
+ return lastSurface;
+}
+
+/*
+================
+SetTerrainTextures
+================
+*/
+void SetTerrainTextures( void ) {
+ int i;
+ int x, y;
+ int layer;
+ int minlayer, maxlayer;
+ float s, t;
+ float min_s, min_t;
+ int alpha[ MAX_POINTS_ON_WINDING ];
+ shaderInfo_t *si, *terrainShader;
+ bspbrush_t *brush;
+ side_t *side;
+ const char *shadername;
+ vec3_t mins, maxs;
+ vec3_t size;
+ int surfwidth, surfheight, surfsize;
+ terrainSurf_t *surf;
+ byte *alphamap;
+ int alphawidth, alphaheight;
+ int num_layers;
+ extern qboolean onlyents;
+
+ if ( onlyents ) {
+ return;
+ }
+
+ shadername = ValueForKey( mapent, "shader" );
+ if ( !shadername[ 0 ] ) {
+ Error ("SetTerrainTextures: shader not specified" );
+ }
+
+ alphamap = LoadAlphaMap( &num_layers, &alphawidth, &alphaheight );
+
+ mapent->firstDrawSurf = numMapDrawSurfs;
+
+ // calculate the size of the terrain
+ CalcTerrainSize( mins, maxs, size );
+
+ surfwidth = ( size[ 0 ] + SURF_WIDTH - 1 ) / SURF_WIDTH;
+ surfheight = ( size[ 1 ] + SURF_HEIGHT - 1 ) / SURF_HEIGHT;
+ surfsize = surfwidth * surfheight;
+
+ lastSurface = NULL;
+ numsurfaces = 0;
+ maxsurfaces = 0;
+ for( i = num_layers; i > 0; i-- ) {
+ maxsurfaces += i * surfsize;
+ }
+ surfaces = malloc( maxsurfaces * sizeof( *surfaces ) );
+ memset( surfaces, 0, maxsurfaces * sizeof( *surfaces ) );
+
+ terrainShader = ShaderInfoForShader( "textures/common/terrain" );
+
+ for( brush = mapent->brushes; brush != NULL; brush = brush->next ) {
+ // only create surfaces for sides marked as terrain
+ for( side = brush->sides; side < &brush->sides[ brush->numsides ]; side++ ) {
+ if ( !side->shaderInfo ) {
+ continue;
+ }
+
+ if ( ( ( side->surfaceFlags | side->shaderInfo->surfaceFlags ) & SURF_NODRAW ) && !strstr( side->shaderInfo->shader, "terrain" ) ) {
+ continue;
+ }
+
+ minlayer = num_layers;
+ maxlayer = 0;
+
+ // project each point of the winding onto the alphamap to determine which
+ // textures to blend
+ min_s = 1.0;
+ min_t = 1.0;
+ for( i = 0; i < side->winding->numpoints; i++ ) {
+ s = floor( side->winding->p[ i ][ 0 ] + 0.1f - mins[ 0 ] ) / size[ 0 ];
+ t = floor( side->winding->p[ i ][ 1 ] + 0.1f - mins[ 0 ] ) / size[ 1 ];
+
+ if ( s < 0 ) {
+ s = 0;
+ }
+
+ if ( t < 0 ) {
+ t = 0;
+ }
+
+ if ( s >= 1.0 ) {
+ s = 1.0;
+ }
+
+ if ( t >= 1.0 ) {
+ t = 1.0;
+ }
+
+ if ( s < min_s ) {
+ min_s = s;
+ }
+
+ if ( t < min_t ) {
+ min_t = t;
+ }
+
+ x = ( alphawidth - 1 ) * s;
+ y = ( alphaheight - 1 ) * t;
+
+ layer = alphamap[ x + y * alphawidth ];
+ if ( layer < minlayer ) {
+ minlayer = layer;
+ }
+
+ if ( layer > maxlayer ) {
+ maxlayer = layer;
+ }
+
+ alpha[ i ] = layer;
+ }
+
+ x = min_s * surfwidth;
+ if ( x >= surfwidth ) {
+ x = surfwidth - 1;
+ }
+
+ y = min_t * surfheight;
+ if ( y >= surfheight ) {
+ y = surfheight - 1;
+ }
+
+ if ( strstr( side->shaderInfo->shader, "terrain" ) ) {
+ si = ShaderForLayer( minlayer, maxlayer, shadername );
+ if ( showseams ) {
+ for( i = 0; i < side->winding->numpoints; i++ ) {
+ if ( ( alpha[ i ] != minlayer ) && ( alpha[ i ] != maxlayer ) ) {
+ si = ShaderInfoForShader( "textures/common/white" );
+ break;
+ }
+ }
+ }
+ surf = SurfaceForShader( si, x, y );
+ EmitTerrainVerts( side, surf, maxlayer, alpha, qtrue );
+ } else {
+ si = side->shaderInfo;
+ side->shaderInfo = terrainShader;
+ surf = SurfaceForShader( si, x, y );
+ EmitTerrainVerts( side, surf, maxlayer, alpha, qfalse );
+ }
+ }
+ }
+
+ // create the final surfaces
+ for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
+ if ( surf->numVerts ) {
+ CreateTerrainSurface( surf, surf->shader );
+ }
+ }
+
+ //
+ // clean up any allocated memory
+ //
+ for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
+ if ( surf->verts ) {
+ free( surf->verts );
+ free( surf->indexes );
+ }
+ }
+ free( alphamap );
+ free( surfaces );
+
+ surfaces = NULL;
+ lastSurface = NULL;
+ numsurfaces = 0;
+ maxsurfaces = 0;
+}
+
+/*****************************************************************************
+
+ New terrain code
+
+******************************************************************************/
+
+typedef struct terrainFace_s {
+ shaderInfo_t *shaderInfo;
+ //texdef_t texdef;
+
+ float vecs[ 2 ][ 4 ]; // texture coordinate mapping
+} terrainFace_t;
+
+typedef struct terrainVert_s {
+ vec3_t xyz;
+ terrainFace_t tri;
+} terrainVert_t;
+
+typedef struct terrainMesh_s {
+ float scale_x;
+ float scale_y;
+ vec3_t origin;
+
+ int width, height;
+ terrainVert_t *map;
+} terrainMesh_t;
+
+terrainVert_t *Terrain_GetVert( terrainMesh_t *pm, int x, int y ) {
+ return &pm->map[ x + y * pm->width ];
+}
+
+void Terrain_GetTriangles( terrainMesh_t *pm, int x, int y, terrainVert_t **verts ) {
+ if ( ( x + y ) & 1 ) {
+ // first tri
+ verts[ 0 ] = Terrain_GetVert( pm, x, y );
+ verts[ 1 ] = Terrain_GetVert( pm, x, y + 1 );
+ verts[ 2 ] = Terrain_GetVert( pm, x + 1, y + 1 );
+
+ // second tri
+ verts[ 3 ] = verts[ 2 ];
+ verts[ 4 ] = Terrain_GetVert( pm, x + 1, y );
+ verts[ 5 ] = verts[ 0 ];
+ } else {
+ // first tri
+ verts[ 0 ] = Terrain_GetVert( pm, x, y );
+ verts[ 1 ] = Terrain_GetVert( pm, x, y + 1 );
+ verts[ 2 ] = Terrain_GetVert( pm, x + 1, y );
+
+ // second tri
+ verts[ 3 ] = verts[ 2 ];
+ verts[ 4 ] = verts[ 1 ];
+ verts[ 5 ] = Terrain_GetVert( pm, x + 1, y + 1 );
+ }
+}
+
+/*
+================
+EmitTerrainVerts2
+================
+*/
+void EmitTerrainVerts2( terrainSurf_t *surf, terrainVert_t **verts, int alpha[ 3 ] ) {
+ int i;
+ int j;
+ drawVert_t *vert;
+ int *indices;
+ int numindices;
+ int maxindices;
+ int xyplane;
+ vec3_t xynorm = { 0, 0, 1 };
+ vec_t shift[ 2 ] = { 0, 0 };
+ vec_t scale[ 2 ] = { 0.5, 0.5 };
+ float vecs[ 2 ][ 4 ];
+ vec4_t plane;
+
+ if ( !surf->verts ) {
+ surf->numVerts = 0;
+ surf->maxVerts = GROW_VERTS;
+ surf->verts = malloc( surf->maxVerts * sizeof( *surf->verts ) );
+
+ surf->numIndexes = 0;
+ surf->maxIndexes = GROW_INDICES;
+ surf->indexes = malloc( surf->maxIndexes * sizeof( *surf->indexes ) );
+ }
+
+ // calculate the texture coordinate vectors
+ xyplane = FindFloatPlane( xynorm, 0 );
+ QuakeTextureVecs( &mapplanes[ xyplane ], shift, 0, scale, vecs );
+
+ // emit the vertexes
+ numindices = 0;
+ maxindices = surf->maxIndexes;
+ assert( maxindices >= 0 );
+ indices = malloc ( maxindices * sizeof( *indices ) );
+
+ PlaneFromPoints( plane, verts[ 0 ]->xyz, verts[ 1 ]->xyz, verts[ 2 ]->xyz );
+
+ for ( i = 0; i < 3; i++ ) {
+ vert = &surf->verts[ surf->numVerts ];
+
+ if ( alpha[ i ] ) {
+ vert->color[3] = 255;
+ } else {
+ vert->color[3] = 0;
+ }
+
+ vert->xyz[ 0 ] = floor( verts[ i ]->xyz[ 0 ] + 0.1f );
+ vert->xyz[ 1 ] = floor( verts[ i ]->xyz[ 1 ] + 0.1f );
+ vert->xyz[ 2 ] = floor( verts[ i ]->xyz[ 2 ] + 0.1f );
+
+ // set the texture coordinates
+ vert->st[0] = ( vecs[0][3] + DotProduct( vecs[ 0 ], vert->xyz ) ) / surf->shader->width;
+ vert->st[1] = ( vecs[1][3] + DotProduct( vecs[ 1 ], vert->xyz ) ) / surf->shader->height;
+
+ VectorCopy( plane, vert->normal );
+
+ for( j = 0; j < surf->numVerts; j++ ) {
+ if ( CompareVert( vert, &surf->verts[ j ], qtrue ) ) {
+ break;
+ }
+ }
+
+ if ( numindices >= maxindices ) {
+ maxindices += GROW_INDICES;
+ indices = realloc( indices, maxindices * sizeof( *indices ) );
+ }
+
+ if ( j != surf->numVerts ) {
+ indices[ numindices++ ] = j;
+ } else {
+ indices[ numindices++ ] = surf->numVerts;
+ surf->numVerts++;
+ if ( surf->numVerts >= surf->maxVerts ) {
+ surf->maxVerts += GROW_VERTS;
+ surf->verts = realloc( surf->verts, surf->maxVerts * sizeof( *surf->verts ) );
+ }
+ }
+ }
+
+ SideAsTristrip( surf, indices, numindices );
+
+ free( indices );
+}
+
+int MapPlaneFromPoints( vec3_t p0, vec3_t p1, vec3_t p2 );
+void QuakeTextureVecs( plane_t *plane, vec_t shift[2], vec_t rotate, vec_t scale[2], vec_t mappingVecs[2][4] );
+qboolean RemoveDuplicateBrushPlanes( bspbrush_t *b );
+void SetBrushContents( bspbrush_t *b );
+
+void AddBrushSide( vec3_t v1, vec3_t v2, vec3_t v3, shaderInfo_t *terrainShader ) {
+ side_t *side;
+ int planenum;
+
+ side = &buildBrush->sides[ buildBrush->numsides ];
+ memset( side, 0, sizeof( *side ) );
+ buildBrush->numsides++;
+
+ side->shaderInfo = terrainShader;
+
+ // find the plane number
+ planenum = MapPlaneFromPoints( v1, v2, v3 );
+ side->planenum = planenum;
+}
+
+void MakeBrushFromTriangle( vec3_t v1, vec3_t v2, vec3_t v3, shaderInfo_t *terrainShader ) {
+ bspbrush_t *b;
+ vec3_t d1;
+ vec3_t d2;
+ vec3_t d3;
+
+ VectorSet( d1, v1[ 0 ], v1[ 1 ], MIN_WORLD_COORD + 10 ); //FIXME
+ VectorSet( d2, v2[ 0 ], v2[ 1 ], MIN_WORLD_COORD + 10 );
+ VectorSet( d3, v3[ 0 ], v3[ 1 ], MIN_WORLD_COORD + 10 );
+
+ buildBrush->numsides = 0;
+ buildBrush->detail = qfalse;
+
+ AddBrushSide( v1, v2, v3, terrainShader );
+ AddBrushSide( v1, d1, v2, terrainShader );
+ AddBrushSide( v2, d2, v3, terrainShader );
+ AddBrushSide( v3, d3, v1, terrainShader );
+ AddBrushSide( d3, d2, d1, terrainShader );
+
+ buildBrush->portalareas[0] = -1;
+ buildBrush->portalareas[1] = -1;
+ buildBrush->entitynum = num_entities-1;
+ buildBrush->brushnum = entitySourceBrushes;
+
+ // if there are mirrored planes, the entire brush is invalid
+ if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
+ return;
+ }
+
+ // get the content for the entire brush
+ SetBrushContents( buildBrush );
+ buildBrush->contents |= CONTENTS_DETAIL;
+
+ b = FinishBrush();
+ if ( !b ) {
+ return;
+ }
+}
+
+void MakeTerrainIntoBrushes( terrainMesh_t *tm ) {
+ int index[ 6 ];
+ int y;
+ int x;
+ terrainVert_t *verts;
+ shaderInfo_t *terrainShader;
+
+ terrainShader = ShaderInfoForShader( "textures/common/terrain" );
+
+ verts = tm->map;
+ for( y = 0; y < tm->height - 1; y++ ) {
+ for( x = 0; x < tm->width - 1; x++ ) {
+ if ( ( x + y ) & 1 ) {
+ // first tri
+ index[ 0 ] = x + y * tm->width;
+ index[ 1 ] = x + ( y + 1 ) * tm->width;
+ index[ 2 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
+ index[ 3 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
+ index[ 4 ] = ( x + 1 ) + y * tm->width;
+ index[ 5 ] = x + y * tm->width;
+ } else {
+ // first tri
+ index[ 0 ] = x + y * tm->width;
+ index[ 1 ] = x + ( y + 1 ) * tm->width;
+ index[ 2 ] = ( x + 1 ) + y * tm->width;
+ index[ 3 ] = ( x + 1 ) + y * tm->width;
+ index[ 4 ] = x + ( y + 1 ) * tm->width;
+ index[ 5 ] = ( x + 1 ) + ( y + 1 ) * tm->width;
+ }
+
+ MakeBrushFromTriangle( verts[ index[ 0 ] ].xyz, verts[ index[ 1 ] ].xyz, verts[ index[ 2 ] ].xyz, terrainShader );
+ MakeBrushFromTriangle( verts[ index[ 3 ] ].xyz, verts[ index[ 4 ] ].xyz, verts[ index[ 5 ] ].xyz, terrainShader );
+ }
+ }
+}
+
+void Terrain_ParseFace( terrainFace_t *face ) {
+ shaderInfo_t *si;
+ vec_t shift[ 2 ];
+ vec_t rotate;
+ vec_t scale[ 2 ];
+ char name[ MAX_QPATH ];
+ char shader[ MAX_QPATH ];
+ plane_t p;
+
+ // read the texturedef
+ GetToken( qfalse );
+ strcpy( name, token );
+
+ GetToken( qfalse );
+ shift[ 0 ] = atof(token);
+ GetToken( qfalse );
+ shift[ 1 ] = atof( token );
+ GetToken( qfalse );
+ rotate = atof( token );
+ GetToken( qfalse );
+ scale[ 0 ] = atof( token );
+ GetToken( qfalse );
+ scale[ 1 ] = atof( token );
+
+ // find default flags and values
+ sprintf( shader, "textures/%s", name );
+ si = ShaderInfoForShader( shader );
+ face->shaderInfo = si;
+ //face->texdef = si->texdef;
+
+ // skip over old contents
+ GetToken( qfalse );
+
+ // skip over old flags
+ GetToken( qfalse );
+
+ // skip over old value
+ GetToken( qfalse );
+
+ //Surface_Parse( &face->texdef );
+ //Surface_BuildTexdef( &face->texdef );
+
+ // make a fake horizontal plane
+ VectorSet( p.normal, 0, 0, 1 );
+ p.dist = 0;
+ p.type = PlaneTypeForNormal( p.normal );
+
+ QuakeTextureVecs( &p, shift, rotate, scale, face->vecs );
+}
+
+#define MAX_TERRAIN_TEXTURES 128
+static int numtextures = 0;;
+static shaderInfo_t *textures[ MAX_TERRAIN_TEXTURES ];
+
+void Terrain_AddTexture( shaderInfo_t *texture ) {
+ int i;
+
+ if ( !texture ) {
+ return;
+ }
+
+ for( i = 0; i < numtextures; i++ ) {
+ if ( textures[ i ] == texture ) {
+ return;
+ }
+ }
+
+ if ( numtextures >= MAX_TERRAIN_TEXTURES ) {
+ Error( "Too many textures on terrain" );
+ return;
+ }
+
+ textures[ numtextures++ ] = texture;
+}
+
+int LayerForShader( shaderInfo_t *shader ) {
+ int i;
+ int l;
+
+ l = strlen( shader->shader );
+ for( i = l - 1; i >= 0; i-- ) {
+ if ( shader->shader[ i ] == '_' ) {
+ return atoi( &shader->shader[ i + 1 ] );
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+=================
+ParseTerrain
+
+Creates a mapDrawSurface_t from the terrain text
+=================
+*/
+
+void ParseTerrain( void ) {
+ int i, j;
+ int x, y;
+ int x1, y1;
+ terrainMesh_t t;
+ int index;
+ terrainVert_t *verts[ 6 ];
+ int num_layers;
+ int layer, minlayer, maxlayer;
+ int alpha[ 6 ];
+ shaderInfo_t *si, *terrainShader;
+ int surfwidth, surfheight, surfsize;
+ terrainSurf_t *surf;
+ char shadername[ MAX_QPATH ];
+
+ mapent->firstDrawSurf = numMapDrawSurfs;
+
+ memset( &t, 0, sizeof( t ) );
+
+ MatchToken( "{" );
+
+ // get width
+ GetToken( qtrue );
+ t.width = atoi( token );
+
+ // get height
+ GetToken( qfalse );
+ t.height = atoi( token );
+
+ // get scale_x
+ GetToken( qfalse );
+ t.scale_x = atof( token );
+
+ // get scale_y
+ GetToken( qfalse );
+ t.scale_y = atof( token );
+
+ // get origin
+ GetToken( qtrue );
+ t.origin[ 0 ] = atof( token );
+ GetToken( qfalse );
+ t.origin[ 1 ] = atof( token );
+ GetToken( qfalse );
+ t.origin[ 2 ] = atof( token );
+
+ t.map = malloc( t.width * t.height * sizeof( t.map[ 0 ] ) );
+
+ if ( t.width <= 0 || t.height <= 0 ) {
+ Error( "ParseTerrain: bad size" );
+ }
+
+ numtextures = 0;
+ index = 0;
+ for ( i = 0; i < t.height; i++ ) {
+ for( j = 0; j < t.width; j++, index++ ) {
+ // get height
+ GetToken( qtrue );
+ t.map[ index ].xyz[ 0 ] = t.origin[ 0 ] + t.scale_x * ( float )j;
+ t.map[ index ].xyz[ 1 ] = t.origin[ 1 ] + t.scale_y * ( float )i;
+ t.map[ index ].xyz[ 2 ] = t.origin[ 2 ] + atof( token );
+
+ Terrain_ParseFace( &t.map[ index ].tri );
+ Terrain_AddTexture( t.map[ index ].tri.shaderInfo );
+ }
+ }
+
+ MatchToken( "}" );
+ MatchToken( "}" );
+
+ MakeTerrainIntoBrushes( &t );
+
+ surfwidth = ( ( t.scale_x * t.width ) + SURF_WIDTH - 1 ) / SURF_WIDTH;
+ surfheight = ( ( t.scale_y * t.height ) + SURF_HEIGHT - 1 ) / SURF_HEIGHT;
+ surfsize = surfwidth * surfheight;
+
+ //FIXME
+ num_layers = 0;
+ for( i = 0; i < numtextures; i++ ) {
+ layer = LayerForShader( textures[ i ] ) + 1;
+ if ( layer > num_layers ) {
+ num_layers = layer;
+ }
+ }
+ num_layers = 4;
+
+ memset( alpha, 0, sizeof( alpha ) );
+
+ lastSurface = NULL;
+ numsurfaces = 0;
+ maxsurfaces = 0;
+ for( i = num_layers; i > 0; i-- ) {
+ maxsurfaces += i * surfsize;
+ }
+
+ surfaces = malloc( maxsurfaces * sizeof( *surfaces ) );
+ memset( surfaces, 0, maxsurfaces * sizeof( *surfaces ) );
+
+ terrainShader = ShaderInfoForShader( "textures/common/terrain" );
+
+ // get the shadername
+ if ( Q_strncasecmp( textures[ 0 ]->shader, "textures/", 9 ) == 0 ) {
+ strcpy( shadername, &textures[ 0 ]->shader[ 9 ] );
+ } else {
+ strcpy( shadername, textures[ 0 ]->shader );
+ }
+ j = strlen( shadername );
+ for( i = j - 1; i >= 0; i-- ) {
+ if ( shadername[ i ] == '_' ) {
+ shadername[ i ] = 0;
+ break;
+ }
+ }
+
+ for( y = 0; y < t.height - 1; y++ ) {
+ for( x = 0; x < t.width - 1; x++ ) {
+ Terrain_GetTriangles( &t, x, y, verts );
+
+ x1 = ( ( float )x / ( float )( t.width - 1 ) ) * surfwidth;
+ if ( x1 >= surfwidth ) {
+ x1 = surfwidth - 1;
+ }
+
+ y1 = ( ( float )y / ( float )( t.height - 1 ) ) * surfheight;
+ if ( y1 >= surfheight ) {
+ y1 = surfheight - 1;
+ }
+
+ maxlayer = minlayer = LayerForShader( verts[ 0 ]->tri.shaderInfo );
+ for( i = 0; i < 3; i++ ) {
+ layer = LayerForShader( verts[ i ]->tri.shaderInfo );
+ if ( layer < minlayer ) {
+ minlayer = layer;
+ }
+ if ( layer > maxlayer ) {
+ maxlayer = layer;
+ }
+ }
+
+ for( i = 0; i < 3; i++ ) {
+ layer = LayerForShader( verts[ i ]->tri.shaderInfo );
+ if ( layer > minlayer ) {
+ alpha[ i ] = 1.0f;
+ } else {
+ alpha[ i ] = 0.0f;
+ }
+ }
+
+ si = ShaderForLayer( minlayer, maxlayer, shadername );
+ surf = SurfaceForShader( si, x1, y1 );
+ EmitTerrainVerts2( surf, &verts[ 0 ], &alpha[ 0 ] );
+
+ // second triangle
+ maxlayer = minlayer = LayerForShader( verts[ 3 ]->tri.shaderInfo );
+ for( i = 3; i < 6; i++ ) {
+ layer = LayerForShader( verts[ i ]->tri.shaderInfo );
+ if ( layer < minlayer ) {
+ minlayer = layer;
+ }
+ if ( layer > maxlayer ) {
+ maxlayer = layer;
+ }
+ }
+
+ for( i = 3; i < 6; i++ ) {
+ layer = LayerForShader( verts[ i ]->tri.shaderInfo );
+ if ( layer > minlayer ) {
+ alpha[ i ] = 1.0f;
+ } else {
+ alpha[ i ] = 0.0f;
+ }
+ }
+
+ si = ShaderForLayer( minlayer, maxlayer, shadername );
+ surf = SurfaceForShader( si, x1, y1 );
+ EmitTerrainVerts2( surf, &verts[ 3 ], &alpha[ 3 ] );
+ }
+ }
+
+ // create the final surfaces
+ for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
+ if ( surf->numVerts ) {
+ CreateTerrainSurface( surf, surf->shader );
+ }
+ }
+
+ //
+ // clean up any allocated memory
+ //
+ for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) {
+ if ( surf->verts ) {
+ free( surf->verts );
+ free( surf->indexes );
+ }
+ }
+ free( surfaces );
+
+ surfaces = NULL;
+ lastSurface = NULL;
+ numsurfaces = 0;
+ maxsurfaces = 0;
+
+ free( t.map );
+}
+
diff --git a/q3map/tjunction.c b/q3map/tjunction.c
new file mode 100755
index 0000000..9d41dc2
--- /dev/null
+++ b/q3map/tjunction.c
@@ -0,0 +1,551 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+typedef struct edgePoint_s {
+ float intercept;
+ vec3_t xyz;
+ struct edgePoint_s *prev, *next;
+} edgePoint_t;
+
+typedef struct edgeLine_s {
+ vec3_t normal1;
+ float dist1;
+
+ vec3_t normal2;
+ float dist2;
+
+ vec3_t origin;
+ vec3_t dir;
+
+ edgePoint_t chain; // unused element of doubly linked list
+} edgeLine_t;
+
+typedef struct {
+ float length;
+ drawVert_t *dv[2];
+} originalEdge_t;
+
+#define MAX_ORIGINAL_EDGES 0x10000
+originalEdge_t originalEdges[MAX_ORIGINAL_EDGES];
+int numOriginalEdges;
+
+
+#define MAX_EDGE_LINES 0x10000
+edgeLine_t edgeLines[MAX_EDGE_LINES];
+int numEdgeLines;
+
+int c_degenerateEdges;
+int c_addedVerts;
+int c_totalVerts;
+
+int c_natural, c_rotate, c_cant;
+
+// these should be whatever epsilon we actually expect,
+// plus SNAP_INT_TO_FLOAT
+#define LINE_POSITION_EPSILON 0.25
+#define POINT_ON_LINE_EPSILON 0.25
+
+/*
+====================
+InsertPointOnEdge
+====================
+*/
+void InsertPointOnEdge( vec3_t v, edgeLine_t *e ) {
+ vec3_t delta;
+ float d;
+ edgePoint_t *p, *scan;
+
+ VectorSubtract( v, e->origin, delta );
+ d = DotProduct( delta, e->dir );
+
+ p = malloc( sizeof(edgePoint_t) );
+ p->intercept = d;
+ VectorCopy( v, p->xyz );
+
+ if ( e->chain.next == &e->chain ) {
+ e->chain.next = e->chain.prev = p;
+ p->next = p->prev = &e->chain;
+ return;
+ }
+
+ scan = e->chain.next;
+ for ( ; scan != &e->chain ; scan = scan->next ) {
+ d = p->intercept - scan->intercept;
+ if ( d > -LINE_POSITION_EPSILON && d < LINE_POSITION_EPSILON ) {
+ free( p );
+ return; // the point is already set
+ }
+
+ if ( p->intercept < scan->intercept ) {
+ // insert here
+ p->prev = scan->prev;
+ p->next = scan;
+ scan->prev->next = p;
+ scan->prev = p;
+ return;
+ }
+ }
+
+ // add at the end
+ p->prev = scan->prev;
+ p->next = scan;
+ scan->prev->next = p;
+ scan->prev = p;
+}
+
+
+/*
+====================
+AddEdge
+====================
+*/
+int AddEdge( vec3_t v1, vec3_t v2, qboolean createNonAxial ) {
+ int i;
+ edgeLine_t *e;
+ float d;
+ vec3_t dir;
+
+ VectorSubtract( v2, v1, dir );
+ d = VectorNormalize( dir, dir );
+ if ( d < 0.1 ) {
+ // if we added a 0 length vector, it would make degenerate planes
+ c_degenerateEdges++;
+ return -1;
+ }
+
+ if ( !createNonAxial ) {
+ if ( fabs( dir[0] + dir[1] + dir[2] ) != 1.0 ) {
+ if ( numOriginalEdges == MAX_ORIGINAL_EDGES ) {
+ Error( "MAX_ORIGINAL_EDGES" );
+ }
+ originalEdges[ numOriginalEdges ].dv[0] = (drawVert_t *)v1;
+ originalEdges[ numOriginalEdges ].dv[1] = (drawVert_t *)v2;
+ originalEdges[ numOriginalEdges ].length = d;
+ numOriginalEdges++;
+ return -1;
+ }
+ }
+
+ for ( i = 0 ; i < numEdgeLines ; i++ ) {
+ e = &edgeLines[i];
+
+ d = DotProduct( v1, e->normal1 ) - e->dist1;
+ if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
+ continue;
+ }
+ d = DotProduct( v1, e->normal2 ) - e->dist2;
+ if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
+ continue;
+ }
+
+ d = DotProduct( v2, e->normal1 ) - e->dist1;
+ if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
+ continue;
+ }
+ d = DotProduct( v2, e->normal2 ) - e->dist2;
+ if ( d < -POINT_ON_LINE_EPSILON || d > POINT_ON_LINE_EPSILON ) {
+ continue;
+ }
+
+ // this is the edge
+ InsertPointOnEdge( v1, e );
+ InsertPointOnEdge( v2, e );
+ return i;
+ }
+
+ // create a new edge
+ if ( numEdgeLines >= MAX_EDGE_LINES ) {
+ Error( "MAX_EDGE_LINES" );
+ }
+
+ e = &edgeLines[ numEdgeLines ];
+ numEdgeLines++;
+
+ e->chain.next = e->chain.prev = &e->chain;
+
+ VectorCopy( v1, e->origin );
+ VectorCopy( dir, e->dir );
+
+ MakeNormalVectors( e->dir, e->normal1, e->normal2 );
+ e->dist1 = DotProduct( e->origin, e->normal1 );
+ e->dist2 = DotProduct( e->origin, e->normal2 );
+
+ InsertPointOnEdge( v1, e );
+ InsertPointOnEdge( v2, e );
+
+ return numEdgeLines - 1;
+}
+
+/*
+====================
+AddSurfaceEdges
+====================
+*/
+void AddSurfaceEdges( mapDrawSurface_t *ds ) {
+ int i;
+
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ // save the edge number in the lightmap field
+ // so we don't need to look it up again
+ ds->verts[i].lightmap[0] =
+ AddEdge( ds->verts[i].xyz, ds->verts[(i+1) % ds->numVerts].xyz, qfalse );
+ }
+}
+
+/*
+================
+ColinearEdge
+================
+*/
+qboolean ColinearEdge( vec3_t v1, vec3_t v2, vec3_t v3 ) {
+ vec3_t midpoint, dir, offset, on;
+ float d;
+
+ VectorSubtract( v2, v1, midpoint );
+ VectorSubtract( v3, v1, dir );
+ d = VectorNormalize( dir, dir );
+ if ( d == 0 ) {
+ return qfalse; // degenerate
+ }
+
+ d = DotProduct( midpoint, dir );
+ VectorScale( dir, d, on );
+ VectorSubtract( midpoint, on, offset );
+ d = VectorLength ( offset );
+
+ if ( d < 0.1 ) {
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+====================
+AddPatchEdges
+
+Add colinear border edges, which will fix some classes of patch to
+brush tjunctions
+====================
+*/
+void AddPatchEdges( mapDrawSurface_t *ds ) {
+ int i;
+ float *v1, *v2, *v3;
+
+ for ( i = 0 ; i < ds->patchWidth - 2; i+=2 ) {
+ v1 = ds->verts[ i ].xyz;
+ v2 = ds->verts[ i + 1 ].xyz;
+ v3 = ds->verts[ i + 2 ].xyz;
+
+ // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
+ if ( ColinearEdge( v1, v2, v3 ) ) {
+ AddEdge( v1, v3, qfalse );
+ }
+
+ v1 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i ].xyz;
+ v2 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 1 ].xyz;
+ v3 = ds->verts[ ( ds->patchHeight - 1 ) * ds->patchWidth + i + 2 ].xyz;
+
+ // if v2 is on the v1 to v3 line, add an edge from v1 to v3
+ if ( ColinearEdge( v1, v2, v3 ) ) {
+ AddEdge( v1, v3, qfalse );
+ }
+ }
+
+ for ( i = 0 ; i < ds->patchHeight - 2 ; i+=2 ) {
+ v1 = ds->verts[ i * ds->patchWidth ].xyz;
+ v2 = ds->verts[ ( i + 1 ) * ds->patchWidth ].xyz;
+ v3 = ds->verts[ ( i + 2 ) * ds->patchWidth ].xyz;
+
+ // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
+ if ( ColinearEdge( v1, v2, v3 ) ) {
+ AddEdge( v1, v3, qfalse );
+ }
+
+ v1 = ds->verts[ ( ds->patchWidth - 1 ) + i * ds->patchWidth ].xyz;
+ v2 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 1 ) * ds->patchWidth ].xyz;
+ v3 = ds->verts[ ( ds->patchWidth - 1 ) + ( i + 2 ) * ds->patchWidth ].xyz;
+
+ // if v2 is the midpoint of v1 to v3, add an edge from v1 to v3
+ if ( ColinearEdge( v1, v2, v3 ) ) {
+ AddEdge( v1, v3, qfalse );
+ }
+ }
+
+
+}
+
+
+/*
+====================
+FixSurfaceJunctions
+====================
+*/
+#define MAX_SURFACE_VERTS 256
+void FixSurfaceJunctions( mapDrawSurface_t *ds ) {
+ int i, j, k;
+ edgeLine_t *e;
+ edgePoint_t *p;
+ int originalVerts;
+ int counts[MAX_SURFACE_VERTS];
+ int originals[MAX_SURFACE_VERTS];
+ int firstVert[MAX_SURFACE_VERTS];
+ drawVert_t verts[MAX_SURFACE_VERTS], *v1, *v2;
+ int numVerts;
+ float start, end, frac;
+ vec3_t delta;
+
+ originalVerts = ds->numVerts;
+
+ numVerts = 0;
+ for ( i = 0 ; i < ds->numVerts ; i++ ) {
+ counts[i] = 0;
+ firstVert[i] = numVerts;
+
+ // copy first vert
+ if ( numVerts == MAX_SURFACE_VERTS ) {
+ Error( "MAX_SURFACE_VERTS" );
+ }
+ verts[numVerts] = ds->verts[i];
+ originals[numVerts] = i;
+ numVerts++;
+
+ // check to see if there are any t junctions before the next vert
+ v1 = &ds->verts[i];
+ v2 = &ds->verts[ (i+1) % ds->numVerts ];
+
+ j = (int)ds->verts[i].lightmap[0];
+ if ( j == -1 ) {
+ continue; // degenerate edge
+ }
+ e = &edgeLines[ j ];
+
+ VectorSubtract( v1->xyz, e->origin, delta );
+ start = DotProduct( delta, e->dir );
+
+ VectorSubtract( v2->xyz, e->origin, delta );
+ end = DotProduct( delta, e->dir );
+
+
+ if ( start < end ) {
+ p = e->chain.next;
+ } else {
+ p = e->chain.prev;
+ }
+
+ for ( ; p != &e->chain ; ) {
+ if ( start < end ) {
+ if ( p->intercept > end - ON_EPSILON ) {
+ break;
+ }
+ } else {
+ if ( p->intercept < end + ON_EPSILON ) {
+ break;
+ }
+ }
+
+ if (
+ ( start < end && p->intercept > start + ON_EPSILON ) ||
+ ( start > end && p->intercept < start - ON_EPSILON ) ) {
+ // insert this point
+ if ( numVerts == MAX_SURFACE_VERTS ) {
+ Error( "MAX_SURFACE_VERTS" );
+ }
+
+ // take the exact intercept point
+ VectorCopy( p->xyz, verts[ numVerts ].xyz );
+
+ // copy the normal
+ VectorCopy( v1->normal, verts[ numVerts ].normal );
+
+ // interpolate the texture coordinates
+ frac = ( p->intercept - start ) / ( end - start );
+ for ( j = 0 ; j < 2 ; j++ ) {
+ verts[ numVerts ].st[j] = v1->st[j] +
+ frac * ( v2->st[j] - v1->st[j] );
+ }
+ originals[numVerts] = i;
+ numVerts++;
+ counts[i]++;
+ }
+
+ if ( start < end ) {
+ p = p->next;
+ } else {
+ p = p->prev;
+ }
+ }
+ }
+
+ c_addedVerts += numVerts - ds->numVerts;
+ c_totalVerts += numVerts;
+
+
+ // FIXME: check to see if the entire surface degenerated
+ // after snapping
+
+ // rotate the points so that the initial vertex is between
+ // two non-subdivided edges
+ for ( i = 0 ; i < numVerts ; i++ ) {
+ if ( originals[ (i+1) % numVerts ] == originals[ i ] ) {
+ continue;
+ }
+ j = (i + numVerts - 1 ) % numVerts;
+ k = (i + numVerts - 2 ) % numVerts;
+ if ( originals[ j ] == originals[ k ] ) {
+ continue;
+ }
+ break;
+ }
+
+ if ( i == 0 ) {
+ // fine the way it is
+ c_natural++;
+
+ ds->numVerts = numVerts;
+ ds->verts = malloc( numVerts * sizeof( *ds->verts ) );
+ memcpy( ds->verts, verts, numVerts * sizeof( *ds->verts ) );
+
+ return;
+ }
+ if ( i == numVerts ) {
+ // create a vertex in the middle to start the fan
+ c_cant++;
+
+/*
+ memset ( &verts[numVerts], 0, sizeof( verts[numVerts] ) );
+ for ( i = 0 ; i < numVerts ; i++ ) {
+ for ( j = 0 ; j < 10 ; j++ ) {
+ verts[numVerts].xyz[j] += verts[i].xyz[j];
+ }
+ }
+ for ( j = 0 ; j < 10 ; j++ ) {
+ verts[numVerts].xyz[j] /= numVerts;
+ }
+
+ i = numVerts;
+ numVerts++;
+*/
+ } else {
+ // just rotate the vertexes
+ c_rotate++;
+
+ }
+
+ ds->numVerts = numVerts;
+ ds->verts = malloc( numVerts * sizeof( *ds->verts ) );
+
+ for ( j = 0 ; j < ds->numVerts ; j++ ) {
+ ds->verts[j] = verts[ ( j + i ) % ds->numVerts ];
+ }
+}
+
+/*
+================
+EdgeCompare
+================
+*/
+int EdgeCompare( const void *elem1, const void *elem2 ) {
+ float d1, d2;
+
+ d1 = ((originalEdge_t *)elem1)->length;
+ d2 = ((originalEdge_t *)elem2)->length;
+
+ if ( d1 < d2 ) {
+ return -1;
+ }
+ if ( d2 > d1 ) {
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+================
+FixTJunctions
+
+Call after the surface list has been pruned, but before lightmap allocation
+================
+*/
+void FixTJunctions( entity_t *ent ) {
+ int i;
+ mapDrawSurface_t *ds;
+ int axialEdgeLines;
+ originalEdge_t *e;
+
+ qprintf("----- FixTJunctions -----\n");
+
+ numEdgeLines = 0;
+ numOriginalEdges = 0;
+
+ // add all the edges
+ // this actually creates axial edges, but it
+ // only creates originalEdge_t structures
+ // for non-axial edges
+ for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
+ ds = &mapDrawSurfs[i];
+ if ( ds->patch ) {
+ AddPatchEdges( ds );
+ } else if ( ds->shaderInfo->autosprite || ds->shaderInfo->notjunc || ds->miscModel ) {
+ // miscModels don't add tjunctions
+ } else {
+ AddSurfaceEdges( ds );
+ }
+ }
+
+ axialEdgeLines = numEdgeLines;
+
+ // sort the non-axial edges by length
+ qsort( originalEdges, numOriginalEdges, sizeof(originalEdges[0]), EdgeCompare );
+
+ // add the non-axial edges, longest first
+ // this gives the most accurate edge description
+ for ( i = 0 ; i < numOriginalEdges ; i++ ) {
+ e = &originalEdges[i];
+ e->dv[0]->lightmap[0] = AddEdge( e->dv[0]->xyz, e->dv[1]->xyz, qtrue );
+ }
+
+ qprintf( "%6i axial edge lines\n", axialEdgeLines );
+ qprintf( "%6i non-axial edge lines\n", numEdgeLines - axialEdgeLines );
+ qprintf( "%6i degenerate edges\n", c_degenerateEdges );
+
+ // insert any needed vertexes
+ for ( i = ent->firstDrawSurf ; i < numMapDrawSurfs ; i++ ) {
+ ds = &mapDrawSurfs[i];
+ if ( ds->patch ) {
+ continue;
+ }
+ if ( ds->shaderInfo->autosprite || ds->shaderInfo->notjunc || ds->miscModel ) {
+ continue;
+ }
+
+ FixSurfaceJunctions( ds );
+ }
+
+ qprintf( "%6i verts added for tjunctions\n", c_addedVerts );
+ qprintf( "%6i total verts\n", c_totalVerts );
+ qprintf( "%6i naturally ordered\n", c_natural );
+ qprintf( "%6i rotated orders\n", c_rotate );
+ qprintf( "%6i can't order\n", c_cant );
+}
diff --git a/q3map/tree.c b/q3map/tree.c
new file mode 100755
index 0000000..4addea6
--- /dev/null
+++ b/q3map/tree.c
@@ -0,0 +1,146 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+
+extern int c_nodes;
+
+void RemovePortalFromNode (portal_t *portal, node_t *l);
+
+node_t *NodeForPoint (node_t *node, vec3_t origin)
+{
+ plane_t *plane;
+ vec_t d;
+
+ while (node->planenum != PLANENUM_LEAF)
+ {
+ plane = &mapplanes[node->planenum];
+ d = DotProduct (origin, plane->normal) - plane->dist;
+ if (d >= 0)
+ node = node->children[0];
+ else
+ node = node->children[1];
+ }
+
+ return node;
+}
+
+
+
+/*
+=============
+FreeTreePortals_r
+=============
+*/
+void FreeTreePortals_r (node_t *node)
+{
+ portal_t *p, *nextp;
+ int s;
+
+ // free children
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FreeTreePortals_r (node->children[0]);
+ FreeTreePortals_r (node->children[1]);
+ }
+
+ // free portals
+ for (p=node->portals ; p ; p=nextp)
+ {
+ s = (p->nodes[1] == node);
+ nextp = p->next[s];
+
+ RemovePortalFromNode (p, p->nodes[!s]);
+ FreePortal (p);
+ }
+ node->portals = NULL;
+}
+
+/*
+=============
+FreeTree_r
+=============
+*/
+void FreeTree_r (node_t *node)
+{
+ // free children
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ FreeTree_r (node->children[0]);
+ FreeTree_r (node->children[1]);
+ }
+
+ // free bspbrushes
+ FreeBrushList (node->brushlist);
+
+ // free the node
+ if (node->volume)
+ FreeBrush (node->volume);
+
+ if (numthreads == 1)
+ c_nodes--;
+ free (node);
+}
+
+
+/*
+=============
+FreeTree
+=============
+*/
+void FreeTree (tree_t *tree)
+{
+ FreeTreePortals_r (tree->headnode);
+ FreeTree_r (tree->headnode);
+ free (tree);
+}
+
+//===============================================================
+
+void PrintTree_r (node_t *node, int depth)
+{
+ int i;
+ plane_t *plane;
+ bspbrush_t *bb;
+
+ for (i=0 ; i<depth ; i++)
+ _printf (" ");
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ if (!node->brushlist)
+ _printf ("NULL\n");
+ else
+ {
+ for (bb=node->brushlist ; bb ; bb=bb->next)
+ _printf ("%i ", bb->original->brushnum);
+ _printf ("\n");
+ }
+ return;
+ }
+
+ plane = &mapplanes[node->planenum];
+ _printf ("#%i (%5.2f %5.2f %5.2f):%5.2f\n", node->planenum,
+ plane->normal[0], plane->normal[1], plane->normal[2],
+ plane->dist);
+ PrintTree_r (node->children[0], depth+1);
+ PrintTree_r (node->children[1], depth+1);
+}
diff --git a/q3map/vis.c b/q3map/vis.c
new file mode 100755
index 0000000..6baab38
--- /dev/null
+++ b/q3map/vis.c
@@ -0,0 +1,1197 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+// vis.c
+
+#include "vis.h"
+#include "threads.h"
+#include "stdlib.h"
+#ifdef _WIN32
+#include "../libs/pakstuff.h"
+#endif
+
+
+#define VIS_HEADER_SIZE 8
+
+extern char outbase[32];
+
+int numportals;
+int portalclusters;
+int numfaces;
+
+char inbase[32];
+
+vportal_t *portals;
+leaf_t *leafs;
+
+vportal_t *faces;
+leaf_t *faceleafs;
+
+int c_portaltest, c_portalpass, c_portalcheck;
+
+int leafbytes; // (portalclusters+63)>>3
+int leaflongs;
+
+int portalbytes, portallongs;
+
+qboolean fastvis;
+qboolean noPassageVis;
+qboolean passageVisOnly;
+qboolean mergevis;
+qboolean nosort;
+qboolean saveprt;
+
+int testlevel = 2;
+
+int totalvis;
+
+vportal_t *sorted_portals[MAX_MAP_PORTALS*2];
+
+void PassageMemory(void);
+
+
+//=============================================================================
+
+void 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);
+}
+
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (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;
+}
+
+
+
+void prl(leaf_t *l)
+{
+ int i;
+ vportal_t *p;
+ plane_t pl;
+
+ for (i=0 ; i<l->numportals ; i++)
+ {
+ p = l->portals[i];
+ pl = p->plane;
+ _printf ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
+ }
+}
+
+
+//=============================================================================
+
+/*
+=============
+SortPortals
+
+Sorts the portals from the least complex, so the later ones can reuse
+the earlier information.
+=============
+*/
+int PComp (const void *a, const void *b)
+{
+ if ( (*(vportal_t **)a)->nummightsee == (*(vportal_t **)b)->nummightsee)
+ return 0;
+ if ( (*(vportal_t **)a)->nummightsee < (*(vportal_t **)b)->nummightsee)
+ return -1;
+ return 1;
+}
+void SortPortals (void)
+{
+ int i;
+
+ for (i=0 ; i<numportals*2 ; i++)
+ sorted_portals[i] = &portals[i];
+
+ if (nosort)
+ return;
+ qsort (sorted_portals, numportals*2, sizeof(sorted_portals[0]), PComp);
+}
+
+
+/*
+==============
+LeafVectorFromPortalVector
+==============
+*/
+int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
+{
+ int i, j, leafnum;
+ vportal_t *p;
+ int c_leafs;
+
+
+ for (i=0 ; i<numportals*2 ; i++)
+ {
+ if (portalbits[i>>3] & (1<<(i&7)) )
+ {
+ p = portals+i;
+ leafbits[p->leaf>>3] |= (1<<(p->leaf&7));
+ }
+ }
+
+ for (j = 0; j < portalclusters; j++)
+ {
+ leafnum = j;
+ while (leafs[leafnum].merged >= 0)
+ leafnum = leafs[leafnum].merged;
+ //if the merged leaf is visible then the original leaf is visible
+ if (leafbits[leafnum>>3] & (1<<(leafnum&7)))
+ {
+ leafbits[j>>3] |= (1<<(j&7));
+ }
+ }
+
+ c_leafs = CountBits (leafbits, portalclusters);
+
+ return c_leafs;
+}
+
+
+/*
+===============
+ClusterMerge
+
+Merges the portal visibility for a leaf
+===============
+*/
+void ClusterMerge (int leafnum)
+{
+ leaf_t *leaf;
+ byte portalvector[MAX_PORTALS/8];
+ byte uncompressed[MAX_MAP_LEAFS/8];
+ int i, j;
+ int numvis, mergedleafnum;
+ vportal_t *p;
+ int pnum;
+
+ // OR together all the portalvis bits
+
+ mergedleafnum = leafnum;
+ while(leafs[mergedleafnum].merged >= 0)
+ mergedleafnum = leafs[mergedleafnum].merged;
+
+ memset (portalvector, 0, portalbytes);
+ leaf = &leafs[mergedleafnum];
+ for (i = 0; i < leaf->numportals; i++)
+ {
+ p = leaf->portals[i];
+ if (p->removed)
+ continue;
+
+ if (p->status != stat_done)
+ Error ("portal not done");
+ for (j=0 ; j<portallongs ; j++)
+ ((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
+ pnum = p - portals;
+ portalvector[pnum>>3] |= 1<<(pnum&7);
+ }
+
+ memset (uncompressed, 0, leafbytes);
+
+ uncompressed[mergedleafnum>>3] |= (1<<(mergedleafnum&7));
+ // convert portal bits to leaf bits
+ numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
+
+// if (uncompressed[leafnum>>3] & (1<<(leafnum&7)))
+// _printf ("WARNING: Leaf portals saw into leaf\n");
+
+// uncompressed[leafnum>>3] |= (1<<(leafnum&7));
+
+ numvis++; // count the leaf itself
+
+ totalvis += numvis;
+
+ qprintf ("cluster %4i : %4i visible\n", leafnum, numvis);
+
+ memcpy (visBytes + VIS_HEADER_SIZE + leafnum*leafbytes, uncompressed, leafbytes);
+}
+
+/*
+==================
+CalcPortalVis
+==================
+*/
+void CalcPortalVis (void)
+{
+#ifdef MREDEBUG
+ _printf("%6d portals out of %d", 0, numportals*2);
+ //get rid of the counter
+ RunThreadsOnIndividual (numportals*2, qfalse, PortalFlow);
+#else
+ RunThreadsOnIndividual (numportals*2, qtrue, PortalFlow);
+#endif
+
+}
+
+/*
+==================
+CalcPassageVis
+==================
+*/
+void CalcPassageVis(void)
+{
+ PassageMemory();
+
+#ifdef MREDEBUG
+ _printf("%6d portals out of %d", 0, numportals*2);
+ RunThreadsOnIndividual (numportals*2, qfalse, CreatePassages);
+ _printf("\n");
+ _printf("%6d portals out of %d", 0, numportals*2);
+ RunThreadsOnIndividual (numportals*2, qfalse, PassageFlow);
+ _printf("\n");
+#else
+ RunThreadsOnIndividual (numportals*2, qtrue, CreatePassages);
+ RunThreadsOnIndividual (numportals*2, qtrue, PassageFlow);
+#endif
+}
+
+/*
+==================
+CalcPassagePortalVis
+==================
+*/
+void CalcPassagePortalVis(void)
+{
+ PassageMemory();
+
+#ifdef MREDEBUG
+ _printf("%6d portals out of %d", 0, numportals*2);
+ RunThreadsOnIndividual (numportals*2, qfalse, CreatePassages);
+ _printf("\n");
+ _printf("%6d portals out of %d", 0, numportals*2);
+ RunThreadsOnIndividual (numportals*2, qfalse, PassagePortalFlow);
+ _printf("\n");
+#else
+ RunThreadsOnIndividual (numportals*2, qtrue, CreatePassages);
+ RunThreadsOnIndividual (numportals*2, qtrue, PassagePortalFlow);
+#endif
+}
+
+/*
+==================
+CalcFastVis
+==================
+*/
+void CalcFastVis(void)
+{
+ int i;
+
+ // fastvis just uses mightsee for a very loose bound
+ for (i=0 ; i<numportals*2 ; i++)
+ {
+ portals[i].portalvis = portals[i].portalflood;
+ portals[i].status = stat_done;
+ }
+}
+
+/*
+==================
+CalcVis
+==================
+*/
+void CalcVis (void)
+{
+ int i;
+
+ RunThreadsOnIndividual (numportals*2, qtrue, BasePortalVis);
+
+// RunThreadsOnIndividual (numportals*2, qtrue, BetterPortalVis);
+
+ SortPortals ();
+
+ if (fastvis) {
+ CalcFastVis();
+ }
+ else if ( noPassageVis ) {
+ CalcPortalVis();
+ }
+ else if ( passageVisOnly ) {
+ CalcPassageVis();
+ }
+ else {
+ CalcPassagePortalVis();
+ }
+ //
+ // assemble the leaf vis lists by oring and compressing the portal lists
+ //
+ _printf("creating leaf vis...\n");
+ for (i=0 ; i<portalclusters ; i++)
+ ClusterMerge (i);
+
+ _printf( "Total visible clusters: %i\n", totalvis );
+ _printf( "Average clusters visible: %i\n", totalvis / portalclusters );
+}
+
+/*
+==================
+SetPortalSphere
+==================
+*/
+void SetPortalSphere (vportal_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;
+}
+
+/*
+=============
+Winding_PlanesConcave
+=============
+*/
+#define WCONVEX_EPSILON 0.2
+
+int Winding_PlanesConcave(winding_t *w1, winding_t *w2,
+ vec3_t normal1, vec3_t normal2,
+ float dist1, float dist2)
+{
+ int i;
+
+ if (!w1 || !w2) return qfalse;
+
+ // check if one of the points of winding 1 is at the front of the plane of winding 2
+ for (i = 0; i < w1->numpoints; i++)
+ {
+ if (DotProduct(normal2, w1->points[i]) - dist2 > WCONVEX_EPSILON) return qtrue;
+ }
+ // check if one of the points of winding 2 is at the front of the plane of winding 1
+ for (i = 0; i < w2->numpoints; i++)
+ {
+ if (DotProduct(normal1, w2->points[i]) - dist1 > WCONVEX_EPSILON) return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+============
+TryMergeLeaves
+============
+*/
+int TryMergeLeaves(int l1num, int l2num)
+{
+ int i, j, k, n, numportals;
+ plane_t plane1, plane2;
+ leaf_t *l1, *l2;
+ vportal_t *p1, *p2;
+ vportal_t *portals[MAX_PORTALS_ON_LEAF];
+
+ for (k = 0; k < 2; k++)
+ {
+ if (k) l1 = &leafs[l1num];
+ else l1 = &faceleafs[l1num];
+ for (i = 0; i < l1->numportals; i++)
+ {
+ p1 = l1->portals[i];
+ if (p1->leaf == l2num) continue;
+ for (n = 0; n < 2; n++)
+ {
+ if (n) l2 = &leafs[l2num];
+ else l2 = &faceleafs[l2num];
+ for (j = 0; j < l2->numportals; j++)
+ {
+ p2 = l2->portals[j];
+ if (p2->leaf == l1num) continue;
+ //
+ plane1 = p1->plane;
+ plane2 = p2->plane;
+ if (Winding_PlanesConcave(p1->winding, p2->winding, plane1.normal, plane2.normal, plane1.dist, plane2.dist))
+ return qfalse;
+ }
+ }
+ }
+ }
+ for (k = 0; k < 2; k++)
+ {
+ if (k)
+ {
+ l1 = &leafs[l1num];
+ l2 = &leafs[l2num];
+ }
+ else
+ {
+ l1 = &faceleafs[l1num];
+ l2 = &faceleafs[l2num];
+ }
+ numportals = 0;
+ //the leaves can be merged now
+ for (i = 0; i < l1->numportals; i++)
+ {
+ p1 = l1->portals[i];
+ if (p1->leaf == l2num)
+ {
+ p1->removed = qtrue;
+ continue;
+ }
+ portals[numportals++] = p1;
+ }
+ for (j = 0; j < l2->numportals; j++)
+ {
+ p2 = l2->portals[j];
+ if (p2->leaf == l1num)
+ {
+ p2->removed = qtrue;
+ continue;
+ }
+ portals[numportals++] = p2;
+ }
+ for (i = 0; i < numportals; i++)
+ {
+ l2->portals[i] = portals[i];
+ }
+ l2->numportals = numportals;
+ l1->merged = l2num;
+ }
+ return qtrue;
+}
+
+/*
+============
+UpdatePortals
+============
+*/
+void UpdatePortals(void)
+{
+ int i;
+ vportal_t *p;
+
+ for (i = 0; i < numportals * 2; i++)
+ {
+ p = &portals[i];
+ if (p->removed)
+ continue;
+ while(leafs[p->leaf].merged >= 0)
+ p->leaf = leafs[p->leaf].merged;
+ }
+}
+
+/*
+============
+MergeLeaves
+
+try to merge leaves but don't merge through hint splitters
+============
+*/
+void MergeLeaves(void)
+{
+ int i, j, nummerges, totalnummerges;
+ leaf_t *leaf;
+ vportal_t *p;
+
+ totalnummerges = 0;
+ do
+ {
+ nummerges = 0;
+ for (i = 0; i < portalclusters; i++)
+ {
+ leaf = &leafs[i];
+ //if this leaf is merged already
+ if (leaf->merged >= 0)
+ continue;
+ //
+ for (j = 0; j < leaf->numportals; j++)
+ {
+ p = leaf->portals[j];
+ //
+ if (p->removed)
+ continue;
+ //never merge through hint portals
+ if (p->hint)
+ continue;
+ if (TryMergeLeaves(i, p->leaf))
+ {
+ UpdatePortals();
+ nummerges++;
+ break;
+ }
+ }
+ }
+ totalnummerges += nummerges;
+ } while (nummerges);
+ _printf("%6d leaves merged\n", totalnummerges);
+}
+
+/*
+============
+TryMergeWinding
+============
+*/
+#define CONTINUOUS_EPSILON 0.005
+
+winding_t *TryMergeWinding (winding_t *f1, winding_t *f2, vec3_t planenormal)
+{
+ vec_t *p1, *p2, *p3, *p4, *back;
+ winding_t *newf;
+ int i, j, k, l;
+ vec3_t normal, delta;
+ vec_t dot;
+ qboolean keep1, keep2;
+
+
+ //
+ // find a common edge
+ //
+ p1 = p2 = NULL; // stop compiler warning
+ j = 0; //
+
+ for (i = 0; i < f1->numpoints; i++)
+ {
+ p1 = f1->points[i];
+ p2 = f1->points[(i+1) % f1->numpoints];
+ for (j = 0; j < f2->numpoints; j++)
+ {
+ p3 = f2->points[j];
+ p4 = f2->points[(j+1) % f2->numpoints];
+ for (k = 0; k < 3; k++)
+ {
+ if (fabs(p1[k] - p4[k]) > 0.1)//EQUAL_EPSILON) //ME
+ break;
+ if (fabs(p2[k] - p3[k]) > 0.1)//EQUAL_EPSILON) //ME
+ break;
+ } //end for
+ if (k==3)
+ break;
+ } //end for
+ if (j < f2->numpoints)
+ break;
+ } //end for
+
+ if (i == f1->numpoints)
+ return NULL; // no matching edges
+
+ //
+ // check slope of connected lines
+ // if the slopes are colinear, the point can be removed
+ //
+ back = f1->points[(i+f1->numpoints-1)%f1->numpoints];
+ VectorSubtract (p1, back, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal, normal);
+
+ back = f2->points[(j+2)%f2->numpoints];
+ VectorSubtract (back, p1, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep1 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+ back = f1->points[(i+2)%f1->numpoints];
+ VectorSubtract (back, p2, delta);
+ CrossProduct (planenormal, delta, normal);
+ VectorNormalize (normal, normal);
+
+ back = f2->points[(j+f2->numpoints-1)%f2->numpoints];
+ VectorSubtract (back, p2, delta);
+ dot = DotProduct (delta, normal);
+ if (dot > CONTINUOUS_EPSILON)
+ return NULL; // not a convex polygon
+ keep2 = (qboolean)(dot < -CONTINUOUS_EPSILON);
+
+ //
+ // build the new polygon
+ //
+ newf = NewWinding (f1->numpoints + f2->numpoints);
+
+ // copy first polygon
+ for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
+ {
+ if (k==(i+1)%f1->numpoints && !keep2)
+ continue;
+
+ VectorCopy (f1->points[k], newf->points[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ // copy second polygon
+ for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
+ {
+ if (l==(j+1)%f2->numpoints && !keep1)
+ continue;
+ VectorCopy (f2->points[l], newf->points[newf->numpoints]);
+ newf->numpoints++;
+ }
+
+ return newf;
+}
+
+/*
+============
+MergeLeafPortals
+============
+*/
+void MergeLeafPortals(void)
+{
+ int i, j, k, nummerges, hintsmerged;
+ leaf_t *leaf;
+ vportal_t *p1, *p2;
+ winding_t *w;
+
+ nummerges = 0;
+ hintsmerged = 0;
+ for (i = 0; i < portalclusters; i++)
+ {
+ leaf = &leafs[i];
+ if (leaf->merged >= 0) continue;
+ for (j = 0; j < leaf->numportals; j++)
+ {
+ p1 = leaf->portals[j];
+ if (p1->removed)
+ continue;
+ for (k = j+1; k < leaf->numportals; k++)
+ {
+ p2 = leaf->portals[k];
+ if (p2->removed)
+ continue;
+ if (p1->leaf == p2->leaf)
+ {
+ w = TryMergeWinding(p1->winding, p2->winding, p1->plane.normal);
+ if (w)
+ {
+ FreeWinding(p1->winding);
+ p1->winding = w;
+ if (p1->hint && p2->hint)
+ hintsmerged++;
+ p1->hint |= p2->hint;
+ SetPortalSphere(p1);
+ p2->removed = qtrue;
+ nummerges++;
+ i--;
+ break;
+ }
+ }
+ }
+ if (k < leaf->numportals)
+ break;
+ }
+ }
+ _printf("%6d portals merged\n", nummerges);
+ _printf("%6d hint portals merged\n", hintsmerged);
+}
+
+
+/*
+============
+WritePortals
+============
+*/
+int CountActivePortals(void)
+{
+ int num, hints, j;
+ vportal_t *p;
+
+ num = 0;
+ hints = 0;
+ for (j = 0; j < numportals * 2; j++)
+ {
+ p = portals + j;
+ if (p->removed)
+ continue;
+ if (p->hint)
+ hints++;
+ num++;
+ }
+ _printf("%6d active portals\n", num);
+ _printf("%6d hint portals\n", hints);
+ return num;
+}
+
+/*
+============
+WritePortals
+============
+*/
+void WriteFloat (FILE *f, vec_t v);
+
+void WritePortals(char *filename)
+{
+ int i, j, num;
+ FILE *pf;
+ vportal_t *p;
+ winding_t *w;
+
+ // write the file
+ pf = fopen (filename, "w");
+ if (!pf)
+ Error ("Error opening %s", filename);
+
+ num = 0;
+ for (j = 0; j < numportals * 2; j++)
+ {
+ p = portals + j;
+ if (p->removed)
+ continue;
+// if (!p->hint)
+// continue;
+ num++;
+ }
+
+ fprintf (pf, "%s\n", PORTALFILE);
+ fprintf (pf, "%i\n", 0);
+ fprintf (pf, "%i\n", num);// + numfaces);
+ fprintf (pf, "%i\n", 0);
+
+ for (j = 0; j < numportals * 2; j++)
+ {
+ p = portals + j;
+ if (p->removed)
+ continue;
+// if (!p->hint)
+// continue;
+ w = p->winding;
+ fprintf (pf,"%i %i %i ",w->numpoints, 0, 0);
+ fprintf (pf, "%d ", p->hint);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pf,"(");
+ WriteFloat (pf, w->points[i][0]);
+ WriteFloat (pf, w->points[i][1]);
+ WriteFloat (pf, w->points[i][2]);
+ fprintf (pf,") ");
+ }
+ fprintf (pf,"\n");
+ }
+
+ /*
+ for (j = 0; j < numfaces; j++)
+ {
+ p = faces + j;
+ w = p->winding;
+ fprintf (pf,"%i %i %i ",w->numpoints, 0, 0);
+ fprintf (pf, "0 ");
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pf,"(");
+ WriteFloat (pf, w->points[i][0]);
+ WriteFloat (pf, w->points[i][1]);
+ WriteFloat (pf, w->points[i][2]);
+ fprintf (pf,") ");
+ }
+ fprintf (pf,"\n");
+ }*/
+
+ fclose (pf);
+}
+
+/*
+============
+LoadPortals
+============
+*/
+void LoadPortals (char *name)
+{
+ int i, j, hint;
+ vportal_t *p;
+ leaf_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);
+
+ // these counts should take advantage of 64 bit systems automatically
+ leafbytes = ((portalclusters+63)&~63)>>3;
+ leaflongs = leafbytes/sizeof(long);
+
+ portalbytes = ((numportals*2+63)&~63)>>3;
+ portallongs = portalbytes/sizeof(long);
+
+ // each file portal is split into two memory portals
+ portals = malloc(2*numportals*sizeof(vportal_t));
+ memset (portals, 0, 2*numportals*sizeof(vportal_t));
+
+ leafs = malloc(portalclusters*sizeof(leaf_t));
+ memset (leafs, 0, portalclusters*sizeof(leaf_t));
+
+ for (i = 0; i < portalclusters; i++)
+ leafs[i].merged = -1;
+
+ numVisBytes = VIS_HEADER_SIZE + portalclusters*leafbytes;
+
+ ((int *)visBytes)[0] = portalclusters;
+ ((int *)visBytes)[1] = leafbytes;
+
+ 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 = NewWinding (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
+ 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->num = i+1;
+ p->hint = hint;
+ p->winding = w;
+ VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
+ p->plane.dist = -plane.dist;
+ p->leaf = leafnums[1];
+ 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->num = i+1;
+ p->hint = hint;
+ p->winding = NewWinding(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];
+ SetPortalSphere (p);
+ p++;
+
+ }
+
+ faces = malloc(2*numfaces*sizeof(vportal_t));
+ memset (faces, 0, 2*numfaces*sizeof(vportal_t));
+
+ faceleafs = malloc(portalclusters*sizeof(leaf_t));
+ memset(faceleafs, 0, portalclusters*sizeof(leaf_t));
+
+ for (i = 0, p = faces; i < numfaces; i++)
+ {
+ if (fscanf (f, "%i %i ", &numpoints, &leafnums[0]) != 2)
+ Error ("LoadPortals: reading portal %i", i);
+
+ w = p->winding = NewWinding (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
+ PlaneFromWinding (w, &plane);
+
+ l = &faceleafs[leafnums[0]];
+ l->merged = -1;
+ if (l->numportals == MAX_PORTALS_ON_LEAF)
+ Error ("Leaf with too many faces");
+ l->portals[l->numportals] = p;
+ l->numportals++;
+
+ p->num = i+1;
+ p->winding = w;
+ // normal pointing out of the leaf
+ VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
+ p->plane.dist = -plane.dist;
+ p->leaf = -1;
+ SetPortalSphere (p);
+ p++;
+ }
+
+ fclose (f);
+}
+
+
+/*
+================
+CalcPHS
+
+Calculate the PHS (Potentially Hearable Set)
+by ORing together all the PVS visible from a leaf
+================
+*/
+void CalcPHS (void)
+{
+ int i, j, k, l, index;
+ int bitbyte;
+ long *dest, *src;
+ byte *scan;
+ int count;
+ byte uncompressed[MAX_MAP_LEAFS/8];
+
+ _printf ("Building PHS...\n");
+
+ count = 0;
+ for (i=0 ; i<portalclusters ; i++)
+ {
+ scan = visBytes + i*leafbytes;
+ memcpy (uncompressed, scan, leafbytes);
+ for (j=0 ; j<leafbytes ; j++)
+ {
+ bitbyte = scan[j];
+ if (!bitbyte)
+ continue;
+ for (k=0 ; k<8 ; k++)
+ {
+ if (! (bitbyte & (1<<k)) )
+ continue;
+ // OR this pvs row into the phs
+ index = ((j<<3)+k);
+ if (index >= portalclusters)
+ Error ("Bad bit in PVS"); // pad bits should be 0
+ src = (long *)(visBytes + index*leafbytes);
+ dest = (long *)uncompressed;
+ for (l=0 ; l<leaflongs ; l++)
+ ((long *)uncompressed)[l] |= src[l];
+ }
+ }
+ for (j=0 ; j<portalclusters ; j++)
+ if (uncompressed[j>>3] & (1<<(j&7)) )
+ count++;
+
+ // FIXME: copy it off
+ }
+
+ _printf ("Average clusters hearable: %i\n", count/portalclusters);
+}
+
+/*
+===========
+VisMain
+===========
+*/
+int VisMain (int argc, char **argv)
+{
+ char portalfile[1024];
+ char name[1024];
+ int i;
+ double start, end;
+
+ _printf ("---- vis ----\n");
+
+ verbose = qfalse;
+ for (i=1 ; i<argc ; i++) {
+ if (!strcmp(argv[i],"-threads")) {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ } else if (!strcmp(argv[i],"-threads")) {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ } else if (!strcmp(argv[i], "-fast")) {
+ _printf ("fastvis = true\n");
+ fastvis = qtrue;
+ } else if (!strcmp(argv[i], "-merge")) {
+ _printf ("merge = true\n");
+ mergevis = qtrue;
+ } else if (!strcmp(argv[i], "-nopassage")) {
+ _printf ("nopassage = true\n");
+ noPassageVis = qtrue;
+ } else if (!strcmp(argv[i], "-passageOnly")) {
+ _printf("passageOnly = true\n");
+ passageVisOnly = qtrue;
+ } else if (!strcmp(argv[i], "-level")) {
+ testlevel = atoi(argv[i+1]);
+ _printf ("testlevel = %i\n", testlevel);
+ i++;
+ } else if (!strcmp(argv[i], "-v")) {
+ _printf ("verbose = true\n");
+ verbose = qtrue;
+ } else if (!strcmp (argv[i],"-nosort")) {
+ _printf ("nosort = true\n");
+ nosort = qtrue;
+ } else if (!strcmp (argv[i],"-saveprt")) {
+ _printf ("saveprt = true\n");
+ saveprt = qtrue;
+ } else if (!strcmp (argv[i],"-tmpin")) {
+ strcpy (inbase, "/tmp");
+ } else if (!strcmp (argv[i],"-tmpout")) {
+ strcpy (outbase, "/tmp");
+ } else if (argv[i][0] == '-') {
+ Error ("Unknown option \"%s\"", argv[i]);
+ } else {
+ break;
+ }
+ }
+
+ if (i != argc - 1)
+ Error ("usage: vis [-threads #] [-level 0-4] [-fast] [-v] bspfile");
+
+#ifdef MREDEBUG
+ start = clock();
+#else
+ start = I_FloatTime ();
+#endif
+
+ ThreadSetDefault ();
+
+ SetQdirFromPath (argv[i]);
+
+#ifdef _WIN32
+ InitPakFile(gamedir, NULL);
+#endif
+
+ // load the bsp
+ sprintf (name, "%s%s", inbase, ExpandArg(argv[i]));
+ StripExtension (name);
+ strcat (name, ".bsp");
+ _printf ("reading %s\n", name);
+ LoadBSPFile (name);
+
+ // load the portal file
+ sprintf (portalfile, "%s%s", inbase, ExpandArg(argv[i]));
+ StripExtension (portalfile);
+ strcat (portalfile, ".prt");
+ _printf ("reading %s\n", portalfile);
+ LoadPortals (portalfile);
+
+ if (mergevis)
+ {
+ MergeLeaves();
+ MergeLeafPortals();
+ }
+
+ CountActivePortals();
+// WritePortals("maps/hints.prs");
+
+ _printf ("visdatasize:%i\n", numVisBytes);
+
+ CalcVis ();
+
+// CalcPHS ();
+
+ // delete the prt file
+ if ( !saveprt ) {
+ remove( portalfile );
+ }
+
+ // write the bsp file
+ _printf ("writing %s\n", name);
+ WriteBSPFile (name);
+
+#ifdef MREDEBUG
+ end = clock();
+ _printf ("%5.2f seconds elapsed\n", (end-start) / CLK_TCK);
+#else
+ end = I_FloatTime ();
+ _printf ("%5.2f seconds elapsed\n", end-start);
+#endif
+ return 0;
+}
+
diff --git a/q3map/vis.h b/q3map/vis.h
new file mode 100755
index 0000000..e607184
--- /dev/null
+++ b/q3map/vis.h
@@ -0,0 +1,162 @@
+/*
+===========================================================================
+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
+===========================================================================
+*/
+// vis.h
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+
+#define MAX_PORTALS 32768
+
+#define PORTALFILE "PRT1"
+
+#define ON_EPSILON 0.1
+
+//#define MREDEBUG
+
+// seperator caching helps a bit
+#define SEPERATORCACHE
+
+// can't have more seperators than the max number of points on a winding
+#define MAX_SEPERATORS 64
+
+typedef struct
+{
+ vec3_t normal;
+ float dist;
+} plane_t;
+
+#define MAX_POINTS_ON_WINDING 64
+#define MAX_POINTS_ON_FIXED_WINDING 12
+
+typedef struct
+{
+ int numpoints;
+ vec3_t points[MAX_POINTS_ON_FIXED_WINDING]; // variable sized
+} winding_t;
+
+winding_t *NewWinding (int points);
+void FreeWinding (winding_t *w);
+winding_t *CopyWinding (winding_t *w);
+
+
+typedef struct passage_s
+{
+ struct passage_s *next;
+ byte cansee[1]; //all portals that can be seen through this passage
+} passage_t;
+
+typedef enum {stat_none, stat_working, stat_done} vstatus_t;
+typedef struct
+{
+ int num;
+ qboolean hint; // true if this portal was created from a hint splitter
+ qboolean removed;
+ plane_t plane; // normal pointing into neighbor
+ int leaf; // neighbor
+
+ vec3_t origin; // for fast clip testing
+ float radius;
+
+ winding_t *winding;
+ vstatus_t status;
+ byte *portalfront; // [portals], preliminary
+ byte *portalflood; // [portals], intermediate
+ byte *portalvis; // [portals], final
+
+ int nummightsee; // bit count on portalflood for sort
+ passage_t *passages; // there are just as many passages as there
+ // are portals in the leaf this portal leads to
+} vportal_t;
+
+#define MAX_PORTALS_ON_LEAF 128
+typedef struct leaf_s
+{
+ int numportals;
+ int merged;
+ vportal_t *portals[MAX_PORTALS_ON_LEAF];
+} leaf_t;
+
+
+typedef struct pstack_s
+{
+ byte mightsee[MAX_PORTALS/8]; // bit string
+ struct pstack_s *next;
+ leaf_t *leaf;
+ vportal_t *portal; // portal exiting
+ winding_t *source;
+ winding_t *pass;
+
+ winding_t windings[3]; // source, pass, temp in any order
+ int freewindings[3];
+
+ plane_t portalplane;
+ int depth;
+#ifdef SEPERATORCACHE
+ plane_t seperators[2][MAX_SEPERATORS];
+ int numseperators[2];
+#endif
+} pstack_t;
+
+typedef struct
+{
+ vportal_t *base;
+ int c_chains;
+ pstack_t pstack_head;
+} threaddata_t;
+
+
+
+extern int numportals;
+extern int portalclusters;
+
+extern vportal_t *portals;
+extern leaf_t *leafs;
+
+extern int c_portaltest, c_portalpass, c_portalcheck;
+extern int c_portalskip, c_leafskip;
+extern int c_vistest, c_mighttest;
+extern int c_chains;
+
+extern byte *vismap, *vismap_p, *vismap_end; // past visfile
+
+extern int testlevel;
+
+extern byte *uncompressed;
+
+extern int leafbytes, leaflongs;
+extern int portalbytes, portallongs;
+
+
+void LeafFlow (int leafnum);
+
+
+void BasePortalVis(int portalnum);
+void BetterPortalVis(int portalnum);
+void PortalFlow(int portalnum);
+void PassagePortalFlow(int portalnum);
+void CreatePassages(int portalnum);
+void PassageFlow(int portalnum);
+
+extern vportal_t *sorted_portals[MAX_MAP_PORTALS*2];
+
+int CountBits (byte *bits, int numbits);
diff --git a/q3map/visflow.c b/q3map/visflow.c
new file mode 100755
index 0000000..c62a33b
--- /dev/null
+++ b/q3map/visflow.c
@@ -0,0 +1,1657 @@
+/*
+===========================================================================
+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 "vis.h"
+
+/*
+
+ each portal will have a list of all possible to see from first portal
+
+ if (!thread->portalmightsee[portalnum])
+
+ portal mightsee
+
+ for p2 = all other portals in leaf
+ get sperating planes
+ for all portals that might be seen by p2
+ mark as unseen if not present in seperating plane
+ flood fill a new mightsee
+ save as passagemightsee
+
+
+ void CalcMightSee (leaf_t *leaf,
+*/
+
+int CountBits (byte *bits, int numbits)
+{
+ int i;
+ int c;
+
+ c = 0;
+ for (i=0 ; i<numbits ; i++)
+ if (bits[i>>3] & (1<<(i&7)) )
+ c++;
+
+ return c;
+}
+
+int c_fullskip;
+int c_portalskip, c_leafskip;
+int c_vistest, c_mighttest;
+
+int c_chop, c_nochop;
+
+int active;
+
+void CheckStack (leaf_t *leaf, threaddata_t *thread)
+{
+ pstack_t *p, *p2;
+
+ for (p=thread->pstack_head.next ; p ; p=p->next)
+ {
+// _printf ("=");
+ if (p->leaf == leaf)
+ Error ("CheckStack: leaf recursion");
+ for (p2=thread->pstack_head.next ; p2 != p ; p2=p2->next)
+ if (p2->leaf == p->leaf)
+ Error ("CheckStack: late leaf recursion");
+ }
+// _printf ("\n");
+}
+
+
+winding_t *AllocStackWinding (pstack_t *stack)
+{
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ {
+ if (stack->freewindings[i])
+ {
+ stack->freewindings[i] = 0;
+ return &stack->windings[i];
+ }
+ }
+
+ Error ("AllocStackWinding: failed");
+
+ return NULL;
+}
+
+void FreeStackWinding (winding_t *w, pstack_t *stack)
+{
+ int i;
+
+ i = w - stack->windings;
+
+ if (i<0 || i>2)
+ return; // not from local
+
+ if (stack->freewindings[i])
+ Error ("FreeStackWinding: allready free");
+ stack->freewindings[i] = 1;
+}
+
+/*
+==============
+VisChopWinding
+
+==============
+*/
+winding_t *VisChopWinding (winding_t *in, pstack_t *stack, plane_t *split)
+{
+ 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 *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 > ON_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+
+ if (!counts[1])
+ return in; // completely on front side
+
+ if (!counts[0])
+ {
+ FreeStackWinding (in, stack);
+ return NULL;
+ }
+
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ neww = AllocStackWinding (stack);
+
+ neww->numpoints = 0;
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->points[i];
+
+ if (neww->numpoints == MAX_POINTS_ON_FIXED_WINDING)
+ {
+ FreeStackWinding (neww, stack);
+ return in; // 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)
+ {
+ FreeStackWinding (neww, stack);
+ return in; // 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++;
+ }
+
+ // free the original winding
+ FreeStackWinding (in, stack);
+
+ return neww;
+}
+
+/*
+==============
+ClipToSeperators
+
+Source, pass, and target are an ordering of portals.
+
+Generates seperating planes canidates by taking two points from source and one
+point from pass, and clips target by them.
+
+If target is totally clipped away, that portal can not be seen through.
+
+Normal clip keeps target on the same side as pass, which is correct if the
+order goes source, pass, target. If the order goes pass, source, target then
+flipclip should be set.
+==============
+*/
+winding_t *ClipToSeperators (winding_t *source, winding_t *pass, winding_t *target, qboolean flipclip, pstack_t *stack)
+{
+ int i, j, k, l;
+ plane_t plane;
+ vec3_t v1, v2;
+ float d;
+ vec_t length;
+ int counts[3];
+ qboolean fliptest;
+
+ // check all combinations
+ for (i=0 ; i<source->numpoints ; i++)
+ {
+ l = (i+1)%source->numpoints;
+ VectorSubtract (source->points[l] , source->points[i], v1);
+
+ // find a vertex of pass that makes a plane that puts all of the
+ // vertexes of pass on the front side and all of the vertexes of
+ // source on the back side
+ for (j=0 ; j<pass->numpoints ; j++)
+ {
+ VectorSubtract (pass->points[j], source->points[i], 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 (pass->points[j], plane.normal);
+
+ //
+ // find out which side of the generated seperating plane has the
+ // source portal
+ //
+#if 1
+ fliptest = qfalse;
+ for (k=0 ; k<source->numpoints ; k++)
+ {
+ if (k == i || k == l)
+ continue;
+ d = DotProduct (source->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ { // source is on the negative side, so we want all
+ // pass and target on the positive side
+ fliptest = qfalse;
+ break;
+ }
+ else if (d > ON_EPSILON)
+ { // source is on the positive side, so we want all
+ // pass and target on the negative side
+ fliptest = qtrue;
+ break;
+ }
+ }
+ if (k == source->numpoints)
+ continue; // planar with source portal
+#else
+ fliptest = flipclip;
+#endif
+ //
+ // flip the normal if the source portal is backwards
+ //
+ if (fliptest)
+ {
+ VectorSubtract (vec3_origin, plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+#if 1
+ //
+ // if all of the pass portal points are now on the positive side,
+ // this is the seperating plane
+ //
+ counts[0] = counts[1] = counts[2] = 0;
+ for (k=0 ; k<pass->numpoints ; k++)
+ {
+ if (k==j)
+ continue;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ break;
+ else if (d > ON_EPSILON)
+ counts[0]++;
+ else
+ counts[2]++;
+ }
+ if (k != pass->numpoints)
+ continue; // points on negative side, not a seperating plane
+
+ if (!counts[0])
+ continue; // planar with seperating plane
+#else
+ k = (j+1)%pass->numpoints;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ continue;
+ k = (j+pass->numpoints-1)%pass->numpoints;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ continue;
+#endif
+ //
+ // flip the normal if we want the back side
+ //
+ if (flipclip)
+ {
+ VectorSubtract (vec3_origin, plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+
+#ifdef SEPERATORCACHE
+ stack->seperators[flipclip][stack->numseperators[flipclip]] = plane;
+ if (++stack->numseperators[flipclip] >= MAX_SEPERATORS)
+ Error("MAX_SEPERATORS");
+#endif
+ //MrE: fast check first
+ d = DotProduct (stack->portal->origin, plane.normal) - plane.dist;
+ //if completely at the back of the seperator plane
+ if (d < -stack->portal->radius)
+ return NULL;
+ //if completely on the front of the seperator plane
+ if (d > stack->portal->radius)
+ break;
+
+ //
+ // clip target by the seperating plane
+ //
+ target = VisChopWinding (target, stack, &plane);
+ if (!target)
+ return NULL; // target is not visible
+
+ break; // optimization by Antony Suter
+ }
+ }
+
+ return target;
+}
+
+/*
+==================
+RecursiveLeafFlow
+
+Flood fill through the leafs
+If src_portal is NULL, this is the originating leaf
+==================
+*/
+void RecursiveLeafFlow (int leafnum, threaddata_t *thread, pstack_t *prevstack)
+{
+ pstack_t stack;
+ vportal_t *p;
+ plane_t backplane;
+ leaf_t *leaf;
+ int i, j, n;
+ long *test, *might, *prevmight, *vis, more;
+ int pnum;
+
+ thread->c_chains++;
+
+ leaf = &leafs[leafnum];
+// CheckStack (leaf, thread);
+
+ prevstack->next = &stack;
+
+ stack.next = NULL;
+ stack.leaf = leaf;
+ stack.portal = NULL;
+ stack.depth = prevstack->depth + 1;
+
+#ifdef SEPERATORCACHE
+ stack.numseperators[0] = 0;
+ stack.numseperators[1] = 0;
+#endif
+
+ might = (long *)stack.mightsee;
+ vis = (long *)thread->base->portalvis;
+
+ // check all portals for flowing into other leafs
+ for (i = 0; i < leaf->numportals; i++)
+ {
+ p = leaf->portals[i];
+ if (p->removed)
+ continue;
+ pnum = p - portals;
+
+ /* MrE: portal trace debug code
+ {
+ int portaltrace[] = {13, 16, 17, 37};
+ pstack_t *s;
+
+ s = &thread->pstack_head;
+ for (j = 0; s->next && j < sizeof(portaltrace)/sizeof(int) - 1; j++, s = s->next)
+ {
+ if (s->portal->num != portaltrace[j])
+ break;
+ }
+ if (j >= sizeof(portaltrace)/sizeof(int) - 1)
+ {
+ if (p->num == portaltrace[j])
+ n = 0; //traced through all the portals
+ }
+ }
+ */
+
+ if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
+ {
+ continue; // can't possibly see it
+ }
+
+ // if the portal can't see anything we haven't allready seen, skip it
+ if (p->status == stat_done)
+ {
+ test = (long *)p->portalvis;
+ }
+ else
+ {
+ test = (long *)p->portalflood;
+ }
+
+ more = 0;
+ prevmight = (long *)prevstack->mightsee;
+ for (j=0 ; j<portallongs ; j++)
+ {
+ might[j] = prevmight[j] & test[j];
+ more |= (might[j] & ~vis[j]);
+ }
+
+ if (!more &&
+ (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) )
+ { // can't see anything new
+ continue;
+ }
+
+ // get plane of portal, point normal into the neighbor leaf
+ stack.portalplane = p->plane;
+ VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
+ backplane.dist = -p->plane.dist;
+
+// c_portalcheck++;
+
+ stack.portal = p;
+ stack.next = NULL;
+ stack.freewindings[0] = 1;
+ stack.freewindings[1] = 1;
+ stack.freewindings[2] = 1;
+
+#if 1
+ {
+ float d;
+
+ d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
+ d -= thread->pstack_head.portalplane.dist;
+ if (d < -p->radius)
+ {
+ continue;
+ }
+ else if (d > p->radius)
+ {
+ stack.pass = p->winding;
+ }
+ else
+ {
+ stack.pass = VisChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+ if (!stack.pass)
+ continue;
+ }
+ }
+#else
+ stack.pass = VisChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+ if (!stack.pass)
+ continue;
+#endif
+
+
+#if 1
+ {
+ float d;
+
+ d = DotProduct (thread->base->origin, p->plane.normal);
+ d -= p->plane.dist;
+ //MrE: vis-bug fix
+ //if (d > p->radius)
+ if (d > thread->base->radius)
+ {
+ continue;
+ }
+ //MrE: vis-bug fix
+ //if (d < -p->radius)
+ else if (d < -thread->base->radius)
+ {
+ stack.source = prevstack->source;
+ }
+ else
+ {
+ stack.source = VisChopWinding (prevstack->source, &stack, &backplane);
+ //FIXME: shouldn't we create a new source origin and radius for fast checks?
+ if (!stack.source)
+ continue;
+ }
+ }
+#else
+ stack.source = VisChopWinding (prevstack->source, &stack, &backplane);
+ if (!stack.source)
+ continue;
+#endif
+
+ if (!prevstack->pass)
+ { // the second leaf can only be blocked if coplanar
+
+ // mark the portal as visible
+ thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+ RecursiveLeafFlow (p->leaf, thread, &stack);
+ continue;
+ }
+
+#ifdef SEPERATORCACHE
+ if (stack.numseperators[0])
+ {
+ for (n = 0; n < stack.numseperators[0]; n++)
+ {
+ stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[0][n]);
+ if (!stack.pass)
+ break; // target is not visible
+ }
+ if (n < stack.numseperators[0])
+ continue;
+ }
+ else
+ {
+ stack.pass = ClipToSeperators (prevstack->source, prevstack->pass, stack.pass, qfalse, &stack);
+ }
+#else
+ stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, qfalse, &stack);
+#endif
+ if (!stack.pass)
+ continue;
+
+#ifdef SEPERATORCACHE
+ if (stack.numseperators[1])
+ {
+ for (n = 0; n < stack.numseperators[1]; n++)
+ {
+ stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[1][n]);
+ if (!stack.pass)
+ break; // target is not visible
+ }
+ }
+ else
+ {
+ stack.pass = ClipToSeperators (prevstack->pass, prevstack->source, stack.pass, qtrue, &stack);
+ }
+#else
+ stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, qtrue, &stack);
+#endif
+ if (!stack.pass)
+ continue;
+
+ // mark the portal as visible
+ thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+ // flow through it for real
+ RecursiveLeafFlow (p->leaf, thread, &stack);
+ //
+ stack.next = NULL;
+ }
+}
+
+/*
+===============
+PortalFlow
+
+generates the portalvis bit vector
+===============
+*/
+void PortalFlow (int portalnum)
+{
+ threaddata_t data;
+ int i;
+ vportal_t *p;
+ int c_might, c_can;
+
+#ifdef MREDEBUG
+ _printf("\r%6d", portalnum);
+#endif
+
+ p = sorted_portals[portalnum];
+
+ if (p->removed)
+ {
+ p->status = stat_done;
+ return;
+ }
+
+ p->status = stat_working;
+
+ c_might = CountBits (p->portalflood, numportals*2);
+
+ memset (&data, 0, sizeof(data));
+ data.base = p;
+
+ data.pstack_head.portal = p;
+ data.pstack_head.source = p->winding;
+ data.pstack_head.portalplane = p->plane;
+ data.pstack_head.depth = 0;
+ for (i=0 ; i<portallongs ; i++)
+ ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
+
+ RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
+
+ p->status = stat_done;
+
+ c_can = CountBits (p->portalvis, numportals*2);
+
+ qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
+ (int)(p - portals), c_might, c_can, data.c_chains);
+}
+
+/*
+==================
+RecursivePassageFlow
+==================
+*/
+void RecursivePassageFlow (vportal_t *portal, threaddata_t *thread, pstack_t *prevstack)
+{
+ pstack_t stack;
+ vportal_t *p;
+ leaf_t *leaf;
+ passage_t *passage, *nextpassage;
+ int i, j;
+ long *might, *vis, *prevmight, *cansee, *portalvis, more;
+ int pnum;
+
+ leaf = &leafs[portal->leaf];
+
+ prevstack->next = &stack;
+
+ stack.next = NULL;
+ stack.depth = prevstack->depth + 1;
+
+ vis = (long *)thread->base->portalvis;
+
+ passage = portal->passages;
+ nextpassage = passage;
+ // check all portals for flowing into other leafs
+ for (i = 0; i < leaf->numportals; i++, passage = nextpassage)
+ {
+ p = leaf->portals[i];
+ if ( p->removed ) {
+ continue;
+ }
+ nextpassage = passage->next;
+ pnum = p - portals;
+
+ if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) ) {
+ continue; // can't possibly see it
+ }
+
+ // mark the portal as visible
+ thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+ prevmight = (long *)prevstack->mightsee;
+ cansee = (long *)passage->cansee;
+ might = (long *)stack.mightsee;
+ memcpy(might, prevmight, portalbytes);
+ if (p->status == stat_done)
+ portalvis = (long *) p->portalvis;
+ else
+ portalvis = (long *) p->portalflood;
+ more = 0;
+ for (j = 0; j < portallongs; j++)
+ {
+ if (*might)
+ {
+ *might &= *cansee++ & *portalvis++;
+ more |= (*might & ~vis[j]);
+ }
+ else
+ {
+ cansee++;
+ portalvis++;
+ }
+ might++;
+ }
+
+ if ( !more ) {
+ // can't see anything new
+ continue;
+ }
+
+ // flow through it for real
+ RecursivePassageFlow(p, thread, &stack);
+
+ stack.next = NULL;
+ }
+}
+
+/*
+===============
+PassageFlow
+===============
+*/
+void PassageFlow (int portalnum)
+{
+ threaddata_t data;
+ int i;
+ vportal_t *p;
+// int c_might, c_can;
+
+#ifdef MREDEBUG
+ _printf("\r%6d", portalnum);
+#endif
+
+ p = sorted_portals[portalnum];
+
+ if (p->removed)
+ {
+ p->status = stat_done;
+ return;
+ }
+
+ p->status = stat_working;
+
+// c_might = CountBits (p->portalflood, numportals*2);
+
+ memset (&data, 0, sizeof(data));
+ data.base = p;
+
+ data.pstack_head.portal = p;
+ data.pstack_head.source = p->winding;
+ data.pstack_head.portalplane = p->plane;
+ data.pstack_head.depth = 0;
+ for (i=0 ; i<portallongs ; i++)
+ ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
+
+ RecursivePassageFlow (p, &data, &data.pstack_head);
+
+ p->status = stat_done;
+
+ /*
+ c_can = CountBits (p->portalvis, numportals*2);
+
+ qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
+ (int)(p - portals), c_might, c_can, data.c_chains);
+ */
+}
+
+/*
+==================
+RecursivePassagePortalFlow
+==================
+*/
+void RecursivePassagePortalFlow (vportal_t *portal, threaddata_t *thread, pstack_t *prevstack)
+{
+ pstack_t stack;
+ vportal_t *p;
+ leaf_t *leaf;
+ plane_t backplane;
+ passage_t *passage, *nextpassage;
+ int i, j, n;
+ long *might, *vis, *prevmight, *cansee, *portalvis, more;
+ int pnum;
+
+// thread->c_chains++;
+
+ leaf = &leafs[portal->leaf];
+// CheckStack (leaf, thread);
+
+ prevstack->next = &stack;
+
+ stack.next = NULL;
+ stack.leaf = leaf;
+ stack.portal = NULL;
+ stack.depth = prevstack->depth + 1;
+
+#ifdef SEPERATORCACHE
+ stack.numseperators[0] = 0;
+ stack.numseperators[1] = 0;
+#endif
+
+ vis = (long *)thread->base->portalvis;
+
+ passage = portal->passages;
+ nextpassage = passage;
+ // check all portals for flowing into other leafs
+ for (i = 0; i < leaf->numportals; i++, passage = nextpassage)
+ {
+ p = leaf->portals[i];
+ if (p->removed)
+ continue;
+ nextpassage = passage->next;
+ pnum = p - portals;
+
+ if ( ! (prevstack->mightsee[pnum >> 3] & (1<<(pnum&7)) ) )
+ continue; // can't possibly see it
+
+ prevmight = (long *)prevstack->mightsee;
+ cansee = (long *)passage->cansee;
+ might = (long *)stack.mightsee;
+ memcpy(might, prevmight, portalbytes);
+ if (p->status == stat_done)
+ portalvis = (long *) p->portalvis;
+ else
+ portalvis = (long *) p->portalflood;
+ more = 0;
+ for (j = 0; j < portallongs; j++)
+ {
+ if (*might)
+ {
+ *might &= *cansee++ & *portalvis++;
+ more |= (*might & ~vis[j]);
+ }
+ else
+ {
+ cansee++;
+ portalvis++;
+ }
+ might++;
+ }
+
+ if (!more && (thread->base->portalvis[pnum>>3] & (1<<(pnum&7))) )
+ { // can't see anything new
+ continue;
+ }
+
+ // get plane of portal, point normal into the neighbor leaf
+ stack.portalplane = p->plane;
+ VectorSubtract (vec3_origin, p->plane.normal, backplane.normal);
+ backplane.dist = -p->plane.dist;
+
+// c_portalcheck++;
+
+ stack.portal = p;
+ stack.next = NULL;
+ stack.freewindings[0] = 1;
+ stack.freewindings[1] = 1;
+ stack.freewindings[2] = 1;
+
+#if 1
+ {
+ float d;
+
+ d = DotProduct (p->origin, thread->pstack_head.portalplane.normal);
+ d -= thread->pstack_head.portalplane.dist;
+ if (d < -p->radius)
+ {
+ continue;
+ }
+ else if (d > p->radius)
+ {
+ stack.pass = p->winding;
+ }
+ else
+ {
+ stack.pass = VisChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+ if (!stack.pass)
+ continue;
+ }
+ }
+#else
+ stack.pass = VisChopWinding (p->winding, &stack, &thread->pstack_head.portalplane);
+ if (!stack.pass)
+ continue;
+#endif
+
+
+#if 1
+ {
+ float d;
+
+ d = DotProduct (thread->base->origin, p->plane.normal);
+ d -= p->plane.dist;
+ //MrE: vis-bug fix
+ //if (d > p->radius)
+ if (d > thread->base->radius)
+ {
+ continue;
+ }
+ //MrE: vis-bug fix
+ //if (d < -p->radius)
+ else if (d < -thread->base->radius)
+ {
+ stack.source = prevstack->source;
+ }
+ else
+ {
+ stack.source = VisChopWinding (prevstack->source, &stack, &backplane);
+ //FIXME: shouldn't we create a new source origin and radius for fast checks?
+ if (!stack.source)
+ continue;
+ }
+ }
+#else
+ stack.source = VisChopWinding (prevstack->source, &stack, &backplane);
+ if (!stack.source)
+ continue;
+#endif
+
+ if (!prevstack->pass)
+ { // the second leaf can only be blocked if coplanar
+
+ // mark the portal as visible
+ thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+ RecursivePassagePortalFlow (p, thread, &stack);
+ continue;
+ }
+
+#ifdef SEPERATORCACHE
+ if (stack.numseperators[0])
+ {
+ for (n = 0; n < stack.numseperators[0]; n++)
+ {
+ stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[0][n]);
+ if (!stack.pass)
+ break; // target is not visible
+ }
+ if (n < stack.numseperators[0])
+ continue;
+ }
+ else
+ {
+ stack.pass = ClipToSeperators (prevstack->source, prevstack->pass, stack.pass, qfalse, &stack);
+ }
+#else
+ stack.pass = ClipToSeperators (stack.source, prevstack->pass, stack.pass, qfalse, &stack);
+#endif
+ if (!stack.pass)
+ continue;
+
+#ifdef SEPERATORCACHE
+ if (stack.numseperators[1])
+ {
+ for (n = 0; n < stack.numseperators[1]; n++)
+ {
+ stack.pass = VisChopWinding (stack.pass, &stack, &stack.seperators[1][n]);
+ if (!stack.pass)
+ break; // target is not visible
+ }
+ }
+ else
+ {
+ stack.pass = ClipToSeperators (prevstack->pass, prevstack->source, stack.pass, qtrue, &stack);
+ }
+#else
+ stack.pass = ClipToSeperators (prevstack->pass, stack.source, stack.pass, qtrue, &stack);
+#endif
+ if (!stack.pass)
+ continue;
+
+ // mark the portal as visible
+ thread->base->portalvis[pnum>>3] |= (1<<(pnum&7));
+
+ // flow through it for real
+ RecursivePassagePortalFlow(p, thread, &stack);
+ //
+ stack.next = NULL;
+ }
+}
+
+/*
+===============
+PassagePortalFlow
+===============
+*/
+void PassagePortalFlow (int portalnum)
+{
+ threaddata_t data;
+ int i;
+ vportal_t *p;
+// int c_might, c_can;
+
+#ifdef MREDEBUG
+ _printf("\r%6d", portalnum);
+#endif
+
+ p = sorted_portals[portalnum];
+
+ if (p->removed)
+ {
+ p->status = stat_done;
+ return;
+ }
+
+ p->status = stat_working;
+
+// c_might = CountBits (p->portalflood, numportals*2);
+
+ memset (&data, 0, sizeof(data));
+ data.base = p;
+
+ data.pstack_head.portal = p;
+ data.pstack_head.source = p->winding;
+ data.pstack_head.portalplane = p->plane;
+ data.pstack_head.depth = 0;
+ for (i=0 ; i<portallongs ; i++)
+ ((long *)data.pstack_head.mightsee)[i] = ((long *)p->portalflood)[i];
+
+ RecursivePassagePortalFlow (p, &data, &data.pstack_head);
+
+ p->status = stat_done;
+
+ /*
+ c_can = CountBits (p->portalvis, numportals*2);
+
+ qprintf ("portal:%4i mightsee:%4i cansee:%4i (%i chains)\n",
+ (int)(p - portals), c_might, c_can, data.c_chains);
+ */
+}
+
+winding_t *PassageChopWinding (winding_t *in, winding_t *out, plane_t *split)
+{
+ 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 *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 > ON_EPSILON)
+ sides[i] = SIDE_FRONT;
+ else if (dot < -ON_EPSILON)
+ sides[i] = SIDE_BACK;
+ else
+ {
+ sides[i] = SIDE_ON;
+ }
+ counts[sides[i]]++;
+ }
+
+ if (!counts[1])
+ return in; // completely on front side
+
+ if (!counts[0])
+ {
+ return NULL;
+ }
+
+ 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)
+ {
+ return in; // 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)
+ {
+ return in; // 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++;
+ }
+
+ return neww;
+}
+
+/*
+===============
+AddSeperators
+===============
+*/
+int AddSeperators (winding_t *source, winding_t *pass, qboolean flipclip, plane_t *seperators, int maxseperators)
+{
+ int i, j, k, l;
+ plane_t plane;
+ vec3_t v1, v2;
+ float d;
+ vec_t length;
+ int counts[3], numseperators;
+ qboolean fliptest;
+
+ numseperators = 0;
+ // check all combinations
+ for (i=0 ; i<source->numpoints ; i++)
+ {
+ l = (i+1)%source->numpoints;
+ VectorSubtract (source->points[l] , source->points[i], v1);
+
+ // find a vertex of pass that makes a plane that puts all of the
+ // vertexes of pass on the front side and all of the vertexes of
+ // source on the back side
+ for (j=0 ; j<pass->numpoints ; j++)
+ {
+ VectorSubtract (pass->points[j], source->points[i], 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 (pass->points[j], plane.normal);
+
+ //
+ // find out which side of the generated seperating plane has the
+ // source portal
+ //
+#if 1
+ fliptest = qfalse;
+ for (k=0 ; k<source->numpoints ; k++)
+ {
+ if (k == i || k == l)
+ continue;
+ d = DotProduct (source->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ { // source is on the negative side, so we want all
+ // pass and target on the positive side
+ fliptest = qfalse;
+ break;
+ }
+ else if (d > ON_EPSILON)
+ { // source is on the positive side, so we want all
+ // pass and target on the negative side
+ fliptest = qtrue;
+ break;
+ }
+ }
+ if (k == source->numpoints)
+ continue; // planar with source portal
+#else
+ fliptest = flipclip;
+#endif
+ //
+ // flip the normal if the source portal is backwards
+ //
+ if (fliptest)
+ {
+ VectorSubtract (vec3_origin, plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+#if 1
+ //
+ // if all of the pass portal points are now on the positive side,
+ // this is the seperating plane
+ //
+ counts[0] = counts[1] = counts[2] = 0;
+ for (k=0 ; k<pass->numpoints ; k++)
+ {
+ if (k==j)
+ continue;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ break;
+ else if (d > ON_EPSILON)
+ counts[0]++;
+ else
+ counts[2]++;
+ }
+ if (k != pass->numpoints)
+ continue; // points on negative side, not a seperating plane
+
+ if (!counts[0])
+ continue; // planar with seperating plane
+#else
+ k = (j+1)%pass->numpoints;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ continue;
+ k = (j+pass->numpoints-1)%pass->numpoints;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ continue;
+#endif
+ //
+ // flip the normal if we want the back side
+ //
+ if (flipclip)
+ {
+ VectorSubtract (vec3_origin, plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+
+ if (numseperators >= maxseperators)
+ Error("max seperators");
+ seperators[numseperators] = plane;
+ numseperators++;
+ break;
+ }
+ }
+ return numseperators;
+}
+
+/*
+===============
+CreatePassages
+
+MrE: create passages from one portal to all the portals in the leaf the portal leads to
+ every passage has a cansee bit string with all the portals that can be
+ seen through the passage
+===============
+*/
+void CreatePassages(int portalnum)
+{
+ int i, j, k, n, numseperators, numsee;
+ float d;
+ vportal_t *portal, *p, *target;
+ leaf_t *leaf;
+ passage_t *passage, *lastpassage;
+ plane_t seperators[MAX_SEPERATORS*2];
+ winding_t *w;
+ winding_t in, out, *res;
+
+#ifdef MREDEBUG
+ _printf("\r%6d", portalnum);
+#endif
+
+ portal = sorted_portals[portalnum];
+
+ if (portal->removed)
+ {
+ portal->status = stat_done;
+ return;
+ }
+
+ lastpassage = NULL;
+ leaf = &leafs[portal->leaf];
+ for (i = 0; i < leaf->numportals; i++)
+ {
+ target = leaf->portals[i];
+ if (target->removed)
+ continue;
+
+ passage = (passage_t *) malloc(sizeof(passage_t) + portalbytes);
+ memset(passage, 0, sizeof(passage_t) + portalbytes);
+ numseperators = AddSeperators(portal->winding, target->winding, qfalse, seperators, MAX_SEPERATORS*2);
+ numseperators += AddSeperators(target->winding, portal->winding, qtrue, &seperators[numseperators], MAX_SEPERATORS*2-numseperators);
+
+ passage->next = NULL;
+ if (lastpassage)
+ lastpassage->next = passage;
+ else
+ portal->passages = passage;
+ lastpassage = passage;
+
+ numsee = 0;
+ //create the passage->cansee
+ for (j = 0; j < numportals * 2; j++)
+ {
+ p = &portals[j];
+ if (p->removed)
+ continue;
+ if ( ! (target->portalflood[j >> 3] & (1<<(j&7)) ) )
+ continue;
+ if ( ! (portal->portalflood[j >> 3] & (1<<(j&7)) ) )
+ continue;
+ for (k = 0; k < numseperators; k++)
+ {
+ //
+ d = DotProduct (p->origin, seperators[k].normal) - seperators[k].dist;
+ //if completely at the back of the seperator plane
+ if (d < -p->radius + ON_EPSILON)
+ break;
+ w = p->winding;
+ for (n = 0; n < w->numpoints; n++)
+ {
+ d = DotProduct (w->points[n], seperators[k].normal) - seperators[k].dist;
+ //if at the front of the seperator
+ if (d > ON_EPSILON)
+ break;
+ }
+ //if no points are at the front of the seperator
+ if (n >= w->numpoints)
+ break;
+ }
+ if (k < numseperators)
+ continue;
+ memcpy(&in, p->winding, sizeof(winding_t));
+ for (k = 0; k < numseperators; k++)
+ {
+ res = PassageChopWinding(&in, &out, &seperators[k]);
+ if (res == &out)
+ memcpy(&in, &out, sizeof(winding_t));
+ if (res == NULL)
+ break;
+ }
+ if (k < numseperators)
+ continue;
+ passage->cansee[j >> 3] |= (1<<(j&7));
+ numsee++;
+ }
+ }
+}
+
+void PassageMemory(void)
+{
+ int i, j, totalmem, totalportals;
+ vportal_t *portal, *target;
+ leaf_t *leaf;
+
+ totalmem = 0;
+ totalportals = 0;
+ for (i = 0; i < numportals; i++)
+ {
+ portal = sorted_portals[i];
+ if (portal->removed)
+ continue;
+ leaf = &leafs[portal->leaf];
+ for (j = 0; j < leaf->numportals; j++)
+ {
+ target = leaf->portals[j];
+ if (target->removed)
+ continue;
+ totalmem += sizeof(passage_t) + portalbytes;
+ totalportals++;
+ }
+ }
+ _printf("%7i average number of passages per leaf\n", totalportals / numportals);
+ _printf("%7i MB required passage memory\n", totalmem >> 10 >> 10);
+}
+
+/*
+===============================================================================
+
+This is a rough first-order aproximation that is used to trivially reject some
+of the final calculations.
+
+
+Calculates portalfront and portalflood bit vectors
+
+thinking about:
+
+typedef struct passage_s
+{
+ struct passage_s *next;
+ struct portal_s *to;
+ stryct sep_s *seperators;
+ byte *mightsee;
+} passage_t;
+
+typedef struct portal_s
+{
+ struct passage_s *passages;
+ int leaf; // leaf portal faces into
+} portal_s;
+
+leaf = portal->leaf
+clear
+for all portals
+
+
+calc portal visibility
+ clear bit vector
+ for all passages
+ passage visibility
+
+
+for a portal to be visible to a passage, it must be on the front of
+all seperating planes, and both portals must be behind the new portal
+
+===============================================================================
+*/
+
+int c_flood, c_vis;
+
+
+/*
+==================
+SimpleFlood
+
+==================
+*/
+void SimpleFlood (vportal_t *srcportal, int leafnum)
+{
+ int i;
+ leaf_t *leaf;
+ vportal_t *p;
+ int pnum;
+
+ leaf = &leafs[leafnum];
+
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+ if (p->removed)
+ continue;
+ pnum = p - portals;
+ if ( ! (srcportal->portalfront[pnum>>3] & (1<<(pnum&7)) ) )
+ continue;
+
+ if (srcportal->portalflood[pnum>>3] & (1<<(pnum&7)) )
+ continue;
+
+ srcportal->portalflood[pnum>>3] |= (1<<(pnum&7));
+
+ SimpleFlood (srcportal, p->leaf);
+ }
+}
+
+/*
+==============
+BasePortalVis
+==============
+*/
+void BasePortalVis (int portalnum)
+{
+ int j, k;
+ vportal_t *tp, *p;
+ float d;
+ winding_t *w;
+
+ p = portals+portalnum;
+
+ if (p->removed)
+ return;
+
+ p->portalfront = malloc (portalbytes);
+ memset (p->portalfront, 0, portalbytes);
+
+ p->portalflood = malloc (portalbytes);
+ memset (p->portalflood, 0, portalbytes);
+
+ p->portalvis = malloc (portalbytes);
+ memset (p->portalvis, 0, portalbytes);
+
+ for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
+ {
+ if (j == portalnum)
+ continue;
+ if (tp->removed)
+ continue;
+ /*
+ if (farplanedist >= 0)
+ {
+ vec3_t dir;
+ VectorSubtract(p->origin, tp->origin, dir);
+ if (VectorLength(dir) > farplanedist - p->radius - tp->radius)
+ continue;
+ }
+ */
+ w = tp->winding;
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ d = DotProduct (w->points[k], p->plane.normal)
+ - p->plane.dist;
+ if (d > ON_EPSILON)
+ break;
+ }
+ if (k == w->numpoints)
+ continue; // no points on front
+
+ w = p->winding;
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ d = DotProduct (w->points[k], tp->plane.normal)
+ - tp->plane.dist;
+ if (d < -ON_EPSILON)
+ break;
+ }
+ if (k == w->numpoints)
+ continue; // no points on front
+
+ p->portalfront[j>>3] |= (1<<(j&7));
+ }
+
+ SimpleFlood (p, p->leaf);
+
+ p->nummightsee = CountBits (p->portalflood, numportals*2);
+// _printf ("portal %i: %i mightsee\n", portalnum, p->nummightsee);
+ c_flood += p->nummightsee;
+}
+
+
+
+
+
+/*
+===============================================================================
+
+This is a second order aproximation
+
+Calculates portalvis bit vector
+
+WAAAAAAY too slow.
+
+===============================================================================
+*/
+
+/*
+==================
+RecursiveLeafBitFlow
+
+==================
+*/
+void RecursiveLeafBitFlow (int leafnum, byte *mightsee, byte *cansee)
+{
+ vportal_t *p;
+ leaf_t *leaf;
+ int i, j;
+ long more;
+ int pnum;
+ byte newmight[MAX_PORTALS/8];
+
+ leaf = &leafs[leafnum];
+
+ // check all portals for flowing into other leafs
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+ if (p->removed)
+ continue;
+ pnum = p - portals;
+
+ // if some previous portal can't see it, skip
+ if (! (mightsee[pnum>>3] & (1<<(pnum&7)) ) )
+ continue;
+
+ // if this portal can see some portals we mightsee, recurse
+ more = 0;
+ for (j=0 ; j<portallongs ; j++)
+ {
+ ((long *)newmight)[j] = ((long *)mightsee)[j]
+ & ((long *)p->portalflood)[j];
+ more |= ((long *)newmight)[j] & ~((long *)cansee)[j];
+ }
+
+ if (!more)
+ continue; // can't see anything new
+
+ cansee[pnum>>3] |= (1<<(pnum&7));
+
+ RecursiveLeafBitFlow (p->leaf, newmight, cansee);
+ }
+}
+
+/*
+==============
+BetterPortalVis
+==============
+*/
+void BetterPortalVis (int portalnum)
+{
+ vportal_t *p;
+
+ p = portals+portalnum;
+
+ if (p->removed)
+ return;
+
+ RecursiveLeafBitFlow (p->leaf, p->portalflood, p->portalvis);
+
+ // build leaf vis information
+ p->nummightsee = CountBits (p->portalvis, numportals*2);
+ c_vis += p->nummightsee;
+}
+
+
diff --git a/q3map/writebsp.c b/q3map/writebsp.c
new file mode 100755
index 0000000..7a56d88
--- /dev/null
+++ b/q3map/writebsp.c
@@ -0,0 +1,418 @@
+/*
+===========================================================================
+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 "qbsp.h"
+
+/*
+============
+EmitShader
+============
+*/
+int EmitShader( const char *shader ) {
+ int i;
+ shaderInfo_t *si;
+
+ if ( !shader ) {
+ shader = "noshader";
+ }
+
+ for ( i = 0 ; i < numShaders ; i++ ) {
+ if ( !Q_stricmp( shader, dshaders[i].shader ) ) {
+ return i;
+ }
+ }
+
+ if ( i == MAX_MAP_SHADERS ) {
+ Error( "MAX_MAP_SHADERS" );
+ }
+ numShaders++;
+ strcpy( dshaders[i].shader, shader );
+
+ si = ShaderInfoForShader( shader );
+ dshaders[i].surfaceFlags = si->surfaceFlags;
+ dshaders[i].contentFlags = si->contents;
+
+ return i;
+}
+
+
+/*
+============
+EmitPlanes
+
+There is no oportunity to discard planes, because all of the original
+brushes will be saved in the map.
+============
+*/
+void EmitPlanes (void)
+{
+ int i;
+ dplane_t *dp;
+ plane_t *mp;
+
+ mp = mapplanes;
+ for (i=0 ; i<nummapplanes ; i++, mp++)
+ {
+ dp = &dplanes[numplanes];
+ VectorCopy ( mp->normal, dp->normal);
+ dp->dist = mp->dist;
+ numplanes++;
+ }
+}
+
+
+
+/*
+==================
+EmitLeaf
+==================
+*/
+void EmitLeaf (node_t *node)
+{
+ dleaf_t *leaf_p;
+ bspbrush_t *b;
+ drawSurfRef_t *dsr;
+
+ // emit a leaf
+ if (numleafs >= MAX_MAP_LEAFS)
+ Error ("MAX_MAP_LEAFS");
+
+ leaf_p = &dleafs[numleafs];
+ numleafs++;
+
+ leaf_p->cluster = node->cluster;
+ leaf_p->area = node->area;
+
+ //
+ // write bounding box info
+ //
+ VectorCopy (node->mins, leaf_p->mins);
+ VectorCopy (node->maxs, leaf_p->maxs);
+
+ //
+ // write the leafbrushes
+ //
+ leaf_p->firstLeafBrush = numleafbrushes;
+ for ( b = node->brushlist ; b ; b = b->next ) {
+ if ( numleafbrushes >= MAX_MAP_LEAFBRUSHES ) {
+ Error( "MAX_MAP_LEAFBRUSHES" );
+ }
+ dleafbrushes[numleafbrushes] = b->original->outputNumber;
+ numleafbrushes++;
+ }
+ leaf_p->numLeafBrushes = numleafbrushes - leaf_p->firstLeafBrush;
+
+ //
+ // write the surfaces visible in this leaf
+ //
+ if ( node->opaque ) {
+ return; // no leaffaces in solids
+ }
+
+ // add the drawSurfRef_t drawsurfs
+ leaf_p->firstLeafSurface = numleafsurfaces;
+ for ( dsr = node->drawSurfReferences ; dsr ; dsr = dsr->nextRef ) {
+ if ( numleafsurfaces >= MAX_MAP_LEAFFACES)
+ Error ("MAX_MAP_LEAFFACES");
+ dleafsurfaces[numleafsurfaces] = dsr->outputNumber;
+ numleafsurfaces++;
+ }
+
+
+ leaf_p->numLeafSurfaces = numleafsurfaces - leaf_p->firstLeafSurface;
+}
+
+
+/*
+============
+EmitDrawNode_r
+============
+*/
+int EmitDrawNode_r (node_t *node)
+{
+ dnode_t *n;
+ int i;
+
+ if (node->planenum == PLANENUM_LEAF)
+ {
+ EmitLeaf (node);
+ return -numleafs;
+ }
+
+ // emit a node
+ if (numnodes == MAX_MAP_NODES)
+ Error ("MAX_MAP_NODES");
+ n = &dnodes[numnodes];
+ numnodes++;
+
+ VectorCopy (node->mins, n->mins);
+ VectorCopy (node->maxs, n->maxs);
+
+ if (node->planenum & 1)
+ Error ("WriteDrawNodes_r: odd planenum");
+ n->planeNum = node->planenum;
+
+ //
+ // recursively output the other nodes
+ //
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i]->planenum == PLANENUM_LEAF)
+ {
+ n->children[i] = -(numleafs + 1);
+ EmitLeaf (node->children[i]);
+ }
+ else
+ {
+ n->children[i] = numnodes;
+ EmitDrawNode_r (node->children[i]);
+ }
+ }
+
+ return n - dnodes;
+}
+
+//=========================================================
+
+
+
+/*
+============
+SetModelNumbers
+============
+*/
+void SetModelNumbers (void)
+{
+ int i;
+ int models;
+ char value[10];
+
+ models = 1;
+ for ( i=1 ; i<num_entities ; i++ ) {
+ if ( entities[i].brushes || entities[i].patches ) {
+ sprintf ( value, "*%i", models );
+ models++;
+ SetKeyValue (&entities[i], "model", value);
+ }
+ }
+
+}
+
+/*
+============
+SetLightStyles
+============
+*/
+#define MAX_SWITCHED_LIGHTS 32
+void SetLightStyles (void)
+{
+ int stylenum;
+ const char *t;
+ entity_t *e;
+ int i, j;
+ char value[10];
+ char lighttargets[MAX_SWITCHED_LIGHTS][64];
+
+
+ // any light that is controlled (has a targetname)
+ // must have a unique style number generated for it
+
+ stylenum = 0;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ e = &entities[i];
+
+ t = ValueForKey (e, "classname");
+ if (Q_strncasecmp (t, "light", 5))
+ continue;
+ t = ValueForKey (e, "targetname");
+ if (!t[0])
+ continue;
+
+ // find this targetname
+ for (j=0 ; j<stylenum ; j++)
+ if (!strcmp (lighttargets[j], t))
+ break;
+ if (j == stylenum)
+ {
+ if (stylenum == MAX_SWITCHED_LIGHTS)
+ Error ("stylenum == MAX_SWITCHED_LIGHTS");
+ strcpy (lighttargets[j], t);
+ stylenum++;
+ }
+ sprintf (value, "%i", 32 + j);
+ SetKeyValue (e, "style", value);
+ }
+
+}
+
+//===========================================================
+
+/*
+==================
+BeginBSPFile
+==================
+*/
+void BeginBSPFile( void ) {
+ // these values may actually be initialized
+ // if the file existed when loaded, so clear them explicitly
+ nummodels = 0;
+ numnodes = 0;
+ numbrushsides = 0;
+ numleafsurfaces = 0;
+ numleafbrushes = 0;
+
+ // leave leaf 0 as an error, because leafs are referenced as
+ // negative number nodes
+ numleafs = 1;
+}
+
+
+/*
+============
+EndBSPFile
+============
+*/
+void EndBSPFile( void ) {
+ char path[1024];
+
+ EmitPlanes ();
+ UnparseEntities ();
+
+ // write the map
+ sprintf (path, "%s.bsp", source);
+ _printf ("Writing %s\n", path);
+ WriteBSPFile (path);
+}
+
+
+//===========================================================
+
+/*
+============
+EmitBrushes
+============
+*/
+void EmitBrushes ( bspbrush_t *brushes ) {
+ int j;
+ dbrush_t *db;
+ bspbrush_t *b;
+ dbrushside_t *cp;
+
+ for ( b = brushes ; b ; b = b->next ) {
+ if ( numbrushes == MAX_MAP_BRUSHES ) {
+ Error( "MAX_MAP_BRUSHES" );
+ }
+ b->outputNumber = numbrushes;
+ db = &dbrushes[numbrushes];
+ numbrushes++;
+
+ db->shaderNum = EmitShader( b->contentShader->shader );
+ db->firstSide = numbrushsides;
+
+ // don't emit any generated backSide sides
+ db->numSides = 0;
+ for ( j=0 ; j<b->numsides ; j++ ) {
+ if ( b->sides[j].backSide ) {
+ continue;
+ }
+ if ( numbrushsides == MAX_MAP_BRUSHSIDES ) {
+ Error( "MAX_MAP_BRUSHSIDES ");
+ }
+ cp = &dbrushsides[numbrushsides];
+ db->numSides++;
+ numbrushsides++;
+ cp->planeNum = b->sides[j].planenum;
+ cp->shaderNum = EmitShader( b->sides[j].shaderInfo->shader );
+ }
+ }
+
+}
+
+
+/*
+==================
+BeginModel
+==================
+*/
+void BeginModel( void ) {
+ dmodel_t *mod;
+ bspbrush_t *b;
+ entity_t *e;
+ vec3_t mins, maxs;
+ parseMesh_t *p;
+ int i;
+
+ if ( nummodels == MAX_MAP_MODELS ) {
+ Error( "MAX_MAP_MODELS" );
+ }
+ mod = &dmodels[nummodels];
+
+ //
+ // bound the brushes
+ //
+ e = &entities[entity_num];
+
+ ClearBounds (mins, maxs);
+ for ( b = e->brushes ; b ; b = b->next ) {
+ if ( !b->numsides ) {
+ continue; // not a real brush (origin brush, etc)
+ }
+ AddPointToBounds (b->mins, mins, maxs);
+ AddPointToBounds (b->maxs, mins, maxs);
+ }
+
+ for ( p = e->patches ; p ; p = p->next ) {
+ for ( i = 0 ; i < p->mesh.width * p->mesh.height ; i++ ) {
+ AddPointToBounds( p->mesh.verts[i].xyz, mins, maxs );
+ }
+ }
+
+ VectorCopy (mins, mod->mins);
+ VectorCopy (maxs, mod->maxs);
+
+ mod->firstSurface = numDrawSurfaces;
+ mod->firstBrush = numbrushes;
+
+ EmitBrushes( e->brushes );
+}
+
+
+
+
+/*
+==================
+EndModel
+==================
+*/
+void EndModel( node_t *headnode ) {
+ dmodel_t *mod;
+
+ qprintf ("--- EndModel ---\n");
+
+ mod = &dmodels[nummodels];
+ EmitDrawNode_r (headnode);
+ mod->numSurfaces = numDrawSurfaces - mod->firstSurface;
+ mod->numBrushes = numbrushes - mod->firstBrush;
+
+ nummodels++;
+}
+