"""Offline packaging/skin transfer of the Studio trial; preserve original finished animations.""" import argparse,hashlib,json,shutil,subprocess,sys from pathlib import Path ROOT=Path(__file__).resolve().parents[1] OUT=ROOT/'Tools/output/enemy_studio_trial/GreatHorn' ASSET='Assets/Bundles/Characters/Enemy/GreatHornStudio' BLENDER='D:/Tools/Blender/blender-5.0.0-windows-x64/blender.exe' sys.path.insert(0,str(ROOT/'Tools/model_tools')) from mt_run import run_blender_script sys.path.insert(0,str(ROOT/'Tools')) from enemy_character_pipeline import unity,script,wait_editor def blender(file,args,log): with (OUT/log).open('w',encoding='utf-8') as f: subprocess.run([BLENDER,'-b','-t','1','--factory-startup','--python',str(ROOT/'Tools/model_tools'/file),'--',*map(str,args)],stdout=f,stderr=subprocess.STDOUT,timeout=600,check=True) def main(): parser=argparse.ArgumentParser();parser.add_argument('--play-check',action='store_true');args=parser.parse_args() receipt=json.loads((OUT/'trial.json').read_text(encoding='utf-8')) assert receipt['status'] in ('mesh_ready','edit_validated','validated') for relative,digest in receipt['originals'].items():assert hashlib.sha256((ROOT/relative).read_bytes()).hexdigest()==digest,relative+' changed' blender('bl_prepare_studio_trial.py',[OUT],'static_export.log') result=run_blender_script(BLENDER,'bl_convert.py',[str(OUT/'static/GreatHornStudio.fbx'),str(OUT/'xpbr'),'1024']) (OUT/'xpbr.json').write_text(json.dumps(result,indent=2),encoding='utf-8') print('XPbr packaging complete',flush=True) blender('bl_transfer_skin.py',[ROOT/'Tools/output/enemy_dgx/GreatHorn/high_rig/rigged.glb',OUT/'xpbr/GreatHornStudio.fbx',OUT/'GreatHornStudio_Rigged.fbx','--low-up','Y','--align-bounds'],'skin_transfer.log') print('Original rig weight transfer complete',flush=True) unity('set_autotick',enable=True) if unity('editor_status')['playMode']!='stopped':raise RuntimeError('Editor is in Play; offline trial is ready, import after it stops') for name in ['GreatHornStudio.fbx','GreatHornStudio_base.png','GreatHornStudio_mix.png']: unity('import_asset',source=str(OUT/'xpbr'/name),path=ASSET+'/'+name,confirm=True) unity('import_asset',source=str(OUT/'GreatHornStudio_Rigged.fbx'),path=ASSET+'/Rigged/GreatHornStudio_Rigged.fbx',confirm=True) script('SetupCharacter','SetupCharacter.Run',['Enemy','GreatHornStudio',.8]) script('GreatHornStudioTrial','GreatHornStudioTrial.Prepare',[]) script('GreatHornStudioTrial','GreatHornStudioTrial.ExportFootConstraints',[]) blender('bl_fit_studio_soles.py',[OUT],'sole_fit.log') unity('import_asset',source=str(OUT/'GreatHornStudio_Rigged.fbx'),path=ASSET+'/Rigged/GreatHornStudio_Rigged.fbx',confirm=True) script('GreatHornStudioTrial','GreatHornStudioTrial.Build',[]) script('GreatHornStudioTrial','GreatHornStudioTrial.BuildReview',[]) script('GreatHornStudioTrial','GreatHornStudioTrial.Capture',[]) receipt['status']='edit_validated' if args.play_check: unity('editor_play') try: wait_editor('playing');script('GreatHornStudioTrial','GreatHornStudioTrial.Play',[]);receipt['status']='validated' finally:unity('editor_stop');wait_editor('stopped') for relative,digest in receipt['originals'].items():assert hashlib.sha256((ROOT/relative).read_bytes()).hexdigest()==digest,relative+' changed' receipt.update(soleFit=json.loads((OUT/'sole_fit.json').read_text(encoding='utf-8')),riggedSha256=hashlib.sha256((OUT/'GreatHornStudio_Rigged.fbx').read_bytes()).hexdigest(),originalResourcesUnchanged=True) (OUT/'trial.json').write_text(json.dumps(receipt,indent=2),encoding='utf-8') if __name__=='__main__':main()