tutorials

Ribbon Tutorial


Here you'll find almost everything to understand how the Ribbon object works and how to play with it.

Under the hood

series of paths paths with different lengths

It's not mandatory that all paths have the same length.
In this example, path2 and path3 are longer than path1 and path4 : https://www.babylonjs-playground.com/#88AZQ -



As you can see, the final ribbon adjust to different lengths. The rule is they all start from first path points and each intermediate ribbon then stops at first of its both constituting paths end.
There is also no incidence on light reflection for ribbon with different length paths : https://www.babylonjs-playground.com/#88AZQ#1 -


Therefore you can't add a texture for now to a ribbon constructed with different length paths.
This is due to the fact that the nested ribbon texturing algorithm only knows how to deal with a unique length for all paths. Indeed, as a ribbon is a parametric shape, so unpredictable, it's assumed that we could unwrap a volumic ribbon (so each one of its paths) onto a full rectangular image and this assumption keeps consistency only with the same length for all paths.

So it's not mandatory that all the ribbon paths have the same length, but it is hardly recommended.
The best way to emulate different lengths for some parts of your mesh is then to simply use many ribbons.

Closed shapes : normals or textures ?

The ribbon mesh provides two ways to automatically close an unclosed shape.

  • closeArray parameter : this will add an extra unit ribbon between the last path and the first path of your pathArray.
  • closePath parameter : this will join the last and first points of each path in your pathArray.

Here's an unclosed ribbon : https://www.babylonjs-playground.com/#3XMWZ#1 -


var ribbon = BABYLON.Mesh.CreateRibbon("ribbon", paths, false, false, 0, scene, false, BABYLON.Mesh.BACKSIDE);

Don't mind about how it is mathematically built. This is not the topic.

The same closed ribbon with closeArray set to true : https://www.babylonjs-playground.com/#3XMWZ#2 -


var ribbon = BABYLON.Mesh.CreateRibbon("ribbon", paths, true, false, 0, scene, false, BABYLON.Mesh.BACKSIDE);

As you can see, when rotating the camera, there is no edge on the ribbon surface. The light reflects continuously.

If we now apply a texture to this automatically closed ribbon : https://www.babylonjs-playground.com/#3XMWZ#3 -



You can notice that the texture isn't stretched on the surface added by the automatic closing.

The reason of this behavior is that, with ribbon closeXXX parameters, priority is given on normals (the tools that compute light reflection) over textures.

If you don't care about continuous light reflection but you do want your texture to be stretched along the whole surface, you just have to forget automatic closing (closeArray then set to false) and close the ribbon by yourself.
A simple way is just to re-push the first path at the end of the pathArray

paths.push(paths[0]);
var ribbon = BABYLON.Mesh.CreateRibbon("ribbon", paths, false, false, 0, scene, false, BABYLON.Mesh.BACKSIDE);

Example : https://www.babylonjs-playground.com/#3XMWZ#4 -


Obviously, the same rules and workarounds apply to the closePath parameter.
automatically path closed : https://www.babylonjs-playground.com/#3XMWZ#5 -



then textured : https://www.babylonjs-playground.com/#3XMWZ#6 -

Maths computed paths

The Ribbon is very adapted to elaborated maths computed meshes.
You can easily start from an equation to get a full volumic complex shape.
There are many ways to do it.

If you don't feel at ease with maths, here is a way to start :

First, let's have a small recall.

We just set points in space. These points have got each a set of three coordinates : x, y and z.
We call here these points Vector3.

When we want to design a curve or a path in space, we need to get a collection of successive Vector3.
We can't have an infinity of points.
So we define a path with a certain number of Vector3. The more Vector3, the more smooth the curve and the more computations too.

