-
Notifications
You must be signed in to change notification settings - Fork 323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Import: fix skinning (and other node hierarchy problems) #857
Conversation
Thanks for the callout. I was getting frustrated. What's a good way to test this? |
I tested #850 and it worked. Thank you so much. 💯 |
Introduces an intermediate data structure, a graph of "virtual" nodes, that gives us a lot of flexibility in manipulating the node graph before we create the "real" Blender nodes. A series of passes transforms the glTF node graph into something we can import into Blender. Skinned meshes are also moved to their armature and hard-skinned into their bind position at mesh creation time. Together all this fixes all the skinning issues I could find. Some stuff was dropped (scenes/collections, Yup2Zup removal, selecting/active, bone chains) for simplicity. Can be worked on later.
Hello @scurest First, I would like to include guys from sketchfab into testing process. @AurL Aurélien, can you please have a look into it, or forward it to the new responsible og glTF in Sketchfab?
One possible solution is to import all in current scene, and to create (if needed) some collection(s) to reflect glTF scene. That way, no issue with glTF node in multiple glTF scenes, because all Blender objects will be in same scene, but collections can be a good way to display glTF scenes. This can be done later, not directly linked to this PR.
Using debug values to get rid of Yup2Zup is part of the production pipeline in Airbus DS. So this has to be restored, we can't drop it.
Same here, Airbus DS need to have the root of imported glTF active at end of import. This need to be restored. The stuff for making bone chains was dropped. You can't change the bone length after import because it has significance in the transform hierarchy (it affects objects parented to the bone). It should be possible to add a vnode pass to rotate/resize bones (see #324) after this. Yes, confirmed. We can drop it for now (and we need to think how we can manage it later). |
Apply coordinate conversions whenever we read coordinates from the glTF file, allowing us to remove the Yup2Zup node. In debug mode, the conversion functions don't do the Yup2Zup conversion. A vnode pass was added to move cameras/lights to fresh children with an appropriate correction factor for the differing conventions about what axes they point on, just like how the exporter works.
Thanks for looking @julienduroure. I know it's huge >< The active object is easy. Could you clarify what is supposed to be selected though? In master it depends on a lot of different things (like whether there are animations and what the debug_value is). Should I just select all (and only) the imported objects? |
I made the first object in the default glTF scene active and selected. Tell me if you wanted something else. I also pushed the code for getting rid of the Yup2Zup node by hard-applying the conversion whenever we read. You'll notice that the direction bones point changes and correction nodes are inserted for cameras/lights (just like how the exporter works). I can change these in a later PR (I have the code for it written already). |
Regarding active object: Regarding Yup2Zup node : No selection is required at end of import. Current rules :
|
@scurest Seems we updated this thread in same time :) |
We did :) I amended the last commit to set the active object like you said and not select anything. |
Can you please also restore log on debug 101 ? |
Sure. I have it print the vnode id now. |
Bones use a cache to all put their channel in an action on the armature object. But objects were (erroneously) not using the cache, so an armature object could wind up with two actions, one with pose bone stuff and one with its own loc/rot/scale.
#862 occurs when an armature object has both its loc/rot/scale animated, and also has pose bone animations, and two actions would get created. That didn't ever used to happen since the importer appears to create a new node for the armature that wasn't in the glTF node tree, so it's loc/rot/scale couldn't be animated. In this branch, an existing node is turned into the armature (except when that's not possible and the dummy root is turned into an armature). So the animation_node code now uses the same cache as the animation_bone code to be sure only one action is created. Here you can see the difference in the node tree, master/this branch. You can see the extra "6" node in master. Thanks for catching that @fire :) |
We need to make a copy or else we modify the pyskin.joints list (same thing as #858).
Is this targeted for 2.82? https://developer.blender.org/project/view/89/ |
@fire No, BCon3 is tomorrow, I don't have time to test it heavily. This is a very good improvement (thanks again scurest :) ), but this is quite a big change, so we need time to test it carefully. |
Pre-calculate the joint matrices so we only have to do one matrix- vector multiply for each term instead of two. Also compute the weight sums during the main loop and divide at the end. This is very slightly faster, but actually skinning the verts doesn't seem to take that long to begin with.
@julienduroure How's this coming? |
Tests are in progress. |
Tests that I/we want/need to perform:
|
|
Thanks a lot @scurest for this huge improvement ! |
These changes look excellent, thank you @scurest! |
Very large patch, but it's a massive improvement for all node hierarchy problems, most notably skinning.
Fixes every file I tested \(T∇T)/
Fixes #239.
Fixes #257.
Fixes #483.
Fixes #509.
Fixes #698.
Fixes #699.
Fixes #700.
Fixes #703.
Fixes #847.
Fixes #848.
Fixes #850.
Also fixes the model from #251.
Super big thanks to @fire btw for filing all the issues with test models <3 They were really invaluable, thanks!
Example before/after (#703)
Virtual nodes
The main piece of machinery added is an intermediate graph of "virtual" nodes, or vnodes. The glTF node graph is copied to a vnode graph, and a series of passes are done to transform it into a form we can import into Blender. This gives us a lot of flexibility in doing "global" transforms to the node graph before we commit to it by creating real Blender nodes for everything.
Vnodes are stored in a flat dict and referenced by keys just like glTF nodes are stored in a flat array and referenced by indices. Integer keys correspond to vnodes that came from glTF nodes. A root node is always inserted with key
'root'
.Summary of the vnode passes
Here's a visualization of how the graph gets transformed from "glTF form" into "Blender form"
Joints, skins, and armatures
In master, the importer believes that every node is used by at most one joint, and it uses the inverse bind matrix of that joint to set the bone's local-to-parent. This is obviously not viable if there is not a unique joint that uses a node.
This PR changes our attitude about bones and joints. The importer now thinks each bone should have the same world transform it has in glTF (no dependence on the inverse binds). It's also no longer correct to think of skins as imported as armatures; instead a vnode pass marks nodes that should be armatures or bones for the sole purpose of satisfying the following Blender constraint
There's no problem with overlapping skins if we think about it this way.
Skinned meshes
Where in the glTF node graph a skinned mesh is instantiated doesn't affect its transform; only the nodes used by its joints do. So we always move a skinned mesh from wherever it was in glTF to (a fresh child of) the armature that contains its bones. The fresh child has the identity transform, so the skinned mesh has the same world transform as the armature, and then there's a clear path bones-to-arma, then arma-to-world, where the mesh isn't affected by anything else.
We also need to calculate the positions of the vertices in the bind pose ie. the pose the edit bones are in. This might not be the same as the glTF, mainly because glTF nodes can be scaled but edit bones can't. Skinning code for the verts was added to primitive creation. This is the place the inverse bind matrices are now used.
Animation
Not much to say here, but it was updated to use vnode ids. The bone code has become simpler; there's a straight-forward formula for the TRS of the pose bones, which is nice.
We do have to be cognizant of objects parented to a bone (we need to translate them back to the head of the bone since they're at the tail by default).
Things I dropped
These are things I stripped out that I'd like to address later