Saving hierarchical pose presets

Issue #1603 resolved
Rakete created an issue

One limitation I’ve been running into is that once I merge armatures, then saving poses for them is not really possible anymore. I care particularly about saving poses for merged Face Controls, but it applies to all kinds of armatures.

To solve that problem I started implementing a way to save hierarchical pose presets in these commits:

https://bitbucket.org/rakete/import_daz/commits/0544b0ec1ca1ae2cea629a6521fdb44fab10102a

https://bitbucket.org/rakete/import_daz/commits/4460c4c2d1e44d33e7595cc605fed58ddb36e87d

The main change there is that I add a DazId to all PoseBones which identifies which armature those bones are from. Then I use that information to write a hierarchical pose preset. I tested this with a figure and the Face Controls merged, and when I applied the saved pose to the figure it was applied to both the figure and the Face Controls. Note the Face Controls need to be parented to the head for this to work, diffeo doesn’t actually do that by default and I don’t know if the information is actually available from loading the scene (it is parented to the figure, but the information that within that figure it is parented to the head bone is missing I think), I just do that in my wrapper code that is not part of diffeo instead.

Currently this only supports bones, morphs will not be saved in the hierarchical pose preset, and for modifiers I wonder if it is even possible. A pose preset can only have pose controls, not modifiers. But either way, what I still need to do to save morphs is a way to get the morph assets url(?) from the morph property so I can store that in the pose preset. I started with this here:

https://bitbucket.org/rakete/import_daz/commits/aaf79dfb990b7213bbd53ccd3377a0833be2ca6a

which just adds a way for me to add something to the ui.description of a prop. That is as far as I can tell the only real way how I could store a prop… on a prop. Otherwise I would have to do something like DazBoneMap maybe, but for props.

The code is kind of incompatible with old scenes, so it will not work unless you import a new scene. I also didn’t implement any UI for it, I just call diffeo through its operators so I only need the useHierarchicalPreset property.

I also added useExtraBones so that bones of merged armatures will be saved into the pose or not. And skipFollowers which skips bones that follow the parent armature (through CopyTransforms), this is so poses for un-merged followers can be saved without also saving all of the parent bones, which messes up the pose when you apply it in Daz to a follower.

