home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Doom I/II Collection
/
DM12.ISO
/
edit
/
bsp12
/
makenode.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-04-13
|
14KB
|
447 lines
/*- MAKENODE.C --------------------------------------------------------------*
Recursively create nodes and return the pointers.
*---------------------------------------------------------------------------*/
struct Node *CreateNode(struct Seg *ts)
{
struct Node *tn;
struct Seg *rights = NULL;
struct Seg *lefts = NULL;
tn = GetMemory( sizeof( struct Node)); /* Create a node*/
DivideSegs(ts,&rights,&lefts); /* Divide node in two*/
num_nodes++;
tn->x = node_x; /* store node line info*/
tn->y = node_y;
tn->dx = node_dx;
tn->dy = node_dy;
FindLimits(lefts); /* Find limits of vertices */
tn->maxy2 = lmaxy;
tn->miny2 = lminy;
tn->minx2 = lminx;
tn->maxx2 = lmaxx;
if(IsItConvex(lefts)) /* Check lefthand side*/
{
tn->nextl = CreateNode(lefts); /* still segs remaining*/
tn->chleft = 0;
}
else
{
tn->nextl = NULL;
tn->chleft = CreateSSector(lefts) | 0x8000;
}
FindLimits(rights); /* Find limits of vertices*/
tn->maxy1 = lmaxy;
tn->miny1 = lminy;
tn->minx1 = lminx;
tn->maxx1 = lmaxx;
if(IsItConvex(rights)) /* Check righthand side*/
{
tn->nextr = CreateNode(rights); /* still segs remaining*/
tn->chright = 0;
}
else
{
tn->nextr = NULL;
tn->chright = CreateSSector(rights) | 0x8000;
}
return tn;
}
/*---------------------------------------------------------------------------*
Split a list of segs (ts) into two using the method described at bottom of
file, this was taken from OBJECTS.C in the DEU5beta source.
This is done by scanning all of the segs and finding the one that does
the least splitting and has the least difference in numbers of segs on either
side.
If the ones on the left side make a SSector, then create another SSector
else put the segs into lefts list.
If the ones on the right side make a SSector, then create another SSector
else put the segs into rights list.
*---------------------------------------------------------------------------*/
void DivideSegs(struct Seg *ts,struct Seg **rs,struct Seg **ls)
{
struct Seg *rights,*lefts;
struct Seg *tmps,*best,*news,*prev;
struct Seg *add_to_rs,*add_to_ls;
struct Seg *new_best,*new_rs,*new_ls;
struct Seg *strights,*stlefts;
int num_secs_r,num_secs_l,last_sec_r,last_sec_l;
int num,least_splits,least;
int fv,tv,num_new = 0;
int bangle,cangle,cangle2,cfv,ctv,dx,dy;
short int x,y,val;
FindLimits(ts); /* Find limits of this set of Segs*/
sp.halfsx = (lmaxx - lminx) / 2; /* Find half width of Node*/
sp.halfsy = (lmaxy - lminy) / 2;
sp.halfx = lminx + sp.halfsx; /* Find middle of Node*/
sp.halfy = lminy + sp.halfsy;
best = PickNode(ts); /* Pick best node to use.*/
if(best == NULL) ProgError("Couldn't pick nodeline!");
node_x = vertices[best->start].x;
node_y = vertices[best->start].y;
node_dx = vertices[best->end].x-vertices[best->start].x;
node_dy = vertices[best->end].y-vertices[best->start].y;
/* When we get to here, best is a pointer to the partition seg.
Using this partition line, we must split any lines that are intersected
into a left and right half, flagging them to be put their respective sides
Ok, now we have the best line to use as a partitioning line, we must
split all of the segs into two lists (rightside & leftside). */
rights = NULL; /* Start off with empty*/
lefts = NULL; /* lists.*/
strights = NULL; /* Start off with empty*/
stlefts = NULL; /* lists.*/
psx = vertices[best->start].x; /* Partition line coords*/
psy = vertices[best->start].y;
pex = vertices[best->end].x;
pey = vertices[best->end].y;
pdx = psx - pex; /* Partition line DX,DY*/
pdy = psy - pey;
for(tmps=ts;tmps;tmps=tmps->next)
{
progress(); /* Something for the user to look at.*/
add_to_rs = NULL;
add_to_ls = NULL;
if(tmps != best)
{
lsx = vertices[tmps->start].x; /* Calculate this here, cos it doesn't*/
lsy = vertices[tmps->start].y; /* change for all the interations of*/
lex = vertices[tmps->end].x; /* the inner loop!*/
ley = vertices[tmps->end].y;
val = DoLinesIntersect();
if((val&2 && val&64) || (val&4 && val&32)) /* If intersecting !!*/
{
ComputeIntersection(&x,&y);
/* printf("Splitting Linedef %d at %d,%d\n",tmps->linedef,x,y);*/
vertices = ResizeMemory(vertices, sizeof(struct Vertex) * (num_verts+1));
vertices[num_verts].x = x;
vertices[num_verts].y = y;
news = GetMemory(sizeof( struct Seg));
news->next = tmps->next;
tmps->next = news;
news->start = num_verts;
news->end = tmps->end;
tmps->end = num_verts;
news->linedef = tmps->linedef;
news->angle = tmps->angle;
news->flip = tmps->flip;
news->dist = SplitDist(news);
/* printf("splitting dist = %d\n",news->dist);*/
/* printf("splitting vertices = %d,%d,%d,%d\n",tmps->start,tmps->end,news->start,news->end);*/
if(val&32) add_to_ls = tmps;
if(val&64) add_to_rs = tmps;
if(val&2) add_to_ls = news;
if(val&4) add_to_rs = news;
tmps = news;
num_verts++;
num_new++;
}
else
{ /* Not split, which side ?*/
if(val&34) add_to_ls = tmps;
if(val&68) add_to_rs = tmps;
if(val&1 && val&16) /* On same line*/
{
if(tmps->flip == best->flip) add_to_rs = tmps;
if(tmps->flip != best->flip) add_to_ls = tmps;
}
}
}
else add_to_rs = tmps; /* This is the partition line*/
/* printf("Val = %X\n",val);*/
if(add_to_rs) /* CHECK IF SHOULD ADD RIGHT ONE */
{
new_rs = GetMemory(sizeof(struct Seg));
if(add_to_rs == best) new_best = new_rs;
new_rs->start = add_to_rs->start;
new_rs->end = add_to_rs->end;
new_rs->linedef = add_to_rs->linedef;
new_rs->angle = add_to_rs->angle;
new_rs->flip = add_to_rs->flip;
new_rs->dist = add_to_rs->dist;
new_rs->next = NULL;
if(!rights) strights = rights = new_rs;
else
{
rights->next = new_rs;
rights = new_rs;
}
}
if(add_to_ls) /* CHECK IF SHOULD ADD LEFT ONE */
{
new_ls = GetMemory(sizeof(struct Seg));
if(add_to_ls == best) new_best = new_ls;
new_ls->start = add_to_ls->start;
new_ls->end = add_to_ls->end;
new_ls->linedef = add_to_ls->linedef;
new_ls->angle = add_to_ls->angle;
new_ls->flip = add_to_ls->flip;
new_ls->dist = add_to_ls->dist;
new_ls->next = NULL;
if(!lefts) stlefts = lefts = new_ls;
else
{
lefts->next = new_ls;
lefts = new_ls;
}
}
}
if(strights == NULL)
{
/* printf("No right side, moving partition into right side\n");*/
strights = rights = new_best;
prev = NULL;
for(tmps=stlefts;tmps;tmps=tmps->next)
{
if(tmps == new_best)
{
if(prev != NULL) prev->next=tmps->next;
else stlefts=tmps->next;
}
prev=tmps;
}
prev->next = NULL;
}
if(stlefts == NULL)
{
/* printf("No left side, moving partition into left side\n");*/
stlefts = lefts = new_best;
prev = NULL;
for(tmps=strights;tmps;tmps=tmps->next)
{
if(tmps == new_best)
{
if(prev != NULL) prev->next=tmps->next;
else strights=tmps->next;
}
prev=tmps;
}
stlefts->next = NULL;
prev->next = NULL; /* Make sure end of list = NULL*/
}
if(rights->next != NULL) rights->next = NULL;
if(lefts->next != NULL) lefts->next = NULL;
for(tmps=ts;tmps;tmps=best)
{
best=tmps->next;
free(tmps);
}
/* printf("Made %d new Vertices and Segs\n",num_new);*/
*rs = strights ; *ls = stlefts;
}
/*--------------------------------------------------------------------------*/
int IsItConvex( struct Seg *ts)
{
struct Seg *line,*check;
int sector,val;
if (ts->flip) sector = sidedefs[linedefs[ ts->linedef].sidedef2].sector;
else sector = sidedefs[linedefs[ts->linedef].sidedef1].sector;
for (line = ts->next; line; line = line->next)
{
if (line->flip)
{
if (sidedefs[ linedefs[ line->linedef].sidedef2].sector != sector)
return TRUE;
}
else
{
if (sidedefs[ linedefs[ line->linedef].sidedef1].sector != sector)
return TRUE;
}
}
/* all of the segs must be on the same side all the other segs */
for(line=ts;line;line=line->next)
{
psx = vertices[line->start].x;
psy = vertices[line->start].y;
pex = vertices[line->end].x;
pey = vertices[line->end].y;
pdx = (psx - pex); /* Partition line DX,DY*/
pdy = (psy - pey);
for(check=ts;check;check=check->next)
{
if(line!=check)
{
lsx = vertices[check->start].x; /* Calculate this here, cos it doesn't*/
lsy = vertices[check->start].y; /* change for all the interations of*/
lex = vertices[check->end].x; /* the inner loop!*/
ley = vertices[check->end].y;
val = DoLinesIntersect();
if(val&34) return TRUE;
}
}
}
/* no need to split the list: these Segs can be put in a SSector */
return FALSE;
}
/*--------------------------------------------------------------------------*/
int CreateSSector(struct Seg *tmps)
{
int n;
if(num_ssectors == 0)
{
ssectors = GetMemory(sizeof(struct SSector));
}
else
{
ssectors = ResizeMemory(ssectors,sizeof(struct SSector)*(num_ssectors+1));
}
ssectors[num_ssectors].first = num_psegs;
n = num_psegs;
/* printf("\n");*/
for(;tmps;tmps=tmps->next)
{
if(num_psegs == 0)
{
psegs = GetMemory(sizeof(struct Pseg));
}
else
{
psegs = ResizeMemory(psegs,sizeof(struct Pseg)*(num_psegs+1));
}
psegs[num_psegs].start = tmps->start;
psegs[num_psegs].end = tmps->end;
psegs[num_psegs].angle = tmps->angle;
psegs[num_psegs].linedef = tmps->linedef;
psegs[num_psegs].flip = tmps->flip;
psegs[num_psegs].dist = tmps->dist;
/*
printf("%d,%d,%u,%d,%d,%u\n",
psegs[num_psegs].start,
psegs[num_psegs].end,
psegs[num_psegs].angle,
psegs[num_psegs].linedef,
psegs[num_psegs].flip,
psegs[num_psegs].dist);
*/
num_psegs++;
}
ssectors[num_ssectors].num = num_psegs-n;
num_ssectors++;
return num_ssectors-1;
}
/*- translate (dx, dy) into an integer angle value (0-65535) ---------------*/
unsigned ComputeAngle( int dx, int dy)
{
double w;
w = (atan2( (double) dy , (double) dx) * (double)(65536/(M_PI*2)));
if(w<0) w = (double)65536+w;
return (unsigned) w;
}
/*---------------------------------------------------------------------------*
This message has been taken, complete, from OBJECTS.C in DEU5beta source.
It outlines the method used here to pick the nodelines.
IF YOU ARE WRITING A DOOM EDITOR, PLEASE READ THIS:
I spent a lot of time writing the Nodes builder. There are some bugs in
it, but most of the code is OK. If you steal any ideas from this program,
put a prominent message in your own editor to make it CLEAR that some
original ideas were taken from DEU. Thanks.
While everyone was talking about LineDefs, I had the idea of taking only
the Segs into account, and creating the Segs directly from the SideDefs.
Also, dividing the list of Segs in two after each call to CreateNodes makes
the algorithm faster. I use several other tricks, such as looking at the
two ends of a Seg to see on which side of the nodeline it lies or if it
should be split in two. I took me a lot of time and efforts to do this.
I give this algorithm to whoever wants to use it, but with this condition:
if your program uses some of the ideas from DEU or the whole algorithm, you
MUST tell it to the user. And if you post a message with all or parts of
this algorithm in it, please post this notice also. I don't want to speak
legalese; I hope that you understand me... I kindly give the sources of my
program to you: please be kind with me...
If you need more information about this, here is my E-mail address:
quinet@montefiore.ulg.ac.be (Raphaël Quinet).
Short description of the algorithm:
1 - Create one Seg for each SideDef: pick each LineDef in turn. If it
has a "first" SideDef, then create a normal Seg. If it has a
"second" SideDef, then create a flipped Seg.
2 - Call CreateNodes with the current list of Segs. The list of Segs is
the only argument to CreateNodes.
3 - Save the Nodes, Segs and SSectors to disk. Start with the leaves of
the Nodes tree and continue up to the root (last Node).
CreateNodes does the following:
1 - Pick a nodeline amongst the Segs (minimize the number of splits and
keep the tree as balanced as possible).
2 - Move all Segs on the right of the nodeline in a list (segs1) and do
the same for all Segs on the left of the nodeline (in segs2).
3 - If the first list (segs1) contains references to more than one
Sector or if the angle between two adjacent Segs is greater than
180°, then call CreateNodes with this (smaller) list. Else, create
a SubSector with all these Segs.
4 - Do the same for the second list (segs2).
5 - Return the new node (its two children are already OK).
Each time CreateSSector is called, the Segs are put in a global list.
When there is no more Seg in CreateNodes' list, then they are all in the
global list and ready to be saved to disk.
*---------------------------------------------------------------------------*/