b3d-library
Asset library component. Loads a GLB file via LoadAssetContainer and holds
it as a reusable parts catalog — nothing is added to the scene until you call
instantiate(name).
Libraries register with the parent B3d by type, so consumers (like a
tile map) can discover them via owner.getLibrary('tiles') without holding
direct references.
Demo
import { b3d, b3dLibrary, b3dLight, b3dSkybox, b3dGround, placeOnSurface, label3d, list3d, button3d } from 'tosijs-3d'
import { elements } from 'tosijs'
const { div, p } = elements
const lib = b3dLibrary({ url: '/test-2.glb', type: 'scene' })
function isInsertable(node) {
return node.isMesh || node.children.some(c => c.isMesh)
}
// Flatten the GLB hierarchy into one scrollable pick list: every insertable node
// (a mesh, or a group that contains meshes), children indented under their parent.
// Instantiating a group clones it whole; instantiating a leaf clones just that.
function flattenInsertable(nodes, depth = 0, out = []) {
for (const node of nodes) {
if (isInsertable(node)) {
out.push({ label: '- '.repeat(depth) + node.name, name: node.name })
}
if (node.children.length) flattenInsertable(node.children, depth + 1, out)
}
return out
}
const scene = b3d(
{
// Dual-presence picker: the mesh list lives in the ⚙ panel, so you can spawn
// parts from inside VR too. The hook re-reads the hierarchy each time the panel
// is (re)built; refreshScenePanel() below updates an already-open panel once
// the GLB finishes loading.
scenePanel: () => {
const items = flattenInsertable(lib.getHierarchy())
return [
label3d({ text: items.length ? 'Spawn a mesh' : 'Loading…' }),
list3d({
items,
onSelect: (it) => {
const placed = lib.instantiate(it.name)
// Rest the spawn on the ground rather than at the origin (where it may
// float or clip depending on the GLB).
if (placed) placeOnSurface(placed)
},
}),
button3d({ label: 'Clear all', onClick: () => lib.clearInstances() }),
]
},
sceneCreated(el, BABYLON) {
const camera = new BABYLON.ArcRotateCamera(
'cam', -Math.PI / 2, Math.PI / 3, 10,
BABYLON.Vector3.Zero(), el.scene
)
camera.attachControl(el.querySelector('canvas'), true)
el.setActiveCamera(camera)
},
},
b3dLight({ y: 1, intensity: 0.7 }),
b3dSkybox({ timeOfDay: 12 }),
b3dGround({ width: 20, height: 20 }),
lib,
)
// Refresh an already-open panel once the model has loaded (opening it after the
// load already picks up the list via the rebuild-on-open above).
lib.ready.then(() => scene.refreshScenePanel())
preview.append(
scene,
div(
{ class: 'debug-panel' },
p('Open the ⚙ to spawn library meshes — works in VR too.'),
),
)
tosi-b3d { width: 100%; height: 100%; }
.debug-panel {
position: absolute;
top: 10px;
right: 10px;
display: flex;
flex-direction: column;
gap: 8px;
padding: 8px 16px;
background: rgba(0, 0, 0, 0.6);
color: white;
border-radius: 6px;
font-size: 14px;
z-index: 10;
}
.debug-panel select, .debug-panel button {
color: white;
background: #444;
border: 1px solid #888;
border-radius: 4px;
padding: 4px 8px;
font-size: 13px;
}
Attributes
| Attribute | Default | Description |
|---|---|---|
url |
'' |
GLB/glTF file URL |
type |
'' |
Library type for scene registry lookup |
API
ready: Promise<void>— resolves when the GLB has loadedgetNames(): string[]— list all mesh names (excluding__root__and-ignore)getRootNames(): string[]— list only top-level mesh names (direct children of root)getHierarchy(): {name, children, isMesh}[]— recursive tree of all nodes (meshes + transforms) reflecting parent–child structureinstantiate(name, options?): Node | null— clone a named node (mesh or transform, with children) into the sceneclearInstances(): void— dispose all previously instantiated clones- Options:
{ x?, y?, z?, rx?, ry?, rz?, parent? }