OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "cc/quads/draw_polygon.h" | 5 #include "cc/quads/draw_polygon.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/memory/ptr_util.h" | |
11 #include "cc/output/bsp_compare_result.h" | 12 #include "cc/output/bsp_compare_result.h" |
12 #include "cc/quads/draw_quad.h" | 13 #include "cc/quads/draw_quad.h" |
13 | 14 |
14 namespace { | 15 namespace { |
15 // This threshold controls how "thick" a plane is. If a point's distance is | 16 // This threshold controls how "thick" a plane is. If a point's distance is |
16 // <= |compare_threshold|, then it is considered on the plane. Only when this | 17 // <= |compare_threshold|, then it is considered on the plane. Only when this |
17 // boundary is crossed do we consider doing splitting. | 18 // boundary is crossed do we consider doing splitting. |
18 static const float compare_threshold = 0.1f; | 19 static const float compare_threshold = 0.1f; |
19 // |split_threshold| is lower in this case because we want the points created | 20 // |split_threshold| is lower in this case because we want the points created |
20 // during splitting to be well within the range of |compare_threshold| for | 21 // during splitting to be well within the range of |compare_threshold| for |
21 // comparison purposes. The splitting operation will produce intersection points | 22 // comparison purposes. The splitting operation will produce intersection points |
22 // that fit within a tighter distance to the splitting plane as a result of this | 23 // that fit within a tighter distance to the splitting plane as a result of this |
23 // value. By using a value >= |compare_threshold| we run the risk of creating | 24 // value. By using a value >= |compare_threshold| we run the risk of creating |
24 // points that SHOULD be intersecting the "thick plane", but actually fail to | 25 // points that SHOULD be intersecting the "thick plane", but actually fail to |
25 // test positively for it because |split_threshold| allowed them to be outside | 26 // test positively for it because |split_threshold| allowed them to be outside |
26 // this range. | 27 // this range. |
27 // This is really supposd to be compare_threshold / 2.0f, but that would | 28 // This is really supposd to be compare_threshold / 2.0f, but that would |
28 // create another static initializer. | 29 // create another static initializer. |
29 static const float split_threshold = 0.05f; | 30 static const float split_threshold = 0.05f; |
30 | 31 |
31 static const float normalized_threshold = 0.001f; | 32 static const float normalized_threshold = 0.001f; |
33 | |
34 void PointInterpolate(const gfx::Point3F& from, | |
35 const gfx::Point3F& to, | |
36 double delta, | |
37 gfx::Point3F* out) { | |
38 out->SetPoint(from.x() + (to.x() - from.x()) * delta, | |
39 from.y() + (to.y() - from.y()) * delta, | |
40 from.z() + (to.z() - from.z()) * delta); | |
41 } | |
32 } // namespace | 42 } // namespace |
33 | 43 |
34 namespace cc { | 44 namespace cc { |
35 | 45 |
36 DrawPolygon::DrawPolygon() { | 46 DrawPolygon::DrawPolygon() { |
37 } | 47 } |
38 | 48 |
39 DrawPolygon::DrawPolygon(const DrawQuad* original, | 49 DrawPolygon::DrawPolygon(const DrawQuad* original, |
40 const std::vector<gfx::Point3F>& in_points, | 50 const std::vector<gfx::Point3F>& in_points, |
41 const gfx::Vector3dF& normal, | 51 const gfx::Vector3dF& normal, |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
129 #endif | 139 #endif |
130 | 140 |
131 float DrawPolygon::SignedPointDistance(const gfx::Point3F& point) const { | 141 float DrawPolygon::SignedPointDistance(const gfx::Point3F& point) const { |
132 return gfx::DotProduct(point - points_[0], normal_); | 142 return gfx::DotProduct(point - points_[0], normal_); |
133 } | 143 } |
134 | 144 |
135 // Checks whether or not shape a lies on the front or back side of b, or | 145 // Checks whether or not shape a lies on the front or back side of b, or |
136 // whether they should be considered coplanar. If on the back side, we | 146 // whether they should be considered coplanar. If on the back side, we |
137 // say A_BEFORE_B because it should be drawn in that order. | 147 // say A_BEFORE_B because it should be drawn in that order. |
138 // Assumes that layers are split and there are no intersecting planes. | 148 // Assumes that layers are split and there are no intersecting planes. |
139 BspCompareResult DrawPolygon::SideCompare(const DrawPolygon& a, | 149 BspCompareResult DrawPolygon::SideCompare(const DrawPolygon& a, |
enne (OOO)
2016/06/08 22:02:51
Is this function used anymore?
Tobias Sargeant
2016/06/09 16:48:44
Only in tests.
| |
140 const DrawPolygon& b) { | 150 const DrawPolygon& b) { |
141 // Let's make sure that this is normalized. Without this SignedPointDistance | 151 // Let's make sure that this is normalized. Without this SignedPointDistance |
142 // will not be right, but putting the check in there will validate it | 152 // will not be right, but putting the check in there will validate it |
143 // redundantly for each point. | 153 // redundantly for each point. |
144 DCHECK_GE(normalized_threshold, std::abs(b.normal_.LengthSquared() - 1.0f)); | 154 DCHECK_GE(normalized_threshold, std::abs(b.normal_.LengthSquared() - 1.0f)); |
145 | 155 |
146 int pos_count = 0; | 156 int pos_count = 0; |
147 int neg_count = 0; | 157 int neg_count = 0; |
148 for (size_t i = 0; i < a.points_.size(); i++) { | 158 for (size_t i = 0; i < a.points_.size(); i++) { |
149 float sign = b.SignedPointDistance(a.points_[i]); | 159 float sign = b.SignedPointDistance(a.points_[i]); |
(...skipping 20 matching lines...) Expand all Loading... | |
170 if ((dot >= 0.0f && a.order_index_ >= b.order_index_) || | 180 if ((dot >= 0.0f && a.order_index_ >= b.order_index_) || |
171 (dot <= 0.0f && a.order_index_ <= b.order_index_)) { | 181 (dot <= 0.0f && a.order_index_ <= b.order_index_)) { |
172 // The sign of the dot product of the normals along with document order | 182 // The sign of the dot product of the normals along with document order |
173 // determine which side it goes on, the vertices are ambiguous. | 183 // determine which side it goes on, the vertices are ambiguous. |
174 return BSP_COPLANAR_BACK; | 184 return BSP_COPLANAR_BACK; |
175 } | 185 } |
176 | 186 |
177 return BSP_COPLANAR_FRONT; | 187 return BSP_COPLANAR_FRONT; |
178 } | 188 } |
179 | 189 |
180 static bool LineIntersectPlane(const gfx::Point3F& line_start, | |
181 const gfx::Point3F& line_end, | |
182 const gfx::Point3F& plane_origin, | |
183 const gfx::Vector3dF& plane_normal, | |
184 gfx::Point3F* intersection, | |
185 float distance_threshold) { | |
186 const gfx::Vector3dF line_start_vec(line_start.x(), line_start.y(), | |
187 line_start.z()); | |
188 const gfx::Vector3dF line_end_vec(line_end.x(), line_end.y(), line_end.z()); | |
189 const gfx::Vector3dF plane_origin_vec(plane_origin.x(), plane_origin.y(), | |
190 plane_origin.z()); | |
191 | |
192 double plane_d = -gfx::DotProduct(plane_origin_vec, plane_normal); | |
193 | |
194 double end_distance = gfx::DotProduct(line_end_vec, plane_normal) + plane_d; | |
195 if (std::abs(end_distance) <= distance_threshold) { | |
196 // No intersection if |line_end| is within |distance_threshold| of plane. | |
197 return false; | |
198 } | |
199 | |
200 double start_distance = | |
201 gfx::DotProduct(line_start_vec, plane_normal) + plane_d; | |
202 if (std::abs(start_distance) <= distance_threshold) { | |
203 // Intersection at |line_start| if |line_start| is within | |
204 // |distance_threshold| of plane. | |
205 intersection->SetPoint(line_start.x(), line_start.y(), line_start.z()); | |
206 return true; | |
207 } | |
208 | |
209 // If signs differ, we cross the plane. | |
210 if (start_distance * end_distance < 0.0) { | |
211 // Plane: P . N + d = 0 [ d = -(plane_normal . plane_origin) ] | |
212 // Ray: P = P0 + Pd * t [ P0 = line_start, Pd = line_end - line_start ] | |
213 // Substituting: | |
214 // (P0 + Pd * t) . N + d = 0 | |
215 // P0 . N + t * Pd . N + d = 0 | |
216 // t = -(P0 . N + d) / Pd . N | |
217 | |
218 gfx::Vector3dF line_delta = line_end - line_start; | |
219 double t = -start_distance / gfx::DotProduct(plane_normal, line_delta); | |
220 intersection->SetPoint(line_start.x() + line_delta.x() * t, | |
221 line_start.y() + line_delta.y() * t, | |
222 line_start.z() + line_delta.z() * t); | |
223 | |
224 return true; | |
225 } | |
226 return false; | |
227 } | |
228 | |
229 // This function is separate from ApplyTransform because it is often unnecessary | 190 // This function is separate from ApplyTransform because it is often unnecessary |
230 // to transform the normal with the rest of the polygon. | 191 // to transform the normal with the rest of the polygon. |
231 // When drawing these polygons, it is necessary to move them back into layer | 192 // When drawing these polygons, it is necessary to move them back into layer |
232 // space before sending them to OpenGL, which requires using ApplyTransform, | 193 // space before sending them to OpenGL, which requires using ApplyTransform, |
233 // but normal information is no longer needed after sorting. | 194 // but normal information is no longer needed after sorting. |
234 void DrawPolygon::ApplyTransformToNormal(const gfx::Transform& transform) { | 195 void DrawPolygon::ApplyTransformToNormal(const gfx::Transform& transform) { |
235 // Now we use the inverse transpose of |transform| to transform the normal. | 196 // Now we use the inverse transpose of |transform| to transform the normal. |
236 gfx::Transform inverse_transform; | 197 gfx::Transform inverse_transform; |
237 bool inverted = transform.GetInverse(&inverse_transform); | 198 bool inverted = transform.GetInverse(&inverse_transform); |
238 DCHECK(inverted); | 199 DCHECK(inverted); |
(...skipping 29 matching lines...) Expand all Loading... | |
268 // In the case of TransformToLayerSpace, we assume that we are giving the | 229 // In the case of TransformToLayerSpace, we assume that we are giving the |
269 // inverse transformation back to the polygon to move it back into layer space | 230 // inverse transformation back to the polygon to move it back into layer space |
270 // but we can ignore the costly process of applying the inverse to the normal | 231 // but we can ignore the costly process of applying the inverse to the normal |
271 // since we know the normal will just reset to its original state. | 232 // since we know the normal will just reset to its original state. |
272 void DrawPolygon::TransformToLayerSpace( | 233 void DrawPolygon::TransformToLayerSpace( |
273 const gfx::Transform& inverse_transform) { | 234 const gfx::Transform& inverse_transform) { |
274 ApplyTransform(inverse_transform); | 235 ApplyTransform(inverse_transform); |
275 normal_ = gfx::Vector3dF(0.0f, 0.0f, -1.0f); | 236 normal_ = gfx::Vector3dF(0.0f, 0.0f, -1.0f); |
276 } | 237 } |
277 | 238 |
278 bool DrawPolygon::Split(const DrawPolygon& splitter, | 239 // Split |polygon| based upon |this|, leaving the results in |front| and |back|. |
279 std::unique_ptr<DrawPolygon>* front, | 240 // If |polygon| is not split by |this|, then move it to either |front| or |back| |
280 std::unique_ptr<DrawPolygon>* back) { | 241 // depending on its orientation relative to |this|. Return true if |polygon| is |
281 gfx::Point3F intersections[2]; | 242 // actually coplanar with |this| (in which case whether it is front facing or |
282 std::vector<gfx::Point3F> out_points[2]; | 243 // back facing is determined by the dot products of normals, and document |
283 // vertex_before stores the index of the vertex before its matching | 244 // order). |
284 // intersection. | 245 bool DrawPolygon::SplitPolygon(std::unique_ptr<DrawPolygon> polygon, |
285 // i.e. vertex_before[0] stores the vertex we saw before we crossed the plane | 246 std::unique_ptr<DrawPolygon>* front, |
286 // which resulted in the line/plane intersection giving us intersections[0]. | 247 std::unique_ptr<DrawPolygon>* back) { |
287 size_t vertex_before[2]; | 248 DCHECK_GE(normalized_threshold, std::abs(normal_.LengthSquared() - 1.0f)); |
288 size_t points_size = points_.size(); | |
289 size_t current_intersection = 0; | |
290 | 249 |
291 size_t current_vertex = 0; | 250 const size_t N_points = polygon->points_.size(); |
enne (OOO)
2016/06/08 22:02:51
N_points isn't valid chromium style.
I think you'
Tobias Sargeant
2016/06/09 16:48:44
Done.
| |
292 // We will only have two intersection points because we assume all polygons | 251 const auto next = [N_points](size_t i) { |
293 // are convex. | 252 return (i + 1) % N_points; |
294 while (current_intersection < 2) { | 253 }; |
295 if (LineIntersectPlane(points_[(current_vertex % points_size)], | 254 const auto prev = [N_points](size_t i) { |
296 points_[(current_vertex + 1) % points_size], | 255 return (i + N_points - 1) % N_points; |
297 splitter.points_[0], | 256 }; |
298 splitter.normal_, | 257 |
299 &intersections[current_intersection], | 258 std::vector<float> vertex_distance; |
300 split_threshold)) { | 259 size_t pos_count = 0; |
301 vertex_before[current_intersection] = current_vertex % points_size; | 260 size_t neg_count = 0; |
302 current_intersection++; | 261 |
303 // We found both intersection points so we're done already. | 262 // Compute plane distances for each vertex of polygon. |
304 if (current_intersection == 2) { | 263 vertex_distance.resize(N_points); |
305 break; | 264 for (size_t i = 0; i < N_points; i++) { |
306 } | 265 vertex_distance[i] = SignedPointDistance(polygon->points_[i]); |
307 } | 266 if (vertex_distance[i] < -split_threshold) { |
308 if (current_vertex++ > (points_size)) { | 267 ++neg_count; |
309 break; | 268 } else if (vertex_distance[i] > split_threshold) { |
269 ++pos_count; | |
270 } else { | |
271 vertex_distance[i] = 0.0; | |
310 } | 272 } |
311 } | 273 } |
312 DCHECK_EQ(current_intersection, static_cast<size_t>(2)); | |
313 | 274 |
314 // Since we found both the intersection points, we can begin building the | 275 // Handle non-splitting cases. |
315 // vertex set for both our new polygons. | 276 if (!pos_count && !neg_count) { |
316 size_t start1 = (vertex_before[0] + 1) % points_size; | 277 // |polygon| is coplanar with |this|. |
317 size_t start2 = (vertex_before[1] + 1) % points_size; | 278 double dot = gfx::DotProduct(normal_, polygon->normal_); |
318 size_t points_remaining = points_size; | 279 if ((dot >= 0.0f && polygon->order_index_ >= order_index_) || |
280 (dot <= 0.0f && polygon->order_index_ <= order_index_)) { | |
281 *back = std::move(polygon); | |
282 } else { | |
283 *front = std::move(polygon); | |
284 } | |
285 return true; | |
286 } else if (!neg_count) { | |
287 *front = std::move(polygon); | |
288 return false; | |
289 } else if (!pos_count) { | |
290 *back = std::move(polygon); | |
291 return false; | |
292 } | |
319 | 293 |
320 // First polygon. | 294 // Handle splitting case. |
321 out_points[0].push_back(intersections[0]); | 295 size_t front_begin; |
322 DCHECK_GE(vertex_before[1], start1); | 296 size_t back_begin; |
323 for (size_t i = start1; i <= vertex_before[1]; i++) { | 297 size_t pre_front_begin; |
324 out_points[0].push_back(points_[i]); | 298 size_t pre_back_begin; |
325 --points_remaining; | |
326 } | |
327 out_points[0].push_back(intersections[1]); | |
328 | 299 |
329 // Second polygon. | 300 // Find the first vertex that is part of the front split polygon. |
330 out_points[1].push_back(intersections[1]); | 301 front_begin = std::find_if(vertex_distance.begin(), vertex_distance.end(), |
331 size_t index = start2; | 302 [](float val) { return val > 0.0; }) - |
332 for (size_t i = 0; i < points_remaining; i++) { | 303 vertex_distance.begin(); |
333 out_points[1].push_back(points_[index % points_size]); | 304 while (vertex_distance[pre_front_begin = prev(front_begin)] > 0.0) |
334 ++index; | 305 front_begin = pre_front_begin; |
335 } | |
336 out_points[1].push_back(intersections[0]); | |
337 | 306 |
338 // Give both polygons the original splitting polygon's ID, so that they'll | 307 // Find the first vertex that is part of the back split polygon. |
339 // still be sorted properly in co-planar instances. | 308 back_begin = std::find_if(vertex_distance.begin(), vertex_distance.end(), |
340 std::unique_ptr<DrawPolygon> poly1( | 309 [](float val) { return val < 0.0; }) - |
341 new DrawPolygon(original_ref_, out_points[0], normal_, order_index_)); | 310 vertex_distance.begin(); |
342 std::unique_ptr<DrawPolygon> poly2( | 311 while (vertex_distance[pre_back_begin = prev(back_begin)] < 0.0) |
343 new DrawPolygon(original_ref_, out_points[1], normal_, order_index_)); | 312 back_begin = pre_back_begin; |
344 | 313 |
345 DCHECK_GE(poly1->points().size(), 3u); | 314 DCHECK(vertex_distance[front_begin] > 0.0); |
346 DCHECK_GE(poly2->points().size(), 3u); | 315 DCHECK(vertex_distance[pre_front_begin] <= 0.0); |
316 DCHECK(vertex_distance[back_begin] < 0.0); | |
317 DCHECK(vertex_distance[pre_back_begin] >= 0.0); | |
347 | 318 |
348 if (SideCompare(*poly1, splitter) == BSP_FRONT) { | 319 gfx::Point3F pre_pos_intersection; |
349 *front = std::move(poly1); | 320 gfx::Point3F pre_neg_intersection; |
350 *back = std::move(poly2); | 321 |
351 } else { | 322 // Compute the intersection points. N.B.: If the "pre" vertex is on |
352 *front = std::move(poly2); | 323 // the thick plane, then the intersection will be at the same point, because |
353 *back = std::move(poly1); | 324 // we set vertex_distance to 0 in this case. |
354 } | 325 PointInterpolate( |
355 return true; | 326 polygon->points_[pre_front_begin], polygon->points_[front_begin], |
327 -vertex_distance[pre_front_begin] / | |
328 gfx::DotProduct(normal_, polygon->points_[front_begin] - | |
329 polygon->points_[pre_front_begin]), | |
330 &pre_pos_intersection); | |
331 PointInterpolate( | |
332 polygon->points_[pre_back_begin], polygon->points_[back_begin], | |
333 -vertex_distance[pre_back_begin] / | |
334 gfx::DotProduct(normal_, polygon->points_[back_begin] - | |
335 polygon->points_[pre_back_begin]), | |
336 &pre_neg_intersection); | |
337 | |
338 // Build the front and back polygons. | |
339 std::vector<gfx::Point3F> out_points; | |
340 | |
341 out_points.push_back(pre_pos_intersection); | |
342 do { | |
343 out_points.push_back(polygon->points_[front_begin]); | |
344 front_begin = next(front_begin); | |
345 } while (vertex_distance[front_begin] > 0.0); | |
346 out_points.push_back(pre_neg_intersection); | |
347 *front = | |
348 base::MakeUnique<DrawPolygon>(polygon->original_ref_, out_points, | |
349 polygon->normal_, polygon->order_index_); | |
350 | |
351 out_points.clear(); | |
352 | |
353 out_points.push_back(pre_neg_intersection); | |
354 do { | |
355 out_points.push_back(polygon->points_[back_begin]); | |
356 back_begin = next(back_begin); | |
357 } while (vertex_distance[back_begin] < 0.0); | |
358 out_points.push_back(pre_pos_intersection); | |
359 *back = | |
360 base::MakeUnique<DrawPolygon>(polygon->original_ref_, out_points, | |
361 polygon->normal_, polygon->order_index_); | |
362 | |
363 return false; | |
enne (OOO)
2016/06/08 22:02:51
Can you add some dchecks that out_points in both c
enne (OOO)
2016/06/08 22:02:51
Can you add some dchecks that out_points in both c
Tobias Sargeant
2016/06/09 16:48:44
Yes, but that latter condition is actually wrong i
enne (OOO)
2016/06/09 17:11:26
Yeah, that was my concern--I didn't want to lose v
| |
364 } | |
365 | |
366 // Convenience overload for testing purposes. | |
367 bool DrawPolygon::SplitPolygon(const DrawPolygon& polygon, | |
368 std::unique_ptr<DrawPolygon>* front, | |
369 std::unique_ptr<DrawPolygon>* back) { | |
370 return SplitPolygon( | |
371 base::MakeUnique<DrawPolygon>(polygon.original_ref_, polygon.points_, | |
372 polygon.normal_, polygon.order_index_), | |
373 front, back); | |
356 } | 374 } |
357 | 375 |
358 // This algorithm takes the first vertex in the polygon and uses that as a | 376 // This algorithm takes the first vertex in the polygon and uses that as a |
359 // pivot point to fan out and create quads from the rest of the vertices. | 377 // pivot point to fan out and create quads from the rest of the vertices. |
360 // |offset| starts off as the second vertex, and then |op1| and |op2| indicate | 378 // |offset| starts off as the second vertex, and then |op1| and |op2| indicate |
361 // offset+1 and offset+2 respectively. | 379 // offset+1 and offset+2 respectively. |
362 // After the first quad is created, the first vertex in the next quad is the | 380 // After the first quad is created, the first vertex in the next quad is the |
363 // same as all the rest, the pivot point. The second vertex in the next quad is | 381 // same as all the rest, the pivot point. The second vertex in the next quad is |
364 // the old |op2|, the last vertex added to the previous quad. This continues | 382 // the old |op2|, the last vertex added to the previous quad. This continues |
365 // until all points are exhausted. | 383 // until all points are exhausted. |
(...skipping 16 matching lines...) Expand all Loading... | |
382 quads->push_back( | 400 quads->push_back( |
383 gfx::QuadF(first, | 401 gfx::QuadF(first, |
384 gfx::PointF(points_[offset].x(), points_[offset].y()), | 402 gfx::PointF(points_[offset].x(), points_[offset].y()), |
385 gfx::PointF(points_[op1].x(), points_[op1].y()), | 403 gfx::PointF(points_[op1].x(), points_[op1].y()), |
386 gfx::PointF(points_[op2].x(), points_[op2].y()))); | 404 gfx::PointF(points_[op2].x(), points_[op2].y()))); |
387 offset = op2; | 405 offset = op2; |
388 } | 406 } |
389 } | 407 } |
390 | 408 |
391 } // namespace cc | 409 } // namespace cc |
OLD | NEW |