make-mesh

el.make.box({ y: 1, color: '#c33', glow: 0.3 }) — Babylon primitives with the four things everyone forgets already done.

Demo

import { b3d, b3dSkybox } from 'tosijs-3d'
import { demoStage, orbitCam } from 'tosijs-3d/demo-utils'

const scene = b3d(
  {
    glowLayerIntensity: 0.7,
    sceneCreated(el) {
      orbitCam(el, { radius: 9, target: [0, 1, 0] })
      // Every one of these casts a shadow and is ray-ready immediately.
      el.make.box({ size: 1.2, x: -2.4, y: 0.9, color: '#b8483a', glow: 0.25 })
      el.make.sphere({ diameter: 1.3, y: 1, color: '#3f7fb8' })
      el.make.cylinder({ height: 1.8, diameter: 0.9, x: 2.4, y: 0.9, color: '#c9a227', glow: 0.15 })
      el.make.torus({ diameter: 1.4, thickness: 0.28, y: 2.6, rx: 90, color: '#6ab04c' })
      // Never hand-written here — the proxy forwards to MeshBuilder.CreateIcoSphere.
      el.make.icoSphere({ radius: 0.7, subdivisions: 2, x: -2.4, y: 2.6, color: '#b07acc', glow: 0.3 })
      el.make.torusKnot({ radius: 0.5, tube: 0.16, x: 2.4, y: 2.7, color: '#e08a3c', glow: 0.2 })
    },
  },
  ...demoStage({ size: 16, tiles: 10, texture: '/tosi-warhol-testgrid.svg' }),
)

preview.append(scene)

b3dBox/b3dSphere/b3dGround cover the DECLARATIVE case. But plenty of scene code is imperative — inside sceneCreated, in a spawner, in a demo — and there the honest version of "add a cube" is:

const box = BABYLON.MeshBuilder.CreateBox('box', { size: 1 }, el.scene)
box.position.set(0, 1, 0)
const mat = new BABYLON.StandardMaterial('box-mat', el.scene)
mat.diffuseColor = BABYLON.Color3.FromHexString('#c33')
box.material = mat
el.register({ meshes: [box] }) // or it casts no shadow
box.computeWorldMatrix(true) // or a ray this frame misses it

Six lines, of which the last two are invisible until something is subtly wrong. This is the same shape b3d-library grew for content (lib.make.scout()), and tosijs's elementCreator before that: the thing you want, named, taking the options you'd have set anyway.

What it does for you

why it matters
material from color/glow/glowColor the same primitiveMaterial b3dBox uses, so the two paths cannot drift
register() with the owner shadow casting, reflection probes — the scene-listener contract. A mesh nobody registered is a mesh the sun has never heard of
computeWorldMatrix(true) see below — this one is a genuine trap
degrees for rx/ry/rz matches AbstractMesh, which is degrees. Babylon is radians, so a bare number is ambiguous exactly where it hurts

It covers everything MeshBuilder has

make is a Proxy that forwards make.<shape> to MeshBuilder.Create<Shape>, so all 26 are there — icoSphere, torusKnot, polyhedron, tube, lathe, geodesic, goldberg, tiledBox — plus whatever Babylon adds later, with no code here to keep in step. Options you don't recognise are simply forwarded, so a shape's own parameters work exactly as they do in Babylon.

It is weightless: one Proxy, and a closure only for the names actually used. That laziness is the point — a hand-written list of eight would be wrong the moment somebody wanted the ninth.

make.decal is refused with a message, because CreateDecal takes a source mesh rather than options and so doesn't fit the forwarding shape.

Why worldMatrix defaults to true

A mesh that has been positioned but never RENDERED has no world matrix, so anything that reads world space this frame — a ray, a bounding check, an absolutePosition — sees it at the origin. It does not throw; it quietly answers about a phantom copy somewhere else.

That is not hypothetical: it cost a debugging detour in the collision demo's tests. A wall placed at z=6, shot at on the first frame, reported an impact at z=0.25 with a confident normal off the wrong face — the ray had started inside the phantom at the origin and exited its far side. Plausible, wrong, and silent.

Pass worldMatrix: false if you are about to reposition the mesh anyway and want to skip the work.