exec(open(r"D:\HADAL - The Descent\models\dnr-cnc\build\lib2.py").read()) root = bpy.data.objects["DNR_CNC_Root"] C_STATIC = get_coll("CNC_Static"); C_COL = get_coll("CNC_Collision") TEX = os.path.join(OUT, "textures") # ------------------------------------------------------------------ image materials def img_mat(name, fname, emit_str, rough=0.45, alpha=True): m = bpy.data.materials.get(name) or bpy.data.materials.new(name) m.use_nodes = True nt = m.node_tree b = nt.nodes.get("Principled BSDF") for n in list(nt.nodes): if n.type == 'TEX_IMAGE': nt.nodes.remove(n) tex = nt.nodes.new('ShaderNodeTexImage') img = bpy.data.images.get(fname) or bpy.data.images.load(os.path.join(TEX, fname)) img.colorspace_settings.name = 'sRGB' img.alpha_mode = 'STRAIGHT' tex.image = img tex.location = (-400, 200) nt.links.new(tex.outputs['Color'], b.inputs['Emission Color']) b.inputs['Emission Strength'].default_value = emit_str b.inputs['Roughness'].default_value = rough if alpha: nt.links.new(tex.outputs['Color'], b.inputs['Base Color']) nt.links.new(tex.outputs['Alpha'], b.inputs['Alpha']) try: m.surface_render_method = 'DITHERED' except Exception: pass else: b.inputs['Base Color'].default_value = (0.0, 0.0, 0.0, 1.0) return m m_dec = img_mat("Decals_Atlas", "T_VMC_Decals_BaseColor.png", 0.25) m_hud = img_mat("Screen_HUD", "T_VMC_PendantHUD_Emissive.png", 2.2, rough=0.15, alpha=False) sc_obj = bpy.data.objects["SM_DNR_PendantScreen"] sc_obj.data.materials.clear(); sc_obj.data.materials.append(m_hud) # ------------------------------------------------------------------ decal quads (own object, hand-set UVs from the atlas) AT = 2048.0 RECT = {"chev": (0, 0, 512, 2048), "hex": (512, 0, 320, 1024), "caution": (832, 0, 1024, 384), "emblem": (832, 400, 384, 384), "hazard": (1232, 400, 256, 256), "elec": (1504, 400, 256, 256), "note": (1776, 400, 256, 256)} bmd = bmesh.new() uvl = bmd.loops.layers.uv.new("UVMap") def decal(key, p0, p1, p2, p3): x, y, w, h = RECT[key] u0, u1 = (x + 4) / AT, (x + w - 4) / AT v0, v1 = 1 - (y + h - 4) / AT, 1 - (y + 4) / AT f = bmd.faces.new([bmd.verts.new(p) for p in (p0, p1, p2, p3)]) for l, uv in zip(f.loops, ((u0, v0), (u1, v0), (u1, v1), (u0, v1))): l[uvl].uv = uv def front_quad(key, cx, y, cz, w, h): decal(key, (cx - w / 2, y, cz - h / 2), (cx + w / 2, y, cz - h / 2), (cx + w / 2, y, cz + h / 2), (cx - w / 2, y, cz + h / 2)) YF = -1.402 front_quad("chev", -1.395, YF, 1.57, 0.25, 1.16) # vertical chevron stack, front-left panel front_quad("hex", -1.585, YF, 1.215, 0.12, 0.384) # hexagon column front_quad("caution", -1.425, YF, 0.64, 0.43, 0.161) # caution band above the underglow front_quad("note", 1.20, YF, 1.80, 0.07, 0.07) # small tag # emblem on the sloped head plate th = math.atan2(0.22, 0.53) nrm = Vector((0, -math.cos(th), math.sin(th))) up = Vector((0, 0.22, 0.53)).normalized() P0 = Vector((0.10, -0.95, 2.30)) + 0.52 * Vector((0, 0.22, 0.53)) + nrm * (0.006 + 0.0075) hw_ = 0.10 decal("emblem", tuple(P0 - Vector((hw_, 0, 0)) - up * hw_), tuple(P0 + Vector((hw_, 0, 0)) - up * hw_), tuple(P0 + Vector((hw_, 0, 0)) + up * hw_), tuple(P0 - Vector((hw_, 0, 0)) + up * hw_)) # chip conveyor discharge box (front face at y = -0.83) and electrical box (y = -0.96) front_quad("emblem", -3.00, -0.832, 1.145, 0.20, 0.20) front_quad("hazard", -2.35, -0.832, 1.145, 0.12, 0.12) front_quad("hazard", -2.265, -0.962, 0.60, 0.10, 0.10) front_quad("elec", -2.265, -0.962, 0.52, 0.08, 0.08) bmesh.ops.recalc_face_normals(bmd, faces=bmd.faces[:]) if False else None me = bpy.data.meshes.new("SM_DNR_Decals") bmd.to_mesh(me); bmd.free() me.materials.append(m_dec) for p in me.polygons: p.use_smooth = False o_dec = bpy.data.objects.new("SM_DNR_Decals", me) C_STATIC.objects.link(o_dec) o_dec.parent = root o_dec.show_wire = False # ------------------------------------------------------------------ UV unwrap (smart project, per object) for everything without hand UVs SKIP = {"SM_DNR_Decals", "SM_DNR_PendantScreen", "SM_DNR_CoolantHose", "SM_DNR_PendantArm"} unwrapped, failed = [], [] view = bpy.context.view_layer for ob in list(bpy.data.objects): if ob.type != 'MESH' or not ob.name.startswith("SM_DNR_") or ob.name in SKIP: continue try: for o2 in view.objects: o2.select_set(False) ob.select_set(True) view.objects.active = ob if ob.data.uv_layers.active is None: ob.data.uv_layers.new(name="UVMap") bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='SELECT') bpy.ops.uv.smart_project(angle_limit=math.radians(66), island_margin=0.003, area_weight=0.0, correct_aspect=True, scale_to_bounds=False) bpy.ops.object.mode_set(mode='OBJECT') unwrapped.append(ob.name) except Exception as ex: try: bpy.ops.object.mode_set(mode='OBJECT') except Exception: pass failed.append((ob.name, str(ex)[:120])) # ------------------------------------------------------------------ collision hulls (UE naming: UCX__NN, convex boxes) for o in list(C_COL.objects): bpy.data.objects.remove(o) def ucx(name, x0, x1, y0, y1, z0, z1): bm = cube_bm((x1 - x0, y1 - y0, z1 - z0), ((x0 + x1) / 2, (y0 + y1) / 2, (z0 + z1) / 2)) m = bpy.data.meshes.new(name) bm.to_mesh(m); bm.free() o = bpy.data.objects.new(name, m) C_COL.objects.link(o) o.parent = root o.display_type = 'WIRE' o.hide_render = True return o ucx("UCX_SM_DNR_Body_00", -1.75, 1.75, -1.40, 1.40, 0.0, 2.30) ucx("UCX_SM_DNR_Body_01", -0.23, 0.43, -0.95, -0.05, 2.30, 2.83) ucx("UCX_SM_DNR_ChipConveyor_00", -3.16, -2.16, -0.83, -0.27, 0.93, 1.36) ucx("UCX_SM_DNR_ChipConveyor_01", -2.44, -1.75, -0.81, -0.29, 0.12, 0.93) ucx("UCX_SM_DNR_ChipConveyor_02", -2.45, -2.08, -0.96, -0.78, 0.46, 0.88) ucx("UCX_SM_DNR_PendantArm_00", 2.06, 2.14, -1.84, -1.76, 0.15, 0.93) # ------------------------------------------------------------------ report tot = 0; rows = [] for ob in bpy.data.objects: if ob.type == 'MESH' and ob.name.startswith("SM_DNR_"): a = audit(ob); tot += a['tris_total'] rows.append((ob.name, a['tris_total'], a['quad'], a['tri'], a['ngon'], a['uv'])) result = {"unwrapped": len(unwrapped), "failed": failed, "total_tris": tot, "rows": rows}