Jump to content

Update vertices of a Box


Colmea
 Share

Recommended Posts

Hello,

It's my first post, so thank you for your awesome work on Babylon.js. I come from Three.js, and I hope my journey here will be good !

Let me explain the situation:

I need to create a custom 6 faces polygon. I decided to start from a Babylon Box, and then update vertices position. It was easy on Three.js, but seems harder on Babylon. I have some questions:

1) What's the order of the vertices position ? It looks like the first vertice is on the back face, in the bottom right side, and the second one bottom left. Is it normal or am I doing something wrong ?

 

2) Why are there two vertices for a same 3D position ? In other words, why can't we share a vertex for several faces ? It's really complicated to have 24 vertices for a single box. 8 vertices are not enough ? (I'm not an expert at all, I guess there's a reason. Again, in Three.js there are only 8 vertices).

 

3) I'm surprised that getVerticesData(POSITION) returns an array of numbers. Why don't we have an array of Vector3 ?

 

All these questions can be summarized in a single one: how do you easily manage vertices update in a box. I find it really complicated to deal with a "big-array-with-96-number-and-deal-with-it" for a single cube (knowing I will have to deal with more complicated polygons in the future).

Hope you can give me advices or tools for that !

Have a nice day,

Colmea

 

Link to comment
Share on other sites

Hi welcome on this forum and welcome in the wonderful BJS world :)

 

1) well, the concepts of front, back, up, down are quite complex in 3D as they depend upon the camera position

To exactly know what face of your box you want to address, you could first give each a different color.
This doc may help you : http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors

http://doc.babylonjs.com/tutorials/CreateBox_Per_Face_Textures_And_Colors#colors

2) In BJS, contrary to ThreeJS, all the vertices are indexed. This means, there is an array called "indices" to reference all the used vertices. As a normal is set per vertex only, it is required to have as many vertices as the normal vectors wanted. This method is really fast as soon as the number of vertices increase, in particular for vertex re-use.

So for a box, there are 2 triangular facets per box side, so 4 vertices per box side (2 are shared between the two facets). So in total, 6  sides * 4 = 24 vertices.

Please have a look at the first part of this tutorial to understand what the indexed vertices mean : https://blogs.msdn.microsoft.com/eternalcoding/2014/04/17/what-do-you-mean-by-shaders-learn-how-to-create-shaders-with-babylon-js/

3 ) getVerticesData(Positions) returns the array "positions" of the mesh... each successive triplet of floats is each vertex coordinates : [v1.x, v1.y, v1.z, v2.x, v2.y, v2.z ....]
If you set your mesh as updatable at creation time, you can easily change its vertex position with updateMeshPositions()

http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh#other-shapes-updatemeshpositions

 

Hope it helps

Link to comment
Share on other sites

Thanks for your fast answer.

Well, that's not good news actually :D (but I expected this kind of answer).

I guess I will have to extend the Box class and create methods to help me update vertices easily (give only 8 Vector3). I will share my code here :)

I hope someone already works on this !

Thanks you (and the per face texture is a good idea ! )

Link to comment
Share on other sites

Actually, all the BJS parametric shapes can be morphed from their geometry declaration (in short, from their vertices or from the way the vertices are built) : http://doc.babylonjs.com/tutorials/How_to_dynamically_morph_a_mesh

Unfortunately, there's no such high level provided method to do this with the fixed shapes... as noone asked for this until now :P
We will enjoy to share your code. Please feel free to use the playground (aka PG, the place we all share here our ideas, bugs, attempts, etc) for this : http://www.babylonjs-playground.com/#

BTW, I guess you're from Belgium (Twitter told me that) so you might attend the very next FOSDEM conference in Brussels and discover the session from Raanan and Temechon, two of the framework core team and nice forum animators, here : https://fosdem.org/2016/schedule/event/babylon/

Confs or speaking directly to the core devs are one of the best experience to get very fast into the framework. It's really worth it imho.

Link to comment
Share on other sites

Hello and Welcome Colmea!

Regarding array of number or array of vector3: the main reason is performance. Internally we are using a foat32Array to provide the fastest support possible. And if, for instance you want to do fancy things like updating your mesh on every frame, this is the best option.

I completely understand your point though but if you want to achieve what you mention you will have to do a copy of data. Which could be ok if you don't plan to do it too much (regarding performance AND memory consumption ;))

