question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Problem with OBJ/MTL loader + THREE.Geometry().fromBufferGeometry() combination

See original GitHub issue

Hi!

We’re programming an education app to visualize orthogonal projections of polyhedra. We think we’ve found a bug with the OBJ/MTL + THREE.Geometry().fromBufferGeometry() combination. Problem is the code works well with some 3D data but fails with other 3D data. We don’t know why this happens because the code is the same and the 3D data seems ok for us.

We use three steps:

  1. First, we load the polyhedron using the provided OBJ/MTL loader.
  2. Next, we use THREE.LineSegments() to get their edges.
  3. Next, use THREE.Geometry().fromBufferGeometry() to get geometries from the edges.

Here is an example that works well: http://www.im-uff.mat.br/tmp/how/simple-01.html (all geometric information is passed, axes and projections are determined correctly).

p9wxx

Now, here is an example where the code fails with THREE.Geometry().fromBufferGeometry() returning null: http://www.im-uff.mat.br/tmp/how/simple-02.html (as result, the orthogonal projections were not computed).

tzhlr

Below we’ve included all the source files.

Thanks in advance, Humberto.

OBJ file

####
#
# OBJ File Generated by Meshlab
#
####
# Object johnson-j01-square-pyramid.obj
#
# Vertices: 5
# Faces: 6
#
####
mtllib ./johnson-j01-square-pyramid.obj.mtl

vn -0.000270 -0.281574 -0.959539
v -0.444457 1.160279 -7.902924 
vn -0.996609 -0.000878 0.082282
v -7.610548 -2.453604 -0.243687
vn -0.105861 0.933870 0.341586
v -0.664351 5.860701 2.143697
vn 0.968495 0.248892 0.008435
v 7.942724 -0.476746 -0.828161
vn 0.125332 -0.821682 0.555996
v 0.776633 -4.090629 6.831076
# 5 vertices, 0 vertices normals

usemtl material_0
f 5//5 2//2 1//1
f 5//5 1//1 4//4

usemtl material_1
f 2//2 5//5 3//3
f 1//1 2//2 3//3
f 4//4 1//1 3//3
f 5//5 4//4 3//3
# 6 faces, 0 coords texture

# End of File

MTL file

#
# Wavefront material file
# Converted by Meshlab Group
#

newmtl material_0
Ka 0.200000 0.200000 0.200000
Kd 1.000000 0.000000 0.000000
Ks 1.000000 1.000000 1.000000
Tr 1.000000
illum 2
Ns 0.000000

newmtl material_1
Ka 0.200000 0.200000 0.200000
Kd 0.000000 0.000000 1.000000
Ks 1.000000 1.000000 1.000000
Tr 1.000000
illum 2
Ns 0.000000

