Jump to content

texture on individual faces of a polyhedron


Recommended Posts

This is probably one for @Jerome but others will have more idea than I do anyway.


Have been playing with the Rhombicuboctahedron and trying to place the same image on each face. (Starting with one image rather than an image atlas as thought it would be easier).


Pinching bits and pieces from here and there from BABYLON.js max I created this playground.


As you can see I got in a bit of a mess - something to do with having the uvs in the wrong order probably.


Any help gratefully received.


Jerome should you want to answer this one then I know you have a long backlog of things to do so no rush - just interested nothing vital.

Link to comment
Share on other sites


This polyhedron has different polygonal shapes for faces (triangles and squares, if I'm not wrong), so you can't have only one "rule" (ie uvs declaration) working for all these polygons.

Moreover you had to convert the mesh to a flat shaded one, what is a good thing to add vertices (*) if you want the same image on each face (it can't really be the same as we have squares and triangles !) and this conversion applied after you set the uvs. I guess the new vertices don't hold any uvs at all.


(*) you can't "copy" the same image on different faces if the vertices are common to several faces.

1 vertex <=> 1 uv


So if the vertex belongs to 2 facets, it can't hold the image coordinates for facet1 and the image coordinates for facet2 in the same time.


It's really hard to do.

You would have to :

- define the uvs for the facet composing a triangle

- define the uvs for the pair of facets composing a square

- iterate on every vertex and guessing if it is a part of a triangle or a square (to choose what definition to use) and where it is on the square or the triangle



Link to comment
Share on other sites

@Jerome in my simple minded way I thought I had some of the points you mentioned covered, though obviously not because it didn't work. I'll describe my thinking and perhaps if you have time you could pinpoint my errors and then I can have another go.


I did try to deal with the fact that there are triangular and square faces and expected the triangular ones possibly not to work but was going to deal with that later provided the square faces worked (but didn't).


I looked at your Babylon.js/src/Mesh/babylon.mesh.vertexData.ts file and saw on lines 6387 to 657 that for each face a uv pair was pushed for each vertex of the face.


So with one image my Vector4 of uvs would be [0,0,1,1] equivalent to you faceUV[index] with index always 0.


So in the playground  I set up my uv pairs as (line 33)

var face_uvs=[[0,0],[1,0],[1,1],[0,1]];

then in lines 42 - 44 for each of the faces in the Rhombicuboctahedron the length of data.face[f] gives the number of vertices so

for(var j = 0; j < data.face[f].length; j++) {    uvs=uvs.concat(face_uvs[j]);}

pushes a uv pair in order to uvs  -- uvs=uvs.concat(face_uvs[j]) being I hope the same as uvs.push(face_uvs[j][0],face_uvs[j][1])


This appears to set the uv for each vertex of each face whether the face has 3 sides or 4 sides


As much as I want it to and it looks like it should it obviously does not cover your points


1 vertex <=> 1 uv


- define the uvs for the facet composing a triangle

- define the uvs for the pair of facets composing a square

- iterate on every vertex and guessing if it is a part of a triangle or a square (to choose what definition to use) and where it is on the square or the triangle


What is wrong with my reasoning?


Does either of BABYLON.VertexData.ComputeNormals or  BABYLON.VertexData._ComputeSides change the order in which vertices are considered and so throw out the match I created between face vertices and the uvs?


Am I just completely wrong?

Link to comment
Share on other sites

computeNormals only sets the normals array

computeSides can change the number of vertices (so the number of indices and uvs also) and/or the indices order... unless if you choose FRONTSIDE or DEFAULTSIDE : these don't change anything.


So for your case, you can forget these two functions, they don't modify anything.


convertToFlatShaded can/will add vertices to your geometry !

So you set uvs to the initial polyhedron vertices and then add new ones. The problem is that we don't know where they are added unless we hack this method seriously.


Let's imagine we don't use it now.


As I can remember the array data.face is filled with the indices of the i-th facet, so 3 vertices per array. This means you should assign 3 pairs of uvs par data.face, not 4.

This is the principle : a UV pair per vertex only

Note well you won't achieve the goal you want because we have here quite all vertices that are common to many faces. (not easy to explain this in english for me, sorry)


It was easy for me to set the uvs on a box because a box has exactly 6 similar faces in term of geometry (2 facets by face, always ordered the same known way) and no vertex was common to two different box faces.

In short : I knew the geometry and the faces were all independant.


It's really hard to do with these polyhedrons if you want the same image on each face.


We should first make sure that each facet doesn't share its vertice with another one (what convertToFladShaded seems to do), else impossible to apply the same image on each face.

Then we should understand the geometry : where are the vertices of each face, in this face : the ones related to the others. No way to set correct uvs without this piece of information.

Finally we would code the algo to assign the right uvs to the right vertices regarding the former two points.


I guess it's easier to fold an image around the whole polyhedron

Link to comment
Share on other sites

Hi John K,


I didn't thoroughly read every line in this post, but I get the gest of what you're trying to do.  If it were me, I might try using multi materials, as you will be able to assign a different material to each set of UVs representing a face on your Rhombicuboctahedron.  And this could be done proceedurally so that a different material is automatically pushed to the UVs which define each face on the mesh limiting (optimizing) the amount of lines of code required.


But are you simply trying to torture yourself, or do you actually have a scene in mind where this might be used.  For me, it would be a method of torture.  It almost is just thinking about it.  :blink: 



Link to comment
Share on other sites

DB I just like the way Jerome applied texture in the new Createxxx method applied to CreateBox. Will get round to multi-materials when I am exhausted from this method. Thank you for the support.


@Jerome understanding is growing. I used the playground to create two Cubes, one uses your CreatePolyhedron method and one the new CreateBox method. Checking the VertexData for both shows the difference.


In the CreateBox method there are 24 positions and 24 normals, since each of the 8 vertices is listed 3 times; once for each face it belongs to and each of the 6 normals (one per face) is listed 4 times once for each vertex on that face. The UVs are also listed 24 times each one matched in 1-1 correspondence with the positions.


In the CreatePolyhedron method there are 8 positions and 8 normals one for each of the 8 vertices. The normals are now not normal to a face but are formed from the 3 face normals at each vertex. This method of storing the VertexData is more efficient since it uses a third less memory but does not contain sufficient data for the uvs to be applied.


http://www.babylonjs-playground.com/#1H7L5C#29 shows the two cubes and the normals at the vertices. The lower one is made using the CreateBox method and all faces contain the image in a proper way.


Interestingly the use of .convertToFlatShadedMesh(); produces 36 positions since it creates two facets per face and then 4 vertices belong to 6 facets and 4 vertices belong to 4 facets and there are 36 normals. The UV array also has 36 entries but I presume they do not now match correctly the positions.


Remove the comment from line 70 in the playground above and see the difference.


So perhaps if I can just work out which uv goes with which position I might get somewhere.

Link to comment
Share on other sites

Waaoww... When I said it was hard for me to explain all of this in english !

You found out everything I wanted to tell you by yourself :)  Very good study !


So you understand now why you can't reproduce the same image on each box side if its vertices belong to many faces at the same time.

And why you can't set all the UV at mesh creation if you convert it to a flat shaded one just after (vertices added after).


Actually, you need to have these extra vertices to hold the image-per-face uvs.

So, two options :

#1 : you create the mesh as you did formerly, you convert it to a flat shaded one, then you try to retrieve all the new vertices and how they are related in order to the initial to set the UVs. Not that simple.

#2 : you change the way the mesh is created : instead of re-using some vertices, you add new ones when building the positions and indices array.

In this case, you know exactly where all your vertices are and how they are linked.

And it's quite easy to do : when a vertex is yet used in the faces array, just create a new one with the same values and reference it instead.

In brief, to construct the geometry of each polyhedron like the current BJS box.


I personally prefer the second method.

I think I will implement it in the future CreatePolyhedron method because the way the normals are computed with computeNormals() on common vertices will get to many light artifacts on these kinds of meshes : many edges, many vertices, many flat planar sides, angles lower than 120° ... so many bad candidates for the magic normal computation.

That's also why I converted all of them into flat shaded ones in the PG example.

Link to comment
Share on other sites

Still needs some work will fail if any polyhedra has a face with more than 4 sides and faceUV not consistent with yours, is array of array not array of Vector4. So still some way to go for a general CreatePolyhedron

Link to comment
Share on other sites

I had a look at your code.

So you don't re-use the vertices and create new positions what is, imho, the right thing to do.


I can see also that you compute your own normals and check on what plane side they should be set for each facet. Usually, the vertices are rightly ordered in the face array by the developer to create all the facets oriented the same way, so this check isn't necessary. Maybe it's not the case with this JSON file... I don't know.

If they are, you don't need to reimplement your own normal computation in this case, ComputeNormals() will do the job as expected because you only create independant planar polygons.


Your algo may work with any polyhedrons imho for the positions/indices aspect. I would have done the same, with ComputeNormals() so far.


For the UV per side, my lead is :

A texture is a rectangle, ok ?  or a square... it is to say a 4-side polygon

The sides of a polyhedron are polygons. We can even assume they are quite regular polygons :triangles, rectangles/squares, pentagons, hexagons, etc


So except for a triangle or a rectangle polygon (polyhedron sides) which can directly fit into a rectangle texture, I would consider the circle within the square texture and set the (face) polygon on this circle (circumcircle) to compute the UV.

Not easy to explain in english once more ...

But have a look please at how the UVs are set in CreateDisc() (this is actually a createPolygon) : https://github.com/BabylonJS/Babylon.js/blob/master/src/Mesh/babylon.mesh.vertexData.ts#L1288

Link to comment
Share on other sites

I guess I will try next week to implement in BJS something  like :

CreatePolyhedron(name, {type: polyhedronType, size: number, custom: polygonObject}, scene);

polyhedronType will be one of the few polyhedrons provided in the core. Few only (tatrehedrons, etc) because we don't want too much code in the core.

If polyhedronType is undefined, then a custom polyhedron object will be expected. These objects will be provided in an extension library, easy to use : just copy/paste the needed variable in your code.

Example :

copy this line from the lib your code :

var cubo = {"name":"Cuboctahedron","category":["Archimedean Solid"],"vertex":[[0,0,1.154701],[1,0,0.5773503],[0.3333333,0.942809,0.5773503],[-1,0,0.5773503],[-0.3333333,-0.942809,0.5773503],[1,0,-0.5773503],[0.6666667,-0.942809,0],[-0.6666667,0.942809,0],[0.3333333,0.942809,-0.5773503],[-1,0,-0.5773503],[-0.3333333,-0.942809,-0.5773503],[0,0,-1.154701]],"edge":[[0,1],[1,2],[2,0],[0,3],[3,4],[4,0],[1,6],[6,5],[5,1],[2,8],[8,7],[7,2],[3,7],[7,9],[9,3],[4,10],[10,6],[6,4],[5,11],[11,8],[8,5],[9,11],[11,10],[10,9]],"face":[[0,1,2],[0,3,4],[1,6,5],[2,8,7],[3,7,9],[4,10,6],[5,11,8],[9,11,10],[0,2,7,3],[0,4,6,1],[1,5,8,2],[3,9,10,4],[5,6,10,11],[7,8,11,9]]};

then :

CreatePolyhedron(name, {custom: cubo}, scene);
Link to comment
Share on other sites

Thanks for you advice, have replaced my normals code with computeNormals. In the end I did not use the CreateDisc idea for the uvs since I needed to find the middle for each face and it gave one more position per face.


My triangles for the uvs all start from one vertex of the polygon face and go to the non-adjacent ones in turn. Having calculated the first vertex I rotate this round the polygon to get the positions of the others and then translate and scale.








What I am considering next is setting up an array spriteNum =[] and then put face numbers in the array and use something like

spriteRow = spriteNum[f] % hSpriteNb;spriteColumn = Math.floor(spriteNum[f] / hSpriteNb); 

I'll look forward to your CreatePolyhedron


Link to comment
Share on other sites

Really nice  :)


The middle of each face is quite easy to compute : barycenter.

You just need the middle location to compute then the location of each vertex of the polygon, as you need with your own rotation.

I don't think that you need to add this middle point to the positions array : all you want to get is the 2D coordinates of your (face) polygon vertices on your texture.


The circumcircle method is easy (probably not the most optimized in terms of texture surface cropping) if you have to deal with polygons with more than 4 sides, what is not your case if you want to keep indeed your code dedicated to this kind of polyhedrons having only triangular and squared faces.

This was just a suggestion in order to generalize the code to any polyhedron types.


I like a lot the 126 samples with the sprites  ;)

