mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
102.73k stars 35.38k forks source link

Blender Scene Exporter normals wrong #3221

Closed willyvvu closed 11 years ago

willyvvu commented 11 years ago

When I export a scene from Blender, the face normals of the meshes are messed up.

From the object that THREE.SceneLoader returns:

If I add the exported object itself to the scene, it is oriented and positioned correctly. The normals are just off. Example: scene.add(data.objects.Thing)

If I add the exported geometry to a mesh, and the mesh to the scene, the model is rotated and positioned incorrectly, but the normals are fine. Example: scene.add(new THREE.Mesh(data.geometries.geo_Thing,new THREE.MeshLambertMaterial())

I have a hunch that the exporter failed to flip the Y and Z for the normals.

One last thing: sometime the Three.js exporter renames the geometries of the objects being exported, without notice. If you look at the scene hierarchy panel before and after exporting, a mesh's geometry might be renamed from Thing to Thing.001, while the name will remain Thing. This messes up THREE.SceneLoader when importing. The weird thing is that it only affects certain meshes. See the attached image.

The geometry for Building is renamed to Building.001. Same thing with Collider's geometry, but somehow not for Track. How bizarre. Screenshot from 2013-03-21 18:37:59

mrdoob commented 11 years ago

Would be helpful if you could share a .bend file that shows the problem.

willyvvu commented 11 years ago

I tried it with multiple .blend files to no avail. I don't think it matters what's inside the .blend file. I realized I'm using blender 2.63, with the plugin for 2.65. I'll try with the newest version of blender and see if it helps.

mrdoob commented 11 years ago

Did that help?

willyvvu commented 11 years ago

Not really. Sorry for the delayed reply. It might just be THREE.Raycaster, because that is what I'm using, which reports an upwards-facing face with a normal of {x:0,y:0,z:1}. It might be that THREE.Raycaster fails to take into account the object's rotation, just using the face normal of the geometry.

mrdoob commented 11 years ago

Well, I think it's time for you to post a jsfiddle.

willyvvu commented 11 years ago

I think I've got it!

I quickly added a plane in Blender 2.65, facing upwards (z axis in Blender, y axis in Three.js) Then, I exported it with two sets of settings.

Model only export

Plane This yielded the following code:

{

    "metadata" :
    {
        "formatVersion" : 3.1,
        "generatedBy"   : "Blender 2.65 Exporter",
        "vertices"      : 4,
        "faces"         : 1,
        "normals"       : 1,
        "colors"        : 0,
        "uvs"           : [],
        "materials"     : 1,
        "morphTargets"  : 0,
        "bones"         : 0
    },

    "scale" : 1.000000,

    "materials" : [ {
        "DbgColor" : 15658734,
        "DbgIndex" : 0,
        "DbgName" : "default",
        "vertexColors" : false
    }],

    "vertices" : [1,4.37114e-08,1,-1,4.37114e-08,1,1,-4.37114e-08,-1,-1,-4.37114e-08,-1],

    "morphTargets" : [],

    "normals" : [0,1,0],

    "colors" : [],

    "uvs" : [],

    "faces" : [35,1,0,2,3,0,0,0,0,0],

    "bones" : [],

    "skinIndices" : [],

    "skinWeights" : [],

    "animation" : {}

}

Note the line specifying normals, which contains "normals" : [0,1,0], This is consistent with Three.js's coordinate system. This is correct in Three.js, which explains why exporting a model without a scene always works. Therefore, when added an THREE.Mesh, then to a THREE.Scene, the object faces the correct way, since THREE.Mesh initializes without any rotations or transformations that could alter the normal.

Scene export

PlaneS This yielded the following code:

{

"metadata" :
{
    "formatVersion" : 3.2,
    "type"          : "scene",
    "sourceFile"    : "",
    "generatedBy"   : "Blender 2.65 Exporter",
    "objects"       : 1,
    "geometries"    : 1,
    "materials"     : 1,
    "textures"      : 0
},

"urlBaseType" : "relativeToScene",

"objects" :
{
    "Plane" : {
        "geometry"  : "geo_Plane",
        "groups"    : [  ],
        "material"  : "",
        "position"  : [ 0, 0, 0 ],
        "rotation"  : [ -1.5708, 0, 0 ],
        "quaternion": [ -0.707107, 0, 0, 0.707107 ],
        "scale"     : [ 1, 1, 1 ],
        "visible"       : true,
        "castShadow"    : false,
        "receiveShadow" : false,
        "doubleSided"   : false
    }
},

"geometries" :
{
    "geo_Plane" : {
        "type" : "embedded",
        "id"  : "emb_Plane"
    }
},

"materials" :
{
    "Material" : {
        "type": "MeshLambertMaterial",
        "parameters": { "color": 10724259, "opacity": 1, "blending": "NormalBlending" }
    }
},

"embeds" :
{
"emb_Plane": {  "scale" : 1.000000,

    "materials" : [ {
        "DbgColor" : 15658734,
        "DbgIndex" : 0,
        "DbgName" : "default",
        "vertexColors" : false
    }],

    "vertices" : [1,-1,0,-1,-1,0,1,1,0,-1,1,0],

    "morphTargets" : [],

    "normals" : [0,0,1],

    "colors" : [],

    "uvs" : [],

    "faces" : [35,1,0,2,3,0,0,0,0,0],

    "bones" : [],

    "skinIndices" : [],

    "skinWeights" : [],

    "animation" : {}
}
},

"transform" :
{
    "position"  : [ 0, 0, 0 ],
    "rotation"  : [ -1.5708, 0, 0 ],
    "scale"     : [ 1, 1, 1 ]
},

"defaults" :
{
    "bgcolor" : [ 0, 0, 0 ],
    "bgalpha" : 1.000000,
    "camera"  : ""
}

}

There are a few things that need to be mentioned. First of all, the same line that declared normals in the previous export now reads "normals" : [0,0,1], This is consistent with Blender, with z as the up axis, but to make the plane face the correct direction in Three.js, it needs to be rotated, which is what "rotation" : [ -1.5708, 0, 0 ], does, appearing twice. This is why when directly adding the geometry (where the normals are [0,0,1]) to a THREE.Mesh to a THREE.Scene yields weird results, because a newly initialized THREE.Mesh does not have the 90 degree rotation needed to be correct in Three.js. When adding the exported object directly to the scene, it renders properly, because the object has the much-needed 90 degree rotation. However, THREE.Raycaster can only provide the original, unrotated face from the geometry, so when a ray is cast at the face, it will correctly report an intersection, but incorrectly report the normals. To summarize, THREE.Raycaster only returns the face in the original, unrotated geometry as opposed to the final, rotated object.

Conclusion

mrdoob commented 11 years ago

Basically, it all boils down to an incorrect assumption of what the intersections array has. I'm afraid I disagree. You should get the original normal of the face. What if you wanted it? You would then have to multiply the inverse matrix and that would be awkward.

So yes, having to do this is expected:

intersection.face.normal.clone().transformDirection(plane.matrix);

In fact, you should be doing this:

intersection.face.normal.clone().transformDirection(plane.matrixWorld);
willyvvu commented 11 years ago

True. I hope this helps clarify the output of THREE.Raycaster for any others who stumble upon this question. Thanks for clearing things up, @mrdoob !

WestLangley commented 11 years ago

In fact, you should be doing this:

intersection.face.normal.clone().transformDirection(plane.matrixWorld);

If plane.matrixWorld has non-uniform scale applied, you have to do the following to properly rotate the normal vector. In fact, I'd do it the following way always.

var normalMatrix = new THREE.Matrix3().getNormalMatrix( plane.matrixWorld );
var normal = intersection.face.normal.clone().applyMatrix3( normalMatrix );
AndrewRayCode commented 8 years ago

Using blender 2.77 and the latest version of the exporter I'm getting unexpected normals for the .json export, but they're working fine for the .obj export. I don't have a flip yz option in the threejs exporter version i'm using. any ideas @willy-vvu ?

Scharnvirk commented 8 years ago

Same to me. Also dealing with broken normals exports from blender, currently no idea. Unfortunately this thread is three years old...

siarhei-klimuts commented 8 years ago

Tried to export mesh (cube with inverted normals) and apply bump map to it. Some faces look good but the rest look awful. If I just don't export normals, all faces look good. Looks like my problem is related to this issue.

dherben commented 8 years ago

@Galiaf47 that probably is a different issue, where some recent changes in the exporter broke bump map orientation. Haven't gotten around to logging a bug for that one yet. Try exporting with exporter from r74 to see if that fixes your issue

siarhei-klimuts commented 8 years ago

@dherben Thanks for your answer, but I moved away from json exporter. I use convert_obj_three.py now.