From 4325fb899348d19158216daf31b716004bceaf61 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 1 Jun 2006 01:43:16 +0000 Subject: [PATCH] - Merged in recent ZDBSP fixes: - Added code to explicitly handle outputting overlapping segs when building GL nodes with ZDBSP, removing the check that discarded them early on. - AddIntersection() should convert to doubles before subtracting the vertex from the node, not after, to avoid integer overflow. (See cah.wad, MAP12 and MAP13.) A simpler dot product will also suffice for distance calculation. - Splitters that come too close to a vertex should be avoided. (See cata.wad.) - Red-Black Tree implementation was broken and colored every node red. - Moved most of the code for outputting degenerate GL subsectors into another function. SVN r160 (trunk) --- docs/rh-log.txt | 17 ++++ src/nodebuild.cpp | 165 ++++++++++++++++------------------ src/nodebuild.h | 7 +- src/nodebuild_events.cpp | 12 ++- src/nodebuild_extract.cpp | 183 +++++++++++++++----------------------- src/nodebuild_gl.cpp | 19 +--- src/nodebuild_utility.cpp | 101 ++++++++------------- 7 files changed, 213 insertions(+), 291 deletions(-) diff --git a/docs/rh-log.txt b/docs/rh-log.txt index ae07df8397..38b12f2f9e 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,20 @@ +May 31, 2006 +- Merged in recent ZDBSP fixes: + - Added code to explicitly handle outputting overlapping segs when + building GL nodes with ZDBSP, removing the check that discarded + them early on. + - AddIntersection() should convert to doubles before subtracting the vertex + from the node, not after, to avoid integer overflow. (See cah.wad, MAP12 + and MAP13.) A simpler dot product will also suffice for distance calculation. + - Splitters that come too close to a vertex should be avoided. (See cata.wad.) + - Red-Black Tree implementation was broken and colored every node red. + - Moved most of the code for outputting degenerate GL subsectors into another + function. + - Removed forgotten debugging file dump from WriteSSectors2(). + - Enabled reference optimization and COMDAT folding in the linker for a slightly + smaller executable. + + May 31, 2006 (Changes by Graf Zahl) - Fixed: Ammo items dropped by monsters that have a default amount of 1 didn't contain any ammo at all. diff --git a/src/nodebuild.cpp b/src/nodebuild.cpp index 505da9b43b..e7e6bc209f 100644 --- a/src/nodebuild.cpp +++ b/src/nodebuild.cpp @@ -88,6 +88,7 @@ void FNodeBuilder::BuildTree () C_InitTicker ("Building BSP", FRACUNIT); HackSeg = DWORD_MAX; + HackMate = DWORD_MAX; CreateNode (0, bbox); CreateSubsectorsForReal (); C_InitTicker (NULL, 0); @@ -320,7 +321,11 @@ bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int } while (seg != DWORD_MAX); if (seg == DWORD_MAX) - { // It's a valid subsector + { // It's a valid non-GL subsector, and probably a valid GL subsector too. + if (GLNodes) + { + return CheckSubsectorOverlappingSegs (set, node, splitseg); + } return false; } @@ -331,20 +336,60 @@ bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int // from multiple sectors, and it seems ZenNode does something // similar. It is the only technique I could find that makes the // "transparent water" in nb_bmtrk.wad work properly. - // - // The seg is marked to indicate that it should be forced to the - // back of the splitter. Because these segs already form a convex - // set, all the other segs will be in front of the splitter. Since - // the splitter is formed from this seg, the back of the splitter - // will have a one-dimensional subsector. SplitSegs() will add two - // new minisegs to close it: one seg replaces this one on the front - // of the splitter, and the other is its partner on the back. - // - // Old code that will actually create valid two-dimensional sectors - // is included below for reference but is not likely to be used again. + return ShoveSegBehind (set, node, seg, DWORD_MAX); +} +// When creating GL nodes, we need to check for segs with the same start and +// end vertices and split them into two subsectors. + +bool FNodeBuilder::CheckSubsectorOverlappingSegs (DWORD set, node_t &node, DWORD &splitseg) +{ + int v1, v2; + DWORD seg1, seg2; + + for (seg1 = set; seg1 != DWORD_MAX; seg1 = Segs[seg1].next) + { + if (Segs[seg1].linedef == -1) + { // Do not check minisegs. + continue; + } + v1 = Segs[seg1].v1; + v2 = Segs[seg1].v2; + for (seg2 = Segs[seg1].next; seg2 != DWORD_MAX; seg2 = Segs[seg2].next) + { + if (Segs[seg2].v1 == v1 && Segs[seg2].v2 == v2) + { + if (Segs[seg2].linedef == -1) + { // Do not put minisegs into a new subsector. + swap (seg1, seg2); + } + D(Printf("Need to synthesize a splitter for set %d on seg %d (ov)\n", set, seg2)); + splitseg = DWORD_MAX; + + return ShoveSegBehind (set, node, seg2, seg1); + } + } + } + // It really is a good subsector. + return false; +} + +// The seg is marked to indicate that it should be forced to the +// back of the splitter. Because these segs already form a convex +// set, all the other segs will be in front of the splitter. Since +// the splitter is formed from this seg, the back of the splitter +// will have a one-dimensional subsector. SplitSegs() will add one +// or two new minisegs to close it: If mate is DWORD_MAX, then a +// new seg is created to replace this one on the front of the +// splitter. Otherwise, mate takes its place. In either case, the +// seg in front of the splitter is partnered with a new miniseg on +// the back so that the back will have two segs. + +bool FNodeBuilder::ShoveSegBehind (DWORD set, node_t &node, DWORD seg, DWORD mate) +{ SetNodeFromSeg (node, &Segs[seg]); HackSeg = seg; + HackMate = mate; if (!Segs[seg].planefront) { node.x += node.dx; @@ -353,79 +398,6 @@ bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int node.dy = -node.dy; } return Heuristic (node, set, false) != 0; - -#if 0 - // If there are only two segs in the set, and they form two sides - // of a triangle, the splitter should pass through their shared - // point and the (imaginary) third side of the triangle - if (setsize == 2) - { - FPrivVert *v1, *v2, *v3; - - if (Vertices[Segs[set].v2] == Vertices[Segs[seg].v1]) - { - v1 = &Vertices[Segs[set].v1]; - v2 = &Vertices[Segs[seg].v2]; - v3 = &Vertices[Segs[set].v2]; - } - else if (Vertices[Segs[set].v1] == Vertices[Segs[seg].v2]) - { - v1 = &Vertices[Segs[seg].v1]; - v2 = &Vertices[Segs[set].v2]; - v3 = &Vertices[Segs[seg].v2]; - } - else - { - v1 = v2 = v3 = NULL; - } - if (v1 != NULL) - { - node.x = v3->x; - node.y = v3->y; - node.dx = v1->x + (v2->x-v1->x)/2 - node.x; - node.dy = v1->y + (v2->y-v1->y)/2 - node.y; - return Heuristic (node, set, false) != 0; - } - } - - bool nosplit = true; - int firsthit = seg; - - do - { - seg = firsthit; - do - { - if (Segs[seg].linedef != -1 && - Segs[seg].frontsector != sec && - Segs[seg].frontsector == Segs[seg].backsector) - { - node.x = Vertices[Segs[set].v1].x; - node.y = Vertices[Segs[set].v1].y; - node.dx = Vertices[Segs[seg].v2].x - node.x; - node.dy = Vertices[Segs[seg].v2].y - node.y; - - if (Heuristic (node, set, nosplit) != 0) - { - return true; - } - - node.dx = Vertices[Segs[seg].v1].x - node.x; - node.dy = Vertices[Segs[seg].v1].y - node.y; - - if (Heuristic (node, set, nosplit) != 0) - { - return true; - } - } - - seg = Segs[seg].next; - } while (seg != DWORD_MAX); - } while ((nosplit ^= 1) == 0); - - // Give up. - return false; -#endif } // Splitters are chosen to coincide with segs in the given set. To reduce the @@ -525,6 +497,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) int side; bool splitter = false; unsigned int max, m2, p, q; + double frac; Touched.Clear (); Colinear.Clear (); @@ -616,6 +589,13 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) } } + // Splitters that are too close to a vertex are bad. + frac = InterceptVector (node, *test); + if (frac < 0.001 || frac > 0.999) + { + score -= int(1 / frac); + } + counts[0]++; counts[1]++; if (test->linedef != -1) @@ -900,15 +880,24 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou DWORD newback, newfront; newback = AddMiniseg (seg->v2, seg->v1, DWORD_MAX, set, splitseg); - newfront = AddMiniseg (Segs[set].v1, Segs[set].v2, newback, set, splitseg); + if (HackMate == DWORD_MAX) + { + newfront = AddMiniseg (Segs[set].v1, Segs[set].v2, newback, set, splitseg); + Segs[newfront].next = outset0; + outset0 = newfront; + } + else + { + newfront = HackMate; + Segs[newfront].partner = newback; + Segs[newback].partner = newfront; + } Segs[newback].frontsector = Segs[newback].backsector = Segs[newfront].frontsector = Segs[newfront].backsector = Segs[set].frontsector; Segs[newback].next = outset1; outset1 = newback; - Segs[newfront].next = outset0; - outset0 = newfront; } set = next; } diff --git a/src/nodebuild.h b/src/nodebuild.h index 605020fe54..167e26065f 100644 --- a/src/nodebuild.h +++ b/src/nodebuild.h @@ -133,6 +133,7 @@ private: TArray SplitSharers; // Segs collinear with the current splitter DWORD HackSeg; // Seg to force to back of splitter + DWORD HackMate; // Seg to use in front of hack seg FLevel &Level; bool GLNodes; // Add minisegs to make GL nodes? @@ -143,7 +144,7 @@ private: int SelectVertexExact (FPrivVert &vertex); void BuildTree (); void MakeSegsFromSides (); - FPrivSeg *CheckSegForDuplicate (const FPrivSeg *check); + int CreateSeg (int linenum, int sidenum); void GroupSegPlanes (); void FindPolyContainers (TArray &spots, TArray &anchors); bool GetPolyExtents (int polynum, fixed_t bbox[4]); @@ -153,7 +154,8 @@ private: int CreateSubsector (DWORD set, fixed_t bbox[4]); void CreateSubsectorsForReal (); bool CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize); - int SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int step, bool nosplit); + bool CheckSubsectorOverlappingSegs (DWORD set, node_t &node, DWORD &splitseg); + bool ShoveSegBehind (DWORD set, node_t &node, DWORD seg, DWORD mate); int SelectSplitter (DWORD set, node_t &node, DWORD &splitseg, int step, bool nosplit); void SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1); DWORD SplitSeg (DWORD segnum, int splitvert, int v1InFront); int Heuristic (node_t &node, DWORD set, bool honorNoSplit); @@ -173,6 +175,7 @@ private: int CloseSubsector (TArray &segs, int subsector, vertex_t *outVerts); DWORD PushGLSeg (TArray &segs, const FPrivSeg *seg, vertex_t *outVerts); void PushConnectingGLSeg (int subsector, TArray &segs, vertex_t *v1, vertex_t *v2); + int OutputDegenerateSubsector (TArray &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts); static int STACK_ARGS SortSegs (const void *a, const void *b); diff --git a/src/nodebuild_events.cpp b/src/nodebuild_events.cpp index a9c0934cff..d2831f6e2d 100644 --- a/src/nodebuild_events.cpp +++ b/src/nodebuild_events.cpp @@ -37,12 +37,15 @@ ** */ +#include #include "doomtype.h" #include "nodebuild.h" FEventTree::FEventTree () : Root (&Nil), Spare (NULL) { + memset (&Nil, 0, sizeof(Nil)); + Nil.Color = FEvent::BLACK; } FEventTree::~FEventTree () @@ -85,7 +88,7 @@ void FEventTree::LeftRotate (FEvent *x) y->Left->Parent = x; } y->Parent = x->Parent; - if (x->Parent != &Nil) + if (x->Parent == &Nil) { Root = y; } @@ -110,7 +113,7 @@ void FEventTree::RightRotate (FEvent *x) y->Right->Parent = x; } y->Parent = x->Parent; - if (x->Parent != &Nil) + if (x->Parent == &Nil) { Root = y; } @@ -176,7 +179,7 @@ void FEventTree::Insert (FEvent *z) z->Right = &Nil; z->Color = FEvent::RED; - while (z != Root && z->Parent->Color != FEvent::RED) + while (z != Root && z->Parent->Color == FEvent::RED) { if (z->Parent == z->Parent->Parent->Left) { @@ -219,10 +222,11 @@ void FEventTree::Insert (FEvent *z) } z->Parent->Color = FEvent::BLACK; z->Parent->Parent->Color = FEvent::RED; - RightRotate (z->Parent->Parent); + LeftRotate (z->Parent->Parent); } } } + Root->Color = FEvent::BLACK; } void FEventTree::Delete (FEvent *z) diff --git a/src/nodebuild_extract.cpp b/src/nodebuild_extract.cpp index 3492965fbf..04d1bfe3f1 100644 --- a/src/nodebuild_extract.cpp +++ b/src/nodebuild_extract.cpp @@ -246,126 +246,16 @@ int FNodeBuilder::CloseSubsector (TArray &segs, int subsector, vertex_t * // to the start seg. // A dot product serves to determine distance from the start seg. - seg = &Segs[SegList[first].SegNum]; - double x1 = Vertices[seg->v1].x; - double y1 = Vertices[seg->v1].y; - double dx = Vertices[seg->v2].x - x1, dx2; - double dy = Vertices[seg->v2].y - y1, dy2; - double lastdot = 0, dot; - bool firstside = seg->planefront; - // Stage 1. Go forward. - for (i = first + 1; i < max; ++i) - { - double bestdot = DBL_MAX; - FPrivSeg *bestseg = NULL; - for (j = first + 1; j < max; ++j) - { - seg = &Segs[SegList[j].SegNum]; - if (seg->planefront != firstside) - { - continue; - } - dx2 = Vertices[seg->v1].x - x1; - dy2 = Vertices[seg->v1].y - y1; - dot = dx*dx2 + dy*dy2; - - if (dot < bestdot && dot > lastdot) - { - bestdot = dot; - bestseg = seg; - } - } - if (bestseg != NULL) - { - if (prev->v2 != bestseg->v1) - { - PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[bestseg->v1]); - count++; - } - seg->storedseg = PushGLSeg (segs, bestseg, outVerts); - count++; - prev = bestseg; - lastdot = bestdot; - } - } + count += OutputDegenerateSubsector (segs, subsector, true, 0, prev, outVerts); // Stage 2. Go backward. - lastdot = DBL_MAX; - for (i = first + 1; i < max; ++i) - { - double bestdot = -DBL_MAX; - FPrivSeg *bestseg = NULL; - for (j = first + 1; j < max; ++j) - { - seg = &Segs[SegList[j].SegNum]; - if (seg->planefront == firstside) - { - continue; - } - dx2 = Vertices[seg->v1].x - x1; - dy2 = Vertices[seg->v1].y - y1; - dot = dx*dx2 + dy*dy2; - - if (dot > bestdot && dot < lastdot) - { - bestdot = dot; - bestseg = seg; - } - } - if (bestseg != NULL) - { - if (prev->v2 != bestseg->v1) - { - PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[bestseg->v1]); - count++; - } - seg->storedseg = PushGLSeg (segs, bestseg, outVerts); - count++; - prev = bestseg; - lastdot = bestdot; - } - } + count += OutputDegenerateSubsector (segs, subsector, false, DBL_MAX, prev, outVerts); // Stage 3. Go forward again. - lastdot = -DBL_MAX; - for (i = first + 1; i < max; ++i) - { - double bestdot = 0; - FPrivSeg *bestseg = NULL; - for (j = first + 1; j < max; ++j) - { - seg = &Segs[SegList[j].SegNum]; - if (seg->planefront != firstside) - { - continue; - } - dx2 = Vertices[seg->v1].x - x1; - dy2 = Vertices[seg->v1].y - y1; - dot = dx*dx2 + dy*dy2; - - if (dot < bestdot && dot > lastdot) - { - bestdot = dot; - bestseg = seg; - } - } - if (bestseg != NULL) - { - if (prev->v2 != bestseg->v1) - { - PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[bestseg->v1]); - count++; - } - seg->storedseg = PushGLSeg (segs, bestseg, outVerts); - count++; - prev = bestseg; - lastdot = bestdot; - } - } + count += OutputDegenerateSubsector (segs, subsector, true, -DBL_MAX, prev, outVerts); } - if (prev->v2 != firstVert) { PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[firstVert]); @@ -375,6 +265,73 @@ int FNodeBuilder::CloseSubsector (TArray &segs, int subsector, vertex_t * return count; } +int FNodeBuilder::OutputDegenerateSubsector (TArray &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts) +{ + static const double bestinit[2] = { -DBL_MAX, DBL_MAX }; + FPrivSeg *seg; + int i, j, first, max, count; + double dot, x1, y1, dx, dy, dx2, dy2; + bool wantside; + + first = Subsectors[subsector].firstline; + max = first + Subsectors[subsector].numlines; + count = 0; + + seg = &Segs[SegList[first].SegNum]; + x1 = Vertices[seg->v1].x; + y1 = Vertices[seg->v1].y; + dx = Vertices[seg->v2].x - x1; + dy = Vertices[seg->v2].y - y1; + wantside = seg->planefront ^ !bForward; + + for (i = first + 1; i < max; ++i) + { + double bestdot = bestinit[bForward]; + FPrivSeg *bestseg = NULL; + for (j = first + 1; j < max; ++j) + { + seg = &Segs[SegList[j].SegNum]; + if (seg->planefront != wantside) + { + continue; + } + dx2 = Vertices[seg->v1].x - x1; + dy2 = Vertices[seg->v1].y - y1; + dot = dx*dx2 + dy*dy2; + + if (bForward) + { + if (dot < bestdot && dot > lastdot) + { + bestdot = dot; + bestseg = seg; + } + } + else + { + if (dot > bestdot && dot < lastdot) + { + bestdot = dot; + bestseg = seg; + } + } + } + if (bestseg != NULL) + { + if (prev->v2 != bestseg->v1) + { + PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[bestseg->v1]); + count++; + } + seg->storedseg = PushGLSeg (segs, bestseg, outVerts); + count++; + prev = bestseg; + lastdot = bestdot; + } + } + return count; +} + DWORD FNodeBuilder::PushGLSeg (TArray &segs, const FPrivSeg *seg, vertex_t *outVerts) { seg_t newseg; diff --git a/src/nodebuild_gl.cpp b/src/nodebuild_gl.cpp index feb8524e1d..0a9b527f11 100644 --- a/src/nodebuild_gl.cpp +++ b/src/nodebuild_gl.cpp @@ -63,24 +63,7 @@ double FNodeBuilder::AddIntersection (const node_t &node, int vertex) // Calculate signed distance of intersection vertex from start of splitter. // Only ordering is important, so we don't need a sqrt. FPrivVert *v = &Vertices[vertex]; - double dx = double(v->x - node.x); - double dy = double(v->y - node.y); - double dist = dx*dx + dy*dy; - - if (node.dx != 0) - { - if ((node.dx > 0 && dx < 0.0) || (node.dx < 0 && dx > 0.0)) - { - dist = -dist; - } - } - else - { - if ((node.dy > 0 && dy < 0.0) || (node.dy < 0 && dy > 0.0)) - { - dist = -dist; - } - } + double dist = (double(v->x) - node.x)*(node.dx) + (double(v->y) - node.y)*(node.dy); FEvent *event = Events.FindEvent (dist); if (event == NULL) diff --git a/src/nodebuild_utility.cpp b/src/nodebuild_utility.cpp index 0732ab2e19..e1c23d5a6c 100644 --- a/src/nodebuild_utility.cpp +++ b/src/nodebuild_utility.cpp @@ -125,14 +125,8 @@ int FNodeBuilder::SelectVertexExact (FPrivVert &vertex) void FNodeBuilder::MakeSegsFromSides () { - FPrivSeg *share1, *share2; - FPrivSeg seg; int i, j; - seg.next = DWORD_MAX; - seg.loopnum = 0; - seg.partner = DWORD_MAX; - if (Level.NumLines == 0) { I_Error ("Map is empty.\n"); @@ -140,28 +134,9 @@ void FNodeBuilder::MakeSegsFromSides () for (i = 0; i < Level.NumLines; ++i) { - share1 = NULL; if (Level.Lines[i].sidenum[0] != NO_SIDE) { - seg.linedef = i; - seg.sidedef = Level.Lines[i].sidenum[0]; - seg.frontsector = Level.Lines[i].frontsector; - seg.backsector = Level.Lines[i].backsector; - seg.v1 = (int)(size_t)Level.Lines[i].v1; - seg.v2 = (int)(size_t)Level.Lines[i].v2; - seg.nextforvert = Vertices[seg.v1].segs; - seg.nextforvert2 = Vertices[seg.v2].segs2; - share1 = CheckSegForDuplicate (&seg); - if (share1 == NULL) - { - j = (int)Segs.Push (seg); - Vertices[seg.v1].segs = j; - Vertices[seg.v2].segs2 = j; - } - else - { - Printf ("Linedefs %d and %d share endpoints.\n", i, share1->linedef); - } + CreateSeg (i, 0); } else { @@ -170,55 +145,49 @@ void FNodeBuilder::MakeSegsFromSides () if (Level.Lines[i].sidenum[1] != NO_SIDE) { - seg.linedef = i; - seg.sidedef = Level.Lines[i].sidenum[1]; - seg.frontsector = Level.Lines[i].backsector; - seg.backsector = Level.Lines[i].frontsector; - seg.v1 = (int)(size_t)Level.Lines[i].v2; - seg.v2 = (int)(size_t)Level.Lines[i].v1; - seg.nextforvert = Vertices[seg.v1].segs; - seg.nextforvert2 = Vertices[seg.v2].segs2; - share2 = CheckSegForDuplicate (&seg); - if (share2 == NULL) + j = CreateSeg (i, 1); + if (Level.Lines[i].sidenum[0] != NO_SIDE) { - j = (int)Segs.Push (seg); - Vertices[seg.v1].segs = j; - Vertices[seg.v2].segs2 = j; - - if (Level.Lines[i].sidenum[0] != NO_SIDE && share1 == NULL) - { - Segs[j-1].partner = j; - Segs[j].partner = j-1; - } - } - else if (share1 == NULL || share2->linedef != share1->linedef) - { - Printf ("Linedefs %d and %d share endpoints.\n", i, share2->linedef); + Segs[j-1].partner = j; + Segs[j].partner = j-1; } } } } -// Check for another seg with the same start and end vertices as this one. -// Combined with its use above, this will find two-sided lines that are shadowed -// by another one- or two-sided line, and it will also find one-sided lines that -// shadow each other. It will not find one-sided lines that share endpoints but -// face opposite directions. Although they should probably be a single two-sided -// line, leaving them in will not generate bad nodes. - -FNodeBuilder::FPrivSeg *FNodeBuilder::CheckSegForDuplicate (const FNodeBuilder::FPrivSeg *check) +int FNodeBuilder::CreateSeg (int linenum, int sidenum) { - DWORD segnum; + FPrivSeg seg; + int segnum; - // Check for segs facing the same direction - for (segnum = check->nextforvert; segnum != DWORD_MAX; segnum = Segs[segnum].nextforvert) - { - if (Segs[segnum].v2 == check->v2) - { - return &Segs[segnum]; - } + seg.next = DWORD_MAX; + seg.loopnum = 0; + seg.partner = DWORD_MAX; + + if (sidenum == 0) + { // front + seg.frontsector = Level.Lines[linenum].frontsector; + seg.backsector = Level.Lines[linenum].backsector; + seg.v1 = (int)(size_t)Level.Lines[linenum].v1; + seg.v2 = (int)(size_t)Level.Lines[linenum].v2; } - return NULL; + else + { // back + seg.frontsector = Level.Lines[linenum].backsector; + seg.backsector = Level.Lines[linenum].frontsector; + seg.v2 = (int)(size_t)Level.Lines[linenum].v1; + seg.v1 = (int)(size_t)Level.Lines[linenum].v2; + } + seg.linedef = linenum; + seg.sidedef = Level.Lines[linenum].sidenum[sidenum]; + seg.nextforvert = Vertices[seg.v1].segs; + seg.nextforvert2 = Vertices[seg.v2].segs2; + + segnum = (int)Segs.Push (seg); + Vertices[seg.v1].segs = segnum; + Vertices[seg.v2].segs2 = segnum; + + return segnum; } // Group colinear segs together so that only one seg per line needs to be checked