Link to comment
Share on other sites

Please forget what I said about the barycenter. Your technique works obviously by rotating in turn from a starting point !

I didn't read it well yesterday.


Actually, I just did the same in CreateDisc() but with another rotation because of a different indices order.


I like yours, I will borrow (steal) it for the CreatePolygon implementation ;)

Link to comment
Share on other sites

thank you


So I borrowed a part of you code, injected it in my original code (which was yet a port from Stemkosky's one in threejs) and cleaned up everything.



(switch the editor off to display the names)


The main program starts from the line 126.

It's not TS for now (ported very soon), but a quite achieved JS draft.

The signature is :

CreatePolyhedron("name", options, scene);

details of the option properties (all optional) :

  • type : not implemented yet, will be one of the polyhedron type provided in the core. This will be documented.
  • custom: an expected polyhedron object if you want another type than the core ones, a copied/pasted variable from a extension file that will be online
  • size: no need to explain
  • sizeX: the same only on X
  • sizeY: only on Y
  • sizeZ: only on Z
  • faceUV: array(nbfaces) the same as for CreateBox => set your own image per face
  • faceColors: array(nbfaces) the same as for CreateBox => set your own color per face, as you can see in the example

Same example with faceUV : http://www.babylonjs-playground.com/#21QRSK#5

Both (colors and faceUV) : http://www.babylonjs-playground.com/#21QRSK#6

Link to comment
Share on other sites

The ones that you break out into the extension, I really hope that they are their OWN mesh subclass.  BTW, do you have one that sort of looks like it could be a mesh proxy for a bone?  See, http://wiki.blender.org/uploads/7/73/Manual-Part-I-Quick51.2.56.png  I know bones do not have a length, well in BJS anyway ;)

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.

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.


  • Recently Browsing   0 members

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