ZDBSP fixes:

- MapSideDef.sector was declared signed but treated as unsigned.
- Degenerate (one-dimensional) subsectors could throw away segs when
  output for GL nodes depending on the seg ordering.

SVN r152 (trunk)
This commit is contained in:
Randy Heit 2006-05-29 03:07:57 +00:00
parent 65cdd0323e
commit 3128c11fd1
8 changed files with 386 additions and 94 deletions

View file

@ -27,7 +27,7 @@ struct MapSideDef
char toptexture[8];
char bottomtexture[8];
char midtexture[8];
short sector;
WORD sector;
};
struct MapLineDef

View file

@ -117,7 +117,7 @@ DWORD FNodeBuilder::CreateSubsector (DWORD set, fixed_t bbox[4])
assert (set != DWORD_MAX);
#if defined(_DEBUG) || 1
#if defined(_DEBUG)// || 1
// Check for segs with duplicate start/end vertices
DWORD s1, s2;
@ -192,8 +192,14 @@ void FNodeBuilder::CreateSubsectorsForReal ()
qsort (&SegList[sub.firstline], sub.numlines, sizeof(int), SortSegs);
// Convert seg pointers into indices
D(printf ("Output subsector %d:\n", Subsectors.Size()));
for (size_t i = sub.firstline; i < SegList.Size(); ++i)
{
D(printf (" Seg %5d (%5d,%5d)-(%5d,%5d)\n", SegList[i].SegPtr - &Segs[0],
Vertices[SegList[i].SegPtr->v1].x>>16,
Vertices[SegList[i].SegPtr->v1].y>>16,
Vertices[SegList[i].SegPtr->v2].x>>16,
Vertices[SegList[i].SegPtr->v2].y>>16));
SegList[i].SegNum = SegList[i].SegPtr - &Segs[0];
}
Subsectors.Push (sub);

View file

@ -19,11 +19,18 @@
*/
#include <string.h>
#include <stdio.h>
#include <float.h>
#include "zdbsp.h"
#include "nodebuild.h"
#include "templates.h"
#if 0
#define D(x) x
#else
#define D(x) do{}while(0)
#endif
void FNodeBuilder::GetGLNodes (MapNodeEx *&outNodes, int &nodeCount,
MapSegGLEx *&outSegs, int &segCount,
MapSubsectorEx *&outSubs, int &subCount)
@ -82,18 +89,31 @@ int FNodeBuilder::CloseSubsector (TArray<MapSegGLEx> &segs, int subsector)
double accumx, accumy;
fixed_t midx, midy;
int i, j, first, max, count, firstVert;
bool diffplanes;
int firstplane;
first = Subsectors[subsector].firstline;
max = first + Subsectors[subsector].numlines;
count = 0;
accumx = accumy = 0.0;
diffplanes = false;
firstplane = Segs[SegList[first].SegNum].planenum;
// Calculate the midpoint of the subsector and also check for degenerate subsectors.
// A subsector is degenerate if it exists in only one dimension, which can be
// detected when all the segs lie in the same plane. This can happen if you have
// outward-facing lines in the void that don't point toward any sector. (Some of the
// polyobjects in Hexen are constructed like this.)
for (i = first; i < max; ++i)
{
seg = &Segs[SegList[i].SegNum];
accumx += double(Vertices[seg->v1].x) + double(Vertices[seg->v2].x);
accumy += double(Vertices[seg->v1].y) + double(Vertices[seg->v2].y);
if (firstplane != seg->planenum)
{
diffplanes = true;
}
}
midx = fixed_t(accumx / (max - first) / 2);
@ -112,69 +132,228 @@ int FNodeBuilder::CloseSubsector (TArray<MapSegGLEx> &segs, int subsector)
{
seg = &Segs[SegList[j].SegNum];
angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
printf ("%d: %5d(%5d,%5d)->%5d(%5d,%5d) - %3.3f\n", j,
printf ("%d: %5d(%5d,%5d)->%5d(%5d,%5d) - %3.3f %d,%d\n", j,
seg->v1, Vertices[seg->v1].x>>16, Vertices[seg->v1].y>>16,
seg->v2, Vertices[seg->v2].x>>16, Vertices[seg->v2].y>>16,
double(ang/2)*180/(1<<30));
double(ang/2)*180/(1<<30),
seg->planenum, seg->planefront);
}
#endif
for (i = first + 1; i < max; ++i)
{
angle_t bestdiff = ANGLE_MAX;
FPrivSeg *bestseg = NULL;
int bestj = -1;
for (j = first; j < max; ++j)
if (diffplanes)
{ // A well-behaved subsector. Output the segs sorted by the angle formed by connecting
// the subsector's center to their first vertex.
D(printf("Well behaved subsector\n"));
for (i = first + 1; i < max; ++i)
{
seg = &Segs[SegList[j].SegNum];
angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
angle_t diff = prevAngle - ang;
if (seg->v1 == prev->v2)
angle_t bestdiff = ANGLE_MAX;
FPrivSeg *bestseg = NULL;
int bestj = -1;
for (j = first; j < max; ++j)
{
bestdiff = diff;
bestseg = seg;
bestj = j;
seg = &Segs[SegList[j].SegNum];
angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
angle_t diff = prevAngle - ang;
if (seg->v1 == prev->v2)
{
bestdiff = diff;
bestseg = seg;
bestj = j;
break;
}
if (diff < bestdiff && diff > 0)
{
bestdiff = diff;
bestseg = seg;
bestj = j;
}
}
if (bestseg != NULL)
{
seg = bestseg;
}
if (prev->v2 != seg->v1)
{
// Add a new miniseg to connect the two segs
PushConnectingGLSeg (subsector, segs, prev->v2, seg->v1);
count++;
}
#if 0
printf ("+%d\n", bestj);
#endif
prevAngle -= bestdiff;
seg->storedseg = PushGLSeg (segs, seg);
count++;
prev = seg;
if (seg->v2 == firstVert)
{
prev = seg;
break;
}
if (diff < bestdiff && diff > 0)
}
#if 0
printf ("\n");
#endif
}
else
{ // A degenerate subsector. These are handled in three stages:
// Stage 1. Proceed in the same direction as the start seg until we
// hit the seg furthest from it.
// Stage 2. Reverse direction and proceed until we hit the seg
// furthest from the start seg.
// Stage 3. Reverse direction again and insert segs until we get
// to the start seg.
// A dot product serves to determine distance from the start seg.
D(printf("degenerate subsector\n"));
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;
#if 0
for (j = first + 1; j < max; ++j)
{
seg = &Segs[SegList[j].SegNum];
dx2 = Vertices[seg->v1].x - x1;
dy2 = Vertices[seg->v1].y - y1;
dot = dx*dx2 + dy*dy2;
printf ("Seg %d: dot %g\n", j, dot);
}
#endif
// 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)
{
bestdiff = diff;
bestseg = seg;
bestj = 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, prev->v2, bestseg->v1);
count++;
}
seg->storedseg = PushGLSeg (segs, bestseg);
count++;
prev = bestseg;
lastdot = bestdot;
}
}
if (bestseg != NULL)
// Stage 2. Go backward.
lastdot = DBL_MAX;
for (i = first + 1; i < max; ++i)
{
seg = bestseg;
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, prev->v2, bestseg->v1);
count++;
}
seg->storedseg = PushGLSeg (segs, bestseg);
count++;
prev = bestseg;
lastdot = bestdot;
}
}
if (prev->v2 != seg->v1)
// Stage 3. Go forward again.
lastdot = -DBL_MAX;
for (i = first + 1; i < max; ++i)
{
// Add a new miniseg to connect the two segs
PushConnectingGLSeg (subsector, segs, prev->v2, seg->v1);
count++;
}
#if 0
printf ("+%d\n", bestj);
#endif
prevAngle -= bestdiff;
seg->storedseg = PushGLSeg (segs, seg);
count++;
prev = seg;
if (seg->v2 == firstVert)
{
prev = seg;
break;
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, prev->v2, bestseg->v1);
count++;
}
seg->storedseg = PushGLSeg (segs, bestseg);
count++;
prev = bestseg;
lastdot = bestdot;
}
}
}
#if 0
printf ("\n");
#endif
if (prev->v2 != firstVert)
{
PushConnectingGLSeg (subsector, segs, prev->v2, firstVert);
count++;
}
#if 0
printf ("Output GL subsector %d:\n", subsector);
for (i = segs.Size() - count; i < (int)segs.Size(); ++i)
{
printf (" Seg %5d%c(%5d,%5d)-(%5d,%5d)\n", i,
segs[i].linedef == NO_INDEX ? '+' : ' ',
Vertices[segs[i].v1].x>>16,
Vertices[segs[i].v1].y>>16,
Vertices[segs[i].v2].x>>16,
Vertices[segs[i].v2].y>>16);
}
#endif
return count;
}

View file

