Closed patrickkidd closed 12 years ago
I'm not sure I understand the question. Shadows should just work with morphs:
http://mrdoob.github.com/three.js/examples/webgl_shadowmap.html http://mrdoob.github.com/three.js/examples/webgl_morphtargets_md2.html
The shadows do update on other objects, but I am having trouble on the object that is morphed itself. Notice how the lighting changes with the following model is rotated (the individual cylinders also rotate inside it, and everything is contained in a single png-textured geometry using morph targets).
The lighting is exactly the same in each frame when I take the spotlight away leaving just the ambient light:
I'm no expert with this stuff, so I'm sorry if I'm making a simple mistake here.
Aha, I think this is not about shadows but about normals - what you need is to turn on morphNormals
.
By default morphs just keep the normals from the first frame, so no matter which change happens in the animation, they will be lit as this first frame (this is cheaper and can look ok if morph poses don't differ a lot).
Check this example to see how morphNormals
work:
http://mrdoob.github.com/three.js/examples/webgl_morphnormals.html
You'll need to do 2 things - turn on morphNormals
flag in the material and compute normals for all animation frames like this:
geometry.computeMorphNormals();
That did it, thanks! computeMorphNormals is slow, but that definitely answers the question.
Another related question - I changed the "shading" property in the JSON materials to "Phong" to get some spectral highlights, and now found that it suddenly changes again when animating:
The first one is when it's stationary, and the appearance suddenly changes to the second image when I start animating it. Am I doing something wrong?
Hmmm, this could be normals issue yet again but a bit different.
The trouble with morph normals is that normals for animation frames are computed automatically while for static mesh you can have custom vertex normals loaded from the model file. Such user supplied normals can have more information than automatically computed ones, e.g. if smoothing groups were used in the modeling program.
Quick test if this is indeed the case could be to export model without any normals and just use automatic normals also for static model:
geometry.computeVertexNormals();
Right again. It's also important to note that you have to call computeMorphNormals() before adding it to the scene or numMorphNormals will = 0 on geometryGroup forever.
It turns out that the normals calculated from the smoothing groups in 3DSMax look better than the three.js ones so I'll try to add support for them in my patched ThreeJSExporter.ms. This should also get around the prohibitively slow performance hit from computeMorphNormals as long as the JSON parsing isn't too slow as a result.
Am I right in assuming this or is there still some computation required if I have normals stored in JSON for each morphTarget? Maybe you can give me a tip on where to get my Loader to stick the morphNormals from MAX into the Geometry object? I see 'faceNormals' and 'vertexNormals' in geometry.morphNormals, not sure where they come from.
Thanks!
Am I right in assuming this or is there still some computation required if I have normals stored in JSON for each morphTarget
I think not, it should be just like with vertex normals for static meshes, these would go directly from the JSON into appropriate data structures (the same place where now go computed vertex normals).
And for face normals, I guess the simplest would be just to ignore them for the moment (unless you would want to add yet another thing to JSON format or have separate method to compute just them).
These come in play just for flat shading and picking.
I see 'faceNormals' and 'vertexNormals' in geometry.morphNormals, not sure where they come from.
These are computed in geometry.computeMorphNormals()
based on vertex positions for each morph pose.
Getting normals and morph normals directly from MAX is preferred to take advantage of things like smoothing groups and also decrease the time it takes to set the object up because you don't have to compute the morph normals. I added morphTargets[n].normals to my MAX exporter (pull request coming, I promise :)), and wrote importMorphNormals as a replacement for computeMorphNormals. Can you do me a favor and have a look at my importMorphNormals? For some reason I'm seeing a change from the unmorphed shading and morphed shading again even when all normals are imported from the JSON. At first glance the values in json.morphTargets[n].normals looks OK...
THREE.Geometry.prototype.importMorphNormals = function(json) {
var i, il, n, nl, f, fl, x, y, z, vA, vB, vC, cb, ab,
quads, morphTarget, morphNormals, faceNormals, vertexNormals, tmpVertexNormals
quads = this.faces[0] instanceof THREE.Face4;
for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {
jsonNormals = json.morphTargets[ i ].normals;
morphTarget = this.morphTargets[ i ];
faceNormals = [];
vertexNormals = [];
morphNormals = this.morphNormals.push( {
faceNormals: faceNormals,
vertexNormals: vertexNormals
} );
// these are duplicated by reference in morphNormals[n].vertexNormals
tmpVertexNormals = [];
for( n = 0, nl = jsonNormals.length; n < nl; n += 3 ) {
x = jsonNormals[ n ];
y = jsonNormals[ n + 1 ];
z = jsonNormals[ n + 2 ];
tmpVertexNormals.push(new THREE.Vector3(x, y, z));
}
cb = new THREE.Vector3()
ab = new THREE.Vector3();
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
// from Geometry.computeFaceNormals()
vA = morphTarget.vertices[ face.a ];
vB = morphTarget.vertices[ face.b ];
vC = morphTarget.vertices[ face.c ];
cb.sub( vC.position, vB.position );
ab.sub( vA.position, vB.position );
cb.crossSelf( ab );
if ( !cb.isZero() ) {
cb.normalize();
}
faceNormals.push( cb.clone() );
if( quads ) {
vertexNormals.push( {
a: tmpVertexNormals[ face.a ],
b: tmpVertexNormals[ face.b ],
c: tmpVertexNormals[ face.c ]
} )
} else {
vertexNormals.push( {
a: tmpVertexNormals[ face.a ],
b: tmpVertexNormals[ face.b ],
c: tmpVertexNormals[ face.c ],
d: tmpVertexNormals[ face.d ]
} )
}
}
}
}
@alteredq I forgot to mention you so you'd get alerted to my new comment. Is it better to create a new issue after this one goes a couple of weeks without activity or just mention you?
Here is an example of what I am seeing:
Loader = function(showStatus) {
THREE.JSONLoader.call( this, showStatus );
}
Loader.prototype = new THREE.JSONLoader();
Loader.prototype.constructor = Loader;
Loader.prototype.supr = THREE.JSONLoader.prototype;
var IMPORT_MORPHNORMALS = true
Loader.prototype.createModel = function ( json, callback, texturePath ) {
var Callback = function(geometry) {
geometry.maxObjectName = json.metadata.name
if ( json.morphTargets !== undefined ) {
for(var i in json.morphTargets ) {
geometry.morphTargets[ i ].action = json.morphTargets[ i ].action; // pks: Animation Name
geometry.morphTargets[ i ].frame = json.morphTargets[ i ].keyframe; // pks: keyframe number
}
for(var i in geometry.materials)
geometry.materials[i].morphNormals = true
if(IMPORT_MORPHNORMALS) {
if(json.metadata.normals > 0 && json.metadata.morphTargets > 0)
geometry.importMorphNormals(json)
} else {
geometry.computeVertexNormals()
geometry.computeMorphNormals()
}
}
if(callback)
callback(geometry)
}
THREE.JSONLoader.prototype.createModel.call(this, json, Callback, texturePath)
}
here is the model JSON:
Here is an runnable example:
That's a lot of code and many steps in the asset pipeline, it's hard to tell what's going on.
I would suggest to break it into smaller problems.
What you could try is to create static geometry from the morph vertices and morph normals for a particular animation frame (instead of usual vertices
and normals
arrays) and check if there shading looks as expected.
From the screenshots and runnable example it kinda looks like if morph states were completely unshaded (colors coming just from texture, not lighting).
@alteredq I've trimmed it even more. MorphAnimMesh.js basically just progresses currentKeyframe like THREE.MorphAnimMesh, and RiggedPerspectiveCamera just controls the camera so you can ignore those two files. Here is an updated version:
http://www.res3d.com/research/morphNormals/roller-screw2.zip
Maybe the normals are flipped or something?
@alteredq I tried making a new mesh for a single morph target but am getting the following error in chrome:
(10) GL_INVALID_OPERATION : glDrawXXX: attempt to access out of range vertices
WebGL: too many errors, no more errors will be reported to the console for this context.
The following code block is how I'm constructing the geometry, assuming "this" is my main mesh from JSONLoader.js, and "morphTarget.tmpVertexNormals" is the vertex normals from the JSON model for that morph target. Am I leaving anything out?
var morphTarget = this.geometry.morphTargets[frame]
var geometry = new THREE.Geometry
geometry.vertices = morphTarget.vertices
// clone faces
var normalIndex = 0
var f, fl, face, faces = this.geometry.faces
for(f = 0, fl = faces.length; f < fl; f++) {
face = geometry.faces[ f ] = faces[ f ].clone()
for(var vn=0; vn < 3; vn++) {
face.vertexNormals[ 0 ] = morphTarget.tmpVertexNormals[ face.a ]
face.vertexNormals[ 1 ] = morphTarget.tmpVertexNormals[ face.b ]
face.vertexNormals[ 2 ] = morphTarget.tmpVertexNormals[ face.c ]
}
}
geometry.computeFaceNormals()
var mesh = new THREE.Mesh(geometry, this.material);
var m, ml
for(m = 0, ml = this.geometry.materials.length; m < ml; m++)
geometry.materials[m] = this.geometry.materials[m]
mesh.scale = this.scale.clone()
@alteredq @mrdoob : SOLVED.
I feel like I kicked up an issue storm with this one. Thanks for humoring me. It turned out that my max exporter was exporting the wrong values for the normals after all. I had to really dig into the values for a few days though.
Oh well, I had to walk the path of learning how the face and vertex normals work and how they are stored in morphNormals.
This is what it's supposed to look like:
http://www.res3d.com/research/morphNormals/roller-screw3.zip
Yay! :)
Yeah, it it looks amazing with smoothing groups from max.
So are you guys interested in a pull request for the patched MAX exporter and accompanying importer code to support morph normals? If so I can issue a pull request with this stuff in it. I don't know if this is too far off your intended feature list or not. Also I use a code style that's less spacious than @alteredq but it works.
The max exporter has lots of new features now. The only ones I can remember right now are:
On May 15, 2012, at 3:59 PM, Mr.doob wrote:
Yay! :)
Reply to this email directly or view it on GitHub: https://github.com/mrdoob/three.js/issues/1828#issuecomment-5730762
I don't know about a pull request. But it you could share the exporter so we can take a look that'd be great :)
OK, will do tomorrow.
On May 15, 2012, at 4:34 PM, Mr.doob wrote:
I don't know about a pull request. But it you could share the exporter so we can take a look that'd be great :)
Reply to this email directly or view it on GitHub: https://github.com/mrdoob/three.js/issues/1828#issuecomment-5731202
Yay indeed ;)
I don't know about a pull request. But it you could share the exporter so we can take a look that'd be great :)
Seconded. It would be good to have it somewhere so that other Max users could use it / build upon it.
Or maybe we could eventually implement morphNormals also to other exporters.
This is what it's supposed to look like:
http://www.res3d.com/research/morphNormals/roller-screw3.zip
Looks much better, though it seems like some faces are inverted (probably because vertices are ordered differently).
I remember having such issues with Max. I think this comes from some modifiers.
It's not normally visible in Max as everything there is by default double sided, but there is a tool somewhere in Max menus to visualize these incorrect faces so you can fix them (I think it highlights them in yellow or green).
If I remember correctly it was something like "Xform reset" in... utils?
Good keywords ;)
Here is what I managed to dig out of my Gmail outbox, instructions I sent for how to fix these:
- find out problematic objects in Max
- select all objects
- set View -> xView to show "Face Orientation"
- select all object that are fully/mostly green
- in Utilities apply Reset XForm to selected objects (this will show messed normals also in Max, otherwise they are hidden)
- fix problematic objects
- apply Normals => flipNormals modifier to selected object
Ah, the memories... It's been 10 years now since I had to do that process. :D
OK guys, I pushed it to my fork. I'm an old CVS/svn guy so I'm new to this github craziness. The new exporter is here:
https://github.com/patrickkidd/three.js/tree/master/utils/exporters/max
This new version has lots of subtle tweaks that help modelers working with legitimate commercial0sized models like the max progress bar, cancel button, better I/O buffer for writing to network drives, multiple named output files, etc. We have a vested interest in getting three.js to work well with max so I have a bunch more features (materials, lights, object animation splines, animations w/ pos/scale/rot, etc) planned for this thing.
Check out the README.txt for changes, etc. There is also Loader.js that is modified to read the morph normals.
Thanks for all your help up to this point.
Is it possible to update the shadows on a mesh when animating with morph targets? When you rotate a textured object the shadows rotate with it...
Thanks!