Scene Graph

The Scene Graph and TransformNode

A Scene keeps flat lists (meshes, lights, cameras) plus a hierarchy of parent links, Scene Graphs's scene graph. The classes climb from Node (name, parent) to TransformNode (position, rotation, scaling), AbstractMesh (bounds, picking) and Mesh (geometry, material). A bare TransformNode is an invisible group, like Three.js 30,815 's Group (The Scene Graph and Object3D). Nothing is drawn, so a NullEngine (NullEngine) will do:

A book parented to a TransformNode shelfJavaScript
const scene = new BABYLON.Scene(new BABYLON.NullEngine());
const shelf = new BABYLON.TransformNode('shelf', scene);
shelf.position.set(0, 1, 2);
shelf.rotation.y = Math.PI / 2;                       // quarter turn about +y
const book = BABYLON.MeshBuilder.CreateBox('book', { width: 0.2, height: 0.9 }, scene);
book.parent = shelf;
book.position.x = 0.5;                                // local to the shelf
book.computeWorldMatrix(true);                        // world matrices update during render
console.log(book.getAbsolutePosition().asArray().map((c) => +c.toFixed(3)));
Output
BJS - [15:43:33]: Babylon.js v9.28.0 - Null engine
[0, 1, 1.5]

The turn sent the local +x offset to world -z, because Babylon 89,603 is left-handed: +x right, +y up, +z into the screen, the opposite of WebGL's and Three.js's convention (MVP Matrices). Set scene.useRightHandedSystem = true to port right-handed code; the glTF loader converts glTF files with a __root__ node (glTF and Containers). rotation holds Euler angles in radians until you assign rotationQuaternion, which then wins (Quaternions vs Euler).

A TransformNode shelf turning its child books, with local and world positionsHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f3ec; font: 13px system-ui, sans-serif; color: #333; }
  canvas { width: 100%; max-width: 600px; height: 340px; display: block; touch-action: none; }
  #info { max-width: 600px; padding: 4px 8px; font-family: monospace; }
</style>
<canvas id="view"></canvas>
<div id="info"></div>
<script src="https://cdn.jsdelivr.net/npm/babylonjs@9.28.0/babylon.js"></script>
<script>
const canvas = document.getElementById('view');
const engine = new BABYLON.Engine(canvas, true);
const scene = new BABYLON.Scene(engine);
scene.clearColor = BABYLON.Color4.FromHexString('#f7f3ecff');
const camera = new BABYLON.ArcRotateCamera('cam', -Math.PI / 2 - 0.6, 1.1, 3.6,
  new BABYLON.Vector3(0, 0.8, 1.2), scene);
camera.attachControl(canvas, true);
new BABYLON.HemisphericLight('sky', new BABYLON.Vector3(0.3, 1, -0.4), scene);

// World axes: Babylon is left-handed, +x right, +y up, +z into the screen
new BABYLON.AxesViewer(scene, 0.8);

// An invisible group node: moving it moves every child
const shelf = new BABYLON.TransformNode('shelf', scene);
shelf.position.set(0, 1, 2);
const board = BABYLON.MeshBuilder.CreateBox('board', { width: 1.6, height: 0.05, depth: 0.35 }, scene);
board.parent = shelf;
board.position.y = -0.47;
const shelfAxes = new BABYLON.AxesViewer(scene, 0.6);   // the shelf's local axes
shelfAxes.xAxis.parent = shelfAxes.yAxis.parent = shelfAxes.zAxis.parent = shelf;

const COLORS = ['#1f5f8b', '#5b3f99', '#e09a10', '#3f7d3a', '#b5452f', '#2a9d8f'];
const books = COLORS.map((hex, i) => {
  const book = BABYLON.MeshBuilder.CreateBox('book' + i, { width: 0.2, height: 0.9, depth: 0.3 }, scene);
  book.parent = shelf;
  book.position.x = -0.6 + i * 0.24;                 // local to the shelf
  book.material = new BABYLON.StandardMaterial('m' + i, scene);
  book.material.diffuseColor = BABYLON.Color3.FromHexString(hex);
  return book;
});
const tracked = books[5];                            // the last book, local x = 0.6
tracked.material.emissiveColor = new BABYLON.Color3(0.25, 0.2, 0);

const info = document.getElementById('info');
const fmt = (v) => v.asArray().map((c) => (c.toFixed(2) === '-0.00' ? '0.00' : c.toFixed(2))).join(', ');
let t = 0;
scene.onBeforeRenderObservable.add(() => {
  t += engine.getDeltaTime() / 1000;
  shelf.rotation.y = (Math.sin(t * 0.8) * 0.5 + 0.5) * Math.PI / 2;   // swings 0 to 90 degrees
  tracked.computeWorldMatrix(true);
  info.innerHTML = `shelf.rotation.y = ${(shelf.rotation.y * 180 / Math.PI).toFixed(0)}&deg;<br>` +
    `teal book local position: (${fmt(tracked.position)})<br>` +
    `teal book world position: (${fmt(tracked.getAbsolutePosition())})`;
});
engine.runRenderLoop(() => scene.render());
addEventListener('resize', () => engine.resize());
</script>