Browse Source

Merge pull request #8 from mcclure/example-hud

Mirror HUD example
Bjorn 6 years ago
parent
commit
a88116d1fd
3 changed files with 294 additions and 0 deletions
  1. 139 0
      examples/Hud/main.lua
  2. 116 0
      examples/Hud/mouse.lua
  3. 39 0
      examples/Hud/shader.lua

+ 139 - 0
examples/Hud/main.lua

@@ -0,0 +1,139 @@
+-- "Second screen experience" demo
+-- Click grid on desktop screen to build a scene simultaneously visible in VR space
+--
+-- Sample contributed by andi mcc
+
+local shader = require 'shader'
+lovr.mouse = require 'mouse'    -- Note: Requires LuaJIT
+
+local mirror = lovr.mirror              -- Backup lovr.mirror before it is overwritten
+local font = lovr.graphics.newFont(36)  -- Font appropriate for screen-space usage
+font:setFlipEnabled(true)
+font:setPixelDensity(1)
+
+-- Simple 2D triangle mesh
+local triangle = lovr.graphics.newMesh(
+	{{ 'lovrPosition', 'float', 3 }, { 'lovrNormal', 'float', 3 }},
+	{{0,-1,0, 0,0,1}, {0.75,1,0, 0,0,1}, {-0.75,1,0, 0,0,1}},
+	'triangles', 'static'
+	)
+
+-- Constants
+local pixwidth = lovr.graphics.getWidth()   -- Window pixel width and height
+local pixheight = lovr.graphics.getHeight()
+local aspect = pixwidth/pixheight           -- Window aspect ratio
+local height = 2                            -- Window width and height in screen coordinates
+local width = aspect*2                      -- ( We will pick the coordinate system [[-1,1],[-aspect,aspect]] )
+local topmargin = 0.2                       -- Space between top of screen and top of grid
+local cells = 7                             -- Number of cells in grid (per side)
+local towerscalexz = 2                      -- How wide is one block in 3D space?
+local towerscaley = 3                       -- How tall (maximum) is one block in 3D space?
+
+-- Derived constants
+local gridheight = (height-topmargin*2)             -- Height of grid
+local gridspan = gridheight/2                       -- Half height of grid
+local cellheight = gridheight/cells                 -- Height of one grid cell
+local cellspan = cellheight/2                       -- Half height of one grid cell
+local bannedcell = math.ceil(cells/2)               -- Do not allow clicks at this x,y coordinate
+local fontscale = height/lovr.graphics.getHeight()  -- Scale argument to screen-space print() functions
+
+-- Screen-space coordinate system
+local matrix = lovr.math.mat4():orthographic(-aspect, aspect, -1, 1, -64, 64)
+
+-- State: We will store the blocks to draw as a 2D array of heights (nil for no block)
+local grid = {}
+for x=1,cells do grid[x] = {} end
+
+function lovr.load()
+	lovr.handlers['mousepressed'] = function(x,y)
+		local inx = x * width / pixwidth - width/2    -- Convert pixel x,y to our coordinate system
+		local iny = y * height / pixheight - height/2
+		local gridorigin = -gridspan - cellheight     -- Upper left of grid ()
+		local gx = (inx - gridorigin) / cellheight    -- Convert coordinate system to grid cells
+		local gy = (iny - gridorigin) / cellheight
+		local fx = math.floor(gx)
+		local fy = math.floor(gy)
+		if fx >= 1 and fy >= 1 and fx <= cells and fy <= cells   -- If the click was within the grid
+		   and not (fx == bannedcell and fy == bannedcell) then  -- and was not the banned center cell
+			if grid[fx][fy] then
+				grid[fx][fy] = nil                -- toggle off
+			else
+				grid[fx][fy] = lovr.math.random() -- toggle on (random height)
+			end
+		end
+	end
+end
+
+function drawGrid()
+	-- Draw cell backgrounds (where present)
+	for _x=1,cells do for _y=1,cells do
+		local gray = grid[_x][_y]
+		if gray then
+			local x = -gridspan + _x * cellheight - cellspan -- Center of cell
+			local y = -gridspan + _y * cellheight - cellspan
+
+			lovr.graphics.setColor(gray,gray,gray,1)
+			lovr.graphics.plane('fill', x, y, 0, cellheight, cellheight)
+		end
+	end end
+
+	-- Draw grid lines
+	lovr.graphics.setColor(1,1,1,1)
+	for c=0,cells do
+		local x = -gridspan + c * cellheight
+		local y = -gridspan + c * cellheight
+		lovr.graphics.line(-gridspan, y, 0, gridspan, y, 0)
+		lovr.graphics.line(x, -gridspan, 0, x, gridspan, 0)
+	end
+
+	-- Draw a red triangle indicating the position and orientation of the headset player
+	lovr.graphics.push()
+	local x, y, z, angle, ax, ay, az = lovr.headset.getPose()
+	-- Flatten the 3-space current rotation of the headset into just its xz axis
+	-- Equation from: http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToEuler/index.htm
+	local s = math.sin(angle);
+	local c = math.cos(angle);
+	local t = 1-c;
+	local xzangle = math.atan2(ay*s - ax*az*t , 1 - (ay*ay + az*az) * t);
+	lovr.graphics.setColor(1,0,0,1)
+	lovr.graphics.translate(x / towerscalexz, z / towerscalexz, 0)
+	lovr.graphics.scale(cellheight*0.5*0.75)
+	lovr.graphics.rotate(-xzangle, 0, 0, 1)
+	triangle:draw()
+	lovr.graphics.pop()
+end
+
+-- Draw HUD overlay
+function lovr.mirror()
+	mirror()
+	-- lovr.graphics.clear() -- Uncomment to hide headset view in background of window
+	lovr.graphics.setShader(nil)
+	lovr.graphics.setDepthTest(nil)
+	lovr.graphics.origin()
+	lovr.graphics.setProjection(matrix) -- Switch to screen space coordinates
+	drawGrid()
+
+	-- Draw instructions
+	lovr.graphics.setColor(1,1,1,1)
+	lovr.graphics.setFont(font)
+	lovr.graphics.print("Instructions: Click the grid to create or remove blocks.", 0, (gridheight+cellheight)/2, 0, fontscale)
+end
+
+-- Draw one block
+function floorbox(_x,_y,gray)
+	local x = -gridspan + _x * cellheight - cellspan
+	local z = -gridspan + _y * cellheight - cellspan
+	local height = gray * towerscaley
+	lovr.graphics.box('fill', x*towerscalexz, height/2, z*towerscalexz, cellheight*towerscalexz, height, cellheight*towerscalexz)
+end
+
+-- Draw 3D scene
+function lovr.draw()
+	lovr.graphics.setDepthTest('lequal', true) -- mirror() will have disabled this
+	lovr.graphics.setShader(shader)
+	lovr.graphics.setColor(0,1,1)
+	for x=1,cells do for y=1,cells do
+		local gray = grid[x][y]
+		if gray then floorbox(x,y,gray) end
+	end end
+end

