1 | using System;
|
---|
2 | using System.Collections.Generic;
|
---|
3 |
|
---|
4 | namespace Oni.Akira
|
---|
5 | {
|
---|
6 | internal struct Polygon2Clipper
|
---|
7 | {
|
---|
8 | private readonly List<Polygon2> result;
|
---|
9 | private readonly RoomBspNode bspTree;
|
---|
10 |
|
---|
11 | #region private struct Line
|
---|
12 |
|
---|
13 | private struct Line
|
---|
14 | {
|
---|
15 | private float a, c, d;
|
---|
16 |
|
---|
17 | public Line(Plane plane)
|
---|
18 | {
|
---|
19 | a = plane.Normal.X;
|
---|
20 | c = plane.Normal.Z;
|
---|
21 | d = plane.D;
|
---|
22 | }
|
---|
23 |
|
---|
24 | public int RelativePosition(Vector2 point)
|
---|
25 | {
|
---|
26 | float r = a * point.X + c * point.Y + d;
|
---|
27 |
|
---|
28 | if (r < -0.00001f)
|
---|
29 | return -1;
|
---|
30 | else if (r > 0.00001f)
|
---|
31 | return 1;
|
---|
32 | else
|
---|
33 | return 0;
|
---|
34 | }
|
---|
35 |
|
---|
36 | public Vector2 Intersect(Vector2 p0, Vector2 p1)
|
---|
37 | {
|
---|
38 | if (p0.X == p1.X)
|
---|
39 | {
|
---|
40 | float x = p0.X;
|
---|
41 | float y = (d + a * x) / -c;
|
---|
42 | return new Vector2(x, y);
|
---|
43 | }
|
---|
44 | else if (p0.Y == p1.Y)
|
---|
45 | {
|
---|
46 | float x = (-c * p0.Y - d) / a;
|
---|
47 | float y = p0.Y;
|
---|
48 | return new Vector2(x, y);
|
---|
49 | }
|
---|
50 | else
|
---|
51 | {
|
---|
52 | float m = (p1.Y - p0.Y) / (p1.X - p0.X);
|
---|
53 | float x = (c * m * p0.X - c * p0.Y - d) / (a + c * m);
|
---|
54 | float y = m * (x - p0.X) + p0.Y;
|
---|
55 | return new Vector2(x, y);
|
---|
56 | }
|
---|
57 | }
|
---|
58 | }
|
---|
59 |
|
---|
60 | #endregion
|
---|
61 |
|
---|
62 | public Polygon2Clipper(RoomBspNode bspTree)
|
---|
63 | {
|
---|
64 | this.result = new List<Polygon2>();
|
---|
65 | this.bspTree = bspTree;
|
---|
66 | }
|
---|
67 |
|
---|
68 | public IEnumerable<Polygon2> Clip(Polygon2 polygon)
|
---|
69 | {
|
---|
70 | result.Clear();
|
---|
71 | Clip(new[] { polygon }, bspTree);
|
---|
72 | return result;
|
---|
73 | }
|
---|
74 |
|
---|
75 | private void Clip(IEnumerable<Polygon2> polygons, RoomBspNode node)
|
---|
76 | {
|
---|
77 | var negative = new List<Polygon2>();
|
---|
78 | var positive = new List<Polygon2>();
|
---|
79 |
|
---|
80 | var plane = node.Plane;
|
---|
81 |
|
---|
82 | if (Math.Abs(plane.Normal.Y) > 0.001f)
|
---|
83 | {
|
---|
84 | negative.AddRange(polygons);
|
---|
85 | positive.AddRange(polygons);
|
---|
86 | }
|
---|
87 | else
|
---|
88 | {
|
---|
89 | var line = new Line(plane);
|
---|
90 |
|
---|
91 | foreach (Polygon2 polygon in polygons)
|
---|
92 | Clip(polygon, line, negative, positive);
|
---|
93 | }
|
---|
94 |
|
---|
95 | if (node.FrontChild != null)
|
---|
96 | Clip(positive, node.FrontChild);
|
---|
97 |
|
---|
98 | if (node.BackChild != null)
|
---|
99 | Clip(negative, node.BackChild);
|
---|
100 | else
|
---|
101 | result.AddRange(negative);
|
---|
102 | }
|
---|
103 |
|
---|
104 | private static void Clip(Polygon2 polygon, Line line, List<Polygon2> negative, List<Polygon2> positive)
|
---|
105 | {
|
---|
106 | var signs = new int[polygon.Length];
|
---|
107 | int positiveCount = 0, negativeCount = 0;
|
---|
108 |
|
---|
109 | for (int i = 0; i < polygon.Length; i++)
|
---|
110 | {
|
---|
111 | signs[i] = line.RelativePosition(polygon[i]);
|
---|
112 |
|
---|
113 | if (signs[i] >= 0)
|
---|
114 | positiveCount++;
|
---|
115 |
|
---|
116 | if (signs[i] <= 0)
|
---|
117 | negativeCount++;
|
---|
118 | }
|
---|
119 |
|
---|
120 | if (negativeCount == polygon.Length)
|
---|
121 | {
|
---|
122 | //
|
---|
123 | // All points are in the negative half plane, nothing to clip.
|
---|
124 | //
|
---|
125 |
|
---|
126 | negative.Add(polygon);
|
---|
127 | return;
|
---|
128 | }
|
---|
129 |
|
---|
130 | if (positiveCount == polygon.Length)
|
---|
131 | {
|
---|
132 | //
|
---|
133 | // All points are in the positive half plane, nothing to clip.
|
---|
134 | //
|
---|
135 |
|
---|
136 | positive.Add(polygon);
|
---|
137 | return;
|
---|
138 | }
|
---|
139 |
|
---|
140 | var negativePoints = new List<Vector2>();
|
---|
141 | var positivePoints = new List<Vector2>();
|
---|
142 |
|
---|
143 | int start = 0;
|
---|
144 | Vector2 p0;
|
---|
145 | int s0;
|
---|
146 |
|
---|
147 | do
|
---|
148 | {
|
---|
149 | p0 = polygon[start];
|
---|
150 | s0 = signs[start];
|
---|
151 | start++;
|
---|
152 |
|
---|
153 | //
|
---|
154 | // do not start right on the clip line
|
---|
155 | //
|
---|
156 |
|
---|
157 | } while (s0 == 0);
|
---|
158 |
|
---|
159 | var intersections = new Vector2[2];
|
---|
160 | int intersectionCount = 0;
|
---|
161 |
|
---|
162 | for (int i = 0; i < polygon.Length; i++)
|
---|
163 | {
|
---|
164 | Vector2 p1 = polygon[(i + start) % polygon.Length];
|
---|
165 | int s1 = signs[(i + start) % polygon.Length];
|
---|
166 |
|
---|
167 | if (s0 == s1)
|
---|
168 | {
|
---|
169 | //
|
---|
170 | // Same half plane, no intersection, add the existing edge.
|
---|
171 | //
|
---|
172 |
|
---|
173 | if (s0 < 0)
|
---|
174 | negativePoints.Add(p0);
|
---|
175 | else
|
---|
176 | positivePoints.Add(p0);
|
---|
177 | }
|
---|
178 | else if (s0 == 0)
|
---|
179 | {
|
---|
180 | //
|
---|
181 | // If the previous point was on the clip line then we need
|
---|
182 | // to use the current point sign to figure out the destination polygon.
|
---|
183 | //
|
---|
184 |
|
---|
185 | if (s1 < 0)
|
---|
186 | negativePoints.Add(p0);
|
---|
187 | else
|
---|
188 | positivePoints.Add(p0);
|
---|
189 | }
|
---|
190 | else
|
---|
191 | {
|
---|
192 | //
|
---|
193 | // Different half plane, split the edge in two.
|
---|
194 | //
|
---|
195 |
|
---|
196 | Vector2 intersection;
|
---|
197 |
|
---|
198 | if (s1 == 0)
|
---|
199 | intersection = p1;
|
---|
200 | else
|
---|
201 | intersection = line.Intersect(p0, p1);
|
---|
202 |
|
---|
203 | intersections[intersectionCount++] = intersection;
|
---|
204 |
|
---|
205 | if (s0 < 0)
|
---|
206 | {
|
---|
207 | // the negative polygon needs to be closed
|
---|
208 | // the positive polygon needs to have an edge added from the previous intersection to the new one
|
---|
209 |
|
---|
210 | negativePoints.Add(p0);
|
---|
211 |
|
---|
212 | if (intersectionCount == 2)
|
---|
213 | {
|
---|
214 | negativePoints.Add(intersection);
|
---|
215 | positivePoints.Add(intersections[0]);
|
---|
216 | }
|
---|
217 |
|
---|
218 | if (s1 != 0)
|
---|
219 | positivePoints.Add(intersection);
|
---|
220 | }
|
---|
221 | else
|
---|
222 | {
|
---|
223 | // the positive polygon needs to be closed
|
---|
224 | // the negative polygon needs to have an edge added from the previous intersection to the new one
|
---|
225 |
|
---|
226 | positivePoints.Add(p0);
|
---|
227 |
|
---|
228 | if (intersectionCount == 2)
|
---|
229 | {
|
---|
230 | positivePoints.Add(intersection);
|
---|
231 | negativePoints.Add(intersections[0]);
|
---|
232 | }
|
---|
233 |
|
---|
234 | if (s1 != 0)
|
---|
235 | negativePoints.Add(intersection);
|
---|
236 | }
|
---|
237 | }
|
---|
238 |
|
---|
239 | p0 = p1;
|
---|
240 | s0 = s1;
|
---|
241 | }
|
---|
242 |
|
---|
243 | negative.Add(new Polygon2(negativePoints.ToArray()));
|
---|
244 | positive.Add(new Polygon2(positivePoints.ToArray()));
|
---|
245 | }
|
---|
246 | }
|
---|
247 | }
|
---|