diff --git a/src/Physics/CMakeLists.txt b/src/Physics/CMakeLists.txt index ef1d51a51..c36e10df6 100644 --- a/src/Physics/CMakeLists.txt +++ b/src/Physics/CMakeLists.txt @@ -33,7 +33,11 @@ set(MagnumPhysics_SRCS ObjectShape.cpp ObjectShapeGroup.cpp ShapeGroup.cpp - Sphere.cpp) + Sphere.cpp + + shapeImplementation.cpp + + Implementation/CollisionDispatch.cpp) set(MagnumPhysics_HEADERS AbstractShape.h @@ -50,7 +54,8 @@ set(MagnumPhysics_HEADERS ShapeGroup.h Sphere.h - magnumPhysicsVisibility.h) + magnumPhysicsVisibility.h + shapeImplementation.h) add_library(MagnumPhysics ${SHARED_OR_STATIC} ${MagnumPhysics_SRCS}) if(BUILD_STATIC_PIC) diff --git a/src/Physics/Implementation/CollisionDispatch.cpp b/src/Physics/Implementation/CollisionDispatch.cpp new file mode 100644 index 000000000..7377ab6c5 --- /dev/null +++ b/src/Physics/Implementation/CollisionDispatch.cpp @@ -0,0 +1,102 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "CollisionDispatch.h" + +#include "Physics/AxisAlignedBox.h" +#include "Physics/Box.h" +#include "Physics/Capsule.h" +#include "Physics/LineSegment.h" +#include "Physics/Plane.h" +#include "Physics/Point.h" +#include "Physics/Sphere.h" +#include "Physics/shapeImplementation.h" + +namespace Magnum { namespace Physics { namespace Implementation { + +namespace { + inline constexpr UnsignedInt operator*(ShapeDimensionTraits<2>::Type a, ShapeDimensionTraits<2>::Type b) { + return UnsignedInt(a)*UnsignedInt(b); + } + inline constexpr UnsignedInt operator*(ShapeDimensionTraits<3>::Type a, ShapeDimensionTraits<3>::Type b) { + return UnsignedInt(a)*UnsignedInt(b); + } +} + +template<> bool collides(const AbstractShape<2>* const a, const AbstractShape<2>* const b) { + if(a->type() < b->type()) return collides(b, a); + + switch(a->type()*b->type()) { + case ShapeDimensionTraits<2>::Type::Sphere*ShapeDimensionTraits<2>::Type::Point: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<2>::Type::Sphere*ShapeDimensionTraits<2>::Type::Line: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<2>::Type::Sphere*ShapeDimensionTraits<2>::Type::LineSegment: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<2>::Type::Sphere*ShapeDimensionTraits<2>::Type::Sphere: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + + case ShapeDimensionTraits<2>::Type::Capsule*ShapeDimensionTraits<2>::Type::Point: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<2>::Type::Capsule*ShapeDimensionTraits<2>::Type::Sphere: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + + case ShapeDimensionTraits<2>::Type::AxisAlignedBox*ShapeDimensionTraits<2>::Type::Point: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + } + + return false; +} + +template<> bool collides(const AbstractShape<3>* const a, const AbstractShape<3>* const b) { + if(a->type() < b->type()) return collides(b, a); + + switch(a->type()*b->type()) { + case ShapeDimensionTraits<3>::Type::Sphere*ShapeDimensionTraits<3>::Type::Point: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<3>::Type::Sphere*ShapeDimensionTraits<3>::Type::Line: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<3>::Type::Sphere*ShapeDimensionTraits<3>::Type::LineSegment: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<3>::Type::Sphere*ShapeDimensionTraits<3>::Type::Sphere: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + + case ShapeDimensionTraits<3>::Type::Capsule*ShapeDimensionTraits<3>::Type::Point: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<3>::Type::Capsule*ShapeDimensionTraits<3>::Type::Sphere: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + + case ShapeDimensionTraits<3>::Type::AxisAlignedBox*ShapeDimensionTraits<3>::Type::Point: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + + case ShapeDimensionTraits<3>::Type::Plane*ShapeDimensionTraits<3>::Type::Line: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + case ShapeDimensionTraits<3>::Type::Plane*ShapeDimensionTraits<3>::Type::LineSegment: + return static_cast*>(a)->shape % static_cast*>(b)->shape; + } + + return false; +} + +}}} diff --git a/src/Physics/Implementation/CollisionDispatch.h b/src/Physics/Implementation/CollisionDispatch.h new file mode 100644 index 000000000..53b51ef46 --- /dev/null +++ b/src/Physics/Implementation/CollisionDispatch.h @@ -0,0 +1,46 @@ +#ifndef Magnum_Physics_Implementation_CollisionDispatch_h +#define Magnum_Physics_Implementation_CollisionDispatch_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Types.h" + +namespace Magnum { namespace Physics { namespace Implementation { + +template struct AbstractShape; + +/* +Shape collision double-dispatch: + +The collision is symmetric, i.e. it doesn't matter if we test Point vs. Sphere +or Sphere vs. Point. Each type is specified by unique prime number. Then we +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); + +}}} + +#endif diff --git a/src/Physics/ShapeGroup.cpp b/src/Physics/ShapeGroup.cpp index 5d6777c4b..ca939a9d6 100644 --- a/src/Physics/ShapeGroup.cpp +++ b/src/Physics/ShapeGroup.cpp @@ -24,52 +24,144 @@ #include "ShapeGroup.h" +#include +#include + +#include "Physics/Implementation/CollisionDispatch.h" + namespace Magnum { namespace Physics { -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; +/* +Hierarchy implementation notes: + +The hierarchy is stored in flat array to provide easy access for the user and +to save some allocations. Each node has zero, one or two subnodes. Value of +`Node::rightNode` describes which child nodes exist: + + * 0 - no child subnodes + * 1 - only left subnode exists + * 2 - only right subnode exists + * >2 - both child nodes exist + +If left node exists, it is right next to current one. If right node exists, it +is at position `Node::rightNode-1` relative to current one (this applies also +when `rightNode` is equal to 2, right node is right next to current one, +because there are no left nodes). + +The node also specifies which shapes belong to it. Root node owns whole shape +array and `Node::rightShape` marks first shape belonging to the right child +node, relatively to begin. This recurses into child nodes, thus left child node +has shapes from parent's begin to parent's `rightShape`. + +Shapes are merged together by concatenating its node and shape list and adding +new node at the beginning with properly set `rightNode` and `rightShape`. +Because these values are relative to parent, they don't need to be modified +when concatenating. +*/ + +template ShapeGroup::ShapeGroup(const ShapeGroup& other): _shapeCount(other._shapeCount), _nodeCount(other._nodeCount) { + copyShapes(0, other); + copyNodes(0, other); +} + +template ShapeGroup::ShapeGroup(ShapeGroup&& other): _shapeCount(other._shapeCount), _nodeCount(other._nodeCount), _shapes(other._shapes), _nodes(other._nodes) { + other._shapes = nullptr; + other._shapeCount = 0; + + other._nodes = nullptr; + other._nodeCount = 0; } template ShapeGroup::~ShapeGroup() { - if(!(operation & Implementation::GroupOperation::RefA)) delete a; - if(!(operation & Implementation::GroupOperation::RefB)) delete b; + for(std::size_t i = 0; i != _shapeCount; ++i) + delete _shapes[i]; + + delete[] _shapes; + delete[] _nodes; } -template ShapeGroup& ShapeGroup::operator=(ShapeGroup&& other) { - if(!(operation & Implementation::GroupOperation::RefA)) delete a; - if(!(operation & Implementation::GroupOperation::RefB)) delete b; +template ShapeGroup& ShapeGroup::operator=(const ShapeGroup& other) { + for(std::size_t i = 0; i != _shapeCount; ++i) + delete _shapes[i]; - operation = other.operation; - a = other.a; - b = other.b; + if(_shapeCount != other._shapeCount) { + delete[] _shapes; + _shapeCount = other._shapeCount; + _shapes = new Implementation::AbstractShape*[_shapeCount]; + } - other.operation = Implementation::GroupOperation::AlwaysFalse; - other.a = nullptr; - other.b = nullptr; + if(_nodeCount != other._nodeCount) { + delete[] _nodes; + _nodeCount = other._nodeCount; + _nodes = new Node[_nodeCount]; + } + copyShapes(0, other); + copyNodes(0, other); return *this; } -template void ShapeGroup::applyTransformationMatrix(const typename DimensionTraits::MatrixType& matrix) { - if(a) a->applyTransformationMatrix(matrix); - if(b) b->applyTransformationMatrix(matrix); +template ShapeGroup& ShapeGroup::operator=(ShapeGroup&& other) { + std::swap(other._shapeCount, _shapeCount); + std::swap(other._nodeCount, _nodeCount); + std::swap(other._shapes, _shapes); + std::swap(other._nodes, _nodes); + return *this; } -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); +template void ShapeGroup::copyShapes(const std::size_t offset, ShapeGroup&& other) { + std::move(other._shapes, other._shapes+other._shapeCount, _shapes+offset); + delete[] other._shapes; + other._shapes = nullptr; + other._shapeCount = 0; +} - default: - return false; - } +template void ShapeGroup::copyShapes(const std::size_t offset, const ShapeGroup& other) { + for(std::size_t i = 0; i != other._shapeCount; ++i) + _shapes[i+offset] = other._shapes[i]->clone(); +} + +template void ShapeGroup::copyNodes(std::size_t offset, const ShapeGroup& other) { + std::copy(other._nodes, other._nodes+other._nodeCount, _nodes+offset); +} + +template ShapeGroup ShapeGroup::transformed(const typename DimensionTraits::MatrixType& matrix) const { + ShapeGroup out(*this); + for(std::size_t i = 0; i != _shapeCount; ++i) + _shapes[i]->transform(matrix, out._shapes[i]); + return out; +} + +template bool ShapeGroup::collides(const Implementation::AbstractShape* const a, const std::size_t node, const std::size_t shapeBegin, const std::size_t shapeEnd) const { + /* Empty group */ + if(shapeBegin == shapeEnd) return false; + + CORRADE_INTERNAL_ASSERT(node < _nodeCount && shapeBegin < shapeEnd); + + /* Collision on the left child. If the node is leaf one (no left child + exists), do it directly, recurse instead. */ + const bool collidesLeft = (_nodes[node].rightNode == 0 || _nodes[node].rightNode == 2) ? + Implementation::collides(a, _shapes[shapeBegin]) : + collides(a, node+1, shapeBegin, shapeBegin+_nodes[node].rightShape); + + /* NOT operation */ + if(_nodes[node].operation == ShapeOperation::Not) + return !collidesLeft; + + /* Short-circuit evaluation for AND/OR */ + if((_nodes[node].operation == ShapeOperation::Or) == collidesLeft) + return collidesLeft; + + /* Now the collision result depends only on the right child. Similar to + collision on the left child. */ + return (_nodes[node].rightNode < 2) ? + Implementation::collides(a, _shapes[shapeBegin+_nodes[node].rightShape]) : + collides(a, node+_nodes[node].rightNode-1, shapeBegin+_nodes[node].rightShape, shapeEnd); } -template class ShapeGroup<2>; -template class ShapeGroup<3>; +#ifndef DOXYGEN_GENERATING_OUTPUT +template class MAGNUM_PHYSICS_EXPORT ShapeGroup<2>; +template class MAGNUM_PHYSICS_EXPORT ShapeGroup<3>; +#endif }} diff --git a/src/Physics/ShapeGroup.h b/src/Physics/ShapeGroup.h index b546d60ba..f41a03b85 100644 --- a/src/Physics/ShapeGroup.h +++ b/src/Physics/ShapeGroup.h @@ -25,168 +25,195 @@ */ /** @file - * @brief Class Magnum::Physics::ShapeGroup, typedef Magnum::Physics::ShapeGroup2D, Magnum::Physics::ShapeGroup3D + * @brief Class Magnum::Physics::ShapeGroup, enum Magnum::Physics::ShapeOperation */ -#include "AbstractShape.h" - -#include #include #include +#include -#include "corradeCompatibility.h" +#include "DimensionTraits.h" +#include "Physics/Physics.h" +#include "Physics/magnumPhysicsVisibility.h" +#include "Physics/shapeImplementation.h" namespace Magnum { namespace Physics { -#ifndef DOXYGEN_GENERATING_OUTPUT 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 - }; + template struct ObjectShapeHelper; + + template inline AbstractShape* getAbstractShape(ShapeGroup& group, std::size_t i) { + return group._shapes[i]; + } + template inline const AbstractShape* getAbstractShape(const ShapeGroup& group, std::size_t i) { + return group._shapes[i]; + } } -#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 + +/** @brief Shape operation */ +enum class ShapeOperation: UnsignedByte { + Not, /**< Boolean NOT */ + And, /**< Boolean AND */ + Or /**< Boolean OR */ +}; /** @brief Shape group Result of logical operations on shapes. See @ref collision-detection for brief introduction. -@see ShapeGroup2D, ShapeGroup3D */ -template class MAGNUM_PHYSICS_EXPORT ShapeGroup: public AbstractShape { - #ifndef DOXYGEN_GENERATING_OUTPUT -// 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 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(-) -// friendOp(^) - friendOp(&&) - friendOp(||) - #undef friendOp - #endif - - ShapeGroup(const ShapeGroup&) = delete; - ShapeGroup& operator=(const ShapeGroup&) = delete; +template class MAGNUM_PHYSICS_EXPORT ShapeGroup { + friend Implementation::AbstractShape* Implementation::getAbstractShape<>(ShapeGroup&, std::size_t); + friend const Implementation::AbstractShape* Implementation::getAbstractShape<>(const ShapeGroup&, std::size_t); + friend struct Implementation::ObjectShapeHelper>; public: - /** @brief Default constructor */ - inline explicit ShapeGroup(): operation(Implementation::GroupOperation::AlwaysFalse), a(nullptr), b(nullptr) {} + enum: UnsignedInt { + Dimensions = dimensions /**< Dimension count */ + }; + + /** @brief Shape type */ + #ifdef DOXYGEN_GENERATING_OUTPUT + enum class Type { + Point, /**< Point */ + Line, /**< Line */ + LineSegment, /**< @ref LineSegment "Line segment" */ + Sphere, /**< Sphere */ + Capsule, /**< Capsule */ + AxisAlignedBox, /**< @ref AxisAlignedBox "Axis aligned box" */ + Box, /**< Box */ + Plane /**< Plane (3D only) */ + }; + #else + typedef typename Implementation::ShapeDimensionTraits::Type Type; + #endif + + /** + * @brief Default constructor + * + * Creates empty hierarchy. + */ + inline explicit ShapeGroup(): _shapeCount(0), _nodeCount(0), _shapes(nullptr), _nodes(nullptr) {} + + /** + * @brief Unary operation constructor + * @param operation Unary operation + * @param a Operand + */ + template explicit ShapeGroup(ShapeOperation operation, T&& a); + + /** + * @brief Binary operation constructor + * @param operation Binary operation + * @param a Left operand + * @param b Right operand + */ + template explicit ShapeGroup(ShapeOperation operation, T&& a, U&& b); + + /** @brief Copy constructor */ + ShapeGroup(const ShapeGroup& other); /** @brief Move constructor */ - ShapeGroup(ShapeGroup&& other); + ShapeGroup(ShapeGroup&& other); - /** @brief Destructor */ ~ShapeGroup(); - /** @brief Move assignment */ - ShapeGroup& operator=(ShapeGroup&& other); + /** @brief Assigment operator */ + ShapeGroup& operator=(const ShapeGroup& other); - inline typename AbstractShape::Type type() const override { - return AbstractShape::Type::ShapeGroup; - } + /** @brief Move assignment operator */ + ShapeGroup& operator=(ShapeGroup&& other); - void applyTransformationMatrix(const typename DimensionTraits::MatrixType& matrix) override; + /** @brief Transformed shape */ + ShapeGroup transformed(const typename DimensionTraits::MatrixType& matrix) const; - bool collides(const AbstractShape* other) const override; + /** @brief Count of shapes in the hierarchy */ + inline std::size_t size() const { return _shapeCount; } - /** - * @brief First object in the group - * - * If there is no such object, returns `nullptr`. - */ - inline AbstractShape* first() { return a; } + /** @brief Type of shape at given position */ + inline Type type(std::size_t i) const { return _shapes[i]->type(); } - /** - * @brief Second object in the group - * - * If there is no such object, returns `nullptr`. - */ - inline AbstractShape* second() { return b; } + /** @brief Shape at given position */ + template const T& get(std::size_t i) const; + + /** @brief Collision with another shape */ + #ifdef DOXYGEN_GENERATING_OUTPUT + template inline bool operator%(const T& other) const { + #else + template inline auto operator%(const T& other) const -> typename std::enable_if::type()), typename Implementation::ShapeDimensionTraits::Type>::value, bool>::type { + #endif + Implementation::Shape a(other); + return collides(&a); + } private: - inline ShapeGroup(int operation, AbstractShape* a, AbstractShape* b): operation(operation), a(a), b(b) {} + struct Node { + std::size_t rightNode, rightShape; + ShapeOperation operation; + }; + + inline bool collides(const Implementation::AbstractShape* a) const { + return collides(a, 0, 0, _shapeCount); + } + + bool collides(const Implementation::AbstractShape* a, std::size_t node, std::size_t shapeBegin, std::size_t shapeEnd) const; + + template inline constexpr static std::size_t shapeCount(const T&) { + return 1; + } + inline constexpr static std::size_t shapeCount(const ShapeGroup& hierarchy) { + return hierarchy._shapeCount; + } + template inline constexpr static std::size_t nodeCount(const T&) { + return 0; + } + inline constexpr static std::size_t nodeCount(const ShapeGroup& hierarchy) { + return hierarchy._nodeCount; + } + + template inline void copyShapes(std::size_t offset, const T& shape) { + _shapes[offset] = new Implementation::Shape(shape); + } + void copyShapes(std::size_t offset, ShapeGroup&& other); + void copyShapes(std::size_t offset, const ShapeGroup& other); - int operation; - AbstractShape* a; - AbstractShape* b; + template inline void copyNodes(std::size_t, const T&) {} + void copyNodes(std::size_t offset, const ShapeGroup& other); + + std::size_t _shapeCount, _nodeCount; + Implementation::AbstractShape** _shapes; + Node* _nodes; }; -/** @brief Two-dimensional shape group */ +/** @brief Two-dimensional shape hierarchy */ typedef ShapeGroup<2> ShapeGroup2D; -/** @brief Three-dimensional shape group */ +/** @brief Three-dimensional shape hierarchy */ typedef ShapeGroup<3> ShapeGroup3D; -// /* @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 +#ifdef DOXYGEN_GENERATING_OUTPUT +/** @debugoperator{Magnum::Physics::ShapeGroup} */ +template Debug operator<<(Debug debug, typename ShapeGroup::Type value); +#endif /** @relates ShapeGroup -@brief Logical NOT of shape +@brief Collision of shape with ShapeGroup */ -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 auto operator!(T&& a) -> enableIfIsBaseType { - return ShapeGroup(Implementation::GroupOperation::Not, new T(std::forward(a)), nullptr); -} -template inline constexpr auto operator!(T& a) -> enableIfIsBaseType { - return ShapeGroup(Implementation::GroupOperation::Not|Implementation::GroupOperation::RefA, &a.get(), nullptr); -} +#ifdef DOXYGEN_GENERATING_OUTPUT +template inline bool operator%(const T& a, const ShapeGroup& b) { +#else +template inline auto operator%(const T& a, const ShapeGroup& b) -> typename std::enable_if::type()), typename Implementation::ShapeDimensionTraits::Type>::value, bool>::type { #endif + return b % a; +} #ifdef DOXYGEN_GENERATING_OUTPUT -// /* @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 NOT of shape +*/ +template inline ShapeGroup operator!(T a); + /** @relates ShapeGroup @brief Logical AND of two shapes @@ -195,7 +222,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 ShapeGroup operator&&(T a, T b); /** @relates ShapeGroup @brief Logical OR of two shapes @@ -204,47 +231,67 @@ template inline constexpr ShapeGroup inline constexpr ShapeGroup operator||(T a, U b); -#else -#define op(type, char) \ -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 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 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 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 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 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 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 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 auto operator char(std::reference_wrapper a, std::reference_wrapper b) -> enableIfAreBaseType { \ - return ShapeGroup(Implementation::GroupOperation::type|Implementation::GroupOperation::RefAB, &a.get(), &b.get()); \ +template inline ShapeGroup operator||(T a, T b); +#endif + +#ifndef DOXYGEN_GENERATING_OUTPUT +#define enableIfIsShapeType typename std::enable_if< \ + std::is_same::type()), typename Implementation::ShapeDimensionTraits::Type>::value, \ + ShapeGroup>::type +#define enableIfAreShapeType typename std::enable_if< \ + std::is_same::type()), typename Implementation::ShapeDimensionTraits::Type>::value && \ + std::is_same::type()), typename Implementation::ShapeDimensionTraits::Type>::value, \ + ShapeGroup>::type +template inline auto operator!(T&& a) -> enableIfIsShapeType { + return ShapeGroup(ShapeOperation::Not, std::forward(a)); +} +template inline auto operator&&(T&& a, U&& b) -> enableIfAreShapeType { + return ShapeGroup(ShapeOperation::And, std::forward(a), std::forward(b)); } -// op(Union, |) -// op(Intersection, &) -// op(Difference, -) -// op(Xor, ^) -op(And, &&) -op(Or, ||) -#undef op +template inline auto operator||(T&& a, U&& b) -> enableIfAreShapeType { + return ShapeGroup(ShapeOperation::Or, std::forward(a), std::forward(b)); +} +#undef enableIfIsShapeType +#undef enableIfAreShapeType #endif -#undef enableIfIsBaseType -#undef enableIfAreBaseType +template template ShapeGroup::ShapeGroup(ShapeOperation operation, T&& a): _shapeCount(shapeCount(a)), _nodeCount(nodeCount(a)+1), _shapes(new Implementation::AbstractShape*[_shapeCount]), _nodes(new Node[_nodeCount]) { + CORRADE_ASSERT(operation == ShapeOperation::Not, + "Physics::ShapeGroup::ShapeGroup(): unary operation expected", ); + _nodes[0].operation = operation; + + /* 0 = no children, 1 = left child only */ + _nodes[0].rightNode = (nodeCount(a) == 0 ? 0 : 1); + _nodes[0].rightShape = shapeCount(a); + copyNodes(1, a); + copyShapes(0, std::forward(a)); +} + +template template ShapeGroup::ShapeGroup(ShapeOperation operation, T&& a, U&& b): _shapeCount(shapeCount(a) + shapeCount(b)), _nodeCount(nodeCount(a) + nodeCount(b) + 1), _shapes(new Implementation::AbstractShape*[_shapeCount]), _nodes(new Node[_nodeCount]) { + CORRADE_ASSERT(operation != ShapeOperation::Not, + "Physics::ShapeGroup::ShapeGroup(): binary operation expected", ); + _nodes[0].operation = operation; + + /* 0 = no children, 1 = left child only, 2 = right child only, >2 = both */ + if(nodeCount(a) == 0 && nodeCount(b) == 0) + _nodes[0].rightNode = 0; + else if(nodeCount(b) == 0) + _nodes[0].rightNode = 1; + else _nodes[0].rightNode = nodeCount(a) + 2; + + _nodes[0].rightShape = shapeCount(a); + copyNodes(1, a); + copyNodes(nodeCount(a) + 1, b); + copyShapes(shapeCount(a), std::forward(b)); + copyShapes(0, std::forward(a)); +} + +template template inline const T& ShapeGroup::get(std::size_t i) const { + CORRADE_ASSERT(_shapes[i]->type() == Implementation::TypeOf::type(), + "Physics::ShapeGroup::get(): given shape is not of type" << Implementation::TypeOf::type(), + *static_cast(nullptr)); + return static_cast*>(_shapes[i])->shape; +} }} diff --git a/src/Physics/Test/CMakeLists.txt b/src/Physics/Test/CMakeLists.txt index 2467787cc..659b3761c 100644 --- a/src/Physics/Test/CMakeLists.txt +++ b/src/Physics/Test/CMakeLists.txt @@ -22,7 +22,7 @@ # DEALINGS IN THE SOFTWARE. # -corrade_add_test(PhysicsAbstractShapeTest AbstractShapeTest.cpp LIBRARIES MagnumPhysics) +corrade_add_test(PhysicsShapeImplementationTest ShapeImplementationTest.cpp LIBRARIES MagnumPhysics) corrade_add_test(PhysicsAxisAlignedBoxTest AxisAlignedBoxTest.cpp LIBRARIES MagnumPhysics) corrade_add_test(PhysicsBoxTest BoxTest.cpp LIBRARIES MagnumPhysics) corrade_add_test(PhysicsCapsuleTest CapsuleTest.cpp LIBRARIES MagnumPhysics) diff --git a/src/Physics/Test/ShapeGroupTest.cpp b/src/Physics/Test/ShapeGroupTest.cpp index 6650405ca..b0ac44194 100644 --- a/src/Physics/Test/ShapeGroupTest.cpp +++ b/src/Physics/Test/ShapeGroupTest.cpp @@ -26,8 +26,11 @@ #include "Math/Matrix4.h" #include "Physics/Point.h" -#include "Physics/LineSegment.h" +#include "Physics/AxisAlignedBox.h" #include "Physics/ShapeGroup.h" +#include "Physics/Sphere.h" + +#include "ShapeTestBase.h" namespace Magnum { namespace Physics { namespace Test { @@ -35,40 +38,91 @@ class ShapeGroupTest: public Corrade::TestSuite::Tester { public: ShapeGroupTest(); - void copy(); - void reference(); + void negated(); + void anded(); + void ored(); + void multipleUnary(); + void hierarchy(); + void empty(); }; ShapeGroupTest::ShapeGroupTest() { - addTests({&ShapeGroupTest::copy, - &ShapeGroupTest::reference}); + addTests({&ShapeGroupTest::negated, + &ShapeGroupTest::anded, + &ShapeGroupTest::ored, + &ShapeGroupTest::multipleUnary, + &ShapeGroupTest::hierarchy, + &ShapeGroupTest::empty}); +} + +void ShapeGroupTest::negated() { + const Physics::ShapeGroup2D a = !Physics::Point2D(Vector2::xAxis(0.5f)); + + CORRADE_COMPARE(a.size(), 1); + CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Point); + CORRADE_COMPARE(a.get(0).position(), Vector2::xAxis(0.5f)); + + VERIFY_NOT_COLLIDES(a, Physics::Sphere2D({}, 1.0f)); } -void ShapeGroupTest::copy() { - ShapeGroup3D group; - { - Physics::Point3D point({1.0f, 2.0f, 3.0f}); - Physics::LineSegment3D segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); +void ShapeGroupTest::anded() { + const Physics::ShapeGroup2D a = Physics::Sphere2D({}, 1.0f) && Physics::Point2D(Vector2::xAxis(0.5f)); + + CORRADE_COMPARE(a.size(), 2); + CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Sphere); + CORRADE_COMPARE(a.type(1), ShapeGroup2D::Type::Point); + CORRADE_COMPARE(a.get(0).position(), Vector2()); + CORRADE_COMPARE(a.get(0).radius(), 1.0f); + CORRADE_COMPARE(a.get(1).position(), Vector2::xAxis(0.5f)); + + VERIFY_NOT_COLLIDES(a, Physics::Point2D()); + VERIFY_COLLIDES(a, Physics::Sphere2D(Vector2::xAxis(0.5f), 0.25f)); +} - group = !(point || segment); - } +void ShapeGroupTest::ored() { + const Physics::ShapeGroup2D a = Physics::Sphere2D({}, 1.0f) || Physics::Point2D(Vector2::xAxis(1.5f)); - /* Just to test that it doesn't crash */ - group.applyTransformationMatrix(Matrix4::translation(Vector3::xAxis(1.0f))); + CORRADE_COMPARE(a.size(), 2); + CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Sphere); + CORRADE_COMPARE(a.type(1), ShapeGroup2D::Type::Point); + CORRADE_COMPARE(a.get(0).position(), Vector2()); + CORRADE_COMPARE(a.get(0).radius(), 1.0f); + CORRADE_COMPARE(a.get(1).position(), Vector2::xAxis(1.5f)); - CORRADE_VERIFY(true); + VERIFY_COLLIDES(a, Physics::Point2D()); + VERIFY_COLLIDES(a, Physics::Sphere2D(Vector2::xAxis(1.5f), 0.25f)); } -void ShapeGroupTest::reference() { - Physics::Point3D point({1.0f, 2.0f, 3.0f}); - Physics::LineSegment3D segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); +void ShapeGroupTest::multipleUnary() { + const Physics::ShapeGroup2D a = !!!!Physics::Point2D(Vector2::xAxis(0.5f)); + + CORRADE_COMPARE(a.size(), 1); + CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Point); + CORRADE_COMPARE(a.get(0).position(), Vector2::xAxis(0.5f)); + + VERIFY_COLLIDES(a, Physics::Sphere2D({}, 1.0f)); +} + +void ShapeGroupTest::hierarchy() { + const Physics::ShapeGroup3D a = Physics::Sphere3D({}, 1.0f) && + (Physics::Point3D(Vector3::xAxis(1.5f)) || !Physics::AxisAlignedBox3D({}, Vector3(0.5f))); + + CORRADE_COMPARE(a.size(), 3); + CORRADE_COMPARE(a.type(0), ShapeGroup3D::Type::Sphere); + CORRADE_COMPARE(a.type(1), ShapeGroup3D::Type::Point); + CORRADE_COMPARE(a.type(2), ShapeGroup3D::Type::AxisAlignedBox); + CORRADE_COMPARE(a.get(1).position(), Vector3::xAxis(1.5f)); + + VERIFY_COLLIDES(a, Physics::Sphere3D(Vector3::xAxis(1.5f), 0.6f)); + VERIFY_NOT_COLLIDES(a, Physics::Point3D(Vector3(0.25f))); +} - ShapeGroup3D group = !(std::ref(point) || std::ref(segment)); +void ShapeGroupTest::empty() { + const Physics::ShapeGroup2D a; - group.applyTransformationMatrix(Matrix4::translation(Vector3(1.0f))); + CORRADE_COMPARE(a.size(), 0); - CORRADE_VERIFY((point.transformedPosition() == Vector3(2.0f, 3.0f, 4.0f))); - CORRADE_VERIFY((segment.transformedA() == Vector3(3.0f, 2.0f, 31.0f))); + VERIFY_NOT_COLLIDES(a, Physics::Sphere2D({}, 1.0f)); } }}} diff --git a/src/Physics/Test/AbstractShapeTest.cpp b/src/Physics/Test/ShapeImplementationTest.cpp similarity index 68% rename from src/Physics/Test/AbstractShapeTest.cpp rename to src/Physics/Test/ShapeImplementationTest.cpp index 0b451e78d..1e7e690ec 100644 --- a/src/Physics/Test/AbstractShapeTest.cpp +++ b/src/Physics/Test/ShapeImplementationTest.cpp @@ -25,31 +25,31 @@ #include #include -#include "Physics/AbstractShape.h" +#include "Physics/shapeImplementation.h" namespace Magnum { namespace Physics { namespace Test { -class AbstractShapeTest: public Corrade::TestSuite::Tester { +class ShapeImplementationTest: public Corrade::TestSuite::Tester { public: - AbstractShapeTest(); + ShapeImplementationTest(); void debug(); }; -AbstractShapeTest::AbstractShapeTest() { - addTests({&AbstractShapeTest::debug}); +ShapeImplementationTest::ShapeImplementationTest() { + addTests({&ShapeImplementationTest::debug}); } -void AbstractShapeTest::debug() { +void ShapeImplementationTest::debug() { std::ostringstream o; - Debug(&o) << AbstractShape2D::Type::ShapeGroup; - CORRADE_COMPARE(o.str(), "AbstractShape2D::Type::ShapeGroup\n"); + Debug(&o) << Implementation::ShapeDimensionTraits<2>::Type::ShapeGroup; + CORRADE_COMPARE(o.str(), "Physics::Shape2D::Type::ShapeGroup\n"); o.str({}); - Debug(&o) << AbstractShape3D::Type::Plane; - CORRADE_COMPARE(o.str(), "AbstractShape3D::Type::Plane\n"); + Debug(&o) << Implementation::ShapeDimensionTraits<3>::Type::Plane; + CORRADE_COMPARE(o.str(), "Physics::Shape3D::Type::Plane\n"); } }}} -CORRADE_TEST_MAIN(Magnum::Physics::Test::AbstractShapeTest) +CORRADE_TEST_MAIN(Magnum::Physics::Test::ShapeImplementationTest) diff --git a/src/Physics/magnumPhysicsVisibility.h b/src/Physics/magnumPhysicsVisibility.h index 6ee2b89c0..e7d67f908 100644 --- a/src/Physics/magnumPhysicsVisibility.h +++ b/src/Physics/magnumPhysicsVisibility.h @@ -31,5 +31,6 @@ #else #define MAGNUM_PHYSICS_EXPORT CORRADE_VISIBILITY_IMPORT #endif +#define MAGNUM_PHYSICS_LOCAL CORRADE_VISIBILITY_LOCAL #endif diff --git a/src/Physics/shapeImplementation.cpp b/src/Physics/shapeImplementation.cpp new file mode 100644 index 000000000..1d777a2a2 --- /dev/null +++ b/src/Physics/shapeImplementation.cpp @@ -0,0 +1,72 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "shapeImplementation.h" + +#include + +namespace Magnum { namespace Physics { namespace Implementation { + +Debug operator<<(Debug debug, ShapeDimensionTraits<2>::Type value) { + switch(value) { + #define _val(value) case ShapeDimensionTraits<2>::Type::value: return debug << "Physics::Shape2D::Type::" #value; + _val(Point) + _val(Line) + _val(LineSegment) + _val(Sphere) + _val(Capsule) + _val(AxisAlignedBox) + _val(Box) + _val(ShapeGroup) + #undef _val + } + + return debug << "Physics::Shape2D::Type::(unknown)"; +} + +Debug operator<<(Debug debug, ShapeDimensionTraits<3>::Type value) { + switch(value) { + #define _val(value) case ShapeDimensionTraits<3>::Type::value: return debug << "Physics::Shape3D::Type::" #value; + _val(Point) + _val(Line) + _val(LineSegment) + _val(Sphere) + _val(Capsule) + _val(AxisAlignedBox) + _val(Box) + _val(Plane) + _val(ShapeGroup) + #undef _val + } + + return debug << "Physics::Shape3D::Type::(unknown)"; +} + +template AbstractShape::~AbstractShape() = default; +template AbstractShape::AbstractShape() = default; + +template struct AbstractShape<2>; +template struct AbstractShape<3>; + +}}} diff --git a/src/Physics/shapeImplementation.h b/src/Physics/shapeImplementation.h new file mode 100644 index 000000000..320649c42 --- /dev/null +++ b/src/Physics/shapeImplementation.h @@ -0,0 +1,155 @@ +#ifndef Magnum_Physics_shapeImplementation_h +#define Magnum_Physics_shapeImplementation_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include "DimensionTraits.h" +#include "Magnum.h" +#include "Physics/Physics.h" +#include "Physics/magnumPhysicsVisibility.h" + +namespace Magnum { namespace Physics { namespace Implementation { + +/* Shape type for given dimension count */ + +template struct ShapeDimensionTraits; + +template<> struct ShapeDimensionTraits<2> { + enum class Type: UnsignedByte { + Point = 1, + Line = 2, + LineSegment = 3, + Sphere = 5, + Capsule = 7, + AxisAlignedBox = 11, + Box = 13, + ShapeGroup = 17 + }; +}; + +template<> struct ShapeDimensionTraits<3> { + enum class Type: UnsignedByte { + Point = 1, + Line = 2, + LineSegment = 3, + Sphere = 5, + Capsule = 7, + AxisAlignedBox = 11, + Box = 13, + Plane = 17, + ShapeGroup = 19 + }; +}; + +Debug MAGNUM_PHYSICS_EXPORT operator<<(Debug debug, ShapeDimensionTraits<2>::Type value); +Debug MAGNUM_PHYSICS_EXPORT operator<<(Debug debug, ShapeDimensionTraits<3>::Type value); + +/* Enum value corresponding to given type */ + +template struct TypeOf; + +template struct TypeOf> { + inline constexpr static typename ShapeDimensionTraits::Type type() { + return ShapeDimensionTraits::Type::Point; + } +}; +template struct TypeOf> { + inline constexpr static typename ShapeDimensionTraits::Type type() { + return ShapeDimensionTraits::Type::Line; + } +}; +template struct TypeOf> { + inline constexpr static typename ShapeDimensionTraits::Type type() { + return ShapeDimensionTraits::Type::LineSegment; + } +}; +template struct TypeOf> { + inline constexpr static typename ShapeDimensionTraits::Type type() { + return ShapeDimensionTraits::Type::Sphere; + } +}; +template struct TypeOf> { + inline constexpr static typename ShapeDimensionTraits::Type type() { + return ShapeDimensionTraits::Type::Capsule; + } +}; +template struct TypeOf> { + inline constexpr static typename ShapeDimensionTraits::Type type() { + return ShapeDimensionTraits::Type::AxisAlignedBox; + } +}; +template struct TypeOf> { + inline constexpr static typename ShapeDimensionTraits::Type type() { + return ShapeDimensionTraits::Type::Box; + } +}; +template<> struct TypeOf { + inline constexpr static typename ShapeDimensionTraits<3>::Type type() { + return ShapeDimensionTraits<3>::Type::Plane; + } +}; +template struct TypeOf> { + inline constexpr static typename ShapeDimensionTraits::Type type() { + return ShapeDimensionTraits::Type::ShapeGroup; + } +}; + +/* Polymorphic shape wrappers */ + +template struct MAGNUM_PHYSICS_EXPORT AbstractShape { + explicit AbstractShape(); + virtual ~AbstractShape(); + + virtual typename ShapeDimensionTraits::Type MAGNUM_PHYSICS_LOCAL type() const = 0; + virtual AbstractShape MAGNUM_PHYSICS_LOCAL * clone() const = 0; + virtual void MAGNUM_PHYSICS_LOCAL transform(const typename DimensionTraits::MatrixType& matrix, AbstractShape* result) const = 0; +}; + +template struct Shape: AbstractShape { + T shape; + + explicit Shape() = default; + explicit Shape(const T& shape): shape(shape) {} + explicit Shape(T&& shape): shape(std::move(shape)) {} + + typename ShapeDimensionTraits::Type type() const override { + return TypeOf::type(); + } + + AbstractShape* clone() const override { + return new Shape(shape); + } + + void transform(const typename DimensionTraits::MatrixType& matrix, AbstractShape* result) const override { + CORRADE_INTERNAL_ASSERT(result->type() == type()); + static_cast*>(result)->shape = shape.transformed(matrix); + } +}; + +}}} + +#endif