diff --git a/doc/collision-detection.dox b/doc/collision-detection.dox index 1544e9157..033f7607d 100644 --- a/doc/collision-detection.dox +++ b/doc/collision-detection.dox @@ -13,12 +13,12 @@ together using various operations. @subsection collision-detection-shapes1D One-dimensional shapes -- Physics::Point - @copybrief Physics::Point -- Physics::Line - @copybrief Physics::Line -- Physics::LineSegment - @copybrief Physics::LineSegment +- @ref Physics::Point "Physics::Point*D" - @copybrief Physics::Point +- @ref Physics::Line "Physics::Line*D" - @copybrief Physics::Line +- @ref Physics::LineSegment "Physics::LineSegment*D" - @copybrief Physics::LineSegment -One-dimensional shapes don't provide collision detection with each other -because of numerical instability. +Because of numerical instability it's not possible to detect collisions of +line and point. Collision of two lines can be detected only in 2D. @subsection collision-detection-shapes2D Two-dimensional shapes @@ -26,10 +26,10 @@ because of numerical instability. @subsection collision-detection-shapes3D Three-dimensional shapes -- Physics::Sphere - @copybrief Physics::Sphere -- Physics::Capsule - @copybrief Physics::Capsule -- Physics::AxisAlignedBox - @copybrief Physics::AxisAlignedBox -- Physics::Box - @copybrief Physics::Box +- @ref Physics::Sphere "Physics::Sphere*D" - @copybrief Physics::Sphere +- @ref Physics::Capsule "Physics::Capsule*D" - @copybrief Physics::Capsule +- @ref Physics::AxisAlignedBox "Physics::AxisAlignedBox*D" - @copybrief Physics::AxisAlignedBox +- @ref Physics::Box "Physics::Box*D" - @copybrief Physics::Box The easiest (and most efficient) shape combination for detecting collisions is point and sphere, followed by two spheres. Computing collision of two boxes @@ -42,10 +42,10 @@ 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::LineSegment segment; -Physics::Point point; +Physics::LineSegment3D segment; +Physics::Point3D point; -Physics::ShapeGroup group = !(segment || point); +Physics::ShapeGroup3D group = !(segment || point); @endcode @note Logical operations are not the same as set operations -- intersection of @@ -66,10 +66,10 @@ 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::LineSegment segment; -Physics::Point point; +Physics::LineSegment3D segment; +Physics::Point3D point; -Physics::ShapeGroup group = !(segment || std::ref(point)); +Physics::ShapeGroup3D group = !(segment || std::ref(point)); point.setPosition({1.0f, -6.0f, 0.5f}); @endcode @@ -88,11 +88,11 @@ 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::Sphere3D sphere; +Physics::Box3D box; +Physics::AxisAlignedBox3D simplified; -Physics::ShapeGroup object = simplified && (sphere || box); +Physics::ShapeGroup3D object = simplified && (sphere || box); @endcode @section collision-detection-shape-collisions Detecting shape collisions @@ -100,8 +100,8 @@ Physics::ShapeGroup object = simplified && (sphere || box); Shape pairs which have collision detection implemented can be tested for collision using operator%(), for example: @code -Physics::Point point; -Physics::Sphere sphere; +Physics::Point3D point; +Physics::Sphere3D sphere; bool collide = point % sphere; @endcode diff --git a/src/Physics/AbstractShape.cpp b/src/Physics/AbstractShape.cpp index da42b21b7..cb920047e 100644 --- a/src/Physics/AbstractShape.cpp +++ b/src/Physics/AbstractShape.cpp @@ -17,7 +17,7 @@ namespace Magnum { namespace Physics { -bool AbstractShape::collides(const AbstractShape* other) const { +template bool AbstractShape::collides(const AbstractShape* other) const { /* Operate only with simpler types than this */ if(static_cast(other->type()) > static_cast(type())) return other->collides(this); @@ -25,4 +25,7 @@ bool AbstractShape::collides(const AbstractShape* other) const { return false; } +template class AbstractShape<2>; +template class AbstractShape<3>; + }} diff --git a/src/Physics/AbstractShape.h b/src/Physics/AbstractShape.h index 363a685be..f4b8ea370 100644 --- a/src/Physics/AbstractShape.h +++ b/src/Physics/AbstractShape.h @@ -25,13 +25,67 @@ namespace Magnum { namespace Physics { +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + template struct ShapeDimensionTraits {}; + + template<> struct ShapeDimensionTraits<2> { + typedef Vector2 VectorType; + typedef Point2D PointType; + typedef Matrix3 MatrixType; + + enum class Type { + Point, + Line, + LineSegment, + Sphere, + Capsule, + AxisAlignedBox, + Box, + ShapeGroup + }; + }; + + template<> struct ShapeDimensionTraits<3> { + typedef Vector3 VectorType; + typedef Point3D PointType; + typedef Matrix4 MatrixType; + + enum class Type { + Point, + Line, + LineSegment, + Sphere, + Capsule, + AxisAlignedBox, + Box, + ShapeGroup, + Plane + }; + }; +} +#endif + /** @brief Base class for shapes See @ref collision-detection for brief introduction. +@see AbstractShape2D, AbstractShape3D */ -class PHYSICS_EXPORT AbstractShape { +template class PHYSICS_EXPORT AbstractShape { public: + /** @brief %Vector type for given dimension count */ + typedef typename Implementation::ShapeDimensionTraits::VectorType VectorType; + + /** @brief %Point type for given dimension count */ + typedef typename Implementation::ShapeDimensionTraits::PointType PointType; + + /** @brief %Matrix type for given dimension count */ + typedef typename Implementation::ShapeDimensionTraits::MatrixType MatrixType; + + /** @brief Dimension count */ + static const size_t Dimensions = dimensions; + /** * @brief Shape type * @@ -39,17 +93,21 @@ class PHYSICS_EXPORT AbstractShape { * the list provides collision detection for previous shapes, not * the other way around. */ + #ifdef DOXYGEN_GENERATING_OUTPUT enum class Type { - Point, - Line, - LineSegment, - Plane, - Sphere, - Capsule, - AxisAlignedBox, - Box, - ShapeGroup + Point, /**< Point */ + Line, /**< Line */ + LineSegment, /**< @ref LineSegment "Line segment" */ + Sphere, /**< Sphere */ + Capsule, /**< Capsule */ + AxisAlignedBox, /**< @ref AxisAlignedBox "Axis aligned box" */ + Box, /**< Box */ + ShapeGroup, /**< @ref ShapeGroup "Shape group" */ + Plane /**< Plane (3D only) */ }; + #else + typedef typename Implementation::ShapeDimensionTraits::Type Type; + #endif /** @brief Destructor */ virtual inline ~AbstractShape() {} @@ -63,7 +121,7 @@ class PHYSICS_EXPORT AbstractShape { * Applies transformation to user-defined shape properties and caches * them for later usage in collision detection. */ - virtual void applyTransformation(const Matrix4& transformation) = 0; + virtual void applyTransformation(const MatrixType& transformation) = 0; /** * @brief Detect collision with other shape @@ -73,9 +131,20 @@ class PHYSICS_EXPORT AbstractShape { * @internal If other shape is more complex than this, returns * `other->collides(this)`. */ - virtual bool collides(const AbstractShape* other) const; + virtual bool collides(const AbstractShape* other) const; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template class PHYSICS_EXPORT AbstractShape<2>; +extern template class PHYSICS_EXPORT AbstractShape<3>; +#endif + +/** @brief Abstract two-dimensional shape */ +typedef AbstractShape<2> AbstractShape2D; + +/** @brief Abstract three-dimensional shape */ +typedef AbstractShape<3> AbstractShape3D; + }} #endif diff --git a/src/Physics/AxisAlignedBox.cpp b/src/Physics/AxisAlignedBox.cpp index 30ad3551b..e9e9c254e 100644 --- a/src/Physics/AxisAlignedBox.cpp +++ b/src/Physics/AxisAlignedBox.cpp @@ -20,9 +20,12 @@ namespace Magnum { namespace Physics { -void AxisAlignedBox::applyTransformation(const Matrix4& transformation) { - _transformedPosition = (transformation*Point3D(_position)).xyz(); +template void AxisAlignedBox::applyTransformation(const typename AbstractShape::MatrixType& transformation) { + _transformedPosition = (transformation*typename AxisAlignedBox::PointType(_position)).vector(); _transformedSize = transformation.rotationScaling()*_size; } +template class AxisAlignedBox<2>; +template class AxisAlignedBox<3>; + }} diff --git a/src/Physics/AxisAlignedBox.h b/src/Physics/AxisAlignedBox.h index ce3505582..45dab8f70 100644 --- a/src/Physics/AxisAlignedBox.h +++ b/src/Physics/AxisAlignedBox.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::Physics::AxisAlignedBox + * @brief Class Magnum::Physics::AxisAlignedBox, typedef Magnum::Physics::AxisAlignedBox2D, Magnum::Physics.:AxisAlignedBox3D */ #include "Math/Vector3.h" @@ -24,48 +24,71 @@ namespace Magnum { namespace Physics { -/** @brief Axis aligned box */ -class PHYSICS_EXPORT AxisAlignedBox: public AbstractShape { +/** +@brief Axis-aligned box + +@see AxisAlignedBox2D, AxisAlignedBox3D +*/ +template class PHYSICS_EXPORT AxisAlignedBox: public AbstractShape { public: /** @brief Constructor */ - inline AxisAlignedBox(const Vector3& position, const Vector3& size): _position(position), _transformedPosition(position), _size(size), _transformedSize(size) {} + inline AxisAlignedBox(const typename AbstractShape::VectorType& position, const typename AbstractShape::VectorType& size): _position(position), _transformedPosition(position), _size(size), _transformedSize(size) {} - void applyTransformation(const Matrix4& transformation); + #ifndef DOXYGEN_GENERATING_OUTPUT + void applyTransformation(const typename AbstractShape::MatrixType& transformation); + #else + void applyTransformation(const MatrixType& transformation); + #endif /** @brief Position */ - inline Vector3 position() const { return _position; } + inline typename AbstractShape::VectorType position() const { + return _position; + } /** @brief Set position */ - inline void setPosition(const Vector3& position) { + inline void setPosition(const typename AbstractShape::VectorType& position) { _position = position; } /** @brief Size */ - inline Vector3 size() const { return _size; } + inline typename AbstractShape::VectorType size() const { return _size; } /** @brief Set size */ - inline void setSize(const Vector3& size) { + inline void setSize(const typename AbstractShape::VectorType& size) { _size = size; } /** @brief Transformed position */ - inline Vector3 transformedPosition() const { + inline typename AbstractShape::VectorType transformedPosition() const { return _transformedPosition; } /** @brief Transformed size */ - inline Vector3 transformedSize() const { + inline typename AbstractShape::VectorType transformedSize() const { return _transformedSize; } protected: - inline Type type() const { return Type::AxisAlignedBox; } + inline typename AbstractShape::Type type() const { + return AbstractShape::Type::AxisAlignedBox; + } private: - Vector3 _position, _transformedPosition, + typename AbstractShape::VectorType _position, _transformedPosition, _size, _transformedSize; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template class PHYSICS_EXPORT AxisAlignedBox<2>; +extern template class PHYSICS_EXPORT AxisAlignedBox<3>; +#endif + +/** @brief Two-dimensional axis-aligned box */ +typedef AxisAlignedBox<2> AxisAlignedBox2D; + +/** @brief Three-dimensional axis-aligned box */ +typedef AxisAlignedBox<3> AxisAlignedBox3D; + }} #endif diff --git a/src/Physics/Box.cpp b/src/Physics/Box.cpp index 36c230870..cc5d292bd 100644 --- a/src/Physics/Box.cpp +++ b/src/Physics/Box.cpp @@ -20,8 +20,11 @@ namespace Magnum { namespace Physics { -void Box::applyTransformation(const Matrix4& transformation) { +template void Box::applyTransformation(const typename AbstractShape::MatrixType& transformation) { _transformedTransformation = (transformation*_transformation); } +template class Box<2>; +template class Box<3>; + }} diff --git a/src/Physics/Box.h b/src/Physics/Box.h index f1c3286ff..9c5aa6e5d 100644 --- a/src/Physics/Box.h +++ b/src/Physics/Box.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::Physics::Box + * @brief Class Magnum::Physics::Box, typedef Magnum::Physics::Box2D, Magnum::Physics::Box3D */ #include "Math/Matrix4.h" @@ -24,34 +24,58 @@ namespace Magnum { namespace Physics { -/** @brief Unit size box with assigned transformation matrix */ -class PHYSICS_EXPORT Box: public AbstractShape { +/** +@brief Unit-size box with assigned transformation matrix + +@see Box2D, Box3D +*/ +template class PHYSICS_EXPORT Box: public AbstractShape { public: /** @brief Constructor */ - inline Box(const Matrix4& transformation): _transformation(transformation), _transformedTransformation(transformation) {} + inline Box(const typename AbstractShape::MatrixType& transformation): _transformation(transformation), _transformedTransformation(transformation) {} - void applyTransformation(const Matrix4& transformation); + #ifndef DOXYGEN_GENERATING_OUTPUT + void applyTransformation(const typename AbstractShape::MatrixType& transformation); + #else + void applyTransformation(const MatrixType& transformation); + #endif /** @brief Transformation */ - inline Matrix4 transformation() const { return _transformation; } + inline typename AbstractShape::MatrixType transformation() const { + return _transformation; + } /** @brief Set transformation */ - inline void setTransformation(const Matrix4& transformation) { + inline void setTransformation(const typename AbstractShape::MatrixType& transformation) { _transformation = transformation; } /** @brief Transformed transformation */ - inline Matrix4 transformedTransformation() const { + inline typename AbstractShape::MatrixType transformedTransformation() const { return _transformedTransformation; } protected: - inline Type type() const { return Type::Box; } + inline typename AbstractShape::Type type() const { + return AbstractShape::Type::Box; + } private: - Matrix4 _transformation, _transformedTransformation; + typename AbstractShape::MatrixType _transformation, + _transformedTransformation; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template class PHYSICS_EXPORT Box<2>; +extern template class PHYSICS_EXPORT Box<3>; +#endif + +/** @brief Two-dimensional box */ +typedef Box<2> Box2D; + +/** @brief Three-dimensional box */ +typedef Box<3> Box3D; + }} #endif diff --git a/src/Physics/Capsule.cpp b/src/Physics/Capsule.cpp index b6d175f7d..a8109e1a8 100644 --- a/src/Physics/Capsule.cpp +++ b/src/Physics/Capsule.cpp @@ -27,30 +27,33 @@ using namespace Magnum::Math::Geometry; namespace Magnum { namespace Physics { -void Capsule::applyTransformation(const Matrix4& transformation) { - _transformedA = (transformation*Point3D(_a)).xyz(); - _transformedB = (transformation*Point3D(_b)).xyz(); - float scaling = (transformation.rotationScaling()*Vector3(1/Math::Constants::sqrt3())).length(); +template void Capsule::applyTransformation(const typename AbstractShape::MatrixType& transformation) { + _transformedA = (transformation*typename AbstractShape::PointType(_a)).vector(); + _transformedB = (transformation*typename AbstractShape::PointType(_b)).vector(); + float scaling = (transformation.rotationScaling()*typename AbstractShape::VectorType(1/Math::Constants::sqrt3())).length(); _transformedRadius = scaling*_radius; } -bool Capsule::collides(const AbstractShape* other) const { - if(other->type() == Type::Point) - return *this % *static_cast(other); - if(other->type() == Type::Sphere) - return *this % *static_cast(other); +template bool Capsule::collides(const AbstractShape* other) const { + if(other->type() == AbstractShape::Type::Point) + return *this % *static_cast*>(other); + if(other->type() == AbstractShape::Type::Sphere) + return *this % *static_cast*>(other); - return AbstractShape::collides(other); + return AbstractShape::collides(other); } -bool Capsule::operator%(const Point& other) const { +template bool Capsule::operator%(const Point& other) const { return Distance::lineSegmentPointSquared(transformedA(), transformedB(), other.transformedPosition()) < Math::pow<2>(transformedRadius()); } -bool Capsule::operator%(const Sphere& other) const { +template bool Capsule::operator%(const Sphere& other) const { return Distance::lineSegmentPointSquared(transformedA(), transformedB(), other.transformedPosition()) < Math::pow<2>(transformedRadius()+other.transformedRadius()); } +template class Capsule<2>; +template class Capsule<3>; + }} diff --git a/src/Physics/Capsule.h b/src/Physics/Capsule.h index d85cef641..76bfc828f 100644 --- a/src/Physics/Capsule.h +++ b/src/Physics/Capsule.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::Physics::Capsule + * @brief Class Magnum::Physics::Capsule, typedef Magnum::Physics::Capsule2D, Magnum::Physics::Capsule3D */ #include "Math/Vector3.h" @@ -24,29 +24,48 @@ namespace Magnum { namespace Physics { -class Point; -class Sphere; +template class Point; +template class Sphere; /** @brief %Capsule defined by cylinder start and end point and radius Unlike other elements the capsule doesn't support asymmetric scaling. When applying transformation, the scale factor is averaged from all axes. +@see Capsule2D, Capsule3D */ -class PHYSICS_EXPORT Capsule: public AbstractShape { +template class PHYSICS_EXPORT Capsule: public AbstractShape { public: /** @brief Constructor */ - inline Capsule(const Vector3& a, const Vector3& b, float radius): _a(a), _transformedA(a), _b(b), _transformedB(b), _radius(radius), _transformedRadius(radius) {} + inline Capsule(const typename AbstractShape::VectorType& a, const typename AbstractShape::VectorType& b, float radius): _a(a), _transformedA(a), _b(b), _transformedB(b), _radius(radius), _transformedRadius(radius) {} - void applyTransformation(const Matrix4& transformation); + #ifndef DOXYGEN_GENERATING_OUTPUT + void applyTransformation(const typename AbstractShape::MatrixType& transformation); + #else + void applyTransformation(const MatrixType& transformation); + #endif - bool collides(const AbstractShape* other) const; + bool collides(const AbstractShape* other) const; - inline Vector3 a() const { return _a; } /**< @brief Start point */ - inline Vector3 b() const { return _a; } /**< @brief End point */ + /** @brief Start point */ + inline typename AbstractShape::VectorType a() const { + return _a; + } + + /** @brief End point */ + inline typename AbstractShape::VectorType b() const { + return _a; + } - inline void setA(const Vector3& a) { _a = a; } /**< @brief Set start point */ - inline void setB(const Vector3& b) { _b = b; } /**< @brief Set end point */ + /** @brief Set start point */ + inline void setA(const typename AbstractShape::VectorType& a) { + _a = a; + } + + /** @brief Set end point */ + inline void setB(const typename AbstractShape::VectorType& b) { + _b = b; + } /** @brief Radius */ inline float radius() const { return _radius; } @@ -55,10 +74,14 @@ class PHYSICS_EXPORT Capsule: public AbstractShape { inline void setRadius(float radius) { _radius = radius; } /** @brief Transformed first point */ - inline Vector3 transformedA() const { return _transformedA; } + inline typename AbstractShape::VectorType transformedA() const { + return _transformedA; + } /** @brief Transformed second point */ - inline Vector3 transformedB() const { return _transformedB; } + inline typename AbstractShape::VectorType transformedB() const { + return _transformedB; + } /** @brief Transformed radius */ inline float transformedRadius() const { @@ -66,25 +89,38 @@ class PHYSICS_EXPORT Capsule: public AbstractShape { } /** @brief Collision with point */ - bool operator%(const Point& other) const; + bool operator%(const Point& other) const; /** @brief Collision with sphere */ - bool operator%(const Sphere& other) const; + bool operator%(const Sphere& other) const; protected: - inline Type type() const { return Type::Capsule; } + inline typename AbstractShape::Type type() const { + return AbstractShape::Type::Capsule; + } private: - Vector3 _a, _transformedA, + typename AbstractShape::VectorType _a, _transformedA, _b, _transformedB; float _radius, _transformedRadius; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template class PHYSICS_EXPORT Capsule<2>; +extern template class PHYSICS_EXPORT Capsule<3>; +#endif + +/** @brief Two-dimensional capsule */ +typedef Capsule<2> Capsule2D; + +/** @brief Three-dimensional capsule */ +typedef Capsule<3> Capsule3D; + /** @collisionoperator{Point,Capsule} */ -inline bool operator%(const Point& a, const Capsule& b) { return b % a; } +template inline bool operator%(const Point& a, const Capsule& b) { return b % a; } /** @collisionoperator{Sphere,Capsule} */ -inline bool operator%(const Sphere& a, const Capsule& b) { return b % a; } +template inline bool operator%(const Sphere& a, const Capsule& b) { return b % a; } }} diff --git a/src/Physics/Line.cpp b/src/Physics/Line.cpp index 3dec9a602..882c516b7 100644 --- a/src/Physics/Line.cpp +++ b/src/Physics/Line.cpp @@ -20,9 +20,13 @@ namespace Magnum { namespace Physics { -void Line::applyTransformation(const Matrix4& transformation) { - _transformedA = (transformation*Point3D(_a)).xyz(); - _transformedB = (transformation*Point3D(_b)).xyz(); +template void Line::applyTransformation(const typename AbstractShape::MatrixType& transformation) { + _transformedA = (transformation*typename AbstractShape::PointType(_a)).vector(); + _transformedB = (transformation*typename AbstractShape::PointType(_b)).vector(); } +/* Explicitly instantiate the templates */ +template class Line<2>; +template class Line<3>; + }} diff --git a/src/Physics/Line.h b/src/Physics/Line.h index 22897adf5..4e43b98a9 100644 --- a/src/Physics/Line.h +++ b/src/Physics/Line.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::Physics::Line + * @brief Class Magnum::Physics::Line, typedef Magnum::Physics::Line2D, Magnum::Physics::Line3D */ #include "Math/Vector3.h" @@ -24,34 +24,74 @@ namespace Magnum { namespace Physics { -/** @brief Infinite line, defined by two points */ -class PHYSICS_EXPORT Line: public AbstractShape { +/** +@brief Infinite line, defined by two points + +@see Line2D, Line3D +@todo collision detection of two Line2D +*/ +template class PHYSICS_EXPORT Line: public AbstractShape { public: /** @brief Constructor */ - inline Line(const Vector3& a, const Vector3& b): _a(a), _transformedA(a), _b(b), _transformedB(b) {} - - void applyTransformation(const Matrix4& transformation); - - inline Vector3 a() const { return _a; } /**< @brief First point */ - inline Vector3 b() const { return _a; } /**< @brief Second point */ - - inline void setA(const Vector3& a) { _a = a; } /**< @brief Set first point */ - inline void setB(const Vector3& b) { _b = b; } /**< @brief Set second point */ + inline Line(const typename AbstractShape::VectorType& a, const typename AbstractShape::VectorType& b): _a(a), _transformedA(a), _b(b), _transformedB(b) {} + + #ifndef DOXYGEN_GENERATING_OUTPUT + void applyTransformation(const typename AbstractShape::MatrixType& transformation); + #else + void applyTransformation(const MatrixType& transformation); + #endif + + /** @brief First point */ + inline typename AbstractShape::VectorType a() const { + return _a; + } + + /** @brief Second point */ + inline typename AbstractShape::VectorType b() const { + return _a; + } + + /** @brief Set first point */ + inline void setA(const typename AbstractShape::VectorType& a) { + _a = a; + } + + /** @brief Set second point */ + inline void setB(const typename AbstractShape::VectorType& b) { + _b = b; + } /** @brief Transformed first point */ - inline Vector3 transformedA() const { return _transformedA; } + inline typename AbstractShape::VectorType transformedA() const { + return _transformedA; + } /** @brief Transformed second point */ - inline Vector3 transformedB() const { return _transformedB; } + inline typename AbstractShape::VectorType transformedB() const { + return _transformedB; + } protected: - inline Type type() const { return Type::Line; } + inline typename AbstractShape::Type type() const { + return AbstractShape::Type::Line; + } private: - Vector3 _a, _transformedA, + typename AbstractShape::VectorType _a, _transformedA, _b, _transformedB; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template class PHYSICS_EXPORT Line<2>; +extern template class PHYSICS_EXPORT Line<3>; +#endif + +/** @brief Infinite two-dimensional line */ +typedef Line<2> Line2D; + +/** @brief Infinite three-dimensional line */ +typedef Line<3> Line3D; + }} #endif diff --git a/src/Physics/LineSegment.h b/src/Physics/LineSegment.h index c10a6d8f0..c7d59863d 100644 --- a/src/Physics/LineSegment.h +++ b/src/Physics/LineSegment.h @@ -16,23 +16,33 @@ */ /** @file - * @brief Class Magnum::Physics::LineSegment + * @brief Class Magnum::Physics::LineSegment, typedef Magnum::Physics::LineSegment2D, Magnum::Physics::LineSegment3D */ #include "Line.h" namespace Magnum { namespace Physics { -/** @brief %Line segment, defined by starting and ending point */ -class LineSegment: public Line { +/** +@brief %Line segment, defined by starting and ending point + +@see LineSegment2D, LineSegment3D +*/ +template class LineSegment: public Line { public: /** @brief Constructor */ - inline LineSegment(const Vector3& a, const Vector3& b): Line(a, b) {} + inline LineSegment(const typename AbstractShape::VectorType& a, const typename AbstractShape::VectorType& b): Line(a, b) {} protected: - inline Type type() const { return Type::LineSegment; } + inline typename AbstractShape::Type type() const { return AbstractShape::Type::LineSegment; } }; +/** @brief Two-dimensional line segment */ +typedef LineSegment<2> LineSegment2D; + +/** @brief Three-dimensional line segment */ +typedef LineSegment<3> LineSegment3D; + }} #endif diff --git a/src/Physics/Plane.cpp b/src/Physics/Plane.cpp index 124b28ccf..79ead18c7 100644 --- a/src/Physics/Plane.cpp +++ b/src/Physics/Plane.cpp @@ -32,21 +32,21 @@ void Plane::applyTransformation(const Matrix4& transformation) { _transformedNormal = transformation.rotation()*_normal; } -bool Plane::collides(const AbstractShape* other) const { +bool Plane::collides(const AbstractShape<3>* other) const { if(other->type() == Type::Line) - return *this % *static_cast(other); + return *this % *static_cast(other); if(other->type() == Type::LineSegment) - return *this % *static_cast(other); + return *this % *static_cast(other); return AbstractShape::collides(other); } -bool Plane::operator%(const Line& other) const { +bool Plane::operator%(const Line3D& other) const { float t = Intersection::planeLine(transformedPosition(), transformedNormal(), other.transformedA(), other.transformedB()); return t != t || (t != numeric_limits::infinity() && t != -numeric_limits::infinity()); } -bool Plane::operator%(const LineSegment& other) const { +bool Plane::operator%(const LineSegment3D& other) const { float t = Intersection::planeLine(transformedPosition(), transformedNormal(), other.transformedA(), other.transformedB()); return t > 0.0f && t < 1.0f; } diff --git a/src/Physics/Plane.h b/src/Physics/Plane.h index dd363b1c7..64e7c116e 100644 --- a/src/Physics/Plane.h +++ b/src/Physics/Plane.h @@ -24,18 +24,24 @@ namespace Magnum { namespace Physics { -class Line; -class LineSegment; +template class Line; +typedef Line<3> Line3D; +template class LineSegment; +typedef LineSegment<3> LineSegment3D; -/** @brief Infinite plane, defined by position and normal */ -class PHYSICS_EXPORT Plane: public AbstractShape { +/** @brief Infinite plane, defined by position and normal (3D only) */ +class PHYSICS_EXPORT Plane: public AbstractShape<3> { public: /** @brief Constructor */ inline Plane(const Vector3& position, const Vector3& normal): _position(position), _transformedPosition(position), _normal(normal), _transformedNormal(normal) {} + #ifndef DOXYGEN_GENERATING_OUTPUT void applyTransformation(const Matrix4& transformation); - + bool collides(const AbstractShape<3>* other) const; + #else + void applyTransformation(const MatrixType& transformation); bool collides(const AbstractShape* other) const; + #endif /** @brief Position */ inline Vector3 position() const { return _position; } @@ -64,10 +70,10 @@ class PHYSICS_EXPORT Plane: public AbstractShape { } /** @brief Collision with line */ - bool operator%(const Line& other) const; + bool operator%(const Line3D& other) const; /** @brief Collision with line segment */ - bool operator%(const LineSegment& other) const; + bool operator%(const LineSegment3D& other) const; protected: inline Type type() const { return Type::Plane; } @@ -78,10 +84,10 @@ class PHYSICS_EXPORT Plane: public AbstractShape { }; /** @collisionoperator{Line,Plane} */ -inline bool operator%(const Line& a, const Plane& b) { return b % a; } +inline bool operator%(const Line3D& a, const Plane& b) { return b % a; } /** @collisionoperator{LineSegment,Plane} */ -inline bool operator%(const LineSegment& a, const Plane& b) { return b % a; } +inline bool operator%(const LineSegment3D& a, const Plane& b) { return b % a; } }} diff --git a/src/Physics/Point.cpp b/src/Physics/Point.cpp index 6ec43579b..048e66207 100644 --- a/src/Physics/Point.cpp +++ b/src/Physics/Point.cpp @@ -20,8 +20,11 @@ namespace Magnum { namespace Physics { -void Point::applyTransformation(const Matrix4& transformation) { - _transformedPosition = (transformation*Point3D(_position)).xyz(); +template void Point::applyTransformation(const typename AbstractShape::MatrixType& transformation) { + _transformedPosition = (transformation*typename AbstractShape::PointType(_position)).vector(); } +template class Point<2>; +template class Point<3>; + }} diff --git a/src/Physics/Point.h b/src/Physics/Point.h index 787236b6b..05483d9b3 100644 --- a/src/Physics/Point.h +++ b/src/Physics/Point.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::Physics::Point + * @brief Class Magnum::Physics::Point, typedef Magnum::Physics::Point2D, Magnum::Physics::Point3D */ #include "Math/Vector3.h" @@ -24,34 +24,55 @@ namespace Magnum { namespace Physics { -/** @brief %Point */ -class PHYSICS_EXPORT Point: public AbstractShape { +/** +@brief %Point + +@see Point2D, Point3D +*/ +template class PHYSICS_EXPORT Point: public AbstractShape { public: /** @brief Constructor */ - inline Point(const Vector3& position): _position(position), _transformedPosition(position) {} + inline Point(const typename AbstractShape::VectorType& position): _position(position), _transformedPosition(position) {} - void applyTransformation(const Matrix4& transformation); + #ifndef DOXYGEN_GENERATING_OUTPUT + void applyTransformation(const typename AbstractShape::MatrixType& transformation); + #else + void applyTransformation(const MatrixType& transformation); + #endif /** @brief Position */ - inline Vector3 position() const { return _position; } + inline typename AbstractShape::VectorType position() const { + return _position; + } /** @brief Set position */ - inline void setPosition(const Vector3& position) { + inline void setPosition(const typename AbstractShape::VectorType& position) { _position = position; } /** @brief Transformed position */ - inline Vector3 transformedPosition() const { + inline typename AbstractShape::VectorType transformedPosition() const { return _transformedPosition; } protected: - inline Type type() const { return Type::Point; } + inline typename AbstractShape::Type type() const { return AbstractShape::Type::Point; } private: - Vector3 _position, _transformedPosition; + typename AbstractShape::VectorType _position, _transformedPosition; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template class PHYSICS_EXPORT Point<2>; +extern template class PHYSICS_EXPORT Point<3>; +#endif + +/** @brief Two-dimensional point */ +typedef Point<2> Point2D; + +/** @brief Three-dimensional point */ +typedef Point<3> Point3D; + }} #endif diff --git a/src/Physics/ShapeGroup.cpp b/src/Physics/ShapeGroup.cpp index 4f70f0b9e..813baf7bd 100644 --- a/src/Physics/ShapeGroup.cpp +++ b/src/Physics/ShapeGroup.cpp @@ -17,47 +17,50 @@ namespace Magnum { namespace Physics { -ShapeGroup::ShapeGroup(ShapeGroup&& other): operation(other.operation), a(other.a), b(other.b) { - other.operation = AlwaysFalse; +template ShapeGroup::ShapeGroup(ShapeGroup&& other): operation(other.operation), a(other.a), b(other.b) { + other.operation = Implementation::GroupOperation::AlwaysFalse; other.a = nullptr; other.b = nullptr; } -ShapeGroup::~ShapeGroup() { - if(!(operation & RefA)) delete a; - if(!(operation & RefB)) delete b; +template ShapeGroup::~ShapeGroup() { + if(!(operation & Implementation::GroupOperation::RefA)) delete a; + if(!(operation & Implementation::GroupOperation::RefB)) delete b; } -ShapeGroup& ShapeGroup::operator=(ShapeGroup&& other) { - if(!(operation & RefA)) delete a; - if(!(operation & RefB)) delete b; +template ShapeGroup& ShapeGroup::operator=(ShapeGroup&& other) { + if(!(operation & Implementation::GroupOperation::RefA)) delete a; + if(!(operation & Implementation::GroupOperation::RefB)) delete b; operation = other.operation; a = other.a; b = other.b; - other.operation = AlwaysFalse; + other.operation = Implementation::GroupOperation::AlwaysFalse; other.a = nullptr; other.b = nullptr; return *this; } -void ShapeGroup::applyTransformation(const Matrix4& transformation) { +template void ShapeGroup::applyTransformation(const typename AbstractShape::MatrixType& transformation) { if(a) a->applyTransformation(transformation); if(b) b->applyTransformation(transformation); } -bool ShapeGroup::collides(const AbstractShape* other) const { - switch(operation & ~RefAB) { - 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); +template bool ShapeGroup::collides(const AbstractShape* other) const { + switch(operation & ~Implementation::GroupOperation::RefAB) { + case Implementation::GroupOperation::And: return a->collides(other) && b->collides(other); + case Implementation::GroupOperation::Or: return a->collides(other) || b->collides(other); + case Implementation::GroupOperation::Not: return !a->collides(other); + case Implementation::GroupOperation::FirstObjectOnly: return a->collides(other); default: return false; } } +template class ShapeGroup<2>; +template class ShapeGroup<3>; + }} diff --git a/src/Physics/ShapeGroup.h b/src/Physics/ShapeGroup.h index 4b56a8cb8..200898761 100644 --- a/src/Physics/ShapeGroup.h +++ b/src/Physics/ShapeGroup.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::Physics::ShapeGroup + * @brief Class Magnum::Physics::ShapeGroup, typedef Magnum::Physics::ShapeGroup2D, Magnum::Physics::ShapeGroup3D */ #include "AbstractShape.h" @@ -27,8 +27,25 @@ namespace Magnum { namespace Physics { #ifndef DOXYGEN_GENERATING_OUTPUT -#define enableIfIsBaseType typename std::enable_if::value, ShapeGroup>::type -#define enableIfAreBaseType typename std::enable_if::value && std::is_base_of::value, ShapeGroup>::type +namespace Implementation { + enum GroupOperation { + RefA = 0x01, + RefB = 0x02, + RefAB = 0x03, +// 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 + }; +} +#define enableIfIsBaseType typename std::enable_if, T>::value, ShapeGroup>::type +#define enableIfAreBaseType typename std::enable_if, T>::value && std::is_base_of, U>::value, ShapeGroup>::type #endif /** @@ -36,26 +53,27 @@ namespace Magnum { namespace Physics { Result of logical operations on shapes. See @ref collision-detection for brief introduction. +@see ShapeGroup2D, ShapeGroup3D */ -class PHYSICS_EXPORT ShapeGroup: public AbstractShape { +template 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 operator~(const T& a) -> enableIfIsBaseType; +// template friend constexpr operator~(T&& a) -> enableIfIsBaseType; +// template friend constexpr operator~(T& a) -> enableIfIsBaseType; + template friend constexpr auto operator!(const T& a) -> enableIfIsBaseType; + template friend constexpr auto operator!(T&& a) -> enableIfIsBaseType; + template friend constexpr auto operator!(T& a) -> enableIfIsBaseType; #define friendOp(char) \ - template friend constexpr enableIfAreBaseType operator char(const T& a, const U& b); \ - template friend constexpr enableIfAreBaseType operator char(const T& a, U&& b); \ - template friend constexpr enableIfAreBaseType operator char(T&& a, const U& b); \ - template friend constexpr enableIfAreBaseType operator char(T&& a, U&& b); \ - template friend constexpr enableIfAreBaseType operator char(const T& a, std::reference_wrapper b); \ - template friend constexpr enableIfAreBaseType operator char(T&& a, std::reference_wrapper b); \ - 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); + template friend constexpr auto operator char(const T& a, const U& b) -> enableIfAreBaseType; \ + template friend constexpr auto operator char(const T& a, U&& b) -> enableIfAreBaseType; \ + template friend constexpr auto operator char(T&& a, const U& b) -> enableIfAreBaseType; \ + template friend constexpr auto operator char(T&& a, U&& b) -> enableIfAreBaseType; \ + template friend constexpr auto operator char(const T& a, std::reference_wrapper b) -> enableIfAreBaseType; \ + template friend constexpr auto operator char(T&& a, std::reference_wrapper b) -> enableIfAreBaseType; \ + template friend constexpr auto operator char(std::reference_wrapper a, const U& b) -> enableIfAreBaseType; \ + template friend constexpr auto operator char(std::reference_wrapper a, U&& b) -> enableIfAreBaseType; \ + template friend constexpr auto operator char(std::reference_wrapper a, std::reference_wrapper b) -> enableIfAreBaseType; // friendOp(|) // friendOp(&) // friendOp(-) @@ -68,26 +86,9 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape { ShapeGroup(const ShapeGroup& other) = delete; ShapeGroup& operator=(const ShapeGroup& other) = delete; - private: - enum Operation { - RefA = 0x01, - RefB = 0x02, - RefAB = 0x03, -// 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: /** @brief Default constructor */ - inline ShapeGroup(): operation(AlwaysFalse), a(nullptr), b(nullptr) {} + inline ShapeGroup(): operation(Implementation::GroupOperation::AlwaysFalse), a(nullptr), b(nullptr) {} /** @brief Move constructor */ ShapeGroup(ShapeGroup&& other); @@ -98,21 +99,36 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape { /** @brief Move assignment */ ShapeGroup& operator=(ShapeGroup&& other); - void applyTransformation(const Matrix4& transformation); + #ifndef DOXYGEN_GENERATING_OUTPUT + void applyTransformation(const typename AbstractShape::MatrixType& transformation); + #else + void applyTransformation(const MatrixType& transformation); + #endif - bool collides(const AbstractShape* other) const; + bool collides(const AbstractShape* other) const; protected: - virtual Type type() const { return Type::ShapeGroup; } + virtual typename AbstractShape::Type type() const { return AbstractShape::Type::ShapeGroup; } private: - inline ShapeGroup(int operation, AbstractShape* a, AbstractShape* b): operation(operation), a(a), b(b) {} + inline ShapeGroup(int operation, AbstractShape* a, AbstractShape* b): operation(operation), a(a), b(b) {} int operation; - AbstractShape* a; - AbstractShape* b; + AbstractShape* a; + AbstractShape* b; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template class PHYSICS_EXPORT ShapeGroup<2>; +extern template class PHYSICS_EXPORT ShapeGroup<3>; +#endif + +/** @brief Two-dimensional shape group */ +typedef ShapeGroup<2> ShapeGroup2D; + +/** @brief Three-dimensional shape group */ +typedef ShapeGroup<3> ShapeGroup3D; + // /* @brief Complement of shape */ // template inline constexpr enableIfIsBaseType operator~(const T& a) { // return ShapeGroup(ShapeGroup::Complement, new T(a), nullptr); @@ -129,15 +145,15 @@ class PHYSICS_EXPORT ShapeGroup: public AbstractShape { /** @relates ShapeGroup @brief Logical NOT of shape */ -template inline constexpr enableIfIsBaseType operator!(const T& a) { - return ShapeGroup(ShapeGroup::Not, new T(a), nullptr); +template inline constexpr auto operator!(const T& a) -> enableIfIsBaseType { + return ShapeGroup(Implementation::GroupOperation::Not, new T(a), nullptr); } #ifndef DOXYGEN_GENERATING_OUTPUT -template inline constexpr enableIfIsBaseType operator!(T&& a) { - return ShapeGroup(ShapeGroup::Not, new T(std::forward(a)), nullptr); +template inline constexpr auto operator!(T&& a) -> enableIfIsBaseType { + return ShapeGroup(Implementation::GroupOperation::Not, new T(std::forward(a)), nullptr); } -template inline constexpr enableIfIsBaseType operator!(T& a) { - return ShapeGroup(ShapeGroup::Not|ShapeGroup::RefA, &a.get(), nullptr); +template inline constexpr auto operator!(T& a) -> enableIfIsBaseType { + return ShapeGroup(Implementation::GroupOperation::Not|Implementation::GroupOperation::RefA, &a.get(), nullptr); } #endif @@ -161,7 +177,7 @@ 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); /** @relates ShapeGroup @brief Logical OR of two shapes @@ -170,35 +186,35 @@ template inline constexpr ShapeGroup operator&&(T a, U b); 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); +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) { \ - return ShapeGroup(ShapeGroup::type, new T(a), new U(b)); \ +template inline constexpr auto operator char(const T& a, const U& b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type, new T(a), new U(b)); \ } \ -template inline constexpr enableIfAreBaseType operator char(const T& a, U&& b) { \ - return ShapeGroup(ShapeGroup::type, new T(a), new U(std::forward(b))); \ +template inline constexpr auto operator char(const T& a, U&& b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type, new T(a), new U(std::forward(b))); \ } \ -template inline constexpr enableIfAreBaseType operator char(T&& a, const U& b) { \ - return ShapeGroup(ShapeGroup::type, new T(std::forward(a)), new U(b)); \ +template inline constexpr auto operator char(T&& a, const U& b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type, new T(std::forward(a)), new U(b)); \ } \ -template inline constexpr enableIfAreBaseType operator char(T&& a, U&& b) { \ - return ShapeGroup(ShapeGroup::type, new T(std::forward(a)), new U(std::forward(b))); \ +template inline constexpr auto operator char(T&& a, U&& b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type, new T(std::forward(a)), new U(std::forward(b))); \ } \ -template inline constexpr enableIfAreBaseType operator char(const T& a, std::reference_wrapper b) { \ - return ShapeGroup(ShapeGroup::type|ShapeGroup::RefB, new T(a), &b.get()); \ +template inline constexpr auto operator char(const T& a, std::reference_wrapper b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type|Implementation::GroupOperation::RefB, new T(a), &b.get()); \ } \ -template inline constexpr enableIfAreBaseType operator char(T&& a, std::reference_wrapper b) { \ - return ShapeGroup(ShapeGroup::type|ShapeGroup::RefB, new T(std::forward(a)), &b.get()); \ +template inline constexpr auto operator char(T&& a, std::reference_wrapper b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type|Implementation::GroupOperation::RefB, new T(std::forward(a)), &b.get()); \ } \ -template inline constexpr enableIfAreBaseType operator char(std::reference_wrapper a, const U& b) { \ - return ShapeGroup(ShapeGroup::type|ShapeGroup::RefA, &a.get(), new U(b)); \ +template inline constexpr auto operator char(std::reference_wrapper a, const U& b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type|Implementation::GroupOperation::RefA, &a.get(), new U(b)); \ } \ -template inline constexpr enableIfAreBaseType operator char(std::reference_wrapper a, U&& b) { \ - return ShapeGroup(ShapeGroup::type|ShapeGroup::RefA, &a.get(), new U(std::forward(b))); \ +template inline constexpr auto operator char(std::reference_wrapper a, U&& b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type|Implementation::GroupOperation::RefA, &a.get(), new U(std::forward(b))); \ } \ -template inline constexpr enableIfAreBaseType operator char(std::reference_wrapper a, std::reference_wrapper b) { \ - return ShapeGroup(ShapeGroup::type|ShapeGroup::RefAB, &a.get(), &b.get()); \ +template inline constexpr auto operator char(std::reference_wrapper a, std::reference_wrapper b) -> enableIfAreBaseType { \ + return ShapeGroup(Implementation::GroupOperation::type|Implementation::GroupOperation::RefAB, &a.get(), &b.get()); \ } // op(Union, |) // op(Intersection, &) diff --git a/src/Physics/Sphere.cpp b/src/Physics/Sphere.cpp index f3b0579b1..b2f6d00fb 100644 --- a/src/Physics/Sphere.cpp +++ b/src/Physics/Sphere.cpp @@ -27,43 +27,58 @@ using namespace Magnum::Math::Geometry; namespace Magnum { namespace Physics { -void Sphere::applyTransformation(const Matrix4& transformation) { - _transformedPosition = (transformation*Point3D(_position)).xyz(); - float scaling = (transformation.rotationScaling()*Vector3(1/Math::Constants::sqrt3())).length(); +namespace { + template static typename AbstractShape::VectorType unitVector(); + + template<> inline Vector2 unitVector<2>() { + return Vector2(1/Math::Constants::sqrt2()); + } + + template<> inline Vector3 unitVector<3>() { + return Vector3(1/Math::Constants::sqrt3()); + } +} + +template void Sphere::applyTransformation(const typename AbstractShape::MatrixType& transformation) { + _transformedPosition = (transformation*typename AbstractShape::PointType(_position)).vector(); + float scaling = (transformation.rotationScaling()*unitVector()).length(); _transformedRadius = scaling*_radius; } -bool Sphere::collides(const AbstractShape* other) const { - if(other->type() == Type::Point) - return *this % *static_cast(other); - if(other->type() == Type::Line) - return *this % *static_cast(other); - if(other->type() == Type::LineSegment) - return *this % *static_cast(other); - if(other->type() == Type::Sphere) - return *this % *static_cast(other); - - return AbstractShape::collides(other); +template bool Sphere::collides(const AbstractShape* other) const { + if(other->type() == AbstractShape::Type::Point) + return *this % *static_cast*>(other); + if(other->type() == AbstractShape::Type::Line) + return *this % *static_cast*>(other); + if(other->type() == AbstractShape::Type::LineSegment) + return *this % *static_cast*>(other); + if(other->type() == AbstractShape::Type::Sphere) + return *this % *static_cast*>(other); + + return AbstractShape::collides(other); } -bool Sphere::operator%(const Point& other) const { +template bool Sphere::operator%(const Point& other) const { return (other.transformedPosition()-transformedPosition()).dot() < Math::pow<2>(transformedRadius()); } -bool Sphere::operator%(const Line& other) const { +template bool Sphere::operator%(const Line& other) const { return Distance::linePointSquared(other.transformedA(), other.transformedB(), transformedPosition()) < Math::pow<2>(transformedRadius()); } -bool Sphere::operator%(const LineSegment& other) const { +template bool Sphere::operator%(const LineSegment& other) const { return Distance::lineSegmentPointSquared(other.transformedA(), other.transformedB(), transformedPosition()) < Math::pow<2>(transformedRadius()); } -bool Sphere::operator%(const Sphere& other) const { +template bool Sphere::operator%(const Sphere& other) const { return (other.transformedPosition()-transformedPosition()).dot() < Math::pow<2>(transformedRadius()+other.transformedRadius()); } +template class Sphere<2>; +template class Sphere<3>; + }} diff --git a/src/Physics/Sphere.h b/src/Physics/Sphere.h index ddc317603..60639a37d 100644 --- a/src/Physics/Sphere.h +++ b/src/Physics/Sphere.h @@ -16,7 +16,7 @@ */ /** @file - * @brief Class Magnum::Physics::Sphere + * @brief Class Magnum::Physics::Sphere, typedef Magnum::Physics::Sphere2D, Magnum::Physics::Sphere3D */ #include "Math/Vector3.h" @@ -24,30 +24,39 @@ namespace Magnum { namespace Physics { -class Line; -class LineSegment; -class Point; +template class Line; +template class LineSegment; +template class Point; /** @brief %Sphere defined by position and radius Unlike other elements the sphere doesn't support asymmetric scaling. When applying transformation, the scale factor is averaged from all axes. +@see Sphere2D, Sphere3D */ -class PHYSICS_EXPORT Sphere: public AbstractShape { +template class PHYSICS_EXPORT Sphere: public AbstractShape { public: /** @brief Constructor */ - inline Sphere(const Vector3& position, float radius): _position(position), _transformedPosition(position), _radius(radius), _transformedRadius(radius) {} + inline Sphere(const typename AbstractShape::VectorType& position, float radius): _position(position), _transformedPosition(position), _radius(radius), _transformedRadius(radius) {} - void applyTransformation(const Matrix4& transformation); + #ifndef DOXYGEN_GENERATING_OUTPUT + void applyTransformation(const typename AbstractShape::MatrixType& transformation); + #else + void applyTransformation(const MatrixType& transformation); + #endif - bool collides(const AbstractShape* other) const; + bool collides(const AbstractShape* other) const; /** @brief Position */ - inline Vector3 position() const { return _position; } + inline typename AbstractShape::VectorType position() const { + return _position; + } /** @brief Set position */ - inline void setPosition(const Vector3& position) { _position = position; } + inline void setPosition(const typename AbstractShape::VectorType& position) { + _position = position; + } /** @brief Radius */ inline float radius() const { return _radius; } @@ -56,7 +65,7 @@ class PHYSICS_EXPORT Sphere: public AbstractShape { inline void setRadius(float radius) { _radius = radius; } /** @brief Transformed position */ - inline Vector3 transformedPosition() const { + inline typename AbstractShape::VectorType transformedPosition() const { return _transformedPosition; } @@ -66,33 +75,47 @@ class PHYSICS_EXPORT Sphere: public AbstractShape { } /** @brief Collision with point */ - bool operator%(const Point& other) const; + bool operator%(const Point& other) const; /** @brief Collision with line */ - bool operator%(const Line& other) const; + bool operator%(const Line& other) const; /** @brief Collision with line segment */ - bool operator%(const LineSegment& other) const; + bool operator%(const LineSegment& other) const; /** @brief Collision with sphere */ - bool operator%(const Sphere& other) const; + bool operator%(const Sphere& other) const; protected: - inline Type type() const { return Type::Sphere; } + inline typename AbstractShape::Type type() const { + return AbstractShape::Type::Sphere; + } private: - Vector3 _position, _transformedPosition; + typename AbstractShape::VectorType _position, + _transformedPosition; float _radius, _transformedRadius; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template class PHYSICS_EXPORT Sphere<2>; +extern template class PHYSICS_EXPORT Sphere<3>; +#endif + +/** @brief Two-dimensional sphere */ +typedef Sphere<2> Sphere2D; + +/** @brief Three-dimensional sphere */ +typedef Sphere<3> Sphere3D; + /** @collisionoperator{Point,Sphere} */ -inline bool operator%(const Point& a, const Sphere& b) { return b % a; } +template inline bool operator%(const Point& a, const Sphere& b) { return b % a; } /** @collisionoperator{Line,Sphere} */ -inline bool operator%(const Line& a, const Sphere& b) { return b % a; } +template inline bool operator%(const Line& a, const Sphere& b) { return b % a; } /** @collisionoperator{LineSegment,Sphere} */ -inline bool operator%(const LineSegment& a, const Sphere& b) { return b % a; } +template inline bool operator%(const LineSegment& a, const Sphere& b) { return b % a; } }} diff --git a/src/Physics/Test/AxisAlignedBoxTest.cpp b/src/Physics/Test/AxisAlignedBoxTest.cpp index 81d6eba57..da5cd0894 100644 --- a/src/Physics/Test/AxisAlignedBoxTest.cpp +++ b/src/Physics/Test/AxisAlignedBoxTest.cpp @@ -28,7 +28,7 @@ AxisAlignedBoxTest::AxisAlignedBoxTest() { } void AxisAlignedBoxTest::applyTransformation() { - Physics::AxisAlignedBox box({-1.0f, -2.0f, -3.0f}, {1.0f, 2.0f, 3.0f}); + Physics::AxisAlignedBox3D box({-1.0f, -2.0f, -3.0f}, {1.0f, 2.0f, 3.0f}); box.applyTransformation(Matrix4::scaling({2.0f, -1.0f, 1.5f})); CORRADE_COMPARE(box.transformedPosition(), Vector3(-2.0f, 2.0f, -4.5f)); diff --git a/src/Physics/Test/BoxTest.cpp b/src/Physics/Test/BoxTest.cpp index e844e4d2e..32c04ff48 100644 --- a/src/Physics/Test/BoxTest.cpp +++ b/src/Physics/Test/BoxTest.cpp @@ -27,7 +27,7 @@ BoxTest::BoxTest() { } void BoxTest::applyTransformation() { - Physics::Box box(Matrix4::translation({1.0f, 2.0f, -3.0f})); + Physics::Box3D box(Matrix4::translation({1.0f, 2.0f, -3.0f})); box.applyTransformation(Matrix4::scaling({2.0f, -1.0f, 1.5f})); CORRADE_COMPARE(box.transformedTransformation(), Matrix4::scaling({2.0f, -1.0f, 1.5f})*Matrix4::translation({1.0f, 2.0f, -3.0f})); diff --git a/src/Physics/Test/CapsuleTest.cpp b/src/Physics/Test/CapsuleTest.cpp index 17feb9204..1d11fabc4 100644 --- a/src/Physics/Test/CapsuleTest.cpp +++ b/src/Physics/Test/CapsuleTest.cpp @@ -30,7 +30,7 @@ CapsuleTest::CapsuleTest() { } void CapsuleTest::applyTransformation() { - Physics::Capsule capsule({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f); + Physics::Capsule3D capsule({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}, 7.0f); capsule.applyTransformation(Matrix4::rotation(deg(90.0f), Vector3::zAxis())); CORRADE_COMPARE(capsule.transformedA(), Vector3(-2.0f, 1.0f, 3.0f)); @@ -43,10 +43,10 @@ void CapsuleTest::applyTransformation() { } void CapsuleTest::collisionPoint() { - Physics::Capsule capsule({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f); - Physics::Point point({2.0f, 0.0f, 0.0f}); - Physics::Point point1({2.9f, 1.0f, 0.0f}); - Physics::Point point2({1.0f, 3.1f, 0.0f}); + Physics::Capsule3D capsule({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f); + Physics::Point3D point({2.0f, 0.0f, 0.0f}); + Physics::Point3D point1({2.9f, 1.0f, 0.0f}); + Physics::Point3D point2({1.0f, 3.1f, 0.0f}); randomTransformation(capsule); randomTransformation(point); @@ -59,10 +59,10 @@ void CapsuleTest::collisionPoint() { } void CapsuleTest::collisionSphere() { - Physics::Capsule capsule({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f); - Physics::Sphere sphere({3.0f, 0.0f, 0.0f}, 0.9f); - Physics::Sphere sphere1({3.5f, 1.0f, 0.0f}, 0.6f); - Physics::Sphere sphere2({1.0f, 4.1f, 0.0f}, 1.0f); + Physics::Capsule3D capsule({-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, 2.0f); + Physics::Sphere3D sphere({3.0f, 0.0f, 0.0f}, 0.9f); + Physics::Sphere3D sphere1({3.5f, 1.0f, 0.0f}, 0.6f); + Physics::Sphere3D sphere2({1.0f, 4.1f, 0.0f}, 1.0f); randomTransformation(capsule); randomTransformation(sphere); diff --git a/src/Physics/Test/LineTest.cpp b/src/Physics/Test/LineTest.cpp index 1e06ccb4b..2a673d33c 100644 --- a/src/Physics/Test/LineTest.cpp +++ b/src/Physics/Test/LineTest.cpp @@ -28,7 +28,7 @@ LineTest::LineTest() { } void LineTest::applyTransformation() { - Physics::Line line({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}); + Physics::Line3D line({1.0f, 2.0f, 3.0f}, {-1.0f, -2.0f, -3.0f}); line.applyTransformation(Matrix4::rotation(deg(90.0f), Vector3::zAxis())); CORRADE_COMPARE(line.transformedA(), Vector3(-2.0f, 1.0f, 3.0f)); CORRADE_COMPARE(line.transformedB(), Vector3(2.0f, -1.0f, -3.0f)); diff --git a/src/Physics/Test/PlaneTest.cpp b/src/Physics/Test/PlaneTest.cpp index f2f799189..75170f84c 100644 --- a/src/Physics/Test/PlaneTest.cpp +++ b/src/Physics/Test/PlaneTest.cpp @@ -45,9 +45,9 @@ void PlaneTest::applyTransformation() { void PlaneTest::collisionLine() { Physics::Plane plane(Vector3(), Vector3::yAxis()); - Physics::Line line({0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}); - Physics::Line line2({0.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}); - Physics::Line line3({0.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}); + Physics::Line3D line({0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}); + Physics::Line3D line2({0.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}); + Physics::Line3D line3({0.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}); randomTransformation(plane); randomTransformation(line); @@ -61,9 +61,9 @@ void PlaneTest::collisionLine() { void PlaneTest::collisionLineSegment() { Physics::Plane plane(Vector3(), Vector3::yAxis()); - Physics::LineSegment line({0.0f, -0.1f, 0.0f}, {0.0f, 7.0f, 0.0f}); - Physics::LineSegment line2({0.0f, 0.1f, 0.0f}, {0.0f, 7.0f, 0.0f}); - Physics::LineSegment line3({0.0f, -7.0f, 0.0f}, {0.0f, -0.1f, 0.0f}); + Physics::LineSegment3D line({0.0f, -0.1f, 0.0f}, {0.0f, 7.0f, 0.0f}); + Physics::LineSegment3D line2({0.0f, 0.1f, 0.0f}, {0.0f, 7.0f, 0.0f}); + Physics::LineSegment3D line3({0.0f, -7.0f, 0.0f}, {0.0f, -0.1f, 0.0f}); randomTransformation(plane); randomTransformation(line); diff --git a/src/Physics/Test/PointTest.cpp b/src/Physics/Test/PointTest.cpp index ed191dfc5..f6e47e785 100644 --- a/src/Physics/Test/PointTest.cpp +++ b/src/Physics/Test/PointTest.cpp @@ -27,7 +27,7 @@ PointTest::PointTest() { } void PointTest::applyTransformation() { - Physics::Point point({1.0f, 2.0f, 3.0f}); + Physics::Point3D point({1.0f, 2.0f, 3.0f}); point.applyTransformation(Matrix4::translation({5.0f, 6.0f, 7.0f})); CORRADE_COMPARE(point.transformedPosition(), Vector3(6.0f, 8.0f, 10.0f)); } diff --git a/src/Physics/Test/ShapeGroupTest.cpp b/src/Physics/Test/ShapeGroupTest.cpp index 69eaffa13..4270fd4be 100644 --- a/src/Physics/Test/ShapeGroupTest.cpp +++ b/src/Physics/Test/ShapeGroupTest.cpp @@ -32,10 +32,10 @@ ShapeGroupTest::ShapeGroupTest() { } void ShapeGroupTest::copy() { - ShapeGroup group; + ShapeGroup3D group; { - Physics::Point point({1.0f, 2.0f, 3.0f}); - Physics::LineSegment segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); + Physics::Point3D point({1.0f, 2.0f, 3.0f}); + Physics::LineSegment3D segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); group = !(point || segment); } @@ -47,10 +47,10 @@ void ShapeGroupTest::copy() { } void ShapeGroupTest::reference() { - Physics::Point point({1.0f, 2.0f, 3.0f}); - Physics::LineSegment segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); + Physics::Point3D point({1.0f, 2.0f, 3.0f}); + Physics::LineSegment3D segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); - ShapeGroup group = !(ref(point) || ref(segment)); + ShapeGroup3D group = !(ref(point) || ref(segment)); group.applyTransformation(Matrix4::translation(Vector3(1.0f))); diff --git a/src/Physics/Test/SphereTest.cpp b/src/Physics/Test/SphereTest.cpp index d78291a2c..473be576c 100644 --- a/src/Physics/Test/SphereTest.cpp +++ b/src/Physics/Test/SphereTest.cpp @@ -33,7 +33,7 @@ SphereTest::SphereTest() { } void SphereTest::applyTransformation() { - Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 7.0f); + Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 7.0f); sphere.applyTransformation(Matrix4::rotation(deg(90.0f), Vector3::yAxis())); CORRADE_COMPARE(sphere.transformedPosition(), Vector3(3.0f, 2.0f, -1.0f)); @@ -50,9 +50,9 @@ void SphereTest::applyTransformation() { } void SphereTest::collisionPoint() { - Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 2.0f); - Physics::Point point({1.0f, 3.0f, 3.0f}); - Physics::Point point2({1.0f, 3.0f, 1.0f}); + Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 2.0f); + Physics::Point3D point({1.0f, 3.0f, 3.0f}); + Physics::Point3D point2({1.0f, 3.0f, 1.0f}); randomTransformation(sphere); randomTransformation(point); @@ -63,9 +63,9 @@ void SphereTest::collisionPoint() { } void SphereTest::collisionLine() { - Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 2.0f); - Physics::Line line({1.0f, 1.5f, 3.5f}, {1.0f, 2.5f, 2.5f}); - Physics::Line line2({1.0f, 2.0f, 5.1f}, {1.0f, 3.0f, 5.1f}); + Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 2.0f); + Physics::Line3D line({1.0f, 1.5f, 3.5f}, {1.0f, 2.5f, 2.5f}); + Physics::Line3D line2({1.0f, 2.0f, 5.1f}, {1.0f, 3.0f, 5.1f}); randomTransformation(sphere); randomTransformation(line); @@ -76,9 +76,9 @@ void SphereTest::collisionLine() { } void SphereTest::collisionLineSegment() { - Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 2.0f); - Physics::LineSegment line({1.0f, 2.0f, 4.9f}, {1.0f, 2.0f, 7.0f}); - Physics::LineSegment line2({1.0f, 2.0f, 5.1f}, {1.0f, 2.0f, 7.0f}); + Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 2.0f); + Physics::LineSegment3D line({1.0f, 2.0f, 4.9f}, {1.0f, 2.0f, 7.0f}); + Physics::LineSegment3D line2({1.0f, 2.0f, 5.1f}, {1.0f, 2.0f, 7.0f}); randomTransformation(sphere); randomTransformation(line); @@ -89,9 +89,9 @@ void SphereTest::collisionLineSegment() { } void SphereTest::collisionSphere() { - Physics::Sphere sphere({1.0f, 2.0f, 3.0f}, 2.0f); - Physics::Sphere sphere1({1.0f, 3.0f, 5.0f}, 1.0f); - Physics::Sphere sphere2({1.0f, 3.0f, 0.0f}, 1.0f); + Physics::Sphere3D sphere({1.0f, 2.0f, 3.0f}, 2.0f); + Physics::Sphere3D sphere1({1.0f, 3.0f, 5.0f}, 1.0f); + Physics::Sphere3D sphere2({1.0f, 3.0f, 0.0f}, 1.0f); randomTransformation(sphere); randomTransformation(sphere1);