Browse Source

Math: cleanup, code compression.

pull/190/head
Vladimír Vondruš 10 years ago
parent
commit
b7eb367dde
  1. 36
      src/Magnum/Math/Frustum.h
  2. 39
      src/Magnum/Math/Geometry/Distance.h
  3. 63
      src/Magnum/Math/Geometry/Intersection.h
  4. 4
      src/Magnum/Math/Geometry/Test/DistanceTest.cpp
  5. 14
      src/Magnum/Math/Geometry/Test/IntersectionTest.cpp
  6. 51
      src/Magnum/Math/Test/FrustumTest.cpp

36
src/Magnum/Math/Frustum.h

@ -42,27 +42,22 @@ namespace Magnum { namespace Math {
/** /**
@brief Camera frustum @brief Camera frustum
Stores camera frustum planes in order left (index `0`), right (index `1`),
bottom (index `2`), top (index `3`), near (index `4`) and far (index `5`).
*/ */
template<class T> class Frustum { template<class T> class Frustum {
public: public:
/** @brief Create a frustum from projection matrix */
/**
* @brief Create a frustum from projection matrix
*/
static Frustum<T> fromMatrix(const Matrix4<T>& m) { static Frustum<T> fromMatrix(const Matrix4<T>& m) {
return Frustum{ return {m.row(3) + m.row(0),
m.row(3) + m.row(0),
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(1), m.row(3) - m.row(1),
m.row(3) + m.row(2), m.row(3) + m.row(2),
m.row(3) - m.row(2) m.row(3) - m.row(2)};
};
} }
/** /** @brief Constructor */
* @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} {} 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} {}
/** /**
@ -70,26 +65,15 @@ template<class T> class Frustum {
* @return One-dimensional array of length `24`. * @return One-dimensional array of length `24`.
*/ */
T* data() { return _data[0].data(); } T* data() { return _data[0].data(); }
constexpr const T* data() const { return _data[0].data(); } /**< @overload */
/** @overload */ /** @brief Frustum planes */
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 { constexpr Corrade::Containers::StaticArrayView<6, const Vector4<T>> planes() const {
/* GCC 4.7 needs explicit construction */
return Corrade::Containers::StaticArrayView<6, const Vector4<T>>{_data}; return Corrade::Containers::StaticArrayView<6, const Vector4<T>>{_data};
} }
/** /** @brief Plane at given index */
* @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]; } constexpr Vector4<T> operator[](std::size_t i) const { return _data[i]; }
private: private:

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

