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
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)

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 <algorithm>
#include <Utility/Assert.h>
#include "Physics/Implementation/CollisionDispatch.h"
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;
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<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() {
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<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(ShapeGroup<dimensions>&& other) {
if(!(operation & Implementation::GroupOperation::RefA)) delete a;
if(!(operation & Implementation::GroupOperation::RefB)) delete b;
template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(const ShapeGroup<dimensions>& 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<dimensions>*[_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<UnsignedInt dimensions> void ShapeGroup<dimensions>::applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) {
if(a) a->applyTransformationMatrix(matrix);
if(b) b->applyTransformationMatrix(matrix);
template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(ShapeGroup<dimensions>&& other) {
std::swap(other._shapeCount, _shapeCount);
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 {
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<UnsignedInt dimensions> void ShapeGroup<dimensions>::copyShapes(const std::size_t offset, ShapeGroup<dimensions>&& other) {
std::move(other._shapes, other._shapes+other._shapeCount, _shapes+offset);
delete[] other._shapes;
other._shapes = nullptr;
other._shapeCount = 0;
}
default:
return false;
}
template<UnsignedInt dimensions> void ShapeGroup<dimensions>::copyShapes(const std::size_t offset, const ShapeGroup<dimensions>& other) {
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>;
template class ShapeGroup<3>;
#ifndef DOXYGEN_GENERATING_OUTPUT
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
* @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 <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 {
#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<class> struct ObjectShapeHelper;
template<UnsignedInt dimensions> inline AbstractShape<dimensions>* getAbstractShape(ShapeGroup<dimensions>& group, std::size_t i) {
return group._shapes[i];
}
template<UnsignedInt dimensions> inline const AbstractShape<dimensions>* getAbstractShape(const ShapeGroup<dimensions>& group, std::size_t i) {
return group._shapes[i];
}
}
#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
#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<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT ShapeGroup: public AbstractShape<dimensions> {
#ifndef DOXYGEN_GENERATING_OUTPUT
// template<class T> friend constexpr operator~(const T& a) -> enableIfIsBaseType;
// template<class T> friend constexpr operator~(T&& a) -> enableIfIsBaseType;
// 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;
template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT ShapeGroup {
friend Implementation::AbstractShape<dimensions>* Implementation::getAbstractShape<>(ShapeGroup<dimensions>&, std::size_t);
friend const Implementation::AbstractShape<dimensions>* Implementation::getAbstractShape<>(const ShapeGroup<dimensions>&, std::size_t);
friend struct Implementation::ObjectShapeHelper<ShapeGroup<dimensions>>;
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<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 */
ShapeGroup(ShapeGroup&& other);
ShapeGroup(ShapeGroup<dimensions>&& other);
/** @brief Destructor */
~ShapeGroup();
/** @brief Move assignment */
ShapeGroup& operator=(ShapeGroup&& other);
/** @brief Assigment operator */
ShapeGroup<dimensions>& operator=(const ShapeGroup<dimensions>& other);
inline typename AbstractShape<dimensions>::Type type() const override {
return AbstractShape<dimensions>::Type::ShapeGroup;
}
/** @brief Move assignment operator */
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 First object in the group
*
* If there is no such object, returns `nullptr`.
*/
inline AbstractShape<dimensions>* 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<dimensions>* second() { return b; }
/** @brief Shape at given position */
template<class T> const T& get(std::size_t i) const;
/** @brief Collision with another shape */
#ifdef DOXYGEN_GENERATING_OUTPUT
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:
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;
AbstractShape<dimensions>* a;
AbstractShape<dimensions>* b;
template<class T> inline void copyNodes(std::size_t, const T&) {}
void copyNodes(std::size_t offset, const ShapeGroup<dimensions>& other);
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;
/** @brief Three-dimensional shape group */
/** @brief Three-dimensional shape hierarchy */
typedef ShapeGroup<3> ShapeGroup3D;
// /* @brief Complement of shape */
// template<class T> inline constexpr enableIfIsBaseType operator~(const T& a) {
// return ShapeGroup(ShapeGroup::Complement, new T(a), nullptr);
// }
// #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
#ifdef DOXYGEN_GENERATING_OUTPUT
/** @debugoperator{Magnum::Physics::ShapeGroup} */
template<UnsignedInt dimensions> Debug operator<<(Debug debug, typename ShapeGroup<dimensions>::Type value);
#endif
/** @relates ShapeGroup
@brief Logical NOT of shape
@brief Collision of shape with ShapeGroup
*/
template<class T> inline constexpr auto operator!(const T& a) -> enableIfIsBaseType {
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::Not, new T(a), nullptr);
}
#ifndef DOXYGEN_GENERATING_OUTPUT
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);
}
#ifdef DOXYGEN_GENERATING_OUTPUT
template<UnsignedInt dimensions, class T> inline bool operator%(const T& a, const ShapeGroup<dimensions>& b) {
#else
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 {
#endif
return b % a;
}
#ifdef DOXYGEN_GENERATING_OUTPUT
// /* @brief Union of two shapes */
// template<class T, class U> inline constexpr ShapeGroup operator&(T a, U b);
//
// /* @brief Intersection of two shapes */
// 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
@brief Logical NOT of shape
*/
template<class T> inline ShapeGroup<T::Dimensions> 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<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
@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
computed.
*/
template<UnsignedInt dimensions, class T, class U> inline constexpr ShapeGroup<dimensions> operator||(T a, U b);
#else
#define op(type, char) \
template<class T, class U> inline constexpr auto operator char(const T& a, const U& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(a), new U(b)); \
} \
template<class T, class U> inline constexpr auto operator char(const T& a, U&& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(a), new U(std::forward<U>(b))); \
} \
template<class T, class U> inline constexpr auto operator char(T&& a, const U& b) -> enableIfAreBaseType { \
return ShapeGroup<T::Dimensions>(Implementation::GroupOperation::type, new T(std::forward<T>(a)), new U(b)); \
} \
template<class T, class U> inline constexpr auto operator char(T&& a, U&& b) -> enableIfAreBaseType { \
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 constexpr auto operator char(const T& a, std::reference_wrapper<U> b) -> enableIfAreBaseType { \
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()); \
template<class T> inline ShapeGroup<T::Dimensions> operator||(T a, T b);
#endif
#ifndef DOXYGEN_GENERATING_OUTPUT
#define enableIfIsShapeType typename std::enable_if< \
std::is_same<decltype(Implementation::TypeOf<T>::type()), typename Implementation::ShapeDimensionTraits<T::Dimensions>::Type>::value, \
ShapeGroup<T::Dimensions>>::type
#define enableIfAreShapeType typename std::enable_if< \
std::is_same<decltype(Implementation::TypeOf<T>::type()), typename Implementation::ShapeDimensionTraits<T::Dimensions>::Type>::value && \
std::is_same<decltype(Implementation::TypeOf<U>::type()), typename Implementation::ShapeDimensionTraits<T::Dimensions>::Type>::value, \
ShapeGroup<T::Dimensions>>::type
template<class T> inline auto operator!(T&& a) -> enableIfIsShapeType {
return ShapeGroup<T::Dimensions>(ShapeOperation::Not, std::forward<T>(a));
}
template<class T, class U> inline auto operator&&(T&& a, U&& b) -> enableIfAreShapeType {
return ShapeGroup<T::Dimensions>(ShapeOperation::And, std::forward<T>(a), std::forward<U>(b));
}
// op(Union, |)
// op(Intersection, &)
// op(Difference, -)
// op(Xor, ^)
op(And, &&)
op(Or, ||)
#undef op
template<class T, class U> inline auto operator||(T&& a, U&& b) -> enableIfAreShapeType {
return ShapeGroup<T::Dimensions>(ShapeOperation::Or, std::forward<T>(a), std::forward<U>(b));
}
#undef enableIfIsShapeType
#undef enableIfAreShapeType
#endif
#undef enableIfIsBaseType
#undef enableIfAreBaseType
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]) {
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.
#
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)

98
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<Physics::Point2D>(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<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 */
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<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() {
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<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)));
CORRADE_VERIFY((segment.transformedA() == Vector3(3.0f, 2.0f, 31.0f)));
VERIFY_NOT_COLLIDES(a, Physics::Sphere2D({}, 1.0f));
}
}}}

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

@ -25,31 +25,31 @@
#include <sstream>
#include <TestSuite/Tester.h>
#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)

1
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

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