Merge branch 'visual-slope2'

This commit is contained in:
biwa 2020-04-10 10:40:42 +02:00
commit 58d5bfd59b
20 changed files with 1624 additions and 563 deletions

View file

@ -252,6 +252,8 @@
<Compile Include="Rendering\Vector3.cs" />
<Compile Include="Rendering\Vector4.cs" />
<Compile Include="Rendering\VertexBuffer.cs" />
<Compile Include="Rendering\VisualSlopeHandle.cs" />
<Compile Include="VisualModes\VisualSlope.cs" />
<Compile Include="Windows\ThingStatisticsForm.cs">
<SubType>Form</SubType>
</Compile>

View file

@ -2181,6 +2181,7 @@ namespace CodeImp.DoomBuilder
// Update settings
renderer3d.CreateProjection();
renderer3d.UpdateVertexHandle(); //mxd
renderer3d.UpdateVisualSlopeHandle();
// Things filters
General.MainWindow.UpdateThingsFilters();

View file

@ -50,6 +50,7 @@ namespace CodeImp.DoomBuilder.Rendering
void AddSectorGeometry(VisualGeometry g);
void AddThingGeometry(VisualThing t);
void SetVisualVertices(List<VisualVertex> verts);
void SetVisualSlopeHandles(List<VisualSlope> handles);
void SetEventLines(List<Line3D> lines);
void RenderCrosshair();
void SetFogMode(bool usefog);

View file

@ -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 }

View file

@ -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<VisualVertex> visualvertices;
// Visual slope handles
private List<VisualSlope> visualslopehandles;
//mxd. Event lines
private List<Line3D> 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<Line3D> lines)
{
@ -1755,6 +1817,8 @@ namespace CodeImp.DoomBuilder.Rendering
//mxd
public void SetVisualVertices(List<VisualVertex> verts) { visualvertices = verts; }
public void SetVisualSlopeHandles(List<VisualSlope> handles) { visualslopehandles = handles; }
//mxd
public void SetEventLines(List<Line3D> lines) { eventlines = lines; }

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -30,6 +30,12 @@ using CodeImp.DoomBuilder.Editing;
namespace CodeImp.DoomBuilder.VisualModes
{
public enum PickingMode
{
Default,
SlopeHandles
}
/// <summary>
/// Provides specialized functionality for a visual (3D) Doom Builder editing mode.
/// </summary>
@ -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<Thing, VisualThing> allthings;
protected Dictionary<Sector, VisualSector> allsectors;
protected Dictionary<Sector, List<VisualSlope>> allslopehandles;
protected List<VisualBlockEntry> visibleblocks;
protected List<VisualThing> visiblethings;
protected List<VisualSector> 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<Vertex, VisualVertexPair> VisualVertices { get { return vertices; } } //mxd
public Dictionary<Sector, List<VisualSlope>> 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<Sector, VisualSector>(General.Map.Map.Sectors.Count);
this.allthings = new Dictionary<Thing, VisualThing>(General.Map.Map.Things.Count);
this.allslopehandles = new Dictionary<Sector, List<VisualSlope>>(General.Map.Map.Sectors.Count);
this.visibleblocks = new List<VisualBlockEntry>();
this.visiblesectors = new List<VisualSector>(50);
this.visiblegeometry = new List<VisualGeometry>(200);
@ -110,6 +122,7 @@ namespace CodeImp.DoomBuilder.VisualModes
this.processgeometry = true;
this.processthings = true;
this.vertices = new Dictionary<Vertex, VisualVertexPair>(); //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<Thing, VisualThing> 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<Vertex, VisualVertexPair> pair in vertices)
foreach (KeyValuePair<Vertex, VisualVertexPair> 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;
}

View file

@ -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
/// <summary>
/// Selected or not? This is only used by the core to determine what color to draw it with.
/// </summary>
public bool Selected { get { return selected; } set { selected = value; } }
/// <summary>
/// Pivot or not? This is only used by the core to determine what color to draw it with.
/// </summary>
public bool Pivot { get { return pivot; } set { pivot = value; } }
/// <summary>
/// Disposed or not?
/// </summary>
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()
{
}
/// <summary>
/// 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.
/// </summary>
public virtual bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir)
{
return true;
}
/// <summary>
/// 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.
/// </summary>
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
}
}

View file

@ -495,6 +495,7 @@
<Compile Include="VisualModes\VisualMiddleDouble.cs" />
<Compile Include="VisualModes\VisualMiddleSingle.cs" />
<Compile Include="VisualModes\VisualSidedefParts.cs" />
<Compile Include="VisualModes\VisualSidedefSlope.cs" />
<Compile Include="VisualModes\VisualUpper.cs" />
<Compile Include="VisualModes\WallPolygon.cs" />
</ItemGroup>

View file

@ -170,6 +170,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
private ICollection<Vertex> unselectedvertices;
private ICollection<Linedef> unselectedlines;
private ICollection<Linedef> unstablelines; //mxd
private Dictionary<Sector, float[]> 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<Sector, float[]>();
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;

View file

@ -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

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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
}

View file

@ -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<string> 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)
{

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -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();
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// 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
/// </summary>
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);
}
/// <summary>
/// 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
/// </summary>
/// <param name="starthandle">The slope handle to start from (the one we need to find a pivot handle for)</param>
/// <returns></returns>
public static VisualSidedefSlope GetSmartPivotHandle(VisualSidedefSlope starthandle, BaseVisualMode mode)
{
VisualSidedefSlope handle = starthandle;
List<VisualSidedefSlope> potentialhandles = new List<VisualSidedefSlope>();
List<IVisualEventReceiver> 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<Sector> sectors = new HashSet<Sector>();
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<Sector, List<VisualSlope>> 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<VisualSidedefSlope> 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<IVisualEventReceiver> selectedsectors = mode.GetSelectedObjects(true, false, false, false, false);
List<SectorLevel> levels = new List<SectorLevel>();
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<Sector, List<VisualSlope>> 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<Sector, List<VisualSlope>> 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
}
}