main.lua 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. -- This demo renders a scene with several uses of stencil maps (skip down to comment "Stencils here")
  2. -- Note: Stencils need to be enabled in conf.lua
  3. -- t.graphics.stencil = true
  4. -- t.headset.stencil = true
  5. --
  6. -- Sample contributed by andi mcc
  7. local scene = {}
  8. function scene.load()
  9. -- So we can see the effects of the stencils, we want to put some things in our scene.
  10. -- A checkerboard floor:
  11. scene.floorSize = 6
  12. -- A series of sideways-drifting cubes (these will be stenciled)
  13. scene.driftCubeCount = 60
  14. scene.boundMin = lovr.math.newVec3(-10, -1, -10)
  15. scene.boundMax = lovr.math.newVec3( 10, 9, 10)
  16. scene.speed = 1
  17. scene.driftCubeSize = 0.6
  18. scene.driftCubes = {}
  19. for i=1,scene.driftCubeCount do
  20. scene.generateDriftCube(i, true)
  21. end
  22. -- A 3x3 cube made of two different stencil types
  23. scene.stencilCubeCenter = lovr.math.newVec3(0, 1.5, -0.5)
  24. scene.stencilCubeSize = 0.25
  25. scene.stencilCubeRotate = 0
  26. scene.stencilCubeRotateSpeed = 1
  27. scene.stencilCubes = {}
  28. for z=-1,1 do for y=-1,1 do for x=-1,1 do -- Iterate over every cube
  29. if not (x==0 and y==0 and z==0) then -- Except the center
  30. table.insert(scene.stencilCubes, {lovr.math.newVec3(x,y,z), math.random(1,2)}) -- Cube center and stencil type
  31. end
  32. end end end
  33. -- Three cubemap skyboxes, of different colors
  34. scene.skybox = {}
  35. local skyboxTextureSize = 32
  36. local bandSize=3
  37. for cube_index,colors in ipairs{
  38. {{1, 0.5, 1}, {1,1,1}}, -- Fuschia and white
  39. {{1, 1, 0.5}, {0,0,0}}, -- Yellow and black
  40. {{1,1,1}, {0.9,0.9,0.9}}, -- White and silver
  41. } do
  42. local layers = {}
  43. for layer=1,6 do -- 6 layers to a cubemap
  44. local data = lovr.data.newImage(skyboxTextureSize, skyboxTextureSize, "rgba8")
  45. for y=1,skyboxTextureSize do for x=1,skyboxTextureSize do
  46. local isBorder = x==1 or x==skyboxTextureSize or y==1 or y==skyboxTextureSize -- Solid color in corners
  47. local direction = cube_index==3 and -1 or 1 -- Reverse direction on third cubemap
  48. local whichColor = (isBorder or ((x+direction*y-2)%(bandSize*2)>=bandSize)) and 1 or 2 -- Diagonal stripes
  49. data:setPixel(x-1,y-1,unpack(colors[whichColor]))
  50. end end
  51. table.insert(layers, data)
  52. end
  53. table.insert(scene.skybox, lovr.graphics.newTexture(layers))
  54. end
  55. scene.sampler = lovr.graphics.newSampler({filter="nearest"})
  56. end
  57. local function randomQuaternion() -- Generate one random rotation
  58. -- Formula from http://planning.cs.uiuc.edu/node198.html
  59. local u,v,w = math.random(), math.random(), math.random()
  60. return lovr.math.newQuat(
  61. math.sqrt(1-u)*math.sin(2*v*math.pi),
  62. math.sqrt(1-u)*math.cos(2*v*math.pi),
  63. math.sqrt(u)*math.sin(2*w*math.pi),
  64. math.sqrt(u)*math.cos(2*w*math.pi),
  65. true -- Raw components
  66. )
  67. end
  68. function scene.generateDriftCube(i, randomX) -- Generate one cube with random position and color and a random rotational velocity
  69. local cube = {}
  70. cube.at = lovr.math.newVec3()
  71. if randomX then
  72. cube.at.x = scene.boundMin.x + math.random()*(scene.boundMax.x-scene.boundMin.x)
  73. else
  74. cube.at.x = scene.boundMin.x
  75. end
  76. cube.at.y = scene.boundMin.y + math.random()*(scene.boundMax.y-scene.boundMin.y)
  77. cube.at.z = scene.boundMin.z + math.random()*(scene.boundMax.z-scene.boundMin.z)
  78. cube.rotateBasis = randomQuaternion()
  79. cube.rotateTarget = lovr.math.newQuat(cube.rotateBasis:conjugate())
  80. cube.rotate = cube.rotateBasis
  81. scene.driftCubes[i] = cube
  82. end
  83. function scene.update(dt) -- On each frame, move each cube and spin it a little
  84. for i,cube in ipairs(scene.driftCubes) do
  85. cube.at.x = cube.at.x + scene.speed*dt
  86. if cube.at.x > scene.boundMax.x then -- If cube left the scene bounds respawn it
  87. scene.generateDriftCube(i)
  88. else
  89. local rotateAmount = (cube.at.x - scene.boundMin.x)/(scene.boundMax.x-scene.boundMin.x)
  90. cube.rotate = cube.rotateBasis:slerp( cube.rotateTarget, rotateAmount )
  91. end
  92. end
  93. -- Also rotate the center cube
  94. scene.stencilCubeRotate = scene.stencilCubeRotate + dt*scene.stencilCubeRotateSpeed
  95. end
  96. function scene.draw(pass)
  97. -- Drawing without culling can make stencils or transparency look weird. We'll be using both...
  98. pass:setCullMode('back')
  99. -- First, draw the skybox
  100. pass:setSampler(scene.sampler)
  101. pass:skybox(scene.skybox[3])
  102. -- Next, draw a floor
  103. local floorRecenter = scene.floorSize/2 + 0.5
  104. for x=1,scene.floorSize do for y=1,scene.floorSize do
  105. if (x+y)%2==0 then
  106. pass:setColor(0.25,0.25,0.25)
  107. else
  108. pass:setColor(0.35,0.35,0.35)
  109. end
  110. pass:plane(x-floorRecenter,0,y-floorRecenter, 1,1, -math.pi/2,1,0,0) -- Face up
  111. end end
  112. pass:setColor(1,1,1,1)
  113. -- Stencils here
  114. -- Using stencils involves drawing twice, once with a stencil write set and once with a stencil test set.
  115. -- Example 1: Using stencils to "paint" scenes
  116. -- Each sub-cube in our 3x3 cube will write a different value to the stencil buffer, 1 or 2.
  117. pass:setColorWrite() -- In the color spectrum, these cubes are completely invisible! They write only stencil and depth.
  118. pass:push() -- Position ourselves in the right place
  119. pass:translate(scene.stencilCubeCenter)
  120. pass:rotate(scene.stencilCubeRotate, 0,1,0)
  121. for _, cube in ipairs(scene.stencilCubes) do
  122. local center, stencilValue = unpack(cube)
  123. -- Draw to stencil (but only when we pass the depth test)
  124. pass:setStencilWrite({"keep", "keep", "replace"}, stencilValue)
  125. pass:cube(center*scene.stencilCubeSize, scene.stencilCubeSize)
  126. end
  127. pass:pop()
  128. pass:setStencilWrite() -- Reset stencil write
  129. pass:setColorWrite(true) -- Reset color write
  130. -- Now that we've painted the stencil buffer, let's draw something with depth-- like a skybox
  131. pass:setDepthTest() -- Turn off depth test because the skybox is "behind" the cubes
  132. for stencilValue=1,2 do
  133. pass:setStencilTest("equal", stencilValue) -- Commands after here will only draw on pixels where the stencil value is right
  134. pass:skybox(scene.skybox[stencilValue])
  135. end
  136. pass:setDepthTest("gequal") -- Turn depth test back on
  137. -- Example 2: Using stencils to prevent collision
  138. -- Here we will write AND test the stencil at the same time! In this step we want to draw a bunch of 50%-transparent cubes,
  139. -- But we don't want any cubes to overlap each other. We want each cube to look like a "world of shadow".
  140. -- The cubes can darken the skybox and the 3x3 cube, but not any pixel where another cube has already drawn.
  141. pass:setStencilTest("notequal", 3) -- We will write the value "3", but refuse to write any pixel where a 3 is already present.
  142. pass:setStencilWrite("replace", 3) -- Note we haven't cleared the stencil buffer, so we can't reuse values 1 or 2.
  143. for _,cube in ipairs(scene.driftCubes) do
  144. pass:setColor(0.75,0.5,0.5,0.5)
  145. pass:cube(cube.at.x, cube.at.y, cube.at.z, scene.driftCubeSize, cube.rotate:unpack())
  146. end
  147. -- The stencil state will reset at the end of this lovr.draw, but let's clear it anyway.
  148. pass:setStencilWrite()
  149. pass:setStencilTest()
  150. end
  151. -- Handle lovr
  152. function lovr.load()
  153. lovr.graphics.setBackgroundColor(1,0,0) -- Red to show up clearly if something goes wrong
  154. scene.load()
  155. end
  156. function lovr.update(dt)
  157. scene.update(dt)
  158. end
  159. function lovr.draw(pass)
  160. scene.draw(pass)
  161. end