Chapter 02Tubes

Using the CreateTube() method of the MeshBuilder class, we can make tubes.

let tube = BABYLON.MeshBuilder.CreateTube("tube", {options}, scene);

The first argument of CreateTube() is a String that we can use to retrieve the entire tube mesh and data about the tube later. The second argument is an object that specifies various properties of the tube. The last argument is a reference to the scene in which the tube will be inserted into.

<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", 3 * Math.PI / 2, 3 * Math.PI / 8, 15, 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 myPath = [
        new BABYLON.Vector3(0, -5, 0),
        new BABYLON.Vector3(-5, 0, 0),
        new BABYLON.Vector3(0, 5, 0),
        new BABYLON.Vector3(5, 0, 0),

        new BABYLON.Vector3(0, -3, 0),
        new BABYLON.Vector3(-3, 0, 0),
        new BABYLON.Vector3(0, 3, 0),
        new BABYLON.Vector3(3, 0, 0),

        new BABYLON.Vector3(0, -1, 0),
        new BABYLON.Vector3(-1, 0, 0),
        new BABYLON.Vector3(0, 1, 0),
        new BABYLON.Vector3(1, 0, 0)
    ];
	
	let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube.material = mat;

	return scene;
};

var scene = createScene(canvas, engine);

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

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

<canvas id="renderCanvas"></canvas>
Figure 1. A Babylon.js Tube.

Properties

Path

The required path property is an array of Vector3 objects where each Vector3 object is a point on the path through the center of the tube.

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

function createScene1(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
	let camera = new BABYLON.ArcRotateCamera("Camera", 3 * Math.PI / 2, 3 * Math.PI / 8, 5, 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 myPath = [
        new BABYLON.Vector3(-3, 0, 0),
        new BABYLON.Vector3(-2, 0, 0),
        new BABYLON.Vector3(-1, 0, 0),
        new BABYLON.Vector3(0, 0, 0),
        new BABYLON.Vector3(1, 0, 0),
        new BABYLON.Vector3(2, 0, 0),
        new BABYLON.Vector3(3, 0, 0)
    ];
	
	let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube.material = mat;

	return scene;
};

var scene1 = createScene1(canvas1, engine1);

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

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

<canvas id="renderCanvas1"></canvas>
Figure 2. The path property uses an array of points to set the path for the tube.

Radius

The radius property can be used to specify the size of the tube from the center to the perimeter of the tube. The default value is 1.

<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", 3 * Math.PI / 2, 3 * Math.PI / 8, 15, 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 myPath = [
        new BABYLON.Vector3(-3, 0, 0),
        new BABYLON.Vector3(-2, 0, 0),
        new BABYLON.Vector3(-1, 0, 0),
        new BABYLON.Vector3(0, 0, 0),
        new BABYLON.Vector3(1, 0, 0),
        new BABYLON.Vector3(2, 0, 0),
        new BABYLON.Vector3(3, 0, 0)
    ];
	
	let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, radius: 4, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube.material = mat;

	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 radius property is set to 4.

Tessellation

The tessellation property specifies the number of sides the tube has. The more sides the tube has, the smoother it is. The default value is 64.

<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 / 6, 3 * Math.PI / 8, 8, 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 myPath = [
        new BABYLON.Vector3(-3, 0, 0),
        new BABYLON.Vector3(-2, 0, 0),
        new BABYLON.Vector3(-1, 0, 0),
        new BABYLON.Vector3(0, 0, 0),
        new BABYLON.Vector3(1, 0, 0),
        new BABYLON.Vector3(2, 0, 0),
        new BABYLON.Vector3(3, 0, 0)
    ];

    let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, tessellation: 3, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube.material = mat;

    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 tessellation property is set to 3, making the tube three-sided.

RadiusFunction

The radiusFunction property defines a function that allows us to change the radius at different points on the tube. It overrides the radius property.

As shown in figure 5, we must use two parameters in the function. One to specify the index of the point, and the second to specify the distance of the point from the first point of the path.

