Streamline custom morphs import to overcome missing morphs on pose import.

Issue #683 resolved
Mel Massadian created an issue

Hi,

I wanted to know if there could be a way to streamline the import of morphs by querying the animated ones first in DAZ.

I just played a bit with the SDK samples and came up with this basic script (below), it basically checks for morphs with more than one keyframe and prints them in the console with the CSV format (required for FBX exporter of DAZ, that’s how I use it in Houdini), couldn’t this be adapted as a separate Diffeomorphic script that the addon can read and import the morphs from the source morph files?

Line 120 is the asset URL that the DAZ importer would need

ListAnimatedMorphs.dsa

// DAZ Studio version 4.12.1.118 filetype DAZ Script

(function () {

    /*********************************************************************/
    // Array<DzProperty> : A function for getting a list of the properties in a group
    function getGroupProperties(oGroup, bTraverse, bRecurse) {
        // Declare an array to hold properties
        var aProperties = [];

        // If a group is not passed in
        if (!oGroup) {
            // We are done, return an empty array
            return aProperties;
        }

        // Get the number of proeprties in the group
        var nProperties = oGroup.getNumProperties();
        // Pre-size the properties array
        aProperties = new Array(nProperties);
        // Iterate over the properties, setting each element in the array
        for (var i = 0; i < nProperties; i += 1) {
            // Assign the property to the position in the array
            aProperties[i] = oGroup.getProperty(i);
        }

        // If we are recursing
        if (bRecurse) {
            // Concatenate the properties array from child groups
            aProperties = aProperties.concat(
                getGroupProperties(oGroup.getFirstChild(), true, bRecurse));
        }

        // If we are traversing
        if (bTraverse) {
            // Concatenate the properties array from sibling groups
            aProperties = aProperties.concat(
                getGroupProperties(oGroup.getNextSibling(), bTraverse, bRecurse));
        }

        // Return the array of properties
        return aProperties;
    };

    /*********************************************************************/
    // Array<DzProperty> : A function for getting the list properties for an element
    function getElementProperties(oElement, bTraverse, bRecurse) {
        // Get the property group tree for the element
        var oPropertyGroupTree = oElement.getPropertyGroups();

        // If the application version is 4.9.4.101 or newer and we want all properties
        if (bTraverse && bRecurse) {
            // Return the properties for the element
            return oPropertyGroupTree.getAllProperties();
        }

        // Get the first group in the tree
        var oPropertyGroup = oPropertyGroupTree.getFirstChild();
        // Return the properties for the element
        return getGroupProperties(oPropertyGroup, bTraverse, bRecurse);
    };



    function isMorph(oProperty) {
        var oOwner = oProperty.getOwner();

        return oOwner.className() == "DzMorph";

    }


    /*********************************************************************/
    // void : A function for reporting information about a property
    function printPropertyInfo(oProperty) {
        var oOwner = oProperty.getOwner();
            print(String("- %1 \"%2\" :: %3 (%4/%5) \"%6\"")
                .arg(oOwner.className())
                .arg(oOwner.name)
                .arg(oProperty.className())
                .arg(oProperty.getPath())
                .arg(oProperty.name)
                .arg(oProperty.getLabel())
            );
    };

    /*********************************************************************/
    // - MAIN -------------------------------------------------------------
    // Get the primary selection
    var oNode = Scene.getPrimarySelection();
    // If nothing is selected
    if (!oNode) {
        // We are done...
        print("NOTHING SELECTED")
        return;
    }

    print("GETTINGS ANIMATED MORPHS\n")

    // Initialize a list of properties
    var aProperties = [];

    // Declare working variable
    var oProperty;

    // Collect the properties on the node
    aProperties = aProperties.concat(getElementProperties(oNode, true, true));

    // Get the properties of the node's parent
    var out = ''
    // Iterate over the collected properties
    for (var i = 0; i < aProperties.length; i += 1) {
        // Get the 'current' property
        oProperty = aProperties[i];

        // If the property has more than one keyframe
        if (oProperty.getNumKeys() > 1) {
            if (isMorph(oProperty)){
                var oOwner = oProperty.getOwner();
                print(oOwner.assetUri.toLocalFilename())
                out+= String('"%1", "Export"\n').arg(oOwner.name);
            }
            else{
                out+= String('"%1", "Export"\n').arg(oProperty.name);
            }
            // Print information about the property
            // printPropertyInfo(oProperty);
        }

    }
    print(out);

    // Finalize the function and invoke
})();

Comments (14)

  1. Mel Massadian reporter

    Maybe this should be done differently. I realize you added pose from scene. I think that those paths should be part of the DAZ scene, so if you parse it we could maybe use pose from scene to also import any custom morphs (with a dialog or smth).
    I will look into the source to see if I can propose a PR

  2. Thomas Larsson repo owner

    I’m not quite sure how this is supposed to work. Should the user create a database with all available morphs in DS once and for all, or should he run a daz script first before importing morphs in Blender? That would require that DS is open, something that I avoid as often as possible. One could figure out the dependencies in Blender as well, but only if you first open and parse all files, which would take a lot of time with a large database.

  3. Mel Massadian reporter

    The script is indeed intended to run in DAZ but it was more a POC on how to get the right URL (oOwner.assetUri.toLocalFilename())

    Because when using several custom morphs, knowing the path of each is quite hard (might be me not knowing the workaround there).

    I thought about parsing the whole scene but you are right, it can be quite long to parse each and every linked dependency.

    So another quite similar idea would be to add a DAZ script for favorite morphs a bit like how the library path script works.

    And again maybe I’m overlooking something, how do you deal with custom morphs usually?

    Thanks

  4. Alessandro Padovani

    my opinion

    Importing all the available morphs for a figure, in general, may be overwhelming and not necessary. That is, a typical figure may have thousands of morphs depending on the user library. So daz studio itself takes ages to load some figures when the user gets a large database. That’s what I call the daz morphs hell and you don’t want to replicate that in blender.

    On the other side it is tedious to have to load the same morphs again and again when we import a daz figure. And that’s what favorite morphs are for, if I understand it correctly.

    https://diffeomorphic.blogspot.com/2021/05/morph-presets-and-easy-import.html

    It seems to me that Mel is proposing a new way to create a favorite morphs list. That is, parsing a daz animation file to search for the used morphs then store it in a favorite morphs list. Or add to an existing favorite morphs list.

    edit. important. As a side note, in a daz animation file there may be several morphs that are baked in dbz, so these morphs should not be loaded in blender, and it may be difficult to check what is what.

  5. Xin

    Something that could be done is to display a list of the detected morphs in a dialogue in daz just before the “favorite morphs” file is generated, so the user can uncheck the morphs they don’t need. This would be similar to what the official daz bridge does, but the official bridge still ignores some morphs and is not reusable like a “favorite morphs” file (you need to do the same for every single export).

    So, basically, this could be a utility script to facilitate the creation of “favorite morphs” files from within daz.

    And I do agree with Alessandro that morphs baked with the dbz should be excluded or receive a warning/special indicator in the dialogue.

  6. Mel Massadian reporter

    Yes, that’s exactly the idea, I animate quite a lot inside DAZ (facial expressions mostly) and I have a vast collection of custom morphs

    In case it wasn’t clear the script I currently use filters only animated morphs so not all the characters morph, and until now they are all valid morphs.
    I’m not familiar with dbz morphs (did not encounter them yet) so I’m not sure about that..

  7. Alessandro Padovani

    Mel, there’s not “dbz morphs“. The dbz file is what you save with the daz export script and the geometry is baked from the viewport so any applied morph will do.

    As for the morphs list, if I understand correctly, what you’re proposing is to store only the animated morphs. That is, the actor morphs usually will not be animated because they are used only in the first keyframe to define the figure, so they will not be exported. The pose morphs will have more than one keyframe so they will be exported.

    An exception is when the daz animation defines a morphing figure, for example a human turning into a werewolf. In this case the daz animation will include actor morphs. That can’t be used in blender because morphing armatures are not supported.

    Another exception is when a daz figure is mixed from other figures. In this case the daz animation will possibly include a mix of custom morphs and jcms. That again, if I understand correctly, is not supported in blender and the standard jcms are used instead.

    But overall the idea to exclude non animated morphs seems good to me. That should match with the dbz fine enough I guess.

  8. Mel Massadian reporter

    Oh sorry, I forgot morph fitting was of dbz extension. You’re right I did not think about that. Is there a distinction in Daz between additive morphs and proportion-changing ones? (I know that they are actually all additive)

  9. Thomas Larsson repo owner

    On a related note, but not quite the same. If you import an expression with Load Pose, the script now tries to load any missing morphs (by default at least). To do so the script scans the entire database, but it only looks at the file names instead of opening and parsing the files, so it is much faster. This can go wrong, though, if morph names are different from file names.

  10. Alessandro Padovani

    It is very likely that morphs ids and file names are different, I mean in my experience it happens often. So it is necessary to open and scan the files I fear.

    • daz morph name = file name
    • daz morph id = unique morph name used in expressions
    • daz morph label = name used in the user interface

  11. Thomas Larsson repo owner

    We do not want to open all files every time we load an expression, but one could have a separate button that does this once and saves the result in a file (at a location where the user has write access). The user will have to rerun the tool every time new assets are added to the daz database though.

  12. Alessandro Padovani

    Yep I agree that scanning the files is slow. But the user too may create new morphs presets. Other than adding content to the library. Creating a “global” reference once may work fine if the library doesn’t change often.

    May be one could go with incremental upodates. That is, create the global reference once, then if a morph is already in the global reference it’s not scanned, otherwise the new references are added. No idea how hard this is to implement though, and if it is worth the effort.

    edit. Also I suspect that a “global“ scan may end up containing a huge amount of “actor” morphs that are really never used in blender because of the dbz.

  13. Log in to comment