Import Xgen Groom with conversion settings not working (Python)

I am trying to import an alembic groom and apply conversion settings via Python.

In the unreal module I’ve found unreal.GroomImportOptions and unreal.GroomConversionSettings. I’ve created instances of both, and set the “conversion_settings” editor property of the GroomImportOptions with my GroomConversionSettings. The GroomImportOptions are then passed to my asset import function. The imported groom did not receive the conversion settings during the import.

I’m new to unreal and wondering where this is breaking down. Is my build_import_task function breaking something, or missing something? Do I need to modify the editor’s Groom Import Options (the ui) via Python somehow before attempting to import? Using UE 4.26. Appreciate any help here. Thanks!

Here’s the code I’m using:

def build_groom_options():
    # xgen conversion values
    vec_rot = unreal.Vector(-90, 0.0, 0.0)
    vec_scl = unreal.Vector(1.0, 1.0, -1.0)
    
    # conversion settings
    groom_settings = unreal.GroomConversionSettings()
    groom_settings.set_editor_property('rotation', vec_rot)
    groom_settings.set_editor_property('scale', vec_scl)

    # generate options and combine conversion settings
    groom_options = unreal.GroomImportOptions()
    groom_options.set_editor_property('conversion_settings', groom_settings)
    
    return groom_options


def build_import_task(path, destination, name=None, options=None):
    if not name:
        name = "no_name" 
    task = unreal.AssetImportTask()
    task.set_editor_property('automated', True)
    task.set_editor_property('destination_name', name)
    task.set_editor_property('destination_path', destination)
    task.set_editor_property('filename', path)
    task.set_editor_property('replace_existing', True)
    task.set_editor_property('replace_existing_settings', True)
    task.set_editor_property('save', False)
    task.set_editor_property('options', options)

    return task


def exec_import(tasks):
    result = unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks(tasks)

    return result


def import_asset(path, destination, name=None, options=None):
    task = build_import_task(path, destination, name, options)
    exec_import([task])

    return task

# test
path = "/path/to/hair.abc"
dest = "/Game/Hair"
name = "hair_001"
options = build_groom_options()

import_asset(path, dest, name, options)

Okay @anonymous_user_87e5ba83 … I have figured out how to get this to work:

You have to create an GroomAssetImportData object and pass in the GroomImportOptions object into the constructor. The finally set the GroomAssetImportData as the AssetImportTask options.

conversionSettings = unreal.GroomConversionSettings(rotation=[90.0, 0.0, 0.0],
scale=[1.0, -1.0, 1.0]) # maya to unreal diffs
options = unreal.GroomImportOptions()
options.set_editor_property(‘conversion_settings’, conversionSettings)
groomData = unreal.GroomAssetImportData(options)
AssetImportTask.set_editor_property(‘options’, groomData)

I had to troll through the C/C++ headers to figure that one out… yikes.

Thank you for responding! I really appreciate you digging through the C/C++ to find an answer.

Unfortunately I was not able to get this to work. I know that I had some false positives previously (before I posted my original question) where it looked like it was working. Apparently if you manually import via Unreal dialogs, when you specify the conversion settings, those values stick for future imports.

Can you confirm your editor’s conversion settings are at their defaults before importing a groom via python?
image

I tried using the GroomAssetImportData as you suggested. Here’s what I tried.

groom_settings = unreal.GroomConversionSettings(rotation=[-90, 0.0, 0.0],
                                                scale=[1.0, 1.0, -1.0])

groom_options = unreal.GroomImportOptions()
groom_options.set_editor_property('conversion_settings', groom_settings)

groom_import_data = unreal.GroomAssetImportData(groom_options)

name = 'hair_001'
path = "/path/to/hair.abc"
dest = "/Game/Hair"

task = unreal.AssetImportTask()
properties = {'destination_name': name,
              'destination_path': dest,
              'filename': path,
              'replace_existing': True,
              'replace_existing_settings': True,
              'save': False,
              'options': groom_import_data,
              'automated': True}

task.set_editor_properties(properties)

unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])

The conversion settings do not get applied in both UE4_26 and UE5.
Do you see anything wrong with this code?

Hiya @anonymous_user_87e5ba83

My GUI import settings are:

while my code conversion settings are:

conversionSettings = unreal.GroomConversionSettings(rotation=[90.0, 0.0, 0.0],
scale=[1.0, -1.0, 1.0])

And the grooms do come in correctly. So yes, the code is being applied correctly.

The only difference that I see with your code versus mine is that I call

AssetImportTask.set_editor_property(‘options’, groomData)

after the AssetImportTask object is created. Logically, that shouldn’t make any difference but who knows what voodoo is going on under the hood in the C/C++ layer.

Here is my full import function, if you want to copy and paste that to try:

def doImport(sourcePath, destPath, replaceExisting) :
AssetTools = unreal.AssetToolsHelpers.get_asset_tools()
import_tasks = []

for fname in os.listdir(sourcePath):
    if fname.endswith('.abc'):
        AssetImportTask = unreal.AssetImportTask()
        AssetImportTask.set_editor_property('automated', True)
        AssetImportTask.set_editor_property('filename', sourcePath + '/' + fname)
        AssetImportTask.set_editor_property('destination_path', destPath)
        AssetImportTask.set_editor_property('replace_existing', replaceExisting)
        AssetImportTask.set_editor_property('save', True)


        conversionSettings = unreal.GroomConversionSettings(rotation=[90.0, 0.0, 0.0],
                                                            scale=[1.0, -1.0, 1.0])  # maya to unreal diffs
        options = unreal.GroomImportOptions()
        options.set_editor_property('conversion_settings', conversionSettings)

        groomData = unreal.GroomAssetImportData(options)  # this part is critical, you have to pass in the options into the GroomAssetImportData object

        AssetImportTask.set_editor_property('options', groomData)

        import_tasks.append(AssetImportTask)

AssetTools.import_asset_tasks(import_tasks)

I have a loop in there because our characters have multiple grooms.

Cheers!

Hey, thanks again for keeping the conversation alive :slight_smile:

Thanks for posting your code. I tried it, but did not have any success. It imported all of the grooms in the directory, but did not apply any conversion settings.

Thanks for posting your GUI import conversion settings, but I’m pretty sure those ones only apply to FBX import. I believe the only way to see the Groom Import Options dialog is by manually importing a groom. Would you be able to test that for me? Could you try to manually import a groom In order to see the Groom Import Options dialog? I need to know that your options for conversion settings are not affecting anything you are doing through code. If you find values, set them back to defaults (rotation 0, 0, 0; scale 1, 1, 1) and try your code again and let me know what you find.

I really appreciate your time and patience on this. This is driving me crazy.

Hiya @anonymous_user_87e5ba83,

Unfortunately, you are right. The options are sticky from the last GUI import. Arrrggghhh…

Hey,

Thanks for confirming my suspicions.

It’s as though we have all of the right ingredients, but lack the correct way to cook up this recipe. We need some Unreal Dev chefs.

Thanks again for your effort to help. Much appreciated.

Hello !

I found how to correctly apply the groom options on import, you were so close !
Thanks for your scripts, was helpful :slight_smile:
Here is the solution:
the ‘options’ of the AssetImportTask should be the GroomImportOptions and not a GroomAssetImportData. We don’t need an GroomAssetImportData.

Edit:
I’m realizing that it is what you did initially, but it didn’t work for you.
I’m on 5.1.1 though, maybe it was silently fixed.