diff --git a/CMakeLists.txt b/CMakeLists.txt index 78264488de..215858b141 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,13 +64,6 @@ set( CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${REL_C_FLAGS}" ) set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${REL_C_FLAGS}" ) set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEB_C_FLAGS} -D_DEBUG" ) -if( CMAKE_COMPILER_IS_GNUCXX AND PROFILE ) - set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" ) - set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" ) - set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -pg" ) - set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -pg" ) -endif( CMAKE_COMPILER_IS_GNUCXX AND PROFILE ) - if( ZLIB_FOUND ) message( STATUS "Using system zlib" ) else( ZLIB_FOUND ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ea4f153a2a..67e06cf3bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -330,6 +330,13 @@ endif( NOT NO_ASM ) # Set up flags for GCC if( CMAKE_COMPILER_IS_GNUCXX ) + if( PROFILE ) + set( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -pg" ) + set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg" ) + set( CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -pg" ) + set( CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -pg" ) + endif( PROFILE ) + set( REL_CXX_FLAGS "-fno-rtti" ) if( NOT PROFILE ) set( REL_CXX_FLAGS "${REL_CXX_FLAGS} -fomit-frame-pointer" ) diff --git a/src/am_map.cpp b/src/am_map.cpp index 5883b9472f..9dccdf2c03 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -1672,6 +1672,22 @@ void AM_drawSeg(seg_t *seg, const AMColor &color) AM_drawMline(&l, color); } +void AM_drawPolySeg(FPolySeg *seg, const AMColor &color) +{ + mline_t l; + l.a.x = seg->v1.x >> FRACTOMAPBITS; + l.a.y = seg->v1.y >> FRACTOMAPBITS; + l.b.x = seg->v2.x >> FRACTOMAPBITS; + l.b.y = seg->v2.y >> FRACTOMAPBITS; + + if (am_rotate == 1 || (am_rotate == 2 && viewactive)) + { + AM_rotatePoint (&l.a.x, &l.a.y); + AM_rotatePoint (&l.b.x, &l.b.y); + } + AM_drawMline(&l, color); +} + void AM_showSS() { if (am_showsubsector >= 0 && am_showsubsector < numsubsectors) @@ -1682,13 +1698,13 @@ void AM_showSS() red.FromRGB(255,0,0); subsector_t *sub = &subsectors[am_showsubsector]; - for(unsigned int i=0;inumlines;i++) + for (unsigned int i = 0; i < sub->numlines; i++) { - AM_drawSeg(&segs[sub->firstline+i], yellow); + AM_drawSeg(sub->firstline + i, yellow); } PO_LinkToSubsectors(); - for(int i=0;isubsectorlinks; @@ -1697,9 +1713,9 @@ void AM_showSS() { if (pnode->subsector == sub) { - for(unsigned j=0;jsegs.Size();j++) + for (unsigned j = 0; j < pnode->segs.Size(); j++) { - AM_drawSeg(&pnode->segs[j], red); + AM_drawPolySeg(&pnode->segs[j], red); } } pnode = pnode->snext; diff --git a/src/nodebuild.cpp b/src/nodebuild.cpp index bbf281b958..804190feee 100644 --- a/src/nodebuild.cpp +++ b/src/nodebuild.cpp @@ -62,10 +62,17 @@ const int AAPreference = 16; #define D(x) do{}while(0) #endif +FNodeBuilder::FNodeBuilder(FLevel &level) +: Level(level), GLNodes(false), SegsStuffed(0) +{ + + VertexMap = NULL; +} + FNodeBuilder::FNodeBuilder (FLevel &level, TArray &polyspots, TArray &anchors, - bool makeGLNodes, bool enableSSE2) - : Level(level), GLNodes(makeGLNodes), EnableSSE2(enableSSE2), SegsStuffed(0) + bool makeGLNodes) + : Level(level), GLNodes(makeGLNodes), SegsStuffed(0) { VertexMap = new FVertexMap (*this, Level.MinX, Level.MinY, Level.MaxX, Level.MaxY); FindUsedVertices (Level.Vertices, Level.NumVertices); @@ -83,6 +90,33 @@ FNodeBuilder::~FNodeBuilder() } } +void FNodeBuilder::BuildMini(bool makeGLNodes) +{ + GLNodes = makeGLNodes; + GroupSegPlanesSimple(); + BuildTree(); +} + +void FNodeBuilder::Clear() +{ + SegsStuffed = 0; + Nodes.Clear(); + Subsectors.Clear(); + SubsectorSets.Clear(); + Segs.Clear(); + Vertices.Clear(); + SegList.Clear(); + PlaneChecked.Clear(); + Planes.Clear(); + Touched.Clear(); + Colinear.Clear(); + SplitSharers.Clear(); + if (VertexMap == NULL) + { + VertexMap = new FVertexMapSimple(*this); + } +} + void FNodeBuilder::BuildTree () { fixed_t bbox[4]; @@ -90,35 +124,38 @@ void FNodeBuilder::BuildTree () C_InitTicker ("Building BSP", FRACUNIT); HackSeg = DWORD_MAX; HackMate = DWORD_MAX; - CreateNode (0, bbox); + CreateNode (0, Segs.Size(), bbox); CreateSubsectorsForReal (); C_InitTicker (NULL, 0); } -int FNodeBuilder::CreateNode (DWORD set, fixed_t bbox[4]) +int FNodeBuilder::CreateNode (DWORD set, unsigned int count, fixed_t bbox[4]) { node_t node; - int skip, count, selstat; + int skip, selstat; DWORD splitseg; - count = CountSegs (set); - skip = count / MaxSegs; + skip = int(count / MaxSegs); + // When building GL nodes, count may not be an exact count of the number of segs + // in the set. That's okay, because we just use it to get a skip count, so an + // estimate is fine. if ((selstat = SelectSplitter (set, node, splitseg, skip, true)) > 0 || (skip > 0 && (selstat = SelectSplitter (set, node, splitseg, 1, true)) > 0) || (selstat < 0 && (SelectSplitter (set, node, splitseg, skip, false) > 0 || (skip > 0 && SelectSplitter (set, node, splitseg, 1, false)))) || - CheckSubsector (set, node, splitseg, count)) + CheckSubsector (set, node, splitseg)) { // Create a normal node DWORD set1, set2; + unsigned int count1, count2; - SplitSegs (set, node, splitseg, set1, set2); + SplitSegs (set, node, splitseg, set1, set2, count1, count2); D(PrintSet (1, set1)); D(Printf (PRINT_LOG, "(%d,%d) delta (%d,%d) from seg %d\n", node.x>>16, node.y>>16, node.dx>>16, node.dy>>16, splitseg)); D(PrintSet (2, set2)); - node.intchildren[0] = CreateNode (set1, node.bbox[0]); - node.intchildren[1] = CreateNode (set2, node.bbox[1]); + node.intchildren[0] = CreateNode (set1, count1, node.bbox[0]); + node.intchildren[1] = CreateNode (set2, count2, node.bbox[1]); bbox[BOXTOP] = MAX (node.bbox[0][BOXTOP], node.bbox[1][BOXTOP]); bbox[BOXBOTTOM] = MIN (node.bbox[0][BOXBOTTOM], node.bbox[1][BOXBOTTOM]); bbox[BOXLEFT] = MIN (node.bbox[0][BOXLEFT], node.bbox[1][BOXLEFT]); @@ -173,17 +210,15 @@ void FNodeBuilder::CreateSubsectorsForReal () subsector_t sub; unsigned int i; - sub.validcount = 0; - sub.CenterX = 0; // Code in p_setup.cpp will set these for us later. - sub.CenterY = 0; sub.sector = NULL; sub.polys = NULL; + sub.BSP = NULL; for (i = 0; i < SubsectorSets.Size(); ++i) { DWORD set = SubsectorSets[i]; + DWORD firstline = (DWORD)SegList.Size(); - sub.firstline = (DWORD)SegList.Size(); while (set != DWORD_MAX) { USegPtr ptr; @@ -192,14 +227,15 @@ void FNodeBuilder::CreateSubsectorsForReal () SegList.Push (ptr); set = ptr.SegPtr->next; } - sub.numlines = (DWORD)(SegList.Size() - sub.firstline); + sub.numlines = (DWORD)(SegList.Size() - firstline); + sub.firstline = (seg_t *)firstline; // Sort segs by linedef for special effects - qsort (&SegList[sub.firstline], sub.numlines, sizeof(USegPtr), SortSegs); + qsort (&SegList[firstline], sub.numlines, sizeof(USegPtr), SortSegs); // Convert seg pointers into indices D(Printf (PRINT_LOG, "Output subsector %d:\n", Subsectors.Size())); - for (unsigned int i = sub.firstline; i < SegList.Size(); ++i) + for (unsigned int i = firstline; i < SegList.Size(); ++i) { D(Printf (PRINT_LOG, " Seg %5d%c(%5d,%5d)-(%5d,%5d)\n", SegList[i].SegPtr - &Segs[0], SegList[i].SegPtr->linedef == -1 ? '+' : ' ', @@ -273,24 +309,12 @@ int STACK_ARGS FNodeBuilder::SortSegs (const void *a, const void *b) } } -int FNodeBuilder::CountSegs (DWORD set) const -{ - int count = 0; - - while (set != DWORD_MAX) - { - count++; - set = Segs[set].next; - } - return count; -} - // Given a set of segs, checks to make sure they all belong to a single // sector. If so, false is returned, and they become a subsector. If not, // a splitter is synthesized, and true is returned to continue processing // down this branch of the tree. -bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize) +bool FNodeBuilder::CheckSubsector (DWORD set, node_t &node, DWORD &splitseg) { sector_t *sec; DWORD seg; @@ -506,7 +530,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) int realSegs[2] = { 0, 0 }; int specialSegs[2] = { 0, 0 }; DWORD i = set; - int sidev1, sidev2; + int sidev[2]; int side; bool splitter = false; unsigned int max, m2, p, q; @@ -525,7 +549,7 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) } else { - side = ClassifyLine (node, test, sidev1, sidev2); + side = ClassifyLine (node, &Vertices[test->v1], &Vertices[test->v2], sidev); } switch (side) { @@ -535,9 +559,9 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) // The "right" thing to do in this case is to only reject it if there is // another nosplit seg from the same sector at this vertex. Note that a line // that lies exactly on top of the splitter is okay. - if (test->loopnum && honorNoSplit && (sidev1 == 0 || sidev2 == 0)) + if (test->loopnum && honorNoSplit && (sidev[0] == 0 || sidev[1] == 0)) { - if ((sidev1 | sidev2) != 0) + if ((sidev[0] | sidev[1]) != 0) { max = Touched.Size(); for (p = 0; p < max; ++p) @@ -735,8 +759,10 @@ int FNodeBuilder::Heuristic (node_t &node, DWORD set, bool honorNoSplit) return score; } -void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1) +void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1, unsigned int &count0, unsigned int &count1) { + unsigned int _count0 = 0; + unsigned int _count1 = 0; outset0 = DWORD_MAX; outset1 = DWORD_MAX; @@ -749,18 +775,18 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou FPrivSeg *seg = &Segs[set]; int next = seg->next; - int sidev1, sidev2, side; + int sidev[2], side; if (HackSeg == set) { HackSeg = DWORD_MAX; side = 1; - sidev1 = sidev2 = 0; + sidev[0] = sidev[1] = 0; hack = true; } else { - side = ClassifyLine (node, seg, sidev1, sidev2); + side = ClassifyLine (node, &Vertices[seg->v1], &Vertices[seg->v2], sidev); hack = false; } @@ -769,11 +795,13 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou case 0: // seg is entirely in front seg->next = outset0; outset0 = set; + _count0++; break; case 1: // seg is entirely in back seg->next = outset1; outset1 = set; + _count1++; break; default: // seg needs to be split @@ -803,18 +831,20 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou Printf("SelectVertexClose selected endpoint of seg %u\n", set); } - seg2 = SplitSeg (set, vertnum, sidev1); + seg2 = SplitSeg (set, vertnum, sidev[0]); Segs[seg2].next = outset0; outset0 = seg2; Segs[set].next = outset1; outset1 = set; + _count0++; + _count1++; // Also split the seg on the back side if (Segs[set].partner != DWORD_MAX) { int partner1 = Segs[set].partner; - int partner2 = SplitSeg (partner1, vertnum, sidev2); + int partner2 = SplitSeg (partner1, vertnum, sidev[1]); // The newly created seg stays in the same set as the // back seg because it has not been considered for splitting // yet. If it had been, then the front seg would have already @@ -835,17 +865,17 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou } if (side >= 0 && GLNodes) { - if (sidev1 == 0) + if (sidev[0] == 0) { double dist1 = AddIntersection (node, seg->v1); - if (sidev2 == 0) + if (sidev[1] == 0) { double dist2 = AddIntersection (node, seg->v2); FSplitSharer share = { dist1, set, dist2 > dist1 }; SplitSharers.Push (share); } } - else if (sidev2 == 0) + else if (sidev[1] == 0) { AddIntersection (node, seg->v2); } @@ -881,6 +911,8 @@ void FNodeBuilder::SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &ou { AddMinisegs (node, splitseg, outset0, outset1); } + count0 = _count0; + count1 = _count1; } void FNodeBuilder::SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const @@ -1038,3 +1070,92 @@ void FNodeBuilder::PrintSet (int l, DWORD set) } Printf (PRINT_LOG, "*\n"); } + + + +#ifdef BACKPATCH +#ifdef _WIN32 +extern "C" { +__declspec(dllimport) int __stdcall VirtualProtect(void *, unsigned long, unsigned long, unsigned long *); +} +#define PAGE_EXECUTE_READWRITE 64 +#else +#include +#include +#endif + +#ifdef __GNUC__ +extern "C" int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) +#else +static int *CallerOffset; +int ClassifyLineBackpatchC (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) +#endif +{ + // Select the routine based on SSE2 availability and patch the caller so that + // they call that routine directly next time instead of going through here. + int *calleroffset; + int diff; + int (*func)(node_t &, const FSimpleVert *, const FSimpleVert *, int[2]); + +#ifdef __GNUC__ + calleroffset = (int *)__builtin_return_address(0); +#else + calleroffset = CallerOffset; +#endif +// printf ("Patching for SSE %d @ %p %d\n", SSELevel, calleroffset, *calleroffset); + + if (CPU.bSSE2) + { + func = ClassifyLineSSE2; + diff = (char *)ClassifyLineSSE2 - (char *)calleroffset; + } + else + { + func = ClassifyLine2; + diff = (char *)ClassifyLine2 - (char *)calleroffset; + } + + calleroffset--; + // Patch the caller. +#ifdef _WIN32 + unsigned long oldprotect; + if (VirtualProtect (calleroffset, 4, PAGE_EXECUTE_READWRITE, &oldprotect)) +#else + // must make this page-aligned for mprotect + long pagesize = sysconf(_SC_PAGESIZE); + char *callerpage = (char *)((intptr_t)calleroffset & ~(pagesize - 1)); + size_t protectlen = (intptr_t)calleroffset + sizeof(void*) - (intptr_t)callerpage; + int ptect; + if (!(ptect = mprotect(callerpage, protectlen, PROT_READ|PROT_WRITE|PROT_EXEC))) +#endif + { + *calleroffset = diff; +#ifdef _WIN32 + VirtualProtect (calleroffset, sizeof(void*), oldprotect, &oldprotect); +#else + mprotect(callerpage, protectlen, PROT_READ|PROT_EXEC); +#endif + } + + // And return by calling the real function. + return func (node, v1, v2, sidev); +} + +#ifndef __GNUC__ +// The ClassifyLineBackpatch() function here is a stub that uses inline assembly and nakedness +// to retrieve the return address of the stack before sending control to the real +// ClassifyLineBackpatchC() function. Since BACKPATCH shouldn't be defined on 64-bit builds, +// we're okay that VC++ can't do inline assembly on that target. + +extern "C" __declspec(noinline) __declspec(naked) int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) +{ + // We store the return address in a global, so as not to need to mess with the parameter list. + __asm + { + mov eax, [esp] + mov CallerOffset, eax + jmp ClassifyLineBackpatchC + } +} +#endif +#endif diff --git a/src/nodebuild.h b/src/nodebuild.h index d0d59e7062..d1ed2cb15b 100644 --- a/src/nodebuild.h +++ b/src/nodebuild.h @@ -1,6 +1,10 @@ #include "doomdata.h" #include "tarray.h" #include "r_defs.h" +#include "x86.h" + +struct FPolySeg; +struct FMiniBSP; struct FEventInfo { @@ -40,6 +44,27 @@ private: FEvent *Predecessor (FEvent *event) const; }; +struct FSimpleVert +{ + fixed_t x, y; +}; + +extern "C" +{ + int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]); +#ifndef DISABLE_SSE + int ClassifyLineSSE1 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]); + int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]); +#ifdef BACKPATCH +#ifdef __GNUC__ + int ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) __attribute__((noinline)); +#else + int __declspec(noinline) ClassifyLineBackpatch (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]); +#endif +#endif +#endif +} + class FNodeBuilder { struct FPrivSeg @@ -60,9 +85,8 @@ class FNodeBuilder bool planefront; FPrivSeg *hashnext; }; - struct FPrivVert + struct FPrivVert : FSimpleVert { - fixed_t x, y; DWORD segs; // segs that use this vertex as v1 DWORD segs2; // segs that use this vertex as v2 @@ -88,7 +112,17 @@ class FNodeBuilder }; // Like a blockmap, but for vertices instead of lines - class FVertexMap + class IVertexMap + { + public: + virtual ~IVertexMap(); + virtual int SelectVertexExact(FPrivVert &vert) = 0; + virtual int SelectVertexClose(FPrivVert &vert) = 0; + private: + IVertexMap &operator=(const IVertexMap &); + }; + + class FVertexMap : public IVertexMap { public: FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy); @@ -116,12 +150,23 @@ class FNodeBuilder assert (y <= MaxY); return (unsigned(x - MinX) >> BLOCK_SHIFT) + (unsigned(y - MinY) >> BLOCK_SHIFT) * BlocksWide; } + }; - FVertexMap &operator= (const FVertexMap &) { return *this; } + class FVertexMapSimple : public IVertexMap + { + public: + FVertexMapSimple(FNodeBuilder &builder); + + int SelectVertexExact(FPrivVert &vert); + int SelectVertexClose(FPrivVert &vert); + private: + int InsertVertex(FPrivVert &vert); + + FNodeBuilder &MyBuilder; }; friend class FVertexMap; - + friend class FVertexMapSimple; public: struct FLevel @@ -132,7 +177,14 @@ public: fixed_t MinX, MinY, MaxX, MaxY; - void FindMapBounds (); + void FindMapBounds(); + void ResetMapBounds() + { + MinX = FIXED_MAX; + MinY = FIXED_MAX; + MaxX = FIXED_MIN; + MaxY = FIXED_MIN; + } }; struct FPolyStart @@ -141,9 +193,10 @@ public: fixed_t x, y; }; + FNodeBuilder (FLevel &level); FNodeBuilder (FLevel &level, TArray &polyspots, TArray &anchors, - bool makeGLNodes, bool enableSSE2); + bool makeGLNodes); ~FNodeBuilder (); void Extract (node_t *&nodes, int &nodeCount, @@ -151,6 +204,13 @@ public: subsector_t *&ssecs, int &subCount, vertex_t *&verts, int &vertCount); + // These are used for building sub-BSP trees for polyobjects. + void Clear(); + void AddPolySegs(FPolySeg *segs, int numsegs); + void AddSegs(seg_t *segs, int numsegs); + void BuildMini(bool makeGLNodes); + void ExtractMini(FMiniBSP *bsp); + static angle_t PointToAngle (fixed_t dx, fixed_t dy); // < 0 : in front of line @@ -160,7 +220,7 @@ public: static inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy); private: - FVertexMap *VertexMap; + IVertexMap *VertexMap; TArray Nodes; TArray Subsectors; @@ -181,7 +241,6 @@ private: DWORD HackMate; // Seg to use in front of hack seg FLevel &Level; bool GLNodes; // Add minisegs to make GL nodes? - bool EnableSSE2; // Progress meter stuff int SegsStuffed; @@ -191,29 +250,27 @@ private: void MakeSegsFromSides (); int CreateSeg (int linenum, int sidenum); void GroupSegPlanes (); + void GroupSegPlanesSimple (); void FindPolyContainers (TArray &spots, TArray &anchors); bool GetPolyExtents (int polynum, fixed_t bbox[4]); int MarkLoop (DWORD firstseg, int loopnum); void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg); - int CreateNode (DWORD set, fixed_t bbox[4]); + int CreateNode (DWORD set, unsigned int count, fixed_t bbox[4]); int CreateSubsector (DWORD set, fixed_t bbox[4]); void CreateSubsectorsForReal (); - bool CheckSubsector (DWORD set, node_t &node, DWORD &splitseg, int setsize); + bool CheckSubsector (DWORD set, node_t &node, DWORD &splitseg); 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); + void SplitSegs (DWORD set, node_t &node, DWORD splitseg, DWORD &outset0, DWORD &outset1, unsigned int &count0, unsigned int &count1); DWORD SplitSeg (DWORD segnum, int splitvert, int v1InFront); int Heuristic (node_t &node, DWORD set, bool honorNoSplit); - int CountSegs (DWORD set) const; // Returns: // 0 = seg is in front // 1 = seg is in back // -1 = seg cuts the node - inline int ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); - int ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); - int ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2); + inline int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]); void FixSplitSharers (const node_t &node); double AddIntersection (const node_t &node, int vertex); @@ -273,21 +330,27 @@ inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int return s_num > 0.0 ? -1 : 1; } -inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) +inline int FNodeBuilder::ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]) { -#if !defined(_M_IX86) && !defined(_M_X64) && !defined(__i386__) && !defined(__amd64__) - return ClassifyLine2 (node, seg, sidev1, sidev2); -#elif defined(__SSE2__) +#ifdef DISABLE_SSE + return ClassifyLine2 (node, v1, v2, sidev); +#else +#if defined(__SSE2__) || defined(_M_IX64) // If compiling with SSE2 support everywhere, just use the SSE2 version. - return ClassifyLineSSE2 (node, seg, sidev1, sidev2); + return ClassifyLineSSE2 (node, v1, v2, sidev); #elif defined(_MSC_VER) && _MSC_VER < 1300 - // VC 6 does not support SSE2 optimizations. - return ClassifyLine2 (node, seg, sidev1, sidev2); + // VC 6 does not support SSE optimizations. + return ClassifyLine2 (node, v1, v2, sidev); #else // Select the routine based on our flag. - if (EnableSSE2) - return ClassifyLineSSE2 (node, seg, sidev1, sidev2); +#ifdef BACKPATCH + return ClassifyLineBackpatch (node, v1, v2, sidev); +#else + if (CPU.bSSE2) + return ClassifyLineSSE2 (node, v1, v2, sidev); else - return ClassifyLine2 (node, seg, sidev1, sidev2); + return ClassifyLine2 (node, v1, v2, sidev); +#endif +#endif #endif } diff --git a/src/nodebuild_classify_nosse2.cpp b/src/nodebuild_classify_nosse2.cpp index 2b7f6bec6b..3fd1da2726 100644 --- a/src/nodebuild_classify_nosse2.cpp +++ b/src/nodebuild_classify_nosse2.cpp @@ -3,11 +3,8 @@ #define FAR_ENOUGH 17179869184.f // 4<<32 -int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) +extern "C" int ClassifyLine2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) { - const FPrivVert *v1 = &Vertices[seg->v1]; - const FPrivVert *v2 = &Vertices[seg->v2]; - double d_x1 = double(node.x); double d_y1 = double(node.y); double d_dx = double(node.dx); @@ -26,13 +23,13 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, { if (s_num2 <= -FAR_ENOUGH) { - sidev1 = sidev2 = 1; + sidev[0] = sidev[1] = 1; return 1; } if (s_num2 >= FAR_ENOUGH) { - sidev1 = 1; - sidev2 = -1; + sidev[0] = 1; + sidev[1] = -1; return -1; } nears = 1; @@ -41,13 +38,13 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, { if (s_num2 >= FAR_ENOUGH) { - sidev1 = sidev2 = -1; + sidev[0] = sidev[1] = -1; return 0; } if (s_num2 <= -FAR_ENOUGH) { - sidev1 = -1; - sidev2 = 1; + sidev[0] = -1; + sidev[1] = 1; return -1; } nears = 1; @@ -65,41 +62,41 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, double dist = s_num1 * s_num1 * l; if (dist < SIDE_EPSILON*SIDE_EPSILON) { - sidev1 = 0; + sidev[0] = 0; } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; } } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; } if (nears & 1) { double dist = s_num2 * s_num2 * l; if (dist < SIDE_EPSILON*SIDE_EPSILON) { - sidev2 = 0; + sidev[1] = 0; } else { - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } } else { - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } - if ((sidev1 | sidev2) == 0) + if ((sidev[0] | sidev[1]) == 0) { // seg is coplanar with the splitter, so use its orientation to determine // which child it ends up in. If it faces the same direction as the splitter, // it goes in front. Otherwise, it goes in back. @@ -127,11 +124,11 @@ int FNodeBuilder::ClassifyLine2 (node_t &node, const FPrivSeg *seg, int &sidev1, } } } - else if (sidev1 <= 0 && sidev2 <= 0) + else if (sidev[0] <= 0 && sidev[1] <= 0) { return 0; } - else if (sidev1 >= 0 && sidev2 >= 0) + else if (sidev[0] >= 0 && sidev[1] >= 0) { return 1; } diff --git a/src/nodebuild_classify_sse2.cpp b/src/nodebuild_classify_sse2.cpp index 05e4684a82..01c469093c 100644 --- a/src/nodebuild_classify_sse2.cpp +++ b/src/nodebuild_classify_sse2.cpp @@ -1,18 +1,16 @@ +#ifndef DISABLE_SSE + #include "doomtype.h" #include "nodebuild.h" #define FAR_ENOUGH 17179869184.f // 4<<32 -// This function is identical to the ClassifyLine2 version. So how does it use SSE2? -// Easy! By explicitly enabling SSE2 in the configuration properties for this one -// file, we can build it with SSE2 enabled without forcing SSE2 on the rest of the -// project. +// You may notice that this function is identical to ClassifyLine2. +// The reason it is SSE2 is because this file is explicitly compiled +// with SSE2 math enabled, but the other files are not. -int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &sidev1, int &sidev2) +extern "C" int ClassifyLineSSE2 (node_t &node, const FSimpleVert *v1, const FSimpleVert *v2, int sidev[2]) { - const FPrivVert *v1 = &Vertices[seg->v1]; - const FPrivVert *v2 = &Vertices[seg->v2]; - double d_x1 = double(node.x); double d_y1 = double(node.y); double d_dx = double(node.dx); @@ -31,13 +29,13 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side { if (s_num2 <= -FAR_ENOUGH) { - sidev1 = sidev2 = 1; + sidev[0] = sidev[1] = 1; return 1; } if (s_num2 >= FAR_ENOUGH) { - sidev1 = 1; - sidev2 = -1; + sidev[0] = 1; + sidev[1] = -1; return -1; } nears = 1; @@ -46,13 +44,13 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side { if (s_num2 >= FAR_ENOUGH) { - sidev1 = sidev2 = -1; + sidev[0] = sidev[1] = -1; return 0; } if (s_num2 <= -FAR_ENOUGH) { - sidev1 = -1; - sidev2 = 1; + sidev[0] = -1; + sidev[1] = 1; return -1; } nears = 1; @@ -70,41 +68,41 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side double dist = s_num1 * s_num1 * l; if (dist < SIDE_EPSILON*SIDE_EPSILON) { - sidev1 = 0; + sidev[0] = 0; } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; } } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; } if (nears & 1) { double dist = s_num2 * s_num2 * l; if (dist < SIDE_EPSILON*SIDE_EPSILON) { - sidev2 = 0; + sidev[1] = 0; } else { - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } } else { - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } } else { - sidev1 = s_num1 > 0.0 ? -1 : 1; - sidev2 = s_num2 > 0.0 ? -1 : 1; + sidev[0] = s_num1 > 0.0 ? -1 : 1; + sidev[1] = s_num2 > 0.0 ? -1 : 1; } - if ((sidev1 | sidev2) == 0) + if ((sidev[0] | sidev[1]) == 0) { // seg is coplanar with the splitter, so use its orientation to determine // which child it ends up in. If it faces the same direction as the splitter, // it goes in front. Otherwise, it goes in back. @@ -132,13 +130,15 @@ int FNodeBuilder::ClassifyLineSSE2 (node_t &node, const FPrivSeg *seg, int &side } } } - else if (sidev1 <= 0 && sidev2 <= 0) + else if (sidev[0] <= 0 && sidev[1] <= 0) { return 0; } - else if (sidev1 >= 0 && sidev2 >= 0) + else if (sidev[0] >= 0 && sidev[1] >= 0) { return 1; } return -1; } + +#endif diff --git a/src/nodebuild_extract.cpp b/src/nodebuild_extract.cpp index 77fa4dec79..24fadcf3b4 100644 --- a/src/nodebuild_extract.cpp +++ b/src/nodebuild_extract.cpp @@ -105,7 +105,7 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount, { DWORD numsegs = CloseSubsector (segs, i, outVerts); outSubs[i].numlines = numsegs; - outSubs[i].firstline = segs.Size() - numsegs; + outSubs[i].firstline = (seg_t *)(size_t)(segs.Size() - numsegs); } segCount = segs.Size (); @@ -142,6 +142,10 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount, out->bPolySeg = false; } } + for (i = 0; i < subCount; ++i) + { + outSubs[i].firstline = &outSegs[(size_t)outSubs[i].firstline]; + } D(Printf("%i segs, %i nodes, %i subsectors\n", segCount, nodeCount, subCount)); @@ -152,6 +156,86 @@ void FNodeBuilder::Extract (node_t *&outNodes, int &nodeCount, } } +void FNodeBuilder::ExtractMini (FMiniBSP *bsp) +{ + unsigned int i; + + bsp->Verts.Resize(Vertices.Size()); + for (i = 0; i < Vertices.Size(); ++i) + { + bsp->Verts[i].x = Vertices[i].x; + bsp->Verts[i].y = Vertices[i].y; + } + + bsp->Subsectors.Resize(Subsectors.Size()); + memset(&bsp->Subsectors[0], 0, Subsectors.Size() * sizeof(subsector_t)); + + bsp->Nodes.Resize(Nodes.Size()); + memcpy(&bsp->Nodes[0], &Nodes[0], Nodes.Size()*sizeof(node_t)); + for (i = 0; i < Nodes.Size(); ++i) + { + D(Printf(PRINT_LOG, "Node %d:\n", i)); + // Go backwards because on 64-bit systems, both of the intchildren are + // inside the first in-game child. + for (int j = 1; j >= 0; --j) + { + if (bsp->Nodes[i].intchildren[j] & 0x80000000) + { + D(Printf(PRINT_LOG, " subsector %d\n", bsp->Nodes[i].intchildren[j] & 0x7FFFFFFF)); + bsp->Nodes[i].children[j] = (BYTE *)&bsp->Subsectors[bsp->Nodes[i].intchildren[j] & 0x7fffffff] + 1; + } + else + { + D(Printf(PRINT_LOG, " node %d\n", bsp->Nodes[i].intchildren[j])); + bsp->Nodes[i].children[j] = &bsp->Nodes[bsp->Nodes[i].intchildren[j]]; + } + } + } + + if (GLNodes) + { + for (i = 0; i < Subsectors.Size(); ++i) + { + DWORD numsegs = CloseSubsector (bsp->Segs, i, &bsp->Verts[0]); + bsp->Subsectors[i].numlines = numsegs; + bsp->Subsectors[i].firstline = &bsp->Segs[bsp->Segs.Size() - numsegs]; + } + + for (i = 0; i < Segs.Size(); ++i) + { + if (bsp->Segs[i].PartnerSeg != NULL) + { + bsp->Segs[i].PartnerSeg = &bsp->Segs[Segs[(unsigned int)(size_t)bsp->Segs[i].PartnerSeg-1].storedseg]; + } + } + } + else + { + memcpy(&bsp->Subsectors[0], &Subsectors[0], Subsectors.Size()*sizeof(subsector_t)); + bsp->Segs.Resize(Segs.Size()); + for (i = 0; i < Segs.Size(); ++i) + { + const FPrivSeg *org = &Segs[SegList[i].SegNum]; + seg_t *out = &bsp->Segs[i]; + + D(Printf(PRINT_LOG, "Seg %d: v1(%d) -> v2(%d)\n", i, org->v1, org->v2)); + + out->v1 = &bsp->Verts[org->v1]; + out->v2 = &bsp->Verts[org->v2]; + out->backsector = org->backsector; + out->frontsector = org->frontsector; + out->linedef = Level.Lines + org->linedef; + out->sidedef = Level.Sides + org->sidedef; + out->PartnerSeg = NULL; + out->bPolySeg = false; + } + for (i = 0; i < bsp->Subsectors.Size(); ++i) + { + bsp->Subsectors[i].firstline = &bsp->Segs[(size_t)bsp->Subsectors[i].firstline]; + } + } +} + int FNodeBuilder::CloseSubsector (TArray &segs, int subsector, vertex_t *outVerts) { FPrivSeg *seg, *prev; @@ -163,7 +247,7 @@ int FNodeBuilder::CloseSubsector (TArray &segs, int subsector, vertex_t * bool diffplanes; int firstplane; - first = Subsectors[subsector].firstline; + first = (DWORD)(size_t)Subsectors[subsector].firstline; max = first + Subsectors[subsector].numlines; count = 0; @@ -322,7 +406,7 @@ int FNodeBuilder::OutputDegenerateSubsector (TArray &segs, int subsector, double dot, x1, y1, dx, dy, dx2, dy2; bool wantside; - first = Subsectors[subsector].firstline; + first = (DWORD)(size_t)Subsectors[subsector].firstline; max = first + Subsectors[subsector].numlines; count = 0; @@ -401,7 +485,6 @@ DWORD FNodeBuilder::PushGLSeg (TArray &segs, const FPrivSeg *seg, vertex_ } newseg.PartnerSeg = (seg_t *)(seg->partner == DWORD_MAX ? 0 : (size_t)seg->partner + 1); newseg.bPolySeg = false; - newseg.Subsector = NULL; return (DWORD)segs.Push (newseg); } @@ -417,6 +500,5 @@ void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray &segs, vert newseg.sidedef = NULL; newseg.PartnerSeg = NULL; newseg.bPolySeg = false; - newseg.Subsector = NULL; segs.Push (newseg); } diff --git a/src/nodebuild_utility.cpp b/src/nodebuild_utility.cpp index a8a618b9ff..ef79ac4d95 100644 --- a/src/nodebuild_utility.cpp +++ b/src/nodebuild_utility.cpp @@ -49,6 +49,7 @@ #include "m_bbox.h" #include "r_main.h" #include "i_system.h" +#include "po_man.h" static const int PO_LINE_START = 1; static const int PO_LINE_EXPLICIT = 5; @@ -177,6 +178,86 @@ int FNodeBuilder::CreateSeg (int linenum, int sidenum) return segnum; } +// For every seg, create FPrivSegs and FPrivVerts. + +void FNodeBuilder::AddSegs(seg_t *segs, int numsegs) +{ + assert(numsegs > 0); + + for (int i = 0; i < numsegs; ++i) + { + FPrivSeg seg; + FPrivVert vert; + int segnum; + + seg.next = DWORD_MAX; + seg.loopnum = 0; + seg.partner = DWORD_MAX; + seg.hashnext = NULL; + seg.planefront = false; + seg.planenum = DWORD_MAX; + seg.storedseg = DWORD_MAX; + + seg.frontsector = segs[i].frontsector; + seg.backsector = segs[i].backsector; + vert.x = segs[i].v1->x; + vert.y = segs[i].v1->y; + seg.v1 = VertexMap->SelectVertexExact(vert); + vert.x = segs[i].v2->x; + vert.y = segs[i].v2->y; + seg.v2 = VertexMap->SelectVertexExact(vert); + seg.linedef = int(segs[i].linedef - Level.Lines); + seg.sidedef = segs[i].sidedef != NULL ? int(segs[i].sidedef - Level.Sides) : int(NO_SIDE); + 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; + } +} + +void FNodeBuilder::AddPolySegs(FPolySeg *segs, int numsegs) +{ + assert(numsegs > 0); + + for (int i = 0; i < numsegs; ++i) + { + FPrivSeg seg; + FPrivVert vert; + int segnum; + + seg.next = DWORD_MAX; + seg.loopnum = 0; + seg.partner = DWORD_MAX; + seg.hashnext = NULL; + seg.planefront = false; + seg.planenum = DWORD_MAX; + seg.storedseg = DWORD_MAX; + + side_t *side = segs[i].wall; + assert(side != NULL); + + seg.frontsector = side->sector; + seg.backsector = side->linedef->frontsector == side->sector ? side->linedef->backsector : side->linedef->frontsector; + vert.x = segs[i].v1.x; + vert.y = segs[i].v1.y; + seg.v1 = VertexMap->SelectVertexExact(vert); + vert.x = segs[i].v2.x; + vert.y = segs[i].v2.y; + seg.v2 = VertexMap->SelectVertexExact(vert); + seg.linedef = int(side->linedef - Level.Lines); + seg.sidedef = int(side - Level.Sides); + 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; + } +} + + // Group colinear segs together so that only one seg per line needs to be checked // by SelectSplitter(). @@ -269,6 +350,27 @@ void FNodeBuilder::GroupSegPlanes () PlaneChecked.Reserve ((planenum + 7) / 8); } +// Just create one plane per seg. Should be good enough for mini BSPs. +void FNodeBuilder::GroupSegPlanesSimple() +{ + Planes.Resize(Segs.Size()); + for (int i = 0; i < (int)Segs.Size(); ++i) + { + FPrivSeg *seg = &Segs[i]; + FSimpleLine *pline = &Planes[i]; + seg->next = i+1; + seg->hashnext = NULL; + seg->planenum = i; + seg->planefront = true; + pline->x = Vertices[seg->v1].x; + pline->y = Vertices[seg->v1].y; + pline->dx = Vertices[seg->v2].x - Vertices[seg->v1].x; + pline->dy = Vertices[seg->v2].y - Vertices[seg->v1].y; + } + Segs.Last().next = DWORD_MAX; + PlaneChecked.Reserve((Segs.Size() + 7) / 8); +} + // Find "loops" of segs surrounding polyobject's origin. Note that a polyobject's origin // is not solely defined by the polyobject's anchor, but also by the polyobject itself. // For the split avoidance to work properly, you must have a convex, complete loop of @@ -507,6 +609,10 @@ void FNodeBuilder::FLevel::FindMapBounds () MaxY = maxy; } +FNodeBuilder::IVertexMap::~IVertexMap() +{ +} + FNodeBuilder::FVertexMap::FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy) : MyBuilder(builder) @@ -606,3 +712,52 @@ int FNodeBuilder::FVertexMap::InsertVertex (FNodeBuilder::FPrivVert &vert) return vertnum; } + +FNodeBuilder::FVertexMapSimple::FVertexMapSimple(FNodeBuilder &builder) + : MyBuilder(builder) +{ +} + +int FNodeBuilder::FVertexMapSimple::SelectVertexExact(FNodeBuilder::FPrivVert &vert) +{ + FPrivVert *verts = &MyBuilder.Vertices[0]; + unsigned int stop = MyBuilder.Vertices.Size(); + + for (unsigned int i = 0; i < stop; ++i) + { + if (verts[i].x == vert.x && verts[i].y == vert.y) + { + return i; + } + } + // Not present: add it! + return InsertVertex(vert); +} + +int FNodeBuilder::FVertexMapSimple::SelectVertexClose(FNodeBuilder::FPrivVert &vert) +{ + FPrivVert *verts = &MyBuilder.Vertices[0]; + unsigned int stop = MyBuilder.Vertices.Size(); + + for (unsigned int i = 0; i < stop; ++i) + { +#if VERTEX_EPSILON <= 1 + if (verts[i].x == vert.x && verts[i].y == y) +#else + if (abs(verts[i].x - vert.x) < VERTEX_EPSILON && + abs(verts[i].y - vert.y) < VERTEX_EPSILON) +#endif + { + return i; + } + } + // Not present: add it! + return InsertVertex (vert); +} + +int FNodeBuilder::FVertexMapSimple::InsertVertex (FNodeBuilder::FPrivVert &vert) +{ + vert.segs = DWORD_MAX; + vert.segs2 = DWORD_MAX; + return (int)MyBuilder.Vertices.Push (vert); +} diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 8ade6cb136..452cb0dcf5 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -855,7 +855,7 @@ void P_LoadGLZSegs (FileReaderBase &data, int type) } data >> side; - seg = &segs[subsectors[i].firstline + j]; + seg = subsectors[i].firstline + j; seg->v1 = &vertexes[v1]; if (j == 0) { @@ -894,7 +894,7 @@ void P_LoadGLZSegs (FileReaderBase &data, int type) { seg->linedef = NULL; seg->sidedef = NULL; - seg->frontsector = seg->backsector = segs[subsectors[i].firstline].frontsector; + seg->frontsector = seg->backsector = subsectors[i].firstline->frontsector; } } } @@ -952,7 +952,7 @@ static void LoadZNodes(FileReaderBase &data, int glnodes) DWORD numsegs; data >> numsegs; - subsectors[i].firstline = currSeg; + subsectors[i].firstline = (seg_t *)(size_t)currSeg; // Oh damn. I should have stored the seg count sooner. subsectors[i].numlines = numsegs; currSeg += numsegs; } @@ -976,6 +976,11 @@ static void LoadZNodes(FileReaderBase &data, int glnodes) segs = new seg_t[numsegs]; memset (segs, 0, numsegs*sizeof(seg_t)); + for (i = 0; i < numSubs; ++i) + { + subsectors[i].firstline = &segs[(size_t)subsectors[i].firstline]; + } + if (glnodes == 0) { P_LoadZSegs (data); @@ -1141,6 +1146,11 @@ void P_LoadSegs (MapData * map) data = new BYTE[lumplen]; map->Read(ML_SEGS, data); + for (i = 0; i < numsubsectors; ++i) + { + subsectors[i].firstline = &segs[(size_t)subsectors[i].firstline]; + } + // phares: 10/4/98: Vertchanged is an array that represents the vertices. // Mark those used by linedefs. A marked vertex is one that is not a // candidate for movement further down. @@ -1329,9 +1339,9 @@ void P_LoadSubsectors (MapData * map) } subsectors[i].numlines = subd.numsegs; - subsectors[i].firstline = subd.firstseg; + subsectors[i].firstline = (seg_t *)(size_t)subd.firstseg; - if (subsectors[i].firstline >= maxseg) + if ((size_t)subsectors[i].firstline >= maxseg) { Printf ("Subsector %d contains invalid segs %u-%u\n" "The BSP will be rebuilt.\n", i, subsectors[i].firstline, @@ -1341,7 +1351,7 @@ void P_LoadSubsectors (MapData * map) delete[] subsectors; break; } - else if (subsectors[i].firstline + subsectors[i].numlines > maxseg) + else if ((size_t)subsectors[i].firstline + subsectors[i].numlines > maxseg) { Printf ("Subsector %d contains invalid segs %u-%u\n" "The BSP will be rebuilt.\n", i, maxseg, @@ -2910,20 +2920,7 @@ static void P_GroupLines (bool buildmap) times[0].Clock(); for (i = 0; i < numsubsectors; i++) { - subsectors[i].sector = segs[subsectors[i].firstline].sidedef->sector; - subsectors[i].validcount = validcount; - - double accumx = 0.0, accumy = 0.0; - - for (jj = 0; jj < subsectors[i].numlines; ++jj) - { - seg_t *seg = &segs[subsectors[i].firstline + jj]; - seg->Subsector = &subsectors[i]; - accumx += seg->v1->x + seg->v2->x; - accumy += seg->v1->y + seg->v2->y; - } - subsectors[i].CenterX = fixed_t(accumx * 0.5 / subsectors[i].numlines); - subsectors[i].CenterY = fixed_t(accumy * 0.5 / subsectors[i].numlines); + subsectors[i].sector = subsectors[i].firstline->sidedef->sector; } times[0].Unclock(); @@ -3369,6 +3366,13 @@ void P_FreeLevelData () } if (subsectors != NULL) { + for (int i = 0; i < numsubsectors; ++i) + { + if (subsectors[i].BSP != NULL) + { + delete subsectors[i].BSP; + } + } delete[] subsectors; subsectors = NULL; } @@ -3786,7 +3790,7 @@ void P_SetupLevel (char *lumpname, int position) }; leveldata.FindMapBounds (); UsingGLNodes |= genglnodes; - FNodeBuilder builder (leveldata, polyspots, anchors, UsingGLNodes, CPU.bSSE2); + FNodeBuilder builder (leveldata, polyspots, anchors, UsingGLNodes); delete[] vertexes; builder.Extract (nodes, numnodes, segs, numsegs, diff --git a/src/po_man.cpp b/src/po_man.cpp index 6b208825da..c240e28fa2 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -142,6 +142,9 @@ static void TranslateToStartSpot (int tag, int originX, int originY); static void DoMovePolyobj (FPolyObj *po, int x, int y); static void InitSegLists (); static void KillSegLists (); +static FPolyNode *NewPolyNode(); +static void FreePolyNode(); +static void ReleaseAllPolyNodes(); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -157,6 +160,7 @@ polyspawns_t *polyspawns; // [RH] Let P_SpawnMapThings() find our thingies for u // PRIVATE DATA DEFINITIONS ------------------------------------------------ static TArray KnownPolySides; +static FPolyNode *FreePolyNodes; // CODE -------------------------------------------------------------------- @@ -758,7 +762,6 @@ FPolyObj::FPolyObj() subsectorlinks = NULL; specialdata = NULL; interpolation = NULL; - SVIndex = -1; } //========================================================================== @@ -1279,20 +1282,6 @@ void FPolyObj::ClosestPoint(fixed_t fx, fixed_t fy, fixed_t &ox, fixed_t &oy, si } } -vertex_t *FPolyObj::GetNewVertex() -{ - if (SVIndex == ~0u || SplitVertices[SVIndex]->used == 10) - { - SVIndex++; - if (SVIndex >= SplitVertices.Size()) - { - SplitVertices.Push(new FPolyVertexBlock); - } - SplitVertices[SVIndex]->clear(); - } - return &SplitVertices[SVIndex]->vertices[SplitVertices[SVIndex]->used++]; -} - //========================================================================== // // InitBlockMap @@ -1699,11 +1688,6 @@ bool PO_Busy (int polyobj) void FPolyObj::ClearSubsectorLinks() { - for(unsigned i=0; iclear(); - } - SVIndex = -1; while (subsectorlinks != NULL) { assert(subsectorlinks->state == 1337); @@ -1725,6 +1709,12 @@ void FPolyObj::ClearSubsectorLinks() { subsectorlinks->subsector->polys = subsectorlinks->pnext; } + + if (subsectorlinks->subsector->BSP != NULL) + { + subsectorlinks->subsector->BSP->bDirty = true; + } + subsectorlinks->state = -1; delete subsectorlinks; subsectorlinks = next; @@ -1734,10 +1724,11 @@ void FPolyObj::ClearSubsectorLinks() void FPolyObj::ClearAllSubsectorLinks() { - for(int i=0;iv1->x; - double v2y = (double)seg->v1->y; - double v2dx = (double)(seg->v2->x - seg->v1->x); - double v2dy = (double)(seg->v2->y - seg->v1->y); + double v2x = (double)seg->v1.x; + double v2y = (double)seg->v1.y; + double v2dx = (double)(seg->v2.x - seg->v1.x); + double v2dy = (double)(seg->v2.y - seg->v1.y); double v1x = (double)bsp->x; double v1y = (double)bsp->y; double v1dx = (double)bsp->dx; @@ -1786,7 +1777,7 @@ static bool GetIntersection(seg_t *seg, node_t *bsp, vertex_t *v) // //========================================================================== -static double PartitionDistance(vertex_t *vt, node_t *node) +static double PartitionDistance(FPolyVertex *vt, node_t *node) { return fabs(double(-node->dy) * (vt->x - node->x) + double(node->dx) * (vt->y - node->y)) / node->len; } @@ -1823,7 +1814,7 @@ static void AddToBBox(fixed_t child[4], fixed_t parent[4]) // //========================================================================== -static void AddToBBox(vertex_t *v, fixed_t bbox[4]) +static void AddToBBox(FPolyVertex *v, fixed_t bbox[4]) { if (v->x < bbox[BOXLEFT]) { @@ -1851,7 +1842,7 @@ static void AddToBBox(vertex_t *v, fixed_t bbox[4]) static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4]) { - static TArray lists[2]; + static TArray lists[2]; static const double POLY_EPSILON = 0.3125; if (!((size_t)node & 1)) // Keep going until found a subsector @@ -1864,7 +1855,7 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4]) lists[1].Clear(); for(unsigned i=0;isegs.Size(); i++) { - seg_t *seg = &pnode->segs[i]; + FPolySeg *seg = &pnode->segs[i]; // Parts of the following code were taken from Eternity and are // being used with permission. @@ -1872,8 +1863,8 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4]) // get distance of vertices from partition line // If the distance is too small, we may decide to // change our idea of sidedness. - double dist_v1 = PartitionDistance(seg->v1, bsp); - double dist_v2 = PartitionDistance(seg->v2, bsp); + double dist_v1 = PartitionDistance(&seg->v1, bsp); + double dist_v2 = PartitionDistance(&seg->v2, bsp); // If the distances are less than epsilon, consider the points as being // on the same side as the polyobj origin. Why? People like to build @@ -1894,27 +1885,27 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4]) } else { - int side = R_PointOnSide(seg->v2->x, seg->v2->y, bsp); + int side = R_PointOnSide(seg->v2.x, seg->v2.y, bsp); lists[side].Push(*seg); } } else if (dist_v2 <= POLY_EPSILON) { - int side = R_PointOnSide(seg->v1->x, seg->v1->y, bsp); + int side = R_PointOnSide(seg->v1.x, seg->v1.y, bsp); lists[side].Push(*seg); } else { - int side1 = R_PointOnSide(seg->v1->x, seg->v1->y, bsp); - int side2 = R_PointOnSide(seg->v2->x, seg->v2->y, bsp); + int side1 = R_PointOnSide(seg->v1.x, seg->v1.y, bsp); + int side2 = R_PointOnSide(seg->v2.x, seg->v2.y, bsp); if(side1 != side2) { // if the partition line crosses this seg, we must split it. - vertex_t *vert = pnode->poly->GetNewVertex(); + FPolyVertex vert; - if (GetIntersection(seg, bsp, vert)) + if (GetIntersection(seg, bsp, &vert)) { lists[0].Push(*seg); lists[1].Push(*seg); @@ -1947,13 +1938,8 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4]) else { // create the new node - FPolyNode *newnode = new FPolyNode; - newnode->state = 1337; + FPolyNode *newnode = NewPolyNode(); newnode->poly = pnode->poly; - newnode->pnext = NULL; - newnode->pprev = NULL; - newnode->subsector = NULL; - newnode->snext = NULL; newnode->segs = lists[1]; // set segs for original node @@ -1995,8 +1981,8 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4]) for (unsigned i = 0; i < pnode->segs.Size(); ++i) { - AddToBBox(pnode->segs[i].v1, subbbox); - AddToBBox(pnode->segs[i].v2, subbbox); + AddToBBox(&pnode->segs[i].v1, subbbox); + AddToBBox(&pnode->segs[i].v2, subbbox); } // Potentially expand the parent node's bounding box to contain these bits of polyobject. AddToBBox(subbbox, bbox); @@ -2011,31 +1997,23 @@ static void SplitPoly(FPolyNode *pnode, void *node, fixed_t bbox[4]) void FPolyObj::CreateSubsectorLinks() { - FPolyNode *node = new FPolyNode; - fixed_t dummybbox[4]; + FPolyNode *node = NewPolyNode(); + // Even though we don't care about it, we need to initialize this + // bounding box to something so that Valgrind won't complain about it + // when SplitPoly modifies it. + fixed_t dummybbox[4] = { 0 }; - node->state = 1337; node->poly = this; - node->pnext = NULL; - node->pprev = NULL; - node->snext = NULL; node->segs.Resize(Sidedefs.Size()); for(unsigned i=0; isegs[i]; + FPolySeg *seg = &node->segs[i]; side_t *side = Sidedefs[i]; seg->v1 = side->V1(); seg->v2 = side->V2(); - seg->sidedef = side; - seg->linedef = side->linedef; - seg->frontsector = side->sector; - seg->backsector = side->linedef->frontsector == side->sector? - side->linedef->backsector : side->linedef->frontsector; - seg->Subsector = NULL; - seg->PartnerSeg = NULL; - seg->bPolySeg = true; + seg->wall = side; } SplitPoly(node, nodes + numnodes - 1, dummybbox); } @@ -2055,4 +2033,62 @@ void PO_LinkToSubsectors() polyobjs[i].CreateSubsectorLinks(); } } -} \ No newline at end of file +} + +//========================================================================== +// +// NewPolyNode +// +//========================================================================== + +static FPolyNode *NewPolyNode() +{ + FPolyNode *node; + + if (FreePolyNodes != NULL) + { + node = FreePolyNodes; + FreePolyNodes = node->pnext; + } + else + { + node = new FPolyNode; + } + node->state = 1337; + node->poly = NULL; + node->pnext = NULL; + node->pprev = NULL; + node->subsector = NULL; + node->snext = NULL; + return node; +} + +//========================================================================== +// +// FreePolyNode +// +//========================================================================== + +void FreePolyNode(FPolyNode *node) +{ + node->segs.Clear(); + node->pnext = FreePolyNodes; + FreePolyNodes = node; +} + +//========================================================================== +// +// ReleaseAllPolyNodes +// +//========================================================================== + +void ReleaseAllPolyNodes() +{ + FPolyNode *node, *next; + + for (node = FreePolyNodes; node != NULL; node = next) + { + next = node->pnext; + delete node; + } +} diff --git a/src/po_man.h b/src/po_man.h index 88565cbbfc..3d963f33bf 100644 --- a/src/po_man.h +++ b/src/po_man.h @@ -5,13 +5,32 @@ #include "r_defs.h" #include "m_bbox.h" + +struct FPolyVertex +{ + fixed_t x, y; + + FPolyVertex &operator=(vertex_t *v) + { + x = v->x; + y = v->y; + return *this; + } +}; + +struct FPolySeg +{ + FPolyVertex v1; + FPolyVertex v2; + side_t *wall; +}; + // // Linked lists of polyobjects // struct FPolyObj; struct FPolyNode { - int state; FPolyObj *poly; // owning polyobject FPolyNode *pnext; // next polyobj in list FPolyNode *pprev; // previous polyobj @@ -19,25 +38,8 @@ struct FPolyNode subsector_t *subsector; // containimg subsector FPolyNode *snext; // next subsector - TArray segs; // segs for this node - fixed_t dist; // distance for sorting -}; - -struct FPolyVertex -{ - fixed_t x, y; -}; - -struct FPolyVertexBlock -{ - int used; - vertex_t vertices[10]; - - void clear() - { - used = 0; - //memset(vertices, 0, sizeof(vertices)); - } + TArray segs; // segs for this node + int state; }; // ===== Polyobj data ===== @@ -51,8 +53,6 @@ struct FPolyObj FPolyVertex StartSpot; FPolyVertex CenterSpot; FBoundingBox Bounds; // Bounds in map coordinates - TDeletingArray SplitVertices; - unsigned int SVIndex; angle_t angle; int tag; // reference tag assigned in HereticEd @@ -77,7 +77,6 @@ struct FPolyObj void LinkPolyobj (); void CreateSubsectorLinks(); void ClearSubsectorLinks(); - vertex_t *GetNewVertex(); void CalcCenter(); static void ClearAllSubsectorLinks(); diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 5ba65cdd63..8eebac3d53 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -43,6 +43,7 @@ #include "r_things.h" #include "a_sharedglobal.h" #include "g_level.h" +#include "nodebuild.h" // State. #include "doomstat.h" @@ -108,6 +109,9 @@ WORD MirrorFlags; seg_t *ActiveWallMirror; TArray WallMirrors; +static FNodeBuilder::FLevel PolyNodeLevel; +static FNodeBuilder PolyNodeBuilder(PolyNodeLevel); + CVAR (Bool, r_drawflat, false, 0) // [RH] Don't texture segs? @@ -1009,44 +1013,76 @@ void R_GetExtraLight (int *light, const secplane_t &plane, FExtraLight *el) } -static int STACK_ARGS polycmp(const void *a, const void *b) -{ - const FPolyNode *A = *(FPolyNode **)a; - const FPolyNode *B = *(FPolyNode **)b; - return A->dist - B->dist; + +//========================================================================== +// +// FMiniBSP Constructor +// +//========================================================================== + +FMiniBSP::FMiniBSP() +{ + bDirty = false; } +//========================================================================== +// +// P_BuildPolyBSP +// +//========================================================================== +static void R_BuildPolyBSP(subsector_t *sub) +{ + assert((sub->BSP == NULL || sub->BSP->bDirty) && "BSP computed more than once"); + + // Set up level information for the node builder. + PolyNodeLevel.Sides = sides; + PolyNodeLevel.NumSides = numsides; + PolyNodeLevel.Lines = lines; + PolyNodeLevel.NumLines = numlines; + + // Feed segs to the nodebuilder and build the nodes. + PolyNodeBuilder.Clear(); + PolyNodeBuilder.AddSegs(sub->firstline, sub->numlines); + for (FPolyNode *pn = sub->polys; pn != NULL; pn = pn->pnext) + { + PolyNodeBuilder.AddPolySegs(&pn->segs[0], (int)pn->segs.Size()); + } + PolyNodeBuilder.BuildMini(false); + if (sub->BSP == NULL) + { + sub->BSP = new FMiniBSP; + } + else + { + sub->BSP->bDirty = false; + } + PolyNodeBuilder.ExtractMini(sub->BSP); + for (unsigned int i = 0; i < sub->BSP->Subsectors.Size(); ++i) + { + sub->BSP->Subsectors[i].sector = sub->sector; + } +} + +void R_Subsector (subsector_t *sub); static void R_AddPolyobjs(subsector_t *sub) { - static TArray sortedpolys; - - FPolyNode *pn = sub->polys; - sortedpolys.Clear(); - while (pn != NULL) + if (sub->BSP == NULL || sub->BSP->bDirty) { - sortedpolys.Push(pn); - pn->dist = R_PointToDist2(pn->poly->CenterSpot.x - viewx, pn->poly->CenterSpot.y - viewy); - pn = pn->pnext; + R_BuildPolyBSP(sub); } - if (sortedpolys.Size() > 1) + if (sub->BSP->Nodes.Size() == 0) { - qsort(&sortedpolys[0], sortedpolys.Size(), sizeof (sortedpolys[0]), polycmp); + R_Subsector(&sub->BSP->Subsectors[0]); } - - for(unsigned i=0; isegs.Size(); j++) - { - R_AddLine(&pn->segs[j]); - } + R_RenderBSPNode(&sub->BSP->Nodes.Last()); } } - // // R_Subsector // Determine floor/ceiling planes. @@ -1061,15 +1097,25 @@ void R_Subsector (subsector_t *sub) int floorlightlevel; // killough 3/16/98: set floor lightlevel int ceilinglightlevel; // killough 4/11/98 +#if 0 #ifdef RANGECHECK if (sub - subsectors >= (ptrdiff_t)numsubsectors) I_Error ("R_Subsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); #endif +#endif + + assert(sub->sector != NULL); + + if (sub->polys) + { // Render the polyobjs in the subsector first + R_AddPolyobjs(sub); + return; + } frontsector = sub->sector; frontsector->MoreFlags |= SECF_DRAWN; count = sub->numlines; - line = &segs[sub->firstline]; + line = sub->firstline; // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, @@ -1133,15 +1179,13 @@ void R_Subsector (subsector_t *sub) ceilinglightlevel : floorlightlevel, FakeSide); // [RH] Add particles - int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight); - for (WORD i = ParticlesInSubsec[(unsigned int)(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) - { - R_ProjectParticle (Particles + i, subsectors[sub-subsectors].sector, shade, FakeSide); - } - - if (sub->polys) - { // Render the polyobjs in the subsector first - R_AddPolyobjs(sub); + if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors) + { // Only do it for the main BSP. + int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight); + for (WORD i = ParticlesInSubsec[(unsigned int)(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) + { + R_ProjectParticle (Particles + i, subsectors[sub-subsectors].sector, shade, FakeSide); + } } while (count--) diff --git a/src/r_defs.h b/src/r_defs.h index e6ad904481..8e0d779d27 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -925,22 +925,7 @@ struct msecnode_t }; struct FPolyNode; - -// -// A SubSector. -// References a Sector. -// Basically, this is a list of LineSegs indicating the visible walls that -// define (all or some) sides of a convex BSP leaf. -// -struct subsector_t -{ - sector_t *sector; - DWORD numlines; - DWORD firstline; - FPolyNode *polys; - int validcount; - fixed_t CenterX, CenterY; -}; +struct FMiniBSP; // // The LineSeg. @@ -957,12 +942,27 @@ struct seg_t sector_t* frontsector; sector_t* backsector; // NULL for one-sided lines - subsector_t* Subsector; seg_t* PartnerSeg; BITFIELD bPolySeg:1; }; +// +// A SubSector. +// References a Sector. +// Basically, this is a list of LineSegs indicating the visible walls that +// define (all or some) sides of a convex BSP leaf. +// +struct subsector_t +{ + sector_t *sector; + FPolyNode *polys; + FMiniBSP *BSP; + seg_t *firstline; + DWORD numlines; +}; + + // @@ -985,6 +985,22 @@ struct node_t }; +// An entire BSP tree. + +struct FMiniBSP +{ + FMiniBSP(); + + bool bDirty; + + TArray Nodes; + TArray Segs; + TArray Subsectors; + TArray Verts; +}; + + + // posts are runs of non masked source pixels struct column_t { diff --git a/src/r_polymost.cpp b/src/r_polymost.cpp index 663c8ae611..2cb3e22256 100644 --- a/src/r_polymost.cpp +++ b/src/r_polymost.cpp @@ -1401,7 +1401,7 @@ void RP_Subsector (subsector_t *sub) frontsector = sub->sector; count = sub->numlines; - line = &segs[sub->firstline]; + line = sub->firstline; // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, diff --git a/src/r_segs.cpp b/src/r_segs.cpp index d1983342f4..5bb3a09fcf 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -1480,17 +1480,17 @@ int side_t::GetLightLevel (bool foggy, int baselight, int *pfakecontrast) const if (((level.flags2 & LEVEL2_SMOOTHLIGHTING) || (Flags & WALLF_SMOOTHLIGHTING) || r_fakecontrast == 2) && linedef->dx != 0) { - rel = int // OMG LEE KILLOUGH LIVES! :/ + rel = xs_RoundToInt // OMG LEE KILLOUGH LIVES! :/ ( - (float(level.WallHorizLight) - +abs(atan(float(linedef->dy)/float(linedef->dx))/float(1.57079)) - *float(level.WallVertLight - level.WallHorizLight)) + level.WallHorizLight + + fabs(atan(double(linedef->dy) / linedef->dx) / 1.57079) + * (level.WallVertLight - level.WallHorizLight) ); } else { - rel = linedef->dx==0? level.WallVertLight : - linedef->dy==0? level.WallHorizLight : 0; + rel = linedef->dx == 0 ? level.WallVertLight : + linedef->dy == 0 ? level.WallHorizLight : 0; } if (pfakecontrast != NULL) { @@ -2008,80 +2008,12 @@ int WallMost (short *mostbuf, const secplane_t &plane) return bad; } -void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat) -{ // swall = scale, lwall = texturecolumn - int x; - float top, bot, i; - float xrepeat = (float)walxrepeat; - float ol, l, topinc, botinc; - - i = (float)(WallSX1 - centerx); - top = WallUoverZorg + WallUoverZstep * i; - bot = WallInvZorg + WallInvZstep * i; - topinc = WallUoverZstep * 4.f; - botinc = WallInvZstep * 4.f; - - x = WallSX1; - - l = top / bot; - swall[x] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); - lwall[x] = xs_RoundToInt(l * xrepeat); - // As long as l is invalid, step one column at a time so that - // we can get as many correct texture columns as possible. - while (l > 1.0 && x+1 < WallSX2) - { - l = (top += WallUoverZstep) / (bot += WallInvZstep); - x++; - swall[x] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); - lwall[x] = xs_RoundToInt(l * xrepeat); - } - l *= xrepeat; - while (x+4 < WallSX2) - { - top += topinc; bot += botinc; - ol = l; l = top / bot; - swall[x+4] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); - lwall[x+4] = xs_RoundToInt(l *= xrepeat); - - i = (ol+l) * 0.5f; - lwall[x+2] = xs_RoundToInt(i); - lwall[x+1] = xs_RoundToInt((ol+i) * 0.5f); - lwall[x+3] = xs_RoundToInt((l+i) * 0.5f); - swall[x+2] = ((swall[x]+swall[x+4])>>1); - swall[x+1] = ((swall[x]+swall[x+2])>>1); - swall[x+3] = ((swall[x+4]+swall[x+2])>>1); - x += 4; - } - if (x+2 < WallSX2) - { - top += topinc * 0.5f; bot += botinc * 0.5f; - ol = l; l = top / bot; - swall[x+2] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); - lwall[x+2] = xs_RoundToInt(l *= xrepeat); - - lwall[x+1] = xs_RoundToInt((l+ol)*0.5f); - swall[x+1] = (swall[x]+swall[x+2])>>1; - x += 2; - } - if (x+1 < WallSX2) - { - l = (top + WallUoverZstep) / (bot + WallInvZstep); - swall[x+1] = xs_RoundToInt(l * WallDepthScale + WallDepthOrg); - lwall[x+1] = xs_RoundToInt(l * xrepeat); - } - /* - for (x = WallSX1; x < WallSX2; x++) - { - frac = top / bot; - lwall[x] = xs_RoundToInt(frac * xrepeat); - swall[x] = xs_RoundToInt(frac * WallDepthScale + WallDepthOrg); - top += WallUoverZstep; - bot += WallInvZstep; - } - */ - +static void PrepWallRoundFix(fixed_t *lwall, fixed_t walxrepeat) +{ // fix for rounding errors fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat-1 : 0; + int x; + if (WallSX1 > 0) { for (x = WallSX1; x < WallSX2; x++) @@ -2110,85 +2042,52 @@ void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat) } } -void PrepLWall (fixed_t *lwall, fixed_t walxrepeat) -{ // lwall = texturecolumn - int x; - float top, bot, i; - float xrepeat = (float)walxrepeat; - float ol, l, topinc, botinc; +void PrepWall (fixed_t *swall, fixed_t *lwall, fixed_t walxrepeat) +{ // swall = scale, lwall = texturecolumn + double top, bot, i; + double xrepeat = walxrepeat; + double topinc, botinc; - i = (float)(WallSX1 - centerx); + i = WallSX1 - centerx; top = WallUoverZorg + WallUoverZstep * i; bot = WallInvZorg + WallInvZstep * i; topinc = WallUoverZstep * 4.f; botinc = WallInvZstep * 4.f; - x = WallSX1; + for (int x = WallSX1; x < WallSX2; x++) + { + double frac = top / bot; + lwall[x] = xs_RoundToInt(frac * xrepeat); + swall[x] = xs_RoundToInt(frac * WallDepthScale + WallDepthOrg); + top += WallUoverZstep; + bot += WallInvZstep; + } + PrepWallRoundFix(lwall, walxrepeat); +} - l = top / bot; - lwall[x] = xs_RoundToInt(l * xrepeat); - // As long as l is invalid, step one column at a time so that - // we can get as many correct texture columns as possible. - while (l > 1.0 && x+1 < WallSX2) - { - l = (top += WallUoverZstep) / (bot += WallInvZstep); - lwall[++x] = xs_RoundToInt(l * xrepeat); - } - l *= xrepeat; - while (x+4 < WallSX2) - { - top += topinc; bot += botinc; - ol = l; l = top / bot; - lwall[x+4] = xs_RoundToInt(l *= xrepeat); +void PrepLWall (fixed_t *lwall, fixed_t walxrepeat) +{ // lwall = texturecolumn + double top, bot, i; + double xrepeat = walxrepeat; + double topinc, botinc; + double topstep; - i = (ol+l) * 0.5f; - lwall[x+2] = xs_RoundToInt(i); - lwall[x+1] = xs_RoundToInt((ol+i) * 0.5f); - lwall[x+3] = xs_RoundToInt((l+i) * 0.5f); - x += 4; - } - if (x+2 < WallSX2) - { - top += topinc * 0.5f; bot += botinc * 0.5f; - ol = l; l = top / bot; - lwall[x+2] = xs_RoundToInt(l *= xrepeat); - lwall[x+1] = xs_RoundToInt((l+ol)*0.5f); - x += 2; - } - if (x+1 < WallSX2) - { - l = (top + WallUoverZstep) / (bot + WallInvZstep); - lwall[x+1] = xs_RoundToInt(l * xrepeat); - } + i = WallSX1 - centerx; + top = WallUoverZorg + WallUoverZstep * i; + bot = WallInvZorg + WallInvZstep * i; + topinc = WallUoverZstep * 4.f; + botinc = WallInvZstep * 4.f; - // fix for rounding errors - fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat-1 : 0; - if (WallSX1 > 0) + top *= xrepeat; + topstep = WallUoverZstep * xrepeat; + + for (int x = WallSX1; x < WallSX2; x++) { - for (x = WallSX1; x < WallSX2; x++) - { - if ((unsigned)lwall[x] >= (unsigned)walxrepeat) - { - lwall[x] = fix; - } - else - { - break; - } - } - } - fix = walxrepeat - 1 - fix; - for (x = WallSX2-1; x >= WallSX1; x--) - { - if ((unsigned)lwall[x] >= (unsigned)walxrepeat) - { - lwall[x] = fix; - } - else - { - break; - } + lwall[x] = xs_RoundToInt(top / bot); + top += topstep; + bot += WallInvZstep; } + PrepWallRoundFix(lwall, walxrepeat); } // pass = 0: when seg is first drawn diff --git a/zdoom.vcproj b/zdoom.vcproj index 190b09e0cb..913cff4a99 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1,7 +1,7 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - @@ -1880,6 +1872,14 @@ Outputs="$(IntDir)/$(InputName).obj" /> + + + @@ -2069,6 +2069,14 @@ Outputs="$(IntDir)\$(InputName).obj" /> + + + @@ -2079,14 +2087,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - + + + - - - + + + @@ -5379,14 +5387,6 @@ AdditionalIncludeDirectories="src\win32;$(NoInherit)" /> - - - @@ -5665,7 +5665,7 @@ />