I am currently exporting a model and clothing separately in the gltf format from blender. I have painted skin weights on the clothing items in blender and they track well when the character is animated in blender. When I import the clothing items into threejs, they are imported as meshes vice skinnedmeshes and the skin weights and skin arrays are all 0. Is this because the gltf loader isn’t recognizing the clothing pieces as skinnedmeshes?
My questions are: How do I ensure that the skin weight data stays with the clothing items even if there isn’t an armature associated with the clothing on import? If I can get the skin weight/indices data to be retained, how do I ensure that the skin weights are associated with the correct armature (I will only ever have one armature in the scene)
I can provide an example of the model later this afternoon if that would be helpful. Thanks!
I’m not sure it will be possible to export a mesh with skin weights, without also exporting an armature… the format does allow it, but it sounds tricky to manage both on the exporter and on the loader. Can you share the files you’re describing?
The armature should not be large; can you include it with the clothing and just replace the bones after loading? Alternatively you could put everything into one glTF file and (if necessary) load it progressively.
I can provide the files this afternoon when I get home from work. I don’t think it will be possible to include all files into one file since the goal is to eventually have hundreds of different clothing items to swap between.
The idea to export each object with the armature attached is very interesting, and something I definitely never considered. Should it automagically affect all previously imported clothing items that have been put on the model as long as it is correctly positioned or does something have to happen behind the scenes to make them associate correctly? I’m assuming that I would have to reparent all previously imported items to the new armature prior to dumping the old armature, but that shouldn’t be too tricky.
merging separate exports
If each file contains one SkinnedMesh, you should be able to:
(1) load the character with its armature and animations, adding everything to the scene
(2) save the SkinnedMesh, e.g.
character = model1.getObjectByName('MyCharacter');
(3) load a clothing model, save the SkinnedMesh, e.g.
shirt = model2.getObjectByName('shirt')
(4) bind the clothing model to the character’s skeleton:
shirt.skeleton = character.skeleton;
shirt.bind( shirt.skeleton, shirt.bindMatrix );
(5) add the entire character model (
model) and only the clothing SkinnedMesh (
shirt) to the scene.
There are plenty of ways this might go wrong, but with some effort it should be possible.
progressively loading a single file
Assuming you can get Blender to export everything in a single glTF, it should be possible to split the data and load it piece-by-piece. This will likely take a little scripting, but may be easier than the option above if the skin data isn’t merging properly…
(1) export everything to a single
.gltf file using the “embedded buffers” option, and ensure textures are not embedded.
(2) split the file so that each mesh’s geometry is in a separate
.bin file. glTF-Split is a (very experimental) way of doing that; it will likely require fixes for your particular models.
(3) use the version of GLTFLoader on the dev branch, and apply changes from https://github.com/mrdoob/three.js/pull/14492.
You’ll end up multiple files and one
if the largest part of your clothing models is textures rather than geometry, you can likely skip (2).
Option 1 is definitely the best option for my situation. The only remaining question I have is how to force the importer to load the object as a skinned mesh rather than as regular mesh? Otherwise, I think it goes ahead and drops all the skin weight data.
The idea with (1) was that you should have the armature included in each model, to ensure all of the skin weights and indices are kept. Once you’ve loaded the clothing model you can just discard those bones and bind the SkinnedMesh to the character’s skeleton.
If you’re not exporting the armature, I have no idea whether Blender will write the weights correctly. Assuming Blender does write the weights and indices, it may or may not be possible to force the loader to create a SkinnedMesh… you also need a bindMatrix, which Blender almost certainly won’t provide without an armature, and I don’t know if you can just copy it from the character model or not.
This all makes sense. I’ll try it out and report back. Thanks!
Update: here is the codepen: https://codepen.io/michael-tipton/pen/PyJXZO?editors=0011 (Sometimes the files dont load correctly. If that is the case, just hit run again and it should eventually load)
I tried implementing solution 1, but I am running into a roadblock. After switching armatures and parenting the object to the correct bone on the new armature, the object does not translate and rotate based on the bone it has been been parented to. Instead it just translates back to the origin in its default rotation. (where local position and rotation are both (0, 0, 0) and (0, 0, 0, 1) respectively. I have attempted calling updateMatrixWorld on the parent bone but this has no effect.
If I dont swap the armature, but just parent the object to the bone on the new armature, it does go to the correct location and rotation.
Do you have any ideas as to why the child mesh is not adopting the position of the bone it is parented to after swapping the armature?
race8.glb (2.9 MB)
glove1.glb (1.7 MB)
I don’t think I’ve ever seen a SkinnedMesh used as a child of its own skeleton in three.js… that doesn’t seem like it should be necessary if the bindMatrix and joint weights are correct.
It isn’t really clear to me what the bind matrix does. How is the initial position and rotation of the skinned mesh set if it isn’t parented to a bone?
Not sure if this is important or not but I did note that in the two cases in the codepen, the world matrix and local matrix for the two examples are almost swapped.
For the correct position/rotation we have: local = Matrix.Identity, world = [ some other stuff ]
In the wrong position/rotation we have local = [ almost the same other stuff] world = Matrix.Identity.
Additionally, the Bind and Inverse Bind Matrices are both the Identity Matrix. Is this likely something that I need to make sure is set correctly when exporting from blender?
So I’m able to initially position the glove using the world matrix of the bone I was orginally parenting the object to as the bindmatrix. This does not work if the model is out of the T-pose. Is the bind matrix just a constant matrix? If so, how does it account for a model that isn’t in the rest position?
I solved this (with lots of help by Don) by using world matrix of the bone that the mesh would be centered on as the bind matrix.
parentBone = getBone(meshName);
shirt.skeleton = character.skeleton;
shirt.bind( shirt.skeleton, parentBone.matrixWorld );
Just make sure that the parentBone you are using actually moves in your animations. I was attempting to use a bone in the hand that wasnt actually being transformed in the animations which was why this was working for me in T-pose, but not in other poses.
Ah that’s great, glad you were able to solve it!