|
@@ -12,95 +12,100 @@ function scene.load()
|
|
|
|
|
|
-- A series of sideways-drifting cubes (these will be stenciled)
|
|
-- A series of sideways-drifting cubes (these will be stenciled)
|
|
scene.driftCubeCount = 60
|
|
scene.driftCubeCount = 60
|
|
- scene.boundMin = lovr.math.newVec3(-10, -1, -10)
|
|
|
|
- scene.boundMax = lovr.math.newVec3( 10, 9, 10)
|
|
|
|
|
|
+ scene.boundMin = vector(-10, -1, -10)
|
|
|
|
+ scene.boundMax = vector( 10, 9, 10)
|
|
scene.speed = 1
|
|
scene.speed = 1
|
|
scene.driftCubeSize = 0.6
|
|
scene.driftCubeSize = 0.6
|
|
scene.driftCubes = {}
|
|
scene.driftCubes = {}
|
|
- for i=1,scene.driftCubeCount do
|
|
|
|
|
|
+ for i = 1, scene.driftCubeCount do
|
|
scene.generateDriftCube(i, true)
|
|
scene.generateDriftCube(i, true)
|
|
end
|
|
end
|
|
|
|
|
|
-- A 3x3 cube made of two different stencil types
|
|
-- A 3x3 cube made of two different stencil types
|
|
- scene.stencilCubeCenter = lovr.math.newVec3(0, 1.5, -0.5)
|
|
|
|
|
|
+ scene.stencilCubeCenter = vector(0, 1.5, -0.5)
|
|
scene.stencilCubeSize = 0.25
|
|
scene.stencilCubeSize = 0.25
|
|
scene.stencilCubeRotate = 0
|
|
scene.stencilCubeRotate = 0
|
|
scene.stencilCubeRotateSpeed = 1
|
|
scene.stencilCubeRotateSpeed = 1
|
|
scene.stencilCubes = {}
|
|
scene.stencilCubes = {}
|
|
- for z=-1,1 do for y=-1,1 do for x=-1,1 do -- Iterate over every cube
|
|
|
|
- if not (x==0 and y==0 and z==0) then -- Except the center
|
|
|
|
- table.insert(scene.stencilCubes, {lovr.math.newVec3(x,y,z), math.random(1,2)}) -- Cube center and stencil type
|
|
|
|
|
|
+ for z = -1, 1 do
|
|
|
|
+ for y = -1, 1 do
|
|
|
|
+ for x = -1, 1 do -- Iterate over every cube
|
|
|
|
+ if not (x==0 and y==0 and z==0) then -- Except the center
|
|
|
|
+ table.insert(scene.stencilCubes, { vector(x,y,z), math.random(1,2) }) -- Cube center and stencil type
|
|
|
|
+ end
|
|
|
|
+ end
|
|
end
|
|
end
|
|
- end end end
|
|
|
|
|
|
+ end
|
|
|
|
|
|
-- Three cubemap skyboxes, of different colors
|
|
-- Three cubemap skyboxes, of different colors
|
|
scene.skybox = {}
|
|
scene.skybox = {}
|
|
local skyboxTextureSize = 32
|
|
local skyboxTextureSize = 32
|
|
local bandSize=3
|
|
local bandSize=3
|
|
for cube_index,colors in ipairs{
|
|
for cube_index,colors in ipairs{
|
|
- {{1, 0.5, 1}, {1,1,1}}, -- Fuschia and white
|
|
|
|
- {{1, 1, 0.5}, {0,0,0}}, -- Yellow and black
|
|
|
|
- {{1,1,1}, {0.9,0.9,0.9}}, -- White and silver
|
|
|
|
|
|
+ {{ 1, 0.5, 1 }, { 1, 1, 1 }}, -- Fuschia and white
|
|
|
|
+ {{ 1, 1, 0.5 }, { 0, 0, 0 }}, -- Yellow and black
|
|
|
|
+ {{ 1, 1, 1 }, { 0.9, 0.9, 0.9 }}, -- White and silver
|
|
} do
|
|
} do
|
|
local layers = {}
|
|
local layers = {}
|
|
- for layer=1,6 do -- 6 layers to a cubemap
|
|
|
|
- local data = lovr.data.newImage(skyboxTextureSize, skyboxTextureSize, "rgba8")
|
|
|
|
- for y=1,skyboxTextureSize do for x=1,skyboxTextureSize do
|
|
|
|
- local isBorder = x==1 or x==skyboxTextureSize or y==1 or y==skyboxTextureSize -- Solid color in corners
|
|
|
|
- local direction = cube_index==3 and -1 or 1 -- Reverse direction on third cubemap
|
|
|
|
- local whichColor = (isBorder or ((x+direction*y-2)%(bandSize*2)>=bandSize)) and 1 or 2 -- Diagonal stripes
|
|
|
|
- data:setPixel(x-1,y-1,unpack(colors[whichColor]))
|
|
|
|
- end end
|
|
|
|
|
|
+ for layer = 1, 6 do -- 6 layers to a cubemap
|
|
|
|
+ local data = lovr.data.newImage(skyboxTextureSize, skyboxTextureSize, 'rgba8')
|
|
|
|
+ for y = 1, skyboxTextureSize do
|
|
|
|
+ for x = 1, skyboxTextureSize do
|
|
|
|
+ local isBorder = x == 1 or x == skyboxTextureSize or y == 1 or y == skyboxTextureSize -- Solid color in corners
|
|
|
|
+ local direction = cube_index==3 and -1 or 1 -- Reverse direction on third cubemap
|
|
|
|
+ local whichColor = (isBorder or ((x + direction * y - 2) % (bandSize * 2) >= bandSize)) and 1 or 2 -- Diagonal stripes
|
|
|
|
+ data:setPixel(x - 1, y - 1, unpack(colors[whichColor]))
|
|
|
|
+ end
|
|
|
|
+ end
|
|
table.insert(layers, data)
|
|
table.insert(layers, data)
|
|
end
|
|
end
|
|
table.insert(scene.skybox, lovr.graphics.newTexture(layers))
|
|
table.insert(scene.skybox, lovr.graphics.newTexture(layers))
|
|
end
|
|
end
|
|
|
|
|
|
- scene.sampler = lovr.graphics.newSampler({filter="nearest"})
|
|
|
|
|
|
+ scene.sampler = lovr.graphics.newSampler({ filter = 'nearest' })
|
|
end
|
|
end
|
|
|
|
|
|
-local function randomQuaternion() -- Generate one random rotation
|
|
|
|
- -- Formula from http://planning.cs.uiuc.edu/node198.html
|
|
|
|
- local u,v,w = math.random(), math.random(), math.random()
|
|
|
|
- return lovr.math.newQuat(
|
|
|
|
- math.sqrt(1-u)*math.sin(2*v*math.pi),
|
|
|
|
- math.sqrt(1-u)*math.cos(2*v*math.pi),
|
|
|
|
- math.sqrt(u)*math.sin(2*w*math.pi),
|
|
|
|
- math.sqrt(u)*math.cos(2*w*math.pi),
|
|
|
|
- true -- Raw components
|
|
|
|
- )
|
|
|
|
|
|
+local function randomQuaternion()
|
|
|
|
+ local u, v, w = math.random(), math.random(), math.random()
|
|
|
|
+ return quaternion.pack(
|
|
|
|
+ math.sqrt(1 - u) * math.sin(2 * v * math.pi),
|
|
|
|
+ math.sqrt(1 - u) * math.cos(2 * v * math.pi),
|
|
|
|
+ math.sqrt(u) * math.sin(2 * w * math.pi),
|
|
|
|
+ math.sqrt(u) * math.cos(2 * w * math.pi)
|
|
|
|
+ )
|
|
end
|
|
end
|
|
|
|
|
|
function scene.generateDriftCube(i, randomX) -- Generate one cube with random position and color and a random rotational velocity
|
|
function scene.generateDriftCube(i, randomX) -- Generate one cube with random position and color and a random rotational velocity
|
|
local cube = {}
|
|
local cube = {}
|
|
- cube.at = lovr.math.newVec3()
|
|
|
|
|
|
+ local x, y, z
|
|
if randomX then
|
|
if randomX then
|
|
- cube.at.x = scene.boundMin.x + math.random()*(scene.boundMax.x-scene.boundMin.x)
|
|
|
|
|
|
+ x = scene.boundMin.x + math.random() * (scene.boundMax.x - scene.boundMin.x)
|
|
else
|
|
else
|
|
- cube.at.x = scene.boundMin.x
|
|
|
|
|
|
+ x = scene.boundMin.x
|
|
end
|
|
end
|
|
- cube.at.y = scene.boundMin.y + math.random()*(scene.boundMax.y-scene.boundMin.y)
|
|
|
|
- cube.at.z = scene.boundMin.z + math.random()*(scene.boundMax.z-scene.boundMin.z)
|
|
|
|
|
|
+ y = scene.boundMin.y + math.random()*(scene.boundMax.y-scene.boundMin.y)
|
|
|
|
+ z = scene.boundMin.z + math.random()*(scene.boundMax.z-scene.boundMin.z)
|
|
|
|
|
|
|
|
+ cube.at = vector(x, y, z)
|
|
cube.rotateBasis = randomQuaternion()
|
|
cube.rotateBasis = randomQuaternion()
|
|
- cube.rotateTarget = lovr.math.newQuat(cube.rotateBasis:conjugate())
|
|
|
|
|
|
+ cube.rotateTarget = cube.rotateBasis:conjugate()
|
|
cube.rotate = cube.rotateBasis
|
|
cube.rotate = cube.rotateBasis
|
|
scene.driftCubes[i] = cube
|
|
scene.driftCubes[i] = cube
|
|
end
|
|
end
|
|
|
|
|
|
function scene.update(dt) -- On each frame, move each cube and spin it a little
|
|
function scene.update(dt) -- On each frame, move each cube and spin it a little
|
|
- for i,cube in ipairs(scene.driftCubes) do
|
|
|
|
- cube.at.x = cube.at.x + scene.speed*dt
|
|
|
|
|
|
+ for i, cube in ipairs(scene.driftCubes) do
|
|
|
|
+ cube.at = cube.at + vector(scene.speed * dt, 0, 0)
|
|
if cube.at.x > scene.boundMax.x then -- If cube left the scene bounds respawn it
|
|
if cube.at.x > scene.boundMax.x then -- If cube left the scene bounds respawn it
|
|
scene.generateDriftCube(i)
|
|
scene.generateDriftCube(i)
|
|
else
|
|
else
|
|
- local rotateAmount = (cube.at.x - scene.boundMin.x)/(scene.boundMax.x-scene.boundMin.x)
|
|
|
|
- cube.rotate = cube.rotateBasis:slerp( cube.rotateTarget, rotateAmount )
|
|
|
|
|
|
+ local rotateAmount = (cube.at.x - scene.boundMin.x) / (scene.boundMax.x - scene.boundMin.x)
|
|
|
|
+ cube.rotate = cube.rotateBasis:slerp(cube.rotateTarget, rotateAmount)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- Also rotate the center cube
|
|
-- Also rotate the center cube
|
|
- scene.stencilCubeRotate = scene.stencilCubeRotate + dt*scene.stencilCubeRotateSpeed
|
|
|
|
|
|
+ scene.stencilCubeRotate = scene.stencilCubeRotate + dt * scene.stencilCubeRotateSpeed
|
|
end
|
|
end
|
|
|
|
|
|
function scene.draw(pass)
|
|
function scene.draw(pass)
|
|
@@ -112,16 +117,18 @@ function scene.draw(pass)
|
|
pass:skybox(scene.skybox[3])
|
|
pass:skybox(scene.skybox[3])
|
|
|
|
|
|
-- Next, draw a floor
|
|
-- Next, draw a floor
|
|
- local floorRecenter = scene.floorSize/2 + 0.5
|
|
|
|
- for x=1,scene.floorSize do for y=1,scene.floorSize do
|
|
|
|
- if (x+y)%2==0 then
|
|
|
|
- pass:setColor(0.25,0.25,0.25)
|
|
|
|
- else
|
|
|
|
- pass:setColor(0.35,0.35,0.35)
|
|
|
|
|
|
+ local floorRecenter = scene.floorSize / 2 + 0.5
|
|
|
|
+ for x = 1, scene.floorSize do
|
|
|
|
+ for y = 1, scene.floorSize do
|
|
|
|
+ if (x + y) % 2 == 0 then
|
|
|
|
+ pass:setColor(0.25, 0.25, 0.25)
|
|
|
|
+ else
|
|
|
|
+ pass:setColor(0.35, 0.35, 0.35)
|
|
|
|
+ end
|
|
|
|
+ pass:plane(x - floorRecenter, 0, y - floorRecenter, 1, 1, -math.pi / 2, 1, 0, 0) -- Face up
|
|
end
|
|
end
|
|
- pass:plane(x-floorRecenter,0,y-floorRecenter, 1,1, -math.pi/2,1,0,0) -- Face up
|
|
|
|
- end end
|
|
|
|
- pass:setColor(1,1,1,1)
|
|
|
|
|
|
+ end
|
|
|
|
+ pass:setColor(1, 1, 1, 1)
|
|
|
|
|
|
-- Stencils here
|
|
-- Stencils here
|
|
-- Using stencils involves drawing twice, once with a stencil write set and once with a stencil test set.
|
|
-- Using stencils involves drawing twice, once with a stencil write set and once with a stencil test set.
|
|
@@ -137,23 +144,21 @@ function scene.draw(pass)
|
|
local center, stencilValue = unpack(cube)
|
|
local center, stencilValue = unpack(cube)
|
|
|
|
|
|
-- Draw to stencil (but only when we pass the depth test)
|
|
-- Draw to stencil (but only when we pass the depth test)
|
|
- pass:setStencilWrite({"keep", "keep", "replace"}, stencilValue)
|
|
|
|
- pass:cube(center*scene.stencilCubeSize, scene.stencilCubeSize)
|
|
|
|
-
|
|
|
|
|
|
+ pass:setStencilWrite({'keep', 'keep', 'replace'}, stencilValue)
|
|
|
|
+ pass:cube(center * scene.stencilCubeSize, scene.stencilCubeSize)
|
|
end
|
|
end
|
|
pass:pop()
|
|
pass:pop()
|
|
|
|
|
|
pass:setStencilWrite() -- Reset stencil write
|
|
pass:setStencilWrite() -- Reset stencil write
|
|
pass:setColorWrite(true) -- Reset color write
|
|
pass:setColorWrite(true) -- Reset color write
|
|
|
|
|
|
- -- Now that we've painted the stencil buffer, let's draw something with depth-- like a skybox
|
|
|
|
|
|
+ -- Now that we've painted the stencil buffer, let's draw something with depth, like a skybox
|
|
pass:setDepthTest() -- Turn off depth test because the skybox is "behind" the cubes
|
|
pass:setDepthTest() -- Turn off depth test because the skybox is "behind" the cubes
|
|
- for stencilValue=1,2 do
|
|
|
|
- pass:setStencilTest("equal", stencilValue) -- Commands after here will only draw on pixels where the stencil value is right
|
|
|
|
-
|
|
|
|
|
|
+ for stencilValue = 1, 2 do
|
|
|
|
+ pass:setStencilTest('equal', stencilValue) -- Commands after here will only draw on pixels where the stencil value is right
|
|
pass:skybox(scene.skybox[stencilValue])
|
|
pass:skybox(scene.skybox[stencilValue])
|
|
end
|
|
end
|
|
- pass:setDepthTest("gequal") -- Turn depth test back on
|
|
|
|
|
|
+ pass:setDepthTest('gequal') -- Turn depth test back on
|
|
|
|
|
|
-- Example 2: Using stencils to prevent collision
|
|
-- Example 2: Using stencils to prevent collision
|
|
|
|
|
|
@@ -161,11 +166,11 @@ function scene.draw(pass)
|
|
-- But we don't want any cubes to overlap each other. We want each cube to look like a "world of shadow".
|
|
-- But we don't want any cubes to overlap each other. We want each cube to look like a "world of shadow".
|
|
-- The cubes can darken the skybox and the 3x3 cube, but not any pixel where another cube has already drawn.
|
|
-- The cubes can darken the skybox and the 3x3 cube, but not any pixel where another cube has already drawn.
|
|
|
|
|
|
- pass:setStencilTest("notequal", 3) -- We will write the value "3", but refuse to write any pixel where a 3 is already present.
|
|
|
|
- pass:setStencilWrite("replace", 3) -- Note we haven't cleared the stencil buffer, so we can't reuse values 1 or 2.
|
|
|
|
- for _,cube in ipairs(scene.driftCubes) do
|
|
|
|
- pass:setColor(0.75,0.5,0.5,0.5)
|
|
|
|
- pass:cube(cube.at.x, cube.at.y, cube.at.z, scene.driftCubeSize, cube.rotate:unpack())
|
|
|
|
|
|
+ pass:setStencilTest('notequal', 3) -- We will write the value "3", but refuse to write any pixel where a 3 is already present.
|
|
|
|
+ pass:setStencilWrite('replace', 3) -- Note we haven't cleared the stencil buffer, so we can't reuse values 1 or 2.
|
|
|
|
+ for _, cube in ipairs(scene.driftCubes) do
|
|
|
|
+ pass:setColor(0.75, 0.5, 0.5, 0.5)
|
|
|
|
+ pass:cube(cube.at, scene.driftCubeSize, cube.rotate)
|
|
end
|
|
end
|
|
|
|
|
|
-- The stencil state will reset at the end of this lovr.draw, but let's clear it anyway.
|
|
-- The stencil state will reset at the end of this lovr.draw, but let's clear it anyway.
|
|
@@ -176,7 +181,7 @@ end
|
|
-- Handle lovr
|
|
-- Handle lovr
|
|
|
|
|
|
function lovr.load()
|
|
function lovr.load()
|
|
- lovr.graphics.setBackgroundColor(1,0,0) -- Red to show up clearly if something goes wrong
|
|
|
|
|
|
+ lovr.graphics.setBackgroundColor(1, 0, 0) -- Red to show up clearly if something goes wrong
|
|
scene.load()
|
|
scene.load()
|
|
end
|
|
end
|
|
|
|
|