some more drawing code (still unfinished)

This commit is contained in:
codeimp 2008-05-01 19:31:49 +00:00
parent c3cc910e0e
commit 72b14c37bd
9 changed files with 454 additions and 22 deletions

View file

@ -94,6 +94,7 @@
<Compile Include="Config\NodebuilderInfo.cs" />
<Compile Include="Geometry\Angle2D.cs" />
<Compile Include="Geometry\BSPTriangulator.cs" />
<Compile Include="Geometry\LinedefsTracePath.cs" />
<Compile Include="Geometry\LinedefAngleSorter.cs" />
<Compile Include="Geometry\EarClipTriangulator.cs" />
<Compile Include="Geometry\EarClipVertex.cs" />
@ -304,6 +305,7 @@
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="Map\Linedef.cs" />
<Compile Include="Map\LinedefSide.cs" />
<Compile Include="Map\MapOptions.cs" />
<Compile Include="Map\MapSet.cs" />
<Compile Include="Data\DataLocation.cs" />

View file

@ -94,6 +94,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
// No selection in this mode
General.Map.Map.ClearAllSelected();
General.Map.Map.ClearAllMarks();
// We have no destructor
GC.SuppressFinalize(this);
@ -131,8 +132,15 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
// Disenagaging
public override void Disengage()
{
List<Vertex> newverts = new List<Vertex>();
List<Linedef> newlines = new List<Linedef>();
List<Vertex> mergeverts = new List<Vertex>();
List<Vertex> nonmergeverts = new List<Vertex>(General.Map.Map.Vertices);
MapSet map = General.Map.Map;
base.Disengage();
Cursor.Current = Cursors.AppStarting;
// When not cancelled and points have been drawn
@ -142,29 +150,66 @@ namespace CodeImp.DoomBuilder.BuilderModes.Editing
General.Map.UndoRedo.CreateUndo("line draw", UndoGroup.None, 0);
// STEP 1: Create the new geometry
List<Vertex> newverts = new List<Vertex>();
List<Linedef> newlines = new List<Linedef>();
List<Vertex> mergeverts = new List<Vertex>();
List<Vertex> nonmergeverts = new List<Vertex>(map.Vertices);
// Make first vertex
Vertex v1 = map.CreateVertex((int)points[0].pos.x, (int)points[0].pos.y);
// Keep references
newverts.Add(v1);
if(points[0].stitch) mergeverts.Add(v1); else nonmergeverts.Add(v1);
// Go for all other points
for(int i = 1; i < points.Count; i++)
{
// Create vertex for point
Vertex v2 = map.CreateVertex((int)points[i].pos.x, (int)points[i].pos.y);
// Keep references
newverts.Add(v2);
if(points[i].stitch) mergeverts.Add(v2); else nonmergeverts.Add(v2);
// Create line between point and previous
Linedef ld = map.CreateLinedef(v1, v2);
ld.Marked = true;
ld.Selected = true;
newlines.Add(ld);
// Next
v1 = v2;
}
// STEP 2: Merge the new geometry
foreach(Vertex v in mergeverts) v.Marked = true;
MapSet.JoinVertices(mergeverts, mergeverts, true, General.Settings.StitchDistance);
map.StitchGeometry();
// STEP 3: Make sectors where possible
bool[] frontsdone = new bool[newlines.Count];
for(int i = 0; i < newlines.Count; i++)
{
Linedef ld = newlines[i];
// Front not marked as done?
if(!frontsdone[i])
{
// Make sector here
Sector newsector = map.MakeSector(ld, true);
if(newsector != null)
{
// Go for all sidedefs in this new sector
foreach(Sidedef sd in newsector.Sidedefs)
{
// Side matches with a side of our new lines?
int lineindex = newlines.IndexOf(sd.Line);
if(lineindex > -1)
{
// Mark this side as done
if(sd.IsFront) frontsdone[lineindex] = true;
}
}
}
}
}
// Update cached values
map.Update();

View file

@ -93,7 +93,7 @@ namespace CodeImp.DoomBuilder.Config
{
// Read the cache variables
blackbrowsers = cfg.ReadSetting("blackbrowsers", false);
stitchdistance = cfg.ReadSetting("stitchdistance", 2.0f);
stitchdistance = cfg.ReadSetting("stitchdistance", 1.0f);
undolevels = cfg.ReadSetting("undolevels", 20);
visualfov = cfg.ReadSetting("visualfov", 80);
visualmousesensx = cfg.ReadSetting("visualmousesensx", 40f);

View file

@ -0,0 +1,131 @@
#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Rendering;
using SlimDX.Direct3D;
using System.Drawing;
using CodeImp.DoomBuilder.Map;
#endregion
namespace CodeImp.DoomBuilder.Geometry
{
public class LinedefTracePath : List<Linedef>
{
#region ================== Constants
#endregion
#region ================== Variables
#endregion
#region ================== Properties
#endregion
#region ================== Constructor / Disposer
// Constructor
public LinedefTracePath()
{
// Initialize
}
// Constructor
public LinedefTracePath(IEnumerable<Linedef> lines) : base(lines)
{
// Initialize
}
// Constructor
public LinedefTracePath(ICollection<LinedefSide> lines) : base(lines.Count)
{
// Initialize
foreach(LinedefSide ls in lines) base.Add(ls.Line);
}
// Constructor
public LinedefTracePath(LinedefTracePath p, Linedef add) : base(p)
{
// Initialize
base.Add(add);
}
#endregion
#region ================== Methods
// This checks if the polygon is closed
public bool CheckIsClosed()
{
// There must be at least 2 sidedefs
if(base.Count > 1)
{
// The end sidedef must share a vertex with the first
return (base[0].Start == base[base.Count - 1].Start) ||
(base[0].Start == base[base.Count - 1].End) ||
(base[0].End == base[base.Count - 1].Start) ||
(base[0].End == base[base.Count - 1].End);
}
else
{
// Not closed
return false;
}
}
// This makes a polygon from the path
public Polygon MakePolygon()
{
Polygon p = new Polygon();
bool forward = true;
// Any sides at all?
if(base.Count > 0)
{
p.AddLast(new EarClipVertex(base[0].Start.Position));
// Add all lines, but the first
for(int i = 1; i < base.Count; i++)
{
// Traverse direction changes?
if((base[i - 1].Start == base[i].Start) ||
(base[i - 1].End == base[i].End))
forward = !forward;
// Add next vertex
if(forward)
p.AddLast(new EarClipVertex(base[i].Start.Position));
else
p.AddLast(new EarClipVertex(base[i].End.Position));
}
}
return p;
}
#endregion
}
}

View file

@ -31,7 +31,7 @@ using CodeImp.DoomBuilder.Map;
namespace CodeImp.DoomBuilder.Geometry
{
internal class SidedefsTracePath : List<Sidedef>
public class SidedefsTracePath : List<Sidedef>
{
#region ================== Constants

View file

@ -34,6 +34,7 @@ namespace CodeImp.DoomBuilder.Map
{
#region ================== Constants
public const float SIDE_POINT_DISTANCE = 0.001f;
public const int NUM_ARGS = 5;
public static readonly byte[] EMPTY_ARGS = new byte[NUM_ARGS];
@ -305,6 +306,26 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Methods
// This returns a point for testing on one side
public Vector2D GetSidePoint(bool front)
{
Vector2D n = new Vector2D();
n.x = (end.Position.x - start.Position.x) * lengthinv * SIDE_POINT_DISTANCE;
n.y = (end.Position.y - start.Position.y) * lengthinv * SIDE_POINT_DISTANCE;
if(front)
{
n.x = -n.x;
n.y = -n.y;
}
Vector2D p = new Vector2D();
p.x = start.Position.x + (end.Position.x - start.Position.x) * 0.5f - n.y;
p.y = start.Position.y + (end.Position.y - start.Position.y) * 0.5f + n.x;
return p;
}
// This applies single/double sided flags
public void ApplySidedFlags()
{
@ -493,6 +514,7 @@ namespace CodeImp.DoomBuilder.Map
{
nsd = map.CreateSidedef(nl, true, front.Sector);
front.CopyPropertiesTo(nsd);
nsd.Marked = front.Marked;
}
// Copy back sidedef if exists
@ -500,6 +522,7 @@ namespace CodeImp.DoomBuilder.Map
{
nsd = map.CreateSidedef(nl, false, back.Sector);
back.CopyPropertiesTo(nsd);
nsd.Marked = back.Marked;
}
// Return result
@ -660,6 +683,7 @@ namespace CodeImp.DoomBuilder.Map
{
sd = map.CreateSidedef(other, front, newside.Sector);
newside.CopyPropertiesTo(sd);
sd.Marked = newside.Marked;
}
}

88
Source/Map/LinedefSide.cs Normal file
View file

@ -0,0 +1,88 @@
#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Rendering;
using SlimDX.Direct3D;
using System.Drawing;
#endregion
namespace CodeImp.DoomBuilder.Map
{
/// <summary>
/// This is used to indicate a side of a line without the need for a sidedef.
/// </summary>
public class LinedefSide
{
#region ================== Constants
#endregion
#region ================== Variables
private Linedef line;
private bool front;
#endregion
#region ================== Properties
public Linedef Line { get { return line; } set { line = value; } }
public bool Front { get { return front; } set { front = value; } }
#endregion
#region ================== Constructor / Disposer
/// <summary>
/// This is used to indicate a side of a line without the need for a sidedef.
/// </summary>
public LinedefSide()
{
// Initialize
}
/// <summary>
/// This is used to indicate a side of a line without the need for a sidedef.
/// </summary>
public LinedefSide(Linedef line, bool front)
{
// Initialize
this.line = line;
this.front = front;
}
// Destructor
~LinedefSide()
{
}
#endregion
#region ================== Methods
#endregion
}
}

View file

@ -436,6 +436,7 @@ namespace CodeImp.DoomBuilder.Map
ClearMarkedThings(false);
ClearMarkedLinedefs(false);
ClearMarkedSectors(false);
ClearMarkedSidedefs(false);
}
// This clears marked vertices
@ -456,6 +457,12 @@ namespace CodeImp.DoomBuilder.Map
foreach(Linedef l in linedefs) l.Marked = mark;
}
// This clears marked sidedefs
public void ClearMarkedSidedefs(bool mark)
{
foreach(Sidedef s in sidedefs) s.Marked = mark;
}
// This clears marked sectors
public void ClearMarkedSectors(bool mark)
{
@ -497,25 +504,40 @@ namespace CodeImp.DoomBuilder.Map
// This creates a marking from selection
public void MarkSelectedVertices(bool selected, bool mark)
{
foreach(Vertex v in vertices) if(v.Selected == selected) v.Marked |= mark;
foreach(Vertex v in vertices) if(v.Selected == selected) v.Marked = mark;
}
// This creates a marking from selection
public void MarkSelectedLinedefs(bool selected, bool mark)
{
foreach(Linedef l in linedefs) if(l.Selected == selected) l.Marked |= mark;
foreach(Linedef l in linedefs) if(l.Selected == selected) l.Marked = mark;
}
// This creates a marking from selection
public void MarkSelectedSectors(bool selected, bool mark)
{
foreach(Sector s in sectors) if(s.Selected == selected) s.Marked |= mark;
foreach(Sector s in sectors) if(s.Selected == selected) s.Marked = mark;
}
// This creates a marking from selection
public void MarkSelectedThings(bool selected, bool mark)
{
foreach(Thing t in things) if(t.Selected == selected) t.Marked |= mark;
foreach(Thing t in things) if(t.Selected == selected) t.Marked = mark;
}
/// <summary>
/// This marks the front and back sidedefs on linedefs with the matching mark
/// </summary>
public void MarkSidedefsFromLinedefs(bool matchmark, bool setmark)
{
foreach(Linedef l in linedefs)
{
if(l.Marked == matchmark)
{
if(l.Front != null) l.Front.Marked = setmark;
if(l.Back != null) l.Back.Marked = setmark;
}
}
}
/// <summary>
@ -755,6 +777,114 @@ namespace CodeImp.DoomBuilder.Map
#region ================== Geometry Tools
/// <summary>
/// This automagically makes a sector, starting at one side of a line.
/// Returns the sector reference when created, return null when not created.
/// </summary>
public Sector MakeSector(Linedef line, bool front)
{
// Find inner path
List<LinedefSide> path = FindInnerMostPath(line, front);
if(path != null)
{
// Make polygon
LinedefTracePath tracepath = new LinedefTracePath(path);
Polygon poly = tracepath.MakePolygon();
// Check if the front of the line is inside the polygon
if(poly.Intersect(line.GetSidePoint(front)))
{
Sidedef source = null;
Sector newsector = CreateSector();
// Check if any of the sides already has a sidedef
// Then we use information from that sidedef to make the others
foreach(LinedefSide ls in path)
{
if(ls.Front)
{
if(ls.Line.Front != null)
{
source = ls.Line.Front;
source.Sector.CopyPropertiesTo(newsector);
break;
}
}
else
{
if(ls.Line.Back != null)
{
source = ls.Line.Back;
source.Sector.CopyPropertiesTo(newsector);
break;
}
}
}
// If we couldn't find anything, try the other sides
if(source == null)
{
foreach(LinedefSide ls in path)
{
if(ls.Front)
{
if(ls.Line.Back != null)
{
source = ls.Line.Back;
source.Sector.CopyPropertiesTo(newsector);
break;
}
}
else
{
if(ls.Line.Front != null)
{
source = ls.Line.Front;
source.Sector.CopyPropertiesTo(newsector);
break;
}
}
}
}
// Go for all sides to make sidedefs
foreach(LinedefSide ls in path)
{
if(ls.Front)
{
// Create sidedef is needed and ensure it points to the new sector
if(ls.Line.Front == null) CreateSidedef(ls.Line, true, newsector);
if(ls.Line.Front.Sector != newsector) ls.Line.Front.ChangeSector(newsector);
if(source != null) source.CopyPropertiesTo(ls.Line.Front); else source = ls.Line.Front;
}
else
{
// Create sidedef is needed and ensure it points to the new sector
if(ls.Line.Back == null) CreateSidedef(ls.Line, false, newsector);
if(ls.Line.Back.Sector != newsector) ls.Line.Back.ChangeSector(newsector);
if(source != null) source.CopyPropertiesTo(ls.Line.Back); else source = ls.Line.Back;
}
// Update line
ls.Line.ApplySidedFlags();
}
// Return the new sector
return newsector;
}
else
{
// Outside the map, can't create a sector
return null;
}
}
else
{
// Impossible to find a path!
return null;
}
}
// This joins overlapping lines together
// Returns the number of joins made
public static int JoinOverlappingLines(ICollection<Linedef> lines)
@ -781,8 +911,10 @@ namespace CodeImp.DoomBuilder.Map
if(l1 != l2)
{
// Merge these two linedefs
while(lines.Remove(l1));
l1.Join(l2);
//while(lines.Remove(l1));
//l1.Join(l2);
while(lines.Remove(l2)) ;
l2.Join(l1);
joinsdone++;
joined = true;
break;
@ -804,8 +936,10 @@ namespace CodeImp.DoomBuilder.Map
if(l1 != l2)
{
// Merge these two linedefs
while(lines.Remove(l1));
l1.Join(l2);
//while(lines.Remove(l1));
//l1.Join(l2);
while(lines.Remove(l2)) ;
l2.Join(l1);
joinsdone++;
joined = true;
break;
@ -1316,20 +1450,19 @@ namespace CodeImp.DoomBuilder.Map
/// This finds the inner path from the beginning of a line to the end of the line.
/// Returns null when no path could be found.
/// </summary>
public static ICollection<Linedef> FindInnerMostPath(Linedef start, bool front)
public List<LinedefSide> FindInnerMostPath(Linedef start, bool front)
{
MapSet map = start.Map;
List<Linedef> path = new List<Linedef>();
Dictionary<Linedef, int> tracecount = new Dictionary<Linedef, int>(map.Linedefs.Count);
List<LinedefSide> path = new List<LinedefSide>();
Dictionary<Linedef, int> tracecount = new Dictionary<Linedef, int>(linedefs.Count);
Linedef nextline = start;
bool nextfront = front;
do
{
// Add line to path
path.Add(nextline);
tracecount[nextline]++;
path.Add(new LinedefSide(nextline, nextfront));
if(!tracecount.ContainsKey(nextline)) tracecount.Add(nextline, 1); else tracecount[nextline]++;
// Determine next vertex to use
Vertex v = nextfront ? nextline.End : nextline.Start;
@ -1356,7 +1489,12 @@ namespace CodeImp.DoomBuilder.Map
else
{
// Trace along the next line
Linedef prevline = nextline;
if(lines[0] == nextline) nextline = lines[1]; else nextline = lines[0];
// Check if front side changes
if((prevline.Start == nextline.Start) ||
(prevline.End == nextline.End)) nextfront = !nextfront;
}
}
// Continue as long as we have not reached the start yet

View file

@ -62,6 +62,9 @@ namespace CodeImp.DoomBuilder.Map
// Additional fields
private SortedList<string, object> fields;
// Selections
private bool marked;
// Disposing
private bool isdisposed = false;
@ -84,6 +87,7 @@ namespace CodeImp.DoomBuilder.Map
public long LongHighTexture { get { return longtexnamehigh; } }
public long LongMiddleTexture { get { return longtexnamemid; } }
public long LongLowTexture { get { return longtexnamelow; } }
public bool Marked { get { return marked; } set { marked = value; } }
public SortedList<string, object> Fields { get { return fields; } }
#endregion