""" The materials of the station's own geometry: by short names, and the few that are the station's own. The outpost's armour is in the Sci-Fi Lite pack's painted metals (the pack the relay dish comes from): white, black and grey, projected from three sides in space, so they need no unwrapping. The pack has no orange and no dark grey, and its one glowing material is a pale blue-white. Those are made as INSTANCES OF THE PACK'S OWN MATERIALS with one value changed, under /Game/Anchorage/Materials: nothing in the pack is touched, and nothing new has to be painted. register(kit) makes them known to the kit, so that Blender shows them (built like their parents) get(kit, key) the Blender material for a short name; the station's own carry what Unreal needs to make them: ["ue_parent"] and ["ue_overrides"] (build/export_layout.py writes them into the layout, build/ue_spawn_layout.py makes the instances) """ import copy import json GAME = "/Game/StarterBundle/" LITE_MI = "/Game/SciFI_Lite_Assets/Materials/MaterialInstances/" OWN = "/Game/Anchorage/Materials/" MAT = { "white": LITE_MI + "CleanMaterials/MI_WhiteMetal_01", "black": LITE_MI + "CleanMaterials/MI_BlackMetal_01", "grey": LITE_MI + "CleanMaterials/MI_GreyMetal_01", "rubber": LITE_MI + "CleanMaterials/MI_GreyRubber_01", "light": LITE_MI + "MI_Emissive_01", "orange": OWN + "MI_ANCH_OrangeMetal", "hull": OWN + "MI_ANCH_HullMetal", "cyan": OWN + "MI_ANCH_LightCyan", "red": OWN + "MI_ANCH_LightRed", "amber": OWN + "MI_ANCH_LightAmber", "deck": GAME + "ModularSciFiMats/Materials/Instances/MI_Floor_E_Scr", "floor": GAME + "ModularSciFiMats/Materials/Instances/MI_Floor_A_Scr2", } # path: the pack material it is an instance of, and the values that differ (Unreal's parameter names, linear colour) OWN_MATERIALS = { MAT["orange"]: {"parent": MAT["white"], "vectors": {"Main Material Color": [1.0, 0.27, 0.02, 1.0]}, "note": "the pack's white painted metal, orange"}, MAT["hull"]: {"parent": MAT["black"], "vectors": {"Main Material Color": [0.075, 0.08, 0.09, 1.0]}, "note": "the pack's black painted metal, a dark blue-grey: the structure behind the armour"}, MAT["cyan"]: {"parent": MAT["light"], "vectors": {"Color": [0.30, 0.80, 1.0, 1.0]}, "note": "the pack's glowing material, cyan: markers and pad lights"}, MAT["red"]: {"parent": MAT["light"], "vectors": {"Color": [1.0, 0.10, 0.04, 1.0]}, "note": "the pack's glowing material, red: obstruction lights on masts and aerials"}, MAT["amber"]: {"parent": MAT["light"], "vectors": {"Color": [1.0, 0.55, 0.12, 1.0]}, "note": "the pack's glowing material, amber: lamps over doors"}, } # Scanned surfaces from the user's Fab library (the vault cache), copied to textures/scans by build/make_scans.py. # They are laid on by the meshes' own UVs (a box projection in metres, hs.py), `size` metres to one repeat. SCAN_MATERIALS = { OWN + "M_ANCH_ConcreteDark": {"scan": "concrete_dark", "size": 2.0, "note": "scanned dark precast concrete panels: the structure behind the armour"}, OWN + "M_ANCH_ConcreteBase": {"scan": "concrete_base", "size": 1.6, "note": "scanned grey concrete with run-off stains: plinths and footings"}, OWN + "M_ANCH_ConcreteFloor": {"scan": "concrete_floor", "size": 2.0, "note": "scanned floated concrete: pads, decks, walkway slabs"}, } MAT["darkmetal"] = MAT["hull"] # the dark metal instance: machinery, frames MAT["hull"] = OWN + "M_ANCH_ConcreteDark" # the walls behind the armour: concrete MAT["concrete"] = OWN + "M_ANCH_ConcreteBase" MAT["slab"] = OWN + "M_ANCH_ConcreteFloor" UV_TILE = 4.4 # the meshes' UVs: one unit is this many metres (hs.py, exterior.py) def _scan_material(kit, path): """A Blender material from a scanned set: colour darkened by its occlusion map, roughness, normal.""" import os import bpy d = SCAN_MATERIALS[path] tex = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "textures", "scans", d["scan"]) mat = bpy.data.materials.new("ANCH_" + path.rsplit("/", 1)[-1]) mat["ue_material"] = path mat["ue_scan"] = d["scan"] mat["ue_scan_size"] = d["size"] mat["ue_scan_files"] = json.dumps({m: "textures/scans/%s/%s.jpg" % (d["scan"], m) for m in ("BaseColor", "Normal", "Roughness", "AO") if os.path.isfile(os.path.join(tex, m + ".jpg"))}) mat["ue_note"] = d["note"] mat.use_nodes = True nt = mat.node_tree nt.nodes.clear() out = nt.nodes.new("ShaderNodeOutputMaterial") out.location = (400, 0) bsdf = nt.nodes.new("ShaderNodeBsdfPrincipled") bsdf.location = (60, 0) nt.links.new(bsdf.outputs["BSDF"], out.inputs["Surface"]) tc = nt.nodes.new("ShaderNodeTexCoord") tc.location = (-1300, 0) mp = nt.nodes.new("ShaderNodeMapping") mp.location = (-1100, 0) k = UV_TILE / float(d["size"]) mp.inputs["Scale"].default_value = (k, k, 1.0) nt.links.new(tc.outputs["UV"], mp.inputs["Vector"]) def image(name, y, colour): p = os.path.join(tex, name + ".jpg") if not os.path.isfile(p): return None node = nt.nodes.new("ShaderNodeTexImage") node.location = (-860, y) node.image = bpy.data.images.load(p, check_existing=True) node.image.colorspace_settings.name = "sRGB" if colour else "Non-Color" nt.links.new(mp.outputs["Vector"], node.inputs["Vector"]) return node base, ao = image("BaseColor", 320, True), image("AO", 40, False) if base is not None: src = base.outputs["Color"] if ao is not None: mix = nt.nodes.new("ShaderNodeMix") mix.data_type, mix.blend_type = "RGBA", "MULTIPLY" mix.location = (-420, 240) mix.inputs[0].default_value = 0.75 nt.links.new(src, mix.inputs[6]) nt.links.new(ao.outputs["Color"], mix.inputs[7]) src = mix.outputs[2] nt.links.new(src, bsdf.inputs["Base Color"]) rgh = image("Roughness", -240, False) if rgh is not None: nt.links.new(rgh.outputs["Color"], bsdf.inputs["Roughness"]) nrm = image("Normal", -520, False) if nrm is not None: nm = nt.nodes.new("ShaderNodeNormalMap") nm.location = (-420, -520) nt.links.new(nrm.outputs["Color"], nm.inputs["Color"]) nt.links.new(nm.outputs["Normal"], bsdf.inputs["Normal"]) mat.diffuse_color = (0.3, 0.3, 0.3, 1.0) return mat def register(kit): """The station's own instances as the kit knows the pack's: the parent's record with the changed values.""" for path, d in OWN_MATERIALS.items(): if path in kit.materials: continue parent = kit.materials.get(d["parent"]) if parent is None: kit.log("own material %s: its parent %s is not in the kit export" % (path, d["parent"])) continue rec = copy.deepcopy(parent) rec["parent"] = d["parent"] rec["type"] = "MaterialInstanceConstant" params = rec.setdefault("params", {}) for group in ("vectors", "scalars", "switches", "textures"): if d.get(group): params.setdefault(group, {}).update(d[group]) kit.materials[path] = rec EXTRA = {} # short name -> a Blender material made elsewhere (Poly Haven surfaces: build/ph.py) def get(kit, key): if key in EXTRA: return EXTRA[key] path = MAT.get(key, key) register(kit) if path in SCAN_MATERIALS: if path not in kit._mat: kit._mat[path] = _scan_material(kit, path) return kit._mat[path] mat = kit.material(path) d = OWN_MATERIALS.get(path) if d is not None: mat["ue_parent"] = d["parent"] mat["ue_overrides"] = json.dumps({g: d[g] for g in ("vectors", "scalars", "switches") if d.get(g)}) mat["ue_note"] = d.get("note", "") return mat