+ 116 - 0
examples/Hud/mouse.lua

@@ -0,0 +1,116 @@
+-- Source: https://github.com/bjornbytes/lovr-mouse/
+-- Source: c2f704db2463e05c453580b80b26200d5dd508a9
+
+local ffi = require 'ffi'
+local C = ffi.os == 'Windows' and ffi.load('glfw3') or ffi.C
+
+ffi.cdef [[
+  typedef enum {
+    GLFW_CURSOR = 0x00033001,
+    GLFW_CURSOR_NORMAL = 0x00034001,
+    GLFW_CURSOR_HIDDEN = 0x00034002,
+    GLFW_CURSOR_DISABLED = 0x00034003
+  } Constants;
+
+  typedef struct GLFWwindow GLFWwindow;
+  typedef void(*GLFWmousebuttonfun)(GLFWwindow*, int, int, int);
+  typedef void(*GLFWcursorposfun)(GLFWwindow*, double, double);
+  typedef void(*GLFWscrollfun)(GLFWwindow*, double, double);
+
+  GLFWwindow* glfwGetCurrentContext(void);
+  void glfwGetInputMode(GLFWwindow* window, int mode);
+  void glfwSetInputMode(GLFWwindow* window, int mode, int value);
+  void glfwGetCursorPos(GLFWwindow* window, double* x, double* y);
+  void glfwSetCursorPos(GLFWwindow* window, double x, double y);
+  int glfwGetMouseButton(GLFWwindow* window, int button);
+  void glfwGetWindowSize(GLFWwindow* window, int* width, int* height);
+  GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback);
+  GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback);
+  GLFWcursorposfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback);
+]]
+
+local window = C.glfwGetCurrentContext()
+
+local mouse = {}
+
+-- Lovr uses framebuffer scale for everything, but glfw uses window scale for events.
+-- It is necessary to convert between the two at all boundaries.
+function mouse.getScale()
+  local x, _ = ffi.new('int[1]'), ffi.new('int[1]')
+  C.glfwGetWindowSize(window, x, _)
+  return lovr.graphics.getWidth()/x[0]
+end
+
+function mouse.getX()
+  local x = ffi.new('double[1]')
+  C.glfwGetCursorPos(window, x, nil)
+  return x[0] * mouse.getScale()
+end
+
+function mouse.getY()
+  local y = ffi.new('double[1]')
+  C.glfwGetCursorPos(window, nil, y)
+  return y[0] * mouse.getScale()
+end
+
+function mouse.getPosition()
+  local x, y = ffi.new('double[1]'), ffi.new('double[1]')
+  local scale = mouse.getScale()
+  C.glfwGetCursorPos(window, x, y)
+  return x[0] * scale, y[0] * scale
+end
+
+function mouse.setX(x)
+  local y = mouse.getY()
+  local scale = mouse.getScale()
+  C.glfwSetCursorPos(window, x/scale, y/scale)
+end
+
+function mouse.setY(y)
+  local x = mouse.getX()
+  C.glfwSetCursorPos(window, x/scale, y/scale)
+end
+
+function mouse.setPosition(x, y)
+  C.glfwSetCursorPos(window, x/scale, y/scale)
+end
+
+function mouse.isDown(button, ...)
+  if not button then return false end
+  return C.glfwGetMouseButton(window, button - 1) > 0 or mouse.isDown(...)
+end
+
+function mouse.getRelativeMode()
+  return C.glfwGetInputMode(window, C.GLFW_CURSOR) == C.GLFW_CURSOR_DISABLED
+end
+
+function mouse.setRelativeMode(enable)
+  C.glfwSetInputMode(window, C.GLFW_CURSOR, enable and C.GLFW_CURSOR_DISABLED or C.GLFW_CURSOR_NORMAL)
+end
+
+C.glfwSetMouseButtonCallback(window, function(target, button, action, mods)
+  if target == window then
+    local x, y = mouse.getPosition()
+    lovr.event.push(action > 0 and 'mousepressed' or 'mousereleased', x, y, button + 1, false)
+  end
+end)
+
+local px, py = mouse.getPosition()
+C.glfwSetCursorPosCallback(window, function(target, x, y)
+  if target == window then
+    local scale = mouse.getScale()
+    x = x * scale
+    y = y * scale
+    lovr.event.push('mousemoved', x, y, x - px, y - py, false)
+    px, py = x, y
+  end
+end)
+
+C.glfwSetScrollCallback(window, function(target, x, y)
+  if target == window then
+    local scale = mouse.getScale()
+    lovr.event.push('wheelmoved', x*scale, y*scale)
+  end
+end)
+
+return mouse

