urt-rs / scripts /analyze_md3_original_hierarchy.py
nmarshall's picture
qvm: original pose driven model hits
37a275a
Raw History Blame Contribute Delete
3.84 kB
#!/usr/bin/env python3
"""Original model hierarchy through actual21770 attachment and25341 geometry."""
from pathlib import Path
from qvm_research_support import source,execute
from analyze_md3_inventory import paths,virtual
from md3 import MD3
from md3_vm import map_md3
from md3_qvm_support import node_record,describe
from analyze_qvm_surface_category import support,put_text
from analyze_qvm_model_hierarchy import walk
from analyze_qvm_complete_leaf import leaf,words,CONTEXT,ORIGIN,DIRECTION
from analyze_qvm_acceleration import word
from analyze_qvm import write_json
NODES=[0x79b00000+256*j for j in range(4)]
ROLES=['lower','upper','head','helmet']
ATTACH=0x79b30000
COMPARE=ATTACH+4096
TEXT=ATTACH+8192
def attach(ins,m,bs,parent,child,tag,allocator):
m=dict(m);put_text(m,bs,TEXT,tag.encode());m[ATTACH+44]=parent;m[ATTACH+48]=child;m[ATTACH+52]=TEXT
def alloc(i,m,a,c):p=allocator[0];allocator[0]+=256;return m,c,p,{'supplied_allocation':p,'bytes':a[8],'heap_not_executed':True}
def zero(i,m,a,c):
assert a[12]==0 and a[16]%4==0
for o in range(0,a[16],4):m[a[8]+o]=0
return m,c,0,{'bounded_zero_fill':a}
def strcmp(i,m,a,c):
m[COMPARE+44]=a[8];m[COMPARE+48]=a[12];m,_,e=execute(i,36307,36487,m,c,COMPARE);return m,c,e['return'],e
m,_,e=execute(ins,21771,21918,m,(0,0,0),ATTACH,{16817:alloc,-101:zero,36306:strcmp},byte_inputs=bs)
return m,{'parent':parent,'child':child,'tag':tag,'result':e['return'],'execution':e}
def scene(raw,h,ins,character,last=False,extra_frames=()):
files={virtual(p).casefold():p for p in paths()};m,bs=support(raw,h);models={};bindings=[]
for j,role in enumerate(ROLES):
p=files[f'models/players/{character}/{role}_3.md3'];model=MD3(p.read_bytes());address=0x70000000+0x1000000*j;frame=model.frames-1 if last else 0;map_md3(m,bs,model,address,[0,frame]+[f for f in extra_frames if f<model.frames]);node_record(m,NODES[j],address,frame);models[NODES[j]]=model;bindings.append({'role':role,'path':str(p),'node':NODES[j],'address':address,'frame':frame})
allocator=[0x79b10000];attachments=[]
for parent,child,tag in [(0,1,'tag_torso'),(1,2,'tag_head'),(2,3,'tag_head')]:m,r=attach(ins,m,bs,NODES[parent],NODES[child],tag,allocator);attachments.append(r)
return m,bs,models,bindings,attachments
def geometry(ins,m,bs,models,args):
def resolved(i,m,a):inputs=describe(m,a,models[a[8]]);m,r=leaf(i,m,bs,a,limit=1000000);r['input']=inputs;return m,r
m,r=walk(ins,m,args,leaf_resolver=resolved)
transforms=[]
def collect(node):
transforms.extend(node['transforms'])
for child in node['nested']:collect(child)
collect(r);return m,{'arguments':args,'leaf_reports':r['leaf_requests'],'transforms':transforms,'execution':{k:v for k,v in r['execution'].items() if k!='children'},'final_context':words(m,args[20])}
def main():
q,raw,h,ins=source();rows=[]
for character in ['athena','orion']:
for last in [False,True]:
m,bs,models,bindings,attachments=scene(raw,h,ins,character,last)
for o in range(0,308,4):m[CONTEXT+o]=0
for p,v in [(ORIGIN,(0,0,100)),(DIRECTION,(0,0,-1))]:
for j,x in enumerate(v):m[p+4*j]=word(x)
m,r=geometry(ins,m,bs,models,{8:NODES[0],12:ORIGIN,16:DIRECTION,20:CONTEXT});r.update({'character':character,'last_frame_fixture':last,'bindings':bindings,'attachments':attachments});rows.append(r)
write_json(Path('docs/research/md3-original-hierarchy.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Actual21770/36306 attach original tags and actual25341/22009/23092 process original hierarchy. Heap allocation/zero fill and finite math modeled. Frame selections/rays are fixtures; no native loader/process/live character parity.'})
if __name__=='__main__':main()