-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmakenode.c
473 lines (396 loc) · 14.6 KB
/
makenode.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/*- MAKENODE.C --------------------------------------------------------------*
Recursively create nodes and return the pointers.
*---------------------------------------------------------------------------*/
#include "structs.h"
#include "bsp.h"
signed short node_x, node_y, node_dx, node_dy;
/*--------------------------------------------------------------------------*/
int SplitDist(struct Seg *ts)
{
double t,dx,dy;
if(ts->flip==0)
{
dx = (double)(vertices[linedefs[ts->linedef].start].x)-(vertices[ts->start].x);
dy = (double)(vertices[linedefs[ts->linedef].start].y)-(vertices[ts->start].y);
if(dx == 0 && dy == 0)
fprintf(stderr,"Trouble in SplitDist %f,%f\n",dx,dy);
t = sqrt((dx*dx) + (dy*dy));
return (int)t;
}
else
{
dx = (double)(vertices[linedefs[ts->linedef].end].x)-(vertices[ts->start].x);
dy = (double)(vertices[linedefs[ts->linedef].end].y)-(vertices[ts->start].y);
if(dx == 0 && dy == 0)
fprintf(stderr,"Trouble in SplitDist %f,%f\n",dx,dy);
t = sqrt((dx*dx) + (dy*dy));
return (int)t;
}
}
/*---------------------------------------------------------------------------*
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.
*---------------------------------------------------------------------------*/
static inline void
DivideSegs(struct Seg *ts, struct Seg **rs, struct Seg **ls, const bbox_t bbox)
{
struct Seg *rights,*lefts;
struct Seg *tmps,*best,*news,*prev;
struct Seg *add_to_rs,*add_to_ls;
struct Seg *new_best=NULL,*new_rs,*new_ls;
struct Seg *strights,*stlefts;
int num_new=0;
short int x,y,val;
best = PickNode(ts,bbox); /* 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 = *tmps;
tmps->next = news;
news->start = num_verts;
tmps->end = num_verts;
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*/
{
/* 06/01/97 Lee Killough: this fixes a bug ever since 1.2x,
probably 1.0, of BSP: when partitioning a parallel seg,
you must take its vertices' orientation into account, NOT the
flip bits, to determine which side of the partitioning line a
parallel seg should go on. If you simply flip the linedef in
question, you will be flipping both its vertices and sidedefs,
and the flip bits as well, even though the basic geometry has
not changed. Thus you need to use the vertices' orientation
(whether the seg is in the same direction or not, regardless
of its original linedef's being flipped or not), into account.
Originally, some segs were partitioned backwards, and if
it happened that there were different sectors on either
side of the seg being partitioned, it could leave holes
in space, causing either invisible barriers or disappearing
Things, because the ssector would be associated with the
wrong sector.
The old logic of tmps->flip != best->flip seems to rest on
the assumption that if two segs are parallel, they came
from the same linedef. This is clearly not always true. */
/* if (tmps->flip != best->flip) old logic -- wrong!!! */
/* We know the segs are parallel or nearly so, so take their
dot product to determine their relative orientation. */
if ( (lsx-lex)*pdx + (lsy-ley)*pdy < 0)
add_to_ls = tmps;
else
add_to_rs = 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));
*new_rs = *add_to_rs;
if(add_to_rs == best) new_best = new_rs;
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));
*new_ls = *add_to_ls;
if(add_to_ls == best) new_best = new_ls;
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;
}
/*--------------------------------------------------------------------------*/
static inline int IsItConvex( struct Seg *ts)
{
struct Seg *line=ts,*check;
int sector,val;
/* All ssectors must come from same sector unless it's marked
"special" with sector tag >= 900. Original idea, Lee Killough */
sector = sidedefs[ts->flip ? linedefs[ts->linedef].sidedef2 :
linedefs[ts->linedef].sidedef1].sector;
if (sectors[sector].tag < 900)
while ((line=line->next)!=0)
{
int ts=sidedefs[line->flip ? linedefs[line->linedef].sidedef2 :
linedefs[line->linedef].sidedef1].sector;
if (ts != sector && sectors[ts].tag < 900)
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;
}
/*--------------------------------------------------------------------------*/
static inline int CreateSSector(struct Seg *tmps)
{
struct Seg *next;
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=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++;
next = tmps->next;
free(tmps); /* This seg is done */
}
ssectors[num_ssectors].num = num_psegs-n;
num_ssectors++;
return num_ssectors-1;
}
/*- translate (dx, dy) into an integer angle value (0-65535) ---------------*/
inline unsigned int 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;
}
struct Node *CreateNode(struct Seg *ts, const bbox_t bbox)
{
struct Node *tn;
struct Seg *rights = NULL;
struct Seg *lefts = NULL;
tn = GetMemory( sizeof( struct Node)); /* Create a node*/
DivideSegs(ts,&rights,&lefts,bbox); /* 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,tn->leftbox); /* Find limits of vertices */
if(IsItConvex(lefts)) /* Check lefthand side*/
{
if (verbosity > 1) Verbose("L");
tn->nextl = CreateNode(lefts,tn->leftbox); /* still segs remaining*/
tn->chleft = 0;
if (verbosity > 1) Verbose("\b");
}
else
{
tn->nextl = NULL;
tn->chleft = CreateSSector(lefts) | 0x8000;
}
FindLimits(rights, tn->rightbox); /* Find limits of vertices*/
if(IsItConvex(rights)) /* Check righthand side*/
{
if (verbosity > 1) Verbose("R");
tn->nextr = CreateNode(rights, tn->rightbox); /* still segs remaining*/
tn->chright = 0;
if (verbosity > 1) Verbose("\b");
}
else
{
tn->nextr = NULL;
tn->chright = CreateSSector(rights) | 0x8000;
}
return tn;
}
/*---------------------------------------------------------------------------*
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:
[email protected] (Raphal 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.
*---------------------------------------------------------------------------*/