diff --git a/Source/Core/Builder.csproj b/Source/Core/Builder.csproj
index d50d8206..a81cc69e 100644
--- a/Source/Core/Builder.csproj
+++ b/Source/Core/Builder.csproj
@@ -252,6 +252,8 @@
+
+
Form
diff --git a/Source/Core/General/MapManager.cs b/Source/Core/General/MapManager.cs
index 96cf2fd9..f5d1df49 100755
--- a/Source/Core/General/MapManager.cs
+++ b/Source/Core/General/MapManager.cs
@@ -2181,6 +2181,7 @@ namespace CodeImp.DoomBuilder
// Update settings
renderer3d.CreateProjection();
renderer3d.UpdateVertexHandle(); //mxd
+ renderer3d.UpdateVisualSlopeHandle();
// Things filters
General.MainWindow.UpdateThingsFilters();
diff --git a/Source/Core/Rendering/IRenderer3D.cs b/Source/Core/Rendering/IRenderer3D.cs
index 28ea5866..17a7579e 100755
--- a/Source/Core/Rendering/IRenderer3D.cs
+++ b/Source/Core/Rendering/IRenderer3D.cs
@@ -50,6 +50,7 @@ namespace CodeImp.DoomBuilder.Rendering
void AddSectorGeometry(VisualGeometry g);
void AddThingGeometry(VisualThing t);
void SetVisualVertices(List verts);
+ void SetVisualSlopeHandles(List handles);
void SetEventLines(List lines);
void RenderCrosshair();
void SetFogMode(bool usefog);
diff --git a/Source/Core/Rendering/RenderDevice.cs b/Source/Core/Rendering/RenderDevice.cs
index 54375cc1..134a7ec7 100755
--- a/Source/Core/Rendering/RenderDevice.cs
+++ b/Source/Core/Rendering/RenderDevice.cs
@@ -68,6 +68,7 @@ namespace CodeImp.DoomBuilder.Rendering
DeclareUniform(UniformName.fogcolor, "fogcolor", UniformType.Vec4f);
DeclareUniform(UniformName.sectorfogcolor, "sectorfogcolor", UniformType.Vec4f);
DeclareUniform(UniformName.lightsEnabled, "lightsEnabled", UniformType.Float);
+ DeclareUniform(UniformName.slopeHandleLength, "slopeHandleLength", UniformType.Float);
// 2d fsaa
CompileShader(ShaderName.display2d_fsaa, "display2d.shader", "display2d_fsaa");
@@ -100,6 +101,9 @@ namespace CodeImp.DoomBuilder.Rendering
CompileShader(ShaderName.world3d_main_fog_vertexcolor, "world3d.shader", "world3d_main_fog_vertexcolor");
CompileShader(ShaderName.world3d_main_highlight_fog_vertexcolor, "world3d.shader", "world3d_main_highlight_fog_vertexcolor");
+ // Slope handle
+ CompileShader(ShaderName.world3d_slope_handle, "world3d.shader", "world3d_slope_handle");
+
SetupSettings();
}
@@ -730,7 +734,8 @@ namespace CodeImp.DoomBuilder.Rendering
world3d_p13,
world3d_main_highlight_fog_vertexcolor,
world3d_vertex_color,
- world3d_constant_color
+ world3d_constant_color,
+ world3d_slope_handle
}
public enum UniformType : int
@@ -772,7 +777,8 @@ namespace CodeImp.DoomBuilder.Rendering
fogsettings,
fogcolor,
sectorfogcolor,
- lightsEnabled
+ lightsEnabled,
+ slopeHandleLength
}
public enum VertexFormat : int { Flat, World }
diff --git a/Source/Core/Rendering/Renderer3D.cs b/Source/Core/Rendering/Renderer3D.cs
index 4a753ff0..f8e86840 100755
--- a/Source/Core/Rendering/Renderer3D.cs
+++ b/Source/Core/Rendering/Renderer3D.cs
@@ -64,7 +64,10 @@ namespace CodeImp.DoomBuilder.Rendering
//mxd
private VisualVertexHandle vertexhandle;
private int[] lightOffsets;
-
+
+ // Slope handle
+ private VisualSlopeHandle visualslopehandle;
+
// Crosshair
private FlatVertex[] crosshairverts;
private bool crosshairbusy;
@@ -112,6 +115,9 @@ namespace CodeImp.DoomBuilder.Rendering
//mxd. Visual vertices
private List visualvertices;
+ // Visual slope handles
+ private List visualslopehandles;
+
//mxd. Event lines
private List eventlines;
@@ -166,7 +172,8 @@ namespace CodeImp.DoomBuilder.Rendering
{
// Clean up
if(vertexhandle != null) vertexhandle.Dispose(); //mxd
-
+ if (visualslopehandle != null) visualslopehandle.Dispose();
+
// Done
base.Dispose();
}
@@ -232,8 +239,17 @@ namespace CodeImp.DoomBuilder.Rendering
}
}
+ internal void UpdateVisualSlopeHandle()
+ {
+ if (visualslopehandle != null)
+ {
+ visualslopehandle.UnloadResource();
+ visualslopehandle.ReloadResource();
+ }
+ }
+
#endregion
-
+
#region ================== Presentation
// This creates the projection
@@ -344,7 +360,10 @@ namespace CodeImp.DoomBuilder.Rendering
//mxd. Crate vertex handle
if(vertexhandle == null) vertexhandle = new VisualVertexHandle();
-
+
+ // Create slope handle
+ if (visualslopehandle == null) visualslopehandle = new VisualSlopeHandle();
+
// Ready
return true;
}
@@ -447,8 +466,12 @@ namespace CodeImp.DoomBuilder.Rendering
//mxd. Visual vertices
RenderVertices();
+ // Slope handles
+ if (General.Map.UDMF /* && General.Settings.ShowVisualSlopeHandles */)
+ RenderSlopeHandles();
+
//mxd. Event lines
- if(General.Settings.GZShowEventLines) RenderArrows(eventlines);
+ if (General.Settings.GZShowEventLines) RenderArrows(eventlines);
// Remove references
graphics.SetTexture(null);
@@ -615,6 +638,45 @@ namespace CodeImp.DoomBuilder.Rendering
graphics.SetUniform(UniformName.texturefactor, new Color4(1f, 1f, 1f, 1f));
}
+ private void RenderSlopeHandles()
+ {
+ if (visualslopehandles == null || !showselection) return;
+
+ graphics.SetAlphaBlendEnable(true);
+ graphics.SetAlphaTestEnable(false);
+ graphics.SetZWriteEnable(false);
+ graphics.SetSourceBlend(Blend.SourceAlpha);
+ graphics.SetDestinationBlend(Blend.InverseSourceAlpha);
+
+ graphics.SetShader(ShaderName.world3d_slope_handle);
+
+ foreach (VisualSlope handle in visualslopehandles)
+ {
+ PixelColor color = General.Colors.Vertices;
+
+ if (handle.Pivot)
+ color = General.Colors.Guideline;
+ else if (handle.Selected)
+ color = General.Colors.Selection3D;
+ else if (handle == highlighted)
+ color = General.Colors.Highlight3D;
+ else if (handle.SmartPivot)
+ color = General.Colors.Vertices;
+
+ world = handle.Position;
+ graphics.SetUniform(UniformName.world, ref world);
+ graphics.SetUniform(UniformName.slopeHandleLength, handle.Length);
+ graphics.SetUniform(UniformName.vertexColor, color.ToColorValue());
+
+ graphics.SetVertexBuffer(visualslopehandle.Geometry);
+ graphics.Draw(PrimitiveType.TriangleList, 0, 2);
+
+ }
+
+ // Done
+ graphics.SetUniform(UniformName.texturefactor, new Color4(1f, 1f, 1f, 1f));
+ }
+
//mxd
private void RenderArrows(ICollection lines)
{
@@ -1755,6 +1817,8 @@ namespace CodeImp.DoomBuilder.Rendering
//mxd
public void SetVisualVertices(List verts) { visualvertices = verts; }
+ public void SetVisualSlopeHandles(List handles) { visualslopehandles = handles; }
+
//mxd
public void SetEventLines(List lines) { eventlines = lines; }
diff --git a/Source/Core/Rendering/VisualSlopeHandle.cs b/Source/Core/Rendering/VisualSlopeHandle.cs
new file mode 100644
index 00000000..7b01a2d3
--- /dev/null
+++ b/Source/Core/Rendering/VisualSlopeHandle.cs
@@ -0,0 +1,89 @@
+#region ================== Namespaces
+
+using System;
+
+#endregion
+
+namespace CodeImp.DoomBuilder.Rendering
+{
+ internal sealed class VisualSlopeHandle : IDisposable, IRenderResource
+ {
+ #region ================== Variables
+
+ private VertexBuffer geometry;
+ private bool isdisposed;
+
+ #endregion
+
+ #region ================== Properties
+
+ public VertexBuffer Geometry { get { return geometry; } }
+
+ #endregion
+
+ #region ================== Constructor / Disposer
+
+ public VisualSlopeHandle()
+ {
+ // Create geometry
+ ReloadResource();
+
+ // Register as source
+ General.Map.Graphics.RegisterResource(this);
+ }
+
+ public void Dispose()
+ {
+ // Not already disposed?
+ if (!isdisposed)
+ {
+ if (geometry != null)
+ geometry.Dispose();
+
+ // Unregister resource
+ General.Map.Graphics.UnregisterResource(this);
+
+ // Done
+ isdisposed = true;
+ }
+ }
+
+ #endregion
+
+ #region ================== Methods
+
+ // This is called resets when the device is reset
+ // (when resized or display adapter was changed)
+ public void ReloadResource()
+ {
+ WorldVertex v0 = new WorldVertex(0.0f, -8.0f, 0.1f);
+ WorldVertex v1 = new WorldVertex(0.0f, 0.0f, 0.1f);
+ WorldVertex v2 = new WorldVertex(1.0f, 0.0f, 0.1f);
+ WorldVertex v3 = new WorldVertex(1.0f, -8.0f, 0.1f);
+
+ v1.c = v2.c = PixelColor.INT_WHITE;
+ v0.c = v3.c = PixelColor.INT_WHITE_NO_ALPHA;
+
+ WorldVertex[] vertices = new[]
+ {
+ v0, v1, v2,
+ v0, v2, v3
+ };
+
+ geometry = new VertexBuffer();
+ General.Map.Graphics.SetBufferData(geometry, vertices);
+ }
+
+ // This is called before a device is reset
+ // (when resized or display adapter was changed)
+ public void UnloadResource()
+ {
+ if (geometry != null)
+ geometry.Dispose();
+
+ geometry = null;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Source/Core/Resources/world3d.shader b/Source/Core/Resources/world3d.shader
index d400c70c..84503bd7 100755
--- a/Source/Core/Resources/world3d.shader
+++ b/Source/Core/Resources/world3d.shader
@@ -25,6 +25,9 @@ uniforms
float ignoreNormals;
float lightsEnabled;
+ // Slope handle length
+ float slopeHandleLength;
+
}
functions
@@ -355,4 +358,16 @@ shader world3d_main_highlight_fog_vertexcolor extends world3d_main_highlight_fog
v2f.UV = in.TextureCoordinate;
v2f.Normal = normalize((modelnormal * vec4(in.Normal, 1.0)).xyz);
}
+}
+
+// Slope handle shader
+shader world3d_slope_handle extends world3d_vertex_color
+{
+ vertex
+ {
+ v2f.viewpos = view * world * vec4(in.Position.x * slopeHandleLength, in.Position.y, in.Position.z, 1.0);
+ gl_Position = projection * v2f.viewpos;
+ v2f.Color = in.Color * vertexColor;
+ v2f.UV = in.TextureCoordinate;
+ }
}
\ No newline at end of file
diff --git a/Source/Core/VisualModes/VisualMode.cs b/Source/Core/VisualModes/VisualMode.cs
index 2c750ad9..9bb71f8b 100755
--- a/Source/Core/VisualModes/VisualMode.cs
+++ b/Source/Core/VisualModes/VisualMode.cs
@@ -30,6 +30,12 @@ using CodeImp.DoomBuilder.Editing;
namespace CodeImp.DoomBuilder.VisualModes
{
+ public enum PickingMode
+ {
+ Default,
+ SlopeHandles
+ }
+
///
/// Provides specialized functionality for a visual (3D) Doom Builder editing mode.
///
@@ -68,10 +74,14 @@ namespace CodeImp.DoomBuilder.VisualModes
private Vector3D playerStartPosition;
private float playerStartAngle;
+ // For picking
+ protected PickingMode pickingmode;
+
// Map
protected VisualBlockMap blockmap;
protected Dictionary allthings;
protected Dictionary allsectors;
+ protected Dictionary> allslopehandles;
protected List visibleblocks;
protected List visiblethings;
protected List visiblesectors;
@@ -85,6 +95,7 @@ namespace CodeImp.DoomBuilder.VisualModes
public bool ProcessThings { get { return processthings; } set { processthings = value; } }
public VisualBlockMap BlockMap { get { return blockmap; } }
public Dictionary VisualVertices { get { return vertices; } } //mxd
+ public Dictionary> AllSlopeHandles { get { return allslopehandles; } }
// Rendering
public IRenderer3D Renderer { get { return renderer; } }
@@ -103,6 +114,7 @@ namespace CodeImp.DoomBuilder.VisualModes
this.blockmap = new VisualBlockMap();
this.allsectors = new Dictionary(General.Map.Map.Sectors.Count);
this.allthings = new Dictionary(General.Map.Map.Things.Count);
+ this.allslopehandles = new Dictionary>(General.Map.Map.Sectors.Count);
this.visibleblocks = new List();
this.visiblesectors = new List(50);
this.visiblegeometry = new List(200);
@@ -110,6 +122,7 @@ namespace CodeImp.DoomBuilder.VisualModes
this.processgeometry = true;
this.processthings = true;
this.vertices = new Dictionary(); //mxd
+ this.pickingmode = PickingMode.Default;
//mxd. Synch camera position to cursor position or center of the screen in 2d-mode
if(General.Settings.GZSynchCameras && General.Editing.Mode is ClassicMode)
@@ -223,8 +236,8 @@ namespace CodeImp.DoomBuilder.VisualModes
// Dispose
foreach(KeyValuePair vt in allthings)
- if(vt.Value != null) vt.Value.Dispose();
-
+ if(vt.Value != null) vt.Value.Dispose();
+
// Apply camera position to thing
General.Map.VisualCamera.ApplyToThing();
@@ -720,6 +733,10 @@ namespace CodeImp.DoomBuilder.VisualModes
VisualSector vs = allsectors[General.Map.VisualCamera.Sector];
sectors.Add(General.Map.VisualCamera.Sector, vs);
foreach(VisualGeometry g in vs.FixedGeometry) pickables.Add(g);
+
+ // Add slope handles
+ if (General.Map.UDMF && pickingmode == PickingMode.SlopeHandles && allslopehandles.ContainsKey(General.Map.VisualCamera.Sector))
+ pickables.AddRange(allslopehandles[General.Map.VisualCamera.Sector]);
}
// Go for all lines to see which ones we intersect
@@ -758,12 +775,16 @@ namespace CodeImp.DoomBuilder.VisualModes
if(!sectors.ContainsKey(ld.Front.Sector))
{
sectors.Add(ld.Front.Sector, vs);
- foreach(VisualGeometry g in vs.FixedGeometry)
+ foreach (VisualGeometry g in vs.FixedGeometry)
{
// Must have content
- if(g.Triangles > 0)
+ if (g.Triangles > 0)
pickables.Add(g);
}
+
+ // Add slope handles
+ if (General.Map.UDMF && pickingmode == PickingMode.SlopeHandles && allslopehandles.ContainsKey(ld.Front.Sector))
+ pickables.AddRange(allslopehandles[ld.Front.Sector]);
}
// Add sidedef if on the front side
@@ -795,12 +816,16 @@ namespace CodeImp.DoomBuilder.VisualModes
if(!sectors.ContainsKey(ld.Back.Sector))
{
sectors.Add(ld.Back.Sector, vs);
- foreach(VisualGeometry g in vs.FixedGeometry)
+ foreach (VisualGeometry g in vs.FixedGeometry)
{
// Must have content
- if(g.Triangles > 0)
+ if (g.Triangles > 0)
pickables.Add(g);
}
+
+ // Add slope handles
+ if (General.Map.UDMF && pickingmode == PickingMode.SlopeHandles && allslopehandles.ContainsKey(ld.Back.Sector))
+ pickables.AddRange(allslopehandles[ld.Back.Sector]);
}
// Add sidedef if on the front side
@@ -828,9 +853,9 @@ namespace CodeImp.DoomBuilder.VisualModes
foreach(VisualThing vt in visiblethings) pickables.Add(vt);
//mxd. And all visual vertices
- if(General.Map.UDMF && General.Settings.GZShowVisualVertices)
+ if (General.Map.UDMF && General.Settings.GZShowVisualVertices)
{
- foreach(KeyValuePair pair in vertices)
+ foreach (KeyValuePair pair in vertices)
pickables.AddRange(pair.Value.Vertices);
}
@@ -864,6 +889,11 @@ namespace CodeImp.DoomBuilder.VisualModes
// Setup final result
result.hitpos = from + to * result.u_ray;
+ // If picking mode is for slope handles only return slope handles. We have to do it this
+ // way because otherwise it's possible to pick slope handles through other geometry
+ if (pickingmode == PickingMode.SlopeHandles && !(result.picked is VisualSlope))
+ result.picked = null;
+
// Done
return result;
}
diff --git a/Source/Core/VisualModes/VisualSlope.cs b/Source/Core/VisualModes/VisualSlope.cs
new file mode 100644
index 00000000..dceebdae
--- /dev/null
+++ b/Source/Core/VisualModes/VisualSlope.cs
@@ -0,0 +1,138 @@
+using System;
+using CodeImp.DoomBuilder.Geometry;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Rendering;
+
+namespace CodeImp.DoomBuilder.VisualModes
+{
+ public abstract class VisualSlope : IVisualPickable
+ {
+ #region ================== Variables
+
+ // Disposing
+ private bool isdisposed;
+
+ // Selected?
+ protected bool selected;
+
+ // Pivot?
+ protected bool pivot;
+
+ // Smart Pivot?
+ protected bool smartpivot;
+
+ // Was changed?
+ private bool changed;
+
+ protected float length;
+
+ private Matrix position;
+
+ #endregion
+
+ #region ================== Properties
+
+ ///
+ /// Selected or not? This is only used by the core to determine what color to draw it with.
+ ///
+ public bool Selected { get { return selected; } set { selected = value; } }
+
+ ///
+ /// Pivot or not? This is only used by the core to determine what color to draw it with.
+ ///
+ public bool Pivot { get { return pivot; } set { pivot = value; } }
+
+ ///
+ /// Disposed or not?
+ ///
+ public bool IsDisposed { get { return isdisposed; } }
+
+ public bool SmartPivot { get { return smartpivot; } set { smartpivot = value; } }
+
+ public bool Changed { get { return changed; } set { changed = value; } }
+
+ public float Length { get { return length; } }
+
+ public Matrix Position { get { return position; } }
+
+ #endregion
+
+ #region ================== Constructor / Destructor
+
+ public VisualSlope()
+ {
+ pivot = false;
+ smartpivot = false;
+ }
+
+ #endregion
+
+ #region ================== Methods
+
+ // This is called before a device is reset (when resized or display adapter was changed)
+ public void UnloadResource()
+ {
+ }
+
+ // This is called resets when the device is reset
+ // (when resized or display adapter was changed)
+ public void ReloadResource()
+ {
+ }
+
+ ///
+ /// This is called when the thing must be tested for line intersection. This should reject
+ /// as fast as possible to rule out all geometry that certainly does not touch the line.
+ ///
+ public virtual bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir)
+ {
+ return true;
+ }
+
+ ///
+ /// This is called when the thing must be tested for line intersection. This should perform
+ /// accurate hit detection and set u_ray to the position on the ray where this hits the geometry.
+ ///
+ public virtual bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray)
+ {
+ return true;
+ }
+
+ public virtual void Update() {}
+
+ public void SetPosition(Line2D line, Plane plane)
+ {
+ Line3D line3d = new Line3D(new Vector3D(line.v1, plane.GetZ(line.v1)), new Vector3D(line.v2, plane.GetZ(line.v2)));
+
+ // This vector is perpendicular to the line, with a 90° angle between it and the plane normal
+ Vector3D perpendicularvector = Vector3D.CrossProduct(line3d.GetDelta().GetNormal(), plane.Normal) * (-1);
+
+ // This vector is on the plane, with a 90° angle to the perpendicular vector (so effectively
+ // it's on the line, but in 3D
+ Vector3D linevector = Vector3D.CrossProduct(plane.Normal, perpendicularvector) * (-1);
+
+ Matrix m = Matrix.Null;
+
+ m.M11 = linevector.x;
+ m.M12 = linevector.y;
+ m.M13 = linevector.z;
+
+ m.M21 = perpendicularvector.x;
+ m.M22 = perpendicularvector.y;
+ m.M23 = perpendicularvector.z;
+
+ m.M31 = plane.Normal.x;
+ m.M32 = plane.Normal.y;
+ m.M33 = plane.Normal.z;
+
+ m.M44 = 1.0f;
+
+ // The matrix is at the 0,0 origin, so move it to the start vertex of the line
+ Vector3D tp = new Vector3D(line.v1, plane.GetZ(line.v1));
+
+ position = Matrix.Multiply(m, Matrix.Translation(RenderDevice.V3(tp)));
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Source/Plugins/BuilderModes/BuilderModes.csproj b/Source/Plugins/BuilderModes/BuilderModes.csproj
index 5c02495d..06813607 100755
--- a/Source/Plugins/BuilderModes/BuilderModes.csproj
+++ b/Source/Plugins/BuilderModes/BuilderModes.csproj
@@ -495,6 +495,7 @@
+
diff --git a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
index c1e10e87..2a143346 100755
--- a/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
+++ b/Source/Plugins/BuilderModes/ClassicModes/EditSelectionMode.cs
@@ -170,6 +170,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
private ICollection unselectedvertices;
private ICollection unselectedlines;
private ICollection unstablelines; //mxd
+ private Dictionary slopeheights;
// Modification
private float rotation;
@@ -1325,6 +1326,32 @@ namespace CodeImp.DoomBuilder.BuilderModes
thingpos.Add(t.Position);
thingangle.Add(t.Angle);
}
+
+ // Get z heights of floor and ceiling slopes (from the center of the sector). This is used
+ // to easily compute the new slope after moving and rotating. We can't simply use the sector
+ // heights, since they might be wrong (as sector heights are technically irrelevant for slopes)
+ // Floor/ceiling heights are stored if there is no slope, but they won't get used anyway
+ // Important: this has to be done before the first call to UpdateGeometry, since that will change
+ // the sector and subsequently the bounding box, but not the slope
+ slopeheights = new Dictionary();
+
+ foreach(Sector s in sectors)
+ {
+ // Make sure the sector has a valid bounding box
+ s.UpdateCache();
+
+ Vector2D center = new Vector2D(s.BBox.X + s.BBox.Width / 2, s.BBox.Y + s.BBox.Height / 2);
+ float floorz = s.FloorHeight;
+ float ceilingz = s.CeilHeight;
+
+ if (!float.IsNaN(s.FloorSlopeOffset))
+ floorz = new Plane(s.FloorSlope, s.FloorSlopeOffset).GetZ(center);
+
+ if (!float.IsNaN(s.CeilSlopeOffset))
+ ceilingz = new Plane(s.CeilSlope, s.CeilSlopeOffset).GetZ(center);
+
+ slopeheights.Add(s, new float[] { floorz, ceilingz });
+ }
// Calculate size
size = right - offset;
@@ -1550,7 +1577,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Update floor slope?
if(s.FloorSlope.GetLengthSq() > 0 && !float.IsNaN(s.FloorSlopeOffset / s.FloorSlope.z))
{
- Vector3D center = new Vector3D(s.BBox.X + s.BBox.Width / 2, s.BBox.Y + s.BBox.Height / 2, s.FloorHeight);
+ Vector3D center = new Vector3D(s.BBox.X + s.BBox.Width / 2, s.BBox.Y + s.BBox.Height / 2, slopeheights[s][0]);
Plane p = new Plane(center, s.FloorSlope.GetAngleXY() + rotation + Angle2D.PIHALF, -s.FloorSlope.GetAngleZ(), true);
s.FloorSlope = p.Normal;
s.FloorSlopeOffset = p.Offset;
@@ -1559,7 +1586,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Update ceiling slope?
if(s.CeilSlope.GetLengthSq() > 0 && !float.IsNaN(s.CeilSlopeOffset / s.CeilSlope.z))
{
- Vector3D center = new Vector3D(s.BBox.X + s.BBox.Width / 2, s.BBox.Y + s.BBox.Height / 2, s.CeilHeight);
+ Vector3D center = new Vector3D(s.BBox.X + s.BBox.Width / 2, s.BBox.Y + s.BBox.Height / 2, slopeheights[s][1]);
Plane p = new Plane(center, s.CeilSlope.GetAngleXY() + rotation + Angle2D.PIHALF, -s.CeilSlope.GetAngleZ(), false);
s.CeilSlope = p.Normal;
s.CeilSlopeOffset = p.Offset;
diff --git a/Source/Plugins/BuilderModes/General/BuilderPlug.cs b/Source/Plugins/BuilderModes/General/BuilderPlug.cs
index 75d8f0b7..2b3c5df2 100755
--- a/Source/Plugins/BuilderModes/General/BuilderPlug.cs
+++ b/Source/Plugins/BuilderModes/General/BuilderPlug.cs
@@ -136,6 +136,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
private bool alphabasedtexturehighlighting; //mxd
private bool showlightradii; //mxd
private bool showsoundradii; //mxd
+ private int scaletexturesonslopes; // 0 = base scale of 1, 1 = use current scale as base, 2 = don't scale
#endregion
@@ -189,6 +190,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public bool AlphaBasedTextureHighlighting { get { return alphabasedtexturehighlighting; } internal set { alphabasedtexturehighlighting = value; } } //mxd
public bool ShowLightRadii { get { return showlightradii; } internal set { showlightradii = value; } } //mxd
public bool ShowSoundRadii { get { return showsoundradii; } internal set { showsoundradii = value; } } //mxd
+ public int ScaleTexturesOnSlopes { get { return scaletexturesonslopes; } internal set { scaletexturesonslopes = value; } }
//mxd. "Make Door" action persistent settings
internal MakeDoorSettings MakeDoor;
@@ -291,6 +293,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
autoAlignTextureOffsetsOnCreate = General.Settings.ReadPluginSetting("autoaligntextureoffsetsoncreate", false); //mxd
dontMoveGeometryOutsideMapBoundary = General.Settings.ReadPluginSetting("dontmovegeometryoutsidemapboundary", false); //mxd
syncSelection = General.Settings.ReadPluginSetting("syncselection", false); //mxd
+ scaletexturesonslopes = General.Settings.ReadPluginSetting("scaletexturesonslopes", 0);
}
//mxd. Load settings, which can be changed via UI
diff --git a/Source/Plugins/BuilderModes/Interface/PreferencesForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/PreferencesForm.Designer.cs
index d284d309..1db5dac6 100755
--- a/Source/Plugins/BuilderModes/Interface/PreferencesForm.Designer.cs
+++ b/Source/Plugins/BuilderModes/Interface/PreferencesForm.Designer.cs
@@ -40,6 +40,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
this.defaultbrightness = new CodeImp.DoomBuilder.Controls.ButtonsNumericTextbox();
this.label11 = new System.Windows.Forms.Label();
this.groupBox3 = new System.Windows.Forms.GroupBox();
+ this.additivepaintselect = new System.Windows.Forms.CheckBox();
this.switchviewmodes = new System.Windows.Forms.CheckBox();
this.autodrawonedit = new System.Windows.Forms.CheckBox();
this.syncSelection = new System.Windows.Forms.CheckBox();
@@ -72,7 +73,8 @@ namespace CodeImp.DoomBuilder.BuilderModes
this.label10 = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label();
this.heightbysidedef = new System.Windows.Forms.ComboBox();
- this.additivepaintselect = new System.Windows.Forms.CheckBox();
+ this.label18 = new System.Windows.Forms.Label();
+ this.scaletexturesonslopes = new System.Windows.Forms.ComboBox();
this.tabs.SuspendLayout();
this.taboptions.SuspendLayout();
this.groupBox4.SuspendLayout();
@@ -120,7 +122,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
this.groupBox4.Controls.Add(this.label12);
this.groupBox4.Controls.Add(this.defaultbrightness);
this.groupBox4.Controls.Add(this.label11);
- this.groupBox4.Location = new System.Drawing.Point(6, 300);
+ this.groupBox4.Location = new System.Drawing.Point(6, 335);
this.groupBox4.Name = "groupBox4";
this.groupBox4.Size = new System.Drawing.Size(272, 136);
this.groupBox4.TabIndex = 2;
@@ -240,13 +242,23 @@ namespace CodeImp.DoomBuilder.BuilderModes
this.groupBox3.Controls.Add(this.editnewthing);
this.groupBox3.Controls.Add(this.editnewsector);
this.groupBox3.Controls.Add(this.additiveselect);
- this.groupBox3.Location = new System.Drawing.Point(284, 104);
+ this.groupBox3.Location = new System.Drawing.Point(284, 139);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(379, 332);
this.groupBox3.TabIndex = 3;
this.groupBox3.TabStop = false;
this.groupBox3.Text = " Options ";
//
+ // additivepaintselect
+ //
+ this.additivepaintselect.AutoSize = true;
+ this.additivepaintselect.Location = new System.Drawing.Point(13, 135);
+ this.additivepaintselect.Name = "additivepaintselect";
+ this.additivepaintselect.Size = new System.Drawing.Size(233, 17);
+ this.additivepaintselect.TabIndex = 11;
+ this.additivepaintselect.Text = "Additive paint selecting without holding Shift";
+ this.additivepaintselect.UseVisualStyleBackColor = true;
+ //
// switchviewmodes
//
this.switchviewmodes.AutoSize = true;
@@ -375,7 +387,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
this.groupBox2.Controls.Add(this.label6);
this.groupBox2.Controls.Add(this.label4);
this.groupBox2.Controls.Add(this.label7);
- this.groupBox2.Location = new System.Drawing.Point(6, 104);
+ this.groupBox2.Location = new System.Drawing.Point(6, 139);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(272, 190);
this.groupBox2.TabIndex = 1;
@@ -569,13 +581,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
//
// groupBox1
//
+ this.groupBox1.Controls.Add(this.scaletexturesonslopes);
+ this.groupBox1.Controls.Add(this.label18);
this.groupBox1.Controls.Add(this.splitbehavior);
this.groupBox1.Controls.Add(this.label10);
this.groupBox1.Controls.Add(this.label1);
this.groupBox1.Controls.Add(this.heightbysidedef);
this.groupBox1.Location = new System.Drawing.Point(6, 6);
this.groupBox1.Name = "groupBox1";
- this.groupBox1.Size = new System.Drawing.Size(657, 92);
+ this.groupBox1.Size = new System.Drawing.Size(657, 127);
this.groupBox1.TabIndex = 0;
this.groupBox1.TabStop = false;
this.groupBox1.Text = " Behavior ";
@@ -607,7 +621,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// label1
//
this.label1.AutoSize = true;
- this.label1.Location = new System.Drawing.Point(9, 22);
+ this.label1.Location = new System.Drawing.Point(15, 22);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(308, 13);
this.label1.TabIndex = 0;
@@ -628,15 +642,28 @@ namespace CodeImp.DoomBuilder.BuilderModes
this.heightbysidedef.Size = new System.Drawing.Size(309, 21);
this.heightbysidedef.TabIndex = 0;
//
- // additivepaintselect
+ // label18
//
- this.additivepaintselect.AutoSize = true;
- this.additivepaintselect.Location = new System.Drawing.Point(13, 135);
- this.additivepaintselect.Name = "additivepaintselect";
- this.additivepaintselect.Size = new System.Drawing.Size(233, 17);
- this.additivepaintselect.TabIndex = 11;
- this.additivepaintselect.Text = "Additive paint selecting without holding Shift";
- this.additivepaintselect.UseVisualStyleBackColor = true;
+ this.label18.AutoSize = true;
+ this.label18.Location = new System.Drawing.Point(133, 94);
+ this.label18.Name = "label18";
+ this.label18.Size = new System.Drawing.Size(190, 13);
+ this.label18.TabIndex = 2;
+ this.label18.Text = "When auto-aligning textures on slopes:";
+ this.label18.TextAlign = System.Drawing.ContentAlignment.TopRight;
+ //
+ // scaletexturesonslopes
+ //
+ this.scaletexturesonslopes.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.scaletexturesonslopes.FormattingEnabled = true;
+ this.scaletexturesonslopes.Items.AddRange(new object[] {
+ "Use a scale of 1 as base",
+ "Use current scale as base",
+ "Don\'t scale"});
+ this.scaletexturesonslopes.Location = new System.Drawing.Point(342, 91);
+ this.scaletexturesonslopes.Name = "scaletexturesonslopes";
+ this.scaletexturesonslopes.Size = new System.Drawing.Size(309, 21);
+ this.scaletexturesonslopes.TabIndex = 3;
//
// PreferencesForm
//
@@ -710,5 +737,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
private System.Windows.Forms.Label label16;
private System.Windows.Forms.Label label17;
private System.Windows.Forms.CheckBox additivepaintselect;
+ private System.Windows.Forms.ComboBox scaletexturesonslopes;
+ private System.Windows.Forms.Label label18;
}
}
\ No newline at end of file
diff --git a/Source/Plugins/BuilderModes/Interface/PreferencesForm.cs b/Source/Plugins/BuilderModes/Interface/PreferencesForm.cs
index 762a6a76..38ff83de 100755
--- a/Source/Plugins/BuilderModes/Interface/PreferencesForm.cs
+++ b/Source/Plugins/BuilderModes/Interface/PreferencesForm.cs
@@ -64,6 +64,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
defaultbrightness.Text = General.Settings.DefaultBrightness.ToString(); //mxd
defaultceilheight.Text = General.Settings.DefaultCeilingHeight.ToString();//mxd
defaultfloorheight.Text = General.Settings.DefaultFloorHeight.ToString(); //mxd
+ scaletexturesonslopes.SelectedIndex = General.Settings.ReadPluginSetting("scaletexturesonslopes", 0);
}
#endregion
@@ -91,8 +92,10 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Settings.WritePluginSetting("autoaligntextureoffsetsoncreate", autoaligntexturesoncreate.Checked);//mxd
General.Settings.WritePluginSetting("dontmovegeometryoutsidemapboundary", dontMoveGeometryOutsideBounds.Checked);//mxd
General.Settings.WritePluginSetting("syncselection", syncSelection.Checked);//mxd
+ General.Settings.WritePluginSetting("scaletexturesonslopes", scaletexturesonslopes.SelectedIndex);
General.Settings.SwitchViewModes = switchviewmodes.Checked; //mxd
General.Settings.SplitLineBehavior = (SplitLineBehavior)splitbehavior.SelectedIndex;//mxd
+
//default sector values
General.Settings.DefaultBrightness = General.Clamp(defaultbrightness.GetResult(192), 0, 255);
diff --git a/Source/Plugins/BuilderModes/Resources/Actions.cfg b/Source/Plugins/BuilderModes/Resources/Actions.cfg
index 1b2eaffb..f5328e9f 100755
--- a/Source/Plugins/BuilderModes/Resources/Actions.cfg
+++ b/Source/Plugins/BuilderModes/Resources/Actions.cfg
@@ -1405,3 +1405,25 @@ visualpaintselect
disregardcontrol = true;
disregardalt = true;
}
+
+togglevisualslopepicking
+{
+ title = "Toggle Visual Slope Picking";
+ category = "visual";
+ description = "Toggles picking visual slope handles.";
+ allowkeys = true;
+ allowmouse = true;
+ allowscroll = false;
+ default = 65623; // Shift-W
+}
+
+slopebetweenhandles
+{
+ title = "Slope Between Handles";
+ category = "visual";
+ description = "Slopes the selected floors and ceilings between the selected slope handles.";
+ allowkeys = true;
+ allowmouse = true;
+ allowscroll = false;
+ default = 131142; // Ctrl-F
+}
\ No newline at end of file
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
index 0de67de4..c4647c12 100755
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualGeometrySector.cs
@@ -298,6 +298,32 @@ namespace CodeImp.DoomBuilder.BuilderModes
//update angle
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "rotationfloor" : "rotationceiling"), sourceAngle, 0f);
+ // Scale texture if it's a slope and the appropriate option is set
+ if (level.plane.Normal.z != 1.0f && BuilderPlug.Me.ScaleTexturesOnSlopes != 2)
+ {
+ Vector2D basescale = new Vector2D(1.0f, 1.0f);
+
+ // User wants to use the current scale as a base?
+ if(BuilderPlug.Me.ScaleTexturesOnSlopes == 1)
+ {
+ basescale.x = scaleX;
+ basescale.y = scaleY;
+ }
+
+ // Create a unit vector of the direction of the target line in 3D space
+ Vector3D targetlinevector = new Line3D(new Vector3D(targetLine.Start.Position, level.plane.GetZ(targetLine.Start.Position)), new Vector3D(targetLine.End.Position, level.plane.GetZ(targetLine.End.Position))).GetDelta().GetNormal();
+
+ // Get a perpendicular vector of the target line in 3D space. This is used to get the slope angle relative to the target line
+ Vector3D targetlineperpendicular = Vector3D.CrossProduct(targetlinevector, level.plane.Normal);
+
+ if (alignx)
+ scaleX = Math.Abs(basescale.x * (1.0f / (float)Math.Cos(targetlinevector.GetAngleZ())));
+
+ if (aligny)
+ scaleY = Math.Abs(basescale.y * (1.0f / (float)Math.Cos(targetlineperpendicular.GetAngleZ())));
+
+ }
+
//set scale
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xscalefloor" : "xscaleceiling"), scaleX, 1.0f);
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "yscalefloor" : "yscaleceiling"), scaleY, 1.0f);
@@ -323,96 +349,6 @@ namespace CodeImp.DoomBuilder.BuilderModes
Sector.UpdateSectorGeometry(false);
}
- //mxd
- protected void AlignTextureToSlopeLine(Linedef slopeSource, float slopeAngle, bool isFront, bool alignx, bool aligny)
- {
- bool isFloor = (geometrytype == VisualGeometryType.FLOOR);
- Sector.Sector.Fields.BeforeFieldsChange();
- float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(slopeSource.Angle) + 90 : -Angle2D.RadToDeg(slopeSource.Angle) - 90), 1);
-
- if(isFloor)
- {
- if((isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight) ||
- (!isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight))
- {
- sourceAngle = General.ClampAngle(sourceAngle + 180);
- }
- }
- else
- {
- if((isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight) ||
- (!isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight))
- {
- sourceAngle = General.ClampAngle(sourceAngle + 180);
- }
- }
-
- //update angle
- UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "rotationfloor" : "rotationceiling"), sourceAngle, 0f);
-
- //update scaleY
- string xScaleKey = (isFloor ? "xscalefloor" : "xscaleceiling");
- string yScaleKey = (isFloor ? "yscalefloor" : "yscaleceiling");
-
- float scaleX = Sector.Sector.Fields.GetValue(xScaleKey, 1.0f);
- float scaleY;
-
- //set scale
- if(aligny)
- {
- scaleY = (float)Math.Round(scaleX * (1 / (float)Math.Cos(slopeAngle)), 2);
- UniFields.SetFloat(Sector.Sector.Fields, yScaleKey, scaleY, 1.0f);
- }
- else
- {
- scaleY = Sector.Sector.Fields.GetValue(yScaleKey, 1.0f);
- }
-
- //update texture offsets
- Vector2D offset;
- if(isFloor)
- {
- if((isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight) ||
- (!isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight))
- {
- offset = slopeSource.End.Position;
- }
- else
- {
- offset = slopeSource.Start.Position;
- }
- }
- else
- {
- if((isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight) ||
- (!isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight))
- {
- offset = slopeSource.End.Position;
- }
- else
- {
- offset = slopeSource.Start.Position;
- }
- }
-
- offset = offset.GetRotated(Angle2D.DegToRad(sourceAngle));
-
- if(alignx)
- {
- if(Texture != null && Texture.IsImageLoaded) offset.x %= Texture.Width / scaleX;
- UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xpanningfloor" : "xpanningceiling"), (float)Math.Round(-offset.x), 0f);
- }
-
- if(aligny)
- {
- if(Texture != null && Texture.IsImageLoaded) offset.y %= Texture.Height / scaleY;
- UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "ypanningfloor" : "ypanningceiling"), (float)Math.Round(offset.y), 0f);
- }
-
- //update geometry
- Sector.UpdateSectorGeometry(false);
- }
-
//mxd
protected void ClearFields(IEnumerable keys, string undodescription, string resultdescription)
{
@@ -878,8 +814,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
if(vs != null) vs.UpdateSectorGeometry(true);
+
+ // Visual slope handles need to be updated, too
+ if (General.Map.UDMF)
+ {
+ if (mode.AllSlopeHandles.ContainsKey(level.sector))
+ foreach (VisualSidedefSlope handle in mode.AllSlopeHandles[level.sector])
+ handle.Changed = true;
+ }
}
-
+
// Sector brightness change
public virtual void OnChangeTargetBrightness(bool up)
{
diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
index 612d3303..52e2cf61 100755
--- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs
@@ -18,6 +18,8 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
using System.Windows.Forms;
using CodeImp.DoomBuilder.BuilderModes.Interface;
using CodeImp.DoomBuilder.Windows;
@@ -392,6 +394,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
}
+ if (General.Map.UDMF)
+ {
+ foreach (KeyValuePair> kvp in allslopehandles)
+ {
+ foreach (VisualSlope handle in kvp.Value)
+ if (handle.Selected) selectedobjects.Add((VisualSidedefSlope)handle);
+ }
+ }
+
//mxd
UpdateSelectionInfo();
}
@@ -411,7 +422,19 @@ namespace CodeImp.DoomBuilder.BuilderModes
allsectors.Add(s, vs); //mxd
return vs;
}
-
+
+ internal VisualSlope CreateVisualSlopeHandle(SectorLevel level, Sidedef sd, bool up)
+ {
+ VisualSidedefSlope handle = new VisualSidedefSlope(this, level, sd, up);
+
+ if (!allslopehandles.ContainsKey(sd.Sector))
+ allslopehandles.Add(sd.Sector, new List());
+
+ allslopehandles[sd.Sector].Add(handle);
+
+ return handle;
+ }
+
// This creates a visual thing
protected override VisualThing CreateVisualThing(Thing t)
{
@@ -448,12 +471,32 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Should we update the info on panels?
bool updateinfo = (newtarget.picked != target.picked);
-
+
+ if (updateinfo)
+ {
+ if (newtarget.picked is VisualSidedefSlope)
+ {
+ // Get the smart pivot handle for the targeted slope handle, so that it can be drawn
+ VisualSidedefSlope handle = VisualSidedefSlope.GetSmartPivotHandle((VisualSidedefSlope)newtarget.picked, this);
+ if (handle != null)
+ handle.SmartPivot = true;
+ }
+ else if(target.picked is VisualSidedefSlope)
+ {
+
+ // Clear smart pivot handles, otherwise it will keep being displayed
+ foreach (KeyValuePair> kvp in allslopehandles)
+ foreach (VisualSidedefSlope checkhandle in kvp.Value)
+ checkhandle.SmartPivot = false;
+ }
+ }
+
// Apply new target
target = newtarget;
// Show target info
- if(updateinfo) ShowTargetInfo();
+ if (updateinfo)
+ ShowTargetInfo();
}
// This shows the picked target information
@@ -512,7 +555,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(vs.Value != null)
{
BaseVisualSector bvs = (BaseVisualSector)vs.Value;
- if(bvs.Changed) bvs.Rebuild();
+ if(bvs.Changed)
+ {
+ bvs.Rebuild();
+
+ // Also update slope handles
+ if (allslopehandles.ContainsKey(vs.Key))
+ foreach (VisualSidedefSlope handle in allslopehandles[vs.Key])
+ handle.Update();
+ }
}
}
@@ -1121,6 +1172,42 @@ namespace CodeImp.DoomBuilder.BuilderModes
break;
}
}
+
+ // Visual slope handles
+ foreach (KeyValuePair> kvp in allslopehandles)
+ {
+ foreach (VisualSlope handle in kvp.Value)
+ if (handle != null)
+ if (handle.Selected) RemoveSelectedObject((VisualSidedefSlope)handle);
+
+ kvp.Value.Clear();
+ }
+ allslopehandles.Clear();
+
+ if (General.Map.UDMF /* && General.Settings.ShowVisualSlopeHandles */)
+ {
+ foreach (Sector s in General.Map.Map.Sectors)
+ {
+ SectorData sectordata = GetSectorData(s);
+
+ sectordata.Update();
+
+ foreach (Sidedef sidedef in s.Sidedefs)
+ {
+ VisualSlope handle = CreateVisualSlopeHandle(sectordata.Floor, sidedef, true);
+ handle = CreateVisualSlopeHandle(sectordata.Ceiling, sidedef, false);
+
+ if (sectordata.ExtraFloors.Count > 0)
+ {
+ foreach (Effect3DFloor floor in sectordata.ExtraFloors)
+ {
+ handle = CreateVisualSlopeHandle(floor.Floor, sidedef, false);
+ handle = CreateVisualSlopeHandle(floor.Ceiling, sidedef, true);
+ }
+ }
+ }
+ }
+ }
}
#endregion
@@ -1361,7 +1448,16 @@ namespace CodeImp.DoomBuilder.BuilderModes
renderer.SetVisualVertices(verts);
}
-
+
+ // Visual slope handles
+ List handles = new List();
+ foreach (KeyValuePair> kvp in allslopehandles)
+ foreach (VisualSlope handle in kvp.Value)
+ if (handle.Selected || handle.Pivot || handle.SmartPivot || target.picked == handle)
+ handles.Add(handle);
+
+ renderer.SetVisualSlopeHandles(handles);
+
// Done rendering geometry
renderer.FinishGeometry();
@@ -1660,7 +1756,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Apply texture offsets
public void ApplyTextureOffsetChange(int dx, int dy)
{
- List objs = GetSelectedObjects(false, true, false, false);
+ List objs = GetSelectedObjects(false, true, false, false, false);
//mxd. Because Upper/Middle/Lower textures offsets should be threated separately in UDMF
//MaxW. But they're not for Eternity, so this needs its own config setting
@@ -1702,7 +1798,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void ApplyFlatOffsetChange(int dx, int dy)
{
HashSet donesectors = new HashSet();
- List objs = GetSelectedObjects(true, false, false, false);
+ List objs = GetSelectedObjects(true, false, false, false, false);
foreach(IVisualEventReceiver i in objs)
{
BaseVisualGeometrySector bvs = (BaseVisualGeometrySector)i;
@@ -1759,7 +1855,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Apply upper unpegged flag
public void ApplyUpperUnpegged(bool set)
{
- List objs = GetSelectedObjects(false, true, false, false);
+ List objs = GetSelectedObjects(false, true, false, false, false);
foreach(IVisualEventReceiver i in objs)
{
i.ApplyUpperUnpegged(set);
@@ -1769,7 +1865,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
// Apply lower unpegged flag
public void ApplyLowerUnpegged(bool set)
{
- List objs = GetSelectedObjects(false, true, false, false);
+ List objs = GetSelectedObjects(false, true, false, false, false);
foreach(IVisualEventReceiver i in objs)
{
i.ApplyLowerUnpegged(set);
@@ -1784,12 +1880,12 @@ namespace CodeImp.DoomBuilder.BuilderModes
if(General.Map.Config.MixTexturesFlats)
{
// Apply on all compatible types
- objs = GetSelectedObjects(true, true, false, false);
+ objs = GetSelectedObjects(true, true, false, false, false);
}
else
{
// We don't want to mix textures and flats, so apply only on the appropriate type
- objs = GetSelectedObjects(flat, !flat, false, false);
+ objs = GetSelectedObjects(flat, !flat, false, false, false);
}
foreach(IVisualEventReceiver i in objs)
@@ -1799,7 +1895,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
// This returns all selected objects
- internal List GetSelectedObjects(bool includesectors, bool includesidedefs, bool includethings, bool includevertices)
+ internal List GetSelectedObjects(bool includesectors, bool includesidedefs, bool includethings, bool includevertices, bool includeslopehandles)
{
List objs = new List();
foreach(IVisualEventReceiver i in selectedobjects)
@@ -1808,6 +1904,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
else if(includesidedefs && (i is BaseVisualGeometrySidedef)) objs.Add(i);
else if(includethings && (i is BaseVisualThing)) objs.Add(i);
else if(includevertices && (i is BaseVisualVertex)) objs.Add(i); //mxd
+ else if (includeslopehandles && (i is VisualSlope)) objs.Add(i); // biwa
}
// Add highlight?
@@ -1818,6 +1915,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
else if(includesidedefs && (i is BaseVisualGeometrySidedef)) objs.Add(i);
else if(includethings && (i is BaseVisualThing)) objs.Add(i);
else if(includevertices && (i is BaseVisualVertex)) objs.Add(i); //mxd
+ else if (includeslopehandles && (i is VisualSlope)) objs.Add(i); // biwa
}
return objs;
@@ -2002,13 +2100,39 @@ namespace CodeImp.DoomBuilder.BuilderModes
return verts;
}
+
+ // This returns all selected slope handles, no doubles
+ private List GetSelectedSlopeHandles()
+ {
+ HashSet added = new HashSet();
+ List handles = new List();
+
+ foreach(IVisualEventReceiver i in selectedobjects)
+ {
+ VisualSidedefSlope handle = i as VisualSidedefSlope;
+ if(handle != null && !added.Contains(handle))
+ {
+ handles.Add(handle);
+ added.Add(handle);
+ }
+ }
+
+ // Add highlight?
+ if((selectedobjects.Count == 0) && (target.picked is VisualSidedefSlope))
+ {
+ VisualSidedefSlope handle = (VisualSidedefSlope)target.picked;
+ if (!added.Contains(handle)) handles.Add(handle);
+ }
+
+ return handles;
+ }
// This returns the IVisualEventReceiver on which the action must be performed
private IVisualEventReceiver GetTargetEventReceiver(bool targetonly)
{
if(target.picked != null)
{
- if(singleselection || target.picked.Selected || targetonly)
+ if(singleselection || target.picked.Selected || targetonly || target.picked is VisualSlope)
{
return (IVisualEventReceiver)target.picked;
}
@@ -2068,14 +2192,15 @@ namespace CodeImp.DoomBuilder.BuilderModes
#region ================== Actions
// [ZZ] I moved this out of ClearSelection because "cut selection" action needs this to only affect things.
- private void ClearSelection(bool clearsectors, bool clearsidedefs, bool clearthings, bool clearvertices, bool displaystatus)
+ private void ClearSelection(bool clearsectors, bool clearsidedefs, bool clearthings, bool clearvertices, bool clearslopehandles, bool displaystatus)
{
selectedobjects.RemoveAll(obj =>
{
return ((obj is BaseVisualGeometrySector && clearsectors) ||
(obj is BaseVisualGeometrySidedef && clearsidedefs) ||
(obj is BaseVisualThing && clearthings) ||
- (obj is BaseVisualVertex && clearvertices));
+ (obj is BaseVisualVertex && clearvertices) ||
+ (obj is VisualSlope && clearslopehandles));
});
//
@@ -2126,8 +2251,24 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
}
- //mxd
- if (displaystatus)
+ // biwa
+ if (clearslopehandles)
+ {
+ if (General.Map.UDMF)
+ {
+ foreach (KeyValuePair> kvp in allslopehandles)
+ {
+ foreach (VisualSidedefSlope handle in kvp.Value)
+ {
+ handle.Selected = false;
+ handle.Pivot = false;
+ }
+ }
+ }
+ }
+
+ //mxd
+ if (displaystatus)
{
General.Interface.DisplayStatus(StatusType.Selection, string.Empty);
}
@@ -2136,7 +2277,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
[BeginAction("clearselection", BaseAction = true)]
public void ClearSelection()
{
- ClearSelection(true, true, true, true, true);
+ ClearSelection(true, true, true, true, true, true);
}
[BeginAction("visualselect", BaseAction = true)]
@@ -2196,8 +2337,11 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void RaiseSector8()
{
PreAction(UndoGroup.SectorHeightChange);
- List objs = GetSelectedObjects(true, true, true, true);
- foreach(IVisualEventReceiver i in objs) i.OnChangeTargetHeight(8);
+ List objs = GetSelectedObjects(true, true, true, true, true);
+ bool hasvisualslopehandles = objs.Any(o => o is VisualSlope);
+ foreach (IVisualEventReceiver i in objs) // If slope handles are selected only apply the action to them
+ if (!hasvisualslopehandles || (hasvisualslopehandles && i is VisualSlope))
+ i.OnChangeTargetHeight(8);
PostAction();
}
@@ -2205,45 +2349,56 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void LowerSector8()
{
PreAction(UndoGroup.SectorHeightChange);
- List objs = GetSelectedObjects(true, true, true, true);
- foreach(IVisualEventReceiver i in objs) i.OnChangeTargetHeight(-8);
+ List objs = GetSelectedObjects(true, true, true, true, true);
+ bool hasvisualslopehandles = objs.Any(o => o is VisualSlope);
+ foreach (IVisualEventReceiver i in objs) // If slope handles are selected only apply the action to them
+ if (!hasvisualslopehandles || (hasvisualslopehandles && i is VisualSlope))
+ i.OnChangeTargetHeight(-8);
PostAction();
}
[BeginAction("raisesector1")]
public void RaiseSector1() {
PreAction(UndoGroup.SectorHeightChange);
- List objs = GetSelectedObjects(true, true, true, true);
- foreach (IVisualEventReceiver i in objs)
- i.OnChangeTargetHeight(1);
- PostAction();
+ List objs = GetSelectedObjects(true, true, true, true, true);
+ bool hasvisualslopehandles = objs.Any(o => o is VisualSlope);
+ foreach (IVisualEventReceiver i in objs) // If slope handles are selected only apply the action to them
+ if (!hasvisualslopehandles || (hasvisualslopehandles && i is VisualSlope))
+ i.OnChangeTargetHeight(1);
+ PostAction();
}
[BeginAction("lowersector1")]
public void LowerSector1() {
PreAction(UndoGroup.SectorHeightChange);
- List objs = GetSelectedObjects(true, true, true, true);
- foreach (IVisualEventReceiver i in objs)
- i.OnChangeTargetHeight(-1);
- PostAction();
+ List objs = GetSelectedObjects(true, true, true, true, true);
+ bool hasvisualslopehandles = objs.Any(o => o is VisualSlope);
+ foreach (IVisualEventReceiver i in objs) // If slope handles are selected only apply the action to them
+ if (!hasvisualslopehandles || (hasvisualslopehandles && i is VisualSlope))
+ i.OnChangeTargetHeight(-1);
+ PostAction();
}
[BeginAction("raisesector128")]
public void RaiseSector128() {
PreAction(UndoGroup.SectorHeightChange);
- List objs = GetSelectedObjects(true, true, true, true);
- foreach (IVisualEventReceiver i in objs)
- i.OnChangeTargetHeight(128);
- PostAction();
+ List objs = GetSelectedObjects(true, true, true, true, true);
+ bool hasvisualslopehandles = objs.Any(o => o is VisualSlope);
+ foreach (IVisualEventReceiver i in objs) // If slope handles are selected only apply the action to them
+ if (!hasvisualslopehandles || (hasvisualslopehandles && i is VisualSlope))
+ i.OnChangeTargetHeight(128);
+ PostAction();
}
[BeginAction("lowersector128")]
public void LowerSector128() {
PreAction(UndoGroup.SectorHeightChange);
- List objs = GetSelectedObjects(true, true, true, true);
- foreach (IVisualEventReceiver i in objs)
- i.OnChangeTargetHeight(-128);
- PostAction();
+ List objs = GetSelectedObjects(true, true, true, true, true);
+ bool hasvisualslopehandles = objs.Any(o => o is VisualSlope);
+ foreach (IVisualEventReceiver i in objs) // If slope handles are selected only apply the action to them
+ if (!hasvisualslopehandles || (hasvisualslopehandles && i is VisualSlope))
+ i.OnChangeTargetHeight(-128);
+ PostAction();
}
@@ -2251,397 +2406,479 @@ namespace CodeImp.DoomBuilder.BuilderModes
[BeginAction("raisesectortonearest")]
public void RaiseSectorToNearest()
{
- Dictionary floors = new Dictionary();
- Dictionary ceilings = new Dictionary();
- List things = new List();
- bool withinSelection = General.Interface.CtrlState;
+ List selectedhandles = GetSelectedSlopeHandles();
- // Get selection
- if(selectedobjects.Count == 0)
+ if (selectedhandles.Count > 0)
{
- if(target.picked is VisualFloor)
+ if (selectedhandles.Count > 1)
{
- VisualFloor vf = (VisualFloor)target.picked;
- floors.Add(vf.Level.sector, vf);
- }
- else if(target.picked is VisualCeiling)
- {
- VisualCeiling vc = (VisualCeiling)target.picked;
- ceilings.Add(vc.Level.sector, vc);
- }
- else if(target.picked is BaseVisualThing)
- {
- things.Add((BaseVisualThing)target.picked);
+ General.Interface.DisplayStatus(StatusType.Warning, "Can only raise to nearest when one visual slope handle is selected");
+ return;
}
- }
- else
- {
- foreach(IVisualEventReceiver i in selectedobjects)
+
+ int startheight = (int)Math.Round(selectedhandles[0].GetCenterPoint().z);
+ int targetheight = int.MaxValue;
+
+ foreach (KeyValuePair> kvp in allslopehandles)
{
- if(i is VisualFloor)
+ foreach (VisualSidedefSlope handle in kvp.Value)
{
- VisualFloor vf = (VisualFloor)i;
- floors.Add(vf.Level.sector, vf);
- }
- else if(i is VisualCeiling)
- {
- VisualCeiling vc = (VisualCeiling)i;
- ceilings.Add(vc.Level.sector, vc);
- }
- else if(i is BaseVisualThing)
- {
- things.Add((BaseVisualThing)i);
+ if (handle != selectedhandles[0] && handle.Sidedef.Line == selectedhandles[0].Sidedef.Line)
+ {
+ int z = (int)Math.Round(handle.GetCenterPoint().z);
+
+ if (z > startheight && z < targetheight)
+ targetheight = z;
+ }
}
}
- }
- // Check what we have
- if(floors.Count + ceilings.Count == 0 && (things.Count == 0 || !General.Map.FormatInterface.HasThingHeight))
- {
- General.Interface.DisplayStatus(StatusType.Warning, "No suitable objects found!");
- return;
- }
-
- if(withinSelection)
- {
- string s = string.Empty;
-
- if(floors.Count == 1) s = "floors";
-
- if(ceilings.Count == 1)
+ if (targetheight != int.MaxValue)
{
- if(!string.IsNullOrEmpty(s)) s += " and ";
- s += "ceilings";
+ PreAction(UndoGroup.SectorHeightChange);
+ selectedhandles[0].OnChangeTargetHeight(targetheight - startheight);
+ PostAction();
+ }
+ else
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Can't raise: already at the highest level");
+ }
+ }
+ else
+ {
+ Dictionary floors = new Dictionary();
+ Dictionary ceilings = new Dictionary();
+ List things = new List();
+ bool withinSelection = General.Interface.CtrlState;
+
+ // Get selection
+ if (selectedobjects.Count == 0)
+ {
+ if (target.picked is VisualFloor)
+ {
+ VisualFloor vf = (VisualFloor)target.picked;
+ floors[vf.Level.sector] = vf;
+ }
+ else if (target.picked is VisualCeiling)
+ {
+ VisualCeiling vc = (VisualCeiling)target.picked;
+ ceilings[vc.Level.sector] = vc;
+ }
+ else if (target.picked is BaseVisualThing)
+ {
+ things.Add((BaseVisualThing)target.picked);
+ }
+ }
+ else
+ {
+ foreach (IVisualEventReceiver i in selectedobjects)
+ {
+ if (i is VisualFloor)
+ {
+ VisualFloor vf = (VisualFloor)i;
+ floors[vf.Level.sector] = vf;
+ }
+ else if (i is VisualCeiling)
+ {
+ VisualCeiling vc = (VisualCeiling)i;
+ ceilings[vc.Level.sector] = vc;
+ }
+ else if (i is BaseVisualThing)
+ {
+ things.Add((BaseVisualThing)i);
+ }
+ }
}
- if(!string.IsNullOrEmpty(s))
+ // Check what we have
+ if (floors.Count + ceilings.Count == 0 && (things.Count == 0 || !General.Map.FormatInterface.HasThingHeight))
{
- General.Interface.DisplayStatus(StatusType.Warning, "Can't do: at least 2 selected " + s + " are required!");
+ General.Interface.DisplayStatus(StatusType.Warning, "No suitable objects found!");
return;
}
- }
- // Process floors...
- int maxSelectedHeight = int.MinValue;
- int minSelectedCeilingHeight = int.MaxValue;
- int targetCeilingHeight = int.MaxValue;
-
- // Get highest ceiling height from selection
- foreach(KeyValuePair group in ceilings)
- {
- if(group.Key.CeilHeight > maxSelectedHeight) maxSelectedHeight = group.Key.CeilHeight;
- }
-
- if(withinSelection)
- {
- // We are raising, so we don't need to check anything
- targetCeilingHeight = maxSelectedHeight;
- }
- else
- {
- // Get next higher floor or ceiling from surrounding unselected sectors
- foreach(Sector s in BuilderModesTools.GetSectorsAround(this, ceilings.Keys))
+ if (withinSelection)
{
- if(s.FloorHeight < targetCeilingHeight && s.FloorHeight > maxSelectedHeight)
- targetCeilingHeight = s.FloorHeight;
- else if(s.CeilHeight < targetCeilingHeight && s.CeilHeight > maxSelectedHeight)
- targetCeilingHeight = s.CeilHeight;
+ string s = string.Empty;
+
+ if (floors.Count == 1) s = "floors";
+
+ if (ceilings.Count == 1)
+ {
+ if (!string.IsNullOrEmpty(s)) s += " and ";
+ s += "ceilings";
+ }
+
+ if (!string.IsNullOrEmpty(s))
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Can't do: at least 2 selected " + s + " are required!");
+ return;
+ }
}
- }
- // Ceilings...
- maxSelectedHeight = int.MinValue;
- int targetFloorHeight = int.MaxValue;
+ // Process floors...
+ int maxSelectedHeight = int.MinValue;
+ int minSelectedCeilingHeight = int.MaxValue;
+ int targetCeilingHeight = int.MaxValue;
- // Get maximum floor and minimum ceiling heights from selection
- foreach(KeyValuePair group in floors)
- {
- if(group.Key.FloorHeight > maxSelectedHeight) maxSelectedHeight = group.Key.FloorHeight;
- if(group.Key.CeilHeight < minSelectedCeilingHeight) minSelectedCeilingHeight = group.Key.CeilHeight;
- }
-
- if(withinSelection)
- {
- // Check heights
- if(minSelectedCeilingHeight < maxSelectedHeight)
+ // Get highest ceiling height from selection
+ foreach (KeyValuePair group in ceilings)
{
- General.Interface.DisplayStatus(StatusType.Warning, "Can't do: lowest ceiling is lower than highest floor!");
+ if (group.Key.CeilHeight > maxSelectedHeight) maxSelectedHeight = group.Key.CeilHeight;
+ }
+
+ if (withinSelection)
+ {
+ // We are raising, so we don't need to check anything
+ targetCeilingHeight = maxSelectedHeight;
+ }
+ else
+ {
+ // Get next higher floor or ceiling from surrounding unselected sectors
+ foreach (Sector s in BuilderModesTools.GetSectorsAround(this, ceilings.Keys))
+ {
+ if (s.FloorHeight < targetCeilingHeight && s.FloorHeight > maxSelectedHeight)
+ targetCeilingHeight = s.FloorHeight;
+ else if (s.CeilHeight < targetCeilingHeight && s.CeilHeight > maxSelectedHeight)
+ targetCeilingHeight = s.CeilHeight;
+ }
+ }
+
+ // Ceilings...
+ maxSelectedHeight = int.MinValue;
+ int targetFloorHeight = int.MaxValue;
+
+ // Get maximum floor and minimum ceiling heights from selection
+ foreach (KeyValuePair group in floors)
+ {
+ if (group.Key.FloorHeight > maxSelectedHeight) maxSelectedHeight = group.Key.FloorHeight;
+ if (group.Key.CeilHeight < minSelectedCeilingHeight) minSelectedCeilingHeight = group.Key.CeilHeight;
+ }
+
+ if (withinSelection)
+ {
+ // Check heights
+ if (minSelectedCeilingHeight < maxSelectedHeight)
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Can't do: lowest ceiling is lower than highest floor!");
+ return;
+ }
+ targetFloorHeight = maxSelectedHeight;
+ }
+ else
+ {
+ // Get next higher floor or ceiling from surrounding unselected sectors
+ foreach (Sector s in BuilderModesTools.GetSectorsAround(this, floors.Keys))
+ {
+ if (s.FloorHeight > maxSelectedHeight && s.FloorHeight < targetFloorHeight && s.FloorHeight <= minSelectedCeilingHeight)
+ targetFloorHeight = s.FloorHeight;
+ else if (s.CeilHeight > maxSelectedHeight && s.CeilHeight < targetFloorHeight && s.CeilHeight <= minSelectedCeilingHeight)
+ targetFloorHeight = s.CeilHeight;
+ }
+ }
+
+ //CHECK VALUES
+ string alignFailDescription = string.Empty;
+
+ if (floors.Count > 0 && targetFloorHeight == int.MaxValue)
+ {
+ // Raise to lowest ceiling?
+ if (!withinSelection && minSelectedCeilingHeight > maxSelectedHeight)
+ {
+ targetFloorHeight = minSelectedCeilingHeight;
+ }
+ else
+ {
+ alignFailDescription = floors.Count > 1 ? "floors" : "floor";
+ }
+ }
+
+ if (ceilings.Count > 0 && targetCeilingHeight == int.MaxValue)
+ {
+ if (!string.IsNullOrEmpty(alignFailDescription)) alignFailDescription += " and ";
+ alignFailDescription += ceilings.Count > 1 ? "ceilings" : "ceiling";
+ }
+
+ if (!string.IsNullOrEmpty(alignFailDescription))
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Unable to align selected " + alignFailDescription + "!");
return;
- }
- targetFloorHeight = maxSelectedHeight;
- }
- else
- {
- // Get next higher floor or ceiling from surrounding unselected sectors
- foreach(Sector s in BuilderModesTools.GetSectorsAround(this, floors.Keys))
- {
- if(s.FloorHeight > maxSelectedHeight && s.FloorHeight < targetFloorHeight && s.FloorHeight <= minSelectedCeilingHeight)
- targetFloorHeight = s.FloorHeight;
- else if(s.CeilHeight > maxSelectedHeight && s.CeilHeight < targetFloorHeight && s.CeilHeight <= minSelectedCeilingHeight)
- targetFloorHeight = s.CeilHeight;
}
- }
- //CHECK VALUES
- string alignFailDescription = string.Empty;
+ //APPLY VALUES
+ PreAction(UndoGroup.SectorHeightChange);
- if(floors.Count > 0 && targetFloorHeight == int.MaxValue)
- {
- // Raise to lowest ceiling?
- if(!withinSelection && minSelectedCeilingHeight > maxSelectedHeight)
+ // Change floors heights
+ if (floors.Count > 0)
{
- targetFloorHeight = minSelectedCeilingHeight;
- }
- else
- {
- alignFailDescription = floors.Count > 1 ? "floors" : "floor";
+ foreach (KeyValuePair group in floors)
+ {
+ if (targetFloorHeight != group.Key.FloorHeight)
+ group.Value.OnChangeTargetHeight(targetFloorHeight - group.Key.FloorHeight);
+ }
}
- }
- if(ceilings.Count > 0 && targetCeilingHeight == int.MaxValue)
- {
- if(!string.IsNullOrEmpty(alignFailDescription)) alignFailDescription += " and ";
- alignFailDescription += ceilings.Count > 1 ? "ceilings" : "ceiling";
- }
-
- if(!string.IsNullOrEmpty(alignFailDescription))
- {
- General.Interface.DisplayStatus(StatusType.Warning, "Unable to align selected " + alignFailDescription + "!");
- return;
- }
-
- //APPLY VALUES
- PreAction(UndoGroup.SectorHeightChange);
-
- // Change floors heights
- if(floors.Count > 0)
- {
- foreach(KeyValuePair group in floors)
+ // Change ceilings heights
+ if (ceilings.Count > 0)
{
- if(targetFloorHeight != group.Key.FloorHeight)
- group.Value.OnChangeTargetHeight(targetFloorHeight - group.Key.FloorHeight);
+ foreach (KeyValuePair group in ceilings)
+ {
+ if (targetCeilingHeight != group.Key.CeilHeight)
+ group.Value.OnChangeTargetHeight(targetCeilingHeight - group.Key.CeilHeight);
+ }
}
- }
- // Change ceilings heights
- if(ceilings.Count > 0)
- {
- foreach(KeyValuePair group in ceilings)
+ // Change things heights. Align to higher 3d floor or actual ceiling.
+ if (General.Map.FormatInterface.HasThingHeight)
{
- if(targetCeilingHeight != group.Key.CeilHeight)
- group.Value.OnChangeTargetHeight(targetCeilingHeight - group.Key.CeilHeight);
+ foreach (BaseVisualThing vt in things)
+ {
+ if (vt.Thing.Sector == null) continue;
+ SectorData sd = GetSectorData(vt.Thing.Sector);
+ vt.OnMove(new Vector3D(vt.Thing.Position, BuilderModesTools.GetHigherThingZ(this, sd, vt)));
+ }
}
- }
- // Change things heights. Align to higher 3d floor or actual ceiling.
- if(General.Map.FormatInterface.HasThingHeight)
- {
- foreach(BaseVisualThing vt in things)
- {
- if(vt.Thing.Sector == null) continue;
- SectorData sd = GetSectorData(vt.Thing.Sector);
- vt.OnMove(new Vector3D(vt.Thing.Position, BuilderModesTools.GetHigherThingZ(this, sd, vt)));
- }
+ PostAction();
}
-
- PostAction();
}
//mxd
[BeginAction("lowersectortonearest")]
public void LowerSectorToNearest()
{
- Dictionary floors = new Dictionary();
- Dictionary ceilings = new Dictionary();
- List things = new List();
- bool withinSelection = General.Interface.CtrlState;
+ List selectedhandles = GetSelectedSlopeHandles();
- // Get selection
- if(selectedobjects.Count == 0)
+ if (selectedhandles.Count > 0)
{
- if(target.picked is VisualFloor)
+ if (selectedhandles.Count > 1)
{
- VisualFloor vf = (VisualFloor)target.picked;
- floors.Add(vf.Level.sector, vf);
- }
- else if(target.picked is VisualCeiling)
+ General.Interface.DisplayStatus(StatusType.Warning, "Can only lower to nearest when one visual slope handle is selected");
+ return;
+ }
+
+ int startheight = (int)Math.Round(selectedhandles[0].GetCenterPoint().z);
+ int targetheight = int.MinValue;
+
+ foreach (KeyValuePair> kvp in allslopehandles)
{
- VisualCeiling vc = (VisualCeiling)target.picked;
- ceilings.Add(vc.Level.sector, vc);
- }
- else if(target.picked is BaseVisualThing)
+ foreach (VisualSidedefSlope handle in kvp.Value)
+ {
+ if (handle != selectedhandles[0] && handle.Sidedef.Line == selectedhandles[0].Sidedef.Line)
+ {
+ int z = (int)Math.Round(handle.GetCenterPoint().z);
+
+ if (z < startheight && z > targetheight)
+ targetheight = z;
+ }
+ }
+ }
+
+ if (targetheight != int.MinValue)
{
- things.Add((BaseVisualThing)target.picked);
+ PreAction(UndoGroup.SectorHeightChange);
+ selectedhandles[0].OnChangeTargetHeight(-(startheight - targetheight));
+ PostAction();
+ }
+ else
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Can't lower: already at the lowest level");
}
}
else
{
- foreach(IVisualEventReceiver i in selectedobjects)
+ Dictionary floors = new Dictionary();
+ Dictionary ceilings = new Dictionary();
+ List things = new List();
+ bool withinSelection = General.Interface.CtrlState;
+
+ // Get selection
+ if (selectedobjects.Count == 0)
{
- if(i is VisualFloor)
+ if (target.picked is VisualFloor)
{
- VisualFloor vf = (VisualFloor)i;
- floors.Add(vf.Level.sector, vf);
- }
- else if(i is VisualCeiling)
+ VisualFloor vf = (VisualFloor)target.picked;
+ floors[vf.Level.sector] = vf;
+ }
+ else if (target.picked is VisualCeiling)
{
- VisualCeiling vc = (VisualCeiling)i;
- ceilings.Add(vc.Level.sector, vc);
- }
- else if(i is BaseVisualThing)
+ VisualCeiling vc = (VisualCeiling)target.picked;
+ ceilings[vc.Level.sector] = vc;
+ }
+ else if (target.picked is BaseVisualThing)
{
- things.Add((BaseVisualThing)i);
+ things.Add((BaseVisualThing)target.picked);
}
}
- }
-
- // Check what we have
- if(floors.Count + ceilings.Count == 0 && (things.Count == 0 || !General.Map.FormatInterface.HasThingHeight))
- {
- General.Interface.DisplayStatus(StatusType.Warning, "No suitable objects found!");
- return;
- }
-
- if(withinSelection)
- {
- string s = string.Empty;
-
- if(floors.Count == 1) s = "floors";
-
- if(ceilings.Count == 1)
+ else
{
- if(!string.IsNullOrEmpty(s)) s += " and ";
- s += "ceilings";
+ foreach (IVisualEventReceiver i in selectedobjects)
+ {
+ if (i is VisualFloor)
+ {
+ VisualFloor vf = (VisualFloor)i;
+ floors[vf.Level.sector] = vf;
+ }
+ else if (i is VisualCeiling)
+ {
+ VisualCeiling vc = (VisualCeiling)i;
+ ceilings[vc.Level.sector] = vc;
+ }
+ else if (i is BaseVisualThing)
+ {
+ things.Add((BaseVisualThing)i);
+ }
+ }
}
- if(!string.IsNullOrEmpty(s))
+ // Check what we have
+ if (floors.Count + ceilings.Count == 0 && (things.Count == 0 || !General.Map.FormatInterface.HasThingHeight))
{
- General.Interface.DisplayStatus(StatusType.Warning, "Can't do: at least 2 selected " + s + " are required!");
+ General.Interface.DisplayStatus(StatusType.Warning, "No suitable objects found!");
return;
}
- }
- // Process floors...
- int minSelectedHeight = int.MaxValue;
- int targetFloorHeight = int.MinValue;
-
- // Get minimum floor height from selection
- foreach(KeyValuePair group in floors)
- {
- if(group.Key.FloorHeight < minSelectedHeight) minSelectedHeight = group.Key.FloorHeight;
- }
-
- if(withinSelection)
- {
- // We are lowering, so we don't need to check anything
- targetFloorHeight = minSelectedHeight;
- }
- else
- {
- // Get next lower ceiling or floor from surrounding unselected sectors
- foreach(Sector s in BuilderModesTools.GetSectorsAround(this, floors.Keys))
+ if (withinSelection)
{
- if(s.CeilHeight > targetFloorHeight && s.CeilHeight < minSelectedHeight)
- targetFloorHeight = s.CeilHeight;
- else if(s.FloorHeight > targetFloorHeight && s.FloorHeight < minSelectedHeight)
- targetFloorHeight = s.FloorHeight;
+ string s = string.Empty;
+
+ if (floors.Count == 1) s = "floors";
+
+ if (ceilings.Count == 1)
+ {
+ if (!string.IsNullOrEmpty(s)) s += " and ";
+ s += "ceilings";
+ }
+
+ if (!string.IsNullOrEmpty(s))
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Can't do: at least 2 selected " + s + " are required!");
+ return;
+ }
}
- }
- // Ceilings...
- minSelectedHeight = int.MaxValue;
- int maxSelectedFloorHeight = int.MinValue;
- int targetCeilingHeight = int.MinValue;
+ // Process floors...
+ int minSelectedHeight = int.MaxValue;
+ int targetFloorHeight = int.MinValue;
- // Get minimum ceiling and maximum floor heights from selection
- foreach(KeyValuePair group in ceilings)
- {
- if(group.Key.CeilHeight < minSelectedHeight) minSelectedHeight = group.Key.CeilHeight;
- if(group.Key.FloorHeight > maxSelectedFloorHeight) maxSelectedFloorHeight = group.Key.FloorHeight;
- }
-
- if(withinSelection)
- {
- if(minSelectedHeight < maxSelectedFloorHeight)
+ // Get minimum floor height from selection
+ foreach (KeyValuePair group in floors)
{
- General.Interface.DisplayStatus(StatusType.Warning, "Can't do: lowest ceiling is lower than highest floor!");
+ if (group.Key.FloorHeight < minSelectedHeight) minSelectedHeight = group.Key.FloorHeight;
+ }
+
+ if (withinSelection)
+ {
+ // We are lowering, so we don't need to check anything
+ targetFloorHeight = minSelectedHeight;
+ }
+ else
+ {
+ // Get next lower ceiling or floor from surrounding unselected sectors
+ foreach (Sector s in BuilderModesTools.GetSectorsAround(this, floors.Keys))
+ {
+ if (s.CeilHeight > targetFloorHeight && s.CeilHeight < minSelectedHeight)
+ targetFloorHeight = s.CeilHeight;
+ else if (s.FloorHeight > targetFloorHeight && s.FloorHeight < minSelectedHeight)
+ targetFloorHeight = s.FloorHeight;
+ }
+ }
+
+ // Ceilings...
+ minSelectedHeight = int.MaxValue;
+ int maxSelectedFloorHeight = int.MinValue;
+ int targetCeilingHeight = int.MinValue;
+
+ // Get minimum ceiling and maximum floor heights from selection
+ foreach (KeyValuePair group in ceilings)
+ {
+ if (group.Key.CeilHeight < minSelectedHeight) minSelectedHeight = group.Key.CeilHeight;
+ if (group.Key.FloorHeight > maxSelectedFloorHeight) maxSelectedFloorHeight = group.Key.FloorHeight;
+ }
+
+ if (withinSelection)
+ {
+ if (minSelectedHeight < maxSelectedFloorHeight)
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Can't do: lowest ceiling is lower than highest floor!");
+ return;
+ }
+ targetCeilingHeight = minSelectedHeight;
+ }
+ else
+ {
+ // Get next lower ceiling or floor from surrounding unselected sectors
+ foreach (Sector s in BuilderModesTools.GetSectorsAround(this, ceilings.Keys))
+ {
+ if (s.CeilHeight > targetCeilingHeight && s.CeilHeight < minSelectedHeight && s.CeilHeight >= maxSelectedFloorHeight)
+ targetCeilingHeight = s.CeilHeight;
+ else if (s.FloorHeight > targetCeilingHeight && s.FloorHeight < minSelectedHeight && s.FloorHeight >= maxSelectedFloorHeight)
+ targetCeilingHeight = s.FloorHeight;
+ }
+ }
+
+ //CHECK VALUES:
+ string alignFailDescription = string.Empty;
+
+ if (floors.Count > 0 && targetFloorHeight == int.MinValue)
+ alignFailDescription = floors.Count > 1 ? "floors" : "floor";
+
+ if (ceilings.Count > 0 && targetCeilingHeight == int.MinValue)
+ {
+ // Drop to highest floor?
+ if (!withinSelection && maxSelectedFloorHeight < minSelectedHeight)
+ {
+ targetCeilingHeight = maxSelectedFloorHeight;
+ }
+ else
+ {
+ if (!string.IsNullOrEmpty(alignFailDescription)) alignFailDescription += " and ";
+ alignFailDescription += ceilings.Count > 1 ? "ceilings" : "ceiling";
+ }
+ }
+
+ if (!string.IsNullOrEmpty(alignFailDescription))
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Unable to align selected " + alignFailDescription + "!");
return;
- }
- targetCeilingHeight = minSelectedHeight;
- }
- else
- {
- // Get next lower ceiling or floor from surrounding unselected sectors
- foreach(Sector s in BuilderModesTools.GetSectorsAround(this, ceilings.Keys))
- {
- if(s.CeilHeight > targetCeilingHeight && s.CeilHeight < minSelectedHeight && s.CeilHeight >= maxSelectedFloorHeight)
- targetCeilingHeight = s.CeilHeight;
- else if(s.FloorHeight > targetCeilingHeight && s.FloorHeight < minSelectedHeight && s.FloorHeight >= maxSelectedFloorHeight)
- targetCeilingHeight = s.FloorHeight;
}
- }
- //CHECK VALUES:
- string alignFailDescription = string.Empty;
+ //APPLY VALUES:
+ PreAction(UndoGroup.SectorHeightChange);
- if(floors.Count > 0 && targetFloorHeight == int.MinValue)
- alignFailDescription = floors.Count > 1 ? "floors" : "floor";
-
- if(ceilings.Count > 0 && targetCeilingHeight == int.MinValue)
- {
- // Drop to highest floor?
- if(!withinSelection && maxSelectedFloorHeight < minSelectedHeight)
+ // Change floor height
+ if (floors.Count > 0)
{
- targetCeilingHeight = maxSelectedFloorHeight;
- }
- else
- {
- if(!string.IsNullOrEmpty(alignFailDescription)) alignFailDescription += " and ";
- alignFailDescription += ceilings.Count > 1 ? "ceilings" : "ceiling";
+ foreach (KeyValuePair group in floors)
+ {
+ if (targetFloorHeight != group.Key.FloorHeight)
+ group.Value.OnChangeTargetHeight(targetFloorHeight - group.Key.FloorHeight);
+ }
}
- }
- if(!string.IsNullOrEmpty(alignFailDescription))
- {
- General.Interface.DisplayStatus(StatusType.Warning, "Unable to align selected " + alignFailDescription + "!");
- return;
- }
-
- //APPLY VALUES:
- PreAction(UndoGroup.SectorHeightChange);
-
- // Change floor height
- if(floors.Count > 0)
- {
- foreach(KeyValuePair group in floors)
+ // Change ceiling height
+ if (ceilings.Count > 0)
{
- if(targetFloorHeight != group.Key.FloorHeight)
- group.Value.OnChangeTargetHeight(targetFloorHeight - group.Key.FloorHeight);
+ foreach (KeyValuePair group in ceilings)
+ {
+ if (targetCeilingHeight != group.Key.CeilHeight)
+ group.Value.OnChangeTargetHeight(targetCeilingHeight - group.Key.CeilHeight);
+ }
}
- }
- // Change ceiling height
- if(ceilings.Count > 0)
- {
- foreach(KeyValuePair group in ceilings)
+ // Change things height. Drop to lower 3d floor or to actual sector's floor.
+ if (General.Map.FormatInterface.HasThingHeight)
{
- if(targetCeilingHeight != group.Key.CeilHeight)
- group.Value.OnChangeTargetHeight(targetCeilingHeight - group.Key.CeilHeight);
+ foreach (BaseVisualThing vt in things)
+ {
+ if (vt.Thing.Sector == null) continue;
+ SectorData sd = GetSectorData(vt.Thing.Sector);
+ vt.OnMove(new Vector3D(vt.Thing.Position, BuilderModesTools.GetLowerThingZ(this, sd, vt)));
+ }
}
- }
- // Change things height. Drop to lower 3d floor or to actual sector's floor.
- if(General.Map.FormatInterface.HasThingHeight)
- {
- foreach(BaseVisualThing vt in things)
- {
- if(vt.Thing.Sector == null) continue;
- SectorData sd = GetSectorData(vt.Thing.Sector);
- vt.OnMove(new Vector3D(vt.Thing.Position, BuilderModesTools.GetLowerThingZ(this, sd, vt)));
- }
+ PostAction();
}
-
- PostAction();
}
//mxd
@@ -2800,7 +3037,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void RaiseBrightness8()
{
PreAction(UndoGroup.SectorBrightnessChange);
- List objs = GetSelectedObjects(true, true, false, false);
+ List objs = GetSelectedObjects(true, true, false, false, false);
foreach(IVisualEventReceiver i in objs) i.OnChangeTargetBrightness(true);
PostAction();
}
@@ -2809,7 +3046,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void LowerBrightness8()
{
PreAction(UndoGroup.SectorBrightnessChange);
- List objs = GetSelectedObjects(true, true, false, false);
+ List objs = GetSelectedObjects(true, true, false, false, false);
foreach(IVisualEventReceiver i in objs) i.OnChangeTargetBrightness(false);
PostAction();
}
@@ -2831,7 +3068,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
private void MoveTextureByOffset(int ox, int oy)
{
PreAction(UndoGroup.TextureOffsetChange);
- IEnumerable objs = RemoveDuplicateSidedefs(GetSelectedObjects(true, true, false, false));
+ IEnumerable objs = RemoveDuplicateSidedefs(GetSelectedObjects(true, true, false, false, false));
foreach(IVisualEventReceiver i in objs) i.OnChangeTextureOffset(ox, oy, true);
PostAction();
}
@@ -2848,7 +3085,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
private void ScaleTexture(int incrementx, int incrementy)
{
PreAction(UndoGroup.TextureScaleChange);
- List objs = GetSelectedObjects(true, true, true, false);
+ List objs = GetSelectedObjects(true, true, true, false, false);
foreach(IVisualEventReceiver i in objs) i.OnChangeScale(incrementx, incrementy);
PostAction();
}
@@ -2878,7 +3115,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void TexturePaste()
{
PreAction(UndoGroup.None);
- List objs = GetSelectedObjects(true, true, false, false);
+ List objs = GetSelectedObjects(true, true, false, false, false);
foreach(IVisualEventReceiver i in objs) i.OnPasteTexture();
PostAction();
}
@@ -2980,7 +3217,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Map.Map.ClearMarkedSidedefs(false);
//get selection
- List objs = GetSelectedObjects(false, true, false, false);
+ List objs = GetSelectedObjects(false, true, false, false, false);
//align
foreach(IVisualEventReceiver i in objs)
@@ -3016,7 +3253,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
PreAction(UndoGroup.None);
// Get selection
- List objs = GetSelectedObjects(false, true, false, false);
+ List objs = GetSelectedObjects(false, true, false, false, false);
List sides = new List();
foreach(IVisualEventReceiver i in objs)
{
@@ -3068,7 +3305,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void ResetTexture()
{
PreAction(UndoGroup.None);
- List objs = GetSelectedObjects(true, true, true, false);
+ List objs = GetSelectedObjects(true, true, true, false, false);
foreach(IVisualEventReceiver i in objs) i.OnResetTextureOffset();
PostAction();
}
@@ -3077,7 +3314,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void ResetLocalOffsets()
{
PreAction(UndoGroup.None);
- List objs = GetSelectedObjects(true, true, true, false);
+ List objs = GetSelectedObjects(true, true, true, false, false);
foreach(IVisualEventReceiver i in objs) i.OnResetLocalTextureOffset();
PostAction();
}
@@ -3102,7 +3339,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void TexturePasteOffsets()
{
PreAction(UndoGroup.None);
- List objs = GetSelectedObjects(true, true, false, false);
+ List objs = GetSelectedObjects(true, true, false, false, false);
foreach(IVisualEventReceiver i in objs) i.OnPasteTextureOffsets();
PostAction();
}
@@ -3119,7 +3356,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void PasteProperties()
{
PreAction(UndoGroup.None);
- List objs = GetSelectedObjects(true, true, true, true);
+ List objs = GetSelectedObjects(true, true, true, true, false);
foreach(IVisualEventReceiver i in objs) i.OnPasteProperties(false);
PostAction();
}
@@ -3134,7 +3371,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
var selection = new List();
// Sectors selected?
- var obj = GetSelectedObjects(true, false, false, false);
+ var obj = GetSelectedObjects(true, false, false, false, false);
if(obj.Count > 0)
{
targettypes.Add(MapElementType.SECTOR);
@@ -3153,7 +3390,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
// Sidedefs selected?
- obj = GetSelectedObjects(false, true, false, false);
+ obj = GetSelectedObjects(false, true, false, false, false);
if(obj.Count > 0)
{
targettypes.Add(MapElementType.SIDEDEF);
@@ -3172,7 +3409,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
// Things selected?
- obj = GetSelectedObjects(false, false, true, false);
+ obj = GetSelectedObjects(false, false, true, false, false);
if(obj.Count > 0)
{
targettypes.Add(MapElementType.THING);
@@ -3191,7 +3428,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
}
// Vertices selected?
- obj = GetSelectedObjects(false, false, false, true);
+ obj = GetSelectedObjects(false, false, false, true, false);
if(obj.Count > 0)
{
targettypes.Add(MapElementType.VERTEX);
@@ -3267,7 +3504,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
public void Delete()
{
PreAction(UndoGroup.None);
- List objs = GetSelectedObjects(true, true, true, true);
+ List objs = GetSelectedObjects(true, true, true, true, false);
foreach (IVisualEventReceiver i in objs)
{
if (i is BaseVisualThing)
@@ -3283,7 +3520,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
[BeginAction("copyselection", BaseAction = true)]
public void CopySelection()
{
- List objs = GetSelectedObjects(false, false, true, false);
+ List objs = GetSelectedObjects(false, false, true, false, false);
if(objs.Count == 0) return;
copybuffer.Clear();
@@ -3308,7 +3545,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
CreateUndo("Cut " + rest);
General.Interface.DisplayStatus(StatusType.Info, "Cut " + rest);
- List objs = GetSelectedObjects(false, false, true, false);
+ List objs = GetSelectedObjects(false, false, true, false, false);
foreach(IVisualEventReceiver i in objs)
{
BaseVisualThing thing = (BaseVisualThing)i;
@@ -3322,7 +3559,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
General.Map.ThingsFilter.Update();
// [ZZ] Clear selected things.
- ClearSelection(false, false, true, false, false);
+ ClearSelection(false, false, true, false, false, false);
// Update event lines
renderer.SetEventLines(LinksCollector.GetHelperShapes(General.Map.ThingsFilter.VisibleThings, blockmap));
@@ -3398,7 +3635,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
PreAction(UndoGroup.ThingAngleChange);
- List selection = GetSelectedObjects(true, false, true, false);
+ List selection = GetSelectedObjects(true, false, true, false, false);
if(selection.Count == 0) return;
foreach(IVisualEventReceiver obj in selection)
@@ -3460,7 +3697,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
PreAction(UndoGroup.ThingPitchChange);
- List selection = GetSelectedObjects(false, false, true, false);
+ List selection = GetSelectedObjects(false, false, true, false, false);
if(selection.Count == 0) return;
foreach(IVisualEventReceiver obj in selection)
@@ -3491,7 +3728,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
PreAction(UndoGroup.ThingRollChange);
- List selection = GetSelectedObjects(false, false, true, false);
+ List selection = GetSelectedObjects(false, false, true, false, false);
if(selection.Count == 0) return;
foreach(IVisualEventReceiver obj in selection)
@@ -3862,6 +4099,105 @@ namespace CodeImp.DoomBuilder.BuilderModes
GetTargetEventReceiver(true).OnPaintSelectEnd();
}
+ [BeginAction("togglevisualslopepicking")]
+ public void ToggleVisualSlopePicking()
+ {
+ if (pickingmode != PickingMode.SlopeHandles)
+ pickingmode = PickingMode.SlopeHandles;
+ else
+ {
+ pickingmode = PickingMode.Default;
+
+ // Clear smart pivot handles, otherwise it will keep being displayed
+ foreach (KeyValuePair> kvp in allslopehandles)
+ foreach (VisualSidedefSlope checkhandle in kvp.Value)
+ checkhandle.SmartPivot = false;
+ }
+ }
+
+ [BeginAction("slopebetweenhandles")]
+ public void SlopeBetweenHandles()
+ {
+ List selectedsectors = GetSelectedObjects(true, false, false, false, false);
+ if (selectedsectors.Count == 0)
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "You need to select floors or ceilings to slope between slope handles.");
+ return;
+ }
+
+ List handles = GetSelectedSlopeHandles();
+
+ // No handles selected, try to slope between highlighted handle and it smart pivot
+ if (handles.Count == 0 && HighlightedTarget is VisualSidedefSlope)
+ {
+ VisualSidedefSlope handle = VisualSidedefSlope.GetSmartPivotHandle((VisualSidedefSlope)HighlightedTarget, this);
+ if (handle == null)
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Couldn't find a smart pivot handle.");
+ return;
+ }
+
+ handles.Add((VisualSidedefSlope)HighlightedTarget);
+ handles.Add(handle);
+ }
+ // One handle selected, try to slope between it and the highlighted handle or the selected one's smart pivot
+ else if (handles.Count == 1)
+ {
+ if (HighlightedTarget == handles[0] || !(HighlightedTarget is VisualSidedefSlope))
+ {
+ VisualSidedefSlope handle;
+
+ if(HighlightedTarget is VisualSidedefSlope)
+ handle = VisualSidedefSlope.GetSmartPivotHandle((VisualSidedefSlope)HighlightedTarget, this);
+ else
+ handle = VisualSidedefSlope.GetSmartPivotHandle(handles[0], this);
+
+ if (handle == null)
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Couldn't find a smart pivot handle.");
+ return;
+ }
+
+ handles.Add(handle);
+ }
+ else
+ {
+ handles.Add((VisualSidedefSlope)HighlightedTarget);
+ }
+ }
+ // Return if more than two handles are selected
+ else if(handles.Count > 2)
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "Too many slope handles selected.");
+ return;
+ }
+ // Everything else
+ else if(handles.Count != 2)
+ {
+ General.Interface.DisplayStatus(StatusType.Warning, "No slope handles selected or highlighted.");
+ return;
+ }
+
+ General.Map.UndoRedo.CreateUndo("Slope between slope handles");
+
+ // Create the new plane
+ Vector3D p1 = new Vector3D(handles[0].Sidedef.Line.Start.Position, handles[0].Level.plane.GetZ(handles[0].Sidedef.Line.Start.Position));
+ Vector3D p2 = new Vector3D(handles[0].Sidedef.Line.End.Position, handles[0].Level.plane.GetZ(handles[0].Sidedef.Line.End.Position));
+ Vector3D p3 = new Vector3D(handles[1].Sidedef.Line.Line.GetCoordinatesAt(0.5f), handles[1].Level.plane.GetZ(handles[1].Sidedef.Line.Line.GetCoordinatesAt(0.5f)));
+ Plane plane = new Plane(p1, p2, p3, true);
+
+ // Apply slope
+ foreach (BaseVisualGeometrySector bvgs in selectedsectors)
+ {
+ VisualSidedefSlope.ApplySlope(bvgs.Level, plane, this);
+ bvgs.Sector.UpdateSectorGeometry(true);
+ }
+
+ UpdateChangedObjects();
+
+ General.Interface.DisplayStatus(StatusType.Action, "Sloped between slope handles.");
+ }
+
#endregion
#region ================== Texture Alignment
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs b/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
index 2ca6960c..c9ca4fa0 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualCeiling.cs
@@ -391,6 +391,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
//mxd. Modify slope offset?
if(level.sector.CeilSlope.GetLengthSq() > 0)
{
+ /*
Vector3D center = new Vector3D(level.sector.BBox.X + level.sector.BBox.Width / 2,
level.sector.BBox.Y + level.sector.BBox.Height / 2,
level.sector.CeilHeight);
@@ -401,6 +402,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
false);
level.sector.CeilSlopeOffset = p.Offset;
+ */
+
+ level.sector.CeilSlopeOffset -= level.sector.CeilSlope.z * amount;
}
}
@@ -650,42 +654,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
if(!General.Map.UDMF) return;
- //is is a surface with line slope?
- float slopeAngle = level.plane.Normal.GetAngleZ() - Angle2D.PIHALF;
-
- if(slopeAngle == 0) //it's a horizontal plane
- {
- AlignTextureToClosestLine(alignx, aligny);
- }
- else //it can be a surface with line slope
- {
- Linedef slopeSource = null;
- bool isFront = false;
-
- foreach(Sidedef side in Sector.Sector.Sidedefs)
- {
- if(side.Line.Action == 181)
- {
- if(side.Line.Args[1] == 1 && side.Line.Front != null && side.Line.Front == side)
- {
- slopeSource = side.Line;
- isFront = true;
- break;
- }
-
- if(side.Line.Args[1] == 2 && side.Line.Back != null && side.Line.Back == side)
- {
- slopeSource = side.Line;
- break;
- }
- }
- }
-
- if(slopeSource != null && slopeSource.Front != null && slopeSource.Front.Sector != null && slopeSource.Back != null && slopeSource.Back.Sector != null)
- AlignTextureToSlopeLine(slopeSource, slopeAngle, isFront, alignx, aligny);
- else
- AlignTextureToClosestLine(alignx, aligny);
- }
+ AlignTextureToClosestLine(alignx, aligny);
}
#endregion
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs b/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
index 8dc4bf31..8b28ce1e 100755
--- a/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualFloor.cs
@@ -351,6 +351,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
//mxd. Modify slope offset?
if(level.sector.FloorSlope.GetLengthSq() > 0)
{
+ /*
Vector3D center = new Vector3D(level.sector.BBox.X + level.sector.BBox.Width / 2,
level.sector.BBox.Y + level.sector.BBox.Height / 2,
level.sector.FloorHeight);
@@ -361,6 +362,9 @@ namespace CodeImp.DoomBuilder.BuilderModes
true);
level.sector.FloorSlopeOffset = p.Offset;
+ */
+
+ level.sector.FloorSlopeOffset -= level.sector.FloorSlope.z * amount;
}
}
@@ -582,42 +586,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
{
if(!General.Map.UDMF) return;
- //is is a surface with line slope?
- float slopeAngle = level.plane.Normal.GetAngleZ() - Angle2D.PIHALF;
-
- if(slopeAngle == 0) //it's a horizontal plane
- {
- AlignTextureToClosestLine(alignx, aligny);
- }
- else //it can be a surface with line slope
- {
- Linedef slopeSource = null;
- bool isFront = false;
-
- foreach(Sidedef side in Sector.Sector.Sidedefs)
- {
- if(side.Line.Action == 181)
- {
- if(side.Line.Args[0] == 1 && side.Line.Front != null && side.Line.Front == side)
- {
- slopeSource = side.Line;
- isFront = true;
- break;
- }
-
- if(side.Line.Args[0] == 2 && side.Line.Back != null && side.Line.Back == side)
- {
- slopeSource = side.Line;
- break;
- }
- }
- }
-
- if(slopeSource != null && slopeSource.Front != null && slopeSource.Front.Sector != null && slopeSource.Back != null && slopeSource.Back.Sector != null)
- AlignTextureToSlopeLine(slopeSource, slopeAngle, isFront, alignx, aligny);
- else
- AlignTextureToClosestLine(alignx, aligny);
- }
+ AlignTextureToClosestLine(alignx, aligny);
}
#endregion
diff --git a/Source/Plugins/BuilderModes/VisualModes/VisualSidedefSlope.cs b/Source/Plugins/BuilderModes/VisualModes/VisualSidedefSlope.cs
new file mode 100644
index 00000000..e35b3503
--- /dev/null
+++ b/Source/Plugins/BuilderModes/VisualModes/VisualSidedefSlope.cs
@@ -0,0 +1,412 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using CodeImp.DoomBuilder.BuilderModes;
+using CodeImp.DoomBuilder.Geometry;
+using CodeImp.DoomBuilder.Map;
+using CodeImp.DoomBuilder.Rendering;
+
+namespace CodeImp.DoomBuilder.VisualModes
+{
+ internal class VisualSidedefSlope : VisualSlope, IVisualEventReceiver
+ {
+ #region ================== Variables
+
+ private readonly BaseVisualMode mode;
+ private readonly Sidedef sidedef;
+ private readonly SectorLevel level;
+ private readonly bool up;
+ private Vector3D pickintersect;
+ private float pickrayu;
+ private Plane plane;
+
+ #endregion
+
+ #region ================== Constants
+
+ private const int SIZE = 8;
+
+ #endregion
+
+ #region ================== Properties
+
+ public Sidedef Sidedef { get { return sidedef; } }
+ public SectorLevel Level { get { return level; } }
+ public int NormalizedAngleDeg { get { return (sidedef.Line.AngleDeg >= 180) ? (sidedef.Line.AngleDeg - 180) : sidedef.Line.AngleDeg; } }
+
+ #endregion
+
+ #region ================== Constructor / Destructor
+
+ public VisualSidedefSlope(BaseVisualMode mode, SectorLevel level, Sidedef sidedef, bool up) : base()
+ {
+ this.mode = mode;
+ this.sidedef = sidedef;
+ this.level = level;
+ this.up = up;
+
+ // length = sidedef.Line.Length;
+
+ Update();
+
+ // We have no destructor
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ #region ================== Methods
+
+ public Vector3D GetCenterPoint()
+ {
+ Vector2D p = sidedef.Line.GetCenterPoint();
+ return new Vector3D(p, level.plane.GetZ(p));
+ }
+
+ public override void Update()
+ {
+ plane = new Plane(level.plane.Normal, level.plane.Offset - 0.1f);
+
+ if (!up)
+ plane = plane.GetInverted();
+
+ UpdatePosition();
+
+ length = new Line3D(new Vector3D(sidedef.Line.Line.v1, plane.GetZ(sidedef.Line.Line.v1)), new Vector3D(sidedef.Line.Line.v2, plane.GetZ(sidedef.Line.Line.v2))).GetDelta().GetLength();
+ }
+
+ ///
+ /// This is called when the thing must be tested for line intersection. This should reject
+ /// as fast as possible to rule out all geometry that certainly does not touch the line.
+ ///
+ public override bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir)
+ {
+ RectangleF bbox = sidedef.Sector.BBox;
+
+ if ((up && plane.Distance(from) > 0.0f) || (!up && plane.Distance(from) < 0.0f))
+ {
+ if (plane.GetIntersection(from, to, ref pickrayu))
+ {
+ if (pickrayu > 0.0f)
+ {
+ pickintersect = from + (to - from) * pickrayu;
+
+ return ((pickintersect.x >= bbox.Left) && (pickintersect.x <= bbox.Right) &&
+ (pickintersect.y >= bbox.Top) && (pickintersect.y <= bbox.Bottom));
+ }
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// This is called when the thing must be tested for line intersection. This should perform
+ /// accurate hit detection and set u_ray to the position on the ray where this hits the geometry.
+ ///
+ public override bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray)
+ {
+ u_ray = pickrayu;
+
+ Sidedef sd = MapSet.NearestSidedef(sidedef.Sector.Sidedefs, pickintersect);
+ if (sd == sidedef) {
+ float side = sd.Line.SideOfLine(pickintersect);
+
+ if ((side <= 0.0f && sd.IsFront) || (side > 0.0f && !sd.IsFront))
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Updates the position. Depending on 3D floors and which side of the linedef the slope handle is on the
+ /// direction of the line used as a base has to be inverted
+ ///
+ public void UpdatePosition()
+ {
+ bool invertline = false;
+
+ if (up)
+ {
+ if (level.extrafloor && level.type == SectorLevelType.Ceiling)
+ {
+ if (sidedef.IsFront)
+ invertline = true;
+ }
+ else
+ {
+ if (!sidedef.IsFront)
+ invertline = true;
+ }
+ }
+ else
+ {
+ if (level.extrafloor && level.type == SectorLevelType.Floor)
+ {
+ if (!sidedef.IsFront)
+ invertline = true;
+ }
+ else
+ {
+ if (sidedef.IsFront)
+ invertline = true;
+ }
+ }
+
+ if (invertline)
+ SetPosition(new Line2D(sidedef.Line.End.Position, sidedef.Line.Start.Position), level.plane);
+ else
+ SetPosition(sidedef.Line.Line, level.plane);
+ }
+
+ ///
+ /// Tries to find a slope handle to pivot around. If possible if finds the handle belonging to a line that has the
+ /// same angle as the start handle, and is the furthest away. If such a handle does not exist it finds one that's
+ /// closest to those specs
+ ///
+ /// The slope handle to start from (the one we need to find a pivot handle for)
+ ///
+ public static VisualSidedefSlope GetSmartPivotHandle(VisualSidedefSlope starthandle, BaseVisualMode mode)
+ {
+ VisualSidedefSlope handle = starthandle;
+ List potentialhandles = new List();
+ List selectedsectors = mode.GetSelectedObjects(true, false, false, false, false);
+
+ if (selectedsectors.Count == 0)
+ {
+ // No sectors selected, so find all handles that belong to the same level
+ foreach (VisualSidedefSlope checkhandle in mode.AllSlopeHandles[starthandle.Sidedef.Sector])
+ if (checkhandle != starthandle && checkhandle.Level == starthandle.Level)
+ potentialhandles.Add(checkhandle);
+ }
+ else
+ {
+ // Sectors are selected, get all handles from those sectors that have the same level
+ HashSet sectors = new HashSet();
+
+ foreach (BaseVisualGeometrySector bvgs in selectedsectors)
+ sectors.Add(bvgs.Sector.Sector);
+
+ foreach (Sector s in sectors)
+ foreach (VisualSidedefSlope checkhandle in mode.AllSlopeHandles[s])
+ if(checkhandle != starthandle)
+ foreach (BaseVisualGeometrySector bvgs in selectedsectors)
+ if (bvgs.Level == checkhandle.Level)
+ potentialhandles.Add(checkhandle);
+ }
+
+ foreach (KeyValuePair> kvp in mode.AllSlopeHandles)
+ foreach (VisualSidedefSlope checkhandle in kvp.Value)
+ checkhandle.SmartPivot = false;
+
+ // Sort potential handles by their angle difference to the start handle. That means that handles with less angle difference will be at the beginning of the list
+ List anglediffsortedhandles = potentialhandles.OrderBy(h => Math.Abs(starthandle.NormalizedAngleDeg - h.NormalizedAngleDeg)).ToList();
+
+ // Get all potential handles that have to same angle as the one that's closest to the start handle, then sort them by distance, and take the one that's furthest away
+ if (anglediffsortedhandles.Count > 0)
+ handle = anglediffsortedhandles.Where(h => h.NormalizedAngleDeg == anglediffsortedhandles[0].NormalizedAngleDeg).OrderByDescending(h => Math.Abs(starthandle.Sidedef.Line.Line.GetDistanceToLine(h.sidedef.Line.GetCenterPoint(), false))).First();
+
+ if (handle == starthandle)
+ return null;
+
+ return handle;
+ }
+
+ public static void ApplySlope(SectorLevel level, Plane plane, BaseVisualMode mode)
+ {
+ bool applytoceiling = false;
+
+ Vector2D center = new Vector2D(level.sector.BBox.X + level.sector.BBox.Width / 2,
+ level.sector.BBox.Y + level.sector.BBox.Height / 2);
+
+ if (level.extrafloor)
+ {
+ // The top side of 3D floors is the ceiling of the sector, but it's a "floor" in UDB, so the
+ // ceiling of the control sector has to be modified
+ if (level.type == SectorLevelType.Floor)
+ applytoceiling = true;
+ }
+ else
+ {
+ if (level.type == SectorLevelType.Ceiling)
+ applytoceiling = true;
+ }
+
+ if (applytoceiling)
+ {
+ Plane downplane = plane.GetInverted();
+ level.sector.CeilSlope = downplane.Normal;
+ level.sector.CeilSlopeOffset = downplane.Offset;
+ level.sector.CeilHeight = (int)new Plane(level.sector.CeilSlope, level.sector.CeilSlopeOffset).GetZ(center);
+ }
+ else
+ {
+ level.sector.FloorSlope = plane.Normal;
+ level.sector.FloorSlopeOffset = plane.Offset;
+ level.sector.FloorHeight = (int)new Plane(level.sector.FloorSlope, level.sector.FloorSlopeOffset).GetZ(center);
+ }
+
+ // Rebuild sector
+ BaseVisualSector vs;
+ if (mode.VisualSectorExists(level.sector))
+ {
+ vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
+ }
+ else
+ {
+ vs = mode.CreateBaseVisualSector(level.sector);
+ }
+
+ if (vs != null) vs.UpdateSectorGeometry(true);
+ }
+
+ #endregion
+
+ #region ================== Events
+
+ public void OnChangeTargetHeight(int amount)
+ {
+ VisualSlope pivothandle = null;
+ List selectedsectors = mode.GetSelectedObjects(true, false, false, false, false);
+ List levels = new List();
+
+ if (selectedsectors.Count == 0)
+ levels.Add(level);
+ else
+ {
+ foreach (BaseVisualGeometrySector bvgs in selectedsectors)
+ levels.Add(bvgs.Level);
+
+ if (!levels.Contains(level))
+ levels.Add(level);
+ }
+
+ // Try to find a slope handle the user set to be the pivot handle
+ // TODO: doing this every time is kind of stupid. Maybe store the pivot handle in the mode?
+ foreach (KeyValuePair> kvp in mode.AllSlopeHandles)
+ {
+ foreach (VisualSidedefSlope handle in kvp.Value)
+ {
+ if (handle.Pivot)
+ {
+ pivothandle = handle;
+ break;
+ }
+ }
+ }
+
+ // User didn't set a pivot handle, try to find the smart pivot handle
+ if(pivothandle == null)
+ pivothandle = GetSmartPivotHandle(this, mode);
+
+ // Still no pivot handle, cancle
+ if (pivothandle == null)
+ return;
+
+ pivothandle.SmartPivot = true;
+
+ mode.CreateUndo("Change slope");
+
+ Plane originalplane = level.plane;
+ Plane pivotplane = ((VisualSidedefSlope)pivothandle).Level.plane;
+
+ // Build a new plane. p1 and p2 are the points of the slope handle that is modified, p3 is on the line of the pivot handle
+ Vector3D p1 = new Vector3D(sidedef.Line.Start.Position, (float)Math.Round(originalplane.GetZ(sidedef.Line.Start.Position)));
+ Vector3D p2 = new Vector3D(sidedef.Line.End.Position, (float)Math.Round(originalplane.GetZ(sidedef.Line.End.Position)));
+ Vector3D p3 = new Vector3D(((VisualSidedefSlope)pivothandle).Sidedef.Line.Line.GetCoordinatesAt(0.5f), (float)Math.Round(pivotplane.GetZ(((VisualSidedefSlope)pivothandle).Sidedef.Line.Line.GetCoordinatesAt(0.5f))));
+
+ // Move the points of the handle up/down
+ p1 += new Vector3D(0f, 0f, amount);
+ p2 += new Vector3D(0f, 0f, amount);
+
+ Plane plane = new Plane(p1, p2, p3, true);
+
+ // Apply slope to surfaces
+ foreach (SectorLevel l in levels)
+ ApplySlope(l, plane, mode);
+
+ mode.SetActionResult("Changed slope.");
+ }
+
+ // Select or deselect
+ public void OnSelectEnd()
+ {
+ if (this.selected)
+ {
+ this.selected = false;
+ mode.RemoveSelectedObject(this);
+ }
+ else
+ {
+ if(this.pivot)
+ {
+ General.Interface.DisplayStatus(Windows.StatusType.Warning, "It is not allowed to mark pivot slope handles as selected.");
+ return;
+ }
+
+ this.selected = true;
+ mode.AddSelectedObject(this);
+ }
+ }
+
+ public void OnEditEnd()
+ {
+ // We can only have one pivot handle, so remove it from all first
+ foreach (KeyValuePair> kvp in mode.AllSlopeHandles)
+ {
+ foreach (VisualSlope handle in kvp.Value)
+ {
+ if (handle == mode.HighlightedTarget)
+ {
+ if (handle.Selected)
+ General.Interface.DisplayStatus(Windows.StatusType.Warning, "It is not allowed to mark selected slope handles as pivot slope handles.");
+ else
+ handle.Pivot = !handle.Pivot;
+ }
+ else
+ handle.Pivot = false;
+ }
+ }
+ }
+
+ // Return texture name
+ public string GetTextureName() { return ""; }
+
+ // Unused
+ public void OnSelectBegin() { }
+ public void OnEditBegin() { }
+ public void OnChangeTargetBrightness(bool up) { }
+ public void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection) { }
+ public void OnSelectTexture() { }
+ public void OnCopyTexture() { }
+ public void OnPasteTexture() { }
+ public void OnCopyTextureOffsets() { }
+ public void OnPasteTextureOffsets() { }
+ public void OnTextureAlign(bool alignx, bool aligny) { }
+ public void OnToggleUpperUnpegged() { }
+ public void OnToggleLowerUnpegged() { }
+ public void OnProcess(long deltatime) { }
+ public void OnTextureFloodfill() { }
+ public void OnInsert() { }
+ public void OnTextureFit(FitTextureOptions options) { } //mxd
+ public void ApplyTexture(string texture) { }
+ public void ApplyUpperUnpegged(bool set) { }
+ public void ApplyLowerUnpegged(bool set) { }
+ public void SelectNeighbours(bool select, bool withSameTexture, bool withSameHeight) { } //mxd
+ public virtual void OnPaintSelectEnd() { } // biwa
+ public void OnChangeScale(int x, int y) { }
+ public void OnResetTextureOffset() { }
+ public void OnResetLocalTextureOffset() { }
+ public void OnCopyProperties() { }
+ public void OnPasteProperties(bool usecopysetting) { }
+ public void OnDelete() { }
+ public void OnPaintSelectBegin() { }
+ public void OnMouseMove(MouseEventArgs e) { }
+
+ #endregion
+ }
+}
\ No newline at end of file