From 8deb22061d7ccbf44f25a7ac99d0ccf7e9b52244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 19 Apr 2013 23:44:49 +0200 Subject: [PATCH] Physics shape rework #2: new implementation of ShapeGroup. This class now stores the tree in flat array, making it easier for user to query the contents, but the internals are much more complicated. This solution already reduces allocation count by count of nodes in the tree, future work might remove the per-shape allocation altogether by using large typeless array and placement-new etc. --- src/Physics/CMakeLists.txt | 9 +- .../Implementation/CollisionDispatch.cpp | 102 +++++ .../Implementation/CollisionDispatch.h | 46 +++ src/Physics/ShapeGroup.cpp | 150 ++++++-- src/Physics/ShapeGroup.h | 361 ++++++++++-------- src/Physics/Test/CMakeLists.txt | 2 +- src/Physics/Test/ShapeGroupTest.cpp | 98 +++-- ...peTest.cpp => ShapeImplementationTest.cpp} | 22 +- src/Physics/magnumPhysicsVisibility.h | 1 + src/Physics/shapeImplementation.cpp | 72 ++++ src/Physics/shapeImplementation.h | 155 ++++++++ 11 files changed, 796 insertions(+), 222 deletions(-) create mode 100644 src/Physics/Implementation/CollisionDispatch.cpp create mode 100644 src/Physics/Implementation/CollisionDispatch.h rename src/Physics/Test/{AbstractShapeTest.cpp => ShapeImplementationTest.cpp} (68%) create mode 100644 src/Physics/shapeImplementation.cpp create mode 100644 src/Physics/shapeImplementation.h 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