Всё, из чего собираются спрайты, и ничего собранного. Источник истины —
assets/manifests и assets/*/catalog.yaml; PNG, .sfa и contact sheet остаются
за бортом, потому что воспроизводятся командой (docs/12-assets.md).
assets/manifests что и как рендерить: окружение, твари, агенты, предметы
assets/agents паперdoll-риг восьми направлений, каталог снаряжения
assets/monsters пять морфологий, каждая своим ригом (docs/14-monsters.md)
assets/sources исходные развёртки и импорты
assets/art_masters мастер-развёртки, по которым сверяется стиль
tools/ сборка ассетов, покрытие, генераторы, blender-скрипты
generated/ заголовки с константами SF_ASSET_*: без них игра не
соберётся, поэтому они в репозитории, хотя и производные
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
66 lines
3.4 KiB
Python
66 lines
3.4 KiB
Python
"""Print Blender render-result/pass APIs without writing project assets."""
|
|
import bpy
|
|
|
|
bpy.ops.mesh.primitive_cube_add()
|
|
scene = bpy.context.scene
|
|
scene.view_layers[0].use_pass_z = True
|
|
scene.view_layers[0].use_pass_normal = True
|
|
print("SCENE_COMPOSITOR_ATTRS", [name for name in dir(scene) if "composit" in name.lower() or "node" in name.lower()])
|
|
scene.use_nodes = True
|
|
tree = scene.compositing_node_group
|
|
if tree is None:
|
|
tree = bpy.data.node_groups.new("SpriteForge compositor", "CompositorNodeTree")
|
|
scene.compositing_node_group = tree
|
|
print("COMPOSITOR_TREE", tree, "NODE_TYPES", [node.bl_idname for node in tree.nodes])
|
|
file_node = tree.nodes.new("CompositorNodeOutputFile")
|
|
print("FILE_NODE_ATTRS", [name for name in dir(file_node) if "file" in name.lower() or "slot" in name.lower() or "item" in name.lower()])
|
|
print("FILE_NODE_PROPS", [prop.identifier for prop in file_node.bl_rna.properties])
|
|
print("FILE_ITEMS_ATTRS", [name for name in dir(file_node.file_output_items) if not name.startswith("__")])
|
|
print("FILE_ITEMS_NEW_DOC", file_node.file_output_items.new.__doc__)
|
|
print("FILE_FORMATS", file_node.format.bl_rna.properties["file_format"].enum_items.keys())
|
|
render_node = tree.nodes.new("CompositorNodeRLayers")
|
|
scene.view_layers[0].update_render_passes()
|
|
print("RENDER_OUTPUTS", [socket.name for socket in render_node.outputs])
|
|
file_node.format.file_format = "OPEN_EXR_MULTILAYER"
|
|
file_node.directory = bpy.app.tempdir
|
|
file_node.file_name = "spriteforge-passes"
|
|
file_node.file_output_items.clear()
|
|
for socket_type, name in (("RGBA", "Image"), ("FLOAT", "Depth"),
|
|
("VECTOR", "Normal")):
|
|
file_node.file_output_items.new(socket_type, name)
|
|
tree.links.new(render_node.outputs[name], file_node.inputs[name])
|
|
print("OUTPUT_INPUTS", [(socket.name, socket.bl_idname) for socket in file_node.inputs])
|
|
print("OUTPUT_ITEM_PROPS", [[prop.identifier for prop in item.bl_rna.properties]
|
|
for item in file_node.file_output_items])
|
|
scene.render.engine = "BLENDER_EEVEE"
|
|
scene.render.resolution_x = 8
|
|
scene.render.resolution_y = 8
|
|
scene.render.resolution_percentage = 100
|
|
camera_data = bpy.data.cameras.new("camera")
|
|
camera = bpy.data.objects.new("camera", camera_data)
|
|
scene.collection.objects.link(camera)
|
|
camera.location = (3, -3, 2)
|
|
camera.rotation_euler = ((0, 0, 0))
|
|
camera.rotation_euler = ((0, 0, 0))
|
|
camera.rotation_euler = ((bpy.context.object.location-camera.location).to_track_quat("-Z", "Y").to_euler())
|
|
scene.camera = camera
|
|
scene.render.image_settings.file_format = "PNG"
|
|
scene.render.filepath = bpy.app.tempdir + "spriteforge-pass-api.exr"
|
|
bpy.ops.render.render(write_still=True)
|
|
result = bpy.data.images.get("Render Result")
|
|
print("RESULT_ATTRS", [name for name in dir(result) if "layer" in name.lower() or "pass" in name.lower() or "slot" in name.lower()])
|
|
print("RESULT_TYPE", type(result), "SIZE", tuple(result.size), "PIXELS", len(result.pixels))
|
|
print("VIEW_LAYER_ATTRS", [name for name in dir(scene.view_layers[0]) if "pass" in name.lower()])
|
|
try:
|
|
import OpenImageIO
|
|
print("OPENIMAGEIO", OpenImageIO.VERSION_STRING)
|
|
source = OpenImageIO.ImageInput.open(bpy.app.tempdir + "spriteforge-passes.exr")
|
|
index = 0
|
|
while source.seek_subimage(index, 0):
|
|
spec = source.spec()
|
|
print("SUBIMAGE", index, spec.width, spec.height, list(spec.channelnames))
|
|
index += 1
|
|
source.close()
|
|
except Exception as error:
|
|
print("OPENIMAGEIO_MISSING", repr(error))
|