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

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

Issue 411793002: DrawPolygon class with Unit Tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 4 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
(Empty)
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.
4
5 #include "cc/quads/draw_polygon.h"
6
7 #include <vector>
8
9 #include "cc/output/bsp_compare_result.h"
10
11 namespace {
12 // This allows for some imperfection in the normal comparison when checking if
13 // two pieces of geometry are coplanar.
14 static const float coplanar_dot_epsilon = 0.01f;
15 // 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 // boundary is crossed do we consider doing splitting.
18 static const float compare_threshold = 1.0f;
19 static const float split_threshold = 0.5f;
enne (OOO) 2014/07/28 21:01:13 Can you leave a comment about this number too?
troyhildebrandt 2014/07/28 21:24:56 Done.
20 } // namespace
21
22 namespace cc {
23
24 DrawPolygon::DrawPolygon() {
25 }
26
27 DrawPolygon::DrawPolygon(DrawQuad* original,
28 const std::vector<gfx::Point3F>& in_points,
29 int draw_order_index)
30 : order_index_(draw_order_index), original_ref_(original) {
31 for (unsigned int i = 0; i < in_points.size(); i++) {
enne (OOO) 2014/07/28 20:46:40 unsigned int => size_t
troyhildebrandt 2014/07/28 21:24:55 Done.
32 points_.push_back(in_points[i]);
33 }
34 normal_ = gfx::Vector3dF(0.0f, 0.0f, -1.0f);
35 }
36
37 DrawPolygon::~DrawPolygon() {
38 }
39
40 void DrawPolygon::SetNormal(const gfx::Vector3dF& normal) {
41 normal_ = normal;
42 }
43
44 scoped_ptr<DrawPolygon> DrawPolygon::CreateCopy() {
45 DrawPolygon* new_polygon = new DrawPolygon();
46 new_polygon->order_index_ = order_index_;
47 new_polygon->original_ref_ = original_ref_;
48 new_polygon->points_.reserve(points_.size());
49 new_polygon->points_ = points_;
50 new_polygon->normal_.set_x(normal_.x());
51 new_polygon->normal_.set_y(normal_.y());
52 new_polygon->normal_.set_z(normal_.z());
53 return scoped_ptr<DrawPolygon>(new_polygon);
54 }
55
56 float DrawPolygon::SignedPointDistance(const gfx::Point3F& point) const {
57 return gfx::DotProduct(point - points_[0], normal_);
58 }
59
60 // Checks whether or not shape a lies on the front or back side of b, or
61 // whether they should be considered coplanar. If on the back side, we
62 // say ABeforeB because it should be drawn in that order.
63 // Assumes that layers are split and there are no intersecting planes.
64 BspCompareResult DrawPolygon::SideCompare(const DrawPolygon& a,
65 const DrawPolygon& b) {
66 // Right away let's check if they're coplanar
67 double dot = gfx::DotProduct(a.normal_, b.normal_);
68 float sign;
69 bool normal_match = false;
70 // This check assumes that the normals are normalized.
71 if (std::abs(dot) >= 1.0f - coplanar_dot_epsilon) {
72 normal_match = true;
73 // The normals are matching enough that we only have to test one point.
74 sign = gfx::DotProduct(a.points_[0] - b.points_[0], b.normal_);
75 // Is it on either side of the splitter?
76 if (sign < -compare_threshold) {
77 return BSP_BACK;
78 }
79
80 if (sign > compare_threshold) {
81 return BSP_FRONT;
82 }
83
84 // No it wasn't, so the sign of the dot product of the normals
85 // along with document order determines which side it goes on.
86 if (dot >= 0.0f) {
87 if (a.order_index_ < b.order_index_) {
88 return BSP_COPLANAR_FRONT;
89 }
90 return BSP_COPLANAR_BACK;
91 }
92
93 if (a.order_index_ < b.order_index_) {
94 return BSP_COPLANAR_BACK;
95 }
96 return BSP_COPLANAR_FRONT;
97 }
98
99 unsigned int pos_count = 0;
100 unsigned int neg_count = 0;
101 for (unsigned int i = 0; i < a.points_.size(); i++) {
102 if (!normal_match || (normal_match && i > 0)) {
103 sign = gfx::DotProduct(a.points_[i] - b.points_[0], b.normal_);
104 }
105
106 if (sign < -compare_threshold) {
107 ++neg_count;
108 } else if (sign > compare_threshold) {
109 ++pos_count;
110 }
111
112 if (pos_count && neg_count) {
113 return BSP_SPLIT;
114 }
115 }
116
117 if (pos_count) {
118 return BSP_FRONT;
119 }
120 return BSP_BACK;
121 }
122
123 static bool LineIntersectPlane(const gfx::Point3F& line_start,
124 const gfx::Point3F& line_end,
125 const gfx::Point3F& plane_origin,
126 const gfx::Vector3dF& plane_normal,
127 gfx::Point3F* intersection,
128 float distance_threshold) {
129 gfx::Vector3dF start_to_origin_vector = plane_origin - line_start;
130 gfx::Vector3dF end_to_origin_vector = plane_origin - line_end;
131
132 double start_distance = gfx::DotProduct(start_to_origin_vector, plane_normal);
133 double end_distance = gfx::DotProduct(end_to_origin_vector, plane_normal);
134
135 // The case where one vertex lies on the thick-plane and the other
136 // is outside of it.
137 if (std::abs(start_distance) < distance_threshold &&
138 std::abs(end_distance) > distance_threshold) {
139 intersection->SetPoint(line_start.x(), line_start.y(), line_start.z());
140 return true;
141 }
142
143 // This is the case where we clearly cross the thick-plane.
144 if ((start_distance > distance_threshold &&
145 end_distance < -distance_threshold) ||
146 (start_distance < -distance_threshold &&
147 end_distance > distance_threshold)) {
148 gfx::Vector3dF v = line_end - line_start;
149 float total_distance = std::abs(start_distance) + std::abs(end_distance);
150 float lerp_factor = std::abs(start_distance) / total_distance;
151
152 intersection->SetPoint(line_start.x() + (v.x() * lerp_factor),
153 line_start.y() + (v.y() * lerp_factor),
154 line_start.z() + (v.z() * lerp_factor));
155
156 return true;
157 }
158 return false;
159 }
160
161 // This function is separate from ApplyTransform because it is often unnecessary
162 // to transform the normal with the rest of the polygon.
163 // When drawing these polygons, it is necessary to move them back into layer
164 // space before sending them to OpenGL, which requires using ApplyTransform,
165 // but normal information is no longer needed after sorting.
166 void DrawPolygon::ApplyTransformToNormal(const gfx::Transform& transform) {
enne (OOO) 2014/07/28 20:46:40 I'm not sure I understand ApplyTransform vs ApplyT
troyhildebrandt 2014/07/28 21:24:55 When we transform the geometry for BSP splitting/s
enne (OOO) 2014/07/28 23:11:33 Recapping in person discussion: change this to Tra
167 // Now we use the inverse transpose of |transform| to transform the normal.
168 gfx::Transform inverse_transform;
169 bool inverted = transform.GetInverse(&inverse_transform);
170 DCHECK(inverted);
171 if (!inverted)
172 return;
173 inverse_transform.Transpose();
174
175 gfx::Point3F new_normal(normal_.x(), normal_.y(), normal_.z());
176 inverse_transform.TransformPoint(&new_normal);
177 // Make sure our normal is still normalized.
178 normal_ = gfx::Vector3dF(new_normal.x(), new_normal.y(), new_normal.z());
179 float normal_magnitude = normal_.Length();
180 if (normal_magnitude != 0 && normal_magnitude != 1) {
181 normal_.Scale(1.0f / normal_magnitude);
182 }
183 }
184
185 void DrawPolygon::ApplyTransform(const gfx::Transform& transform) {
186 for (unsigned int i = 0; i < points_.size(); i++) {
187 transform.TransformPoint(&points_[i]);
188 }
189 }
190
191 bool DrawPolygon::Split(const DrawPolygon& splitter,
192 scoped_ptr<DrawPolygon>* front,
193 scoped_ptr<DrawPolygon>* back) {
194 gfx::Point3F intersections[2];
195 std::vector<gfx::Point3F> out_points[2];
196 // vertex_before stores the index of the vertex before its matching
197 // intersection.
198 // i.e. vertex_before[0] stores the vertex we saw before we crossed the plane
199 // which resulted in the line/plane intersection giving us intersections[0].
200 unsigned int vertex_before[2];
enne (OOO) 2014/07/28 20:46:40 unsigned int => size_t here and elsewhere in this
troyhildebrandt 2014/07/28 21:24:55 Done.
201 unsigned int points_size = points_.size();
202 unsigned int current_intersection = 0;
203
204 unsigned int current_vertex = 0;
205 // We will only have two intersection points because we assume all polygons
206 // are convex.
207 while (current_intersection < 2) {
208 if (LineIntersectPlane(points_[(current_vertex % points_size)],
209 points_[(current_vertex + 1) % points_size],
210 splitter.points_[0],
211 splitter.normal_,
212 &intersections[current_intersection],
213 split_threshold)) {
214 vertex_before[current_intersection] = current_vertex % points_size;
215 current_intersection++;
216 // We found both intersection points so we're done already.
217 if (current_intersection == 2) {
218 break;
219 }
220 }
221 if (current_vertex++ > points_size) {
222 break;
223 }
224 }
225 if (current_intersection < 2) {
enne (OOO) 2014/07/28 20:46:40 Can you DCHECK here for boundary cases that should
troyhildebrandt 2014/07/28 21:24:55 Done.
226 return false;
227 }
228
229 // Since we found both the intersection points, we can begin building the
230 // vertex set for both our new polygons.
231 unsigned int start1 = (vertex_before[0] + 1) % points_size;
enne (OOO) 2014/07/28 20:46:40 If you mean "the size of a vector" or "an index in
troyhildebrandt 2014/07/28 21:24:55 Done.
232 unsigned int start2 = (vertex_before[1] + 1) % points_size;
233 unsigned int points_remaining = points_size;
234
235 // First polygon.
236 out_points[0].push_back(intersections[0]);
237 for (unsigned int i = start1; i <= vertex_before[1]; i++) {
238 out_points[0].push_back(points_[i]);
239 --points_remaining;
240 }
241 out_points[0].push_back(intersections[1]);
242
243 // Second polygon.
244 out_points[1].push_back(intersections[1]);
245 unsigned int index = start2;
246 for (unsigned int i = 0; i < points_remaining; i++) {
247 out_points[1].push_back(points_[index % points_size]);
248 ++index;
249 }
250 out_points[1].push_back(intersections[0]);
251
252 // Give both polygons the original splitting polygon's ID, so that they'll
253 // still be sorted properly in co-planar instances.
254 // Send false as last parameter for is_original because they're split.
enne (OOO) 2014/07/28 20:46:40 Comment doesn't make sense here. Add this in a fu
troyhildebrandt 2014/07/28 21:24:55 Done.
255 scoped_ptr<DrawPolygon> poly1(
256 new DrawPolygon(original_ref_, out_points[0], order_index_));
257 scoped_ptr<DrawPolygon> poly2(
258 new DrawPolygon(original_ref_, out_points[1], order_index_));
259
260 poly1->SetNormal(normal_);
enne (OOO) 2014/07/28 20:46:40 Should normal be a constructor parameter?
troyhildebrandt 2014/07/28 21:24:56 It could be. I've changed it so it is.
261 poly2->SetNormal(normal_);
262
263 if (SideCompare(*poly1, splitter) == BSP_FRONT) {
264 *front = poly1.Pass();
265 *back = poly2.Pass();
266 } else {
267 *front = poly2.Pass();
268 *back = poly1.Pass();
269 }
270 return true;
271 }
272
273 // This algorithm takes the first vertex in the polygon and uses that as a
274 // pivot point to fan out and create quads from the rest of the vertices.
275 // |offset| starts off as the second vertex, and then |op1| and |op2| indicate
276 // offset+1 and offset+2 respectively.
277 // After the first quad is created, the first vertex in the next quad is the
278 // same as all the rest, the pivot point. The second vertex in the next quad is
279 // the old |op2|, the last vertex added to the previous quad. This continues
280 // until all points are exhausted.
281 // The special case here is where there are only 3 points remaining, in which
282 // case we use the same values for vertex 3 and 4 to make a degenerate quad
283 // that represents a triangle.
284 void DrawPolygon::ToQuads2D(std::vector<gfx::QuadF>* quads) const {
285 if (points_.size() <= 2)
286 return;
287
288 gfx::PointF first(points_[0].x(), points_[0].y());
289 unsigned int offset = 1;
enne (OOO) 2014/07/28 20:46:40 unsigned int => size_t, here and elsewhere in this
troyhildebrandt 2014/07/28 21:24:55 Done.
290 while (offset < points_.size() - 1) {
291 unsigned int op1 = offset + 1;
292 unsigned int op2 = offset + 2;
293 if (op2 >= points_.size()) {
294 // It's going to be a degenerate triangle.
295 op2 = op1;
296 }
297 quads->push_back(
298 gfx::QuadF(first,
299 gfx::PointF(points_[offset].x(), points_[offset].y()),
300 gfx::PointF(points_[op1].x(), points_[op1].y()),
301 gfx::PointF(points_[op2].x(), points_[op2].y())));
302 offset = op2;
303 }
304 }
305
306 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698