"""CPU blockout standard: cube/capsule proxies only, no anatomy/fingers/fine props.
>=1 asymmetric landmark; 864x480 matches H3; 24fps; 5-6s and <=15s.
Run blender -b -t 4 --python build_blocking.py -- --out DIR
Derived from build_previs.py, preserving workbench/camera projection pipeline.
"""
import argparse,json,math,sys
from pathlib import Path
import bpy
from mathutils import Vector
from bpy_extras.object_utils import world_to_camera_view
p=argparse.ArgumentParser();p.add_argument('--out',required=True);p.add_argument('--frames',type=int,default=124)
a=p.parse_args(sys.argv[sys.argv.index('--')+1:]);assert 5<=a.frames/24<=6 and a.frames/24<=15
out=Path(a.out).expanduser().resolve();(out/'frames').mkdir(parents=True,exist_ok=True)
bpy.ops.object.select_all(action='SELECT');bpy.ops.object.delete(use_global=False)
s=bpy.context.scene;s.render.engine='BLENDER_WORKBENCH';s.render.resolution_x=864;s.render.resolution_y=480;s.render.resolution_percentage=100;s.render.fps=24
s.frame_start=1;s.frame_end=a.frames;s.render.image_settings.file_format='PNG';s.render.filepath=str(out/'frames/frame_');s.render.threads_mode='FIXED';s.render.threads=4
s.world.color=(.22,.22,.22);sh=s.display.shading;sh.light='STUDIO';sh.color_type='OBJECT';sh.show_shadows=True;sh.show_cavity=True;sh.background_type='WORLD'
def cube(name,loc,dim,color):
 bpy.ops.mesh.primitive_cube_add(size=1,location=loc);o=bpy.context.object;o.name=name;o.dimensions=dim;o.color=(*color,1);return o
# A is one capsule mesh: low tessellation hemispheres joined by a cylindrical span.
r=.32;h=2.4;verts=[];faces=[];rings=[]
for top in (False,True):
 for j in range(5):
  angle=(-math.pi/2+j*math.pi/8) if not top else j*math.pi/8
  z=(r if not top else h-r)+r*math.sin(angle);rr=max(.0001,r*math.cos(angle))
  rings.append(len(verts))
  for k in range(16): verts.append((rr*math.cos(k*math.tau/16),rr*math.sin(k*math.tau/16),z-h/2))
for j in range(len(rings)-1):
 for k in range(16):faces.append((rings[j]+k,rings[j]+(k+1)%16,rings[j+1]+(k+1)%16,rings[j+1]+k))
mesh=bpy.data.meshes.new('CapsuleProxy');mesh.from_pydata(verts,[],faces);mesh.update();A=bpy.data.objects.new('A_red_tall_capsule',mesh);s.collection.objects.link(A);A.color=(.85,.035,.025,1)
B=cube('B_blue_short_box',(0.65,.15,.8),(.68,.50,1.6),(.025,.12,.85))
# Only coarse cubes form an asymmetric arch at rear left.
for name,loc,dim in [('ArchLeft',(-2.6,1.7,1.55),(.28,.45,3.1)),('ArchRight',(-1.5,1.7,1.55),(.28,.45,3.1)),('ArchLintel',(-2.05,1.7,3.0),(1.38,.45,.25))]:cube(name,loc,dim,(.65,.65,.65))
cube('Floor',(0,0,-.08),(20,20,.1),(.30,.30,.30))
bpy.ops.object.camera_add();cam=bpy.context.object;cam.name='BlockingCamera';cam.data.lens=44;s.camera=cam;target=Vector((0,.1,1.45))
for f in range(1,a.frames+1):
 t=(f-1)/(a.frames-1);u=max(0,min(1,(t-.12)/.76));e=u*u*(3-2*u)
 A.location=(-1.35+3.3*e,-.65,1.2);A.keyframe_insert('location',frame=f)
 B.rotation_euler.z=math.radians(-12+35*math.sin(math.pi*t));B.keyframe_insert('rotation_euler',frame=f)
 cam.location=(.15,-9+.3*t,3.25-.05*t);cam.rotation_euler=(target-cam.location).to_track_quat('-Z','Y').to_euler();cam.keyframe_insert('location',frame=f);cam.keyframe_insert('rotation_euler',frame=f)
assert all(o.type in ('MESH','CAMERA') for o in s.objects)
audit=[]
def project(v):
 uv=world_to_camera_view(s,cam,v);return {'x':uv.x*864,'y':(1-uv.y)*480,'depth':uv.z,'uv':list(uv)}
for f in range(1,a.frames+1):
 s.frame_set(f);s.view_layers.update();row={'frame':f,'frame_index':f-1,'seconds':(f-1)/24,'camera_matrix':[list(x) for x in cam.matrix_world]}
 for name,o,z in [('A',A,h-.22),('B',B,1.6-.18)]:
  row[name]={'head':project(Vector((o.location.x,o.location.y,z))),'center':project(o.location),'world':list(o.location),'rotation_z':o.rotation_euler.z,'bbox_pixels':[project(o.matrix_world@Vector(c)) for c in o.bound_box]}
 row['x_order']='A<B' if row['A']['head']['x']<row['B']['head']['x'] else 'B<A';row['front']='A' if row['A']['center']['depth']<row['B']['center']['depth'] else 'B';audit.append(row)
(out/'coordinates.json').write_text(json.dumps({'size':[864,480],'fps':24,'frames':a.frames,'duration_seconds':a.frames/24,'head_definition':'capsule top minus .22m; box top minus .18m, centerline','standard':{'proxy_shapes':['capsule','cube'],'no_fine_geometry':True,'asymmetric_landmarks':1,'max_seconds':15,'camera':'gentle .3m push'},'frames_data':audit},indent=2))
s.frame_set(1);bpy.ops.wm.save_as_mainfile(filepath=str(out/'blocking.blend'));bpy.ops.render.render(animation=True)
