|
24 | 24 |
|
25 | 25 | #include "ShapeGroup.h"
|
26 | 26 |
|
| 27 | +#include <algorithm> |
| 28 | +#include <Utility/Assert.h> |
| 29 | + |
| 30 | +#include "Physics/Implementation/CollisionDispatch.h" |
| 31 | + |
27 | 32 | namespace Magnum { namespace Physics {
|
28 | 33 |
|
29 |
| -template<UnsignedInt dimensions> ShapeGroup<dimensions>::ShapeGroup(ShapeGroup<dimensions>&& other): operation(other.operation), a(other.a), b(other.b) { |
30 |
| - other.operation = Implementation::GroupOperation::AlwaysFalse; |
31 |
| - other.a = nullptr; |
32 |
| - other.b = nullptr; |
| 34 | +/* |
| 35 | +Hierarchy implementation notes: |
| 36 | +
|
| 37 | +The hierarchy is stored in flat array to provide easy access for the user and |
| 38 | +to save some allocations. Each node has zero, one or two subnodes. Value of |
| 39 | +`Node::rightNode` describes which child nodes exist: |
| 40 | +
|
| 41 | + * 0 - no child subnodes |
| 42 | + * 1 - only left subnode exists |
| 43 | + * 2 - only right subnode exists |
| 44 | + * >2 - both child nodes exist |
| 45 | +
|
| 46 | +If left node exists, it is right next to current one. If right node exists, it |
| 47 | +is at position `Node::rightNode-1` relative to current one (this applies also |
| 48 | +when `rightNode` is equal to 2, right node is right next to current one, |
| 49 | +because there are no left nodes). |
| 50 | +
|
| 51 | +The node also specifies which shapes belong to it. Root node owns whole shape |
| 52 | +array and `Node::rightShape` marks first shape belonging to the right child |
| 53 | +node, relatively to begin. This recurses into child nodes, thus left child node |
| 54 | +has shapes from parent's begin to parent's `rightShape`. |
| 55 | +
|
| 56 | +Shapes are merged together by concatenating its node and shape list and adding |
| 57 | +new node at the beginning with properly set `rightNode` and `rightShape`. |
| 58 | +Because these values are relative to parent, they don't need to be modified |
| 59 | +when concatenating. |
| 60 | +*/ |
| 61 | + |
| 62 | +template<UnsignedInt dimensions> ShapeGroup<dimensions>::ShapeGroup(const ShapeGroup<dimensions>& other): _shapeCount(other._shapeCount), _nodeCount(other._nodeCount) { |
| 63 | + copyShapes(0, other); |
| 64 | + copyNodes(0, other); |
| 65 | +} |
| 66 | + |
| 67 | +template<UnsignedInt dimensions> ShapeGroup<dimensions>::ShapeGroup(ShapeGroup<dimensions>&& other): _shapeCount(other._shapeCount), _nodeCount(other._nodeCount), _shapes(other._shapes), _nodes(other._nodes) { |
| 68 | + other._shapes = nullptr; |
| 69 | + other._shapeCount = 0; |
| 70 | + |
| 71 | + other._nodes = nullptr; |
| 72 | + other._nodeCount = 0; |
33 | 73 | }
|
34 | 74 |
|
35 | 75 | template<UnsignedInt dimensions> ShapeGroup<dimensions>::~ShapeGroup() {
|
36 |
| - if(!(operation & Implementation::GroupOperation::RefA)) delete a; |
37 |
| - if(!(operation & Implementation::GroupOperation::RefB)) delete b; |
| 76 | + for(std::size_t i = 0; i != _shapeCount; ++i) |
| 77 | + delete _shapes[i]; |
| 78 | + |
| 79 | + delete[] _shapes; |
| 80 | + delete[] _nodes; |
38 | 81 | }
|
39 | 82 |
|
40 |
| -template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(ShapeGroup<dimensions>&& other) { |
41 |
| - if(!(operation & Implementation::GroupOperation::RefA)) delete a; |
42 |
| - if(!(operation & Implementation::GroupOperation::RefB)) delete b; |
| 83 | +template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(const ShapeGroup<dimensions>& other) { |
| 84 | + for(std::size_t i = 0; i != _shapeCount; ++i) |
| 85 | + delete _shapes[i]; |
43 | 86 |
|
44 |
| - operation = other.operation; |
45 |
| - a = other.a; |
46 |
| - b = other.b; |
| 87 | + if(_shapeCount != other._shapeCount) { |
| 88 | + delete[] _shapes; |
| 89 | + _shapeCount = other._shapeCount; |
| 90 | + _shapes = new Implementation::AbstractShape<dimensions>*[_shapeCount]; |
| 91 | + } |
47 | 92 |
|
48 |
| - other.operation = Implementation::GroupOperation::AlwaysFalse; |
49 |
| - other.a = nullptr; |
50 |
| - other.b = nullptr; |
| 93 | + if(_nodeCount != other._nodeCount) { |
| 94 | + delete[] _nodes; |
| 95 | + _nodeCount = other._nodeCount; |
| 96 | + _nodes = new Node[_nodeCount]; |
| 97 | + } |
51 | 98 |
|
| 99 | + copyShapes(0, other); |
| 100 | + copyNodes(0, other); |
52 | 101 | return *this;
|
53 | 102 | }
|
54 | 103 |
|
55 |
| -template<UnsignedInt dimensions> void ShapeGroup<dimensions>::applyTransformationMatrix(const typename DimensionTraits<dimensions>::MatrixType& matrix) { |
56 |
| - if(a) a->applyTransformationMatrix(matrix); |
57 |
| - if(b) b->applyTransformationMatrix(matrix); |
| 104 | +template<UnsignedInt dimensions> ShapeGroup<dimensions>& ShapeGroup<dimensions>::operator=(ShapeGroup<dimensions>&& other) { |
| 105 | + std::swap(other._shapeCount, _shapeCount); |
| 106 | + std::swap(other._nodeCount, _nodeCount); |
| 107 | + std::swap(other._shapes, _shapes); |
| 108 | + std::swap(other._nodes, _nodes); |
| 109 | + return *this; |
58 | 110 | }
|
59 | 111 |
|
60 |
| -template<UnsignedInt dimensions> bool ShapeGroup<dimensions>::collides(const AbstractShape<dimensions>* other) const { |
61 |
| - switch(operation & ~Implementation::GroupOperation::RefAB) { |
62 |
| - case Implementation::GroupOperation::And: return a->collides(other) && b->collides(other); |
63 |
| - case Implementation::GroupOperation::Or: return a->collides(other) || b->collides(other); |
64 |
| - case Implementation::GroupOperation::Not: return !a->collides(other); |
65 |
| - case Implementation::GroupOperation::FirstObjectOnly: return a->collides(other); |
| 112 | +template<UnsignedInt dimensions> void ShapeGroup<dimensions>::copyShapes(const std::size_t offset, ShapeGroup<dimensions>&& other) { |
| 113 | + std::move(other._shapes, other._shapes+other._shapeCount, _shapes+offset); |
| 114 | + delete[] other._shapes; |
| 115 | + other._shapes = nullptr; |
| 116 | + other._shapeCount = 0; |
| 117 | +} |
66 | 118 |
|
67 |
| - default: |
68 |
| - return false; |
69 |
| - } |
| 119 | +template<UnsignedInt dimensions> void ShapeGroup<dimensions>::copyShapes(const std::size_t offset, const ShapeGroup<dimensions>& other) { |
| 120 | + for(std::size_t i = 0; i != other._shapeCount; ++i) |
| 121 | + _shapes[i+offset] = other._shapes[i]->clone(); |
| 122 | +} |
| 123 | + |
| 124 | +template<UnsignedInt dimensions> void ShapeGroup<dimensions>::copyNodes(std::size_t offset, const ShapeGroup<dimensions>& other) { |
| 125 | + std::copy(other._nodes, other._nodes+other._nodeCount, _nodes+offset); |
| 126 | +} |
| 127 | + |
| 128 | +template<UnsignedInt dimensions> ShapeGroup<dimensions> ShapeGroup<dimensions>::transformed(const typename DimensionTraits<dimensions>::MatrixType& matrix) const { |
| 129 | + ShapeGroup<dimensions> out(*this); |
| 130 | + for(std::size_t i = 0; i != _shapeCount; ++i) |
| 131 | + _shapes[i]->transform(matrix, out._shapes[i]); |
| 132 | + return out; |
| 133 | +} |
| 134 | + |
| 135 | +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 { |
| 136 | + /* Empty group */ |
| 137 | + if(shapeBegin == shapeEnd) return false; |
| 138 | + |
| 139 | + CORRADE_INTERNAL_ASSERT(node < _nodeCount && shapeBegin < shapeEnd); |
| 140 | + |
| 141 | + /* Collision on the left child. If the node is leaf one (no left child |
| 142 | + exists), do it directly, recurse instead. */ |
| 143 | + const bool collidesLeft = (_nodes[node].rightNode == 0 || _nodes[node].rightNode == 2) ? |
| 144 | + Implementation::collides(a, _shapes[shapeBegin]) : |
| 145 | + collides(a, node+1, shapeBegin, shapeBegin+_nodes[node].rightShape); |
| 146 | + |
| 147 | + /* NOT operation */ |
| 148 | + if(_nodes[node].operation == ShapeOperation::Not) |
| 149 | + return !collidesLeft; |
| 150 | + |
| 151 | + /* Short-circuit evaluation for AND/OR */ |
| 152 | + if((_nodes[node].operation == ShapeOperation::Or) == collidesLeft) |
| 153 | + return collidesLeft; |
| 154 | + |
| 155 | + /* Now the collision result depends only on the right child. Similar to |
| 156 | + collision on the left child. */ |
| 157 | + return (_nodes[node].rightNode < 2) ? |
| 158 | + Implementation::collides(a, _shapes[shapeBegin+_nodes[node].rightShape]) : |
| 159 | + collides(a, node+_nodes[node].rightNode-1, shapeBegin+_nodes[node].rightShape, shapeEnd); |
70 | 160 | }
|
71 | 161 |
|
72 |
| -template class ShapeGroup<2>; |
73 |
| -template class ShapeGroup<3>; |
| 162 | +#ifndef DOXYGEN_GENERATING_OUTPUT |
| 163 | +template class MAGNUM_PHYSICS_EXPORT ShapeGroup<2>; |
| 164 | +template class MAGNUM_PHYSICS_EXPORT ShapeGroup<3>; |
| 165 | +#endif |
74 | 166 |
|
75 | 167 | }}
|
0 commit comments