Added: more magic to the autoalign/select neighbours logic; These functions should not anymore wrap around to the opposite side of two-sided linedefs, allowing you to select back and front sides separately using shift+click, and reducing infinite broken autoalign loops.

This commit is contained in:
ZZYZX 2017-01-26 15:33:07 +02:00 committed by sphere
parent f8bbe45c7b
commit c677627c09
4 changed files with 202 additions and 50 deletions

View File

@ -48,7 +48,7 @@ namespace CodeImp.DoomBuilder.Geometry
public string newtexlow;
}
private struct SidedefFillJob
public struct SidedefFillJob
{
public Sidedef sidedef;
@ -1788,6 +1788,12 @@ namespace CodeImp.DoomBuilder.Geometry
{
Sidedef side1 = forward ? ld.Front : ld.Back;
Sidedef side2 = forward ? ld.Back : ld.Front;
// [ZZ] don't iterate the same linedef twice.
//
if ((side1 != null && side1.Marked) ||
(side2 != null && side2.Marked)) continue;
if((ld.Start == v) && (side1 != null) && !side1.Marked)
{
if(SidedefTextureMatch(side1, texturelongname))

View File

@ -692,5 +692,112 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
#endregion
#region ================== Texture Floodfill
// This performs texture floodfill along all walls that match with the same texture
// NOTE: This method uses the sidedefs marking to indicate which sides have been filled
// When resetsidemarks is set to true, all sidedefs will first be marked false (not aligned).
// Setting resetsidemarks to false is usefull to fill only within a specific selection
// (set the marked property to true for the sidedefs outside the selection)
public static void FloodfillTextures(BaseVisualMode mode, Sidedef start, HashSet<long> originaltextures, string filltexture, bool resetsidemarks)
{
Stack<Tools.SidedefFillJob> todo = new Stack<Tools.SidedefFillJob>(50);
// Mark all sidedefs false (they will be marked true when the texture is aligned)
if(resetsidemarks) General.Map.Map.ClearMarkedSidedefs(false);
// Begin with first sidedef
if(SidedefTextureMatch(mode, start, originaltextures))
todo.Push(new Tools.SidedefFillJob { sidedef = start, forward = true });
// Continue until nothing more to align
while(todo.Count > 0)
{
// Get the align job to do
Tools.SidedefFillJob j = todo.Pop();
//mxd. Get visual parts
if(mode.VisualSectorExists(j.sidedef.Sector))
{
VisualSidedefParts parts = ((BaseVisualSector)mode.GetVisualSector(j.sidedef.Sector)).GetSidedefParts(j.sidedef);
// Apply texturing
if((parts.upper != null && parts.upper.Triangles > 0) && originaltextures.Contains(j.sidedef.LongHighTexture))
j.sidedef.SetTextureHigh(filltexture);
if(((parts.middledouble != null && parts.middledouble.Triangles > 0) || (parts.middlesingle != null && parts.middlesingle.Triangles > 0)) && originaltextures.Contains(j.sidedef.LongMiddleTexture))
j.sidedef.SetTextureMid(filltexture);
if((parts.lower != null && parts.lower.Triangles > 0) && originaltextures.Contains(j.sidedef.LongLowTexture))
j.sidedef.SetTextureLow(filltexture);
}
j.sidedef.Marked = true;
if(j.forward)
{
// Add sidedefs forward (connected to the right vertex)
Vertex v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start;
AddSidedefsForFloodfill(mode, todo, v, true, originaltextures);
// Add sidedefs backward (connected to the left vertex)
v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End;
AddSidedefsForFloodfill(mode, todo, v, false, originaltextures);
}
else
{
// Add sidedefs backward (connected to the left vertex)
Vertex v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End;
AddSidedefsForFloodfill(mode, todo, v, false, originaltextures);
// Add sidedefs forward (connected to the right vertex)
v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start;
AddSidedefsForFloodfill(mode, todo, v, true, originaltextures);
}
}
}
// This adds the matching, unmarked sidedefs from a vertex for texture alignment
private static void AddSidedefsForFloodfill(BaseVisualMode mode, Stack<Tools.SidedefFillJob> stack, Vertex v, bool forward, HashSet<long> texturelongnames)
{
foreach(Linedef ld in v.Linedefs)
{
Sidedef side1 = (forward ? ld.Front : ld.Back);
Sidedef side2 = (forward ? ld.Back : ld.Front);
// [ZZ] don't iterate the same linedef twice.
//
if ((side1 != null && side1.Marked) ||
(side2 != null && side2.Marked)) continue;
if ((ld.Start == v) && (side1 != null) && !side1.Marked)
{
if(SidedefTextureMatch(mode, side1, texturelongnames))
stack.Push(new Tools.SidedefFillJob { forward = forward, sidedef = side1 });
}
else if((ld.End == v) && (side2 != null) && !side2.Marked)
{
if(SidedefTextureMatch(mode, side2, texturelongnames))
stack.Push(new Tools.SidedefFillJob { forward = forward, sidedef = side2 });
}
}
}
#endregion
#region ================== Texture Alignment
//mxd. This checks if any of the sidedef texture match the given textures
public static bool SidedefTextureMatch(BaseVisualMode mode, Sidedef sd, HashSet<long> texturelongnames)
{
if(!mode.VisualSectorExists(sd.Sector)) return false;
VisualSidedefParts parts = ((BaseVisualSector)mode.GetVisualSector(sd.Sector)).GetSidedefParts(sd);
return (texturelongnames.Contains(sd.LongHighTexture) && (parts.upper != null && parts.upper.Triangles > 0)) ||
(texturelongnames.Contains(sd.LongLowTexture) && (parts.lower != null && parts.lower.Triangles > 0)) ||
(texturelongnames.Contains(sd.LongMiddleTexture)
&& ((parts.middledouble != null && parts.middledouble.Triangles > 0) || (parts.middlesingle != null && parts.middlesingle.Triangles > 0)));
}
#endregion
}
}

View File

@ -562,7 +562,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
//mxd
public void SelectNeighbours(bool select, bool matchtexture, bool matchheight)
{
if (Sidedef.Sector == null || Triangles < 1 || (!matchtexture && !matchheight)) return;
SelectNeighbours(select, matchtexture, matchheight, true, true);
}
private void SelectNeighbours(bool select, bool matchtexture, bool matchheight, bool clearlinedefs, bool forward)
{
if(Sidedef.Sector == null || Triangles < 1 || (!matchtexture && !matchheight)) return;
Rectangle rect = BuilderModesTools.GetSidedefPartSize(this);
if(rect.Height == 0) return;
@ -578,57 +583,86 @@ namespace CodeImp.DoomBuilder.BuilderModes
mode.RemoveSelectedObject(this);
}
// [ZZ] use the marking system.
if (clearlinedefs) General.Map.Map.ClearMarkedLinedefs(false);
Sidedef.Line.Marked = true;
// Select
SelectNeighbourLines(Sidedef.Line.Start.Linedefs, rect, select, matchtexture, matchheight);
SelectNeighbourLines(Sidedef.Line.End.Linedefs, rect, select, matchtexture, matchheight);
Vertex v;
if (forward)
{
v = Sidedef.IsFront ? Sidedef.Line.End : Sidedef.Line.Start;
SelectNeighbourLines(v.Linedefs, v, rect, select, matchtexture, matchheight, true);
v = Sidedef.IsFront ? Sidedef.Line.Start : Sidedef.Line.End;
SelectNeighbourLines(v.Linedefs, v, rect, select, matchtexture, matchheight, false);
}
//mxd
private void SelectNeighbourLines(IEnumerable<Linedef> lines, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight)
else
{
foreach (Linedef line in lines)
{
if (line.Index == Sidedef.Line.Index) continue;
if (line.Front != null && line.Front.Sector != null)
SelectNeighbourSideParts(line.Front, sourcerect, select, matchtexture, matchheight);
if (line.Back != null && line.Back.Sector != null)
SelectNeighbourSideParts(line.Back, sourcerect, select, matchtexture, matchheight);
v = Sidedef.IsFront ? Sidedef.Line.Start : Sidedef.Line.End;
SelectNeighbourLines(v.Linedefs, v, rect, select, matchtexture, matchheight, false);
v = Sidedef.IsFront ? Sidedef.Line.End : Sidedef.Line.Start;
SelectNeighbourLines(v.Linedefs, v, rect, select, matchtexture, matchheight, true);
}
}
//mxd
private void SelectNeighbourSideParts(Sidedef side, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight)
private void SelectNeighbourLines(IEnumerable<Linedef> lines, Vertex v, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight, bool forward)
{
BaseVisualSector s = mode.GetVisualSector(side.Sector) as BaseVisualSector;
if (s != null)
foreach(Linedef line in lines)
{
// [ZZ] decide which side of the next linedef to iterate.
// do NOT do both
Sidedef next = null;
Sidedef side1 = forward ? line.Front : line.Back;
Sidedef side2 = forward ? line.Back : line.Front;
if (line.Start == v)
next = side1;
else if (line.End == v)
next = side2;
if (next == null || next.Sector == null)
continue;
SelectNeighbourSideParts(next, sourcerect, select, matchtexture, matchheight, forward);
}
}
//mxd
private void SelectNeighbourSideParts(Sidedef side, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight, bool forward)
{
if (side.Line.Marked)
return;
BaseVisualSector s = (BaseVisualSector)mode.GetVisualSector(side.Sector);
if(s != null)
{
VisualSidedefParts parts = s.GetSidedefParts(side);
SelectNeighbourSidePart(parts.lower, sourcerect, select, matchtexture, matchheight);
SelectNeighbourSidePart(parts.middlesingle, sourcerect, select, matchtexture, matchheight);
SelectNeighbourSidePart(parts.middledouble, sourcerect, select, matchtexture, matchheight);
SelectNeighbourSidePart(parts.upper, sourcerect, select, matchtexture, matchheight);
SelectNeighbourSidePart(parts.lower, sourcerect, select, matchtexture, matchheight, forward);
SelectNeighbourSidePart(parts.middlesingle, sourcerect, select, matchtexture, matchheight, forward);
SelectNeighbourSidePart(parts.middledouble, sourcerect, select, matchtexture, matchheight, forward);
SelectNeighbourSidePart(parts.upper, sourcerect, select, matchtexture, matchheight, forward);
if (parts.middle3d != null)
if(parts.middle3d != null)
{
foreach (VisualMiddle3D middle3D in parts.middle3d)
SelectNeighbourSidePart(middle3D, sourcerect, select, matchtexture, matchheight);
foreach(VisualMiddle3D middle3D in parts.middle3d)
SelectNeighbourSidePart(middle3D, sourcerect, select, matchtexture, matchheight, forward);
}
}
}
//mxd
private void SelectNeighbourSidePart(BaseVisualGeometrySidedef visualside, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight)
private void SelectNeighbourSidePart(BaseVisualGeometrySidedef visualside, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight, bool forward)
{
if (visualside != null && visualside.Triangles > 0 && visualside.Selected != select)
if(visualside != null && visualside.Triangles > 0 && visualside.Selected != select)
{
Rectangle r = BuilderModesTools.GetSidedefPartSize(visualside);
if (r.Width == 0 || r.Height == 0) return;
if ((matchtexture && visualside.Texture == Texture && r.IntersectsWith(sourcerect)) ||
(matchheight && sourcerect.Height == r.Height && sourcerect.Y == r.Y))
if(r.Width == 0 || r.Height == 0) return;
if((!matchtexture || (visualside.Texture == Texture && r.IntersectsWith(sourcerect))) &&
(!matchheight || (sourcerect.Height == r.Height && sourcerect.Y == r.Y)))
{
visualside.SelectNeighbours(select, matchtexture, matchheight);
visualside.SelectNeighbours(select, matchtexture, matchheight, false, forward);
}
}
}

View File

@ -4640,6 +4640,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
Sidedef side1 = forward ? ld.Front : ld.Back;
Sidedef side2 = forward ? ld.Back : ld.Front;
// [ZZ] I don't know what logic here is.
// I'm going to check if any side is marked, and if so, don't add.
if ((side1 != null && side1.Marked) ||
(side2 != null && side2.Marked)) continue;
if((ld.Start == v) && (side1 != null) && !side1.Marked)
{
List<Sidedef> controlSides = GetControlSides(side1, udmf); //mxd