We are scheduling an educational application for orthogonal projections of polyhedra that are read via the Three.js library's OBJ + MTL loader. The problem is that depending on the OBJ + MTL file, the loader sometimes does not work and we do not know why, because the data seems ok.
The method we are using:
The problem is in Step 3: depending on the OBJ + MTL data file, the THREE.Geometry () function fromBufferGeometry () returns null.
Here is an example where everything works: link (all information geometry is passed, axes and projections are determined correctly).
HereisanexamplewherethereisaproblemwiththeTHREE.Geometry()function.fromBufferGeometry(): link (because it was not possible to copy the geometry of the edges, the projections in the coordinate planes failed).
Herearethepolyhedrondatathatgivesproblems:
OBJ:
######OBJFileGeneratedbyMeshlab######Objectjohnson-j01-square-pyramid.obj##Vertices:5#Faces:6#####mtllib./johnson-j01-square-pyramid.obj.mtlvn-0.000270-0.281574-0.959539v-0.4444571.160279-7.902924vn-0.996609-0.0008780.082282v-7.610548-2.453604-0.243687vn-0.1058610.9338700.341586v-0.6643515.8607012.143697vn0.9684950.2488920.008435v7.942724-0.476746-0.828161vn0.125332-0.8216820.555996v0.776633-4.0906296.831076#5vertices,0verticesnormalsusemtlmaterial_0f5//52//21//1f5//51//14//4usemtlmaterial_1f2//25//53//3f1//12//23//3f4//41//13//3f5//54//43//3#6faces,0coordstexture#EndofFile
MTL:
##Wavefrontmaterialfile#ConvertedbyMeshlabGroup#newmtlmaterial_0Ka0.2000000.2000000.200000Kd1.0000000.0000000.000000Ks1.0000001.0000001.000000Tr1.000000illum2Ns0.000000newmtlmaterial_1Ka0.2000000.2000000.200000Kd0.0000000.0000001.000000Ks1.0000001.0000001.000000Tr1.000000illum2Ns0.000000
JavaScript+HTML:
<!DOCTYPEhtml><html><head><metahttp-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>
What's going on?