106 lines
5.2 KiB
Python
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)
|