Created
June 23, 2016 09:49
-
-
Save dermotbalson/caa3fdc7015ffb6c600c346ca4094154 to your computer and use it in GitHub Desktop.
3D box 5
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 | |
displayMode(FULLSCREEN) | |
function setup() | |
size,radius=150,15 | |
CreateScene() | |
end | |
function CreateScene() --size is size of 3D box | |
local img=readImage("SpaceCute:Background") | |
box=CreateBlock(size,size,size,color(255),vec3(0,0,0),img) | |
local pos={} | |
balls={} | |
local img=image(3,3) | |
setContext(img) | |
background(193, 116, 77, 255) | |
setContext() | |
for i=1,6 do | |
--create random position but check they dont overlap | |
local p | |
while true do | |
p=(vec3(.2,.2,.2)+0.5*vec3(math.random()-0.5,math.random()-0.5,math.random()-0.5))*size | |
local overlap=false | |
for j=1,i-1 do if p:dist(pos[j])<radius*2 then overlap=true end end | |
if not overlap then break end | |
end | |
local v=vec3(math.random()-0.5,math.random()-0.5,math.random()-0.5)*math.random(1,5) | |
local col=color(math.random(0,255),math.random(0,255),math.random(0,255)) | |
balls[i]=Balls(radius,img,col,p,v) | |
pos[i]=p | |
end | |
--make a list of wall normals and any point on each wall surface | |
walls={{vec3(0,0,-1),vec3(0,0,size/2)},{vec3(0,0,1),vec3(0,0,-size/2)},{vec3(0,-1,0),vec3(0,size/2,0)}, | |
{vec3(0,1,0),vec3(0,-size/2,0)},{vec3(-1,0,0),vec3(size/2,0,0)},{vec3(1,0,0),vec3(-size/2,0,0)}} | |
dir=vec3(-1,2,1) | |
tween( 10, dir, { x = 0.5,y=-1 }, { easing = tween.easing.linear, | |
loop = tween.loop.pingpong } ) | |
end | |
function draw() | |
background(220) | |
perspective() | |
camera(-size/2+1,size/2-1,size/2-1,size/2,-size/2,-size/2) | |
box:draw() | |
Balls:draw(walls,dir) | |
end | |
--# Balls2 | |
Balls=class() | |
Balls.restitution=1 | |
Balls.shader=DiffuseShader | |
Balls.ambient=color(255)*0.3 | |
Balls.diffuse=color(255)*0.5 | |
Balls.direction=vec3(-1,2,-1):normalize() | |
Balls.list={} --list of all balls | |
Balls.mesh=nil | |
--r is radius, img is texture,col is colour, pos is (vec3) position, vel is (vec3) velocity | |
function Balls:init(r,img,col,pos,vel,m) | |
self.radius=r | |
self.pos=vec3(pos.x,pos.y,pos.z) | |
self.vel=vel or vec3(0,0,0) | |
self.col=col | |
self.mass=m or 1 | |
if not Balls.mesh then | |
self.mesh=CreateSphere(r,img,color(255)) | |
Balls.mesh=DiffuseShader.Setup(self.mesh,Balls.ambient,Balls.diffuse,Balls.direction) | |
end | |
self.mesh=Balls.mesh | |
table.insert(Balls.list,self) | |
self.id=#Balls.list | |
end | |
function Balls:DrawBall(dir) | |
pushMatrix() | |
translate(self.pos:unpack()) | |
DiffuseShader.Update(self.mesh,self.pos,dir,self.col) | |
self.mesh:draw() | |
popMatrix() | |
end | |
function Balls:draw(ww,dir) --walls is table of wall normals (normalised) and points | |
if dir then Balls.direction=dir:normalize() end | |
--calculate updated positions, so we can test for collisions, store them separately for now | |
for i,b in pairs(Balls.list) do | |
b.pos1=b.pos+b.vel --store in pos1, not pos | |
b.vel0=vec3(b.vel.x,b.vel.y,b.vel.z) | |
-- test for wall collisions | |
b.f=0 | |
for _,w in pairs(ww) do | |
local d1=math.abs(w[1]:dot(b.pos1-w[2]))-b.radius | |
if d1<0 then | |
local g=w[1]*(b.vel:dot(w[1])*2) | |
b.vel=vec3(b.vel.x-g.x,b.vel.y-g.y,b.vel.z-g.z) | |
local d0=math.abs(w[1]:dot(b.pos-w[2]))-b.radius | |
b.f=-d1/(d0-d1) | |
if b.f<0 then b.f=0 end | |
end | |
end | |
end | |
--test for ball collisions if there hasn't been a wall collision | |
for _,b1 in pairs(Balls.list) do | |
if b1.f==0 then | |
for _,b2 in pairs(Balls.list) do | |
if b2.f==0 and b1.id~=b2.id then --don't compare with ourselves | |
local d=b1.pos1:dist(b2.pos1)-b1.radius-b2.radius --distance between centres | |
if d<0 then | |
b1.vel,b2.vel=Balls.CollisionVelocities(b1,b2) | |
--reflect to reverse embedding | |
local d0=b1.pos:dist(b2.pos)-b1.radius-b2.radius | |
b1.f=math.max(0,d/(d-d0)) --fraction of distance to reverse | |
b2.f=b1.f | |
b2.pos=b2.pos+b2.vel0*(1-b2.f)+b2.vel*b2.f | |
b2:DrawBall() | |
break | |
end | |
end | |
if b1.f~=0 then break end | |
end | |
end | |
b1.pos=b1.pos+b1.vel0*(1-b1.f)+b1.vel*b1.f | |
b1:DrawBall(dir) | |
end | |
end | |
function Balls.CollisionVelocities(b1,b2) | |
local n=(b2.pos1-b1.pos1):normalize() | |
local c=n:dot(b1.vel-b2.vel) | |
local e=(1+Balls.restitution)*n*c/(b1.mass+b2.mass) | |
return b1.vel-b2.mass*e,b2.vel+b1.mass*e | |
end | |
function CollisionVelocities(p1,p2,v1,v2,r,m1,m2) | |
r=r or 1 | |
m1=m1 or 1 | |
m2=m2 or 1 | |
local n=(p2-p1):normalize() | |
local c=n:dot(v1-v2) | |
local u1=v1-(m2*c/(m1+m2))*(1+r)*n | |
local u2=v2+(m1*c/(m1+m2))*(1+r)*n | |
return u1,u2 | |
end | |
--# Utility | |
--#Utility | |
function CreateBlock(w,h,d,col,pos,tex) --width,height,depth,colour,position,texture | |
local x,X,y,Y,z,Z=pos.x-w/2,pos.x+w/2,pos.y-h/2,pos.y+h/2,pos.z-d/2,pos.z+d/2 | |
local v={vec3(x,y,Z),vec3(X,y,Z),vec3(X,Y,Z),vec3(x,Y,Z),vec3(x,y,z),vec3(X,y,z),vec3(X,Y,z),vec3(x,Y,z)} | |
local vert={v[1],v[2],v[3],v[1],v[3],v[4],v[2],v[6],v[7],v[2],v[7],v[3],v[6],v[5],v[8],v[6],v[8],v[7], | |
v[5],v[1],v[4],v[5],v[4],v[8],v[4],v[3],v[7],v[4],v[7],v[8],v[5],v[6],v[2],v[5],v[2],v[1]} | |
local texCoords | |
if tex then | |
local t={vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1)} | |
texCoords={t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3], | |
t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3],t[1],t[2],t[4],t[1],t[4],t[3]} | |
end | |
local n={vec3(0,0,1),vec3(1,0,0),vec3(0,0,-1),vec3(-1,0,0),vec3(1,0,0),vec3(-1,0,0)} | |
local norm={} | |
for i=1,6 do for j=1,6 do norm[#norm+1]=n[i] end end | |
local ms = mesh() | |
ms.vertices = vert | |
ms.normals=norm | |
if tex then ms.texture,ms.texCoords = tex,texCoords end | |
ms:setColors(col or color(255)) | |
return ms | |
end | |
function CreateSphere(r,tex,col,nx,ny) | |
local vertices,tc = Sphere_OptimMesh(nx or 40,ny or 20) | |
vertices = Sphere_WarpVertices(vertices) | |
for i=1,#vertices do vertices[i]=vertices[i]*r end | |
local ms = mesh() | |
ms.vertices=vertices | |
if tex then ms.texture,ms.texCoords=tex,tc end | |
ms:setColors(col or color(255)) | |
return ms | |
end | |
function Sphere_OptimMesh(nx,ny) | |
local v,t={},{} | |
local k,s,x,y,x1,x2,i1,i2,sx,sy=0,1,0,0,{},{},0,0,nx/ny,1/ny | |
local c = vec3(1,0.5,0) | |
local m1,m2 | |
for y=0,ny-1 do | |
local nx1 = math.floor( nx * math.abs(math.cos(( y*sy-0.5)*2 * math.pi/2)) ) | |
if nx1<6 then nx1=6 end | |
local nx2 = math.floor( nx * math.abs(math.cos(((y+1)*sy-0.5)*2 * math.pi/2)) ) | |
if nx2<6 then nx2=6 end | |
x1,x2 = {},{} | |
for i1 = 1,nx1 do x1[i1] = (i1-1)/(nx1-1)*sx end x1[nx1+1] = x1[nx1] | |
for i2 = 1,nx2 do x2[i2] = (i2-1)/(nx2-1)*sx end x2[nx2+1] = x2[nx2] | |
local i1,i2,n,nMax,continue=1,1,0,0,true | |
nMax = nx*2+1 | |
while continue do | |
m1,m2=(x1[i1]+x1[i1+1])/2,(x2[i2]+x2[i2+1])/2 | |
if m1<=m2 then | |
v[k+1],v[k+2],v[k+3]=vec3(x1[i1],sy*y,1)-c,vec3(x1[i1+1],sy*y,1)-c,vec3(x2[i2],sy*(y+1),1)-c | |
t[k+1],t[k+2],t[k+3]=vec2(-x1[i1]/2,sy*y) ,vec2(-x1[i1+1]/2,sy*y),vec2(-x2[i2]/2,sy*(y+1)) | |
if i1<nx1 then i1 = i1 +1 end | |
else | |
v[k+1],v[k+2],v[k+3]=vec3(x1[i1],sy*y,1)-c,vec3(x2[i2],sy*(y+1),1)-c,vec3(x2[i2+1],sy*(y+1),1)-c | |
t[k+1],t[k+2],t[k+3]=vec2(-x1[i1]/2,sy*y),vec2(-x2[i2]/2,sy*(y+1)),vec2(-x2[i2+1]/2,sy*(y+1)) | |
if i2<nx2 then i2 = i2 +1 end | |
end | |
if i1==nx1 and i2==nx2 then continue=false end | |
k,n=k+3,n+1 | |
if n>nMax then continue=false end | |
end | |
end | |
return v,t | |
end | |
function Sphere_WarpVertices(verts) | |
local m = matrix(0,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0) | |
local vx,vy,vz,vm | |
for i,v in ipairs(verts) do | |
vx,vy = v[1], v[2] | |
vm = m:rotate(180*vy,1,0,0):rotate(180*vx,0,1,0) | |
vx,vy,vz = vm[1],vm[5],vm[9] | |
verts[i] = vec3(vx,vy,vz) | |
end | |
return verts | |
end | |
--# Shader | |
DiffuseShader={} | |
function DiffuseShader.Setup(m,a,d,dir) --mesh,ambient color,diffuse color, light direction | |
m.shader=shader(DiffuseShader.shader.v,DiffuseShader.shader.f) | |
m.shader.ambientColor=a | |
m.shader.directColor=d | |
m.shader.directDirection=dir | |
return m | |
end | |
function DiffuseShader.Update(m,pos,dir,col) | |
m.shader.mModel=modelMatrix() | |
m.shader.centre=vec4(pos.x,pos.y,pos.z,1) | |
m.shader.colour=col | |
if dir then m.shader.directDirection=dir end | |
end | |
DiffuseShader.shader = { | |
v = [[ | |
uniform mat4 modelViewProjection; | |
uniform mat4 mModel; | |
attribute vec4 position; | |
attribute vec4 color; | |
attribute vec2 texCoord; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
vTexCoord = texCoord; | |
vPosition = mModel * position; | |
gl_Position = modelViewProjection * position; | |
} | |
]], | |
f = [[ | |
precision highp float; | |
uniform lowp sampler2D texture; | |
uniform vec4 ambientColor; | |
uniform vec3 directDirection; | |
uniform vec4 directColor; | |
uniform vec4 centre; | |
uniform vec4 colour; | |
varying highp vec2 vTexCoord; | |
varying highp vec4 vPosition; | |
void main() | |
{ | |
vec4 norm = normalize(vPosition-centre); | |
float diffuse = max( 0.0, dot( norm.xyz, directDirection )); //calculate strength of reflection | |
vec4 col = colour * (ambientColor + diffuse * directColor); //total color | |
col.a = 1.0; | |
gl_FragColor=col; | |
} | |
]] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment