UltimateZoneBuilder/Source/Plugins/StairSectorBuilder/StairSectorBuilderMode.cs
spherallic ba67a87c75 Optimize SRB2 NiGHTS/zoom tube/polyobject rendering
(Text labels are removed because of performance issues)
2023-06-15 12:25:46 +02:00

1737 lines
58 KiB
C#
Executable file

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows.Forms;
using System.Drawing;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
#endregion
namespace CodeImp.DoomBuilder.StairSectorBuilderMode
{
//[EditMode(DisplayName = "Stair Sector Builder",
// Volatile = true)]
[EditMode(DisplayName = "Stair Sector Builder Mode",
SwitchAction = "stairsectorbuildermode", // Action name used to switch to this mode
ButtonImage = "StairIcon.png", // Image resource name for the button
ButtonOrder = int.MinValue + 500, // Position of the button (lower is more to the left)
ButtonGroup = "000_editing",
UseByDefault = true,
SafeStartMode = false,
Volatile = true )]
public sealed class StairSectorBuilderMode : ClassicMode
{
#region ================== Constants
private const float LINE_THICKNESS = 0.6f;
private const float CONTROLPOINT_SIZE = 10.0f;
private const int INNER_SPLINE = 0;
private const int OUTER_SPLINE = 1;
#endregion
#region ================== Variables
private StairSectorBuilderForm stairsectorbuilderform;
private List<StairInfo> stairsectors;
// private List<Vector2D> controlpoints;
// private List<Vector2D> tangents;
private List<CatmullRomSplineGroup> catmullromsplinegroups;
private int numcontrolpoints;
private ControlpointPointer selectedcontrolpoint;
private bool linesconnected;
private List<ConnectedLinedefs> connectedlinedefs;
private bool baseheightset;
private int oldflipping = -1;
private List<List<Vector2D>> exactsplines;
#endregion
#region ================== Structures
private struct CatmullRomSplineData
{
public Line2D line;
public List<Vector2D> controlpoints;
public List<Vector2D> tangents;
}
private struct CatmullRomSplineGroup
{
public CatmullRomSplineData[] splines;
public Linedef sourcelinedef;
}
private struct ControlpointPointer
{
public int crsg;
public int crsd;
public int cp;
}
private struct ConnectedLinedefs
{
public bool closed;
public int sector;
public List<Linedef> linedefs;
public Linedef firstlinedef;
}
private struct StairInfo
{
public int floorheight;
public int ceilingheight;
public List<List<Vector2D>> sectors;
public List<List<Vector2D>> postlines;
}
#endregion
#region ================== Properties
// Just keep the base mode button checked
public override string EditModeButtonName { get { return General.Editing.PreviousStableMode.Name; } }
#endregion
#region ================== Constructor / Disposer
#endregion
#region ================== Methods
private void InitializeCatmullRomSplines()
{
numcontrolpoints = stairsectorbuilderform.NumControlPoints;
if(General.Map.Map.GetSelectedLinedefs(true).Count <= 0) return;
if(catmullromsplinegroups == null)
catmullromsplinegroups = new List<CatmullRomSplineGroup>();
else
catmullromsplinegroups.Clear();
List<Linedef> sourceld = new List<Linedef>(General.Map.Map.GetSelectedLinedefs(true));
for(int l1 = 0; l1 < sourceld.Count - 1; l1++)
{
int l2 = l1 + 1;
Vector2D s1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].End.Position : sourceld[l1].Start.Position;
Vector2D e1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].Start.Position : sourceld[l1].End.Position;
Vector2D s2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].End.Position : sourceld[l2].Start.Position;
Vector2D e2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].Start.Position : sourceld[l2].End.Position;
//innerline = new Line2D(sourceld[l2].Start.Position, sourceld[l1].Start.Position);
//outerline = new Line2D(sourceld[l1].End.Position, sourceld[l2].End.Position);
Line2D innerline = new Line2D(s2, s1);
Line2D outerline = new Line2D(e1, e2);
CatmullRomSplineData innerspline = new CatmullRomSplineData();
innerspline.controlpoints = new List<Vector2D>();
innerspline.line = innerline;
innerspline.controlpoints.Add(new Vector2D(innerline.v1));
for(int k = 1; k <= numcontrolpoints - 2; k++)
innerspline.controlpoints.Add(new Vector2D(innerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k)));
innerspline.controlpoints.Add(new Vector2D(innerline.v2));
ComputeTangents(ref innerspline);
CatmullRomSplineData outerspline = new CatmullRomSplineData();
outerspline.controlpoints = new List<Vector2D>();
outerspline.line = outerline;
outerspline.controlpoints.Add(new Vector2D(outerline.v1));
for(int k = 1; k <= numcontrolpoints - 2; k++)
outerspline.controlpoints.Add(new Vector2D(outerline.GetCoordinatesAt(1.0f / (numcontrolpoints - 1) * k)));
outerspline.controlpoints.Add(new Vector2D(outerline.v2));
ComputeTangents(ref outerspline);
CatmullRomSplineGroup splinegroup = new CatmullRomSplineGroup();
splinegroup.splines = new CatmullRomSplineData[2];
splinegroup.splines[INNER_SPLINE] = innerspline;
splinegroup.splines[OUTER_SPLINE] = outerspline;
splinegroup.sourcelinedef = sourceld[l1];
catmullromsplinegroups.Add(splinegroup);
}
}
// Modifies the number of control points on the splines
public void ModifyControlpointNumber(int num)
{
if(catmullromsplinegroups == null) return;
for(int crsg=0; crsg < catmullromsplinegroups.Count;crsg++) {
for(int s = 0; s < 2; s++)
{
List<Vector2D> verts = GenerateCatmullRom(catmullromsplinegroups[crsg].splines[s], num-1);
catmullromsplinegroups[crsg].splines[s].controlpoints.Clear();
foreach(Vector2D v in verts)
{
catmullromsplinegroups[crsg].splines[s].controlpoints.Add(v);
}
// catmullromsplinegroups[crsg].splines[s].controlpoints.Add(catmullromsplinegroups[crsg].splines[s].line.v1);
// New tangents of the control points have to be calculated
ComputeTangents(ref catmullromsplinegroups[crsg].splines[s]);
}
}
}
// Create the sector data depending on the selected tab
public void UpdateVertexData()
{
// Straight stair
if(stairsectorbuilderform.Tabs.SelectedIndex == 0)
{
if(General.Map.Map.SelectedSectorsCount == 0)
{
stairsectors = CreateStraightStairSectorsFromLines(new List<Linedef>(General.Map.Map.GetSelectedLinedefs(true)));
}
else
{
stairsectors = CreateStraightStairSectorsFromSectors(new List<Sector>(General.Map.Map.GetSelectedSectors(true)));
}
}
// Curved stair
else if(stairsectorbuilderform.Tabs.SelectedIndex == 1)
{
stairsectors = CreateCurvedStairSectors();
}
// Catmull Rom stair
else if(stairsectorbuilderform.Tabs.SelectedIndex == 2)
{
if(oldflipping != stairsectorbuilderform.Flipping)
{
oldflipping = stairsectorbuilderform.Flipping;
InitializeCatmullRomSplines();
}
exactsplines = new List<List<Vector2D>>();
stairsectors = CreateCatmullRomStairSectors();
}
if(stairsectors.Count > 0)
{
stairsectorbuilderform.OriginalCeilingBase = stairsectors[0].ceilingheight;
stairsectorbuilderform.OriginalFloorBase = stairsectors[0].floorheight;
}
}
/*private bool CheckConnectedLines()
{
//List<Linedef> sourceld = new List<Linedef>();
//if(General.Map.Map.GetSelectedLinedefs(true).Count <= 0 && General.Map.Map.SelectedSectorsCount <= 0)
//{
// return false;
//}
// connectedlinedefs = new List<ConnectedLinedefs>();
//foreach(Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
//{
// sourceld.Add(ld);
//}
// GetConnectedLines(ref sourceld, -1);
return true;
}*/
private void GetConnectedLines(ref List<Linedef> sourceld, int sector)
{
List<Linedef> connectedld = new List<Linedef>();
Linedef currentld = null;
connectedlinedefs = new List<ConnectedLinedefs>();
while(sourceld.Count > 0)
{
// List is empty
if(connectedld.Count == 0)
{
connectedld.Add(sourceld[0]);
currentld = sourceld[0];
sourceld.RemoveAt(0);
}
// Find all connected linedefs starting from the start of the current linedef
while(currentld != null)
{
bool found = false;
foreach(Linedef ld in sourceld)
{
// if(currentld.Start.Linedefs.Contains(ld))
if(ld.Start == currentld.Start || ld.End == currentld.Start)
{
// add the connected linedef to the beginning of the list
connectedld.Insert(0, ld);
currentld = ld;
sourceld.Remove(ld);
found = true;
break;
}
}
if(found == false) currentld = null;
}
currentld = connectedld[connectedld.Count - 1];
// Find all connected linedefs starting from the end of the current linedef
while(currentld != null)
{
bool found = false;
foreach(Linedef ld in sourceld)
{
// if(currentld.End.Linedefs.Contains(ld))
if(ld.Start == currentld.End || ld.End == currentld.End)
{
// add the connected linedef to the end of the list
connectedld.Add(ld);
currentld = ld;
sourceld.Remove(ld);
found = true;
break;
}
}
if(found == false) currentld = null;
}
ConnectedLinedefs cld = new ConnectedLinedefs();
cld.linedefs = new List<Linedef>();
cld.sector = sector;
foreach(Linedef ld in connectedld)
{
cld.linedefs.Add(ld);
}
if(General.Map.Map.SelectedLinedefsCount > 0)
{
foreach(Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
{
if(cld.linedefs.Contains(ld))
{
cld.firstlinedef = ld;
break;
}
}
// if(connectedvertices[0] == cld.firstlinedef.End.Position && connectedvertices[connectedvertices.Count-1] == cld.firstlinedef.Start.Position) {
}
else
{
cld.closed = true;
cld.firstlinedef = cld.linedefs[cld.linedefs.Count-1];
}
connectedlinedefs.Add(cld);
connectedld.Clear();
}
}
// Computes the tangents of the control points of a Catmull Rom spline
private void ComputeTangents(ref CatmullRomSplineData spline)
{
float f = 2.0f;
int i;
if(spline.tangents == null)
spline.tangents = new List<Vector2D>();
else
spline.tangents.Clear();
// Special case for the first control point
// spline.tangents.Add(new Vector2D((spline.controlpoints[1] - spline.controlpoints[0]) / f));
spline.tangents.Add(new Vector2D(spline.controlpoints[1] - spline.controlpoints[0]));
for(i = 1; i < spline.controlpoints.Count - 1; i++)
{
spline.tangents.Add(new Vector2D((spline.controlpoints[i + 1] - spline.controlpoints[i - 1]) / f));
}
// Special case for the last control point
//spline.tangents.Add(new Vector2D((spline.controlpoints[i] - spline.controlpoints[i - 1]) / f));
spline.tangents.Add(new Vector2D(spline.controlpoints[i] - spline.controlpoints[i - 1]));
}
// Creates sectors for a curved stair
private List<StairInfo> CreateCurvedStairSectors()
{
List<List<Vector2D>> secs = new List<List<Vector2D>>();
int innervertexmultiplier = stairsectorbuilderform.InnerVertexMultiplier;
int outervertexmultiplier = stairsectorbuilderform.OuterVertexMultiplier;
int numsteps = (int)stairsectorbuilderform.NumberOfSectors;
List<Linedef> sourceld = new List<Linedef>();
List<StairInfo> stairinfo = new List<StairInfo>();
StairInfo si;
List<List<Vector2D>> sisecs = new List<List<Vector2D>>();
secs.Clear();
if(General.Map.Map.GetSelectedLinedefs(true).Count <= 0)
{
return null;
}
// Add all selected linedefs to a source linedef list
foreach(Linedef ld in General.Map.Map.GetSelectedLinedefs(true))
{
sourceld.Add(ld);
}
// Go through all source linedefs
for(int l1 = 0; l1 < sourceld.Count - 1; l1++)
{
// A curve will always be made in the order the lines were selected
int l2 = l1 + 1;
float distance = 128;
bool fixedcurve = true;
Vector2D s1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].End.Position : sourceld[l1].Start.Position;
Vector2D e1 = stairsectorbuilderform.Flipping == 1 ? sourceld[l1].Start.Position : sourceld[l1].End.Position;
Vector2D s2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].End.Position : sourceld[l2].Start.Position;
Vector2D e2 = stairsectorbuilderform.Flipping == 2 ? sourceld[l2].Start.Position : sourceld[l2].End.Position;
Line2D innerline, outerline;
bool clockwise;
if(Vector2D.Distance(s1, s2) < Vector2D.Distance(e1, e2))
{
clockwise = true;
innerline = new Line2D(s2, s1);
outerline = new Line2D(e1, e2);
}
else // Counter clockwise
{
clockwise = false;
innerline = new Line2D(e1, e2);
outerline = new Line2D(s2, s1);
}
// Compute the angle of the inner and outer line
double innerangle, outerangle;
if(sourceld[l1].Angle == sourceld[l2].Angle && stairsectorbuilderform.Flipping != 1 && stairsectorbuilderform.Flipping != 2)
{
innerangle = 1;
outerangle = 1;
distance = 0;
fixedcurve = false;
}
else
{
if(clockwise)
{
innerangle = outerangle = sourceld[l1].Angle - sourceld[l2].Angle;
if(innerangle < 0.0f) innerangle += Angle2D.PI2;
if(outerangle < 0.0f) outerangle += Angle2D.PI2;
}
else
{
innerangle = outerangle = sourceld[l2].Angle - sourceld[l1].Angle;
if(innerangle < 0.0f) innerangle += Angle2D.PI2;
if(outerangle < 0.0f) outerangle += Angle2D.PI2;
}
if(stairsectorbuilderform.Flipping != 0)
{
if(sourceld[l1].Angle == sourceld[l2].Angle)
{
if(stairsectorbuilderform.Flipping == 1)
{
innerangle = Math.Abs(innerangle - Angle2D.PI);
outerangle = Math.Abs(outerangle - Angle2D.PI);
}
else if(stairsectorbuilderform.Flipping == 2)
{
innerangle -= Angle2D.PI;
outerangle -= Angle2D.PI;
}
}
else
{
innerangle = Math.Abs(innerangle - Angle2D.PI2);
outerangle = Math.Abs(outerangle - Angle2D.PI2);
}
}
}
// Generate the vertices on the curve, and add the start and end vertices of the inner line to the list
List<Vector2D> innervertices = GenerateCurve(innerline, numsteps * innervertexmultiplier - 1, innerangle, false, distance, fixedcurve);
innervertices.Insert(0, innerline.v1);
innervertices.Add(innerline.v2);
// Generate the vertices on the curve, and add the start and end vertices of the outer line to the list
List<Vector2D> outervertices = GenerateCurve(outerline, numsteps * outervertexmultiplier - 1, outerangle, true, distance, fixedcurve);
outervertices.Insert(0, outerline.v1);
outervertices.Add(outerline.v2);
// If the vertices were created in counter-clockwise order turn them into clockwise order
if(!clockwise)
{
List<Vector2D> tmpvertices = innervertices;
int tmpmultiplier = innervertexmultiplier;
innervertices = outervertices;
outervertices = tmpvertices;
innervertexmultiplier = outervertexmultiplier;
outervertexmultiplier = tmpmultiplier;
}
// Create the sectors from the created vertices
for(int i = 0; i < numsteps; i++)
{
List<Vector2D> newsec = new List<Vector2D>();
// Depending on the outer and innter vertex multipliers more
// vertices from the curve have to be added to the new sector
for(int k = 0; k <= outervertexmultiplier; k++)
{
newsec.Add(outervertices[i * outervertexmultiplier + k]);
}
// Depending on the outer and innter vertex multipliers more
// vertices from the curve have to be added to the new sector
for(int k = 0; k <= innervertexmultiplier; k++)
{
newsec.Add(innervertices[(numsteps - 1 - i) * innervertexmultiplier + k]);
}
// Don't forget the add the first vertex again, to actually have
// a whole sector
newsec.Add(outervertices[i * outervertexmultiplier]);
sisecs.Add(newsec);
}
}
GetSetTextureAndHeightInfo(sourceld[0], out si);
si.sectors = sisecs;
stairinfo.Add(si);
stairsectorbuilderform.StepMultiplier = sourceld.Count - 1;
return stairinfo;
}
/*private List<Vector2D> GenerateCurve(Line2D line, int vertices, float angle, bool backwards)
{
return GenerateCurve(line, vertices, angle, backwards, 128, true);
}*/
// This generates the vertices to split the line with, from start to end
private static List<Vector2D> GenerateCurve(Line2D line, int vertices, double angle, bool backwards, double distance, bool fixedcurve)
{
// Make list
List<Vector2D> points = new List<Vector2D>(vertices);
//Added by Anders Ã…strand 2008-05-18
//The formulas used are taken from http://mathworld.wolfram.com/CircularSegment.html
//c and theta are known (length of line and angle parameter). d, R and h are
//calculated from those two
//If the curve is not supposed to be a circular segment it's simply deformed to fit
//the value set for distance.
//The vertices are generated to be evenly distributed (by angle) along the curve
//and lastly they are rotated and moved to fit with the original line
//calculate some identities of a circle segment (refer to the graph in the url above)
double c = line.GetLength();
double theta = angle;
double d = (c / Math.Tan(theta / 2)) / 2;
double R = d / Math.Cos(theta / 2);
double h = R - d;
double yDeform = fixedcurve ? 1 : distance / h;
if(backwards) yDeform = -yDeform;
for(int v = 1; v <= vertices; v++)
{
//calculate the angle for this vertex
//the curve starts at PI/2 - theta/2 and is segmented into vertices+1 segments
//this assumes the line is horisontal and on y = 0, the point is rotated and moved later
double a = (Math.PI - theta) / 2 + v * (theta / (vertices + 1));
//calculate the coordinates of the point, and distort the y coordinate
//using the deform factor calculated above
double x = Math.Cos(a) * R;
double y = (Math.Sin(a) * R - d) * yDeform;
//rotate and transform to fit original line
Vector2D vertex = new Vector2D((float)x, (float)y).GetRotated(line.GetAngle() + Angle2D.PIHALF);
vertex = vertex.GetTransformed(line.GetCoordinatesAt(0.5f).x, line.GetCoordinatesAt(0.5f).y, 1, 1);
points.Add(vertex);
}
// Done
return points;
}
// Creates stair sectors from two Catmull Rom splines
private List<StairInfo> CreateCatmullRomStairSectors()
{
List<List<Vector2D>> secs = new List<List<Vector2D>>();
int innervertexmultiplier = stairsectorbuilderform.InnerVertexMultiplier;
int outervertexmultiplier = stairsectorbuilderform.OuterVertexMultiplier;
int numsteps = (int)stairsectorbuilderform.NumberOfSectors;
List<List<Vector2D>> sisecs = new List<List<Vector2D>>();
List<StairInfo> stairinfo = new List<StairInfo>();
StairInfo si;
secs.Clear();
foreach(CatmullRomSplineGroup crsg in catmullromsplinegroups)
{
// Generate the vertices for both the inner and outer spline
List<Vector2D> innervertices = GenerateCatmullRom(crsg.splines[INNER_SPLINE], numsteps * innervertexmultiplier);
List<Vector2D> outervertices = GenerateCatmullRom(crsg.splines[OUTER_SPLINE], numsteps * outervertexmultiplier);
// Create the data to build complete sectors from the splines
for(int i = 0; i < numsteps; i++)
{
List<Vector2D> newsec = new List<Vector2D>();
// Depending on the outer and innter vertex multipliers more
// vertices from the splines have to be added to the new sector
for(int k = 0; k <= outervertexmultiplier; k++)
{
newsec.Add(outervertices[i * outervertexmultiplier + k]);
}
for(int k = 0; k <= innervertexmultiplier; k++)
{
newsec.Add(innervertices[(numsteps - 1 - i) * innervertexmultiplier + k]);
}
// Don't forget the add the first vertex again, to actually have
// a whole sector
newsec.Add(outervertices[i * outervertexmultiplier]);
sisecs.Add(newsec);
}
}
GetSetTextureAndHeightInfo(catmullromsplinegroups[0].sourcelinedef, out si);
si.sectors = sisecs;
stairinfo.Add(si);
return stairinfo;
}
private List<StairInfo> CreateStraightStairSectorsFromSectors(List<Sector> selectedsectors)
{
List<StairInfo> stairinfo = new List<StairInfo>();
foreach(Sector s in selectedsectors)
{
List<Linedef> linedefs = new List<Linedef>();
List<Linedef> fliplinedefs = new List<Linedef>();
foreach(Sidedef sd in s.Sidedefs)
{
if(sd.Line.Front.Sector.Index != s.Index)
{
fliplinedefs.Add(sd.Line);
sd.Line.FlipVertices();
sd.Line.FlipSidedefs();
}
linedefs.Add(sd.Line);
}
stairinfo.AddRange(CreateStraightStairSectorsFromLines(linedefs));
foreach(Linedef ld in fliplinedefs)
{
ld.FlipSidedefs();
ld.FlipVertices();
}
}
return stairinfo;
}
private List<StairInfo> CreateStraightStairSectorsFromLines(List<Linedef> selectedlinedefs)
{
List<StairInfo> stairinfo = new List<StairInfo>();
uint numsteps = stairsectorbuilderform.NumberOfSectors;
uint depth = stairsectorbuilderform.SectorDepth;
int spacing = stairsectorbuilderform.Spacing;
List<Linedef> sourceld = new List<Linedef>(selectedlinedefs);
StairInfo si;
GetConnectedLines(ref selectedlinedefs, -1);
// Build an independend set of steps from each selected line
if(!stairsectorbuilderform.SingleSteps.Checked)
{
// Go through each selected line
for(int k = 0; k < sourceld.Count; k++)
{
List<List<Vector2D>> sisecs = new List<List<Vector2D>>();
// Get the direction to build the stair to. "Flip" the vector depending on the selected build direction modifier
Vector2D direction = sourceld[k].Line.GetPerpendicular().GetNormal() * (stairsectorbuilderform.SideFront ? -1 : 1);
for(int i = 0; i < numsteps; i++)
{
//List<DrawnVertex> vertices = new List<DrawnVertex>();
List<Vector2D> newsec = new List<Vector2D>();
// Compute the position of the vertices that form the line opposite the source line
Vector2D v1 = sourceld[k].Start.Position + direction * depth * i;
Vector2D v2 = sourceld[k].End.Position + direction * depth * i;
Vector2D v3 = v1 + direction * depth;
Vector2D v4 = v2 + direction * depth;
// Apply spacing to each vertex
if(spacing > 0)
{
v1 += (direction * spacing) * i;
v2 += (direction * spacing) * i;
v3 += (direction * spacing) * i;
v4 += (direction * spacing) * i;
}
// v1 is added twice so it will actually draw a complete sector
newsec.Add(v1);
newsec.Add(v3);
newsec.Add(v4);
newsec.Add(v2);
newsec.Add(v1);
sisecs.Add(newsec);
}
GetSetTextureAndHeightInfo(sourceld[k], out si);
si.sectors = sisecs;
stairinfo.Add(si);
}
}
else if(stairsectorbuilderform.SingleSteps.Checked)
{
Vector2D direction = new Vector2D();
bool closed = false;
foreach(ConnectedLinedefs cld in connectedlinedefs)
{
List<Vector2D> connectedvertices = new List<Vector2D>();
List<Vector2D> connecteddirections = new List<Vector2D>();
List<double> connectedlength = new List<double>();
List<List<Vector2D>> sisecs = new List<List<Vector2D>>();
List<List<Vector2D>> sipostlines = new List<List<Vector2D>>();
Vector2D globaldirection = Vector2D.FromAngle(cld.firstlinedef.Angle + Angle2D.DegToRad(stairsectorbuilderform.SideFront ? -90.0f : 90.0f));
for(int i = 0; i < cld.linedefs.Count; i++)
{
if(cld.sector >= 0)
{
if(cld.linedefs[i].Front.Sector.Index == cld.sector)
{
if(!connectedvertices.Contains(cld.linedefs[i].End.Position))
connectedvertices.Add(cld.linedefs[i].End.Position);
if(!connectedvertices.Contains(cld.linedefs[i].Start.Position))
connectedvertices.Add(cld.linedefs[i].Start.Position);
}
else
{
if(!connectedvertices.Contains(cld.linedefs[i].Start.Position))
connectedvertices.Add(cld.linedefs[i].Start.Position);
if(!connectedvertices.Contains(cld.linedefs[i].End.Position))
connectedvertices.Add(cld.linedefs[i].End.Position);
}
}
else
{
if(!connectedvertices.Contains(cld.linedefs[i].Start.Position))
connectedvertices.Add(cld.linedefs[i].Start.Position);
if(!connectedvertices.Contains(cld.linedefs[i].End.Position))
connectedvertices.Add(cld.linedefs[i].End.Position);
}
}
if(cld.sector >= 0)
{
closed = true;
}
else
{
if(connectedvertices[0] == cld.firstlinedef.End.Position && connectedvertices[connectedvertices.Count - 1] == cld.firstlinedef.Start.Position)
closed = true;
}
for(int i = 0; i < connectedvertices.Count; i++)
{
if(i == 0 && closed == false)
{
connecteddirections.Add(Vector2D.FromAngle(Vector2D.GetAngle(connectedvertices[0], connectedvertices[1]) - Angle2D.DegToRad(stairsectorbuilderform.SideFront ? -90.0f : 90.0f)));
connectedlength.Add(depth);
}
else if(i == connectedvertices.Count - 1 && closed == false)
{
connecteddirections.Add(Vector2D.FromAngle(Vector2D.GetAngle(connectedvertices[i], connectedvertices[i - 1]) + Angle2D.DegToRad(stairsectorbuilderform.SideFront ? -90.0f : 90.0f)));
connectedlength.Add(depth);
}
else
{
Vector2D v1;
Vector2D v2;
if(closed && i == 0)
{
v1 = new Line2D(connectedvertices[1], connectedvertices[0]).GetPerpendicular();
v2 = new Line2D(connectedvertices[0], connectedvertices[connectedvertices.Count - 1]).GetPerpendicular();
}
else if(closed && i == connectedvertices.Count - 1)
{
v1 = new Line2D(connectedvertices[0], connectedvertices[i]).GetPerpendicular();
v2 = new Line2D(connectedvertices[i], connectedvertices[i - 1]).GetPerpendicular();
}
else
{
v1 = new Line2D(connectedvertices[i + 1], connectedvertices[i]).GetPerpendicular();
v2 = new Line2D(connectedvertices[i], connectedvertices[i - 1]).GetPerpendicular();
}
double a = (v1.GetNormal() + v2.GetNormal()).GetAngle();
double b = a - v1.GetNormal().GetAngle();
double opl = Math.Tan(b) * depth;
double distance = Math.Sqrt(depth * depth + opl * opl);
Vector2D v3 = Vector2D.FromAngle((float)a);
connecteddirections.Add(Vector2D.FromAngle(v3.GetAngle() + Angle2D.DegToRad(stairsectorbuilderform.SideFront ? 0.0f : 180.0f)));
connectedlength.Add(distance);
}
}
for(int i = 0; i < numsteps; i++)
{
List<Vector2D> newsec = new List<Vector2D>();
float length;
if(closed == false)
{
for(int k = 0; k < connectedvertices.Count; k++)
{
if(!stairsectorbuilderform.SingleDirection.Checked)
{
direction = connecteddirections[k];
length = (float)connectedlength[k];
}
else
{
direction = globaldirection;
length = depth;
}
newsec.Add(connectedvertices[k] + direction * length * i + (direction * spacing) * i);
}
}
for(int k = connectedvertices.Count - 1; k >= 0; k--)
{
if(!stairsectorbuilderform.SingleDirection.Checked)
{
direction = connecteddirections[k];
length = (float)connectedlength[k];
}
else
{
direction = globaldirection;
length = depth;
}
newsec.Add(connectedvertices[k] + direction * length * (i + 1) + (direction * spacing) * i);
// Add lines to draw after the stair has been created, if desired
if (stairsectorbuilderform.DistinctSectors.Checked)
{
// Skip last and first vertex if vertices are not in a loop, the line would
// just draw over existing lines anyway
if (closed || (!closed && (k != connectedvertices.Count - 1 && k != 0)))
{
sipostlines.Add(new List<Vector2D>() {
connectedvertices[k] + direction * length * i + (direction * spacing) * i,
connectedvertices[k] + direction * length * (i + 1) + (direction * spacing) * i
});
}
}
}
if(closed == false)
newsec.Add(connectedvertices[0] + direction * depth * i + (direction * spacing) * i);
else
newsec.Add(newsec[0]);
// If the steps are drawn on the back side the vertices are in counter-clockwise
// order, so reverse their order
if(!stairsectorbuilderform.SideFront) newsec.Reverse();
sisecs.Add(newsec);
}
GetSetTextureAndHeightInfo(cld.firstlinedef, out si);
si.sectors = sisecs;
si.postlines = sipostlines;
stairinfo.Add(si);
}
}
stairsectorbuilderform.StepMultiplier = 1;
return stairinfo;
}
// Generats the Catmull Rom spline from the given control points and their tangents.
// Returns a list of Vector2D.
// The line between two control points is a "section". There is always one section
// less than there are control points.
// A "hop" is the distance between two to-be-created vertices. The distance is not
// in map units, but a relative indication where on the line between two control
// points the vertex will be.
// First it is calculated in which section the vertex will be in, then the relative
// position on the line between the control points of this section is calculated.
// This value will be from 0.0 to 0.99999...
// A workaround has to be done for the last vertex, which will inevitably be exactly
// on the last control point, thus computing the wrong section and relative position
private List<Vector2D> GenerateCatmullRom(CatmullRomSplineData crsd, int numverts)
{
List<Vector2D> vertices = new List<Vector2D>();
//int sections = crsd.controlpoints.Count - 1;
//double hop = (double)sections / numverts;
double distance = 0.0f;
List<double> cpdistance = new List<double>();
// Compute the length of the whole spline and the length of the parts on the
// spline between the control points
for(int i = 0; i < crsd.controlpoints.Count-1; i++)
{
int h = (int)Vector2D.Distance(crsd.controlpoints[i], crsd.controlpoints[i + 1]);
double dist = 0;
List<Vector2D> dc = new List<Vector2D>();
Vector2D p0 = crsd.controlpoints[i];
Vector2D p1 = crsd.controlpoints[i + 1];
Vector2D t0 = crsd.tangents[i];
Vector2D t1 = crsd.tangents[i + 1];
for(int k = 0; k <= h; k++)
{
dc.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)k / h));
}
for(int k = 0; k < h; k++)
{
dist += Vector2D.Distance(dc[k], dc[k + 1]);
}
distance += dist;
cpdistance.Add(dist);
exactsplines.Add(dc);
}
double unithop = distance / numverts;
for(int i = 0; i <= numverts; i++)
{
int s = 0;
double u;
double distancefromstart = i * unithop;
double max = 0.0f;
// Find out what section the vertex is in
while(max < distancefromstart)
{
max += cpdistance[s];
if(max < distancefromstart) s++;
// Work around for rounding errors
if(s > cpdistance.Count-1)
{
s = cpdistance.Count - 1;
max = distancefromstart;
}
}
// Compute the u component with special cases for the first
// and last vertex
if(distancefromstart == 0)
u = 0.0f;
else if(distancefromstart == distance)
u = 1.0f;
else
u = 1.0f - ((max - distancefromstart) / cpdistance[s]);
Vector2D p0 = crsd.controlpoints[s];
Vector2D p1 = crsd.controlpoints[s + 1];
Vector2D t0 = crsd.tangents[s];
Vector2D t1 = crsd.tangents[s + 1];
vertices.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)u));
}
// OLD CODE
//for(int x = 0; x <= numverts; x++)
//{
// // Compute the section the vertex will be in
// int s = (int)(x * hop);
// double u;
// // Workaround for the last vertex
// if((x * hop) - s < 0.00001f && s == sections)
// {
// u = 1.0f;
// s--;
// }
// else
// {
// //u = (summe - x * unithop) / Vector2D.Distance(crsd.controlpoints[cs], crsd.controlpoints[cs+1]);
// u = (x * hop) - s;
// }
// if(u > 1.0f) u = 1.0f;
// Vector2D p0 = crsd.controlpoints[s];
// Vector2D p1 = crsd.controlpoints[s + 1];
// Vector2D t0 = crsd.tangents[s];
// Vector2D t1 = crsd.tangents[s + 1];
// vertices.Add(Tools.HermiteSpline(p0, t0, p1, t1, (float)u));
//}
return vertices;
}
// Turns a position into a DrawnVertex and returns it
private DrawnVertex SectorVertex(double x, double y)
{
DrawnVertex v = new DrawnVertex();
v.stitch = true;
v.stitchline = true;
v.pos = new Vector2D(Math.Round(x, General.Map.FormatInterface.VertexDecimals), Math.Round(y, General.Map.FormatInterface.VertexDecimals));
return v;
}
private DrawnVertex SectorVertex(Vector2D v)
{
return SectorVertex(v.x, v.y);
}
// Remember info for floor and ceiling height. The info is retrieved
// from either front or back of the source line, depending in what
// direction the it should build
// Also set the upper/lower textures to use if they aren't already
private void GetSetTextureAndHeightInfo(Linedef ld, out StairInfo siout)
{
StairInfo si = new StairInfo();
Sidedef primary;
Sidedef secondary;
GetSetBaseHeights(ld);
if(stairsectorbuilderform.SideFront)
{
if(ld.Front == null)
{
primary = ld.Back;
secondary = ld.Front;
}
else
{
primary = ld.Front;
secondary = ld.Back;
}
}
else
{
if(ld.Back == null)
{
primary = ld.Front;
secondary = ld.Back;
}
else
{
primary = ld.Back;
secondary = ld.Front;
}
}
if (primary == null || primary.IsDisposed)
{
si.ceilingheight = General.Settings.DefaultCeilingHeight;
si.floorheight = General.Settings.DefaultFloorHeight;
}
else
{
si.ceilingheight = primary.Sector.CeilHeight;
si.floorheight = primary.Sector.FloorHeight;
}
if (stairsectorbuilderform.UpperTextureTexture == "")
{
if ((primary == null || primary.IsDisposed) && (secondary == null || secondary.IsDisposed))
stairsectorbuilderform.UpperTextureTexture = General.Settings.DefaultTexture;
else if (secondary == null || secondary.IsDisposed)
stairsectorbuilderform.UpperTextureTexture = primary.MiddleTexture;
else
stairsectorbuilderform.UpperTextureTexture = secondary.HighTexture;
}
if(stairsectorbuilderform.LowerTextureTexture == "")
{
if ((primary == null || primary.IsDisposed) && (secondary == null || secondary.IsDisposed))
stairsectorbuilderform.LowerTextureTexture = General.Settings.DefaultTexture;
else if (secondary == null || secondary.IsDisposed)
stairsectorbuilderform.LowerTextureTexture = primary.MiddleTexture;
else
stairsectorbuilderform.LowerTextureTexture = secondary.LowTexture;
}
siout = si;
}
/// <summary>
/// Get the base heights from a linedef. If there are no sidedefs it uses the default values.
/// </summary>
/// <param name="ld">Linedef to the the base heights from</param>
private void GetSetBaseHeights(Linedef ld)
{
if(baseheightset) return;
baseheightset = true;
if(stairsectorbuilderform.SideFront)
{
if(ld.Back == null)
{
if (ld.Front != null && !ld.Front.IsDisposed && ld.Front.Sector != null && !ld.Front.Sector.IsDisposed)
{
stairsectorbuilderform.CeilingBase = ld.Front.Sector.CeilHeight;
stairsectorbuilderform.FloorBase = ld.Front.Sector.FloorHeight;
}
else
{
stairsectorbuilderform.CeilingBase = General.Settings.DefaultCeilingHeight;
stairsectorbuilderform.FloorBase = General.Settings.DefaultFloorHeight;
}
}
else
{
if (ld.Back != null && !ld.Back.IsDisposed && ld.Back.Sector != null && !ld.Back.Sector.IsDisposed)
{
stairsectorbuilderform.CeilingBase = ld.Back.Sector.CeilHeight;
stairsectorbuilderform.FloorBase = ld.Back.Sector.FloorHeight;
}
else
{
stairsectorbuilderform.CeilingBase = General.Settings.DefaultCeilingHeight;
stairsectorbuilderform.FloorBase = General.Settings.DefaultFloorHeight;
}
}
}
else
{
if (ld.Front != null && !ld.Front.IsDisposed && ld.Front.Sector != null && !ld.Front.Sector.IsDisposed)
{
stairsectorbuilderform.CeilingBase = ld.Front.Sector.CeilHeight;
stairsectorbuilderform.FloorBase = ld.Front.Sector.FloorHeight;
}
else
{
stairsectorbuilderform.CeilingBase = General.Settings.DefaultCeilingHeight;
stairsectorbuilderform.FloorBase = General.Settings.DefaultFloorHeight;
}
}
}
#endregion
#region ================== Events
public override void OnHelp()
{
General.ShowHelp("e_curvelinedefs.html");
}
// Cancelled
public override void OnCancel()
{
// Cancel base class
base.OnCancel();
// stairsectorbuilderform.Close();
// Return to base mode
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
}
// Mode engages
public override void OnEngage()
{
base.OnEngage();
// If no lines are selected nothing can be done, so exit this mode immediately
if(General.Map.Map.SelectedLinedefsCount == 0 && General.Map.Map.SelectedSectorsCount == 0)
{
General.ToastManager.ShowToast(ToastMessages.ENGAGEFAILED, ToastType.ERROR, "Failed to start Stair Sector Builder Mode", "You need to select at least one linedef or sector to enter Stair Sector Builder Mode");
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
return;
}
renderer.SetPresentation(Presentation.Standard);
linesconnected = true; //CheckConnectedLines();
LoadPrefabs();
// stairsectorbuilderform = BuilderPlug.Me.StairSectorBuilderForm;
stairsectorbuilderform = new StairSectorBuilderForm();
InitializeCatmullRomSplines();
selectedcontrolpoint.cp = -1;
selectedcontrolpoint.crsd = -1;
selectedcontrolpoint.crsg = -1;
stairsectorbuilderform.SingleSteps.Enabled = linesconnected;
stairsectorbuilderform.DistinctBaseHeights.Checked = (General.Map.Map.SelectedLinedefsCount != 1);
// Show toolbox window
// stairsectorbuilderform.Show();
stairsectorbuilderform.Show((Form)General.Interface);
}
// Disenagaging
public override void OnDisengage()
{
base.OnDisengage();
SavePrefabs();
// Sort of work around so that the DB2 window won't lose focus
General.Interface.Focus();
if(stairsectorbuilderform != null)
stairsectorbuilderform.Hide();
}
// This applies the curves and returns to the base mode
public override void OnAccept()
{
string uppertexture = stairsectorbuilderform.UpperTextureTexture;
string lowertexture = stairsectorbuilderform.LowerTextureTexture;
string middletexture = stairsectorbuilderform.MiddleTextureTexture;
string olddefaulttexture = "-";
List<Vertex> newvertices = new List<Vertex>();
List<Linedef> newlinedefs = new List<Linedef>();
List<Sector> allnewsectors = new List<Sector>();
if(stairsectors == null) return;
// Create undo
General.Map.UndoRedo.CreateUndo("Build stair sectors");
// Get the default settings and remember the default texture
General.Settings.FindDefaultDrawSettings();
if(stairsectorbuilderform.MiddleTexture)
{
olddefaulttexture = General.Map.Options.DefaultWallTexture;
General.Map.Options.DefaultWallTexture = "-";
}
// This creates the geometry. Uses exactly the same data used
// for the preview
try
{
foreach(StairInfo si in stairsectors)
{
int stepcounter = 1;
foreach(List<Vector2D> lv in si.sectors)
{
List<Sector> oldsectors = new List<Sector>(General.Map.Map.Sectors);
//List<Sector> newsectors = new List<Sector>();
List<DrawnVertex> vertices = new List<DrawnVertex>();
for(int i = 0; i < lv.Count; i++)
{
vertices.Add(SectorVertex(lv[i]));
}
// Draw the new sector
if(!Tools.DrawLines(vertices)) throw new Exception("Failed drawing lines");
// Update newly created sector(s) with the given textures and heights
foreach(Sector s in General.Map.Map.Sectors)
{
if(!oldsectors.Contains(s))
{
allnewsectors.Add(s);
// Apply floor heights if necessary
if(stairsectorbuilderform.FloorHeight)
{
// Should each stair use individual base height or the global one?
if(stairsectorbuilderform.DistinctBaseHeights.Checked && stairsectorbuilderform.StairType == 0)
s.FloorHeight = si.floorheight + stairsectorbuilderform.FloorHeightModification * stepcounter;
else
s.FloorHeight = stairsectorbuilderform.FloorBase + stairsectorbuilderform.FloorHeightModification * stepcounter;
}
// Apply ceiling heights if necessary
if(stairsectorbuilderform.CeilingHeight)
{
// Should each stair use individual base height or the global one?
if(stairsectorbuilderform.DistinctBaseHeights.Checked && stairsectorbuilderform.StairType == 0)
s.CeilHeight = si.ceilingheight + stairsectorbuilderform.CeilingHeightModification * stepcounter;
else
s.CeilHeight = stairsectorbuilderform.CeilingBase + stairsectorbuilderform.CeilingHeightModification * stepcounter;
}
// Apply floor and ceiling textures if necessary
if(stairsectorbuilderform.FloorFlat)
s.SetFloorTexture(stairsectorbuilderform.FloorFlatTexture);
if(stairsectorbuilderform.CeilingFlat)
s.SetCeilTexture(stairsectorbuilderform.CeilingFlatTexture);
// Fix missing textures
foreach(Sidedef sd in s.Sidedefs)
{
if(!newlinedefs.Contains(sd.Line)) newlinedefs.Add(sd.Line);
if(stairsectorbuilderform.UpperTexture)
{
if(sd.Line.Back != null && sd.Line.Back.HighRequired()) sd.Line.Back.SetTextureHigh(uppertexture);
if(sd.Line.Front != null && sd.Line.Front.HighRequired()) sd.Line.Front.SetTextureHigh(uppertexture);
if(stairsectorbuilderform.UpperUnpegged) sd.Line.SetFlag(General.Map.Config.UpperUnpeggedFlag, true);
}
if(stairsectorbuilderform.LowerTexture)
{
if(sd.Line.Front != null && sd.Line.Front.LowRequired()) sd.Line.Front.SetTextureLow(lowertexture);
if(sd.Line.Back != null && sd.Line.Back.LowRequired()) sd.Line.Back.SetTextureLow(lowertexture);
if(stairsectorbuilderform.LowerUnpegged) sd.Line.SetFlag(General.Map.Config.LowerUnpeggedFlag, true);
}
if(stairsectorbuilderform.MiddleTexture)
{
if(sd.Line.Front != null && sd.Line.Front.MiddleRequired()) sd.Line.Front.SetTextureMid(middletexture);
if(sd.Line.Back != null && sd.Line.Back.MiddleRequired()) sd.Line.Back.SetTextureMid(middletexture);
}
}
}
}
stepcounter++;
}
}
// Draw post lines
foreach (StairInfo si in stairsectors)
{
if (si.postlines != null)
{
foreach (List<Vector2D> lv in si.postlines)
{
List<Sector> oldsectors = new List<Sector>(General.Map.Map.Sectors);
List<DrawnVertex> vertices = new List<DrawnVertex>();
for (int i = 0; i < lv.Count; i++)
vertices.Add(SectorVertex(lv[i]));
// Draw the new sector
if (!Tools.DrawLines(vertices)) throw new Exception("Failed drawing lines");
// Find new sectors
foreach (Sector s in General.Map.Map.Sectors)
if (!oldsectors.Contains(s))
allnewsectors.Add(s);
}
}
}
if (stairsectorbuilderform.MiddleTexture)
General.Map.Options.DefaultWallTexture = olddefaulttexture;
// Snap to map format accuracy
General.Map.Map.SnapAllToAccuracy();
// Clean the map up. Merge all vertices that share the same position and
// remove looped linedefs
foreach(Sector s in allnewsectors)
{
if(s.Sidedefs == null) continue;
foreach(Sidedef sd in s.Sidedefs)
{
if(!newvertices.Contains(sd.Line.Start)) newvertices.Add(sd.Line.Start);
if(!newvertices.Contains(sd.Line.End)) newvertices.Add(sd.Line.End);
}
}
General.Map.Map.BeginAddRemove();
MapSet.JoinVertices(newvertices, newvertices, false, MapSet.STITCH_DISTANCE);
General.Map.Map.EndAddRemove();
MapSet.RemoveLoopedLinedefs(newlinedefs);
// Update textures
General.Map.Data.UpdateUsedTextures();
// Update caches
General.Map.Map.Update();
General.Map.IsChanged = true;
}
catch(Exception e)
{
General.Interface.DisplayStatus(StatusType.Warning, e.ToString());
General.Map.UndoRedo.WithdrawUndo();
}
// Return to base mode
General.Editing.ChangeMode(General.Editing.PreviousStableMode.Name);
}
// Redrawing display
public override void OnRedrawDisplay()
{
if(!stairsectorbuilderform.FullyLoaded) return;
base.OnRedrawDisplay();
renderer.RedrawSurface();
// Render lines
if(renderer.StartPlotter(true))
{
renderer.PlotLinedefSet(General.Map.Map.Linedefs);
renderer.PlotVerticesSet(General.Map.Map.Vertices);
renderer.Finish();
}
// Render things
if(renderer.StartThings(true))
{
renderer.RenderThingSet(General.Map.Map.Things, General.Settings.ActiveThingsAlpha);
renderer.RenderSRB2Extras();
renderer.Finish();
}
// Render overlay
if(renderer.StartOverlay(true))
{
if(stairsectorbuilderform.Tabs.SelectedIndex == 2 && numcontrolpoints != stairsectorbuilderform.NumControlPoints)
{
ModifyControlpointNumber(stairsectorbuilderform.NumControlPoints);
numcontrolpoints = stairsectorbuilderform.NumControlPoints;
}
// Create sector info based on the settings...
UpdateVertexData();
// Render the control points if the Catmull Rom spline tab is selected
if(stairsectorbuilderform.Tabs.SelectedIndex == 2)
{
foreach(List<Vector2D> lv in exactsplines)
{
for(int i = 0; i < lv.Count - 1; i++)
{
renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, new PixelColor(255, 128, 128, 128), true);
}
}
foreach(CatmullRomSplineGroup crsg in catmullromsplinegroups)
{
CatmullRomSplineData[] crsd = new CatmullRomSplineData[2] { crsg.splines[INNER_SPLINE], crsg.splines[OUTER_SPLINE] };
foreach(CatmullRomSplineData c in crsd)
{
for(int i = 0; i < c.controlpoints.Count; i++)
{
RectangleF rect = new RectangleF((float)(c.controlpoints[i].x - CONTROLPOINT_SIZE / 2), (float)(c.controlpoints[i].y - CONTROLPOINT_SIZE / 2), 10.0f, 10.0f);
if(i == 0) renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true);
else if(i == c.controlpoints.Count -1) renderer.RenderRectangle(rect, 2, new PixelColor(128, 0, 255, 0), true);
else renderer.RenderRectangle(rect, 2, General.Colors.Indication, true);
//renderer.RenderLine(c.controlpoints[i], c.controlpoints[i] + c.tangents[i], LINE_THICKNESS, new PixelColor(255, 128, 128, 128), true);
}
}
}
}
// ... and draw the preview
foreach(StairInfo si in stairsectors)
{
foreach(List<Vector2D> lv in si.sectors)
{
for(int i = 0; i < lv.Count - 1; i++)
{
renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, General.Colors.Highlight, true);
RectangleF rect = new RectangleF((float)(lv[i].x - CONTROLPOINT_SIZE / 2), (float)(lv[i].y - CONTROLPOINT_SIZE / 2), 10.0f, 10.0f);
renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true);
}
}
if (si.postlines != null)
{
foreach (List<Vector2D> lv in si.postlines)
{
for (int i = 0; i < lv.Count - 1; i++)
{
renderer.RenderLine(lv[i], lv[i + 1], LINE_THICKNESS, General.Colors.Highlight, true);
//RectangleF rect = new RectangleF(lv[i].x - CONTROLPOINT_SIZE / 2, lv[i].y - CONTROLPOINT_SIZE / 2, 10.0f, 10.0f);
//renderer.RenderRectangle(rect, 2, new PixelColor(128, 255, 0, 0), true);
}
}
}
}
renderer.Finish();
}
renderer.Present();
}
protected override void OnSelectBegin()
{
base.OnSelectBegin();
if(stairsectorbuilderform.Tabs.SelectedIndex != 2) return;
// Go through all Catmull Rom Spline Groups
for(int crsg = 0; crsg < catmullromsplinegroups.Count; crsg++)
{
// Check inner and outer splines
for(int crsd = 0; crsd < 2; crsd++)
{
// Check all control points except the first and last, they belong to the linedefs
// and may not be moved
for(int cp = 1; cp < catmullromsplinegroups[crsg].splines[crsd].controlpoints.Count-1; cp++)
{
Vector2D p = catmullromsplinegroups[crsg].splines[crsd].controlpoints[cp];
// If the mouse is inside a control point, set the info about that control point
// in the selectedcontrolpoint struct
if(mousemappos.x >= p.x - CONTROLPOINT_SIZE / 2 && mousemappos.x <= p.x + CONTROLPOINT_SIZE / 2 && mousemappos.y >= p.y - CONTROLPOINT_SIZE / 2 && mousemappos.y <= p.y + CONTROLPOINT_SIZE / 2)
{
selectedcontrolpoint.cp = cp;
selectedcontrolpoint.crsd = crsd;
selectedcontrolpoint.crsg = crsg;
}
}
}
}
}
// When selected button is released
protected override void OnSelectEnd()
{
base.OnSelectEnd();
// No control point is selected
selectedcontrolpoint.cp = -1;
selectedcontrolpoint.crsd = -1;
selectedcontrolpoint.crsg = -1;
// Redraw
General.Map.Map.Update();
General.Interface.RedrawDisplay();
}
// Mouse moves
public override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
// If a control point is selected move it around
if(selectedcontrolpoint.cp != -1)
{
if(General.Interface.ShiftState ^ General.Interface.SnapToGrid)
{
catmullromsplinegroups[selectedcontrolpoint.crsg].splines[selectedcontrolpoint.crsd].controlpoints[selectedcontrolpoint.cp] = new Vector2D(General.Map.Grid.SnappedToGrid(mousemappos).x, General.Map.Grid.SnappedToGrid(mousemappos).y);
}
else
{
catmullromsplinegroups[selectedcontrolpoint.crsg].splines[selectedcontrolpoint.crsd].controlpoints[selectedcontrolpoint.cp] = new Vector2D(mousemappos.x, mousemappos.y);
}
// New tangents of the control points have to be calculated
ComputeTangents(ref catmullromsplinegroups[selectedcontrolpoint.crsg].splines[selectedcontrolpoint.crsd]);
General.Interface.RedrawDisplay();
}
}
public static void SavePrefabs()
{
ListDictionary prefabdata = new ListDictionary();
int counter = 1;
foreach(BuilderPlug.Prefab p in BuilderPlug.Me.Prefabs)
{
ListDictionary data = new ListDictionary();
data.Add("name", p.name);
data.Add("numberofsectors", p.numberofsectors);
data.Add("outervertexmultiplier", p.outervertexmultiplier);
data.Add("innervertexmultiplier", p.innervertexmultiplier);
data.Add("stairtype", p.stairtype);
data.Add("sectordepth", p.sectordepth);
data.Add("spacing", p.spacing);
data.Add("frontside", p.frontside);
data.Add("singlesectors", p.singlesteps);
data.Add("distinctsectors", p.distinctsectors);
data.Add("singledirection", p.singledirection);
data.Add("distinctbaseheights", p.distinctbaseheights);
data.Add("flipping", p.flipping);
data.Add("numberofcontrolpoints", p.numberofcontrolpoints);
data.Add("applyfloormod", p.applyfloormod);
data.Add("floormod", p.floormod);
//data.Add("floorbase", p.floorbase);
data.Add("applyceilingmod", p.applyceilingmod);
data.Add("ceilingmod", p.ceilingmod);
//data.Add("ceilingbase", p.ceilingbase);
data.Add("applyfloortexture", p.applyfloortexture);
data.Add("floortexture", p.floortexture);
data.Add("applyceilingtexture", p.applyceilingtexture);
data.Add("ceilingtexture", p.ceilingtexture);
data.Add("applyuppertexture", p.applyuppertexture);
data.Add("uppertexture", p.uppertexture);
data.Add("upperunpegged", p.upperunpegged);
data.Add("applymiddletexture", p.applymiddletexture);
data.Add("middletexture", p.middletexture);
data.Add("applylowertexture", p.applylowertexture);
data.Add("lowertexture", p.lowertexture);
data.Add("lowerunpegged", p.lowerunpegged);
prefabdata.Add("prefab" + counter, data);
counter++;
}
General.Map.Options.WritePluginSetting("prefabs", prefabdata);
}
private void LoadPrefabs()
{
// load the light info from the .dbs
ListDictionary prefabdata = (ListDictionary)General.Map.Options.ReadPluginSetting("prefabs", new ListDictionary());
BuilderPlug.Me.Prefabs.Clear();
foreach(DictionaryEntry prefabentry in prefabdata)
{
BuilderPlug.Prefab p = new BuilderPlug.Prefab();
foreach(DictionaryEntry entry in (ListDictionary)prefabentry.Value)
{
if((string)entry.Key == "name") p.name = (string)entry.Value;
if((string)entry.Key == "numberofsectors") p.numberofsectors = (int)entry.Value;
if((string)entry.Key == "outervertexmultiplier") p.outervertexmultiplier = (int)entry.Value;
if((string)entry.Key == "innervertexmultiplier") p.innervertexmultiplier = (int)entry.Value;
if((string)entry.Key == "stairtype") p.stairtype = (int)entry.Value;
if((string)entry.Key == "sectordepth") p.sectordepth = (int)entry.Value;
if((string)entry.Key == "spacing") p.spacing = (int)entry.Value;
if((string)entry.Key == "frontside") p.frontside = (bool)entry.Value;
if((string)entry.Key == "singlesectors") p.singlesteps = (bool)entry.Value;
if((string)entry.Key == "distinctsectors") p.distinctsectors = (bool)entry.Value;
if((string)entry.Key == "singledirection") p.singledirection = (bool)entry.Value;
if((string)entry.Key == "distinctbaseheights") p.distinctbaseheights = (bool)entry.Value;
if((string)entry.Key == "flipping") p.flipping = (int)entry.Value;
if((string)entry.Key == "numberofcontrolpoints") p.numberofcontrolpoints = (int)entry.Value;
if((string)entry.Key == "applyfloormod") p.applyfloormod = (bool)entry.Value;
if((string)entry.Key == "floormod") p.floormod = (int)entry.Value;
//if((string)entry.Key == "floorbase") p.floorbase = (int)entry.Value;
if((string)entry.Key == "applyceilingmod") p.applyceilingmod = (bool)entry.Value;
if((string)entry.Key == "ceilingmod") p.ceilingmod = (int)entry.Value;
//if((string)entry.Key == "ceilingbase") p.ceilingbase = (int)entry.Value;
if((string)entry.Key == "applyfloortexture") p.applyfloortexture = (bool)entry.Value;
if((string)entry.Key == "floortexture") p.floortexture = (string)entry.Value;
if((string)entry.Key == "applyceilingtexture") p.applyceilingtexture = (bool)entry.Value;
if((string)entry.Key == "ceilingtexture") p.ceilingtexture = (string)entry.Value;
if((string)entry.Key == "applyuppertexture") p.applyuppertexture = (bool)entry.Value;
if((string)entry.Key == "upperunpegged") p.upperunpegged = (bool)entry.Value;
if((string)entry.Key == "uppertexture") p.uppertexture = (string)entry.Value;
if((string)entry.Key == "applymiddletexture") p.applymiddletexture = (bool)entry.Value;
if((string)entry.Key == "middletexture") p.middletexture = (string)entry.Value;
if((string)entry.Key == "applylowertexture") p.applylowertexture = (bool)entry.Value;
if((string)entry.Key == "lowertexture") p.lowertexture = (string)entry.Value;
if((string)entry.Key == "lowerunpegged") p.lowerunpegged = (bool)entry.Value;
}
BuilderPlug.Me.Prefabs.Add(p);
}
}
#endregion
}
}