Reproduzierbarer Export aus den Blender-Quellen der Godot-Referenz nach FBX, geprüft auf beiden Seiten; sechs repräsentative Assets vollständig in Unity verifiziert (normale Einheit g01, Einheit mit Skelett Funkenlanze, Turm Grenzwacht, Ancient, Schlundkrone, Vegetation/Kartenrequisiten), dazu Bastion, Kernwacht und Bossgrotte für die Szene. - tools/blender: Manifest mit Exportregeln je Asset, Exportskript mit Prüfungen (Einheiten, Maßstab, Achsen, Vorderseite, Mesh-Namen, Dreiecke, Materialslots, Normalen, Tangenten, Vertexfarben, Maße, Root Bone, Bone-Namen, Clips, Blend Shapes) gegen die Manifeste der Godot-GLBs, FBX-Rundreise, byteidentische Ausgabe (feste Uhrzeit, PYTHONHASHSEED). - Regeln wie beim GLB-Export: HIGH-RES-Modifikatoren weglassen, logische Namen, Skin-Teile zusammenfügen; 180°-Drehung, damit die Vorderseite nach Unitys Achsen-Bake auf +Z liegt (Unity = (-x, y, z) der Godot-Koordinaten). - Unity: Importregeln (Postprocessor), Verifier gegen das Blender-Protokoll, Vorschaubilder; URP-Asset, Linear-Farbraum, URP-Settings nach Assets/Luneraria/Settings/Rendering. - Ergebnis: 176/176 Blender-Prüfungen, 128/128 Unity-Prüfungen, Dreiecke gleich den Godot-GLBs, Maße auf 0,05 mm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
33 lines
2.4 KiB
Python
33 lines
2.4 KiB
Python
"""Prints a JSON summary of a .blend file (objects, meshes, materials, armatures, actions).
|
|
Usage: blender --background <file.blend> --python tools/blender/inspect_blend.py -- <out.json>"""
|
|
import bpy, json, sys
|
|
out = sys.argv[sys.argv.index('--') + 1] if '--' in sys.argv else None
|
|
scene = bpy.context.scene
|
|
info = {'file': bpy.data.filepath, 'unit_system': scene.unit_settings.system, 'scale_length': scene.unit_settings.scale_length,
|
|
'fps': scene.render.fps, 'objects': [], 'actions': []}
|
|
for obj in bpy.data.objects:
|
|
entry = {'name': obj.name, 'type': obj.type, 'parent': obj.parent.name if obj.parent else None,
|
|
'location': [round(v, 4) for v in obj.location], 'rotation': [round(v, 4) for v in obj.rotation_euler],
|
|
'scale': [round(v, 4) for v in obj.scale], 'in_scene': obj.name in scene.objects,
|
|
'hide_render': obj.hide_render}
|
|
if obj.type == 'MESH':
|
|
me = obj.data
|
|
entry.update({'mesh': me.name, 'verts': len(me.vertices), 'polys': len(me.polygons),
|
|
'ngons': sum(1 for p in me.polygons if len(p.vertices) > 4),
|
|
'materials': [m.name if m else None for m in me.materials], 'uv_layers': [uv.name for uv in me.uv_layers],
|
|
'color_attributes': [(c.name, c.domain, c.data_type) for c in me.color_attributes],
|
|
'custom_normals': me.has_custom_normals, 'shape_keys': [k.name for k in me.shape_keys.key_blocks] if me.shape_keys else [],
|
|
'modifiers': [(m.type, m.name) for m in obj.modifiers], 'vertex_groups': len(obj.vertex_groups)})
|
|
if obj.type == 'ARMATURE':
|
|
arm = obj.data
|
|
entry.update({'bones': len(arm.bones), 'roots': [b.name for b in arm.bones if b.parent is None],
|
|
'bone_names': [b.name for b in arm.bones][:80], 'action': obj.animation_data.action.name if obj.animation_data and obj.animation_data.action else None,
|
|
'nla': [(t.name, [s.action.name for s in t.strips if s.action]) for t in obj.animation_data.nla_tracks] if obj.animation_data else []})
|
|
info['objects'].append(entry)
|
|
for act in bpy.data.actions:
|
|
info['actions'].append({'name': act.name, 'range': [round(v, 2) for v in act.frame_range], 'users': act.users, 'fake_user': act.use_fake_user})
|
|
text = json.dumps(info, indent=1)
|
|
if out:
|
|
open(out, 'w').write(text)
|
|
else:
|
|
print(text)
|