@ -168,17 +168,16 @@ class Distance {
/** /**
* @brief Distance of point from plane * @brief Distance of point from plane
* *
* The distance **d** is computed from point **p** and plane with normal * The distance **d** is computed from point **p** and plane with
* **n** and **w** using: @f[ * normal **n** and **w** using: @f[
* d = \frac{\sum_i^3 (p \cdot n) + w}{\left| n \right|} * d = \frac{\sum_i^3 (p \cdot n) + w}{\left| n \right|}
* @f] * @f]
* The distance is negative if the point lies behind the plane. * The distance is negative if the point lies behind the plane.
* *
* In cases where the planes normal is a unit vector, @ref pointPlaneUnnormalized() * In cases where the planes normal is a unit vector,
* is more efficient. * @ref pointPlaneUnnormalized() is more efficient. If merely the sign
* * of the distance is of interest, @ref pointPlaneScaled() is more
* If merely the sign of the distance is of interest, @ref pointPlaneScaled() * efficient.
* is more efficient.
*/ */
template<class T> static T pointPlane(const Vector3<T>& point, const Vector4<T>& plane) { template<class T> static T pointPlane(const Vector3<T>& point, const Vector4<T>& plane) {
return pointPlaneScaled<T>(point, plane)/plane.xyz().length(); return pointPlaneScaled<T>(point, plane)/plane.xyz().length();
@ -187,15 +186,16 @@ class Distance {
/** /**
* @brief Distance of point from plane, scaled by the length of the planes normal * @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 * The distance **d** is computed from point **p** and plane with
* **n** and **w** using: @f[ * normal **n** and **w** using: @f[
* d = \sum_i^3 (p \cdot n) + w * d = \sum_i^3 (p \cdot n) + w
* @f] * @f]
* The distance is negative if the point lies behind the plane. * The distance is negative if the point lies behind the plane.
* *
* More efficient than @ref pointPlane() when merely the sign of the distance is * More efficient than @ref pointPlane() when merely the sign of the
* of interest, for example when testing on which half space of the plane the * distance is of interest, for example when testing on which half
* point lies. * space of the plane the point lies.
* @see @ref pointPlaneNormalized()
*/ */
template<class T> static T pointPlaneScaled(const Vector3<T>& point, const Vector4<T>& plane) { template<class T> static T pointPlaneScaled(const Vector3<T>& point, const Vector4<T>& plane) {
return (plane.xyz()*point).sum() + plane.w(); return (plane.xyz()*point).sum() + plane.w();
@ -204,21 +204,22 @@ class Distance {
/** /**
* @brief Distance of point from plane with normalized normal * @brief Distance of point from plane with normalized normal
* *
* The distance **d** is computed from point **p** and plane with normal * The distance **d** is computed from point **p** and plane with
* **n** and **w** using: @f[ * normal **n** and **w** using: @f[
* d = \sum_i^3 (p \cdot n) + w * d = \sum_i^3 (p \cdot n) + w
* @f] * @f]
* The distance is negative if the point lies behind the plane. * The distance is negative if the point lies behind the plane. Expects
* that @p plane normal is normalized.
* *
* More efficient than @ref pointPlane() in cases where the planes normal is * More efficient than @ref pointPlane() in cases where the planes
* normalized. * normal is normalized. Equivalent to @ref pointPlaneScaled() but with
* assertion added on top.
*/ */
template<class T> static T pointPlaneNormalized(const Vector3<T>& point, const Vector4<T>& plane) { template<class T> static T pointPlaneNormalized(const Vector3<T>& point, const Vector4<T>& plane) {
CORRADE_ASSERT(plane.xyz().isNormalized(), CORRADE_ASSERT(plane.xyz().isNormalized(),
"Math::Geometry::Distance::pointPlaneNormalized(): the planes normal is not a unit vector", {}); "Math::Geometry::Distance::pointPlaneNormalized(): plane normal is not an unit vector", {});
return pointPlaneScaled<T>(point, plane); 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) {

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

@ -134,72 +134,61 @@ class Intersection {
* @param frustum Frustum planes with normals pointing outwards * @param frustum Frustum planes with normals pointing outwards
* @return `true` if the point is on or inside the frustum. * @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 * Checks for each plane of the frustum whether the point is behind the
* (the points distance from the plane is negative) using * plane (the points distance from the plane is negative) using
* @ref Distance::pointPlaneScaled(). * @ref Distance::pointPlaneScaled().
*/ */
template<class T> static bool pointFrustum(const Vector3<T>& point, const Frustum<T>& frustum); template<class T> static bool pointFrustum(const Vector3<T>& point, const Frustum<T>& frustum);
/** /**
* @brief Intersection of a range and a camera frustum * @brief Intersection of a range and a camera frustum
* @return `true` if the box intersects with the 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 * Counts for each plane of the frustum how many points of the box lie
* front of the plane (outside of the frustum). If none, the box must lie * in front of the plane (outside of the frustum). If none, the box
* entirely outside of the frustum and there is no intersection. * must lie entirely outside of the frustum and there is no
* Else, the box is considered as intersecting, even if it is merely corners * intersection. Else, the box is considered as intersecting, even if
* of the box overlapping with corners of the frustum, since checking the * it is merely corners of the box overlapping with corners of the
* corners is less efficient. * 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> 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) { template<class T> bool Intersection::pointFrustum(const Vector3<T>& point, const Frustum<T>& frustum) {
for(const Vector4<T>& f : frustum.planes()) { for(const Vector4<T>& plane: frustum.planes()) {
if(Distance::pointPlaneScaled<T>(point, f) < T(0)) { /* The point is in front of one of the frustum planes (normals point
/* the point is in front of one of the frustum planes (normals point outwards) */ outwards) */
if(Distance::pointPlaneScaled<T>(point, plane) < T(0))
return false; return false;
}
} }
return true; return true;
} }
template<class T> bool Intersection::boxFrustum(const Range3D<T>& box, const Frustum<T>& frustum) { 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
* 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
* Check for each corner of an octant whether it is inside the frustum. only some of the corners are inside, the octant requires further checks. */
* If only some of the corners are inside, the octant requires further checks. Int planes = 0;
*/
int planes = 0;
for(const Vector4<T>& plane : frustum.planes()) { for(const Vector4<T>& plane: frustum.planes()) {
int corners = 0; Int corners = 0;
for(UnsignedByte c = 0; c < 8; ++c) { for(UnsignedByte c = 0; c != 8; ++c) {
const Vector3<T> corner = Math::lerp(box.min(), box.max(), Math::BoolVector<3>{c}); const Vector3<T> corner = Math::lerp(box.min(), box.max(), Math::BoolVector<3>{c});
if(Distance::pointPlaneScaled<T>(corner, plane) >= T(0)) { if(Distance::pointPlaneScaled<T>(corner, plane) >= T(0))
++corners; ++corners;
}
} }
if(corners == 0) { /* All corners are outside this plane */
/* all corners are outside this plane */ if(corners == 0) return false;
return false;
}
if(corners == 8) {
++planes;
}
}
if(planes == 6) { if(corners == 8) ++planes;
return true;
} }
// potentially check corners here to avoid false positives! /** @todo potentially check corners here to avoid false positives */
/* if(planes == 6) return true; */
return true; return true;
} }

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

@ -39,6 +39,7 @@ struct DistanceTest: Corrade::TestSuite::Tester {
void linePoint3D(); void linePoint3D();
void lineSegmentPoint2D(); void lineSegmentPoint2D();
void lineSegmentPoint3D(); void lineSegmentPoint3D();
void pointPlane(); void pointPlane();
void pointPlaneScaled(); void pointPlaneScaled();
void pointPlaneNormalized(); void pointPlaneNormalized();
@ -54,6 +55,7 @@ DistanceTest::DistanceTest() {
&DistanceTest::linePoint3D, &DistanceTest::linePoint3D,
&DistanceTest::lineSegmentPoint2D, &DistanceTest::lineSegmentPoint2D,
&DistanceTest::lineSegmentPoint3D, &DistanceTest::lineSegmentPoint3D,
&DistanceTest::pointPlane, &DistanceTest::pointPlane,
&DistanceTest::pointPlaneScaled, &DistanceTest::pointPlaneScaled,
&DistanceTest::pointPlaneNormalized}); &DistanceTest::pointPlaneNormalized});
@ -189,7 +191,7 @@ void DistanceTest::pointPlaneNormalized() {
std::ostringstream o; std::ostringstream o;
Error redirectError{&o}; Error redirectError{&o};
Distance::pointPlaneNormalized(point, invalidPlane); Distance::pointPlaneNormalized(point, invalidPlane);
CORRADE_COMPARE(o.str(), "Math::Geometry::Distance::pointPlaneNormalized(): the planes normal is not a unit vector\n"); CORRADE_COMPARE(o.str(), "Math::Geometry::Distance::pointPlaneNormalized(): plane normal is not an unit vector\n");
} }
}}}} }}}}

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

