mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-03-22 02:11:19 +00:00
Whee, now we have qfbsp. It compiles, but won't work as it hasn't been
`ported' to the new bspfile stuff when it comes to creating/editing a bsp.
This commit is contained in:
parent
e8d74f3a26
commit
afa0b21336
23 changed files with 7714 additions and 2 deletions
14
configure.ac
14
configure.ac
|
@ -1477,7 +1477,7 @@ QF_WITH_TARGETS(
|
|||
QF_WITH_TARGETS(
|
||||
tools,
|
||||
[ --with-tools=<list> compile qf tools:],
|
||||
[pak,qfcc,qfdefs,qflight,qfmodelgen,qfprogs,qfvis,qwaq,wav],dummy
|
||||
[pak,qfbsp,qfcc,qfdefs,qflight,qfmodelgen,qfprogs,qfvis,qwaq,wav],dummy
|
||||
)
|
||||
|
||||
unset CL_TARGETS
|
||||
|
@ -1652,6 +1652,9 @@ unset TOOLS_TARGETS
|
|||
if test "x$ENABLE_tools_pak" = xyes; then
|
||||
TOOLS_TARGETS="$TOOLS_TARGETS pak"
|
||||
fi
|
||||
if test "x$ENABLE_tools_qfbsp" = xyes; then
|
||||
TOOLS_TARGETS="$TOOLS_TARGETS qfbsp"
|
||||
fi
|
||||
if test "x$ENABLE_tools_qfcc" = xyes; then
|
||||
TOOLS_TARGETS="$TOOLS_TARGETS qfcc"
|
||||
fi
|
||||
|
@ -1677,6 +1680,7 @@ if test "x$ENABLE_tools_wav" = xyes; then
|
|||
TOOLS_TARGETS="$TOOLS_TARGETS wav"
|
||||
fi
|
||||
AM_CONDITIONAL(BUILD_PAK, test "$ENABLE_tools_pak" = "yes")
|
||||
AM_CONDITIONAL(BUILD_QFBSP, test "$ENABLE_tools_qfbsp" = "yes")
|
||||
AM_CONDITIONAL(BUILD_QFCC, test "$ENABLE_tools_qfcc" = "yes")
|
||||
AM_CONDITIONAL(BUILD_QFDEFS, test "$ENABLE_tools_qfdefs" = "yes")
|
||||
AM_CONDITIONAL(BUILD_QFLIGHT, test "$ENABLE_tools_qflight" = "yes")
|
||||
|
@ -1888,6 +1892,11 @@ AC_SUBST(VID_TARGETS)
|
|||
AC_SUBST(plugin_ldflags)
|
||||
AC_SUBST(plugin_libadd)
|
||||
|
||||
QF_DEPS(QFBSP,
|
||||
[-I$(top_srcdir)/tools/qfbsp/include],
|
||||
[$(top_builddir)/libs/util/libQFutil.la],
|
||||
[$(WIN32_LIBS)],
|
||||
)
|
||||
QF_DEPS(QFCC,
|
||||
[-I$(top_srcdir)/tools/qfcc/include],
|
||||
[$(top_builddir)/libs/gamecode/engine/libQFgamecode.la $(top_builddir)/libs/util/libQFutil.la],
|
||||
|
@ -1995,6 +2004,9 @@ AC_OUTPUT(
|
|||
|
||||
tools/Makefile
|
||||
tools/pak/Makefile
|
||||
tools/qfbsp/Makefile
|
||||
tools/qfbsp/include/Makefile
|
||||
tools/qfbsp/source/Makefile
|
||||
tools/qfcc/Makefile
|
||||
tools/qfcc/doc/Makefile
|
||||
tools/qfcc/doc/man/Makefile
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
SUBDIRS=pak qfcc qfdefs qflight qfmodelgen qfprogs qfvis qwaq wav
|
||||
SUBDIRS=pak qfbsp qfcc qfdefs qflight qfmodelgen qfprogs qfvis qwaq wav
|
||||
bin_SCRIPTS=zpak
|
||||
|
|
4
tools/qfbsp/.gitignore
vendored
Normal file
4
tools/qfbsp/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
.vimrc
|
||||
.deps
|
||||
Makefile.in
|
||||
Makefile
|
3
tools/qfbsp/Makefile.am
Normal file
3
tools/qfbsp/Makefile.am
Normal file
|
@ -0,0 +1,3 @@
|
|||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
SUBDIRS= include source
|
3
tools/qfbsp/include/.gitignore
vendored
Normal file
3
tools/qfbsp/include/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
.vimrc
|
||||
Makefile.in
|
||||
Makefile
|
3
tools/qfbsp/include/Makefile.am
Normal file
3
tools/qfbsp/include/Makefile.am
Normal file
|
@ -0,0 +1,3 @@
|
|||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
EXTRA_DIST= bsp5.h map.h
|
318
tools/qfbsp/include/bsp5.h
Normal file
318
tools/qfbsp/include/bsp5.h
Normal file
|
@ -0,0 +1,318 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
|
||||
// bsp5.h
|
||||
|
||||
#include "QF/mathlib.h"
|
||||
#include "QF/bspfile.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t normal;
|
||||
vec_t dist;
|
||||
int type;
|
||||
} plane_t;
|
||||
|
||||
|
||||
#include "map.h"
|
||||
|
||||
#define MAX_THREADS 4
|
||||
|
||||
#define ON_EPSILON 0.05
|
||||
#define BOGUS_RANGE 18000
|
||||
|
||||
// the exact bounding box of the brushes is expanded some for the headnode
|
||||
// volume. is this still needed?
|
||||
#define SIDESPACE 24
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int numpoints;
|
||||
vec3_t points[8]; // variable sized
|
||||
} winding_t;
|
||||
|
||||
#define MAX_POINTS_ON_WINDING 64
|
||||
|
||||
winding_t *BaseWindingForPlane (plane_t *p);
|
||||
void CheckWinding (winding_t *w);
|
||||
winding_t *NewWinding (int points);
|
||||
void FreeWinding (winding_t *w);
|
||||
winding_t *CopyWinding (winding_t *w);
|
||||
winding_t *ClipWinding (winding_t *in, plane_t *split, qboolean keepon);
|
||||
void DivideWinding (winding_t *in, plane_t *split, winding_t **front, winding_t **back);
|
||||
|
||||
//============================================================================
|
||||
|
||||
#define MAXEDGES 32
|
||||
#define MAXPOINTS 28 // don't let a base face get past this
|
||||
// because it can be split more later
|
||||
|
||||
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
|
||||
int numpoints;
|
||||
vec3_t pts[MAXEDGES]; // FIXME: change to use winding_t
|
||||
int edges[MAXEDGES];
|
||||
} face_t;
|
||||
|
||||
|
||||
typedef struct surface_s
|
||||
{
|
||||
struct surface_s *next;
|
||||
struct surface_s *original; // before BSP cuts it up
|
||||
int planenum;
|
||||
int outputplanenum; // only valid after WriteSurfacePlanes
|
||||
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;
|
||||
|
||||
|
||||
//
|
||||
// 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
|
||||
|
||||
// information for decision nodes
|
||||
int planenum; // -1 = leaf node
|
||||
int outputplanenum; // only valid after WriteNodePlanes
|
||||
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;
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// brush.c
|
||||
|
||||
#define NUM_HULLS 2 // normal and +16
|
||||
|
||||
#define NUM_CONTENTS 2 // solid and water
|
||||
|
||||
typedef struct brush_s
|
||||
{
|
||||
struct brush_s *next;
|
||||
vec3_t mins, maxs;
|
||||
face_t *faces;
|
||||
int contents;
|
||||
} brush_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t mins, maxs;
|
||||
brush_t *brushes; // NULL terminated list
|
||||
} brushset_t;
|
||||
|
||||
extern int numbrushplanes;
|
||||
extern plane_t planes[MAX_MAP_PLANES];
|
||||
|
||||
brushset_t *Brush_LoadEntity (entity_t *ent, int hullnum);
|
||||
int PlaneTypeForNormal (vec3_t normal);
|
||||
int FindPlane (plane_t *dplane, int *side);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// csg4.c
|
||||
|
||||
// build surfaces is also used by GatherNodeFaces
|
||||
extern face_t *validfaces[MAX_MAP_PLANES];
|
||||
surface_t *BuildSurfaces (void);
|
||||
|
||||
face_t *NewFaceFromFace (face_t *in);
|
||||
surface_t *CSGFaces (brushset_t *bs);
|
||||
void SplitFace (face_t *in, plane_t *split, face_t **front, face_t **back);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// solidbsp.c
|
||||
|
||||
void DivideFacet (face_t *in, plane_t *split, face_t **front, face_t **back);
|
||||
void CalcSurfaceInfo (surface_t *surf);
|
||||
void SubdivideFace (face_t *f, face_t **prevptr);
|
||||
node_t *SolidBSP (surface_t *surfhead, qboolean midsplit);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// merge.c
|
||||
|
||||
void MergePlaneFaces (surface_t *plane);
|
||||
face_t *MergeFaceToList (face_t *face, face_t *list);
|
||||
face_t *FreeMergeListScraps (face_t *merged);
|
||||
void MergeAll (surface_t *surfhead);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// surfaces.c
|
||||
|
||||
extern int c_cornerverts;
|
||||
extern int c_tryedges;
|
||||
extern face_t *edgefaces[MAX_MAP_EDGES][2];
|
||||
|
||||
extern int firstmodeledge;
|
||||
extern int firstmodelface;
|
||||
|
||||
void SubdivideFaces (surface_t *surfhead);
|
||||
|
||||
surface_t *GatherNodeFaces (node_t *headnode);
|
||||
|
||||
void MakeFaceEdges (node_t *headnode);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// portals.c
|
||||
|
||||
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;
|
||||
|
||||
extern node_t outside_node; // portals outside the world face this
|
||||
|
||||
void PortalizeWorld (node_t *headnode);
|
||||
void WritePortalfile (node_t *headnode);
|
||||
void FreeAllPortals (node_t *node);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// region.c
|
||||
|
||||
void GrowNodeRegions (node_t *headnode);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// tjunc.c
|
||||
|
||||
void tjunc (node_t *headnode);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// writebsp.c
|
||||
|
||||
void WriteNodePlanes (node_t *headnode);
|
||||
void WriteClipNodes (node_t *headnode);
|
||||
void WriteDrawNodes (node_t *headnode);
|
||||
|
||||
void BumpModel (int hullnum);
|
||||
int FindFinalPlane (dplane_t *p);
|
||||
|
||||
void BeginBSPFile (void);
|
||||
void FinishBSPFile (void);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// draw.c
|
||||
|
||||
void Draw_ClearBounds (void);
|
||||
void Draw_AddToBounds (vec3_t v);
|
||||
void Draw_DrawFace (face_t *f);
|
||||
void Draw_ClearWindow (void);
|
||||
void Draw_SetRed (void);
|
||||
void Draw_SetGrey (void);
|
||||
void Draw_SetBlack (void);
|
||||
void DrawPoint (vec3_t v);
|
||||
|
||||
void Draw_SetColor (int c);
|
||||
void SetColor (int c);
|
||||
void DrawPortal (portal_t *p);
|
||||
void DrawLeaf (node_t *l, int color);
|
||||
void DrawBrush (brush_t *b);
|
||||
|
||||
void DrawWinding (winding_t *w);
|
||||
void DrawTri (vec3_t p1, vec3_t p2, vec3_t p3);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// outside.c
|
||||
|
||||
qboolean FillOutside (node_t *node);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
extern qboolean drawflag;
|
||||
extern qboolean nofill;
|
||||
extern qboolean notjunc;
|
||||
extern qboolean noclip;
|
||||
extern qboolean verbose;
|
||||
|
||||
extern int subdivide_size;
|
||||
|
||||
extern int hullnum;
|
||||
|
||||
extern brushset_t *brushset;
|
||||
|
||||
void qprintf (char *fmt, ...); // only prints if verbose
|
||||
|
||||
extern int valid;
|
||||
|
||||
extern char portfilename[1024];
|
||||
extern char bspfilename[1024];
|
||||
extern char pointfilename[1024];
|
||||
|
||||
extern qboolean worldmodel;
|
||||
|
||||
|
||||
// misc functions
|
||||
|
||||
face_t *AllocFace (void);
|
||||
void FreeFace (face_t *f);
|
||||
|
||||
struct portal_s *AllocPortal (void);
|
||||
void FreePortal (struct portal_s *p);
|
||||
|
||||
surface_t *AllocSurface (void);
|
||||
void FreeSurface (surface_t *s);
|
||||
|
||||
node_t *AllocNode (void);
|
||||
struct brush_s *AllocBrush (void);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
extern bsp_t *bsp;
|
||||
|
||||
#define EQUAL_EPSILON 0.001
|
69
tools/qfbsp/include/map.h
Normal file
69
tools/qfbsp/include/map.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define MAX_FACES 16
|
||||
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;
|
||||
|
||||
typedef struct epair_s
|
||||
{
|
||||
struct epair_s *next;
|
||||
char *key;
|
||||
char *value;
|
||||
} epair_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t origin;
|
||||
mbrush_t *brushes;
|
||||
epair_t *epairs;
|
||||
} entity_t;
|
||||
|
||||
extern int nummapbrushes;
|
||||
extern mbrush_t mapbrushes[MAX_MAP_BRUSHES];
|
||||
|
||||
extern int num_entities;
|
||||
extern entity_t entities[MAX_MAP_ENTITIES];
|
||||
|
||||
extern int nummiptex;
|
||||
extern char miptex[MAX_MAP_TEXINFO][16];
|
||||
|
||||
void LoadMapFile (char *filename);
|
||||
|
||||
int FindMiptex (char *name);
|
||||
|
||||
void PrintEntity (entity_t *ent);
|
||||
char *ValueForKey (entity_t *ent, char *key);
|
||||
void SetKeyValue (entity_t *ent, char *key, char *value);
|
||||
float FloatForKey (entity_t *ent, char *key);
|
||||
void GetVectorForKey (entity_t *ent, char *key, vec3_t vec);
|
||||
|
||||
void WriteEntitiesToString (void);
|
6
tools/qfbsp/source/.gitignore
vendored
Normal file
6
tools/qfbsp/source/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
.vimrc
|
||||
.deps
|
||||
.libs
|
||||
Makefile.in
|
||||
Makefile
|
||||
qfbsp
|
25
tools/qfbsp/source/Makefile.am
Normal file
25
tools/qfbsp/source/Makefile.am
Normal file
|
@ -0,0 +1,25 @@
|
|||
AUTOMAKE_OPTIONS= foreign
|
||||
|
||||
QFBSP_LIBS=@QFBSP_LIBS@
|
||||
QFBSP_DEPS=@QFBSP_DEPS@
|
||||
QFBSP_INCS=@QFBSP_INCS@
|
||||
PTHREAD_FLAGS=@PTHREAD_FLAGS@
|
||||
|
||||
INCLUDES= -I$(top_srcdir)/include $(QFBSP_INCS)
|
||||
|
||||
if BUILD_QFBSP
|
||||
qfbsp=qfbsp
|
||||
else
|
||||
qfbsp=
|
||||
endif
|
||||
|
||||
bin_PROGRAMS= $(qfbsp)
|
||||
EXTRA_PROGRAMS= qfbsp
|
||||
|
||||
qfbsp_SOURCES= \
|
||||
brush.c csg4.c map.c merge.c nodraw.c outside.c portals.c qfbsp.c \
|
||||
region.c solidbsp.c surfaces.c tjunc.c writebsp.c
|
||||
|
||||
qfbsp_LDFLAGS= $(PTHREAD_FLAGS)
|
||||
qfbsp_LDADD= $(QFBSP_LIBS)
|
||||
qfbsp_DEPENDENCIES= $(QFBSP_DEPS)
|
903
tools/qfbsp/source/brush.c
Normal file
903
tools/qfbsp/source/brush.c
Normal file
|
@ -0,0 +1,903 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
// brush.c
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
int numbrushplanes;
|
||||
plane_t planes[MAX_MAP_PLANES];
|
||||
|
||||
int numbrushfaces;
|
||||
mface_t faces[128]; // beveled clipping hull can generate many extra
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
CheckFace
|
||||
|
||||
Note: this will not catch 0 area polygons
|
||||
=================
|
||||
*/
|
||||
void CheckFace (face_t *f)
|
||||
{
|
||||
int i, j;
|
||||
vec_t *p1, *p2;
|
||||
vec_t d, edgedist;
|
||||
vec3_t dir, edgenormal, facenormal;
|
||||
|
||||
if (f->numpoints < 3)
|
||||
Sys_Error ("CheckFace: %i points",f->numpoints);
|
||||
|
||||
VectorCopy (planes[f->planenum].normal, facenormal);
|
||||
if (f->planeside)
|
||||
{
|
||||
VectorSubtract (vec3_origin, facenormal, facenormal);
|
||||
}
|
||||
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
{
|
||||
p1 = f->pts[i];
|
||||
|
||||
for (j=0 ; j<3 ; j++)
|
||||
if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE)
|
||||
Sys_Error ("CheckFace: BUGUS_RANGE: %f",p1[j]);
|
||||
|
||||
j = i+1 == f->numpoints ? 0 : i+1;
|
||||
|
||||
// check the point is on the face plane
|
||||
d = DotProduct (p1, planes[f->planenum].normal) - planes[f->planenum].dist;
|
||||
if (d < -ON_EPSILON || d > ON_EPSILON)
|
||||
Sys_Error ("CheckFace: point off plane");
|
||||
|
||||
// check the edge isn't degenerate
|
||||
p2 = f->pts[j];
|
||||
VectorSubtract (p2, p1, dir);
|
||||
|
||||
if (VectorLength (dir) < ON_EPSILON)
|
||||
Sys_Error ("CheckFace: degenerate edge");
|
||||
|
||||
CrossProduct (facenormal, dir, edgenormal);
|
||||
VectorNormalize (edgenormal);
|
||||
edgedist = DotProduct (p1, edgenormal);
|
||||
edgedist += ON_EPSILON;
|
||||
|
||||
// all other points must be on front side
|
||||
for (j=0 ; j<f->numpoints ; j++)
|
||||
{
|
||||
if (j == i)
|
||||
continue;
|
||||
d = DotProduct (f->pts[j], edgenormal);
|
||||
if (d > edgedist)
|
||||
Sys_Error ("CheckFace: non-convex");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
ClearBounds
|
||||
=================
|
||||
*/
|
||||
void ClearBounds (brushset_t *bs)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (j=0 ; j<NUM_HULLS ; j++)
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
bs->mins[i] = 99999;
|
||||
bs->maxs[i] = -99999;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
AddToBounds
|
||||
=================
|
||||
*/
|
||||
void AddToBounds (brushset_t *bs, vec3_t v)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
if (v[i] < bs->mins[i])
|
||||
bs->mins[i] = v[i];
|
||||
if (v[i] > bs->maxs[i])
|
||||
bs->maxs[i] = v[i];
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
int PlaneTypeForNormal (vec3_t normal)
|
||||
{
|
||||
float 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)
|
||||
Sys_Error ("PlaneTypeForNormal: not a canonical vector");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#define DISTEPSILON 0.01
|
||||
#define ANGLEEPSILON 0.00001
|
||||
|
||||
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;
|
||||
}
|
||||
if (dp->normal[1] == -1.0)
|
||||
{
|
||||
dp->normal[1] = 1.0;
|
||||
dp->dist = -dp->dist;
|
||||
}
|
||||
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)
|
||||
{
|
||||
VectorSubtract (vec3_origin, dp->normal, dp->normal);
|
||||
dp->dist = -dp->dist;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
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;
|
||||
|
||||
dot = VectorLength(dplane->normal);
|
||||
if (dot < 1.0 - ANGLEEPSILON || dot > 1.0 + ANGLEEPSILON)
|
||||
Sys_Error ("FindPlane: normalization error");
|
||||
|
||||
pl = *dplane;
|
||||
NormalizePlane (&pl);
|
||||
if (DotProduct(pl.normal, dplane->normal) > 0)
|
||||
*side = 0;
|
||||
else
|
||||
*side = 1;
|
||||
|
||||
dp = planes;
|
||||
for (i=0 ; i<numbrushplanes;i++, dp++)
|
||||
{
|
||||
dot = DotProduct (dp->normal, pl.normal);
|
||||
if (dot > 1.0 - ANGLEEPSILON
|
||||
&& fabs(dp->dist - pl.dist) < DISTEPSILON )
|
||||
{ // regular match
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
if (numbrushplanes == MAX_MAP_PLANES)
|
||||
Sys_Error ("numbrushplanes == MAX_MAP_PLANES");
|
||||
|
||||
planes[numbrushplanes] = pl;
|
||||
|
||||
numbrushplanes++;
|
||||
|
||||
return numbrushplanes-1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
FindPlane_old
|
||||
|
||||
Returns a global plane number and the side that will be the front
|
||||
===============
|
||||
*/
|
||||
int FindPlane_old (plane_t *dplane, int *side)
|
||||
{
|
||||
int i;
|
||||
plane_t *dp;
|
||||
vec_t dot, ax, ay, az;
|
||||
|
||||
dot = VectorLength(dplane->normal);
|
||||
if (dot < 1.0 - ANGLEEPSILON || dot > 1.0 + ANGLEEPSILON)
|
||||
Sys_Error ("FindPlane: normalization error");
|
||||
|
||||
dp = planes;
|
||||
|
||||
for (i=0 ; i<numbrushplanes;i++, dp++)
|
||||
{
|
||||
dot = DotProduct (dplane->normal, dp->normal);
|
||||
if (dot > 1.0 - ANGLEEPSILON
|
||||
&& fabs(dplane->dist - dp->dist) < DISTEPSILON )
|
||||
{ // regular match
|
||||
*side = 0;
|
||||
return i;
|
||||
}
|
||||
if (dot < -1.0+ANGLEEPSILON
|
||||
&& fabs(dplane->dist + dp->dist) < DISTEPSILON )
|
||||
{ // inverse of vector
|
||||
*side = 1;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// allocate a new plane, flipping normal to a consistant direction
|
||||
// if needed
|
||||
*dp = *dplane;
|
||||
|
||||
if (numbrushplanes == MAX_MAP_PLANES)
|
||||
Sys_Error ("numbrushplanes == MAX_MAP_PLANES");
|
||||
numbrushplanes++;
|
||||
|
||||
*side = 0;
|
||||
|
||||
// NOTE: should these have an epsilon around 1.0?
|
||||
if (dplane->normal[0] == 1.0)
|
||||
dp->type = PLANE_X;
|
||||
else if (dplane->normal[1] == 1.0)
|
||||
dp->type = PLANE_Y;
|
||||
else if (dplane->normal[2] == 1.0)
|
||||
dp->type = PLANE_Z;
|
||||
else if (dplane->normal[0] == -1.0)
|
||||
{
|
||||
dp->type = PLANE_X;
|
||||
dp->normal[0] = 1.0;
|
||||
dp->dist = -dp->dist;
|
||||
*side = 1;
|
||||
}
|
||||
else if (dplane->normal[1] == -1.0)
|
||||
{
|
||||
dp->type = PLANE_Y;
|
||||
dp->normal[1] = 1.0;
|
||||
dp->dist = -dp->dist;
|
||||
*side = 1;
|
||||
}
|
||||
else if (dplane->normal[2] == -1.0)
|
||||
{
|
||||
dp->type = PLANE_Z;
|
||||
dp->normal[2] = 1.0;
|
||||
dp->dist = -dp->dist;
|
||||
*side = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ax = fabs(dplane->normal[0]);
|
||||
ay = fabs(dplane->normal[1]);
|
||||
az = fabs(dplane->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 (dplane->normal[dp->type-PLANE_ANYX] < 0)
|
||||
{
|
||||
VectorSubtract (vec3_origin, dp->normal, dp->normal);
|
||||
dp->dist = -dp->dist;
|
||||
*side = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
TURN BRUSHES INTO GROUPS OF FACES
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
vec3_t brush_mins, brush_maxs;
|
||||
face_t *brush_faces;
|
||||
|
||||
/*
|
||||
=================
|
||||
CreateBrushFaces
|
||||
=================
|
||||
*/
|
||||
#define ZERO_EPSILON 0.001
|
||||
void CreateBrushFaces (void)
|
||||
{
|
||||
int i,j, k;
|
||||
vec_t r;
|
||||
face_t *f;
|
||||
winding_t *w;
|
||||
plane_t plane;
|
||||
mface_t *mf;
|
||||
|
||||
brush_mins[0] = brush_mins[1] = brush_mins[2] = 99999;
|
||||
brush_maxs[0] = brush_maxs[1] = brush_maxs[2] = -99999;
|
||||
|
||||
brush_faces = NULL;
|
||||
|
||||
for (i=0 ; i<numbrushfaces ; i++)
|
||||
{
|
||||
mf = &faces[i];
|
||||
|
||||
w = BaseWindingForPlane (&mf->plane);
|
||||
|
||||
for (j=0 ; j<numbrushfaces && w ; j++)
|
||||
{
|
||||
if (j == i)
|
||||
continue;
|
||||
// flip the plane, because we want to keep the back side
|
||||
VectorSubtract (vec3_origin,faces[j].plane.normal, plane.normal);
|
||||
plane.dist = -faces[j].plane.dist;
|
||||
|
||||
w = ClipWinding (w, &plane, false);
|
||||
}
|
||||
|
||||
if (!w)
|
||||
continue; // overcontrained plane
|
||||
|
||||
// this face is a keeper
|
||||
f = AllocFace ();
|
||||
f->numpoints = w->numpoints;
|
||||
if (f->numpoints > MAXEDGES)
|
||||
Sys_Error ("f->numpoints > MAXEDGES");
|
||||
|
||||
for (j=0 ; j<w->numpoints ; j++)
|
||||
{
|
||||
for (k=0 ; k<3 ; k++)
|
||||
{
|
||||
r = (int) (w->points[j][k] + 0.5);
|
||||
if ( fabs(w->points[j][k] - r) < ZERO_EPSILON)
|
||||
f->pts[j][k] = r;
|
||||
else
|
||||
f->pts[j][k] = w->points[j][k];
|
||||
|
||||
if (f->pts[j][k] < brush_mins[k])
|
||||
brush_mins[k] = f->pts[j][k];
|
||||
if (f->pts[j][k] > brush_maxs[k])
|
||||
brush_maxs[k] = f->pts[j][k];
|
||||
}
|
||||
|
||||
}
|
||||
FreeWinding (w);
|
||||
f->texturenum = mf->texinfo;
|
||||
f->planenum = FindPlane (&mf->plane, &f->planeside);
|
||||
f->next = brush_faces;
|
||||
brush_faces = f;
|
||||
CheckFace (f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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} }
|
||||
|
||||
};
|
||||
|
||||
#define MAX_HULL_POINTS 32
|
||||
#define MAX_HULL_EDGES 64
|
||||
|
||||
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;
|
||||
float l;
|
||||
|
||||
if (numbrushfaces == MAX_FACES)
|
||||
Sys_Error ("AddBrushPlane: numbrushfaces == MAX_FACES");
|
||||
l = VectorLength (plane->normal);
|
||||
if (l < 0.999 || l > 1.001)
|
||||
Sys_Error ("AddBrushPlane: bad normal");
|
||||
|
||||
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;
|
||||
VectorSubtract (vec3_origin, 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])
|
||||
{
|
||||
VectorSubtract (vec3_origin, 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)
|
||||
Sys_Error ("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; // allread added
|
||||
|
||||
if (num_hull_edges == MAX_HULL_EDGES)
|
||||
Sys_Error ("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];
|
||||
|
||||
VectorCopy (vec3_origin, 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;
|
||||
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)
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
AddHullPoint (f->pts[i], hullnum);
|
||||
|
||||
// expand all of the planes
|
||||
for (i=0 ; i<numbrushfaces ; i++)
|
||||
{
|
||||
p = &faces[i].plane;
|
||||
VectorCopy (vec3_origin, 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
|
||||
VectorCopy (vec3_origin, 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)
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
AddHullEdge (f->pts[i], f->pts[(i+1)%f->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[bsp->texinfo[mb->faces->texinfo].miptex];
|
||||
|
||||
if (!strcasecmp(name, "clip") && hullnum == 0)
|
||||
return NULL; // "clip" brushes don't show up in the draw hull
|
||||
|
||||
if (name[0] == '*' && worldmodel) // entities never use water merging
|
||||
{
|
||||
if (!strncasecmp(name+1,"lava",4))
|
||||
contents = CONTENTS_LAVA;
|
||||
else if (!strncasecmp(name+1,"slime",5))
|
||||
contents = CONTENTS_SLIME;
|
||||
else
|
||||
contents = CONTENTS_WATER;
|
||||
}
|
||||
else if (!strncasecmp (name, "sky",3) && worldmodel && 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_DrawAll
|
||||
|
||||
============
|
||||
*/
|
||||
void Brush_DrawAll (brushset_t *bs)
|
||||
{
|
||||
brush_t *b;
|
||||
face_t *f;
|
||||
|
||||
for (b=bs->brushes ; b ; b=b->next)
|
||||
for (f=b->faces ; f ; f=f->next)
|
||||
Draw_DrawFace (f);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
Brush_LoadEntity
|
||||
============
|
||||
*/
|
||||
brushset_t *Brush_LoadEntity (entity_t *ent, int hullnum)
|
||||
{
|
||||
brush_t *b, *next, *water, *other;
|
||||
mbrush_t *mbr;
|
||||
int numbrushes;
|
||||
brushset_t *bset;
|
||||
|
||||
bset = malloc (sizeof(brushset_t));
|
||||
memset (bset, 0, sizeof(brushset_t));
|
||||
ClearBounds (bset);
|
||||
|
||||
numbrushes = 0;
|
||||
other = water = NULL;
|
||||
|
||||
qprintf ("--- Brush_LoadEntity ---\n");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
AddToBounds (bset, b->mins);
|
||||
AddToBounds (bset, b->maxs);
|
||||
}
|
||||
|
||||
// add all of the water textures at the start
|
||||
for (b=water ; b ; b=next)
|
||||
{
|
||||
next = b->next;
|
||||
b->next = other;
|
||||
other = b;
|
||||
}
|
||||
|
||||
bset->brushes = other;
|
||||
|
||||
brushset = bset;
|
||||
Brush_DrawAll (bset);
|
||||
|
||||
qprintf ("%i brushes read\n",numbrushes);
|
||||
|
||||
return bset;
|
||||
}
|
||||
|
||||
|
489
tools/qfbsp/source/csg4.c
Normal file
489
tools/qfbsp/source/csg4.c
Normal file
|
@ -0,0 +1,489 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
// csg4.c
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
/*
|
||||
|
||||
NOTES
|
||||
-----
|
||||
Brushes that touch still need to be split at the cut point to make a tjunction
|
||||
|
||||
*/
|
||||
|
||||
face_t *validfaces[MAX_MAP_PLANES];
|
||||
|
||||
|
||||
face_t *inside, *outside;
|
||||
int brushfaces;
|
||||
int csgfaces;
|
||||
int csgmergefaces;
|
||||
|
||||
void DrawList (face_t *list)
|
||||
{
|
||||
for ( ; list ; list=list->next)
|
||||
Draw_DrawFace (list);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
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)
|
||||
{
|
||||
vec_t dists[MAXEDGES+1];
|
||||
int sides[MAXEDGES+1];
|
||||
int counts[3];
|
||||
vec_t dot;
|
||||
int i, j;
|
||||
face_t *newf, *new2;
|
||||
vec_t *p1, *p2;
|
||||
vec3_t mid;
|
||||
|
||||
if (in->numpoints < 0)
|
||||
Sys_Error ("SplitFace: freed face");
|
||||
counts[0] = counts[1] = counts[2] = 0;
|
||||
|
||||
// determine sides for each point
|
||||
for (i=0 ; i<in->numpoints ; i++)
|
||||
{
|
||||
dot = DotProduct (in->pts[i], split->normal);
|
||||
dot -= split->dist;
|
||||
dists[i] = dot;
|
||||
if (dot > ON_EPSILON)
|
||||
sides[i] = SIDE_FRONT;
|
||||
else if (dot < -ON_EPSILON)
|
||||
sides[i] = SIDE_BACK;
|
||||
else
|
||||
sides[i] = SIDE_ON;
|
||||
counts[sides[i]]++;
|
||||
}
|
||||
sides[i] = sides[0];
|
||||
dists[i] = dists[0];
|
||||
|
||||
if (!counts[0])
|
||||
{
|
||||
*front = NULL;
|
||||
*back = in;
|
||||
return;
|
||||
}
|
||||
if (!counts[1])
|
||||
{
|
||||
*front = in;
|
||||
*back = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*back = newf = NewFaceFromFace (in);
|
||||
*front = new2 = NewFaceFromFace (in);
|
||||
|
||||
// distribute the points and generate splits
|
||||
|
||||
for (i=0 ; i<in->numpoints ; i++)
|
||||
{
|
||||
if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
|
||||
Sys_Error ("SplitFace: numpoints > MAXEDGES");
|
||||
|
||||
p1 = in->pts[i];
|
||||
|
||||
if (sides[i] == SIDE_ON)
|
||||
{
|
||||
VectorCopy (p1, newf->pts[newf->numpoints]);
|
||||
newf->numpoints++;
|
||||
VectorCopy (p1, new2->pts[new2->numpoints]);
|
||||
new2->numpoints++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sides[i] == SIDE_FRONT)
|
||||
{
|
||||
VectorCopy (p1, new2->pts[new2->numpoints]);
|
||||
new2->numpoints++;
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorCopy (p1, newf->pts[newf->numpoints]);
|
||||
newf->numpoints++;
|
||||
}
|
||||
|
||||
if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
|
||||
continue;
|
||||
|
||||
// generate a split point
|
||||
p2 = in->pts[(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, newf->pts[newf->numpoints]);
|
||||
newf->numpoints++;
|
||||
VectorCopy (mid, new2->pts[new2->numpoints]);
|
||||
new2->numpoints++;
|
||||
}
|
||||
|
||||
if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
|
||||
Sys_Error ("SplitFace: numpoints > MAXEDGES");
|
||||
|
||||
#if 0
|
||||
CheckFace (newf);
|
||||
CheckFace (new2);
|
||||
#endif
|
||||
|
||||
// free the original face now that is is represented by the fragments
|
||||
FreeFace (in);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
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 = &planes[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 (qboolean mirror)
|
||||
{
|
||||
face_t *f , *next, *newf;
|
||||
int i;
|
||||
int planenum;
|
||||
|
||||
for (f=outside ; f ; f=next)
|
||||
{
|
||||
next = f->next;
|
||||
csgfaces++;
|
||||
Draw_DrawFace (f);
|
||||
planenum = f->planenum;
|
||||
|
||||
if (mirror)
|
||||
{
|
||||
newf = NewFaceFromFace (f);
|
||||
|
||||
newf->numpoints = f->numpoints;
|
||||
newf->planeside = f->planeside ^ 1; // reverse side
|
||||
newf->contents[0] = f->contents[1];
|
||||
newf->contents[1] = f->contents[0];
|
||||
|
||||
for (i=0 ; i<f->numpoints ; i++) // add points backwards
|
||||
{
|
||||
VectorCopy (f->pts[f->numpoints-1-i], newf->pts[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
newf = NULL;
|
||||
|
||||
validfaces[planenum] = MergeFaceToList(f, validfaces[planenum]);
|
||||
if (newf)
|
||||
validfaces[planenum] = MergeFaceToList(newf, validfaces[planenum]);
|
||||
|
||||
validfaces[planenum] = FreeMergeListScraps (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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
BuildSurfaces
|
||||
|
||||
Returns a chain of all the external surfaces with one or more visible
|
||||
faces.
|
||||
==================
|
||||
*/
|
||||
surface_t *BuildSurfaces (void)
|
||||
{
|
||||
face_t **f;
|
||||
face_t *count;
|
||||
int i;
|
||||
surface_t *s;
|
||||
surface_t *surfhead;
|
||||
|
||||
surfhead = NULL;
|
||||
|
||||
f = validfaces;
|
||||
for (i=0 ; i<numbrushplanes ; 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
|
||||
}
|
||||
|
||||
return surfhead;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
CopyFacesToOutside
|
||||
==================
|
||||
*/
|
||||
void CopyFacesToOutside (brush_t *b)
|
||||
{
|
||||
face_t *f, *newf;
|
||||
|
||||
outside = NULL;
|
||||
|
||||
for (f=b->faces ; f ; f=f->next)
|
||||
{
|
||||
brushfaces++;
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
printf ("(%f,%f,%f) ",f->pts[i][0], f->pts[i][1], f->pts[i][2]);
|
||||
printf ("\n");
|
||||
}
|
||||
#endif
|
||||
newf = AllocFace ();
|
||||
*newf = *f;
|
||||
newf->next = outside;
|
||||
newf->contents[0] = CONTENTS_EMPTY;
|
||||
newf->contents[1] = b->contents;
|
||||
outside = newf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
CSGFaces
|
||||
|
||||
Returns a list of surfaces containing aall of the faces
|
||||
==================
|
||||
*/
|
||||
surface_t *CSGFaces (brushset_t *bs)
|
||||
{
|
||||
brush_t *b1, *b2;
|
||||
int i;
|
||||
qboolean overwrite;
|
||||
face_t *f;
|
||||
surface_t *surfhead;
|
||||
|
||||
qprintf ("---- CSGFaces ----\n");
|
||||
|
||||
memset (validfaces, 0, sizeof(validfaces));
|
||||
|
||||
csgfaces = brushfaces = csgmergefaces = 0;
|
||||
|
||||
Draw_ClearWindow ();
|
||||
|
||||
//
|
||||
// do the solid faces
|
||||
//
|
||||
for (b1=bs->brushes ; b1 ; b1 = b1->next)
|
||||
{
|
||||
// set outside to a copy of the brush's faces
|
||||
CopyFacesToOutside (b1);
|
||||
|
||||
overwrite = false;
|
||||
|
||||
for (b2=bs->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 (true); // mirror faces for inside view
|
||||
else
|
||||
SaveOutside (false);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!csgfaces)
|
||||
Sys_Error ("No faces");
|
||||
#endif
|
||||
|
||||
surfhead = BuildSurfaces ();
|
||||
|
||||
qprintf ("%5i brushfaces\n", brushfaces);
|
||||
qprintf ("%5i csgfaces\n", csgfaces);
|
||||
qprintf ("%5i mergedfaces\n", csgmergefaces);
|
||||
|
||||
return surfhead;
|
||||
}
|
||||
|
||||
|
612
tools/qfbsp/source/map.c
Normal file
612
tools/qfbsp/source/map.c
Normal file
|
@ -0,0 +1,612 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
// map.c
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/quakefs.h"
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
int nummapbrushes;
|
||||
mbrush_t mapbrushes[MAX_MAP_BRUSHES];
|
||||
|
||||
int num_entities;
|
||||
entity_t entities[MAX_MAP_ENTITIES];
|
||||
|
||||
int nummiptex;
|
||||
char miptex[MAX_MAP_TEXINFO][16];
|
||||
|
||||
//============================================================================
|
||||
|
||||
/*
|
||||
===============
|
||||
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)
|
||||
Sys_Error ("nummiptex == MAX_MAP_TEXINFO");
|
||||
strcpy (miptex[i], name);
|
||||
nummiptex++;
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
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] == '*'
|
||||
|| !strncasecmp (miptex[t->miptex], "sky",3) )
|
||||
t->flags |= TEX_SPECIAL;
|
||||
|
||||
|
||||
tex = bsp->texinfo;
|
||||
for (i=0 ; i<bsp->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 (bsp->numtexinfo == MAX_MAP_TEXINFO)
|
||||
Sys_Error ("numtexinfo == MAX_MAP_TEXINFO");
|
||||
bsp->texinfo[i] = *t;
|
||||
bsp->numtexinfo++;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
#define MAXTOKEN 128
|
||||
|
||||
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 allready waiting?
|
||||
return true;
|
||||
|
||||
//
|
||||
// skip space
|
||||
//
|
||||
skipspace:
|
||||
while (*script_p <= 32)
|
||||
{
|
||||
if (!*script_p)
|
||||
{
|
||||
if (!crossline)
|
||||
Sys_Error ("Line %i is incomplete",scriptline);
|
||||
return false;
|
||||
}
|
||||
if (*script_p++ == '\n')
|
||||
{
|
||||
if (!crossline)
|
||||
Sys_Error ("Line %i is incomplete",scriptline);
|
||||
scriptline++;
|
||||
}
|
||||
}
|
||||
|
||||
if (script_p[0] == '/' && script_p[1] == '/') // comment field
|
||||
{
|
||||
if (!crossline)
|
||||
Sys_Error ("Line %i is incomplete\n",scriptline);
|
||||
while (*script_p++ != '\n')
|
||||
if (!*script_p)
|
||||
{
|
||||
if (!crossline)
|
||||
Sys_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)
|
||||
Sys_Error ("EOF inside quoted token");
|
||||
*token_p++ = *script_p++;
|
||||
if (token_p > &token[MAXTOKEN-1])
|
||||
Sys_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])
|
||||
Sys_Error ("Token too large on line %i",scriptline);
|
||||
}
|
||||
|
||||
*token_p = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UngetToken ()
|
||||
{
|
||||
unget = true;
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
entity_t *mapent;
|
||||
|
||||
/*
|
||||
=================
|
||||
ParseEpair
|
||||
=================
|
||||
*/
|
||||
void ParseEpair (void)
|
||||
{
|
||||
epair_t *e;
|
||||
|
||||
e = malloc (sizeof(epair_t));
|
||||
memset (e, 0, sizeof(epair_t));
|
||||
e->next = mapent->epairs;
|
||||
mapent->epairs = e;
|
||||
|
||||
if (strlen(token) >= MAX_KEY-1)
|
||||
Sys_Error ("ParseEpar: token too long");
|
||||
e->key = strdup (token);
|
||||
GetToken (false);
|
||||
if (strlen(token) >= MAX_VALUE-1)
|
||||
Sys_Error ("ParseEpar: token too long");
|
||||
e->value = strdup (token);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
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;
|
||||
float 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 (void)
|
||||
{
|
||||
mbrush_t *b;
|
||||
mface_t *f, *f2;
|
||||
vec3_t planepts[3];
|
||||
vec3_t t1, t2, t3;
|
||||
int i,j;
|
||||
texinfo_t tx;
|
||||
vec_t d;
|
||||
float shift[2], rotate, scale[2];
|
||||
|
||||
b = &mapbrushes[nummapbrushes];
|
||||
nummapbrushes++;
|
||||
b->next = mapent->brushes;
|
||||
mapent->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, "(") )
|
||||
Sys_Error ("parsing brush");
|
||||
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
GetToken (false);
|
||||
planepts[i][j] = atoi(token);
|
||||
}
|
||||
|
||||
GetToken (false);
|
||||
if (strcmp (token, ")") )
|
||||
Sys_Error ("parsing brush");
|
||||
|
||||
}
|
||||
|
||||
// read the texturedef
|
||||
memset (&tx, 0, sizeof(tx));
|
||||
GetToken (false);
|
||||
tx.miptex = FindMiptex (token);
|
||||
GetToken (false);
|
||||
shift[0] = atoi(token);
|
||||
GetToken (false);
|
||||
shift[1] = atoi(token);
|
||||
GetToken (false);
|
||||
rotate = atoi(token);
|
||||
GetToken (false);
|
||||
scale[0] = atof(token);
|
||||
GetToken (false);
|
||||
scale[1] = atof(token);
|
||||
|
||||
// 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\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
f = malloc(sizeof(mface_t));
|
||||
f->next = b->faces;
|
||||
b->faces = f;
|
||||
|
||||
// convert to a vector / dist plane
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
t1[j] = planepts[0][j] - planepts[1][j];
|
||||
t2[j] = planepts[2][j] - planepts[1][j];
|
||||
t3[j] = planepts[1][j];
|
||||
}
|
||||
|
||||
CrossProduct(t1,t2, f->plane.normal);
|
||||
if (VectorCompare (f->plane.normal, vec3_origin))
|
||||
{
|
||||
printf ("WARNING: brush plane with no normal\n");
|
||||
b->faces = f->next;
|
||||
free (f);
|
||||
break;
|
||||
}
|
||||
VectorNormalize (f->plane.normal);
|
||||
f->plane.dist = DotProduct (t3, f->plane.normal);
|
||||
|
||||
//
|
||||
// fake proper texture vectors from QuakeEd style
|
||||
//
|
||||
{
|
||||
vec3_t vecs[2];
|
||||
int sv, tv;
|
||||
float ang, sinv, cosv;
|
||||
float ns, nt;
|
||||
|
||||
TextureAxisFromPlane(&f->plane, vecs[0], vecs[1]);
|
||||
|
||||
if (!scale[0])
|
||||
scale[0] = 1;
|
||||
if (!scale[1])
|
||||
scale[1] = 1;
|
||||
|
||||
|
||||
// rotate axis
|
||||
if (rotate == 0)
|
||||
{ sinv = 0 ; cosv = 1; }
|
||||
else if (rotate == 90)
|
||||
{ sinv = 1 ; cosv = 0; }
|
||||
else if (rotate == 180)
|
||||
{ sinv = 0 ; cosv = -1; }
|
||||
else if (rotate == 270)
|
||||
{ sinv = -1 ; cosv = 0; }
|
||||
else
|
||||
{
|
||||
ang = rotate / 180 * M_PI;
|
||||
sinv = sin(ang);
|
||||
cosv = cos(ang);
|
||||
}
|
||||
|
||||
if (vecs[0][0])
|
||||
sv = 0;
|
||||
else if (vecs[0][1])
|
||||
sv = 1;
|
||||
else
|
||||
sv = 2;
|
||||
|
||||
if (vecs[1][0])
|
||||
tv = 0;
|
||||
else if (vecs[1][1])
|
||||
tv = 1;
|
||||
else
|
||||
tv = 2;
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
|
||||
nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
|
||||
vecs[i][sv] = ns;
|
||||
vecs[i][tv] = nt;
|
||||
}
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
for (j=0 ; j<3 ; j++)
|
||||
tx.vecs[i][j] = vecs[i][j] / scale[i];
|
||||
|
||||
tx.vecs[0][3] = shift[0];
|
||||
tx.vecs[1][3] = shift[1];
|
||||
}
|
||||
|
||||
// unique the texinfo
|
||||
f->texinfo = FindTexinfo (&tx);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
ParseEntity
|
||||
================
|
||||
*/
|
||||
qboolean ParseEntity (void)
|
||||
{
|
||||
if (!GetToken (true))
|
||||
return false;
|
||||
|
||||
if (strcmp (token, "{") )
|
||||
Sys_Error ("ParseEntity: { not found");
|
||||
|
||||
if (num_entities == MAX_MAP_ENTITIES)
|
||||
Sys_Error ("num_entities == MAX_MAP_ENTITIES");
|
||||
|
||||
mapent = &entities[num_entities];
|
||||
num_entities++;
|
||||
|
||||
do
|
||||
{
|
||||
if (!GetToken (true))
|
||||
Sys_Error ("ParseEntity: EOF without closing brace");
|
||||
if (!strcmp (token, "}") )
|
||||
break;
|
||||
if (!strcmp (token, "{") )
|
||||
ParseBrush ();
|
||||
else
|
||||
ParseEpair ();
|
||||
} while (1);
|
||||
|
||||
GetVectorForKey (mapent, "origin", mapent->origin);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
LoadMapFile
|
||||
================
|
||||
*/
|
||||
void LoadMapFile (char *filename)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = COM_LoadFile (filename, 0);
|
||||
|
||||
StartTokenParsing (buf);
|
||||
|
||||
num_entities = 0;
|
||||
|
||||
while (ParseEntity ())
|
||||
{
|
||||
}
|
||||
|
||||
free (buf);
|
||||
|
||||
qprintf ("--- LoadMapFile ---\n");
|
||||
qprintf ("%s\n", filename);
|
||||
qprintf ("%5i brushes\n", nummapbrushes);
|
||||
qprintf ("%5i entities\n", num_entities);
|
||||
qprintf ("%5i miptex\n", nummiptex);
|
||||
qprintf ("%5i texinfo\n", bsp->numtexinfo);
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
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) )
|
||||
{
|
||||
free (ep->value);
|
||||
ep->value = strdup (value);
|
||||
return;
|
||||
}
|
||||
ep = malloc (sizeof(*ep));
|
||||
ep->next = ent->epairs;
|
||||
ent->epairs = ep;
|
||||
ep->key = strdup (key);
|
||||
ep->value = strdup (value);
|
||||
}
|
||||
|
||||
float FloatForKey (entity_t *ent, char *key)
|
||||
{
|
||||
char *k;
|
||||
|
||||
k = ValueForKey (ent, key);
|
||||
return atof(k);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
void WriteEntitiesToString (void)
|
||||
{
|
||||
char *buf, *end;
|
||||
epair_t *ep;
|
||||
char line[128];
|
||||
int i;
|
||||
|
||||
buf = bsp->entdata;
|
||||
end = buf;
|
||||
*end = 0;
|
||||
|
||||
for (i=0 ; i<num_entities ; i++)
|
||||
{
|
||||
ep = entities[i].epairs;
|
||||
if (!ep)
|
||||
continue; // ent got removed
|
||||
|
||||
strcat (end,"{\n");
|
||||
end += 2;
|
||||
|
||||
for (ep = entities[i].epairs ; 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)
|
||||
Sys_Error ("Entity text too long");
|
||||
}
|
||||
bsp->entdatasize = end - buf + 1;
|
||||
}
|
||||
|
302
tools/qfbsp/source/merge.c
Normal file
302
tools/qfbsp/source/merge.c
Normal file
|
@ -0,0 +1,302 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
// merge.c
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
|
||||
#define CONTINUOUS_EPSILON 0.001
|
||||
|
||||
/*
|
||||
================
|
||||
CheckColinear
|
||||
|
||||
================
|
||||
*/
|
||||
void CheckColinear (face_t *f)
|
||||
{
|
||||
int i, j;
|
||||
vec3_t v1, v2;
|
||||
|
||||
for (i=0 ; i<f->numpoints ;i++)
|
||||
{
|
||||
// skip the point if the vector from the previous point is the same
|
||||
// as the vector to the next point
|
||||
j = (i - 1 < 0) ? f->numpoints - 1 : i - 1;
|
||||
VectorSubtract (f->pts[i], f->pts[j], v1);
|
||||
VectorNormalize (v1);
|
||||
|
||||
j = (i + 1 == f->numpoints) ? 0 : i + 1;
|
||||
VectorSubtract (f->pts[j], f->pts[i], v2);
|
||||
VectorNormalize (v2);
|
||||
|
||||
if (VectorCompare (v1, v2))
|
||||
Sys_Error ("Colinear edge");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
TryMerge
|
||||
|
||||
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 faces couldn't be merged, or the new face.
|
||||
The originals will NOT be freed.
|
||||
=============
|
||||
*/
|
||||
face_t *TryMerge (face_t *f1, face_t *f2)
|
||||
{
|
||||
vec_t *p1, *p2, *p3, *p4, *back;
|
||||
face_t *newf;
|
||||
int i, j, k, l;
|
||||
vec3_t normal, delta, planenormal;
|
||||
vec_t dot;
|
||||
plane_t *plane;
|
||||
qboolean keep1, keep2;
|
||||
|
||||
if (f1->numpoints == -1 || f2->numpoints == -1)
|
||||
return NULL;
|
||||
if (f1->planeside != f2->planeside)
|
||||
return NULL;
|
||||
if (f1->texturenum != f2->texturenum)
|
||||
return NULL;
|
||||
if (f1->contents[0] != f2->contents[0])
|
||||
return NULL;
|
||||
if (f1->contents[1] != f2->contents[1])
|
||||
return NULL;
|
||||
|
||||
//
|
||||
// find a common edge
|
||||
//
|
||||
p1 = p2 = NULL; // stop compiler warning
|
||||
j = 0; //
|
||||
|
||||
for (i=0 ; i<f1->numpoints ; i++)
|
||||
{
|
||||
p1 = f1->pts[i];
|
||||
p2 = f1->pts[(i+1)%f1->numpoints];
|
||||
for (j=0 ; j<f2->numpoints ; j++)
|
||||
{
|
||||
p3 = f2->pts[j];
|
||||
p4 = f2->pts[(j+1)%f2->numpoints];
|
||||
for (k=0 ; k<3 ; k++)
|
||||
{
|
||||
if (fabs(p1[k] - p4[k]) > EQUAL_EPSILON)
|
||||
break;
|
||||
if (fabs(p2[k] - p3[k]) > EQUAL_EPSILON)
|
||||
break;
|
||||
}
|
||||
if (k==3)
|
||||
break;
|
||||
}
|
||||
if (j < f2->numpoints)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == f1->numpoints)
|
||||
return NULL; // no matching edges
|
||||
|
||||
//
|
||||
// check slope of connected lines
|
||||
// if the slopes are colinear, the point can be removed
|
||||
//
|
||||
plane = &planes[f1->planenum];
|
||||
VectorCopy (plane->normal, planenormal);
|
||||
if (f1->planeside)
|
||||
VectorSubtract (vec3_origin, planenormal, planenormal);
|
||||
|
||||
back = f1->pts[(i+f1->numpoints-1)%f1->numpoints];
|
||||
VectorSubtract (p1, back, delta);
|
||||
CrossProduct (planenormal, delta, normal);
|
||||
VectorNormalize (normal);
|
||||
|
||||
back = f2->pts[(j+2)%f2->numpoints];
|
||||
VectorSubtract (back, p1, delta);
|
||||
dot = DotProduct (delta, normal);
|
||||
if (dot > CONTINUOUS_EPSILON)
|
||||
return NULL; // not a convex polygon
|
||||
keep1 = dot < -CONTINUOUS_EPSILON;
|
||||
|
||||
back = f1->pts[(i+2)%f1->numpoints];
|
||||
VectorSubtract (back, p2, delta);
|
||||
CrossProduct (planenormal, delta, normal);
|
||||
VectorNormalize (normal);
|
||||
|
||||
back = f2->pts[(j+f2->numpoints-1)%f2->numpoints];
|
||||
VectorSubtract (back, p2, delta);
|
||||
dot = DotProduct (delta, normal);
|
||||
if (dot > CONTINUOUS_EPSILON)
|
||||
return NULL; // not a convex polygon
|
||||
keep2 = dot < -CONTINUOUS_EPSILON;
|
||||
|
||||
//
|
||||
// build the new polygon
|
||||
//
|
||||
if (f1->numpoints + f2->numpoints > MAXEDGES)
|
||||
{
|
||||
// Sys_Error ("TryMerge: too many edges!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newf = NewFaceFromFace (f1);
|
||||
|
||||
// copy first polygon
|
||||
for (k=(i+1)%f1->numpoints ; k != i ; k=(k+1)%f1->numpoints)
|
||||
{
|
||||
if (k==(i+1)%f1->numpoints && !keep2)
|
||||
continue;
|
||||
|
||||
VectorCopy (f1->pts[k], newf->pts[newf->numpoints]);
|
||||
newf->numpoints++;
|
||||
}
|
||||
|
||||
// copy second polygon
|
||||
for (l= (j+1)%f2->numpoints ; l != j ; l=(l+1)%f2->numpoints)
|
||||
{
|
||||
if (l==(j+1)%f2->numpoints && !keep1)
|
||||
continue;
|
||||
VectorCopy (f2->pts[l], newf->pts[newf->numpoints]);
|
||||
newf->numpoints++;
|
||||
}
|
||||
|
||||
return newf;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
MergeFaceToList
|
||||
===============
|
||||
*/
|
||||
qboolean mergedebug;
|
||||
face_t *MergeFaceToList (face_t *face, face_t *list)
|
||||
{
|
||||
face_t *newf, *f;
|
||||
|
||||
for (f=list ; f ; f=f->next)
|
||||
{
|
||||
//CheckColinear (f);
|
||||
if (mergedebug)
|
||||
{
|
||||
Draw_ClearWindow ();
|
||||
Draw_DrawFace (face);
|
||||
Draw_DrawFace (f);
|
||||
Draw_SetBlack ();
|
||||
}
|
||||
newf = TryMerge (face, f);
|
||||
if (!newf)
|
||||
continue;
|
||||
FreeFace (face);
|
||||
f->numpoints = -1; // merged out
|
||||
return MergeFaceToList (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;
|
||||
|
||||
head = NULL;
|
||||
for ( ; merged ; merged = next)
|
||||
{
|
||||
next = merged->next;
|
||||
if (merged->numpoints == -1)
|
||||
FreeFace (merged);
|
||||
else
|
||||
{
|
||||
merged->next = head;
|
||||
head = merged;
|
||||
}
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
MergePlaneFaces
|
||||
===============
|
||||
*/
|
||||
void MergePlaneFaces (surface_t *plane)
|
||||
{
|
||||
face_t *f1, *next;
|
||||
face_t *merged;
|
||||
|
||||
merged = NULL;
|
||||
|
||||
for (f1 = plane->faces ; f1 ; f1 = next)
|
||||
{
|
||||
next = f1->next;
|
||||
merged = MergeFaceToList (f1, merged);
|
||||
}
|
||||
|
||||
// chain all of the non-empty faces to the plane
|
||||
plane->faces = FreeMergeListScraps (merged);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
MergeAll
|
||||
============
|
||||
*/
|
||||
void MergeAll (surface_t *surfhead)
|
||||
{
|
||||
surface_t *surf;
|
||||
int mergefaces;
|
||||
face_t *f;
|
||||
|
||||
printf ("---- MergeAll ----\n");
|
||||
|
||||
mergefaces = 0;
|
||||
for (surf = surfhead ; surf ; surf=surf->next)
|
||||
{
|
||||
MergePlaneFaces (surf);
|
||||
Draw_ClearWindow ();
|
||||
for (f=surf->faces ; f ; f=f->next)
|
||||
{
|
||||
Draw_DrawFace (f);
|
||||
mergefaces++;
|
||||
}
|
||||
}
|
||||
|
||||
printf ("%i mergefaces\n", mergefaces);
|
||||
}
|
73
tools/qfbsp/source/nodraw.c
Normal file
73
tools/qfbsp/source/nodraw.c
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
void Draw_ClearBounds (void)
|
||||
{
|
||||
}
|
||||
|
||||
void Draw_AddToBounds (vec3_t v)
|
||||
{
|
||||
}
|
||||
|
||||
void Draw_DrawFace (face_t *f)
|
||||
{
|
||||
}
|
||||
|
||||
void Draw_ClearWindow (void)
|
||||
{
|
||||
}
|
||||
|
||||
void Draw_SetRed (void)
|
||||
{
|
||||
}
|
||||
|
||||
void Draw_SetGrey (void)
|
||||
{
|
||||
}
|
||||
|
||||
void Draw_SetBlack (void)
|
||||
{
|
||||
}
|
||||
|
||||
void DrawPoint (vec3_t v)
|
||||
{
|
||||
}
|
||||
|
||||
void DrawLeaf (node_t *l, int color)
|
||||
{
|
||||
}
|
||||
|
||||
void DrawBrush (brush_t *b)
|
||||
{
|
||||
}
|
||||
|
||||
void DrawWinding (winding_t *w)
|
||||
{
|
||||
}
|
||||
|
||||
void DrawTri (vec3_t p1, vec3_t p2, vec3_t p3)
|
||||
{
|
||||
}
|
||||
|
||||
void DrawPortal (portal_t *portal)
|
||||
{
|
||||
}
|
276
tools/qfbsp/source/outside.c
Normal file
276
tools/qfbsp/source/outside.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
int outleafs;
|
||||
|
||||
/*
|
||||
===========
|
||||
PointInLeaf
|
||||
===========
|
||||
*/
|
||||
node_t *PointInLeaf (node_t *node, vec3_t point)
|
||||
{
|
||||
vec_t d;
|
||||
|
||||
if (node->contents)
|
||||
return node;
|
||||
|
||||
d = DotProduct (planes[node->planenum].normal, point) - planes[node->planenum]. dist;
|
||||
|
||||
if (d > 0)
|
||||
return PointInLeaf (node->children[0], point);
|
||||
|
||||
return PointInLeaf (node->children[1], point);
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
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, j;
|
||||
vec3_t p1, p2, dir;
|
||||
float len;
|
||||
portal_t *n1;
|
||||
|
||||
if (hullnum)
|
||||
return;
|
||||
|
||||
n1 = prevleaknode;
|
||||
prevleaknode = n2;
|
||||
|
||||
if (!n1)
|
||||
return;
|
||||
|
||||
VectorCopy (n2->winding->points[0], p1);
|
||||
for (i=1 ; i< n2->winding->numpoints ; i++)
|
||||
{
|
||||
for (j=0 ; j<3 ; j++)
|
||||
p1[j] = (p1[j] + n2->winding->points[i][j]) / 2;
|
||||
}
|
||||
|
||||
VectorCopy (n1->winding->points[0], p2);
|
||||
for (i=1 ; i< n1->winding->numpoints ; i++)
|
||||
{
|
||||
for (j=0 ; j<3 ; j++)
|
||||
p2[j] = (p2[j] + n1->winding->points[i][j]) / 2;
|
||||
}
|
||||
|
||||
VectorSubtract (p2, p1, dir);
|
||||
len = VectorLength (dir);
|
||||
VectorNormalize (dir);
|
||||
|
||||
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;
|
||||
int backdraw;
|
||||
qboolean RecursiveFillOutside (node_t *l, 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)
|
||||
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], fill) )
|
||||
{ // leaked, so stop filling
|
||||
if (backdraw-- > 0)
|
||||
{
|
||||
MarkLeakTrail (p);
|
||||
DrawLeaf (l, 2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
p = p->next[!s];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
ClearOutFaces
|
||||
|
||||
==================
|
||||
*/
|
||||
void ClearOutFaces (node_t *node)
|
||||
{
|
||||
face_t **fp;
|
||||
|
||||
if (node->planenum != -1)
|
||||
{
|
||||
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
|
||||
(*fp)->numpoints = 0;
|
||||
}
|
||||
node->faces = NULL;
|
||||
}
|
||||
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
===========
|
||||
FillOutside
|
||||
|
||||
===========
|
||||
*/
|
||||
qboolean FillOutside (node_t *node)
|
||||
{
|
||||
int s;
|
||||
vec_t *v;
|
||||
int i;
|
||||
qboolean inside;
|
||||
|
||||
qprintf ("----- FillOutside ----\n");
|
||||
|
||||
if (nofill)
|
||||
{
|
||||
printf ("skipped\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
inside = false;
|
||||
for (i=1 ; i<num_entities ; i++)
|
||||
{
|
||||
if (!VectorCompare(entities[i].origin, vec3_origin))
|
||||
{
|
||||
if (PlaceOccupant (i, entities[i].origin, node))
|
||||
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 (!hullnum)
|
||||
{
|
||||
leakfile = fopen (pointfilename, "w");
|
||||
if (!leakfile)
|
||||
Sys_Error ("Couldn't open %s\n", pointfilename);
|
||||
}
|
||||
|
||||
if (RecursiveFillOutside (outside_node.portals->nodes[s], false))
|
||||
{
|
||||
v = entities[hit_occupied].origin;
|
||||
qprintf ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
qprintf ("reached occupant at: (%4.0f,%4.0f,%4.0f)\n"
|
||||
, v[0], v[1], v[2]);
|
||||
qprintf ("no filling performed\n");
|
||||
if (!hullnum)
|
||||
fclose (leakfile);
|
||||
qprintf ("leak file written to %s\n", pointfilename);
|
||||
qprintf ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
||||
return false;
|
||||
}
|
||||
if (!hullnum)
|
||||
fclose (leakfile);
|
||||
|
||||
// now go back and fill things in
|
||||
valid++;
|
||||
RecursiveFillOutside (outside_node.portals->nodes[s], true);
|
||||
|
||||
// remove faces from filled in leafs
|
||||
ClearOutFaces (node);
|
||||
|
||||
qprintf ("%4i outleafs\n", outleafs);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
600
tools/qfbsp/source/portals.c
Normal file
600
tools/qfbsp/source/portals.c
Normal file
|
@ -0,0 +1,600 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
|
||||
node_t outside_node; // portals outside the world face this
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
=============
|
||||
AddPortalToNodes
|
||||
=============
|
||||
*/
|
||||
void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
|
||||
{
|
||||
if (p->nodes[0] || p->nodes[1])
|
||||
Sys_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)
|
||||
Sys_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
|
||||
Sys_Error ("RemovePortalFromNode: portal not bounding leaf");
|
||||
}
|
||||
|
||||
if (portal->nodes[0] == l)
|
||||
{
|
||||
*pp = portal->next[0];
|
||||
portal->nodes[0] = NULL;
|
||||
}
|
||||
else if (portal->nodes[1] == l)
|
||||
{
|
||||
*pp = portal->next[1];
|
||||
portal->nodes[1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
void PrintPortal (portal_t *p)
|
||||
{
|
||||
int i;
|
||||
winding_t *w;
|
||||
|
||||
w = p->winding;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
printf ("(%5.0f,%5.0f,%5.0f)\n",w->points[i][0]
|
||||
, w->points[i][1], w->points[i][2]);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
MakeHeadnodePortals
|
||||
|
||||
The created portals will face the global outside_node
|
||||
================
|
||||
*/
|
||||
void MakeHeadnodePortals (node_t *node)
|
||||
{
|
||||
vec3_t bounds[2];
|
||||
int i, j, n;
|
||||
portal_t *p, *portals[6];
|
||||
plane_t bplanes[6], *pl;
|
||||
int side;
|
||||
|
||||
Draw_ClearWindow ();
|
||||
|
||||
// pad with some space so there will never be null volume leafs
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
bounds[0][i] = brushset->mins[i] - SIDESPACE;
|
||||
bounds[1][i] = brushset->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
void CheckWindingInNode (winding_t *w, node_t *node)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
for (j=0 ; j<3 ; j++)
|
||||
if (w->points[i][j] < node->mins[j] - 1
|
||||
|| w->points[i][j] > node->maxs[j] + 1)
|
||||
{
|
||||
printf ("WARNING: CheckWindingInNode: outside\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckWindingArea (winding_t *w)
|
||||
{
|
||||
int i;
|
||||
float total, add;
|
||||
vec3_t v1, v2, cross;
|
||||
|
||||
total = 0;
|
||||
for (i=1 ; i<w->numpoints ; i++)
|
||||
{
|
||||
VectorSubtract (w->points[i], w->points[0], v1);
|
||||
VectorSubtract (w->points[i+1], w->points[0], v2);
|
||||
CrossProduct (v1, v2, cross);
|
||||
add = VectorLength (cross);
|
||||
total += add*0.5;
|
||||
}
|
||||
if (total < 16)
|
||||
printf ("WARNING: winding area %f\n", total);
|
||||
}
|
||||
|
||||
|
||||
void PlaneFromWinding (winding_t *w, plane_t *plane)
|
||||
{
|
||||
vec3_t v1, v2;
|
||||
|
||||
// calc plane
|
||||
VectorSubtract (w->points[2], w->points[1], v1);
|
||||
VectorSubtract (w->points[0], w->points[1], v2);
|
||||
CrossProduct (v2, v1, plane->normal);
|
||||
VectorNormalize (plane->normal);
|
||||
plane->dist = DotProduct (w->points[0], plane->normal);
|
||||
}
|
||||
|
||||
void CheckLeafPortalConsistancy (node_t *node)
|
||||
{
|
||||
int side, side2;
|
||||
portal_t *p, *p2;
|
||||
plane_t plane, plane2;
|
||||
int i;
|
||||
winding_t *w;
|
||||
float dist;
|
||||
|
||||
side = side2 = 0; // quiet compiler warning
|
||||
|
||||
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
|
||||
Sys_Error ("CutNodePortals_r: mislinked portal");
|
||||
CheckWindingInNode (p->winding, node);
|
||||
CheckWindingArea (p->winding);
|
||||
|
||||
// check that the side orders are correct
|
||||
plane = planes[p->planenum];
|
||||
PlaneFromWinding (p->winding, &plane2);
|
||||
|
||||
for (p2 = node->portals ; p2 ; p2 = p2->next[side2])
|
||||
{
|
||||
if (p2->nodes[0] == node)
|
||||
side2 = 0;
|
||||
else if (p2->nodes[1] == node)
|
||||
side2 = 1;
|
||||
else
|
||||
Sys_Error ("CutNodePortals_r: mislinked portal");
|
||||
w = p2->winding;
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
dist = DotProduct (w->points[i], plane.normal) - plane.dist;
|
||||
if ( (side == 0 && dist < -1) || (side == 1 && dist > 1) )
|
||||
{
|
||||
printf ("WARNING: portal siding direction is wrong\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
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;
|
||||
|
||||
// CheckLeafPortalConsistancy (node);
|
||||
|
||||
//
|
||||
// seperate the portals on node into it's children
|
||||
//
|
||||
if (node->contents)
|
||||
{
|
||||
return; // at a leaf, no more dividing
|
||||
}
|
||||
|
||||
plane = &planes[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
|
||||
//
|
||||
new_portal = AllocPortal ();
|
||||
new_portal->planenum = node->planenum;
|
||||
|
||||
w = BaseWindingForPlane (&planes[node->planenum]);
|
||||
side = 0; // shut up compiler warning
|
||||
for (p = node->portals ; p ; p = p->next[side])
|
||||
{
|
||||
clipplane = planes[p->planenum];
|
||||
if (p->nodes[0] == node)
|
||||
side = 0;
|
||||
else if (p->nodes[1] == node)
|
||||
{
|
||||
clipplane.dist = -clipplane.dist;
|
||||
VectorSubtract (vec3_origin, clipplane.normal, clipplane.normal);
|
||||
side = 1;
|
||||
}
|
||||
else
|
||||
Sys_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->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
|
||||
Sys_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
|
||||
//
|
||||
DivideWinding (p->winding, plane, &frontwinding, &backwinding);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
DrawLeaf (f,1);
|
||||
DrawLeaf (b,2);
|
||||
|
||||
CutNodePortals_r (f);
|
||||
CutNodePortals_r (b);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
PortalizeWorld
|
||||
|
||||
Builds the exact polyhedrons for the nodes and leafs
|
||||
==================
|
||||
*/
|
||||
void PortalizeWorld (node_t *headnode)
|
||||
{
|
||||
qprintf ("----- portalize ----\n");
|
||||
|
||||
MakeHeadnodePortals (headnode);
|
||||
CutNodePortals_r (headnode);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
FreeAllPortals
|
||||
|
||||
==================
|
||||
*/
|
||||
void FreeAllPortals (node_t *node)
|
||||
{
|
||||
portal_t *p, *nextp;
|
||||
|
||||
if (!node->contents)
|
||||
{
|
||||
FreeAllPortals (node->children[0]);
|
||||
FreeAllPortals (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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
PORTAL FILE GENERATION
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#define PORTALFILE "PRT1"
|
||||
|
||||
FILE *pf;
|
||||
int num_visleafs; // leafs the player can be in
|
||||
int num_visportals;
|
||||
|
||||
void WriteFloat (FILE *f, vec_t v)
|
||||
{
|
||||
if ( fabs(v - (int) (v + 0.5)) < 0.001 )
|
||||
fprintf (f,"%i ",(int)(v + 0.5));
|
||||
else
|
||||
fprintf (f,"%f ",v);
|
||||
}
|
||||
|
||||
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;
|
||||
if (w && p->nodes[0] == node
|
||||
&& p->nodes[0]->contents == p->nodes[1]->contents)
|
||||
{
|
||||
// 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 = &planes[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);
|
||||
for (i=0 ; i<w->numpoints ; i++)
|
||||
{
|
||||
fprintf (pf,"(");
|
||||
WriteFloat (pf, w->points[i][0]);
|
||||
WriteFloat (pf, w->points[i][1]);
|
||||
WriteFloat (pf, w->points[i][2]);
|
||||
fprintf (pf,") ");
|
||||
}
|
||||
fprintf (pf,"\n");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Draw_ClearWindow ();
|
||||
DrawLeaf (node, 1);
|
||||
|
||||
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)
|
||||
num_visportals++;
|
||||
p = p->next[0];
|
||||
}
|
||||
else
|
||||
p = p->next[1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
WritePortalfile
|
||||
================
|
||||
*/
|
||||
void WritePortalfile (node_t *headnode)
|
||||
{
|
||||
// set the visleafnum field in every leaf and count the total number of portals
|
||||
num_visleafs = 0;
|
||||
num_visportals = 0;
|
||||
NumberLeafs_r (headnode);
|
||||
|
||||
// write the file
|
||||
printf ("writing %s\n", portfilename);
|
||||
pf = fopen (portfilename, "w");
|
||||
if (!pf)
|
||||
Sys_Error ("Error opening %s", portfilename);
|
||||
|
||||
fprintf (pf, "%s\n", PORTALFILE);
|
||||
fprintf (pf, "%i\n", num_visleafs);
|
||||
fprintf (pf, "%i\n", num_visportals);
|
||||
|
||||
WritePortalFile_r (headnode);
|
||||
|
||||
fclose (pf);
|
||||
}
|
||||
|
||||
|
1070
tools/qfbsp/source/qfbsp.c
Normal file
1070
tools/qfbsp/source/qfbsp.c
Normal file
File diff suppressed because it is too large
Load diff
536
tools/qfbsp/source/region.c
Normal file
536
tools/qfbsp/source/region.c
Normal file
|
@ -0,0 +1,536 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
// region.h
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
/*
|
||||
|
||||
input
|
||||
-----
|
||||
vertexes
|
||||
edges
|
||||
faces
|
||||
|
||||
output
|
||||
------
|
||||
smaller set of vertexes
|
||||
smaller set of edges
|
||||
regions
|
||||
? triangulated regions
|
||||
face to region mapping numbers
|
||||
|
||||
*/
|
||||
|
||||
#define MAX_EDGES_IN_REGION 32
|
||||
|
||||
int firstedge;
|
||||
|
||||
vec3_t region_mins, region_maxs;
|
||||
|
||||
void AddPointToRegion (vec3_t p)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
if (p[i] < region_mins[i])
|
||||
region_mins[i] = p[i];
|
||||
if (p[i] > region_maxs[i])
|
||||
region_maxs[i] = p[i];
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRegionSize (void)
|
||||
{
|
||||
region_mins[0] = region_mins[1] = region_mins[2] = 9999;
|
||||
region_maxs[0] = region_maxs[1] = region_maxs[2] = -9999;
|
||||
}
|
||||
|
||||
void AddFaceToRegionSize (face_t *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
AddPointToRegion (f->pts[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
CanJoinFaces
|
||||
==============
|
||||
*/
|
||||
qboolean CanJoinFaces (face_t *f, face_t *f2)
|
||||
{
|
||||
vec3_t oldmins, oldmaxs;
|
||||
int i;
|
||||
|
||||
if (f2->planenum != f->planenum
|
||||
|| f2->planeside != f->planeside
|
||||
|| f2->texturenum != f->texturenum)
|
||||
return false;
|
||||
if (f2->outputnumber != -1)
|
||||
return false;
|
||||
if (f2->contents[0] != f->contents[0])
|
||||
{ // does this ever happen? theyy shouldn't share.
|
||||
printf ("CanJoinFaces: edge with different contents");
|
||||
return false;
|
||||
}
|
||||
|
||||
// check size constraints
|
||||
if ( ! (bsp->texinfo[f->texturenum].flags & TEX_SPECIAL) )
|
||||
{
|
||||
VectorCopy (region_mins, oldmins);
|
||||
VectorCopy (region_maxs, oldmaxs);
|
||||
AddFaceToRegionSize (f2);
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
if (region_maxs[i] - region_mins[i] > 240)
|
||||
{
|
||||
VectorCopy (oldmins, region_mins);
|
||||
VectorCopy (oldmaxs, region_maxs);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bsp->numsurfedges - firstedge + f2->numpoints > MAX_EDGES_IN_REGION)
|
||||
return false; // a huge water or sky polygon
|
||||
}
|
||||
|
||||
// check edge count constraints
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
RecursiveGrowRegion
|
||||
==============
|
||||
*/
|
||||
void RecursiveGrowRegion (dface_t *r, face_t *f)
|
||||
{
|
||||
int e;
|
||||
face_t *f2;
|
||||
int i;
|
||||
|
||||
if (f->outputnumber == bsp->numfaces)
|
||||
return;
|
||||
|
||||
if (f->outputnumber != -1)
|
||||
Sys_Error ("RecursiveGrowRegion: region collision");
|
||||
f->outputnumber = bsp->numfaces;
|
||||
|
||||
// add edges
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
{
|
||||
e = f->edges[i];
|
||||
if (!edgefaces[abs(e)][0])
|
||||
continue; // edge has allready been removed
|
||||
if (e > 0)
|
||||
f2 = edgefaces[e][1];
|
||||
else
|
||||
f2 = edgefaces[-e][0];
|
||||
if (f2 && f2->outputnumber == bsp->numfaces)
|
||||
{
|
||||
edgefaces[abs(e)][0] = NULL;
|
||||
edgefaces[abs(e)][1] = NULL;
|
||||
continue; // allready merged
|
||||
}
|
||||
if (f2 && CanJoinFaces (f, f2))
|
||||
{ // remove the edge and merge the faces
|
||||
edgefaces[abs(e)][0] = NULL;
|
||||
edgefaces[abs(e)][1] = NULL;
|
||||
RecursiveGrowRegion (r, f2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// emit a surfedge
|
||||
if (bsp->numsurfedges == MAX_MAP_SURFEDGES)
|
||||
Sys_Error ("numsurfedges == MAX_MAP_SURFEDGES");
|
||||
bsp->surfedges[bsp->numsurfedges] = e;
|
||||
bsp->numsurfedges++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PrintDface (int f)
|
||||
{ // for debugging
|
||||
dface_t *df;
|
||||
dedge_t *e;
|
||||
int i, n;
|
||||
|
||||
df = &bsp->faces[f];
|
||||
for (i=0 ; i<df->numedges ; i++)
|
||||
{
|
||||
n = bsp->surfedges[df->firstedge+i];
|
||||
e = &bsp->edges[abs(n)];
|
||||
if (n < 0)
|
||||
printf ("%5i = %5i : %5i\n", n, e->v[1], e->v[0]);
|
||||
else
|
||||
printf ("%5i = %5i : %5i\n", n, e->v[0], e->v[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void FindVertexUse (int v)
|
||||
{ // for debugging
|
||||
int i, j, n;
|
||||
dface_t *df;
|
||||
dedge_t *e;
|
||||
|
||||
for (i=firstmodelface ; i<bsp->numfaces ; i++)
|
||||
{
|
||||
df = &bsp->faces[i];
|
||||
for (j=0 ; j<df->numedges ; j++)
|
||||
{
|
||||
n = bsp->surfedges[df->firstedge+j];
|
||||
e = &bsp->edges[abs(n)];
|
||||
if (e->v[0] == v || e->v[1] == v)
|
||||
{
|
||||
printf ("on face %i\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FindEdgeUse (int v)
|
||||
{ // for debugging
|
||||
int i, j, n;
|
||||
dface_t *df;
|
||||
|
||||
for (i=firstmodelface ; i<bsp->numfaces ; i++)
|
||||
{
|
||||
df = &bsp->faces[i];
|
||||
for (j=0 ; j<df->numedges ; j++)
|
||||
{
|
||||
n = bsp->surfedges[df->firstedge+j];
|
||||
if (n == v || -n == v)
|
||||
{
|
||||
printf ("on face %i\n", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
HealEdges
|
||||
|
||||
Extends e1 so that it goes all the way to e2, and removes all references
|
||||
to e2
|
||||
================
|
||||
*/
|
||||
int edgemapping[MAX_MAP_EDGES];
|
||||
void HealEdges (int e1, int e2)
|
||||
{
|
||||
int i, j, n, saved;
|
||||
dface_t *df;
|
||||
dedge_t *ed, *ed2;
|
||||
vec3_t v1, v2;
|
||||
dface_t *found[2];
|
||||
int foundj[2];
|
||||
|
||||
return;
|
||||
e1 = edgemapping[e1];
|
||||
e2 = edgemapping[e2];
|
||||
|
||||
// extend e1 to e2
|
||||
ed = &bsp->edges[e1];
|
||||
ed2 = &bsp->edges[e2];
|
||||
VectorSubtract (bsp->vertexes[ed->v[1]].point, bsp->vertexes[ed->v[0]].point, v1);
|
||||
VectorNormalize (v1);
|
||||
|
||||
if (ed->v[0] == ed2->v[0])
|
||||
ed->v[0] = ed2->v[1];
|
||||
else if (ed->v[0] == ed2->v[1])
|
||||
ed->v[0] = ed2->v[0];
|
||||
else if (ed->v[1] == ed2->v[0])
|
||||
ed->v[1] = ed2->v[1];
|
||||
else if (ed->v[1] == ed2->v[1])
|
||||
ed->v[1] = ed2->v[0];
|
||||
else
|
||||
Sys_Error ("HealEdges: edges don't meet");
|
||||
|
||||
VectorSubtract (bsp->vertexes[ed->v[1]].point, bsp->vertexes[ed->v[0]].point, v2);
|
||||
VectorNormalize (v2);
|
||||
|
||||
if (!VectorCompare (v1, v2))
|
||||
Sys_Error ("HealEdges: edges not colinear");
|
||||
|
||||
edgemapping[e2] = e1;
|
||||
saved = 0;
|
||||
|
||||
// remove all uses of e2
|
||||
for (i=firstmodelface ; i<bsp->numfaces ; i++)
|
||||
{
|
||||
df = &bsp->faces[i];
|
||||
for (j=0 ; j<df->numedges ; j++)
|
||||
{
|
||||
n = bsp->surfedges[df->firstedge+j];
|
||||
if (n == e2 || n == -e2)
|
||||
{
|
||||
found[saved] = df;
|
||||
foundj[saved] = j;
|
||||
saved++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (saved != 2)
|
||||
printf ("WARNING: didn't find both faces for a saved edge\n");
|
||||
else
|
||||
{
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{ // remove this edge
|
||||
df = found[i];
|
||||
j = foundj[i];
|
||||
for (j++ ; j<df->numedges ; j++)
|
||||
bsp->surfedges[df->firstedge+j-1] =
|
||||
bsp->surfedges[df->firstedge+j];
|
||||
bsp->surfedges[df->firstedge+j-1] = 0;
|
||||
df->numedges--;
|
||||
}
|
||||
|
||||
|
||||
edgefaces[e2][0] = edgefaces[e2][1] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int numedges;
|
||||
int edges[2];
|
||||
} checkpoint_t;
|
||||
|
||||
checkpoint_t checkpoints[MAX_MAP_VERTS];
|
||||
|
||||
/*
|
||||
==============
|
||||
RemoveColinearEdges
|
||||
==============
|
||||
*/
|
||||
void RemoveColinearEdges (void)
|
||||
{
|
||||
int i,j, v;
|
||||
int c0, c1, c2, c3;
|
||||
checkpoint_t *cp;
|
||||
|
||||
// no edges remapped yet
|
||||
for (i=0 ; i<bsp->numedges ; i++)
|
||||
edgemapping[i] = i;
|
||||
|
||||
// find vertexes that only have two edges
|
||||
memset (checkpoints, 0, sizeof(checkpoints));
|
||||
|
||||
for (i=firstmodeledge ; i<bsp->numedges ; i++)
|
||||
{
|
||||
if (!edgefaces[i][0])
|
||||
continue; // removed
|
||||
for (j=0 ; j<2 ; j++)
|
||||
{
|
||||
v = bsp->edges[i].v[j];
|
||||
cp = &checkpoints[v];
|
||||
if (cp->numedges<2)
|
||||
cp->edges[cp->numedges] = i;
|
||||
cp->numedges++;
|
||||
}
|
||||
}
|
||||
|
||||
// if a vertex only has two edges and they are colinear, it can be removed
|
||||
c0 = c1 = c2 = c3 = 0;
|
||||
|
||||
for (i=0 ; i<bsp->numvertexes ; i++)
|
||||
{
|
||||
cp = &checkpoints[i];
|
||||
switch (cp->numedges)
|
||||
{
|
||||
case 0:
|
||||
c0++;
|
||||
break;
|
||||
case 1:
|
||||
c1++;
|
||||
break;
|
||||
case 2:
|
||||
c2++;
|
||||
HealEdges (cp->edges[0], cp->edges[1]);
|
||||
break;
|
||||
default:
|
||||
c3++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// qprintf ("%5i c0\n", c0);
|
||||
// qprintf ("%5i c1\n", c1);
|
||||
// qprintf ("%5i c2\n", c2);
|
||||
// qprintf ("%5i c3+\n", c3);
|
||||
qprintf ("%5i deges removed by tjunction healing\n", c2);
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
CountRealNumbers
|
||||
==============
|
||||
*/
|
||||
void CountRealNumbers (void)
|
||||
{
|
||||
int i;
|
||||
int c;
|
||||
|
||||
qprintf ("%5i regions\n", bsp->numfaces-firstmodelface);
|
||||
|
||||
c = 0;
|
||||
for (i=firstmodelface ; i<bsp->numfaces ; i++)
|
||||
c += bsp->faces[i].numedges;
|
||||
qprintf ("%5i real marksurfaces\n", c);
|
||||
|
||||
c = 0;
|
||||
for (i=firstmodeledge ; i<bsp->numedges ; i++)
|
||||
if (edgefaces[i][0])
|
||||
c++; // not removed
|
||||
|
||||
qprintf ("%5i real edges\n", c);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
/*
|
||||
==============
|
||||
GrowNodeRegion_r
|
||||
==============
|
||||
*/
|
||||
void GrowNodeRegion_r (node_t *node)
|
||||
{
|
||||
dface_t *r;
|
||||
face_t *f;
|
||||
int i;
|
||||
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
|
||||
node->firstface = bsp->numfaces;
|
||||
|
||||
for (f=node->faces ; f ; f=f->next)
|
||||
{
|
||||
// if (f->outputnumber != -1)
|
||||
// continue; // allready grown into an earlier region
|
||||
|
||||
// emit a region
|
||||
|
||||
if (bsp->numfaces == MAX_MAP_FACES)
|
||||
Sys_Error ("MAX_MAP_FACES");
|
||||
f->outputnumber = bsp->numfaces;
|
||||
r = &bsp->faces[bsp->numfaces];
|
||||
|
||||
r->planenum = node->outputplanenum;
|
||||
r->side = f->planeside;
|
||||
r->texinfo = f->texturenum;
|
||||
for (i=0 ; i<MAXLIGHTMAPS ; i++)
|
||||
r->styles[i] = 255;
|
||||
r->lightofs = -1;
|
||||
|
||||
// add the face and mergable neighbors to it
|
||||
|
||||
#if 0
|
||||
ClearRegionSize ();
|
||||
AddFaceToRegionSize (f);
|
||||
RecursiveGrowRegion (r, f);
|
||||
#endif
|
||||
r->firstedge = firstedge = bsp->numsurfedges;
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
{
|
||||
if (bsp->numsurfedges == MAX_MAP_SURFEDGES)
|
||||
Sys_Error ("numsurfedges == MAX_MAP_SURFEDGES");
|
||||
bsp->surfedges[bsp->numsurfedges] = f->edges[i];
|
||||
bsp->numsurfedges++;
|
||||
}
|
||||
|
||||
r->numedges = bsp->numsurfedges - r->firstedge;
|
||||
|
||||
bsp->numfaces++;
|
||||
}
|
||||
|
||||
node->numfaces = bsp->numfaces - node->firstface;
|
||||
|
||||
GrowNodeRegion_r (node->children[0]);
|
||||
GrowNodeRegion_r (node->children[1]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
GrowNodeRegions
|
||||
==============
|
||||
*/
|
||||
void GrowNodeRegions (node_t *headnode)
|
||||
{
|
||||
qprintf ("---- GrowRegions ----\n");
|
||||
|
||||
GrowNodeRegion_r (headnode);
|
||||
|
||||
//RemoveColinearEdges ();
|
||||
CountRealNumbers ();
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
Turn the faces on a plane into optimal non-convex regions
|
||||
The edges may still be split later as a result of tjunctions
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t dir;
|
||||
vec3_t origin;
|
||||
vec3_t p[2];
|
||||
}
|
||||
for all faces
|
||||
for all edges
|
||||
for all edges so far
|
||||
if overlap
|
||||
split
|
||||
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
780
tools/qfbsp/source/solidbsp.c
Normal file
780
tools/qfbsp/source/solidbsp.c
Normal file
|
@ -0,0 +1,780 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
// solidbsp.c
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
int leaffaces;
|
||||
int nodefaces;
|
||||
int splitnodes;
|
||||
|
||||
int c_solid, c_empty, c_water;
|
||||
|
||||
qboolean usemidsplit;
|
||||
|
||||
//============================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
FaceSide
|
||||
|
||||
For BSP hueristic
|
||||
==================
|
||||
*/
|
||||
int FaceSide (face_t *in, plane_t *split)
|
||||
{
|
||||
int frontcount, backcount;
|
||||
vec_t dot;
|
||||
int i;
|
||||
vec_t *p;
|
||||
|
||||
|
||||
frontcount = backcount = 0;
|
||||
|
||||
// axial planes are fast
|
||||
if (split->type < 3)
|
||||
for (i=0, p = in->pts[0]+split->type ; i<in->numpoints ; i++, p+=3)
|
||||
{
|
||||
if (*p > split->dist + ON_EPSILON)
|
||||
{
|
||||
if (backcount)
|
||||
return SIDE_ON;
|
||||
frontcount = 1;
|
||||
}
|
||||
else if (*p < split->dist - ON_EPSILON)
|
||||
{
|
||||
if (frontcount)
|
||||
return SIDE_ON;
|
||||
backcount = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
// sloping planes take longer
|
||||
for (i=0, p = in->pts[0] ; i<in->numpoints ; i++, p+=3)
|
||||
{
|
||||
dot = DotProduct (p, split->normal);
|
||||
dot -= split->dist;
|
||||
if (dot > ON_EPSILON)
|
||||
{
|
||||
if (backcount)
|
||||
return SIDE_ON;
|
||||
frontcount = 1;
|
||||
}
|
||||
else if (dot < -ON_EPSILON)
|
||||
{
|
||||
if (frontcount)
|
||||
return SIDE_ON;
|
||||
backcount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!frontcount)
|
||||
return SIDE_BACK;
|
||||
if (!backcount)
|
||||
return SIDE_FRONT;
|
||||
|
||||
return SIDE_ON;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
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 = 6*8192*8192;
|
||||
bestsurface = NULL;
|
||||
|
||||
for (p=surfaces ; p ; p=p->next)
|
||||
{
|
||||
if (p->onnode)
|
||||
continue;
|
||||
|
||||
plane = &planes[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 * plane->normal[l];
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
if (j == l)
|
||||
{
|
||||
value += (maxs[l]-dist)*(maxs[l]-dist);
|
||||
value += (dist-mins[l])*(dist-mins[l]);
|
||||
}
|
||||
else
|
||||
value += 2*(maxs[j]-mins[j])*(maxs[j]-mins[j]);
|
||||
}
|
||||
|
||||
if (value > bestvalue)
|
||||
continue;
|
||||
|
||||
//
|
||||
// currently the best!
|
||||
//
|
||||
bestvalue = value;
|
||||
bestsurface = p;
|
||||
}
|
||||
|
||||
if (!bestsurface)
|
||||
{
|
||||
for (p=surfaces ; p ; p=p->next)
|
||||
if (!p->onnode)
|
||||
return p; // first valid surface
|
||||
Sys_Error ("ChooseMidPlaneFromList: no valid planes");
|
||||
}
|
||||
|
||||
return bestsurface;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
ChoosePlaneFromList
|
||||
|
||||
The real BSP hueristic
|
||||
==================
|
||||
*/
|
||||
surface_t *ChoosePlaneFromList (surface_t *surfaces, vec3_t mins, vec3_t maxs, qboolean usefloors)
|
||||
{
|
||||
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 = 99999;
|
||||
bestsurface = NULL;
|
||||
bestdistribution = 9e30;
|
||||
|
||||
for (p=surfaces ; p ; p=p->next)
|
||||
{
|
||||
if (p->onnode)
|
||||
continue;
|
||||
|
||||
plane = &planes[p->planenum];
|
||||
k = 0;
|
||||
|
||||
if (!usefloors && plane->normal[2] == 1)
|
||||
continue;
|
||||
|
||||
for (p2=surfaces ; p2 ; p2=p2->next)
|
||||
{
|
||||
if (p2 == p)
|
||||
continue;
|
||||
if (p2->onnode)
|
||||
continue;
|
||||
|
||||
for (f=p2->faces ; f ; f=f->next)
|
||||
{
|
||||
if (FaceSide (f, plane) == SIDE_ON)
|
||||
{
|
||||
k++;
|
||||
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;
|
||||
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
if (j == l)
|
||||
{
|
||||
dist = plane->dist * plane->normal[l];
|
||||
value += (maxs[l]-dist)*(maxs[l]-dist);
|
||||
value += (dist-mins[l])*(dist-mins[l]);
|
||||
}
|
||||
else
|
||||
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,j;
|
||||
vec3_t mins, maxs;
|
||||
surface_t *p, *bestsurface;
|
||||
|
||||
//
|
||||
// count onnode surfaces
|
||||
//
|
||||
i = 0;
|
||||
bestsurface = NULL;
|
||||
for (p=surfaces ; p ; p=p->next)
|
||||
if (!p->onnode)
|
||||
{
|
||||
i++;
|
||||
bestsurface = p;
|
||||
}
|
||||
|
||||
if (i==0)
|
||||
return NULL;
|
||||
|
||||
if (i==1)
|
||||
return bestsurface; // this is a final split
|
||||
|
||||
//
|
||||
// calculate a bounding box of the entire surfaceset
|
||||
//
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
mins[i] = 99999;
|
||||
maxs[i] = -99999;
|
||||
}
|
||||
|
||||
for (p=surfaces ; p ; p=p->next)
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
if (p->mins[j] < mins[j])
|
||||
mins[j] = p->mins[j];
|
||||
if (p->maxs[j] > maxs[j])
|
||||
maxs[j] = p->maxs[j];
|
||||
}
|
||||
|
||||
if (usemidsplit) // do fast way for clipping hull
|
||||
return ChooseMidPlaneFromList (surfaces, mins, maxs);
|
||||
|
||||
// do slow way to save poly splits for drawing hull
|
||||
#if 0
|
||||
bestsurface = ChoosePlaneFromList (surfaces, mins, maxs, false);
|
||||
if (bestsurface)
|
||||
return bestsurface;
|
||||
#endif
|
||||
return ChoosePlaneFromList (surfaces, mins, maxs, true);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
/*
|
||||
=================
|
||||
CalcSurfaceInfo
|
||||
|
||||
Calculates the bounding box
|
||||
=================
|
||||
*/
|
||||
void CalcSurfaceInfo (surface_t *surf)
|
||||
{
|
||||
int i,j;
|
||||
face_t *f;
|
||||
|
||||
if (!surf->faces)
|
||||
Sys_Error ("CalcSurfaceInfo: surface without a face");
|
||||
|
||||
//
|
||||
// calculate a bounding box
|
||||
//
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
surf->mins[i] = 99999;
|
||||
surf->maxs[i] = -99999;
|
||||
}
|
||||
|
||||
for (f=surf->faces ; f ; f=f->next)
|
||||
{
|
||||
if (f->contents[0] >= 0 || f->contents[1] >= 0)
|
||||
Sys_Error ("Bad contents");
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
for (j=0 ; j<3 ; j++)
|
||||
{
|
||||
if (f->pts[i][j] < surf->mins[j])
|
||||
surf->mins[j] = f->pts[i][j];
|
||||
if (f->pts[i][j] > surf->maxs[j])
|
||||
surf->maxs[j] = f->pts[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
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 = &planes[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 == 1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
DivideNodeBounds
|
||||
==================
|
||||
*/
|
||||
void DivideNodeBounds (node_t *node, plane_t *split)
|
||||
{
|
||||
VectorCopy (node->mins, node->children[0]->mins);
|
||||
VectorCopy (node->mins, node->children[1]->mins);
|
||||
VectorCopy (node->maxs, node->children[0]->maxs);
|
||||
VectorCopy (node->maxs, node->children[1]->maxs);
|
||||
|
||||
// OPTIMIZE: sloping cuts can give a better bbox than this...
|
||||
if (split->type > 2)
|
||||
return;
|
||||
|
||||
node->children[0]->mins[split->type] =
|
||||
node->children[1]->maxs[split->type] = split->dist;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
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)
|
||||
{
|
||||
face_t *f, *next;
|
||||
surface_t *surf, *pnext;
|
||||
int i, count;
|
||||
|
||||
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])
|
||||
Sys_Error ("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:
|
||||
case CONTENTS_SLIME:
|
||||
case CONTENTS_LAVA:
|
||||
case CONTENTS_SKY:
|
||||
c_water++;
|
||||
break;
|
||||
default:
|
||||
Sys_Error ("LinkConvexFaces: bad contents number");
|
||||
}
|
||||
|
||||
//
|
||||
// write the list of faces, and free the originals
|
||||
//
|
||||
leaffaces += count;
|
||||
leafnode->markfaces = malloc(sizeof(face_t *)*(count+1));
|
||||
i = 0;
|
||||
for ( surf = planelist ; surf ; surf = pnext)
|
||||
{
|
||||
pnext = surf->next;
|
||||
for (f = surf->faces ; f ; f=next)
|
||||
{
|
||||
next = f->next;
|
||||
leafnode->markfaces[i] = f->original;
|
||||
i++;
|
||||
FreeFace (f);
|
||||
}
|
||||
FreeSurface (surf);
|
||||
}
|
||||
leafnode->markfaces[i] = NULL; // sentinal
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
LinkNodeFaces
|
||||
|
||||
Returns a duplicated list of all faces on surface
|
||||
==================
|
||||
*/
|
||||
face_t *LinkNodeFaces (surface_t *surface)
|
||||
{
|
||||
face_t *f, *new, **prevptr;
|
||||
face_t *list;
|
||||
|
||||
list = NULL;
|
||||
|
||||
|
||||
// subdivide
|
||||
prevptr = &surface->faces;
|
||||
while (1)
|
||||
{
|
||||
f = *prevptr;
|
||||
if (!f)
|
||||
break;
|
||||
SubdivideFace (f, prevptr);
|
||||
f = *prevptr;
|
||||
prevptr = &f->next;
|
||||
}
|
||||
|
||||
// copy
|
||||
for (f=surface->faces ; f ; f=f->next)
|
||||
{
|
||||
nodefaces++;
|
||||
new = AllocFace ();
|
||||
*new = *f;
|
||||
f->original = new;
|
||||
new->next = list;
|
||||
list = new;
|
||||
}
|
||||
|
||||
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 = &planes[split->planenum];
|
||||
|
||||
DivideNodeBounds (node, splitplane);
|
||||
|
||||
|
||||
//
|
||||
// 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 && backfrag)
|
||||
{
|
||||
// the plane was split, which may expose oportunities to merge
|
||||
// adjacent faces into a single face
|
||||
// MergePlaneFaces (frontfrag);
|
||||
// MergePlaneFaces (backfrag);
|
||||
}
|
||||
|
||||
if (frontfrag)
|
||||
{
|
||||
if (!frontfrag->faces)
|
||||
Sys_Error ("surface with no faces");
|
||||
frontfrag->next = frontlist;
|
||||
frontlist = frontfrag;
|
||||
}
|
||||
if (backfrag)
|
||||
{
|
||||
if (!backfrag->faces)
|
||||
Sys_Error ("surface with no faces");
|
||||
backfrag->next = backlist;
|
||||
backlist = backfrag;
|
||||
}
|
||||
}
|
||||
|
||||
PartitionSurfaces (frontlist, node->children[0]);
|
||||
PartitionSurfaces (backlist, node->children[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
DrawSurface
|
||||
==================
|
||||
*/
|
||||
void DrawSurface (surface_t *surf)
|
||||
{
|
||||
face_t *f;
|
||||
|
||||
for (f=surf->faces ; f ; f=f->next)
|
||||
Draw_DrawFace (f);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
DrawSurfaceList
|
||||
==================
|
||||
*/
|
||||
void DrawSurfaceList (surface_t *surf)
|
||||
{
|
||||
Draw_ClearWindow ();
|
||||
while (surf)
|
||||
{
|
||||
DrawSurface (surf);
|
||||
surf = surf->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SolidBSP
|
||||
==================
|
||||
*/
|
||||
node_t *SolidBSP (surface_t *surfhead, 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] = brushset->mins[i] - SIDESPACE;
|
||||
headnode->maxs[i] = brushset->maxs[i] + SIDESPACE;
|
||||
}
|
||||
|
||||
//
|
||||
// recursively partition everything
|
||||
//
|
||||
Draw_ClearWindow ();
|
||||
splitnodes = 0;
|
||||
leaffaces = 0;
|
||||
nodefaces = 0;
|
||||
c_solid = c_empty = c_water = 0;
|
||||
|
||||
PartitionSurfaces (surfhead, 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 leaffaces\n",leaffaces);
|
||||
qprintf ("%5i nodefaces\n", nodefaces);
|
||||
|
||||
return headnode;
|
||||
}
|
||||
|
538
tools/qfbsp/source/surfaces.c
Normal file
538
tools/qfbsp/source/surfaces.c
Normal file
|
@ -0,0 +1,538 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
// divide.h
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
|
||||
surface_t newcopy_t;
|
||||
|
||||
/*
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
int subdivides;
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
SubdivideFace
|
||||
|
||||
If the face is >256 in either texture direction, carve a valid sized
|
||||
piece off and insert the remainder in the next link
|
||||
===============
|
||||
*/
|
||||
void SubdivideFace (face_t *f, face_t **prevptr)
|
||||
{
|
||||
float mins, maxs;
|
||||
vec_t v;
|
||||
int axis, i;
|
||||
plane_t plane;
|
||||
face_t *front, *back, *next;
|
||||
texinfo_t *tex;
|
||||
|
||||
// special (non-surface cached) faces don't need subdivision
|
||||
tex = &bsp->texinfo[f->texturenum];
|
||||
|
||||
if ( tex->flags & TEX_SPECIAL)
|
||||
return;
|
||||
|
||||
|
||||
for (axis = 0 ; axis < 2 ; axis++)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
mins = 9999;
|
||||
maxs = -9999;
|
||||
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
{
|
||||
v = DotProduct (f->pts[i], tex->vecs[axis]);
|
||||
if (v < mins)
|
||||
mins = v;
|
||||
if (v > maxs)
|
||||
maxs = v;
|
||||
}
|
||||
|
||||
if (maxs - mins <= subdivide_size)
|
||||
break;
|
||||
|
||||
// split it
|
||||
subdivides++;
|
||||
|
||||
VectorCopy (tex->vecs[axis], plane.normal);
|
||||
v = VectorLength (plane.normal);
|
||||
VectorNormalize (plane.normal);
|
||||
plane.dist = (mins + subdivide_size - 16)/v;
|
||||
next = f->next;
|
||||
SplitFace (f, &plane, &front, &back);
|
||||
if (!front || !back)
|
||||
Sys_Error ("SubdivideFace: didn't split the polygon");
|
||||
*prevptr = back;
|
||||
back->next = front;
|
||||
front->next = next;
|
||||
f = back;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
SubdivideFaces
|
||||
================
|
||||
*/
|
||||
void SubdivideFaces (surface_t *surfhead)
|
||||
{
|
||||
surface_t *surf;
|
||||
face_t *f , **prevptr;
|
||||
|
||||
qprintf ("--- SubdivideFaces ---\n");
|
||||
|
||||
subdivides = 0;
|
||||
|
||||
for (surf = surfhead ; surf ; surf=surf->next)
|
||||
{
|
||||
prevptr = &surf->faces;
|
||||
while (1)
|
||||
{
|
||||
f = *prevptr;
|
||||
if (!f)
|
||||
break;
|
||||
SubdivideFace (f, prevptr);
|
||||
f = *prevptr;
|
||||
prevptr = &f->next;
|
||||
}
|
||||
}
|
||||
|
||||
qprintf ("%i faces added by subdivision\n", subdivides);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
GatherNodeFaces
|
||||
|
||||
Frees the current node tree and returns a new chain of the surfaces that
|
||||
have inside faces.
|
||||
=============================================================================
|
||||
*/
|
||||
|
||||
void GatherNodeFaces_r (node_t *node)
|
||||
{
|
||||
face_t *f, *next;
|
||||
|
||||
if (node->planenum != PLANENUM_LEAF)
|
||||
{
|
||||
//
|
||||
// decision node
|
||||
//
|
||||
for (f=node->faces ; f ; f=next)
|
||||
{
|
||||
next = f->next;
|
||||
if (!f->numpoints)
|
||||
{ // face was removed outside
|
||||
FreeFace (f);
|
||||
}
|
||||
else
|
||||
{
|
||||
f->next = validfaces[f->planenum];
|
||||
validfaces[f->planenum] = f;
|
||||
}
|
||||
}
|
||||
|
||||
GatherNodeFaces_r (node->children[0]);
|
||||
GatherNodeFaces_r (node->children[1]);
|
||||
|
||||
free (node);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// leaf node
|
||||
//
|
||||
free (node);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
GatherNodeFaces
|
||||
|
||||
================
|
||||
*/
|
||||
surface_t *GatherNodeFaces (node_t *headnode)
|
||||
{
|
||||
memset (validfaces, 0, sizeof(validfaces));
|
||||
GatherNodeFaces_r (headnode);
|
||||
return BuildSurfaces ();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
typedef struct hashvert_s
|
||||
{
|
||||
struct hashvert_s *next;
|
||||
vec3_t point;
|
||||
int num;
|
||||
int numplanes; // for corner determination
|
||||
int planenums[2];
|
||||
int numedges;
|
||||
} hashvert_t;
|
||||
|
||||
#define POINT_EPSILON 0.01
|
||||
|
||||
int c_cornerverts;
|
||||
|
||||
hashvert_t hvertex[MAX_MAP_VERTS];
|
||||
hashvert_t *hvert_p;
|
||||
|
||||
face_t *edgefaces[MAX_MAP_EDGES][2];
|
||||
int firstmodeledge = 1;
|
||||
int firstmodelface;
|
||||
|
||||
//============================================================================
|
||||
|
||||
#define NUM_HASH 4096
|
||||
|
||||
hashvert_t *hashverts[NUM_HASH];
|
||||
|
||||
static vec3_t hash_min, hash_scale;
|
||||
|
||||
static void InitHash (void)
|
||||
{
|
||||
vec3_t size;
|
||||
vec_t volume;
|
||||
vec_t scale;
|
||||
int newsize[2];
|
||||
int i;
|
||||
|
||||
memset (hashverts, 0, sizeof(hashverts));
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
hash_min[i] = -8000;
|
||||
size[i] = 16000;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
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]);
|
||||
if ( h >= NUM_HASH)
|
||||
return NUM_HASH - 1;
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=============
|
||||
GetVertex
|
||||
=============
|
||||
*/
|
||||
int GetVertex (vec3_t in, int planenum)
|
||||
{
|
||||
int h;
|
||||
int i;
|
||||
hashvert_t *hv;
|
||||
vec3_t vert;
|
||||
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
if ( fabs(in[i] - (int) (in[i] + 0.5)) < 0.001)
|
||||
vert[i] = (int) (in[i] + 0.5);
|
||||
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)
|
||||
c_cornerverts++;
|
||||
else
|
||||
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];
|
||||
hashverts[h] = hv;
|
||||
VectorCopy (vert, hv->point);
|
||||
hv->num = bsp->numvertexes;
|
||||
if (hv->num==MAX_MAP_VERTS)
|
||||
Sys_Error ("GetVertex: MAX_MAP_VERTS");
|
||||
hvert_p++;
|
||||
|
||||
// emit a vertex
|
||||
if (bsp->numvertexes == MAX_MAP_VERTS)
|
||||
Sys_Error ("numvertexes == MAX_MAP_VERTS");
|
||||
|
||||
bsp->vertexes[bsp->numvertexes].point[0] = vert[0];
|
||||
bsp->vertexes[bsp->numvertexes].point[1] = vert[1];
|
||||
bsp->vertexes[bsp->numvertexes].point[2] = vert[2];
|
||||
bsp->numvertexes++;
|
||||
|
||||
return hv->num;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
GetEdge
|
||||
|
||||
Don't allow four way edges
|
||||
==================
|
||||
*/
|
||||
int c_tryedges;
|
||||
|
||||
int GetEdge (vec3_t p1, vec3_t p2, face_t *f)
|
||||
{
|
||||
int v1, v2;
|
||||
dedge_t *edge;
|
||||
int i;
|
||||
|
||||
if (!f->contents[0])
|
||||
Sys_Error ("GetEdge: 0 contents");
|
||||
|
||||
c_tryedges++;
|
||||
v1 = GetVertex (p1, f->planenum);
|
||||
v2 = GetVertex (p2, f->planenum);
|
||||
for (i=firstmodeledge ; i < bsp->numedges ; i++)
|
||||
{
|
||||
edge = &bsp->edges[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;
|
||||
}
|
||||
}
|
||||
|
||||
// emit an edge
|
||||
if (bsp->numedges == MAX_MAP_EDGES)
|
||||
Sys_Error ("numedges == MAX_MAP_EDGES");
|
||||
edge = &bsp->edges[bsp->numedges];
|
||||
bsp->numedges++;
|
||||
edge->v[0] = v1;
|
||||
edge->v[1] = v2;
|
||||
edgefaces[i][0] = f;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
FindFaceEdges
|
||||
==================
|
||||
*/
|
||||
void FindFaceEdges (face_t *face)
|
||||
{
|
||||
int i;
|
||||
|
||||
face->outputnumber = -1;
|
||||
if (face->numpoints > MAXEDGES)
|
||||
Sys_Error ("WriteFace: %i points", face->numpoints);
|
||||
|
||||
for (i=0; i<face->numpoints ; i++)
|
||||
face->edges[i] = GetEdge
|
||||
(face->pts[i], face->pts[(i+1)%face->numpoints], face);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
CheckVertexes
|
||||
// debugging
|
||||
=============
|
||||
*/
|
||||
void CheckVertexes (void)
|
||||
{
|
||||
int cb, c0, c1, c2, c3;
|
||||
hashvert_t *hv;
|
||||
|
||||
cb = c0 = c1 = c2 = c3 = 0;
|
||||
for (hv=hvertex ; hv!=hvert_p ; hv++)
|
||||
{
|
||||
if (hv->numedges < 0 || hv->numedges & 1)
|
||||
cb++;
|
||||
else if (!hv->numedges)
|
||||
c0++;
|
||||
else if (hv->numedges == 2)
|
||||
c1++;
|
||||
else if (hv->numedges == 4)
|
||||
c2++;
|
||||
else
|
||||
c3++;
|
||||
}
|
||||
|
||||
qprintf ("%5i bad edge points\n", cb);
|
||||
qprintf ("%5i 0 edge points\n", c0);
|
||||
qprintf ("%5i 2 edge points\n", c1);
|
||||
qprintf ("%5i 4 edge points\n", c2);
|
||||
qprintf ("%5i 6+ edge points\n", c3);
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
CheckEdges
|
||||
// debugging
|
||||
=============
|
||||
*/
|
||||
void CheckEdges (void)
|
||||
{
|
||||
dedge_t *edge;
|
||||
int i;
|
||||
dvertex_t *d1, *d2;
|
||||
face_t *f1, *f2;
|
||||
int c_nonconvex;
|
||||
int c_multitexture;
|
||||
|
||||
c_nonconvex = c_multitexture = 0;
|
||||
|
||||
// CheckVertexes ();
|
||||
|
||||
for (i=1 ; i < bsp->numedges ; i++)
|
||||
{
|
||||
edge = &bsp->edges[i];
|
||||
if (!edgefaces[i][1])
|
||||
{
|
||||
d1 = &bsp->vertexes[edge->v[0]];
|
||||
d2 = &bsp->vertexes[edge->v[1]];
|
||||
qprintf ("unshared edge at: (%8.2f, %8.2f, %8.2f) (%8.2f, %8.2f, %8.2f)\n",d1->point[0], d1->point[1], d1->point[2], d2->point[0], d2->point[1], d2->point[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
f1 = edgefaces[i][0];
|
||||
f2 = edgefaces[i][1];
|
||||
if (f1->planeside != f2->planeside)
|
||||
continue;
|
||||
if (f1->planenum != f2->planenum)
|
||||
continue;
|
||||
|
||||
// on the same plane, might be discardable
|
||||
if (f1->texturenum == f2->texturenum)
|
||||
{
|
||||
hvertex[edge->v[0]].numedges-=2;
|
||||
hvertex[edge->v[1]].numedges-=2;
|
||||
c_nonconvex++;
|
||||
}
|
||||
else
|
||||
c_multitexture++;
|
||||
}
|
||||
}
|
||||
|
||||
// qprintf ("%5i edges\n", i);
|
||||
// qprintf ("%5i c_nonconvex\n", c_nonconvex);
|
||||
// qprintf ("%5i c_multitexture\n", c_multitexture);
|
||||
|
||||
// CheckVertexes ();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
MakeFaceEdges_r
|
||||
================
|
||||
*/
|
||||
void MakeFaceEdges_r (node_t *node)
|
||||
{
|
||||
face_t *f;
|
||||
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
|
||||
for (f=node->faces ; f ; f=f->next)
|
||||
FindFaceEdges (f);
|
||||
|
||||
MakeFaceEdges_r (node->children[0]);
|
||||
MakeFaceEdges_r (node->children[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
MakeFaceEdges
|
||||
================
|
||||
*/
|
||||
void MakeFaceEdges (node_t *headnode)
|
||||
{
|
||||
qprintf ("----- MakeFaceEdges -----\n");
|
||||
|
||||
InitHash ();
|
||||
c_tryedges = 0;
|
||||
c_cornerverts = 0;
|
||||
|
||||
MakeFaceEdges_r (headnode);
|
||||
|
||||
// CheckEdges ();
|
||||
|
||||
GrowNodeRegions (headnode);
|
||||
|
||||
firstmodeledge = bsp->numedges;
|
||||
firstmodelface = bsp->numfaces;
|
||||
}
|
||||
|
531
tools/qfbsp/source/tjunc.c
Normal file
531
tools/qfbsp/source/tjunc.c
Normal file
|
@ -0,0 +1,531 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
// tjunc.c
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "QF/sys.h"
|
||||
|
||||
#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 tjuncs;
|
||||
int tjuncfaces;
|
||||
|
||||
#define MAXWVERTS 0x20000
|
||||
#define MAXWEDGES 0x10000
|
||||
|
||||
|
||||
wvert_t wverts[MAXWVERTS];
|
||||
wedge_t wedges[MAXWEDGES];
|
||||
|
||||
|
||||
void PrintFace (face_t *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i<f->numpoints ; i++)
|
||||
printf ("(%5.2f, %5.2f, %5.2f)\n", f->pts[i][0], f->pts[i][1], f->pts[i][2]);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
void CanonicalVector (vec3_t vec)
|
||||
{
|
||||
VectorNormalize (vec);
|
||||
if (vec[0] > EQUAL_EPSILON)
|
||||
return;
|
||||
else if (vec[0] < -EQUAL_EPSILON)
|
||||
{
|
||||
VectorSubtract (vec3_origin, vec, vec);
|
||||
return;
|
||||
}
|
||||
else
|
||||
vec[0] = 0;
|
||||
|
||||
if (vec[1] > EQUAL_EPSILON)
|
||||
return;
|
||||
else if (vec[1] < -EQUAL_EPSILON)
|
||||
{
|
||||
VectorSubtract (vec3_origin, vec, vec);
|
||||
return;
|
||||
}
|
||||
else
|
||||
vec[1] = 0;
|
||||
|
||||
if (vec[2] > EQUAL_EPSILON)
|
||||
return;
|
||||
else if (vec[2] < -EQUAL_EPSILON)
|
||||
{
|
||||
VectorSubtract (vec3_origin, vec, vec);
|
||||
return;
|
||||
}
|
||||
else
|
||||
vec[2] = 0;
|
||||
Sys_Error ("CanonicalVector: degenerate");
|
||||
}
|
||||
|
||||
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);
|
||||
CanonicalVector (dir);
|
||||
|
||||
*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 == MAXWEDGES)
|
||||
Sys_Error ("FindEdge: numwedges == MAXWEDGES");
|
||||
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 == MAXWVERTS)
|
||||
Sys_Error ("AddVert: numwverts == MAXWVERTS");
|
||||
|
||||
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);
|
||||
AddVert (w, t1);
|
||||
AddVert (w, t2);
|
||||
}
|
||||
|
||||
/*
|
||||
===============
|
||||
AddFaceEdges
|
||||
|
||||
===============
|
||||
*/
|
||||
void AddFaceEdges (face_t *f)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i=0 ; i < f->numpoints ; i++)
|
||||
{
|
||||
j = (i+1)%f->numpoints;
|
||||
AddEdge (f->pts[i], f->pts[j]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
// a specially allocated face that can hold hundreds of edges if needed
|
||||
byte superfacebuf[8192];
|
||||
face_t *superface = (face_t *)superfacebuf;
|
||||
|
||||
void FixFaceEdges (face_t *f);
|
||||
|
||||
face_t *newlist;
|
||||
|
||||
void SplitFaceForTjunc (face_t *f, face_t *original)
|
||||
{
|
||||
int i;
|
||||
face_t *new, *chain;
|
||||
vec3_t dir, test;
|
||||
vec_t v;
|
||||
int firstcorner, lastcorner;
|
||||
|
||||
chain = NULL;
|
||||
do
|
||||
{
|
||||
if (f->numpoints <= MAXPOINTS)
|
||||
{ // the face is now small enough without more cutting
|
||||
// so copy it back to the original
|
||||
*original = *f;
|
||||
original->original = chain;
|
||||
original->next = newlist;
|
||||
newlist = original;
|
||||
return;
|
||||
}
|
||||
|
||||
tjuncfaces++;
|
||||
|
||||
restart:
|
||||
// find the last corner
|
||||
VectorSubtract (f->pts[f->numpoints-1], f->pts[0], dir);
|
||||
VectorNormalize (dir);
|
||||
for (lastcorner=f->numpoints-1 ; lastcorner > 0 ; lastcorner--)
|
||||
{
|
||||
VectorSubtract (f->pts[lastcorner-1], f->pts[lastcorner], test);
|
||||
VectorNormalize (test);
|
||||
v = DotProduct (test, dir);
|
||||
if (v < 0.9999 || v > 1.00001)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// find the first corner
|
||||
VectorSubtract (f->pts[1], f->pts[0], dir);
|
||||
VectorNormalize (dir);
|
||||
for (firstcorner=1 ; firstcorner < f->numpoints-1 ; firstcorner++)
|
||||
{
|
||||
VectorSubtract (f->pts[firstcorner+1], f->pts[firstcorner], test);
|
||||
VectorNormalize (test);
|
||||
v = DotProduct (test, dir);
|
||||
if (v < 0.9999 || v > 1.00001)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstcorner+2 >= MAXPOINTS)
|
||||
{
|
||||
// rotate the point winding
|
||||
VectorCopy (f->pts[0], test);
|
||||
for (i=1 ; i<f->numpoints ; i++)
|
||||
{
|
||||
VectorCopy (f->pts[i], f->pts[i-1]);
|
||||
}
|
||||
VectorCopy (test, f->pts[f->numpoints-1]);
|
||||
goto restart;
|
||||
}
|
||||
|
||||
|
||||
// cut off as big a piece as possible, less than MAXPOINTS, and not
|
||||
// past lastcorner
|
||||
|
||||
new = NewFaceFromFace (f);
|
||||
if (f->original)
|
||||
Sys_Error ("SplitFaceForTjunc: f->original");
|
||||
|
||||
new->original = chain;
|
||||
chain = new;
|
||||
new->next = newlist;
|
||||
newlist = new;
|
||||
if (f->numpoints - firstcorner <= MAXPOINTS)
|
||||
new->numpoints = firstcorner+2;
|
||||
else if (lastcorner+2 < MAXPOINTS &&
|
||||
f->numpoints - lastcorner <= MAXPOINTS)
|
||||
new->numpoints = lastcorner+2;
|
||||
else
|
||||
new->numpoints = MAXPOINTS;
|
||||
|
||||
for (i=0 ; i<new->numpoints ; i++)
|
||||
{
|
||||
VectorCopy (f->pts[i], new->pts[i]);
|
||||
}
|
||||
|
||||
|
||||
for (i=new->numpoints-1 ; i<f->numpoints ; i++)
|
||||
{
|
||||
VectorCopy (f->pts[i], f->pts[i-(new->numpoints-2)]);
|
||||
}
|
||||
f->numpoints -= (new->numpoints-2);
|
||||
} while (1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===============
|
||||
FixFaceEdges
|
||||
|
||||
===============
|
||||
*/
|
||||
void FixFaceEdges (face_t *f)
|
||||
{
|
||||
int i, j, k;
|
||||
wedge_t *w;
|
||||
wvert_t *v;
|
||||
vec_t t1, t2;
|
||||
|
||||
*superface = *f;
|
||||
|
||||
restart:
|
||||
for (i=0 ; i < superface->numpoints ; i++)
|
||||
{
|
||||
j = (i+1)%superface->numpoints;
|
||||
|
||||
w = FindEdge (superface->pts[i], superface->pts[j], &t1, &t2);
|
||||
|
||||
for (v=w->head.next ; v->t < t1 + T_EPSILON ; v = v->next)
|
||||
{
|
||||
}
|
||||
|
||||
if (v->t < t2-T_EPSILON)
|
||||
{
|
||||
tjuncs++;
|
||||
// insert a new vertex here
|
||||
for (k = superface->numpoints ; k> j ; k--)
|
||||
{
|
||||
VectorCopy (superface->pts[k-1], superface->pts[k]);
|
||||
}
|
||||
VectorMA (w->origin, v->t, w->dir, superface->pts[j]);
|
||||
superface->numpoints++;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (superface->numpoints <= MAXPOINTS)
|
||||
{
|
||||
*f = *superface;
|
||||
f->next = newlist;
|
||||
newlist = f;
|
||||
return;
|
||||
}
|
||||
|
||||
// the face needs to be split into multiple faces because of too many edges
|
||||
|
||||
SplitFaceForTjunc (superface, f);
|
||||
|
||||
}
|
||||
|
||||
|
||||
//============================================================================
|
||||
|
||||
void tjunc_find_r (node_t *node)
|
||||
{
|
||||
face_t *f;
|
||||
|
||||
if (node->planenum == PLANENUM_LEAF)
|
||||
return;
|
||||
|
||||
for (f=node->faces ; f ; f=f->next)
|
||||
AddFaceEdges (f);
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
tjunc
|
||||
|
||||
===========
|
||||
*/
|
||||
void tjunc (node_t *headnode)
|
||||
{
|
||||
vec3_t maxs, mins;
|
||||
int i;
|
||||
|
||||
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
|
||||
for (i=0 ; i<3 ; i++)
|
||||
{
|
||||
if ( fabs(brushset->maxs[i]) > fabs(brushset->mins[i]) )
|
||||
maxs[i] = fabs(brushset->maxs[i]);
|
||||
else
|
||||
maxs[i] = fabs(brushset->mins[i]);
|
||||
}
|
||||
VectorSubtract (vec3_origin, maxs, mins);
|
||||
|
||||
InitHash (mins, maxs);
|
||||
|
||||
numwedges = numwverts = 0;
|
||||
|
||||
tjunc_find_r (headnode);
|
||||
|
||||
qprintf ("%i world edges %i edge points\n", numwedges, numwverts);
|
||||
|
||||
//
|
||||
// add extra vertexes on edges where needed
|
||||
//
|
||||
tjuncs = tjuncfaces = 0;
|
||||
|
||||
tjunc_fix_r (headnode);
|
||||
|
||||
qprintf ("%i edges added by tjunctions\n", tjuncs);
|
||||
qprintf ("%i faces added by tjunctions\n", tjuncfaces);
|
||||
}
|
559
tools/qfbsp/source/writebsp.c
Normal file
559
tools/qfbsp/source/writebsp.c
Normal file
|
@ -0,0 +1,559 @@
|
|||
/* Copyright (C) 1996-1997 Id Software, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See file, 'COPYING', for details.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "QF/qendian.h"
|
||||
#include "QF/sys.h"
|
||||
|
||||
#include "bsp5.h"
|
||||
|
||||
|
||||
int headclipnode;
|
||||
int firstface;
|
||||
|
||||
//===========================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
FindFinalPlane
|
||||
|
||||
Used to find plane index numbers for clip nodes read from child processes
|
||||
==================
|
||||
*/
|
||||
int FindFinalPlane (dplane_t *p)
|
||||
{
|
||||
int i;
|
||||
dplane_t *dplane;
|
||||
|
||||
for (i=0, dplane = bsp->planes ; i<bsp->numplanes ; i++, dplane++)
|
||||
{
|
||||
if (p->type != dplane->type)
|
||||
continue;
|
||||
if (p->dist != dplane->dist)
|
||||
continue;
|
||||
if (p->normal[0] != dplane->normal[0])
|
||||
continue;
|
||||
if (p->normal[1] != dplane->normal[1])
|
||||
continue;
|
||||
if (p->normal[2] != dplane->normal[2])
|
||||
continue;
|
||||
return i;
|
||||
}
|
||||
|
||||
//
|
||||
// new plane
|
||||
//
|
||||
if (bsp->numplanes == MAX_MAP_PLANES)
|
||||
Sys_Error ("numplanes == MAX_MAP_PLANES");
|
||||
dplane = &bsp->planes[bsp->numplanes];
|
||||
*dplane = *p;
|
||||
bsp->numplanes++;
|
||||
|
||||
return bsp->numplanes - 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int planemapping[MAX_MAP_PLANES];
|
||||
|
||||
void WriteNodePlanes_r (node_t *node)
|
||||
{
|
||||
plane_t *plane;
|
||||
dplane_t *dplane;
|
||||
|
||||
if (node->planenum == -1)
|
||||
return;
|
||||
if (planemapping[node->planenum] == -1)
|
||||
{ // a new plane
|
||||
planemapping[node->planenum] = bsp->numplanes;
|
||||
|
||||
if (bsp->numplanes == MAX_MAP_PLANES)
|
||||
Sys_Error ("numplanes == MAX_MAP_PLANES");
|
||||
plane = &planes[node->planenum];
|
||||
dplane = &bsp->planes[bsp->numplanes];
|
||||
dplane->normal[0] = plane->normal[0];
|
||||
dplane->normal[1] = plane->normal[1];
|
||||
dplane->normal[2] = plane->normal[2];
|
||||
dplane->dist = plane->dist;
|
||||
dplane->type = plane->type;
|
||||
|
||||
bsp->numplanes++;
|
||||
}
|
||||
|
||||
node->outputplanenum = planemapping[node->planenum];
|
||||
|
||||
WriteNodePlanes_r (node->children[0]);
|
||||
WriteNodePlanes_r (node->children[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteNodePlanes
|
||||
|
||||
==================
|
||||
*/
|
||||
void WriteNodePlanes (node_t *nodes)
|
||||
{
|
||||
memset (planemapping,-1, sizeof(planemapping));
|
||||
WriteNodePlanes_r (nodes);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteClipNodes_r
|
||||
|
||||
==================
|
||||
*/
|
||||
int WriteClipNodes_r (node_t *node)
|
||||
{
|
||||
int i, c;
|
||||
dclipnode_t *cn;
|
||||
int num;
|
||||
|
||||
// FIXME: free more stuff?
|
||||
if (node->planenum == -1)
|
||||
{
|
||||
num = node->contents;
|
||||
free (node);
|
||||
return num;
|
||||
}
|
||||
|
||||
// emit a clipnode
|
||||
c = bsp->numclipnodes;
|
||||
cn = &bsp->clipnodes[bsp->numclipnodes];
|
||||
bsp->numclipnodes++;
|
||||
cn->planenum = node->outputplanenum;
|
||||
for (i=0 ; i<2 ; i++)
|
||||
cn->children[i] = WriteClipNodes_r(node->children[i]);
|
||||
|
||||
free (node);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteClipNodes
|
||||
|
||||
Called after the clipping hull is completed. Generates a disk format
|
||||
representation and frees the original memory.
|
||||
==================
|
||||
*/
|
||||
void WriteClipNodes (node_t *nodes)
|
||||
{
|
||||
headclipnode = bsp->numclipnodes;
|
||||
WriteClipNodes_r (nodes);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteLeaf
|
||||
==================
|
||||
*/
|
||||
void WriteLeaf (node_t *node)
|
||||
{
|
||||
face_t **fp, *f;
|
||||
dleaf_t *leaf_p;
|
||||
|
||||
// emit a leaf
|
||||
leaf_p = &bsp->leafs[bsp->numleafs];
|
||||
bsp->numleafs++;
|
||||
|
||||
leaf_p->contents = node->contents;
|
||||
|
||||
//
|
||||
// write bounding box info
|
||||
//
|
||||
VectorCopy (node->mins, leaf_p->mins);
|
||||
VectorCopy (node->maxs, leaf_p->maxs);
|
||||
|
||||
leaf_p->visofs = -1; // no vis info yet
|
||||
|
||||
//
|
||||
// write the marksurfaces
|
||||
//
|
||||
leaf_p->firstmarksurface = bsp->nummarksurfaces;
|
||||
|
||||
for (fp=node->markfaces ; *fp ; fp++)
|
||||
{
|
||||
// emit a marksurface
|
||||
if (bsp->nummarksurfaces == MAX_MAP_MARKSURFACES)
|
||||
Sys_Error ("nummarksurfaces == MAX_MAP_MARKSURFACES");
|
||||
f = *fp;
|
||||
do
|
||||
{
|
||||
bsp->marksurfaces[bsp->nummarksurfaces] = f->outputnumber;
|
||||
bsp->nummarksurfaces++;
|
||||
f=f->original; // grab tjunction split faces
|
||||
} while (f);
|
||||
}
|
||||
|
||||
leaf_p->nummarksurfaces = bsp->nummarksurfaces - leaf_p->firstmarksurface;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteDrawNodes_r
|
||||
==================
|
||||
*/
|
||||
void WriteDrawNodes_r (node_t *node)
|
||||
{
|
||||
dnode_t *n;
|
||||
int i;
|
||||
|
||||
// emit a node
|
||||
if (bsp->numnodes == MAX_MAP_NODES)
|
||||
Sys_Error ("numnodes == MAX_MAP_NODES");
|
||||
n = &bsp->nodes[bsp->numnodes];
|
||||
bsp->numnodes++;
|
||||
|
||||
VectorCopy (node->mins, n->mins);
|
||||
VectorCopy (node->maxs, n->maxs);
|
||||
|
||||
n->planenum = node->outputplanenum;
|
||||
n->firstface = node->firstface;
|
||||
n->numfaces = node->numfaces;
|
||||
|
||||
//
|
||||
// recursively output the other nodes
|
||||
//
|
||||
|
||||
for (i=0 ; i<2 ; i++)
|
||||
{
|
||||
if (node->children[i]->planenum == -1)
|
||||
{
|
||||
if (node->children[i]->contents == CONTENTS_SOLID)
|
||||
n->children[i] = -1;
|
||||
else
|
||||
{
|
||||
n->children[i] = -(bsp->numleafs + 1);
|
||||
WriteLeaf (node->children[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
n->children[i] = bsp->numnodes;
|
||||
WriteDrawNodes_r (node->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteDrawNodes
|
||||
==================
|
||||
*/
|
||||
void WriteDrawNodes (node_t *headnode)
|
||||
{
|
||||
int i;
|
||||
int start;
|
||||
dmodel_t *bm;
|
||||
|
||||
#if 0
|
||||
if (headnode->contents < 0)
|
||||
Sys_Error ("FinishBSPModel: empty model");
|
||||
#endif
|
||||
|
||||
// emit a model
|
||||
if (bsp->nummodels == MAX_MAP_MODELS)
|
||||
Sys_Error ("nummodels == MAX_MAP_MODELS");
|
||||
bm = &bsp->models[bsp->nummodels];
|
||||
bsp->nummodels++;
|
||||
|
||||
bm->headnode[0] = bsp->numnodes;
|
||||
bm->firstface = firstface;
|
||||
bm->numfaces = bsp->numfaces - firstface;
|
||||
firstface = bsp->numfaces;
|
||||
|
||||
start = bsp->numleafs;
|
||||
|
||||
if (headnode->contents < 0)
|
||||
WriteLeaf (headnode);
|
||||
else
|
||||
WriteDrawNodes_r (headnode);
|
||||
bm->visleafs = bsp->numleafs - start;
|
||||
|
||||
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;
|
||||
}
|
||||
// FIXME: are all the children decendant of padded nodes?
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
BumpModel
|
||||
|
||||
Used by the clipping hull processes that only need to store headclipnode
|
||||
==================
|
||||
*/
|
||||
void BumpModel (int hullnum)
|
||||
{
|
||||
dmodel_t *bm;
|
||||
|
||||
// emit a model
|
||||
if (bsp->nummodels == MAX_MAP_MODELS)
|
||||
Sys_Error ("nummodels == MAX_MAP_MODELS");
|
||||
bm = &bsp->models[bsp->nummodels];
|
||||
bsp->nummodels++;
|
||||
|
||||
bm->headnode[hullnum] = headclipnode;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char identification[4]; // should be 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;
|
||||
|
||||
QFile *texfile;
|
||||
wadinfo_t wadinfo;
|
||||
lumpinfo_t *lumpinfo;
|
||||
|
||||
void CleanupName (char *in, char *out)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i< 16 ; i++ )
|
||||
{
|
||||
if (!in[i])
|
||||
break;
|
||||
|
||||
out[i] = toupper(in[i]);
|
||||
}
|
||||
|
||||
for ( ; i< 16 ; i++ )
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
TEX_InitFromWad
|
||||
=================
|
||||
*/
|
||||
void TEX_InitFromWad (char *path)
|
||||
{
|
||||
int i;
|
||||
|
||||
texfile = Qopen (path, "rb");
|
||||
Qread (texfile, &wadinfo, sizeof(wadinfo));
|
||||
if (strncmp (wadinfo.identification, "WAD2", 4))
|
||||
Sys_Error ("TEX_InitFromWad: %s isn't a wadfile",path);
|
||||
wadinfo.numlumps = LittleLong(wadinfo.numlumps);
|
||||
wadinfo.infotableofs = LittleLong(wadinfo.infotableofs);
|
||||
Qseek (texfile, wadinfo.infotableofs, SEEK_SET);
|
||||
lumpinfo = malloc(wadinfo.numlumps*sizeof(lumpinfo_t));
|
||||
Qread (texfile, lumpinfo, wadinfo.numlumps*sizeof(lumpinfo_t));
|
||||
|
||||
for (i=0 ; i<wadinfo.numlumps ; i++)
|
||||
{
|
||||
CleanupName (lumpinfo[i].name, lumpinfo[i].name);
|
||||
lumpinfo[i].filepos = LittleLong(lumpinfo[i].filepos);
|
||||
lumpinfo[i].disksize = LittleLong(lumpinfo[i].disksize);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
LoadLump
|
||||
==================
|
||||
*/
|
||||
int LoadLump (char *name, byte *dest)
|
||||
{
|
||||
int i;
|
||||
char cname[16];
|
||||
|
||||
CleanupName (name, cname);
|
||||
|
||||
for (i=0 ; i<wadinfo.numlumps ; i++)
|
||||
{
|
||||
if (!strcmp(cname, lumpinfo[i].name))
|
||||
{
|
||||
Qseek (texfile, lumpinfo[i].filepos, SEEK_SET);
|
||||
Qread (texfile, dest, lumpinfo[i].disksize);
|
||||
return lumpinfo[i].disksize;
|
||||
}
|
||||
}
|
||||
|
||||
printf ("WARNING: texture %s not found\n", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
AddAnimatingTextures
|
||||
==================
|
||||
*/
|
||||
void AddAnimatingTextures (void)
|
||||
{
|
||||
int base;
|
||||
int i, j, k;
|
||||
char name[32];
|
||||
|
||||
base = nummiptex;
|
||||
|
||||
for (i=0 ; i<base ; i++)
|
||||
{
|
||||
if (miptex[i][0] != '+')
|
||||
continue;
|
||||
strcpy (name, miptex[i]);
|
||||
|
||||
for (j=0 ; j<20 ; j++)
|
||||
{
|
||||
if (j < 10)
|
||||
name[1] = '0'+j;
|
||||
else
|
||||
name[1] = 'A'+j-10; // alternate animation
|
||||
|
||||
|
||||
// see if this name exists in the wadfile
|
||||
for (k=0 ; k<wadinfo.numlumps ; k++)
|
||||
if (!strcmp(name, lumpinfo[k].name))
|
||||
{
|
||||
FindMiptex (name); // add to the miptex list
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf ("added %i texture frames\n", nummiptex - base);
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
WriteMiptex
|
||||
==================
|
||||
*/
|
||||
void WriteMiptex (void)
|
||||
{
|
||||
int i, len;
|
||||
byte *data;
|
||||
dmiptexlump_t *l;
|
||||
char *path;
|
||||
char fullpath[1024];
|
||||
|
||||
path = ValueForKey (&entities[0], "_wad");
|
||||
if (!path || !path[0])
|
||||
{
|
||||
path = ValueForKey (&entities[0], "wad");
|
||||
if (!path || !path[0])
|
||||
{
|
||||
printf ("WARNING: no wadfile specified\n");
|
||||
bsp->texdatasize = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sprintf (fullpath, "%s/%s", /*FIXME gamedir*/".", path);
|
||||
|
||||
TEX_InitFromWad (fullpath);
|
||||
|
||||
AddAnimatingTextures ();
|
||||
|
||||
l = (dmiptexlump_t *)bsp->texdata;
|
||||
data = (byte *)&l->dataofs[nummiptex];
|
||||
l->nummiptex = nummiptex;
|
||||
for (i=0 ; i<nummiptex ; i++)
|
||||
{
|
||||
l->dataofs[i] = data - (byte *)l;
|
||||
len = LoadLump (miptex[i], data);
|
||||
if (data + len - bsp->texdata >= MAX_MAP_MIPTEX)
|
||||
Sys_Error ("Textures exceeded MAX_MAP_MIPTEX");
|
||||
if (!len)
|
||||
l->dataofs[i] = -1; // didn't find the texture
|
||||
data += len;
|
||||
}
|
||||
|
||||
bsp->texdatasize = data - bsp->texdata;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
BeginBSPFile
|
||||
==================
|
||||
*/
|
||||
void BeginBSPFile (void)
|
||||
{
|
||||
// edge 0 is not used, because 0 can't be negated
|
||||
bsp->numedges = 1;
|
||||
|
||||
// leaf 0 is common solid with no faces
|
||||
bsp->numleafs = 1;
|
||||
bsp->leafs[0].contents = CONTENTS_SOLID;
|
||||
|
||||
firstface = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
FinishBSPFile
|
||||
==================
|
||||
*/
|
||||
void FinishBSPFile (void)
|
||||
{
|
||||
QFile *f;
|
||||
printf ("--- FinishBSPFile ---\n");
|
||||
printf ("WriteBSPFile: %s\n", bspfilename);
|
||||
|
||||
WriteMiptex ();
|
||||
|
||||
//XXX PrintBSPFileSizes ();
|
||||
f = Qopen (bspfilename, "wb");
|
||||
WriteBSPFile (bsp, f);
|
||||
Qclose (f);
|
||||
}
|
||||
|
Loading…
Reference in a new issue