# Critique (read-only): dump objects, triangle counts, n-gons, material base colours / roughness / metallic / emission import bpy, json, bmesh out = {"objects": {}, "materials": {}} for o in bpy.data.objects: if o.type != "MESH": continue me = o.data nt = sum(len(p.vertices) - 2 for p in me.polygons) ng = sum(1 for p in me.polygons if len(p.vertices) > 4) out["objects"][o.name] = dict(tris=nt, faces=len(me.polygons), ngons=ng, verts=len(me.vertices), mats=[s.material.name if s.material else None for s in o.material_slots], uv=[u.name for u in me.uv_layers], hidden=o.hide_render, dims=[round(d, 3) for d in o.dimensions], mods=[m.type for m in o.modifiers]) for m in bpy.data.materials: if not m.use_nodes: continue info = {} for n in m.node_tree.nodes: if n.type == "BSDF_PRINCIPLED": def val(name): i = n.inputs.get(name) if i is None: return None if i.is_linked: return "linked:" + i.links[0].from_node.type + ":" + i.links[0].from_node.name v = i.default_value try: return [round(x, 3) for x in v] except TypeError: return round(v, 3) for k in ("Base Color", "Metallic", "Roughness", "Emission Color", "Emission Strength", "Transmission Weight", "Coat Weight", "Alpha"): info[k] = val(k) if n.type == "TEX_IMAGE" and n.image: info.setdefault("images", []).append(n.image.name + " (" + n.image.filepath + ")") if n.type == "RGB": info.setdefault("rgb", []).append([round(x, 3) for x in n.outputs[0].default_value]) if n.type == "VALTORGB": info.setdefault("ramp", []).append([[round(e.position, 2)] + [round(x, 3) for x in e.color] for e in n.color_ramp.elements]) if n.type == "MIX" and n.data_type == "RGBA": info.setdefault("mixcols", []).append([[round(x, 3) for x in n.inputs[6].default_value], [round(x, 3) for x in n.inputs[7].default_value]]) out["materials"][m.name] = info print("JSONSTART" + json.dumps(out) + "JSONEND")