"""Surfaces for the Mako, the Spearfish's and the Manta's method (models/tender-spearfish/build/surfaces_spearfish.py), run on Tender_Mako.blend after the build: blender -b Tender_Mako.blend --python build/surfaces_mako.py (generated by build/make_surfaces.py from this head + the Spearfish's helper functions and run, with this head's screen_uvs) * hard surfaces take tiling sets from the user's own Season 2 packs (already in the Unreal project), read in place from models/vessel-interior/textures: ScratchedMetal under the paints, StainedSteel for bare metal, FloorPanel_A on the cockpit deck, Glass_DirtRough as a smudge mask, T_Scratch_Norm for fine scratches. The seats take the CC0 leather scan the captain's quarters brought in (models/vessel-interior/source_textures/leather_white). Nothing is downloaded. * two maps baked from the model in Cycles per atlas: Masks (R occlusion, G edges, B crevices and seams) and BevelNormal (hard edges rounded 12 mm, the shell's panel seams as grooves). Exterior atlas 4096 px, cabin atlas 2048 px. * the colours follow the user's sheet: an orange paint under a clear coat on the shell, hood, collar, jaw and fins, a dark slate lower hull and side pods, mid grey big fins and ventral fin, light blue-tinted glass, graphite fittings, yellow marks on the docking clips. Worn as a working machine is: chips at exposed edges down to dark primer, grime in crevices, smudges in the clear coat; no rust. Two UV layers on every mesh (the order matters for Unreal, which builds its tangents from the first): UVMap (UV0) a unique unwrap in the part's square of its atlas (the baked maps); the screens map onto T_Mako_Screens. UV_Tile (UV1) metres, box-projected (the tiling sets). """ import bpy, bmesh, math, os, time, json import numpy as np from mathutils import Vector OUT = os.path.dirname(bpy.data.filepath) HERE = os.path.join(OUT, "build") TEX = os.path.join(OUT, "textures") os.makedirs(TEX, exist_ok=True) VI = os.path.normpath(os.path.join(OUT, "..", "vessel-interior")) PACK = os.path.join(VI, "textures", "ModSci_Engineer", "Textures") SCR = ("Base/ScratchedMetal/T_ScratchedMetal_Diff.png", "Base/ScratchedMetal/T_ScratchedMetal_Rough.png", "Base/ScratchedMetal/T_ScratchedMetal_Normal.png") STN = ("Base/StainedSteel/T_StainSteeled_Diff.png", "Base/StainedSteel/T_StainSteeled_Rough.png", None) FLOOR = ("FloorPanel_A/T_FloorPanel_A_diff.png", "FloorPanel_A/T_FloorPanel_A_Rough.png", "FloorPanel_A/T_FloorPanel_A_normals.png") LEATHER = tuple(os.path.join(VI, "source_textures", "leather_white", f) for f in ("leather_white_diff_2k.jpg", "leather_white_rough_2k.jpg", "leather_white_nor_gl_2k.jpg")) DIRT = "Base/Glass/T_Glass_DirtRough.png" SCRATCH_N = "Base/Glass/T_Scratch_Norm.png" REPORT = {} t0 = time.time() # a re-run replaces the maps of the run before: drop their images first, or the new ones come out named ".001" for _im in list(bpy.data.images): if _im.name.startswith("T_Mako_"): bpy.data.images.remove(_im) for _f in os.listdir(TEX): if _f.startswith("T_Mako_") and _f.count(".") > 1: os.remove(os.path.join(TEX, _f)) ATLASES = { "Exterior": dict(size=4096, rects={"SM_Mako_Hull": (0.0, 0.0, 0.86), "SM_Mako_Glass": (0.86, 0.0, 0.14), "SM_Mako_Hatch": (0.86, 0.14, 0.14), "SM_Mako_Arm_L": (0.86, 0.28, 0.07), "SM_Mako_Arm_R": (0.93, 0.28, 0.07), "SM_Mako_Rotor": (0.86, 0.35, 0.07)}), "Cabin": dict(size=2048, rects={"SM_Mako_Cabin": (0.0, 0.0, 1.0)}), } BEVEL_R, EDGE_R = 0.012, 0.020 SEAM_W0, SEAM_W1, SEAM_DEPTH = 0.0045, 0.0018, 0.0018 SURF = { "MAKO | Hull paint": dict(set=SCR, tint=(0.70, 0.205, 0.020), metal=0.0, tile=1.20, shade=(0.95, 1.04), rough=(0.34, 0.26), normal=0.04, dirt=0.02, dirt_tile=2.4, grime=0.22, wear=0.16, wear_col=(0.15, 0.155, 0.16), wear_rough=0.45, coat=1.0, coat_rough=0.05, coat_dirt=0.04), "MAKO | Lower hull": dict(set=(STN[0], SCR[1], SCR[2]), tint=(0.040, 0.045, 0.052), metal=0.0, tile=0.90, shade=(0.92, 1.08), rough=(0.50, 0.38), normal=0.12, dirt=0.05, dirt_tile=1.9, grime=0.15, wear=0.30, wear_col=(0.26, 0.27, 0.28), wear_rough=0.36, coat=0.35, coat_rough=0.10, coat_dirt=0.06), "MAKO | Graphite": dict(set=(STN[0], SCR[1], SCR[2]), tint=(0.028, 0.031, 0.036), metal=0.0, tile=0.70, shade=(0.92, 1.08), rough=(0.55, 0.42), normal=0.15, dirt=0.06, dirt_tile=1.9, grime=0.15, wear=0.40, wear_col=(0.30, 0.31, 0.32), wear_rough=0.36), "MAKO | Fin grey": dict(set=SCR, tint=(0.19, 0.205, 0.225), metal=0.0, tile=1.00, shade=(0.94, 1.05), rough=(0.40, 0.30), normal=0.06, dirt=0.03, dirt_tile=2.0, grime=0.18, wear=0.22, wear_col=(0.42, 0.43, 0.44), wear_rough=0.36, coat=0.6, coat_rough=0.08, coat_dirt=0.05), "MAKO | Black gloss": dict(set=(None, SCR[1], None), tint=(0.006, 0.007, 0.009), metal=0.0, tile=0.30, shade=(1.0, 1.0), rough=(0.12, 0.05), normal=0.0, dirt=0.05, dirt_tile=0.5, grime=0.0, wear=0.0, coat=1.0, coat_rough=0.03, coat_dirt=0.05), "MAKO | Steel": dict(set=STN, tint=(0.60, 0.61, 0.63), metal=1.0, tile=0.90, shade=(0.93, 1.05), rough=(0.24, 0.32), normal=0.50, normal_map=SCRATCH_N, dirt=0.05, dirt_tile=0.9, grime=0.20, wear=0.0), "MAKO | Rubber": dict(set=(None, SCR[1], None), tint=(0.017, 0.017, 0.018), metal=0.0, tile=0.40, shade=(1.0, 1.0), rough=(0.86, 0.62), normal=0.0, dirt=0.08, dirt_tile=1.2, grime=0.10, wear=0.0, dust=0.03), "MAKO | Yellow marking": dict(set=SCR, tint=(0.80, 0.50, 0.025), metal=0.0, tile=0.60, shade=(0.94, 1.04), rough=(0.40, 0.32), normal=0.06, dirt=0.06, dirt_tile=1.4, grime=0.22, wear=0.50, wear_col=(0.50, 0.51, 0.52), wear_rough=0.36, coat=0.6, coat_rough=0.06, coat_dirt=0.1), "MAKO | Cabin panel": dict(set=SCR, tint=(0.20, 0.21, 0.225), metal=0.0, tile=0.90, shade=(0.90, 1.05), rough=(0.62, 0.42), normal=0.30, dirt=0.04, dirt_tile=2.0, grime=0.25, wear=0.15, wear_col=(0.45, 0.46, 0.47), wear_rough=0.40), "MAKO | Cabin floor": dict(set=FLOOR, tint=(0.18, 0.18, 0.19), metal=0.6, tile=1.00, shade=(0.80, 1.10), rough=(0.55, 0.35), normal=0.80, dirt=0.07, dirt_tile=1.5, grime=0.30, wear=0.0), "MAKO | Seat": dict(set=LEATHER, tint=(0.045, 0.048, 0.055), metal=0.0, tile=0.55, shade=(0.80, 1.15), rough=(0.62, 0.40), normal=0.6, dirt=0.04, dirt_tile=1.2, grime=0.25, wear=0.0, abs_paths=True), } GLOW = {"MAKO | Lamp light": ((1.0, 0.92, 0.80), 30.0), "MAKO | Cabin light": ((0.72, 0.86, 1.0), 18.0)} GLASSES = {"MAKO | Glass": (0.66, 0.85, 0.95), "MAKO | Lamp lens": (0.98, 0.99, 1.0)} # light blue, as the sheet (passed twice) SCREEN_INK = (0.35, 0.80, 1.0) # blue-white instruments, amber for alerts SCREEN_ALERT = (1.0, 0.55, 0.10) def screen_uvs(o): """every Screen plate: its display face (the biggest face of the plate, on the side toward the cockpit's middle) gets a third of the sheet (the console's three in order across, the small ones by turn); the plate's other faces a dark corner""" me = o.data names = [m.name for m in me.materials] si = names.index("MAKO | Screen") uvl = me.uv_layers["UVMap"].data bm = bmesh.new(); bm.from_mesh(me); bm.faces.ensure_lookup_table() todo = {f.index for f in bm.faces if f.material_index == si} islands = [] while todo: seed = todo.pop(); isl = [seed]; stack = [seed] while stack: f = bm.faces[stack.pop()] for e in f.edges: for g in e.link_faces: if g.index in todo: todo.discard(g.index); isl.append(g.index); stack.append(g.index) islands.append(isl) bm.free() def centre(ids): return sum((me.polygons[i].center for i in ids), Vector()) / len(ids) islands.sort(key=lambda isl: (round(centre(isl).y, 1), centre(isl).x)) n_disp = 0 for k, isl in enumerate(islands): c = centre(isl) # which way the display looks: the console's three toward the seat, the rear bay's rack lights outboard, the rest up D = Vector((0, 0.616, 0.788)) if c.y < -0.6 else (Vector((math.copysign(1, c.x), 0, 0)) if c.y > 0.5 else Vector((0, 0, 1))) amax = max(me.polygons[i].area for i in isl) best = max(isl, key=lambda i: me.polygons[i].normal.dot(D) + (0.0 if me.polygons[i].area > 0.5 * amax else -2.0)) third = k % 3 for i in isl: p = me.polygons[i] if i == best: nrm = p.normal right = Vector((1, 0, 0)) if abs(nrm.x) < 0.8 else Vector((0, 1, 0)) right = (right - right.dot(nrm) * nrm).normalized() up = nrm.cross(right).normalized() cos_ = [me.vertices[me.loops[li].vertex_index].co for li in range(p.loop_start, p.loop_start + p.loop_total)] us = [q.dot(right) for q in cos_]; vs = [q.dot(up) for q in cos_] for li, q in zip(range(p.loop_start, p.loop_start + p.loop_total), cos_): uu = (third + 0.04 + 0.92 * (q.dot(right) - min(us)) / max(1e-6, max(us) - min(us))) / 3.0 vv = 0.04 + 0.92 * (q.dot(up) - min(vs)) / max(1e-6, max(vs) - min(vs)) uvl[li].uv = (uu, vv) n_disp += 1 else: for li in range(p.loop_start, p.loop_start + p.loop_total): uvl[li].uv = (0.004, 0.004) return n_disp