Why material is undefined in THREE.OBJLoader?

obj-loader

#1

Hello,
I don’t understand why Javascript shows error:
TypeError: object.material is undefined
In line:
object.material.map.needsUpdate = true;

I supposed it is array but not. Could you help me?

Whole function body:
(it is interesting that textures are loading on object, it generallyy looks well)

function loadObj(objPath, mtlPath) {
var loader = new THREE.OBJLoader();
var mtlLoader = new THREE.MTLLoader();

console.log('mtl path: ' + mtlPath);
console.log('obj path: ' + objPath);

mtlLoader.load( mtlPath, function( materials ) {
	materials.preload();
                        
    loader.load(
		objPath,
        function( object ) {
        object.traverse( function(child) {
			if ( child instanceof THREE.Mesh ) {
				child.material.map = texture;
			} 
		});

        object.receiveShadow = true;
		object.castShadow = true;
		object.position.x = 0;
		object.position.y = 0;
		object.position.z = 0;
		object.name = 'mesh1';

		scene.add( object );

		object.material.map.needsUpdate = true;
		console.log( 'Object loaded.' );
	},
	function( xhr ){
		console.log( ( Math.round( xhr.loaded / xhr.total, 2 ) * 100 ) + '% loaded' );
	},
	function( error ){
		console.log( 'error:' + error.message );
	}
);
});

return loader;

}


#2

The object return by OBJLoader is of type THREE.Group which has no property material. Think of this object as a container than groups multiples objects (meshes, lines or points) defined in your OBJ file. So executing object.material.map.needsUpdate = true; is invalid at this place.


#3

So how can get this object which I loaded and get access to the material, texture etc.?

Thanks,
A.


#4

Um, you already have the necessary code in your first post. Just traverse through the container object like so:

object.traverse( function( child ) {

    if ( child instanceof THREE.Mesh ) {

        child.material.map = texture;
        // access other properties of material

    }

} );

#5

Thank you, but I mean I want to invoke:

object.material.map.needsUpdate = true;

On the end of the function, to be sure that it is updated. How can I achieve this? Without invoke .traverse() again… Which is not fast function, I suppose.

And I completely not understand this. Please, let’s see the documentation:
https://threejs.org/docs/#api/en/core/Object3D.traverse)

.traverse ( callback : Function ) : null

callback - A function with as first argument an object3D object.
Executes the callback on this object and all descendants.

.traverse() is an Object3D method, yes? Like in docs. So why:

first argument an object3D object.

it has same type (Object3D) as argument and in this method I can:

Executes the callback on this object and all descendants.

I can have acces to .material?
If in the first argument it is Object3D, so why can’t I do it on this object “above” traverse.
What is wrong with my reasoning? (Exluding lack of experience.)


#6

You have to do put this line of code inside the if statement after updating the texture. Not at the end of the onLoad() callback. Try it like so:

if ( child instanceof THREE.Mesh ) {

     child.material.map = texture;
     child.material.map.needsUpdate = true;

}

#7

And then updating of maeterial map will make on the end of load() callback?
It is very begining project and I suppose I will do with this object something more later. And for 2 weeks I don’t want to search why something is not updated.
Is there in Documentaiton description how it works?

Or maybe it is waiting on the loop end (new render function call) and it is no any difference where I assign “true” value on the needsUpdate property? Yes?