/
RenderableObjectCollection.h
111 lines (88 loc) · 2.86 KB
/
RenderableObjectCollection.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#pragma once
#include <map>
#include <sigc++/connection.h>
#include <sigc++/trackable.h>
#include <sigc++/functors/mem_fun.h>
#include "irender.h"
#include "irenderableobject.h"
#include "itextstream.h"
namespace entity
{
class RenderableObjectCollection :
public sigc::trackable
{
private:
AABB _collectionBounds;
bool _collectionBoundsNeedUpdate;
struct ObjectData
{
Shader* shader;
sigc::connection boundsChangedConnection;
};
std::map<render::IRenderableObject::Ptr, ObjectData> _objects;
public:
RenderableObjectCollection() :
_collectionBoundsNeedUpdate(true)
{}
void addRenderable(const render::IRenderableObject::Ptr& object, Shader* shader)
{
sigc::connection subscription = object->signal_boundsChanged().connect(
sigc::mem_fun(*this, &RenderableObjectCollection::onObjectBoundsChanged));
if (!_objects.try_emplace(object, ObjectData{ shader, subscription }).second)
{
// We've already been subscribed to this one
subscription.disconnect();
rWarning() << "Renderable has already been attached to entity" << std::endl;
return;
}
_collectionBoundsNeedUpdate = true;
}
void removeRenderable(const render::IRenderableObject::Ptr& object)
{
auto mapping = _objects.find(object);
if (mapping != _objects.end())
{
mapping->second.boundsChangedConnection.disconnect();
_objects.erase(mapping);
}
else
{
rWarning() << "Renderable has not been attached to entity" << std::endl;
}
_collectionBoundsNeedUpdate = true;
}
void foreachRenderableTouchingBounds(const AABB& bounds,
const IRenderEntity::ObjectVisitFunction& functor)
{
if (_objects.empty()) return;
ensureBoundsUpToDate();
// If the whole collection doesn't intersect, quit early
if (!_collectionBounds.intersects(bounds)) return;
for (const auto& [object, objectData] : _objects)
{
auto orientedBounds = AABB::createFromOrientedAABBSafe(
object->getObjectBounds(), object->getObjectTransform());
if (bounds.intersects(orientedBounds))
{
functor(object, objectData.shader);
}
}
}
private:
void onObjectBoundsChanged()
{
_collectionBoundsNeedUpdate = true;
}
void ensureBoundsUpToDate()
{
if (!_collectionBoundsNeedUpdate) return;
_collectionBoundsNeedUpdate = false;
_collectionBounds = AABB();
for (const auto& [object, _] : _objects)
{
_collectionBounds.includeAABB(AABB::createFromOrientedAABBSafe(
object->getObjectBounds(), object->getObjectTransform()));
}
}
};
}