1//
2// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7//            Based on ParticleSystem.c from
8// Book:      OpenGL(R) ES 2.0 Programming Guide
9// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
10// ISBN-10:   0321502795
11// ISBN-13:   9780321502797
12// Publisher: Addison-Wesley Professional
13// URLs:      http://safari.informit.com/9780321563835
14//            http://www.opengles-book.com
15
16#include "SampleApplication.h"
17#include "Vector.h"
18#include "shader_utils.h"
19#include "random_utils.h"
20#include "tga_utils.h"
21#include "path_utils.h"
22
23#define _USE_MATH_DEFINES
24#include <math.h>
25
26class ParticleSystemSample : public SampleApplication
27{
28  public:
29      ParticleSystemSample::ParticleSystemSample()
30        : SampleApplication("ParticleSystem", 1280, 720)
31    {
32    }
33
34    virtual bool initialize()
35    {
36        const std::string vs = SHADER_SOURCE
37        (
38            uniform float u_time;
39            uniform vec3 u_centerPosition;
40            attribute float a_lifetime;
41            attribute vec3 a_startPosition;
42            attribute vec3 a_endPosition;
43            varying float v_lifetime;
44            void main()
45            {
46                if (u_time <= a_lifetime)
47                {
48                    gl_Position.xyz = a_startPosition + (u_time * a_endPosition);
49                    gl_Position.xyz += u_centerPosition;
50                    gl_Position.w = 1.0;
51                }
52                else
53                {
54                    gl_Position = vec4(-1000, -1000, 0, 0);
55                }
56                v_lifetime = 1.0 - (u_time / a_lifetime);
57                v_lifetime = clamp(v_lifetime, 0.0, 1.0);
58                gl_PointSize = (v_lifetime * v_lifetime) * 40.0;
59            }
60        );
61
62        const std::string fs = SHADER_SOURCE
63        (
64            precision mediump float;
65            uniform vec4 u_color;
66            varying float v_lifetime;
67            uniform sampler2D s_texture;
68            void main()
69            {
70                vec4 texColor;
71                texColor = texture2D(s_texture, gl_PointCoord);
72                gl_FragColor = vec4(u_color) * texColor;
73                gl_FragColor.a *= v_lifetime;
74            }
75        );
76
77        mProgram = CompileProgram(vs, fs);
78        if (!mProgram)
79        {
80            return false;
81        }
82
83        // Get the attribute locations
84        mLifetimeLoc = glGetAttribLocation(mProgram, "a_lifetime");
85        mStartPositionLoc = glGetAttribLocation(mProgram, "a_startPosition");
86        mEndPositionLoc = glGetAttribLocation(mProgram, "a_endPosition");
87
88        // Get the uniform locations
89        mTimeLoc = glGetUniformLocation(mProgram, "u_time");
90        mCenterPositionLoc = glGetUniformLocation(mProgram, "u_centerPosition");
91        mColorLoc = glGetUniformLocation(mProgram, "u_color");
92        mSamplerLoc = glGetUniformLocation(mProgram, "s_texture");
93
94        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
95
96        // Fill in particle data array
97        for (size_t i = 0; i < mParticleCount; i++)
98        {
99            mParticles[i].lifetime = RandomBetween(0.0f, 1.0f);
100
101            float endAngle = RandomBetween(0, 2.0f * float(M_PI));
102            float endRadius = RandomBetween(0.0f, 2.0f);
103            mParticles[i].endPosition.x = sinf(endAngle) * endRadius;
104            mParticles[i].endPosition.y = cosf(endAngle) * endRadius;
105            mParticles[i].endPosition.z = 0.0f;
106
107            float startAngle = RandomBetween(0, 2.0f * float(M_PI));
108            float startRadius = RandomBetween(0.0f, 0.25f);
109            mParticles[i].startPosition.x = sinf(startAngle) * startRadius;
110            mParticles[i].startPosition.y = cosf(startAngle) * startRadius;
111            mParticles[i].startPosition.z = 0.0f;
112        }
113
114        mParticleTime = 1.0f;
115
116        TGAImage img;
117        if (!LoadTGAImageFromFile(GetExecutableDirectory() + "/smoke.tga", &img))
118        {
119            return false;
120        }
121        mTextureID = LoadTextureFromTGAImage(img);
122        if (!mTextureID)
123        {
124            return false;
125        }
126
127        return true;
128    }
129
130    virtual void destroy()
131    {
132        glDeleteProgram(mProgram);
133    }
134
135    virtual void step(float dt, double totalTime)
136    {
137        // Use the program object
138        glUseProgram(mProgram);
139
140        mParticleTime += dt;
141        if (mParticleTime >= 1.0f)
142        {
143            mParticleTime = 0.0f;
144
145            // Pick a new start location and color
146            Vector3 centerPos(RandomBetween(-0.5f, 0.5f),
147                              RandomBetween(-0.5f, 0.5f),
148                              RandomBetween(-0.5f, 0.5f));
149            glUniform3fv(mCenterPositionLoc, 1, centerPos.data);
150
151            // Random color
152            Vector4 color(RandomBetween(0.0f, 1.0f),
153                          RandomBetween(0.0f, 1.0f),
154                          RandomBetween(0.0f, 1.0f),
155                          0.5f);
156            glUniform4fv(mColorLoc, 1, color.data);
157        }
158
159        // Load uniform time variable
160        glUniform1f(mTimeLoc, mParticleTime);
161    }
162
163    virtual void draw()
164    {
165        // Set the viewport
166        glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
167
168        // Clear the color buffer
169        glClear(GL_COLOR_BUFFER_BIT);
170
171        // Use the program object
172        glUseProgram(mProgram);
173
174        // Load the vertex attributes
175        glVertexAttribPointer(mLifetimeLoc, 1, GL_FLOAT, GL_FALSE, sizeof(Particle), &mParticles[0].lifetime);
176        glVertexAttribPointer(mEndPositionLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Particle), &mParticles[0].endPosition);
177        glVertexAttribPointer(mStartPositionLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Particle), &mParticles[0].startPosition);
178
179        glEnableVertexAttribArray(mLifetimeLoc);
180        glEnableVertexAttribArray(mEndPositionLoc);
181        glEnableVertexAttribArray(mStartPositionLoc);
182
183        // Blend particles
184        glEnable(GL_BLEND);
185        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
186
187        // Bind the texture
188        glActiveTexture(GL_TEXTURE0);
189        glBindTexture(GL_TEXTURE_2D, mTextureID);
190
191        // Set the sampler texture unit to 0
192        glUniform1i(mSamplerLoc, 0);
193
194        glDrawArrays(GL_POINTS, 0, mParticleCount);
195    }
196
197  private:
198    // Handle to a program object
199    GLuint mProgram;
200
201    // Attribute locations
202    GLint mLifetimeLoc;
203    GLint mStartPositionLoc;
204    GLint mEndPositionLoc;
205
206    // Uniform location
207    GLint mTimeLoc;
208    GLint mColorLoc;
209    GLint mCenterPositionLoc;
210    GLint mSamplerLoc;
211
212    // Texture handle
213    GLuint mTextureID;
214
215    // Particle vertex data
216    struct Particle
217    {
218        float lifetime;
219        Vector3 startPosition;
220        Vector3 endPosition;
221    };
222    static const size_t mParticleCount = 1024;
223    std::array<Particle, mParticleCount> mParticles;
224    float mParticleTime;
225};
226
227int main(int argc, char **argv)
228{
229    ParticleSystemSample app;
230    return app.run();
231}
232