Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Codea Lightsaber Effect
--# Main
-- Lightsaber
function setup()
touches = {}
lightsaber = Saber(300, 15)
lightsaber:setTransform(matrix():translate(WIDTH/2, HEIGHT/2, 0), false)
end
function draw()
background(2, 2, 2, 255)
font("GillSans")
fontSize(24)
fill(129, 158, 180, 255)
textAlign(CENTER)
text("Use two fingers to swing the lightsaber",WIDTH/2,100)
lightsaber:draw()
lightsaber:deleteTransform()
end
function touched(touch)
if touch.state == BEGAN then
table.insert(touches, touch)
table.sort(touches, function(t1,t2)
return t1.y < t2.y
end)
elseif touch.state == MOVING then
replace(touches, touch, function(t)
return touch.id == t.id
end)
elseif touch.state == ENDED or
touch.state == CANCELLED then
touches = filter(touches, function(t)
return touch.id ~= t.id
end)
end
local xform = transformFromTouches(touches)
if xform ~= nil then
lightsaber:setTransform(xform, true)
end
end
function transformFromTouches(t)
local gesture = map(first(t,2), function(touch)
return vec2(touch.x, touch.y)
end)
if #gesture < 2 then
return nil
end
local up = vec2(0, 1)
local dir = (gesture[2] - gesture[1]):normalize()
local center = gesture[2]
local angle = math.deg(up:angleBetween(dir))
return matrix()
:translate(center.x, center.y, 0)
:rotate(angle, 0, 0, 1)
end
--# Saber
Saber = class()
function Saber:init(length, trail)
self.baseTex = blurredSquare(128,128)
self.bladeGlowTex = blurredSquare(18,length)
self.bladeTex = roundedTex(18,length,1)
self.transformCount = trail or 20
self.transformHistory = { matrix() }
self.points = { vec3(0,0,0), vec3(0,20,0), vec3(0,length-20,0), vec3(0,length,0) }
self.length = length
self.bladeMeshBase = mesh()
self.bladeMesh = mesh()
end
function Saber:computeMeshes()
self.bladeMesh = meshByExtrudingPointsThroughTransforms(self.points, self.transformHistory)
self.bladeMeshBase.vertices = self.bladeMesh.vertices
self.bladeMesh.texture = self.baseTex
end
function Saber:setTransform(xform, record)
if record == true then
table.insert(self.transformHistory, 1, xform)
if #self.transformHistory == (self.transformCount + 1) then
table.remove(self.transformHistory, self.transformCount + 1)
end
else
self.transformHistory = { xform }
end
self:computeMeshes()
end
function Saber:deleteTransform()
if #self.transformHistory > 1 then
table.remove(self.transformHistory)
end
self:computeMeshes()
end
function Saber:draw()
pushStyle()
pushMatrix()
modelMatrix(self.transformHistory[1])
blendMode(NORMAL)
fill(71, 91, 122, 255)
rectMode(CORNER)
rect(-5, -120, 10, 124)
fill(110, 124, 149, 255)
rect(-10, -110, 20, 100)
popMatrix()
fill(248, 39, 21, 255)
blendMode(ADDITIVE)
self.bladeMeshBase:draw()
fill(223, 153, 155, 255)
self.bladeMesh:draw()
pushMatrix()
modelMatrix(self.transformHistory[1])
tint(248, 39, 21, 255)
spriteMode(CORNER)
sprite(self.bladeTex, -9, 0)
tint(255, 255, 255, 255)
spriteMode(CORNER)
sprite(self.bladeGlowTex, -9, -30, 18, self.length + 60)
popMatrix()
popStyle()
end
function Saber:touched(touch)
-- Codea does not automatically call this method
end
--# Extrude
function transformedPoints(points, xform)
return map(points, function(p)
return xform * p
end)
end
function quadsByExtrudingPoints(pointsA, pointsB, t0, t1)
local v = {}
local tc = {}
local c = (#pointsA - 1)
for i = 1,c do
local tcA1 = vec2(t0, (i-1)/c)
local tcA2 = vec2(t0, i/c)
local tcB1 = vec2(t1, tcA1.y)
local tcB2 = vec2(t1, tcA2.y)
local pA1 = pointsA[i]
local pA2 = pointsA[i+1]
local pB1 = pointsB[i]
local pB2 = pointsB[i+1]
append(v, {pA1, pA2, pB2, pB2, pB1, pA1})
append(tc, {tcA1, tcA2, tcB2, tcB2, tcB1, tcA1})
end
return v, tc
end
function meshByExtrudingPointsThroughTransforms(points, xforms)
-- Result
local m = mesh()
local v = {}
local tc = {}
local c = (#xforms - 1)
for i = 1,c do
local x1 = xforms[i]
local x2 = xforms[i+1]
local pStart = transformedPoints(points, x1)
local pEnd = transformedPoints(points, x2)
local section, stc = quadsByExtrudingPoints(pStart, pEnd, (i-1)/c, (i+1)/c)
append(v, section)
append(tc, stc)
end
m.vertices = v
m.texCoords = tc
return m
end
--# Table
function map(t, f)
local m = {}
for i,v in pairs(t) do
m[i] = f(v)
end
return m
end
function filter(t, f)
local m = {}
for i,v in pairs(t) do
if f(v) then m[i] = v end
end
return m
end
function replace(t, r, f)
for i,v in pairs(t) do
if f(v) then t[i] = r end
end
end
function count(t)
local c = 0
for _,_ in pairs(t) do
c = c + 1
end
return c
end
function append(t, d)
for i,v in pairs(d) do
table.insert(t, v)
end
end
function first(t, n)
local set = {}
for k,v in pairs(t) do
if #set > n then
break
end
table.insert(set, v)
end
return set
end
--# TexGen
function roundRect(x,y,w,h,r)
ellipseMode(RADIUS)
rectMode(CENTER)
ellipse(x - w/2 + r, y - h/2 + r, r)
ellipse(x + w/2 - r, y - h/2 + r, r)
ellipse(x + w/2 - r, y + h/2 - r, r)
ellipse(x - w/2 + r, y + h/2 - r, r)
rect(x,y,w,h - r*2)
rect(x,y,w - r*2,h)
end
function roundedTex(w, h, f)
local tex = image(w,h)
setContext(tex)
fill(255)
local rw,rh = w*f,h*f
roundRect(w/2,h/2,rw,rh,math.min(rh*0.0625,rw/2))
setContext()
return tex
end
function blurredSquare(w, h)
local tex = roundedTex(w,h,0.75)
local m = mesh()
m:addRect(w/2,h/2,w,h)
m.texture = tex
m.shader = shader("Filters:Radial Blur")
m.shader.sampleDist = 1.0
m.shader.sampleStrength = 2.2
tex = image(w,h)
setContext(tex)
m:draw()
setContext()
m.texture = tex
tex = image(w,h)
setContext(tex)
m:draw()
setContext()
return tex
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.