HTML+JavaScript file:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Orthogonal Projections 2</title>

    <!-- JavaScript Libraries -->

    <script src="threejs.r91/build/three.js"></script>
    <script src="threejs.r91/examples/js/controls/TrackballControls.js"></script>
    <script src="threejs.r91/examples/js/loaders/DDSLoader.js"></script>
    <script src="threejs.r91/examples/js/loaders/MTLLoader.js"></script>
    <script src="threejs.r91/examples/js/loaders/OBJLoader.js"></script>

    <!-- ThreeJS Code -->
    <script type="text/javascript">
        var transparency_value = 0;

        function MakeQuadrilateral(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4)
        {
           var geo = new THREE.Geometry();

            // generate vertices
            geo.vertices.push( new THREE.Vector3(x1,y1,z1));
            geo.vertices.push( new THREE.Vector3(x2,y2,z2));
            geo.vertices.push( new THREE.Vector3(x3,y3,z3));
            geo.vertices.push( new THREE.Vector3(x4,y4,z4));


            geo.faces.push( new THREE.Face3(0,1,2));
            geo.faces.push( new THREE.Face3(0,2,3));


            // Return the geometry object
            return geo;
        }


       // Adds text to the screen
        function makeSpriteText(text)
        {
            var canvas = document.createElement('canvas');
            var size = 256; // CHANGED
            canvas.width = size;
            canvas.height = size;
            var context = canvas.getContext('2d');
            context.fillStyle = '#ffffff'; // CHANGED
            context.textAlign = 'center';
            context.font = '48px Times';
            context.fillText(text, size / 2, size / 2);

            var amap = new THREE.Texture(canvas);
            amap.needsUpdate = true;

            var mat = new THREE.SpriteMaterial({
                map: amap,
                transparent: false,
                useScreenCoordinates: false,
                color: 0x0000ff // CHANGED
            });

            var sp = new THREE.Sprite(mat);
            return(sp);
        }


        // Check capabilities, and start if sufficient
        var hasWebGl = (function() {try {return !! window.WebGLRenderingContext &&
                !! document.createElement('canvas').getContext('experimental-webgl');}
        catch(e){return false;}})();
        var hasCanvas = !! window.CanvasRenderingContext2D; // Converts value to boolean
        var hasVibration = navigator.vibrate;


        window.onresize = function(event)
        {
            if (loaded == true)
            {
                loaded = false;

                context0.controls = new THREE.TrackballControls(context0.camera, context0.renderer.domElement);
                context0.controls.target.set(0, 0, 0);
                context0.controls.noZoom = false;
                context0.controls.noPan = true;
                context0.controls.rotateSpeed = 1.2;
                context0.controls.dynamicDampingFactor = 0.2;
                context0.controls.staticMoving = false;
            }
        }

        if (hasCanvas)
        {
            document.addEventListener( "DOMContentLoaded", init, false);
        } // End of if()

        function init()
        {
            // Setup idiom
            document.getElementById("msgwebglcontext0").innerHTML = "<br>";

            /* spawns the objects, scenes, cameras, renderers etc. */
            context0 = {color: 0xccff33, name: "0", width: 440, height: 440, factor: 30, arrowLength: 25};

            // set the scene
            if (hasWebGl)
            {
                context0.renderer = new THREE.WebGLRenderer({alpha: true, antialias: true });
            }
            else
            {
                context0.renderer = new THREE.CanvasRenderer({alpha: true, antialias: true });
            }
            context0.renderer.setSize(context0.width, context0.height);

            // Add the renderer to the document.
            // This should be called before THREE.TrackballControls().
            document.getElementById("webglcontext0").appendChild(context0.renderer.domElement);

            context0.scene = new THREE.Scene();
            context0.camera = new THREE.PerspectiveCamera(20, context0.height/context0.width, 2, 10000);   // 20: small values cause z-buffer fighting
            context0.camera.position.z = 70;
            context0.camera.position.x = 70;
            context0.camera.position.y = 70;
            context0.scene.add(context0.camera);
            context0.controls = new THREE.TrackballControls(context0.camera, context0.renderer.domElement);
            context0.controls.target.set(0, 0, 0);
            context0.controls.noZoom = false;
            context0.controls.noPan = true;

            // Model
            var onProgress = function ( xhr ) {
                if ( xhr.lengthComputable ) {
                    var percentComplete = xhr.loaded / xhr.total * 100;
                    console.log( Math.round(percentComplete, 2) + '% downloaded' );
                }
            };
            var onError = function ( xhr ) { };
            THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
            var mtlLoader = new THREE.MTLLoader();
            mtlLoader.setPath( 'obj/' );
            mtlLoader.load( 'johnson-j01-square-pyramid.obj.mtl', function( materials ) {
                materials.preload();
                var objLoader = new THREE.OBJLoader();
                objLoader.setMaterials( materials );
                objLoader.setPath( 'obj/' );
                objLoader.load( 'johnson-j01-square-pyramid.obj', function ( object )
                            {

                                        object.traverse(function (child)
                                        {
                                            if (child instanceof THREE.Mesh)
                                            {
                                                // Polyhedra
                                                child.name = "pdp-faces";
                                                context0.scene.add(child);
                                                var geometry = new THREE.Geometry().fromBufferGeometry( child.geometry );
                                                console.log('From loaded OBJ: ' + geometry.vertices.length);

                                                if ( Array.isArray( child.material ) )
                                                {
                                                    var n = child.material.length;
                                                    for (i = 0; i < n; i++)
                                                    {
                                                       child.material[i].transparent = true;
                                                       child.material[i].opacity = (1 - transparency_value/100.0);
                                                    };
                                                }
                                                else
                                                {
                                                       child.material.transparent = true;
                                                       child.material.opacity = (1 - transparency_value/100.0);
                                                }

                                                // Edges
                                                var edges = new THREE.LineSegments(new THREE.EdgesGeometry(child.geometry), new THREE.LineBasicMaterial( {color: 0x000000}) );
                                                edges.name = "pdp-edges";
                                                context0.scene.add(edges);

                                                // Vertices
                                                var geometry = new THREE.Geometry().fromBufferGeometry( edges.geometry );
                                                console.log('Geometry vertices length: ' + geometry.vertices.length);
                                                var vertices = [];
                                                var isNew;
                                                var tolerance = 0.0000001;
                                                if (geometry.vertices.length > 0)
                                                {
                                                    vertices.push(new THREE.Vector3(geometry.vertices[0].x, geometry.vertices[0].y, geometry.vertices[0].z));
                                                }
                                                for (i = 1; i < geometry.vertices.length; i++)
                                                {
                                                    l = vertices.length;
                                                    isNew = true;
                                                    for (j = 0; j < l; j++)
                                                    {
                                                        var d = geometry.vertices[i].distanceTo(vertices[j]);
                                                        if (d < tolerance)
                                                        {
                                                            isNew = false;
                                                        }
                                                    }
                                                    if (isNew == true)
                                                    {
                                                        vertices.push(new THREE.Vector3(geometry.vertices[i].x, geometry.vertices[i].y, geometry.vertices[i].z));
                                                    }
                                                }

/*
                                                alert('Unique vertices: ' + vertices.length);

                                                for (i = 0; i < vertices.length; i++)
                                                {
                                                    alert(vertices[i].x);
                                                }
*/
                                                // https://github.com/stemkoski/stemkoski.github.com/blob/master/Three.js/Polyhedra.html
                                                // https://stemkoski.github.io/Three.js/Polyhedra.html

                                                // Fit screen
                                                child.geometry.computeBoundingSphere();
                                                var fov = context0.camera.fov * ( Math.PI / 180 );
                                                var objectSize = child.geometry.boundingSphere.radius;
                                                var distance = 0.7*Math.abs( objectSize / Math.sin( fov / 2 ) );
                                                context0.camera.position.z = 2.5*distance;
                                                context0.camera.position.x = 2.5*distance;
                                                context0.camera.position.y = 2.5*distance;

                                                // PLANE PROJECTIONS
                                                var geometry_xy =  new THREE.Geometry().fromBufferGeometry( edges.geometry );
                                                var geometry_xz =  new THREE.Geometry().fromBufferGeometry( edges.geometry );
                                                var geometry_yz =  new THREE.Geometry().fromBufferGeometry( edges.geometry );
                                                geometry_xy.dynamic = true;
                                                geometry_xz.dynamic = true;
                                                geometry_yz.dynamic = true;
                                                for (i = 0; i < geometry_xy.vertices.length; i++)
                                                {
                                                    var xOld = geometry_xy.vertices[i].x;
                                                    var yOld = geometry_xy.vertices[i].y;
                                                    var zOld = geometry_xy.vertices[i].z;
                                                    geometry_xy.vertices[i].set(xOld, yOld, -distance/3);
                                                    geometry_xz.vertices[i].set(xOld, -distance/3, zOld);
                                                    geometry_yz.vertices[i].set(-distance/3, yOld, zOld);
                                                    // console.log(geometry_xy.vertices[i].x + ' ' + geometry_xy.vertices[i].y + ' ' + geometry_xy.vertices[i].z);
                                                }
                                                geometry_xy.verticesNeedUpdate = true;
                                                geometry_xz.verticesNeedUpdate = true;
                                                geometry_yz.verticesNeedUpdate = true;


                                                var edges_xy = new THREE.LineSegments(geometry_xy, new THREE.LineBasicMaterial( {color: 0x000000}) );
                                                var edges_xz = new THREE.LineSegments(geometry_xz, new THREE.LineBasicMaterial( {color: 0x000000}) );
                                                var edges_yz = new THREE.LineSegments(geometry_yz, new THREE.LineBasicMaterial( {color: 0x000000}) );
                                                edges_xy.name = "pdp-edges-xy";
                                                edges_xz.name = "pdp-edges-xz";
                                                edges_yz.name = "pdp-edges-yz";
                                                context0.scene.add(edges_xy);
                                                context0.scene.add(edges_xz);
                                                context0.scene.add(edges_yz);


                                                // VERTICES
                                                var vertexGeometry = new THREE.SphereGeometry(child.geometry.boundingSphere.radius/35.0, 12, 6 );
                                                var vertexMaterial = new THREE.MeshBasicMaterial( { color: 0x000000 } );
                                                var vertexSingleMesh = new THREE.Mesh( vertexGeometry );
                                                var vertexAmalgam = new THREE.Geometry();
                                                for (var i = 0; i < vertices.length; i++)
                                                {
                                                    var vMesh = vertexSingleMesh.clone();
                                                    vMesh.position.set(vertices[i].x, vertices[i].y, vertices[i].z);
                                                    THREE.GeometryUtils.merge( vertexAmalgam, vMesh );
                                                }
                                                var vertexMesh = new THREE.Mesh( vertexAmalgam, vertexMaterial );
                                                vertexMesh.name = "pdp-vertices";
                                                context0.scene.add(vertexMesh);
                                                context0.scene.getObjectByName("pdp-vertices").visible = false;


                                                // PLANES
                                                var d = distance/3;
                                                var xyGeometry = MakeQuadrilateral( d, -d, -d,
                                                                                    d,  d, -d,
                                                                                   -d,  d, -d,
                                                                                   -d, -d, -d);
                                                var xyPlane = new THREE.Mesh(xyGeometry, new THREE.MeshBasicMaterial({color: 0x090909, side: THREE.DoubleSide, opacity: 0.2, transparent: true }));
                                                xyPlane.name = "xyPlane";
                                                context0.scene.add(xyPlane);

                                                var d = distance/3;
                                                var xzGeometry = MakeQuadrilateral(  d, -d,  d,
                                                                                     d, -d, -d,
                                                                                    -d, -d, -d,
                                                                                    -d, -d,  d);
                                                var xzPlane = new THREE.Mesh(xzGeometry, new THREE.MeshBasicMaterial({color: 0x090909, side: THREE.DoubleSide, opacity: 0.2, transparent: true }));
                                                xzPlane.name = "xzPlane";
                                                context0.scene.add(xzPlane);

                                                var d = distance/3;
                                                var yzGeometry = MakeQuadrilateral( -d, -d,  d,
                                                                                    -d, -d, -d,
                                                                                    -d,  d, -d,
                                                                                    -d,  d,  d);
                                                var yzPlane = new THREE.Mesh(yzGeometry, new THREE.MeshBasicMaterial({color: 0x090909, side: THREE.DoubleSide, opacity: 0.2, transparent: true }));
                                                yzPlane.name = "yzPlane";
                                                context0.scene.add(yzPlane);


                                                // AXES
                                                var xVector = new THREE.ArrowHelper(new THREE.Vector3(1, 0, 0),
                                                                new THREE.Vector3(-d, -d, -d ),
                                                                2*d + 0.5*d, 0x0000ff, 0.2*d, 0.1*d);
                                                xVector.name = "xVector";
                                                xVector.line.visible = true;
                                                xVector.cone.visible = true;
                                                context0.scene.add( xVector );
                                                var xLabel = makeSpriteText("y");
                                                xLabel.name = "xLabel";
                                                xLabel.position.set(d + 0.7*d, -d, -d);
                                                xLabel.scale.set( d, d, d );
                                                context0.scene.add(xLabel);

                                                var yVector = new THREE.ArrowHelper(new THREE.Vector3(0, 1, 0),
                                                                new THREE.Vector3(-d, -d, -d),
                                                                2*d + 0.5*d, 0x0000ff, 0.2*d, 0.1*d);
                                                yVector.name = "yVector";
                                                yVector.line.visible = true;
                                                yVector.cone.visible = true;
                                                context0.scene.add(yVector );
                                                var yLabel = makeSpriteText("z");
                                                yLabel.name = "yLabel";
                                                yLabel.position.set(-d, d + 0.6*d, -d);
                                                yLabel.scale.set( 10, 10, 10 );
                                                context0.scene.add(yLabel);

                                                var zVector = new THREE.ArrowHelper(new THREE.Vector3(0, 0, 1),
                                                                new THREE.Vector3(-d, -d, -d),
                                                                2*d + 0.5*d, 0x0000ff, 0.2*d, 0.1*d);
                                                zVector.name = "zVector";
                                                zVector.line.visible = true;
                                                zVector.cone.visible = true;
                                                context0.scene.add(zVector );
                                                var zLabel = makeSpriteText("x");
                                                zLabel.name = "zLabel";
                                                zLabel.position.set(-d, -d, d + 0.7*d);
                                                zLabel.scale.set( 10, 10, 10 );
                                                context0.scene.add(zLabel);

                                            }

                                        });

                }, onProgress, onError );
            });



            // var ambLight = new THREE.AmbientLight(0x404040);
            // context0.scene.add(ambLight);
            context0.light = new THREE.DirectionalLight(0xffffff, 1);
            context0.light.position = context0.camera.position;
            context0.scene.add(context0.light);



            // Run
            context0.camera.updateProjectionMatrix();
            render();
            animate();

            loaded = true;
        } // End of init()

        function animate()
        {
            /* One animation tick */
            requestAnimationFrame(animate);
            context0.controls.update();
            render();
        } // End of animate()

        function render()
        {
            /* renders our little scene */
            context0.renderer.render(context0.scene, context0.camera);
        } // End of render()


