File: RenderableParticle.cpp

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (194 lines) | stat: -rw-r--r-- 4,656 bytes parent folder | download | duplicates (3)
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
#include "RenderableParticle.h"

namespace particles
{

RenderableParticle::RenderableParticle(const IParticleDef::Ptr& 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({});
}

// Time is in msecs
void RenderableParticle::update(const Matrix4& viewRotation, const Matrix4& localToWorld, IRenderEntity* entity)
{
	auto renderSystem = _renderSystem.lock();

	if (!renderSystem) return; // no rendersystem there yet

	auto 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.
	auto invViewRotation = viewRotation.getInverse();

	// Traverse the stages and call update
	for (const auto& pair : _shaderMap)
	{
		for (const auto& stage : pair.second.stages)
		{
            if (!stage->getDef().isVisible())
            {
                stage->clear();
                continue;
            }

            // Update the particle quads
            stage->update(time, invViewRotation);

            // Check if the stage is empty, otherwise remove any geometry
            if (stage->getNumQuads() == 0)
            {
                stage->clear();
                continue;
            }

            // Attach the geometry to the shader
            stage->submitGeometry(pair.second.shader, localToWorld);

            // Attach to the parent entity for lighting mode
            stage->attachToEntity(entity);
		}
	}
}

void RenderableParticle::clearRenderables()
{
    for (const auto& pair : _shaderMap)
    {
        for (const auto& stage : pair.second.stages)
        {
            stage->clear();
        }
    }
}

void RenderableParticle::onPreRender(const VolumeTest& volume)
{}


void RenderableParticle::renderHighlights(IRenderableCollector& collector, const VolumeTest& volume)
{
}

void RenderableParticle::setRenderSystem(const RenderSystemPtr& renderSystem)
{
	_renderSystem = renderSystem;
}

const IParticleDef::Ptr& RenderableParticle::getParticleDef() const
{
	return _particleDef;
}

void RenderableParticle::setParticleDef(const IParticleDef::Ptr& 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 _entityColour
	// 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 (const auto& pair : _shaderMap)
	{
		for (const auto& stage : pair.second.stages)
		{
			_bounds.includeAABB(stage->getBounds());
		}
	}
}

// Sort stages into groups sharing a material, without capturing the shader yet
void RenderableParticle::setupStages()
{
	_shaderMap.clear();

	if (!_particleDef) return; // nothing to do.

	for (std::size_t i = 0; i < _particleDef->getNumStages(); ++i)
	{
		const auto& stage = _particleDef->getStage(i);
		const auto& materialName = stage->getMaterialName();

		if (_shaderMap.find(materialName) == _shaderMap.end())
		{
			_shaderMap.emplace(materialName, ParticleStageGroup());
		}

		// Create a new renderable stage and add it to the shader
		auto renderableStage = std::make_shared<RenderableParticleStage>(*stage, _random, _direction, _entityColour);
		_shaderMap[materialName].stages.emplace_back(std::move(renderableStage));
	}
}

// Capture all shaders, if necessary
void RenderableParticle::ensureShaders(RenderSystem& renderSystem)
{
	for (auto& pair : _shaderMap)
	{
		if (!pair.second.shader)
		{
			pair.second.shader = renderSystem.capture(pair.first);
		}
	}
}

} // namespace