<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", 3 * Math.PI / 2, 3 * Math.PI / 8, 10, 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 myPath = [
        new BABYLON.Vector3(-3, 0, 0),
        new BABYLON.Vector3(-2, 0, 0),
        new BABYLON.Vector3(-1, 0, 0),
        new BABYLON.Vector3(0, 0, 0),
        new BABYLON.Vector3(1, 0, 0),
        new BABYLON.Vector3(2, 0, 0),
        new BABYLON.Vector3(3, 0, 0)
    ];

    function radiusChange(i, distance){
        let radius =  distance / 2;
        return radius;
    };
	
	let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, radiusFunction: radiusChange, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube.material = mat;

	return scene;
};

var scene4 = createScene4(canvas4, engine4);

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

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

<canvas id="renderCanvas4"></canvas>
Figure 5. The radiusFunction property is used to make the radius of the tube get larger towards the right.

Cap

The cap property can be used to add flat ends to the tube mesh. The default value is NO_CAP.

There are four options for the cap property:

<script>
var canvas5 = document.getElementById("renderCanvas5");
var engine5 = new BABYLON.Engine(canvas5, true);

function createScene5(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
    let camera = new BABYLON.ArcRotateCamera("Camera", 3 * Math.PI / 2, 3 * Math.PI / 8, 15, 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 myPath = [
        new BABYLON.Vector3(0, -3, 0),
        new BABYLON.Vector3(0, 0, 0),
        new BABYLON.Vector3(0, 3, 0)
    ];

    let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, cap: BABYLON.Mesh.NO_CAP}, scene);
    tube.material = mat;
    tube.position = new BABYLON.Vector3(-6, 0, 0);

    let tube1 = BABYLON.MeshBuilder.CreateTube("tube1", {path: myPath, cap: BABYLON.Mesh.CAP_START}, scene);
    tube1.material = mat;
    tube1.position = new BABYLON.Vector3(-2, 0, 0);

    let tube2 = BABYLON.MeshBuilder.CreateTube("tube2", {path: myPath, cap: BABYLON.Mesh.CAP_END}, scene);
    tube2.material = mat;
    tube2.position = new BABYLON.Vector3(2, 0, 0);

    let tube3 = BABYLON.MeshBuilder.CreateTube("tube3", {path: myPath, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube3.material = mat;
    tube3.position = new BABYLON.Vector3(6, 0, 0);

    return scene;
};

var scene5 = createScene5(canvas5, engine5);

engine5.runRenderLoop(function(){
    scene5.render();
});

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

<canvas id="renderCanvas5"></canvas>
Figure 6. From left to right, the cap property is set to NO_CAP, CAP_START, CAP_END, and CAP_ALL.

Arc

The arc property allows us to decide how much of the perimeter of the tube is visible in the scene. The default value is 1, where 1 is the entire perimeter of the tube. We can use any value between 0 and 1.

<script>
var canvas6 = document.getElementById("renderCanvas6");
var engine6 = new BABYLON.Engine(canvas6, true);

function createScene6(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
    let camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 1.5, Math.PI / 6, 15, BABYLON.Vector3.Zero());
    camera.attachControl(canvas, true);
    let light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(1, 1, 0));

    let mat = new BABYLON.StandardMaterial("mat", scene);
    mat.diffuseColor = new BABYLON.Color3(0, 1, 0);

    let myPath = [
        new BABYLON.Vector3(0, -3, 0),
        new BABYLON.Vector3(0, -2, 0),
        new BABYLON.Vector3(0, -1, 0),
        new BABYLON.Vector3(0, 0, 0),
        new BABYLON.Vector3(0, 1, 0),
        new BABYLON.Vector3(0, 2, 0),
        new BABYLON.Vector3(0, 3, 0)
    ];

    let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, arc: 0.25, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube.material = mat;
    tube.position = new BABYLON.Vector3(-6, 0, 0);

    let tube1 = BABYLON.MeshBuilder.CreateTube("tube1", {path: myPath, arc: 0.5, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube1.material = mat;
    tube1.position = new BABYLON.Vector3(-2, 0, 0);

    let tube2 = BABYLON.MeshBuilder.CreateTube("tube2", {path: myPath, arc: 0.75, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube2.material = mat;
    tube2.position = new BABYLON.Vector3(2, 0, 0);

    let tube3 = BABYLON.MeshBuilder.CreateTube("tube3", {path: myPath, arc: 1, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube3.material = mat;
    tube3.position = new BABYLON.Vector3(6, 0, 0);

    return scene;
};

var scene6 = createScene6(canvas6, engine6);

engine6.runRenderLoop(function(){
    scene6.render();
});

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

<canvas id="renderCanvas6"></canvas>
Figure 7. From left to right, the arc property is set to 0.25, 0.5, 0.75, and 1.

Updatable

If we want the tube mesh to be able to have its internal geometry changed after creation, we can set the Boolean parameter updatable equal to true. The default value is false.

SideOrientation

The sideOrientation property allows us to change what part of the disc is visible. The default value is FRONTSIDE, which only makes the front of the disc visible in the scene.

The different values for the sideOrientation property are:

FrontUVs and BackUVs

If the tube is double-sided, we can choose what parts of a texture to crop and stick on the front and back sides of the mesh with the frontUVs and backUVs properties, respectively. The default value for both is Vector4(0, 0, 1, 1), which is the entire texture.

<script>
var canvas7 = document.getElementById("renderCanvas7");
var engine7 = new BABYLON.Engine(canvas7, true);

function createScene7(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
	let camera = new BABYLON.ArcRotateCamera("Camera", 3.5 * Math.PI / 4, 3 * Math.PI / 8, 5, 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.diffuseTexture = new BABYLON.Texture("https://www.babylonjs-playground.com/textures/fur.jpg", scene);
    mat.diffuseColor = new BABYLON.Color3(0, 1, 0);

    let front = new BABYLON.Vector4(0, 0, 1, 1);
    let back = new BABYLON.Vector4(0, 0, 1, 1);

	let myPath = [
        new BABYLON.Vector3(-1, 0, 0),
        new BABYLON.Vector3(1, 0, 0)
    ];
	
	let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, sideOrientation: BABYLON.Mesh.DOUBLESIDE, frontUVs: front, backUVs: back}, scene);
    tube.material = mat;

	return scene;
};

var scene7 = createScene7(canvas7, engine7);

engine7.runRenderLoop(function(){
    scene7.render();
});

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

<canvas id="renderCanvas7"></canvas>
Figure 8. The frontUVs and backUVs is used to add a texture to the front and back side of the tube respectively.

Instance

The instance property, whose default value null, allows us to change the points used in a tube mesh, as long as the number of points used in the mesh does not change and the updatable property is set to true.

<script>
var canvas8 = document.getElementById("renderCanvas8");
var engine8 = new BABYLON.Engine(canvas8, true);

function createScene8(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
    let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI/2, Math.PI / 2, 20, BABYLON.Vector3.Zero(), scene);
    camera.attachControl(canvas, true);
    let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0), scene);

    let mat = new BABYLON.StandardMaterial("mat", scene);
    mat.diffuseColor = new BABYLON.Color3(0, 1, 0);

    const myPaths = [
        new BABYLON.Vector3(0, -5, 0),
        new BABYLON.Vector3(-5, 0, 0),
        new BABYLON.Vector3(0, 5, 0),
        new BABYLON.Vector3(5, 0, 0),

        new BABYLON.Vector3(0, -3, 0),
        new BABYLON.Vector3(-3, 0, 0),
        new BABYLON.Vector3(0, 3, 0),
        new BABYLON.Vector3(3, 0, 0),

        new BABYLON.Vector3(0, -1, 0),
        new BABYLON.Vector3(-1, 0, 0),
        new BABYLON.Vector3(0, 1, 0),
        new BABYLON.Vector3(1, 0, 0)
    ];

    let options = {path: myPaths, updatable: true, cap: BABYLON.Mesh.CAP_ALL};

    let tube = BABYLON.MeshBuilder.CreateTube("tube1", options, scene);
    tube.material = mat;

    return scene;
};

var scene8 = createScene8(canvas8, engine8);

engine8.runRenderLoop(function(){
    scene8.render();
});

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

var ogPoints = true;

function changePoints() {
    let tube = scene8.getMeshByName("tube1");

    let options = {};

    options.instance = tube;

    if (ogPoints){
        options.path = [
            new BABYLON.Vector3(0, 1, 0),
            new BABYLON.Vector3(1, 0, 0),
            new BABYLON.Vector3(0, -1, 0),
            new BABYLON.Vector3(-1, 0, 0),

            new BABYLON.Vector3(0, 3, 0),
            new BABYLON.Vector3(3, 0, 0),
            new BABYLON.Vector3(0, -3, 0),
            new BABYLON.Vector3(-3, 0, 0),

            new BABYLON.Vector3(0, 5, 0),
            new BABYLON.Vector3(5, 0, 0),
            new BABYLON.Vector3(0, -5, 0),
            new BABYLON.Vector3(-5, 0, 0)
        ];

        ogPoints = false;
    } else{
        options.path = [
            new BABYLON.Vector3(0, -5, 0),
            new BABYLON.Vector3(-5, 0, 0),
            new BABYLON.Vector3(0, 5, 0),
            new BABYLON.Vector3(5, 0, 0),

            new BABYLON.Vector3(0, -3, 0),
            new BABYLON.Vector3(-3, 0, 0),
            new BABYLON.Vector3(0, 3, 0),
            new BABYLON.Vector3(3, 0, 0),

            new BABYLON.Vector3(0, -1, 0),
            new BABYLON.Vector3(-1, 0, 0),
            new BABYLON.Vector3(0, 1, 0),
            new BABYLON.Vector3(1, 0, 0)
        ];

        ogPoints = true;
    }

    tube = BABYLON.MeshBuilder.CreateTube("tube", options);
}
</script>

<canvas id="renderCanvas8"></canvas>

<button onclick = "changePoints()">Change Instance</button>
Figure 9. The instance property is used to change the points used in the tube mesh.

InvertUV

The invertUV array rotates the texture on the tube 90 degrees counter-clockwise by swapping the u and v coordinates of the texture. The default value is false.

<script>
var canvas9 = document.getElementById("renderCanvas9");
var engine9 = new BABYLON.Engine(canvas9, true);

function createScene9(canvas, engine){
    let scene = new BABYLON.Scene(engine);
    scene.clearColor = new BABYLON.Color3(1, 1, 1);
    let camera = new BABYLON.ArcRotateCamera("Camera", Math.PI / 2, Math.PI / 2, 5, 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.diffuseTexture = new BABYLON.Texture("https://www.babylonjs-playground.com/textures/icons/Back.png", scene);

    let myPath = [
        new BABYLON.Vector3(0, -1, 0),
        new BABYLON.Vector3(0, 1, 0)
    ];

    let tube = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, sideOrientation: BABYLON.Mesh.DOUBLESIDE, cap: BABYLON.Mesh.CAP_ALL}, scene);
    tube.material = mat;
    tube.position = new BABYLON.Vector3(-2, 0, 0);

    let tube1 = BABYLON.MeshBuilder.CreateTube("tube", {path: myPath, sideOrientation: BABYLON.Mesh.DOUBLESIDE, cap: BABYLON.Mesh.CAP_ALL, invertUV: true}, scene);
    tube1.material = mat;
    tube1.position = new BABYLON.Vector3(2, 0, 0);

    return scene;
};

var scene9 = createScene9(canvas9, engine9);

engine9.runRenderLoop(function(){
    scene9.render();
});

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

<canvas id="renderCanvas9"></canvas>
Figure 10. The invertUV is set to true in the left-hand tube mesh.

References