diff --git a/docs/send2ue/introduction/images/8.png b/docs/send2ue/introduction/images/8.png index 1b71428f..ba17d84d 100644 Binary files a/docs/send2ue/introduction/images/8.png and b/docs/send2ue/introduction/images/8.png differ diff --git a/docs/send2ue/trouble-shooting/faq.md b/docs/send2ue/trouble-shooting/faq.md index 083e5eed..2db7988c 100644 --- a/docs/send2ue/trouble-shooting/faq.md +++ b/docs/send2ue/trouble-shooting/faq.md @@ -3,6 +3,8 @@ ### Could not find an open Unreal Editor instance! Toggle `remote execution` off and back on in your project settings. +Make sure the Multicast Bind Address in the unreal project is set to `127.0.0.1`. + Go through the quickstart again. If that doesn't work the issue is most likely due to a networking issue. Check your systems firewall for blender and python. Also check the ports `6766` and `9998` on your computer to see if they are blocked by another application. diff --git a/requirements.txt b/requirements.txt index 30057af5..650c9b1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -docker==6.0.1 +docker==6.1.3 unittest-xml-reporting==3.2.0 -fake-bpy-module-latest==20220324 -PyGithub==1.57 +fake-bpy-module-latest==20231010 +PyGithub==2.1.1 diff --git a/send2ue/__init__.py b/send2ue/__init__.py index d8a8818e..47bdbd20 100644 --- a/send2ue/__init__.py +++ b/send2ue/__init__.py @@ -13,7 +13,7 @@ bl_info = { "name": "Send to Unreal", "author": "Epic Games Inc.", - "version": (2, 4, 1), + "version": (2, 4, 2), "blender": (3, 3, 0), "location": "Header > Pipeline > Send to Unreal", "description": "Sends an asset to the first open Unreal Editor instance on your machine.", diff --git a/send2ue/core/formatting.py b/send2ue/core/formatting.py index e03b9ba8..12ea97bd 100644 --- a/send2ue/core/formatting.py +++ b/send2ue/core/formatting.py @@ -241,7 +241,17 @@ def update_unreal_skeleton_asset_path(self, context): :param object self: This is a reference to the property data object. :param object context: The context when the property was called. """ - auto_format_unreal_asset_path('unreal_skeleton_asset_path', self) + name = 'unreal_skeleton_asset_path' + error_message = auto_format_unreal_asset_path(name, self) + value = getattr(self, name) + if value and not error_message: + asset_type = UnrealRemoteCalls.get_asset_type(value) + if asset_type != 'Skeleton': + error_message = f'This is a {asset_type} asset. This must be a Skeleton asset.' + set_property_error_message( + name, + error_message + ) def update_unreal_physics_asset_path(self, context): diff --git a/send2ue/core/utilities.py b/send2ue/core/utilities.py index 9ca93be5..a2ea3717 100644 --- a/send2ue/core/utilities.py +++ b/send2ue/core/utilities.py @@ -1087,9 +1087,6 @@ def setup_project(*args): :param args: This soaks up the extra arguments for the app handler. """ - # set the auth token variable - addon_properties = bpy.context.preferences.addons.get(ToolInfo.NAME.value) - # remove the cached files remove_temp_folder() diff --git a/send2ue/dependencies/remote_execution.py b/send2ue/dependencies/remote_execution.py index c2083ec6..1f80fb62 100644 --- a/send2ue/dependencies/remote_execution.py +++ b/send2ue/dependencies/remote_execution.py @@ -1,5 +1,6 @@ # Copyright Epic Games, Inc. All Rights Reserved. +import os import sys as _sys import json as _json import uuid as _uuid @@ -23,7 +24,7 @@ DEFAULT_MULTICAST_TTL = 0 # Multicast TTL (0 is limited to the local host, 1 is limited to the local subnet) DEFAULT_MULTICAST_GROUP_ENDPOINT = ('239.0.0.1', 6766) # The multicast group endpoint tuple that the UDP multicast socket should join (must match the "Multicast Group Endpoint" setting in the Python plugin) -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) +DEFAULT_MULTICAST_BIND_ADDRESS = '127.0.0.1' # The adapter address that the UDP multicast socket should bind to, or 127.0.0.1 to bind to all adapters (must match the "Multicast Bind Address" setting in the Python plugin) DEFAULT_COMMAND_ENDPOINT = ('127.0.0.1', 6776) # The endpoint tuple for the TCP command connection hosted by this client (that the remote client will connect to) DEFAULT_RECEIVE_BUFFER_SIZE = 8192 # The default receive buffer size @@ -540,7 +541,7 @@ def to_json(self): if self.data: json_obj['data'] = self.data return _json.dumps(json_obj, ensure_ascii=False) - + def to_json_bytes(self): ''' Convert this message to its JSON representation as UTF-8 bytes. diff --git a/send2ue/dependencies/unreal.py b/send2ue/dependencies/unreal.py index 1b5f678f..3655dae5 100644 --- a/send2ue/dependencies/unreal.py +++ b/send2ue/dependencies/unreal.py @@ -332,6 +332,24 @@ def get_skeletal_mesh_handle(component_handles, mesh_asset_path): return data_handle return None + @staticmethod + def get_bone_pose_for_frame(anim_sequence, bone_name, frame, extract_root_motion): + """ + Gets the local transform of the bone at the specified frame in the anim sequence. + + + :param object anim_sequence: An unreal anim sequence object. + :param str bone_name: A bone name. + :param float frame: The frame to get the transform at. + :param bool extract_root_motion: Whether to include root motion in the returned transform. + :returns: A transform. + :rtype: unreal.Transform + """ + pose_options = unreal.AnimPoseEvaluationOptions() + pose_options.set_editor_property('extract_root_motion', extract_root_motion) + pose = unreal.AnimPoseExtensions.get_anim_pose_at_frame(anim_sequence, frame, pose_options) + return unreal.AnimPoseExtensions.get_bone_pose(pose, bone_name) + @staticmethod def set_settings(property_group, data_object): """ @@ -985,6 +1003,17 @@ def get_lod_count(asset_path): lod_count = unreal.get_editor_subsystem(unreal.StaticMeshEditorSubsystem).get_lod_count(asset) return lod_count + @staticmethod + def get_asset_type(asset_path): + """ + Gets the asset type of the given asset. + + :param str asset_path: The path to the unreal asset. + :return str: The number of lods on the asset. + """ + asset = Unreal.get_asset(asset_path) + if asset: + return asset.__class__.__name__ @staticmethod def asset_exists(asset_path): @@ -992,7 +1021,7 @@ def asset_exists(asset_path): Checks to see if an asset exist in unreal. :param str asset_path: The path to the unreal asset. - :return bool: Whether or not the asset exists. + :return bool: Whether the asset exists. """ return bool(unreal.load_asset(asset_path)) @@ -1085,7 +1114,7 @@ def has_socket(asset_path, socket_name): :param str asset_path: The path to the unreal asset. :param str socket_name: The name of the socket to look for. - :return bool: Whether or not the asset has the given socket or not. + :return bool: Whether the asset has the given socket or not. """ mesh = Unreal.get_asset(asset_path) return bool(mesh.find_socket(socket_name)) @@ -1097,7 +1126,7 @@ def has_binding_groom_asset(binding_asset_path, groom_asset_path): :param str binding_asset_path: The path to the unreal binding asset. :param str groom_asset_path: The path to the unreal groom asset. - :return bool: Whether or not the binding asset has the given groom. + :return bool: Whether the binding asset has the given groom. """ binding_asset = unreal.load_asset(binding_asset_path) groom_asset = unreal.load_asset(groom_asset_path) @@ -1114,7 +1143,7 @@ def has_binding_target(binding_asset_path, target_mesh_path): :param str binding_asset_path: The path to the unreal binding asset. :param str target_mesh_path: The path to the unreal skeletal mesh asset. - :return bool: Whether or not the binding asset has the given skeletal mesh target. + :return bool: Whether the binding asset has the given skeletal mesh target. """ binding_asset = unreal.load_asset(binding_asset_path) mesh_asset = unreal.load_asset(target_mesh_path) @@ -1181,7 +1210,7 @@ def has_socket_outer(asset_path, socket_name): :param str asset_path: The path to the unreal asset. :param str socket_name: The name of the socket to look for. - :return bool: Whether or not the asset has the given socket and the owner (outer) is properly assigned or not. + :return bool: Whether the asset has the given socket and the owner (outer) is properly assigned or not. """ mesh = Unreal.get_asset(asset_path) socket = mesh.find_socket(socket_name) @@ -1196,7 +1225,7 @@ def delete_asset(asset_path): Deletes an asset in unreal. :param str asset_path: The path to the unreal asset. - :return bool: Whether or not the asset was deleted. + :return bool: Whether the asset was deleted. """ if unreal.EditorAssetLibrary.does_asset_exist(asset_path): unreal.EditorAssetLibrary.delete_asset(asset_path) @@ -1204,14 +1233,13 @@ def delete_asset(asset_path): @staticmethod def delete_directory(directory_path): """ - Deletes an folder and its contents in unreal. + Deletes a folder and its contents in unreal. :param str directory_path: The game path to the unreal project folder. - :return bool: Whether or not the directory was deleted. + :return bool: Whether the directory was deleted. """ - # API BUG:cant check if exists https://jira.it.epicgames.com/browse/UE-142234 - # if unreal.EditorAssetLibrary.does_directory_exist(directory_path): - unreal.EditorAssetLibrary.delete_directory(directory_path) + if unreal.EditorAssetLibrary.does_directory_exist(directory_path): + unreal.EditorAssetLibrary.delete_directory(directory_path) @staticmethod def import_asset(file_path, asset_data, property_data): @@ -1246,7 +1274,7 @@ def create_asset(asset_path, asset_class=None, asset_factory=None, unique_name=T :param str asset_path: The project path to the asset. :param str asset_class: The name of the unreal asset class. :param str asset_factory: The name of the unreal factory. - :param bool unique_name: Whether or not the check if the name is unique before creating the asset. + :param bool unique_name: Whether the check if the name is unique before creating the asset. """ asset_class = getattr(unreal, asset_class) factory = getattr(unreal, asset_factory)() @@ -1472,11 +1500,13 @@ def get_bone_transform_for_frame(asset_path, bone_name, frame): """ animation = Unreal.get_asset(asset_path) path = unreal.AnimationLibrary.find_bone_path_to_root(animation, bone_name) - transform = unreal.AnimationLibrary.get_bone_pose_for_frame(animation, bone_name, frame, True) + transform = Unreal.get_bone_pose_for_frame(animation, bone_name, frame, True) world_rotation = unreal.Rotator() world_location = unreal.Transform() + + # this walks the bone hierarchy to get the world transforms for bone in path: - bone_transform = unreal.AnimationLibrary.get_bone_pose_for_frame(animation, str(bone), frame, True) + bone_transform = Unreal.get_bone_pose_for_frame(animation, str(bone), frame, True) world_rotation = world_rotation.combine(bone_transform.rotation.rotator()) world_location = world_location.multiply(bone_transform) @@ -1502,7 +1532,7 @@ def get_bone_count(skeleton_path): @staticmethod def get_origin(asset_path): """ - Gets the location of the assets origin. + Gets the location of the asset origin. :param str asset_path: The project path to the asset. :return list: A list of bone name all the way to the root bone. diff --git a/send2ue/release_notes.md b/send2ue/release_notes.md index 28b74179..b51bbf57 100644 --- a/send2ue/release_notes.md +++ b/send2ue/release_notes.md @@ -1,13 +1,19 @@ ## Minor Changes -* Fixed rotations on instance assets extension. - * [602](https://github.com/EpicGames/BlenderTools/issues/602) - * [621](https://github.com/EpicGames/BlenderTools/issues/621) -* Fixed transformation bug with use object origin option. - * [610](https://github.com/EpicGames/BlenderTools/issues/610) +* Fixed positional argument bug with get_full_import_path. + * [631](https://github.com/EpicGames/BlenderTools/issues/631) +* Removed uniform scale option from UI that has no effect. + * [637](https://github.com/EpicGames/BlenderTools/issues/637) +* Added validation to check skeleton asset type. + * [638](https://github.com/EpicGames/BlenderTools/issues/638) +* Changed the default Multicast Bind Address. It is now `127.0.0.1`. + * [669](https://github.com/EpicGames/BlenderTools/issues/669) + +## Documentation Changes +* Updated [Errors](https://epicgames.github.io/BlenderTools/send2ue/trouble-shooting/errors.html) To include Multicast Bind Address setting ## Special Thanks -@abhiraaid +@DKingAlpha ## Tests Passing On -* Blender `3.3`, `3.5` (installed from blender.org) +* Blender `3.3`, `3.6` (installed from blender.org) * Unreal `5.1`, `5.2` diff --git a/send2ue/resources/setting_templates/default.json b/send2ue/resources/setting_templates/default.json index a478ce11..2e855aae 100644 --- a/send2ue/resources/setting_templates/default.json +++ b/send2ue/resources/setting_templates/default.json @@ -193,7 +193,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "material_curve_suffixes": "", "preserve_local_transform": true, "remove_redundant_keys": true, @@ -220,7 +219,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "morph_threshold_position": 0.014999999664723873, "normal_generation_method": "unreal.FBXNormalGenerationMethod.MIKK_T_SPACE", "normal_import_method": "unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS", @@ -261,7 +259,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "normal_generation_method": "unreal.FBXNormalGenerationMethod.MIKK_T_SPACE", "normal_import_method": "unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS", "one_convex_hull_per_ucx": true, @@ -299,7 +296,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "invert_normal_maps": false, "material_search_location": "unreal.MaterialSearchLocation.LOCAL" } diff --git a/send2ue/resources/settings.json b/send2ue/resources/settings.json index 3fd95efd..bb59c8b1 100644 --- a/send2ue/resources/settings.json +++ b/send2ue/resources/settings.json @@ -275,12 +275,6 @@ 0.0 ] }, - "import_uniform_scale": { - "name": "Import Uniform Scale", - "description": "Import Uniform Scale", - "type": "FLOAT", - "default": 1.0 - }, "reorder_material_to_fbx_order": { "name": "Reorder Material to FBX Order", "description": "The material list will be reorder to the same order has the FBX file", @@ -532,12 +526,6 @@ 0.0 ] }, - "import_uniform_scale": { - "name": "Import Uniform Scale", - "description": "Import Uniform Scale", - "type": "FLOAT", - "default": 1.0 - }, "reorder_material_to_fbx_order": { "name": "Reorder Material to FBX Order", "description": "The material list will be reorder to the same order has the FBX file", @@ -704,12 +692,6 @@ 0.0, 0.0 ] - }, - "import_uniform_scale": { - "name": "Import Uniform Scale", - "description": "Import Uniform Scale", - "type": "FLOAT", - "default": 1.0 } }, "texture_import_data": { @@ -862,12 +844,6 @@ 0.0, 0.0 ] - }, - "import_uniform_scale": { - "name": "Import Uniform Scale", - "description": "Import Uniform Scale", - "type": "FLOAT", - "default": 1.0 } } }, diff --git a/send2ue/ui/addon_preferences.py b/send2ue/ui/addon_preferences.py index 30048960..008b29d6 100644 --- a/send2ue/ui/addon_preferences.py +++ b/send2ue/ui/addon_preferences.py @@ -29,7 +29,6 @@ def draw(self, context): row.prop(self, 'extensions_repo_path', text='') row.operator('send2ue.reload_extensions', text='', icon='UV_SYNC_SELECT') - def register(): """ Registers the addon preferences when the addon is enabled. diff --git a/tests/test_files/send2ue_templates/default.json b/tests/test_files/send2ue_templates/default.json index a478ce11..2e855aae 100644 --- a/tests/test_files/send2ue_templates/default.json +++ b/tests/test_files/send2ue_templates/default.json @@ -193,7 +193,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "material_curve_suffixes": "", "preserve_local_transform": true, "remove_redundant_keys": true, @@ -220,7 +219,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "morph_threshold_position": 0.014999999664723873, "normal_generation_method": "unreal.FBXNormalGenerationMethod.MIKK_T_SPACE", "normal_import_method": "unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS", @@ -261,7 +259,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "normal_generation_method": "unreal.FBXNormalGenerationMethod.MIKK_T_SPACE", "normal_import_method": "unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS", "one_convex_hull_per_ucx": true, @@ -299,7 +296,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "invert_normal_maps": false, "material_search_location": "unreal.MaterialSearchLocation.LOCAL" } diff --git a/tests/test_files/send2ue_templates/template_1.json b/tests/test_files/send2ue_templates/template_1.json index bb3d3066..462db537 100644 --- a/tests/test_files/send2ue_templates/template_1.json +++ b/tests/test_files/send2ue_templates/template_1.json @@ -193,7 +193,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "material_curve_suffixes": "", "preserve_local_transform": true, "remove_redundant_keys": true, @@ -220,7 +219,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "morph_threshold_position": 0.014999999664723873, "normal_generation_method": "unreal.FBXNormalGenerationMethod.BUILT_IN", "normal_import_method": "unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS", @@ -261,7 +259,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "normal_generation_method": "unreal.FBXNormalGenerationMethod.MIKK_T_SPACE", "normal_import_method": "unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS", "one_convex_hull_per_ucx": true, @@ -299,7 +296,6 @@ 0.0, 0.0 ], - "import_uniform_scale": 1.0, "invert_normal_maps": false, "material_search_location": "unreal.MaterialSearchLocation.LOCAL" } diff --git a/tests/test_files/unreal_projects/test01/Config/DefaultEngine.ini b/tests/test_files/unreal_projects/test01/Config/DefaultEngine.ini index 520d777f..82caa850 100644 --- a/tests/test_files/unreal_projects/test01/Config/DefaultEngine.ini +++ b/tests/test_files/unreal_projects/test01/Config/DefaultEngine.ini @@ -44,4 +44,35 @@ ManualIPAddress= [/Script/PythonScriptPlugin.PythonScriptPluginSettings] bRemoteExecution=True +RemoteExecutionMulticastBindAddress=127.0.0.1 + +[/Script/WindowsTargetPlatform.WindowsTargetSettings] +DefaultGraphicsRHI=DefaultGraphicsRHI_Default +-D3D12TargetedShaderFormats=PCD3D_SM5 ++D3D12TargetedShaderFormats=PCD3D_SM5 ++D3D12TargetedShaderFormats=PCD3D_SM6 +-D3D11TargetedShaderFormats=PCD3D_SM5 ++D3D11TargetedShaderFormats=PCD3D_SM5 +Compiler=Default +AudioSampleRate=48000 +AudioCallbackBufferFrameSize=1024 +AudioNumBuffersToEnqueue=1 +AudioMaxChannels=0 +AudioNumSourceWorkers=4 +SpatializationPlugin= +SourceDataOverridePlugin= +ReverbPlugin= +OcclusionPlugin= +CompressionOverrides=(bOverrideCompressionTimes=False,DurationThreshold=5.000000,MaxNumRandomBranches=0,SoundCueQualityIndex=0) +CacheSizeKB=65536 +MaxChunkSizeOverrideKB=0 +bResampleForDevice=False +MaxSampleRate=48000.000000 +HighSampleRate=32000.000000 +MedSampleRate=24000.000000 +LowSampleRate=12000.000000 +MinSampleRate=8000.000000 +CompressionQualityModifier=1.000000 +AutoStreamingThreshold=0.000000 +SoundCueCookQualityIndex=-1 diff --git a/tests/test_files/unreal_projects/test01/test01.uproject b/tests/test_files/unreal_projects/test01/test01.uproject index cee5a315..03eaeed4 100644 --- a/tests/test_files/unreal_projects/test01/test01.uproject +++ b/tests/test_files/unreal_projects/test01/test01.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "5.2", + "EngineAssociation": "5.3", "Category": "", "Description": "", "Plugins": [ diff --git a/tests/utils/addon_packager.py b/tests/utils/addon_packager.py index 9ea86455..827cb735 100644 --- a/tests/utils/addon_packager.py +++ b/tests/utils/addon_packager.py @@ -13,6 +13,12 @@ pass +IGNORE_PATTERNS = [ + "__pycache__", + "*.pyc" +] + + class AddonPackager: def __init__(self, addon_name, addon_folder_path, output_folder): @@ -148,7 +154,11 @@ def zip_addon(self): logging.warning(f'Could not delete {versioned_folder_path}!') # copy the addon module in to the versioned directory with its addon module name as a sub directory - shutil.copytree(self.addon_folder_path, os.path.join(versioned_folder_path, self.addon_name)) + shutil.copytree( + self.addon_folder_path, + os.path.join(versioned_folder_path, self.addon_name), + ignore=shutil.ignore_patterns(*IGNORE_PATTERNS) + ) # make a zip archive of the copied folder shutil.make_archive(versioned_folder_path, 'zip', versioned_folder_path)