Skip to content

VectorPosition and Rotation

BabylonJS - Positioning and Rotating Meshes

Section titled “BabylonJS - Positioning and Rotating Meshes”

Understanding how to position and orient objects in 3D space is fundamental. BabylonJS uses a right-handed coordinate system. By default:

  • X-axis: Points to the right.
  • Y-axis: Points upwards.
  • Z-axis: Points out of the screen, towards you.

We’ll use BABYLON.Vector3 to represent positions and rotations (as Euler angles).

The following code includes a helper function showAxis that draws lines and labels for the X, Y, and Z axes, making it easier to visualize the coordinate system and object placement.

import * as BABYLON from '@babylonjs/core';
import '@babylonjs/gui'; // Needed for DynamicTexture used in showAxis
const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true);
const createScene = async function() {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0.2, 0.2, 0.2, 1.0);
// Camera: Positioned to view the origin
const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2 + 0.5, Math.PI / 2.5, 20, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
camera.wheelPrecision = 50; // Adjust zoom speed
// Light
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.8;
// --- Axis Visualization Function ---
const showAxis = function(size) {
const makeTextPlane = function(text, color, textSize) {
const dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", {
width: text.length * 20 + 20, // Adjust width based on text length
height: 70
}, scene, false);
dynamicTexture.hasAlpha = true;
dynamicTexture.drawText(text, null, null, "bold 36px Arial", color, "transparent", true);
const plane = BABYLON.MeshBuilder.CreatePlane("TextPlane", {size: textSize}, scene);
plane.material = new BABYLON.StandardMaterial("TextPlaneMaterial", scene); // StandardMaterial works well for unlit text
plane.material.backFaceCulling = false;
plane.material.specularColor = new BABYLON.Color3(0, 0, 0);
plane.material.diffuseTexture = dynamicTexture;
plane.material.useAlphaFromDiffuseTexture = true;
return plane;
};
const axisX = BABYLON.MeshBuilder.CreateLines("axisX", {
points: [
new BABYLON.Vector3(-size, 0, 0), new BABYLON.Vector3(size, 0, 0)
]
}, scene);
axisX.color = new BABYLON.Color3(1, 0, 0);
const xChar = makeTextPlane("X", "red", size / 10);
xChar.position = new BABYLON.Vector3(size * 0.9, 0.05 * size, 0);
// Add numbers along X
for (let i = -size; i <= size; i++) {
if (i === 0) continue;
const xNum = makeTextPlane(i.toString(), "red", size / 20);
xNum.position = new BABYLON.Vector3(i, 0.05 * size, 0);
}
const axisY = BABYLON.MeshBuilder.CreateLines("axisY", {
points: [
new BABYLON.Vector3(0, -size, 0), new BABYLON.Vector3(0, size, 0)
]
}, scene);
axisY.color = new BABYLON.Color3(0, 1, 0);
const yChar = makeTextPlane("Y", "green", size / 10);
yChar.position = new BABYLON.Vector3(0, size * 0.9, 0.05 * size);
// Add numbers along Y
for (let i = -size; i <= size; i++) {
if (i === 0) continue;
const yNum = makeTextPlane(i.toString(), "green", size / 20);
yNum.position = new BABYLON.Vector3(0, i, 0.05 * size);
}
const axisZ = BABYLON.MeshBuilder.CreateLines("axisZ", {
points: [
new BABYLON.Vector3(0, 0, -size), new BABYLON.Vector3(0, 0, size)
]
}, scene);
axisZ.color = new BABYLON.Color3(0, 0, 1);
const zChar = makeTextPlane("Z", "blue", size / 10);
zChar.position = new BABYLON.Vector3(0, 0.05 * size, size * 0.9);
// Add numbers along Z
for (let i = -size; i <= size; i++) {
if (i === 0) continue;
const zNum = makeTextPlane(i.toString(), "blue", size / 20);
zNum.position = new BABYLON.Vector3(0, 0.05 * size, i);
// Rotate Z labels to face camera better initially
zNum.rotation.y = Math.PI / 2;
}
};
// --- End Axis Visualization Function ---
// Call the function to draw axes of size 10
showAxis(10);
return scene;
};
createScene().then(scene => {
engine.runRenderLoop(function() {
if (scene) {
scene.render();
}
});
});
window.addEventListener('resize', function() {
engine.resize();
});

This example draws red (X), green (Y), and blue (Z) lines originating from (0,0,0) with labels and number markers. Run this code to get a clear visual reference for the coordinate system.

Note: The makeTextPlane function uses DynamicTexture to draw text onto a plane mesh. For more complex UI or text rendering, consider using the BabylonJS GUI extension (@babylonjs/gui).

Every mesh has a position property, which is a BABYLON.Vector3. You can set its x, y, and z components directly or assign a new Vector3.

