Last active
January 14, 2016 19:12
-
-
Save sp4cemonkey/5335250 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# 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