├── .gitignore
├── .gitmodules
├── Integration
├── PrismInit.py
└── UE4PrismPlugins
│ ├── .gitignore
│ ├── Config
│ └── UserEngine.ini
│ ├── Content
│ └── Python
│ │ ├── BlueprintLibrary
│ │ ├── SampleActorAction.py
│ │ ├── SampleAssetAction.py
│ │ ├── SampleBlueprintFunction.py
│ │ └── __init__.py
│ │ ├── UserInterfaces
│ │ ├── SampleEditorExtension.py
│ │ └── __init__.py
│ │ ├── init_unreal.py
│ │ ├── unreal_global.py
│ │ ├── unreal_icon.py
│ │ ├── unreal_startup.py
│ │ ├── unreal_uiutils.py
│ │ └── unreal_utils.py
│ ├── LICENSE
│ ├── PythonScripting.uplugin
│ ├── README.md
│ └── Resources
│ └── Icon128.png
├── Scripts
├── Prism_UnrealEngine_Functions.py
├── Prism_UnrealEngine_Integration.py
├── Prism_UnrealEngine_Variables.py
├── Prism_UnrealEngine_externalAccess_Functions.py
├── Prism_UnrealEngine_init.py
└── Prism_UnrealEngine_init_unloaded.py
└── external_modules
├── remote_execution.py
└── ue4.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | __pycache__
3 | .venv
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "external_modules/UnrealAPI"]
2 | path = external_modules/UnrealAPI
3 | url = https://github.com/josephkirk/UnrealApi
4 | branch = py2
5 |
--------------------------------------------------------------------------------
/Integration/PrismInit.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 |
5 | def prismInit():
6 | prismRoot = os.getenv("PRISM_ROOT")
7 | if not prismRoot:
8 | prismRoot = PRISMROOT
9 |
10 | scriptDir = os.path.join(prismRoot, "Scripts")
11 |
12 | if scriptDir not in sys.path:
13 | sys.path.append(scriptDir)
14 |
15 | import PrismCore
16 |
17 | pcore = PrismCore.PrismCore(app="UE4")
18 | return pcore
19 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | __pycache__
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Config/UserEngine.ini:
--------------------------------------------------------------------------------
1 | [/Script/PythonScriptPlugin.PythonScriptPluginSettings]
2 | +AdditionalPaths=(Path="D:\unreal\PythonProject\Python")
3 | bDeveloperMode=True
4 | bRemoteExecution=True
5 | RemoteExecutionMulticastGroupEndpoint=239.0.0.1:6766
6 | RemoteExecutionMulticastBindAddress=0.0.0.0
7 | RemoteExecutionSendBufferSizeBytes=2097152
8 | RemoteExecutionReceiveBufferSizeBytes=2097152
9 | RemoteExecutionMulticastTtl=0
10 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/BlueprintLibrary/SampleActorAction.py:
--------------------------------------------------------------------------------
1 | from unreal_global import *
2 | import unreal_utils
3 | import unreal
4 |
5 |
6 | @unreal.uclass()
7 | class SampleActorAction(unreal.ActorActionUtility):
8 | # @unreal.ufunction(override=True)
9 | # def get_supported_class(self):
10 | # return unreal.Actor
11 |
12 | @unreal.ufunction(
13 | static=True,
14 | meta=dict(CallInEditor=True, Category="Sample Actor Action Library"),
15 | )
16 | def python_test_actor_action():
17 | unreal.log("Execute Sample Actor Action")
18 |
19 | @unreal.ufunction(
20 | params=[str],
21 | static=True,
22 | meta=dict(CallInEditor=True, Category="Sample Actor Action Library"),
23 | )
24 | def python_test_actor_action_with_parameters(input_string):
25 | unreal.log("Execute Sample Actor Action with {}".format(input_string))
26 |
27 |
28 | # Unreal need a actual blueprint asset to be created and inherit from the new class for it to work
29 | # unreal_utils.register_editor_utility_blueprint("SampleActorUtility", SampleActorAction)
30 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/BlueprintLibrary/SampleAssetAction.py:
--------------------------------------------------------------------------------
1 | from unreal_global import *
2 | import unreal_utils
3 | import unreal
4 |
5 |
6 | @unreal.uclass()
7 | class SamplePythonAssetAction(unreal.AssetActionUtility):
8 | # @unreal.ufunction(override=True)
9 | # def get_supported_class(self):
10 | # return unreal.StaticMesh
11 |
12 | @unreal.ufunction(
13 | static=True,
14 | meta=dict(CallInEditor=True, Category="Sample Asset Action Library"),
15 | )
16 | def python_dump_asset_info():
17 | selected_assets = UtilLibrary.get_selected_assets()
18 | for asset in selected_assets:
19 | unreal.log(
20 | "{} {} {}".format(
21 | asset.get_name(), asset.get_outermost(), asset.get_path_name()
22 | )
23 | )
24 |
25 | # @unreal.ufunction(params=[int], static=True, meta=dict(CallInEditor=True, Category="Sample Asset Action Library"))
26 | # def test_asset_action(input_num):
27 | # unreal.log("Execute Sample Asset Action with {}".format(input_num))
28 |
29 |
30 | # Unreal need a actual blueprint asset to be created and inherit from the new class for it to work
31 | # unreal_utils.register_editor_utility_blueprint(
32 | # "SampleAssetUtility", SamplePythonAssetAction
33 | # )
34 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/BlueprintLibrary/SampleBlueprintFunction.py:
--------------------------------------------------------------------------------
1 | from unreal_global import *
2 |
3 |
4 | @unreal.uclass()
5 | class SamplePythonBlueprintLibrary(unreal.BlueprintFunctionLibrary):
6 | @unreal.ufunction(
7 | static=True, meta=dict(Category="Sample Blueprint Function Library")
8 | )
9 | def python_test_bp_action_noinput():
10 | unreal.log("Execute Bluerprint Action No Input")
11 |
12 | @unreal.ufunction(
13 | params=[int, str],
14 | static=True,
15 | meta=dict(Category="Sample Blueprint Function Library"),
16 | )
17 | def python_test_bp_action_input(input_num, input_str):
18 | unreal.log(
19 | "Execute Bluerprint Action With Inputs {} {}".format(
20 | input_num, input_string
21 | )
22 | )
23 |
24 | @unreal.ufunction(
25 | ret=str, static=True, meta=dict(Category="Sample Blueprint Function Library")
26 | )
27 | def python_test_bp_action_return():
28 | result = "Success"
29 | unreal.log("Execute Bluerprint Action Return")
30 | return result
31 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/BlueprintLibrary/__init__.py:
--------------------------------------------------------------------------------
1 | import unreal
2 | from os.path import dirname, basename, isfile, join
3 | import glob
4 | import importlib
5 | import traceback
6 | import sys
7 |
8 | dir_name = dirname(__file__)
9 | dir_basename = basename(dir_name)
10 | modules = glob.glob(join(dir_name, "*.py"))
11 | __all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
12 |
13 | unreal.log("""@
14 |
15 | ####################
16 |
17 | Load Blueprint Library
18 |
19 | ####################
20 | """)
21 |
22 | for m in __all__:
23 | # __import__(m, locals(), globals())
24 | try:
25 | mod = importlib.import_module("{}.{}".format(dir_basename, m))
26 | if m in globals():
27 | unreal.log("""@
28 | ####################
29 |
30 | ReLoad Blueprint Library
31 |
32 | ####################
33 | """)
34 | try:
35 | reload(mod)
36 | except:
37 | importlib.reload(mod)
38 | # try:
39 | # unreal.reload(mod)
40 | # except:
41 | # unreal.log("{} is not Unreal Generated Class".format(m))
42 |
43 | unreal.log("Successfully import {}".format(m))
44 | except Exception as why:
45 | unreal.log_error("Fail to import {}.\n {}".format(m, why))
46 | traceback.print_exc(file=sys.stdout)
47 |
48 | unreal.log("""@
49 |
50 | ####################
51 | """)
52 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/UserInterfaces/SampleEditorExtension.py:
--------------------------------------------------------------------------------
1 | # Create Virtuos Menu
2 | # Nguyen PHi Hung
3 |
4 | import unreal_uiutils
5 | from unreal_utils import AssetRegistryPostLoad
6 | import unreal
7 |
8 |
9 | def extend_editor():
10 | # Create standard menu entry
11 | me_reloadbutton = unreal_uiutils.create_menu_button(
12 | name="ReloadBtn",
13 | label="Reload",
14 | command_string="import importlib; import unreal_startup; importlib.reload(unreal_startup); unreal_startup.reload()",
15 | )
16 | me_quitbutton = unreal_uiutils.create_menu_button(
17 | name="QuitUnrealBtn",
18 | label="Quit Unreal",
19 | command_string="SystemLib.quit_editor()",
20 | )
21 |
22 | # This create a button on toolboard
23 | tb_reloadbutton = unreal_uiutils.create_toolbar_button(
24 | name="ReloadBtn",
25 | label="PyReload",
26 | command_string="import importlib; import unreal_startup; importlib.reload(unreal_startup); unreal_startup.reload()",
27 | )
28 |
29 | # This create a drop down button on toolboard
30 | tb_combobutton = unreal_uiutils.create_toolbar_combo_button(
31 | "comboBtn", "python.tools"
32 | )
33 | #TODO: Find out where it is possible to register a new context menu for combo button
34 |
35 | # create submenu in 'File' Menu and register menu entry created above
36 | pythonsubmenu = unreal_uiutils.extend_mainmenu_item(
37 | "File", "PythonTools", "PythonTools", "Python Tools"
38 | )
39 | pythonsubmenu.add_section("python.file.menu", "Python Tools")
40 | pythonsubmenu.add_menu_entry("python.file.menu", me_reloadbutton)
41 | pythonsubmenu.add_menu_entry("python.file.menu", me_quitbutton)
42 |
43 | # Create Standalone Menu and register menu entry created above
44 | new_mainmenu = unreal_uiutils.extend_mainmenu("Python", "Python")
45 | section = new_mainmenu.add_section("python.menu", "Python Tools")
46 | new_mainmenu.add_menu_entry("python.menu", me_reloadbutton)
47 | new_mainmenu.add_menu_entry("python.menu", me_quitbutton)
48 | new_entry = unreal.ToolMenuEntryExtensions.init_menu_entry(
49 | new_mainmenu.menu_name,
50 | "submenu",
51 | "Test Sub Menu",
52 | "nothing here",
53 | unreal.ToolMenuStringCommandType.COMMAND,
54 | "command",
55 | "",
56 | )
57 | print(f"Script Object: {new_entry.script_object}")
58 |
59 | # Extend Asset Context Menu
60 | sub_asset_context_menu = unreal_uiutils.extend_toolmenu(unreal_uiutils.get_asset_context_menu(), "PythonAssetContextMenu", "Python Asset Actions")
61 | sub_asset_context_menu.add_section("python.assetmenu", "Tools")
62 | action_sampleassetprint = unreal_uiutils.create_menu_button(
63 | name="SampleAssetTool",
64 | label="Print Selected Assets",
65 | command_string='print(UtilLibrary.get_selected_assets())',
66 | )
67 | sub_asset_context_menu.add_menu_entry("python.assetmenu", action_sampleassetprint)
68 |
69 | # Extend Asset Context Menu - Only show for Level Sequence Asset
70 | sequence_contextmenu = unreal_uiutils.get_sequence_asset_context_menu()
71 | sub_sequence_context_menu = unreal_uiutils.extend_toolmenu(sequence_contextmenu, "PythonSequenceContextMenu", "Python Sequence Actions")
72 | sub_sequence_context_menu.add_section("python.sequencemenu", "Tools")
73 | action_samplesequenceprint = unreal_uiutils.create_menu_button(
74 | name="SampleSequenceTool",
75 | label="Print Selected Assets",
76 | command_string='print(UtilLibrary.get_selected_assets())',
77 | )
78 | sub_sequence_context_menu.add_menu_entry("python.sequencemenu", action_samplesequenceprint)
79 |
80 | # Extend Actor Context Menu
81 | actor_context_menu = unreal_uiutils.get_actor_context_menu()
82 | actor_context_menu.add_section("python.actoractions", "Python Tools")
83 | #!NOTE: The submenu can only be seen when right click in viewport and not the outliner
84 | sub_actor_context_menu = actor_context_menu.add_sub_menu(actor_context_menu.menu_name, "python.actoractions", "PythonActorContextMenu", "Python Actor Actions")
85 | # is_sub_actor_context_menu_registered = unreal.ToolMenus.get().is_menu_registered(sub_actor_context_menu.menu_name)
86 | # if not is_sub_actor_context_menu_registered:
87 | # unreal.ToolMenus.get().register_menu(sub_actor_context_menu.menu_name, actor_context_menu.menu_name)
88 | # print("{} - is registered {}".format(sub_actor_context_menu.menu_name, is_sub_actor_context_menu_registered))
89 | sub_actor_context_menu.add_section("python.actormenu", "Tools")
90 | action_sampleactorprint = unreal_uiutils.create_menu_button(
91 | name="SampleActorTool",
92 | label="Print Selected Actor",
93 | command_string='print(LevelLibrary.get_selected_level_actors())',
94 | )
95 | sub_actor_context_menu.add_menu_entry("python.actormenu", action_sampleactorprint)
96 | # actor_context_menu.add_menu_entry("python.actoractions", action_sampleactorprint)
97 |
98 | # AssetRegistryPostLoad.register_callback(extend_editor)
99 | extend_editor()
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/UserInterfaces/__init__.py:
--------------------------------------------------------------------------------
1 | import unreal
2 | import sys
3 | from os.path import dirname, basename, isfile, join
4 | import glob
5 | import importlib
6 | import traceback
7 |
8 | dir_name = dirname(__file__)
9 | dir_basename = basename(dir_name)
10 | modules = glob.glob(join(dir_name, "*.py"))
11 | __all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
12 |
13 | unreal.log("""@
14 |
15 | ####################
16 |
17 | Load UI Library
18 |
19 | ####################
20 | """)
21 | for m in __all__:
22 | # __import__(m, locals(), globals())
23 | try:
24 | mod = importlib.import_module("{}.{}".format(dir_basename, m))
25 | if m in globals():
26 | unreal.log("""@
27 | ####################
28 |
29 | ReLoad UI Library
30 |
31 | ####################
32 | """)
33 | try:
34 | reload(mod)
35 | except:
36 | importlib.reload(mod)
37 | unreal.log("Successfully import {}".format(m))
38 | except Exception as why:
39 | unreal.log_error("Fail to import {}.\n {}".format(m, why))
40 | traceback.print_exc(file=sys.stdout)
41 | unreal.log("""@
42 |
43 | ####################
44 | """)
45 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/init_unreal.py:
--------------------------------------------------------------------------------
1 | import unreal
2 | from unreal_global import *
3 |
4 | unreal.log("""@
5 |
6 | ####################
7 |
8 | Unreal Init Script
9 |
10 | ####################
11 |
12 | """)
13 |
14 | import unreal_startup
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/unreal_global.py:
--------------------------------------------------------------------------------
1 | import unreal
2 |
3 | AssetRegistry = unreal.AssetRegistryHelpers.get_asset_registry()
4 |
5 | UtilLibrary = unreal.EditorUtilityLibrary
6 | """
7 | rename_asset(asset, new_name) -> None
8 | get_selection_set() -> Array(Actor)
9 | get_selection_bounds() -> (origin=Vector, box_extent=Vector, sphere_radius=float)
10 | get_selected_blueprint_classes() -> Array(type(Class))
11 | get_selected_assets() -> Array(Object)
12 | get_selected_asset_data() -> Array(AssetData)
13 | get_actor_reference(path_to_actor) -> Actor
14 | """
15 |
16 | AutomationScheduler = unreal.AutomationScheduler
17 |
18 | AssetTools = unreal.AssetToolsHelpers.get_asset_tools()
19 | """
20 | rename_referencing_soft_object_paths(packages_to_check, asset_redirector_map)
21 |
22 | rename_assets_with_dialog(assets_and_names, auto_checkout=False)
23 |
24 | rename_assets(assets_and_names)
25 |
26 | open_editor_for_assets(assets)
27 |
28 | import_asset_tasks(import_tasks)
29 |
30 | import_assets_with_dialog(destination_path)
31 |
32 | import_assets_automated(import_data)
33 |
34 | find_soft_references_to_object(target_object)
35 |
36 | export_assets_with_dialog(assets_to_export, prompt_for_individual_filenames)
37 |
38 | export_assets(assets_to_export, export_path)
39 |
40 | duplicate_asset_with_dialog_and_title(asset_name, package_path, original_object, dialog_title)
41 |
42 | duplicate_asset_with_dialog(asset_name, package_path, original_object)
43 |
44 | duplicate_asset(asset_name, package_path, original_object)
45 |
46 | create_unique_asset_name(base_package_name, suffix)
47 |
48 | create_asset_with_dialog(asset_name, package_path, asset_class, factory, calling_context="None")
49 |
50 | create_asset(asset_name, package_path, asset_class, factory, calling_context="None")
51 | """
52 |
53 | AssetLibrary = unreal.EditorAssetLibrary
54 | """
55 | sync_browser_to_objects(cls, asset_paths)
56 |
57 | set_metadata_tag(cls, object, tag, value)
58 |
59 | save_loaded_assets(cls, assets_to_save, only_if_is_dirty=True)
60 |
61 | save_loaded_asset(cls, asset_to_save, only_if_is_dirty=True)
62 |
63 | save_directory(cls, directory_path, only_if_is_dirty=True, recursive=True)
64 |
65 | save_asset(cls, asset_to_save, only_if_is_dirty=True)
66 |
67 | rename_loaded_asset(cls, source_asset, destination_asset_path)
68 |
69 | rename_directory(cls, source_directory_path, destination_directory_path)
70 |
71 | rename_asset(cls, source_asset_path, destination_asset_path)
72 |
73 | remove_metadata_tag(cls, object, tag)
74 |
75 | make_directory(cls, directory_path)
76 |
77 | load_blueprint_class(cls, asset_path)
78 |
79 | load_asset(cls, asset_path)
80 |
81 | list_assets(cls, directory_path, recursive=True, include_folder=False)
82 |
83 | list_asset_by_tag_value(cls, tag_name, tag_value)
84 |
85 | get_tag_values(cls, asset_path)
86 |
87 | get_path_name_for_loaded_asset(cls, loaded_asset)
88 |
89 | get_metadata_tag_values(cls, object)
90 |
91 | get_metadata_tag(cls, object, tag)
92 |
93 | find_package_referencers_for_asset(cls, asset_path, load_assets_to_confirm=False)
94 |
95 | find_asset_data(cls, asset_path)
96 |
97 | duplicate_loaded_asset(cls, source_asset, destination_asset_path)
98 |
99 | duplicate_directory(cls, source_directory_path, destination_directory_path)
100 |
101 | duplicate_asset(cls, source_asset_path, destination_asset_path)
102 |
103 | does_directory_have_assets(cls, directory_path, recursive=True)
104 |
105 | does_directory_exist(cls, directory_path)
106 |
107 | does_asset_exist(cls, asset_path)
108 |
109 | do_assets_exist(cls, asset_paths)
110 |
111 | delete_loaded_assets(cls, assets_to_delete)
112 |
113 | delete_loaded_asset(cls, asset_to_delete)
114 |
115 | delete_directory(cls, directory_path)
116 |
117 | delete_asset(cls, asset_path_to_delete)
118 |
119 | consolidate_assets(cls, asset_to_consolidate_to, assets_to_consolidate)
120 |
121 | checkout_loaded_assets(cls, assets_to_checkout)
122 |
123 | checkout_loaded_asset(cls, asset_to_checkout)
124 |
125 | checkout_directory(cls, directory_path, recursive=True)
126 |
127 | checkout_asset(cls, asset_to_checkout)
128 | """
129 |
130 | FilterLibrary = unreal.EditorFilterLibrary
131 | """
132 | Utility class to filter a list of objects. Object should be in the World Editor.
133 |
134 | by_selection(cls, target_array, filter_type=EditorScriptingFilterType.INCLUDE)
135 |
136 | by_level_name(cls, target_array, level_name, filter_type=EditorScriptingFilterType.INCLUDE)
137 |
138 | by_layer(cls, target_array, layer_name, filter_type=EditorScriptingFilterType.INCLUDE)
139 |
140 | by_id_name(cls, target_array, name_sub_string, string_match=EditorScriptingStringMatchType.CONTAINS, filter_type=EditorScriptingFilterType.INCLUDE)
141 |
142 | by_class(cls, target_array, object_class, filter_type=EditorScriptingFilterType.INCLUDE)
143 |
144 | by_actor_tag(cls, target_array, tag, filter_type=EditorScriptingFilterType.INCLUDE)
145 |
146 | by_actor_label(cls, target_array, name_sub_string, string_match=EditorScriptingStringMatchType.CONTAINS, filter_type=EditorScriptingFilterType.INCLUDE, ignore_case=True)
147 |
148 | """
149 |
150 | AutomationLibrary = unreal.AutomationLibrary
151 | """
152 | take_high_res_screenshot(cls, res_x, res_y, filename, camera=None, mask_enabled=False, capture_hdr=False, comparison_tolerance=ComparisonTolerance.LOW, comparison_notes="")
153 |
154 | take_automation_screenshot_of_ui(cls, world_context_object, latent_info, name, options)
155 |
156 | take_automation_screenshot_at_camera(cls, world_context_object, latent_info, camera, name_override, notes, options)
157 |
158 | take_automation_screenshot(cls, world_context_object, latent_info, name, notes, options)
159 |
160 | set_scalability_quality_to_low(cls, world_context_object)
161 |
162 | set_scalability_quality_to_epic(cls, world_context_object)
163 |
164 | set_scalability_quality_level_relative_to_max(cls, world_context_object, value=1)
165 |
166 | get_stat_inc_max(cls, stat_name)
167 |
168 | get_stat_inc_average(cls, stat_name)
169 |
170 | get_stat_exc_max(cls, stat_name)
171 |
172 | get_stat_exc_average(cls, stat_name)
173 |
174 | get_stat_call_count(cls, stat_name)
175 |
176 | get_default_screenshot_options_for_rendering(cls, tolerance=ComparisonTolerance.LOW, delay=0.200000)
177 |
178 | get_default_screenshot_options_for_gameplay(cls, tolerance=ComparisonTolerance.LOW, delay=0.200000)
179 |
180 | enable_stat_group(cls, world_context_object, group_name)
181 |
182 | disable_stat_group(cls, world_context_object, group_name)
183 |
184 | automation_wait_for_loading(cls, world_context_object, latent_info)
185 |
186 | are_automated_tests_running(cls)
187 |
188 | add_expected_log_error(cls, expected_pattern_string, occurrences=1, exact_match=False)
189 | """
190 |
191 | LevelLibrary = unreal.EditorLevelLibrary
192 | """
193 | spawn_actor_from_object(object_to_use, location, rotation=[0.000000, 0.000000, 0.000000]) -> Actor
194 | spawn_actor_from_class(actor_class, location, rotation=[0.000000, 0.000000, 0.000000]) -> Actor
195 | set_selected_level_actors(actors_to_select) -> None
196 | set_level_viewport_camera_info(camera_location, camera_rotation) -> None
197 | set_current_level_by_name(level_name) -> bool
198 | set_actor_selection_state(actor, should_be_selected) -> None
199 | select_nothing() -> None
200 | save_current_level() -> bool
201 | save_all_dirty_levels() -> bool
202 | replace_mesh_components_meshes_on_actors(actors, mesh_to_be_replaced, new_mesh) -> None
203 | replace_mesh_components_meshes(mesh_components, mesh_to_be_replaced, new_mesh) -> None
204 | replace_mesh_components_materials_on_actors(actors, material_to_be_replaced, new_material) -> None
205 | replace_mesh_components_materials(mesh_components, material_to_be_replaced, new_material) -> None
206 | pilot_level_actor(actor_to_pilot) -> None
207 | new_level_from_template(asset_path, template_asset_path) -> bool
208 | new_level(asset_path) -> bool
209 | merge_static_mesh_actors(actors_to_merge, merge_options) -> StaticMeshActor or None
210 | load_level(asset_path) -> bool
211 | join_static_mesh_actors(actors_to_join, join_options) -> Actor
212 | get_selected_level_actors() -> Array(Actor)
213 | get_level_viewport_camera_info() -> (camera_location=Vector, camera_rotation=Rotator) or None
214 | get_game_world() -> World
215 | get_editor_world() -> World
216 | get_all_level_actors_components() -> Array(ActorComponent)
217 | get_all_level_actors() -> Array(Actor)
218 | get_actor_reference(path_to_actor) -> Actor
219 | eject_pilot_level_actor() -> None
220 | editor_set_game_view(game_view) -> None
221 | editor_play_simulate() -> None
222 | editor_invalidate_viewports() -> None
223 | destroy_actor(actor_to_destroy) -> bool
224 | create_proxy_mesh_actor(actors_to_merge, merge_options) -> StaticMeshActor or None
225 | convert_actors(actors, actor_class, static_mesh_package_path) -> Array(Actor)
226 | clear_actor_selection_set() -> None
227 | """
228 |
229 | SequenceBindingLibrary = unreal.MovieSceneBindingExtensions
230 | """
231 | set_parent(binding, parent_binding) -> None
232 | remove_track(binding, track_to_remove) -> None
233 | remove(binding) -> None
234 | is_valid(binding) -> bool
235 | get_tracks(binding) -> Array(MovieSceneTrack)
236 | get_possessed_object_class(binding) -> type(Class)
237 | get_parent(binding) -> SequencerBindingProxy
238 | get_object_template(binding) -> Object
239 | get_name(binding) -> str
240 | get_id(binding) -> Guid
241 | get_display_name(binding) -> Text
242 | get_child_possessables(binding) -> Array(SequencerBindingProxy)
243 | find_tracks_by_type(binding, track_type) -> Array(MovieSceneTrack)
244 | find_tracks_by_exact_type(binding, track_type) -> Array(MovieSceneTrack)
245 | add_track(binding, track_type) -> MovieSceneTrack
246 | """
247 |
248 | SequenceFolderLibrary = unreal.MovieSceneFolderExtensions
249 | """
250 | set_folder_name(folder, folder_name) -> bool
251 | set_folder_color(folder, folder_color) -> bool
252 | remove_child_object_binding(folder, object_binding) -> bool
253 | remove_child_master_track(folder, master_track) -> bool
254 | remove_child_folder(target_folder, folder_to_remove) -> bool
255 | get_folder_name(folder) -> Name
256 | get_folder_color(folder) -> Color
257 | get_child_object_bindings(folder) -> Array(SequencerBindingProxy)
258 | get_child_master_tracks(folder) -> Array(MovieSceneTrack)
259 | get_child_folders(folder) -> Array(MovieSceneFolder)
260 | add_child_object_binding(folder, object_binding) -> bool
261 | add_child_master_track(folder, master_track) -> bool
262 | add_child_folder(target_folder, folder_to_add) -> bool
263 | """
264 |
265 | SequencePropertyLibrary = unreal.MovieScenePropertyTrackExtensions
266 | """
267 | X.set_property_name_and_path(track, property_name, property_path) -> None
268 | X.set_object_property_class(track, property_class) -> None
269 | X.get_unique_track_name(track) -> Name
270 | X.get_property_path(track) -> str
271 | X.get_property_name(track) -> Name
272 | X.get_object_property_class(track) -> type(Class)
273 | """
274 |
275 | SequenceSectionLibrary = unreal.MovieSceneSectionExtensions
276 | """
277 | set_start_frame_seconds(section, start_time) -> None
278 | set_start_frame_bounded(section, is_bounded) -> None
279 | set_start_frame(section, start_frame) -> None
280 | set_range_seconds(section, start_time, end_time) -> None
281 | set_range(section, start_frame, end_frame) -> None
282 | set_end_frame_seconds(section, end_time) -> None
283 | set_end_frame_bounded(section, is_bounded) -> None
284 | set_end_frame(section, end_frame) -> None
285 | get_start_frame_seconds(section) -> float
286 | get_start_frame(section) -> int32
287 | get_parent_sequence_frame(section, frame, parent_sequence) -> int32
288 | get_end_frame_seconds(section) -> float
289 | get_end_frame(section) -> int32
290 | get_channels(section) -> Array(MovieSceneScriptingChannel)
291 | find_channels_by_type(section, channel_type) -> Array(MovieSceneScriptingChannel)
292 | """
293 | SequenceLibrary = unreal.MovieSceneSectionExtensions
294 | """
295 | set_work_range_start(sequence, start_time_in_seconds) -> None
296 | set_work_range_end(sequence, end_time_in_seconds) -> None
297 | set_view_range_start(sequence, start_time_in_seconds) -> None
298 | set_view_range_end(sequence, end_time_in_seconds) -> None
299 | set_tick_resolution(sequence, tick_resolution) -> None
300 | set_read_only(sequence, read_only) -> None
301 | set_playback_start_seconds(sequence, start_time) -> None
302 | set_playback_start(sequence, start_frame) -> None
303 | set_playback_end_seconds(sequence, end_time) -> None
304 | set_playback_end(sequence, end_frame) -> None
305 | set_display_rate(sequence, display_rate) -> None
306 | make_range_seconds(sequence, start_time, duration) -> SequencerScriptingRange
307 | make_range(sequence, start_frame, duration) -> SequencerScriptingRange
308 | make_binding_id(master_sequence, binding, space=MovieSceneObjectBindingSpace.ROOT) -> MovieSceneObjectBindingID
309 | locate_bound_objects(sequence, binding, context) -> Array(Object)
310 | is_read_only(sequence) -> bool
311 | get_work_range_start(sequence) -> float
312 | get_work_range_end(sequence) -> float
313 | get_view_range_start(sequence) -> float
314 | get_view_range_end(sequence) -> float
315 | get_timecode_source(sequence) -> Timecode
316 | get_tick_resolution(sequence) -> FrameRate
317 | get_spawnables(sequence) -> Array(SequencerBindingProxy)
318 | get_root_folders_in_sequence(sequence) -> Array(MovieSceneFolder)
319 | get_possessables(sequence) -> Array(SequencerBindingProxy)
320 | get_playback_start_seconds(sequence) -> float
321 | get_playback_start(sequence) -> int32
322 | get_playback_range(sequence) -> SequencerScriptingRange
323 | get_playback_end_seconds(sequence) -> float
324 | get_playback_end(sequence) -> int32
325 | get_movie_scene(sequence) -> MovieScene
326 | get_master_tracks(sequence) -> Array(MovieSceneTrack)
327 | get_marked_frames(sequence) -> Array(MovieSceneMarkedFrame)
328 | get_display_rate(sequence) -> FrameRate
329 | get_bindings(sequence) -> Array(SequencerBindingProxy)
330 | find_next_marked_frame(sequence, frame_number, forward) -> int32
331 | find_master_tracks_by_type(sequence, track_type) -> Array(MovieSceneTrack)
332 | find_master_tracks_by_exact_type(sequence, track_type) -> Array(MovieSceneTrack)
333 | find_marked_frame_by_label(sequence, label) -> int32
334 | find_marked_frame_by_frame_number(sequence, frame_number) -> int32
335 | find_binding_by_name(sequence, name) -> SequencerBindingProxy
336 | delete_marked_frames(sequence) -> None
337 | delete_marked_frame(sequence, delete_index) -> None
338 | add_spawnable_from_instance(sequence, object_to_spawn) -> SequencerBindingProxy
339 | add_spawnable_from_class(sequence, class_to_spawn) -> SequencerBindingProxy
340 | add_root_folder_to_sequence(sequence, new_folder_name) -> MovieSceneFolder
341 | add_possessable(sequence, object_to_possess) -> SequencerBindingProxy
342 | add_master_track(sequence, track_type) -> MovieSceneTrack
343 | add_marked_frame(sequence, marked_frame) -> int32
344 | """
345 |
346 | SequenceTrackLibrary = unreal.MovieSceneTrackExtensions
347 | """
348 | remove_section(track, section) -> None
349 | get_sections(track) -> Array(MovieSceneSection)
350 | get_display_name(track) -> Text
351 | add_section(track) -> MovieSceneSection
352 | """
353 |
354 | SequenceTools = unreal.SequencerTools
355 | """
356 | render_movie(capture_settings, on_finished_callback) -> bool
357 | is_rendering_movie() -> bool
358 | import_fbx(world, sequence, bindings, import_fbx_settings, import_filename) -> bool
359 | get_object_bindings(world, sequence, object, range) -> Array(SequencerBoundObjects)
360 | get_bound_objects(world, sequence, bindings, range) -> Array(SequencerBoundObjects)
361 | export_fbx(world, sequence, bindings, override_options, fbx_file_name) -> bool
362 | export_anim_sequence(world, sequence, anim_sequence, binding) -> bool
363 | cancel_movie_render() -> None
364 | """
365 |
366 | LevelSequenceLibrary = unreal.LevelSequenceEditorBlueprintLibrary
367 | """
368 | set_lock_level_sequence(lock) -> None
369 | set_current_time(new_frame) -> None
370 | play() -> None
371 | pause() -> None
372 | open_level_sequence(level_sequence) -> bool
373 | is_playing() -> bool
374 | is_level_sequence_locked() -> bool
375 | get_current_time() -> int32
376 | get_current_level_sequence() -> LevelSequence
377 | close_level_sequence() -> None
378 | """
379 |
380 | PathLib = unreal.Paths
381 | """
382 | video_capture_dir() -> str
383 | validate_path(path) -> (did_succeed=bool, out_reason=Text)
384 | split(path) -> (path_part=str, filename_part=str, extension_part=str)
385 | source_config_dir() -> str
386 | should_save_to_user_dir() -> bool
387 | shader_working_dir() -> str
388 | set_project_file_path(new_game_project_file_path) -> None
389 | set_extension(path, new_extension) -> str
390 | screen_shot_dir() -> str
391 | sandboxes_dir() -> str
392 | root_dir() -> str
393 | remove_duplicate_slashes(path) -> str
394 | project_user_dir() -> str
395 | project_saved_dir() -> str
396 | project_plugins_dir() -> str
397 | project_persistent_download_dir() -> str
398 | project_mods_dir() -> str
399 | project_log_dir() -> str
400 | project_intermediate_dir() -> str
401 | project_dir() -> str
402 | project_content_dir() -> str
403 | project_config_dir() -> str
404 | profiling_dir() -> str
405 | normalize_filename(path) -> str
406 | normalize_directory_name(path) -> str
407 | make_valid_file_name(string, replacement_char="") -> str
408 | make_standard_filename(path) -> str
409 | make_platform_filename(path) -> str
410 | make_path_relative_to(path, relative_to) -> str or None
411 | launch_dir() -> str
412 | is_same_path(path_a, path_b) -> bool
413 | is_restricted_path(path) -> bool
414 | is_relative(path) -> bool
415 | is_project_file_path_set() -> bool
416 | is_drive(path) -> bool
417 | has_project_persistent_download_dir() -> bool
418 | get_tool_tip_localization_paths() -> Array(str)
419 | get_restricted_folder_names() -> Array(str)
420 | get_relative_path_to_root() -> str
421 | get_property_name_localization_paths() -> Array(str)
422 | get_project_file_path() -> str
423 | get_path(path) -> str
424 | get_invalid_file_system_chars() -> str
425 | get_game_localization_paths() -> Array(str)
426 | get_extension(path, include_dot=False) -> str
427 | get_engine_localization_paths() -> Array(str)
428 | get_editor_localization_paths() -> Array(str)
429 | get_clean_filename(path) -> str
430 | get_base_filename(path, remove_path=True) -> str
431 | generated_config_dir() -> str
432 | game_user_developer_dir() -> str
433 | game_source_dir() -> str
434 | game_developers_dir() -> str
435 | game_agnostic_saved_dir() -> str
436 | file_exists(path) -> bool
437 | feature_pack_dir() -> str
438 | enterprise_plugins_dir() -> str
439 | enterprise_feature_pack_dir() -> str
440 | enterprise_dir() -> str
441 | engine_version_agnostic_user_dir() -> str
442 | engine_user_dir() -> str
443 | engine_source_dir() -> str
444 | engine_saved_dir() -> str
445 | engine_plugins_dir() -> str
446 | engine_intermediate_dir() -> str
447 | engine_dir() -> str
448 | engine_content_dir() -> str
449 | engine_config_dir() -> str
450 | directory_exists(path) -> bool
451 | diff_dir() -> str
452 | create_temp_filename(path, prefix="", extension=".tmp") -> str
453 | convert_to_sandbox_path(path, sandbox_name) -> str
454 | convert_relative_path_to_full(path, base_path="") -> str
455 | convert_from_sandbox_path(path, sandbox_name) -> str
456 | combine(paths) -> str
457 | collapse_relative_directories(path) -> str or None
458 | cloud_dir() -> str
459 | change_extension(path, new_extension) -> str
460 | bug_it_dir() -> str
461 | automation_transient_dir() -> str
462 | automation_log_dir() -> str
463 | automation_dir() -> str
464 | """
465 |
466 | SystemLib = unreal.SystemLibrary
467 | """
468 | unregister_for_remote_notifications() -> None
469 | unload_primary_asset_list(primary_asset_id_list) -> None
470 | unload_primary_asset(primary_asset_id) -> None
471 | transact_object(object) -> None
472 | sphere_trace_single_for_objects(world_context_object, start, end, radius, object_types, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
473 | sphere_trace_single_by_profile(world_context_object, start, end, radius, profile_name, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
474 | sphere_trace_single(world_context_object, start, end, radius, trace_channel, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
475 | sphere_trace_multi_for_objects(world_context_object, start, end, radius, object_types, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
476 | sphere_trace_multi_by_profile(world_context_object, start, end, radius, profile_name, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
477 | sphere_trace_multi(world_context_object, start, end, radius, trace_channel, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
478 | sphere_overlap_components(world_context_object, sphere_pos, sphere_radius, object_types, component_class_filter, actors_to_ignore) -> Array(PrimitiveComponent) or None
479 | sphere_overlap_actors(world_context_object, sphere_pos, sphere_radius, object_types, actor_class_filter, actors_to_ignore) -> Array(Actor) or None
480 | snapshot_object(object) -> None
481 | show_platform_specific_leaderboard_screen(category_name) -> None
482 | show_platform_specific_achievements_screen(specific_player) -> None
483 | show_interstitial_ad() -> None
484 | show_ad_banner(ad_id_index, show_on_bottom_of_screen) -> None
485 | set_window_title(title) -> None
486 | set_volume_buttons_handled_by_system(enabled) -> None
487 | set_user_activity(user_activity) -> None
488 | set_suppress_viewport_transition_message(world_context_object, state) -> None
489 | set_gamepads_block_device_feedback(block) -> None
490 | retriggerable_delay(world_context_object, duration, latent_info) -> None
491 | reset_gamepad_assignment_to_controller(controller_id) -> None
492 | reset_gamepad_assignments() -> None
493 | register_for_remote_notifications() -> None
494 | quit_game(world_context_object, specific_player, quit_preference, ignore_platform_restrictions) -> None
495 | quit_editor() -> None
496 | print_text(world_context_object, text="Hello", print_to_screen=True, print_to_log=True, text_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=2.000000) -> None
497 | print_string(world_context_object, string="Hello", print_to_screen=True, print_to_log=True, text_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=2.000000) -> None
498 | not_equal_soft_object_reference(a, b) -> bool
499 | not_equal_soft_class_reference(a, b) -> bool
500 | not_equal_primary_asset_type(a, b) -> bool
501 | not_equal_primary_asset_id(a, b) -> bool
502 | normalize_filename(filename) -> str
503 | move_component_to(component, target_relative_location, target_relative_rotation, ease_out, ease_in, over_time, force_shortest_rotation_path, move_action, latent_info) -> None
504 | make_literal_text(value) -> Text
505 | make_literal_string(value) -> str
506 | make_literal_name(value) -> Name
507 | make_literal_int(value) -> int32
508 | make_literal_float(value) -> float
509 | make_literal_byte(value) -> uint8
510 | make_literal_bool(value) -> bool
511 | load_interstitial_ad(ad_id_index) -> None
512 | load_class_asset_blocking(asset_class) -> type(Class)
513 | load_asset_blocking(asset) -> Object
514 | line_trace_single_for_objects(world_context_object, start, end, object_types, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
515 | line_trace_single_by_profile(world_context_object, start, end, profile_name, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
516 | line_trace_single(world_context_object, start, end, trace_channel, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
517 | line_trace_multi_for_objects(world_context_object, start, end, object_types, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
518 | line_trace_multi_by_profile(world_context_object, start, end, profile_name, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
519 | line_trace_multi(world_context_object, start, end, trace_channel, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
520 | launch_url(url) -> None
521 | un_pause_timer_handle(world_context_object, handle) -> None
522 | un_pause_timer_delegate(delegate) -> None
523 | un_pause_timer(object, function_name) -> None
524 | timer_exists_handle(world_context_object, handle) -> bool
525 | timer_exists_delegate(delegate) -> bool
526 | timer_exists(object, function_name) -> bool
527 | set_timer_delegate(delegate, time, looping, initial_start_delay=0.000000, initial_start_delay_variance=0.000000) -> TimerHandle
528 | set_timer(object, function_name, time, looping, initial_start_delay=0.000000, initial_start_delay_variance=0.000000) -> TimerHandle
529 | pause_timer_handle(world_context_object, handle) -> None
530 | pause_timer_delegate(delegate) -> None
531 | pause_timer(object, function_name) -> None
532 | is_valid_timer_handle(handle) -> bool
533 | is_timer_paused_handle(world_context_object, handle) -> bool
534 | is_timer_paused_delegate(delegate) -> bool
535 | is_timer_paused(object, function_name) -> bool
536 | is_timer_active_handle(world_context_object, handle) -> bool
537 | is_timer_active_delegate(delegate) -> bool
538 | is_timer_active(object, function_name) -> bool
539 | invalidate_timer_handle(handle) -> (TimerHandle, handle=TimerHandle)
540 | get_timer_remaining_time_handle(world_context_object, handle) -> float
541 | get_timer_remaining_time_delegate(delegate) -> float
542 | get_timer_remaining_time(object, function_name) -> float
543 | get_timer_elapsed_time_handle(world_context_object, handle) -> float
544 | get_timer_elapsed_time_delegate(delegate) -> float
545 | get_timer_elapsed_time(object, function_name) -> float
546 | clear_timer_handle(world_context_object, handle) -> None
547 | clear_timer_delegate(delegate) -> None
548 | clear_timer(object, function_name) -> None
549 | clear_and_invalidate_timer_handle(world_context_object, handle) -> TimerHandle
550 | is_valid_soft_object_reference(soft_object_reference) -> bool
551 | is_valid_soft_class_reference(soft_class_reference) -> bool
552 | is_valid_primary_asset_type(primary_asset_type) -> bool
553 | is_valid_primary_asset_id(primary_asset_id) -> bool
554 | is_valid_class(class_) -> bool
555 | is_valid(object) -> bool
556 | is_unattended() -> bool
557 | is_standalone(world_context_object) -> bool
558 | is_split_screen(world_context_object) -> bool
559 | is_server(world_context_object) -> bool
560 | is_screensaver_enabled() -> bool
561 | is_packaged_for_distribution() -> bool
562 | is_logged_in(specific_player) -> bool
563 | is_interstitial_ad_requested() -> bool
564 | is_interstitial_ad_available() -> bool
565 | is_dedicated_server(world_context_object) -> bool
566 | is_controller_assigned_to_gamepad(controller_id) -> bool
567 | hide_ad_banner() -> None
568 | get_volume_buttons_handled_by_system() -> bool
569 | get_unique_device_id() -> str
570 | get_supported_fullscreen_resolutions() -> Array(IntPoint) or None
571 | get_soft_object_reference_from_primary_asset_id(primary_asset_id) -> Object
572 | get_soft_class_reference_from_primary_asset_id(primary_asset_id) -> Class
573 | get_rendering_material_quality_level() -> int32
574 | get_rendering_detail_mode() -> int32
575 | get_project_saved_directory() -> str
576 | get_project_directory() -> str
577 | get_project_content_directory() -> str
578 | get_primary_assets_with_bundle_state(required_bundles, excluded_bundles, valid_types, force_current_state) -> Array(PrimaryAssetId)
579 | get_primary_asset_id_list(primary_asset_type) -> Array(PrimaryAssetId)
580 | get_primary_asset_id_from_soft_object_reference(soft_object_reference) -> PrimaryAssetId
581 | get_primary_asset_id_from_soft_class_reference(soft_class_reference) -> PrimaryAssetId
582 | get_primary_asset_id_from_object(object) -> PrimaryAssetId
583 | get_primary_asset_id_from_class(class_) -> PrimaryAssetId
584 | get_preferred_languages() -> Array(str)
585 | get_platform_user_name() -> str
586 | get_platform_user_dir() -> str
587 | get_path_name(object) -> str
588 | get_outer_object(object) -> Object
589 | get_object_name(object) -> str
590 | get_object_from_primary_asset_id(primary_asset_id) -> Object
591 | get_min_y_resolution_for_ui() -> int32
592 | get_min_y_resolution_for3d_view() -> int32
593 | get_local_currency_symbol() -> str
594 | get_local_currency_code() -> str
595 | get_game_time_in_seconds(world_context_object) -> float
596 | get_gamepad_controller_name(controller_id) -> str
597 | get_game_name() -> str
598 | get_game_bundle_id() -> str
599 | get_frame_count() -> int64
600 | get_engine_version() -> str
601 | get_display_name(object) -> str
602 | get_device_id() -> str
603 | get_default_locale() -> str
604 | get_default_language() -> str
605 | get_current_bundle_state(primary_asset_id, force_current_state) -> Array(Name) or None
606 | get_convenient_windowed_resolutions() -> Array(IntPoint) or None
607 | get_console_variable_int_value(variable_name) -> int32
608 | get_console_variable_float_value(variable_name) -> float
609 | get_console_variable_bool_value(variable_name) -> bool
610 | get_component_bounds(component) -> (origin=Vector, box_extent=Vector, sphere_radius=float)
611 | get_command_line() -> str
612 | get_class_from_primary_asset_id(primary_asset_id) -> type(Class)
613 | get_class_display_name(class_) -> str
614 | get_ad_id_count() -> int32
615 | get_actor_list_from_component_list(component_list, actor_class_filter) -> Array(Actor)
616 | get_actor_bounds(actor) -> (origin=Vector, box_extent=Vector)
617 | force_close_ad_banner() -> None
618 | flush_persistent_debug_lines(world_context_object) -> None
619 | flush_debug_strings(world_context_object) -> None
620 | execute_console_command(world_context_object, command, specific_player=None) -> None
621 | equal_equal_soft_object_reference(a, b) -> bool
622 | equal_equal_soft_class_reference(a, b) -> bool
623 | equal_equal_primary_asset_type(a, b) -> bool
624 | equal_equal_primary_asset_id(a, b) -> bool
625 | end_transaction() -> int32
626 | draw_debug_string(world_context_object, text_location, text, test_base_actor=None, text_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000) -> None
627 | draw_debug_sphere(world_context_object, center, radius=100.000000, segments=12, line_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000, thickness=0.000000) -> None
628 | draw_debug_point(world_context_object, position, size, point_color, duration=0.000000) -> None
629 | draw_debug_plane(world_context_object, plane_coordinates, location, size, plane_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000) -> None
630 | draw_debug_line(world_context_object, line_start, line_end, line_color, duration=0.000000, thickness=0.000000) -> None
631 | draw_debug_frustum(world_context_object, frustum_transform, frustum_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000, thickness=0.000000) -> None
632 | draw_debug_float_history_transform(world_context_object, float_history, draw_transform, draw_size, draw_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000) -> None
633 | draw_debug_float_history_location(world_context_object, float_history, draw_location, draw_size, draw_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000) -> None
634 | draw_debug_cylinder(world_context_object, start, end, radius=100.000000, segments=12, line_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000, thickness=0.000000) -> None
635 | draw_debug_coordinate_system(world_context_object, axis_loc, axis_rot, scale=1.000000, duration=0.000000, thickness=0.000000) -> None
636 | draw_debug_cone_in_degrees(world_context_object, origin, direction, length=100.000000, angle_width=45.000000, angle_height=45.000000, num_sides=12, line_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000, thickness=0.000000) -> None
637 | draw_debug_cone(world_context_object, origin, direction, length, angle_width, angle_height, num_sides, line_color, duration=0.000000, thickness=0.000000) -> None
638 | draw_debug_circle(world_context_object, center, radius, num_segments=12, line_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000, thickness=0.000000, y_axis=[0.000000, 1.000000, 0.000000], z_axis=[0.000000, 0.000000, 1.000000], draw_axis=False) -> None
639 | draw_debug_capsule(world_context_object, center, half_height, radius, rotation, line_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000, thickness=0.000000) -> None
640 | draw_debug_camera(camera_actor, camera_color=[0.000000, 0.000000, 0.000000, 0.000000], duration=0.000000) -> None
641 | draw_debug_box(world_context_object, center, extent, line_color, rotation=[0.000000, 0.000000, 0.000000], duration=0.000000, thickness=0.000000) -> None
642 | draw_debug_arrow(world_context_object, line_start, line_end, arrow_size, line_color, duration=0.000000, thickness=0.000000) -> None
643 | does_implement_interface(test_object, interface) -> bool
644 | delay(world_context_object, duration, latent_info) -> None
645 | create_copy_for_undo_buffer(object_to_modify) -> None
646 | convert_to_relative_path(filename) -> str
647 | convert_to_absolute_path(filename) -> str
648 | conv_soft_obj_path_to_soft_obj_ref(soft_object_path) -> Object
649 | conv_soft_object_reference_to_string(soft_object_reference) -> str
650 | conv_soft_class_reference_to_string(soft_class_reference) -> str
651 | conv_soft_class_path_to_soft_class_ref(soft_class_path) -> Class
652 | conv_primary_asset_type_to_string(primary_asset_type) -> str
653 | conv_primary_asset_id_to_string(primary_asset_id) -> str
654 | conv_interface_to_object(interface) -> Object
655 | control_screensaver(allow_screen_saver) -> None
656 | component_overlap_components(component, component_transform, object_types, component_class_filter, actors_to_ignore) -> Array(PrimitiveComponent) or None
657 | component_overlap_actors(component, component_transform, object_types, actor_class_filter, actors_to_ignore) -> Array(Actor) or None
658 | collect_garbage() -> None
659 | capsule_trace_single_for_objects(world_context_object, start, end, radius, half_height, object_types, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
660 | capsule_trace_single_by_profile(world_context_object, start, end, radius, half_height, profile_name, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
661 | capsule_trace_single(world_context_object, start, end, radius, half_height, trace_channel, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
662 | capsule_trace_multi_for_objects(world_context_object, start, end, radius, half_height, object_types, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
663 | capsule_trace_multi_by_profile(world_context_object, start, end, radius, half_height, profile_name, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
664 | capsule_trace_multi(world_context_object, start, end, radius, half_height, trace_channel, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
665 | capsule_overlap_components(world_context_object, capsule_pos, radius, half_height, object_types, component_class_filter, actors_to_ignore) -> Array(PrimitiveComponent) or None
666 | capsule_overlap_actors(world_context_object, capsule_pos, radius, half_height, object_types, actor_class_filter, actors_to_ignore) -> Array(Actor) or None
667 | can_launch_url(url) -> bool
668 | cancel_transaction(index) -> None
669 | box_trace_single_for_objects(world_context_object, start, end, half_size, orientation, object_types, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
670 | box_trace_single_by_profile(world_context_object, start, end, half_size, orientation, profile_name, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
671 | box_trace_single(world_context_object, start, end, half_size, orientation, trace_channel, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> HitResult or None
672 | box_trace_multi_for_objects(world_context_object, start, end, half_size, orientation, object_types, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
673 | box_trace_multi_by_profile(world_context_object, start, end, half_size, orientation, profile_name, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
674 | box_trace_multi(world_context_object, start, end, half_size, orientation, trace_channel, trace_complex, actors_to_ignore, draw_debug_type, ignore_self, trace_color=[0.000000, 0.000000, 0.000000, 0.000000], trace_hit_color=[0.000000, 0.000000, 0.000000, 0.000000], draw_time=5.000000) -> Array(HitResult) or None
675 | box_overlap_components(world_context_object, box_pos, extent, object_types, component_class_filter, actors_to_ignore) -> Array(PrimitiveComponent) or None
676 | box_overlap_actors(world_context_object, box_pos, box_extent, object_types, actor_class_filter, actors_to_ignore) -> Array(Actor) or None
677 | begin_transaction(context, description, primary_object) -> int32
678 | add_float_history_sample(value, float_history) -> DebugFloatHistory
679 | """
680 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/unreal_icon.py:
--------------------------------------------------------------------------------
1 | # Know default icon in EditorStyle set
2 | EditorStyle = [
3 | "Kismet.Status.Unknown"
4 | "Kismet.Status.Error"
5 | "Kismet.Status.Good"
6 | "Kismet.Status.Warning"
7 |
8 | "AssetEditor.SaveAsset"
9 | "SystemWideCommands.FindInContentBrowser"
10 | "BlueprintEditor.FindInBlueprint"
11 | "MaterialEditor.FindInMaterial"
12 | "TranslationEditor.Search"
13 |
14 | "FullBlueprintEditor.EditGlobalOptions"
15 | "FullBlueprintEditor.EditClassDefaults"
16 | "FullBlueprintEditor.SwitchToBlueprintDefaultsMode"
17 | "BlueprintEditor.EnableSimulation"
18 | "PlayWorld.PlayInViewport"
19 | "GraphEditor.ToggleHideUnrelatedNodes"
20 | "LevelEditor.Build"
21 | "LevelEditor.Recompile"
22 | "LevelEditor.OpenContentBrowser"
23 | "LevelEditor.EditorModes"
24 | "LevelEditor.ToggleVR"
25 | "LevelEditor.GameSettings"
26 | "LevelEditor.OpenLevelBlueprint"
27 | "LevelEditor.OpenMarketplace"
28 | "LevelEditor.EditMatinee"
29 |
30 | "LevelEditor.SourceControl"
31 | "LevelEditor.SourceControl.On"
32 | "LevelEditor.SourceControl.Off"
33 | "LevelEditor.SourceControl.Unknown"
34 | "LevelEditor.SourceControl.Problem"
35 | "MaterialEditor.Apply"
36 | "MaterialEditor.CameraHome"
37 | "MaterialEditor.ToggleRealtimeExpressions"
38 | "MaterialEditor.AlwaysRefreshAllPreviews"
39 | "MaterialEditor.ToggleLivePreview"
40 | "MaterialEditor.ToggleMaterialStats"
41 | "MaterialEditor.TogglePlatformStats"
42 | "MaterialEditor.CleanUnusedExpressions"
43 | "MaterialEditor.ShowHideConnectors"
44 |
45 | "PlayWorld.PausePlaySession"
46 | "PlayWorld.StopPlaySession"
47 | "PlayWorld.PossessPlayer"
48 | "PlayWorld.EjectFromPlayer"
49 | "PlayWorld.RepeatLastLaunch"
50 | "PlayWorld.PlayInNewProcess"
51 | "PlayWorld.PlayInEditorFloating"
52 | "PlayWorld.PlayInMobilePreview"
53 | "PlayWorld.PlayInVR"
54 | "PlayWorld.LateJoinSession"
55 | "PlayWorld.ResumePlaySession"
56 | "PlayWorld.Simulate"
57 | "PlayWorld.RepeatLastPlay"
58 |
59 | "PlayWorld.SingleFrameAdvance"
60 | "PlayWorld.ShowCurrentStatement"
61 | "PlayWorld.StepOut"
62 | "PlayWorld.StepInto"
63 | "PlayWorld.StepOver"
64 |
65 | "BTEditor.SwitchToBehaviorTreeMode"
66 | "UMGEditor.SwitchToDesigner"
67 | "FullBlueprintEditor.SwitchToScriptingMode"
68 |
69 | "EditorViewport.ToggleRealTime"
70 |
71 | "StaticMeshEditor.SetShowWireframe"
72 | "StaticMeshEditor.SetShowVertexColor"
73 | "StaticMeshEditor.SetRealtimePreview"
74 | "StaticMeshEditor.ReimportMesh"
75 | "StaticMeshEditor.SetShowBounds"
76 | "StaticMeshEditor.SetShowCollision"
77 | "StaticMeshEditor.SetShowGrid"
78 | "StaticMeshEditor.SetDrawUVs"
79 | "StaticMeshEditor.ResetCamera"
80 | "StaticMeshEditor.SetShowPivot"
81 | "StaticMeshEditor.SetShowSockets"
82 | "StaticMeshEditor.SetShowNormals"
83 | "StaticMeshEditor.SetShowTangents"
84 | "StaticMeshEditor.SetShowBinormals"
85 | "StaticMeshEditor.SetDrawAdditionalData"
86 | "StaticMeshEditor.SetShowVertices"
87 | "StaticMeshEditor.ToggleShowPivots"
88 | "StaticMeshEditor.ToggleShowSockets"
89 | "StaticMeshEditor.ToggleShowNormals"
90 | "StaticMeshEditor.ToggleShowTangents"
91 | "StaticMeshEditor.ToggleShowBinormals"
92 | "StaticMeshEditor.ToggleShowBounds"
93 | "StaticMeshEditor.ToggleShowGrids"
94 | "StaticMeshEditor.ToggleShowVertices"
95 | "StaticMeshEditor.ToggleShowWireframes"
96 | "StaticMeshEditor.ToggleShowVertexColors"
97 | "Persona.BakeMaterials"
98 |
99 | "AnimationEditor.ApplyCompression"
100 | "AnimationEditor.ExportToFBX"
101 | "AnimationEditor.ReimportAnimation"
102 | "AnimationEditor.CreateAsset"
103 | "AnimationEditor.SetKey"
104 | "AnimationEditor.ApplyAnimation"
105 |
106 | "Persona.TogglePreviewAsset"
107 | "Persona.CreateAsset"
108 | "Persona.ExportToFBX"
109 | "Persona.ConvertToStaticMesh"
110 |
111 | "EditorViewport.LocationGridSnap"
112 | "EditorViewport.RotationGridSnap"
113 | "EditorViewport.Layer2DSnap"
114 | "EditorViewport.ScaleGridSnap"
115 | "EditorViewport.ToggleSurfaceSnapping"
116 | "EditorViewport.RelativeCoordinateSystem_Local"
117 | "EditorViewport.RelativeCoordinateSystem_Local.Small"
118 | "EditorViewport.RelativeCoordinateSystem_World"
119 | "EditorViewport.RelativeCoordinateSystem_World.Small"
120 | "EditorViewport.CamSpeedSetting"
121 |
122 | "DetailsView.EditRawProperties"
123 |
124 | "CurveEd.Visible"
125 | "CurveEd.VisibleHighlight"
126 | "CurveEd.Invisible"
127 | "CurveEd.InvisibleHighlight"
128 | "Level.VisibleIcon16x"
129 | "Level.VisibleHighlightIcon16x"
130 | "Level.NotVisibleIcon16x"
131 | "Level.NotVisibleHighlightIcon16x"
132 | "GenericViewButton"
133 |
134 | "PropertyWindow.Button_CreateNewBlueprint"
135 | "PropertyWindow.Button_Browse"
136 | "PropertyWindow.Button_Use"
137 |
138 | "GenericLock"
139 | "GenericLock.Small"
140 | "GenericUnlock"
141 | "GenericUnlock.Small"
142 | "PropertyWindow.Locked"
143 | "PropertyWindow.Unlocked"
144 | "FindResults.LockButton_Locked"
145 | "FindResults.LockButton_Unlocked"
146 | "ContentBrowser.LockButton_Locked"
147 | "ContentBrowser.LockButton_Unlocked"
148 |
149 | "DetailsView.PulldownArrow.Down"
150 | "DetailsView.PulldownArrow.Down.Hovered"
151 | "DetailsView.PulldownArrow.Up"
152 | "DetailsView.PulldownArrow.Up.Hovered"
153 |
154 | "TreeArrow_Collapsed"
155 | "TreeArrow_Collapsed_Hovered"
156 | "TreeArrow_Expanded"
157 | "TreeArrow_Expanded_Hovered"
158 |
159 | "EditorViewport.TranslateMode"
160 | "EditorViewport.RotateMode"
161 | "EditorViewport.ScaleMode"
162 |
163 | "TimelineEditor.AddFloatTrack"
164 | "TimelineEditor.AddVectorTrack"
165 | "TimelineEditor.AddEventTrack"
166 | "TimelineEditor.AddColorTrack"
167 | "TimelineEditor.AddCurveAssetTrack"
168 | "TimelineEditor.DeleteTrack"
169 | ]
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/unreal_startup.py:
--------------------------------------------------------------------------------
1 | import unreal
2 | import unreal_uiutils
3 | from unreal_global import *
4 | from unreal_utils import AssetRegistryPostLoad
5 | unreal.log("""@
6 |
7 | ####################
8 |
9 | Init Start up Script
10 |
11 | ####################
12 |
13 | """)
14 |
15 | assetregistry_pretickhandle = None
16 |
17 | def assetregistry_postload_handle(deltaTime):
18 | """
19 | Run callback method after registry run to prevent crashed when create new asset at startupS
20 | """
21 | unreal.log_warning("..Checking Asset Registy Status...")
22 | if AssetRegistry.is_loading_assets():
23 | unreal.log_warning("..Asset registy still loading...")
24 | else:
25 | unreal.log_warning("Asset registy ready!")
26 | unreal.unregister_slate_post_tick_callback(assetregistry_pretickhandle)
27 | AssetRegistryPostLoad.run_callbacks()
28 |
29 | assetregistry_pretickhandle = unreal.register_slate_post_tick_callback(assetregistry_postload_handle)
30 |
31 | import BlueprintLibrary
32 | import UserInterfaces
33 |
34 | def reload():
35 | import importlib
36 | importlib.reload(BlueprintLibrary)
37 | importlib.reload(UserInterfaces)
38 |
39 | unreal_uiutils.refresh()
40 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/unreal_uiutils.py:
--------------------------------------------------------------------------------
1 | # 4.25 +
2 |
3 | import unreal
4 |
5 | # @unreal.uclass()
6 | class EditorToolbarMenuEntry(unreal.ToolMenuEntryScript):
7 | def __init__(
8 | self,
9 | menu="None",
10 | section="None",
11 | name="None",
12 | label="",
13 | tool_tip="",
14 | icon=["None", "None", "None"],
15 | owner_name="None",
16 | insert_position=["None", unreal.ToolMenuInsertType.DEFAULT],
17 | advanced=[
18 | "None",
19 | unreal.MultiBlockType.MENU_ENTRY,
20 | unreal.UserInterfaceActionType.BUTTON,
21 | False,
22 | False,
23 | True,
24 | False,
25 | ],
26 | ):
27 | unreal.Object.__init__(self)
28 | self._data = unreal.ToolMenuEntryScriptData(
29 | menu,
30 | section,
31 | name,
32 | label,
33 | tool_tip,
34 | icon,
35 | owner_name,
36 | insert_position,
37 | advanced,
38 | )
39 |
40 | @property
41 | def data(self):
42 | return self._data
43 |
44 |
45 | def refresh():
46 | unreal.ToolMenus.get().refresh_all_widgets()
47 |
48 | # Get Toolbar
49 |
50 | def get_toolbar():
51 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar")
52 |
53 | def get_staticmesh_toolbar():
54 | return unreal.ToolMenus.get().find_menu("AssetEditor.StaticMeshEditor.ToolBar")
55 |
56 | def get_skeletalmesh_toolbar():
57 | return unreal.ToolMenus.get().find_menu("AssetEditor.SkeletalMeshEditor.ToolBar")
58 |
59 | # Get Toolbar SubMenu
60 |
61 | def get_buildcombo_sub_menu():
62 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.BuildComboButton")
63 |
64 | def get_buildcombo_ligtingquality_sub_menu():
65 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.BuildComboButton.LightingQuality")
66 |
67 | def get_buildcombo_ligtinginfo_sub_menu():
68 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.BuildComboButton.LightingInfo")
69 |
70 | def get_buildcombo_ligtingdensity_sub_menu():
71 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.BuildComboButton.LightingInfo.LightingDensity")
72 |
73 | def get_buildcombo_ligtingresolution_sub_menu():
74 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.BuildComboButton.LightingInfo.LightingResolution")
75 |
76 | def get_leveltoolbar_setttings_sub_menu():
77 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.LevelToolbarQuickSettings")
78 |
79 | def get_sourcecontrol_sub_menu():
80 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.SourceControl")
81 |
82 | def get_editormodes_sub_menu():
83 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.EditorModes")
84 |
85 | def get_openblueprint_sub_menu():
86 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.OpenBlueprint")
87 |
88 | def get_cinematics_sub_menu():
89 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.Cinematics")
90 |
91 | def get_compilecombo_sub_menu():
92 | return unreal.ToolMenus.get().find_menu("LevelEditor.LevelEditorToolBar.CompileComboButton")
93 |
94 | # Get Context Menu
95 |
96 | def get_asset_context_menu():
97 | return unreal.ToolMenus.get().find_menu("ContentBrowser.AssetContextMenu")
98 |
99 | def get_folder_context_menu():
100 | return unreal.ToolMenus.get().find_menu("ContentBrowser.FolderContextMenu")
101 |
102 | def get_actor_context_menu():
103 | return unreal.ToolMenus.get().find_menu("LevelEditor.ActorContextMenu")
104 |
105 | def get_dragdrop_context_menu():
106 | return unreal.ToolMenus.get().find_menu("ContentBrowser.DragDropContextMenu")
107 |
108 | def get_sequence_asset_context_menu():
109 | return unreal.ToolMenus.get().find_menu("ContentBrowser.AssetContextMenu.LevelSequence")
110 |
111 | def get_cameraanim_asset_context_menu():
112 | return unreal.ToolMenus.get().find_menu("ContentBrowser.AssetContextMenu.CameraAnim")
113 |
114 | def get_mediaplayer_assetpicker_context_menu():
115 | return unreal.ToolMenus.get().find_menu("MediaPlayer.AssetPickerAssetContextMenu")
116 |
117 | def get_soundwave_asset_context_menu():
118 | return unreal.ToolMenus.get().find_menu("ContentBrowser.AssetContextMenu.SoundWave")
119 |
120 | def get_addnew_context_menu():
121 | return unreal.ToolMenus.get().find_menu("ContentBrowser.AddNewContextMenu")
122 |
123 | def get_toolbar_item(name):
124 | return unreal.ToolMenus.get().find_menu(
125 | "LevelEditor.LevelEditorToolBar.{}".format(name)
126 | )
127 |
128 | # Get Main Menu
129 |
130 | def get_mainmenu():
131 | return unreal.ToolMenus.get().find_menu("MainFrame.MainMenu")
132 |
133 | def get_file_menu():
134 | return unreal.ToolMenus.get().find_menu("MainFrame.MainMenu.File")
135 |
136 | def get_edit_menu():
137 | return unreal.ToolMenus.get().find_menu("MainFrame.MainMenu.Edit")
138 |
139 | def get_asset_menu():
140 | return unreal.ToolMenus.get().find_menu("MainFrame.MainMenu.Window")
141 |
142 | def get_window_menu():
143 | return unreal.ToolMenus.get().find_menu("MainFrame.MainMenu.Help")
144 |
145 | def get_mainmenu_item(name):
146 | return unreal.ToolMenus.get().find_menu("MainFrame.MainMenu.{}".format(name))
147 |
148 |
149 | def create_python_tool_menu_entry(
150 | name, label, command_string="", entry_type=unreal.MultiBlockType.MENU_ENTRY
151 | ):
152 | menu_entry = unreal.ToolMenuEntry(name, type=entry_type)
153 | menu_entry.set_label(label)
154 | if command_string:
155 | menu_entry.set_string_command(
156 | unreal.ToolMenuStringCommandType.PYTHON, "python", command_string
157 | )
158 | return menu_entry
159 |
160 |
161 | def create_menu_button(name, label, command_string=""):
162 | return create_python_tool_menu_entry(name, label, command_string)
163 |
164 |
165 | def create_toolbar_button(
166 | name, label, section_name="", icons =["EditorStyle", "LevelEditor.EditorModes", "LevelEditor.EditorModes"], command_string="", register_button=True
167 | ):
168 | button = create_python_tool_menu_entry(
169 | name, label, command_string, entry_type=unreal.MultiBlockType.TOOL_BAR_BUTTON
170 | )
171 | button.set_icon(*icons)
172 | if register_button:
173 | get_toolbar().add_menu_entry(section_name, button)
174 | return button
175 |
176 |
177 | def create_toolbar_combo_button(name, section_name, tool_tip="", register_button=True):
178 | # menu_name = ".".join([str(get_toolbar().menu_name),menu])
179 | # section_name = ".".join([str(get_toolbar().menu_name), menu, section])
180 | # get_toolbar().add_section(section_name)
181 | # menu_entry_script = EditorToolbarMenuEntry(get_toolbar().menu_name, section_name, name, "", tool_tip, ["EditorStyle", "DetailsView.PulldownArrow.Down", "DetailsView.PulldownArrow.Down"], get_toolbar().menu_name, ["None", unreal.ToolMenuInsertType.DEFAULT], ["None", unreal.MultiBlockType.TOOL_BAR_COMBO_BUTTON, unreal.UserInterfaceActionType.BUTTON, True, True, True, False])
182 | # menu_entry_script.register_menu_entry()
183 | menu_entry = unreal.ToolMenuEntry(
184 | name, type=unreal.MultiBlockType.TOOL_BAR_COMBO_BUTTON
185 | )
186 | if register_button:
187 | get_toolbar().add_menu_entry(section_name, menu_entry)
188 | return menu_entry
189 |
190 |
191 | def create_editable_text(name, label):
192 | menu_entry = unreal.ToolMenuEntry(name, type=unreal.MultiBlockType.EDITABLE_TEXT)
193 | menu_entry.set_label(label)
194 | return menu_entry
195 |
196 |
197 | def create_widget(name):
198 | return unreal.ToolMenuEntry(name, type=unreal.MultiBlockType.WIDGET)
199 |
200 |
201 | def create_heading(name):
202 | return unreal.ToolMenuEntry(name, type=unreal.MultiBlockType.HEADING)
203 |
204 |
205 | def create_separator(name="Separator"):
206 | return unreal.ToolMenuEntry(name, type=unreal.MultiBlockType.SEPARATOR)
207 |
208 |
209 | def extend_mainmenu_item(mainmenu_name, section_name, name, label, tooltips=""):
210 | parent_menu = get_mainmenu_item(mainmenu_name)
211 | return parent_menu.add_sub_menu(
212 | parent_menu.menu_name, section_name, name, label, tooltips
213 | )
214 |
215 | def extend_file_menu(section_name, name, label, tooltips=""):
216 | extend_mainmenu_item("File", section_name, name, label, tooltips="")
217 |
218 | def extend_edit_menu(section_name, name, label, tooltips=""):
219 | extend_mainmenu_item("Edit", section_name, name, label, tooltips="")
220 |
221 | def extend_asset_menu(section_name, name, label, tooltips=""):
222 | extend_mainmenu_item("Asset", section_name, name, label, tooltips="")
223 |
224 | def extend_mesh_menu(section_name, name, label, tooltips=""):
225 | extend_mainmenu_item("Mesh", section_name, name, label, tooltips="")
226 |
227 | def extend_help_menu(section_name, name, label, tooltips=""):
228 | extend_mainmenu_item("Help", section_name, name, label, tooltips="")
229 |
230 | def extend_mainmenu(name, label, tooltip=""):
231 | main_menu = get_mainmenu()
232 | return main_menu.add_sub_menu(
233 | main_menu.menu_name, unreal.Name(), name, label, tooltip
234 | )
235 |
236 | def extend_toolmenu(owner, name, label, section_name=unreal.Name(), tooltip=""):
237 | return owner.add_sub_menu(
238 | owner.menu_name, section_name, name, label, tooltip
239 | )
240 |
241 | def extend_toolbar(name, label, tooltip=""):
242 | toolbar = get_toolbar()
243 | return toolbar.add_sub_menu(toolbar.menu_name, unreal.Name(), name, label, tooltip)
244 |
245 | def add_sequencer_toolbarbutton(
246 | name, label, section_name="Asset", icons = None, command_string="", register_button=True
247 | ):
248 | button = create_python_tool_menu_entry(
249 | name, label, command_string, entry_type=unreal.MultiBlockType.TOOL_BAR_BUTTON
250 | )
251 | if icons:
252 | button.set_icon(*icons)
253 | if register_button:
254 | get_sequencer_toolbar().add_menu_entry(section_name, button)
255 | return button
256 |
257 | def parent_qt_window(qt_widget):
258 | unreal.parent_external_window_to_slate(qt_widget.winId())
259 |
260 | def show_message(title, message, message_type, default_value=unreal.AppReturnType.NO):
261 | return unreal.EditorDialog.show_message(title, message, message_type, default_value)
262 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Content/Python/unreal_utils.py:
--------------------------------------------------------------------------------
1 | # Nguyen Phi Hung @ 2020
2 | # hung.nguyen@onikuma.games
3 |
4 | from unreal_global import *
5 | import hashlib
6 | import unreal
7 |
8 |
9 | class AssetRegistryPostLoad:
10 | _callbacks = {}
11 |
12 | @classmethod
13 | def register_callback(cls, func, *args, **kws):
14 | cls._callbacks[hashlib.md5(str((func, args, kws)).encode()).hexdigest()] = (
15 | func,
16 | args,
17 | kws,
18 | )
19 |
20 | @classmethod
21 | def unregister_callback(cls, func, *args, **kws):
22 | try:
23 | del cls._callbacks[hashlib.md5(str((func, args, kws)).encode()).hexdigest()]
24 | except KeyError:
25 | unreal.log_error(
26 | "No callback name {} with arguments: {} and keywords {} to unregister".format(
27 | func.__name__, args, kws
28 | )
29 | )
30 |
31 | @classmethod
32 | def run_callbacks(cls):
33 | for func_name, func_param in cls._callbacks.items():
34 | func, args, kws = func_param
35 | unreal.log_warning(
36 | "execute {} with param {} keywords: {}".format(func.__name__, args, kws)
37 | )
38 | try:
39 | func(*args, **kws)
40 | except Exception as why:
41 | unreal.log_error("failed to run with error:\n{}".format(why))
42 |
43 | def get_outer_package():
44 | return unreal.find_object(None, "/Engine/Transient")
45 |
46 | def create_unreal_asset(
47 | asset_name, package_path, factory, asset_class, force=False, save=True
48 | ):
49 | """
50 |
51 | INPUT:
52 | asset_name: str
53 | Exp: "MyAwesomeBPActorClass"
54 | package_path: str
55 | Exp: "/Game/MyContentFolder"
56 | package_path: unreal.Factory:
57 | Exp: unreal.BlueprintFactory()
58 | asset_class: unreal.Object
59 | Exp: unreal.Actor
60 | force: bool
61 | Force remove old and create new one
62 | save: bool
63 | Save asset after creation
64 |
65 | OUPUT:
66 | unreal.Object
67 |
68 | """
69 |
70 | asset_path = "{}/{}".format(package_path, asset_name)
71 | if AssetLibrary.does_asset_exist(asset_path):
72 | if force:
73 | unreal.log_warning("{} exists. Skip creating".format(asset_name))
74 | return
75 | else:
76 | unreal.log_warning("{} exists. Remove existing asset.".format(asset_name))
77 | AssetLibrary.delete_asset(asset_path)
78 |
79 | factory.set_editor_property("ParentClass", asset_class)
80 |
81 | new_asset = AssetTools.create_asset(asset_name, package_path, None, factory)
82 |
83 | if save:
84 | AssetLibrary.save_loaded_asset(new_asset)
85 |
86 | return new_asset
87 |
88 |
89 | def create_levelsequence_asset(asset_name, package_path, force=False, save=True):
90 | create_unreal_asset(
91 | asset_name,
92 | package_path,
93 | unreal.LevelSequenceFactoryNew(),
94 | unreal.LevelSequence,
95 | force,
96 | save,
97 | )
98 |
99 |
100 | def create_blueprint_asset(
101 | asset_name, package_path, asset_parent_class, force=False, save=True
102 | ):
103 | create_unreal_asset(
104 | asset_name,
105 | package_path,
106 | unreal.BlueprintFactory(),
107 | asset_parent_class,
108 | force,
109 | save,
110 | )
111 |
112 |
113 | def create_editor_utility_blueprint(
114 | asset_name, asset_parent_class, force=False, save=False
115 | ):
116 | create_unreal_asset(
117 | f"{asset_name}",
118 | "/Engine/",
119 | unreal.EditorUtilityBlueprintFactory(),
120 | asset_parent_class,
121 | force,
122 | save,
123 | )
124 |
125 |
126 | def register_editor_utility_blueprint(asset_name, asset_parent_class):
127 | AssetRegistryPostLoad.register_callback(
128 | create_editor_utility_blueprint, asset_name, asset_parent_class
129 | )
130 |
131 |
132 | # def new_object(object_class):
133 | # unreal.load_object(unreal.new_object(object_class))
134 | # def register_editor_utility_blueprint(asset_name, asset_parent_class):
135 | # AssetRegistryPostLoad.register_callback(new_object, asset_parent_class)
136 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Phi Hung Nguyen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/PythonScripting.uplugin:
--------------------------------------------------------------------------------
1 | {
2 | "FileVersion": 3,
3 | "Version": 1,
4 | "VersionName": "1.0",
5 | "FriendlyName": "PythonScripting",
6 | "Description": "Python Scripting Template",
7 | "Category": "Other",
8 | "CreatedBy": "Nguyen Phi Hung",
9 | "CreatedByURL": "",
10 | "DocsURL": "",
11 | "MarketplaceURL": "",
12 | "SupportURL": "",
13 | "CanContainContent": true,
14 | "IsBetaVersion": false,
15 | "IsExperimentalVersion": false,
16 | "Installed": false,
17 | "Plugins": [
18 | {
19 | "Name": "PythonScriptPlugin",
20 | "Enabled": true
21 | },
22 | {
23 | "Name": "SequencerScripting",
24 | "Enabled": true
25 | },
26 | {
27 | "Name": "PythonAutomationTest",
28 | "Enabled": true
29 | },
30 | {
31 | "Name": "EditorScriptingUtilities",
32 | "Enabled": true
33 | }
34 | ]
35 | }
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/README.md:
--------------------------------------------------------------------------------
1 | # Template for extend unreal editor with python
2 |
3 | ## USAGE
4 | Clone this project to your Unreal Project Plugin folder
5 |
6 | In **BlueprintLibrary** Module duplicate relevant blueprint action utility sample and extend functionality, any module will be auto generated with blueprint asset when unreal load.
7 |
8 | In **User Interfaces** store examples of how to create menu and toolbar button as well as add python function to asset/actor context menu.
9 |
10 | !!! Note
11 | Test in Unreal 4.26
12 |
--------------------------------------------------------------------------------
/Integration/UE4PrismPlugins/Resources/Icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josephkirk/UnrealEnginePrismPlugin/afbe3817bc2bb657ffbfd46ed441e6a0ada6591a/Integration/UE4PrismPlugins/Resources/Icon128.png
--------------------------------------------------------------------------------
/Scripts/Prism_UnrealEngine_Functions.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | ####################################################
4 | #
5 | # PRISM - Pipeline for animation and VFX projects
6 | #
7 | # www.prism-pipeline.com
8 | #
9 | # contact: contact@prism-pipeline.com
10 | #
11 | ####################################################
12 | #
13 | #
14 | # Copyright (C) 2016-2020 Richard Frangenberg
15 | #
16 | # Licensed under GNU GPL-3.0-or-later
17 | #
18 | # This file is part of Prism.
19 | #
20 | # Prism is free software: you can redistribute it and/or modify
21 | # it under the terms of the GNU General Public License as published by
22 | # the Free Software Foundation, either version 3 of the License, or
23 | # (at your option) any later version.
24 | #
25 | # Prism is distributed in the hope that it will be useful,
26 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | # GNU General Public License for more details.
29 | #
30 | # You should have received a copy of the GNU General Public License
31 | # along with Prism. If not, see .
32 |
33 |
34 | import os
35 | import sys
36 | import platform
37 | import subprocess
38 |
39 | try:
40 | from PySide2.QtCore import *
41 | from PySide2.QtGui import *
42 | from PySide2.QtWidgets import *
43 | except:
44 | from PySide.QtCore import *
45 | from PySide.QtGui import *
46 |
47 |
48 | from PrismUtils.Decorators import err_catcher as err_catcher
49 |
50 |
51 | class Prism_UnrealEngine_Functions(object):
52 | def __init__(self, core, plugin):
53 | self.core = core
54 | self.plugin = plugin
55 | self.win = platform.system() == "Windows"
56 |
57 | @err_catcher(name=__name__)
58 | def startup(self, origin):
59 | # for obj in QApplication.topLevelWidgets():
60 | # if obj.objectName() == 'UnrealEngineWindow':
61 | # QtParent = obj
62 | # break
63 | # else:
64 | # return False
65 |
66 | origin.timer.stop()
67 |
68 | origin.messageParent = QWidget()
69 | # origin.messageParent.setParent(QtParent, Qt.Window)
70 | if self.core.useOnTop:
71 | origin.messageParent.setWindowFlags(
72 | origin.messageParent.windowFlags() ^ Qt.WindowStaysOnTopHint
73 | )
74 |
75 | # origin.startasThread()
76 |
77 | return False
78 |
79 | # @err_catcher(name=__name__)
80 | # def prismSettings_loadUI(self, settings, tab):
81 | # # get executable path
82 | # tab.setLayout(QVBoxLayout())
83 | # return ""
84 |
85 | @err_catcher(name=__name__)
86 | def autosaveEnabled(self, origin):
87 | # get autosave enabled
88 | return False
89 |
90 | @err_catcher(name=__name__)
91 | def onProjectChanged(self, origin):
92 | pass
93 |
94 | @err_catcher(name=__name__)
95 | def sceneOpen(self, origin):
96 | if hasattr(origin, "asThread") and origin.asThread.isRunning():
97 | origin.startasThread()
98 |
99 | @err_catcher(name=__name__)
100 | def executeScript(self, origin, code, execute=False, logErr=True):
101 | if logErr:
102 | try:
103 | if not execute:
104 | return eval(code)
105 | else:
106 | exec(code)
107 | except Exception as e:
108 | msg = "\npython code:\n%s" % code
109 | exec("raise type(e), type(e)(e.message + msg), sys.exc_info()[2]")
110 | else:
111 | try:
112 | if not execute:
113 | return eval(code)
114 | else:
115 | exec(code)
116 | except:
117 | pass
118 |
119 | @err_catcher(name=__name__)
120 | def getCurrentFileName(self, origin, path=True):
121 | return ""
122 |
123 | @err_catcher(name=__name__)
124 | def getSceneExtension(self, origin):
125 | return self.sceneFormats[0]
126 |
127 | @err_catcher(name=__name__)
128 | def saveScene(self, origin, filepath, details={}):
129 | # save scenefile
130 | return True
131 |
132 | @err_catcher(name=__name__)
133 | def getImportPaths(self, origin):
134 | return []
135 |
136 | @err_catcher(name=__name__)
137 | def getFrameRange(self, origin):
138 | startframe = 0
139 | endframe = 100
140 |
141 | return [startframe, endframe]
142 |
143 | @err_catcher(name=__name__)
144 | def setFrameRange(self, origin, startFrame, endFrame):
145 | pass
146 |
147 | @err_catcher(name=__name__)
148 | def getFPS(self, origin):
149 | return 60
150 |
151 | @err_catcher(name=__name__)
152 | def setFPS(self, origin, fps):
153 | pass
154 |
155 | @err_catcher(name=__name__)
156 | def getAppVersion(self, origin):
157 | return "1.0"
158 |
159 | @err_catcher(name=__name__)
160 | def onProjectBrowserStartup(self, origin):
161 | # origin.sl_preview.mousePressEvent = origin.sliderDrag
162 | origin.sl_preview.mousePressEvent = origin.sl_preview.origMousePressEvent
163 |
164 | @err_catcher(name=__name__)
165 | def openScene(self, origin, filepath, force=False):
166 | # load scenefile
167 | return True
168 |
169 | @err_catcher(name=__name__)
170 | def correctExt(self, origin, lfilepath):
171 | return lfilepath
172 |
173 | @err_catcher(name=__name__)
174 | def setSaveColor(self, origin, btn):
175 | btn.setPalette(origin.savedPalette)
176 |
177 | @err_catcher(name=__name__)
178 | def clearSaveColor(self, origin, btn):
179 | btn.setPalette(origin.oldPalette)
180 |
181 | @err_catcher(name=__name__)
182 | def setProject_loading(self, origin):
183 | pass
184 |
185 | @err_catcher(name=__name__)
186 | def onPrismSettingsOpen(self, origin):
187 | pass
188 |
189 | @err_catcher(name=__name__)
190 | def createProject_startup(self, origin):
191 | pass
192 |
193 | @err_catcher(name=__name__)
194 | def editShot_startup(self, origin):
195 | pass
196 |
197 | @err_catcher(name=__name__)
198 | def shotgunPublish_startup(self, origin):
199 | pass
200 |
201 | @err_catcher(name=__name__)
202 | def sm_export_addObjects(self, origin, objects=None):
203 | if not objects:
204 | objects = [] # get selected objects from scene
205 |
206 | for i in objects:
207 | if not i in origin.nodes:
208 | origin.nodes.append(i)
209 |
210 | origin.updateUi()
211 | origin.stateManager.saveStatesToScene()
212 |
213 | @err_catcher(name=__name__)
214 | def getNodeName(self, origin, node):
215 | if self.isNodeValid(origin, node):
216 | try:
217 | return node.name
218 | except:
219 | QMessageBox.warning(
220 | self.core.messageParent, "Warning", "Cannot get name from %s" % node
221 | )
222 | return node
223 | else:
224 | return "invalid"
225 |
226 | @err_catcher(name=__name__)
227 | def selectNodes(self, origin):
228 | if origin.lw_objects.selectedItems() != []:
229 | nodes = []
230 | for i in origin.lw_objects.selectedItems():
231 | node = origin.nodes[origin.lw_objects.row(i)]
232 | if self.isNodeValid(origin, node):
233 | nodes.append(node)
234 | # select(nodes)
235 |
236 | @err_catcher(name=__name__)
237 | def isNodeValid(self, origin, handle):
238 | return True
239 |
240 | @err_catcher(name=__name__)
241 | def getCamNodes(self, origin, cur=False):
242 | sceneCams = [] # get cams from scene
243 | if cur:
244 | sceneCams = ["Current View"] + sceneCams
245 |
246 | return sceneCams
247 |
248 | @err_catcher(name=__name__)
249 | def getCamName(self, origin, handle):
250 | if handle == "Current View":
251 | return handle
252 |
253 | return str(nodes[0])
254 |
255 | @err_catcher(name=__name__)
256 | def selectCam(self, origin):
257 | if self.isNodeValid(origin, origin.curCam):
258 | select(origin.curCam)
259 |
260 | @err_catcher(name=__name__)
261 | def sm_export_startup(self, origin):
262 | pass
263 |
264 | # @err_catcher(name=__name__)
265 | # def sm_export_setTaskText(self, origin, prevTaskName, newTaskName):
266 | # origin.l_taskName.setText(newTaskName)
267 |
268 | @err_catcher(name=__name__)
269 | def sm_export_removeSetItem(self, origin, node):
270 | pass
271 |
272 | @err_catcher(name=__name__)
273 | def sm_export_clearSet(self, origin):
274 | pass
275 |
276 | @err_catcher(name=__name__)
277 | def sm_export_updateObjects(self, origin):
278 | pass
279 |
280 | @err_catcher(name=__name__)
281 | def sm_export_exportShotcam(self, origin, startFrame, endFrame, outputName):
282 | result = self.sm_export_exportAppObjects(
283 | origin,
284 | startFrame,
285 | endFrame,
286 | (outputName + ".abc"),
287 | nodes=[origin.curCam],
288 | expType=".abc",
289 | )
290 | result = self.sm_export_exportAppObjects(
291 | origin,
292 | startFrame,
293 | endFrame,
294 | (outputName + ".fbx"),
295 | nodes=[origin.curCam],
296 | expType=".fbx",
297 | )
298 | return result
299 |
300 | @err_catcher(name=__name__)
301 | def sm_export_exportAppObjects(
302 | self,
303 | origin,
304 | startFrame,
305 | endFrame,
306 | outputName,
307 | scaledExport=False,
308 | nodes=None,
309 | expType=None,
310 | ):
311 | pass
312 |
313 | @err_catcher(name=__name__)
314 | def sm_export_preDelete(self, origin):
315 | pass
316 |
317 | @err_catcher(name=__name__)
318 | def sm_export_unColorObjList(self, origin):
319 | origin.lw_objects.setStyleSheet(
320 | "QListWidget { border: 3px solid rgb(50,50,50); }"
321 | )
322 |
323 | @err_catcher(name=__name__)
324 | def sm_export_typeChanged(self, origin, idx):
325 | pass
326 |
327 | @err_catcher(name=__name__)
328 | def sm_export_preExecute(self, origin, startFrame, endFrame):
329 | warnings = []
330 |
331 | return warnings
332 |
333 | @err_catcher(name=__name__)
334 | def sm_export_loadData(self, origin, data):
335 | pass
336 |
337 | @err_catcher(name=__name__)
338 | def sm_export_getStateProps(self, origin, stateProps):
339 | stateProps.update()
340 |
341 | return stateProps
342 |
343 | @err_catcher(name=__name__)
344 | def sm_render_isVray(self, origin):
345 | return False
346 |
347 | @err_catcher(name=__name__)
348 | def sm_render_setVraySettings(self, origin):
349 | pass
350 |
351 | @err_catcher(name=__name__)
352 | def sm_render_startup(self, origin):
353 | pass
354 |
355 | @err_catcher(name=__name__)
356 | def sm_render_getRenderLayer(self, origin):
357 | rlayerNames = []
358 |
359 | return rlayerNames
360 |
361 | @err_catcher(name=__name__)
362 | def sm_render_refreshPasses(self, origin):
363 | pass
364 |
365 | @err_catcher(name=__name__)
366 | def sm_render_openPasses(self, origin, item=None):
367 | pass
368 |
369 | @err_catcher(name=__name__)
370 | def removeAOV(self, aovName):
371 | pass
372 |
373 | @err_catcher(name=__name__)
374 | def sm_render_preSubmit(self, origin, rSettings):
375 | pass
376 |
377 | @err_catcher(name=__name__)
378 | def sm_render_startLocalRender(self, origin, outputName, rSettings):
379 | pass
380 |
381 | @err_catcher(name=__name__)
382 | def sm_render_undoRenderSettings(self, origin, rSettings):
383 | pass
384 |
385 | @err_catcher(name=__name__)
386 | def sm_render_getDeadlineParams(self, origin, dlParams, homeDir):
387 | pass
388 |
389 | @err_catcher(name=__name__)
390 | def getCurrentRenderer(self, origin):
391 | return "Renderer"
392 |
393 | @err_catcher(name=__name__)
394 | def getCurrentSceneFiles(self, origin):
395 | curFileName = self.core.getCurrentFileName()
396 | scenefiles = [curFileName]
397 | return scenefiles
398 |
399 | @err_catcher(name=__name__)
400 | def sm_render_getRenderPasses(self, origin):
401 | return []
402 |
403 | @err_catcher(name=__name__)
404 | def sm_render_addRenderPass(self, origin, passName, steps):
405 | pass
406 |
407 | @err_catcher(name=__name__)
408 | def sm_render_preExecute(self, origin):
409 | warnings = []
410 |
411 | return warnings
412 |
413 | @err_catcher(name=__name__)
414 | def sm_render_fixOutputPath(self, origin, outputName):
415 | return outputName
416 |
417 | @err_catcher(name=__name__)
418 | def getProgramVersion(self, origin):
419 | return "1.0"
420 |
421 | @err_catcher(name=__name__)
422 | def sm_render_getDeadlineSubmissionParams(self, origin, dlParams, jobOutputFile):
423 | dlParams["Build"] = dlParams["build"]
424 | dlParams["OutputFilePath"] = os.path.split(jobOutputFile)[0]
425 | dlParams["OutputFilePrefix"] = os.path.splitext(
426 | os.path.basename(jobOutputFile)
427 | )[0]
428 | dlParams["Renderer"] = self.getCurrentRenderer(origin)
429 |
430 | if origin.chb_resOverride.isChecked() and "resolution" in dlParams:
431 | resString = "Image"
432 | dlParams[resString + "Width"] = str(origin.sp_resWidth.value())
433 | dlParams[resString + "Height"] = str(origin.sp_resHeight.value())
434 |
435 | return dlParams
436 |
437 | @err_catcher(name=__name__)
438 | def deleteNodes(self, origin, handles, num=0):
439 | pass
440 |
441 | @err_catcher(name=__name__)
442 | def sm_import_startup(self, origin):
443 | pass
444 |
445 | @err_catcher(name=__name__)
446 | def sm_import_disableObjectTracking(self, origin):
447 | self.deleteNodes(origin, [origin.setName])
448 |
449 | @err_catcher(name=__name__)
450 | def sm_import_importToApp(self, origin, doImport, update, impFileName):
451 | return {"result": result, "doImport": doImport}
452 |
453 | @err_catcher(name=__name__)
454 | def sm_import_updateObjects(self, origin):
455 | pass
456 |
457 | @err_catcher(name=__name__)
458 | def sm_import_removeNameSpaces(self, origin):
459 | pass
460 |
461 | @err_catcher(name=__name__)
462 | def sm_import_unitConvert(self, origin):
463 | pass
464 |
465 | @err_catcher(name=__name__)
466 | def sm_playblast_startup(self, origin):
467 | frange = self.getFrameRange(origin)
468 | origin.sp_rangeStart.setValue(frange[0])
469 | origin.sp_rangeEnd.setValue(frange[1])
470 |
471 | @err_catcher(name=__name__)
472 | def sm_playblast_createPlayblast(self, origin, jobFrames, outputName):
473 | pass
474 |
475 | @err_catcher(name=__name__)
476 | def sm_playblast_preExecute(self, origin):
477 | warnings = []
478 |
479 | return warnings
480 |
481 | @err_catcher(name=__name__)
482 | def sm_playblast_execute(self, origin):
483 | pass
484 |
485 | @err_catcher(name=__name__)
486 | def sm_playblast_postExecute(self, origin):
487 | pass
488 |
489 | @err_catcher(name=__name__)
490 | def onStateManagerOpen(self, origin):
491 | pass
492 |
493 | @err_catcher(name=__name__)
494 | def sm_saveStates(self, origin, buf):
495 | pass
496 |
497 | @err_catcher(name=__name__)
498 | def sm_saveImports(self, origin, importPaths):
499 | pass
500 |
501 | @err_catcher(name=__name__)
502 | def sm_readStates(self, origin):
503 | return []
504 |
505 | @err_catcher(name=__name__)
506 | def sm_deleteStates(self, origin):
507 | pass
508 |
509 | @err_catcher(name=__name__)
510 | def sm_getExternalFiles(self, origin):
511 | extFiles = []
512 | return [extFiles, []]
513 |
514 | @err_catcher(name=__name__)
515 | def sm_createRenderPressed(self, origin):
516 | origin.createPressed("Render")
517 |
--------------------------------------------------------------------------------
/Scripts/Prism_UnrealEngine_Integration.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | ####################################################
4 | #
5 | # PRISM - Pipeline for animation and VFX projects
6 | #
7 | # www.prism-pipeline.com
8 | #
9 | # contact: contact@prism-pipeline.com
10 | #
11 | ####################################################
12 | #
13 | #
14 | # Copyright (C) 2016-2020 Richard Frangenberg
15 | #
16 | # Licensed under GNU GPL-3.0-or-later
17 | #
18 | # This file is part of Prism.
19 | #
20 | # Prism is free software: you can redistribute it and/or modify
21 | # it under the terms of the GNU General Public License as published by
22 | # the Free Software Foundation, either version 3 of the License, or
23 | # (at your option) any later version.
24 | #
25 | # Prism is distributed in the hope that it will be useful,
26 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | # GNU General Public License for more details.
29 | #
30 | # You should have received a copy of the GNU General Public License
31 | # along with Prism. If not, see .
32 |
33 |
34 | import os
35 | import sys
36 | import platform
37 | import shutil
38 |
39 | try:
40 | from PySide2.QtCore import *
41 | from PySide2.QtGui import *
42 | from PySide2.QtWidgets import *
43 | except:
44 | from PySide.QtCore import *
45 | from PySide.QtGui import *
46 |
47 | from PrismUtils.Decorators import err_catcher_plugin as err_catcher
48 |
49 |
50 | class Prism_UnrealEngine_Integration(object):
51 | def __init__(self, core, plugin):
52 | self.core = core
53 | self.plugin = plugin
54 |
55 | if platform.system() == "Windows":
56 | self.examplePath = (
57 | r"C:\Program Files\Epic Games\UE_4.26"
58 | )
59 | elif platform.system() == "Linux":
60 | userName = (
61 | os.environ["SUDO_USER"]
62 | if "SUDO_USER" in os.environ
63 | else os.environ["USER"]
64 | )
65 | self.examplePath = os.path.join("/home", userName, "Epic Games", "UE_4.26")
66 | elif platform.system() == "Darwin":
67 | userName = (
68 | os.environ["SUDO_USER"]
69 | if "SUDO_USER" in os.environ
70 | else os.environ["USER"]
71 | )
72 | self.examplePath = (
73 | "/Users/%s/Library/Preferences/Epic Games/UE_4.26" % userName
74 | )
75 |
76 | @err_catcher(name=__name__)
77 | def getExecutable(self):
78 | execPath = ""
79 | if platform.system() == "Windows":
80 | defaultpath = os.path.join(self.getUnrealEnginePath(), "UE4_Editor.exe")
81 | if os.path.exists(defaultpath):
82 | execPath = defaultpath
83 |
84 | return execPath
85 |
86 |
87 | @err_catcher(name=__name__)
88 | def getUnrealEnginePath(self):
89 | # get executable path
90 | return ""
91 |
92 | def addIntegration(self, installPath):
93 | try:
94 | integrationBase = os.path.join(
95 | os.path.dirname(os.path.dirname(__file__)), "Integration"
96 | )
97 | addedFiles = []
98 |
99 | initpath = os.path.join(installPath, "scripts", "PrismInit.py")
100 |
101 | if os.path.exists(initpath):
102 | os.remove(initpath)
103 |
104 | if os.path.exists(initpath + "c"):
105 | os.remove(initpath + "c")
106 |
107 | origInitFile = os.path.join(integrationBase, "PrismInit.py")
108 | shutil.copy2(origInitFile, initpath)
109 | addedFiles.append(initpath)
110 |
111 | with open(initpath, "r") as init:
112 | initStr = init.read()
113 |
114 | with open(initpath, "w") as init:
115 | initStr = initStr.replace(
116 | "PRISMROOT", '"%s"' % self.core.prismRoot.replace("\\", "/")
117 | )
118 | init.write(initStr)
119 |
120 | if platform.system() in ["Linux", "Darwin"]:
121 | for i in addedFiles:
122 | os.chmod(i, 0o777)
123 |
124 | return True
125 |
126 | except Exception as e:
127 | exc_type, exc_obj, exc_tb = sys.exc_info()
128 |
129 | msgStr = (
130 | "Errors occurred during the installation of the UnrealEngine integration.\nThe installation is possibly incomplete.\n\n%s\n%s\n%s"
131 | % (str(e), exc_type, exc_tb.tb_lineno)
132 | )
133 | msgStr += "\n\nRunning this application as administrator could solve this problem eventually."
134 |
135 | QMessageBox.warning(self.core.messageParent, "Prism Integration", msgStr)
136 | return False
137 |
138 | def removeIntegration(self, installPath):
139 | try:
140 | initPy = os.path.join(installPath, "scripts", "PrismInit.py")
141 | initPyc = os.path.join(installPath, "scripts", "PrismInit.pyc")
142 | shelfpath = os.path.join(installPath, "prefs", "shelves", "shelf_Prism.mel")
143 |
144 | for i in [initPy, initPyc, shelfpath]:
145 | if os.path.exists(i):
146 | os.remove(i)
147 |
148 | userSetup = os.path.join(installPath, "scripts", "userSetup.py")
149 | self.core.integration.removeIntegrationData(filepath=userSetup)
150 |
151 | return True
152 |
153 | except Exception as e:
154 | exc_type, exc_obj, exc_tb = sys.exc_info()
155 |
156 | msgStr = (
157 | "Errors occurred during the removal of the UnrealEngine integration.\n\n%s\n%s\n%s"
158 | % (str(e), exc_type, exc_tb.tb_lineno)
159 | )
160 | msgStr += "\n\nRunning this application as administrator could solve this problem eventually."
161 |
162 | QMessageBox.warning(self.core.messageParent, "Prism Integration", msgStr)
163 | return False
164 |
165 | def updateInstallerUI(self, userFolders, pItem):
166 | try:
167 | pluginItem = QTreeWidgetItem([self.plugin.pluginName])
168 | pItem.addChild(pluginItem)
169 |
170 | pluginPath = self.examplePath
171 |
172 | if pluginPath != None and os.path.exists(pluginPath):
173 | pluginItem.setCheckState(0, Qt.Checked)
174 | pluginItem.setText(1, pluginPath)
175 | pluginItem.setToolTip(0, pluginPath)
176 | else:
177 | pluginItem.setCheckState(0, Qt.Unchecked)
178 | pluginItem.setText(1, "< doubleclick to browse path >")
179 | except Exception as e:
180 | exc_type, exc_obj, exc_tb = sys.exc_info()
181 | msg = QMessageBox.warning(
182 | self.core.messageParent,
183 | "Prism Installation",
184 | "Errors occurred during the installation.\n The installation is possibly incomplete.\n\n%s\n%s\n%s\n%s"
185 | % (__file__, str(e), exc_type, exc_tb.tb_lineno),
186 | )
187 | return False
188 |
189 | def installerExecute(self, pluginItem, result):
190 | try:
191 | pluginPaths = []
192 | installLocs = []
193 |
194 | if pluginItem.checkState(0) != Qt.Checked:
195 | return installLocs
196 |
197 | for i in range(pluginItem.childCount()):
198 | item = pluginItem.child(i)
199 | if item.checkState(0) == Qt.Checked and os.path.exists(item.text(1)):
200 | pluginPaths.append(item.text(1))
201 |
202 | for i in pluginPaths:
203 | result["UnrealEngine integration"] = self.core.integration.addIntegration(self.plugin.pluginName, path=i, quiet=True)
204 | if result["UnrealEngine integration"]:
205 | installLocs.append(i)
206 |
207 | return installLocs
208 | except Exception as e:
209 | exc_type, exc_obj, exc_tb = sys.exc_info()
210 | msg = QMessageBox.warning(
211 | self.core.messageParent,
212 | "Prism Installation",
213 | "Errors occurred during the installation.\n The installation is possibly incomplete.\n\n%s\n%s\n%s\n%s"
214 | % (__file__, str(e), exc_type, exc_tb.tb_lineno),
215 | )
216 | return False
217 |
--------------------------------------------------------------------------------
/Scripts/Prism_UnrealEngine_Variables.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | ####################################################
4 | #
5 | # PRISM - Pipeline for animation and VFX projects
6 | #
7 | # www.prism-pipeline.com
8 | #
9 | # contact: contact@prism-pipeline.com
10 | #
11 | ####################################################
12 | #
13 | #
14 | # Copyright (C) 2016-2020 Richard Frangenberg
15 | #
16 | # Licensed under GNU GPL-3.0-or-later
17 | #
18 | # This file is part of Prism.
19 | #
20 | # Prism is free software: you can redistribute it and/or modify
21 | # it under the terms of the GNU General Public License as published by
22 | # the Free Software Foundation, either version 3 of the License, or
23 | # (at your option) any later version.
24 | #
25 | # Prism is distributed in the hope that it will be useful,
26 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | # GNU General Public License for more details.
29 | #
30 | # You should have received a copy of the GNU General Public License
31 | # along with Prism. If not, see .
32 |
33 |
34 | class Prism_UnrealEngine_Variables(object):
35 | def __init__(self, core, plugin):
36 | self.version = "v1.3.0.0"
37 | self.pluginName = "UnrealEngine"
38 | self.pluginType = "App"
39 | self.appShortName = "UE4"
40 | self.appType = "3d"
41 | self.hasQtParent = False
42 | self.sceneFormats = [".uprismasset"]
43 | self.appSpecificFormats = self.sceneFormats
44 | self.outputFormats = [".uasset",".abc", ".obj", ".fbx"]
45 | self.appColor = [255, 255, 255]
46 | self.appVersionPresets = ["4.25", "4.26"]
47 | self.renderPasses = []
48 | self.hasFrameRange = False
49 | self.canOverrideExecuteable = False
50 | self.platforms = ["Windows", "Linux", "Darwin"]
51 |
--------------------------------------------------------------------------------
/Scripts/Prism_UnrealEngine_externalAccess_Functions.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | ####################################################
4 | #
5 | # PRISM - Pipeline for animation and VFX projects
6 | #
7 | # www.prism-pipeline.com
8 | #
9 | # contact: contact@prism-pipeline.com
10 | #
11 | ####################################################
12 | #
13 | #
14 | # Copyright (C) 2016-2020 Richard Frangenberg
15 | #
16 | # Licensed under GNU GPL-3.0-or-later
17 | #
18 | # This file is part of Prism.
19 | #
20 | # Prism is free software: you can redistribute it and/or modify
21 | # it under the terms of the GNU General Public License as published by
22 | # the Free Software Foundation, either version 3 of the License, or
23 | # (at your option) any later version.
24 | #
25 | # Prism is distributed in the hope that it will be useful,
26 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | # GNU General Public License for more details.
29 | #
30 | # You should have received a copy of the GNU General Public License
31 | # along with Prism. If not, see .
32 |
33 |
34 | from asyncio.log import logger
35 | import os
36 | import platform
37 |
38 | try:
39 | from PySide2.QtCore import *
40 | from PySide2.QtGui import *
41 | from PySide2.QtWidgets import *
42 | except:
43 | from PySide.QtCore import *
44 | from PySide.QtGui import *
45 |
46 | from PrismUtils.Decorators import err_catcher_plugin as err_catcher
47 |
48 |
49 | class Prism_UnrealEngine_externalAccess_Functions(object):
50 | def __init__(self, core, plugin):
51 | self.core = core
52 | self.plugin = plugin
53 | self.callbacks = []
54 | # self.registerCallbacks()
55 | self._editorpath = ""
56 | self._projectpath = ""
57 |
58 | @property
59 | def editor(self):
60 | return self._editorpath
61 |
62 | @property
63 | def project(self):
64 | return self._projectpath
65 |
66 | @err_catcher(name=__name__)
67 | def setEditorPath(self, value):
68 | self._editorpath = value
69 |
70 | @err_catcher(name=__name__)
71 | def setProjectPath(self, value):
72 | self._projectpath = value
73 |
74 | @err_catcher(name=__name__)
75 | def registerCallbacks(self):
76 | self.callbacks.append(self.core.registerCallback("prismSettings_loadUI", self.prismSettings_loadUI))
77 |
78 | @err_catcher(name=__name__)
79 | def getAutobackPath(self, origin, tab):
80 | autobackpath = ""
81 | fileStr = "Unreal Engine Scene File ("
82 | for i in self.sceneFormats:
83 | fileStr += "*%s " % i
84 |
85 | fileStr += ")"
86 |
87 | return autobackpath, fileStr
88 |
89 | @err_catcher(name=__name__)
90 | def projectBrowser_loadUI(self, origin):
91 | if self.core.appPlugin.pluginName == "Standalone":
92 | psMenu = QMenu("Unreal Engine")
93 | psAction = QAction("Connect", origin)
94 | psAction.triggered.connect(lambda: self.connectToUnrealEngine(origin))
95 | psMenu.addAction(psAction)
96 | origin.menuTools.insertSeparator(origin.menuTools.actions()[-2])
97 | origin.menuTools.insertMenu(origin.menuTools.actions()[-2], psMenu)
98 |
99 | @err_catcher(name=__name__)
100 | def resolvePath(self, filepath):
101 | filepath = self.core.fixPath(filepath)
102 | result = self.core.callback(name="sc_resolvePath", types=["custom", "prjManagers"], args=[self, filepath], **{"as_depot_file": True})
103 | filepath = result if result else filepath
104 |
105 | @err_catcher(name=__name__)
106 | def browse(self, origin, getFile=False, windowTitle="Browse", fStr="All files (*)", uiEdit=None, subscribe_callable=None):
107 | browse_dir = getattr(uiEdit, "text", lambda : "")()
108 | if getFile:
109 | selectedPath = QFileDialog.getOpenFileName(
110 | origin, windowTitle, browse_dir, fStr
111 | )[0]
112 | else:
113 | selectedPath = QFolderDialog.getExistingDirectory(
114 | origin, windowTitle, browse_dir
115 | )[0]
116 |
117 | if getattr(uiEdit, "setText"):
118 | uiEdit.setText(self.resolvePath(selectedPath))
119 | if subscribe_callable:
120 | try:
121 | subscribe_callable(self.resolvePath(selectedPath))
122 | except Exception as why:
123 | logger.error(why)
124 |
125 |
126 | @err_catcher(name=__name__)
127 | def prismSettings_loadUI(self, origin, tab=None):
128 |
129 | w_ue4edior = QWidget()
130 | w_ue4edior.setLayout(QHBoxLayout())
131 | l_ue4editor = QLabel("UE4 editor path")
132 | origin.le_ue4editor = QLineEdit()
133 | origin.le_ue4editor.setText(self.editor)
134 | b_ue4editor = QPushButton("...")
135 | w_ue4edior.layout().addWidget(l_ue4editor)
136 | w_ue4edior.layout().addWidget(origin.le_ue4editor)
137 | w_ue4edior.layout().addWidget(b_ue4editor)
138 |
139 | w_ue4project = QWidget()
140 | w_ue4project.setLayout(QHBoxLayout())
141 | l_ue4project = QLabel("UE4 project path")
142 | origin.le_ue4project = QLineEdit()
143 | origin.le_ue4project.setText(self.project)
144 | b_ue4project = QPushButton("...")
145 | w_ue4project.layout().addWidget(l_ue4project)
146 | w_ue4project.layout().addWidget(origin.le_ue4project)
147 | w_ue4project.layout().addWidget(b_ue4project)
148 |
149 | # tab.layout().addWidget(w_ue4edior)
150 | tab.layout().addWidget(w_ue4project)
151 |
152 | origin.le_ue4editor.editingFinished.connect(self.setEditorPath)
153 | origin.le_ue4project.editingFinished.connect(self.setProjectPath)
154 |
155 | b_ue4editor.clicked.connect(lambda : self.browse(origin, True, "Select UE4-Editor.exe", "Executable (*.exe)", origin.le_ue4editor, self.setEditorPath))
156 | b_ue4project.clicked.connect(lambda : self.browse(origin, True, "Select Uproject file", "Uproject (*.uproject)", origin.le_ue4project, self.setProjectPath))
157 |
158 | return ""
159 |
160 | @err_catcher(name=__name__)
161 | def prismSettings_savePrjSettings(self, origin, settings):
162 | if "unrealengine" not in settings:
163 | settings["unrealengine"] = {}
164 | if hasattr(origin, "le_ue4editor"):
165 | settings["unrealengine"]["editor"] = self.editor
166 | if hasattr(origin, "le_ue4project"):
167 | settings["unrealengine"]["uproject"] = self.project
168 |
169 | @err_catcher(name=__name__)
170 | def prismSettings_loadPrjSettings(self, origin, settings):
171 | if "unrealengine" in settings:
172 | if "uproject" in settings["unrealengine"]:
173 | self.setProjectPath(settings["unrealengine"]["uproject"])
174 | if "editor" in settings["unrealengine"]:
175 | self.setEditorPath(settings["unrealengine"]["editor"])
176 |
177 | @err_catcher(name=__name__)
178 | def customizeExecutable(self, origin, appPath, filepath):
179 | # self.connectToUnrealEngine(origin, filepath=filepath)
180 | fileStarted = False
181 | # if self.core.getConfig("nuke", "usenukex"):
182 | # if appPath == "":
183 | # if not hasattr(self, "nukePath"):
184 | # self.getNukePath(origin)
185 |
186 | # if self.nukePath is not None and os.path.exists(self.nukePath):
187 | # appPath = self.nukePath
188 | # else:
189 | # QMessageBox.warning(
190 | # self.core.messageParent,
191 | # "Warning",
192 | # "Nuke executable doesn't exist:\n\n%s" % self.nukePath,
193 | # )
194 |
195 | # if appPath is not None and appPath != "":
196 | # subprocess.Popen([appPath, "--nukex", self.core.fixPath(filepath)])
197 | # fileStarted = True
198 |
199 | return False
200 |
201 | @err_catcher(name=__name__)
202 | def copySceneFile(self, origin, origFile, targetPath, mode="copy"):
203 | pass
204 |
205 | @err_catcher(name=__name__)
206 | def onProjectCreated(self, origin, projectPath, projectName):
207 | pass
208 |
209 | @err_catcher(name=__name__)
210 | def connectToUnrealEngine(self, origin, filepath=""):
211 | pass
--------------------------------------------------------------------------------
/Scripts/Prism_UnrealEngine_init.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | ####################################################
4 | #
5 | # PRISM - Pipeline for animation and VFX projects
6 | #
7 | # www.prism-pipeline.com
8 | #
9 | # contact: contact@prism-pipeline.com
10 | #
11 | ####################################################
12 | #
13 | #
14 | # Copyright (C) 2016-2020 Richard Frangenberg
15 | #
16 | # Licensed under GNU GPL-3.0-or-later
17 | #
18 | # This file is part of Prism.
19 | #
20 | # Prism is free software: you can redistribute it and/or modify
21 | # it under the terms of the GNU General Public License as published by
22 | # the Free Software Foundation, either version 3 of the License, or
23 | # (at your option) any later version.
24 | #
25 | # Prism is distributed in the hope that it will be useful,
26 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | # GNU General Public License for more details.
29 | #
30 | # You should have received a copy of the GNU General Public License
31 | # along with Prism. If not, see .
32 |
33 |
34 | from Prism_UnrealEngine_Variables import Prism_UnrealEngine_Variables
35 | from Prism_UnrealEngine_externalAccess_Functions import (
36 | Prism_UnrealEngine_externalAccess_Functions,
37 | )
38 | from Prism_UnrealEngine_Functions import Prism_UnrealEngine_Functions
39 | from Prism_UnrealEngine_Integration import Prism_UnrealEngine_Integration
40 |
41 |
42 | class Prism_Plugin_UnrealEngine(
43 | Prism_UnrealEngine_Variables,
44 | Prism_UnrealEngine_externalAccess_Functions,
45 | Prism_UnrealEngine_Functions,
46 | Prism_UnrealEngine_Integration,
47 | ):
48 | def __init__(self, core):
49 | Prism_UnrealEngine_Variables.__init__(self, core, self)
50 | Prism_UnrealEngine_externalAccess_Functions.__init__(self, core, self)
51 | Prism_UnrealEngine_Functions.__init__(self, core, self)
52 | Prism_UnrealEngine_Integration.__init__(self, core, self)
53 |
--------------------------------------------------------------------------------
/Scripts/Prism_UnrealEngine_init_unloaded.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | ####################################################
4 | #
5 | # PRISM - Pipeline for animation and VFX projects
6 | #
7 | # www.prism-pipeline.com
8 | #
9 | # contact: contact@prism-pipeline.com
10 | #
11 | ####################################################
12 | #
13 | #
14 | # Copyright (C) 2016-2020 Richard Frangenberg
15 | #
16 | # Licensed under GNU GPL-3.0-or-later
17 | #
18 | # This file is part of Prism.
19 | #
20 | # Prism is free software: you can redistribute it and/or modify
21 | # it under the terms of the GNU General Public License as published by
22 | # the Free Software Foundation, either version 3 of the License, or
23 | # (at your option) any later version.
24 | #
25 | # Prism is distributed in the hope that it will be useful,
26 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
27 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 | # GNU General Public License for more details.
29 | #
30 | # You should have received a copy of the GNU General Public License
31 | # along with Prism. If not, see .
32 |
33 |
34 | from Prism_UnrealEngine_Variables import Prism_UnrealEngine_Variables
35 | from Prism_UnrealEngine_externalAccess_Functions import (
36 | Prism_UnrealEngine_externalAccess_Functions,
37 | )
38 | from Prism_UnrealEngine_Integration import Prism_UnrealEngine_Integration
39 |
40 |
41 | class Prism_UnrealEngine_unloaded(
42 | Prism_UnrealEngine_Variables,
43 | Prism_UnrealEngine_externalAccess_Functions,
44 | Prism_UnrealEngine_Integration,
45 | ):
46 | def __init__(self, core):
47 | Prism_UnrealEngine_Variables.__init__(self, core, self)
48 | Prism_UnrealEngine_externalAccess_Functions.__init__(self, core, self)
49 | Prism_UnrealEngine_Integration.__init__(self, core, self)
50 |
--------------------------------------------------------------------------------
/external_modules/remote_execution.py:
--------------------------------------------------------------------------------
1 | # Copyright Epic Games, Inc. All Rights Reserved.
2 |
3 | import sys as _sys
4 | import json as _json
5 | import uuid as _uuid
6 | import time as _time
7 | import socket as _socket
8 | import logging as _logging
9 | import threading as _threading
10 |
11 | def hello():
12 | _logging.debug("Hello from remote")
13 |
14 | # Protocol constants (see PythonScriptRemoteExecution.cpp for the full protocol definition)
15 | _PROTOCOL_VERSION = 1 # Protocol version number
16 | _PROTOCOL_MAGIC = 'ue_py' # Protocol magic identifier
17 | _TYPE_PING = 'ping' # Service discovery request (UDP)
18 | _TYPE_PONG = 'pong' # Service discovery response (UDP)
19 | _TYPE_OPEN_CONNECTION = 'open_connection' # Open a TCP command connection with the requested server (UDP)
20 | _TYPE_CLOSE_CONNECTION = 'close_connection' # Close any active TCP command connection (UDP)
21 | _TYPE_COMMAND = 'command' # Execute a remote Python command (TCP)
22 | _TYPE_COMMAND_RESULT = 'command_result' # Result of executing a remote Python command (TCP)
23 |
24 | _NODE_PING_SECONDS = 1 # Number of seconds to wait before sending another "ping" message to discover remote notes
25 | _NODE_TIMEOUT_SECONDS = 5 # Number of seconds to wait before timing out a remote node that was discovered via UDP and has stopped sending "pong" responses
26 |
27 | DEFAULT_MULTICAST_TTL = 0 # Multicast TTL (0 is limited to the local host, 1 is limited to the local subnet)
28 | DEFAULT_MULTICAST_GROUP_ENDPOINT = ('239.0.0.1',
29 | 6766) # The multicast group endpoint tuple that the UDP multicast socket should join (must match the "Multicast Group Endpoint" setting in the Python plugin)
30 | DEFAULT_MULTICAST_BIND_ADDRESS = '0.0.0.0' # The adapter address that the UDP multicast socket should bind to, or 0.0.0.0 to bind to all adapters (must match the "Multicast Bind Address" setting in the Python plugin)
31 | DEFAULT_COMMAND_ENDPOINT = ('127.0.0.1',
32 | 6776) # The endpoint tuple for the TCP command connection hosted by this client (that the remote client will connect to)
33 |
34 | # Execution modes (these must match the names given to LexToString for EPythonCommandExecutionMode in IPythonScriptPlugin.h)
35 | MODE_EXEC_FILE = 'ExecuteFile' # Execute the Python command as a file. This allows you to execute either a literal Python script containing multiple statements, or a file with optional arguments
36 | MODE_EXEC_STATEMENT = 'ExecuteStatement' # Execute the Python command as a single statement. This will execute a single statement and print the result. This mode cannot run files
37 | MODE_EVAL_STATEMENT = 'EvaluateStatement' # Evaluate the Python command as a single statement. This will evaluate a single statement and return the result. This mode cannot run files
38 |
39 |
40 | class RemoteExecutionConfig(object):
41 | '''
42 | Configuration data for establishing a remote connection with a UE4 instance running Python.
43 | '''
44 |
45 | def __init__(self):
46 | self.multicast_ttl = DEFAULT_MULTICAST_TTL
47 | self.multicast_group_endpoint = DEFAULT_MULTICAST_GROUP_ENDPOINT
48 | self.multicast_bind_address = DEFAULT_MULTICAST_BIND_ADDRESS
49 | self.command_endpoint = DEFAULT_COMMAND_ENDPOINT
50 |
51 |
52 | class RemoteExecution(object):
53 | '''
54 | A remote execution session. This class can discover remote "nodes" (UE4 instances running Python), and allow you to open a command channel to a particular instance.
55 |
56 | Args:
57 | config (RemoteExecutionConfig): Configuration controlling the connection settings for this session.
58 | '''
59 |
60 | def __init__(self, config=RemoteExecutionConfig()):
61 | self._config = config
62 | self._broadcast_connection = None
63 | self._command_connection = None
64 | self._node_id = str(_uuid.uuid4())
65 |
66 | @property
67 | def remote_nodes(self):
68 | '''
69 | Get the current set of discovered remote "nodes" (UE4 instances running Python).
70 |
71 | Returns:
72 | list: A list of dicts containg the node ID and the other data.
73 | '''
74 | return self._broadcast_connection.remote_nodes if self._broadcast_connection else []
75 |
76 | def start(self):
77 | '''
78 | Start the remote execution session. This will begin the discovey process for remote "nodes" (UE4 instances running Python).
79 | '''
80 | self._broadcast_connection = _RemoteExecutionBroadcastConnection(self._config, self._node_id)
81 | self._broadcast_connection.open()
82 |
83 | def stop(self):
84 | '''
85 | Stop the remote execution session. This will end the discovey process for remote "nodes" (UE4 instances running Python), and close any open command connection.
86 | '''
87 | self.close_command_connection()
88 | if self._broadcast_connection:
89 | self._broadcast_connection.close()
90 | self._broadcast_connection = None
91 |
92 | def has_command_connection(self):
93 | '''
94 | Check whether the remote execution session has an active command connection.
95 |
96 | Returns:
97 | bool: True if the remote execution session has an active command connection, False otherwise.
98 | '''
99 | return self._command_connection is not None
100 |
101 | def open_command_connection(self, remote_node_id):
102 | '''
103 | Open a command connection to the given remote "node" (a UE4 instance running Python), closing any command connection that may currently be open.
104 |
105 | Args:
106 | remote_node_id (string): The ID of the remote node (this can be obtained by querying `remote_nodes`).
107 | '''
108 | self._command_connection = _RemoteExecutionCommandConnection(self._config, self._node_id, remote_node_id)
109 | self._command_connection.open(self._broadcast_connection)
110 |
111 | def close_command_connection(self):
112 | '''
113 | Close any command connection that may currently be open.
114 | '''
115 | if self._command_connection:
116 | self._command_connection.close(self._broadcast_connection)
117 | self._command_connection = None
118 |
119 | def run_command(self, command, unattended=True, exec_mode=MODE_EXEC_FILE, raise_on_failure=False):
120 | '''
121 | Run a command remotely based on the current command connection.
122 |
123 | Args:
124 | command (string): The Python command to run remotely.
125 | unattended (bool): True to run this command in "unattended" mode (suppressing some UI).
126 | exec_mode (string): The execution mode to use as a string value (must be one of MODE_EXEC_FILE, MODE_EXEC_STATEMENT, or MODE_EVAL_STATEMENT).
127 | raise_on_failure (bool): True to raise a RuntimeError if the command fails on the remote target.
128 |
129 | Returns:
130 | dict: The result from running the remote command (see `command_result` from the protocol definition).
131 | '''
132 | data = self._command_connection.run_command(command, unattended, exec_mode)
133 | if raise_on_failure and not data['success']:
134 | raise RuntimeError('Remote Python Command failed! {0}'.format(data['result']))
135 | return data
136 |
137 |
138 | class _RemoteExecutionNode(object):
139 | '''
140 | A discovered remote "node" (aka, a UE4 instance running Python).
141 |
142 | Args:
143 | data (dict): The data representing this node (from its "pong" reponse).
144 | now (float): The timestamp at which this node was last seen.
145 | '''
146 |
147 | def __init__(self, data, now=None):
148 | self.data = data
149 | self._last_pong = _time_now(now)
150 |
151 | def should_timeout(self, now=None):
152 | '''
153 | Check to see whether this remote node should be considered timed-out.
154 |
155 | Args:
156 | now (float): The current timestamp.
157 |
158 | Returns:
159 | bool: True of the node has exceeded the timeout limit (`_NODE_TIMEOUT_SECONDS`), False otherwise.
160 | '''
161 | return (self._last_pong + _NODE_TIMEOUT_SECONDS) < _time_now(now)
162 |
163 |
164 | class _RemoteExecutionBroadcastNodes(object):
165 | '''
166 | A thread-safe set of remote execution "nodes" (UE4 instances running Python).
167 | '''
168 |
169 | def __init__(self):
170 | self._remote_nodes = {}
171 | self._remote_nodes_lock = _threading.RLock()
172 |
173 | @property
174 | def remote_nodes(self):
175 | '''
176 | Get the current set of discovered remote "nodes" (UE4 instances running Python).
177 |
178 | Returns:
179 | list: A list of dicts containg the node ID and the other data.
180 | '''
181 | with self._remote_nodes_lock:
182 | remote_nodes_list = []
183 | for node_id, node in self._remote_nodes.items():
184 | remote_node_data = dict(node.data)
185 | remote_node_data['node_id'] = node_id
186 | remote_nodes_list.append(remote_node_data)
187 | return remote_nodes_list
188 |
189 | def update_remote_node(self, node_id, node_data, now=None):
190 | '''
191 | Update a remote node, replacing any existing data.
192 |
193 | Args:
194 | node_id (str): The ID of the remote node (from its "pong" reponse).
195 | node_data (dict): The data representing this node (from its "pong" reponse).
196 | now (float): The timestamp at which this node was last seen.
197 | '''
198 | now = _time_now(now)
199 | with self._remote_nodes_lock:
200 | if node_id not in self._remote_nodes:
201 | _logger.debug('Found Node {0}: {1}'.format(node_id, node_data))
202 | self._remote_nodes[node_id] = _RemoteExecutionNode(node_data, now)
203 |
204 | def timeout_remote_nodes(self, now=None):
205 | '''
206 | Check to see whether any remote nodes should be considered timed-out, and if so, remove them from this set.
207 |
208 | Args:
209 | now (float): The current timestamp.
210 | '''
211 | now = _time_now(now)
212 | with self._remote_nodes_lock:
213 | for node_id, node in list(self._remote_nodes.items()):
214 | if node.should_timeout(now):
215 | _logger.debug('Lost Node {0}: {1}'.format(node_id, node.data))
216 | del self._remote_nodes[node_id]
217 |
218 |
219 | class _RemoteExecutionBroadcastConnection(object):
220 | '''
221 | A remote execution broadcast connection (for UDP based messaging and node discovery).
222 |
223 | Args:
224 | config (RemoteExecutionConfig): Configuration controlling the connection settings.
225 | node_id (string): The ID of the local "node" (this session).
226 | '''
227 |
228 | def __init__(self, config, node_id):
229 | self._config = config
230 | self._node_id = node_id
231 | self._nodes = None
232 | self._running = False
233 | self._broadcast_socket = None
234 | self._broadcast_listen_thread = None
235 |
236 | @property
237 | def remote_nodes(self):
238 | '''
239 | Get the current set of discovered remote "nodes" (UE4 instances running Python).
240 |
241 | Returns:
242 | list: A list of dicts containg the node ID and the other data.
243 | '''
244 | return self._nodes.remote_nodes if self._nodes else []
245 |
246 | def open(self):
247 | '''
248 | Open the UDP based messaging and discovery connection. This will begin the discovey process for remote "nodes" (UE4 instances running Python).
249 | '''
250 | self._running = True
251 | self._last_ping = None
252 | self._nodes = _RemoteExecutionBroadcastNodes()
253 | self._init_broadcast_socket()
254 | self._init_broadcast_listen_thread()
255 |
256 | def close(self):
257 | '''
258 | Close the UDP based messaging and discovery connection. This will end the discovey process for remote "nodes" (UE4 instances running Python).
259 | '''
260 | self._running = False
261 | if self._broadcast_listen_thread:
262 | self._broadcast_listen_thread.join()
263 | if self._broadcast_socket:
264 | self._broadcast_socket.close()
265 | self._broadcast_socket = None
266 | self._nodes = None
267 |
268 | def _init_broadcast_socket(self):
269 | '''
270 | Initialize the UDP based broadcast socket based on the current configuration.
271 | '''
272 | self._broadcast_socket = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM,
273 | _socket.IPPROTO_UDP) # UDP/IP socket
274 | if hasattr(_socket, 'SO_REUSEPORT'):
275 | self._broadcast_socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEPORT, 1)
276 | else:
277 | self._broadcast_socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1)
278 | self._broadcast_socket.bind((self._config.multicast_bind_address, self._config.multicast_group_endpoint[1]))
279 | self._broadcast_socket.setsockopt(_socket.IPPROTO_IP, _socket.IP_MULTICAST_LOOP, 1)
280 | self._broadcast_socket.setsockopt(_socket.IPPROTO_IP, _socket.IP_MULTICAST_TTL, self._config.multicast_ttl)
281 | self._broadcast_socket.setsockopt(_socket.IPPROTO_IP, _socket.IP_ADD_MEMBERSHIP, _socket.inet_aton(
282 | self._config.multicast_group_endpoint[0]) + _socket.inet_aton('0.0.0.0'))
283 | self._broadcast_socket.settimeout(0.1)
284 |
285 | def _init_broadcast_listen_thread(self):
286 | '''
287 | Initialize the listen thread for the UDP based broadcast socket to allow discovery to run async.
288 | '''
289 | self._broadcast_listen_thread = _threading.Thread(target=self._run_broadcast_listen_thread)
290 | self._broadcast_listen_thread.daemon = True
291 | self._broadcast_listen_thread.start()
292 |
293 | def _run_broadcast_listen_thread(self):
294 | '''
295 | Main loop for the listen thread that handles processing discovery messages.
296 | '''
297 | while self._running:
298 | # Receive and process all pending data
299 | while True:
300 | try:
301 | data = self._broadcast_socket.recv(4096)
302 | except _socket.timeout:
303 | data = None
304 | if data:
305 | self._handle_data(data)
306 | else:
307 | break
308 | # Run tick logic
309 | now = _time_now()
310 | self._broadcast_ping(now)
311 | self._nodes.timeout_remote_nodes(now)
312 | _time.sleep(0.1)
313 |
314 | def _broadcast_message(self, message):
315 | '''
316 | Broadcast the given message over the UDP socket to anything that might be listening.
317 |
318 | Args:
319 | message (_RemoteExecutionMessage): The message to broadcast.
320 | '''
321 | self._broadcast_socket.sendto(message.to_json_bytes(), self._config.multicast_group_endpoint)
322 |
323 | def _broadcast_ping(self, now=None):
324 | '''
325 | Broadcast a "ping" message over the UDP socket to anything that might be listening.
326 |
327 | Args:
328 | now (float): The current timestamp.
329 | '''
330 | now = _time_now(now)
331 | if not self._last_ping or ((self._last_ping + _NODE_PING_SECONDS) < now):
332 | self._last_ping = now
333 | self._broadcast_message(_RemoteExecutionMessage(_TYPE_PING, self._node_id))
334 |
335 | def broadcast_open_connection(self, remote_node_id):
336 | '''
337 | Broadcast an "open_connection" message over the UDP socket to be handled by the specified remote node.
338 |
339 | Args:
340 | remote_node_id (string): The ID of the remote node that we want to open a command connection with.
341 | '''
342 | self._broadcast_message(_RemoteExecutionMessage(_TYPE_OPEN_CONNECTION, self._node_id, remote_node_id, {
343 | 'command_ip': self._config.command_endpoint[0],
344 | 'command_port': self._config.command_endpoint[1],
345 | }))
346 |
347 | def broadcast_close_connection(self, remote_node_id):
348 | '''
349 | Broadcast a "close_connection" message over the UDP socket to be handled by the specified remote node.
350 |
351 | Args:
352 | remote_node_id (string): The ID of the remote node that we want to close a command connection with.
353 | '''
354 | self._broadcast_message(_RemoteExecutionMessage(_TYPE_CLOSE_CONNECTION, self._node_id, remote_node_id))
355 |
356 | def _handle_data(self, data):
357 | '''
358 | Handle data received from the UDP broadcast socket.
359 |
360 | Args:
361 | data (bytes): The raw bytes received from the socket.
362 | '''
363 | message = _RemoteExecutionMessage(None, None)
364 | if message.from_json_bytes(data):
365 | self._handle_message(message)
366 |
367 | def _handle_message(self, message):
368 | '''
369 | Handle a message received from the UDP broadcast socket.
370 |
371 | Args:
372 | message (_RemoteExecutionMessage): The message received from the socket.
373 | '''
374 | if not message.passes_receive_filter(self._node_id):
375 | return
376 | if message.type_ == _TYPE_PONG:
377 | self._handle_pong_message(message)
378 | return
379 | _logger.debug('Unhandled remote execution message type "{0}"'.format(message.type_))
380 |
381 | def _handle_pong_message(self, message):
382 | '''
383 | Handle a "pong" message received from the UDP broadcast socket.
384 |
385 | Args:
386 | message (_RemoteExecutionMessage): The message received from the socket.
387 | '''
388 | self._nodes.update_remote_node(message.source, message.data)
389 |
390 |
391 | class _RemoteExecutionCommandConnection(object):
392 | '''
393 | A remote execution command connection (for TCP based command processing).
394 |
395 | Args:
396 | config (RemoteExecutionConfig): Configuration controlling the connection settings.
397 | node_id (string): The ID of the local "node" (this session).
398 | remote_node_id (string): The ID of the remote "node" (the UE4 instance running Python).
399 | '''
400 |
401 | def __init__(self, config, node_id, remote_node_id):
402 | self._config = config
403 | self._node_id = node_id
404 | self._remote_node_id = remote_node_id
405 | self._command_listen_socket = None
406 | self._command_channel_socket = _socket.socket() # This type is only here to appease PyLint
407 |
408 | def open(self, broadcast_connection):
409 | '''
410 | Open the TCP based command connection, and wait to accept the connection from the remote party.
411 |
412 | Args:
413 | broadcast_connection (_RemoteExecutionBroadcastConnection): The broadcast connection to send UDP based messages over.
414 | '''
415 | self._nodes = _RemoteExecutionBroadcastNodes()
416 | self._init_command_listen_socket()
417 | self._try_accept(broadcast_connection)
418 |
419 | def close(self, broadcast_connection):
420 | '''
421 | Close the TCP based command connection, attempting to notify the remote party.
422 |
423 | Args:
424 | broadcast_connection (_RemoteExecutionBroadcastConnection): The broadcast connection to send UDP based messages over.
425 | '''
426 | broadcast_connection.broadcast_close_connection(self._remote_node_id)
427 | if self._command_channel_socket:
428 | self._command_channel_socket.close()
429 | self._command_channel_socket = None
430 | if self._command_listen_socket:
431 | self._command_listen_socket.close()
432 | self._command_listen_socket = None
433 |
434 | def run_command(self, command, unattended, exec_mode):
435 | '''
436 | Run a command on the remote party.
437 |
438 | Args:
439 | command (string): The Python command to run remotely.
440 | unattended (bool): True to run this command in "unattended" mode (suppressing some UI).
441 | exec_mode (string): The execution mode to use as a string value (must be one of MODE_EXEC_FILE, MODE_EXEC_STATEMENT, or MODE_EVAL_STATEMENT).
442 |
443 | Returns:
444 | dict: The result from running the remote command (see `command_result` from the protocol definition).
445 | '''
446 | self._send_message(_RemoteExecutionMessage(_TYPE_COMMAND, self._node_id, self._remote_node_id, {
447 | 'command': command,
448 | 'unattended': unattended,
449 | 'exec_mode': exec_mode,
450 | }))
451 | result = self._receive_message(_TYPE_COMMAND_RESULT)
452 | return result.data
453 |
454 | def _send_message(self, message):
455 | '''
456 | Send the given message over the TCP socket to the remote party.
457 |
458 | Args:
459 | message (_RemoteExecutionMessage): The message to send.
460 | '''
461 | self._command_channel_socket.sendall(message.to_json_bytes())
462 |
463 | def _receive_message(self, expected_type):
464 | '''
465 | Receive a message over the TCP socket from the remote party.
466 |
467 | Args:
468 | expected_type (string): The type of message we expect to receive.
469 |
470 | Returns:
471 | The message that was received.
472 | '''
473 | data = self._command_channel_socket.recv(4096)
474 | if data:
475 | message = _RemoteExecutionMessage(None, None)
476 | if message.from_json_bytes(data) and message.passes_receive_filter(
477 | self._node_id) and message.type_ == expected_type:
478 | return message
479 | raise RuntimeError('Remote party failed to send a valid response!')
480 |
481 | def _init_command_listen_socket(self):
482 | '''
483 | Initialize the TCP based command socket based on the current configuration, and set it to listen for an incoming connection.
484 | '''
485 | self._command_listen_socket = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM,
486 | _socket.IPPROTO_TCP) # TCP/IP socket
487 | if hasattr(_socket, 'SO_REUSEPORT'):
488 | self._command_listen_socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEPORT, 1)
489 | else:
490 | self._command_listen_socket.setsockopt(_socket.SOL_SOCKET, _socket.SO_REUSEADDR, 1)
491 | self._command_listen_socket.bind(self._config.command_endpoint)
492 | self._command_listen_socket.listen(1)
493 | self._command_listen_socket.settimeout(5)
494 |
495 | def _try_accept(self, broadcast_connection):
496 | '''
497 | Wait to accept a connection on the TCP based command connection. This makes 6 attempts to receive a connection, waiting for 5 seconds between each attempt (30 seconds total).
498 |
499 | Args:
500 | broadcast_connection (_RemoteExecutionBroadcastConnection): The broadcast connection to send UDP based messages over.
501 | '''
502 | for _n in range(6):
503 | broadcast_connection.broadcast_open_connection(self._remote_node_id)
504 | try:
505 | self._command_channel_socket = self._command_listen_socket.accept()[0]
506 | self._command_channel_socket.setblocking(True)
507 | return
508 | except _socket.timeout:
509 | continue
510 | raise RuntimeError('Remote party failed to attempt the command socket connection!')
511 |
512 |
513 | class _RemoteExecutionMessage(object):
514 | '''
515 | A message sent or received by remote execution (on either the UDP or TCP connection), as UTF-8 encoded JSON.
516 |
517 | Args:
518 | type_ (string): The type of this message (see the `_TYPE_` constants).
519 | source (string): The ID of the node that sent this message.
520 | dest (string): The ID of the destination node of this message, or None to send to all nodes (for UDP broadcast).
521 | data (dict): The message specific payload data.
522 | '''
523 |
524 | def __init__(self, type_, source, dest=None, data=None):
525 | self.type_ = type_
526 | self.source = source
527 | self.dest = dest
528 | self.data = data
529 |
530 | def passes_receive_filter(self, node_id):
531 | '''
532 | Test to see whether this message should be received by the current node (wasn't sent to itself, and has a compatible destination ID).
533 |
534 | Args:
535 | node_id (string): The ID of the local "node" (this session).
536 |
537 | Returns:
538 | bool: True if this message should be received by the current node, False otherwise.
539 | '''
540 | return self.source != node_id and (not self.dest or self.dest == node_id)
541 |
542 | def to_json(self):
543 | '''
544 | Convert this message to its JSON representation.
545 |
546 | Returns:
547 | str: The JSON representation of this message.
548 | '''
549 | if not self.type_:
550 | raise ValueError('"type" cannot be empty!')
551 | if not self.source:
552 | raise ValueError('"source" cannot be empty!')
553 | json_obj = {
554 | 'version': _PROTOCOL_VERSION,
555 | 'magic': _PROTOCOL_MAGIC,
556 | 'type': self.type_,
557 | 'source': self.source,
558 | }
559 | if self.dest:
560 | json_obj['dest'] = self.dest
561 | if self.data:
562 | json_obj['data'] = self.data
563 | return _json.dumps(json_obj, ensure_ascii=False)
564 |
565 | def to_json_bytes(self):
566 | '''
567 | Convert this message to its JSON representation as UTF-8 bytes.
568 |
569 | Returns:
570 | bytes: The JSON representation of this message as UTF-8 bytes.
571 | '''
572 | json_str = self.to_json()
573 | return json_str.encode('utf-8')
574 |
575 | def from_json(self, json_str):
576 | '''
577 | Parse this message from its JSON representation.
578 |
579 | Args:
580 | json_str (str): The JSON representation of this message.
581 |
582 | Returns:
583 | bool: True if this message could be parsed, False otherwise.
584 | '''
585 | try:
586 | json_obj = _json.loads(json_str, encoding='utf-8')
587 | # Read and validate required protocol version information
588 | if json_obj['version'] != _PROTOCOL_VERSION:
589 | raise ValueError(
590 | '"version" is incorrect (got {0}, expected {1})!'.format(json_obj['version'], _PROTOCOL_VERSION))
591 | if json_obj['magic'] != _PROTOCOL_MAGIC:
592 | raise ValueError(
593 | '"magic" is incorrect (got "{0}", expected "{1}")!'.format(json_obj['magic'], _PROTOCOL_MAGIC))
594 | # Read required fields
595 | local_type = json_obj['type']
596 | local_source = json_obj['source']
597 | self.type_ = local_type
598 | self.source = local_source
599 | # Read optional fields
600 | self.dest = json_obj.get('dest')
601 | self.data = json_obj.get('data')
602 | except Exception as e:
603 | _logger.error('Failed to deserialize JSON "{0}": {1}'.format(json_str, str(e)))
604 | return False
605 | return True
606 |
607 | def from_json_bytes(self, json_bytes):
608 | '''
609 | Parse this message from its JSON representation as UTF-8 bytes.
610 |
611 | Args:
612 | json_bytes (bytes): The JSON representation of this message as UTF-8 bytes.
613 |
614 | Returns:
615 | bool: True if this message could be parsed, False otherwise.
616 | '''
617 | json_str = json_bytes.decode('utf-8')
618 | return self.from_json(json_str)
619 |
620 |
621 | def _time_now(now=None):
622 | '''
623 | Utility function to resolve a potentially cached time value.
624 |
625 | Args:
626 | now (float): The cached timestamp, or None to return the current time.
627 |
628 | Returns:
629 | float: The cached timestamp (if set), otherwise the current time.
630 | '''
631 | return _time.time() if now is None else now
632 |
633 |
634 | # Log handling
635 | _logger = _logging.getLogger(__name__)
636 | _log_handler = _logging.StreamHandler()
637 | _logger.addHandler(_log_handler)
638 |
639 |
640 | def set_log_level(log_level):
641 | _logger.setLevel(log_level)
642 | _log_handler.setLevel(log_level)
643 |
--------------------------------------------------------------------------------
/external_modules/ue4.py:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/python
2 | # -*- coding: utf-8 -*-
3 | import os
4 | import glob
5 | from os.path import basename, dirname, splitext
6 | from subprocess import Popen
7 | import json
8 | from functools import partial
9 |
10 | def get_temp_path():
11 | p = os.path.join(os.getenv("TMP"), os.path.splitext(os.path.basename(__file__))[0])
12 | if not os.path.exists(p):
13 | os.makedirs(p)
14 | return p
15 |
16 |
17 | class RenderOutputFormat(object):
18 | JPG = "jpg"
19 | BMP = "bmp"
20 | PNG = "png"
21 | Video = "video"
22 |
23 | class ImportData(object):
24 | def __init__(self):
25 | # self.ImportTranslation= [0,0,0]
26 | # self.ImportRotation = [0,0,0]
27 | # self.ImportUniformScale = [1,1,1]
28 | self.bConvertScene = True
29 | self.bConvertSceneUnit = True
30 | self.bConvertAsScene = True
31 |
32 | class StaticMeshImportData(ImportData):
33 | def __init__(self):
34 | ImportData.__init__(self)
35 | self.bRemoveDegenerates = True
36 | self.bBuildAdjacencyBuffer = True
37 | self.bBuildReversedIndexBuffer = False
38 | self.bGenerateLightmapUVs = False
39 | self.bOneConvexHullPerUCX = True
40 | self.bAutoGenerateCollision = False
41 | self.bCombineMeshes = False
42 |
43 | class SkeletalMeshImportData(ImportData):
44 | TargetSkeleton = ""
45 | def __init__(self):
46 | ImportData.__init__(self)
47 | self.bUpdateSkeletonReferencePose = True
48 | self.bUseT0AsRefPose = True
49 | self.bPreserveSmoothingGroups = True
50 | self.bImportMeshesInBoneHierarchy = False
51 | self.bImportMorphTargets = True
52 | self.bKeepOverlappingVertices = True
53 |
54 |
55 | class AnimSequenceImportData(ImportData):
56 | TargetSkeleton = ""
57 | def __init__(self):
58 | ImportData.__init__(self)
59 | self.bImportCustomAttribute = True
60 | self.bDeleteExistingCustomAttributeCurves = True
61 | self.bDeleteExistingNonCurveCustomAttributes = True
62 | self.bImportBoneTracks = True
63 | self.bSetMaterialDriveParameterOnCustomAttribute = True
64 | self.bRemoveRedundantKeys = False
65 | self.bDeleteExistingMorphTargetCurves = False
66 | self.bDoNotImportCurveWithZero = True
67 | self.bPreserveLocalTransform = False
68 |
69 | class TextureImportData(ImportData):
70 | def __init__(self):
71 | self.bInvertNormalMaps = True
72 |
73 | class FBXImportSettings(object):
74 | """
75 | ImportSettings in Setting Group is UFbxImportUI:
76 | bIsObjImport /** Whether or not the imported file is in OBJ format */
77 | OriginalImportType /** The original detected type of this import */
78 | MeshTypeToImport /** Type of asset to import from the FBX file */
79 | bOverrideFullName : true /** Use the string in "Name" field as full name of mesh. The option only works when the scene contains one mesh. */
80 | bImportAsSkeletal /** Whether to import the incoming FBX as a skeletal object */
81 | bImportMesh /** Whether to import the incoming FBX as a Subdivision Surface (could be made a combo box together with bImportAsSkeletal) (Experimental, Early work in progress) */
82 | /** Whether to import the mesh. Allows animation only import when importing a skeletal mesh. */
83 | Skeleton /** Skeleton to use for imported asset. When importing a mesh, leaving this as "None" will create a new skeleton. When importing an animation this MUST be specified to import the asset. */
84 | bCreatePhysicsAsset /** If checked, create new PhysicsAsset if it doesn't have it */
85 | bAutoComputeLodDistances ** If checked, the editor will automatically compute screen size values for the static mesh's LODs. If unchecked, the user can enter custom screen size values for each LOD. */
86 | LodDistance0 /** Set a screen size value for LOD 0*/
87 | LodDistance1 /** Set a screen size value for LOD 1*/
88 | LodDistance2 /** Set a screen size value for LOD 2*/
89 | LodDistance3 /** Set a screen size value for LOD 3*/
90 | LodDistance4 /** Set a screen size value for LOD 4*/
91 | LodDistance5 /** Set a screen size value for LOD 5*/
92 | LodDistance6 /** Set a screen size value for LOD 6*/
93 | LodDistance7 /** Set a screen size value for LOD 7*/
94 | MinimumLodNumber /** Set the minimum LOD used for rendering. Setting the value to 0 will use the default value of LOD0. */
95 | LodNumber /** Set the number of LODs for the editor to import. Setting the value to 0 imports the number of LODs found in the file (up to the maximum). */
96 | bImportAnimations /** True to import animations from the FBX File */
97 | OverrideAnimationName /** Override for the name of the animation to import. By default, it will be the name of FBX **/
98 | bImportRigidMesh /** Enables importing of 'rigid skeletalmesh' (unskinned, hierarchy-based animation) from this FBX file, no longer shown, used behind the scenes */
99 | bImportMaterials /** If no existing materials are found, whether to automatically create Unreal materials for materials found in the FBX scene */
100 | bImportTextures /** Whether or not we should import textures. This option is disabled when we are importing materials because textures are always imported in that case. */
101 | bResetToFbxOnMaterialConflict /** If true, the imported material sections will automatically be reset to the imported data in case of a reimport conflict. */
102 | StaticMeshImportData /** Import data used when importing static meshes */
103 | SkeletalMeshImportData /** Import data used when importing skeletal meshes */
104 | AnimSequenceImportData /** Import data used when importing animations */
105 | TextureImportData /** Import data used when importing textures */
106 | bAutomatedImportShouldDetectType /** If true the automated import path should detect the import type. If false the import type was specified by the user */
107 | bIsReimport
108 | bAllowContentTypeImport
109 | """
110 | def __init__(self):
111 | self.settings = dict(ImportGroups = [])
112 | self._groups = []
113 |
114 | def _add_group(self, setting_group):
115 | self.settings["ImportGroups"].append(setting_group)
116 |
117 | def addGroup(self, group_name, files_path, destination, import_setting, is_reimport=False):
118 | if not issubclass(import_setting.__class__, ImportData):
119 | print("import_setting input should be of class ImportSetting")
120 | setting_group = {
121 | "GroupName": group_name,
122 | "Filenames": files_path,
123 | "DestinationPath": destination,
124 | "bReplaceExisting": True,
125 | "bSkipReadOnly": True,
126 | "FactoryName": "FbxFactory",
127 | "ImportSettings": {}
128 | }
129 | raw_setting = vars(import_setting)
130 | if hasattr(import_setting, "TargetSkeleton"):
131 | setting_group["ImportSettings"]["Skeleton"] = str(import_setting.TargetSkeleton)
132 | raw_setting.pop("TargetSkeleton")
133 | if is_reimport:
134 | setting_group["ImportSettings"]["bIsReimport"] = True
135 | setting_group["ImportSettings"][import_setting.__class__.__name__] = raw_setting
136 |
137 | self._add_group(setting_group)
138 | return setting_group
139 |
140 | def getGroup(self, group_name):
141 | for setting in self.settings["ImportGroups"]:
142 | if setting["GroupName"] == group_name:
143 | return setting
144 |
145 | def asJson(self, filepath=""):
146 | if not filepath:
147 | import datetime
148 | filepath = os.path.join(os.getenv("TMP"), "UE4CMD", "importsetting.{}.json".format(datetime.datetime.now().strftime("%d%m%Y%H%M%S")))
149 | try:
150 | os.makedirs(os.path.dirname(filepath))
151 | except:
152 | pass
153 | try:
154 | with open(filepath, "w+") as f:
155 | json.dump(self.settings, f, sort_keys=True, indent=4, separators=(',', ': '))
156 | except:
157 | print("Failed to export setting as json")
158 | return filepath
159 |
160 | class NoEditorException(Exception):
161 | pass
162 |
163 | class NoProjectException(Exception):
164 | pass
165 |
166 |
167 | class Unreal4Config(object):
168 | pass
169 |
170 | class UserEngine(Unreal4Config):
171 | content = r"""
172 | [/Script/PythonScriptPlugin.PythonScriptPluginSettings]
173 | {}
174 | bDeveloperMode=True
175 | bRemoteExecution=True
176 | RemoteExecutionMulticastGroupEndpoint={}
177 | RemoteExecutionMulticastBindAddress={}
178 | RemoteExecutionSendBufferSizeBytes={}
179 | RemoteExecutionReceiveBufferSizeBytes={}
180 | RemoteExecutionMulticastTtl=0
181 | """
182 | multicastendpoint = "239.0.0.1:6766"
183 | multicastbindaddress = "0.0.0.0"
184 | SendBufferSize = 2097152
185 | ReceiveBufferSize = 2097152
186 | def __init__(self):
187 | self.startupScript = ""
188 | self.AdditionalPaths = []
189 |
190 | def setStarupScript(self, startupscript_path):
191 | if os.path.exists(startupscript_path) and startupscript_path.endswith(".py"):
192 | self.startupScript = "+StartupScripts={}".format(startupscript_path)
193 |
194 | def addAdditionalPythonPath(self, python_path):
195 | if os.path.exists(python_path):
196 | self.AdditionalPaths.append(r"+AdditionalPaths=(Path=\"{}\")".format(python_path))
197 |
198 | def saveAsConfig(self, outputpath=""):
199 | script_path = []
200 | if self.startupScript:
201 | script_path.append(self.startupScript)
202 | if self.AdditionalPaths:
203 | script_path.extend(self.AdditionalPaths)
204 | script_path = "\n".join(script_path)
205 | content = self.content.format(script_path, self.multicastendpoint, self.multicastbindaddress, self.SendBufferSize, self.ReceiveBufferSize)
206 | config_path = outputpath
207 | if not config_path:
208 | config_path = os.path.join(get_temp_path(), "UserEngine.ini")
209 | with open(config_path, "w+") as f:
210 | f.write(content)
211 | return config_path
212 |
213 | @classmethod
214 | def asConfig(cls, outputpath=""):
215 | return cls().saveAsConfig(outputpath)
216 |
217 | class Unreal4CMD(object):
218 | def __init__(self, editor="", project=""):
219 | self._editor = editor or os.getenv("UE4Editor", "")
220 | self._project = project or os.getenv("UE4Project", "")
221 |
222 | @property
223 | def editor(self):
224 | return str(self._editor)
225 |
226 | @editor.setter
227 | def editor(self, editor_path):
228 | self._editor = editor_path
229 |
230 | @property
231 | def project(self):
232 | return str(self._project)
233 |
234 | @project.setter
235 | def project(self, project_path):
236 | self._project = project_path
237 |
238 | def getCMD(self):
239 | if self.editor and os.path.exists(self.editor):
240 | if os.path.isfile(self.editor) and os.path.basename(self.editor) == "UE4Editor-Cmd.exe":
241 | return self.editor
242 | for r in glob.glob(os.path.normpath(self.editor) + "/Binaries/Win64/UE4Editor-Cmd.exe"):
243 | return r
244 |
245 | def getEditor(self):
246 | if self.editor and os.path.exists(self.editor):
247 | if os.path.isfile(self.editor) and os.path.basename(self.editor) == "UE4Editor.exe":
248 | return self.editor
249 | for r in (glob.glob(os.path.normpath(self.editor) + "/Binaries/Win64/UE4Editor.exe")):
250 | return r
251 |
252 | def getProject(self):
253 | if self.project and os.path.exists(self.project):
254 | if os.path.isfile(self.project) and self.project.endswith("*.uproject"):
255 | return self.project
256 | for r in glob.glob(self.project + "/*.uproject"):
257 | return r
258 |
259 | def getProjectConfig(self):
260 | projectfile_path = self.getProject()
261 | if projectfile_path:
262 | try:
263 | from StringIO import StringIO
264 | print(projectfile_path)
265 | data = str.decode(open(projectfile_path, 'r').read(), encoding='utf-8-sig')
266 | return json.loads(data)
267 | except Exception as why:
268 | print("Failed to parse project {} as json!\n{}".format(projectfile_path, why))
269 | else:
270 | raise NoProjectException("No valid file project!")
271 |
272 | def saveProjectConfig(self, project_config={}):
273 | if not project_config:
274 | project_config = self.getProjectConfig()
275 | projectfile_path = self.getProject()
276 | try:
277 | with open(projectfile_path, "w+") as f:
278 | json.dump(project_config, f, sort_keys=True, indent=4, separators=(',', ': '))
279 | except Exception as why:
280 | print(r"Cannot directly edit {} with error {}".format(os.path.basename(projectfile_path), why))
281 | projectfile_path = os.path.join(os.path.dirname(projectfile_path), "Edit{}".format(os.path.basename(projectfile_path)))
282 | with open(projectfile_path, "w+") as f:
283 | json.dump(project_config, f, sort_keys=True, indent=4, separators=(',', ': '))
284 |
285 | def setPlugins(self, projectfile_path, plugins):
286 | project_config = self.getProjectConfig()
287 | for plugin in plugins:
288 | for project_plugin in project_config["Plugins"]:
289 | if project_plugin.get("Name") == plugin.get("Name"):
290 | project_plugin["Enabled"] = plugin.get("Enabled")
291 | break
292 | else:
293 | project_config["Plugins"].append(plugin)
294 | self.saveProjectConfig(project_config)
295 | return projectfile_path
296 |
297 |
298 | def run_editor(
299 | self,
300 | map_path=None,
301 | argv= [],
302 | plugins = [
303 | {
304 | "Name": "PythonScriptPlugin",
305 | "Enabled": True
306 | },
307 | {
308 | "Name": "SequencerScripting",
309 | "Enabled": True
310 | },
311 | {
312 | "Name": "PythonAutomationTest",
313 | "Enabled": True
314 | },
315 | {
316 | "Name": "EditorScriptingUtilities",
317 | "Enabled": True
318 | }
319 | ],
320 | log= False,
321 | consolevariables= [],
322 | run_process_callable= Popen,
323 | custom_editor_path= "",
324 | custom_project_path= "",
325 | UserConfig = UserEngine.asConfig(),
326 | as_cmd= False,
327 | communicate = False
328 | ):
329 | getEditor = self.getCMD if as_cmd else self.getEditor
330 | editor_path = getEditor() or custom_editor_path
331 | if not editor_path:
332 | raise NoEditorException("No UE4 Editor Define")
333 |
334 | project_path = self.getProject() or custom_project_path
335 | if not project_path:
336 | raise NoEditorException("No Project Define")
337 |
338 | project_path = self.setPlugins(project_path, plugins)
339 |
340 | argv.append(r'-ExecCmds="{}"'.format(";".join(consolevariables)))
341 | if log:
342 | if isinstance(log, str):
343 | if os.path.split(log)[0]:
344 | argv.append(r'ABSLOG="{}"'.format(log))
345 | else:
346 | argv.append(r'LOG="{}"'.format(log))
347 | else:
348 | argv.append("-log")
349 | cmd = [editor_path, project_path]
350 | if map_path:
351 | cmd.append(map_path)
352 | if as_cmd:
353 | cmd.append("-silent")
354 | cmd.append("-UNATTENDED")
355 | cmd.append("-NOSPLASH")
356 | cmd.append("-NOLOADSTARTUPPACKAGES")
357 | cmd.append("-targetplatform=WindowsNoEditor")
358 | cmd.extend(argv)
359 | cmd.append("EDITORUSERSETTINGSINI={}".format(UserConfig))
360 | print("Run Unreal Editor with commands: {}".format(" ".join(cmd)))
361 | p = run_process_callable(cmd)
362 | if run_process_callable == Popen and communicate:
363 | p.communicate()
364 | return p
365 |
366 | def run_render(
367 | self,
368 | map_path,
369 | sequence_path,
370 | output_folder = "render",
371 | output_name = r"Render.{frame}",
372 | output_format= RenderOutputFormat.PNG,
373 | start_frame = 0,
374 | end_frame = 0,
375 | res_x = 1920,
376 | rex_y = 1080,
377 | frame_rate = 30,
378 | quality = 100,
379 | warmup_frames = 30,
380 | delay_frames = 30,
381 | delaybeforeshot_frames = 0,
382 | delayeveryframe_frames = 0,
383 | preview= False,
384 | shot=None,
385 | useburnin=False,
386 | write_editdecisionlist=None,
387 | write_finalcutxml=None,
388 | log=True
389 | ):
390 | cmds = [
391 | "-game",
392 | '-MovieSceneCaptureType="/Script/MovieSceneCapture.AutomatedLevelSequenceCapture"',
393 | '-LevelSequence="{}"'.format(sequence_path),
394 | "-NoLoadingScreen",
395 | "-ResX={}".format(res_x),
396 | "-ResY={}".format(rex_y),
397 | "-ForceRes",
398 | "-NoVSync" if preview else "-VSync",
399 | "-MovieFrameRate={}".format(frame_rate),
400 | "-NoTextureStreaming",
401 | "-MovieCinematicMode=Yes",
402 | "-MovieWarmUpFrames={}".format(warmup_frames),
403 | "-MovieDelayBeforeWarmUp={}".format(delay_frames),
404 | "-MovieDelayBeforeShotWarmUp={}".format(delaybeforeshot_frames),
405 | "-MovieDelayEveryFrame={}".format(delayeveryframe_frames),
406 | "-MovieQuality={}".format(quality),
407 | '-MovieFolder="{}"'.format(output_folder),
408 | '-MovieName="{}"'.format(output_name),
409 | '-MovieFormat="{}"'.format(output_format),
410 | '-UseBurnIn="{}"'.format(useburnin),
411 | "-NoScreenMessage",
412 | "-WINDOWED"
413 | ]
414 | if shot:
415 | cmds.append('-Shot="{}"'.format(shot))
416 | if start_frame:
417 | cmds.append("-MovieStartFrame={}".format(start_frame))
418 | if end_frame:
419 | cmds.append("-MovieEndFrame={}".format(end_frame))
420 | if write_editdecisionlist:
421 | cmds.append("-WriteEditDecisionList={}".format(write_editdecisionlist))
422 | if write_finalcutxml:
423 | cmds.append("-WriteFinalCutProXML={}".format(write_finalcutxml))
424 |
425 | console_commands = [
426 | "DisableAllScreenMessages",
427 | "r.DepthOfFieldQuality=4",
428 | "r.MotionBlurSeparable=1",
429 | "r.MotionBlurQuality=4"
430 | ]
431 | return self.run_editor(map_path= map_path, argv=cmds, consolevariables=console_commands, as_cmd=True, log=log, communicate=True)
432 |
433 | def run_python(
434 | self,
435 | python_file,
436 | fully_initialize = False,
437 | log = False,
438 | timeout = None,
439 | ):
440 | use_cmd = True
441 | cmd = ["-run=pythonscript", r"-script={}".format(python_file)]
442 |
443 | if fully_initialize:
444 | use_cmd = False
445 | cmd = [r'-ExecutePythonScript="{}"'.format(python_file)]
446 |
447 | return self.run_editor(
448 | argv=cmd,
449 | log=log,
450 | as_cmd=use_cmd,
451 | communicate=True
452 | )
453 |
454 | def run_import(self, importsettings, use_source_control = False, submit_desc="", log=False):
455 | try:
456 | cmd = ["-run=ImportAssets"]
457 | cmd.append(r'-importsettings="{}"'.format(importsettings))
458 | cmd.append("-replaceexisting")
459 | if not use_source_control:
460 | cmd.append("-nosourcecontrol")
461 | else:
462 | if submit_desc:
463 | cmd.append(r'-submitdesc="{}"'.format(submit_desc))
464 | except:
465 | print("OK")
466 | self.run_editor(argv=cmd, as_cmd=True, communicate=True, log=log)
--------------------------------------------------------------------------------