From f4b633d56099c435cb59b68cc721da1da673c7da Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Fri, 28 Aug 2026 11:26:17 +0200 Subject: [PATCH] luax wip --- README.md | 4 ++ nix/luax-release.sh | 19 ++++++++ xurfer.sh | 7 ++- xurfer/conf.lua | 1 + xurfer/ecs.lua | 1 + xurfer/events.lua | 10 +++++ xurfer/ext/3DFile/main.lua | 1 + xurfer/ext/AudioFile/main.lua | 1 + xurfer/ext/URI/main.lua | 1 + xurfer/ext/button/main.lua | 63 ++++++++++++++++++++++++++ xurfer/ext/gazecontrol/main.lua | 19 +------- xurfer/ext/http-lovr/main.lua | 1 + xurfer/ext/kitchensink/main.lua | 2 +- xurfer/ext/skeleton/main.lua | 1 + xurfer/ext/startupscene/main.lua | 1 + xurfer/ext/tween/main.lua | 1 + xurfer/ext/tween/tween.lua | 1 + xurfer/ext/unixy/main.lua | 1 + xurfer/ext/xrfragments/level2.lua | 1 + xurfer/ext/xrfragments/level4.lua | 1 + xurfer/ext/xrfragments/lovr-xrf.lua | 1 + xurfer/ext/xrfragments/main.lua | 3 +- xurfer/init.lua | 1 + xurfer/lib/url.lua | 16 +++++-- xurfer/lib/xmlSimple.lua | 2 +- xurfer/main.lua | 7 +++ xurfer/runtime/lovr/main.lua | 70 ++++++++++++++++------------- xurfer/test/main.lua | 1 + 28 files changed, 180 insertions(+), 58 deletions(-) create mode 100755 nix/luax-release.sh create mode 100644 xurfer/events.lua create mode 100644 xurfer/ext/button/main.lua diff --git a/README.md b/README.md index 1a7c243..573ad50 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,10 @@ $ ./lovr xurfer ... ``` +## Longterm goal + +* Android launcher for OpenXR & WebXR apps + ## Security Instead of the traditional sandbox-in-a-browser-model (Chromium/Firefox e.g.), we suggest running XURFER via [bubblewrap](https://github.com/meta-quest/bubblewrap) diff --git a/nix/luax-release.sh b/nix/luax-release.sh new file mode 100755 index 0000000..be17300 --- /dev/null +++ b/nix/luax-release.sh @@ -0,0 +1,19 @@ +#!/bin/sh +export PATH=$PATH:. +which luax || { + wget "https://cdelord.fr/pub/luax/10.10.0/luax-linux-x86_64-musl" + ln -s luax-linux-x86_64-musl luax + chmod +x luax +} + +cd xurfer +files="$(find * -type f | grep lua | grep -vE '(runtime/lov*)')" +echo $files + +luax compile -t list | tail -n+3 | awk '$1 ~ /[a-z]-[a-z]/ {print $1}' | \ + while read platform; do + #luax compile -t $platform -o ./../xurfer-$platform ${files} + # TODO: luaX needs unique modulenames : main.lua clashes in the binary + luax compile -t $platform -o ./../xurfer-$platform conf.lua ecs.lua events.lua ext/kitchensink/main.lua ext/xrfragments/main.lua #ext/xrfragments/level2.lua ext/xrfragments/level4.lua ext/xrfragments/lovr-xrf.lua ext/skeleton/main.lua ext/unixy/main.lua ext/gazecontrol/main.lua ext/janusxr/main.lua ext/http-lovr/main.lua ext/startupscene/main.lua ext/3DFile/main.lua ext/tween/main.lua ext/tween/tween.lua ext/AudioFile/main.lua ext/button/main.lua ext/URI/main.lua init.lua lib/lust.lua lib/tiny-ecs.lua lib/xmlSimple.lua lib/json.lua lib/url.lua main.lua runtime/lua/conf.lua runtime/lua/main.lua runtime/lua/api.lua runtime/detect.lua test/main.lua util.lua + exit + done diff --git a/xurfer.sh b/xurfer.sh index bdb2994..f58aafb 100755 --- a/xurfer.sh +++ b/xurfer.sh @@ -1,8 +1,13 @@ #!/bin/sh export DEBUG=1 cd xurfer -runtime="/home/leon/apps/lovr-v0.18.0-x86_64.AppImage" +#./main.lua +#runtime="/home/leon/apps/lovr-v0.18.0-x86_64.AppImage" +runtime="/home/leon/apps/lovr-v0.18.1-x86_64.AppImage" #appimage-run $runtime . test.glb appimage-run $runtime . https://snips.sh/f/_U5-XctEVE?r=1 #appimage-run $runtime . http://localhost:8080/simple-a.glb #appimage-run $runtime . http://localhost:8080/level3-4-playaudio.glb +#appimage-run $runtime . http://localhost:8080/simple-b.glb +#appimage-run $runtime . test.localhost.jml0 +#appimage-run $runtime . http://localhost:8080/simple-b-wireframe.glb diff --git a/xurfer/conf.lua b/xurfer/conf.lua index 2333ee5..981ecbe 100644 --- a/xurfer/conf.lua +++ b/xurfer/conf.lua @@ -1,4 +1,5 @@ -- default argument +--@LIB if arg[1] == nil then arg[1] = "https://snips.sh/f/_U5-XctEVE?r=1" end diff --git a/xurfer/ecs.lua b/xurfer/ecs.lua index 364e754..5d6a857 100644 --- a/xurfer/ecs.lua +++ b/xurfer/ecs.lua @@ -1,3 +1,4 @@ +--@LIB local ecs = require("lib/tiny-ecs") ecs.init = function(api) diff --git a/xurfer/events.lua b/xurfer/events.lua new file mode 100644 index 0000000..d6d57ba --- /dev/null +++ b/xurfer/events.lua @@ -0,0 +1,10 @@ +--@LIB +return { + input = { + pointer = { released = false, pressed = false, moved = false, hover = false, wheel = false}, + key = { released = false, pressed = false }, + input = false, -- string if true, + collider = false, -- lovr collider + hitpoint = false, -- {x=0, y=0, z=0} + } +} diff --git a/xurfer/ext/3DFile/main.lua b/xurfer/ext/3DFile/main.lua index 8bd9de1..562ef8b 100644 --- a/xurfer/ext/3DFile/main.lua +++ b/xurfer/ext/3DFile/main.lua @@ -1,3 +1,4 @@ +--@LIB local api = ... local ecs = api.ecs diff --git a/xurfer/ext/AudioFile/main.lua b/xurfer/ext/AudioFile/main.lua index 56eccc0..7b58bde 100644 --- a/xurfer/ext/AudioFile/main.lua +++ b/xurfer/ext/AudioFile/main.lua @@ -1,3 +1,4 @@ +--@LIB local api = ... local ecs = api.ecs diff --git a/xurfer/ext/URI/main.lua b/xurfer/ext/URI/main.lua index b97fbab..89b3005 100644 --- a/xurfer/ext/URI/main.lua +++ b/xurfer/ext/URI/main.lua @@ -1,3 +1,4 @@ +--@LIB local api = ... local ecs = api.ecs diff --git a/xurfer/ext/button/main.lua b/xurfer/ext/button/main.lua new file mode 100644 index 0000000..b29191e --- /dev/null +++ b/xurfer/ext/button/main.lua @@ -0,0 +1,63 @@ +--@LIB +local api = ... +local ecs = api.ecs + +-- this is an input extension which adds data the input-event +-- which is fed to update-calls of extensions and ECS + +local button +button = { + name = "button", + enabled = true, + settings = { -- keep this settings serializable (no functions e.g.) + use lovr key-names + + -- (SHIFT) TAB/ENTER as described in the Accessibility guidelines + -- of XR (URI) fragments: https://xrfragment.org/#accessibility + { event={name="next"}, key="tab" }, + { event={name="previous"}, key="tab", shift=true}, + { event={name="select"}, key="return", pointer={released=true} }, + { event={name="select"}, key="space", pointer={released=true} }, + + }, + state = { shift = false }, + + init = function() + if not api.settings.button then + api.settings.button = button.settings + end + end, + + isShift = function(key) return key == "rshift" or key == "lshift" end, + + matchKey = function(mapping, event) + return mapping.key ~= nil and + mapping.shift == (button.state.shift or nil) and + mapping.key == event.key + end, + + input = function(dt,input) + -- sense shift state + if input.key.pressed and button.isShift(input.key.pressed.key) then button.state.shift = true end + if input.key.released and button.isShift(input.key.released.key) then button.state.shift = false end + + local mappings = api.settings.button + -- process keymapping + if input.key.released then + foreach(mappings, function(k,mapping) + if button.matchKey( mapping, input.key.released ) then + input[ mapping.event.name ] = mapping.event -- add to input event (so update-calls can see it) + end + end) + end + -- process keymapping + if input.pointer.released then + foreach(mappings, function(k,mapping) + if mapping.pointer and mapping.pointer.released then + input[ mapping.event.name ] = mapping.event -- add to input event (so update-calls can see it) + end + end) + end + end, +} + +return button diff --git a/xurfer/ext/gazecontrol/main.lua b/xurfer/ext/gazecontrol/main.lua index 941cc0d..e2e0d9c 100644 --- a/xurfer/ext/gazecontrol/main.lua +++ b/xurfer/ext/gazecontrol/main.lua @@ -1,3 +1,4 @@ +--@LIB local api = ... local ecs = api.ecs @@ -25,7 +26,6 @@ gctl = { init = function() --lovr.mouse.setRelativeMode(true) gctl.initVisor() - gctl.extendECS() api.player.matrix = lovr.math.newMat4() end, @@ -84,23 +84,6 @@ gctl = { pass:plane(x, y, z, scale, scale, angle, ax, ay, az) end, - -- ECS API docs: https://bakpakin.github.io/tiny-ecs/doc - extendECS = function() - - local visorSystem = ecs.processingSystem({ - updatethread = true, - filter = ecs.requireAll('mycomponent'), - onAdd = function(self,obj) print_r(obj) end, - onRemove = function(self,obj) end, - process = function(self,obj) end, - }) - ecs.addSystem( api.world, visorSystem ) - - ecs.add( api.world, { visor = gctl.visor } ) - --gctl.visor.commit() -- notify other systems to update entitycache - - end - } return gctl diff --git a/xurfer/ext/http-lovr/main.lua b/xurfer/ext/http-lovr/main.lua index 8dbdfcd..aae13c4 100644 --- a/xurfer/ext/http-lovr/main.lua +++ b/xurfer/ext/http-lovr/main.lua @@ -1,3 +1,4 @@ +--@LIB -- this extension will make network requests run async in LoVR runtime local api = ... diff --git a/xurfer/ext/kitchensink/main.lua b/xurfer/ext/kitchensink/main.lua index 41d6a20..dc800d2 100644 --- a/xurfer/ext/kitchensink/main.lua +++ b/xurfer/ext/kitchensink/main.lua @@ -1,5 +1,5 @@ +--@LIB local api = ... - local sample return { diff --git a/xurfer/ext/skeleton/main.lua b/xurfer/ext/skeleton/main.lua index f189739..a11ff5b 100644 --- a/xurfer/ext/skeleton/main.lua +++ b/xurfer/ext/skeleton/main.lua @@ -1,3 +1,4 @@ +--@LIB local api = ... local ecs = api.ecs diff --git a/xurfer/ext/startupscene/main.lua b/xurfer/ext/startupscene/main.lua index 1d53077..ce9f5d4 100644 --- a/xurfer/ext/startupscene/main.lua +++ b/xurfer/ext/startupscene/main.lua @@ -1,3 +1,4 @@ +--@LIB local api = ... local envTex local shader diff --git a/xurfer/ext/tween/main.lua b/xurfer/ext/tween/main.lua index decafdb..942e6f1 100644 --- a/xurfer/ext/tween/main.lua +++ b/xurfer/ext/tween/main.lua @@ -1,3 +1,4 @@ +--@LIB local api = ... local tween = require "ext/tween/tween" -- https://github.com/kikito/tween.lua diff --git a/xurfer/ext/tween/tween.lua b/xurfer/ext/tween/tween.lua index 4f2c681..17a992e 100644 --- a/xurfer/ext/tween/tween.lua +++ b/xurfer/ext/tween/tween.lua @@ -1,3 +1,4 @@ +--@LIB local tween = { _VERSION = 'tween 2.1.1', _DESCRIPTION = 'tweening for lua', diff --git a/xurfer/ext/unixy/main.lua b/xurfer/ext/unixy/main.lua index 08e8bb1..73fa9f3 100644 --- a/xurfer/ext/unixy/main.lua +++ b/xurfer/ext/unixy/main.lua @@ -1,3 +1,4 @@ +--@LIB -- The unixy extension allows proxying api-functions through cli apps. -- Why? -- Sometimes the host OS already has cli-apps which can make up for missing lua functionality. diff --git a/xurfer/ext/xrfragments/level2.lua b/xurfer/ext/xrfragments/level2.lua index c0f50e5..e10d0e2 100644 --- a/xurfer/ext/xrfragments/level2.lua +++ b/xurfer/ext/xrfragments/level2.lua @@ -1,3 +1,4 @@ +--@LIB local api = ... local xrf = require("ext/xrfragments/lovr-xrf") local url = require("url") diff --git a/xurfer/ext/xrfragments/level4.lua b/xurfer/ext/xrfragments/level4.lua index 2a5057a..861f0b2 100644 --- a/xurfer/ext/xrfragments/level4.lua +++ b/xurfer/ext/xrfragments/level4.lua @@ -1,3 +1,4 @@ +--@LIB local xrf = require("ext/xrfragments/lovr-xrf") local url = require("url") diff --git a/xurfer/ext/xrfragments/lovr-xrf.lua b/xurfer/ext/xrfragments/lovr-xrf.lua index c004a09..55052d3 100644 --- a/xurfer/ext/xrfragments/lovr-xrf.lua +++ b/xurfer/ext/xrfragments/lovr-xrf.lua @@ -1,3 +1,4 @@ +--@LIB -- Copyright 2026 Leon van Kammen / coderofsalvation. All rights reserved. -- -- Redistribution and use in source and binary forms, with or without modification, are diff --git a/xurfer/ext/xrfragments/main.lua b/xurfer/ext/xrfragments/main.lua index 1db9a4c..afbca7e 100644 --- a/xurfer/ext/xrfragments/main.lua +++ b/xurfer/ext/xrfragments/main.lua @@ -1,3 +1,4 @@ +--@LIB=xrfragment/main.lua local api = ... local url = require('url') local ecs = api.ecs @@ -19,7 +20,7 @@ xrfimpl = { local href = obj.node.extras.href trace("\n[xrf] href was clicked: " .. href) api.ext.exec("onClick", obj, collider) - local ok = xrf.level2.load( href, obj, api.ext.URI.current, xrfimpl.onLoad) + local ok = xrf.level2.load( href, obj, referer, xrfimpl.onLoad) or xrf.level4.import( href, obj, referer, xrfimpl.onImport) diff --git a/xurfer/init.lua b/xurfer/init.lua index 871d653..4fce523 100644 --- a/xurfer/init.lua +++ b/xurfer/init.lua @@ -1 +1,2 @@ +--@LIB print("hello!") diff --git a/xurfer/lib/url.lua b/xurfer/lib/url.lua index 4a9ecc1..56103d4 100644 --- a/xurfer/lib/url.lua +++ b/xurfer/lib/url.lua @@ -85,6 +85,7 @@ function url.parse(url_string) URN = "", isLocal = false } + if not url_string then return URI end local remaining = url_string local protocol = string.match(remaining, "^(%w+://)") -- protocol if protocol then @@ -152,11 +153,18 @@ url.getAbsolute = function(href,referer,keephash) if referer ~= nil and URL.protocol == 'file' and URLref.protocol ~= 'file' then URL.protocol = URLref.protocol or '' URL.domain = URLref.domain or '' - URL.string = URL.protocol .. '://' .. URL.domain .. '/' .. URL.path .. URL.hash - if keephash then - URL.URN = URL.string + URL.string = URL.protocol .. '://' .. URL.domain .. '/' + if href[1] == "/" then + URL.string = URL.string .. URL.path else - URL.URN = URL.string:gsub("#.*", "") + URL.string = URL.string .. + URLref.path:sub( 0, URLref.path:find( URLref.file,1,true)-1 ) .. + URL.file + end + URL.string = URL.string .. URL.hash + URL = url.parse(URL.string) -- reparse + if keephash then + URL.hash = URLref.hash end end URL.referer = referer diff --git a/xurfer/lib/xmlSimple.lua b/xurfer/lib/xmlSimple.lua index 6780e25..c991492 100644 --- a/xurfer/lib/xmlSimple.lua +++ b/xurfer/lib/xmlSimple.lua @@ -1,6 +1,6 @@ --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- --- +--@LIB -- xml.lua - XML parser for use with the Corona SDK. -- https://github.com/Cluain/Lua-Simple-XML-Parser -- diff --git a/xurfer/main.lua b/xurfer/main.lua index 659b026..089af65 100755 --- a/xurfer/main.lua +++ b/xurfer/main.lua @@ -1,4 +1,7 @@ #!/usr/bin/env lua +--please do not remove the following luaX tag: +--@MAIN +-- -- -- GLB URLS -- ./lovr xurfer https://coderofsalvation.codeberg.page/xrfragment-haxe/example/assets/example.glb?bar=1&f=2#foo' -- ./lovr xurfer https://codeberg.org/coderofsalvation/xrfragment/raw/branch/main/assets/template/website/website.glb' @@ -37,6 +40,10 @@ api = { max = 10000, -- max plugins (used for sorting) exec = function(...) util.exec(api.ext,...) end -- util function to call func on each extension }, + settings = { -- settings to store to ~/.config/xurfer/config.lua e.g. + version="0.0.1" + }, + events = require('events'), i = 1 } diff --git a/xurfer/runtime/lovr/main.lua b/xurfer/runtime/lovr/main.lua index 5e70354..f580cf9 100644 --- a/xurfer/runtime/lovr/main.lua +++ b/xurfer/runtime/lovr/main.lua @@ -81,6 +81,7 @@ function lovr.load() end function lovr.update(dt) + local event = interactions.collect() ecs.update( api.world, dt, ecs.filterUpdate ) iui.beginFrame(dt) @@ -89,7 +90,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, interactions.event ) + api.ext.exec("update", dt, event ) iui.endWindow() @@ -99,7 +100,7 @@ function lovr.update(dt) if mainWindow:beginFrame() then iui.beginWindow(mainWindow.w, mainWindow.h) - api.ext.exec("update", dt, interactions.event ) + api.ext.exec("update", dt, event ) iui.endWindow() @@ -108,6 +109,7 @@ function lovr.update(dt) end iui.endFrame() + interactions.clear() end function lovr.draw(pass) @@ -129,7 +131,7 @@ function lovr.draw(pass) -- -- Dot - --if interactions.selectedBox then + --if interactions.hitpoint then -- pass:setColor(0, 0, 1) -- pass:sphere( interactions.hitpoint, .01) --end @@ -173,7 +175,6 @@ 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) @@ -197,12 +198,13 @@ function initInteractions(ecs) interactions = ecs.processingSystem() interactions.updatethread = true interactions.filter = function(obj) return true end -- always run + interactions.pointer = util.clone( api.events.input.pointer ) + interactions.key = util.clone( api.events.input.key ) + interactions.input = "" -- collider vars - 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.direction = quat():direction() interactions.clearTable = function(o) foreach( o, function(k,v) o[k] = false end ) end interactions.clear = function() interactions.clearTable( interactions.pointer ) @@ -210,23 +212,20 @@ function initInteractions(ecs) interactions.input = false end - function interactions:process(obj, dt) - ecs.worldPhysics:update(dt) - - local castsrc = 'hand/left/point' - local castsrc = 'head' - + interactions.collect = function(dt) + local castsrc = 'head' -- 'hand/left/point' + local event = util.clone(api.events.input) + interactions.event = event -- for convenience (ecs systems can always reach it) local ox, oy, oz = lovr.headset.getPosition(castsrc) - local direction = quat(lovr.headset.getOrientation(castsrc)):direction() - if direction ~= nil then - local dx, dy, dz = vector( direction * 50 ):unpack() + interactions.direction = quat(lovr.headset.getOrientation(castsrc)):direction() + if interactions.direction ~= nil then + local dx, dy, dz = vector( interactions.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.pointer.hover = true interactions.hitpoint.x = x interactions.hitpoint.y = y interactions.hitpoint.z = z @@ -239,22 +238,29 @@ function initInteractions(ecs) interactions.pointer.released = true end end - if interactions.pointer.released and node.onclick ~= nil then - node.onclick(node,collider) - end + event.collider = collider + event.node = node + end + end + event.direction = util.clone(direction) + event.input = interactions.input + event.pointer = util.clone(interactions.pointer) + event.key = util.clone(interactions.key) + event.hitpoint = { x = interactions.hitpoint.x, y = interactions.hitpoint.y, z = interactions.z } + --event.head = { x = ox, y = oy, z = oz, direction = direction}, + api.ext.exec('input', dt, event ) + return event + end + + function interactions:process(obj, dt) + ecs.worldPhysics:update(dt) + local event = interactions.event + if event.collider then + --if event.pointer.released and event.node and event.node.onclick ~= nil then + if event.select and event.node and event.node.onclick ~= nil then + event.node.onclick(event.node, event.collider) end end - -- 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, interactions ) end diff --git a/xurfer/test/main.lua b/xurfer/test/main.lua index 5627be4..568bf7e 100644 --- a/xurfer/test/main.lua +++ b/xurfer/test/main.lua @@ -1,3 +1,4 @@ +--@LIB local lust = require 'lust' local describe, it, expect = lust.describe, lust.it, lust.expect