Module hmap2: Change committed
havoc at icculus.org
havoc at icculus.org
Sun Feb 15 21:50:48 EST 2004
Commiter : havoc
CVSROOT : /cvs/cvsroot/twilight
Module : hmap2
Commit time: 2004-02-16 02:50:47 UTC
Log message:
Vic's greatly improved hmap, now called hmap2 (this is hqbsp/hvis/hlight/bsp2prt with tons of bugfixes and improvements, combined into one program)
Added files:
Makefile brush.c bsp2prt.c bsp5.h bspfile.c bspfile.h cmdlib.c
cmdlib.h csg4.c faces.c fast.bat full.bat hmap2.dsp
hmapreadme.txt light.c light.h light_face.c light_trace.c map.c
map.h mathlib.c mathlib.h mem.c mem.h outside.c portals.c qbsp.c
relight.bat revis.bat scriptlib.c solidbsp.c tjunc.c tree.c vis.c
vis.h vis_flow.c vis_sound.c wad.c winding.c winding.h writebsp.c
------=MIME.bae42047cce5199d623e0cd9135a8d56
Content-Type: text/plain; name="hmap2.20040216.025047.havoc.diff"
Content-Disposition: attachment; filename="hmap2.20040216.025047.havoc.diff"
Content-Transfer-Encoding: 8bit
Index: hmap2/Makefile
diff -u /dev/null hmap2/Makefile:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/Makefile Sun Feb 15 21:50:37 2004
@@ -0,0 +1,53 @@
+##### Win32 variables #####
+
+WIN32_HMAP_EXE=hmap.exe
+WIN32_LDFLAGS=-lwinmm
+
+##### Unix variables #####
+
+UNIX_HMAP_EXE=hmap
+UNIX_LDFLAGS=-lm
+
+##### Common variables #####
+
+HMAPOBJECTS= cmdlib.o mathlib.o bspfile.o mem.o winding.o scriptlib.o \
+ brush.o csg4.o faces.o map.o outside.o portals.o \
+ qbsp.o solidbsp.o tjunc.o tree.o writebsp.o \
+ wad.o bsp2prt.o vis.o vis_flow.o vis_sound.o light.o \
+ light_face.o light_trace.o
+
+CC=gcc
+CFLAGS=-Wall -O2
+
+##### Commands #####
+
+.PHONY: all mingw clean
+
+all:
+ifdef windir
+ $(MAKE) HMAP_EXE=$(WIN32_HMAP_EXE) LDFLAGS="$(WIN32_LDFLAGS)" $(WIN32_HMAP_EXE)
+else
+ $(MAKE) HMAP_EXE=$(UNIX_HMAP_EXE) LDFLAGS="$(UNIX_LDFLAGS)" $(UNIX_HMAP_EXE)
+endif
+
+mingw:
+ $(MAKE) HMAP_EXE=$(WIN32_HMAP_EXE) LDFLAGS="$(WIN32_LDFLAGS)" $(WIN32_HMAP_EXE)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $*.c -o $*.o
+
+$(HMAP_EXE): $(HMAPOBJECTS)
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+clean:
+ifdef windir
+ del $(WIN32_HMAP_EXE)
+ del *.o
+ del *.d
+else
+ rm -f $(WIN32_HMAP_EXE) $(UNIX_HMAP_EXE) *.[od]
+endif
+
+.PHONY: clean
+
+-include *.d
Index: hmap2/brush.c
diff -u /dev/null hmap2/brush.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/brush.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,585 @@
+// brush.c
+
+#include "bsp5.h"
+
+int numbrushfaces;
+mface_t faces[MAX_FACES]; // beveled clipping hull can generate many extra
+
+entity_t *CurrentEntity;
+
+//============================================================================
+
+/*
+===========
+AllocBrush
+===========
+*/
+brush_t *AllocBrush( void )
+{
+ brush_t *b;
+
+ b = qmalloc( sizeof( brush_t ) );
+ memset( b, 0, sizeof( brush_t ) );
+
+ return b;
+}
+
+/*
+===========
+FreeBrush
+===========
+*/
+void FreeBrush( brush_t *b ) {
+ qfree( b );
+}
+
+//============================================================================
+
+/*
+=============================================================================
+
+TURN BRUSHES INTO GROUPS OF FACES
+
+=============================================================================
+*/
+
+vec3_t brush_mins, brush_maxs;
+face_t *brush_faces;
+
+entity_t *FindTargetEntity(char *targetname)
+{
+ int entnum;
+
+ for (entnum = 0;entnum < num_entities;entnum++)
+ if (!strcmp(targetname, ValueForKey(&entities[entnum], "targetname")))
+ return &entities[entnum];
+ return NULL;
+}
+
+/*
+=================
+CreateBrushFaces
+=================
+*/
+#define ZERO_EPSILON 0.001
+void CreateBrushFaces (void)
+{
+ int i,j, k, rotate;
+ vec_t r;
+ face_t *f;
+ winding_t *w;
+ plane_t *p, plane;
+ mface_t *mf;
+ vec3_t offset, point;
+
+ offset[0] = offset[1] = offset[2] = 0;
+ brush_mins[0] = brush_mins[1] = brush_mins[2] = BOGUS_RANGE;
+ brush_maxs[0] = brush_maxs[1] = brush_maxs[2] = -BOGUS_RANGE;
+
+ brush_faces = NULL;
+
+ rotate = !strncmp(ValueForKey(CurrentEntity, "classname"), "rotate_", 7);
+ if (rotate)
+ {
+ entity_t *FoundEntity;
+ char *searchstring;
+ char text[20];
+
+ searchstring = ValueForKey (CurrentEntity, "target");
+ FoundEntity = FindTargetEntity(searchstring);
+ if (FoundEntity)
+ GetVectorForKey(FoundEntity, "origin", offset);
+
+ sprintf(text, "%g %g %g", offset[0], offset[1], offset[2]);
+ SetKeyValue(CurrentEntity, "origin", text);
+ }
+
+ for (i = 0;i < numbrushfaces;i++)
+ {
+ mf = &faces[i];
+ VectorNegate( mf->plane.normal, point );
+
+ w = BaseWindingForPlane (&mf->plane);
+
+ for (j = 0;j < numbrushfaces && w;j++)
+ {
+ p = &faces[i].plane;
+ if( j == i/* || VectorCompare( p->normal, point )*/ )
+ continue;
+
+ // flip the plane, because we want to keep the back side
+ VectorNegate(faces[j].plane.normal, plane.normal);
+ plane.dist = -faces[j].plane.dist;
+
+ w = ClipWindingEpsilon (w, &plane, true, ON_EPSILON);
+ }
+
+ if (!w)
+ continue; // overcontrained plane
+
+ CheckWinding( w );
+
+ // this face is a keeper
+ f = AllocFace ();
+ f->winding = w;
+
+ for (j = 0;j < w->numpoints;j++)
+ {
+ for (k = 0;k < 3;k++)
+ {
+ point[k] = w->points[j][k] - offset[k];
+ r = Q_rint( point[k] );
+ if ( fabs( point[k] - r ) < ZERO_EPSILON)
+ w->points[j][k] = r;
+ else
+ w->points[j][k] = point[k];
+
+ AddPointToBounds( w->points[j], brush_mins, brush_maxs );
+ }
+ }
+
+ VectorCopy (mf->plane.normal, plane.normal);
+ VectorScale (mf->plane.normal, mf->plane.dist, point);
+ VectorSubtract (point, offset, point);
+ plane.dist = DotProduct (plane.normal, point);
+
+ f->texturenum = mf->texinfo;
+ f->planenum = FindPlane (&plane, &f->planeside);
+ f->next = brush_faces;
+ brush_faces = f;
+ }
+
+ // Rotatable objects have to have a bounding box big enough
+ // to account for all its rotations.
+ if (rotate)
+ {
+ vec_t delta;
+
+ delta = RadiusFromBounds( brush_mins, brush_maxs );
+
+ for (k = 0;k < 3;k++)
+ {
+ brush_mins[k] = -delta;
+ brush_maxs[k] = delta;
+ }
+ }
+}
+
+
+
+/*
+==============================================================================
+
+BEVELED CLIPPING HULL GENERATION
+
+This is done by brute force, and could easily get a lot faster if anyone cares.
+==============================================================================
+*/
+
+vec3_t hull_size[3][2] =
+{
+{{ 0, 0, 0}, { 0, 0, 0}},
+{{-16,-16,-32}, { 16, 16, 24}},
+{{-32,-32,-64}, { 32, 32, 24}}
+};
+
+// LordHavoc: these were 32 and 64 respectively
+#define MAX_HULL_POINTS 512
+#define MAX_HULL_EDGES 1024
+
+int num_hull_points;
+vec3_t hull_points[MAX_HULL_POINTS];
+vec3_t hull_corners[MAX_HULL_POINTS*8];
+int num_hull_edges;
+int hull_edges[MAX_HULL_EDGES][2];
+
+/*
+============
+AddBrushPlane
+=============
+*/
+void AddBrushPlane (plane_t *plane)
+{
+ int i;
+ plane_t *pl;
+ double l;
+
+ if (numbrushfaces == MAX_FACES)
+ Error ("AddBrushPlane: numbrushfaces == MAX_FACES");
+ l = VectorLength (plane->normal);
+ if (l < 0.999 || l > 1.001)
+ Error ("AddBrushPlane: bad normal (%f %f %f, length %f)", plane->normal[0], plane->normal[1], plane->normal[2], l);
+
+ for (i=0 ; i<numbrushfaces ; i++)
+ {
+ pl = &faces[i].plane;
+ if (VectorCompare (pl->normal, plane->normal) && fabs(pl->dist - plane->dist) < ON_EPSILON)
+ return;
+ }
+ faces[i].plane = *plane;
+ faces[i].texinfo = faces[0].texinfo;
+ numbrushfaces++;
+}
+
+
+/*
+============
+TestAddPlane
+
+Adds the given plane to the brush description if all of the original brush
+vertexes can be put on the front side
+=============
+*/
+void TestAddPlane (plane_t *plane)
+{
+ int i, c;
+ vec_t d;
+ vec_t *corner;
+ plane_t flip;
+ vec3_t inv;
+ int counts[3];
+ plane_t *pl;
+
+ // see if the plane has allready been added
+ for (i=0 ; i<numbrushfaces ; i++)
+ {
+ pl = &faces[i].plane;
+ if (VectorCompare (plane->normal, pl->normal) && fabs(plane->dist - pl->dist) < ON_EPSILON)
+ return;
+ VectorNegate (plane->normal, inv);
+ if (VectorCompare (inv, pl->normal) && fabs(plane->dist + pl->dist) < ON_EPSILON)
+ return;
+ }
+
+ // check all the corner points
+ counts[0] = counts[1] = counts[2] = 0;
+ c = num_hull_points * 8;
+
+ corner = hull_corners[0];
+ for (i=0 ; i<c ; i++, corner += 3)
+ {
+ d = DotProduct (corner, plane->normal) - plane->dist;
+ if (d < -ON_EPSILON)
+ {
+ if (counts[0])
+ return;
+ counts[1]++;
+ }
+ else if (d > ON_EPSILON)
+ {
+ if (counts[1])
+ return;
+ counts[0]++;
+ }
+ else
+ counts[2]++;
+ }
+
+ // the plane is a seperator
+
+ if (counts[0])
+ {
+ VectorNegate (plane->normal, flip.normal);
+ flip.dist = -plane->dist;
+ plane = &flip;
+ }
+
+ AddBrushPlane (plane);
+}
+
+/*
+============
+AddHullPoint
+
+Doesn't add if duplicated
+=============
+*/
+int AddHullPoint (vec3_t p, int hullnum)
+{
+ int i;
+ vec_t *c;
+ int x,y,z;
+
+ for (i=0 ; i<num_hull_points ; i++)
+ if (VectorCompare (p, hull_points[i]))
+ return i;
+
+ VectorCopy (p, hull_points[num_hull_points]);
+
+ c = hull_corners[i*8];
+
+ for (x=0 ; x<2 ; x++)
+ for (y=0 ; y<2 ; y++)
+ for (z=0; z<2 ; z++)
+ {
+ c[0] = p[0] + hull_size[hullnum][x][0];
+ c[1] = p[1] + hull_size[hullnum][y][1];
+ c[2] = p[2] + hull_size[hullnum][z][2];
+ c += 3;
+ }
+
+ if (num_hull_points == MAX_HULL_POINTS)
+ Error ("AddHullPoint: MAX_HULL_POINTS");
+
+ num_hull_points++;
+
+ return i;
+}
+
+
+/*
+============
+AddHullEdge
+
+Creates all of the hull planes around the given edge, if not done allready
+=============
+*/
+void AddHullEdge (vec3_t p1, vec3_t p2, int hullnum)
+{
+ int pt1, pt2;
+ int i;
+ int a, b, c, d, e;
+ vec3_t edgevec, planeorg, planevec;
+ plane_t plane;
+ vec_t l;
+
+ pt1 = AddHullPoint (p1, hullnum);
+ pt2 = AddHullPoint (p2, hullnum);
+
+ for (i=0 ; i<num_hull_edges ; i++)
+ if ((hull_edges[i][0] == pt1 && hull_edges[i][1] == pt2) || (hull_edges[i][0] == pt2 && hull_edges[i][1] == pt1))
+ return; // already added
+
+ if (num_hull_edges == MAX_HULL_EDGES)
+ Error ("AddHullEdge: MAX_HULL_EDGES");
+
+ hull_edges[i][0] = pt1;
+ hull_edges[i][1] = pt2;
+ num_hull_edges++;
+
+ VectorSubtract (p1, p2, edgevec);
+ VectorNormalize (edgevec);
+
+ for (a=0 ; a<3 ; a++)
+ {
+ b = (a+1)%3;
+ c = (a+2)%3;
+ for (d=0 ; d<=1 ; d++)
+ for (e=0 ; e<=1 ; e++)
+ {
+ VectorCopy (p1, planeorg);
+ planeorg[b] += hull_size[hullnum][d][b];
+ planeorg[c] += hull_size[hullnum][e][c];
+
+ VectorClear (planevec);
+ planevec[a] = 1;
+
+ CrossProduct (planevec, edgevec, plane.normal);
+ l = VectorLength (plane.normal);
+ if (l < 1-ANGLEEPSILON || l > 1+ANGLEEPSILON)
+ continue;
+ plane.dist = DotProduct (planeorg, plane.normal);
+ TestAddPlane (&plane);
+ }
+ }
+}
+
+/*
+============
+ExpandBrush
+=============
+*/
+void ExpandBrush (int hullnum)
+{
+ int i, x, s;
+ vec3_t corner;
+ face_t *f;
+ winding_t *w;
+ plane_t plane, *p;
+
+ num_hull_points = 0;
+ num_hull_edges = 0;
+
+ // create all the hull points
+ for (f=brush_faces ; f ; f=f->next) {
+ w = f->winding;
+ for (i=0 ; i<w->numpoints ; i++)
+ AddHullPoint (w->points[i], hullnum);
+ }
+
+ // expand all of the planes
+ for (i=0 ; i<numbrushfaces ; i++)
+ {
+ p = &faces[i].plane;
+ VectorClear (corner);
+ for (x=0 ; x<3 ; x++)
+ {
+ if (p->normal[x] > 0)
+ corner[x] = hull_size[hullnum][1][x];
+ else if (p->normal[x] < 0)
+ corner[x] = hull_size[hullnum][0][x];
+ }
+ p->dist += DotProduct (corner, p->normal);
+ }
+
+ // add any axis planes not contained in the brush to bevel off corners
+ for (x=0 ; x<3 ; x++)
+ for (s=-1 ; s<=1 ; s+=2)
+ {
+ // add the plane
+ VectorClear (plane.normal);
+ plane.normal[x] = s;
+ if (s == -1)
+ plane.dist = -brush_mins[x] + -hull_size[hullnum][0][x];
+ else
+ plane.dist = brush_maxs[x] + hull_size[hullnum][1][x];
+ AddBrushPlane (&plane);
+ }
+
+ // add all of the edge bevels
+ for (f=brush_faces ; f ; f=f->next) {
+ w = f->winding;
+ for (i=0 ; i<w->numpoints ; i++)
+ AddHullEdge (w->points[i], w->points[(i+1)%w->numpoints], hullnum);
+ }
+}
+
+//============================================================================
+
+
+/*
+===============
+LoadBrush
+
+Converts a mapbrush to a bsp brush
+===============
+*/
+brush_t *LoadBrush (mbrush_t *mb, int hullnum)
+{
+ brush_t *b;
+ int contents;
+ char *name;
+ mface_t *f;
+
+ //
+ // check texture name for attributes
+ //
+ name = miptex[texinfo[mb->faces->texinfo].miptex];
+
+ if (!Q_strcasecmp(name, "clip") && hullnum == 0)
+ return NULL; // "clip" brushes don't show up in the draw hull
+
+ if (name[0] == '*') // entities never use water merging
+ {
+ if (!Q_strncasecmp(name+1,"lava",4))
+ contents = CONTENTS_LAVA;
+ else if (!Q_strncasecmp(name+1,"slime",5))
+ contents = CONTENTS_SLIME;
+ else
+ contents = CONTENTS_WATER;
+ }
+ else if (!Q_strncasecmp (name, "sky",3) && hullnum == 0)
+ contents = CONTENTS_SKY;
+ else
+ contents = CONTENTS_SOLID;
+
+ if (hullnum && contents != CONTENTS_SOLID && contents != CONTENTS_SKY)
+ return NULL; // water brushes don't show up in clipping hulls
+
+ // no seperate textures on clip hull
+
+ //
+ // create the faces
+ //
+ brush_faces = NULL;
+
+ numbrushfaces = 0;
+ for (f=mb->faces ; f ; f=f->next)
+ {
+ faces[numbrushfaces] = *f;
+ if (hullnum)
+ faces[numbrushfaces].texinfo = 0;
+ numbrushfaces++;
+ }
+
+ CreateBrushFaces ();
+
+ if (!brush_faces)
+ {
+ printf ("WARNING: couldn't create brush faces\n");
+ return NULL;
+ }
+
+ if (hullnum)
+ {
+ ExpandBrush (hullnum);
+ CreateBrushFaces ();
+ }
+
+ //
+ // create the brush
+ //
+ b = AllocBrush ();
+ b->contents = contents;
+ b->faces = brush_faces;
+ VectorCopy (brush_mins, b->mins);
+ VectorCopy (brush_maxs, b->maxs);
+
+ return b;
+}
+
+//=============================================================================
+
+/*
+============
+Brush_LoadEntity
+============
+*/
+void Brush_LoadEntity( entity_t *ent, tree_t *tree, int hullnum )
+{
+ mbrush_t *mbr;
+ int numbrushes;
+ brush_t *b, *next, *water, *other;
+
+ numbrushes = 0;
+ other = water = NULL;
+
+ qprintf ("--- Brush_LoadEntity ---\n");
+
+ CurrentEntity = ent;
+
+ for (mbr = ent->brushes ; mbr ; mbr=mbr->next)
+ {
+ b = LoadBrush (mbr, hullnum);
+ if (!b)
+ continue;
+
+ numbrushes++;
+
+ if (b->contents != CONTENTS_SOLID)
+ {
+ b->next = water;
+ water = b;
+ }
+ else
+ {
+ b->next = other;
+ other = b;
+ }
+
+ AddPointToBounds (b->mins, tree->mins, tree->maxs);
+ AddPointToBounds (b->maxs, tree->mins, tree->maxs);
+ }
+
+ // add all of the water textures at the start
+ for (b=water ; b ; b=next)
+ {
+ next = b->next;
+ b->next = other;
+ other = b;
+ }
+
+ tree->brushes = other;
+
+ qprintf( "%i brushes read\n", numbrushes );
+}
Index: hmap2/bsp2prt.c
diff -u /dev/null hmap2/bsp2prt.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/bsp2prt.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,133 @@
+#include "bsp5.h"
+
+/*
+==================
+Bsp2Prt_BuildTree_r
+==================
+*/
+static node_t *Bsp2Prt_BuildTree_r( int nodenum )
+{
+ node_t *n;
+
+ if( nodenum < 0 ) {
+ dleaf_t *leaf = &dleafs[-1-nodenum];
+
+ n = AllocNode ();
+ n->planenum = PLANENUM_LEAF;
+ n->contents = leaf->contents;
+ } else {
+ int side;
+ plane_t plane;
+ dnode_t *node = &dnodes[nodenum];
+
+ n = AllocNode ();
+ plane.dist = dplanes[node->planenum].dist;
+ VectorCopy( dplanes[node->planenum].normal, plane.normal );
+ n->planenum = FindPlane( &plane, &side );
+
+ if( side )
+ Error( "Bad node plane" );
+
+ n->children[0] = Bsp2Prt_BuildTree_r( node->children[0] );
+ n->children[1] = Bsp2Prt_BuildTree_r( node->children[1] );
+ }
+
+ return n;
+}
+
+/*
+==================
+Bsp2Prt_GetWorldBounds
+==================
+*/
+static void Bsp2Prt_GetWorldBounds( vec3_t mins, vec3_t maxs )
+{
+ int i, j, e;
+ dface_t *face;
+ dvertex_t *v;
+ vec3_t point;
+
+ ClearBounds( mins, maxs );
+
+ for( i = 0, face = dfaces; i < dmodels[0].numfaces; i++, face++ ) {
+ for( j = 0; j < face->numedges; j++ ) {
+ e = dsurfedges[face->firstedge + j];
+ if( e >= 0 )
+ v = dvertexes + dedges[e].v[0];
+ else
+ v = dvertexes + dedges[-e].v[1];
+
+ VectorCopy( v->point, point );
+ AddPointToBounds( point, mins, maxs );
+ }
+ }
+}
+
+/*
+==================
+Bsp2Prt_ProcessFile
+==================
+*/
+static void Bsp2Prt_ProcessFile( char *filename )
+{
+ tree_t *tree;
+
+ // create filenames
+ strcpy( bspfilename, filename );
+ strcpy( portfilename, filename );
+ DefaultExtension( bspfilename, ".bsp" );
+ ReplaceExtension( portfilename, ".prt" );
+
+ LoadBSPFile( bspfilename );
+
+ tree = AllocTree ();
+
+ Bsp2Prt_GetWorldBounds( tree->mins, tree->maxs );
+
+ tree->headnode = Bsp2Prt_BuildTree_r( 0 );
+
+ PortalizeTree( tree );
+ WritePortalfile( tree );
+ FreeTreePortals( tree );
+
+ FreeTree( tree );
+}
+
+/*
+==================
+Bsp2Prt_Main
+==================
+*/
+int Bsp2Prt_Main( int argc, char **argv )
+{
+ double start, end;
+
+ if( argc < 2 ) {
+ Error ("%s",
+"usage: bsp2prt sourcefile\n"
+ );
+ }
+
+ // init memory
+ Q_InitMem ();
+
+ // do it!
+ start = I_DoubleTime ();
+
+ end = I_DoubleTime ();
+ Bsp2Prt_ProcessFile( argv[argc-1] );
+ printf( "%5.2f seconds elapsed\n\n", end - start );
+
+ // print memory stats
+ Q_PrintMem ();
+
+#if _MSC_VER && _DEBUG
+ printf( "press any key\n" );
+ getchar ();
+#endif
+
+ // free allocated memory
+ Q_ShutdownMem ();
+
+ return 0;
+}
Index: hmap2/bsp5.h
diff -u /dev/null hmap2/bsp5.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/bsp5.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,213 @@
+
+// bsp5.h
+
+#include <math.h>
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "mem.h"
+#include "winding.h"
+#include "map.h"
+
+#define ON_EPSILON 0.05
+
+// the exact bounding box of the brushes is expanded some for the headnode
+// volume. is this still needed?
+#define SIDESPACE 24
+
+//============================================================================
+
+typedef struct visfacet_s
+{
+ struct visfacet_s *next;
+
+ int planenum;
+ int planeside; // which side is the front of the face
+ int texturenum;
+ int contents[2]; // 0 = front side
+
+ struct visfacet_s *original; // face on node
+ int outputnumber; // only valid for original faces after write surfaces
+ winding_t *winding;
+} face_t;
+
+typedef struct surface_s
+{
+ struct surface_s *next;
+
+ int planenum;
+ vec3_t mins, maxs;
+ qboolean onnode; // true if surface has already been used as a splitting node
+ face_t *faces; // links to all the faces on either side of the surf
+} surface_t;
+
+typedef struct brush_s
+{
+ struct brush_s *next;
+
+ vec3_t mins, maxs;
+ face_t *faces;
+ int contents;
+} brush_t;
+
+//
+// there is a node_t structure for every node and leaf in the bsp tree
+//
+#define PLANENUM_LEAF -1
+
+typedef struct node_s
+{
+ vec3_t mins, maxs; // bounding volume, not just points inside
+ int planenum; // PLANENUM_LEAF = leaf node
+
+ // information for decision nodes
+ int outputplanenum; // only valid after EmitNodePlanes
+ int firstface; // decision node only
+ int numfaces; // decision node only
+ struct node_s *children[2]; // only valid for decision nodes
+ face_t *faces; // decision nodes only, list for both sides
+
+ // information for leafs
+ int contents; // leaf nodes (0 for decision nodes)
+ face_t **markfaces; // leaf nodes only, point to node faces
+ struct portal_s *portals;
+ int visleafnum; // -1 = solid
+ int valid; // for flood filling
+ int occupied; // light number in leaf for outside filling
+} node_t;
+
+typedef struct portal_s
+{
+ int planenum;
+ node_t *nodes[2]; // [0] = front side of planenum
+ struct portal_s *next[2];
+ winding_t *winding;
+} portal_t;
+
+typedef struct
+{
+ vec3_t mins, maxs; // bounding box
+ brush_t *brushes; // NULL terminated list
+ face_t *validfaces[MAX_MAP_PLANES]; // build surfaces is also used by GatherNodeFaces
+ surface_t *surfaces;
+ node_t *headnode;
+} tree_t;
+
+//=============================================================================
+
+// brush.c
+brush_t *AllocBrush( void );
+void FreeBrush( brush_t *b );
+void Brush_LoadEntity( entity_t *ent, tree_t *tree, int hullnum );
+
+//=============================================================================
+
+// csg4.c
+surface_t *AllocSurface( void );
+void FreeSurface( surface_t *s );
+void BuildSurfaces( tree_t *tree );
+void CSGFaces( tree_t *tree );
+
+//=============================================================================
+
+// solidbsp.c
+node_t *AllocNode( void );
+void FreeNode( node_t *n );
+
+void CalcSurfaceInfo( surface_t *surf );
+void SolidBSP( tree_t *tree, qboolean midsplit );
+
+//=============================================================================
+
+// merge.c
+
+//=============================================================================
+
+// surfaces.c
+face_t *AllocFace( void );
+void FreeFace( face_t *f );
+face_t *NewFaceFromFace( face_t *in );
+
+void SplitFace( face_t *in, plane_t *split, face_t **front, face_t **back );
+int SubdivideFace( face_t *f, face_t **prevptr );
+
+face_t *MergeFaceToList_r( face_t *face, face_t *list );
+face_t *FreeMergeListScraps( face_t *merged );
+void MergeTreeFaces( tree_t *tree );
+
+void GatherTreeFaces( tree_t *tree );
+
+//=============================================================================
+
+// portals.c
+extern node_t outside_node; // portals outside the world face this
+
+portal_t *AllocPortal( void );
+void FreePortal( struct portal_s *p );
+
+void PortalizeTree( tree_t *tree );
+void FreeTreePortals( tree_t *tree );
+void WritePortalfile( tree_t *tree );
+
+//=============================================================================
+
+// tjunc.c
+void FixTJunctions( tree_t *tree );
+
+//=============================================================================
+
+// tree.c
+tree_t *AllocTree( void );
+void FreeTree( tree_t *t );
+tree_t *Tree_ProcessEntity( entity_t *ent, int modnum, int hullnum );
+
+//=============================================================================
+
+// writebsp.c
+void EmitVertex( vec3_t point );
+void EmitEdge( int v1, int v2 );
+void EmitNodeFaceEdges( node_t *headnode );
+void EmitNodeFaces( node_t *node );
+void EmitNodePlanes( node_t *headnode );
+void EmitClipNodes( node_t *nodes, int modnum, int hullnum );
+void EmitDrawNodes( node_t *headnode );
+
+void BeginBSPFile( void );
+void FinishBSPFile( void );
+
+//=============================================================================
+
+// outside.c
+
+qboolean FillOutside( tree_t *tree, int hullnum );
+
+//=============================================================================
+
+// wad.c
+
+void WriteMiptex (void);
+
+//=============================================================================
+
+extern qboolean nofill;
+extern qboolean notjunc;
+extern qboolean noclip;
+extern qboolean verbose;
+extern qboolean forcevis;
+extern qboolean oldbsp;
+
+extern int subdivide_size;
+
+void qprintf (char *fmt, ...);
+
+extern int valid;
+
+extern char portfilename[1024];
+extern char bspfilename[1024];
+extern char pointfilename[1024];
+
+// misc functions
+
+void qprintf( char *fmt, ... );
+
+//=============================================================================
Index: hmap2/bspfile.c
diff -u /dev/null hmap2/bspfile.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/bspfile.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,710 @@
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "mem.h"
+
+//=============================================================================
+
+int nummodels;
+dmodel_t dmodels[MAX_MAP_MODELS];
+
+int visdatasize;
+byte dvisdata[MAX_MAP_VISIBILITY];
+
+int lightdatasize;
+byte dlightdata[MAX_MAP_LIGHTING];
+
+// LordHavoc: stored in .lit file
+int rgblightdatasize;
+byte drgblightdata[MAX_MAP_LIGHTING*3];
+
+int texdatasize;
+byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
+
+int entdatasize;
+char dentdata[MAX_MAP_ENTSTRING];
+
+int numleafs;
+dleaf_t dleafs[MAX_MAP_LEAFS];
+
+int numplanes;
+dplane_t dplanes[MAX_MAP_PLANES];
+
+int numvertexes;
+dvertex_t dvertexes[MAX_MAP_VERTS];
+
+int numnodes;
+dnode_t dnodes[MAX_MAP_NODES];
+
+int numtexinfo;
+texinfo_t texinfo[MAX_MAP_TEXINFO];
+
+int numfaces;
+dface_t dfaces[MAX_MAP_FACES];
+
+int numclipnodes;
+dclipnode_t dclipnodes[MAX_MAP_CLIPNODES];
+
+int numedges;
+dedge_t dedges[MAX_MAP_EDGES];
+
+int nummarksurfaces;
+unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES];
+
+int numsurfedges;
+int dsurfedges[MAX_MAP_SURFEDGES];
+
+//=============================================================================
+
+/*
+=============
+SwapBSPFile
+
+Byte swaps all data in a bsp file.
+=============
+*/
+void SwapBSPFile (qboolean todisk)
+{
+ int i, j, c;
+ dmodel_t *d;
+ dmiptexlump_t *mtl;
+
+
+ // models
+ for (i=0 ; i<nummodels ; i++)
+ {
+ d = &dmodels[i];
+
+ for (j=0 ; j<MAX_MAP_HULLS ; j++)
+ d->headnode[j] = LittleLong (d->headnode[j]);
+
+ d->visleafs = LittleLong (d->visleafs);
+ d->firstface = LittleLong (d->firstface);
+ d->numfaces = LittleLong (d->numfaces);
+
+ for (j=0 ; j<3 ; j++)
+ {
+ d->mins[j] = LittleFloat(d->mins[j]);
+ d->maxs[j] = LittleFloat(d->maxs[j]);
+ d->origin[j] = LittleFloat(d->origin[j]);
+ }
+ }
+
+ //
+ // vertexes
+ //
+ for (i=0 ; i<numvertexes ; i++)
+ for (j=0 ; j<3 ; j++)
+ dvertexes[i].point[j] = LittleFloat (dvertexes[i].point[j]);
+
+ //
+ // planes
+ //
+ for (i=0 ; i<numplanes ; i++)
+ {
+ for (j=0 ; j<3 ; j++)
+ dplanes[i].normal[j] = LittleFloat (dplanes[i].normal[j]);
+ dplanes[i].dist = LittleFloat (dplanes[i].dist);
+ dplanes[i].type = LittleLong (dplanes[i].type);
+ }
+
+ //
+ // texinfos
+ //
+ for (i=0 ; i<numtexinfo ; i++)
+ {
+ for (j=0 ; j<8 ; j++)
+ texinfo[i].vecs[0][j] = LittleFloat (texinfo[i].vecs[0][j]);
+ texinfo[i].miptex = LittleLong (texinfo[i].miptex);
+ texinfo[i].flags = LittleLong (texinfo[i].flags);
+ }
+
+ //
+ // faces
+ //
+ for (i=0 ; i<numfaces ; i++)
+ {
+ dfaces[i].texinfo = LittleShort (dfaces[i].texinfo);
+ dfaces[i].planenum = LittleShort (dfaces[i].planenum);
+ dfaces[i].side = LittleShort (dfaces[i].side);
+ dfaces[i].lightofs = LittleLong (dfaces[i].lightofs);
+ dfaces[i].firstedge = LittleLong (dfaces[i].firstedge);
+ dfaces[i].numedges = LittleShort (dfaces[i].numedges);
+ }
+
+ //
+ // nodes
+ //
+ for (i=0 ; i<numnodes ; i++)
+ {
+ dnodes[i].planenum = LittleLong (dnodes[i].planenum);
+ for (j=0 ; j<3 ; j++)
+ {
+ dnodes[i].mins[j] = LittleShort (dnodes[i].mins[j]);
+ dnodes[i].maxs[j] = LittleShort (dnodes[i].maxs[j]);
+ }
+ dnodes[i].children[0] = LittleShort (dnodes[i].children[0]);
+ dnodes[i].children[1] = LittleShort (dnodes[i].children[1]);
+ dnodes[i].firstface = LittleShort (dnodes[i].firstface);
+ dnodes[i].numfaces = LittleShort (dnodes[i].numfaces);
+ }
+
+ //
+ // leafs
+ //
+ for (i=0 ; i<numleafs ; i++)
+ {
+ dleafs[i].contents = LittleLong (dleafs[i].contents);
+ for (j=0 ; j<3 ; j++)
+ {
+ dleafs[i].mins[j] = LittleShort (dleafs[i].mins[j]);
+ dleafs[i].maxs[j] = LittleShort (dleafs[i].maxs[j]);
+ }
+
+ dleafs[i].firstmarksurface = LittleShort (dleafs[i].firstmarksurface);
+ dleafs[i].nummarksurfaces = LittleShort (dleafs[i].nummarksurfaces);
+ dleafs[i].visofs = LittleLong (dleafs[i].visofs);
+ }
+
+ //
+ // clipnodes
+ //
+ for (i=0 ; i<numclipnodes ; i++)
+ {
+ dclipnodes[i].planenum = LittleLong (dclipnodes[i].planenum);
+ dclipnodes[i].children[0] = LittleShort (dclipnodes[i].children[0]);
+ dclipnodes[i].children[1] = LittleShort (dclipnodes[i].children[1]);
+ }
+
+ //
+ // miptex
+ //
+ if (texdatasize)
+ {
+ mtl = (dmiptexlump_t *)dtexdata;
+ if (todisk)
+ c = mtl->nummiptex;
+ else
+ c = LittleLong(mtl->nummiptex);
+ mtl->nummiptex = LittleLong (mtl->nummiptex);
+ for (i=0 ; i<c ; i++)
+ mtl->dataofs[i] = LittleLong(mtl->dataofs[i]);
+ }
+
+ //
+ // marksurfaces
+ //
+ for (i=0 ; i<nummarksurfaces ; i++)
+ dmarksurfaces[i] = LittleShort (dmarksurfaces[i]);
+
+ //
+ // surfedges
+ //
+ for (i=0 ; i<numsurfedges ; i++)
+ dsurfedges[i] = LittleLong (dsurfedges[i]);
+
+ //
+ // edges
+ //
+ for (i=0 ; i<numedges ; i++)
+ {
+ dedges[i].v[0] = LittleShort (dedges[i].v[0]);
+ dedges[i].v[1] = LittleShort (dedges[i].v[1]);
+ }
+}
+
+
+dheader_t *header;
+
+int CopyLump (int lump, void *dest, int size)
+{
+ int length, ofs;
+
+ length = header->lumps[lump].filelen;
+ ofs = header->lumps[lump].fileofs;
+
+ if (length % size)
+ Error ("LoadBSPFile: odd lump size");
+
+ memcpy (dest, (byte *)header + ofs, length);
+
+ return length / size;
+}
+
+/*
+=============
+LoadBSPFile
+=============
+*/
+void LoadBSPFile (char *filename)
+{
+ int i;
+
+ //
+ // load the file header
+ //
+ LoadFile (filename, (void **)&header);
+
+ // swap the header
+ for (i=0 ; i< (int)sizeof(dheader_t)/4 ; i++)
+ ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
+
+ if (header->version != BSPVERSION)
+ Error ("%s is version %i, should be %i", filename, i, BSPVERSION);
+
+ nummodels = CopyLump (LUMP_MODELS, dmodels, sizeof(dmodel_t));
+ numvertexes = CopyLump (LUMP_VERTEXES, dvertexes, sizeof(dvertex_t));
+ numplanes = CopyLump (LUMP_PLANES, dplanes, sizeof(dplane_t));
+ numleafs = CopyLump (LUMP_LEAFS, dleafs, sizeof(dleaf_t));
+ numnodes = CopyLump (LUMP_NODES, dnodes, sizeof(dnode_t));
+ numtexinfo = CopyLump (LUMP_TEXINFO, texinfo, sizeof(texinfo_t));
+ numclipnodes = CopyLump (LUMP_CLIPNODES, dclipnodes, sizeof(dclipnode_t));
+ numfaces = CopyLump (LUMP_FACES, dfaces, sizeof(dface_t));
+ nummarksurfaces = CopyLump (LUMP_MARKSURFACES, dmarksurfaces, sizeof(dmarksurfaces[0]));
+ numsurfedges = CopyLump (LUMP_SURFEDGES, dsurfedges, sizeof(dsurfedges[0]));
+ numedges = CopyLump (LUMP_EDGES, dedges, sizeof(dedge_t));
+
+ texdatasize = CopyLump (LUMP_TEXTURES, dtexdata, 1);
+ visdatasize = CopyLump (LUMP_VISIBILITY, dvisdata, 1);
+ lightdatasize = CopyLump (LUMP_LIGHTING, dlightdata, 1);
+ entdatasize = CopyLump (LUMP_ENTITIES, dentdata, 1);
+
+ qfree (header); // everything has been copied out
+
+ //
+ // swap everything
+ //
+ SwapBSPFile (false);
+}
+
+//============================================================================
+
+FILE *wadfile;
+dheader_t outheader;
+
+void AddLump (int lumpnum, void *data, int len)
+{
+ lump_t *lump;
+
+ lump = &header->lumps[lumpnum];
+
+ lump->fileofs = LittleLong( ftell(wadfile) );
+ lump->filelen = LittleLong(len);
+ SafeWrite (wadfile, data, (len+3)&~3);
+}
+
+void CheckLightmaps(void)
+{
+ /*
+ int i, l;
+ dface_t *face;
+ if (lightdatasize == 0)
+ return;
+
+ for (i = 0;i < numfaces;i++)
+ {
+ face = dfaces + i;
+ l = face->lightofs - 3;//1000;
+ if (l >= 0)
+ {
+ for (;l < face->lightofs;l++)
+ if (dlightdata[l] != (l & 0xFF))
+ printf("%i = %i\n", l, dlightdata[l]);
+ }
+ }
+ */
+}
+
+/*
+=============
+WriteBSPFile
+
+Swaps the bsp file in place, so it should not be referenced again
+=============
+*/
+void WriteBSPFile (char *filename, qboolean litonly)
+{
+ if (!litonly)
+ {
+ header = &outheader;
+ memset (header, 0, sizeof(dheader_t));
+
+ SwapBSPFile (true);
+
+ header->version = LittleLong (BSPVERSION);
+
+ wadfile = SafeOpenWrite (filename);
+ SafeWrite (wadfile, header, sizeof(dheader_t)); // overwritten later
+
+ AddLump (LUMP_PLANES, dplanes, numplanes*sizeof(dplane_t));
+ AddLump (LUMP_LEAFS, dleafs, numleafs*sizeof(dleaf_t));
+ AddLump (LUMP_VERTEXES, dvertexes, numvertexes*sizeof(dvertex_t));
+ AddLump (LUMP_NODES, dnodes, numnodes*sizeof(dnode_t));
+ AddLump (LUMP_TEXINFO, texinfo, numtexinfo*sizeof(texinfo_t));
+ AddLump (LUMP_FACES, dfaces, numfaces*sizeof(dface_t));
+ AddLump (LUMP_CLIPNODES, dclipnodes, numclipnodes*sizeof(dclipnode_t));
+ AddLump (LUMP_MARKSURFACES, dmarksurfaces, nummarksurfaces*sizeof(dmarksurfaces[0]));
+ AddLump (LUMP_SURFEDGES, dsurfedges, numsurfedges*sizeof(dsurfedges[0]));
+ AddLump (LUMP_EDGES, dedges, numedges*sizeof(dedge_t));
+ AddLump (LUMP_MODELS, dmodels, nummodels*sizeof(dmodel_t));
+
+ AddLump (LUMP_LIGHTING, dlightdata, lightdatasize);
+ AddLump (LUMP_VISIBILITY, dvisdata, visdatasize);
+ AddLump (LUMP_ENTITIES, dentdata, entdatasize);
+ AddLump (LUMP_TEXTURES, dtexdata, texdatasize);
+
+ fseek (wadfile, 0, SEEK_SET);
+ SafeWrite (wadfile, header, sizeof(dheader_t));
+ fclose (wadfile);
+ }
+
+ if (rgblightdatasize)
+ {
+ FILE *litfile;
+ char litfilename[1024];
+
+ strcpy(litfilename, filename);
+ ReplaceExtension(litfilename, ".lit");
+ litfile = SafeOpenWrite(litfilename);
+
+ if (litfile)
+ {
+ fputc('Q', litfile);
+ fputc('L', litfile);
+ fputc('I', litfile);
+ fputc('T', litfile);
+ fputc(1, litfile);
+ fputc(0, litfile);
+ fputc(0, litfile);
+ fputc(0, litfile);
+ SafeWrite(litfile, drgblightdata, rgblightdatasize);
+ fclose(litfile);
+ }
+ else
+ printf("unable to write \"%s\"\n", litfilename);
+ }
+}
+
+//============================================================================
+
+/*
+=============
+PrintBSPFileSizes
+
+Dumps info about current file
+=============
+*/
+void PrintBSPFileSizes (void)
+{
+ printf ("%5i models %6i\n", nummodels, (int)(nummodels*sizeof(dmodel_t)));
+ printf ("%5i planes %6i\n", numplanes, (int)(numplanes*sizeof(dplane_t)));
+ printf ("%5i vertexes %6i\n", numvertexes, (int)(numvertexes*sizeof(dvertex_t)));
+ printf ("%5i nodes %6i\n", numnodes, (int)(numnodes*sizeof(dnode_t)));
+ printf ("%5i texinfo %6i\n", numtexinfo, (int)(numtexinfo*sizeof(texinfo_t)));
+ printf ("%5i faces %6i\n", numfaces, (int)(numfaces*sizeof(dface_t)));
+ printf ("%5i clipnodes %6i\n", numclipnodes, (int)(numclipnodes*sizeof(dclipnode_t)));
+ printf ("%5i leafs %6i\n", numleafs, (int)(numleafs*sizeof(dleaf_t)));
+ printf ("%5i marksurfaces %6i\n", nummarksurfaces, (int)(nummarksurfaces*sizeof(dmarksurfaces[0])));
+ printf ("%5i surfedges %6i\n", numsurfedges, (int)(numsurfedges*sizeof(dmarksurfaces[0])));
+ printf ("%5i edges %6i\n", numedges, (int)(numedges*sizeof(dedge_t)));
+ if (!texdatasize)
+ printf (" 0 textures 0\n");
+ else
+ printf ("%5i textures %6i\n", ((dmiptexlump_t*)dtexdata)->nummiptex, texdatasize);
+ printf (" lightdata %6i\n", lightdatasize);
+ printf (" visdata %6i\n", visdatasize);
+ printf (" entdata %6i\n", entdatasize);
+}
+
+//============================================================================
+
+/*
+===============
+CompressVis
+===============
+*/
+int CompressVis (byte *vis, byte *dest, int visrow)
+{
+ int j;
+ int rep;
+ byte *dest_p;
+
+ dest_p = dest;
+
+ for (j=0 ; j<visrow ; j++)
+ {
+ *dest_p++ = vis[j];
+ if (vis[j])
+ continue;
+
+ rep = 1;
+ for (j++; j<visrow ; j++)
+ if (vis[j] || rep == 255)
+ break;
+ else
+ rep++;
+ *dest_p++ = rep;
+ j--;
+ }
+
+ return dest_p - dest;
+}
+
+/*
+===============
+DecompressVis
+===============
+*/
+void DecompressVis(byte *in, byte *out, int size)
+{
+ byte *end = out + size;
+ int n;
+ while (out < end)
+ {
+ n = *in++;
+ if (n)
+ *out++ = n;
+ else
+ {
+ n = *in++;
+ while (n--)
+ *out++ = 0;
+ }
+ }
+}
+
+//============================================================================
+
+int num_entities = 0;
+entity_t entities[MAX_MAP_ENTITIES];
+
+void PrintEntity (entity_t *ent)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ printf ("%20s : %s\n", ep->key, ep->value);
+}
+
+
+char *ValueForKey (entity_t *ent, char *key)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ return ep->value;
+ return "";
+}
+
+vec_t FloatForKey (entity_t *ent, char *key)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ return atof( ep->value );
+
+ return 0;
+}
+
+void SetKeyValue (entity_t *ent, char *key, char *value)
+{
+ epair_t *ep;
+
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ if (!strcmp (ep->key, key) )
+ {
+ qfree (ep->value);
+ ep->value = copystring(value);
+ return;
+ }
+ ep = qmalloc (sizeof(*ep));
+ ep->next = ent->epairs;
+ ent->epairs = ep;
+ ep->key = copystring(key);
+ ep->value = copystring(value);
+}
+
+/*
+=================
+ParseEpair
+=================
+*/
+epair_t *ParseEpair (void)
+{
+ epair_t *e;
+
+ e = qmalloc (sizeof(epair_t));
+ memset (e, 0, sizeof(epair_t));
+
+ if (strlen(token) >= MAX_KEY-1)
+ Error ("ParseEpair: token too long");
+ e->key = copystring(token);
+ GetToken (false);
+ if (strlen(token) >= MAX_VALUE-1)
+ Error ("ParseEpair: token too long");
+ e->value = copystring(token);
+
+ return e;
+}
+
+entity_t *FindEntityWithKeyPair( char *key, char *value )
+{
+ entity_t *ent;
+ epair_t *ep;
+ int i;
+
+ for (i=0 ; i<num_entities ; i++)
+ {
+ ent = &entities[ i ];
+ for (ep=ent->epairs ; ep ; ep=ep->next)
+ {
+ if (!strcmp (ep->key, key) )
+ {
+ if ( !strcmp( ep->value, value ) )
+ return ent;
+ break;
+ }
+ }
+ }
+ return NULL;
+}
+
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
+{
+ char *k;
+ double v1, v2, v3;
+
+ k = ValueForKey (ent, key);
+ v1 = v2 = v3 = 0;
+ // scanf into doubles, then assign, so it is vec_t size independent
+ sscanf (k, "%lf %lf %lf", &v1, &v2, &v3);
+ vec[0] = v1;
+ vec[1] = v2;
+ vec[2] = v3;
+}
+
+/*
+==================
+ParseEntities
+==================
+*/
+void ParseEntities (void)
+{
+ char *data;
+ entity_t *entity;
+ char key[MAX_KEY];
+ epair_t *epair;
+
+ data = dentdata;
+
+ //
+ // start parsing
+ //
+ num_entities = 0;
+
+ // go through all the entities
+ while (1)
+ {
+ // parse the opening brace
+ data = COM_Parse (data);
+ if (!data)
+ break;
+ if (com_token[0] != '{')
+ Error ("LoadEntities: found %s when expecting {", com_token);
+
+ if (num_entities == MAX_MAP_ENTITIES)
+ Error ("LoadEntities: MAX_MAP_ENTITIES");
+
+ entity = &entities[num_entities++];
+
+ // go through all the keys in this entity
+ while (1)
+ {
+ int c;
+
+ // parse key
+ data = COM_Parse (data);
+ if (!data)
+ Error ("LoadEntities: EOF without closing brace");
+ if (!strcmp(com_token,"}"))
+ break;
+
+ strcpy (key, com_token);
+
+ // parse value
+ data = COM_Parse (data);
+ if (!data)
+ Error ("LoadEntities: EOF without closing brace");
+ c = com_token[0];
+ if (c == '}')
+ Error ("LoadEntities: closing brace without data");
+
+ epair = malloc (sizeof(epair_t));
+ epair->key = copystring( key );
+ epair->value = copystring( com_token );
+ epair->next = entity->epairs;
+ entity->epairs = epair;
+ }
+ }
+}
+
+/*
+==================
+UnparseEntity
+==================
+*/
+static void UnparseEntity( entity_t *ent, char *buf, char **end )
+{
+ epair_t *ep;
+ char line[16384];
+
+ ep = ent->epairs;
+ if( !ep )
+ return;
+
+ strcat (*end,"{\n");
+ *end += 2;
+
+ for ( ; ep ; ep=ep->next)
+ {
+ sprintf (line, "\"%s\" \"%s\"\n", ep->key, ep->value);
+ strcat (*end, line);
+ *end += strlen(line);
+ }
+ strcat (*end,"}\n");
+ *end += 2;
+
+ if (*end > buf + MAX_MAP_ENTSTRING)
+ Error ("Entity text too long");
+}
+
+/*
+==================
+UnparseEntities
+==================
+*/
+void UnparseEntities (void)
+{
+ int i;
+ entity_t *ent;
+ char *buf, *end;
+
+ buf = dentdata;
+ end = buf;
+ *end = 0;
+
+ // Vic: write "worldspawn" as the very first entity (engines might depend on it)
+ ent = FindEntityWithKeyPair( "classname", "worldspawn" );
+ if( ent )
+ UnparseEntity( ent, buf, &end );
+
+ for (i=0 ; i<num_entities ; i++) {
+ if( &entities[i] != ent )
+ UnparseEntity( &entities[i], buf, &end );
+ }
+
+ entdatasize = end - buf + 1;
+}
Index: hmap2/bspfile.h
diff -u /dev/null hmap2/bspfile.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/bspfile.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,287 @@
+
+#ifndef BSPFILE_H
+#define BSPFILE_H
+
+// upper design bounds
+
+#define MAX_MAP_HULLS 4
+
+#define MAX_MAP_MODELS 256
+#define MAX_MAP_BRUSHES 32768 // LordHavoc: I ran into this myself, was 4096
+#define MAX_MAP_ENTITIES 2048 // LordHavoc: was 1024
+#define MAX_MAP_ENTSTRING 0x100000 // LordHavoc: was 65536
+
+#define MAX_MAP_PLANES 32767 // LordHavoc: I ran into this myself, was 8192
+#define MAX_MAP_NODES 32767 // because negative shorts are contents
+#define MAX_MAP_CLIPNODES 32767 //
+#define MAX_MAP_LEAFS 32767 //
+#define MAX_MAP_VERTS 65535
+#define MAX_MAP_FACES 65535
+#define MAX_MAP_MARKSURFACES 65535
+#define MAX_MAP_TEXINFO 4096
+#define MAX_MAP_EDGES 0x100000 // LordHavoc: was 256000
+#define MAX_MAP_SURFEDGES 0x200000 // LordHavoc: was 512000
+#define MAX_MAP_MIPTEX 0x800000 // LordHavoc: quadrupled (was 0x200000)
+#define MAX_MAP_LIGHTING 0x400000 // LordHavoc: raised from 0x100000 (1mb) to 0x400000 (4mb)
+#define MAX_MAP_VISIBILITY 0x400000 // LordHavoc: quadrupled (was 0x100000)
+
+// key / value pair sizes
+
+#define MAX_KEY 32
+#define MAX_VALUE 1024
+
+//=============================================================================
+
+
+#define BSPVERSION 29
+
+typedef struct
+{
+ int fileofs, filelen;
+} lump_t;
+
+#define LUMP_ENTITIES 0
+#define LUMP_PLANES 1
+#define LUMP_TEXTURES 2
+#define LUMP_VERTEXES 3
+#define LUMP_VISIBILITY 4
+#define LUMP_NODES 5
+#define LUMP_TEXINFO 6
+#define LUMP_FACES 7
+#define LUMP_LIGHTING 8
+#define LUMP_CLIPNODES 9
+#define LUMP_LEAFS 10
+#define LUMP_MARKSURFACES 11
+#define LUMP_EDGES 12
+#define LUMP_SURFEDGES 13
+#define LUMP_MODELS 14
+
+#define HEADER_LUMPS 15
+
+typedef struct
+{
+ float mins[3], maxs[3];
+ float origin[3];
+ int headnode[MAX_MAP_HULLS];
+ int visleafs; // not including the solid leaf 0
+ int firstface, numfaces;
+} dmodel_t;
+
+typedef struct
+{
+ int version;
+ lump_t lumps[HEADER_LUMPS];
+} dheader_t;
+
+typedef struct
+{
+ int nummiptex;
+ int dataofs[4]; // [nummiptex]
+} dmiptexlump_t;
+
+#define MIPLEVELS 4
+typedef struct miptex_s
+{
+ char name[16];
+ unsigned width, height;
+ unsigned offsets[MIPLEVELS]; // four mip maps stored
+} miptex_t;
+
+
+typedef struct
+{
+ float point[3];
+} dvertex_t;
+
+
+// 0-2 are axial planes
+#define PLANE_X 0
+#define PLANE_Y 1
+#define PLANE_Z 2
+
+// 3-5 are non-axial planes snapped to the nearest
+#define PLANE_ANYX 3
+#define PLANE_ANYY 4
+#define PLANE_ANYZ 5
+
+typedef struct
+{
+ float normal[3];
+ float dist;
+ int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate
+} dplane_t;
+
+
+
+#define CONTENTS_EMPTY -1
+#define CONTENTS_SOLID -2
+#define CONTENTS_WATER -3
+#define CONTENTS_SLIME -4
+#define CONTENTS_LAVA -5
+#define CONTENTS_SKY -6
+
+// !!! if this is changed, it must be changed in asm_i386.h too !!!
+typedef struct
+{
+ int planenum;
+ short children[2]; // negative numbers are -(leafs+1), not nodes
+ short mins[3]; // for sphere culling
+ short maxs[3];
+ unsigned short firstface;
+ unsigned short numfaces; // counting both sides
+} dnode_t;
+
+typedef struct
+{
+ int planenum;
+ short children[2]; // negative numbers are contents
+} dclipnode_t;
+
+
+typedef struct texinfo_s
+{
+ float vecs[2][4]; // [s/t][xyz offset]
+ int miptex;
+ int flags;
+} texinfo_t;
+#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision
+
+// note that edge 0 is never used, because negative edge nums are used for
+// counterclockwise use of the edge in a face
+typedef struct
+{
+ unsigned short v[2]; // vertex numbers
+} dedge_t;
+
+#define MAXLIGHTMAPS 4
+typedef struct
+{
+ short planenum;
+ short side;
+
+ int firstedge; // we must support > 64k edges
+ short numedges;
+ short texinfo;
+
+// lighting info
+ byte styles[MAXLIGHTMAPS];
+ int lightofs; // start of [numstyles*surfsize] samples
+} dface_t;
+
+
+
+#define AMBIENT_WATER 0
+#define AMBIENT_SKY 1
+#define AMBIENT_SLIME 2
+#define AMBIENT_LAVA 3
+
+#define NUM_AMBIENTS 4 // automatic ambient sounds
+
+// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas
+// all other leafs need visibility info
+typedef struct
+{
+ int contents;
+ int visofs; // -1 = no visibility info
+
+ short mins[3]; // for frustum culling
+ short maxs[3];
+
+ unsigned short firstmarksurface;
+ unsigned short nummarksurfaces;
+
+ byte ambient_level[NUM_AMBIENTS];
+} dleaf_t;
+
+//============================================================================
+
+#ifndef QUAKE_GAME
+
+// the utilities get to be lazy and just use large static arrays
+
+extern int nummodels;
+extern dmodel_t dmodels[MAX_MAP_MODELS];
+
+extern int visdatasize;
+extern byte dvisdata[MAX_MAP_VISIBILITY];
+
+extern int lightdatasize;
+extern byte dlightdata[MAX_MAP_LIGHTING];
+
+// LordHavoc: stored in .lit file
+extern int rgblightdatasize;
+extern byte drgblightdata[MAX_MAP_LIGHTING*3];
+
+extern int texdatasize;
+extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t)
+
+extern int entdatasize;
+extern char dentdata[MAX_MAP_ENTSTRING];
+
+extern int numleafs;
+extern dleaf_t dleafs[MAX_MAP_LEAFS];
+
+extern int numplanes;
+extern dplane_t dplanes[MAX_MAP_PLANES];
+
+extern int numvertexes;
+extern dvertex_t dvertexes[MAX_MAP_VERTS];
+
+extern int numnodes;
+extern dnode_t dnodes[MAX_MAP_NODES];
+
+extern int numtexinfo;
+extern texinfo_t texinfo[MAX_MAP_TEXINFO];
+
+extern int numfaces;
+extern dface_t dfaces[MAX_MAP_FACES];
+
+extern int numclipnodes;
+extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES];
+
+extern int numedges;
+extern dedge_t dedges[MAX_MAP_EDGES];
+
+extern int nummarksurfaces;
+extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES];
+
+extern int numsurfedges;
+extern int dsurfedges[MAX_MAP_SURFEDGES];
+
+typedef struct epair_s
+{
+ struct epair_s *next;
+ char *key;
+ char *value;
+} epair_t;
+
+typedef struct
+{
+ epair_t *epairs;
+ struct mbrush_s *brushes;
+} entity_t;
+
+extern int num_entities;
+extern entity_t entities[MAX_MAP_ENTITIES];
+
+void PrintEntity (entity_t *ent);
+char *ValueForKey (entity_t *ent, char *key);
+vec_t FloatForKey (entity_t *ent, char *key);
+void SetKeyValue (entity_t *ent, char *key, char *value);
+entity_t *FindEntityWithKeyPair( char *key, char *value );
+void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
+epair_t *ParseEpair (void);
+void ParseEntities (void);
+void UnparseEntities (void);
+
+int CompressVis (byte *vis, byte *dest, int visrow);
+void DecompressVis(byte *in, byte *out, int size);
+
+void LoadBSPFile (char *filename);
+void WriteBSPFile (char *filename, qboolean litonly);
+void PrintBSPFileSizes (void);
+
+#endif
+
+#endif
+
Index: hmap2/cmdlib.c
diff -u /dev/null hmap2/cmdlib.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/cmdlib.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,847 @@
+// cmdlib.c
+
+#ifndef WIN32
+#define USEGETTIMEOFDAY 1
+#endif
+
+#define PATHSEPERATOR '/'
+#define __USE_BSD 1
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <limits.h>
+#include <direct.h>
+#include <windows.h>
+#endif
+#include "cmdlib.h"
+#if USEGETTIMEOFDAY
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include "mem.h"
+
+// set these before calling CheckParm
+int myargc;
+char **myargv;
+
+char com_token[1024];
+qboolean com_eof;
+
+qboolean archive;
+char archivedir[1024];
+
+
+/*
+=================
+Error
+
+For abnormal program terminations
+=================
+*/
+void Error (char *error, ...)
+{
+ va_list argptr;
+
+ printf ("************ ERROR ************\n");
+
+ va_start (argptr,error);
+ vprintf (error,argptr);
+ va_end (argptr);
+ printf ("\n");
+
+#if _MSC_VER && _DEBUG
+ printf("press any key\n");
+ getchar();
+#endif
+
+ exit (1);
+}
+
+
+/*
+
+qdir will hold the path up to the quake directory, including the slash
+
+ f:\quake\
+ /raid/quake/
+
+gamedir will hold qdir + the game directory (id1, id2, etc)
+
+ */
+
+char *copystring(char *s)
+{
+ char *b;
+ b = qmalloc(strlen(s)+1);
+ strcpy (b, s);
+ return b;
+}
+
+
+
+/*
+================
+I_DoubleTime
+================
+*/
+double I_DoubleTime (void)
+{
+#ifdef WIN32
+ static DWORD starttime;
+ static qboolean first = true;
+ DWORD now;
+
+ if (first)
+ timeBeginPeriod (1);
+
+ now = timeGetTime ();
+
+ if (first)
+ {
+ first = false;
+ starttime = now;
+ return 0.0;
+ }
+
+ if (now < starttime) // wrapped?
+ return (now / 1000.0) + (LONG_MAX - starttime / 1000.0);
+
+ if (now - starttime == 0)
+ return 0.0;
+
+ return (now - starttime) / 1000.0;
+#elif USEGETTIMEOFDAY
+ // LordHavoc: see top of file to disable this cleanly if your system does not support it
+#include <time.h>
+ struct timeval tp;
+ struct timezone tzp;
+ static int secbase;
+
+ gettimeofday(&tp, &tzp);
+
+ if (!secbase)
+ {
+ secbase = tp.tv_sec;
+ return tp.tv_usec/1000000.0;
+ }
+
+ return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
+#else
+ time_t t;
+
+ time (&t);
+
+ return t;
+#endif
+}
+
+/*
+void Q_getwd (char *out)
+{
+#ifdef WIN32
+ _getcwd (out, 256);
+ strcat (out, "\\");
+#else
+ getwd (out);
+ strcat (out, "/");
+#endif
+}
+*/
+/*
+void Q_getwd (char *out)
+{
+ getwd (out);
+}
+*/
+
+
+void Q_mkdir (char *path)
+{
+#ifdef WIN32
+ if (mkdir (path) != -1)
+#else
+ if (mkdir (path, 0777) != -1)
+#endif
+ return;
+ if (errno != EEXIST)
+ Error ("mkdir %s: %s",path, strerror(errno));
+}
+
+/*
+============
+FileTime
+
+returns -1 if not present
+============
+*/
+int FileTime (char *path)
+{
+ struct stat buf;
+
+ if (stat (path,&buf) == -1)
+ return -1;
+
+ return buf.st_mtime;
+}
+
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+ int c;
+ int len;
+
+ len = 0;
+ com_token[0] = 0;
+
+ if (!data)
+ return NULL;
+
+ // skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ {
+ com_eof = true;
+ return NULL; // end of file;
+ }
+ data++;
+ }
+
+ // skip // comments
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+
+
+ // handle quoted strings specially
+ if (c == '\"')
+ {
+ data++;
+ do
+ {
+ c = *data++;
+ if (c=='\"')
+ {
+ com_token[len] = 0;
+ return data;
+ }
+ com_token[len] = c;
+ len++;
+ } while (1);
+ }
+
+ // parse single characters
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+ {
+ com_token[len] = c;
+ len++;
+ com_token[len] = 0;
+ return data+1;
+ }
+
+ // parse a regular word
+ do
+ {
+ com_token[len] = c;
+ data++;
+ len++;
+ c = *data;
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+ break;
+ } while (c>32);
+
+ com_token[len] = 0;
+ return data;
+}
+
+
+int Q_strncasecmp (char *s1, char *s2, int n)
+{
+ int c1, c2;
+
+ while (1)
+ {
+ c1 = *s1++;
+ c2 = *s2++;
+
+ if (!n--)
+ return 0; // strings are equal until end point
+
+ if (c1 != c2)
+ {
+ if (c1 >= 'a' && c1 <= 'z')
+ c1 -= ('a' - 'A');
+ if (c2 >= 'a' && c2 <= 'z')
+ c2 -= ('a' - 'A');
+ if (c1 != c2)
+ return -1; // strings not equal
+ }
+ if (!c1)
+ return 0; // strings are equal
+ }
+
+ return -1;
+}
+
+int Q_strcasecmp (char *s1, char *s2)
+{
+ return Q_strncasecmp (s1, s2, 99999);
+}
+
+
+char *Q_strupr (char *start)
+{
+ char *in;
+ in = start;
+ while (*in)
+ {
+ *in = toupper(*in);
+ in++;
+ }
+ return start;
+}
+
+char *Q_strlower (char *start)
+{
+ char *in;
+ in = start;
+ while (*in)
+ {
+ *in = tolower(*in);
+ in++;
+ }
+ return start;
+}
+
+
+/*
+=============================================================================
+
+ MISC FUNCTIONS
+
+=============================================================================
+*/
+
+
+/*
+=================
+CheckParm
+
+Checks for the given parameter in the program's command line arguments
+Returns the argument number (1 to argc-1) or 0 if not present
+=================
+*/
+int CheckParm (char *check)
+{
+ int i;
+
+ for (i = 1;i<myargc;i++)
+ {
+ if ( !Q_strcasecmp(check, myargv[i]) )
+ return i;
+ }
+
+ return 0;
+}
+
+
+
+/*
+================
+Q_filelength
+================
+*/
+int Q_filelength (FILE *f)
+{
+ int pos;
+ int end;
+
+ pos = ftell (f);
+ fseek (f, 0, SEEK_END);
+ end = ftell (f);
+ fseek (f, pos, SEEK_SET);
+
+ return end;
+}
+
+
+FILE *SafeOpenWrite (char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "wb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+FILE *SafeOpenRead (char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "rb");
+
+ if (!f)
+ Error ("Error opening %s: %s",filename,strerror(errno));
+
+ return f;
+}
+
+
+void SafeRead (FILE *f, void *buffer, int count)
+{
+ if ( fread (buffer, 1, count, f) != (size_t)count)
+ Error ("File read failure");
+}
+
+
+void SafeWrite (FILE *f, void *buffer, int count)
+{
+ if (fwrite (buffer, 1, count, f) != (size_t)count)
+ Error ("File write failure");
+}
+
+
+
+/*
+==============
+LoadFile
+==============
+*/
+int LoadFile (char *filename, void **bufferptr)
+{
+ FILE *f;
+ int length;
+ void *buffer;
+
+ f = SafeOpenRead (filename);
+ length = Q_filelength (f);
+ buffer = qmalloc (length+1);
+ ((char *)buffer)[length] = 0;
+ SafeRead (f, buffer, length);
+ fclose (f);
+
+ *bufferptr = buffer;
+ return length;
+}
+
+
+/*
+==============
+SaveFile
+==============
+*/
+void SaveFile (char *filename, void *buffer, int count)
+{
+ FILE *f;
+
+ f = SafeOpenWrite (filename);
+ SafeWrite (f, buffer, count);
+ fclose (f);
+}
+
+
+
+void DefaultExtension (char *path, char *extension)
+{
+ char *src;
+ //
+ // if path doesn't have a .EXT, append extension
+ // (extension should include the .)
+ //
+ src = path + strlen(path) - 1;
+
+ while (*src != PATHSEPERATOR && src != path)
+ {
+ if (*src == '.')
+ return; // it has an extension
+ src--;
+ }
+
+ strcat (path, extension);
+}
+
+
+void DefaultPath (char *path, char *basepath)
+{
+ char temp[128];
+
+ if (path[0] == PATHSEPERATOR)
+ return; // absolute path location
+ strcpy (temp,path);
+ strcpy (path,basepath);
+ strcat (path,temp);
+}
+
+
+void ReplaceExtension (char *path, char *extension)
+{
+ char *src;
+ //
+ // if path has a .EXT, replace it with extension
+ // if path doesn't have a .EXT, append extension
+ // (extension should include the .)
+ //
+ src = path + strlen(path) - 1;
+
+ while (*src != PATHSEPERATOR && src != path)
+ {
+ if (*src == '.')
+ {
+ *src = 0;
+ break;
+ }
+ src--;
+ }
+
+ strcat (path, extension);
+}
+
+
+/*
+====================
+Extract file parts
+====================
+*/
+void ExtractFilePath (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+ //
+ // back up until a \ or the start
+ //
+ while (src != path && *(src-1) != PATHSEPERATOR)
+ src--;
+
+ memcpy (dest, path, src-path);
+ dest[src-path] = 0;
+}
+
+void ExtractFileBase (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+ //
+ // back up until a \ or the start
+ //
+ while (src != path && *(src-1) != PATHSEPERATOR)
+ src--;
+
+ while (*src && *src != '.')
+ {
+ *dest++ = *src++;
+ }
+ *dest = 0;
+}
+
+void ExtractFileExtension (char *path, char *dest)
+{
+ char *src;
+
+ src = path + strlen(path) - 1;
+
+ //
+ // back up until a . or the start
+ //
+ while (src != path && *(src-1) != '.')
+ src--;
+ if (src == path)
+ {
+ *dest = 0; // no extension
+ return;
+ }
+
+ strcpy (dest,src);
+}
+
+
+/*
+==============
+ParseNum / ParseHex
+==============
+*/
+int ParseHex (char *hex)
+{
+ char *str;
+ int num;
+
+ num = 0;
+ str = hex;
+
+ while (*str)
+ {
+ num <<= 4;
+ if (*str >= '0' && *str <= '9')
+ num += *str-'0';
+ else if (*str >= 'a' && *str <= 'f')
+ num += 10 + *str-'a';
+ else if (*str >= 'A' && *str <= 'F')
+ num += 10 + *str-'A';
+ else
+ Error ("Bad hex number: %s",hex);
+ str++;
+ }
+
+ return num;
+}
+
+
+int ParseNum (char *str)
+{
+ if (str[0] == '$')
+ return ParseHex (str+1);
+ if (str[0] == '0' && str[1] == 'x')
+ return ParseHex (str+2);
+ return atol (str);
+}
+
+
+
+/*
+============================================================================
+
+ BYTE ORDER FUNCTIONS
+
+============================================================================
+*/
+
+#ifdef _SGI_SOURCE
+#define __BIG_ENDIAN__
+#endif
+
+#ifdef __BIG_ENDIAN__
+
+short LittleShort (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short BigShort (short l)
+{
+ return l;
+}
+
+
+int LittleLong (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int BigLong (int l)
+{
+ return l;
+}
+
+
+float LittleFloat (float l)
+{
+ union {byte b[4]; float f;} in, out;
+
+ in.f = l;
+ out.b[0] = in.b[3];
+ out.b[1] = in.b[2];
+ out.b[2] = in.b[1];
+ out.b[3] = in.b[0];
+
+ return out.f;
+}
+
+float BigFloat (float l)
+{
+ return l;
+}
+
+
+#else
+
+
+short BigShort (short l)
+{
+ byte b1,b2;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+
+ return (b1<<8) + b2;
+}
+
+short LittleShort (short l)
+{
+ return l;
+}
+
+
+int BigLong (int l)
+{
+ byte b1,b2,b3,b4;
+
+ b1 = l&255;
+ b2 = (l>>8)&255;
+ b3 = (l>>16)&255;
+ b4 = (l>>24)&255;
+
+ return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
+}
+
+int LittleLong (int l)
+{
+ return l;
+}
+
+float BigFloat (float l)
+{
+ union {byte b[4]; float f;} in, out;
+
+ in.f = l;
+ out.b[0] = in.b[3];
+ out.b[1] = in.b[2];
+ out.b[2] = in.b[1];
+ out.b[3] = in.b[0];
+
+ return out.f;
+}
+
+float LittleFloat (float l)
+{
+ return l;
+}
+
+
+#endif
+
+
+//=======================================================
+
+
+// FIXME: byte swap?
+
+// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
+// and the initial and final xor values shown below... in other words, the
+// CCITT standard CRC used by XMODEM
+
+#define CRC_INIT_VALUE 0xffff
+#define CRC_XOR_VALUE 0x0000
+
+static unsigned short crctable[256] =
+{
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+void CRC_Init(unsigned short *crcvalue)
+{
+ *crcvalue = CRC_INIT_VALUE;
+}
+
+void CRC_ProcessByte(unsigned short *crcvalue, byte data)
+{
+ *crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
+}
+
+unsigned short CRC_Value(unsigned short crcvalue)
+{
+ return crcvalue ^ CRC_XOR_VALUE;
+}
+//=============================================================================
+
+/*
+============
+COM_CreatePath
+============
+*/
+void COM_CreatePath (char *path)
+{
+ char *ofs, c;
+
+ for (ofs = path+1 ; *ofs ; ofs++)
+ {
+ c = *ofs;
+ if (c == '/' || c == '\\')
+ { // create the directory
+ *ofs = 0;
+ Q_mkdir (path);
+ *ofs = c;
+ }
+ }
+}
+
+
+/*
+============
+COM_CopyFile
+
+ Used to archive source files
+============
+*/
+void COM_CopyFile (char *from, char *to)
+{
+ void *buffer;
+ int length;
+
+ length = LoadFile (from, &buffer);
+ COM_CreatePath (to);
+ SaveFile (to, buffer, length);
+ qfree (buffer);
+}
Index: hmap2/cmdlib.h
diff -u /dev/null hmap2/cmdlib.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/cmdlib.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,105 @@
+// cmdlib.h
+
+#ifndef __CMDLIB__
+#define __CMDLIB__
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <stdarg.h>
+//#include <unistd.h>
+
+#ifndef __BYTEBOOL__
+#define __BYTEBOOL__
+typedef enum {false, true} qboolean;
+typedef unsigned char byte;
+#endif
+
+// the dec offsetof macro doesn't work very well...
+#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
+
+// set these before calling CheckParm
+extern int myargc;
+extern char **myargv;
+
+extern char *Q_strupr (char *in);
+extern char *Q_strlower (char *in);
+extern int Q_strncasecmp (char *s1, char *s2, int n);
+extern int Q_strcasecmp (char *s1, char *s2);
+//extern void Q_getwd (char *out);
+
+extern int Q_filelength (FILE *f);
+extern int FileTime (char *path);
+
+extern void Q_mkdir (char *path);
+
+extern char qdir[1024];
+extern char gamedir[1024];
+//extern void SetQdirFromPath (char *path);
+//extern char *ExpandPath (char *path);
+//extern char *ExpandPathAndArchive (char *path);
+
+
+extern double I_DoubleTime (void);
+
+extern void Error (char *error, ...);
+
+extern int CheckParm (char *check);
+
+extern FILE *SafeOpenWrite (char *filename);
+extern FILE *SafeOpenRead (char *filename);
+extern void SafeRead (FILE *f, void *buffer, int count);
+extern void SafeWrite (FILE *f, void *buffer, int count);
+
+extern int LoadFile (char *filename, void **bufferptr);
+extern void SaveFile (char *filename, void *buffer, int count);
+
+extern void DefaultExtension (char *path, char *extension);
+extern void DefaultPath (char *path, char *basepath);
+extern void ReplaceExtension (char *path, char *extension);
+
+extern void ExtractFilePath (char *path, char *dest);
+extern void ExtractFileBase (char *path, char *dest);
+extern void ExtractFileExtension (char *path, char *dest);
+
+extern int ParseNum (char *str);
+
+extern short BigShort (short l);
+extern short LittleShort (short l);
+extern int BigLong (int l);
+extern int LittleLong (int l);
+extern float BigFloat (float l);
+extern float LittleFloat (float l);
+
+
+extern char *COM_Parse (char *data);
+
+extern char com_token[1024];
+extern qboolean com_eof;
+
+extern char *copystring(char *s);
+
+// LordHavoc: increased maximum token length from 128 to 1024
+#define MAXTOKEN 1024
+
+extern char token[MAXTOKEN];
+extern int scriptline;
+
+void StartTokenParsing (char *data);
+qboolean GetToken (qboolean crossline);
+void UngetToken (void);
+
+extern void CRC_Init(unsigned short *crcvalue);
+extern void CRC_ProcessByte(unsigned short *crcvalue, byte data);
+extern unsigned short CRC_Value(unsigned short crcvalue);
+
+extern void COM_CreatePath (char *path);
+extern void COM_CopyFile (char *from, char *to);
+
+extern qboolean archive;
+extern char archivedir[1024];
+
+#endif
Index: hmap2/csg4.c
diff -u /dev/null hmap2/csg4.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/csg4.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,311 @@
+// csg4.c
+
+#include "bsp5.h"
+
+/*
+
+NOTES
+-----
+Brushes that touch still need to be split at the cut point to make a tjunction
+
+*/
+
+face_t *inside, *outside;
+int brushfaces;
+int csgfaces;
+int csgmergefaces;
+
+/*
+=================
+ClipInside
+
+Clips all of the faces in the inside list, possibly moving them to the
+outside list or spliting it into a piece in each list.
+
+Faces exactly on the plane will stay inside unless overdrawn by later brush
+
+frontside is the side of the plane that holds the outside list
+=================
+*/
+void ClipInside (int splitplane, int frontside, qboolean precedence)
+{
+ face_t *f, *next;
+ face_t *frags[2];
+ face_t *insidelist;
+ plane_t *split;
+
+ split = &mapplanes[splitplane];
+
+ insidelist = NULL;
+ for (f=inside ; f ; f=next)
+ {
+ next = f->next;
+
+ if (f->planenum == splitplane)
+ { // exactly on, handle special
+ if ( frontside != f->planeside || precedence )
+ { // allways clip off opposite faceing
+ frags[frontside] = NULL;
+ frags[!frontside] = f;
+ }
+ else
+ { // leave it on the outside
+ frags[frontside] = f;
+ frags[!frontside] = NULL;
+ }
+ }
+ else
+ { // proper split
+ SplitFace (f, split, &frags[0], &frags[1]);
+ }
+
+ if (frags[frontside])
+ {
+ frags[frontside]->next = outside;
+ outside = frags[frontside];
+ }
+ if (frags[!frontside])
+ {
+ frags[!frontside]->next = insidelist;
+ insidelist = frags[!frontside];
+ }
+ }
+
+ inside = insidelist;
+}
+
+
+/*
+==================
+SaveOutside
+
+Saves all of the faces in the outside list to the bsp plane list
+==================
+*/
+void SaveOutside (tree_t *tree, qboolean mirror)
+{
+ int planenum;
+ face_t *f , *next, *newf;
+
+ for (f=outside ; f ; f=next)
+ {
+ next = f->next;
+ csgfaces++;
+ planenum = f->planenum;
+
+ if (mirror)
+ {
+ newf = NewFaceFromFace (f);
+ newf->planeside = f->planeside ^ 1; // reverse side
+ newf->contents[0] = f->contents[1];
+ newf->contents[1] = f->contents[0];
+ newf->winding = ReverseWinding( f->winding );
+ tree->validfaces[planenum] = MergeFaceToList_r( newf, tree->validfaces[planenum] );
+ }
+
+ tree->validfaces[planenum] = MergeFaceToList_r( f, tree->validfaces[planenum]);
+ tree->validfaces[planenum] = FreeMergeListScraps( tree->validfaces[planenum] );
+ }
+}
+
+/*
+==================
+FreeInside
+
+Free all the faces that got clipped out
+==================
+*/
+void FreeInside (int contents)
+{
+ face_t *f, *next;
+
+ for (f=inside ; f ; f=next)
+ {
+ next = f->next;
+
+ if (contents != CONTENTS_SOLID)
+ {
+ f->contents[0] = contents;
+ f->next = outside;
+ outside = f;
+ }
+ else
+ FreeFace (f);
+ }
+}
+
+//==========================================================================
+
+/*
+===========
+AllocSurface
+===========
+*/
+surface_t *AllocSurface( void )
+{
+ surface_t *s;
+
+ s = qmalloc( sizeof( surface_t ) );
+ memset( s, 0, sizeof( surface_t ) );
+
+ return s;
+}
+
+/*
+===========
+FreeSurface
+===========
+*/
+void FreeSurface( surface_t *s ) {
+ qfree( s );
+}
+
+//==========================================================================
+
+/*
+==================
+BuildSurfaces
+
+Returns a chain of all the external surfaces with one or more visible
+faces.
+==================
+*/
+void BuildSurfaces (tree_t *tree)
+{
+ face_t **f;
+ face_t *count;
+ int i;
+ surface_t *s;
+ surface_t *surfhead;
+
+ surfhead = NULL;
+
+ f = tree->validfaces;
+ for (i=0 ; i<nummapplanes ; i++, f++)
+ {
+ if (!*f)
+ continue; // nothing left on this plane
+
+// create a new surface to hold the faces on this plane
+ s = AllocSurface ();
+ s->planenum = i;
+ s->next = surfhead;
+ surfhead = s;
+ s->faces = *f;
+ for (count = s->faces ; count ; count=count->next)
+ csgmergefaces++;
+ CalcSurfaceInfo (s); // bounding box and flags
+ }
+
+ tree->surfaces = surfhead;
+}
+
+//==========================================================================
+
+/*
+==================
+CopyFacesToOutside
+==================
+*/
+void CopyFacesToOutside (brush_t *b)
+{
+ face_t *f, *newf;
+
+ outside = NULL;
+
+ for (f=b->faces ; f ; f=f->next)
+ {
+ if( !f->winding )
+ continue;
+
+ brushfaces++;
+ newf = AllocFace ();
+ newf->texturenum = f->texturenum;
+ newf->planenum = f->planenum;
+ newf->planeside = f->planeside;
+ newf->original = f->original;
+ newf->winding = CopyWinding( f->winding );
+ newf->next = outside;
+ newf->contents[0] = CONTENTS_EMPTY;
+ newf->contents[1] = b->contents;
+ outside = newf;
+ }
+}
+
+/*
+==================
+CSGFaces
+
+Builds a list of surfaces containing all of the faces
+==================
+*/
+void CSGFaces (tree_t *tree)
+{
+ brush_t *b1, *b2;
+ int i;
+ qboolean overwrite;
+ face_t *f;
+
+ qprintf ("---- CSGFaces ----\n");
+
+ memset (tree->validfaces, 0, sizeof(tree->validfaces));
+
+ csgfaces = brushfaces = csgmergefaces = 0;
+
+//
+// do the solid faces
+//
+ for (b1=tree->brushes ; b1 ; b1 = b1->next)
+ {
+ // set outside to a copy of the brush's faces
+ CopyFacesToOutside (b1);
+
+ overwrite = false;
+
+ for (b2=tree->brushes ; b2 ; b2 = b2->next)
+ {
+ // see if b2 needs to clip a chunk out of b1
+
+ if (b1==b2)
+ {
+ overwrite = true; // later brushes now overwrite
+ continue;
+ }
+
+ // check bounding box first
+ for (i=0 ; i<3 ; i++)
+ if (b1->mins[i] > b2->maxs[i] || b1->maxs[i] < b2->mins[i])
+ break;
+ if (i<3)
+ continue;
+
+ // divide faces by the planes of the new brush
+
+ inside = outside;
+ outside = NULL;
+
+ for (f=b2->faces ; f ; f=f->next)
+ ClipInside (f->planenum, f->planeside, overwrite);
+
+ // these faces are continued in another brush, so get rid of them
+ if (b1->contents == CONTENTS_SOLID && b2->contents <= CONTENTS_WATER)
+ FreeInside (b2->contents);
+ else
+ FreeInside (CONTENTS_SOLID);
+ }
+
+ // all of the faces left in outside are real surface faces
+ if (b1->contents != CONTENTS_SOLID)
+ SaveOutside (tree, true); // mirror faces for inside view
+ else
+ SaveOutside (tree, false);
+ }
+
+ BuildSurfaces ( tree );
+
+ qprintf ("%5i brushfaces\n", brushfaces);
+ qprintf ("%5i csgfaces\n", csgfaces);
+ qprintf ("%5i mergedfaces\n", csgmergefaces);
+}
+
+
Index: hmap2/faces.c
diff -u /dev/null hmap2/faces.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/faces.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,572 @@
+// surfaces.c
+
+#include "bsp5.h"
+
+/*
+
+a surface has all of the faces that could be drawn on a given plane
+the outside filling stage can remove some of them so a better bsp can be generated
+
+*/
+
+//===========================================================================
+
+/*
+===========
+AllocFace
+===========
+*/
+face_t *AllocFace( void )
+{
+ face_t *f;
+
+ f = qmalloc( sizeof( face_t ) );
+ memset( f, 0, sizeof( face_t ) );
+ f->planenum = -1;
+ f->outputnumber = -1;
+
+ return f;
+}
+
+/*
+===========
+FreeFace
+===========
+*/
+void FreeFace( face_t *f )
+{
+ if( f->winding )
+ FreeWinding( f->winding );
+ qfree( f );
+}
+
+/*
+==================
+NewFaceFromFace
+
+Duplicates the non point information of a face,
+used by SplitFace and MergeFace.
+==================
+*/
+face_t *NewFaceFromFace( face_t *in )
+{
+ face_t *newf;
+
+ newf = AllocFace ();
+ newf->planenum = in->planenum;
+ newf->texturenum = in->texturenum;
+ newf->planeside = in->planeside;
+ newf->original = in->original;
+ newf->contents[0] = in->contents[0];
+ newf->contents[1] = in->contents[1];
+
+ return newf;
+}
+
+//===========================================================================
+
+/*
+==================
+SplitFace
+==================
+*/
+void SplitFace( face_t *in, plane_t *split, face_t **front, face_t **back )
+{
+ winding_t *frontw, *backw;
+
+ if( !in->winding )
+ Error( "SplitFace: freed face" );
+
+ DivideWindingEpsilon( in->winding, split, &frontw, &backw, ON_EPSILON );
+
+ if( !backw ) {
+ *front = in;
+ *back = NULL;
+ return;
+ }
+ if( !frontw ) {
+ *front = NULL;
+ *back = in;
+ return;
+ }
+
+ if( frontw->numpoints < 3 || backw->numpoints < 3 )
+ Error( "SplitFace: numpoints < 3" );
+
+ *front = NewFaceFromFace( in );
+ (*front)->winding = frontw;
+
+ *back = NewFaceFromFace( in );
+ (*back)->winding = backw;
+
+ // free the original face now that is is represented by the fragments
+ FreeFace( in );
+}
+
+/*
+================
+SubdivideFace
+
+If the face is > subdivide_size in either texture direction,
+carve a valid sized piece off and insert the remainder in the next link
+================
+*/
+int SubdivideFace( face_t *f, face_t **prevptr )
+{
+ int i, axis;
+ int subdivides;
+ vec_t mins, maxs, v;
+ plane_t plane;
+ winding_t *w;
+ face_t *front, *back, *next;
+ texinfo_t *tex;
+
+ tex = &texinfo[f->texturenum];
+
+ // special (non-surface cached) faces don't need subdivision
+ if ( tex->flags & TEX_SPECIAL )
+ return 0;
+
+ subdivides = 0;
+ for( axis = 0; axis < 2 ; axis++ ) {
+ while( 1 ) {
+ mins = BOGUS_RANGE;
+ maxs = -BOGUS_RANGE;
+
+ w = f->winding;
+ for( i = 0; i < w->numpoints; i++ ) {
+ v = DotProduct( w->points[i], tex->vecs[axis] );
+ if( v < mins )
+ mins = v;
+ if( v > maxs )
+ maxs = v;
+ }
+
+ if( maxs - mins <= subdivide_size )
+ break;
+
+ // split it
+ VectorCopy( tex->vecs[axis], plane.normal );
+ v = VectorNormalize( plane.normal );
+ if( !v )
+ Error( "SubdivideFace: zero length normal" );
+ plane.dist = (mins + subdivide_size - 16) / v;
+
+ next = f->next;
+ SplitFace( f, &plane, &front, &back );
+
+ if( !front || !back )
+ Error( "SubdivideFace: didn't split the polygon" );
+
+ *prevptr = back;
+ back->next = front;
+ front->next = next;
+ f = back;
+ subdivides++;
+ }
+ }
+
+ return subdivides;
+}
+
+//===========================================================================
+
+/*
+=============
+TryMergeFaces
+
+Returns NULL if the faces couldn't be merged, or the new face.
+The originals will NOT be freed.
+=============
+*/
+static face_t *TryMergeFaces( face_t *f1, face_t *f2 )
+{
+ face_t *newf;
+ vec3_t planenormal;
+ plane_t *plane;
+ winding_t *neww;
+
+ if( !f1->winding || !f2->winding ||
+ f1->planeside != f2->planeside ||
+ f1->texturenum != f2->texturenum ||
+ f1->contents[0] != f2->contents[0] ||
+ f1->contents[1] != f2->contents[1] )
+ return NULL;
+
+ plane = &mapplanes[f1->planenum];
+ VectorCopy( plane->normal, planenormal );
+ if( f1->planeside )
+ VectorNegate( planenormal, planenormal );
+
+ neww = TryMergeWinding( f1->winding, f2->winding, planenormal );
+ if( !neww )
+ return NULL;
+
+ newf = NewFaceFromFace( f1 );
+ newf->winding = neww;
+
+ return newf;
+}
+
+/*
+===============
+MergeFaceToList_r
+===============
+*/
+face_t *MergeFaceToList_r( face_t *face, face_t *list )
+{
+ face_t *newf, *f;
+
+ for( f = list; f; f = f->next ) {
+ newf = TryMergeFaces( face, f );
+ if( !newf )
+ continue;
+
+ FreeFace( face );
+ FreeWinding( f->winding );
+ f->winding = NULL; // merged out
+
+ return MergeFaceToList_r( newf, list );
+ }
+
+ // didn't merge, so add at start
+ face->next = list;
+ return face;
+}
+
+/*
+===============
+FreeMergeListScraps
+===============
+*/
+face_t *FreeMergeListScraps( face_t *merged )
+{
+ face_t *head, *next;
+
+ for( head = NULL; merged ; merged = next) {
+ next = merged->next;
+
+ if( !merged->winding ) {
+ FreeFace( merged );
+ continue;
+ }
+
+ merged->next = head;
+ head = merged;
+ }
+
+ return head;
+}
+
+/*
+===============
+MergePlaneFaces
+===============
+*/
+static int MergePlaneFaces( surface_t *plane )
+{
+ int count;
+ face_t *f1, *next;
+ face_t *merged;
+
+ merged = NULL;
+ for( f1 = plane->faces; f1; f1 = next ) {
+ next = f1->next;
+ merged = MergeFaceToList_r( f1, merged );
+ }
+
+ // chain all of the non-empty faces to the plane
+ plane->faces = FreeMergeListScraps( merged );
+
+ for( f1 = plane->faces, count = 0; f1; f1 = f1->next, count++ );
+
+ return count;
+}
+
+/*
+============
+MergeTreeFaces
+============
+*/
+void MergeTreeFaces( tree_t *tree )
+{
+ surface_t *surf;
+ int mergefaces;
+
+ printf( "---- MergeTreeFaces ----\n" );
+
+ mergefaces = 0;
+ for( surf = tree->surfaces; surf; surf = surf->next )
+ mergefaces += MergePlaneFaces( surf );
+
+ printf( "%i mergefaces\n", mergefaces );
+}
+
+//===========================================================================
+
+/*
+================
+GatherNodeFaces_r
+================
+*/
+static void GatherNodeFaces_r( node_t *node, face_t **validfaces )
+{
+ face_t *f, *next;
+
+ if( node->planenum != PLANENUM_LEAF ) { // decision node
+ for( f = node->faces; f; f = next ) {
+ next = f->next;
+
+ if( !f->winding ) { // face was removed outside
+ FreeFace( f );
+ continue;
+ }
+
+ f->next = validfaces[f->planenum];
+ validfaces[f->planenum] = f;
+ }
+
+ GatherNodeFaces_r( node->children[0], validfaces );
+ GatherNodeFaces_r( node->children[1], validfaces );
+ }
+
+ FreeNode( node );
+}
+
+/*
+================
+GatherTreeFaces
+
+Frees the current node tree and returns a new chain of
+the surfaces that have inside faces.
+================
+*/
+void GatherTreeFaces( tree_t *tree )
+{
+ memset( tree->validfaces, 0, sizeof( tree->validfaces ) );
+
+ GatherNodeFaces_r( tree->headnode, tree->validfaces );
+
+ BuildSurfaces( tree );
+}
+
+//===========================================================================
+
+typedef struct hashvert_s
+{
+ vec3_t point;
+ int num;
+ int numplanes; // for corner determination
+ int planenums[2];
+ int numedges;
+ struct hashvert_s *next;
+} hashvert_t;
+
+#define NUM_HASH 8192
+
+#define POINT_EPSILON 0.01
+
+static hashvert_t hvertex[MAX_MAP_VERTS];
+static hashvert_t *hvert_p;
+
+static hashvert_t *hashverts[NUM_HASH];
+
+static face_t *edgefaces[MAX_MAP_EDGES][2];
+static int firstmodeledge;
+
+static vec3_t hash_min, hash_scale;
+
+static void InitHash( vec3_t mins, vec3_t maxs )
+{
+ int i;
+ vec3_t size;
+ vec_t scale;
+ int newsize[2];
+
+ memset( hashverts, 0, sizeof( hashverts ) );
+
+ for( i = 0; i < 3; i++ ) {
+ hash_min[i] = mins[i];
+ size[i] = maxs[i] - mins[i];
+ }
+
+ scale = sqrt( size[0] * size[1] / NUM_HASH );
+
+ newsize[0] = size[0] / scale;
+ newsize[1] = size[1] / scale;
+
+ hash_scale[0] = newsize[0] / size[0];
+ hash_scale[1] = newsize[1] / size[1];
+ hash_scale[2] = newsize[1];
+
+ hvert_p = hvertex;
+}
+
+static unsigned HashVec( vec3_t vec )
+{
+ unsigned h;
+
+ h = hash_scale[0] * (vec[0] - hash_min[0]) * hash_scale[2] + hash_scale[1] * (vec[1] - hash_min[1]);
+ return h % NUM_HASH;
+}
+
+//============================================================================
+
+/*
+=============
+GetVertex
+=============
+*/
+static int GetVertex( vec3_t in, int planenum )
+{
+ int i;
+ int h;
+ hashvert_t *hv;
+ vec3_t vert;
+
+ for( i = 0; i < 3; i++ ) {
+ h = Q_rint( in[i] );
+
+ if( fabs( in[i] - h ) < 0.001 )
+ vert[i] = h;
+ else
+ vert[i] = in[i];
+ }
+
+ h = HashVec( vert );
+ for( hv = hashverts[h]; hv; hv = hv->next ) {
+ if( fabs( hv->point[0] - vert[0] ) < POINT_EPSILON
+ && fabs( hv->point[1] - vert[1] ) < POINT_EPSILON
+ && fabs( hv->point[2] - vert[2] ) < POINT_EPSILON ) {
+ hv->numedges++;
+
+ if( hv->numplanes == 3 )
+ return hv->num; // allready known to be a corner
+
+ for( i = 0; i < hv->numplanes; i++ ) {
+ if( hv->planenums[i] == planenum )
+ return hv->num; // allready know this plane
+ }
+
+ if( hv->numplanes != 2 )
+ hv->planenums[hv->numplanes] = planenum;
+ hv->numplanes++;
+ return hv->num;
+ }
+ }
+
+ hv = hvert_p;
+ hv->numedges = 1;
+ hv->numplanes = 1;
+ hv->planenums[0] = planenum;
+ hv->next = hashverts[h];
+ hv->num = numvertexes;
+ VectorCopy( vert, hv->point );
+ hashverts[h] = hv;
+ hvert_p++;
+
+ EmitVertex( vert );
+
+ return hv->num;
+}
+
+/*
+==================
+EmitFaceEdge
+
+Don't allow four way edges
+==================
+*/
+static int EmitFaceEdge( vec3_t p1, vec3_t p2, face_t *f )
+{
+ int i;
+ int v1, v2;
+ dedge_t *edge;
+
+ v1 = GetVertex( p1, f->planenum );
+ v2 = GetVertex( p2, f->planenum );
+ for( i = firstmodeledge; i < numedges; i++ ) {
+ edge = &dedges[i];
+
+ if( v1 == edge->v[1] && v2 == edge->v[0] && !edgefaces[i][1]
+ && edgefaces[i][0]->contents[0] == f->contents[0] ) {
+ edgefaces[i][1] = f;
+ return -i;
+ }
+ }
+
+ edgefaces[i][0] = f;
+
+ EmitEdge( v1, v2 );
+
+ return i;
+}
+
+/*
+==============
+EmitNodeFaces_r
+==============
+*/
+static void EmitNodeFaces_r( node_t *node )
+{
+ int i;
+ dface_t *f;
+ face_t *face;
+ winding_t *w;
+
+ if( node->planenum == PLANENUM_LEAF )
+ return;
+
+ node->firstface = numfaces;
+ for( face = node->faces; face; face = face->next ) {
+ if( numfaces == MAX_MAP_FACES )
+ Error( "numfaces == MAX_MAP_FACES" );
+
+ f = &dfaces[numfaces]; // emit a face
+ f->planenum = node->outputplanenum;
+ f->side = face->planeside;
+ f->texinfo = face->texturenum;
+ f->lightofs = -1;
+ for( i = 0; i < MAXLIGHTMAPS; i++ )
+ f->styles[i] = 255;
+
+ // add the face and mergable neighbors to it
+ f->firstedge = numsurfedges;
+ for( i = 0, w = face->winding; i < w->numpoints; i++ ) {
+ if( numsurfedges == MAX_MAP_SURFEDGES )
+ Error( "numsurfedges == MAX_MAP_SURFEDGES" );
+ dsurfedges[numsurfedges++] = EmitFaceEdge( w->points[i], w->points[(i+1) % w->numpoints], face );
+ }
+ f->numedges = numsurfedges - f->firstedge;
+
+ face->outputnumber = numfaces++;
+ }
+
+ node->numfaces = numfaces - node->firstface;
+
+ EmitNodeFaces_r( node->children[0] );
+ EmitNodeFaces_r( node->children[1] );
+}
+
+/*
+================
+EmitNodeFaces
+================
+*/
+void EmitNodeFaces( node_t *headnode )
+{
+ vec_t radius;
+ vec3_t maxs, mins;
+
+// qprintf( "--- EmitNodeFaces ---\n" );
+
+ // origin points won't allways be inside the map, so extend the hash area
+ radius = RadiusFromBounds( headnode->mins, headnode->maxs );
+ VectorSet( maxs, radius, radius, radius );
+ VectorSet( mins, -radius, -radius, -radius );
+
+ InitHash( headnode->mins, headnode->maxs );
+
+ firstmodeledge = numedges;
+ EmitNodeFaces_r( headnode );
+}
Index: hmap2/fast.bat
diff -u /dev/null hmap2/fast.bat:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/fast.bat Sun Feb 15 21:50:37 2004
@@ -0,0 +1,3 @@
+ at hmap %1
+ at hmap -vis -fast %1
+ at hmap -light %1
Index: hmap2/full.bat
diff -u /dev/null hmap2/full.bat:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/full.bat Sun Feb 15 21:50:37 2004
@@ -0,0 +1,3 @@
+ at hmap %1
+ at hmap -vis %1
+ at hmap -light -extra8x8 %1
Index: hmap2/hmap2.dsp
diff -u /dev/null hmap2/hmap2.dsp:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/hmap2.dsp Sun Feb 15 21:50:37 2004
@@ -0,0 +1,234 @@
+# Microsoft Developer Studio Project File - Name="hmap2" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=hmap2 - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "hmap2.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "hmap2.mak" CFG="hmap2 - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "hmap2 - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "hmap2 - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "hmap2 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /Ob2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 winmm.lib /nologo /subsystem:console /machine:I386 /out:"hmap2.exe"
+
+!ELSEIF "$(CFG)" == "hmap2 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 winmm.lib /nologo /subsystem:console /debug /machine:I386 /out:"hmap2.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "hmap2 - Win32 Release"
+# Name "hmap2 - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\brush.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\bsp2prt.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\bspfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\cmdlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\csg4.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\faces.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\light.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\light_face.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\light_trace.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\map.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mathlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mem.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\outside.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\portals.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\qbsp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\scriptlib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\solidbsp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\tjunc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\tree.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\vis.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\vis_flow.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\vis_sound.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\wad.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\winding.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\writebsp.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\bsp5.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\bspfile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\cmdlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\light.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\map.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\mathlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\mem.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\vis.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\winding.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
Index: hmap2/hmapreadme.txt
diff -u /dev/null hmap2/hmapreadme.txt:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/hmapreadme.txt Sun Feb 15 21:50:37 2004
@@ -0,0 +1,83 @@
+-====== hmap2 ======-
+Author: Vic
+Email: vic at quakesrc.org
+
+Additions beyond standard hqbsp+hlight+hvis features:
+
+hmap:
+hqbsp + hlight + hvis + bsp2prt in one exe
+
+general:
+no memory leaks
+general code cleanup
+rewrote many parts of the code to run faster and be easier to read
+bsp2prt allows maps to be re-vised
+
+hqbsp:
+no hull files
+support for maps where "worldspawn" is not the first entity
+support for "func_group" entities
+reduced memory usage
+misc bugfixes
+
+hlight:
+8x8 lightmaps antialiasing (-extra8x8 option)
+
+hvis:
+better vis compression (-reuse option, DO NOT use to revis old maps)
+fixed ambient sounds calculations (they now fade with distance)
+-ambientslime enables the unused slime sound channel (not supported by most (all?) quake engines, normally slime uses the water channel)
+
+-=================-
+
+hmap utilities by Forest "LordHavoc" Hale ( lordhavoc at users.sourceforge.net )
+Based on source code from id Software (Quake utilities) and Ritual Entertainment (Scourge of Armagon mission pack)
+Website: http://darkplaces.gamevisions.com
+
+Run the utilities with no parameters in a DOS prompt or Linux/UNIX terminal to get information about each.
+
+Or read the source code (qbsp.c, vis.c, or light.c, respectively) to see the same usage information.
+
+The sourcecode zip includes a Makefile for GNU make, a Makefile.mingw for mingw (make -f Makefile.mingw), and Microsoft Visual C++ 6.0 project files (open the hmap.dsw workspace).
+
+How to use the new hlight light properties:
+"wait" - affects size of light without affecting it's intensity - 1.0 default, 0.5 = bigger radius, 2 = smaller.
+"_color" - 3 values (red green blue), specifies color of light, the scale of the numbers does not matter ("1 3 2.5" is identical to "1000 3000 2500").
+"_lightradius" - limits light to this radius (and darkens light to make it blend well at the edges), only intended for switchable light styles so they don't cover whole rooms if you don't want them to.
+"light" - can be one of four different formats:
+quake: "light" "100" (intensity) white light but can be colored by "_color", this example is white light of intensity 100
+hlight: "light" "100 50 30" (red green blue) this is the same as using "light" "100" "_color" "1 0.5 0.3", this example is orange light of intensity 100
+halflife: "light" "100 255 255 200" (intensity red green blue) red/green/blue are 0-255 range, this example is yellowish-white light of intensity 100
+
+hqbsp features:
+Most errors report where the problem is (whether by line number in the .map file, or location in the map editor).
+CheckFace: point off plane is now only a warning, and tries to self-correct.
+Rewritten wad loader which should be far less finicky than the original, and allows multiple wads.
+Water/slime/lava is transparent by default (hqbsp -nowater to disable this).
+-darkplaces option to increase subdivide size to 4080 (really huge polygons), can make a map much faster but requires darkplaces engine to be used, or any other engine implementing 256x256 texel lightmaps.
+Support for very large maps (up to +-32767 units, the very limits of the .bsp file format).
+Hipnotic rotation support.
+Greatly increased limits for everything.
+Can vis a leaky map (use hqbsp -noforcevis if you want vis to fail on a leaky map).
+Shows useful information if run with no parameters.
+Can compile .map files using HalfLife WorldCraft texture alignment
+
+hvis features:
+Can disable liquid sounds (-noambient, -noambientwater, -noambientsky, -noambientlava (quake never used ambient lava sounds though)).
+RVis optimization for faster vising (this does cause a 0.001% degradation in vis quality, too small to care about, -norvis to disable).
+Defaults to -level 4 vis quality (which is often faster than vis's default of level 2).
+Shows useful information if run with no parameters.
+
+hlight features:
+More realistic attenuation. (original light.exe method is not supported in any way, nor are tyrlite modes, this is intentional; only realistic is supported)
+Falloff scaling from arghlite. (but remember the attenuation is quite different already)
+Colored lighting (.lit file) for enhanced engines while not breaking classic quake.
+16 sample lightmap antialiasing (hlight -extra4x4).
+Better handling of floor-meets-wall seams in lighting, none of those messy problem cases seen in the original light.exe.
+-relight option to make a .lit file for an existing .bsp without modifying the .bsp in any way (this will not add color, unless the lights contain color info already).
+Support for extremely large polygons as produced by hqbsp -darkplaces.
+Hipnotic rotation support.
+Uses vis data to light map faster (-nolightvis to ignore the vis data).
+Shows useful information if run with no parameters.
+
+Probably other features I forgot to mention...
Index: hmap2/light.c
diff -u /dev/null hmap2/light.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/light.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,595 @@
+// lighting.c
+
+#include "light.h"
+
+/*
+
+NOTES
+-----
+
+*/
+
+dmodel_t *bspmodel;
+
+qboolean lightvis;
+qboolean relight;
+qboolean verbose;
+
+int extrasamplesbit; // power of 2 extra sampling (0 = 1x1 sampling, 1 = 2x2 sampling, 2 = 4x4 sampling, etc)
+vec_t extrasamplesscale; // 1.0 / pointspersample (extrasamples related)
+vec_t globallightscale;
+
+// filename to write light list to
+char lightsfilename[1024];
+
+byte currentvis[(MAX_MAP_LEAFS + 7) / 8];
+
+int c_occluded;
+
+int num_directlights;
+directlight_t directlights[MAP_DIRECTLIGHTS];
+
+/*
+==============================================================================
+
+ENTITY FILE PARSING
+
+If a light has a targetname, generate a unique style in the 32-63 range
+==============================================================================
+*/
+
+int numlighttargets;
+char lighttargets[32][128];
+
+int LightStyleForTargetname( char *targetname )
+{
+ int i;
+
+ for( i = 0; i < numlighttargets; i++ ) {
+ if( !strcmp (lighttargets[i], targetname) )
+ return 32 + i;
+ }
+
+ if( numlighttargets == 32 )
+ Error( "LightStyleForTargetname: numlighttargets == 32" );
+
+ strcpy( lighttargets[numlighttargets], targetname );
+ numlighttargets++;
+ return numlighttargets - 1 + 32;
+}
+
+
+/*
+==================
+ParseLightEntities
+==================
+*/
+void ParseLightEntities( void )
+{
+ int i, j;
+ entity_t *ent;
+ char *value, *targetname, *style;
+ directlight_t *l;
+ double vec[4], color2[3];
+ qboolean isLight;
+
+ num_directlights = 0;
+ for( i = 0, ent = entities; i < num_entities; i++, ent++ ) {
+ value = ValueForKey( ent, "classname" );
+
+ if( strncmp (value, "light", 5) )
+ continue;
+
+ if( !strcmp (value, "light") )
+ isLight = true;
+ else
+ isLight = false;
+
+ if( num_directlights == MAP_DIRECTLIGHTS )
+ Error( "numdirectlights == MAP_DIRECTLIGHTS" );
+
+ l = &directlights[num_directlights++];
+ memset( l, 0, sizeof (*l) );
+ color2[0] = color2[1] = color2[2] = 1.0f;
+ l->color[0] = l->color[1] = l->color[2] = 1.0f;
+ l->lightoffset = LIGHTDISTBIAS;
+ GetVectorForKey( ent, "origin", l->origin );
+
+ l->falloff = FloatForKey( ent, "wait" );
+ if( !l->falloff )
+ l->falloff = DEFAULTFALLOFF;
+ l->falloff *= l->falloff; // square it for lighting calculations
+
+ l->lightradius = FloatForKey( ent, "_lightradius" );
+
+ l->style = FloatForKey( ent, "style" );
+ if( (unsigned)l->style > 254 )
+ Error( "LoadLights: Bad light style %i (must be 0-254)", l->style );
+
+ l->angle = FloatForKey( ent, "angle" );
+
+ value = ValueForKey( ent, "color" );
+ if( !value[0] )
+ value = ValueForKey( ent, "_color" );
+ if( value[0] ) {
+ // scan into doubles, then assign
+ // which makes it vec_t size independent
+ if( sscanf (value, "%lf %lf %lf", &vec[0], &vec[1], &vec[2]) != 3 )
+ Error( "LoadEntities: not 3 values for color" );
+
+ // scale the color to have at least one component at 1.0
+ vec[3] = vec[0];
+ if( vec[1] > vec[3] )
+ vec[3] = vec[1];
+ if( vec[2] > vec[3] )
+ vec[3] = vec[2];
+ if( vec[3] != 0.0 )
+ vec[3] = 1.0 / vec[3];
+ color2[0] = vec[0] * vec[3];
+ color2[1] = vec[1] * vec[3];
+ color2[2] = vec[2] * vec[3];
+ }
+
+ value = ValueForKey( ent, "light" );
+ if( !value[0] )
+ value = ValueForKey( ent, "_light" );
+
+ if( value[0] ) {
+ i = sscanf ( value, "%lf %lf %lf %lf", &vec[0], &vec[1], &vec[2], &vec[3] );
+
+ switch( i ) {
+ case 4:// HalfLife light
+ l->light = (int)vec[3];
+ l->color[0] = vec[0] * (1.0f / 255.0f);
+ l->color[1] = vec[1] * (1.0f / 255.0f);
+ l->color[2] = vec[2] * (1.0f / 255.0f);
+ break;
+ case 3:
+ l->light = 1;
+ l->color[0] = vec[0];
+ l->color[1] = vec[1];
+ l->color[2] = vec[2];
+ break;
+ case 1:
+ l->light = (int)vec[0];
+ l->color[0] = 1.0f;
+ l->color[1] = 1.0f;
+ l->color[2] = 1.0f;
+ break;
+ default:
+ Error( "LoadEntities: _light (or light) key must be 1 (Quake), 4 (HalfLife), or 3 (HLight) values, \"%s\" is not valid\n", value );
+ }
+ }
+
+ if( !l->light )
+ l->light = DEFAULTLIGHTLEVEL;
+
+ // convert to subtraction to the brightness for the whole light, so it will fade nicely, rather than being clipped off
+ l->color[0] *= color2[0] * l->light * /*l->falloff */ 16384.0 * globallightscale;
+ l->color[1] *= color2[1] * l->light * /*l->falloff */ 16384.0 * globallightscale;
+ l->color[2] *= color2[2] * l->light * /*l->falloff */ 16384.0 * globallightscale;
+
+ if( l->lightradius )
+ l->subbrightness = 1.0 / (l->lightradius * l->lightradius * l->falloff + LIGHTDISTBIAS);
+ if( l->subbrightness < (1.0 / 1048576.0) )
+ l->subbrightness = (1.0 / 1048576.0);
+
+ if( isLight ) {
+ value = ValueForKey( ent, "targetname" );
+
+ if( value[0] && !l->style ) {
+ char s[16];
+
+ l->style = LightStyleForTargetname( value );
+
+ memset( s, 0, sizeof(s) );
+ sprintf( s, "%i", l->style );
+ SetKeyValue( ent, "style", s );
+ }
+ }
+
+ value = ValueForKey( ent, "target" );
+ if( !value[0] )
+ continue;
+
+ for( j = 0; j < num_entities; j++ ) {
+ if( i == j )
+ continue;
+
+ targetname = ValueForKey( &entities[j], "targetname" );
+ if( !strcmp (targetname, value) ) {
+ vec3_t origin;
+
+ GetVectorForKey( &entities[j], "origin", origin );
+
+ // set up spotlight values for lighting code to use
+ VectorSubtract( origin, l->origin, l->spotdir );
+ VectorNormalize( l->spotdir );
+
+ if( !l->angle )
+ l->spotcone = -cos( 20 * Q_PI / 180 );
+ else
+ l->spotcone = -cos( l->angle / 2 * Q_PI / 180 );
+
+ if( l->spotcone <= 0 ) {
+ VectorClear( l->spotdir );
+ l->spotcone = 0;
+ }
+ break;
+ }
+ }
+
+ if( j == num_entities ) {
+ printf( "WARNING: light at (%i,%i,%i) has unmatched target\n", (int)l->origin[0], (int)l->origin[1], (int)l->origin[2]);
+ continue;
+ }
+
+ // set the style on the source ent for switchable lights
+ style = ValueForKey( &entities[j], "style" );
+ if( style[0] && atof (style) ) {
+ char s[16];
+
+ l->style = atof( style );
+ if( (unsigned)l->style > 254 )
+ Error( "LoadLights: Bad target light style %i (must be 0-254)", l->style );
+
+ memset( s, 0, sizeof(s) );
+ sprintf( s, "%i", l->style );
+ SetKeyValue( ent, "style", s );
+ }
+ }
+}
+
+void WriteLights( void )
+{
+ int i;
+ FILE *f;
+ directlight_t *l;
+
+ printf ("building .lights file\n");
+
+ f = fopen( lightsfilename, "wb" );
+ for( i = 0, l = directlights; i < num_directlights; i++, l++ )
+ fprintf( f, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d\n", (double)l->origin[0], (double)l->origin[1], (double)l->origin[2], (double)l->falloff, (double)l->color[0], (double)l->color[1], (double)l->color[2], (double)l->subbrightness, (double)l->spotdir[0], (double)l->spotdir[1], (double)l->spotdir[2], (double)l->spotcone, (double)l->lightoffset, l->style );
+ fclose( f );
+}
+
+dleaf_t *Light_PointInLeaf(vec3_t point)
+{
+ int num;
+ num = 0;
+ while (num >= 0)
+ num = dnodes[num].children[DotProduct(point, dplanes[dnodes[num].planenum].normal) < dplanes[dnodes[num].planenum].dist];
+ return dleafs + (-1 - num);
+}
+
+#define LIGHTCHAINS (MAX_MAP_FACES * 32)
+
+lightchain_t *surfacelightchain[MAX_MAP_FACES];
+lightchain_t lightchainbuf[LIGHTCHAINS];
+byte surfacehit[MAX_MAP_FACES];
+directlight_t *novislight[MAX_MAP_ENTITIES];
+directlight_t *alllight[MAX_MAP_ENTITIES];
+int novislights, alllights;
+int lightchainbufindex;
+
+/*
+=============
+LightWorld
+=============
+*/
+extern int dlightdatapos;
+void LightWorld (void)
+{
+ int i, k, n, m, count;
+ unsigned short *mark;
+ time_t lightstarttime, oldtime, newtime;
+ directlight_t *light;
+ dleaf_t *leaf;
+ int lightcount = 0, castcount = 0, emptycount = 0, solidcount = 0, watercount = 0, slimecount = 0, lavacount = 0, skycount = 0, misccount = 0, ignorevis;
+ vec3_t org;
+ char name[8];
+ entity_t *ent;
+// filebase = file_p = dlightdata;
+// file_end = filebase + MAX_MAP_LIGHTING;
+ if (!relight)
+ lightdatasize = 0;
+ rgblightdatasize = 0;
+ lightstarttime = time(NULL);
+
+ lightchainbufindex = 0;
+ novislights = alllights = 0;
+ memset(surfacelightchain, 0, sizeof(surfacelightchain));
+
+ // LordHavoc: find the right leaf for each entity
+ for (i = 0, light = directlights;i < num_directlights;i++, light++)
+ {
+ lightcount++;
+ alllight[alllights++] = light;
+ leaf = Light_PointInLeaf(light->origin);
+ ignorevis = false;
+ switch (leaf->contents)
+ {
+ case CONTENTS_EMPTY:
+ emptycount++;
+ break;
+ case CONTENTS_SOLID:
+ solidcount++;
+ ignorevis = true;
+ break;
+ case CONTENTS_WATER:
+ watercount++;
+ break;
+ case CONTENTS_SLIME:
+ slimecount++;
+ break;
+ case CONTENTS_LAVA:
+ lavacount++;
+ break;
+ case CONTENTS_SKY:
+ skycount++;
+ ignorevis = true;
+ break;
+ default:
+ misccount++;
+ break;
+ }
+ if (leaf->visofs == -1 || ignorevis || !lightvis)
+ {
+ /*
+ if ((lightchainbufindex + numfaces) > LIGHTCHAINS)
+ Error("LightWorld: ran out of light chains! complain to maintainer of hlight\n");
+ for (m = 0;m < numfaces;m++)
+ {
+ castcount++;
+ lightchainbuf[lightchainbufindex].light = entity;
+ lightchainbuf[lightchainbufindex].next = surfacelightchain[m];
+ surfacelightchain[m] = &lightchainbuf[lightchainbufindex++];
+ }
+ */
+ castcount += numfaces;
+ novislight[novislights++] = light;
+ }
+ else
+ {
+ DecompressVis(dvisdata + leaf->visofs, currentvis, (numleafs + 7) >> 3);
+ memset(surfacehit, 0, numfaces);
+ for (n = 0, leaf = dleafs+1;n < numleafs;n++, leaf++) // leafs begin at 1
+ {
+ if (!leaf->nummarksurfaces)
+ continue;
+ if (currentvis[n >> 3] & (1 << (n & 7)))
+ {
+ if ((lightchainbufindex + leaf->nummarksurfaces) > LIGHTCHAINS)
+ Error("LightWorld: ran out of light chains! complain to maintainer of hlight\n");
+ for (m = 0, mark = dmarksurfaces + leaf->firstmarksurface;m < leaf->nummarksurfaces;m++, mark++)
+ {
+ if (surfacehit[*mark])
+ continue;
+ surfacehit[*mark] = true;
+ castcount++;
+ lightchainbuf[lightchainbufindex].light = light;
+ lightchainbuf[lightchainbufindex].next = surfacelightchain[*mark];
+ surfacelightchain[*mark] = &lightchainbuf[lightchainbufindex++];
+ }
+ }
+ }
+ }
+ }
+
+ printf("%4i lights, %4i air, %4i solid, %4i water, %4i slime, %4i lava, %4i sky, %4i unknown\n", lightcount, emptycount, solidcount, watercount, slimecount, lavacount, skycount, misccount);
+
+ i = 0;
+ for (m = 0;m < numfaces;m++)
+ if (surfacelightchain[m])
+ i++;
+ printf("%5i faces, %5i (%3i%%) may receive light\n", numfaces, i, i * 100 / numfaces);
+
+ if (solidcount || skycount)
+ printf("warning: %4i lights of %4i lights (%3i%%) were found in sky or solid and will not be accelerated using vis, move them out of the solid or sky to accelerate compiling\n", solidcount+skycount, lightcount, (solidcount+skycount) * 100 / lightcount);
+
+ printf("%4i lights will be cast onto %5i surfaces, %10i casts will be performed\n", lightcount, numfaces, castcount);
+
+ // LordHavoc: let there be light
+ count = dmodels[0].numfaces;
+// k = 1;
+// j = (int) ((double) count * (double) k * (1.0 / 100.0));
+ org[0] = org[1] = org[2] = 0;
+ oldtime = time(NULL);
+ for (m = 0;m < count;)
+ {
+ LightFace (dfaces + m + dmodels[0].firstface, surfacelightchain[m + dmodels[0].firstface], novislight, novislights, org);
+ m++;
+ newtime = time(NULL);
+ if (newtime != oldtime)
+ {
+ printf ("\rworld face %5i of %5i (%3i%%), estimated time left: %5i ", m, count, (int) (m*100)/count, (int) (((count-m)*(newtime-lightstarttime))/m));
+ fflush(stdout);
+ oldtime = newtime;
+ }
+ }
+ printf ("\n%5i faces done\nlightdatasize: %i\n", numfaces, lightdatasize);
+ printf ("c_occluded: %i\n", c_occluded);
+
+ printf("\nlighting %5i submodels:\n", nummodels);
+ fflush(stdout);
+ // LordHavoc: light bmodels
+ for (k = 1;k < nummodels;k++)
+ {
+ newtime = time(NULL);
+ if (newtime != oldtime)
+ {
+ m = k;
+ count = nummodels;
+ printf ("\rsubmodel %3i of %3i (%3i%%), estimated time left: %5i ", m, count, (int) (m*100)/count, (int) (((count-m)*(newtime-lightstarttime))/m));
+ fflush(stdout);
+ oldtime = newtime;
+ }
+ sprintf(name, "*%d", k);
+ ent = FindEntityWithKeyPair("model", name);
+ if (!ent)
+ Error("FindFaceOffsets: Couldn't find entity for model %s.\n", name);
+
+ org[0] = org[1] = org[2] = 0;
+ if (!strncmp(ValueForKey (ent, "classname"), "rotate_", 7))
+ GetVectorForKey(ent, "origin", org);
+
+ for (m = 0;m < dmodels[k].numfaces;m++)
+ LightFace (dfaces + m + dmodels[k].firstface, NULL, alllight, alllights, org);
+ }
+
+// lightdatasize = file_p - filebase;
+// rgblightdatasize = lightdatasize * 3;
+
+ printf ("\n%5i submodels done\nlightdatasize: %i\n", nummodels, lightdatasize);
+ printf ("c_occluded: %i\n", c_occluded);
+}
+
+void CheckLightmaps(void);
+
+/*
+========
+Light_Main
+
+light modelfile
+========
+*/
+int Light_Main (int argc, char **argv)
+{
+ char source[1024];
+ double start, end;
+ int i;
+
+// LordHavoc
+ printf ("hlight 1.04 by LordHavoc\n");
+ printf ("based on id Software's quake light utility source code\n");
+ extrasamplesbit = 0;
+ lightvis = true;
+ relight = false;
+ globallightscale = 1.0;
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i],"-extra"))
+ {
+ extrasamplesbit = 1;
+ printf ("2x2 sampling enabled (tip: -extra4x4 is even higher quality)\n");
+ }
+ else if (!strcmp(argv[i],"-extra4x4"))
+ {
+ extrasamplesbit = 2;
+ printf ("4x4 sampling enabled\n");
+ }
+ else if (!strcmp(argv[i],"-extra8x8"))
+ {
+ extrasamplesbit = 3;
+ printf ("8x8 sampling enabled\n");
+ }
+ else if (!strcmp(argv[i],"-nolightvis"))
+ {
+ printf("use of vis data to optimize lighting disabled\n");
+ lightvis = false;
+ }
+ else if (!strcmp(argv[i],"-relight"))
+ {
+ printf("relighting map to create .lit file without modifying .bsp\n");
+ relight = true;
+ }
+ else if (!strcmp(argv[i],"-intensity"))
+ {
+ i++;
+ if (i >= argc)
+ Error("no value was given to -intensity\n");
+ globallightscale = atof(argv[i]);
+ if (globallightscale < 0.01)
+ globallightscale = 0.01;
+ }
+ else if (argv[i][0] == '-')
+ Error ("Unknown option \"%s\"", argv[i]);
+ else
+ break;
+ }
+
+ extrasamplesscale = 1.0f / (1 << (extrasamplesbit * 2));
+
+// LordHavoc
+ if (i != argc - 1)
+ Error ("%s",
+"usage: hlight [options] bspfile\n"
+"Quick usage notes for entities: (place these in key/value pairs)\n"
+"wait - falloff rate (1.0 default, 0.5 = bigger radius, 2 = smaller, affects area the light covers)\n"
+"_color - 3 values (red green blue), specifies color of light, the scale of the numbers does not matter (\"1 3 2.5\" is identical to \"1000 3000 2500\")\n"
+"_lightradius - limits light to this radius (and darkens light to make it blend well at the edges)\n"
+"What the options do:\n"
+"-extra antialiased lighting (takes much longer, higher quality)\n"
+"-extra4x4 antialiased lighting (even slower and higher quality than -extra)\n"
+"-extra8x8 antialiased lighting (even slower and higher quality than -extra4x4)\n"
+"-nolightvis disables use of visibility data to optimize lighting\n"
+"-relight makes a .lit file for an existing map without modifying the .bsp\n"
+ );
+
+ // init memory
+ Q_InitMem ();
+
+ printf ("----- LightFaces ----\n");
+
+// InitThreads ();
+
+ start = I_DoubleTime ();
+
+ strcpy(source, argv[i]);
+ strcpy(lightsfilename, source);
+ DefaultExtension(source, ".bsp");
+ ReplaceExtension(lightsfilename, ".lights");
+
+ LoadBSPFile (source);
+ memset(dlightdata, 0, sizeof(dlightdata));
+ memset(drgblightdata, 0, sizeof(drgblightdata));
+ //for (i = 0;i < sizeof(dlightdata);i++)
+ // dlightdata[i] = i;
+ CheckLightmaps();
+
+ if (!visdatasize)
+ {
+ printf("no visibility data found (run -vis before -light to compile faster)\n");
+ lightvis = false;
+ }
+
+ ParseEntities ();
+ printf ("%i entities read\n", num_entities);
+
+ ParseLightEntities ();
+
+ CheckLightmaps();
+
+ MakeTnodes (&dmodels[0]);
+ CheckLightmaps();
+
+ LightWorld ();
+ CheckLightmaps();
+
+ WriteLights ();
+
+ UnparseEntities ();
+
+ CheckLightmaps();
+ WriteBSPFile (source, relight);
+ CheckLightmaps();
+
+ end = I_DoubleTime ();
+ printf ("%5.2f seconds elapsed\n\n", end-start);
+
+ // print memory stats
+ Q_PrintMem ();
+
+#if _MSC_VER && _DEBUG
+ printf("press any key\n");
+ getchar();
+#endif
+
+ // free allocated memory
+ Q_ShutdownMem ();
+
+ return 0;
+}
+
Index: hmap2/light.h
diff -u /dev/null hmap2/light.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/light.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,68 @@
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "mem.h"
+
+#define ON_EPSILON 0.1
+
+#define MAXLIGHTS 1024
+#define LIGHTDISTBIAS 65536.0
+
+#define DEFAULTLIGHTLEVEL 300
+#define DEFAULTFALLOFF 1.0f
+
+extern char lightsfilename[1024];
+
+typedef struct
+{
+ vec3_t origin;
+ vec_t angle;
+ int light;
+ int style;
+
+ vec_t falloff;
+ vec_t lightradius;
+ vec_t subbrightness;
+ vec_t lightoffset;
+ vec3_t color;
+ vec3_t spotdir;
+ vec_t spotcone;
+} directlight_t;
+
+typedef struct lightchain_s
+{
+ directlight_t *light;
+ struct lightchain_s *next;
+} lightchain_t;
+
+typedef struct
+{
+ qboolean startSolid;
+ vec_t fraction;
+ vec3_t impact;
+ vec3_t filter;
+ plane_t plane;
+} lightTrace_t;
+
+int Light_PointContents( vec3_t p );
+qboolean Light_TraceLine (lightTrace_t *trace, vec3_t start, vec3_t end);
+
+void LightFace (dface_t *f, lightchain_t *lightchain, directlight_t **novislight, int novislights, vec3_t faceorg);
+void LightLeaf (dleaf_t *leaf);
+
+void MakeTnodes (dmodel_t *bm);
+
+extern int c_occluded;
+extern int c_culldistplane, c_proper;
+
+extern qboolean relight;
+
+#define MAP_DIRECTLIGHTS MAX_MAP_ENTITIES
+
+extern int num_directlights;
+extern directlight_t directlights[MAP_DIRECTLIGHTS];
+
+extern int extrasamplesbit; // power of 2 extra sampling (0 = 1x1 sampling, 1 = 2x2 sampling, 2 = 4x4 sampling, etc)
+extern vec_t extrasamplesscale; // 1.0 / pointspersample (extrasamples related)
+extern vec_t globallightscale;
Index: hmap2/light_face.c
diff -u /dev/null hmap2/light_face.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/light_face.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,663 @@
+
+#include "light.h"
+
+/*
+===============================================================================
+
+SAMPLE POINT DETERMINATION
+
+void SetupBlock (dface_t *f) Returns with point[] set
+
+This is a little tricky because the lightmap covers more area than the face.
+If done in the straightforward fashion, some of the
+sample points will be inside walls or on the other side of walls, causing
+false shadows and light bleeds.
+
+To solve this, I only consider a sample point valid if a line can be drawn
+between it and the exact midpoint of the face. If invalid, it is adjusted
+towards the center until it is valid.
+
+(this doesn't completely work)
+
+===============================================================================
+*/
+
+// LordHavoc: increased from 18x18 samples to 256x256 samples
+#define SINGLEMAP (256*256)
+
+typedef struct
+{
+ vec3_t v;
+ qboolean occluded;
+ int samplepos; // the offset into the lightmap that this point contributes to
+} lightpoint_t;
+
+typedef struct
+{
+ vec3_t c;
+} lightsample_t;
+
+typedef struct
+{
+ vec_t facedist;
+ vec3_t facenormal;
+
+ int numpoints;
+ int numsamples;
+ // *32 for -extra8x8
+// lightpoint_t point[SINGLEMAP*64];
+ lightpoint_t *point;
+ lightsample_t sample[MAXLIGHTMAPS][SINGLEMAP];
+
+ vec3_t texorg;
+ vec3_t worldtotex[2]; // s = (world - texorg) . worldtotex[0]
+ vec3_t textoworld[2]; // world = texorg + s * textoworld[0]
+
+ vec_t exactmins[2], exactmaxs[2];
+
+ int texmins[2], texsize[2];
+ int lightstyles[MAXLIGHTMAPS];
+ dface_t *face;
+} lightinfo_t;
+
+//#define BUGGY_TEST
+
+/*
+================
+CalcFaceVectors
+
+Fills in texorg, worldtotex. and textoworld
+================
+*/
+void CalcFaceVectors (lightinfo_t *l, vec3_t faceorg)
+{
+ texinfo_t *tex;
+ int i, j;
+ vec3_t texnormal;
+ vec_t distscale;
+ vec_t dist, len;
+
+ tex = &texinfo[l->face->texinfo];
+
+// convert from float to vec_t
+ for (i=0 ; i<2 ; i++)
+ for (j=0 ; j<3 ; j++)
+ l->worldtotex[i][j] = tex->vecs[i][j];
+
+// calculate a normal to the texture axis. points can be moved along this
+// without changing their S/T
+ // LordHavoc: this is actually a CrossProduct
+ texnormal[0] = tex->vecs[1][1]*tex->vecs[0][2] - tex->vecs[1][2]*tex->vecs[0][1];
+ texnormal[1] = tex->vecs[1][2]*tex->vecs[0][0] - tex->vecs[1][0]*tex->vecs[0][2];
+ texnormal[2] = tex->vecs[1][0]*tex->vecs[0][1] - tex->vecs[1][1]*tex->vecs[0][0];
+ VectorNormalize (texnormal);
+
+// flip it towards plane normal
+ distscale = DotProduct (texnormal, l->facenormal);
+ if (!distscale)
+ Error ("Texture axis perpendicular to face");
+ if (distscale < 0)
+ {
+ distscale = -distscale;
+ VectorNegate (texnormal, texnormal);
+ }
+
+// distscale is the ratio of the distance along the texture normal to
+// the distance along the plane normal
+ distscale = 1/distscale;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ len = VectorLength (l->worldtotex[i]);
+ dist = DotProduct (l->worldtotex[i], l->facenormal);
+ dist *= distscale;
+ VectorMA (l->worldtotex[i], -dist, texnormal, l->textoworld[i]);
+ VectorScale (l->textoworld[i], (1/len)*(1/len), l->textoworld[i]);
+ }
+
+
+// calculate texorg on the texture plane
+ for (i=0 ; i<3 ; i++)
+ l->texorg[i] = -tex->vecs[0][3] * l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i];
+
+ VectorAdd(l->texorg, faceorg, l->texorg);
+
+// project back to the face plane
+ dist = DotProduct (l->texorg, l->facenormal) - l->facedist - 1;
+ dist *= distscale;
+ VectorMA (l->texorg, -dist, texnormal, l->texorg);
+}
+
+/*
+================
+CalcFaceExtents
+
+Fills in s->texmins[] and s->texsize[]
+also sets exactmins[] and exactmaxs[]
+================
+*/
+void CalcFaceExtents (lightinfo_t *l)
+{
+ dface_t *s;
+ vec_t mins[2], maxs[2], val;
+ int i,j, e;
+ dvertex_t *v;
+ texinfo_t *tex;
+
+ s = l->face;
+
+ mins[0] = mins[1] = BOGUS_RANGE;
+ maxs[0] = maxs[1] = -BOGUS_RANGE;
+
+ tex = &texinfo[s->texinfo];
+
+ for (i=0 ; i<s->numedges ; i++)
+ {
+ e = dsurfedges[s->firstedge+i];
+ if (e >= 0)
+ v = dvertexes + dedges[e].v[0];
+ else
+ v = dvertexes + dedges[-e].v[1];
+
+ for (j=0 ; j<2 ; j++)
+ {
+ val = v->point[0] * tex->vecs[j][0] +
+ v->point[1] * tex->vecs[j][1] +
+ v->point[2] * tex->vecs[j][2] +
+ tex->vecs[j][3];
+ if (val < mins[j])
+ mins[j] = val;
+ if (val > maxs[j])
+ maxs[j] = val;
+ }
+ }
+
+ for (i=0 ; i<2 ; i++)
+ {
+ l->exactmins[i] = mins[i];
+ l->exactmaxs[i] = maxs[i];
+
+ mins[i] = floor(mins[i]/16);
+ maxs[i] = ceil(maxs[i]/16);
+
+ l->texmins[i] = mins[i];
+ l->texsize[i] = maxs[i] + 1 - mins[i];
+ if (l->texsize[i] > 256) // LordHavoc: was 17, much much bigger allowed now
+ Error ("Bad surface extents");
+ }
+}
+
+void CalcSamples (lightinfo_t *l)
+{
+ /*
+ int i, mapnum;
+ lightsample_t *sample;
+ */
+
+ l->numsamples = l->texsize[0] * l->texsize[1];
+ // no need to clear because the lightinfo struct was cleared already
+ /*
+ for (mapnum = 0;mapnum < MAXLIGHTMAPS;mapnum++)
+ {
+ for (i = 0, sample = l->sample[mapnum];i < l->numsamples;i++, sample++)
+ {
+ sample->c[0] = 0;
+ sample->c[1] = 0;
+ sample->c[2] = 0;
+ }
+ }
+ */
+}
+
+/*
+=================
+CalcPoints
+
+For each texture aligned grid point, back project onto the plane
+to get the world xyz value of the sample point
+=================
+*/
+//int c_bad;
+void CalcPoints (lightinfo_t *l)
+{
+ int j, s, t, w, h, realw, realh, stepbit;
+ vec_t starts, startt, us, ut, mids, midt;
+ vec3_t facemid, base;
+#ifndef BUGGY_TEST
+ vec3_t v;
+#else
+ int i;
+#endif
+ lightTrace_t tr;
+ lightpoint_t *point;
+
+//
+// fill in point array
+// the points are biased towards the center of the surface
+// to help avoid edge cases just inside walls
+//
+ mids = (l->exactmaxs[0] + l->exactmins[0]) * 0.5;
+ midt = (l->exactmaxs[1] + l->exactmins[1]) * 0.5;
+
+ VectorAdd( l->texorg, l->facenormal, base );
+
+ for (j = 0;j < 3;j++)
+ facemid[j] = base[j] + l->textoworld[0][j] * mids + l->textoworld[1][j] * midt;
+
+ realw = l->texsize[0];
+ realh = l->texsize[1];
+ starts = l->texmins[0] * 16;
+ startt = l->texmins[1] * 16;
+
+ stepbit = 4 - extrasamplesbit;
+
+ w = realw << extrasamplesbit;
+ h = realh << extrasamplesbit;
+
+ if (stepbit < 4)
+ {
+ starts -= 1 << stepbit;
+ startt -= 1 << stepbit;
+ }
+
+ point = l->point;
+ l->numpoints = w * h;
+ for (t = 0;t < h;t++)
+ {
+ for (s = 0;s < w;s++, point++)
+ {
+ us = starts + (s << stepbit);
+ ut = startt + (t << stepbit);
+ point->occluded = false;
+ point->samplepos = (t >> extrasamplesbit) * realw + (s >> extrasamplesbit);
+
+#ifndef BUGGY_TEST
+ // calculate texture point
+ for (j = 0;j < 3;j++)
+ point->v[j] = base[j] + l->textoworld[0][j] * us + l->textoworld[1][j] * ut;
+
+ if( !Light_TraceLine( &tr, facemid, point->v ) ) {
+ // test failed, adjust to nearest position
+ VectorCopy(tr.impact, point->v);
+ VectorSubtract(facemid, point->v, v);
+ VectorNormalize(v);
+ VectorMA(point->v, 0.5, v, point->v);
+
+ if( Light_PointContents( point->v ) == CONTENTS_SOLID ) {
+ c_occluded++;
+ point->occluded = true;
+ }
+ }
+
+ //VectorSubtract(facemid, point->v, v);
+ //VectorNormalize(v);
+ //VectorMA(point->v, 0.25, v, point->v);
+#else
+ // if a line can be traced from point to facemid, the point is good
+ for (i = 0;i < 6;i++)
+ {
+ // calculate texture point
+ for (j = 0;j < 3;j++)
+ point->v[j] = base[j] + l->textoworld[0][j] * us + l->textoworld[1][j] * ut;
+
+ if (!Light_TraceLine (&tr, facemid, point->v))
+ break; // got it
+
+ if (i & 1)
+ {
+ if (us > mids)
+ {
+ us -= 8;
+ if (us < mids)
+ us = mids;
+ }
+ else
+ {
+ us += 8;
+ if (us > mids)
+ us = mids;
+ }
+ }
+ else
+ {
+ if (ut > midt)
+ {
+ ut -= 8;
+ if (ut < midt)
+ ut = midt;
+ }
+ else
+ {
+ ut += 8;
+ if (ut > midt)
+ ut = midt;
+ }
+ }
+ }
+ //if (i == 2)
+ // c_bad++;
+#endif
+ }
+ }
+}
+
+
+/*
+===============================================================================
+
+FACE LIGHTING
+
+===============================================================================
+*/
+
+int c_culldistplane, c_proper;
+
+/*
+================
+SingleLightFace
+================
+*/
+void SingleLightFace (directlight_t *light, lightinfo_t *l)
+{
+ vec_t dist, idist;
+ //vec_t lightbrightness;
+ vec_t lightsubtract;
+ vec_t lightfalloff;
+ vec3_t incoming;
+ vec_t add;
+ qboolean hit;
+ int mapnum;
+ int i;
+ vec3_t spotvec;
+ vec_t spotcone;
+ lightpoint_t *point;
+ lightsample_t *sample;
+ lightTrace_t tr;
+
+ dist = (DotProduct (light->origin, l->facenormal) - l->facedist);
+
+// don't bother with lights behind the surface
+ if (dist < -0.25)
+ return;
+
+ //lightbrightness = light->light * 16384.0;
+ lightfalloff = light->falloff;
+ lightsubtract = light->subbrightness;
+// don't bother with light too far away
+ if (lightsubtract > (1.0 / (dist * dist * lightfalloff + LIGHTDISTBIAS)))
+ {
+ c_culldistplane++;
+ return;
+ }
+
+ for (mapnum = 0;mapnum < MAXLIGHTMAPS;mapnum++)
+ {
+ if (l->lightstyles[mapnum] == light->style)
+ break;
+ if (l->lightstyles[mapnum] == 255)
+ {
+ if (relight)
+ return;
+ memset(l->sample[mapnum], 0, sizeof(lightsample_t) * l->numsamples);
+ break;
+ }
+ }
+
+ if (mapnum == MAXLIGHTMAPS)
+ {
+ printf ("WARNING: Too many light styles on a face\n");
+ return;
+ }
+
+ spotcone = light->spotcone;
+ VectorCopy(light->spotdir, spotvec);
+
+//
+// check it for real
+//
+ hit = relight; // if relighting, the style is already considered used
+ c_proper++;
+
+ for (i = 0, point = l->point;i < l->numpoints;i++, point++)
+ {
+ VectorSubtract (light->origin, point->v, incoming);
+ dist = sqrt(DotProduct(incoming, incoming));
+ if (!dist)
+ continue;
+ idist = 1.0 / dist;
+ VectorScale( incoming, idist, incoming );
+
+ // spotlight
+ if (spotcone > 0 && DotProduct (spotvec, incoming) > spotcone)
+ continue;
+
+ // LordHavoc: changed to be more realistic (entirely different lighting model)
+ // LordHavoc: FIXME: use subbrightness on all lights, simply to have some distance culling
+ add = 1.0 / (dist * dist * lightfalloff + LIGHTDISTBIAS) - lightsubtract; // LordHavoc: added subbrightness
+ if (add <= 0)
+ continue;
+
+ if (point->occluded || !Light_TraceLine(&tr, point->v, light->origin))
+ continue;
+
+ // LordHavoc: FIXME: decide this 0.5 bias based on shader properties (some are dull, some are shiny)
+ add *= (DotProduct (incoming, l->facenormal) * 0.5 + 0.5);
+ add *= extrasamplesscale;
+ sample = &l->sample[mapnum][point->samplepos];
+ sample->c[0] += add * light->color[0] * tr.filter[0];
+ sample->c[1] += add * light->color[1] * tr.filter[1];
+ sample->c[2] += add * light->color[2] * tr.filter[2];
+ // ignore real tiny lights
+ if (!hit && ((sample->c[0]+sample->c[1]+sample->c[2]) >= 1))
+ hit = true;
+ }
+
+ // if the style has some data now, make sure it is in the list
+ if (hit)
+ l->lightstyles[mapnum] = light->style;
+}
+
+/*
+============
+FixMinlight
+============
+*/
+/*
+void FixMinlight (lightinfo_t *l)
+{
+ int i, j;
+ lightsample_t *sample;
+
+// if minlight is set, there must be a style 0 light map
+ if (!minlight)
+ return;
+
+ for (i = 0;i < l->numlightstyles;i++)
+ {
+ if (l->lightstyles[i] == 0)
+ {
+ for (j = 0, sample = l->sample[i];j < l->numsamples;j++, sample++)
+ {
+ if (sample->c[0] < minlight)
+ sample->c[0] = minlight;
+ if (sample->c[1] < minlight)
+ sample->c[1] = minlight;
+ if (sample->c[2] < minlight)
+ sample->c[2] = minlight;
+ }
+ return;
+ }
+ }
+
+ if (l->numlightstyles == MAXLIGHTMAPS)
+ return; // oh well..
+ for (j = 0, sample = l->sample[i];j < l->numsamples;j++, sample++)
+ {
+ sample->c[0] = minlight;
+ sample->c[1] = minlight;
+ sample->c[2] = minlight;
+ }
+ l->lightstyles[i] = 0;
+ l->numlightstyles++;
+}
+*/
+
+int ranout = false;
+
+/*
+============
+LightFace
+============
+*/
+lightinfo_t l; // if this is made multithreaded again, this should be inside the function, but be warned, it's currently 38mb
+void LightFace (dface_t *f, lightchain_t *lightchain, directlight_t **novislight, int novislights, vec3_t faceorg)
+{
+ int i, j, size;
+ int red, green, blue, white;
+ byte *out, *lit;
+ lightsample_t *sample;
+
+ //memset (&l, 0, sizeof(l));
+ l.face = f;
+ if( !l.point )
+ l.point = qmalloc( SINGLEMAP * (1<<extrasamplesbit)*(1<<extrasamplesbit) );
+
+//
+// some surfaces don't need lightmaps
+//
+
+ if (relight)
+ {
+ if (f->lightofs == -1)
+ return;
+ }
+ else
+ {
+ for (i = 0;i < MAXLIGHTMAPS;i++)
+ f->styles[i] = l.lightstyles[i] = 255;
+ f->lightofs = -1;
+
+ if (texinfo[f->texinfo].flags & TEX_SPECIAL)
+ {
+ // non-lit texture
+ return;
+ }
+ }
+
+//
+// rotate plane
+//
+ VectorCopy (dplanes[f->planenum].normal, l.facenormal);
+ l.facedist = dplanes[f->planenum].dist;
+ if (f->side)
+ {
+ VectorNegate (l.facenormal, l.facenormal);
+ l.facedist = -l.facedist;
+ }
+
+ CalcFaceVectors (&l, faceorg);
+ CalcFaceExtents (&l);
+ CalcSamples (&l);
+ CalcPoints (&l);
+
+ if (l.numsamples > SINGLEMAP)
+ Error ("Bad lightmap size");
+
+ if (relight)
+ {
+ // reserve the correct light styles
+ for (i = 0;i < MAXLIGHTMAPS;i++)
+ {
+ l.lightstyles[i] = f->styles[i];
+ if (l.lightstyles[i] != 255)
+ memset(l.sample[i], 0, sizeof(lightsample_t) * l.numsamples);
+ }
+ }
+
+//
+// cast all lights
+//
+ while (lightchain)
+ {
+ SingleLightFace (lightchain->light, &l);
+ lightchain = lightchain->next;
+ }
+ while (novislights--)
+ SingleLightFace (*novislight++, &l);
+
+ //FixMinlight (&l);
+
+// save out the values
+
+ if (relight)
+ {
+ // relighting an existing map without changing it's lightofs table
+
+ for (i = 0;i < MAXLIGHTMAPS;i++)
+ if (f->styles[i] == 255)
+ break;
+ size = l.numsamples * i;
+
+ if (f->lightofs < 0 || f->lightofs + size > lightdatasize)
+ Error("LightFace: Error while trying to relight map: invalid lightofs value, %i must be >= 0 && < %i\n", f->lightofs, lightdatasize);
+ }
+ else
+ {
+ // creating lightofs table from scratch
+
+ for (i = 0;i < MAXLIGHTMAPS;i++)
+ if (l.lightstyles[i] == 255)
+ break;
+ size = l.numsamples * i;
+
+ if (!size)
+ {
+ // no light styles
+ return;
+ }
+
+ if (lightdatasize + size > MAX_MAP_LIGHTING)
+ {
+ //Error("LightFace: ran out of lightmap dataspace");
+ if (!ranout)
+ printf("LightFace: ran out of lightmap dataspace");
+ ranout = true;
+ return;
+ }
+
+ for (i = 0;i < MAXLIGHTMAPS;i++)
+ f->styles[i] = l.lightstyles[i];
+
+ f->lightofs = lightdatasize;
+ lightdatasize += size;
+ }
+
+ rgblightdatasize = lightdatasize * 3;
+
+ out = dlightdata + f->lightofs;
+ lit = drgblightdata + f->lightofs * 3;
+
+ for (i = 0;i < MAXLIGHTMAPS && f->styles[i] != 255;i++)
+ {
+ for (j = 0, sample = l.sample[i];j < l.numsamples;j++, sample++)
+ {
+ red = (int) sample->c[0];
+ green = (int) sample->c[1];
+ blue = (int) sample->c[2];
+ white = (int) ((sample->c[0] + sample->c[1] + sample->c[2]) * (1.0 / 3.0));
+ if (red > 255) red = 255;
+ if (red < 0) red = 0;
+ if (green > 255) green = 255;
+ if (green < 0) green = 0;
+ if (blue > 255) blue = 255;
+ if (blue < 0) blue = 0;
+ if (white > 255) white = 255;
+ if (white < 0) white = 0;
+ *lit++ = red;
+ *lit++ = green;
+ *lit++ = blue;
+ *out++ = white;
+ }
+ }
+}
+
Index: hmap2/light_trace.c
diff -u /dev/null hmap2/light_trace.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/light_trace.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,239 @@
+// trace.c
+
+#include "light.h"
+
+typedef struct
+{
+ int type;
+ vec3_t normal;
+ vec_t dist;
+ int children[2];
+} tnode_t;
+
+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;
+
+ t = tnode_p++;
+
+ node = dnodes + nodenum;
+ plane = dplanes + node->planenum;
+
+ t->type = plane->type;
+ VectorCopy (plane->normal, t->normal);
+ t->dist = plane->dist;
+
+ for (i=0 ; i<2 ; i++)
+ {
+ if (node->children[i] < 0)
+ t->children[i] = dleafs[-node->children[i] - 1].contents;
+ else
+ {
+ t->children[i] = tnode_p - tnodes;
+ MakeTnode (node->children[i]);
+ }
+ }
+
+}
+
+
+/*
+=============
+MakeTnodes
+
+Loads the node structure out of a .bsp file to be used for light occlusion
+=============
+*/
+void MakeTnodes (dmodel_t *bm)
+{
+ tnodes = qmalloc((numnodes/*+1*/) * sizeof(tnode_t));
+// tnodes = (tnode_t *)(((int)tnodes + 31) & ~31);
+ tnode_p = tnodes;
+
+ MakeTnode (0);
+}
+
+
+
+/*
+==============================================================================
+
+LINE TRACING
+
+The major lighting operation is a point to point visibility test, performed
+by recursive subdivision of the line by the BSP tree.
+
+==============================================================================
+*/
+
+int Light_PointContents( vec3_t p )
+{
+ vec_t d;
+ tnode_t *tnode;
+ int num = 0;
+
+ while( num >= 0 ) {
+ tnode = tnodes + num;
+ if( tnode->type < 3 )
+ d = p[tnode->type] - tnode->dist;
+ else
+ d = DotProduct( tnode->normal, p ) - tnode->dist;
+ num = tnode->children[(d < 0)];
+ }
+
+ return num;
+}
+
+#define TESTLINESTATE_BLOCKED 0
+#define TESTLINESTATE_EMPTY 1
+#define TESTLINESTATE_SOLID 2
+
+/*
+==============
+TestLine
+==============
+*/
+// LordHavoc: TestLine returns true if there is no impact,
+// see below for precise definition of impact (important)
+lightTrace_t *trace_trace;
+
+static int RecursiveTestLine (int num, float p1f, float p2f, vec3_t p1, vec3_t p2)
+{
+ int side, ret;
+ vec_t t1, t2, frac, midf;
+ vec3_t mid;
+ tnode_t *tnode;
+
+ // LordHavoc: this function operates by doing depth-first front-to-back
+ // recursion through the BSP tree, checking at every split for an empty to solid
+ // transition (impact) in the children, and returns false if one is found
+
+ // LordHavoc: note: 'no impact' does not mean it is empty, it occurs when there
+ // is no transition from empty to solid; all solid or a transition from solid
+ // to empty are not considered impacts. (this does mean that tracing is not
+ // symmetrical; point A to point B may have different results than point B to
+ // point A, if either start in solid)
+
+ // check for empty
+loc0:
+ if (num < 0)
+ {
+ if (num == CONTENTS_SOLID)
+ {
+ trace_trace->startSolid = true;
+// VectorClear( trace_trace->filter );
+ return TESTLINESTATE_SOLID;
+ }
+ else if (num == CONTENTS_WATER)
+ {
+// trace_trace->filter[0] *= 0.6;
+// trace_trace->filter[1] *= 0.6;
+ }
+ else if (num == CONTENTS_LAVA)
+ {
+// trace_trace->filter[1] *= 0.6;
+// trace_trace->filter[2] *= 0.6;
+ }
+ return TESTLINESTATE_EMPTY;
+ }
+
+ // find the point distances
+ tnode = tnodes + num;
+
+ if (tnode->type < 3)
+ {
+ t1 = p1[tnode->type] - tnode->dist;
+ t2 = p2[tnode->type] - tnode->dist;
+ }
+ else
+ {
+ t1 = DotProduct (tnode->normal, p1) - tnode->dist;
+ t2 = DotProduct (tnode->normal, p2) - tnode->dist;
+ }
+
+ if (t1 >= 0)
+ {
+ if (t2 >= 0)
+ {
+ num = tnode->children[0];
+ goto loc0;
+ }
+ side = 0;
+ }
+ else
+ {
+ if (t2 < 0)
+ {
+ num = tnode->children[1];
+ goto loc0;
+ }
+ side = 1;
+ }
+
+ frac = t1 / (t1 - t2);
+ if (frac < 0)
+ frac = 0;
+ if (frac > 1)
+ frac = 1;
+
+ midf = p1f + (p2f - p1f) * frac;
+ mid[0] = p1[0] + frac * (p2[0] - p1[0]);
+ mid[1] = p1[1] + frac * (p2[1] - p1[1]);
+ mid[2] = p1[2] + frac * (p2[2] - p1[2]);
+
+ // front side first
+ ret = RecursiveTestLine (tnode->children[side], p1f, midf, p1, mid);
+ if (ret != TESTLINESTATE_EMPTY)
+ return ret; // solid or blocked
+
+ ret = RecursiveTestLine (tnode->children[!side], midf, p2f, mid, p2);
+ if (ret != TESTLINESTATE_SOLID)
+ return ret; // empty or blocked
+
+ if (!side)
+ {
+ VectorCopy (tnode->normal, trace_trace->plane.normal);
+ trace_trace->plane.dist = tnode->dist;
+ trace_trace->plane.type = tnode->type;
+ }
+ else
+ {
+ VectorNegate (tnode->normal, trace_trace->plane.normal);
+ trace_trace->plane.dist = -tnode->dist;
+ trace_trace->plane.type = PLANE_ANYX;
+ }
+
+ trace_trace->fraction = midf;
+ VectorCopy( mid, trace_trace->impact );
+
+ return TESTLINESTATE_BLOCKED;
+}
+
+qboolean Light_TraceLine (lightTrace_t *trace, vec3_t start, vec3_t end)
+{
+ static plane_t nullplane;
+
+ if (!trace)
+ return false;
+
+ trace_trace = trace;
+ trace_trace->fraction = 1.0;
+ trace_trace->plane = nullplane;
+ trace_trace->startSolid = false;
+ VectorSet (trace_trace->filter, 1, 1, 1);
+ VectorCopy (end, trace_trace->impact);
+
+ return ( RecursiveTestLine( 0, 0, 1, start, end ) != TESTLINESTATE_BLOCKED );
+}
Index: hmap2/map.c
diff -u /dev/null hmap2/map.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/map.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,476 @@
+// map.c
+
+#include "bsp5.h"
+
+// just for statistics
+int nummapbrushfaces;
+
+int nummapbrushes;
+mbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+int nummapplanes;
+plane_t mapplanes[MAX_MAP_PLANES];
+
+int nummiptex;
+char miptex[MAX_MAP_TEXINFO][16];
+
+//============================================================================
+
+/*
+===============
+FindPlane
+
+Returns a global plane number and the side that will be the front
+===============
+*/
+int FindPlane( plane_t *dplane, int *side )
+{
+ int i;
+ plane_t *dp, pl;
+ vec_t dot;
+
+ pl = *dplane;
+ NormalizePlane( &pl );
+
+ if( DotProduct( pl.normal, dplane->normal ) > 0 )
+ *side = 0;
+ else
+ *side = 1;
+
+ for( i = 0, dp = mapplanes; i < nummapplanes; i++, dp++ ) {
+ if( DotProduct( dp->normal, pl.normal ) > 1.0 - ANGLEEPSILON && fabs( dp->dist - pl.dist ) < DISTEPSILON )
+ return i; // regular match
+ }
+
+ if( nummapplanes == MAX_MAP_PLANES )
+ Error( "FindPlane: nummapplanes == MAX_MAP_PLANES" );
+
+ dot = VectorLength( dplane->normal );
+ if( dot < 1.0 - ANGLEEPSILON || dot > 1.0 + ANGLEEPSILON )
+ Error( "FindPlane: normalization error (%f %f %f, length %f)", dplane->normal[0], dplane->normal[1], dplane->normal[2], dot );
+
+ mapplanes[nummapplanes] = pl;
+
+ return nummapplanes++;
+}
+
+/*
+===============
+FindMiptex
+===============
+*/
+int FindMiptex( char *name )
+{
+ int i;
+
+ for( i = 0; i < nummiptex; i++ ) {
+ if( !strcmp( name, miptex[i] ) )
+ return i;
+ }
+
+ if( nummiptex == MAX_MAP_TEXINFO )
+ Error ("nummiptex == MAX_MAP_TEXINFO");
+
+ strcpy( miptex[i], name );
+
+ return nummiptex++;
+}
+
+/*
+===============
+FindTexinfo
+
+Returns a global texinfo number
+===============
+*/
+int FindTexinfo( texinfo_t *t )
+{
+ int i, j;
+ texinfo_t *tex;
+
+ // set the special flag
+ if( miptex[t->miptex][0] == '*' || !Q_strncasecmp (miptex[t->miptex], "sky", 3) )
+ t->flags |= TEX_SPECIAL;
+
+ tex = texinfo;
+ for( i = 0; i < numtexinfo; i++, tex++ ) {
+ if( t->miptex != tex->miptex )
+ continue;
+ if( t->flags != tex->flags )
+ continue;
+
+ for( j = 0; j < 8; j++ ) {
+ if( t->vecs[0][j] != tex->vecs[0][j] )
+ break;
+ }
+ if( j != 8 )
+ continue;
+
+ return i;
+ }
+
+ // allocate a new texture
+ if( numtexinfo == MAX_MAP_TEXINFO )
+ Error( "numtexinfo == MAX_MAP_TEXINFO" );
+
+ texinfo[i] = *t;
+
+ return numtexinfo++;
+}
+
+//============================================================================
+
+
+/*
+==================
+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);
+}
+
+
+//=============================================================================
+
+
+/*
+=================
+ParseBrush
+=================
+*/
+void ParseBrush (entity_t *ent)
+{
+ int i, j, sv, tv, hltexdef;
+ vec_t planepts[3][3], t1[3], t2[3], d, rotate, scale[2], vecs[2][4], ang, sinv, cosv, ns, nt;
+ mbrush_t *b;
+ mface_t *f, *f2;
+ plane_t plane;
+ texinfo_t tx;
+
+ b = &mapbrushes[nummapbrushes];
+ nummapbrushes++;
+ b->next = ent->brushes;
+ ent->brushes = b;
+
+ do
+ {
+ if (!GetToken (true))
+ break;
+ if (!strcmp (token, "}") )
+ break;
+
+ // read the three point plane definition
+ for (i = 0;i < 3;i++)
+ {
+ if (i != 0)
+ GetToken (true);
+ if (strcmp (token, "(") )
+ Error ("parsing brush on line %d\n", scriptline);
+
+ for (j = 0;j < 3;j++)
+ {
+ GetToken (false);
+ planepts[i][j] = (vec_t)atof(token); // LordHavoc: float coords
+ }
+
+ GetToken (false);
+ if (strcmp (token, ")") )
+ Error ("parsing brush on line %d\n", scriptline);
+ }
+
+ //fflush(stdout);
+
+ // convert points to a plane
+ VectorSubtract(planepts[0], planepts[1], t1);
+ VectorSubtract(planepts[2], planepts[1], t2);
+ CrossProduct(t1, t2, plane.normal);
+ VectorNormalize(plane.normal);
+ plane.dist = DotProduct(planepts[1], plane.normal);
+
+ // read the texturedef
+ memset (&tx, 0, sizeof(tx));
+ GetToken (false);
+ tx.miptex = FindMiptex (token);
+ GetToken (false);
+ if ((hltexdef = !strcmp(token, "[")))
+ {
+ // S vector
+ GetToken(false);
+ vecs[0][0] = (vec_t)atof(token);
+ GetToken(false);
+ vecs[0][1] = (vec_t)atof(token);
+ GetToken(false);
+ vecs[0][2] = (vec_t)atof(token);
+ GetToken(false);
+ vecs[0][3] = (vec_t)atof(token);
+ // ]
+ GetToken(false);
+ // [
+ GetToken(false);
+ // T vector
+ GetToken(false);
+ vecs[1][0] = (vec_t)atof(token);
+ GetToken(false);
+ vecs[1][1] = (vec_t)atof(token);
+ GetToken(false);
+ vecs[1][2] = (vec_t)atof(token);
+ GetToken(false);
+ vecs[1][3] = (vec_t)atof(token);
+ // ]
+ GetToken(false);
+ }
+ else
+ {
+ vecs[0][3] = (vec_t)atof(token); // LordHavoc: float coords
+ GetToken (false);
+ vecs[1][3] = (vec_t)atof(token); // LordHavoc: float coords
+ }
+ GetToken (false);
+ rotate = atof(token); // LordHavoc: float coords
+ GetToken (false);
+ scale[0] = (vec_t)atof(token); // LordHavoc: was already float coords
+ GetToken (false);
+ scale[1] = (vec_t)atof(token); // LordHavoc: was already float coords
+
+ // if the three points are all on a previous plane, it is a
+ // duplicate plane
+ for (f2 = b->faces ; f2 ; f2=f2->next)
+ {
+ for (i = 0;i < 3;i++)
+ {
+ d = DotProduct(planepts[i],f2->plane.normal) - f2->plane.dist;
+ if (d < -ON_EPSILON || d > ON_EPSILON)
+ break;
+ }
+ if (i==3)
+ break;
+ }
+ if (f2)
+ {
+ printf ("WARNING: brush with duplicate plane (first point is at %g %g %g, .map file line number %d)\n", planepts[0][0], planepts[0][1], planepts[0][2], scriptline);
+ continue;
+ }
+
+ if (DotProduct(plane.normal, plane.normal) < 0.1)
+ {
+ printf ("WARNING: brush plane with no normal on line %d\n", scriptline);
+ continue;
+ }
+
+ /*
+ // LordHavoc: fix for CheckFace: point off plane errors in some maps (most notably QOOLE ones),
+ // and hopefully preventing most 'portal clipped away' warnings
+ VectorNormalize (plane.normal);
+ for (j = 0;j < 3;j++)
+ plane.normal[j] = (Q_rint((vec_t) plane.normal[j] * (vec_t) 8.0)) * (vec_t) (1.0 / 8.0);
+ VectorNormalize (plane.normal);
+ plane.dist = DotProduct (t3, plane.normal);
+ d = (Q_rint(plane.dist * 8.0)) * (1.0 / 8.0);
+ //if (fabs(d - plane.dist) >= (0.4 / 8.0))
+ // printf("WARNING: correcting minor math errors in brushface on line %d\n", scriptline);
+ plane.dist = d;
+ */
+
+ /*
+ VectorNormalize (plane.normal);
+ plane.dist = DotProduct (t3, plane.normal);
+
+ VectorCopy(plane.normal, v);
+ //for (j = 0;j < 3;j++)
+ // v[j] = (Q_rint((vec_t) v[j] * (vec_t) 32.0)) * (vec_t) (1.0 / 32.0);
+ VectorNormalize (v);
+ d = (Q_rint(DotProduct (t3, v) * 8.0)) * (1.0 / 8.0);
+
+ // if deviation is too high, warn (frequently happens on QOOLE maps)
+ if (fabs(DotProduct(v, plane.normal) - 1.0) > (0.5 / 32.0)
+ || fabs(d - plane.dist) >= (0.25 / 8.0))
+ printf("WARNING: minor misalignment of brushface on line %d\n"
+ "normal %f %f %f (l: %f d: %f)\n"
+ "rounded to %f %f %f (l: %f d: %f r: %f)\n",
+ scriptline,
+ (vec_t) plane.normal[0], (vec_t) plane.normal[1], (vec_t) plane.normal[2], (vec_t) sqrt(DotProduct(plane.normal, plane.normal)), (vec_t) DotProduct (t3, plane.normal),
+ (vec_t) v[0], (vec_t) v[1], (vec_t) v[2], (vec_t) sqrt(DotProduct(v, v)), (vec_t) DotProduct(t3, v), (vec_t) d);
+ //VectorCopy(v, plane.normal);
+ //plane.dist = d;
+ */
+
+ if (!hltexdef)
+ {
+ // fake proper texture vectors from QuakeEd style
+ TextureAxisFromPlane(&plane, vecs[0], vecs[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 * (Q_PI / 180);sinv = sin(ang);cosv = cos(ang);}
+
+ // LordHavoc: I don't quite understand this
+ for (sv = 0;sv < 2 && !vecs[0][sv];sv++);
+ for (tv = 0;tv < 2 && !vecs[1][tv];tv++);
+
+ for (i = 0;i < 2;i++)
+ {
+ // rotate
+ 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;
+ // scale and store into texinfo
+ d = 1.0 / (scale[i] ? scale[i] : 1.0);
+ for (j = 0;j < 3;j++)
+ tx.vecs[i][j] = vecs[i][j] * d;
+ tx.vecs[i][3] = vecs[i][3];
+ }
+
+ f = qmalloc(sizeof(mface_t));
+ f->next = b->faces;
+ b->faces = f;
+ f->plane = plane;
+ f->texinfo = FindTexinfo (&tx);
+ nummapbrushfaces++;
+ }
+ while (1);
+}
+
+/*
+============
+MoveEntityBrushesIntoWorld
+
+Moves entity's brushes into tree.
+============
+*/
+void MoveEntityBrushesIntoWorld( entity_t *ent )
+{
+ entity_t *world;
+ mbrush_t *b, *next;
+
+ world = &entities[0];
+ if( ent == world )
+ return;
+
+ for( b = ent->brushes; b; b = next ) {
+ next = b->next;
+ b->next = world->brushes;
+ world->brushes = b;
+ }
+
+ ent->brushes = NULL;
+}
+
+/*
+================
+ParseEntity
+================
+*/
+qboolean ParseEntity (void)
+{
+ epair_t *e, *next;
+ entity_t *ent;
+
+ if( !GetToken( true ) )
+ return false;
+ if( strcmp( token, "{" ) )
+ Error( "ParseEntity: { not found" );
+
+ if( num_entities == MAX_MAP_ENTITIES )
+ Error( "num_entities == MAX_MAP_ENTITIES" );
+
+ ent = &entities[num_entities++];
+ ent->epairs = NULL;
+
+ do {
+ fflush( stdout );
+
+ if( !GetToken( true ) )
+ Error( "ParseEntity: EOF without closing brace" );
+
+ if( !strcmp (token, "}") )
+ break;
+ else if( !strcmp( token, "{" ) ) {
+ ParseBrush( ent );
+ } else {
+ e = ParseEpair ();
+ e->next = ent->epairs;
+ ent->epairs = e;
+ }
+ } while( 1 );
+
+ if( !strcmp( ValueForKey( ent, "classname" ), "func_group" ) ) {
+ MoveEntityBrushesIntoWorld( ent );
+
+ for( e = ent->epairs; e; e = next ) {
+ next = e->next;
+ qfree( e );
+ }
+
+ num_entities--;
+ }
+
+ return true;
+}
+
+/*
+================
+LoadMapFile
+================
+*/
+void LoadMapFile (char *filename)
+{
+ void *buf;
+
+ num_entities = 0;
+
+ nummapbrushfaces = 0;
+ nummapbrushes = 0;
+ nummapplanes = 0;
+ nummiptex = 0;
+
+ LoadFile (filename, &buf);
+
+ StartTokenParsing (buf);
+
+ while (ParseEntity ());
+
+ qfree (buf);
+
+ qprintf ("--- LoadMapFile ---\n");
+ qprintf ("%s\n", filename);
+ qprintf ("%5i faces\n", nummapbrushfaces);
+ qprintf ("%5i brushes\n", nummapbrushes);
+ qprintf ("%5i entities\n", num_entities);
+ qprintf ("%5i textures\n", nummiptex);
+ qprintf ("%5i texinfo\n", numtexinfo);
+}
Index: hmap2/map.h
diff -u /dev/null hmap2/map.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/map.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,33 @@
+
+#define DISTEPSILON 0.001
+#define ANGLEEPSILON 0.00001
+
+// LordHavoc: raised MAX_FACES From 32 to 256
+#define MAX_FACES 256
+
+typedef struct mface_s
+{
+ struct mface_s *next;
+ plane_t plane;
+ int texinfo;
+} mface_t;
+
+typedef struct mbrush_s
+{
+ struct mbrush_s *next;
+ mface_t *faces;
+} mbrush_t;
+
+extern int nummapbrushes;
+extern mbrush_t mapbrushes[MAX_MAP_BRUSHES];
+
+extern int nummapplanes;
+extern plane_t mapplanes[MAX_MAP_PLANES];
+
+extern int nummiptex;
+extern char miptex[MAX_MAP_TEXINFO][16];
+
+void LoadMapFile (char *filename);
+
+int FindPlane( plane_t *dplane, int *side );
+int FindMiptex (char *name);
Index: hmap2/mathlib.c
diff -u /dev/null hmap2/mathlib.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/mathlib.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,190 @@
+// mathlib.c -- math primitives
+
+#include "cmdlib.h"
+#include "mathlib.h"
+
+vec3_t vec3_origin = { 0, 0, 0 };
+
+vec_t VectorNormalize( vec3_t v )
+{
+ vec_t length;
+
+ length = VectorLength( v );
+ if( length ) {
+ vec_t ilength;
+
+ ilength = 1.0 / length;
+ VectorScale( v, ilength, v );
+ }
+
+ return length;
+}
+
+vec_t Q_rint( vec_t n )
+{
+ if (n >= 0)
+ return ( vec_t )(( int )(n + 0.5));
+ else
+ return ( vec_t )(( int )(n - 0.5));
+}
+
+/*
+=================
+ClearBounds
+=================
+*/
+void ClearBounds( vec3_t mins, vec3_t maxs )
+{
+ int i;
+
+ for( i = 0; i < 3; i++ ) {
+ mins[i] = BOGUS_RANGE;
+ maxs[i] = -BOGUS_RANGE;
+ }
+}
+
+/*
+=================
+AddPointToBounds
+=================
+*/
+void AddPointToBounds( vec3_t v, vec3_t mins, vec3_t maxs )
+{
+ int i;
+
+ for( i = 0; i < 3; i++ ) {
+ if (v[i] < mins[i])
+ mins[i] = v[i];
+ if (v[i] > maxs[i])
+ maxs[i] = v[i];
+ }
+}
+
+/*
+=================
+RadiusFromBounds
+=================
+*/
+vec_t RadiusFromBounds( vec3_t mins, vec3_t maxs )
+{
+ vec_t mind, maxd;
+
+ if( mins[0] < mins[1] )
+ mind = mins[0];
+ else
+ mind = mins[1];
+ if( mins[2] < mind )
+ mind = mins[2];
+ mind = fabs( mind );
+
+ if( maxs[0] > maxs[1] )
+ maxd = maxs[0];
+ else
+ maxd = maxs[1];
+ if( maxs[2] > maxd )
+ maxd = maxs[2];
+ maxd = fabs( maxd );
+
+ if( mind > maxd )
+ return mind;
+ return maxd;
+}
+
+/*
+=================
+PlaneTypeForNormal
+=================
+*/
+int PlaneTypeForNormal( vec3_t normal )
+{
+ vec_t ax, ay, az;
+
+ // NOTE: should these have an epsilon around 1.0?
+ if( normal[0] == 1.0 )
+ return PLANE_X;
+ if( normal[1] == 1.0 )
+ return PLANE_Y;
+ if( normal[2] == 1.0 )
+ return PLANE_Z;
+ if( normal[0] == -1.0 || normal[1] == -1.0 || normal[2] == -1.0 )
+ Error ("PlaneTypeForNormal: not a canonical vector (%f %f %f)", normal[0], normal[1], normal[2]);
+
+ ax = fabs( normal[0] );
+ ay = fabs( normal[1] );
+ az = fabs( normal[2] );
+
+ if( ax >= ay && ax >= az )
+ return PLANE_ANYX;
+ if( ay >= ax && ay >= az )
+ return PLANE_ANYY;
+ return PLANE_ANYZ;
+}
+
+/*
+=================
+NormalizePlane
+=================
+*/
+void NormalizePlane( plane_t *dp )
+{
+ vec_t ax, ay, az;
+
+ if( dp->normal[0] == -1.0 ) {
+ dp->normal[0] = 1.0;
+ dp->dist = -dp->dist;
+ } else if( dp->normal[1] == -1.0 ) {
+ dp->normal[1] = 1.0;
+ dp->dist = -dp->dist;
+ } else if( dp->normal[2] == -1.0 ) {
+ dp->normal[2] = 1.0;
+ dp->dist = -dp->dist;
+ }
+
+ if( dp->normal[0] == 1.0 ) {
+ dp->type = PLANE_X;
+ return;
+ }
+ if( dp->normal[1] == 1.0 ) {
+ dp->type = PLANE_Y;
+ return;
+ }
+ if( dp->normal[2] == 1.0 ) {
+ dp->type = PLANE_Z;
+ return;
+ }
+
+ ax = fabs( dp->normal[0] );
+ ay = fabs( dp->normal[1] );
+ az = fabs( dp->normal[2] );
+
+ if( ax >= ay && ax >= az )
+ dp->type = PLANE_ANYX;
+ else if( ay >= ax && ay >= az )
+ dp->type = PLANE_ANYY;
+ else
+ dp->type = PLANE_ANYZ;
+
+ if( dp->normal[dp->type - PLANE_ANYX] < 0 ) {
+ VectorNegate( dp->normal, dp->normal );
+ dp->dist = -dp->dist;
+ }
+}
+
+/*
+=================
+PlaneCompare
+=================
+*/
+qboolean PlaneCompare( plane_t *p1, plane_t *p2 )
+{
+ int i;
+
+ if( fabs( p1->dist - p2->dist ) > 0.01)
+ return false;
+
+ for( i = 0; i < 3; i++ )
+ if( fabs(p1->normal[i] - p2->normal[i] ) > 0.001 )
+ return false;
+
+ return true;
+}
Index: hmap2/mathlib.h
diff -u /dev/null hmap2/mathlib.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/mathlib.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,89 @@
+// mathlib.h
+
+#ifndef __MATHLIB__
+#define __MATHLIB__
+
+#include <math.h>
+
+#if _MSC_VER
+# pragma warning(disable : 4244) // MIPS
+# pragma warning(disable : 4136) // X86
+# pragma warning(disable : 4051) // ALPHA
+#endif
+
+#define DOUBLEVEC_T
+
+#ifdef DOUBLEVEC_T
+typedef double vec_t;
+#else
+typedef float vec_t;
+#endif
+typedef vec_t vec3_t[3];
+
+extern vec3_t vec3_origin;
+
+typedef struct
+{
+ vec3_t normal;
+ vec_t dist;
+ int type;
+} plane_t;
+
+#define SIDE_FRONT 0
+#define SIDE_ON 2
+#define SIDE_BACK 1
+#define SIDE_CROSS -2
+
+#define Q_PI 3.14159265358979323846
+
+#define EQUAL_EPSILON 0.001
+
+#ifndef PLANE_X
+
+// 0-2 are axial planes
+# define PLANE_X 0
+# define PLANE_Y 1
+# define PLANE_Z 2
+
+// 3-5 are non-axial planes snapped to the nearest
+# define PLANE_ANYX 3
+# define PLANE_ANYY 4
+# define PLANE_ANYZ 5
+
+#endif
+
+// LordHavoc: moved BOGUS_RANGE from bsp5.h and increased from 18000 to 1000000000
+#define BOGUS_RANGE 1000000000
+
+#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2])
+#define VectorSet(a,x,y,z) ((a[0])=(x),(a)[1]=(y),(a)[2]=(z))
+#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2])
+#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2])
+#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2])
+#define VectorLength(v) (sqrt(((v)[0]*v[0]) + ((v)[1]*v[1]) + ((v)[2]*(v)[2])))
+#define VectorCompare(v1, v2) (fabs((v1)[0]-(v2)[0]) < EQUAL_EPSILON && \
+ fabs((v1)[1]-(v2)[1]) < EQUAL_EPSILON && \
+ fabs((v1)[2]-(v2)[2]) < EQUAL_EPSILON)
+#define VectorMA(va, scale, vb, vc) ((vc)[0]=(va)[0]+(scale)*(vb)[0],(vc)[1]=(va)[1]+(scale)*(vb)[1],(vc)[2]=(va)[2]+(scale)*(vb)[2])
+#define CrossProduct(v1, v2, cross) ((cross)[0]=(v1)[1]*(v2)[2]-(v1)[2]*(v2)[1],(cross)[1]=(v1)[2]*(v2)[0]-(v1)[0]*(v2)[2],(cross)[2]=(v1)[0]*(v2)[1]-(v1)[1]*(v2)[0])
+#define VectorClear(v) ((v)[0]=(v)[1]=(v)[2]=0)
+#define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2])
+#define VectorInverse(v) ((v)[0]=-(v)[0],(v)[1]=-(v)[1],(v)[2]=-(v)[2])
+#define VectorScale(v, scale, out) ((out)[0]=(v)[0]*(scale),(out)[1]=(v)[1]*(scale),(out)[2]=(v)[2]*(scale))
+#define bound(min, value, max) ((value) < (min) ? (min) : ((value) > (max) ? (max) : (value)))
+#define PlaneDiff(point,plane) (((plane)->type < 3 ? (point)[(plane)->type] : DotProduct((point), (plane)->normal)) - (plane)->dist)
+
+vec_t VectorNormalize( vec3_t v );
+
+vec_t Q_rint( vec_t n );
+
+void ClearBounds( vec3_t mins, vec3_t maxs );
+void AddPointToBounds( vec3_t v, vec3_t mins, vec3_t maxs );
+vec_t RadiusFromBounds( vec3_t mins, vec3_t maxs );
+
+int PlaneTypeForNormal( vec3_t normal );
+void NormalizePlane( plane_t *dp );
+qboolean PlaneCompare( plane_t *p1, plane_t *p2 );
+
+#endif
+
Index: hmap2/mem.c
diff -u /dev/null hmap2/mem.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/mem.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,120 @@
+#include "cmdlib.h"
+#include "mem.h"
+
+static qboolean mem_initialized = false;
+
+typedef struct chunkheader_s
+{
+ // next and previous memheaders in chain belonging to pool
+ struct chunkheader_s *next;
+ struct chunkheader_s *prev;
+
+ // size of the memory after the header (excluding header and sentinel2)
+ size_t size;
+
+ // immediately followed by data
+} chunkheader_t;
+
+static chunkheader_t *chunks;
+static size_t total_allocated;
+static size_t total_active, total_peakactive;
+
+void Q_InitMem( void )
+{
+ if( mem_initialized )
+ return;
+
+ chunks = NULL;
+ mem_initialized = true;
+ total_allocated = 0;
+ total_active = 0;
+ total_peakactive = 0;
+}
+
+void Q_PrintMem( void )
+{
+ if( !mem_initialized )
+ return;
+
+ printf( "Active memory: %f MB (%i bytes)\n", total_active / 1048576.0, total_active );
+ printf( "Peak active memory: %f MB (%i bytes)\n", total_peakactive / 1048576.0, total_peakactive );
+ printf( "Total memory allocated: %f MB (%i bytes)\n", total_allocated / 1048576.0, total_allocated );
+}
+
+void Q_ShutdownMem( void )
+{
+ size_t total;
+
+ if( !mem_initialized )
+ return;
+
+ total = 0;
+ while( chunks ) {
+ total += chunks->size;
+ qfree( (void *)((unsigned char *)chunks + sizeof(chunkheader_t)) );
+ }
+
+// printf( "Leaked memory: %f MB (%i bytes)\n", total / 1048576.0, total );
+// printf( "Total memory allocated: %f MB (%i bytes)\n", total_allocated / 1048576.0, total_allocated );
+
+ mem_initialized = false;
+}
+
+void *qmalloc( size_t size )
+{
+ void *data;
+ chunkheader_t *chunk;
+
+ if( size <= 0 )
+ return NULL;
+
+ if( mem_initialized )
+ data = malloc( sizeof(chunkheader_t) + size );
+ else
+ data = malloc( size );
+
+ if( !data )
+ Error( "Failed on allocating %d bytes\n", size );
+
+ if( !mem_initialized )
+ return data;
+
+ chunk = ( chunkheader_t * )data;
+ chunk->size = size;
+ chunk->next = chunks;
+ chunk->prev = NULL;
+ chunks = chunk;
+ if (chunk->next)
+ chunk->next->prev = chunk;
+
+ total_allocated += size;
+ total_active += size;
+ if( total_active > total_peakactive )
+ total_peakactive = total_active;
+
+ return ( void * )(( unsigned char * )data + sizeof(chunkheader_t) );
+}
+
+void qfree( void *data )
+{
+ chunkheader_t *chunk;
+
+ if( !data )
+ return;
+ if( !mem_initialized ) {
+ free( data );
+ return;
+ }
+
+ chunk = ( chunkheader_t * )(( unsigned char * )data - sizeof(chunkheader_t) );
+ if( chunk->prev )
+ chunk->prev->next = chunk->next;
+ else
+ chunks = chunk->next;
+ if( chunk->next )
+ chunk->next->prev = chunk->prev;
+
+ total_active -= chunk->size;
+
+ free( chunk );
+}
Index: hmap2/mem.h
diff -u /dev/null hmap2/mem.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/mem.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,7 @@
+
+void Q_InitMem( void );
+void Q_PrintMem( void );
+void Q_ShutdownMem( void );
+
+void *qmalloc( size_t size );
+void qfree( void *data );
Index: hmap2/outside.c
diff -u /dev/null hmap2/outside.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/outside.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,236 @@
+
+#include "bsp5.h"
+
+int outleafs;
+
+/*
+===========
+PointInLeaf
+
+Vic: rewrote to be faster
+LordHavoc: shortened a little
+===========
+*/
+node_t *PointInLeaf (node_t *node, vec3_t point)
+{
+ while (!node->contents)
+ node = node->children[PlaneDiff( point, &mapplanes[node->planenum] ) <= 0];
+ return node;
+}
+
+/*
+===========
+PlaceOccupant
+===========
+*/
+qboolean PlaceOccupant (int num, vec3_t point, node_t *headnode)
+{
+ node_t *n;
+
+ n = PointInLeaf (headnode, point);
+ if (n->contents == CONTENTS_SOLID)
+ return false;
+ n->occupied = num;
+ return true;
+}
+
+
+/*
+==============
+MarkLeakTrail
+==============
+*/
+portal_t *prevleaknode;
+FILE *leakfile;
+void MarkLeakTrail (portal_t *n2)
+{
+ int i;
+ vec3_t p1, p2, dir;
+ vec_t len;
+ portal_t *n1;
+
+ n1 = prevleaknode;
+ prevleaknode = n2;
+
+ if (!n1)
+ return;
+
+ WindingCentre( n2->winding, p1, NULL );
+ WindingCentre( n1->winding, p2, NULL );
+
+ VectorSubtract (p2, p1, dir);
+ len = VectorLength (dir);
+ VectorNormalize (dir);
+
+ if (!leakfile)
+ leakfile = fopen (pointfilename, "w");
+ if (!leakfile)
+ Error ("Couldn't open %s\n", pointfilename);
+
+ while (len > 2)
+ {
+ fprintf (leakfile, "%f %f %f\n", p1[0], p1[1], p1[2]);
+ for (i = 0;i < 3;i++)
+ p1[i] += dir[i] * 2;
+ len -= 2;
+ }
+}
+
+/*
+==================
+RecursiveFillOutside
+
+If fill is false, just check, don't fill
+Returns true if an occupied leaf is reached
+==================
+*/
+int hit_occupied;
+qboolean RecursiveFillOutside (node_t *l, int hullnum, qboolean fill)
+{
+ portal_t *p;
+ int s;
+
+ if (l->contents == CONTENTS_SOLID || l->contents == CONTENTS_SKY)
+ return false;
+
+ if (l->valid == valid)
+ return false;
+
+ if (l->occupied)
+ {
+ hit_occupied = l->occupied; // LordHavoc: this was missing from the released source... odd
+ return true;
+ }
+
+ l->valid = valid;
+
+// fill it and it's neighbors
+ if (fill)
+ l->contents = CONTENTS_SOLID;
+ outleafs++;
+
+ for (p = l->portals;p;)
+ {
+ s = (p->nodes[0] == l);
+
+ if (RecursiveFillOutside (p->nodes[s], hullnum, fill) )
+ {
+ // leaked, so stop filling
+ if (!hullnum)
+ MarkLeakTrail (p);
+ return true;
+ }
+ p = p->next[!s];
+ }
+
+ return false;
+}
+
+/*
+==================
+ClearOutFaces
+
+==================
+*/
+void ClearOutFaces (node_t *node)
+{
+ face_t **fp;
+
+ if (node->planenum != PLANENUM_LEAF)
+ {
+ ClearOutFaces (node->children[0]);
+ ClearOutFaces (node->children[1]);
+ return;
+ }
+ if (node->contents != CONTENTS_SOLID)
+ return;
+
+ for( fp = node->markfaces; *fp; fp++ ) {
+ // mark all the original faces that are removed
+ FreeWinding( (*fp)->winding );
+ (*fp)->winding = NULL;
+ }
+ node->faces = NULL;
+}
+
+
+//=============================================================================
+
+/*
+===========
+FillOutside
+
+===========
+*/
+qboolean FillOutside (tree_t *tree, int hullnum)
+{
+ int i;
+ int s;
+ qboolean inside;
+ vec3_t origin;
+
+ qprintf ("----- FillOutside ----\n");
+
+ if (nofill)
+ {
+ printf ("skipped\n");
+ return false;
+ }
+
+ inside = false;
+ for (i=1 ; i<num_entities ; i++)
+ {
+ GetVectorForKey (&entities[i], "origin", origin);
+
+ if (DotProduct (origin, origin) >= 0.1)
+ {
+ if (PlaceOccupant (i, origin, tree->headnode))
+ inside = true;
+ }
+ }
+
+ if (!inside)
+ {
+ printf ("Hullnum %i: No entities in empty space -- no filling performed\n", hullnum);
+ return false;
+ }
+
+ s = !(outside_node.portals->nodes[1] == &outside_node);
+
+// first check to see if an occupied leaf is hit
+ outleafs = 0;
+ valid++;
+
+ prevleaknode = NULL;
+
+ if (RecursiveFillOutside (outside_node.portals->nodes[s], hullnum, false))
+ {
+ if (leakfile)
+ fclose(leakfile);
+ leakfile = NULL;
+ if (!hullnum)
+ {
+ GetVectorForKey (&entities[hit_occupied], "origin", origin);
+
+ qprintf ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
+ qprintf ("reached occupant at: (%4.0f,%4.0f,%4.0f)\n", origin[0], origin[1], origin[2]);
+ qprintf ("no filling performed\n");
+ qprintf ("leak file written to %s\n", pointfilename);
+ qprintf ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
+ }
+
+ // remove faces from filled in leafs
+ ClearOutFaces (tree->headnode);
+ return false;
+ }
+
+ // now go back and fill things in
+ valid++;
+ RecursiveFillOutside (outside_node.portals->nodes[s], hullnum, true);
+
+ // remove faces from filled in leafs
+ ClearOutFaces (tree->headnode);
+
+ qprintf ("%4i outleafs\n", outleafs);
+ return true;
+}
Index: hmap2/portals.c
diff -u /dev/null hmap2/portals.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/portals.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,523 @@
+
+#include "bsp5.h"
+
+
+node_t outside_node; // portals outside the world face this
+
+//=============================================================================
+
+/*
+===========
+AllocPortal
+===========
+*/
+portal_t *AllocPortal( void )
+{
+ portal_t *p;
+
+ p = qmalloc( sizeof( portal_t ) );
+ memset( p, 0, sizeof( portal_t ) );
+
+ return p;
+}
+
+/*
+===========
+FreePortal
+===========
+*/
+void FreePortal( portal_t *p ) {
+ qfree( p );
+}
+
+//=============================================================================
+
+/*
+=============
+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;
+ }
+}
+
+//============================================================================
+
+/*
+================
+CalcNodeBounds
+
+Vic: proper node's bounding box calculation
+================
+*/
+void CalcNodeBounds (node_t *node)
+{
+ int i;
+ portal_t *p;
+ winding_t *w;
+ int side;
+
+ ClearBounds( node->mins, node->maxs );
+
+ for (p = node->portals ; p ; p = p->next[side])
+ {
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ side = 1;
+ else
+ {
+ Error ("CalcNodeBounds: mislinked portal");
+ return; // hush compiler about side uninitialized
+ }
+
+ for (i = 0, w = p->winding; i < w->numpoints; i++)
+ AddPointToBounds( w->points[i], node->mins, node->maxs );
+ }
+}
+
+/*
+================
+MakeHeadnodePortals
+
+The created portals will face the global outside_node
+================
+*/
+void MakeHeadnodePortals (node_t *node, vec3_t mins, vec3_t maxs)
+{
+ vec3_t bounds[2];
+ int i, j, n;
+ portal_t *p, *portals[6];
+ plane_t bplanes[6], *pl;
+ int side;
+
+ // pad with some space so there will never be null volume leafs
+ for (i=0 ; i<3 ; i++)
+ {
+ bounds[0][i] = mins[i] - SIDESPACE;
+ bounds[1][i] = maxs[i] + SIDESPACE;
+ }
+
+ outside_node.contents = CONTENTS_SOLID;
+ outside_node.portals = NULL;
+
+ 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->planenum = FindPlane (pl, &side);
+ p->winding = BaseWindingForPlane (pl);
+ if (side)
+ AddPortalToNodes (p, &outside_node, node);
+ else
+ AddPortalToNodes (p, node, &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;
+ portals[i]->winding = ClipWinding (portals[i]->winding, &bplanes[j], true);
+ }
+ }
+}
+
+//============================================================================
+
+/*
+================
+CutNodePortals_r
+
+================
+*/
+void CutNodePortals_r (node_t *node)
+{
+ plane_t *plane, clipplane;
+ node_t *f, *b, *other_node;
+ portal_t *p, *new_portal, *next_portal;
+ winding_t *w, *frontwinding, *backwinding;
+ int side;
+
+ // Vic: properly calculate the bounding box
+ CalcNodeBounds (node);
+
+ //
+ // separate the portals on node into it's children
+ //
+ if (node->contents)
+ return; // at a leaf, no more dividing
+
+ plane = &mapplanes[node->planenum];
+
+ f = node->children[0];
+ b = node->children[1];
+
+ //
+ // create the new portal by taking the full plane winding for the cutting plane
+ // and clipping it by all of the planes from the other portals
+ //
+ w = BaseWindingForPlane (&mapplanes[node->planenum]);
+ side = 0; // shut up compiler warning
+ for (p = node->portals ; p ; p = p->next[side])
+ {
+ clipplane = mapplanes[p->planenum];
+ if (p->nodes[0] == node)
+ side = 0;
+ else if (p->nodes[1] == node)
+ {
+ clipplane.dist = -clipplane.dist;
+ VectorNegate (clipplane.normal, clipplane.normal);
+ side = 1;
+ }
+ else
+ Error ("CutNodePortals_r: mislinked portal");
+
+ w = ClipWinding (w, &clipplane, true);
+ if (!w)
+ {
+ printf ("WARNING: CutNodePortals_r:new portal was clipped away\n");
+ break;
+ }
+ }
+
+ if (w)
+ {
+ // if the plane was not clipped on all sides, there was an error
+ new_portal = AllocPortal ();
+ new_portal->planenum = node->planenum;
+ new_portal->winding = w;
+ AddPortalToNodes (new_portal, f, b);
+ }
+
+ // partition the portals
+ 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 ("CutNodePortals_r: 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
+ DivideWindingEpsilon( p->winding, plane, &frontwinding, &backwinding, ON_EPSILON );
+
+ if (!frontwinding)
+ {
+ if (side == 0)
+ AddPortalToNodes (p, b, other_node);
+ else
+ AddPortalToNodes (p, other_node, b);
+ continue;
+ }
+ if (!backwinding)
+ {
+ 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);
+ }
+ }
+
+ CutNodePortals_r (f);
+ CutNodePortals_r (b);
+}
+
+
+/*
+==================
+PortalizeTree
+
+Builds the exact polyhedrons for the nodes and leafs
+==================
+*/
+void PortalizeTree (tree_t *tree)
+{
+ qprintf ("----- portalize ----\n");
+
+ MakeHeadnodePortals (tree->headnode, tree->mins, tree->maxs);
+ CutNodePortals_r (tree->headnode);
+}
+
+/*
+==================
+FreeNodePortals_r
+==================
+*/
+void FreeNodePortals_r (node_t *node)
+{
+ portal_t *p, *nextp;
+
+ if (!node->contents)
+ {
+ FreeNodePortals_r (node->children[0]);
+ FreeNodePortals_r (node->children[1]);
+ }
+
+ for (p=node->portals ; p ; p=nextp)
+ {
+ if (p->nodes[0] == node)
+ nextp = p->next[0];
+ else
+ nextp = p->next[1];
+ RemovePortalFromNode (p, p->nodes[0]);
+ RemovePortalFromNode (p, p->nodes[1]);
+ FreeWinding (p->winding);
+ FreePortal (p);
+ }
+}
+
+/*
+==================
+FreeTreePortals
+==================
+*/
+void FreeTreePortals (tree_t *tree) {
+ FreeNodePortals_r( tree->headnode );
+}
+
+/*
+==============================================================================
+
+PORTAL FILE GENERATION
+
+==============================================================================
+*/
+
+#define PORTALFILE "PRT1"
+
+FILE *pf;
+int num_visleafs; // leafs the player can be in
+int num_visportals;
+
+qboolean transwater;
+
+// Vic: proper float output
+void WriteFloatToPortalFile (vec_t f)
+{
+ int i;
+
+ i = Q_rint( f );
+ if( f == i )
+ fprintf( pf, "%i", i );
+ else
+ fprintf( pf, "%f", f );
+}
+
+void WritePortalFile_r (node_t *node)
+{
+ int i;
+ portal_t *p;
+ winding_t *w;
+ plane_t *pl, plane2;
+
+ if (!node->contents)
+ {
+ WritePortalFile_r (node->children[0]);
+ WritePortalFile_r (node->children[1]);
+ return;
+ }
+
+ if (node->contents == CONTENTS_SOLID)
+ return;
+
+ for (p = node->portals ; p ; )
+ {
+ w = p->winding;
+ // LordHavoc: transparent water support
+ if (w && p->nodes[0] == node && (p->nodes[0]->contents == p->nodes[1]->contents || (transwater && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)))
+ {
+ // 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
+ pl = &mapplanes[p->planenum];
+ PlaneFromWinding (w, &plane2);
+ if ( DotProduct (pl->normal, plane2.normal) < 0.99 ) // backwards...
+ fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->visleafnum, p->nodes[0]->visleafnum);
+ else
+ fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->visleafnum, p->nodes[1]->visleafnum);
+
+ // Vic: proper float output
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ fprintf (pf, "(");
+ WriteFloatToPortalFile (w->points[i][0]);
+ fprintf (pf, " ");
+ WriteFloatToPortalFile (w->points[i][1]);
+ fprintf (pf, " ");
+ WriteFloatToPortalFile (w->points[i][2]);
+ fprintf (pf, ")");
+ fprintf (pf, i == w->numpoints-1 ? "\n" : " ");
+ }
+ }
+
+ if (p->nodes[0] == node)
+ p = p->next[0];
+ else
+ p = p->next[1];
+ }
+}
+
+/*
+================
+NumberLeafs_r
+================
+*/
+void NumberLeafs_r (node_t *node)
+{
+ portal_t *p;
+
+ if (!node->contents)
+ { // decision node
+ node->visleafnum = -99;
+ NumberLeafs_r (node->children[0]);
+ NumberLeafs_r (node->children[1]);
+ return;
+ }
+
+ if (node->contents == CONTENTS_SOLID)
+ { // solid block, viewpoint never inside
+ node->visleafnum = -1;
+ return;
+ }
+
+ node->visleafnum = num_visleafs++;
+
+ for (p = node->portals ; p ; )
+ {
+ if (p->nodes[0] == node) // only write out from first leaf
+ {
+ if (p->nodes[0]->contents == p->nodes[1]->contents || (transwater && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY))
+ num_visportals++;
+ p = p->next[0];
+ }
+ else
+ p = p->next[1];
+ }
+}
+
+
+/*
+================
+WritePortalfile
+================
+*/
+void WritePortalfile (tree_t *tree)
+{
+ // set the visleafnum field in every leaf and count the total number of portals
+ num_visleafs = 0;
+ num_visportals = 0;
+ NumberLeafs_r (tree->headnode);
+
+ // write the file
+ printf ("writing %s\n", portfilename);
+ pf = fopen (portfilename, "w");
+ if (!pf)
+ Error ("Error opening %s", portfilename);
+
+ fprintf (pf, "%s\n", PORTALFILE);
+ fprintf (pf, "%i\n", num_visleafs);
+ fprintf (pf, "%i\n", num_visportals);
+
+ WritePortalFile_r (tree->headnode);
+
+ fclose (pf);
+}
Index: hmap2/qbsp.c
diff -u /dev/null hmap2/qbsp.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/qbsp.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,313 @@
+// bsp5.c
+
+#include "bsp5.h"
+
+//
+// command line flags
+//
+qboolean nofill;
+qboolean notjunc;
+qboolean noclip;
+qboolean onlyents;
+qboolean verbose;
+qboolean allverbose;
+qboolean transwater;
+qboolean forcevis;
+
+int subdivide_size;
+
+int valid;
+
+char bspfilename[1024];
+char pointfilename[1024];
+char portfilename[1024];
+
+int Vis_Main( int argc, char **argv );
+int Light_Main( int argc, char **argv );
+int Bsp2Prt_Main( int argc, char **argv );
+
+//===========================================================================
+
+void qprintf( char *fmt, ... )
+{
+ va_list argptr;
+ extern qboolean verbose;
+
+ if( !verbose )
+ return; // only print if verbose
+
+ va_start( argptr, fmt );
+ vprintf( fmt, argptr );
+ va_end( argptr );
+}
+
+//===========================================================================
+
+
+/*
+===============
+ProcessEntity
+===============
+*/
+void ProcessEntity (int entnum, int modnum, int hullnum)
+{
+ entity_t *ent;
+ tree_t *tree;
+
+ ent = &entities[entnum];
+ if( !ent->brushes )
+ return;
+
+ tree = Tree_ProcessEntity( ent, modnum, hullnum );
+ EmitNodePlanes( tree->headnode );
+
+ if( hullnum != 0 ) {
+ EmitClipNodes( tree->headnode, modnum, hullnum );
+ } else {
+ EmitNodeFaces( tree->headnode );
+ EmitDrawNodes( tree->headnode );
+ }
+
+ FreeTree( tree );
+}
+
+/*
+=================
+UpdateEntLump
+=================
+*/
+void UpdateEntLump (void)
+{
+ int m, entnum;
+ char mod[80];
+
+ m = 1;
+ for (entnum = 1 ; entnum < num_entities ; entnum++)
+ {
+ if (!entities[entnum].brushes)
+ continue;
+ sprintf (mod, "*%i", m);
+ SetKeyValue (&entities[entnum], "model", mod);
+ m++;
+ }
+
+ UnparseEntities();
+}
+
+/*
+=================
+CreateSingleHull
+=================
+*/
+void CreateSingleHull ( int hullnum )
+{
+ int entnum;
+ int modnum;
+
+ // for each entity in the map file that has geometry
+ verbose = true; // print world
+ for( entnum = 0, modnum = 0; entnum < num_entities; entnum++ ) {
+ if( !entities[entnum].brushes )
+ continue;
+
+ ProcessEntity( entnum, modnum++, hullnum );
+ if( !allverbose )
+ verbose = false; // don't print rest of entities
+ }
+}
+
+/*
+=================
+CreateHulls
+=================
+*/
+void CreateHulls (void)
+{
+ // commanded to ignore the hulls altogether
+ if (noclip)
+ {
+ CreateSingleHull ( 0 );
+ return;
+ }
+
+ // create all the hulls
+
+ // create the hulls sequentially
+ printf ("building hulls sequentially...\n");
+
+ CreateSingleHull ( 0 );
+ CreateSingleHull ( 1 );
+ CreateSingleHull ( 2 );
+}
+
+/*
+=================
+ProcessFile
+=================
+*/
+void ProcessFile (char *sourcebase, char *bspfilename1)
+{
+ // create filenames
+ strcpy(bspfilename, bspfilename1);
+ strcpy(portfilename, bspfilename1);
+ strcpy(pointfilename, bspfilename1);
+ DefaultExtension(bspfilename, ".bsp");
+ ReplaceExtension(portfilename, ".prt");
+ ReplaceExtension(pointfilename, ".pts");
+
+ if (!onlyents)
+ {
+ remove (bspfilename);
+ remove (portfilename);
+ remove (pointfilename);
+ }
+
+ // load brushes and entities
+ LoadMapFile (sourcebase);
+ if (onlyents)
+ {
+ LoadBSPFile (bspfilename);
+ UpdateEntLump ();
+ WriteBSPFile (bspfilename, false);
+ return;
+ }
+
+ // init the tables to be shared by all models
+ BeginBSPFile ();
+
+ // the clipping hulls will be written out to text files by forked processes
+ CreateHulls ();
+
+ UpdateEntLump ();
+
+ WriteMiptex ();
+
+ FinishBSPFile ();
+}
+
+
+/*
+==================
+main
+
+==================
+*/
+int main (int argc, char **argv)
+{
+ int i = 0;
+ double start, end;
+ char sourcename[1024];
+ char destname[1024];
+
+ // malloc_debug (15);
+
+ //
+ // check command line flags
+ //
+
+ nofill = false;
+ notjunc = false;
+ noclip = false;
+ onlyents = false;
+ verbose = true;
+ allverbose = false;
+ transwater = true;
+ forcevis = true;
+ subdivide_size = 240;
+
+ if( argc == 1 )
+ goto error;
+
+ if( !strcmp( argv[1], "-bsp2prt" ) )
+ return Bsp2Prt_Main( argc - 1, argv + 1 );
+ else if( !strcmp( argv[1], "-vis" ) )
+ return Vis_Main( argc - 1, argv + 1 );
+ else if( !strcmp( argv[1], "-light" ) )
+ return Light_Main( argc - 1, argv + 1 );
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (argv[i][0] != '-')
+ break;
+ else if (!strcmp (argv[i],"-nowater"))
+ transwater = false;
+ else if (!strcmp (argv[i],"-notjunc"))
+ notjunc = true;
+ else if (!strcmp (argv[i],"-nofill"))
+ nofill = true;
+ else if (!strcmp (argv[i],"-noclip"))
+ noclip = true;
+ else if (!strcmp (argv[i],"-onlyents"))
+ onlyents = true;
+ else if (!strcmp (argv[i],"-verbose"))
+ allverbose = true;
+ else if (!strcmp (argv[i],"-subdivide"))
+ {
+ subdivide_size = atoi(argv[i+1]);
+ i++;
+ }
+ else if (!strcmp (argv[i],"-darkplaces"))
+ {
+ // produce 256x256 texel lightmaps
+ subdivide_size = 4080;
+ }
+ else if (!strcmp (argv[i],"-noforcevis"))
+ forcevis = false;
+ else
+ Error ("qbsp: Unknown option '%s'", argv[i]);
+ }
+
+ if (i != argc - 2 && i != argc - 1)
+error:
+ Error ("%s",
+"usage: hqbsp [options] sourcefile [destfile]\n"
+"options:\n"
+"-bsp2prt builds .prt file for .bsp, run -bsp2prt as the first parameter for more\n"
+"-light lighting utility, run -light as the first parameter for more\n"
+"-vis vis utility, run -vis as the first parameter for more\n"
+"-nowater disable watervis; r_wateralpha in glquake will not work right\n"
+"-notjunc disable tjunction fixing; glquake will have holes between polygons\n"
+"-nofill disable sealing of map and vis, used for ammoboxes\n"
+"-onlyents patchs entities in existing .bsp, for relighting\n"
+"-verbose show more messages\n"
+"-darkplaces allow really big polygons\n"
+"-noforcevis don't make a .prt if the map leaks\n"
+"-fast build the bsp tree only once, compiles a bit faster (experimental)\n"
+ );
+
+ // argv[i] and argv[arc-1] are the same if only one name is supplied
+ strcpy(sourcename, argv[i]);
+ strcpy(destname, argv[argc-1]);
+ DefaultExtension(sourcename, ".map");
+ DefaultExtension(destname, ".bsp");
+ if (!strcmp(sourcename, destname))
+ ReplaceExtension(destname, ".bsp");
+ printf("inputfile: %s\n", sourcename);
+ printf("outputfile: %s\n", destname);
+ if (!strcmp(sourcename, destname))
+ Error("sourcename == destname\n");
+
+ // init memory
+ Q_InitMem ();
+
+ //
+ // do it!
+ //
+ start = I_DoubleTime ();
+ ProcessFile (sourcename, destname);
+ end = I_DoubleTime ();
+ printf ("%5.1f seconds elapsed\n\n", end-start);
+
+ // print memory stats
+ Q_PrintMem ();
+
+#if _MSC_VER && _DEBUG
+ printf("press any key\n");
+ getchar();
+#endif
+
+ // free allocated memory
+ Q_ShutdownMem ();
+
+ return 0;
+}
Index: hmap2/relight.bat
diff -u /dev/null hmap2/relight.bat:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/relight.bat Sun Feb 15 21:50:37 2004
@@ -0,0 +1 @@
+ at hmap -light -relight %1
Index: hmap2/revis.bat
diff -u /dev/null hmap2/revis.bat:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/revis.bat Sun Feb 15 21:50:37 2004
@@ -0,0 +1,2 @@
+ at hmap -bsp2prt %1
+ at hmap -vis -noreuse %1
Index: hmap2/scriptlib.c
diff -u /dev/null hmap2/scriptlib.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/scriptlib.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,93 @@
+#include "cmdlib.h"
+
+// LordHavoc: increased maximum token length from 128 to 1024
+#define MAXTOKEN 1024
+
+char token[MAXTOKEN];
+qboolean unget;
+char *script_p;
+int scriptline;
+
+void StartTokenParsing (char *data)
+{
+ scriptline = 1;
+ script_p = data;
+ unget = false;
+}
+
+qboolean GetToken (qboolean crossline)
+{
+ char *token_p;
+
+ if (unget) // is a token already waiting?
+ return true;
+
+ //
+ // skip space
+ //
+ skipspace:
+ while (*script_p <= 32)
+ {
+ if (!*script_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ return false;
+ }
+ if (*script_p++ == '\n')
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ scriptline++;
+ }
+ }
+
+ if (script_p[0] == '/' && script_p[1] == '/') // comment field
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete\n",scriptline);
+ while (*script_p++ != '\n')
+ if (!*script_p)
+ {
+ if (!crossline)
+ Error ("Line %i is incomplete",scriptline);
+ return false;
+ }
+ goto skipspace;
+ }
+
+ //
+ // copy token
+ //
+ token_p = token;
+
+ if (*script_p == '"')
+ {
+ script_p++;
+ while ( *script_p != '"' )
+ {
+ if (!*script_p)
+ Error ("EOF inside quoted token");
+ *token_p++ = *script_p++;
+ if (token_p > &token[MAXTOKEN-1])
+ Error ("Token too large on line %i",scriptline);
+ }
+ script_p++;
+ }
+ else while ( *script_p > 32 )
+ {
+ *token_p++ = *script_p++;
+ if (token_p > &token[MAXTOKEN-1])
+ Error ("Token too large on line %i",scriptline);
+ }
+
+ *token_p = 0;
+
+ return true;
+}
+
+void UngetToken (void)
+{
+ unget = true;
+}
+
Index: hmap2/solidbsp.c
diff -u /dev/null hmap2/solidbsp.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/solidbsp.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,558 @@
+// solidbsp.c
+
+#include "bsp5.h"
+
+int leaffaces;
+int c_nodefaces;
+int c_subdivides;
+int splitnodes;
+
+int c_solid, c_empty, c_water, c_slime, c_lava, c_sky;
+
+qboolean usemidsplit;
+
+//============================================================================
+
+/*
+===========
+AllocNode
+===========
+*/
+node_t *AllocNode( void )
+{
+ node_t *n;
+
+ n = qmalloc( sizeof( node_t ) );
+ memset( n, 0, sizeof( node_t ) );
+
+ return n;
+}
+
+/*
+===========
+FreeNode
+===========
+*/
+void FreeNode( node_t *n ) {
+ qfree( n );
+}
+
+//============================================================================
+
+/*
+==================
+ChooseMidPlaneFromList
+
+The clipping hull BSP doesn't worry about avoiding splits
+==================
+*/
+surface_t *ChooseMidPlaneFromList( surface_t *surfaces, vec3_t mins, vec3_t maxs )
+{
+ int j, l;
+ surface_t *p, *bestsurface;
+ vec_t bestvalue, value, dist;
+ plane_t *plane;
+
+ // pick the plane that splits the least
+ bestvalue = 8*8192*8192;
+ bestsurface = NULL;
+
+ for( p = surfaces; p; p = p->next ) {
+ if( p->onnode )
+ continue;
+
+ plane = &mapplanes[p->planenum];
+
+ // check for axis aligned surfaces
+ l = plane->type;
+ if( l > PLANE_Z )
+ continue;
+
+ // calculate the split metric along axis l, smaller values are better
+ value = 0;
+ dist = plane->dist;
+
+ for( j = 0; j < 3; j++ ) {
+ if( j == l ) {
+ value += (maxs[l] - dist) * (maxs[l] - dist);
+ value += (dist - mins[l]) * (dist - mins[l]);
+ continue;
+ }
+ value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]);
+ }
+
+ if( value < bestvalue ) { // currently the best!
+ bestvalue = value;
+ bestsurface = p;
+ }
+ }
+
+ if( !bestsurface ) {
+ for( p = surfaces; p; p = p->next ) {
+ if( !p->onnode )
+ return p; // first valid surface
+ }
+ Error( "ChooseMidPlaneFromList: no valid planes" );
+ }
+
+ return bestsurface;
+}
+
+/*
+==================
+ChoosePlaneFromList
+
+The real BSP hueristic
+==================
+*/
+surface_t *ChoosePlaneFromList( surface_t *surfaces, vec3_t mins, vec3_t maxs )
+{
+ int j, k, l;
+ surface_t *p, *p2, *bestsurface;
+ vec_t bestvalue, bestdistribution, value, dist;
+ plane_t *plane;
+ face_t *f;
+
+ // pick the plane that splits the least
+ bestvalue = 8*8192*8192;
+ bestsurface = NULL;
+ bestdistribution = 9e30;
+
+ for( p = surfaces; p; p = p->next ) {
+ if( p->onnode )
+ continue;
+
+ k = 0;
+ plane = &mapplanes[p->planenum];
+
+ for( p2 = surfaces; p2; p2 = p2->next ) {
+ if( p2 == p || p2->onnode )
+ continue;
+
+ for( f = p2->faces; f; f = f->next ) {
+ if( WindingOnPlaneSide( f->winding, plane ) == SIDE_CROSS ) {
+ if( ++k >= bestvalue )
+ break;
+ }
+ }
+ if( k > bestvalue )
+ break;
+ }
+
+ if( k > bestvalue )
+ continue;
+
+ // if equal numbers, axial planes win, then decide on spatial subdivision
+ if( k < bestvalue || (k == bestvalue && plane->type < PLANE_ANYX) ) {
+ // check for axis aligned surfaces
+ l = plane->type;
+
+ if( l <= PLANE_Z ) { // axial aligned
+ // calculate the split metric along axis l
+ value = 0;
+ dist = plane->dist;
+
+ for( j = 0; j < 3; j++ ) {
+ if( j == l ) {
+ value += (maxs[l] - dist) * (maxs[l] - dist);
+ value += (dist - mins[l]) * (dist - mins[l]);
+ continue;
+ }
+ value += 2 * (maxs[j] - mins[j]) * (maxs[j] - mins[j]);
+ }
+
+ if( value > bestdistribution && k == bestvalue )
+ continue;
+ bestdistribution = value;
+ }
+
+ // currently the best!
+ bestvalue = k;
+ bestsurface = p;
+ }
+ }
+
+ return bestsurface;
+}
+
+
+/*
+==================
+SelectPartition
+
+Selects a surface from a linked list of surfaces to split the group on
+returns NULL if the surface list can not be divided any more (a leaf)
+==================
+*/
+surface_t *SelectPartition( surface_t *surfaces )
+{
+ int i;
+ vec3_t mins, maxs;
+ surface_t *p, *bestsurface;
+
+ // calculate a bounding box of the entire surfaceset
+ ClearBounds( mins, maxs );
+
+ // count onnode surfaces
+ i = 0;
+ bestsurface = NULL;
+ for( p = surfaces; p; p = p->next ) {
+ AddPointToBounds( p->mins, mins, maxs );
+ AddPointToBounds( p->maxs, mins, maxs );
+
+ if( !p->onnode ) {
+ i++;
+ bestsurface = p;
+ }
+ }
+
+ if( !i )
+ return NULL;
+ else if( i == 1 )
+ return bestsurface; // this is a final split
+
+ if( usemidsplit ) // do fast way for clipping hull
+ return ChooseMidPlaneFromList( surfaces, mins, maxs );
+
+ // do slow way to save poly splits for drawing hull
+ return ChoosePlaneFromList( surfaces, mins, maxs );
+}
+
+//============================================================================
+
+/*
+=================
+CalcSurfaceInfo
+
+Calculates the bounding box
+=================
+*/
+void CalcSurfaceInfo( surface_t *surf )
+{
+ int i;
+ face_t *f;
+ winding_t *w;
+
+ if( !surf->faces )
+ Error( "CalcSurfaceInfo: surface without a face" );
+
+ // calculate a bounding box
+ ClearBounds( surf->mins, surf->maxs );
+
+ for( f = surf->faces; f; f = f->next ) {
+ if( f->contents[0] >= 0 || f->contents[1] >= 0 )
+ Error( "Bad contents" );
+
+ for( i = 0, w = f->winding; i < w->numpoints; i++ )
+ AddPointToBounds( w->points[i], surf->mins, surf->maxs );
+ }
+}
+
+/*
+==================
+DividePlane
+==================
+*/
+void DividePlane( surface_t *in, plane_t *split, surface_t **front, surface_t **back )
+{
+ face_t *facet, *next;
+ face_t *frontlist, *backlist;
+ face_t *frontfrag, *backfrag;
+ surface_t *news;
+ plane_t *inplane;
+
+ inplane = &mapplanes[in->planenum];
+
+ // parallel case is easy
+ if( VectorCompare( inplane->normal, split->normal ) ) {
+ // check for exactly on node
+ if( inplane->dist == split->dist ) { // divide the facets to the front and back sides
+ news = AllocSurface ();
+ *news = *in;
+
+ facet = in->faces;
+ in->faces = NULL;
+ news->faces = NULL;
+ in->onnode = news->onnode = true;
+
+ for( ; facet; facet = next ) {
+ next = facet->next;
+
+ if( facet->planeside ) {
+ facet->next = news->faces;
+ news->faces = facet;
+ } else {
+ facet->next = in->faces;
+ in->faces = facet;
+ }
+ }
+
+ if( in->faces )
+ *front = in;
+ else
+ *front = NULL;
+ if( news->faces )
+ *back = news;
+ else
+ *back = NULL;
+ return;
+ }
+
+ if( inplane->dist > split->dist ) {
+ *front = in;
+ *back = NULL;
+ } else {
+ *front = NULL;
+ *back = in;
+ }
+ return;
+ }
+
+ // do a real split. may still end up entirely on one side
+ // OPTIMIZE: use bounding box for fast test
+ frontlist = NULL;
+ backlist = NULL;
+
+ for( facet = in->faces; facet; facet = next ) {
+ next = facet->next;
+ SplitFace( facet, split, &frontfrag, &backfrag );
+
+ if( frontfrag ) {
+ frontfrag->next = frontlist;
+ frontlist = frontfrag;
+ }
+ if( backfrag ) {
+ backfrag->next = backlist;
+ backlist = backfrag;
+ }
+ }
+
+ // if nothing actually got split, just move the in plane
+ if( frontlist == NULL ) {
+ *front = NULL;
+ *back = in;
+ in->faces = backlist;
+ return;
+ }
+
+ if( backlist == NULL ) {
+ *front = in;
+ *back = NULL;
+ in->faces = frontlist;
+ return;
+ }
+
+ // stuff got split, so allocate one new plane and reuse in
+ news = AllocSurface ();
+ *news = *in;
+ news->faces = backlist;
+ *back = news;
+
+ in->faces = frontlist;
+ *front = in;
+
+ // recalc bboxes and flags
+ CalcSurfaceInfo( news );
+ CalcSurfaceInfo( in );
+}
+
+/*
+==================
+LinkConvexFaces
+
+Determines the contents of the leaf and creates the final list of
+original faces that have some fragment inside this leaf
+==================
+*/
+void LinkConvexFaces( surface_t *planelist, node_t *leafnode )
+{
+ int i, count;
+ face_t *f, *next;
+ surface_t *surf, *pnext;
+
+ leafnode->faces = NULL;
+ leafnode->contents = 0;
+ leafnode->planenum = -1;
+
+ count = 0;
+ for( surf = planelist; surf; surf = surf->next ) {
+ for( f = surf->faces; f; f = f->next ) {
+ count++;
+ if( !leafnode->contents )
+ leafnode->contents = f->contents[0];
+ else if( leafnode->contents != f->contents[0] )
+ Error ("LinkConvexFaces: Mixed face contents in leafnode");
+ }
+ }
+
+ if( !leafnode->contents )
+ leafnode->contents = CONTENTS_SOLID;
+
+ switch( leafnode->contents ) {
+ case CONTENTS_EMPTY:
+ c_empty++;
+ break;
+ case CONTENTS_SOLID:
+ c_solid++;
+ break;
+ case CONTENTS_WATER:
+ c_water++;
+ break;
+ case CONTENTS_SLIME:
+ c_slime++;
+ break;
+ case CONTENTS_LAVA:
+ c_lava++;
+ break;
+ case CONTENTS_SKY:
+ c_sky++;
+ break;
+ default:
+ Error ("LinkConvexFaces: bad contents number");
+ }
+
+ // write the list of faces, and free the originals
+ leaffaces += count;
+ leafnode->markfaces = qmalloc( sizeof(face_t *) * (count + 1) );
+ for( i = 0, surf = planelist; surf; surf = pnext ) {
+ pnext = surf->next;
+ for( f = surf->faces; f; f = next, i++ ) {
+ next = f->next;
+ leafnode->markfaces[i] = f->original;
+ FreeFace( f );
+ }
+ FreeSurface( surf );
+ }
+ leafnode->markfaces[i] = NULL; // sentinel
+}
+
+/*
+==================
+LinkNodeFaces
+
+Returns a duplicated list of all faces on surface
+==================
+*/
+face_t *LinkNodeFaces( surface_t *surface )
+{
+ face_t *f, *list, *newf, **prevptr;
+
+ list = NULL;
+
+ // subdivide
+ prevptr = &surface->faces;
+ while( 1 ) {
+ f = *prevptr;
+ if( !f )
+ break;
+ c_subdivides += SubdivideFace( f, prevptr );
+ f = *prevptr;
+ prevptr = &f->next;
+ }
+
+ // copy
+ for( f = surface->faces; f; f = f->next, c_nodefaces++ ) {
+ newf = NewFaceFromFace( f );
+ newf->winding = CopyWinding( f->winding );
+ f->original = newf;
+ newf->next = list;
+ list = newf;
+ }
+
+ return list;
+}
+
+/*
+==================
+PartitionSurfaces
+==================
+*/
+void PartitionSurfaces( surface_t *surfaces, node_t *node )
+{
+ surface_t *split, *p, *next;
+ surface_t *frontlist, *backlist;
+ surface_t *frontfrag, *backfrag;
+ plane_t *splitplane;
+
+ split = SelectPartition (surfaces);
+ if( !split ) { // this is a leaf node
+ node->planenum = PLANENUM_LEAF;
+ LinkConvexFaces( surfaces, node );
+ return;
+ }
+
+ splitnodes++;
+ node->faces = LinkNodeFaces( split );
+ node->children[0] = AllocNode ();
+ node->children[1] = AllocNode ();
+ node->planenum = split->planenum;
+
+ splitplane = &mapplanes[split->planenum];
+
+ // multiple surfaces, so split all the polysurfaces into front and back lists
+ frontlist = NULL;
+ backlist = NULL;
+
+ for( p = surfaces; p; p = next ) {
+ next = p->next;
+ DividePlane( p, splitplane, &frontfrag, &backfrag );
+
+ if( frontfrag ) {
+ if( !frontfrag->faces )
+ Error( "surface with no faces" );
+ frontfrag->next = frontlist;
+ frontlist = frontfrag;
+ }
+ if( backfrag ) {
+ if( !backfrag->faces )
+ Error ("surface with no faces");
+ backfrag->next = backlist;
+ backlist = backfrag;
+ }
+ }
+
+ PartitionSurfaces( frontlist, node->children[0] );
+ PartitionSurfaces( backlist, node->children[1] );
+}
+
+/*
+==================
+SolidBSP
+==================
+*/
+void SolidBSP( tree_t *tree, qboolean midsplit )
+{
+ int i;
+ node_t *headnode;
+
+ qprintf( "----- SolidBSP -----\n" );
+
+ headnode = AllocNode ();
+ usemidsplit = midsplit;
+
+ // calculate a bounding box for the entire model
+ for( i = 0; i < 3; i++ ) {
+ headnode->mins[i] = tree->mins[i] - SIDESPACE;
+ headnode->maxs[i] = tree->maxs[i] + SIDESPACE;
+ }
+
+ // recursively partition everything
+ splitnodes = 0;
+ leaffaces = 0;
+ c_nodefaces = 0;
+ c_solid = c_empty = c_water = c_slime = c_lava = c_sky = 0;
+ c_subdivides = 0;
+
+ PartitionSurfaces( tree->surfaces, tree->headnode = headnode );
+
+ qprintf( "%5i split nodes\n", splitnodes );
+ qprintf( "%5i solid leafs\n", c_solid );
+ qprintf( "%5i empty leafs\n", c_empty );
+ qprintf( "%5i water leafs\n", c_water );
+ qprintf( "%5i slime leafs\n", c_slime );
+ qprintf( "%5i lava leafs\n", c_lava );
+ qprintf( "%5i sky leafs\n", c_sky );
+ qprintf( "%5i leaffaces\n",leaffaces );
+ qprintf( "%5i nodefaces\n", c_nodefaces );
+ qprintf( "%5i subdivides\n", c_subdivides );
+}
Index: hmap2/tjunc.c
diff -u /dev/null hmap2/tjunc.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/tjunc.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,517 @@
+// tjunc.c
+
+#include "bsp5.h"
+
+
+typedef struct wvert_s
+{
+ vec_t t;
+ struct wvert_s *prev, *next;
+} wvert_t;
+
+typedef struct wedge_s
+{
+ struct wedge_s *next;
+ vec3_t dir;
+ vec3_t origin;
+ wvert_t head;
+} wedge_t;
+
+int numwedges, numwverts;
+int c_tjuncs;
+int c_tjuncfaces;
+int c_rotated;
+int c_degenerateEdges;
+int c_degenerateFaces;
+
+#define MAX_WVERTS 0x40000
+#define MAX_WEDGES 0x20000
+
+
+wvert_t *wverts;
+wedge_t *wedges;
+
+//============================================================================
+
+#define NUM_HASH 1024
+
+wedge_t *wedge_hash[NUM_HASH];
+
+static vec3_t hash_min, hash_scale;
+
+static void InitHash (vec3_t mins, vec3_t maxs)
+{
+ vec3_t size;
+ vec_t volume;
+ vec_t scale;
+ int newsize[2];
+
+ VectorCopy (mins, hash_min);
+ VectorSubtract (maxs, mins, size);
+ memset (wedge_hash, 0, sizeof(wedge_hash));
+
+ volume = size[0]*size[1];
+
+ scale = sqrt(volume / NUM_HASH);
+
+ newsize[0] = size[0] / scale;
+ newsize[1] = size[1] / scale;
+
+ hash_scale[0] = newsize[0] / size[0];
+ hash_scale[1] = newsize[1] / size[1];
+ hash_scale[2] = newsize[1];
+}
+
+static unsigned HashVec (vec3_t vec)
+{
+ unsigned h;
+
+ h = hash_scale[0] * (vec[0] - hash_min[0]) * hash_scale[2]
+ + hash_scale[1] * (vec[1] - hash_min[1]);
+ if ( h >= NUM_HASH)
+ return NUM_HASH - 1;
+ return h;
+}
+
+//============================================================================
+
+// Vic: changed this to qboolean
+qboolean CanonicalVector (vec3_t vec)
+{
+ vec_t length;
+
+ length = VectorLength (vec);
+
+ // Vic: ignore degenerate edges
+ if (length < 0.1)
+ return false;
+
+ length = (vec_t)1.0 / length;
+ vec[0] *= length;
+ vec[1] *= length;
+ vec[2] *= length;
+
+ if (vec[0] > EQUAL_EPSILON)
+ return true;
+ else if (vec[0] < -EQUAL_EPSILON)
+ {
+ VectorNegate (vec, vec);
+ return true;
+ }
+ else
+ vec[0] = 0;
+
+ if (vec[1] > EQUAL_EPSILON)
+ return true;
+ else if (vec[1] < -EQUAL_EPSILON)
+ {
+ VectorNegate (vec, vec);
+ return true;
+ }
+ else
+ vec[1] = 0;
+
+ if (vec[2] > EQUAL_EPSILON)
+ return true;
+ else if (vec[2] < -EQUAL_EPSILON)
+ {
+ VectorNegate (vec, vec);
+ return true;
+ }
+ else
+ vec[2] = 0;
+
+ Error ("CanonicalVector: degenerate");
+ return false;
+}
+
+wedge_t *FindEdge (vec3_t p1, vec3_t p2, vec_t *t1, vec_t *t2)
+{
+ vec3_t origin;
+ vec3_t dir;
+ wedge_t *w;
+ vec_t temp;
+ int h;
+
+ VectorSubtract (p2, p1, dir);
+
+ // ignore degenerate edges
+ if (!CanonicalVector (dir))
+ {
+ c_degenerateEdges++;
+ return NULL;
+ }
+
+ *t1 = DotProduct (p1, dir);
+ *t2 = DotProduct (p2, dir);
+
+ VectorMA (p1, -*t1, dir, origin);
+
+ if (*t1 > *t2)
+ {
+ temp = *t1;
+ *t1 = *t2;
+ *t2 = temp;
+ }
+
+ h = HashVec (origin);
+
+ for (w = wedge_hash[h] ; w ; w=w->next)
+ {
+ temp = w->origin[0] - origin[0];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+ temp = w->origin[1] - origin[1];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+ temp = w->origin[2] - origin[2];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+
+ temp = w->dir[0] - dir[0];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+ temp = w->dir[1] - dir[1];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+ temp = w->dir[2] - dir[2];
+ if (temp < -EQUAL_EPSILON || temp > EQUAL_EPSILON)
+ continue;
+
+ return w;
+ }
+
+ if (numwedges == MAX_WEDGES)
+ Error ("FindEdge: numwedges == MAX_WEDGES");
+ w = &wedges[numwedges];
+ numwedges++;
+
+ w->next = wedge_hash[h];
+ wedge_hash[h] = w;
+
+ VectorCopy (origin, w->origin);
+ VectorCopy (dir, w->dir);
+ w->head.next = w->head.prev = &w->head;
+ w->head.t = 99999;
+ return w;
+}
+
+
+/*
+===============
+AddVert
+
+===============
+*/
+#define T_EPSILON 0.01
+
+void AddVert (wedge_t *w, vec_t t)
+{
+ wvert_t *v, *newv;
+
+ v = w->head.next;
+ do
+ {
+ if (fabs(v->t - t) < T_EPSILON)
+ return;
+ if (v->t > t)
+ break;
+ v = v->next;
+ } while (1);
+
+// insert a new wvert before v
+ if (numwverts == MAX_WVERTS)
+ Error ("AddVert: numwverts == MAX_WVERTS");
+
+ newv = &wverts[numwverts];
+ numwverts++;
+
+ newv->t = t;
+ newv->next = v;
+ newv->prev = v->prev;
+ v->prev->next = newv;
+ v->prev = newv;
+}
+
+
+/*
+===============
+AddEdge
+
+===============
+*/
+void AddEdge (vec3_t p1, vec3_t p2)
+{
+ wedge_t *w;
+ vec_t t1, t2;
+
+ w = FindEdge(p1, p2, &t1, &t2);
+ if (w)
+ {
+ AddVert (w, t1);
+ AddVert (w, t2);
+ }
+}
+
+//============================================================================
+
+face_t *newlist;
+
+#define MAX_VERTS_ON_FACE 32
+#define MAX_VERTS_ON_SUPERFACE 8192
+
+typedef struct
+{
+ int numpoints;
+ vec3_t points[MAX_VERTS_ON_SUPERFACE];
+ face_t original;
+} superface_t;
+
+superface_t superface;
+
+void FaceFromSuperface (face_t *original)
+{
+ int i, numpts;
+ face_t *newf, *chain;
+ vec3_t dir, test;
+ vec_t v;
+ int firstcorner, lastcorner;
+
+ chain = NULL;
+ do
+ {
+ if( superface.numpoints < 3 ) {
+ c_degenerateFaces++;
+ return;
+ }
+
+ if (superface.numpoints <= MAX_VERTS_ON_FACE)
+ { // the face is now small enough without more cutting
+ // so copy it back to the original
+ *original = superface.original;
+ original->winding = CopyWindingExt( superface.numpoints, superface.points );
+ original->original = chain;
+ original->next = newlist;
+ newlist = original;
+ return;
+ }
+
+ c_tjuncfaces++;
+
+restart:
+ // find the last corner
+ VectorSubtract (superface.points[superface.numpoints-1], superface.points[0], dir);
+ VectorNormalize (dir);
+ for (lastcorner=superface.numpoints-1 ; lastcorner > 0 ; lastcorner--)
+ {
+ VectorSubtract (superface.points[lastcorner-1], superface.points[lastcorner], test);
+ VectorNormalize (test);
+ v = DotProduct (test, dir);
+ if (v < 0.9999 || v > 1.00001)
+ {
+ break;
+ }
+ }
+
+ // find the first corner
+ VectorSubtract (superface.points[1], superface.points[0], dir);
+ VectorNormalize (dir);
+ for (firstcorner=1 ; firstcorner < superface.numpoints-1 ; firstcorner++)
+ {
+ VectorSubtract (superface.points[firstcorner+1], superface.points[firstcorner], test);
+ VectorNormalize (test);
+ v = DotProduct (test, dir);
+ if (v < 0.9999 || v > 1.00001)
+ {
+ break;
+ }
+ }
+
+ if (firstcorner+2 >= MAX_VERTS_ON_FACE)
+ {
+ c_rotated++;
+ // rotate the point winding
+ VectorCopy (superface.points[0], test);
+ for (i=1 ; i<superface.numpoints ; i++)
+ {
+ VectorCopy (superface.points[i], superface.points[i-1]);
+ }
+ VectorCopy (test, superface.points[superface.numpoints-1]);
+ goto restart;
+ }
+
+
+ // cut off as big a piece as possible, less than MAXPOINTS, and not
+ // past lastcorner
+ newf = NewFaceFromFace (&superface.original);
+ newf->original = chain;
+ chain = newf;
+ newf->next = newlist;
+ newlist = newf;
+
+ if (superface.numpoints - firstcorner <= MAX_VERTS_ON_FACE)
+ numpts = firstcorner + 2;
+ else if (lastcorner+2 < MAX_VERTS_ON_FACE && superface.numpoints - lastcorner <= MAX_VERTS_ON_FACE)
+ numpts = lastcorner + 2;
+ else
+ numpts = MAX_VERTS_ON_FACE;
+
+ if( numpts < 3 ) {
+ c_degenerateFaces++;
+ return;
+ }
+ newf->winding = CopyWindingExt( numpts, superface.points );
+
+ for (i=newf->winding->numpoints-1 ; i<superface.numpoints ; i++)
+ VectorCopy (superface.points[i], superface.points[i-(newf->winding->numpoints-2)]);
+ superface.numpoints -= (newf->winding->numpoints-2);
+ } while (1);
+}
+
+/*
+===============
+FixFaceEdges
+
+===============
+*/
+void FixFaceEdges (face_t *f)
+{
+ int i, j, k;
+ wedge_t *w;
+ wvert_t *v;
+ vec_t t1, t2;
+
+ if( f->winding->numpoints > MAX_VERTS_ON_SUPERFACE )
+ Error( "FixFaceEdges: f->winding->numpoints > MAX_VERTS_ON_SUPERFACE" );
+
+ superface.original = *f;
+ superface.numpoints = f->winding->numpoints;
+ memcpy( superface.points, f->winding->points, sizeof(vec3_t)*f->winding->numpoints );
+ FreeWinding( f->winding );
+
+ // LordHavoc: FIXME: rewrite this mess to find points on edges,
+ // rather than finding edges that run into polygons
+restart:
+ for (i=0 ; i < superface.numpoints ; i++)
+ {
+ j = (i+1)%superface.numpoints;
+
+ w = FindEdge (superface.points[i], superface.points[j], &t1, &t2);
+ if (!w)
+ continue;
+
+ for (v=w->head.next ; v->t < t1 + T_EPSILON ; v = v->next)
+ {
+ }
+
+ if (v->t < t2-T_EPSILON)
+ {
+ c_tjuncs++;
+ // insert a new vertex here
+ for (k = superface.numpoints ; k> j ; k--)
+ VectorCopy (superface.points[k-1], superface.points[k]);
+ VectorMA (w->origin, v->t, w->dir, superface.points[j]);
+ superface.numpoints++;
+ goto restart;
+ }
+ }
+
+ // the face might be split into multiple faces because of too many edges
+ FaceFromSuperface( f );
+}
+
+//============================================================================
+
+void tjunc_find_r (node_t *node)
+{
+ int i, j;
+ face_t *f;
+ winding_t *w;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ for (f=node->faces ; f ; f=f->next) {
+ w = f->winding;
+ for( i = 0, j = 1; i < w->numpoints; i++, j = (i + 1) % w->numpoints )
+ AddEdge( w->points[i], w->points[j] );
+ }
+
+ tjunc_find_r (node->children[0]);
+ tjunc_find_r (node->children[1]);
+}
+
+void tjunc_fix_r (node_t *node)
+{
+ face_t *f, *next;
+
+ if (node->planenum == PLANENUM_LEAF)
+ return;
+
+ newlist = NULL;
+
+ for (f=node->faces ; f ; f=next)
+ {
+ next = f->next;
+ FixFaceEdges (f);
+ }
+
+ node->faces = newlist;
+
+ tjunc_fix_r (node->children[0]);
+ tjunc_fix_r (node->children[1]);
+}
+
+/*
+===========
+FixTJunctions
+===========
+*/
+void FixTJunctions(tree_t *tree)
+{
+ vec_t radius;
+ vec3_t maxs, mins;
+
+ qprintf ("---- tjunc ----\n");
+
+ if (notjunc)
+ return;
+
+//
+// identify all points on common edges
+//
+
+// origin points won't allways be inside the map, so extend the hash area
+ radius = RadiusFromBounds( tree->mins, tree->maxs );
+ VectorSet( maxs, radius, radius, radius );
+ VectorSet( mins, -radius, -radius, -radius );
+
+ InitHash (mins, maxs);
+
+ numwedges = numwverts = 0;
+ wverts = qmalloc( sizeof(*wverts) * MAX_WVERTS );
+ wedges = qmalloc( sizeof(*wverts) * MAX_WEDGES );
+
+ tjunc_find_r (tree->headnode);
+
+ qprintf ("%i world edges %i edge points\n", numwedges, numwverts);
+
+//
+// add extra vertexes on edges where needed
+//
+ c_tjuncs = c_tjuncfaces = c_degenerateEdges = c_degenerateFaces = c_rotated = 0;
+
+ tjunc_fix_r (tree->headnode);
+
+ qfree( wverts );
+ qfree( wedges );
+
+ // Vic: report number of degenerate edges
+ qprintf ("%i degenerate edges\n", c_degenerateEdges);
+ qprintf ("%i degenerate faces\n", c_degenerateFaces);
+ qprintf ("%i edges added by tjunctions\n", c_tjuncs);
+ qprintf ("%i faces added by tjunctions\n", c_tjuncfaces);
+ qprintf ("%i naturally ordered\n", c_tjuncfaces - c_rotated);
+ qprintf ("%i rotated orders\n", c_rotated);
+}
+
Index: hmap2/tree.c
diff -u /dev/null hmap2/tree.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/tree.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,122 @@
+#include "bsp5.h"
+
+//============================================================================
+
+/*
+===========
+AllocTree
+===========
+*/
+tree_t *AllocTree( void )
+{
+ tree_t *t;
+
+ t = qmalloc( sizeof( tree_t ) );
+ memset( t, 0, sizeof( tree_t ) );
+ ClearBounds( t->mins, t->maxs );
+
+ return t;
+}
+
+/*
+===========
+FreeTree
+===========
+*/
+void FreeTree( tree_t *t ) {
+ brush_t *b, *next;
+
+ for( b = t->brushes; b; b = next ) {
+ next = b->next;
+ FreeBrush( b );
+ }
+ qfree( t );
+}
+
+//============================================================================
+
+/*
+===============
+Tree_ProcessEntity
+===============
+*/
+tree_t *Tree_ProcessEntity( entity_t *ent, int modnum, int hullnum )
+{
+ tree_t *tree;
+ qboolean worldmodel;
+
+ if( !strcmp( ValueForKey( ent, "classname" ), "worldspawn" ) ) {
+ worldmodel = true;
+ } else {
+ worldmodel = false;
+
+ if( verbose )
+ PrintEntity( ent );
+ }
+
+ // allocate a tree to hold our entity
+ tree = AllocTree ();
+
+ // take the brushes and clip off all overlapping and contained faces,
+ // leaving a perfect skin of the model with no hidden faces
+ Brush_LoadEntity( ent, tree, hullnum );
+ if( !tree->brushes ) {
+ PrintEntity( ent );
+ Error( "Entity with no valid brushes" );
+ }
+
+ CSGFaces( tree );
+
+ if( hullnum != 0 ) {
+ SolidBSP( tree, true );
+
+ // assume non-world bmodels are simple
+ if( worldmodel && !nofill ) {
+ PortalizeTree( tree );
+
+ if( FillOutside( tree, hullnum ) ) {
+ GatherTreeFaces( tree );
+ SolidBSP( tree, false ); // make a really good tree
+ }
+ FreeTreePortals( tree );
+ }
+ } else {
+ // if not the world, make a good tree first
+ // the world is just going to make a bad tree
+ // because the outside filling will force a regeneration later
+ SolidBSP( tree, worldmodel ); // SolidBSP generates a node tree
+
+ // build all the portals in the bsp tree
+ // some portals are solid polygons, and some are paths to other leafs
+
+ // assume non-world bmodels are simple
+ if( worldmodel && (!nofill || forcevis) ) {
+ PortalizeTree( tree );
+
+ if( FillOutside( tree, 0 ) || forcevis ) {
+ FreeTreePortals( tree );
+
+ // get the remaining faces together into surfaces again
+ GatherTreeFaces( tree );
+
+ // merge polygons
+ MergeTreeFaces( tree );
+
+ // make a really good tree
+ SolidBSP( tree, false );
+
+ // make the real portals for vis tracing
+ PortalizeTree( tree );
+
+ // save portal file for vis tracing
+ WritePortalfile( tree );
+
+ // fix tjunctions
+ FixTJunctions( tree );
+ }
+ FreeTreePortals( tree );
+ }
+ }
+
+ return tree;
+}
Index: hmap2/vis.c
diff -u /dev/null hmap2/vis.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/vis.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,728 @@
+// vis.c
+
+#include "vis.h"
+
+#define MAX_THREADS 4
+
+int numportals;
+int portalleafs;
+
+portal_t *portals;
+leaf_t *leafs;
+
+int c_portaltest, c_portalpass, c_portalcheck;
+int c_reused;
+
+int c_cnt;
+
+// past visfile
+byte *vismap, *vismap_p, *vismap_end;
+int originalvismapsize;
+
+// [bitbytes*portalleafs]
+byte *uncompressed;
+
+// (portalleafs+63)>>3
+int bitbytes;
+int bitlongs;
+
+int numthreads = 4;
+
+qboolean fastvis;
+qboolean verbose;
+qboolean rvis;
+qboolean noreuse;
+
+// LordHavoc: default to level 4 vis
+int testlevel = 4;
+// LordHavoc: optional ambient sounds
+qboolean noambient, noambientwater, noambientslime, noambientlava, noambientsky;
+
+//=============================================================================
+
+/*
+=============
+GetNextPortal
+
+Returns the next portal for a thread to work on
+Returns the portals from the least complex, so the later ones can reuse
+the earlier information.
+=============
+*/
+portal_t *GetNextPortal (void)
+{
+ int j;
+ portal_t *p, *tp;
+ int min;
+
+ min = 99999;
+ p = NULL;
+
+ for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
+ {
+ if (tp->nummightsee < min && tp->status == stat_none)
+ {
+ min = tp->nummightsee;
+ p = tp;
+ }
+ }
+
+
+ if (p)
+ p->status = stat_working;
+
+ return p;
+}
+
+long portalizestarttime, portalschecked;
+
+/*
+==============
+LeafThread
+==============
+*/
+void *LeafThread (int thread)
+{
+ time_t oldtime, newtime;
+ portal_t *p;
+
+//printf ("Begining LeafThread: %i\n",(int)thread);
+ oldtime = time(NULL);
+ do
+ {
+ p = GetNextPortal ();
+ if (!p)
+ break;
+
+ PortalFlow (p);
+
+ portalschecked++;
+ if (verbose)
+ printf ("portal %4i of %4i mightsee:%4i cansee:%4i\n", (int) portalschecked, (int) numportals * 2, (int) p->nummightsee, (int) p->numcansee);
+ else
+ {
+ newtime = time(NULL);
+ if (newtime != oldtime)
+ {
+ printf("\rportal %4i of %4i (%3i%%), estimated time left: %i seconds", (int) portalschecked, (int) numportals * 2, (int) (portalschecked*100/(numportals*2)), (int) ((numportals*2-portalschecked)*(newtime-portalizestarttime)/portalschecked));
+ fflush(stdout);
+ oldtime = newtime;
+ }
+ }
+ } while (1);
+ printf("\n");
+
+//printf ("Completed LeafThread: %i\n",(int)thread);
+
+ return NULL;
+}
+
+/*
+===============
+LeafFlow
+
+Builds the entire visibility list for a leaf
+===============
+*/
+int totalvis;
+
+void LeafFlow (int leafnum)
+{
+ leaf_t *leaf;
+ byte *outbuffer;
+ byte compressed[MAX_MAP_LEAFS/8];
+ int i, j;
+ int numvis;
+ byte *dest;
+ portal_t *p;
+
+//
+// flow through all portals, collecting visible bits
+//
+ outbuffer = uncompressed + leafnum*bitbytes;
+ leaf = &leafs[leafnum];
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+ if (p->status != stat_done)
+ Error ("portal not done");
+ for (j=0 ; j<bitbytes ; j++)
+ outbuffer[j] |= p->visbits[j];
+ }
+
+ if (outbuffer[leafnum>>3] & (1<<(leafnum&7)))
+ Error ("Leaf portals saw into leaf");
+
+ outbuffer[leafnum>>3] |= (1<<(leafnum&7));
+
+ numvis = 0;
+ for (i=0 ; i<portalleafs ; i++)
+ if (outbuffer[i>>3] & (1<<(i&3)))
+ numvis++;
+
+//
+// compress the bit string
+//
+ if (verbose)
+ printf ("leaf %4i : %4i visible\n", leafnum, numvis);
+ totalvis += numvis;
+
+ if( !noreuse ) { // Vic: reuse old vis data
+ int i;
+ byte *data;
+
+ data = uncompressed;
+ for( i = 0; i < leafnum; i++, data += bitbytes ) {
+ if( !memcmp( data, outbuffer, bitbytes ) ) {
+ c_reused++;
+ dleafs[leafnum+1].visofs = dleafs[i+1].visofs;
+ return;
+ }
+ }
+ }
+
+#if 0
+ i = bitbytes;
+ memcpy (compressed, outbuffer, bitbytes);
+#else
+ i = CompressVis (outbuffer, compressed, bitbytes);
+#endif
+
+
+ dest = vismap_p;
+ vismap_p += i;
+
+ if (vismap_p > vismap_end)
+ Error ("Vismap expansion overflow");
+
+ dleafs[leafnum+1].visofs = dest-vismap; // leaf 0 is a common solid
+
+ memcpy (dest, compressed, i);
+}
+
+
+/*
+==================
+CalcPortalVis
+==================
+*/
+void CalcPortalVis (void)
+{
+ int i;
+
+// fastvis just uses mightsee for a very loose bound
+ if (fastvis)
+ {
+ for (i=0 ; i<numportals*2 ; i++)
+ {
+ portals[i].visbits = portals[i].mightsee;
+ portals[i].status = stat_done;
+ }
+ return;
+ }
+
+ portalizestarttime = time(NULL);
+
+ LeafThread (0);
+
+ if (verbose)
+ {
+ printf ("portalcheck: %i portaltest: %i portalpass: %i\n",c_portalcheck, c_portaltest, c_portalpass);
+ printf ("c_vistest: %i c_mighttest: %i\n",c_vistest, c_mighttest);
+ }
+}
+
+/*
+==================
+CalcVis
+==================
+*/
+void CalcVis (void)
+{
+ int i;
+
+ BasePortalVis ();
+
+ CalcPortalVis ();
+
+//
+// assemble the leaf vis lists by oring and compressing the portal lists
+//
+ for (i=0 ; i<portalleafs ; i++)
+ LeafFlow (i);
+
+ printf ("average leafs visible: %i\n", totalvis / portalleafs);
+ fflush(stdout);
+}
+
+/*
+==============================================================================
+
+PASSAGE CALCULATION (not used yet...)
+
+==============================================================================
+*/
+
+int count_sep;
+
+sep_t *Findpassages (viswinding_t *source, viswinding_t *pass)
+{
+ int i, j, k, l;
+ plane_t plane;
+ vec3_t v1, v2;
+ double d;
+ double length;
+ int counts[3];
+ qboolean fliptest;
+ sep_t *sep, *list;
+
+ list = NULL;
+
+// check all combinations
+ for (i=0 ; i<source->numpoints ; i++)
+ {
+ l = (i+1)%source->numpoints;
+ VectorSubtract (source->points[l] , source->points[i], v1);
+
+ // fing a vertex of pass that makes a plane that puts all of the
+ // vertexes of pass on the front side and all of the vertexes of
+ // source on the back side
+ for (j=0 ; j<pass->numpoints ; j++)
+ {
+ VectorSubtract (pass->points[j], source->points[i], v2);
+ CrossProduct(v1, v2, plane.normal);
+
+ // if points don't make a valid plane, skip it
+ length = DotProduct( plane.normal, plane.normal );
+
+ if (length < ON_EPSILON)
+ continue;
+
+ length = 1/sqrt(length);
+ VectorScale( plane.normal, length, plane.normal );
+ plane.dist = DotProduct (pass->points[j], plane.normal);
+
+ //
+ // find out which side of the generated seperating plane has the
+ // source portal
+ //
+ fliptest = false;
+ for (k=0 ; k<source->numpoints ; k++)
+ {
+ if (k == i || k == l)
+ continue;
+ d = DotProduct (source->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ { // source is on the negative side, so we want all
+ // pass and target on the positive side
+ fliptest = false;
+ break;
+ }
+ else if (d > ON_EPSILON)
+ { // source is on the positive side, so we want all
+ // pass and target on the negative side
+ fliptest = true;
+ break;
+ }
+ }
+ if (k == source->numpoints)
+ continue; // planar with source portal
+
+ //
+ // flip the normal if the source portal is backwards
+ //
+ if (fliptest)
+ {
+ VectorNegate (plane.normal, plane.normal);
+ plane.dist = -plane.dist;
+ }
+
+ //
+ // if all of the pass portal points are now on the positive side,
+ // this is the seperating plane
+ //
+ counts[0] = counts[1] = counts[2] = 0;
+ for (k=0 ; k<pass->numpoints ; k++)
+ {
+ if (k==j)
+ continue;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ break;
+ else if (d > ON_EPSILON)
+ counts[0]++;
+ else
+ counts[2]++;
+ }
+ if (k != pass->numpoints)
+ continue; // points on negative side, not a seperating plane
+
+ if (!counts[0])
+ continue; // planar with pass portal
+
+ //
+ // save this out
+ //
+ count_sep++;
+
+ sep = qmalloc(sizeof(*sep));
+ sep->next = list;
+ list = sep;
+ sep->plane = plane;
+ }
+ }
+
+ return list;
+}
+
+
+
+/*
+============
+CalcPassages
+============
+*/
+void CalcPassages (void)
+{
+ int i, j, k;
+ int count, count2;
+ leaf_t *l;
+ portal_t *p1, *p2;
+ sep_t *sep;
+ passage_t *passages;
+
+ printf ("building passages...\n");
+
+ count = count2 = 0;
+ for (i=0 ; i<portalleafs ; i++)
+ {
+ l = &leafs[i];
+
+ for (j=0 ; j<l->numportals ; j++)
+ {
+ p1 = l->portals[j];
+ for (k=0 ; k<l->numportals ; k++)
+ {
+ if (k==j)
+ continue;
+
+ count++;
+ p2 = l->portals[k];
+
+ // definately can't see into a coplanar portal
+ if (PlaneCompare (&p1->plane, &p2->plane) )
+ continue;
+
+ count2++;
+
+ sep = Findpassages (p1->winding, p2->winding);
+ if (!sep)
+ {
+// Error ("No seperating planes found in portal pair");
+ count_sep++;
+ sep = qmalloc(sizeof(*sep));
+ sep->next = NULL;
+ sep->plane = p1->plane;
+ }
+ passages = qmalloc(sizeof(*passages));
+ passages->planes = sep;
+ passages->from = p1->leaf;
+ passages->to = p2->leaf;
+ passages->next = l->passages;
+ l->passages = passages;
+ }
+ }
+ }
+
+ printf ("numpassages: %i (%i)\n", count2, count);
+ printf ("total passages: %i\n", count_sep);
+}
+
+//=============================================================================
+
+/*
+============
+LoadPortals
+============
+*/
+void LoadPortals (char *name)
+{
+ int i, j;
+ portal_t *p;
+ leaf_t *l;
+ char magic[80];
+ FILE *f;
+ int numpoints;
+ viswinding_t *w;
+ int leafnums[2];
+ plane_t plane;
+
+ if (!strcmp(name,"-"))
+ f = stdin;
+ else
+ {
+ f = fopen(name, "r");
+ if (!f)
+ {
+ printf ("LoadPortals: couldn't read %s\n",name);
+ printf ("No vising performed.\n");
+ exit (1);
+ }
+ }
+
+ if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalleafs, &numportals) != 3)
+ Error ("LoadPortals: failed to read header");
+ if (strcmp(magic,PORTALFILE))
+ Error ("LoadPortals: not a portal file");
+
+ printf ("%4i portalleafs\n", portalleafs);
+ printf ("%4i numportals\n", numportals);
+
+ bitbytes = ((portalleafs+63)&~63)>>3;
+ bitlongs = bitbytes/sizeof(long);
+
+// each file portal is split into two memory portals
+ portals = qmalloc(2*numportals*sizeof(portal_t));
+ memset (portals, 0, 2*numportals*sizeof(portal_t));
+
+ leafs = qmalloc(portalleafs*sizeof(leaf_t));
+ memset (leafs, 0, portalleafs*sizeof(leaf_t));
+
+ originalvismapsize = portalleafs*((portalleafs+7)/8);
+
+ vismap = vismap_p = dvisdata;
+ vismap_end = vismap + MAX_MAP_VISIBILITY;
+
+ for (i=0, p=portals ; i<numportals ; i++)
+ {
+ if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1])
+ != 3)
+ Error ("LoadPortals: reading portal %i", i);
+ if (numpoints > MAX_POINTS_ON_VISWINDING)
+ Error ("LoadPortals: portal %i has too many points", i);
+ if ((unsigned)leafnums[0] > (unsigned)portalleafs
+ || (unsigned)leafnums[1] > (unsigned)portalleafs)
+ Error ("LoadPortals: reading portal %i", i);
+
+ w = p->winding = NewVisWinding (numpoints);
+ w->original = true;
+ w->numpoints = numpoints;
+
+ for (j=0 ; j<numpoints ; j++)
+ {
+ double v[3];
+ int k;
+
+ // scanf into double, then assign to vec_t
+ if (fscanf (f, "(%lf %lf %lf ) ", &v[0], &v[1], &v[2]) != 3)
+ Error ("LoadPortals: reading portal %i", i);
+ for (k=0 ; k<3 ; k++)
+ w->points[j][k] = v[k];
+ }
+ fscanf (f, "\n");
+
+ // calc plane
+ PlaneFromVisWinding (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;
+ VectorNegate (plane.normal, p->plane.normal);
+ p->plane.dist = -plane.dist;
+ if (p->plane.normal[0] == 1)
+ p->plane.type = PLANE_X;
+ else if (p->plane.normal[1] == 1)
+ p->plane.type = PLANE_Y;
+ else if (p->plane.normal[2] == 1)
+ p->plane.type = PLANE_Z;
+ else
+ p->plane.type = PLANE_ANYX;
+ p->leaf = leafnums[1];
+ 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 = w;
+ p->plane = plane;
+ if (p->plane.normal[0] == 1)
+ p->plane.type = PLANE_X;
+ else if (p->plane.normal[1] == 1)
+ p->plane.type = PLANE_Y;
+ else if (p->plane.normal[2] == 1)
+ p->plane.type = PLANE_Z;
+ else
+ p->plane.type = PLANE_ANYX;
+ p->leaf = leafnums[0];
+ p++;
+
+ }
+
+ fclose (f);
+}
+
+
+/*
+===========
+Vis_Main
+===========
+*/
+int Vis_Main( int argc, char **argv )
+{
+ int i;
+ char portalfile[1024];
+ char source[1024];
+ double start, end;
+
+ printf ("---- vis ----\n");
+
+ fastvis = false;
+ verbose = false;
+ rvis = true;
+ noambientslime = true;
+ noreuse = false;
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i], "-norvis"))
+ {
+ rvis = false;
+ printf ("rvis optimization disabled\n");
+ }
+ else if (!strcmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-fast"))
+ {
+ printf ("fastvis = true\n");
+ fastvis = true;
+ }
+ else if (!strcmp(argv[i], "-level"))
+ {
+ testlevel = atoi(argv[i+1]);
+ printf ("testlevel = %i\n", testlevel);
+ i++;
+ }
+ else if (!strcmp(argv[i], "-v"))
+ {
+ printf ("verbose = true\n");
+ verbose = true;
+ }
+ else if (!strcmp(argv[i], "-noreuse"))
+ {
+ printf ("vis rows reusage disabled\n");
+ noreuse = true;
+ }
+ else if (!strcmp(argv[i], "-noambient"))
+ {
+ noambient = true;
+ printf ("all ambient sounds disabled\n");
+ }
+ else if (!strcmp(argv[i], "-noambientwater"))
+ {
+ noambientwater = true;
+ printf ("ambient water sounds disabled\n");
+ }
+ else if (!strcmp(argv[i], "-ambientslime"))
+ {
+ noambientslime = false;
+ printf ("ambient slime sounds enabled\n");
+ }
+ else if (!strcmp(argv[i], "-noambientlava"))
+ {
+ noambientlava = true;
+ printf ("ambient lava sounds disabled\n");
+ }
+ else if (!strcmp(argv[i], "-noambientsky"))
+ {
+ noambientsky = true;
+ printf ("ambient sky sounds disabled\n");
+ }
+ else if (argv[i][0] == '-')
+ Error ("Unknown option \"%s\"", argv[i]);
+ else
+ break;
+ }
+
+ if (i != argc - 1)
+ {
+ Error ("%s",
+"usage: hvis [options] bspfile"
+"options:\n"
+"-level 0-4 quality, default 4\n"
+"-fast fast but bad quality vis\n"
+"-v verbose\n"
+"-norvis disable rvis optimization, 0.001% better quality and 30% slower\n"
+"-ambientslime enables ambient slime sounds (reserve a channel, do not default to water)\n"
+"-noambient disable ambient sounds (water bubbling, wind, etc)\n"
+"-noambientwater disable ambient water sounds (water)\n"
+"-noambientsky disable ambient sky sounds (wind)\n"
+"-noambientlava disable ambient lava sounds (unused by quake)\n"
+ );
+ }
+
+ // init memory
+ Q_InitMem ();
+
+ start = I_DoubleTime ();
+
+ strcpy(source, argv[i]);
+ strcpy(portalfile, source);
+ DefaultExtension(source, ".bsp");
+ ReplaceExtension(portalfile, ".prt");
+
+ LoadBSPFile (source);
+ LoadPortals (portalfile);
+
+ uncompressed = qmalloc(bitbytes*portalleafs);
+ memset (uncompressed, 0, bitbytes*portalleafs);
+
+// CalcPassages ();
+
+ CalcVis ();
+
+ printf ("row size: %i\n",bitbytes);
+ printf ("c_reused: %i\n",c_reused);
+ printf ("c_chains: %i\n",c_chains);
+
+ visdatasize = vismap_p - dvisdata;
+ printf ("reused bytes: %i\n",c_reused*bitbytes);
+ printf ("visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize);
+
+ if (!noambient)
+ CalcAmbientSounds ();
+
+ WriteBSPFile (source, false);
+
+// unlink (portalfile);
+
+ printf( "%i\n", c_cnt );
+
+ end = I_DoubleTime ();
+ printf ("%5.2f seconds elapsed\n\n", end-start);
+
+ // print memory stats
+ Q_PrintMem ();
+
+#if _MSC_VER && _DEBUG
+ printf("press any key\n");
+ getchar();
+#endif
+
+ // free allocated memory
+ Q_ShutdownMem ();
+
+ return 0;
+}
+
Index: hmap2/vis.h
diff -u /dev/null hmap2/vis.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/vis.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,114 @@
+// vis.h
+
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "bspfile.h"
+#include "mem.h"
+
+#define MAX_PORTALS 32768
+
+#define PORTALFILE "PRT1"
+
+#define ON_EPSILON 0.1
+
+typedef struct
+{
+ qboolean original; // don't free, it's part of the portal
+ int numpoints;
+ vec3_t points[8]; // variable sized
+} viswinding_t;
+
+#define MAX_POINTS_ON_VISWINDING 64
+
+viswinding_t *NewVisWinding (int points);
+void FreeVisWinding (viswinding_t *w);
+viswinding_t *ClipVisWinding (viswinding_t *in, plane_t *split, qboolean keepon);
+viswinding_t *CopyVisWinding (viswinding_t *w);
+void PlaneFromVisWinding (viswinding_t *w, plane_t *plane);
+void VisWindingCentre( viswinding_t *w, vec3_t centre, vec_t *radius );
+
+
+typedef enum {stat_none, stat_working, stat_done} vstatus_t;
+typedef struct
+{
+ plane_t plane; // normal pointing into neighbor
+ int leaf; // neighbor
+ viswinding_t *winding;
+ vstatus_t status;
+ byte *visbits;
+ byte *mightsee;
+ int nummightsee;
+ int numcansee;
+} portal_t;
+
+typedef struct seperating_plane_s
+{
+ struct seperating_plane_s *next;
+ plane_t plane; // from portal is on positive side
+} sep_t;
+
+
+typedef struct passage_s
+{
+ struct passage_s *next;
+ int from, to; // leaf numbers
+ sep_t *planes;
+} passage_t;
+
+// LordHavoc: a friend ran into this limit (was 128)
+#define MAX_PORTALS_ON_LEAF 512
+typedef struct leaf_s
+{
+ int numportals;
+ passage_t *passages;
+ portal_t *portals[MAX_PORTALS_ON_LEAF];
+} leaf_t;
+
+
+typedef struct pstack_s
+{
+ struct pstack_s *next;
+ leaf_t *leaf;
+ portal_t *portal; // portal exiting
+ viswinding_t *source, *pass;
+ plane_t portalplane;
+ byte *mightsee; // bit string
+} pstack_t;
+
+typedef struct
+{
+ byte *leafvis; // bit string
+ portal_t *base;
+ pstack_t pstack_head;
+} threaddata_t;
+
+extern int numportals;
+extern int portalleafs;
+
+extern portal_t *portals;
+extern leaf_t *leafs;
+
+extern int c_portaltest, c_portalpass, c_portalcheck;
+extern int c_portalskip, c_leafskip;
+extern int c_vistest, c_mighttest;
+extern int c_chains;
+extern int c_reused;
+
+extern byte *vismap, *vismap_p, *vismap_end; // past visfile
+
+extern int testlevel;
+
+extern qboolean rvis;
+extern qboolean noreuse;
+extern qboolean noambientwater, noambientslime, noambientlava, noambientsky;
+
+extern byte *uncompressed;
+extern int bitbytes;
+extern int bitlongs;
+
+void LeafFlow (int leafnum);
+void BasePortalVis (void);
+
+void PortalFlow (portal_t *p);
+
+void CalcAmbientSounds (void);
Index: hmap2/vis_flow.c
diff -u /dev/null hmap2/vis_flow.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/vis_flow.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,697 @@
+#include "vis.h"
+
+int c_chains;
+int c_portalskip, c_leafskip;
+int c_vistest, c_mighttest;
+
+int active;
+
+//=============================================================================
+
+/*
+==================
+NewVisWinding
+==================
+*/
+viswinding_t *NewVisWinding (int points)
+{
+ viswinding_t *w;
+ int size;
+
+ if (points > MAX_POINTS_ON_VISWINDING)
+ Error ("NewWinding: %i points", points);
+
+ size = (int)((viswinding_t *)0)->points[points];
+ w = qmalloc (size);
+ memset (w, 0, size);
+
+ return w;
+}
+
+/*
+==================
+FreeVisWinding
+==================
+*/
+void FreeVisWinding (viswinding_t *w)
+{
+ if (!w->original)
+ qfree (w);
+}
+
+/*
+==================
+CopyVisWinding
+==================
+*/
+viswinding_t *CopyVisWinding (viswinding_t *w)
+{
+ int size;
+ viswinding_t *c;
+
+ size = (int)((viswinding_t *)0)->points[w->numpoints];
+ c = qmalloc (size);
+ memcpy (c, w, size);
+ c->original = false;
+ return c;
+}
+
+/*
+==================
+ClipVisWinding
+
+Clips the winding to the plane, returning the new winding on the positive side
+Frees the input winding.
+If keepon is true, an exactly on-plane winding will be saved, otherwise
+it will be clipped away.
+==================
+*/
+viswinding_t *ClipVisWinding (viswinding_t *in, plane_t *split, qboolean keepon)
+{
+ int i, j;
+ vec_t dists[MAX_POINTS_ON_VISWINDING + 1];
+ int sides[MAX_POINTS_ON_VISWINDING + 1];
+ int counts[3];
+ vec_t dot;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ viswinding_t *neww;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+// determine sides for each point
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
+ 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]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (keepon && !counts[0] && !counts[1])
+ return in;
+
+ if (!counts[0])
+ {
+ FreeVisWinding (in);
+ return NULL;
+ }
+ if (!counts[1])
+ return in;
+
+ maxpts = in->numpoints+4; // can't use counts[0]+2 because
+ // of fp grouping errors
+ neww = NewVisWinding (maxpts);
+
+ for (i=0 ; i<in->numpoints ; i++)
+ {
+ p1 = in->points[i];
+
+ 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;
+
+ // 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++;
+ }
+
+ if (neww->numpoints > maxpts)
+ Error ("ClipVisWinding: points exceeded estimate");
+
+// free the original winding
+ FreeVisWinding (in);
+
+ return neww;
+}
+
+void PlaneFromVisWinding (viswinding_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->dist = DotProduct (w->points[0], plane->normal);
+}
+
+/*
+=================
+VisWindingCentre
+=================
+*/
+void VisWindingCentre( viswinding_t *w, vec3_t centre, vec_t *radius )
+{
+ int i;
+ vec3_t edge;
+ vec_t dist, best;
+
+ if( !w->numpoints )
+ Error( "WindingCentre: no points" );
+
+ VectorCopy( w->points[0], centre );
+ for( i = 1; i < w->numpoints; i++ )
+ VectorAdd( centre, w->points[i], centre );
+ VectorScale( centre, (vec_t)1.0/w->numpoints, centre );
+
+ if( radius ) {
+ best = 0;
+ for( i = 0; i < w->numpoints; i++ ) {
+ VectorSubtract( centre, w->points[i], edge );
+ dist = VectorLength( edge );
+ if( dist > best )
+ best = dist;
+ }
+
+ #ifdef PARANOID
+ if( best == 0 )
+ Error( "WindingCentre: winding has zero radius" );
+ #endif
+
+ *radius = best;
+ }
+}
+
+void CheckStack (leaf_t *leaf, threaddata_t *thread)
+{
+ pstack_t *p;
+
+ for (p=thread->pstack_head.next ; p ; p=p->next)
+ if (p->leaf == leaf)
+ Error ("CheckStack: leaf recursion");
+}
+
+
+/*
+==============
+ClipToSeperators
+
+Source, pass, and target are an ordering of portals.
+
+Generates separating planes candidates 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.
+==============
+*/
+viswinding_t *ClipToSeperators (viswinding_t *source, viswinding_t *pass, viswinding_t *target, qboolean flipclip)
+{
+ int i, j, k, l;
+ plane_t plane;
+ vec3_t v1, v2;
+ vec_t d, epcompare;
+ vec_t length;
+// int counts[3];
+ qboolean fliptest;
+
+// check all combinations
+ for (i=0 ; i<source->numpoints ; i++)
+ {
+ l = (i+1)%source->numpoints;
+ VectorSubtract (source->points[l] , source->points[i], v1);
+
+ // find a vertex of pass that makes a plane that puts all of the
+ // vertexes of pass on the front side and all of the vertexes of
+ // source on the back side
+ for (j=0 ; j<pass->numpoints ; j++)
+ {
+ VectorSubtract (pass->points[j], source->points[i], v2);
+ CrossProduct(v1, v2, plane.normal);
+
+ // if points don't make a valid plane, skip it
+ length = DotProduct(plane.normal, plane.normal);
+
+ if (length < ON_EPSILON)
+ continue;
+
+ length = 1/sqrt(length);
+ VectorScale( plane.normal, length, plane.normal );
+ plane.dist = DotProduct (pass->points[j], plane.normal);
+
+ //
+ // find out which side of the generated seperating plane has the
+ // source portal
+ //
+ fliptest = false;
+ for (k=0 ; k<source->numpoints ; k++)
+ {
+ if (k == i || k == l)
+ continue;
+ d = DotProduct (source->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ { // source is on the negative side, so we want all
+ // pass and target on the positive side
+ fliptest = false;
+ break;
+ }
+ else if (d > ON_EPSILON)
+ { // source is on the positive side, so we want all
+ // pass and target on the negative side
+ fliptest = true;
+ break;
+ }
+ }
+ if (k == source->numpoints)
+ continue; // planar with source portal
+
+ //
+ // flip the normal if the source portal is backwards
+ //
+ if (fliptest)
+ {
+ VectorInverse (plane.normal);
+ plane.dist = -plane.dist;
+ }
+
+ //
+ // if all of the pass portal points are now on the positive side,
+ // this is the seperating plane
+ //
+ // LordHavoc: rewrote this to be faster
+ for (k=0;k<j;k++)
+ {
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON) goto cliptoseperatorscontinue;
+ else if (d > ON_EPSILON) {epcompare = plane.dist - ON_EPSILON;goto cliptoseperatorsloop1;}
+ }
+ for (k=j+1;k<pass->numpoints;k++)
+ {
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON) goto cliptoseperatorscontinue;
+ else if (d > ON_EPSILON) {epcompare = plane.dist - ON_EPSILON;goto cliptoseperatorsloop2;}
+ }
+ continue;
+ for (;k<j;k++)
+ {
+ if (DotProduct(pass->points[k], plane.normal) < epcompare) goto cliptoseperatorscontinue;
+cliptoseperatorsloop1:
+ ;
+ }
+ for (k=j+1;k<pass->numpoints;k++)
+ {
+ if (DotProduct(pass->points[k], plane.normal) < epcompare) goto cliptoseperatorscontinue;
+cliptoseperatorsloop2:
+ ;
+ }
+ /*
+ // original code
+ counts[0] = counts[1] = counts[2] = 0;
+ for (k=0 ; k<pass->numpoints ; k++)
+ {
+ if (k==j)
+ continue;
+ d = DotProduct (pass->points[k], plane.normal) - plane.dist;
+ if (d < -ON_EPSILON)
+ break;
+ else if (d > ON_EPSILON)
+ counts[0]++;
+ else
+ counts[2]++;
+ }
+ if (k != pass->numpoints)
+ continue; // points on negative side, not a seperating plane
+
+ if (!counts[0])
+ {
+ continue; // planar with seperating plane
+ }
+ */
+
+ //
+ // flip the normal if we want the back side
+ //
+ if (flipclip)
+ {
+ VectorInverse(plane.normal);
+ plane.dist = -plane.dist;
+ }
+
+ //
+ // clip target by the seperating plane
+ //
+ target = ClipVisWinding (target, &plane, false);
+ if (!target)
+ return NULL; // target is not visible
+
+ if (rvis)
+ break;
+cliptoseperatorscontinue:
+ ;
+ }
+ }
+
+ 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;
+ portal_t *p;
+ plane_t backplane;
+ viswinding_t *source, *target;
+ leaf_t *leaf;
+ int i, j;
+ long *test, *might, *vis;
+ qboolean more;
+
+ c_chains++;
+
+ leaf = &leafs[leafnum];
+ CheckStack (leaf, thread);
+
+// mark the leaf as visible
+ if (! (thread->leafvis[leafnum>>3] & (1<<(leafnum&7)) ) )
+ {
+ thread->leafvis[leafnum>>3] |= 1<<(leafnum&7);
+ thread->base->numcansee++;
+ }
+
+ prevstack->next = &stack;
+ stack.next = NULL;
+ stack.leaf = leaf;
+ stack.portal = NULL;
+ stack.mightsee = qmalloc(bitbytes);
+ might = (long *)stack.mightsee;
+ vis = (long *)thread->leafvis;
+
+// check all portals for flowing into other leafs
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+
+ if ( ! (prevstack->mightsee[p->leaf>>3] & (1<<(p->leaf&7)) ) )
+ {
+ c_leafskip++;
+ 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)
+ {
+ c_vistest++;
+ test = (long *)p->visbits;
+ }
+ else
+ {
+ c_mighttest++;
+ test = (long *)p->mightsee;
+ }
+ more = false;
+ for (j=0 ; j<bitlongs ; j++)
+ {
+ might[j] = ((long *)prevstack->mightsee)[j] & test[j];
+ if (might[j] & ~vis[j])
+ more = true;
+ }
+
+ if (!more)
+ { // can't see anything new
+ c_portalskip++;
+ continue;
+ }
+
+// get plane of portal, point normal into the neighbor leaf
+ stack.portalplane = p->plane;
+ VectorNegate (p->plane.normal, backplane.normal);
+ backplane.dist = -p->plane.dist;
+
+ if (VectorCompare (prevstack->portalplane.normal, backplane.normal) )
+ continue; // can't go out a coplanar face
+
+ c_portalcheck++;
+
+ stack.portal = p;
+ stack.next = NULL;
+
+ target = ClipVisWinding (p->winding, &thread->pstack_head.portalplane, false);
+ if (!target)
+ continue;
+
+ if (!prevstack->pass)
+ { // the second leaf can only be blocked if coplanar
+
+ stack.source = prevstack->source;
+ stack.pass = target;
+ RecursiveLeafFlow (p->leaf, thread, &stack);
+ FreeVisWinding (target);
+ continue;
+ }
+
+ target = ClipVisWinding (target, &prevstack->portalplane, false);
+ if (!target)
+ continue;
+
+ source = CopyVisWinding (prevstack->source);
+
+ source = ClipVisWinding (source, &backplane, false);
+ if (!source)
+ {
+ FreeVisWinding (target);
+ continue;
+ }
+
+ c_portaltest++;
+
+ if (testlevel > 0)
+ {
+ target = ClipToSeperators (source, prevstack->pass, target, false);
+ if (!target)
+ {
+ FreeVisWinding (source);
+ continue;
+ }
+ }
+
+ if (testlevel > 1)
+ {
+ target = ClipToSeperators (prevstack->pass, source, target, true);
+ if (!target)
+ {
+ FreeVisWinding (source);
+ continue;
+ }
+ }
+
+ if (testlevel > 2)
+ {
+ source = ClipToSeperators (target, prevstack->pass, source, false);
+ if (!source)
+ {
+ FreeVisWinding (target);
+ continue;
+ }
+ }
+
+ if (testlevel > 3)
+ {
+ source = ClipToSeperators (prevstack->pass, target, source, true);
+ if (!source)
+ {
+ FreeVisWinding (target);
+ continue;
+ }
+ }
+
+ stack.source = source;
+ stack.pass = target;
+
+ c_portalpass++;
+
+ // flow through it for real
+ RecursiveLeafFlow (p->leaf, thread, &stack);
+
+ FreeVisWinding (source);
+ FreeVisWinding (target);
+ }
+
+ qfree (stack.mightsee);
+}
+
+
+/*
+===============
+PortalFlow
+
+===============
+*/
+void PortalFlow (portal_t *p)
+{
+ threaddata_t data;
+
+ if (p->status != stat_working)
+ Error ("PortalFlow: reflowed");
+ p->status = stat_working;
+
+ p->visbits = qmalloc (bitbytes);
+ memset (p->visbits, 0, bitbytes);
+
+ memset (&data, 0, sizeof(data));
+ data.leafvis = p->visbits;
+ data.base = p;
+
+ data.pstack_head.portal = p;
+ data.pstack_head.source = p->winding;
+ data.pstack_head.portalplane = p->plane;
+ data.pstack_head.mightsee = p->mightsee;
+
+ RecursiveLeafFlow (p->leaf, &data, &data.pstack_head);
+
+ p->status = stat_done;
+}
+
+
+/*
+===============================================================================
+
+This is a rough first-order aproximation that is used to trivially reject some
+of the final calculations.
+
+===============================================================================
+*/
+
+byte portalsee[MAX_PORTALS];
+int c_leafsee, c_portalsee;
+
+void SimpleFlood (portal_t *srcportal, int leafnum)
+{
+ int i;
+ leaf_t *leaf;
+ portal_t *p;
+
+ if (srcportal->mightsee[leafnum>>3] & (1<<(leafnum&7)) )
+ return;
+ srcportal->mightsee[leafnum>>3] |= (1<<(leafnum&7));
+ c_leafsee++;
+
+ leaf = &leafs[leafnum];
+
+ for (i=0 ; i<leaf->numportals ; i++)
+ {
+ p = leaf->portals[i];
+ if ( !portalsee[ p - portals ] )
+ continue;
+ SimpleFlood (srcportal, p->leaf);
+ }
+}
+
+
+/*
+==============
+BasePortalVis
+==============
+*/
+void BasePortalVis (void)
+{
+ int i, j, k;
+ portal_t *tp, *p;
+ plane_t *plane;
+ vec_t d;
+ viswinding_t *w;
+ time_t oldtime, newtime;
+
+ oldtime = time(NULL);
+ for (i=0, p = portals ; i<numportals*2 ; i++, p++)
+ {
+ p->mightsee = qmalloc (bitbytes);
+ memset (p->mightsee, 0, bitbytes);
+
+ c_portalsee = 0;
+ memset (portalsee, 0, numportals*2);
+
+ for (j=0, tp = portals ; j<numportals*2 ; j++, tp++)
+ {
+ if (j == i)
+ continue;
+ w = tp->winding;
+ plane = &p->plane;
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ if (plane->type < 3)
+ d = w->points[k][plane->type] - plane->dist;
+ else
+ d = DotProduct (w->points[k], plane->normal) - plane->dist;
+ if (d > ON_EPSILON)
+ break;
+ }
+ if (k == w->numpoints)
+ continue; // no points on front
+
+ w = p->winding;
+ plane = &tp->plane;
+ for (k=0 ; k<w->numpoints ; k++)
+ {
+ if (plane->type < 3)
+ d = w->points[k][plane->type] - plane->dist;
+ else
+ d = DotProduct (w->points[k], plane->normal) - plane->dist;
+ if (d < -ON_EPSILON)
+ break;
+ }
+ if (k == w->numpoints)
+ continue; // no points on front
+
+ portalsee[j] = 1;
+ c_portalsee++;
+
+ }
+
+ c_leafsee = 0;
+ SimpleFlood (p, p->leaf);
+ p->nummightsee = c_leafsee;
+// printf ("portal:%4i c_leafsee:%4i \n", i, c_leafsee);
+ newtime = time(NULL);
+ if (newtime != oldtime)
+ {
+ printf ("\rbasevis: portal%5i of%5i", i, numportals*2);
+ fflush(stdout);
+ newtime = oldtime;
+ }
+ }
+ printf ("\rbasevis done \n");
+ fflush(stdout);
+}
Index: hmap2/vis_sound.c
diff -u /dev/null hmap2/vis_sound.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/vis_sound.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,177 @@
+
+#include "vis.h"
+
+/*
+
+Some textures (sky, water, slime, lava) are considered ambient sound emiters.
+Find an aproximate distance to the nearest emiter of each class for each leaf.
+
+*/
+
+/*
+====================
+SurfaceBBox
+====================
+*/
+void SurfaceBBox (dface_t *s, vec3_t mins, vec3_t maxs)
+{
+ int i;
+ int e;
+ int vi;
+ vec3_t v;
+
+ ClearBounds( mins, maxs );
+
+ // Vic: old buggy code
+// mins[0] = mins[1] = BOGUS_RANGE;
+// maxs[0] = maxs[1] = -BOGUS_RANGE;
+
+ for (i=0 ; i<s->numedges ; i++)
+ {
+ e = dsurfedges[s->firstedge+i];
+ if (e >= 0)
+ vi = dedges[e].v[0];
+ else
+ vi = dedges[-e].v[1];
+
+ VectorCopy( dvertexes[vi].point, v );
+ AddPointToBounds( v, mins, maxs );
+ }
+}
+
+/*
+====================
+CalcAmbientSounds
+====================
+*/
+void CalcAmbientSounds (void)
+{
+ int i, j, k, l;
+ dleaf_t *leaf, *hit;
+ byte *vis;
+ dface_t *surf;
+ vec3_t mins, maxs;
+ vec_t d, maxd;
+ int ambient_type;
+ texinfo_t *info;
+ miptex_t *miptex;
+ int ofs;
+ vec_t dists[NUM_AMBIENTS];
+ vec_t vol;
+
+ printf ("---- ambient ----\n");
+
+ if (noambientslime)
+ printf ("defaulting slime to water\n");
+
+ for (i=0 ; i< portalleafs ; i++)
+ {
+ leaf = &dleafs[i+1];
+
+ //
+ // clear ambients
+ //
+ for (j=0 ; j<NUM_AMBIENTS ; j++)
+ dists[j] = BOGUS_RANGE;
+
+ vis = &uncompressed[i*bitbytes];
+
+ for (j=0 ; j< portalleafs ; j++)
+ {
+ if ( !(vis[j>>3] & (1<<(j&7))) )
+ continue;
+
+ //
+ // check this leaf for sound textures
+ //
+ hit = &dleafs[j+1];
+
+ for (k=0 ; k< hit->nummarksurfaces ; k++)
+ {
+ surf = &dfaces[dmarksurfaces[hit->firstmarksurface + k]];
+ info = &texinfo[surf->texinfo];
+ ofs = ((dmiptexlump_t *)dtexdata)->dataofs[info->miptex];
+ miptex = (miptex_t *)(&dtexdata[ofs]);
+
+ // LordHavoc: optimized this to reduce wasted Q_strncasecmp calls
+ if ((miptex->name[0] == 's' || miptex->name[0] == 'S') && (miptex->name[1] == 'k' || miptex->name[1] == 'K') && (miptex->name[2] == 'y' || miptex->name[2] == 'Y'))
+ {
+ if (noambientsky)
+ continue;
+ ambient_type = AMBIENT_SKY;
+ }
+ else if (miptex->name[0] == '*')
+ {
+ if ( !Q_strncasecmp (miptex->name, "*water", 6) )
+ {
+ if (noambientwater)
+ continue;
+ ambient_type = AMBIENT_WATER;
+ }
+ else if ( !Q_strncasecmp (miptex->name, "*slime", 6) )
+ {
+ if (noambientwater)
+ continue;
+ if (noambientslime)
+ ambient_type = AMBIENT_WATER;
+ else
+ ambient_type = AMBIENT_SLIME;
+ }
+ else if ( !Q_strncasecmp (miptex->name, "*lava", 6) )
+ {
+ if (noambientlava)
+ continue;
+ ambient_type = AMBIENT_LAVA;
+ }
+ else if ( !Q_strncasecmp (miptex->name, "*04water", 8) )
+ {
+ if (noambientwater)
+ continue;
+ ambient_type = AMBIENT_WATER;
+ }
+ else
+ continue;
+ }
+ else
+ continue;
+
+ // find distance from source leaf to polygon
+ SurfaceBBox (surf, mins, maxs);
+ maxd = 0;
+ for (l=0 ; l<3 ; l++)
+ {
+ if (mins[l] > leaf->maxs[l])
+ d = mins[l] - leaf->maxs[l];
+ else if (maxs[l] < leaf->mins[l])
+ d = leaf->mins[l] - mins[l];
+ else
+ d = 0;
+ if (d > maxd)
+ maxd = d;
+ }
+
+ // Vic: old buggy code
+// maxd = 0.25;
+ if (maxd < dists[ambient_type])
+ dists[ambient_type] = maxd;
+ }
+ }
+
+ for (j=0 ; j<NUM_AMBIENTS ; j++)
+ {
+ if (dists[j] < 100)
+ vol = 1.0;
+ else
+ {
+ // Vic: old buggy code
+// vol = 1.0 - dists[2]*0.002;
+ vol = 1.0 - dists[j]*0.002;
+ if (vol < 0)
+ vol = 0;
+ else if (vol > 1.0)
+ vol = 1.0; // Vic
+ }
+ leaf->ambient_level[j] = vol*255;
+ }
+ }
+}
Index: hmap2/wad.c
diff -u /dev/null hmap2/wad.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/wad.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,329 @@
+
+#include "bsp5.h"
+
+typedef struct
+{
+ char identification[4]; // "WAD2"
+ int numlumps;
+ int infotableofs;
+}
+wadinfo_t;
+
+typedef struct
+{
+ int filepos;
+ int disksize;
+ int size; // uncompressed
+ char type;
+ char compression;
+ char pad1, pad2;
+ char name[16]; // must be null terminated
+}
+lumpinfo_t;
+
+#define TYP_NONE 0
+#define TYP_LABEL 1
+
+#define TYP_LUMPY 64 // 64 + grab command number
+#define TYP_PALETTE 64
+#define TYP_QTEX 65
+#define TYP_QPIC 66
+#define TYP_SOUND 67
+#define TYP_MIPTEX 68
+
+typedef struct
+{
+ FILE *file;
+ int filepos;
+ int size;
+ char name[16];
+}
+miptexfile_t;
+
+#define MAX_MIPTEXFILES 32768
+
+miptexfile_t miptexfile[MAX_MIPTEXFILES];
+int nummiptexfiles = 0;
+
+void CleanupName (char *in, char *out)
+{
+ int i;
+
+ for (i = 0;i < 15 && in[i];i++)
+ out[i] = toupper(in[i]);
+
+ for (;i < 16;i++)
+ out[i] = 0;
+}
+
+int MipTexUsed(char *texname)
+{
+ int i;
+ char name[16];
+ CleanupName(texname, name);
+
+ for (i = 0;i < nummiptex;i++)
+ if (!strcmp(miptex[i], name))
+ return true;
+ return false;
+}
+
+miptexfile_t *FindMipTexFile(char *texname)
+{
+ int i;
+ char name[16];
+ CleanupName(texname, name);
+
+ for (i = 0;i < nummiptexfiles;i++)
+ if (!strcmp(miptexfile[i].name, name))
+ return miptexfile + i;
+ return NULL;
+}
+
+void AddAnimatingTextures (void)
+{
+ int base;
+ int i, j;
+ char name[32];
+
+ base = nummiptex;
+
+ for (i = 0;i < base;i++)
+ {
+ if (miptex[i][0] != '+')
+ continue;
+ CleanupName (miptex[i], name);
+
+ for (j = 0;j < 20;j++)
+ {
+ if (j < 10)
+ name[1] = j + '0';
+ else
+ name[1] = j + 'A' - 10; // alternate animation
+
+ if (!MipTexUsed(name) && FindMipTexFile(name))
+ FindMiptex (name);
+ }
+ }
+}
+
+void getwadname(char *out, char **inpointer)
+{
+ char *in;
+ in = *inpointer;
+ while (*in && (*in == ';' || *in <= ' '))
+ in++;
+ if (*in == '"')
+ {
+ // quoted name
+ in++;
+ while (*in && *in != '"')
+ *out++ = *in++;
+ if (*in == '"')
+ in++;
+ // FIXME: error if quoted string is not properly terminated?
+ }
+ else
+ {
+ // normal name
+ while (*in && *in != ';')
+ *out++ = *in++;
+ }
+ *out++ = 0;
+ *inpointer = in;
+}
+
+int loadwad(char *filename)
+{
+ int i, oldnummiptexfiles = nummiptexfiles;
+ wadinfo_t wadinfo;
+ lumpinfo_t lumpinfo;
+ FILE *file;
+ file = fopen(filename, "rb");
+ if (!file)
+ {
+ printf("unable to open wad \"%s\"\n", filename);
+ return -1;
+ }
+ if (fread(&wadinfo, sizeof(wadinfo_t), 1, file) < 1)
+ {
+ printf("error reading wad header in \"%s\"\n", filename);
+ fclose(file);
+ return -1;
+ }
+ // FIXME: support halflife WAD3?
+ if (memcmp(wadinfo.identification, "WAD2", 4))
+ {
+ printf("\"%s\" is not a quake WAD2 archive\n", filename);
+ fclose(file);
+ return -1;
+ }
+
+ wadinfo.numlumps = LittleLong(wadinfo.numlumps);
+ wadinfo.infotableofs = LittleLong(wadinfo.infotableofs);
+ if (fseek(file, wadinfo.infotableofs, SEEK_SET))
+ {
+ printf("error seeking in wad \"%s\"\n", filename);
+ fclose(file);
+ return -1;
+ }
+
+ for (i = 0;i < wadinfo.numlumps;i++)
+ {
+ if (fread(&lumpinfo, sizeof(lumpinfo_t), 1, file) < 1)
+ {
+ printf("error reading lump header in wad \"%s\"\n", filename);
+ fclose(file);
+ return -1;
+ }
+ lumpinfo.filepos = LittleLong(lumpinfo.filepos);
+ lumpinfo.disksize = LittleLong(lumpinfo.disksize);
+ lumpinfo.size = LittleLong(lumpinfo.size);
+ CleanupName(lumpinfo.name, lumpinfo.name);
+ // LordHavoc: some wad editors write 0 size
+ // disabled the size check because some wad editors write garbage for the lump size
+ if (lumpinfo.compression)// || (lumpinfo.size && lumpinfo.size != lumpinfo.disksize))
+ {
+ printf("lump \"%s\" in wad \"%s\" is compressed??\n", lumpinfo.name, filename);
+ continue;
+ }
+ if (lumpinfo.type == TYP_MIPTEX)
+ {
+ miptexfile[nummiptexfiles].file = file;
+ miptexfile[nummiptexfiles].filepos = lumpinfo.filepos;
+ miptexfile[nummiptexfiles].size = lumpinfo.disksize;
+ memcpy(miptexfile[nummiptexfiles].name, lumpinfo.name, 16);
+ nummiptexfiles++;
+ }
+ }
+ printf("loaded wad \"%s\" (%i miptex found)\n", filename, nummiptexfiles - oldnummiptexfiles);
+ return nummiptexfiles - oldnummiptexfiles;
+}
+
+int ReadMipTexFile(miptexfile_t *m, byte *out)
+{
+ if (fseek(m->file, m->filepos, SEEK_SET))
+ {
+ printf("error seeking to data for lump \"%s\"\n", m->name);
+ return true;
+ }
+ if (fread(out, m->size, 1, m->file) < 1)
+ {
+ printf("error reading data for lump \"%s\"\n", m->name);
+ return true;
+ }
+ return false;
+}
+
+static char wadname[2048], wadfilename[2048];
+void WriteMiptex (void)
+{
+ int i, success;
+ byte *miptex_data;
+ dmiptexlump_t *miptex_lumps;
+ char *path, *currentpath;
+ miptexfile_t *m;
+
+ path = ValueForKey (&entities[0], "_wad");
+ if (!path || !path[0])
+ {
+ path = ValueForKey (&entities[0], "wad");
+ if (!path || !path[0])
+ {
+ printf ("WriteMiptex: no wads specified in \"wad\" key in worldspawn\n");
+ texdatasize = 0;
+ return;
+ }
+ }
+
+ nummiptexfiles = 0;
+
+ currentpath = path;
+ while (*currentpath)
+ {
+ getwadname(wadname, ¤tpath);
+ if (wadname[0])
+ {
+ success = false;
+ // try prepending each -wadpath on the commandline to the wad name
+ for (i = 1;i < myargc;i++)
+ {
+ if (!Q_strcasecmp("-wadpath", myargv[i]))
+ {
+ i++;
+ if (i < myargc)
+ {
+ sprintf(wadfilename, "%s%s", myargv[i], wadname);
+ if ((success = loadwad(wadfilename) >= 0))
+ break;
+ }
+ }
+ }
+ if (!success)
+ {
+ // if the map name has a path, we can try loading the wad from there
+ ExtractFilePath(bspfilename, wadfilename);
+ if (wadfilename[0])
+ {
+ strcat(wadfilename, wadname);
+ if (!(success = loadwad(wadfilename) >= 0))
+ {
+ // try the parent directory
+ ExtractFilePath(bspfilename, wadfilename);
+ strcat(wadfilename, "../");
+ strcat(wadfilename, wadname);
+ success = loadwad(wadfilename) >= 0;
+ }
+ }
+ }
+ if (!success)
+ {
+ // try the wadname itself
+ success = loadwad(wadname) >= 0;
+ }
+ if (!success)
+ printf("Could not find wad \"%s\" using -wadpath options or in the same directory as the map or it's parent directory, so there!\n", wadname);
+ }
+ }
+
+ for (i = 0;i < nummiptex;i++)
+ CleanupName(miptex[i], miptex[i]);
+
+ AddAnimatingTextures();
+
+ miptex_lumps = (dmiptexlump_t *)dtexdata;
+ miptex_data = (byte *)&miptex_lumps->dataofs[nummiptex];
+ miptex_lumps->nummiptex = nummiptex;
+ for (i=0 ; i < nummiptex ; i++)
+ {
+ printf("miptex used: %s", miptex[i]);
+ m = FindMipTexFile(miptex[i]);
+ if (m)
+ {
+ if (miptex_data + m->size - dtexdata >= MAX_MAP_MIPTEX)
+ {
+ miptex_lumps->dataofs[i] = -1;
+ printf(" (MAX_MAP_MIPTEX exceeded)\n");
+ }
+ else if (ReadMipTexFile(m, miptex_data))
+ {
+ miptex_lumps->dataofs[i] = -1;
+ printf(" (READ ERROR)\n");
+ }
+ else
+ {
+ miptex_lumps->dataofs[i] = miptex_data - (byte *)miptex_lumps;
+ printf("\n");
+ miptex_data += m->size;
+ }
+ }
+ else
+ {
+ miptex_lumps->dataofs[i] = -1;
+ printf(" (NOT FOUND)\n");
+ }
+ }
+
+ texdatasize = miptex_data - dtexdata;
+}
+
Index: hmap2/winding.c
diff -u /dev/null hmap2/winding.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/winding.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,681 @@
+#include "cmdlib.h"
+#include "mathlib.h"
+#include "mem.h"
+#include "winding.h"
+
+#define WINDING_EPSILON 0.05
+
+//===========================================================================
+
+/*
+==================
+AllocWinding
+==================
+*/
+winding_t *AllocWinding( int points )
+{
+ winding_t *w;
+ size_t size;
+
+ if( points > MAX_POINTS_ON_WINDING )
+ Error( "AllocWinding: %i points", points );
+
+ size = ( size_t )((winding_t *)0)->points[points];
+ w = qmalloc( size );
+ memset( w, 0, size );
+
+ return w;
+}
+
+/*
+==================
+FreeWinding
+==================
+*/
+void FreeWinding( winding_t *w )
+{
+ qfree( w );
+}
+
+/*
+==================
+CopyWinding
+==================
+*/
+winding_t *CopyWinding( winding_t *w )
+{
+ winding_t *c;
+ size_t size;
+
+ size = ( size_t )((winding_t *)0)->points[w->numpoints];
+ c = qmalloc( size );
+ memcpy( c, w, size );
+
+ return c;
+}
+
+/*
+==================
+CopyWindingExt
+==================
+*/
+winding_t *CopyWindingExt( int numpoints, vec3_t *points )
+{
+ winding_t *c;
+ size_t size;
+
+ if( numpoints > MAX_POINTS_ON_WINDING )
+ Error( "CopyWinding: %i points", numpoints );
+
+ size = ( size_t )((winding_t *)0)->points[numpoints];
+ c = malloc( size );
+ c->numpoints = numpoints;
+ memcpy( c->points, points, sizeof(*points)*numpoints );
+
+ return c;
+}
+
+/*
+==================
+ReverseWinding
+==================
+*/
+winding_t *ReverseWinding( winding_t *w )
+{
+ int i;
+ winding_t *neww;
+
+ if( w->numpoints > MAX_POINTS_ON_WINDING )
+ Error( "ReverseWinding: %i points", w->numpoints );
+
+ neww = AllocWinding( w->numpoints );
+ neww->numpoints = w->numpoints;
+ for( i = 0; i < w->numpoints; i++ ) // add points backwards
+ VectorCopy( w->points[w->numpoints - 1 - i], neww->points[i] );
+
+#ifdef PARANOID
+ CheckWinding( neww );
+#endif
+
+ return neww;
+}
+
+//===========================================================================
+
+/*
+=================
+BaseWindingForPlane
+=================
+*/
+winding_t *BaseWindingForPlane( plane_t *p )
+{
+ int i, x;
+ vec_t max, v;
+ vec3_t org, vright, vup;
+ winding_t *w;
+
+ // find the major axis
+ max = -BOGUS_RANGE;
+ x = -1;
+ for( i = 0; i < 3; i++ ) {
+ v = fabs( p->normal[i] );
+ if( v > max ) {
+ x = i;
+ max = v;
+ }
+ }
+
+ if( x == -1 )
+ Error( "BaseWindingForPlane: no axis found" );
+
+ VectorClear( vup );
+ switch( x )
+ {
+ case 0:
+ case 1:
+ vup[2] = 1;
+ break;
+ default:
+ vup[0] = 1;
+ break;
+ }
+
+ v = DotProduct( vup, p->normal );
+ VectorMA( vup, -v, p->normal, vup );
+ VectorNormalize( vup );
+
+ VectorScale( p->normal, p->dist, org );
+
+ CrossProduct( vup, p->normal, vright );
+
+ VectorScale( vup, BOGUS_RANGE, vup );
+ VectorScale( vright, BOGUS_RANGE, vright );
+
+ // project a really big axis aligned box onto the plane
+ w = AllocWinding( 4 );
+
+ VectorSubtract( org, vright, w->points[0] );
+ VectorAdd( w->points[0], vup, w->points[0] );
+
+ VectorAdd( org, vright, w->points[1] );
+ VectorAdd( w->points[1], vup, w->points[1] );
+
+ VectorAdd( org, vright, w->points[2] );
+ VectorSubtract( w->points[2], vup, w->points[2] );
+
+ VectorSubtract( org, vright, w->points[3] );
+ VectorSubtract( w->points[3], vup, w->points[3] );
+
+ w->numpoints = 4;
+
+ return w;
+}
+
+
+/*
+==================
+CheckWinding
+
+Check for possible errors
+==================
+*/
+void CheckWinding( winding_t *w )
+{
+ int i, j;
+ vec_t *p1, *p2;
+ vec_t d, edgedist;
+ vec3_t dir, edgenormal;
+ plane_t facenormal;
+
+ PlaneFromWinding ( w, &facenormal );
+
+ for( i = 0; i < w->numpoints; i++ ) {
+ p1 = w->points[i];
+ p2 = w->points[(i + 1) % w->numpoints];
+
+ for( j = 0; j < 3; j++ ) {
+ if( p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE )
+ Error( "CheckWinding: BOGUS_RANGE: %f %f %f", p1[0], p1[1], p1[2] );
+ }
+
+ // check the point is on the face plane
+ d = DotProduct( p1, facenormal.normal ) - facenormal.dist;
+ if( d < -WINDING_EPSILON || d > WINDING_EPSILON )
+ printf( "CheckWinding: point off plane at %f %f %f, correcting", p1[0], p1[1], p1[2] );
+
+ // check the edge isn't degenerate
+ VectorSubtract( p2, p1, dir );
+
+ if( VectorLength( dir ) < WINDING_EPSILON )
+ Error( "CheckWinding: degenerate edge at %f %f %f", p2[0], p2[1], p2[2] );
+
+ CrossProduct( facenormal.normal, dir, edgenormal );
+ VectorNormalize( edgenormal );
+ edgedist = DotProduct( p1, edgenormal );
+ edgedist += WINDING_EPSILON;
+
+ // all other points must be on front side
+ for( j = 0; j < w->numpoints; j++ ) {
+ if( j == i )
+ continue;
+
+ d = DotProduct( w->points[j], edgenormal );
+ if( d > edgedist )
+ Error( "CheckWinding: non-convex at %f %f %f", w->points[j][0], w->points[j][1], w->points[j][2] );
+ }
+ }
+}
+
+/*
+==================
+WindingIsTiny
+==================
+*/
+qboolean WindingIsTiny( winding_t *w )
+{
+ int i, cnt;
+ vec3_t edge;
+ vec_t len;
+
+#ifdef PARANOID
+ if( !w || !w->numpoints )
+ return true;
+#endif
+
+ cnt = 0;
+ for( i = 0; i < w->numpoints; i++ ) {
+ VectorSubtract( w->points[i], w->points[(i+1)%w->numpoints], edge );
+ len = VectorLength( edge );
+ if( len > 0.3 ) {
+ if( ++cnt == 3 )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+=================
+WindingArea
+=================
+*/
+vec_t WindingArea( winding_t *w )
+{
+ int i;
+ vec_t total;
+ vec3_t v1, v2, cross;
+
+ total = 0;
+ for( i = 2; i < w->numpoints; i++ ) {
+ VectorSubtract( w->points[i-1], w->points[0], v1 );
+ VectorSubtract( w->points[i ], w->points[0], v2 );
+ CrossProduct( v1, v2, cross );
+ total += VectorLength( cross ) * 0.5;
+ }
+
+ return total;
+}
+
+/*
+=================
+WindingCentre
+=================
+*/
+void WindingCentre( winding_t *w, vec3_t centre, vec_t *radius )
+{
+ int i;
+ vec3_t edge;
+ vec_t dist, best;
+
+ if( !w->numpoints )
+ Error( "WindingCentre: no points" );
+
+ VectorCopy( w->points[0], centre );
+ for( i = 1; i < w->numpoints; i++ )
+ VectorAdd( centre, w->points[i], centre );
+ VectorScale( centre, (vec_t)1.0/w->numpoints, centre );
+
+ if( radius ) {
+ best = 0;
+ for( i = 0; i < w->numpoints; i++ ) {
+ VectorSubtract( centre, w->points[i], edge );
+ dist = VectorLength( edge );
+ if( dist > best )
+ best = dist;
+ }
+
+ #ifdef PARANOID
+ if( best == 0 )
+ Error( "WindingCentre: winding has zero radius" );
+ #endif
+
+ *radius = best;
+ }
+}
+
+/*
+=================
+PlaneFromWinding
+=================
+*/
+void PlaneFromWinding( winding_t *w, plane_t *plane )
+{
+ vec3_t v1, v2;
+
+ if( w->numpoints < 3 )
+ Error( "PlaneFromWinding: %i points at %f %f %f", w->numpoints, w->points[0], w->points[1], w->points[2] );
+
+ // 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->dist = DotProduct( w->points[0], plane->normal );
+}
+
+/*
+==================
+WindingOnPlaneSide
+==================
+*/
+int WindingOnPlaneSide( winding_t *w, plane_t *plane )
+{
+ int i;
+ vec_t dist;
+ qboolean back, front;
+
+ back = front = false;
+ for( i = 0; i < w->numpoints; i++ ) {
+ dist = DotProduct( w->points[i], plane->normal ) - plane->dist;
+
+ if( dist < -WINDING_EPSILON ) {
+ if( front )
+ return SIDE_CROSS;
+ back = true;
+ } else if( dist > WINDING_EPSILON ) {
+ if( back )
+ return SIDE_CROSS;
+ front = true;
+ }
+ }
+
+ if( back )
+ return SIDE_BACK;
+ if ( front )
+ return SIDE_FRONT;
+ return SIDE_ON;
+}
+
+/*
+==================
+ClipWindingEpsilon
+==================
+*/
+winding_t *ClipWindingEpsilon( winding_t *in, plane_t *split, vec_t epsilon, qboolean keepon )
+{
+ int i, j;
+ vec_t dists[MAX_POINTS_ON_WINDING + 1];
+ int sides[MAX_POINTS_ON_WINDING + 1];
+ int counts[3];
+ vec_t dot;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *neww;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+ // determine sides for each point
+ for( i = 0; i < in->numpoints; i++ )
+ {
+ dists[i] = dot = DotProduct( in->points[i], split->normal ) - split->dist;
+ if( dot > epsilon )
+ sides[i] = SIDE_FRONT;
+ else if( dot < -epsilon )
+ sides[i] = SIDE_BACK;
+ else
+ sides[i] = SIDE_ON;
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if( keepon && !counts[SIDE_FRONT] && !counts[SIDE_BACK] )
+ return in;
+
+ if( !counts[SIDE_FRONT] ) {
+ FreeWinding( in );
+ return NULL;
+ }
+ if( !counts[SIDE_BACK] )
+ return in;
+
+ maxpts = in->numpoints + 4; // can't use counts[0]+2 because of fp grouping errors
+ neww = AllocWinding( maxpts );
+
+ for( i = 0; i < in->numpoints; i++ ) {
+ p1 = in->points[i];
+
+ 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;
+
+ // 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++;
+ }
+
+ if( neww->numpoints > maxpts )
+ Error( "ClipWinding: points exceeded estimate" );
+
+ // free the original winding
+ FreeWinding( in );
+
+ return neww;
+}
+
+/*
+==================
+ClipWinding
+==================
+*/
+winding_t *ClipWinding( winding_t *in, plane_t *split, qboolean keepon ) {
+ return ClipWindingEpsilon( in, split, WINDING_EPSILON, keepon );
+}
+
+/*
+==================
+DivideWindingEpsilon
+
+Divides a winding by a plane, producing one or two windings. The
+original winding is not damaged or freed. If only on one side, the
+returned winding will be the input winding. If on both sides, two
+new windings will be created.
+==================
+*/
+void DivideWindingEpsilon( winding_t *in, plane_t *split, winding_t **front, winding_t **back, vec_t epsilon )
+{
+ int i, j;
+ vec_t dists[MAX_POINTS_ON_WINDING + 1];
+ int sides[MAX_POINTS_ON_WINDING + 1];
+ int counts[3];
+ vec_t dot;
+ vec_t *p1, *p2;
+ vec3_t mid;
+ winding_t *f, *b;
+ int maxpts;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+ // determine sides for each point
+ for( i = 0; i < in->numpoints; i++ ) {
+ dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
+ if( dot > epsilon )
+ sides[i] = SIDE_FRONT;
+ else if( dot < -epsilon )
+ sides[i] = SIDE_BACK;
+ else
+ sides[i] = SIDE_ON;
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ *front = *back = NULL;
+
+ if( !counts[SIDE_FRONT] ) {
+ *back = in;
+ return;
+ }
+ if( !counts[SIDE_BACK] ) {
+ *front = in;
+ return;
+ }
+
+ maxpts = in->numpoints + 4; // can't use counts[0]+2 because of fp grouping errors
+
+ *front = f = AllocWinding( maxpts );
+ *back = b = AllocWinding( maxpts );
+
+ for( i = 0; i < in->numpoints; i++ ) {
+ p1 = in->points[i];
+
+ if( sides[i] == SIDE_ON ) {
+ VectorCopy( p1, f->points[f->numpoints] );
+ f->numpoints++;
+ VectorCopy( p1, b->points[b->numpoints] );
+ b->numpoints++;
+ continue;
+ }
+
+ if( sides[i] == SIDE_FRONT ) {
+ VectorCopy( p1, f->points[f->numpoints] );
+ f->numpoints++;
+ } else if( sides[i] == SIDE_BACK ) {
+ VectorCopy( p1, b->points[b->numpoints] );
+ b->numpoints++;
+ }
+
+ if( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] )
+ continue;
+
+ // 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, f->points[f->numpoints] );
+ f->numpoints++;
+ VectorCopy( mid, b->points[b->numpoints] );
+ b->numpoints++;
+ }
+
+ if( f->numpoints > maxpts || b->numpoints > maxpts )
+ Error ("ClipWinding: points exceeded estimate");
+}
+
+/*
+==================
+DivideWinding
+==================
+*/
+void DivideWinding( winding_t *in, plane_t *split, winding_t **front, winding_t **back ) {
+ DivideWindingEpsilon( in, split, front, back, WINDING_EPSILON );
+}
+
+/*
+=============
+TryMergeWinding
+
+If two polygons share a common edge and the edges that meet at the
+common points are both inside the other polygons, merge them
+
+Returns NULL if the windings couldn't be merged, or the new winding.
+The originals will NOT be freed.
+=============
+*/
+#define CONTINUOUS_EPSILON 0.001
+
+winding_t *TryMergeWinding( winding_t *w1, winding_t *w2, vec3_t planenormal )
+{
+ int i, j = 0, k, l;
+ vec_t *p1 = NULL, *p2 = NULL, *p3, *p4, *back;
+ winding_t *neww;
+ vec3_t normal, delta;
+ vec_t dot;
+ qboolean keep1, keep2;
+
+ //
+ // find a common edge
+ //
+ for( i = 0; i < w1->numpoints; i++ ) {
+ p1 = w1->points[i];
+ p2 = w1->points[(i + 1) % w1->numpoints];
+
+ for( j = 0; j < w2->numpoints; j++ ) {
+ p3 = w2->points[j];
+ p4 = w2->points[(j + 1) % w2->numpoints];
+
+ for( k = 0; k < 3; k++ ) {
+ if( fabs( p1[k] - p4[k] ) > EQUAL_EPSILON ||
+ fabs( p2[k] - p3[k] ) > EQUAL_EPSILON )
+ break;
+ }
+
+ if( k == 3 )
+ break;
+ }
+
+ if( j < w2->numpoints )
+ break;
+ }
+
+ if( i == w1->numpoints )
+ return NULL; // no matching edges
+
+ //
+ // check slope of connected lines
+ // if the slopes are colinear, the point can be removed
+ //
+ back = w1->points[(i + w1->numpoints - 1) % w1->numpoints];
+ VectorSubtract( p1, back, delta );
+ CrossProduct( planenormal, delta, normal );
+ VectorNormalize( normal );
+
+ back = w2->points[(j + 2) % w2->numpoints];
+ VectorSubtract( back, p1, delta );
+ dot = DotProduct( delta, normal );
+ if( dot > CONTINUOUS_EPSILON )
+ return NULL; // not a convex polygon
+ keep1 = dot < -CONTINUOUS_EPSILON;
+
+ back = w1->points[(i + 2) % w1->numpoints];
+ VectorSubtract( back, p2, delta );
+ CrossProduct( planenormal, delta, normal );
+ VectorNormalize( normal );
+
+ back = w2->points[(j + w2->numpoints - 1) % w2->numpoints];
+ VectorSubtract( back, p2, delta );
+ dot = DotProduct( delta, normal );
+ if( dot > CONTINUOUS_EPSILON )
+ return NULL; // not a convex polygon
+ keep2 = dot < -CONTINUOUS_EPSILON;
+
+ //
+ // build the new polygon
+ //
+ neww = AllocWinding( w1->numpoints + w2->numpoints - 4 + keep1 + keep2 );
+
+ // copy first polygon
+ for( k = (i + 1) % w1->numpoints; k != i; k = (k + 1)%w1->numpoints ) {
+ if( k == (i + 1) % w1->numpoints && !keep2 )
+ continue;
+
+ VectorCopy( w1->points[k], neww->points[neww->numpoints] );
+ neww->numpoints++;
+ }
+
+ // copy second polygon
+ for( l = (j + 1) % w2->numpoints; l != j; l = (l + 1) % w2->numpoints ) {
+ if( l == (j + 1) % w2->numpoints && !keep1 )
+ continue;
+
+ VectorCopy( w2->points[l], neww->points[neww->numpoints] );
+ neww->numpoints++;
+ }
+
+ return neww;
+}
Index: hmap2/winding.h
diff -u /dev/null hmap2/winding.h:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/winding.h Sun Feb 15 21:50:37 2004
@@ -0,0 +1,30 @@
+#ifndef __WINDING_H__
+#define __WINDING_H__
+
+typedef struct
+{
+ int numpoints;
+ vec3_t points[8]; // variable sized
+} winding_t;
+
+#define MAX_POINTS_ON_WINDING 64
+
+winding_t *AllocWinding (int points);
+void FreeWinding (winding_t *w);
+winding_t *CopyWinding (winding_t *w);
+winding_t *CopyWindingExt( int numpoints, vec3_t *points );
+winding_t *ReverseWinding( winding_t *w );
+winding_t *BaseWindingForPlane( plane_t *p );
+void CheckWinding( winding_t *w );
+qboolean WindingIsTiny( winding_t *w );
+vec_t WindingArea( winding_t *w );
+void WindingCentre( winding_t *w, vec3_t centre, vec_t *radius );
+void PlaneFromWinding( winding_t *w, plane_t *plane );
+int WindingOnPlaneSide( winding_t *w, plane_t *plane );
+winding_t *ClipWinding( winding_t *in, plane_t *split, qboolean keepon );
+winding_t *ClipWindingEpsilon( winding_t *in, plane_t *split, vec_t epsilon, qboolean keepon );
+void DivideWinding( winding_t *in, plane_t *split, winding_t **front, winding_t **back );
+void DivideWindingEpsilon( winding_t *in, plane_t *split, winding_t **front, winding_t **back, vec_t epsilon );
+winding_t *TryMergeWinding( winding_t *w1, winding_t *w2, vec3_t planenormal );
+
+#endif //__WINDING_H__
Index: hmap2/writebsp.c
diff -u /dev/null hmap2/writebsp.c:1.1
--- /dev/null Sun Feb 15 21:50:47 2004
+++ hmap2/writebsp.c Sun Feb 15 21:50:37 2004
@@ -0,0 +1,308 @@
+
+#include "bsp5.h"
+
+int firstface;
+int *planemapping;
+
+//===========================================================================
+
+/*
+==================
+FindFinalPlane
+
+Used to find plane index numbers for clip nodes read from child processes
+==================
+*/
+int FindFinalPlane( vec3_t normal, vec_t dist, int type )
+{
+ int i;
+ dplane_t *dplane;
+
+ for( i = 0, dplane = dplanes; i < numplanes; i++, dplane++ ) {
+ if( dplane->type != type
+ || dplane->dist != dist
+ || dplane->normal[0] != normal[0]
+ || dplane->normal[1] != normal[1]
+ || dplane->normal[2] != normal[2])
+ continue;
+ return i;
+ }
+
+ // new plane
+ if( numplanes == MAX_MAP_PLANES )
+ Error( "numplanes == MAX_MAP_PLANES" );
+
+ dplane->type = type;
+ dplane->dist = dist;
+ VectorCopy( normal, dplane->normal );
+
+ return numplanes++;
+}
+
+/*
+==================
+EmitNodePlanes
+==================
+*/
+static void EmitNodePlanes_r( node_t *node )
+{
+ if( node->planenum == PLANENUM_LEAF )
+ return;
+
+ if( planemapping[node->planenum] == -1 ) { // a new plane
+ plane_t *plane;
+
+ plane = &mapplanes[node->planenum];
+ planemapping[node->planenum] = FindFinalPlane( plane->normal, plane->dist, plane->type );
+ }
+
+ node->outputplanenum = planemapping[node->planenum];
+
+ EmitNodePlanes_r( node->children[0] );
+ EmitNodePlanes_r( node->children[1] );
+}
+
+/*
+==================
+EmitNodePlanes
+==================
+*/
+void EmitNodePlanes( node_t *nodes )
+{
+// qprintf( "--- EmitNodePlanes ---\n" );
+
+ EmitNodePlanes_r( nodes );
+}
+
+//===========================================================================
+
+/*
+==================
+EmitClipNodes_r
+==================
+*/
+static int EmitClipNodes_r( node_t *node )
+{
+ int i, c;
+ dclipnode_t *cn;
+ int num;
+
+ // FIXME: free more stuff?
+ if( node->planenum == PLANENUM_LEAF ) {
+ num = node->contents;
+ FreeNode( node );
+ return num;
+ }
+
+ c = numclipnodes;
+ cn = &dclipnodes[numclipnodes++]; // emit a clipnode
+ cn->planenum = node->outputplanenum;
+ for( i = 0; i < 2; i++ )
+ cn->children[i] = EmitClipNodes_r( node->children[i] );
+
+ FreeNode( node );
+
+ return c;
+}
+
+/*
+==================
+EmitClipNodes
+
+Called after the clipping hull is completed. Generates a disk format
+representation and frees the original memory.
+==================
+*/
+void EmitClipNodes( node_t *nodes, int modnum, int hullnum )
+{
+// qprintf( "--- EmitClipNodes ---\n" );
+
+ dmodels[modnum].headnode[hullnum] = numclipnodes;
+ EmitClipNodes_r( nodes );
+}
+
+//===========================================================================
+
+/*
+==================
+EmitVertex
+==================
+*/
+void EmitVertex( vec3_t point )
+{
+ dvertex_t *vert;
+
+ if( numvertexes == MAX_MAP_VERTS )
+ Error( "numvertexes == MAX_MAP_VERTS" );
+
+ vert = &dvertexes[numvertexes++]; // emit a vertex
+ VectorCopy( point, vert->point );
+}
+
+/*
+==================
+EmitEdge
+==================
+*/
+void EmitEdge( int v1, int v2 )
+{
+ dedge_t *edge;
+
+ if( numedges == MAX_MAP_EDGES )
+ Error( "numedges == MAX_MAP_EDGES" );
+
+ edge = &dedges[numedges++]; // emit an edge
+ edge->v[0] = v1;
+ edge->v[1] = v2;
+}
+
+//===========================================================================
+
+/*
+==================
+EmitLeaf
+==================
+*/
+static void EmitLeaf( node_t *node )
+{
+ face_t **fp, *f;
+ dleaf_t *leaf_p;
+
+ if( numleafs == MAX_MAP_LEAFS )
+ Error( "numleafs == MAX_MAP_LEAFS" );
+
+ leaf_p = &dleafs[numleafs++]; // emit a leaf
+ leaf_p->contents = node->contents;
+ leaf_p->visofs = -1; // no vis info yet
+
+ // write bounding box info
+ VectorCopy( node->mins, leaf_p->mins );
+ VectorCopy( node->maxs, leaf_p->maxs );
+
+ // write the marksurfaces
+ leaf_p->firstmarksurface = nummarksurfaces;
+
+ for( fp = node->markfaces; *fp ; fp++ ) {
+ for( f = *fp; f; f = f->original ) { // grab tjunction split faces
+ if( nummarksurfaces == MAX_MAP_MARKSURFACES )
+ Error( "nummarksurfaces == MAX_MAP_MARKSURFACES" );
+ if( f->outputnumber == -1 )
+ Error( "f->outputnumber == -1" );
+ dmarksurfaces[nummarksurfaces++] = f->outputnumber; // emit a marksurface
+ }
+ }
+
+ leaf_p->nummarksurfaces = nummarksurfaces - leaf_p->firstmarksurface;
+}
+
+/*
+==================
+EmitDrawNodes_r
+==================
+*/
+void EmitDrawNodes_r( node_t *node )
+{
+ int i;
+ dnode_t *n;
+
+ if( numnodes == MAX_MAP_NODES )
+ Error( "numnodes == MAX_MAP_NODES" );
+
+ n = &dnodes[numnodes++]; // emit a node
+ n->planenum = node->outputplanenum;
+ n->firstface = node->firstface;
+ n->numfaces = node->numfaces;
+ VectorCopy( node->mins, n->mins );
+ VectorCopy( node->maxs, n->maxs );
+
+ // recursively output the other nodes
+ for( i = 0; i < 2; i++ ) {
+ if( node->children[i]->planenum == PLANENUM_LEAF ) {
+ n->children[i] = -1;
+
+ if( node->children[i]->contents != CONTENTS_SOLID ) {
+ n->children[i] = -(numleafs + 1);
+ EmitLeaf( node->children[i] );
+ }
+ } else {
+ n->children[i] = numnodes;
+ EmitDrawNodes_r( node->children[i] );
+ }
+ }
+}
+
+/*
+==================
+EmitDrawNodes
+==================
+*/
+void EmitDrawNodes( node_t *headnode )
+{
+ int i;
+ dmodel_t *bm;
+ int firstleaf;
+
+// qprintf( "--- EmitDrawNodes ---\n" );
+
+ if( nummodels == MAX_MAP_MODELS )
+ Error( "nummodels == MAX_MAP_MODELS" );
+
+ bm = &dmodels[nummodels++]; // emit a model
+ bm->headnode[0] = numnodes;
+ bm->firstface = firstface;
+ bm->numfaces = numfaces - firstface;
+ firstface = numfaces;
+
+ firstleaf = numleafs;
+ if( headnode->contents < 0 )
+ EmitLeaf( headnode );
+ else
+ EmitDrawNodes_r( headnode );
+ bm->visleafs = numleafs - firstleaf;
+
+ for( i = 0; i < 3; i++ ) {
+ bm->mins[i] = headnode->mins[i] + SIDESPACE + 1; // remove the padding
+ bm->maxs[i] = headnode->maxs[i] - SIDESPACE - 1;
+ }
+}
+
+//=============================================================================
+
+/*
+==================
+BeginBSPFile
+==================
+*/
+void BeginBSPFile( void )
+{
+ qprintf( "--- BeginBSPFile ---\n" );
+
+ planemapping = qmalloc( sizeof( *planemapping ) * MAX_MAP_PLANES );
+ memset( planemapping, -1, sizeof( *planemapping ) * MAX_MAP_PLANES );
+
+ // edge 0 is not used, because 0 can't be negated
+ numedges = 1;
+
+ // leaf 0 is common solid with no faces
+ numleafs = 1;
+ dleafs[0].contents = CONTENTS_SOLID;
+ dleafs[0].visofs = -1; // thanks to Vic for suggesting this line
+
+ firstface = 0;
+}
+
+/*
+==================
+FinishBSPFile
+==================
+*/
+void FinishBSPFile( void )
+{
+ printf( "--- BSP file information ---\n" );
+ PrintBSPFileSizes ();
+
+ qprintf( "--- WriteBSPFile: %s ---\n", bspfilename );
+ WriteBSPFile( bspfilename, false );
+
+ qfree( planemapping );
+}
More information about the twilight-commits
mailing list