Created
December 19, 2015 14:00
-
-
Save simsaens/0ee267d280c5b0c90610 to your computer and use it in GitHub Desktop.
Codea Lightsaber Effect
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 | |
-- 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