Files
2026-10-31 17:03:41 +01:00

106 lines
5.2 KiB
Python

"""Synchronize basic glTF PBR properties preserved by UE mesh reimports."""
import json
import base64
import re
import struct
import unreal
from .paths import PROJECT
def link_textures(source, mesh, destination):
if source.suffix.lower() == ".glb":
data = source.read_bytes()
size, kind = struct.unpack_from("<II", data, 12)
if kind != 0x4E4F534A:
raise ValueError("GLB nie zawiera poczatkowego chunka JSON.")
document = json.loads(data[20:20 + size])
binary = data[28 + size:]
else:
document = json.loads(source.read_text(encoding="utf-8"))
binary = b""
def sanitized(value):
return re.sub(r"[^A-Za-z0-9_]", "_", value)
textures = {}
def load_texture(info, parameter):
index = document["textures"][info["index"]]["source"]
role = "Normal" if parameter == "NormalTexture" else (
"Color" if parameter in {"BaseColorTexture", "EmissiveTexture"} else "Linear")
key = (index, role)
if key in textures:
return textures[key]
image = document["images"][index]
name = f"T_{index:03d}_{sanitized(image.get('name', 'Image'))}_{role}"
uri = image.get("uri", "")
if "bufferView" in image:
view = document["bufferViews"][image["bufferView"]]
offset = view.get("byteOffset", 0)
payload = binary[offset:offset + view["byteLength"]]
elif uri.startswith("data:"):
payload = base64.b64decode(uri.split(",", 1)[1])
else:
from urllib.parse import unquote
payload = (source.parent / unquote(uri)).read_bytes()
extension = ".png" if payload.startswith(b"\x89PNG") else ".jpg" if payload.startswith(b"\xff\xd8") else None
if extension is None:
raise ValueError("Tekstury warsztatowe musza byc zapisane jako PNG lub JPEG.")
file = PROJECT / "build/GltfTextures" / source.stem / (name + extension)
file.parent.mkdir(parents=True, exist_ok=True)
file.write_bytes(payload)
task = unreal.AssetImportTask()
task.filename = str(file)
task.destination_path = destination + "/WorkshopTextures"
task.destination_name = name
task.automated = True
task.replace_existing = True
task.save = True
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
imported = [obj for obj in task.get_objects() if isinstance(obj, unreal.Texture2D)]
if len(imported) != 1:
raise RuntimeError("Nie mozna zaimportowac tekstury " + name)
texture = imported[0]
texture.set_editor_property("srgb", role == "Color")
texture.set_editor_property("flip_green_channel", role == "Normal")
texture.set_editor_property("compression_settings", unreal.TextureCompressionSettings.TC_NORMALMAP
if role == "Normal" else unreal.TextureCompressionSettings.TC_DEFAULT
if role == "Color" else unreal.TextureCompressionSettings.TC_MASKS)
unreal.EditorAssetLibrary.save_loaded_asset(texture)
textures[key] = texture
return texture
materials = {sanitized(item.get("name", "")): item for item in document.get("materials", [])}
lib = unreal.MaterialEditingLibrary
for slot in mesh.static_materials:
material = slot.material_interface
if material is None or material.get_name() not in materials:
continue
spec = materials[material.get_name()]
pbr = spec.get("pbrMetallicRoughness", {})
lib.set_material_instance_vector_parameter_value(
material, "BaseColorFactor", unreal.LinearColor(*pbr.get("baseColorFactor", [1, 1, 1, 1])))
lib.set_material_instance_vector_parameter_value(
material, "EmissiveFactor", unreal.LinearColor(*spec.get("emissiveFactor", [0, 0, 0]), 1))
lib.set_material_instance_scalar_parameter_value(material, "MetallicFactor", pbr.get("metallicFactor", 1))
lib.set_material_instance_scalar_parameter_value(material, "RoughnessFactor", pbr.get("roughnessFactor", 1))
maps = {"BaseColorTexture": pbr.get("baseColorTexture"),
"MetallicRoughnessTexture": pbr.get("metallicRoughnessTexture"),
"NormalTexture": spec.get("normalTexture"),
"OcclusionTexture": spec.get("occlusionTexture"),
"EmissiveTexture": spec.get("emissiveTexture")}
for parameter, info in maps.items():
lib.set_material_instance_static_switch_parameter_value(material, "bHas" + parameter, info is not None)
lib.update_material_instance(material)
for parameter, info in maps.items():
if info is None:
continue
texture = load_texture(info, parameter)
# UE 5.7's setter returns False even when it succeeds; verify by reading.
lib.set_material_instance_texture_parameter_value(material, parameter, texture)
if lib.get_material_instance_texture_parameter_value(material, parameter) != texture:
raise RuntimeError(f"Nie mozna przypisac {parameter} w {material.get_name()}.")
lib.update_material_instance(material)
unreal.EditorAssetLibrary.save_loaded_asset(material)