Files
tekton/tools/rename_rig_in_glb.py

91 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Rename rig root node in character GLBs to 'Character' for consistent paths.
This must run before Godot imports the GLBs. It modifies the .glb JSON to rename
the rig root node (oldpop-rig, bob-rig, etc.) to 'Character' so all characters
share the same skeleton path: Character/Skeleton3D:bone_name
Backups are created as .glb.bak on first run.
Re-runnable: if backup exists, restore from backup first to ensure idempotency.
"""
import json
import os
import struct
import shutil
import sys
CHARACTERS = {
'Bob.glb': 'bob-rig',
'Oldpop.glb': 'oldpop-rig',
'Masbro.glb': 'masbro-tpose',
'Gatot.glb': 'gatot-tpose',
}
NEW_NAME = 'Character'
CHARS_DIR = os.path.join(os.path.dirname(__file__), '..', 'assets', 'characters')
def find_rig_node(nodes, old_name):
for i, n in enumerate(nodes):
if n.get('name') == old_name:
return i
return -1
def process_glb(path, old_name):
backup = path + '.bak'
if os.path.exists(backup):
shutil.copy(backup, path)
else:
shutil.copy(path, backup)
with open(path, 'rb') as f:
f.read(12) # magic + version + length
chunk_len = struct.unpack('<I', f.read(4))[0]
f.read(4) # chunk type
json_data = f.read(chunk_len)
rest = f.read()
data = json.loads(json_data)
nodes = data.get('nodes', [])
idx = find_rig_node(nodes, old_name)
if idx < 0:
print(f' {os.path.basename(path)}: rig root "{old_name}" NOT FOUND, skipping')
return False
nodes[idx]['name'] = NEW_NAME
new_json = json.dumps(data, separators=(',', ':')).encode('utf-8')
new_chunk_len = len(new_json)
new_length = 12 + 8 + new_chunk_len + len(rest)
with open(path, 'wb') as f:
f.write(b'glTF')
f.write(struct.pack('<I', 2))
f.write(struct.pack('<I', new_length))
f.write(struct.pack('<I', new_chunk_len))
f.write(b'JSON')
f.write(new_json)
f.write(rest)
print(f' {os.path.basename(path)}: renamed {old_name} -> {NEW_NAME}')
return True
def main():
os.chdir(CHARS_DIR)
print(f'Processing GLBs in {CHARS_DIR}')
for glb, old_name in CHARACTERS.items():
path = os.path.join(CHARS_DIR, glb)
if not os.path.exists(path):
print(f' {glb}: not found, skipping')
continue
process_glb(path, old_name)
print('Done.')
if __name__ == '__main__':
main()