summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorthildebr@chromium.org <thildebr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-30 12:50:20 +0000
committerthildebr@chromium.org <thildebr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-30 12:50:20 +0000
commit80fae1ef974518320a0c16cd25c510a5abcfcbb5 (patch)
treebc4355bfb934fd30569f2861839188b9ae319c08
parente3a62a3b4f5114fb198518fba848d88418567a5b (diff)
downloadchromium_src-80fae1ef974518320a0c16cd25c510a5abcfcbb5.zip
chromium_src-80fae1ef974518320a0c16cd25c510a5abcfcbb5.tar.gz
chromium_src-80fae1ef974518320a0c16cd25c510a5abcfcbb5.tar.bz2
WIP BSP Tree for 3D Layer Sorting
Still adding test scenarios as I come up with them. Review URL: https://codereview.chromium.org/384083002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@286499 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--cc/cc.gyp5
-rw-r--r--cc/cc_tests.gyp1
-rw-r--r--cc/output/bsp_tree.cc121
-rw-r--r--cc/output/bsp_tree.h115
-rw-r--r--cc/output/bsp_tree_unittest.cc348
-rw-r--r--cc/output/bsp_walk_action.cc23
-rw-r--r--cc/output/bsp_walk_action.h34
7 files changed, 646 insertions, 1 deletions
diff --git a/cc/cc.gyp b/cc/cc.gyp
index 67a50326..83d408e 100644
--- a/cc/cc.gyp
+++ b/cc/cc.gyp
@@ -244,7 +244,10 @@
'layers/video_layer_impl.h',
'output/begin_frame_args.cc',
'output/begin_frame_args.h',
- 'output/bsp_compare_result.h',
+ 'output/bsp_tree.cc',
+ 'output/bsp_tree.h',
+ 'output/bsp_walk_action.cc',
+ 'output/bsp_walk_action.h',
'output/compositor_frame.cc',
'output/compositor_frame.h',
'output/compositor_frame_ack.cc',
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp
index d29413d..18d38f9 100644
--- a/cc/cc_tests.gyp
+++ b/cc/cc_tests.gyp
@@ -58,6 +58,7 @@
'layers/ui_resource_layer_unittest.cc',
'layers/video_layer_impl_unittest.cc',
'output/begin_frame_args_unittest.cc',
+ 'output/bsp_tree_unittest.cc',
'output/delegating_renderer_unittest.cc',
'output/filter_operations_unittest.cc',
'output/gl_renderer_unittest.cc',
diff --git a/cc/output/bsp_tree.cc b/cc/output/bsp_tree.cc
new file mode 100644
index 0000000..2755c0b
--- /dev/null
+++ b/cc/output/bsp_tree.cc
@@ -0,0 +1,121 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/output/bsp_tree.h"
+
+#include <list>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/scoped_ptr_deque.h"
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/output/bsp_compare_result.h"
+#include "cc/quads/draw_polygon.h"
+
+namespace cc {
+
+BspNode::BspNode(scoped_ptr<DrawPolygon> data) : node_data(data.Pass()) {
+}
+
+BspNode::~BspNode() {
+}
+
+BspTree::BspTree(ScopedPtrDeque<DrawPolygon>* list) {
+ if (list->size() == 0)
+ return;
+
+ root_ = scoped_ptr<BspNode>(new BspNode(list->take_front()));
+ BuildTree(root_.get(), list);
+}
+
+// The idea behind using a deque for BuildTree's input is that we want to be
+// able to place polygons that we've decided aren't splitting plane candidates
+// at the back of the queue while moving the candidate splitting planes to the
+// front when the heuristic decides that they're a better choice. This way we
+// can always simply just take from the front of the deque for our node's
+// data.
+void BspTree::BuildTree(BspNode* node,
+ ScopedPtrDeque<DrawPolygon>* polygon_list) {
+ ScopedPtrDeque<DrawPolygon> front_list;
+ ScopedPtrDeque<DrawPolygon> back_list;
+
+ // We take in a list of polygons at this level of the tree, and have to
+ // find a splitting plane, then classify polygons as either in front of
+ // or behind that splitting plane.
+ while (polygon_list->size() > 0) {
+ // Is this particular polygon in front of or behind our splitting polygon.
+ BspCompareResult comparer_result =
+ GetNodePositionRelative(*polygon_list->front(), *(node->node_data));
+
+ // If it's clearly behind or in front of the splitting plane, we use the
+ // heuristic to decide whether or not we should put it at the back
+ // or front of the list.
+ switch (comparer_result) {
+ case BSP_FRONT:
+ front_list.push_back(polygon_list->take_front().Pass());
+ break;
+ case BSP_BACK:
+ back_list.push_back(polygon_list->take_front().Pass());
+ break;
+ case BSP_SPLIT:
+ {
+ scoped_ptr<DrawPolygon> polygon;
+ scoped_ptr<DrawPolygon> new_front;
+ scoped_ptr<DrawPolygon> new_back;
+ bool split_result = false;
+ // Time to split this geometry, *it needs to be split by node_data.
+ polygon = polygon_list->take_front();
+ split_result =
+ polygon->Split(*(node->node_data), &new_front, &new_back);
+ DCHECK(split_result);
+ if (!split_result) {
+ break;
+ }
+ front_list.push_back(new_front.Pass());
+ back_list.push_back(new_back.Pass());
+ break;
+ }
+ case BSP_COPLANAR_FRONT:
+ node->coplanars_front.push_back(polygon_list->take_front());
+ break;
+ case BSP_COPLANAR_BACK:
+ node->coplanars_back.push_back(polygon_list->take_front());
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ // Build the back subtree using the front of the back_list as our splitter.
+ if (back_list.size() > 0) {
+ node->back_child = scoped_ptr<BspNode>(new BspNode(back_list.take_front()));
+ BuildTree(node->back_child.get(), &back_list);
+ }
+
+ // Build the front subtree using the front of the front_list as our splitter.
+ if (front_list.size() > 0) {
+ node->front_child =
+ scoped_ptr<BspNode>(new BspNode(front_list.take_front()));
+ BuildTree(node->front_child.get(), &front_list);
+ }
+}
+
+BspCompareResult BspTree::GetNodePositionRelative(const DrawPolygon& node_a,
+ const DrawPolygon& node_b) {
+ return DrawPolygon::SideCompare(node_a, node_b);
+}
+
+// The base comparer with 0,0,0 as camera position facing forward
+BspCompareResult BspTree::GetCameraPositionRelative(const DrawPolygon& node) {
+ if (node.normal().z() > 0.0f) {
+ return BSP_FRONT;
+ }
+ return BSP_BACK;
+}
+
+BspTree::~BspTree() {
+}
+
+} // namespace cc
diff --git a/cc/output/bsp_tree.h b/cc/output/bsp_tree.h
new file mode 100644
index 0000000..6e7f66b
--- /dev/null
+++ b/cc/output/bsp_tree.h
@@ -0,0 +1,115 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_OUTPUT_BSP_TREE_H_
+#define CC_OUTPUT_BSP_TREE_H_
+
+#include <list>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/scoped_ptr_deque.h"
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/output/bsp_compare_result.h"
+#include "cc/quads/draw_polygon.h"
+
+namespace cc {
+
+struct BspNode {
+ // This represents the splitting plane.
+ scoped_ptr<DrawPolygon> node_data;
+ // This represents any coplanar geometry we found while building the BSP.
+ ScopedPtrVector<DrawPolygon> coplanars_front;
+ ScopedPtrVector<DrawPolygon> coplanars_back;
+
+ scoped_ptr<BspNode> back_child;
+ scoped_ptr<BspNode> front_child;
+
+ explicit BspNode(scoped_ptr<DrawPolygon> data);
+ ~BspNode();
+};
+
+class CC_EXPORT BspTree {
+ public:
+ explicit BspTree(ScopedPtrDeque<DrawPolygon>* list);
+ scoped_ptr<BspNode>& root() { return root_; }
+
+ template <typename ActionHandlerType>
+ void TraverseWithActionHandler(ActionHandlerType* action_handler) const {
+ if (root_) {
+ WalkInOrderRecursion<ActionHandlerType>(action_handler, root_.get());
+ }
+ }
+
+ ~BspTree();
+
+ private:
+ scoped_ptr<BspNode> root_;
+
+ void FromList(ScopedPtrVector<DrawPolygon>* list);
+ void BuildTree(BspNode* node, ScopedPtrDeque<DrawPolygon>* data);
+
+ template <typename ActionHandlerType>
+ void WalkInOrderAction(ActionHandlerType* action_handler,
+ DrawPolygon* item) const {
+ (*action_handler)(item);
+ }
+
+ template <typename ActionHandlerType>
+ void WalkInOrderVisitNodes(
+ ActionHandlerType* action_handler,
+ const BspNode* node,
+ const BspNode* first_child,
+ const BspNode* second_child,
+ const ScopedPtrVector<DrawPolygon>& first_coplanars,
+ const ScopedPtrVector<DrawPolygon>& second_coplanars) const {
+ if (first_child) {
+ WalkInOrderRecursion(action_handler, first_child);
+ }
+ for (size_t i = 0; i < first_coplanars.size(); i++) {
+ WalkInOrderAction(action_handler, first_coplanars[i]);
+ }
+ WalkInOrderAction(action_handler, node->node_data.get());
+ for (size_t i = 0; i < second_coplanars.size(); i++) {
+ WalkInOrderAction(action_handler, second_coplanars[i]);
+ }
+ if (second_child) {
+ WalkInOrderRecursion(action_handler, second_child);
+ }
+ }
+
+ template <typename ActionHandlerType>
+ void WalkInOrderRecursion(ActionHandlerType* action_handler,
+ const BspNode* node) const {
+ // If our view is in front of the the polygon
+ // in this node then walk back then front.
+ if (GetCameraPositionRelative(*(node->node_data)) == BSP_FRONT) {
+ WalkInOrderVisitNodes<ActionHandlerType>(action_handler,
+ node,
+ node->back_child.get(),
+ node->front_child.get(),
+ node->coplanars_front,
+ node->coplanars_back);
+ } else {
+ WalkInOrderVisitNodes<ActionHandlerType>(action_handler,
+ node,
+ node->front_child.get(),
+ node->back_child.get(),
+ node->coplanars_back,
+ node->coplanars_front);
+ }
+ }
+
+ // Returns whether or not nodeA is on one or the other side of nodeB,
+ // coplanar, or whether it crosses nodeB's plane and needs to be split
+ static BspCompareResult GetNodePositionRelative(const DrawPolygon& node_a,
+ const DrawPolygon& node_b);
+ // Returns whether or not our viewer is in front of or behind the plane
+ // defined by this polygon/node
+ static BspCompareResult GetCameraPositionRelative(const DrawPolygon& node);
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_BSP_TREE_H_
diff --git a/cc/output/bsp_tree_unittest.cc b/cc/output/bsp_tree_unittest.cc
new file mode 100644
index 0000000..100bc91
--- /dev/null
+++ b/cc/output/bsp_tree_unittest.cc
@@ -0,0 +1,348 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/scoped_ptr_deque.h"
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/output/bsp_tree.h"
+#include "cc/output/bsp_walk_action.h"
+#include "cc/quads/draw_polygon.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+#define EXPECT_SORTED_LISTS_EQ(polygon_list, compare_list) \
+ do { \
+ EXPECT_EQ(polygon_list.size(), compare_list.size()); \
+ for (unsigned int i = 0; i < polygon_list.size(); i++) { \
+ EXPECT_EQ(polygon_list[i]->order_index(), compare_list[i]); \
+ } \
+ } while (false);
+
+#define INT_VECTOR_FROM_ARRAY(array) \
+ std::vector<int>(array, array + arraysize(array))
+
+#define CREATE_DRAW_POLYGON(vertex_vector, normal, polygon_id) \
+ new DrawPolygon(NULL, vertex_vector, normal, polygon_id)
+
+class BspTreeTest {
+ public:
+ static void RunTest(ScopedPtrDeque<DrawPolygon>* test_polygons,
+ const std::vector<int>& compare_list) {
+ BspTree bsp_tree(test_polygons);
+
+ std::vector<DrawPolygon*> sorted_list;
+ BspWalkActionToVector action_handler(&sorted_list);
+ bsp_tree.TraverseWithActionHandler(&action_handler);
+
+ EXPECT_SORTED_LISTS_EQ(sorted_list, compare_list);
+ EXPECT_TRUE(VerifySidedness(bsp_tree.root()));
+ }
+
+ static bool VerifySidedness(const scoped_ptr<BspNode>& node) {
+ // We check if both the front and back child nodes have geometry that is
+ // completely on the expected side of the current node.
+ bool front_ok = true;
+ bool back_ok = true;
+ if (node->back_child) {
+ // Make sure the back child lies entirely behind this node.
+ BspCompareResult result = DrawPolygon::SideCompare(
+ *(node->back_child->node_data), *(node->node_data));
+ if (result != BSP_BACK) {
+ return false;
+ }
+ back_ok = VerifySidedness(node->back_child);
+ }
+ // Make sure the front child lies entirely in front of this node.
+ if (node->front_child) {
+ BspCompareResult result = DrawPolygon::SideCompare(
+ *(node->front_child->node_data), *(node->node_data));
+ if (result != BSP_FRONT) {
+ return false;
+ }
+ front_ok = VerifySidedness(node->front_child);
+ }
+ if (!back_ok || !front_ok) {
+ return false;
+ }
+
+ // Now we need to make sure our coplanar geometry is all actually coplanar.
+ for (size_t i = 0; i < node->coplanars_front.size(); i++) {
+ BspCompareResult result = DrawPolygon::SideCompare(
+ *(node->coplanars_front[i]), *(node->node_data));
+ if (result != BSP_COPLANAR_FRONT) {
+ return false;
+ }
+ }
+ for (size_t i = 0; i < node->coplanars_back.size(); i++) {
+ BspCompareResult result = DrawPolygon::SideCompare(
+ *(node->coplanars_back[i]), *(node->node_data));
+ if (result != BSP_COPLANAR_BACK) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+// Simple standing quads all parallel with each other, causing no splits.
+TEST(BspTreeTest, NoSplit) {
+ std::vector<gfx::Point3F> vertices_a;
+ vertices_a.push_back(gfx::Point3F(0.0f, 10.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(0.0f, 0.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(10.0f, 0.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(10.0f, 10.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_b;
+ vertices_b.push_back(gfx::Point3F(0.0f, 10.0f, -5.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, 0.0f, -5.0f));
+ vertices_b.push_back(gfx::Point3F(10.0f, 0.0f, -5.0f));
+ vertices_b.push_back(gfx::Point3F(10.0f, 10.0f, -5.0f));
+ std::vector<gfx::Point3F> vertices_c;
+ vertices_c.push_back(gfx::Point3F(0.0f, 10.0f, 5.0f));
+ vertices_c.push_back(gfx::Point3F(0.0f, 0.0f, 5.0f));
+ vertices_c.push_back(gfx::Point3F(10.0f, 0.0f, 5.0f));
+ vertices_c.push_back(gfx::Point3F(10.0f, 10.0f, 5.0f));
+
+ scoped_ptr<DrawPolygon> polygon_a(
+ CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0));
+ scoped_ptr<DrawPolygon> polygon_b(
+ CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 1));
+ scoped_ptr<DrawPolygon> polygon_c(
+ CREATE_DRAW_POLYGON(vertices_c, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 2));
+
+ ScopedPtrDeque<DrawPolygon> polygon_list;
+ polygon_list.push_back(polygon_a.Pass());
+ polygon_list.push_back(polygon_b.Pass());
+ polygon_list.push_back(polygon_c.Pass());
+
+ int compare_ids[] = {1, 0, 2};
+ std::vector<int> compare_list = INT_VECTOR_FROM_ARRAY(compare_ids);
+ BspTreeTest::RunTest(&polygon_list, compare_list);
+}
+
+// Basic two polygon split, can be viewed as a + from above.
+TEST(BspTreeTest, BasicSplit) {
+ std::vector<gfx::Point3F> vertices_a;
+ vertices_a.push_back(gfx::Point3F(-5.0f, -5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(-5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, -5.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_b;
+ vertices_b.push_back(gfx::Point3F(0.0f, -5.0f, -5.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, 5.0f, -5.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, 5.0f, 5.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, -5.0f, 5.0f));
+
+ scoped_ptr<DrawPolygon> polygon_a(
+ CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0));
+ scoped_ptr<DrawPolygon> polygon_b(
+ CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 1));
+
+ ScopedPtrDeque<DrawPolygon> polygon_list;
+ polygon_list.push_back(polygon_a.Pass());
+ polygon_list.push_back(polygon_b.Pass());
+
+ int compare_ids[] = {1, 0, 1};
+ std::vector<int> compare_list = INT_VECTOR_FROM_ARRAY(compare_ids);
+ BspTreeTest::RunTest(&polygon_list, compare_list);
+}
+
+// Same as above with the second quad offset so it doesn't intersect. One quad
+// should be very clearly on one side of the other, and no splitting should
+// occur.
+TEST(BspTreeTest, QuadOffset) {
+ std::vector<gfx::Point3F> vertices_a;
+ vertices_a.push_back(gfx::Point3F(-5.0f, -5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(-5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, -5.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_b;
+ vertices_b.push_back(gfx::Point3F(0.0f, 5.0f, -15.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, -5.0f, -15.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, -5.0f, -10.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, 5.0f, -10.0f));
+
+ scoped_ptr<DrawPolygon> polygon_a(
+ CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0));
+ scoped_ptr<DrawPolygon> polygon_b(
+ CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 1));
+
+ ScopedPtrDeque<DrawPolygon> polygon_list;
+ polygon_list.push_back(polygon_a.Pass());
+ polygon_list.push_back(polygon_b.Pass());
+
+ int compare_ids[] = {1, 0};
+ std::vector<int> compare_list = INT_VECTOR_FROM_ARRAY(compare_ids);
+ BspTreeTest::RunTest(&polygon_list, compare_list);
+}
+
+// Same as above, but this time we change the order in which the quads are
+// inserted into the tree, causing one to actually cross the plane of the other
+// and cause a split.
+TEST(BspTreeTest, QuadOffsetSplit) {
+ std::vector<gfx::Point3F> vertices_a;
+ vertices_a.push_back(gfx::Point3F(-5.0f, -5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(-5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, -5.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_b;
+ vertices_b.push_back(gfx::Point3F(0.0f, -5.0f, -15.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, 5.0f, -15.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, 5.0f, -10.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, -5.0f, -10.0f));
+
+ scoped_ptr<DrawPolygon> polygon_a(
+ CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0));
+ scoped_ptr<DrawPolygon> polygon_b(
+ CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 1));
+
+ ScopedPtrDeque<DrawPolygon> polygon_list;
+ polygon_list.push_back(polygon_b.Pass());
+ polygon_list.push_back(polygon_a.Pass());
+
+ int compare_ids[] = {0, 1, 0};
+ std::vector<int> compare_list = INT_VECTOR_FROM_ARRAY(compare_ids);
+ BspTreeTest::RunTest(&polygon_list, compare_list);
+}
+
+// In addition to what can be viewed as a + from above, another piece of
+// geometry is inserted to cut these pieces right in the middle, viewed as
+// a quad from overhead.
+TEST(BspTreeTest, ThreeWaySplit) {
+ std::vector<gfx::Point3F> vertices_a;
+ vertices_a.push_back(gfx::Point3F(-5.0f, -5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(-5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, -5.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_b;
+ vertices_b.push_back(gfx::Point3F(0.0f, -5.0f, -5.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, 5.0f, -5.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, 5.0f, 5.0f));
+ vertices_b.push_back(gfx::Point3F(0.0f, -5.0f, 5.0f));
+ std::vector<gfx::Point3F> vertices_c;
+ vertices_c.push_back(gfx::Point3F(-5.0f, 0.0f, -5.0f));
+ vertices_c.push_back(gfx::Point3F(-5.0f, 0.0f, 5.0f));
+ vertices_c.push_back(gfx::Point3F(5.0f, 0.0f, 5.0f));
+ vertices_c.push_back(gfx::Point3F(5.0f, 0.0f, -5.0f));
+
+ scoped_ptr<DrawPolygon> polygon_a(
+ CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0));
+ scoped_ptr<DrawPolygon> polygon_b(
+ CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 1));
+ scoped_ptr<DrawPolygon> polygon_c(
+ CREATE_DRAW_POLYGON(vertices_c, gfx::Vector3dF(0.0f, 1.0f, 0.0f), 2));
+
+ ScopedPtrDeque<DrawPolygon> polygon_list;
+ polygon_list.push_back(polygon_a.Pass());
+ polygon_list.push_back(polygon_b.Pass());
+ polygon_list.push_back(polygon_c.Pass());
+
+ int compare_ids[] = {2, 1, 2, 0, 2, 1, 2};
+ std::vector<int> compare_list = INT_VECTOR_FROM_ARRAY(compare_ids);
+ BspTreeTest::RunTest(&polygon_list, compare_list);
+}
+
+// This test checks whether coplanar geometry, when inserted into the tree in
+// order, comes back in the same order as it should.
+TEST(BspTreeTest, Coplanar) {
+ std::vector<gfx::Point3F> vertices_a;
+ vertices_a.push_back(gfx::Point3F(-5.0f, -5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(-5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, -5.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_b;
+ vertices_b.push_back(gfx::Point3F(-4.0f, -4.0f, 0.0f));
+ vertices_b.push_back(gfx::Point3F(-4.0f, 4.0f, 0.0f));
+ vertices_b.push_back(gfx::Point3F(4.0f, 4.0f, 0.0f));
+ vertices_b.push_back(gfx::Point3F(4.0f, -4.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_c;
+ vertices_c.push_back(gfx::Point3F(-3.0f, -3.0f, 0.0f));
+ vertices_c.push_back(gfx::Point3F(-3.0f, 3.0f, 0.0f));
+ vertices_c.push_back(gfx::Point3F(3.0f, 3.0f, 0.0f));
+ vertices_c.push_back(gfx::Point3F(3.0f, -3.0f, 0.0f));
+
+ scoped_ptr<DrawPolygon> polygon_a(
+ CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0));
+ scoped_ptr<DrawPolygon> polygon_b(
+ CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 1));
+ scoped_ptr<DrawPolygon> polygon_c(
+ CREATE_DRAW_POLYGON(vertices_c, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 2));
+
+ scoped_ptr<DrawPolygon> polygon_d = polygon_a->CreateCopy();
+ scoped_ptr<DrawPolygon> polygon_e = polygon_b->CreateCopy();
+ scoped_ptr<DrawPolygon> polygon_f = polygon_c->CreateCopy();
+
+ {
+ ScopedPtrDeque<DrawPolygon> polygon_list;
+ polygon_list.push_back(polygon_a.Pass());
+ polygon_list.push_back(polygon_b.Pass());
+ polygon_list.push_back(polygon_c.Pass());
+
+ int compare_ids[] = {0, 1, 2};
+ std::vector<int> compare_list = INT_VECTOR_FROM_ARRAY(compare_ids);
+ BspTreeTest::RunTest(&polygon_list, compare_list);
+ }
+
+ // Now check a different order and ensure we get that back as well
+ {
+ ScopedPtrDeque<DrawPolygon> polygon_list;
+ polygon_list.push_back(polygon_f.Pass());
+ polygon_list.push_back(polygon_d.Pass());
+ polygon_list.push_back(polygon_e.Pass());
+
+ int compare_ids[] = {0, 1, 2};
+ std::vector<int> compare_list = INT_VECTOR_FROM_ARRAY(compare_ids);
+ BspTreeTest::RunTest(&polygon_list, compare_list);
+ }
+}
+
+// A bunch of coplanar geometry should end up sharing a single node, and
+// result in the final piece of geometry splitting into just two pieces on
+// either side of the shared plane.
+TEST(BspTreeTest, CoplanarSplit) {
+ std::vector<gfx::Point3F> vertices_a;
+ vertices_a.push_back(gfx::Point3F(-5.0f, -5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(-5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, 5.0f, 0.0f));
+ vertices_a.push_back(gfx::Point3F(5.0f, -5.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_b;
+ vertices_b.push_back(gfx::Point3F(-4.0f, -4.0f, 0.0f));
+ vertices_b.push_back(gfx::Point3F(-4.0f, 4.0f, 0.0f));
+ vertices_b.push_back(gfx::Point3F(4.0f, 4.0f, 0.0f));
+ vertices_b.push_back(gfx::Point3F(4.0f, -4.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_c;
+ vertices_c.push_back(gfx::Point3F(-3.0f, -3.0f, 0.0f));
+ vertices_c.push_back(gfx::Point3F(-3.0f, 3.0f, 0.0f));
+ vertices_c.push_back(gfx::Point3F(3.0f, 3.0f, 0.0f));
+ vertices_c.push_back(gfx::Point3F(3.0f, -3.0f, 0.0f));
+ std::vector<gfx::Point3F> vertices_d;
+ vertices_d.push_back(gfx::Point3F(0.0f, -15.0f, -15.0f));
+ vertices_d.push_back(gfx::Point3F(0.0f, 15.0f, -15.0f));
+ vertices_d.push_back(gfx::Point3F(0.0f, 15.0f, 15.0f));
+ vertices_d.push_back(gfx::Point3F(0.0f, -15.0f, 15.0f));
+
+ scoped_ptr<DrawPolygon> polygon_a(
+ CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0));
+ scoped_ptr<DrawPolygon> polygon_b(
+ CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 1));
+ scoped_ptr<DrawPolygon> polygon_c(
+ CREATE_DRAW_POLYGON(vertices_c, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 2));
+ scoped_ptr<DrawPolygon> polygon_d(
+ CREATE_DRAW_POLYGON(vertices_d, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 3));
+
+ ScopedPtrDeque<DrawPolygon> polygon_list;
+ polygon_list.push_back(polygon_a.Pass());
+ polygon_list.push_back(polygon_b.Pass());
+ polygon_list.push_back(polygon_c.Pass());
+ polygon_list.push_back(polygon_d.Pass());
+
+ int compare_ids[] = {3, 0, 1, 2, 3};
+ std::vector<int> compare_list = INT_VECTOR_FROM_ARRAY(compare_ids);
+ BspTreeTest::RunTest(&polygon_list, compare_list);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/output/bsp_walk_action.cc b/cc/output/bsp_walk_action.cc
new file mode 100644
index 0000000..da5ada5
--- /dev/null
+++ b/cc/output/bsp_walk_action.cc
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/output/bsp_walk_action.h"
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/output/direct_renderer.h"
+#include "cc/quads/draw_polygon.h"
+
+namespace cc {
+
+void BspWalkActionToVector::operator()(DrawPolygon* item) {
+ list_->push_back(item);
+}
+
+BspWalkActionToVector::BspWalkActionToVector(std::vector<DrawPolygon*>* in_list)
+ : list_(in_list) {
+}
+
+} // namespace cc
diff --git a/cc/output/bsp_walk_action.h b/cc/output/bsp_walk_action.h
new file mode 100644
index 0000000..bb31865
--- /dev/null
+++ b/cc/output/bsp_walk_action.h
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_OUTPUT_BSP_WALK_ACTION_H_
+#define CC_OUTPUT_BSP_WALK_ACTION_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/output/direct_renderer.h"
+#include "cc/quads/draw_polygon.h"
+
+namespace cc {
+
+class CC_EXPORT BspWalkAction {
+ public:
+ virtual void operator()(DrawPolygon* item) = 0;
+};
+
+// The BspTree class takes ownership of all the DrawPolygons returned in list_
+// so the BspTree must be preserved while elements in that vector are in use.
+class CC_EXPORT BspWalkActionToVector : public BspWalkAction {
+ public:
+ explicit BspWalkActionToVector(std::vector<DrawPolygon*>* in_list);
+ virtual void operator()(DrawPolygon* item) OVERRIDE;
+
+ private:
+ std::vector<DrawPolygon*>* list_;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_BSP_WALK_ACTION_H_