// 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(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* test_polygons, const std::vector& compare_list) { BspTree bsp_tree(test_polygons); std::vector 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& 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 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 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 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 polygon_a( CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0)); scoped_ptr polygon_b( CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 1)); scoped_ptr polygon_c( CREATE_DRAW_POLYGON(vertices_c, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 2)); ScopedPtrDeque 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 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 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 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 polygon_a( CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0)); scoped_ptr polygon_b( CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 1)); ScopedPtrDeque polygon_list; polygon_list.push_back(polygon_a.Pass()); polygon_list.push_back(polygon_b.Pass()); int compare_ids[] = {1, 0, 1}; std::vector 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 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 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 polygon_a( CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0)); scoped_ptr polygon_b( CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 1)); ScopedPtrDeque polygon_list; polygon_list.push_back(polygon_a.Pass()); polygon_list.push_back(polygon_b.Pass()); int compare_ids[] = {1, 0}; std::vector 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 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 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 polygon_a( CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0)); scoped_ptr polygon_b( CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 1)); ScopedPtrDeque polygon_list; polygon_list.push_back(polygon_b.Pass()); polygon_list.push_back(polygon_a.Pass()); int compare_ids[] = {0, 1, 0}; std::vector 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 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 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 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 polygon_a( CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0)); scoped_ptr polygon_b( CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 1)); scoped_ptr polygon_c( CREATE_DRAW_POLYGON(vertices_c, gfx::Vector3dF(0.0f, 1.0f, 0.0f), 2)); ScopedPtrDeque 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 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 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 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 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 polygon_a( CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0)); scoped_ptr polygon_b( CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 1)); scoped_ptr polygon_c( CREATE_DRAW_POLYGON(vertices_c, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 2)); scoped_ptr polygon_d = polygon_a->CreateCopy(); scoped_ptr polygon_e = polygon_b->CreateCopy(); scoped_ptr polygon_f = polygon_c->CreateCopy(); { ScopedPtrDeque 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 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 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 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 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 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 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 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 polygon_a( CREATE_DRAW_POLYGON(vertices_a, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 0)); scoped_ptr polygon_b( CREATE_DRAW_POLYGON(vertices_b, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 1)); scoped_ptr polygon_c( CREATE_DRAW_POLYGON(vertices_c, gfx::Vector3dF(0.0f, 0.0f, 1.0f), 2)); scoped_ptr polygon_d( CREATE_DRAW_POLYGON(vertices_d, gfx::Vector3dF(-1.0f, 0.0f, 0.0f), 3)); ScopedPtrDeque 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 compare_list = INT_VECTOR_FROM_ARRAY(compare_ids); BspTreeTest::RunTest(&polygon_list, compare_list); } } // namespace } // namespace cc