Created
January 6, 2014 02:28
-
-
Save tnlogy/8277297 to your computer and use it in GitHub Desktop.
Example of implementing 3D touch. In progress.
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 | |
function setup() | |
displayMode(FULLSCREEN) | |
sky = Skybox() | |
forward, right, up = vec3(0,0,-1), vec3(1,0,0), vec3(0,1,0) | |
rays = {} | |
-- creating some random triangles to hit | |
tris = {} | |
local r = function (d) return math.random(-d,d) end | |
for i=1,300 do | |
local m = mesh() | |
local r1 = vec3(r(2000),r(2000),r(2000)) | |
local r2 = r1 + vec3(r(100),r(100),r(100)) | |
local r3 = r1 + vec3(r(100),r(100),r(100)) | |
m.vertices = {r1,r2,r3} | |
m:setColors(color(229, 215, 33, 255)) | |
tris[i] = m | |
end | |
end | |
function draw() | |
background(0, 0, 0, 255) | |
perspective(45, WIDTH/HEIGHT, .1, 50000) | |
-- rotate camera with iPad orientation | |
local m = matrix() | |
:rotate(RotationRate.z, forward.x,forward.y,forward.z) | |
:rotate(-RotationRate.x, right.x,right.y,right.z) | |
:rotate(-RotationRate.y, up.x,up.y,up.z) | |
forward, right, up = mult(m,forward),mult(m,right),mult(m,up) | |
camera(0,0,0, forward.x, forward.y, forward.z, up.x, up.y, up.z) | |
for i,o in ipairs(rays) do | |
pushMatrix() | |
local p = o.dir * o.t | |
translate(p.x,p.y,p.z) | |
o.m:draw() | |
popMatrix() | |
-- remove mesh when tween is finished | |
if o.t == 2000 then table.remove(rays, i) end | |
end | |
for i,o in ipairs(tris) do | |
o:draw() | |
end | |
rotate(-90,1,0,0) | |
sky:draw() | |
end | |
function touched(touch) | |
if touch.state == BEGAN then | |
local o = addRay(touch) | |
-- check if a triangle is hit | |
local d,j = false | |
for i,tri in ipairs(tris) do | |
local vs = tri.vertices | |
d = trintersect(vec3(0,0,0), o.dir, vs[1], vs[2], vs[3]) | |
if d then j=i;break end | |
end | |
if not d then | |
tween(.8, o, {t=2000}) | |
else -- if hit move to distance d then remove triangle | |
tween(.8, o, {t=d}, tween.easing.quadInOut, function () | |
table.remove(tris,j) | |
o.t = 2000 | |
end) | |
end | |
end | |
end | |
function addRay(touch) | |
-- touch position in interval -1,1 | |
local tp = vec2(touch.x/WIDTH-.5,touch.y/HEIGHT-.5) * 2 | |
-- distance from eye to screen for perspective(45) | |
local d = 1/math.tan(math.rad(45/2)) | |
local p = vec3(0,0,0) | |
local dir = forward*d + up*tp.y + right*tp.x*WIDTH/HEIGHT | |
local o = { | |
t = 100, | |
dir = dir:normalize(), | |
m = Mesh("Space Art:Red Explosion"):right(right) | |
:up(up):quad(p, 10,10) | |
} | |
table.insert(rays, o) | |
return o | |
end | |
--# Mesh | |
Mesh = class() | |
function Mesh:init(texture) | |
self.vs, self.uvs = {}, {} | |
self.buffers = {} | |
self.texture = texture | |
self.ds = { | |
right = vec3(1,0,0), | |
up = vec3(0,1,0), | |
size = vec2(1,1), | |
center = true | |
} | |
self:reset() | |
end | |
function Mesh:reset() | |
self.ps = {} | |
for k,v in pairs(self.ds) do | |
self.ps[k] = v | |
end | |
return self | |
end | |
function Mesh:defaults() | |
for k,v in pairs(self.ps) do | |
self.ds[k] = v | |
end | |
return self | |
end | |
function Mesh:buffer(name, val, n) | |
if not self.buffers[name] then | |
self.buffers[name] = {} | |
end | |
for i=1,n do | |
table.insert(self.buffers[name], val) | |
end | |
return self | |
end | |
function Mesh:right(v) self.ps.right = v;return self end | |
function Mesh:up(v) self.ps.up = v;return self end | |
function Mesh:size(v) self.ps.size = v;return self end | |
function Mesh:rotate(a) self.ps.rotate = a;return self end | |
function mult(m, v) -- matrix multiplication | |
return vec3( | |
m[1]*v.x+m[2]*v.y+m[3]*v.z, | |
m[5]*v.x+m[6]*v.y+m[7]*v.z, | |
m[9]*v.x+m[10]*v.y+m[11]*v.z | |
) | |
end | |
function Mesh:quad(pos, w, h) | |
local ps = self.ps | |
w,h = w or ps.size.x, h or w or ps.size.y | |
local right = ps.right * w | |
local up = ps.up * h | |
if ps.rotate then | |
local n = right:cross(up):normalize() | |
local m = matrix():rotate(ps.rotate, n.x,n.y,n.z) | |
right, up = mult(m,right), mult(m,up) | |
end | |
if ps.center then | |
pos = pos - (right+up)*.5 | |
end | |
table.insert(self.vs, pos) | |
table.insert(self.vs, pos+right) | |
table.insert(self.vs, pos+up) | |
table.insert(self.vs, pos+right) | |
table.insert(self.vs, pos+up+right) | |
table.insert(self.vs, pos+up) | |
table.insert(self.uvs, vec2(0,0)) | |
table.insert(self.uvs, vec2(1,0)) | |
table.insert(self.uvs, vec2(0,1)) | |
table.insert(self.uvs, vec2(1,0)) | |
table.insert(self.uvs, vec2(1,1)) | |
table.insert(self.uvs, vec2(0,1)) | |
self:reset() | |
return self | |
end | |
function Mesh:construct(s) | |
local m = mesh() | |
if s then m.shader = s end | |
m.vertices, m.texCoords = self.vs, self.uvs | |
if self.texture then | |
m.texture = asset(self.texture) | |
end | |
for k,v in pairs(self.buffers) do | |
(m:buffer(k)):set(v) | |
end | |
m:setColors(color(255, 255, 255, 255)) | |
self.m = m | |
return m | |
end | |
function Mesh:draw() | |
if not self.m then self:construct() end | |
self.m:draw() | |
end | |
-- http://en.wikipedia.org/wiki/Möller–Trumbore_intersection_algorithm | |
function trintersect(origin, dir, v1, v2, v3) | |
local epsilon = .001 | |
local e1, e2 = v2-v1, v3-v1 | |
local pv = dir:cross(e2) | |
local det = e1:dot(pv) | |
if det > -epsilon and det < epsilon then return false end | |
local invDet = 1/det | |
local tv = origin - v1 | |
local u = tv:dot(pv) * invDet | |
if (u < 0) or (u > 1) then return false end | |
local qv = tv:cross(e1) | |
local v = dir:dot(qv) * invDet | |
if v < 0 or (u+v) > 1 then return false end | |
local hitDistance = e2:dot(qv) * invDet | |
return (hitDistance >= 0) and hitDistance or false | |
end | |
--# Skybox | |
Skybox = class() | |
function Skybox:init() | |
local s = 20000 | |
local hs = s/2 | |
self.ms = { | |
Mesh("GSSkyny"):quad(vec3(0,0,-hs),s):construct(), | |
Mesh("GSSkypy"):up(vec3(0,-1,0)): | |
quad(vec3(0,0,hs),s):construct(), | |
Mesh("GSSkynz"):right(vec3(-1,0,0)): | |
up(vec3(0,0,1)):quad(vec3(0,-hs,0),s):construct(), | |
Mesh("GSSkypz"):up(vec3(0,0,1)): | |
quad(vec3(0,hs,0),s):construct(), | |
Mesh("GSSkypx"):right(vec3(0,-1,0)):up(vec3(0,0,1)): | |
quad(vec3(hs,0,0),s):construct(), | |
Mesh("GSSkynx"):right(vec3(0,1,0)):up(vec3(0,0,1)): | |
quad(vec3(-hs,0,0),s):construct() | |
} | |
end | |
function Skybox:draw() | |
for i,v in ipairs(self.ms) do | |
v:draw() | |
end | |
end | |
function Skybox:touched(touch) | |
end | |
function Skybox:hit(origin, ray) | |
local vs = self.ms[1].vertices | |
if trintersect(origin, ray, vs[1],vs[2],vs[3]) or | |
trintersect(origin, ray, vs[4],vs[5],vs[6]) then | |
print("yes") | |
end | |
end | |
--# Assets | |
-- utility function to download assets for the first run. | |
-- returns an empty image with the correct size while waiting | |
-- for http.request result. | |
local baseUrl = "http://goingsouth.meteor.com/" | |
local c = ContentScaleFactor | |
local imageMap = { | |
GSWing = {256,256,"wing.png"}, | |
GSTail = {64,256,"tailfeather.png"}, | |
GSTnlogy = {512,256,"tnlogy.png"}, | |
GSLogo = {512,256,"goingsouth.png"}, | |
GSClouds = {256,256, "clouds.png"}, | |
-- Using skybox graphics by Jochum Skoglund | |
GSSkynx = {c*512,c*512,"skybox/nx.jpg"}, | |
GSSkyny = {c*512,c*512,"skybox/ny.jpg"}, | |
GSSkynz = {c*512,c*512,"skybox/nz.jpg"}, | |
GSSkypx = {c*512,c*512,"skybox/px.jpg"}, | |
GSSkypy = {c*512,c*512,"skybox/py.jpg"}, | |
GSSkypz = {c*512,c*512,"skybox/pz.jpg"}, | |
} | |
function asset(name) | |
if string.find(name, ":") then return name end | |
local img = readImage("Documents:" .. name) | |
if img == nil then | |
local w,h,url = unpack(imageMap[name] or {1,1}) | |
img = image(w,h) | |
if url then | |
http.request(baseUrl .. url, function (data) | |
pushStyle() | |
setContext(img) | |
spriteMode(CORNER) | |
sprite(data,0,0,w,h) | |
setContext() | |
popStyle() | |
saveImage("Documents:" .. name, data) | |
end) | |
end | |
end | |
return img | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment