Chapter 01Translating and Scaling Meshes

There are 3 types of transformations that we can apply to a mesh: rotation, translation, and scaling. Rotations are transformations that preserve the position of at least one point in the mesh. Translations, on the other hand, reposition all of the vertices of a mesh in the same direction and by the same distance. Scaling compresses or expands the size of a mesh.

In the scene below the cube, sphere, and torus are initially positioned on the z-axis. After each time the scene is rendered, BJS runs an anonymous function that we pass to scene.registerAfterRender. The anonymous function rotates the cube, scales the sphere, and translates the torus.

<script>
  var canvas1 = document.getElementById("canvas1");
  var engine1 = new BABYLON.Engine(canvas1, true);

  var createScene1 = function(engine, canvas) {
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1,1,1);
    
    let camera = new BABYLON.ArcRotateCamera("cam", 0, 15*Math.PI/32, 5, new BABYLON.Vector3(0,0,3), scene);
    camera.attachControl(canvas, true);
    
    let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1,1,0), scene);
    light.intensity = 0.8;

    let box = BABYLON.MeshBuilder.CreateBox("box", scene);
    box.rotation.y = Math.PI/4;
    
    let mat = new BABYLON.StandardMaterial("mat", scene);
    mat.emissiveColor = new BABYLON.Color3(0,0,1);
    box.material = mat;

    let sphere = BABYLON.MeshBuilder.CreateSphere("sphere", scene);
    sphere.position = new BABYLON.Vector3(0,0,3);
    
    mat = new BABYLON.StandardMaterial("mat", scene);
    mat.emissiveColor = new BABYLON.Color3(0,1,0);
    sphere.material = mat;

    let torus = BABYLON.MeshBuilder.CreateTorus("torus", {thickness: 0.5}, scene);
    torus.position = new BABYLON.Vector3(0,0,6);
    torus.rotation.x = Math.PI/2;
    torus.rotation.y = Math.PI/2;
     
    mat = new BABYLON.StandardMaterial("mat", scene);
    mat.emissiveColor = new BABYLON.Color3(1,0,0);
    torus.material = mat;

    let direction = 1;
    let i = 0;

    scene.registerAfterRender(function () {
      let torus = scene.getMeshByName("torus");
      let pos = torus.position.y;
      if (pos < -1 || pos > 1) {
        direction = direction * -1
      }
      let delta = direction * 0.01;
    
      torus.position.y += delta;
      
      let box = scene.getMeshByName("box");
      box.rotate(BABYLON.Axis.Z, delta, BABYLON.Space.LOCAL);
      
      let sphere = scene.getMeshByName("sphere");
   	  sphere.scaling.x += delta;
      sphere.scaling.y += delta;
      sphere.scaling.z += delta;
    });

    return scene;
  };

  var scene1 = createScene1(engine1, canvas1);

  engine1.runRenderLoop(function () {
    scene1.render();
  });

  window.addEventListener("resize", function () {
    engine1.resize();
  });

</script>
<canvas id="canvas1" height="200px" width="500px"></canvas>
Figure 1. Rotation, Scaling, and Translation.

Below we discuss the local and world coordinate systems and then discuss the various ways that we can rotate, scale, and translate meshes.

World Matrices

In the last section we saw that the geometry of a mesh is defined in two arrays named the vertex buffer and the index buffer and that the coordinates of the vertices were defined in the mesh’s local space.

mesh.getPositionExpressedInLocalSpace()

A meshes’ vertex data is stored in an array of coordinates in the local space of the mesh, should we need them for tranformations. The getPositionExpressedInLocalSpace() method in the Mesh class returns the meshes origin coordinates as Vector3[] values.

BABYLON.Vector3.TransformCoordinates()

BABYLON.Vector3.TransformCoordinates() takes two arguments, the local Vector3[] position and the transformation matrix, in order to transform a meshes’ position relative to the referenced world matrix. The first argument is a local Vector3[] position that the user wants to use to transform the sphere, relative to its prior position. The second argument is the transformation matrix.

<script>
var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);

