Skip to content

Instantly share code, notes, and snippets.

@sp4cemonkey
Last active January 14, 2016 19:12
Show Gist options
  • Save sp4cemonkey/5335250 to your computer and use it in GitHub Desktop.
Save sp4cemonkey/5335250 to your computer and use it in GitHub Desktop.
--# Main
-- Particle Effect
-- Use this function to perform your initial setup
function setup()
displayMode(FULLSCREEN)
--to get ghost trails
backingMode(RETAINED)
--b=Backup("Particle Fireworks Ver 002")
gravity = 20
particles = Particles(createCircleTexture(), gravity)
launches = {}
end
function createCircleTexture()
img = image(100,100)
setContext(img)
for i=1,25 do
fill(255,255,255,255/(26-i))
ellipse(50,50,(26-i)*4,(26-i)*4)
end
return img
end
--function Particles:addParticle(startX,startY,endDeltaX, endDeltaY, timeToLive, startAngle, deltaAngle, startSize, deltaSize, startAlpha, deltaAlpha, easePos, easeSize, easeRot, easeAlpha, particleColor)
function generateFirework(x,y)
workCol = color(math.random(255), math.random(255), math.random(255), 255)
--launch
launchX = WIDTH/2 + math.random(30) - 15
--adjust target position so gravity won't stop it getting there
targetY = y + (0.5 * gravity * 2^2)
particles:addParticle(launchX, 0, x - launchX, targetY, ElapsedTime, 2, 0, 0, 10, 0, 1, .5, 36, 1, 1, 4, workCol)
--bang
doubleBang = math.random(3)
numBangs = math.random(30)+10
bangSize = math.random(50)+100
rings = math.random(4)
if doubleBang == 1 then
--double bang, only 1 ring)
rings = 1
end
generateBang(x,y, ElapsedTime+2, math.random(3)+1, numBangs, bangSize, rings, workCol, doubleBang)
end
function generateBang(x,y,time, timeToLive, points, size, rings, col, rebang)
for j=1,rings do
for i=1,points do
target = vec2(0,size*(j+1)/(rings+1) + math.random(10)):rotate(math.rad(360*i/points + math.random(10)))
ttl = timeToLive + (math.random(20) - 10)/10
particles:addParticle(x, y, target.x, target.y, time, ttl, 0,0,5,5,1,0,36,1,1,4,col)
if rebang == 1 then
generateBang(x+target.x, y+target.y - (0.5*gravity*ttl^2), time+ttl, math.random(3), math.random(5), size/(math.random(3)-1), 1, col, 0)
end
end
end
end
function touched(touch)
if touch.state == BEGAN then
--to avoid stalling queue fireworks, max launch 1 per frame
launches[touch.id] = touch
end
end
-- This function gets called once every frame
function draw()
for k,v in pairs(launches) do
generateFirework(v.x, v.y)
launches[k] = nil
break
end
output.clear()
--debug info
print(1/DeltaTime)
print(particles.numParticles)
--background(0, 0, 0, 5)
fill(0, 0, 0, 20)
--fade to black
rect(0, 0 , WIDTH, HEIGHT)
--draw the particles
particles:draw()
end
--# Particles
Particles = class()
function Particles:init(particleImage, gravity)
self.particleMesh = mesh()
self.particleMesh.shader = shader(ParticleShader.vertexShader, ParticleShader.fragmentShader)
self.particleMesh.texture = particleImage
self.particleMesh.shader.gravity = gravity
self.particleBuffers = {}
--x,y is the start location z,w is the movement to the end location
self.particleBuffers["startAndEnd"] = self.particleMesh:buffer("startAndEnd")
--x,y is the start time and time to live z,w is the start rotation and delta rotation
self.particleBuffers["timeAndRotation"] = self.particleMesh:buffer("timeAndRotation")
--x,y is the start size and delta size, z,w is the start alpha and alpha delta
self.particleBuffers["sizeAndAlpha"] = self.particleMesh:buffer("sizeAndAlpha")
--easing mode x is position, y is rotation, z is size, w is alpha
--modes
--type 1 linear, 2 quadin, 4 quadout, 8 expoin, 16 expoout
--apply gravity (only applies to position) add 32
self.particleBuffers["easing"] = self.particleMesh:buffer("easing")
self.particleDeath = {}
self.numParticles = 0
end
function Particles:draw()
self.particleMesh.shader.time = ElapsedTime
self.particleMesh:draw()
end
function Particles:addParticle(startX,startY,endDeltaX, endDeltaY, startTime, timeToLive, startAngle, deltaAngle, startSize, deltaSize, startAlpha, deltaAlpha, easePos, easeSize, easeRot, easeAlpha, particleColor)
etime = startTime
firstVertex = 0
for i=1,self.numParticles do
if self.particleDeath[i] < ElapsedTime then
firstVertex = i*6-5
break
end
end
if firstVertex == 0 then
firstVertex = self.particleMesh.size + 1
self.particleMesh:addRect(0,0,1,1)
self.particleBuffers["startAndEnd"]:resize(firstVertex + 5)
self.particleBuffers["timeAndRotation"]:resize(firstVertex + 5)
self.particleBuffers["sizeAndAlpha"]:resize(firstVertex + 5)
self.particleBuffers["easing"]:resize(firstVertex + 5)
self.numParticles = self.numParticles + 1
end
self.particleMesh:setRectColor((firstVertex+5)/6, particleColor)
self.particleDeath[(firstVertex+5)/6] = etime+timeToLive
for i=firstVertex,firstVertex+5 do
self.particleBuffers["startAndEnd"][i] = vec4(startX,startY,endDeltaX, endDeltaY)
self.particleBuffers["timeAndRotation"][i] = vec4(etime, timeToLive, math.rad(startAngle), math.rad(deltaAngle))
self.particleBuffers["sizeAndAlpha"][i] = vec4(startSize, deltaSize, startAlpha, deltaAlpha)
self.particleBuffers["easing"][i] = vec4(easePos, easeSize, easeRot, easeAlpha)
end
end
ParticleShader = {
vertexShader = [[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform float time;
uniform float gravity;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 color;
attribute vec4 position;
attribute vec2 texCoord;
attribute vec4 startAndEnd;
attribute vec4 timeAndRotation;
attribute vec4 sizeAndAlpha;
attribute vec4 easing;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
//Pass the mesh color to the fragment shader
vColor = easing;
vColor = color;
vTexCoord = texCoord;
vec4 lPosition = position;
if (time < (timeAndRotation.x + timeAndRotation.y)) {
//type 1 linear, 2 quadin, 4 quadout, 8 expoin, 16 expoout
float timeDelta = (time - timeAndRotation.x)/timeAndRotation.y;
float positionDelta = timeDelta;
float rotationDelta = timeDelta;
float sizeDelta = timeDelta;
float alphaDelta = timeDelta;
if (mod(easing.x, 4.0) > 1.5) {
positionDelta = pow(timeDelta, 2.0);
}
if (mod(easing.x, 8.0) > 3.5) {
positionDelta = 1.0 - pow((1.0-timeDelta),2.0);
}
if (mod(easing.x, 16.0) > 7.5) {
positionDelta = pow(2.0, 10.0 * (timeDelta - 1.0)) - 0.001;
}
if (mod(easing.x, 32.0) > 15.5) {
positionDelta = 1.001 * (-pow(2.0, -10.0 * timeDelta) + 1.0);
}
if (mod(easing.y, 4.0) > 1.5) {
rotationDelta = pow(timeDelta, 2.0);
}
if (mod(easing.y, 8.0) > 3.5) {
rotationDelta = 1.0 - pow((1.0-timeDelta),2.0);
}
if (mod(easing.y, 16.0) > 7.5) {
rotationDelta = pow(2.0, 10.0 * (timeDelta - 1.0)) - 0.001;
}
if (mod(easing.y, 32.0) > 15.5) {
rotationDelta = 1.001 * (-pow(2.0, -10.0 * timeDelta) + 1.0);
}
if (mod(easing.z, 4.0) > 1.5) {
sizeDelta = pow(timeDelta, 2.0);
}
if (mod(easing.z, 8.0) > 3.5) {
sizeDelta = 1.0 - pow((1.0-timeDelta),2.0);
}
if (mod(easing.z, 16.0) > 7.5) {
sizeDelta = pow(2.0, 10.0 * (timeDelta - 1.0)) - 0.001;
}
if (mod(easing.z, 32.0) > 15.5) {
sizeDelta = 1.001 * (-pow(2.0, -10.0 * timeDelta) + 1.0);
}
if (mod(easing.w, 4.0) > 1.5) {
alphaDelta = pow(timeDelta, 2.0);
}
if (mod(easing.w, 8.0) > 3.5) {
alphaDelta = 1.0 - pow((1.0-timeDelta),2.0);
}
if (mod(easing.w, 16.0) > 7.5) {
alphaDelta = pow(2.0, 10.0 * (timeDelta - 1.0)) - 0.001;
}
if (mod(easing.w, 32.0) > 15.5) {
alphaDelta = 1.001 * (-pow(2.0, -10.0 * timeDelta) + 1.0);
}
float sinAngle = sin(timeAndRotation.z + rotationDelta * timeAndRotation.w);
float cosAngle = cos(timeAndRotation.z + rotationDelta * timeAndRotation.w);
lPosition.xy = vec2(dot(lPosition.xy, vec2(cosAngle, -sinAngle)),
dot(lPosition.xy, vec2(sinAngle, cosAngle)));
lPosition.xy = lPosition.xy * (sizeAndAlpha.x + sizeDelta * sizeAndAlpha.y);
lPosition.xy = lPosition.xy + startAndEnd.xy + (startAndEnd.zw * positionDelta);
if (mod(easing.x,64.0) > 31.5) {
//adjust position by gravity
lPosition.y = lPosition.y - (0.5 * gravity * pow((time - timeAndRotation.x), 2.0));
}
//not sure why just alpha channel isn't working...
//vColor.a = sizeAndAlpha.z + (alphaDelta * sizeAndAlpha.w);
vColor = vColor * sizeAndAlpha.z + (alphaDelta * sizeAndAlpha.w);
}
else {
lPosition = vec4(-1000.0,-1000.0, -1000.0, -1000.0);
}
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * lPosition;
}
]],
fragmentShader = [[
//
// A basic fragment shader
//
//Default precision qualifier
precision highp float;
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
//Set the output color to the texture color
gl_FragColor = col;
}
]]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment