mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2025-01-18 14:31:50 +00:00
sector triangulator changes
This commit is contained in:
parent
2dd337cffa
commit
46935a6132
16 changed files with 285 additions and 167 deletions
|
@ -117,7 +117,7 @@
|
|||
<Compile Include="Geometry\LinedefsTracePath.cs" />
|
||||
<Compile Include="Geometry\LinedefAngleSorter.cs" />
|
||||
<Compile Include="Geometry\Tools.cs" />
|
||||
<Compile Include="Geometry\Triangulator.cs" />
|
||||
<Compile Include="Geometry\Triangulation.cs" />
|
||||
<Compile Include="Geometry\EarClipVertex.cs" />
|
||||
<Compile Include="Geometry\Line2D.cs" />
|
||||
<Compile Include="Geometry\EarClipPolygon.cs" />
|
||||
|
|
|
@ -153,7 +153,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
*/
|
||||
|
||||
// Render the geometry
|
||||
renderer.RenderGeometry(s.Vertices, null, true);
|
||||
renderer.RenderGeometry(s.FlatVertices, null, true);
|
||||
}
|
||||
|
||||
if(selecting) RenderMultiSelection();
|
||||
|
|
|
@ -596,7 +596,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
{
|
||||
// Make polygon
|
||||
LinedefTracePath tracepath = new LinedefTracePath(pathlines);
|
||||
EarClipPolygon pathpoly = tracepath.MakePolygon();
|
||||
EarClipPolygon pathpoly = tracepath.MakePolygon(true);
|
||||
|
||||
// Check if the front of the line is outside the polygon
|
||||
if(!pathpoly.Intersect(ld.GetSidePoint(true)))
|
||||
|
@ -611,7 +611,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
{
|
||||
// Make polygon
|
||||
tracepath = new LinedefTracePath(pathlines);
|
||||
pathpoly = tracepath.MakePolygon();
|
||||
pathpoly = tracepath.MakePolygon(true);
|
||||
|
||||
// Check if the back of the line is inside the polygon
|
||||
if(pathpoly.Intersect(ld.GetSidePoint(false)))
|
||||
|
|
|
@ -295,8 +295,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
// Quickly flash this sector to indicate it was created
|
||||
General.Map.IsChanged = true;
|
||||
General.Map.Map.Update();
|
||||
flashpolygon = new FlatVertex[s.Vertices.Length];
|
||||
s.Vertices.CopyTo(flashpolygon, 0);
|
||||
flashpolygon = new FlatVertex[s.FlatVertices.Length];
|
||||
s.FlatVertices.CopyTo(flashpolygon, 0);
|
||||
flashintensity = 1.0f;
|
||||
flashstarttime = (double)General.Clock.GetCurrentTime();
|
||||
General.Interface.SetProcessorState(true);
|
||||
|
@ -344,8 +344,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
|
|||
// Quickly flash this sector to indicate it was created
|
||||
General.Map.IsChanged = true;
|
||||
General.Map.Map.Update();
|
||||
flashpolygon = new FlatVertex[s.Vertices.Length];
|
||||
s.Vertices.CopyTo(flashpolygon, 0);
|
||||
flashpolygon = new FlatVertex[s.FlatVertices.Length];
|
||||
s.FlatVertices.CopyTo(flashpolygon, 0);
|
||||
flashintensity = 1.0f;
|
||||
flashstarttime = (double)General.Clock.GetCurrentTime();
|
||||
General.Interface.SetProcessorState(true);
|
||||
|
|
|
@ -54,6 +54,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
|
||||
// Highlighted item
|
||||
private Sector highlighted;
|
||||
|
||||
// Labels
|
||||
private ICollection<Vector2D> labelpos;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -138,6 +141,18 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
renderer.Finish();
|
||||
}
|
||||
|
||||
// Render labels
|
||||
if(renderer.StartOverlay(true))
|
||||
{
|
||||
if(labelpos != null)
|
||||
{
|
||||
foreach(Vector2D v in labelpos)
|
||||
renderer.RenderRectangleFilled(new RectangleF(v.x - 5, v.y - 5, 10, 10), General.Colors.Indication, true);
|
||||
}
|
||||
|
||||
renderer.Finish();
|
||||
}
|
||||
|
||||
// Do not show things
|
||||
if(renderer.StartThings(true))
|
||||
{
|
||||
|
@ -160,15 +175,31 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
// Set new highlight
|
||||
highlighted = s;
|
||||
|
||||
// Get label positions
|
||||
if(s != null) labelpos = Tools.FindLabelPositions(s);
|
||||
|
||||
// Render highlighted item
|
||||
if((highlighted != null) && !highlighted.IsDisposed)
|
||||
renderer.PlotSector(highlighted, General.Colors.Highlight);
|
||||
|
||||
// Done
|
||||
renderer.Finish();
|
||||
renderer.Present();
|
||||
}
|
||||
|
||||
// Render labels
|
||||
if(renderer.StartOverlay(true))
|
||||
{
|
||||
if(labelpos != null)
|
||||
{
|
||||
foreach(Vector2D v in labelpos)
|
||||
renderer.RenderRectangleFilled(new RectangleF(v.x - 5, v.y - 5, 10, 10), General.Colors.Indication, true);
|
||||
}
|
||||
|
||||
renderer.Finish();
|
||||
}
|
||||
|
||||
renderer.Present();
|
||||
|
||||
// Show highlight info
|
||||
if((highlighted != null) && !highlighted.IsDisposed)
|
||||
General.Interface.ShowSectorInfo(highlighted);
|
||||
|
@ -271,83 +302,28 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
// Item highlighted?
|
||||
if((highlighted != null) && !highlighted.IsDisposed)
|
||||
{
|
||||
// Edit button is used?
|
||||
if(General.Interface.CheckActionActive(null, "classicedit"))
|
||||
// Get a triangulator and bind events
|
||||
Triangulation t = Triangulation.Create(highlighted);
|
||||
|
||||
// Start with a clear display
|
||||
if(renderer.StartPlotter(true))
|
||||
{
|
||||
// Anything selected?
|
||||
selected = General.Map.Map.GetSelectedSectors(true);
|
||||
if(selected.Count > 0)
|
||||
// Render lines and vertices
|
||||
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
|
||||
renderer.PlotVerticesSet(General.Map.Map.Vertices);
|
||||
|
||||
// Go for all triangle vertices
|
||||
for(int i = 0; i < t.Vertices.Length; i += 3)
|
||||
{
|
||||
// Remove highlight
|
||||
Highlight(null);
|
||||
|
||||
// Clear selection
|
||||
General.Map.Map.ClearSelectedSectors();
|
||||
General.Map.Map.ClearSelectedLinedefs();
|
||||
General.Interface.RedrawDisplay();
|
||||
|
||||
// Get a triangulator and bind events
|
||||
Triangulator t = new Triangulator();
|
||||
t.OnShowLine = new Triangulator.ShowLine(ShowLine);
|
||||
t.OnShowPolygon = new Triangulator.ShowPolygon(ShowPolygon);
|
||||
t.OnShowPoint = new Triangulator.ShowPoint(ShowPoint);
|
||||
t.OnShowEarClip = new Triangulator.ShowEarClip(ShowEarClip);
|
||||
|
||||
// Triangulate this now!
|
||||
triangles = t.PerformTriangulation(General.GetByIndex<Sector>(selected, 0));
|
||||
|
||||
// Start with a clear display
|
||||
if(renderer.StartPlotter(true))
|
||||
{
|
||||
// Render lines and vertices
|
||||
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
|
||||
renderer.PlotVerticesSet(General.Map.Map.Vertices);
|
||||
|
||||
// Go for all triangle vertices
|
||||
for(int i = 0; i < triangles.Count; i += 3)
|
||||
{
|
||||
renderer.PlotLine(triangles[i + 0], triangles[i + 1], General.Colors.Selection);
|
||||
renderer.PlotLine(triangles[i + 1], triangles[i + 2], General.Colors.Selection);
|
||||
renderer.PlotLine(triangles[i + 2], triangles[i + 0], General.Colors.Selection);
|
||||
}
|
||||
|
||||
// Done
|
||||
renderer.Finish();
|
||||
renderer.Present();
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], General.Colors.Selection);
|
||||
renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], General.Colors.Selection);
|
||||
renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], General.Colors.Selection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get a triangulator and bind events
|
||||
Triangulator t = new Triangulator();
|
||||
|
||||
// Triangulate the whole map!
|
||||
triangles = new TriangleList();
|
||||
foreach(Sector s in General.Map.Map.Sectors)
|
||||
triangles.AddRange(t.PerformTriangulation(s));
|
||||
|
||||
// Start with a clear display
|
||||
if(renderer.StartPlotter(true))
|
||||
{
|
||||
// Render lines and vertices
|
||||
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
|
||||
renderer.PlotVerticesSet(General.Map.Map.Vertices);
|
||||
|
||||
// Go for all triangle vertices
|
||||
for(int i = 0; i < triangles.Count; i += 3)
|
||||
{
|
||||
renderer.PlotLine(triangles[i + 0], triangles[i + 1], General.Colors.Selection);
|
||||
renderer.PlotLine(triangles[i + 1], triangles[i + 2], General.Colors.Selection);
|
||||
renderer.PlotLine(triangles[i + 2], triangles[i + 0], General.Colors.Selection);
|
||||
}
|
||||
|
||||
// Done
|
||||
renderer.Finish();
|
||||
renderer.Present();
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
// Done
|
||||
renderer.Finish();
|
||||
renderer.Present();
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,8 +64,8 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
base.Texture = General.Map.Data.GetFlatImage(s.LongCeilTexture);
|
||||
|
||||
// Make vertices
|
||||
verts = new WorldVertex[s.Vertices.Length];
|
||||
for(int i = 0; i < s.Vertices.Length; i++)
|
||||
verts = new WorldVertex[s.Triangles.Vertices.Length];
|
||||
for(int i = 0; i < s.Triangles.Vertices.Length; i++)
|
||||
{
|
||||
// Use sector brightness for color shading
|
||||
PixelColor pc = new PixelColor(255, unchecked((byte)s.Brightness), unchecked((byte)s.Brightness), unchecked((byte)s.Brightness));
|
||||
|
@ -75,18 +75,18 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
// Grid aligned texture coordinates
|
||||
if(base.Texture.IsImageLoaded)
|
||||
{
|
||||
verts[i].u = s.Vertices[i].x / base.Texture.ScaledWidth;
|
||||
verts[i].v = s.Vertices[i].y / base.Texture.ScaledHeight;
|
||||
verts[i].u = s.Triangles.Vertices[i].x / base.Texture.ScaledWidth;
|
||||
verts[i].v = s.Triangles.Vertices[i].y / base.Texture.ScaledHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
verts[i].u = s.Vertices[i].x / 64;
|
||||
verts[i].v = s.Vertices[i].y / 64;
|
||||
verts[i].u = s.Triangles.Vertices[i].x / 64;
|
||||
verts[i].v = s.Triangles.Vertices[i].y / 64;
|
||||
}
|
||||
|
||||
// Vertex coordinates
|
||||
verts[i].x = s.Vertices[i].x;
|
||||
verts[i].y = s.Vertices[i].y;
|
||||
verts[i].x = s.Triangles.Vertices[i].x;
|
||||
verts[i].y = s.Triangles.Vertices[i].y;
|
||||
verts[i].z = (float)s.CeilHeight;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,8 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
base.Texture = General.Map.Data.GetFlatImage(s.LongFloorTexture);
|
||||
|
||||
// Make vertices
|
||||
verts = new WorldVertex[s.Vertices.Length];
|
||||
for(int i = 0; i < s.Vertices.Length; i++)
|
||||
verts = new WorldVertex[s.Triangles.Vertices.Length];
|
||||
for(int i = 0; i < s.Triangles.Vertices.Length; i++)
|
||||
{
|
||||
// Use sector brightness for color shading
|
||||
PixelColor pc = new PixelColor(255, unchecked((byte)s.Brightness), unchecked((byte)s.Brightness), unchecked((byte)s.Brightness));
|
||||
|
@ -74,18 +74,18 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
|
|||
// Grid aligned texture coordinates
|
||||
if(base.Texture.IsImageLoaded)
|
||||
{
|
||||
verts[i].u = s.Vertices[i].x / base.Texture.ScaledWidth;
|
||||
verts[i].v = s.Vertices[i].y / base.Texture.ScaledHeight;
|
||||
verts[i].u = s.Triangles.Vertices[i].x / base.Texture.ScaledWidth;
|
||||
verts[i].v = s.Triangles.Vertices[i].y / base.Texture.ScaledHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
verts[i].u = s.Vertices[i].x / 64;
|
||||
verts[i].v = s.Vertices[i].y / 64;
|
||||
verts[i].u = s.Triangles.Vertices[i].x / 64;
|
||||
verts[i].v = s.Triangles.Vertices[i].y / 64;
|
||||
}
|
||||
|
||||
// Vertex coordinates
|
||||
verts[i].x = s.Vertices[i].x;
|
||||
verts[i].y = s.Vertices[i].y;
|
||||
verts[i].x = s.Triangles.Vertices[i].x;
|
||||
verts[i].y = s.Triangles.Vertices[i].y;
|
||||
verts[i].z = (float)s.FloorHeight;
|
||||
}
|
||||
|
||||
|
|
|
@ -112,9 +112,6 @@ namespace CodeImp.DoomBuilder
|
|||
// States
|
||||
private static bool debugbuild;
|
||||
|
||||
// Tools
|
||||
private static Triangulator earclipper;
|
||||
|
||||
// Command line arguments
|
||||
private static string[] cmdargs;
|
||||
private static string autoloadfile = null;
|
||||
|
@ -144,7 +141,6 @@ namespace CodeImp.DoomBuilder
|
|||
internal static PluginManager Plugins { get { return plugins; } }
|
||||
public static Clock Clock { get { return clock; } }
|
||||
public static bool DebugBuild { get { return debugbuild; } }
|
||||
internal static Triangulator EarClipper { get { return earclipper; } }
|
||||
internal static TypesManager Types { get { return types; } }
|
||||
internal static string AutoLoadFile { get { return autoloadfile; } }
|
||||
internal static string AutoLoadMap { get { return autoloadmap; } }
|
||||
|
@ -506,9 +502,6 @@ namespace CodeImp.DoomBuilder
|
|||
mainwindow.Show();
|
||||
mainwindow.Update();
|
||||
|
||||
// Create tools
|
||||
earclipper = new Triangulator();
|
||||
|
||||
// Start Direct3D
|
||||
General.WriteLogLine("Starting Direct3D graphics driver...");
|
||||
try { D3DDevice.Startup(); }
|
||||
|
|
|
@ -38,6 +38,9 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
// Position
|
||||
private Vector2D pos;
|
||||
|
||||
// Along a sidedef?
|
||||
private Sidedef sidedef;
|
||||
|
||||
// Lists
|
||||
private LinkedListNode<EarClipVertex> vertslink;
|
||||
private LinkedListNode<EarClipVertex> reflexlink;
|
||||
|
@ -51,26 +54,40 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
internal LinkedListNode<EarClipVertex> MainListNode { get { return vertslink; } }
|
||||
public bool IsReflex { get { return (reflexlink != null); } }
|
||||
public bool IsEarTip { get { return (eartiplink != null); } }
|
||||
|
||||
internal Sidedef Sidedef { get { return sidedef; } set { sidedef = value; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Disposer
|
||||
|
||||
// Constructor
|
||||
// Copy constructor
|
||||
internal EarClipVertex(EarClipVertex v)
|
||||
{
|
||||
// Initialize
|
||||
this.pos = v.pos;
|
||||
this.sidedef = v.sidedef;
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
internal EarClipVertex(EarClipVertex v, Sidedef sidedef)
|
||||
{
|
||||
// Initialize
|
||||
this.pos = v.pos;
|
||||
this.sidedef = sidedef;
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// Constructor
|
||||
internal EarClipVertex(Vector2D v)
|
||||
internal EarClipVertex(Vector2D v, Sidedef sidedef)
|
||||
{
|
||||
// Initialize
|
||||
this.pos = v;
|
||||
this.sidedef = sidedef;
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
|
@ -82,6 +99,7 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
reflexlink = null;
|
||||
eartiplink = null;
|
||||
vertslink = null;
|
||||
sidedef = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -83,9 +83,12 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
ay = CalculateRelativeAngle(baseline, y);
|
||||
|
||||
// Compare results
|
||||
/*
|
||||
if(ax < ay) return 1;
|
||||
else if(ax > ay) return -1;
|
||||
else return 0;
|
||||
*/
|
||||
return Math.Sign(ax - ay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,15 +97,18 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
}
|
||||
|
||||
// This makes a polygon from the path
|
||||
public EarClipPolygon MakePolygon()
|
||||
public EarClipPolygon MakePolygon(bool startfront)
|
||||
{
|
||||
EarClipPolygon p = new EarClipPolygon();
|
||||
bool forward = true;
|
||||
bool forward = startfront;
|
||||
|
||||
// Any sides at all?
|
||||
if(base.Count > 0)
|
||||
{
|
||||
p.AddLast(new EarClipVertex(base[0].Start.Position));
|
||||
if(forward)
|
||||
p.AddLast(new EarClipVertex(base[0].Start.Position, base[0].Front));
|
||||
else
|
||||
p.AddLast(new EarClipVertex(base[0].End.Position, base[0].Back));
|
||||
|
||||
// Add all lines, but the first
|
||||
for(int i = 1; i < base.Count; i++)
|
||||
|
@ -117,9 +120,9 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
|
||||
// Add next vertex
|
||||
if(forward)
|
||||
p.AddLast(new EarClipVertex(base[i].Start.Position));
|
||||
p.AddLast(new EarClipVertex(base[i].Start.Position, base[0].Front));
|
||||
else
|
||||
p.AddLast(new EarClipVertex(base[i].End.Position));
|
||||
p.AddLast(new EarClipVertex(base[i].End.Position, base[0].Back));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -85,9 +85,12 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
ay = CalculateRelativeAngle(baseside, y);
|
||||
|
||||
// Compare results
|
||||
/*
|
||||
if(ax < ay) return 1;
|
||||
else if(ax > ay) return -1;
|
||||
else return 0;
|
||||
*/
|
||||
return Math.Sign(ax - ay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,9 +96,9 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
{
|
||||
// On front or back?
|
||||
if(base[i].IsFront)
|
||||
p.AddLast(new EarClipVertex(base[i].Line.End.Position));
|
||||
p.AddLast(new EarClipVertex(base[i].Line.End.Position, base[i]));
|
||||
else
|
||||
p.AddLast(new EarClipVertex(base[i].Line.Start.Position));
|
||||
p.AddLast(new EarClipVertex(base[i].Line.Start.Position, base[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
{
|
||||
// Make polygon
|
||||
LinedefTracePath tracepath = new LinedefTracePath(innerlines);
|
||||
EarClipPolygon innerpoly = tracepath.MakePolygon();
|
||||
EarClipPolygon innerpoly = tracepath.MakePolygon(true);
|
||||
|
||||
// Check if the front of the line is outside the polygon
|
||||
if(!innerpoly.Intersect(foundline.GetSidePoint(foundlinefront)))
|
||||
|
@ -240,7 +240,7 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
{
|
||||
// Make polygon
|
||||
LinedefTracePath tracepath = new LinedefTracePath(pathlines);
|
||||
EarClipPolygon poly = tracepath.MakePolygon();
|
||||
EarClipPolygon poly = tracepath.MakePolygon(true);
|
||||
|
||||
// Check if the front of the line is inside the polygon
|
||||
if(poly.Intersect(line.GetSidePoint(front)))
|
||||
|
@ -621,13 +621,100 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
|
||||
#region ================== Sector Labels
|
||||
|
||||
// This finds the ideal label position for a sector
|
||||
public static Vector2D FindLabelPosition(Sector s, float resolution)
|
||||
// This finds the ideal label positions for a sector
|
||||
public static ICollection<Vector2D> FindLabelPositions(Sector s)
|
||||
{
|
||||
Vector2D foundpoint = new Vector2D();
|
||||
float founddist = 0.0f;
|
||||
List<Vector2D> positions = new List<Vector2D>(2);
|
||||
|
||||
return foundpoint;
|
||||
// Do we have a triangulation?
|
||||
Triangulation triangles = s.Triangles;
|
||||
if(triangles != null)
|
||||
{
|
||||
// Go for all islands
|
||||
for(int island = 0; island < triangles.IslandVertices.Length; island++)
|
||||
{
|
||||
Dictionary<Sidedef, Linedef> sides = new Dictionary<Sidedef, Linedef>(triangles.IslandVertices[island] >> 1);
|
||||
List<Line2D> candidatelines = new List<Line2D>(triangles.IslandVertices[island] >> 1);
|
||||
float founddistance = float.MinValue;
|
||||
Vector2D foundposition = new Vector2D();
|
||||
|
||||
// Make candidate lines that are not along sidedefs
|
||||
// We do this before testing the candidate against the sidedefs so that
|
||||
// we can collect the relevant sidedefs first in the same run
|
||||
for(int i = 0; i < triangles.Vertices.Length; i += 3)
|
||||
{
|
||||
Vector2D v1 = triangles.Vertices[i + 2];
|
||||
for(int k = 0; k < 3; k++)
|
||||
{
|
||||
Vector2D v2 = triangles.Vertices[i + k];
|
||||
Sidedef sd = triangles.Sidedefs[i + k];
|
||||
|
||||
// Not along a sidedef? Then this line is across the sector
|
||||
// and guaranteed to be inside the sector!
|
||||
if(sd == null)
|
||||
{
|
||||
// Make the line
|
||||
candidatelines.Add(new Line2D(v1, v2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This sidedefs is part of this island and must be checked against
|
||||
sides[sd] = sd.Line;
|
||||
}
|
||||
|
||||
// Next
|
||||
v1 = v2;
|
||||
}
|
||||
}
|
||||
|
||||
// Any candidate lines found at all?
|
||||
if(candidatelines.Count > 0)
|
||||
{
|
||||
// Start with the first line
|
||||
foreach(Line2D sourceline in candidatelines)
|
||||
{
|
||||
// Get center point
|
||||
Vector2D candidateposition = sourceline.GetCoordinatesAt(0.5f);
|
||||
|
||||
// Check distance against other lines
|
||||
float smallestdist = int.MaxValue;
|
||||
foreach(KeyValuePair<Sidedef, Linedef> sd in sides)
|
||||
{
|
||||
// Check the distance
|
||||
float distance = sd.Value.DistanceToSq(candidateposition, true);
|
||||
smallestdist = Math.Min(smallestdist, distance);
|
||||
}
|
||||
|
||||
// Keep this candidate if it is better than previous
|
||||
if(smallestdist > founddistance)
|
||||
{
|
||||
foundposition = candidateposition;
|
||||
founddistance = smallestdist;
|
||||
}
|
||||
}
|
||||
|
||||
// No cceptable line found, just use the first!
|
||||
positions.Add(foundposition);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No candidate lines found. Just return the center point.
|
||||
//RectangleF rect = s.CreateBBox();
|
||||
//return new Vector2D(rect.X + (rect.Width * 0.5f), rect.Y + (rect.Height * 0.5f));
|
||||
positions.Add(new Vector2D());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No triangulation was made. Just return the center point.
|
||||
//RectangleF rect = s.CreateBBox();
|
||||
//return new Vector2D(rect.X + (rect.Width * 0.5f), rect.Y + (rect.Height * 0.5f));
|
||||
positions.Add(new Vector2D());
|
||||
}
|
||||
|
||||
// Done
|
||||
return positions;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -35,8 +35,7 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
/// Responsible for creating sector polygons.
|
||||
/// Performs triangulation of sectors by using ear clipping.
|
||||
/// </summary>
|
||||
/// See: http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
|
||||
public sealed class Triangulator
|
||||
public sealed class Triangulation
|
||||
{
|
||||
#region ================== Delegates
|
||||
|
||||
|
@ -62,32 +61,46 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
|
||||
#region ================== Variables
|
||||
|
||||
// Number of vertices per island
|
||||
private int[] islandvertices;
|
||||
|
||||
// Vertices that result from the triangulation, 3 per triangle.
|
||||
private Vector2D[] vertices;
|
||||
|
||||
// These sidedefs match with the vertices. If a vertex is not the start
|
||||
// along a sidedef, this list contains a null entry for that vertex.
|
||||
private Sidedef[] sidedefs;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Properties
|
||||
|
||||
public int[] IslandVertices { get { return islandvertices; } }
|
||||
public Vector2D[] Vertices { get { return vertices; } }
|
||||
public Sidedef[] Sidedefs { get { return sidedefs; } }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Constructor / Disposer
|
||||
|
||||
// Constructor
|
||||
public Triangulator()
|
||||
// I don't like using constructors that do more than simple initialization work
|
||||
public static Triangulation Create(Sector sector)
|
||||
{
|
||||
// Initialize
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
return new Triangulation(sector);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ================== Methods
|
||||
|
||||
// This triangulates a sector and stores it
|
||||
public TriangleList PerformTriangulation(Sector sector)
|
||||
// Constructor
|
||||
private Triangulation(Sector s)
|
||||
{
|
||||
// Initialize
|
||||
TriangleList triangles = new TriangleList();
|
||||
List<EarClipPolygon> polys;
|
||||
List<int> islandslist = new List<int>();
|
||||
List<Vector2D> verticeslist = new List<Vector2D>();
|
||||
List<Sidedef> sidedefslist = new List<Sidedef>();
|
||||
|
||||
// We have no destructor
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
/*
|
||||
* This process is divided into several steps:
|
||||
|
@ -104,20 +117,23 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
*/
|
||||
|
||||
// TRACING
|
||||
polys = DoTrace(sector);
|
||||
polys = DoTrace(s);
|
||||
|
||||
// CUTTING
|
||||
DoCutting(polys);
|
||||
|
||||
// EAR-CLIPPING
|
||||
foreach(EarClipPolygon p in polys) triangles.AddRange(DoEarClip(p));
|
||||
foreach(EarClipPolygon p in polys)
|
||||
islandslist.Add(DoEarClip(p, verticeslist, sidedefslist));
|
||||
|
||||
// Return result
|
||||
return triangles;
|
||||
// Make arrays
|
||||
islandvertices = islandslist.ToArray();
|
||||
vertices = verticeslist.ToArray();
|
||||
sidedefs = sidedefslist.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ================== Tracing
|
||||
|
||||
// This traces sector lines to create a polygon tree
|
||||
|
@ -489,10 +505,10 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
if(insertbefore != null)
|
||||
{
|
||||
// Find the position where we have to split the outer polygon
|
||||
split = new EarClipVertex(starttoright.GetCoordinatesAt(foundu));
|
||||
split = new EarClipVertex(starttoright.GetCoordinatesAt(foundu), null);
|
||||
|
||||
// Insert manual split vertices
|
||||
p.AddBefore(insertbefore, new EarClipVertex(split));
|
||||
p.AddBefore(insertbefore, new EarClipVertex(split, null));
|
||||
|
||||
// Start inserting from the start (do I make sense this time?)
|
||||
v1 = start;
|
||||
|
@ -505,8 +521,9 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
while(v1 != start);
|
||||
|
||||
// Insert manual split vertices
|
||||
p.AddBefore(insertbefore, new EarClipVertex(start.Value));
|
||||
p.AddBefore(insertbefore, new EarClipVertex(split));
|
||||
Sidedef sd = (insertbefore.Previous == null) ? insertbefore.List.Last.Value.Sidedef : insertbefore.Previous.Value.Sidedef;
|
||||
p.AddBefore(insertbefore, new EarClipVertex(start.Value, null));
|
||||
p.AddBefore(insertbefore, new EarClipVertex(split, sd));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,15 +533,16 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
|
||||
// This clips a polygon and returns the triangles
|
||||
// The polygon may not have any holes or islands
|
||||
private TriangleList DoEarClip(EarClipPolygon poly)
|
||||
/// See: http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
|
||||
private int DoEarClip(EarClipPolygon poly, List<Vector2D> verticeslist, List<Sidedef> sidedefslist)
|
||||
{
|
||||
LinkedList<EarClipVertex> verts = new LinkedList<EarClipVertex>();
|
||||
List<EarClipVertex> convexes = new List<EarClipVertex>(poly.Count);
|
||||
LinkedList<EarClipVertex> reflexes = new LinkedList<EarClipVertex>();
|
||||
LinkedList<EarClipVertex> eartips = new LinkedList<EarClipVertex>();
|
||||
TriangleList result = new TriangleList();
|
||||
EarClipVertex v, v1, v2;
|
||||
EarClipVertex[] t, t1, t2;
|
||||
int countvertices = 0;
|
||||
|
||||
// Go for all vertices to fill list
|
||||
foreach(EarClipVertex vec in poly)
|
||||
|
@ -564,7 +582,7 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
// Go for all convex vertices to see if they are ear tips
|
||||
foreach(EarClipVertex cv in convexes)
|
||||
{
|
||||
// Add when this a valid ear
|
||||
// Add when this is a valid ear
|
||||
t = GetTriangle(cv);
|
||||
if(CheckValidEar(t, reflexes)) cv.AddEarTip(eartips);
|
||||
}
|
||||
|
@ -577,7 +595,8 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
t = GetTriangle(v);
|
||||
|
||||
// Add ear as triangle
|
||||
result.Add(t);
|
||||
AddTriangleToList(t, verticeslist, sidedefslist, (verts.Count > 3));
|
||||
countvertices += 3;
|
||||
|
||||
// Remove this ear from all lists
|
||||
v.Remove();
|
||||
|
@ -619,9 +638,9 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
|
||||
// Dispose remaining vertices
|
||||
foreach(EarClipVertex ecv in verts) ecv.Dispose();
|
||||
|
||||
// Return result
|
||||
return result;
|
||||
|
||||
// Return the number of vertices in the result
|
||||
return countvertices;
|
||||
}
|
||||
|
||||
// This checks if a given ear is a valid (no intersections from reflex vertices)
|
||||
|
@ -666,6 +685,21 @@ namespace CodeImp.DoomBuilder.Geometry
|
|||
(Line2D.GetSideOfLine(t[2].Position, t[0].Position, p) < 0.00001f);
|
||||
}
|
||||
|
||||
// This adds an array of vertices
|
||||
private void AddTriangleToList(EarClipVertex[] triangle, List<Vector2D> verticeslist, List<Sidedef> sidedefslist, bool last)
|
||||
{
|
||||
// Create triangle
|
||||
verticeslist.Add(triangle[0].Position);
|
||||
sidedefslist.Add(triangle[0].Sidedef);
|
||||
verticeslist.Add(triangle[1].Position);
|
||||
sidedefslist.Add(triangle[1].Sidedef);
|
||||
verticeslist.Add(triangle[2].Position);
|
||||
if(last) sidedefslist.Add(null); else sidedefslist.Add(triangle[2].Sidedef);
|
||||
|
||||
// Modify the first earclipvertex of this triangle, it no longer lies along a sidedef
|
||||
triangle[0].Sidedef = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -72,8 +72,8 @@ namespace CodeImp.DoomBuilder.Map
|
|||
// Triangulation
|
||||
private bool updateneeded;
|
||||
private bool triangulationneeded;
|
||||
private TriangleList triverts;
|
||||
private FlatVertex[] vertices;
|
||||
private Triangulation triangles;
|
||||
private FlatVertex[] flatvertices;
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -96,7 +96,8 @@ namespace CodeImp.DoomBuilder.Map
|
|||
public bool Marked { get { return marked; } set { marked = value; } }
|
||||
public bool UpdateNeeded { get { return updateneeded; } set { updateneeded |= value; triangulationneeded |= value; } }
|
||||
public Sector Clone { get { return clone; } set { clone = value; } }
|
||||
public FlatVertex[] Vertices { get { return vertices; } }
|
||||
public Triangulation Triangles { get { return triangles; } }
|
||||
public FlatVertex[] FlatVertices { get { return flatvertices; } }
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -214,10 +215,10 @@ namespace CodeImp.DoomBuilder.Map
|
|||
if(updateneeded)
|
||||
{
|
||||
// Triangulate again?
|
||||
if(triangulationneeded || (triverts == null))
|
||||
if(triangulationneeded || (triangles == null))
|
||||
{
|
||||
// Triangulate sector
|
||||
triverts = General.EarClipper.PerformTriangulation(this);
|
||||
triangles = Triangulation.Create(this);
|
||||
}
|
||||
|
||||
// Brightness color (alpha is opaque)
|
||||
|
@ -228,15 +229,15 @@ namespace CodeImp.DoomBuilder.Map
|
|||
int brightint = brightcolor.ToInt();
|
||||
|
||||
// Make vertices
|
||||
vertices = new FlatVertex[triverts.Count];
|
||||
for(int i = 0; i < triverts.Count; i++)
|
||||
flatvertices = new FlatVertex[triangles.Vertices.Length];
|
||||
for(int i = 0; i < triangles.Vertices.Length; i++)
|
||||
{
|
||||
vertices[i].x = triverts[i].x;
|
||||
vertices[i].y = triverts[i].y;
|
||||
vertices[i].z = 1.0f;
|
||||
vertices[i].c = brightint;
|
||||
vertices[i].u = triverts[i].x;
|
||||
vertices[i].v = triverts[i].y;
|
||||
flatvertices[i].x = triangles.Vertices[i].x;
|
||||
flatvertices[i].y = triangles.Vertices[i].y;
|
||||
flatvertices[i].z = 1.0f;
|
||||
flatvertices[i].c = brightint;
|
||||
flatvertices[i].u = triangles.Vertices[i].x;
|
||||
flatvertices[i].v = triangles.Vertices[i].y;
|
||||
}
|
||||
|
||||
// Updated
|
||||
|
@ -249,7 +250,7 @@ namespace CodeImp.DoomBuilder.Map
|
|||
#region ================== Methods
|
||||
|
||||
// This creates a bounding box rectangle
|
||||
// This requires the sector polygon to be up-to-date!
|
||||
// This requires the sector triangulation to be up-to-date!
|
||||
public RectangleF CreateBBox()
|
||||
{
|
||||
// Setup
|
||||
|
@ -259,7 +260,7 @@ namespace CodeImp.DoomBuilder.Map
|
|||
float bottom = float.MinValue;
|
||||
|
||||
// Go for vertices
|
||||
foreach(FlatVertex v in vertices)
|
||||
foreach(Vector2D v in triangles.Vertices)
|
||||
{
|
||||
// Update rect
|
||||
if(v.x < left) left = v.x;
|
||||
|
|
Loading…
Reference in a new issue