Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(239)

Side by Side Diff: cc/quads/draw_polygon.cc

Issue 2043283002: Perform BSP polygon splitting and orientation selection in a single step. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update test expectations Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698