xurfer/src/ext/xrfragments/lovr-xrf.lua

97 lines
3 KiB
Lua
Raw Normal View History

2026-06-07 14:48:52 +02:00
local json = require("json")
local xrf = {}
xrf.traverseNodesContaining = function(key,obj,cb)
local metadata = json.decode( obj['model']:getMetadata() )
-- Blender stores extras in single-object glb's to 'mesh' but multi-object glb's to 'nodes'
-- hence node_or_mesh
local keys = {'nodes','meshes'}
for k, tkey in pairs(keys) do
local i = 1
local tree = metadata[tkey]
xrf.traverse( tree, function(node)
if node['extras'] ~= nil then
if node['extras'][key] ~= nil then
cb(node,obj['model'], i, metadata)
end
end
i = i + 1
end)
end
end
xrf.getNodeFromMesh = function(mesh,metadata, i)
local node
if mesh['mesh'] == nil then
local extras = mesh['extras']
node = metadata['nodes'][i]
node['extras'] = extras
end
return node
end
xrf.calcDimensions = function(bbox)
local w = math.abs( bbox['maxx'] - bbox['minx'] )
local h = math.abs( bbox['maxy'] - bbox['miny'] )
local d = math.abs( bbox['maxz'] - bbox['minz'] )
return w, h, d
end
xrf.calcCenteredWorldPosition = function(node, model, bbox)
-- adjust for meshes which don't have origin in their center
local x, y, z = model:getNodePosition(node['name'], 'root') -- worldcoords
x = x + ( ( bbox['maxx'] + bbox['minx']) /2 )
y = y + ( ( bbox['maxy'] + bbox['miny']) /2 )
z = z + ( ( bbox['maxz'] + bbox['minz']) /2 )
return x, y, z
end
xrf.calcScale = function(node)
return {
node['scale'] and node['scale'][1] or 1,
node['scale'] and node['scale'][2] or 1,
node['scale'] and node['scale'][3] or 1,
}
end
xrf.makeClickable = function( physicsWorld, cb)
return function(node_or_mesh,model, i, metadata)
local node = node_or_mesh
if node['mesh'] == nil then node = xrf.getNodeFromMesh(node_or_mesh, metadata, i) end
print("making node " .. node['name'] .. " clickable")
local meshindex = 1 + node['mesh']
local mesh = model:getMesh( meshindex )
local minx, maxx, miny, maxy, minz, maxz = mesh:getBoundingBox()
local bbox = {maxx=maxx, minx=minx, maxy=maxy, miny=miny, maxz=maxz, minz=minz}
local w, h, d = xrf.calcDimensions( bbox )
local x, y, z = xrf.calcCenteredWorldPosition( node, model, bbox )
local scale = xrf.calcScale(node)
-- we cannot use newMeshCollider because storage type of modelmesh is not cpu
local collider = physicsWorld:newBoxCollider(
x, y, z,
scale[1] * (w + 0.01), -- add 0.01 to avoid 0
scale[2] * (h + 0.01), -- in case of
scale[3] * (d + 0.01) -- planes e.g.
)
collider:setUserData( {name = node['name'], node = node, onclick = cb })
end
end
-- utility function to traverse recursive table
xrf.traverse = function(arr, cb, key)
if key == nil then key = 'children' end
for k, child in pairs(arr) do
if type(child) == 'table' then
cb(child)
if type(child[key]) == 'table' then
if child[key][1] then
xrf.traverse( child[key], cb, key )
end
end
end
end
end
return xrf