using System; using System.Collections.Generic; using Oni.Imaging; namespace Oni.Akira { internal class Polygon { #region Private data private PolygonMesh mesh; private GunkFlags flags; private int[] pointIndices; private int[] texCoordIndices; private int[] normalIndices; private Color[] colors; private Material material; private Plane plane; private int objectType = -1; private int objectId = -1; private int scriptId; private string fileName; private string objectName; private PolygonEdge[] edges; private BoundingBox bbox; #endregion public Polygon(PolygonMesh mesh, int[] pointIndices) { this.mesh = mesh; this.pointIndices = pointIndices; this.plane = GetPlane(); this.bbox = GetBoundingBox(); BuildFlags(); } public Polygon(PolygonMesh mesh, int[] pointIndices, GunkFlags flags) : this(mesh, pointIndices) { this.flags |= flags; } public Polygon(PolygonMesh mesh, int[] pointIndices, Plane plane) { this.mesh = mesh; this.pointIndices = pointIndices; this.plane = plane; BuildFlags(); } public PolygonMesh Mesh => mesh; public GunkFlags Flags { get { if (material == null) return flags; return flags | material.Flags; } set { flags = value; } } public bool IsTransparent => (Flags & GunkFlags.Transparent) != 0; public bool IsStairs=>(Flags & GunkFlags.Stairs) != 0; public Material Material { get { return material; } set { material = value; } } public int VertexCount => pointIndices.Length; public int[] PointIndices => pointIndices; public IEnumerable Points { get { foreach (int i in pointIndices) yield return mesh.Points[i]; } } public int[] TexCoordIndices { get { return texCoordIndices; } set { texCoordIndices = value; } } public int[] NormalIndices { get { return normalIndices; } set { normalIndices = value; } } public Color[] Colors { get { return colors; } set { colors = value; } } public Plane Plane => plane; public int ObjectType { get { return objectType; } set { objectType = value; } } public int ObjectId { get { return objectId; } set { objectId = value; } } public int ScriptId { get { return scriptId; } set { scriptId = value; } } public string FileName { get { return fileName; } set { fileName = value; } } public string ObjectName { get { return objectName; } set { objectName = value; } } private Plane GetPlane() { var plane = new Plane( mesh.Points[pointIndices[0]], mesh.Points[pointIndices[1]], mesh.Points[pointIndices[2]]); var bbox = GetBoundingBox(); var bboxSize = bbox.Max - bbox.Min; if (Math.Abs(bboxSize.X) < 0.0001f) { if (plane.Normal.X < 0.0f) plane = new Plane(Vector3.Left, bbox.Min.X); else plane = new Plane(Vector3.Right, -bbox.Max.X); } else if (Math.Abs(bboxSize.Y) < 0.0001f) { if (plane.Normal.Y < 0.0f) plane = new Plane(Vector3.Down, bbox.Min.Y); else plane = new Plane(Vector3.Up, -bbox.Max.Y); } else if (Math.Abs(bboxSize.Z) < 0.0001f) { if (plane.Normal.Z < 0.0f) plane = new Plane(Vector3.Forward, bbox.Min.Z); else plane = new Plane(Vector3.Backward, -bbox.Max.Z); } else { plane.Normal.X = FMath.Round(plane.Normal.X, 4); plane.Normal.Y = FMath.Round(plane.Normal.Y, 4); plane.Normal.Z = FMath.Round(plane.Normal.Z, 4); } return plane; } public BoundingBox BoundingBox => bbox; private BoundingBox GetBoundingBox() { var point = mesh.Points[pointIndices[0]]; var bbox = new BoundingBox(point, point); for (int i = 1; i < pointIndices.Length; i++) { point = mesh.Points[pointIndices[i]]; Vector3.Min(ref bbox.Min, ref point, out bbox.Min); Vector3.Max(ref bbox.Max, ref point, out bbox.Max); } return bbox; } private void BuildFlags() { SetProjectionPlane(); SetHorizontalVertical(); } private void SetHorizontalVertical() { if (Math.Abs(Vector3.Dot(plane.Normal, Vector3.UnitY)) < 0.3420201f) flags |= GunkFlags.Vertical; else flags |= GunkFlags.Horizontal; } private void SetProjectionPlane() { var points = new Vector3[pointIndices.Length]; for (int i = 0; i < pointIndices.Length; i++) points[i] = mesh.Points[pointIndices[i]]; float xyArea = MathHelper.Area(Project(points, PolygonProjectionPlane.XY)); float yzArea = MathHelper.Area(Project(points, PolygonProjectionPlane.YZ)); float xzArea = MathHelper.Area(Project(points, PolygonProjectionPlane.XZ)); var plane = PolygonProjectionPlane.None; if (xyArea > yzArea) { if (xyArea > xzArea) plane = PolygonProjectionPlane.XY; else plane = PolygonProjectionPlane.XZ; } else { if (yzArea > xzArea) plane = PolygonProjectionPlane.YZ; else plane = PolygonProjectionPlane.XZ; } flags |= (GunkFlags)((int)plane << 25); } private static Vector2[] Project(Vector3[] points, PolygonProjectionPlane plane) { var result = new Vector2[points.Length]; switch (plane) { case PolygonProjectionPlane.XY: for (int i = 0; i < points.Length; i++) { result[i].X = points[i].X; result[i].Y = points[i].Y; } break; case PolygonProjectionPlane.XZ: for (int i = 0; i < points.Length; i++) { result[i].X = points[i].X; result[i].Y = points[i].Z; } break; case PolygonProjectionPlane.YZ: for (int i = 0; i < points.Length; i++) { result[i].X = points[i].Z; result[i].Y = points[i].Y; } break; } return result; } public PolygonEdge[] Edges { get { if (edges == null) { edges = new PolygonEdge[pointIndices.Length]; for (int i = 0; i < edges.Length; i++) edges[i] = new PolygonEdge(this, i); } return edges; } } } }