Browse Source

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.
pull/278/head
Vladimír Vondruš 13 years ago
parent
commit
8deb22061d
  1. 9
      src/Physics/CMakeLists.txt
  2. 102
      src/Physics/Implementation/CollisionDispatch.cpp
  3. 46
      src/Physics/Implementation/CollisionDispatch.h
  4. 150
      src/Physics/ShapeGroup.cpp
  5. 361
      src/Physics/ShapeGroup.h
  6. 2
      src/Physics/Test/CMakeLists.txt
  7. 98
      src/Physics/Test/ShapeGroupTest.cpp
  8. 22
      src/Physics/Test/ShapeImplementationTest.cpp
  9. 1
      src/Physics/magnumPhysicsVisibility.h
  10. 72
      src/Physics/shapeImplementation.cpp
  11. 155
      src/Physics/shapeImplementation.h

9
src/Physics/CMakeLists.txt

@ -33,7 +33,11 @@ set(MagnumPhysics_SRCS
ObjectShape.cpp ObjectShape.cpp
ObjectShapeGroup.cpp ObjectShapeGroup.cpp
ShapeGroup.cpp ShapeGroup.cpp
Sphere.cpp) Sphere.cpp
shapeImplementation.cpp
Implementation/CollisionDispatch.cpp)
set(MagnumPhysics_HEADERS set(MagnumPhysics_HEADERS
AbstractShape.h AbstractShape.h
@ -50,7 +54,8 @@ set(MagnumPhysics_HEADERS
ShapeGroup.h ShapeGroup.h
Sphere.h Sphere.h
magnumPhysicsVisibility.h) magnumPhysicsVisibility.h
shapeImplementation.h)
add_library(MagnumPhysics ${SHARED_OR_STATIC} ${MagnumPhysics_SRCS}) add_library(MagnumPhysics ${SHARED_OR_STATIC} ${MagnumPhysics_SRCS})
if(BUILD_STATIC_PIC) if(BUILD_STATIC_PIC)

102
src/Physics/Implementation/CollisionDispatch.cpp

@ -0,0 +1,102 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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<const Shape<Sphere2D>*>(a)->shape % static_cast<const Shape<Point2D>*>(b)->shape;
case ShapeDimensionTraits<2>::Type::Sphere*ShapeDimensionTraits<2>::Type::Line:
return static_cast<const Shape<Sphere2D>*>(a)->shape % static_cast<const Shape<Line2D>*>(b)->shape;
case ShapeDimensionTraits<2>::Type::Sphere*ShapeDimensionTraits<2>::Type::LineSegment:
return static_cast<const Shape<Sphere2D>*>(a)->shape % static_cast<const Shape<LineSegment2D>*>(b)->shape;
case ShapeDimensionTraits<2>::Type::Sphere*ShapeDimensionTraits<2>::Type::Sphere:
return static_cast<const Shape<Sphere2D>*>(a)->shape % static_cast<const Shape<Sphere2D>*>(b)->shape;
case ShapeDimensionTraits<2>::Type::Capsule*ShapeDimensionTraits<2>::Type::Point:
return static_cast<const Shape<Capsule2D>*>(a)->shape % static_cast<const Shape<Point2D>*>(b)->shape;
case ShapeDimensionTraits<2>::Type::Capsule*ShapeDimensionTraits<2>::Type::Sphere:
return static_cast<const Shape<Capsule2D>*>(a)->shape % static_cast<const Shape<Sphere2D>*>(b)->shape;
case ShapeDimensionTraits<2>::Type::AxisAlignedBox*ShapeDimensionTraits<2>::Type::Point:
return static_cast<const Shape<AxisAlignedBox2D>*>(a)->shape % static_cast<const Shape<Point2D>*>(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<const Shape<Sphere3D>*>(a)->shape % static_cast<const Shape<Point3D>*>(b)->shape;
case ShapeDimensionTraits<3>::Type::Sphere*ShapeDimensionTraits<3>::Type::Line:
return static_cast<const Shape<Sphere3D>*>(a)->shape % static_cast<const Shape<Line3D>*>(b)->shape;
case ShapeDimensionTraits<3>::Type::Sphere*ShapeDimensionTraits<3>::Type::LineSegment:
return static_cast<const Shape<Sphere3D>*>(a)->shape % static_cast<const Shape<LineSegment3D>*>(b)->shape;
case ShapeDimensionTraits<3>::Type::Sphere*ShapeDimensionTraits<3>::Type::Sphere:
return static_cast<const Shape<Sphere3D>*>(a)->shape % static_cast<const Shape<Sphere3D>*>(b)->shape;
case ShapeDimensionTraits<3>::Type::Capsule*ShapeDimensionTraits<3>::Type::Point:
return static_cast<const Shape<Capsule3D>*>(a)->shape % static_cast<const Shape<Point3D>*>(b)->shape;
case ShapeDimensionTraits<3>::Type::Capsule*ShapeDimensionTraits<3>::Type::Sphere:
return static_cast<const Shape<Capsule3D>*>(a)->shape % static_cast<const Shape<Sphere3D>*>(b)->shape;
case ShapeDimensionTraits<3>::Type::AxisAlignedBox*ShapeDimensionTraits<3>::Type::Point:
return static_cast<const Shape<AxisAlignedBox3D>*>(a)->shape % static_cast<const Shape<Point3D>*>(b)->shape;
case ShapeDimensionTraits<3>::Type::Plane*ShapeDimensionTraits<3>::Type::Line:
return static_cast<const Shape<Plane>*>(a)->shape % static_cast<const Shape<Line3D>*>(b)->shape;
case ShapeDimensionTraits<3>::Type::Plane*ShapeDimensionTraits<3>::Type::LineSegment:
return static_cast<const Shape<Plane>*>(a)->shape % static_cast<const Shape<LineSegment3D>*>(b)->shape;
}
return false;
}
}}}

