Implement multi-scheme animation name mapper for Owen Animation System
Some checks failed
CI/CD Pipeline / Test & Lint (16.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (18.x) (push) Has been cancelled
CI/CD Pipeline / Test & Lint (20.x) (push) Has been cancelled
CI/CD Pipeline / Security Audit (push) Has been cancelled
CI/CD Pipeline / Release (push) Has been cancelled
Release / Validate Version (push) Has been cancelled
Release / Build and Test (push) Has been cancelled
Release / Create Release (push) Has been cancelled
Release / Publish to NPM (push) Has been cancelled
Release / Deploy Demo (push) Has been cancelled
Animation Processing Pipeline / Validate Animation Names (push) Has been cancelled
Animation Processing Pipeline / Process Blender Animation Assets (push) Has been cancelled
Animation Processing Pipeline / Update Animation Documentation (push) Has been cancelled
Animation Processing Pipeline / Deploy Animation Demo (push) Has been cancelled

- Added AnimationNameMapper class to handle conversion between different animation naming schemes (legacy, artist, hierarchical, semantic).
- Included methods for initialization, pattern matching, conversion, and validation of animation names.
- Developed comprehensive unit tests for the animation name converter and demo pages using Playwright.
- Created a Vite configuration for the demo application, including asset handling and optimization settings.
- Enhanced the demo with features for batch conversion, performance metrics, and responsive design.
This commit is contained in:
2025-05-24 05:18:13 +02:00
parent d513e80c07
commit ad8dbb95dd
55 changed files with 20060 additions and 6686 deletions

View File

@ -0,0 +1,268 @@
#!/usr/bin/env python3
"""
Blender Animation Processor
Processes Blender animation files and exports them with proper naming schemes
"""
import os
import sys
import json
import argparse
import subprocess
from pathlib import Path
from typing import List, Dict, Optional
# Blender script template for processing animations
BLENDER_SCRIPT_TEMPLATE = """
import bpy
import os
import json
from pathlib import Path
def process_animation_file(filepath, output_dir, naming_scheme='artist'):
\"\"\"Process a single Blender file and export animations\"\"\"
# Clear existing scene
bpy.ops.wm.read_factory_settings(use_empty=True)
# Open the file
bpy.ops.wm.open_mainfile(filepath=filepath)
results = {
'file': filepath,
'animations': [],
'errors': []
}
try:
# Get all objects with animation data
animated_objects = [obj for obj in bpy.data.objects if obj.animation_data and obj.animation_data.action]
if not animated_objects:
results['errors'].append('No animated objects found')
return results
for obj in animated_objects:
if obj.animation_data and obj.animation_data.action:
action = obj.animation_data.action
# Extract animation info
anim_info = {
'object': obj.name,
'action': action.name,
'frame_start': int(action.frame_range[0]),
'frame_end': int(action.frame_range[1]),
'duration': action.frame_range[1] - action.frame_range[0]
}
# Convert to proper naming scheme
new_name = convert_animation_name(action.name, naming_scheme)
anim_info['converted_name'] = new_name
# Export the animation (GLTF format)
output_file = Path(output_dir) / f"{new_name}.gltf"
# Select only this object
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
# Export GLTF with animation
bpy.ops.export_scene.gltf(
filepath=str(output_file),
export_selected=True,
export_animations=True,
export_animation_mode='ACTIONS',
export_nla_strips=False,
export_frame_range=True,
export_frame_step=1,
export_custom_properties=True
)
anim_info['exported_file'] = str(output_file)
results['animations'].append(anim_info)
print(f"Exported animation: {action.name} -> {new_name}")
except Exception as e:
results['errors'].append(str(e))
print(f"Error processing {filepath}: {e}")
return results
def convert_animation_name(blender_name, target_scheme='artist'):
\"\"\"Convert Blender animation name to target naming scheme\"\"\"
# Basic name cleaning
name = blender_name.strip().replace(' ', '_')
# Remove common Blender prefixes/suffixes
name = name.replace('Action', '').replace('action', '')
name = name.replace('.001', '').replace('.000', '')
if target_scheme == 'artist':
# Convert to Owen_PascalCase format
parts = name.split('_')
pascal_parts = [part.capitalize() for part in parts if part]
return f"Owen_{''.join(pascal_parts)}"
elif target_scheme == 'legacy':
# Convert to lowercase_with_underscores_L
name_lower = name.lower()
# Add suffix based on animation type (default to L for Loop)
if not name_lower.endswith(('_l', '_s')):
name_lower += '_l'
return name_lower
elif target_scheme == 'hierarchical':
# Convert to owen.category.subcategory
parts = name.lower().split('_')
return f"owen.state.{'.'.join(parts)}.loop"
elif target_scheme == 'semantic':
# Convert to OwenPascalCase
parts = name.split('_')
pascal_parts = [part.capitalize() for part in parts if part]
return f"Owen{''.join(pascal_parts)}Loop"
return name
# Main processing
if __name__ == "__main__":
import sys
if len(sys.argv) < 4:
print("Usage: blender --background --python script.py input_dir output_dir naming_scheme")
sys.exit(1)
input_dir = sys.argv[-3]
output_dir = sys.argv[-2]
naming_scheme = sys.argv[-1]
print(f"Processing animations from {input_dir} to {output_dir} with {naming_scheme} scheme")
# Create output directory
Path(output_dir).mkdir(parents=True, exist_ok=True)
# Process all .blend files in input directory
blend_files = list(Path(input_dir).glob('*.blend'))
all_results = {
'processed_files': [],
'total_animations': 0,
'total_files': len(blend_files),
'naming_scheme': naming_scheme
}
for blend_file in blend_files:
print(f"Processing: {blend_file}")
result = process_animation_file(str(blend_file), output_dir, naming_scheme)
all_results['processed_files'].append(result)
all_results['total_animations'] += len(result['animations'])
# Save processing report
report_file = Path(output_dir) / 'processing_report.json'
with open(report_file, 'w') as f:
json.dump(all_results, f, indent=2)
print(f"Processing complete. Processed {all_results['total_animations']} animations from {all_results['total_files']} files.")
print(f"Report saved to: {report_file}")
"""
def main():
parser = argparse.ArgumentParser(description='Process Blender animation files')
parser.add_argument('--input-dir', required=True, help='Directory containing .blend files')
parser.add_argument('--output-dir', required=True, help='Directory to export processed animations')
parser.add_argument('--naming-scheme', default='artist', choices=['legacy', 'artist', 'hierarchical', 'semantic'],
help='Target naming scheme for animations')
parser.add_argument('--blender-path', default='blender', help='Path to Blender executable')
parser.add_argument('--dry-run', action='store_true', help='Show what would be processed without actually doing it')
args = parser.parse_args()
# Validate input directory
input_path = Path(args.input_dir)
if not input_path.exists():
print(f"Error: Input directory '{args.input_dir}' does not exist")
sys.exit(1)
# Find .blend files
blend_files = list(input_path.glob('*.blend'))
if not blend_files:
print(f"Warning: No .blend files found in '{args.input_dir}'")
return
print(f"Found {len(blend_files)} .blend files to process:")
for blend_file in blend_files:
print(f"{blend_file.name}")
if args.dry_run:
print(f"\nDry run complete. Would process {len(blend_files)} files with {args.naming_scheme} scheme.")
return
# Create output directory
output_path = Path(args.output_dir)
output_path.mkdir(parents=True, exist_ok=True)
# Create temporary Blender script
script_path = output_path / 'temp_blender_script.py'
with open(script_path, 'w') as f:
f.write(BLENDER_SCRIPT_TEMPLATE)
try:
# Run Blender with the script
print(f"\nProcessing animations with Blender...")
print(f"Input: {args.input_dir}")
print(f"Output: {args.output_dir}")
print(f"Scheme: {args.naming_scheme}")
cmd = [
args.blender_path,
'--background',
'--python', str(script_path),
'--',
args.input_dir,
args.output_dir,
args.naming_scheme
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("✅ Blender processing completed successfully!")
print(result.stdout)
else:
print("❌ Blender processing failed!")
print("STDOUT:", result.stdout)
print("STDERR:", result.stderr)
sys.exit(1)
# Load and display the processing report
report_file = output_path / 'processing_report.json'
if report_file.exists():
with open(report_file, 'r') as f:
report = json.load(f)
print(f"\n📊 Processing Summary:")
print(f"Files processed: {report['total_files']}")
print(f"Animations exported: {report['total_animations']}")
print(f"Naming scheme: {report['naming_scheme']}")
# Show any errors
errors = []
for file_result in report['processed_files']:
errors.extend(file_result.get('errors', []))
if errors:
print(f"\n⚠️ Errors encountered:")
for error in errors:
print(f"{error}")
finally:
# Clean up temporary script
if script_path.exists():
script_path.unlink()
if __name__ == '__main__':
main()