Trying to load an OBJ file with accompanying MTL based on example from online docs.

I’m pretty much just trying duplicate the example found here:

https://threejs.org/examples/webgl_loader_obj_mtl

I’m new to WebGL and THREE and may be jumping into the deep end with a pocket full of stones. I almost just duplicated the code but still I’m obviously doing something wrong. Would you mind having a look?

I’ve downloaded the relevant files and serve them from my own Apache instance, but for some reason I don’t seem to have a lot of success. Sometimes the object is displayed, sometimes not. The texture is never correct. At first I suspected wrong ambient lighting or something but I use the same as in the example.

I should maybe mention that I link symbolically to the javascript files in /usr/share/javascript/three. It may well be relevant.

The MTL file always seem to load only 28% before the timer ends.

	<!DOCTYPE html>
	<!-- html lang="en" -->
		<head>
			<meta charset="utf-8" />
			<meta name="viewport" content="width=device-width, initial-scale=1" />
			<title>SBROTHY</title>
			<style>
				html, body {
				   margin: 0;
				   height: 100%;
				}
			</style>
		</head>

		<body>
			<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>			

			<script type="importmap">	
			{
				"imports": {
					"three": "./js/three/",
					"three/addons":  "./js/three/examples/jsm/"
				}
			}
			</script>

			<!-- ---------------------------------------------------------------------------------------------- -->
			<script type="module">
				import * as THREE from './js/three/three.module.js';
				import { OBJLoader } from './js/three/examples/jsm/loaders/OBJLoader.js';
				import { MTLLoader } from './js/three/examples/jsm/loaders/MTLLoader.js';
				import { OrbitControls } from './js/three/examples/jsm/controls/OrbitControls.js';
				
				/* *************************************************************************************************** 
				*/ 
				let renderer, camera, scene, controls;

				let mouseX = 0, mouseY = 0;

				let windowHalfX = window.innerWidth / 2;
				let windowHalfY = window.innerHeight / 2;


				init();
				animate();


				/* *************************************************************************************************** 
				*/
				function onWindowResize() {
					windowHalfX = window.innerWidth / 2;
					windowHalfY = window.innerHeight / 2;

					camera.aspect = window.innerWidth / window.innerHeight;
					camera.updateProjectionMatrix();

					renderer.setSize(window.innerWidth, window.innerHeight);
				}

				/* *************************************************************************************************** 
				*/
				function onDocumentMouseMove( event ) {
					mouseX = ( event.clientX - windowHalfX ) / 2;
					mouseY = ( event.clientY - windowHalfY ) / 2;
				}

				/* *************************************************************************************************** 
				*/
				function init() {
					const container = document.createElement('div');
					document.body.appendChild(container);					

					/* *************************************************************************************************** 
					* create camera
					*/
					const fieldOfView = 45;
					const aspect = window.innerWidth / window.InnerHeight;
					const near = 1;
					const far = 2000;
					camera = new THREE.PerspectiveCamera(fieldOfView, aspect, near, far);
					camera.position.z = 250;

					console.log('camera created');

					/* *************************************************************************************************** 
					* create scene
					*/
					scene = new THREE.Scene();

					const ambientLight = new THREE.AmbientLight(0xcccccc, 1);
					scene.add(ambientLight);
		
					const pointLight = new THREE.PointLight(0xffffff, 0.8);
					camera.add(pointLight);
					scene.add(camera);

					console.log('scene and light created');

					/* *************************************************************************************************** 
					* load model
					*/
					const onProgress = function(xhr) {
						console.log('onProgress: \r\n[' + JSON.stringify(xhr, null, 4) + ']\r\n');
						if(xhr.lengthComputable) {
							const percentComplete = xhr.loaded / xhr.total * 100;
							console.log('onProgress: ' + Math.round(percentComplete, 2) + '% downloaded');
						}
					};

					/* *************************************************************************************************** 
					*/
					const onError = function(err) {
						console.log('onError: \r\n[' + JSON.stringify(err, null, 4) + ']\r\n');						
						console.error('Error happened loading ' + err + ': ' + err);					
					};
					

					/* *************************************************************************************************** 
					*/
					
					new MTLLoader()
						.setPath('./obj/')
						.load( 
							'male02.mtl', 
							function (materials) {
								materials.preload();

								new OBJLoader()
									.setMaterials(materials)
									.setPath('./obj/')
									.load(
										'male02.obj', 
										function (object) {
											object.position.y = - 95;
											scene.add(object);
										},
										onProgress,
										onError
									);
								},
								onProgress,
								onError
							);
							

					/* *************************************************************************************************** 
					* create renderer
					*/
					renderer = new THREE.WebGLRenderer();

					renderer.outputEncoding = THREE.sRGBEncoding;
					renderer.setPixelRatio(window.devicePixelRatio);
					renderer.setSize(window.innerWidth, window.innerHeight);
					container.appendChild(renderer.domElement);					

					console.log('renderer created');

					//document.addEventListener( 'mousemove', onDocumentMouseMove );
					window.addEventListener('resize', onWindowResize);


					controls = new OrbitControls(camera, renderer.domElement);
					controls.update();
					
					console.log('controls created');					
				}


				/* *************************************************************************************************** 
				* animate
				*/
				function animate() {
					requestAnimationFrame(animate);
					
					controls.update();
					
					render();
				}


				/* *************************************************************************************************** 
				* render
				*/
				function render() {
					camera.position.x += ( mouseX - camera.position.x ) * .05;
					camera.position.y += ( - mouseY - camera.position.y ) * .05;
					camera.lookAt( scene.position );
					
					renderer.render(scene, camera);
				}

				
				/* *************************************************************************************************** 
				*/
				function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
					const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
					const halfFovY = THREE.MathUtils.degToRad(camera.fov * .5);
					const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);

					// compute a unit vector that points in the direction the camera is now
					// from the center of the box
					const direction = (new THREE.Vector3()).subVectors(camera.position, boxCenter).normalize();

					// move the camera to a position distance units way from the center
					// in whatever direction the camera was from the center already
					camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));

					// pick some near and far values for the frustum that
					// will contain the box.
					camera.near = boxSize / 100;
					camera.far = boxSize * 100;

					camera.updateProjectionMatrix();

					// point the camera to look at the center of the box
					camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
				}
			
			</script>

		</body>
	</html>

