Jump to content

Babylon Js UV Structer not Match With .Obj Format


NasimiAsl
 Share

Recommended Posts

let me explain with sample:


 


vertiex :


 


p1: 0,0,0                                  uv : 0.0,0.0


p2: 0,0,1                                  uv : 0.5,0.0


p3: 1,0,0 : face 1(p1,p2,p3)     uv : 0.0,0.5


p4: 1,0,1 : face 2(p2,p3,p4)     uv : 0.5,0.5


 


everything is ok to p4


so i want fill back face with out append more vertex


 


so 


 


face 3 (p4,p2,p3)   :   i cant set any new uv 


face 4 (p3,p1,p2)   :   *** same problem  


 * in babylonjs i can set uv per  vertex  but in .obj format i have more uvs per vertex


Link to comment
Share on other sites

i apologize for bad writing .

 

we have a simple plan with 4 point  like  

 

vertices :  [{x:0,y:0,z:0}, {x:1,y:0,z:0} , {x:1,y:0,z:1} , {x:0,y:0,z:1}]

 

faces : [{ a: 0,b:1,c:2}  , { a:1,b: 2, c: 3 } ]   // two face for up side of plan

 

uvs :   [ [0,0], [0.5,0], [0.5,0.5] , [0,0.5] ]  //  per vertex 

 

now i want add back side without append any vertex

 

so push two face like this :    faces .push({ a: 3,b:1,c:2}  , { a:0,b: 2, c: 1 });

 

i fill UVS from 0 to 0.5 for Up Side of Plan now i want continue  UV from 0.5 to 1.0

 

but when i push new uvs nothing change in my geometry and back side have uv like up side

 

 

* important we need build this plan with 4 points.

           

 

Link to comment
Share on other sites

UVs are per vertex.

 

3 vertices <=> 3 UV pairs.

 

If you need to stretch your texture backside with other UV values than the ones used for the front side, you have then to add three more vertices

 

In your example, you have only 3 vertices ... whatever the number of faces they are used, or re-used, in.

So only the 3 first coordinate pairs in your UV array will be be used.

 

http://www.html5gamedevs.com/topic/12467-double-sided-mesh/?p=71736

Link to comment
Share on other sites

thanks jerome

 

i want import .obj geometry i dont want change the vertices array but i have problem in last face  .  some vertex in last faces have uvs but need a new uvs .

i want know why uvs defined per vertex but in   3d software it is not like this .

 

i thinks in three js it is not like this so maybe we can correct babylonjs core?

Link to comment
Share on other sites

I guess ThreeJS and BJS behave the same way on this topic, because it's just the WebGL / OpenGL rule : a normal per vertex and a UV per vertex only.

 

You could get your vertex array and face array,  instanciate a VertexData object  andt hen use the (public and private) methods :

ComputeNormals(positions, indices, normals) : re-computes the normals array given positions and indices

and 

_ComputeSides(sideOrientation, positions, indices, normals, uvs) : re-computes vertex, indices, normals and uv arrays given a sideOrientation value.

This method will add the missing backside vertices, faces, normals and uvs for you if you want a double sided mesh.

 

sideOrientation values are detailed here in the Side Orientation part: http://doc.babylonjs.com/page.php?p=22011

 

 

 

 

https://github.com/BabylonJS/Babylon.js/blob/master/Babylon/Mesh/babylon.mesh.vertexData.ts#L1162

Link to comment
Share on other sites

Well, maybe you could edit your post and wrap the code in the code tag (select the text and click on "<>" in the tool bar) so it will be easier to read.

I just can't read in this function what gives the ability to have more and an uv per vertex so far.

 

here is a part of the khronos (the guys who made Wegl) group forum :

https://www.khronos.org/message_boards/showthread.php/7063-Texture-coordinates-per-face-index-instead-of-per-vertex

No - you should duplicate the vertex if the texture coord is different (note also that your surface normal vector will also be different - which gives you two reasons to duplicate it).

 

btw, there's a uv2 property in the VertexData object, but I don't know what is for ...

Link to comment
Share on other sites

I think you can delete the code from your post, if you wish.  I think the same code is here:

 

http://threejs.org/examples/js/loaders/OBJLoader.js

 

there seems to be ANOTHER version... for Ejecta.

 

https://gist.github.com/phoboslab/5964733

 

*shrug*

 

I think we need to write a book called "The Last UV".  The last uv has troubled quite a few people.  :)

 

The book will be about a team of relay racers... with troubles.  They keep making the mistake of trying to hand the baton to the first runner... when the last runner reaches the finish line.  The first runner says "NO NO, I already gave that away to somebody".  (Wingnut trying comedy, failing)

 

Maybe the last uv needs to be treated like a VALUE that is "averaged" with the first uv.  The first uv gets adjusted to "split the difference", and then the last uv values are discarded.  *shrug*  Just guessing.  :)

Link to comment
Share on other sites

uv2 just for  use 2 type of uv map for geo in shader and can set 2 texture .

 

we need .obj loader for babylonjs anyway  i convert that  but i have problem with last uv

 

i can find last faces but i want resolve that without change vertices of geometry 

any append new vertex make problem for smooth property so help me to resolve that.

Link to comment
Share on other sites

Hi!

Trying to understand your initial question - 

You have 4 vertices, defined with xyz on each. Each has a uv (v and u).

So your positions and usv vector look like this (babylon format):

positions = [0,0,0,0,0,1,1,0,0,1,0,1] // 12 itemsuvs = [0,0,0,0.5,0.5,0,0.5,0.5] // 8 items

indices vector (the faces) will look like this:

indices = [0, 1, 2, 0, 2, 3] 

This created a plane.

Now, If I understand you fully, you wanted to "reuse" the positions and add a new uv definition.

In OBJ you can reference the location of both position and uv in the faces definition :

f 1/2 2/1 3/2

Which avoids having redundant uv definitions.

The babylon format works differently. It is rather redundant, but wonderful for performance. If you want to add a new uv to an existing position definition, you will have to duplicate this position and add the uvs definition at the correct position. This is due to the indices definition. Not like obj, indices is only 1 number, meaning 1 position of the array. You can't reference a specific uv definition and a different positions definition.

 

So - to do what you wanted to do, you would have to add a new position to the positions (which is a duplicate of an existing position) and add the uv:

 

(let's say you want to add a new uv definition for the 0,0,0 positions)

positions = [0,0,0,0,0,1,1,0,0,1,0,1, 0,0,0] //15 itemsuvs = [0,0,0,0.5,0.5,0,0.5,0.5, 0.5,1.0] // 10 items

and now create a new face using the 4th "definition location"

indices = [0, 1, 2, 0, 2, 3, 1, 2, 4] 

I am not sure if I understood your question correctly, I hope this clears things up a bit.

Link to comment
Share on other sites

thanks 

RaananW

 

we wont add a new position  actually this problem is not for a simple plan we want have solution for all object .

 

Main Problem : We have some vertices in the  .obj format that have many uvs but in babylon js we have one uv(two number) per vertex.  

Link to comment
Share on other sites

I think you can delete the code from your post, if you wish.  I think the same code is here:

 

http://threejs.org/examples/js/loaders/OBJLoader.js

 

there seems to be ANOTHER version... for Ejecta.

 

https://gist.github.com/phoboslab/5964733

 

*shrug*

 

I think we need to write a book called "The Last UV".  The last uv has troubled quite a few people.  :)

 

The book will be about a team of relay racers... with troubles.  They keep making the mistake of trying to hand the baton to the first runner... when the last runner reaches the finish line.  The first runner says "NO NO, I already gave that away to somebody".  (Wingnut trying comedy, failing)

 

Maybe the last uv needs to be treated like a VALUE that is "averaged" with the first uv.  The first uv gets adjusted to "split the difference", and then the last uv values are discarded.  *shrug*  Just guessing.  :)

 

main question is why uvs set per vertex 1<=>1 ( who design this?) :))

Link to comment
Share on other sites

This is exactly what I meant in my post. And it's more than possible, Babylon supports creating such objects, naturally.

The obj representation of a face is more complex than babylon's. In obj you can choose the uv, v and normal for a single face vertex. Babylon requires you to duplicate the data.

Look at the first face in your txt file. 1/1/1, 2/2/2 etc. This is how Babylon works. You can't do

f 1/2/1 2/2/3, 1/4/1

in Babylon.

The amount of faces will eventually be the same. The size of the array that represents the vertices will be bigger.

If it's not clear, I'll try explaining a bit more thoroughly, not a problem.

Btw - maybe try the asset converter with your obj and see how the data is represented. http://www.babylonjs.com/converter.html

