API

Interface Mesh

A renderable mesh — plain data with transform, material, and GPU geometry. Works with both standard and PBR pipelines; routing is based on material type. Extends SceneNode for the full TRS + parent + children hierarchy.

interface Mesh {
    boundMax?: [number, number, number];
    boundMin?: [number, number, number];
    children: SceneNode[];
    hasVertexAlpha?: boolean;
    id?: string;
    material: Material;
    meshBlendingTag?: number;
    metadata?: LiteMetadata;
    morphTargets?: MorphTargetData | null;
    name: string;
    parent: IWorldMatrixProvider | null;
    pickable?: boolean;
    position: ObservableVec3;
    receiveShadows: boolean;
    renderOnTop?: boolean;
    renderOrder?: number;
    rotation: EulerProxy;
    rotationQuaternion: ObservableQuat;
    scaling: ObservableVec3;
    skeleton?: SkeletonData | null;
    thinInstances?: ThinInstanceData | null;
    vat?: VatData | null;
    visible?: boolean;
    worldMatrix: Mat4;
    worldMatrixVersion: number;
}

Hierarchy (View Summary)

Index
boundMax?: [number, number, number]
boundMin?: [number, number, number]

OBJECT-LOCAL axis-aligned bounding box of this mesh's own geometry — the box the vertex buffer occupies BEFORE worldMatrix, and before any thin-instance matrix. Every reader composes it the same way the shaders do: plain mesh → worldMatrix × corner thin-instanced → worldMatrix × instanceMatrix × corner

Local, not world, because it is the only frame that survives the mesh moving. A Mesh owns a live TRS plus a parent chain, so a world-baked box goes stale the instant anything in that chain changes and there is no way to recover the local box from it. Local also is the only frame in which a thin-instance prototype can be expressed at all: one prototype box is reused under hundreds of instance matrices, so it cannot hold any one instance's world placement. The shadow fit (computeDirectionalLightMatrix, _castersWorldAabb), the GPU thin-instance cull, the Havok shape extents and computeMaxExtents all rely on exactly this.

This used to be documented as world-space, and the glTF loader honoured that by baking the node's world matrix in while leaving the mesh parented under that same node — so every reader that (correctly) applied worldMatrix transformed loaded meshes twice, and CSM cascades mis-fit for any parented glTF caster. Loaders now publish the raw geometry box and let the node chain supply the transform, which is lossless: the mesh's worldMatrix already reproduces that node's world matrix. Consumers that need a loaded model's box in its ROOT frame must compose it with the node world-at-load matrix themselves.

A consumer MAY overwrite these with a wider hand-computed box (e.g. a thin-instance prototype publishing the union of all its placements onto an identity-world mesh) — the contract is only that the box is stated in the frame worldMatrix maps out of.

children: SceneNode[]
hasVertexAlpha?: boolean

Explicit opt-in that this mesh's RGBA vertex or thin-instance colours drive translucency (Babylon AbstractMesh.hasVertexAlpha). When true and the mesh carries either colour source, the Standard forward path treats it as alpha-blended: source-over blending, depth-write disabled, and sorted into the transparent phase. The geometry path applies the same behavior for vertex colours. Defaults to false/opaque. Set this explicitly (or via a loader that knows the vertex-colour accessor is VEC4); Lite never scans buffers to infer it.

id?: string

Unique ID from source file (e.g. .babylon). Used for light include/exclude filtering.

material: Material
meshBlendingTag?: number

Packed mesh-blending tag. Undefined is equivalent to zero/disabled. Bits 0..5 are the group and bits 6..7 are the radius class.

metadata?: LiteMetadata

User metadata. glTF loads populate metadata.gltf.extras when source extras exist.

morphTargets?: MorphTargetData | null

Morph target GPU data. Type-only — no module dependency.

name: string
parent: IWorldMatrixProvider | null
pickable?: boolean

When false, the GPU picker skips this mesh. Defaults to true (undefined behaves as pickable). Mirrors BJS AbstractMesh.isPickable.

position: ObservableVec3
receiveShadows: boolean
renderOnTop?: boolean

On a transmission-enabled render task, draw this transparent mesh LAST — after the transmissive surface and after the scene-colour grab — so it sits on top of the water/glass AND is excluded from what that surface refracts (e.g. lily pads resting on water should not appear in the refraction). Enable transmission on the task with enableRenderTaskTransmission; only transparent surfaces (needAlphaBlending) are deferred. No effect on tasks without transmission.

renderOrder?: number

User-controlled render order. Lower = drawn first within phase. Only affects ordering within the opaque or transparent phase.

rotation: EulerProxy

Euler XYZ bidirectional proxy — reads decompose current quat; writes update quat atomically.

rotationQuaternion: ObservableQuat

Quaternion rotation — source of truth for the local matrix.

skeleton?: SkeletonData | null

Skeleton GPU data (skeletal animation). Type-only — no module dependency.

thinInstances?: ThinInstanceData | null

Thin instance data (CPU-side). GPU buffer managed by render system.

vat?: VatData | null

Baked vertex-animation (VAT) GPU data — replaces live skinning so the mesh thin-instances. Mutually exclusive with live skeleton skinning. Type-only — no module dependency.

visible?: boolean

Self-visibility. Undefined/true = visible; false skips render + camera AABB. Cascade is materialized at write-time by setSubtreeVisible.

worldMatrix: Mat4
worldMatrixVersion: number