diff --git a/.gitignore b/.gitignore
index 885bf479..47349ee9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,10 +1,13 @@
-# Blender-For-UnrealEngine-Addons
+# Docs exported files
+ExportedFbx/
+
+# BleuRaven Blender Addons
+/generated_builds
**.zip
-**.blend[1-9]*
+**.blend[1-32]*
.git/
.vscode/
.vs/
-ExportedFbx/
# Byte-compiled / optimized / DLL files
__pycache__/
diff --git a/ReleaseLogs/Version_4.3.2.md b/ReleaseLogs/Version_4.3.2.md
new file mode 100644
index 00000000..0fa87b4d
--- /dev/null
+++ b/ReleaseLogs/Version_4.3.2.md
@@ -0,0 +1,6 @@
+# Unreal Engine Assets Exporter - Release Log
+Release Logs: https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Release-Logs
+
+### Version 4.3.2
+
+- New: Addon renamed from "Blender For Unreal Engine" To "Unreal Engine Assets Exporter". This was needed to follow [Blender Extensions Terms of Service](https://extensions.blender.org/terms-of-service/) (Branding)
diff --git a/ReleaseLogs/Version_4.3.3.md b/ReleaseLogs/Version_4.3.3.md
new file mode 100644
index 00000000..a2f36c65
--- /dev/null
+++ b/ReleaseLogs/Version_4.3.3.md
@@ -0,0 +1,7 @@
+# Unreal Engine Assets Exporter - Release Log
+Release Logs: https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Release-Logs
+
+### Version 4.3.3
+
+- Update import scripts to avoid sys.path manipulations durring BFU module imports.
+- Fixed: If export folder is relative empty "//" addons export assets at the disc root.
diff --git a/ReleaseLogs/Version_4.3.4.md b/ReleaseLogs/Version_4.3.4.md
new file mode 100644
index 00000000..4bdcc237
--- /dev/null
+++ b/ReleaseLogs/Version_4.3.4.md
@@ -0,0 +1,9 @@
+# Unreal Engine Assets Exporter - Release Log
+Release Logs: https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Release-Logs
+
+### Version 4.3.4
+
+- Update import scripts to avoid sys.argv manipulations durring BFU module imports.
+- Removed useless fbxio folders depending Blender Version.
+- Fixed: If export folder is relative empty "//" addons export import scripts at the disc root.
+
diff --git a/ReleaseLogs/Version_4.3.5.md b/ReleaseLogs/Version_4.3.5.md
new file mode 100644
index 00000000..4fef1226
--- /dev/null
+++ b/ReleaseLogs/Version_4.3.5.md
@@ -0,0 +1,7 @@
+# Unreal Engine Assets Exporter - Release Log
+Release Logs: https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Release-Logs
+
+### Version 4.3.5
+
+- Fix copyright.
+- Fix import script fail. (import 'importlib.util' and sys.modules manipulation)
\ No newline at end of file
diff --git a/ReleaseLogs/Version_4.3.6.md b/ReleaseLogs/Version_4.3.6.md
new file mode 100644
index 00000000..5efcf550
--- /dev/null
+++ b/ReleaseLogs/Version_4.3.6.md
@@ -0,0 +1,6 @@
+# Unreal Engine Assets Exporter - Release Log
+Release Logs: https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Release-Logs
+
+### Version 4.3.6
+
+- Fixed: Names in data_path are not escaped.
\ No newline at end of file
diff --git a/ReleaseLogs/Version_4.3.7.md b/ReleaseLogs/Version_4.3.7.md
new file mode 100644
index 00000000..043bc8df
--- /dev/null
+++ b/ReleaseLogs/Version_4.3.7.md
@@ -0,0 +1,14 @@
+# Unreal Engine Assets Exporter - Release Log
+Release Logs: https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Release-Logs
+
+### Version 4.3.7
+
+- New: Support for Unreal Engine 5.5.
+- New: Correct Extrem UV Scale: Added the "Use Positive Pos" option to keep UV islands in positive positions.
+- New: Correct Extrem UV Scale: Added the "Step Scale" option for export.
+- New: Correct Extrem UV Scale: Added the "Use Positive Pos" option for export.
+- Changes: The addon now uses the new Interchange Assets pipeline for importing assets into Unreal Engine 5.5.
+- Fixed: Correct Extrem UV Scale: UV changes are now applied to the entire asset.
+- Fixed: Animations were not exported to subfolders on disk.
+- Fixed: NLA lost animated_influence FCurve after export.
+- Cleanup: Removed debug logs.
diff --git a/ReleaseLogs/Version_4.3.8.md b/ReleaseLogs/Version_4.3.8.md
new file mode 100644
index 00000000..8b9c0b14
--- /dev/null
+++ b/ReleaseLogs/Version_4.3.8.md
@@ -0,0 +1,12 @@
+# Unreal Engine Assets Exporter - Release Log
+Release Logs: https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Release-Logs
+
+### Version 4.3.8
+
+- Full addon refactoring for better properties and UI management and organization.
+- Fixed: Custom Skeletal Mesh Ref not well used.
+- Fixed: Skeleton search use only loaded assets.
+- Fixed: Export curve as static mesh do export fail.
+- Fixed: Script fail at import for Unreal Engine 4.27
+- Fixed: Script fail at import in Unreal Engine when python plugins is disabled.
+- Fixed: Import mesh with multiple lods may create new material slots.
\ No newline at end of file
diff --git a/blender-for-unrealengine/__init__.py b/blender-for-unrealengine/__init__.py
index e34e5dd7..6bb0b904 100644
--- a/blender-for-unrealengine/__init__.py
+++ b/blender-for-unrealengine/__init__.py
@@ -32,13 +32,22 @@
import bpy
import importlib
-from . import bps
+from . import bpl
from . import bbpl
from . import bfu_assets_manager
from . import bfu_propertys
+from . import bfu_base_object
+from . import bfu_adv_object
+from . import bfu_base_collection
from . import bfu_static_mesh
from . import bfu_skeletal_mesh
+from . import bfu_modular_skeletal_mesh
from . import bfu_alembic_animation
+from . import bfu_anim_base
+from . import bfu_anim_action
+from . import bfu_anim_action_adv
+from . import bfu_anim_nla
+from . import bfu_anim_nla_adv
from . import bfu_groom
from . import bfu_camera
from . import bfu_spline
@@ -47,8 +56,14 @@
from . import bfu_material
from . import bfu_vertex_color
from . import bfu_lod
+from . import bfu_uv_map
+from . import bfu_light_map
+from . import bfu_assets_references
from . import bfu_custom_property
from . import bfu_addon_parts
+from . import bfu_export_nomenclature
+from . import bfu_export_filter
+from . import bfu_export_process
from . import bfu_export_procedure
from . import bfu_addon_pref
from . import bfu_export_logs
@@ -68,20 +83,39 @@
from . import bfu_cached_asset_list
-if "bps" in locals():
- importlib.reload(bps)
+
+if "bpl" in locals():
+ importlib.reload(bpl)
if "bbpl" in locals():
importlib.reload(bbpl)
if "bfu_assets_manager" in locals():
importlib.reload(bfu_assets_manager)
if "bfu_propertys" in locals():
importlib.reload(bfu_propertys)
+if "bfu_base_object" in locals():
+ importlib.reload(bfu_base_object)
+if "bfu_adv_object" in locals():
+ importlib.reload(bfu_adv_object)
+if "bfu_base_collection" in locals():
+ importlib.reload(bfu_base_collection)
if "bfu_static_mesh" in locals():
importlib.reload(bfu_static_mesh)
if "bfu_skeletal_mesh" in locals():
importlib.reload(bfu_skeletal_mesh)
+if "bfu_modular_skeletal_mesh" in locals():
+ importlib.reload(bfu_modular_skeletal_mesh)
if "bfu_alembic_animation" in locals():
importlib.reload(bfu_alembic_animation)
+if "bfu_anim_base" in locals():
+ importlib.reload(bfu_anim_base)
+if "bfu_anim_action" in locals():
+ importlib.reload(bfu_anim_action)
+if "bfu_anim_action_adv" in locals():
+ importlib.reload(bfu_anim_action_adv)
+if "bfu_anim_nla" in locals():
+ importlib.reload(bfu_anim_nla)
+if "bfu_anim_nla_adv" in locals():
+ importlib.reload(bfu_anim_nla_adv)
if "bfu_groom" in locals():
importlib.reload(bfu_groom)
if "bfu_camera" in locals():
@@ -98,10 +132,22 @@
importlib.reload(bfu_vertex_color)
if "bfu_lod" in locals():
importlib.reload(bfu_lod)
+if "bfu_uv_map" in locals():
+ importlib.reload(bfu_uv_map)
+if "bfu_light_map" in locals():
+ importlib.reload(bfu_light_map)
+if "bfu_assets_references" in locals():
+ importlib.reload(bfu_assets_references)
if "bfu_custom_property" in locals():
importlib.reload(bfu_custom_property)
if "bfu_addon_parts" in locals():
importlib.reload(bfu_addon_parts)
+if "bfu_export_nomenclature" in locals():
+ importlib.reload(bfu_export_nomenclature)
+if "bfu_export_filter" in locals():
+ importlib.reload(bfu_export_filter)
+if "bfu_export_process" in locals():
+ importlib.reload(bfu_export_process)
if "bfu_export_procedure" in locals():
importlib.reload(bfu_export_procedure)
if "bfu_addon_pref" in locals():
@@ -137,19 +183,7 @@
if "bfu_cached_asset_list" in locals():
importlib.reload(bfu_cached_asset_list)
-bl_info = {
- 'name': 'Blender for UnrealEngine',
- 'author': 'Loux Xavier (BleuRaven)',
- 'version': (4, 3, 1),
- 'blender': (2, 80, 0),
- 'location': 'View3D > UI > Unreal Engine',
- 'description': "This add-ons allows to easily export several objects at the same time and import in Unreal Engine.",
- 'warning': '',
- "wiki_url": "https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki",
- 'tracker_url': 'https://github.com/xavier150/Blender-For-UnrealEngine-Addons/issues',
- 'support': 'COMMUNITY',
- 'category': 'Import-Export'}
-
+bl_info = {}
class BFUCachedAction(bpy.types.PropertyGroup):
"""
@@ -168,9 +202,18 @@ def register():
bbpl.register()
bfu_assets_manager.register()
bfu_propertys.register()
+ bfu_base_object.register()
+ bfu_adv_object.register()
+ bfu_base_collection.register()
bfu_static_mesh.register()
bfu_skeletal_mesh.register()
+ bfu_modular_skeletal_mesh.register()
bfu_alembic_animation.register()
+ bfu_anim_base.register()
+ bfu_anim_action.register()
+ bfu_anim_action_adv.register()
+ bfu_anim_nla.register()
+ bfu_anim_nla_adv.register()
bfu_groom.register()
bfu_camera.register()
bfu_spline.register()
@@ -179,8 +222,14 @@ def register():
bfu_material.register()
bfu_vertex_color.register()
bfu_lod.register()
+ bfu_uv_map.register()
+ bfu_light_map.register()
+ bfu_assets_references.register()
bfu_custom_property.register()
bfu_addon_parts.register()
+ bfu_export_nomenclature.register()
+ bfu_export_filter.register()
+ bfu_export_process.register()
bfu_export_procedure.register()
bfu_addon_pref.register()
bfu_export_logs.register()
@@ -200,8 +249,14 @@ def unregister():
bfu_export_logs.unregister()
bfu_addon_pref.unregister()
bfu_export_procedure.unregister()
+ bfu_export_process.unregister()
+ bfu_export_filter.unregister()
+ bfu_export_nomenclature.unregister()
bfu_addon_parts.unregister()
bfu_custom_property.unregister()
+ bfu_assets_references.unregister()
+ bfu_light_map.unregister()
+ bfu_uv_map.unregister()
bfu_lod.unregister()
bfu_vertex_color.unregister()
bfu_material.unregister()
@@ -209,10 +264,19 @@ def unregister():
bfu_collision.unregister()
bfu_spline.unregister()
bfu_camera.unregister()
+ bfu_anim_nla_adv.unregister()
+ bfu_anim_nla.unregister()
+ bfu_anim_action_adv.unregister()
+ bfu_anim_action.unregister()
+ bfu_anim_base.unregister()
bfu_alembic_animation.unregister()
bfu_groom.unregister()
+ bfu_modular_skeletal_mesh.unregister()
bfu_skeletal_mesh.unregister()
bfu_static_mesh.unregister()
+ bfu_base_collection.unregister()
+ bfu_adv_object.unregister()
+ bfu_base_object.unregister()
bfu_propertys.unregister()
bfu_assets_manager.unregister()
bbpl.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/addon_generate_config.json b/blender-for-unrealengine/addon_generate_config.json
new file mode 100644
index 00000000..e67ebe10
--- /dev/null
+++ b/blender-for-unrealengine/addon_generate_config.json
@@ -0,0 +1,89 @@
+{
+ "schema_version": [1,0,0],
+ "blender_manifest": {
+ "id": "unrealengine_assets_exporter",
+ "version": [4,3,8],
+ "name": "Unreal Engine Assets Exporter",
+ "tagline": "Allows to batch export and import in Unreal Engine",
+ "maintainer": "Loux Xavier (BleuRaven) xavierloux.loux@gmail.com",
+
+ "website_url": "https://github.com/xavier150/Blender-For-UnrealEngine-Addons/",
+ "report_issue_url": "https://github.com/xavier150/Blender-For-UnrealEngine-Addons/issues",
+ "support": "COMMUNITY",
+
+ "type": "add-on",
+ "tags": ["Import-Export"],
+ "category": "Import-Export",
+ "license": ["SPDX:GPL-3.0-or-later"],
+
+ "copyright": [
+ "2024 Xavier Loux",
+ "2013 Blender Foundation",
+ "2006-2012 assimp team",
+ "2013 Campbell Barton",
+ "2014 Bastien Montagne"
+ ],
+
+ "permissions": {
+ "files": "Import/export FBX from/to disk",
+ "clipboard": "Copy generated script paths"
+ }
+ },
+
+ "builds": {
+ "unrealengine_assets_exporter_4.3": {
+ "generate_method": "EXTENTION_COMMAND",
+ "auto_install_range": [[4,3,0], [4,3,0]],
+ "naming": "{Name}_{Version}-blender_4.3.zip",
+ "module": "blender-for-unrealengine",
+ "pkg_id": "unrealengine_assets_exporter",
+ "exclude_paths": [
+ "fbxio/"
+ ],
+ "include_paths": [
+ "fbxio/__init__.py/",
+ "fbxio/io_scene_fbx_4_3/"
+ ],
+ "blender_version_min": [4,3,0]
+ },
+ "unrealengine_assets_exporter_4.2": {
+ "generate_method": "EXTENTION_COMMAND",
+ "auto_install_range": [[4,2,0], [4,2,3]],
+ "naming": "{Name}_{Version}-blender_4.2.zip",
+ "module": "blender-for-unrealengine",
+ "pkg_id": "unrealengine_assets_exporter",
+ "exclude_paths": [
+ "fbxio/"
+ ],
+ "include_paths": [
+ "fbxio/__init__.py/",
+ "fbxio/io_scene_fbx_4_2/"
+ ],
+ "blender_version_min": [4,2,0]
+ },
+ "unrealengine_assets_exporter_2.8": {
+ "generate_method": "SIMPLE_ZIP",
+ "auto_install_range": [[2,80,0], [4,1,0]],
+ "naming": "{Name}_{Version}-blender_2.8-4.1.zip",
+ "module": "blender-for-unrealengine",
+ "pkg_id": "unrealengine_assets_exporter",
+ "exclude_paths": [
+ "fbxio/"
+ ],
+ "include_paths": [
+ "fbxio/__init__.py/",
+ "fbxio/io_scene_fbx_4_1/",
+ "fbxio/io_scene_fbx_4_0/",
+ "fbxio/io_scene_fbx_3_6/",
+ "fbxio/io_scene_fbx_3_5/",
+ "fbxio/io_scene_fbx_3_4/",
+ "fbxio/io_scene_fbx_3_3/",
+ "fbxio/io_scene_fbx_3_2/",
+ "fbxio/io_scene_fbx_3_1/",
+ "fbxio/io_scene_fbx_2_93/",
+ "fbxio/io_scene_fbx_2_83/"
+ ],
+ "blender_version_min": [2,80,0]
+ }
+ }
+}
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbam/__init__.py b/blender-for-unrealengine/bbam/__init__.py
new file mode 100644
index 00000000..7994211d
--- /dev/null
+++ b/blender-for-unrealengine/bbam/__init__.py
@@ -0,0 +1,110 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import os
+import json
+import importlib
+
+from . import config
+from . import manifest_generate
+from . import bl_info_generate
+from . import addon_file_management
+from . import utils
+from . import blender_exec
+from . import blender_utils
+
+# Reloading modules if they're already loaded
+if "config" in locals():
+ importlib.reload(config)
+if "manifest_generate" in locals():
+ importlib.reload(manifest_generate)
+if "bl_info_generate" in locals():
+ importlib.reload(bl_info_generate)
+if "addon_file_management" in locals():
+ importlib.reload(addon_file_management)
+if "utils" in locals():
+ importlib.reload(utils)
+if "blender_exec" in locals():
+ importlib.reload(blender_exec)
+if "blender_utils" in locals():
+ importlib.reload(blender_utils)
+
+def install_from_blender():
+ """
+ Loads the addon's configuration file to retrieve its manifest data and initiates
+ the installation process within Blender.
+ """
+ # Get the path of the current addon's configuration file from `config`
+ addon_manifest = config.addon_generate_config
+
+ # Construct absolute paths for addon and manifest file
+ addon_path = os.path.abspath(os.path.join(__file__, '..', '..'))
+ search_addon_folder = os.path.abspath(os.path.join(addon_path, addon_manifest))
+
+ # Load the manifest file data if it exists
+ if os.path.isfile(search_addon_folder):
+ with open(search_addon_folder, 'r', encoding='utf-8') as file:
+ data = json.load(file)
+ install_from_blender_with_build_data(addon_path, data)
+ else:
+ print(f"Error: '{addon_manifest}' was not found in '{search_addon_folder}'.")
+
+def install_from_blender_with_build_data(addon_path, addon_manifest_data):
+ """
+ Manages the addon installation in Blender based on the build data from the manifest.
+
+ Parameters:
+ addon_path (str): The path to the addon's root directory.
+ addon_manifest_data (dict): The data structure containing build specifications.
+ """
+ # Import bpy lib here when exec from Blender.
+ import bpy
+
+ # Get Blender executable path from bpy
+ blender_executable_path = bpy.app.binary_path
+
+ # Process each build specified in the manifest data
+ for target_build_name in addon_manifest_data["builds"]:
+ # Create temporary addon folder
+ temp_addon_path = addon_file_management.create_temp_addon_folder(
+ addon_path, addon_manifest_data, target_build_name
+ )
+ # Zip the addon folder for installation
+ zip_file = addon_file_management.zip_addon_folder(
+ temp_addon_path, addon_path, addon_manifest_data, target_build_name, blender_executable_path
+ )
+
+ build_data = addon_manifest_data["builds"][target_build_name]
+ pkg_id = build_data.get("pkg_id")
+ module = build_data.get("module")
+
+ # Check if the addon should be installed based on Blender's version
+ auto_install_range = utils.get_tuple_range_version(build_data.get("auto_install_range"))
+ should_install = utils.get_version_in_range(bpy.app.version, auto_install_range)
+ if should_install:
+
+ # Uninstall previous versions if they exist
+ blender_utils.uninstall_addon_from_blender(bpy, pkg_id, module)
+ blender_utils.install_zip_addon_from_blender(bpy, zip_file, module)
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbam/addon_file_management.py b/blender-for-unrealengine/bbam/addon_file_management.py
new file mode 100644
index 00000000..bd2add4a
--- /dev/null
+++ b/blender-for-unrealengine/bbam/addon_file_management.py
@@ -0,0 +1,188 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import shutil
+import tempfile
+import sys
+import os
+from . import manifest_generate
+from . import bl_info_generate
+from . import config
+from . import utils
+from . import blender_exec
+from . import blender_utils
+
+
+def copy_addon_folder(src, dst, exclude_paths=[], include_paths=[]):
+ """
+ Copies the addon folder from 'src' to 'dst' while excluding specified files and folders.
+
+ Parameters:
+ src (str): Source path of the addon.
+ dst (str): Destination path for the copied addon.
+ exclude_paths (list): List of file or folder paths to exclude during the copy process.
+ """
+ # Normalize paths for comparison
+ exclude_paths = [os.path.normpath(path) for path in exclude_paths]
+ include_paths = [os.path.normpath(path) for path in include_paths]
+
+ # Ignore function to exclude specific files/folders during the copy
+ def ignore_files(dir, files):
+ ignore_list = []
+ for file in files:
+ file_path = os.path.join(dir, file)
+ relative_path = os.path.normpath(os.path.relpath(file_path, src))
+
+ # Skip directories if only files should be ignored
+ if os.path.isdir(file_path):
+ continue
+
+ # Check if the file path should be included
+ if any(relative_path.startswith(os.path.normpath(path)) for path in include_paths):
+ continue # Skip excluding this file or folder
+
+ # Check if the file path should be excluded
+ if any(relative_path.startswith(os.path.normpath(path)) for path in exclude_paths):
+ ignore_list.append(file)
+ return set(ignore_list)
+
+ shutil.copytree(src, dst, ignore=ignore_files)
+
+
+def create_temp_addon_folder(addon_path, addon_manifest_data, target_build_name, show_debug=True):
+ """
+ Creates a temporary folder for the addon, copies relevant files, and generates the manifest.
+
+ Parameters:
+ addon_path (str): Root path of the addon.
+ addon_manifest_data (dict): Manifest data containing build specifications.
+ target_build_name (str): Name of the target build configuration.
+ show_debug (bool): If True, debug information is displayed.
+
+ Returns:
+ str: Path to the temporary addon folder.
+ """
+ build_data = addon_manifest_data["builds"][target_build_name]
+ generate_method = build_data["generate_method"]
+
+ # Step 1: Create a temporary directory for the addon
+ temp_dir = tempfile.mkdtemp(prefix="blender_addon_")
+ temp_addon_path = os.path.join(temp_dir, os.path.basename(addon_path))
+
+ # Step 2: Copy addon folder to temporary directory, excluding specified paths
+ exclude_paths = build_data.get("exclude_paths", [])
+ include_paths = build_data.get("include_paths", [])
+ exclude_paths.append("bbam/") # Exclude addon manager from the final build
+ copy_addon_folder(addon_path, temp_addon_path, exclude_paths, include_paths)
+ print(f"Copied build '{target_build_name}' to temporary location: {temp_addon_path}")
+
+ # Step 3: Generate addon manifest based on generation method
+ if generate_method == "EXTENTION_COMMAND":
+ new_manifest = manifest_generate.generate_new_manifest(addon_manifest_data, target_build_name)
+ manifest_generate.save_addon_manifest(temp_addon_path, new_manifest, show_debug)
+ elif generate_method == "SIMPLE_ZIP":
+ new_manifest = bl_info_generate.generate_new_bl_info(addon_manifest_data, target_build_name)
+ bl_info_generate.update_file_bl_info(temp_addon_path, new_manifest, show_debug)
+
+ return temp_addon_path
+
+def get_zip_output_filename(addon_path, addon_manifest_data, target_build_name):
+ """
+ Generates the output filename for the ZIP file based on naming conventions in the manifest.
+
+ Parameters:
+ addon_path (str): Root path of the addon.
+ addon_manifest_data (dict): Manifest data containing build specifications.
+ target_build_name (str): Name of the target build configuration.
+
+ Returns:
+ str: Full path of the output ZIP file.
+ """
+ manifest_data = addon_manifest_data["blender_manifest"]
+ build_data = addon_manifest_data["builds"][target_build_name]
+ version = utils.get_str_version(manifest_data["version"])
+
+ # Formatting output filename
+ output_folder_path = os.path.abspath(os.path.join(addon_path, '..', config.build_output_folder))
+ formatted_file_name = build_data["naming"].replace("{Name}", build_data["pkg_id"]).replace("{Version}", version)
+ output_filepath = os.path.join(output_folder_path, formatted_file_name)
+ return output_filepath
+
+def zip_addon_folder(src, addon_path, addon_manifest_data, target_build_name, blender_executable_path):
+ """
+ Creates a ZIP archive of the addon folder, either through Blender's extension command
+ or by using a simple ZIP method.
+
+ Parameters:
+ src (str): Path to the source addon folder.
+ addon_path (str): Root path of the addon.
+ addon_manifest_data (dict): Manifest data containing build specifications.
+ target_build_name (str): Name of the target build configuration.
+ blender_executable_path (str): Path to the Blender executable for running commands.
+
+ Returns:
+ str: Path to the created ZIP file.
+ """
+ build_data = addon_manifest_data["builds"][target_build_name]
+ generate_method = build_data["generate_method"]
+
+ # Define output file path and ensure the output directory exists
+ output_filepath = get_zip_output_filename(addon_path, addon_manifest_data, target_build_name)
+ output_dir = os.path.dirname(output_filepath)
+ os.makedirs(output_dir, exist_ok=True)
+
+ # Run addon zip process based on the specified generation method
+ if generate_method == "EXTENTION_COMMAND":
+ print("Start build with extension command")
+ result = blender_exec.build_extension(src, output_filepath, blender_executable_path)
+ print(result.stdout)
+ print(result.stderr, file=sys.stderr)
+
+ created_filename = blender_exec.get_build_file(result)
+ if created_filename:
+ print("Start Validate")
+ blender_exec.validate_extension(created_filename, blender_executable_path)
+ print("End Validate")
+
+ return created_filename
+
+ elif generate_method == "SIMPLE_ZIP":
+ print("Start creating simple ZIP file with root folder using shutil")
+
+ # Specify the root folder name inside the ZIP file
+ root_folder_name = "my_addon_root_folder"
+
+ # Create a temporary directory
+ with tempfile.TemporaryDirectory() as temp_dir:
+ # Copy the source folder to a temporary root folder
+ temp_root = os.path.join(temp_dir, root_folder_name)
+ shutil.copytree(src, temp_root)
+
+ # Use shutil to create the ZIP archive from the temporary directory
+ base_name = os.path.splitext(output_filepath)[0] # Path without .zip extension
+ shutil.make_archive(base_name, 'zip', temp_dir, root_folder_name)
+
+ print(f"SIMPLE_ZIP created successfully at {output_filepath}")
+ return output_filepath
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbam/bl_info_generate.py b/blender-for-unrealengine/bbam/bl_info_generate.py
new file mode 100644
index 00000000..da3dfde5
--- /dev/null
+++ b/blender-for-unrealengine/bbam/bl_info_generate.py
@@ -0,0 +1,101 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import os
+import re
+from . import config
+
+def generate_new_bl_info(addon_generate_config_data, target_build_name):
+ """
+ Generates a new `bl_info` dictionary for the addon based on the configuration data.
+
+ Parameters:
+ addon_generate_config_data (dict): Data containing addon configurations.
+ target_build_name (str): The name of the target build configuration.
+
+ Returns:
+ dict: A dictionary representing the new `bl_info` for the addon.
+ """
+ # Check if the target build data exists
+ if target_build_name not in addon_generate_config_data["builds"]:
+ print(f"Error: Build data for '{target_build_name}' not found!")
+ return {}
+
+ manifest_data = addon_generate_config_data["blender_manifest"]
+ build_data = addon_generate_config_data["builds"][target_build_name]
+
+ # Populate `bl_info` with addon details
+ data = {
+ 'name': manifest_data["name"],
+ 'author': manifest_data["maintainer"],
+ 'version': tuple(manifest_data["version"]),
+ 'blender': tuple(build_data["blender_version_min"]),
+ 'location': 'View3D > UI > Unreal Engine',
+ 'description': manifest_data["tagline"],
+ 'warning': '',
+ "wiki_url": manifest_data["website_url"],
+ 'tracker_url': manifest_data["report_issue_url"],
+ 'support': manifest_data["support"],
+ 'category': manifest_data["category"]
+ }
+
+ return data
+
+def update_file_bl_info(addon_path, data, show_debug=False):
+ """
+ Updates the `bl_info` dictionary in the addon's __init__.py file with new data.
+
+ Parameters:
+ addon_path (str): Path to the addon's root folder.
+ data (dict): New `bl_info` dictionary to update in the file.
+ show_debug (bool): If True, displays debug information about the update process.
+ """
+ addon_init_file_path = os.path.join(addon_path, "__init__.py")
+
+ # Format the new `bl_info` dictionary with line breaks and indentation
+ new_bl_info_lines = ["bl_info = {\n"]
+ for key, value in data.items():
+ new_bl_info_lines.append(f" '{key}': {repr(value)},\n")
+ new_bl_info_lines.append("}\n\n") # Close `bl_info` and add an extra line break for readability
+
+ # Read the existing lines of the __init__.py file
+ with open(addon_init_file_path, 'r') as file:
+ lines = file.readlines()
+
+ # Write the updated lines, replacing the old `bl_info` if it exists
+ with open(addon_init_file_path, "w") as file:
+ in_bl_info = False
+ for line in lines:
+ # Detect the start of `bl_info`
+ if line.strip().startswith("bl_info = {") and not in_bl_info:
+ in_bl_info = True
+ file.writelines(new_bl_info_lines) # Write the new `bl_info` dictionary
+ elif in_bl_info and line.strip() == "}":
+ in_bl_info = False # End of `bl_info`, but skip this closing brace line
+ elif not in_bl_info:
+ file.write(line) # Write all other lines unchanged
+
+ if show_debug:
+ print(f"Addon bl_info successfully updated at: {addon_init_file_path}")
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbam/blender_exec.py b/blender-for-unrealengine/bbam/blender_exec.py
new file mode 100644
index 00000000..f48d12ad
--- /dev/null
+++ b/blender-for-unrealengine/bbam/blender_exec.py
@@ -0,0 +1,85 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import sys
+import re
+import subprocess
+
+def build_extension(src, dst, blender_executable_path):
+ """
+ Builds an extension using Blender's executable with specified source and destination paths.
+
+ Parameters:
+ src (str): Path to the source directory of the extension.
+ dst (str): Destination path for the built extension.
+ blender_executable_path (str): Path to the Blender executable.
+
+ Returns:
+ subprocess.CompletedProcess: The result of the subprocess command execution.
+ """
+ command = [
+ blender_executable_path,
+ '--command', 'extension', 'build',
+ '--source-dir', src,
+ '--output-filepath', dst,
+ ]
+ result = subprocess.run(command, capture_output=True, text=True)
+ return result
+
+def get_build_file(build_result):
+ """
+ Extracts the path of the created build file from the build result output.
+
+ Parameters:
+ build_result (subprocess.CompletedProcess): The result of the build command.
+
+ Returns:
+ str: The path of the created build file, if found; otherwise, None.
+ """
+ match = re.search(r'created: "([^"]+)"', build_result.stdout)
+ if match:
+ return match.group(1)
+ return None
+
+def validate_extension(path, blender_executable_path):
+ """
+ Validates the built extension using Blender's executable.
+
+ Parameters:
+ path (str): Path to the extension file to validate.
+ blender_executable_path (str): Path to the Blender executable.
+ """
+ validate_command = [
+ blender_executable_path,
+ '--command', 'extension', 'validate',
+ path,
+ ]
+ result = subprocess.run(validate_command, capture_output=True, text=True)
+
+ # Output results for debugging purposes
+ if result.returncode == 0:
+ print("Validation successful.")
+ else:
+ print(f"Validation failed. Error: {result.stderr}", file=sys.stderr)
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbam/blender_utils.py b/blender-for-unrealengine/bbam/blender_utils.py
new file mode 100644
index 00000000..59f85338
--- /dev/null
+++ b/blender-for-unrealengine/bbam/blender_utils.py
@@ -0,0 +1,66 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import os
+
+def uninstall_addon_from_blender(bpy, pkg_id, module):
+ """
+ Uninstalls an addon from Blender, using the correct method based on Blender's version.
+
+ Parameters:
+ bpy (module): Blender's Python API module.
+ pkg_id (str): Package ID for the extension (used in newer Blender versions).
+ module (str): Name of the addon module to uninstall.
+ """
+ # For Blender version 4.2.0 and above, use `package_uninstall`
+ if bpy.app.version >= (4, 2, 0):
+ print(f"Uninstalling extension '{pkg_id}'...")
+ bpy.ops.extensions.package_uninstall(repo_index=1, pkg_id=pkg_id)
+ bpy.ops.preferences.addon_remove(module=module)
+ else:
+ # For earlier versions, directly remove the addon using `addon_remove`
+ print(f"Uninstalling add-on '{module}'...")
+ bpy.ops.preferences.addon_remove(module=module)
+
+def install_zip_addon_from_blender(bpy, zip_file, module):
+ """
+ Installs a ZIP addon file in Blender, using the correct method based on Blender's version.
+
+ Parameters:
+ bpy (module): Blender's Python API module.
+ zip_file (str): Path to the ZIP file containing the addon.
+ module (str): Name of the addon module to enable after installation.
+ """
+ if bpy.app.version >= (4, 2, 0):
+ # For Blender version 4.2.0 and above, install as an extension
+ print("Installing as extension...", zip_file)
+ bpy.ops.extensions.package_install_files(repo="user_default", filepath=zip_file, enable_on_install=True)
+ print("Extension installation complete.")
+ else:
+ # For earlier versions, install and enable as an addon
+ print("Installing as add-on...", zip_file)
+ bpy.ops.preferences.addon_install(overwrite=True, filepath=zip_file)
+ bpy.ops.preferences.addon_enable(module=module)
+ print("Add-on installation complete.")
diff --git a/blender-for-unrealengine/bbam/config.py b/blender-for-unrealengine/bbam/config.py
new file mode 100644
index 00000000..04235c04
--- /dev/null
+++ b/blender-for-unrealengine/bbam/config.py
@@ -0,0 +1,38 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+# Configuration file names and paths for the addon build process
+
+# JSON file containing addon generation configurations
+addon_generate_config = "addon_generate_config.json"
+
+# TOML file containing the Blender manifest details
+blender_manifest = "blender_manifest.toml"
+
+# Version of the manifest schema being used
+manifest_schema_version = "1.0.0"
+
+# Folder where the generated build files will be stored
+build_output_folder = "generated_builds"
diff --git a/blender-for-unrealengine/bbam/exec/install_from_blender.py b/blender-for-unrealengine/bbam/exec/install_from_blender.py
new file mode 100644
index 00000000..3c091d05
--- /dev/null
+++ b/blender-for-unrealengine/bbam/exec/install_from_blender.py
@@ -0,0 +1,65 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import os
+import sys
+import importlib.util
+
+# get bbam __init__.py file
+bbam_path = os.path.abspath(os.path.join(__file__, '..', '..', '__init__.py'))
+module_name = "bbam"
+
+# Load and run bbam
+spec = importlib.util.spec_from_file_location(module_name, bbam_path)
+module = importlib.util.module_from_spec(spec)
+sys.modules[module_name] = module
+spec.loader.exec_module(module)
+module.install_from_blender()
+
+
+# Instructions for running this script from Blender
+'''
+# Run this script in Blender to generate and install the addon build.
+# Ensure the paths in `addon_directories` point to the correct addon directories.
+# For more details, visit the GitHub repository: https://github.com/xavier150/BBAM
+
+import os
+import importlib.util
+
+# List of addon paths using BBAM
+addon_directories = [
+ # Uncomment and adjust paths as needed
+ r"P:/GitHubBlenderAddon/Blender-For-UnrealEngine-Addons/blender-for-unrealengine",
+ # r"M:/MMVS_ProjectFiles/Other/BlenderForMMVS",
+ # r"P:/GitHubBlenderAddon/Modular-Auto-Rig/modular-auto-rig",
+]
+
+for dir in addon_directories:
+ # Install or reinstall the addon
+ script_path = os.path.join(dir, "bbam/exec/install_from_blender.py")
+ spec = importlib.util.spec_from_file_location("install_from_blender", script_path)
+ module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(module)
+'''
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbam/manifest_generate.py b/blender-for-unrealengine/bbam/manifest_generate.py
new file mode 100644
index 00000000..4d9b8e9a
--- /dev/null
+++ b/blender-for-unrealengine/bbam/manifest_generate.py
@@ -0,0 +1,106 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import os
+import toml
+from toml.encoder import TomlEncoder
+
+from . import config
+from . import utils
+from . import blender_utils
+
+class MultiLineTomlEncoder(TomlEncoder):
+ """
+ Custom TOML encoder that formats lists to display on multiple lines in the output.
+ """
+ def dump_list(self, v):
+ """
+ Formats a list to display each item on a new line in the TOML output.
+
+ Parameters:
+ v (list): The list to format for multi-line display.
+
+ Returns:
+ str: A formatted multi-line string representation of the list.
+ """
+ output = "[\n"
+ for item in v:
+ output += f" {toml.encoder._dump_str(item)},\n" # Indent each item
+ output += "]"
+ return output
+
+def generate_new_manifest(addon_generate_config_data, target_build_name):
+ """
+ Generates a new manifest dictionary for the addon based on the configuration data.
+
+ Parameters:
+ addon_generate_config_data (dict): The configuration data for the addon.
+ target_build_name (str): The name of the target build.
+
+ Returns:
+ dict: A dictionary representing the new manifest for the addon.
+ """
+ # Check if the target build data exists
+ if target_build_name not in addon_generate_config_data["builds"]:
+ print(f"Error: Build data for '{target_build_name}' not found!")
+ return {}
+
+ data = {}
+ manifest_data = addon_generate_config_data["blender_manifest"]
+ build_data = addon_generate_config_data["builds"][target_build_name]
+
+ # Populate generic information
+ data["schema_version"] = config.manifest_schema_version
+ data["id"] = manifest_data["id"]
+ data["version"] = utils.get_str_version(manifest_data["version"])
+ data["name"] = manifest_data["name"]
+ data["maintainer"] = manifest_data["maintainer"]
+ data["tagline"] = manifest_data["tagline"]
+ data["website"] = manifest_data["website_url"]
+ data["type"] = manifest_data["type"]
+ data["tags"] = manifest_data["tags"]
+ data["permissions"] = manifest_data["permissions"]
+ data["blender_version_min"] = utils.get_str_version(build_data["blender_version_min"])
+ data["license"] = manifest_data["license"]
+ data["copyright"] = manifest_data["copyright"]
+ return data
+
+def save_addon_manifest(addon_path, data, show_debug=False):
+ """
+ Saves the addon manifest as a TOML file using the MultiLineTomlEncoder for custom formatting.
+
+ Parameters:
+ addon_path (str): Path to the addon's root folder.
+ data (dict): Manifest data to save as a TOML file.
+ show_debug (bool): If True, displays debug information about the save process.
+ """
+ addon_manifest_path = os.path.join(addon_path, config.blender_manifest)
+
+ # Save the manifest as a TOML file with custom encoder for multi-line list formatting
+ with open(addon_manifest_path, "w") as file:
+ toml.dump(data, file, encoder=MultiLineTomlEncoder())
+
+ if show_debug:
+ print(f"Addon manifest saved successfully at: {addon_manifest_path}")
diff --git a/blender-for-unrealengine/bbam/utils.py b/blender-for-unrealengine/bbam/utils.py
new file mode 100644
index 00000000..cbb1a226
--- /dev/null
+++ b/blender-for-unrealengine/bbam/utils.py
@@ -0,0 +1,67 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBAM -> BleuRaven Blender Addon Manager
+# https://github.com/xavier150/BBAM
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+def get_str_version(data):
+ """
+ Converts a list of version components into a version string.
+
+ Parameters:
+ data (list): A list of integers representing the version, e.g., [1, 2, 3].
+
+ Returns:
+ str: A string representation of the version, e.g., "1.2.3".
+ """
+ return f'{data[0]}.{data[1]}.{data[2]}'
+
+
+def get_tuple_range_version(data):
+ """
+ Converts version range data into a list of version tuples.
+
+ Parameters:
+ data (list): A list of two lists, each representing a version range,
+ e.g., [[1, 0, 0], [2, 0, 0]].
+
+ Returns:
+ list: A list of tuples representing the version range,
+ e.g., [(1, 0, 0), (2, 0, 0)].
+ """
+ return [tuple(data[0]), tuple(data[1])]
+
+
+def get_version_in_range(version, range):
+ """
+ Checks if a given version is within a specified version range.
+
+ Parameters:
+ version (tuple): A tuple representing the current version, e.g., (1, 2, 0).
+ range (list): A list of two tuples representing the minimum and maximum versions,
+ e.g., [(1, 0, 0), (2, 0, 0)].
+
+ Returns:
+ bool: True if the version is within the specified range; False otherwise.
+ """
+ min_version, max_version = range
+ return min_version <= version <= max_version
diff --git a/blender-for-unrealengine/bbpl/__init__.py b/blender-for-unrealengine/bbpl/__init__.py
index 64a28bcc..94793df2 100644
--- a/blender-for-unrealengine/bbpl/__init__.py
+++ b/blender-for-unrealengine/bbpl/__init__.py
@@ -30,6 +30,7 @@
from . import backward_compatibility
from . import blender_rig
from . import blender_addon
+from . import save_data
from . import blender_extension
from . import basics
from . import utils
@@ -51,6 +52,8 @@
importlib.reload(blender_addon)
if "blender_extension" in locals():
importlib.reload(blender_extension)
+if "save_data" in locals():
+ importlib.reload(save_data)
if "basics" in locals():
importlib.reload(basics)
if "utils" in locals():
diff --git a/blender-for-unrealengine/bbpl/__internal__/utils.py b/blender-for-unrealengine/bbpl/__internal__/utils.py
index eea63982..bb4f5a06 100644
--- a/blender-for-unrealengine/bbpl/__internal__/utils.py
+++ b/blender-for-unrealengine/bbpl/__internal__/utils.py
@@ -23,23 +23,35 @@
# ----------------------------------------------
import bpy
+from .. import __package__ as base_package
def get_package_name():
- package_name = __package__
- package_name = package_name.split(".")[0] # Isolating 'addon name'
+ # Before 4.2 __package__ will look like that:
+ # my_blender_addon.bbpl.__internal__
+
+ # After 4.2 __package__ will look like that:
+ # bl_ext.user_default.my_blender_addon.bbpl.__internal__
+
+ package_name = base_package.split(".")[-1]
return package_name
def get_reduced_package_name():
package_name = get_package_name()
- # From blender-for-unrealengine
- # To bdfunr
- parts = package_name.split("-")
+ # blender-for-unrealengine -> bdfunr
+ # unrealengine_assets_exporter -> unrassexp
+
+ separators = ["-", "_", "."]
+ for sep in separators:
+ package_name = package_name.replace(sep, " ")
+ parts = package_name.split()
special_reductions = {
"blender": "bd",
"for": "f",
"to": "t",
+ "assets": "ass",
+ "asset": "as",
}
reduced_parts = []
@@ -50,7 +62,6 @@ def get_reduced_package_name():
reduced_name = ''.join(reduced_parts).lower()[:12] # Max length is 12
return reduced_name
-
def get_operator_class_name(name):
package_name = get_reduced_package_name()
return f"BBPL_OT_{package_name}_{name}"
diff --git a/blender-for-unrealengine/bbpl/anim_utils.py b/blender-for-unrealengine/bbpl/anim_utils.py
index 0cd494f6..736fbe55 100644
--- a/blender-for-unrealengine/bbpl/anim_utils.py
+++ b/blender-for-unrealengine/bbpl/anim_utils.py
@@ -146,7 +146,7 @@ def __init__(self, nla_strip: bpy.types.NlaStrip):
self.fcurves = []
# Since 3.5 interact to a NlaStripFCurves not linked to an object produce Blender Crash.
for fcurve in nla_strip.fcurves:
- self.fcurves.append(ProxyCopy_FCurve(fcurve))
+ self.fcurves.append(ProxyCopy_StripFCurve(fcurve))
self.frame_end = nla_strip.frame_end
if bpy.app.version >= (3, 3, 0):
self.frame_end_ui = nla_strip.frame_end_ui
@@ -181,15 +181,14 @@ def paste_data_on(self, nla_strip: bpy.types.NlaStrip):
nla_strip.blend_type = self.blend_type
nla_strip.extrapolation = self.extrapolation
for fcurve in self.fcurves:
- new_fcurve = nla_strip.fcurves.find(fcurve.data_path) # Can't create so use find
- fcurve.paste_data_on(new_fcurve)
+ fcurve.paste_data_on(nla_strip)
nla_strip.frame_end = self.frame_end
if bpy.app.version >= (3, 3, 0):
nla_strip.frame_end_ui = self.frame_end_ui
nla_strip.frame_start = self.frame_start
if bpy.app.version >= (3, 3, 0):
nla_strip.frame_start_ui = self.frame_start_ui
- nla_strip.influence = self.influence
+ nla_strip.influence = self.influence
# nla_strip.modifiers = self.modifiers #TO DO
nla_strip.mute = self.mute
# nla_strip.name = self.name
@@ -208,18 +207,65 @@ def paste_data_on(self, nla_strip: bpy.types.NlaStrip):
nla_strip.use_sync_length = self.use_sync_length
+class ProxyCopy_StripFCurve():
+ """
+ Proxy class for copying bpy.types.NlaStripFCurves. (NLA Strip only)
+
+ It is used to safely copy the bpy.types.NlaStripFCurves struct.
+ """
+
+ def __init__(self, fcurve: bpy.types.NlaStripFCurves):
+ self.data_path = fcurve.data_path
+ self.keyframe_points = []
+ for keyframe_point in fcurve.keyframe_points:
+ self.keyframe_points.append(ProxyCopy_Keyframe(keyframe_point))
+
+ def paste_data_on(self, strips: bpy.types.NlaStrips):
+ if self.data_path == "influence":
+ # Create the curve with use_animated_influence
+ strips.use_animated_influence = True
+
+ for key in self.keyframe_points:
+ strips.influence = key.co[1]
+ strips.keyframe_insert(data_path="influence", frame=key.co[0], keytype=key.type)
+
+
+
class ProxyCopy_FCurve():
"""
- Proxy class for copying bpy.types.FCurve.
+ Proxy class for copying bpy.types.FCurve.
It is used to safely copy the bpy.types.FCurve struct.
"""
def __init__(self, fcurve: bpy.types.FCurve):
self.data_path = fcurve.data_path
+ self.keyframe_points = []
+ for keyframe_point in fcurve.keyframe_points:
+ self.keyframe_points.append(ProxyCopy_Keyframe(keyframe_point))
def paste_data_on(self, fcurve: bpy.types.FCurve):
- pass
+ fcurve.data_path = self.data_path
+ for keyframe_point in self.keyframe_points:
+ pass
+ #TODO
+
+
+class ProxyCopy_Keyframe():
+ """
+ Proxy class for copying bpy.types.Keyframe. (NLA Strip only)
+
+ It is used to safely copy the bpy.types.Keyframe struct.
+ """
+
+ def __init__(self, keyframe: bpy.types.Keyframe):
+ self.co = keyframe.co
+ self.type = keyframe.type
+
+ def paste_data_on(self, keyframe: bpy.types.Keyframe):
+ keyframe.co = self.co
+ keyframe.type = self.type
+
def copy_attributes(a, b):
diff --git a/blender-for-unrealengine/bbpl/backward_compatibility/__init__.py b/blender-for-unrealengine/bbpl/backward_compatibility/__init__.py
index 6214b06c..9b8145c9 100644
--- a/blender-for-unrealengine/bbpl/backward_compatibility/__init__.py
+++ b/blender-for-unrealengine/bbpl/backward_compatibility/__init__.py
@@ -23,6 +23,7 @@
# ----------------------------------------------
import bpy
+from typing import List
import importlib
classes = (
@@ -63,7 +64,7 @@ def __init__(self):
self.remove_fcurve = 0
self.print_log = False
- def update_action_curve_data_path(self, action, old_data_paths, new_data_path, remove_if_already_exists=False):
+ def update_action_curve_data_path(self, action: bpy.types.Action, old_data_paths: List[str], new_data_path: str, remove_if_already_exists=False, show_debug=False):
"""
Update the data paths of FCurves in a given action by replacing old data paths with a new one.
@@ -77,36 +78,40 @@ def update_action_curve_data_path(self, action, old_data_paths, new_data_path, r
Returns:
None
"""
- cache_action_fcurves = []
- cache_data_paths = []
+ cache_action_fcurves: List[bpy.types.FCurve] = []
+ cache_data_paths: List[str] = []
for fcurve in action.fcurves:
cache_action_fcurves.append(fcurve)
cache_data_paths.append(fcurve.data_path)
-
for action_fcurve in cache_action_fcurves:
for old_data_path in old_data_paths:
current_target = action_fcurve.data_path
- if old_data_path in current_target:
+ if old_data_path in current_target:
# ---
+ if show_debug:
+ print(f"{old_data_path} found in {current_target} for action {action.name}.")
new_target = current_target.replace(old_data_path, new_data_path)
if new_target not in cache_data_paths:
action_fcurve.data_path = new_target
- if self.print_log:
+ if self.print_log or show_debug:
print(f'"{current_target}" updated to "{new_target}" in {action.name} action.')
self.update_fcurve += 1
else:
if remove_if_already_exists:
action.fcurves.remove(action_fcurve)
- if self.print_log:
+ if self.print_log or show_debug:
print(f'"{current_target}" can not be updated to "{new_target}" in {action.name} action. (Alredy exist!) It was removed in {action.name} action.')
self.remove_fcurve += 1
break #FCurve removed so no neew to test the other old_var_names
else:
- if self.print_log:
+ if self.print_log or show_debug:
print(f'"{current_target}" can not be updated to "{new_target}" in {action.name} action. (Alredy exist!)')
+ else:
+ if show_debug:
+ print(f"{old_data_path} not found in {current_target} for action {action.name}.")
def remove_action_curve_by_data_path(self, action, data_paths):
"""
diff --git a/blender-for-unrealengine/bbpl/basics.py b/blender-for-unrealengine/bbpl/basics.py
index f03628c1..35cffb27 100644
--- a/blender-for-unrealengine/bbpl/basics.py
+++ b/blender-for-unrealengine/bbpl/basics.py
@@ -29,21 +29,7 @@
import bmesh
import addon_utils
import pathlib
-
-def is_deleted(obj):
- """
- Checks if the specified Blender object has been deleted.
-
- Args:
- obj (bpy.types.Object): The Blender object to check.
-
- Returns:
- bool: True if the object has been deleted, False otherwise.
- """
- if obj and obj is not None:
- return obj.name not in bpy.data.objects
- else:
- return True
+from typing import Optional
def check_plugin_is_activated(plugin_name):
@@ -104,8 +90,9 @@ def get_childs(obj):
Returns:
list: A list of direct children objects.
"""
+ scene = bpy.context.scene
childs_obj = []
- for child_obj in bpy.data.objects:
+ for child_obj in scene.objects:
if child_obj.library is None:
parent = child_obj.parent
if parent is not None:
@@ -115,56 +102,76 @@ def get_childs(obj):
return childs_obj
-def get_root_bone_parent(bone):
+def get_armature_root_bone(obj):
"""
- Retrieves the root bone parent of a given bone.
+ Retrieves the root bone of an armature object.
Args:
- bone (bpy.types.Bone): The bone to find the root bone parent for.
+ obj (bpy.types.Object): The armature object to find the root bone for.
Returns:
- bpy.types.Bone: The root bone parent.
+ bpy.types.Bone: The root bone of the armature, or None if not found.
"""
- if bone.parent is not None:
- return get_root_bone_parent(bone.parent)
- return bone
+ # Vérifie si l'objet est une armature et s'il a des données d'armature
+ if obj.type == 'ARMATURE' and obj.data:
+ armature = obj.data
+
+ # Parcours tous les os de l'armature pour trouver le(s) root(s)
+ for bone in armature.bones:
+ if bone.parent is None:
+ return bone
+ return None
-def get_first_deform_bone_parent(bone):
+def get_armature_root_bone(obj: bpy.types.Object) -> Optional[bpy.types.Bone]:
"""
- Retrieves the first deform bone parent of a given bone.
+ Retrieves the root bone of an armature object.
Args:
- bone (bpy.types.Bone): The bone to find the first deform bone parent for.
+ obj (bpy.types.Object): The armature object to find the root bone for.
Returns:
- bpy.types.Bone: The first deform bone parent.
+ bpy.types.Bone: The root bone of the armature, or None if not found.
"""
- if bone.parent is not None:
- if bone.use_deform is True:
- return bone
- else:
- return get_first_deform_bone_parent(bone.parent)
+ if obj.type == 'ARMATURE' and obj.data:
+ armature = obj.data
+
+ for bone in armature.bones:
+ if bone.parent is None:
+ return bone
+ return None
+
+
+def get_root_bone_parent(bone: bpy.types.Bone) -> bpy.types.Bone:
+ """
+ Retrieves the root bone parent of a given bone by traversing the bone's parents.
+
+ Args:
+ bone (bpy.types.Bone): The bone to find the root bone parent for.
+
+ Returns:
+ bpy.types.Bone: The root bone parent.
+ """
+ while bone.parent:
+ bone = bone.parent
return bone
-def set_collection_use(collection):
+def get_first_deform_bone_parent(bone: bpy.types.Bone) -> Optional[bpy.types.Bone]:
"""
- Sets the visibility and selectability of a collection.
+ Retrieves the first deform bone parent of a given bone by traversing the bone's parents.
Args:
- collection (bpy.types.Collection): The collection to modify.
+ bone (bpy.types.Bone): The bone to find the first deform bone parent for.
Returns:
- None
+ bpy.types.Bone: The first deform bone parent, or None if not found.
"""
- collection.hide_viewport = False
- collection.hide_select = False
- layer_collection = bpy.context.view_layer.layer_collection
- if collection.name in layer_collection.children:
- layer_collection.children[collection.name].hide_viewport = False
- else:
- print(collection.name, "not found in view_layer.layer_collection")
+ while bone.parent:
+ if bone.use_deform:
+ return bone
+ bone = bone.parent
+ return bone if bone.use_deform else None
def get_recursive_childs(target_obj):
@@ -186,8 +193,9 @@ def get_recursive_parent(parent, start_obj):
return True
return False
+ scene = bpy.context.scene
save_objs = []
- for obj in bpy.data.objects:
+ for obj in scene.objects:
if get_recursive_parent(target_obj, obj):
save_objs.append(obj)
return save_objs
@@ -313,15 +321,16 @@ def set_windows_clipboard(text):
def get_obj_childs(obj):
# Get all direct childs of a object
- ChildsObj = []
- for childObj in bpy.data.objects:
+ scene = bpy.context.scene
+ childs_obj = []
+ for childObj in scene.objects:
if childObj.library is None:
pare = childObj.parent
if pare is not None:
if pare.name == obj.name:
- ChildsObj.append(childObj)
+ childs_obj.append(childObj)
- return ChildsObj
+ return childs_obj
def get_recursive_obj_childs(obj, include_self = False):
# Get all recursive childs of a object
diff --git a/blender-for-unrealengine/bbpl/blender_addon/addon_utils.py b/blender-for-unrealengine/bbpl/blender_addon/addon_utils.py
index 4997bfa0..61d3389f 100644
--- a/blender-for-unrealengine/bbpl/blender_addon/addon_utils.py
+++ b/blender-for-unrealengine/bbpl/blender_addon/addon_utils.py
@@ -26,6 +26,7 @@
import os
import bpy
import addon_utils
+from .. import __internal__
def get_addon_version(addon_name):
diff --git a/blender-for-unrealengine/bbpl/blender_extension/extension_utils.py b/blender-for-unrealengine/bbpl/blender_extension/extension_utils.py
index f91aa745..2ebed6e2 100644
--- a/blender-for-unrealengine/bbpl/blender_extension/extension_utils.py
+++ b/blender-for-unrealengine/bbpl/blender_extension/extension_utils.py
@@ -25,23 +25,43 @@
import os
import bpy
+from ... import __package__ as base_package
-
-def get_package_version(pkg_id):
-
+def get_package_version(pkg_idname = None, repo_module = 'user_default'):
+ if bpy.app.version < (4, 2, 0):
+ print("Blender extensions are not supported under 4.2. Please use bbpl.blender_addon.addon_utils instead.")
+ return None
+
+ manifest_filename = "blender_manifest.toml"
+
+ if pkg_idname:
+ file_path = os.path.join(bpy.utils.user_resource('EXTENSIONS'), repo_module, pkg_idname, manifest_filename)
+ else:
+ from addon_utils import _extension_module_name_decompose
+ repo_module, pkg_idname = _extension_module_name_decompose(base_package)
+ file_path = os.path.join(bpy.utils.user_resource('EXTENSIONS'), repo_module, pkg_idname, manifest_filename)
+
version = None
+ if os.path.isfile(file_path):
+ with open(file_path, 'r') as file:
+ for line in file:
+ if line.startswith("version"):
+ version = line.split('=')[1].strip().strip('"')
+ break
+ else:
+ print(f"File {file_path} does not exist.")
+
+ return version
- # @TODO this look like a bad way to do this. Need found how use bpy.ops.extensions.
- file_path = os.path.join(bpy.utils.user_resource('EXTENSIONS'), 'user_default', pkg_id, "blender_manifest.toml")
- with open(file_path, 'r') as file:
- for line in file:
- if line.startswith("version"):
- # Extraire la partie droite de la ligne après le signe '=' et enlever les espaces et guillemets
- version = line.split('=')[1].strip().strip('"')
- break
+def get_package_path(pkg_idname = None, repo_module = 'user_default'):
+ if bpy.app.version < (4, 2, 0):
+ print("Blender extensions are not supported under 4.2. Please use bbpl.blender_addon.addon_utils instead.")
+ return None
- return version
+ if pkg_idname:
+ return os.path.join(bpy.utils.user_resource('EXTENSIONS'), repo_module, pkg_idname)
+ else:
+ from addon_utils import _extension_module_name_decompose
+ repo_module, pkg_idname = _extension_module_name_decompose(base_package)
+ return os.path.join(bpy.utils.user_resource('EXTENSIONS'), repo_module, pkg_idname)
-def get_package_path(pkg_id):
- # @TODO this look like a bad way to do this. Need found how use bpy.ops.extensions.
- return os.path.join(bpy.utils.user_resource('EXTENSIONS'), 'user_default', pkg_id)
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbpl/blender_layout/layout_selector/functions.py b/blender-for-unrealengine/bbpl/blender_layout/layout_selector/functions.py
index 741ecb43..10170f0e 100644
--- a/blender-for-unrealengine/bbpl/blender_layout/layout_selector/functions.py
+++ b/blender-for-unrealengine/bbpl/blender_layout/layout_selector/functions.py
@@ -30,12 +30,13 @@
-def add_string_selector(property_name, property_selector_name, default: str="", name: str="", description: str="", items=[]) -> types.StringSelector:
+def add_string_selector(property_name, property_selector_name, default: str="", name: str="", description: str="", items=[], update=None) -> types.StringSelector:
my_string_selector = types.StringSelector(property_name, property_selector_name)
my_string_selector.name = name
my_string_selector.default = default
my_string_selector.description = description
my_string_selector.items = items
+ my_string_selector.update = update
my_string_selector.create_propertys()
return my_string_selector
diff --git a/blender-for-unrealengine/bbpl/blender_layout/layout_selector/types.py b/blender-for-unrealengine/bbpl/blender_layout/layout_selector/types.py
index ad4be687..55bc2586 100644
--- a/blender-for-unrealengine/bbpl/blender_layout/layout_selector/types.py
+++ b/blender-for-unrealengine/bbpl/blender_layout/layout_selector/types.py
@@ -26,23 +26,19 @@
from . import utils
from ... import __internal__
-def update_string_from_selector(self, context, string_selector):
- if context.region.type != "UI":
- return
+def update_string_from_selector(self, string_selector):
string_name = string_selector.property_name
selector_name = string_selector.property_selector_name
if getattr(self, string_name) != getattr(self, selector_name):
setattr(self, string_name, getattr(self, selector_name))
- print("Selector update...")
+ #print("Selector update...")
-def update_selector_from_string(self, context, string_selector):
- if context.region.type != "UI":
- return
+def update_selector_from_string(self, string_selector):
string_name = string_selector.property_name
selector_name = string_selector.property_selector_name
if getattr(self, selector_name) != getattr(self, string_name):
setattr(self, selector_name, getattr(self, string_name))
- print("Selector update...")
+ #print("Selector update...")
class StringSelector():
@@ -53,6 +49,7 @@ def __init__(self, property_name, property_selector_name):
self.default = ""
self.description = ""
self.items = []
+ self.update = None
self.string_property = None
self.enum_selector = None
@@ -60,10 +57,15 @@ def __init__(self, property_name, property_selector_name):
def create_propertys(self):
string_selector = self
def string_update_wrapper(self, context):
- update_selector_from_string(self, context, string_selector)
+ update_selector_from_string(self, string_selector)
+ if string_selector.update:
+ string_selector.update()
def selector_update_wrapper(self, context):
- update_string_from_selector(self, context, string_selector)
+ update_string_from_selector(self, string_selector)
+ if string_selector.update:
+ string_selector.update()
+
self.string_property = bpy.props.StringProperty(
default=self.default,
diff --git a/blender-for-unrealengine/bbpl/blender_layout/layout_template_list/types.py b/blender-for-unrealengine/bbpl/blender_layout/layout_template_list/types.py
index db7b4f61..ac332d10 100644
--- a/blender-for-unrealengine/bbpl/blender_layout/layout_template_list/types.py
+++ b/blender-for-unrealengine/bbpl/blender_layout/layout_template_list/types.py
@@ -70,7 +70,7 @@ class BBPL_UI_TemplateItem(bpy.types.PropertyGroup):
return BBPL_UI_TemplateItem
def create_template_item_draw_class():
- class BBPL_UI_TemplateItemDraw(bpy.types.UIList):
+ class BBPL_UL_TemplateItemDraw(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
prop_line = layout
@@ -92,15 +92,14 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn
prop_data.prop(item, "name", text="")
prop_data.enabled = item.use
- BBPL_UI_TemplateItemDraw.__name__ = utils.get_operator_class_name("TemplateItemDraw")
- return BBPL_UI_TemplateItemDraw
+ BBPL_UL_TemplateItemDraw.__name__ = utils.get_operator_class_name("TemplateItemDraw")
+ return BBPL_UL_TemplateItemDraw
def create_template_list_class(TemplateItem, TemplateItemDraw):
class BBPL_UI_TemplateList(bpy.types.PropertyGroup):
template_collection: bpy.props.CollectionProperty(type = TemplateItem)
- template_collection_uilist_class: bpy.props.StringProperty(default = TemplateItemDraw.__name__)
- #template_collection_uilist_class: bpy.props.StringProperty(default = "BBPL_UI_TemplateItemDraw")
+ template_collection_uilist_class_name = ""
active_template_property: bpy.props.IntProperty(default = 0)
rows: bpy.props.IntProperty(default = 6)
maxrows: bpy.props.IntProperty(default = 6)
@@ -146,13 +145,17 @@ def get_name(self):
def draw(self, layout: bpy.types.UILayout):
template_row = layout.row()
- template_row.template_list(
- self.template_collection_uilist_class, "", # type and unique id
- self, "template_collection", # pointer to the CollectionProperty
- self, "active_template_property", # pointer to the active identifier
- rows=self.rows,
- maxrows=self.maxrows,
- )
+ if self.template_collection_uilist_class_name == "":
+ print("template_collection_uilist_class_name was not set!")
+
+ else:
+ template_row.template_list(
+ self.template_collection_uilist_class_name, "", # type and unique id
+ self, "template_collection", # pointer to the CollectionProperty
+ self, "active_template_property", # pointer to the active identifier
+ rows=self.rows,
+ maxrows=self.maxrows,
+ )
template_column = template_row.column(align=True)
diff --git a/blender-for-unrealengine/bbpl/blender_rig/rig_utils.py b/blender-for-unrealengine/bbpl/blender_rig/rig_utils.py
index 2906f20a..566adf5f 100644
--- a/blender-for-unrealengine/bbpl/blender_rig/rig_utils.py
+++ b/blender-for-unrealengine/bbpl/blender_rig/rig_utils.py
@@ -55,51 +55,35 @@ def create_safe_bone(arm, bone_name, context_id=None):
return bone
-
def get_mirror_bone_name(original_bones):
"""
Get the mirror bone name for the given bone(s).
"""
- bones = []
- new_bones = []
-
if not isinstance(original_bones, list):
bones = [original_bones] # Convert to list
else:
bones = original_bones
def try_to_invert_bones(bone):
- def invert(bone, old, new):
- if bone.endswith(old):
- new_bone_name = bone[:-len(old)]
- new_bone_name = new_bone_name + new
- return new_bone_name
- return None
-
change = [
- ["_l", "_r"],
- ["_L", "_R"]
+ ("_l", "_r"),
+ ("_L", "_R")
]
- for c in change:
- a = invert(bone, c[0], c[1])
- if a:
- return a
- b = invert(bone, c[1], c[0])
- if b:
- return b
-
- # Return original If no invert found.
- return bone
- for bone in bones:
- new_bones.append(try_to_invert_bones(bone))
+ for old, new in change:
+ if bone.endswith(old):
+ return bone[:-len(old)] + new
+ elif bone.endswith(new):
+ return bone[:-len(new)] + old
- # Can return same bone when don't found mirror
- if not isinstance(original_bones, list):
- return new_bones[0]
- else:
- return new_bones
+ # Return original if no invert found
+ return bone
+
+ # Using list comprehension for performance
+ new_bones = [try_to_invert_bones(bone) for bone in bones]
+ # Return a single element if the input was not a list
+ return new_bones[0] if not isinstance(original_bones, list) else new_bones
def get_name_with_new_prefix(name, old_prefix, new_prefix):
"""
@@ -114,7 +98,6 @@ def get_name_with_new_prefix(name, old_prefix, new_prefix):
raise TypeError('"' + old_prefix + '" not found as prefix in "' + name + '".')
return new_bone_name
-
def get_name_list_with_new_prefix(name_list, old_prefix, new_prefix):
"""
Replace a prefix and add a new prefix to each name in a list.
@@ -125,7 +108,6 @@ def get_name_list_with_new_prefix(name_list, old_prefix, new_prefix):
new_list.append(get_name_with_new_prefix(name, old_prefix, new_prefix))
return new_list
-
def no_num(name):
"""
Remove the number index from a bone name.
@@ -181,7 +163,6 @@ def change_current_layer(layer, source):
if i != layer:
source.layers[i] = False
-
def change_select_layer(layer):
"""
Change the active bone layer in the armature to the specified layer.
@@ -189,14 +170,12 @@ def change_select_layer(layer):
layer_values = [layer == i for i in range(32)]
bpy.ops.armature.bone_layers(layers=layer_values)
-
def change_user_view_layer(layer):
"""
Change the active layer in the user view to the specified layer.
"""
change_current_layer(layer, bpy.context.object.data)
-
def duplicate_rig_layer(armature, original_layer, new_layer, old_prefix, new_prefix):
"""
Duplicates the bones in the specified original layer of the armature and moves them to the new layer.
@@ -235,6 +214,7 @@ def duplicate_rig_layer(armature, original_layer, new_layer, old_prefix, new_pre
armature.data.pose_position = 'POSE' # Set the pose position back to pose mode
return new_bone_names
+
class Orig_prefixhanBone():
"""
Create a new Orig_prefixhanBone instance.
@@ -268,7 +248,6 @@ def set_bone_orientation(armature, bone_name, vector, roll):
bone.tail = bone.head + vector * length
bone.roll = roll
-
def get_bone_with_length(armature, bone_name, new_length, apply_tail=True):
"""
Evaluate the edit_bone tail position with specific length
@@ -280,8 +259,6 @@ def get_bone_with_length(armature, bone_name, new_length, apply_tail=True):
new_tail = bone.head + (vector * new_length)
return new_tail
-
-
def set_bone_length(armature, bone_name, new_length):
"""
Définit la longueur d'un os dans l'armature.
@@ -303,7 +280,6 @@ def get_bone_vector(armature, bone_name):
tail = armature.data.edit_bones[bone_name].tail
return head - tail
-
def set_bone_scale(armature, bone_name, new_scale, apply_tail=True):
"""
Définit l'échelle d'un os dans l'armature.
@@ -336,7 +312,6 @@ def get_first_parent(bone):
else:
return bone
-
def create_simple_stretch(armature, bone, target_bone_name, name):
"""
Create a simple stretch constraint for a bone in an armature.
@@ -390,9 +365,9 @@ def apply_driver(self, bone_name):
description=self.description,
overridable=True)
- bone_const = self.bone_const_name
- constraints = self.constraint_name
- driver_value = 'pose.bones["' + bone_const + '"].constraints["' + constraints + '"].influence'
+ escaped_bone_const = bpy.utils.escape_identifier(self.bone_const_name)
+ escaped_constraints = bpy.utils.escape_identifier(self.constraint_name)
+ driver_value = f'pose.bones["{escaped_bone_const}"].constraints["{escaped_constraints}"].influence'
driver = self.armature.driver_add(driver_value).driver
set_driver(self.armature, driver, bone_name, self.property_name)
@@ -412,10 +387,11 @@ def create_bone_custom_property(armature, property_bone_name, property_name, def
soft_max=value_max,
description=description
)
- property_bone.property_overridable_library_set('["' + property_name + '"]', overridable)
-
- return 'pose.bones["' + property_bone_name + '"]["' + property_name + '"]'
-
+ escaped_property_name = bpy.utils.escape_identifier(property_name)
+ escaped_property_bone_name = bpy.utils.escape_identifier(property_bone_name)
+ property_bone.property_overridable_library_set(f'["{escaped_property_name}"]', overridable)
+ data_path = f'pose.bones["{escaped_property_bone_name}"]["{escaped_property_name}"]'
+ return data_path
def set_driver(armature, driver, bone_name, driver_name, clean_previous=True):
"""
@@ -423,14 +399,18 @@ def set_driver(armature, driver, bone_name, driver_name, clean_previous=True):
"""
if clean_previous:
utils.clear_driver_var(driver)
+
+ # Échapper les noms de bone et driver pour les utiliser en toute sécurité dans data_path
+ escaped_bone_name = bpy.utils.escape_identifier(bone_name)
+ escaped_driver_name = bpy.utils.escape_identifier(driver_name)
+
v = driver.variables.new()
v.name = driver_name.replace(" ", "_")
v.targets[0].id = armature
- v.targets[0].data_path = 'pose.bones["' + bone_name + '"]["' + driver_name + '"]'
- driver.expression = driver_name.replace(" ", "_")
+ v.targets[0].data_path = f'pose.bones["{escaped_bone_name}"]["{escaped_driver_name}"]'
+ driver.expression = v.name
return v
-
def subdivise_one_bone(armature, bone_name, subdivise_prefix_name="Subdivise_", split_number=2, keep_parent=True, ):
"""
Subdivides a bone into multiple segments.
@@ -484,7 +464,6 @@ def subdivise_one_bone(armature, bone_name, subdivise_prefix_name="Subdivise_",
# Final reparenting
return chain
-
def duplicate_bone(arm, bone_name, new_name=None):
"""
Creates a duplicate bone in the armature.
@@ -497,6 +476,7 @@ def duplicate_bone(arm, bone_name, new_name=None):
Returns:
str: The name of the created bone.
"""
+
edit_bone = arm.data.edit_bones[bone_name]
if new_name is None:
new_name = edit_bone.name + "_dup"
@@ -520,7 +500,6 @@ def duplicate_bone(arm, bone_name, new_name=None):
return new_bone.name
-
def copy_constraint(armature, copy_bone_name, paste_bone_name, clear=True):
"""
Copies constraints from one bone to another in the armature.
@@ -547,13 +526,10 @@ def copy_constraint(armature, copy_bone_name, paste_bone_name, clear=True):
# armature.pose.bones[paste_bone].constraints = armature.pose.bones[copy_bone].constraints
-
-
def set_bones_lock(armature, bone_names, lock):
for bone_name in bone_names:
set_bone_lock(armature, bone_name, lock)
-
def set_bone_lock(armature, bone_name, lock):
# Check if we are in Pose mode
if armature.mode != 'POSE':
diff --git a/blender-for-unrealengine/bbpl/rig_bone_visual.py b/blender-for-unrealengine/bbpl/rig_bone_visual.py
index ce8517dd..f4a0f18a 100644
--- a/blender-for-unrealengine/bbpl/rig_bone_visual.py
+++ b/blender-for-unrealengine/bbpl/rig_bone_visual.py
@@ -156,7 +156,7 @@ def generate_bone_shape_from_prop(
bone = armature.data.bones.get(bone_name)
new_shape_name = "Shape_CustomGeneratedShape_" + bone_name
- # Vérifier si la forme existe et la supprimer le cas échéant
+ # Check in all blender objs the form exists and delete it if so
if new_shape_name in bpy.data.objects:
bpy.data.objects.remove(bpy.data.objects[new_shape_name], do_unlink=True)
diff --git a/blender-for-unrealengine/bbpl/save_data/__init__.py b/blender-for-unrealengine/bbpl/save_data/__init__.py
new file mode 100644
index 00000000..feb7cb80
--- /dev/null
+++ b/blender-for-unrealengine/bbpl/save_data/__init__.py
@@ -0,0 +1,34 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBPL -> BleuRaven Blender Python Library
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import bpy
+import importlib
+
+from . import scene_save
+from . import select_save
+
+if "scene_save" in locals():
+ importlib.reload(scene_save)
+if "select_save" in locals():
+ importlib.reload(select_save)
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbpl/save_data/scene_save.py b/blender-for-unrealengine/bbpl/save_data/scene_save.py
new file mode 100644
index 00000000..e9442653
--- /dev/null
+++ b/blender-for-unrealengine/bbpl/save_data/scene_save.py
@@ -0,0 +1,262 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBPL -> BleuRaven Blender Python Library
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import bpy
+from typing import List, TYPE_CHECKING
+from . import select_save
+from .. import utils
+
+class SavedObject():
+ """
+ Saved data from a blender object.
+ """
+
+ def __init__(self, obj: bpy.types.Object):
+ if obj:
+ self.ref = obj
+ self.name = obj.name
+ self.select = obj.select_get()
+ self.hide = obj.hide_get()
+ self.hide_select = obj.hide_select
+ self.hide_viewport = obj.hide_viewport
+
+ def get_obj(self, use_names: bool = False)-> bpy.types.Object:
+ scene = bpy.context.scene
+ if use_names:
+ if self.name != "":
+ if self.name in scene.objects:
+ if self.name in bpy.context.view_layer.objects:
+ return scene.objects[self.name]
+ return None
+ else:
+ return self.ref
+
+
+
+
+class SavedBones():
+ """
+ Saved data from a blender armature bone.
+ """
+
+ def __init__(self, bone):
+ if bone:
+ self.name = bone.name
+ self.select = bone.select
+ self.hide = bone.hide
+
+
+class SavedCollection():
+ """
+ Saved data from a blender collection.
+ """
+
+ def __init__(self, col :bpy.types.Collection):
+ if col:
+ self.ref:bpy.types.Collection = col
+ self.name = col.name
+ self.hide_select = col.hide_select
+ self.hide_viewport = col.hide_viewport
+
+ def get_col(self, use_names: bool = False)-> bpy.types.Collection:
+ if use_names:
+ if self.name != "":
+ if self.name in bpy.data.collections:
+ return bpy.data.collections[self.name]
+ return None
+ else:
+ return self.ref
+
+class SavedViewLayerChildren():
+ """
+ Saved data from a blender ViewLayerChildren.
+ """
+
+ def __init__(self, vlayer :bpy.types.ViewLayer, child_col :bpy.types.LayerCollection):
+ if child_col:
+ self.vlayer_name = vlayer.name
+ self.name = child_col.name
+ self.exclude = child_col.exclude
+ self.hide_viewport = child_col.hide_viewport
+
+
+class UserSceneSave():
+ """
+ Manager for saving and resetting the user scene.
+ """
+
+ def __init__(self):
+ # Select
+ self.user_select_class = select_save.UserSelectSave()
+
+ self.user_bone_active = None
+ self.user_bone_active_name = ""
+
+ # Stats
+ self.user_mode = None
+ self.use_simplify = False
+
+ # Data
+ self.objects: List[SavedObject] = []
+ self.object_bones: List[SavedBones] = []
+ self.collections: List[SavedCollection] = []
+ self.view_layer_collections: List[SavedViewLayerChildren] = []
+ self.action_names: List[str] = []
+ self.collection_names: List[str] = []
+
+ def save_current_scene(self):
+ """
+ Save the current scene data.
+ """
+ # Save data (This can take time)
+ scene = bpy.context.scene
+
+ # Select
+ self.user_select_class.save_current_select()
+
+ # Stats
+ if self.user_select_class.user_active:
+ if bpy.ops.object.mode_set.poll():
+ self.user_mode = self.user_select_class.user_active.mode # Save current mode
+ self.use_simplify = bpy.context.scene.render.use_simplify
+
+ # Data
+ for obj in scene.objects:
+ self.objects.append(SavedObject(obj))
+ for col in bpy.data.collections:
+ self.collections.append(SavedCollection(col))
+ for vlayer in scene.view_layers:
+ layer_collections = utils.get_layer_collections_recursive(vlayer.layer_collection)
+ for layer_collection in layer_collections:
+ self.view_layer_collections.append(SavedViewLayerChildren(vlayer, layer_collection))
+ for action in bpy.data.actions:
+ self.action_names.append(action.name)
+ for collection in bpy.data.collections:
+ self.collection_names.append(collection.name)
+
+ # Data for armature
+ if self.user_select_class.user_active:
+ if self.user_select_class.user_active.type == "ARMATURE":
+ if self.user_select_class.user_active.data.bones.active:
+ self.user_bone_active = self.user_select_class.user_active.data.bones.active
+ self.user_bone_active_name = self.user_select_class.user_active.data.bones.active.name
+ for bone in self.user_select_class.user_active.data.bones:
+ self.object_bones.append(SavedBones(bone))
+
+ def reset_select(self, use_names: bool = False):
+ """
+ Reset the user selection based on object references.
+ """
+ self.user_select_class.reset_select(use_names)
+ self.reset_bones_select(use_names)
+
+ def reset_bones_select(self, use_names: bool = False):
+ """
+ Reset bone selection by name (works only in pose mode).
+ """
+ # Work only in pose mode!
+ if len(self.object_bones) > 0:
+ user_active = self.user_select_class.get_user_active(use_names)
+ if user_active:
+ if bpy.ops.object.mode_set.poll():
+ if user_active.mode == "POSE":
+ bpy.ops.pose.select_all(action='DESELECT')
+ for bone in self.object_bones:
+ if bone.select:
+ if bone.name in user_active.data.bones:
+ user_active.data.bones[bone.name].select = True
+
+ if self.user_bone_active_name is not None:
+ if self.user_bone_active_name in user_active.data.bones:
+ new_active = user_active.data.bones[self.user_bone_active_name]
+ user_active.data.bones.active = new_active
+
+ def reset_mode_at_save(self):
+ """
+ Reset the user mode at the last save.
+ """
+ if self.user_mode:
+ utils.safe_mode_set(self.user_mode, bpy.ops.object)
+
+ def reset_scene_at_save(self, print_removed_items = False, use_names: bool = False):
+ """
+ Reset the user scene to at the last save.
+ """
+ scene = bpy.context.scene
+ self.reset_mode_at_save()
+
+ bpy.context.scene.render.use_simplify = self.use_simplify
+
+ # Reset hide and select
+ for obj in self.objects:
+ try:
+ obj_ref = obj.get_obj(use_names)
+ if obj_ref:
+ if obj_ref.hide_select != obj.hide_select:
+ obj_ref.hide_select = obj.hide_select
+ if obj_ref.hide_viewport != obj.hide_viewport:
+ obj_ref.hide_viewport = obj.hide_viewport
+ if obj_ref.hide_get() != obj.hide:
+ obj_ref.hide_set(obj.hide)
+ else:
+ if print_removed_items:
+ print(f"/!\\ {obj.name} not found.")
+ except ReferenceError:
+ if print_removed_items:
+ print(f"/!\\ object {obj.name} has been removed.")
+
+ # Reset hide and select (bpy.data.collections)
+ for col in self.collections:
+ try:
+ col_ref = col.get_col(use_names)
+ if col_ref:
+ if col_ref.hide_select != col.hide_select:
+ col_ref.hide_select = col.hide_select
+ if col_ref.hide_viewport != col.hide_viewport:
+ col_ref.hide_viewport = col.hide_viewport
+ else:
+ if print_removed_items:
+ print(f"/!\\ {col.name} not found.")
+ except ReferenceError:
+ if print_removed_items:
+ print(f"/!\\ collection {col.name} has been removed.")
+
+ # Reset hide and viewport (collections from view_layers)
+ for vlayer in scene.view_layers:
+ layer_collections = utils.get_layer_collections_recursive(vlayer.layer_collection)
+
+ def get_layer_collection_in_list(name, collections) -> bpy.types.LayerCollection:
+ for layer_collection in collections:
+ if layer_collection.name == name:
+ return layer_collection
+
+ for view_layer_collection in self.view_layer_collections:
+ if view_layer_collection.vlayer_name == vlayer.name:
+ layer_collection = get_layer_collection_in_list(view_layer_collection.name, layer_collections)
+ if layer_collection:
+ if layer_collection.exclude != view_layer_collection.exclude:
+ layer_collection.exclude = view_layer_collection.exclude
+ if layer_collection.hide_viewport != view_layer_collection.hide_viewport:
+ layer_collection.hide_viewport = view_layer_collection.hide_viewport
+
diff --git a/blender-for-unrealengine/bbpl/save_data/select_save.py b/blender-for-unrealengine/bbpl/save_data/select_save.py
new file mode 100644
index 00000000..7732c866
--- /dev/null
+++ b/blender-for-unrealengine/bbpl/save_data/select_save.py
@@ -0,0 +1,116 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BBPL -> BleuRaven Blender Python Library
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import bpy
+from typing import List
+from .. import utils
+
+class UserSelectSave():
+ """
+ Manager for user selection.
+ """
+
+ def __init__(self):
+ # Select
+ self.user_active = None
+ self.user_active_name = ""
+ self.user_selecteds: List[bpy.types.Object] = []
+ self.user_selected_names: List[str] = []
+
+ # Stats
+ self.user_mode = None
+
+ def save_current_select(self):
+ """
+ Save user selection.
+ """
+
+ # Save data (This can take time)
+
+ # Select
+ self.user_active = bpy.context.active_object # Save current active object
+ if self.user_active:
+ self.user_active_name = self.user_active.name
+
+ self.user_selecteds = bpy.context.selected_objects # Save current selected objects
+ self.user_selected_names = [obj.name for obj in bpy.context.selected_objects]
+
+ def reset_select(self, use_names: bool = False):
+ """
+ Reset user selection at the last save.
+ """
+
+ scene = bpy.context.scene
+ self.save_mode(use_names)
+ utils.safe_mode_set("OBJECT", bpy.ops.object)
+ bpy.ops.object.select_all(action='DESELECT')
+
+ if use_names:
+ for obj in scene.objects:
+ if obj.name in self.user_selected_names:
+ if obj.name in bpy.context.view_layer.objects:
+ scene.objects.get(obj.name).select_set(True) # Use the name because can be duplicated name
+
+ if self.user_active_name != "":
+ if self.user_active_name in scene.objects:
+ if self.user_active_name in bpy.context.view_layer.objects:
+ bpy.context.view_layer.objects.active = scene.objects.get(self.user_active_name)
+
+
+ else:
+ for obj in scene.objects: # Resets previous selected object if still exist
+ if obj in self.user_selecteds:
+ obj.select_set(True)
+
+ bpy.context.view_layer.objects.active = self.user_active
+
+ self.reset_mode_at_save()
+
+ def save_mode(self, use_names: bool = False):
+ """
+ Save user mode.
+ """
+
+ user_active = self.get_user_active(use_names)
+ if user_active:
+ if bpy.ops.object.mode_set.poll():
+ self.user_mode = user_active.mode # Save current mode
+
+ def reset_mode_at_save(self):
+ """
+ Reset user mode at the last save.
+ """
+ if self.user_mode:
+ utils.safe_mode_set(self.user_mode, bpy.ops.object)
+
+ def get_user_active(self, use_names: bool = False):
+ scene = bpy.context.scene
+ if use_names:
+ if self.user_active_name != "":
+ if self.user_active_name in scene.objects:
+ if self.user_active_name in bpy.context.view_layer.objects:
+ return scene.objects.get(self.user_active_name)
+ return None
+ else:
+ return self.user_active
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbpl/skin_utils.py b/blender-for-unrealengine/bbpl/skin_utils.py
index 489e0853..ab996f24 100644
--- a/blender-for-unrealengine/bbpl/skin_utils.py
+++ b/blender-for-unrealengine/bbpl/skin_utils.py
@@ -97,33 +97,19 @@ def copy_rig_group(obj, source):
bpy.ops.object.modifier_apply(modifier=mod_name)
-def apply_auto_rig_parent(armature, target_objects, parent_type='ARMATURE_AUTO', use_only_bone_white_list=False, white_list_bones=None, black_list_bones=None):
+def apply_auto_rig_parent(armature, target_objects, parent_type='ARMATURE_AUTO', white_list_bones=[], black_list_bones=[]):
"""
Apply an automatic rig parent to the target object using the armature.
Optionally, specify a white list or black list of bones to control the deform flag.
"""
- if white_list_bones is None:
- white_list_bones = []
- if black_list_bones is None:
- black_list_bones = []
- # Exit current mode
- #if bpy.ops.object.mode_set.poll():
- # bpy.ops.object.mode_set(mode='OBJECT')
+ save_defom = save_defoms_bones(armature)
- #bpy.ops.object.select_all(action='DESELECT')
- #armature.select_set(state=True)
- #target.select_set(state=True)
- #bpy.context.view_layer.objects.active = armature
+ if len(white_list_bones) > 0:
+ set_all_bones_deforms(armature, False)
- if len(white_list_bones) > 0 or len(black_list_bones) > 0:
- save_defom = save_defoms_bones(armature)
-
- if use_only_bone_white_list:
- set_all_bones_deforms(armature, False)
-
- set_bones_deforms(armature, white_list_bones, True)
- set_bones_deforms(armature, black_list_bones, False)
+ set_bones_deforms(armature, white_list_bones, True)
+ set_bones_deforms(armature, black_list_bones, False)
for obj in target_objects:
for modifier in obj.modifiers:
@@ -146,5 +132,4 @@ def apply_auto_rig_parent(armature, target_objects, parent_type='ARMATURE_AUTO',
bpy.ops.object.parent_set(override_context, type=parent_type)
- if len(white_list_bones) > 0 or len(black_list_bones) > 0:
- reset_deform_bones(armature, save_defom)
\ No newline at end of file
+ reset_deform_bones(armature, save_defom)
\ No newline at end of file
diff --git a/blender-for-unrealengine/bbpl/utils.py b/blender-for-unrealengine/bbpl/utils.py
index 74014af1..c1d962f4 100644
--- a/blender-for-unrealengine/bbpl/utils.py
+++ b/blender-for-unrealengine/bbpl/utils.py
@@ -27,142 +27,6 @@
import bpy
import mathutils
-
-class SavedObject():
- """
- Saved data from a blender object.
- """
-
- def __init__(self, obj: bpy.types.Object):
- if obj:
- self.ref = obj
- self.name = obj.name
- self.select = obj.select_get()
- self.hide = obj.hide_get()
- self.hide_select = obj.hide_select
- self.hide_viewport = obj.hide_viewport
-
-
-class SavedBones():
- """
- Saved data from a blender armature bone.
- """
-
- def __init__(self, bone):
- if bone:
- self.name = bone.name
- self.select = bone.select
- self.hide = bone.hide
-
-
-class SavedCollection():
- """
- Saved data from a blender collection.
- """
-
- def __init__(self, col):
- if col:
- self.ref = col
- self.name = col.name
- self.hide_select = col.hide_select
- self.hide_viewport = col.hide_viewport
-
-
-class SavedViewLayerChildren():
- """
- Saved data from a blender ViewLayerChildren.
- """
-
- def __init__(self, vlayer, childCol):
- if childCol:
- self.vlayer_name = vlayer.name
- self.name = childCol.name
- self.exclude = childCol.exclude
- self.hide_viewport = childCol.hide_viewport
-
-
-class UserSelectSave():
- """
- Manager for user selection.
- """
-
- def __init__(self):
- # Select
- self.user_active = None
- self.user_active_name = ""
- self.user_selecteds = []
- self.user_selected_names = []
-
- # Stats
- self.user_mode = None
-
- def save_current_select(self):
- """
- Save user selection.
- """
-
- # Save data (This can take time)
-
- # Select
- self.user_active = bpy.context.active_object # Save current active object
- if self.user_active:
- self.user_active_name = self.user_active.name
-
- self.user_selecteds = bpy.context.selected_objects # Save current selected objects
- self.user_selected_names = [obj.name for obj in bpy.context.selected_objects]
-
-
- def reset_select_by_ref(self):
- """
- Reset user selection at the last save. (Use objects refs)
- """
-
- self.save_mode()
- safe_mode_set("OBJECT", bpy.ops.object)
- bpy.ops.object.select_all(action='DESELECT')
- for obj in bpy.data.objects: # Resets previous selected object if still exist
- if obj in self.user_selecteds:
- obj.select_set(True)
-
- bpy.context.view_layer.objects.active = self.user_active
-
- self.reset_mode_at_save()
-
- def reset_select_by_name(self):
- """
- Reset user selection at the last save. (Use objects names)
- """
-
- self.save_mode()
- safe_mode_set("OBJECT", bpy.ops.object)
- bpy.ops.object.select_all(action='DESELECT')
- for obj in bpy.data.objects:
- if obj.name in self.user_selected_names:
- if obj.name in bpy.context.view_layer.objects:
- bpy.data.objects[obj.name].select_set(True) # Use the name because can be duplicated name
-
- if self.user_active_name != "":
- if self.user_active_name in bpy.data.objects:
- if self.user_active_name in bpy.context.view_layer.objects:
- bpy.context.view_layer.objects.active = bpy.data.objects[self.user_active_name]
-
- self.reset_mode_at_save()
-
- def save_mode(self):
- """
- Save user mode.
- """
- if self.user_active:
- if bpy.ops.object.mode_set.poll():
- self.user_mode = self.user_active.mode # Save current mode
-
- def reset_mode_at_save(self):
- """
- Reset user mode at the last save.
- """
- if self.user_mode:
- safe_mode_set(self.user_mode, bpy.ops.object)
-
def select_specific_object(obj: bpy.types.Object):
"""
Selects a specific object in Blender.
@@ -179,170 +43,6 @@ def select_specific_object(obj: bpy.types.Object):
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
-
-class UserSceneSave():
- """
- Manager for saving and resetting the user scene.
- """
-
- def __init__(self):
- # Select
- self.user_select_class = UserSelectSave()
-
- self.user_bone_active = None
- self.user_bone_active_name = ""
-
- # Stats
- self.user_mode = None
- self.use_simplify = False
-
- # Data
- self.objects = []
- self.object_bones = []
- self.collections = []
- self.view_layer_collections = []
- self.action_names = []
- self.collection_names = []
-
- def save_current_scene(self):
- """
- Save the current scene data.
- """
- # Save data (This can take time)
- c = bpy.context
- # Select
- self.user_select_class.save_current_select()
-
- # Stats
- if self.user_select_class.user_active:
- if bpy.ops.object.mode_set.poll():
- self.user_mode = self.user_select_class.user_active.mode # Save current mode
- self.use_simplify = bpy.context.scene.render.use_simplify
-
- # Data
- for obj in bpy.data.objects:
- self.objects.append(SavedObject(obj))
- for col in bpy.data.collections:
- self.collections.append(SavedCollection(col))
- for vlayer in c.scene.view_layers:
- layer_collections = get_layer_collections_recursive(vlayer.layer_collection)
- for layer_collection in layer_collections:
- self.view_layer_collections.append(SavedViewLayerChildren(vlayer, layer_collection))
- for action in bpy.data.actions:
- self.action_names.append(action.name)
- for collection in bpy.data.collections:
- self.collection_names.append(collection.name)
-
- # Data for armature
- if self.user_select_class.user_active:
- if self.user_select_class.user_active.type == "ARMATURE":
- if self.user_select_class.user_active.data.bones.active:
- self.user_bone_active = self.user_select_class.user_active.data.bones.active
- self.user_bone_active_name = self.user_select_class.user_active.data.bones.active.name
- for bone in self.user_select_class.user_active.data.bones:
- self.object_bones.append(SavedBones(bone))
-
- def reset_select_by_ref(self):
- """
- Reset the user selection based on object references.
- """
- self.user_select_class.reset_select_by_ref()
- self.reset_bones_select_by_name()
-
- def reset_select_by_name(self):
- """
- Reset the user selection based on object names.
- """
- self.user_select_class.reset_select_by_name()
- self.reset_bones_select_by_name()
-
- def reset_bones_select_by_name(self):
- """
- Reset bone selection by name (works only in pose mode).
- """
- # Work only in pose mode!
- if len(self.object_bones) > 0:
- if self.user_select_class.user_active:
- if bpy.ops.object.mode_set.poll():
- if self.user_select_class.user_active.mode == "POSE":
- bpy.ops.pose.select_all(action='DESELECT')
- for bone in self.object_bones:
- if bone.select:
- if bone.name in self.user_select_class.user_active.data.bones:
- self.user_select_class.user_active.data.bones[bone.name].select = True
-
- if self.user_bone_active_name is not None:
- if self.user_bone_active_name in self.user_select_class.user_active.data.bones:
- new_active = self.user_select_class.user_active.data.bones[self.user_bone_active_name]
- self.user_select_class.user_active.data.bones.active = new_active
-
- def reset_mode_at_save(self):
- """
- Reset the user mode at the last save.
- """
- if self.user_mode:
- safe_mode_set(self.user_mode, bpy.ops.object)
-
- def reset_scene_at_save(self, print_removed_items = False):
- """
- Reset the user scene to at the last save.
- """
- scene = bpy.context.scene
- self.reset_mode_at_save()
-
- bpy.context.scene.render.use_simplify = self.use_simplify
-
- # Reset hide and select (bpy.data.objects)
- for obj in self.objects:
- try:
- if obj.ref:
- if obj.ref.hide_select != obj.hide_select:
- obj.ref.hide_select = obj.hide_select
- if obj.ref.hide_viewport != obj.hide_viewport:
- obj.ref.hide_viewport = obj.hide_viewport
- if obj.ref.hide_get() != obj.hide:
- obj.ref.hide_set(obj.hide)
- else:
- if print_removed_items:
- print(f"/!\\ {obj.name} not found.")
- except ReferenceError:
- if print_removed_items:
- print(f"/!\\ {obj.name} has been removed.")
-
- # Reset hide and select (bpy.data.collections)
- for col in self.collections:
- try:
- if col.ref.name in bpy.data.collections:
- if col.ref.hide_select != col.hide_select:
- col.ref.hide_select = col.hide_select
- if col.ref.hide_viewport != col.hide_viewport:
- col.ref.hide_viewport = col.hide_viewport
- else:
- if print_removed_items:
- print(f"/!\\ {col.name} not found.")
- except ReferenceError:
- if print_removed_items:
- print(f"/!\\ {col.name} has been removed.")
-
- # Reset hide and viewport (collections from view_layers)
- for vlayer in scene.view_layers:
- layer_collections = get_layer_collections_recursive(vlayer.layer_collection)
-
- def get_layer_collection_in_list(name, collections):
- for layer_collection in collections:
- if layer_collection.name == name:
- return layer_collection
-
- for view_layer_collection in self.view_layer_collections:
- if view_layer_collection.vlayer_name == vlayer.name:
- layer_collection = get_layer_collection_in_list(view_layer_collection.name, layer_collections)
- if layer_collection:
- if layer_collection.exclude != view_layer_collection.exclude:
- layer_collection.exclude = view_layer_collection.exclude
- if layer_collection.hide_viewport != view_layer_collection.hide_viewport:
- layer_collection.hide_viewport = view_layer_collection.hide_viewport
-
-
class UserArmatureDataSave():
"""
Manager for saving and resetting an armature.
@@ -712,7 +412,6 @@ def __init__(self):
def LoadUserRenderSimplify(self):
bpy.context.scene.render.use_simplify = self.use_simplify
-
class SaveObjectReferanceUser():
"""
This class is used to save and update references to an object in constraints
@@ -725,39 +424,41 @@ def __init__(self):
"""
self.using_constraints = []
- def save_refs_from_object(self, obj: bpy.types.Object):
+ def save_refs_from_object(self, targe_obj: bpy.types.Object):
"""
Scans all objects in the Blender scene to find and save constraints in armature bones
that reference the specified object.
:param obj: The target bpy.types.Object to find references to.
"""
- for objet in bpy.data.objects:
- if objet.type == 'ARMATURE':
- for bone in objet.pose.bones:
+ scene = bpy.context.scene
+ for obj in scene.objects:
+ if obj.type == 'ARMATURE':
+ for bone in obj.pose.bones:
for contrainte in bone.constraints:
- if hasattr(contrainte, 'target') and contrainte.target and contrainte.target.name == obj.name:
+ if hasattr(contrainte, 'target') and contrainte.target and contrainte.target.name == targe_obj.name:
constraint_info = {
- 'armature_object': objet.name,
+ 'armature_object': obj.name,
'bone': bone.name,
'constraint': contrainte.name
}
self.using_constraints.append(constraint_info)
- def update_refs_with_object(self, obj: bpy.types.Object):
+ def update_refs_with_object(self, targe_obj: bpy.types.Object):
"""
Updates all previously found constraints to reference a new object.
:param obj: The new bpy.types.Object to be used as the target for the saved constraints.
"""
+ scene = bpy.context.scene
for info in self.using_constraints:
- if info['armature_object'] in bpy.data.objects:
- armature_object = bpy.data.objects[info['armature_object']]
+ if info['armature_object'] in scene.objects:
+ armature_object = scene.objects.get(info['armature_object'])
if info['bone'] in armature_object.pose.bones:
bone = armature_object.pose.bones[info['bone']]
if info['constraint'] in bone.constraints:
constraint = bone.constraints[info['constraint']]
- constraint.target = obj
+ constraint.target = targe_obj
def active_mode_is(targetMode):
# Return True is active obj mode == targetMode
@@ -794,4 +495,105 @@ def found_type_in_selection(targetType, include_active=True):
for obj in select:
if obj.type == targetType:
return True
- return False
\ No newline at end of file
+ return False
+
+def get_bone_path(armature: bpy.types.Object, start_bone_name: str, end_bone_name: str):
+ """
+ Returns a list of bone names between start_bone and end_bone in an armature.
+
+ :param armature: The armature object.
+ :param start_bone_name: The name of the starting bone.
+ :param end_bone_name: The name of the ending bone.
+ :return: List of bone names between start_bone and end_bone, or an empty list if no path is found.
+ """
+
+ # Access bones directly.
+ if armature.mode == 'EDIT':
+ bones = armature.data.edit_bones
+ else:
+ bones = armature.data.bones
+
+ # Initialize the bones
+ start_bone = bones[start_bone_name]
+ end_bone = bones[end_bone_name]
+
+ # Depth-First Search to find the path from start_bone to end_bone
+ def find_path(current_bone, path):
+ path.append(current_bone.name)
+
+ # Check if we've reached the end bone
+ if current_bone == end_bone:
+ return path
+
+ # Explore each child recursively
+ for child in current_bone.children:
+ result = find_path(child, path[:]) # Use a copy of the current path
+ if result: # If a valid path is found, return it
+ return result
+
+ return None # Return None if no path is found from this branch
+
+ # Start the recursive search
+ all_bones = find_path(start_bone, [])
+ return all_bones
+
+
+def get_bone_path_to_end(armature: bpy.types.Object, start_bone_name: str):
+ """
+ Returns a list of bone names from the start_bone to the last child in a chain.
+
+ :param armature: The armature object.
+ :param start_bone_name: The name of the starting bone.
+ :return: List of bone names from start_bone to the last child.
+ """
+
+ # Access bones directly.
+ if armature.mode == 'EDIT':
+ bones = armature.data.edit_bones
+ else:
+ bones = armature.data.bones
+
+ # Initialize the bones
+ start_bone = bones[start_bone_name]
+
+ # Traverse bones from start_bone to the last child in the chain
+ current_bone = start_bone
+ bone_path = [current_bone.name]
+
+ while current_bone.children:
+ # Use first child only
+ current_bone = current_bone.children[0]
+ bone_path.append(current_bone.name)
+
+ return bone_path
+
+def get_bone_and_children(armature: bpy.types.Object, start_bone_name: str):
+ """
+ Returns a list of all descendant bones of the specified start_bone, including all children recursively.
+
+ :param armature: The armature object.
+ :param start_bone_name: The name of the starting bone.
+ :return: List of bone names, including the start bone and all its descendants.
+ """
+
+ # Access bones directly.
+ if armature.mode == 'EDIT':
+ bones = armature.data.edit_bones
+ else:
+ bones = armature.data.bones
+
+ # Initialize the bones
+ bones = armature.data.edit_bones
+ start_bone = bones.get(start_bone_name)
+
+
+ # Recursive function to collect all children bones
+ def collect_children(bone):
+ bone_list = [bone.name]
+ for child in bone.children:
+ bone_list.extend(collect_children(child))
+ return bone_list
+
+ # Get all bones starting from the start_bone
+ all_bones = collect_children(start_bone)
+ return all_bones
diff --git a/blender-for-unrealengine/bfu_addon_parts/__init__.py b/blender-for-unrealengine/bfu_addon_parts/__init__.py
index 5aea252d..14d8272a 100644
--- a/blender-for-unrealengine/bfu_addon_parts/__init__.py
+++ b/blender-for-unrealengine/bfu_addon_parts/__init__.py
@@ -1,29 +1,20 @@
import bpy
import importlib
-from . import bfu_modular_skeletal_specified_parts_meshs
-from . import bfu_object_ui_and_props
-from . import bfu_tool_ui_and_props
-from . import bfu_export_ui_and_props
-from . import bfu_unreal_engine_refs_props
+from . import bfu_panel_object
+from . import bfu_panel_tools
+from . import bfu_panel_export
from . import bfu_export_correct_and_improv_panel
-from . import bfu_debug_ui_and_props_panel
-
-
-if "bfu_modular_skeletal_specified_parts_meshs" in locals():
- importlib.reload(bfu_modular_skeletal_specified_parts_meshs)
-if "bfu_object_ui_and_props" in locals():
- importlib.reload(bfu_object_ui_and_props)
-if "bfu_tool_ui_and_props" in locals():
- importlib.reload(bfu_tool_ui_and_props)
-if "bfu_export_ui_and_props" in locals():
- importlib.reload(bfu_export_ui_and_props)
-if "bfu_unreal_engine_refs_props" in locals():
- importlib.reload(bfu_unreal_engine_refs_props)
+
+
+if "bfu_panel_object" in locals():
+ importlib.reload(bfu_panel_object)
+if "bfu_panel_tools" in locals():
+ importlib.reload(bfu_panel_tools)
+if "bfu_panel_export" in locals():
+ importlib.reload(bfu_panel_export)
if "bfu_export_correct_and_improv_panel" in locals():
importlib.reload(bfu_export_correct_and_improv_panel)
-if "bfu_debug_ui_and_props_panel" in locals():
- importlib.reload(bfu_debug_ui_and_props_panel)
classes = (
)
@@ -33,22 +24,16 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
- bfu_modular_skeletal_specified_parts_meshs.register()
- bfu_object_ui_and_props.register()
- bfu_tool_ui_and_props.register()
- bfu_export_ui_and_props.register()
- bfu_unreal_engine_refs_props.register()
+ bfu_panel_object.register()
+ bfu_panel_tools.register()
+ bfu_panel_export.register()
bfu_export_correct_and_improv_panel.register()
- bfu_debug_ui_and_props_panel.register()
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
- bfu_modular_skeletal_specified_parts_meshs.unregister()
- bfu_object_ui_and_props.unregister()
- bfu_tool_ui_and_props.unregister()
- bfu_unreal_engine_refs_props.unregister()
- bfu_export_ui_and_props.unregister()
bfu_export_correct_and_improv_panel.unregister()
- bfu_debug_ui_and_props_panel.unregister()
\ No newline at end of file
+ bfu_panel_export.unregister()
+ bfu_panel_tools.unregister()
+ bfu_panel_object.unregister()
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_debug_ui_and_props_panel.py b/blender-for-unrealengine/bfu_addon_parts/bfu_debug_ui_and_props_panel.py
deleted file mode 100644
index ce88b356..00000000
--- a/blender-for-unrealengine/bfu_addon_parts/bfu_debug_ui_and_props_panel.py
+++ /dev/null
@@ -1,72 +0,0 @@
-import bpy
-
-from .. import bfu_utils
-from .. import bfu_assets_manager
-
-class BFU_PT_BlenderForUnrealDebug(bpy.types.Panel):
- # Debug panel for get dev info and test
-
- bl_idname = "BFU_PT_BlenderForUnrealDebug"
- bl_label = "Debug"
- bl_space_type = "VIEW_3D"
- bl_region_type = "UI"
- bl_category = "Unreal Engine"
-
- bpy.types.Object.bfu_use_socket_custom_Name = bpy.props.BoolProperty(
- name="Socket custom name",
- description='Use a custom name in Unreal Engine for this socket?',
- default=False
- )
-
- bpy.types.Object.bfu_socket_custom_Name = bpy.props.StringProperty(
- name="",
- description='',
- default="MySocket"
- )
-
- def draw(self, context):
-
- layout = self.layout
- obj = context.object
- layout.label(text="This panel is only for Debug", icon='INFO')
-
- if obj:
- layout.label(text="Full path name as Static Mesh:")
- asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
- if asset_class:
- obj_export_name = asset_class.get_obj_export_name(obj)
- obj_export_dir = asset_class.get_obj_export_directory_path(obj)
- obj_export_abs_dir = asset_class.get_obj_export_abs_directory_path(obj)
-
- else:
- obj_export_name = "XXX"
- obj_export_dir = "XXX"
- obj_export_abs_dir = "XXX"
-
- layout.label(text="Obj Export Name:" + obj_export_name)
- layout.label(text="Obj Export Dir" + obj_export_dir)
- layout.label(text="Obj Export Abs Dir:" + obj_export_abs_dir)
-
-
- if obj.type == "CAMERA":
- layout.label(text="CameraPositionForUnreal (Loc):" + str(bfu_utils.EvaluateCameraPositionForUnreal(obj)[0]))
- layout.label(text="CameraPositionForUnreal (Rot):" + str(bfu_utils.EvaluateCameraPositionForUnreal(obj)[1]))
- layout.label(text="CameraPositionForUnreal (Scale):" + str(bfu_utils.EvaluateCameraPositionForUnreal(obj)[2]))
-
-# -------------------------------------------------------------------
-# Register & Unregister
-# -------------------------------------------------------------------
-
-classes = (
- # BFU_PT_BlenderForUnrealDebug, # Unhide for dev
-)
-
-
-def register():
- for cls in classes:
- bpy.utils.register_class(cls)
-
-
-def unregister():
- for cls in reversed(classes):
- bpy.utils.unregister_class(cls)
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_export_correct_and_improv_panel.py b/blender-for-unrealengine/bfu_addon_parts/bfu_export_correct_and_improv_panel.py
index d7f74a19..36cdf780 100644
--- a/blender-for-unrealengine/bfu_addon_parts/bfu_export_correct_and_improv_panel.py
+++ b/blender-for-unrealengine/bfu_addon_parts/bfu_export_correct_and_improv_panel.py
@@ -1,6 +1,7 @@
import bpy
from .. import bfu_utils
+from .. import languages
class BFU_PT_CorrectAndImprov(bpy.types.Panel):
# Is Clipboard panel
@@ -12,23 +13,28 @@ class BFU_PT_CorrectAndImprov(bpy.types.Panel):
bl_category = "Unreal Engine"
class BFU_OT_CorrectExtremUV(bpy.types.Operator):
- bl_label = "Correct Extrem UV For Unreal"
+ bl_label = (languages.ti('correct_use_extrem_uv_scale_name'))
bl_idname = "object.correct_extrem_uv"
- bl_description = (
- "Correct extrem UV island of the selected object" +
- " for better use in real time engines"
- )
+ bl_description = (languages.tt('correct_extrem_uv_scale_operator_desc'))
bl_options = {'REGISTER', 'UNDO'}
- stepScale: bpy.props.IntProperty(
- name="Step scale",
+ step_scale: bpy.props.IntProperty(
+ name=(languages.ti('correct_extrem_uv_scale_step_scale_name')),
+ description =(languages.tt('correct_use_extrem_uv_scale_desc')),
default=2,
min=1,
- max=100)
+ max=100,
+ )
+
+ move_to_absolute: bpy.props.BoolProperty(
+ name=(languages.ti('correct_extrem_uv_scale_use_absolute_name')),
+ description =(languages.tt('correct_extrem_uv_scale_use_absolute_desc')),
+ default=False,
+ )
def execute(self, context):
if bpy.context.active_object.mode == "EDIT":
- bfu_utils.CorrectExtremeUV(stepScale=self.stepScale)
+ bfu_utils.CorrectExtremeUV(step_scale=self.step_scale, move_to_absolute=self.move_to_absolute)
self.report(
{'INFO'},
"UV corrected!")
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_export_ui_and_props.py b/blender-for-unrealengine/bfu_addon_parts/bfu_export_ui_and_props.py
deleted file mode 100644
index e87ac9ba..00000000
--- a/blender-for-unrealengine/bfu_addon_parts/bfu_export_ui_and_props.py
+++ /dev/null
@@ -1,828 +0,0 @@
-import os
-import bpy
-from .. import bfu_export
-from .. import bfu_write_text
-from .. import bfu_basics
-from .. import bfu_utils
-from .. import bfu_check_potential_error
-from .. import bfu_cached_asset_list
-from .. import bfu_ui
-from .. import bbpl
-from .. import bps
-from .. import bfu_collision
-from .. import bfu_socket
-from .. import bfu_assets_manager
-
-
-
-class BFU_PT_Export(bpy.types.Panel):
- # Is Export panel
-
- bl_idname = "BFU_PT_Export"
- bl_label = "BFU Export"
- bl_space_type = "VIEW_3D"
- bl_region_type = "UI"
- bl_category = "Unreal Engine"
-
- # Prefix
- bpy.types.Scene.bfu_static_mesh_prefix_export_name = bpy.props.StringProperty(
- name="StaticMesh Prefix",
- description="Prefix of staticMesh",
- maxlen=32,
- default="SM_")
-
- bpy.types.Scene.bfu_skeletal_mesh_prefix_export_name = bpy.props.StringProperty(
- name="SkeletalMesh Prefix ",
- description="Prefix of SkeletalMesh",
- maxlen=32,
- default="SKM_")
-
- bpy.types.Scene.bfu_skeleton_prefix_export_name = bpy.props.StringProperty(
- name="skeleton Prefix ",
- description="Prefix of skeleton",
- maxlen=32,
- default="SK_")
-
- bpy.types.Scene.bfu_alembic_animation_prefix_export_name = bpy.props.StringProperty(
- name="Alembic Prefix ",
- description="Prefix of Alembic (SkeletalMesh in unreal)",
- maxlen=32,
- default="SKM_")
-
- bpy.types.Scene.bfu_groom_simulation_prefix_export_name = bpy.props.StringProperty(
- name="Groom Prefix ",
- description="Prefix of Groom Simulation",
- maxlen=32,
- default="GS_")
-
-
-
- bpy.types.Scene.bfu_anim_prefix_export_name = bpy.props.StringProperty(
- name="AnimationSequence Prefix",
- description="Prefix of AnimationSequence",
- maxlen=32,
- default="Anim_")
-
- bpy.types.Scene.bfu_pose_prefix_export_name = bpy.props.StringProperty(
- name="AnimationSequence(Pose) Prefix",
- description="Prefix of AnimationSequence with only one frame",
- maxlen=32,
- default="Pose_")
-
- bpy.types.Scene.bfu_camera_prefix_export_name = bpy.props.StringProperty(
- name="Camera anim Prefix",
- description="Prefix of camera animations",
- maxlen=32,
- default="Cam_")
-
- bpy.types.Scene.bfu_spline_prefix_export_name = bpy.props.StringProperty(
- name="Spline anim Prefix",
- description="Prefix of spline animations",
- maxlen=32,
- default="Spline_")
-
- # Sub folder
- bpy.types.Scene.bfu_anim_subfolder_name = bpy.props.StringProperty(
- name="Animations sub folder name",
- description=(
- "The name of sub folder for animations New." +
- " You can now use ../ for up one directory."),
- maxlen=512,
- default="Anim")
-
- # File path
- bpy.types.Scene.bfu_export_static_file_path = bpy.props.StringProperty(
- name="StaticMesh Export Path",
- description="Choose a directory to export StaticMesh(s)",
- maxlen=512,
- default="//" + os.path.join("ExportedFbx", "StaticMesh"),
- subtype='DIR_PATH')
-
- bpy.types.Scene.bfu_export_skeletal_file_path = bpy.props.StringProperty(
- name="SkeletalMesh Export Path",
- description="Choose a directory to export SkeletalMesh(s)",
- maxlen=512,
- default="//" + os.path.join("ExportedFbx", "SkeletalMesh"),
- subtype='DIR_PATH')
-
- bpy.types.Scene.bfu_export_alembic_file_path = bpy.props.StringProperty(
- name="Alembic Export Path",
- description="Choose a directory to export Alembic animation(s)",
- maxlen=512,
- default="//" + os.path.join("ExportedFbx", "Alembic"),
- subtype='DIR_PATH')
-
- bpy.types.Scene.bfu_export_groom_file_path = bpy.props.StringProperty(
- name="Groom Export Path",
- description="Choose a directory to export Groom simulation(s)",
- maxlen=512,
- default="//" + os.path.join("ExportedFbx", "Groom"),
- subtype='DIR_PATH')
-
- bpy.types.Scene.bfu_export_camera_file_path = bpy.props.StringProperty(
- name="Camera Export Path",
- description="Choose a directory to export Camera(s)",
- maxlen=512,
- default="//" + os.path.join("ExportedFbx", "Sequencer"),
- subtype='DIR_PATH')
-
- bpy.types.Scene.bfu_export_spline_file_path = bpy.props.StringProperty(
- name="Spline Export Path",
- description="Choose a directory to export Spline(s)",
- maxlen=512,
- default="//" + os.path.join("ExportedFbx", "Spline"),
- subtype='DIR_PATH')
-
- bpy.types.Scene.bfu_export_other_file_path = bpy.props.StringProperty(
- name="Other Export Path",
- description="Choose a directory to export text file and other",
- maxlen=512,
- default="//" + os.path.join("ExportedFbx"),
- subtype='DIR_PATH')
-
- # File name
- bpy.types.Scene.bfu_file_export_log_name = bpy.props.StringProperty(
- name="Export log name",
- description="Export log name",
- maxlen=64,
- default="ExportLog.txt")
-
- bpy.types.Scene.bfu_file_import_asset_script_name = bpy.props.StringProperty(
- name="Import asset script Name",
- description="Import asset script name",
- maxlen=64,
- default="ImportAssetScript.py")
-
- bpy.types.Scene.bfu_file_import_sequencer_script_name = bpy.props.StringProperty(
- name="Import sequencer script Name",
- description="Import sequencer script name",
- maxlen=64,
- default="ImportSequencerScript.py")
-
- bpy.types.Scene.bfu_unreal_import_module = bpy.props.StringProperty(
- name="Unreal import module",
- description="Which module (plugin name) to import to. Default is 'Game', meaning it will be put into your project's /Content/ folder. If you wish to import to a plugin (for example a plugin called 'myPlugin'), just write its name here",
- maxlen=512,
- default='Game')
-
- bpy.types.Scene.bfu_unreal_import_location = bpy.props.StringProperty(
- name="Unreal import location",
- description="Unreal assets import location inside the module",
- maxlen=512,
- default='ImportedFbx')
-
- class BFU_MT_NomenclaturePresets(bpy.types.Menu):
- bl_label = 'Nomenclature Presets'
- preset_subdir = 'blender-for-unrealengine/nomenclature-presets'
- preset_operator = 'script.execute_preset'
- draw = bpy.types.Menu.draw_preset
-
- from bl_operators.presets import AddPresetBase
-
- class BFU_OT_AddNomenclaturePreset(AddPresetBase, bpy.types.Operator):
- bl_idname = 'object.add_nomenclature_preset'
- bl_label = 'Add or remove a preset for Nomenclature'
- bl_description = 'Add or remove a preset for Nomenclature'
- preset_menu = 'BFU_MT_NomenclaturePresets'
-
- # Common variable used for all preset values
- preset_defines = [
- 'obj = bpy.context.object',
- 'scene = bpy.context.scene'
- ]
-
- # Properties to store in the preset
- preset_values = [
- 'scene.bfu_static_mesh_prefix_export_name',
- 'scene.bfu_skeletal_mesh_prefix_export_name',
- 'scene.bfu_skeleton_prefix_export_name',
- 'scene.bfu_alembic_animation_prefix_export_name',
- 'scene.bfu_groom_simulation_prefix_export_name',
- 'scene.bfu_anim_prefix_export_name',
- 'scene.bfu_pose_prefix_export_name',
- 'scene.bfu_camera_prefix_export_name',
- 'scene.bfu_spline_prefix_export_name',
- 'scene.bfu_anim_subfolder_name',
- 'scene.bfu_export_static_file_path',
- 'scene.bfu_export_skeletal_file_path',
- 'scene.bfu_export_alembic_file_path',
- 'scene.bfu_export_groom_file_path',
- 'scene.bfu_export_camera_file_path',
- 'scene.bfu_export_spline_file_path',
- 'scene.bfu_export_other_file_path',
- 'scene.bfu_file_export_log_name',
- 'scene.bfu_file_import_asset_script_name',
- 'scene.bfu_file_import_sequencer_script_name',
- # Import location:
- 'scene.bfu_unreal_import_module',
- 'scene.bfu_unreal_import_location',
- ]
-
- # Directory to store the presets
- preset_subdir = 'blender-for-unrealengine/nomenclature-presets'
-
- class BFU_OT_ShowAssetToExport(bpy.types.Operator):
- bl_label = "Show asset(s)"
- bl_idname = "object.showasset"
- bl_description = "Click to show assets that are to be exported."
-
- def execute(self, context):
-
- obj = context.object
- if obj:
- if obj.type == "ARMATURE":
- animation_asset_cache = bfu_cached_asset_list.GetAnimationAssetCache(obj)
- animation_asset_cache.UpdateActionCache()
-
-
- final_asset_cache = bfu_cached_asset_list.GetfinalAssetCache()
- final_asset_list_to_export = final_asset_cache.GetFinalAssetList()
- popup_title = "Assets list"
- if len(final_asset_list_to_export) > 0:
- popup_title = str(len(final_asset_list_to_export))+' asset(s) will be exported.'
- else:
- popup_title = 'No exportable assets were found.'
-
- def draw(self, context):
- col = self.layout.column()
- for asset in final_asset_list_to_export:
- asset :bfu_cached_asset_list.AssetToExport
- row = col.row()
- if asset.obj is not None:
- if asset.action is not None:
- if (type(asset.action) is bpy.types.Action):
- # Action name
- action = asset.action.name
- elif (type(asset.action) is bpy.types.AnimData):
- # Nonlinear name
- action = asset.obj.bfu_anim_nla_export_name
- else:
- action = "..."
- row.label(
- text="- ["+asset.name+"] --> " +
- action+" ("+asset.asset_type+")")
- else:
- if asset.asset_type != "Collection StaticMesh":
- row.label(
- text="- "+asset.name +
- " ("+asset.asset_type+")")
- else:
- row.label(
- text="- "+asset.obj.name +
- " ("+asset.asset_type+")")
-
- else:
- row.label(text="- ("+asset.asset_type+")")
- bpy.context.window_manager.popup_menu(
- draw,
- title=popup_title,
- icon='PACKAGE')
- return {'FINISHED'}
-
- class BFU_OT_CheckPotentialErrorPopup(bpy.types.Operator):
- bl_label = "Check Potential Errors"
- bl_idname = "object.checkpotentialerror"
- bl_description = "Check potential errors."
- text = "none"
-
- def execute(self, context):
- fix_info = bfu_check_potential_error.process_general_fix()
- invoke_info = ""
- for x, fix_info_key in enumerate(fix_info):
- fix_info_data = fix_info[fix_info_key]
- invoke_info += fix_info_key + ": " + str(fix_info_data)
- if x < len(fix_info)-1:
- invoke_info += "\n"
-
-
- bfu_check_potential_error.UpdateUnrealPotentialError()
- bpy.ops.object.openpotentialerror(
- "INVOKE_DEFAULT",
- invoke_info=invoke_info,
- )
- return {'FINISHED'}
-
- class BFU_OT_OpenPotentialErrorPopup(bpy.types.Operator):
- bl_label = "Open potential errors"
- bl_idname = "object.openpotentialerror"
- bl_description = "Open potential errors"
- invoke_info: bpy.props.StringProperty(default="...")
-
- class BFU_OT_FixitTarget(bpy.types.Operator):
- bl_label = "Fix it !"
- bl_idname = "object.fixit_objet"
- bl_description = "Correct target error"
- errorIndex: bpy.props.IntProperty(default=-1)
-
- def execute(self, context):
- result = bfu_check_potential_error.TryToCorrectPotentialError(self.errorIndex)
- self.report({'INFO'}, result)
- return {'FINISHED'}
-
- class BFU_OT_SelectObjectButton(bpy.types.Operator):
- bl_label = "Select(Object)"
- bl_idname = "object.select_error_objet"
- bl_description = "Select target Object."
- errorIndex: bpy.props.IntProperty(default=-1)
-
- def execute(self, context):
- bfu_check_potential_error.SelectPotentialErrorObject(self.errorIndex)
- return {'FINISHED'}
-
- class BFU_OT_SelectVertexButton(bpy.types.Operator):
- bl_label = "Select(Vertex)"
- bl_idname = "object.select_error_vertex"
- bl_description = "Select target Vertex."
- errorIndex: bpy.props.IntProperty(default=-1)
-
- def execute(self, context):
- bfu_check_potential_error.SelectPotentialErrorVertex(self.errorIndex)
- return {'FINISHED'}
-
- class BFU_OT_SelectPoseBoneButton(bpy.types.Operator):
- bl_label = "Select(PoseBone)"
- bl_idname = "object.select_error_posebone"
- bl_description = "Select target Pose Bone."
- errorIndex: bpy.props.IntProperty(default=-1)
-
- def execute(self, context):
- bfu_check_potential_error.SelectPotentialErrorPoseBone(self.errorIndex)
- return {'FINISHED'}
-
- class BFU_OT_OpenPotentialErrorDocs(bpy.types.Operator):
- bl_label = "Open docs"
- bl_idname = "object.open_potential_error_docs"
- bl_description = "Open potential error docs."
- octicon: bpy.props.StringProperty(default="")
-
- def execute(self, context):
- os.system(
- "start \"\" " +
- "https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/How-avoid-potential-errors" +
- "#"+self.octicon)
- return {'FINISHED'}
-
- def execute(self, context):
- return {'FINISHED'}
-
- def invoke(self, context, event):
- wm = context.window_manager
- return wm.invoke_popup(self, width=1020)
-
- def check(self, context):
- return True
-
- def draw(self, context):
-
- layout = self.layout
- if len(bpy.context.scene.potentialErrorList) > 0:
- popup_title = (
- str(len(bpy.context.scene.potentialErrorList)) +
- " potential error(s) found!")
- else:
- popup_title = "No potential error to correct!"
-
-
- layout.label(text=popup_title)
- invoke_info_lines = self.invoke_info.split("\n")
- for invoke_info_line in invoke_info_lines:
- layout.label(text="- "+invoke_info_line)
-
- layout.separator()
- row = layout.row()
- col = row.column()
- for x in range(len(bpy.context.scene.potentialErrorList)):
- error = bpy.context.scene.potentialErrorList[x]
-
- myLine = col.box().split(factor=0.85)
- # ----
- if error.type == 0:
- msgType = 'INFO'
- msgIcon = 'INFO'
- elif error.type == 1:
- msgType = 'WARNING'
- msgIcon = 'ERROR'
- elif error.type == 2:
- msgType = 'ERROR'
- msgIcon = 'CANCEL'
- # ----
-
- # Text
- TextLine = myLine.column()
- errorFullMsg = msgType+": "+error.text
- splitedText = errorFullMsg.split("\n")
-
- for text, Line in enumerate(splitedText):
- if (text < 1):
-
- FisrtTextLine = TextLine.row()
- if (error.docsOcticon != "None"): # Doc button
- props = FisrtTextLine.operator(
- "object.open_potential_error_docs",
- icon="HELP",
- text="")
- props.octicon = error.docsOcticon
-
- FisrtTextLine.label(text=Line, icon=msgIcon)
- else:
- TextLine.label(text=Line)
-
- # Select and fix button
- ButtonLine = myLine.column()
- if (error.correctRef != "None"):
- props = ButtonLine.operator(
- "object.fixit_objet",
- text=error.correctlabel)
- props.errorIndex = x
- if (error.object is not None):
- if (error.selectObjectButton):
- props = ButtonLine.operator(
- "object.select_error_objet")
- props.errorIndex = x
- if (error.selectVertexButton):
- props = ButtonLine.operator(
- "object.select_error_vertex")
- props.errorIndex = x
- if (error.selectPoseBoneButton):
- props = ButtonLine.operator(
- "object.select_error_posebone")
- props.errorIndex = x
-
- class BFU_OT_ExportForUnrealEngineButton(bpy.types.Operator):
- bl_label = "Export for Unreal Engine"
- bl_idname = "object.exportforunreal"
- bl_description = "Export all assets of this scene."
-
- def execute(self, context):
- scene = bpy.context.scene
-
- def isReadyForExport():
-
- def GetIfOneTypeCheck():
- all_assets = bfu_assets_manager.bfu_asset_manager_utils.get_all_asset_class()
- for assets in all_assets:
- assets: bfu_assets_manager.bfu_asset_manager_type.BFU_BaseAssetClass
- if assets.can_export_asset():
- return True
-
- if (scene.static_collection_export
- or scene.anin_export):
- return True
- else:
- return False
-
- if not bfu_basics.CheckPluginIsActivated("io_scene_fbx"):
- self.report(
- {'WARNING'},
- 'Add-on FBX format is not activated!' +
- ' Edit > Preferences > Add-ons > And check "FBX format"')
- return False
-
- if not GetIfOneTypeCheck():
- self.report(
- {'WARNING'},
- "No asset type is checked.")
- return False
-
- final_asset_cache = bfu_cached_asset_list.GetfinalAssetCache()
- final_asset_list_to_export = final_asset_cache.GetFinalAssetList()
- if not len(final_asset_list_to_export) > 0:
- self.report(
- {'WARNING'},
- "Not found assets with" +
- " \"Export recursive\" properties " +
- "or collection to export.")
- return False
-
- if not bpy.data.is_saved:
- # Primary check if file is saved
- # to avoid windows PermissionError
- self.report(
- {'WARNING'},
- "Please save this .blend file before export.")
- return False
-
- if bbpl.scene_utils.is_tweak_mode():
- # Need exit Tweakmode because the Animation data is read only.
- self.report(
- {'WARNING'},
- "Exit Tweakmode in NLA Editor. [Tab]")
- return False
-
- return True
-
- if not isReadyForExport():
- return {'FINISHED'}
-
- scene.UnrealExportedAssetsList.clear()
- counter = bps.utils.CounterTimer()
- bfu_check_potential_error.process_general_fix()
- bfu_export.bfu_export_asset.process_export(self)
- bfu_write_text.WriteAllTextFiles()
-
- self.report(
- {'INFO'},
- "Export of " + str(len(scene.UnrealExportedAssetsList)) + " asset(s) has been finalized in " + counter.get_str_time() + " Look in console for more info.")
- print(
- "=========================" +
- " Exported asset(s) " +
- "=========================")
- print("")
- lines = bfu_write_text.WriteExportLog().splitlines()
- for line in lines:
- print(line)
- print("")
- print(
- "=========================" +
- " ... " +
- "=========================")
-
- return {'FINISHED'}
-
- class BFU_OT_CopyImportAssetScriptCommand(bpy.types.Operator):
- bl_label = "Copy import script (Assets)"
- bl_idname = "object.copy_importassetscript_command"
- bl_description = "Copy Import Asset Script command"
-
- def execute(self, context):
- scene = context.scene
- bfu_basics.setWindowsClipboard(bfu_utils.GetImportAssetScriptCommand())
- self.report(
- {'INFO'},
- "command for "+scene.bfu_file_import_asset_script_name +
- " copied")
- return {'FINISHED'}
-
- class BFU_OT_CopyImportSequencerScriptCommand(bpy.types.Operator):
- bl_label = "Copy import script (Sequencer)"
- bl_idname = "object.copy_importsequencerscript_command"
- bl_description = "Copy Import Sequencer Script command"
-
- def execute(self, context):
- scene = context.scene
- bfu_basics.setWindowsClipboard(bfu_utils.GetImportSequencerScriptCommand())
- self.report(
- {'INFO'},
- "command for "+scene.bfu_file_import_sequencer_script_name +
- " copied")
- return {'FINISHED'}
-
- # Categories :
- bpy.types.Scene.static_export = bpy.props.BoolProperty(
- name="StaticMesh(s)",
- description="Check mark to export StaticMesh(s)",
- default=True
- )
-
- bpy.types.Scene.static_collection_export = bpy.props.BoolProperty(
- name="Collection(s) ",
- description="Check mark to export Collection(s)",
- default=True
- )
-
- bpy.types.Scene.skeletal_export = bpy.props.BoolProperty(
- name="SkeletalMesh(s)",
- description="Check mark to export SkeletalMesh(s)",
- default=True
- )
-
- bpy.types.Scene.anin_export = bpy.props.BoolProperty(
- name="Animation(s)",
- description="Check mark to export Animation(s)",
- default=True
- )
-
- bpy.types.Scene.alembic_export = bpy.props.BoolProperty(
- name="Alembic Animation(s)",
- description="Check mark to export Alembic animation(s)",
- default=True
- )
-
- bpy.types.Scene.groom_simulation_export = bpy.props.BoolProperty(
- name="Groom Simulation(s)",
- description="Check mark to export Alembic animation(s)",
- default=True
- )
-
- bpy.types.Scene.camera_export = bpy.props.BoolProperty(
- name="Camera(s)",
- description="Check mark to export Camera(s)",
- default=True
- )
-
- bpy.types.Scene.spline_export = bpy.props.BoolProperty(
- name="Spline(s)",
- description="Check mark to export Spline(s)",
- default=True
- )
-
- # Additional file
- bpy.types.Scene.text_ExportLog = bpy.props.BoolProperty(
- name="Export Log",
- description="Check mark to write export log file",
- default=True
- )
-
- bpy.types.Scene.text_ImportAssetScript = bpy.props.BoolProperty(
- name="Import assets script",
- description="Check mark to write import asset script file",
- default=True
- )
-
- bpy.types.Scene.text_ImportSequenceScript = bpy.props.BoolProperty(
- name="Import sequence script",
- description="Check mark to write import sequencer script file",
- default=True
- )
-
- bpy.types.Scene.text_AdditionalData = bpy.props.BoolProperty(
- name="Additional data",
- description=(
- "Check mark to write additional data" +
- " like parameter or anim tracks"),
- default=True
- )
-
- # exportProperty
- bpy.types.Scene.bfu_export_selection_filter = bpy.props.EnumProperty(
- name="Selection filter",
- items=[
- ('default', "No Filter", "Export as normal all objects with the recursive export option.", 0),
- ('only_object', "Only selected", "Export only the selected and visible object(s)", 1),
- ('only_object_action', "Only selected and active action",
- "Export only the selected and visible object(s) and active action on this object", 2),
- ],
- description=(
- "Choose what need be export from asset list."),
- default="default"
- )
-
- def draw(self, context):
- scene = context.scene
- scene = context.scene
- addon_prefs = bfu_basics.GetAddonPrefs()
-
- # Categories :
- layout = self.layout
-
- # Presets
- row = self.layout.row(align=True)
- row.menu('BFU_MT_NomenclaturePresets', text='Export Presets')
- row.operator('object.add_nomenclature_preset', text='', icon='ADD')
- row.operator(
- 'object.add_nomenclature_preset',
- text='',
- icon='REMOVE').remove_active = True
-
- scene.bfu_nomenclature_properties_expanded.draw(layout)
- if scene.bfu_nomenclature_properties_expanded.is_expend():
-
- # Prefix
- propsPrefix = self.layout.row()
- propsPrefix = propsPrefix.column()
- propsPrefix.prop(scene, 'bfu_static_mesh_prefix_export_name', icon='OBJECT_DATA')
- propsPrefix.prop(scene, 'bfu_skeletal_mesh_prefix_export_name', icon='OBJECT_DATA')
- propsPrefix.prop(scene, 'bfu_skeleton_prefix_export_name', icon='OBJECT_DATA')
- propsPrefix.prop(scene, 'bfu_alembic_animation_prefix_export_name', icon='OBJECT_DATA')
- propsPrefix.prop(scene, 'bfu_groom_simulation_prefix_export_name', icon='OBJECT_DATA')
- propsPrefix.prop(scene, 'bfu_anim_prefix_export_name', icon='OBJECT_DATA')
- propsPrefix.prop(scene, 'bfu_pose_prefix_export_name', icon='OBJECT_DATA')
- propsPrefix.prop(scene, 'bfu_camera_prefix_export_name', icon='OBJECT_DATA')
- propsPrefix.prop(scene, 'bfu_spline_prefix_export_name', icon='OBJECT_DATA')
-
- # Sub folder
- propsSub = self.layout.row()
- propsSub = propsSub.column()
- propsSub.prop(scene, 'bfu_anim_subfolder_name', icon='FILE_FOLDER')
-
- if addon_prefs.useGeneratedScripts:
- bfu_unreal_import_module = propsSub.column()
- bfu_unreal_import_module.prop(
- scene,
- 'bfu_unreal_import_module',
- icon='FILE_FOLDER')
- bfu_unreal_import_location = propsSub.column()
- bfu_unreal_import_location.prop(
- scene,
- 'bfu_unreal_import_location',
- icon='FILE_FOLDER')
-
- # File path
- filePath = self.layout.row()
- filePath = filePath.column()
- filePath.prop(scene, 'bfu_export_static_file_path')
- filePath.prop(scene, 'bfu_export_skeletal_file_path')
- filePath.prop(scene, 'bfu_export_alembic_file_path')
- filePath.prop(scene, 'bfu_export_groom_file_path')
- filePath.prop(scene, 'bfu_export_camera_file_path')
- filePath.prop(scene, 'bfu_export_spline_file_path')
- filePath.prop(scene, 'bfu_export_other_file_path')
-
- # File name
- fileName = self.layout.row()
- fileName = fileName.column()
- fileName.prop(scene, 'bfu_file_export_log_name', icon='FILE')
- if addon_prefs.useGeneratedScripts:
- fileName.prop(
- scene,
- 'bfu_file_import_asset_script_name',
- icon='FILE')
- fileName.prop(
- scene,
- 'bfu_file_import_sequencer_script_name',
- icon='FILE')
-
- scene.bfu_export_filter_properties_expanded.draw(layout)
- if scene.bfu_export_filter_properties_expanded.is_expend():
-
- # Assets
- row = layout.row()
- AssetsCol = row.column()
- AssetsCol.label(text="Asset types to export", icon='PACKAGE')
- AssetsCol.prop(scene, 'static_export')
- AssetsCol.prop(scene, 'static_collection_export')
- AssetsCol.prop(scene, 'skeletal_export')
- AssetsCol.prop(scene, 'anin_export')
- AssetsCol.prop(scene, 'alembic_export')
- AssetsCol.prop(scene, 'groom_simulation_export')
- AssetsCol.prop(scene, 'camera_export')
- AssetsCol.prop(scene, 'spline_export')
- layout.separator()
-
- # Additional file
- FileCol = row.column()
- FileCol.label(text="Additional file", icon='PACKAGE')
- FileCol.prop(scene, 'text_ExportLog')
- FileCol.prop(scene, 'text_ImportAssetScript')
- FileCol.prop(scene, 'text_ImportSequenceScript')
- if addon_prefs.useGeneratedScripts:
- FileCol.prop(scene, 'text_AdditionalData')
-
- # exportProperty
- export_by_select = layout.row()
- export_by_select.prop(scene, 'bfu_export_selection_filter')
-
- scene.bfu_export_process_properties_expanded.draw(layout)
- if scene.bfu_export_process_properties_expanded.is_expend():
-
- # Feedback info :
- final_asset_cache = bfu_cached_asset_list.GetfinalAssetCache()
- final_asset_list_to_export = final_asset_cache.GetFinalAssetList()
- AssetNum = len(final_asset_list_to_export)
- AssetInfo = layout.row().box().split(factor=0.75)
- AssetFeedback = str(AssetNum) + " Asset(s) will be exported."
- AssetInfo.label(text=AssetFeedback, icon='INFO')
- AssetInfo.operator("object.showasset")
-
- # Export button :
- checkButton = layout.row(align=True)
- checkButton.operator("object.checkpotentialerror", icon='FILE_TICK')
- checkButton.operator("object.openpotentialerror", icon='LOOP_BACK', text="")
-
- exportButton = layout.row()
- exportButton.scale_y = 2.0
- exportButton.operator("object.exportforunreal", icon='EXPORT')
-
- scene.bfu_script_tool_expanded.draw(layout)
- if scene.bfu_script_tool_expanded.is_expend():
- if addon_prefs.useGeneratedScripts:
- copyButton = layout.row()
- copyButton.operator("object.copy_importassetscript_command")
- copyButton.operator("object.copy_importsequencerscript_command")
- layout.label(text="Click on one of the buttons to copy the import command.", icon='INFO')
- layout.label(text="Then paste it into the cmd console of unreal.")
- layout.label(text="You need activate python plugins in Unreal Engine.")
-
- else:
- layout.label(text='(Generated scripts are deactivated.)')
-
-# -------------------------------------------------------------------
-# Register & Unregister
-# -------------------------------------------------------------------
-
-classes = (
- BFU_PT_Export,
- BFU_PT_Export.BFU_MT_NomenclaturePresets,
- BFU_PT_Export.BFU_OT_AddNomenclaturePreset,
- BFU_PT_Export.BFU_OT_ShowAssetToExport,
- BFU_PT_Export.BFU_OT_CheckPotentialErrorPopup,
- BFU_PT_Export.BFU_OT_OpenPotentialErrorPopup,
- BFU_PT_Export.BFU_OT_OpenPotentialErrorPopup.BFU_OT_FixitTarget,
- BFU_PT_Export.BFU_OT_OpenPotentialErrorPopup.BFU_OT_SelectObjectButton,
- BFU_PT_Export.BFU_OT_OpenPotentialErrorPopup.BFU_OT_SelectVertexButton,
- BFU_PT_Export.BFU_OT_OpenPotentialErrorPopup.BFU_OT_SelectPoseBoneButton,
- BFU_PT_Export.BFU_OT_OpenPotentialErrorPopup.BFU_OT_OpenPotentialErrorDocs,
- BFU_PT_Export.BFU_OT_ExportForUnrealEngineButton,
- BFU_PT_Export.BFU_OT_CopyImportAssetScriptCommand,
- BFU_PT_Export.BFU_OT_CopyImportSequencerScriptCommand,
-)
-
-
-def register():
- for cls in classes:
- bpy.utils.register_class(cls)
-
-
-def unregister():
- for cls in reversed(classes):
- bpy.utils.unregister_class(cls)
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_object_ui_and_props.py b/blender-for-unrealengine/bfu_addon_parts/bfu_object_ui_and_props.py
deleted file mode 100644
index 814d9293..00000000
--- a/blender-for-unrealengine/bfu_addon_parts/bfu_object_ui_and_props.py
+++ /dev/null
@@ -1,1653 +0,0 @@
-# ====================== BEGIN GPL LICENSE BLOCK ============================
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-# All rights reserved.
-#
-# ======================= END GPL LICENSE BLOCK =============================
-
-
-import os
-import bpy
-import addon_utils
-from . import bfu_modular_skeletal_specified_parts_meshs
-from . import bfu_unreal_engine_refs_props
-from .. import bbpl
-from .. import bps
-from .. import bfu_export_procedure
-from .. import bfu_basics
-from .. import bfu_utils
-from .. import bfu_cached_asset_list
-from .. import bfu_export
-from .. import bfu_ui
-from .. import languages
-from .. import bfu_custom_property
-from .. import bfu_material
-from .. import bfu_camera
-from .. import bfu_spline
-from .. import bfu_vertex_color
-from .. import bfu_static_mesh
-from .. import bfu_skeletal_mesh
-from .. import bfu_lod
-from .. import bfu_alembic_animation
-from .. import bfu_groom
-from .. import bfu_assets_manager
-from .. import bfu_light_map
-
-
-class BFU_PT_BlenderForUnrealObject(bpy.types.Panel):
- # Unreal engine export panel
-
- bl_idname = "BFU_PT_BlenderForUnrealObject"
- bl_label = "Blender for Unreal Engine"
- bl_space_type = "VIEW_3D"
- bl_region_type = "UI"
- bl_category = "Unreal Engine"
-
- # Object Properties
- bpy.types.Object.bfu_export_type = bpy.props.EnumProperty(
- name="Export type",
- description="Export procedure",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ("auto",
- "Auto",
- "Export with the parent if the parents is \"Export recursive\"",
- "BOIDS",
- 1),
- ("export_recursive",
- "Export recursive",
- "Export self object and all children",
- "KEYINGSET",
- 2),
- ("dont_export",
- "Not exported",
- "Will never export",
- "CANCEL",
- 3)
- ]
- )
-
- bpy.types.Object.bfu_export_folder_name = bpy.props.StringProperty(
- name="Sub folder name",
- description=(
- 'The name of sub folder.' +
- ' You can now use ../ for up one directory.'
- ),
- override={'LIBRARY_OVERRIDABLE'},
- maxlen=64,
- default="",
- subtype='FILE_NAME'
- )
-
- # Collection Properties
- bpy.types.Collection.bfu_export_folder_name = bpy.props.StringProperty(
- name="Sub folder name",
- description=(
- 'The name of sub folder.' +
- ' You can now use ../ for up one directory.'
- ),
- override={'LIBRARY_OVERRIDABLE'},
- maxlen=64,
- default="",
- subtype='FILE_NAME'
- )
-
- bpy.types.Object.bfu_export_as_lod_mesh = bpy.props.BoolProperty(
- name="Export as lod?",
- description=(
- "If true this mesh will be exported" +
- " as a level of detail for another mesh"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_export_deform_only = bpy.props.BoolProperty(
- name="Export only deform bones",
- description=(
- "Only write deforming bones" +
- " (and non-deforming ones when they have deforming children)"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=True
- )
-
- bpy.types.Object.bfu_use_custom_export_name = bpy.props.BoolProperty(
- name="Export with custom name",
- description=("Specify a custom name for the exported file"),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_custom_export_name = bpy.props.StringProperty(
- name="",
- description="The name of exported file",
- override={'LIBRARY_OVERRIDABLE'},
- default="MyObjectExportName.fbx"
- )
-
- # Object Import Properties
-
- # Lod list
- bpy.types.Object.bfu_lod_target1 = bpy.props.PointerProperty(
- name="LOD1",
- description="Target objet for level of detail 01",
- override={'LIBRARY_OVERRIDABLE'},
- type=bpy.types.Object
- )
-
- bpy.types.Object.bfu_lod_target2 = bpy.props.PointerProperty(
- name="LOD2",
- description="Target objet for level of detail 02",
- override={'LIBRARY_OVERRIDABLE'},
- type=bpy.types.Object
- )
-
- bpy.types.Object.bfu_lod_target3 = bpy.props.PointerProperty(
- name="LOD3",
- description="Target objet for level of detail 03",
- override={'LIBRARY_OVERRIDABLE'},
- type=bpy.types.Object
- )
-
- bpy.types.Object.bfu_lod_target4 = bpy.props.PointerProperty(
- name="LOD4",
- description="Target objet for level of detail 04",
- override={'LIBRARY_OVERRIDABLE'},
- type=bpy.types.Object
- )
-
- bpy.types.Object.bfu_lod_target5 = bpy.props.PointerProperty(
- name="LOD5",
- description="Target objet for level of detail 05",
- override={'LIBRARY_OVERRIDABLE'},
- type=bpy.types.Object
- )
-
- # ImportUI
- # https://api.unrealengine.com/INT/API/Editor/UnrealEd/Factories/UFbxImportUI/index.html
-
- bpy.types.Object.bfu_create_physics_asset = bpy.props.BoolProperty(
- name="Create PhysicsAsset",
- description="If checked, create a PhysicsAsset when is imported",
- override={'LIBRARY_OVERRIDABLE'},
- default=True
- )
-
-
- # StaticMeshImportData
- # https://api.unrealengine.com/INT/API/Editor/UnrealEd/Factories/UFbxStaticMeshImportData/index.html
-
- bpy.types.Object.bfu_use_static_mesh_lod_group = bpy.props.BoolProperty(
- name="",
- description='',
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_static_mesh_lod_group = bpy.props.StringProperty(
- name="LOD Group",
- description=(
- "The LODGroup to associate with this mesh when it is imported." +
- " Default: LevelArchitecture, SmallProp, " +
- "LargeProp, Deco, Vista, Foliage, HighDetail"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- maxlen=32,
- default="SmallProp"
- )
-
- bpy.types.Object.bfu_static_mesh_light_map_mode = bpy.props.EnumProperty(
- name="Light Map",
- description='Specify how the light map resolution will be generated',
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ("Default",
- "Default",
- "Has no effect on light maps",
- 1),
- ("CustomMap",
- "Custom map",
- "Set the custom light map resolution",
- 2),
- ("SurfaceArea",
- "Surface Area",
- "Set light map resolution depending on the surface Area",
- 3)
- ]
- )
-
- bpy.types.Object.bfu_static_mesh_custom_light_map_res = bpy.props.IntProperty(
- name="Light Map Resolution",
- description="This is the resolution of the light map",
- override={'LIBRARY_OVERRIDABLE'},
- soft_max=2048,
- soft_min=16,
- max=4096, # Max for unreal
- min=4, # Min for unreal
- default=64
- )
-
- bpy.types.Object.computedStaticMeshLightMapRes = bpy.props.FloatProperty(
- name="Computed Light Map Resolution",
- description="This is the computed resolution of the light map",
- override={'LIBRARY_OVERRIDABLE'},
- default=64.0
- )
-
- bpy.types.Object.bfu_static_mesh_light_map_surface_scale = bpy.props.FloatProperty(
- name="Surface scale",
- description="This is for resacle the surface Area value",
- override={'LIBRARY_OVERRIDABLE'},
- min=0.00001, # Min for unreal
- default=64
- )
-
- bpy.types.Object.bfu_static_mesh_light_map_round_power_of_two = bpy.props.BoolProperty(
- name="Round power of 2",
- description=(
- "round Light Map resolution to nearest power of 2"
- ),
- default=True
- )
-
- bpy.types.Object.bfu_use_static_mesh_light_map_world_scale = bpy.props.BoolProperty(
- name="Use world scale",
- description=(
- "If not that will use the object scale."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_generate_light_map_uvs = bpy.props.BoolProperty(
- name="Generate LightmapUVs",
- description=(
- "If checked, UVs for Lightmap will automatically be generated."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=True,
- )
-
- bpy.types.Object.bfu_convert_geometry_node_attribute_to_uv = bpy.props.BoolProperty(
- name="Convert Attribute To Uv",
- description=(
- "convert target geometry node attribute to UV when found."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False,
- )
-
- bpy.types.Object.bfu_convert_geometry_node_attribute_to_uv_name = bpy.props.StringProperty(
- name="Attribute name",
- description=(
- "Name of the Attribute to convert"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default="UVMap",
- )
-
- bpy.types.Object.bfu_correct_extrem_uv_scale = bpy.props.BoolProperty(
- name=(languages.ti('correct_extrem_uv_scale_name')),
- description=(languages.tt('correct_extrem_uv_scale_desc')),
- override={'LIBRARY_OVERRIDABLE'},
- default=False,
- )
-
- bpy.types.Object.bfu_auto_generate_collision = bpy.props.BoolProperty(
- name="Auto Generate Collision",
- description=(
- "If checked, collision will automatically be generated" +
- " (ignored if custom collision is imported or used)."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=True,
- )
-
-
- bpy.types.Object.bfu_collision_trace_flag = bpy.props.EnumProperty(
- name="Collision Complexity",
- description="Collision Trace Flag",
- override={'LIBRARY_OVERRIDABLE'},
- # Vania python
- # https://docs.unrealengine.com/en-US/PythonAPI/class/CollisionTraceFlag.html
- # C++ API
- # https://api.unrealengine.com/INT/API/Runtime/Engine/PhysicsEngine/ECollisionTraceFlag/index.html
- items=[
- ("CTF_UseDefault",
- "Project Default",
- "Create only complex shapes (per poly)." +
- " Use complex shapes for all scene queries" +
- " and collision tests." +
- " Can be used in simulation for" +
- " static shapes only" +
- " (i.e can be collided against but not moved" +
- " through forces or velocity.",
- 1),
- ("CTF_UseSimpleAndComplex",
- "Use Simple And Complex",
- "Use project physics settings (DefaultShapeComplexity)",
- 2),
- ("CTF_UseSimpleAsComplex",
- "Use Simple as Complex",
- "Create both simple and complex shapes." +
- " Simple shapes are used for regular scene queries" +
- " and collision tests. Complex shape (per poly)" +
- " is used for complex scene queries.",
- 3),
- ("CTF_UseComplexAsSimple",
- "Use Complex as Simple",
- "Create only simple shapes." +
- " Use simple shapes for all scene" +
- " queries and collision tests.",
- 4)
- ]
- )
-
- bpy.types.Object.bfu_enable_skeletal_mesh_per_poly_collision = bpy.props.BoolProperty(
- name="Enable Per-Poly Collision",
- description="Enable per-polygon collision for Skeletal Mesh",
- default=False
- )
-
-
-
- bpy.types.Object.bfu_anim_action_export_enum = bpy.props.EnumProperty(
- name="Action to export",
- description="Export procedure for actions (Animations and poses)",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ("export_auto",
- "Export auto",
- "Export all actions connected to the bones names",
- "FILE_SCRIPT",
- 1),
- ("export_specific_list",
- "Export specific list",
- "Export only actions that are checked in the list",
- "LINENUMBERS_ON",
- 3),
- ("export_specific_prefix",
- "Export specific prefix",
- "Export only actions with a specific prefix" +
- " or the beginning of the actions names",
- "SYNTAX_ON",
- 4),
- ("dont_export",
- "Not exported",
- "No action will be exported",
- "MATPLANE",
- 5),
- ("export_current",
- "Export Current",
- "Export only the current actions",
- "FILE_SCRIPT",
- 6),
- ]
- )
-
- bpy.types.Object.bfu_prefix_name_to_export = bpy.props.StringProperty(
- # properties used with ""export_specific_prefix" on bfu_anim_action_export_enum
- name="Prefix name",
- description="Indicate the prefix of the actions that must be exported",
- override={'LIBRARY_OVERRIDABLE'},
- maxlen=32,
- default="Example_",
- )
-
- bpy.types.Object.bfu_anim_action_start_end_time_enum = bpy.props.EnumProperty(
- name="Action Start/End Time",
- description="Set when animation starts and end",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ("with_keyframes",
- "Auto",
- "The time will be defined according" +
- " to the first and the last frame",
- "KEYTYPE_KEYFRAME_VEC",
- 1),
- ("with_sceneframes",
- "Scene time",
- "Time will be equal to the scene time",
- "SCENE_DATA",
- 2),
- ("with_customframes",
- "Custom time",
- 'The time of all the animations of this object' +
- ' is defined by you.' +
- ' Use "bfu_anim_action_custom_start_frame" and "bfu_anim_action_custom_end_frame"',
- "HAND",
- 3),
- ]
- )
-
- bpy.types.Object.bfu_anim_action_start_frame_offset = bpy.props.IntProperty(
- name="Offset at start frame",
- description="Offset for the start frame.",
- override={'LIBRARY_OVERRIDABLE'},
- default=0
- )
-
- bpy.types.Object.bfu_anim_action_end_frame_offset = bpy.props.IntProperty(
- name="Offset at end frame",
- description=(
- "Offset for the end frame. +1" +
- " is recommended for the sequences | 0 is recommended" +
- " for UnrealEngine cycles | -1 is recommended for Sketchfab cycles"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=0
- )
-
- bpy.types.Object.bfu_anim_action_custom_start_frame = bpy.props.IntProperty(
- name="Custom start time",
- description="Set when animation start",
- override={'LIBRARY_OVERRIDABLE'},
- default=0
- )
-
- bpy.types.Object.bfu_anim_action_custom_end_frame = bpy.props.IntProperty(
- name="Custom end time",
- description="Set when animation end",
- override={'LIBRARY_OVERRIDABLE'},
- default=1
- )
-
- bpy.types.Object.bfu_anim_nla_start_end_time_enum = bpy.props.EnumProperty(
- name="NLA Start/End Time",
- description="Set when animation starts and end",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ("with_sceneframes",
- "Scene time",
- "Time will be equal to the scene time",
- "SCENE_DATA",
- 1),
- ("with_customframes",
- "Custom time",
- 'The time of all the animations of this object' +
- ' is defined by you.' +
- ' Use "bfu_anim_action_custom_start_frame" and "bfu_anim_action_custom_end_frame"',
- "HAND",
- 2),
- ]
- )
-
- bpy.types.Object.bfu_anim_nla_start_frame_offset = bpy.props.IntProperty(
- name="Offset at start frame",
- description="Offset for the start frame.",
- override={'LIBRARY_OVERRIDABLE'},
- default=0
- )
-
- bpy.types.Object.bfu_anim_nla_end_frame_offset = bpy.props.IntProperty(
- name="Offset at end frame",
- description=(
- "Offset for the end frame. +1" +
- " is recommended for the sequences | 0 is recommended" +
- " for UnrealEngine cycles | -1 is recommended for Sketchfab cycles"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=0
- )
-
- bpy.types.Object.bfu_anim_nla_custom_start_frame = bpy.props.IntProperty(
- name="Custom start time",
- description="Set when animation start",
- override={'LIBRARY_OVERRIDABLE'},
- default=0
- )
-
- bpy.types.Object.bfu_anim_nla_custom_end_frame = bpy.props.IntProperty(
- name="Custom end time",
- description="Set when animation end",
- override={'LIBRARY_OVERRIDABLE'},
- default=1
- )
-
-
- bpy.types.Object.bfu_sample_anim_for_export = bpy.props.FloatProperty(
- name="Sampling Rate",
- description="How often to evaluate animated values (in frames)",
- override={'LIBRARY_OVERRIDABLE'},
- min=0.01, max=100.0,
- soft_min=0.01, soft_max=100.0,
- default=1.0,
- )
-
- bpy.types.Object.bfu_simplify_anim_for_export = bpy.props.FloatProperty(
- name="Simplify animations",
- description=(
- "How much to simplify baked values" +
- " (0.0 to disable, the higher the more simplified)"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- # No simplification to up to 10% of current magnitude tolerance.
- min=0.0, max=100.0,
- soft_min=0.0, soft_max=10.0,
- default=0.0,
- )
-
- bpy.types.Object.bfu_disable_free_scale_animation = bpy.props.BoolProperty(
- name="Disable non-uniform scale animation.",
- description=(
- "If checked, scale animation track's elements always have same value. " +
- "This applies basic bones only."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_anim_nla_use = bpy.props.BoolProperty(
- name="Export NLA (Nonlinear Animation)",
- description=(
- "If checked, exports the all animation of the scene with the NLA " +
- "(Don't work with Auto-Rig Pro for the moment.)"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_anim_nla_export_name = bpy.props.StringProperty(
- name="NLA export name",
- description="Export NLA name (Don't work with Auto-Rig Pro for the moment.)",
- override={'LIBRARY_OVERRIDABLE'},
- maxlen=64,
- default="NLA_animation",
- subtype='FILE_NAME'
- )
-
- bpy.types.Object.bfu_anim_naming_type = bpy.props.EnumProperty(
- name="Naming type",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ('action_name', "Action name", 'Exemple: "Anim_MyAction"'),
- ('include_armature_name',
- "Include Armature Name",
- 'Include armature name in animation export file name.' +
- ' Exemple: "Anim_MyArmature_MyAction"'),
- ('include_custom_name',
- "Include custom name",
- 'Include custom name in animation export file name.' +
- ' Exemple: "Anim_MyCustomName_MyAction"'),
- ],
- default='action_name'
- )
-
- bpy.types.Object.bfu_anim_naming_custom = bpy.props.StringProperty(
- name="Export name",
- override={'LIBRARY_OVERRIDABLE'},
- default='MyCustomName'
- )
-
- bpy.types.Object.bfu_export_global_scale = bpy.props.FloatProperty(
- name="Global scale",
- description="Scale, change is not recommended with SkeletalMesh.",
- override={'LIBRARY_OVERRIDABLE'},
- default=1.0
- )
-
- bpy.types.Object.bfu_override_procedure_preset = bpy.props.BoolProperty(
- name="Override Export Preset",
- description="If true override the export precedure preset.",
- override={'LIBRARY_OVERRIDABLE'},
- default=False,
- )
-
- bpy.types.Object.bfu_export_use_space_transform = bpy.props.BoolProperty(
- name="Use Space Transform",
- default=True,
- )
-
- bpy.types.Object.bfu_export_axis_forward = bpy.props.EnumProperty(
- name="Axis Forward",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ('X', "X Forward", ""),
- ('Y', "Y Forward", ""),
- ('Z', "Z Forward", ""),
- ('-X', "-X Forward", ""),
- ('-Y', "-Y Forward", ""),
- ('-Z', "-Z Forward", ""),
- ],
- default='-Z',
- )
-
- bpy.types.Object.bfu_export_axis_up = bpy.props.EnumProperty(
- name="Axis Up",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ('X', "X Up", ""),
- ('Y', "Y Up", ""),
- ('Z', "Z Up", ""),
- ('-X', "-X Up", ""),
- ('-Y', "-Y Up", ""),
- ('-Z', "-Z Up", ""),
- ],
- default='Y',
- )
-
- bpy.types.Object.bfu_export_primary_bone_axis = bpy.props.EnumProperty(
- name="Primary Axis Bone",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ('X', "X", ""),
- ('Y', "Y", ""),
- ('Z', "Z", ""),
- ('-X', "-X", ""),
- ('-Y', "-Y", ""),
- ('-Z', "-Z", ""),
- ],
- default='Y',
- )
-
- bpy.types.Object.bfu_export_secondary_bone_axis = bpy.props.EnumProperty(
- name="Secondary Axis Bone",
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ('X', "X", ""),
- ('Y', "Y", ""),
- ('Z', "Z", ""),
- ('-X', "-X", ""),
- ('-Y', "-Y", ""),
- ('-Z', "-Z", ""),
- ],
- default='X',
- )
-
- bpy.types.Object.bfu_export_animation_without_mesh = bpy.props.BoolProperty(
- name="Export animation without mesh",
- description=(
- "If checked, When exporting animation, do not include mesh data in the FBX file."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=True
- )
-
- bpy.types.Object.bfu_mirror_symmetry_right_side_bones = bpy.props.BoolProperty(
- name="Revert direction of symmetry right side bones",
- description=(
- "If checked, The right-side bones will be mirrored for mirroring physic object in UE PhysicAsset Editor."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=True
- )
-
- bpy.types.Object.bfu_use_ue_mannequin_bone_alignment = bpy.props.BoolProperty(
- name="Apply bone alignments similar to UE Mannequin.",
- description=(
- "If checked, similar to the UE Mannequin, the leg bones will be oriented upwards, and the pelvis and feet bone will be aligned facing upwards during export."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_move_to_center_for_export = bpy.props.BoolProperty(
- name="Move to center",
- description=(
- "If true use object origin else use scene origin." +
- " | If true the mesh will be moved to the center" +
- " of the scene for export." +
- " (This is used so that the origin of the fbx file" +
- " is the same as the mesh in blender)"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=True
- )
-
- bpy.types.Object.bfu_rotate_to_zero_for_export = bpy.props.BoolProperty(
- name="Rotate to zero",
- description=(
- "If true use object rotation else use scene rotation." +
- " | If true the mesh will use zero rotation for export."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_move_action_to_center_for_export = bpy.props.BoolProperty(
- name="Move animation to center",
- description=(
- "(Action animation only) If true use object origin else use scene origin." +
- " | If true the mesh will be moved to the center" +
- " of the scene for export." +
- " (This is used so that the origin of the fbx file" +
- " is the same as the mesh in blender)" +
- " Note: Unreal Engine ignore the position of the skeleton at the import."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=True
- )
-
- bpy.types.Object.bfu_rotate_action_to_zero_for_export = bpy.props.BoolProperty(
- name="Rotate Action to zero",
- description=(
- "(Action animation only) If true use object rotation else use scene rotation." +
- " | If true the mesh will use zero rotation for export."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_move_nla_to_center_for_export = bpy.props.BoolProperty(
- name="Move NLA to center",
- description=(
- "(Non linear animation only) If true use object origin else use scene origin." +
- " | If true the mesh will be moved to the center" +
- " of the scene for export." +
- " (This is used so that the origin of the fbx file" +
- " is the same as the mesh in blender)" +
- " Note: Unreal Engine ignore the position of the skeleton at the import."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=True
- )
-
- bpy.types.Object.bfu_rotate_nla_to_zero_for_export = bpy.props.BoolProperty(
- name="Rotate NLA to zero",
- description=(
- "(Non linear animation only) If true use object rotation else use scene rotation." +
- " | If true the mesh will use zero rotation for export."
- ),
- override={'LIBRARY_OVERRIDABLE'},
- default=False
- )
-
- bpy.types.Object.bfu_additional_location_for_export = bpy.props.FloatVectorProperty(
- name="Additional location",
- description=(
- "This will add a additional absolute location to the mesh"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- subtype="TRANSLATION",
- default=(0, 0, 0)
- )
-
- bpy.types.Object.bfu_additional_rotation_for_export = bpy.props.FloatVectorProperty(
- name="Additional rotation",
- description=(
- "This will add a additional absolute rotation to the mesh"
- ),
- override={'LIBRARY_OVERRIDABLE'},
- subtype="EULER",
- default=(0, 0, 0)
- )
-
- # Scene and global
-
-
-
- class BFU_OT_OpenDocumentationPage(bpy.types.Operator):
- bl_label = "Documentation"
- bl_idname = "object.bfu_open_documentation_page"
- bl_description = "Clic for open documentation page on GitHub"
-
- def execute(self, context):
- os.system(
- "start \"\" " +
- "https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki"
- )
- return {'FINISHED'}
-
-
-
- # Animation :
-
- class BFU_UL_ActionExportTarget(bpy.types.UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_property, index):
- action_is_valid = False
- if item.name in bpy.data.actions:
- action_is_valid = True
-
- if self.layout_type in {'DEFAULT', 'COMPACT'}:
- if action_is_valid: # If action is valid
- layout.prop(
- bpy.data.actions[item.name],
- "name",
- text="",
- emboss=False,
- icon="ACTION"
- )
- layout.prop(item, "use", text="")
- else:
- dataText = (
- 'Action data named "' + item.name +
- '" Not Found. Please click on update'
- )
- layout.label(text=dataText, icon="ERROR")
- # Not optimized for 'GRID' layout type.
- elif self.layout_type in {'GRID'}:
- layout.alignment = 'CENTER'
- layout.label(text="", icon_value=icon)
-
- class BFU_OT_UpdateObjActionListButton(bpy.types.Operator):
- bl_label = "Update action list"
- bl_idname = "object.updateobjactionlist"
- bl_description = "Update action list"
-
- def execute(self, context):
- def UpdateExportActionList(obj):
- # Update the provisional action list known by the object
-
- def SetUseFromLast(anim_list, ActionName):
- for item in anim_list:
- if item[0] == ActionName:
- if item[1]:
- return True
- return False
-
- animSave = [["", False]]
- for Anim in obj.bfu_animation_asset_list: # CollectionProperty
- name = Anim.name
- use = Anim.use
- animSave.append([name, use])
- obj.bfu_animation_asset_list.clear()
- for action in bpy.data.actions:
- obj.bfu_animation_asset_list.add().name = action.name
- useFromLast = SetUseFromLast(animSave, action.name)
- obj.bfu_animation_asset_list[action.name].use = useFromLast
- UpdateExportActionList(bpy.context.object)
- return {'FINISHED'}
-
- class BFU_OT_ShowActionToExport(bpy.types.Operator):
- bl_label = "Show action(s)"
- bl_idname = "object.showobjaction"
- bl_description = (
- "Click to show actions that are" +
- " to be exported with this armature."
- )
-
- def execute(self, context):
- obj = context.object
- animation_asset_cache = bfu_cached_asset_list.GetAnimationAssetCache(obj)
- animation_asset_cache.UpdateActionCache()
- animation_to_export = animation_asset_cache.GetAnimationAssetList()
-
- popup_title = "Action list"
- if len(animation_to_export) > 0:
- animationNumber = len(animation_to_export)
- if obj.bfu_anim_nla_use:
- animationNumber += 1
- popup_title = (
- str(animationNumber) +
- ' action(s) found for obj named "'+obj.name+'".'
- )
- else:
- popup_title = (
- 'No action found for obj named "' +
- obj.name+'".')
-
- def draw(self, context):
- col = self.layout.column()
-
- def addAnimRow(
- action_name,
- action_type,
- frame_start,
- frame_end):
- row = col.row()
- row.label(
- text="- ["+action_name +
- "] Frame "+frame_start+" to "+frame_end +
- " ("+action_type+")"
- )
-
- for action in animation_to_export:
- Frames = bfu_utils.GetDesiredActionStartEndTime(obj, action)
- frame_start = str(Frames[0])
- frame_end = str(Frames[1])
- addAnimRow(action.name, bfu_utils.GetActionType(action), frame_start, frame_end)
- if obj.bfu_anim_nla_use:
- scene = context.scene
- addAnimRow(obj.bfu_anim_nla_export_name, "NlAnim", str(scene.frame_start), str(scene.frame_end))
-
- bpy.context.window_manager.popup_menu(
- draw,
- title=popup_title,
- icon='ACTION'
- )
- return {'FINISHED'}
-
- class BFU_MT_ObjectGlobalPropertiesPresets(bpy.types.Menu):
- bl_label = 'Global Properties Presets'
- preset_subdir = 'blender-for-unrealengine/global-properties-presets'
- preset_operator = 'script.execute_preset'
- draw = bpy.types.Menu.draw_preset
-
- from bl_operators.presets import AddPresetBase
-
-
-
- class BFU_OT_AddObjectGlobalPropertiesPreset(AddPresetBase, bpy.types.Operator):
- bl_idname = 'object.add_globalproperties_preset'
- bl_label = 'Add or remove a preset for Global properties'
- bl_description = 'Add or remove a preset for Global properties'
- preset_menu = 'BFU_MT_ObjectGlobalPropertiesPresets'
-
- def get_object_global_preset_propertys():
- preset_values = [
- 'obj.bfu_export_type',
- 'obj.bfu_export_folder_name',
- 'col.bfu_export_folder_name',
- 'obj.bfu_export_as_lod_mesh',
- 'obj.bfu_export_deform_only',
- 'obj.bfu_lod_target1',
- 'obj.bfu_lod_target2',
- 'obj.bfu_lod_target3',
- 'obj.bfu_lod_target4',
- 'obj.bfu_lod_target5',
- 'obj.bfu_create_physics_asset',
- 'obj.bfu_use_static_mesh_lod_group',
- 'obj.bfu_static_mesh_lod_group',
- 'obj.bfu_static_mesh_light_map_mode',
- 'obj.bfu_static_mesh_custom_light_map_res',
- 'obj.bfu_static_mesh_light_map_surface_scale',
- 'obj.bfu_static_mesh_light_map_round_power_of_two',
- 'obj.bfu_use_static_mesh_light_map_world_scale',
- 'obj.bfu_generate_light_map_uvs',
- 'obj.bfu_convert_geometry_node_attribute_to_uv',
- 'obj.bfu_convert_geometry_node_attribute_to_uv_name',
- 'obj.bfu_correct_extrem_uv_scale',
- 'obj.bfu_auto_generate_collision',
- 'obj.bfu_collision_trace_flag',
- 'obj.bfu_enable_skeletal_mesh_per_poly_collision',
- 'obj.bfu_anim_action_export_enum',
- 'obj.bfu_prefix_name_to_export',
- 'obj.bfu_anim_action_start_end_time_enum',
- 'obj.bfu_anim_nla_start_end_time_enum',
- 'obj.bfu_anim_action_start_frame_offset',
- 'obj.bfu_anim_action_end_frame_offset',
- 'obj.bfu_anim_action_custom_start_frame',
- 'obj.bfu_anim_action_custom_end_frame',
- 'obj.bfu_anim_nla_start_frame_offset',
- 'obj.bfu_anim_nla_end_frame_offset',
- 'obj.bfu_anim_nla_custom_start_frame',
- 'obj.bfu_anim_nla_custom_end_frame',
- 'obj.bfu_sample_anim_for_export',
- 'obj.bfu_simplify_anim_for_export',
- 'obj.bfu_anim_nla_use',
- 'obj.bfu_anim_nla_export_name',
- 'obj.bfu_anim_naming_type',
- 'obj.bfu_anim_naming_custom',
- 'obj.bfu_export_global_scale',
- 'obj.bfu_override_procedure_preset',
- 'obj.bfu_export_use_space_transform',
- 'obj.bfu_export_axis_forward',
- 'obj.bfu_export_axis_up',
- 'obj.bfu_export_with_meta_data',
- 'obj.bfu_export_axis_forward',
- 'obj.bfu_export_axis_up',
- 'obj.bfu_export_primary_bone_axis',
- 'obj.bfu_export_secondary_bone_axis',
- 'obj.bfu_export_animation_without_mesh',
- 'obj.bfu_mirror_symmetry_right_side_bones',
- 'obj.bfu_use_ue_mannequin_bone_alignment',
- 'obj.bfu_disable_free_scale_animation',
- 'obj.bfu_move_to_center_for_export',
- 'obj.bfu_rotate_to_zero_for_export',
- 'obj.bfu_move_action_to_center_for_export',
- 'obj.bfu_rotate_action_to_zero_for_export',
- 'obj.bfu_move_nla_to_center_for_export',
- 'obj.bfu_rotate_nla_to_zero_for_export',
- 'obj.bfu_additional_location_for_export',
- 'obj.bfu_additional_rotation_for_export',
- ]
- preset_values += bfu_modular_skeletal_specified_parts_meshs.get_preset_values()
- preset_values += bfu_unreal_engine_refs_props.get_preset_values()
- preset_values += bfu_custom_property.bfu_custom_property_props.get_preset_values()
- preset_values += bfu_material.bfu_material_props.get_preset_values()
- preset_values += bfu_camera.bfu_camera_ui_and_props.get_preset_values()
- preset_values += bfu_spline.bfu_spline_ui_and_props.get_preset_values()
- preset_values += bfu_static_mesh.bfu_static_mesh_props.get_preset_values()
- preset_values += bfu_skeletal_mesh.bfu_skeletal_mesh_props.get_preset_values()
- preset_values += bfu_alembic_animation.bfu_alembic_animation_props.get_preset_values()
- preset_values += bfu_vertex_color.bfu_vertex_color_props.get_preset_values()
- return preset_values
-
- # Common variable used for all preset values
- preset_defines = [
- 'obj = bpy.context.object',
- 'col = bpy.context.collection',
- 'scene = bpy.context.scene'
- ]
-
- # Properties to store in the preset
- preset_values = get_object_global_preset_propertys()
-
- # Directory to store the presets
- preset_subdir = 'blender-for-unrealengine/global-properties-presets'
-
-
-
- class BFU_UL_CollectionExportTarget(bpy.types.UIList):
-
- def draw_item(self, context, layout, data, item, icon, active_data, active_property, index, flt_flag):
-
- collection_is_valid = False
- if item.name in bpy.data.collections:
- collection_is_valid = True
-
- if self.layout_type in {'DEFAULT', 'COMPACT'}:
- if collection_is_valid: # If action is valid
- layout.prop(
- bpy.data.collections[item.name],
- "name",
- text="",
- emboss=False,
- icon="OUTLINER_COLLECTION")
- layout.prop(item, "use", text="")
- else:
- dataText = (
- 'Collection named "' +
- item.name +
- '" Not Found. Please clic on update')
- layout.label(text=dataText, icon="ERROR")
- # Not optimised for 'GRID' layout type.
- elif self.layout_type in {'GRID'}:
- layout.alignment = 'CENTER'
- layout.label(text="", icon_value=icon)
-
- class BFU_OT_UpdateCollectionButton(bpy.types.Operator):
- bl_label = "Update collection list"
- bl_idname = "object.updatecollectionlist"
- bl_description = "Update collection list"
-
- def execute(self, context):
- def UpdateExportCollectionList(scene):
- # Update the provisional collection list known by the object
-
- def SetUseFromLast(col_list, CollectionName):
- for item in col_list:
- if item[0] == CollectionName:
- if item[1]:
- return True
- return False
-
- colSave = [["", False]]
- for col in scene.bfu_collection_asset_list: # CollectionProperty
- name = col.name
- use = col.use
- colSave.append([name, use])
- scene.bfu_collection_asset_list.clear()
- for col in bpy.data.collections:
- scene.bfu_collection_asset_list.add().name = col.name
- useFromLast = SetUseFromLast(colSave, col.name)
- scene.bfu_collection_asset_list[col.name].use = useFromLast
- UpdateExportCollectionList(context.scene)
- return {'FINISHED'}
-
- class BFU_OT_ShowCollectionToExport(bpy.types.Operator):
- bl_label = "Show collection(s)"
- bl_idname = "object.showscenecollection"
- bl_description = "Click to show collections to export"
-
- def execute(self, context):
- scene = context.scene
- collection_asset_cache = bfu_cached_asset_list.GetCollectionAssetCache()
- collection_export_asset_list = collection_asset_cache.GetCollectionAssetList()
- popup_title = "Collection list"
- if len(collection_export_asset_list) > 0:
- popup_title = (
- str(len(collection_export_asset_list))+' collection(s) to export found.')
- else:
- popup_title = 'No collection to export found.'
-
- def draw(self, context):
- col = self.layout.column()
- for collection in collection_export_asset_list:
- row = col.row()
- row.label(text="- "+collection.name)
- bpy.context.window_manager.popup_menu(
- draw,
- title=popup_title,
- icon='GROUP')
- return {'FINISHED'}
-
- def draw(self, context):
-
- scene = bpy.context.scene
- obj = bpy.context.object
- addon_prefs = bfu_basics.GetAddonPrefs()
- layout = self.layout
-
- if bpy.app.version >= (4, 2, 0):
- version_str = 'Version '+ bbpl.blender_extension.extension_utils.get_package_version("blender_for_unrealengine")
- else:
- version_str = 'Version '+ bbpl.blender_addon.addon_utils.get_addon_version_str("Blender for UnrealEngine")
-
- credit_box = layout.box()
- credit_box.label(text=languages.ti('intro'))
- credit_box.label(text=version_str)
- credit_box.operator("object.bfu_open_documentation_page", icon="HELP")
-
- row = layout.row(align=True)
- row.menu(
- 'BFU_MT_ObjectGlobalPropertiesPresets',
- text='Global Properties Presets'
- )
- row.operator(
- 'object.add_globalproperties_preset',
- text='',
- icon='ADD'
- )
- row.operator(
- 'object.add_globalproperties_preset',
- text='',
- icon='REMOVE'
- ).remove_active = True
-
- layout.row().prop(scene, "bfu_active_tab", expand=True)
- if scene.bfu_active_tab == "OBJECT":
- layout.row().prop(scene, "bfu_active_object_tab", expand=True)
- if scene.bfu_active_tab == "SCENE":
- layout.row().prop(scene, "bfu_active_scene_tab", expand=True)
-
- bfu_material.bfu_material_ui.draw_ui_object_collision(layout)
- bfu_vertex_color.bfu_vertex_color_ui.draw_ui_object_collision(layout)
-
- if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
-
- scene.bfu_object_properties_expanded.draw(layout)
- if scene.bfu_object_properties_expanded.is_expend():
-
- if obj is None:
- layout.row().label(text='No selected object.')
- else:
-
- AssetType = layout.row()
- AssetType.prop(obj, 'name', text="", icon='OBJECT_DATA')
- # Show asset type
- asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
- if asset_class:
- asset_type_name = asset_class.get_asset_type_name(obj)
- else:
- asset_type_name = "Asset type not found."
-
- AssetType.label(text='('+asset_type_name+')')
-
- ExportType = layout.column()
- ExportType.prop(obj, 'bfu_export_type')
-
-
- if obj.bfu_export_type == "export_recursive":
-
- folderNameProperty = layout.column()
- folderNameProperty.prop(obj, 'bfu_export_folder_name', icon='FILE_FOLDER')
-
- ProxyProp = layout.column()
- if bfu_utils.GetExportAsProxy(obj):
- ProxyProp.label(text="The Armature was detected as a proxy.")
- proxy_child = bfu_utils.GetExportProxyChild(obj)
- if proxy_child:
- ProxyProp.label(text="Proxy child: " + proxy_child.name)
- else:
- ProxyProp.label(text="Proxy child not found")
-
- if not bfu_utils.GetExportAsProxy(obj):
- # exportCustomName
- exportCustomName = layout.row()
- exportCustomName.prop(obj, "bfu_use_custom_export_name")
- useCustomName = obj.bfu_use_custom_export_name
- exportCustomNameText = exportCustomName.column()
- exportCustomNameText.prop(obj, "bfu_custom_export_name")
- exportCustomNameText.enabled = useCustomName
- bfu_alembic_animation.bfu_alembic_animation_ui.draw_general_ui_object(layout, obj)
- bfu_groom.bfu_groom_ui.draw_general_ui_object(layout, obj)
- bfu_skeletal_mesh.bfu_skeletal_mesh_ui.draw_general_ui_object(layout, obj)
-
-
-
-
-
- bfu_camera.bfu_camera_ui_and_props.draw_ui_object_camera(layout, obj)
- bfu_spline.bfu_spline_ui_and_props.draw_ui_object_spline(layout, obj)
- bfu_skeletal_mesh.bfu_skeletal_mesh_ui.draw_ui_object(layout, obj)
- bfu_static_mesh.bfu_static_mesh_ui.draw_ui_object(layout, obj)
- bfu_lod.bfu_lod_ui.draw_ui_object(layout, obj)
- bfu_alembic_animation.bfu_alembic_animation_ui.draw_ui_object(layout, obj)
- bfu_groom.bfu_groom_ui.draw_ui_object(layout, obj)
-
- scene.bfu_object_advanced_properties_expanded.draw(layout)
- if scene.bfu_object_advanced_properties_expanded.is_expend():
- if obj is not None:
- if obj.bfu_export_type == "export_recursive":
-
- transformProp = layout.column()
- is_not_alembic_animation = not bfu_alembic_animation.bfu_alembic_animation_utils.is_alembic_animation(obj)
- is_not_camera = not bfu_camera.bfu_camera_utils.is_camera(obj)
- if is_not_alembic_animation and is_not_camera:
- transformProp.prop(obj, "bfu_move_to_center_for_export")
- transformProp.prop(obj, "bfu_rotate_to_zero_for_export")
- transformProp.prop(obj, "bfu_additional_location_for_export")
- transformProp.prop(obj, "bfu_additional_rotation_for_export")
-
- transformProp.prop(obj, 'bfu_export_global_scale')
- if bfu_camera.bfu_camera_utils.is_camera(obj):
- transformProp.prop(obj, "bfu_additional_location_for_export")
-
- AxisProperty = layout.column()
-
- AxisProperty.prop(obj, 'bfu_override_procedure_preset')
- if obj.bfu_override_procedure_preset:
- AxisProperty.prop(obj, 'bfu_export_use_space_transform')
- AxisProperty.prop(obj, 'bfu_export_axis_forward')
- AxisProperty.prop(obj, 'bfu_export_axis_up')
- bbpl.blender_layout.layout_doc_button.add_doc_page_operator(AxisProperty, text="About axis Transforms", url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Axis-Transforms")
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- BoneAxisProperty = layout.column()
- BoneAxisProperty.prop(obj, 'bfu_export_primary_bone_axis')
- BoneAxisProperty.prop(obj, 'bfu_export_secondary_bone_axis')
- else:
- box = layout.box()
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- preset = bfu_export_procedure.bfu_skeleton_export_procedure.get_obj_skeleton_procedure_preset(obj)
- else:
- preset = bfu_export_procedure.bfu_static_export_procedure.get_obj_static_procedure_preset(obj)
- var_lines = box.column()
- for key, value in preset.items():
- display_key = bps.utils.format_property_name(key)
- var_lines.label(text=f"{display_key}: {value}\n")
- export_data = layout.column()
- bfu_custom_property.bfu_custom_property_utils.draw_ui_custom_property(export_data, obj)
- export_data.prop(obj, "bfu_export_with_meta_data")
-
-
- else:
- layout.label(text='(No properties to show.)')
-
-
-
- scene.bfu_engine_ref_properties_expanded.draw(layout)
- if scene.bfu_engine_ref_properties_expanded.is_expend():
- if addon_prefs.useGeneratedScripts and obj is not None:
- if obj.bfu_export_type == "export_recursive":
-
- # SkeletalMesh prop
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- if not obj.bfu_export_as_lod_mesh:
- unreal_engine_refs = layout.column()
- bfu_unreal_engine_refs_props.draw_skeleton_prop(unreal_engine_refs, obj)
- bfu_unreal_engine_refs_props.draw_skeletal_mesh_prop(unreal_engine_refs, obj)
- else:
- layout.label(text='(No properties to show.)')
-
-
-
-
- if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "ANIM"):
- if obj is not None:
- if obj.bfu_export_type == "export_recursive" and not obj.bfu_export_as_lod_mesh:
-
- scene.bfu_animation_action_properties_expanded.draw(layout)
- if scene.bfu_animation_action_properties_expanded.is_expend():
- if (bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj) or
- bfu_camera.bfu_camera_utils.is_camera(obj) or
- bfu_alembic_animation.bfu_alembic_animation_utils.is_alembic_animation(obj)):
-
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- # Action list
- ActionListProperty = layout.column()
- ActionListProperty.prop(obj, 'bfu_anim_action_export_enum')
- if obj.bfu_anim_action_export_enum == "export_specific_list":
- ActionListProperty.template_list(
- # type and unique id
- "BFU_UL_ActionExportTarget", "",
- # pointer to the CollectionProperty
- obj, "bfu_animation_asset_list",
- # pointer to the active identifier
- obj, "bfu_active_animation_asset_list",
- maxrows=5,
- rows=5
- )
- ActionListProperty.operator(
- "object.updateobjactionlist",
- icon='RECOVER_LAST')
- if obj.bfu_anim_action_export_enum == "export_specific_prefix":
- ActionListProperty.prop(obj, 'bfu_prefix_name_to_export')
-
- # Action Time
- if obj.type != "CAMERA" and obj.bfu_skeleton_export_procedure != "auto-rig-pro":
- ActionTimeProperty = layout.column()
- ActionTimeProperty.enabled = obj.bfu_anim_action_export_enum != 'dont_export'
- ActionTimeProperty.prop(obj, 'bfu_anim_action_start_end_time_enum')
- if obj.bfu_anim_action_start_end_time_enum == "with_customframes":
- OfsetTime = ActionTimeProperty.row()
- OfsetTime.prop(obj, 'bfu_anim_action_custom_start_frame')
- OfsetTime.prop(obj, 'bfu_anim_action_custom_end_frame')
- if obj.bfu_anim_action_start_end_time_enum != "with_customframes":
- OfsetTime = ActionTimeProperty.row()
- OfsetTime.prop(obj, 'bfu_anim_action_start_frame_offset')
- OfsetTime.prop(obj, 'bfu_anim_action_end_frame_offset')
-
- else:
- layout.label(
- text=(
- "Note: animation start/end use scene frames" +
- " with the camera for the sequencer.")
- )
-
- # Nomenclature
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- export_anim_naming = layout.column()
- export_anim_naming.enabled = obj.bfu_anim_action_export_enum != 'dont_export'
- export_anim_naming.prop(obj, 'bfu_anim_naming_type')
- if obj.bfu_anim_naming_type == "include_custom_name":
- export_anim_naming_text = export_anim_naming.column()
- export_anim_naming_text.prop(obj, 'bfu_anim_naming_custom')
-
-
-
- else:
- layout.label(
- text='(This assets is not a SkeletalMesh or Camera)')
-
- scene.bfu_animation_action_advanced_properties_expanded.draw(layout)
- if scene.bfu_animation_action_advanced_properties_expanded.is_expend():
-
- if bfu_alembic_animation.bfu_alembic_animation_utils.is_not_alembic_animation(obj):
- transformProp = layout.column()
- transformProp.enabled = obj.bfu_anim_action_export_enum != 'dont_export'
- transformProp.prop(obj, "bfu_move_action_to_center_for_export")
- transformProp.prop(obj, "bfu_rotate_action_to_zero_for_export")
-
- scene.bfu_animation_nla_properties_expanded.draw(layout)
- if scene.bfu_animation_nla_properties_expanded.is_expend():
- # NLA
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- NLAAnim = layout.row()
- NLAAnim.prop(obj, 'bfu_anim_nla_use')
- NLAAnimChild = NLAAnim.column()
- NLAAnimChild.enabled = obj.bfu_anim_nla_use
- NLAAnimChild.prop(obj, 'bfu_anim_nla_export_name')
- if obj.bfu_skeleton_export_procedure == "auto-rig-pro":
- NLAAnim.enabled = False
- NLAAnimChild.enabled = False
-
- # NLA Time
- if obj.type != "CAMERA" and obj.bfu_skeleton_export_procedure != "auto-rig-pro":
- NLATimeProperty = layout.column()
- NLATimeProperty.enabled = obj.bfu_anim_nla_use
- NLATimeProperty.prop(obj, 'bfu_anim_nla_start_end_time_enum')
- if obj.bfu_anim_nla_start_end_time_enum == "with_customframes":
- OfsetTime = NLATimeProperty.row()
- OfsetTime.prop(obj, 'bfu_anim_nla_custom_start_frame')
- OfsetTime.prop(obj, 'bfu_anim_nla_custom_end_frame')
- if obj.bfu_anim_nla_start_end_time_enum != "with_customframes":
- OfsetTime = NLATimeProperty.row()
- OfsetTime.prop(obj, 'bfu_anim_nla_start_frame_offset')
- OfsetTime.prop(obj, 'bfu_anim_nla_end_frame_offset')
-
-
- scene.bfu_animation_nla_advanced_properties_expanded.draw(layout)
- if scene.bfu_animation_nla_advanced_properties_expanded.is_expend():
- if bfu_alembic_animation.bfu_alembic_animation_utils.is_not_alembic_animation(obj):
- transformProp2 = layout.column()
- transformProp2.enabled = obj.bfu_anim_nla_use
- transformProp2.prop(obj, "bfu_move_nla_to_center_for_export")
- transformProp2.prop(obj, "bfu_rotate_nla_to_zero_for_export")
-
-
- scene.bfu_animation_advanced_properties_expanded.draw(layout)
- if scene.bfu_animation_advanced_properties_expanded.is_expend():
- # Animation fbx properties
- if bfu_alembic_animation.bfu_alembic_animation_utils.is_not_alembic_animation(obj):
- propsFbx = layout.row()
- if obj.bfu_skeleton_export_procedure != "auto-rig-pro":
- propsFbx.prop(obj, 'bfu_sample_anim_for_export')
- propsFbx.prop(obj, 'bfu_simplify_anim_for_export')
- propsScaleAnimation = layout.row()
- propsScaleAnimation.prop(obj, "bfu_disable_free_scale_animation")
-
- # Armature export action list feedback
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- layout.label(
- text='Note: The Action with only one' +
- ' frame are exported like Pose.')
- ArmaturePropertyInfo = (
- layout.row().box().split(factor=0.75)
- )
- animation_asset_cache = bfu_cached_asset_list.GetAnimationAssetCache(obj)
- animation_to_export = animation_asset_cache.GetAnimationAssetList()
- ActionNum = len(animation_to_export)
- if obj.bfu_anim_nla_use:
- ActionNum += 1
- actionFeedback = (
- str(ActionNum) +
- " Animation(s) will be exported with this object.")
- ArmaturePropertyInfo.label(
- text=actionFeedback,
- icon='INFO')
- ArmaturePropertyInfo.operator("object.showobjaction")
- else:
- layout.label(text='(No properties to show.)')
- else:
- layout.label(text='(No properties to show.)')
-
- if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "MISC"):
-
- scene.bfu_object_lod_properties_expanded.draw(layout)
- if scene.bfu_object_lod_properties_expanded.is_expend():
- if addon_prefs.useGeneratedScripts and obj is not None:
- if obj.bfu_export_type == "export_recursive":
-
- # Lod selection
- if not obj.bfu_export_as_lod_mesh:
- # Unreal python no longer support Skeletal mesh LODS import.
- if (bfu_static_mesh.bfu_static_mesh_utils.is_static_mesh(obj)):
- LodList = layout.column()
- LodList.prop(obj, 'bfu_lod_target1')
- LodList.prop(obj, 'bfu_lod_target2')
- LodList.prop(obj, 'bfu_lod_target3')
- LodList.prop(obj, 'bfu_lod_target4')
- LodList.prop(obj, 'bfu_lod_target5')
-
- # StaticMesh prop
- if bfu_static_mesh.bfu_static_mesh_utils.is_static_mesh(obj):
- if not obj.bfu_export_as_lod_mesh:
- bfu_static_mesh_lod_group = layout.row()
- bfu_static_mesh_lod_group.prop(
- obj,
- 'bfu_use_static_mesh_lod_group',
- text="")
- SMLODGroupChild = bfu_static_mesh_lod_group.column()
- SMLODGroupChild.enabled = obj.bfu_use_static_mesh_lod_group
- SMLODGroupChild.prop(
- obj,
- 'bfu_static_mesh_lod_group'
- )
- scene.bfu_object_collision_properties_expanded.draw(layout)
- if scene.bfu_object_collision_properties_expanded.is_expend():
- if addon_prefs.useGeneratedScripts and obj is not None:
- if obj.bfu_export_type == "export_recursive":
-
- # StaticMesh prop
- if bfu_static_mesh.bfu_static_mesh_utils.is_static_mesh(obj):
- if not obj.bfu_export_as_lod_mesh:
- auto_generate_collision = layout.row()
- auto_generate_collision.prop(
- obj,
- 'bfu_auto_generate_collision'
- )
- collision_trace_flag = layout.row()
- collision_trace_flag.prop(
- obj,
- 'bfu_collision_trace_flag'
- )
- # SkeletalMesh prop
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- if not obj.bfu_export_as_lod_mesh:
- create_physics_asset = layout.row()
- create_physics_asset.prop(obj, "bfu_create_physics_asset")
- enable_skeletal_mesh_per_poly_collision = layout.row()
- enable_skeletal_mesh_per_poly_collision.prop(obj, 'bfu_enable_skeletal_mesh_per_poly_collision')
-
-
-
- scene.bfu_object_light_map_properties_expanded.draw(layout)
- if scene.bfu_object_light_map_properties_expanded.is_expend():
- if addon_prefs.useGeneratedScripts and obj is not None:
- if obj.bfu_export_type == "export_recursive":
-
- # Light map
- if bfu_static_mesh.bfu_static_mesh_utils.is_static_mesh(obj):
- StaticMeshLightMapRes = layout.box()
- StaticMeshLightMapRes.prop(obj, 'bfu_static_mesh_light_map_mode')
- if obj.bfu_static_mesh_light_map_mode == "CustomMap":
- CustomLightMap = StaticMeshLightMapRes.column()
- CustomLightMap.prop(obj, 'bfu_static_mesh_custom_light_map_res')
- if obj.bfu_static_mesh_light_map_mode == "SurfaceArea":
- SurfaceAreaLightMap = StaticMeshLightMapRes.column()
- SurfaceAreaLightMapButton = SurfaceAreaLightMap.row()
- SurfaceAreaLightMapButton.operator("object.computlightmap", icon='TEXTURE')
- SurfaceAreaLightMapButton.operator("object.computalllightmap", icon='TEXTURE')
- SurfaceAreaLightMap.prop(obj, 'bfu_use_static_mesh_light_map_world_scale')
- SurfaceAreaLightMap.prop(obj, 'bfu_static_mesh_light_map_surface_scale')
- SurfaceAreaLightMap.prop(obj, 'bfu_static_mesh_light_map_round_power_of_two')
- if obj.bfu_static_mesh_light_map_mode != "Default":
- CompuntedLightMap = str(bfu_light_map.bfu_light_map_utils.GetCompuntedLightMap(obj))
- StaticMeshLightMapRes.label(text='Compunted light map: ' + CompuntedLightMap)
- bfu_generate_light_map_uvs = layout.row()
- bfu_generate_light_map_uvs.prop(obj, 'bfu_generate_light_map_uvs')
-
-
- scene.bfu_object_uv_map_properties_expanded.draw(layout)
- if scene.bfu_object_uv_map_properties_expanded.is_expend():
- if obj.bfu_export_type == "export_recursive":
- # Geometry Node Uv
- bfu_convert_geometry_node_attribute_to_uv = layout.column()
- convert_geometry_node_attribute_to_uv_use = bfu_convert_geometry_node_attribute_to_uv.row()
- convert_geometry_node_attribute_to_uv_use.prop(obj, 'bfu_convert_geometry_node_attribute_to_uv')
- bbpl.blender_layout.layout_doc_button.add_doc_page_operator(convert_geometry_node_attribute_to_uv_use, url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/UV-Maps#geometry-node-uv")
- bfu_convert_geometry_node_attribute_to_uv_name = bfu_convert_geometry_node_attribute_to_uv.row()
- bfu_convert_geometry_node_attribute_to_uv_name.prop(obj, 'bfu_convert_geometry_node_attribute_to_uv_name')
- bfu_convert_geometry_node_attribute_to_uv_name.enabled = obj.bfu_convert_geometry_node_attribute_to_uv
-
- # Extreme UV Scale
- bfu_correct_extrem_uv_scale = layout.row()
- bfu_correct_extrem_uv_scale.prop(obj, 'bfu_correct_extrem_uv_scale')
- bbpl.blender_layout.layout_doc_button.add_doc_page_operator(bfu_correct_extrem_uv_scale, url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/UV-Maps#extreme-uv-scale")
-
- if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("SCENE", "GENERAL"):
-
- scene.bfu_collection_properties_expanded.draw(layout)
- if scene.bfu_collection_properties_expanded.is_expend():
- collectionListProperty = layout.column()
- collectionListProperty.template_list(
- # type and unique id
- "BFU_UL_CollectionExportTarget", "",
- # pointer to the CollectionProperty
- scene, "bfu_collection_asset_list",
- # pointer to the active identifier
- scene, "bfu_active_collection_asset_list",
- maxrows=5,
- rows=5
- )
- collectionListProperty.operator(
- "object.updatecollectionlist",
- icon='RECOVER_LAST')
-
- if scene.bfu_active_collection_asset_list < len(scene.bfu_collection_asset_list):
- col_name = scene.bfu_collection_asset_list[scene.bfu_active_collection_asset_list].name
- if col_name in bpy.data.collections:
- col = bpy.data.collections[col_name]
- col_prop = layout
- col_prop.prop(col, 'bfu_export_folder_name', icon='FILE_FOLDER')
- bfu_export_procedure.bfu_export_procedure_ui.draw_collection_export_procedure(layout, col)
-
- collectionPropertyInfo = layout.row().box().split(factor=0.75)
- collection_asset_cache = bfu_cached_asset_list.GetCollectionAssetCache()
- collection_export_asset_list = collection_asset_cache.GetCollectionAssetList()
- collectionNum = len(collection_export_asset_list)
- collectionFeedback = (
- str(collectionNum) +
- " Collection(s) will be exported.")
- collectionPropertyInfo.label(text=collectionFeedback, icon='INFO')
- collectionPropertyInfo.operator("object.showscenecollection")
- layout.label(text='Note: The collection are exported like StaticMesh.')
-
-
-
-class BFU_OT_SceneCollectionExport(bpy.types.PropertyGroup):
- name: bpy.props.StringProperty(name="collection data name", default="Unknown", override={'LIBRARY_OVERRIDABLE'})
- use: bpy.props.BoolProperty(name="export this collection", default=False, override={'LIBRARY_OVERRIDABLE'})
-
-class BFU_OT_ObjExportAction(bpy.types.PropertyGroup):
- name: bpy.props.StringProperty(name="Action data name", default="Unknown", override={'LIBRARY_OVERRIDABLE'})
- use: bpy.props.BoolProperty(name="use this action", default=False, override={'LIBRARY_OVERRIDABLE'})
-
-
-
-
-# -------------------------------------------------------------------
-# Register & Unregister
-# -------------------------------------------------------------------
-
-classes = (
- BFU_PT_BlenderForUnrealObject,
- BFU_PT_BlenderForUnrealObject.BFU_MT_ObjectGlobalPropertiesPresets,
- BFU_PT_BlenderForUnrealObject.BFU_OT_AddObjectGlobalPropertiesPreset,
- BFU_PT_BlenderForUnrealObject.BFU_OT_OpenDocumentationPage,
- BFU_PT_BlenderForUnrealObject.BFU_UL_ActionExportTarget,
- BFU_PT_BlenderForUnrealObject.BFU_OT_UpdateObjActionListButton,
- BFU_PT_BlenderForUnrealObject.BFU_OT_ShowActionToExport,
- BFU_PT_BlenderForUnrealObject.BFU_UL_CollectionExportTarget,
- BFU_PT_BlenderForUnrealObject.BFU_OT_UpdateCollectionButton,
- BFU_PT_BlenderForUnrealObject.BFU_OT_ShowCollectionToExport,
- BFU_OT_SceneCollectionExport,
- BFU_OT_ObjExportAction,
-)
-
-
-def register():
- for cls in classes:
- bpy.utils.register_class(cls)
-
- bpy.types.Scene.bfu_collection_asset_list = bpy.props.CollectionProperty(
- type=BFU_OT_SceneCollectionExport,
- options={'LIBRARY_EDITABLE'},
- override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'},
- )
-
- bpy.types.Scene.bfu_active_collection_asset_list = bpy.props.IntProperty(
- name="Active Collection",
- description="Index of the currently active collection",
- override={'LIBRARY_OVERRIDABLE'},
- default=0
- )
-
- bpy.types.Object.bfu_animation_asset_list = bpy.props.CollectionProperty(
- type=BFU_OT_ObjExportAction,
- options={'LIBRARY_EDITABLE'},
- override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'},
- )
-
- bpy.types.Object.bfu_active_animation_asset_list = bpy.props.IntProperty(
- name="Active Scene Action",
- description="Index of the currently active object action",
- override={'LIBRARY_OVERRIDABLE'},
- default=0
- )
-
- bpy.types.Object.bfu_export_with_meta_data = bpy.props.BoolProperty(
- name=(languages.ti('export_with_meta_data_name')),
- description=(languages.tt('export_with_meta_data_desc')),
- override={'LIBRARY_OVERRIDABLE'},
- default=False,
- )
-
-
-
-def unregister():
- for cls in reversed(classes):
- bpy.utils.unregister_class(cls)
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_panel_export.py b/blender-for-unrealengine/bfu_addon_parts/bfu_panel_export.py
new file mode 100644
index 00000000..505fb970
--- /dev/null
+++ b/blender-for-unrealengine/bfu_addon_parts/bfu_panel_export.py
@@ -0,0 +1,103 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import os
+import bpy
+from .. import bfu_export_nomenclature
+from .. import bfu_export_filter
+from .. import bfu_export_process
+
+class BFU_PT_Export(bpy.types.Panel):
+ # Is Export panel
+
+ bl_idname = "BFU_PT_Export"
+ bl_label = "UE AE Export"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "UI"
+ bl_category = "Unreal Engine"
+
+
+ class BFU_MT_NomenclaturePresets(bpy.types.Menu):
+ bl_label = 'Nomenclature Presets'
+ preset_subdir = 'blender-for-unrealengine/nomenclature-presets'
+ preset_operator = 'script.execute_preset'
+ draw = bpy.types.Menu.draw_preset
+
+ from bl_operators.presets import AddPresetBase
+
+ class BFU_OT_AddNomenclaturePreset(AddPresetBase, bpy.types.Operator):
+ bl_idname = 'object.add_nomenclature_preset'
+ bl_label = 'Add or remove a preset for Nomenclature'
+ bl_description = 'Add or remove a preset for Nomenclature'
+ preset_menu = 'BFU_MT_NomenclaturePresets'
+
+ def get_export_global_preset_propertys():
+ preset_values = []
+ preset_values += bfu_export_nomenclature.bfu_export_nomenclature_props.get_preset_values()
+ preset_values += bfu_export_filter.bfu_export_filter_props.get_preset_values()
+ preset_values += bfu_export_process.bfu_export_process_props.get_preset_values()
+ return preset_values
+
+ # Common variable used for all preset values
+ preset_defines = [
+ 'obj = bpy.context.object',
+ 'scene = bpy.context.scene'
+ ]
+
+ # Properties to store in the preset
+ preset_values = get_export_global_preset_propertys()
+
+ # Directory to store the presets
+ preset_subdir = 'blender-for-unrealengine/nomenclature-presets'
+
+
+ def draw(self, context: bpy.types.Context):
+
+ layout = self.layout
+
+ # Presets
+ row = layout.row(align=True)
+ row.menu('BFU_MT_NomenclaturePresets', text='Export Presets')
+ row.operator('object.add_nomenclature_preset', text='', icon='ADD')
+ row.operator('object.add_nomenclature_preset', text='', icon='REMOVE').remove_active = True
+
+ # Export sections
+ bfu_export_nomenclature.bfu_export_nomenclature_ui.draw_ui(layout, context)
+ bfu_export_filter.bfu_export_filter_ui.draw_ui(layout, context)
+ bfu_export_process.bfu_export_process_ui.draw_ui(layout, context)
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_PT_Export,
+ BFU_PT_Export.BFU_MT_NomenclaturePresets,
+ BFU_PT_Export.BFU_OT_AddNomenclaturePreset,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_panel_object.py b/blender-for-unrealengine/bfu_addon_parts/bfu_panel_object.py
new file mode 100644
index 00000000..cca99b16
--- /dev/null
+++ b/blender-for-unrealengine/bfu_addon_parts/bfu_panel_object.py
@@ -0,0 +1,196 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import os
+import bpy
+from .. import bbpl
+from .. import bfu_basics
+from .. import languages
+from .. import bfu_custom_property
+from .. import bfu_base_object
+from .. import bfu_adv_object
+from .. import bfu_base_collection
+from .. import bfu_material
+from .. import bfu_camera
+from .. import bfu_spline
+from .. import bfu_vertex_color
+from .. import bfu_static_mesh
+from .. import bfu_skeletal_mesh
+from .. import bfu_modular_skeletal_mesh
+from .. import bfu_lod
+from .. import bfu_alembic_animation
+from .. import bfu_anim_base
+from .. import bfu_anim_action
+from .. import bfu_anim_action_adv
+from .. import bfu_anim_nla
+from .. import bfu_anim_nla_adv
+from .. import bfu_groom
+from .. import bfu_uv_map
+from .. import bfu_light_map
+from .. import bfu_assets_references
+from .. import bfu_collision
+
+class BFU_PT_BlenderForUnrealObject(bpy.types.Panel):
+ # Unreal engine export panel
+
+ bl_idname = "BFU_PT_BlenderForUnrealObject"
+ bl_label = "Unreal Engine Assets Exporter"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "UI"
+ bl_category = "Unreal Engine"
+
+
+ class BFU_MT_ObjectGlobalPropertiesPresets(bpy.types.Menu):
+ bl_label = 'Global Properties Presets'
+ preset_subdir = 'blender-for-unrealengine/global-properties-presets'
+ preset_operator = 'script.execute_preset'
+ draw = bpy.types.Menu.draw_preset
+
+ from bl_operators.presets import AddPresetBase
+
+ class BFU_OT_AddObjectGlobalPropertiesPreset(AddPresetBase, bpy.types.Operator):
+ bl_idname = 'object.add_globalproperties_preset'
+ bl_label = 'Add or remove a preset for Global properties'
+ bl_description = 'Add or remove a preset for Global properties'
+ preset_menu = 'BFU_MT_ObjectGlobalPropertiesPresets'
+
+ def get_object_global_preset_propertys():
+ preset_values = []
+ preset_values += bfu_base_object.bfu_base_obj_props.get_preset_values()
+ preset_values += bfu_adv_object.bfu_adv_obj_props.get_preset_values()
+ preset_values += bfu_base_collection.bfu_base_col_props.get_preset_values()
+ preset_values += bfu_modular_skeletal_mesh.bfu_modular_skeletal_mesh_props.get_preset_values()
+ preset_values += bfu_custom_property.bfu_custom_property_props.get_preset_values()
+ preset_values += bfu_material.bfu_material_props.get_preset_values()
+ preset_values += bfu_camera.bfu_camera_ui_and_props.get_preset_values()
+ preset_values += bfu_spline.bfu_spline_ui_and_props.get_preset_values()
+ preset_values += bfu_static_mesh.bfu_static_mesh_props.get_preset_values()
+ preset_values += bfu_skeletal_mesh.bfu_skeletal_mesh_props.get_preset_values()
+ preset_values += bfu_alembic_animation.bfu_alembic_animation_props.get_preset_values()
+ preset_values += bfu_anim_base.bfu_anim_base_props.get_preset_values()
+ preset_values += bfu_anim_action.bfu_anim_action_props.get_preset_values()
+ preset_values += bfu_anim_action_adv.bfu_anim_action_adv_props.get_preset_values()
+ preset_values += bfu_anim_nla.bfu_anim_nla_props.get_preset_values()
+ preset_values += bfu_anim_nla_adv.bfu_anim_nla_adv_props.get_preset_values()
+ preset_values += bfu_vertex_color.bfu_vertex_color_props.get_preset_values()
+ preset_values += bfu_lod.bfu_lod_props.get_preset_values()
+ preset_values += bfu_uv_map.bfu_uv_map_props.get_preset_values()
+ preset_values += bfu_light_map.bfu_light_map_props.get_preset_values()
+ preset_values += bfu_assets_references.bfu_asset_ref_props.get_preset_values()
+ preset_values += bfu_collision.bfu_collision_props.get_preset_values()
+ return preset_values
+
+ # Common variable used for all preset values
+ preset_defines = [
+ 'obj = bpy.context.object',
+ 'col = bpy.context.collection',
+ 'scene = bpy.context.scene'
+ ]
+
+ # Properties to store in the preset
+ preset_values = get_object_global_preset_propertys()
+
+ # Directory to store the presets
+ preset_subdir = 'blender-for-unrealengine/global-properties-presets'
+
+
+ def draw(self, context: bpy.types.Context):
+
+ scene = bpy.context.scene
+ obj = bpy.context.object
+ addon_prefs = bfu_basics.GetAddonPrefs()
+ layout = self.layout
+
+ # Extension details
+ if bpy.app.version >= (4, 2, 0):
+ version_str = 'Version '+ str(bbpl.blender_extension.extension_utils.get_package_version())
+ else:
+ version_str = 'Version '+ bbpl.blender_addon.addon_utils.get_addon_version_str("Unreal Engine Assets Exporter")
+
+ credit_box = layout.box()
+ credit_box.label(text=languages.ti('intro'))
+ credit_box.label(text=version_str)
+ bbpl.blender_layout.layout_doc_button.functions.add_doc_page_operator(
+ layout = layout,
+ url = "https://github.com/xavier150/Blender-For-UnrealEngine-Addons",
+ text = "Open Github page",
+ icon="HELP"
+ )
+
+ # Presets
+ row = layout.row(align=True)
+ row.menu('BFU_MT_ObjectGlobalPropertiesPresets', text='Global Properties Presets')
+ row.operator('object.add_globalproperties_preset', text='', icon='ADD')
+ row.operator('object.add_globalproperties_preset', text='', icon='REMOVE').remove_active = True
+
+ # Tab Buttions
+ layout.row().prop(scene, "bfu_active_tab", expand=True)
+ if scene.bfu_active_tab == "OBJECT":
+ layout.row().prop(scene, "bfu_active_object_tab", expand=True)
+ if scene.bfu_active_tab == "SCENE":
+ layout.row().prop(scene, "bfu_active_scene_tab", expand=True)
+
+ # Object
+ bfu_base_object.bfu_base_obj_ui.draw_ui(layout, obj)
+ bfu_adv_object.bfu_adv_obj_ui.draw_ui(layout, obj)
+ bfu_static_mesh.bfu_static_mesh_ui.draw_ui_object(layout, obj)
+ bfu_skeletal_mesh.bfu_skeletal_mesh_ui.draw_ui_object(layout, obj)
+ bfu_modular_skeletal_mesh.bfu_modular_skeletal_mesh_ui.draw_ui_object(layout, obj)
+ bfu_alembic_animation.bfu_alembic_animation_ui.draw_ui_object(layout, obj)
+ bfu_groom.bfu_groom_ui.draw_ui_object(layout, obj)
+ bfu_camera.bfu_camera_ui_and_props.draw_ui_object_camera(layout, obj)
+ bfu_spline.bfu_spline_ui_and_props.draw_ui_object_spline(layout, obj)
+ bfu_lod.bfu_lod_ui.draw_ui(layout, obj)
+ bfu_collision.bfu_collision_ui.draw_ui_object(layout, obj)
+ bfu_uv_map.bfu_uv_map_ui.draw_obj_ui(layout, obj)
+ bfu_light_map.bfu_light_map_ui.draw_obj_ui(layout, obj)
+ bfu_material.bfu_material_ui.draw_ui_object(layout)
+ bfu_vertex_color.bfu_vertex_color_ui.draw_ui_object(layout)
+ bfu_assets_references.bfu_asset_ref_ui.draw_ui(layout, obj)
+
+ # Animations
+ bfu_anim_action.bfu_anim_action_ui.draw_ui(layout, obj)
+ bfu_anim_action_adv.bfu_anim_action_adv_ui.draw_ui(layout, obj)
+ bfu_anim_nla.bfu_anim_nla_ui.draw_ui(layout, obj)
+ bfu_anim_nla_adv.bfu_anim_nla_adv_ui.draw_ui(layout, obj)
+ bfu_anim_base.bfu_anim_base_ui.draw_ui(layout, obj)
+ bfu_anim_base.bfu_anim_base_ui.draw_animation_tab_foot_ui(layout, obj)
+
+ # Scene
+ bfu_base_collection.bfu_base_col_ui.draw_ui(layout, context)
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_PT_BlenderForUnrealObject,
+ BFU_PT_BlenderForUnrealObject.BFU_MT_ObjectGlobalPropertiesPresets,
+ BFU_PT_BlenderForUnrealObject.BFU_OT_AddObjectGlobalPropertiesPreset,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_panel_tools.py b/blender-for-unrealengine/bfu_addon_parts/bfu_panel_tools.py
new file mode 100644
index 00000000..74a5638d
--- /dev/null
+++ b/blender-for-unrealengine/bfu_addon_parts/bfu_panel_tools.py
@@ -0,0 +1,70 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_camera
+from .. import bfu_spline
+from .. import bfu_collision
+from .. import bfu_socket
+from .. import bfu_uv_map
+from .. import bfu_light_map
+
+class BFU_PT_BlenderForUnrealTool(bpy.types.Panel):
+ # Tool panel
+
+ bl_idname = "BFU_PT_BlenderForUnrealTool"
+ bl_label = "UE AE Tools"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "UI"
+ bl_category = "Unreal Engine"
+
+ def draw(self, context: bpy.types.Context):
+
+ layout = self.layout
+
+ # Tools sections
+ bfu_camera.bfu_camera_ui_and_props.draw_tools_ui(layout, context)
+ bfu_spline.bfu_spline_ui_and_props.draw_tools_ui(layout, context)
+
+ bfu_collision.bfu_collision_ui.draw_tools_ui(layout, context)
+ bfu_socket.bfu_socket_ui_and_props.draw_tools_ui(layout, context)
+
+ bfu_uv_map.bfu_uv_map_ui.draw_tools_ui(layout, context)
+ bfu_light_map.bfu_light_map_ui.draw_tools_ui(layout, context)
+
+
+''
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_PT_BlenderForUnrealTool,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_tool_ui_and_props.py b/blender-for-unrealengine/bfu_addon_parts/bfu_tool_ui_and_props.py
deleted file mode 100644
index c8b4f314..00000000
--- a/blender-for-unrealengine/bfu_addon_parts/bfu_tool_ui_and_props.py
+++ /dev/null
@@ -1,85 +0,0 @@
-import bpy
-from .. import bfu_basics
-from .. import bfu_utils
-from .. import bfu_ui
-from .. import bfu_camera
-from .. import bfu_spline
-from .. import bfu_collision
-from .. import bfu_socket
-from .. import bbpl
-
-
-class BFU_PT_BlenderForUnrealTool(bpy.types.Panel):
- # Tool panel
-
- bl_idname = "BFU_PT_BlenderForUnrealTool"
- bl_label = "BFU Tool"
- bl_space_type = "VIEW_3D"
- bl_region_type = "UI"
- bl_category = "Unreal Engine"
-
-
-
-
-
-
-
-
- def draw(self, context):
-
-
- layout = self.layout
- scene = bpy.context.scene
-
-
-
-
-
- bfu_camera.bfu_camera_ui_and_props.draw_ui_scene_camera(layout)
- bfu_spline.bfu_spline_ui_and_props.draw_ui_scene_spline(layout)
-
- bfu_collision.bfu_collision_ui_and_props.draw_ui_scene_collision(layout)
- bfu_socket.bfu_socket_ui_and_props.draw_ui_scene_socket(layout)
-
- scene.bfu_uvmap_expanded.draw(layout)
- if scene.bfu_uvmap_expanded.is_expend():
- ready_for_correct_extrem_uv_scale = False
- obj = bpy.context.object
- if obj and obj.type == "MESH":
- if bbpl.utils.active_mode_is("EDIT"):
- ready_for_correct_extrem_uv_scale = True
- else:
- layout.label(text="Switch to Edit Mode.", icon='INFO')
- else:
- layout.label(text="Select an mesh object", icon='INFO')
-
-
- # Draw buttons (correct_extrem_uv)
- Buttons_correct_extrem_uv_scale = layout.row()
- Button_correct_extrem_uv_scale = Buttons_correct_extrem_uv_scale.column()
- Button_correct_extrem_uv_scale.enabled = ready_for_correct_extrem_uv_scale
- Button_correct_extrem_uv_scale.operator("object.correct_extrem_uv", icon='UV')
- bbpl.blender_layout.layout_doc_button.add_doc_page_operator(Buttons_correct_extrem_uv_scale, url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/UV-Maps#extreme-uv-scale")
-
- scene.bfu_lightmap_expanded.draw(layout)
- if scene.bfu_lightmap_expanded.is_expend():
- checkButton = layout.column()
- checkButton.operator("object.computalllightmap", icon='TEXTURE')
-
-# -------------------------------------------------------------------
-# Register & Unregister
-# -------------------------------------------------------------------
-
-classes = (
- BFU_PT_BlenderForUnrealTool,
-)
-
-
-def register():
- for cls in classes:
- bpy.utils.register_class(cls)
-
-
-def unregister():
- for cls in reversed(classes):
- bpy.utils.unregister_class(cls)
diff --git a/blender-for-unrealengine/bfu_addon_pref.py b/blender-for-unrealengine/bfu_addon_pref.py
index 3537bb5e..ed9c23c4 100644
--- a/blender-for-unrealengine/bfu_addon_pref.py
+++ b/blender-for-unrealengine/bfu_addon_pref.py
@@ -184,7 +184,7 @@ def execute(self, context):
)
return {'FINISHED'}
- def draw(self, context):
+ def draw(self, context: bpy.types.Context):
layout: bpy.types.UILayout = self.layout
boxColumn = layout.column().split(
diff --git a/blender-for-unrealengine/bfu_adv_object/__init__.py b/blender-for-unrealengine/bfu_adv_object/__init__.py
new file mode 100644
index 00000000..4781bf01
--- /dev/null
+++ b/blender-for-unrealengine/bfu_adv_object/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_adv_obj_props
+from . import bfu_adv_obj_ui
+from . import bfu_adv_obj_utils
+
+if "bfu_adv_obj_props" in locals():
+ importlib.reload(bfu_adv_obj_props)
+if "bfu_adv_obj_ui" in locals():
+ importlib.reload(bfu_adv_obj_ui)
+if "bfu_adv_obj_utils" in locals():
+ importlib.reload(bfu_adv_obj_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_adv_obj_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_adv_obj_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_props.py b/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_props.py
new file mode 100644
index 00000000..ff9a3a28
--- /dev/null
+++ b/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_props.py
@@ -0,0 +1,210 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import languages
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_move_to_center_for_export',
+ 'obj.bfu_rotate_to_zero_for_export',
+ 'obj.bfu_additional_location_for_export',
+ 'obj.bfu_additional_rotation_for_export',
+ 'obj.bfu_export_global_scale',
+ 'obj.bfu_override_procedure_preset',
+ 'obj.bfu_export_use_space_transform',
+ 'obj.bfu_export_axis_forward',
+ 'obj.bfu_export_axis_up',
+ 'obj.bfu_export_primary_bone_axis',
+ 'obj.bfu_export_secondary_bone_axis',
+ 'obj.bfu_export_with_meta_data',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_object_advanced_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Object Advanced Properties")
+
+ bpy.types.Object.bfu_move_to_center_for_export = bpy.props.BoolProperty(
+ name="Move to center",
+ description=(
+ "If true use object origin else use scene origin." +
+ " | If true the mesh will be moved to the center" +
+ " of the scene for export." +
+ " (This is used so that the origin of the fbx file" +
+ " is the same as the mesh in blender)"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True
+ )
+
+ bpy.types.Object.bfu_rotate_to_zero_for_export = bpy.props.BoolProperty(
+ name="Rotate to zero",
+ description=(
+ "If true use object rotation else use scene rotation." +
+ " | If true the mesh will use zero rotation for export."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+ bpy.types.Object.bfu_additional_location_for_export = bpy.props.FloatVectorProperty(
+ name="Additional location",
+ description=(
+ "This will add a additional absolute location to the mesh"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ subtype="TRANSLATION",
+ default=(0, 0, 0)
+ )
+
+ bpy.types.Object.bfu_additional_rotation_for_export = bpy.props.FloatVectorProperty(
+ name="Additional rotation",
+ description=(
+ "This will add a additional absolute rotation to the mesh"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ subtype="EULER",
+ default=(0, 0, 0)
+ )
+
+ bpy.types.Object.bfu_export_global_scale = bpy.props.FloatProperty(
+ name="Global scale",
+ description="Scale, change is not recommended with SkeletalMesh.",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=1.0
+ )
+
+ bpy.types.Object.bfu_override_procedure_preset = bpy.props.BoolProperty(
+ name="Override Export Preset",
+ description="If true override the export precedure preset.",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False,
+ )
+
+ bpy.types.Object.bfu_export_use_space_transform = bpy.props.BoolProperty(
+ name="Use Space Transform",
+ default=True,
+ )
+
+ bpy.types.Object.bfu_export_axis_forward = bpy.props.EnumProperty(
+ name="Axis Forward",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ('X', "X Forward", ""),
+ ('Y', "Y Forward", ""),
+ ('Z', "Z Forward", ""),
+ ('-X', "-X Forward", ""),
+ ('-Y', "-Y Forward", ""),
+ ('-Z', "-Z Forward", ""),
+ ],
+ default='-Z',
+ )
+
+ bpy.types.Object.bfu_export_axis_up = bpy.props.EnumProperty(
+ name="Axis Up",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ('X', "X Up", ""),
+ ('Y', "Y Up", ""),
+ ('Z', "Z Up", ""),
+ ('-X', "-X Up", ""),
+ ('-Y', "-Y Up", ""),
+ ('-Z', "-Z Up", ""),
+ ],
+ default='Y',
+ )
+
+ bpy.types.Object.bfu_export_primary_bone_axis = bpy.props.EnumProperty(
+ name="Primary Axis Bone",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ('X', "X", ""),
+ ('Y', "Y", ""),
+ ('Z', "Z", ""),
+ ('-X', "-X", ""),
+ ('-Y', "-Y", ""),
+ ('-Z', "-Z", ""),
+ ],
+ default='Y',
+ )
+
+ bpy.types.Object.bfu_export_secondary_bone_axis = bpy.props.EnumProperty(
+ name="Secondary Axis Bone",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ('X', "X", ""),
+ ('Y', "Y", ""),
+ ('Z', "Z", ""),
+ ('-X', "-X", ""),
+ ('-Y', "-Y", ""),
+ ('-Z', "-Z", ""),
+ ],
+ default='X',
+ )
+
+ bpy.types.Object.bfu_export_with_meta_data = bpy.props.BoolProperty(
+ name=(languages.ti('export_with_meta_data_name')),
+ description=(languages.tt('export_with_meta_data_desc')),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False,
+ )
+
+
+
+
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_export_with_meta_data
+
+ del bpy.types.Object.bfu_export_secondary_bone_axis
+ del bpy.types.Object.bfu_export_primary_bone_axis
+ del bpy.types.Object.bfu_export_axis_up
+ del bpy.types.Object.bfu_export_axis_forward
+ del bpy.types.Object.bfu_export_use_space_transform
+ del bpy.types.Object.bfu_override_procedure_preset
+
+ del bpy.types.Object.bfu_export_global_scale
+ del bpy.types.Object.bfu_additional_rotation_for_export
+ del bpy.types.Object.bfu_additional_location_for_export
+ del bpy.types.Object.bfu_rotate_to_zero_for_export
+ del bpy.types.Object.bfu_move_to_center_for_export
+
+ del bpy.types.Scene.bfu_object_advanced_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_ui.py b/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_ui.py
new file mode 100644
index 00000000..a4e9ad04
--- /dev/null
+++ b/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_ui.py
@@ -0,0 +1,89 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bpl
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_alembic_animation
+from .. import bfu_camera
+from .. import bfu_skeletal_mesh
+from .. import bfu_export_procedure
+from .. import bfu_custom_property
+
+
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ if obj is None:
+ return
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
+ scene.bfu_object_advanced_properties_expanded.draw(layout)
+ if scene.bfu_object_advanced_properties_expanded.is_expend():
+ transformProp = layout.column()
+ is_not_alembic_animation = not bfu_alembic_animation.bfu_alembic_animation_utils.is_alembic_animation(obj)
+ is_not_camera = not bfu_camera.bfu_camera_utils.is_camera(obj)
+ if is_not_alembic_animation and is_not_camera:
+ transformProp.prop(obj, "bfu_move_to_center_for_export")
+ transformProp.prop(obj, "bfu_rotate_to_zero_for_export")
+ transformProp.prop(obj, "bfu_additional_location_for_export")
+ transformProp.prop(obj, "bfu_additional_rotation_for_export")
+
+ transformProp.prop(obj, 'bfu_export_global_scale')
+ if bfu_camera.bfu_camera_utils.is_camera(obj):
+ transformProp.prop(obj, "bfu_additional_location_for_export")
+
+ AxisProperty = layout.column()
+
+ AxisProperty.prop(obj, 'bfu_override_procedure_preset')
+ if obj.bfu_override_procedure_preset:
+ AxisProperty.prop(obj, 'bfu_export_use_space_transform')
+ AxisProperty.prop(obj, 'bfu_export_axis_forward')
+ AxisProperty.prop(obj, 'bfu_export_axis_up')
+ bbpl.blender_layout.layout_doc_button.add_doc_page_operator(AxisProperty, text="About axis Transforms", url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Axis-Transforms")
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ BoneAxisProperty = layout.column()
+ BoneAxisProperty.prop(obj, 'bfu_export_primary_bone_axis')
+ BoneAxisProperty.prop(obj, 'bfu_export_secondary_bone_axis')
+ else:
+ box = layout.box()
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ preset = bfu_export_procedure.bfu_skeleton_export_procedure.get_obj_skeleton_procedure_preset(obj)
+ else:
+ preset = bfu_export_procedure.bfu_static_export_procedure.get_obj_static_procedure_preset(obj)
+ var_lines = box.column()
+ for key, value in preset.items():
+ display_key = bpl.utils.format_property_name(key)
+ var_lines.label(text=f"{display_key}: {value}\n")
+ export_data = layout.column()
+ bfu_custom_property.bfu_custom_property_utils.draw_ui_custom_property(export_data, obj)
+ export_data.prop(obj, "bfu_export_with_meta_data")
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_utils.py b/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_adv_object/bfu_adv_obj_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_alembic_animation/bfu_alembic_animation_type.py b/blender-for-unrealengine/bfu_alembic_animation/bfu_alembic_animation_type.py
index 175a35bf..72aa9800 100644
--- a/blender-for-unrealengine/bfu_alembic_animation/bfu_alembic_animation_type.py
+++ b/blender-for-unrealengine/bfu_alembic_animation/bfu_alembic_animation_type.py
@@ -30,7 +30,7 @@ def __init__(self):
super().__init__()
self.use_materials = True
- def support_asset_type(self, obj):
+ def support_asset_type(self, obj, details = None):
if obj.bfu_export_as_alembic_animation:
return True
return False
@@ -48,18 +48,24 @@ def get_obj_file_name(self, obj, desired_name="", fileType=".fbx"):
return bfu_basics.ValidFilename(scene.bfu_alembic_animation_prefix_export_name+desired_name+fileType)
return bfu_basics.ValidFilename(scene.bfu_alembic_animation_prefix_export_name+obj.name+fileType)
- def get_obj_export_directory_path(self, obj):
+ def get_obj_export_directory_path(self, obj, absolute = True):
folder_name = bfu_utils.get_export_folder_name(obj)
scene = bpy.context.scene
+ if(absolute):
+ root_path = bpy.path.abspath(scene.bfu_export_alembic_file_path)
+ else:
+ root_path = scene.bfu_export_alembic_file_path
+
+
if obj.bfu_create_sub_folder_with_alembic_name:
- dirpath = os.path.join(scene.bfu_export_alembic_file_path, folder_name, self.get_asset_type_name(obj))
+ dirpath = os.path.join(root_path, folder_name, self.get_asset_type_name(obj))
else:
- dirpath = os.path.join(scene.bfu_export_alembic_file_path, folder_name)
+ dirpath = os.path.join(root_path, folder_name)
return dirpath
def can_export_asset(self):
scene = bpy.context.scene
- return scene.alembic_export
+ return scene.bfu_use_alembic_export
def can_export_obj_asset(self, obj):
return self.can_export_asset()
diff --git a/blender-for-unrealengine/bfu_anim_action/__init__.py b/blender-for-unrealengine/bfu_anim_action/__init__.py
new file mode 100644
index 00000000..02866fae
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_action/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_anim_action_props
+from . import bfu_anim_action_ui
+from . import bfu_anim_action_utils
+
+if "bfu_anim_action_props" in locals():
+ importlib.reload(bfu_anim_action_props)
+if "bfu_anim_action_ui" in locals():
+ importlib.reload(bfu_anim_action_ui)
+if "bfu_anim_action_utils" in locals():
+ importlib.reload(bfu_anim_action_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_anim_action_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_anim_action_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_props.py b/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_props.py
new file mode 100644
index 00000000..82c04bf6
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_props.py
@@ -0,0 +1,277 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_anim_action_export_enum',
+ 'obj.bfu_prefix_name_to_export',
+ 'obj.bfu_anim_action_start_end_time_enum',
+ 'obj.bfu_anim_action_start_frame_offset',
+ 'obj.bfu_anim_action_end_frame_offset',
+ 'obj.bfu_anim_action_custom_start_frame',
+ 'obj.bfu_anim_action_custom_end_frame',
+ 'obj.bfu_anim_naming_type',
+ 'obj.bfu_anim_naming_custom',
+ ]
+ return preset_values
+
+class BFU_UL_ActionExportTarget(bpy.types.UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_property, index):
+ action_is_valid = False
+ if item.name in bpy.data.actions:
+ action_is_valid = True
+
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ if action_is_valid: # If action is valid
+ layout.prop(
+ bpy.data.actions[item.name],
+ "name",
+ text="",
+ emboss=False,
+ icon="ACTION"
+ )
+ layout.prop(item, "use", text="")
+ else:
+ dataText = (
+ 'Action data named "' + item.name +
+ '" Not Found. Please click on update'
+ )
+ layout.label(text=dataText, icon="ERROR")
+ # Not optimized for 'GRID' layout type.
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+class BFU_OT_UpdateObjActionListButton(bpy.types.Operator):
+ bl_label = "Update action list"
+ bl_idname = "object.updateobjactionlist"
+ bl_description = "Update action list"
+
+ def execute(self, context):
+ def UpdateExportActionList(obj):
+ # Update the provisional action list known by the object
+
+ def SetUseFromLast(anim_list, ActionName):
+ for item in anim_list:
+ if item[0] == ActionName:
+ if item[1]:
+ return True
+ return False
+
+ animSave = [["", False]]
+ for Anim in obj.bfu_action_asset_list: # CollectionProperty
+ name = Anim.name
+ use = Anim.use
+ animSave.append([name, use])
+ obj.bfu_action_asset_list.clear()
+ for action in bpy.data.actions:
+ obj.bfu_action_asset_list.add().name = action.name
+ useFromLast = SetUseFromLast(animSave, action.name)
+ obj.bfu_action_asset_list[action.name].use = useFromLast
+ UpdateExportActionList(bpy.context.object)
+ return {'FINISHED'}
+
+class BFU_OT_ObjExportAction(bpy.types.PropertyGroup):
+ name: bpy.props.StringProperty(name="Action data name", default="Unknown", override={'LIBRARY_OVERRIDABLE'})
+ use: bpy.props.BoolProperty(name="use this action", default=False, override={'LIBRARY_OVERRIDABLE'})
+
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_UL_ActionExportTarget,
+ BFU_OT_UpdateObjActionListButton,
+ BFU_OT_ObjExportAction,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_animation_action_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Actions Properties")
+
+ bpy.types.Object.bfu_action_asset_list = bpy.props.CollectionProperty(
+ type=BFU_OT_ObjExportAction,
+ options={'LIBRARY_EDITABLE'},
+ override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'},
+ )
+
+ bpy.types.Object.bfu_active_action_asset_list = bpy.props.IntProperty(
+ name="Active Scene Action",
+ description="Index of the currently active object action",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=0
+ )
+
+ bpy.types.Object.bfu_anim_action_export_enum = bpy.props.EnumProperty(
+ name="Action to export",
+ description="Export procedure for actions (Animations and poses)",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ("export_auto",
+ "Export auto",
+ "Export all actions connected to the bones names",
+ "FILE_SCRIPT",
+ 1),
+ ("export_specific_list",
+ "Export specific list",
+ "Export only actions that are checked in the list",
+ "LINENUMBERS_ON",
+ 3),
+ ("export_specific_prefix",
+ "Export specific prefix",
+ "Export only actions with a specific prefix" +
+ " or the beginning of the actions names",
+ "SYNTAX_ON",
+ 4),
+ ("dont_export",
+ "Not exported",
+ "No action will be exported",
+ "MATPLANE",
+ 5),
+ ("export_current",
+ "Export Current",
+ "Export only the current actions",
+ "FILE_SCRIPT",
+ 6),
+ ]
+ )
+
+ bpy.types.Object.bfu_prefix_name_to_export = bpy.props.StringProperty(
+ # properties used with ""export_specific_prefix" on bfu_anim_action_export_enum
+ name="Prefix name",
+ description="Indicate the prefix of the actions that must be exported",
+ override={'LIBRARY_OVERRIDABLE'},
+ maxlen=32,
+ default="Example_",
+ )
+
+ bpy.types.Object.bfu_anim_action_start_end_time_enum = bpy.props.EnumProperty(
+ name="Action Start/End Time",
+ description="Set when animation starts and end",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ("with_keyframes",
+ "Auto",
+ "The time will be defined according" +
+ " to the first and the last frame",
+ "KEYTYPE_KEYFRAME_VEC",
+ 1),
+ ("with_sceneframes",
+ "Scene time",
+ "Time will be equal to the scene time",
+ "SCENE_DATA",
+ 2),
+ ("with_customframes",
+ "Custom time",
+ 'The time of all the animations of this object' +
+ ' is defined by you.' +
+ ' Use "bfu_anim_action_custom_start_frame" and "bfu_anim_action_custom_end_frame"',
+ "HAND",
+ 3),
+ ]
+ )
+
+ bpy.types.Object.bfu_anim_action_start_frame_offset = bpy.props.IntProperty(
+ name="Offset at start frame",
+ description="Offset for the start frame.",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=0
+ )
+
+ bpy.types.Object.bfu_anim_action_end_frame_offset = bpy.props.IntProperty(
+ name="Offset at end frame",
+ description=(
+ "Offset for the end frame. +1" +
+ " is recommended for the sequences | 0 is recommended" +
+ " for UnrealEngine cycles | -1 is recommended for Sketchfab cycles"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=0
+ )
+
+
+ bpy.types.Object.bfu_anim_action_custom_start_frame = bpy.props.IntProperty(
+ name="Custom start time",
+ description="Set when animation start",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=0
+ )
+
+ bpy.types.Object.bfu_anim_action_custom_end_frame = bpy.props.IntProperty(
+ name="Custom end time",
+ description="Set when animation end",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=1
+ )
+
+
+ bpy.types.Object.bfu_anim_naming_type = bpy.props.EnumProperty(
+ name="Naming type",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ('action_name', "Action name", 'Exemple: "Anim_MyAction"'),
+ ('include_armature_name',
+ "Include Armature Name",
+ 'Include armature name in animation export file name.' +
+ ' Exemple: "Anim_MyArmature_MyAction"'),
+ ('include_custom_name',
+ "Include custom name",
+ 'Include custom name in animation export file name.' +
+ ' Exemple: "Anim_MyCustomName_MyAction"'),
+ ],
+ default='action_name'
+ )
+
+ bpy.types.Object.bfu_anim_naming_custom = bpy.props.StringProperty(
+ name="Export name",
+ override={'LIBRARY_OVERRIDABLE'},
+ default='MyCustomName'
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_anim_naming_custom
+ del bpy.types.Object.bfu_anim_naming_type
+
+ del bpy.types.Object.bfu_anim_action_custom_end_frame
+ del bpy.types.Object.bfu_anim_action_custom_start_frame
+ del bpy.types.Object.bfu_anim_action_end_frame_offset
+ del bpy.types.Object.bfu_anim_action_start_frame_offset
+ del bpy.types.Object.bfu_anim_action_start_end_time_enum
+
+ del bpy.types.Object.bfu_prefix_name_to_export
+ del bpy.types.Object.bfu_anim_action_export_enum
+ del bpy.types.Object.bfu_active_action_asset_list
+ del bpy.types.Object.bfu_action_asset_list
+ del bpy.types.Scene.bfu_animation_action_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_ui.py b/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_ui.py
new file mode 100644
index 00000000..621f63a3
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_ui.py
@@ -0,0 +1,101 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_skeletal_mesh
+from .. import bfu_alembic_animation
+from .. import bfu_camera
+
+
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+ is_skeletal_mesh = bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj)
+ is_camera = bfu_camera.bfu_camera_utils.is_camera(obj)
+ is_alembic_animation = bfu_alembic_animation.bfu_alembic_animation_utils.is_alembic_animation(obj)
+
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+ if True not in [is_skeletal_mesh, is_camera, is_alembic_animation]:
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "ANIM"):
+ scene.bfu_animation_action_properties_expanded.draw(layout)
+ if scene.bfu_animation_action_properties_expanded.is_expend():
+ if is_skeletal_mesh:
+ # Action list
+ ActionListProperty = layout.column()
+ ActionListProperty.prop(obj, 'bfu_anim_action_export_enum')
+ if obj.bfu_anim_action_export_enum == "export_specific_list":
+ ActionListProperty.template_list(
+ # type and unique id
+ "BFU_UL_ActionExportTarget", "",
+ # pointer to the CollectionProperty
+ obj, "bfu_action_asset_list",
+ # pointer to the active identifier
+ obj, "bfu_active_action_asset_list",
+ maxrows=5,
+ rows=5
+ )
+ ActionListProperty.operator(
+ "object.updateobjactionlist",
+ icon='RECOVER_LAST')
+ if obj.bfu_anim_action_export_enum == "export_specific_prefix":
+ ActionListProperty.prop(obj, 'bfu_prefix_name_to_export')
+
+ # Action Time
+ if obj.type != "CAMERA" and obj.bfu_skeleton_export_procedure != "auto-rig-pro":
+ ActionTimeProperty = layout.column()
+ ActionTimeProperty.enabled = obj.bfu_anim_action_export_enum != 'dont_export'
+ ActionTimeProperty.prop(obj, 'bfu_anim_action_start_end_time_enum')
+ if obj.bfu_anim_action_start_end_time_enum == "with_customframes":
+ OfsetTime = ActionTimeProperty.row()
+ OfsetTime.prop(obj, 'bfu_anim_action_custom_start_frame')
+ OfsetTime.prop(obj, 'bfu_anim_action_custom_end_frame')
+ if obj.bfu_anim_action_start_end_time_enum != "with_customframes":
+ OfsetTime = ActionTimeProperty.row()
+ OfsetTime.prop(obj, 'bfu_anim_action_start_frame_offset')
+ OfsetTime.prop(obj, 'bfu_anim_action_end_frame_offset')
+
+ else:
+ layout.label(
+ text=(
+ "Note: animation start/end use scene frames" +
+ " with the camera for the sequencer.")
+ )
+
+ # Nomenclature
+ if is_skeletal_mesh:
+ export_anim_naming = layout.column()
+ export_anim_naming.enabled = obj.bfu_anim_action_export_enum != 'dont_export'
+ export_anim_naming.prop(obj, 'bfu_anim_naming_type')
+ if obj.bfu_anim_naming_type == "include_custom_name":
+ export_anim_naming_text = export_anim_naming.column()
+ export_anim_naming_text.prop(obj, 'bfu_anim_naming_custom')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_utils.py b/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_action/bfu_anim_action_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_anim_action_adv/__init__.py b/blender-for-unrealengine/bfu_anim_action_adv/__init__.py
new file mode 100644
index 00000000..5ad38cca
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_action_adv/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_anim_action_adv_props
+from . import bfu_anim_action_adv_ui
+from . import bfu_anim_action_adv_utils
+
+if "bfu_anim_action_adv_props" in locals():
+ importlib.reload(bfu_anim_action_adv_props)
+if "bfu_anim_action_adv_ui" in locals():
+ importlib.reload(bfu_anim_action_adv_ui)
+if "bfu_anim_action_adv_utils" in locals():
+ importlib.reload(bfu_anim_action_adv_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_anim_action_adv_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_anim_action_adv_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_props.py b/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_props.py
new file mode 100644
index 00000000..9c4fa152
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_props.py
@@ -0,0 +1,81 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_move_action_to_center_for_export',
+ 'obj.bfu_rotate_action_to_zero_for_export',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_animation_action_advanced_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Actions Advanced Properties")
+
+ bpy.types.Object.bfu_move_action_to_center_for_export = bpy.props.BoolProperty(
+ name="Move animation to center",
+ description=(
+ "(Action animation only) If true use object origin else use scene origin." +
+ " | If true the mesh will be moved to the center" +
+ " of the scene for export." +
+ " (This is used so that the origin of the fbx file" +
+ " is the same as the mesh in blender)" +
+ " Note: Unreal Engine ignore the position of the skeleton at the import."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True
+ )
+
+ bpy.types.Object.bfu_rotate_action_to_zero_for_export = bpy.props.BoolProperty(
+ name="Rotate Action to zero",
+ description=(
+ "(Action animation only) If true use object rotation else use scene rotation." +
+ " | If true the mesh will use zero rotation for export."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_rotate_action_to_zero_for_export
+ del bpy.types.Object.bfu_move_action_to_center_for_export
+
+ del bpy.types.Scene.bfu_animation_action_advanced_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_ui.py b/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_ui.py
new file mode 100644
index 00000000..e3e716d6
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_ui.py
@@ -0,0 +1,48 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_alembic_animation
+
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+ if bfu_alembic_animation.bfu_alembic_animation_utils.is_alembic_animation(obj):
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "ANIM"):
+ scene.bfu_animation_action_advanced_properties_expanded.draw(layout)
+ if scene.bfu_animation_action_advanced_properties_expanded.is_expend():
+ transformProp = layout.column()
+ transformProp.enabled = obj.bfu_anim_action_export_enum != 'dont_export'
+ transformProp.prop(obj, "bfu_move_action_to_center_for_export")
+ transformProp.prop(obj, "bfu_rotate_action_to_zero_for_export")
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_utils.py b/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_action_adv/bfu_anim_action_adv_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_anim_base/__init__.py b/blender-for-unrealengine/bfu_anim_base/__init__.py
new file mode 100644
index 00000000..8631601c
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_base/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_anim_base_props
+from . import bfu_anim_base_ui
+from . import bfu_anim_base_utils
+
+if "bfu_anim_base_props" in locals():
+ importlib.reload(bfu_anim_base_props)
+if "bfu_anim_base_ui" in locals():
+ importlib.reload(bfu_anim_base_ui)
+if "bfu_anim_base_utils" in locals():
+ importlib.reload(bfu_anim_base_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_anim_base_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_anim_base_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_props.py b/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_props.py
new file mode 100644
index 00000000..9762e21b
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_props.py
@@ -0,0 +1,151 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_cached_asset_list
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_disable_free_scale_animation',
+ 'obj.bfu_sample_anim_for_export',
+ 'obj.bfu_simplify_anim_for_export',
+ ]
+ return preset_values
+
+class BFU_OT_ShowActionToExport(bpy.types.Operator):
+ bl_label = "Show action(s)"
+ bl_idname = "object.showobjaction"
+ bl_description = (
+ "Click to show actions that are" +
+ " to be exported with this armature."
+ )
+
+ def execute(self, context):
+ obj = context.object
+ animation_asset_cache = bfu_cached_asset_list.GetAnimationAssetCache(obj)
+ animation_asset_cache.UpdateActionCache()
+ animation_to_export = animation_asset_cache.GetAnimationAssetList()
+
+ popup_title = "Action list"
+ if len(animation_to_export) > 0:
+ animationNumber = len(animation_to_export)
+ if obj.bfu_anim_nla_use:
+ animationNumber += 1
+ popup_title = (
+ str(animationNumber) +
+ ' action(s) found for obj named "'+obj.name+'".'
+ )
+ else:
+ popup_title = (
+ 'No action found for obj named "' +
+ obj.name+'".')
+
+ def draw(self, context: bpy.types.Context):
+ col = self.layout.column()
+
+ def addAnimRow(
+ action_name,
+ action_type,
+ frame_start,
+ frame_end):
+ row = col.row()
+ row.label(
+ text="- ["+action_name +
+ "] Frame "+frame_start+" to "+frame_end +
+ " ("+action_type+")"
+ )
+
+ for action in animation_to_export:
+ Frames = bfu_utils.GetDesiredActionStartEndTime(obj, action)
+ frame_start = str(Frames[0])
+ frame_end = str(Frames[1])
+ addAnimRow(action.name, bfu_utils.GetActionType(action), frame_start, frame_end)
+ if obj.bfu_anim_nla_use:
+ scene = context.scene
+ addAnimRow(obj.bfu_anim_nla_export_name, "NlAnim", str(scene.frame_start), str(scene.frame_end))
+
+ bpy.context.window_manager.popup_menu(
+ draw,
+ title=popup_title,
+ icon='ACTION'
+ )
+ return {'FINISHED'}
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_OT_ShowActionToExport,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_animation_advanced_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Animation Advanced Properties")
+
+ bpy.types.Object.bfu_sample_anim_for_export = bpy.props.FloatProperty(
+ name="Sampling Rate",
+ description="How often to evaluate animated values (in frames)",
+ override={'LIBRARY_OVERRIDABLE'},
+ min=0.01, max=100.0,
+ soft_min=0.01, soft_max=100.0,
+ default=1.0,
+ )
+
+ bpy.types.Object.bfu_simplify_anim_for_export = bpy.props.FloatProperty(
+ name="Simplify animations",
+ description=(
+ "How much to simplify baked values" +
+ " (0.0 to disable, the higher the more simplified)"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ # No simplification to up to 10% of current magnitude tolerance.
+ min=0.0, max=100.0,
+ soft_min=0.0, soft_max=10.0,
+ default=0.0,
+ )
+
+ bpy.types.Object.bfu_disable_free_scale_animation = bpy.props.BoolProperty(
+ name="Disable non-uniform scale animation.",
+ description=(
+ "If checked, scale animation track's elements always have same value. " +
+ "This applies basic bones only."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_disable_free_scale_animation
+ del bpy.types.Object.bfu_simplify_anim_for_export
+ del bpy.types.Object.bfu_sample_anim_for_export
+ del bpy.types.Scene.bfu_animation_advanced_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_ui.py b/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_ui.py
new file mode 100644
index 00000000..2eaac84d
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_ui.py
@@ -0,0 +1,89 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_skeletal_mesh
+from .. import bfu_alembic_animation
+from .. import bfu_cached_asset_list
+
+
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "ANIM"):
+ scene.bfu_animation_advanced_properties_expanded.draw(layout)
+ if scene.bfu_animation_advanced_properties_expanded.is_expend():
+ # Animation fbx properties
+ if bfu_alembic_animation.bfu_alembic_animation_utils.is_not_alembic_animation(obj):
+ propsFbx = layout.row()
+ if obj.bfu_skeleton_export_procedure != "auto-rig-pro":
+ propsFbx.prop(obj, 'bfu_sample_anim_for_export')
+ propsFbx.prop(obj, 'bfu_simplify_anim_for_export')
+ propsScaleAnimation = layout.row()
+ propsScaleAnimation.prop(obj, "bfu_disable_free_scale_animation")
+
+def draw_animation_tab_foot_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_not_skeletal_mesh(obj):
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "ANIM"):
+ # Armature export action list feedback
+ layout.label(
+ text='Note: The Action with only one' +
+ ' frame are exported like Pose.')
+ ArmaturePropertyInfo = (
+ layout.row().box().split(factor=0.75)
+ )
+ animation_asset_cache = bfu_cached_asset_list.GetAnimationAssetCache(obj)
+ animation_to_export = animation_asset_cache.GetAnimationAssetList()
+ ActionNum = len(animation_to_export)
+ if obj.bfu_anim_nla_use:
+ ActionNum += 1
+ actionFeedback = (
+ str(ActionNum) +
+ " Animation(s) will be exported with this object.")
+ ArmaturePropertyInfo.label(
+ text=actionFeedback,
+ icon='INFO')
+ ArmaturePropertyInfo.operator("object.showobjaction")
diff --git a/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_utils.py b/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_base/bfu_anim_base_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_anim_nla/__init__.py b/blender-for-unrealengine/bfu_anim_nla/__init__.py
new file mode 100644
index 00000000..54d04a70
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_nla/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_anim_nla_props
+from . import bfu_anim_nla_ui
+from . import bfu_anim_nla_utils
+
+if "bfu_anim_nla_props" in locals():
+ importlib.reload(bfu_anim_nla_props)
+if "bfu_anim_nla_ui" in locals():
+ importlib.reload(bfu_anim_nla_ui)
+if "bfu_anim_nla_utils" in locals():
+ importlib.reload(bfu_anim_nla_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_anim_nla_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_anim_nla_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_props.py b/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_props.py
new file mode 100644
index 00000000..45cd120d
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_props.py
@@ -0,0 +1,137 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_anim_nla_use',
+ 'obj.bfu_anim_nla_export_name',
+ 'obj.bfu_anim_nla_start_end_time_enum',
+ 'obj.bfu_anim_nla_start_frame_offset',
+ 'obj.bfu_anim_nla_end_frame_offset',
+ 'obj.bfu_anim_nla_custom_start_frame',
+ 'obj.bfu_anim_nla_custom_end_frame',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_animation_nla_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="NLA Properties")
+
+ bpy.types.Object.bfu_anim_nla_use = bpy.props.BoolProperty(
+ name="Export NLA (Nonlinear Animation)",
+ description=(
+ "If checked, exports the all animation of the scene with the NLA " +
+ "(Don't work with Auto-Rig Pro for the moment.)"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+ bpy.types.Object.bfu_anim_nla_export_name = bpy.props.StringProperty(
+ name="NLA export name",
+ description="Export NLA name (Don't work with Auto-Rig Pro for the moment.)",
+ override={'LIBRARY_OVERRIDABLE'},
+ maxlen=64,
+ default="NLA_animation",
+ subtype='FILE_NAME'
+ )
+
+ bpy.types.Object.bfu_anim_nla_start_end_time_enum = bpy.props.EnumProperty(
+ name="NLA Start/End Time",
+ description="Set when animation starts and end",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ("with_sceneframes",
+ "Scene time",
+ "Time will be equal to the scene time",
+ "SCENE_DATA",
+ 1),
+ ("with_customframes",
+ "Custom time",
+ 'The time of all the animations of this object' +
+ ' is defined by you.' +
+ ' Use "bfu_anim_action_custom_start_frame" and "bfu_anim_action_custom_end_frame"',
+ "HAND",
+ 2),
+ ]
+ )
+
+ bpy.types.Object.bfu_anim_nla_start_frame_offset = bpy.props.IntProperty(
+ name="Offset at start frame",
+ description="Offset for the start frame.",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=0
+ )
+
+ bpy.types.Object.bfu_anim_nla_end_frame_offset = bpy.props.IntProperty(
+ name="Offset at end frame",
+ description=(
+ "Offset for the end frame. +1" +
+ " is recommended for the sequences | 0 is recommended" +
+ " for UnrealEngine cycles | -1 is recommended for Sketchfab cycles"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=0
+ )
+
+ bpy.types.Object.bfu_anim_nla_custom_start_frame = bpy.props.IntProperty(
+ name="Custom start time",
+ description="Set when animation start",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=0
+ )
+
+ bpy.types.Object.bfu_anim_nla_custom_end_frame = bpy.props.IntProperty(
+ name="Custom end time",
+ description="Set when animation end",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=1
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_anim_nla_custom_end_frame
+ del bpy.types.Object.bfu_anim_nla_custom_start_frame
+ del bpy.types.Object.bfu_anim_nla_end_frame_offset
+ del bpy.types.Object.bfu_anim_nla_start_frame_offset
+ del bpy.types.Object.bfu_anim_nla_export_name
+ del bpy.types.Object.bfu_anim_nla_use
+
+ del bpy.types.Scene.bfu_animation_nla_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_ui.py b/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_ui.py
new file mode 100644
index 00000000..cb861e34
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_ui.py
@@ -0,0 +1,69 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_skeletal_mesh
+
+
+
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+ is_skeletal_mesh = bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj)
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "ANIM"):
+ scene.bfu_animation_nla_properties_expanded.draw(layout)
+ if scene.bfu_animation_nla_properties_expanded.is_expend():
+ # NLA
+ if is_skeletal_mesh:
+ NLAAnim = layout.row()
+ NLAAnim.prop(obj, 'bfu_anim_nla_use')
+ NLAAnimChild = NLAAnim.column()
+ NLAAnimChild.enabled = obj.bfu_anim_nla_use
+ NLAAnimChild.prop(obj, 'bfu_anim_nla_export_name')
+ if obj.bfu_skeleton_export_procedure == "auto-rig-pro":
+ NLAAnim.enabled = False
+ NLAAnimChild.enabled = False
+
+ # NLA Time
+ if obj.type != "CAMERA" and obj.bfu_skeleton_export_procedure != "auto-rig-pro":
+ NLATimeProperty = layout.column()
+ NLATimeProperty.enabled = obj.bfu_anim_nla_use
+ NLATimeProperty.prop(obj, 'bfu_anim_nla_start_end_time_enum')
+ if obj.bfu_anim_nla_start_end_time_enum == "with_customframes":
+ OfsetTime = NLATimeProperty.row()
+ OfsetTime.prop(obj, 'bfu_anim_nla_custom_start_frame')
+ OfsetTime.prop(obj, 'bfu_anim_nla_custom_end_frame')
+ if obj.bfu_anim_nla_start_end_time_enum != "with_customframes":
+ OfsetTime = NLATimeProperty.row()
+ OfsetTime.prop(obj, 'bfu_anim_nla_start_frame_offset')
+ OfsetTime.prop(obj, 'bfu_anim_nla_end_frame_offset')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_utils.py b/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_nla/bfu_anim_nla_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_anim_nla_adv/__init__.py b/blender-for-unrealengine/bfu_anim_nla_adv/__init__.py
new file mode 100644
index 00000000..83baefc6
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_nla_adv/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_anim_nla_adv_props
+from . import bfu_anim_nla_adv_ui
+from . import bfu_anim_nla_adv_utils
+
+if "bfu_anim_nla_adv_props" in locals():
+ importlib.reload(bfu_anim_nla_adv_props)
+if "bfu_anim_nla_adv_ui" in locals():
+ importlib.reload(bfu_anim_nla_adv_ui)
+if "bfu_anim_nla_adv_utils" in locals():
+ importlib.reload(bfu_anim_nla_adv_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_anim_nla_adv_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_anim_nla_adv_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_props.py b/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_props.py
new file mode 100644
index 00000000..f6ad145c
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_props.py
@@ -0,0 +1,81 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_move_nla_to_center_for_export',
+ 'obj.bfu_rotate_nla_to_zero_for_export',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_animation_nla_advanced_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="NLA Advanced Properties")
+
+ bpy.types.Object.bfu_move_nla_to_center_for_export = bpy.props.BoolProperty(
+ name="Move NLA to center",
+ description=(
+ "(Non linear animation only) If true use object origin else use scene origin." +
+ " | If true the mesh will be moved to the center" +
+ " of the scene for export." +
+ " (This is used so that the origin of the fbx file" +
+ " is the same as the mesh in blender)" +
+ " Note: Unreal Engine ignore the position of the skeleton at the import."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True
+ )
+
+ bpy.types.Object.bfu_rotate_nla_to_zero_for_export = bpy.props.BoolProperty(
+ name="Rotate NLA to zero",
+ description=(
+ "(Non linear animation only) If true use object rotation else use scene rotation." +
+ " | If true the mesh will use zero rotation for export."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_rotate_nla_to_zero_for_export
+ del bpy.types.Object.bfu_move_nla_to_center_for_export
+
+ del bpy.types.Scene.bfu_animation_nla_advanced_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_ui.py b/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_ui.py
new file mode 100644
index 00000000..3b7dba6b
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_ui.py
@@ -0,0 +1,50 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_alembic_animation
+
+
+
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+ if bfu_alembic_animation.bfu_alembic_animation_utils.is_alembic_animation(obj):
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "ANIM"):
+ scene.bfu_animation_nla_advanced_properties_expanded.draw(layout)
+ if scene.bfu_animation_nla_advanced_properties_expanded.is_expend():
+ transformProp2 = layout.column()
+ transformProp2.enabled = obj.bfu_anim_nla_use
+ transformProp2.prop(obj, "bfu_move_nla_to_center_for_export")
+ transformProp2.prop(obj, "bfu_rotate_nla_to_zero_for_export")
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_utils.py b/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_anim_nla_adv/bfu_anim_nla_adv_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_assets_manager/bfu_asset_manager_type.py b/blender-for-unrealengine/bfu_assets_manager/bfu_asset_manager_type.py
index 4b95952d..49df4d63 100644
--- a/blender-for-unrealengine/bfu_assets_manager/bfu_asset_manager_type.py
+++ b/blender-for-unrealengine/bfu_assets_manager/bfu_asset_manager_type.py
@@ -27,7 +27,7 @@ def __init__(self):
self.use_materials = False
self.use_sockets = False
- def support_asset_type(self, obj):
+ def support_asset_type(self, obj, details = None):
return False
def get_asset_type_name(self, obj):
@@ -39,12 +39,8 @@ def get_obj_export_name(self, obj):
def get_obj_file_name(self, obj, desired_name="", fileType=".fbx"):
return ""
- def get_obj_export_directory_path(self, obj):
+ def get_obj_export_directory_path(self, obj, absolute = True):
return ""
-
- def get_obj_export_abs_directory_path(self, obj):
- dirpath = self.get_obj_export_directory_path(obj)
- return bpy.path.abspath(dirpath)
def can_export_asset(self):
return False
diff --git a/blender-for-unrealengine/bfu_assets_manager/bfu_asset_manager_utils.py b/blender-for-unrealengine/bfu_assets_manager/bfu_asset_manager_utils.py
index 04911731..492794c2 100644
--- a/blender-for-unrealengine/bfu_assets_manager/bfu_asset_manager_utils.py
+++ b/blender-for-unrealengine/bfu_assets_manager/bfu_asset_manager_utils.py
@@ -22,10 +22,10 @@
-def get_asset_class(obj) -> bfu_asset_manager_type.BFU_BaseAssetClass:
+def get_asset_class(obj, details = None) -> bfu_asset_manager_type.BFU_BaseAssetClass:
for asset in bfu_asset_manager_registred_assets.get_registred_asset_class():
asset: bfu_asset_manager_type.BFU_BaseAssetClass
- if asset.support_asset_type(obj):
+ if asset.support_asset_type(obj, details):
return asset
def get_all_asset_class():
diff --git a/blender-for-unrealengine/bfu_assets_references/__init__.py b/blender-for-unrealengine/bfu_assets_references/__init__.py
new file mode 100644
index 00000000..bbb33671
--- /dev/null
+++ b/blender-for-unrealengine/bfu_assets_references/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_asset_ref_props
+from . import bfu_asset_ref_ui
+from . import bfu_asset_ref_utils
+
+if "bfu_asset_ref_props" in locals():
+ importlib.reload(bfu_asset_ref_props)
+if "bfu_asset_ref_ui" in locals():
+ importlib.reload(bfu_asset_ref_ui)
+if "bfu_asset_ref_utils" in locals():
+ importlib.reload(bfu_asset_ref_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_asset_ref_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_asset_ref_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_unreal_engine_refs_props.py b/blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_props.py
similarity index 80%
rename from blender-for-unrealengine/bfu_addon_parts/bfu_unreal_engine_refs_props.py
rename to blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_props.py
index e5d7078d..38143165 100644
--- a/blender-for-unrealengine/bfu_addon_parts/bfu_unreal_engine_refs_props.py
+++ b/blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_props.py
@@ -16,35 +16,14 @@
#
# ======================= END GPL LICENSE BLOCK =============================
+
import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
-classes = (
-)
-def draw_skeleton_prop(layout: bpy.types.UILayout, obj: bpy.types.Object):
- layout.prop(obj, "bfu_engine_ref_skeleton_search_mode")
- if obj.bfu_engine_ref_skeleton_search_mode == "auto":
- pass
- if obj.bfu_engine_ref_skeleton_search_mode == "custom_name":
- layout.prop(obj, "bfu_engine_ref_skeleton_custom_name")
- if obj.bfu_engine_ref_skeleton_search_mode == "custom_path_name":
- layout.prop(obj, "bfu_engine_ref_skeleton_custom_path")
- layout.prop(obj, "bfu_engine_ref_skeleton_custom_name")
- if obj.bfu_engine_ref_skeleton_search_mode == "custom_reference":
- layout.prop(obj, "bfu_engine_ref_skeleton_custom_ref")
-
-
-def draw_skeletal_mesh_prop(layout: bpy.types.UILayout, obj: bpy.types.Object):
- layout.prop(obj, "bfu_engine_ref_skeletal_mesh_search_mode")
- if obj.bfu_engine_ref_skeletal_mesh_search_mode == "auto":
- pass
- if obj.bfu_engine_ref_skeletal_mesh_search_mode == "custom_name":
- layout.prop(obj, "bfu_engine_ref_skeletal_mesh_custom_name")
- if obj.bfu_engine_ref_skeletal_mesh_search_mode == "custom_path_name":
- layout.prop(obj, "bfu_engine_ref_skeletal_mesh_custom_path")
- layout.prop(obj, "bfu_engine_ref_skeletal_mesh_custom_name")
- if obj.bfu_engine_ref_skeletal_mesh_search_mode == "custom_reference":
- layout.prop(obj, "bfu_engine_ref_skeletal_mesh_custom_ref")
def get_preset_values():
@@ -61,11 +40,21 @@ def get_preset_values():
]
return preset_values
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
def register():
for cls in classes:
bpy.utils.register_class(cls)
+ bpy.types.Scene.bfu_engine_ref_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Engine Refs")
+
+
bpy.types.Object.bfu_engine_ref_skeleton_search_mode = bpy.props.EnumProperty(
name="Skeleton Ref",
description='Specify the skeleton location in Unreal',
@@ -163,8 +152,6 @@ def register():
default="SkeletalMesh'/Game/ImportedFbx/SKM_MySkeletalMesh.SKM_MySkeletalMesh'"
)
-
-
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
@@ -177,4 +164,6 @@ def unregister():
del bpy.types.Object.bfu_engine_ref_skeletal_mesh_custom_ref
del bpy.types.Object.bfu_engine_ref_skeletal_mesh_custom_name
del bpy.types.Object.bfu_engine_ref_skeletal_mesh_custom_path
- del bpy.types.Object.bfu_engine_ref_skeletal_mesh_search_mode
\ No newline at end of file
+ del bpy.types.Object.bfu_engine_ref_skeletal_mesh_search_mode
+
+ del bpy.types.Scene.bfu_engine_ref_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_ui.py b/blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_ui.py
new file mode 100644
index 00000000..3de1e89c
--- /dev/null
+++ b/blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_ui.py
@@ -0,0 +1,83 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bbpl
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bfu_skeletal_mesh
+
+
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ if obj is None:
+ return
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if addon_prefs.useGeneratedScripts is False:
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_not_skeletal_mesh(obj):
+ return
+
+ # Draw UI
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
+ scene.bfu_engine_ref_properties_expanded.draw(layout)
+ if scene.bfu_engine_ref_properties_expanded.is_expend():
+
+ # SkeletalMesh prop
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ if not obj.bfu_export_as_lod_mesh:
+ unreal_engine_refs = layout.column()
+ draw_skeleton_prop(unreal_engine_refs, obj)
+ draw_skeletal_mesh_prop(unreal_engine_refs, obj)
+
+
+def draw_skeleton_prop(layout: bpy.types.UILayout, obj: bpy.types.Object):
+ layout.prop(obj, "bfu_engine_ref_skeleton_search_mode")
+ if obj.bfu_engine_ref_skeleton_search_mode == "auto":
+ pass
+ if obj.bfu_engine_ref_skeleton_search_mode == "custom_name":
+ layout.prop(obj, "bfu_engine_ref_skeleton_custom_name")
+ if obj.bfu_engine_ref_skeleton_search_mode == "custom_path_name":
+ layout.prop(obj, "bfu_engine_ref_skeleton_custom_path")
+ layout.prop(obj, "bfu_engine_ref_skeleton_custom_name")
+ if obj.bfu_engine_ref_skeleton_search_mode == "custom_reference":
+ layout.prop(obj, "bfu_engine_ref_skeleton_custom_ref")
+
+def draw_skeletal_mesh_prop(layout: bpy.types.UILayout, obj: bpy.types.Object):
+ layout.prop(obj, "bfu_engine_ref_skeletal_mesh_search_mode")
+ if obj.bfu_engine_ref_skeletal_mesh_search_mode == "auto":
+ pass
+ if obj.bfu_engine_ref_skeletal_mesh_search_mode == "custom_name":
+ layout.prop(obj, "bfu_engine_ref_skeletal_mesh_custom_name")
+ if obj.bfu_engine_ref_skeletal_mesh_search_mode == "custom_path_name":
+ layout.prop(obj, "bfu_engine_ref_skeletal_mesh_custom_path")
+ layout.prop(obj, "bfu_engine_ref_skeletal_mesh_custom_name")
+ if obj.bfu_engine_ref_skeletal_mesh_search_mode == "custom_reference":
+ layout.prop(obj, "bfu_engine_ref_skeletal_mesh_custom_ref")
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_utils.py b/blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_utils.py
new file mode 100644
index 00000000..a6e76eb3
--- /dev/null
+++ b/blender-for-unrealengine/bfu_assets_references/bfu_asset_ref_utils.py
@@ -0,0 +1,22 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import fnmatch
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_backward_compatibility.py b/blender-for-unrealengine/bfu_backward_compatibility.py
index 3602a5be..f90a4ae4 100644
--- a/blender-for-unrealengine/bfu_backward_compatibility.py
+++ b/blender-for-unrealengine/bfu_backward_compatibility.py
@@ -39,8 +39,6 @@ def update_old_variables():
print("Updating old bfu variables...")
for obj in bpy.data.objects:
-
-
update_variable(obj, ["bfu_skeleton_search_mode"], "bfu_engine_ref_skeleton_search_mode", enum_callback)
update_variable(obj, ["bfu_target_skeleton_custom_path"], "bfu_engine_ref_skeleton_custom_path")
update_variable(obj, ["bfu_target_skeleton_custom_name"], "bfu_engine_ref_skeleton_custom_name")
@@ -71,7 +69,6 @@ def update_old_variables():
update_variable(obj, ["GenerateLightmapUVs"], "bfu_generate_light_map_uvs")
update_variable(obj, ["convert_geometry_node_attribute_to_uv"], "bfu_convert_geometry_node_attribute_to_uv")
update_variable(obj, ["convert_geometry_node_attribute_to_uv_name"], "bfu_convert_geometry_node_attribute_to_uv_name")
- update_variable(obj, ["correct_extrem_uv_scale"], "bfu_correct_extrem_uv_scale")
update_variable(obj, ["AutoGenerateCollision"], "bfu_auto_generate_collision")
update_variable(obj, ["MaterialSearchLocation"], "bfu_material_search_location", enum_callback)
update_variable(obj, ["CollisionTraceFlag"], "bfu_collision_trace_flag", enum_callback)
@@ -99,13 +96,13 @@ def update_old_variables():
update_variable(obj, ["AdditionalLocationForExport"], "bfu_additional_location_for_export")
update_variable(obj, ["AdditionalRotationForExport"], "bfu_additional_rotation_for_export")
- update_variable(obj, ["exportActionList"], "bfu_animation_asset_list")
- update_variable(obj, ["active_ObjectAction"], "bfu_active_animation_asset_list")
+ update_variable(obj, ["exportActionList, bfu_animation_asset_list"], "bfu_action_asset_list")
+ update_variable(obj, ["active_ObjectAction, bfu_active_animation_asset_list"], "bfu_active_action_asset_list")
update_variable(obj, ["ExportAsAlembic, bfu_export_as_alembic"], "bfu_export_as_alembic_animation")
-
-
+ update_variable(obj, ["correct_extrem_uv_scale", "bfu_correct_extrem_uv_scale"], "bfu_use_correct_extrem_uv_scale")
+ update_variable(obj, ["bfu_invert_normal_maps"], "bfu_flip_normal_map_green_channel")
for col in bpy.data.collections:
update_variable(col, ["exportFolderName"], "bfu_export_folder_name")
@@ -133,6 +130,20 @@ def update_old_variables():
update_variable(scene, ["CollectionExportList"], "bfu_collection_asset_list")
update_variable(scene, ["active_CollectionExportList"], "bfu_active_collection_asset_list")
+ update_variable(scene, ["static_export"], "bfu_use_static_export")
+ update_variable(scene, ["static_collection_export"], "bfu_use_static_collection_export")
+ update_variable(scene, ["skeletal_export"], "bfu_use_skeletal_export")
+ update_variable(scene, ["anin_export"], "bfu_use_anin_export")
+ update_variable(scene, ["alembic_export"], "bfu_use_alembic_export")
+ update_variable(scene, ["groom_simulation_export"], "bfu_use_groom_simulation_export")
+ update_variable(scene, ["camera_export"], "bfu_use_camera_export")
+ update_variable(scene, ["spline_export"], "bfu_use_spline_export")
+
+ update_variable(scene, ["text_ExportLog"], "bfu_use_text_export_log")
+ update_variable(scene, ["text_ImportAssetScript"], "bfu_use_text_import_asset_script")
+ update_variable(scene, ["text_ImportSequenceScript"], "bfu_use_text_import_sequence_script")
+ update_variable(scene, ["text_AdditionalData"], "bfu_use_text_additional_data")
+
def enum_callback(data, old_var_name, new_var_name):
value = data[old_var_name] # Get value ast int
diff --git a/blender-for-unrealengine/bfu_base_collection/__init__.py b/blender-for-unrealengine/bfu_base_collection/__init__.py
new file mode 100644
index 00000000..ed58fcc4
--- /dev/null
+++ b/blender-for-unrealengine/bfu_base_collection/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_base_col_props
+from . import bfu_base_col_ui
+from . import bfu_base_col_utils
+
+if "bfu_base_col_props" in locals():
+ importlib.reload(bfu_base_col_props)
+if "bfu_base_col_ui" in locals():
+ importlib.reload(bfu_base_col_ui)
+if "bfu_base_col_utils" in locals():
+ importlib.reload(bfu_base_col_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_base_col_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_base_col_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_base_collection/bfu_base_col_props.py b/blender-for-unrealengine/bfu_base_collection/bfu_base_col_props.py
new file mode 100644
index 00000000..110bdb6c
--- /dev/null
+++ b/blender-for-unrealengine/bfu_base_collection/bfu_base_col_props.py
@@ -0,0 +1,176 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ ]
+ return preset_values
+
+class BFU_UL_CollectionExportTarget(bpy.types.UIList):
+
+ def draw_item(self, context, layout, data, item, icon, active_data, active_property, index, flt_flag):
+
+ collection_is_valid = False
+ if item.name in bpy.data.collections:
+ collection_is_valid = True
+
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ if collection_is_valid: # If action is valid
+ layout.prop(
+ bpy.data.collections[item.name],
+ "name",
+ text="",
+ emboss=False,
+ icon="OUTLINER_COLLECTION")
+ layout.prop(item, "use", text="")
+ else:
+ dataText = (
+ 'Collection named "' +
+ item.name +
+ '" Not Found. Please clic on update')
+ layout.label(text=dataText, icon="ERROR")
+ # Not optimised for 'GRID' layout type.
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+class BFU_OT_UpdateCollectionButton(bpy.types.Operator):
+ bl_label = "Update collection list"
+ bl_idname = "object.updatecollectionlist"
+ bl_description = "Update collection list"
+
+ def execute(self, context):
+ def UpdateExportCollectionList(scene):
+ # Update the provisional collection list known by the object
+
+ def SetUseFromLast(col_list, CollectionName):
+ for item in col_list:
+ if item[0] == CollectionName:
+ if item[1]:
+ return True
+ return False
+
+ colSave = [["", False]]
+ for col in scene.bfu_collection_asset_list: # CollectionProperty
+ name = col.name
+ use = col.use
+ colSave.append([name, use])
+ scene.bfu_collection_asset_list.clear()
+ for col in bpy.data.collections:
+ scene.bfu_collection_asset_list.add().name = col.name
+ useFromLast = SetUseFromLast(colSave, col.name)
+ scene.bfu_collection_asset_list[col.name].use = useFromLast
+ UpdateExportCollectionList(context.scene)
+ return {'FINISHED'}
+
+class BFU_OT_ShowCollectionToExport(bpy.types.Operator):
+ bl_label = "Show collection(s)"
+ bl_idname = "object.showscenecollection"
+ bl_description = "Click to show collections to export"
+
+ def execute(self, context):
+ scene = context.scene
+ collection_asset_cache = bfu_cached_asset_list.GetCollectionAssetCache()
+ collection_export_asset_list = collection_asset_cache.GetCollectionAssetList()
+ popup_title = "Collection list"
+ if len(collection_export_asset_list) > 0:
+ popup_title = (
+ str(len(collection_export_asset_list))+' collection(s) to export found.')
+ else:
+ popup_title = 'No collection to export found.'
+
+ def draw(self, context: bpy.types.Context):
+ col = self.layout.column()
+ for collection in collection_export_asset_list:
+ row = col.row()
+ row.label(text="- "+collection.name)
+ bpy.context.window_manager.popup_menu(
+ draw,
+ title=popup_title,
+ icon='GROUP')
+ return {'FINISHED'}
+
+
+class BFU_OT_SceneCollectionExport(bpy.types.PropertyGroup):
+ name: bpy.props.StringProperty(name="collection data name", default="Unknown", override={'LIBRARY_OVERRIDABLE'})
+ use: bpy.props.BoolProperty(name="export this collection", default=False, override={'LIBRARY_OVERRIDABLE'})
+
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_UL_CollectionExportTarget,
+ BFU_OT_UpdateCollectionButton,
+ BFU_OT_ShowCollectionToExport,
+ BFU_OT_SceneCollectionExport,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_collection_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Collection Properties")
+
+ bpy.types.Scene.bfu_collection_asset_list = bpy.props.CollectionProperty(
+ type=BFU_OT_SceneCollectionExport,
+ options={'LIBRARY_EDITABLE'},
+ override={'LIBRARY_OVERRIDABLE', 'USE_INSERTION'},
+ )
+
+ bpy.types.Scene.bfu_active_collection_asset_list = bpy.props.IntProperty(
+ name="Active Collection",
+ description="Index of the currently active collection",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=0
+ )
+
+ bpy.types.Collection.bfu_export_folder_name = bpy.props.StringProperty(
+ name="Sub folder name",
+ description=(
+ 'The name of sub folder.' +
+ ' You can now use ../ for up one directory.'
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ maxlen=64,
+ default="",
+ subtype='FILE_NAME'
+ )
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Collection.bfu_export_folder_name
+
+ del bpy.types.Scene.bfu_active_collection_asset_list
+ del bpy.types.Scene.bfu_collection_asset_list
+ del bpy.types.Scene.bfu_collection_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_base_collection/bfu_base_col_ui.py b/blender-for-unrealengine/bfu_base_collection/bfu_base_col_ui.py
new file mode 100644
index 00000000..1b5bc0f0
--- /dev/null
+++ b/blender-for-unrealengine/bfu_base_collection/bfu_base_col_ui.py
@@ -0,0 +1,70 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_export_procedure
+from .. import bfu_cached_asset_list
+
+
+def draw_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+
+
+ scene = bpy.context.scene
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("SCENE", "GENERAL"):
+
+ scene.bfu_collection_properties_expanded.draw(layout)
+ if scene.bfu_collection_properties_expanded.is_expend():
+ collectionListProperty = layout.column()
+ collectionListProperty.template_list(
+ # type and unique id
+ "BFU_UL_CollectionExportTarget", "",
+ # pointer to the CollectionProperty
+ scene, "bfu_collection_asset_list",
+ # pointer to the active identifier
+ scene, "bfu_active_collection_asset_list",
+ maxrows=5,
+ rows=5
+ )
+ collectionListProperty.operator(
+ "object.updatecollectionlist",
+ icon='RECOVER_LAST')
+
+ if scene.bfu_active_collection_asset_list < len(scene.bfu_collection_asset_list):
+ col_name = scene.bfu_collection_asset_list[scene.bfu_active_collection_asset_list].name
+ if col_name in bpy.data.collections:
+ col = bpy.data.collections[col_name]
+ col_prop = layout
+ col_prop.prop(col, 'bfu_export_folder_name', icon='FILE_FOLDER')
+ bfu_export_procedure.bfu_export_procedure_ui.draw_collection_export_procedure(layout, col)
+
+ collectionPropertyInfo = layout.row().box().split(factor=0.75)
+ collection_asset_cache = bfu_cached_asset_list.GetCollectionAssetCache()
+ collection_export_asset_list = collection_asset_cache.GetCollectionAssetList()
+ collectionNum = len(collection_export_asset_list)
+ collectionFeedback = (
+ str(collectionNum) +
+ " Collection(s) will be exported.")
+ collectionPropertyInfo.label(text=collectionFeedback, icon='INFO')
+ collectionPropertyInfo.operator("object.showscenecollection")
+ layout.label(text='Note: The collection are exported like StaticMesh.')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_base_collection/bfu_base_col_utils.py b/blender-for-unrealengine/bfu_base_collection/bfu_base_col_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_base_collection/bfu_base_col_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_base_object/__init__.py b/blender-for-unrealengine/bfu_base_object/__init__.py
new file mode 100644
index 00000000..8d5b79d1
--- /dev/null
+++ b/blender-for-unrealengine/bfu_base_object/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_base_obj_props
+from . import bfu_base_obj_ui
+from . import bfu_base_obj_utils
+
+if "bfu_base_obj_props" in locals():
+ importlib.reload(bfu_base_obj_props)
+if "bfu_base_obj_ui" in locals():
+ importlib.reload(bfu_base_obj_ui)
+if "bfu_base_obj_utils" in locals():
+ importlib.reload(bfu_base_obj_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_base_obj_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_base_obj_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_base_object/bfu_base_obj_props.py b/blender-for-unrealengine/bfu_base_object/bfu_base_obj_props.py
new file mode 100644
index 00000000..ce012967
--- /dev/null
+++ b/blender-for-unrealengine/bfu_base_object/bfu_base_obj_props.py
@@ -0,0 +1,111 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_export_type',
+ 'obj.bfu_export_folder_name',
+ 'obj.bfu_use_custom_export_name',
+ 'obj.bfu_custom_export_name',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_object_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Object Properties")
+
+ bpy.types.Object.bfu_export_type = bpy.props.EnumProperty(
+ name="Export type",
+ description="Export procedure",
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ("auto",
+ "Auto",
+ "Export with the parent if the parents is \"Export recursive\"",
+ "BOIDS",
+ 1),
+ ("export_recursive",
+ "Export recursive",
+ "Export self object and all children",
+ "KEYINGSET",
+ 2),
+ ("dont_export",
+ "Not exported",
+ "Will never export",
+ "CANCEL",
+ 3)
+ ]
+ )
+
+ bpy.types.Object.bfu_export_folder_name = bpy.props.StringProperty(
+ name="Sub folder name",
+ description=(
+ 'The name of sub folder.' +
+ ' You can now use ../ for up one directory.'
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ maxlen=64,
+ default="",
+ subtype='FILE_NAME'
+ )
+
+ bpy.types.Object.bfu_use_custom_export_name = bpy.props.BoolProperty(
+ name="Export with custom name",
+ description=("Specify a custom name for the exported file"),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+ bpy.types.Object.bfu_custom_export_name = bpy.props.StringProperty(
+ name="",
+ description="The name of exported file",
+ override={'LIBRARY_OVERRIDABLE'},
+ default="MyObjectExportName.fbx"
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_custom_export_name
+ del bpy.types.Object.bfu_use_custom_export_name
+
+ del bpy.types.Object.bfu_export_folder_name
+ del bpy.types.Object.bfu_export_type
+
+ del bpy.types.Scene.bfu_object_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_base_object/bfu_base_obj_ui.py b/blender-for-unrealengine/bfu_base_object/bfu_base_obj_ui.py
new file mode 100644
index 00000000..c74b9d25
--- /dev/null
+++ b/blender-for-unrealengine/bfu_base_object/bfu_base_obj_ui.py
@@ -0,0 +1,87 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_assets_manager
+from .. import bfu_alembic_animation
+from .. import bfu_groom
+from .. import bfu_skeletal_mesh
+
+
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ # Hide filters
+ if obj is None:
+ layout.row().label(text='No active object.')
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
+ scene.bfu_object_properties_expanded.draw(layout)
+ if scene.bfu_object_properties_expanded.is_expend():
+ AssetType = layout.row()
+ AssetType.prop(obj, 'name', text="", icon='OBJECT_DATA')
+ # Show asset type
+ asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
+ if asset_class:
+ asset_type_name = asset_class.get_asset_type_name(obj)
+ else:
+ asset_type_name = "Asset type not found."
+
+ AssetType.label(text='('+asset_type_name+')')
+
+ ExportType = layout.column()
+ ExportType.prop(obj, 'bfu_export_type')
+
+
+ if obj.bfu_export_type == "export_recursive":
+
+ folderNameProperty = layout.column()
+ folderNameProperty.prop(obj, 'bfu_export_folder_name', icon='FILE_FOLDER')
+
+ ProxyProp = layout.column()
+ if bfu_utils.GetExportAsProxy(obj):
+ ProxyProp.label(text="The Armature was detected as a proxy.")
+ proxy_child = bfu_utils.GetExportProxyChild(obj)
+ if proxy_child:
+ ProxyProp.label(text="Proxy child: " + proxy_child.name)
+ else:
+ ProxyProp.label(text="Proxy child not found")
+
+ if not bfu_utils.GetExportAsProxy(obj):
+ # exportCustomName
+ exportCustomName = layout.row()
+ exportCustomName.prop(obj, "bfu_use_custom_export_name")
+ useCustomName = obj.bfu_use_custom_export_name
+ exportCustomNameText = exportCustomName.column()
+ exportCustomNameText.prop(obj, "bfu_custom_export_name")
+ exportCustomNameText.enabled = useCustomName
+ bfu_alembic_animation.bfu_alembic_animation_ui.draw_general_ui_object(layout, obj)
+ bfu_groom.bfu_groom_ui.draw_general_ui_object(layout, obj)
+ bfu_skeletal_mesh.bfu_skeletal_mesh_ui.draw_general_ui_object(layout, obj)
diff --git a/blender-for-unrealengine/bfu_base_object/bfu_base_obj_utils.py b/blender-for-unrealengine/bfu_base_object/bfu_base_obj_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_base_object/bfu_base_obj_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_basics.py b/blender-for-unrealengine/bfu_basics.py
index e142385d..89e396f3 100644
--- a/blender-for-unrealengine/bfu_basics.py
+++ b/blender-for-unrealengine/bfu_basics.py
@@ -28,69 +28,21 @@ def GetAddonPrefs():
return bpy.context.preferences.addons[__package__].preferences
-def is_deleted(o):
- if o and o is not None:
- return not (o.name in bpy.data.objects)
- else:
- return True
-
-
def CheckPluginIsActivated(PluginName):
is_enabled, is_loaded = addon_utils.check(PluginName)
return is_enabled and is_loaded
-def ChecksRelationship(arrayA, arrayB):
- # Checks if it exits an identical variable in two lists
-
- for a in arrayA:
- for b in arrayB:
- if a == b:
- return True
- return False
-
-
-def nextPowerOfTwo(n):
- # compute power of two greater than or equal to n
-
- # decrement n (to handle cases when n itself
- # is a power of 2)
- n = n - 1
-
- # do till only one bit is left
- while n & n - 1:
- n = n & n - 1 # unset rightmost bit
-
- # n is now a power of two (less than n)
- return n << 1
-
-
-def previousPowerOfTwo(n):
- # compute power of two less than or equal to n
-
- # do till only one bit is left
- while (n & n - 1):
- n = n & n - 1 # unset rightmost bit
-
- # n is now a power of two (less than or equal to n)
- return n
-
-
def RemoveFolderTree(folder):
dirpath = Path(folder)
if dirpath.exists() and dirpath.is_dir():
shutil.rmtree(dirpath, ignore_errors=True)
-
-
-
-
def getRootBoneParent(bone):
if bone.parent is not None:
return getRootBoneParent(bone.parent)
return bone
-
def getFirstDeformBoneParent(bone):
if bone.parent is not None:
if bone.use_deform is True:
@@ -99,7 +51,6 @@ def getFirstDeformBoneParent(bone):
return getFirstDeformBoneParent(bone.parent)
return bone
-
def SetCollectionUse(collection):
# Set if collection is hide and selectable
collection.hide_viewport = False
diff --git a/blender-for-unrealengine/bfu_cached_asset_list.py b/blender-for-unrealengine/bfu_cached_asset_list.py
index 05d9416a..06266e02 100644
--- a/blender-for-unrealengine/bfu_cached_asset_list.py
+++ b/blender-for-unrealengine/bfu_cached_asset_list.py
@@ -159,7 +159,7 @@ def GetAnimationAssetList(self):
elif obj.bfu_anim_action_export_enum == "export_specific_list":
for action in bpy.data.actions:
- for targetAction in obj.bfu_animation_asset_list:
+ for targetAction in obj.bfu_action_asset_list:
if targetAction.use:
if targetAction.name == action.name:
TargetActionToExport.append(action)
@@ -230,7 +230,7 @@ def getHaveParentToExport(obj):
for collection in collectionList:
# Collection
- if scene.static_collection_export:
+ if scene.bfu_use_static_collection_export:
TargetAssetToExport.append(AssetToExport(collection, None, "Collection StaticMesh"))
for obj in objList:
@@ -239,7 +239,7 @@ def getHaveParentToExport(obj):
if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
# Skeletal Mesh
- if scene.skeletal_export:
+ if scene.bfu_use_skeletal_export:
if obj.bfu_modular_skeletal_mesh_mode == "all_in_one":
asset = AssetToExport(obj, None, "SkeletalMesh")
asset.name = obj.name
@@ -270,7 +270,7 @@ def getHaveParentToExport(obj):
TargetAssetToExport.append(asset)
# NLA
- if scene.anin_export:
+ if scene.bfu_use_anin_export:
if obj.bfu_anim_nla_use:
TargetAssetToExport.append(AssetToExport(obj, obj.animation_data, "NlAnim"))
@@ -283,12 +283,12 @@ def getHaveParentToExport(obj):
TargetAssetToExport.append(AssetToExport(obj, action, "Action"))
else:
# Action
- if scene.anin_export:
+ if scene.bfu_use_anin_export:
if bfu_utils.GetActionType(action) == "Action":
TargetAssetToExport.append(AssetToExport(obj, action, "Action"))
# Pose
- if scene.anin_export:
+ if scene.bfu_use_anin_export:
if bfu_utils.GetActionType(action) == "Pose":
TargetAssetToExport.append(AssetToExport(obj, action, "Pose"))
# Others
diff --git a/blender-for-unrealengine/bfu_camera/bfu_camera_data.py b/blender-for-unrealengine/bfu_camera/bfu_camera_data.py
index 6469cbf8..25e99c62 100644
--- a/blender-for-unrealengine/bfu_camera/bfu_camera_data.py
+++ b/blender-for-unrealengine/bfu_camera/bfu_camera_data.py
@@ -2,7 +2,7 @@
import math
from typing import Dict, Any
from . import bfu_camera_unreal_utils
-from .. import bps
+from .. import bpl
from .. import bbpl
from .. import languages
from .. import bfu_basics
@@ -283,7 +283,7 @@ def evaluate_all_tracks(self, camera, frame_start, frame_end):
addon_prefs = bfu_basics.GetAddonPrefs()
print(f"Start evaluate camera {camera.name} Frames:({str(frame_start)}-{str(frame_end)})")
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
slms = bfu_utils.TimelineMarkerSequence()
@@ -341,7 +341,7 @@ def optimizated_evaluate_track_at_frame(evaluate: BFU_CameraTracks):
scene = bpy.context.scene
addon_prefs = bfu_basics.GetAddonPrefs()
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
slms = bfu_utils.TimelineMarkerSequence()
diff --git a/blender-for-unrealengine/bfu_camera/bfu_camera_type.py b/blender-for-unrealengine/bfu_camera/bfu_camera_type.py
index 01b08cdb..5bc2d788 100644
--- a/blender-for-unrealengine/bfu_camera/bfu_camera_type.py
+++ b/blender-for-unrealengine/bfu_camera/bfu_camera_type.py
@@ -30,7 +30,7 @@ def __init__(self):
super().__init__()
pass
- def support_asset_type(self, obj):
+ def support_asset_type(self, obj, details = None):
if obj.type == "CAMERA":
return True
return False
@@ -48,15 +48,20 @@ def get_obj_file_name(self, obj, desired_name="", fileType=".fbx"):
return bfu_basics.ValidFilename(scene.bfu_camera_prefix_export_name+desired_name+fileType)
return bfu_basics.ValidFilename(scene.bfu_camera_prefix_export_name+obj.name+fileType)
- def get_obj_export_directory_path(self, obj):
+ def get_obj_export_directory_path(self, obj, absolute = True):
folder_name = bfu_utils.get_export_folder_name(obj)
scene = bpy.context.scene
- dirpath = os.path.join(scene.bfu_export_camera_file_path, folder_name)
+ if(absolute):
+ root_path = bpy.path.abspath(scene.bfu_export_camera_file_path)
+ else:
+ root_path = scene.bfu_export_camera_file_path
+
+ dirpath = os.path.join(root_path, folder_name)
return dirpath
def can_export_asset(self):
scene = bpy.context.scene
- return scene.camera_export
+ return scene.bfu_use_camera_export
def can_export_obj_asset(self, obj):
return self.can_export_asset()
diff --git a/blender-for-unrealengine/bfu_camera/bfu_camera_ui_and_props.py b/blender-for-unrealengine/bfu_camera/bfu_camera_ui_and_props.py
index 409d49d9..a6d10a60 100644
--- a/blender-for-unrealengine/bfu_camera/bfu_camera_ui_and_props.py
+++ b/blender-for-unrealengine/bfu_camera/bfu_camera_ui_and_props.py
@@ -22,6 +22,7 @@
from . import bfu_camera_utils
from . import bfu_camera_write_paste_commands
from .. import bfu_basics
+from .. import bfu_ui
from .. import bbpl
from .. import languages
@@ -48,29 +49,29 @@ def draw_ui_object_camera(layout: bpy.types.UILayout, obj: bpy.types.Object):
camera_ui = layout.column()
scene = bpy.context.scene
- scene.bfu_camera_properties_expanded.draw(camera_ui)
- if scene.bfu_camera_properties_expanded.is_expend():
- if obj.type == "CAMERA":
- camera_ui_pop = camera_ui.column()
-
- export_procedure_prop = camera_ui_pop.column()
- export_procedure_prop.prop(obj, 'bfu_camera_export_procedure')
-
- camera_ui_pop.prop(obj, 'bfu_desired_camera_type')
- if obj.bfu_desired_camera_type == "CUSTOM":
- camera_ui_pop.prop(obj, 'bfu_custom_camera_actor')
- camera_ui_pop.prop(obj, 'bfu_custom_camera_default_actor')
- camera_ui_pop.prop(obj, 'bfu_custom_camera_component')
- camera_ui_pop.prop(obj, 'bfu_export_fbx_camera')
- camera_ui_pop.prop(obj, 'bfu_fix_axis_flippings')
- camera_ui_pop.enabled = obj.bfu_export_type == "export_recursive"
- camera_ui.operator("object.bfu_copy_active_camera_data", icon="COPYDOWN")
-
-
-def draw_ui_scene_camera(layout: bpy.types.UILayout):
-
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
+ scene.bfu_camera_properties_expanded.draw(camera_ui)
+ if scene.bfu_camera_properties_expanded.is_expend():
+ if obj.type == "CAMERA":
+ camera_ui_pop = camera_ui.column()
+
+ export_procedure_prop = camera_ui_pop.column()
+ export_procedure_prop.prop(obj, 'bfu_camera_export_procedure')
+
+ camera_ui_pop.prop(obj, 'bfu_desired_camera_type')
+ if obj.bfu_desired_camera_type == "CUSTOM":
+ camera_ui_pop.prop(obj, 'bfu_custom_camera_actor')
+ camera_ui_pop.prop(obj, 'bfu_custom_camera_default_actor')
+ camera_ui_pop.prop(obj, 'bfu_custom_camera_component')
+ camera_ui_pop.prop(obj, 'bfu_export_fbx_camera')
+ camera_ui_pop.prop(obj, 'bfu_fix_axis_flippings')
+ camera_ui_pop.enabled = obj.bfu_export_type == "export_recursive"
+ camera_ui.operator("object.bfu_copy_active_camera_data", icon="COPYDOWN")
+
+
+def draw_tools_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
camera_ui = layout.column()
- scene = bpy.context.scene
scene.bfu_camera_tools_expanded.draw(camera_ui)
if scene.bfu_camera_tools_expanded.is_expend():
camera_ui.operator("object.copy_selected_cameras_data", icon="COPYDOWN")
diff --git a/blender-for-unrealengine/bfu_check_potential_error.py b/blender-for-unrealengine/bfu_check_potential_error.py
deleted file mode 100644
index 1baedd8a..00000000
--- a/blender-for-unrealengine/bfu_check_potential_error.py
+++ /dev/null
@@ -1,748 +0,0 @@
-# ====================== BEGIN GPL LICENSE BLOCK ============================
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-# All rights reserved.
-#
-# ======================= END GPL LICENSE BLOCK =============================
-
-
-import bpy
-import fnmatch
-import math
-
-from . import bbpl
-from . import bfu_basics
-from . import bfu_assets_manager
-from . import bfu_utils
-from . import bfu_cached_asset_list
-
-from . import bfu_collision
-from . import bfu_socket
-from . import bfu_camera
-from . import bfu_alembic_animation
-from . import bfu_groom
-from . import bfu_spline
-from . import bfu_skeletal_mesh
-from . import bfu_static_mesh
-
-
-
-
-def process_general_fix():
- fixed_collisions = bfu_collision.bfu_collision_utils.fix_export_type_on_collision()
- fixed_collision_names = bfu_collision.bfu_collision_utils.fix_name_on_collision()
- fixed_sockets = bfu_socket.bfu_socket_utils.fix_export_type_on_socket()
- fixed_socket_names = bfu_socket.bfu_socket_utils.fix_name_on_socket()
-
- fix_info = {
- "Fixed Collision(s)": fixed_collisions,
- "Fixed Collision Names(s)": fixed_collision_names,
- "Fixed Socket(s)": fixed_sockets,
- "Fixed Socket Names(s)": fixed_socket_names,
- }
-
- return fix_info
-
-
-
-
-def GetVertexWithZeroWeight(Armature, Mesh):
- vertices = []
-
- # Créez un ensemble des noms des os de l'armature pour une recherche plus rapide
- armature_bone_names = set(bone.name for bone in Armature.data.bones)
-
-
- for vertex in Mesh.data.vertices: #MeshVertex(bpy_struct)
- cumulateWeight = 0
-
- if vertex.groups:
- for group_elem in vertex.groups: #VertexGroupElement(bpy_struct)
- if group_elem.weight > 0:
- group_index = group_elem.group
- group_len = len(Mesh.vertex_groups)
- if group_index <= group_len:
- group = Mesh.vertex_groups[group_elem.group]
-
- # Utilisez l'ensemble des noms d'os pour vérifier l'appartenance à l'armature
- if group.name in armature_bone_names:
- cumulateWeight += group_elem.weight
-
- if cumulateWeight == 0:
- vertices.append(vertex)
-
- return vertices
-
-
-def ContainsArmatureModifier(obj):
- for mod in obj.modifiers:
- if mod.type == "ARMATURE":
- return True
- return False
-
-def GetSkeletonMeshs(obj):
- meshs = []
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- childs = bfu_utils.GetExportDesiredChilds(obj)
- for child in childs:
- if child.type == "MESH":
- meshs.append(child)
- return meshs
-
-
-def UpdateUnrealPotentialError():
- # Find and reset list of all potential error in scene
-
- addon_prefs = bfu_basics.GetAddonPrefs()
- PotentialErrors = bpy.context.scene.potentialErrorList
- PotentialErrors.clear()
-
- # prepares the data to avoid unnecessary loops
- objToCheck = []
- final_asset_cache = bfu_cached_asset_list.GetfinalAssetCache()
- final_asset_list_to_export = final_asset_cache.GetFinalAssetList()
- for Asset in final_asset_list_to_export:
- if Asset.obj in bfu_utils.GetAllobjectsByExportType("export_recursive"):
- if Asset.obj not in objToCheck:
- objToCheck.append(Asset.obj)
- for child in bfu_utils.GetExportDesiredChilds(Asset.obj):
- if child not in objToCheck:
- objToCheck.append(child)
-
- MeshTypeToCheck = []
- for obj in objToCheck:
- if obj.type == 'MESH':
- MeshTypeToCheck.append(obj)
-
- MeshTypeWithoutCol = [] # is Mesh Type To Check Without Collision
- for obj in MeshTypeToCheck:
- if not bfu_utils.CheckIsCollision(obj):
- MeshTypeWithoutCol.append(obj)
-
- def CheckUnitScale():
- # Check if the unit scale is equal to 0.01.
- if addon_prefs.notifyUnitScalePotentialError:
- unit_scale = bfu_utils.get_scene_unit_scale()
- if not bfu_utils.get_scene_unit_scale_is_close(0.01):
- MyError = PotentialErrors.add()
- MyError.name = bpy.context.scene.name
- MyError.type = 1
- MyError.text = ('Scene "'+bpy.context.scene.name + '" has a UnitScale egal to ' + str(unit_scale))
- MyError.text += ('\nFor Unreal unit scale equal to 0.01 is recommended.')
- MyError.text += ('\n(You can disable this potential error in addon_prefs)')
- MyError.object = None
- MyError.correctRef = "SetUnrealUnit"
- MyError.correctlabel = 'Set Unreal Unit'
-
- def CheckSceneFrameRate():
- # Check Scene Frame Rate.
- scene = bpy.context.scene
- denominator = scene.render.fps_base
- numerator = scene.render.fps
-
- # Ensure denominator and numerator are at least 1 and int 32
- new_denominator = max(round(denominator), 1)
- new_numerator = max(round(numerator), 1)
-
- if denominator != new_denominator or numerator != new_numerator:
- message = ('Frame rate denominator and numerator must be an int32 over zero.\n'
- 'Float denominator and numerator is not supported in Unreal Engine Sequencer.\n'
- f'- Denominator: {denominator} -> {new_denominator}\n'
- f'- Numerator: {numerator} -> {new_numerator}')
-
- MyError = PotentialErrors.add()
- MyError.name = bpy.context.scene.name
- MyError.type = 2
- MyError.text = (message)
- MyError.docsOcticon = 'scene-frame-rate'
-
-
- def CheckObjType():
- # Check if objects use a non-recommended type
- for obj in objToCheck:
- if (obj.type == "SURFACE" or
- obj.type == "META" or
- obj.type == "FONT"):
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 1
- MyError.text = (
- 'Object "'+obj.name +
- '" is a '+obj.type +
- '. The object of the type SURFACE,' +
- ' META and FONT is not recommended.')
- MyError.object = obj
- MyError.correctRef = "ConvertToMesh"
- MyError.correctlabel = 'Convert to mesh'
-
- def CheckShapeKeys():
- for obj in MeshTypeToCheck:
- if obj.data.shape_keys is not None:
- # Check that no modifiers is destructive for the key shapes
- if len(obj.data.shape_keys.key_blocks) > 0:
- for modif in obj.modifiers:
- if modif.type != "ARMATURE":
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 2
- MyError.object = obj
- MyError.itemName = modif.name
- MyError.text = (
- 'In object "'+obj.name +
- '" the modifier '+modif.type +
- ' named "'+modif.name +
- '" can destroy shape keys.' +
- ' Please use only Armature modifier' +
- ' with shape keys.')
- MyError.correctRef = "RemoveModfier"
- MyError.correctlabel = 'Remove modifier'
-
- # Check that the key shapes are not out of bounds for Unreal
- for key in obj.data.shape_keys.key_blocks:
- # Min
- if key.slider_min < -5:
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 1
- MyError.object = obj
- MyError.itemName = key.name
- MyError.text = (
- 'In object "'+obj.name +
- '" the shape key "'+key.name +
- '" is out of bounds for Unreal.' +
- ' The min range of must not be inferior to -5.')
- MyError.correctRef = "SetKeyRangeMin"
- MyError.correctlabel = 'Set min range to -5'
-
- # Max
- if key.slider_max > 5:
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 1
- MyError.object = obj
- MyError.itemName = key.name
- MyError.text = (
- 'In object "'+obj.name +
- '" the shape key "'+key.name +
- '" is out of bounds for Unreal.' +
- ' The max range of must not be superior to 5.')
- MyError.correctRef = "SetKeyRangeMax"
- MyError.correctlabel = 'Set max range to -5'
-
- def CheckUVMaps():
- # Check that the objects have at least one UV map valid
- for obj in MeshTypeWithoutCol:
- if len(obj.data.uv_layers) < 1:
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 1
- MyError.text = (
- 'Object "'+obj.name +
- '" does not have any UV Layer.')
- MyError.object = obj
- MyError.correctRef = "CreateUV"
- MyError.correctlabel = 'Create Smart UV Project'
-
- def CheckBadStaicMeshExportedLikeSkeletalMesh():
- # Check if the correct object is defined as exportable
- for obj in MeshTypeToCheck:
- for modif in obj.modifiers:
- if modif.type == "ARMATURE":
- if obj.bfu_export_type == "export_recursive":
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 1
- MyError.text = (
- 'In object "'+obj.name +
- '" the modifier '+modif.type +
- ' named "'+modif.name +
- '" will not be applied when exported' +
- ' with StaticMesh assets.\nNote: with armature' +
- ' if you want export objets as skeletal mesh you' +
- ' need set only the armature as' +
- ' export_recursive not the childs')
- MyError.object = obj
-
- def CheckArmatureScale():
- # Check if the ARMATURE use the same value on all scale axes
- for obj in objToCheck:
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- if obj.scale.z != obj.scale.y or obj.scale.z != obj.scale.x:
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 2
- MyError.text = (
- 'In object "'+obj.name +
- '" do not use the same value on all scale axes ')
- MyError.text += (
- '\nScale x:' +
- str(obj.scale.x)+' y:'+str(obj.scale.y) +
- ' z:'+str(obj.scale.z))
- MyError.object = obj
-
- def CheckArmatureNumber():
- # check Modifier or Constraint ARMATURE number = 1
- for obj in objToCheck:
- meshs = GetSkeletonMeshs(obj)
- for mesh in meshs:
- # Count
- armature_modifiers = 0
- armature_constraint = 0
- for mod in mesh.modifiers:
- if mod.type == "ARMATURE":
- armature_modifiers += 1
- for const in mesh.constraints:
- if const.type == "ARMATURE":
- armature_constraint += 1
-
- # Check result > 1
- if armature_modifiers + armature_constraint > 1:
- MyError = PotentialErrors.add()
- MyError.name = mesh.name
- MyError.type = 2
- MyError.text = (
- 'In object "'+mesh.name + '" ' +
- str(armature_modifiers) + ' Armature modifier(s) and ' +
- str(armature_modifiers) + ' Armature constraint(s) was found. ' +
- ' Please use only one Armature modifier or one Armature constraint.')
- MyError.object = mesh
-
- # Check result == 0
- if armature_modifiers + armature_constraint == 0:
- MyError = PotentialErrors.add()
- MyError.name = mesh.name
- MyError.type = 2
- MyError.text = (
- 'In object "'+mesh.name + '" ' +
- ' no Armature modifiers or constraints was found. ' +
- ' Please use only one Armature modifier or one Armature constraint.')
- MyError.object = mesh
-
- def CheckArmatureModData():
- # check the parameter of Modifier ARMATURE
- for obj in MeshTypeToCheck:
- for mod in obj.modifiers:
- if mod.type == "ARMATURE":
- if mod.use_deform_preserve_volume:
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 2
- MyError.text = (
- 'In object "'+obj.name +
- '" the modifier '+mod.type +
- ' named "'+mod.name +
- '". The parameter Preserve Volume' +
- ' must be set to False.')
- MyError.object = obj
- MyError.itemName = mod.name
- MyError.correctRef = "PreserveVolume"
- MyError.correctlabel = 'Set Preserve Volume to False'
-
- def CheckArmatureConstData():
- # check the parameter of constraint ARMATURE
- for obj in MeshTypeToCheck:
- for const in obj.constraints:
- if const.type == "ARMATURE":
- pass
- # TO DO.
-
- def CheckArmatureBoneData():
- # check the parameter of the ARMATURE bones
- for obj in objToCheck:
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- for bone in obj.data.bones:
- if (not obj.bfu_export_deform_only or
- (bone.use_deform and obj.bfu_export_deform_only)):
-
- if bone.bbone_segments > 1:
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 1
- MyError.text = (
- 'In object3 "'+obj.name +
- '" the bone named "'+bone.name +
- '". The parameter Bendy Bones / Segments' +
- ' must be set to 1.')
- MyError.text += (
- '\nBendy bones are not supported by' +
- ' Unreal Engine, so that better to disable' +
- ' it if you want the same animation preview' +
- ' in Unreal and blender.')
- MyError.object = obj
- MyError.itemName = bone.name
- MyError.selectPoseBoneButton = True
- MyError.correctRef = "BoneSegments"
- MyError.correctlabel = 'Set Bone Segments to 1'
- MyError.docsOcticon = 'bendy-bone'
-
- def CheckArmatureValidChild():
- # Check that skeleton also has a mesh to export
-
- for obj in objToCheck:
- export_as_proxy = bfu_utils.GetExportAsProxy(obj)
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- childs = bfu_utils.GetExportDesiredChilds(obj)
- validChild = 0
- for child in childs:
- if child.type == "MESH":
- validChild += 1
- if export_as_proxy:
- if bfu_utils.GetExportProxyChild(obj) is not None:
- validChild += 1
- if validChild < 1:
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 2
- MyError.text = (
- 'Object "'+obj.name +
- '" is an Armature and does not have' +
- ' any valid children.')
- MyError.object = obj
-
- def CheckArmatureChildWithBoneParent():
- # If you use Parent Bone to parent your mesh to your armature the import will fail.
- for obj in objToCheck:
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- childs = bfu_utils.GetExportDesiredChilds(obj)
- for child in childs:
- if child.type == "MESH":
- if child.parent_type == 'BONE':
- MyError = PotentialErrors.add()
- MyError.name = child.name
- MyError.type = 2
- MyError.text = (
- 'Object "'+child.name +
- '" use Parent Bone to parent. ' +
- '\n If you use Parent Bone to parent your mesh to your armature the import will fail.')
- MyError.object = child
- MyError.docsOcticon = 'armature-child-with-bone-parent'
-
- def CheckArmatureMultipleRoots():
- # Check that skeleton have multiples roots
- for obj in objToCheck:
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- rootBones = bfu_utils.GetArmatureRootBones(obj)
-
- if len(rootBones) > 1:
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 1
- MyError.text = (
- 'Object "'+obj.name +
- '" have Multiple roots bones.' +
- ' Unreal only support single root bone')
- MyError.text += '\nA custom root bone will be added at the export.'
- MyError.text += ' '+str(len(rootBones))+' root bones found: '
- MyError.text += '\n'
- for rootBone in rootBones:
- MyError.text += rootBone.name+', '
- MyError.object = obj
-
- def CheckArmatureNoDeformBone():
- # Check that skeleton have at less one deform bone
- for obj in objToCheck:
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- if obj.bfu_export_deform_only:
- for bone in obj.data.bones:
- if bone.use_deform:
- return
- MyError = PotentialErrors.add()
- MyError.name = obj.name
- MyError.type = 2
- MyError.text = (
- 'Object "'+obj.name +
- '" don\'t have any deform bones.' +
- ' Unreal will import it like a StaticMesh.')
- MyError.object = obj
-
- def CheckMarkerOverlay():
- # Check that there is no overlap with the Marker
- usedFrame = []
- for marker in bpy.context.scene.timeline_markers:
- if marker.frame in usedFrame:
- MyError = PotentialErrors.add()
- MyError.type = 2
- MyError.text = (
- 'In the scene timeline the frame "' +
- str(marker.frame)+'" contains overlaped Markers' +
- '\n To avoid camera conflict in the generation' +
- ' of sequencer you must use max one marker per frame.')
- else:
- usedFrame.append(marker.frame)
-
- def CheckVertexGroupWeight():
- # Check that all vertex have a weight
- for obj in objToCheck:
- meshs = GetSkeletonMeshs(obj)
- for mesh in meshs:
- if mesh.type == "MESH":
- if ContainsArmatureModifier(mesh):
- # Result data
- VertexWithZeroWeight = GetVertexWithZeroWeight(
- obj,
- mesh)
- if len(VertexWithZeroWeight) > 0:
- MyError = PotentialErrors.add()
- MyError.name = mesh.name
- MyError.type = 1
- MyError.text = (
- 'Object named "'+mesh.name +
- '" contains '+str(len(VertexWithZeroWeight)) +
- ' vertex with zero cumulative valid weight.')
- MyError.text += (
- '\nNote: Vertex groups must have' +
- ' a bone with the same name to be valid.')
- MyError.object = mesh
- MyError.selectVertexButton = True
- MyError.selectOption = "VertexWithZeroWeight"
-
- def CheckZeroScaleKeyframe():
- # Check that animations do not use a invalid value
- for obj in objToCheck:
- if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- animation_asset_cache = bfu_cached_asset_list.GetAnimationAssetCache(obj)
- animation_to_export = animation_asset_cache.GetAnimationAssetList()
- for action in animation_to_export:
- for fcurve in action.fcurves:
- if fcurve.data_path.split(".")[-1] == "scale":
- for key in fcurve.keyframe_points:
- xCurve, yCurve = key.co
- if key.co[1] == 0:
- MyError = PotentialErrors.add()
- MyError.type = 2
- MyError.text = (
- 'In action "'+action.name +
- '" at frame '+str(key.co[0]) +
- ', the bone named "' +
- fcurve.data_path.split('"')[1] +
- '" has a zero value in scale' +
- ' transform. ' +
- 'This is invalid in Unreal.')
-
- CheckUnitScale()
- CheckSceneFrameRate()
- CheckObjType()
- CheckShapeKeys()
- CheckUVMaps()
- CheckBadStaicMeshExportedLikeSkeletalMesh()
- CheckArmatureScale()
- CheckArmatureNumber()
- CheckArmatureModData()
- CheckArmatureConstData()
- CheckArmatureBoneData()
- CheckArmatureValidChild()
- CheckArmatureMultipleRoots()
- CheckArmatureChildWithBoneParent()
- CheckArmatureNoDeformBone()
- CheckMarkerOverlay()
- CheckVertexGroupWeight()
- CheckZeroScaleKeyframe()
-
- return PotentialErrors
-
-
-def SelectPotentialErrorObject(errorIndex):
- # Select potential error
-
- bbpl.utils.safe_mode_set('OBJECT', bpy.context.active_object)
- scene = bpy.context.scene
- error = scene.potentialErrorList[errorIndex]
- obj = error.object
-
- bpy.ops.object.select_all(action='DESELECT')
- obj.hide_viewport = False
- obj.hide_set(False)
- obj.select_set(True)
- bpy.context.view_layer.objects.active = obj
-
- # show collection for select object
- for collection in bpy.data.collections:
- for ColObj in collection.objects:
- if ColObj == obj:
- bfu_basics.SetCollectionUse(collection)
- bpy.ops.view3d.view_selected()
- return obj
-
-
-def SelectPotentialErrorVertex(errorIndex):
- # Select potential error
- SelectPotentialErrorObject(errorIndex)
- bbpl.utils.safe_mode_set('EDIT')
-
- scene = bpy.context.scene
- error = scene.potentialErrorList[errorIndex]
- obj = error.object
- bpy.ops.mesh.select_mode(type="VERT")
- bpy.ops.mesh.select_all(action='DESELECT')
-
- bbpl.utils.safe_mode_set('OBJECT')
- if error.selectOption == "VertexWithZeroWeight":
- for vertex in GetVertexWithZeroWeight(obj.parent, obj):
- vertex.select = True
- bbpl.utils.safe_mode_set('EDIT')
- bpy.ops.view3d.view_selected()
- return obj
-
-
-def SelectPotentialErrorPoseBone(errorIndex):
- # Select potential error
- SelectPotentialErrorObject(errorIndex)
- bbpl.utils.safe_mode_set('POSE')
-
- scene = bpy.context.scene
- error = scene.potentialErrorList[errorIndex]
- obj = error.object
- bone = obj.data.bones[error.itemName]
-
- # Make bone visible if hide in a layer
- for x, layer in enumerate(bone.layers):
- if not obj.data.layers[x] and layer:
- obj.data.layers[x] = True
-
- bpy.ops.pose.select_all(action='DESELECT')
- obj.data.bones.active = bone
- bone.select = True
-
- bpy.ops.view3d.view_selected()
- return obj
-
-
-def TryToCorrectPotentialError(errorIndex):
- # Try to correct potential error
-
- scene = bpy.context.scene
- error = scene.potentialErrorList[errorIndex]
- global successCorrect
- successCorrect = False
-
- local_view_areas = bbpl.scene_utils.move_to_global_view()
-
- MyCurrentDataSave = bbpl.utils.UserSceneSave()
- MyCurrentDataSave.save_current_scene()
-
- bbpl.utils.safe_mode_set('OBJECT', MyCurrentDataSave.user_select_class.user_active)
-
- print("Start correct")
-
- def SelectObj(obj):
- bpy.ops.object.select_all(action='DESELECT')
- obj.select_set(True)
- bpy.context.view_layer.objects.active = obj
-
- # Correction list
-
- if error.correctRef == "SetUnrealUnit":
- bpy.context.scene.unit_settings.scale_length = 0.01
- successCorrect = True
-
- if error.correctRef == "ConvertToMesh":
- obj = error.object
- SelectObj(obj)
- bpy.ops.object.convert(target='MESH')
- successCorrect = True
-
- if error.correctRef == "SetKeyRangeMin":
- obj = error.object
- key = obj.data.shape_keys.key_blocks[error.itemName]
- key.slider_min = -5
- successCorrect = True
-
- if error.correctRef == "SetKeyRangeMax":
- obj = error.object
- key = obj.data.shape_keys.key_blocks[error.itemName]
- key.slider_max = 5
- successCorrect = True
-
- if error.correctRef == "CreateUV":
- obj = error.object
- SelectObj(obj)
- if bbpl.utils.safe_mode_set("EDIT", obj):
- bpy.ops.uv.smart_project()
- successCorrect = True
- else:
- successCorrect = False
-
- if error.correctRef == "RemoveModfier":
- obj = error.object
- mod = obj.modifiers[error.itemName]
- obj.modifiers.remove(mod)
- successCorrect = True
-
- if error.correctRef == "PreserveVolume":
- obj = error.object
- mod = obj.modifiers[error.itemName]
- mod.use_deform_preserve_volume = False
- successCorrect = True
-
- if error.correctRef == "BoneSegments":
- obj = error.object
- bone = obj.data.bones[error.itemName]
- bone.bbone_segments = 1
- successCorrect = True
-
- if error.correctRef == "InheritScale":
- obj = error.object
- bone = obj.data.bones[error.itemName]
- bone.use_inherit_scale = True
- successCorrect = True
-
- # ----------------------------------------Reset data
- MyCurrentDataSave.reset_select_by_name()
- MyCurrentDataSave.reset_scene_at_save()
- bbpl.scene_utils.move_to_local_view(local_view_areas)
-
- # ----------------------------------------
-
- if successCorrect:
- scene.potentialErrorList.remove(errorIndex)
- print("end correct, Error: " + error.correctRef)
- return "Corrected"
- print("end correct, Error not found")
- return "Correct fail"
-
-
-class BFU_OT_UnrealPotentialError(bpy.types.PropertyGroup):
- type: bpy.props.IntProperty(default=0) # 0:Info, 1:Warning, 2:Error
- object: bpy.props.PointerProperty(type=bpy.types.Object)
- ###
- selectObjectButton: bpy.props.BoolProperty(default=True)
- selectVertexButton: bpy.props.BoolProperty(default=False)
- selectPoseBoneButton: bpy.props.BoolProperty(default=False)
- ###
- selectOption: bpy.props.StringProperty(default="None") # 0:VertexWithZeroWeight
- itemName: bpy.props.StringProperty(default="None")
- text: bpy.props.StringProperty(default="Unknown")
- correctRef: bpy.props.StringProperty(default="None")
- correctlabel: bpy.props.StringProperty(default="Fix it !")
- correctDesc: bpy.props.StringProperty(default="Correct target error")
- docsOcticon: bpy.props.StringProperty(default="None")
-
-
-classes = (
- BFU_OT_UnrealPotentialError,
-)
-
-
-def register():
- for cls in classes:
- bpy.utils.register_class(cls)
-
- bpy.types.Scene.potentialErrorList = bpy.props.CollectionProperty(type=BFU_OT_UnrealPotentialError)
-
-
-def unregister():
- for cls in reversed(classes):
- bpy.utils.unregister_class(cls)
-
- del bpy.types.Scene.potentialErrorList
diff --git a/blender-for-unrealengine/bfu_check_potential_error/__init__.py b/blender-for-unrealengine/bfu_check_potential_error/__init__.py
new file mode 100644
index 00000000..8291b872
--- /dev/null
+++ b/blender-for-unrealengine/bfu_check_potential_error/__init__.py
@@ -0,0 +1,52 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_check_props
+from . import bfu_check_operators
+from . import bfu_check_ui
+from . import bfu_check_utils
+
+if "bfu_check_props" in locals():
+ importlib.reload(bfu_check_props)
+if "bfu_check_operators" in locals():
+ importlib.reload(bfu_check_operators)
+if "bfu_check_ui" in locals():
+ importlib.reload(bfu_check_ui)
+if "bfu_check_utils" in locals():
+ importlib.reload(bfu_check_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_check_props.register()
+ bfu_check_operators.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_check_operators.unregister()
+ bfu_check_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_check_potential_error/bfu_check_operators.py b/blender-for-unrealengine/bfu_check_potential_error/bfu_check_operators.py
new file mode 100644
index 00000000..ac7b9de4
--- /dev/null
+++ b/blender-for-unrealengine/bfu_check_potential_error/bfu_check_operators.py
@@ -0,0 +1,278 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_cached_asset_list
+from .. import bfu_check_potential_error
+
+
+class BFU_OT_ShowAssetToExport(bpy.types.Operator):
+ bl_label = "Show asset(s)"
+ bl_idname = "object.showasset"
+ bl_description = "Click to show assets that are to be exported."
+
+ def execute(self, context):
+
+ obj = context.object
+ if obj:
+ if obj.type == "ARMATURE":
+ animation_asset_cache = bfu_cached_asset_list.GetAnimationAssetCache(obj)
+ animation_asset_cache.UpdateActionCache()
+
+
+ final_asset_cache = bfu_cached_asset_list.GetfinalAssetCache()
+ final_asset_list_to_export = final_asset_cache.GetFinalAssetList()
+ popup_title = "Assets list"
+ if len(final_asset_list_to_export) > 0:
+ popup_title = str(len(final_asset_list_to_export))+' asset(s) will be exported.'
+ else:
+ popup_title = 'No exportable assets were found.'
+
+ def draw(self, context: bpy.types.Context):
+ col = self.layout.column()
+ for asset in final_asset_list_to_export:
+ asset :bfu_cached_asset_list.AssetToExport
+ row = col.row()
+ if asset.obj is not None:
+ if asset.action is not None:
+ if (type(asset.action) is bpy.types.Action):
+ # Action name
+ action = asset.action.name
+ elif (type(asset.action) is bpy.types.AnimData):
+ # Nonlinear name
+ action = asset.obj.bfu_anim_nla_export_name
+ else:
+ action = "..."
+ row.label(
+ text="- ["+asset.name+"] --> " +
+ action+" ("+asset.asset_type+")")
+ else:
+ if asset.asset_type != "Collection StaticMesh":
+ row.label(
+ text="- "+asset.name +
+ " ("+asset.asset_type+")")
+ else:
+ row.label(
+ text="- "+asset.obj.name +
+ " ("+asset.asset_type+")")
+
+ else:
+ row.label(text="- ("+asset.asset_type+")")
+ bpy.context.window_manager.popup_menu(
+ draw,
+ title=popup_title,
+ icon='PACKAGE')
+ return {'FINISHED'}
+
+class BFU_OT_CheckPotentialErrorPopup(bpy.types.Operator):
+ bl_label = "Check Potential Errors"
+ bl_idname = "object.checkpotentialerror"
+ bl_description = "Check potential errors."
+ text = "none"
+
+ def execute(self, context):
+ fix_info = bfu_check_potential_error.bfu_check_utils.process_general_fix()
+ invoke_info = ""
+ for x, fix_info_key in enumerate(fix_info):
+ fix_info_data = fix_info[fix_info_key]
+ invoke_info += fix_info_key + ": " + str(fix_info_data)
+ if x < len(fix_info)-1:
+ invoke_info += "\n"
+
+
+ bfu_check_potential_error.bfu_check_utils.update_unreal_potential_error()
+ bpy.ops.object.openpotentialerror(
+ "INVOKE_DEFAULT",
+ invoke_info=invoke_info,
+ )
+ return {'FINISHED'}
+
+class BFU_OT_OpenPotentialErrorPopup(bpy.types.Operator):
+ bl_label = "Open potential errors"
+ bl_idname = "object.openpotentialerror"
+ bl_description = "Open potential errors"
+ invoke_info: bpy.props.StringProperty(default="...")
+
+ class BFU_OT_FixitTarget(bpy.types.Operator):
+ bl_label = "Fix it !"
+ bl_idname = "object.fixit_objet"
+ bl_description = "Correct target error"
+ errorIndex: bpy.props.IntProperty(default=-1)
+
+ def execute(self, context):
+ result = bfu_check_potential_error.bfu_check_utils.TryToCorrectPotentialError(self.errorIndex)
+ self.report({'INFO'}, result)
+ return {'FINISHED'}
+
+ class BFU_OT_SelectObjectButton(bpy.types.Operator):
+ bl_label = "Select(Object)"
+ bl_idname = "object.select_error_objet"
+ bl_description = "Select target Object."
+ errorIndex: bpy.props.IntProperty(default=-1)
+
+ def execute(self, context):
+ bfu_check_potential_error.bfu_check_utils.select_potential_error_object(self.errorIndex)
+ return {'FINISHED'}
+
+ class BFU_OT_SelectVertexButton(bpy.types.Operator):
+ bl_label = "Select(Vertex)"
+ bl_idname = "object.select_error_vertex"
+ bl_description = "Select target Vertex."
+ errorIndex: bpy.props.IntProperty(default=-1)
+
+ def execute(self, context):
+ bfu_check_potential_error.bfu_check_utils.SelectPotentialErrorVertex(self.errorIndex)
+ return {'FINISHED'}
+
+ class BFU_OT_SelectPoseBoneButton(bpy.types.Operator):
+ bl_label = "Select(PoseBone)"
+ bl_idname = "object.select_error_posebone"
+ bl_description = "Select target Pose Bone."
+ errorIndex: bpy.props.IntProperty(default=-1)
+
+ def execute(self, context):
+ bfu_check_potential_error.bfu_check_utils.SelectPotentialErrorPoseBone(self.errorIndex)
+ return {'FINISHED'}
+
+ class BFU_OT_OpenPotentialErrorDocs(bpy.types.Operator):
+ bl_label = "Open docs"
+ bl_idname = "object.open_potential_error_docs"
+ bl_description = "Open potential error docs."
+ octicon: bpy.props.StringProperty(default="")
+
+ def execute(self, context):
+ os.system(
+ "start \"\" " +
+ "https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/How-avoid-potential-errors" +
+ "#"+self.octicon)
+ return {'FINISHED'}
+
+ def execute(self, context):
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ wm = context.window_manager
+ return wm.invoke_popup(self, width=1020)
+
+ def check(self, context):
+ return True
+
+ def draw(self, context: bpy.types.Context):
+
+ layout = self.layout
+ if len(bpy.context.scene.potentialErrorList) > 0:
+ popup_title = (
+ str(len(bpy.context.scene.potentialErrorList)) +
+ " potential error(s) found!")
+ else:
+ popup_title = "No potential error to correct!"
+
+
+ layout.label(text=popup_title)
+ invoke_info_lines = self.invoke_info.split("\n")
+ for invoke_info_line in invoke_info_lines:
+ layout.label(text="- "+invoke_info_line)
+
+ layout.separator()
+ row = layout.row()
+ col = row.column()
+ for x in range(len(bpy.context.scene.potentialErrorList)):
+ error = bpy.context.scene.potentialErrorList[x]
+
+ myLine = col.box().split(factor=0.85)
+ # ----
+ if error.type == 0:
+ msgType = 'INFO'
+ msgIcon = 'INFO'
+ elif error.type == 1:
+ msgType = 'WARNING'
+ msgIcon = 'ERROR'
+ elif error.type == 2:
+ msgType = 'ERROR'
+ msgIcon = 'CANCEL'
+ # ----
+
+ # Text
+ TextLine = myLine.column()
+ errorFullMsg = msgType+": "+error.text
+ splitedText = errorFullMsg.split("\n")
+
+ for text, Line in enumerate(splitedText):
+ if (text < 1):
+
+ FisrtTextLine = TextLine.row()
+ if (error.docsOcticon != "None"): # Doc button
+ props = FisrtTextLine.operator(
+ "object.open_potential_error_docs",
+ icon="HELP",
+ text="")
+ props.octicon = error.docsOcticon
+
+ FisrtTextLine.label(text=Line, icon=msgIcon)
+ else:
+ TextLine.label(text=Line)
+
+ # Select and fix button
+ ButtonLine = myLine.column()
+ if (error.correctRef != "None"):
+ props = ButtonLine.operator(
+ "object.fixit_objet",
+ text=error.correctlabel)
+ props.errorIndex = x
+ if (error.object is not None):
+ if (error.selectObjectButton):
+ props = ButtonLine.operator(
+ "object.select_error_objet")
+ props.errorIndex = x
+ if (error.selectVertexButton):
+ props = ButtonLine.operator(
+ "object.select_error_vertex")
+ props.errorIndex = x
+ if (error.selectPoseBoneButton):
+ props = ButtonLine.operator(
+ "object.select_error_posebone")
+ props.errorIndex = x
+
+
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_OT_ShowAssetToExport,
+ BFU_OT_CheckPotentialErrorPopup,
+ BFU_OT_OpenPotentialErrorPopup,
+ BFU_OT_OpenPotentialErrorPopup.BFU_OT_FixitTarget,
+ BFU_OT_OpenPotentialErrorPopup.BFU_OT_SelectObjectButton,
+ BFU_OT_OpenPotentialErrorPopup.BFU_OT_SelectVertexButton,
+ BFU_OT_OpenPotentialErrorPopup.BFU_OT_SelectPoseBoneButton,
+ BFU_OT_OpenPotentialErrorPopup.BFU_OT_OpenPotentialErrorDocs,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
diff --git a/blender-for-unrealengine/bfu_check_potential_error/bfu_check_props.py b/blender-for-unrealengine/bfu_check_potential_error/bfu_check_props.py
new file mode 100644
index 00000000..7aafb916
--- /dev/null
+++ b/blender-for-unrealengine/bfu_check_potential_error/bfu_check_props.py
@@ -0,0 +1,67 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from . import bfu_check_operators
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ ]
+ return preset_values
+
+class BFU_OT_UnrealPotentialError(bpy.types.PropertyGroup):
+ type: bpy.props.IntProperty(default=0) # 0:Info, 1:Warning, 2:Error
+ object: bpy.props.PointerProperty(type=bpy.types.Object)
+ ###
+ selectObjectButton: bpy.props.BoolProperty(default=True)
+ selectVertexButton: bpy.props.BoolProperty(default=False)
+ selectPoseBoneButton: bpy.props.BoolProperty(default=False)
+ ###
+ selectOption: bpy.props.StringProperty(default="None") # 0:VertexWithZeroWeight
+ itemName: bpy.props.StringProperty(default="None")
+ text: bpy.props.StringProperty(default="Unknown")
+ correctRef: bpy.props.StringProperty(default="None")
+ correctlabel: bpy.props.StringProperty(default="Fix it !")
+ correctDesc: bpy.props.StringProperty(default="Correct target error")
+ docsOcticon: bpy.props.StringProperty(default="None")
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_OT_UnrealPotentialError,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.potentialErrorList = bpy.props.CollectionProperty(type=BFU_OT_UnrealPotentialError)
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Scene.potentialErrorList
diff --git a/blender-for-unrealengine/bfu_check_potential_error/bfu_check_ui.py b/blender-for-unrealengine/bfu_check_potential_error/bfu_check_ui.py
new file mode 100644
index 00000000..22370baa
--- /dev/null
+++ b/blender-for-unrealengine/bfu_check_potential_error/bfu_check_ui.py
@@ -0,0 +1,25 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+
+
+
+def draw_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ pass
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_check_potential_error/bfu_check_utils.py b/blender-for-unrealengine/bfu_check_potential_error/bfu_check_utils.py
new file mode 100644
index 00000000..dec12f8d
--- /dev/null
+++ b/blender-for-unrealengine/bfu_check_potential_error/bfu_check_utils.py
@@ -0,0 +1,694 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+import fnmatch
+import math
+
+from .. import bbpl
+from .. import bfu_basics
+from .. import bfu_assets_manager
+from .. import bfu_utils
+from .. import bfu_cached_asset_list
+
+from .. import bfu_collision
+from .. import bfu_socket
+from .. import bfu_camera
+from .. import bfu_alembic_animation
+from .. import bfu_groom
+from .. import bfu_spline
+from .. import bfu_skeletal_mesh
+from .. import bfu_static_mesh
+
+
+
+
+def process_general_fix():
+ fixed_collisions = bfu_collision.bfu_collision_utils.fix_export_type_on_collision()
+ fixed_collision_names = bfu_collision.bfu_collision_utils.fix_name_on_collision()
+ fixed_sockets = bfu_socket.bfu_socket_utils.fix_export_type_on_socket()
+ fixed_socket_names = bfu_socket.bfu_socket_utils.fix_name_on_socket()
+
+ fix_info = {
+ "Fixed Collision(s)": fixed_collisions,
+ "Fixed Collision Names(s)": fixed_collision_names,
+ "Fixed Socket(s)": fixed_sockets,
+ "Fixed Socket Names(s)": fixed_socket_names,
+ }
+
+ return fix_info
+
+
+
+
+def GetVertexWithZeroWeight(Armature, Mesh):
+ vertices = []
+
+ # Créez un ensemble des noms des os de l'armature pour une recherche plus rapide
+ armature_bone_names = set(bone.name for bone in Armature.data.bones)
+
+
+ for vertex in Mesh.data.vertices: #MeshVertex(bpy_struct)
+ cumulateWeight = 0
+
+ if vertex.groups:
+ for group_elem in vertex.groups: #VertexGroupElement(bpy_struct)
+ if group_elem.weight > 0:
+ group_index = group_elem.group
+ group_len = len(Mesh.vertex_groups)
+ if group_index <= group_len:
+ group = Mesh.vertex_groups[group_elem.group]
+
+ # Utilisez l'ensemble des noms d'os pour vérifier l'appartenance à l'armature
+ if group.name in armature_bone_names:
+ cumulateWeight += group_elem.weight
+
+ if cumulateWeight == 0:
+ vertices.append(vertex)
+
+ return vertices
+
+
+def ContainsArmatureModifier(obj):
+ for mod in obj.modifiers:
+ if mod.type == "ARMATURE":
+ return True
+ return False
+
+def GetSkeletonMeshs(obj):
+ meshs = []
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ childs = bfu_utils.GetExportDesiredChilds(obj)
+ for child in childs:
+ if child.type == "MESH":
+ meshs.append(child)
+ return meshs
+
+
+def update_unreal_potential_error():
+ # Find and reset list of all potential error in scene
+
+ addon_prefs = bfu_basics.GetAddonPrefs()
+ potential_errors = bpy.context.scene.potentialErrorList
+ potential_errors.clear()
+
+ # prepares the data to avoid unnecessary loops
+ obj_to_check = []
+ final_asset_cache = bfu_cached_asset_list.GetfinalAssetCache()
+ final_asset_list_to_export = final_asset_cache.GetFinalAssetList()
+ for Asset in final_asset_list_to_export:
+ if Asset.obj in bfu_utils.GetAllobjectsByExportType("export_recursive"):
+ if Asset.obj not in obj_to_check:
+ obj_to_check.append(Asset.obj)
+ for child in bfu_utils.GetExportDesiredChilds(Asset.obj):
+ if child not in obj_to_check:
+ obj_to_check.append(child)
+
+ mesh_type_to_check = []
+ for obj in obj_to_check:
+ if obj.type == 'MESH':
+ mesh_type_to_check.append(obj)
+
+ mesh_type_without_col = [] # is Mesh Type To Check Without Collision
+ for obj in mesh_type_to_check:
+ if not bfu_utils.CheckIsCollision(obj):
+ mesh_type_without_col.append(obj)
+
+ def check_unit_scale():
+ # Check if the unit scale is equal to 0.01.
+ if addon_prefs.notifyUnitScalePotentialError:
+ if not bfu_utils.get_scene_unit_scale_is_close(0.01):
+ str_unit_scale = str(bfu_utils.get_scene_unit_scale())
+ my_po_error = potential_errors.add()
+ my_po_error.name = bpy.context.scene.name
+ my_po_error.type = 1
+ my_po_error.text = (f'Scene "{bpy.context.scene.name}" has a Unit Scale equal to {str_unit_scale}.')
+ my_po_error.text += ('\nFor Unreal, a unit scale equal to 0.01 is recommended.')
+ my_po_error.text += ('\n(You can disable this potential error in the addon preferences.)')
+ my_po_error.object = None
+ my_po_error.correctRef = "SetUnrealUnit"
+ my_po_error.correctlabel = 'Set Unreal Unit'
+
+ def check_scene_frame_rate():
+ # Check Scene Frame Rate.
+ scene = bpy.context.scene
+ denominator = scene.render.fps_base
+ numerator = scene.render.fps
+
+ # Ensure denominator and numerator are at least 1 and int 32
+ new_denominator = max(round(denominator), 1)
+ new_numerator = max(round(numerator), 1)
+
+ if denominator != new_denominator or numerator != new_numerator:
+ message = ('Frame rate denominator and numerator must be an int32 over zero.\n'
+ 'Float denominator and numerator is not supported in Unreal Engine Sequencer.\n'
+ f'- Denominator: {denominator} -> {new_denominator}\n'
+ f'- Numerator: {numerator} -> {new_numerator}')
+
+ my_po_error = potential_errors.add()
+ my_po_error.name = bpy.context.scene.name
+ my_po_error.type = 2
+ my_po_error.text = (message)
+ my_po_error.docsOcticon = 'scene-frame-rate'
+
+
+ def check_obj_type():
+ # Check if objects use a non-recommended type
+
+ non_recommended_types = {"SURFACE", "META", "FONT"}
+ for obj in obj_to_check:
+ if obj.type in non_recommended_types:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 1
+ my_po_error.text = (
+ f'Object "{obj.name}" is a {obj.type}. The object of the type '
+ 'SURFACE, META, and FONT is not recommended.'
+ )
+ my_po_error.object = obj
+ my_po_error.correctRef = "ConvertToMesh"
+ my_po_error.correctlabel = 'Convert to mesh'
+
+ def check_shape_keys():
+ destructive_modifiers = {"ARMATURE"}
+
+ for obj in mesh_type_to_check:
+ shape_keys = obj.data.shape_keys
+ if shape_keys is not None and len(shape_keys.key_blocks) > 0:
+ # Check that no modifiers is destructive for the key shapes
+ for modif in obj.modifiers:
+ if modif.type in destructive_modifiers:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 2
+ my_po_error.object = obj
+ my_po_error.itemName = modif.name
+ my_po_error.text = (
+ f'In object "{obj.name}", the modifier "{modif.type}" '
+ f'named "{modif.name}" can destroy shape keys. '
+ 'Please use only the Armature modifier with shape keys.'
+ )
+ my_po_error.correctRef = "RemoveModifier"
+ my_po_error.correctlabel = 'Remove modifier'
+
+ # Check shape key ranges for Unreal Engine compatibility
+ unreal_engine_shape_key_max = 5
+ unreal_engine_shape_key_min = -5
+ for key in shape_keys.key_blocks:
+ # Min check
+ if key.slider_min < unreal_engine_shape_key_min:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 1
+ my_po_error.object = obj
+ my_po_error.itemName = key.name
+ my_po_error.text = (
+ f'In object "{obj.name}", the shape key "{key.name}" '
+ 'is out of bounds for Unreal. The minimum range must not be less than {unreal_engine_shape_key_min}.'
+ )
+ my_po_error.correctRef = "SetKeyRangeMin"
+ my_po_error.correctlabel = f'Set min range to {unreal_engine_shape_key_min}'
+
+ # Max check
+ if key.slider_max > unreal_engine_shape_key_max:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 1
+ my_po_error.object = obj
+ my_po_error.itemName = key.name
+ my_po_error.text = (
+ f'In object "{obj.name}", the shape key "{key.name}" '
+ 'is out of bounds for Unreal. The maximum range must not exceed {unreal_engine_shape_key_max}.'
+ )
+ my_po_error.correctRef = "SetKeyRangeMax"
+ my_po_error.correctlabel = f'Set max range to {unreal_engine_shape_key_max}'
+
+ def check_uv_maps():
+ # Check that the objects have at least one UV map valid
+ for obj in mesh_type_without_col:
+ if len(obj.data.uv_layers) < 1:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 1
+ my_po_error.text = (f'Object "{obj.name}" does not have any UV Layer.')
+ my_po_error.object = obj
+ my_po_error.correctRef = "CreateUV"
+ my_po_error.correctlabel = 'Create Smart UV Project'
+
+ def check_bad_static_mesh_exported_like_skeletal_mesh():
+ # Check if the correct object is defined as exportable
+ for obj in mesh_type_to_check:
+ for modif in obj.modifiers:
+ if modif.type == "ARMATURE" and obj.bfu_export_type == "export_recursive":
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 1
+ my_po_error.text = (
+ f'In object "{obj.name}", the modifier "{modif.type}" '
+ f'named "{modif.name}" will not be applied when exported '
+ 'with StaticMesh assets.\nNote: with armature, if you want to export '
+ 'objects as skeletal mesh, you need to set only the armature as '
+ 'export_recursive, not the child objects.'
+ )
+ my_po_error.object = obj
+
+ def check_armature_scale():
+ # Check if the ARMATURE use the same value on all scale axes
+ for obj in obj_to_check:
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ if obj.scale.z != obj.scale.y or obj.scale.z != obj.scale.x:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'In object "{obj.name}", the scale values are not consistent across all axes.'
+ )
+ my_po_error.text += (
+ f'\nScale x: {obj.scale.x}, y: {obj.scale.y}, z: {obj.scale.z}'
+ )
+ my_po_error.object = obj
+
+ def check_armature_number():
+ # Check if the number of ARMATURE modifiers or constraints is exactly 1
+ for obj in obj_to_check:
+ meshs = GetSkeletonMeshs(obj)
+ for mesh in meshs:
+ # Count the number of ARMATURE modifiers and constraints
+ armature_modifiers = sum(1 for mod in mesh.modifiers if mod.type == "ARMATURE")
+ armature_constraints = sum(1 for const in mesh.constraints if const.type == "ARMATURE")
+
+ # Check if the total number of ARMATURE modifiers and constraints is greater than 1
+ if armature_modifiers + armature_constraints > 1:
+ my_po_error = potential_errors.add()
+ my_po_error.name = mesh.name
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'In object "{mesh.name}", {armature_modifiers} Armature modifier(s) and '
+ f'{armature_constraints} Armature constraint(s) were found. '
+ 'Please use only one Armature modifier or one Armature constraint.'
+ )
+ my_po_error.object = mesh
+
+ # Check if no ARMATURE modifiers or constraints are found
+ if armature_modifiers + armature_constraints == 0:
+ my_po_error = potential_errors.add()
+ my_po_error.name = mesh.name
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'In object "{mesh.name}", no Armature modifiers or constraints were found. '
+ 'Please use one Armature modifier or one Armature constraint.'
+ )
+ my_po_error.object = mesh
+
+ def check_armature_mod_data():
+ # Check the parameters of ARMATURE modifiers
+ for obj in mesh_type_to_check:
+ for mod in obj.modifiers:
+ if mod.type == "ARMATURE":
+ if mod.use_deform_preserve_volume:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'In object "{obj.name}", the ARMATURE modifier '
+ f'named "{mod.name}" has the Preserve Volume parameter set to True. '
+ 'This parameter must be set to False.'
+ )
+ my_po_error.object = obj
+ my_po_error.itemName = mod.name
+ my_po_error.correctRef = "PreserveVolume"
+ my_po_error.correctlabel = 'Set Preserve Volume to False'
+
+ def check_armature_const_data():
+ # Check the parameters of ARMATURE constraints
+ for obj in mesh_type_to_check:
+ for const in obj.constraints:
+ if const.type == "ARMATURE":
+ # TO DO.
+ pass
+
+ def check_armature_bone_data():
+ # Check the parameters of the ARMATURE bones
+ for obj in obj_to_check:
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ for bone in obj.data.bones:
+ if (not obj.bfu_export_deform_only or
+ (bone.use_deform and obj.bfu_export_deform_only)):
+
+ if bone.bbone_segments > 1:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 1
+ my_po_error.text = (
+ f'In object "{obj.name}", the bone named "{bone.name}" '
+ 'has the Bendy Bones / Segments parameter set to more than 1. '
+ 'This parameter must be set to 1.'
+ )
+ my_po_error.text += (
+ '\nBendy bones are not supported by Unreal Engine, '
+ 'so it is better to disable it if you want the same '
+ 'animation preview in Unreal and Blender.'
+ )
+ my_po_error.object = obj
+ my_po_error.itemName = bone.name
+ my_po_error.selectPoseBoneButton = True
+ my_po_error.correctRef = "BoneSegments"
+ my_po_error.correctlabel = 'Set Bone Segments to 1'
+ my_po_error.docsOcticon = 'bendy-bone'
+
+ def check_armature_valid_child():
+ # Check that the skeleton has at least one valid mesh child to export
+ for obj in obj_to_check:
+ export_as_proxy = bfu_utils.GetExportAsProxy(obj)
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ childs = bfu_utils.GetExportDesiredChilds(obj)
+ valid_child = sum(1 for child in childs if child.type == "MESH")
+
+ if export_as_proxy and bfu_utils.GetExportProxyChild(obj) is not None:
+ valid_child += 1
+
+ if valid_child < 1:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'Object "{obj.name}" is an Armature and does not have '
+ 'any valid children.'
+ )
+ my_po_error.object = obj
+
+ def check_armature_child_with_bone_parent():
+ # Check if a mesh child is parented to a bone, which will cause import issues
+ for obj in obj_to_check:
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ childs = bfu_utils.GetExportDesiredChilds(obj)
+ for child in childs:
+ if child.type == "MESH" and child.parent_type == 'BONE':
+ my_po_error = potential_errors.add()
+ my_po_error.name = child.name
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'Object "{child.name}" uses Parent Bone to parent. '
+ '\nIf you use Parent Bone to parent your mesh to your armature, the import will fail.'
+ )
+ my_po_error.object = child
+ my_po_error.docsOcticon = 'armature-child-with-bone-parent'
+
+ def check_armature_multiple_roots():
+ # Check if the skeleton has multiple root bones
+ for obj in obj_to_check:
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ root_bones = bfu_utils.get_armature_root_bones(obj)
+
+ root_bones_str = ""
+ for bone in root_bones:
+ if bone.use_deform:
+ root_bones_str += bone.name + "(def), "
+ else:
+ root_bones_str += bone.name + "(def child(s)), "
+
+ if len(root_bones) > 1:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 1
+ my_po_error.text = f'Object "{obj.name}" has multiple root bones. Unreal only supports a single root bone.'
+ my_po_error.text += '\n' + f' {len(root_bones)} root bone(s) found: {root_bones_str}'
+ my_po_error.text += '\n' + 'A custom root bone will be added at export.'
+ my_po_error.object = obj
+
+ def check_armature_no_deform_bone():
+ # Check that the skeleton has at least one deform bone
+ for obj in obj_to_check:
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ if obj.bfu_export_deform_only:
+ has_deform_bone = any(bone.use_deform for bone in obj.data.bones)
+ if not has_deform_bone:
+ my_po_error = potential_errors.add()
+ my_po_error.name = obj.name
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'Object "{obj.name}" does not have any deform bones. '
+ 'Unreal will import it as a StaticMesh.'
+ )
+ my_po_error.object = obj
+
+ def check_marker_overlay():
+ # Check that there is no overlap with the markers in the scene timeline
+ used_frames = []
+ for marker in bpy.context.scene.timeline_markers:
+ if marker.frame in used_frames:
+ my_po_error = potential_errors.add()
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'In the scene timeline, the frame "{marker.frame}" contains overlapping markers.'
+ '\nTo avoid camera conflicts in the generation of the sequencer, '
+ 'you must use a maximum of one marker per frame.'
+ )
+ else:
+ used_frames.append(marker.frame)
+
+ def check_vertex_group_weight():
+ # Check that all vertices have a weight
+ for obj in obj_to_check:
+ meshes = GetSkeletonMeshs(obj)
+ for mesh in meshes:
+ if mesh.type == "MESH" and ContainsArmatureModifier(mesh):
+ # Get vertices with zero weight
+ vertices_with_zero_weight = GetVertexWithZeroWeight(obj, mesh)
+ if vertices_with_zero_weight:
+ my_po_error = potential_errors.add()
+ my_po_error.name = mesh.name
+ my_po_error.type = 1
+ my_po_error.text = (
+ f'Object "{mesh.name}" contains {len(vertices_with_zero_weight)} '
+ 'vertices with zero cumulative valid weight.'
+ )
+ my_po_error.text += (
+ '\nNote: Vertex groups must have a bone with the same name to be valid.'
+ )
+ my_po_error.object = mesh
+ my_po_error.selectVertexButton = True
+ my_po_error.selectOption = "VertexWithZeroWeight"
+
+ def check_zero_scale_keyframe():
+ # Check that animations do not use an invalid scale value
+ for obj in obj_to_check:
+ if bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
+ animation_asset_cache = bfu_cached_asset_list.GetAnimationAssetCache(obj)
+ animations_to_export = animation_asset_cache.GetAnimationAssetList()
+ for action in animations_to_export:
+ for fcurve in action.fcurves:
+ if fcurve.data_path.split(".")[-1] == "scale":
+ for key in fcurve.keyframe_points:
+ x_curve, y_curve = key.co
+ if y_curve == 0:
+ bone_name = fcurve.data_path.split('"')[1]
+ my_po_error = potential_errors.add()
+ my_po_error.type = 2
+ my_po_error.text = (
+ f'In action "{action.name}" at frame {x_curve}, '
+ f'the bone named "{bone_name}" has a zero value in the scale '
+ 'transform. This is invalid in Unreal.'
+ )
+
+ check_unit_scale()
+ check_scene_frame_rate()
+ check_obj_type()
+ check_shape_keys()
+ check_uv_maps()
+ check_bad_static_mesh_exported_like_skeletal_mesh()
+ check_armature_scale()
+ check_armature_number()
+ check_armature_mod_data()
+ check_armature_const_data()
+ check_armature_bone_data()
+ check_armature_valid_child()
+ check_armature_multiple_roots()
+ check_armature_child_with_bone_parent()
+ check_armature_no_deform_bone()
+ check_marker_overlay()
+ check_vertex_group_weight()
+ check_zero_scale_keyframe()
+
+ return potential_errors
+
+
+def select_potential_error_object(errorIndex):
+ # Select potential error
+
+ bbpl.utils.safe_mode_set('OBJECT', bpy.context.active_object)
+ scene = bpy.context.scene
+ error = scene.potentialErrorList[errorIndex]
+ obj = error.object
+
+ bpy.ops.object.select_all(action='DESELECT')
+ obj.hide_viewport = False
+ obj.hide_set(False)
+ obj.select_set(True)
+ bpy.context.view_layer.objects.active = obj
+
+ # show collection for select object
+ for collection in bpy.data.collections:
+ for ColObj in collection.objects:
+ if ColObj == obj:
+ bfu_basics.SetCollectionUse(collection)
+ bpy.ops.view3d.view_selected()
+ return obj
+
+
+def SelectPotentialErrorVertex(errorIndex):
+ # Select potential error
+ select_potential_error_object(errorIndex)
+ bbpl.utils.safe_mode_set('EDIT')
+
+ scene = bpy.context.scene
+ error = scene.potentialErrorList[errorIndex]
+ obj = error.object
+ bpy.ops.mesh.select_mode(type="VERT")
+ bpy.ops.mesh.select_all(action='DESELECT')
+
+ bbpl.utils.safe_mode_set('OBJECT')
+ if error.selectOption == "VertexWithZeroWeight":
+ for vertex in GetVertexWithZeroWeight(obj.parent, obj):
+ vertex.select = True
+ bbpl.utils.safe_mode_set('EDIT')
+ bpy.ops.view3d.view_selected()
+ return obj
+
+
+def SelectPotentialErrorPoseBone(errorIndex):
+ # Select potential error
+ select_potential_error_object(errorIndex)
+ bbpl.utils.safe_mode_set('POSE')
+
+ scene = bpy.context.scene
+ error = scene.potentialErrorList[errorIndex]
+ obj = error.object
+ bone = obj.data.bones[error.itemName]
+
+ # Make bone visible if hide in a layer
+ for x, layer in enumerate(bone.layers):
+ if not obj.data.layers[x] and layer:
+ obj.data.layers[x] = True
+
+ bpy.ops.pose.select_all(action='DESELECT')
+ obj.data.bones.active = bone
+ bone.select = True
+
+ bpy.ops.view3d.view_selected()
+ return obj
+
+
+def TryToCorrectPotentialError(errorIndex):
+ # Try to correct potential error
+
+ scene = bpy.context.scene
+ error = scene.potentialErrorList[errorIndex]
+ global successCorrect
+ successCorrect = False
+
+ local_view_areas = bbpl.scene_utils.move_to_global_view()
+
+ MyCurrentDataSave = bbpl.save_data.scene_save.UserSceneSave()
+ MyCurrentDataSave.save_current_scene()
+
+ bbpl.utils.safe_mode_set('OBJECT', MyCurrentDataSave.user_select_class.user_active)
+
+ print("Start correct")
+
+ def SelectObj(obj):
+ bpy.ops.object.select_all(action='DESELECT')
+ obj.select_set(True)
+ bpy.context.view_layer.objects.active = obj
+
+ # Correction list
+
+ if error.correctRef == "SetUnrealUnit":
+ bpy.context.scene.unit_settings.scale_length = 0.01
+ successCorrect = True
+
+ if error.correctRef == "ConvertToMesh":
+ obj = error.object
+ SelectObj(obj)
+ bpy.ops.object.convert(target='MESH')
+ successCorrect = True
+
+ if error.correctRef == "SetKeyRangeMin":
+ obj = error.object
+ key = obj.data.shape_keys.key_blocks[error.itemName]
+ key.slider_min = -5
+ successCorrect = True
+
+ if error.correctRef == "SetKeyRangeMax":
+ obj = error.object
+ key = obj.data.shape_keys.key_blocks[error.itemName]
+ key.slider_max = 5
+ successCorrect = True
+
+ if error.correctRef == "CreateUV":
+ obj = error.object
+ SelectObj(obj)
+ if bbpl.utils.safe_mode_set("EDIT", obj):
+ bpy.ops.uv.smart_project()
+ successCorrect = True
+ else:
+ successCorrect = False
+
+ if error.correctRef == "RemoveModfier":
+ obj = error.object
+ mod = obj.modifiers[error.itemName]
+ obj.modifiers.remove(mod)
+ successCorrect = True
+
+ if error.correctRef == "PreserveVolume":
+ obj = error.object
+ mod = obj.modifiers[error.itemName]
+ mod.use_deform_preserve_volume = False
+ successCorrect = True
+
+ if error.correctRef == "BoneSegments":
+ obj = error.object
+ bone = obj.data.bones[error.itemName]
+ bone.bbone_segments = 1
+ successCorrect = True
+
+ if error.correctRef == "InheritScale":
+ obj = error.object
+ bone = obj.data.bones[error.itemName]
+ bone.use_inherit_scale = True
+ successCorrect = True
+
+ # ----------------------------------------Reset data
+ MyCurrentDataSave.reset_select(use_names = True)
+ MyCurrentDataSave.reset_scene_at_save()
+ bbpl.scene_utils.move_to_local_view(local_view_areas)
+
+ # ----------------------------------------
+
+ if successCorrect:
+ scene.potentialErrorList.remove(errorIndex)
+ print("end correct, Error: " + error.correctRef)
+ return "Corrected"
+ print("end correct, Error not found")
+ return "Correct fail"
+
+
+
+
+
diff --git a/blender-for-unrealengine/bfu_collision/__init__.py b/blender-for-unrealengine/bfu_collision/__init__.py
index 5cbfbcc8..5d2cc497 100644
--- a/blender-for-unrealengine/bfu_collision/__init__.py
+++ b/blender-for-unrealengine/bfu_collision/__init__.py
@@ -19,11 +19,17 @@
import bpy
import importlib
-from . import bfu_collision_ui_and_props
+from . import bfu_collision_props
+from . import bfu_collision_types
+from . import bfu_collision_ui
from . import bfu_collision_utils
-if "bfu_collision_ui_and_props" in locals():
- importlib.reload(bfu_collision_ui_and_props)
+if "bfu_collision_types" in locals():
+ importlib.reload(bfu_collision_types)
+if "bfu_collision_props" in locals():
+ importlib.reload(bfu_collision_props)
+if "bfu_collision_ui" in locals():
+ importlib.reload(bfu_collision_ui)
if "bfu_collision_utils" in locals():
importlib.reload(bfu_collision_utils)
@@ -35,10 +41,12 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
- bfu_collision_ui_and_props.register()
+ bfu_collision_types.register()
+ bfu_collision_props.register()
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
- bfu_collision_ui_and_props.unregister()
\ No newline at end of file
+ bfu_collision_props.unregister()
+ bfu_collision_types.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_collision/bfu_collision_props.py b/blender-for-unrealengine/bfu_collision/bfu_collision_props.py
new file mode 100644
index 00000000..6a733b7f
--- /dev/null
+++ b/blender-for-unrealengine/bfu_collision/bfu_collision_props.py
@@ -0,0 +1,129 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from . import bfu_collision_utils
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_create_physics_asset',
+ 'obj.bfu_auto_generate_collision',
+ 'obj.bfu_collision_trace_flag',
+ 'obj.bfu_enable_skeletal_mesh_per_poly_collision',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_object_collision_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Collision")
+ bpy.types.Scene.bfu_tools_collision_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Collision")
+
+ # ImportUI
+ # https://api.unrealengine.com/INT/API/Editor/UnrealEd/Factories/UFbxImportUI/index.html
+
+ bpy.types.Object.bfu_create_physics_asset = bpy.props.BoolProperty(
+ name="Create PhysicsAsset",
+ description="If checked, create a PhysicsAsset when is imported",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True
+ )
+
+
+ bpy.types.Object.bfu_auto_generate_collision = bpy.props.BoolProperty(
+ name="Auto Generate Collision",
+ description=(
+ "If checked, collision will automatically be generated" +
+ " (ignored if custom collision is imported or used)."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True,
+ )
+
+
+ bpy.types.Object.bfu_collision_trace_flag = bpy.props.EnumProperty(
+ name="Collision Complexity",
+ description="Collision Trace Flag",
+ override={'LIBRARY_OVERRIDABLE'},
+ # Vania python
+ # https://docs.unrealengine.com/en-US/PythonAPI/class/CollisionTraceFlag.html
+ # C++ API
+ # https://api.unrealengine.com/INT/API/Runtime/Engine/PhysicsEngine/ECollisionTraceFlag/index.html
+ items=[
+ ("CTF_UseDefault",
+ "Project Default",
+ "Create only complex shapes (per poly)." +
+ " Use complex shapes for all scene queries" +
+ " and collision tests." +
+ " Can be used in simulation for" +
+ " static shapes only" +
+ " (i.e can be collided against but not moved" +
+ " through forces or velocity.",
+ 1),
+ ("CTF_UseSimpleAndComplex",
+ "Use Simple And Complex",
+ "Use project physics settings (DefaultShapeComplexity)",
+ 2),
+ ("CTF_UseSimpleAsComplex",
+ "Use Simple as Complex",
+ "Create both simple and complex shapes." +
+ " Simple shapes are used for regular scene queries" +
+ " and collision tests. Complex shape (per poly)" +
+ " is used for complex scene queries.",
+ 3),
+ ("CTF_UseComplexAsSimple",
+ "Use Complex as Simple",
+ "Create only simple shapes." +
+ " Use simple shapes for all scene" +
+ " queries and collision tests.",
+ 4)
+ ]
+ )
+
+ bpy.types.Object.bfu_enable_skeletal_mesh_per_poly_collision = bpy.props.BoolProperty(
+ name="Enable Per-Poly Collision",
+ description="Enable per-polygon collision for Skeletal Mesh",
+ default=False
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_enable_skeletal_mesh_per_poly_collision
+
+ del bpy.types.Object.bfu_collision_trace_flag
+ del bpy.types.Object.bfu_auto_generate_collision
+ del bpy.types.Object.bfu_create_physics_asset
+
+ del bpy.types.Scene.bfu_tools_collision_properties_expanded
+ del bpy.types.Scene.bfu_object_collision_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_collision/bfu_collision_ui_and_props.py b/blender-for-unrealengine/bfu_collision/bfu_collision_types.py
similarity index 72%
rename from blender-for-unrealengine/bfu_collision/bfu_collision_ui_and_props.py
rename to blender-for-unrealengine/bfu_collision/bfu_collision_types.py
index 5455b8a4..6cf2c623 100644
--- a/blender-for-unrealengine/bfu_collision/bfu_collision_ui_and_props.py
+++ b/blender-for-unrealengine/bfu_collision/bfu_collision_types.py
@@ -24,7 +24,6 @@
from .. import bfu_ui
from .. import bbpl
-#@TODO Move in bfu_collision_types.py
class BFU_OT_ConvertToCollisionButtonBox(bpy.types.Operator):
bl_label = "Convert to box (UBX)"
bl_idname = "object.converttoboxcollision"
@@ -47,7 +46,6 @@ def execute(self, context):
" (Active object is the owner of the collision)")
return {'FINISHED'}
-#@TODO Move in bfu_collision_types.py
class BFU_OT_ConvertToCollisionButtonCapsule(bpy.types.Operator):
bl_label = "Convert to capsule (UCP)"
bl_idname = "object.converttocapsulecollision"
@@ -70,7 +68,6 @@ def execute(self, context):
" (Active object is the owner of the collision)")
return {'FINISHED'}
-#@TODO Move in bfu_collision_types.py
class BFU_OT_ConvertToCollisionButtonSphere(bpy.types.Operator):
bl_label = "Convert to sphere (USP)"
bl_idname = "object.converttospherecollision"
@@ -93,7 +90,6 @@ def execute(self, context):
" (Active object is the owner of the collision)")
return {'FINISHED'}
-#@TODO Move in bfu_collision_types.py
class BFU_OT_ConvertToCollisionButtonConvex(bpy.types.Operator):
bl_label = "Convert to convex shape (UCX)"
bl_idname = "object.converttoconvexcollision"
@@ -116,7 +112,6 @@ def execute(self, context):
" (Active object is the owner of the collision)")
return {'FINISHED'}
-#@TODO Move in bfu_collision_types.py
class BFU_OT_ToggleCollisionVisibility(bpy.types.Operator):
bl_label = "Toggle Collision Visibility"
bl_idname = "object.toggle_collision_visibility"
@@ -132,40 +127,6 @@ def execute(self, context):
return {'FINISHED'}
-def draw_ui_scene_collision(layout: bpy.types.UILayout):
- #@TODO Move in bfu_collision_ui.py
- scene = bpy.context.scene
- scene.bfu_collision_expanded.draw(layout)
- if scene.bfu_collision_expanded.is_expend():
-
- # Draw user tips and check can use buttons
- ready_for_convert_collider = False
- if not bbpl.utils.active_mode_is("OBJECT"):
- layout.label(text="Switch to Object Mode.", icon='INFO')
- else:
- if bbpl.utils.found_type_in_selection("MESH", False):
- if bbpl.utils.active_type_is_not("ARMATURE") and len(bpy.context.selected_objects) > 1:
- layout.label(text="Click on button for convert to collider.", icon='INFO')
- ready_for_convert_collider = True
- else:
- layout.label(text="Select with [SHIFT] the collider owner.", icon='INFO')
- else:
- layout.label(text="Please select your collider Object(s). Active should be the owner.", icon='INFO')
-
- # Draw buttons
- convertButtons = layout.row().split(factor=0.80)
- convertStaticCollisionButtons = convertButtons.column()
- convertStaticCollisionButtons.enabled = ready_for_convert_collider
- convertStaticCollisionButtons.operator("object.converttoboxcollision", icon='MESH_CUBE')
- convertStaticCollisionButtons.operator("object.converttoconvexcollision", icon='MESH_ICOSPHERE')
- convertStaticCollisionButtons.operator("object.converttocapsulecollision", icon='MESH_CAPSULE')
- convertStaticCollisionButtons.operator("object.converttospherecollision", icon='MESH_UVSPHERE')
- layout.operator("object.toggle_collision_visibility", text="Toggle Collision Visibility", icon='HIDE_OFF')
-
-def draw_ui_object_collision(layout: bpy.types.UILayout):
- #@TODO Move in bfu_collision_ui.py
- pass
- # Move collision content from bfu_object_ui_and_property.py
# -------------------------------------------------------------------
# Register & Unregister
diff --git a/blender-for-unrealengine/bfu_collision/bfu_collision_ui.py b/blender-for-unrealengine/bfu_collision/bfu_collision_ui.py
new file mode 100644
index 00000000..6fab13c9
--- /dev/null
+++ b/blender-for-unrealengine/bfu_collision/bfu_collision_ui.py
@@ -0,0 +1,100 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_static_mesh
+from .. import bfu_skeletal_mesh
+
+
+def draw_ui_object(layout: bpy.types.UILayout, obj: bpy.types.Object):
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+ is_static_mesh = bfu_static_mesh.bfu_static_mesh_utils.is_static_mesh(obj)
+ is_skeletal_mesh = bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj)
+
+ # Hide filters
+ if obj is None:
+ return
+ if addon_prefs.useGeneratedScripts is False:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+ if is_static_mesh == False and is_skeletal_mesh == False:
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "MISC"):
+ scene.bfu_object_collision_properties_expanded.draw(layout)
+ if scene.bfu_object_collision_properties_expanded.is_expend():
+ # StaticMesh prop
+ if is_static_mesh:
+ if not obj.bfu_export_as_lod_mesh:
+ auto_generate_collision = layout.row()
+ auto_generate_collision.prop(
+ obj,
+ 'bfu_auto_generate_collision'
+ )
+ collision_trace_flag = layout.row()
+ collision_trace_flag.prop(
+ obj,
+ 'bfu_collision_trace_flag'
+ )
+ # SkeletalMesh prop
+ if is_skeletal_mesh:
+ if not obj.bfu_export_as_lod_mesh:
+ create_physics_asset = layout.row()
+ create_physics_asset.prop(obj, "bfu_create_physics_asset")
+ enable_skeletal_mesh_per_poly_collision = layout.row()
+ enable_skeletal_mesh_per_poly_collision.prop(obj, 'bfu_enable_skeletal_mesh_per_poly_collision')
+
+
+def draw_tools_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
+
+ scene.bfu_tools_collision_properties_expanded.draw(layout)
+ if scene.bfu_tools_collision_properties_expanded.is_expend():
+
+ # Draw user tips and check can use buttons
+ ready_for_convert_collider = False
+ if not bbpl.utils.active_mode_is("OBJECT"):
+ layout.label(text="Switch to Object Mode.", icon='INFO')
+ else:
+ if bbpl.utils.found_type_in_selection("MESH", False):
+ if bbpl.utils.active_type_is_not("ARMATURE") and len(bpy.context.selected_objects) > 1:
+ layout.label(text="Click on button for convert to collider.", icon='INFO')
+ ready_for_convert_collider = True
+ else:
+ layout.label(text="Select with [SHIFT] the collider owner.", icon='INFO')
+ else:
+ layout.label(text="Please select your collider Object(s). Active should be the owner.", icon='INFO')
+
+ # Draw buttons
+ convertButtons = layout.row().split(factor=0.80)
+ convertStaticCollisionButtons = convertButtons.column()
+ convertStaticCollisionButtons.enabled = ready_for_convert_collider
+ convertStaticCollisionButtons.operator("object.converttoboxcollision", icon='MESH_CUBE')
+ convertStaticCollisionButtons.operator("object.converttoconvexcollision", icon='MESH_ICOSPHERE')
+ convertStaticCollisionButtons.operator("object.converttocapsulecollision", icon='MESH_CAPSULE')
+ convertStaticCollisionButtons.operator("object.converttospherecollision", icon='MESH_UVSPHERE')
+ layout.operator("object.toggle_collision_visibility", text="Toggle Collision Visibility", icon='HIDE_OFF')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_asset.py b/blender-for-unrealengine/bfu_export/bfu_export_asset.py
index 0f4ebb90..1a28e3e4 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_asset.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_asset.py
@@ -28,7 +28,6 @@
from . import bfu_export_single_static_mesh_collection
from . import bfu_export_single_groom_simulation
from .. import bfu_cached_asset_list
-from .. import bps
from .. import bbpl
from .. import bfu_assets_manager
from .. import bfu_basics
@@ -45,7 +44,7 @@
def IsValidActionForExport(scene, obj, animType):
if animType == "Action":
- if scene.anin_export:
+ if scene.bfu_use_anin_export:
if obj.bfu_skeleton_export_procedure == 'auto-rig-pro':
if bfu_basics.CheckPluginIsActivated('auto_rig_pro-master'):
return True
@@ -54,7 +53,7 @@ def IsValidActionForExport(scene, obj, animType):
else:
return False
elif animType == "Pose":
- if scene.anin_export:
+ if scene.bfu_use_anin_export:
if obj.bfu_skeleton_export_procedure == 'auto-rig-pro':
if bfu_basics.CheckPluginIsActivated('auto_rig_pro-master'):
return True
@@ -63,7 +62,7 @@ def IsValidActionForExport(scene, obj, animType):
else:
return False
elif animType == "NLA":
- if scene.anin_export:
+ if scene.bfu_use_anin_export:
if obj.bfu_skeleton_export_procedure == 'auto-rig-pro':
return False
else:
@@ -80,7 +79,9 @@ def IsValidObjectForExport(scene, obj):
return asset_class.can_export_obj_asset(obj)
def PrepareSceneForExport():
- for obj in bpy.data.objects:
+ scene = bpy.context.scene
+
+ for obj in scene.objects:
if obj.hide_select:
obj.hide_select = False
if obj.hide_viewport:
@@ -109,8 +110,8 @@ def process_export(op):
local_view_areas = bbpl.scene_utils.move_to_global_view()
- MyCurrentDataSave = bbpl.utils.UserSceneSave()
- MyCurrentDataSave.save_current_scene()
+ user_scene_save = bbpl.save_data.scene_save.UserSceneSave()
+ user_scene_save.save_current_scene()
if export_filter == "default":
PrepareSceneForExport()
@@ -123,7 +124,7 @@ def process_export(op):
PrepareSceneForExport()
- bbpl.utils.safe_mode_set('OBJECT', MyCurrentDataSave.user_select_class.user_active)
+ bbpl.utils.safe_mode_set('OBJECT', user_scene_save.user_select_class.user_active)
if addon_prefs.revertExportPath:
bfu_basics.RemoveFolderTree(bpy.path.abspath(scene.bfu_export_static_file_path))
@@ -155,12 +156,12 @@ def process_export(op):
if Asset.obj not in obj_list:
obj_list.append(Asset.obj)
- MyCurrentDataSave.reset_select_by_name()
- MyCurrentDataSave.reset_scene_at_save(print_removed_items = True)
+ user_scene_save.reset_select(use_names = True)
+ user_scene_save.reset_scene_at_save(print_removed_items = True)
# Clean actions
for action in bpy.data.actions:
- if action.name not in MyCurrentDataSave.action_names:
+ if action.name not in user_scene_save.action_names:
bpy.data.actions.remove(action)
bbpl.scene_utils.move_to_local_view(local_view_areas)
@@ -182,7 +183,7 @@ def export_collection_from_asset_list(op, asset_list: bfu_cached_asset_list.Asse
addon_prefs = bfu_basics.GetAddonPrefs()
print("Start Export collection(s)")
- if scene.static_collection_export:
+ if scene.bfu_use_static_collection_export:
collection_asset_cache = bfu_cached_asset_list.GetCollectionAssetCache()
collection_export_asset_list = collection_asset_cache.GetCollectionAssetList()
for col in collection_export_asset_list:
@@ -204,7 +205,7 @@ def export_camera_from_asset_list(op, asset_list: bfu_cached_asset_list.AssetToE
camera_list = []
- use_camera_evaluate = (scene.text_AdditionalData and addon_prefs.useGeneratedScripts)
+ use_camera_evaluate = (scene.bfu_use_text_additional_data and addon_prefs.useGeneratedScripts)
if use_camera_evaluate:
multi_camera_tracks = bfu_camera.bfu_camera_data.BFU_MultiCameraTracks()
multi_camera_tracks.set_start_end_frames(scene.frame_start, scene.frame_end+1)
@@ -246,7 +247,7 @@ def export_spline_from_asset_list(op, asset_list: bfu_cached_asset_list.AssetToE
spline_list = []
- use_spline_evaluate = (scene.text_AdditionalData and addon_prefs.useGeneratedScripts)
+ use_spline_evaluate = (scene.bfu_use_text_additional_data and addon_prefs.useGeneratedScripts)
if use_spline_evaluate:
multi_spline_tracks = bfu_spline.bfu_spline_data.BFU_MultiSplineTracks()
@@ -300,7 +301,7 @@ def export_skeletal_mesh_from_asset_list(op, asset_list: bfu_cached_asset_list.A
print("Start Export SkeletalMesh(s)")
for asset in asset_list:
asset: bfu_cached_asset_list.AssetToExport
- if asset.asset_type == bfu_skeletal_mesh.bfu_skeletal_mesh_config.asset_type_name:
+ if asset.asset_type == bfu_skeletal_mesh.bfu_skeletal_mesh_config.mesh_asset_type_name:
armature = asset.obj
mesh_parts = asset.obj_list
desired_name = asset.name
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_alembic_animation.py b/blender-for-unrealengine/bfu_export/bfu_export_single_alembic_animation.py
index 492a9e46..88761f26 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_alembic_animation.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_alembic_animation.py
@@ -32,7 +32,7 @@ def ProcessAlembicAnimationExport(obj):
scene = bpy.context.scene
asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
- dirpath = asset_class.get_obj_export_directory_path(obj)
+ dirpath = asset_class.get_obj_export_directory_path(obj, True)
file_name = asset_class.get_obj_file_name(obj, obj.name, "")
MyAsset: bfu_export_logs.BFU_OT_UnrealExportedAsset = scene.UnrealExportedAssetsList.add()
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_camera.py b/blender-for-unrealengine/bfu_export/bfu_export_single_camera.py
index 757a85b3..2ad7a134 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_camera.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_camera.py
@@ -21,11 +21,9 @@
from . import bfu_fbx_export
from . import bfu_export_utils
from .. import bfu_camera
-from .. import bps
from .. import bbpl
from .. import bfu_basics
from .. import bfu_utils
-from .. import bfu_naming
from .. import bfu_export_logs
from .. import bfu_assets_manager
@@ -37,7 +35,7 @@ def ProcessCameraExport(op, obj, pre_bake_camera: bfu_camera.bfu_camera_data.BFU
asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
asset_type = asset_class.get_asset_type_name(obj)
- dirpath = asset_class.get_obj_export_directory_path(obj)
+ dirpath = asset_class.get_obj_export_directory_path(obj, True)
file_name = asset_class.get_obj_file_name(obj, obj.name, "")
file_name_at = asset_class.get_obj_file_name(obj, obj.name+"_AdditionalTrack", "")
@@ -60,7 +58,7 @@ def ProcessCameraExport(op, obj, pre_bake_camera: bfu_camera.bfu_camera_data.BFU
ExportSingleFbxCamera(op, dirpath, file.GetFileWithExtension(), obj)
- if scene.text_AdditionalData and addon_prefs.useGeneratedScripts:
+ if scene.bfu_use_text_additional_data and addon_prefs.useGeneratedScripts:
file: bfu_export_logs.BFU_OT_FileExport = MyAsset.files.add()
file.file_name = file_name_at
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_fbx_action.py b/blender-for-unrealengine/bfu_export/bfu_export_single_fbx_action.py
index 64107d35..6cb6972f 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_fbx_action.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_fbx_action.py
@@ -32,8 +32,8 @@
def ProcessActionExport(op, obj, action, action_curve_scale):
scene = bpy.context.scene
- asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
- dirpath = asset_class.get_obj_export_directory_path(obj)
+ asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj, "SkeletalAnimation")
+ dirpath = asset_class.get_obj_export_directory_path(obj, True)
MyAsset: bfu_export_logs.BFU_OT_UnrealExportedAsset = scene.UnrealExportedAssetsList.add()
MyAsset.object = obj
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_fbx_nla_anim.py b/blender-for-unrealengine/bfu_export/bfu_export_single_fbx_nla_anim.py
index f07c441d..68a062df 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_fbx_nla_anim.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_fbx_nla_anim.py
@@ -34,8 +34,8 @@
def ProcessNLAAnimExport(op, obj):
scene = bpy.context.scene
- asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
- dirpath = asset_class.get_obj_export_directory_path(obj)
+ asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj, "SkeletalAnimation")
+ dirpath = asset_class.get_obj_export_directory_path(obj, True)
scene.frame_end += 1 # Why ?
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_groom_simulation.py b/blender-for-unrealengine/bfu_export/bfu_export_single_groom_simulation.py
index 67e0e620..f78b9a4e 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_groom_simulation.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_groom_simulation.py
@@ -32,7 +32,7 @@ def ProcessGroomSimulationExport(obj):
scene = bpy.context.scene
asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
- dirpath = asset_class.get_obj_export_directory_path(obj)
+ dirpath = asset_class.get_obj_export_directory_path(obj, True)
file_name = asset_class.get_obj_file_name(obj, obj.name, "")
asset_type = asset_class.get_asset_type_name(obj)
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_skeletal_mesh.py b/blender-for-unrealengine/bfu_export/bfu_export_single_skeletal_mesh.py
index 506e1380..4c19a96e 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_skeletal_mesh.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_skeletal_mesh.py
@@ -37,8 +37,7 @@ def ProcessSkeletalMeshExport(op, armature, mesh_parts, desired_name=""):
asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(armature)
asset_type = asset_class.get_asset_type_name(armature)
- dirpath = asset_class.get_obj_export_directory_path(armature)
- absdirpath = asset_class.get_obj_export_abs_directory_path(armature)
+ dirpath = asset_class.get_obj_export_directory_path(armature, True)
if desired_name:
final_name = desired_name
@@ -66,14 +65,14 @@ def ProcessSkeletalMeshExport(op, armature, mesh_parts, desired_name=""):
ExportSingleSkeletalMesh(op, scene, dirpath, file.GetFileWithExtension(), armature, mesh_parts)
if not armature.bfu_export_as_lod_mesh:
- if (scene.text_AdditionalData and addon_prefs.useGeneratedScripts):
+ if (scene.bfu_use_text_additional_data and addon_prefs.useGeneratedScripts):
file: bfu_export_logs.BFU_OT_FileExport = MyAsset.files.add()
file.file_name = file_name_at
file.file_extension = "json"
file.file_path = dirpath
file.file_type = "AdditionalTrack"
- bfu_export_utils.ExportAdditionalParameter(absdirpath, file.GetFileWithExtension(), MyAsset)
+ bfu_export_utils.ExportAdditionalParameter(dirpath, file.GetFileWithExtension(), MyAsset)
MyAsset.EndAssetExport(True)
return MyAsset
@@ -88,6 +87,8 @@ def ExportSingleSkeletalMesh(
mesh_parts
):
+ print("--", dirpath, filename)
+
'''
#####################################################
#SKELETAL MESH
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_spline.py b/blender-for-unrealengine/bfu_export/bfu_export_single_spline.py
index 2494561f..deb1373d 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_spline.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_spline.py
@@ -21,11 +21,9 @@
from . import bfu_fbx_export
from . import bfu_export_utils
from .. import bfu_spline
-from .. import bps
from .. import bbpl
from .. import bfu_basics
from .. import bfu_utils
-from .. import bfu_naming
from .. import bfu_export_logs
from .. import bfu_assets_manager
@@ -38,7 +36,7 @@ def ProcessSplineExport(op, obj, pre_bake_spline: bfu_spline.bfu_spline_data.BFU
asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
asset_type = asset_class.get_asset_type_name(obj)
- dirpath = asset_class.get_obj_export_directory_path(obj)
+ dirpath = asset_class.get_obj_export_directory_path(obj, True)
file_name = asset_class.get_obj_file_name(obj, obj.name, "")
file_name_at = asset_class.get_obj_file_name(obj, obj.name+"_AdditionalTrack", "")
@@ -61,7 +59,7 @@ def ProcessSplineExport(op, obj, pre_bake_spline: bfu_spline.bfu_spline_data.BFU
ExportSingleFbxSpline(op, dirpath, file.GetFileWithExtension(), obj)
- if scene.text_AdditionalData and addon_prefs.useGeneratedScripts:
+ if scene.bfu_use_text_additional_data and addon_prefs.useGeneratedScripts:
file: bfu_export_logs.BFU_OT_FileExport = MyAsset.files.add()
file.file_name = file_name_at
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_static_mesh.py b/blender-for-unrealengine/bfu_export/bfu_export_single_static_mesh.py
index e343050b..aa1fbfbe 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_static_mesh.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_static_mesh.py
@@ -37,8 +37,7 @@ def ProcessStaticMeshExport(op, obj, desired_name=""):
asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
asset_type = asset_class.get_asset_type_name(obj)
- dirpath = asset_class.get_obj_export_directory_path(obj)
- absdirpath = asset_class.get_obj_export_abs_directory_path(obj)
+ dirpath = asset_class.get_obj_export_directory_path(obj, True)
if desired_name:
final_name = desired_name
@@ -65,14 +64,14 @@ def ProcessStaticMeshExport(op, obj, desired_name=""):
ExportSingleStaticMesh(op, dirpath, file.GetFileWithExtension(), obj)
if not obj.bfu_export_as_lod_mesh:
- if (scene.text_AdditionalData and addon_prefs.useGeneratedScripts):
+ if (scene.bfu_use_text_additional_data and addon_prefs.useGeneratedScripts):
file: bfu_export_logs.BFU_OT_FileExport = MyAsset.files.add()
file.file_name = file_name_at
file.file_extension = "json"
file.file_path = dirpath
file.file_type = "AdditionalTrack"
- bfu_export_utils.ExportAdditionalParameter(absdirpath, file.GetFileWithExtension(), MyAsset)
+ bfu_export_utils.ExportAdditionalParameter(dirpath, file.GetFileWithExtension(), MyAsset)
MyAsset.EndAssetExport(True)
return MyAsset
@@ -84,6 +83,8 @@ def ExportSingleStaticMesh(
filename,
obj
):
+
+ print("--", dirpath, filename)
'''
#####################################################
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_single_static_mesh_collection.py b/blender-for-unrealengine/bfu_export/bfu_export_single_static_mesh_collection.py
index f51a36c1..5a32d58e 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_single_static_mesh_collection.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_single_static_mesh_collection.py
@@ -33,7 +33,6 @@ def ProcessCollectionExport(op, col):
addon_prefs = bfu_basics.GetAddonPrefs()
dirpath = bfu_utils.GetCollectionExportDir(bpy.data.collections[col.name])
- absdirpath = bpy.path.abspath(dirpath)
scene = bpy.context.scene
MyAsset: bfu_export_logs.BFU_OT_UnrealExportedAsset = scene.UnrealExportedAssetsList.add()
@@ -52,14 +51,14 @@ def ProcessCollectionExport(op, col):
MyAsset.StartAssetExport()
ExportSingleStaticMeshCollection(op, dirpath, file.GetFileWithExtension(), col.name)
- if (scene.text_AdditionalData and addon_prefs.useGeneratedScripts):
+ if (scene.bfu_use_text_additional_data and addon_prefs.useGeneratedScripts):
file: bfu_export_logs.BFU_OT_FileExport = MyAsset.files.add()
file.file_name = bfu_naming.get_collection_file_name(col, col.name+"_AdditionalTrack", "")
file.file_extension = "json"
file.file_path = dirpath
file.file_type = "AdditionalTrack"
- bfu_export_utils.ExportAdditionalParameter(absdirpath, file.GetFileWithExtension(), MyAsset)
+ bfu_export_utils.ExportAdditionalParameter(dirpath, file.GetFileWithExtension(), MyAsset)
MyAsset.EndAssetExport(True)
return MyAsset
diff --git a/blender-for-unrealengine/bfu_export/bfu_export_utils.py b/blender-for-unrealengine/bfu_export/bfu_export_utils.py
index a2d0fb78..e3c407cf 100644
--- a/blender-for-unrealengine/bfu_export/bfu_export_utils.py
+++ b/blender-for-unrealengine/bfu_export/bfu_export_utils.py
@@ -47,6 +47,7 @@ def GetExportFullpath(dirpath, filename):
def ApplyProxyData(obj):
+ scene = bpy.context.scene
# Apply proxy data if needed.
if bfu_utils.GetExportProxyChild(obj) is not None:
@@ -76,8 +77,8 @@ def ReasignProxySkeleton(newArmature, oldArmature):
cons.target.name +
"_UEProxyChild"
)
- if ChildProxyName in bpy.data.objects:
- cons.target = bpy.data.objects[ChildProxyName]
+ if ChildProxyName in scene.objects:
+ cons.target = scene.objects[ChildProxyName]
# Get old armature in selected objects
OldProxyChildArmature = None
@@ -101,17 +102,17 @@ def ReasignProxySkeleton(newArmature, oldArmature):
else:
ToRemove.append(selectedObj)
ReasignProxySkeleton(obj, OldProxyChildArmature)
- SavedSelect = bbpl.utils.UserSelectSave()
+ SavedSelect = bbpl.save_data.select_save.UserSelectSave()
SavedSelect.save_current_select()
RemovedObjects = bfu_utils.CleanDeleteObjects(ToRemove)
SavedSelect.remove_from_list_by_name(RemovedObjects)
- SavedSelect.reset_select_by_ref()
+ SavedSelect.reset_select()
def BakeArmatureAnimation(armature, frame_start, frame_end):
# Change to pose mode
- SavedSelect = bbpl.utils.UserSelectSave()
+ SavedSelect = bbpl.save_data.select_save.UserSelectSave()
SavedSelect.save_current_select()
bpy.ops.object.select_all(action='DESELECT')
bbpl.utils.select_specific_object(armature)
@@ -125,7 +126,7 @@ def BakeArmatureAnimation(armature, frame_start, frame_end):
bake_types={'POSE'}
)
bpy.ops.object.select_all(action='DESELECT')
- SavedSelect.reset_select_by_ref()
+ SavedSelect.reset_select()
def DuplicateSelectForExport():
@@ -138,12 +139,12 @@ def __init__(self):
self.duplicate_select = None
def SetOriginSelect(self):
- select = bbpl.utils.UserSelectSave()
+ select = bbpl.save_data.select_save.UserSelectSave()
select.save_current_select()
self.origin_select = select
def SetDuplicateSelect(self):
- select = bbpl.utils.UserSelectSave()
+ select = bbpl.save_data.select_save.UserSelectSave()
select.save_current_select()
self.duplicate_select = select
@@ -157,6 +158,7 @@ def __init__(self, data_name, data_type):
def RemoveData(self):
bfu_utils.RemoveUselessSpecificData(self.data_name, self.data_type)
+ scene = bpy.context.scene
duplicate_data = DuplicateData()
duplicate_data.SetOriginSelect()
for user_selected in duplicate_data.origin_select.user_selecteds:
@@ -181,7 +183,7 @@ def RemoveData(self):
for objSelect in currentSelectNames:
if objSelect not in bpy.context.selected_objects:
- bpy.data.objects[objSelect].select_set(True)
+ scene.objects[objSelect].select_set(True)
# Make sigle user and clean useless data.
for objScene in bpy.context.selected_objects:
@@ -215,7 +217,8 @@ def ResetDuplicateNameAfterExport(duplicate_data):
def ConvertSelectedCurveToMesh():
# Have to convert curve to mesh before MakeSelectVisualReal for avoid double duplicate issue.
- select = bbpl.utils.UserSelectSave()
+ scene = bpy.context.scene
+ select = bbpl.save_data.select_save.UserSelectSave()
select.save_current_select()
bpy.ops.object.select_all(action='DESELECT')
@@ -223,11 +226,11 @@ def ConvertSelectedCurveToMesh():
for selected_obj in select.user_selecteds:
if selected_obj.type == "CURVE":
- selected_obj.select_set(True)
+ selected_obj.select_set(False)
# Save object list
previous_objects = []
- for obj in bpy.data.objects:
+ for obj in scene.objects:
previous_objects.append(obj)
# Convert to mesh
@@ -235,20 +238,21 @@ def ConvertSelectedCurveToMesh():
bpy.context.view_layer.objects.active = bpy.context.selected_objects[0] #Convert fail if active is none.
bpy.ops.object.convert(target='MESH')
- select.reset_select_by_name()
+ select.reset_select(use_names = True)
# Select the new objects
- for obj in bpy.data.objects:
+ for obj in scene.objects:
if obj not in previous_objects:
obj.select_set(True)
def MakeSelectVisualReal():
- select = bbpl.utils.UserSelectSave()
+ scene = bpy.context.scene
+ select = bbpl.save_data.select_save.UserSelectSave()
select.save_current_select()
# Save object list
previous_objects = []
- for obj in bpy.data.objects:
+ for obj in scene.objects:
previous_objects.append(obj)
# Visual Transform Apply
@@ -260,10 +264,10 @@ def MakeSelectVisualReal():
use_hierarchy=True
)
- select.reset_select_by_name()
+ select.reset_select(use_names = True)
# Select the new objects
- for obj in bpy.data.objects:
+ for obj in scene.objects:
if obj not in previous_objects:
obj.select_set(True)
@@ -402,7 +406,7 @@ def ConvertGeometryNodeAttributeToUV(obj, attrib_name):
if attribute.name == attrib_name:
obj.data.attributes.active_index = x
- SavedSelect = bbpl.utils.UserSelectSave()
+ SavedSelect = bbpl.save_data.select_save.UserSelectSave()
SavedSelect.save_current_select()
bbpl.utils.select_specific_object(obj)
if bpy.app.version >= (3, 5, 0):
@@ -411,7 +415,7 @@ def ConvertGeometryNodeAttributeToUV(obj, attrib_name):
else:
if obj.data.attributes.active:
bpy.ops.geometry.attribute_convert(mode='UV_MAP', domain='CORNER', data_type='FLOAT2')
- SavedSelect.reset_select_by_ref()
+ SavedSelect.reset_select()
# Because it not possible to move UV index I need recreate all UV for place new UV Map at start...
if len(obj.data.uv_layers) < 8: # Blender Cannot add more than 8 UV maps.
@@ -465,13 +469,14 @@ def ConvertGeometryNodeAttributeToUV(obj, attrib_name):
def CorrectExtremUVAtExport(obj):
- if obj.bfu_correct_extrem_uv_scale:
- SavedSelect = bbpl.utils.UserSelectSave()
+ if obj.bfu_use_correct_extrem_uv_scale:
+ SavedSelect = bbpl.save_data.select_save.UserSelectSave()
SavedSelect.save_current_select()
+ bbpl.utils.select_specific_object(obj)
if bfu_utils.GoToMeshEditMode():
- bfu_utils.CorrectExtremeUV(2)
+ bfu_utils.CorrectExtremeUV(obj.bfu_correct_extrem_uv_scale_step_scale, obj.bfu_correct_extrem_uv_scale_use_absolute)
bbpl.utils.safe_mode_set('OBJECT')
- SavedSelect.reset_select_by_ref()
+ SavedSelect.reset_select()
return True
return False
diff --git a/blender-for-unrealengine/bfu_export/bfu_fbx_export.py b/blender-for-unrealengine/bfu_export/bfu_fbx_export.py
index e4101cf8..45688f91 100644
--- a/blender-for-unrealengine/bfu_export/bfu_fbx_export.py
+++ b/blender-for-unrealengine/bfu_export/bfu_fbx_export.py
@@ -21,9 +21,9 @@
# Better to look about an class that amange all export type in future?
import traceback
-import sys
import bpy
from mathutils import Matrix
+from .. import bpl
from .. import fbxio
@@ -105,9 +105,8 @@ def export_scene_fbx_with_custom_fbx_io(operator, context, filepath='', check_ex
fbxio.current_fbxio.export_fbx_bin.save(**params)
except Exception as e:
# Capture and print the detailed error information
- exc_type, exc_value, exc_tb = sys.exc_info()
- error_message = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
- print(f"\033[91m{error_message}\033[0m")
+ error_message = traceback.format_exc()
+ print(bpl.color_set.red(error_message))
def export_scene_fbx(filepath='', check_existing=True, filter_glob='*.fbx', use_selection=False, use_visible=False, use_active_collection=False, global_scale=1.0, apply_unit_scale=True, apply_scale_options='FBX_SCALE_NONE', use_space_transform=True, bake_space_transform=False, object_types={'ARMATURE', 'CAMERA', 'EMPTY', 'LIGHT', 'MESH', 'OTHER'}, use_mesh_modifiers=True, use_mesh_modifiers_render=True, mesh_smooth_type='OFF', colors_type='SRGB', prioritize_active_color=False, use_subsurf=False, use_mesh_edges=False, use_tspace=False, use_triangles=False, use_custom_props=False, add_leaf_bones=True, primary_bone_axis='Y', secondary_bone_axis='X', use_armature_deform_only=False, armature_nodetype='NULL', bake_anim=True, bake_anim_use_all_bones=True, bake_anim_use_nla_strips=True, bake_anim_use_all_actions=True, bake_anim_force_startend_keying=True, bake_anim_step=1.0, bake_anim_simplify_factor=1.0, path_mode='AUTO', embed_textures=False, batch_mode='OFF', use_batch_own_dir=True, use_metadata=True, axis_forward='-Z', axis_up='Y'):
@@ -175,6 +174,5 @@ def export_scene_fbx(filepath='', check_existing=True, filter_glob='*.fbx', use_
bpy.ops.export_scene.fbx(**params)
except Exception as e:
# Capture and print the detailed error information
- exc_type, exc_value, exc_tb = sys.exc_info()
- error_message = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
- print(f"\033[91m{error_message}\033[0m")
+ error_message = traceback.format_exc()
+ print(bpl.color_set.red(error_message))
diff --git a/blender-for-unrealengine/bfu_export_filter/__init__.py b/blender-for-unrealengine/bfu_export_filter/__init__.py
new file mode 100644
index 00000000..b04174cf
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_filter/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_export_filter_props
+from . import bfu_export_filter_ui
+from . import bfu_export_filter_utils
+
+if "bfu_export_filter_props" in locals():
+ importlib.reload(bfu_export_filter_props)
+if "bfu_export_filter_ui" in locals():
+ importlib.reload(bfu_export_filter_ui)
+if "bfu_export_filter_utils" in locals():
+ importlib.reload(bfu_export_filter_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_export_filter_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_export_filter_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_props.py b/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_props.py
new file mode 100644
index 00000000..6bdffd11
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_props.py
@@ -0,0 +1,176 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ # Filter Categories
+ 'scene.bfu_use_static_export',
+ 'scene.bfu_use_static_collection_export',
+ 'scene.bfu_use_skeletal_export',
+ 'scene.bfu_use_anin_export',
+ 'scene.bfu_use_alembic_export',
+ 'scene.bfu_use_groom_simulation_export',
+ 'scene.bfu_use_camera_export',
+ 'scene.bfu_use_spline_export',
+
+ # Additional Files
+ 'scene.bfu_use_text_export_log',
+ 'scene.bfu_use_text_import_asset_script',
+ 'scene.bfu_use_text_import_sequence_script',
+ 'scene.bfu_use_text_additional_data',
+
+ # Export Filter
+ 'scene.bfu_export_selection_filter',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_export_filter_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Export filters")
+
+ # Filter Categories
+ bpy.types.Scene.bfu_use_static_export = bpy.props.BoolProperty(
+ name="StaticMesh(s)",
+ description="Check mark to export StaticMesh(s)",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_static_collection_export = bpy.props.BoolProperty(
+ name="Collection(s) ",
+ description="Check mark to export Collection(s)",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_skeletal_export = bpy.props.BoolProperty(
+ name="SkeletalMesh(s)",
+ description="Check mark to export SkeletalMesh(s)",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_anin_export = bpy.props.BoolProperty(
+ name="Animation(s)",
+ description="Check mark to export Animation(s)",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_alembic_export = bpy.props.BoolProperty(
+ name="Alembic Animation(s)",
+ description="Check mark to export Alembic animation(s)",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_groom_simulation_export = bpy.props.BoolProperty(
+ name="Groom Simulation(s)",
+ description="Check mark to export Alembic animation(s)",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_camera_export = bpy.props.BoolProperty(
+ name="Camera(s)",
+ description="Check mark to export Camera(s)",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_spline_export = bpy.props.BoolProperty(
+ name="Spline(s)",
+ description="Check mark to export Spline(s)",
+ default=True
+ )
+
+ # Additional Files
+ bpy.types.Scene.bfu_use_text_export_log = bpy.props.BoolProperty(
+ name="Export Log",
+ description="Check mark to write export log file",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_text_import_asset_script = bpy.props.BoolProperty(
+ name="Import assets script",
+ description="Check mark to write import asset script file",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_text_import_sequence_script = bpy.props.BoolProperty(
+ name="Import sequence script",
+ description="Check mark to write import sequencer script file",
+ default=True
+ )
+
+ bpy.types.Scene.bfu_use_text_additional_data = bpy.props.BoolProperty(
+ name="Additional data",
+ description=(
+ "Check mark to write additional data" +
+ " like parameter or anim tracks"),
+ default=True
+ )
+
+ # Export Filter
+ bpy.types.Scene.bfu_export_selection_filter = bpy.props.EnumProperty(
+ name="Selection filter",
+ items=[
+ ('default', "No Filter", "Export as normal all objects with the recursive export option.", 0),
+ ('only_object', "Only selected", "Export only the selected and visible object(s)", 1),
+ ('only_object_action', "Only selected and active action",
+ "Export only the selected and visible object(s) and active action on this object", 2),
+ ],
+ description=(
+ "Choose what need be export from asset list."),
+ default="default"
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Scene.bfu_export_selection_filter
+
+ del bpy.types.Scene.bfu_use_text_additional_data
+ del bpy.types.Scene.bfu_use_text_import_sequence_script
+ del bpy.types.Scene.bfu_use_text_import_asset_script
+ del bpy.types.Scene.bfu_use_text_export_log
+
+ del bpy.types.Scene.bfu_use_spline_export
+ del bpy.types.Scene.bfu_use_camera_export
+ del bpy.types.Scene.bfu_use_groom_simulation_export
+ del bpy.types.Scene.bfu_use_alembic_export
+ del bpy.types.Scene.bfu_use_anin_export
+ del bpy.types.Scene.bfu_use_skeletal_export
+ del bpy.types.Scene.bfu_use_static_collection_export
+ del bpy.types.Scene.bfu_use_static_export
+
+ del bpy.types.Scene.bfu_export_filter_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_ui.py b/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_ui.py
new file mode 100644
index 00000000..71cdee75
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_ui.py
@@ -0,0 +1,59 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+def draw_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ scene.bfu_export_filter_properties_expanded.draw(layout)
+ if scene.bfu_export_filter_properties_expanded.is_expend():
+
+ # Assets
+ row = layout.row()
+ AssetsCol = row.column()
+ AssetsCol.label(text="Asset types to export", icon='PACKAGE')
+ AssetsCol.prop(scene, 'bfu_use_static_export')
+ AssetsCol.prop(scene, 'bfu_use_static_collection_export')
+ AssetsCol.prop(scene, 'bfu_use_skeletal_export')
+ AssetsCol.prop(scene, 'bfu_use_anin_export')
+ AssetsCol.prop(scene, 'bfu_use_alembic_export')
+ AssetsCol.prop(scene, 'bfu_use_groom_simulation_export')
+ AssetsCol.prop(scene, 'bfu_use_camera_export')
+ AssetsCol.prop(scene, 'bfu_use_spline_export')
+ layout.separator()
+
+ # Additional file
+ FileCol = row.column()
+ FileCol.label(text="Additional file", icon='PACKAGE')
+ FileCol.prop(scene, 'bfu_use_text_export_log')
+ FileCol.prop(scene, 'bfu_use_text_import_asset_script')
+ FileCol.prop(scene, 'bfu_use_text_import_sequence_script')
+ if addon_prefs.useGeneratedScripts:
+ FileCol.prop(scene, 'bfu_use_text_additional_data')
+
+ # exportProperty
+ export_by_select = layout.row()
+ export_by_select.prop(scene, 'bfu_export_selection_filter')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_utils.py b/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_filter/bfu_export_filter_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_export_nomenclature/__init__.py b/blender-for-unrealengine/bfu_export_nomenclature/__init__.py
new file mode 100644
index 00000000..c877d807
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_nomenclature/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_export_nomenclature_props
+from . import bfu_export_nomenclature_ui
+from . import bfu_export_nomenclature_utils
+
+if "bfu_export_nomenclature_props" in locals():
+ importlib.reload(bfu_export_nomenclature_props)
+if "bfu_export_nomenclature_ui" in locals():
+ importlib.reload(bfu_export_nomenclature_ui)
+if "bfu_export_nomenclature_utils" in locals():
+ importlib.reload(bfu_export_nomenclature_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_export_nomenclature_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_export_nomenclature_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_props.py b/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_props.py
new file mode 100644
index 00000000..eb8522eb
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_props.py
@@ -0,0 +1,256 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import os
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ # Prefix
+ 'scene.bfu_static_mesh_prefix_export_name',
+ 'scene.bfu_skeletal_mesh_prefix_export_name',
+ 'scene.bfu_skeleton_prefix_export_name',
+ 'scene.bfu_alembic_animation_prefix_export_name',
+ 'scene.bfu_groom_simulation_prefix_export_name',
+ 'scene.bfu_anim_prefix_export_name',
+ 'scene.bfu_pose_prefix_export_name',
+ 'scene.bfu_camera_prefix_export_name',
+ 'scene.bfu_spline_prefix_export_name',
+
+ # Sub folder
+ 'scene.bfu_anim_subfolder_name',
+
+ # Import location
+ 'scene.bfu_unreal_import_module',
+ 'scene.bfu_unreal_import_location',
+
+ # File path
+ 'scene.bfu_export_static_file_path',
+ 'scene.bfu_export_skeletal_file_path',
+ 'scene.bfu_export_alembic_file_path',
+ 'scene.bfu_export_groom_file_path',
+ 'scene.bfu_export_camera_file_path',
+ 'scene.bfu_export_spline_file_path',
+ 'scene.bfu_export_other_file_path',
+
+ # File name
+ 'scene.bfu_file_export_log_name',
+ 'scene.bfu_file_import_asset_script_name',
+ 'scene.bfu_file_import_sequencer_script_name',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_nomenclature_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Nomenclature")
+
+ # Prefix
+ bpy.types.Scene.bfu_static_mesh_prefix_export_name = bpy.props.StringProperty(
+ name="StaticMesh Prefix",
+ description="Prefix of staticMesh",
+ maxlen=32,
+ default="SM_")
+
+ bpy.types.Scene.bfu_skeletal_mesh_prefix_export_name = bpy.props.StringProperty(
+ name="SkeletalMesh Prefix ",
+ description="Prefix of SkeletalMesh",
+ maxlen=32,
+ default="SKM_")
+
+ bpy.types.Scene.bfu_skeleton_prefix_export_name = bpy.props.StringProperty(
+ name="skeleton Prefix ",
+ description="Prefix of skeleton",
+ maxlen=32,
+ default="SK_")
+
+ bpy.types.Scene.bfu_alembic_animation_prefix_export_name = bpy.props.StringProperty(
+ name="Alembic Prefix ",
+ description="Prefix of Alembic (SkeletalMesh in unreal)",
+ maxlen=32,
+ default="SKM_")
+
+ bpy.types.Scene.bfu_groom_simulation_prefix_export_name = bpy.props.StringProperty(
+ name="Groom Prefix ",
+ description="Prefix of Groom Simulation",
+ maxlen=32,
+ default="GS_")
+
+ bpy.types.Scene.bfu_anim_prefix_export_name = bpy.props.StringProperty(
+ name="AnimationSequence Prefix",
+ description="Prefix of AnimationSequence",
+ maxlen=32,
+ default="Anim_")
+
+ bpy.types.Scene.bfu_pose_prefix_export_name = bpy.props.StringProperty(
+ name="AnimationSequence(Pose) Prefix",
+ description="Prefix of AnimationSequence with only one frame",
+ maxlen=32,
+ default="Pose_")
+
+ bpy.types.Scene.bfu_camera_prefix_export_name = bpy.props.StringProperty(
+ name="Camera anim Prefix",
+ description="Prefix of camera animations",
+ maxlen=32,
+ default="Cam_")
+
+ bpy.types.Scene.bfu_spline_prefix_export_name = bpy.props.StringProperty(
+ name="Spline anim Prefix",
+ description="Prefix of spline animations",
+ maxlen=32,
+ default="Spline_")
+
+ # Sub folder
+ bpy.types.Scene.bfu_anim_subfolder_name = bpy.props.StringProperty(
+ name="Animations sub folder name",
+ description=(
+ "The name of sub folder for animations New." +
+ " You can now use ../ for up one directory."),
+ maxlen=512,
+ default="Anim")
+
+ # Import location
+ bpy.types.Scene.bfu_unreal_import_module = bpy.props.StringProperty(
+ name="Unreal import module",
+ description="Which module (plugin name) to import to. Default is 'Game', meaning it will be put into your project's /Content/ folder. If you wish to import to a plugin (for example a plugin called 'myPlugin'), just write its name here",
+ maxlen=512,
+ default='Game')
+
+ bpy.types.Scene.bfu_unreal_import_location = bpy.props.StringProperty(
+ name="Unreal import location",
+ description="Unreal assets import location inside the module",
+ maxlen=512,
+ default='ImportedFbx')
+
+ # File path
+ bpy.types.Scene.bfu_export_static_file_path = bpy.props.StringProperty(
+ name="StaticMesh Export Path",
+ description="Choose a directory to export StaticMesh(s)",
+ maxlen=512,
+ default="//" + os.path.join("ExportedFbx", "StaticMesh"),
+ subtype='DIR_PATH')
+
+ bpy.types.Scene.bfu_export_skeletal_file_path = bpy.props.StringProperty(
+ name="SkeletalMesh Export Path",
+ description="Choose a directory to export SkeletalMesh(s)",
+ maxlen=512,
+ default="//" + os.path.join("ExportedFbx", "SkeletalMesh"),
+ subtype='DIR_PATH')
+
+ bpy.types.Scene.bfu_export_alembic_file_path = bpy.props.StringProperty(
+ name="Alembic Export Path",
+ description="Choose a directory to export Alembic animation(s)",
+ maxlen=512,
+ default="//" + os.path.join("ExportedFbx", "Alembic"),
+ subtype='DIR_PATH')
+
+ bpy.types.Scene.bfu_export_groom_file_path = bpy.props.StringProperty(
+ name="Groom Export Path",
+ description="Choose a directory to export Groom simulation(s)",
+ maxlen=512,
+ default="//" + os.path.join("ExportedFbx", "Groom"),
+ subtype='DIR_PATH')
+
+ bpy.types.Scene.bfu_export_camera_file_path = bpy.props.StringProperty(
+ name="Camera Export Path",
+ description="Choose a directory to export Camera(s)",
+ maxlen=512,
+ default="//" + os.path.join("ExportedFbx", "Sequencer"),
+ subtype='DIR_PATH')
+
+ bpy.types.Scene.bfu_export_spline_file_path = bpy.props.StringProperty(
+ name="Spline Export Path",
+ description="Choose a directory to export Spline(s)",
+ maxlen=512,
+ default="//" + os.path.join("ExportedFbx", "Spline"),
+ subtype='DIR_PATH')
+
+ bpy.types.Scene.bfu_export_other_file_path = bpy.props.StringProperty(
+ name="Other Export Path",
+ description="Choose a directory to export text file and other",
+ maxlen=512,
+ default="//" + os.path.join("ExportedFbx"),
+ subtype='DIR_PATH')
+
+ # File name
+ bpy.types.Scene.bfu_file_export_log_name = bpy.props.StringProperty(
+ name="Export log name",
+ description="Export log name",
+ maxlen=64,
+ default="ExportLog.txt")
+
+ bpy.types.Scene.bfu_file_import_asset_script_name = bpy.props.StringProperty(
+ name="Import asset script Name",
+ description="Import asset script name",
+ maxlen=64,
+ default="ImportAssetScript.py")
+
+ bpy.types.Scene.bfu_file_import_sequencer_script_name = bpy.props.StringProperty(
+ name="Import sequencer script Name",
+ description="Import sequencer script name",
+ maxlen=64,
+ default="ImportSequencerScript.py")
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Scene.bfu_file_import_sequencer_script_name
+ del bpy.types.Scene.bfu_file_import_asset_script_name
+ del bpy.types.Scene.bfu_file_export_log_name
+
+ del bpy.types.Scene.bfu_export_other_file_path
+ del bpy.types.Scene.bfu_export_spline_file_path
+ del bpy.types.Scene.bfu_export_camera_file_path
+ del bpy.types.Scene.bfu_export_groom_file_path
+ del bpy.types.Scene.bfu_export_alembic_file_path
+ del bpy.types.Scene.bfu_export_skeletal_file_path
+ del bpy.types.Scene.bfu_export_static_file_path
+
+ del bpy.types.Scene.bfu_unreal_import_location
+ del bpy.types.Scene.bfu_unreal_import_module
+ del bpy.types.Scene.bfu_anim_subfolder_name
+
+ del bpy.types.Scene.bfu_spline_prefix_export_name
+ del bpy.types.Scene.bfu_camera_prefix_export_name
+ del bpy.types.Scene.bfu_pose_prefix_export_name
+ del bpy.types.Scene.bfu_anim_prefix_export_name
+ del bpy.types.Scene.bfu_groom_simulation_prefix_export_name
+ del bpy.types.Scene.bfu_alembic_animation_prefix_export_name
+ del bpy.types.Scene.bfu_skeleton_prefix_export_name
+ del bpy.types.Scene.bfu_skeletal_mesh_prefix_export_name
+ del bpy.types.Scene.bfu_static_mesh_prefix_export_name
+
+ del bpy.types.Scene.bfu_nomenclature_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_ui.py b/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_ui.py
new file mode 100644
index 00000000..dc4b6f22
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_ui.py
@@ -0,0 +1,87 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+def draw_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ scene.bfu_nomenclature_properties_expanded.draw(layout)
+ if scene.bfu_nomenclature_properties_expanded.is_expend():
+
+ # Prefix
+ propsPrefix = layout.row()
+ propsPrefix = propsPrefix.column()
+ propsPrefix.prop(scene, 'bfu_static_mesh_prefix_export_name', icon='OBJECT_DATA')
+ propsPrefix.prop(scene, 'bfu_skeletal_mesh_prefix_export_name', icon='OBJECT_DATA')
+ propsPrefix.prop(scene, 'bfu_skeleton_prefix_export_name', icon='OBJECT_DATA')
+ propsPrefix.prop(scene, 'bfu_alembic_animation_prefix_export_name', icon='OBJECT_DATA')
+ propsPrefix.prop(scene, 'bfu_groom_simulation_prefix_export_name', icon='OBJECT_DATA')
+ propsPrefix.prop(scene, 'bfu_anim_prefix_export_name', icon='OBJECT_DATA')
+ propsPrefix.prop(scene, 'bfu_pose_prefix_export_name', icon='OBJECT_DATA')
+ propsPrefix.prop(scene, 'bfu_camera_prefix_export_name', icon='OBJECT_DATA')
+ propsPrefix.prop(scene, 'bfu_spline_prefix_export_name', icon='OBJECT_DATA')
+
+ # Sub folder
+ propsSub = layout.row()
+ propsSub = propsSub.column()
+ propsSub.prop(scene, 'bfu_anim_subfolder_name', icon='FILE_FOLDER')
+
+ if addon_prefs.useGeneratedScripts:
+ bfu_unreal_import_module = propsSub.column()
+ bfu_unreal_import_module.prop(
+ scene,
+ 'bfu_unreal_import_module',
+ icon='FILE_FOLDER')
+ bfu_unreal_import_location = propsSub.column()
+ bfu_unreal_import_location.prop(
+ scene,
+ 'bfu_unreal_import_location',
+ icon='FILE_FOLDER')
+
+ # File path
+ filePath = layout.row()
+ filePath = filePath.column()
+ filePath.prop(scene, 'bfu_export_static_file_path')
+ filePath.prop(scene, 'bfu_export_skeletal_file_path')
+ filePath.prop(scene, 'bfu_export_alembic_file_path')
+ filePath.prop(scene, 'bfu_export_groom_file_path')
+ filePath.prop(scene, 'bfu_export_camera_file_path')
+ filePath.prop(scene, 'bfu_export_spline_file_path')
+ filePath.prop(scene, 'bfu_export_other_file_path')
+
+ # File name
+ fileName = layout.row()
+ fileName = fileName.column()
+ fileName.prop(scene, 'bfu_file_export_log_name', icon='FILE')
+ if addon_prefs.useGeneratedScripts:
+ fileName.prop(
+ scene,
+ 'bfu_file_import_asset_script_name',
+ icon='FILE')
+ fileName.prop(
+ scene,
+ 'bfu_file_import_sequencer_script_name',
+ icon='FILE')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_utils.py b/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_nomenclature/bfu_export_nomenclature_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_export_process/__init__.py b/blender-for-unrealengine/bfu_export_process/__init__.py
new file mode 100644
index 00000000..d9a93ef2
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_process/__init__.py
@@ -0,0 +1,52 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_export_process_props
+from . import bfu_export_process_operators
+from . import bfu_export_process_ui
+from . import bfu_export_process_utils
+
+if "bfu_export_process_props" in locals():
+ importlib.reload(bfu_export_process_props)
+if "bfu_export_process_operators" in locals():
+ importlib.reload(bfu_export_process_operators)
+if "bfu_export_process_ui" in locals():
+ importlib.reload(bfu_export_process_ui)
+if "bfu_export_process_utils" in locals():
+ importlib.reload(bfu_export_process_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_export_process_props.register()
+ bfu_export_process_operators.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_export_process_operators.unregister()
+ bfu_export_process_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_process/bfu_export_process_operators.py b/blender-for-unrealengine/bfu_export_process/bfu_export_process_operators.py
new file mode 100644
index 00000000..876ba06f
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_process/bfu_export_process_operators.py
@@ -0,0 +1,176 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bpl
+from .. import bbpl
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bfu_assets_manager
+from .. import bfu_cached_asset_list
+from .. import bfu_check_potential_error
+from .. import bfu_export
+from .. import bfu_write_text
+
+
+class BFU_OT_ExportForUnrealEngineButton(bpy.types.Operator):
+ bl_label = "Export for Unreal Engine"
+ bl_idname = "object.exportforunreal"
+ bl_description = "Export all assets of this scene."
+
+ def execute(self, context):
+ scene = bpy.context.scene
+
+ def isReadyForExport():
+
+ def GetIfOneTypeCheck():
+ all_assets = bfu_assets_manager.bfu_asset_manager_utils.get_all_asset_class()
+ for assets in all_assets:
+ assets: bfu_assets_manager.bfu_asset_manager_type.BFU_BaseAssetClass
+ if assets.can_export_asset():
+ return True
+
+ if (scene.bfu_use_static_collection_export
+ or scene.bfu_use_anin_export):
+ return True
+ else:
+ return False
+
+ if not bfu_basics.CheckPluginIsActivated("io_scene_fbx"):
+ self.report(
+ {'WARNING'},
+ 'Add-on FBX format is not activated!' +
+ ' Edit > Preferences > Add-ons > And check "FBX format"')
+ return False
+
+ if not GetIfOneTypeCheck():
+ self.report(
+ {'WARNING'},
+ "No asset type is checked.")
+ return False
+
+ final_asset_cache = bfu_cached_asset_list.GetfinalAssetCache()
+ final_asset_list_to_export = final_asset_cache.GetFinalAssetList()
+ if not len(final_asset_list_to_export) > 0:
+ self.report(
+ {'WARNING'},
+ "Not found assets with" +
+ " \"Export recursive\" properties " +
+ "or collection to export.")
+ return False
+
+ if not bpy.data.is_saved:
+ # Primary check if file is saved
+ # to avoid windows PermissionError
+ self.report(
+ {'WARNING'},
+ "Please save this .blend file before export.")
+ return False
+
+ if bbpl.scene_utils.is_tweak_mode():
+ # Need exit Tweakmode because the Animation data is read only.
+ self.report(
+ {'WARNING'},
+ "Exit Tweakmode in NLA Editor. [Tab]")
+ return False
+
+ return True
+
+ if not isReadyForExport():
+ return {'FINISHED'}
+
+ scene.UnrealExportedAssetsList.clear()
+ counter = bpl.utils.CounterTimer()
+ bfu_check_potential_error.bfu_check_utils.process_general_fix()
+ bfu_export.bfu_export_asset.process_export(self)
+ bfu_write_text.WriteAllTextFiles()
+
+ self.report(
+ {'INFO'},
+ "Export of " + str(len(scene.UnrealExportedAssetsList)) + " asset(s) has been finalized in " + counter.get_str_time() + " Look in console for more info.")
+ print(
+ "=========================" +
+ " Exported asset(s) " +
+ "=========================")
+ print("")
+ lines = bfu_write_text.WriteExportLog().splitlines()
+ for line in lines:
+ print(line)
+ print("")
+ print(
+ "=========================" +
+ " ... " +
+ "=========================")
+
+ return {'FINISHED'}
+
+class BFU_OT_CopyImportAssetScriptCommand(bpy.types.Operator):
+ bl_label = "Copy import script (Assets)"
+ bl_idname = "object.copy_importassetscript_command"
+ bl_description = "Copy Import Asset Script command"
+
+ def execute(self, context):
+ scene = context.scene
+ bfu_basics.setWindowsClipboard(bfu_utils.GetImportAssetScriptCommand())
+ self.report(
+ {'INFO'},
+ "command for "+scene.bfu_file_import_asset_script_name +
+ " copied")
+ return {'FINISHED'}
+
+class BFU_OT_CopyImportSequencerScriptCommand(bpy.types.Operator):
+ bl_label = "Copy import script (Sequencer)"
+ bl_idname = "object.copy_importsequencerscript_command"
+ bl_description = "Copy Import Sequencer Script command"
+
+ def execute(self, context):
+ scene = context.scene
+ bfu_basics.setWindowsClipboard(bfu_utils.GetImportSequencerScriptCommand())
+ self.report(
+ {'INFO'},
+ "command for "+scene.bfu_file_import_sequencer_script_name +
+ " copied")
+ return {'FINISHED'}
+
+
+def get_preset_values():
+ preset_values = [
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+ BFU_OT_ExportForUnrealEngineButton,
+ BFU_OT_CopyImportAssetScriptCommand,
+ BFU_OT_CopyImportSequencerScriptCommand,
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
diff --git a/blender-for-unrealengine/bfu_export_process/bfu_export_process_props.py b/blender-for-unrealengine/bfu_export_process/bfu_export_process_props.py
new file mode 100644
index 00000000..5ca05fed
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_process/bfu_export_process_props.py
@@ -0,0 +1,52 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+def get_preset_values():
+ preset_values = [
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_export_process_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Export process")
+ bpy.types.Scene.bfu_script_tool_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Copy Import Script")
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Scene.bfu_script_tool_expanded
+ del bpy.types.Scene.bfu_export_process_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_process/bfu_export_process_ui.py b/blender-for-unrealengine/bfu_export_process/bfu_export_process_ui.py
new file mode 100644
index 00000000..73b20bb8
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_process/bfu_export_process_ui.py
@@ -0,0 +1,64 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import bfu_cached_asset_list
+
+
+def draw_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ scene.bfu_export_process_properties_expanded.draw(layout)
+ if scene.bfu_export_process_properties_expanded.is_expend():
+
+ # Feedback info :
+ final_asset_cache = bfu_cached_asset_list.GetfinalAssetCache()
+ final_asset_list_to_export = final_asset_cache.GetFinalAssetList()
+ AssetNum = len(final_asset_list_to_export)
+ AssetInfo = layout.row().box().split(factor=0.75)
+ AssetFeedback = str(AssetNum) + " Asset(s) will be exported."
+ AssetInfo.label(text=AssetFeedback, icon='INFO')
+ AssetInfo.operator("object.showasset")
+
+ # Export button :
+ checkButton = layout.row(align=True)
+ checkButton.operator("object.checkpotentialerror", icon='FILE_TICK')
+ checkButton.operator("object.openpotentialerror", icon='LOOP_BACK', text="")
+
+ exportButton = layout.row()
+ exportButton.scale_y = 2.0
+ exportButton.operator("object.exportforunreal", icon='EXPORT')
+
+ scene.bfu_script_tool_expanded.draw(layout)
+ if scene.bfu_script_tool_expanded.is_expend():
+ if addon_prefs.useGeneratedScripts:
+ copyButton = layout.row()
+ copyButton.operator("object.copy_importassetscript_command")
+ copyButton.operator("object.copy_importsequencerscript_command")
+ layout.label(text="Click on one of the buttons to copy the import command.", icon='INFO')
+ layout.label(text="Then paste it into the cmd console of unreal.")
+ layout.label(text="You need activate python plugins in Unreal Engine.")
+
+ else:
+ layout.label(text='(Generated scripts are deactivated.)')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_export_process/bfu_export_process_utils.py b/blender-for-unrealengine/bfu_export_process/bfu_export_process_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_export_process/bfu_export_process_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_groom/bfu_groom_type.py b/blender-for-unrealengine/bfu_groom/bfu_groom_type.py
index 34a52122..67bb0f8d 100644
--- a/blender-for-unrealengine/bfu_groom/bfu_groom_type.py
+++ b/blender-for-unrealengine/bfu_groom/bfu_groom_type.py
@@ -30,7 +30,7 @@ def __init__(self):
super().__init__()
pass
- def support_asset_type(self, obj):
+ def support_asset_type(self, obj, details = None):
if obj.bfu_export_as_groom_simulation:
return True
return False
@@ -48,15 +48,20 @@ def get_obj_file_name(self, obj, desired_name="", fileType=".fbx"):
return bfu_basics.ValidFilename(scene.bfu_groom_simulation_prefix_export_name+desired_name+fileType)
return bfu_basics.ValidFilename(scene.bfu_groom_simulation_prefix_export_name+obj.name+fileType)
- def get_obj_export_directory_path(self, obj):
+ def get_obj_export_directory_path(self, obj, absolute = True):
folder_name = bfu_utils.get_export_folder_name(obj)
scene = bpy.context.scene
- dirpath = os.path.join(scene.bfu_export_groom_file_path, folder_name)
+ if(absolute):
+ root_path = bpy.path.abspath(scene.bfu_export_groom_file_path)
+ else:
+ root_path = scene.bfu_export_groom_file_path
+
+ dirpath = os.path.join(root_path, folder_name)
return dirpath
def can_export_asset(self):
scene = bpy.context.scene
- return scene.groom_simulation_export
+ return scene.bfu_use_groom_simulation_export
def can_export_obj_asset(self, obj):
return self.can_export_asset()
diff --git a/blender-for-unrealengine/bfu_import_module/__init__.py b/blender-for-unrealengine/bfu_import_module/__init__.py
index 8950af17..1fd4d242 100644
--- a/blender-for-unrealengine/bfu_import_module/__init__.py
+++ b/blender-for-unrealengine/bfu_import_module/__init__.py
@@ -18,7 +18,8 @@
import importlib
-from . import bps
+from . import bpl
+from . import config
from . import import_module_utils
from . import import_module_unreal_utils
from . import import_module_post_treatment
@@ -26,11 +27,17 @@
from . import asset_import
from . import sequencer_import
from . import sequencer_utils
+from . import bfu_import_animations
from . import bfu_import_materials
+from . import bfu_import_vertex_color
from . import bfu_import_sequencer
+from . import import_module_tasks_class
+from . import import_module_tasks_helper
-if "bps" in locals():
- importlib.reload(bps)
+if "bpl" in locals():
+ importlib.reload(bpl)
+if "config" in locals():
+ importlib.reload(config)
if "import_module_utils" in locals():
importlib.reload(import_module_utils)
if "import_module_unreal_utils" in locals():
@@ -43,15 +50,24 @@
importlib.reload(sequencer_import)
if "sequencer_utils" in locals():
importlib.reload(sequencer_utils)
+if "bfu_import_animations" in locals():
+ importlib.reload(bfu_import_animations)
if "bfu_import_materials" in locals():
importlib.reload(bfu_import_materials)
+if "bfu_import_vertex_color" in locals():
+ importlib.reload(bfu_import_vertex_color)
if "bfu_import_sequencer" in locals():
importlib.reload(bfu_import_sequencer)
+if "import_module_tasks_class" in locals():
+ importlib.reload(import_module_tasks_class)
+if "import_module_tasks_helper" in locals():
+ importlib.reload(import_module_tasks_helper)
-def run_asset_import(assets_data, show_finished_popup=True):
+def run_asset_import(assets_data, show_finished_popup=False):
if asset_import.ready_for_asset_import():
return asset_import.ImportAllAssets(assets_data, show_finished_popup)
-def run_sequencer_import(sequence_data, show_finished_popup=True):
+def run_sequencer_import(sequence_data, show_finished_popup=False):
if sequencer_import.ready_for_sequence_import():
- return sequencer_import.CreateSequencer(sequence_data, show_finished_popup)
\ No newline at end of file
+ return sequencer_import.CreateSequencer(sequence_data, show_finished_popup)
+
diff --git a/blender-for-unrealengine/bfu_import_module/asset_import.py b/blender-for-unrealengine/bfu_import_module/asset_import.py
index 0c63e60d..e7669321 100644
--- a/blender-for-unrealengine/bfu_import_module/asset_import.py
+++ b/blender-for-unrealengine/bfu_import_module/asset_import.py
@@ -18,11 +18,16 @@
import os.path
-from . import bps
+from . import bpl
from . import import_module_utils
from . import import_module_unreal_utils
from . import import_module_post_treatment
+from . import import_module_tasks_class
+from . import import_module_tasks_helper
+from . import bfu_import_animations
from . import bfu_import_materials
+from . import bfu_import_vertex_color
+from . import config
try:
import unreal
@@ -33,26 +38,26 @@
def ready_for_asset_import():
- if import_module_unreal_utils.is_unreal_version_greater_or_equal(4,20): # TO DO: EditorAssetLibrary was added in witch version exactly?
- if not hasattr(unreal, 'EditorAssetLibrary'):
- message = 'WARNING: Editor Scripting Utilities should be activated.' + "\n"
- message += 'Edit > Plugin > Scripting > Editor Scripting Utilities.'
- import_module_unreal_utils.show_warning_message("Editor Scripting Utilities not activated.", message)
- return False
+ if not import_module_unreal_utils.editor_scripting_utilities_active():
+ message = 'WARNING: Editor Scripting Utilities Plugin should be activated.' + "\n"
+ message += 'Edit > Plugin > Scripting > Editor Scripting Utilities.'
+ import_module_unreal_utils.show_warning_message("Editor Scripting Utilities not activated.", message)
+ return False
return True
-def ImportAsset(asset_data):
+def ImportTask(asset_data):
+ asset_type = asset_data["asset_type"]
- if asset_data["asset_type"] == "StaticMesh" or asset_data["asset_type"] == "SkeletalMesh":
+ if asset_type == "StaticMesh" or asset_type == "SkeletalMesh":
if "lod" in asset_data:
if asset_data["lod"] > 0: # Lod should not be imported here so return if lod is not 0.
return "FAIL", None
- if asset_data["asset_type"] == "Alembic":
+ if asset_type == "Alembic":
FileType = "ABC"
else:
FileType = "FBX"
@@ -65,73 +70,54 @@ def GetAdditionalData():
asset_additional_data = GetAdditionalData()
- if asset_data["asset_type"] in ["Animation", "SkeletalMesh"]:
- origin_skeletal_mesh = None
+ if asset_type in ["Animation", "SkeletalMesh"]:
origin_skeleton = None
+ origin_skeletal_mesh = None
- find_asset = unreal.find_asset(asset_data["target_skeleton_ref"])
- if isinstance(find_asset, unreal.Skeleton):
- origin_skeleton = find_asset
- elif isinstance(find_asset, unreal.SkeletalMesh):
- origin_skeletal_mesh = find_asset
- origin_skeleton = find_asset.skeleton
- else:
- origin_skeleton = None
+
+ if "target_skeleton_ref" in asset_data:
+ find_sk_asset = import_module_unreal_utils.load_asset(asset_data["target_skeleton_ref"])
+ if isinstance(find_sk_asset, unreal.Skeleton):
+ origin_skeleton = find_sk_asset
+ elif isinstance(find_sk_asset, unreal.SkeletalMesh):
+ origin_skeleton = find_sk_asset.skeleton
+ origin_skeletal_mesh = find_sk_asset
+
+ if "target_skeletal_mesh_ref" in asset_data:
+ find_skm_asset = import_module_unreal_utils.load_asset(asset_data["target_skeletal_mesh_ref"])
+ if isinstance(find_skm_asset, unreal.SkeletalMesh):
+ origin_skeleton = find_skm_asset.skeleton
+ origin_skeletal_mesh = find_skm_asset
+ elif isinstance(find_skm_asset, unreal.Skeleton):
+ origin_skeletal_mesh = find_skm_asset
- if origin_skeleton is None:
- if asset_data["asset_type"] == "Animation":
+
+ if asset_type == "Animation":
+ if origin_skeleton:
+ print('"target_skeleton_ref": ' + asset_data["target_skeleton_ref"] + "was found:", origin_skeleton)
+ else:
message = "WARNING: Could not find skeleton." + "\n"
message += '"target_skeleton_ref": ' + asset_data["target_skeleton_ref"]
import_module_unreal_utils.show_warning_message("Skeleton not found.", message)
- # docs.unrealengine.com/5.3/en-US/PythonAPI/class/AssetImportTask.html
- task = unreal.AssetImportTask()
+ itask = import_module_tasks_class.ImportTaks()
- def GetStaticMeshImportData() -> unreal.FbxStaticMeshImportData:
- if asset_data["asset_type"] == "StaticMesh":
- return task.get_editor_property('options').static_mesh_import_data
- return None
-
- def GetSkeletalMeshImportData() -> unreal.FbxSkeletalMeshImportData:
- if asset_data["asset_type"] == "SkeletalMesh":
- return task.get_editor_property('options').skeletal_mesh_import_data
- return None
-
- def GetAnimationImportData() -> unreal.FbxAnimSequenceImportData:
- if asset_data["asset_type"] == "Animation":
- return task.get_editor_property('options').anim_sequence_import_data
- return None
-
- def GetAlembicImportData():
- if asset_data["asset_type"] == "Alembic":
- return task.get_editor_property('options')
- return None
-
- def GetMeshImportData():
- if asset_data["asset_type"] == "StaticMesh":
- return GetStaticMeshImportData()
- if asset_data["asset_type"] == "SkeletalMesh":
- return GetSkeletalMeshImportData()
- return None
-
- if asset_data["asset_type"] == "Alembic":
- task.filename = asset_data["abc_path"]
- else:
- task.filename = asset_data["fbx_path"]
- task.destination_path = os.path.normpath(asset_data["full_import_path"]).replace('\\', '/')
- task.automated = True
- # task.automated = False #Debug for show dialog
- task.save = True
- task.replace_existing = True
-
- if asset_data["asset_type"] == "Alembic":
- task.set_editor_property('options', unreal.AbcImportSettings())
+ if asset_type == "Alembic":
+ itask.get_task().filename = asset_data["abc_path"]
else:
- task.set_editor_property('options', unreal.FbxImportUI())
-
+ itask.get_task().filename = asset_data["fbx_path"]
+ itask.get_task().destination_path = os.path.normpath(asset_data["full_import_path"]).replace('\\', '/')
+ itask.get_task().automated = config.automated_import_tasks
+ itask.get_task().save = True
+ itask.get_task().replace_existing = True
+
+ TaskOption = import_module_tasks_helper.init_options_data(asset_type, itask.use_interchange)
+ itask.set_task_option(TaskOption)
+ print("S1")
# Alembic
- alembic_import_data = GetAlembicImportData()
- if alembic_import_data:
+
+ if asset_type == "Alembic":
+ alembic_import_data = itask.get_abc_import_settings()
alembic_import_data.static_mesh_settings.set_editor_property("merge_meshes", True)
alembic_import_data.set_editor_property("import_type", unreal.AlembicImportType.SKELETAL)
alembic_import_data.conversion_settings.set_editor_property("flip_u", False)
@@ -142,168 +128,225 @@ def GetMeshImportData():
alembic_import_data.conversion_settings.set_editor_property("scale", ue_scale)
alembic_import_data.conversion_settings.set_editor_property("rotation", rotation)
- # Vertex color
- vertex_override_color = import_module_unreal_utils.get_vertex_override_color(asset_additional_data)
- vertex_color_import_option = import_module_unreal_utils.get_vertex_color_import_option(asset_additional_data)
-
# #################################[Change]
# unreal.FbxImportUI
# https://docs.unrealengine.com/4.26/en-US/PythonAPI/class/FbxImportUI.html
+ print("S1.5")
# Import transform
- anim_sequence_import_data = GetAnimationImportData()
- if anim_sequence_import_data:
+ if itask.use_interchange:
+ animation_pipeline = itask.get_igap_animation()
+ if "do_not_import_curve_with_zero" in asset_data:
+ animation_pipeline.set_editor_property('do_not_import_curve_with_zero', asset_data["do_not_import_curve_with_zero"])
+
+ else:
+ anim_sequence_import_data = itask.get_animation_import_data()
anim_sequence_import_data.import_translation = unreal.Vector(0, 0, 0)
if "do_not_import_curve_with_zero" in asset_data:
anim_sequence_import_data.set_editor_property('do_not_import_curve_with_zero', asset_data["do_not_import_curve_with_zero"])
- # Vertex color
- if vertex_color_import_option and GetMeshImportData():
- GetMeshImportData().set_editor_property('vertex_color_import_option', vertex_color_import_option)
+ print("S2")
- if vertex_override_color and GetMeshImportData():
- GetMeshImportData().set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
-
- if asset_data["asset_type"] == "Alembic":
- task.get_editor_property('options').set_editor_property('import_type', unreal.AlembicImportType.SKELETAL)
+ if asset_type in ["SkeletalMesh", "StaticMesh"]:
+ # Vertex color
+ bfu_import_vertex_color.bfu_import_vertex_color_utils.apply_import_settings(itask, asset_type, asset_additional_data)
+ if asset_type == "Alembic":
+ print("S2.1")
+ itask.get_abc_import_settings().set_editor_property('import_type', unreal.AlembicImportType.SKELETAL)
+
else:
- if asset_data["asset_type"] == "Animation" or asset_data["asset_type"] == "SkeletalMesh":
- if origin_skeleton:
- task.get_editor_property('options').set_editor_property('Skeleton', origin_skeleton)
+
+ print("S2.2")
+ if asset_type == "Animation" :
+ if itask.use_interchange:
+ if origin_skeleton:
+ itask.get_igap_skeletal_mesh().set_editor_property('Skeleton', origin_skeleton)
+ itask.get_igap_skeletal_mesh().set_editor_property('import_only_animations', True)
+ print("S2.25")
+ else:
+ fail_reason = 'Skeleton ' + asset_data["target_skeleton_ref"] + ' Not found for ' + asset_data["asset_name"] + ' asset.'
+ return fail_reason, None
+
else:
- if asset_data["asset_type"] == "Animation":
+ if origin_skeleton:
+ itask.get_fbx_import_ui().set_editor_property('Skeleton', origin_skeleton)
+ else:
fail_reason = 'Skeleton ' + asset_data["target_skeleton_ref"] + ' Not found for ' + asset_data["asset_name"] + ' asset.'
return fail_reason, None
+
+ print("S2.3")
+ if asset_type == "SkeletalMesh":
+ if itask.use_interchange:
+ if origin_skeleton:
+ itask.get_igap_skeletal_mesh().set_editor_property('Skeleton', origin_skeleton)
else:
print("Skeleton is not set, a new skeleton asset will be created...")
+
+ else:
+ if origin_skeleton:
+ itask.get_fbx_import_ui().set_editor_property('Skeleton', origin_skeleton)
+ else:
+ print("Skeleton is not set, a new skeleton asset will be created...")
+
+
+ print("S3")
+ # Set Asset Type
+ if itask.use_interchange:
+ if asset_type == "StaticMesh":
+ itask.get_igap_common_mesh().set_editor_property('force_all_mesh_as_type', unreal.InterchangeForceMeshType.IFMT_STATIC_MESH)
+ if asset_type == "SkeletalMesh":
+ itask.get_igap_common_mesh().set_editor_property('force_all_mesh_as_type', unreal.InterchangeForceMeshType.IFMT_SKELETAL_MESH)
+ if asset_type == "Animation":
+ itask.get_igap_common_mesh().set_editor_property('force_all_mesh_as_type', unreal.InterchangeForceMeshType.IFMT_NONE)
+ else:
+ itask.get_igap_common_mesh().set_editor_property('force_all_mesh_as_type', unreal.InterchangeForceMeshType.IFMT_NONE)
-
- if asset_data["asset_type"] == "StaticMesh":
- task.get_editor_property('options').set_editor_property('original_import_type', unreal.FBXImportType.FBXIT_STATIC_MESH)
- elif asset_data["asset_type"] == "Animation":
- task.get_editor_property('options').set_editor_property('original_import_type', unreal.FBXImportType.FBXIT_ANIMATION)
else:
- task.get_editor_property('options').set_editor_property('original_import_type', unreal.FBXImportType.FBXIT_SKELETAL_MESH)
-
- if asset_data["asset_type"] == "Animation":
- task.get_editor_property('options').set_editor_property('import_materials', False)
+ if asset_type == "StaticMesh":
+ itask.get_fbx_import_ui().set_editor_property('original_import_type', unreal.FBXImportType.FBXIT_STATIC_MESH)
+ elif asset_type == "Animation":
+ itask.get_fbx_import_ui().set_editor_property('original_import_type', unreal.FBXImportType.FBXIT_ANIMATION)
+ else:
+ itask.get_fbx_import_ui().set_editor_property('original_import_type', unreal.FBXImportType.FBXIT_SKELETAL_MESH)
+ print("S4")
+ # Set Material Use
+ if itask.use_interchange:
+ if asset_type == "Animation":
+ itask.get_igap_material().set_editor_property('import_materials', False)
+ else:
+ itask.get_igap_material().set_editor_property('import_materials', True)
else:
- task.get_editor_property('options').set_editor_property('import_materials', True)
-
- task.get_editor_property('options').set_editor_property('import_textures', False)
-
- if asset_data["asset_type"] == "Animation":
-
- task.get_editor_property('options').set_editor_property('import_animations', True)
- task.get_editor_property('options').set_editor_property('import_mesh', False)
- task.get_editor_property('options').set_editor_property('create_physics_asset',False)
+ if asset_type == "Animation":
+ itask.get_fbx_import_ui().set_editor_property('import_materials', False)
+ else:
+ itask.get_fbx_import_ui().set_editor_property('import_materials', True)
+ print("S5")
+ # Set Texture Use
+ if itask.use_interchange:
+ itask.get_igap_texture().set_editor_property('import_textures', False)
+ else:
+ itask.get_fbx_import_ui().set_editor_property('import_textures', False)
+
+ print("S6")
+ if itask.use_interchange:
+ if asset_type == "Animation":
+ itask.get_igap_animation().set_editor_property('import_animations', True)
+ itask.get_igap_mesh().set_editor_property('import_skeletal_meshes', False)
+ itask.get_igap_mesh().set_editor_property('import_static_meshes', False)
+ itask.get_igap_mesh().set_editor_property('create_physics_asset',False)
+ else:
+ itask.get_igap_animation().set_editor_property('import_animations', False)
+ itask.get_igap_mesh().set_editor_property('import_skeletal_meshes', True)
+ itask.get_igap_mesh().set_editor_property('import_static_meshes', True)
+ if "create_physics_asset" in asset_data:
+ itask.get_igap_mesh().set_editor_property('create_physics_asset', asset_data["create_physics_asset"])
else:
- task.get_editor_property('options').set_editor_property('import_animations', False)
- task.get_editor_property('options').set_editor_property('import_mesh', True)
- if "create_physics_asset" in asset_data:
- task.get_editor_property('options').set_editor_property('create_physics_asset', asset_data["create_physics_asset"])
+ if asset_type == "Animation":
+ itask.get_fbx_import_ui().set_editor_property('import_as_skeletal',True)
+ itask.get_fbx_import_ui().set_editor_property('import_animations', True)
+ itask.get_fbx_import_ui().set_editor_property('import_mesh', False)
+ itask.get_fbx_import_ui().set_editor_property('create_physics_asset',False)
+ else:
+ itask.get_fbx_import_ui().set_editor_property('import_animations', False)
+ itask.get_fbx_import_ui().set_editor_property('import_mesh', True)
+ if "create_physics_asset" in asset_data:
+ itask.get_fbx_import_ui().set_editor_property('create_physics_asset', asset_data["create_physics_asset"])
# unreal.FbxMeshImportData
-
- bfu_import_materials.bfu_import_materials_utils.update_task_with_material_data(task, asset_data)
-
-
- if asset_data["asset_type"] == "StaticMesh":
- # unreal.FbxStaticMeshImportData
- task.get_editor_property('options').static_mesh_import_data.set_editor_property('combine_meshes', True)
- if "auto_generate_collision" in asset_data:
- task.get_editor_property('options').static_mesh_import_data.set_editor_property('auto_generate_collision', asset_data["auto_generate_collision"])
+ print("S7")
+ bfu_import_materials.bfu_import_materials_utils.apply_import_settings(itask, asset_data)
+
+ print("S8")
+ if itask.use_interchange:
+ itask.get_igap_mesh().set_editor_property('combine_static_meshes', True)
+ itask.get_igap_mesh().set_editor_property('combine_skeletal_meshes', True)
+ # @TODO auto_generate_collision Removed with InterchangeGenericAssetsPipeline?
+ # I yes need also remove auto_generate_collision from the addon propertys.
if "static_mesh_lod_group" in asset_data:
- if asset_data["static_mesh_lod_group"]:
- task.get_editor_property('options').static_mesh_import_data.set_editor_property('static_mesh_lod_group', asset_data["static_mesh_lod_group"])
+ lod_group = asset_data["static_mesh_lod_group"]
+ if lod_group:
+ itask.get_igap_mesh().set_editor_property('lod_group', lod_group)
if "generate_lightmap_u_vs" in asset_data:
- task.get_editor_property('options').static_mesh_import_data.set_editor_property('generate_lightmap_u_vs', asset_data["generate_lightmap_u_vs"])
+ itask.get_igap_mesh().set_editor_property('generate_lightmap_u_vs', asset_data["generate_lightmap_u_vs"])
+ itask.get_igap_mesh().set_editor_property('import_morph_targets', True)
- if asset_data["asset_type"] == "SkeletalMesh" or asset_data["asset_type"] == "Animation":
- # unreal.FbxSkeletalMeshImportData
- task.get_editor_property('options').skeletal_mesh_import_data.set_editor_property('import_morph_targets', True)
- task.get_editor_property('options').skeletal_mesh_import_data.set_editor_property('convert_scene', True)
- task.get_editor_property('options').skeletal_mesh_import_data.set_editor_property('normal_import_method', unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS)
+ else:
+ if asset_type == "StaticMesh":
+ # unreal.FbxStaticMeshImportData
+ itask.get_static_mesh_import_data().set_editor_property('combine_meshes', True)
+ if "auto_generate_collision" in asset_data:
+ itask.get_static_mesh_import_data().set_editor_property('auto_generate_collision', asset_data["auto_generate_collision"])
+ if "static_mesh_lod_group" in asset_data:
+ lod_group = asset_data["static_mesh_lod_group"]
+ if lod_group:
+ itask.get_static_mesh_import_data().set_editor_property('static_mesh_lod_group', lod_group)
+ if "generate_lightmap_u_vs" in asset_data:
+ itask.get_static_mesh_import_data().set_editor_property('generate_lightmap_u_vs', asset_data["generate_lightmap_u_vs"])
+
+ if asset_type == "SkeletalMesh" or asset_type == "Animation":
+ # unreal.FbxSkeletalMeshImportData
+ itask.get_skeletal_mesh_import_data().set_editor_property('import_morph_targets', True)
+ itask.get_skeletal_mesh_import_data().set_editor_property('convert_scene', True)
+ itask.get_skeletal_mesh_import_data().set_editor_property('normal_import_method', unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS)
# ###############[ pre import ]################
-
+ print("S9")
# Check is the file alredy exit
if asset_additional_data:
if "preview_import_path" in asset_additional_data:
- task_asset_full_path = task.destination_path+"/"+asset_additional_data["preview_import_path"]+"."+asset_additional_data["preview_import_path"]
- find_asset = unreal.find_asset(task_asset_full_path)
- if find_asset:
+ task_asset_full_path = itask.get_task().destination_path+"/"+asset_additional_data["preview_import_path"]+"."+asset_additional_data["preview_import_path"]
+ find_target_asset = import_module_unreal_utils.load_asset(task_asset_full_path)
+ if find_target_asset:
# Vertex color
-
- asset_import_data = find_asset.get_editor_property('asset_import_data')
- if vertex_color_import_option:
- asset_import_data.set_editor_property('vertex_color_import_option', vertex_color_import_option)
-
- if vertex_override_color:
- asset_import_data.set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
-
+ bfu_import_vertex_color.bfu_import_vertex_color_utils.apply_asset_settings(itask, find_target_asset, asset_additional_data)
+
# ###############[ import asset ]################
-
- if asset_data["asset_type"] == "Animation":
+ print("S10")
+ if asset_type == "Animation":
# For animation the script will import a skeletal mesh and remove after.
- # If the skeletal mesh alredy exist try to remove.
-
-
- AssetName = asset_data["asset_name"]
- AssetName = import_module_unreal_utils.ValidUnrealAssetsName(AssetName)
- AssetPath = "SkeletalMesh'"+asset_data["full_import_path"]+"/"+AssetName+"."+AssetName+"'"
-
- if unreal.EditorAssetLibrary.does_asset_exist(AssetPath):
- oldAsset = unreal.EditorAssetLibrary.find_asset_data(AssetPath)
- if oldAsset.asset_class == "SkeletalMesh":
- unreal.EditorAssetLibrary.delete_asset(AssetPath)
+ # If the skeletal mesh already exists, try to remove it.
- unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
+ asset_name = import_module_unreal_utils.valid_unreal_asset_name(asset_data["asset_name"])
+ asset_path = f"SkeletalMesh'{asset_data['full_import_path']}/{asset_name}.{asset_name}'"
- if len(task.imported_object_paths) > 0:
- asset_path = task.imported_object_paths[0]
- asset = unreal.find_asset(asset_path)
- else:
- asset = None
+ if unreal.EditorAssetLibrary.does_asset_exist(asset_path):
+ old_asset = unreal.EditorAssetLibrary.find_asset_data(asset_path)
+ if old_asset.asset_class == "SkeletalMesh":
+ unreal.EditorAssetLibrary.delete_asset(asset_path)
- if asset is None:
+ print("S10.5")
+ itask.import_asset_task()
+ print("S11")
+
+ if len(itask.get_imported_assets()) == 0:
fail_reason = 'Error zero imported object for: ' + asset_data["asset_name"]
return fail_reason, None
-
+
+ print("S11.5")
if asset_data["asset_type"] == "Animation":
- # For animation remove the extra mesh
- if type(asset) is not unreal.AnimSequence:
- p = task.imported_object_paths[0]
- animAssetName = p.split('.')[0]+'_anim.'+p.split('.')[1]+'_anim'
- animAssetNameDesiredPath = p.split('.')[0]+'.'+p.split('.')[1]
- animAsset = unreal.find_asset(animAssetName)
- if animAsset is not None:
- unreal.EditorAssetLibrary.delete_asset(p)
- unreal.EditorAssetLibrary.rename_asset(animAssetName, animAssetNameDesiredPath)
- asset = animAsset
- else:
- fail_reason = 'animAsset ' + asset_data["asset_name"] + ' not found for after inport: ' + animAssetName
- return fail_reason, None
+ bfu_import_animations.bfu_import_animations_utils.apply_post_import_assets_changes(itask, asset_data)
+ print("S12")
# ###############[ Post treatment ]################
- asset_import_data = asset.get_editor_property('asset_import_data')
- if asset_data["asset_type"] == "StaticMesh":
+
+
+ if asset_type == "StaticMesh":
if "static_mesh_lod_group" in asset_data:
if asset_data["static_mesh_lod_group"]:
- asset.set_editor_property('lod_group', asset_data["static_mesh_lod_group"])
+ itask.get_imported_static_mesh().set_editor_property('lod_group', asset_data["static_mesh_lod_group"])
if "use_custom_light_map_resolution" in asset_data:
if asset_data["use_custom_light_map_resolution"]:
if "light_map_resolution" in asset_data:
- asset.set_editor_property('light_map_resolution', asset_data["light_map_resolution"])
- build_settings = unreal.EditorStaticMeshLibrary.get_lod_build_settings(asset, 0)
+ itask.get_imported_static_mesh().set_editor_property('light_map_resolution', asset_data["light_map_resolution"])
+ build_settings = unreal.EditorStaticMeshLibrary.get_lod_build_settings(itask.get_imported_static_mesh(), 0)
build_settings.min_lightmap_resolution = asset_data["light_map_resolution"]
- unreal.EditorStaticMeshLibrary.set_lod_build_settings(asset, 0, build_settings)
+ unreal.EditorStaticMeshLibrary.set_lod_build_settings(itask.get_imported_static_mesh(), 0, build_settings)
if "collision_trace_flag" in asset_data:
- collision_data = asset.get_editor_property('body_setup')
+ collision_data = itask.get_imported_static_mesh().get_editor_property('body_setup')
if collision_data:
if asset_data["collision_trace_flag"] == "CTF_UseDefault":
collision_data.set_editor_property('collision_trace_flag', unreal.CollisionTraceFlag.CTF_USE_DEFAULT)
@@ -314,31 +357,47 @@ def GetMeshImportData():
elif asset_data["collision_trace_flag"] == "CTF_UseComplexAsSimple":
collision_data.set_editor_property('collision_trace_flag', unreal.CollisionTraceFlag.CTF_USE_COMPLEX_AS_SIMPLE)
+ print("S13")
+ if asset_type == "SkeletalMesh":
+ if origin_skeleton is None:
+ # Unreal create a new skeleton when no skeleton was selected, so addon rename it.
+ skeleton = itask.get_imported_skeleton()
+ if skeleton:
+ unreal.EditorAssetLibrary.rename_asset(skeleton.get_path_name(), asset_data["target_skeleton_ref"])
+
+ print("S13.5")
+ if itask.use_interchange:
+ if asset_type == "StaticMesh":
+ if "generate_lightmap_u_vs" in asset_data:
+ mesh_pipeline = itask.get_imported_static_mesh().get_editor_property('asset_import_data').get_pipelines()[0].get_editor_property('mesh_pipeline')
+ mesh_pipeline.set_editor_property('generate_lightmap_u_vs', asset_data["generate_lightmap_u_vs"]) # Import data
+ unreal.EditorStaticMeshLibrary.set_generate_lightmap_uv(itask.get_imported_static_mesh(), asset_data["generate_lightmap_u_vs"]) # Build settings at lod
+
+ if asset_type == "SkeletalMesh":
+ if "enable_skeletal_mesh_per_poly_collision" in asset_data:
+ itask.get_imported_skeletal_mesh().set_editor_property('enable_per_poly_collision', asset_data["enable_skeletal_mesh_per_poly_collision"])
+
+ else:
+ if asset_type == "StaticMesh":
+ asset_import_data = itask.get_imported_static_mesh().get_editor_property('asset_import_data')
+ if "generate_lightmap_u_vs" in asset_data:
+ asset_import_data.set_editor_property('generate_lightmap_u_vs', asset_data["generate_lightmap_u_vs"]) # Import data
+ unreal.EditorStaticMeshLibrary.set_generate_lightmap_uv(itask.get_imported_static_mesh(), asset_data["generate_lightmap_u_vs"]) # Build settings at lod
- if asset_data["asset_type"] == "StaticMesh":
- if "generate_lightmap_u_vs" in asset_data:
- asset_import_data.set_editor_property('generate_lightmap_u_vs', asset_data["generate_lightmap_u_vs"]) # Import data
- unreal.EditorStaticMeshLibrary.set_generate_lightmap_uv(asset, asset_data["generate_lightmap_u_vs"]) # Build settings at lod
+ elif asset_type == "SkeletalMesh":
+ asset_import_data = itask.get_imported_skeletal_mesh().get_editor_property('asset_import_data')
+ asset_import_data.set_editor_property('normal_import_method', unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS)
- if asset_data["asset_type"] == "SkeletalMesh":
- asset_import_data.set_editor_property('normal_import_method', unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS_AND_TANGENTS)
- if origin_skeleton is None:
- #Unreal create a new skeleton when no skeleton was selected, so addon rename it.
- p = task.imported_object_paths[0]
- old_skeleton_name = p.split('.')[0]+'_Skeleton.'+p.split('.')[1]+'_Skeleton'
- new_skeleton_name = asset_data["target_skeleton_ref"]
- unreal.EditorAssetLibrary.rename_asset(old_skeleton_name, new_skeleton_name)
-
- if "enable_skeletal_mesh_per_poly_collision" in asset_data:
- asset.set_editor_property('enable_per_poly_collision', asset_data["enable_skeletal_mesh_per_poly_collision"])
+ if "enable_skeletal_mesh_per_poly_collision" in asset_data:
+ itask.get_imported_skeletal_mesh().set_editor_property('enable_per_poly_collision', asset_data["enable_skeletal_mesh_per_poly_collision"])
-
+ print("S14")
# Socket
- if asset_data["asset_type"] == "SkeletalMesh":
+ if asset_type == "SkeletalMesh":
# Import the SkeletalMesh socket(s)
sockets_to_add = asset_additional_data["Sockets"]
for socket in sockets_to_add:
- old_socket = asset.find_socket(socket["SocketName"])
+ old_socket = itask.get_imported_skeletal_mesh().find_socket(socket["SocketName"])
if old_socket:
# Edit socket
pass
@@ -358,30 +417,47 @@ def GetMeshImportData():
# NEED UNREAL ENGINE IMPLEMENTATION IN PYTHON API.
# skeleton.add_socket(new_socket)
+ print("S15")
# Lod
- if asset_data["asset_type"] == "StaticMesh":
- import_module_post_treatment.set_static_mesh_lods(asset, asset_data, asset_additional_data)
+ if asset_type == "StaticMesh":
+ import_module_post_treatment.set_static_mesh_lods(itask.get_imported_static_mesh(), itask.get_task_options(), asset_data, asset_additional_data)
- if asset_data["asset_type"] == "SkeletalMesh":
- import_module_post_treatment.set_skeletal_mesh_lods(asset, asset_data, asset_additional_data)
+ print("S15.1")
+ if asset_type == "SkeletalMesh":
+ import_module_post_treatment.set_skeletal_mesh_lods(itask.get_imported_skeletal_mesh(), itask.get_task_options(), asset_data, asset_additional_data)
+ print("S15.2")
# Preview mesh
- if asset_data["asset_type"] == "Animation":
- import_module_post_treatment.set_sequence_preview_skeletal_mesh(asset, origin_skeletal_mesh)
-
- # Vertex color
- if vertex_override_color:
- asset_import_data.set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
-
- if vertex_color_import_option:
- asset_import_data.set_editor_property('vertex_color_import_option', vertex_color_import_option)
-
+ if asset_type == "Animation":
+ import_module_post_treatment.set_sequence_preview_skeletal_mesh(itask.get_imported_anim_sequence(), origin_skeletal_mesh)
+
+ print("S15.3")
+ if asset_type in ["SkeletalMesh", "StaticMesh"]:
+ # Vertex color
+ bfu_import_vertex_color.bfu_import_vertex_color_utils.apply_asset_settings(itask, itask.get_imported_static_mesh(), asset_additional_data)
+ bfu_import_vertex_color.bfu_import_vertex_color_utils.apply_asset_settings(itask, itask.get_imported_skeletal_mesh(), asset_additional_data)
+
+ print("S15.4")
+ if asset_type == "Alembic":
+ pass
+ # @TODO Need to found how create an physical asset, generate bodies, and assign it.
+ """
+ skeletal_mesh_path = itask.GetImportedSkeletalMeshAsset().get_path_name()
+ path = skeletal_mesh_path.rsplit('/', 1)[0]
+ name = skeletal_mesh_path.rsplit('/', 1)[1] + "_Physics"
+
+ physical_asset_factory = unreal.PhysicsAssetFactory()
+ physical_asset = unreal.AssetToolsHelpers.get_asset_tools().create_asset(
+ asset_name=name,
+ package_path=path,
+ asset_class=unreal.PhysicsAsset,
+ factory=physical_asset_factory
+ )
+ """
+
+ print("S16")
# #################################[EndChange]
- if asset_data["asset_type"] == "StaticMesh" or asset_data["asset_type"] == "SkeletalMesh":
- unreal.EditorAssetLibrary.save_loaded_asset(asset)
- return "SUCCESS", asset
-
-
+ return "SUCCESS", itask.get_imported_assets()
def ImportAllAssets(assets_data, show_finished_popup=True):
ImportedList = []
@@ -394,13 +470,14 @@ def GetAssetByType(type):
target_assets.append(asset)
return target_assets
- def PrepareImportAsset(asset_data):
+ def PrepareImportTask(asset_data):
counter = str(len(ImportedList)+1) + "/" + str(len(assets_data["assets"]))
print("Import asset " + counter + ": ", asset_data["asset_name"])
-
- result, asset = ImportAsset(asset_data)
+ print("S0")
+ result, assets = ImportTask(asset_data)
+ print("S17")
if result == "SUCCESS":
- ImportedList.append([asset, asset_data["asset_type"]])
+ ImportedList.append([assets, asset_data["asset_type"]])
else:
ImportFailList.append(result)
@@ -408,18 +485,18 @@ def PrepareImportAsset(asset_data):
# Process import
print('========================= Import started ! =========================')
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
# Import assets with a specific order
for asset_data in GetAssetByType("Alembic"):
- PrepareImportAsset(asset_data)
+ PrepareImportTask(asset_data)
for asset_data in GetAssetByType("StaticMesh"):
- PrepareImportAsset(asset_data)
+ PrepareImportTask(asset_data)
for asset_data in GetAssetByType("SkeletalMesh"):
- PrepareImportAsset(asset_data)
+ PrepareImportTask(asset_data)
for asset_data in GetAssetByType("Animation"):
- PrepareImportAsset(asset_data)
+ PrepareImportTask(asset_data)
print('========================= Full import completed ! =========================')
@@ -428,15 +505,17 @@ def PrepareImportAsset(asset_data):
SkeletalMesh_ImportedList = []
Alembic_ImportedList = []
Animation_ImportedList = []
- for asset in ImportedList:
- if asset[1] == 'StaticMesh':
- StaticMesh_ImportedList.append(asset[0])
- elif asset[1] == 'SkeletalMesh':
- SkeletalMesh_ImportedList.append(asset[0])
- elif asset[1] == 'Alembic':
- Alembic_ImportedList.append(asset[0])
+ for inport_data in ImportedList:
+ assets = inport_data[0]
+ source_asset_type = inport_data[1]
+ if source_asset_type == 'StaticMesh':
+ StaticMesh_ImportedList.append(assets)
+ elif source_asset_type == 'SkeletalMesh':
+ SkeletalMesh_ImportedList.append(assets)
+ elif source_asset_type == 'Alembic':
+ Alembic_ImportedList.append(assets)
else:
- Animation_ImportedList.append(asset[0])
+ Animation_ImportedList.append(assets)
import_log = []
import_log.append('Imported StaticMesh: '+str(len(StaticMesh_ImportedList)))
@@ -453,8 +532,9 @@ def PrepareImportAsset(asset_data):
# Select asset(s) in content browser
PathList = []
- for asset in (StaticMesh_ImportedList + SkeletalMesh_ImportedList + Alembic_ImportedList + Animation_ImportedList):
- PathList.append(asset.get_path_name())
+ for assets in (StaticMesh_ImportedList + SkeletalMesh_ImportedList + Alembic_ImportedList + Animation_ImportedList):
+ for asset in assets:
+ PathList.append(asset.get_path_name())
unreal.EditorAssetLibrary.sync_browser_to_objects(PathList)
print('=========================')
diff --git a/blender-for-unrealengine/bfu_import_module/asset_import_script.py b/blender-for-unrealengine/bfu_import_module/asset_import_script.py
index b529a59b..dcfd6d3b 100644
--- a/blender-for-unrealengine/bfu_import_module/asset_import_script.py
+++ b/blender-for-unrealengine/bfu_import_module/asset_import_script.py
@@ -1,65 +1,30 @@
-# This script was generated with the addons Blender for UnrealEngine : https://github.com/xavier150/Blender-For-UnrealEngine-Addons
-# It will import into Unreal Engine all the assets of type StaticMesh, SkeletalMesh, Animation and Pose
-# The script must be used in Unreal Engine Editor with Python plugins : https://docs.unrealengine.com/en-US/Engine/Editor/ScriptingAndAutomation/Python
-# Use this command in Unreal cmd consol: py "[ScriptLocation]\asset_import_script.py"
+# This script was generated with the addons Unreal Engine Assets Exporter.
+# This script should be run in Unreal Engine to import into Unreal Engine 4 and 5 assets.
+# The assets are exported from from Unreal Engine Assets Exporter. More detail here. https://github.com/xavier150/Blender-For-UnrealEngine-Addons
+# Use the following command in Unreal Engine cmd consol to import assets:
+# py "[ScriptLocation]\asset_import_script.py"
import importlib
-import sys
+import importlib.util
import os
-import json
+from . import import_module_utils
-def JsonLoad(json_file):
- # Changed in Python 3.9: The keyword argument encoding has been removed.
- if sys.version_info >= (3, 9):
- return json.load(json_file)
- else:
- return json.load(json_file, encoding="utf8")
-
-def JsonLoadFile(json_file_path):
- if sys.version_info[0] < 3:
- with open(json_file_path, "r") as json_file:
- return JsonLoad(json_file)
- else:
- with open(json_file_path, "r", encoding="utf8") as json_file:
- return JsonLoad(json_file)
-
-def load_module(import_module_path):
- # Import and run the module
- module_name = os.path.basename(import_module_path).replace('.py', '')
- module_dir = os.path.dirname(import_module_path)
-
- if module_dir not in sys.path:
- sys.path.append(module_dir)
-
- imported_module = importlib.import_module(module_name)
- importlib.reload(imported_module)
-
- # Assuming the module has a main function to run
- if hasattr(imported_module, 'main'):
- imported_module.main()
-
- return imported_module, module_name
-
-def unload_module(module_name):
- # Vérifier si le module est dans sys.modules
- if module_name in sys.modules:
- # Récupérer la référence du module
- module = sys.modules[module_name]
- # Supprimer la référence globale
- del sys.modules[module_name]
- del module
def RunImportScriptWithJsonData():
# Prepare process import
json_data_file = 'ImportAssetData.json'
dir_path = os.path.dirname(os.path.realpath(__file__))
-
- import_assets_data = JsonLoadFile(os.path.join(dir_path, json_data_file))
+ import_file_path = os.path.join(dir_path, json_data_file)
+ assets_data = import_module_utils.JsonLoadFile(import_file_path)
- import_module_path = import_assets_data["info"]["import_modiule_path"] # Module to run
- imported_module, module_name = load_module(import_module_path)
- imported_module.run_asset_import(import_assets_data, False)
- unload_module(module_name)
+ file_path = os.path.join(assets_data["info"]["addon_path"],'run_unreal_import_script.py')
+ spec = importlib.util.spec_from_file_location("__import_assets__", file_path)
+ module = importlib.util.module_from_spec(spec)
+
+ # Run script module function
+ spec.loader.exec_module(module)
+ module.run_from_asset_import_script(import_file_path)
if __name__ == "__main__":
RunImportScriptWithJsonData()
+
diff --git a/blender-for-unrealengine/bfu_import_module/bfu_import_animations/__init__.py b/blender-for-unrealengine/bfu_import_module/bfu_import_animations/__init__.py
new file mode 100644
index 00000000..e2759498
--- /dev/null
+++ b/blender-for-unrealengine/bfu_import_module/bfu_import_animations/__init__.py
@@ -0,0 +1,24 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import importlib
+
+from . import bfu_import_animations_utils
+
+if "bfu_import_animations_utils" in locals():
+ importlib.reload(bfu_import_animations_utils)
diff --git a/blender-for-unrealengine/bfu_import_module/bfu_import_animations/bfu_import_animations_utils.py b/blender-for-unrealengine/bfu_import_module/bfu_import_animations/bfu_import_animations_utils.py
new file mode 100644
index 00000000..e79bdf54
--- /dev/null
+++ b/blender-for-unrealengine/bfu_import_module/bfu_import_animations/bfu_import_animations_utils.py
@@ -0,0 +1,85 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+from .. import import_module_unreal_utils
+from .. import import_module_tasks_class
+
+try:
+ import unreal
+except ImportError:
+ import unreal_engine as unreal
+
+
+
+def apply_post_import_assets_changes(itask: import_module_tasks_class.ImportTaks, asset_data):
+ """Applies post-import changes based on whether Interchange or FBX is used."""
+ if itask.use_interchange:
+ apply_interchange_post_import(itask, asset_data)
+ else:
+ apply_fbxui_post_import(itask, asset_data)
+
+def apply_interchange_post_import(itask: import_module_tasks_class.ImportTaks, asset_data):
+ # When Import FBX animation using the Interchange it create Anim_0_Root and Root_MorphAnim_0.
+ # I'm not sure if that a bug... So remove I Root_MorphAnim_0 or other animations and I rename Anim_0_Root.
+ asset_paths_to_remove = []
+ main_anim_path = None
+ for imported_asset in itask.get_imported_assets():
+ if type(imported_asset) is unreal.AnimSequence:
+ anim_asset_path = imported_asset.get_path_name()
+ path, name = anim_asset_path.rsplit('/', 1)
+ if name == "Anim_0_Root.Anim_0_Root":
+ main_anim_path = imported_asset.get_path_name()
+ else:
+ asset_paths_to_remove.append(imported_asset.get_path_name())
+
+ # Remove wrong animation assets
+ for asset_path in asset_paths_to_remove:
+ unreal.EditorAssetLibrary.delete_asset(asset_path)
+
+ # Rename correct animation asset
+ if main_anim_path:
+ anim_asset_path = imported_asset.get_path_name()
+ path, name = anim_asset_path.rsplit('/', 1)
+ new_anim_path = path + "/" + asset_data["asset_name"] + "." + asset_data["asset_name"]
+ unreal.EditorAssetLibrary.rename_asset(main_anim_path, new_anim_path)
+ else:
+ fail_reason = f"animAsset {asset_data['asset_name']} not found after import: {main_anim_path}"
+ return fail_reason, None
+
+def apply_fbxui_post_import(itask: import_module_tasks_class.ImportTaks, asset_data):
+ """Applies post-import changes for FBX pipeline."""
+ # When Import FBX animation using FbxImportUI it create a skeletal mesh and the animation at this side.
+ # I'm not sure if that a bug too... So remove the extra mesh
+ imported_anim_sequence = itask.get_imported_anim_sequence()
+ if imported_anim_sequence is None:
+ # If Imported Anim Sequence is None it maybe imported the asset as Skeletal Mesh.
+ skeleta_mesh_assset = itask.get_imported_skeletal_mesh()
+ if skeleta_mesh_assset:
+ # If Imported as Skeletal Mesh Search the real Anim Sequence
+ path = skeleta_mesh_assset.get_path_name()
+ base_name = path.split('.')[0]
+ anim_asset_name = f"{base_name}_anim.{base_name.split('/')[-1]}_anim"
+ desired_anim_path = f"{base_name}.{base_name.split('/')[-1]}"
+ animAsset = import_module_unreal_utils.load_asset(anim_asset_name)
+ if animAsset is not None:
+ # Remove the imported skeletal mesh and rename te anim sequence with his correct name.
+ unreal.EditorAssetLibrary.delete_asset(path)
+ unreal.EditorAssetLibrary.rename_asset(anim_asset_name, desired_anim_path)
+ else:
+ fail_reason = f"animAsset {asset_data['asset_name']} not found after import: {anim_asset_name}"
+ return fail_reason, None
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/bfu_import_materials/bfu_import_materials_utils.py b/blender-for-unrealengine/bfu_import_module/bfu_import_materials/bfu_import_materials_utils.py
index 007f1cb0..0c1db363 100644
--- a/blender-for-unrealengine/bfu_import_module/bfu_import_materials/bfu_import_materials_utils.py
+++ b/blender-for-unrealengine/bfu_import_module/bfu_import_materials/bfu_import_materials_utils.py
@@ -16,50 +16,88 @@
#
# ======================= END GPL LICENSE BLOCK =============================
+from .. import import_module_unreal_utils
+from .. import import_module_tasks_class
+
try:
import unreal
except ImportError:
import unreal_engine as unreal
-def update_task_with_material_data(task: unreal.AssetImportTask, asset_data):
-
- if asset_data["asset_type"] in ["StaticMesh", "SkeletalMesh"]:
+def apply_import_settings(itask: import_module_tasks_class.ImportTaks, asset_data: dict) -> None:
+ """Applies material and texture import settings to StaticMesh and SkeletalMesh assets."""
+
+ print("Mat S0")
+
+ asset_type = asset_data.get("asset_type")
+ if asset_type not in ["StaticMesh", "SkeletalMesh"]:
+ # Only apply settings for StaticMesh and SkeletalMesh
+ return
- fbx_import_ui = task.get_editor_property('options')
- fbx_import_ui: unreal.FbxTextureImportData
+ print("Mat S1")
+
+ # Material and texture import settings
+ if itask.use_interchange:
+ if "import_materials" in asset_data:
+ itask.get_igap_material().set_editor_property('import_materials', asset_data["import_materials"])
+ if "import_textures" in asset_data:
+ itask.get_igap_texture().set_editor_property('import_textures', asset_data["import_textures"])
+ else:
if "import_materials" in asset_data:
- fbx_import_ui.set_editor_property('import_materials', asset_data["import_materials"])
+ itask.get_fbx_import_ui().set_editor_property('import_materials', asset_data["import_materials"])
if "import_textures" in asset_data:
- fbx_import_ui.set_editor_property('import_textures', asset_data["import_textures"])
+ itask.get_fbx_import_ui().set_editor_property('import_textures', asset_data["import_textures"])
+ print("Mat S2")
+
+ # Material search location and normal map green channel flip
+ if itask.use_interchange:
+ if "material_search_location" in asset_data:
+ search_location = asset_data["material_search_location"]
+ location_enum = {
+ "Local": unreal.InterchangeMaterialSearchLocation.LOCAL,
+ "UnderParent": unreal.InterchangeMaterialSearchLocation.UNDER_PARENT,
+ "UnderRoot": unreal.InterchangeMaterialSearchLocation.UNDER_ROOT,
+ "AllAssets": unreal.InterchangeMaterialSearchLocation.ALL_ASSETS
+ }
+ if search_location in location_enum:
+ itask.get_igap_material().set_editor_property('search_location', location_enum[search_location])
- fbx_texture_import_data = fbx_import_ui.texture_import_data
- fbx_texture_import_data: unreal.FbxTextureImportData
+ if "flip_normal_map_green_channel" in asset_data:
+ itask.get_igap_texture().set_editor_property('flip_normal_map_green_channel', asset_data["flip_normal_map_green_channel"])
+ else:
+ texture_import_data = itask.get_texture_import_data()
+
if "material_search_location" in asset_data:
- if asset_data["material_search_location"] == "Local":
- fbx_texture_import_data.set_editor_property('material_search_location', unreal.MaterialSearchLocation.LOCAL)
- if asset_data["material_search_location"] == "UnderParent":
- fbx_texture_import_data.set_editor_property('material_search_location', unreal.MaterialSearchLocation.UNDER_PARENT)
- if asset_data["material_search_location"] == "UnderRoot":
- fbx_texture_import_data.set_editor_property('material_search_location', unreal.MaterialSearchLocation.UNDER_ROOT)
- if asset_data["material_search_location"] == "AllAssets":
- fbx_texture_import_data.set_editor_property('material_search_location', unreal.MaterialSearchLocation.ALL_ASSETS)
+ search_location = asset_data["material_search_location"]
+ location_enum = {
+ "Local": unreal.MaterialSearchLocation.LOCAL,
+ "UnderParent": unreal.MaterialSearchLocation.UNDER_PARENT,
+ "UnderRoot": unreal.MaterialSearchLocation.UNDER_ROOT,
+ "AllAssets": unreal.MaterialSearchLocation.ALL_ASSETS
+ }
+ if search_location in location_enum:
+ texture_import_data.set_editor_property('material_search_location', location_enum[search_location])
- if "invert_normal_maps" in asset_data:
- fbx_texture_import_data.set_editor_property('invert_normal_maps', asset_data["invert_normal_maps"])
+ if "flip_normal_map_green_channel" in asset_data:
+ texture_import_data.set_editor_property('invert_normal_maps', asset_data["flip_normal_map_green_channel"])
+ print("Mat S3")
+
+ # Mat order
+ if itask.use_interchange:
+ # @TODO reorder_material_to_fbx_order Removed with InterchangeGenericAssetsPipeline?
+ # I yes need also remove reorder_material_to_fbx_order from the addon propertys.
+ pass
- if asset_data["asset_type"] =="StaticMesh":
- static_mesh_import_data = fbx_import_ui.static_mesh_import_data
- static_mesh_import_data: unreal.FbxSkeletalMeshImportData
+ else:
+ if asset_type =="StaticMesh":
if "reorder_material_to_fbx_order" in asset_data:
- static_mesh_import_data.set_editor_property('reorder_material_to_fbx_order', asset_data["reorder_material_to_fbx_order"])
+ itask.get_static_mesh_import_data().set_editor_property('reorder_material_to_fbx_order', asset_data["reorder_material_to_fbx_order"])
- if asset_data["asset_type"] == "SkeletalMesh":
- skeletal_mesh_import_data = fbx_import_ui.skeletal_mesh_import_data
- skeletal_mesh_import_data: unreal.FbxStaticMeshImportData
+ elif asset_type == "SkeletalMesh":
if "reorder_material_to_fbx_order" in asset_data:
- skeletal_mesh_import_data.set_editor_property('reorder_material_to_fbx_order', asset_data["reorder_material_to_fbx_order"])
\ No newline at end of file
+ itask.get_skeletal_mesh_import_data().set_editor_property('reorder_material_to_fbx_order', asset_data["reorder_material_to_fbx_order"])
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/bfu_import_vertex_color/__init__.py b/blender-for-unrealengine/bfu_import_module/bfu_import_vertex_color/__init__.py
new file mode 100644
index 00000000..a8c4cb76
--- /dev/null
+++ b/blender-for-unrealengine/bfu_import_module/bfu_import_vertex_color/__init__.py
@@ -0,0 +1,24 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import importlib
+
+from . import bfu_import_vertex_color_utils
+
+if "bfu_import_vertex_color_utils" in locals():
+ importlib.reload(bfu_import_vertex_color_utils)
diff --git a/blender-for-unrealengine/bfu_import_module/bfu_import_vertex_color/bfu_import_vertex_color_utils.py b/blender-for-unrealengine/bfu_import_module/bfu_import_vertex_color/bfu_import_vertex_color_utils.py
new file mode 100644
index 00000000..ecebc4e9
--- /dev/null
+++ b/blender-for-unrealengine/bfu_import_module/bfu_import_vertex_color/bfu_import_vertex_color_utils.py
@@ -0,0 +1,128 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+from typing import Optional
+from .. import import_module_unreal_utils
+from .. import import_module_tasks_class
+
+try:
+ import unreal
+except ImportError:
+ import unreal_engine as unreal
+
+support_interchange = import_module_unreal_utils.get_support_interchange()
+
+def get_vertex_override_color(asset_additional_data: dict) -> Optional[unreal.LinearColor]:
+ """Retrieves the vertex override color from the asset data, if available."""
+ if asset_additional_data is None:
+ return None
+
+ if "vertex_override_color" in asset_additional_data:
+ return unreal.LinearColor(
+ asset_additional_data["vertex_override_color"][0],
+ asset_additional_data["vertex_override_color"][1],
+ asset_additional_data["vertex_override_color"][2]
+ )
+
+ return None
+
+if support_interchange:
+ def get_interchange_vertex_color_import_option(asset_additional_data: dict) -> Optional[unreal.InterchangeVertexColorImportOption]:
+ """Retrieves the vertex color import option based on the asset data and pipeline."""
+ if asset_additional_data is None:
+ return None
+
+ key = "vertex_color_import_option"
+ option_value = asset_additional_data.get(key)
+
+ # For unreal.InterchangeGenericCommonMeshesProperties
+ if option_value == "IGNORE":
+ return unreal.InterchangeVertexColorImportOption.IVCIO_IGNORE
+ elif option_value == "OVERRIDE":
+ return unreal.InterchangeVertexColorImportOption.IVCIO_OVERRIDE
+ elif option_value == "REPLACE":
+ return unreal.InterchangeVertexColorImportOption.IVCIO_REPLACE
+ return unreal.InterchangeVertexColorImportOption.IVCIO_REPLACE # Default
+
+
+def get_vertex_color_import_option(asset_additional_data: dict) -> Optional[unreal.VertexColorImportOption]:
+ """Retrieves the vertex color import option based on the asset data and pipeline."""
+ if asset_additional_data is None:
+ return None
+
+ key = "vertex_color_import_option"
+ option_value = asset_additional_data.get(key)
+
+ # For unreal.FbxStaticMeshImportData
+ if option_value == "IGNORE":
+ return unreal.VertexColorImportOption.IGNORE
+ elif option_value == "OVERRIDE":
+ return unreal.VertexColorImportOption.OVERRIDE
+ elif option_value == "REPLACE":
+ return unreal.VertexColorImportOption.REPLACE
+ return unreal.VertexColorImportOption.REPLACE # Default
+
+
+
+def apply_import_settings(itask: import_module_tasks_class.ImportTaks, asset_type: str, asset_additional_data: dict) -> None:
+ """Applies vertex color settings during the import process."""
+ vertex_override_color = get_vertex_override_color(asset_additional_data)
+ if itask.use_interchange:
+ vertex_color_import_option = get_interchange_vertex_color_import_option(asset_additional_data)
+ else:
+ vertex_color_import_option = get_vertex_color_import_option(asset_additional_data)
+
+ if itask.use_interchange:
+ itask.get_igap_common_mesh().set_editor_property('vertex_color_import_option', vertex_color_import_option)
+ if vertex_override_color:
+ itask.get_igap_common_mesh().set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
+ else:
+ if asset_type == "StaticMesh":
+ itask.get_static_mesh_import_data().set_editor_property('vertex_color_import_option', vertex_color_import_option)
+ if vertex_override_color:
+ itask.get_static_mesh_import_data().set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
+
+ elif asset_type == "SkeletalMesh":
+ itask.get_skeletal_mesh_import_data().set_editor_property('vertex_color_import_option', vertex_color_import_option)
+ if vertex_override_color:
+ itask.get_skeletal_mesh_import_data().set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
+
+
+def apply_asset_settings(itask: import_module_tasks_class.ImportTaks, asset: unreal.Object, asset_additional_data: dict) -> None:
+ """Applies vertex color settings to an already imported asset."""
+ if asset is None:
+ return
+
+ vertex_override_color = get_vertex_override_color(asset_additional_data)
+ if itask.use_interchange:
+ vertex_color_import_option = get_interchange_vertex_color_import_option(asset_additional_data)
+ else:
+ vertex_color_import_option = get_vertex_color_import_option(asset_additional_data)
+
+ if itask.use_interchange:
+ common_meshes_properties = asset.get_editor_property('asset_import_data').get_pipelines()[0].get_editor_property('common_meshes_properties')
+ if vertex_override_color:
+ common_meshes_properties.set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
+ if vertex_color_import_option:
+ common_meshes_properties.set_editor_property('vertex_color_import_option', vertex_color_import_option)
+ else:
+ asset_import_data = asset.get_editor_property('asset_import_data')
+ if vertex_override_color:
+ asset_import_data.set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
+ if vertex_color_import_option:
+ asset_import_data.set_editor_property('vertex_color_import_option', vertex_color_import_option)
\ No newline at end of file
diff --git a/blender-for-unrealengine/bps/__init__.py b/blender-for-unrealengine/bfu_import_module/bpl/__init__.py
similarity index 93%
rename from blender-for-unrealengine/bps/__init__.py
rename to blender-for-unrealengine/bfu_import_module/bpl/__init__.py
index 3da665cf..ff3e288d 100644
--- a/blender-for-unrealengine/bps/__init__.py
+++ b/blender-for-unrealengine/bfu_import_module/bpl/__init__.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
@@ -45,4 +46,5 @@
if "blender_sub_process" in locals():
importlib.reload(blender_sub_process)
if "naming" in locals():
- importlib.reload(naming)
\ No newline at end of file
+ importlib.reload(naming)
+
diff --git a/blender-for-unrealengine/bfu_import_module/bps/advprint.py b/blender-for-unrealengine/bfu_import_module/bpl/advprint.py
similarity index 85%
rename from blender-for-unrealengine/bfu_import_module/bps/advprint.py
rename to blender-for-unrealengine/bfu_import_module/bpl/advprint.py
index 75ac6f27..414d5efb 100644
--- a/blender-for-unrealengine/bfu_import_module/bps/advprint.py
+++ b/blender-for-unrealengine/bfu_import_module/bpl/advprint.py
@@ -17,13 +17,12 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
-
-import sys
import time
@@ -34,7 +33,7 @@ def _get_name(self):
def _set_name(self, value):
if not isinstance(value, str):
- raise TypeError("name must be set to an String")
+ raise TypeError("name must be set to a String")
self.__name = value
name = property(_get_name, _set_name)
@@ -57,7 +56,6 @@ def _get_total_step(self):
def _set_total_step(self, value):
if not (isinstance(value, int) or isinstance(value, float)):
raise TypeError("total_step must be set to an Integer or Float")
-
self.__total_step = value
total_step = property(_get_total_step, _set_total_step)
@@ -80,29 +78,25 @@ def update_progress(self, progress):
total_step = self.__total_step
self.__previous_step = progress # Update the previous step.
- is_done = False
- if progress >= total_step:
- is_done = True
+ is_done = progress >= total_step
- # Write message.
+ # Write message
msg = "\r{0}:".format(job_title)
if self.show_block:
- block = int(round(length*progress/total_step))
- msg += " [{0}]".format("#"*block + "-"*(length-block))
+ block = int(round(length * progress / total_step))
+ msg += " [{0}]".format("#" * block + "-" * (length - block))
if self.show_steps:
msg += " {0}/{1}".format(progress, total_step)
if is_done:
- msg += " DONE IN {0}s\r\n".format(round(time.perf_counter()-self.__counter_start, 3))
-
- else:
- if self.show_percentage:
- msg += " {0}%".format(round((progress*100)/total_step, 2))
+ msg += " DONE IN {0}s\r\n".format(round(time.perf_counter() - self.__counter_start, 3))
+ elif self.show_percentage:
+ msg += " {0}%".format(round((progress * 100) / total_step, 2))
- sys.stdout.write(msg)
- sys.stdout.flush()
+ # Print the progress message on the same line
+ print(msg, end='', flush=True)
def print_separation(number=60, char="-"):
diff --git a/blender-for-unrealengine/bfu_import_module/bps/blender_sub_process.py b/blender-for-unrealengine/bfu_import_module/bpl/blender_sub_process.py
similarity index 93%
rename from blender-for-unrealengine/bfu_import_module/bps/blender_sub_process.py
rename to blender-for-unrealengine/bfu_import_module/bpl/blender_sub_process.py
index f845d516..035cac54 100644
--- a/blender-for-unrealengine/bfu_import_module/bps/blender_sub_process.py
+++ b/blender-for-unrealengine/bfu_import_module/bpl/blender_sub_process.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bfu_import_module/bps/color_set.py b/blender-for-unrealengine/bfu_import_module/bpl/color_set.py
similarity index 98%
rename from blender-for-unrealengine/bfu_import_module/bps/color_set.py
rename to blender-for-unrealengine/bfu_import_module/bpl/color_set.py
index 8d39ebe4..3b066ea5 100644
--- a/blender-for-unrealengine/bfu_import_module/bps/color_set.py
+++ b/blender-for-unrealengine/bfu_import_module/bpl/color_set.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bfu_import_module/bpl/console_utils.py b/blender-for-unrealengine/bfu_import_module/bpl/console_utils.py
new file mode 100644
index 00000000..c6c1ba47
--- /dev/null
+++ b/blender-for-unrealengine/bfu_import_module/bpl/console_utils.py
@@ -0,0 +1,29 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import os
+
+def clear_console():
+ os.system('cls' if os.name == 'nt' else 'clear')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bps/math.py b/blender-for-unrealengine/bfu_import_module/bpl/math.py
similarity index 96%
rename from blender-for-unrealengine/bps/math.py
rename to blender-for-unrealengine/bfu_import_module/bpl/math.py
index d036605a..376bc45b 100644
--- a/blender-for-unrealengine/bps/math.py
+++ b/blender-for-unrealengine/bfu_import_module/bpl/math.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bfu_import_module/bps/naming.py b/blender-for-unrealengine/bfu_import_module/bpl/naming.py
similarity index 97%
rename from blender-for-unrealengine/bfu_import_module/bps/naming.py
rename to blender-for-unrealengine/bfu_import_module/bpl/naming.py
index 9c43c4f6..497e43e1 100644
--- a/blender-for-unrealengine/bfu_import_module/bps/naming.py
+++ b/blender-for-unrealengine/bfu_import_module/bpl/naming.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bps/utils.py b/blender-for-unrealengine/bfu_import_module/bpl/utils.py
similarity index 97%
rename from blender-for-unrealengine/bps/utils.py
rename to blender-for-unrealengine/bfu_import_module/bpl/utils.py
index 320f3319..bc6592d7 100644
--- a/blender-for-unrealengine/bps/utils.py
+++ b/blender-for-unrealengine/bfu_import_module/bpl/utils.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bfu_import_module/bps/console_utils.py b/blender-for-unrealengine/bfu_import_module/bps/console_utils.py
deleted file mode 100644
index 596331d9..00000000
--- a/blender-for-unrealengine/bfu_import_module/bps/console_utils.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import os
-
-def clear_console():
- os.system('cls' if os.name == 'nt' else 'clear')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/config.py b/blender-for-unrealengine/bfu_import_module/config.py
new file mode 100644
index 00000000..e1383f5a
--- /dev/null
+++ b/blender-for-unrealengine/bfu_import_module/config.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+automated_import_tasks = True #False when debug only, used to show import dialog.
+force_use_interchange = "Auto" #Auto by default, you can use Interchange or FBX
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/import_module_post_treatment.py b/blender-for-unrealengine/bfu_import_module/import_module_post_treatment.py
index cb584134..8798a244 100644
--- a/blender-for-unrealengine/bfu_import_module/import_module_post_treatment.py
+++ b/blender-for-unrealengine/bfu_import_module/import_module_post_treatment.py
@@ -25,9 +25,9 @@
import unreal_engine as unreal
-def import_static_lod(asset, asset_data, asset_additional_data, lod_name, lod_number):
- vertex_override_color = import_module_unreal_utils.get_vertex_override_color(asset_additional_data)
- vertex_color_import_option = import_module_unreal_utils.get_vertex_color_import_option(asset_additional_data)
+def import_static_lod(asset, asset_options, asset_data, asset_additional_data, lod_name, lod_number):
+
+ print(f"Start Import Lod_{str(lod_number)} ({lod_name})")
if "LevelOfDetail" in asset_additional_data:
if lod_name in asset_additional_data["LevelOfDetail"]:
@@ -38,18 +38,16 @@ def import_static_lod(asset, asset_data, asset_additional_data, lod_name, lod_nu
lodTask.automated = True
lodTask.replace_existing = True
- # Set vertex color import settings to replicate base StaticMesh's behaviour
- if asset_data["asset_type"] == "Alembic":
- lodTask.set_editor_property('options', unreal.AbcImportSettings())
+ if asset_options:
+ lodTask.set_editor_property('options', asset_options)
else:
- lodTask.set_editor_property('options', unreal.FbxImportUI())
+ # Replicate asset import settings when asset_options is None
+ lodTask.set_editor_property('options', asset.get_editor_property('asset_import_data'))
- lodTask.get_editor_property('options').static_mesh_import_data.set_editor_property('vertex_color_import_option', vertex_color_import_option)
- lodTask.get_editor_property('options').static_mesh_import_data.set_editor_property('vertex_override_color', vertex_override_color.to_rgbe())
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([lodTask])
if len(lodTask.imported_object_paths) > 0:
- lodAsset = unreal.find_asset(lodTask.imported_object_paths[0])
+ lodAsset = import_module_unreal_utils.load_asset(lodTask.imported_object_paths[0])
slot_replaced = unreal.EditorStaticMeshLibrary.set_lod_from_static_mesh(asset, lod_number, lodAsset, 0, True)
unreal.EditorAssetLibrary.delete_asset(lodTask.imported_object_paths[0])
@@ -59,27 +57,27 @@ def import_skeletal_lod(asset, asset_data, asset_additional_data, lod_name, lod_
# Unreal python no longer support Skeletal mesh LODS import.
pass
-def set_static_mesh_lods(asset, asset_data, asset_additional_data):
+def set_static_mesh_lods(asset, asset_options, asset_data, asset_additional_data):
# Import the StaticMesh lod(s)
unreal.EditorStaticMeshLibrary.remove_lods(asset)
- import_static_lod(asset, asset_data, asset_additional_data, "lod_1", 1)
- import_static_lod(asset, asset_data, asset_additional_data, "lod_2", 2)
- import_static_lod(asset, asset_data, asset_additional_data, "lod_3", 3)
- import_static_lod(asset, asset_data, asset_additional_data, "lod_4", 4)
- import_static_lod(asset, asset_data, asset_additional_data, "lod_5", 5)
+ import_static_lod(asset, asset_options, asset_data, asset_additional_data, "lod_1", 1)
+ import_static_lod(asset, asset_options, asset_data, asset_additional_data, "lod_2", 2)
+ import_static_lod(asset, asset_options, asset_data, asset_additional_data, "lod_3", 3)
+ import_static_lod(asset, asset_options, asset_data, asset_additional_data, "lod_4", 4)
+ import_static_lod(asset, asset_options, asset_data, asset_additional_data, "lod_5", 5)
-def set_skeletal_mesh_lods(asset, asset_data, asset_additional_data):
+def set_skeletal_mesh_lods(asset, asset_options, asset_data, asset_additional_data):
# Import the SkeletalMesh lod(s)
- import_skeletal_lod(asset, asset_data, asset_additional_data, "lod_1", 1)
- import_skeletal_lod(asset, asset_data, asset_additional_data, "lod_2", 2)
- import_skeletal_lod(asset, asset_data, asset_additional_data, "lod_3", 3)
- import_skeletal_lod(asset, asset_data, asset_additional_data, "lod_4", 4)
- import_skeletal_lod(asset, asset_data, asset_additional_data, "lod_5", 5)
+ import_skeletal_lod(asset, asset_options, asset_data, asset_additional_data, "lod_1", 1)
+ import_skeletal_lod(asset, asset_options, asset_data, asset_additional_data, "lod_2", 2)
+ import_skeletal_lod(asset, asset_options, asset_data, asset_additional_data, "lod_3", 3)
+ import_skeletal_lod(asset, asset_options, asset_data, asset_additional_data, "lod_4", 4)
+ import_skeletal_lod(asset, asset_options, asset_data, asset_additional_data, "lod_5", 5)
def set_sequence_preview_skeletal_mesh(asset: unreal.AnimSequence, origin_skeletal_mesh):
if origin_skeletal_mesh:
- #todo:: preview_pose_asset doesn’t retarget right now. Need wait update in Unreal Engine Python API.
+ # @TODO preview_pose_asset doesn’t retarget right now. Need wait update in Unreal Engine Python API.
asset.get_editor_property('preview_pose_asset')
pass
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/import_module_tasks_class.py b/blender-for-unrealengine/bfu_import_module/import_module_tasks_class.py
new file mode 100644
index 00000000..07e2d8c0
--- /dev/null
+++ b/blender-for-unrealengine/bfu_import_module/import_module_tasks_class.py
@@ -0,0 +1,142 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+from typing import List, Optional
+from . import import_module_unreal_utils
+from . import config
+
+try:
+ import unreal
+except ImportError:
+ import unreal_engine as unreal
+
+support_interchange = import_module_unreal_utils.get_support_interchange()
+
+class ImportTaks():
+
+ def __init__(self) -> None:
+ self.task = unreal.AssetImportTask()
+ self.task_option = None
+
+ if config.force_use_interchange == "Interchange":
+ self.use_interchange = True
+
+ elif config.force_use_interchange == "FBX":
+ self.use_interchange = False
+
+ else:
+ # Interchange is avaliable since 5.0,
+ # but I preffer start to use at 5.5 to avoid issue with previous versions.
+ if support_interchange and import_module_unreal_utils.is_unreal_version_greater_or_equal(5,5):
+ # Set values inside unreal.InterchangeGenericAssetsPipeline (unreal.InterchangeGenericCommonMeshesProperties or ...)
+ self.use_interchange = True
+ else:
+ # Set values inside unreal.FbxStaticMeshImportData or ...
+ self.use_interchange = False
+
+ def set_task_option(self, new_task_option):
+ self.task_option = new_task_option
+
+ def get_task(self):
+ return self.task
+
+ def get_fbx_import_ui(self) -> unreal.FbxImportUI:
+ return self.task_option
+
+ def get_abc_import_settings(self) -> unreal.AbcImportSettings:
+ return self.task_option
+
+ def get_static_mesh_import_data(self) -> unreal.FbxStaticMeshImportData:
+ return self.task_option.static_mesh_import_data
+
+ def get_skeletal_mesh_import_data(self) -> unreal.FbxSkeletalMeshImportData:
+ return self.task_option.skeletal_mesh_import_data
+
+ def get_animation_import_data(self) -> unreal.FbxAnimSequenceImportData:
+ return self.task_option.anim_sequence_import_data
+
+ def get_texture_import_data(self) -> unreal.FbxTextureImportData:
+ return self.task_option.texture_import_data
+
+
+
+ if support_interchange:
+ def get_igap(self) -> unreal.InterchangeGenericAssetsPipeline:
+ # unreal.InterchangeGenericAssetsPipeline
+ return self.task_option
+
+ def get_igap_mesh(self) -> unreal.InterchangeGenericMeshPipeline:
+ # unreal.InterchangeGenericMeshPipeline
+ return self.task_option.get_editor_property('mesh_pipeline')
+
+ def get_igap_skeletal_mesh(self) -> unreal.InterchangeGenericCommonSkeletalMeshesAndAnimationsProperties:
+ # unreal.InterchangeGenericCommonSkeletalMeshesAndAnimationsProperties
+ return self.task_option.get_editor_property('common_skeletal_meshes_and_animations_properties')
+
+ def get_igap_common_mesh(self) -> unreal.InterchangeGenericCommonMeshesProperties:
+ # unreal.InterchangeGenericCommonMeshesProperties
+ return self.task_option.get_editor_property('common_meshes_properties')
+
+ def get_igap_material(self) -> unreal.InterchangeGenericMaterialPipeline:
+ # unreal.InterchangeGenericMaterialPipeline
+ return self.task_option.get_editor_property('material_pipeline')
+
+ def get_igap_texture(self) -> unreal.InterchangeGenericTexturePipeline:
+ # unreal.InterchangeGenericTexturePipeline
+ return self.task_option.get_editor_property('material_pipeline').get_editor_property('texture_pipeline')
+
+ def get_igap_animation(self) -> unreal.InterchangeGenericAnimationPipeline:
+ # unreal.InterchangeGenericAnimationPipeline
+ return self.task_option.get_editor_property('animation_pipeline')
+
+ def get_imported_assets(self) -> List[unreal.Object]:
+ assets = []
+ for path in self.task.imported_object_paths:
+ search_asset = import_module_unreal_utils.load_asset(path)
+ if search_asset:
+ assets.append(search_asset)
+ return assets
+
+ def get_imported_static_mesh(self) -> Optional[unreal.StaticMesh]:
+ return next((asset for asset in self.get_imported_assets() if isinstance(asset, unreal.StaticMesh)), None)
+
+ def get_imported_skeleton(self) -> Optional[unreal.Skeleton]:
+ return next((asset for asset in self.get_imported_assets() if isinstance(asset, unreal.Skeleton)), None)
+
+ def get_imported_skeletal_mesh(self) -> Optional[unreal.SkeletalMesh]:
+ return next((asset for asset in self.get_imported_assets() if isinstance(asset, unreal.SkeletalMesh)), None)
+
+ def get_imported_anim_sequence(self) -> Optional[unreal.AnimSequence]:
+ return next((asset for asset in self.get_imported_assets() if isinstance(asset, unreal.AnimSequence)), None)
+
+ def import_asset_task(self):
+ if self.use_interchange:
+ self.task.set_editor_property('options', unreal.InterchangePipelineStackOverride())
+ self.task.get_editor_property('options').add_pipeline(self.task_option)
+ else:
+ self.task.set_editor_property('options', self.task_option)
+ unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([self.task])
+
+ def get_task_options(self):
+ if self.use_interchange:
+ new_option = unreal.InterchangePipelineStackOverride()
+ new_option.add_pipeline(self.task_option)
+ return new_option
+ else:
+ return self.task_option
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/import_module_tasks_helper.py b/blender-for-unrealengine/bfu_import_module/import_module_tasks_helper.py
new file mode 100644
index 00000000..9bf63d8e
--- /dev/null
+++ b/blender-for-unrealengine/bfu_import_module/import_module_tasks_helper.py
@@ -0,0 +1,124 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+from typing import Union
+from . import import_module_unreal_utils
+from . import import_module_tasks_class
+
+try:
+ import unreal
+except ImportError:
+ import unreal_engine as unreal
+
+support_interchange = import_module_unreal_utils.get_support_interchange()
+
+if support_interchange:
+ def task_options_default_preset(use_interchange: bool = True) -> Union[unreal.FbxImportUI, unreal.InterchangeGenericAssetsPipeline]:
+ """Returns default task options preset based on interchange usage and Unreal version."""
+ if use_interchange:
+ options = unreal.InterchangeGenericAssetsPipeline()
+ else:
+ options = unreal.FbxImportUI()
+ return options
+
+ def task_options_static_mesh_preset(use_interchange: bool = True) -> Union[unreal.InterchangeGenericAssetsPipeline, unreal.FbxImportUI]:
+ """Returns static mesh task options preset based on interchange usage."""
+ if use_interchange:
+ options = unreal.InterchangeGenericAssetsPipeline()
+ else:
+ options = unreal.FbxImportUI()
+ return options
+
+ def task_options_skeletal_mesh_preset(use_interchange: bool = True) -> Union[unreal.InterchangeGenericAssetsPipeline, unreal.FbxImportUI]:
+ """Returns skeletal mesh task options preset based on interchange usage."""
+ if use_interchange:
+ options = unreal.InterchangeGenericAssetsPipeline()
+ else:
+ options = unreal.FbxImportUI()
+ return options
+
+ def task_options_animation_preset(use_interchange: bool = True) -> Union[unreal.InterchangeGenericAssetsPipeline, unreal.FbxImportUI]:
+ """Returns animation task options preset based on interchange usage."""
+ if use_interchange:
+ options = unreal.InterchangeGenericAssetsPipeline()
+ else:
+ options = unreal.FbxImportUI()
+ return options
+else:
+ def task_options_default_preset(use_interchange: bool = True) -> unreal.FbxImportUI:
+ """Returns default task options preset for Unreal Engine versions below 5, without interchange support."""
+ return unreal.FbxImportUI()
+
+ def task_options_static_mesh_preset(use_interchange: bool = True) -> unreal.FbxImportUI:
+ """Returns static mesh task options preset without interchange support."""
+ return unreal.FbxImportUI()
+
+ def task_options_skeletal_mesh_preset(use_interchange: bool = True) -> unreal.FbxImportUI:
+ """Returns skeletal mesh task options preset without interchange support."""
+ return unreal.FbxImportUI()
+
+ def task_options_animation_preset(use_interchange: bool = True) -> unreal.FbxImportUI:
+ """Returns animation task options preset without interchange support."""
+ return unreal.FbxImportUI()
+
+def task_options_alembic_preset(use_interchange: bool = True) -> unreal.AbcImportSettings:
+ """Returns Alembic task options preset."""
+ options = unreal.AbcImportSettings()
+ return options
+
+if support_interchange:
+ def init_options_data(asset_type: str, use_interchange: bool = True) -> Union[unreal.InterchangeGenericAssetsPipeline, unreal.FbxImportUI, unreal.AbcImportSettings]:
+ """Initializes task options based on asset type and interchange usage."""
+
+ if asset_type == "Alembic":
+ options = task_options_alembic_preset(use_interchange)
+
+ elif asset_type == "StaticMesh":
+ options = task_options_static_mesh_preset(use_interchange)
+
+ elif asset_type == "SkeletalMesh":
+ options = task_options_skeletal_mesh_preset(use_interchange)
+
+ elif asset_type == "Animation":
+ options = task_options_animation_preset(use_interchange)
+
+ else:
+ options = task_options_default_preset(use_interchange)
+
+ return options
+else:
+ def init_options_data(asset_type: str, use_interchange: bool = True) -> Union[unreal.FbxImportUI, unreal.AbcImportSettings]:
+ """Initializes task options based on asset type and interchange usage."""
+
+ if asset_type == "Alembic":
+ options = task_options_alembic_preset(use_interchange)
+
+ elif asset_type == "StaticMesh":
+ options = task_options_static_mesh_preset(use_interchange)
+
+ elif asset_type == "SkeletalMesh":
+ options = task_options_skeletal_mesh_preset(use_interchange)
+
+ elif asset_type == "Animation":
+ options = task_options_animation_preset(use_interchange)
+
+ else:
+ options = task_options_default_preset(use_interchange)
+
+ return options
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/import_module_unreal_utils.py b/blender-for-unrealengine/bfu_import_module/import_module_unreal_utils.py
index f47925e3..a76b9ba8 100644
--- a/blender-for-unrealengine/bfu_import_module/import_module_unreal_utils.py
+++ b/blender-for-unrealengine/bfu_import_module/import_module_unreal_utils.py
@@ -7,45 +7,54 @@
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
+# along with this program. If not, see .
# All rights reserved.
#
# ======================= END GPL LICENSE BLOCK =============================
import string
+from typing import List, Tuple
+from . import import_module_unreal_utils
try:
import unreal
except ImportError:
import unreal_engine as unreal
-def get_selected_level_actors():
- selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors()
- return selected_actors
+def load_asset(name):
+ find_asset = unreal.find_asset(name, follow_redirectors=True)
+ if find_asset is None:
+ # Load asset if not find.
+ find_asset = unreal.load_asset(name, follow_redirectors=True)
+ return find_asset
+
-def get_unreal_version():
+def get_selected_level_actors() -> List[unreal.Actor]:
+ """Returns a list of selected actors in the level."""
+ return unreal.EditorLevelLibrary.get_selected_level_actors()
+
+def get_unreal_version() -> Tuple[int, int, int]:
+ """Returns the Unreal Engine version as a tuple of (major, minor, patch)."""
version_info = unreal.SystemLibrary.get_engine_version().split('-')[0]
- version_numbers = version_info.split('.')
- major = int(version_numbers[0])
- minor = int(version_numbers[1])
- patch = int(version_numbers[2])
+ major, minor, patch = map(int, version_info.split('.'))
return major, minor, patch
-def is_unreal_version_greater_or_equal(target_major, target_minor=0, target_patch=0):
+def is_unreal_version_greater_or_equal(target_major: int, target_minor: int = 0, target_patch: int = 0) -> bool:
+ """Checks if the Unreal Engine version is greater than or equal to the target version."""
major, minor, patch = get_unreal_version()
-
- if major > target_major or (major == target_major and minor >= target_minor) or (major == target_major and minor == target_minor and patch >= target_patch):
- return True
- else:
- return False
+ return (
+ major > target_major or
+ (major == target_major and minor > target_minor) or
+ (major == target_major and minor == target_minor and patch >= target_patch)
+ )
-def ValidUnrealAssetsName(filename):
- # Normalizes string, removes non-alpha characters
- # Asset name in Unreal use
+
+def valid_unreal_asset_name(filename):
+ """Returns a valid Unreal asset name by replacing invalid characters."""
filename = filename.replace('.', '_')
filename = filename.replace('(', '_')
@@ -55,37 +64,30 @@ def ValidUnrealAssetsName(filename):
filename = ''.join(c for c in filename if c in valid_chars)
return filename
-def show_simple_message(title, message):
- return unreal.EditorDialog.show_message(title, message, unreal.AppMsgType.OK)
-
-def show_warning_message(title, message):
- print('--------------------------------------------------')
- print(message)
- return unreal.EditorDialog.show_message(title, message, unreal.AppMsgType.OK)
-
-def get_vertex_override_color(asset_additional_data):
- if asset_additional_data is None:
- return None
+def show_simple_message(title: str, message: str) -> unreal.AppReturnType:
+ """Displays a simple message dialog in Unreal Editor."""
+ if hasattr(unreal, 'EditorDialog'):
+ return unreal.EditorDialog.show_message(title, message, unreal.AppMsgType.OK)
+ else:
+ print('--------------------------------------------------')
+ print(message)
- if "vertex_override_color" in asset_additional_data:
- vertex_override_color = unreal.LinearColor(
- asset_additional_data["vertex_override_color"][0],
- asset_additional_data["vertex_override_color"][1],
- asset_additional_data["vertex_override_color"][2]
- )
- return vertex_override_color
+def show_warning_message(title: str, message: str) -> unreal.AppReturnType:
+ """Displays a warning message in Unreal Editor and prints it to the console."""
+ if hasattr(unreal, 'EditorDialog'):
+ unreal.EditorDialog.show_message(title, message, unreal.AppMsgType.OK)
+ else:
+ print('--------------------------------------------------')
+ print(message)
-def get_vertex_color_import_option(asset_additional_data):
- if asset_additional_data is None:
- return None
+def get_support_interchange() -> bool:
+ return import_module_unreal_utils.is_unreal_version_greater_or_equal(5, 0)
- vertex_color_import_option = unreal.VertexColorImportOption.REPLACE # Default
- if "vertex_color_import_option" in asset_additional_data:
- if asset_additional_data["vertex_color_import_option"] == "IGNORE":
- vertex_color_import_option = unreal.VertexColorImportOption.IGNORE
- elif asset_additional_data["vertex_color_import_option"] == "OVERRIDE":
- vertex_color_import_option = unreal.VertexColorImportOption.OVERRIDE
- elif asset_additional_data["vertex_color_import_option"] == "REPLACE":
- vertex_color_import_option = unreal.VertexColorImportOption.REPLACE
- return vertex_color_import_option
+def editor_scripting_utilities_active() -> bool:
+ if is_unreal_version_greater_or_equal(4,20):
+ if hasattr(unreal, 'EditorAssetLibrary'):
+ return True
+ return False
+def sequencer_scripting_active() -> bool:
+ return hasattr(unreal.MovieSceneSequence, 'set_display_rate')
diff --git a/blender-for-unrealengine/bfu_import_module/import_module_utils.py b/blender-for-unrealengine/bfu_import_module/import_module_utils.py
index 6c5d3df3..51f0b5e7 100644
--- a/blender-for-unrealengine/bfu_import_module/import_module_utils.py
+++ b/blender-for-unrealengine/bfu_import_module/import_module_utils.py
@@ -19,6 +19,7 @@
import sys
import json
+
def JsonLoad(json_file):
# Changed in Python 3.9: The keyword argument encoding has been removed.
if sys.version_info >= (3, 9):
diff --git a/blender-for-unrealengine/bfu_import_module/sequencer_import.py b/blender-for-unrealengine/bfu_import_module/sequencer_import.py
index 118af9dc..9970dc66 100644
--- a/blender-for-unrealengine/bfu_import_module/sequencer_import.py
+++ b/blender-for-unrealengine/bfu_import_module/sequencer_import.py
@@ -17,33 +17,30 @@
# ======================= END GPL LICENSE BLOCK =============================
import os.path
+
try:
import unreal
except ImportError:
import unreal_engine as unreal
-from . import bps
+
from . import import_module_utils
from . import import_module_unreal_utils
from . import sequencer_utils
-
-
-
-
def ready_for_sequence_import():
- if import_module_unreal_utils.is_unreal_version_greater_or_equal(4,20): # TO DO: EditorAssetLibrary was added in witch version exactly?
- if not hasattr(unreal, 'EditorAssetLibrary'):
- message = 'WARNING: Editor Scripting Utilities should be activated.' + "\n"
- message += 'Edit > Plugin > Scripting > Editor Scripting Utilities.'
- import_module_unreal_utils.show_warning_message("Editor Scripting Utilities not activated.", message)
- return False
- if not hasattr(unreal.MovieSceneSequence, 'set_display_rate'):
- message = 'WARNING: Editor Scripting Utilities should be activated.' + "\n"
- message += 'Edit > Plugin > Scripting > Sequencer Scripting.'
+ if not import_module_unreal_utils.editor_scripting_utilities_active():
+ message = 'WARNING: Editor Scripting Utilities Plugin should be activated.' + "\n"
+ message += 'Edit > Plugin > Scripting > Editor Scripting Utilities.'
import_module_unreal_utils.show_warning_message("Editor Scripting Utilities not activated.", message)
return False
+
+ if not import_module_unreal_utils.sequencer_scripting_active():
+ message = 'WARNING: Sequencer Scripting Plugin should be activated.' + "\n"
+ message += 'Edit > Plugin > Scripting > Sequencer Scripting.'
+ import_module_unreal_utils.show_warning_message("Sequencer Scripting not activated.", message)
+ return False
return True
def CreateSequencer(sequence_data, show_finished_popup=True):
diff --git a/blender-for-unrealengine/bfu_import_module/sequencer_import_script.py b/blender-for-unrealengine/bfu_import_module/sequencer_import_script.py
index 55ebf220..03a66105 100644
--- a/blender-for-unrealengine/bfu_import_module/sequencer_import_script.py
+++ b/blender-for-unrealengine/bfu_import_module/sequencer_import_script.py
@@ -1,65 +1,29 @@
-# This script was generated with the addons Blender for UnrealEngine : https://github.com/xavier150/Blender-For-UnrealEngine-Addons
-# It will import into Unreal Engine all the assets of type StaticMesh, SkeletalMesh, Animation and Pose
-# The script must be used in Unreal Engine Editor with Python plugins : https://docs.unrealengine.com/en-US/Engine/Editor/ScriptingAndAutomation/Python
-# Use this command in Unreal cmd consol: py "[ScriptLocation]\sequencer_import_script.py"
+# This script was generated with the addons Unreal Engine Assets Exporter.
+# This script should be run in Unreal Engine to import into Unreal Engine 4 and 5 assets.
+# The assets are exported from from Unreal Engine Assets Exporter. More detail here. https://github.com/xavier150/Blender-For-UnrealEngine-Addons
+# Use the following command in Unreal Engine cmd consol to import sequencer:
+# py "[ScriptLocation]\sequencer_import_script.py"
import importlib
-import sys
+import importlib.util
import os
-import json
+from . import import_module_utils
-def JsonLoad(json_file):
- # Changed in Python 3.9: The keyword argument encoding has been removed.
- if sys.version_info >= (3, 9):
- return json.load(json_file)
- else:
- return json.load(json_file, encoding="utf8")
-
-def JsonLoadFile(json_file_path):
- if sys.version_info[0] < 3:
- with open(json_file_path, "r") as json_file:
- return JsonLoad(json_file)
- else:
- with open(json_file_path, "r", encoding="utf8") as json_file:
- return JsonLoad(json_file)
-
-def load_module(import_module_path):
- # Import and run the module
- module_name = os.path.basename(import_module_path).replace('.py', '')
- module_dir = os.path.dirname(import_module_path)
-
- if module_dir not in sys.path:
- sys.path.append(module_dir)
-
- imported_module = importlib.import_module(module_name)
- importlib.reload(imported_module)
-
- # Assuming the module has a main function to run
- if hasattr(imported_module, 'main'):
- imported_module.main()
-
- return imported_module, module_name
-
-def unload_module(module_name):
- # Vérifier si le module est dans sys.modules
- if module_name in sys.modules:
- # Récupérer la référence du module
- module = sys.modules[module_name]
- # Supprimer la référence globale
- del sys.modules[module_name]
- del module
def RunImportScriptWithJsonData():
# Prepare process import
json_data_file = 'ImportSequencerData.json'
dir_path = os.path.dirname(os.path.realpath(__file__))
-
- sequence_data = JsonLoadFile(os.path.join(dir_path, json_data_file))
+ import_file_path = os.path.join(dir_path, json_data_file)
+ sequence_data = import_module_utils.JsonLoadFile(import_file_path)
- import_module_path = sequence_data["info"]["import_modiule_path"] # Module to run
- imported_module, module_name = load_module(import_module_path)
- imported_module.run_sequencer_import(sequence_data, False)
- unload_module(module_name)
+ file_path = os.path.join(sequence_data["info"]["addon_path"],'run_unreal_import_script.py')
+ spec = importlib.util.spec_from_file_location("__import_sequencer__", file_path)
+ module = importlib.util.module_from_spec(spec)
+
+ # Run script module function
+ spec.loader.exec_module(module)
+ module.run_from_sequencer_import_script(import_file_path)
if __name__ == "__main__":
RunImportScriptWithJsonData()
diff --git a/blender-for-unrealengine/bfu_import_module/sequencer_utils.py b/blender-for-unrealengine/bfu_import_module/sequencer_utils.py
index 3d2575a2..fc748913 100644
--- a/blender-for-unrealengine/bfu_import_module/sequencer_utils.py
+++ b/blender-for-unrealengine/bfu_import_module/sequencer_utils.py
@@ -16,241 +16,245 @@
#
# ======================= END GPL LICENSE BLOCK =============================
+from typing import TYPE_CHECKING, Dict, Any
+
try:
import unreal
except ImportError:
import unreal_engine as unreal
+
from . import import_module_utils
from . import import_module_unreal_utils
-if import_module_unreal_utils.is_unreal_version_greater_or_equal(5,1):
- MovieSceneBindingProxy = unreal.MovieSceneBindingProxy
-else:
- MovieSceneBindingProxy = unreal.SequencerBindingProxy
+# Since 5.1 MovieSceneBindingProxy replace SequencerBindingProxy.
+use_movie_scene = import_module_unreal_utils.is_unreal_version_greater_or_equal(5,1)
+sequencer_scripting_active = import_module_unreal_utils.sequencer_scripting_active()
+
+
+
+if sequencer_scripting_active:
-from typing import Dict, Any
+ def get_sequencer_framerate(denominator = 1, numerator = 24) -> unreal.FrameRate:
+ """
+ Adjusts the given frame rate to be compatible with Unreal Engine Sequencer.
-def get_sequencer_framerate(denominator = 1, numerator = 24) -> unreal.FrameRate:
- """
- Adjusts the given frame rate to be compatible with Unreal Engine Sequencer.
+ Ensures the denominator and numerator are integers over zero and warns if the input values are adjusted.
- Ensures the denominator and numerator are integers over zero and warns if the input values are adjusted.
+ Parameters:
+ - denominator (float): The original denominator value.
+ - numerator (float): The original numerator value.
- Parameters:
- - denominator (float): The original denominator value.
- - numerator (float): The original numerator value.
+ Returns:
+ - unreal.FrameRate: The adjusted frame rate object.
+ """
+ # Ensure denominator and numerator are at least 1 and int 32
+ new_denominator = max(round(denominator), 1)
+ new_numerator = max(round(numerator), 1)
+ myFFrameRate = unreal.FrameRate(numerator=new_numerator, denominator=new_denominator)
- Returns:
- - unreal.FrameRate: The adjusted frame rate object.
- """
- # Ensure denominator and numerator are at least 1 and int 32
- new_denominator = max(round(denominator), 1)
- new_numerator = max(round(numerator), 1)
- myFFrameRate = unreal.FrameRate(numerator=new_numerator, denominator=new_denominator)
+ if denominator != new_denominator or numerator != new_numerator:
+ message = ('WARNING: Frame rate denominator and numerator must be an int32 over zero.\n'
+ 'Float denominator and numerator is not supported in Unreal Engine Sequencer.\n\n'
+ f'- Before: Denominator: {denominator}, Numerator: {numerator}\n'
+ f'- After: Denominator: {new_denominator}, Numerator: {new_numerator}')
+ import_module_unreal_utils.show_warning_message("Frame Rate Adjustment Warning", message)
- if denominator != new_denominator or numerator != new_numerator:
- message = ('WARNING: Frame rate denominator and numerator must be an int32 over zero.\n'
- 'Float denominator and numerator is not supported in Unreal Engine Sequencer.\n\n'
- f'- Before: Denominator: {denominator}, Numerator: {numerator}\n'
- f'- After: Denominator: {new_denominator}, Numerator: {new_numerator}')
- import_module_unreal_utils.show_warning_message("Frame Rate Adjustment Warning", message)
+ return myFFrameRate
- return myFFrameRate
+ def get_section_all_channel(section: unreal.MovieSceneSection):
+ if import_module_unreal_utils.is_unreal_version_greater_or_equal(5,0):
+ return section.get_all_channels()
+ else:
+ return section.get_channels()
-def get_section_all_channel(section: unreal.MovieSceneSection):
- if import_module_unreal_utils.is_unreal_version_greater_or_equal(5,0):
- return section.get_all_channels()
- else:
- return section.get_channels()
+ def AddSequencerSectionTransformKeysByIniFile(section: unreal.MovieSceneSection, track_dict: Dict[str, Any]):
+ for key in track_dict.keys():
+ value = track_dict[key] # (x,y,z x,y,z x,y,z)
+ frame = unreal.FrameNumber(int(key))
-def AddSequencerSectionTransformKeysByIniFile(section: unreal.MovieSceneSection, track_dict: Dict[str, Any]):
- for key in track_dict.keys():
- value = track_dict[key] # (x,y,z x,y,z x,y,z)
- frame = unreal.FrameNumber(int(key))
+ get_section_all_channel(section)[0].add_key(frame, value["location_x"])
+ get_section_all_channel(section)[1].add_key(frame, value["location_y"])
+ get_section_all_channel(section)[2].add_key(frame, value["location_z"])
+ get_section_all_channel(section)[3].add_key(frame, value["rotation_x"])
+ get_section_all_channel(section)[4].add_key(frame, value["rotation_y"])
+ get_section_all_channel(section)[5].add_key(frame, value["rotation_z"])
+ get_section_all_channel(section)[6].add_key(frame, value["scale_x"])
+ get_section_all_channel(section)[7].add_key(frame, value["scale_y"])
+ get_section_all_channel(section)[8].add_key(frame, value["scale_z"])
- get_section_all_channel(section)[0].add_key(frame, value["location_x"])
- get_section_all_channel(section)[1].add_key(frame, value["location_y"])
- get_section_all_channel(section)[2].add_key(frame, value["location_z"])
- get_section_all_channel(section)[3].add_key(frame, value["rotation_x"])
- get_section_all_channel(section)[4].add_key(frame, value["rotation_y"])
- get_section_all_channel(section)[5].add_key(frame, value["rotation_z"])
- get_section_all_channel(section)[6].add_key(frame, value["scale_x"])
- get_section_all_channel(section)[7].add_key(frame, value["scale_y"])
- get_section_all_channel(section)[8].add_key(frame, value["scale_z"])
+ def AddSequencerSectionDoubleVectorKeysByIniFile(section, track_dict: Dict[str, Any]):
+ for key in track_dict.keys():
+ value = track_dict[key] # (x,y,z x,y,z x,y,z)
+ frame = unreal.FrameNumber(int(key))
+ get_section_all_channel(section)[0].add_key(frame, value["x"])
+ get_section_all_channel(section)[1].add_key(frame, value["y"])
-def AddSequencerSectionDoubleVectorKeysByIniFile(section, track_dict: Dict[str, Any]):
- for key in track_dict.keys():
- value = track_dict[key] # (x,y,z x,y,z x,y,z)
- frame = unreal.FrameNumber(int(key))
+ def AddSequencerSectionFloatKeysByIniFile(section, track_dict: Dict[str, Any]):
+ for key in track_dict.keys():
+ frame = unreal.FrameNumber(int(key))
+ value = track_dict[key]
- get_section_all_channel(section)[0].add_key(frame, value["x"])
- get_section_all_channel(section)[1].add_key(frame, value["y"])
+ get_section_all_channel(section)[0].add_key(frame, value)
-def AddSequencerSectionFloatKeysByIniFile(section, track_dict: Dict[str, Any]):
- for key in track_dict.keys():
- frame = unreal.FrameNumber(int(key))
- value = track_dict[key]
+ def AddSequencerSectionBoolKeysByIniFile(section, track_dict: Dict[str, Any]):
+ for key in track_dict.keys():
+ frame = unreal.FrameNumber(int(key))
+ value = track_dict[key]
- get_section_all_channel(section)[0].add_key(frame, value)
+ get_section_all_channel(section)[0].add_key(frame, value)
+ def create_new_sequence():
+ factory = unreal.LevelSequenceFactoryNew()
+ asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
+ seq = asset_tools.create_asset_with_dialog('MySequence', '/Game', None, factory)
+ if seq is None:
+ return 'ERROR: level sequencer factory_create fail'
+ return seq
-def AddSequencerSectionBoolKeysByIniFile(section, track_dict: Dict[str, Any]):
- for key in track_dict.keys():
- frame = unreal.FrameNumber(int(key))
- value = track_dict[key]
+ def Sequencer_add_new_camera(seq: unreal.LevelSequence, camera_target_class = unreal.CineCameraActor, camera_name = "MyCamera", is_spawnable_camera = False):
- get_section_all_channel(section)[0].add_key(frame, value)
+ #Create bindings
+ if is_spawnable_camera:
+ '''
+ I preffer create an level camera an convert to spawnable than use seq.add_spawnable_from_class()
+ Because with seq.add_spawnable_from_class() it not possible to change actor name an add create camera_component_binding.
+ Need more control in the API.
+ '''
+ # Create camera
+ temp_camera_actor = unreal.EditorLevelLibrary().spawn_actor_from_class(camera_target_class, unreal.Vector(0, 0, 0), unreal.Rotator(0, 0, 0))
+ temp_camera_actor.set_actor_label(camera_name)
-def create_new_sequence():
- factory = unreal.LevelSequenceFactoryNew()
- asset_tools = unreal.AssetToolsHelpers.get_asset_tools()
- seq = asset_tools.create_asset_with_dialog('MySequence', '/Game', None, factory)
- if seq is None:
- return 'ERROR: level sequencer factory_create fail'
- return seq
+ # Add camera to sequencer
+ temp_camera_binding = seq.add_possessable(temp_camera_actor)
-def Sequencer_add_new_camera(seq: unreal.LevelSequence, camera_target_class = unreal.CineCameraActor, camera_name = "MyCamera", is_spawnable_camera = False) -> MovieSceneBindingProxy:
+ if isinstance(temp_camera_actor, unreal.CineCameraActor):
+ camera_component_binding = seq.add_possessable(temp_camera_actor.get_cine_camera_component())
+ elif isinstance(temp_camera_actor, unreal.CameraActor):
+ camera_component_binding = seq.add_possessable(temp_camera_actor.camera_component)
+ else:
+ camera_component_binding = seq.add_possessable(temp_camera_actor.get_component_by_class(unreal.CameraComponent))
+ # Convert to spawnable
+ camera_binding = seq.add_spawnable_from_instance(temp_camera_actor)
+ camera_component_binding.set_parent(camera_binding)
+ temp_camera_binding.remove()
- #Create bindings
- if is_spawnable_camera:
- '''
- I preffer create an level camera an convert to spawnable than use seq.add_spawnable_from_class()
- Because with seq.add_spawnable_from_class() it not possible to change actor name an add create camera_component_binding.
- Need more control in the API.
- '''
+ #Clean old camera
+ temp_camera_actor.destroy_actor()
- # Create camera
- temp_camera_actor = unreal.EditorLevelLibrary().spawn_actor_from_class(camera_target_class, unreal.Vector(0, 0, 0), unreal.Rotator(0, 0, 0))
- temp_camera_actor.set_actor_label(camera_name)
- # Add camera to sequencer
- temp_camera_binding = seq.add_possessable(temp_camera_actor)
- if isinstance(temp_camera_actor, unreal.CineCameraActor):
- camera_component_binding = seq.add_possessable(temp_camera_actor.get_cine_camera_component())
- elif isinstance(temp_camera_actor, unreal.CameraActor):
- camera_component_binding = seq.add_possessable(temp_camera_actor.camera_component)
else:
- camera_component_binding = seq.add_possessable(temp_camera_actor.get_component_by_class(unreal.CameraComponent))
-
-
- # Convert to spawnable
- camera_binding = seq.add_spawnable_from_instance(temp_camera_actor)
- camera_component_binding.set_parent(camera_binding)
- temp_camera_binding.remove()
-
- #Clean old camera
- temp_camera_actor.destroy_actor()
-
-
-
- else:
- # Create possessable camera
- camera_actor = unreal.EditorLevelLibrary().spawn_actor_from_class(camera_target_class, unreal.Vector(0, 0, 0), unreal.Rotator(0, 0, 0))
- camera_actor.set_actor_label(camera_name)
- camera_binding = seq.add_possessable(camera_actor)
- camera_component_binding = seq.add_possessable(camera_actor.get_cine_camera_component())
-
- if import_module_unreal_utils.is_unreal_version_greater_or_equal(4,26):
- camera_binding.set_display_name(camera_name)
- else:
- pass
-
- return camera_binding, camera_component_binding
-
-
-
-def update_sequencer_camera_tracks(seq: unreal.LevelSequence, camera_binding: MovieSceneBindingProxy, camera_component_binding: MovieSceneBindingProxy, camera_tracks: Dict[str, Any]):
-
-
- # Transform
- transform_track = camera_binding.add_track(unreal.MovieScene3DTransformTrack)
- transform_section = transform_track.add_section()
- transform_section.set_end_frame_bounded(False)
- transform_section.set_start_frame_bounded(False)
- AddSequencerSectionTransformKeysByIniFile(transform_section, camera_tracks['ue_camera_transform'])
-
- # Focal Length
- TrackFocalLength = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
- TrackFocalLength.set_property_name_and_path('Current Focal Length', 'CurrentFocalLength')
- sectionFocalLength = TrackFocalLength.add_section()
- sectionFocalLength.set_end_frame_bounded(False)
- sectionFocalLength.set_start_frame_bounded(False)
- AddSequencerSectionFloatKeysByIniFile(sectionFocalLength, camera_tracks['camera_focal_length'])
-
- # Sensor Width
- TrackSensorWidth = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
- TrackSensorWidth.set_property_name_and_path('Sensor Width (Filmback)', 'Filmback.SensorWidth')
- sectionSensorWidth = TrackSensorWidth.add_section()
- sectionSensorWidth.set_end_frame_bounded(False)
- sectionSensorWidth.set_start_frame_bounded(False)
- AddSequencerSectionFloatKeysByIniFile(sectionSensorWidth, camera_tracks['ue_camera_sensor_width'])
-
- # Sensor Height
- TrackSensorHeight = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
- TrackSensorHeight.set_property_name_and_path('Sensor Height (Filmback)', 'Filmback.SensorHeight')
- sectionSensorHeight = TrackSensorHeight.add_section()
- sectionSensorHeight.set_end_frame_bounded(False)
- sectionSensorHeight.set_start_frame_bounded(False)
- AddSequencerSectionFloatKeysByIniFile(sectionSensorHeight, camera_tracks['ue_camera_sensor_height'])
-
- # Focus Distance
- TrackFocusDistance = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
- if import_module_unreal_utils.is_unreal_version_greater_or_equal(4,24):
- TrackFocusDistance.set_property_name_and_path('Manual Focus Distance (Focus Settings)', 'FocusSettings.ManualFocusDistance')
- else:
- TrackFocusDistance.set_property_name_and_path('Current Focus Distance', 'ManualFocusDistance')
- sectionFocusDistance = TrackFocusDistance.add_section()
- sectionFocusDistance.set_end_frame_bounded(False)
- sectionFocusDistance.set_start_frame_bounded(False)
- AddSequencerSectionFloatKeysByIniFile(sectionFocusDistance, camera_tracks['camera_focus_distance'])
-
- # Current Aperture
- TracknAperture = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
- TracknAperture.set_property_name_and_path('Current Aperture', 'CurrentAperture')
- sectionAperture = TracknAperture.add_section()
- sectionAperture.set_end_frame_bounded(False)
- sectionAperture.set_start_frame_bounded(False)
- AddSequencerSectionFloatKeysByIniFile(sectionAperture, camera_tracks['camera_aperture'])
-
- if camera_tracks['camera_type'] == "ARCHVIS":
-
- # MovieSceneDoubleVectorTrack not supported in Unreal Engine 5.0 and older
- if import_module_unreal_utils.is_unreal_version_greater_or_equal(5,0):
- # Camera Shift X/Y
- TrackArchVisShift = camera_component_binding.add_track(unreal.MovieSceneDoubleVectorTrack)
- TrackArchVisShift.set_property_name_and_path('Manual Correction (Shift)', 'ProjectionOffset')
- TrackArchVisShift.set_num_channels_used(2)
- SectionArchVisShift = TrackArchVisShift.add_section()
- SectionArchVisShift.set_end_frame_bounded(False)
- SectionArchVisShift.set_start_frame_bounded(False)
- AddSequencerSectionDoubleVectorKeysByIniFile(SectionArchVisShift, camera_tracks['archvis_camera_shift'])
-
- # Disable auto correct perspective
- TrackArchVisCorrectPersp = camera_component_binding.add_track(unreal.MovieSceneBoolTrack)
- TrackArchVisCorrectPersp.set_property_name_and_path('Correct Perspective (Auto)', 'bCorrectPerspective')
- SectionArchVisCorrectPersp = TrackArchVisCorrectPersp.add_section()
- start_frame = unreal.FrameNumber(int(camera_tracks['frame_start']))
- get_section_all_channel(SectionArchVisCorrectPersp)[0].add_key(start_frame, False)
-
-
-
- # Spawned
- tracksSpawned = camera_binding.find_tracks_by_exact_type(unreal.MovieSceneSpawnTrack)
- if len(tracksSpawned) > 0:
- sectionSpawned = tracksSpawned[0].get_sections()[0]
- AddSequencerSectionBoolKeysByIniFile(sectionSpawned, camera_tracks['camera_spawned'])
-
- # @TODO Need found a way to set this values...
- #camera_component.set_editor_property('aspect_ratio', camera_tracks['desired_screen_ratio'])
-
- #Projection mode supported since UE 4.26.
- #camera_component.set_editor_property('projection_mode', camera_tracks['projection_mode'])
- #camera_component.set_editor_property('ortho_width', camera_tracks['ortho_scale'])
+ # Create possessable camera
+ camera_actor = unreal.EditorLevelLibrary().spawn_actor_from_class(camera_target_class, unreal.Vector(0, 0, 0), unreal.Rotator(0, 0, 0))
+ camera_actor.set_actor_label(camera_name)
+ camera_binding = seq.add_possessable(camera_actor)
+ camera_component_binding = seq.add_possessable(camera_actor.get_cine_camera_component())
+
+ if import_module_unreal_utils.is_unreal_version_greater_or_equal(4,26):
+ camera_binding.set_display_name(camera_name)
+ else:
+ pass
+
+ return camera_binding, camera_component_binding
+
+ def update_sequencer_camera_tracks(seq: unreal.LevelSequence, camera_binding, camera_component_binding, camera_tracks: Dict[str, Any]):
+
+ if TYPE_CHECKING:
+ if use_movie_scene:
+ camera_binding: unreal.MovieSceneBindingProxy
+ camera_component_binding: unreal.MovieSceneBindingProxy
+ else:
+ camera_binding: unreal.SequencerBindingProxy
+ camera_component_binding: unreal.SequencerBindingProxy
+
+ # Transform
+ transform_track = camera_binding.add_track(unreal.MovieScene3DTransformTrack)
+ transform_section = transform_track.add_section()
+ transform_section.set_end_frame_bounded(False)
+ transform_section.set_start_frame_bounded(False)
+ AddSequencerSectionTransformKeysByIniFile(transform_section, camera_tracks['ue_camera_transform'])
+
+ # Focal Length
+ TrackFocalLength = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
+ TrackFocalLength.set_property_name_and_path('Current Focal Length', 'CurrentFocalLength')
+ sectionFocalLength = TrackFocalLength.add_section()
+ sectionFocalLength.set_end_frame_bounded(False)
+ sectionFocalLength.set_start_frame_bounded(False)
+ AddSequencerSectionFloatKeysByIniFile(sectionFocalLength, camera_tracks['camera_focal_length'])
+
+ # Sensor Width
+ TrackSensorWidth = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
+ TrackSensorWidth.set_property_name_and_path('Sensor Width (Filmback)', 'Filmback.SensorWidth')
+ sectionSensorWidth = TrackSensorWidth.add_section()
+ sectionSensorWidth.set_end_frame_bounded(False)
+ sectionSensorWidth.set_start_frame_bounded(False)
+ AddSequencerSectionFloatKeysByIniFile(sectionSensorWidth, camera_tracks['ue_camera_sensor_width'])
+
+ # Sensor Height
+ TrackSensorHeight = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
+ TrackSensorHeight.set_property_name_and_path('Sensor Height (Filmback)', 'Filmback.SensorHeight')
+ sectionSensorHeight = TrackSensorHeight.add_section()
+ sectionSensorHeight.set_end_frame_bounded(False)
+ sectionSensorHeight.set_start_frame_bounded(False)
+ AddSequencerSectionFloatKeysByIniFile(sectionSensorHeight, camera_tracks['ue_camera_sensor_height'])
+
+ # Focus Distance
+ TrackFocusDistance = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
+ if import_module_unreal_utils.is_unreal_version_greater_or_equal(4,24):
+ TrackFocusDistance.set_property_name_and_path('Manual Focus Distance (Focus Settings)', 'FocusSettings.ManualFocusDistance')
+ else:
+ TrackFocusDistance.set_property_name_and_path('Current Focus Distance', 'ManualFocusDistance')
+ sectionFocusDistance = TrackFocusDistance.add_section()
+ sectionFocusDistance.set_end_frame_bounded(False)
+ sectionFocusDistance.set_start_frame_bounded(False)
+ AddSequencerSectionFloatKeysByIniFile(sectionFocusDistance, camera_tracks['camera_focus_distance'])
+
+ # Current Aperture
+ TracknAperture = camera_component_binding.add_track(unreal.MovieSceneFloatTrack)
+ TracknAperture.set_property_name_and_path('Current Aperture', 'CurrentAperture')
+ sectionAperture = TracknAperture.add_section()
+ sectionAperture.set_end_frame_bounded(False)
+ sectionAperture.set_start_frame_bounded(False)
+ AddSequencerSectionFloatKeysByIniFile(sectionAperture, camera_tracks['camera_aperture'])
+
+ if camera_tracks['camera_type'] == "ARCHVIS":
+
+ # MovieSceneDoubleVectorTrack not supported in Unreal Engine 5.0 and older
+ if import_module_unreal_utils.is_unreal_version_greater_or_equal(5,0):
+ # Camera Shift X/Y
+ TrackArchVisShift = camera_component_binding.add_track(unreal.MovieSceneDoubleVectorTrack)
+ TrackArchVisShift.set_property_name_and_path('Manual Correction (Shift)', 'ProjectionOffset')
+ TrackArchVisShift.set_num_channels_used(2)
+ SectionArchVisShift = TrackArchVisShift.add_section()
+ SectionArchVisShift.set_end_frame_bounded(False)
+ SectionArchVisShift.set_start_frame_bounded(False)
+ AddSequencerSectionDoubleVectorKeysByIniFile(SectionArchVisShift, camera_tracks['archvis_camera_shift'])
+
+ # Disable auto correct perspective
+ TrackArchVisCorrectPersp = camera_component_binding.add_track(unreal.MovieSceneBoolTrack)
+ TrackArchVisCorrectPersp.set_property_name_and_path('Correct Perspective (Auto)', 'bCorrectPerspective')
+ SectionArchVisCorrectPersp = TrackArchVisCorrectPersp.add_section()
+ start_frame = unreal.FrameNumber(int(camera_tracks['frame_start']))
+ get_section_all_channel(SectionArchVisCorrectPersp)[0].add_key(start_frame, False)
+
+
+
+ # Spawned
+ tracksSpawned = camera_binding.find_tracks_by_exact_type(unreal.MovieSceneSpawnTrack)
+ if len(tracksSpawned) > 0:
+ sectionSpawned = tracksSpawned[0].get_sections()[0]
+ AddSequencerSectionBoolKeysByIniFile(sectionSpawned, camera_tracks['camera_spawned'])
+
+ # @TODO Need found a way to set this values...
+ #camera_component.set_editor_property('aspect_ratio', camera_tracks['desired_screen_ratio'])
- #camera_component.lens_settings.set_editor_property('min_f_stop', camera_tracks['ue_lens_minfstop'])
- #camera_component.lens_settings.set_editor_property('max_f_stop', camera_tracks['ue_lens_maxfstop'])
+ #Projection mode supported since UE 4.26.
+ #camera_component.set_editor_property('projection_mode', camera_tracks['projection_mode'])
+ #camera_component.set_editor_property('ortho_width', camera_tracks['ortho_scale'])
+
+ #camera_component.lens_settings.set_editor_property('min_f_stop', camera_tracks['ue_lens_minfstop'])
+ #camera_component.lens_settings.set_editor_property('max_f_stop', camera_tracks['ue_lens_maxfstop'])
diff --git a/blender-for-unrealengine/bfu_light_map/bfu_light_map_props.py b/blender-for-unrealengine/bfu_light_map/bfu_light_map_props.py
index 670bfdbf..965187d0 100644
--- a/blender-for-unrealengine/bfu_light_map/bfu_light_map_props.py
+++ b/blender-for-unrealengine/bfu_light_map/bfu_light_map_props.py
@@ -25,11 +25,20 @@
from .. import bbpl
-
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_static_mesh_light_map_mode',
+ 'obj.bfu_static_mesh_custom_light_map_res',
+ 'obj.bfu_static_mesh_light_map_surface_scale',
+ 'obj.bfu_static_mesh_light_map_round_power_of_two',
+ 'obj.bfu_use_static_mesh_light_map_world_scale',
+ 'obj.bfu_generate_light_map_uvs',
+ ]
+ return preset_values
class BFU_OT_ComputLightMap(bpy.types.Operator):
bl_label = "Calculate surface area"
- bl_idname = "object.computlightmap"
+ bl_idname = "object.comput_lightmap"
bl_description = "Click to calculate the surface of the object"
def execute(self, context):
@@ -43,7 +52,7 @@ def execute(self, context):
class BFU_OT_ComputAllLightMap(bpy.types.Operator):
bl_label = "Calculate all surface area"
- bl_idname = "object.computalllightmap"
+ bl_idname = "object.comput_all_lightmap"
bl_description = (
"Click to calculate the surface of the all object in the scene"
)
@@ -68,9 +77,97 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
+ bpy.types.Scene.bfu_object_light_map_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Light map")
+ bpy.types.Scene.bfu_tools_light_map_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Light Map")
+
+
+ # StaticMeshImportData
+ # https://api.unrealengine.com/INT/API/Editor/UnrealEd/Factories/UFbxStaticMeshImportData/index.html
+
+
+ bpy.types.Object.bfu_static_mesh_light_map_mode = bpy.props.EnumProperty(
+ name="Light Map",
+ description='Specify how the light map resolution will be generated',
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ("Default",
+ "Default",
+ "Has no effect on light maps",
+ 1),
+ ("CustomMap",
+ "Custom map",
+ "Set the custom light map resolution",
+ 2),
+ ("SurfaceArea",
+ "Surface Area",
+ "Set light map resolution depending on the surface Area",
+ 3)
+ ]
+ )
+
+ bpy.types.Object.bfu_static_mesh_custom_light_map_res = bpy.props.IntProperty(
+ name="Light Map Resolution",
+ description="This is the resolution of the light map",
+ override={'LIBRARY_OVERRIDABLE'},
+ soft_max=2048,
+ soft_min=16,
+ max=4096, # Max for unreal
+ min=4, # Min for unreal
+ default=64
+ )
+
+ bpy.types.Object.computedStaticMeshLightMapRes = bpy.props.FloatProperty(
+ name="Computed Light Map Resolution",
+ description="This is the computed resolution of the light map",
+ override={'LIBRARY_OVERRIDABLE'},
+ default=64.0
+ )
+
+ bpy.types.Object.bfu_static_mesh_light_map_surface_scale = bpy.props.FloatProperty(
+ name="Surface scale",
+ description="This is for resacle the surface Area value",
+ override={'LIBRARY_OVERRIDABLE'},
+ min=0.00001, # Min for unreal
+ default=64
+ )
+ bpy.types.Object.bfu_static_mesh_light_map_round_power_of_two = bpy.props.BoolProperty(
+ name="Round power of 2",
+ description=(
+ "round Light Map resolution to nearest power of 2"
+ ),
+ default=True
+ )
+ bpy.types.Object.bfu_use_static_mesh_light_map_world_scale = bpy.props.BoolProperty(
+ name="Use world scale",
+ description=(
+ "If not that will use the object scale."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+ bpy.types.Object.bfu_generate_light_map_uvs = bpy.props.BoolProperty(
+ name="Generate LightmapUVs",
+ description=(
+ "If checked, UVs for Lightmap will automatically be generated."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True,
+ )
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_generate_light_map_uvs
+ del bpy.types.Object.bfu_use_static_mesh_light_map_world_scale
+ del bpy.types.Object.bfu_static_mesh_light_map_round_power_of_two
+ del bpy.types.Object.bfu_static_mesh_light_map_surface_scale
+ del bpy.types.Object.computedStaticMeshLightMapRes
+ del bpy.types.Object.bfu_static_mesh_custom_light_map_res
+ del bpy.types.Object.bfu_static_mesh_light_map_mode
+
+ del bpy.types.Scene.bfu_tools_light_map_properties_expanded
+ del bpy.types.Scene.bfu_object_light_map_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_light_map/bfu_light_map_ui.py b/blender-for-unrealengine/bfu_light_map/bfu_light_map_ui.py
index 51398bc4..18b1c673 100644
--- a/blender-for-unrealengine/bfu_light_map/bfu_light_map_ui.py
+++ b/blender-for-unrealengine/bfu_light_map/bfu_light_map_ui.py
@@ -18,14 +18,56 @@
import bpy
+from . import bfu_light_map_utils
from .. import bfu_basics
from .. import bfu_utils
from .. import bfu_ui
from .. import bbpl
+from .. import bfu_static_mesh
-def draw_ui_object(layout: bpy.types.UILayout, obj: bpy.types.Object):
- pass
+def draw_obj_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
-def draw_ui_scene(layout: bpy.types.UILayout):
- pass
\ No newline at end of file
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+ is_static_mesh = bfu_static_mesh.bfu_static_mesh_utils.is_static_mesh(obj)
+
+ # Hide filters
+ if obj is None:
+ return
+ if addon_prefs.useGeneratedScripts is False:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "MISC"):
+ scene.bfu_object_light_map_properties_expanded.draw(layout)
+ if scene.bfu_object_light_map_properties_expanded.is_expend():
+ if is_static_mesh:
+ StaticMeshLightMapRes = layout.box()
+ StaticMeshLightMapRes.prop(obj, 'bfu_static_mesh_light_map_mode')
+ if obj.bfu_static_mesh_light_map_mode == "CustomMap":
+ CustomLightMap = StaticMeshLightMapRes.column()
+ CustomLightMap.prop(obj, 'bfu_static_mesh_custom_light_map_res')
+ if obj.bfu_static_mesh_light_map_mode == "SurfaceArea":
+ SurfaceAreaLightMap = StaticMeshLightMapRes.column()
+ SurfaceAreaLightMapButton = SurfaceAreaLightMap.row()
+ SurfaceAreaLightMapButton.operator("object.comput_lightmap", icon='TEXTURE')
+ SurfaceAreaLightMapButton.operator("object.comput_all_lightmap", icon='TEXTURE')
+ SurfaceAreaLightMap.prop(obj, 'bfu_use_static_mesh_light_map_world_scale')
+ SurfaceAreaLightMap.prop(obj, 'bfu_static_mesh_light_map_surface_scale')
+ SurfaceAreaLightMap.prop(obj, 'bfu_static_mesh_light_map_round_power_of_two')
+ if obj.bfu_static_mesh_light_map_mode != "Default":
+ CompuntedLightMap = str(bfu_light_map_utils.GetCompuntedLightMap(obj))
+ StaticMeshLightMapRes.label(text='Compunted light map: ' + CompuntedLightMap)
+ bfu_generate_light_map_uvs = layout.row()
+ bfu_generate_light_map_uvs.prop(obj, 'bfu_generate_light_map_uvs')
+
+def draw_tools_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
+ scene.bfu_tools_light_map_properties_expanded.draw(layout)
+ if scene.bfu_tools_light_map_properties_expanded.is_expend():
+ checkButton = layout.column()
+ checkButton.operator("object.comput_all_lightmap", icon='TEXTURE')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_light_map/bfu_light_map_utils.py b/blender-for-unrealengine/bfu_light_map/bfu_light_map_utils.py
index 66c1619a..bbd8a1df 100644
--- a/blender-for-unrealengine/bfu_light_map/bfu_light_map_utils.py
+++ b/blender-for-unrealengine/bfu_light_map/bfu_light_map_utils.py
@@ -18,13 +18,10 @@
import bpy
import fnmatch
-from .. import bps
+from .. import bpl
from .. import bbpl
from .. import bfu_basics
from .. import bfu_utils
-from .. import bfu_unreal_utils
-from .. import bfu_export_logs
-from .. import bfu_assets_manager
from .. import bfu_static_mesh
@@ -33,7 +30,7 @@ def GetExportRealSurfaceArea(obj):
local_view_areas = bbpl.scene_utils.move_to_global_view()
bbpl.utils.safe_mode_set('OBJECT')
- SavedSelect = bbpl.utils.UserSelectSave()
+ SavedSelect = bbpl.save_data.select_save.UserSelectSave()
SavedSelect.save_current_select()
bfu_utils.SelectParentAndDesiredChilds(obj)
@@ -66,7 +63,7 @@ def GetExportRealSurfaceArea(obj):
active = bpy.context.view_layer.objects.active
area = bfu_basics.GetSurfaceArea(active)
bfu_utils.CleanDeleteObjects(bpy.context.selected_objects)
- SavedSelect.reset_select_by_ref()
+ SavedSelect.reset_select()
bbpl.scene_utils.move_to_local_view(local_view_areas)
return area
@@ -95,7 +92,7 @@ def GetCompuntedLightMap(obj):
area *= obj.bfu_static_mesh_light_map_surface_scale/2
if obj.bfu_static_mesh_light_map_round_power_of_two:
- return bps.math.nearest_power_of_two(int(round(area)))
+ return bpl.math.nearest_power_of_two(int(round(area)))
return int(round(area))
def UpdateAreaLightMapList(objects_to_update=None):
@@ -113,7 +110,7 @@ def UpdateAreaLightMapList(objects_to_update=None):
UpdatedRes = 0
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
for obj in objs:
obj.computedStaticMeshLightMapRes = GetExportRealSurfaceArea(obj)
UpdatedRes += 1
diff --git a/blender-for-unrealengine/bfu_lod/bfu_lod_props.py b/blender-for-unrealengine/bfu_lod/bfu_lod_props.py
index 6a1c8c85..06dfa9e0 100644
--- a/blender-for-unrealengine/bfu_lod/bfu_lod_props.py
+++ b/blender-for-unrealengine/bfu_lod/bfu_lod_props.py
@@ -27,6 +27,19 @@
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_export_as_lod_mesh',
+ 'obj.bfu_use_static_mesh_lod_group',
+ 'obj.bfu_static_mesh_lod_group',
+ 'obj.bfu_lod_target1',
+ 'obj.bfu_lod_target2',
+ 'obj.bfu_lod_target3',
+ 'obj.bfu_lod_target4',
+ 'obj.bfu_lod_target5',
+ ]
+ return preset_values
+
# -------------------------------------------------------------------
# Register & Unregister
# -------------------------------------------------------------------
@@ -41,9 +54,85 @@ def register():
bpy.types.Scene.bfu_lod_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Lod")
+ bpy.types.Object.bfu_export_as_lod_mesh = bpy.props.BoolProperty(
+ name="Export as lod?",
+ description=(
+ "If true this mesh will be exported" +
+ " as a level of detail for another mesh"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+ # Lod Group
+ bpy.types.Object.bfu_use_static_mesh_lod_group = bpy.props.BoolProperty(
+ name="",
+ description='',
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
+
+ bpy.types.Object.bfu_static_mesh_lod_group = bpy.props.StringProperty(
+ name="LOD Group",
+ description=(
+ "The LODGroup to associate with this mesh when it is imported." +
+ " Default: LevelArchitecture, SmallProp, " +
+ "LargeProp, Deco, Vista, Foliage, HighDetail"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ maxlen=32,
+ default="SmallProp"
+ )
+
+ # Lod list
+ bpy.types.Object.bfu_lod_target1 = bpy.props.PointerProperty(
+ name="LOD1",
+ description="Target objet for level of detail 01",
+ override={'LIBRARY_OVERRIDABLE'},
+ type=bpy.types.Object
+ )
+
+ bpy.types.Object.bfu_lod_target2 = bpy.props.PointerProperty(
+ name="LOD2",
+ description="Target objet for level of detail 02",
+ override={'LIBRARY_OVERRIDABLE'},
+ type=bpy.types.Object
+ )
+
+ bpy.types.Object.bfu_lod_target3 = bpy.props.PointerProperty(
+ name="LOD3",
+ description="Target objet for level of detail 03",
+ override={'LIBRARY_OVERRIDABLE'},
+ type=bpy.types.Object
+ )
+
+ bpy.types.Object.bfu_lod_target4 = bpy.props.PointerProperty(
+ name="LOD4",
+ description="Target objet for level of detail 04",
+ override={'LIBRARY_OVERRIDABLE'},
+ type=bpy.types.Object
+ )
+
+ bpy.types.Object.bfu_lod_target5 = bpy.props.PointerProperty(
+ name="LOD5",
+ description="Target objet for level of detail 05",
+ override={'LIBRARY_OVERRIDABLE'},
+ type=bpy.types.Object
+ )
+
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
+ del bpy.types.Object.bfu_lod_target5
+ del bpy.types.Object.bfu_lod_target4
+ del bpy.types.Object.bfu_lod_target3
+ del bpy.types.Object.bfu_lod_target2
+ del bpy.types.Object.bfu_lod_target1
+
+ del bpy.types.Object.bfu_static_mesh_lod_group
+ del bpy.types.Object.bfu_use_static_mesh_lod_group
+
+ del bpy.types.Object.bfu_export_as_lod_mesh
del bpy.types.Scene.bfu_lod_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_lod/bfu_lod_ui.py b/blender-for-unrealengine/bfu_lod/bfu_lod_ui.py
index 9087aa88..3de0ed91 100644
--- a/blender-for-unrealengine/bfu_lod/bfu_lod_ui.py
+++ b/blender-for-unrealengine/bfu_lod/bfu_lod_ui.py
@@ -24,9 +24,10 @@
from .. import bfu_ui
from .. import bbpl
from .. import bfu_assets_manager
+from .. import bfu_static_mesh
-def draw_ui_object(layout: bpy.types.UILayout, obj: bpy.types.Object):
+def draw_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
if obj is None:
return
@@ -34,17 +35,44 @@ def draw_ui_object(layout: bpy.types.UILayout, obj: bpy.types.Object):
scene = bpy.context.scene
addon_prefs = bfu_basics.GetAddonPrefs()
- if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if addon_prefs.useGeneratedScripts is False:
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+
+ # Draw UI
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "MISC"):
scene.bfu_lod_properties_expanded.draw(layout)
if scene.bfu_lod_properties_expanded.is_expend():
- if obj.bfu_export_type == "export_recursive":
- if not bfu_utils.GetExportAsProxy(obj):
- if addon_prefs.useGeneratedScripts:
- # Unreal python no longer support Skeletal mesh LODS import.
- asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
- if asset_class and asset_class.use_lods == True:
- LodProp = layout.column()
- LodProp.prop(obj, 'bfu_export_as_lod_mesh')
-
-def draw_ui_scene(layout: bpy.types.UILayout):
- pass
\ No newline at end of file
+
+ # Unreal python no longer support Skeletal mesh LODS import.
+ asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
+ if asset_class and asset_class.use_lods == True:
+ LodProp = layout.column()
+ LodProp.prop(obj, 'bfu_export_as_lod_mesh')
+
+ #Static only because Unreal python not support Skeletal mesh LODS import.
+ if (bfu_static_mesh.bfu_static_mesh_utils.is_static_mesh(obj)):
+
+ # Lod Groups
+ bfu_static_mesh_lod_group = layout.row()
+ bfu_static_mesh_lod_group.prop(obj, 'bfu_use_static_mesh_lod_group', text="")
+ SMLODGroupChild = bfu_static_mesh_lod_group.column()
+ SMLODGroupChild.enabled = obj.bfu_use_static_mesh_lod_group
+ SMLODGroupChild.prop(obj, 'bfu_static_mesh_lod_group')
+ bfu_static_mesh_lod_group.enabled = obj.bfu_export_as_lod_mesh is False
+
+ # Lod Slots
+ LodList = layout.column()
+ LodList.prop(obj, 'bfu_lod_target1')
+ LodList.prop(obj, 'bfu_lod_target2')
+ LodList.prop(obj, 'bfu_lod_target3')
+ LodList.prop(obj, 'bfu_lod_target4')
+ LodList.prop(obj, 'bfu_lod_target5')
+ LodList.enabled = obj.bfu_export_as_lod_mesh is False and obj.bfu_use_static_mesh_lod_group is False
+
diff --git a/blender-for-unrealengine/bfu_lod/bfu_lod_utils.py b/blender-for-unrealengine/bfu_lod/bfu_lod_utils.py
index db041b58..a6e76eb3 100644
--- a/blender-for-unrealengine/bfu_lod/bfu_lod_utils.py
+++ b/blender-for-unrealengine/bfu_lod/bfu_lod_utils.py
@@ -19,8 +19,4 @@
import bpy
import fnmatch
from .. import bbpl
-from .. import bfu_basics
-from .. import bfu_utils
-from .. import bfu_unreal_utils
-from .. import bfu_export_logs
diff --git a/blender-for-unrealengine/bfu_material/bfu_material_props.py b/blender-for-unrealengine/bfu_material/bfu_material_props.py
index 27d5dcc9..45c3def2 100644
--- a/blender-for-unrealengine/bfu_material/bfu_material_props.py
+++ b/blender-for-unrealengine/bfu_material/bfu_material_props.py
@@ -60,9 +60,9 @@ def register():
default=False
)
- # Used for set invert_normal_maps in FbxTextureImportData
+ # Used for set flip_normal_map_green_channel in FbxTextureImportData
# https://docs.unrealengine.com/5.3/en-US/PythonAPI/class/FbxTextureImportData.html
- bpy.types.Object.bfu_invert_normal_maps = bpy.props.BoolProperty(
+ bpy.types.Object.bfu_flip_normal_map_green_channel = bpy.props.BoolProperty(
name="Invert Normal Maps",
description="This option will cause normal map Y (Green) values to be inverted.",
default=False
@@ -117,7 +117,7 @@ def unregister():
del bpy.types.Object.bfu_material_search_location
del bpy.types.Object.bfu_reorder_material_to_fbx_order
- del bpy.types.Object.bfu_invert_normal_maps
+ del bpy.types.Object.bfu_flip_normal_map_green_channel
del bpy.types.Object.bfu_import_textures
del bpy.types.Object.bfu_import_materials
diff --git a/blender-for-unrealengine/bfu_material/bfu_material_ui.py b/blender-for-unrealengine/bfu_material/bfu_material_ui.py
index 1926b87e..01dedd34 100644
--- a/blender-for-unrealengine/bfu_material/bfu_material_ui.py
+++ b/blender-for-unrealengine/bfu_material/bfu_material_ui.py
@@ -27,7 +27,7 @@
-def draw_ui_object_collision(layout: bpy.types.UILayout):
+def draw_ui_object(layout: bpy.types.UILayout):
if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "MISC"):
scene = bpy.context.scene
@@ -46,7 +46,7 @@ def draw_ui_object_collision(layout: bpy.types.UILayout):
bfu_material_search_location.prop(obj, 'bfu_material_search_location')
bfu_material_search_location.prop(obj, 'bfu_import_materials')
bfu_material_search_location.prop(obj, 'bfu_import_textures')
- bfu_material_search_location.prop(obj, 'bfu_invert_normal_maps')
+ bfu_material_search_location.prop(obj, 'bfu_flip_normal_map_green_channel')
bfu_material_search_location.prop(obj, 'bfu_reorder_material_to_fbx_order')
diff --git a/blender-for-unrealengine/bfu_material/bfu_material_utils.py b/blender-for-unrealengine/bfu_material/bfu_material_utils.py
index a0f86577..568d8b3b 100644
--- a/blender-for-unrealengine/bfu_material/bfu_material_utils.py
+++ b/blender-for-unrealengine/bfu_material/bfu_material_utils.py
@@ -30,7 +30,7 @@ def get_material_asset_data(asset: bfu_export_logs.BFU_OT_UnrealExportedAsset):
if asset.asset_type in ["StaticMesh", "SkeletalMesh"]:
asset_data["import_materials"] = asset.object.bfu_import_materials
asset_data["import_textures"] = asset.object.bfu_import_textures
- asset_data["invert_normal_maps"] = asset.object.bfu_invert_normal_maps
+ asset_data["flip_normal_map_green_channel"] = asset.object.bfu_flip_normal_map_green_channel
asset_data["reorder_material_to_fbx_order"] = asset.object.bfu_reorder_material_to_fbx_order
asset_data["material_search_location"] = asset.object.bfu_material_search_location
return asset_data
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_modular_skeletal_mesh/__init__.py b/blender-for-unrealengine/bfu_modular_skeletal_mesh/__init__.py
new file mode 100644
index 00000000..588d2061
--- /dev/null
+++ b/blender-for-unrealengine/bfu_modular_skeletal_mesh/__init__.py
@@ -0,0 +1,54 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_modular_skeletal_mesh_props
+from . import bfu_modular_skeletal_mesh_type
+from . import bfu_modular_skeletal_mesh_ui
+from . import bfu_modular_skeletal_mesh_utils
+
+
+if "bfu_modular_skeletal_mesh_props" in locals():
+ importlib.reload(bfu_modular_skeletal_mesh_props)
+if "bfu_modular_skeletal_mesh_ui" in locals():
+ importlib.reload(bfu_modular_skeletal_mesh_ui)
+if "bfu_modular_skeletal_mesh_utils" in locals():
+ importlib.reload(bfu_modular_skeletal_mesh_utils)
+if "bfu_modular_skeletal_mesh_type" in locals():
+ importlib.reload(bfu_modular_skeletal_mesh_type)
+
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_modular_skeletal_mesh_props.register()
+ bfu_modular_skeletal_mesh_type.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_modular_skeletal_mesh_type.unregister()
+ bfu_modular_skeletal_mesh_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_props.py b/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_props.py
new file mode 100644
index 00000000..57286b84
--- /dev/null
+++ b/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_props.py
@@ -0,0 +1,80 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bbpl
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_modular_skeletal_mesh_mode',
+ 'obj.bfu_modular_skeletal_mesh_every_meshs_separate',
+ 'obj.bfu_modular_skeletal_specified_parts_meshs_template'
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_modular_skeletal_mesh_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Modular Skeletal Mesh")
+
+ bpy.types.Object.bfu_modular_skeletal_mesh_mode = bpy.props.EnumProperty(
+ name="Modular Skeletal Mesh Mode",
+ description='Modular skeletal mesh mode',
+ override={'LIBRARY_OVERRIDABLE'},
+ items=[
+ ("all_in_one",
+ "All In One",
+ "Export all child meshs of the armature as one skeletal mesh.",
+ 1),
+ ("every_meshs",
+ "Every Meshs",
+ "Export one skeletal mesh for every child meshs of the armature.",
+ 2),
+ ("specified_parts",
+ "Specified Parts",
+ "Export specified mesh parts.",
+ 3)
+ ]
+ )
+
+ bpy.types.Object.bfu_modular_skeletal_mesh_every_meshs_separate = bpy.props.StringProperty(
+ name="Separate string",
+ description="String between armature name and mesh name",
+ override={'LIBRARY_OVERRIDABLE'},
+ default="_"
+ )
+
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Scene.bfu_modular_skeletal_mesh_properties_expanded
+ del bpy.types.Object.bfu_modular_skeletal_mesh_every_meshs_separate
+ del bpy.types.Object.bfu_modular_skeletal_mesh_mode
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_addon_parts/bfu_modular_skeletal_specified_parts_meshs.py b/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_type.py
similarity index 67%
rename from blender-for-unrealengine/bfu_addon_parts/bfu_modular_skeletal_specified_parts_meshs.py
rename to blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_type.py
index bb2e874a..c081e196 100644
--- a/blender-for-unrealengine/bfu_addon_parts/bfu_modular_skeletal_specified_parts_meshs.py
+++ b/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_type.py
@@ -16,23 +16,17 @@
#
# ======================= END GPL LICENSE BLOCK =============================
+import os
import bpy
+import fnmatch
from .. import bbpl
-def get_preset_values():
- preset_values = [
- 'obj.bfu_modular_skeletal_mesh_mode',
- 'obj.bfu_modular_skeletal_mesh_every_meshs_separate',
- 'obj.bfu_modular_skeletal_specified_parts_meshs_template'
- ]
- return preset_values
-
BBPL_UI_TemplateItem = bbpl.blender_layout.layout_template_list.types.create_template_item_class()
-BBPL_UI_TemplateItemDraw = bbpl.blender_layout.layout_template_list.types.create_template_item_draw_class()
-BBPL_UI_TemplateList = bbpl.blender_layout.layout_template_list.types.create_template_list_class(BBPL_UI_TemplateItem, BBPL_UI_TemplateItemDraw)
+BBPL_UL_TemplateItemDraw = bbpl.blender_layout.layout_template_list.types.create_template_item_draw_class()
+BBPL_UI_TemplateList = bbpl.blender_layout.layout_template_list.types.create_template_list_class(BBPL_UI_TemplateItem, BBPL_UL_TemplateItemDraw)
-class BFU_UL_ModularSkeletalSpecifiedPartsTargetItem(BBPL_UI_TemplateItem):
+class BFU_UI_ModularSkeletalSpecifiedPartsTargetItem(BBPL_UI_TemplateItem): # Item class (bpy.types.PropertyGroup)
enabled: bpy.props.BoolProperty(
name="Use",
default=True
@@ -60,8 +54,7 @@ class BFU_UL_ModularSkeletalSpecifiedPartsTargetItem(BBPL_UI_TemplateItem):
type=bpy.types.Collection,
)
-
-class BFU_UL_ModularSkeletalSpecifiedPartsTargetItemDraw(BBPL_UI_TemplateItemDraw):
+class BFU_UL_ModularSkeletalSpecifiedPartsTargetItemDraw(BBPL_UL_TemplateItemDraw): # Draw Item class (bpy.types.UIList)
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
prop_line = layout
@@ -86,18 +79,14 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn
elif item.target_type == "COLLECTION":
prop_data.prop(item, "collection", text="")
prop_data.enabled = item.enabled
-
-class BFU_ModularSkeletalSpecifiedPartsTargetList(BBPL_UI_TemplateList):
- template_collection: bpy.props.CollectionProperty(type=BFU_UL_ModularSkeletalSpecifiedPartsTargetItem)
- template_collection_uilist_class: bpy.props.StringProperty(default = "BFU_UL_ModularSkeletalSpecifiedPartsTargetItemDraw")
+class BFU_UI_ModularSkeletalSpecifiedPartsTargetList(BBPL_UI_TemplateList): # Draw Item class (bpy.types.UIList)
+ template_collection: bpy.props.CollectionProperty(type=BFU_UI_ModularSkeletalSpecifiedPartsTargetItem)
+ template_collection_uilist_class_name = "BFU_UL_ModularSkeletalSpecifiedPartsTargetItemDraw"
rows: bpy.props.IntProperty(default = 3)
maxrows: bpy.props.IntProperty(default = 3)
-
-
-
-class BFU_UL_ModularSkeletalSpecifiedPartsMeshItem(BBPL_UI_TemplateItem):
+class BFU_UI_ModularSkeletalSpecifiedPartsMeshItem(BBPL_UI_TemplateItem): # Item class (bpy.types.PropertyGroup)
enabled: bpy.props.BoolProperty(
name="Use",
default=True
@@ -110,10 +99,10 @@ class BFU_UL_ModularSkeletalSpecifiedPartsMeshItem(BBPL_UI_TemplateItem):
)
skeletal_parts: bpy.props.PointerProperty(
- type=BFU_ModularSkeletalSpecifiedPartsTargetList
+ type=BFU_UI_ModularSkeletalSpecifiedPartsTargetList
)
-class BFU_UL_ModularSkeletalSpecifiedPartsMeshItemDraw(BBPL_UI_TemplateItemDraw):
+class BFU_UL_ModularSkeletalSpecifiedPartsMeshItemDraw(BBPL_UL_TemplateItemDraw): # Draw Item class (bpy.types.UIList)
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
prop_line = layout
@@ -144,11 +133,10 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn
if obj_len+col_len == 0:
prop_data.label(text="", icon="ERROR")
prop_data.enabled = item.enabled
-
-class BFU_ModularSkeletalSpecifiedPartsMeshs(BBPL_UI_TemplateList):
- template_collection: bpy.props.CollectionProperty(type=BFU_UL_ModularSkeletalSpecifiedPartsMeshItem)
- template_collection_uilist_class: bpy.props.StringProperty(default = "BFU_UL_ModularSkeletalSpecifiedPartsMeshItemDraw")
+class BFU_UI_ModularSkeletalSpecifiedPartsMeshs(BBPL_UI_TemplateList): # Draw Item class (bpy.types.UIList)
+ template_collection: bpy.props.CollectionProperty(type=BFU_UI_ModularSkeletalSpecifiedPartsMeshItem)
+ template_collection_uilist_class_name = "BFU_UL_ModularSkeletalSpecifiedPartsMeshItemDraw"
def draw(self, layout: bpy.types.UILayout):
super().draw(layout)
@@ -167,16 +155,20 @@ def draw(self, layout: bpy.types.UILayout):
prop_data.enabled = item.enabled
item.skeletal_parts.draw(box).enabled = item.enabled
-
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
classes = (
- BFU_UL_ModularSkeletalSpecifiedPartsTargetItem,
+ BFU_UI_ModularSkeletalSpecifiedPartsTargetItem,
BFU_UL_ModularSkeletalSpecifiedPartsTargetItemDraw,
- BFU_ModularSkeletalSpecifiedPartsTargetList,
- BFU_UL_ModularSkeletalSpecifiedPartsMeshItem,
+ BFU_UI_ModularSkeletalSpecifiedPartsTargetList,
+
+ BFU_UI_ModularSkeletalSpecifiedPartsMeshItem,
BFU_UL_ModularSkeletalSpecifiedPartsMeshItemDraw,
- BFU_ModularSkeletalSpecifiedPartsMeshs,
+ BFU_UI_ModularSkeletalSpecifiedPartsMeshs,
)
@@ -184,41 +176,11 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
- bpy.types.Object.bfu_modular_skeletal_mesh_mode = bpy.props.EnumProperty(
- name="Modular Skeletal Mesh Mode",
- description='Modular skeletal mesh mode',
- override={'LIBRARY_OVERRIDABLE'},
- items=[
- ("all_in_one",
- "All In One",
- "Export all child meshs of the armature as one skeletal mesh.",
- 1),
- ("every_meshs",
- "Every Meshs",
- "Export one skeletal mesh for every child meshs of the armature.",
- 2),
- ("specified_parts",
- "Specified Parts",
- "Export specified mesh parts.",
- 3)
- ]
- )
-
- bpy.types.Object.bfu_modular_skeletal_mesh_every_meshs_separate = bpy.props.StringProperty(
- name="Separate string",
- description="String between armature name and mesh name",
- override={'LIBRARY_OVERRIDABLE'},
- default="_"
- )
-
-
- bpy.types.Object.bfu_modular_skeletal_specified_parts_meshs_template = bpy.props.PointerProperty(type=BFU_ModularSkeletalSpecifiedPartsMeshs)
+ bpy.types.Object.bfu_modular_skeletal_specified_parts_meshs_template = bpy.props.PointerProperty(type=BFU_UI_ModularSkeletalSpecifiedPartsMeshs)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
- del bpy.types.Object.bfu_modular_skeletal_specified_parts_meshs_template
- del bpy.types.Object.bfu_modular_skeletal_mesh_every_meshs_separate
- del bpy.types.Object.bfu_modular_skeletal_mesh_mode
\ No newline at end of file
+ del bpy.types.Object.bfu_modular_skeletal_specified_parts_meshs_template
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_ui.py b/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_ui.py
new file mode 100644
index 00000000..d791b5cb
--- /dev/null
+++ b/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_ui.py
@@ -0,0 +1,74 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_ui
+from .. import bfu_skeletal_mesh
+
+
+def draw_general_ui_object(layout: bpy.types.UILayout, obj: bpy.types.Object):
+ if obj is None:
+ return
+
+ if obj.type != "ARMATURE":
+ return
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
+ if scene.bfu_object_properties_expanded.is_expend():
+ if obj.bfu_export_type == "export_recursive":
+ if not obj.bfu_export_as_alembic_animation:
+ AssetType2 = layout.column()
+ # Show asset type
+ AssetType2.prop(obj, "bfu_export_skeletal_mesh_as_static_mesh")
+
+def draw_ui_object(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+ is_skeletal_mesh = bfu_skeletal_mesh.bfu_skeletal_mesh_utils.is_skeletal_mesh(obj)
+
+
+ if obj is None:
+ return
+ if obj.type != "ARMATURE":
+ return
+ if is_skeletal_mesh is False:
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
+ scene.bfu_modular_skeletal_mesh_properties_expanded.draw(layout)
+ if scene.bfu_modular_skeletal_mesh_properties_expanded.is_expend():
+
+ # SkeletalMesh prop
+ if not obj.bfu_export_as_lod_mesh:
+ modular_skeletal_mesh = layout.column()
+ modular_skeletal_mesh.prop(obj, "bfu_modular_skeletal_mesh_mode")
+ if obj.bfu_modular_skeletal_mesh_mode == "every_meshs":
+ modular_skeletal_mesh.prop(obj, "bfu_modular_skeletal_mesh_every_meshs_separate")
+ if obj.bfu_modular_skeletal_mesh_mode == "specified_parts":
+ obj.bfu_modular_skeletal_specified_parts_meshs_template.draw(modular_skeletal_mesh)
+
+def draw_ui_scene(layout: bpy.types.UILayout):
+ pass
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_utils.py b/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_utils.py
new file mode 100644
index 00000000..e57be8f1
--- /dev/null
+++ b/blender-for-unrealengine/bfu_modular_skeletal_mesh/bfu_modular_skeletal_mesh_utils.py
@@ -0,0 +1,19 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
diff --git a/blender-for-unrealengine/bfu_naming.py b/blender-for-unrealengine/bfu_naming.py
index 93c7ae6b..8f72a824 100644
--- a/blender-for-unrealengine/bfu_naming.py
+++ b/blender-for-unrealengine/bfu_naming.py
@@ -18,16 +18,6 @@
import bpy
-import bmesh
-import string
-import fnmatch
-import mathutils
-import math
-import os
-import math
-from . import bbpl
-from . import bps
-from . import bfu_write_text
from . import bfu_basics
from . import bfu_utils
diff --git a/blender-for-unrealengine/bfu_propertys/bfu_scene_propertys.py b/blender-for-unrealengine/bfu_propertys/bfu_scene_propertys.py
index 13d98b7f..f09f68f9 100644
--- a/blender-for-unrealengine/bfu_propertys/bfu_scene_propertys.py
+++ b/blender-for-unrealengine/bfu_propertys/bfu_scene_propertys.py
@@ -28,31 +28,6 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
- bpy.types.Scene.bfu_object_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Object Properties")
- bpy.types.Scene.bfu_object_lod_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Lod")
- bpy.types.Scene.bfu_object_collision_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Collision")
- bpy.types.Scene.bfu_object_light_map_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Light map")
- bpy.types.Scene.bfu_object_uv_map_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="UV map")
-
- bpy.types.Scene.bfu_animation_action_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Actions Properties")
- bpy.types.Scene.bfu_animation_action_advanced_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Actions Advanced Properties")
- bpy.types.Scene.bfu_animation_nla_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="NLA Properties")
- bpy.types.Scene.bfu_animation_nla_advanced_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="NLA Advanced Properties")
- bpy.types.Scene.bfu_animation_advanced_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Animation Advanced Properties")
-
- bpy.types.Scene.bfu_engine_ref_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Engine Refs")
-
- bpy.types.Scene.bfu_collection_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Collection Properties")
- bpy.types.Scene.bfu_object_advanced_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Object advanced Properties")
- bpy.types.Scene.bfu_collision_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Collision")
- bpy.types.Scene.bfu_socket_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Socket")
- bpy.types.Scene.bfu_uvmap_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="UV Map")
- bpy.types.Scene.bfu_lightmap_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Light Map")
- bpy.types.Scene.bfu_nomenclature_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Nomenclature")
- bpy.types.Scene.bfu_export_filter_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Export filters")
- bpy.types.Scene.bfu_export_process_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Export process")
- bpy.types.Scene.bfu_script_tool_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Copy Import Script")
-
bpy.types.Scene.bfu_active_tab = bpy.props.EnumProperty(
items=(
('OBJECT', 'Object', 'Object tab.'),
@@ -81,30 +56,7 @@ def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
- del bpy.types.Scene.bfu_object_properties_expanded
- del bpy.types.Scene.bfu_object_lod_properties_expanded
- del bpy.types.Scene.bfu_object_collision_properties_expanded
- del bpy.types.Scene.bfu_object_light_map_properties_expanded
- del bpy.types.Scene.bfu_object_uv_map_properties_expanded
-
- del bpy.types.Scene.bfu_animation_action_properties_expanded
- del bpy.types.Scene.bfu_animation_action_advanced_properties_expanded
- del bpy.types.Scene.bfu_animation_nla_properties_expanded
- del bpy.types.Scene.bfu_animation_nla_advanced_properties_expanded
- del bpy.types.Scene.bfu_animation_advanced_properties_expanded
-
- del bpy.types.Scene.bfu_engine_ref_properties_expanded
-
- del bpy.types.Scene.bfu_collection_properties_expanded
- del bpy.types.Scene.bfu_object_advanced_properties_expanded
- del bpy.types.Scene.bfu_collision_expanded
- del bpy.types.Scene.bfu_uvmap_expanded
- del bpy.types.Scene.bfu_socket_expanded
- del bpy.types.Scene.bfu_lightmap_expanded
- del bpy.types.Scene.bfu_nomenclature_properties_expanded
- del bpy.types.Scene.bfu_export_filter_properties_expanded
- del bpy.types.Scene.bfu_export_process_properties_expanded
- del bpy.types.Scene.bfu_script_tool_expanded
-
+ del bpy.types.Scene.bfu_active_scene_tab
del bpy.types.Scene.bfu_active_object_tab
+ del bpy.types.Scene.bfu_active_tab
diff --git a/blender-for-unrealengine/bfu_skeletal_mesh/__init__.py b/blender-for-unrealengine/bfu_skeletal_mesh/__init__.py
index aa244360..f993167a 100644
--- a/blender-for-unrealengine/bfu_skeletal_mesh/__init__.py
+++ b/blender-for-unrealengine/bfu_skeletal_mesh/__init__.py
@@ -23,6 +23,7 @@
from . import bfu_skeletal_mesh_ui
from . import bfu_skeletal_mesh_utils
from . import bfu_skeletal_mesh_type
+from . import bfu_skeletal_animation_type
from . import bfu_skeletal_mesh_config
if "bfu_skeletal_mesh_props" in locals():
@@ -33,6 +34,8 @@
importlib.reload(bfu_skeletal_mesh_utils)
if "bfu_skeletal_mesh_type" in locals():
importlib.reload(bfu_skeletal_mesh_type)
+if "bfu_skeletal_animation_type" in locals():
+ importlib.reload(bfu_skeletal_animation_type)
if "bfu_skeletal_mesh_config" in locals():
importlib.reload(bfu_skeletal_mesh_config)
@@ -47,11 +50,13 @@ def register():
bfu_skeletal_mesh_props.register()
bfu_skeletal_mesh_type.register()
+ bfu_skeletal_animation_type.register()
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
+ bfu_skeletal_animation_type.unregister()
bfu_skeletal_mesh_type.unregister()
bfu_skeletal_mesh_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_animation_type.py b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_animation_type.py
new file mode 100644
index 00000000..bd77af96
--- /dev/null
+++ b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_animation_type.py
@@ -0,0 +1,100 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import os
+import bpy
+import fnmatch
+from . import bfu_skeletal_mesh_config
+from .. import bfu_assets_manager
+from .. import bfu_utils
+from .. import bfu_basics
+
+
+class BFU_SkeletalAnimation(bfu_assets_manager.bfu_asset_manager_type.BFU_BaseAssetClass):
+ def __init__(self):
+ super().__init__()
+ self.use_materials = True
+
+ def support_asset_type(self, obj, details = None):
+ if obj.type == "ARMATURE" and not obj.bfu_export_skeletal_mesh_as_static_mesh:
+ if details == "SkeletalAnimation":
+ return True
+ return False
+
+ def get_asset_type_name(self, obj):
+ return bfu_skeletal_mesh_config.animation_asset_type_name
+
+ def get_obj_export_name(self, obj):
+ if bfu_utils.GetExportAsProxy(obj):
+ proxy_child = bfu_utils.GetExportProxyChild(obj)
+ if proxy_child is not None:
+ return bfu_basics.ValidFilename(proxy_child.name)
+ return super().get_obj_export_name(obj)
+
+ def get_obj_file_name(self, obj, desired_name="", fileType=".fbx"):
+ # Generate assset file name for skeletal mesh
+ scene = bpy.context.scene
+ if obj.bfu_use_custom_export_name:
+ if obj.bfu_custom_export_name:
+ return obj.bfu_custom_export_name
+ if desired_name:
+ return bfu_basics.ValidFilename(scene.bfu_skeletal_mesh_prefix_export_name+desired_name+fileType)
+ return bfu_basics.ValidFilename(scene.bfu_skeletal_mesh_prefix_export_name+obj.name+fileType)
+
+ def get_obj_export_directory_path(self, obj, absolute = True):
+ folder_name = bfu_utils.get_export_folder_name(obj)
+ scene = bpy.context.scene
+ if(absolute):
+ root_path = bpy.path.abspath(scene.bfu_export_skeletal_file_path)
+ else:
+ root_path = scene.bfu_export_skeletal_file_path
+
+ if obj.bfu_create_sub_folder_with_skeletal_mesh_name:
+ dirpath = os.path.join(root_path, folder_name, self.get_obj_export_name(obj), scene.bfu_anim_subfolder_name)
+ else:
+ dirpath = os.path.join(root_path, folder_name, scene.bfu_anim_subfolder_name)
+ return dirpath
+
+ def get_meshs_object_for_skeletal_mesh(self, obj):
+ meshs = []
+ if self.support_asset_type(obj): # Skeleton / Armature
+ childs = bfu_utils.GetExportDesiredChilds(obj)
+ for child in childs:
+ if child.type == "MESH":
+ meshs.append(child)
+ return meshs
+
+ def can_export_asset(self):
+ scene = bpy.context.scene
+ return scene.bfu_use_skeletal_export
+
+ def can_export_obj_asset(self, obj):
+ if self.can_export_asset():
+ if obj.bfu_skeleton_export_procedure == 'auto-rig-pro':
+ if bfu_basics.CheckPluginIsActivated('auto_rig_pro-master'):
+ return True
+ else:
+ return True
+ else:
+ False
+
+def register():
+ bfu_assets_manager.bfu_asset_manager_registred_assets.register_asset_class(BFU_SkeletalAnimation())
+
+def unregister():
+ pass
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_config.py b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_config.py
index f262672b..4e7f9e20 100644
--- a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_config.py
+++ b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_config.py
@@ -16,4 +16,5 @@
#
# ======================= END GPL LICENSE BLOCK =============================
-asset_type_name = "SkeletalMesh"
+mesh_asset_type_name = "SkeletalMesh"
+animation_asset_type_name = "SkeletalAnimation"
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_props.py b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_props.py
index 97e69aef..12032a1a 100644
--- a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_props.py
+++ b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_props.py
@@ -27,9 +27,13 @@
def get_preset_values():
preset_values = [
+ 'obj.bfu_export_deform_only',
'obj.bfu_export_skeletal_mesh_as_static_mesh',
- 'obj.bfu_create_sub_folder_with_skeletal_mesh_name'
- ]
+ 'obj.bfu_create_sub_folder_with_skeletal_mesh_name',
+ 'obj.bfu_export_animation_without_mesh',
+ 'obj.bfu_mirror_symmetry_right_side_bones',
+ 'obj.bfu_use_ue_mannequin_bone_alignment',
+ ]
return preset_values
# -------------------------------------------------------------------
@@ -44,6 +48,18 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
+ bpy.types.Scene.bfu_skeleton_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Skeleton")
+
+ bpy.types.Object.bfu_export_deform_only = bpy.props.BoolProperty(
+ name="Export only deform bones",
+ description=(
+ "Only write deforming bones" +
+ " (and non-deforming ones when they have deforming children)"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True
+ )
+
bpy.types.Object.bfu_export_skeletal_mesh_as_static_mesh = bpy.props.BoolProperty(
name="Export as Static Mesh",
description="If true this mesh will be exported as a Static Mesh",
@@ -58,17 +74,45 @@ def register():
default=True
)
- bpy.types.Scene.bfu_skeleton_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Skeleton")
- bpy.types.Scene.bfu_modular_skeletal_mesh_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Modular Skeletal Mesh")
+ bpy.types.Object.bfu_export_animation_without_mesh = bpy.props.BoolProperty(
+ name="Export animation without mesh",
+ description=(
+ "If checked, When exporting animation, do not include mesh data in the FBX file."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True
+ )
+ bpy.types.Object.bfu_mirror_symmetry_right_side_bones = bpy.props.BoolProperty(
+ name="Revert direction of symmetry right side bones",
+ description=(
+ "If checked, The right-side bones will be mirrored for mirroring physic object in UE PhysicAsset Editor."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=True
+ )
+
+ bpy.types.Object.bfu_use_ue_mannequin_bone_alignment = bpy.props.BoolProperty(
+ name="Apply bone alignments similar to UE Mannequin.",
+ description=(
+ "If checked, similar to the UE Mannequin, the leg bones will be oriented upwards, and the pelvis and feet bone will be aligned facing upwards during export."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False
+ )
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
- del bpy.types.Scene.bfu_modular_skeletal_mesh_properties_expanded
- del bpy.types.Scene.bfu_skeleton_properties_expanded
+ del bpy.types.Object.bfu_use_ue_mannequin_bone_alignment
+ del bpy.types.Object.bfu_mirror_symmetry_right_side_bones
+ del bpy.types.Object.bfu_export_animation_without_mesh
del bpy.types.Object.bfu_create_sub_folder_with_skeletal_mesh_name
- del bpy.types.Object.bfu_export_skeletal_mesh_as_static_mesh
\ No newline at end of file
+ del bpy.types.Object.bfu_export_skeletal_mesh_as_static_mesh
+
+ del bpy.types.Object.bfu_export_deform_only
+
+ del bpy.types.Scene.bfu_skeleton_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_type.py b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_type.py
index b692c731..9c5e6a45 100644
--- a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_type.py
+++ b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_type.py
@@ -30,13 +30,14 @@ def __init__(self):
super().__init__()
self.use_materials = True
- def support_asset_type(self, obj):
+ def support_asset_type(self, obj, details = None):
if obj.type == "ARMATURE" and not obj.bfu_export_skeletal_mesh_as_static_mesh:
- return True
+ if details == None:
+ return True
return False
def get_asset_type_name(self, obj):
- return bfu_skeletal_mesh_config.asset_type_name
+ return bfu_skeletal_mesh_config.mesh_asset_type_name
def get_obj_export_name(self, obj):
if bfu_utils.GetExportAsProxy(obj):
@@ -55,13 +56,18 @@ def get_obj_file_name(self, obj, desired_name="", fileType=".fbx"):
return bfu_basics.ValidFilename(scene.bfu_skeletal_mesh_prefix_export_name+desired_name+fileType)
return bfu_basics.ValidFilename(scene.bfu_skeletal_mesh_prefix_export_name+obj.name+fileType)
- def get_obj_export_directory_path(self, obj):
+ def get_obj_export_directory_path(self, obj, absolute = True):
folder_name = bfu_utils.get_export_folder_name(obj)
scene = bpy.context.scene
+ if(absolute):
+ root_path = bpy.path.abspath(scene.bfu_export_skeletal_file_path)
+ else:
+ root_path = scene.bfu_export_skeletal_file_path
+
if obj.bfu_create_sub_folder_with_skeletal_mesh_name:
- dirpath = os.path.join(scene.bfu_export_skeletal_file_path, folder_name, self.get_obj_export_name(obj))
+ dirpath = os.path.join(root_path, folder_name, self.get_obj_export_name(obj))
else:
- dirpath = os.path.join(scene.bfu_export_skeletal_file_path, folder_name)
+ dirpath = os.path.join(root_path, folder_name)
return dirpath
def get_meshs_object_for_skeletal_mesh(self, obj):
@@ -75,7 +81,7 @@ def get_meshs_object_for_skeletal_mesh(self, obj):
def can_export_asset(self):
scene = bpy.context.scene
- return scene.skeletal_export
+ return scene.bfu_use_skeletal_export
def can_export_obj_asset(self, obj):
if self.can_export_asset():
diff --git a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_ui.py b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_ui.py
index 6baa08b9..8102f39a 100644
--- a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_ui.py
+++ b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_ui.py
@@ -44,55 +44,43 @@ def draw_general_ui_object(layout: bpy.types.UILayout, obj: bpy.types.Object):
AssetType2.prop(obj, "bfu_export_skeletal_mesh_as_static_mesh")
def draw_ui_object(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+ addon_prefs = bfu_basics.GetAddonPrefs()
+ is_skeletal_mesh = bfu_skeletal_mesh_utils.is_skeletal_mesh(obj)
+
if obj is None:
return
-
if obj.type != "ARMATURE":
return
+ if is_skeletal_mesh is False:
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+ if obj.bfu_export_as_lod_mesh:
+ return
- scene = bpy.context.scene
- addon_prefs = bfu_basics.GetAddonPrefs()
-
if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
- if scene.bfu_object_properties_expanded.is_expend():
-
- if bfu_skeletal_mesh_utils.is_skeletal_mesh(obj):
- scene.bfu_skeleton_properties_expanded.draw(layout)
- if scene.bfu_skeleton_properties_expanded.is_expend():
- if addon_prefs.useGeneratedScripts and obj is not None:
- if obj.bfu_export_type == "export_recursive":
-
- # SkeletalMesh prop
- if not obj.bfu_export_as_lod_mesh:
- AssetType2 = layout.column()
-
- export_procedure_prop = AssetType2.column()
- export_procedure_prop.prop(obj, 'bfu_skeleton_export_procedure')
+ scene.bfu_skeleton_properties_expanded.draw(layout)
+ if scene.bfu_skeleton_properties_expanded.is_expend():
- AssetType2.prop(obj, 'bfu_create_sub_folder_with_skeletal_mesh_name')
- AssetType2.prop(obj, 'bfu_export_deform_only')
- ue_standard_skeleton = layout.column()
- ue_standard_skeleton.label(text="(ue-standard)")
- ue_standard_skeleton_props = ue_standard_skeleton.column()
- ue_standard_skeleton_props.enabled = obj.bfu_skeleton_export_procedure == "ue-standard"
- ue_standard_skeleton_props.prop(obj, "bfu_export_animation_without_mesh")
- ue_standard_skeleton_props.prop(obj, "bfu_mirror_symmetry_right_side_bones")
- mirror_symmetry_right_side_bones = ue_standard_skeleton_props.row()
- mirror_symmetry_right_side_bones.enabled = obj.bfu_mirror_symmetry_right_side_bones
- mirror_symmetry_right_side_bones.prop(obj, "bfu_use_ue_mannequin_bone_alignment")
+ # SkeletalMesh prop
+ AssetType2 = layout.column()
- scene.bfu_modular_skeletal_mesh_properties_expanded.draw(layout)
- if scene.bfu_modular_skeletal_mesh_properties_expanded.is_expend():
- if obj.bfu_export_type == "export_recursive":
+ export_procedure_prop = AssetType2.column()
+ export_procedure_prop.prop(obj, 'bfu_skeleton_export_procedure')
- # SkeletalMesh prop
- if not obj.bfu_export_as_lod_mesh:
- modular_skeletal_mesh = layout.column()
- modular_skeletal_mesh.prop(obj, "bfu_modular_skeletal_mesh_mode")
- if obj.bfu_modular_skeletal_mesh_mode == "every_meshs":
- modular_skeletal_mesh.prop(obj, "bfu_modular_skeletal_mesh_every_meshs_separate")
- if obj.bfu_modular_skeletal_mesh_mode == "specified_parts":
- obj.bfu_modular_skeletal_specified_parts_meshs_template.draw(modular_skeletal_mesh)
+ AssetType2.prop(obj, 'bfu_create_sub_folder_with_skeletal_mesh_name')
+ AssetType2.prop(obj, 'bfu_export_deform_only')
+ ue_standard_skeleton = layout.column()
+ ue_standard_skeleton.label(text="(ue-standard)")
+ ue_standard_skeleton_props = ue_standard_skeleton.column()
+ ue_standard_skeleton_props.enabled = obj.bfu_skeleton_export_procedure == "ue-standard"
+ ue_standard_skeleton_props.prop(obj, "bfu_export_animation_without_mesh")
+ ue_standard_skeleton_props.prop(obj, "bfu_mirror_symmetry_right_side_bones")
+ mirror_symmetry_right_side_bones = ue_standard_skeleton_props.row()
+ mirror_symmetry_right_side_bones.enabled = obj.bfu_mirror_symmetry_right_side_bones
+ mirror_symmetry_right_side_bones.prop(obj, "bfu_use_ue_mannequin_bone_alignment")
def draw_ui_scene(layout: bpy.types.UILayout):
pass
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_utils.py b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_utils.py
index fb66e2ff..fad88326 100644
--- a/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_utils.py
+++ b/blender-for-unrealengine/bfu_skeletal_mesh/bfu_skeletal_mesh_utils.py
@@ -43,7 +43,7 @@ def deselect_socket(obj):
def is_skeletal_mesh(obj):
asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(obj)
if asset_class:
- if asset_class.get_asset_type_name(obj) == bfu_skeletal_mesh_config.asset_type_name:
+ if asset_class.get_asset_type_name(obj) == bfu_skeletal_mesh_config.mesh_asset_type_name:
return True
return False
diff --git a/blender-for-unrealengine/bfu_socket/bfu_socket_ui_and_props.py b/blender-for-unrealengine/bfu_socket/bfu_socket_ui_and_props.py
index b69e8654..2ba1d94c 100644
--- a/blender-for-unrealengine/bfu_socket/bfu_socket_ui_and_props.py
+++ b/blender-for-unrealengine/bfu_socket/bfu_socket_ui_and_props.py
@@ -69,10 +69,11 @@ def execute(self, context):
"Skeletal sockets copied. Paste in Unreal Engine Skeletal Mesh assets for import sockets. (Ctrl+V)")
return {'FINISHED'}
-def draw_ui_scene_socket(layout: bpy.types.UILayout):
- scene = bpy.context.scene
- scene.bfu_socket_expanded.draw(layout)
- if scene.bfu_socket_expanded.is_expend():
+def draw_tools_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
+
+ scene.bfu_tools_socket_properties_expanded.draw(layout)
+ if scene.bfu_tools_socket_properties_expanded.is_expend():
addon_prefs = bfu_basics.GetAddonPrefs()
# Draw user tips and check can use buttons
@@ -162,6 +163,8 @@ def register():
for cls in classes:
bpy.utils.register_class(cls)
+ bpy.types.Scene.bfu_tools_socket_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="Socket")
+
bpy.types.Object.bfu_use_socket_custom_Name = bpy.props.BoolProperty(
name="Socket custom name",
description='Use a custom name in Unreal Engine for this socket?',
@@ -181,3 +184,5 @@ def unregister():
del bpy.types.Object.bfu_socket_custom_Name
del bpy.types.Object.bfu_use_socket_custom_Name
+
+ del bpy.types.Scene.bfu_tools_socket_properties_expanded
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_spline/bfu_spline_data.py b/blender-for-unrealengine/bfu_spline/bfu_spline_data.py
index 45066a68..2fe93ce6 100644
--- a/blender-for-unrealengine/bfu_spline/bfu_spline_data.py
+++ b/blender-for-unrealengine/bfu_spline/bfu_spline_data.py
@@ -4,7 +4,7 @@
from typing import Dict, Any
from . import bfu_spline_utils
from . import bfu_spline_unreal_utils
-from .. import bps
+from .. import bpl
from .. import bbpl
from .. import languages
from .. import bfu_basics
@@ -204,7 +204,7 @@ def evaluate_spline_data(self, spline_obj: bpy.types.Object, spline_data: bpy.ty
addon_prefs = bfu_basics.GetAddonPrefs()
#print(f"Start evaluate spline_data index {str(index)}")
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
if spline_data.type in ["POLY"]:
for point in spline_data.points:
@@ -274,7 +274,7 @@ def evaluate_spline_obj_data(self, spline_obj: bpy.types.Object):
addon_prefs = bfu_basics.GetAddonPrefs()
#print(f"Start evaluate spline {spline_obj.name}")
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
for x, spline_data in enumerate(spline_obj.data.splines):
simple_spline = self.simple_splines[x] = BFU_SimpleSpline(spline_data)
@@ -301,7 +301,7 @@ def evaluate_all_splines(self):
scene = bpy.context.scene
addon_prefs = bfu_basics.GetAddonPrefs()
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
slms = bfu_utils.TimelineMarkerSequence()
diff --git a/blender-for-unrealengine/bfu_spline/bfu_spline_type.py b/blender-for-unrealengine/bfu_spline/bfu_spline_type.py
index ceb5fa87..0df76a78 100644
--- a/blender-for-unrealengine/bfu_spline/bfu_spline_type.py
+++ b/blender-for-unrealengine/bfu_spline/bfu_spline_type.py
@@ -30,7 +30,7 @@ def __init__(self):
super().__init__()
pass
- def support_asset_type(self, obj):
+ def support_asset_type(self, obj, details = None):
if obj.type == "CURVE" and not obj.bfu_export_spline_as_static_mesh:
return True
return False
@@ -48,15 +48,20 @@ def get_obj_file_name(self, obj, desired_name="", fileType=".fbx"):
return bfu_basics.ValidFilename(scene.bfu_spline_prefix_export_name+desired_name+fileType)
return bfu_basics.ValidFilename(scene.bfu_spline_prefix_export_name+obj.name+fileType)
- def get_obj_export_directory_path(self, obj):
+ def get_obj_export_directory_path(self, obj, absolute = True):
folder_name = bfu_utils.get_export_folder_name(obj)
scene = bpy.context.scene
- dirpath = os.path.join(scene.bfu_export_spline_file_path, folder_name)
+ if(absolute):
+ root_path = bpy.path.abspath(scene.bfu_export_spline_file_path)
+ else:
+ root_path = scene.bfu_export_spline_file_path
+
+ dirpath = os.path.join(root_path, folder_name)
return dirpath
def can_export_asset(self):
scene = bpy.context.scene
- return scene.spline_export
+ return scene.bfu_use_spline_export
def can_export_obj_asset(self, obj):
return self.can_export_asset()
diff --git a/blender-for-unrealengine/bfu_spline/bfu_spline_ui_and_props.py b/blender-for-unrealengine/bfu_spline/bfu_spline_ui_and_props.py
index 79bf68c0..f3bb45b2 100644
--- a/blender-for-unrealengine/bfu_spline/bfu_spline_ui_and_props.py
+++ b/blender-for-unrealengine/bfu_spline/bfu_spline_ui_and_props.py
@@ -22,6 +22,7 @@
from . import bfu_spline_utils
from . import bfu_spline_write_paste_commands
from .. import bfu_basics
+from .. import bfu_ui
from .. import bbpl
from .. import languages
from ..bbpl.blender_layout import layout_doc_button
@@ -46,31 +47,32 @@ def draw_ui_object_spline(layout: bpy.types.UILayout, obj: bpy.types.Object):
spline_ui = layout.column()
scene = bpy.context.scene
- scene.bfu_spline_properties_expanded.draw(spline_ui)
- if scene.bfu_spline_properties_expanded.is_expend():
- if obj.type == "CURVE":
- spline_ui_pop = spline_ui.column()
- spline_ui_as_static_mesh = spline_ui_pop.column()
- spline_ui_as_static_mesh.prop(obj, 'bfu_export_spline_as_static_mesh')
- spline_ui_as_static_mesh.prop(obj, 'bfu_export_fbx_spline')
- spline_ui_as_static_mesh.enabled = obj.bfu_export_type == "export_recursive"
-
- spline_ui_spline_type = spline_ui_pop.column()
- spline_ui_spline_type.prop(obj, 'bfu_desired_spline_type')
- if obj.bfu_desired_spline_type == "CUSTOM":
- spline_ui_spline_type.prop(obj, 'bfu_custom_spline_component')
- if bfu_spline_utils.contain_nurbs_spline(obj):
- resample_resolution = spline_ui_spline_type.row()
- resample_resolution.prop(obj, 'bfu_spline_resample_resolution')
- layout_doc_button.add_doc_page_operator(resample_resolution, text="", url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Curve-and-Spline#notes")
- spline_ui_spline_type.enabled = obj.bfu_export_spline_as_static_mesh is False
- spline_ui.operator("object.bfu_copy_active_spline_data", icon="COPYDOWN")
-
-
-def draw_ui_scene_spline(layout: bpy.types.UILayout):
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "GENERAL"):
+ scene.bfu_spline_properties_expanded.draw(spline_ui)
+ if scene.bfu_spline_properties_expanded.is_expend():
+ if obj.type == "CURVE":
+ spline_ui_pop = spline_ui.column()
+ spline_ui_as_static_mesh = spline_ui_pop.column()
+ spline_ui_as_static_mesh.prop(obj, 'bfu_export_spline_as_static_mesh')
+ spline_ui_as_static_mesh.prop(obj, 'bfu_export_fbx_spline')
+ spline_ui_as_static_mesh.enabled = obj.bfu_export_type == "export_recursive"
+
+ spline_ui_spline_type = spline_ui_pop.column()
+ spline_ui_spline_type.prop(obj, 'bfu_desired_spline_type')
+ if obj.bfu_desired_spline_type == "CUSTOM":
+ spline_ui_spline_type.prop(obj, 'bfu_custom_spline_component')
+ if bfu_spline_utils.contain_nurbs_spline(obj):
+ resample_resolution = spline_ui_spline_type.row()
+ resample_resolution.prop(obj, 'bfu_spline_resample_resolution')
+ layout_doc_button.add_doc_page_operator(resample_resolution, text="", url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/Curve-and-Spline#notes")
+ spline_ui_spline_type.enabled = obj.bfu_export_spline_as_static_mesh is False
+ spline_ui.operator("object.bfu_copy_active_spline_data", icon="COPYDOWN")
+
+
+def draw_tools_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
spline_ui = layout.column()
- scene = bpy.context.scene
scene.bfu_spline_tools_expanded.draw(spline_ui)
if scene.bfu_spline_tools_expanded.is_expend():
spline_ui.operator("object.copy_selected_splines_data", icon="COPYDOWN")
diff --git a/blender-for-unrealengine/bfu_spline/bfu_spline_write_paste_commands.py b/blender-for-unrealengine/bfu_spline/bfu_spline_write_paste_commands.py
index 8cc71bef..82048f08 100644
--- a/blender-for-unrealengine/bfu_spline/bfu_spline_write_paste_commands.py
+++ b/blender-for-unrealengine/bfu_spline/bfu_spline_write_paste_commands.py
@@ -46,7 +46,7 @@ def AddSplineToCommand(spline: bpy.types.Object, pre_bake_spline: bfu_spline_dat
def GetImportSplineScriptCommand(objs):
# Return (success, command)
scene = bpy.context.scene
- save_select = bbpl.utils.UserSelectSave()
+ save_select = bbpl.save_data.select_save.UserSelectSave()
save_select.save_current_select()
success = False
@@ -79,5 +79,5 @@ def GetImportSplineScriptCommand(objs):
success = True
command = t
report = str(add_spline_num) + " Spline(s) copied. Paste in Unreal Engine scene for import the spline. (Use CTRL+V in Unreal viewport)"
- save_select.reset_select_by_name()
+ save_select.reset_select(use_names = True)
return (success, command, report)
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_static_mesh/bfu_static_mesh_type.py b/blender-for-unrealengine/bfu_static_mesh/bfu_static_mesh_type.py
index 6424a8a6..b58b06e4 100644
--- a/blender-for-unrealengine/bfu_static_mesh/bfu_static_mesh_type.py
+++ b/blender-for-unrealengine/bfu_static_mesh/bfu_static_mesh_type.py
@@ -32,7 +32,7 @@ def __init__(self):
self.use_lods = True
self.use_materials = True
- def support_asset_type(self, obj):
+ def support_asset_type(self, obj, details = None):
if obj.bfu_export_skeletal_mesh_as_static_mesh:
return True
elif obj.bfu_export_as_groom_simulation:
@@ -58,15 +58,20 @@ def get_obj_file_name(self, obj, desired_name="", fileType=".fbx"):
return bfu_basics.ValidFilename(scene.bfu_static_mesh_prefix_export_name+desired_name+fileType)
return bfu_basics.ValidFilename(scene.bfu_static_mesh_prefix_export_name+obj.name+fileType)
- def get_obj_export_directory_path(self, obj):
+ def get_obj_export_directory_path(self, obj, absolute = True):
+
folder_name = bfu_utils.get_export_folder_name(obj)
scene = bpy.context.scene
- dirpath = os.path.join(scene.bfu_export_static_file_path, folder_name)
+ if(absolute):
+ root_path = bpy.path.abspath(scene.bfu_export_static_file_path)
+ else:
+ root_path = scene.bfu_export_static_file_path
+ dirpath = os.path.join(root_path, folder_name)
return dirpath
def can_export_asset(self):
scene = bpy.context.scene
- return scene.static_export
+ return scene.bfu_use_static_export
def can_export_obj_asset(self, obj):
return self.can_export_asset()
diff --git a/blender-for-unrealengine/bfu_unreal_utils.py b/blender-for-unrealengine/bfu_unreal_utils.py
index 5f23eca6..5351d8b4 100644
--- a/blender-for-unrealengine/bfu_unreal_utils.py
+++ b/blender-for-unrealengine/bfu_unreal_utils.py
@@ -18,46 +18,43 @@
import os
import bpy
-from . import bbpl
-from . import bps
-from . import bfu_basics
from . import bfu_utils
-def GetPredictedSkeletonName(obj):
+def get_predicted_skeleton_name(obj):
# Get the predicted skeleton name in Unreal Engine
scene = bpy.context.scene
return scene.bfu_skeleton_prefix_export_name + bfu_utils.ValidUnrealAssetsName(obj.name) + "_Skeleton"
-def GetPredictedSkeletonPath(obj):
+def get_predicted_skeleton_path(obj):
scene = bpy.context.scene
skeleton_path = os.path.join("/" + scene.bfu_unreal_import_module + "/", scene.bfu_unreal_import_location, obj.bfu_export_folder_name)
skeleton_path = skeleton_path.replace('\\', '/')
return skeleton_path
-def GetPredictedSkeletonRef(obj):
- name = GetPredictedSkeletonName(obj)
- path = GetPredictedSkeletonPath(obj)
- skeleton_ref = os.path.join(path, name + "." + name)
+def get_predicted_skeleton_ref(obj):
+ name = get_predicted_skeleton_name(obj)
+ path = get_predicted_skeleton_path(obj)
+ skeleton_ref = os.path.join(path, f"{name}.{name}")
skeleton_ref = skeleton_ref.replace('\\', '/')
- return "/Script/Engine.Skeleton'" + skeleton_ref + "'"
+ return f"/Script/Engine.Skeleton'{skeleton_ref}'"
-def GetPredictedSkeletalMeshName(obj):
+def get_predicted_skeleton_name(obj):
# Get the predicted SkeletalMesh name in Unreal Engine
scene = bpy.context.scene
return scene.bfu_skeletal_mesh_prefix_export_name + bfu_utils.ValidUnrealAssetsName(obj.name)
-def GetPredictedSkeletalMeshPath(obj):
+def get_predicted_skeleton_path(obj):
scene = bpy.context.scene
skeleton_path = os.path.join("/" + scene.bfu_unreal_import_module + "/", scene.bfu_unreal_import_location, obj.bfu_export_folder_name)
skeleton_path = skeleton_path.replace('\\', '/')
return skeleton_path
-def GetPredictedSkeletalMeshRef(obj):
- name = GetPredictedSkeletalMeshName(obj)
- path = GetPredictedSkeletalMeshPath(obj)
- skeletal_mesh_ref = os.path.join(path, name + "." + name)
- skeletal_mesh_ref = skeletal_mesh_ref.replace('\\', '/')
- return "/Script/Engine.SkeletalMesh'" + skeletal_mesh_ref + "'"
+def get_predicted_skeleton_ref(obj):
+ name = get_predicted_skeleton_name(obj)
+ path = get_predicted_skeleton_path(obj)
+ skeleton_ref = os.path.join(path, f"{name}.{name}")
+ skeleton_ref = skeleton_ref.replace('\\', '/')
+ return f"/Script/Engine.Skeleton'{skeleton_ref}'"
def generate_name_for_unreal_engine(desired_name, current_name = ""):
# Generate a new name with suffix number
diff --git a/blender-for-unrealengine/bfu_utils.py b/blender-for-unrealengine/bfu_utils.py
index 1ab55c1c..06996bdb 100644
--- a/blender-for-unrealengine/bfu_utils.py
+++ b/blender-for-unrealengine/bfu_utils.py
@@ -24,13 +24,10 @@
import mathutils
import math
import os
-import math
-import addon_utils
from typing import List
from . import bbpl
-from . import bps
from . import bfu_basics
-from . import bfu_assets_manager
+
class SavedBones():
@@ -660,7 +657,7 @@ def SelectParentAndSpecificChilds(active, objects):
def RemoveSocketFromSelectForProxyArmature():
- select = bbpl.utils.UserSelectSave()
+ select = bbpl.save_data.select_save.UserSelectSave()
select.save_current_select()
# With skeletal mesh the socket must be not exported,
# ue4 read it like a bone
@@ -669,7 +666,7 @@ def RemoveSocketFromSelectForProxyArmature():
if fnmatch.fnmatchcase(obj.name, "SOCKET*"):
sockets.append(obj)
CleanDeleteObjects(sockets)
- select.reset_select_by_name()
+ select.reset_select(use_names = True)
def GoToMeshEditMode():
@@ -683,14 +680,14 @@ def GoToMeshEditMode():
def ApplyNeededModifierToSelect():
- SavedSelect = bbpl.utils.UserSelectSave()
+ SavedSelect = bbpl.save_data.select_save.UserSelectSave()
SavedSelect.save_current_select()
# Get selected objects with modifiers.
for obj in bpy.context.selected_objects:
ApplyObjectModifiers(obj, ['ARMATURE'])
- SavedSelect.reset_select_by_ref()
+ SavedSelect.reset_select()
def ApplyObjectModifiers(obj: bpy.types.Object, blacklist_type = []):
@@ -725,8 +722,8 @@ def ApplyObjectModifiers(obj: bpy.types.Object, blacklist_type = []):
-def CorrectExtremeUV(stepScale=2):
-
+def CorrectExtremeUV(step_scale=2, move_to_absolute=False):
+
def GetHaveConnectedLoop(faceTarget):
# In bmesh faces
for loop in faceTarget.loops:
@@ -779,16 +776,24 @@ def GetAllIsland(bm, uv_lay):
return Islands
- def MoveItlandToCenter(faces, uv_lay, minDistance):
+ def MoveItlandToCenter(faces, uv_lay, min_distance, absolute):
loop = faces[-1].loops[-1]
- x = round(loop[uv_lay].uv[0]/minDistance, 0)*minDistance
- y = round(loop[uv_lay].uv[1]/minDistance, 0)*minDistance
+ delta_x = round(loop[uv_lay].uv[0]/min_distance, 0)*min_distance
+ delta_y = round(loop[uv_lay].uv[1]/min_distance, 0)*min_distance
for face in faces:
for loop in face.loops:
- loop[uv_lay].uv[0] -= x
- loop[uv_lay].uv[1] -= y
+ loop[uv_lay].uv[0] -= delta_x
+ loop[uv_lay].uv[1] -= delta_y
+
+ if(absolute == True):
+ # Move Faces to make it alway positive
+ for face in faces:
+ for loop in face.loops:
+ loop[uv_lay].uv[0] = abs(loop[uv_lay].uv[0])
+ loop[uv_lay].uv[1] = abs(loop[uv_lay].uv[1])
+
def IsValidForUvEdit(obj):
if obj.type == "MESH":
@@ -806,11 +811,10 @@ def IsValidForUvEdit(obj):
for faces in GetAllIsland(bm, uv_lay):
uv_lay = bm.loops.layers.uv.active
- MoveItlandToCenter(faces, uv_lay, stepScale)
+ MoveItlandToCenter(faces, uv_lay, step_scale, move_to_absolute)
obj.data.update()
-
def ApplyExportTransform(obj, use_type="Object"):
newMatrix = obj.matrix_world @ mathutils.Matrix.Translation((0, 0, 0))
@@ -912,7 +916,7 @@ def ApplySkeletalExportScale(self, rescale, target_animation_data=None, is_a_pro
armature_animation_data.clear_animation_data(armature)
if is_a_proxy:
- SavedSelect = bbpl.utils.UserSelectSave()
+ SavedSelect = bbpl.save_data.select_save.UserSelectSave()
SavedSelect.save_current_select()
bpy.ops.object.select_all(action='DESELECT')
armature.select_set(True)
@@ -931,7 +935,7 @@ def ApplySkeletalExportScale(self, rescale, target_animation_data=None, is_a_pro
properties=True
)
if is_a_proxy:
- SavedSelect.reset_select_by_ref()
+ SavedSelect.reset_select()
# Apply armature location
armature.location = old_location*rescale
@@ -1096,27 +1100,27 @@ def GetAnimSample(obj):
return obj.bfu_sample_anim_for_export
-def GetArmatureRootBones(obj):
- rootBones = []
- if obj.type == "ARMATURE":
-
- if not obj.bfu_export_deform_only:
- for bone in obj.data.bones:
- if bone.parent is None:
- rootBones.append(bone)
+def get_armature_root_bones(armature: bpy.types.Object) -> List[bpy.types.EditBone]:
+ root_bones = []
+ if armature.type == "ARMATURE":
- if obj.bfu_export_deform_only:
- for bone in obj.data.bones:
+ if armature.bfu_export_deform_only:
+ for bone in armature.data.bones:
if bone.use_deform:
rootBone = bfu_basics.getRootBoneParent(bone)
- if rootBone not in rootBones:
- rootBones.append(rootBone)
- return rootBones
+ if rootBone not in root_bones:
+ root_bones.append(rootBone)
+
+ else:
+ for bone in armature.data.bones:
+ if bone.parent is None:
+ root_bones.append(bone)
+ return root_bones
def GetDesiredExportArmatureName(obj):
addon_prefs = bfu_basics.GetAddonPrefs()
- single_root = len(GetArmatureRootBones(obj)) == 1
+ single_root = len(get_armature_root_bones(obj)) == 1
if addon_prefs.add_skeleton_root_bone or single_root != 1:
return addon_prefs.skeleton_root_bone_name
return "Armature"
diff --git a/blender-for-unrealengine/bfu_uv_map/__init__.py b/blender-for-unrealengine/bfu_uv_map/__init__.py
new file mode 100644
index 00000000..18cbb4bb
--- /dev/null
+++ b/blender-for-unrealengine/bfu_uv_map/__init__.py
@@ -0,0 +1,47 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+import importlib
+
+from . import bfu_uv_map_props
+from . import bfu_uv_map_ui
+from . import bfu_uv_map_utils
+
+if "bfu_uv_map_props" in locals():
+ importlib.reload(bfu_uv_map_props)
+if "bfu_uv_map_ui" in locals():
+ importlib.reload(bfu_uv_map_ui)
+if "bfu_uv_map_utils" in locals():
+ importlib.reload(bfu_uv_map_utils)
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bfu_uv_map_props.register()
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ bfu_uv_map_props.unregister()
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_props.py b/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_props.py
new file mode 100644
index 00000000..96ababd6
--- /dev/null
+++ b/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_props.py
@@ -0,0 +1,107 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+from .. import languages
+
+
+
+
+def get_preset_values():
+ preset_values = [
+ 'obj.bfu_convert_geometry_node_attribute_to_uv',
+ 'obj.bfu_convert_geometry_node_attribute_to_uv_name',
+ 'obj.bfu_use_correct_extrem_uv_scale',
+ 'obj.bfu_correct_extrem_uv_scale_step_scale',
+ 'obj.bfu_correct_extrem_uv_scale_use_absolute',
+ ]
+ return preset_values
+
+# -------------------------------------------------------------------
+# Register & Unregister
+# -------------------------------------------------------------------
+
+classes = (
+)
+
+
+def register():
+ for cls in classes:
+ bpy.utils.register_class(cls)
+
+ bpy.types.Scene.bfu_object_uv_map_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="UV map")
+ bpy.types.Scene.bfu_tools_uv_map_properties_expanded = bbpl.blender_layout.layout_accordion.add_ui_accordion(name="UV Map")
+
+ bpy.types.Object.bfu_convert_geometry_node_attribute_to_uv = bpy.props.BoolProperty(
+ name="Convert Attribute To Uv",
+ description=(
+ "convert target geometry node attribute to UV when found."
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False,
+ )
+
+ bpy.types.Object.bfu_convert_geometry_node_attribute_to_uv_name = bpy.props.StringProperty(
+ name="Attribute name",
+ description=(
+ "Name of the Attribute to convert"
+ ),
+ override={'LIBRARY_OVERRIDABLE'},
+ default="UVMap",
+ )
+
+ bpy.types.Object.bfu_use_correct_extrem_uv_scale = bpy.props.BoolProperty(
+ name=(languages.ti('correct_use_extrem_uv_scale_name')),
+ description=(languages.tt('correct_use_extrem_uv_scale_desc')),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False,
+ )
+
+ bpy.types.Object.bfu_correct_extrem_uv_scale_step_scale = bpy.props.IntProperty(
+ name=(languages.ti('correct_extrem_uv_scale_step_scale_name')),
+ description=(languages.tt('correct_extrem_uv_scale_step_scale_desc')),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=2,
+ min=1,
+ max=100,
+ )
+
+ bpy.types.Object.bfu_correct_extrem_uv_scale_use_absolute = bpy.props.BoolProperty(
+ name=(languages.ti('correct_extrem_uv_scale_use_absolute_name')),
+ description=(languages.tt('correct_extrem_uv_scale_use_absolute_desc')),
+ override={'LIBRARY_OVERRIDABLE'},
+ default=False,
+ )
+
+def unregister():
+ for cls in reversed(classes):
+ bpy.utils.unregister_class(cls)
+
+ del bpy.types.Object.bfu_correct_extrem_uv_scale_use_absolute
+ del bpy.types.Object.bfu_correct_extrem_uv_scale_step_scale
+ del bpy.types.Object.bfu_use_correct_extrem_uv_scale
+ del bpy.types.Object.bfu_convert_geometry_node_attribute_to_uv_name
+ del bpy.types.Object.bfu_convert_geometry_node_attribute_to_uv
+
+ del bpy.types.Scene.bfu_tools_uv_map_properties_expanded
+ del bpy.types.Scene.bfu_object_uv_map_properties_expanded
diff --git a/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_ui.py b/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_ui.py
new file mode 100644
index 00000000..59ecc754
--- /dev/null
+++ b/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_ui.py
@@ -0,0 +1,82 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+
+import bpy
+from .. import bfu_basics
+from .. import bfu_utils
+from .. import bfu_ui
+from .. import bbpl
+
+
+def draw_obj_ui(layout: bpy.types.UILayout, obj: bpy.types.Object):
+
+ scene = bpy.context.scene
+
+ # Hide filters
+ if obj is None:
+ return
+ if bfu_utils.GetExportAsProxy(obj):
+ return
+ if obj.bfu_export_type != "export_recursive":
+ return
+
+ if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "MISC"):
+ scene.bfu_object_uv_map_properties_expanded.draw(layout)
+ if scene.bfu_object_uv_map_properties_expanded.is_expend():
+ # Geometry Node Uv
+ bfu_convert_geometry_node_attribute_to_uv = layout.column()
+ convert_geometry_node_attribute_to_uv_use = bfu_convert_geometry_node_attribute_to_uv.row()
+ convert_geometry_node_attribute_to_uv_use.prop(obj, 'bfu_convert_geometry_node_attribute_to_uv')
+ bbpl.blender_layout.layout_doc_button.add_doc_page_operator(convert_geometry_node_attribute_to_uv_use, url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/UV-Maps#geometry-node-uv")
+ bfu_convert_geometry_node_attribute_to_uv_name = bfu_convert_geometry_node_attribute_to_uv.column()
+ bfu_convert_geometry_node_attribute_to_uv_name.prop(obj, 'bfu_convert_geometry_node_attribute_to_uv_name')
+ bfu_convert_geometry_node_attribute_to_uv_name.enabled = obj.bfu_convert_geometry_node_attribute_to_uv
+
+ # Extreme UV Scale
+ ui_correct_extrem_uv_scale = layout.column()
+ ui_correct_extrem_uv_scale_use = ui_correct_extrem_uv_scale.row()
+ ui_correct_extrem_uv_scale_use.prop(obj, 'bfu_use_correct_extrem_uv_scale')
+ bbpl.blender_layout.layout_doc_button.add_doc_page_operator(ui_correct_extrem_uv_scale_use, url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/UV-Maps#extreme-uv-scale")
+ ui_correct_extrem_uv_scale_options = ui_correct_extrem_uv_scale.column()
+ ui_correct_extrem_uv_scale_options.prop(obj, 'bfu_correct_extrem_uv_scale_step_scale')
+ ui_correct_extrem_uv_scale_options.prop(obj, 'bfu_correct_extrem_uv_scale_use_absolute')
+ ui_correct_extrem_uv_scale_options.enabled = obj.bfu_use_correct_extrem_uv_scale
+
+
+def draw_tools_ui(layout: bpy.types.UILayout, context: bpy.types.Context):
+ scene = context.scene
+ scene.bfu_tools_uv_map_properties_expanded.draw(layout)
+ if scene.bfu_tools_uv_map_properties_expanded.is_expend():
+ ready_for_correct_extrem_uv_scale = False
+ obj = bpy.context.object
+ if obj and obj.type == "MESH":
+ if bbpl.utils.active_mode_is("EDIT"):
+ ready_for_correct_extrem_uv_scale = True
+ else:
+ layout.label(text="Switch to Edit Mode.", icon='INFO')
+ else:
+ layout.label(text="Select an mesh object", icon='INFO')
+
+
+ # Draw buttons (correct_extrem_uv)
+ Buttons_correct_extrem_uv_scale = layout.row()
+ Button_correct_extrem_uv_scale = Buttons_correct_extrem_uv_scale.column()
+ Button_correct_extrem_uv_scale.enabled = ready_for_correct_extrem_uv_scale
+ Button_correct_extrem_uv_scale.operator("object.correct_extrem_uv", icon='UV')
+ bbpl.blender_layout.layout_doc_button.add_doc_page_operator(Buttons_correct_extrem_uv_scale, url="https://github.com/xavier150/Blender-For-UnrealEngine-Addons/wiki/UV-Maps#extreme-uv-scale")
diff --git a/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_utils.py b/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_utils.py
new file mode 100644
index 00000000..1f5fdd31
--- /dev/null
+++ b/blender-for-unrealengine/bfu_uv_map/bfu_uv_map_utils.py
@@ -0,0 +1,21 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+import bpy
+from .. import bbpl
+
diff --git a/blender-for-unrealengine/bfu_vertex_color/bfu_vertex_color_ui.py b/blender-for-unrealengine/bfu_vertex_color/bfu_vertex_color_ui.py
index 5f1de0ab..e0e04a4a 100644
--- a/blender-for-unrealengine/bfu_vertex_color/bfu_vertex_color_ui.py
+++ b/blender-for-unrealengine/bfu_vertex_color/bfu_vertex_color_ui.py
@@ -26,7 +26,7 @@
-def draw_ui_object_collision(layout: bpy.types.UILayout):
+def draw_ui_object(layout: bpy.types.UILayout):
if bfu_ui.bfu_ui_utils.DisplayPropertyFilter("OBJECT", "MISC"):
scene = bpy.context.scene
diff --git a/blender-for-unrealengine/bfu_write_import_asset_script.py b/blender-for-unrealengine/bfu_write_import_asset_script.py
index 4b0f1b73..41e9119f 100644
--- a/blender-for-unrealengine/bfu_write_import_asset_script.py
+++ b/blender-for-unrealengine/bfu_write_import_asset_script.py
@@ -91,7 +91,7 @@ def WriteImportAssetScript():
# Skeleton
if(asset.object.bfu_engine_ref_skeleton_search_mode) == "auto":
- asset_data["target_skeleton_ref"] = bfu_unreal_utils.GetPredictedSkeletonRef(asset.object)
+ asset_data["target_skeleton_ref"] = bfu_unreal_utils.get_predicted_skeleton_ref(asset.object)
elif(asset.object.bfu_engine_ref_skeleton_search_mode) == "custom_name":
name = bfu_utils.ValidUnrealAssetsName(asset.object.bfu_engine_ref_skeleton_custom_name)
@@ -111,7 +111,7 @@ def WriteImportAssetScript():
# Skeletal Mesh
if(asset.object.bfu_engine_ref_skeletal_mesh_search_mode) == "auto":
- asset_data["target_skeletal_mesh_ref"] = bfu_unreal_utils.GetPredictedSkeletalMeshRef(asset.object)
+ asset_data["target_skeletal_mesh_ref"] = bfu_unreal_utils.get_predicted_skeleton_ref(asset.object)
elif(asset.object.bfu_engine_ref_skeletal_mesh_search_mode) == "custom_name":
name = bfu_utils.ValidUnrealAssetsName(asset.object.bfu_engine_ref_skeletal_mesh_custom_name)
diff --git a/blender-for-unrealengine/bfu_write_text.py b/blender-for-unrealengine/bfu_write_text.py
index 3ff4f5c7..717e6e28 100644
--- a/blender-for-unrealengine/bfu_write_text.py
+++ b/blender-for-unrealengine/bfu_write_text.py
@@ -22,7 +22,7 @@
import bpy
import math
-from . import bps
+from . import bpl
from . import bbpl
from . import languages
from . import bfu_basics
@@ -42,7 +42,7 @@
def ExportSingleText(text, dirpath, filename):
# Export single text
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
absdirpath = bpy.path.abspath(dirpath)
bfu_basics.VerifiDirs(absdirpath)
@@ -59,7 +59,7 @@ def ExportSingleText(text, dirpath, filename):
def ExportSingleJson(json_data, dirpath, filename):
# Export single Json
- counter = bps.utils.CounterTimer()
+ counter = bpl.utils.CounterTimer()
absdirpath = bpy.path.abspath(dirpath)
bfu_basics.VerifiDirs(absdirpath)
@@ -172,7 +172,7 @@ def WriteSingleMeshAdditionalParameter(unreal_exported_asset):
def GetLodPath(lod_obj):
asset_class = bfu_assets_manager.bfu_asset_manager_utils.get_asset_class(lod_obj)
if asset_class:
- directory_path = asset_class.get_obj_export_abs_directory_path(lod_obj)
+ directory_path = asset_class.get_obj_export_directory_path(lod_obj, True)
file_name = asset_class.get_obj_file_name(lod_obj)
return os.path.join(directory_path, file_name)
@@ -211,33 +211,34 @@ def WriteAllTextFiles():
scene = bpy.context.scene
addon_prefs = bfu_basics.GetAddonPrefs()
-
- if scene.text_ExportLog:
+
+ root_dirpath = bpy.path.abspath(scene.bfu_export_other_file_path)
+ if scene.bfu_use_text_export_log:
Text = languages.ti("write_text_additional_track_start") + "\n"
Text += "" + "\n"
Text += WriteExportLog()
if Text is not None:
Filename = bfu_basics.ValidFilename(scene.bfu_file_export_log_name)
- ExportSingleText(Text, scene.bfu_export_other_file_path, Filename)
+ ExportSingleText(Text, root_dirpath, Filename)
# Import script
if bpy.app.version >= (4, 2, 0):
- bfu_path = os.path.join(bbpl.blender_extension.extension_utils.get_package_path("blender_for_unrealengine"), "bfu_import_module")
+ bfu_path = os.path.join(bbpl.blender_extension.extension_utils.get_package_path(), "bfu_import_module")
else:
- bfu_path = os.path.join(bbpl.blender_addon.addon_utils.get_addon_path("Blender for UnrealEngine"), "bfu_import_module")
+ bfu_path = os.path.join(bbpl.blender_addon.addon_utils.get_addon_path("Unreal Engine Assets Exporter"), "bfu_import_module")
- if scene.text_ImportAssetScript:
+ if scene.bfu_use_text_import_asset_script:
json_data = bfu_write_import_asset_script.WriteImportAssetScript()
- ExportSingleJson(json_data, scene.bfu_export_other_file_path, "ImportAssetData.json")
+ ExportSingleJson(json_data, root_dirpath, "ImportAssetData.json")
source = os.path.join(bfu_path, "asset_import_script.py")
filename = bfu_basics.ValidFilename(scene.bfu_file_import_asset_script_name)
- destination = bpy.path.abspath(os.path.join(scene.bfu_export_other_file_path, filename))
+ destination = os.path.join(root_dirpath, filename)
copyfile(source, destination)
- if scene.text_ImportSequenceScript:
+ if scene.bfu_use_text_import_sequence_script:
json_data = bfu_write_import_sequencer_script.WriteImportSequencerTracks()
- ExportSingleJson(json_data, scene.bfu_export_other_file_path, "ImportSequencerData.json")
+ ExportSingleJson(json_data, root_dirpath, "ImportSequencerData.json")
source = os.path.join(bfu_path, "sequencer_import_script.py")
filename = bfu_basics.ValidFilename(scene.bfu_file_import_sequencer_script_name)
- destination = bpy.path.abspath(os.path.join(scene.bfu_export_other_file_path, filename))
+ destination = os.path.join(root_dirpath, filename)
copyfile(source, destination)
diff --git a/blender-for-unrealengine/bfu_write_utils.py b/blender-for-unrealengine/bfu_write_utils.py
index 62890db0..d4eaaf24 100644
--- a/blender-for-unrealengine/bfu_write_utils.py
+++ b/blender-for-unrealengine/bfu_write_utils.py
@@ -19,6 +19,7 @@
import os
import bpy
import datetime
+
from . import bbpl
from . import bfu_basics
from . import bfu_utils
@@ -70,11 +71,11 @@ def add_generated_json_meta_data(json_data):
blender_file_path = bpy.data.filepath
if bpy.app.version >= (4, 2, 0):
- version_str = 'Version '+ bbpl.blender_extension.extension_utils.get_package_version("blender_for_unrealengine")
- addon_path = bbpl.blender_extension.extension_utils.get_package_path("blender_for_unrealengine")
+ version_str = 'Version '+ str(bbpl.blender_extension.extension_utils.get_package_version())
+ addon_path = bbpl.blender_extension.extension_utils.get_package_path()
else:
- version_str = 'Version '+ bbpl.blender_addon.addon_utils.get_addon_version_str("Blender for UnrealEngine")
- addon_path = bbpl.blender_addon.addon_utils.get_addon_path("Blender for UnrealEngine")
+ version_str = 'Version '+ bbpl.blender_addon.addon_utils.get_addon_version_str("Unreal Engine Assets Exporter")
+ addon_path = bbpl.blender_addon.addon_utils.get_addon_path("Unreal Engine Assets Exporter")
import_modiule_path = os.path.join(addon_path, "bfu_import_module")
diff --git a/blender-for-unrealengine/blender_manifest.toml b/blender-for-unrealengine/blender_manifest.toml
deleted file mode 100644
index e494c6d6..00000000
--- a/blender-for-unrealengine/blender_manifest.toml
+++ /dev/null
@@ -1,27 +0,0 @@
-schema_version = "1.0.0"
-
-id = "blender_for_unrealengine"
-version = "4.3.1"
-name = "Blender for UnrealEngine"
-tagline = "Allows to batch export and import in Unreal Engine"
-maintainer = "Loux Xavier (BleuRaven) xavierloux.loux@gmail.com"
-type = "add-on"
-
-website = "https://github.com/xavier150/Blender-For-UnrealEngine-Addons/"
-
-tags = ["Import-Export"]
-
-blender_version_min = "4.2.0"
-
-license = [
- "SPDX:GPL-3.0-or-later",
-]
-
-copyright = [
- "Copyright 2024, Xavier Loux. All rights reserved.",
-]
-
-platforms = ['windows-x64', 'linux-x64']
-
-[permissions]
-files = "Import/export FBX from/to disk"
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/bps/__init__.py b/blender-for-unrealengine/bpl/__init__.py
similarity index 93%
rename from blender-for-unrealengine/bfu_import_module/bps/__init__.py
rename to blender-for-unrealengine/bpl/__init__.py
index 3da665cf..ff3e288d 100644
--- a/blender-for-unrealengine/bfu_import_module/bps/__init__.py
+++ b/blender-for-unrealengine/bpl/__init__.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
@@ -45,4 +46,5 @@
if "blender_sub_process" in locals():
importlib.reload(blender_sub_process)
if "naming" in locals():
- importlib.reload(naming)
\ No newline at end of file
+ importlib.reload(naming)
+
diff --git a/blender-for-unrealengine/bps/advprint.py b/blender-for-unrealengine/bpl/advprint.py
similarity index 85%
rename from blender-for-unrealengine/bps/advprint.py
rename to blender-for-unrealengine/bpl/advprint.py
index 75ac6f27..414d5efb 100644
--- a/blender-for-unrealengine/bps/advprint.py
+++ b/blender-for-unrealengine/bpl/advprint.py
@@ -17,13 +17,12 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
-
-import sys
import time
@@ -34,7 +33,7 @@ def _get_name(self):
def _set_name(self, value):
if not isinstance(value, str):
- raise TypeError("name must be set to an String")
+ raise TypeError("name must be set to a String")
self.__name = value
name = property(_get_name, _set_name)
@@ -57,7 +56,6 @@ def _get_total_step(self):
def _set_total_step(self, value):
if not (isinstance(value, int) or isinstance(value, float)):
raise TypeError("total_step must be set to an Integer or Float")
-
self.__total_step = value
total_step = property(_get_total_step, _set_total_step)
@@ -80,29 +78,25 @@ def update_progress(self, progress):
total_step = self.__total_step
self.__previous_step = progress # Update the previous step.
- is_done = False
- if progress >= total_step:
- is_done = True
+ is_done = progress >= total_step
- # Write message.
+ # Write message
msg = "\r{0}:".format(job_title)
if self.show_block:
- block = int(round(length*progress/total_step))
- msg += " [{0}]".format("#"*block + "-"*(length-block))
+ block = int(round(length * progress / total_step))
+ msg += " [{0}]".format("#" * block + "-" * (length - block))
if self.show_steps:
msg += " {0}/{1}".format(progress, total_step)
if is_done:
- msg += " DONE IN {0}s\r\n".format(round(time.perf_counter()-self.__counter_start, 3))
-
- else:
- if self.show_percentage:
- msg += " {0}%".format(round((progress*100)/total_step, 2))
+ msg += " DONE IN {0}s\r\n".format(round(time.perf_counter() - self.__counter_start, 3))
+ elif self.show_percentage:
+ msg += " {0}%".format(round((progress * 100) / total_step, 2))
- sys.stdout.write(msg)
- sys.stdout.flush()
+ # Print the progress message on the same line
+ print(msg, end='', flush=True)
def print_separation(number=60, char="-"):
diff --git a/blender-for-unrealengine/bps/blender_sub_process.py b/blender-for-unrealengine/bpl/blender_sub_process.py
similarity index 93%
rename from blender-for-unrealengine/bps/blender_sub_process.py
rename to blender-for-unrealengine/bpl/blender_sub_process.py
index f845d516..035cac54 100644
--- a/blender-for-unrealengine/bps/blender_sub_process.py
+++ b/blender-for-unrealengine/bpl/blender_sub_process.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bps/color_set.py b/blender-for-unrealengine/bpl/color_set.py
similarity index 98%
rename from blender-for-unrealengine/bps/color_set.py
rename to blender-for-unrealengine/bpl/color_set.py
index 8d39ebe4..3b066ea5 100644
--- a/blender-for-unrealengine/bps/color_set.py
+++ b/blender-for-unrealengine/bpl/color_set.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bpl/console_utils.py b/blender-for-unrealengine/bpl/console_utils.py
new file mode 100644
index 00000000..c6c1ba47
--- /dev/null
+++ b/blender-for-unrealengine/bpl/console_utils.py
@@ -0,0 +1,29 @@
+# ====================== BEGIN GPL LICENSE BLOCK ============================
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+# ======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
+# BleuRaven.fr
+# XavierLoux.com
+# ----------------------------------------------
+
+import os
+
+def clear_console():
+ os.system('cls' if os.name == 'nt' else 'clear')
\ No newline at end of file
diff --git a/blender-for-unrealengine/bfu_import_module/bps/math.py b/blender-for-unrealengine/bpl/math.py
similarity index 96%
rename from blender-for-unrealengine/bfu_import_module/bps/math.py
rename to blender-for-unrealengine/bpl/math.py
index d036605a..376bc45b 100644
--- a/blender-for-unrealengine/bfu_import_module/bps/math.py
+++ b/blender-for-unrealengine/bpl/math.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bps/naming.py b/blender-for-unrealengine/bpl/naming.py
similarity index 97%
rename from blender-for-unrealengine/bps/naming.py
rename to blender-for-unrealengine/bpl/naming.py
index 9c43c4f6..497e43e1 100644
--- a/blender-for-unrealengine/bps/naming.py
+++ b/blender-for-unrealengine/bpl/naming.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bfu_import_module/bps/utils.py b/blender-for-unrealengine/bpl/utils.py
similarity index 97%
rename from blender-for-unrealengine/bfu_import_module/bps/utils.py
rename to blender-for-unrealengine/bpl/utils.py
index 320f3319..bc6592d7 100644
--- a/blender-for-unrealengine/bfu_import_module/bps/utils.py
+++ b/blender-for-unrealengine/bpl/utils.py
@@ -17,7 +17,8 @@
# ======================= END GPL LICENSE BLOCK =============================
# ----------------------------------------------
-# BPS -> BleuRaven Python Script
+# BPL -> BleuRaven Python Library
+# https://github.com/xavier150/BPL
# BleuRaven.fr
# XavierLoux.com
# ----------------------------------------------
diff --git a/blender-for-unrealengine/bps/console_utils.py b/blender-for-unrealengine/bps/console_utils.py
deleted file mode 100644
index 596331d9..00000000
--- a/blender-for-unrealengine/bps/console_utils.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import os
-
-def clear_console():
- os.system('cls' if os.name == 'nt' else 'clear')
\ No newline at end of file
diff --git a/blender-for-unrealengine/languages/local_list/en_US.json b/blender-for-unrealengine/languages/local_list/en_US.json
index 476e8888..3efe1e2a 100644
--- a/blender-for-unrealengine/languages/local_list/en_US.json
+++ b/blender-for-unrealengine/languages/local_list/en_US.json
@@ -1,8 +1,10 @@
{
"interface": {
- "intro": "Blender for Unreal Engine by Xavier Loux. (BleuRaven)",
+ "intro": "Unreal Engine Assets Exporter by Xavier Loux. (BleuRaven)",
"bake_armature_action_name": "Bake Armature animation",
- "correct_extrem_uv_scale_name": "Correct Extrem UV Scale",
+ "correct_use_extrem_uv_scale_name": "Correct Extrem UV Scale For Unreal",
+ "correct_extrem_uv_scale_step_scale_name": "Step Scale",
+ "correct_extrem_uv_scale_use_absolute_name": "Use Positive Pos",
"add_skeleton_root_bone_name": "Add root bone",
"skeleton_root_bone_name_name": "Skeleton root bone name",
"rescale_full_rig_at_export_name": "Rescale exported rig",
@@ -37,7 +39,10 @@
},
"tooltips": {
"bake_armature_action_desc": "Bake Armature animation for export (Export will take more time).",
- "correct_extrem_uv_scale_desc": "Correct Extrem UV Scale for better UV quality in UE4 (Export will take more time).",
+ "correct_use_extrem_uv_scale_desc": "Correct Extrem UV Scale for better UV quality in Unreal Engine (Export will take more time).",
+ "correct_extrem_uv_scale_step_scale_desc": "Scale of the snap grid.",
+ "correct_extrem_uv_scale_use_absolute_desc": "Keep uv islands to positive positions.",
+ "correct_extrem_uv_scale_operator_desc": "Correct Extrem UV Scale for better UV quality in Unreal Engine (Export will take more time).",
"add_skeleton_root_bone_desc": "Remove the armature root bone.",
"skeleton_root_bone_name_desc": "Name of the armature when exported. This is used to change the root bone name. If egal \"Armature\" Ue4 will remove the Armature root bone.",
"rescale_full_rig_at_export_desc": "This will rescale the full rig at the export with the all constraints.",
diff --git a/blender-for-unrealengine/languages/local_list/fr_FR.json b/blender-for-unrealengine/languages/local_list/fr_FR.json
index 528c16c2..ebd9cc07 100644
--- a/blender-for-unrealengine/languages/local_list/fr_FR.json
+++ b/blender-for-unrealengine/languages/local_list/fr_FR.json
@@ -2,7 +2,7 @@
"tooltips": {
},
"interface": {
- "intro": "Blender for Unreal Engine par Xavier Loux."
+ "intro": "Unreal Engine Assets Exporter par Xavier Loux."
},
"new_data": {
}
diff --git a/blender-for-unrealengine/languages/local_list/ru_RU.json b/blender-for-unrealengine/languages/local_list/ru_RU.json
index 3889e8ae..38200197 100644
--- a/blender-for-unrealengine/languages/local_list/ru_RU.json
+++ b/blender-for-unrealengine/languages/local_list/ru_RU.json
@@ -1,8 +1,10 @@
{
"interface": {
- "intro": "Blender for Unreal Engine от Xavier Loux. (BleuRaven)",
+ "intro": "Unreal Engine Assets Exporter от Xavier Loux. (BleuRaven)",
"bake_armature_action_name": "Запечь анимацию скелета",
- "correct_extrem_uv_scale_name": "Исправить масштаб UV",
+ "correct_use_extrem_uv_scale_name": "Исправить масштаб UV",
+ "correct_extrem_uv_scale_step_scale_name": "Step Scale",
+ "correct_extrem_uv_scale_use_absolute_name": "Use Positive Pos",
"add_skeleton_root_bone_name": "Добавить корневую кость",
"skeleton_root_bone_name_name": "Название корневой кости скелета",
"rescale_full_rig_at_export_name": "Изменить масштаб экспортированного рига",
@@ -37,7 +39,10 @@
},
"tooltips": {
"bake_armature_action_desc": "Запечь анимацию скелета для экспорта (экспорт займет больше времени).",
- "correct_extrem_uv_scale_desc": "Исправить масштаб UV для лучшего качества UV в UE4 (экспорт займет больше времени).",
+ "correct_use_extrem_uv_scale_desc": "Исправить масштаб UV для лучшего качества UV в UE4 (экспорт займет больше времени).",
+ "correct_extrem_uv_scale_step_scale_desc": "Scale of the snap grid.",
+ "correct_extrem_uv_scale_use_absolute_desc": "Keep uv islands to positive positions.",
+ "correct_extrem_uv_scale_operator_desc": "Correct Extrem UV Scale for better UV quality in Unreal Engine (Export will take more time).",
"add_skeleton_root_bone_desc": "Удалить корневую кость скелета.",
"skeleton_root_bone_name_desc": "Название скелета при экспорте. Это используется для изменения имени корневой кости. Если равно \"Armature\", Ue4 удалит корневую кость Armature.",
"rescale_full_rig_at_export_desc": "Это изменит масштаб всего рига при экспорте со всеми ограничениями.",
diff --git a/blender-for-unrealengine/languages/local_list/zh_HANS.json b/blender-for-unrealengine/languages/local_list/zh_HANS.json
new file mode 100644
index 00000000..3c5709f5
--- /dev/null
+++ b/blender-for-unrealengine/languages/local_list/zh_HANS.json
@@ -0,0 +1,69 @@
+{
+ "interface": {
+ "intro": "Blender for Unreal Engine by Xavier Loux. (BleuRaven)",
+ "bake_armature_action_name": "烘焙骨架动画",
+ "correct_extrem_uv_scale_name": "纠正极端UV缩放",
+ "add_skeleton_root_bone_name": "添加根骨骼",
+ "skeleton_root_bone_name_name": "骨架根骨骼名称",
+ "rescale_full_rig_at_export_name": "导出时调整rig比例",
+ "rescale_full_rig_at_export_auto_name": "自动",
+ "rescale_full_rig_at_export_custom_rescale_name": "自定义比例",
+ "rescale_full_rig_at_export_dont_rescale_name": "不调整比例",
+ "new_rig_scale_name": "新比例",
+ "static_sockets_add_90_x_name": "导出静态网格插槽时X轴+90度",
+ "rescale_sockets_at_export_name": "导出时调整插槽比例",
+ "rescale_sockets_at_export_auto_name": "自动",
+ "rescale_sockets_at_export_custom_rescale_name": "自定义比例",
+ "rescale_sockets_at_export_dont_rescale_name": "不调整比例",
+ "static_sockets_imported_size_name": "静态网格插槽导入尺寸",
+ "skeletal_sockets_imported_size_name": "骨架网格插槽导入尺寸",
+ "export_camera_as_fbx_name": "导出相机为FBX",
+ "export_spline_as_fbx_name": "导出样条为FBX",
+ "bake_only_key_visible_in_cut_name": "仅烘焙可见帧",
+ "ignore_nla_for_action_name": "忽略Actions的非线性动画",
+ "export_with_custom_props_name": "导出自定义属性",
+ "export_with_custom_curves_name": "导出自定义曲线值",
+ "export_with_meta_data_name": "导出元数据",
+ "revert_export_path_name": "每次导出时重置所有导出路径。",
+ "use_generated_scripts_name": "使用生成的脚本导入资源和序列。",
+ "collision_color_name": "碰撞体颜色",
+ "notify_unit_scale_potential_error_name": "如果单位比例不等于0.01, 则通知潜在错误",
+ "write_text_additional_track_start": "此文件由插件Blender for UnrealEngine生成: https://github.com/xavier150/Blender-For-UnrealEngine-Addons",
+ "write_text_additional_track_end": "此脚本应在UE编辑器中与其Python插件一起使用: https://docs.unrealengine.com/en-US/Engine/Editor/ScriptingAndAutomation/Python",
+ "write_text_additional_track_spline": "此文件包含fbx文件不支持的样条线数据附加信息。",
+ "write_text_additional_track_camera": "此文件包含fbx文件不支持的相机动画附加信息。",
+ "write_text_additional_track_all": "用于在UE中导入所有类型的资源, 如StaticMesh、SkeletalMesh、Animation、Pose、Camera等",
+ "end": "结束"
+ },
+ "tooltips": {
+ "bake_armature_action_desc": "烘焙骨架动画以便导出(更长导出时间)。",
+ "correct_extrem_uv_scale_desc": "修正UV比例以提高UE4中的UV质量(更长导出时间)。",
+ "add_skeleton_root_bone_desc": "移除骨架的根骨骼。",
+ "skeleton_root_bone_name_desc": "导出时的骨架名称。用于更改根骨骼的名称。如果为“Armature”, Ue4将删除Armature根骨骼。",
+ "rescale_full_rig_at_export_desc": "导出时调整整个骨架的比例,包括所有约束。",
+ "rescale_full_rig_at_export_auto_desc": "仅在单位比例不等于0.01时调整比例。",
+ "rescale_full_rig_at_export_custom_rescale_desc": "您可以选择导出时如何调整骨架比例",
+ "rescale_full_rig_at_export_dont_rescale_desc": "不调整rig比例",
+ "new_rig_scale_desc": "新rig比例。自动: [新比例] = 100 * [单位比例]",
+ "static_sockets_add_90_x_desc": "静态网格插槽在UE中以X轴-90度自动导入",
+ "rescale_sockets_at_export_desc": "导出时调整所有插槽的比例。",
+ "rescale_sockets_at_export_auto_desc": "仅在单位比例不等于0.01时调整比例。",
+ "rescale_sockets_at_export_custom_rescale_desc": "您可以选择导出时如何调整插槽比例。",
+ "rescale_sockets_at_export_dont_rescale_desc": "不调整插槽比例。自动: 1([新比例] = 100 / [单位比例])",
+ "static_sockets_imported_size_desc": "在UE中导入时的插槽尺寸。",
+ "skeletal_sockets_imported_size_desc": "在UE中导入时的插槽尺寸。自动: 1([新比例] = 100 / [单位比例])",
+ "export_camera_as_fbx_desc": "当导出附加数据(导出 -> 导出过滤器)并使用序列导入脚本时,取消选中此项可以缩短导出时间。",
+ "export_spline_as_fbx_desc": "导出样条线为FBX文件",
+ "bake_only_key_visible_in_cut_desc": "仅当在相机帧中可见时才烘焙相机。 仅在相机帧中可见时烘焙相机。",
+ "ignore_nla_for_action_desc": "导出动作并忽略非线性动画中的所有层。",
+ "export_with_custom_props_desc": "处理带有自定义属性的导出(可用于元数据)。",
+ "export_with_custom_curves_desc": "处理带有自定义属性的导出(用于UE自定义曲线值)",
+ "export_with_meta_data_desc": "处理带有元数据的导出。",
+ "revert_export_path_desc": "每次导出时删除所有导出路径的文件夹。",
+ "use_generated_scripts_desc": "如果为 false, 则所有仅适用于导入脚本的属性都将被禁用。",
+ "collision_color_desc": "Blender中的碰撞体颜色。",
+ "notify_unit_scale_potential_error_desc": "如果单位比例不等于0.01, 则通知潜在错误。",
+ "end": "结束"
+ },
+ "new_data": {}
+}
\ No newline at end of file
diff --git a/blender-for-unrealengine/run_unreal_import_script.py b/blender-for-unrealengine/run_unreal_import_script.py
new file mode 100644
index 00000000..5e08ca81
--- /dev/null
+++ b/blender-for-unrealengine/run_unreal_import_script.py
@@ -0,0 +1,102 @@
+# This script should be run in Unreal Engine to import assets into Unreal Engine 4 and 5.
+# The assets are exported from the Unreal Engine Assets Exporter. More details can be found here: https://github.com/xavier150/Blender-For-UnrealEngine-Addons
+# Use the following command in Unreal cmd console and follow the instructions:
+# py "[ScriptLocation]\run_unreal_import_script.py" -h
+# py "M:\MMVS_ProjectFiles\content\Level\TalasCozyHouse\ExportedFbx\ImportAssetScript.py" -h
+
+import os
+import sys
+import importlib.util
+import argparse
+import json
+
+def json_load(json_file):
+ # In Python 3.9: The keyword argument encoding has been removed.
+ if sys.version_info >= (3, 9):
+ return json.load(json_file)
+ else:
+ return json.load(json_file, encoding="utf8")
+
+def json_load_file(json_file_path):
+ # In Python 3.9: The keyword argument encoding has been removed.
+ if sys.version_info[0] < 3:
+ with open(json_file_path, "r") as json_file:
+ return json_load(json_file)
+ else:
+ with open(json_file_path, "r", encoding="utf8") as json_file:
+ return json_load(json_file)
+
+def import_unreal_module():
+ # Get the script directory
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ module_name = "bfu_import_module"
+ module_file = os.path.join(script_dir, module_name, "__init__.py")
+
+ # Load the module dynamically
+ spec = importlib.util.spec_from_file_location(module_name, module_file)
+ module = importlib.util.module_from_spec(spec)
+ sys.modules[module_name] = module
+ spec.loader.exec_module(module)
+ return module
+
+def clear_unreal_module():
+ module_name = "bfu_import_module"
+ del sys.modules[module_name]
+
+def run_from_asset_import_script(import_data_filepath):
+ module = import_unreal_module()
+ try:
+ module.run_asset_import(json_load_file(import_data_filepath))
+ except Exception as e:
+ print(f"An error has occurred: {e}")
+ clear_unreal_module()
+
+def run_from_sequencer_import_script(import_data_filepath):
+ module = import_unreal_module()
+ try:
+ module.run_sequencer_import(json_load_file(import_data_filepath))
+ except Exception as e:
+ print(f"An error has occurred: {e}")
+ clear_unreal_module()
+
+def run_from_arguments():
+ args_valid = False
+ parser = argparse.ArgumentParser(description='Process Unreal Engine asset or sequencer import.')
+ parser.add_argument('--type', type=str, required=True, help='Content type to import in Unreal Engine. (required)')
+ parser.add_argument('--data_filepath', type=str, required=True, help='JSON filename with data to import. (required)')
+ parser.add_argument('--show_finished_popup', action='store_true', help='Show a popup when finished. (optional)')
+
+ try:
+ args = parser.parse_args()
+ args_valid = True
+ except argparse.ArgumentError as e:
+ print(f"Argument error: {e}")
+ except SystemExit as e:
+ print("Error: Required arguments are missing. Use -h in arguments for help.")
+
+ if(args_valid):
+ import_type = args.type
+ import_data_filepath = args.data_filepath
+ show_finished_popup = args.show_finished_popup
+
+ if import_type == "assets":
+ asset_data = json_load_file(import_data_filepath)
+ module = import_unreal_module()
+ try:
+ module.run_asset_import(asset_data, show_finished_popup)
+ except Exception as e:
+ print(f"An error has occurred: {e}")
+ clear_unreal_module()
+ elif import_type == "sequencer":
+ asset_data = json_load_file(import_data_filepath)
+ module = import_unreal_module()
+ try:
+ module.run_sequencer_import(asset_data, show_finished_popup)
+ except Exception as e:
+ print(f"An error has occurred: {e}")
+ clear_unreal_module()
+ else:
+ print("Error: --type must be 'assets' or 'sequencer'")
+
+if __name__ == "__main__":
+ run_from_arguments()
diff --git a/docs/Examples/AssetsExample_Alembic.blend b/docs/Examples/AssetsExample_Alembic.blend
index 7903567e..6b130603 100644
Binary files a/docs/Examples/AssetsExample_Alembic.blend and b/docs/Examples/AssetsExample_Alembic.blend differ
diff --git a/docs/Examples/AssetsExample_Animation_SkeletonSearch.blend b/docs/Examples/AssetsExample_Animation_SkeletonSearch.blend
index 20943d62..7bc3b5b0 100644
Binary files a/docs/Examples/AssetsExample_Animation_SkeletonSearch.blend and b/docs/Examples/AssetsExample_Animation_SkeletonSearch.blend differ
diff --git a/docs/Examples/AssetsExample_Lod.blend b/docs/Examples/AssetsExample_Lod.blend
index 4d1dc5ae..ce5e564b 100644
Binary files a/docs/Examples/AssetsExample_Lod.blend and b/docs/Examples/AssetsExample_Lod.blend differ
diff --git a/docs/Examples/AssetsExample_ModularExport.blend b/docs/Examples/AssetsExample_ModularExport.blend
index 5b2fb4bf..d93cbb8f 100644
Binary files a/docs/Examples/AssetsExample_ModularExport.blend and b/docs/Examples/AssetsExample_ModularExport.blend differ
diff --git a/docs/Examples/AssetsExample_MultiTypeExport.blend b/docs/Examples/AssetsExample_MultiTypeExport.blend
index f5c44cec..4091a86d 100644
Binary files a/docs/Examples/AssetsExample_MultiTypeExport.blend and b/docs/Examples/AssetsExample_MultiTypeExport.blend differ
diff --git a/docs/promo_pictures/Source Featured Images/Featured Image (NoBlenderLogo) 16.9.pdn b/docs/promo_pictures/Source Featured Images/Featured Image (NoBlenderLogo) 16.9.pdn
new file mode 100644
index 00000000..e5d7a443
Binary files /dev/null and b/docs/promo_pictures/Source Featured Images/Featured Image (NoBlenderLogo) 16.9.pdn differ
diff --git a/docs/promo_pictures/Source Featured Images/Icon Image (NoBlenderLogo) 1.1.pdn b/docs/promo_pictures/Source Featured Images/Icon Image (NoBlenderLogo) 1.1.pdn
new file mode 100644
index 00000000..819ede66
--- /dev/null
+++ b/docs/promo_pictures/Source Featured Images/Icon Image (NoBlenderLogo) 1.1.pdn
@@ -0,0 +1,1892 @@
+PDN3V