source: OniSplit/Akira/Polygon2Clipper.cs@ 1114

Last change on this file since 1114 was 1114, checked in by iritscen, 5 years ago

Adding OniSplit source code (v0.9.99.0). Many thanks to Neo for all his work over the years.

File size: 7.4 KB
Line 
1using System;
2using System.Collections.Generic;
3
4namespace 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}
Note: See TracBrowser for help on using the repository browser.