home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
D!Zone (Collector's Edition)
/
D_ZONE_CD.ISO
/
programs
/
editors
/
vnb1050
/
nodes.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-05-25
|
29KB
|
701 lines
/******************************************************************************
MODULE: NODES.C
WRITTEN BY: Robert Fenske, Jr. (rfenske@swri.edu)
Southwest Research Institute
Electromagnetics Division
6220 Culebra
San Antonio, Texas 78238-5166
CREATED: Mar. 1994
DESCRIPTION: This module contains routines to generate the SEGS,
SSECTORS, and NODES sections. It needs only the
LINEDEFS and VERTEXES as input. These three sections
combine to form a binary space partition tree. This
tree is built by recursively dividing the space into
sections that balance (or at least try to) the number
of segments and produce the least number of segment
splits. The nodes are kept in a binary tree. The
segments are kept in an ordered doubly-linked list.
The created vertices are added to the end of the
vertex list. Once the divisions are complete, the
SEGS, SSECTORS, and NODES structures are created from
the binary tree and the segment and vertex lists. Any
memory allocated for the binary tree or the linked
list is freed when no longer needed.
This module does not do any error checking on any
memory allocation. If any allocation ever fails, this
module will bomb.
This module used to do some error checking while
building the node tree, but now the tree is generated
regardless of whether the input describes a correct,
playable map.
I wrote the code from the description of the binary
space partition in the Unofficial DOOM Specs written
by Matt Fell. I found these references after I did
the coding:
1) Computer Graphics Principles and Practice,
2nd ed 1990
Foley J.D., van Dam A., Feiner S.K., Hughes J.F.
ISBN 0-201-12110-7
2) "On Visible Surface Generation by a Priori Tree
Structures"
Fuchs H., Kedem Z.M., Naylor B.F.
Computer Graphics (SIGGRAPH '80 Proceedings)
v14 #3 July 1980 pp 124-133
3) "Near Real-Time Shaded Display of Rigid Objects"
Fuchs H., Abram G.D., Grant E.D.
Computer Graphics (SIGGRAPH '83 Proceesings)
v17 #3 July 1983 pp 65-72
DOOM is a trademark of id Software, Inc.
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "dmglobal.i"
#define tree_branch(c) ((c)=blockmem(NODE_TREE,1), \
(c)->left=NULL, (c)->right=NULL, (c))
#define two_sided(l) (Lines[l].lsidndx != -1)
#define vdist2(v1,v2) ((long)((v1).x-(v2).x)*((v1).x-(v2).x)+\
(long)((v1).y-(v2).y)*((v1).y-(v2).y))
typedef struct SEGS_INFO {
DOOM_SEGS seg; /* a SEGment */
struct SEGS_INFO *prev, *next; /* to previous, next SEGment */
} SEGS_INFO;
typedef struct NODE_TREE {
short ymin, ymax, xmin, xmax; /* node rectangle limits */
SEGS_INFO *pseg; /* partition line SEG */
SEGS_INFO *segs; /* node's SEGS */
short nsegs; /* # initial SEGS in node */
struct NODE_TREE *left, *right; /* left, right children */
} NODE_TREE;
typedef struct NODE_INFO {
DOOM_NODE *nodes; /* all nodes */
int nnodes; /* total # NODES */
DOOM_VERT *sverts; /* all SEGS vertices */
int nsverts; /* total # SEGS vertices */
DOOM_SEGS *segs; /* all nodes' SEGS */
int nsegs; /* total # segs */
DOOM_SSECTOR *ssecs; /* all nodes' SSECTORs */
int nssecs; /* total # SSECTORs */
SEGS_INFO *sinfo; /* all SEGS lists */
} NODE_INFO;
NODE_INFO ninfo; /* node information */
/******************************************************************************
ROUTINE: nodes_segs_init(lines,nlines)
WRITTEN BY: Robert Fenske, Jr.
CREATED: Mar. 1994
DESCRIPTION: This routine initializes the SEGS list by scanning the
LINEDEFS list and creating the appropriate SEGS
entries. A seg is created for each side a LINEDEF has.
It returns the number of SEGS created.
******************************************************************************/
int nodes_segs_init(lines,nlines)
register DOOM_LINE *lines;
int nlines;
{
DOOM_VERT *vf, *vt;
register SEGS_INFO *sinfo;
register int nsegs;
register int l;
nsegs = 0;
ninfo.sinfo = sinfo = blockmem(SEGS_INFO,1);
sinfo->prev = NULL;
for (l = 0; l < nlines; l++) { /* scan all lines */
sinfo->seg.fndx = lines[l].fndx;
sinfo->seg.tndx = lines[l].tndx;
vf = &ninfo.sverts[sinfo->seg.fndx], vt = &ninfo.sverts[sinfo->seg.tndx];
sinfo->seg.angle = (bams)(vt->y==vf->y && vt->x<vf->x ? BAMS180 :
atan2((double)(vt->y-vf->y),
(double)(vt->x-vf->x))*rad_to_bams+
0.5*sgn(vt->y-vf->y));
sinfo->seg.sndx = 0; /* right side */
sinfo->seg.lndx = l;
sinfo->seg.loffset = 0;
nsegs++;
sinfo->next = blockmem(SEGS_INFO,1); /* set up for next one */
sinfo->next->prev = sinfo;
sinfo = sinfo->next;
if (two_sided(l)) { /* a left side also */
sinfo->seg.fndx = lines[l].tndx; /* switch vertices */
sinfo->seg.tndx = lines[l].fndx;
sinfo->seg.angle = sinfo->prev->seg.angle+BAMS180;
sinfo->seg.sndx = 1; /* left side */
sinfo->seg.lndx = l;
sinfo->seg.loffset = 0;
nsegs++;
sinfo->next = blockmem(SEGS_INFO,1); /* set up for next one */
sinfo->next->prev = sinfo;
sinfo = sinfo->next;
}
}
sinfo->prev->next = NULL;
blockfree(sinfo); /* don't need this one */
return nsegs; /* return # created SEGS */
}
/******************************************************************************
ROUTINE: nodes_split_seg(pseg,seg,right,left)
WRITTEN BY: Robert Fenske, Jr.
CREATED: Mar. 1994
DESCRIPTION: This routine splits the input segment into left and
right segments based on the input partition line. The
intersection of the partition line (based on the input
"from" and "to" coordinates) with the segment is found
so that a new vertex can be added to the vertex list.
The offset field of the left and right segments are
computed from the distance to the new vertex along the
segment.
******************************************************************************/
void nodes_split_seg(pseg,seg,right,left)
SEGS_INFO *pseg, *seg;
register SEGS_INFO **right, **left;
{
DOOM_VERT *pf = &ninfo.sverts[pseg->seg.fndx],
*pt = &ninfo.sverts[pseg->seg.tndx],
*vf = &ninfo.sverts[seg->seg.fndx],
*vt = &ninfo.sverts[seg->seg.tndx];
long Ap = -((long)pt->y - pf->y), /* partition line is */
Bp = (long)pt->x - pf->x, /* Ax + By + C = 0 */
Cp = (long)pt->y*pf->x - (long)pt->x*pf->y;
long As = -((long)vt->y - vf->y), /* SEG line is */
Bs = (long)vt->x - vf->x, /* Ax + By + C = 0 */
Cs = (long)vt->y*vf->x - (long)vt->x*vf->y;
double x, y; /* intersection coords */
DOOM_VERT iv; /* intersection vertex */
register int v; /* intersection vertex index */
*right = blockmem(SEGS_INFO,1); /* new right side SEG */
(*right)->seg = seg->seg;
(*right)->next = NULL;
*left = blockmem(SEGS_INFO,1); /* new left side SEG */
(*left)->seg = seg->seg;
(*left)->next = NULL;
x = ((double)Bs*Cp - (double)Bp*Cs)/((double)Bp*As - (double)Bs*Ap);
y = -((double)As*Cp - (double)Ap*Cs)/((double)Bp*As - (double)Bs*Ap);
iv.x = x + sgn(x)*0.5;
iv.y = y + sgn(y)*0.5;
ninfo.sverts[v = ninfo.nsverts++] = iv; /* add new vertex to list */
if (seg->seg.sndx == 0) { /* this is a right side SEG */
if (Ap*vf->x + Bp*vf->y + Cp < 0) {
(*right)->seg.tndx = v;
(*left)->seg.fndx = v;
(*left)->seg.loffset = seg->seg.loffset + sqrt((double)vdist2(*vf,iv));
}else {
(*right)->seg.fndx = v;
(*right)->seg.loffset = seg->seg.loffset + sqrt((double)vdist2(*vt,iv));
(*left)->seg.tndx = v;
}
}else { /* this is a left side SEG */
if (Ap*vt->x + Bp*vt->y + Cp < 0) {
(*right)->seg.fndx = v;
(*right)->seg.loffset = seg->seg.loffset + sqr