"""1080×1920 실제 카메라 투영으로 T5 주석 위치와 필요한 매 프레임 키를 내보낸다.""" import argparse import hashlib import json from pathlib import Path import sys import bpy import numpy as np from mathutils import Vector from bpy_extras.object_utils import world_to_camera_view ROOT=Path(__file__).resolve().parents[3] PROJECT=ROOT/'projects/water-hydrogen-bond-surface-tension' parser=argparse.ArgumentParser() parser.add_argument('--source',required=True) parser.add_argument('--out',required=True) args=parser.parse_args(sys.argv[sys.argv.index('--')+1:]) source=Path(args.source).resolve() bpy.ops.wm.open_mainfile(filepath=str(source)) cuts=json.loads((PROJECT/'stages/07-cuts/r1/artifact.json').read_text(encoding='utf-8'))['payload']['cuts'] plans=json.loads((PROJECT/'stages/08-scenes/r3/artifact.json').read_text(encoding='utf-8'))['payload']['scenes'] cache={} def bbox_objects(obj): return [obj] if obj.type=='MESH' else [child for child in obj.children if child.type=='MESH'] def coordinates(obj,deps): if obj.name not in cache: assert not obj.modifiers and not obj.data.shape_keys, '정점 캐시가 불가능한 변형 mesh' evaluated=obj.evaluated_get(deps) mesh=evaluated.to_mesh() cache[obj.name]=np.array([tuple(v.co) for v in mesh.vertices]) evaluated.to_mesh_clear() matrix=np.array(obj.evaluated_get(deps).matrix_world) return cache[obj.name] @ matrix[:3,:3].T + matrix[:3,3] result=[] for cut,plan in zip(cuts,plans): scene=bpy.data.scenes[plan['start_state'].split(';')[0].split('=')[1]] bpy.context.window.scene=scene assert scene.camera.data.type=='ORTHO' origin=world_to_camera_view(scene,scene.camera,Vector((0,0,0))) projection=np.array([tuple(world_to_camera_view(scene,scene.camera,Vector(b))-origin)[:2] for b in [(1,0,0),(0,1,0),(0,0,1)]]) offset=np.array(tuple(origin)[:2]) objects=[obj for obj in scene.objects if obj.get('anchor')] start,end=cut['start_ms']//20,cut['end_ms']//20 tracks={obj['anchor']:[] for obj in objects} states=[] for frame in range(start,end+1): scene.frame_set(frame+1) deps=bpy.context.evaluated_depsgraph_get() labels={} for obj in objects: center=np.array(tuple(obj.matrix_world.translation)) @ projection + offset target=[round(float(center[0]*1080),2),round(float((1-center[1])*1920),2)] parts=bbox_objects(obj) box=None if parts: vertices=np.vstack([coordinates(part,deps) for part in parts]) projected=vertices @ projection + offset px=projected[:,0]*1080 py=(1-projected[:,1])*1920 box=[round(float(px.min()),2),round(float(py.min()),2),round(float(px.max()),2),round(float(py.max()),2)] label=[round((box[0]+box[2])/2,2),round(box[1]-32,2)] else: label=target assert 0