Browse Source

Physics shape rework #5: renaming.

ObjectShape* is now Shape*, ShapeGroup is now Composition.
pull/278/head
Vladimír Vondruš 13 years ago
parent
commit
c089fce239
  1. 44
      src/DebugTools/ShapeRenderer.cpp
  2. 4
      src/DebugTools/ShapeRenderer.h
  3. 24
      src/Physics/AbstractShape.cpp
  4. 34
      src/Physics/AbstractShape.h
  5. 14
      src/Physics/CMakeLists.txt
  6. 167
      src/Physics/Composition.cpp
  7. 298
      src/Physics/Composition.h
  8. 58
      src/Physics/ObjectShapeGroup.cpp
  9. 113
      src/Physics/ObjectShapeGroup.h
  10. 28
      src/Physics/Physics.h
  11. 24
      src/Physics/Shape.cpp
  12. 70
      src/Physics/Shape.h
  13. 139
      src/Physics/ShapeGroup.cpp
  14. 285
      src/Physics/ShapeGroup.h
  15. 4
      src/Physics/Test/CMakeLists.txt
  16. 64
      src/Physics/Test/CompositionTest.cpp
  17. 4
      src/Physics/Test/ShapeImplementationTest.cpp
  18. 44
      src/Physics/Test/ShapeTest.cpp
  19. 4
      src/Physics/shapeImplementation.cpp
  20. 8
      src/Physics/shapeImplementation.h

44
src/DebugTools/ShapeRenderer.cpp

