diff --git a/Sledge.Formats.Map/Factories/Solids/ConeSolidFactory.cs b/Sledge.Formats.Map/Factories/Solids/ConeSolidFactory.cs index ecdb4db..49ca0a5 100644 --- a/Sledge.Formats.Map/Factories/Solids/ConeSolidFactory.cs +++ b/Sledge.Formats.Map/Factories/Solids/ConeSolidFactory.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Linq; using System.Numerics; using Sledge.Formats.Geometric; using Sledge.Formats.Map.Objects; diff --git a/Sledge.Formats.Map/Factories/Solids/ISolidFactory.cs b/Sledge.Formats.Map/Factories/Solids/ISolidFactory.cs index 0a23e42..a7ea43d 100644 --- a/Sledge.Formats.Map/Factories/Solids/ISolidFactory.cs +++ b/Sledge.Formats.Map/Factories/Solids/ISolidFactory.cs @@ -1,5 +1,4 @@ -using System.ComponentModel; -using Sledge.Formats.Map.Objects; +using Sledge.Formats.Map.Objects; namespace Sledge.Formats.Map.Factories.Solids { diff --git a/Sledge.Formats.Map/Factories/Solids/PipeSolidFactory.cs b/Sledge.Formats.Map/Factories/Solids/PipeSolidFactory.cs index 7d51a9e..ccc062f 100644 --- a/Sledge.Formats.Map/Factories/Solids/PipeSolidFactory.cs +++ b/Sledge.Formats.Map/Factories/Solids/PipeSolidFactory.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Drawing; using System.Numerics; using Sledge.Formats.Geometric; using Sledge.Formats.Map.Objects; @@ -22,22 +21,6 @@ public class PipeSolidFactory : SolidFactoryBase [MapObjectFactoryPropertyData(MinValue = 0.01, DecimalPrecision = 2)] public int WallWidth { get; set; } - private static Solid MakeSolid(IEnumerable<(string textureName, Vector3[] points)> faces, Color col) - { - var solid = new Solid { Color = col }; - foreach (var (texture, arr) in faces) - { - var face = new Face - { - Plane = Plane.CreateFromVertices(arr[0], arr[1], arr[2]), - TextureName = texture - }; - face.Vertices.AddRange(arr); - solid.Faces.Add(face); - } - return solid; - } - public override IEnumerable Create(Box box) { var numSides = NumberOfSides; diff --git a/Sledge.Formats.Map/Factories/Solids/PyramidSolidFactory.cs b/Sledge.Formats.Map/Factories/Solids/PyramidSolidFactory.cs new file mode 100644 index 0000000..5daf949 --- /dev/null +++ b/Sledge.Formats.Map/Factories/Solids/PyramidSolidFactory.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Numerics; +using Sledge.Formats.Geometric; +using Sledge.Formats.Map.Objects; + +namespace Sledge.Formats.Map.Factories.Solids +{ + public class PyramidSolidFactory : SolidFactoryBase + { + public override string Name => "Pyramid"; + + public override IEnumerable Create(Box box) + { + var solid = new Solid + { + Color = ColorUtils.GetRandomBrushColour() + }; + + // The lower Z plane will be base + var c1 = new Vector3(box.Start.X, box.Start.Y, box.Start.Z).Round(RoundDecimals); + var c2 = new Vector3(box.End.X, box.Start.Y, box.Start.Z).Round(RoundDecimals); + var c3 = new Vector3(box.End.X, box.End.Y, box.Start.Z).Round(RoundDecimals); + var c4 = new Vector3(box.Start.X, box.End.Y, box.Start.Z).Round(RoundDecimals); + var c5 = new Vector3(box.Center.X, box.Center.Y, box.End.Z).Round(RoundDecimals); + var faces = new[] + { + new[] { c4, c3, c2, c1 }, + new[] { c5, c1, c2 }, + new[] { c5, c2, c3 }, + new[] { c5, c3, c4 }, + new[] { c5, c4, c1 } + }; + foreach (var arr in faces) + { + var face = new Face + { + Plane = Plane.CreateFromVertices(arr[0], arr[1], arr[2]), + TextureName = VisibleTextureName + }; + face.Vertices.AddRange(arr); + solid.Faces.Add(face); + } + yield return solid; + } + } +} \ No newline at end of file diff --git a/Sledge.Formats.Map/Factories/Solids/SolidFactoryBase.cs b/Sledge.Formats.Map/Factories/Solids/SolidFactoryBase.cs index 3c4103f..2dd2681 100644 --- a/Sledge.Formats.Map/Factories/Solids/SolidFactoryBase.cs +++ b/Sledge.Formats.Map/Factories/Solids/SolidFactoryBase.cs @@ -1,4 +1,8 @@ -using System.ComponentModel; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Numerics; +using Sledge.Formats.Map.Objects; namespace Sledge.Formats.Map.Factories.Solids { @@ -18,5 +22,21 @@ public abstract class SolidFactoryBase : MapObjectFactoryBase, ISolidFactory [DisplayName("Round decimals")] [Description("The number of decimals to round vertex positions to")] public int RoundDecimals { get; set; } + + protected static Solid MakeSolid(IEnumerable<(string textureName, Vector3[] points)> faces, Color col) + { + var solid = new Solid { Color = col }; + foreach (var (texture, arr) in faces) + { + var face = new Face + { + Plane = Plane.CreateFromVertices(arr[0], arr[1], arr[2]), + TextureName = texture + }; + face.Vertices.AddRange(arr); + solid.Faces.Add(face); + } + return solid; + } } } \ No newline at end of file diff --git a/Sledge.Formats.Map/Factories/Solids/TetrahedronSolidFactory.cs b/Sledge.Formats.Map/Factories/Solids/TetrahedronSolidFactory.cs new file mode 100644 index 0000000..15e9288 --- /dev/null +++ b/Sledge.Formats.Map/Factories/Solids/TetrahedronSolidFactory.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Numerics; +using Sledge.Formats.Geometric; +using Sledge.Formats.Map.Objects; + +namespace Sledge.Formats.Map.Factories.Solids +{ + public class TetrahedronSolidFactory : SolidFactoryBase + { + public override string Name => "Tetrahedron"; + + [DisplayName("Top vertex at centroid")] + [Description("Put the top vertex at the centroid of the base triangle instead of at the center of the bounding box")] + public bool TopVertexAtCentroid { get; set; } + + public override IEnumerable Create(Box box) + { + var useCentroid = TopVertexAtCentroid; + + // The lower Z plane will be the triangle, with the lower Y value getting the two corners + var c1 = new Vector3(box.Start.X, box.Start.Y, box.Start.Z).Round(RoundDecimals); + var c2 = new Vector3(box.End.X, box.Start.Y, box.Start.Z).Round(RoundDecimals); + var c3 = new Vector3(box.Center.X, box.End.Y, box.Start.Z).Round(RoundDecimals); + var centroid = new Vector3((c1.X + c2.X + c3.X) / 3, (c1.Y + c2.Y + c3.Y) / 3, box.End.Z); + var c4 = (useCentroid ? centroid : new Vector3(box.Center.X, box.Center.Y, box.End.Z)).Round(RoundDecimals); + + var faces = new[] { + new[] { c3, c2, c1 }, + new[] { c3, c1, c4 }, + new[] { c2, c3, c4 }, + new[] { c1, c2, c4 } + }; + + var solid = new Solid + { + Color = ColorUtils.GetRandomBrushColour() + }; + + foreach (var arr in faces) + { + var face = new Face + { + Plane = Plane.CreateFromVertices(arr[0], arr[1], arr[2]), + TextureName = VisibleTextureName + }; + face.Vertices.AddRange(arr); + solid.Faces.Add(face); + } + yield return solid; + } + } +} \ No newline at end of file diff --git a/Sledge.Formats.Map/Factories/Solids/UVSphereSolidFactory.cs b/Sledge.Formats.Map/Factories/Solids/UVSphereSolidFactory.cs new file mode 100644 index 0000000..50a7d5c --- /dev/null +++ b/Sledge.Formats.Map/Factories/Solids/UVSphereSolidFactory.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Numerics; +using Sledge.Formats.Geometric; +using Sledge.Formats.Map.Objects; + +namespace Sledge.Formats.Map.Factories.Solids +{ + public class UVSphereSolidFactory : SolidFactoryBase + { + public override string Name => "UV Sphere"; + + [DisplayName("Number of segments (vertical)")] + [Description("The number of segments the sphere will have vertically")] + [MapObjectFactoryPropertyData(MinValue = 3)] + public int NumberOfSegmentsVertical { get; set; } + + [DisplayName("Number of segments (horizontal)")] + [Description("The number of segments the sphere will have horizontally")] + [MapObjectFactoryPropertyData(MinValue = 3)] + public int NumberOfSegmentsHorizontal { get; set; } + + public override IEnumerable Create(Box box) + { + var numSidesV = NumberOfSegmentsVertical; + if (numSidesV < 3) throw new ArgumentException("NumberOfSegmentsVertical must be >= 3", nameof(NumberOfSegmentsVertical)); + + var numSidesH = NumberOfSegmentsHorizontal; + if (numSidesH < 3) throw new ArgumentException("NumberOfSegmentsHorizontal must be >= 3", nameof(NumberOfSegmentsHorizontal)); + + var roundDecimals = Math.Max(2, RoundDecimals); // don't support rounding < 2 because it would result in invalid faces too often + + var width = box.Width; + var length = box.Length; + var height = box.Height; + var major = width / 2; + var minor = length / 2; + var heightRadius = height / 2; + + var angleV = (float)Math.PI / numSidesV; + var angleH = (float)(Math.PI * 2) / numSidesH; + + var faces = new List<(string textureName, Vector3[] points)>(); + var bottom = new Vector3(box.Center.X, box.Center.Y, box.Start.Z).Round(roundDecimals); + var top = new Vector3(box.Center.X, box.Center.Y, box.End.Z).Round(roundDecimals); + + for (var i = 0; i < numSidesV; i++) + { + // Top -> bottom + var zAngleStart = angleV * i; + var zAngleEnd = angleV * (i + 1); + var zStart = heightRadius * (float)Math.Cos(zAngleStart); + var zEnd = heightRadius * (float)Math.Cos(zAngleEnd); + var zMultStart = (float)Math.Sin(zAngleStart); + var zMultEnd = (float)Math.Sin(zAngleEnd); + for (var j = 0; j < numSidesH; j++) + { + // Go around the circle in X/Y + var xyAngleStart = angleH * j; + var xyAngleEnd = angleH * ((j + 1) % numSidesH); + var xyStartX = major * (float)Math.Cos(xyAngleStart); + var xyStartY = minor * (float)Math.Sin(xyAngleStart); + var xyEndX = major * (float)Math.Cos(xyAngleEnd); + var xyEndY = minor * (float)Math.Sin(xyAngleEnd); + var one = (new Vector3(xyStartX * zMultStart, xyStartY * zMultStart, zStart) + box.Center).Round(roundDecimals); + var two = (new Vector3(xyEndX * zMultStart, xyEndY * zMultStart, zStart) + box.Center).Round(roundDecimals); + var three = (new Vector3(xyEndX * zMultEnd, xyEndY * zMultEnd, zEnd) + box.Center).Round(roundDecimals); + var four = (new Vector3(xyStartX * zMultEnd, xyStartY * zMultEnd, zEnd) + box.Center).Round(roundDecimals); + if (i == 0) + { + // Top faces are triangles + faces.Add((VisibleTextureName, new[] { four, three, top })); + } + else if (i == numSidesV - 1) + { + // Bottom faces are also triangles + faces.Add((VisibleTextureName, new[] { two, one, bottom })); + } + else + { + // Inner faces are quads + faces.Add((VisibleTextureName, new[] { four, three, two, one })); + } + } + } + yield return MakeSolid(faces, ColorUtils.GetRandomBrushColour()); + } + } +} diff --git a/Sledge.Formats.Map/Factories/Solids/WedgeSolidFactory.cs b/Sledge.Formats.Map/Factories/Solids/WedgeSolidFactory.cs new file mode 100644 index 0000000..a4682c9 --- /dev/null +++ b/Sledge.Formats.Map/Factories/Solids/WedgeSolidFactory.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Numerics; +using Sledge.Formats.Geometric; +using Sledge.Formats.Map.Objects; + +namespace Sledge.Formats.Map.Factories.Solids +{ + public class WedgeSolidFactory : SolidFactoryBase + { + public override string Name => "Wedge"; + + public override IEnumerable Create(Box box) + { + var solid = new Solid + { + Color = ColorUtils.GetRandomBrushColour() + }; + + // The lower Z plane will be base, the x planes will be triangles + var c1 = new Vector3(box.Start.X, box.Start.Y, box.Start.Z).Round(RoundDecimals); + var c2 = new Vector3(box.End.X, box.Start.Y, box.Start.Z).Round(RoundDecimals); + var c3 = new Vector3(box.End.X, box.End.Y, box.Start.Z).Round(RoundDecimals); + var c4 = new Vector3(box.Start.X, box.End.Y, box.Start.Z).Round(RoundDecimals); + var c5 = new Vector3(box.Center.X, box.Start.Y, box.End.Z).Round(RoundDecimals); + var c6 = new Vector3(box.Center.X, box.End.Y, box.End.Z).Round(RoundDecimals); + var faces = new[] + { + new[] { c4, c3, c2, c1 }, + new[] { c5, c1, c2 }, + new[] { c2, c3, c6, c5 }, + new[] { c6, c3, c4 }, + new[] { c4, c1, c5, c6 } + }; + foreach (var arr in faces) + { + var face = new Face + { + Plane = Plane.CreateFromVertices(arr[0], arr[1], arr[2]), + TextureName = VisibleTextureName + }; + face.Vertices.AddRange(arr); + solid.Faces.Add(face); + } + yield return solid; + } + } +} \ No newline at end of file diff --git a/Sledge.Formats.sln.DotSettings b/Sledge.Formats.sln.DotSettings new file mode 100644 index 0000000..cf58991 --- /dev/null +++ b/Sledge.Formats.sln.DotSettings @@ -0,0 +1,2 @@ + + UV \ No newline at end of file