// SphereLib: the 40250 sphere tree (verbatim src/port/SphereLib) that CCullingManager builds with the same // factory parameters. Checks the range, 2D point, ray and frustum queries CCullingManager makes and the // Frustum plane test on its own. #include "SphereLib/StdAfx.h" #include "SphereLib/spherepack.h" #include #include static int g_failures = 0; #define CHECK(cond) \ do { \ if (!(cond)) { \ std::fprintf(stderr, "%s:%d: CHECK(%s)\n", __FILE__, __LINE__, #cond); \ ++g_failures; \ } \ } while (0) namespace { // Collects the user data of the leaves a query reports, like CCullingManager's callbacks. struct Collector : SpherePackCallback { std::set hits; std::set hidden; void Add(SpherePack * sphere) { hits.insert(long(reinterpret_cast(sphere->GetUserData()))); } void VisibilityCallback(const Frustum &, SpherePack * sphere, ViewState state) override { if (state == VS_OUTSIDE) hidden.insert(long(reinterpret_cast(sphere->GetUserData()))); else Add(sphere); } void RayTraceCallback(const Vector3d &, const Vector3d &, float, const Vector3d &, SpherePack * sphere) override { Add(sphere); } void RangeTestCallback(const Vector3d &, float, SpherePack * sphere, ViewState) override { Add(sphere); } void PointTest2dCallback(const Vector3d &, SpherePack * sphere, ViewState) override { Add(sphere); } }; void * Tag(long v) { return reinterpret_cast(intptr_t(v)); } void TestFrustumPlanes() { // Identity view-projection: the D3D clip box x,y in [-1,1], z in [0,1]. D3DXMATRIX m; D3DXMatrixIdentity(&m); Frustum f; f.BuildViewFrustum(m); CHECK(f.ViewVolumeTest(Vector3d(0.0f, 0.0f, 0.5f), 0.1f) == VS_INSIDE); CHECK(f.ViewVolumeTest(Vector3d(1.0f, 0.0f, 0.5f), 0.1f) == VS_PARTIAL); CHECK(f.ViewVolumeTest(Vector3d(5.0f, 0.0f, 0.5f), 0.1f) == VS_OUTSIDE); CHECK(f.ViewVolumeTest(Vector3d(0.0f, 0.0f, -1.0f), 0.1f) == VS_OUTSIDE); } void TestFactoryQueries() { SpherePackFactory factory(10000, 6400, 1600, 400); // CCullingManager::CCullingManager SpherePack * a = factory.AddSphere_(Vector3d(0.0f, 0.0f, 0.0f), 50.0f, Tag(1), false); SpherePack * b = factory.AddSphere_(Vector3d(100.0f, 0.0f, 0.0f), 50.0f, Tag(2), false); factory.AddSphere_(Vector3d(20000.0f, 0.0f, 0.0f), 50.0f, Tag(3), false); factory.Process(); { Collector c; factory.RangeTest(Vector3d(0.0f, 0.0f, 0.0f), 500.0f, &c); CHECK((c.hits == std::set{1, 2})); } { Collector c; factory.PointTest2d(Vector3d(100.0f, 10.0f, 0.0f), &c); CHECK((c.hits == std::set{2})); } { // The second argument is the ray (direction times length), as CCullingManager::FindRay passes it. Collector c; factory.RayTrace(Vector3d(-1000.0f, 0.0f, 0.0f), Vector3d(2000.0f, 0.0f, 0.0f), &c); CHECK((c.hits == std::set{1, 2})); Collector shortRay; factory.RayTrace(Vector3d(-1000.0f, 0.0f, 0.0f), Vector3d(1000.0f, 0.0f, 0.0f), &shortRay); CHECK((shortRay.hits == std::set{1})); } { // Clip box [-1000,1000] x [-1000,1000] x [-500,500] in world space. D3DXMATRIX m; D3DXMatrixIdentity(&m); m._11 = m._22 = m._33 = 0.001f; m._43 = 0.5f; Frustum f; f.BuildViewFrustum(m); Collector c; factory.FrustumTest(f, &c); CHECK((c.hits == std::set{1, 2})); CHECK(c.hidden.count(3) == 1); } // A moved sphere is found at its new position once the factory re-integrates it. b->NewPos(Vector3d(20000.0f, 100.0f, 0.0f)); factory.Process(); { Collector c; factory.RangeTest(Vector3d(20000.0f, 0.0f, 0.0f), 500.0f, &c); CHECK((c.hits == std::set{2, 3})); } factory.Remove(a); factory.Process(); { Collector c; factory.RangeTest(Vector3d(0.0f, 0.0f, 0.0f), 500.0f, &c); CHECK(c.hits.empty()); } } } // namespace int main() { TestFrustumPlanes(); TestFactoryQueries(); if (g_failures) std::fprintf(stderr, "port_spherelib_test: %d failure(s)\n", g_failures); else std::puts("port_spherelib_test: ok"); return g_failures ? 1 : 0; }