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 |
} |