Compare commits
5 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9360423a5c | |||
| f11b420197 | |||
| 0930bd7544 | |||
| 6a995dd3c5 | |||
| 14500b44a2 |
26 changed files with 993 additions and 756 deletions
|
|
@ -1,16 +1,13 @@
|
||||||
|
-- default argument
|
||||||
|
if arg[1] == nil then
|
||||||
|
arg[1] = "https://snips.sh/f/_U5-XctEVE?r=1"
|
||||||
|
end
|
||||||
|
|
||||||
-- sync lovr/love positional cli arguments
|
-- sync lovr/love positional cli arguments
|
||||||
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
|
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
|
||||||
|
|
||||||
package.path = package.path .. ';' ..
|
package.path = package.path .. ';' .. '?.lua;' .. 'lib/?.lua'
|
||||||
'?.lua;' ..
|
|
||||||
'?/init.lua;' ..
|
|
||||||
'lib/?.lua;'
|
|
||||||
|
|
||||||
local runtime = require("runtime/detect")
|
local runtime = require("runtime/detect")
|
||||||
|
|
||||||
package.path = package.path .. ';' ..
|
|
||||||
arg[0] .. '/' .. runtime.path .. '/?.lua;' ..
|
|
||||||
arg[0] .. '/' .. runtime.path .. '/?/init.lua;' ..
|
|
||||||
arg[0] .. '/lib/?.lua'
|
|
||||||
|
|
||||||
require( runtime.path .. "/conf")
|
require( runtime.path .. "/conf")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local ecs = require("tiny-ecs")
|
local ecs = require("lib/tiny-ecs")
|
||||||
|
|
||||||
ecs.init = function(api)
|
ecs.init = function(api)
|
||||||
print("[i] loading ecs")
|
print("[i] loading ecs")
|
||||||
|
|
@ -30,4 +30,14 @@ ecs.clear = function()
|
||||||
api.world.commit()
|
api.world.commit()
|
||||||
end
|
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
|
return ecs
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,25 @@ return {
|
||||||
obj.root = false
|
obj.root = false
|
||||||
if obj.URI.target == '_top' then
|
if obj.URI.target == '_top' then
|
||||||
obj.root = true
|
obj.root = true
|
||||||
|
if obj.URI.protocol ~= 'xrf' then -- xrf:// should not update toplevel URI
|
||||||
api.ext.URI.current = obj.URL -- set current url
|
api.ext.URI.current = obj.URL -- set current url
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
if type(obj.URI.target) == 'table' and obj.URI.target.obj ~= nil then
|
||||||
|
local parent = obj.URI.target.obj
|
||||||
|
-- TODO: the runtime/lovr/render/model code should calculate parent offset
|
||||||
|
obj.x = parent.x
|
||||||
|
obj.y = parent.y
|
||||||
|
obj.z = parent.z
|
||||||
|
obj.scale = parent.scale
|
||||||
|
else
|
||||||
obj.x = obj.x or 0
|
obj.x = obj.x or 0
|
||||||
obj.y = obj.y or 0
|
obj.y = obj.y or 0
|
||||||
obj.z = obj.z or 0
|
obj.z = obj.z or 0
|
||||||
obj.scale = obj.scale or 1
|
obj.scale = obj.scale or 1
|
||||||
|
end
|
||||||
if obj.URL.protocol == 'file' then
|
if obj.URL.protocol == 'file' then
|
||||||
obj.model = api.graphics.newModel( obj.URL.string )
|
obj.model = api.graphics.newModel( obj.URL.file )
|
||||||
else
|
else
|
||||||
obj.model = api.graphics.newModel( api.data.newBlob( obj.URLResponse.data) )
|
obj.model = api.graphics.newModel( api.data.newBlob( obj.URLResponse.data) )
|
||||||
end
|
end
|
||||||
|
|
|
||||||
106
xurfer/ext/gazecontrol/main.lua
Normal file
106
xurfer/ext/gazecontrol/main.lua
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
local api = ...
|
||||||
|
local ecs = api.ecs
|
||||||
|
|
||||||
|
local gctl
|
||||||
|
|
||||||
|
gctl = {
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
|
||||||
|
initVisor = function()
|
||||||
|
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 pos = vector( lovr.headset.getPosition('head') ) + vector( api.player.matrix:getPosition() )
|
||||||
|
local dirx, diry, dirz = lovr.headset.getDirection('head')
|
||||||
|
local distance = 1
|
||||||
|
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 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,
|
||||||
|
|
||||||
|
-- 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
|
||||||
BIN
xurfer/ext/gazecontrol/reticle_hover.png
Normal file
BIN
xurfer/ext/gazecontrol/reticle_hover.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
xurfer/ext/gazecontrol/reticle_idle.png
Normal file
BIN
xurfer/ext/gazecontrol/reticle_idle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
|
|
@ -7,7 +7,12 @@ return {
|
||||||
enabled = true,
|
enabled = true,
|
||||||
|
|
||||||
onURI = function(obj)
|
onURI = function(obj)
|
||||||
if obj.URL ~= nil and obj.URLResponse ~= nil and obj.URLResponse.ok then
|
if obj.URL ~= nil and (obj.URLResponse ~= nil or obj.URL.protocol == 'file') then
|
||||||
|
if obj.URL.protocol == 'file' and obj.URLResponse.ok == nil then
|
||||||
|
local data = util.readfile(obj.URL.file)
|
||||||
|
obj.URLResponse = { data = data, ok = (data ~= nil) }
|
||||||
|
end
|
||||||
|
if obj.URLResponse.ok then
|
||||||
local res = obj.URLResponse
|
local res = obj.URLResponse
|
||||||
-- JML heuristic
|
-- JML heuristic
|
||||||
if res.data:lower():match("<fireboxroom>") or res.data:lower():match("<room[ >]") then
|
if res.data:lower():match("<fireboxroom>") or res.data:lower():match("<room[ >]") then
|
||||||
|
|
@ -20,6 +25,10 @@ return {
|
||||||
JML0 = JML0:gsub("</[Rr]oom>.*","</room>")
|
JML0 = JML0:gsub("</[Rr]oom>.*","</room>")
|
||||||
api.ext.janusxr.loadXML( JML0, res, obj )
|
api.ext.janusxr.loadXML( JML0, res, obj )
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
print("[janusxr] error: could not load " .. obj.URI.url .. ": ")
|
||||||
|
print_r(obj.URLResponse)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,9 @@ return {
|
||||||
--api.ext.skeleton.extendECS()
|
--api.ext.skeleton.extendECS()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
update = function()
|
update = function(dt,input) end,
|
||||||
local iui = api.iui
|
|
||||||
local backend = api.backend
|
|
||||||
local mainWindow = api.mainWindow
|
|
||||||
end,
|
|
||||||
|
|
||||||
load = function() end,
|
load = function() end,
|
||||||
|
draw = function(pass,dt) end,
|
||||||
draw = function(pass)
|
|
||||||
if api.iui.idiom == "vr" then
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- ECS API docs: https://bakpakin.github.io/tiny-ecs/doc
|
-- ECS API docs: https://bakpakin.github.io/tiny-ecs/doc
|
||||||
extendECS = function()
|
extendECS = function()
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ local shader
|
||||||
return {
|
return {
|
||||||
name = "startupscene",
|
name = "startupscene",
|
||||||
enabled = (lovr ~= nil),
|
enabled = (lovr ~= nil),
|
||||||
|
index = api.ext.max * 0.1,
|
||||||
|
|
||||||
init = function() end,
|
init = function() end,
|
||||||
|
|
||||||
|
|
@ -45,25 +46,30 @@ return {
|
||||||
-- skybox
|
-- skybox
|
||||||
pass:setCullMode('back')
|
pass:setCullMode('back')
|
||||||
pass:setBlendMode()
|
pass:setBlendMode()
|
||||||
|
pass:setShader()
|
||||||
|
pass:setColor(1,1,1,1)
|
||||||
pass:skybox(api.world.skybox)
|
pass:skybox(api.world.skybox)
|
||||||
|
|
||||||
pass:setColor(1, 1, 1)
|
pass:setColor(1, 1, 1)
|
||||||
pass:setMaterial(envTex)
|
pass:setMaterial(envTex)
|
||||||
|
pass:setDepthWrite(false)
|
||||||
pass:draw(envTex, 0, 0, 0, 512, math.pi * 0.5, 1, 0, 0)
|
pass:draw(envTex, 0, 0, 0, 512, math.pi * 0.5, 1, 0, 0)
|
||||||
|
|
||||||
-- floor
|
-- floor
|
||||||
--pass:setMaterial()
|
pass:setMaterial()
|
||||||
pass:setCullMode('front')
|
pass:setDepthWrite(true)
|
||||||
pass:setBlendMode('alpha')
|
pass:setShader()
|
||||||
|
--pass:setCullMode('front')
|
||||||
|
--pass:setBlendMode('alpha')
|
||||||
pass:setMaterial()
|
pass:setMaterial()
|
||||||
pass:setColor(0.8,0.8,0.8,1)
|
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)
|
pass:plane(0, 0, 0, 500, 500, math.pi * 0.5, 1, 0, 0, "fill", 5, 5)
|
||||||
pass:setColor( 0, 0, 0, 0.75 )
|
pass:setColor( 0, 0, 0, 0.75 )
|
||||||
pass:plane(0, 0.01, 0, 500, 500, math.pi * 0.5, 1, 0, 0, "line", 100, 100)
|
pass:plane(0, 0.01, 0, 500, 500, math.pi * 0.5, 1, 0, 0, "line", 100, 100)
|
||||||
|
|
||||||
pass:setShader( lovr.shader.pbr )
|
--pass:setShader( lovr.shader.pbr )
|
||||||
pass:send('cubemap', api.world.environmentMap)
|
--pass:send('cubemap', api.world.environmentMap)
|
||||||
pass:send('sphericalHarmonics', api.world.sphericalHarmonics)
|
--pass:send('sphericalHarmonics', api.world.sphericalHarmonics)
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ unixy = {
|
||||||
stderr_file = stderr_file,
|
stderr_file = stderr_file,
|
||||||
callback = callback
|
callback = callback
|
||||||
})
|
})
|
||||||
trace("[unixy] start job with pid " .. pid .. " => 1> " .. stdout_file .. " 2> " .. stderr_file)
|
trace("[unixy] start job with pid " .. pid .. ": " .. cmd)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
check_jobs = function()
|
check_jobs = function()
|
||||||
|
|
|
||||||
|
|
@ -1,386 +0,0 @@
|
||||||
--
|
|
||||||
-- json.lua
|
|
||||||
--
|
|
||||||
-- Copyright (c) 2020 rxi
|
|
||||||
--
|
|
||||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
-- this software and associated documentation files (the "Software"), to deal in
|
|
||||||
-- the Software without restriction, including without limitation the rights to
|
|
||||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
-- so, subject to the following conditions:
|
|
||||||
--
|
|
||||||
-- The above copyright notice and this permission notice shall be included in all
|
|
||||||
-- copies or substantial portions of the Software.
|
|
||||||
--
|
|
||||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
-- SOFTWARE.
|
|
||||||
--
|
|
||||||
|
|
||||||
local json = { _version = "0.1.2" }
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Encode
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local encode
|
|
||||||
|
|
||||||
local escape_char_map = {
|
|
||||||
[ "\\" ] = "\\",
|
|
||||||
[ "\"" ] = "\"",
|
|
||||||
[ "\b" ] = "b",
|
|
||||||
[ "\f" ] = "f",
|
|
||||||
[ "\n" ] = "n",
|
|
||||||
[ "\r" ] = "r",
|
|
||||||
[ "\t" ] = "t",
|
|
||||||
}
|
|
||||||
|
|
||||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
|
||||||
for k, v in pairs(escape_char_map) do
|
|
||||||
escape_char_map_inv[v] = k
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function escape_char(c)
|
|
||||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_nil(val)
|
|
||||||
return "null"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_table(val, stack)
|
|
||||||
local res = {}
|
|
||||||
stack = stack or {}
|
|
||||||
|
|
||||||
-- Circular reference?
|
|
||||||
if stack[val] then error("circular reference") end
|
|
||||||
|
|
||||||
stack[val] = true
|
|
||||||
|
|
||||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
|
||||||
-- Treat as array -- check keys are valid and it is not sparse
|
|
||||||
local n = 0
|
|
||||||
for k in pairs(val) do
|
|
||||||
if type(k) ~= "number" then
|
|
||||||
error("invalid table: mixed or invalid key types")
|
|
||||||
end
|
|
||||||
n = n + 1
|
|
||||||
end
|
|
||||||
if n ~= #val then
|
|
||||||
error("invalid table: sparse array")
|
|
||||||
end
|
|
||||||
-- Encode
|
|
||||||
for i, v in ipairs(val) do
|
|
||||||
table.insert(res, encode(v, stack))
|
|
||||||
end
|
|
||||||
stack[val] = nil
|
|
||||||
return "[" .. table.concat(res, ",") .. "]"
|
|
||||||
|
|
||||||
else
|
|
||||||
-- Treat as an object
|
|
||||||
for k, v in pairs(val) do
|
|
||||||
if type(k) ~= "string" then
|
|
||||||
error("invalid table: mixed or invalid key types")
|
|
||||||
end
|
|
||||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
|
||||||
end
|
|
||||||
stack[val] = nil
|
|
||||||
return "{" .. table.concat(res, ",") .. "}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_string(val)
|
|
||||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function encode_number(val)
|
|
||||||
-- Check for NaN, -inf and inf
|
|
||||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
|
||||||
error("unexpected number value '" .. tostring(val) .. "'")
|
|
||||||
end
|
|
||||||
return string.format("%.14g", val)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local type_func_map = {
|
|
||||||
[ "nil" ] = encode_nil,
|
|
||||||
[ "table" ] = encode_table,
|
|
||||||
[ "string" ] = encode_string,
|
|
||||||
[ "number" ] = encode_number,
|
|
||||||
[ "boolean" ] = tostring,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
encode = function(val, stack)
|
|
||||||
local t = type(val)
|
|
||||||
local f = type_func_map[t]
|
|
||||||
if f then
|
|
||||||
return f(val, stack)
|
|
||||||
end
|
|
||||||
error("unexpected type '" .. t .. "'")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function json.encode(val)
|
|
||||||
return ( encode(val) )
|
|
||||||
end
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Decode
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local parse
|
|
||||||
|
|
||||||
local function create_set(...)
|
|
||||||
local res = {}
|
|
||||||
for i = 1, select("#", ...) do
|
|
||||||
res[ select(i, ...) ] = true
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
|
||||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
|
||||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
|
||||||
local literals = create_set("true", "false", "null")
|
|
||||||
|
|
||||||
local literal_map = {
|
|
||||||
[ "true" ] = true,
|
|
||||||
[ "false" ] = false,
|
|
||||||
[ "null" ] = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local function next_char(str, idx, set, negate)
|
|
||||||
for i = idx, #str do
|
|
||||||
if set[str:sub(i, i)] ~= negate then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return #str + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function decode_error(str, idx, msg)
|
|
||||||
local line_count = 1
|
|
||||||
local col_count = 1
|
|
||||||
for i = 1, idx - 1 do
|
|
||||||
col_count = col_count + 1
|
|
||||||
if str:sub(i, i) == "\n" then
|
|
||||||
line_count = line_count + 1
|
|
||||||
col_count = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function codepoint_to_utf8(n)
|
|
||||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
|
||||||
local f = math.floor
|
|
||||||
if n <= 0x7f then
|
|
||||||
return string.char(n)
|
|
||||||
elseif n <= 0x7ff then
|
|
||||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
|
||||||
elseif n <= 0xffff then
|
|
||||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
|
||||||
elseif n <= 0x10ffff then
|
|
||||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
|
||||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
|
||||||
end
|
|
||||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_unicode_escape(s)
|
|
||||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
|
||||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
|
||||||
-- Surrogate pair?
|
|
||||||
if n2 then
|
|
||||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
|
||||||
else
|
|
||||||
return codepoint_to_utf8(n1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_string(str, i)
|
|
||||||
local res = ""
|
|
||||||
local j = i + 1
|
|
||||||
local k = j
|
|
||||||
|
|
||||||
while j <= #str do
|
|
||||||
local x = str:byte(j)
|
|
||||||
|
|
||||||
if x < 32 then
|
|
||||||
decode_error(str, j, "control character in string")
|
|
||||||
|
|
||||||
elseif x == 92 then -- `\`: Escape
|
|
||||||
res = res .. str:sub(k, j - 1)
|
|
||||||
j = j + 1
|
|
||||||
local c = str:sub(j, j)
|
|
||||||
if c == "u" then
|
|
||||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
|
||||||
or str:match("^%x%x%x%x", j + 1)
|
|
||||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
|
||||||
res = res .. parse_unicode_escape(hex)
|
|
||||||
j = j + #hex
|
|
||||||
else
|
|
||||||
if not escape_chars[c] then
|
|
||||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
|
||||||
end
|
|
||||||
res = res .. escape_char_map_inv[c]
|
|
||||||
end
|
|
||||||
k = j + 1
|
|
||||||
|
|
||||||
elseif x == 34 then -- `"`: End of string
|
|
||||||
res = res .. str:sub(k, j - 1)
|
|
||||||
return res, j + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
j = j + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
decode_error(str, i, "expected closing quote for string")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_number(str, i)
|
|
||||||
local x = next_char(str, i, delim_chars)
|
|
||||||
local s = str:sub(i, x - 1)
|
|
||||||
local n = tonumber(s)
|
|
||||||
if not n then
|
|
||||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
|
||||||
end
|
|
||||||
return n, x
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_literal(str, i)
|
|
||||||
local x = next_char(str, i, delim_chars)
|
|
||||||
local word = str:sub(i, x - 1)
|
|
||||||
if not literals[word] then
|
|
||||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
|
||||||
end
|
|
||||||
return literal_map[word], x
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_array(str, i)
|
|
||||||
local res = {}
|
|
||||||
local n = 1
|
|
||||||
i = i + 1
|
|
||||||
while 1 do
|
|
||||||
local x
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
-- Empty / end of array?
|
|
||||||
if str:sub(i, i) == "]" then
|
|
||||||
i = i + 1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
-- Read token
|
|
||||||
x, i = parse(str, i)
|
|
||||||
res[n] = x
|
|
||||||
n = n + 1
|
|
||||||
-- Next token
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
local chr = str:sub(i, i)
|
|
||||||
i = i + 1
|
|
||||||
if chr == "]" then break end
|
|
||||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
|
||||||
end
|
|
||||||
return res, i
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function parse_object(str, i)
|
|
||||||
local res = {}
|
|
||||||
i = i + 1
|
|
||||||
while 1 do
|
|
||||||
local key, val
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
-- Empty / end of object?
|
|
||||||
if str:sub(i, i) == "}" then
|
|
||||||
i = i + 1
|
|
||||||
break
|
|
||||||
end
|
|
||||||
-- Read key
|
|
||||||
if str:sub(i, i) ~= '"' then
|
|
||||||
decode_error(str, i, "expected string for key")
|
|
||||||
end
|
|
||||||
key, i = parse(str, i)
|
|
||||||
-- Read ':' delimiter
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
if str:sub(i, i) ~= ":" then
|
|
||||||
decode_error(str, i, "expected ':' after key")
|
|
||||||
end
|
|
||||||
i = next_char(str, i + 1, space_chars, true)
|
|
||||||
-- Read value
|
|
||||||
val, i = parse(str, i)
|
|
||||||
-- Set
|
|
||||||
res[key] = val
|
|
||||||
-- Next token
|
|
||||||
i = next_char(str, i, space_chars, true)
|
|
||||||
local chr = str:sub(i, i)
|
|
||||||
i = i + 1
|
|
||||||
if chr == "}" then break end
|
|
||||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
|
||||||
end
|
|
||||||
return res, i
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local char_func_map = {
|
|
||||||
[ '"' ] = parse_string,
|
|
||||||
[ "0" ] = parse_number,
|
|
||||||
[ "1" ] = parse_number,
|
|
||||||
[ "2" ] = parse_number,
|
|
||||||
[ "3" ] = parse_number,
|
|
||||||
[ "4" ] = parse_number,
|
|
||||||
[ "5" ] = parse_number,
|
|
||||||
[ "6" ] = parse_number,
|
|
||||||
[ "7" ] = parse_number,
|
|
||||||
[ "8" ] = parse_number,
|
|
||||||
[ "9" ] = parse_number,
|
|
||||||
[ "-" ] = parse_number,
|
|
||||||
[ "t" ] = parse_literal,
|
|
||||||
[ "f" ] = parse_literal,
|
|
||||||
[ "n" ] = parse_literal,
|
|
||||||
[ "[" ] = parse_array,
|
|
||||||
[ "{" ] = parse_object,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
parse = function(str, idx)
|
|
||||||
local chr = str:sub(idx, idx)
|
|
||||||
local f = char_func_map[chr]
|
|
||||||
if f then
|
|
||||||
return f(str, idx)
|
|
||||||
end
|
|
||||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function json.decode(str)
|
|
||||||
if type(str) ~= "string" then
|
|
||||||
error("expected argument of type string, got " .. type(str))
|
|
||||||
end
|
|
||||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
|
||||||
idx = next_char(str, idx, space_chars, true)
|
|
||||||
if idx <= #str then
|
|
||||||
decode_error(str, idx, "trailing garbage")
|
|
||||||
end
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
|
|
||||||
return json
|
|
||||||
|
|
@ -107,7 +107,7 @@ xrf.makeClickable = function( physicsWorld, cb)
|
||||||
scale[3] * (d + 0.01) -- planes e.g.
|
scale[3] * (d + 0.01) -- planes e.g.
|
||||||
)
|
)
|
||||||
print("making node " .. node['name'] .. " clickable: " .. node.extras.href )
|
print("making node " .. node['name'] .. " clickable: " .. node.extras.href )
|
||||||
collider:setUserData( {name = node['name'], node = node, model = model, mesh = mesh, onclick = cb })
|
collider:setUserData( {name = node['name'], node = node, model = model, mesh = mesh, onclick = cb, obj = obj })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ xrfimpl = {
|
||||||
init = function() end,
|
init = function() end,
|
||||||
|
|
||||||
on3DFile = function(obj)
|
on3DFile = function(obj)
|
||||||
|
local referer = obj.URI.url
|
||||||
if lovr ~= nil then
|
if lovr ~= nil then
|
||||||
xrf.traverseNodesContaining('href', obj,
|
xrf.traverseNodesContaining('href', obj,
|
||||||
xrf.makeClickable( api.ecs.worldPhysics,
|
xrf.makeClickable( api.ecs.worldPhysics,
|
||||||
|
|
@ -18,10 +19,9 @@ xrfimpl = {
|
||||||
local href = obj.node.extras.href
|
local href = obj.node.extras.href
|
||||||
trace("\n[xrf] href was clicked: " .. href)
|
trace("\n[xrf] href was clicked: " .. href)
|
||||||
api.ext.exec("onClick", obj, collider)
|
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, api.ext.URI.current, xrfimpl.onLoad)
|
||||||
or
|
or
|
||||||
xrf.level4.import( href, obj, api.ext.URI.current, xrfimpl.onImport)
|
xrf.level4.import( href, obj, referer, xrfimpl.onImport)
|
||||||
|
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
@ -51,10 +51,12 @@ xrfimpl = {
|
||||||
local filter = function(o)
|
local filter = function(o)
|
||||||
return (o.URL ~= nil and o.URL.URN:gsub("#.*","") == absoluteHref:gsub("#.*",""))
|
return (o.URL ~= nil and o.URL.URN:gsub("#.*","") == absoluteHref:gsub("#.*",""))
|
||||||
end
|
end
|
||||||
local obj = api.world.get(filter)
|
local newobj = api.world.get(filter)
|
||||||
if obj then
|
if newobj then
|
||||||
obj.URL = url.parse(absoluteHref) -- re-parse to detect media fragment change
|
if newobj.URL.audio ~= nil then
|
||||||
|
newobj.URL = url.parse(absoluteHref) -- re-parse to detect media fragment change
|
||||||
xrfimpl.onAudioFile(obj)
|
xrfimpl.onAudioFile(obj)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
api.ecs.add( api.world, { URI = { url = absoluteHref, method = 'GET', target = obj } })
|
api.ecs.add( api.world, { URI = { url = absoluteHref, method = 'GET', target = obj } })
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,166 +0,0 @@
|
||||||
-- Copyright 2026 Leon van Kammen / coderofsalvation. All rights reserved.
|
|
||||||
--
|
|
||||||
-- Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
-- permitted provided that the following conditions are met:
|
|
||||||
--
|
|
||||||
-- 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
-- conditions and the following disclaimer.
|
|
||||||
--
|
|
||||||
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
-- of conditions and the following disclaimer in the documentation and/or other materials
|
|
||||||
-- provided with the distribution.
|
|
||||||
--
|
|
||||||
-- THIS SOFTWARE IS PROVIDED BY AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
||||||
-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
||||||
-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR
|
|
||||||
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
-- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
||||||
--
|
|
||||||
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
--
|
|
||||||
-- The views and conclusions contained in the software and documentation are those of the
|
|
||||||
-- authors and should not be interpreted as representing official policies, either expressed
|
|
||||||
-- or implied, of
|
|
||||||
|
|
||||||
-- below is a lua adaptation of Godot's URI.gd
|
|
||||||
-- https://gist.github.com/coderofsalvation/b2b111a2631fbdc8e76d6cab3bea8f17
|
|
||||||
|
|
||||||
local url = {}
|
|
||||||
|
|
||||||
-- Helper function to split a string by a delimiter
|
|
||||||
local function split(str, delimiter)
|
|
||||||
local result = {}
|
|
||||||
if delimiter == "" then return {str} end
|
|
||||||
local pattern = string.format("([^%s]+)", delimiter)
|
|
||||||
for match in string.gmatch(str, pattern) do
|
|
||||||
table.insert(result, match)
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper for guess_type (converts numbers/booleans from strings)
|
|
||||||
local function guess_type(val)
|
|
||||||
if tonumber(val) then return tonumber(val) end
|
|
||||||
if val == "true" then return true end
|
|
||||||
if val == "false" then return false end
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper to parse query parameters and fragments
|
|
||||||
local function parseArgs(fragment)
|
|
||||||
local ARG = {}
|
|
||||||
if not fragment or fragment == "" then return ARG end
|
|
||||||
|
|
||||||
local items = split(fragment, "&")
|
|
||||||
for _, item in ipairs(items) do
|
|
||||||
local key_value = split(item, "=")
|
|
||||||
if #key_value > 1 then
|
|
||||||
local k = key_value[1]
|
|
||||||
ARG[k] = guess_type(key_value[2]) -- xr fragment operators preserved
|
|
||||||
ARG[k:gsub("[^%w]","") ] = guess_type(key_value[2]) -- alphanumeric only
|
|
||||||
elseif #key_value == 1 then
|
|
||||||
ARG[key_value[1]] = ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ARG
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Main URL Parser function attached to the module object
|
|
||||||
function url.parse(url_string)
|
|
||||||
if type(url_string) == 'table' then return url_string end -- already parsed
|
|
||||||
-- Setup initial URI table structure
|
|
||||||
local URI = {
|
|
||||||
domain = "",
|
|
||||||
fragment = {},
|
|
||||||
file = "",
|
|
||||||
extension = "",
|
|
||||||
string = url_string,
|
|
||||||
protocol = "",
|
|
||||||
path = "",
|
|
||||||
query = {},
|
|
||||||
hash = "",
|
|
||||||
URN = "",
|
|
||||||
isLocal = false
|
|
||||||
}
|
|
||||||
local remaining = url_string
|
|
||||||
local protocol = string.match(remaining, "^(%w+://)") -- protocol
|
|
||||||
if protocol then
|
|
||||||
URI.protocol = protocol:gsub("://", "")
|
|
||||||
remaining = remaining:sub(#protocol + 1)
|
|
||||||
else
|
|
||||||
URI.protocol = "file"
|
|
||||||
end
|
|
||||||
local hash = string.match(remaining, "(#.*)$") -- fragment
|
|
||||||
if hash then
|
|
||||||
URI.hash = hash
|
|
||||||
remaining = remaining:sub(1, #remaining - #hash)
|
|
||||||
end
|
|
||||||
local query = string.match(remaining, "(%?[^#]+)$")
|
|
||||||
if query then
|
|
||||||
URI.query = query
|
|
||||||
remaining = remaining:sub(1, #remaining - #query)
|
|
||||||
end
|
|
||||||
URI.path = remaining
|
|
||||||
-- Process Path, Domain, and File
|
|
||||||
if URI.path and URI.path ~= "" then
|
|
||||||
local pathParts = split(URI.path, "/")
|
|
||||||
|
|
||||||
if #pathParts > 1 then
|
|
||||||
local firstPart = pathParts[1]
|
|
||||||
-- Check if the first part looks like a domain (contains '.' or ':')
|
|
||||||
if string.find(firstPart, "%.") or string.find(firstPart, ":") then
|
|
||||||
URI.domain = table.remove(pathParts, 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- Reconstruct path
|
|
||||||
URI.path = table.concat(pathParts, "/")
|
|
||||||
-- Extract file if present
|
|
||||||
if #pathParts > 0 then
|
|
||||||
local lastPart = pathParts[#pathParts]
|
|
||||||
if string.find(lastPart, "%.") then
|
|
||||||
URI.file = lastPart
|
|
||||||
URI.extension = URI.file:match("%.([^%.]+)$"):upper()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if URI.hash and URI.hash ~= "" then
|
|
||||||
URI.fragment = parseArgs(URI.hash:sub(2)) -- sub(2) skips the '#'
|
|
||||||
else
|
|
||||||
URI.fragment = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
if URI.query and type(URI.query) == "string" and URI.query ~= "" then
|
|
||||||
URI.query = parseArgs(URI.query:sub(2)) -- sub(2) skips the '?'
|
|
||||||
else
|
|
||||||
URI.query = {}
|
|
||||||
end
|
|
||||||
if URI.domain ~= "" then
|
|
||||||
URI.URN = URI.string:gsub("%?.*", "")
|
|
||||||
else
|
|
||||||
URI.URN = ""
|
|
||||||
end
|
|
||||||
URI.isLocal = (URI.domain == "")
|
|
||||||
return URI
|
|
||||||
end
|
|
||||||
|
|
||||||
url.getAbsolute = function(href,referer,keephash)
|
|
||||||
local URL = url.parse(href)
|
|
||||||
local URLref = url.parse(referer)
|
|
||||||
if referer ~= nil and URL.protocol == 'file' and URLref.protocol ~= 'file' then
|
|
||||||
URL.protocol = URLref.protocol
|
|
||||||
URL.domain = URLref.domain
|
|
||||||
URL.string = URLref.protocol .. '://' .. URLref.domain .. '/' .. URL.path .. URL.hash
|
|
||||||
if keephash then
|
|
||||||
URL.URN = URL.string
|
|
||||||
else
|
|
||||||
URL.URN = URL.string:gsub("#.*", "")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return URL
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return the module table so it can be assigned via require()
|
|
||||||
return url
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
../ext/xrfragments/json.lua
|
|
||||||
386
xurfer/lib/json.lua
Normal file
386
xurfer/lib/json.lua
Normal file
|
|
@ -0,0 +1,386 @@
|
||||||
|
--
|
||||||
|
-- json.lua
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2020 rxi
|
||||||
|
--
|
||||||
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
-- this software and associated documentation files (the "Software"), to deal in
|
||||||
|
-- the Software without restriction, including without limitation the rights to
|
||||||
|
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
-- so, subject to the following conditions:
|
||||||
|
--
|
||||||
|
-- The above copyright notice and this permission notice shall be included in all
|
||||||
|
-- copies or substantial portions of the Software.
|
||||||
|
--
|
||||||
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
-- SOFTWARE.
|
||||||
|
--
|
||||||
|
|
||||||
|
local json = { _version = "0.1.2" }
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Encode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local encode
|
||||||
|
|
||||||
|
local escape_char_map = {
|
||||||
|
[ "\\" ] = "\\",
|
||||||
|
[ "\"" ] = "\"",
|
||||||
|
[ "\b" ] = "b",
|
||||||
|
[ "\f" ] = "f",
|
||||||
|
[ "\n" ] = "n",
|
||||||
|
[ "\r" ] = "r",
|
||||||
|
[ "\t" ] = "t",
|
||||||
|
}
|
||||||
|
|
||||||
|
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||||
|
for k, v in pairs(escape_char_map) do
|
||||||
|
escape_char_map_inv[v] = k
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function escape_char(c)
|
||||||
|
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_nil(val)
|
||||||
|
return "null"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_table(val, stack)
|
||||||
|
local res = {}
|
||||||
|
stack = stack or {}
|
||||||
|
|
||||||
|
-- Circular reference?
|
||||||
|
if stack[val] then error("circular reference") end
|
||||||
|
|
||||||
|
stack[val] = true
|
||||||
|
|
||||||
|
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||||
|
-- Treat as array -- check keys are valid and it is not sparse
|
||||||
|
local n = 0
|
||||||
|
for k in pairs(val) do
|
||||||
|
if type(k) ~= "number" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
if n ~= #val then
|
||||||
|
error("invalid table: sparse array")
|
||||||
|
end
|
||||||
|
-- Encode
|
||||||
|
for i, v in ipairs(val) do
|
||||||
|
table.insert(res, encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "[" .. table.concat(res, ",") .. "]"
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Treat as an object
|
||||||
|
for k, v in pairs(val) do
|
||||||
|
if type(k) ~= "string" then
|
||||||
|
error("invalid table: mixed or invalid key types")
|
||||||
|
end
|
||||||
|
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||||
|
end
|
||||||
|
stack[val] = nil
|
||||||
|
return "{" .. table.concat(res, ",") .. "}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_string(val)
|
||||||
|
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function encode_number(val)
|
||||||
|
-- Check for NaN, -inf and inf
|
||||||
|
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||||
|
error("unexpected number value '" .. tostring(val) .. "'")
|
||||||
|
end
|
||||||
|
return string.format("%.14g", val)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local type_func_map = {
|
||||||
|
[ "nil" ] = encode_nil,
|
||||||
|
[ "table" ] = encode_table,
|
||||||
|
[ "string" ] = encode_string,
|
||||||
|
[ "number" ] = encode_number,
|
||||||
|
[ "boolean" ] = tostring,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
encode = function(val, stack)
|
||||||
|
local t = type(val)
|
||||||
|
local f = type_func_map[t]
|
||||||
|
if f then
|
||||||
|
return f(val, stack)
|
||||||
|
end
|
||||||
|
error("unexpected type '" .. t .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.encode(val)
|
||||||
|
return ( encode(val) )
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Decode
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local parse
|
||||||
|
|
||||||
|
local function create_set(...)
|
||||||
|
local res = {}
|
||||||
|
for i = 1, select("#", ...) do
|
||||||
|
res[ select(i, ...) ] = true
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||||
|
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||||
|
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||||
|
local literals = create_set("true", "false", "null")
|
||||||
|
|
||||||
|
local literal_map = {
|
||||||
|
[ "true" ] = true,
|
||||||
|
[ "false" ] = false,
|
||||||
|
[ "null" ] = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local function next_char(str, idx, set, negate)
|
||||||
|
for i = idx, #str do
|
||||||
|
if set[str:sub(i, i)] ~= negate then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return #str + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function decode_error(str, idx, msg)
|
||||||
|
local line_count = 1
|
||||||
|
local col_count = 1
|
||||||
|
for i = 1, idx - 1 do
|
||||||
|
col_count = col_count + 1
|
||||||
|
if str:sub(i, i) == "\n" then
|
||||||
|
line_count = line_count + 1
|
||||||
|
col_count = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function codepoint_to_utf8(n)
|
||||||
|
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||||
|
local f = math.floor
|
||||||
|
if n <= 0x7f then
|
||||||
|
return string.char(n)
|
||||||
|
elseif n <= 0x7ff then
|
||||||
|
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||||
|
elseif n <= 0xffff then
|
||||||
|
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
elseif n <= 0x10ffff then
|
||||||
|
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||||
|
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||||
|
end
|
||||||
|
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_unicode_escape(s)
|
||||||
|
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||||
|
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||||
|
-- Surrogate pair?
|
||||||
|
if n2 then
|
||||||
|
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||||
|
else
|
||||||
|
return codepoint_to_utf8(n1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_string(str, i)
|
||||||
|
local res = ""
|
||||||
|
local j = i + 1
|
||||||
|
local k = j
|
||||||
|
|
||||||
|
while j <= #str do
|
||||||
|
local x = str:byte(j)
|
||||||
|
|
||||||
|
if x < 32 then
|
||||||
|
decode_error(str, j, "control character in string")
|
||||||
|
|
||||||
|
elseif x == 92 then -- `\`: Escape
|
||||||
|
res = res .. str:sub(k, j - 1)
|
||||||
|
j = j + 1
|
||||||
|
local c = str:sub(j, j)
|
||||||
|
if c == "u" then
|
||||||
|
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||||
|
or str:match("^%x%x%x%x", j + 1)
|
||||||
|
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||||
|
res = res .. parse_unicode_escape(hex)
|
||||||
|
j = j + #hex
|
||||||
|
else
|
||||||
|
if not escape_chars[c] then
|
||||||
|
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||||
|
end
|
||||||
|
res = res .. escape_char_map_inv[c]
|
||||||
|
end
|
||||||
|
k = j + 1
|
||||||
|
|
||||||
|
elseif x == 34 then -- `"`: End of string
|
||||||
|
res = res .. str:sub(k, j - 1)
|
||||||
|
return res, j + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
j = j + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
decode_error(str, i, "expected closing quote for string")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_number(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local s = str:sub(i, x - 1)
|
||||||
|
local n = tonumber(s)
|
||||||
|
if not n then
|
||||||
|
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||||
|
end
|
||||||
|
return n, x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_literal(str, i)
|
||||||
|
local x = next_char(str, i, delim_chars)
|
||||||
|
local word = str:sub(i, x - 1)
|
||||||
|
if not literals[word] then
|
||||||
|
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||||
|
end
|
||||||
|
return literal_map[word], x
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_array(str, i)
|
||||||
|
local res = {}
|
||||||
|
local n = 1
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local x
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of array?
|
||||||
|
if str:sub(i, i) == "]" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read token
|
||||||
|
x, i = parse(str, i)
|
||||||
|
res[n] = x
|
||||||
|
n = n + 1
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "]" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function parse_object(str, i)
|
||||||
|
local res = {}
|
||||||
|
i = i + 1
|
||||||
|
while 1 do
|
||||||
|
local key, val
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
-- Empty / end of object?
|
||||||
|
if str:sub(i, i) == "}" then
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
-- Read key
|
||||||
|
if str:sub(i, i) ~= '"' then
|
||||||
|
decode_error(str, i, "expected string for key")
|
||||||
|
end
|
||||||
|
key, i = parse(str, i)
|
||||||
|
-- Read ':' delimiter
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
if str:sub(i, i) ~= ":" then
|
||||||
|
decode_error(str, i, "expected ':' after key")
|
||||||
|
end
|
||||||
|
i = next_char(str, i + 1, space_chars, true)
|
||||||
|
-- Read value
|
||||||
|
val, i = parse(str, i)
|
||||||
|
-- Set
|
||||||
|
res[key] = val
|
||||||
|
-- Next token
|
||||||
|
i = next_char(str, i, space_chars, true)
|
||||||
|
local chr = str:sub(i, i)
|
||||||
|
i = i + 1
|
||||||
|
if chr == "}" then break end
|
||||||
|
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||||
|
end
|
||||||
|
return res, i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local char_func_map = {
|
||||||
|
[ '"' ] = parse_string,
|
||||||
|
[ "0" ] = parse_number,
|
||||||
|
[ "1" ] = parse_number,
|
||||||
|
[ "2" ] = parse_number,
|
||||||
|
[ "3" ] = parse_number,
|
||||||
|
[ "4" ] = parse_number,
|
||||||
|
[ "5" ] = parse_number,
|
||||||
|
[ "6" ] = parse_number,
|
||||||
|
[ "7" ] = parse_number,
|
||||||
|
[ "8" ] = parse_number,
|
||||||
|
[ "9" ] = parse_number,
|
||||||
|
[ "-" ] = parse_number,
|
||||||
|
[ "t" ] = parse_literal,
|
||||||
|
[ "f" ] = parse_literal,
|
||||||
|
[ "n" ] = parse_literal,
|
||||||
|
[ "[" ] = parse_array,
|
||||||
|
[ "{" ] = parse_object,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
parse = function(str, idx)
|
||||||
|
local chr = str:sub(idx, idx)
|
||||||
|
local f = char_func_map[chr]
|
||||||
|
if f then
|
||||||
|
return f(str, idx)
|
||||||
|
end
|
||||||
|
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function json.decode(str)
|
||||||
|
if type(str) ~= "string" then
|
||||||
|
error("expected argument of type string, got " .. type(str))
|
||||||
|
end
|
||||||
|
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||||
|
idx = next_char(str, idx, space_chars, true)
|
||||||
|
if idx <= #str then
|
||||||
|
decode_error(str, idx, "trailing garbage")
|
||||||
|
end
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
|
return json
|
||||||
|
|
@ -695,7 +695,9 @@ function tiny_manageEntities(world)
|
||||||
system.modified = true
|
system.modified = true
|
||||||
local tmpEntity = ses[#ses]
|
local tmpEntity = ses[#ses]
|
||||||
ses[index] = tmpEntity
|
ses[index] = tmpEntity
|
||||||
|
if tmpEntity ~= nil then
|
||||||
seis[tmpEntity] = index
|
seis[tmpEntity] = index
|
||||||
|
end
|
||||||
seis[entity] = nil
|
seis[entity] = nil
|
||||||
ses[#ses] = nil
|
ses[#ses] = nil
|
||||||
local onRemove = system.onRemove
|
local onRemove = system.onRemove
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
../ext/xrfragments/url.lua
|
|
||||||
167
xurfer/lib/url.lua
Normal file
167
xurfer/lib/url.lua
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
-- Copyright 2026 Leon van Kammen / coderofsalvation. All rights reserved.
|
||||||
|
--
|
||||||
|
-- Redistribution and use in source and binary forms, with or without modification, are
|
||||||
|
-- permitted provided that the following conditions are met:
|
||||||
|
--
|
||||||
|
-- 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
|
-- conditions and the following disclaimer.
|
||||||
|
--
|
||||||
|
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
|
-- of conditions and the following disclaimer in the documentation and/or other materials
|
||||||
|
-- provided with the distribution.
|
||||||
|
--
|
||||||
|
-- THIS SOFTWARE IS PROVIDED BY AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR
|
||||||
|
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
-- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
--
|
||||||
|
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
--
|
||||||
|
-- The views and conclusions contained in the software and documentation are those of the
|
||||||
|
-- authors and should not be interpreted as representing official policies, either expressed
|
||||||
|
-- or implied, of
|
||||||
|
|
||||||
|
-- below is a lua adaptation of Godot's URI.gd
|
||||||
|
-- https://gist.github.com/coderofsalvation/b2b111a2631fbdc8e76d6cab3bea8f17
|
||||||
|
|
||||||
|
local url = {}
|
||||||
|
|
||||||
|
-- Helper function to split a string by a delimiter
|
||||||
|
local function split(str, delimiter)
|
||||||
|
local result = {}
|
||||||
|
if delimiter == "" then return {str} end
|
||||||
|
local pattern = string.format("([^%s]+)", delimiter)
|
||||||
|
for match in string.gmatch(str, pattern) do
|
||||||
|
table.insert(result, match)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper for guess_type (converts numbers/booleans from strings)
|
||||||
|
local function guess_type(val)
|
||||||
|
if tonumber(val) then return tonumber(val) end
|
||||||
|
if val == "true" then return true end
|
||||||
|
if val == "false" then return false end
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Helper to parse query parameters and fragments
|
||||||
|
local function parseArgs(fragment)
|
||||||
|
local ARG = {}
|
||||||
|
if not fragment or fragment == "" then return ARG end
|
||||||
|
|
||||||
|
local items = split(fragment, "&")
|
||||||
|
for _, item in ipairs(items) do
|
||||||
|
local key_value = split(item, "=")
|
||||||
|
if #key_value > 1 then
|
||||||
|
local k = key_value[1]
|
||||||
|
ARG[k] = guess_type(key_value[2]) -- xr fragment operators preserved
|
||||||
|
ARG[k:gsub("[^%w]","") ] = guess_type(key_value[2]) -- alphanumeric only
|
||||||
|
elseif #key_value == 1 then
|
||||||
|
ARG[key_value[1]] = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ARG
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Main URL Parser function attached to the module object
|
||||||
|
function url.parse(url_string)
|
||||||
|
if type(url_string) == 'table' then return url_string end -- already parsed
|
||||||
|
-- Setup initial URI table structure
|
||||||
|
local URI = {
|
||||||
|
domain = "",
|
||||||
|
fragment = {},
|
||||||
|
file = "",
|
||||||
|
extension = "",
|
||||||
|
string = url_string,
|
||||||
|
protocol = "",
|
||||||
|
path = "",
|
||||||
|
query = {},
|
||||||
|
hash = "",
|
||||||
|
URN = "",
|
||||||
|
isLocal = false
|
||||||
|
}
|
||||||
|
local remaining = url_string
|
||||||
|
local protocol = string.match(remaining, "^(%w+://)") -- protocol
|
||||||
|
if protocol then
|
||||||
|
URI.protocol = protocol:gsub("://", "")
|
||||||
|
remaining = remaining:sub(#protocol + 1)
|
||||||
|
else
|
||||||
|
URI.protocol = "file"
|
||||||
|
end
|
||||||
|
local hash = string.match(remaining, "(#.*)$") -- fragment
|
||||||
|
if hash then
|
||||||
|
URI.hash = hash
|
||||||
|
remaining = remaining:sub(1, #remaining - #hash)
|
||||||
|
end
|
||||||
|
local query = string.match(remaining, "(%?[^#]+)$")
|
||||||
|
if query then
|
||||||
|
URI.query = query
|
||||||
|
remaining = remaining:sub(1, #remaining - #query)
|
||||||
|
end
|
||||||
|
URI.path = remaining
|
||||||
|
-- Process Path, Domain, and File
|
||||||
|
if URI.path and URI.path ~= "" then
|
||||||
|
local pathParts = split(URI.path, "/")
|
||||||
|
|
||||||
|
if #pathParts > 1 then
|
||||||
|
local firstPart = pathParts[1]
|
||||||
|
-- Check if the first part looks like a domain (contains '.' or ':')
|
||||||
|
if string.find(firstPart, "%.") or string.find(firstPart, ":") then
|
||||||
|
URI.domain = table.remove(pathParts, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Reconstruct path
|
||||||
|
URI.path = table.concat(pathParts, "/")
|
||||||
|
-- Extract file if present
|
||||||
|
if #pathParts > 0 then
|
||||||
|
local lastPart = pathParts[#pathParts]
|
||||||
|
if string.find(lastPart, "%.") then
|
||||||
|
URI.file = lastPart
|
||||||
|
URI.extension = URI.file:match("%.([^%.]+)$"):upper()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if URI.hash and URI.hash ~= "" then
|
||||||
|
URI.fragment = parseArgs(URI.hash:sub(2)) -- sub(2) skips the '#'
|
||||||
|
else
|
||||||
|
URI.fragment = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if URI.query and type(URI.query) == "string" and URI.query ~= "" then
|
||||||
|
URI.query = parseArgs(URI.query:sub(2)) -- sub(2) skips the '?'
|
||||||
|
else
|
||||||
|
URI.query = {}
|
||||||
|
end
|
||||||
|
if URI.domain ~= "" then
|
||||||
|
URI.URN = URI.string:gsub("%?.*", "")
|
||||||
|
else
|
||||||
|
URI.URN = ""
|
||||||
|
end
|
||||||
|
URI.isLocal = (URI.domain == "")
|
||||||
|
return URI
|
||||||
|
end
|
||||||
|
|
||||||
|
url.getAbsolute = function(href,referer,keephash)
|
||||||
|
local URL = url.parse(href)
|
||||||
|
local URLref = url.parse(referer)
|
||||||
|
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
|
||||||
|
else
|
||||||
|
URL.URN = URL.string:gsub("#.*", "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
URL.referer = referer
|
||||||
|
return URL
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return the module table so it can be assigned via require()
|
||||||
|
return url
|
||||||
|
|
@ -167,3 +167,8 @@ function newNode(name)
|
||||||
return node
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
newNode = newNode,
|
||||||
|
newParser = newParser
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@
|
||||||
-- ./lovr xurfer https://janusxr.org/index.html'
|
-- ./lovr xurfer https://janusxr.org/index.html'
|
||||||
-- ./lovr xurfer https://snips.sh/f/rHFLg-cewi?r=1' -- cube
|
-- ./lovr xurfer https://snips.sh/f/rHFLg-cewi?r=1' -- cube
|
||||||
-- ./lovr xurfer https://snips.sh/f/_U5-XctEVE?r=1' -- cube, monkey, scene
|
-- ./lovr xurfer https://snips.sh/f/_U5-XctEVE?r=1' -- cube, monkey, scene
|
||||||
|
--
|
||||||
print("\n X U R F Ξ R")
|
print("\n X U R F Ξ R")
|
||||||
print(" ⠭ ⠥ ⠗ ⠋ ⠑ ⠗\n")
|
print(" ⠭ ⠥ ⠗ ⠋ ⠑ ⠗\n")
|
||||||
|
|
||||||
package.path = package.path .. ';' .. '?.lua;'
|
package.path = package.path .. ';' .. '?.lua'
|
||||||
require('conf')
|
require('conf')
|
||||||
|
|
||||||
local runtime = require("runtime/detect")
|
local runtime = require("runtime/detect")
|
||||||
|
|
@ -31,20 +31,21 @@ api = {
|
||||||
ecs = ecs,
|
ecs = ecs,
|
||||||
world = ecs.world(),
|
world = ecs.world(),
|
||||||
protocol = {},
|
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
|
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
|
exec = function(...) util.exec(api.ext,...) end -- util function to call func on each extension
|
||||||
}
|
},
|
||||||
|
i = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
api = util.merge( api, runtime.api ) -- overlay api onto lovr/love-compatible runtime api
|
api = util.merge( api, runtime.api ) -- overlay api onto lovr/love-compatible runtime api
|
||||||
|
|
||||||
|
util.loaddir( "ext", api, api.ext )
|
||||||
require( runtime.path .. "/main")
|
require( runtime.path .. "/main")
|
||||||
|
|
||||||
util.loaddir( "ext", api, api.ext )
|
api.ext.exec('init', api)
|
||||||
util.loaddir( "media", api, api.media )
|
|
||||||
|
|
||||||
ecs.init(api)
|
|
||||||
api.ext.exec('init')
|
|
||||||
|
|
||||||
-- load urls passed on the cli
|
-- load urls passed on the cli
|
||||||
foreach( arg, function(k,uri)
|
foreach( arg, function(k,uri)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,31 @@
|
||||||
|
local runtime = require("runtime/detect")
|
||||||
|
-- sync lovr/love positional cli arguments
|
||||||
|
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
|
||||||
|
|
||||||
|
local runtime = require("runtime/detect")
|
||||||
|
|
||||||
|
-- setup path
|
||||||
|
print("!!!!!!!")
|
||||||
|
package.path = package.path ..
|
||||||
|
'runtime/lovr/?.lua;' ..
|
||||||
|
'runtime/lovr/?/init.lua;' ..
|
||||||
|
'lib/?.lua;' ..
|
||||||
|
'?.lua'
|
||||||
|
--arg[0] .. 'runtime/lovr/?.lua;' ..
|
||||||
|
--arg[0] .. 'runtime/lovr/?/init.lua;' ..
|
||||||
|
--arg[0] .. 'lib/?.lua;' ..
|
||||||
|
lovr.filesystem.setRequirePath(
|
||||||
|
lovr.filesystem.getRequirePath() .. ";" ..
|
||||||
|
'runtime/lovr/?.lua;' ..
|
||||||
|
'runtime/lovr/?/init.lua;' ..
|
||||||
|
'lib/?.lua;' ..
|
||||||
|
'?.lua'
|
||||||
|
)
|
||||||
|
|
||||||
|
print(lovr.filesystem.getRequirePath())
|
||||||
|
|
||||||
local util = require("util")
|
local util = require("util")
|
||||||
local launch = require "launch"
|
local launch = require "runtime/lovr/launch"
|
||||||
|
|
||||||
local function isVR()
|
local function isVR()
|
||||||
return launch.mode == "vr" or util.match(arg,"--desktop") == false
|
return launch.mode == "vr" or util.match(arg,"--desktop") == false
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ end
|
||||||
local util = require "util"
|
local util = require "util"
|
||||||
local launch = require "launch"
|
local launch = require "launch"
|
||||||
|
|
||||||
|
require('shaders').create(api)
|
||||||
|
|
||||||
local iui = require "lib.iui"
|
local iui = require "lib.iui"
|
||||||
local backend = require "lib.lovr-iui"
|
local backend = require "lib.lovr-iui"
|
||||||
|
|
||||||
|
|
@ -26,9 +28,30 @@ local ecs = api.ecs
|
||||||
ecs.filterDraw = ecs.requireAll('drawthread')
|
ecs.filterDraw = ecs.requireAll('drawthread')
|
||||||
ecs.filterUpdate = ecs.requireAll('updatethread')
|
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
|
-- ecs systems
|
||||||
local interactionsUpdater
|
local interactions
|
||||||
|
|
||||||
--- @type Texture
|
--- @type Texture
|
||||||
local envTex
|
local envTex
|
||||||
|
|
@ -38,8 +61,7 @@ local mainWindow
|
||||||
|
|
||||||
function lovr.load()
|
function lovr.load()
|
||||||
if launch.mode == "desktop" then
|
if launch.mode == "desktop" then
|
||||||
backend.mouse = require "lib.lovr-mouse"
|
--backend.mouse = require "lib.lovr-mouse"
|
||||||
|
|
||||||
lovr.system.setKeyRepeat(true)
|
lovr.system.setKeyRepeat(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -48,7 +70,10 @@ function lovr.load()
|
||||||
api.ext.exec("load")
|
api.ext.exec("load")
|
||||||
|
|
||||||
if iui.idiom == "vr" then
|
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()
|
mainWindow = backend.worldWindow.new()
|
||||||
api.mainWindow = mainWindow
|
api.mainWindow = mainWindow
|
||||||
|
|
@ -64,7 +89,7 @@ function lovr.update(dt)
|
||||||
-- In desktop mode, we use IUI's standard window API to fill the screen.
|
-- In desktop mode, we use IUI's standard window API to fill the screen.
|
||||||
iui.beginWindow(lovr.system.getWindowDimensions())
|
iui.beginWindow(lovr.system.getWindowDimensions())
|
||||||
|
|
||||||
api.ext.exec("update", dt)
|
api.ext.exec("update", dt, interactions.event )
|
||||||
|
|
||||||
iui.endWindow()
|
iui.endWindow()
|
||||||
|
|
||||||
|
|
@ -74,7 +99,7 @@ function lovr.update(dt)
|
||||||
if mainWindow:beginFrame() then
|
if mainWindow:beginFrame() then
|
||||||
iui.beginWindow(mainWindow.w, mainWindow.h)
|
iui.beginWindow(mainWindow.w, mainWindow.h)
|
||||||
|
|
||||||
api.ext.exec("update", dt)
|
api.ext.exec("update", dt, interactions.event )
|
||||||
|
|
||||||
iui.endWindow()
|
iui.endWindow()
|
||||||
|
|
||||||
|
|
@ -86,120 +111,152 @@ function lovr.update(dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
function lovr.draw(pass)
|
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
|
if iui.idiom == "desktop" then
|
||||||
backend.graphics.pass = pass
|
backend.graphics.pass = pass
|
||||||
|
|
||||||
iui.draw()
|
iui.draw()
|
||||||
end
|
end
|
||||||
if api.iui.idiom == "vr" then
|
if api.iui.idiom == "vr" then
|
||||||
pass:setClear(0.5, 0.5, 0.5)
|
pass:setClear(0.2, 0.2, 0.2)
|
||||||
end
|
end
|
||||||
|
|
||||||
api.ext.exec("draw",pass)
|
-- call draw() function of extensions (sorted by .index)
|
||||||
ecs.update( api.world, pass, ecs.filterDraw )
|
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 pointer
|
-- -- Dot
|
||||||
local hand = vec3(lovr.headset.getPosition('hand/left/point'))
|
--if interactions.selectedBox then
|
||||||
local direction = quat(lovr.headset.getOrientation('hand/left/point')):direction()
|
-- pass:setColor(0, 0, 1)
|
||||||
pass:setColor(1, 1, 1)
|
-- pass:sphere( interactions.hitpoint, .01)
|
||||||
pass:line(hand, interactionsUpdater.selectedBox and interactionsUpdater.hitpoint or (hand + direction * 50))
|
--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
|
--if api.mainWindow then
|
||||||
api.mainWindow:draw(pass)
|
-- api.mainWindow:draw(pass)
|
||||||
end
|
--end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function lovr.recenter()
|
function lovr.recenter()
|
||||||
if iui.idiom == "vr" then
|
if iui.idiom == "vr" and lovr.headset.isActive() then
|
||||||
mainWindow:recenter()
|
mainWindow:recenter()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if launch.mode == "desktop" then
|
|
||||||
function lovr.mousemoved(x, y, dx, dy)
|
function lovr.mousemoved(x, y, dx, dy)
|
||||||
backend.mousemoved(x, y, dx, dy)
|
backend.mousemoved(x, y, dx, dy)
|
||||||
interactionsUpdater.mouse.moved = {x=x, y=y}
|
interactions.pointer.moved = {x=x, y=y}
|
||||||
end
|
end
|
||||||
|
|
||||||
function lovr.mousepressed(x, y, button)
|
function lovr.mousepressed(x, y, button)
|
||||||
backend.mousepressed(x, y, button)
|
backend.mousepressed(x, y, button)
|
||||||
interactionsUpdater.mouse.pressed = {x=x, y=y, button=button}
|
interactions.pointer.pressed = {x=x, y=y, button=button}
|
||||||
end
|
end
|
||||||
|
|
||||||
function lovr.mousereleased(x, y, button)
|
function lovr.mousereleased(x, y, button)
|
||||||
backend.mousereleased(x, y, button)
|
backend.mousereleased(x, y, button)
|
||||||
interactionsUpdater.mouse.released = {x=x, y=y, button=button}
|
interactions.pointer.released = {x=x, y=y, button=button}
|
||||||
print("mouserelease")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function lovr.wheelmoved(x, y)
|
function lovr.wheelmoved(x, y)
|
||||||
backend.wheelmoved(x, y)
|
backend.wheelmoved(x, y)
|
||||||
interactionsUpdater.mouse.wheel = {x=x, y=y}
|
interactions.pointer.wheel = {x=x, y=y}
|
||||||
|
print_r(interactions.pointer.wheel)
|
||||||
end
|
end
|
||||||
|
|
||||||
function lovr.keypressed(key, scancode, isRepeat)
|
function lovr.keypressed(key, scancode, isRepeat)
|
||||||
backend.keypressed(key, scancode, isRepeat)
|
backend.keypressed(key, scancode, isRepeat)
|
||||||
interactionsUpdater.key.pressed = {key=key,scancode=scancode,isRepeat=isRepeat}
|
interactions.key.pressed = {key=key,scancode=scancode,isRepeat=isRepeat}
|
||||||
end
|
end
|
||||||
|
|
||||||
function lovr.keyreleased(key, scancode)
|
function lovr.keyreleased(key, scancode)
|
||||||
backend.keyreleased(key, scancode)
|
backend.keyreleased(key, scancode)
|
||||||
interactionsUpdater.key.released = {key=key,scancode=scancode}
|
interactions.key.released = {key=key,scancode=scancode}
|
||||||
end
|
end
|
||||||
|
|
||||||
function lovr.textinput(text)
|
function lovr.textinput(text)
|
||||||
backend.textinput(text)
|
backend.textinput(text)
|
||||||
interactionsUpdater.input = text
|
interactions.input = text
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function initInteractions(ecs)
|
function initInteractions(ecs)
|
||||||
|
|
||||||
ecs.worldPhysics = lovr.physics.newWorld(0, 0, 0)
|
ecs.worldPhysics = lovr.physics.newWorld(0, 0, 0)
|
||||||
interactionsUpdater = ecs.processingSystem()
|
interactions = ecs.processingSystem()
|
||||||
interactionsUpdater.updatethread = true
|
interactions.updatethread = true
|
||||||
interactionsUpdater.filter = function(obj) return true end -- always run
|
interactions.filter = function(obj) return true end -- always run
|
||||||
-- collider vars
|
-- collider vars
|
||||||
interactionsUpdater.mouse = { released = false, pressed = false, moved = false, wheel = false}
|
interactions.pointer = { released = false, pressed = false, moved = false, hover = false, wheel = false}
|
||||||
interactionsUpdater.key = { released = false, pressed = false }
|
interactions.key = { released = false, pressed = false }
|
||||||
interactionsUpdater.input = ""
|
interactions.input = ""
|
||||||
interactionsUpdater.hitpoint = lovr.math.newVec3()
|
interactions.hitpoint = vector()
|
||||||
interactionsUpdater.selectedBox = nil
|
interactions.selectedBox = nil
|
||||||
interactionsUpdater.clear = function(o) foreach( o, function(k,v) o[k] = false end ) end
|
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)
|
ecs.worldPhysics:update(dt)
|
||||||
|
|
||||||
local ox, oy, oz = lovr.headset.getPosition('hand/left/point')
|
local castsrc = 'hand/left/point'
|
||||||
local dx, dy, dz = quat(lovr.headset.getOrientation('hand/left/point')):direction():mul(50):unpack()
|
local castsrc = 'head'
|
||||||
|
|
||||||
|
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()
|
||||||
local collider, shape, x, y, z = ecs.worldPhysics:raycast(ox, oy, oz, ox + dx, oy + dy, oz + dz)
|
local collider, shape, x, y, z = ecs.worldPhysics:raycast(ox, oy, oz, ox + dx, oy + dy, oz + dz)
|
||||||
|
|
||||||
if collider then
|
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
|
for i, hand in ipairs(lovr.headset.getHands()) do
|
||||||
if lovr.headset.isDown(hand, 'trigger') then
|
if lovr.headset.isDown(hand, 'trigger') then
|
||||||
print("buttondown")
|
interactions.pointer.pressed = true
|
||||||
end
|
end
|
||||||
if lovr.headset.wasReleased(hand, 'trigger') or interactionsUpdater['mouse']['released'] then
|
if lovr.headset.wasReleased(hand, 'trigger') then
|
||||||
local node = collider:getUserData()
|
interactions.pointer.released = true
|
||||||
interactionsUpdater.selectedBox = collider
|
end
|
||||||
interactionsUpdater.hitpoint:set(x, y, z)
|
end
|
||||||
if node.onclick ~= nil then node.onclick(node,collider) end
|
if interactions.pointer.released and node.onclick ~= nil then
|
||||||
|
node.onclick(node,collider)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- reset stuff
|
-- backup & reset stuff
|
||||||
interactionsUpdater.clear( interactionsUpdater.mouse )
|
interactions.event = {
|
||||||
interactionsUpdater.clear( interactionsUpdater.key )
|
input = util.clone(interactions.input),
|
||||||
interactionsUpdater.input = false
|
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
|
end
|
||||||
ecs.addSystem( api.world, interactionsUpdater )
|
ecs.addSystem( api.world, interactions )
|
||||||
end
|
end
|
||||||
|
|
||||||
function initCleanups(ecs)
|
function initCleanups(ecs)
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ modelRenderer = {
|
||||||
init = function(api)
|
init = function(api)
|
||||||
local ecs = api.ecs
|
local ecs = api.ecs
|
||||||
api.world.shader = { pbr = false }
|
api.world.shader = { pbr = false }
|
||||||
lovr.shader = lovr.shader or {}
|
|
||||||
lovr.shader['pbr'] = modelRenderer.initShaderPBR()
|
|
||||||
|
|
||||||
renderer = ecs.processingSystem()
|
renderer = ecs.processingSystem()
|
||||||
renderer.filter = ecs.requireAll('model')
|
renderer.filter = ecs.requireAll('model')
|
||||||
|
|
@ -36,38 +34,6 @@ modelRenderer = {
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ecs.addSystem( api.world, renderer )
|
ecs.addSystem( api.world, renderer )
|
||||||
end,
|
|
||||||
|
|
||||||
initShaderPBR = function()
|
|
||||||
return lovr.graphics.newShader([[
|
|
||||||
vec4 lovrmain() {
|
|
||||||
return DefaultPosition;
|
|
||||||
}
|
|
||||||
]], [[
|
|
||||||
uniform textureCube cubemap;
|
|
||||||
uniform sphericalHarmonics { vec3 sh[9]; };
|
|
||||||
|
|
||||||
vec4 lovrmain() {
|
|
||||||
Surface surface;
|
|
||||||
initSurface(surface);
|
|
||||||
|
|
||||||
vec3 color = vec3(0);
|
|
||||||
vec3 lightDirection = vec3(-1, -1, -1);
|
|
||||||
vec4 lightColorAndBrightness = vec4(1, 1, 1, 3);
|
|
||||||
float visibility = 1.;
|
|
||||||
color += getLighting(surface, lightDirection, lightColorAndBrightness, visibility);
|
|
||||||
color += getIndirectLighting(surface, cubemap, sh);
|
|
||||||
|
|
||||||
return vec4(color, 1);
|
|
||||||
}
|
|
||||||
]], {
|
|
||||||
flags = {
|
|
||||||
glow = true,
|
|
||||||
normalMap = true,
|
|
||||||
vertexTangents = false, -- DamagedHelmet doesn't have vertex tangents
|
|
||||||
tonemap = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
34
xurfer/runtime/lovr/shaders.lua
Normal file
34
xurfer/runtime/lovr/shaders.lua
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
return {
|
||||||
|
create = function(api)
|
||||||
|
lovr.shader = {}
|
||||||
|
lovr.shader.pbr = lovr.graphics.newShader([[
|
||||||
|
vec4 lovrmain() {
|
||||||
|
return DefaultPosition;
|
||||||
|
}
|
||||||
|
]], [[
|
||||||
|
uniform textureCube cubemap;
|
||||||
|
uniform sphericalHarmonics { vec3 sh[9]; };
|
||||||
|
|
||||||
|
vec4 lovrmain() {
|
||||||
|
Surface surface;
|
||||||
|
initSurface(surface);
|
||||||
|
|
||||||
|
vec3 color = vec3(0);
|
||||||
|
vec3 lightDirection = vec3(-1, -1, -1);
|
||||||
|
vec4 lightColorAndBrightness = vec4(1, 1, 1, 3);
|
||||||
|
float visibility = 1.;
|
||||||
|
color += getLighting(surface, lightDirection, lightColorAndBrightness, visibility);
|
||||||
|
color += getIndirectLighting(surface, cubemap, sh);
|
||||||
|
|
||||||
|
return vec4(color, 1);
|
||||||
|
}
|
||||||
|
]], {
|
||||||
|
flags = {
|
||||||
|
glow = true,
|
||||||
|
normalMap = true,
|
||||||
|
vertexTangents = false, -- DamagedHelmet doesn't have vertex tangents
|
||||||
|
tonemap = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
@ -11,13 +11,6 @@ api.ext.lua = {
|
||||||
api.ecs.filterUpdate = api.ecs.requireAll('updatethread')
|
api.ecs.filterUpdate = api.ecs.requireAll('updatethread')
|
||||||
api.ecs.filterDraw = api.ecs.requireAll('drawthread')
|
api.ecs.filterDraw = api.ecs.requireAll('drawthread')
|
||||||
|
|
||||||
api.protocol.http.request( "https://2wa.isvery.ninja", {}, function(status,data,headers)
|
|
||||||
print("request done")
|
|
||||||
--print_r(status)
|
|
||||||
--print_r(headers)
|
|
||||||
--print_r(data)
|
|
||||||
end)
|
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local current_time = os.clock()
|
local current_time = os.clock()
|
||||||
local dt = current_time - last_time
|
local dt = current_time - last_time
|
||||||
|
|
@ -38,11 +31,11 @@ api.ext.lua = {
|
||||||
-- via wget
|
-- via wget
|
||||||
api.protocol.http.request = api.ext.unixy.proxyCLI(
|
api.protocol.http.request = api.ext.unixy.proxyCLI(
|
||||||
"wget",
|
"wget",
|
||||||
function(url,opts, cb) return string.format( "out=$(mktemp); wget -O $out https://2wa.isvery.ninja; cat $out; rm $out", url ) end,
|
function(url,opts, cb) return string.format( "out=$(mktemp); wget -O $out %s; cat $out; rm $out", url ) end,
|
||||||
function( stdout, stderr, retcode, url, opts, cb)
|
function( stdout, stderr, retcode, url, opts, cb)
|
||||||
local status = 200
|
local status = 200
|
||||||
if retcode ~= 0 then status = retcode end
|
if retcode ~= 'exit' then status = 500 end
|
||||||
cb( retcode, stdout, {string = stderr}) -- todo: parse string into key/values
|
cb( status, stdout, {string = stderr, retcode = retcode})
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -52,8 +45,8 @@ api.ext.lua = {
|
||||||
function(url,opts, cb) return string.format( "curl -v -s %s", url ) end,
|
function(url,opts, cb) return string.format( "curl -v -s %s", url ) end,
|
||||||
function( stdout, stderr, retcode, url, opts, cb)
|
function( stdout, stderr, retcode, url, opts, cb)
|
||||||
local status = 200
|
local status = 200
|
||||||
if retcode ~= 0 then status = retcode end
|
if retcode ~= 'exit' then status = 500 end
|
||||||
cb( retcode, stdout, {string = stderr}) -- todo: parse string into key/values
|
cb( status, stdout, {string = stderr, retcode = retcode})
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
1
xurfer/test.localhost.jml0
Symbolic link
1
xurfer/test.localhost.jml0
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../test.localhost.jml0
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
local util = { ext = {} }
|
local util = { ext = {} }
|
||||||
|
|
||||||
|
util.readfile = function(filename, mode)
|
||||||
|
local file, err = io.open(filename, mode or "r")
|
||||||
|
if not file then return print("Error opening file:", err) end
|
||||||
|
local content = file:read("*a")
|
||||||
|
file:close()
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
util.loaddir = function( path, api, target)
|
util.loaddir = function( path, api, target)
|
||||||
local dir = api.filesystem.getDirectoryItems(path)
|
local dir = api.filesystem.getDirectoryItems(path)
|
||||||
foreach( dir, function(k,v)
|
foreach( dir, function(k,v)
|
||||||
|
|
@ -7,10 +15,26 @@ util.loaddir = function( path, api, target)
|
||||||
if api.filesystem.isFile(ext) then
|
if api.filesystem.isFile(ext) then
|
||||||
print("[i] loading '" .. v .. "'")
|
print("[i] loading '" .. v .. "'")
|
||||||
target[ v ] = api.filesystem.load( ext )(api)
|
target[ v ] = api.filesystem.load( ext )(api)
|
||||||
|
if target[ v ].index == nil then target[v].index = api.ext.max / 2 end
|
||||||
end
|
end
|
||||||
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)
|
util.cmdExist = function(cmd)
|
||||||
if api.runtime.os == 'unixy' then cmd = cmd .. " 1>/dev/null 2>/dev/null" end
|
if api.runtime.os == 'unixy' then cmd = cmd .. " 1>/dev/null 2>/dev/null" end
|
||||||
local stdout, retstr, retcode = os.execute(cmd)
|
local stdout, retstr, retcode = os.execute(cmd)
|
||||||
|
|
@ -153,8 +177,8 @@ end
|
||||||
-- entity.commit() -- this recalculates the filtercache of systems
|
-- entity.commit() -- this recalculates the filtercache of systems
|
||||||
util.commit = function(world, obj, api)
|
util.commit = function(world, obj, api)
|
||||||
return function(extCb)
|
return function(extCb)
|
||||||
for j = 1, #world.entities do
|
for i = 1, #world.systems do
|
||||||
local system = world.systems[j]
|
local system = world.systems[i]
|
||||||
system.entities = {}
|
system.entities = {}
|
||||||
for j = 1, #world.entities do
|
for j = 1, #world.entities do
|
||||||
local entity = world.entities[j]
|
local entity = world.entities[j]
|
||||||
|
|
@ -190,9 +214,9 @@ end
|
||||||
-- LUA SYNTAX SUGAR (from here on )
|
-- LUA SYNTAX SUGAR (from here on )
|
||||||
--
|
--
|
||||||
|
|
||||||
function foreach(table, f)
|
function foreach(t, f, sort)
|
||||||
if table ~= nil then
|
if t ~= nil then
|
||||||
for k, v in pairs(table) do f(k, v) end
|
for k, v in pairs(t) do f(k, v) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue