As detailed in a previous question I have learned how to use THREE.js OBJMTLLoader by using the same objects & materials used in the official example.
That example uses a (for me) complex model with the DDSLoader.
I would like to load simpler OBJ+MTL models and have been trying with several free models obtained from the web. I have managed to load the OBJ files OK (by applying further THREE.js code such as defining normals) but there is a problem loading material textures from the MTL files.
Here is a simple example of my code.
//...DolphinB
var posX = -3445; var posY = 750; var posZ = -100;
var common_scale = 100;
var loader = new THREE.OBJMTLLoader();
loader.load(
'TRI_VP_files/models/dolphin/DOLPHIN_B.obj',
'TRI_VP_files/models/dolphin/DOLPHIN_B.mtl',
function(object)
{
object.position.set( posX, posY, posZ );
scene222.add( object );
object.scale.set(common_scale, common_scale, common_scale);
} );
Here is the MTL code
# Wavefront material library
# Tue Aug 03 07:47:56 1999
# Created with Viewpoint Interchange www.viewpoint.com
newmtl dl_body
Ka 0 0 0
Kd 0 0.8 0.9
Ks 0 0 0
illum 1
map_Kd DOLPHIN2.JPG
My Question
Please could someone point me to some simple OBJ + MTL files which are known to load OK with OBJMTLLoader.
You can use the following free-for-private-use fileset hand created by Mohammad Alizadeh (nice work thankyou Mohammad).
It uses a single .JPG image file as source for the material texture.
It uses a single material.
Here is the .MTL file contents...
# Blender MTL File: 'Hand.blend'
# Material Count: 1
newmtl defaultMat
Ns 96.078431
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ni 1.000000
d 1.000000
illum 2
map_Kd hand_mapNew.jpg
You will need to change the top few lines of the .OBJ file from...
# Blender v2.74 (sub 0) OBJ File: 'Hand.blend'
# www.blender.org
mtllib Hand.mtl
o ZBrushPolyMesh3D
v 0.614360 0.281365 -0.675872
v 0.684894 0.445729 -0.634615
to
# Blender v2.74 (sub 0) OBJ File: 'Hand.blend'
# www.blender.org
## mtllib Hand.mtl <===== commented out
usemtl defaultMat ## <===== added usemtl command,note proper name of material
## o ZBrushPolyMesh3D <===== commented out
v 0.614360 0.281365 -0.675872
v 0.684894 0.445729 -0.634615
Note that many free 3D object filesets use .TIF image files. But .TIF's cannot display in browsers (or THREE.js). Converting them to .JPG format is possible but the UV mapping is not preserved.
Also note that some free 3D object filesets need to be edited so that the material names in the .OBJ file match the names given in the .MTL file.
Also note that some .OBJ files (like the hand example above) need to be editted so that the material is indicated by a usemtl command e.g.:-
usemtl defaultMat
Child processing
For the Hand fileset there are vertex normals (vn) in the .OBJ file. But for some reason smoothing is not applied. Applying the following code will produce smoothing (and adjust shininess and set rootObject references for object picking):-
object.traverse ( function (child)
{
if (child instanceof THREE.Mesh)
{
child.material.shininess = 10;//range 0.1 to 30 (default) to 1000 or more, applies to Phong materials.
//child.userData.rootObject = object; //... see West Langley answer at http://stackoverflow.com/questions/22228203/picking-object3d-loaded-via-objmtlloader
//... used for object picking so that, for further operations, we can select picked child object or child's rootObject.
child.rootObject = object; //... avoids infinite loop if cloning 3D objects.
child.geometry.computeFaceNormals();
child.geometry.computeVertexNormals();
//child.geometry.normalsNeedUpdate = true; //... only required if object has already been rendered.
}
}; )
DISCLAIMER
These tricks got things working for me with this particular scenario. I don't say that this is the best way of doing things.
Related
Please take a look at this code
The scene is static so it should be enough to explicitly call renderer.render() command once, after the assets are loaded, and see the head.
But the head is not seen.
If I replace renderer.render( scene, camera ); with animate() (set doRenderViaSingleRender = false) the head is seen.
I think that the failure when calling render() once, is because the texture is not yet loaded.
Whereas, animate() calls render() constantly, and so, when the texture is finally loaded, the head is seen.
After setting the materials in objLoader.setMaterials( materials ); I expect the map to be defined, but it is undefined.
the console log of the material map shows:
console.log('objLoader.materials.materials[lambert2SG.001].map', objLoader.materials.materials['lambert2SG.001'].map);
null
How can I make the program wait until after the texture has been loaded?
Thanks
That material doesn't have a map set. If you open the material file https://threejs.org/examples/models/obj/walt/WaltHead.mtl in a text editor you'll see it has several attributes, but you won't find a path to a texture, so that's why performing console.log() on the map property outputs null.
# Material Count: 1
newmtl lambert2SG.001
Ns 92.156863
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.250000 0.250000 0.250000
Ni 1.000000
d 1.000000
illum 2
Additionally, that WaltHead object file has vertex position and normals but it does not have any UV data, so even if you assigned a map texture, it won't display without UVs.
However, to answer your question, you could manually download any texture with TextureLoader.load(). The first argument is the path to the texture file, and the second argument is the callback function that's triggered after the texture has finished loading.
I have a 3D model in .obj format. However the coordinates for this 3D model are not (0,0,0). This a 3D render of drone imagery so the coordinates are actual georeferenced coordinates.
I'm following the example in Three.js on how to load an obj with its mtl on webgl. I use the original HTML except that I simply replace the obj listed as male02 by CerroPelaoLow and the files are placed in the obj directory. Firefox displays the model correctly but the position is the problem.
Note that this render is generated by a program this way and even though I can manipulate the model with a program such as Meshlab I'd still prefer the minimum manipulation possible.
So how can I use local coordinates of my object or focus the camera and then use a different set of controls?
You can use the boundingSphere or boundingBox of your object's geometry to determine the position and of your camera. I have already implemented a functionality to focus an object or a set objects. So, here I share some code:
// assuming following variables:
// object -> your obj model (THREE.Mesh)
// camera -> PerspectiveCamera
// controls -> I'm also using OrbitControls
// if boundingSphere isn't set yet
object.computeBoundingSphere();
var sphere = object.geometry.boundingSphere.clone();
sphere.applyMatrix4( object.matrixWorld );
// vector from current center to camera position (shouldn't be zero in length)
var s = new THREE.Vector3().subVectors( camera.position, controls.center );
var h = sphere.radius / Math.tan( camera.fov / 2 * Math.PI / 180 );
var newPos = new THREE.Vector3().addVectors( sphere.center, s.setLength(h) );
camera.position.copy( newPos );
controls.center.copy( sphere.center );
I am new to 3D Technology by referring few sited I have managed to get some code running to render 3D object. The object was generated by 3D Software Maya. I received these files –
.obj (object file)
.mtl (material file)
.png (texture files).
Using ‘three.js’ I tried to render the object in browser and for that I used three approaches as given below –
1. OBJLoader and explicitly load texture file.
2. OBJMTLLoader
3. JSON loader by converting .obj file into .js.
In all the cases 3D object was rendered successfully but common problem I faced is that it is not at all loading its ‘texture’. Object file, Material file and Texture file all are at same location. It is observed that shading is applied on the object so I can see some parts colourful but the ‘texture’ part (png files) is not getting applied on the object so partly it looks black and dark. There is no error as browser doesn’t show any.
Snippets using JSON loader –
var oLoader = new THREE.JSONLoader();
oLoader.load('Main_unit_01.js', function (geometry, materials) {
// get original materials
var material = new THREE.MeshFaceMaterial(materials);
//var material = new THREE.MeshPhongMaterial();
//material.map = new THREE.ImageUtils.loadTexture(
// "final_texture_v01.png");
var mesh = new THREE.Mesh(geometry, material);
mesh.scale.set(10, 10, 10);
Test.scene.add(mesh);
OBJMTL loader needs material file, using this approach it is observed that it loads both object and material file but not texture, here is a small part information present in MTL file –
newmtl phongE1SG
illum 4
Kd 0.00 0.00 0.00
Ka 0.00 0.00 0.00
Tf 1.00 1.00 1.00
map_Kd final_texture_v01.png
Ni 1.00
Ks 0.50 0.50 0.50
Here is the some part of information present in .js file which was the result of .obj to .js conversion –
"DbgColor" : 238,
"DbgIndex" : 3,
"DbgName" : "phongE1SG",
"colorAmbient" : [0.0, 0.0, 0.0],
"colorDiffuse" : [0.0, 0.0, 0.0],
"colorSpecular" : [0.5, 0.5, 0.5],
"illumination" : 4,
"mapDiffuse" : "final_texture_v01.png",
"opticalDensity" : 1.0
In both above material and js files we can see that ‘.png’ file has been mentioned with some tag. After reading some suggestions given on some blogs I tried to explicitly set the relative path into these files but no success. I have checked most of the blog here and applied most of suggestion but it didn't work. Also I have checked in "github.com" for help but could not get any help. None of the browser gives any error like particular file not found.
Here are the few links which I have followed.
1. three.js JSONLoader Material Not Showing
2. Loading textures with JSONloader
I am not able to post more than 2 links but I have followed so many sites.
Please suggest if any solution to get texture on the object.
The MTL file Kd attribute corresponds to the js attribute colorDiffuse. The Ka attribute is the colorAmbient. As you can see they are both 0. That is why you cannot see the model. Try to make the ambient term (0.2, 0.2, 0.2) and the diffuse term (0.8, 0.0, 0.8).
In blender i create a normal map, i will export to blender like OBJ (Wawefront) and there is stored in *.mtl file like "map_Bump".
map_Bump have always the same bumpsacale. What parramter in MTL file defines bumpscale in THREE.JS?
my MTL file:
newmtl ship_white
Ns 0.000000
Ka 0.000000 0.000000 0.000000
Kd 0.656604 0.656604 0.656604
Ks 0.433962 0.433962 0.433962
Ni 1.000000
d 1.000000
illum 2
map_Kd 2t3l.png
map_Bump 2t3l_normal.jpg
Currently this is not supported. However you can remove the bump map from the file and map it manually in Three.js.
Loading the texture.
var bmap = THREE.ImageUtils.loadTexture('bumpmap.jpg');
You will need to traverse your model and find it's material. e.g.
object.traverse( function( node ) {
if ( node instanceof THREE.Mesh ) {
// for smoothing
node.geometry.computeVertexNormals();
console.log(node);
}
if (node instanceof THREE.Mesh && node.material instanceof THREE.MeshPhongMaterial ) {
// console.log(node);
geometry = node.geometry;
material = node.material;
}
});
Then either assign directly on the material you need. Or create a new Three JS mesh and assign it there. Be warned though if the OBJ contains multiple meshes you will need to find the 'right' one.
mesh = new THREE.Mesh( geometry, material);
material.bumpMap = bmap;
material.bumpScale = 1;
I'm loading an OBJ with an MTL which references a diffuse map and a bump. The map_Kd (diffuse map) is reading and loading in the renderer, but the map_Bump (bump map) is not. When I log the material to the console, the bumpmap property is null. Does OBJ MTL Loader work with bump maps?
I looked into MTLLoader.js and found that bump maps are not added from the mtl file. I think I've fixed this:
In the file, there's a section for diffuse maps:
case 'map_kd':
// Diffuse texture map
params[ 'map' ] = this.loadTexture( this.baseUrl + value );
params[ 'map' ].wrapS = this.wrap;
params[ 'map' ].wrapT = this.wrap;
break;
Immediately after that, I added this:
case 'map_bump':
// Diffuse bump map
params[ 'bumpMap' ] = this.loadTexture( this.baseUrl + value );
params[ 'bumpMap' ].wrapS = this.wrap;
params[ 'bumpMap' ].wrapT = this.wrap;
break;
This is working for my example. If any developers see pitfalls with this modification, please let me know. Thanks.