Skip to content

Instantly share code, notes, and snippets.

@SkyTheCoder SkyTheCoder/3DSnow.lua
Last active Dec 26, 2015

Embed
What would you like to do?
--# Main
-- 3D Snow
function setup()
displayMode(OVERLAY)
displayMode(FULLSCREEN)
parameter.watch("FPS")
parameter.boolean("camOrtho", false)
parameter.number("timeSpeed", 0, 8, 1)
parameter.number("hspread", 0, 50, 50)
parameter.number("vspread", 0, 50, 0)
parameter.number("stretch", 0, 16, 0)
parameter.boolean("vortex", false)
parameter.boolean("night", false)
instances = 300
snowMesh = mesh() -- Generate a mesh with 100 triangles randomly positioned and rotated in it, with texture coordinates
local v = {}
local tc = {}
local size = 0.5 -- Lower this to make each individual triangle smaller
local hspread = 25 -- The horizontal spread of triangles within the mesh
local vspread = 5 -- The vertical spread of triangles within the mesh
for i = 1, 100 do
local rel = {vec3(-0.5, -0.5, 0), vec3(0.5, -0.5, 0.0), vec3(0.5, 0.5, 0.0)}
for k, vec in ipairs(rel) do
math.randomseed(i * math.pi * 10000 * 123456789)
vec = matrix()
:translate((math.random() * 2 - 1) * hspread, (math.random() * 2 - 1) * vspread, (math.random() * 2 - 1) * hspread)
:rotate(math.random(0, 359), 1, 0, 0):rotate(math.random(0, 359), 0, 1, 0):rotate(math.random(0, 359), 0, 0, 1)
*vec3(vec.x * size, vec.y * size, vec.z * size)
table.insert(v, vec)
end
table.insert(tc, vec2(0, 0))
table.insert(tc, vec2(1, 0))
table.insert(tc, vec2(1, 1))
end
snowMesh.vertices = v
snowMesh.texCoords = tc
snowMesh.shader = shader("Snow") -- The shader transforms and textures the vertices
snowMesh.shader.stretch = 0
transform = snowMesh:buffer("transform")
transform.instanced = true
transform:resize(instances)
instTint = snowMesh:buffer("tint")
instTint.instanced = true
instTint:resize(instances)
instAlpha = snowMesh:buffer("alpha")
instAlpha.instanced = true
instAlpha:resize(instances)
for i = 1, instances do
math.randomseed(i)
transform[i] = matrix()
instTint[i] = color(255)
end
time = 0
ts = timeSpeed
pos = vec3(0, -50, 0) -- Variables to handle camera movement and direction
ang = vec2(1, 1):normalize()
ang2 = vec2(1, 1):normalize()
tpos = vec3(pos.x, pos.y, pos.z)
tang = vec2(ang.x, ang.y)
tang2 = vec2(ang2.x, ang2.y)
mId = 0
lId = 0
zoom = 20
tzoom = zoom
end
function draw()
if night then
background(0)
else
background(191, 220, 255)
end
local pmix = 0.0375 -- Smoothes the camera movement
pos.x = pos.x * (1 - pmix) + tpos.x * pmix
pos.y = pos.y * (1 - pmix) + tpos.y * pmix
pos.z = pos.z * (1 - pmix) + tpos.z * pmix
local amix = 0.1
ang.x = ang.x * (1 - amix) + tang.x * amix
ang.y = ang.y * (1 - amix) + tang.y * amix
ang2.x = ang2.x * (1 - amix) + tang2.x * amix
ang2.y = ang2.y * (1 - amix) + tang2.y * amix
local zmix = 0.1
zoom = zoom * (1 - zmix) + tzoom * zmix
local smix = 0.05
ts = ts * (1 - smix) + timeSpeed * smix
time = time + DeltaTime * ts
if camOrtho then
camera(ang.x * ang2.x * -100, ang2.y * -100, ang.y * ang2.x * -100, ang.x * ang2.x, ang2.y, ang.y * ang2.x, 0, 1, 0)
local aspect = WIDTH / HEIGHT
ortho(-zoom * aspect, zoom * aspect, -zoom, zoom, 0.001, 65536)
else
camera(pos.x, pos.y, pos.z, pos.x + ang.x * ang2.x, pos.y + ang2.y, pos.z + ang.y * ang2.x, 0, 1, 0)
perspective(70, WIDTH / HEIGHT, 0.001, 65536)
end
--local hspread = 0
--local vspread = 0
local step = 1
local height = 20
local pOffset = 1
local lOffset = 0
for i = 1, instances do
math.randomseed(i * math.pi * 10000)
local max = (instances * height * step * lOffset + height * step * (1 - lOffset))
local offset = (time + i * step * pOffset) % (instances * height * step * lOffset + height * step * (1 - lOffset))
transform[i] = matrix()
if vortex then
transform[i] = transform[i]:rotate(time * 45, 0, 1, 0)
end
transform[i] = transform[i]:translate(0.0, -offset * 3.0, 0.0)
:translate((math.random() * 2 - 1) * hspread, (math.random() * 2 - 1) * vspread, (math.random() * 2 - 1) * hspread)
:rotate(math.random(0, 359), 0, 1, 0)
instAlpha[i] = math.min(1, (offset / max) * height) * math.min(1, (1 - offset / max) * height)
end
snowMesh.shader.stretch = stretch
snowMesh:draw(instances)
collectgarbage()
collectgarbage()
collectgarbage()
end
function touched(touch) -- Handles camera movement and direction
if touch.state == BEGAN then
if touch.x < WIDTH / 2 and mId == 0 then
mId = touch.id
elseif touch.x >= WIDTH / 2 and lId == 0 then
lId = touch.id
end
end
if touch.id == mId then
if camOrtho then
tzoom = tzoom - touch.deltaY / 20 * (tzoom / 40 + 0.5)
else
local speed = 1 / 32
tpos.x = tpos.x + tang:rotate(math.pi / 2).x * touch.deltaX * speed
tpos.z = tpos.z + tang:rotate(math.pi / 2).y * touch.deltaX * speed
tpos.x = tpos.x + tang.x * tang2.x * touch.deltaY * speed
tpos.z = tpos.z + tang.y * tang2.x * touch.deltaY * speed
tpos.y = tpos.y + tang2.y * touch.deltaY * speed
end
elseif touch.id == lId then
local speed = zoom / 40 + 0.5
tang = tang:rotate(math.rad(touch.deltaX / 2 * speed))
tang2 = tang2:rotate(math.rad(touch.deltaY / 2 * speed))
if tang2.x < 0.01 then
if tang2.y < 0 then
tang2 = vec2(0.01, -1):normalize()
else
tang2 = vec2(0.01, 1):normalize()
end
end
end
if touch.state == ENDED or touch.state == CANCELLED then
if touch.id == mId then
mId = 0
elseif touch.id == lId then
lId = 0
end
end
end
vert=[[
uniform mat4 modelViewProjection;
attribute mat4 transform;
attribute vec4 position;
attribute vec4 color;
attribute mediump vec2 texCoord;
varying lowp vec4 vColor;
varying mediump vec2 vTexCoord;
void main()
{
vColor = color;
vTexCoord = texCoord;
gl_Position = modelViewProjection * (transform * position);
}
]]
frag=[[
uniform sampler2D texture;
varying lowp vec4 vColor;
varying mediump vec2 vTexCoord;
void main()
{
gl_FragColor = vec4(1.0);//texture2D(texture, vTexCoord) * vColor;
}
]]
--# Primitive
Primitive = class()
-- Primitive class originally by @spacemonkey
-- Edited by @SkyTheCoder to add options for width, height, length, and position, also to generate texture coordinates for the cube
--primitves gives basic mesh building for cubes and isospheres
--triangles are wound consistently to avoid gl_facing issues
function Primitive:Cube(w, h, l, x, y, z)
local s = 1
w = w or 1
h = h or w
l = l or h
x = x or 0
y = y or 0
z = z or 0
local vertices = {
vec3(-0.5*s, -0.5*s, 0.5*s), -- Left bottom front
vec3( 0.5*s, -0.5*s, 0.5*s), -- Right bottom front
vec3( 0.5*s, 0.5*s, 0.5*s), -- Right top front
vec3(-0.5*s, 0.5*s, 0.5*s), -- Left top front
vec3(-0.5*s, -0.5*s, -0.5*s), -- Left bottom back
vec3( 0.5*s, -0.5*s, -0.5*s), -- Right bottom back
vec3( 0.5*s, 0.5*s, -0.5*s), -- Right top back
vec3(-0.5*s, 0.5*s, -0.5*s), -- Left top back
}
-- now construct a cube out of the vertices above
v = {
-- Front
vertices[1], vertices[2], vertices[3],
vertices[1], vertices[3], vertices[4],
-- Right
vertices[2], vertices[6], vertices[7],
vertices[2], vertices[7], vertices[3],
-- Back
vertices[6], vertices[5], vertices[8],
vertices[6], vertices[8], vertices[7],
-- Left
vertices[5], vertices[1], vertices[4],
vertices[5], vertices[4], vertices[8],
-- Top
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[8],
-- Bottom
vertices[5], vertices[6], vertices[2],
vertices[5], vertices[2], vertices[1],
}
for i, j in ipairs(v) do
v[i] = vec3(j.x * w + x, j.y * h + y, j.z * l + z)
end
return v
end
function Primitive:CubeTexCoords(w, h, x)
w = w or 1
h = h or w
x = x or Primitive:Cube(1)
local ret = {}
for i = 1, #x / 3 do
table.insert(ret, vec2(0.0, 0.0))
table.insert(ret, vec2(w, 0.0))
table.insert(ret, vec2(w, h))
table.insert(ret, vec2(0.0, 0.0))
table.insert(ret, vec2(w, h))
table.insert(ret, vec2(0.0, h))
end
return ret
end
function Primitive:Sphere(w, h, l, x, y, z, depth)
local s = 1
w = w or 1
h = h or w
l = l or h
x = x or 0
y = y or 0
z = z or 0
depth = depth or 1
local t = (1 + math.sqrt(5)) / 2
--all the vertices of an icosohedron
local vertices = {
vec3(-1 , t, 0):normalize(),
vec3(1 , t, 0):normalize(),
vec3(-1 , -t, 0):normalize(),
vec3(1 , -t, 0):normalize(),
vec3(0 , -1, t):normalize(),
vec3(0 , 1, t):normalize(),
vec3(0 , -1, -t):normalize(),
vec3(0 , 1, -t):normalize(),
vec3(t , 0, -1):normalize(),
vec3(t , 0, 1):normalize(),
vec3(-t , 0, -1):normalize(),
vec3(-t , 0, 1):normalize()
}
--20 faces
icovertices = {
-- 5 faces around point 0
vertices[1], vertices[12], vertices[6],
vertices[1], vertices[6], vertices[2],
vertices[1], vertices[2], vertices[8],
vertices[1], vertices[8], vertices[11],
vertices[1], vertices[11], vertices[12],
-- 5 adjacent faces
vertices[2], vertices[6], vertices[10],
vertices[6], vertices[12], vertices[5],
vertices[12], vertices[11], vertices[3],
vertices[11], vertices[8], vertices[7],
vertices[8], vertices[2], vertices[9],
-- 5 faces around point 3
vertices[4], vertices[10], vertices[5],
vertices[4], vertices[5], vertices[3],
vertices[4], vertices[3], vertices[7],
vertices[4], vertices[7], vertices[9],
vertices[4], vertices[9], vertices[10],
--5 adjacent faces
vertices[5], vertices[10], vertices[6],
vertices[3], vertices[5], vertices[12],
vertices[7], vertices[3], vertices[11],
vertices[9], vertices[7], vertices[8],
vertices[10], vertices[9], vertices[2]
}
local finalVertices = {}
--divide each triangle into 4 sub triangles to make an isosphere
--this can be repeated (based on depth) for higher res spheres
for j=1,depth do
for i=1,#icovertices/3 do
midpoint1 = ((icovertices[i*3-2] + icovertices[i*3-1])/2):normalize()
midpoint2 = ((icovertices[i*3-1] + icovertices[i*3])/2):normalize()
midpoint3 = ((icovertices[i*3] + icovertices[i*3-2])/2):normalize()
--triangle 1
table.insert(finalVertices,icovertices[i*3-2])
table.insert(finalVertices,midpoint1)
table.insert(finalVertices,midpoint3)
--triangle 2
table.insert(finalVertices,midpoint1)
table.insert(finalVertices,icovertices[i*3-1])
table.insert(finalVertices,midpoint2)
--triangle 3
table.insert(finalVertices,midpoint2)
table.insert(finalVertices,icovertices[i*3])
table.insert(finalVertices,midpoint3)
--triangle 4
table.insert(finalVertices,midpoint1)
table.insert(finalVertices,midpoint2)
table.insert(finalVertices,midpoint3)
end
icovertices = finalVertices
finalVertices = {}
end
for i, j in ipairs(icovertices) do
icovertices[i] = vec3(j.x * w + x, j.y * h + y, j.z * l + z)
end
return icovertices
end
--# FPS
FPS = 0 -- Code to calculate accurate frames per secoond
local frames = 0
local time = 0
tween.delay(0.001, function()
local d = draw
draw = function()
frames = frames + 1
if math.floor(ElapsedTime) ~= math.floor(time) then
FPS = frames - 1
frames = 1
end
time = ElapsedTime
d()
end
end)
--# Shaders
Shaders = {
--shaderstart:Snow
Snow = {
vS = [[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform highp float stretch;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
attribute mat4 transform;
attribute vec4 tint;
attribute lowp float alpha;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
varying lowp vec4 vTint;
varying lowp float vAlpha;
void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = texCoord;
vTint = tint;
vAlpha = alpha;
vec4 offset = vec4(0.0);
if (vTexCoord.xy == vec2(1.0, 1.0)) offset = vec4(0.0, stretch, 0.0, 0.0);
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * (transform * (position + offset));
}
]],
fS = [[
//
// A basic fragment shader
//
//Default precision qualifier
precision highp float;
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
varying lowp vec4 vTint;
varying lowp float vAlpha;
void main()
{
//Sample the texture at the interpolated coordinate
//lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;
float dist = (1.0 - pow(min(1.0, distance(vTexCoord, vec2(0.75, 0.25)) * 4.0), 3.0));
if (dist <= 0.1) discard;
//Set the output color to the texture color
gl_FragColor = vec4(vTint.rgb, vAlpha) * dist;//col;
}
]],
},
--shaderend:Snow
}
--# Utility
function math.dec(x)
return x - math.floor(x)
end
function math.mix(a, b, x)
return a * x + b * (1 - x)
end
-- Function tweak to make shaders easier, so I can call shader("X") instead of shader(Shaders.X.vS, Shaders.X.fS)
local _shader = shader
function shader(...)
if select("#", ...) == 1 then
local data = Shaders[select(1, ...)]
if data ~= nil then
return _shader(data.vS, data.fS)
else
return _shader(...)
end
else
return _shader(...)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.