Announcement

Collapse
No announcement yet.

Batch translate Solidworks Headlessly via Python

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    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

    #2
    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.

    Comment


      #3
      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/...r-using-Python

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

      Code:
       
       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/...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?

      Comment


        #4
        Robb Surridge - 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

        Comment


          #5
          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.


          Code:
          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))

          Comment


            #6
            Originally posted by Ro-Su- View Post
            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.


            Code:
            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!

            Comment


              #7
              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)

              Code:
              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))

              Comment


                #8
                Originally posted by Ro-Su- View Post
                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.


                Code:
                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))
                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....

                Comment


                  #9
                  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.



                  Code:
                  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

                  Code:
                  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)
                  Last edited by UE_FlavienP; 08-23-2019, 09:18 AM.

                  Comment


                    #10
                    Originally posted by UE_FlavienP View Post
                    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.



                    Code:
                    import unreal
                    
                    .
                    .
                    .
                    .
                    .
                    .
                    .
                    ######################### 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

                    Code:
                    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 ...

                    Comment


                      #11
                      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
                      Code:
                      ...
                      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()
                      ...

                      Comment


                        #12
                        thanks UE_FlavienP! it works now!
                        I am a
                        Rookie of UE4, learning UE4 from last week´╝îThank you for your patience

                        Comment


                          #13
                          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

                          Comment


                            #14
                            Code:
                            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.

                            Comment


                              #15
                              Hello!
                              Can someone help me.
                              i look here - https://docs.unrealengine.com/en-US/...nImportOptions but it don`t give me the answer
                              I want to import FBX with deltagen, but i cannot find option for not merge actors into single one in code

                              Comment

                              Working...
                              X