Other question: why 24 instead of 8? Because of uv and normal. With only 8 vertices you cannot provide decent shading (normal) or per-face textures coordinates.

 

The core foundation of BJS is simplicity and I must agree with you. This is not the case here. But sometimes simplicity has to disappear to gain performance. ANd in this case, you will always be able to count on this community to help you

 

Link to comment
Share on other sites

Thanks for your clarification Deltakosh !

I agree, simplicity is not always the right choice. However, in my case, vertices are not updated on each frame: it's an event triggered by the user (which fill a form to build a new polygon). So performance is not really the main issue in my case.

One last thing: I understand the reason behind the 24 vertices, but is there a single reason why someone would like to move two "same" vertices on different positions ? In other words, why BABYLON doesn't hide this complexity for developers ? 

 

I'm working on a custom Box class to help me move vertices easily. Here is a (WIP) demo: http://babylonjs-playground.com/#1QHDMR#0

This way I can move vertices from a specific face and don't care about linked vertices anymore:

// Create custom box (extend createBox helper)
var box = new CustomBox('customBox', scene);
	
// Move vertice 0 and 2 on top face (according to world axis)
// This automatically move linked vertices 
var topFace = [];
topFace[0] = new BABYLON.Vector3(-3, 2, 0);
topFace[2] = new BABYLON.Vector3(0.5, 0.5, 0);

box.updateFacesVertices({
    top: topFace
});

 

Some important points:

  • The vertice index on each face is related to the order of creation in the CreateBox method (which is not logical between faces, or I'm missing something).
  • Code is not DRY at all, and the 'top', 'back', 'left', ... references are arbitrary. Need some clean up.

Next step is to be able to do something like this:

box.getFace('top').vertices[0].position.x += 10;

 

I know this is a really specific use case. But maybe it can helps someone ;)

Link to comment
Share on other sites

  • 4 weeks later...

My explanation of this code just got evaporated when I did a minor edit to this post so here it is again.   I had the exact same issue after coming from Three.JS so I had to come up with an easy solution. Here is a function that will allow you to move any vertex in a mesh.  If the vertex is shared among faces, all shared vertices will move with it.  Some of the code originally came from a tutorial I found online.  Sorry if it's a little messy.

use:  shiftVertex(yourMesh, vertexNumber, [x, y, z]) where x y z is the amount to move the vertex.

/* Shift a vertex by an amount on the xyz axis */
function shiftVertex(mesh, vertexNumber, xyz) {
    var vertexData = BABYLON.VertexData.ExtractFromMesh(mesh);
    var positions = vertexData.positions;
    var numberOfPoints = positions.length / 3;

    // Build a map containing all vertices at the same position
    var map = [];
    for (var i = 0; i < numberOfPoints; i++) {
        var p = new BABYLON.Vector3(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);

        var found = false;
        for (var index = 0; index < map.length && !found; index++) {
            var array = map[index];
            var p0 = array[0];
            if (p0.equals(p) || (p0.subtract(p)).lengthSquared() < 0.01) {
                array.push(i * 3);
                found = true;
            }
        }
        if (!found) {
            var array = [];
            array.push(p, i * 3);
            map.push(array);
        }
    }
    var thisVertex = map[vertexNumber];
    /* Change the "+=" to just "=" for direct coordinate placement */
    for (index = 1; index < thisVertex.length; index++) {
        var i = thisVertex[index];
        positions += xyz[0];
        positions[i + 1] += xyz[1];
        positions[i + 2] += xyz[2];
    }
    
    vertexData.applyToMesh(mesh);
}

 

Link to comment
Share on other sites

Here is a useful vertex labeller to use with the shiftVertex function above.  This will label every vertex of a mesh with a number that corresponds to the vertex number.  Used in tandem with scene.debugLayer.show() and "clickable labels" turned on, it's very useful as each vertex will be clearly labeled for use on my previous shiftVertex function.  Use labelVertices(mesh) to add labels, and clearLabels() to remove labels.

/* 
Mark each vertex with a number.  WARNING!  Large meshes will raise havoc here if the maxLabelsForSafety is too high!
Make sure you define 'camera' and 'scene' if their names are different 
*/

