mirror of https://github.com/mosra/magnum.git
4 changed files with 963 additions and 30 deletions
@ -0,0 +1,222 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 |
||||
Vladimír Vondruš <mosra@centrum.cz> |
||||
Copyright © 2018 Jonathan Hale <squareys@googlemail.com> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a |
||||
copy of this software and associated documentation files (the "Software"), |
||||
to deal in the Software without restriction, including without limitation |
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
and/or sell copies of the Software, and to permit persons to whom the |
||||
Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included |
||||
in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
DEALINGS IN THE SOFTWARE. |
||||
*/ |
||||
|
||||
#include <Corrade/TestSuite/Tester.h> |
||||
|
||||
#include <random> |
||||
#include <tuple> |
||||
#include <utility> |
||||
|
||||
#include "Magnum/Math/Geometry/Intersection.h" |
||||
#include "Magnum/Math/Angle.h" |
||||
#include "Magnum/Math/Matrix4.h" |
||||
|
||||
namespace Magnum { namespace Math { namespace Geometry { namespace Test { |
||||
|
||||
template<class T> bool rangeFrustumNaive(const Math::Range3D<T>& box, const Math::Frustum<T>& frustum) { |
||||
for(const Math::Vector4<T>& plane: frustum.planes()) { |
||||
bool cornerHit = 0; |
||||
|
||||
for(UnsignedByte c = 0; c != 8; ++c) { |
||||
const Math::Vector3<T> corner = Math::lerp(box.min(), box.max(), Math::BoolVector<3>{c}); |
||||
|
||||
if(Distance::pointPlaneScaled<T>(corner, plane) >= T(0)) { |
||||
cornerHit = true; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
/* All corners are outside this plane */ |
||||
if(!cornerHit) return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/* @brief Ground truth, slow sphere cone intersection - calculating exact distances,
|
||||
* no optimizations, no precomputations |
||||
* @param sphereCenter Sphere center |
||||
* @param radius Sphere radius |
||||
* @param origin Origin of the cone |
||||
* @param normal Cone normal |
||||
* @param angle Cone opening angle (0 < angle < pi). |
||||
* |
||||
* Returns `true` if the sphere intersects with the cone. */ |
||||
template<class T> bool sphereConeGT( |
||||
const Math::Vector3<T>& sphereCenter, const T radius, |
||||
const Math::Vector3<T>& origin, const Math::Vector3<T>& normal, const Math::Rad<T> angle) { |
||||
const Math::Vector3<T> diff = sphereCenter - origin; |
||||
const Math::Vector3<T> dir = diff.normalized(); |
||||
const Math::Rad<T> halfAngle = angle/T(2); |
||||
|
||||
/* Compute angle between normal and point */ |
||||
const Math::Rad<T> actual = Math::acos(dot(normal, dir)); |
||||
|
||||
/* Distance from cone surface */ |
||||
const T distanceFromCone = Math::sin(actual - halfAngle)*diff.length(); |
||||
|
||||
/* Either the sphere center lies in cone, or cone is max radius away from the cone */ |
||||
return actual <= halfAngle || distanceFromCone <= radius; |
||||
} |
||||
|
||||
template<class T> |
||||
Math::Matrix4<T> coneViewFromCone(const Math::Vector3<T>& origin, const Math::Vector3<T>& normal) { |
||||
return Math::Matrix4<T>::lookAt(origin, origin + normal, Math::Vector3<T>::yAxis()).inverted(); |
||||
} |
||||
|
||||
typedef Math::Vector2<Float> Vector2; |
||||
typedef Math::Vector3<Float> Vector3; |
||||
typedef Math::Vector4<Float> Vector4; |
||||
typedef Math::Matrix4<Float> Matrix4; |
||||
typedef Math::Frustum<Float> Frustum; |
||||
typedef Math::Range3D<Float> Range3D; |
||||
typedef Math::Deg<Float> Deg; |
||||
typedef Math::Rad<Float> Rad; |
||||
|
||||
struct IntersectionBenchmark: Corrade::TestSuite::Tester { |
||||
explicit IntersectionBenchmark(); |
||||
|
||||
void rangeFrustumNaive(); |
||||
void rangeFrustum(); |
||||
|
||||
void rangeCone(); |
||||
|
||||
void sphereFrustum(); |
||||
|
||||
void sphereConeNaive(); |
||||
void sphereCone(); |
||||
void sphereConeView(); |
||||
|
||||
Frustum _frustum; |
||||
std::tuple<Vector3, Vector3, Rad> _cone; |
||||
Matrix4 _coneView; |
||||
|
||||
std::vector<Range3D> _boxes; |
||||
std::vector<Vector4> _spheres; |
||||
}; |
||||
|
||||
IntersectionBenchmark::IntersectionBenchmark() { |
||||
addBenchmarks({&IntersectionBenchmark::rangeFrustumNaive, |
||||
&IntersectionBenchmark::rangeFrustum, |
||||
|
||||
&IntersectionBenchmark::rangeCone, |
||||
|
||||
&IntersectionBenchmark::sphereFrustum, |
||||
|
||||
&IntersectionBenchmark::sphereConeNaive, |
||||
&IntersectionBenchmark::sphereCone, |
||||
&IntersectionBenchmark::sphereConeView}, 25); |
||||
|
||||
/* Generate random data for the benchmarks */ |
||||
std::random_device rnd; |
||||
std::mt19937 g(rnd()); |
||||
/* Position distribution */ |
||||
std::uniform_real_distribution<float> pd(-10.0f, 10.0f); |
||||
/* Cone angle distribution */ |
||||
std::uniform_real_distribution<float> ad(1.0f, 179.0f); |
||||
|
||||
_cone = std::make_tuple(Vector3{pd(g), pd(g), pd(g)}, |
||||
Vector3{pd(g), pd(g), pd(g)}.normalized(), |
||||
Rad(Deg(ad(g)))); |
||||
_coneView = coneViewFromCone(std::get<0>(_cone), std::get<1>(_cone)); |
||||
_frustum = Frustum::fromMatrix(_coneView*Matrix4::perspectiveProjection(std::get<2>(_cone), 1.0f, 0.001f, 100.0f)); |
||||
|
||||
_boxes.reserve(512); |
||||
_spheres.reserve(512); |
||||
for(int i = 0; i < 512; ++i) { |
||||
Vector3 center{pd(g), pd(g), pd(g)}; |
||||
Vector3 extents{pd(g), pd(g), pd(g)}; |
||||
_boxes.emplace_back(center - extents, center + extents); |
||||
_spheres.emplace_back(center, extents.length()); |
||||
} |
||||
} |
||||
|
||||
void IntersectionBenchmark::rangeFrustumNaive() { |
||||
volatile bool b = false; |
||||
CORRADE_BENCHMARK(50) for(auto& box: _boxes) { |
||||
b = b ^ Test::rangeFrustumNaive<Float>(box, _frustum); |
||||
} |
||||
} |
||||
|
||||
void IntersectionBenchmark::rangeFrustum() { |
||||
volatile bool b = false; |
||||
CORRADE_BENCHMARK(50) for(auto& box: _boxes) { |
||||
b = b ^ Intersection::rangeFrustum(box, _frustum); |
||||
} |
||||
} |
||||
|
||||
void IntersectionBenchmark::rangeCone() { |
||||
volatile bool b = false; |
||||
CORRADE_BENCHMARK(50) { |
||||
const Float tanAngle = Math::tan(std::get<2>(_cone)); |
||||
const Float tanAngleSqPlusOne = tanAngle*tanAngle + 1.0f; |
||||
for(auto& box: _boxes) { |
||||
b = b ^ Intersection::rangeCone(box, std::get<0>(_cone), std::get<1>(_cone), tanAngleSqPlusOne); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void IntersectionBenchmark::sphereFrustum() { |
||||
volatile bool b = false; |
||||
CORRADE_BENCHMARK(50) for(auto& sphere: _spheres) { |
||||
b = b ^ Intersection::sphereFrustum(sphere.xyz(), sphere.w(), _frustum); |
||||
} |
||||
} |
||||
|
||||
void IntersectionBenchmark::sphereConeNaive() { |
||||
volatile bool b = false; |
||||
CORRADE_BENCHMARK(50) for(auto& sphere: _spheres) { |
||||
b = b ^ sphereConeGT<Float>(sphere.xyz(), sphere.w(), std::get<0>(_cone), std::get<1>(_cone), std::get<2>(_cone)); |
||||
} |
||||
} |
||||
|
||||
void IntersectionBenchmark::sphereCone() { |
||||
volatile bool b = false; |
||||
CORRADE_BENCHMARK(50) { |
||||
const Float sinAngle = Math::sin(std::get<2>(_cone)); |
||||
const Float tanAngle = Math::tan(std::get<2>(_cone)); |
||||
const Float tanAngleSqPlusOne = tanAngle*tanAngle + 1.0f; |
||||
for(auto& sphere: _spheres) { |
||||
b = b ^ Intersection::sphereCone(sphere.xyz(), sphere.w(), std::get<0>(_cone), std::get<1>(_cone), sinAngle, tanAngleSqPlusOne); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void IntersectionBenchmark::sphereConeView() { |
||||
volatile bool b = false; |
||||
CORRADE_BENCHMARK(50) { |
||||
const Float sinAngle = Math::sin(std::get<2>(_cone)); |
||||
const Float cosAngle = Math::cos(std::get<2>(_cone)); |
||||
const Float tanAngle = Math::tan(std::get<2>(_cone)); |
||||
for(auto& sphere: _spheres) { |
||||
b = b ^ Intersection::sphereConeView(sphere.xyz(), sphere.w(), _coneView, sinAngle, cosAngle, tanAngle); |
||||
} |
||||
} |
||||
} |
||||
|
||||
}}}} |
||||
|
||||
CORRADE_TEST_MAIN(Magnum::Math::Geometry::Test::IntersectionBenchmark) |
||||
Loading…
Reference in new issue