Created
February 24, 2013 17:53
-
-
Save tnlogy/5024809 to your computer and use it in GitHub Desktop.
Scene and Noise and Pinch Zoom
This file contains 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 | |
-- 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