function labelVertices(mesh) {
    var maxLabelsForSafety = 100; /* Set a maximum number of labels to prevent complete lockup on large meshes */
    
    /* If labels already exist, clear them first */
    if( typeof _global_vertex_labelgroup != "undefined" ) {
        clearLabels();
    } else { _global_vertex_labelgroup = []; }

    var positions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
    var numberOfPoints = positions.length / 3;

    // Build a map containing all vertices at the same position
    var map = [];
    for (var i = 0; i < numberOfPoints; i++) {
        var p = new BABYLON.Vector3(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);

        var found = false;
        for (var index = 0; index < map.length && !found; index++) {
            var array = map[index];
            var p0 = array[0];
            if (p0.equals(p) || (p0.subtract(p)).lengthSquared() < 0.01) {
                array.push(i * 3);
                found = true;
            }
        }
        if (!found) {
            var array = [];
            array.push(p, i * 3);
            map.push(array);
        }
    }

    var vertCount = 0;
    map.forEach(function(array) {
        
        var index;
        
            if(_global_vertex_labelgroup.length >= maxLabelsForSafety) { console.log("Too many labels! Aborting."); return; }
        
            index = 1;
            var i = array[index];
            var textPlane = BABYLON.MeshBuilder.CreatePlane("Vertex " + vertCount, {width:1,height:1}, scene);
            textPlane.position = new BABYLON.Vector3(positions, positions[i+1],positions[i+2]);   
            textPlane.billboardMode = 7;
            
            var text = vertCount.toString();
            var dynamicText = new BABYLON.DynamicTexture("dt"+vertCount, 1024, scene, true);
            dynamicText.drawText(text, 100, 700, "bold 452px Arial", "white", "black", true);     
            var textMaterial = new BABYLON.StandardMaterial("Text"+vertCount, scene);
            textMaterial.diffuseTexture = dynamicText;          
            textPlane.material = textMaterial;
            var cleanupGroup = [ textPlane, dynamicText, textMaterial];
            _global_vertex_labelgroup.push(cleanupGroup);
            vertCount++;
    
    });

}

/* Clear any existing vertex labels in the scene.  Make sure Babylon disposes of them cleanly. */
function clearLabels() {
    if( !_global_vertex_labelgroup ) {
        console.log("No labels were found to be cleared!");
        return;
    }
    for(var i = 0; i < _global_vertex_labelgroup.length; i++) {
        _global_vertex_labelgroup[0].dispose();
        _global_vertex_labelgroup[1].dispose();
        _global_vertex_labelgroup[2].dispose();
    }
    _global_vertex_labelgroup = [];
}

Admittedly, I only tested this in an app I'm working on but it works well.

Edited by DeadTaco
Numbers were being shown one value off
Link to comment
Share on other sites

why not just push the array of three numbers into an object array that holds a Quat, so like with the 4th value being a unique identifier or the vertex number.  then just parse then arrange those any way you would want then parse them back to a standard array minus the unique ID when you need to propagate them back.

Link to comment
Share on other sites

Thanks DeadTaco, it's really interesting !

 

14 hours ago, DeadTaco said:

If the vertex is shared among faces, all shared vertices will move with it. 

If I understand the code, you define "shared" vertices by their position. This could be a problem for me (vertices sometimes are on the same position).

But I will use your code, thanks.

What is missing now is an easy way to create polygons, and remove the "3 vertices complexity". Would be awesome to do something like this:

 

// Create a custom pyramid
vertices = [
  new BABYLON.Vector3(0, 0, 0), // 0
  new BABYLON.Vector3(4, 0, 0), // 1
  new BABYLON.Vector3(2, 0, 4), // 2
  new BABYLON.Vector3(2, 4, 2), // 3
];

var pyramid = new BABYLON.CreatePolygon('myPyramid', scene, vertices, ...);

// Move vertices with DeadTaco's function adaption
// Move vertex 0 (and all shared vertices) to a new position
pyramid.moveVertex(0, new BABYLON.Vector3(1, 0, 0));

 

Do you think it's feasible with Babylon ? (I mean, the vertices algo is not a problem, but I don't know a lot about faces, normals, ... Is it possible to compute them based on vertices ?)

 

Link to comment
Share on other sites

http://doc.babylonjs.com/tutorials/Mesh_CreateXXX_Methods_With_Options_Parameter#polyhedron

by default, all the faces of a polyhedron are independant, so you can move/morph them without changing the others

 

[EDIT] http://www.babylonjs-playground.com/#21QRSK#1

Minimize the editor (button EDITOR-) to see their names with the mouse pointer

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...