diff --git a/Source/Core/Geometry/Tools.cs b/Source/Core/Geometry/Tools.cs index c336b01..5d076db 100644 --- a/Source/Core/Geometry/Tools.cs +++ b/Source/Core/Geometry/Tools.cs @@ -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)) diff --git a/Source/Plugins/BuilderModes/General/BuilderModesTools.cs b/Source/Plugins/BuilderModes/General/BuilderModesTools.cs index ed5026f..1b0038e 100644 --- a/Source/Plugins/BuilderModes/General/BuilderModesTools.cs +++ b/Source/Plugins/BuilderModes/General/BuilderModesTools.cs @@ -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 originaltextures, string filltexture, bool resetsidemarks) + { + Stack todo = new Stack(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 stack, Vertex v, bool forward, HashSet 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 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 } } diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs index 9f127e7..bb9391e 100644 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs @@ -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,60 +583,89 @@ 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; - //mxd - private void SelectNeighbourLines(IEnumerable lines, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight) - { - foreach (Linedef line in lines) + if (forward) { - 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.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 SelectNeighbourSideParts(Sidedef side, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight) - { - BaseVisualSector s = mode.GetVisualSector(side.Sector) as BaseVisualSector; - if (s != null) + else { - 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); - - if (parts.middle3d != null) - { - foreach (VisualMiddle3D middle3D in parts.middle3d) - SelectNeighbourSidePart(middle3D, 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 SelectNeighbourSidePart(BaseVisualGeometrySidedef visualside, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight) - { - 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)) - { - visualside.SelectNeighbours(select, matchtexture, matchheight); - } - } - } + //mxd + private void SelectNeighbourLines(IEnumerable lines, Vertex v, Rectangle sourcerect, bool select, bool matchtexture, bool matchheight, bool forward) + { + 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, 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) + { + 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, bool forward) + { + 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))) + { + visualside.SelectNeighbours(select, matchtexture, matchheight, false, forward); + } + } + } //mxd public virtual bool IsSelected() diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs index 642e674..e4fbe0c 100644 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs @@ -4635,11 +4635,16 @@ namespace CodeImp.DoomBuilder.BuilderModes // This adds the matching, unmarked sidedefs from a vertex for texture alignment private void AddSidedefsForAlignment(Stack stack, Vertex v, bool forward, float offsetx, float scaleY, long texturelongname, bool udmf) { - foreach(Linedef ld in v.Linedefs) + foreach(Linedef ld in v.Linedefs) { 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 controlSides = GetControlSides(side1, udmf); //mxd