From 6bf20c78f5b69d40bcc4931df93d29198435ab67 Mon Sep 17 00:00:00 2001 From: zakk Date: Fri, 26 Aug 2005 17:39:27 +0000 Subject: newlines fixed git-svn-id: svn://svn.icculus.org/quake3/trunk@6 edf5b092-35ff-0310-97b2-ce42778d08ea --- q3map/brush.c | 1680 ++++---- q3map/brush_primit.c | 62 +- q3map/bsp.c | 1168 ++--- q3map/facebsp.c | 716 ++-- q3map/fog.c | 1066 ++--- q3map/gldraw.c | 422 +- q3map/glfile.c | 254 +- q3map/leakfile.c | 158 +- q3map/light.c | 4256 +++++++++--------- q3map/light.h | 260 +- q3map/light_trace.c | 1846 ++++---- q3map/lightmaps.c | 748 ++-- q3map/lightv.c | 11452 ++++++++++++++++++++++++------------------------- q3map/makefile | 296 +- q3map/map.c | 2460 +++++------ q3map/mesh.c | 1322 +++--- q3map/mesh.h | 54 +- q3map/misc_model.c | 902 ++-- q3map/nodraw.c | 52 +- q3map/patch.c | 530 +-- q3map/portals.c | 1644 +++---- q3map/prtfile.c | 502 +-- q3map/q3map.sln | 112 +- q3map/q3map.vcproj | 3212 +++++++------- q3map/qbsp.h | 868 ++-- q3map/shaders.c | 1174 ++--- q3map/shaders.h | 100 +- q3map/soundv.c | 11442 ++++++++++++++++++++++++------------------------ q3map/surface.c | 2274 +++++----- q3map/terrain.c | 2468 +++++------ q3map/tjunction.c | 1060 ++--- q3map/tree.c | 250 +- q3map/vis.c | 2352 +++++----- q3map/vis.h | 282 +- q3map/visflow.c | 3272 +++++++------- q3map/writebsp.c | 794 ++-- 36 files changed, 30755 insertions(+), 30755 deletions(-) (limited to 'q3map') diff --git a/q3map/brush.c b/q3map/brush.c index e373e17..506bd1d 100755 --- a/q3map/brush.c +++ b/q3map/brush.c @@ -19,843 +19,843 @@ 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]; -} +#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 index 5ec44cc..198e90e 100755 --- a/q3map/brush_primit.c +++ b/q3map/brush_primit.c @@ -19,34 +19,34 @@ 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); -} +#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 index 12d66aa..2cf15bd 100755 --- a/q3map/bsp.c +++ b/q3map/bsp.c @@ -19,587 +19,587 @@ 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; -} - +#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 index d1b00b4..a146a21 100755 --- a/q3map/facebsp.c +++ b/q3map/facebsp.c @@ -19,361 +19,361 @@ 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; -} - + +#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 index cf4060a..fa938dd 100755 --- a/q3map/fog.c +++ b/q3map/fog.c @@ -19,536 +19,536 @@ 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 ); -} +#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 index bf2ceba..80620a4 100755 --- a/q3map/gldraw.c +++ b/q3map/gldraw.c @@ -19,214 +19,214 @@ 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; -} + +#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 index 8bb10c6..b00df75 100755 --- a/q3map/glfile.c +++ b/q3map/glfile.c @@ -19,130 +19,130 @@ 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); -} - + +#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 index 101e32d..683c22a 100755 --- a/q3map/leakfile.c +++ b/q3map/leakfile.c @@ -19,82 +19,82 @@ 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); -} - + +#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 index a3d368b..1563d58 100755 --- a/q3map/light.c +++ b/q3map/light.c @@ -19,2131 +19,2131 @@ 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; -} - +// 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 index 5faa24a..ada2763 100755 --- a/q3map/light.h +++ b/q3map/light.h @@ -19,133 +19,133 @@ 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; - - + +#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 index f8a51e7..2c78575 100755 --- a/q3map/light_trace.c +++ b/q3map/light_trace.c @@ -19,926 +19,926 @@ 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; - } -} - +#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 index 95064e5..ce1fc12 100755 --- a/q3map/lightmaps.c +++ b/q3map/lightmaps.c @@ -19,377 +19,377 @@ 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 ); -} - - - +#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 index dddecb7..3e9617b 100755 --- a/q3map/lightv.c +++ b/q3map/lightv.c @@ -20,5729 +20,5729 @@ 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; -} + +#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 index 15c6a0b..ff8d6c1 100755 --- a/q3map/makefile +++ b/q3map/makefile @@ -1,148 +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 - - + +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 index e275e34..3de6ede 100755 --- a/q3map/map.c +++ b/q3map/map.c @@ -19,1233 +19,1233 @@ 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"); -} +// 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 index c49c76b..e2b3010 100755 --- a/q3map/mesh.c +++ b/q3map/mesh.c @@ -19,664 +19,664 @@ 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); -} + +#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 index 06f5684..ee338d4 100755 --- a/q3map/mesh.h +++ b/q3map/mesh.h @@ -19,30 +19,30 @@ 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); + +// 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 index c83e7e7..cd003d6 100755 --- a/q3map/misc_model.c +++ b/q3map/misc_model.c @@ -19,454 +19,454 @@ 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 ); -} - + +#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 index 9931f27..7503a48 100755 --- a/q3map/nodraw.c +++ b/q3map/nodraw.c @@ -19,29 +19,29 @@ 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) -{ -} + +#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 index 5dbd354..df461d6 100755 --- a/q3map/patch.c +++ b/q3map/patch.c @@ -19,268 +19,268 @@ 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 ); -} - +#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 index dee708f..059760f 100755 --- a/q3map/portals.c +++ b/q3map/portals.c @@ -19,825 +19,825 @@ 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); -} - - -//============================================================== - + +#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 index 902ae05..6a7bb15 100755 --- a/q3map/prtfile.c +++ b/q3map/prtfile.c @@ -19,254 +19,254 @@ 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); -} - + +#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 index 6e4bb3c..e0b2699 100755 --- a/q3map/q3map.sln +++ b/q3map/q3map.sln @@ -1,56 +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 +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 index 64ad4f8..6fa6606 100755 --- a/q3map/q3map.vcproj +++ b/q3map/q3map.vcproj @@ -1,1606 +1,1606 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/q3map/qbsp.h b/q3map/qbsp.h index 0890713..7c7cfba 100755 --- a/q3map/qbsp.h +++ b/q3map/qbsp.h @@ -19,437 +19,437 @@ 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); + +#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 index 1efc513..4adfcf0 100755 --- a/q3map/shaders.c +++ b/q3map/shaders.c @@ -19,590 +19,590 @@ 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); -} - + +#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 index fc187c6..27918bb 100755 --- a/q3map/shaders.h +++ b/q3map/shaders.h @@ -19,53 +19,53 @@ 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 ); - + + +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 index 1883c52..e7a57b6 100755 --- a/q3map/soundv.c +++ b/q3map/soundv.c @@ -19,5724 +19,5724 @@ 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; -} +/***************************************************************************** + * 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 index 462449d..75daf1c 100755 --- a/q3map/surface.c +++ b/q3map/surface.c @@ -19,1140 +19,1140 @@ 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 ); -} - - - + +#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 index 9446737..129ac08 100755 --- a/q3map/terrain.c +++ b/q3map/terrain.c @@ -19,1237 +19,1237 @@ 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 ); -} - +#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 index 9d41dc2..a1acabe 100755 --- a/q3map/tjunction.c +++ b/q3map/tjunction.c @@ -19,533 +19,533 @@ 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 ); -} +#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 index 4addea6..f400db1 100755 --- a/q3map/tree.c +++ b/q3map/tree.c @@ -19,128 +19,128 @@ 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); -} +#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 index 6baab38..329d192 100755 --- a/q3map/vis.c +++ b/q3map/vis.c @@ -19,1179 +19,1179 @@ 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 ; i>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; -} - - +#include "vis.h" + +/* + + each portal will have a list of all possible to see from first portal + + if (!thread->portalmightsee[portalnum]) + + portal mightsee + + for p2 = all other portals in leaf + get sperating planes + for all portals that might be seen by p2 + mark as unseen if not present in seperating plane + flood fill a new mightsee + save as passagemightsee + + + void CalcMightSee (leaf_t *leaf, +*/ + +int CountBits (byte *bits, int numbits) +{ + int i; + int c; + + c = 0; + for (i=0 ; i>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 index 7a56d88..7b9e3ff 100755 --- a/q3map/writebsp.c +++ b/q3map/writebsp.c @@ -19,400 +19,400 @@ 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++; -} - +#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