Link to comment
Share on other sites

As Raanaw said, the indices array is the reference.

The indices refers what vertex triplet should be used from the positions array to draw a face (triangle), but also what normal from normals array to apply to each vertex and what uv froms uvs array to associate to each vertex.

This is way the indices array is iterated (I guess).

 

If you have more positions elements than indices corresponding elements, they don't matter, they aren't used.

The same for normals and uvs. This is the reason why your extra uvs aren't applied.

If you have less positions elements than indices corresponding elements, it will raise a WebGL error in the console. 

You can therefore have less or none normals and uvs elements than indices elements (set to zero by default ?).

 

I don't really know if it is a BJS design choice or a WebGL limitation.

As they often talk about this need for vertex duplication in openGL forums, I guess it's related to WebGL itself, but don't sincerely know. 

 

 

Briefly, you still want to use BJS, you will have to duplicate vertices to have normals and uvs both side of your mesh.

 

 

This is why I just led you to this internal function doing the job :

 

_ComputeSides(sideOrientation, positions, indices, normals, uvs) : re-computes vertex, indices, normals and uv arrays given a sideOrientationvalue.

This method will add the missing backside vertices, faces, normals and uvs for you if you want a double sided mesh.

 

Link to comment
Share on other sites

thanks all 

 

i Submission and rewrite   objloader (objparser ) for babaylon js ( used  by threejs objloader http://threejs.org/e...rs/OBJLoader.js )  let me refactor  that  i put it here.

 

unbeautyfull code just for now create only geometry without material and normals

 

corrected lastUV    

function def(a, d) {    if (a != undefined && a != null) return (d != undefined && def != null ? a : true);    else        if (d != _null)            return (d != undefined && d != null ? d : false);    return null;}function n_1(ar) {    ar = def(ar, []);    if (!def(ar.length)) return null;    return ar[ar.length - 1];}objParse = function (text) {    var objects = [];    var correctLastUVIndexer = 0;    var uvsHelper = [];    var geometry = null;    var i0 = 0;    function parseVertexIndex(index) {        index = parseInt(index);        return index >= 0 ? index - 1 : index + vertices.length;    }    function parseUVIndex(index) {        index = parseInt(index);        return index >= 0 ? index - 1 : 1.0;    }    function add_face(a, b, c, uvs) {        a = parseVertexIndex(a - oldIndex);        b = parseVertexIndex(b - oldIndex);        c = parseVertexIndex(c - oldIndex);        if (def(n_1(objects).uvs[a * 2]) && n_1(objects).uvs[a * 2] != uvsHelper[parseUVIndex(uvs[0])].x && n_1(objects).uvs[a * 2 + 1] != uvsHelper[parseUVIndex(uvs[0])].y) {            correctLastUVIndexer++;            a = $3d.tools.push1(n_1(objects), { x: parseFloat(n_1(objects).positions[a * 3]), y: parseFloat(n_1(objects).positions[a * 3 + 1]), z: parseFloat(n_1(objects).positions[a * 3 + 2]) }, false);        }        if (def(n_1(objects).uvs[b * 2]) && n_1(objects).uvs[b * 2] != uvsHelper[parseUVIndex(uvs[1])].x && n_1(objects).uvs[b * 2 + 1] != uvsHelper[parseUVIndex(uvs[1])].y) {            b = $3d.tools.push1(n_1(objects), { x: parseFloat(n_1(objects).positions[b * 3]), y: parseFloat(n_1(objects).positions[b * 3 + 1]), z: parseFloat(n_1(objects).positions[b * 3 + 2]) }, false);            correctLastUVIndexer++;        }        if (def(n_1(objects).uvs[c * 2]) && n_1(objects).uvs[c * 2] != uvsHelper[parseUVIndex(uvs[2])].x && n_1(objects).uvs[c * 2 + 1] != uvsHelper[parseUVIndex(uvs[2])].y) {            c = $3d.tools.push1(n_1(objects), { x: parseFloat(n_1(objects).positions[c * 3]), y: parseFloat(n_1(objects).positions[c * 3 + 1]), z: parseFloat(n_1(objects).positions[c * 3 + 2]) }, false);            correctLastUVIndexer++;        }        $3d.tools.face3(n_1(objects),            a,            b,            c, { flip: !def(op.back, false) }        );        if (!def(n_1(objects).uvs[a * 2]))            n_1(objects).uvs[a * 2] = uvsHelper[parseUVIndex(uvs[0])].x;        if (!def(n_1(objects).uvs[a * 2 + 1]))            n_1(objects).uvs[a * 2 + 1] = uvsHelper[parseUVIndex(uvs[0])].y;        if (!def(n_1(objects).uvs[b * 2]))            n_1(objects).uvs[b * 2] = uvsHelper[parseUVIndex(uvs[1])].x;        if (!def(n_1(objects).uvs[b * 2 + 1]))            n_1(objects).uvs[b * 2 + 1] = uvsHelper[parseUVIndex(uvs[1])].y;        if (!def(n_1(objects).uvs[c * 2]))            n_1(objects).uvs[c * 2] = uvsHelper[parseUVIndex(uvs[2])].x;        if (!def(n_1(objects).uvs[c * 2 + 1]))            n_1(objects).uvs[c * 2 + 1] = uvsHelper[parseUVIndex(uvs[2])].y;    }    function handle_face_line(faces, uvs, normals_inds) {        uvs = def(uvs, [0, 0, 0, 0]);        if (faces[3] === undefined) {            add_face(faces[0], faces[1], faces[2], uvs);        } else {            add_face(faces[0], faces[1], faces[3], [uvs[0], uvs[1], uvs[3]]);            add_face(faces[1], faces[2], faces[3], [uvs[1], uvs[2], uvs[3]]);        }    }    // create mesh if no objects in text    if (/^o /gm.test(text) === false) {        geometry = $3d.tools.geometryBase();        objects.push(geometry);    }    // v float float float    var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;    // vn float float float    var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;    // vt float float    var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/;    // f vertex vertex vertex ...    var face_pattern1 = /f( +-?\d+)( +-?\d+)( +-?\d+)( +-?\d+)?/;    // f vertex/uv vertex/uv vertex/uv ...    var face_pattern2 = /f( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))?/;    // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...    var face_pattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/;    // f vertex//normal vertex//normal vertex//normal ...     var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/    //    var lines = text.split('\n');    var oldIndex = 0;    var oldLine = '';    var lastchar = '';    function newGeo() {        if (objects.length == 0 || n_1(objects).vertices.length > 0) {            oldIndex += objects.length > 0 ? n_1(objects).vertices.length - correctLastUVIndexer : 0;            correctLastUVIndexer = 0;            geometry = $3d.tools.geometryBase();            objects.push(geometry);        }    }    for (var i = 0; i < lines.length; i++) {        var line = lines[i];        line = line.trim();        var result;        if (line.length === 0 || line.charAt(0) === '#') {            continue;        } else if ((result = vertex_pattern.exec(line)) !== null) {            // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]            if (lastchar == 'g')                newGeo();            $3d.tools.push1(n_1(objects), { x: -1 * result[1], y: result[2], z: result[3] }, false);            lastchar = 'v';        } else if ((result = normal_pattern.exec(line)) !== null) {            // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]            // normals.push({ x: result[1], y: result[2], z: result[3] });            lastchar = 'n';        } else if ((result = uv_pattern.exec(line)) !== null) {            // ["vt 0.1 0.2", "0.1", "0.2"]            uvsHelper.push({ x: parseFloat(result[1]), y: parseFloat(result[2]) });            // uvs.push({ x: result[1], y: result[2] });            lastchar = 't';        } else if ((result = face_pattern1.exec(line)) !== null) {            // ["f 1 2 3", "1", "2", "3", undefined]            handle_face_line(                [result[1], result[2], result[3], result[4]]            );            lastchar = 'f';        } else if ((result = face_pattern2.exec(line)) !== null) {            // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined]            handle_face_line(                [result[2], result[5], result[8], result[11]], //faces                [result[3], result[6], result[9], result[12]] //uv            );            lastchar = 'f';        } else if ((result = face_pattern3.exec(line)) !== null) {            // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined]            handle_face_line(                [result[2], result[6], result[10], result[14]], //faces                [result[3], result[7], result[11], result[15]], //uv                [result[4], result[8], result[12], result[16]] //normal            );            lastchar = 'f';        } else if ((result = face_pattern4.exec(line)) !== null) {            // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined]            handle_face_line(                [result[2], result[5], result[8], result[11]], //faces                [], //uv                [result[3], result[6], result[9], result[12]] //normal            );            lastchar = 'f';        } else if (/^o /.test(line)) { // || /^g /.test(line)) {            if (line.replace('o', '').trim() != 'default') {                oldLine = line.replace('o', '').trim();                if (oldLine != '' && objects.length > 0)                    n_1(objects).refname = oldLine;            }            else oldLine = '';            newGeo();            lastchar = 'o';        }        else if (/^g /.test(line)) {            if (line.replace('g', '').trim() != 'default') {                oldLine = line.replace('g', '').trim();                if (oldLine != '' && objects.length > 0)                    n_1(objects).refname = oldLine;            }            else oldLine = '';            lastchar = 'g';        }        else if (/^usemtl /.test(line)) {            // material            // material.name = line.substring( 7 ).trim();            lastchar = 'u';        } else if (/^mtllib /.test(line)) {            // mtl file            lastchar = 'm';        } else if (/^s /.test(line)) {            // smooth shading             lastchar = 's';        } else {            // console.log( "THREE.OBJLoader: Unhandled line " + line );          }    }    return objects;}///  $3d.tools.geometryBase() : you need initialize in again for yourself///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //geometryBase: function (firstp, builder, exgeo, custom, op) { //      var geo = { //          faces: [], //          vertices: [], //          normals: [], //          positions: [], //          uvs: [] //      }; // //      if (!exgeo) //          exgeo = geo; // //      if (builder) { //          builder(firstp, exgeo); //      } // //      if (custom) { //          exgeo = custom(exgeo); //      } // //      return exgeo; //  }, ///// ///  $3d.tools.push1 = push vertex match by custom engin just push a vertics array [{x,y,z},..] and positions [x,y,x,..]////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// my push method //////////// push1: function (geo, p1, uv) {//  uv = def(uv, true);//  geo.vertices.push(p1); geo.positions.push(p1.x, p1.y, p1.z);//  if (uv) geo.uvs.push(0.0, 0.0);//  return geo.vertices.length - 1;//  },////  $3d.tools.face3/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  my push face method ////////////////////  face3: function (geo, p1, p2, p3, op) {//  if (!op) { op = {}; }//  function exch(p) { return (p.x || p.x == 0.0); }//  if (!op.uv) { op.uv = "0123"; }//// if (def(op.noUV, false)) op.uv = ".....";//function addUv(i) {//    if (op.uv[i].toString() == "0") geo.uvs.push(0.0, 0.0);//    if (op.uv[i].toString() == "1") geo.uvs.push(0.0, op.vp);//    if (op.uv[i].toString() == "2") geo.uvs.push(op.up, 0.0);//};//if (!op.up) { op.up = 1.0; }//if (!op.vp) { op.vp = 1.0; }//if (exch(p1)) { geo.vertices.push(p1); geo.positions.push(p1.x, p1.y, p1.z); addUv(0); op.p1Ind = geo.vertices.length - 1; }//if (exch(p2)) { geo.vertices.push(p2); geo.positions.push(p2.x, p2.y, p2.z); addUv(1); op.p2Ind = geo.vertices.length - 1; }//if (exch(p3)) { geo.vertices.push(p3); geo.positions.push(p3.x, p3.y, p3.z); addUv(2); op.p3Ind = geo.vertices.length - 1; }//if (op.p1Ind == null || op.p1Ind == undefined) op.p1Ind = p1;//if (op.p2Ind == null || op.p2Ind == undefined) op.p2Ind = p2;//if (op.p3Ind == null || op.p3Ind == undefined) op.p3Ind = p3;//if (!def(isInOp)) {//    if (op.flip) {//        geo.faces.push(op.p1Ind, op.p2Ind, op.p3Ind);//    }//    else {//        geo.faces.push(op.p1Ind, op.p3Ind, op.p2Ind);//    }//}//else {//    if (op.flip) {//        if (isInOp.a && isInOp.b && isInOp.c) geo.faces.push(op.p1Ind, op.p2Ind, op.p3Ind);//    }//    else {//        if (isInOp.a && isInOp.c && isInOp. geo.faces.push(op.p1Ind, op.p3Ind, op.p2Ind);//    }//}//isInOp = null;//return [op.p1Ind, op.p2Ind, op.p3Ind];//},

post-13038-0-86274400-1425220348.jpg

post-13038-0-92754100-1425220349.jpg

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...