function createScene(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
	let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 3, 3 * Math.PI / 8, 22, BABYLON.Vector3.Zero());
	camera.attachControl(canvas, true);
	let light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 50, 0));

    let mat = new BABYLON.StandardMaterial("mat", scene);
    mat.diffuseColor = new BABYLON.Color3(0, 1, 0);
	
	let sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 3}, scene);
    sphere.material = mat;
    sphere.position.x = 3;
    sphere.position.z = 3;

    let axis_origin = createLocalAxes(4, scene);
    axis_origin.parent = sphere;

    createAxis(8, scene);

	return scene;
};

function createLocalAxes(size, scene) {
    let [local_axisX, local_axisY, local_axisZ] = createAxis(size, scene);

    let origin = new BABYLON.TransformNode("origin");

    local_axisX.parent = origin;
    local_axisY.parent = origin;
    local_axisZ.parent = origin;

    return origin;
}

function createAxis(size, scene) {
    let makeTextPlane = function(text, color, size, camera, billboardMode) {
        let dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", 50, scene, true);
        dynamicTexture.hasAlpha = true;
        dynamicTexture.drawText(text, 5, 40, "bold 36px Arial", color , "transparent", true);

        let plane = new BABYLON.MeshBuilder.CreatePlane("TextPlane", {size: size, updatable: true}, scene);
        plane.material = new BABYLON.StandardMaterial("TextPlaneMaterial", scene);
        plane.material.specularColor = new BABYLON.Color3(0, 0, 0);
        plane.material.diffuseTexture = dynamicTexture;
        plane.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL;

        return plane;
    };

    let axisX = BABYLON.Mesh.CreateLines("axisX", [
        new BABYLON.Vector3.Zero(),
        new BABYLON.Vector3(size, 0, 0),
        new BABYLON.Vector3(size * 0.95, 0.05 * size, 0),
        new BABYLON.Vector3(size, 0, 0),
        new BABYLON.Vector3(size * 0.95, -0.05 * size, 0)
    ], scene);
    axisX.color = new BABYLON.Color3(1, 0, 0);

    let xChar = makeTextPlane("X", "red", size / 8);
    xChar.position = new BABYLON.Vector3(0.9 * size, -0.05 * size, 0);
    xChar.parent = axisX;

    let axisY = BABYLON.Mesh.CreateLines("axisY", [
        new BABYLON.Vector3.Zero(),
        new BABYLON.Vector3(0, size, 0),
        new BABYLON.Vector3( -0.05 * size, size * 0.95, 0),
        new BABYLON.Vector3(0, size, 0),
        new BABYLON.Vector3( 0.05 * size, size * 0.95, 0)
    ], scene);
    axisY.color = new BABYLON.Color3(0, 1, 0);

    let yChar = makeTextPlane("Y", "green", size / 8);
    yChar.position = new BABYLON.Vector3(0, 0.9 * size, -0.05 * size);
    yChar.parent = axisY;

    let axisZ = BABYLON.Mesh.CreateLines("axisZ", [
        new BABYLON.Vector3.Zero(),
        new BABYLON.Vector3(0, 0, size),
        new BABYLON.Vector3( 0 , -0.05 * size, size * 0.95),
        new BABYLON.Vector3(0, 0, size),
        new BABYLON.Vector3( 0, 0.05 * size, size * 0.95)
    ], scene);
    axisZ.color = new BABYLON.Color3(0, 0, 1);

    let zChar = makeTextPlane("Z", "blue", size / 8);
    zChar.position = new BABYLON.Vector3(0, 0.05 * size, 0.9 * size);
    zChar.parent = axisZ;

    return [axisX, axisY, axisZ];
}

var scene = createScene(canvas, engine);

engine.runRenderLoop(function(){
    scene.render();
});

window.addEventListener("resize", function(){
    engine.resize();
});

var reset = true;

