Files
ShortVideoGenerator/projects/water-hydrogen-bond-surface-tension/blender/production_requests.py
T

78 lines
5.4 KiB
Python

"""승인된 31컷에서 정식 요청만 작성한다. 실제 결과는 실행 후 채운다."""
import argparse
import json
from pathlib import Path
ROOT=Path(__file__).resolve().parents[3]
PROJECT=ROOT/'projects/water-hydrogen-bond-surface-tension'
PROFILE=dict(engine='BLENDER_EEVEE',device='GPU',fps=50,width=1080,height=1920,samples=64,seed=0)
REFERENCE_PLAN=[
('style-3d','style',None,'BondPair','StyleAssets',1017),
('background-droplet','background','bg-droplet','OpeningDroplet','DropletStudioAssets',0),
('background-molecules','background','bg-molecules','BondPair','MoleculeStudioAssets',1017),
('background-interface','background','bg-interface','Interface','InterfaceAssets',3050),
('background-comparison','background','bg-comparison','EqualVolumeComparison','ComparisonStudioAssets',4076),
('object-molecule','object',None,'SingleMolecule','MoleculeAssets',396),
('object-droplet','object',None,'OpeningDroplet','DropletAssets',0)]
def read(stage,revision):
return json.loads((PROJECT/f'stages/{stage}/r{revision}/artifact.json').read_text(encoding='utf-8'))
def write(stage,request,inputs):
path=PROJECT/f'stages/{stage}/r2/artifact.json'
existing=json.loads(path.read_text(encoding='utf-8')) if path.exists() else None
assert not existing or not existing['payload']['result'], '완료 또는 부분 실제 결과 덮어쓰기 금지'
value=dict(schema_version='1.1',project_id=PROJECT.name,stage_id=stage,revision=2,inputs=[dict(stage_id=s,revision=r) for s,r in inputs],payload=dict(request=request,result=None))
path.write_text(json.dumps(value,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
if __name__=='__main__':
parser=argparse.ArgumentParser()
parser.add_argument('--stage',choices=['10-references','11-images','12-motion'],required=True)
args=parser.parse_args()
plans=read('08-scenes',3)['payload']['scenes']
if args.stage!='12-motion':
entities=read('09-entities',3)['payload']
read('02-research',1)
if args.stage=='10-references':
backgrounds={b['id']:b for b in entities['backgrounds']}
items=[]
for id,kind,entity,scene,obj,frame in REFERENCE_PLAN:
prompt='승인 08 r3의 '+scene+' 상태와 교육용 3D 고정 특징. 실제 '+obj+' 자산. '
if entity:
prompt+=' '.join(backgrounds[entity]['fixed_features'])
elif kind=='object':
prompt+='O1H2·V자·내부 남색 실선과 금색 분자간 H···O의 구별을 유지한다.' if id=='object-molecule' else '매끈한 청색 닫힌 mesh와 같은 물의 양을 유지하며 별도 껍질이 없다.'
else:
prompt+='references/1.png를 보존하고 단순 형태·정돈된 색·부드러운 조명·크림 배경으로 해석한다. 글자는 합성한다.'
items.append(dict(id=id,kind=kind,entity_id=entity,prompt=prompt))
write(args.stage,dict(style_reference='references/1.png',items=items,engine='Blender',render_profile=PROFILE),[('02-research',1),('08-scenes',3),('09-entities',3)])
elif args.stage=='11-images':
read('03-script',1)
cuts=read('07-cuts',1)['payload']['cuts']
reference=read('10-references',2)
assert reference['payload']['result'] and len(reference['payload']['result']['items'])==7
appearances={p['cut_id']:p for p in entities['appearances']}
mapping={'bg-droplet':'background-droplet','bg-molecules':'background-molecules','bg-interface':'background-interface','bg-comparison':'background-comparison'}
frames=[]
for cut,plan in zip(cuts,plans):
scene=plan['start_state'].split(';')[0].split('=')[1]
refs=['style-3d']+[mapping[b] for b in appearances[cut['id']]['background_ids']]
refs+=['object-molecule'] if scene in ['SingleMolecule','BondPair','LiquidNetwork','Interface'] else ['object-droplet']
if scene=='AnswerDroplet':
refs.append('object-molecule')
for position,key in [('start','start_ms'),('end','end_ms')]:
frames.append(dict(id=cut['id']+'-'+position,cut_id=cut['id'],position=position,prompt=plan['description']+' '+plan[position+'_state']+' '+plan['camera'],reference_ids=refs,scene_name=scene,frame_index=cut[key]//20))
write(args.stage,dict(frames=frames,engine='Blender',render_profile=PROFILE,scene_path='projects/'+PROJECT.name+'/stages/11-images/r2/generated-source/animation.blend'),[('02-research',1),('03-script',1),('07-cuts',1),('08-scenes',3),('09-entities',3),('10-references',2)])
else:
cuts=read('07-cuts',1)['payload']['cuts']
images=read('11-images',2)
assert images['payload']['result'] and len(images['payload']['result']['frames'])==62
frames={f['id']:f for f in images['payload']['request']['frames']}
clips=[]
for cut,plan in zip(cuts,plans):
start,end=frames[cut['id']+'-start'],frames[cut['id']+'-end']
clips.append(dict(cut_id=cut['id'],start_frame_id=start['id'],end_frame_id=end['id'],duration_ms=cut['end_ms']-cut['start_ms'],motion_prompt=plan['description']+' '+plan['start_state']+' → '+plan['end_state']+' '+plan['camera'],scene_name=start['scene_name'],start_frame=start['frame_index'],end_frame=end['frame_index']))
write(args.stage,dict(clips=clips,engine='Blender',render_profile=PROFILE,scene_path=images['payload']['result']['scene_file']['path']),[('07-cuts',1),('08-scenes',3),('11-images',2)])