@ -124,6 +124,8 @@ void FNodeBuilder::MakeSegsFromSides ()
j = (int)Segs.Push (seg);
Vertices[seg.v1].segs = j;
Vertices[seg.v2].segs2 = j;
D(printf("Seg %4d: From line %d, side front (%5d,%5d)-(%5d,%5d)\n", j, i, Vertices[seg.v1].x>>16,
Vertices[seg.v1].y>>16, Vertices[seg.v2].x>>16, Vertices[seg.v2].y>>16));
}
else
{
@ -162,6 +164,8 @@ void FNodeBuilder::MakeSegsFromSides ()
Segs[j-1].partner = j;
Segs[j].partner = j-1;
}
D(printf("Seg %4d: From line %d, side back (%5d,%5d)-(%5d,%5d)\n", j, i, Vertices[seg.v1].x>>16,
Vertices[seg.v1].y>>16, Vertices[seg.v2].x>>16, Vertices[seg.v2].y>>16));
}
else if (share2->linedef != share1->linedef)
{
@ -280,7 +284,7 @@ void FNodeBuilder::GroupSegPlanes ()
}
}
D(Printf ("%d planes from %d segs\n", planenum, Segs.Size()));
D(printf ("%d planes from %d segs\n", planenum, Segs.Size()));
planenum = (planenum+7)/8;
PlaneChecked.Reserve (planenum);

View file

@ -93,8 +93,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,6,0,0
PRODUCTVERSION 1,6,0,0
FILEVERSION 1,7,0,0
PRODUCTVERSION 1,7,0,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -110,12 +110,12 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "ZDBSP Node Builder"
VALUE "FileVersion", "1.6"
VALUE "FileVersion", "1.7"
VALUE "InternalName", "zdbsp"
VALUE "LegalCopyright", "Copyright (C) 2002,2003"
VALUE "OriginalFilename", "zdbsp.exe"
VALUE "ProductName", "ZDBSP"
VALUE "ProductVersion", "1.6"
VALUE "ProductVersion", "1.7"
END
END
BLOCK "VarFileInfo"

193
tarray.h
View file