The 28% loading seems to be irrelevant. It does indeed load completely. Other OBJ/MTL pairs work fine (with some minor lighting problems).

Also, I replaced the DOC header with this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">

after some complaints about “quirks mode”.

Cleaned up the code:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
	<!-- html lang="en" -->
		<head>
			<meta charset="utf-8" />
			<meta name="viewport" content="width=device-width, initial-scale=1" />
			<title>SBROTHY</title>
			<style>
				html, body {
				   margin: 0;
				   height: 100%;
				}
			</style>
		</head>

		<body>
			<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>			

			<script type="importmap">	
			{
				"imports": {
					"three": "./js/three/",
					"three/addons":  "./js/three/examples/jsm/"
				}
			}
			</script>

			<!-- ---------------------------------------------------------------------------------------------- -->
			<script type="module">
				import * as THREE from './js/three/three.module.js';
				import { OBJLoader } from './js/three/examples/jsm/loaders/OBJLoader.js';
				import { MTLLoader } from './js/three/examples/jsm/loaders/MTLLoader.js';
				import { OrbitControls } from './js/three/examples/jsm/controls/OrbitControls.js';
				
				/* *************************************************************************************************** 
				*/ 
				let renderer, camera, scene, controls;

				let mouseX = 0, mouseY = 0;

				let windowHalfX = window.innerWidth / 2;
				let windowHalfY = window.innerHeight / 2;


				init();
				animate();


				/* *************************************************************************************************** 
				*/
				function onWindowResize() {
					windowHalfX = window.innerWidth / 2;
					windowHalfY = window.innerHeight / 2;

					camera.aspect = window.innerWidth / window.innerHeight;
					camera.updateProjectionMatrix();

					renderer.setSize(window.innerWidth, window.innerHeight);
				}

				/* *************************************************************************************************** 
				*/
				function init() {
					const container = document.createElement('div');
					document.body.appendChild(container);					

					/* *************************************************************************************************** 
					* create camera
					*/
					const fieldOfView = 45;
					const aspect = window.innerWidth / window.InnerHeight;
					const near = 1;
					const far = 2000;
					camera = new THREE.PerspectiveCamera(fieldOfView, aspect, near, far);
					camera.position.z = 250;

					console.log('camera created');

					/* *************************************************************************************************** 
					* create scene
					*/
					scene = new THREE.Scene();

					const ambientLight = new THREE.AmbientLight(0xcccccc, 1);
					scene.add(ambientLight);
		
					const pointLight = new THREE.PointLight(0xffffff, 0.8);
					camera.add(pointLight);
					scene.add(camera);

					console.log('scene and light created');

					/* *************************************************************************************************** 
					* create renderer
					*/
					renderer = new THREE.WebGLRenderer();

					renderer.outputEncoding = THREE.sRGBEncoding;
					renderer.setPixelRatio(window.devicePixelRatio);
					renderer.setSize(window.innerWidth, window.innerHeight);
					container.appendChild(renderer.domElement);					

					console.log('renderer created');

					controls = new OrbitControls(camera, renderer.domElement);
					controls.update();
					
					console.log('controls created');					

					//document.addEventListener( 'mousemove', onDocumentMouseMove );
					window.addEventListener('resize', onWindowResize);


					/* *************************************************************************************************** 
					* load model
					*/
					const onProgress = function(xhr) {
						console.log('onProgress: \r\n[' + JSON.stringify(xhr, null, 4) + ']\r\n');
						if(xhr.lengthComputable) {
							const percentComplete = xhr.loaded / xhr.total * 100;
							console.log('onProgress: ' + Math.round(percentComplete, 2) + '% downloaded');
						}
					};

					/* *************************************************************************************************** 
					*/
					const onError = function(err) {
						console.log('onError: \r\n[' + JSON.stringify(err, null, 4) + ']\r\n');						
						console.error('Error happened loading ' + err + ': ' + err);					
					};
					

					/* *************************************************************************************************** 
					*/
					
					new MTLLoader()
						.setPath('./obj/')
						.load( 
							'molecule.mtl', 
							function (materials) {
								materials.preload();

								new OBJLoader()
									.setMaterials(materials)
									.setPath('./obj/')
									.load(
										'molecule.obj', 
										function (object) {
											object.position.y = - 95;
											scene.add(object);
										},
										onProgress,
										onError
									);
								},
								onProgress,
								onError
							);
					}
					


				/* *************************************************************************************************** 
				* animate
				*/
				function animate() {
					requestAnimationFrame(animate);
					
					controls.update();
					
					render();
				}


				/* *************************************************************************************************** 
				* render
				*/
				function render() {
					camera.position.x += ( mouseX - camera.position.x ) * .05;
					camera.position.y += ( - mouseY - camera.position.y ) * .05;
					camera.lookAt( scene.position );
					
					renderer.render(scene, camera);
				}
			
			</script>

		</body>
	</html>

This “molecule” object works somewhat except the lighting trouble I mentioned.

Never mind. I realized I may be fooling around with an inferior data type. GLTF seems to the way to go…

1 Like