</script>

</head>
<body> <!--    <body onload='disableScroll();'>         -->
<center>
<span id="webglcontext0" style="width:410px; height:50px; display: table-cell; text-align:center; vertical-align: middle; border-style: solid; border-width: 1px;"></span>
<div id="msgwebglcontext0" style="text-align:center; display: table; margin-left: -3px;">
<span style="width:743px; height:30px; display: table-cell; text-align:justify; padding:10px; vertical-align: middle; border-style: solid; border-width: 1px;">
Por favor, espere o navegador carregar a página. Caso
    isto já tenha acontecido e o applet abaixo não executou, isto significa que seu navegador parece não suportar WebGL ou esta opção não está habilitada.
Em caso de dúvidas, entre em contato conosco pelo e-mail:
<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#99;&#111;&#110;&#116;&#101;&#117;&#100;&#111;&#115;&#100;&#105;&#103;&#105;&#116;&#97;&#105;&#115;&#64;&#105;&#109;&#46;&#117;&#102;&#102;&#46;&#98;&#114;">&#99;&#111;&#110;&#116;&#101;&#117;&#100;&#111;&#115;&#100;&#105;&#103;&#105;&#116;&#97;&#105;&#115;&#64;&#105;&#109;&#46;&#117;&#102;&#102;&#46;&#98;&#114;</a>.
</span>
</div>
</center>
</body>
</html>
Three.js version
  • Dev
  • r91
