From 04d58b3dbd08fab221cec0ebe301ea27360818a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 26 Sep 2012 01:53:33 +0200 Subject: [PATCH] Fixed collision detection boolean/logical operations confusion. Current implementation provided logical operations as if it were boolean operations, which is wrong. Booleans might (or might not) be implemented in the future, but currently the logical are enough. --- doc/collision-detection.dox | 60 +++++++------- src/Physics/ShapeGroup.cpp | 8 +- src/Physics/ShapeGroup.h | 119 ++++++++++++++++++---------- src/Physics/Test/ShapeGroupTest.cpp | 12 +-- 4 files changed, 120 insertions(+), 79 deletions(-) diff --git a/doc/collision-detection.dox b/doc/collision-detection.dox index bde0bd606..1544e9157 100644 --- a/doc/collision-detection.dox +++ b/doc/collision-detection.dox @@ -5,13 +5,13 @@ namespace Magnum { namespace Physics { The essential thing in collision detection is to define a complex object with collection of simple shapes, for which it is easy to detect collisions. These shapes can be either one-, two- or three-dimensional and they can be grouped -together using five different set operations. +together using various operations. @tableofcontents -@section CollisionDetectionShapeCollection Available shapes +@section collision-detection-shape-collection Available shapes -@subsection CollisionDetectionShapes1D One-dimensional shapes +@subsection collision-detection-shapes1D One-dimensional shapes - Physics::Point - @copybrief Physics::Point - Physics::Line - @copybrief Physics::Line @@ -20,11 +20,11 @@ together using five different set operations. One-dimensional shapes don't provide collision detection with each other because of numerical instability. -@subsection CollisionDetectionShapes2D Two-dimensional shapes +@subsection collision-detection-shapes2D Two-dimensional shapes - Physics::Plane - @copybrief Physics::Plane -@subsection CollisionDetectionShapes3D Three-dimensional shapes +@subsection collision-detection-shapes3D Three-dimensional shapes - Physics::Sphere - @copybrief Physics::Sphere - Physics::Capsule - @copybrief Physics::Capsule @@ -35,26 +35,30 @@ The easiest (and most efficient) shape combination for detecting collisions is point and sphere, followed by two spheres. Computing collision of two boxes is least efficient. -@section CollisionDetectionShapeGroups Creating hierarchic groups of shapes +@section collision-detection-shape-groups Creating hierarchic groups of shapes -Shapes can be grouped together using one of five set operations: complement, -union, intersection, difference and XOR. These operations are mapped to -operator~(), operator|(), operator&(), operator-() and operator^(), so for -example creating complement of union of sphere and box is simple as this: +Shapes can be grouped together using one of three available logical +operations: AND, OR and NOT. These operations are mapped to operator&&(), +operator||() and operator!(), so for example creating negation of logical OR +of line segment and point is simple as this: @code -Physics::Sphere sphere; -Physics::Box box; +Physics::LineSegment segment; +Physics::Point point; -Physics::ShapeGroup group = ~(sphere|box); +Physics::ShapeGroup group = !(segment || point); @endcode +@note Logical operations are not the same as set operations -- intersection of + two spheres will not generate any collision if they are disjoint, but + logical AND will if the object collides with both of them. + The resulting object internally stores copies of both shapes, so the original instances can be destroyed. For simple combinations appropriate resulting -shape is generated (e.g. intersection of line and three-dimensional object -can be a line segment) and stored inside ShapeGroup instead of two original +shape is generated (e.g. logical OR of point and sphere can be only the sphere, +if the point lies inside) and stored inside ShapeGroup instead of two original objects. -@subsection CollisionDetectionShapeReference Referencing the shapes for later changes +@subsection collision-detection-shape-reference Referencing the shapes for later changes Sometimes you may want to modify the shape based on changes of the object itself. In previous example all the shapes were copied into ShapeGroup, so it @@ -62,34 +66,36 @@ was not possible to change their properties such as sphere radius without recreating the group again. You can, however, explicitly pass a reference to original object, so you can change it later: @code -Physics::Sphere sphere; -Physics::Box box; +Physics::LineSegment segment; +Physics::Point point; -Physics::ShapeGroup group = ~(std::ref(sphere)|box); +Physics::ShapeGroup group = !(segment || std::ref(point)); -sphere.setRadius(2.0f); +point.setPosition({1.0f, -6.0f, 0.5f}); @endcode Note that passing a reference implies that you must not destroy the original -instance (in this case the sphere). Also because the referenced instance could +instance (in this case the point). Also because the referenced instance could change, there are no shape optimizations done, unlike above. -@subsection CollisionDetectionShapeSimplification Providing simplified version of shape for better performance +@subsection collision-detection-shape-simplification Providing simplified version of shape for better performance If there are many shapes grouped together, it might hurt performance of collision detection, because it might be testing collision with more shapes than necessary. It's then good to specify simplified version of such shape, so the collision detection is done on the original if and only if collision -was detected with the simplified shape. It is in fact intersection group - -the collision is initially detected on first (simplified) shape and then on -the other: +was detected with the simplified shape. It is in fact logical AND using +operator&&() - the collision is initially detected on first (simplified) shape +and then on the other: @code +Physics::Sphere sphere; +Physics::Box box; Physics::AxisAlignedBox simplified; -Physics::ShapeGroup object = simplified & (sphere|box); +Physics::ShapeGroup object = simplified && (sphere || box); @endcode -@section CollisionDetectionShapeCollisions Detecting shape collisions +@section collision-detection-shape-collisions Detecting shape collisions Shape pairs which have collision detection implemented can be tested for collision using operator%(), for example: diff --git a/src/Physics/ShapeGroup.cpp b/src/Physics/ShapeGroup.cpp index 32aaeaeb5..4f70f0b9e 100644 --- a/src/Physics/ShapeGroup.cpp +++ b/src/Physics/ShapeGroup.cpp @@ -50,11 +50,9 @@ void ShapeGroup::applyTransformation(const Matrix4& transformation) { bool ShapeGroup::collides(const AbstractShape* other) const { switch(operation & ~RefAB) { - case Complement: return !a->collides(other); - case Union: return a->collides(other) || b->collides(other); - case Intersection: return a->collides(other) && b->collides(other); - case Difference: return a->collides(other) && !b->collides(other); - case Xor: return a->collides(other) != b->collides(other); + case And: return a->collides(other) && b->collides(other); + case Or: return a->collides(other) || b->collides(other); + case Not: return !a->collides(other); case FirstObjectOnly: return a->collides(other); default: diff --git a/src/Physics/ShapeGroup.h b/src/Physics/ShapeGroup.h index 35be57000..4b56a8cb8 100644 --- a/src/Physics/ShapeGroup.h +++ b/src/Physics/ShapeGroup.h @@ -32,16 +32,19 @@ namespace Magnum { namespace Physics { #endif /** -@brief Collider group with defined set operation +@brief Shape group -Result of union, intersection, subtraction or XOR of two collider objects. +Result of logical operations on shapes. See @ref collision-detection for brief introduction. */ class PHYSICS_EXPORT ShapeGroup: public AbstractShape { #ifndef DOXYGEN_GENERATING_OUTPUT - template friend constexpr enableIfIsBaseType operator~(const T& a); - template friend constexpr enableIfIsBaseType operator~(T&& a); - template friend constexpr enableIfIsBaseType operator~(T& a); +// template friend constexpr enableIfIsBaseType operator~(const T& a); +// template friend constexpr enableIfIsBaseType operator~(T&& a); +// template friend constexpr enableIfIsBaseType operator~(T& a); + template friend constexpr enableIfIsBaseType operator!(const T& a); + template friend constexpr enableIfIsBaseType operator!(T&& a); + template friend constexpr enableIfIsBaseType operator!(T& a); #define friendOp(char) \ template friend constexpr enableIfAreBaseType operator char(const T& a, const U& b); \ @@ -53,10 +56,12 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape { template friend constexpr enableIfAreBaseType operator char(std::reference_wrapper a, const U& b); \ template friend constexpr enableIfAreBaseType operator char(std::reference_wrapper a, U&& b); \ template friend constexpr enableIfAreBaseType operator char(std::reference_wrapper a, std::reference_wrapper b); - friendOp(|) - friendOp(&) - friendOp(-) - friendOp(^) +// friendOp(|) +// friendOp(&) +// friendOp(-) +// friendOp(^) + friendOp(&&) + friendOp(||) #undef friendOp #endif @@ -68,13 +73,16 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape { RefA = 0x01, RefB = 0x02, RefAB = 0x03, - Complement = 1 << 2, - Union = 2 << 2, - Intersection = 3 << 2, - Difference = 4 << 2, - Xor = 5 << 2, - FirstObjectOnly = 6 << 2, - AlwaysFalse = 7 << 2 +// Complement = 1 << 2, +// Union = 2 << 2, +// Intersection = 3 << 2, +// Difference = 4 << 2, +// Xor = 5 << 2, + And = 6 << 2, + Or = 7 << 2, + Not = 8 << 2, + FirstObjectOnly = 9 << 2, + AlwaysFalse = 10 << 2 }; public: @@ -105,37 +113,64 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape { AbstractShape* b; }; -/** @brief Complement of shape */ -template inline constexpr enableIfIsBaseType operator~(const T& a) { - return ShapeGroup(ShapeGroup::Complement, new T(a), nullptr); +// /* @brief Complement of shape */ +// template inline constexpr enableIfIsBaseType operator~(const T& a) { +// return ShapeGroup(ShapeGroup::Complement, new T(a), nullptr); +// } +// #ifndef DOXYGEN_GENERATING_OUTPUT +// template inline constexpr enableIfIsBaseType operator~(T&& a) { +// return ShapeGroup(ShapeGroup::Complement, new T(std::forward(a)), nullptr); +// } +// template inline constexpr enableIfIsBaseType operator~(T& a) { +// return ShapeGroup(ShapeGroup::Complement|ShapeGroup::RefA, &a.get(), nullptr); +// } +// #endif + +/** @relates ShapeGroup +@brief Logical NOT of shape +*/ +template inline constexpr enableIfIsBaseType operator!(const T& a) { + return ShapeGroup(ShapeGroup::Not, new T(a), nullptr); } #ifndef DOXYGEN_GENERATING_OUTPUT -template inline constexpr enableIfIsBaseType operator~(T&& a) { - return ShapeGroup(ShapeGroup::Complement, new T(std::forward(a)), nullptr); +template inline constexpr enableIfIsBaseType operator!(T&& a) { + return ShapeGroup(ShapeGroup::Not, new T(std::forward(a)), nullptr); } -template inline constexpr enableIfIsBaseType operator~(T& a) { - return ShapeGroup(ShapeGroup::Complement|ShapeGroup::RefA, &a.get(), nullptr); +template inline constexpr enableIfIsBaseType operator!(T& a) { + return ShapeGroup(ShapeGroup::Not|ShapeGroup::RefA, &a.get(), nullptr); } #endif #ifdef DOXYGEN_GENERATING_OUTPUT -/** @brief Union of two shapes */ -template inline constexpr ShapeGroup operator&(T a, U b); - -/** -@brief Intersection of two shapes - -Collision with @p a is computed first, so this operation can be also used for -providing simplified version for shape @p b. See @ref CollisionDetectionShapeGroups -for an example. +// /* @brief Union of two shapes */ +// template inline constexpr ShapeGroup operator&(T a, U b); +// +// /* @brief Intersection of two shapes */ +// template inline constexpr ShapeGroup operator&(T a, U b); +// +// /* @brief Difference of two shapes */ +// template inline constexpr ShapeGroup operator-(T a, U b); +// +// /* @brief XOR of two shapes */ +// template inline constexpr ShapeGroup operator^(T a, U b); +/** @relates ShapeGroup +@brief Logical AND of two shapes + +[Short-circuit evaluation](http://en.wikipedia.org/wiki/Short-circuit_evaluation) +is used here, so this operation can be used for providing simplified shape +version, because collision with @p b is computed only if @p a collides. +See @ref collision-detection-shape-simplification for an example. */ -template inline constexpr ShapeGroup operator&(T a, U b); +template inline constexpr ShapeGroup operator&&(T a, U b); -/** @brief Difference of two shapes */ -template inline constexpr ShapeGroup operator-(T a, U b); +/** @relates ShapeGroup +@brief Logical OR of two shapes -/** @brief XOR of two shapes */ -template inline constexpr ShapeGroup operator^(T a, U b); +[Short-circuit evaluation](http://en.wikipedia.org/wiki/Short-circuit_evaluation) +is used, so if collision with @p a is detected, collision with @p b is not +computed. +*/ +template inline constexpr ShapeGroup operator||(T a, U b); #else #define op(type, char) \ template inline constexpr enableIfAreBaseType operator char(const T& a, const U& b) { \ @@ -165,10 +200,12 @@ template inline constexpr enableIfAreBaseType operator char(st template inline constexpr enableIfAreBaseType operator char(std::reference_wrapper a, std::reference_wrapper b) { \ return ShapeGroup(ShapeGroup::type|ShapeGroup::RefAB, &a.get(), &b.get()); \ } -op(Union, |) -op(Intersection, &) -op(Difference, -) -op(Xor, ^) +// op(Union, |) +// op(Intersection, &) +// op(Difference, -) +// op(Xor, ^) +op(And, &&) +op(Or, ||) #undef op #endif diff --git a/src/Physics/Test/ShapeGroupTest.cpp b/src/Physics/Test/ShapeGroupTest.cpp index a78ddc9d6..69eaffa13 100644 --- a/src/Physics/Test/ShapeGroupTest.cpp +++ b/src/Physics/Test/ShapeGroupTest.cpp @@ -17,7 +17,7 @@ #include "Math/Matrix4.h" #include "Physics/Point.h" -#include "Physics/Sphere.h" +#include "Physics/LineSegment.h" #include "Physics/ShapeGroup.h" using namespace std; @@ -35,9 +35,9 @@ void ShapeGroupTest::copy() { ShapeGroup group; { Physics::Point point({1.0f, 2.0f, 3.0f}); - Physics::Sphere sphere({2.0f, 1.0f, 30.0f}, 1.0f); + Physics::LineSegment segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); - group = ~(point|sphere); + group = !(point || segment); } /* Just to test that it doesn't crash */ @@ -48,14 +48,14 @@ void ShapeGroupTest::copy() { void ShapeGroupTest::reference() { Physics::Point point({1.0f, 2.0f, 3.0f}); - Physics::Sphere sphere({2.0f, 1.0f, 30.0f}, 1.0f); + Physics::LineSegment segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); - ShapeGroup group = ~(ref(point)|ref(sphere)); + ShapeGroup group = !(ref(point) || ref(segment)); group.applyTransformation(Matrix4::translation(Vector3(1.0f))); CORRADE_VERIFY((point.transformedPosition() == Vector3(2.0f, 3.0f, 4.0f))); - CORRADE_VERIFY((sphere.transformedPosition() == Vector3(3.0f, 2.0f, 31.0f))); + CORRADE_VERIFY((segment.transformedA() == Vector3(3.0f, 2.0f, 31.0f))); } }}}