Skip to content

Instantly share code, notes, and snippets.

@tnlogy
Created February 24, 2013 17:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tnlogy/5024809 to your computer and use it in GitHub Desktop.
Save tnlogy/5024809 to your computer and use it in GitHub Desktop.
Scene and Noise and Pinch Zoom
--# Main
-- 0 UI
-- Use this function to perform your initial setup
function setup()
displayMode(FULLSCREEN)
local p1 = UI.Pad({0,100,200,200})
local p2 = UI.Pad({550,100,200,200})
local shipPos = UI.Item({500,500,scale=0.1},
UI.Image{image=readImage("Cargo Bot:Star Filled")},
UI.Text{100,30, text="Ship Position",size=32}
)
l = Level(p1, p2, shipPos)
pads = UI.Item({visible=false}, p1,p2)
sliders = UI.Item({220,150,visible=false},
UI.Slider{0,0,250,40,value=1,min=0.5,max=4,
callback=function(v) l:setScale(v) end
},
UI.Text{0,40,text="Scale",size=14},
UI.Slider{0,60,200,30,value=1,min=1,max=10,
callback=function(c) l:setSpeed(c) end
},
UI.Text{0,90,text="Speed",size=14})
function togglePads() pads.visible = not pads.visible end
function toggleSliders() sliders.visible = not sliders.visible end
scene = UI.Item({},
l,
UI.View({0,HEIGHT-300,WIDTH/2,300,bg=color(50,50,50),
range={vec2(0,0), vec2(2048,1024)},
scroll=vec2(-400,-400)},
UI.Image({image="Documents:Earth"},
shipPos)
),
UI.View({WIDTH/2,HEIGHT-300,WIDTH/2,300,
bg=color(17, 22, 24, 255),
scroll=vec2(100,100),zoom=.1},
UI.Image({image="Documents:Earth"})
),
UI.TabButton{300,0,210,40,
text="Toggle Pads",callback=togglePads},
UI.Button{300,55,210,40,
text="Toggle Sliders",callback=toggleSliders},
sliders,
pads
)
end
-- This function gets called once every frame
function draw()
background(40, 40, 50)
scene:draw()
end
function touched(touch)
scene:touched(touch)
end
--# UI
UI = {}
UI.focusColor = color(0, 144, 255, 255)
UI.buttonColor = color(127, 127, 127, 255)
function UI.clamp(value, min, max)
return math.max(min, math.min(max, value))
end
UI.Item = class()
function UI.Item:init(ps, ...)
self.x, self.y = ps[1] or ps.x or 0, ps[2] or ps.y or 0
self.scale = ps.scale or 1
self.w, self.h = ps[3] or ps.w, ps[4] or ps.h
self.visible = ps.visible
self.children = arg
self.touches = {}
self.ps = ps
if not ps.color then ps.color = color(255, 255, 255, 255) end
end
function UI.Item:draw()
for i,c in ipairs(self.children) do
if c.visible ~= false then
pushMatrix()
if c.x then translate(c.x,c.y) scale(c.scale) end
c:draw()
popMatrix()
end
end
end
function UI.Item:touched(touch, m)
if self.visible == false then return end
m = (m or matrix()):translate(self.x,self.y)
if self.pressed then self:handleTouch(touch,m) end
for i,c in ipairs(self.children) do
if c.touched then c:touched(touch, m) end
end
end
function UI.Item:handleTouch(touch,m)
local down = touch.state == BEGAN or touch.state == MOVING
local inside, pos = self:inside(touch, m)
if touch.state == BEGAN and inside then
self.touches[touch.id] = pos --touch
end
if self.touches[touch.id] and self.pressed then
self.touches[touch.id] = pos --touch
self:pressed(pos, down, inside)
end
if not down then
self.touches[touch.id] = nil
end
end
function UI.Item:inside(touch, m)
local p = vec2(m[13], m[14])
local lt = vec2(touch.x,touch.y) - p
local inside = lt.x >= 0 and lt.x <= self.w and
lt.y >= 0 and lt.y <= self.h
return inside, lt
end
UI.Rect = class(UI.Item)
function UI.Rect:init(ps, ...)
UI.Item.init(self, ps, ...)
self.m = mesh()
if ps.centerOrigin then
self.m:addRect(0, 0, self.w, self.h)
else
self.m:addRect(self.w/2, self.h/2, self.w, self.h)
end
self.m:setColors(self.ps.color)
end
function UI.Rect:draw()
self.m:draw()
UI.Item.draw(self)
end
UI.View = class(UI.Rect)
function UI.View:init(ps,...)
UI.Rect.init(self,ps,...)
self.m.texture = image(self.w, self.h)
self.zoomRange = ps.zoomRange or {0.01, math.huge}
self.range = ps.range -- {vec2,vec2}
if self.range then
local mr = self.range[2]
self.zoomRange[1] = math.max(self.zoomRange[1],
self.w/mr.x, self.h/mr.y)
end
self.scroll = ps.scroll or vec2(0,0)
self.zoom = ps.zoom or 1
end
function UI.View:pressed(pos, down)
end
function UI.View:draw()
pushMatrix()
resetMatrix()
setContext(self.m.texture)
background(self.ps.bg or color(0,0,0,0))
self:processTouches()
UI.Item.draw(self)
setContext()
popMatrix()
self.m:draw()
end
function UI.View:processTouches()
local checkRange, ts = false, {}
for k,touch in pairs(self.touches) do
table.insert(ts,touch)
end
if #ts == 2 then
local t1, t2 = ts[1], ts[2]
local dist, mid = t1:dist(t2), (t1 + t2)/2
if self.lastPinchDist ~= nil then
self.pinchDelta = dist/self.lastPinchDist
else
self.pinchOffset = (mid-self.scroll)/self.zoom
end
self.pinchCenter = mid
self.lastPinchDist = dist
else
self.pinchDelta = 1.0
checkRange = self.lastPinchDist
self.lastPinchDist = nil
end
if self.lastPinchDist and #ts ~= 0 then
self.zoom = self.zoom*self.pinchDelta
self.scroll = self.pinchCenter - self.pinchOffset*self.zoom
end
if checkRange then self:checkRange() end
translate(self.scroll.x, self.scroll.y)
scale(self.zoom)
end
function UI.View:checkRange()
local zoom = UI.clamp(self.zoom,
self.zoomRange[1], self.zoomRange[2])
local scroll = self.pinchCenter - self.pinchOffset*zoom
if self.range then
local min = self.range[1]*zoom
local max = self.range[2]*zoom - vec2(self.w, self.h)
tween(0.15, self.scroll, {
x = UI.clamp(scroll.x, -max.x, min.x),
y = UI.clamp(scroll.y, -max.y, min.y)
})
end
tween(0.15, self, {zoom = zoom})
end
UI.Text = class(UI.Rect)
function UI.Text:init(ps,...)
UI.Item.init(self,ps,...)
if not self.ps.size then self.ps.size = 20 end
self:calculateSize()
self.img = image(self.w,self.h)
self.m = mesh()
self.m.texture = self.img
self.m:addRect(self.w/2, self.h/2, self.w, self.h)
self.m:setColors(self.ps.color)
if ps.text then self:setText(ps.text) end
end
function UI.Text:calculateSize()
fontSize(self.ps.size)
local w,h = textSize(self.ps.text)
self.w, self.h = self.w or w, self.h or h
end
function UI.Text:setText(str)
self.ps.text = str
setContext(self.img)
background(self.ps.bg or color(0,0,0,0))
textMode(CENTER)
fill(198, 198, 198, 255)
fontSize(self.ps.size)
text(str,self.w/2,self.h/2)
setContext()
end
UI.Image = class(UI.Rect)
function UI.Image:init(ps, ...)
local img = readRemoteImage(ps.image)
UI.Item.init(self,ps,...)
if not self.w or not self.h then
ps.w, ps.h = spriteSize(img)
end
UI.Rect.init(self,ps,...)
self.m.texture = img
end
UI.BorderImage = class(UI.Rect)
function UI.BorderImage:init(ps, ...)
UI.Item.init(self, ps, ...)
self.m = mesh()
self.m.texture = readRemoteImage(ps.image)
local iw, ih = spriteSize(self.m.texture)
local b = ps.border
if type(b) == "number" then b = {b,b,b,b} end
local bl,br,bt,bb = unpack(b)
local mw,mh = iw-bl-br, ih-bt-bb
local ri = 0
function slice(x,y,w,h, tx,ty,tw,th)
if w == 0 or h == 0 then return end
self.m:addRect(x+w/2, y+h/2, w, h)
ri = ri + 1
self.m:setRectTex(ri,tx/iw,ty/ih,tw/iw,th/ih)
end
slice(0,0,bl,bb, 0,0,bl,bb)
slice(bl,0,self.w-bl-br,bb, bl,0,mw,bb)
slice(self.w-br,0,br,bb, bl+mw,0,br,bb)
slice(0,bb,bl,self.h-bt-bb, 0,bb,bl,mh)
slice(bl,bb,self.w-bl-br,self.h-bt-bb, bl,bb,mw,mh)
slice(self.w-br,bb,br,self.h-bt-bb, bl+mw,bb,br,mh)
slice(0,self.h-bt,bl,bt, 0,bb+mh,bl,bt)
slice(bl,self.h-bt,self.w-bl-br,bt, bl,bb+mh,mw,bt)
slice(self.w-br,self.h-bt,br,bt, bl+mw,bb+mh,br,bt)
self.m:setColors(self.ps.color or color(255,255,255,255))
end
UI.Row = class(UI.Item)
function UI.Row:init(ps,...)
UI.Item.init(self,ps,...)
ps.spacing = ps.spacing or 0
self.h = self.h or 0
local fc = self.children[1]
local x,y = 0,self.h-fc.h
for i,c in ipairs(self.children) do
c.x,c.y = x,y
x = x + c.w + ps.spacing
if self.w and x >= self.w then
x,y = 0,y - c.h - ps.spacing
end
end
end
UI.Column = class(UI.Item)
function UI.Column:init(ps,...)
UI.Item.init(self,ps,...)
local fc = self.children[1]
local x,y = 0,self.h-fc.h
for i,c in ipairs(self.children) do
c.x,c.y = x,y
y = y - c.h - ps.spacing
if y < 0 then
x,y = x+c.w+ps.spacing,self.h-fc.h
end
end
end
--# UIComponents
UI.Button = class(UI.Image)
function UI.Button:init(ps,...)
ps.image = "Documents:GreyButton"
ps.border = 10
ps.color = UI.buttonColor
UI.Item.init(self,ps,...)
if not self.w then
self.textItem = UI.Text{5,10,text=ps.text,size=self.h-20}
ps.w = self.textItem.w+10
end
UI.BorderImage.init(self,ps,...)
self.textItem = self.textItem or
UI.Text{5,10,self.w-10,text=ps.text,size=self.h-20}
self.children = {self.textItem}
end
function UI.Button:pressed(pos, down, inside)
if down then
self.m:setColors(UI.focusColor)
else
self.m:setColors(self.ps.color)
if inside and self.ps.callback then
self.ps.callback(self)
end
end
end
function UI.Button:setText(str) self.textItem:setText(str) end
UI.TabButton = class(UI.Button)
function UI.TabButton:init(ps,...)
ps.image = readImage("Cargo Bot:Platform")
ps.border = {20,20,20,0}
ps.color = UI.buttonColor
UI.BorderImage.init(self,ps,...)
self.textItem = UI.Text{5,5,self.w-10,text=ps.text,size=self.h-20}
self.children = {self.textItem}
end
UI.Slider = class(UI.Rect)
function UI.Slider:init(ps,...)
ps.color = ps.color or color(50)
UI.Rect.init(self,ps,...)
self.slideItem = UI.Rect({0,0,self.h,self.h,color=color(128)})
if not ps.textItem then
self.slideText = UI.Text({self.w,0,
text=self:formatValue(ps.max),
size=self.h})
self.children = {self.slideItem, self.slideText}
else
self.slideText = ps.textItem
self.children = {self.slideItem}
end
self:setValue(ps.value)
end
function UI.Slider:formatValue(v)
return string.format("%5.1f", v)
end
function UI.Slider:setValue(v)
self.value = v
local x = (v-self.ps.min)/(self.ps.max-self.ps.min)
self.slideItem.x = x*(self.w-self.h)
self.slideText:setText(self:formatValue(v))
if self.ps.callback then self.ps.callback(v) end
end
function UI.Slider:pressed(pos, down, inside)
UI.Button.pressed(self.slideItem, pos, down, inside)
if down then
local v = math.max(0,math.min(self.w-self.h, pos.x))
local t = v/(self.w-self.h)
local tv = self.ps.min + t*(self.ps.max-self.ps.min)
self:setValue(tv)
end
end
UI.Pad = class(UI.Image)
function UI.Pad:init(ps,...)
ps.image = readImage("Space Art:Eclipse")
UI.Image.init(self,ps,...)
self.pad = UI.Image({self.w/2, self.h/2,
self.w/2,self.h/2,centerOrigin=true,
image=readImage("Space Art:UFO")
})
self.children = {self.pad}
end
function UI.Pad:pressed(pos, down, inside)
local v = pos
self.pad.x, self.pad.y = v.x, v.y
if not down then
tween(0.1, self.pad, {x=self.w/2,y=self.h/2})
end
end
function UI.Pad:value(max)
local v = vec2(self.pad.x, self.pad.y) - vec2(self.w/2, self.h/2)
if max and v:len() > max then
v = v:normalize() * max
end
return v
end
function UI.Pad:len(max)
return math.min(max or math.huge, self:value():len())
end
function UI.Pad:angle()
return vec2(1,0):angleBetween(self:value())
end
UI.Toolbar = class(UI.Item)
function UI.Toolbar:init(ps,...)
UI.Item.init(self,ps,...)
function expand()
local r = self.children[1]
local b = self.children[2]
r.visible = not r.visible
if r.visible then
b.m:setColors(color(82, 203, 27, 255))
r.x = -200
tween(.1, r, {x=b.w})
end
end
function select(b)
expand()
self.children[2]:setText(b.ps.text)
end
function selectItem()
self.children = {
UI.Rect({y=5,w=5+45*4,h=50,visible=false},
UI.Row({5,5,h=40,spacing=5},
UI.Button({text="A",w=40,h=40,callback=select}),
UI.Button({text="B",w=40,h=40,callback=select}),
UI.Button({text="C",w=40,h=40,callback=select}),
UI.Button({text="D",w=40,h=40,callback=select})
)),
UI.Button({text="A",w=60,h=60,callback=expand})
}
end
function selectSlider()
local b = UI.Button({text="A",w=100,h=60,callback=expand})
self.children = {
UI.Rect({y=5,w=300,h=50,visible=false,
color=color(90, 95, 100, 255)
},
UI.Slider({5,5,290,40,
value=0,min=0,max=10,textItem=b.textItem})),
b
}
end
selectSlider()
end
--# ReadRemoteImage
local imageMap = {
["Documents:GreyButton"] = {128,128,
"https://dl.dropbox.com/s/97ru3ti7zvwa9eu/Photo%202013-02-23%2021%2031%2037.png"
},
["Documents:Earth"] = {2048,1024,
"https://dl.dropbox.com/s/fzrsg4mzzchm325/earth.jpg"
}
}
function readRemoteImage(name)
if type(name) == "string" then
local img = readImage(name)
if img == nil then
local data = imageMap[name]
if type(data) == "userdata" then
return data
end
local w,h,url = unpack(data or {1,1})
img = image(w,h)
imageMap[name] = img
if url then
http.request(url, function (data)
pushStyle()
setContext(img)
spriteMode(CORNER)
sprite(data,0,0,w,h)
setContext()
popStyle()
saveImage(name, data)
end)
end
end
return img
else
return name
end
end
--# Level
Level = class()
function Level:init(pad1, pad2, shipPos)
self.p1, self.p2, self.sp = pad1, pad2, shipPos
self.pos = vec2(0,0)
self.r = 0
self.s = 1
self.c = 1
self.visible = true
local r = vec2(WIDTH/10, HEIGHT/10)
self.bg = mesh()
self.bgImage = image(r.x,r.y)
self.bg.shader = shader("Basic:Invert")
self.bg.shader.fragmentProgram = Clouds
self.bg.shader.resolution = r
self.bg:addRect(r.x/2,r.y/2,r.x,r.y)
end
function Level:drawBg()
setContext(self.bgImage)
background()
self.bg.shader.time = 1 --ElapsedTime*self.c
self.bg.shader.direction = self.pos*.01
-- self.bg.shader.direction = vec2(0,1):rotate(math.rad(self.r))
self.bg:draw()
setContext()
sprite(self.bgImage, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT)
end
function Level:setScale(s) self.s = s end
function Level:setSpeed(c) self.c = c end
function Level:draw()
local d = self.p1:value(200):rotate(math.rad(self.r))
self.pos = self.pos + d*DeltaTime*5
local p = self.pos*0.01 + vec2(500,500)
self.sp.x, self.sp.y = p.x, p.y
self.r = self.r - self.p2:value(200).x * DeltaTime*5
self:drawBg()
translate(WIDTH/2, HEIGHT/2)
-- translate(self.pos.x, self.pos.y)
scale(self.s)
rotate(180+self.r)
fill(188, 204, 65, 255)
sprite("Tyrian Remastered:Plane Boss")
end
--# Clouds
Clouds = [[
precision mediump float;
#define CLOUD_COVER 0.75
#define CLOUD_SHARPNESS 0.035
uniform float time;
uniform vec2 resolution;
uniform vec2 direction;
float hash( float n )
{
return fract(sin(n)*43758.5453);
}
float noise( in vec2 x )
{
vec2 p = floor(x);
vec2 f = fract(x);
f = f*f*(3.0-2.0*f);
float n = p.x + p.y*57.0;
float res = mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x), mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y);
return res;
}
float fbm( vec2 p )
{
float f = 0.0;
f += 0.50000*noise( p ); p = p*2.02;
f += 0.25000*noise( p ); p = p*2.03;
f += 0.12500*noise( p ); p = p*2.01;
f += 0.06250*noise( p ); p = p*2.04;
f += 0.03125*noise( p );
return f/0.984375;
}
// Entry point
void main( void ) {
// Wind - Used to animate the clouds
vec2 wind_vec = direction +vec2(0.001, 0.003 + time);
// Set up domain
vec2 q = ( gl_FragCoord.xy / resolution.xy );
vec2 p = -1.0 + 3.0 * q + wind_vec;
// Fix aspect ratio
p.x *= resolution.x / resolution.y;
// Create noise using fBm
float f = fbm( 4.0*p );
float cover = CLOUD_COVER;
float sharpness = CLOUD_SHARPNESS;
float c = f - (1.0 - cover);
if ( c < 0.0 ) c = 0.0;
f = 1.0 - (pow(sharpness, c));
gl_FragColor = vec4( f, f, 1.0/f, f );
}
]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment