Christian Heisch
Christian Heisch

Reputation: 245

Three.js: Assign multiple materials to an imported OBJ-file

I have made an OBJ-file with blender. This file has two materials assigned. The file is being imported nicely.

But: How do I assign different materials to the faces in three.js? I understand, that the materials have to be inside of an array (no idea, if I did it right). And now, I want to assign the material to the faces.

This is the interesting part of my code:

....

  loader.load( './beardedbox3.obj', addBananaInScene);
};

var addBananaInScene = function(object){

  banana = object;
  //Move the banana in the scene
  banana.rotation.x = Math.PI/2;
  banana.position.x = 0;
  banana.position.y = 0;
  banana.position.z = 0;
  banana.scale.x = 200;
  banana.scale.y = 200;
  banana.scale.z = 200;
   
  var weiss = new THREE.MeshPhongMaterial( { color: 0xffffff, 
                                        specular: 0xffffff, 
                                        shininess: 30, 
                                        shading: THREE.FlatShading } );
  var rot = new THREE.MeshPhongMaterial( { color: 0xff0000, 
                                        specular: 0xFF9900, 
                                        shininess: 30, 
                                        shading: THREE.FlatShading } ); 

  object.traverse( function ( child ) {
    if(child instanceof THREE.Mesh){
        child.materials = [weiss, rot];
        child.geometry.computeVertexNormals();
    }
  });

  //Add the 3D object in the scene
  scene.add(banana);

The OBJ-file looks like this:

.... Some vertex-coordinates
usemtl weiss
s 1
f 1//1 2//1 3//1
f 4//2 5//2 6//2
... some more faces
f 130//23 34//23 141//23
usemtl rot
f 41//51 42//51 43//51
f 45//52 46//52 47//52
f 42//53 11//53
... even more faces

This seems to me, as if I could use a simple loop, which assigns the materials to faces. But I don't know, how to do that. Or is there an easier way?

Regards Christian

edit:

Thanks for your answer, Sounds logical to me, but unfortunately i didn't get it to work. I seem to be missing some basic knowledge.

Important part of my code now looks like this:

var weiss = new THREE.MeshPhongMaterial({
  color: 0xffffff,
  specular: 0xffffff,
  shininess: 30,
  shading: THREE.FlatShading
});

var rot = new THREE.MeshPhongMaterial({
  color: 0xff0000,
  specular: 0xFF9900,
  shininess: 30,
  shading: THREE.FlatShading
});

object.traverse(function (child) {
  if (child instanceof THREE.Mesh) {
    child.materials = [weiss, rot];
    child.geometry.computeVertexNormals();
    child.geometry.elementsNeedUpdate = true;

    for (var i = 0; i < child.geometry.faces.length; i++) {
      if (i < child.geometry.faces.length / 2) {
        child.geometry.faces[i].materialIndex = 0;
      } else {
        child.geometry.faces[i].materialIndex = 1;
      }
    }
  }
});

And I get the errormessage:

TypeError: child.geometry.faces is undefined
    addBananaInScene/<()                   box.js:78
    THREE.Object3D.prototype.traverse()    three.min.js:179
    THREE.Object3D.prototype.traverse()    three.min.js:179
    addBananaInScene()                     box.js:69
    THREE.OBJLoader.prototype.load/<()     OBJLoader.js:25
    THREE.XHRLoader.prototype.load/<()     three.min.js:377

I guess, I did something very stupid ;)

Thanks in advance for your appreciated help.

Christian

Upvotes: 0

Views: 2229

Answers (2)

Thirupathi
Thirupathi

Reputation: 81

You can load multiple material in single object. Use below code

var loader = new THREE.OBJLoader( manager );
loader.load( 'obj_model/Jersey_1.obj', function ( event ) {
var object = event;
var geometry = object.children[ 0 ].geometry;
var materials = [];
materials.push(new THREE.MeshLambertMaterial( { map : THREE.ImageUtils.loadTexture( 'obj_model/Jrsy_1_Color_1.png'),transparent: true, opacity: 1,color: 0xFF4C33 } ) );
materials.push(new THREE.MeshLambertMaterial( { map : THREE.ImageUtils.loadTexture( 'obj_model/Jrsy_1_Color_2.png'),transparent: true, opacity: 1,color: 0xFFF933 } ) );
materials.push(new THREE.MeshLambertMaterial( { map : THREE.ImageUtils.loadTexture( 'obj_model/Jrsy_1_Tag.png'),transparent: true, opacity: 1,color: 0xFF0000 } ) );
materials.push(new THREE.MeshLambertMaterial( { map : THREE.ImageUtils.loadTexture( 'obj_model/Jrsy_Ptrn.png'),transparent: true, opacity: 1,color: 0xFF33E0 } ) );
mesh = THREE.SceneUtils.createMultiMaterialObject(geometry, materials);
mesh.scale = new THREE.Vector3( 8,8,8 );
scene.add(mesh);
});

Upvotes: 1

Radio
Radio

Reputation: 2853

Yes. Use the multimaterial.

http://threejs.org/docs/#Reference/Materials/MultiMaterial

And then where, for example you have a material for each face and you have access to your geometry after the obj has loaded, you could have the first half of faces use one material and the remaining faces use another:

  for ( var i = 0; i < geometry.faces.length; i ++ ) {
    if(i<geometry.faces.length/2){
        geometry.faces[i].materialIndex = 0;
    }else{
        geometry.faces[i].materialIndex = 1;
    }
  }

Upvotes: 2

Related Questions