@ -35,6 +35,7 @@ struct IntersectionTest: Corrade::TestSuite::Tester {
void planeLine(); void planeLine();
void lineLine(); void lineLine();
void pointFrustum(); void pointFrustum();
void boxFrustum(); void boxFrustum();
}; };
@ -49,6 +50,7 @@ typedef Math::Range3D<Float> Range3D;
IntersectionTest::IntersectionTest() { IntersectionTest::IntersectionTest() {
addTests({&IntersectionTest::planeLine, addTests({&IntersectionTest::planeLine,
&IntersectionTest::lineLine, &IntersectionTest::lineLine,
&IntersectionTest::pointFrustum, &IntersectionTest::pointFrustum,
&IntersectionTest::boxFrustum}); &IntersectionTest::boxFrustum});
} }
@ -115,11 +117,11 @@ void IntersectionTest::pointFrustum() {
{0.0f, 0.0f, -1.0f, 10.0f}}; {0.0f, 0.0f, -1.0f, 10.0f}};
/* Point on edge */ /* Point on edge */
CORRADE_VERIFY(Intersection::pointFrustum<Float>(Vector3{}, frustum)); CORRADE_VERIFY(Intersection::pointFrustum({}, frustum));
/* Point inside */ /* Point inside */
CORRADE_VERIFY(Intersection::pointFrustum<Float>(Vector3{5.0f, 5.0f, 5.0f}, frustum)); CORRADE_VERIFY(Intersection::pointFrustum({5.0f, 5.0f, 5.0f}, frustum));
/* Point outside */ /* Point outside */
CORRADE_VERIFY(!Intersection::pointFrustum<Float>(Vector3{0.0f, 0.0f, 100.0f}, frustum)); CORRADE_VERIFY(!Intersection::pointFrustum({0.0f, 0.0f, 100.0f}, frustum));
} }
void IntersectionTest::boxFrustum() { void IntersectionTest::boxFrustum() {
@ -131,11 +133,11 @@ void IntersectionTest::boxFrustum() {
{0.0f, 0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, -1.0f, 10.0f}}; {0.0f, 0.0f, -1.0f, 10.0f}};
CORRADE_VERIFY(Intersection::boxFrustum<Float>(Range3D{Vector3{1.0f}, Vector3{2.0f}}, frustum)); CORRADE_VERIFY(Intersection::boxFrustum({Vector3{1.0f}, Vector3{2.0f}}, frustum));
/* Bigger than frustum, but still intersects */ /* Bigger than frustum, but still intersects */
CORRADE_VERIFY(Intersection::boxFrustum<Float>(Range3D{Vector3{-100.0f}, Vector3{100.0f}}, frustum)); CORRADE_VERIFY(Intersection::boxFrustum(Range3D{Vector3{-100.0f}, Vector3{100.0f}}, frustum));
/* Outside of frustum */ /* Outside of frustum */
CORRADE_VERIFY(!Intersection::boxFrustum<Float>(Range3D{Vector3{-10.0f}, Vector3{-5.0f}}, frustum)); CORRADE_VERIFY(!Intersection::boxFrustum(Range3D{Vector3{-10.0f}, Vector3{-5.0f}}, frustum));
} }
}}}} }}}}

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

