Problem with OBJ/MTL loader + THREE.Geometry().fromBufferGeometry() combination
See original GitHub issueHi!
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:
- First, we load the polyhedron using the provided OBJ/MTL loader.
- Next, we use THREE.LineSegments() to get their edges.
- 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).
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).
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="mailto:conteudosdigitais@im.uff.br">conteudosdigitais@im.uff.br</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:
- Created 5 years ago
- Comments:5
Top GitHub Comments
@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.
See https://github.com/mrdoob/three.js/issues/6610#issuecomment-363819332