Browse Source

Merge 85f3f07348 into bd8d807e1c

pull/185/merge
Jonathan Hale 10 years ago committed by GitHub
parent
commit
01459976e3
  1. 1
      src/Magnum/Math/CMakeLists.txt
  2. 101
      src/Magnum/Math/Frustum.h
  3. 56
      src/Magnum/Math/Geometry/Distance.h
  4. 78
      src/Magnum/Math/Geometry/Intersection.h
  5. 4
      src/Magnum/Math/Geometry/Test/CMakeLists.txt
  6. 37
      src/Magnum/Math/Geometry/Test/DistanceTest.cpp
  7. 43
      src/Magnum/Math/Geometry/Test/IntersectionTest.cpp
  8. 1
      src/Magnum/Math/Test/CMakeLists.txt
  9. 89
      src/Magnum/Math/Test/FrustumTest.cpp

1
src/Magnum/Math/CMakeLists.txt

@ -33,6 +33,7 @@ set(MagnumMath_HEADERS
Dual.h Dual.h
DualComplex.h DualComplex.h
DualQuaternion.h DualQuaternion.h
Frustum.h
Functions.h Functions.h
Math.h Math.h
TypeTraits.h TypeTraits.h

101
src/Magnum/Math/Frustum.h

@ -0,0 +1,101 @@
#ifndef Magnum_Math_Frustum_h
#define Magnum_Math_Frustum_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016
Vladimír Vondruš <mosra@centrum.cz>
Copyright © 2016 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.
*/
/** @file
* @brief Class @ref Magnum::Math::Frustum
*/
#include <Corrade/configure.h>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Utility/Debug.h>
#include "Magnum/Math/Matrix4.h"
#include "Magnum/Math/Vector4.h"
namespace Magnum { namespace Math {
/**
@brief Camera frustum
*/
template<class T> class Frustum {
public:
/**
* @brief Create a frustum from projection matrix
*/
static Frustum<T> fromMatrix(const Matrix4<T>& m) {
return Frustum{
m.row(3) + m.row(0),
m.row(3) - m.row(0),
m.row(3) + m.row(1),
m.row(3) - m.row(1),
m.row(3) + m.row(2),
m.row(3) - m.row(2)
};
}
/**
* @brief Construct frustum from frustum planes
*/
constexpr /*implicit*/ Frustum(const Vector4<T>& left, const Vector4<T>& right, const Vector4<T>& bottom, const Vector4<T>& top, const Vector4<T>& near, const Vector4<T>& far): _data{left, right, bottom, top, near, far} {}
/**
* @brief Raw data
* @return One-dimensional array of length `24`.
*/
T* data() { return _data[0].data(); }
/** @overload */
constexpr const T* data() const { return _data[0].data(); }
/**
* @brief The frustum planes
*
* In order left (index `0`), right (index `1`), bottom (index `1`),
* top (index `3`), near (index `4`), far (index `5`).
*/
constexpr Corrade::Containers::StaticArrayView<6, const Vector4<T>> planes() const {
return Corrade::Containers::StaticArrayView<6, const Vector4<T>>{_data};
}
/**
* @brief Plane at given index
*
* In order left (index `0`), right (index `1`), bottom (index `1`),
* top (index `3`), near (index `4`), far (index `5`).
*/
constexpr Vector4<T> operator[](std::size_t i) const { return _data[i]; }
private:
Vector4<T> _data[6];
};
}}
#endif

56
src/Magnum/Math/Geometry/Distance.h

@ -31,6 +31,7 @@
#include "Magnum/Math/Functions.h" #include "Magnum/Math/Functions.h"
#include "Magnum/Math/Vector3.h" #include "Magnum/Math/Vector3.h"
#include "Magnum/Math/Vector4.h"
namespace Magnum { namespace Math { namespace Geometry { namespace Magnum { namespace Math { namespace Geometry {
@ -162,6 +163,61 @@ class Distance {
* the square root. * the square root.
*/ */
template<class T> static T lineSegmentPointSquared(const Vector3<T>& a, const Vector3<T>& b, const Vector3<T>& point); template<class T> static T lineSegmentPointSquared(const Vector3<T>& a, const Vector3<T>& b, const Vector3<T>& point);
/**
* @brief Distance of point from plane
*
* The distance **d** is computed from point **p** and plane with normal
* **n** and **w** using: @f[
* d = \frac{\sum_i^3 (p \cdot n) + w}{\left| n \right|}
* @f]
* The distance is negative if the point lies behind the plane.
*
* In cases where the planes normal is a unit vector, @ref pointPlaneUnnormalized()
* is more efficient.
*
* If merely the sign of the distance is of interest, @ref pointPlaneScaled()
* is more efficient.
*/
template<class T> static T pointPlane(const Vector3<T>& point, const Vector4<T>& plane) {
return pointPlaneScaled<T>(point, plane)/plane.xyz().length();
}
/**
* @brief Distance of point from plane, scaled by the length of the planes normal
*
* The distance **d** is computed from point **p** and plane with normal
* **n** and **w** using: @f[
* d = \sum_i^3 (p \cdot n) + w
* @f]
* The distance is negative if the point lies behind the plane.
*
* More efficient than @ref pointPlane() when merely the sign of the distance is
* of interest, for example when testing on which half space of the plane the
* point lies.
*/
template<class T> static T pointPlaneScaled(const Vector3<T>& point, const Vector4<T>& plane) {
return (plane.xyz()*point).sum() + plane.w();
}
/**
* @brief Distance of point from plane with normalized normal
*
* The distance **d** is computed from point **p** and plane with normal
* **n** and **w** using: @f[
* d = \sum_i^3 (p \cdot n) + w
* @f]
* The distance is negative if the point lies behind the plane.
*
* More efficient than @ref pointPlane() in cases where the planes normal is
* normalized.
*/
template<class T> static T pointPlaneNormalized(const Vector3<T>& point, const Vector4<T>& plane) {
CORRADE_ASSERT(plane.xyz().isNormalized(),
"Math::Geometry::Distance::pointPlaneNormalized(): the planes normal is not a unit vector", {});
return pointPlaneScaled<T>(point, plane);
}
}; };
template<class T> T Distance::lineSegmentPoint(const Vector2<T>& a, const Vector2<T>& b, const Vector2<T>& point) { template<class T> T Distance::lineSegmentPoint(const Vector2<T>& a, const Vector2<T>& b, const Vector2<T>& point) {

78
src/Magnum/Math/Geometry/Intersection.h

@ -29,6 +29,9 @@
* @brief Class @ref Magnum::Math::Geometry::Intersection * @brief Class @ref Magnum::Math::Geometry::Intersection
*/ */
#include "Magnum/Math/Frustum.h"
#include "Magnum/Math/Geometry/Distance.h"
#include "Magnum/Math/Range.h"
#include "Magnum/Math/Vector3.h" #include "Magnum/Math/Vector3.h"
namespace Magnum { namespace Math { namespace Geometry { namespace Magnum { namespace Math { namespace Geometry {
@ -123,8 +126,83 @@ class Intersection {
const T f = dot(planePosition, planeNormal); const T f = dot(planePosition, planeNormal);
return (f-dot(planeNormal, p))/dot(planeNormal, r); return (f-dot(planeNormal, p))/dot(planeNormal, r);
} }
/**
* @brief Intersection of a point and a camera frustum
* @param point Point
* @param frustum Frustum planes with normals pointing outwards
* @return `true` if the point is on or inside the frustum.
*
* Checks for each plane of the frustum whether the point is behind the plane
* (the points distance from the plane is negative) using
* @ref Distance::pointPlaneScaled().
*/
template<class T> static bool pointFrustum(const Vector3<T>& point, const Frustum<T>& frustum);
/**
* @brief Intersection of a range and a camera frustum
* @return `true` if the box intersects with the camera frustum.
*
* Counts for each plane of the frustum how many points of the box lie in
* front of the plane (outside of the frustum). If none, the box must lie
* entirely outside of the frustum and there is no intersection.
* Else, the box is considered as intersecting, even if it is merely corners
* of the box overlapping with corners of the frustum, since checking the
* corners is less efficient.
*/
template<class T> static bool boxFrustum(const Range3D<T>& box, const Frustum<T>& frustum);
}; };
template<class T> bool Intersection::pointFrustum(const Vector3<T>& point, const Frustum<T>& frustum) {
for(const Vector4<T>& f : frustum.planes()) {
if(Distance::pointPlaneScaled<T>(point, f) < T(0)) {
/* the point is in front of one of the frustum planes (normals point outwards) */
return false;
}
}
return true;
}
template<class T> bool Intersection::boxFrustum(const Range3D<T>& box, const Frustum<T>& frustum) {
/*
* Create the 8 vertices of the box from the 2 given vertices min and max
* Check for each corner of an octant whether it is inside the frustum.
* If only some of the corners are inside, the octant requires further checks.
*/
int planes = 0;
for(const Vector4<T>& plane : frustum.planes()) {
int corners = 0;
for(UnsignedByte c = 0; c < 8; ++c) {
const Vector3<T> corner = Math::lerp(box.min(), box.max(), Math::BoolVector<3>{c});
if(Distance::pointPlaneScaled<T>(corner, plane) >= T(0)) {
++corners;
}
}
if(corners == 0) {
/* all corners are outside this plane */
return false;
}
if(corners == 8) {
++planes;
}
}
if(planes == 6) {
return true;
}
// potentially check corners here to avoid false positives!
return true;
}
}}} }}}
#endif #endif

4
src/Magnum/Math/Geometry/Test/CMakeLists.txt

@ -25,3 +25,7 @@
corrade_add_test(MathGeometryDistanceTest DistanceTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathGeometryDistanceTest DistanceTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathGeometryIntersectionTest IntersectionTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathGeometryIntersectionTest IntersectionTest.cpp LIBRARIES MagnumMathTestLib)
set_property(TARGET
MathGeometryDistanceTest
APPEND PROPERTY COMPILE_DEFINITIONS "CORRADE_GRACEFUL_ASSERT")

37
src/Magnum/Math/Geometry/Test/DistanceTest.cpp

@ -23,6 +23,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <sstream>
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include "Magnum/Math/Constants.h" #include "Magnum/Math/Constants.h"
@ -37,17 +38,24 @@ struct DistanceTest: Corrade::TestSuite::Tester {
void linePoint3D(); void linePoint3D();
void lineSegmentPoint2D(); void lineSegmentPoint2D();
void lineSegmentPoint3D(); void lineSegmentPoint3D();
void pointPlane();
void pointPlaneScaled();
void pointPlaneNormalized();
}; };
typedef Math::Vector2<Float> Vector2; typedef Math::Vector2<Float> Vector2;
typedef Math::Vector3<Float> Vector3; typedef Math::Vector3<Float> Vector3;
typedef Math::Vector4<Float> Vector4;
typedef Math::Constants<Float> Constants; typedef Math::Constants<Float> Constants;
DistanceTest::DistanceTest() { DistanceTest::DistanceTest() {
addTests({&DistanceTest::linePoint2D, addTests({&DistanceTest::linePoint2D,
&DistanceTest::linePoint3D, &DistanceTest::linePoint3D,
&DistanceTest::lineSegmentPoint2D, &DistanceTest::lineSegmentPoint2D,
&DistanceTest::lineSegmentPoint3D}); &DistanceTest::lineSegmentPoint3D,
&DistanceTest::pointPlane,
&DistanceTest::pointPlaneScaled,
&DistanceTest::pointPlaneNormalized});
} }
void DistanceTest::linePoint2D() { void DistanceTest::linePoint2D() {
@ -156,6 +164,33 @@ void DistanceTest::lineSegmentPoint3D() {
Constants::sqrt2()); Constants::sqrt2());
} }
void DistanceTest::pointPlane() {
Vector3 point{0.0f, 0.0f, 0.0f};
Vector4 plane{3.0f, 0.0f, 4.0f, 5.0f};
CORRADE_COMPARE(Distance::pointPlane(point, plane), 1.0f);
}
void DistanceTest::pointPlaneScaled() {
Vector3 point{1.0f, 1.0f, 1.0f};
Vector4 plane{2.0f, 2.0f, 2.0f, 0.0f};
CORRADE_COMPARE(Distance::pointPlaneScaled(point, plane), 6.0f);
}
void DistanceTest::pointPlaneNormalized() {
Vector3 point{1.0f, 2.0f, 3.0f};
Vector4 invalidPlane{2.0f, 2.0f, 2.0f, 0.0f};
const Vector4 plane{0.0f, 1.0f, 0.0f, 1.0f};
CORRADE_COMPARE(Distance::pointPlaneNormalized(point, plane), 3.0f);
std::ostringstream o;
Error redirectError{&o};
Distance::pointPlaneNormalized(point, invalidPlane);
CORRADE_COMPARE(o.str(), "Math::Geometry::Distance::pointPlaneNormalized(): the planes normal is not a unit vector\n");
}
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::Math::Geometry::Test::DistanceTest) CORRADE_TEST_MAIN(Magnum::Math::Geometry::Test::DistanceTest)

43
src/Magnum/Math/Geometry/Test/IntersectionTest.cpp

@ -3,6 +3,7 @@
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016 Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016
Vladimír Vondruš <mosra@centrum.cz> Vladimír Vondruš <mosra@centrum.cz>
Copyright © 2016 Jonathan Hale <squareys@googlemail.com>
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"), copy of this software and associated documentation files (the "Software"),
@ -34,15 +35,22 @@ struct IntersectionTest: Corrade::TestSuite::Tester {
void planeLine(); void planeLine();
void lineLine(); void lineLine();
void pointFrustum();
void boxFrustum();
}; };
typedef Math::Vector2<Float> Vector2; typedef Math::Vector2<Float> Vector2;
typedef Math::Vector3<Float> Vector3; typedef Math::Vector3<Float> Vector3;
typedef Math::Vector4<Float> Vector4;
typedef Math::Frustum<Float> Frustum;
typedef Math::Constants<Float> Constants; typedef Math::Constants<Float> Constants;
typedef Math::Range3D<Float> Range3D;
IntersectionTest::IntersectionTest() { IntersectionTest::IntersectionTest() {
addTests({&IntersectionTest::planeLine, addTests({&IntersectionTest::planeLine,
&IntersectionTest::lineLine}); &IntersectionTest::lineLine,
&IntersectionTest::pointFrustum,
&IntersectionTest::boxFrustum});
} }
void IntersectionTest::planeLine() { void IntersectionTest::planeLine() {
@ -97,6 +105,39 @@ void IntersectionTest::lineLine() {
{0.0f, 0.0f}, {1.0f, 2.0f}), Constants::inf()); {0.0f, 0.0f}, {1.0f, 2.0f}), Constants::inf());
} }
void IntersectionTest::pointFrustum() {
const Frustum frustum{
{1.0f, 0.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f, 10.0f},
{0.0f, 1.0f, 0.0f, 0.0f},
{0.0f, -1.0f, 0.0f, 10.0f},
{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 10.0f}};
/* Point on edge */
CORRADE_VERIFY(Intersection::pointFrustum<Float>(Vector3{}, frustum));
/* Point inside */
CORRADE_VERIFY(Intersection::pointFrustum<Float>(Vector3{5.0f, 5.0f, 5.0f}, frustum));
/* Point outside */
CORRADE_VERIFY(!Intersection::pointFrustum<Float>(Vector3{0.0f, 0.0f, 100.0f}, frustum));
}
void IntersectionTest::boxFrustum() {
const Frustum frustum{
{1.0f, 0.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f, 10.0f},
{0.0f, 1.0f, 0.0f, 0.0f},
{0.0f, -1.0f, 0.0f, 10.0f},
{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 10.0f}};
CORRADE_VERIFY(Intersection::boxFrustum<Float>(Range3D{Vector3{1.0f}, Vector3{2.0f}}, frustum));
/* Bigger than frustum, but still intersects */
CORRADE_VERIFY(Intersection::boxFrustum<Float>(Range3D{Vector3{-100.0f}, Vector3{100.0f}}, frustum));
/* Outside of frustum */
CORRADE_VERIFY(!Intersection::boxFrustum<Float>(Range3D{Vector3{-10.0f}, Vector3{-5.0f}}, frustum));
}
}}}} }}}}
CORRADE_TEST_MAIN(Magnum::Math::Geometry::Test::IntersectionTest) CORRADE_TEST_MAIN(Magnum::Math::Geometry::Test::IntersectionTest)