46
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š <mosra@centrum.cz>
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<UnsignedInt> 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<UnsignedInt dimensions> bool collides(const AbstractShape<dimensions>* a, const AbstractShape<dimensions>* b);
}}}
#endif

150
src/Physics/ShapeGroup.cpp

@ -24,52 +24,144 @@
#include "ShapeGroup.h" #include "ShapeGroup.h"
#include <algorithm>
#include <Utility/Assert.h>
#include "Physics/Implementation/CollisionDispatch.h"
namespace Magnum { namespace Physics { namespace Magnum { namespace Physics {
template<UnsignedInt dimensions> ShapeGroup<dimensions>::ShapeGroup(ShapeGroup<dimensions>&& other): operation(other.operation), a(other.a), b(other.b) { /*
other.operation = Implementation::GroupOperation::AlwaysFalse; Hierarchy implementation notes:
other.a = nullptr;
other.b = nullptr; 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<UnsignedInt dimensions> ShapeGroup<dimensions>::ShapeGroup(const ShapeGroup<dimensions>& other): _shapeCount(other._shapeCount), _nodeCount(other._nodeCount) {
copyShapes(0, other);
copyNodes(0, other);
}
template<UnsignedInt dimensions> ShapeGroup<dimensions>::ShapeGroup(ShapeGroup<dimensions>&& 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<UnsignedInt dimensions> ShapeGroup<dimensions>::~ShapeGroup() { template<UnsignedInt dimensions> ShapeGroup<dimensions>::~ShapeGroup() {
if(!(operation & Implementation::GroupOperation::RefA)) delete a; for(std::size_t i = 0; i != _shapeCount; ++i)
if(!(operation & Implementation::GroupOperation::RefB)) delete b; delete _shapes[i];
delete[] _shapes;
delete[] _nodes;
} }
template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(ShapeGroup<dimensions>&& other) { template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(const ShapeGroup<dimensions>& other) {
if(!(operation & Implementation::GroupOperation::RefA)) delete a; for(std::size_t i = 0; i != _shapeCount; ++i)
if(!(operation & Implementation::GroupOperation::RefB)) delete b; delete _shapes[i];
operation = other.operation; if(_shapeCount != other._shapeCount) {
a = other.a; delete[] _shapes;
b = other.b; _shapeCount = other._shapeCount;
_shapes = new Implementation::AbstractShape<dimensions>*[_shapeCount];
}
other.operation = Implementation::GroupOperation::AlwaysFalse; if(_nodeCount != other._nodeCount) {
other.a = nullptr; delete[] _nodes;
other.b = nullptr; _nodeCount = other._nodeCount;
_nodes = new Node[_nodeCount];
}
copyShapes(0, other);
copyNodes(0, other);
return *this; return *this;
} }
template<UnsignedInt dimensions> void ShapeGroup<dimensions>::applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) { template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(ShapeGroup<dimensions>&& other) {
if(a) a->applyTransformationMatrix(matrix); std::swap(other._shapeCount, _shapeCount);
if(b) b->applyTransformationMatrix(matrix); std::swap(other._nodeCount, _nodeCount);
std::swap(other._shapes, _shapes);
std::swap(other._nodes, _nodes);
return *this;
} }
template<UnsignedInt dimensions> bool ShapeGroup<dimensions>::collides(const AbstractShape<dimensions>* other) const { template<UnsignedInt dimensions> void ShapeGroup<dimensions>::copyShapes(const std::size_t offset, ShapeGroup<dimensions>&& other) {
switch(operation & ~Implementation::GroupOperation::RefAB) { std::move(other._shapes, other._shapes+other._shapeCount, _shapes+offset);
case Implementation::GroupOperation::And: return a->collides(other) && b->collides(other); delete[] other._shapes;
case Implementation::GroupOperation::Or: return a->collides(other) || b->collides(other); other._shapes = nullptr;
case Implementation::GroupOperation::Not: return !a->collides(other); other._shapeCount = 0;
case Implementation::GroupOperation::FirstObjectOnly: return a->collides(other); }
default: template<UnsignedInt dimensions> void ShapeGroup<dimensions>::copyShapes(const std::size_t offset, const ShapeGroup<dimensions>& other) {
return false; for(std::size_t i = 0; i != other._shapeCount; ++i)
} _shapes[i+offset] = other._shapes[i]->clone();
}
template<UnsignedInt dimensions> void ShapeGroup<dimensions>::copyNodes(std::size_t offset, const ShapeGroup<dimensions>& other) {
std::copy(other._nodes, other._nodes+other._nodeCount, _nodes+offset);
}
template<UnsignedInt dimensions> ShapeGroup<dimensions> ShapeGroup<dimensions>::transformed(const typename DimensionTraits<dimensions>::MatrixType& matrix) const {
ShapeGroup<dimensions> out(*this);
for(std::size_t i = 0; i != _shapeCount; ++i)
_shapes[i]->transform(matrix, out._shapes[i]);
return out;
}
template<UnsignedInt dimensions> bool ShapeGroup<dimensions>::collides(const Implementation::AbstractShape<dimensions>* 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>; #ifndef DOXYGEN_GENERATING_OUTPUT
template class ShapeGroup<3>; template class MAGNUM_PHYSICS_EXPORT ShapeGroup<2>;
template class MAGNUM_PHYSICS_EXPORT ShapeGroup<3>;
#endif
}} }}

361
src/Physics/ShapeGroup.h

@ -25,168 +25,195 @@
*/ */
/** @file /** @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 <functional>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <Utility/Assert.h>
#include "corradeCompatibility.h" #include "DimensionTraits.h"
#include "Physics/Physics.h"
#include "Physics/magnumPhysicsVisibility.h"
#include "Physics/shapeImplementation.h"
namespace Magnum { namespace Physics { namespace Magnum { namespace Physics {
#ifndef DOXYGEN_GENERATING_OUTPUT
namespace Implementation { namespace Implementation {
enum GroupOperation { template<class> struct ObjectShapeHelper;
RefA = 0x01,
RefB = 0x02, template<UnsignedInt dimensions> inline AbstractShape<dimensions>* getAbstractShape(ShapeGroup<dimensions>& group, std::size_t i) {
RefAB = 0x03, return group._shapes[i];
// Complement = 1 << 2, }
// Union = 2 << 2, template<UnsignedInt dimensions> inline const AbstractShape<dimensions>* getAbstractShape(const ShapeGroup<dimensions>& group, std::size_t i) {
// Intersection = 3 << 2, return group._shapes[i];
// 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<std::is_base_of<AbstractShape<T::Dimensions>, T>::value, ShapeGroup<T::Dimensions>>::type
#define enableIfAreBaseType typename std::enable_if<T::Dimensions == U::Dimensions && std::is_base_of<AbstractShape<T::Dimensions>, T>::value && std::is_base_of<AbstractShape<T::Dimensions>, U>::value, ShapeGroup<T::Dimensions>>::type /** @brief Shape operation */
#endif enum class ShapeOperation: UnsignedByte {
Not, /**< Boolean NOT */
And, /**< Boolean AND */
Or /**< Boolean OR */
};
/** /**
@brief Shape group @brief Shape group
Result of logical operations on shapes. Result of logical operations on shapes.
See @ref collision-detection for brief introduction. See @ref collision-detection for brief introduction.
@see ShapeGroup2D, ShapeGroup3D
*/ */
template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT ShapeGroup: public AbstractShape<dimensions> { template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT ShapeGroup {
#ifndef DOXYGEN_GENERATING_OUTPUT friend Implementation::AbstractShape<dimensions>* Implementation::getAbstractShape<>(ShapeGroup<dimensions>&, std::size_t);
// template<class T> friend constexpr operator~(const T& a) -> enableIfIsBaseType; friend const Implementation::AbstractShape<dimensions>* Implementation::getAbstractShape<>(const ShapeGroup<dimensions>&, std::size_t);
// template<class T> friend constexpr operator~(T&& a) -> enableIfIsBaseType; friend struct Implementation::ObjectShapeHelper<ShapeGroup<dimensions>>;
// template<class T> friend constexpr operator~(T& a) -> enableIfIsBaseType;
template<class T> friend constexpr auto operator!(const T& a) -> enableIfIsBaseType;
template<class T> friend constexpr auto operator!(T&& a) -> enableIfIsBaseType;
template<class T> friend constexpr auto operator!(T& a) -> enableIfIsBaseType;
#define friendOp(char) \
template<class T, class U> friend constexpr auto operator char(const T& a, const U& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(const T& a, U&& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(T&& a, const U& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(T&& a, U&& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(const T& a, std::reference_wrapper<U> b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(T&& a, std::reference_wrapper<U> b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(std::reference_wrapper<T> a, const U& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(std::reference_wrapper<T> a, U&& b) -> enableIfAreBaseType; \
template<class T, class U> friend constexpr auto operator char(std::reference_wrapper<T> a, std::reference_wrapper<U> b) -> enableIfAreBaseType;
// friendOp(|)
// friendOp(&)
// friendOp(-)
// friendOp(^)
friendOp(&&)
friendOp(||)
#undef friendOp
#endif
ShapeGroup(const ShapeGroup&) = delete;
ShapeGroup& operator=(const ShapeGroup&) = delete;
public: public:
/** @brief Default constructor */ enum: UnsignedInt {
inline explicit ShapeGroup(): operation(Implementation::GroupOperation::AlwaysFalse), a(nullptr), b(nullptr) {} 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<dimensions>::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<class T> explicit ShapeGroup(ShapeOperation operation, T&& a);
/**
* @brief Binary operation constructor
* @param operation Binary operation
* @param a Left operand
* @param b Right operand
*/
template<class T, class U> explicit ShapeGroup(ShapeOperation operation, T&& a, U&& b);
/** @brief Copy constructor */
ShapeGroup(const ShapeGroup<dimensions>& other);
/** @brief Move constructor */ /** @brief Move constructor */
ShapeGroup(ShapeGroup&& other); ShapeGroup(ShapeGroup<dimensions>&& other);
/** @brief Destructor */
~ShapeGroup(); ~ShapeGroup();
/** @brief Move assignment */ /** @brief Assigment operator */
ShapeGroup& operator=(ShapeGroup&& other); ShapeGroup<dimensions>& operator=(const ShapeGroup<dimensions>& other);
inline typename AbstractShape<dimensions>::Type type() const override { /** @brief Move assignment operator */
return AbstractShape<dimensions>::Type::ShapeGroup; ShapeGroup<dimensions>& operator=(ShapeGroup<dimensions>&& other);
}
void applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) override; /** @brief Transformed shape */
ShapeGroup<dimensions> transformed(const typename DimensionTraits<dimensions>::MatrixType& matrix) const;
bool collides(const AbstractShape<dimensions>* other) const override; /** @brief Count of shapes in the hierarchy */
inline std::size_t size() const { return _shapeCount; }
/** /** @brief Type of shape at given position */
* @brief First object in the group inline Type type(std::size_t i) const { return _shapes[i]->type(); }
*
* If there is no such object, returns `nullptr`.
*/
inline AbstractShape<dimensions>* first() { return a; }
/** /** @brief Shape at given position */
* @brief Second object in the group template<class T> const T& get(std::size_t i) const;
*
* If there is no such object, returns `nullptr`. /** @brief Collision with another shape */
*/ #ifdef DOXYGEN_GENERATING_OUTPUT
inline AbstractShape<dimensions>* second() { return b; } template<class T> inline bool operator%(const T& other) const {
#else
template<class T> inline auto operator%(const T& other) const -> typename std::enable_if<std::is_same<decltype(Implementation::TypeOf<T>::type()), typename Implementation::ShapeDimensionTraits<dimensions>::Type>::value, bool>::type {
#endif
Implementation::Shape<T> a(other);
return collides(&a);
}
private: private:
inline ShapeGroup(int operation, AbstractShape<dimensions>* a, AbstractShape<dimensions>* b): operation(operation), a(a), b(b) {} struct Node {
std::size_t rightNode, rightShape;
ShapeOperation operation;
};
inline bool collides(const Implementation::AbstractShape<dimensions>* a) const {
return collides(a, 0, 0, _shapeCount);
}
bool collides(const Implementation::AbstractShape<dimensions>* a, std::size_t node, std::size_t shapeBegin, std::size_t shapeEnd) const;
template<class T> inline constexpr static std::size_t shapeCount(const T&) {
return 1;
}
inline constexpr static std::size_t shapeCount(const ShapeGroup<dimensions>& hierarchy) {
return hierarchy._shapeCount;
}
template<class T> inline constexpr static std::size_t nodeCount(const T&) {
return 0;
}
inline constexpr static std::size_t nodeCount(const ShapeGroup<dimensions>& hierarchy) {
return hierarchy._nodeCount;
}
template<class T> inline void copyShapes(std::size_t offset, const T& shape) {
_shapes[offset] = new Implementation::Shape<T>(shape);
}
void copyShapes(std::size_t offset, ShapeGroup<dimensions>&& other);
void copyShapes(std::size_t offset, const ShapeGroup<dimensions>& other);
int operation; template<class T> inline void copyNodes(std::size_t, const T&) {}
AbstractShape<dimensions>* a; void copyNodes(std::size_t offset, const ShapeGroup<dimensions>& other);
AbstractShape<dimensions>* b;
std::size_t _shapeCount, _nodeCount;
Implementation::AbstractShape<dimensions>** _shapes;
Node* _nodes;
}; };
/** @brief Two-dimensional shape group */ /** @brief Two-dimensional shape hierarchy */
typedef ShapeGroup<2> ShapeGroup2D; typedef ShapeGroup<2> ShapeGroup2D;
/** @brief Three-dimensional shape group */ /** @brief Three-dimensional shape hierarchy */
typedef ShapeGroup<3> ShapeGroup3D; typedef ShapeGroup<3> ShapeGroup3D;
// /* @brief Complement of shape */ #ifdef DOXYGEN_GENERATING_OUTPUT
// template<class T> inline constexpr enableIfIsBaseType operator~(const T& a) { /** @debugoperator{Magnum::Physics::ShapeGroup} */
// return ShapeGroup(ShapeGroup::Complement, new T(a), nullptr); template<UnsignedInt dimensions> Debug operator<<(Debug debug, typename ShapeGroup<dimensions>::Type value);
// } #endif
// #ifndef DOXYGEN_GENERATING_OUTPUT
// template<class T> inline constexpr enableIfIsBaseType operator~(T&& a) {
// return ShapeGroup(ShapeGroup::Complement, new T(std::forward<T>(a)), nullptr);
// }
// template<class T> inline constexpr enableIfIsBaseType operator~(T& a) {
// return ShapeGroup(ShapeGroup::Complement|ShapeGroup::RefA, &a.get(), nullptr);
// }
// #endif
/** @relates ShapeGroup /** @relates ShapeGroup
@brief Logical NOT of shape @brief Collision of shape with ShapeGroup
*/ */
template<class T> inline constexpr auto operator!(const T& a) -> enableIfIsBaseType { #ifdef DOXYGEN_GENERATING_OUTPUT
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::Not, new T(a), nullptr); template<UnsignedInt dimensions, class T> inline bool operator%(const T& a, const ShapeGroup<dimensions>& b) {
} #else
#ifndef DOXYGEN_GENERATING_OUTPUT template<UnsignedInt dimensions, class T> inline auto operator%(const T& a, const ShapeGroup<dimensions>& b) -> typename std::enable_if<std::is_same<decltype(Implementation::TypeOf<T>::type()), typename Implementation::ShapeDimensionTraits<dimensions>::Type>::value, bool>::type {
template<class T> inline constexpr auto operator!(T&& a) -> enableIfIsBaseType {
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::Not, new T(std::forward<T>(a)), nullptr);
}
template<class T> inline constexpr auto operator!(T& a) -> enableIfIsBaseType {
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::Not|Implementation::GroupOperation::RefA, &a.get(), nullptr);
}
#endif #endif
return b % a;
}
#ifdef DOXYGEN_GENERATING_OUTPUT #ifdef DOXYGEN_GENERATING_OUTPUT
// /* @brief Union of two shapes */ /** @relates ShapeGroup
// template<class T, class U> inline constexpr ShapeGroup operator&(T a, U b); @brief Logical NOT of shape
// */
// /* @brief Intersection of two shapes */ template<class T> inline ShapeGroup<T::Dimensions> operator!(T a);
// template<class T, class U> inline constexpr ShapeGroup operator&(T a, U b);
//
// /* @brief Difference of two shapes */
// template<class T, class U> inline constexpr ShapeGroup operator-(T a, U b);
//
// /* @brief XOR of two shapes */
// template<class T, class U> inline constexpr ShapeGroup operator^(T a, U b);
/** @relates ShapeGroup /** @relates ShapeGroup
@brief Logical AND of two shapes @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. version, because collision with @p b is computed only if @p a collides.
See @ref collision-detection-shape-simplification for an example. See @ref collision-detection-shape-simplification for an example.
*/ */
template<UnsignedInt dimensions, class T, class U> inline constexpr ShapeGroup<dimensions> operator&&(T a, U b); template<class T> inline ShapeGroup<T::Dimensions> operator&&(T a, T b);
/** @relates ShapeGroup /** @relates ShapeGroup
@brief Logical OR of two shapes @brief Logical OR of two shapes
@ -204,47 +231,67 @@ template<UnsignedInt dimensions, class T, class U> inline constexpr ShapeGroup<d
is used, so if collision with @p a is detected, collision with @p b is not is used, so if collision with @p a is detected, collision with @p b is not
computed. computed.
*/ */
template<UnsignedInt dimensions, class T, class U> inline constexpr ShapeGroup<dimensions> operator||(T a, U b); template<class T> inline ShapeGroup<T::Dimensions> operator||(T a, T b);
#else #endif
#define op(type, char) \
template<class T, class U> inline constexpr auto operator char(const T& a, const U& b) -> enableIfAreBaseType { \ #ifndef DOXYGEN_GENERATING_OUTPUT
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(a), new U(b)); \ #define enableIfIsShapeType typename std::enable_if< \
} \ std::is_same<decltype(Implementation::TypeOf<T>::type()), typename Implementation::ShapeDimensionTraits<T::Dimensions>::Type>::value, \
template<class T, class U> inline constexpr auto operator char(const T& a, U&& b) -> enableIfAreBaseType { \ ShapeGroup<T::Dimensions>>::type
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(a), new U(std::forward<U>(b))); \ #define enableIfAreShapeType typename std::enable_if< \
} \ std::is_same<decltype(Implementation::TypeOf<T>::type()), typename Implementation::ShapeDimensionTraits<T::Dimensions>::Type>::value && \
template<class T, class U> inline constexpr auto operator char(T&& a, const U& b) -> enableIfAreBaseType { \ std::is_same<decltype(Implementation::TypeOf<U>::type()), typename Implementation::ShapeDimensionTraits<T::Dimensions>::Type>::value, \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(std::forward<T>(a)), new U(b)); \ ShapeGroup<T::Dimensions>>::type
} \ template<class T> inline auto operator!(T&& a) -> enableIfIsShapeType {
template<class T, class U> inline constexpr auto operator char(T&& a, U&& b) -> enableIfAreBaseType { \ return ShapeGroup<T::Dimensions>(ShapeOperation::Not, std::forward<T>(a));
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(std::forward<T>(a)), new U(std::forward<U>(b))); \ }
} \ template<class T, class U> inline auto operator&&(T&& a, U&& b) -> enableIfAreShapeType {
template<class T, class U> inline constexpr auto operator char(const T& a, std::reference_wrapper<U> b) -> enableIfAreBaseType { \ return ShapeGroup<T::Dimensions>(ShapeOperation::And, std::forward<T>(a), std::forward<U>(b));
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefB, new T(a), &b.get()); \
} \
template<class T, class U> inline constexpr auto operator char(T&& a, std::reference_wrapper<U> b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefB, new T(std::forward<T>(a)), &b.get()); \
} \
template<class T, class U> inline constexpr auto operator char(std::reference_wrapper<T> a, const U& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefA, &a.get(), new U(b)); \
} \
template<class T, class U> inline constexpr auto operator char(std::reference_wrapper<T> a, U&& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefA, &a.get(), new U(std::forward<U>(b))); \
} \
template<class T, class U> inline constexpr auto operator char(std::reference_wrapper<T> a, std::reference_wrapper<U> b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type|Implementation::GroupOperation::RefAB, &a.get(), &b.get()); \
} }
// op(Union, |) template<class T, class U> inline auto operator||(T&& a, U&& b) -> enableIfAreShapeType {
// op(Intersection, &) return ShapeGroup<T::Dimensions>(ShapeOperation::Or, std::forward<T>(a), std::forward<U>(b));
// op(Difference, -) }
// op(Xor, ^) #undef enableIfIsShapeType
op(And, &&) #undef enableIfAreShapeType
op(Or, ||)
#undef op
#endif #endif
#undef enableIfIsBaseType template<UnsignedInt dimensions> template<class T> ShapeGroup<dimensions>::ShapeGroup(ShapeOperation operation, T&& a): _shapeCount(shapeCount(a)), _nodeCount(nodeCount(a)+1), _shapes(new Implementation::AbstractShape<dimensions>*[_shapeCount]), _nodes(new Node[_nodeCount]) {
#undef enableIfAreBaseType 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<T>(a));
}
template<UnsignedInt dimensions> template<class T, class U> ShapeGroup<dimensions>::ShapeGroup(ShapeOperation operation, T&& a, U&& b): _shapeCount(shapeCount(a) + shapeCount(b)), _nodeCount(nodeCount(a) + nodeCount(b) + 1), _shapes(new Implementation::AbstractShape<dimensions>*[_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<U>(b));
copyShapes(0, std::forward<T>(a));
}
template<UnsignedInt dimensions> template<class T> inline const T& ShapeGroup<dimensions>::get(std::size_t i) const {
CORRADE_ASSERT(_shapes[i]->type() == Implementation::TypeOf<T>::type(),
"Physics::ShapeGroup::get(): given shape is not of type" << Implementation::TypeOf<T>::type(),
*static_cast<T*>(nullptr));
return static_cast<Implementation::Shape<T>*>(_shapes[i])->shape;
}
}} }}

