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
3 // found in the LICENSE file.
5 #include "cc/quads/draw_polygon.h"
9 #include "cc/output/bsp_compare_result.h"
10 #include "cc/quads/draw_quad.h"
13 // This allows for some imperfection in the normal comparison when checking if
14 // two pieces of geometry are coplanar.
15 static const float coplanar_dot_epsilon
= 0.001f
;
16 // This threshold controls how "thick" a plane is. If a point's distance is
17 // <= |compare_threshold|, then it is considered on the plane. Only when this
18 // boundary is crossed do we consider doing splitting.
19 static const float compare_threshold
= 1.0f
;
20 // |split_threshold| is lower in this case because we want the points created
21 // during splitting to be well within the range of |compare_threshold| for
22 // comparison purposes. The splitting operation will produce intersection points
23 // that fit within a tighter distance to the splitting plane as a result of this
24 // value. By using a value >= |compare_threshold| we run the risk of creating
25 // points that SHOULD be intersecting the "thick plane", but actually fail to
26 // test positively for it because |split_threshold| allowed them to be outside
28 // This is really supposd to be compare_threshold / 2.0f, but that would
29 // create another static initializer.
30 static const float split_threshold
= 0.5f
;
32 static const float normalized_threshold
= 0.001f
;
37 DrawPolygon::DrawPolygon() {
40 DrawPolygon::DrawPolygon(const DrawQuad
* original
,
41 const std::vector
<gfx::Point3F
>& in_points
,
42 const gfx::Vector3dF
& normal
,
44 : order_index_(draw_order_index
), original_ref_(original
), is_split_(true) {
45 for (size_t i
= 0; i
< in_points
.size(); i
++) {
46 points_
.push_back(in_points
[i
]);
51 // This takes the original DrawQuad that this polygon should be based on,
52 // a visible content rect to make the 4 corner points from, and a transformation
53 // to move it and its normal into screen space.
54 DrawPolygon::DrawPolygon(const DrawQuad
* original_ref
,
55 const gfx::RectF
& visible_layer_rect
,
56 const gfx::Transform
& transform
,
58 : normal_(0.0f
, 0.0f
, 1.0f
),
59 order_index_(draw_order_index
),
60 original_ref_(original_ref
),
62 gfx::Point3F points
[8];
63 int num_vertices_in_clipped_quad
;
64 gfx::QuadF
send_quad(visible_layer_rect
);
66 // Doing this mapping here is very important, since we can't just transform
67 // the points without clipping and not run into strange geometry issues when
68 // crossing w = 0. At this point, in the constructor, we know that we're
69 // working with a quad, so we can reuse the MathUtil::MapClippedQuad3d
70 // function instead of writing a generic polygon version of it.
71 MathUtil::MapClippedQuad3d(
72 transform
, send_quad
, points
, &num_vertices_in_clipped_quad
);
73 for (int i
= 0; i
< num_vertices_in_clipped_quad
; i
++) {
74 points_
.push_back(points
[i
]);
76 ApplyTransformToNormal(transform
);
79 DrawPolygon::~DrawPolygon() {
82 scoped_ptr
<DrawPolygon
> DrawPolygon::CreateCopy() {
83 scoped_ptr
<DrawPolygon
> new_polygon(new DrawPolygon());
84 new_polygon
->order_index_
= order_index_
;
85 new_polygon
->original_ref_
= original_ref_
;
86 new_polygon
->points_
.reserve(points_
.size());
87 new_polygon
->points_
= points_
;
88 new_polygon
->normal_
.set_x(normal_
.x());
89 new_polygon
->normal_
.set_y(normal_
.y());
90 new_polygon
->normal_
.set_z(normal_
.z());
91 return new_polygon
.Pass();
94 float DrawPolygon::SignedPointDistance(const gfx::Point3F
& point
) const {
95 return gfx::DotProduct(point
- points_
[0], normal_
);
98 // Checks whether or not shape a lies on the front or back side of b, or
99 // whether they should be considered coplanar. If on the back side, we
100 // say A_BEFORE_B because it should be drawn in that order.
101 // Assumes that layers are split and there are no intersecting planes.
102 BspCompareResult
DrawPolygon::SideCompare(const DrawPolygon
& a
,
103 const DrawPolygon
& b
) {
104 // Let's make sure that both of these are normalized.
105 DCHECK_GE(normalized_threshold
, std::abs(a
.normal_
.LengthSquared() - 1.0f
));
106 DCHECK_GE(normalized_threshold
, std::abs(b
.normal_
.LengthSquared() - 1.0f
));
107 // Right away let's check if they're coplanar
108 double dot
= gfx::DotProduct(a
.normal_
, b
.normal_
);
110 bool normal_match
= false;
111 // This check assumes that the normals are normalized.
112 if (std::abs(dot
) >= 1.0f
- coplanar_dot_epsilon
) {
114 // The normals are matching enough that we only have to test one point.
115 sign
= b
.SignedPointDistance(a
.points_
[0]);
116 // Is it on either side of the splitter?
117 if (sign
< -compare_threshold
) {
121 if (sign
> compare_threshold
) {
125 // No it wasn't, so the sign of the dot product of the normals
126 // along with document order determines which side it goes on.
128 if (a
.order_index_
< b
.order_index_
) {
129 return BSP_COPLANAR_FRONT
;
131 return BSP_COPLANAR_BACK
;
134 if (a
.order_index_
< b
.order_index_
) {
135 return BSP_COPLANAR_BACK
;
137 return BSP_COPLANAR_FRONT
;
142 for (size_t i
= 0; i
< a
.points_
.size(); i
++) {
143 if (!normal_match
|| (normal_match
&& i
> 0)) {
144 sign
= gfx::DotProduct(a
.points_
[i
] - b
.points_
[0], b
.normal_
);
147 if (sign
< -compare_threshold
) {
149 } else if (sign
> compare_threshold
) {
153 if (pos_count
&& neg_count
) {
164 static bool LineIntersectPlane(const gfx::Point3F
& line_start
,
165 const gfx::Point3F
& line_end
,
166 const gfx::Point3F
& plane_origin
,
167 const gfx::Vector3dF
& plane_normal
,
168 gfx::Point3F
* intersection
,
169 float distance_threshold
) {
170 gfx::Vector3dF start_to_origin_vector
= plane_origin
- line_start
;
171 gfx::Vector3dF end_to_origin_vector
= plane_origin
- line_end
;
173 double start_distance
= gfx::DotProduct(start_to_origin_vector
, plane_normal
);
174 double end_distance
= gfx::DotProduct(end_to_origin_vector
, plane_normal
);
176 // The case where one vertex lies on the thick-plane and the other
178 if (std::abs(start_distance
) <= distance_threshold
&&
179 std::abs(end_distance
) > distance_threshold
) {
180 intersection
->SetPoint(line_start
.x(), line_start
.y(), line_start
.z());
184 // This is the case where we clearly cross the thick-plane.
185 if ((start_distance
> distance_threshold
&&
186 end_distance
< -distance_threshold
) ||
187 (start_distance
< -distance_threshold
&&
188 end_distance
> distance_threshold
)) {
189 gfx::Vector3dF v
= line_end
- line_start
;
190 float total_distance
= std::abs(start_distance
) + std::abs(end_distance
);
191 float lerp_factor
= std::abs(start_distance
) / total_distance
;
193 intersection
->SetPoint(line_start
.x() + (v
.x() * lerp_factor
),
194 line_start
.y() + (v
.y() * lerp_factor
),
195 line_start
.z() + (v
.z() * lerp_factor
));
202 // This function is separate from ApplyTransform because it is often unnecessary
203 // to transform the normal with the rest of the polygon.
204 // When drawing these polygons, it is necessary to move them back into layer
205 // space before sending them to OpenGL, which requires using ApplyTransform,
206 // but normal information is no longer needed after sorting.
207 void DrawPolygon::ApplyTransformToNormal(const gfx::Transform
& transform
) {
208 // Now we use the inverse transpose of |transform| to transform the normal.
209 gfx::Transform inverse_transform
;
210 bool inverted
= transform
.GetInverse(&inverse_transform
);
214 inverse_transform
.Transpose();
216 gfx::Point3F
new_normal(normal_
.x(), normal_
.y(), normal_
.z());
217 inverse_transform
.TransformPoint(&new_normal
);
218 // Make sure our normal is still normalized.
219 normal_
= gfx::Vector3dF(new_normal
.x(), new_normal
.y(), new_normal
.z());
220 float normal_magnitude
= normal_
.Length();
221 if (normal_magnitude
!= 0 && normal_magnitude
!= 1) {
222 normal_
.Scale(1.0f
/ normal_magnitude
);
226 void DrawPolygon::ApplyTransform(const gfx::Transform
& transform
) {
227 for (size_t i
= 0; i
< points_
.size(); i
++) {
228 transform
.TransformPoint(&points_
[i
]);
232 // TransformToScreenSpace assumes we're moving a layer from its layer space
233 // into 3D screen space, which for sorting purposes requires the normal to
234 // be transformed along with the vertices.
235 void DrawPolygon::TransformToScreenSpace(const gfx::Transform
& transform
) {
236 ApplyTransform(transform
);
237 ApplyTransformToNormal(transform
);
240 // In the case of TransformToLayerSpace, we assume that we are giving the
241 // inverse transformation back to the polygon to move it back into layer space
242 // but we can ignore the costly process of applying the inverse to the normal
243 // since we know the normal will just reset to its original state.
244 void DrawPolygon::TransformToLayerSpace(
245 const gfx::Transform
& inverse_transform
) {
246 ApplyTransform(inverse_transform
);
247 normal_
= gfx::Vector3dF(0.0f
, 0.0f
, -1.0f
);
250 bool DrawPolygon::Split(const DrawPolygon
& splitter
,
251 scoped_ptr
<DrawPolygon
>* front
,
252 scoped_ptr
<DrawPolygon
>* back
) {
253 gfx::Point3F intersections
[2];
254 std::vector
<gfx::Point3F
> out_points
[2];
255 // vertex_before stores the index of the vertex before its matching
257 // i.e. vertex_before[0] stores the vertex we saw before we crossed the plane
258 // which resulted in the line/plane intersection giving us intersections[0].
259 size_t vertex_before
[2];
260 size_t points_size
= points_
.size();
261 size_t current_intersection
= 0;
263 size_t current_vertex
= 0;
264 // We will only have two intersection points because we assume all polygons
266 while (current_intersection
< 2) {
267 if (LineIntersectPlane(points_
[(current_vertex
% points_size
)],
268 points_
[(current_vertex
+ 1) % points_size
],
271 &intersections
[current_intersection
],
273 vertex_before
[current_intersection
] = current_vertex
% points_size
;
274 current_intersection
++;
275 // We found both intersection points so we're done already.
276 if (current_intersection
== 2) {
280 if (current_vertex
++ > (points_size
)) {
284 DCHECK_EQ(current_intersection
, static_cast<size_t>(2));
286 // Since we found both the intersection points, we can begin building the
287 // vertex set for both our new polygons.
288 size_t start1
= (vertex_before
[0] + 1) % points_size
;
289 size_t start2
= (vertex_before
[1] + 1) % points_size
;
290 size_t points_remaining
= points_size
;
293 out_points
[0].push_back(intersections
[0]);
294 DCHECK_GE(vertex_before
[1], start1
);
295 for (size_t i
= start1
; i
<= vertex_before
[1]; i
++) {
296 out_points
[0].push_back(points_
[i
]);
299 out_points
[0].push_back(intersections
[1]);
302 out_points
[1].push_back(intersections
[1]);
303 size_t index
= start2
;
304 for (size_t i
= 0; i
< points_remaining
; i
++) {
305 out_points
[1].push_back(points_
[index
% points_size
]);
308 out_points
[1].push_back(intersections
[0]);
310 // Give both polygons the original splitting polygon's ID, so that they'll
311 // still be sorted properly in co-planar instances.
312 scoped_ptr
<DrawPolygon
> poly1(
313 new DrawPolygon(original_ref_
, out_points
[0], normal_
, order_index_
));
314 scoped_ptr
<DrawPolygon
> poly2(
315 new DrawPolygon(original_ref_
, out_points
[1], normal_
, order_index_
));
317 DCHECK_GE(poly1
->points().size(), 3u);
318 DCHECK_GE(poly2
->points().size(), 3u);
320 if (SideCompare(*poly1
, splitter
) == BSP_FRONT
) {
321 *front
= poly1
.Pass();
322 *back
= poly2
.Pass();
324 *front
= poly2
.Pass();
325 *back
= poly1
.Pass();
330 // This algorithm takes the first vertex in the polygon and uses that as a
331 // pivot point to fan out and create quads from the rest of the vertices.
332 // |offset| starts off as the second vertex, and then |op1| and |op2| indicate
333 // offset+1 and offset+2 respectively.
334 // After the first quad is created, the first vertex in the next quad is the
335 // same as all the rest, the pivot point. The second vertex in the next quad is
336 // the old |op2|, the last vertex added to the previous quad. This continues
337 // until all points are exhausted.
338 // The special case here is where there are only 3 points remaining, in which
339 // case we use the same values for vertex 3 and 4 to make a degenerate quad
340 // that represents a triangle.
341 void DrawPolygon::ToQuads2D(std::vector
<gfx::QuadF
>* quads
) const {
342 if (points_
.size() <= 2)
345 gfx::PointF
first(points_
[0].x(), points_
[0].y());
347 while (offset
< points_
.size() - 1) {
348 size_t op1
= offset
+ 1;
349 size_t op2
= offset
+ 2;
350 if (op2
>= points_
.size()) {
351 // It's going to be a degenerate triangle.
356 gfx::PointF(points_
[offset
].x(), points_
[offset
].y()),
357 gfx::PointF(points_
[op1
].x(), points_
[op1
].y()),
358 gfx::PointF(points_
[op2
].x(), points_
[op2
].y())));