From 952c5c128f9efaea89d41d882c4ea3ade7df4591 Mon Sep 17 00:00:00 2001 From: zakk Date: Fri, 26 Aug 2005 04:48:05 +0000 Subject: Itsa me, quake3io! git-svn-id: svn://svn.icculus.org/quake3/trunk@2 edf5b092-35ff-0310-97b2-ce42778d08ea --- q3map/brush.c | 861 ++++++++ q3map/brush_primit.c | 52 + q3map/bsp.c | 605 ++++++ q3map/facebsp.c | 379 ++++ q3map/fog.c | 554 +++++ q3map/gldraw.c | 232 ++ q3map/glfile.c | 148 ++ q3map/leakfile.c | 100 + q3map/light.c | 2149 +++++++++++++++++++ q3map/light.h | 151 ++ q3map/light_trace.c | 944 +++++++++ q3map/lightmaps.c | 395 ++++ q3map/lightv.c | 5748 ++++++++++++++++++++++++++++++++++++++++++++++++++ q3map/makefile | 148 ++ q3map/map.c | 1251 +++++++++++ q3map/mesh.c | 682 ++++++ q3map/mesh.h | 48 + q3map/misc_model.c | 472 +++++ q3map/nodraw.c | 47 + q3map/patch.c | 286 +++ q3map/portals.c | 843 ++++++++ q3map/prtfile.c | 272 +++ q3map/q3map.sln | 56 + q3map/q3map.vcproj | 1606 ++++++++++++++ q3map/qbsp.h | 455 ++++ q3map/shaders.c | 608 ++++++ q3map/shaders.h | 71 + q3map/soundv.c | 5742 +++++++++++++++++++++++++++++++++++++++++++++++++ q3map/surface.c | 1158 ++++++++++ q3map/terrain.c | 1255 +++++++++++ q3map/tjunction.c | 551 +++++ q3map/tree.c | 146 ++ q3map/vis.c | 1197 +++++++++++ q3map/vis.h | 162 ++ q3map/visflow.c | 1657 +++++++++++++++ q3map/writebsp.c | 418 ++++ 36 files changed, 31449 insertions(+) create mode 100755 q3map/brush.c create mode 100755 q3map/brush_primit.c create mode 100755 q3map/bsp.c create mode 100755 q3map/facebsp.c create mode 100755 q3map/fog.c create mode 100755 q3map/gldraw.c create mode 100755 q3map/glfile.c create mode 100755 q3map/leakfile.c create mode 100755 q3map/light.c create mode 100755 q3map/light.h create mode 100755 q3map/light_trace.c create mode 100755 q3map/lightmaps.c create mode 100755 q3map/lightv.c create mode 100755 q3map/makefile create mode 100755 q3map/map.c create mode 100755 q3map/mesh.c create mode 100755 q3map/mesh.h create mode 100755 q3map/misc_model.c create mode 100755 q3map/nodraw.c create mode 100755 q3map/patch.c create mode 100755 q3map/portals.c create mode 100755 q3map/prtfile.c create mode 100755 q3map/q3map.sln create mode 100755 q3map/q3map.vcproj create mode 100755 q3map/qbsp.h create mode 100755 q3map/shaders.c create mode 100755 q3map/shaders.h create mode 100755 q3map/soundv.c create mode 100755 q3map/surface.c create mode 100755 q3map/terrain.c create mode 100755 q3map/tjunction.c create mode 100755 q3map/tree.c create mode 100755 q3map/vis.c create mode 100755 q3map/vis.h create mode 100755 q3map/visflow.c create mode 100755 q3map/writebsp.c (limited to 'q3map') 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 ; inumsides ; 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 ; inumsides ; 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 ; inumsides ; 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 ; inumsides ; 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;inumsides ; 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 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; inumsides ; 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 ( ; inumsides ; 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 ; inumsides ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; inumsides ; i++) + { + w = brush->sides[i].winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; inumsides && 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 ; inumsides ; 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 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 +#include +#include +#include + +#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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 [- ...]] \n" + "\n" + "Switches:\n" + " v = verbose output\n" + " threads = set number of threads to X\n" + " area = set the area light scale to V\n" + " point = 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 = 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= 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 ; iverts; + + 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; i++) + { + VectorAdd (total, w->points[i], total); + } + + for (i=0 ; i<3 ; i++) + total[i] /= w->numpoints; + + bestr = 0; + for (i=0 ; inumpoints ; 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 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 ; jpoints[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 ; jnumpoints ; 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 [- ...]] \n" + "\n" + "Switches:\n" + " v = verbose output\n" + " threads = set number of threads to X\n" + " area = set the area light scale to V\n" + " point = 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 = 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 ; ihash_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 ; inumsides ; 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 ; inumsides ; i++) + { + s = buildBrush->sides + i; + w = s->winding; + if (!w) + continue; + for (j=0 ; jnumpoints ; 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 ; lnumpoints ; 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 ; inumsides ; 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 ; inumsides ; 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 ; inumpoints ; 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 ; iwinding->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 ; ioutside_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 ; inumpoints ; 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 ; inumpoints ; 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 +#include +#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 + if ( !Q_stricmp( token, "qer_editorimage" ) ) { + GetToken( qfalse ); + strcpy( si->editorimage, token ); + DefaultExtension( si->editorimage, ".tga" ); + continue; + } + + // q3map_lightimage + if ( !Q_stricmp( token, "q3map_lightimage" ) ) { + GetToken( qfalse ); + strcpy( si->lightimage, token ); + DefaultExtension( si->lightimage, ".tga" ); + continue; + } + + // q3map_surfacelight + if ( !Q_stricmp( token, "q3map_surfacelight" ) ) { + GetToken( qfalse ); + si->value = atoi( token ); + continue; + } + + // q3map_lightsubdivide + if ( !Q_stricmp( token, "q3map_lightsubdivide" ) ) { + GetToken( qfalse ); + si->lightSubdivide = atoi( token ); + continue; + } + + // q3map_lightmapsamplesize + 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 + if ( !Q_stricmp( token, "q3map_backsplash" ) ) { + GetToken( qfalse ); + si->backsplashFraction = atof( token ) * 0.01; + GetToken( qfalse ); + si->backsplashDistance = atof( token ); + continue; + } + + // q3map_backshader + if ( !Q_stricmp( token, "q3map_backshader" ) ) { + GetToken( qfalse ); + strcpy( si->backShader, token ); + continue; + } + + // q3map_flare + if ( !Q_stricmp( token, "q3map_flare" ) ) { + GetToken( qfalse ); + strcpy( si->flareShader, token ); + continue; + } + + // light + // old style flare specification + if ( !Q_stricmp( token, "light" ) ) { + GetToken( qfalse ); + strcpy( si->flareShader, "flareshader" ); + continue; + } + + // q3map_sun + // 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; i++) + { + VectorAdd (total, w->points[i], total); + } + + for (i=0 ; i<3 ; i++) + total[i] /= w->numpoints; + + bestr = 0; + for (i=0 ; inumpoints ; 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 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 ; jpoints[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 ; jnumpoints ; 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 [- ...]] \n" + "\n" + "Switches:\n" + " v = verbose output\n" + " threads = set number of threads to X\n" + " area = set the area light scale to V\n" + " point = 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 = 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 + +#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 ; iplanenum == 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 ; inumportals ; 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>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 ; jportalvis)[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 ; iwinding; + VectorCopy (vec3_origin, total); + for (i=0 ; inumpoints ; i++) + { + VectorAdd (total, w->points[i], total); + } + + for (i=0 ; i<3 ; i++) + total[i] /= w->numpoints; + + bestr = 0; + for (i=0 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 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 ; jpoints[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 ; jnumpoints ; 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 ; jpoints[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) + Error ("Bad bit in PVS"); // pad bits should be 0 + src = (long *)(visBytes + index*leafbytes); + dest = (long *)uncompressed; + for (l=0 ; l>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 ; iportalmightsee[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>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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; jnumpoints ; 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 ; knumpoints ; 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 ; knumpoints ; 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 ; jbase->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 ; iportalflood)[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 ; iportalflood)[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 ; iportalflood)[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 ; inumpoints ; 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 ; inumpoints ; 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 ; inumpoints ; 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 ; jnumpoints ; 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 ; knumpoints ; 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 ; knumpoints ; 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 ; inumportals ; 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 ; jremoved) + 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 ; knumpoints ; 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 ; knumpoints ; 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 ; inumportals ; 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 ; jportalflood)[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 ; inormal, 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 ; inext ) { + 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 ; jnumsides ; 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++; +} + -- cgit v1.2.3