2
src/Physics/Test/CMakeLists.txt

@ -22,7 +22,7 @@
# DEALINGS IN THE SOFTWARE. # 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(PhysicsAxisAlignedBoxTest AxisAlignedBoxTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsBoxTest BoxTest.cpp LIBRARIES MagnumPhysics) corrade_add_test(PhysicsBoxTest BoxTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsCapsuleTest CapsuleTest.cpp LIBRARIES MagnumPhysics) corrade_add_test(PhysicsCapsuleTest CapsuleTest.cpp LIBRARIES MagnumPhysics)

98
src/Physics/Test/ShapeGroupTest.cpp

@ -26,8 +26,11 @@
#include "Math/Matrix4.h" #include "Math/Matrix4.h"
#include "Physics/Point.h" #include "Physics/Point.h"
#include "Physics/LineSegment.h" #include "Physics/AxisAlignedBox.h"
#include "Physics/ShapeGroup.h" #include "Physics/ShapeGroup.h"
#include "Physics/Sphere.h"
#include "ShapeTestBase.h"
namespace Magnum { namespace Physics { namespace Test { namespace Magnum { namespace Physics { namespace Test {
@ -35,40 +38,91 @@ class ShapeGroupTest: public Corrade::TestSuite::Tester {
public: public:
ShapeGroupTest(); ShapeGroupTest();
void copy(); void negated();
void reference(); void anded();
void ored();
void multipleUnary();
void hierarchy();
void empty();
}; };
ShapeGroupTest::ShapeGroupTest() { ShapeGroupTest::ShapeGroupTest() {
addTests({&ShapeGroupTest::copy, addTests({&ShapeGroupTest::negated,
&ShapeGroupTest::reference}); &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<Physics::Point2D>(0).position(), Vector2::xAxis(0.5f));
VERIFY_NOT_COLLIDES(a, Physics::Sphere2D({}, 1.0f));
} }
void ShapeGroupTest::copy() { void ShapeGroupTest::anded() {
ShapeGroup3D group; const Physics::ShapeGroup2D a = Physics::Sphere2D({}, 1.0f) && Physics::Point2D(Vector2::xAxis(0.5f));
{
Physics::Point3D point({1.0f, 2.0f, 3.0f}); CORRADE_COMPARE(a.size(), 2);
Physics::LineSegment3D segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f}); CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Sphere);
CORRADE_COMPARE(a.type(1), ShapeGroup2D::Type::Point);
CORRADE_COMPARE(a.get<Physics::Sphere2D>(0).position(), Vector2());
CORRADE_COMPARE(a.get<Physics::Sphere2D>(0).radius(), 1.0f);
CORRADE_COMPARE(a.get<Physics::Point2D>(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 */ CORRADE_COMPARE(a.size(), 2);
group.applyTransformationMatrix(Matrix4::translation(Vector3::xAxis(1.0f))); CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Sphere);
CORRADE_COMPARE(a.type(1), ShapeGroup2D::Type::Point);
CORRADE_COMPARE(a.get<Physics::Sphere2D>(0).position(), Vector2());
CORRADE_COMPARE(a.get<Physics::Sphere2D>(0).radius(), 1.0f);
CORRADE_COMPARE(a.get<Physics::Point2D>(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() { void ShapeGroupTest::multipleUnary() {
Physics::Point3D point({1.0f, 2.0f, 3.0f}); const Physics::ShapeGroup2D a = !!!!Physics::Point2D(Vector2::xAxis(0.5f));
Physics::LineSegment3D segment({2.0f, 1.0f, 30.0f}, {1.0f, -20.0f, 3.0f});
CORRADE_COMPARE(a.size(), 1);
CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Point);
CORRADE_COMPARE(a.get<Physics::Point2D>(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<Physics::Point3D>(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))); VERIFY_NOT_COLLIDES(a, Physics::Sphere2D({}, 1.0f));
CORRADE_VERIFY((segment.transformedA() == Vector3(3.0f, 2.0f, 31.0f)));
} }
}}} }}}

22
src/Physics/Test/AbstractShapeTest.cpp → src/Physics/Test/ShapeImplementationTest.cpp

@ -25,31 +25,31 @@
#include <sstream> #include <sstream>
#include <TestSuite/Tester.h> #include <TestSuite/Tester.h>
#include "Physics/AbstractShape.h" #include "Physics/shapeImplementation.h"
namespace Magnum { namespace Physics { namespace Test { namespace Magnum { namespace Physics { namespace Test {
class AbstractShapeTest: public Corrade::TestSuite::Tester { class ShapeImplementationTest: public Corrade::TestSuite::Tester {
public: public:
AbstractShapeTest(); ShapeImplementationTest();
void debug(); void debug();
}; };
AbstractShapeTest::AbstractShapeTest() { ShapeImplementationTest::ShapeImplementationTest() {
addTests({&AbstractShapeTest::debug}); addTests({&ShapeImplementationTest::debug});
} }
void AbstractShapeTest::debug() { void ShapeImplementationTest::debug() {
std::ostringstream o; std::ostringstream o;
Debug(&o) << AbstractShape2D::Type::ShapeGroup; Debug(&o) << Implementation::ShapeDimensionTraits<2>::Type::ShapeGroup;
CORRADE_COMPARE(o.str(), "AbstractShape2D::Type::ShapeGroup\n"); CORRADE_COMPARE(o.str(), "Physics::Shape2D::Type::ShapeGroup\n");
o.str({}); o.str({});
Debug(&o) << AbstractShape3D::Type::Plane; Debug(&o) << Implementation::ShapeDimensionTraits<3>::Type::Plane;
CORRADE_COMPARE(o.str(), "AbstractShape3D::Type::Plane\n"); CORRADE_COMPARE(o.str(), "Physics::Shape3D::Type::Plane\n");
} }
}}} }}}
CORRADE_TEST_MAIN(Magnum::Physics::Test::AbstractShapeTest) CORRADE_TEST_MAIN(Magnum::Physics::Test::ShapeImplementationTest)

1
src/Physics/magnumPhysicsVisibility.h

@ -31,5 +31,6 @@
#else #else
#define MAGNUM_PHYSICS_EXPORT CORRADE_VISIBILITY_IMPORT #define MAGNUM_PHYSICS_EXPORT CORRADE_VISIBILITY_IMPORT
#endif #endif
#define MAGNUM_PHYSICS_LOCAL CORRADE_VISIBILITY_LOCAL
#endif #endif

72
src/Physics/shapeImplementation.cpp

@ -0,0 +1,72 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <Utility/Debug.h>
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<UnsignedInt dimensions> AbstractShape<dimensions>::~AbstractShape() = default;
template<UnsignedInt dimensions> AbstractShape<dimensions>::AbstractShape() = default;
template struct AbstractShape<2>;
template struct AbstractShape<3>;
}}}

