Subversion Repository Public Repository

Divide-Framework

This repository has no backups
This repository's network speed is throttled to 100KB/sec

Diff Revisions 330 vs 331 for /trunk/Source Code/Dynamics/Entities/Particles/ParticleEmitter.cpp

Diff revisions: vs.
  @@ -10,305 +10,328 @@
10 10 #include "Geometry/Material/Headers/Material.h"
11 11
12 12 namespace Divide {
13 + ParticleEmitterDescriptor::ParticleEmitterDescriptor() : PropertyDescriptor(PropertyDescriptor::DESCRIPTOR_PARTICLE)
14 + {
15 + setDefaultValues();
16 + }
17 +
18 + ParticleEmitter::ParticleEmitter() : SceneNode(TYPE_PARTICLE_EMITTER),
19 + _drawImpostor(false),
20 + _updateParticleEmitterBB(true),
21 + _lastUsedParticle(0),
22 + _particlesCurrentCount(0),
23 + _particleStateBlockHash(0),
24 + _enabled(false),
25 + _uploaded(false),
26 + _created(false),
27 + _particleTexture(nullptr),
28 + _particleShader(nullptr),
29 + _particleGPUBuffer(nullptr),
30 + _particleDepthShader(nullptr)
31 + {
32 + _drawCommand = GenericDrawCommand(TRIANGLE_STRIP, 0, 4, 1);
33 + _readOffset = 0;
34 + _writeOffset = 2;
35 + }
36 +
37 + ParticleEmitter::~ParticleEmitter()
38 + {
39 + unload();
40 + }
41 +
42 + bool ParticleEmitter::initData(){
43 + // assert if double init!
44 + DIVIDE_ASSERT(_particleGPUBuffer == nullptr, "ParticleEmitter error: Double initData detected!");
45 +
46 + _particleGPUBuffer = GFX_DEVICE.newGVD(/*true*/false);
47 + _particleGPUBuffer->Create(3);
48 +
49 + // Not using Quad3D to improve performance
50 + static F32 particleQuad[] = {
51 + -0.5f, -0.5f, 0.0f,
52 + 0.5f, -0.5f, 0.0f,
53 + -0.5f, 0.5f, 0.0f,
54 + 0.5f, 0.5f, 0.0f,
55 + };
56 +
57 + _particleGPUBuffer->SetBuffer(0, 12, sizeof(F32), 1, particleQuad, false, false);
58 + _particleGPUBuffer->getDrawAttribDescriptor(VERTEX_POSITION_LOCATION).set(0, 0, 3, false, 0, 0, FLOAT_32);
59 +
60 + //Generate a render state
61 + RenderStateBlockDescriptor particleStateDesc;
62 + particleStateDesc.setCullMode(CULL_MODE_NONE);
63 + particleStateDesc.setBlend(true, BLEND_PROPERTY_SRC_ALPHA, BLEND_PROPERTY_INV_SRC_ALPHA);
64 + _particleStateBlockHash = GFX_DEVICE.getOrCreateStateBlock(particleStateDesc);
65 +
66 + ResourceDescriptor particleShaderDescriptor("particles");
67 + _particleShader = CreateResource<ShaderProgram>(particleShaderDescriptor);
68 + _particleShader->UniformTexture("depthBuffer", 1);
69 + REGISTER_TRACKED_DEPENDENCY(_particleShader);
70 + ResourceDescriptor particleDepthShaderDescriptor("particles.Depth");
71 + _particleDepthShader = CreateResource<ShaderProgram>(particleDepthShaderDescriptor);
72 + REGISTER_TRACKED_DEPENDENCY(_particleDepthShader);
73 + _impostor = CreateResource<Impostor>(ResourceDescriptor(_name + "_impostor"));
74 + _impostor->renderState().setDrawState(false);
75 + _impostor->getMaterialTpl()->setDiffuse(vec4<F32>(0.0f, 0.0f, 1.0f, 1.0f));
76 + _impostor->getMaterialTpl()->setAmbient(vec4<F32>(0.0f, 0.0f, 1.0f, 1.0f));
77 + _renderState.addToDrawExclusionMask(SHADOW_STAGE);
78 + return (_particleShader != nullptr);
79 + }
80 +
81 + bool ParticleEmitter::unload(){
82 + if (getState() != RES_LOADED && getState() != RES_LOADING) {
83 + return true;
84 + }
85 + UNREGISTER_TRACKED_DEPENDENCY(_particleTexture);
86 + UNREGISTER_TRACKED_DEPENDENCY(_particleShader);
87 + UNREGISTER_TRACKED_DEPENDENCY(_particleDepthShader);
88 + RemoveResource(_particleTexture);
89 + RemoveResource(_particleShader);
90 + RemoveResource(_particleDepthShader);
13 91
14 - ParticleEmitterDescriptor::ParticleEmitterDescriptor() : PropertyDescriptor( PropertyDescriptor::DESCRIPTOR_PARTICLE )
15 - {
16 - setDefaultValues();
17 - }
18 -
19 - ParticleEmitter::ParticleEmitter() : SceneNode(TYPE_PARTICLE_EMITTER),
20 - _drawImpostor(false),
21 - _updateParticleEmitterBB(true),
22 - _lastUsedParticle(0),
23 - _particlesCurrentCount(0),
24 - _particleStateBlockHash(0),
25 - _enabled(false),
26 - _uploaded(false),
27 - _created(false),
28 - _particleTexture(nullptr),
29 - _particleShader(nullptr),
30 - _particleGPUBuffer(nullptr),
31 - _particleDepthShader(nullptr)
32 - {
33 - _drawCommand = GenericDrawCommand(TRIANGLE_STRIP, 0, 4, 1);
34 - _readOffset = 0;
35 - _writeOffset = 2;
36 - }
37 -
38 - ParticleEmitter::~ParticleEmitter()
39 - {
40 - unload();
41 - }
42 -
43 - bool ParticleEmitter::initData(){
44 - // assert if double init!
45 - DIVIDE_ASSERT(_particleGPUBuffer == nullptr, "ParticleEmitter error: Double initData detected!");
46 -
47 - _particleGPUBuffer = GFX_DEVICE.newGVD(/*true*/false);
48 - _particleGPUBuffer->Create(3);
49 -
50 - // Not using Quad3D to improve performance
51 - static F32 particleQuad[] = {
52 - -0.5f, -0.5f, 0.0f,
53 - 0.5f, -0.5f, 0.0f,
54 - -0.5f, 0.5f, 0.0f,
55 - 0.5f, 0.5f, 0.0f,
56 - };
57 -
58 - _particleGPUBuffer->SetBuffer(0, 12, sizeof(F32), 1, particleQuad, false, false);
59 - _particleGPUBuffer->getDrawAttribDescriptor(VERTEX_POSITION_LOCATION).set(0, 0, 3, false, 0, 0, FLOAT_32);
60 -
61 - //Generate a render state
62 - RenderStateBlockDescriptor particleStateDesc;
63 - particleStateDesc.setCullMode(CULL_MODE_NONE);
64 - particleStateDesc.setBlend(true, BLEND_PROPERTY_SRC_ALPHA, BLEND_PROPERTY_INV_SRC_ALPHA);
65 - _particleStateBlockHash = GFX_DEVICE.getOrCreateStateBlock(particleStateDesc);
66 -
67 - ResourceDescriptor particleShaderDescriptor("particles");
68 - _particleShader = CreateResource<ShaderProgram>(particleShaderDescriptor);
69 - _particleShader->UniformTexture("depthBuffer", 1);
70 - REGISTER_TRACKED_DEPENDENCY(_particleShader);
71 - ResourceDescriptor particleDepthShaderDescriptor("particles.Depth");
72 - _particleDepthShader = CreateResource<ShaderProgram>(particleDepthShaderDescriptor);
73 - REGISTER_TRACKED_DEPENDENCY(_particleDepthShader);
74 - _impostor = CreateResource<Impostor>(ResourceDescriptor(_name + "_impostor"));
75 - _impostor->renderState().setDrawState( false );
76 - _impostor->getMaterialTpl()->setDiffuse( vec4<F32>( 0.0f, 0.0f, 1.0f, 1.0f ) );
77 - _impostor->getMaterialTpl()->setAmbient(vec4<F32>(0.0f, 0.0f, 1.0f, 1.0f));
78 - _renderState.addToDrawExclusionMask(SHADOW_STAGE);
79 - return (_particleShader != nullptr);
80 - }
81 -
82 - bool ParticleEmitter::unload(){
83 - if ( getState() != RES_LOADED && getState() != RES_LOADING ) {
84 - return true;
85 - }
86 - UNREGISTER_TRACKED_DEPENDENCY(_particleTexture);
87 - UNREGISTER_TRACKED_DEPENDENCY(_particleShader);
88 - UNREGISTER_TRACKED_DEPENDENCY(_particleDepthShader);
89 - RemoveResource(_particleTexture);
90 - RemoveResource(_particleShader);
91 - RemoveResource(_particleDepthShader);
92 -
93 - MemoryManager::SAFE_DELETE( _particleGPUBuffer );
94 -
95 - _particles.clear();
96 - _particlePositionData.clear();
97 - _particleColorData.clear();
98 - _created = false;
99 - return SceneNode::unload();
100 - }
101 -
102 - void ParticleEmitter::postLoad(SceneGraphNode* const sgn){
103 - sgn->addNode(_impostor)->setActive(false);
104 - SceneNode::postLoad(sgn);
105 - }
106 -
107 - bool ParticleEmitter::computeBoundingBox(SceneGraphNode* const sgn){
108 - if ( !_enabled || !_created ) {
109 - return false;
110 - }
111 -
112 - _updateParticleEmitterBB = true;
113 - sgn->getBoundingBox().set(vec3<F32>(-5), vec3<F32>(5));
114 - return SceneNode::computeBoundingBox(sgn);
115 - }
116 -
117 - void ParticleEmitter::onCameraChange(SceneGraphNode* const sgn){
118 - const mat4<F32>& viewMatrixCache = GFX_DEVICE.getMatrix(VIEW_MATRIX);
119 - _particleShader->Uniform("CameraRight_worldspace", vec3<F32>(viewMatrixCache.m[0][0], viewMatrixCache.m[1][0], viewMatrixCache.m[2][0]));
120 - _particleShader->Uniform("CameraUp_worldspace", vec3<F32>(viewMatrixCache.m[0][1], viewMatrixCache.m[1][1], viewMatrixCache.m[2][1]));
121 - _particleDepthShader->Uniform("CameraRight_worldspace", vec3<F32>(viewMatrixCache.m[0][0], viewMatrixCache.m[1][0], viewMatrixCache.m[2][0]));
122 - _particleDepthShader->Uniform("CameraUp_worldspace", vec3<F32>(viewMatrixCache.m[0][1], viewMatrixCache.m[1][1], viewMatrixCache.m[2][1]));
123 - }
124 -
125 - void ParticleEmitter::getDrawCommands(SceneGraphNode* const sgn, const RenderStage& currentRenderStage, SceneRenderState& sceneRenderState, vectorImpl<GenericDrawCommand>& drawCommandsOut) {
126 - if (!(_particlesCurrentCount > 0 && _enabled && _created)){
127 - return;
128 - }
129 -
130 - _drawCommand.renderWireframe(sgn->getComponent<RenderingComponent>()->renderWireframe());
131 - _drawCommand.stateHash(_particleStateBlockHash);
132 - _drawCommand.instanceCount(_particlesCurrentCount);
133 - _drawCommand.shaderProgram(currentRenderStage == FINAL_STAGE ? _particleShader : _particleDepthShader);
134 - _drawCommand.sourceBuffer(_particleGPUBuffer);
135 - drawCommandsOut.push_back(_drawCommand);
136 - }
137 -
138 - ///When the SceneGraph calls the particle emitter's render function, we draw the impostor if needed
139 - void ParticleEmitter::render(SceneGraphNode* const sgn, const SceneRenderState& sceneRenderState, const RenderStage& currentRenderStage){
140 - if(_particlesCurrentCount > 0 && _enabled && _created){
141 - _particleTexture->Bind(ShaderProgram::TEXTURE_UNIT0);
142 - GFX_DEVICE.getRenderTarget(GFXDevice::RENDER_TARGET_DEPTH)->Bind(ShaderProgram::TEXTURE_UNIT1, TextureDescriptor::Depth);
143 - GFX_DEVICE.submitRenderCommand(sgn->getComponent<RenderingComponent>()->getDrawCommands());
144 - }
145 - }
146 -
147 - ///The descriptor defines the particle properties
148 - void ParticleEmitter::setDescriptor(const ParticleEmitterDescriptor& descriptor){
149 - _descriptor = descriptor;
150 - _particleGPUBuffer->SetBuffer(1, descriptor._particleCount * 3, 4 * sizeof(F32), 1, NULL, true, true, /*true*/false);
151 - _particleGPUBuffer->SetBuffer(2, descriptor._particleCount * 3, 4 * sizeof(U8), 1, NULL, true, true, /*true*/false);
152 -
153 - _particleGPUBuffer->getDrawAttribDescriptor(13).set(1, 1, 4, false, 0, 0, FLOAT_32);
154 - _particleGPUBuffer->getDrawAttribDescriptor(VERTEX_COLOR_LOCATION).set(2, 1, 4, true, 0, 0, UNSIGNED_BYTE);
155 -
156 - _particles.resize(descriptor._particleCount);
157 - _particlePositionData.resize(descriptor._particleCount * 4);
158 - _particleColorData.resize(descriptor._particleCount * 4);
159 - for(U32 i = 0; i < descriptor._particleCount; ++i){
160 - _particles[i].life = -1.0f;
161 - _particles[i].distanceToCameraSq = -1.0f;
92 + MemoryManager::DELETE(_particleGPUBuffer);
93 +
94 + _particles.clear();
95 + _particlePositionData.clear();
96 + _particleColorData.clear();
97 + _created = false;
98 + return SceneNode::unload();
162 99 }
163 100
164 - if(_particleTexture){
165 - UNREGISTER_TRACKED_DEPENDENCY(_particleTexture);
166 - RemoveResource(_particleTexture);
101 + void ParticleEmitter::postLoad(SceneGraphNode* const sgn){
102 + sgn->addNode(_impostor)->setActive(false);
103 + SceneNode::postLoad(sgn);
167 104 }
168 105
169 - SamplerDescriptor textureSampler;
170 - textureSampler.toggleSRGBColorSpace(true);
171 - ResourceDescriptor texture(descriptor._textureFileName);
172 - texture.setResourceLocation(ParamHandler::getInstance().getParam<stringImpl>("assetsLocation") + "/" +
173 - ParamHandler::getInstance().getParam<stringImpl>("defaultTextureLocation") + "/" +
174 - descriptor._textureFileName);
175 - texture.setFlag(true);
176 - texture.setPropertyDescriptor<SamplerDescriptor>(textureSampler);
177 - _particleTexture = CreateResource<Texture>(texture);
178 - REGISTER_TRACKED_DEPENDENCY(_particleTexture);
179 - _created = true;
180 - }
181 -
182 - void ParticleEmitter::uploadToGPU(){
183 - static const size_t attribSize_float = 4 * sizeof(F32);
184 - static const size_t attribSize_char = 4 * sizeof(U8);
185 - if(_uploaded || !_created)
186 - return;
106 + bool ParticleEmitter::computeBoundingBox(SceneGraphNode* const sgn){
107 + if (!_enabled || !_created) {
108 + return false;
109 + }
187 110
188 - U32 writeOffset = _writeOffset * (U32)_particles.size();
189 - U32 readOffset = _readOffset * (U32)_particles.size();
111 + _updateParticleEmitterBB = true;
112 + sgn->getBoundingBox().set(vec3<F32>(-5), vec3<F32>(5));
113 + return SceneNode::computeBoundingBox(sgn);
114 + }
115 +
116 + void ParticleEmitter::onCameraChange(SceneGraphNode* const sgn){
117 + const mat4<F32>& viewMatrixCache = GFX_DEVICE.getMatrix(VIEW_MATRIX);
118 + _particleShader->Uniform("CameraRight_worldspace", vec3<F32>(viewMatrixCache.m[0][0],
119 + viewMatrixCache.m[1][0],
120 + viewMatrixCache.m[2][0]));
121 + _particleShader->Uniform("CameraUp_worldspace", vec3<F32>(viewMatrixCache.m[0][1],
122 + viewMatrixCache.m[1][1],
123 + viewMatrixCache.m[2][1]));
124 + _particleDepthShader->Uniform("CameraRight_worldspace", vec3<F32>(viewMatrixCache.m[0][0],
125 + viewMatrixCache.m[1][0],
126 + viewMatrixCache.m[2][0]));
127 + _particleDepthShader->Uniform("CameraUp_worldspace", vec3<F32>(viewMatrixCache.m[0][1],
128 + viewMatrixCache.m[1][1],
129 + viewMatrixCache.m[2][1]));
130 + }
131 +
132 + void ParticleEmitter::getDrawCommands(SceneGraphNode* const sgn,
133 + const RenderStage& currentRenderStage,
134 + SceneRenderState& sceneRenderState,
135 + vectorImpl<GenericDrawCommand>& drawCommandsOut)
136 + {
190 137
191 - _particleGPUBuffer->UpdateBuffer(1, _particlesCurrentCount, writeOffset, &_particlePositionData[0]);
192 - _particleGPUBuffer->UpdateBuffer(2, _particlesCurrentCount, writeOffset, &_particleColorData[0]);
193 -
194 - _particleGPUBuffer->getDrawAttribDescriptor(13).set(1, 1, 4, false, 0, readOffset, FLOAT_32);
195 - _particleGPUBuffer->getDrawAttribDescriptor(VERTEX_COLOR_LOCATION).set(2, 1, 4, true, 0, readOffset, UNSIGNED_BYTE);
138 + if (!(_particlesCurrentCount > 0 && _enabled && _created)) {
139 + return;
140 + }
196 141
197 - _writeOffset = (_writeOffset + 1) % 3;
198 - _readOffset = (_readOffset + 1) % 3;
142 + _drawCommand.renderWireframe(sgn->getComponent<RenderingComponent>()->renderWireframe());
143 + _drawCommand.stateHash(_particleStateBlockHash);
144 + _drawCommand.instanceCount(_particlesCurrentCount);
145 + _drawCommand.shaderProgram(currentRenderStage == FINAL_STAGE ? _particleShader : _particleDepthShader);
146 + _drawCommand.sourceBuffer(_particleGPUBuffer);
147 + drawCommandsOut.push_back(_drawCommand);
148 + }
149 +
150 + ///When the SceneGraph calls the particle emitter's render function, we draw the impostor if needed
151 + void ParticleEmitter::render(SceneGraphNode* const sgn,
152 + const SceneRenderState& sceneRenderState,
153 + const RenderStage& currentRenderStage) {
154 + if (_particlesCurrentCount > 0 && _enabled && _created){
155 + _particleTexture->Bind(ShaderProgram::TEXTURE_UNIT0);
156 + GFX_DEVICE.getRenderTarget(GFXDevice::RENDER_TARGET_DEPTH)->Bind(ShaderProgram::TEXTURE_UNIT1,
157 + TextureDescriptor::Depth);
158 + GFX_DEVICE.submitRenderCommand(sgn->getComponent<RenderingComponent>()->getDrawCommands());
159 + }
160 + }
199 161
200 - _uploaded = true;
201 - }
162 + ///The descriptor defines the particle properties
163 + void ParticleEmitter::setDescriptor(const ParticleEmitterDescriptor& descriptor){
164 + _descriptor = descriptor;
165 + _particleGPUBuffer->SetBuffer(1, descriptor._particleCount * 3, 4 * sizeof(F32), 1, NULL, true, true, /*true*/false);
166 + _particleGPUBuffer->SetBuffer(2, descriptor._particleCount * 3, 4 * sizeof(U8), 1, NULL, true, true, /*true*/false);
167 +
168 + _particleGPUBuffer->getDrawAttribDescriptor(13).set(1, 1, 4, false, 0, 0, FLOAT_32);
169 + _particleGPUBuffer->getDrawAttribDescriptor(VERTEX_COLOR_LOCATION).set(2, 1, 4, true, 0, 0, UNSIGNED_BYTE);
170 +
171 + _particles.resize(descriptor._particleCount);
172 + _particlePositionData.resize(descriptor._particleCount * 4);
173 + _particleColorData.resize(descriptor._particleCount * 4);
174 +
175 + for (U32 i = 0; i < descriptor._particleCount; ++i) {
176 + _particles[i].life = -1.0f;
177 + _particles[i].distanceToCameraSq = -1.0f;
178 + }
202 179
203 - ///The onDraw call will emit particles
204 - bool ParticleEmitter::onDraw(SceneGraphNode* const sgn, const RenderStage& currentStage){
205 - if (!_enabled || _particlesCurrentCount == 0 || !_created) {
206 - return false;
207 - }
208 - std::sort(_particles.begin(), _particles.end());
209 - uploadToGPU();
210 -
211 - return true;
212 - }
213 -
214 - /// Pre-process particles
215 - void ParticleEmitter::sceneUpdate(const U64 deltaTime, SceneGraphNode* const sgn, SceneState& sceneState){
216 - if(!_enabled || !_created)
217 - return;
218 -
219 - if (_updateParticleEmitterBB) {
220 - sgn->updateBoundingBoxTransform(sgn->getComponent<PhysicsComponent>()->getWorldMatrix());
221 - _updateParticleEmitterBB = false;
222 - }
223 -
224 - F32 delta = getUsToSec(deltaTime);
225 -
226 - I32 newParticles = _descriptor._emissionInterval + random(-_descriptor._emissionIntervalVariance, _descriptor._emissionIntervalVariance);
227 - newParticles = (I32)(newParticles * delta) / (sgn->getComponent<RenderingComponent>()->lodLevel() + 1);
228 - PhysicsComponent* const transform = sgn->getComponent<PhysicsComponent>();
229 - const vec3<F32>& eyePos = sceneState.getRenderState().getCameraConst().getEye();
230 - const vec3<F32>& origin = transform->getPosition();
231 - const Quaternion<F32>& orientation = transform->getOrientation();
232 - F32 spread = _descriptor._spread;
233 - vec3<F32> mainDir = orientation * vec3<F32>(0, (_descriptor._velocity + random(-_descriptor._velocityVariance, _descriptor._velocityVariance)), 0);
234 -
235 - for(I32 i = 0; i < newParticles; ++i){
236 - ParticleDescriptor& currentParticle = _particles[findUnusedParticle()];
237 - currentParticle.life = _descriptor._lifetime + getMsToSec(random(-_descriptor._lifetimeVariance, _descriptor._lifetimeVariance));
238 - memcpy(currentParticle.pos, origin._v, 3 * sizeof(F32));
239 - // Very bad way to generate a random direction;
240 - // See for instance http://stackoverflow.com/questions/5408276/python-uniform-spherical-distribution instead,
241 - // combined with some user-controlled parameters (main direction, spread, etc)
242 - currentParticle.speed[0] = mainDir.x + (rand()%2000 - 1000.0f)/1000.0f * spread;
243 - currentParticle.speed[1] = mainDir.y + (rand()%2000 - 1000.0f)/1000.0f * spread;
244 - currentParticle.speed[2] = mainDir.z + (rand()%2000 - 1000.0f)/1000.0f * spread;
245 - // Very bad way to generate a random color
246 - currentParticle.rgba[0] = rand() % 256;
247 - currentParticle.rgba[1] = rand() % 256;
248 - currentParticle.rgba[2] = rand() % 256;
249 - currentParticle.rgba[3] = (rand() % 256) / 2;
250 -
251 - currentParticle.size = (rand()%1000)/2000.0f + 0.1f;
252 - }
253 -
254 - // Simulate all particles
255 - I32 particlesCount = 0;
256 - vec3<F32> half_gravity = DEFAULT_GRAVITY * delta * 0.5f;
257 - for (ParticleDescriptor& p : _particles){
258 - if(p.life > 0.0f){
259 - // Decrease life
260 - p.life -= delta;
261 - if (p.life > 0.0f){
262 -
263 - // Simulate simple physics : gravity only, no collisions
264 - for (U8 i = 0; i < 3; ++i) {
265 - p.speed[i] += half_gravity[i];
266 - p.pos[i] += p.speed[i] * delta;
267 - }
268 - p.distanceToCameraSq = vec3<F32>(p.pos).distanceSquared(eyePos);
269 -
270 - _particlePositionData[4*particlesCount+0] = p.pos[0];
271 - _particlePositionData[4*particlesCount+1] = p.pos[1];
272 - _particlePositionData[4*particlesCount+2] = p.pos[2];
273 - _particlePositionData[4*particlesCount+3] = p.size;
274 -
275 - _particleColorData[4*particlesCount+0] = p.rgba[0];
276 - _particleColorData[4*particlesCount+1] = p.rgba[1];
277 - _particleColorData[4*particlesCount+2] = p.rgba[2];
278 - _particleColorData[4*particlesCount+3] = p.rgba[3];
279 -
280 - }else{
281 - // Particles that just died will be put at the end of the buffer in SortParticles();
282 - p.distanceToCameraSq = -1.0f;
283 - }
284 -
285 - particlesCount++;
180 + if (_particleTexture) {
181 + UNREGISTER_TRACKED_DEPENDENCY(_particleTexture);
182 + RemoveResource(_particleTexture);
286 183 }
184 +
185 + SamplerDescriptor textureSampler;
186 + textureSampler.toggleSRGBColorSpace(true);
187 + ResourceDescriptor texture(descriptor._textureFileName);
188 + texture.setResourceLocation(ParamHandler::getInstance().getParam<stringImpl>("assetsLocation") + "/" +
189 + ParamHandler::getInstance().getParam<stringImpl>("defaultTextureLocation") + "/" +
190 + descriptor._textureFileName);
191 + texture.setFlag(true);
192 + texture.setPropertyDescriptor<SamplerDescriptor>(textureSampler);
193 + _particleTexture = CreateResource<Texture>(texture);
194 + REGISTER_TRACKED_DEPENDENCY(_particleTexture);
195 + _created = true;
196 + }
197 +
198 + void ParticleEmitter::uploadToGPU(){
199 + static const size_t attribSize_float = 4 * sizeof(F32);
200 + static const size_t attribSize_char = 4 * sizeof(U8);
201 + if (_uploaded || !_created)
202 + return;
203 +
204 + U32 writeOffset = _writeOffset * (U32)_particles.size();
205 + U32 readOffset = _readOffset * (U32)_particles.size();
206 +
207 + _particleGPUBuffer->UpdateBuffer(1, _particlesCurrentCount, writeOffset, &_particlePositionData[0]);
208 + _particleGPUBuffer->UpdateBuffer(2, _particlesCurrentCount, writeOffset, &_particleColorData[0]);
209 +
210 + _particleGPUBuffer->getDrawAttribDescriptor(13).set(1, 1, 4, false, 0, readOffset, FLOAT_32);
211 + _particleGPUBuffer->getDrawAttribDescriptor(VERTEX_COLOR_LOCATION).set(2, 1, 4, true, 0, readOffset, UNSIGNED_BYTE);
212 +
213 + _writeOffset = (_writeOffset + 1) % 3;
214 + _readOffset = (_readOffset + 1) % 3;
215 +
216 + _uploaded = true;
287 217 }
288 - _particlesCurrentCount = particlesCount;
289 - _uploaded = false;
290 218
291 - SceneNode::sceneUpdate(deltaTime, sgn, sceneState);
292 - }
219 + ///The onDraw call will emit particles
220 + bool ParticleEmitter::onDraw(SceneGraphNode* const sgn, const RenderStage& currentStage) {
293 221
294 - // Finds a Particle in ParticlesContainer which isn't used yet. (i.e. life < 0);
295 - I32 ParticleEmitter::findUnusedParticle(){
296 -
297 - for(U32 i = _lastUsedParticle; i < _particles.size(); ++i){
298 - if (_particles[i].life < 0.0f){
299 - _lastUsedParticle = i;
300 - return i;
222 + if (!_enabled || _particlesCurrentCount == 0 || !_created) {
223 + return false;
301 224 }
225 + std::sort(_particles.begin(), _particles.end());
226 + uploadToGPU();
227 +
228 + return true;
302 229 }
303 -
304 - for(I32 i = 0; i < _lastUsedParticle; ++i){
305 - if (_particles[i].life < 0.0f){
306 - _lastUsedParticle = i;
307 - return i;
230 +
231 + /// Pre-process particles
232 + void ParticleEmitter::sceneUpdate(const U64 deltaTime, SceneGraphNode* const sgn, SceneState& sceneState) {
233 +
234 + if (!_enabled || !_created) {
235 + return;
236 + }
237 +
238 + PhysicsComponent* const transform = sgn->getComponent<PhysicsComponent>();
239 +
240 + if (_updateParticleEmitterBB) {
241 + sgn->updateBoundingBoxTransform(transform->getWorldMatrix());
242 + _updateParticleEmitterBB = false;
243 + }
244 +
245 +
246 + F32 delta = getUsToSec(deltaTime);
247 + F32 emissionVariance = random(-_descriptor._emissionIntervalVariance, _descriptor._emissionIntervalVariance);
248 + I32 newParticles = _descriptor._emissionInterval + emissionVariance;
249 + newParticles = (I32)(newParticles * delta) / (sgn->getComponent<RenderingComponent>()->lodLevel() + 1);
250 +
251 +
252 + const vec3<F32>& eyePos = sceneState.getRenderState().getCameraConst().getEye();
253 + const vec3<F32>& origin = transform->getPosition();
254 + const Quaternion<F32>& orientation = transform->getOrientation();
255 +
256 + F32 spread = _descriptor._spread;
257 + F32 velVariance = random(-_descriptor._velocityVariance, _descriptor._velocityVariance);
258 + vec3<F32> mainDir = orientation * (WORLD_Y_AXIS * (_descriptor._velocity + velVariance));
259 +
260 + for (I32 i = 0; i < newParticles; ++i) {
261 + ParticleDescriptor& currentParticle = _particles[findUnusedParticle()];
262 + F32 lifetimeVariance = random(-_descriptor._lifetimeVariance, _descriptor._lifetimeVariance);
263 + currentParticle.life = _descriptor._lifetime + getMsToSec(lifetimeVariance);
264 + memcpy(currentParticle.pos, origin._v, 3 * sizeof(F32));
265 + // Very bad way to generate a random direction;
266 + // See for instance http://stackoverflow.com/questions/5408276/python-uniform-spherical-distribution instead,
267 + // combined with some user-controlled parameters (main direction, spread, etc)
268 + currentParticle.speed[0] = mainDir.x + (rand() % 2000 - 1000.0f) / 1000.0f * spread;
269 + currentParticle.speed[1] = mainDir.y + (rand() % 2000 - 1000.0f) / 1000.0f * spread;
270 + currentParticle.speed[2] = mainDir.z + (rand() % 2000 - 1000.0f) / 1000.0f * spread;
271 + // Very bad way to generate a random color
272 + memcpy(currentParticle.rgba, DefaultColors::RANDOM()._v, 3 * sizeof(U8));
273 + currentParticle.rgba[3] = (rand() % 256) / 2;
274 +
275 + currentParticle.size = (rand() % 1000) / 2000.0f + 0.1f;
276 + }
277 +
278 + // Simulate all particles
279 + I32 particlesCount = 0;
280 + vec3<F32> half_gravity = DEFAULT_GRAVITY * delta * 0.5f;
281 + for (ParticleDescriptor& p : _particles) {
282 + if (p.life > 0.0f) {
283 + // Decrease life
284 + p.life -= delta;
285 + if (p.life > 0.0f) {
286 + // Simulate simple physics : gravity only, no collisions
287 + for (U8 i = 0; i < 3; ++i) {
288 + p.speed[i] += half_gravity[i];
289 + p.pos[i] += p.speed[i] * delta;
290 + }
291 + p.distanceToCameraSq = vec3<F32>(p.pos).distanceSquared(eyePos);
292 +
293 +
294 + _particlePositionData[4 * particlesCount + 0] = p.pos[0];
295 + _particlePositionData[4 * particlesCount + 1] = p.pos[1];
296 + _particlePositionData[4 * particlesCount + 2] = p.pos[2];
297 + _particlePositionData[4 * particlesCount + 3] = p.size;
298 +
299 + _particleColorData[4 * particlesCount + 0] = p.rgba[0];
300 + _particleColorData[4 * particlesCount + 1] = p.rgba[1];
301 + _particleColorData[4 * particlesCount + 2] = p.rgba[2];
302 + _particleColorData[4 * particlesCount + 3] = p.rgba[3];
303 + } else {
304 + // Particles that just died will be put at the end of the buffer in SortParticles();
305 + p.distanceToCameraSq = -1.0f;
306 + }
307 +
308 + particlesCount++;
309 + }
308 310 }
311 +
312 + _particlesCurrentCount = particlesCount;
313 + _uploaded = false;
314 +
315 + SceneNode::sceneUpdate(deltaTime, sgn, sceneState);
309 316 }
310 -
311 - return 0; // All particles are taken, override the first one
312 - }
313 317
318 + // Finds a Particle in ParticlesContainer which isn't used yet. (i.e. life < 0);
319 + I32 ParticleEmitter::findUnusedParticle() {
320 + for (U32 i = _lastUsedParticle; i < _particles.size(); ++i) {
321 + if (_particles[i].life < 0.0f) {
322 + _lastUsedParticle = i;
323 + return i;
324 + }
325 + }
326 +
327 + for (I32 i = 0; i < _lastUsedParticle; ++i) {
328 + if (_particles[i].life < 0.0f) {
329 + _lastUsedParticle = i;
330 + return i;
331 + }
332 + }
333 +
334 + // All particles are taken, override the first one
335 + return 0;
336 + }
314 337 };