diff --git a/xurfer/ecs.lua b/xurfer/ecs.lua index 131318d..364e754 100644 --- a/xurfer/ecs.lua +++ b/xurfer/ecs.lua @@ -30,4 +30,14 @@ ecs.clear = function() api.world.commit() end +ecs.extension = { + name = "ecs", + enabled = true, + index = 300, + init = function(api) ecs.init(api) end, + draw = function(pass) + ecs.update( api.world, pass, ecs.filterDraw ) + end +} + return ecs diff --git a/xurfer/ext/gazecontrol/main.lua b/xurfer/ext/gazecontrol/main.lua index 7902364..941cc0d 100644 --- a/xurfer/ext/gazecontrol/main.lua +++ b/xurfer/ext/gazecontrol/main.lua @@ -5,49 +5,82 @@ local gctl gctl = { - name = "gctl", - enabled = true, + name = "gazecontrol", + enabled = (lovr ~= nil), + index = api.ext.max * 0.9, -- important: render last! (renderOrder) texture = {}, material = nil, dtmax = 1, dt = 0, + visor = { + rotate = { x = 0, y = 0, z = 0}, + scaleTo = 0.1, + scale = 0.1, + scaleSpeed = 0.005, + walkingSpeed = 2, + texture = false, + textures = {} + }, init = function() --lovr.mouse.setRelativeMode(true) gctl.initVisor() gctl.extendECS() + api.player.matrix = lovr.math.newMat4() end, - load = function() - gctl.texture.idle = lovr.graphics.newTexture("ext/gazecontrol/reticle_idle.png") - end, - initVisor = function() - -- add visor object - gctl.visor = { - rotate = { x = 0, y = 0, z = 0}, - scale = 0.1, - texture = gctl.texture.idle - } + local visor = gctl.visor + visor.textures.idle = lovr.graphics.newTexture("ext/gazecontrol/reticle_idle.png") + visor.textures.hover = lovr.graphics.newTexture("ext/gazecontrol/reticle_hover.png") + visor.texture = visor.textures.idle + end, + + update = function(dt, input) + local visor = gctl.visor + if input.pointer.hover then + visor.texture = visor.textures.hover + if visor.scale == visor.scaleTo then + visor.scale = 0.05 + end + else + visor.texture = visor.textures.idle + visor.scale = visor.scaleTo + end + + if input.pointer.wheel then -- move forward/backward via scrollwheel + print( input.pointer.wheel.y) + local direction = vector(lovr.headset.getDirection('head')) + if not api.player.flying then direction = vector(direction.x, 0, direction.z) end + -- move forward/backward via scrollwheel + api.player.matrix:translate(direction * (input.pointer.wheel.y * visor.walkingSpeed) ) + end end, draw = function(pass,dt) -- *TODO* render this last (as it needs to be rendered after skybox & floor) - local posx, posy, posz = lovr.headset.getPosition('head') + local pos = vector( lovr.headset.getPosition('head') ) + vector( api.player.matrix:getPosition() ) local dirx, diry, dirz = lovr.headset.getDirection('head') local distance = 1 - local x = posx + distance * dirx - local y = posy + distance * diry - local z = posz + distance * dirz + local x = pos.x + distance * dirx + local y = pos.y + distance * diry + local z = pos.z + distance * dirz local angle, ax, ay, az = lovr.headset.getOrientation(device) - local rot = gctl.visor.rotate - local scale = gctl.visor.scale - pass:setMaterial(gctl.texture.idle) - pass:setShader() --'unlit') - pass:setBlendMode('alpha') - pass:setColor(1, 1, 1) + local visor = gctl.visor + local rot = visor.rotate + local scale = visor.scale + if scale < visor.scaleTo then + scale = scale + visor.scaleSpeed + visor.scale = scale + end + pass:setMaterial(visor.texture) + pass:setColor(1, 1, 1, 1) pass:setCullMode('back') + --pass:setFaceCull() + --pass:setDepthWrite(true) + pass:setBlendMode("alpha", "alphamultiply") + --pass:setColorWrite(true) pass:plane(x, y, z, scale, scale, angle, ax, ay, az) end, diff --git a/xurfer/ext/gazecontrol/reticle_hover.png b/xurfer/ext/gazecontrol/reticle_hover.png new file mode 100644 index 0000000..f1fb1b3 Binary files /dev/null and b/xurfer/ext/gazecontrol/reticle_hover.png differ diff --git a/xurfer/ext/gazecontrol/reticle_idle.png b/xurfer/ext/gazecontrol/reticle_idle.png index f1fb1b3..36e0040 100644 Binary files a/xurfer/ext/gazecontrol/reticle_idle.png and b/xurfer/ext/gazecontrol/reticle_idle.png differ diff --git a/xurfer/ext/skeleton/main.lua b/xurfer/ext/skeleton/main.lua index 4137ef2..f189739 100644 --- a/xurfer/ext/skeleton/main.lua +++ b/xurfer/ext/skeleton/main.lua @@ -9,11 +9,9 @@ return { --api.ext.skeleton.extendECS() end, - update = function(dt) end, - - load = function() end, - - draw = function(pass,dt) end, + update = function(dt,input) end, + load = function() end, + draw = function(pass,dt) end, -- ECS API docs: https://bakpakin.github.io/tiny-ecs/doc extendECS = function() diff --git a/xurfer/ext/startupscene/main.lua b/xurfer/ext/startupscene/main.lua index a4f3e76..1d53077 100644 --- a/xurfer/ext/startupscene/main.lua +++ b/xurfer/ext/startupscene/main.lua @@ -5,6 +5,7 @@ local shader return { name = "startupscene", enabled = (lovr ~= nil), + index = api.ext.max * 0.1, init = function() end, @@ -51,13 +52,15 @@ return { pass:setColor(1, 1, 1) pass:setMaterial(envTex) + pass:setDepthWrite(false) pass:draw(envTex, 0, 0, 0, 512, math.pi * 0.5, 1, 0, 0) -- floor - --pass:setMaterial() + pass:setMaterial() + pass:setDepthWrite(true) pass:setShader() - pass:setCullMode('front') - pass:setBlendMode('alpha') + --pass:setCullMode('front') + --pass:setBlendMode('alpha') pass:setMaterial() pass:setColor(0.8,0.8,0.8,1) pass:plane(0, 0, 0, 500, 500, math.pi * 0.5, 1, 0, 0, "fill", 5, 5) diff --git a/xurfer/main.lua b/xurfer/main.lua index 168d96c..659b026 100755 --- a/xurfer/main.lua +++ b/xurfer/main.lua @@ -31,7 +31,10 @@ api = { ecs = ecs, world = ecs.world(), protocol = {}, + player = { x = 0, y = 0, z = 0, ox = 0, oy = 1, oz = 0, oangle = 0, flying = true }, ext = { -- all extensions are loaded here from disk at runtime + ecs = ecs.extension, + max = 10000, -- max plugins (used for sorting) exec = function(...) util.exec(api.ext,...) end -- util function to call func on each extension }, i = 1 @@ -39,14 +42,11 @@ api = { api = util.merge( api, runtime.api ) -- overlay api onto lovr/love-compatible runtime api -require( runtime.path .. "/main") - util.loaddir( "ext", api, api.ext ) -util.loaddir( "media", api, api.media ) - -ecs.init(api) -api.ext.exec('init') +require( runtime.path .. "/main") +api.ext.exec('init', api) + -- load urls passed on the cli foreach( arg, function(k,uri) if uri:find(".") and k ~= 0 and k ~= -1 then diff --git a/xurfer/runtime/lovr/main.lua b/xurfer/runtime/lovr/main.lua index b2bf969..5e70354 100644 --- a/xurfer/runtime/lovr/main.lua +++ b/xurfer/runtime/lovr/main.lua @@ -28,9 +28,30 @@ local ecs = api.ecs ecs.filterDraw = ecs.requireAll('drawthread') ecs.filterUpdate = ecs.requireAll('updatethread') +-- sort drawcalls of extensions +api.ext.lovr = { + drawcalls = {}, + enabled = true, + name = "lovr", + index = api.ext.max-1, + init = function() + api.ext.lovr.sortDrawCalls() + end, + + sortDrawCalls = function() + local drawcalls = api.ext.lovr.drawcalls + foreach( api.ext, function(k,v) + if type(v) == 'table' and v.draw ~= nil then + table.insert(drawcalls, v) + end + end) + table.sort( drawcalls, function(a,b) return a.index < b.index end) + end +} + -- ecs systems -local interactionsUpdater +local interactions --- @type Texture local envTex @@ -40,8 +61,7 @@ local mainWindow function lovr.load() if launch.mode == "desktop" then - backend.mouse = require "lib.lovr-mouse" - + --backend.mouse = require "lib.lovr-mouse" lovr.system.setKeyRepeat(true) end @@ -50,7 +70,10 @@ function lovr.load() api.ext.exec("load") if iui.idiom == "vr" then - lovr.headset.setPassthrough("opaque") + local modes = lovr.headset.getPassthroughModes() + local mode = "opaque" + if modes.blend then mode = "blend" end + lovr.headset.setPassthrough( mode ) mainWindow = backend.worldWindow.new() api.mainWindow = mainWindow @@ -66,7 +89,7 @@ function lovr.update(dt) -- In desktop mode, we use IUI's standard window API to fill the screen. iui.beginWindow(lovr.system.getWindowDimensions()) - api.ext.exec("update", dt) + api.ext.exec("update", dt, interactions.event ) iui.endWindow() @@ -76,7 +99,7 @@ function lovr.update(dt) if mainWindow:beginFrame() then iui.beginWindow(mainWindow.w, mainWindow.h) - api.ext.exec("update", dt) + api.ext.exec("update", dt, interactions.event ) iui.endWindow() @@ -88,6 +111,10 @@ function lovr.update(dt) end function lovr.draw(pass) + --api.player.matrix:setPosition( api.player.x, api.player.y, api.player.z ) + --api.player.matrix.setOrientation( api.player.x, api.player.y, api.player.z ) + pass:transform(mat4(api.player.matrix):invert()) + if iui.idiom == "desktop" then backend.graphics.pass = pass @@ -97,27 +124,28 @@ function lovr.draw(pass) pass:setClear(0.2, 0.2, 0.2) end - api.ext.exec("draw",pass) - ecs.update( api.world, pass, ecs.filterDraw ) + -- call draw() function of extensions (sorted by .index) + foreach( api.ext.lovr.drawcalls, function(k,ext) ext.draw(pass) end) - -- Dot - if interactionsUpdater.selectedBox then - pass:setColor(0, 0, 1) - pass:sphere( interactionsUpdater.hitpoint, .01) - end - - -- Laser pointers - local hands = {"left","right"} - foreach( hands, function(k,side) - local hand = vec3(lovr.headset.getPosition('hand/' .. side .. '/point')) - local direction = quat(lovr.headset.getOrientation('hand/' .. side .. '/point')):direction() - pass:setColor(1, 1, 1) - pass:line(hand, interactionsUpdater.selectedBox and interactionsUpdater.hitpoint or (hand + direction * 50)) - end) - if api.mainWindow then - api.mainWindow:draw(pass) - end + -- -- Dot + --if interactions.selectedBox then + -- pass:setColor(0, 0, 1) + -- pass:sphere( interactions.hitpoint, .01) + --end + -- + ---- Laser pointers + --local hands = {"left","right"} + --foreach( hands, function(k,side) + -- local hand = vec3(lovr.headset.getPosition('hand/' .. side .. '/point')) + -- local direction = quat(lovr.headset.getOrientation('hand/' .. side .. '/point')):direction() + -- pass:setColor(1, 1, 1) + -- pass:line(hand, interactions.selectedBox and interactions.hitpoint or (hand + direction * 50)) + --end) + + --if api.mainWindow then + -- api.mainWindow:draw(pass) + --end return false end @@ -127,59 +155,62 @@ function lovr.recenter() end end -if launch.mode == "desktop" then - function lovr.mousemoved(x, y, dx, dy) - backend.mousemoved(x, y, dx, dy) - interactionsUpdater.mouse.moved = {x=x, y=y} - end +function lovr.mousemoved(x, y, dx, dy) + backend.mousemoved(x, y, dx, dy) + interactions.pointer.moved = {x=x, y=y} +end - function lovr.mousepressed(x, y, button) - backend.mousepressed(x, y, button) - interactionsUpdater.mouse.pressed = {x=x, y=y, button=button} - end +function lovr.mousepressed(x, y, button) + backend.mousepressed(x, y, button) + interactions.pointer.pressed = {x=x, y=y, button=button} +end - function lovr.mousereleased(x, y, button) - backend.mousereleased(x, y, button) - interactionsUpdater.mouse.released = {x=x, y=y, button=button} - print("mouserelease") - end +function lovr.mousereleased(x, y, button) + backend.mousereleased(x, y, button) + interactions.pointer.released = {x=x, y=y, button=button} +end - function lovr.wheelmoved(x, y) - backend.wheelmoved(x, y) - interactionsUpdater.mouse.wheel = {x=x, y=y} - end +function lovr.wheelmoved(x, y) + backend.wheelmoved(x, y) + interactions.pointer.wheel = {x=x, y=y} + print_r(interactions.pointer.wheel) +end - function lovr.keypressed(key, scancode, isRepeat) - backend.keypressed(key, scancode, isRepeat) - interactionsUpdater.key.pressed = {key=key,scancode=scancode,isRepeat=isRepeat} - end +function lovr.keypressed(key, scancode, isRepeat) + backend.keypressed(key, scancode, isRepeat) + interactions.key.pressed = {key=key,scancode=scancode,isRepeat=isRepeat} +end - function lovr.keyreleased(key, scancode) - backend.keyreleased(key, scancode) - interactionsUpdater.key.released = {key=key,scancode=scancode} - end +function lovr.keyreleased(key, scancode) + backend.keyreleased(key, scancode) + interactions.key.released = {key=key,scancode=scancode} +end - function lovr.textinput(text) - backend.textinput(text) - interactionsUpdater.input = text - end +function lovr.textinput(text) + backend.textinput(text) + interactions.input = text end function initInteractions(ecs) ecs.worldPhysics = lovr.physics.newWorld(0, 0, 0) - interactionsUpdater = ecs.processingSystem() - interactionsUpdater.updatethread = true - interactionsUpdater.filter = function(obj) return true end -- always run + interactions = ecs.processingSystem() + interactions.updatethread = true + interactions.filter = function(obj) return true end -- always run -- collider vars - interactionsUpdater.mouse = { released = false, pressed = false, moved = false, wheel = false} - interactionsUpdater.key = { released = false, pressed = false } - interactionsUpdater.input = "" - interactionsUpdater.hitpoint = lovr.math.newVec3() - interactionsUpdater.selectedBox = nil - interactionsUpdater.clear = function(o) foreach( o, function(k,v) o[k] = false end ) end + interactions.pointer = { released = false, pressed = false, moved = false, hover = false, wheel = false} + interactions.key = { released = false, pressed = false } + interactions.input = "" + interactions.hitpoint = vector() + interactions.selectedBox = nil + interactions.clearTable = function(o) foreach( o, function(k,v) o[k] = false end ) end + interactions.clear = function() + interactions.clearTable( interactions.pointer ) + interactions.clearTable( interactions.key ) + interactions.input = false + end - function interactionsUpdater:process(obj, dt) + function interactions:process(obj, dt) ecs.worldPhysics:update(dt) local castsrc = 'hand/left/point' @@ -189,30 +220,43 @@ function initInteractions(ecs) local direction = quat(lovr.headset.getOrientation(castsrc)):direction() if direction ~= nil then - local dx, dy, dz = direction:mul(50):unpack() + local dx, dy, dz = vector( direction * 50 ):unpack() local collider, shape, x, y, z = ecs.worldPhysics:raycast(ox, oy, oz, ox + dx, oy + dy, oz + dz) if collider then + interactions.pointer.hover = true + local node = collider:getUserData() + interactions.selectedBox = collider + interactions.hitpoint.x = x + interactions.hitpoint.y = y + interactions.hitpoint.z = z + for i, hand in ipairs(lovr.headset.getHands()) do if lovr.headset.isDown(hand, 'trigger') then - print("buttondown") + interactions.pointer.pressed = true end - if lovr.headset.wasReleased(hand, 'trigger') or interactionsUpdater['mouse']['released'] then - print("cast: mousedown!") - local node = collider:getUserData() - interactionsUpdater.selectedBox = collider - interactionsUpdater.hitpoint:set(x, y, z) - if node.onclick ~= nil then node.onclick(node,collider) end + if lovr.headset.wasReleased(hand, 'trigger') then + interactions.pointer.released = true end end + if interactions.pointer.released and node.onclick ~= nil then + node.onclick(node,collider) + end end end - -- reset stuff - interactionsUpdater.clear( interactionsUpdater.mouse ) - interactionsUpdater.clear( interactionsUpdater.key ) - interactionsUpdater.input = false + -- backup & reset stuff + interactions.event = { + input = util.clone(interactions.input), + pointer = util.clone(interactions.pointer), + key = util.clone(interactions.key), + hitpoint = { x = interactions.hitpoint.x, y = interactions.hitpoint.y, z = interactions.z }, + --head = { x = ox, y = oy, z = oz, direction = direction}, + collider = collider + } + -- clear interactions + interactions.clear() end - ecs.addSystem( api.world, interactionsUpdater ) + ecs.addSystem( api.world, interactions ) end function initCleanups(ecs) diff --git a/xurfer/util.lua b/xurfer/util.lua index ae8724e..c3ec80c 100644 --- a/xurfer/util.lua +++ b/xurfer/util.lua @@ -15,10 +15,26 @@ util.loaddir = function( path, api, target) if api.filesystem.isFile(ext) then print("[i] loading '" .. v .. "'") target[ v ] = api.filesystem.load( ext )(api) + if target[ v ].index == nil then target[v].index = api.ext.max / 2 end end end) end +util.clone = function(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[util.clone(orig_key)] = util.clone(orig_value) + end + setmetatable(copy, util.clone(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy +end + util.cmdExist = function(cmd) if api.runtime.os == 'unixy' then cmd = cmd .. " 1>/dev/null 2>/dev/null" end local stdout, retstr, retcode = os.execute(cmd) @@ -27,7 +43,7 @@ end util.exec = function(obj, fn,a,b,c,d,e,f) foreach( obj, - when('enabled',true, util.call(fn,a,b,c,d,e,f) ) + when('enabled', true, util.call(fn,a,b,c,d,e,f) ) ) end @@ -198,9 +214,9 @@ end -- LUA SYNTAX SUGAR (from here on ) -- -function foreach(table, f) - if table ~= nil then - for k, v in pairs(table) do f(k, v) end +function foreach(t, f, sort) + if t ~= nil then + for k, v in pairs(t) do f(k, v) end end end