155
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š <mosra@centrum.cz>
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 <utility>
#include <Utility/Assert.h>
#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<UnsignedInt> 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<class> struct TypeOf;
template<UnsignedInt dimensions> struct TypeOf<Physics::Point<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::Point;
}
};
template<UnsignedInt dimensions> struct TypeOf<Physics::Line<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::Line;
}
};
template<UnsignedInt dimensions> struct TypeOf<Physics::LineSegment<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::LineSegment;
}
};
template<UnsignedInt dimensions> struct TypeOf<Physics::Sphere<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::Sphere;
}
};
template<UnsignedInt dimensions> struct TypeOf<Physics::Capsule<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::Capsule;
}
};
template<UnsignedInt dimensions> struct TypeOf<Physics::AxisAlignedBox<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::AxisAlignedBox;
}
};
template<UnsignedInt dimensions> struct TypeOf<Physics::Box<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::Box;
}
};
template<> struct TypeOf<Physics::Plane> {
inline constexpr static typename ShapeDimensionTraits<3>::Type type() {
return ShapeDimensionTraits<3>::Type::Plane;
}
};
template<UnsignedInt dimensions> struct TypeOf<Physics::ShapeGroup<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::ShapeGroup;
}
};
/* Polymorphic shape wrappers */
template<UnsignedInt dimensions> struct MAGNUM_PHYSICS_EXPORT AbstractShape {
explicit AbstractShape();
virtual ~AbstractShape();
virtual typename ShapeDimensionTraits<dimensions>::Type MAGNUM_PHYSICS_LOCAL type() const = 0;
virtual AbstractShape<dimensions> MAGNUM_PHYSICS_LOCAL * clone() const = 0;
virtual void MAGNUM_PHYSICS_LOCAL transform(const typename DimensionTraits<dimensions>::MatrixType& matrix, AbstractShape<dimensions>* result) const = 0;
};
template<class T> struct Shape: AbstractShape<T::Dimensions> {
T shape;
explicit Shape() = default;
explicit Shape(const T& shape): shape(shape) {}
explicit Shape(T&& shape): shape(std::move(shape)) {}
typename ShapeDimensionTraits<T::Dimensions>::Type type() const override {
return TypeOf<T>::type();
}
AbstractShape<T::Dimensions>* clone() const override {
return new Shape<T>(shape);
}
void transform(const typename DimensionTraits<T::Dimensions>::MatrixType& matrix, AbstractShape<T::Dimensions>* result) const override {
CORRADE_INTERNAL_ASSERT(result->type() == type());
static_cast<Shape<T>*>(result)->shape = shape.transformed(matrix);
}
};
}}}
#endif
Loading…
Cancel
Save