public IEnumerable <MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour(), Flags = (System.UInt32)SolidFlags.solid }; // The lower Z plane will be base, the x planes will be triangles var c1 = new Coordinate(box.Start.X, box.Start.Y, box.Start.Z).Round(roundDecimals); var c2 = new Coordinate(box.End.X, box.Start.Y, box.Start.Z).Round(roundDecimals); var c3 = new Coordinate(box.End.X, box.End.Y, box.Start.Z).Round(roundDecimals); var c4 = new Coordinate(box.Start.X, box.End.Y, box.Start.Z).Round(roundDecimals); var c5 = new Coordinate(box.Center.X, box.Start.Y, box.End.Z).Round(roundDecimals); var c6 = new Coordinate(box.Center.X, box.End.Y, box.End.Z).Round(roundDecimals); var faces = new[] { new[] { c1, c2, c3, c4 }, new[] { c2, c1, c5 }, new[] { c5, c6, c3, c2 }, new[] { c4, c3, c6 }, new[] { c6, c5, c1, c4 } }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.Init(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return(solid); }
private static Face ReadFace(GenericStructure side, IDGenerator generator) { var id = side.PropertyLong("id"); if (id == 0) { id = generator.GetNextFaceID(); } var dispinfo = side.GetChildren("dispinfo").FirstOrDefault(); var ret = dispinfo != null?ReadDisplacement(id, dispinfo) : new Face(id); // id, plane, material, uaxis, vaxis, rotation, lightmapscale, smoothing_groups var uaxis = side.PropertyTextureAxis("uaxis"); var vaxis = side.PropertyTextureAxis("vaxis"); ret.Texture.Name = side["material"]; ret.Texture.UAxis = uaxis.Item1; ret.Texture.XShift = uaxis.Item2; ret.Texture.XScale = uaxis.Item3; ret.Texture.VAxis = vaxis.Item1; ret.Texture.YShift = vaxis.Item2; ret.Texture.YScale = vaxis.Item3; ret.Texture.Rotation = side.PropertyDecimal("rotation"); ret.Plane = side.PropertyPlane("plane"); var verts = side.Children.FirstOrDefault(x => x.Name == "vertex"); if (verts != null) { var count = verts.PropertyInteger("count"); for (var i = 0; i < count; i++) { ret.Vertices.Add(new Vertex(verts.PropertyCoordinate("vertex" + i), ret)); } } return(ret); }
private Solid MakeSolid(IDGenerator generator, IEnumerable <Coordinate[]> faces, ITexture texture, Color col) { var solid = new Solid(generator.GetNextObjectID()) { Colour = col, Flags = (System.UInt32)SolidFlags.solid }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.Init(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); return(solid); }
public IEnumerable <MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour(), Flags = (System.UInt32)SolidFlags.solid }; foreach (var arr in box.GetBoxFaces()) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture }, //Flags = FaceFlags.Visible }; face.Vertices.AddRange(arr.Select(x => new Vertex(x.Round(roundDecimals), face))); face.Init(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return(solid); }
public IEnumerable <MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var numSides = (int)_numSides.GetValue(); if (numSides < 3) { yield break; } // Cylinders can be elliptical so use both major and minor rather than just the radius // NOTE: when a low number (< 10ish) of faces are selected this will cause the cylinder to not touch all the edges of the box. var width = box.Width; var length = box.Length; var height = box.Height; var major = width / 2; var minor = length / 2; var angle = 2 * DMath.PI / numSides; // Calculate the X and Y points for the ellipse var points = new Coordinate[numSides]; for (var i = 0; i < numSides; i++) { var a = i * angle; var xval = box.Center.X + major * DMath.Cos(a); var yval = box.Center.Y + minor * DMath.Sin(a); var zval = box.Start.Z; points[i] = new Coordinate(xval, yval, zval).Round(roundDecimals); } var faces = new List <Coordinate[]>(); // Add the vertical faces var z = new Coordinate(0, 0, height).Round(roundDecimals); for (var i = 0; i < numSides; i++) { var next = (i + 1) % numSides; faces.Add(new[] { points[i], points[i] + z, points[next] + z, points[next] }); } // Add the elliptical top and bottom faces faces.Add(points.ToArray()); faces.Add(points.Select(x => x + z).Reverse().ToArray()); // Nothing new here, move along var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour() }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return(solid); }
public IEnumerable <MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var width = box.Width; var length = Math.Max(1, Math.Abs((int)box.Length)); var height = box.Height; var flatten = (float)_flattenFactor.Value; var text = _text.GetValue(); var family = _fontChooser.GetFontFamily(); var style = Enum.GetValues(typeof(FontStyle)).OfType <FontStyle>().FirstOrDefault(fs => family.IsStyleAvailable(fs)); if (!family.IsStyleAvailable(style)) { family = FontFamily.GenericSansSerif; } var set = new PolygonSet(); var sizes = new List <RectangleF>(); using (var bmp = new Bitmap(1, 1)) { using (var g = System.Drawing.Graphics.FromImage(bmp)) { using (var font = new Font(family, length, style, GraphicsUnit.Pixel)) { for (var i = 0; i < text.Length; i += 32) { using (var sf = new StringFormat(StringFormat.GenericTypographic)) { var rem = Math.Min(text.Length, i + 32) - i; var range = Enumerable.Range(0, rem).Select(x => new CharacterRange(x, 1)).ToArray(); sf.SetMeasurableCharacterRanges(range); var reg = g.MeasureCharacterRanges(text.Substring(i, rem), font, new RectangleF(0, 0, float.MaxValue, float.MaxValue), sf); sizes.AddRange(reg.Select(x => x.GetBounds(g))); } } } } } var xOffset = box.Start.DX; var yOffset = box.End.DY; for (var ci = 0; ci < text.Length; ci++) { var c = text[ci]; var size = sizes[ci]; var gp = new GraphicsPath(); gp.AddString(c.ToString(CultureInfo.InvariantCulture), family, (int)style, length, new PointF(0, 0), StringFormat.GenericTypographic); gp.Flatten(new System.Drawing.Drawing2D.Matrix(), flatten); var polygons = new List <Polygon>(); var poly = new List <PolygonPoint>(); for (var i = 0; i < gp.PointCount; i++) { var type = gp.PathTypes[i]; var point = gp.PathPoints[i]; poly.Add(new PolygonPoint(point.X + xOffset, -point.Y + yOffset)); if ((type & 0x80) == 0x80) { polygons.Add(new Polygon(poly)); poly.Clear(); } } var tri = new List <Polygon>(); Polygon polygon = null; foreach (var p in polygons) { if (polygon == null) { polygon = p; tri.Add(p); } else if (p.CalculateWindingOrder() != polygon.CalculateWindingOrder()) { polygon.AddHole(p); } else { polygon = null; tri.Add(p); } } foreach (var pp in tri) { try { P2T.Triangulate(pp); set.Add(pp); } catch { // Ignore } } xOffset += size.Width; } var zOffset = box.Start.Z; foreach (var polygon in set.Polygons) { foreach (var t in polygon.Triangles) { var points = t.Points.Select(x => new Coordinate((decimal)x.X, (decimal)x.Y, zOffset).Round(roundDecimals)).ToList(); var faces = new List <Coordinate[]>(); // Add the vertical faces var z = new Coordinate(0, 0, height).Round(roundDecimals); for (var j = 0; j < points.Count; j++) { var next = (j + 1) % points.Count; faces.Add(new[] { points[j], points[j] + z, points[next] + z, points[next] }); } // Add the top and bottom faces faces.Add(points.ToArray()); faces.Add(points.Select(x => x + z).Reverse().ToArray()); // Nothing new here, move along var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour() }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return(solid); } } }
private static List <Face> CalculateDecalGeometry(Entity entity, ITexture decal) { var decalGeometry = new List <Face>(); if (decal == null || entity.Parent == null) { return(decalGeometry); // Texture not found } var boxRadius = Coordinate.One * 4; // Decals apply to all faces that intersect within an 8x8x8 bounding box // centered at the origin of the decal var box = new Box(entity.Origin - boxRadius, entity.Origin + boxRadius); var root = MapObject.GetRoot(entity.Parent); // Get the faces that intersect with the decal's radius var faces = root.GetAllNodesIntersectingWith(box).OfType <Solid>() .SelectMany(x => x.Faces).Where(x => x.IntersectsWithBox(box)); var idg = new IDGenerator(); // Dummy generator foreach (var face in faces) { // Project the decal onto the face var center = face.Plane.Project(entity.Origin); var texture = face.Texture.Clone(); texture.Name = decal.Name; texture.Texture = decal; texture.XShift = -decal.Width / 2m; texture.YShift = -decal.Height / 2m; var decalFace = new Face(idg.GetNextFaceID()) { Colour = entity.Colour, IsSelected = entity.IsSelected, IsHidden = entity.IsCodeHidden, Plane = face.Plane, Texture = texture }; // Re-project the vertices in case the texture axes are not on the face plane var xShift = face.Texture.UAxis * face.Texture.XScale * decal.Width / 2; var yShift = face.Texture.VAxis * face.Texture.YScale * decal.Height / 2; var verts = new[] { new Vertex(face.Plane.Project(center + xShift - yShift), decalFace), // Bottom Right new Vertex(face.Plane.Project(center + xShift + yShift), decalFace), // Top Right new Vertex(face.Plane.Project(center - xShift + yShift), decalFace), // Top Left new Vertex(face.Plane.Project(center - xShift - yShift), decalFace) // Bottom Left }; // Because the texture axes don't have to align to the face, we might have a reversed face here // If so, reverse the points to get a valid face for the plane. // TODO: Is there a better way to do this? var vertPlane = new Plane(verts[0].Location, verts[1].Location, verts[2].Location); if (!face.Plane.Normal.EquivalentTo(vertPlane.Normal)) { Array.Reverse(verts); } decalFace.Vertices.AddRange(verts); decalFace.UpdateBoundingBox(); // Calculate the X and Y shift bases on the first vertex location (assuming U/V of first vertex is zero) - we dont want these to change var vtx = decalFace.Vertices[0]; decalFace.Texture.XShift = -(vtx.Location.Dot(decalFace.Texture.UAxis)) / decalFace.Texture.XScale; decalFace.Texture.YShift = -(vtx.Location.Dot(decalFace.Texture.VAxis)) / decalFace.Texture.YScale; decalFace.CalculateTextureCoordinates(true); // Next, the decal geometry needs to be clipped to the face so it doesn't spill into the void // Create a fake solid out of the decal geometry and clip it against all the brush planes var fake = CreateFakeDecalSolid(decalFace); foreach (var f in face.Parent.Faces.Except(new[] { face })) { Solid back, front; fake.Split(f.Plane, out back, out front, idg); fake = back ?? fake; } // Extract out the original face decalFace = fake.Faces.FirstOrDefault(x => x.Plane.EquivalentTo(face.Plane, 0.05m)); if (decalFace == null) { continue; } // Add a tiny bit to the normal axis to ensure the decal is rendered in front of the face var normalAdd = face.Plane.Normal * 0.2m; decalFace.Transform(new UnitTranslate(normalAdd), TransformFlags.TextureLock); decalFace.IsSelected = entity.IsSelected; decalGeometry.Add(decalFace); } return(decalGeometry); }