68 lines
3.3 KiB
Python
68 lines
3.3 KiB
Python
|
|
# 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)
|