So when you want your path to follow a mathematical curve, you need to compute each path Vector3 coordinates.
You could choose a known math curve in wikipedia or dedicated sites (http://en.wikipedia.org/wiki/List_of_curves , http://www.mathcurve.com/courbes2d/courbes2d.shtml , http://www.uiowa.edu/~examserv/mathmatters/tutorial_quiz/geometry/commoncurves.html, etc) or, when you feel more comfortable, create you own.

As you can see, curve equations are often like this : f(x, y) = 0 or like this : y = f(x).
This means y is expressed in function of x.
This kind of equation is called a cartesian equation. It is probably the most used among mathematicians, but it won't help us a lot because we need to compute x and y (and z) simultaneously to set each Vector3.
So we will prefer the parametric equations.
In a parametric equation each different coordinate is defined in function of a parameter k :
x = fct1(k)
y = fct2(k)
z = fct3(k)

So if you are given a cartesian equation, it is quite almost possible to translate it in a parametric equation.
example with a parabola : y = x²
the cartesian equation y = x * x will give the parametric equation :
x = k
y = k * k
You then give k values from -20 to 20 for example and you get your 40 successive Vector3 on the parabola. Easy, isn't it ?
You now know the way to fill a path with successive Vector3 along a math curve.

Back to the Ribbon

Well, we just learnt how to fill a path but a Ribbon needs many paths (okay, we can still construct a ribbon with a single path too, but it's more complex), so how do we add different paths as there is no real interest to add many times the same path ?
It's quite easy once you've got your parametric equation.

Let's get into javascript now.
Your former parametric equation could be this way :

var path = [];
for (var k = -20; k <= 20; k++) {
  var x = k;
  var y = k * k;
  var z = 0;
  path.push(new BABYLON.Vector3(x, y, z));
}

Right ?
https://www.babylonjs-playground.com/#1HSC2O -


Let's now imagine, you create the same path array 10 times on the z-axis with z = t :

var paths = [];
for (var t = 0; t < 10; t++) {
  var path = [];
  for (var k = -20; k <= 20; k++) {
    var x = k;
    var y = k * k;
    var z = t;
    path.push(new BABYLON.Vector3(x, y, z));
  }
  paths.push(path);
}

What do we get now ?
https://www.babylonjs-playground.com/#1HSC2O#1 -



An array paths filled with 10 similar paths.
Just what is needed to create a ribbon : https://www.babylonjs-playground.com/#1HSC2O#2 -


If you now change slightly each path equation so they aren't all similar, say, by dividing y by t :

var paths = [];
for (var t = 1; t < 10; t++) {
  var path = [];
  for (var k = -20; k <= 20; k++) {
    var x = k;
    var y = k * k / t;
    var z = t;
    path.push(new BABYLON.Vector3(x, y, z));
  }
  paths.push(path);
}

You immediatly get a set of different paths along the z-axis : https://www.babylonjs-playground.com/#1HSC2O#8 -



So a more complex ribbon : https://www.babylonjs-playground.com/#1HSC2O#9 -

At last, if we change a bit x and z variation to scale the curve, we can get a nice parabolic shape :

var paths = [];
for (var t = 1; t < 10; t++) {
  var path = [];
  for (var k = -20; k <= 20; k++) {
    var x = k * 8;
    var y = k * k / t;
    var z = t * 50;
    path.push(new BABYLON.Vector3(x, y, z));
  }
  paths.push(path);
}

https://www.babylonjs-playground.com/#1HSC2O#10 -



Quick fun ?
multiply y by Math.sin(t) to make it wave : https://www.babylonjs-playground.com/#1HSC2O#11 -


or funnier : https://www.babylonjs-playground.com/#1HSC2O#12 -


I couldn't stop playing ...

Summary

An easy way to create math computed shapes is so :

  • to choose a 2D math curve,
  • to get its parametric equation,
  • to fill an array with Vector3 computed with a simple for loop iterating on the number of points wanted (set them in a 2D plane to start, with z = 0 for instance),
  • to check your curve with BABYLON.Mesh.CreateLines("name", yourArray, scene),
  • to derivate your first curve by varying x or y and iterating on z since adding each derivated path into a paths array,
  • to check again with BABYLON.Mesh.CreateLines("name", yourArray, scene) on each z iteration,
  • to finally build your ribbon with the paths array.

From Basic Shapes to Complex Ones

The Ribbon is very versatile. So you can redo every BabylonJS basic shapes.
Why would you want to do this ?
Well, you probably don't. There is no need to re-invent the wheel. But you could need to model your own shape which derivates from one of the basic shapes.
The main rule should be :

  • if you need a basic shape as it stands, then use the provided BJS basic shapes.
  • if you need a shape made up of many basic shapes, then use Constructive Solid Geometry or merge provided BJS basic shapes.
  • if you need a computed shape having a symetry axis, then use the Tube mesh or the extrusion, which don't require many maths.
  • if you need something else, then use the Ribbon itself... and your maths skills.

Sphere

Let's try here to redo a sphere and then to modify it into something different.
As you've seen in the former part, you need to create many paths to build a ribbon. For a sphere, you can imagine that you stack many circles, each circle being a path.
To create a circle, you just set points at x = sin(angle) and z = cos(angle) and give angle some values between 0 and 2 x PI.

var pi2 = Math.PI * 2;
var step = pi2 / 60;       // we want 60 points
for (var i = 0; i < pi2; i += step ) {
  var x = radius * Math.sin(i);
  var z = radius * Math.cos(i);
  var y = 0;
  path.push( new BABYLON.Vector3(x, y, z) );
  }
path.push(path[0]);       // to close the circle

demo : https://www.babylonjs-playground.com/#E6IX1#1 -


Now, you add circles along the y-axis, making the radius evolving with another angle p varying from the sphere south pole -PI / 2 to its north pole +PI /2. These circles (path) are stored in an array called paths :

var radius = 10;
var tes = 60;
var pi2 = Math.PI * 2;
var step = pi2 / tes;
var paths = [];
for (var p = -Math.PI / 2; p < Math.PI / 2; p += step / 2) {
  var path = [];
  for (var i = 0; i < pi2; i += step ) {
    var x = radius * Math.sin(i) * Math.cos(p);
    var z = radius * Math.cos(i) * Math.cos(p);
    var y = radius * Math.sin(p);
    path.push( new BABYLON.Vector3(x, y, z) );
    }
    path.push(path[0]);
    paths.push(path);
}

demo : https://www.babylonjs-playground.com/#E6IX1 -


Let's apply a ribbon to these paths : https://www.babylonjs-playground.com/#E6IX1#2 -



You get (almost) a sphere.
To get a nice full sphere, you need to complete the missing point at north pole and set the ribbon closePath parameter to true instead of manually close each circle after the former iteration :

var lastPath = [];
for (var j = 0; j < pi2; j += step ) {
  lastPath.push( new BABYLON.Vector3(0, radius, 0) );
}
paths.push(lastPath);

var sphere = BABYLON.Mesh.CreateRibbon("sph", paths, false, true ,  0, scene);

demo : https://www.babylonjs-playground.com/#E6IX1#3 -



Pretty much maths and iterations o far to get a simple sphere, isn't it ?
This is why you should really use the BJS provided sphere if you only want a sphere !

But don't worry, all those efforts so far aren't vain. From now, let's the magic happens with only little changes ...
Remember : the for loop iterating on p is for the south to north pole angle. What if you don't increment p until PI / 2 but stop before, say at PI /2 - 1.5 :

for (var p = -Math.PI / 2; p < Math.PI / 2 - 1.5; p += step / 2) {

demo : https://www.babylonjs-playground.com/#E6IX1#4 -



Quite easy. You just derivated the initial sphere into another shape you wouldn't have got another way.

Now, you can keep the original pole angle limit PI / 2 but add a new behavior : if a certain angle limit is reached, then inverse the y radius around this limit.

var yRadius;
var limit = Math.PI / 2 - 1;
for (var p = -Math.PI / 2; p < Math.PI / 2; p += step / 2) {
  var path = [];
  yRadius = p < limit ? Math.sin(p) : 2 * Math.sin(limit) - Math.sin(p) ;
  for (var i = 0; i < pi2; i += step ) {
    var x = radius * Math.sin(i) * Math.cos(p);
    var z = radius * Math.cos(i) * Math.cos(p);
    var y = radius * yRadius;
    path.push( new BABYLON.Vector3(x, y, z) );
  }
  paths.push(path);
}

demo : https://www.babylonjs-playground.com/#E6IX1#5 -


Let's change the initial for loop limits now :

for (var p = -Math.PI / 2 + 0.5; p < Math.PI / 2  - 0.5; p += step / 2) {

demo : https://www.babylonjs-playground.com/#E6IX1#6 -



Well, is this still a sphere ?
Let's close the ribbon :

var sphere = BABYLON.Mesh.CreateRibbon("sph", paths, true, true ,  0, scene);

demo : https://www.babylonjs-playground.com/#E6IX1#7 -



Well, it's no longer a sphere, but a symetric shape you could probably have got in a simpler way with a Tube mesh or with CSG ... or not.
Since you wrote the initial sphere maths code, you've added until now very few changes to get this derivated shape. Too symetric, not enough, ok ? let's morph it once more so you get out the CSG or Tube way : let's moderate x with an extra cosinus function

for (var i = 0; i < pi2; i += step ) {
  var x = radius * Math.sin(i) * Math.cos(p) * Math.cos(i / 6);
  var z = radius * Math.cos(i) * Math.cos(p);
  var y = radius * yRadius;
  path.push( new BABYLON.Vector3(x, y, z) );
}

demo : https://www.babylonjs-playground.com/#E6IX1#8 -



You are now in the real Ribbon world ! Want more ? let's moderate y with another cosinus function and multiply z by 2 (why ? why not !).

for (var i = 0; i < pi2; i += step ) {
  var x = radius * Math.sin(i) * Math.cos(p) * Math.cos(i / 6);
  var z = radius * Math.cos(i) * Math.cos(p) * 2;
  var y = radius * yRadius * Math.cos(i * 2);
  path.push( new BABYLON.Vector3(x, y, z) );
}

demo : https://www.babylonjs-playground.com/#E6IX1#9 -


Various paths concatenation

make many different computed or manual pathArrays, then concatenate them

edition in progress