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