Skip to content

Instantly share code, notes, and snippets.

@SkyTheCoder
Created June 9, 2014 04:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SkyTheCoder/7e155882fa6941053cdf to your computer and use it in GitHub Desktop.
Save SkyTheCoder/7e155882fa6941053cdf to your computer and use it in GitHub Desktop.
--# ExampleA
ExampleA = class()
function ExampleA:init()
self:start()
end
function ExampleA:start()
self.rot = false
if self.t ~= nil then
tween.stop(self.t)
end
self.t = tween.delay(3, function()
self.rot = true
end)
self.cam = vec2(0, -2)
self.explosions = {Explosion(vec3(0, 0, 0), true)}
parameter.action("New Explosion", function()
table.insert(self.explosions, Explosion(vec3(math.random(-5, 5), 0, math.random(-5, 5))))
end)
parameter.boolean("Anarchy", false)
end
function ExampleA:draw()
if self.rot then
self.cam = self.cam:rotate(math.rad(45 / 60))
end
if Anarchy and ElapsedTime % 1 <= 5 / 60 then
table.insert(self.explosions, Explosion(vec3(math.random(-5, 5), 0, math.random(-5, 5))))
end
camera(self.cam.x, 0.5, self.cam.y, 0, 0.5, 0)
perspective(45)
for k, v in pairs(self.explosions) do
v:draw()
if v.stopped then
table.remove(self.explosions, k)
end
end
rotate(90, 1, 0, 0)
scale(0.01)
sprite("Platformer Art:Block Brick")
end
function ExampleA:touched(touch)
tween.stop(self.t)
self.rot = false
self.cam = self.cam:rotate(math.rad(touch.deltaX))
self.t = tween.delay(3, function()
self.rot = true
end)
end
--# ExampleB
ExampleB = class()
function ExampleB:init()
self:start()
end
function ExampleB:start()
self.exp = nil
-- all the unique vertices that make up a cube
local vertices = {
vec3(-0.5, -0.5, 0.5), -- Left bottom front
vec3( 0.5, -0.5, 0.5), -- Right bottom front
vec3( 0.5, 0.5, 0.5), -- Right top front
vec3(-0.5, 0.5, 0.5), -- Left top front
vec3(-0.5, -0.5, -0.5), -- Left bottom back
vec3( 0.5, -0.5, -0.5), -- Right bottom back
vec3( 0.5, 0.5, -0.5), -- Right top back
vec3(-0.5, 0.5, -0.5), -- Left top back
}
-- now construct a cube out of the vertices above
local cubeverts = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
-- all the unique texture positions needed
local texvertices = { vec2(0.03,0.24),
vec2(0.97,0.24),
vec2(0.03,0.69),
vec2(0.97,0.69) }
-- apply the texture coordinates to each triangle
local cubetexCoords = {
-- Front
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Right
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Back
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Left
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Top
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
-- Bottom
texvertices[1], texvertices[2], texvertices[4],
texvertices[1], texvertices[4], texvertices[3],
}
-- now we make our 3 different block types
self.ms = mesh()
self.ms.vertices = cubeverts
self.ms.texture = "Planet Cute:Stone Block"
self.ms.texCoords = cubetexCoords
self.ms:setColors(255,255,255,255)
self.md = mesh()
self.md.vertices = cubeverts
self.md.texture = "Planet Cute:Dirt Block"
self.md.texCoords = cubetexCoords
self.md:setColors(255,255,255,255)
self.mg = mesh()
self.mg.vertices = cubeverts
self.mg.texture = "Planet Cute:Grass Block"
self.mg.texCoords = cubetexCoords
self.mg:setColors(255,255,255,255)
-- currently doesnt work properly without backfaces
self.mw = mesh()
self.mw.vertices = cubeverts
self.mw.texture = "Planet Cute:Water Block"
self.mw.texCoords = cubetexCoords
self.mw:setColors(255,255,255,100)
-- stick 'em in a table
self.blocks = { self.mg, self.md, self.ms }
-- our scene itself
-- numbers correspond to block positions in the blockTypes table
-- bottom middle top
self.scene = { { {1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0}, },
{ {1, 1, 1, 1, 1, 1}, {1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, },
{ {1, 1, 1, 1, 1, 1}, {1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, },
{ {1, 1, 1, 1, 1, 1}, {1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, },
{ {1, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, },
{ {1, 1, 1, 1, 1, 1}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, },
}
self.time = 0
end
function ExampleB:draw()
self.time = self.time + DeltaTime
pushMatrix()
pushStyle()
-- Make a floor
--translate(0,-Size/2,0)
--rotate(Angle,0,1,0)
local norm = vec3(0.1, 1, 0):normalize()
camera(0, 0.23, -0.12, 0, 0.15, 2, norm.x, norm.y, norm.z)
perspective(45)
rotate(90,1,0,0)
--sprite("SpaceCute:Background", 0, 0, 300, 300)
-- render each block in turn
for zi,zv in ipairs(self.scene) do
for yi,yv in ipairs(zv) do
for xi, xv in ipairs(yv) do
-- apply each transform need - rotate, scale, translate to the correct place
resetMatrix()
--rotate(Angle,0,1,0)
--local s = Size*0.25
--scale(s,s,s)
scale(0.25)
translate(xi-2, yi-2, zi-2) -- renders based on corner
-- so -2 fudges it near center
if xv > 0 then
self.blocks[xv]:draw()
end
end
end
end
popStyle()
popMatrix()
if self.exp ~= nil then
self.exp:draw()
if self.exp.stopped then
self.exp = nil
self.time = 0
end
elseif self.time >= 3 then
self.exp = Explosion(vec3(0, 0, 0.5))
end
end
function ExampleB:touched(touch)
end
--# Explosion
Explosion
= class() -- I usually move the "= class()" to another line so if it crashes, all you see is "Explosion"
local expScene = {}
local expSim = {}
local fireSim = {}
local fireTrail = {}
local rad, sin, cos = math.rad, math.sin, math.cos -- efficiency
for i = 0, 360 do -- Generate mushroom particles
local a = i % 90 * 4 -- Angle of particle
local d = i / 360 -- Distance from center
local r = rad(a) -- Radian version of angle
local s, c = sin(r), cos(r) -- Sine and cosine of angle
table.insert(expSim, {x = s * d, y = 0, z = c * d, m = 3, a = 0, s = 1}) -- Create particle
end
for i = 0, 360, 22.5 do -- Generate fire burst particles
local a = i % 90 * 4 -- Angle of particle
local d = i / 720 + 0.5 -- Distance from center
local r = rad(a) -- Radian version of angle
local s, c = sin(r), cos(r) -- Sine and cosine of angle
table.insert(fireSim, {x = s * d, y = 0, z = c * d, m = d * 3, a = 0, s = 1.2}) -- Create particle
end
pushStyle()
noStroke()
for f = 0, 180 do -- Pre-simulate particle physics and pre-render particles for unlimited explosions with no lag
if f <= 120 then
for a = 1, 10 do -- Generate stem particles
table.insert(expSim, {x = sin(f + a * 0.5), y = 0, z = cos(f + a * 0.5), m = 3, a = 0, s = 1}) -- Create particle
end
else
local c = 0 -- Disintegrate particles after prime
for i = 1, #expSim do
table.remove(expSim, i)
c = c + 1
if c > 32 then -- Limit maximum removals per frame (disintegrate effect)
break
end
end
end
for k, v in pairs(expSim) do -- Remove edge particles
if expSim[k].x <= -10 or expSim[k].x >= 10 then -- Check if on screen edge
table.remove(expSim, k)
end
end
for k, v in pairs(fireSim) do -- Remove edge particles
if fireSim[k].y <= -5 then -- Check if on screen edge
table.remove(fireSim, k)
end
end
local c = 0
for k, v in pairs(expSim) do -- Disintegrate old particles
expSim[k].a = expSim[k].a + 1 -- Add age
if expSim[k].a >= 80 then
table.remove(expSim, k)
c = c + 1
if c > 15 then -- Limit maximum removals per frame (disintegrate effect)
break
end
end
end
expScene[f] = image(128, 128) -- Init image for current frame
setContext(expScene[f]) -- Pre-render all the particles
for k, v in pairs(fireTrail) do -- Draw smoke trail behind fire
fireTrail[k].a = fireTrail[k].a + 1
fill(127)
ellipse(v.x * 5 + 64, v.y, 5)
if fireTrail[k].a >= 10 then
table.remove(fireTrail, k)
end
end
for i = 1, #fireSim do -- Draw fire particles
fireSim[i].s = fireSim[i].s * 0.993 -- Particle physics
fireSim[i].x = fireSim[i].x * math.max(1, fireSim[i].s)
fireSim[i].z = fireSim[i].z * math.max(1, fireSim[i].s)
fireSim[i].m = fireSim[i].m - 0.15
fireSim[i].y = fireSim[i].y + fireSim[i].m
local tbl = {}
for k, v in pairs(fireSim[i]) do
tbl[k] = v
end
table.insert(fireTrail, tbl)
fireTrail[#fireTrail].a = 0
pushStyle()
local middle = color(255, 255, 255, 255)
local inner = color(255, 127, 0, 255)
local rim = color(255, 0, 0, 255)--]]
local a = math.abs(fireSim[i].x * 5) / 16
local b = math.abs(fireSim[i].x * 5) / 32
fill(rim:mix(inner:mix(middle, a), b))
ellipse(fireSim[i].x * 5 + 64, fireSim[i].y, 5)
popStyle()
end
for i = 1, #expSim do -- Draw the explosion particles
if expSim[i].y < 50 then -- Particle physics
expSim[i].x = expSim[i].x * (1.05 * expSim[i].s)
expSim[i].z = expSim[i].z * (1.05 * expSim[i].s)
else
expSim[i].x = expSim[i].x * (0.99 * expSim[i].s)
expSim[i].z = expSim[i].z * (0.99 * expSim[i].s)
end
expSim[i].m = expSim[i].m * 0.95
expSim[i].y = expSim[i].y + expSim[i].m
if expSim[i].y >= 50 then
expSim[i].y = expSim[i].y + 0.1
end
pushStyle()
-- Old colors, just saved for archiving
--[[local middle = color(255, 255, 255, 255)
local inner = color(255, 127, 0, 255)
local rim = color(255, 0, 0, 255)--]]
-- New colors
local middle = color(191)
local inner = color(127)
local rim = color(63)
local a = math.abs(expSim[i].x * 5) / 16 -- Color calculation
local b = math.abs(expSim[i].x * 5) / 32
fill(rim:mix(inner:mix(middle, a), b))
ellipse(expSim[i].x * 5 + 64, expSim[i].y, 5)
popStyle()
end
setContext()
end
popStyle()
----
-- pos: vec3 (optional, vec3(x, y, z))
-- loop: boolean (optional, true or false)
-- speed: number (optional, 0.5 - 2 etc.)
-- res: number (optional, 0.5 - 2 etc.)
----
function Explosion:init(pos, loop, speed, res)
self.pos = pos or vec3(0, 0, 0)
self.loop = loop or false
self.speed = speed or 1
self.res = res or 1
self.time = 0
self.stopped = false
self.m = mesh() -- Basic 3D plane mesh
self.m.vertices = {
vec3(-0.5, 0, 0), vec3(-0.5, 1, 0), vec3(0.5, 1, 0),
vec3(-0.5, 0, 0), vec3(0.5, 1, 0), vec3(0.5, 0, 0),
}
self.m.texCoords = {
vec2(0, 0), vec2(0, 1), vec2(1, 1),
vec2(0, 0), vec2(1, 1), vec2(1, 0),
}
self.m:setColors(color(255))
self.m.shader = shader(Shaders.Discard.vS, Shaders.Discard.fS) -- Simple shader to discard backfaces and non-visible pixels
end
function Explosion:draw()
self.time = self.time + DeltaTime * self.speed -- Increment explosion time
if self.loop or self.time <= #expScene / 60 then -- If not done
pushMatrix()
pushStyle()
translate(self.pos.x, self.pos.y, self.pos.z)
smooth()
self.m.texture = expScene[math.floor(self.time % (#expScene / 60) * 60)] -- Assign current pre-rendered frame to mesh
for i = 0, 360, 45 / self.res do -- Resoltion for loop
pushMatrix()
rotate(i, 0, 1, 0)
self.m:draw() -- Draw the mesh
popMatrix()
end
popStyle()
popMatrix()
else
self.stopped = true
end
end
--# FPS
FPS = 0
local frames = 0
local time = 0
tween.delay(0, function()
local d = draw
draw = function()
frames = frames + 1
if math.floor(ElapsedTime) ~= math.floor(time) then
FPS = frames - 1
frames = 1
end
time = ElapsedTime
d()
end
end)
--# Main
-- 3D Explosion Simulator
spriteBatching(false)
-- Use this function to perform your initial setup
function setup()
print("Hello World!")
scenes = {ExampleA(), ExampleB()}
parameter.integer("Scene", 1, #scenes, 1, function()
scenes[Scene]:start()
end)
parameter.watch("FPS")
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
strokeWidth(5)
-- Do your drawing here
scenes[Scene]:draw()
end
function touched(touch)
scenes[Scene]:touched(touch)
end
--# Shaders
Shaders = {
Discard = {
vS = [[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
//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 = color;
vTexCoord = texCoord;
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * position;
}
]],
fS = [[
//
// 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()
{
if (!gl_FrontFacing) discard; // Discard backfaces
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
if (col.a <= 0.8) discard; // Discard non-visible pixels
//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