@ -3,7 +3,7 @@
** Templated, automatically resizing array
**
**---------------------------------------------------------------------------
** Copyright 1998-2001 Randy Heit
** Copyright 1998-2005 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -36,11 +36,28 @@
#define __TARRAY_H__
#include <stdlib.h>
#include <assert.h>
#include <malloc.h>
#include <new>
template <class T>
class TArray
{
public:
////////
// This is a dummy constructor that does nothing. The purpose of this
// is so you can create a global TArray in the data segment that gets
// used by code before startup without worrying about the constructor
// resetting it after it's already been used. You MUST NOT use it for
// heap- or stack-allocated TArrays.
enum ENoInit
{
NoInit
};
TArray (ENoInit dummy)
{
}
////////
TArray ()
{
Most = 0;
@ -51,7 +68,11 @@ public:
{
Most = max;
Count = 0;
Array = new T[max];
Array = (T *)malloc (sizeof(T)*max);
if (Array == NULL)
{
throw std::bad_alloc();
}
}
TArray (const TArray<T> &other)
{
@ -63,7 +84,11 @@ public:
{
if (Array != NULL)
{
delete[] Array;
if (Count > 0)
{
DoDelete (0, Count-1);
}
free (Array);
}
DoCopy (other);
}
@ -72,20 +97,25 @@ public:
~TArray ()
{
if (Array)
{
if (Count > 0)
{
DoDelete (0, Count-1);
}
free (Array);
Array = NULL;
Count = 0;
Most = 0;
}
}
T &operator[] (size_t index) const
T &operator[] (unsigned int index) const
{
return Array[index];
}
size_t Push (const T &item)
unsigned int Push (const T &item)
{
if (Count >= Most)
{
Most = (Most >= 16) ? Most + Most / 2 : 16;
Realloc (Count, Most);
}
Array[Count] = item;
Grow (1);
::new((void*)&Array[Count]) T(item);
return Count++;
}
bool Pop (T &item)
@ -93,26 +123,42 @@ public:
if (Count > 0)
{
item = Array[--Count];
Array[Count].~T();
return true;
}
return false;
}
void Delete (int index)
void Delete (unsigned int index)
{
if (index < Count-1)
memmove (Array + index, Array + index + 1, (Count - index) * sizeof(T));
else if (index < Count)
Count--;
}
void Realloc (size_t count, size_t most)
{
T *copy = new T[most];
for (size_t i = 0; i < count; ++i)
if (index < Count)
{
copy[i] = Array[i];
Array[index].~T();
memmove (&Array[index], &Array[index+1], sizeof(T)*(Count - index - 1));
Count--;
}
}
// Inserts an item into the array, shifting elements as needed
void Insert (unsigned int index, const T &item)
{
if (index >= Count)
{
// Inserting somewhere past the end of the array, so we can
// just add it without moving things.
Resize (index + 1);
::new ((void *)&Array[index]) T(item);
}
else
{
// Inserting somewhere in the middle of the array,
// so make room for it
Resize (Count + 1);
// Now move items from the index and onward out of the way
memmove (&Array[index+1], &Array[index], sizeof(T)*(Count - index - 1));
// And put the new element in
::new ((void *)&Array[index]) T(item);
}
delete[] Array;
Array = copy;
}
void ShrinkToFit ()
{
@ -129,70 +175,82 @@ public:
}
else
{
Realloc (Count, Most);
DoResize ();
}
}
}
// Grow Array to be large enough to hold amount more entries without
// further growing.
void Grow (size_t amount)
void Grow (unsigned int amount)
{
if (Count + amount > Most)
{
const size_t choicea = Count + amount;
const size_t choiceb = Most + Most/2;
const unsigned int choicea = Count + amount;
const unsigned int choiceb = Most = (Most >= 16) ? Most + Most / 2 : 16;
Most = (choicea > choiceb ? choicea : choiceb);
Realloc (Count, Most);
DoResize ();
}
}
// Resize Array so that it has exactly amount entries in use.
void Resize (size_t amount)
void Resize (unsigned int amount)
{
if (Count < amount)
{
// Adding new entries
Grow (amount - Count);
for (unsigned int i = Count; i < amount; ++i)
{
::new((void *)&Array[i]) T;
}
}
else if (Count > amount)
else if (Count != amount)
{
Count = amount;
// Deleting old entries
DoDelete (amount, Count - 1);
}
Count = amount;
}
// Reserves amount entries at the end of the array, but does nothing
// with them.
size_t Reserve (size_t amount)
unsigned int Reserve (unsigned int amount)
{
if (Count + amount > Most)
{
Grow (amount);
}
size_t place = Count;
Grow (amount);
unsigned int place = Count;
Count += amount;
return place;
}
size_t Size () const
unsigned int Size () const
{
return Count;
}
size_t Max () const
unsigned int Max () const
{
return Most;
}
void Clear ()
{
Count = 0;
if (Count > 0)
{
DoDelete (0, Count-1);
Count = 0;
}
}
private:
T *Array;
size_t Most;
size_t Count;
unsigned int Most;
unsigned int Count;
void DoCopy (const TArray<T> &other)
{
Most = Count = other.Count;
if (Count != 0)
{
Array = new T[Most];
for (size_t i = 0; i < Count; ++i)
Array = (T *)malloc (sizeof(T)*Most);
if (Array == NULL)
{
throw std::bad_alloc();
}
for (unsigned int i = 0; i < Count; ++i)
{
Array[i] = other.Array[i];
}
@ -202,6 +260,51 @@ private:
Array = NULL;
}
}
void DoResize ()
{
size_t allocsize = sizeof(T)*Most;
Array = (T *)realloc (Array, allocsize);
if (Array == NULL)
{
throw std::bad_alloc();
}
}
void DoDelete (unsigned int first, unsigned int last)
{
assert (last != ~0u);
for (unsigned int i = first; i <= last; ++i)
{
Array[i].~T();
}
}
};
// An array with accessors that automatically grow the
// array as needed. But can still be used as a normal
// TArray if needed. Used by ACS world and global arrays.
template <class T>
class TAutoGrowArray : public TArray<T>
{
public:
T GetVal (unsigned int index)
{
if (index >= this->Size())
{
return 0;
}
return (*this)[index];
}
void SetVal (unsigned int index, T val)
{
if (index >= this->Size())
{
this->Resize (index + 1);
}
(*this)[index] = val;
}
};
#endif //__TARRAY_H__

View file

@ -8,7 +8,7 @@
#include <limits.h>
#include <exception>
#define ZDBSP_VERSION "1.6"
#define ZDBSP_VERSION "1.7"
enum EBlockmapMode
{

View file

@ -92,7 +92,7 @@
AdditionalIncludeDirectories="zlib"
PreprocessorDefinitions="WIN32,_DEBUG,_CONSOLE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
PrecompiledHeaderFile=".\Debug/zdbsp.pch"
AssemblerListingLocation=".\Debug/"