/* * Copyright 2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // This file implements unit tests for class BoundingBox. #include "core/cross/client.h" #include "tests/common/win/testing_common.h" #include "core/cross/bounding_box.h" #include "core/cross/math_utilities.h" #include "core/cross/ray_intersection_info.h" #include "core/cross/transform.h" #include "core/cross/renderer.h" namespace o3d { class BoundingBoxTest : public testing::Test { }; // Tests the creation of a BoundingBox. TEST_F(BoundingBoxTest, Basic) { // Check that it defaults to invalid. EXPECT_FALSE(BoundingBox().valid()); BoundingBox bounding_box(Point3(-1.0f, 2.0f, -3.0f), Point3(1.0f, -2.0f, 3.0f)); // Check it's valid. EXPECT_TRUE(bounding_box.valid()); // Check it got set. EXPECT_EQ(bounding_box.min_extent().getX(), -1.0f); EXPECT_EQ(bounding_box.min_extent().getY(), -2.0f); EXPECT_EQ(bounding_box.min_extent().getZ(), -3.0f); EXPECT_EQ(bounding_box.max_extent().getX(), 1.0f); EXPECT_EQ(bounding_box.max_extent().getY(), 2.0f); EXPECT_EQ(bounding_box.max_extent().getZ(), 3.0f); } // Tests BoundingBox::Add TEST_F(BoundingBoxTest, Add) { BoundingBox box(Point3(-1.0f, -2.0f, -3.0f), Point3(1.0f, 2.0f, 3.0f)); box.Add(BoundingBox(Point3(-4.0f, -1.0f, 9.0f), Point3(5.0f, 1.0f, 11.0f)), &box); // Check the results are the sum of both boxes. EXPECT_TRUE(box.valid()); EXPECT_EQ(box.min_extent().getX(), -4.0f); EXPECT_EQ(box.min_extent().getY(), -2.0f); EXPECT_EQ(box.min_extent().getZ(), -3.0f); EXPECT_EQ(box.max_extent().getX(), 5.0f); EXPECT_EQ(box.max_extent().getY(), 2.0f); EXPECT_EQ(box.max_extent().getZ(), 11.0f); // Check only the original box is valid. box.Add(BoundingBox(), &box); // Check the results are just the first box. EXPECT_TRUE(box.valid()); EXPECT_EQ(box.min_extent().getX(), -4.0f); EXPECT_EQ(box.min_extent().getY(), -2.0f); EXPECT_EQ(box.min_extent().getZ(), -3.0f); EXPECT_EQ(box.max_extent().getX(), 5.0f); EXPECT_EQ(box.max_extent().getY(), 2.0f); EXPECT_EQ(box.max_extent().getZ(), 11.0f); // Check only the otherbox is valid. box = BoundingBox(); box.Add(BoundingBox(Point3(-4.0f, -1.0f, 9.0f), Point3(5.0f, 1.0f, 11.0f)), &box); // Check the results are just the second box. EXPECT_TRUE(box.valid()); EXPECT_EQ(box.min_extent().getX(), -4.0f); EXPECT_EQ(box.min_extent().getY(), -1.0f); EXPECT_EQ(box.min_extent().getZ(), 9.0f); EXPECT_EQ(box.max_extent().getX(), 5.0f); EXPECT_EQ(box.max_extent().getY(), 1.0f); EXPECT_EQ(box.max_extent().getZ(), 11.0f); // Check neither box valid box = BoundingBox(); box.Add(BoundingBox(), &box); // Check the results are not valid. EXPECT_FALSE(box.valid()); } static const float kEpsilon = 0.0001f; // Tests BoundingBox::Mul TEST_F(BoundingBoxTest, Mul) { BoundingBox bounding_box(Point3(-10.0f, 1.0f, -3.0f), Point3(0.0f, 2.0f, 3.0f)); // Rotate around Z 180 degrees and check the values are as expected. BoundingBox box; bounding_box.Mul(Matrix4::rotationZ(3.14159f), &box); EXPECT_TRUE(fabsf(box.min_extent().getX() - 0.0f) < kEpsilon); EXPECT_TRUE(fabsf(box.min_extent().getY() - -2.0f) < kEpsilon); EXPECT_TRUE(fabsf(box.min_extent().getZ() - -3.0f) < kEpsilon); EXPECT_TRUE(fabsf(box.max_extent().getX() - 10.0f) < kEpsilon); EXPECT_TRUE(fabsf(box.max_extent().getY() - -1.0f) < kEpsilon); EXPECT_TRUE(fabsf(box.max_extent().getZ() - 3.0f) < kEpsilon); } // Tests BoundingBox::IntersectRay TEST_F(BoundingBoxTest, IntersectRay) { BoundingBox bounding_box(Point3(-1.0f, -2.0f, -3.0f), Point3(1.0f, 2.0f, 3.0f)); // Check a ray that collides. RayIntersectionInfo info; bounding_box.IntersectRay(Point3(-2.0f, -4.0f, -6.0f), Point3(2.0f, 4.0f, 6.0f), &info); EXPECT_TRUE(info.valid()); EXPECT_TRUE(info.intersected()); EXPECT_TRUE(fabsf(info.position().getX() - -1.0f) < kEpsilon); EXPECT_TRUE(fabsf(info.position().getY() - -2.0f) < kEpsilon); EXPECT_TRUE(fabsf(info.position().getZ() - -3.0f) < kEpsilon); // Check a ray that misses. bounding_box.IntersectRay(Point3(2.0f, 4.0f, 6.0f), Point3(12.0f, 14.0f, 16.0f), &info); EXPECT_TRUE(info.valid()); EXPECT_FALSE(info.intersected()); // Check that an invalid box returns an invalid ray. BoundingBox invalid_box; invalid_box.IntersectRay(Point3(2.0f, 4.0f, 6.0f), Point3(12.0f, 14.0f, 16.0f), &info); EXPECT_FALSE(info.valid()); } // Tests BoundingBox::InFrustum TEST_F(BoundingBoxTest, InFrustum) { Matrix4 view(Matrix4::lookAt(Point3(0.0f, 0.0f, 0.0f), Point3(0.0f, 0.0f, 1.0f), Vector3(0.0f, 1.0f, 0.0f))); Matrix4 projection(Vectormath::Aos::CreatePerspectiveMatrix( 45.0f * 180.0f / 3.14159f, 4.0f / 3.0f, 1.0, 10.0)); Matrix4 frustum(projection * view); // Check a box completely inside the frustum. EXPECT_TRUE(BoundingBox(Point3(1.0f, 1.0f, 1.0f), Point3(2.0f, 2.0f, 2.0f)).InFrustum(frustum)); // Check a box completely outside the frustum. EXPECT_FALSE(BoundingBox(Point3(11.0f, 11.0f, 11.0f), Point3(12.0f, 12.0f, 12.0f)).InFrustum(frustum)); // Check a box crossing the edge of the frustum. EXPECT_TRUE(BoundingBox(Point3(1.0f, 1.0f, 0.0f), Point3(2.0f, 2.0f, 2.0f)).InFrustum(frustum)); } #if 0 // TODO: change this to a ParamOperation // Tests ParamBindFloat3sToBoundingBox TEST_F(BoundingBoxTest, ParamBindFloat3sToBoundingBox) { Client client(g_service_locator); client.Init(); Pack* pack = client.CreatePack(); Transform* transform = pack->Create(); ASSERT_TRUE(transform != NULL); ParamBoundingBox* box_param = transform->CreateParam("foo"); ASSERT_TRUE(box_param != NULL); ParamBindFloat3sToBoundingBox* bind = pack->Create(); ASSERT_TRUE(bind != NULL); ParamFloat3* param1 = transform->CreateParam("f1"); ParamFloat3* param2 = transform->CreateParam("f2"); ASSERT_TRUE(param1 != NULL); ASSERT_TRUE(param2 != NULL); // Check that we can bind to the inputs and outputs. EXPECT_TRUE( bind->BindInput(ParamBindFloat3sToBoundingBox::kInput1Name, param1)); EXPECT_TRUE( bind->BindInput(ParamBindFloat3sToBoundingBox::kInput2Name, param2)); EXPECT_TRUE( bind->BindOutput(ParamBindFloat3sToBoundingBox::kOutputName, box_param)); // Check that if we set the values, the make it through the bind and update // our bounding box. param1->set_value(Float3(-1.0f, 2.0f, -3.0f)); param2->set_value(Float3(1.0f, -2.0f, 3.0f)); BoundingBox box(box_param->value()); EXPECT_EQ(box.min_extent()[0], -1.0f); EXPECT_EQ(box.min_extent()[1], -2.0f); EXPECT_EQ(box.min_extent()[2], -3.0f); EXPECT_EQ(box.max_extent()[0], 1.0f); EXPECT_EQ(box.max_extent()[1], 2.0f); EXPECT_EQ(box.max_extent()[2], 3.0f); pack->Destroy(); } #endif } // namespace o3d