Files
animation/characters/work/lena_leafbikini/06_open_in_blender.py
T

68 lines
3.3 KiB
Python
Raw Normal View History

# lena_leafbikini lane, stage 06: open a lane mesh in the Blender GUI, framed and shaded.
#
# blender --python 06_open_in_blender.py -- <mesh.glb>
#
# Runs inside a GUI session, NOT --background, and deliberately NOT --factory-startup: the
# TinqsBlenderBridge autostarts from scripts/startup (tools/install_blender_bridge.ps1), and
# factory startup would skip it, leaving the session undriveable from the terminal afterwards.
#
# TWO THINGS THIS SCRIPT LEARNED THE HARD WAY, both consequences of `--python` running BEFORE
# Blender's window exists:
# 1. `bpy.context.screen` is None at this point, so any viewport setup here dies with
# AttributeError. All of it is deferred onto an app timer, which first fires once the UI
# is up and can therefore see the 3D View.
# 2. `bpy.ops.wm.read_homefile()` kills the bridge — the startup module has already
# registered its socket server by now, and reloading the file takes it down with no
# autostart to bring it back. The default cube/light/camera are removed by hand instead.
#
# Material Preview, not Solid: the point of looking at this file is the boundary between the
# holes and her skin, and in Solid shading the baked leaf contact shadows in the albedo are
# invisible — which is half of what makes the rim read the way it does.
import bpy, sys, os
GLB = os.path.abspath(sys.argv[sys.argv.index("--") + 1:][0])
for o in list(bpy.data.objects):
if o.name in {"Cube", "Light", "Camera"}:
bpy.data.objects.remove(o, do_unlink=True)
bpy.ops.import_scene.gltf(filepath=GLB)
body = max([o for o in bpy.data.objects if o.type == 'MESH'], key=lambda o: len(o.data.vertices))
bpy.context.view_layer.objects.active = body
for o in bpy.data.objects:
o.select_set(o is body)
print(f"[open] {os.path.basename(GLB)}: {len(body.data.vertices)}v {len(body.data.polygons)}f",
flush=True)
def setup_viewport():
"""Fires once, after the window exists."""
screen = getattr(bpy.context, "screen", None)
if screen is None:
return 0.25 # UI not up yet — ask the timer to call again
for area in screen.areas:
if area.type != 'VIEW_3D':
continue
sp = area.spaces.active
sp.shading.type = 'MATERIAL'
sp.shading.use_scene_lights = False
sp.shading.use_scene_world = False
sp.overlay.show_floor = False
sp.overlay.show_axis_x = sp.overlay.show_axis_y = False
sp.overlay.show_relationship_lines = False
sp.clip_start = 0.001
sp.clip_end = 100.0
region = next((r for r in area.regions if r.type == 'WINDOW'), None)
if region:
with bpy.context.temp_override(area=area, region=region, space_data=sp):
# BACK, not FRONT. These lane GLBs are exported with export_yup=True and come
# back in facing +Y, so Blender's FRONT view (looking from -Y) puts you behind
# her — which is the wrong side for judging a cut that is mostly on her front.
bpy.ops.view3d.view_axis(type='BACK')
bpy.ops.view3d.view_selected()
print("[open] viewport ready — Material Preview, framed on her front", flush=True)
return None # done; unregister
bpy.app.timers.register(setup_viewport, first_interval=0.5)