+ 39 - 0
examples/Hud/shader.lua

@@ -0,0 +1,39 @@
+return lovr.graphics.newShader([[
+out vec3 lightDirection;
+out vec3 normalDirection;
+
+vec3 lightPosition = vec3(0, 10, 3);
+
+vec4 position(mat4 projection, mat4 transform, vec4 vertex) {
+  vec4 vVertex = transform * vec4(lovrPosition, 1.);
+  vec4 vLight = lovrView * vec4(lightPosition, 1.);
+
+  lightDirection = normalize(vec3(vLight - vVertex));
+  normalDirection = normalize(lovrNormalMatrix * lovrNormal);
+
+  return projection * transform * vertex;
+}
+]], [[
+in vec3 lightDirection;
+in vec3 normalDirection;
+
+vec3 cAmbient = vec3(.25);
+vec3 cDiffuse = vec3(.75);
+vec3 cSpecular = vec3(.35);
+
+vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) {
+  float diffuse = max(dot(normalDirection, lightDirection), 0.);
+  float specular = 0.;
+
+  if (diffuse > 0.) {
+    vec3 r = reflect(lightDirection, normalDirection);
+    vec3 viewDirection = normalize(-vec3(gl_FragCoord));
+
+    float specularAngle = max(dot(r, viewDirection), 0.);
+    specular = pow(specularAngle, 5.);
+  }
+
+  vec3 cFinal = pow(clamp(vec3(diffuse) * cDiffuse + vec3(specular) * cSpecular, cAmbient, vec3(1.)), vec3(.4545));
+  return vec4(cFinal, 1.) * graphicsColor * texture(image, uv);
+}
+]])