"""Render actual exported pilot GLBs for offline asset review, not a browser screenshot.""" import bpy, json, math from pathlib import Path from mathutils import Vector BASE = Path('.scratch/cockpit-shell-panel-layout/verify/artifacts/t35/negative-estate-pilot').resolve() OUT = BASE/'offline-renders-v3' if OUT.exists(): raise RuntimeError('Use a fresh render directory') OUT.mkdir() plan = json.loads((BASE/'input-v2/seated-plan.json').read_text()) manifest = json.loads((BASE/'export-v3/manifest.json').read_text()) bpy.ops.wm.read_factory_settings(use_empty=True) for cell in manifest['cells']: bpy.ops.import_scene.gltf(filepath=str(BASE/'export-v3'/cell['lod0']['uri'])) scene = bpy.context.scene scene.render.engine = 'CYCLES';scene.cycles.samples=16 scene.render.resolution_x=512;scene.render.resolution_y=512;scene.render.resolution_percentage=100 scene.render.image_settings.file_format='PNG' scene.world = bpy.data.worlds.new('Review world');scene.world.use_nodes=True scene.world.node_tree.nodes['Background'].inputs[0].default_value=(.65,.68,.72,1) scene.world.node_tree.nodes['Background'].inputs[1].default_value=.6 scene.view_settings.view_transform='AgX' sun_data=bpy.data.lights.new('Review sun','SUN');sun_data.energy=2.5 sun=bpy.data.objects.new('Review sun',sun_data);scene.collection.objects.link(sun);sun.rotation_euler=(math.radians(25),math.radians(-20),math.radians(-30)) cam_data=bpy.data.cameras.new('Review camera');cam=bpy.data.objects.new('Review camera',cam_data);scene.collection.objects.link(cam);scene.camera=cam cam_data.type='ORTHO';cam_data.lens=50 meshes = [o for o in scene.objects if o.type=='MESH'] records=[] for building in plan['buildings']: target=next(o for o in meshes if o.name==building['id']) for o in meshes:o.hide_render=(o!=target) points=[target.matrix_world@Vector(c) for c in target.bound_box] low=Vector(tuple(min(p[i] for p in points) for i in range(3)));high=Vector(tuple(max(p[i] for p in points) for i in range(3)));center=(low+high)/2 size=max(high-low);cam.location=center+Vector((1,-1,.85)).normalized()*size*3 cam.rotation_euler=(center-cam.location).to_track_quat('-Z','Y').to_euler();cam_data.ortho_scale=size*1.5 file=OUT/(building['id']+'.png');scene.render.filepath=str(file);bpy.ops.render.render(write_still=True) records.append({'id':building['id'],'image':str(file),'from':'export-v3 actual LOD0 glTF import','estimatedFloors':building['floors'],'estimatedHeightM':building['heightM']}) (OUT/'manifest.json').write_text(json.dumps({'images':records,'browserUsed':False,'runtimeVisualAcceptance':False,'note':'Isolated authored asset view; world lighting differs from cockpit grade/fog, foundation shown below grade.'},indent=2),encoding='utf8') print('PILOT_REVIEW_RENDER',json.dumps({'images':len(records),'output':str(OUT)}))