Comments (35)

  1. Alessandro Padovani

    Handling hierarchical poses would be good, for example to load a figure pose with geografts, or with rigged hair or clothing or items in general. Actually we didn’t test hierarchical poses, may be something already works since when we merge rigs the extra bones are preserved. For example I can import a geograft animation after merging rigs.

    Then saving hierarchical poses as pose presets can be useful too for people using blender to animate back in daz studio.

  2. Alessandro Padovani

    Here in Italy the midsummer is the feast of St. John the baptist that we’re going to celebrate this sunday. Have a nice holyday.

  3. Rakete reporter

    I just tried it out, and it doesn’t work for me. There seem to be multiple problems:

    First the ID of the figure you use the name, but that can be changed in blender by the user, and will also be the wrong thing to use if I import a figure where I set a custom label in daz. Instead I think you need to use the thing after # from the DazId.

    Second the DazRigIndex, I can see the idea behind it, but I don’t think it works very well. If I understand the code correctly it will start counting up from zero everytime the merge operator runs, so it works fine when I merge all followers at once, but if I do them one by one, then the DazRigIndex will end up wrong (it will always be 1). But I don’t actually think this is causing any problems, its just inaccurate and makes it not that much more useful then the already existing DazExtraBone flag.

    The other thing though is that you then use the DazRigIndex to name the bones in the pose preset:

    def getDazBone(self, bname, pb):
            idx = pb.bone.get("DazRigIndex", 0)
            if idx == 0:
                return bname
            else:
                return "%s-%d" % (bname, idx)
    

    and that just doesn’t work? I don’t know if you can just rename the bones like that? For me at least I don’t seem to get any pose saved for merged rigs and when I revert that change it starts working again.

  4. Alessandro Padovani

    Commit a14dbe1.

    Doesn’t seem to work here either, I get errors. This also seems to break save pose preset in general, I mean without using the new hierarchical option.

    steps:

    1. import g8f with some geografts, I used futalicious plus headlights but any will do
    2. convert to simple ik and do some animation including geografts
    3. bake
    4. save as hierarchical pose preset

    Traceback (most recent call last):
    File "C:\Users\Alessandro\AppData\Roaming\Blender Foundation\Blender\3.5\scripts\addons\import_daz\error.py", line 222, in execute
    self.run(context)
    File "C:\Users\Alessandro\AppData\Roaming\Blender Foundation\Blender\3.5\scripts\addons\import_daz\preset.py", line 187, in run
    self.saveHierarchialPreset(rig)
    File "C:\Users\Alessandro\AppData\Roaming\Blender Foundation\Blender\3.5\scripts\addons\import_daz\preset.py", line 573, in saveHierarchialPreset
    struct["scene"]["animations"] = self.getAnimations(rig)
    File "C:\Users\Alessandro\AppData\Roaming\Blender Foundation\Blender\3.5\scripts\addons\import_daz\preset.py", line 639, in getAnimations
    Ls = [self.Ls[frame][bname] for frame in range(self.first, self.last+1)]
    File "C:\Users\Alessandro\AppData\Roaming\Blender Foundation\Blender\3.5\scripts\addons\import_daz\preset.py", line 639, in <listcomp>
    Ls = [self.Ls[frame][bname] for frame in range(self.first, self.last+1)]
    KeyError: 'shaftRoot-1'

    note. Personally I don’t use save pose preset since I’m not interested to import animations back in daz studio. So may be I miss something.

  5. Thomas Larsson repo owner

    I didn’t catch this bug because I didn’t test with actions. It should be gone in the last commit, and i finally go presets wtih geografts to work.

  6. Alessandro Padovani

    Commit a3365fd.

    Doesn’t seem to work here. Tried with a simple animation with G8F + futalicious but it seems to export nothing. I included a test scene it’s my simple animation already baked and ready to export.

    If I save the pose preset without using the hierarchy option then it works fine, but it only exports the figure animation without the geograft.

    steps:

    1. load the test scene futa-baked.blend
    2. save as hierarchy preset
    3. load the preset in daz studio, it requires G8F + futalicious

  7. Thomas Larsson repo owner

    It is the Root bone that messes up the bone hierarchy. If I delete it and export the preset, the animation appears in DS. The IK bones also don’t belong in the hierarchy, but they don’t seem to matter because they do not interrupt the line between the hip and the figure.

    We can use mhx (and probably rigify) to create presets as follows:

    1. Generate mhx with Keep DAZ Rig enabled.
    2. Create animation.
    3. Select daz rig.
    4. Mute Deform Rig
    5. Export preset.

  8. Alessandro Padovani

    I can confirm that deleting the root works fine. Any chance to fix this ?

    I mean we introduced the root for simple ik for better animation and it is supported by save pose preset without hierarchy. We save the object animation for daz studio as object + root iirc, should be the same for hierarchy. Let us know.

  9. Thomas Larsson repo owner

    Do we really need the root bone? It is really the same thing as doing an object transformation. But if it is important it could be ignored when saving presets.

    Hierarchial presets are much more picky than ordinary ones. The former work as long as there is a bone with the right name, but the latter require that the entire hierarchy is correct. Or at least all ancestors of a given bone.

  10. Alessandro Padovani

    The root bone is handy to avoid switching between object/pose mode when animating. Using the root bone is “the blender way”.

    In the simple ik rig the hierarchy is exactly the same since we preserve the daz rig, that’s what simple ik is for. It just adds the root bone at the top of the hierarchy that’s to be exported together with the object in the pose preset. That is, daz object animation = blender object animation + root bone animation = root bone animation in world space.

    # blender to daz
    daz object animation = root bone animation in world space
    

    note. If it is necessary we may remove the root bone from simple ik, as it was before. But it would be nice to keep it.

  11. Thomas Larsson repo owner

    OK, it wasn’t too hard to fix it. Now the root bone is ignored when we save a preset. The code quality wasn’t very good anyway, so it was good to improve it.

    Note that every bone called “Root” is ignored, so if we really have a bone with that name the preset will be broken. Names like “shaftRoot” are ok, though.

  12. Thomas Larsson repo owner

    Btw, I changed the names of (Un)mute Deform Rig to (Un)mute Control Rig. The operator names already referred to the control rig, so only the labels on the buttons have changed.

  13. Alessandro Padovani

    Commit 0ce8973 works fine here, thank you for the fix.

    I have no other bugs to report, but again I don’t use “save pose preset“ myself so others or Rakete may test it better.

  14. Rakete reporter

    I tried it by merging with my changes, and I still can’t get a working hierarchical pose out of it. I use it to export a pose for the Face Controls merged into a Genesis 8 rig.

    I looked at the pose it writes out and it seems the code tries to save bones for the merged rig that are not actually in the merged rig. Like hip, abdomen, neck, etc. when it should only save bones from Face_Controls_XYZ onwards (Face_Controls_XYZ is the root for the face controls follower, which is then parented to the head bone). I suspect that is causing my problem currently but I have not checked further.

    EDIT: One small change I already did is I used self.RootNames in def skipBone:

    def skipBone(self, pb):
            return (isDrvBone(pb.name) or
                    isFinal(pb.name) or
                    pb.name in self.RootNames)
    

    instead of just skipping over “Root” this skips over everything that is marked as root bone in self.RootNames.

  15. Thomas Larsson repo owner

    I focused on getting presets working for geografts, so I forgot checking that they worked for the face controls. Now the bone copies are not generated if the original figure was bone parented like the face controls. They are still needed for geografts, however.

    Now the generated duf files look fine, but face control poses don’t appear in DS, most of the time. I don’t know the reason for that. One reason could be that Merge Rigs deletes face control bones that are not used in Blender, thus changing the hierarchy. That has now been changed so the unused bones are still there but hidden. However, that didn’t seem to do the trick.

  16. Alessandro Padovani

    Didn’t do any test on this so it’s just a guess. But may be daz studio limits face controls to G81, the fact that we can use them in blender for G8 G9 doesn’t mean it is allowed in daz studio.

  17. Rakete reporter

    Still doesn’t work. I tried without my changes in master to make sure it is not something I changed.

    I created a simple test scene with just Genesis 8.1 + Face Controls, saved that, exported to blender, and imported using diffeo.

    Then I merged the Face Controls by selecting the Face Controls first, then ctrl select the main rig, click merge rigs.

    Then I changed the pose of both figure and face controls, and saved a hierarchical pose preset.

    When I then try to apply the saved pose preset in Daz I get the figure pose, but not the face controls pose.

    EDIT: Oh, I actually didn’t read the second sentence in your comment where you basicly say yourself it doesn’t work. I assumed it worked in your tests.

    Either way, yeah, something is still wrong. I still suspect its related to the naming of bones.

    EDIT2: Also I just noticed I did my tests with my changes applied actually, because blender crashed when disabling my addon and enabling master diffeo. But I just tried again with only master diffeo and the result is pretty much the same.

  18. Rakete reporter

    I think I figured it out, you’re missing a # in front of the parent for the Face_Controls_XYZ root bone:

    {
      "id" : "Face_Controls_XYZ-1",
      "url" : "name://@selection/Face_Controls_XYZ:",
      "parent" : "Genesis%208.1%20Face%20Controls"
    },
    

    needs to be:

    {
      "id" : "Face_Controls_XYZ-1",
      "url" : "name://@selection/Face_Controls_XYZ:",
      "parent" : "#Genesis%208.1%20Face%20Controls"
    },
    

    then it works.

    EDIT: in def getAncestors one of the node[“parent”] guys is missing a “#s”:

    node["parent"] = quote(figure2)
    

    needs to change to:

    node["parent"] = "#%s" % quote(figure2)
    

  19. Rakete reporter

    Also you mentioned deleted bones in the Face Controls, I actually ran into the same problem. Instead of changing the diffeo code to not delete unused bones, I just renamed the bones so they would use the names needed in Daz.

    For example for the mouth corners there are a whole bunch of bones and you ended up using the l/r_mouth_morners bones in blender, but in Daz the movement needs to be applied to l/r_mouth_corners2. So instead of keeping all the unused bones, you could also just do the same thing and rename the bones so that they all have the names of the bones in Daz that need to receive the movement.

    renames = {
        'l_Mouth_Corner': 'l_Mouth_Corner2',
        'r_Mouth_Corner': 'r_Mouth_Corner2',
        'Tongue_Narrow': 'Tongue_Narrow_L2',
        'Tongue_Narrow_R': 'Tongue_Narrow_R2',
        'Tongue_Tip_Narrow_Wide_L': 'Tongue_Tip_Narrow_Wide_L2',
        'Tongue_Tip_Narrow_Wide_R': 'Tongue_Tip_Narrow_Wide_R2',
        'Lips_Funnel_L': 'Lips_Funnel_L2',
        'Lips_Funnel_R': 'Lips_Funnel_R2',
        'Mouth_Press_Top': 'Mouth_Press_Top2',
        'Mouth_Press_Bottom': 'Mouth_Press_Bottom2',
    }
    for from_name, to_name in renames.items():
        if from_name in daz_rig.pose.bones and not to_name in daz_rig.pose.bones:
            daz_rig.pose.bones[from_name].name = to_name
    

    Maybe having slightly weird names is better then keeping a bunch of unused bones around.

    EDIT: Well, it is kind of a hack, I guess the proper solution would be to use the correct bones in the first place. That way the bone structure (as in how everything is parented to each other) would be kept, though when you’re already deleting bones, then I don’t think it matters all that much. Renaming them is easier.

  20. Thomas Larsson repo owner

    Thank you, that was it. I don’t think we need a hack for the unused bones. In the last commit they are kept on layer 5 but hidden, so the hierarchy is correct. When loading the preset into DS the face control bones set the facs morphs as expected.

  21. Log in to comment