Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """
- UE 4.27.2 - Export ONLY the currently selected asset(s) to T3D (SLIM, with dialogs)
- Exports only assets highlighted in the Content Browser (single or multi-select).
- Mirrors the /Game/... hierarchy under DEST_ROOT. Existing files at the destination
- that are NOT the selected asset are left untouched. Shows a popup at start (if
- nothing exportable was selected) and at completion.
- Setup:
- Requires "Python Editor Script Plugin" (Edit > Plugins > Scripting).
- Usage:
- 1. In the Content Browser, select one or more assets (Ctrl/Shift-click).
- 2. File > Execute Python Script... OR right-click > Scripted Asset Actions.
- """
- import os
- import re
- import unreal
- # ============================================================
- # CONFIG
- # ============================================================
- DEST_ROOT = r"F:/AI-ALIENS-T3D-slim"
- WHITELIST = {
- "Blueprint",
- "WidgetBlueprint",
- "BehaviorTree",
- "BlackboardData",
- "EnvQuery",
- "UserDefinedEnum",
- "UserDefinedStruct",
- "AnimBlueprint",
- "Material",
- "MaterialFunction",
- "MaterialInstanceConstant",
- }
- # ============================================================
- # ---- UI helpers ----------------------------------------------
- def format_bytes(n):
- if n < 1024:
- return "%d B" % n
- if n < 1024 * 1024:
- return "%.1f KB" % (n / 1024.0)
- return "%.1f MB" % (n / (1024.0 * 1024.0))
- def show_dialog(title, message):
- """Modal OK dialog. Falls back to Output Log if the API is missing."""
- try:
- unreal.EditorDialog.show_message(title, message, unreal.AppMsgType.OK)
- except Exception as e:
- unreal.log_warning("[Dialog fallback: %s] %s | %s" % (e, title, message))
- # ---- Optimizer -----------------------------------------------
- _DROP_LINE = re.compile(
- r"^\s*("
- r"NodePosX|NodePosY|NodeHeight|NodeWidth|"
- r"NodeGuid|BlueprintGuid|GraphGuid|VarGuid|"
- r"bLegacyNeedToPurgeSkelRefs|"
- r"bCommentBubblePinned|bCommentBubbleVisible|"
- r"bUserSetEnabledState|"
- r"PinPreviewIndex|"
- r"ThumbnailInfo"
- r")\s*="
- )
- _DROP_INDEXED = re.compile(r"^\s*(LastEditedDocuments|CategorySorting)\(\d+\)\s*=")
- _SKIP_OBJECT_HINTS = ("SceneThumbnailInfo",)
- _INLINE_STRIP = [
- re.compile(r"PersistentGuid=00000000000000000000000000000000,"),
- re.compile(r"bIsReference=False,"),
- re.compile(r"bIsConst=False,"),
- re.compile(r"bIsWeakPointer=False,"),
- re.compile(r"bIsUObjectWrapper=False,"),
- re.compile(r"bHidden=False,"),
- re.compile(r"bNotConnectable=False,"),
- re.compile(r"bDefaultValueIsReadOnly=False,"),
- re.compile(r"bDefaultValueIsIgnored=False,"),
- re.compile(r"bAdvancedView=False,"),
- re.compile(r"bOrphanedPin=False,"),
- re.compile(r'PinType\.PinSubCategory="",'),
- re.compile(r"PinType\.PinSubCategoryObject=None,"),
- re.compile(r"PinType\.PinSubCategoryMemberReference=\(\),"),
- re.compile(r"PinType\.PinValueType=\(\),"),
- re.compile(r"PinType\.ContainerType=None,"),
- re.compile(r'PinToolTip="[^"]*",'),
- re.compile(r"PinFriendlyName=NSLOCTEXT\([^)]*\),"),
- re.compile(r'AutogeneratedDefaultValue="[^"]*",'),
- ]
- def optimize_t3d_text(text):
- try:
- lines = text.split("\n")
- out = []
- skip_depth = 0
- for raw in lines:
- line = raw.rstrip("\r")
- stripped = line.strip()
- if skip_depth > 0:
- if stripped.startswith("Begin Object"):
- skip_depth += 1
- elif stripped.startswith("End Object"):
- skip_depth -= 1
- continue
- if stripped.startswith("Begin Object") and any(
- h in stripped for h in _SKIP_OBJECT_HINTS
- ):
- skip_depth = 1
- continue
- if _DROP_LINE.match(line) or _DROP_INDEXED.match(line):
- continue
- for pat in _INLINE_STRIP:
- line = pat.sub("", line)
- out.append(line)
- return "\r\n".join(out)
- except Exception as e:
- unreal.log_warning("optimize_t3d_text failed (%s); keeping raw." % e)
- return text
- # ---- Export helpers ------------------------------------------
- def content_path_to_relative(content_path):
- if content_path.startswith("/Game/"):
- return content_path[len("/Game/"):]
- if content_path == "/Game":
- return ""
- return content_path.lstrip("/")
- def export_asset(asset, out_file):
- task = unreal.AssetExportTask()
- task.object = asset
- task.filename = out_file
- task.automated = True
- task.prompt = False
- task.replace_identical = True
- task.use_file_archive = False
- task.write_empty_files = False
- return unreal.Exporter.run_asset_export_task(task)
- def slim_file_in_place(path):
- with open(path, "rb") as f:
- raw = f.read()
- orig = len(raw)
- try:
- text = raw.decode("utf-8")
- except UnicodeDecodeError:
- text = raw.decode("utf-8", errors="replace")
- new_bytes = optimize_t3d_text(text).encode("utf-8")
- with open(path, "wb") as f:
- f.write(new_bytes)
- return orig, len(new_bytes)
- # ---- Main ----------------------------------------------------
- def main():
- selected = unreal.EditorUtilityLibrary.get_selected_asset_data()
- if not selected:
- show_dialog(
- "T3D Export",
- "Nothing selected.\n\nHighlight one or more assets in the Content Browser and try again.",
- )
- return
- total = len(selected)
- unreal.log("Destination : %s" % DEST_ROOT)
- unreal.log("Selected : %d asset(s)" % total)
- exported = skipped = failed = 0
- skipped_names = []
- failed_list = []
- total_orig = 0
- total_new = 0
- for i, ad in enumerate(selected, start=1):
- name = str(ad.asset_name)
- cls = str(ad.asset_class)
- if cls not in WHITELIST:
- skipped += 1
- skipped_names.append("%s (%s)" % (name, cls))
- unreal.log_warning(
- "[%d/%d] SKIP (not in whitelist: %s) %s" % (i, total, cls, name)
- )
- continue
- pkg_path = str(ad.package_path)
- asset = ad.get_asset()
- if asset is None:
- failed += 1
- failed_list.append("%s (load failed)" % name)
- continue
- rel = content_path_to_relative(pkg_path)
- out_dir = os.path.normpath(os.path.join(DEST_ROOT, rel))
- try:
- os.makedirs(out_dir, exist_ok=True)
- except Exception as e:
- failed += 1
- failed_list.append("%s (mkdir: %s)" % (name, e))
- continue
- out_file = os.path.join(out_dir, name + ".t3d")
- ok = export_asset(asset, out_file)
- if not ok:
- failed += 1
- failed_list.append(name)
- unreal.log_warning("[%d/%d] FAIL %s" % (i, total, name))
- continue
- try:
- orig, new = slim_file_in_place(out_file)
- total_orig += orig
- total_new += new
- reduction = (1 - new / orig) * 100 if orig else 0
- exported += 1
- unreal.log(
- "[%d/%d] OK %s %d -> %d bytes (-%.0f%%)"
- % (i, total, name, orig, new, reduction)
- )
- except Exception as e:
- exported += 1
- unreal.log_warning(
- "[%d/%d] OK (slim failed: %s) %s" % (i, total, e, name)
- )
- unreal.log("=" * 60)
- unreal.log(
- "Done. Exported=%d Skipped(not-whitelisted)=%d Failed=%d"
- % (exported, skipped, failed)
- )
- if total_orig:
- overall = (1 - total_new / total_orig) * 100
- unreal.log(
- "Total size: %d -> %d bytes (saved %.0f%%)"
- % (total_orig, total_new, overall)
- )
- # -- Popup summary --
- if exported > 0:
- msg = "Exported %d of %d asset(s) to:\n%s" % (exported, total, DEST_ROOT)
- if total_orig:
- overall = (1 - total_new / total_orig) * 100
- msg += "\n\nSize: %s -> %s (saved %.0f%%)" % (
- format_bytes(total_orig), format_bytes(total_new), overall,
- )
- if skipped:
- msg += "\n\nSkipped (not a supported type): %d" % skipped
- if failed:
- msg += "\nFailed: %d" % failed
- show_dialog("T3D Export Complete", msg)
- else:
- msg = "No assets were exported.\n\n"
- if skipped:
- preview = "\n".join(" - " + n for n in skipped_names[:8])
- more = "" if len(skipped_names) <= 8 else "\n ...and %d more" % (len(skipped_names) - 8)
- msg += "Skipped (not a supported type): %d\n%s%s\n\n" % (skipped, preview, more)
- if failed:
- msg += "Failed: %d\n\n" % failed
- msg += "Supported: Blueprint, WidgetBlueprint, BehaviorTree, BlackboardData, EnvQuery, UserDefinedEnum/Struct, AnimBlueprint, Material, MaterialFunction, MaterialInstanceConstant."
- show_dialog("T3D Export - Nothing Done", msg)
- main()
Advertisement
Add Comment
Please, Sign In to add comment