1
src/Magnum/Math/Test/CMakeLists.txt

@ -52,6 +52,7 @@ corrade_add_test(MathQuaternionTest QuaternionTest.cpp LIBRARIES MagnumMathTestL
corrade_add_test(MathDualQuaternionTest DualQuaternionTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathDualQuaternionTest DualQuaternionTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathBezierTest BezierTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(MathBezierTest BezierTest.cpp LIBRARIES MagnumMathTestLib)
corrade_add_test(MathFrustumTest FrustumTest.cpp LIBRARIES MagnumMathTestLib)
set_property(TARGET set_property(TARGET
MathVectorTest MathVectorTest

89
src/Magnum/Math/Test/FrustumTest.cpp

@ -0,0 +1,89 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016
Vladimír Vondruš <mosra@centrum.cz>
Copyright © 2016 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 <cmath>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Math/Frustum.h"
using namespace Corrade;
namespace Magnum { namespace Math { namespace Test {
struct FrustumTest: TestSuite::Tester {
explicit FrustumTest();
void construct();
void constructFromMatrix();
};
typedef Vector4<Float> Vector4;
typedef Matrix4<Float> Matrix4;
typedef Frustum<Float> Frustum;
typedef Deg<Float> Degf;
FrustumTest::FrustumTest() {
addTests({&FrustumTest::construct,
&FrustumTest::constructFromMatrix});
}
void FrustumTest::construct() {
Vector4 planes[6]{
{-1.0f, 0.0f, 0.0f, 1.0f},
{ 1.0f, 0.0f, 0.0f, 1.0f},
{ 0.0f,-1.0f, 0.0f, 1.0f},
{ 0.0f, 1.0f, 0.0f, 1.0f},
{ 0.0f, 0.0f,-1.0f, 1.0f},
{ 0.0f, 0.0f, 1.0f, 1.0f}};
Frustum frustum{
planes[0], planes[1],
planes[2], planes[3],
planes[4], planes[5],
};
CORRADE_COMPARE_AS(frustum.planes(), Containers::ArrayView<const Vector4>(planes), TestSuite::Compare::Container);
}
void FrustumTest::constructFromMatrix() {
Vector4 planes[6]{
{ 1.0f, 0.0f,-1.0f, 0.0f},
{-1.0f, 0.0f,-1.0f, 0.0f},
{ 0.0f, 1.0f,-1.0f, 0.0f},
{ 0.0f,-1.0f,-1.0f, 0.0f},
{ 0.0f, 0.0f,-2.22222f,-2.22222f},
{ 0.0f, 0.0f, 0.22222f, 2.22222f}};
const Frustum frustum = Frustum::fromMatrix(
Matrix4::perspectiveProjection(Degf(90.0f), 1.0f, 1.0f, 10.0f));
CORRADE_COMPARE_AS(frustum.planes(), Containers::ArrayView<const Vector4>(planes), TestSuite::Compare::Container);
}
}}}
CORRADE_TEST_MAIN(Magnum::Math::Test::FrustumTest)
Loading…
Cancel
Save