@ -24,27 +24,23 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <cmath>
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h> #include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Math/Frustum.h" #include "Magnum/Math/Frustum.h"
using namespace Corrade;
namespace Magnum { namespace Math { namespace Test { namespace Magnum { namespace Math { namespace Test {
struct FrustumTest: TestSuite::Tester { struct FrustumTest: Corrade::TestSuite::Tester {
explicit FrustumTest(); explicit FrustumTest();
void construct(); void construct();
void constructFromMatrix(); void constructFromMatrix();
}; };
typedef Vector4<Float> Vector4; typedef Math::Vector4<Float> Vector4;
typedef Matrix4<Float> Matrix4; typedef Math::Matrix4<Float> Matrix4;
typedef Frustum<Float> Frustum; typedef Math::Frustum<Float> Frustum;
typedef Deg<Float> Degf;
FrustumTest::FrustumTest() { FrustumTest::FrustumTest() {
addTests({&FrustumTest::construct, addTests({&FrustumTest::construct,
@ -53,35 +49,38 @@ FrustumTest::FrustumTest() {
void FrustumTest::construct() { void FrustumTest::construct() {
Vector4 planes[6]{ Vector4 planes[6]{
{-1.0f, 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f, 1.0f},
{ 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, 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, 1.0f, 1.0f}}; { 0.0f, 0.0f, 1.0f, 1.0f}};
Frustum frustum{ Frustum frustum{
planes[0], planes[1], planes[0], planes[1],
planes[2], planes[3], planes[2], planes[3],
planes[4], planes[5], planes[4], planes[5]};
};
CORRADE_COMPARE_AS(frustum.planes(), Containers::ArrayView<const Vector4>(planes), TestSuite::Compare::Container); CORRADE_COMPARE_AS(frustum.planes(), Corrade::Containers::ArrayView<const Vector4>(planes),
Corrade::TestSuite::Compare::Container);
} }
void FrustumTest::constructFromMatrix() { void FrustumTest::constructFromMatrix() {
Vector4 planes[6]{ using namespace Magnum::Math::Literals;
{ 1.0f, 0.0f,-1.0f, 0.0f},
{-1.0f, 0.0f,-1.0f, 0.0f}, Frustum expected{
{ 0.0f, 1.0f,-1.0f, 0.0f}, { 1.0f, 0.0f, -1.0f, 0.0f},
{ 0.0f,-1.0f,-1.0f, 0.0f}, {-1.0f, 0.0f, -1.0f, 0.0f},
{ 0.0f, 0.0f,-2.22222f,-2.22222f}, { 0.0f, 1.0f, -1.0f, 0.0f},
{ 0.0f, 0.0f, 0.22222f, 2.22222f}}; { 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( const Frustum frustum = Frustum::fromMatrix(
Matrix4::perspectiveProjection(Degf(90.0f), 1.0f, 1.0f, 10.0f)); Matrix4::perspectiveProjection(90.0_degf, 1.0f, 1.0f, 10.0f));
CORRADE_COMPARE_AS(frustum.planes(), Containers::ArrayView<const Vector4>(planes), TestSuite::Compare::Container); CORRADE_COMPARE_AS(frustum.planes(), expected.planes(),
TestSuite::Compare::Container);
} }
}}} }}}

Loading…
Cancel
Save