// Inside createScene, after showAxis(10);
// --- Positioning Meshes ---
// Create a sphere material
const sphereMat = new BABYLON.PBRMaterial("sphereMat", scene);
sphereMat.albedoColor = new BABYLON.Color3(1, 1, 0); // Yellow
sphereMat.metallic = 0.2;
sphereMat.roughness = 0.5;
// Sphere 1: Set components individually
const sphere1 = BABYLON.MeshBuilder.CreateSphere("sphere1", {diameter: 2}, scene);
sphere1.material = sphereMat;
sphere1.position.x = 5;
sphere1.position.y = 2;
sphere1.position.z = 3;
// Final position: (5, 2, 3)
// Sphere 2: Assign a new Vector3
const sphere2 = BABYLON.MeshBuilder.CreateSphere("sphere2", {diameter: 1.5}, scene);
sphere2.material = sphereMat;
sphere2.position = new BABYLON.Vector3(-4, 1, -2);
// Final position: (-4, 1, -2)
// Sphere 3: Default position is (0, 0, 0)
const sphere3 = BABYLON.MeshBuilder.CreateSphere("sphere3", {diameter: 1}, scene);
sphere3.material = sphereMat;
// Final position: (0, 0, 0)
// --- Moving Meshes (Relative Positioning) ---
// Move sphere1 2 units along the positive X-axis from its current position
sphere1.position.x += 2; // Now at (7, 2, 3)
// Move sphere2 using addInPlace
const moveVector = new BABYLON.Vector3(1, 2, 0);
sphere2.position.addInPlace(moveVector); // Now at (-3, 3, -2)
// Translate sphere3 along its local Z-axis (initially world Z)
// Note: This requires considering rotation if the mesh is rotated.
// For simple translation along world axes, addInPlace or direct modification is simpler.
sphere3.translate(BABYLON.Axis.Z, 5, BABYLON.Space.WORLD); // Now at (0, 0, 5)

Key methods for positioning:

  • mesh.position: Get or set the mesh’s position Vector3 in world space.
  • mesh.position.x / .y / .z: Access individual components.
  • mesh.position.addInPlace(vector): Adds the vector components to the mesh’s current position (modifies mesh.position). Efficient for frequent updates.
  • mesh.translate(axis, distance, space): Moves the mesh along a specified axis (BABYLON.Axis.X/Y/Z) by a distance. space can be BABYLON.Space.LOCAL (relative to mesh’s rotation) or BABYLON.Space.WORLD.

Rotation is often represented using Euler angles (rotations around the X, Y, and Z axes). BabylonJS stores this in the mesh.rotation property (a Vector3) where components represent angles in radians.

Alternatively, quaternions (mesh.rotationQuaternion) are used for more complex or numerically stable rotations, especially when interpolating or avoiding gimbal lock. For basic rotations, Euler angles are often simpler.

Remember: Angles are in Radians! To convert degrees to radians, use the formula: radians = degrees * (Math.PI / 180).

// Inside createScene...
// --- Rotating Meshes ---
// Create a box material
const boxMat = new BABYLON.PBRMaterial("boxMat", scene);
boxMat.albedoColor = new BABYLON.Color3(0.6, 0.6, 1.0); // Light Blue
boxMat.metallic = 0.1;
boxMat.roughness = 0.7;
// Box 1: Rotate 45 degrees around the Y-axis
const box1 = BABYLON.MeshBuilder.CreateBox("box1", {width: 4, height: 1, depth: 2}, scene);
box1.material = boxMat;
box1.position = new BABYLON.Vector3(-5, 0.5, 5);
box1.rotation.y = Math.PI / 4; // 45 degrees in radians
// Box 2: Set multiple rotations
const box2 = BABYLON.MeshBuilder.CreateBox("box2", {size: 2}, scene);
box2.material = boxMat;
box2.position = new BABYLON.Vector3(5, 1, -5);
box2.rotation = new BABYLON.Vector3(
BABYLON.Tools.ToRadians(30), // 30 degrees around X
BABYLON.Tools.ToRadians(0), // 0 degrees around Y
BABYLON.Tools.ToRadians(-45) // -45 degrees around Z
);
// Box 3: Add rotation incrementally (e.g., in render loop)
const box3 = BABYLON.MeshBuilder.CreateBox("box3", {size: 1.5}, scene);
box3.material = boxMat;
box3.position = new BABYLON.Vector3(0, 0.75, 0);
scene.onBeforeRenderObservable.add(() => {
// Rotate box3 around its local Y-axis each frame
box3.rotation.y += 0.01;
// Rotate box1 around world X-axis slowly
box1.rotate(BABYLON.Axis.X, 0.005, BABYLON.Space.WORLD);
});
// Using Quaternions (Alternative)
const box4 = BABYLON.MeshBuilder.CreateBox("box4", {size: 1}, scene);
box4.material = boxMat;
box4.position = new BABYLON.Vector3(3, 0.5, 3);
// Rotate 90 degrees around an arbitrary axis (e.g., normalized vector (1, 1, 0))
const rotationAxis = new BABYLON.Vector3(1, 1, 0).normalize();
const angle = Math.PI / 2; // 90 degrees
box4.rotationQuaternion = BABYLON.Quaternion.RotationAxis(rotationAxis, angle);

Key methods for rotation:

  • mesh.rotation: Get or set the mesh’s Euler rotation Vector3 (in radians).
  • mesh.rotation.x / .y / .z: Access individual rotation components (radians).
  • BABYLON.Tools.ToRadians(degrees): Helper function to convert degrees to radians.
  • mesh.addRotation(x, y, z): Adds the given radian values to the current mesh.rotation components.
  • mesh.rotate(axis, angle, space): Rotates the mesh around a specified axis (BABYLON.Axis.X/Y/Z or a custom Vector3) by an angle (radians). space can be BABYLON.Space.LOCAL or BABYLON.Space.WORLD.
  • mesh.rotationQuaternion: Get or set the mesh’s rotation using a BABYLON.Quaternion. Useful for avoiding gimbal lock and for smooth interpolations. Various static methods on BABYLON.Quaternion help create them (e.g., RotationAxis, RotationYawPitchRoll).

Experiment by changing the position and rotation values in the examples while using the axis visualization to see how the objects are placed and oriented in the 3D scene.