From 599b8ce6670002c82a2053be3d4f570a5b567b56 Mon Sep 17 00:00:00 2001 From: codeimp Date: Mon, 12 Jan 2009 22:00:31 +0000 Subject: [PATCH] - added texture Flood-Fill action in Visual Mode - fixed list of used key combinations on Controls preferences when action without key is selected - fixed automatic texture loading when geometry with new textures is pasted in the map --- .../ClassicModes/EditSelectionMode.cs | 1 + Source/BuilderModes/Resources/Actions.cfg | 11 ++ .../VisualModes/BaseVisualGeometrySector.cs | 1 + .../VisualModes/BaseVisualGeometrySidedef.cs | 45 ++++++++ .../VisualModes/BaseVisualMode.cs | 9 ++ .../VisualModes/BaseVisualThing.cs | 1 + .../VisualModes/IVisualEventReceiver.cs | 1 + Source/Geometry/Tools.cs | 109 +++++++++++++++++- Source/Windows/PreferencesForm.cs | 24 ++-- 9 files changed, 189 insertions(+), 13 deletions(-) diff --git a/Source/BuilderModes/ClassicModes/EditSelectionMode.cs b/Source/BuilderModes/ClassicModes/EditSelectionMode.cs index 9f8feae8..8382313d 100644 --- a/Source/BuilderModes/ClassicModes/EditSelectionMode.cs +++ b/Source/BuilderModes/ClassicModes/EditSelectionMode.cs @@ -885,6 +885,7 @@ namespace CodeImp.DoomBuilder.BuilderModes General.Map.Map.SnapAllToAccuracy(); // Update cached values + General.Map.Data.UpdateUsedTextures(); General.Map.Map.Update(); // Clear selection diff --git a/Source/BuilderModes/Resources/Actions.cfg b/Source/BuilderModes/Resources/Actions.cfg index 4c6a3892..8db71434 100644 --- a/Source/BuilderModes/Resources/Actions.cfg +++ b/Source/BuilderModes/Resources/Actions.cfg @@ -554,3 +554,14 @@ placevisualstart allowmouse = true; allowscroll = true; } + +floodfilltextures +{ + title = "Flood Fill Textures"; + category = "visual"; + description = "This allows you to select a texture and flood-fill all adjacent textures that are identical to the original with the selected texture."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 65540; +} diff --git a/Source/BuilderModes/VisualModes/BaseVisualGeometrySector.cs b/Source/BuilderModes/VisualModes/BaseVisualGeometrySector.cs index 95624902..b89a7da3 100644 --- a/Source/BuilderModes/VisualModes/BaseVisualGeometrySector.cs +++ b/Source/BuilderModes/VisualModes/BaseVisualGeometrySector.cs @@ -87,6 +87,7 @@ namespace CodeImp.DoomBuilder.BuilderModes public virtual void OnToggleLowerUnpegged() { } public virtual void OnResetTextureOffset() { } protected virtual void SetTexture(string texturename) { } + public virtual void OnTextureFloodfill() { } // Processing public virtual void OnProcess(double deltatime) diff --git a/Source/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs b/Source/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs index 4de444f1..473a2434 100644 --- a/Source/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs +++ b/Source/BuilderModes/VisualModes/BaseVisualGeometrySidedef.cs @@ -200,6 +200,51 @@ namespace CodeImp.DoomBuilder.BuilderModes if(parts.middlesingle != null) parts.middlesingle.Setup(); if(parts.upper != null) parts.upper.Setup(); } + + // Flood-fill textures + public virtual void OnTextureFloodfill() + { + if(General.Interface.IsActiveWindow) + { + string oldtexture = GetTextureName(); + string newtexture = General.Interface.BrowseTexture(General.Interface, oldtexture); + if(newtexture != oldtexture) + { + General.Map.UndoRedo.CreateUndo("Flood-fill textures with " + newtexture); + + mode.Renderer.SetCrosshairBusy(true); + General.Interface.RedrawDisplay(); + + // Get the texture + ImageData newtextureimage = General.Map.Data.GetTextureImage(newtexture); + if(newtextureimage != null) + { + // Do the alignment + Tools.FloodfillTextures(this.Sidedef, base.Texture, newtextureimage, true); + + // Get the changed sidedefs + List changes = General.Map.Map.GetMarkedSidedefs(true); + foreach(Sidedef sd in changes) + { + // Update the parts for this sidedef! + if(mode.VisualSectorExists(sd.Sector)) + { + BaseVisualSector vs = (mode.GetVisualSector(sd.Sector) as BaseVisualSector); + VisualSidedefParts parts = vs.GetSidedefParts(sd); + if(parts.lower != null) parts.lower.Setup(); + if(parts.middledouble != null) parts.middledouble.Setup(); + if(parts.middlesingle != null) parts.middlesingle.Setup(); + if(parts.upper != null) parts.upper.Setup(); + } + } + + General.Map.Data.UpdateUsedTextures(); + mode.Renderer.SetCrosshairBusy(false); + mode.ShowTargetInfo(); + } + } + } + } // Auto-align texture X offsets public virtual void OnTextureAlign(bool alignx, bool aligny) diff --git a/Source/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/BuilderModes/VisualModes/BaseVisualMode.cs index bf37a595..4111b089 100644 --- a/Source/BuilderModes/VisualModes/BaseVisualMode.cs +++ b/Source/BuilderModes/VisualModes/BaseVisualMode.cs @@ -76,6 +76,7 @@ namespace CodeImp.DoomBuilder.BuilderModes #region ================== Properties + public IRenderer3D Renderer { get { return renderer; } } public string CopiedTexture { get { return copiedtexture; } set { copiedtexture = value; } } public string CopiedFlat { get { return copiedflat; } set { copiedflat = value; } } @@ -546,6 +547,14 @@ namespace CodeImp.DoomBuilder.BuilderModes if(target.picked != null) (target.picked as IVisualEventReceiver).OnResetTextureOffset(); ShowTargetInfo(); } + + [BeginAction("floodfilltextures")] + public void FloodfillTextures() + { + PickTargetUnlocked(); + if(target.picked != null) (target.picked as IVisualEventReceiver).OnTextureFloodfill(); + ShowTargetInfo(); + } #endregion } diff --git a/Source/BuilderModes/VisualModes/BaseVisualThing.cs b/Source/BuilderModes/VisualModes/BaseVisualThing.cs index fc501e0c..083b4c88 100644 --- a/Source/BuilderModes/VisualModes/BaseVisualThing.cs +++ b/Source/BuilderModes/VisualModes/BaseVisualThing.cs @@ -349,6 +349,7 @@ namespace CodeImp.DoomBuilder.BuilderModes public virtual void OnToggleLowerUnpegged() { } public virtual void OnResetTextureOffset() { } public virtual void OnProcess(double deltatime) { } + public virtual void OnTextureFloodfill() { } // Return texture name public virtual string GetTextureName() { return ""; } diff --git a/Source/BuilderModes/VisualModes/IVisualEventReceiver.cs b/Source/BuilderModes/VisualModes/IVisualEventReceiver.cs index 2968dbd4..281bce24 100644 --- a/Source/BuilderModes/VisualModes/IVisualEventReceiver.cs +++ b/Source/BuilderModes/VisualModes/IVisualEventReceiver.cs @@ -52,6 +52,7 @@ namespace CodeImp.DoomBuilder.BuilderModes void OnCopyTexture(); void OnPasteTexture(); void OnTextureAlign(bool alignx, bool aligny); + void OnTextureFloodfill(); void OnToggleUpperUnpegged(); void OnToggleLowerUnpegged(); void OnProcess(double deltatime); diff --git a/Source/Geometry/Tools.cs b/Source/Geometry/Tools.cs index 1cab3aeb..09089303 100644 --- a/Source/Geometry/Tools.cs +++ b/Source/Geometry/Tools.cs @@ -63,7 +63,15 @@ namespace CodeImp.DoomBuilder.Geometry // must be subtracted from the X offset first. public bool forward; } - + + private struct SidedefFillJob + { + public Sidedef sidedef; + + // Moving forward along the sidedef? + public bool forward; + } + #endregion #region ================== Constants @@ -1224,7 +1232,104 @@ namespace CodeImp.DoomBuilder.Geometry } #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(Sidedef start, ImageData texture, ImageData filltexture, bool resetsidemarks) + { + Stack todo = new Stack(50); + + // Begin with first sidedef + if(SidedefTextureMatch(start, texture.LongName)) + { + SidedefFillJob first = new SidedefFillJob(); + first.sidedef = start; + first.forward = true; + todo.Push(first); + } + + // Continue until nothing more to align + while(todo.Count > 0) + { + // Get the align job to do + SidedefFillJob j = todo.Pop(); + + if(j.forward) + { + Vertex v; + + // Apply texturing + if(j.sidedef.LongHighTexture == texture.LongName) j.sidedef.SetTextureHigh(filltexture.Name); + if(j.sidedef.LongMiddleTexture == texture.LongName) j.sidedef.SetTextureMid(filltexture.Name); + if(j.sidedef.LongLowTexture == texture.LongName) j.sidedef.SetTextureLow(filltexture.Name); + j.sidedef.Marked = true; + + // Add sidedefs forward (connected to the right vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start; + AddSidedefsForFloodfill(todo, v, true, texture.LongName); + + // Add sidedefs backward (connected to the left vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End; + AddSidedefsForFloodfill(todo, v, false, texture.LongName); + } + else + { + Vertex v; + + // Apply texturing + if(j.sidedef.LongHighTexture == texture.LongName) j.sidedef.SetTextureHigh(filltexture.Name); + if(j.sidedef.LongMiddleTexture == texture.LongName) j.sidedef.SetTextureMid(filltexture.Name); + if(j.sidedef.LongLowTexture == texture.LongName) j.sidedef.SetTextureLow(filltexture.Name); + j.sidedef.Marked = true; + + // Add sidedefs backward (connected to the left vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End; + AddSidedefsForFloodfill(todo, v, false, texture.LongName); + + // Add sidedefs forward (connected to the right vertex) + v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start; + AddSidedefsForFloodfill(todo, v, true, texture.LongName); + } + } + } + + // This adds the matching, unmarked sidedefs from a vertex for texture alignment + private static void AddSidedefsForFloodfill(Stack stack, Vertex v, bool forward, long texturelongname) + { + foreach(Linedef ld in v.Linedefs) + { + Sidedef side1 = forward ? ld.Front : ld.Back; + Sidedef side2 = forward ? ld.Back : ld.Front; + if((ld.Start == v) && (side1 != null) && !side1.Marked) + { + if(SidedefTextureMatch(side1, texturelongname)) + { + SidedefFillJob nj = new SidedefFillJob(); + nj.forward = forward; + nj.sidedef = side1; + stack.Push(nj); + } + } + else if((ld.End == v) && (side2 != null) && !side2.Marked) + { + if(SidedefTextureMatch(side2, texturelongname)) + { + SidedefFillJob nj = new SidedefFillJob(); + nj.forward = forward; + nj.sidedef = side2; + stack.Push(nj); + } + } + } + } + + #endregion + #region ================== Texture Alignment // This performs texture alignment along all walls that match with the same texture diff --git a/Source/Windows/PreferencesForm.cs b/Source/Windows/PreferencesForm.cs index eda5d7c7..3450e3b3 100644 --- a/Source/Windows/PreferencesForm.cs +++ b/Source/Windows/PreferencesForm.cs @@ -315,19 +315,21 @@ namespace CodeImp.DoomBuilder.Windows { // Get info int thiskey = (int)listactions.SelectedItems[0].SubItems[1].Tag; - - // Find actions with same key - foreach(ListViewItem item in listactions.Items) + if(thiskey != 0) { - // Don't count the selected action - if(item != listactions.SelectedItems[0]) + // Find actions with same key + foreach(ListViewItem item in listactions.Items) { - Action a = General.Actions[item.Name]; - int akey = (int)item.SubItems[1].Tag; - - // Check if the key combination matches - if((thiskey & a.ShortcutMask) == (akey & a.ShortcutMask)) - usedactions.Add(General.Actions.Categories[a.Category] + ": " + a.Title); + // Don't count the selected action + if(item != listactions.SelectedItems[0]) + { + Action a = General.Actions[item.Name]; + int akey = (int)item.SubItems[1].Tag; + + // Check if the key combination matches + if((thiskey & a.ShortcutMask) == (akey & a.ShortcutMask)) + usedactions.Add(a.Title + " (" + General.Actions.Categories[a.Category] + ")"); + } } } }