param([switch]$Generate, [switch]$BuildReview) $ErrorActionPreference = 'Stop' $repoPath = Split-Path -Parent $PSScriptRoot $unityProject = Join-Path $repoPath 'AIClient' $workPath = Join-Path $repoPath 'Tools/output/rockball_dgx/walk_verified' $rigPath = Join-Path $repoPath 'Tools/output/rockball_dgx/high_rig/rigged.glb' $lowPath = Join-Path $unityProject 'Assets/Bundles/Characters/Animal/RockBall/RockBall.fbx' $blenderPath = if ($env:BLENDER_EXE) { $env:BLENDER_EXE } else { 'D:/Tools/Blender/blender-5.0.0-windows-x64/blender.exe' } function Invoke-WalkUnity { param([string[]]$CommandArgs) $raw = & unity --json command --project-path $unityProject @CommandArgs if ($LASTEXITCODE -ne 0) { throw "Unity CLI failed: $CommandArgs" } $response = ($raw -join "`n") | ConvertFrom-Json if (-not $response.success -or $response.data.result.success -eq $false) { throw ($raw -join "`n") } $response.data.result } Push-Location -LiteralPath $repoPath try { Invoke-WalkUnity -CommandArgs @('set_autotick','--enable','true') | Out-Host $editorState = Invoke-WalkUnity -CommandArgs @('editor_status') if ($editorState.playMode -ne 'stopped' -or $editorState.compiling) { throw 'Use an idle Editor in Edit Mode.' } if ($Generate) { & python Tools/generate_animal_walk.py --rig $rigPath --out "$workPath/motion" --face-right bone_8 --face-left bone_10 if ($LASTEXITCODE -ne 0) { throw 'Walk generation failed' } } if (-not (Test-Path -LiteralPath "$workPath/motion/animated.fbx")) { throw 'Missing cached Walk: use -Generate first.' } & $blenderPath -b --factory-startup --python Tools/model_tools/bl_transfer_skin.py -- $rigPath $lowPath "$workPath/RockBall_Rigged.fbx" --low-up Y --align-bounds if ($LASTEXITCODE -ne 0) { throw 'Skin transfer failed' } $assetRoot = 'Assets/Bundles/Characters/Animal/RockBall' Invoke-WalkUnity -CommandArgs @('import_asset','--source',"$workPath/RockBall_Rigged.fbx",'--path',"$assetRoot/Rigged/RockBall_Rigged.fbx",'--confirm','true') | Out-Host Invoke-WalkUnity -CommandArgs @('import_asset','--source',"$workPath/motion/animated.fbx",'--path',"$assetRoot/Anims/RockBall_Walk.fbx",'--confirm','true') | Out-Host foreach ($step in @( @('SetupAnimalAnimator','SetupAnimalAnimator.SetupRockBallWalk'), @('FinalizeAnimalWalk','FinalizeAnimalWalk.Run'), @('VerifyAnimalWalk','VerifyAnimalWalk.Run') )) { Invoke-WalkUnity -CommandArgs @('run_script','--file',"AgentScripts/$($step[0]).cs",'--entry',$step[1]) | Out-Host } if ($BuildReview) { Invoke-WalkUnity -CommandArgs @('run_script','--file','AgentScripts/ReviewRockBallWalk.cs','--entry','ReviewRockBallWalk.Build') | Out-Host } $manifest = @{ ok = $true; name = 'RockBall'; pipeline = 'same-character-high-12bone -> aligned-approved-low -> UniMate biped Walk -> offline authored gait' rigged_fbx = "$workPath/RockBall_Rigged.fbx"; source_mesh = $lowPath; source_rig = $rigPath source_mesh_sha256 = (Get-FileHash -LiteralPath $lowPath -Algorithm SHA256).Hash source_rig_sha256 = (Get-FileHash -LiteralPath $rigPath -Algorithm SHA256).Hash anims = @{ Walk = "$workPath/motion/animated.fbx" } setup_entry = 'SetupAnimalAnimator.SetupRockBallWalk' postprocess = @('AgentScripts/FinalizeAnimalWalk.cs -> FinalizeAnimalWalk.Run','AgentScripts/VerifyAnimalWalk.cs -> VerifyAnimalWalk.Run') notes = 'Walk only; old 19-bone Idle/Attack must not be connected to this 12-bone rig.' } $manifestPath = Join-Path $repoPath 'Tools/output/rockball_dgx/RockBall_manifest.json' [IO.File]::WriteAllText($manifestPath, ($manifest | ConvertTo-Json -Depth 5), [Text.UTF8Encoding]::new($false)) Write-Host 'PASS: RockBall Walk imported, authored and verified.' } finally { Pop-Location }