Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save dermotbalson/caa3fdc7015ffb6c600c346ca4094154 to your computer and use it in GitHub Desktop.
Save dermotbalson/caa3fdc7015ffb6c600c346ca4094154 to your computer and use it in GitHub Desktop.
3D box 5
--# 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