f@5: /*
f@5:
f@5: Copyright (C) 2015 Fiore Martin
f@5: Copyright (C) 2016 Queen Mary University of London
f@5: Author: Fiore Martin
f@5:
f@5: This file is part of Collidoscope.
f@5:
f@5: Collidoscope is free software: you can redistribute it and/or modify
f@5: it under the terms of the GNU General Public License as published by
f@5: the Free Software Foundation, either version 3 of the License, or
f@5: (at your option) any later version.
f@5:
f@5: This program is distributed in the hope that it will be useful,
f@5: but WITHOUT ANY WARRANTY; without even the implied warranty of
f@5: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@5: GNU General Public License for more details.
f@5:
f@5: You should have received a copy of the GNU General Public License
f@5: along with this program. If not, see .
f@5:
f@5: */
f@5:
f@0: #include "ParticleController.h"
f@0: #include "cinder/Rand.h"
f@0:
f@0: #include
f@0:
f@0: using namespace ci;
f@0: using std::list;
f@0:
f@0: ParticleController::ParticleController() :
f@0: mNumParticles( 0 )
f@0:
f@0: {
f@16: // uses Cinder (and OpenGL) drawing based on virtual buffer object
f@4: // see ParticleSphereCPU example in Cinder library
f@4:
f@0: mParticles.assign( kMaxParticles, Particle() );
f@0: mParticlePositions.assign( kMaxParticles, vec2( -1, -1 ) );
f@0:
f@0: mParticleVbo = gl::Vbo::create( GL_ARRAY_BUFFER, mParticlePositions, GL_DYNAMIC_DRAW );
f@0:
f@0: geom::BufferLayout particleLayout;
f@0: particleLayout.append( geom::Attrib::POSITION, 2, sizeof( vec2 ), 0 );
f@0:
f@0: auto mesh = gl::VboMesh::create( mParticlePositions.size(), GL_POINTS, { { particleLayout, mParticleVbo } } );
f@0:
f@4: // creates glsl program to run the batch with
f@0: #if ! defined( CINDER_GL_ES )
f@0: auto glsl = gl::GlslProg::create( gl::GlslProg::Format()
f@0: .vertex( CI_GLSL( 150,
f@0: uniform mat4 ciModelViewProjection;
f@0: in vec4 ciPosition;
f@0:
f@0: void main( void ) {
f@0: gl_Position = ciModelViewProjection * ciPosition;
f@0: gl_PointSize = 1.0;
f@0: }
f@0: ) )
f@0: .fragment( CI_GLSL( 150,
f@0: out vec4 oColor;
f@0:
f@0: void main( void ) {
f@0: oColor = vec4( 1.0f, 1.0f, 1.0f, 1.0f );
f@0: }
f@0: ) )
f@0: );
f@0:
f@0: mParticleBatch = gl::Batch::create( mesh, glsl );
f@0:
f@0: #else
f@0: auto glsl = gl::GlslProg::create( gl::GlslProg::Format()
f@0: .vertex( CI_GLSL( 100,
f@0: uniform mat4 ciModelViewProjection;
f@0: attribute vec4 ciPosition;
f@0:
f@0: void main( void ) {
f@0: gl_Position = ciModelViewProjection * ciPosition;
f@0: gl_PointSize = 1.0;
f@0: }
f@0: ) )
f@0: .fragment( CI_GLSL( 100,
f@0: precision highp float;
f@0:
f@0: void main( void ) {
f@0: gl_FragColor = vec4( 1, 1, 1, 1 );
f@0: }
f@0: ) )
f@0: );
f@0:
f@0: mParticleBatch = gl::Batch::create( mesh, glsl );
f@0: #endif
f@0:
f@0:
f@0: }
f@0:
f@0: void ParticleController::updateParticles()
f@0: {
f@16: // update the positions of the particles and dispose them if they've reached their timespan
f@0: for ( size_t i = 0; i < mNumParticles; i++ ){
f@0:
f@0: Particle &particle = mParticles[i];
f@0: vec2 &pos = mParticlePositions[i];
f@0:
f@0: particle.mAge++;
f@0:
f@0:
f@0: if ( (!particle.mFlyOver && particle.mAge > particle.mLifespan)
f@0: || (particle.mFlyOver && particle.mAge >= 300) ){
f@0: // dispose particle
f@0: mParticles[i] = mParticles[mNumParticles - 1];
f@0: mParticlePositions[i] = mParticlePositions[mNumParticles - 1];
f@0: mParticlePositions[mNumParticles - 1].x = -1.0f;
f@0: mParticlePositions[mNumParticles - 1].y = -1.0f;
f@0: mNumParticles--;
f@0: continue;
f@0: }
f@0:
f@0: pos += particle.mVel;
f@0: if ( ci::distance( pos, particle.mCloudCenter ) > particle.mCloudSize && !particle.mFlyOver ){
f@0: particle.mVel = rotate( particle.mVel, 5 );
f@0: }
f@0: }
f@0:
f@4: // Copy particle data onto the GPU.
f@4: // Map the GPU memory and write over it.
f@0: void *gpuMem = mParticleVbo->mapReplace();
f@0: memcpy( gpuMem, mParticlePositions.data(), mParticlePositions.size() * sizeof( vec2 ) );
f@0: mParticleVbo->unmap();
f@0: }
f@0:
f@0: void ParticleController::addParticles(int amount, const vec2 &initialLocation, const float cloudSize)
f@0: {
f@16: // reduce the particles linearly to the total number of particles already present
f@4: // the more particles aleary present the less particle are added
f@0: int reduction = ci::lmap(mNumParticles, 0, kMaxParticles, 0, kMaxParticleAdd);
f@0: amount -= reduction;
f@0:
f@0: if ( mNumParticles + amount > kMaxParticles ){
f@4: //a.k.a. return if reached kMaxParticles
f@0: amount = kMaxParticles - mNumParticles;
f@0: }
f@0:
f@0: if( amount <= 0 )
f@0: return;
f@0:
f@0: for( size_t i = 0; i < amount; i++ ){
f@0: // init new particle
f@0: Particle &particle = mParticles[mNumParticles + i];
f@0: vec2 &pos = mParticlePositions[mNumParticles + i];
f@0:
f@0: pos = initialLocation + Rand::randVec2() * 5.0f; // find a location nearby the initial location
f@0: particle.mCloudCenter = pos;
f@0: particle.mVel = Rand::randVec2() * Rand::randFloat( 1.0f, 5.0f );
f@0: particle.mCloudSize = cloudSize;
f@0: particle.mAge = 0;
f@0: particle.mLifespan = Rand::randInt( 30, 60 );
f@0: particle.mFlyOver = (Rand::randInt( 500 ) == 0);
f@0:
f@0: }
f@0:
f@0: mNumParticles += amount ;
f@0:
f@0: }
f@0:
f@0:
f@0: