Download

Batch translate Solidworks Headlessly via Python

Does Unreal Studio / Datasmith have the ability to be driven headlessly (pushing commands in Windows to a .EXE via command-line to initiate a Python process)? If so would anyone have any introductory pointers on workflow (like what .EXE I call upon and how I’d initialize a Python process)?

I’m a green horn when it comes to the real-time scene and UE4. That being said I’m going to start diving in to learn as much about the UE4 kit as possible for real-time product viz purposes. I’m very much in the market for a CAD translator tool to integrate into our Python based Maya pipeline. Automate this process all within a Maya session-

In Maya-

Point to Solidworks assembly → push command to UE4/Datasmith EXE with arguments including solidworks assembly —> Translate CAD and export a .FBX —> Bring FBX back into Maya

Is this possible to do headlessly via pushing commands for python script execution to the appropriate .EXE in windows via CMD? If so, then diving into UE4 Studio training has become much more urgent for me as I’d like to consolidate as much of the pipeline toolset as possible.

I know the above might sound sort of weird as the FBX is being fed back into Maya, but we need a translation tool for offline rendering just as we need one to translate for real-time work.

Thank you,
Clayton

Just to add to this question as to make it a bit clearer.

In Maya I can initialize a command line, GUI-less session of Maya. I have full feature access by command line only, no GUI. In essence, I’m asking if this is possible with UE4 Editor.

Initialize a fresh scene, tell it to fire a .PY with some arguments, close the scene. All through command line and no GUI.

Yes, it is possible to run the Unreal Editor and ask it to execute a Python script. The docs for Python in Unreal give a couple of ways to do it:

https://docs.unrealengine.com/en-us/Editor/Scripting-and-Automating-the-Editor/Scripting-the-Editor-using-Python

You’ll want to use the -ExecutePythonScript command line parameter. For example:


 
 UE4Editor-Cmd.exe "D:\Unreal Projects\my_studio_project.uproject" -ExecutePythonScript="c:\my_script.py" 

Note that you’ll need to pass on the command line the name of an existing Unreal Studio project in which you’ve already enabled the Python plugin and the EditorScriptingUtilities plugin.

This doesn’t run headless in that the UI is still displayed, but it shuts down immediately after the Python script is done, so it’s still good for scripting and unattended usage. (There is a way to run a Python script without any UI using the commandlet option, but unfortunately you can’t do a Datasmith import using that commandlet mode.)

You should be able to start a Datasmith import from Solidworks in your Python script using the script examples you’ll find in the docs here:

https://docs.unrealengine.com/en-us/Studio/Unreal-Datasmith/Customizing-the-Datasmith-Import-Process

Getting the results back out in FBX might be tricky though. Datasmith creates many Static Mesh assets in the Unreal project, and it also assembles instances of these assets in the Level at specific translations and rotations so that what you see in the level matches what you had in your initial scene. Now, you could iterate through all the Static Mesh assets and export each one individually as an FBX, but then you’d have a zillion tiny FBX files, and you’d lose the placement of those assets in the scene. If you want to export the WHOLE datasmith scene as a single FBX, I think you’d have to get all the Static Mesh Actors in the level and merge them all together into a new single Static Mesh (which could end up being super enormous and the merging might take a lot of time). You’d use the unreal.EditorStaticMeshLibrary.merge_static_mesh_actors() function to do the merge. Then, once you had the merge done, you could export the new merged Static Mesh Asset as FBX.

That’s an off-the-top-of-my-head answer without trying out the export part. Does that make sense to you?

[USER=“1823580”]Robb Surridge[/USER] - Thank you for taking the time to write your reply. Yes, what you are saying is making sense to me. I appreciate your providing me with a solid starting point for all of this. I’ll post up here if I have any further questions about the process.

Best,
Clayton

Hey,
I just worked up a proof of concept. When I run this with the -ExecutePythonScript flag, I get a single FBX exported for my assembly.

Hopefully this looks pretty straightforward. Let me know if you have questions.


import unreal

file_to_import = "D:\\assets\\CAD\\Mage-Bike\\Clutch assembly.SLDASM"
final_fbx_file = "D:\\my_filename.fbx"

# clear anything existing in the level.
all_actors = unreal.EditorLevelLibrary.get_all_level_actors()

for a in all_actors:
    unreal.EditorLevelLibrary.destroy_actor(a)

# Construct the Datasmith Scene from a file on disk.
ds_scene_in_memory = unreal.DatasmithCADSceneElement.construct_datasmith_scene_from_file(file_to_import, "/Game/MyCADScene")

print("constructed the scene")

if ds_scene_in_memory is None:
    print "Scene loading failed."
    quit()

# Set import options.
import_options = ds_scene_in_memory.get_import_options()
tessellation_options = import_options.tessellation_options
tessellation_options.chord_tolerance = 15
tessellation_options.max_edge_length = 40
tessellation_options.normal_tolerance = 45
base_options = import_options.base_options
base_options.scene_handling = unreal.DatasmithImportScene.CURRENT_LEVEL

# Finalize the process by creating assets and actors.
ds_scene_in_memory.import_scene()

# Clean up the Datasmith Scene.
ds_scene_in_memory.destroy_scene()

print("Import complete!")

# merge the actors into one object
all_actors = unreal.EditorLevelLibrary.get_all_level_actors()
merge_options = unreal.EditorScriptingMergeStaticMeshActorsOptions()
# look for the unreal.MeshMergingSettings class to see what options you can set in here
merge_options.base_package_name = "/Game/NEW_MESH"
new_mesh_actor = unreal.EditorLevelLibrary.merge_static_mesh_actors(all_actors, merge_options)

# load the merged asset
loaded_asset = unreal.EditorAssetLibrary.load_asset("/Game/NEW_MESH")

# set up the FBX export options
task = unreal.AssetExportTask()
task.object = loaded_asset      # the asset to export
task.filename = final_fbx_file        # the filename to export as
task.automated = True           # don't display the export options dialog
task.replace_identical = True   # always overwrite the output
task.options = unreal.FbxExportOption()

# export!
result = unreal.Exporter.run_asset_export_task(task)

print("Export complete!")
for error_msg in task.errors:
    unreal.log_error("{}".format(error_msg))


I got the script to successfully run. Is exporting to other file formats such as OBJ and STL possible? If so, what modifications would I need to make to your originally provided script? I tried searching the API documentation for other “ExportOption()” calls but was unable to find any besides FBX.

Thank you!

Hey, @BeardMagician UE4 does support exporting assets to OBJ. I think all you need to do is set the task.exporter property to a new instance of unreal.StaticMeshExporterOBJ. I just ran this version and got it to spit out a .obj file (though I haven’t tested that everything is there)


import unreal

file_to_import = "D:\\assets\\CAD\\Mage-Bike\\Clutch assembly.SLDASM"
final_obj_file = "D:\\my_filename.obj"

# clear anything existing in the level.
all_actors = unreal.EditorLevelLibrary.get_all_level_actors()

for a in all_actors:
    unreal.EditorLevelLibrary.destroy_actor(a)

# Construct the Datasmith Scene from a file on disk.
ds_scene_in_memory = unreal.DatasmithCADSceneElement.construct_datasmith_scene_from_file(file_to_import, "/Game/MyCADScene")

print("constructed the scene")

if ds_scene_in_memory is None:
    print "Scene loading failed."
    quit()

# Set import options.
import_options = ds_scene_in_memory.get_import_options()
tessellation_options = import_options.tessellation_options
tessellation_options.chord_tolerance = 15
tessellation_options.max_edge_length = 40
tessellation_options.normal_tolerance = 45
base_options = import_options.base_options
base_options.scene_handling = unreal.DatasmithImportScene.CURRENT_LEVEL

# Finalize the process by creating assets and actors.
ds_scene_in_memory.import_scene()

# Clean up the Datasmith Scene.
ds_scene_in_memory.destroy_scene()

print("Import complete!")

# merge the actors into one object
all_actors = unreal.EditorLevelLibrary.get_all_level_actors()
merge_options = unreal.EditorScriptingMergeStaticMeshActorsOptions()
# look for the unreal.MeshMergingSettings class to see what options you can set in here
merge_options.base_package_name = "/Game/NEW_MESH"
new_mesh_actor = unreal.EditorLevelLibrary.merge_static_mesh_actors(all_actors, merge_options)

# load the merged asset
loaded_asset = unreal.EditorAssetLibrary.load_asset("/Game/NEW_MESH")

# set up the OBJ export options
task = unreal.AssetExportTask()
task.object = loaded_asset      # the asset to export
task.filename = final_obj_file        # the filename to export as
task.automated = True           # don't display the export options dialog
task.replace_identical = True   # always overwrite the output
task.exporter = unreal.StaticMeshExporterOBJ()

# export!
result = unreal.Exporter.run_asset_export_task(task)

print("Export complete!")
for error_msg in task.errors:
    unreal.log_error("{}".format(error_msg))

Hi Ro-Su-, thanks you share the python code,
short question, if I would not merger all actors, just export not mergered meshes to fbx, how to change your code?
I tried comment the merger actor part of your code , and loaded_asset = unreal.EditorAssetLibrary.load_asset("
/Game/MyCADScene**")
but it does not work…**

Hello,
please find attach two bits of code,

  1. will export every single static mesh used by **selected **static mesh actors of the scene as a separate fbx.
  2. will export the selected actors of the scene in a single fbx. The actors are not merged.


import unreal


######################### Below to export each static mesh as a separate fbx ######################################

def get_static_mesh(actor):
    smc = actor.static_mesh_component
    if smc == None:
        return None
    return smc.static_mesh

output_path = 'D:/projects/FBX/'
actors = unreal.EditorLevelLibrary.get_selected_level_actors()
static_mesh_actors = unreal.EditorFilterLibrary.by_class(actors,unreal.StaticMeshActor)
for a in static_mesh_actors:
    task = unreal.AssetExportTask()
    sm = get_static_mesh(a)
    if sm == None:
        break
    task.object = sm
    task.filename = output_path+a.get_name()+'.fbx'
    task.replace_identical = False
    task.prompt = False
    task.automated = True
    task.options = unreal.FbxExportOption()
    task.options.vertex_color = False
    task.options.collision = False
    task.options.level_of_detail = False
    unreal.Exporter.run_asset_export_task(task)

######################### Below to export all selected actors in the scene in a single fbx file ######################################

output_path = 'D:/projects/FBX/'
actors = unreal.EditorLevelLibrary.get_selected_level_actors()
static_mesh_actors = unreal.EditorFilterLibrary.by_class(actors,unreal.StaticMeshActor)

task = unreal.AssetExportTask()
#export full scene
task.object = a.get_world()
#export selected only
task.selected = True
#export one object
task.filename = output_path+'MySene.fbx'
task.replace_identical = False
task.prompt = False
task.automated = True
task.options = unreal.FbxExportOption()
task.options.vertex_color = False
task.options.collision = False
task.options.level_of_detail = False
unreal.Exporter.run_asset_export_task(task)


To select all static mesh actors of the scene you can do something like



actors = unreal.EditorLevelLibrary.get_selected_level_actors()
static_mesh_actors = unreal.EditorFilterLibrary.by_class(actors,unreal.StaticMeshActor)
unreal.EditorLevelLibrary.set_selected_level_actors(static_mesh_actors)


thanks UE_FlavienP!
I tried run your code, but it stoped in “task.object = a.get_world()”
what is “a”? tried “task.object = actors.get_world()” and “task.object = static_mesh_actors.get_world()”
both would not work …

Sorry,
you need to give a valid actor to call the get_world().
So what you should do is make sure you actually have actors in your array and then use the first one to get the world



...
static_mesh_actors = unreal.EditorFilterLibrary.by_class(actors,unreal.StaticMeshActor)
if(len(static_mesh_actors) == 0):
    exit
task = unreal.AssetExportTask()
#export full scene
task.object = static_mesh_actors[0].get_world()
...


thanks **UE_FlavienP! it works now!
I am a **Rookie of UE4, **learning UE4 from last week,**Thank you for your patience

Hi guys,
I would like to use part of Your code, but unreal 4.23 does not have DatasmithCADSceneElement component anymore.
Do You know how to implement this functionality in python 4.23?

best regards



ds_scene_in_memory = unreal.DatasmithSceneElement.construct_datasmith_scene_from_file(ds_file_on_disk)

if ds_scene_in_memory is None:
    print "Scene loading failed."
    quit()

# Main import options:
import_options = ds_scene_in_memory.get_options(unreal.DatasmithImportOptions)
import_options.base_options.scene_handling = unreal.DatasmithImportScene.CURRENT_LEVEL

# CAD-only surface tessellation options:
tessellation_options = ds_scene_in_memory.get_options(unreal.DatasmithCommonTessellationOptions)
tessellation_options.options.chord_tolerance = 0.01
tessellation_options.options.max_edge_length = 0
tessellation_options.options.normal_tolerance = 20
tessellation_options.options.stitching_technique = unreal.DatasmithCADStitchingTechnique.STITCHING_SEW

# Additional CAD-only options:
cad_import_options = ds_scene_in_memory.get_options(unreal.DatasmithCADImportOptions)
cad_import_options.uv_generation = unreal.CADUVGeneration.KEEP
cad_import_options.num_threads = 8

# Finalize the process by creating assets and actors.
ds_scene_in_memory.import_scene("/Game/MyCADScene2")

please note:

  • *DatasmithSceneElement *replaces DatasmithCADSceneElement
  • *construct_datasmith_scene_from_file *only takes the input file as a parameter
  • *import_scene *is now holding the content folder path of the imported asset.

However there is some issues on getting the tessellation parameters to be used through python in 4.23 and 4.24. This has been logged in our ticketing system.

Hello! :slight_smile:
Can someone help me.
i look here - unreal.DatasmithDeltaGenImportOptions — Unreal Python 4.26 (Experimental) documentation but it don`t give me the answer :frowning:
I want to import FBX with deltagen, but i cannot find option for not merge actors into single one in code :frowning:

Hi UE_FlavienP,
How would the code change when instead of bringing in a single file (the top product), you bring in a group of files. For instance STEP files.
Thanks