| 1 |
using System; |
| 2 |
using System.Collections.Generic; |
| 3 |
using Oni.Imaging; |
| 4 |
|
| 5 |
namespace Oni.Akira |
| 6 |
{ |
| 7 |
internal class Polygon |
| 8 |
{ |
| 9 |
#region Private data |
| 10 |
private PolygonMesh mesh; |
| 11 |
private GunkFlags flags; |
| 12 |
private int[] pointIndices; |
| 13 |
private int[] texCoordIndices; |
| 14 |
private int[] normalIndices; |
| 15 |
private Color[] colors; |
| 16 |
private Material material; |
| 17 |
private Plane plane; |
| 18 |
private int objectType = -1; |
| 19 |
private int objectId = -1; |
| 20 |
private int scriptId; |
| 21 |
private string fileName; |
| 22 |
private string objectName; |
| 23 |
private PolygonEdge[] edges; |
| 24 |
private BoundingBox bbox; |
| 25 |
#endregion |
| 26 |
|
| 27 |
public Polygon(PolygonMesh mesh, int[] pointIndices) |
| 28 |
{ |
| 29 |
this.mesh = mesh; |
| 30 |
this.pointIndices = pointIndices; |
| 31 |
this.plane = GetPlane(); |
| 32 |
this.bbox = GetBoundingBox(); |
| 33 |
|
| 34 |
BuildFlags(); |
| 35 |
} |
| 36 |
|
| 37 |
public Polygon(PolygonMesh mesh, int[] pointIndices, GunkFlags flags) |
| 38 |
: this(mesh, pointIndices) |
| 39 |
{ |
| 40 |
this.flags |= flags; |
| 41 |
} |
| 42 |
|
| 43 |
public Polygon(PolygonMesh mesh, int[] pointIndices, Plane plane) |
| 44 |
{ |
| 45 |
this.mesh = mesh; |
| 46 |
this.pointIndices = pointIndices; |
| 47 |
this.plane = plane; |
| 48 |
|
| 49 |
BuildFlags(); |
| 50 |
} |
| 51 |
|
| 52 |
public PolygonMesh Mesh => mesh; |
| 53 |
|
| 54 |
public GunkFlags Flags |
| 55 |
{ |
| 56 |
get |
| 57 |
{ |
| 58 |
if (material == null) |
| 59 |
return flags; |
| 60 |
|
| 61 |
return flags | material.Flags; |
| 62 |
} |
| 63 |
set |
| 64 |
{ |
| 65 |
flags = value; |
| 66 |
} |
| 67 |
} |
| 68 |
|
| 69 |
public bool IsTransparent => (Flags & GunkFlags.Transparent) != 0; |
| 70 |
public bool IsStairs=>(Flags & GunkFlags.Stairs) != 0; |
| 71 |
|
| 72 |
public Material Material |
| 73 |
{ |
| 74 |
get { return material; } |
| 75 |
set { material = value; } |
| 76 |
} |
| 77 |
|
| 78 |
public int VertexCount => pointIndices.Length; |
| 79 |
|
| 80 |
public int[] PointIndices => pointIndices; |
| 81 |
|
| 82 |
public IEnumerable<Vector3> Points |
| 83 |
{ |
| 84 |
get |
| 85 |
{ |
| 86 |
foreach (int i in pointIndices) |
| 87 |
yield return mesh.Points[i]; |
| 88 |
} |
| 89 |
} |
| 90 |
|
| 91 |
public int[] TexCoordIndices |
| 92 |
{ |
| 93 |
get { return texCoordIndices; } |
| 94 |
set { texCoordIndices = value; } |
| 95 |
} |
| 96 |
|
| 97 |
public int[] NormalIndices |
| 98 |
{ |
| 99 |
get { return normalIndices; } |
| 100 |
set { normalIndices = value; } |
| 101 |
} |
| 102 |
|
| 103 |
public Color[] Colors |
| 104 |
{ |
| 105 |
get { return colors; } |
| 106 |
set { colors = value; } |
| 107 |
} |
| 108 |
|
| 109 |
public Plane Plane => plane; |
| 110 |
|
| 111 |
public int ObjectType |
| 112 |
{ |
| 113 |
get { return objectType; } |
| 114 |
set { objectType = value; } |
| 115 |
} |
| 116 |
|
| 117 |
public int ObjectId |
| 118 |
{ |
| 119 |
get { return objectId; } |
| 120 |
set { objectId = value; } |
| 121 |
} |
| 122 |
|
| 123 |
public int ScriptId |
| 124 |
{ |
| 125 |
get { return scriptId; } |
| 126 |
set { scriptId = value; } |
| 127 |
} |
| 128 |
|
| 129 |
public string FileName |
| 130 |
{ |
| 131 |
get { return fileName; } |
| 132 |
set { fileName = value; } |
| 133 |
} |
| 134 |
|
| 135 |
public string ObjectName |
| 136 |
{ |
| 137 |
get { return objectName; } |
| 138 |
set { objectName = value; } |
| 139 |
} |
| 140 |
|
| 141 |
private Plane GetPlane() |
| 142 |
{ |
| 143 |
var plane = new Plane( |
| 144 |
mesh.Points[pointIndices[0]], |
| 145 |
mesh.Points[pointIndices[1]], |
| 146 |
mesh.Points[pointIndices[2]]); |
| 147 |
|
| 148 |
var bbox = GetBoundingBox(); |
| 149 |
var bboxSize = bbox.Max - bbox.Min; |
| 150 |
|
| 151 |
if (Math.Abs(bboxSize.X) < 0.0001f) |
| 152 |
{ |
| 153 |
if (plane.Normal.X < 0.0f) |
| 154 |
plane = new Plane(Vector3.Left, bbox.Min.X); |
| 155 |
else |
| 156 |
plane = new Plane(Vector3.Right, -bbox.Max.X); |
| 157 |
} |
| 158 |
else if (Math.Abs(bboxSize.Y) < 0.0001f) |
| 159 |
{ |
| 160 |
if (plane.Normal.Y < 0.0f) |
| 161 |
plane = new Plane(Vector3.Down, bbox.Min.Y); |
| 162 |
else |
| 163 |
plane = new Plane(Vector3.Up, -bbox.Max.Y); |
| 164 |
} |
| 165 |
else if (Math.Abs(bboxSize.Z) < 0.0001f) |
| 166 |
{ |
| 167 |
if (plane.Normal.Z < 0.0f) |
| 168 |
plane = new Plane(Vector3.Forward, bbox.Min.Z); |
| 169 |
else |
| 170 |
plane = new Plane(Vector3.Backward, -bbox.Max.Z); |
| 171 |
} |
| 172 |
else |
| 173 |
{ |
| 174 |
plane.Normal.X = FMath.Round(plane.Normal.X, 4); |
| 175 |
plane.Normal.Y = FMath.Round(plane.Normal.Y, 4); |
| 176 |
plane.Normal.Z = FMath.Round(plane.Normal.Z, 4); |
| 177 |
} |
| 178 |
|
| 179 |
return plane; |
| 180 |
} |
| 181 |
|
| 182 |
public BoundingBox BoundingBox => bbox; |
| 183 |
|
| 184 |
private BoundingBox GetBoundingBox() |
| 185 |
{ |
| 186 |
var point = mesh.Points[pointIndices[0]]; |
| 187 |
var bbox = new BoundingBox(point, point); |
| 188 |
|
| 189 |
for (int i = 1; i < pointIndices.Length; i++) |
| 190 |
{ |
| 191 |
point = mesh.Points[pointIndices[i]]; |
| 192 |
|
| 193 |
Vector3.Min(ref bbox.Min, ref point, out bbox.Min); |
| 194 |
Vector3.Max(ref bbox.Max, ref point, out bbox.Max); |
| 195 |
} |
| 196 |
|
| 197 |
return bbox; |
| 198 |
} |
| 199 |
|
| 200 |
private void BuildFlags() |
| 201 |
{ |
| 202 |
SetProjectionPlane(); |
| 203 |
SetHorizontalVertical(); |
| 204 |
} |
| 205 |
|
| 206 |
private void SetHorizontalVertical() |
| 207 |
{ |
| 208 |
if (Math.Abs(Vector3.Dot(plane.Normal, Vector3.UnitY)) < 0.3420201f) |
| 209 |
flags |= GunkFlags.Vertical; |
| 210 |
else |
| 211 |
flags |= GunkFlags.Horizontal; |
| 212 |
} |
| 213 |
|
| 214 |
private void SetProjectionPlane() |
| 215 |
{ |
| 216 |
var points = new Vector3[pointIndices.Length]; |
| 217 |
|
| 218 |
for (int i = 0; i < pointIndices.Length; i++) |
| 219 |
points[i] = mesh.Points[pointIndices[i]]; |
| 220 |
|
| 221 |
float xyArea = MathHelper.Area(Project(points, PolygonProjectionPlane.XY)); |
| 222 |
float yzArea = MathHelper.Area(Project(points, PolygonProjectionPlane.YZ)); |
| 223 |
float xzArea = MathHelper.Area(Project(points, PolygonProjectionPlane.XZ)); |
| 224 |
|
| 225 |
var plane = PolygonProjectionPlane.None; |
| 226 |
|
| 227 |
if (xyArea > yzArea) |
| 228 |
{ |
| 229 |
if (xyArea > xzArea) |
| 230 |
plane = PolygonProjectionPlane.XY; |
| 231 |
else |
| 232 |
plane = PolygonProjectionPlane.XZ; |
| 233 |
} |
| 234 |
else |
| 235 |
{ |
| 236 |
if (yzArea > xzArea) |
| 237 |
plane = PolygonProjectionPlane.YZ; |
| 238 |
else |
| 239 |
plane = PolygonProjectionPlane.XZ; |
| 240 |
} |
| 241 |
|
| 242 |
flags |= (GunkFlags)((int)plane << 25); |
| 243 |
} |
| 244 |
|
| 245 |
private static Vector2[] Project(Vector3[] points, PolygonProjectionPlane plane) |
| 246 |
{ |
| 247 |
var result = new Vector2[points.Length]; |
| 248 |
|
| 249 |
switch (plane) |
| 250 |
{ |
| 251 |
case PolygonProjectionPlane.XY: |
| 252 |
for (int i = 0; i < points.Length; i++) |
| 253 |
{ |
| 254 |
result[i].X = points[i].X; |
| 255 |
result[i].Y = points[i].Y; |
| 256 |
} |
| 257 |
break; |
| 258 |
case PolygonProjectionPlane.XZ: |
| 259 |
for (int i = 0; i < points.Length; i++) |
| 260 |
{ |
| 261 |
result[i].X = points[i].X; |
| 262 |
result[i].Y = points[i].Z; |
| 263 |
} |
| 264 |
break; |
| 265 |
case PolygonProjectionPlane.YZ: |
| 266 |
for (int i = 0; i < points.Length; i++) |
| 267 |
{ |
| 268 |
result[i].X = points[i].Z; |
| 269 |
result[i].Y = points[i].Y; |
| 270 |
} |
| 271 |
break; |
| 272 |
} |
| 273 |
|
| 274 |
return result; |
| 275 |
} |
| 276 |
|
| 277 |
public PolygonEdge[] Edges |
| 278 |
{ |
| 279 |
get |
| 280 |
{ |
| 281 |
if (edges == null) |
| 282 |
{ |
| 283 |
edges = new PolygonEdge[pointIndices.Length]; |
| 284 |
|
| 285 |
for (int i = 0; i < edges.Length; i++) |
| 286 |
edges[i] = new PolygonEdge(this, i); |
| 287 |
} |
| 288 |
|
| 289 |
return edges; |
| 290 |
} |
| 291 |
} |
| 292 |
} |
| 293 |
} |