1 module evael.graphics.particles.ParticleEmitter; 2 3 import evael.graphics.Drawable; 4 import evael.graphics.GraphicsDevice; 5 import evael.graphics.Texture; 6 import evael.graphics.shaders.BasicShader; 7 import evael.graphics.particles.Particle; 8 9 import evael.system.AssetLoader; 10 11 import evael.utils.Math; 12 import evael.utils.Color; 13 14 class ParticleEmitter : Drawable 15 { 16 private BasicShader m_updateShader; 17 18 private uint m_query; 19 private uint m_transformFeedbackBuffer; 20 private uint[2] m_particleBuffer; 21 22 private uint m_currentVertexBuffer; 23 24 private ParticleEmitterDefinition m_emitterDefinition; 25 26 /// Elapsed time since last frame 27 private float m_elapsedTime; 28 29 /// Current particles number 30 private int m_particlesNumber; 31 32 public this(GraphicsDevice graphics, ParticleEmitterDefinition definition) 33 { 34 this.m_graphicsDevice = graphics; 35 this.m_emitterDefinition = definition; 36 this.m_texture = definition.texture; 37 38 this.m_particlesNumber = 1; 39 this.m_elapsedTime = 0.8f; 40 41 const(char*) [6] varyings = 42 [ 43 "vPosition", 44 "vVelocity", 45 "vColor", 46 "fLifeTime", 47 "fSize", 48 "iType" 49 ]; 50 51 this.m_updateShader = AssetLoader.getInstance().load!(BasicShader)("ParticleInit", false); 52 53 gl.TransformFeedbackVaryings(this.m_updateShader.programID, 6, varyings.ptr, GL_INTERLEAVED_ATTRIBS); 54 55 this.m_updateShader.link(); 56 57 auto particle = Particle(vec3(0, 10, 0), vec3(0.0f, 0.0001f, 0.0f), vec3(0, 1, 0), 0.0f, 1, 0); 58 59 gl.GenTransformFeedbacks(1, &this.m_transformFeedbackBuffer); 60 gl.GenQueries(1, &this.m_query); 61 62 for (int i = 0; i < 2 ; i++) 63 { 64 this.m_particleBuffer[i] = this.m_graphicsDevice.createVertexBuffer(Particle.sizeof * definition.maxParticlesNumber, &particle, BufferUsage.DynamicDraw); 65 } 66 67 this.m_shader = AssetLoader.getInstance().load!(BasicShader)("Particle"); 68 } 69 70 public void dispose() 71 { 72 this.m_graphicsDevice.deleteBuffer(this.m_particleBuffer[0]); 73 this.m_graphicsDevice.deleteBuffer(this.m_particleBuffer[1]); 74 } 75 76 public override void draw(in float deltaTime, mat4 view, mat4 projection) 77 { 78 this.m_graphicsDevice.bindTexture(this.m_texture); 79 80 this.update(deltaTime); 81 82 this.m_currentVertexBuffer = 1 - this.m_currentVertexBuffer; 83 84 gl.BlendFunc(GL_SRC_ALPHA, GL_ONE); 85 gl.DepthMask(false); 86 87 this.m_graphicsDevice.disable(EnableCap.RasterizeDiscard); 88 89 this.m_graphicsDevice.enableShader(this.m_shader); 90 91 //this.m_graphicsDevice.setMatrix("modelView", modelView.ptr); 92 //this.m_graphicsDevice.setMatrix("projection", projection.ptr); 93 94 this.m_graphicsDevice.bindVertexBuffer(this.m_particleBuffer[this.m_currentVertexBuffer]); 95 96 Particle *particle = null; 97 /*gl.VertexAttribPointer(this.m_shader.getAttribute("vPosition"), 3, GL_FLOAT, false, Particle.sizeof, &particle.position); 98 gl.VertexAttribPointer(this.m_shader.getAttribute("vColor"), 3, GL_FLOAT, false, Particle.sizeof, &particle.color); 99 gl.VertexAttribPointer(this.m_shader.getAttribute("fLifeTime"), 1, GL_FLOAT, false, Particle.sizeof, &particle.lifeTime); 100 gl.VertexAttribPointer(this.m_shader.getAttribute("fSize"), 1, GL_FLOAT, false, Particle.sizeof, &particle.size); 101 gl.VertexAttribPointer(this.m_shader.getAttribute("iType"), 1, GL_INT, false, Particle.sizeof, &particle.type); 102 */ 103 this.m_graphicsDevice.drawPrimitives!(PrimitiveType.Points)(this.m_particlesNumber); 104 105 this.m_graphicsDevice.disableShader(); 106 107 gl.DepthMask(true); 108 gl.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 109 } 110 111 public void draw(in float deltaTime) 112 { 113 this.draw(deltaTime, this.m_graphicsDevice.viewMatrix, this.m_graphicsDevice.projectionMatrix); 114 } 115 116 private void update(in float deltaTime) 117 { 118 this.m_graphicsDevice.enableShader(this.m_updateShader); 119 120 import std.random; 121 122 auto randomSeed = vec3(uniform(-10.0f, 20.0f), uniform(-10, 20.0f), uniform(-10.0f, 20.0f)); 123 124 /*this.m_graphicsDevice.setUniform!("1f", float)("timePassed", deltaTime); 125 this.m_graphicsDevice.setUniform!("3fv", float)("effectPosition", this.m_position.ptr); 126 this.m_graphicsDevice.setUniform!("3fv", float)("effectColor", this.m_emitterDefinition.color.ptr); 127 this.m_graphicsDevice.setUniform!("3fv", float)("effectGravity", this.m_emitterDefinition.gravity.ptr); 128 this.m_graphicsDevice.setUniform!("3fv", float)("effectVelocityMin", this.m_emitterDefinition.velocityMin.ptr); 129 this.m_graphicsDevice.setUniform!("3fv", float)("effectVelocityRange", this.m_emitterDefinition.velocityRange.ptr); 130 131 this.m_graphicsDevice.setUniform!("1f", float)("effectLifeMin", this.m_emitterDefinition.lifeMin); 132 this.m_graphicsDevice.setUniform!("1f", float)("effectLifeRange", this.m_emitterDefinition.lifeRange); 133 134 this.m_graphicsDevice.setUniform!("1f", float)("effectSize", this.m_emitterDefinition.size); 135 */ 136 this.m_elapsedTime += deltaTime; 137 138 if(this.m_elapsedTime > this.m_emitterDefinition.nextGenerationTime) 139 { 140 //this.m_graphicsDevice.setUniform!("1i", int)("particlesCount", this.m_emitterDefinition.particlesNumberToGenerate); 141 142 this.m_elapsedTime -= this.m_emitterDefinition.nextGenerationTime; 143 144 //this.m_graphicsDevice.setUniform!("3fv", float)("randomSeed", randomSeed.ptr); 145 } 146 // else this.m_graphicsDevice.setUniform!("1i", int)("particlesCount", 0); 147 148 this.m_graphicsDevice.enable(EnableCap.RasterizeDiscard); 149 150 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, this.m_transformFeedbackBuffer); 151 this.m_graphicsDevice.bindVertexBuffer(this.m_particleBuffer[this.m_currentVertexBuffer]); 152 153 Particle *particle = null; 154 /* gl.VertexAttribPointer(this.m_updateShader.getAttribute("in_Position"), 3, GL_FLOAT, false, Particle.sizeof, &particle.position); 155 gl.VertexAttribPointer(this.m_updateShader.getAttribute("in_Velocity"), 3, GL_FLOAT, false, Particle.sizeof, &particle.velocity); 156 gl.VertexAttribPointer(this.m_updateShader.getAttribute("in_Color"), 3, GL_FLOAT, false, Particle.sizeof, &particle.color); 157 gl.VertexAttribPointer(this.m_updateShader.getAttribute("in_LifeTime"), 1, GL_FLOAT, false, Particle.sizeof, &particle.lifeTime); 158 gl.VertexAttribPointer(this.m_updateShader.getAttribute("in_Size"), 1, GL_FLOAT, false, Particle.sizeof, &particle.size); 159 gl.VertexAttribPointer(this.m_updateShader.getAttribute("in_Type"), 1, GL_INT, false, Particle.sizeof, &particle.type); 160 */ 161 gl.BindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, this.m_particleBuffer[1 - this.m_currentVertexBuffer]); 162 163 gl.BeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, this.m_query); 164 gl.BeginTransformFeedback(GL_POINTS); 165 166 this.m_graphicsDevice.drawPrimitives!(PrimitiveType.Points)(this.m_particlesNumber); 167 168 gl.EndTransformFeedback(); 169 170 gl.EndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); 171 gl.GetQueryObjectiv(this.m_query, GL_QUERY_RESULT, &this.m_particlesNumber); 172 173 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); 174 175 this.m_graphicsDevice.disableShader(); 176 } 177 } 178 179 class ParticleEmitterDefinition 180 { 181 /// Maximum particles 182 public uint maxParticlesNumber; 183 184 /// Number of particles to generate each x time 185 public uint particlesNumberToGenerate; 186 187 public float nextGenerationTime; 188 public float lifeMin, lifeRange, size; 189 190 public vec3 velocityMin; 191 public vec3 velocityRange; 192 public vec3 gravity; 193 public vec3 color; 194 195 public Texture texture; 196 197 public this(in uint maxParticlesNumber) 198 { 199 this.maxParticlesNumber = maxParticlesNumber; 200 } 201 202 public void setGeneratorProperties()(in auto ref vec3 velocityMin, in auto ref vec3 velocityMax, 203 in auto ref vec3 gravity, 204 auto ref Color color, in float lifeMin, in float lifeMax, in float size, in float nextGenerationTime, in int numberToGenerate) 205 { 206 this.velocityMin = velocityMin; 207 this.velocityRange = velocityMax - velocityMin; 208 209 this.gravity = gravity; 210 this.color = vec3(color.asFloat()[0..3]); 211 this.size = size; 212 213 this.lifeMin = lifeMin; 214 this.lifeRange = lifeMax - lifeMin; 215 216 this.nextGenerationTime = nextGenerationTime; 217 218 this.particlesNumberToGenerate = numberToGenerate; 219 } 220 }