/
RenderableParticle.cpp
204 lines (167 loc) · 5.3 KB
/
RenderableParticle.cpp
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include "RenderableParticle.h"
namespace particles
{
RenderableParticle::RenderableParticle(const IParticleDefPtr& particleDef) :
_particleDef(), // don't initialise the ptr yet
_random(rand()), // use a random seed
_direction(0,0,1), // default direction
_entityColour(1,1,1) // default entity colour
{
// Use this method, for observer handling
setParticleDef(particleDef);
}
RenderableParticle::~RenderableParticle()
{
// Clear the particle def reference (remove this class as observer)
setParticleDef(IParticleDefPtr());
}
// Time is in msecs
void RenderableParticle::update(const Matrix4& viewRotation)
{
RenderSystemPtr renderSystem = _renderSystem.lock();
if (!renderSystem) return; // no rendersystem there yet
std::size_t time = renderSystem->getTime();
// Invalidate our bounds information
_bounds = AABB();
// Make sure all shaders are constructed
ensureShaders(*renderSystem);
// greebo: Use the inverse matrix of the incoming matrix, this is enough to compensate
// the camera rotation.
Matrix4 invViewRotation = viewRotation.getInverse();
// Traverse the stages and call update
for (ShaderMap::const_iterator i = _shaderMap.begin(); i != _shaderMap.end(); ++i)
{
for (RenderableParticleStageList::const_iterator stage = i->second.stages.begin();
stage != i->second.stages.end(); ++stage)
{
(*stage)->update(time, invViewRotation);
}
}
}
// Front-end render methods
void RenderableParticle::renderSolid(RenderableCollector& collector,
const VolumeTest& volume,
const Matrix4& localToWorld,
const IRenderEntity* entity) const
{
for (const ShaderMap::value_type& pair : _shaderMap)
{
assert(pair.second.shader); // ensure we're realised
// For each stage using this shader
for (const RenderableParticleStagePtr& stage : pair.second.stages)
{
// Skip invisible stages
if (!stage->getDef().isVisible()) continue;
if (entity)
{
collector.addRenderable(pair.second.shader, *stage, localToWorld, *entity);
}
else
{
collector.addRenderable(pair.second.shader, *stage, localToWorld);
}
}
}
}
void RenderableParticle::renderSolid(RenderableCollector& collector, const VolumeTest& volume) const
{
renderSolid(collector, volume, Matrix4::getIdentity(), nullptr);
}
void RenderableParticle::renderWireframe(RenderableCollector& collector, const VolumeTest& volume,
const Matrix4& localToWorld, const IRenderEntity* entity) const
{
// Does the same thing as renderSolid
renderSolid(collector, volume, localToWorld, entity);
}
void RenderableParticle::renderWireframe(RenderableCollector& collector, const VolumeTest& volume) const
{
// Does the same thing as renderSolid
renderSolid(collector, volume);
}
void RenderableParticle::setRenderSystem(const RenderSystemPtr& renderSystem)
{
_renderSystem = renderSystem;
}
const IParticleDefPtr& RenderableParticle::getParticleDef() const
{
return _particleDef;
}
void RenderableParticle::setParticleDef(const IParticleDefPtr& def)
{
if (_particleDef)
{
_defConnection.disconnect();
}
_particleDef = def;
if (_particleDef)
{
// Start monitoring this particle for reload events
_defConnection = _particleDef->signal_changed().connect(
sigc::mem_fun(this, &RenderableParticle::setupStages)
);
}
// Re-construct our stage information
setupStages();
}
void RenderableParticle::setMainDirection(const Vector3& direction)
{
_direction = direction;
// The particle stages hold a const-reference to _direction
// so no further update is needed
}
void RenderableParticle::setEntityColour(const Vector3& colour)
{
_entityColour = colour;
// The particle stages hold a const-reference to _direction
// so no further update is needed
}
// Updates bounds from stages and returns the value
const AABB& RenderableParticle::getBounds()
{
if (!_bounds.isValid())
{
calculateBounds();
}
return _bounds;
}
void RenderableParticle::calculateBounds()
{
for (ShaderMap::const_iterator i = _shaderMap.begin(); i != _shaderMap.end(); ++i)
{
for (RenderableParticleStageList::const_iterator stage = i->second.stages.begin();
stage != i->second.stages.end(); ++stage)
{
_bounds.includeAABB((*stage)->getBounds());
}
}
}
// Sort stages into groups sharing a material, without capturing the shader yet
void RenderableParticle::setupStages()
{
_shaderMap.clear();
if (_particleDef == NULL) return; // nothing to do.
for (std::size_t i = 0; i < _particleDef->getNumStages(); ++i)
{
const IStageDef& stage = _particleDef->getStage(i);
const std::string& materialName = stage.getMaterialName();
if (_shaderMap.find(materialName) == _shaderMap.end())
{
_shaderMap.insert(ShaderMap::value_type(materialName, ParticleStageGroup()));
}
// Create a new renderable stage and add it to the shader
RenderableParticleStagePtr renderableStage(new RenderableParticleStage(stage, _random, _direction, _entityColour));
_shaderMap[materialName].stages.push_back(renderableStage);
}
}
// Capture all shaders, if necessary
void RenderableParticle::ensureShaders(RenderSystem& renderSystem)
{
for (ShaderMap::iterator i = _shaderMap.begin(); i != _shaderMap.end(); ++i)
{
if (i->second.shader == NULL)
{
i->second.shader = renderSystem.capture(i->first);
}
}
}
} // namespace