65 lines
4.1 KiB
Python
65 lines
4.1 KiB
Python
"""Isolated GreatHorn trial: real Hunyuan Studio lowpoly/UV, bake, conditional UV-preserving reduction."""
|
|
import sys
|
|
sys.dont_write_bytecode = True
|
|
import hashlib
|
|
import json
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
ROOT=Path(__file__).resolve().parents[1]
|
|
STUDIO=Path('D:/UD/AI/XModel/LLM3DGraphics')
|
|
OUT=ROOT/'Tools/output/enemy_studio_trial/GreatHorn'
|
|
SOURCE=ROOT/'Tools/output/enemy_3d/GreatHorn/greathorn.glb'
|
|
sys.path.insert(0,str(ROOT/'Tools'))
|
|
from hunyuan_3d import auth
|
|
sys.path.insert(0,str(STUDIO))
|
|
from backend import hunyuan_studio as studio
|
|
from backend.retopology import _run_blender_step,compress_bake_maps,limit_studio_faces
|
|
|
|
def sha(path):return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
def update(**values):print(json.dumps(values,ensure_ascii=True),flush=True)
|
|
|
|
def main():
|
|
OUT.mkdir(parents=True,exist_ok=True)
|
|
receipt=OUT/'trial.json'
|
|
current=json.loads(receipt.read_text(encoding='utf-8')) if receipt.exists() else {}
|
|
digest=sha(SOURCE)
|
|
if current and current.get('sourceSha256')!=digest:raise ValueError('Trial source changed')
|
|
original=ROOT/'AIClient/Assets/Bundles/Characters/Enemy/GreatHorn'
|
|
originals=[original/'GreatHorn_Enemy.prefab',original/'GreatHorn_Animator.controller',*[original/'Clips'/f'{n}.anim' for n in ('Idle','Walk','Attack')], ROOT/'Tools/output/enemy_dgx/GreatHorn/high_rig/rigged.glb']
|
|
snapshot={str(p.relative_to(ROOT)):sha(p) for p in originals}
|
|
if current.get('originals') and current['originals']!=snapshot:raise ValueError('Original skeleton/animations changed during trial')
|
|
current.update(actor='GreatHorn',source=str(SOURCE.relative_to(ROOT)),sourceSha256=digest,targetTriangles=2000,originals=snapshot,status='studio')
|
|
receipt.write_text(json.dumps(current,indent=2),encoding='utf-8')
|
|
shutil.copy2(SOURCE,OUT/'model.glb')
|
|
# Reuse this project's existing authorised Hunyuan session through the installed
|
|
# Studio integration. Credentials stay local and are never logged or copied.
|
|
studio.is_logged_in=lambda client_id=None:auth.is_logged_in()
|
|
studio.load_storage_state=lambda client_id=None:(json.loads(auth.storage_state_path().read_text(encoding='utf-8')),None)
|
|
uv=OUT/'model_lowpoly_uv.glb'
|
|
if uv.exists():studio.validate_uv_glb(uv)
|
|
else:
|
|
meta=json.loads((OUT/'hunyuan_studio.json').read_text(encoding='utf-8')) if (OUT/'hunyuan_studio.json').exists() else {}
|
|
studio.process_existing(SOURCE,OUT,update,target_faces=2000,resume_low_id=meta.get('lowpoly_works_id'),resume_uv_id=meta.get('uv_works_id'))
|
|
current.update(status='baking',studio=json.loads((OUT/'hunyuan_studio.json').read_text(encoding='utf-8')))
|
|
receipt.write_text(json.dumps(current,indent=2),encoding='utf-8')
|
|
blender='D:/Tools/Blender/blender-5.0.0-windows-x64/blender.exe'
|
|
aligned=OUT/'model_lowpoly_uv_aligned.glb'
|
|
if not aligned.exists():
|
|
import subprocess
|
|
with (OUT/'alignment.log').open('w',encoding='utf-8') as log:
|
|
subprocess.run([blender,'-b','--factory-startup','--python',str(ROOT/'Tools/model_tools/bl_align_studio_uv.py'),'--',str(OUT)],stdout=log,stderr=subprocess.STDOUT,check=True,timeout=120)
|
|
# A completed source alignment is mandatory before the ray projection.
|
|
if not current.get('bakeAligned'):
|
|
if (OUT/'model_baked.glb').exists():shutil.copy2(OUT/'model_baked.glb',OUT/'diagnostic_unaligned_baked.glb')
|
|
_run_blender_step(OUT,blender,'blender_lowpoly_bake.py',(OUT/'model.glb',OUT/'model_baked.glb',1,1024,aligned),'bake.log',marker='Lowpoly bake saved to')
|
|
compress_bake_maps(OUT)
|
|
current['bakeAligned']=True
|
|
shutil.copy2(OUT/'model_baked.glb',OUT/'model_ex.glb')
|
|
mesh,decimation=limit_studio_faces(OUT,2000,blender,update)
|
|
current.update(status='mesh_ready',triangles=len(mesh.faces),vertices=len(mesh.vertices),decimation=decimation,bakeAlignment=json.loads((OUT/'bake_alignment.json').read_text(encoding='utf-8')),finalSha256=sha(OUT/'model_ex.glb'))
|
|
receipt.write_text(json.dumps(current,indent=2),encoding='utf-8')
|
|
update(status='mesh_ready',triangles=len(mesh.faces),decimation=decimation)
|
|
|
|
if __name__=='__main__':main()
|