function transformCoor(){
    let sphere = scene.getMeshByName("sphere");
    let matrix = sphere.computeWorldMatrix(true);

    if (reset){
        let localPos = new BABYLON.Vector3(5, 3, 5);
        sphere.position = BABYLON.Vector3.TransformCoordinates(localPos, matrix);

        reset = false;
    }else{
        sphere.position.x = 3;
        sphere.position.y = 0;
        sphere.position.z = 3;
        
        reset = true;
    }
}
</script>

<canvas id="renderCanvas"></canvas>

<button onclick = "transformCoor()">Transform Coordinate</button>
Figure 2. To transform the sphere's coordinates, click the button. The sphere's local and world axes are also displayed in the sandbox.

In figure 1, we find our first argument, the local position, by setting variable localpos to specified coordinates. The second argument uses the computeWorldMatrix() method from the Mesh class so that the transformation matrix is in reference to the world matrix. Because computeWorldMatrix() has boolean argument true, the world matrix is created from scratch each time.

Translation

Translation is used to move a mesh from its origin point to another point based on a three-dimensional axis.

mesh.position

mesh.position uses the Vector3[] class to translate the coordinate values of a mesh on the x, y, and z axes. The default position for a mesh is (0, 0, 0), which is the origin.

mesh_name.position = new BABYLON.Vector3(x, y, z)
<script>
var canvas2 = document.getElementById("renderCanvas2");
var engine2 = new BABYLON.Engine(canvas2, true);

function createScene2(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
	let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 3, 3 * Math.PI / 8, 23, BABYLON.Vector3.Zero());
	camera.attachControl(canvas, true);
	let light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 50, 0));

    let mat = new BABYLON.StandardMaterial("mat", scene);
    mat.diffuseColor = new BABYLON.Color3(0, 1, 0);
	
	let sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 3}, scene);
    sphere.material = mat;
    sphere.position = new BABYLON.Vector3(1, 1, 1);

    let axis_origin = createLocalAxes(4, scene);
    axis_origin.parent = sphere;

    createAxis(8, scene);

	return scene;
};

var scene2 = createScene2(canvas2, engine2);

engine2.runRenderLoop(function(){
    scene2.render();
});

window.addEventListener("resize", function(){
    engine2.resize();
});
</script>

<canvas id="renderCanvas2"></canvas>
Figure 3. The position of the sphere is set to coordinates (1, 1, 1).

mesh.position.x, mesh.position.y, and mesh.position.z

mesh.position.x, mesh.position.y, and mesh.position.z can be used to translate a mesh on the x, y, and z axis respectively. The default value is for each is 0.

<script>
var canvas3 = document.getElementById("renderCanvas3");
var engine3 = new BABYLON.Engine(canvas3, true);

function createScene3(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
	let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 3, 3 * Math.PI / 8, 22, BABYLON.Vector3.Zero());
	camera.attachControl(canvas, true);
	let light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 50, 0));

    let mat = new BABYLON.StandardMaterial("mat", scene);
    mat.diffuseColor = new BABYLON.Color3(0, 1, 0);
	
	let sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 3}, scene);
    sphere.material = mat;
    sphere.position.x = 1;
    sphere.position.y = 2;
    sphere.position.z = 3;

    createAxis(8, scene);

	return scene;
};

var scene3 = createScene3(canvas3, engine3);

engine3.runRenderLoop(function(){
    scene3.render();
});

window.addEventListener("resize", function(){
    engine3.resize();
});
</script>

<canvas id="renderCanvas3"></canvas>
Figure 4. The position on the x, y, and z axis is set to 1, 2, and 3 respectively.

mesh.setPositionWithLocalVector()

The setPositionWithLocalVector() method sets the meshes’ position from the origin using a Vector3[] argument.

<script>
var canvas4 = document.getElementById("renderCanvas4");
var engine4 = new BABYLON.Engine(canvas4, true);

function createScene4(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
	let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 3, 3 * Math.PI / 8, 23, BABYLON.Vector3.Zero());
	camera.attachControl(canvas, true);
	let light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 50, 0));

    let mat = new BABYLON.StandardMaterial("mat", scene);
    mat.diffuseColor = new BABYLON.Color3(0, 1, 0);
	
	let sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 3}, scene);
    sphere.material = mat;

    sphere.setPositionWithLocalVector(new BABYLON.Vector3(2, 1, 0));

    let axis_origin = createLocalAxes4(4, scene);
    axis_origin.parent = sphere;

    createAxis4(8, scene);

	return scene;
};