@ -25,8 +25,8 @@
#include "ShapeRenderer.h"
#include "ResourceManager.h"
#include "Physics/ObjectShape.h"
#include "Physics/ShapeGroup.h"
#include "Physics/Composition.h"
#include "Physics/Shape.h"
#include "SceneGraph/AbstractCamera.h"
#include "Implementation/AxisAlignedBoxRenderer.h"
@ -41,25 +41,25 @@ namespace Implementation {
template<> void createDebugMesh(ShapeRenderer<2>* renderer, const Physics::Implementation::AbstractShape<2>* shape) {
switch(shape->type()) {
case Physics::AbstractObjectShape2D::Type::AxisAlignedBox:
case Physics::AbstractShape2D::Type::AxisAlignedBox:
renderer->renderers.push_back(new Implementation::AxisAlignedBoxRenderer<2>(shape));
break;
case Physics::AbstractObjectShape2D::Type::Box:
case Physics::AbstractShape2D::Type::Box:
renderer->renderers.push_back(new Implementation::BoxRenderer<2>(shape));
break;
case Physics::AbstractObjectShape2D::Type::LineSegment:
case Physics::AbstractShape2D::Type::LineSegment:
renderer->renderers.push_back(new Implementation::LineSegmentRenderer<2>(shape));
break;
case Physics::AbstractObjectShape2D::Type::Point:
case Physics::AbstractShape2D::Type::Point:
renderer->renderers.push_back(new Implementation::PointRenderer<2>(shape));
break;
case Physics::AbstractObjectShape2D::Type::ShapeGroup: {
const Physics::ShapeGroup2D& group =
static_cast<const Physics::Implementation::Shape<Physics::ShapeGroup2D>*>(shape)->shape;
for(std::size_t i = 0; i != group.size(); ++i)
createDebugMesh(renderer, Physics::Implementation::getAbstractShape(group, i));
case Physics::AbstractShape2D::Type::Composition: {
const Physics::Composition2D& composition =
static_cast<const Physics::Implementation::Shape<Physics::Composition2D>*>(shape)->shape;
for(std::size_t i = 0; i != composition.size(); ++i)
createDebugMesh(renderer, Physics::Implementation::getAbstractShape(composition, i));
} break;
case Physics::AbstractObjectShape2D::Type::Sphere:
case Physics::AbstractShape2D::Type::Sphere:
renderer->renderers.push_back(new Implementation::SphereRenderer<2>(shape));
break;
default:
@ -69,23 +69,23 @@ template<> void createDebugMesh(ShapeRenderer<2>* renderer, const Physics::Imple
template<> void createDebugMesh(ShapeRenderer<3>* renderer, const Physics::Implementation::AbstractShape<3>* shape) {
switch(shape->type()) {
case Physics::AbstractObjectShape3D::Type::AxisAlignedBox:
case Physics::AbstractShape3D::Type::AxisAlignedBox:
renderer->renderers.push_back(new Implementation::AxisAlignedBoxRenderer<3>(shape));
break;
case Physics::AbstractObjectShape3D::Type::Box:
case Physics::AbstractShape3D::Type::Box:
renderer->renderers.push_back(new Implementation::BoxRenderer<3>(shape));
break;
case Physics::AbstractObjectShape3D::Type::LineSegment:
case Physics::AbstractShape3D::Type::LineSegment:
renderer->renderers.push_back(new Implementation::LineSegmentRenderer<3>(shape));
break;
case Physics::AbstractObjectShape3D::Type::Point:
case Physics::AbstractShape3D::Type::Point:
renderer->renderers.push_back(new Implementation::PointRenderer<3>(shape));
break;
case Physics::AbstractObjectShape3D::Type::ShapeGroup: {
const Physics::ShapeGroup3D& group =
static_cast<const Physics::Implementation::Shape<Physics::ShapeGroup3D>*>(shape)->shape;
for(std::size_t i = 0; i != group.size(); ++i)
createDebugMesh(renderer, Physics::Implementation::getAbstractShape(group, i));
case Physics::AbstractShape3D::Type::Composition: {
const Physics::Composition3D& composition =
static_cast<const Physics::Implementation::Shape<Physics::Composition3D>*>(shape)->shape;
for(std::size_t i = 0; i != composition.size(); ++i)
createDebugMesh(renderer, Physics::Implementation::getAbstractShape(composition, i));
} break;
default:
Warning() << "DebugTools::ShapeRenderer3D::createShapeRenderer(): type" << shape->type() << "not implemented";
@ -94,7 +94,7 @@ template<> void createDebugMesh(ShapeRenderer<3>* renderer, const Physics::Imple
}
template<UnsignedInt dimensions> ShapeRenderer<dimensions>::ShapeRenderer(Physics::AbstractObjectShape<dimensions>* shape, ResourceKey options, SceneGraph::DrawableGroup<dimensions>* drawables): SceneGraph::Drawable<dimensions>(shape->object(), drawables), options(ResourceManager::instance()->get<ShapeRendererOptions>(options)) {
template<UnsignedInt dimensions> ShapeRenderer<dimensions>::ShapeRenderer(Physics::AbstractShape<dimensions>* shape, ResourceKey options, SceneGraph::DrawableGroup<dimensions>* drawables): SceneGraph::Drawable<dimensions>(shape->object(), drawables), options(ResourceManager::instance()->get<ShapeRendererOptions>(options)) {
Implementation::createDebugMesh(this, Physics::Implementation::getAbstractShape(shape));
}

4
src/DebugTools/ShapeRenderer.h

@ -144,7 +144,7 @@ template<UnsignedInt dimensions> class MAGNUM_DEBUGTOOLS_EXPORT ShapeRenderer: p
public:
/**
* @brief Constructor
* @param shape Object for which to create debug renderer
* @param shape Shape for which to create debug renderer
* @param options Options resource key. See
* @ref ShapeRenderer-usage "class documentation" for more
* information.
@ -154,7 +154,7 @@ template<UnsignedInt dimensions> class MAGNUM_DEBUGTOOLS_EXPORT ShapeRenderer: p
* @p shape must be available for the whole lifetime of the renderer
* and if it is group, it must not change its internal structure.
*/
explicit ShapeRenderer(Physics::AbstractObjectShape<dimensions>* shape, ResourceKey options = ResourceKey(), SceneGraph::DrawableGroup<dimensions>* drawables = nullptr);
explicit ShapeRenderer(Physics::AbstractShape<dimensions>* shape, ResourceKey options = ResourceKey(), SceneGraph::DrawableGroup<dimensions>* drawables = nullptr);
~ShapeRenderer();

24
src/Physics/AbstractObjectShape.cpp → src/Physics/AbstractShape.cpp

@ -22,42 +22,42 @@
DEALINGS IN THE SOFTWARE.
*/
#include "AbstractObjectShape.h"
#include "AbstractShape.h"
#include <Utility/Debug.h>
#include "Physics/ObjectShapeGroup.h"
#include "Physics/ShapeGroup.h"
#include "Physics/Implementation/CollisionDispatch.h"
namespace Magnum { namespace Physics {
template<UnsignedInt dimensions> AbstractObjectShape<dimensions>::AbstractObjectShape(SceneGraph::AbstractObject<dimensions>* object, ObjectShapeGroup<dimensions>* group): SceneGraph::AbstractGroupedFeature<dimensions, AbstractObjectShape<dimensions>>(object, group) {
template<UnsignedInt dimensions> AbstractShape<dimensions>::AbstractShape(SceneGraph::AbstractObject<dimensions>* object, ShapeGroup<dimensions>* group): SceneGraph::AbstractGroupedFeature<dimensions, AbstractShape<dimensions>>(object, group) {
this->setCachedTransformations(SceneGraph::AbstractFeature<dimensions>::CachedTransformation::Absolute);
}
template<UnsignedInt dimensions> ObjectShapeGroup<dimensions>* AbstractObjectShape<dimensions>::group() {
return static_cast<ObjectShapeGroup<dimensions>*>(SceneGraph::AbstractGroupedFeature<dimensions, AbstractObjectShape<dimensions>>::group());
template<UnsignedInt dimensions> ShapeGroup<dimensions>* AbstractShape<dimensions>::group() {
return static_cast<ShapeGroup<dimensions>*>(SceneGraph::AbstractGroupedFeature<dimensions, AbstractShape<dimensions>>::group());
}
template<UnsignedInt dimensions> const ObjectShapeGroup<dimensions>* AbstractObjectShape<dimensions>::group() const {
return static_cast<const ObjectShapeGroup<dimensions>*>(SceneGraph::AbstractGroupedFeature<dimensions, AbstractObjectShape<dimensions>>::group());
template<UnsignedInt dimensions> const ShapeGroup<dimensions>* AbstractShape<dimensions>::group() const {
return static_cast<const ShapeGroup<dimensions>*>(SceneGraph::AbstractGroupedFeature<dimensions, AbstractShape<dimensions>>::group());
}
template<UnsignedInt dimensions> auto AbstractObjectShape<dimensions>::type() const -> Type {
template<UnsignedInt dimensions> auto AbstractShape<dimensions>::type() const -> Type {
return abstractTransformedShape()->type();
}
template<UnsignedInt dimensions> bool AbstractObjectShape<dimensions>::collides(const AbstractObjectShape<dimensions>* other) const {
template<UnsignedInt dimensions> bool AbstractShape<dimensions>::collides(const AbstractShape<dimensions>* other) const {
return Implementation::collides(abstractTransformedShape(), other->abstractTransformedShape());
}
template<UnsignedInt dimensions> void AbstractObjectShape<dimensions>::markDirty() {
template<UnsignedInt dimensions> void AbstractShape<dimensions>::markDirty() {
group()->setDirty();
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template class MAGNUM_PHYSICS_EXPORT AbstractObjectShape<2>;
template class MAGNUM_PHYSICS_EXPORT AbstractObjectShape<3>;
template class MAGNUM_PHYSICS_EXPORT AbstractShape<2>;
template class MAGNUM_PHYSICS_EXPORT AbstractShape<3>;
#endif
}}

34
src/Physics/AbstractObjectShape.h → src/Physics/AbstractShape.h

@ -1,5 +1,5 @@
#ifndef Magnum_Physics_AbstractObjectShape_h
#define Magnum_Physics_AbstractObjectShape_h
#ifndef Magnum_Physics_AbstractShape_h
#define Magnum_Physics_AbstractShape_h
/*
This file is part of Magnum.
@ -25,7 +25,7 @@
*/
/** @file
* @brief Class Magnum::Physics::AbstractObjectShape, typedef Magnum::Physics::AbstractObjectShape2D, Magnum::Physics::AbstractObjectShape3D
* @brief Class Magnum::Physics::AbstractShape, typedef Magnum::Physics::AbstractShape2D, Magnum::Physics::AbstractShape3D
*/
#include "Magnum.h"
@ -37,19 +37,19 @@
namespace Magnum { namespace Physics {
namespace Implementation {
template<UnsignedInt dimensions> inline const AbstractShape<dimensions>* getAbstractShape(const AbstractObjectShape<dimensions>* objectShape) {
return objectShape->abstractTransformedShape();
template<UnsignedInt dimensions> inline const AbstractShape<dimensions>* getAbstractShape(const Physics::AbstractShape<dimensions>* shape) {
return shape->abstractTransformedShape();
}
}
/**
@brief Base class for object shapes
This class is not directly instantiable, see ObjectShape instead.
@see AbstractObjectShape2D, AbstractObjectShape3D
This class is not directly instantiable, see Shape instead.
@see AbstractShape2D, AbstractShape3D
*/
template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT AbstractObjectShape: public SceneGraph::AbstractGroupedFeature<dimensions, AbstractObjectShape<dimensions>> {
friend const Implementation::AbstractShape<dimensions>* Implementation::getAbstractShape<>(const AbstractObjectShape<dimensions>* objectShape);
template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT AbstractShape: public SceneGraph::AbstractGroupedFeature<dimensions, AbstractShape<dimensions>> {
friend const Implementation::AbstractShape<dimensions>* Implementation::getAbstractShape<>(const AbstractShape<dimensions>*);
public:
enum: UnsignedInt {
@ -66,7 +66,7 @@ template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT AbstractObjectShape
Capsule, /**< Capsule */
AxisAlignedBox, /**< @ref AxisAlignedBox "Axis aligned box" */
Box, /**< Box */
ShapeGroup, /**< @ref ShapeGroup "Shape group" */
Composition, /**< @ref Composition "Shape group" */
Plane /**< Plane (3D only) */
};
#else
@ -78,15 +78,15 @@ template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT AbstractObjectShape
* @param object Object holding this feature
* @param group Group this shape belongs to
*/
explicit AbstractObjectShape(SceneGraph::AbstractObject<dimensions>* object, ObjectShapeGroup<dimensions>* group = nullptr);
explicit AbstractShape(SceneGraph::AbstractObject<dimensions>* object, ShapeGroup<dimensions>* group = nullptr);
/**
* @brief Object shape group containing this shape
* @brief Shape group containing this shape
*
* If the shape doesn't belong to any group, returns `nullptr`.
*/
ObjectShapeGroup<dimensions>* group();
const ObjectShapeGroup<dimensions>* group() const; /**< @overload */
ShapeGroup<dimensions>* group();
const ShapeGroup<dimensions>* group() const; /**< @overload */
/**
* @brief Shape type
@ -98,7 +98,7 @@ template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT AbstractObjectShape
*
* Default implementation returns false.
*/
bool collides(const AbstractObjectShape<dimensions>* other) const;
bool collides(const AbstractShape<dimensions>* other) const;
protected:
/** Marks also the group as dirty */
@ -109,10 +109,10 @@ template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT AbstractObjectShape
};
/** @brief Base class for two-dimensional object shapes */
typedef AbstractObjectShape<2> AbstractObjectShape2D;
typedef AbstractShape<2> AbstractShape2D;
/** @brief Base class for three-dimensional object shapes */
typedef AbstractObjectShape<3> AbstractObjectShape3D;
typedef AbstractShape<3> AbstractShape3D;
}}

14
src/Physics/CMakeLists.txt

@ -23,15 +23,15 @@
#
set(MagnumPhysics_SRCS
AbstractObjectShape.cpp
AbstractShape.cpp
AxisAlignedBox.cpp
Box.cpp
Capsule.cpp
Composition.cpp
Line.cpp
Plane.cpp
Point.cpp
ObjectShape.cpp
ObjectShapeGroup.cpp
Shape.cpp
ShapeGroup.cpp
Sphere.cpp
@ -40,18 +40,18 @@ set(MagnumPhysics_SRCS
Implementation/CollisionDispatch.cpp)
set(MagnumPhysics_HEADERS
AbstractObjectShape.h
AbstractShape.h
AxisAlignedBox.h
Box.h
Capsule.h
Composition.h
Line.h
LineSegment.h
ObjectShape.h
ObjectShapeGroup.h
Shape.h
ShapeGroup.h
Physics.h
Plane.h
Point.h
ShapeGroup.h
Sphere.h
magnumPhysicsVisibility.h

167
src/Physics/Composition.cpp

@ -0,0 +1,167 @@
/*
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 "Composition.h"
#include <algorithm>
#include <Utility/Assert.h>
#include "Physics/Implementation/CollisionDispatch.h"
namespace Magnum { namespace Physics {
/*
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> Composition<dimensions>::Composition(const Composition<dimensions>& other): _shapeCount(other._shapeCount), _nodeCount(other._nodeCount) {
copyShapes(0, other);
copyNodes(0, other);
}
template<UnsignedInt dimensions> Composition<dimensions>::Composition(Composition<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> Composition<dimensions>::~Composition() {
for(std::size_t i = 0; i != _shapeCount; ++i)
delete _shapes[i];
delete[] _shapes;
delete[] _nodes;
}
template<UnsignedInt dimensions> Composition<dimensions>& Composition<dimensions>::operator=(const Composition<dimensions>& other) {
for(std::size_t i = 0; i != _shapeCount; ++i)
delete _shapes[i];
if(_shapeCount != other._shapeCount) {
delete[] _shapes;
_shapeCount = other._shapeCount;
_shapes = new Implementation::AbstractShape<dimensions>*[_shapeCount];
}
if(_nodeCount != other._nodeCount) {
delete[] _nodes;
_nodeCount = other._nodeCount;
_nodes = new Node[_nodeCount];
}
copyShapes(0, other);
copyNodes(0, other);
return *this;
}
template<UnsignedInt dimensions> Composition<dimensions>& Composition<dimensions>::operator=(Composition<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> void Composition<dimensions>::copyShapes(const std::size_t offset, Composition<dimensions>&& other) {
std::move(other._shapes, other._shapes+other._shapeCount, _shapes+offset);
delete[] other._shapes;
other._shapes = nullptr;
other._shapeCount = 0;
}
template<UnsignedInt dimensions> void Composition<dimensions>::copyShapes(const std::size_t offset, const Composition<dimensions>& other) {
for(std::size_t i = 0; i != other._shapeCount; ++i)
_shapes[i+offset] = other._shapes[i]->clone();
}
template<UnsignedInt dimensions> void Composition<dimensions>::copyNodes(std::size_t offset, const Composition<dimensions>& other) {
std::copy(other._nodes, other._nodes+other._nodeCount, _nodes+offset);
}
template<UnsignedInt dimensions> Composition<dimensions> Composition<dimensions>::transformed(const typename DimensionTraits<dimensions>::MatrixType& matrix) const {
Composition<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 Composition<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 == CompositionOperation::Not)
return !collidesLeft;
/* Short-circuit evaluation for AND/OR */
if((_nodes[node].operation == CompositionOperation::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);
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template class MAGNUM_PHYSICS_EXPORT Composition<2>;
template class MAGNUM_PHYSICS_EXPORT Composition<3>;
#endif
}}

298
src/Physics/Composition.h

@ -0,0 +1,298 @@
#ifndef Magnum_Physics_Composition_h
#define Magnum_Physics_Composition_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.
*/
/** @file
* @brief Class Magnum::Physics::Composition, enum Magnum::Physics::CompositionOperation
*/
#include <type_traits>
#include <utility>
#include <Utility/Assert.h>
#include "DimensionTraits.h"
#include "Physics/Physics.h"
#include "Physics/magnumPhysicsVisibility.h"
#include "Physics/shapeImplementation.h"
namespace Magnum { namespace Physics {
namespace Implementation {
template<class> struct ShapeHelper;
template<UnsignedInt dimensions> inline AbstractShape<dimensions>* getAbstractShape(Composition<dimensions>& group, std::size_t i) {
return group._shapes[i];
}
template<UnsignedInt dimensions> inline const AbstractShape<dimensions>* getAbstractShape(const Composition<dimensions>& group, std::size_t i) {
return group._shapes[i];
}
}
/** @brief Shape operation */
enum class CompositionOperation: UnsignedByte {
Not, /**< Boolean NOT */
And, /**< Boolean AND */
Or /**< Boolean OR */
};
/**
@brief Composition of shapes
Result of logical operations on shapes.
See @ref collision-detection for brief introduction.
*/
template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT Composition {
friend Implementation::AbstractShape<dimensions>* Implementation::getAbstractShape<>(Composition<dimensions>&, std::size_t);
friend const Implementation::AbstractShape<dimensions>* Implementation::getAbstractShape<>(const Composition<dimensions>&, std::size_t);
friend struct Implementation::ShapeHelper<Composition<dimensions>>;
public:
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 Composition(): _shapeCount(0), _nodeCount(0), _shapes(nullptr), _nodes(nullptr) {}
/**
* @brief Unary operation constructor
* @param operation Unary operation
* @param a Operand
*/
template<class T> explicit Composition(CompositionOperation 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 Composition(CompositionOperation operation, T&& a, U&& b);
/** @brief Copy constructor */
Composition(const Composition<dimensions>& other);
/** @brief Move constructor */
Composition(Composition<dimensions>&& other);
~Composition();
/** @brief Assigment operator */
Composition<dimensions>& operator=(const Composition<dimensions>& other);
/** @brief Move assignment operator */
Composition<dimensions>& operator=(Composition<dimensions>&& other);
/** @brief Transformed shape */
Composition<dimensions> transformed(const typename DimensionTraits<dimensions>::MatrixType& matrix) const;
/** @brief Count of shapes in the hierarchy */
inline std::size_t size() const { return _shapeCount; }
/** @brief Type of shape at given position */
inline Type type(std::size_t i) const { return _shapes[i]->type(); }
/** @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:
struct Node {
std::size_t rightNode, rightShape;
CompositionOperation 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 Composition<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 Composition<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, Composition<dimensions>&& other);
void copyShapes(std::size_t offset, const Composition<dimensions>& other);
template<class T> inline void copyNodes(std::size_t, const T&) {}
void copyNodes(std::size_t offset, const Composition<dimensions>& other);
std::size_t _shapeCount, _nodeCount;
Implementation::AbstractShape<dimensions>** _shapes;
Node* _nodes;
};
/** @brief Two-dimensional shape hierarchy */
typedef Composition<2> Composition2D;
/** @brief Three-dimensional shape hierarchy */
typedef Composition<3> Composition3D;
#ifdef DOXYGEN_GENERATING_OUTPUT
/** @debugoperator{Magnum::Physics::Composition} */
template<UnsignedInt dimensions> Debug operator<<(Debug debug, typename Composition<dimensions>::Type value);
#endif
/** @relates Composition
@brief Collision of shape with Composition
*/
#ifdef DOXYGEN_GENERATING_OUTPUT
template<UnsignedInt dimensions, class T> inline bool operator%(const T& a, const Composition<dimensions>& b) {
#else
template<UnsignedInt dimensions, class T> inline auto operator%(const T& a, const Composition<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
/** @relates Composition
@brief Logical NOT of shape
*/
template<class T> inline Composition<T::Dimensions> operator!(T a);
/** @relates Composition
@brief Logical AND of two shapes
[Short-circuit evaluation](http://en.wikipedia.org/wiki/Short-circuit_evaluation)
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<class T> inline Composition<T::Dimensions> operator&&(T a, T b);
/** @relates Composition
@brief Logical OR of two shapes
[Short-circuit evaluation](http://en.wikipedia.org/wiki/Short-circuit_evaluation)
is used, so if collision with @p a is detected, collision with @p b is not
computed.
*/
template<class T> inline Composition<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, \
Composition<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, \
Composition<T::Dimensions>>::type
template<class T> inline auto operator!(T&& a) -> enableIfIsShapeType {
return Composition<T::Dimensions>(CompositionOperation::Not, std::forward<T>(a));
}
template<class T, class U> inline auto operator&&(T&& a, U&& b) -> enableIfAreShapeType {
return Composition<T::Dimensions>(CompositionOperation::And, std::forward<T>(a), std::forward<U>(b));
}
template<class T, class U> inline auto operator||(T&& a, U&& b) -> enableIfAreShapeType {
return Composition<T::Dimensions>(CompositionOperation::Or, std::forward<T>(a), std::forward<U>(b));
}
#undef enableIfIsShapeType
#undef enableIfAreShapeType
#endif
template<UnsignedInt dimensions> template<class T> Composition<dimensions>::Composition(CompositionOperation operation, T&& a): _shapeCount(shapeCount(a)), _nodeCount(nodeCount(a)+1), _shapes(new Implementation::AbstractShape<dimensions>*[_shapeCount]), _nodes(new Node[_nodeCount]) {
CORRADE_ASSERT(operation == CompositionOperation::Not,
"Physics::Composition::Composition(): 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> Composition<dimensions>::Composition(CompositionOperation 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 != CompositionOperation::Not,
"Physics::Composition::Composition(): 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& Composition<dimensions>::get(std::size_t i) const {
CORRADE_ASSERT(_shapes[i]->type() == Implementation::TypeOf<T>::type(),
"Physics::Composition::get(): given shape is not of type" << Implementation::TypeOf<T>::type() <<
"but" << _shapes[i]->type(), *static_cast<T*>(nullptr));
return static_cast<Implementation::Shape<T>*>(_shapes[i])->shape;
}
}}
#endif

58
src/Physics/ObjectShapeGroup.cpp

@ -1,58 +0,0 @@
/*
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 "ObjectShapeGroup.h"
#include "Physics/AbstractObjectShape.h"
namespace Magnum { namespace Physics {
template<UnsignedInt dimensions> void ObjectShapeGroup<dimensions>::setClean() {
/* Clean all objects */
if(!this->isEmpty()) {
std::vector<SceneGraph::AbstractObject<dimensions>*> objects(this->size());
for(std::size_t i = 0; i != this->size(); ++i)
objects[i] = (*this)[i]->object();
objects[0]->setClean(objects);
}
dirty = false;
}
template<UnsignedInt dimensions> AbstractObjectShape<dimensions>* ObjectShapeGroup<dimensions>::firstCollision(const AbstractObjectShape<dimensions>* shape) {
setClean();
for(std::size_t i = 0; i != this->size(); ++i)
if((*this)[i] != shape && (*this)[i]->collides(shape))
return (*this)[i];
return nullptr;
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template class MAGNUM_PHYSICS_EXPORT ObjectShapeGroup<2>;
template class MAGNUM_PHYSICS_EXPORT ObjectShapeGroup<3>;
#endif
}}

113
src/Physics/ObjectShapeGroup.h

@ -1,113 +0,0 @@
#ifndef Magnum_Physics_ObjectShapeGroup_h
#define Magnum_Physics_ObjectShapeGroup_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.
*/
/** @file
* @brief Class Magnum::Physics::ObjectShapeGroup, typedef Magnum::Physics::ObjectShapeGroup2D, Magnum::Physics::ObjectShapeGroup3D
*/
#include <vector>
#include "Physics/Physics.h"
#include "SceneGraph/FeatureGroup.h"
#include "magnumPhysicsVisibility.h"
namespace Magnum { namespace Physics {
/**
@brief Group of object shapes
See ObjectShape for more information.
@see @ref scenegraph, ObjectShapeGroup2D, ObjectShapeGroup3D
*/
template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT ObjectShapeGroup: public SceneGraph::FeatureGroup<dimensions, AbstractObjectShape<dimensions>> {
friend class AbstractObjectShape<dimensions>;
public:
/**
* @brief Constructor
*
* Marks the group as dirty.
*/
inline explicit ObjectShapeGroup(): dirty(true) {}
/**
* @brief Whether the group is dirty
* @return True if any object in the group is dirty, false otherwise.
*/
inline bool isDirty() const { return dirty; }
/**
* @brief Set the group as dirty
*
* If some body in the group changes its transformation, it sets dirty
* status also on the group to indicate that the body and maybe also
* group state needs to be cleaned before computing collisions.
*
* @see setClean()
*/
inline void setDirty() { dirty = true; }
/**
* @brief Set the group and all bodies as clean
*
* This function is called before computing any collisions to ensure
* all objects are cleaned.
*/
void setClean();
/**
* @brief First collision of given shape with other shapes in the group
*
* Returns first shape colliding with given one. If there aren't any
* collisions, returns `nullptr`. Calls setClean() before the
* operation.
*/
AbstractObjectShape<dimensions>* firstCollision(const AbstractObjectShape<dimensions>* shape);
private:
bool dirty;
};
/**
@brief Group of two-dimensional shaped objects
See ObjectShape for more information.
@see ObjectShapeGroup3D
*/
typedef ObjectShapeGroup<2> ObjectShapeGroup2D;
/**
@brief Group of three-dimensional shaped objects
See ObjectShape for more information.
@see ObjectShapeGroup2D
*/
typedef ObjectShapeGroup<3> ObjectShapeGroup3D;
}}
#endif

28
src/Physics/Physics.h

@ -34,9 +34,9 @@ namespace Magnum { namespace Physics {
/** @todoc remove when doxygen is sane again */
#ifndef DOXYGEN_GENERATING_OUTPUT
template<UnsignedInt> class AbstractObjectShape;
typedef AbstractObjectShape<2> AbstractObjectShape2D;
typedef AbstractObjectShape<3> AbstractObjectShape3D;
template<UnsignedInt> class AbstractShape;
typedef AbstractShape<2> AbstractShape2D;
typedef AbstractShape<3> AbstractShape3D;
template<UnsignedInt> class AxisAlignedBox;
typedef AxisAlignedBox<2> AxisAlignedBox2D;
@ -50,6 +50,10 @@ template<UnsignedInt> class Capsule;
typedef Capsule<2> Capsule2D;
typedef Capsule<3> Capsule3D;
template<UnsignedInt> class Composition;
typedef Composition<2> Composition2D;
typedef Composition<3> Composition3D;
template<UnsignedInt> class Line;
typedef Line<2> Line2D;
typedef Line<3> Line3D;
@ -58,17 +62,7 @@ template<UnsignedInt> class LineSegment;
typedef LineSegment<2> LineSegment2D;
typedef LineSegment<3> LineSegment3D;
template<class> class ObjectShape;
template<UnsignedInt> class ObjectShapeGroup;
typedef ObjectShapeGroup<2> ObjectShapeGroup2D;
typedef ObjectShapeGroup<3> ObjectShapeGroup3D;
class Plane;
template<UnsignedInt> class Point;
typedef Point<2> Point2D;
typedef Point<3> Point3D;
template<class> class Shape;
template<UnsignedInt> class ShapeGroup;
typedef ShapeGroup<2> ShapeGroup2D;
@ -79,6 +73,12 @@ typedef Sphere<2> Sphere2D;
typedef Sphere<3> Sphere3D;
#endif
class Plane;
template<UnsignedInt> class Point;
typedef Point<2> Point2D;
typedef Point<3> Point3D;
}}
#endif

24
src/Physics/ObjectShape.cpp → src/Physics/Shape.cpp

@ -22,27 +22,27 @@
DEALINGS IN THE SOFTWARE.
*/
#include "ObjectShape.h"
#include "Shape.h"
#include "Physics/ShapeGroup.h"
#include "Physics/Composition.h"
namespace Magnum { namespace Physics { namespace Implementation {
template<UnsignedInt dimensions> void ObjectShapeHelper<ShapeGroup<dimensions>>::set(ObjectShape<ShapeGroup<dimensions>>& objectShape, const ShapeGroup<dimensions>& shape) {
objectShape._transformedShape.shape = objectShape._shape.shape = shape;
template<UnsignedInt dimensions> void ShapeHelper<Composition<dimensions>>::set(Physics::Shape<Composition<dimensions>>& shape, const Composition<dimensions>& composition) {
shape._transformedShape.shape = shape._shape.shape = composition;
}
template<UnsignedInt dimensions> void ObjectShapeHelper<ShapeGroup<dimensions>>::set(ObjectShape<ShapeGroup<dimensions>>& objectShape, ShapeGroup<dimensions>&& shape) {
objectShape._transformedShape.shape = objectShape._shape.shape = std::move(shape);
template<UnsignedInt dimensions> void ShapeHelper<Composition<dimensions>>::set(Physics::Shape<Composition<dimensions>>& shape, Composition<dimensions>&& composition) {
shape._transformedShape.shape = shape._shape.shape = std::move(composition);
}
template<UnsignedInt dimensions> void ObjectShapeHelper<ShapeGroup<dimensions>>::transform(ObjectShape<ShapeGroup<dimensions>>& objectShape, const typename DimensionTraits<dimensions>::MatrixType& absoluteTransformationMatrix) {
CORRADE_INTERNAL_ASSERT(objectShape._shape.shape.size() == objectShape._transformedShape.shape.size());
for(std::size_t i = 0; i != objectShape.shape().size(); ++i)
objectShape._shape.shape._shapes[i]->transform(absoluteTransformationMatrix, objectShape._transformedShape.shape._shapes[i]);
template<UnsignedInt dimensions> void ShapeHelper<Composition<dimensions>>::transform(Physics::Shape<Composition<dimensions>>& shape, const typename DimensionTraits<dimensions>::MatrixType& absoluteTransformationMatrix) {
CORRADE_INTERNAL_ASSERT(shape._shape.shape.size() == shape._transformedShape.shape.size());
for(std::size_t i = 0; i != shape.shape().size(); ++i)
shape._shape.shape._shapes[i]->transform(absoluteTransformationMatrix, shape._transformedShape.shape._shapes[i]);
}
template struct MAGNUM_PHYSICS_EXPORT ObjectShapeHelper<ShapeGroup<2>>;
template struct MAGNUM_PHYSICS_EXPORT ObjectShapeHelper<ShapeGroup<3>>;
template struct MAGNUM_PHYSICS_EXPORT ShapeHelper<Composition<2>>;
template struct MAGNUM_PHYSICS_EXPORT ShapeHelper<Composition<3>>;
}}}

70
src/Physics/ObjectShape.h → src/Physics/Shape.h

@ -1,5 +1,5 @@
#ifndef Magnum_Physics_ObjectShape_h
#define Magnum_Physics_ObjectShape_h
#ifndef Magnum_Physics_Shape_h
#define Magnum_Physics_Shape_h
/*
This file is part of Magnum.
@ -25,10 +25,10 @@
*/
/** @file
* @brief Class Magnum::Physics::ObjectShape
* @brief Class Magnum::Physics::Shape
*/
#include "Physics/AbstractObjectShape.h"
#include "Physics/AbstractShape.h"
#include "Physics/Physics.h"
#include "magnumPhysicsVisibility.h"
@ -36,33 +36,33 @@
namespace Magnum { namespace Physics {
namespace Implementation {
template<class> struct ObjectShapeHelper;
template<class> struct ShapeHelper;
}
/**
@brief Object shape
Adds shape for collision detection to object. Each %ObjectShape is part of
some ObjectShapeGroup, which essentially maintains a set of objects which can
Adds shape for collision detection to object. Each %Shape is part of
some ShapeGroup, which essentially maintains a set of objects which can
collide with each other.
@section ObjectShape-usage Usage
@section Shape-usage Usage
Add the feature to the object and some shape group (you can also use
ObjectShapeGroup::add() and ObjectShapeGroup::remove() later) and configure the
ShapeGroup::add() and ShapeGroup::remove() later) and configure the
shape.
@code
Physics::ObjectShapeGroup3D shapes;
Physics::ShapeGroup3D shapes;
Object3D* object;
auto shape = new Physics::ObjectShape<Physics::Sphere3D>(object, {{}, 0.75f}, &shapes);
auto shape = new Physics::Shape<Physics::Sphere3D>(object, {{}, 0.75f}, &shapes);
@endcode
@see @ref scenegraph, ObjectShapeGroup2D, ObjectShapeGroup3D,
@see @ref scenegraph, ShapeGroup2D, ShapeGroup3D,
DebugTools::ShapeRenderer
*/
template<class T> class MAGNUM_PHYSICS_EXPORT ObjectShape: public AbstractObjectShape<T::Dimensions> {
friend struct Implementation::ObjectShapeHelper<T>;
template<class T> class MAGNUM_PHYSICS_EXPORT Shape: public AbstractShape<T::Dimensions> {
friend struct Implementation::ShapeHelper<T>;
public:
/**
@ -71,17 +71,17 @@ template<class T> class MAGNUM_PHYSICS_EXPORT ObjectShape: public AbstractObject
* @param shape Shape
* @param group Group this shape belongs to
*/
template<class ...U> explicit ObjectShape(SceneGraph::AbstractObject<T::Dimensions>* object, const T& shape, ObjectShapeGroup<T::Dimensions>* group = nullptr): AbstractObjectShape<T::Dimensions>(object, group) {
Implementation::ObjectShapeHelper<T>::set(*this, shape);
template<class ...U> explicit Shape(SceneGraph::AbstractObject<T::Dimensions>* object, const T& shape, ShapeGroup<T::Dimensions>* group = nullptr): AbstractShape<T::Dimensions>(object, group) {
Implementation::ShapeHelper<T>::set(*this, shape);
}
/** @overload */
template<class ...U> explicit ObjectShape(SceneGraph::AbstractObject<T::Dimensions>* object, T&& shape, ObjectShapeGroup<T::Dimensions>* group = nullptr): AbstractObjectShape<T::Dimensions>(object, group) {
Implementation::ObjectShapeHelper<T>::set(*this, std::move(shape));
template<class ...U> explicit Shape(SceneGraph::AbstractObject<T::Dimensions>* object, T&& shape, ShapeGroup<T::Dimensions>* group = nullptr): AbstractShape<T::Dimensions>(object, group) {
Implementation::ShapeHelper<T>::set(*this, std::move(shape));
}
/** @overload */
template<class ...U> explicit ObjectShape(SceneGraph::AbstractObject<T::Dimensions>* object, ObjectShapeGroup<T::Dimensions>* group = nullptr): AbstractObjectShape<T::Dimensions>(object, group) {}
template<class ...U> explicit Shape(SceneGraph::AbstractObject<T::Dimensions>* object, ShapeGroup<T::Dimensions>* group = nullptr): AbstractShape<T::Dimensions>(object, group) {}
/** @brief Shape */
inline const T& shape() const { return _shape.shape; }
@ -92,7 +92,7 @@ template<class T> class MAGNUM_PHYSICS_EXPORT ObjectShape: public AbstractObject
*
* Marks the feature as dirty.
*/
ObjectShape<T>* setShape(const T& shape);
Shape<T>* setShape(const T& shape);
/**
* @brief Transformed shape
@ -116,41 +116,41 @@ template<class T> class MAGNUM_PHYSICS_EXPORT ObjectShape: public AbstractObject
Implementation::Shape<T> _shape, _transformedShape;
};
template<class T> inline ObjectShape<T>* ObjectShape<T>::setShape(const T& shape) {
Implementation::ObjectShapeHelper<T>::set(*this, shape);
template<class T> inline Shape<T>* Shape<T>::setShape(const T& shape) {
Implementation::ShapeHelper<T>::set(*this, shape);
this->object()->setDirty();
return this;
}
template<class T> inline const T& ObjectShape<T>::transformedShape() {
template<class T> inline const T& Shape<T>::transformedShape() {
this->object()->setClean();
return _transformedShape.shape;
}
template<class T> void ObjectShape<T>::markDirty() {
template<class T> void Shape<T>::markDirty() {
if(this->group()) this->group()->setDirty();
}
template<class T> void ObjectShape<T>::clean(const typename DimensionTraits<T::Dimensions>::MatrixType& absoluteTransformationMatrix) {
Implementation::ObjectShapeHelper<T>::transform(*this, absoluteTransformationMatrix);
template<class T> void Shape<T>::clean(const typename DimensionTraits<T::Dimensions>::MatrixType& absoluteTransformationMatrix) {
Implementation::ShapeHelper<T>::transform(*this, absoluteTransformationMatrix);
}
namespace Implementation {
template<class T> struct ObjectShapeHelper {
inline static void set(ObjectShape<T>& objectShape, const T& shape) {
objectShape._shape.shape = shape;
template<class T> struct ShapeHelper {
inline static void set(Physics::Shape<T>& shape, const T& s) {
shape._shape.shape = s;
}
inline static void transform(ObjectShape<T>& objectShape, const typename DimensionTraits<T::Dimensions>::MatrixType& absoluteTransformationMatrix) {
objectShape._transformedShape.shape = objectShape._shape.shape.transformed(absoluteTransformationMatrix);
inline static void transform(Physics::Shape<T>& shape, const typename DimensionTraits<T::Dimensions>::MatrixType& absoluteTransformationMatrix) {
shape._transformedShape.shape = shape._shape.shape.transformed(absoluteTransformationMatrix);
}
};
template<UnsignedInt dimensions> struct MAGNUM_PHYSICS_EXPORT ObjectShapeHelper<ShapeGroup<dimensions>> {
static void set(ObjectShape<ShapeGroup<dimensions>>& objectShape, const ShapeGroup<dimensions>& shape);
static void set(ObjectShape<ShapeGroup<dimensions>>& objectShape, ShapeGroup<dimensions>&& shape);
template<UnsignedInt dimensions> struct MAGNUM_PHYSICS_EXPORT ShapeHelper<Composition<dimensions>> {
static void set(Physics::Shape<Composition<dimensions>>& shape, const Composition<dimensions>& composition);
static void set(Physics::Shape<Composition<dimensions>>& shape, Composition<dimensions>&& composition);
static void transform(ObjectShape<ShapeGroup<dimensions>>& objectShape, const typename DimensionTraits<dimensions>::MatrixType& absoluteTransformationMatrix);
static void transform(Physics::Shape<Composition<dimensions>>& shape, const typename DimensionTraits<dimensions>::MatrixType& absoluteTransformationMatrix);
};
}

139
src/Physics/ShapeGroup.cpp

@ -24,139 +24,30 @@
#include "ShapeGroup.h"
#include <algorithm>
#include <Utility/Assert.h>
#include "Physics/Implementation/CollisionDispatch.h"
#include "Physics/AbstractShape.h"
namespace Magnum { namespace Physics {
/*
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() {
for(std::size_t i = 0; i != _shapeCount; ++i)
delete _shapes[i];
delete[] _shapes;
delete[] _nodes;
}
template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(const ShapeGroup<dimensions>& other) {
for(std::size_t i = 0; i != _shapeCount; ++i)
delete _shapes[i];
template<UnsignedInt dimensions> void ShapeGroup<dimensions>::setClean() {
/* Clean all objects */
if(!this->isEmpty()) {
std::vector<SceneGraph::AbstractObject<dimensions>*> objects(this->size());
for(std::size_t i = 0; i != this->size(); ++i)
objects[i] = (*this)[i]->object();
if(_shapeCount != other._shapeCount) {
delete[] _shapes;
_shapeCount = other._shapeCount;
_shapes = new Implementation::AbstractShape<dimensions>*[_shapeCount];
objects[0]->setClean(objects);
}
if(_nodeCount != other._nodeCount) {
delete[] _nodes;
_nodeCount = other._nodeCount;
_nodes = new Node[_nodeCount];
}
copyShapes(0, other);
copyNodes(0, other);
return *this;
}
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> 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;
}
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();
dirty = false;
}
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;
template<UnsignedInt dimensions> AbstractShape<dimensions>* ShapeGroup<dimensions>::firstCollision(const AbstractShape<dimensions>* shape) {
setClean();
for(std::size_t i = 0; i != this->size(); ++i)
if((*this)[i] != shape && (*this)[i]->collides(shape))
return (*this)[i];
/* 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);
return nullptr;
}
#ifndef DOXYGEN_GENERATING_OUTPUT

285
src/Physics/ShapeGroup.h

@ -25,273 +25,88 @@
*/
/** @file
* @brief Class Magnum::Physics::ShapeGroup, enum Magnum::Physics::ShapeOperation
* @brief Class Magnum::Physics::ShapeGroup, typedef Magnum::Physics::ShapeGroup2D, Magnum::Physics::ShapeGroup3D
*/
#include <type_traits>
#include <utility>
#include <Utility/Assert.h>
#include <vector>
#include "DimensionTraits.h"
#include "Physics/Physics.h"
#include "Physics/magnumPhysicsVisibility.h"
#include "Physics/shapeImplementation.h"
#include "SceneGraph/FeatureGroup.h"
namespace Magnum { namespace Physics {
namespace Implementation {
template<class> struct ObjectShapeHelper;
#include "magnumPhysicsVisibility.h"
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];
}
}
/** @brief Shape operation */
enum class ShapeOperation: UnsignedByte {
Not, /**< Boolean NOT */
And, /**< Boolean AND */
Or /**< Boolean OR */
};
namespace Magnum { namespace Physics {
/**
@brief Shape group
@brief Group of shapes
Result of logical operations on shapes.
See @ref collision-detection for brief introduction.
See Shape for more information.
@see @ref scenegraph, ShapeGroup2D, ShapeGroup3D
*/
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>>;
template<UnsignedInt dimensions> class MAGNUM_PHYSICS_EXPORT ShapeGroup: public SceneGraph::FeatureGroup<dimensions, AbstractShape<dimensions>> {
friend class AbstractShape<dimensions>;
public:
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
* @brief Constructor
*
* Creates empty hierarchy.
* Marks the group as dirty.
*/
inline explicit ShapeGroup(): _shapeCount(0), _nodeCount(0), _shapes(nullptr), _nodes(nullptr) {}
inline explicit ShapeGroup(): dirty(true) {}
/**
* @brief Unary operation constructor
* @param operation Unary operation
* @param a Operand
* @brief Whether the group is dirty
* @return True if any object in the group is dirty, false otherwise.
*/
template<class T> explicit ShapeGroup(ShapeOperation operation, T&& a);
inline bool isDirty() const { return dirty; }
/**
* @brief Binary operation constructor
* @param operation Binary operation
* @param a Left operand
* @param b Right operand
* @brief Set the group as dirty
*
* If some body in the group changes its transformation, it sets dirty
* status also on the group to indicate that the body and maybe also
* group state needs to be cleaned before computing collisions.
*
* @see setClean()
*/
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<dimensions>&& other);
~ShapeGroup();
/** @brief Assigment operator */
ShapeGroup<dimensions>& operator=(const ShapeGroup<dimensions>& other);
/** @brief Move assignment operator */
ShapeGroup<dimensions>& operator=(ShapeGroup<dimensions>&& other);
inline void setDirty() { dirty = true; }
/** @brief Transformed shape */
ShapeGroup<dimensions> transformed(const typename DimensionTraits<dimensions>::MatrixType& matrix) const;
/** @brief Count of shapes in the hierarchy */
inline std::size_t size() const { return _shapeCount; }
/** @brief Type of shape at given position */
inline Type type(std::size_t i) const { return _shapes[i]->type(); }
/** @brief Shape at given position */
template<class T> const T& get(std::size_t i) const;
/**
* @brief Set the group and all bodies as clean
*
* This function is called before computing any collisions to ensure
* all objects are cleaned.
*/
void setClean();
/** @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);
}
/**
* @brief First collision of given shape with other shapes in the group
*
* Returns first shape colliding with given one. If there aren't any
* collisions, returns `nullptr`. Calls setClean() before the
* operation.
*/
AbstractShape<dimensions>* firstCollision(const AbstractShape<dimensions>* shape);
private:
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);
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;
bool dirty;
};
/** @brief Two-dimensional shape hierarchy */
typedef ShapeGroup<2> ShapeGroup2D;
/** @brief Three-dimensional shape hierarchy */
typedef ShapeGroup<3> ShapeGroup3D;
#ifdef DOXYGEN_GENERATING_OUTPUT
/** @debugoperator{Magnum::Physics::ShapeGroup} */
template<UnsignedInt dimensions> Debug operator<<(Debug debug, typename ShapeGroup<dimensions>::Type value);
#endif
/** @relates ShapeGroup
@brief Collision of shape with ShapeGroup
*/
#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
/** @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
/**
@brief Group of two-dimensional shaped objects
[Short-circuit evaluation](http://en.wikipedia.org/wiki/Short-circuit_evaluation)
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.
See Shape for more information.
@see ShapeGroup3D
*/
template<class T> inline ShapeGroup<T::Dimensions> operator&&(T a, T b);
typedef ShapeGroup<2> ShapeGroup2D;
/** @relates ShapeGroup
@brief Logical OR of two shapes
/**
@brief Group of three-dimensional shaped objects
[Short-circuit evaluation](http://en.wikipedia.org/wiki/Short-circuit_evaluation)
is used, so if collision with @p a is detected, collision with @p b is not
computed.
See Shape for more information.
@see ShapeGroup2D
*/
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));
}
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
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() <<
"but" << _shapes[i]->type(), *static_cast<T*>(nullptr));
return static_cast<Implementation::Shape<T>*>(_shapes[i])->shape;
}
typedef ShapeGroup<3> ShapeGroup3D;
}}

4
src/Physics/Test/CMakeLists.txt

@ -29,7 +29,7 @@ corrade_add_test(PhysicsCapsuleTest CapsuleTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsLineTest LineTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsPlaneTest PlaneTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsPointTest PointTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsShapeGroupTest ShapeGroupTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsCompositionTest CompositionTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsSphereTest SphereTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsObjectShapeTest ObjectShapeTest.cpp LIBRARIES MagnumPhysics)
corrade_add_test(PhysicsShapeTest ShapeTest.cpp LIBRARIES MagnumPhysics)

64
src/Physics/Test/ShapeGroupTest.cpp → src/Physics/Test/CompositionTest.cpp

@ -27,16 +27,16 @@
#include "Math/Matrix4.h"
#include "Physics/Point.h"
#include "Physics/AxisAlignedBox.h"
#include "Physics/ShapeGroup.h"
#include "Physics/Composition.h"
#include "Physics/Sphere.h"
#include "ShapeTestBase.h"
namespace Magnum { namespace Physics { namespace Test {
class ShapeGroupTest: public Corrade::TestSuite::Tester {
class CompositionTest: public Corrade::TestSuite::Tester {
public:
ShapeGroupTest();
CompositionTest();
void negated();
void anded();
@ -46,31 +46,31 @@ class ShapeGroupTest: public Corrade::TestSuite::Tester {
void empty();
};
ShapeGroupTest::ShapeGroupTest() {
addTests({&ShapeGroupTest::negated,
&ShapeGroupTest::anded,
&ShapeGroupTest::ored,
&ShapeGroupTest::multipleUnary,
&ShapeGroupTest::hierarchy,
&ShapeGroupTest::empty});
CompositionTest::CompositionTest() {
addTests({&CompositionTest::negated,
&CompositionTest::anded,
&CompositionTest::ored,
&CompositionTest::multipleUnary,
&CompositionTest::hierarchy,
&CompositionTest::empty});
}
void ShapeGroupTest::negated() {
const Physics::ShapeGroup2D a = !Physics::Point2D(Vector2::xAxis(0.5f));
void CompositionTest::negated() {
const Physics::Composition2D a = !Physics::Point2D(Vector2::xAxis(0.5f));
CORRADE_COMPARE(a.size(), 1);
CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Point);
CORRADE_COMPARE(a.type(0), Composition2D::Type::Point);
CORRADE_COMPARE(a.get<Physics::Point2D>(0).position(), Vector2::xAxis(0.5f));
VERIFY_NOT_COLLIDES(a, Physics::Sphere2D({}, 1.0f));
}
void ShapeGroupTest::anded() {
const Physics::ShapeGroup2D a = Physics::Sphere2D({}, 1.0f) && Physics::Point2D(Vector2::xAxis(0.5f));
void CompositionTest::anded() {
const Physics::Composition2D 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.type(0), Composition2D::Type::Sphere);
CORRADE_COMPARE(a.type(1), Composition2D::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));
@ -79,12 +79,12 @@ void ShapeGroupTest::anded() {
VERIFY_COLLIDES(a, Physics::Sphere2D(Vector2::xAxis(0.5f), 0.25f));
}
void ShapeGroupTest::ored() {
const Physics::ShapeGroup2D a = Physics::Sphere2D({}, 1.0f) || Physics::Point2D(Vector2::xAxis(1.5f));
void CompositionTest::ored() {
const Physics::Composition2D a = Physics::Sphere2D({}, 1.0f) || Physics::Point2D(Vector2::xAxis(1.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.type(0), Composition2D::Type::Sphere);
CORRADE_COMPARE(a.type(1), Composition2D::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));
@ -93,32 +93,32 @@ void ShapeGroupTest::ored() {
VERIFY_COLLIDES(a, Physics::Sphere2D(Vector2::xAxis(1.5f), 0.25f));
}
void ShapeGroupTest::multipleUnary() {
const Physics::ShapeGroup2D a = !!!!Physics::Point2D(Vector2::xAxis(0.5f));
void CompositionTest::multipleUnary() {
const Physics::Composition2D a = !!!!Physics::Point2D(Vector2::xAxis(0.5f));
CORRADE_COMPARE(a.size(), 1);
CORRADE_COMPARE(a.type(0), ShapeGroup2D::Type::Point);
CORRADE_COMPARE(a.type(0), Composition2D::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) &&
void CompositionTest::hierarchy() {
const Physics::Composition3D 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.type(0), Composition3D::Type::Sphere);
CORRADE_COMPARE(a.type(1), Composition3D::Type::Point);
CORRADE_COMPARE(a.type(2), Composition3D::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)));
}
void ShapeGroupTest::empty() {
const Physics::ShapeGroup2D a;
void CompositionTest::empty() {
const Physics::Composition2D a;
CORRADE_COMPARE(a.size(), 0);
@ -127,4 +127,4 @@ void ShapeGroupTest::empty() {
}}}
CORRADE_TEST_MAIN(Magnum::Physics::Test::ShapeGroupTest)
CORRADE_TEST_MAIN(Magnum::Physics::Test::CompositionTest)

4
src/Physics/Test/ShapeImplementationTest.cpp

@ -42,8 +42,8 @@ ShapeImplementationTest::ShapeImplementationTest() {
void ShapeImplementationTest::debug() {
std::ostringstream o;
Debug(&o) << Implementation::ShapeDimensionTraits<2>::Type::ShapeGroup;
CORRADE_COMPARE(o.str(), "Physics::Shape2D::Type::ShapeGroup\n");
Debug(&o) << Implementation::ShapeDimensionTraits<2>::Type::Composition;
CORRADE_COMPARE(o.str(), "Physics::Shape2D::Type::Composition\n");
o.str({});
Debug(&o) << Implementation::ShapeDimensionTraits<3>::Type::Plane;

44
src/Physics/Test/ObjectShapeTest.cpp → src/Physics/Test/ShapeTest.cpp

@ -24,10 +24,10 @@
#include <TestSuite/Tester.h>
#include "Physics/ObjectShapeGroup.h"
#include "Physics/ObjectShape.h"
#include "Physics/Point.h"
#include "Physics/ShapeGroup.h"
#include "Physics/Shape.h"
#include "Physics/Point.h"
#include "Physics/Composition.h"
#include "Physics/Sphere.h"
#include "SceneGraph/MatrixTransformation2D.h"
#include "SceneGraph/MatrixTransformation3D.h"
@ -35,9 +35,9 @@
namespace Magnum { namespace Physics { namespace Test {
class ObjectShapeTest: public Corrade::TestSuite::Tester {
class ShapeTest: public Corrade::TestSuite::Tester {
public:
ObjectShapeTest();
ShapeTest();
void clean();
void firstCollision();
@ -49,22 +49,22 @@ typedef SceneGraph::Object<SceneGraph::MatrixTransformation2D<>> Object2D;
typedef SceneGraph::Scene<SceneGraph::MatrixTransformation3D<>> Scene3D;
typedef SceneGraph::Object<SceneGraph::MatrixTransformation3D<>> Object3D;
ObjectShapeTest::ObjectShapeTest() {
addTests({&ObjectShapeTest::clean,
&ObjectShapeTest::firstCollision,
&ObjectShapeTest::shapeGroup});
ShapeTest::ShapeTest() {
addTests({&ShapeTest::clean,
&ShapeTest::firstCollision,
&ShapeTest::shapeGroup});
}
void ObjectShapeTest::clean() {
void ShapeTest::clean() {
Scene3D scene;
ObjectShapeGroup3D shapes;
ShapeGroup3D shapes;
Object3D a(&scene);
auto shape = new Physics::ObjectShape<Physics::Point3D>(&a, {{1.0f, -2.0f, 3.0f}}, &shapes);
auto shape = new Physics::Shape<Physics::Point3D>(&a, {{1.0f, -2.0f, 3.0f}}, &shapes);
a.scale(Vector3(-2.0f));
Object3D b(&scene);
new Physics::ObjectShape<Physics::Point3D>(&b, &shapes);
new Physics::Shape<Physics::Point3D>(&b, &shapes);
/* Everything is dirty at the beginning */
CORRADE_VERIFY(shapes.isDirty());
@ -95,18 +95,18 @@ void ObjectShapeTest::clean() {
CORRADE_VERIFY(b.isDirty());
}
void ObjectShapeTest::firstCollision() {
void ShapeTest::firstCollision() {
Scene3D scene;
ObjectShapeGroup3D shapes;
ShapeGroup3D shapes;
Object3D a(&scene);
auto aShape = new ObjectShape<Physics::Sphere3D>(&a, {{1.0f, -2.0f, 3.0f}, 1.5f}, &shapes);
auto aShape = new Shape<Physics::Sphere3D>(&a, {{1.0f, -2.0f, 3.0f}, 1.5f}, &shapes);
Object3D b(&scene);
auto bShape = new ObjectShape<Physics::Point3D>(&b, {{3.0f, -2.0f, 3.0f}}, &shapes);
auto bShape = new Shape<Physics::Point3D>(&b, {{3.0f, -2.0f, 3.0f}}, &shapes);
Object3D c(&scene);
new ObjectShape<Physics::ShapeGroup3D>(&c, &shapes);
new Shape<Physics::Composition3D>(&c, &shapes);
/* No collisions initially */
CORRADE_VERIFY(!shapes.firstCollision(aShape));
@ -123,13 +123,13 @@ void ObjectShapeTest::firstCollision() {
CORRADE_VERIFY(!shapes.isDirty());
}
void ObjectShapeTest::shapeGroup() {
void ShapeTest::shapeGroup() {
Scene2D scene;
ObjectShapeGroup2D shapes;
ShapeGroup2D shapes;
/* Verify construction */
Object2D a(&scene);
auto shape = new ObjectShape<Physics::ShapeGroup2D>(&a, Physics::Sphere2D({}, 0.5f) || Physics::Point2D({0.25f, -1.0f}));
auto shape = new Shape<Physics::Composition2D>(&a, Physics::Sphere2D({}, 0.5f) || Physics::Point2D({0.25f, -1.0f}));
CORRADE_COMPARE(shape->transformedShape().size(), 2);
/* Verify the original shape is updated */
@ -141,4 +141,4 @@ void ObjectShapeTest::shapeGroup() {
}}}
CORRADE_TEST_MAIN(Magnum::Physics::Test::ObjectShapeTest)
CORRADE_TEST_MAIN(Magnum::Physics::Test::ShapeTest)

4
src/Physics/shapeImplementation.cpp

@ -38,7 +38,7 @@ Debug operator<<(Debug debug, ShapeDimensionTraits<2>::Type value) {
_val(Capsule)
_val(AxisAlignedBox)
_val(Box)
_val(ShapeGroup)
_val(Composition)
#undef _val
}
@ -56,7 +56,7 @@ Debug operator<<(Debug debug, ShapeDimensionTraits<3>::Type value) {
_val(AxisAlignedBox)
_val(Box)
_val(Plane)
_val(ShapeGroup)
_val(Composition)
#undef _val
}

8
src/Physics/shapeImplementation.h

@ -47,7 +47,7 @@ template<> struct ShapeDimensionTraits<2> {
Capsule = 7,
AxisAlignedBox = 11,
Box = 13,
ShapeGroup = 17
Composition = 17
};
};
@ -61,7 +61,7 @@ template<> struct ShapeDimensionTraits<3> {
AxisAlignedBox = 11,
Box = 13,
Plane = 17,
ShapeGroup = 19
Composition = 19
};
};
@ -112,9 +112,9 @@ template<> struct TypeOf<Physics::Plane> {
return ShapeDimensionTraits<3>::Type::Plane;
}
};
template<UnsignedInt dimensions> struct TypeOf<Physics::ShapeGroup<dimensions>> {
template<UnsignedInt dimensions> struct TypeOf<Physics::Composition<dimensions>> {
inline constexpr static typename ShapeDimensionTraits<dimensions>::Type type() {
return ShapeDimensionTraits<dimensions>::Type::ShapeGroup;
return ShapeDimensionTraits<dimensions>::Type::Composition;
}
};

Loading…
Cancel
Save