Normal map in *.obj file - three.js

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;

Related

How to make mtlLoader, and objloader wait for material texture to be loaded, before continuing with the rest of the program?

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.

Blender export for THREE.js

I have an issue with THREE.js export in Blender, namely: materials and textures don't get exported at all.
To show the issue, I've created a simple plane with one material and one texture.
This is entire file generated by JSON export:
{
"faces":[41,0,1,3,2,0,1,2,3,0,0,0,0],
"metadata":{
"faces":1,
"type":"Geometry",
"uvs":1,
"generator":"io_three",
"normals":1,
"version":3,
"vertices":4
},
"uvs":[[0.0001,0.0001,0.9999,0.0001,0.9999,0.9999,0.0001,0.9999]],
"normals":[1,0,0],
"name":"PlaneGeometry",
"vertices":[-0,3e-06,16,1e-06,-4e-06,-16,-1e-06,32,16,0,32,-16]
}
In comparision, this one is for OBJ/MTL Wavefront format export (which confirms there indeed is a material and texture set for the model)
# Blender v2.76 (sub 0) OBJ File: 'wall_pipes.blend'
# www.blender.org
mtllib wall_pipes.mtl
o Plane
v -0.000000 0.000003 16.000002
v 0.000001 -0.000002 -15.999997
v -0.000001 32.000000 15.999997
v 0.000000 31.999996 -16.000002
vt 0.000100 0.000100
vt 0.999900 0.000100
vt 0.999900 0.999900
vt 0.000100 0.999900
vn 1.000000 0.000000 0.000000
usemtl Material.001
s off
f 1/1/1 2/2/1 4/3/1 3/4/1
...and its corresponding mtl file...
# Blender MTL File: 'wall_pipes.blend'
# Material Count: 1
newmtl Material.001
Ns 96.078431
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.021739 0.021739 0.021739
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd E:\blender\models\library\textures\pk02_pipes01_C.png
map_Bump E:\\blender\\models\\library\\textures\\pk02_pipes01_C.png
I was thinking about using these generated obj/mtl files, but THREE.js r73 (I prefer its shadow format over the new one) seems to have some issues with them:
[.CommandBufferContext]RENDER WARNING: Render count or primcount is 0.
Anyway I'd like to use JSON format as it feels more natural to me and this is what other people suggest elsewhere. I apologise in case this lack-of-material issue is something really, really stupid :)
Here is the .blend file, maybe I am doing something wrong or have a checkbox unchecked somewhere...?
https://github.com/Scharnvirk/wingmod/raw/blender_issue/models/wall_pipes.blend
Thanks in advance!
The three.js exporter has a lot of options, check that each material/UV option is on.
With the following options enabled
I get the following json
{
"faces":[43,0,1,3,2,0,0,1,2,3,0,0,0,0],
"metadata":{
"version":3,
"type":"Geometry",
"uvs":1,
"materials":1,
"faces":1,
"normals":1,
"generator":"io_three",
"vertices":4
},
"normals":[1,0,0],
"name":"PlaneGeometry",
"uvs":[[0.0001,0.0001,0.9999,0.0001,0.9999,0.9999,0.0001,0.9999]],
"materials":[{
"mapBumpAnisotropy":1,
"mapDiffuseAnisotropy":1,
"wireframe":false,
"mapBumpWrap":["RepeatWrapping","RepeatWrapping"],
"mapSpecularAnisotropy":1,
"specularCoef":50,
"visible":true,
"mapSpecularRepeat":[1,1],
"mapBumpScale":[2.05634,2.05634],
"DbgColor":15658734,
"mapSpecular":"pk02_pipes01_C.png",
"mapDiffuse":"pk02_pipes01_C.png",
"depthWrite":true,
"mapBump":"pk02_pipes01_C.png",
"mapDiffuseWrap":["RepeatWrapping","RepeatWrapping"],
"shading":"phong",
"depthTest":true,
"DbgName":"Material.001",
"opacity":1,
"colorDiffuse":[0.8,0.8,0.8],
"transparent":false,
"mapSpecularWrap":["RepeatWrapping","RepeatWrapping"],
"DbgIndex":0,
"colorSpecular":[0.021739,0.021739,0.021739],
"mapBumpRepeat":[1,1],
"mapDiffuseRepeat":[1,1],
"colorEmissive":[0,0,0],
"blending":"NormalBlending"
}],
"vertices":[-0,3e-06,16,1e-06,-4e-06,-16,-1e-06,32,16,0,32,-16]
}

THREE.js OBJMTLLoader - Request for simple example files

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.

three.js pick one of the geometry from obj file

i use objloader to load a obj file and add it into scene,
var loader = new THREE.OBJLoader();
loader.load( 'testcude.obj', function ( object ) {
object.traverse( function ( child ) {
if( child instanceof THREE.Mesh ) {
child.material = woodmap;
}
});
object.name="stairs";
scene.add( object );
});
and i found that inside obj file, there is formatted like this:
#
# object Box002
#
v -14.0000 17.9249 9.0000
v -14.0000 17.9249 -9.0000
...
...
...
f 10/8/12 9/5/12 13/6/12 16/7/12
# 6 polygons
my question is, is that possible to pick the 'Box002' and set a rotation.x?
i try getObjectByName but no work..
i exported this obj file from 3dsmax, or any other file format possible to do this?
r66.
thank you, cheer~~
If you want to apply different operations to different parts of your model you need to save your model as separate objects from within 3dsmax. Wavefront .obj models are not named as you might think. What you are seeing is just a comment in the file and it is ignored when the file is parsed.

THREE.js Reusing geometry does not seem to work efficiently

I am loading several models into scene using the same geometry like so (pseudo code):
var geoCache = [];
function parseJSONGeometry(json_geo){
// this code is the three.js model parser from the jsonloader
return geometry;
}
function loadCachedGeo(data){
if( !geoCache[data.id] ){
geoCache[json.id] = parseJSONGeometry(data);
}
return geoCache[json.id];
}
function loadObjects(json){
var mats = [];
combined = new THREE.Geometry();
for(i=0<i<json.geometries.length;i++){
data = json.geometries[i];
geo = loadCachedGeo(data.id);
mats.push(new THREE.MeshBasicMaterial(map:THREE.imageUtils.loadTexture(data.src)));
mesh = new THREE.Mesh(geo);
mesh.position.set(data.x,data.y,data.z);
combined = THREE.GeometryUtils.mergeGeometry(combined,mesh);
}
mesh = new THREE.Mesh(combined,new THREE.MeshFaceMaterial(mats));
scene.add(mesh);
}
I also cache the textures, however I omitted that for the sake of simplicity.
When I call:
renderer.info.render.faces
renderer.info.memory.textures
renderer.info.memory.programs
renderer.info.memory.geometries;
renderer.info.render.calls
I notice when one object is on the screen the poly count is say 1000, textures: 1, calls: 1, shaders: 1 and geometries: 1. When two objects are on the screen 2000 faces are reported, 1 texture, 1 shader, 2 calls, and 2 geometries.
I thought that reusing geometry in this fashion only loads the geometry once into the gpu. Am I missing something, can someone PLEASE explain this behavior?
Three.js r59
You need to inspect
renderer.info.memory.geometries
There is also
renderer.info.memory.textures
renderer.info.memory.programs
three.js r.59

Resources