function createLocalAxes4(size, scene) {
    let [local_axisX, local_axisY, local_axisZ] = createAxis(size, scene);

    let origin = new BABYLON.TransformNode("origin");

    local_axisX.parent = origin;
    local_axisY.parent = origin;
    local_axisZ.parent = origin;

    return origin;
}

function createAxis4(size, scene) {
    let makeTextPlane = function(text, color, size, camera, billboardMode) {
        let dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", 50, scene, true);
        dynamicTexture.hasAlpha = true;
        dynamicTexture.drawText(text, 5, 40, "bold 36px Arial", color , "transparent", true);

        let plane = new BABYLON.MeshBuilder.CreatePlane("TextPlane", {size: size, updatable: true}, scene);
        plane.material = new BABYLON.StandardMaterial("TextPlaneMaterial", scene);
        plane.material.specularColor = new BABYLON.Color3(0, 0, 0);
        plane.material.diffuseTexture = dynamicTexture;
        plane.billboardMode = BABYLON.Mesh.BILLBOARDMODE_ALL;

        return plane;
    };

    let axisX = BABYLON.Mesh.CreateLines("axisX", [
        new BABYLON.Vector3.Zero(),
        new BABYLON.Vector3(size, 0, 0),
        new BABYLON.Vector3(size * 0.95, 0.05 * size, 0),
        new BABYLON.Vector3(size, 0, 0),
        new BABYLON.Vector3(size * 0.95, -0.05 * size, 0)
    ], scene);
    axisX.color = new BABYLON.Color3(1, 0, 0);

    let xChar = makeTextPlane("X", "red", size / 8);
    xChar.position = new BABYLON.Vector3(0.9 * size, -0.05 * size, 0);
    xChar.parent = axisX;

    let axisY = BABYLON.Mesh.CreateLines("axisY", [
        new BABYLON.Vector3.Zero(),
        new BABYLON.Vector3(0, size, 0),
        new BABYLON.Vector3( -0.05 * size, size * 0.95, 0),
        new BABYLON.Vector3(0, size, 0),
        new BABYLON.Vector3( 0.05 * size, size * 0.95, 0)
    ], scene);
    axisY.color = new BABYLON.Color3(0, 1, 0);

    let yChar = makeTextPlane("Y", "green", size / 8);
    yChar.position = new BABYLON.Vector3(0, 0.9 * size, -0.05 * size);
    yChar.parent = axisY;

    let axisZ = BABYLON.Mesh.CreateLines("axisZ", [
        new BABYLON.Vector3.Zero(),
        new BABYLON.Vector3(0, 0, size),
        new BABYLON.Vector3( 0 , -0.05 * size, size * 0.95),
        new BABYLON.Vector3(0, 0, size),
        new BABYLON.Vector3( 0, 0.05 * size, size * 0.95)
    ], scene);
    axisZ.color = new BABYLON.Color3(0, 0, 1);

    let zChar = makeTextPlane("Z", "blue", size / 8);
    zChar.position = new BABYLON.Vector3(0, 0.05 * size, 0.9 * size);
    zChar.parent = axisZ;

    return [axisX, axisY, axisZ];
}

var scene4 = createScene4(canvas4, engine4);

engine4.runRenderLoop(function(){
    scene4.render();
});

window.addEventListener("resize", function(){
    engine4.resize();
});
</script>

<canvas id="renderCanvas4"></canvas>
Figure 5. The position of the sphere on the x, y, and z axis is 2, 1, and 0 respectively.

mesh.locallyTranslate()

mesh.translate()

Scaling

mesh.scaling

mesh.scaling.x

mesh.scaling.y

mesh.scaling.z

Modifying Origin of Transformation

origin of local axis

mesh.parent

TransformNode

Pivot Point

Baking transformations

Face Camera

mesh.lookAt()

BILLBOARD mode

References