diff --git a/src/Magnum/Shapes/AbstractShape.cpp b/src/Magnum/Shapes/AbstractShape.cpp index 913ed01e9..8bb10797a 100644 --- a/src/Magnum/Shapes/AbstractShape.cpp +++ b/src/Magnum/Shapes/AbstractShape.cpp @@ -27,6 +27,7 @@ #include +#include "Magnum/Shapes/Collision.h" #include "Magnum/Shapes/ShapeGroup.h" #include "Magnum/Shapes/Implementation/CollisionDispatch.h" @@ -52,6 +53,10 @@ template bool AbstractShape::collides(const return Implementation::collides(abstractTransformedShape(), other.abstractTransformedShape()); } +template Collision AbstractShape::collision(const AbstractShape& other) const { + return Implementation::collision(abstractTransformedShape(), other.abstractTransformedShape()); +} + template void AbstractShape::markDirty() { if(group()) group()->setDirty(); } diff --git a/src/Magnum/Shapes/AbstractShape.h b/src/Magnum/Shapes/AbstractShape.h index 2273944de..fdcb3a75c 100644 --- a/src/Magnum/Shapes/AbstractShape.h +++ b/src/Magnum/Shapes/AbstractShape.h @@ -102,6 +102,13 @@ template class MAGNUM_SHAPES_EXPORT AbstractShape: publi */ bool collides(const AbstractShape& other) const; + /** + * @brief Collision with other shape + * + * Default implementation returns empty collision. + */ + Collision collision(const AbstractShape& other) const; + protected: /** Marks also the group as dirty */ void markDirty() override; diff --git a/src/Magnum/Shapes/Implementation/CollisionDispatch.cpp b/src/Magnum/Shapes/Implementation/CollisionDispatch.cpp index 621396a1b..11a76adcd 100644 --- a/src/Magnum/Shapes/Implementation/CollisionDispatch.cpp +++ b/src/Magnum/Shapes/Implementation/CollisionDispatch.cpp @@ -65,6 +65,21 @@ template<> bool collides(const AbstractShape<2>& a, const AbstractShape<2>& b) { return false; } +template<> Collision<2> collision(const AbstractShape<2>& a, const AbstractShape<2>& b) { + if(a.type() < b.type()) return collision(b, a); + + switch(UnsignedInt(a.type())*UnsignedInt(b.type())) { + #define _c(aType, aClass, bType, bClass) \ + case UnsignedInt(ShapeDimensionTraits<2>::Type::aType)*UnsignedInt(ShapeDimensionTraits<2>::Type::bType): \ + return static_cast&>(a).shape / static_cast&>(b).shape; + _c(Sphere, Sphere2D, Point, Point2D) + _c(Sphere, Sphere2D, Sphere, Sphere2D) + #undef _c + } + + return {}; +} + template<> bool collides(const AbstractShape<3>& a, const AbstractShape<3>& b) { if(a.type() < b.type()) return collides(b, a); @@ -96,4 +111,19 @@ template<> bool collides(const AbstractShape<3>& a, const AbstractShape<3>& b) { return false; } +template<> Collision<3> collision(const AbstractShape<3>& a, const AbstractShape<3>& b) { + if(a.type() < b.type()) return collision(b, a); + + switch(UnsignedInt(a.type())*UnsignedInt(b.type())) { + #define _c(aType, aClass, bType, bClass) \ + case UnsignedInt(ShapeDimensionTraits<3>::Type::aType)*UnsignedInt(ShapeDimensionTraits<3>::Type::bType): \ + return static_cast&>(a).shape / static_cast&>(b).shape; + _c(Sphere, Sphere3D, Point, Point3D) + _c(Sphere, Sphere3D, Sphere, Sphere3D) + #undef _c + } + + return {}; +} + }}} diff --git a/src/Magnum/Shapes/Implementation/CollisionDispatch.h b/src/Magnum/Shapes/Implementation/CollisionDispatch.h index cd1dd564b..84d79aa2f 100644 --- a/src/Magnum/Shapes/Implementation/CollisionDispatch.h +++ b/src/Magnum/Shapes/Implementation/CollisionDispatch.h @@ -26,6 +26,7 @@ */ #include "Magnum/Types.h" +#include "Magnum/Shapes/Shapes.h" namespace Magnum { namespace Shapes { namespace Implementation { @@ -40,8 +41,11 @@ multiply the two numbers together and switch() on the result. Because of multiplying two prime numbers, there is no ambiguity (the result is unique for each combination). */ + template bool collides(const AbstractShape& a, const AbstractShape& b); +template Collision collision(const AbstractShape& a, const AbstractShape& b); + }}} #endif diff --git a/src/Magnum/Shapes/Test/ShapeTest.cpp b/src/Magnum/Shapes/Test/ShapeTest.cpp index 9f88909c4..e9a74d915 100644 --- a/src/Magnum/Shapes/Test/ShapeTest.cpp +++ b/src/Magnum/Shapes/Test/ShapeTest.cpp @@ -42,6 +42,7 @@ class ShapeTest: public TestSuite::Tester { void clean(); void collides(); + void collision(); void firstCollision(); void shapeGroup(); }; @@ -54,6 +55,7 @@ typedef SceneGraph::Object Object3D; ShapeTest::ShapeTest() { addTests({&ShapeTest::clean, &ShapeTest::collides, + &ShapeTest::collision, &ShapeTest::firstCollision, &ShapeTest::shapeGroup}); } @@ -137,6 +139,49 @@ void ShapeTest::collides() { } } +void ShapeTest::collision() { + Scene3D scene; + ShapeGroup3D shapes; + Object3D a(&scene); + Shape aShape(a, {{1.0f, -2.0f, 3.0f}, 1.5f}, &shapes); + + { + /* Collision with point inside the sphere */ + Shape aShape2(a, {{1.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + const Collision3D collision = aShape.collision(aShape2); + CORRADE_VERIFY(collision); + CORRADE_COMPARE(collision.position(), Vector3(1.0f, -2.0f, 3.0f)); + } { + /* No collision with point inside the sphere, but not in the same group */ + ShapeGroup3D shapes2; + Shape aShape3(a, {{1.0f, -2.0f, 3.0f}}, &shapes2); + shapes2.setClean(); + CORRADE_VERIFY(!aShape.collision(aShape3)); + } { + CORRADE_EXPECT_FAIL("Should cross-scene collision work or not?"); + /* No collision with point inside the sphere, but not in the same scene */ + Scene3D scene2; + Object3D c(&scene2); + Shape cShape(c, {{1.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + CORRADE_VERIFY(!aShape.collision(cShape)); + } { + /* No collision with point outside of the sphere */ + Object3D b(&scene); + Shape bShape(b, {{3.0f, -2.0f, 3.0f}}, &shapes); + shapes.setClean(); + CORRADE_VERIFY(!aShape.collision(bShape)); + + /* Move point inside the sphere -- collision */ + b.translate(Vector3::xAxis(-1.0f)); + shapes.setClean(); + const Collision3D collision = aShape.collision(bShape); + CORRADE_VERIFY(collision); + CORRADE_COMPARE(collision.position(), Vector3(2.0f, -2.0f, 3.0f)); + } +} + void ShapeTest::firstCollision() { Scene3D scene; ShapeGroup3D shapes;