Browser
  • All of them
  • Chrome
  • Firefox
  • Internet Explorer
OS
  • All of them
  • Windows
  • macOS
  • Linux
  • Android
  • iOS
Hardware Requirements (graphics card, VR Device, …)

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:5

github_iconTop GitHub Comments

1reaction
WestLangleycommented, Apr 7, 2018

@hjbortol You need to answer that question yourself by stepping through with a debugger.

My guess is the method expects the length of the vertices array to be a multiple of 3 (as if rendering a mesh). In the case of edges or lines, that may, or may not, be the case.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Converting BufferGeometry to Geometry with FBXLoader in ...
I wanted to use var geometry = new THREE.Geometry().fromBufferGeometry( bufferGeometry ); to fix this, but I don't seem to build a mesh in...
Read more >
Three.js + TypeScript + MTL + OBJ + MTL + WTF
It describes a problem, which is that all of the Loader classes use BufferGeometry, which does not allow for the changes we need...
Read more >
Load a 3D OBJ + MTL model with Three.js
Import OBJLoader and MTLLoader classes. When a 3D object is modeled in an external tool such as Blender, it is possible to export...
Read more >
OBJ/MTL Loader - threejs - Google Sites
The function (third argument to loader.load) is the onLoad callback (asynchronous), called when both resource loads are completed. One thing to notice is...
Read more >
[Solved]-Three.js OBJ loader not working on codepen?
Your server does not allow you to access those object files. I'm getting CORS error from your server. If load them from the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found