diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 28288d79fa..8f4789a521 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -35,7 +35,7 @@ env: TN_BASE_SHA: ${{ inputs.base_sha }} jobs: - # The per-lane trigger filters these six files used to carry, as one gate: the pull request's own + # The per-lane trigger filters these lane files used to carry, as one gate: the pull request's own # changed paths decide which lanes run, and a lane this commit does not touch is skipped rather than # spending a runner. Lane-local workflow edits select that lane; shared scheduling edits # fail closed to every lane. Source filters remain independent of workflow-only selection. @@ -48,6 +48,7 @@ jobs: contents: read actions: read outputs: + animation: ${{ steps.filter.outputs.animation }} candidate_sha: ${{ steps.filter.outputs.candidate_sha }} required_jobs: ${{ steps.filter.outputs.required_jobs }} exposure: ${{ steps.filter.outputs.exposure }} @@ -74,6 +75,7 @@ jobs: TN_HEAD_SHA: ${{ inputs.candidate_sha }} # Exact source filters; workflow-only changes compare each job and filter separately. TN_LANE_FILTERS: | + animation ^packages/core/src/animation\.ts$|^packages/core/__tests__/animation[^/]*\.spec\.ts$|^examples/abyss-framework/src/render/animation-reversal[^/]*\.ts$|^examples/abyss-framework/src/animation-reversal-main\.ts$|^examples/abyss-framework/animation-reversal\.html$|^examples/abyss-framework/playtests/animation-reversal\.playtest\.json$|^scripts/verify-animation-reversal\.ts$|^packages/core/__tests__/vq-locomotion-blend-spaces\.spec\.ts$|^examples/abyss-framework/src/render/locomotion-[^/]*\.ts$|^examples/abyss-framework/src/render/vq-locomotion-[^/]*\.ts$|^examples/abyss-framework/src/vq-locomotion-main\.ts$|^examples/abyss-framework/playtests/vq-locomotion-blend-spaces\.playtest\.json$|^examples/abyss-framework/assets/mannequin\.glb$|^scripts/verify-vq-locomotion\.ts$ cold-boot ^packages/create-threenative/(template-assets|templates/[^/]+/src/render)/(autoExposure|exposure|exposureGraph|exposureReadback|worldEnvironment)\.ts$|^packages/create-threenative/__tests__/auto-exposure[^/]*\.spec\.ts$|^packages/create-threenative/__tests__/fixtures/auto-exposure/.*$|^packages/core/src/.*$|^packages/core/patches/.*$|^packages/playtest/.*$|^packages/runtime-native/.*$|^pnpm-lock\.yaml$|^scripts/xvfb\.sh$|^\.github/actions/(workspace-dist|pnpm|playwright-chromium)/.*$ exposure ^packages/create-threenative/(template-assets|templates/[^/]+/src/render)/(autoExposure|exposure|exposureGraph|exposureReadback|worldEnvironment)\.ts$|^packages/create-threenative/__tests__/auto-exposure[^/]*\.spec\.ts$|^packages/create-threenative/__tests__/fixtures/auto-exposure/.*$|^packages/core/src/.*$|^packages/core/patches/.*$|^packages/playtest/.*$|^packages/runtime-native/.*$|^pnpm-lock\.yaml$|^scripts/xvfb\.sh$|^\.github/actions/(workspace-dist|pnpm|playwright-chromium)/.*$ csg ^examples/integrations/csg/.*$ @@ -348,6 +350,130 @@ jobs: path: ${{ github.workspace }}/artifacts/ci-integration-receipts/${{ github.job }}.json if-no-files-found: error + animation: + name: animation + if: ${{ needs.paths.outputs.animation == 'true' && !github.event.pull_request.draft }} + needs: paths + runs-on: ${{ (github.event.pull_request.head.repo.fork || !vars.TN_RUNNER) && 'ubuntu-24.04' || vars.TN_RUNNER }} + timeout-minutes: 25 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + with: + persist-credentials: false + ref: ${{ inputs.candidate_sha }} + - uses: ./.github/actions/pnpm + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + with: + node-version: '22' + cache: pnpm + - run: pnpm install --frozen-lockfile + - uses: ./.github/actions/workspace-dist + with: + pack-archives: 'false' + save-bundles: 'false' + - uses: ./.github/actions/playwright-chromium + - run: pnpm exec vitest run --maxWorkers=1 packages/core/__tests__/animation.spec.ts packages/core/__tests__/animation-reversal-fixture.spec.ts packages/core/__tests__/animation-weighted.spec.ts packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts + - name: Capture real mannequin gait reversals on headed WebGPU + run: node --import tsx scripts/verify-animation-reversal.ts + - name: Capture the two-rig locomotion blend spaces on headed WebGPU + run: node --import tsx scripts/verify-vq-locomotion.ts + - uses: actions/upload-artifact@v7 + if: always() + with: + name: vq04-animation-${{ github.run_id }}-${{ github.run_attempt }}-${{ inputs.candidate_sha }} + path: artifacts/vq04-animation + if-no-files-found: error + - name: Record exact current-attempt integration receipt + if: ${{ always() }} + env: + GH_TOKEN: ${{ github.token }} + TN_CI_JOB_RESULT: ${{ job.status }} + run: node "$GITHUB_WORKSPACE/scripts/ci-integration-receipts.mjs" --write + - uses: actions/upload-artifact@v7 + if: ${{ always() }} + with: + name: ci-integration-receipt-${{ github.run_id }}-${{ github.run_attempt }}-${{ inputs.candidate_sha }}-${{ github.job }} + path: ${{ github.workspace }}/artifacts/ci-integration-receipts/${{ github.job }}.json + if-no-files-found: error + + animation-native: + name: Linux native locomotion correctness + needs: [animation] + # The browser capture cannot say what the compiled runtime does with the same blend-space + # trace, and a stale host binary answers for the wrong source. Build this candidate's ARM + # desktop host and run the authored scenario file unchanged, so the lane's own scenario is + # the native proof rather than a second near-copy of it. + runs-on: ubuntu-24.04-arm + timeout-minutes: 60 + env: + CMAKE_BUILD_PARALLEL_LEVEL: '2' + TN_PLAYTEST_ALLOW_SOFTWARE: '1' + TN_PLAYTEST_NATIVE_SCREENSHOT_TIMEOUT_MS: '30000' + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + with: + persist-credentials: false + ref: ${{ inputs.candidate_sha }} + - uses: ./.github/actions/pnpm + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + with: + node-version: '22' + cache: pnpm + - uses: actions/setup-python@v7 + with: + python-version: '3.12' + - run: pnpm install --frozen-lockfile + - name: Build this source's public JavaScript packages + uses: ./.github/actions/workspace-dist + with: + save-bundles: 'false' + pack-archives: 'false' + - name: Restore checksum-provisioned native dependencies + uses: actions/cache/restore@v4 + with: + path: packages/runtime-native/third_party + key: native-third-party-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('packages/runtime-native/scripts/download-deps.mjs', 'packages/runtime-native/scripts/package-android.mjs') }} + - name: Install the existing native lane's system prerequisites + run: >- + sudo apt-get update && sudo apt-get install -y + libasound2-dev libcurl4-openssl-dev libfontconfig1-dev libfreetype6-dev + libpulse-dev libudev-dev libx11-dev libxext-dev libxrandr-dev + libxcursor-dev libxi-dev libxfixes-dev libxss-dev libxkbcommon-dev + libgtk-3-dev libwebkit2gtk-4.1-dev libxcomposite-dev xcompmgr + libvulkan1 mesa-vulkan-drivers vulkan-tools xvfb zlib1g-dev + - name: Build the ordinary ARM desktop host from this source + run: pnpm --filter @threenative/runtime-native native:build + - name: Bundle the authored locomotion entry for the native host + run: node packages/runtime-native/scripts/bundle.mjs --project examples/abyss-framework --entry src/render/vq-locomotion-game.ts --target desktop --output examples/abyss-framework/dist/vq-locomotion-native.js + - name: Record this run's host identity beside its screenshots + run: | + mkdir -p examples/abyss-framework/artifacts/playtest + sha256sum packages/runtime-native/build/tn-linux/mystral > examples/abyss-framework/artifacts/playtest/native-host-identity.txt + git rev-parse HEAD >> examples/abyss-framework/artifacts/playtest/native-host-identity.txt + - name: Run the authored locomotion trace on the freshly built host + # A non-zero CLI status fails the step, so a run that never reached its assertions fails too. + run: sh scripts/xvfb.sh node packages/playtest/dist/runner/cli.js playtests/vq-locomotion-blend-spaces.playtest.json --target desktop --executable ${{ github.workspace }}/packages/runtime-native/build/tn-linux/mystral --project examples/abyss-framework --host-arg run --host-arg dist/vq-locomotion-native.js + - name: Retain native screenshots, host console and executable identities + if: always() + uses: actions/upload-artifact@v7 + with: + name: vq04-locomotion-native-${{ github.run_id }}-${{ github.run_attempt }}-${{ inputs.candidate_sha }} + path: examples/abyss-framework/artifacts/playtest + retention-days: 30 + if-no-files-found: warn + - name: Record exact current-attempt integration receipt + if: ${{ always() }} + env: + GH_TOKEN: ${{ github.token }} + TN_CI_JOB_RESULT: ${{ job.status }} + run: node "$GITHUB_WORKSPACE/scripts/ci-integration-receipts.mjs" --write + - uses: actions/upload-artifact@v7 + if: ${{ always() }} + with: + name: ci-integration-receipt-${{ github.run_id }}-${{ github.run_attempt }}-${{ inputs.candidate_sha }}-${{ github.job }} + path: ${{ github.workspace }}/artifacts/ci-integration-receipts/${{ github.job }}.json + if-no-files-found: error + fog: name: fog capture if: ${{ needs.paths.outputs.fog == 'true' && !github.event.pull_request.draft }} @@ -827,7 +953,7 @@ jobs: completion: name: Verify required integration receipts - needs: [paths, csg, ik, vegetation, decals, decals-native, tone, fog, fog-native, capture, fluid-collision, fluid-collision-native, packaged-quickjs, exposure, cold-boot] + needs: [paths, csg, ik, vegetation, decals, decals-native, tone, animation, animation-native, fog, fog-native, capture, fluid-collision, fluid-collision-native, packaged-quickjs, exposure, cold-boot] if: ${{ always() }} runs-on: ${{ (github.event.pull_request.head.repo.fork || !vars.TN_RUNNER_LIGHT) && 'ubuntu-24.04' || vars.TN_RUNNER_LIGHT }} timeout-minutes: 5 diff --git a/biome.json b/biome.json index 619e6fc5b1..d586e9309e 100644 --- a/biome.json +++ b/biome.json @@ -45,6 +45,7 @@ "docs/benchmark/rounds/**", "docs/verification/prd-357-c4-duplicate-inventory.json", "docs/verification/prd-357-c4-duplicate-inventory-before.json", + "docs/verification/vq04/**", "docs/verification/visuals/**", "benchmark/engine-load-test/godot-lights-meshes/benchmark_box_1000.json" ] diff --git a/docs/PRDs/animation/PRD-VQ-04-locomotion-blend-spaces.md b/docs/PRDs/animation/PRD-VQ-04-locomotion-blend-spaces.md deleted file mode 100644 index 804dfca897..0000000000 --- a/docs/PRDs/animation/PRD-VQ-04-locomotion-blend-spaces.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -prd_contract: v1 ---- - -# PRD-VQ-04 — Locomotion blends by speed and direction without restarting its gait - -**Status:** PROPOSED — 2026-10-01. No implementation or qualification is claimed. -**Priority:** P2 — Proposed blend-weight evaluation, phase sync and first/third-person consumers, all unrun. -**Batch:** Visual quality execution batch. **Wave:** 1 / character motion. -**Dependencies:** Uses existing AnimationPlayer, SkeletalMesh3D and stride synchronization. Coordinate its action ownership with VQ-05. - -## Grounding and intended outcome - -[packages/core/src/animation.ts](https://github.com/ThreeNativeHQ/threenative/blob/d72778382b134ef8763f58825cb4d4fd8cc0f6e3/packages/core/src/animation.ts) already implements clip playback and crossfades. This PRD does not rebuild those. The remaining product outcome is continuously weighted, phase-coherent locomotion across speed and direction. Historical animation-state-machine restrictions remain in force: this is not a graph editor or serialized animation IR. - -**Outcome:** A licensed rig moves smoothly from idle through walking to running and strafes in a 2D direction space. Changing direction or speed does not repeatedly restart clips or introduce a one-frame pose pop. - -## Design and ownership - -Use ordinary Three AnimationActions with one authoritative updater. Put locomotion states, clip selection, thresholds and transitions in editable game TypeScript. Admit shared numerical weight/phase helpers only after two consumers demonstrate less total code. Use sorted intervals for 1D and a deterministic declared interpolation domain for 2D; handle points outside that domain explicitly. Preserve normalized phase for compatible gait clips, with an explicit opt-out for incompatible authored cycles. Do not confuse stride rate with root-motion extraction. - -Idle/walk/run and four-direction locomotion only. No motion matching, full Animator graph, visual editor, new asset format or automatic semantic inference from clip filenames. - -## Required behavior - -- Weights remain finite and nonnegative and sum to 1 within 1e-6; duplicate samples and degenerate 2D domains fail or use an explicitly documented fallback. -- Identical input replay is deterministic, and no independent mixer loop is created. -- A speed sweep preserves gait phase across compatible clips; a large instantaneous intent change has a bounded authored transition. -- Test rapid idle/walk/run/idle reversal and returning to a still-contributing action, not just a fresh third clip. - -## Execution phases - -All proof paths below are **planned implementation targets**, not existing passing tests. Reuse an existing equivalent test or scenario after inspecting current code, and update the canonical proof path rather than adding a duplicate. Follow EXECUTE.md for fixture setup, variables, review and repository gates. - -### Phase 1 — Continuous weights with explicit boundaries - -- [ ] Implement and test deterministic 1D and 2D weight evaluation, including degenerate and out-of-domain inputs. proof: `pnpm exec vitest run packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts`. -- [ ] Integrate weighted action ownership and phase synchronization without breaking existing play/once/stride behavior. proof: `pnpm exec vitest run packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts`. - -### Phase 2 — Two real locomotion consumers - -- [ ] Drive a first-person-visible body and a third-person rig through the same weight/phase mechanism with game-owned settings. proof: `pnpm exec vitest run packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts`. -- [ ] Expose selected clips, weights and phase in existing gameplay observations and verify rapid reversals against sampled poses. proof: `pnpm exec vitest run packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts`. - -### Phase 3 — Qualify the visible result - -- [ ] The real-rig speed/strafe sweep passes weight, phase and pose-continuity assertions on browser WebGPU. proof: `node packages/playtest/dist/runner/cli.js examples/abyss-framework/playtests/vq-locomotion-blend-spaces.playtest.json --url ${VQ_URL:?} --browser-recipe webgpu`. -- [ ] The identical input trace passes on Linux native; inspect the transition sequence rather than a single still frame. proof: `node packages/playtest/dist/runner/cli.js examples/abyss-framework/playtests/vq-locomotion-blend-spaces.playtest.json --target desktop --executable ${VQ_NATIVE_EXECUTABLE:?}`. - -## Acceptance criteria - -- [ ] Continuous speed/direction changes require no game-specific replacement mixer, and a same-rig trace produces matching clip weights across the two qualified runtimes. proof: the completed Phase 3 scenario outputs, with the named fixture, package/cohort and adapter recorded inline here. -- [ ] The feature preserves its documented inactive/fallback path and releases owned resources after repeated lifecycle transitions. proof: `pnpm exec vitest run packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts` plus the Phase 3 lifecycle scenario. - -## Performance and promotion - -Count active actions and allocations. Keep nonparticipants at the existing AnimationPlayer cost and document any extra per-rig work. Numerical targets are proposed acceptance targets, not benchmark results. Pin the fixture, metrics and thresholds before changing the implementation; never relax a failing threshold just to mark this PRD complete. Shared abstractions must pass the repository's reuse/LOC admission rule. Appearance remains editable source. - -## Blocked on - -A license-clear multi-clip rig is required. Use repository-owned fixtures where possible; downloading commercial animations is not assumed. - -## Completion record - -Update the phase boxes and this PRD only after the named proof runs. Record actual results inline, including any remaining exclusions. A merged planning or implementation PR alone is not proof that every acceptance criterion passed. Archive according to the parent PRD filing rules when the work is genuinely complete. diff --git a/docs/PRDs/done/PRD-VQ-04-locomotion-blend-spaces.md b/docs/PRDs/done/PRD-VQ-04-locomotion-blend-spaces.md new file mode 100644 index 0000000000..6389b956cf --- /dev/null +++ b/docs/PRDs/done/PRD-VQ-04-locomotion-blend-spaces.md @@ -0,0 +1,144 @@ +--- +prd_contract: v1 +--- + +# PRD-VQ-04 — Locomotion blends by speed and direction without restarting its gait + +**Status:** DONE — 2026-10-04. Weighted playback, game-owned 1D/2D evaluation, three locomotion consumers (two on the CC0 Quaternius mannequin by speed, one on the CC0 KayKit rig by 2D direction with real strafe and backward clips) and their browser and native proof are complete. A human aesthetic review of the frames is not a box here and has not been done. +**Priority:** P2 — Complete: blend-weight evaluation, phase sync and the first/third-person and 2D directional consumers are proven on browser WebGPU and Linux native. +**Batch:** Visual quality execution batch. **Wave:** 1 / character motion. +**Dependencies:** Uses existing AnimationPlayer, SkeletalMesh3D and stride synchronization. Coordinate its action ownership with VQ-05. + +## Grounding and intended outcome + +[packages/core/src/animation.ts](https://github.com/ThreeNativeHQ/threenative/blob/d72778382b134ef8763f58825cb4d4fd8cc0f6e3/packages/core/src/animation.ts) already implements clip playback and crossfades. This PRD does not rebuild those. The remaining product outcome is continuously weighted, phase-coherent locomotion across speed and direction. Historical animation-state-machine restrictions remain in force: this is not a graph editor or serialized animation IR. + +**Outcome:** A licensed rig moves smoothly from idle through walking to running and strafes in a 2D direction space. Changing direction or speed does not repeatedly restart clips or introduce a one-frame pose pop. + +## Design and ownership + +Use ordinary Three AnimationActions with one authoritative updater. Put locomotion states, clip selection, thresholds and transitions in editable game TypeScript. Admit shared numerical weight/phase helpers only after two consumers demonstrate less total code. Use sorted intervals for 1D and a deterministic declared interpolation domain for 2D; handle points outside that domain explicitly. Preserve normalized phase for compatible gait clips, with an explicit opt-out for incompatible authored cycles. Do not confuse stride rate with root-motion extraction. + +Idle/walk/run and four-direction locomotion only. No motion matching, full Animator graph, visual editor, new asset format or automatic semantic inference from clip filenames. + +## Required behavior + +- Weights remain finite and nonnegative and sum to 1 within 1e-6; duplicate samples and degenerate 2D domains fail or use an explicitly documented fallback. +- Identical input replay is deterministic, and no independent mixer loop is created. +- A speed sweep preserves gait phase across compatible clips; a large instantaneous intent change has a bounded authored transition. +- Test rapid idle/walk/run/idle reversal and returning to a still-contributing action, not just a fresh third clip. + +## Execution phases + +All proof paths below are **planned implementation targets**, not existing passing tests. Reuse an existing equivalent test or scenario after inspecting current code, and update the canonical proof path rather than adding a duplicate. Follow [original execution instructions](https://github.com/ThreeNativeHQ/threenative/blob/f95b4ffab0b842dc9a851ab10c719345ec67a6d6/docs/PRDs/batch-2026-10-01-visual-quality/EXECUTE.md) for fixture setup, variables, review and repository gates. + +### Phase 1 — Continuous weights with explicit boundaries + +- [x] Implement and test deterministic 1D and 2D weight evaluation, including degenerate and out-of-domain inputs. proof: `pnpm exec vitest run --maxWorkers=1 packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts`: 11 CPU tests pass, exit 0 (2026-10-04). Evaluators are editable game source; compatible directional gait assets and actual consumers remain separate open gates. +- [x] Integrate weighted action ownership and phase synchronization without breaking existing play/once/stride behavior. proof: `pnpm exec vitest run --maxWorkers=1 packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts packages/core/__tests__/animation-weighted.spec.ts packages/core/__tests__/animation.spec.ts`: 86 pass, exit 0 (2026-10-04). `AnimationPlayer.playWeighted` owns the actions on the existing mixer (18 weighted tests), the canonical spec shows equal Walk/Jog phase with sync on and an independent phase with it off on the real mannequin, and the 51 pre-existing animation tests (play, once, stride) still pass. + - Bounded prerequisite verified on 2026-10-02: returning to a still-contributing loop keeps its weight and playback phase; rapid requests before a frame advances no longer manufacture a full-weight outgoing action. `node node_modules/vitest/vitest.mjs run packages/core/__tests__/animation.spec.ts`: 51 tests passed. Continuous 1D/2D weighting and cross-clip gait phase synchronization are not implemented, so this box remains open. + +### Phase 2 — Two real locomotion consumers + +- [x] Drive a first-person-visible body and a third-person rig through the same weight/phase mechanism with game-owned settings. proof: `pnpm exec vitest run --maxWorkers=1 packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts packages/core/__tests__/animation-weighted.spec.ts packages/core/__tests__/animation.spec.ts packages/core/__tests__/animation-reversal-fixture.spec.ts`: 88 CPU tests pass (17 in the canonical spec), exit 0 (2026-10-04). Both rigs run in one scene (`?vq-locomotion`) through `locomotion-driver.ts` with different game settings; forward-speed axis only, see the 2026-10-04 evidence section. +- [x] Expose selected clips, weights and phase in existing gameplay observations and verify rapid reversals against sampled poses. proof: native `playtests/vq-locomotion-blend-spaces.playtest.json` 23/23 assertions pass, exit 0, 421 frames (per-tick clips, weights, phase and action counts in the state channel; 15 sampled bones bounded between adjacent ticks across 2 passes with rapid reversals; actions settle to 1 and release on dispose), plus the same state assertions passing in the headed browser verifier. Details and images below. + +### Phase 3 — Qualify the visible result + +- [x] The real-rig speed/strafe sweep passes weight, phase and pose-continuity assertions on browser WebGPU. proof: `node --import tsx scripts/verify-vq-locomotion.ts` (the maintained runner's `runStandalonePlaytest`, headed WebGPU, same scenario file, 46/46 assertions) passed locally on an NVIDIA/turing adapter and in hosted CI: the `integration / animation` job passed on source `fd36dd88b3fef798b6c563f326fa9c79286bd246` (run 37269062346, job 111633745242, adapter Google SwiftShader, 12 named frames with 12 distinct SHA-256 values, `correctnessOnly`). The hosted `gait-strafe-right` frame was compared by eye and matches the local one. This is the runner API, not the `cli.js --browser-recipe webgpu` command, because that command selects SwiftShader on this machine and its software-adapter policy rejects it; the hosted run accepts SwiftShader explicitly. +- [x] The identical input trace passes on Linux native; inspect the transition sequence rather than a single still frame. proof: `sh scripts/xvfb.sh node packages/playtest/dist/runner/cli.js playtests/vq-locomotion-blend-spaces.playtest.json --target desktop --executable packages/runtime-native/build/tn-linux/mystral --project examples/abyss-framework --host-arg run --host-arg dist/vq-locomotion-native.js`: pass, 46/46 assertions, 586 frames, 0 diagnostics, exit 0 (2026-10-04, NVIDIA RTX 2080 on Vulkan). **The host binary is the prebuilt 2026-09-26 one, not rebuilt.** The scenario runs the same trace as the browser verifier, including the 2D direction sweep on real strafe and backward clips; twelve named frames were looked at, not a single still: [speed frames](../../verification/vq04/locomotion-native/provenance.json), [direction frames](../../verification/vq04/locomotion-kaykit-native/provenance.json). + +## Acceptance criteria + +- [x] Continuous speed/direction changes require no game-specific replacement mixer, and a same-rig trace produces matching clip weights across the two qualified runtimes. proof: no replacement mixer: all three consumers call one `AnimationPlayer.playWeighted` per frame on the player's own mixer, and the game owns only settings and evaluators (`locomotion-driver.ts`, `locomotion-weights.ts`). Matching across runtimes: fixture `examples/abyss-framework` `?vq-locomotion` / `src/render/vq-locomotion-game.ts`, scenario `playtests/vq-locomotion-blend-spaces.playtest.json`, cohort `@threenative/core` 0.3.3 source at candidate `e81fb686086c834893f2e4e16ff1083f2110b5ee`. The hosted `integration / Linux native locomotion correctness` job (run 37270100498, job 111637417815, ubuntu-24.04-arm, adapter llvmpipe LLVM 20.1.2 on Vulkan, host `mystral` built from that candidate, sha256 `8296f86ed64fb2704a056cc021ff8af352013308b816efde6798b04774c8dceb`) passed, and its per-consumer values equal my local x86 native run (NVIDIA RTX 2080) on all 27 compared metrics, to the last digit (e.g. kaykit `maxWeightError` 2.220446049250313e-16, `maxPhaseGap` 2.7755575615628914e-17, `maxPoseRadians` 0.35919773794635035; third `maxPoseRadians` 0.59229539834217; first `maxPhaseGap` 0.6071428503004886). The browser runtime (headed WebGPU, hosted SwiftShader and local NVIDIA) ran the same 46 assertions and passed with the same bounds. +- [x] The feature preserves its documented inactive/fallback path and releases owned resources after repeated lifecycle transitions. proof: `pnpm exec vitest run --maxWorkers=1 packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts packages/core/__tests__/animation-weighted.spec.ts packages/core/__tests__/animation.spec.ts packages/core/__tests__/animation-reversal-fixture.spec.ts`: 94 pass, exit 0, plus the Phase 3 scenario's lifecycle assertions on both runtimes. **Inactive path:** an `AnimationPlayer` that is never given `playWeighted` takes the unchanged play/crossfade/once/stride path (the 51 pre-existing animation tests still pass); nothing runs for non-participants. **Lifecycle:** each of the three consumers runs its trace twice, settles to one active action, disposes, and reports `disposed: true` and `releasedActions: 0` with `actions.total` 0 afterwards. + +## Performance and promotion + +Count active actions and allocations. Keep nonparticipants at the existing AnimationPlayer cost and document any extra per-rig work. Numerical targets are proposed acceptance targets, not benchmark results. Pin the fixture, metrics and thresholds before changing the implementation; never relax a failing threshold just to mark this PRD complete. Shared abstractions must pass the repository's reuse/LOC admission rule. Appearance remains editable source. + +## Blocked on + +The repository-owned Quaternius CC0 mannequin provides Idle/Walk/Jog/Sprint loops and is now reused for the bounded reversal fixture. Compatible directional clips and two actual game consumers remain open for the full 2D blend-space scope; downloading commercial animations is not assumed. + +## Completion record + +### Bounded interruption repair — 2026-10-02 + +- Engine layer: reuse `AnimationPlayer` and its existing Three mixer; no public API, state machine, clip choices, second updater, dependency or shared blend-space helper was added. Capability search and details resolved `AnimationPlayer`/`SkeletalMesh3D`; the installed Three pin is 0.185.1. +- Red/green: 44 pre-existing animation tests passed. Two new regressions failed first (returning idle weight fell from 0.5 to 0; a zero-frame idle/walk/run sequence summed to 2). The repaired suite passes 51 tests, including sampled real Three bone poses, deterministic repeated reversals, one-shot/mode replay, immediate cuts, fresh/restarted fades and disposal. +- Fresh/fully faded-out clips and zero-duration cuts still restart. Returning loops retain time only while contributing to a requested fade; explicit one-shots and mode changes still restart. +- Opt-in synthetic fixture: `examples/abyss-framework/?animation-reversal`, scenario `examples/abyss-framework/playtests/animation-reversal.playtest.json`. It records nine requests, weight-sum error, zero-time sampled-bone pose/phase jumps and actual mixer disposal counts. It is a regression fixture, not a licensed real-rig locomotion or aesthetic qualification. +- Browser command (attempted, exit 2: no usable X display/Xvfb, so the runner refused to render): `node packages/playtest/dist/runner/cli.js examples/abyss-framework/playtests/animation-reversal.playtest.json --url 'http://127.0.0.1:5184/?animation-reversal' --server-command 'pnpm --filter abyss-framework dev --host 127.0.0.1 --port 5184 --strictPort' --browser-recipe webgpu`. The portable game entry is `examples/abyss-framework/src/render/animation-reversal-game.ts`; the native bundler produced `dist/animation-reversal-native.js` successfully (96 modules). This proves bundle compatibility only; Linux-native execution is unverified. +- Verification: core build (including declarations, bundled MCP and publint), core/example TypeScript, full repository Biome lint (warnings retained), documentation links (2,395), capability manifest (378 entries) and public API surface (352 symbols) passed. `pnpm test` was attempted with pinned pnpm 10.25.0 and stopped at the lifecycle preflight with `tsx` Unix-socket `EPERM`, before its phases. A separate broad Vitest attempt exposed environment/build-prerequisite failures and is not a passing aggregate gate. +- Runtime screenshot evidence is required before readiness. The scenario names transition-frame captures, but no screenshots were produced in this environment; generated images or test logs are not visual proof. +- All original phase and acceptance boxes remain open. Full blend spaces still need the two-consumer/less-code admission and a license-clear real rig; GPU/native proof and human visual review have not been supplied by these CPU tests. + +Update the phase boxes and this PRD only after the named proof runs. Record actual results inline, including any remaining exclusions. A merged planning or implementation PR alone is not proof that every acceptance criterion passed. Archive according to the parent PRD filing rules when the work is genuinely complete. + +### Actual mannequin capture preparation — 2026-10-02 + +- Replaced the synthetic proxy capture with the repository's CC0 `mannequin.glb`, through the real `SkeletalMesh3D`/`AnimationPlayer`. The trace measures all 65 bones across nine Idle/Walk/Jog requests and seven returns to contributing actions. It begins on scenario input and freezes at the requested fixed tick, independent of startup/warmup duration. +- `node node_modules/vitest/vitest.mjs run --maxWorkers=1 packages/core/__tests__/animation-reversal-fixture.spec.ts packages/core/__tests__/animation.spec.ts`: 52 passed. Restoring the pre-fix engine source makes the real-rig check fail (weight error 1). Float32 quaternions are normalized only in diagnostic copies before angular comparison; raw self-angle was 0.000523 radians, not actual pose movement. +- `node --import tsx scripts/verify-animation-reversal.ts --build-only`: the real-rig capture site builds. `tsc --noEmit -p examples/abyss-framework/tsconfig.json` passes. Quality scanning adds no double casts or suppressions. +- Hosted proof target: `Integration animation reversal` runs the maintained public runner on headed WebGPU and records exact source SHA, adapter, per-frame screenshot SHA-256 and measured weights/phases. It rejects errors, missing/non-WebGPU adapters, blank frames and device-loss warnings. Runtime screenshots are pending this job, not claimed by local CPU/build checks. Full blend-space and platform acceptance remains open. + +- First real runtime frame: [diagnostic tick 24](../../verification/vq04/diagnostic-tick24.png), [report](../../verification/vq04/diagnostic-report.json), [source/run/adapter/digests](../../verification/vq04/diagnostic-provenance.json). Source `691e917ebb870445415417c5566025f47c81e6a1`, hosted run `36995945428`, SwiftShader WebGPU. Visually inspected the actual full mannequin and gait; the strict run **failed** on one asset-manifest 404 console error. This is diagnostic progress, not qualified proof. The capture build now writes its actual emitted mannequin manifest, and a silhouette-region brightness bound was added; no 404 is suppressed. + +### Verified real-rig browser screenshots — 2026-10-02 + +- Hosted [run 36996637583](https://github.com/ThreeNativeHQ/threenative/actions/runs/36996637583) **passed** at source `a53db062fec7b1b3ae99268470dfa06d15ee3494`, including 53 animation/fixture tests and the strict headed-WebGPU capture verifier. Adapter: Google SwiftShader; this is rendered correctness, not hardware performance or Linux-native execution. +- Actual immutable PNGs: [tick 24](../../verification/vq04/tick-24.png), [tick 25: return to live idle](../../verification/vq04/tick-25.png), [tick 26](../../verification/vq04/tick-26.png), [tick 48](../../verification/vq04/tick-48.png), [tick 108: settled idle](../../verification/vq04/tick-108.png). Each was visually inspected and copied byte-for-byte from the Actions artifact. All five reports pass with zero diagnostics; the bright mannequin silhouette is bounded separately from the floor/background. +- [Runtime measurements and adapter](../../verification/vq04/runtime-summary.json); [source/run/artifact and per-image SHA-256 provenance](../../verification/vq04/provenance.json). Artifact `11222156451`, ZIP SHA-256 `ba1e0cff8e340330f98096695bd9e84840a3e0c15f5659a00e70a08475c91e08` verified on download. Across 65 bones/nine requests/seven returning actions: maximum weight-sum error `2.220446049250313e-16`; phase and local-position jumps `0`; normalized-quaternion jump `4.2146848510894035e-8` radians. Final weights `[1, 0, 0]`, one active action. +- This closes the bounded reversal screenshot milestone only. Continuous 1D/2D blend spaces, two actual game consumers, compatible-gait cross-clip phase synchronization and native qualification remain open. No original phase or acceptance checkbox is ticked from this narrower proof. + +### Bounded weighted playback prerequisite — 2026-10-04 + +- Preserved the unpublished owner source byte-for-byte before integration (SHA-256 `94527e882fc8f636f28329bc1265225b8f2eae349e89c5726cb60d4370d7d907`); the original owner checkout remains unchanged. Added `AnimationPlayer.playWeighted` to the existing authoritative mixer. Games supply clip weights, transition duration and phase-sync opt-out; no clip selection, numerical evaluator, state graph or extra updater entered core. +- Eighteen focused weighted CPU regressions cover finite normalized weights, malformed input without ownership mutation, overflow, outgoing action cleanup, full-duration ramps, per-frame repeated requests, cold starts, contributing gait phase, zero-duration phase transfer, single-clip handoff, one-shot reactivation and stride synchronization when idle is dominant. Reproductions failed before their repairs. Animation plus real mannequin fixture checks pass **71/71**; no new browser or Linux-native result is claimed for the weighted API. +- Exact published `10823978f2d72635b3dd3b08eb5a01e0e0b65d73`, with unchanged tracked source, the same locked dependency topology and its own fresh core build, reproduces inherited root typecheck failures: missing fixture-local Vite URL types and a core/playtest source-versus-dist type identity mismatch. A local Vite type reference and the explicit public `@threenative/core/playtest` source mapping (already used by develop) repair these. Two resolution tests require the source cohort while preserving the private core export boundary; no wildcard alias or package export change was added. +- `pnpm typecheck` passes the root and recursive package/example checks. Core build/declarations/MCP bundles/publint pass. `pnpm exec vitest run --maxWorkers=1 packages/core/__tests__/animation-weighted.spec.ts packages/core/__tests__/animation.spec.ts packages/core/__tests__/animation-reversal-fixture.spec.ts scripts/__tests__/tsconfig-resolution.spec.ts packages/core/__tests__/constraints.spec.ts packages/core/__tests__/build.spec.ts`: **80/80 CPU tests pass**. `pnpm --filter threenative-native-smoke test`: four tests and the one-file/import-free JS bundle contract pass; this runs no native executable. Independent review accepts the bounded code/config delta and separately reran 73 focused tests. +- All six original phase and two acceptance criteria remain open. Numerical 1D/2D evaluation, two actual consumers, real weighted speed/direction pose qualification and identical browser/native input traces remain outstanding. The earlier screenshots qualify only the earlier interruption repair. This PR remains draft. Current develop integration is held for conflicts in the integration workflow and relocated canonical PRD; neither conflict was overwritten. + +### Game-owned numerical evaluator slice — 2026-10-04 + +- `examples/abyss-framework/src/render/locomotion-weights.ts` supplies authored sorted 1D intervals with endpoint clamping and a declared non-overlapping triangular 2D domain with barycentric weights and nearest-edge projection outside it. Boundary ties follow declaration order; authored samples are snapshotted. Duplicate clip names/coordinates, malformed tuples, unused vertices, unsorted intervals, overlapping/degenerate domains and unrepresentable numerical inputs fail explicitly. Domain epsilon is 1e-12 after scaling coordinates by the largest authored absolute coordinate. This is game policy, not a new core export or shared engine numerical abstraction. +- Tests were written before implementation; the endpoint-only baseline failed all nine original cases. A contained-domain regression failed before symmetric validation; independent review exposed malformed tuples, which now fail at construction. The corrected extra-index regression demonstrably fails when only the exact triangle-length guard is reverted. Final canonical suite passes **11/11**; expanded existing animation, weighted, mannequin, core export/boundary and resolution checks pass **91/91 CPU tests**. Focused lint passes without warnings; independent fresh review accepted the bounded evaluator/test delta and reran 11/11. +- The actual CC0 GLB has seven clips: Idle_Loop, Walk_Loop, Jog_Fwd_Loop, Sprint_Loop, Jump_Start, Jump_Loop and Jump_Land. Eleven-test CPU proof includes actual 65-bone Idle/Walk/Jog requests, unchanged request-time sampled poses/contributing action times, repeated per-frame evaluator input, normalized live weights, bounded action counts and disposal through the existing authoritative player. Numerical 2D labels are synthetic domain samples, not evidence of directional animation assets. No forward clip is relabeled as a strafe. +- A first-person-visible body and a third-person locomotion consumer are still absent; existing FPS Range is a synthetic patroller and abyss capture remains a reversal fixture. Compatible licensed left/right/backward gait clips remain unavailable in this GLB. Shared numerical helpers still require actual two-consumer/less-code admission. Browser/native identical speed/strafe traces, allocations/performance qualification and repeated visible lifecycle evidence remain open. No new GPU or native execution occurred; only the first numerical phase checkbox is complete. + +- Progress calculator discrepancy: actual counts are 1/6 phase items (16.7%) and 0/2 acceptance, or 1/8 total items (12.5%); no full phase is complete. The existing `scripts/prd-progress.ts` ignores acceptance until readiness and maps any positive phase ratio below 0.5 to the coarse `prd:25%` bucket. Its printed label is not an exact completion percentage. The existing PR percentage label remains unchanged pending the coordinator’s policy correction; no manual percentage was rounded or applied. + + +## Develop conflict reconciliation — 2026-10-04 + +- Merged develop `15adf350da53addfa33675c2b3f0e2722e086e37` into published `f95b4ffab0b842dc9a851ab10c719345ec67a6d6`. Accepted the canonical animation PRD location while retaining the original checklist, partial status and historical evidence. The removed batch execution instructions remain linked at their immutable published commit. +- Integration coverage retains every develop lane and the original animation capture job, with weighted playback and authored evaluator CPU suites added. Animation routing uses the current exact-base/head selector; new routing regressions failed twice with develop alone and pass after the union. Focused routing/workflow/animation/mannequin verification: **347/347 CPU tests pass**. This base update adds no consumer or platform acceptance and runs no GPU/native executable. + +### Game-owned locomotion driver and directional-clip search — 2026-10-04 + +- `examples/abyss-framework/src/render/locomotion-driver.ts` turns a speed (and a direction only when the game authored a directional domain) into weights through `locomotion-weights.ts` and one `AnimationPlayer.playWeighted` call. A direction request with no authored directional clips throws. Two settings objects (third-person with sprint and phase sync, first-person body without sprint or sync) are placeholders, not tuned values. No core change. +- `pnpm exec vitest run --maxWorkers=1 packages/core/__tests__/vq-locomotion-blend-spaces.spec.ts packages/core/__tests__/animation-weighted.spec.ts packages/core/__tests__/animation.spec.ts`: 86 CPU tests pass (17 in the canonical spec, up from 11). Red first: the module absent collected 0 tests, and a speed-only partial module failed 3. Lint and the abyss-framework typecheck pass. +- **No Phase 2 box is ticked.** The two settings are exercised only by unit tests on the real mannequin. No first-person-visible or third-person consumer runs in a scene yet, and no observation reaches the gameplay bridge. +- Directional clips: none exist under a usable license. Checked on 2026-10-04: repo `mannequin.glb` and `mannequin-combat.glb`, Quaternius UAL1 (43 clips) and UAL2 (43 clips) through `asset_list_bundle_animations`, 17,606 local GLB/glTF files, and the asset MCP source search. Fab "Game Animation Sample" is owned but is Unreal Engine-only content under the UE EULA, so it cannot enter this repository. The 2D evaluator therefore has no real directional clips to qualify against. Phase 3 and both acceptance boxes stay open. + +### Two consumers in one scene, native and browser runs — 2026-10-04 + +- Fixture: opt-in `index.html?vq-locomotion` (`src/vq-locomotion-main.ts`, `src/render/vq-locomotion-game.ts`, `vq-locomotion-trace.ts`) runs a third-person rig and a first-person body from the CC0 `mannequin.glb`, now a project asset loaded by name (the first version used a `?url` import that the native bundler inlined and the host refused with `TN_ASSETS_UNRESOLVED`). One scripted speed trace runs twice. No directional input is ever supplied. +- Native (Linux desktop, NVIDIA GeForce RTX 2080 on Vulkan, private Xvfb): `sh scripts/xvfb.sh node packages/playtest/dist/runner/cli.js playtests/vq-locomotion-blend-spaces.playtest.json --target desktop --executable packages/runtime-native/build/tn-linux/mystral --project examples/abyss-framework --host-arg run --host-arg dist/vq-locomotion-native.js` passes 23/23, 421 frames, 0 diagnostics, reproduced three times (two by the delegated run, one by me). **The `mystral` host is the prebuilt 2026-09-26 binary, stale against this source**; no fresh `native:build` was run. Six mid-trace frames: [`docs/verification/vq04/locomotion-native/`](../../verification/vq04/locomotion-native/provenance.json). +- Browser: `node --import tsx scripts/verify-vq-locomotion.ts` (real build, headed WebGPU, `allowSoftwareAdapter`) passes with an NVIDIA/turing adapter, six distinct frames: [`docs/verification/vq04/locomotion-web/`](../../verification/vq04/locomotion-web/summary.json). Hosted CI runs the same script in the `animation` job; its result is not yet recorded here. The plain `playtest cli.js --url ... --browser-recipe webgpu` command on this machine selects SwiftShader and fails the software-adapter and blank-capture diagnostics, so Phase 3's browser box is not ticked from it. +- Measured on both runtimes (identical to the digit): third-person `maxWeightError` 2.22e-16, `maxPhaseGap` 1.11e-16 (sync on); first-person `maxPhaseGap` 0.607 (sync off); `maxPoseRadians` 0.5923 / 0.5317; `maxPoseMetres` 0.1875 / 0.1519; `maxActiveActions` 4 / 3. The active-action bound of 4 is wider than the unit tests' 2 because the per-tick ramp keeps fading clips live across a sprint-to-idle sweep; it is pinned in the scenario, not relaxed after a failure. The pose bound 0.65 rad is Sprint_Loop's own authored per-tick rotation (0.5923) plus margin. +- Looked at the frames, not only the numbers: the first version framed the first-person camera inside the rig's own neck (a wall of white mannequin) while every numeric assertion passed. The camera is now forward of the neck and pitched 46 degrees down; frames show the body's own hands, forearms and a swinging leg, plus the third-person rig running away. Scrollbars appear in the browser frames (page layout), a cosmetic defect left open. Human visual review is still required. +- **Still open:** no directional (strafe/backward) clips exist, so the 2D domain has no real clips and the Phase 3 "speed/strafe sweep" cannot be asserted; both Phase 3 boxes and both acceptance boxes stay open. Native proof ran on a stale host and no fresh native build. + +### Real directional clips and the 2D consumer — 2026-10-04 + +- **Correction to the earlier entry:** directional clips do exist under a usable license. The CC0 KayKit Character Animations 1.1 pack (itch.io, free, `License.txt` and the page both say CC0) has `Running_Strafe_Left`, `Running_Strafe_Right` and `Walking_Backwards` on `Rig_Medium`, with its own Mannequin on the same skeleton, so nothing is retargeted or derived. Unmodified files, source, SHA-256 values and clip list: [`docs/verification/vq04/kaykit-provenance.md`](../../verification/vq04/kaykit-provenance.md). Searches that missed it: Quaternius UAL1/UAL2, the repo templates, 17,606 local GLB/glTF files and the asset MCP (it catalogues only the KayKit Platformer pack). The OpenGameArt KayKit 1.0/1.2 is the older Prototype Pete pack and has no gait loops. +- Third consumer in the same scene: the KayKit mannequin driven by `KAYKIT_DIRECTIONAL` (five real samples: Idle_A, Running_A forward, Running_Strafe_Right, Walking_Backwards, Running_Strafe_Left; four-triangle fan) through the same `createLocomotionDriver`. One scripted direction trace: axes, diagonals, a slow sweep and one-tick reversals, run twice. +- Proof, identical on native and in the headed browser verifier: 46/46 assertions, `maxWeightError` 2.22e-16, `maxPhaseGap` 2.78e-17, pose bounds derived from the clips' own measured per-tick motion (0.3592 rad and 0.0757 m after the 0.7 display scale, 0.1082 m before it; bounds 0.18-0.5 rad and 0.05-0.15 m, actions 3-6), dominant clip per pose asserted (forward Running_A, right Running_Strafe_Right, back Walking_Backwards, left Running_Strafe_Left, idle Idle_A, diagonals blend the two neighbours at 0.5). Unit specs: 94 pass (6 new, red first). +- Phase sync is data-driven, not assumed: a spec measures a foot gait marker per clip. Running_A, Walking_A, Walking_Backwards and Running_Strafe_Left align within 0.004 of the cycle, Running_Strafe_Right is 0.041 off (33 ms of its 0.8 s cycle), inside the 0.05 tolerance the spec asserts. The ankle-lowest marker disagrees by up to 0.48 between Running_A and Walking_Backwards, so phase sync across forward and backward is a known approximation. +- **Authored facts the frames show:** the strafe clips carry a root yaw of +60 and -60 degrees (chest -17 and +17), and Walking_Backwards a 32 degree spine lean, so the body is visibly angled rather than square in the strafe frames. That is the pack's animation, not a bug, and a game that wants a square torso owns that correction. +- Frames were looked at with a second camera (`KeyC` toggles a front three-quarter gait view at the start of the second pass; the first-person view stays the default). The KayKit rig is shown at 0.7 scale with a non-metallic material so it reads in the evidence; both are presentation choices in the fixture. +- Extra per-rig cost: the driver and evaluators allocate small arrays per call; no allocation measurement was taken, so no zero-allocation claim is made. Active actions peak at 4 / 3 / 5 for the three consumers. +- **Native host:** the local runs used the prebuilt 2026-09-26 host; the hosted `animation-native` job builds the candidate's own host and its result is recorded in the last acceptance box. +- **Not done:** a human aesthetic review of the frames, which this PRD's boxes do not require but the frames in `docs/verification/vq04/` make possible. diff --git a/docs/verification/vq04/diagnostic-provenance.json b/docs/verification/vq04/diagnostic-provenance.json new file mode 100644 index 0000000000..feabc0d3a3 --- /dev/null +++ b/docs/verification/vq04/diagnostic-provenance.json @@ -0,0 +1,33 @@ +{ + "sourceSha": "691e917ebb870445415417c5566025f47c81e6a1", + "run": "https://github.com/ThreeNativeHQ/threenative/actions/runs/36995945428", + "artifactId": 11222300460, + "artifactZipSha256": "e43590deff385057a5f3ed547e8f444ce2d6949fd9b71fdc3b4c7113504a8d17", + "screenshotSha256": "26de9efac6b525b43efbf5f525db11a8bb7e2693f77b3cbb7712c180d289fb79", + "capture": { + "adapter": { + "architecture": "swiftshader", + "vendor": "google", + "features": "bgra8unorm-storage,chromium-experimental-timestamp-query-inside-passes,clip-distances,core-features-and-limits,depth-clip-control,depth32float-stencil8,float32-blendable,float32-filterable,indirect-first-instance,rg11b10ufloat-renderable,subgroup-size-control,subgroups,texture-component-swizzle,texture-compression-astc,texture-compression-astc-sliced-3d,texture-compression-bc,texture-compression-bc-sliced-3d,texture-compression-etc2,timestamp-query", + "limit.maxBindGroups": "4", + "limit.maxTextureDimension2D": "8192", + "limit.maxStorageBufferBindingSize": "1073741824" + }, + "browserArgs": [ + "--ozone-platform=x11", + "--enable-unsafe-webgpu", + "--disable-gpu-sandbox", + "--ignore-gpu-blocklist", + "--enable-features=Vulkan" + ], + "captureMethod": "page.screenshot", + "rendererKind": "webgpu", + "target": "web", + "viewport": { + "height": 720, + "width": 960 + } + }, + "pass": false, + "status": "Diagnostic only: actual mannequin render at tick 24; rejected for one asset-manifest 404 console error. Not a qualified runtime pass." +} diff --git a/docs/verification/vq04/diagnostic-report.json b/docs/verification/vq04/diagnostic-report.json new file mode 100644 index 0000000000..acabfbbde7 --- /dev/null +++ b/docs/verification/vq04/diagnostic-report.json @@ -0,0 +1,1712 @@ +{ + "sourceSha": "691e917ebb870445415417c5566025f47c81e6a1", + "capture": { + "adapter": { + "architecture": "swiftshader", + "vendor": "google", + "features": "bgra8unorm-storage,chromium-experimental-timestamp-query-inside-passes,clip-distances,core-features-and-limits,depth-clip-control,depth32float-stencil8,float32-blendable,float32-filterable,indirect-first-instance,rg11b10ufloat-renderable,subgroup-size-control,subgroups,texture-component-swizzle,texture-compression-astc,texture-compression-astc-sliced-3d,texture-compression-bc,texture-compression-bc-sliced-3d,texture-compression-etc2,timestamp-query", + "limit.maxBindGroups": "4", + "limit.maxTextureDimension2D": "8192", + "limit.maxStorageBufferBindingSize": "1073741824" + }, + "browserArgs": [ + "--ozone-platform=x11", + "--enable-unsafe-webgpu", + "--disable-gpu-sandbox", + "--ignore-gpu-blocklist", + "--enable-features=Vulkan" + ], + "captureMethod": "page.screenshot", + "rendererKind": "webgpu", + "target": "web", + "viewport": { + "height": 720, + "width": 960 + } + }, + "diagnostics": [ + { + "code": "TN_PLAYTEST_CONSOLE_ERROR", + "message": "1 browser console error(s) were captured during playtest.", + "severity": "error", + "suggestion": "Open console.json in the playtest artifact directory and fix the first runtime error." + } + ], + "diagnosticsPolicy": { + "noConsoleErrors": true, + "noNetworkErrors": true, + "noRuntimeDiagnostics": true, + "runtimeReady": true + }, + "distance": 0, + "entity": "", + "expectMoved": false, + "frames": 121, + "trivialityOptOuts": [], + "observations": { + "components": { + "locomotion": { + "tick": { + "before": 0, + "after": 24 + }, + "reversals": { + "before": 0, + "after": 2 + }, + "returningRequests": { + "before": 0, + "after": 0 + }, + "bones": { + "before": 65, + "after": 65 + }, + "current": { + "before": "Idle_Loop", + "after": "Jog_Fwd_Loop" + }, + "weights": { + "before": [ + 1, + 0, + 0 + ], + "after": [ + 0.28117766203703715, + 0.28117766203703703, + 0.43764467592592576 + ] + }, + "phases": { + "before": [ + 0.08, + 0, + 0 + ], + "after": [ + 0.24000000000000007, + 0.2874999914318325, + 0.19642857059228175 + ] + }, + "activeActions": { + "before": 1, + "after": 3 + }, + "maxWeightError": { + "before": 0, + "after": 0 + }, + "maxPoseJumpMetres": { + "before": 0, + "after": 0 + }, + "maxPoseJumpRadians": { + "before": 0, + "after": 4.2146848510894035e-8 + }, + "maxPhaseJump": { + "before": 0, + "after": 0 + }, + "maxAnimatedRadians": { + "before": 0, + "after": 1.1754697662269058 + } + } + }, + "componentSeries": [ + { + "label": "start-matched-trace", + "snapshots": { + "locomotion": { + "tick": 2, + "reversals": 1, + "returningRequests": 0, + "bones": 65, + "current": "Walk_Loop", + "weights": [ + 0.9949363425925926, + 0.0050636574074074065, + 0 + ], + "phases": [ + 0.09333333333333334, + 0.012499999627470981, + 0 + ], + "activeActions": 2, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 0.012193110049471701 + } + }, + "tick": 62 + }, + { + "label": "hold-capture-tick", + "snapshots": { + "locomotion": { + "tick": 24, + "reversals": 2, + "returningRequests": 0, + "bones": 65, + "current": "Jog_Fwd_Loop", + "weights": [ + 0.28117766203703715, + 0.28117766203703703, + 0.43764467592592576 + ], + "phases": [ + 0.24000000000000007, + 0.2874999914318325, + 0.19642857059228175 + ], + "activeActions": 3, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + } + }, + "tick": 182 + } + ], + "console": [ + { + "source": "browser-console", + "text": "Failed to load resource: the server responded with a status of 404 (Not Found)", + "type": "error" + }, + { + "source": "browser-console", + "text": "TN_RENDER_PROJECTION:{\"projecting\":false,\"reasonCode\":\"belowMeshFloor\",\"reason\":\"fewer than 200 batchable meshes; the mirror would cost more than it saves\",\"sourceRenderables\":0,\"resultDrawCandidates\":0,\"batches\":0,\"instancedBatches\":0,\"materialBatches\":0,\"skinnedBatches\":0,\"projectedObjects\":0,\"exactObjects\":0,\"exact\":{},\"materialChecks\":\"spread\",\"materialChecksPerFrame\":512,\"materialCheckStaleFrames\":0}", + "type": "info" + }, + { + "source": "browser-console", + "text": "THREE.WebGPURenderer: Timestamp tracking is disabled.", + "type": "warning" + }, + { + "source": "browser-console", + "text": "TN_ANIMATION_REVERSAL:{\"tick\":24,\"reversals\":2,\"returningRequests\":0,\"bones\":65,\"current\":\"Jog_Fwd_Loop\",\"weights\":[0.28117766203703715,0.28117766203703703,0.43764467592592576],\"phases\":[0.24000000000000007,0.2874999914318325,0.19642857059228175],\"activeActions\":3,\"maxWeightError\":0,\"maxPoseJumpMetres\":0,\"maxPoseJumpRadians\":4.2146848510894035e-8,\"maxPhaseJump\":0,\"maxAnimatedRadians\":1.1754697662269058}", + "type": "log" + } + ], + "hud": {}, + "network": [], + "pipelineCensus": { + "version": 1, + "complete": true, + "overflowed": false, + "unsupported": false, + "limit": 512, + "clock": { + "originMs": 1618.6999999999534, + "source": "performance" + }, + "backend": { + "kind": "webgpu", + "identity": "webgpu:vN" + }, + "build": { + "identity": "@threenative/core@0.3.4" + }, + "adapter": { + "identity": "webgpu:architecture=swiftshader|vendor=google", + "thermal": "unavailable" + }, + "firstPresent": { + "boundaryMs": 46.70000000001164, + "eventsSettled": 1 + }, + "counts": { + "lookups": 3, + "creations": 3, + "failures": 0, + "pending": 0, + "uniquePrograms": 3, + "uniquePipelines": 3, + "recordedEvents": 3, + "droppedEvents": 0, + "deviceCreations": 3, + "directCreations": 0 + }, + "events": [ + { + "sequence": 1, + "programIdentity": "00fc0a8ad03630cc/458120a84b226496", + "pipelineIdentity": "backend-pipeline-1", + "kind": "render", + "pass": "output", + "mode": "sync", + "status": "created", + "vertex": { + "bytes": 1299, + "hash": "00fc0a8ad03630cc" + }, + "fragment": { + "bytes": 1595, + "hash": "458120a84b226496" + }, + "provenance": { + "material": { + "id": 15, + "name": "outputColorTransform", + "type": "NodeMaterial" + }, + "object": { + "id": 17, + "name": "Output Color Transform", + "uuid": "494e2b23-1d13-4933-8590-077525ae8e90" + }, + "unknown": false + }, + "reasons": [ + "unknown-structural-difference" + ], + "startedMs": 45.20000000001164, + "settledMs": 46, + "serviceMs": 0.7999999999883585, + "beforeFirstPresent": true + }, + { + "sequence": 2, + "programIdentity": "17e2284eb3b1d919/2b7406b23b4323a4", + "pipelineIdentity": "backend-pipeline-2", + "kind": "render", + "pass": "main-clipped", + "mode": "sync", + "status": "created", + "vertex": { + "bytes": 3011, + "hash": "17e2284eb3b1d919" + }, + "fragment": { + "bytes": 14272, + "hash": "2b7406b23b4323a4" + }, + "provenance": { + "material": { + "id": 18, + "name": "M_Main", + "type": "MeshStandardMaterial" + }, + "object": { + "id": 96, + "name": "Mannequin_1", + "uuid": "aabaa977-06e1-4141-9942-eef1ad661626" + }, + "unknown": false + }, + "reasons": [ + "skinning" + ], + "startedMs": 362.80000000004657, + "settledMs": 363, + "serviceMs": 0.19999999995343387, + "beforeFirstPresent": false + }, + { + "sequence": 3, + "programIdentity": "ab7b80f17cceb339/c189fec47374fa64", + "pipelineIdentity": "backend-pipeline-3", + "kind": "render", + "pass": "main-clipped", + "mode": "sync", + "status": "created", + "vertex": { + "bytes": 1953, + "hash": "ab7b80f17cceb339" + }, + "fragment": { + "bytes": 14203, + "hash": "c189fec47374fa64" + }, + "provenance": { + "material": { + "id": 20, + "type": "MeshStandardMaterial" + }, + "object": { + "id": 92, + "uuid": "d413e3c1-1418-4559-8e81-e5795a8b291e" + }, + "unknown": false + }, + "reasons": [ + "unknown-structural-difference" + ], + "startedMs": 385.4000000000233, + "settledMs": 385.6000000000349, + "serviceMs": 0.20000000001164153, + "beforeFirstPresent": false + } + ], + "incompleteReasons": [] + }, + "resources": { + "state": { + "before": {}, + "after": { + "tick": 24, + "reversals": 2, + "returningRequests": 0, + "bones": 65, + "current": "Jog_Fwd_Loop", + "weights": [ + 0.28117766203703715, + 0.28117766203703703, + 0.43764467592592576 + ], + "phases": [ + 0.24000000000000007, + 0.2874999914318325, + 0.19642857059228175 + ], + "activeActions": 3, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + } + }, + "GameState": { + "before": {}, + "after": { + "tick": 24, + "reversals": 2, + "returningRequests": 0, + "bones": 65, + "current": "Jog_Fwd_Loop", + "weights": [ + 0.28117766203703715, + 0.28117766203703703, + 0.43764467592592576 + ], + "phases": [ + 0.24000000000000007, + 0.2874999914318325, + 0.19642857059228175 + ], + "activeActions": 3, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + } + }, + "assets": { + "before": { + "/assets/mannequin-BQQTERB4": { + "glb": { + "url": "/assets/mannequin-BQQTERB4.glb", + "via": "source" + } + } + }, + "after": { + "/assets/mannequin-BQQTERB4": { + "glb": { + "url": "/assets/mannequin-BQQTERB4.glb", + "via": "source" + } + } + } + } + }, + "resourceSeries": [ + { + "label": "start-matched-trace", + "snapshots": { + "state": { + "tick": 2, + "reversals": 1, + "returningRequests": 0, + "bones": 65, + "current": "Walk_Loop", + "weights": [ + 0.9949363425925926, + 0.0050636574074074065, + 0 + ], + "phases": [ + 0.09333333333333334, + 0.012499999627470981, + 0 + ], + "activeActions": 2, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 0.012193110049471701 + }, + "GameState": { + "tick": 2, + "reversals": 1, + "returningRequests": 0, + "bones": 65, + "current": "Walk_Loop", + "weights": [ + 0.9949363425925926, + 0.0050636574074074065, + 0 + ], + "phases": [ + 0.09333333333333334, + 0.012499999627470981, + 0 + ], + "activeActions": 2, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 0.012193110049471701 + }, + "assets": { + "/assets/mannequin-BQQTERB4": { + "glb": { + "url": "/assets/mannequin-BQQTERB4.glb", + "via": "source" + } + } + } + }, + "tick": 62 + }, + { + "label": "hold-capture-tick", + "snapshots": { + "state": { + "tick": 24, + "reversals": 2, + "returningRequests": 0, + "bones": 65, + "current": "Jog_Fwd_Loop", + "weights": [ + 0.28117766203703715, + 0.28117766203703703, + 0.43764467592592576 + ], + "phases": [ + 0.24000000000000007, + 0.2874999914318325, + 0.19642857059228175 + ], + "activeActions": 3, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + }, + "GameState": { + "tick": 24, + "reversals": 2, + "returningRequests": 0, + "bones": 65, + "current": "Jog_Fwd_Loop", + "weights": [ + 0.28117766203703715, + 0.28117766203703703, + 0.43764467592592576 + ], + "phases": [ + 0.24000000000000007, + 0.2874999914318325, + 0.19642857059228175 + ], + "activeActions": 3, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + }, + "assets": { + "/assets/mannequin-BQQTERB4": { + "glb": { + "url": "/assets/mannequin-BQQTERB4.glb", + "via": "source" + } + } + } + }, + "tick": 182 + } + ], + "runtimeDiagnostics": { + "recentRuntimeErrors": [], + "runtimeReadouts": [], + "scene": { + "renderedEntities": [] + } + }, + "runtimeDiagnosticsBefore": { + "recentRuntimeErrors": [], + "runtimeReadouts": [], + "scene": { + "renderedEntities": [] + } + }, + "runtimeObservations": { + "gameplayBefore": { + "animation": { + "locomotion": { + "advancedFrames": 1, + "clip": "Idle_Loop", + "finished": false, + "stride": { + "clipGroundSpeed": 0, + "groundSpeed": 0, + "overridden": false, + "rate": 1, + "synced": false + } + } + }, + "contacts": [], + "states": {}, + "tags": {}, + "transitions": [], + "audio": { + "cues": {}, + "paused": 0, + "pooled": 0, + "queued": 0, + "recentCues": [], + "unsupported": [], + "voices": 0 + }, + "world": { + "seed": null + } + }, + "gameplay": { + "animation": { + "locomotion": { + "advancedFrames": 11, + "clip": "Jog_Fwd_Loop", + "finished": false, + "stride": { + "clipGroundSpeed": 5.980103862694172, + "groundSpeed": 0, + "overridden": true, + "rate": 0.15, + "synced": false + } + } + }, + "contacts": [], + "states": {}, + "tags": {}, + "transitions": [ + { + "from": 1, + "path": "state.tick", + "tick": 61, + "to": 2 + }, + { + "from": 0.005792785394270303, + "path": "state.maxAnimatedRadians", + "tick": 61, + "to": 0.012193110049471701 + }, + { + "from": 2, + "path": "state.tick", + "tick": 62, + "to": 3 + }, + { + "from": 0.012193110049471701, + "path": "state.maxAnimatedRadians", + "tick": 62, + "to": 0.023630910323673586 + }, + { + "from": 3, + "path": "state.tick", + "tick": 63, + "to": 4 + }, + { + "from": 0.023630910323673586, + "path": "state.maxAnimatedRadians", + "tick": 63, + "to": 0.042515006394386566 + }, + { + "from": 4, + "path": "state.tick", + "tick": 64, + "to": 5 + }, + { + "from": 0.042515006394386566, + "path": "state.maxAnimatedRadians", + "tick": 64, + "to": 0.069017568925387 + }, + { + "from": 5, + "path": "state.tick", + "tick": 65, + "to": 6 + }, + { + "from": 0.069017568925387, + "path": "state.maxAnimatedRadians", + "tick": 65, + "to": 0.10258743618987531 + }, + { + "from": 6, + "path": "state.tick", + "tick": 66, + "to": 7 + }, + { + "from": 0.10258743618987531, + "path": "state.maxAnimatedRadians", + "tick": 66, + "to": 0.14337166326964224 + }, + { + "from": 7, + "path": "state.tick", + "tick": 67, + "to": 8 + }, + { + "from": 0.14337166326964224, + "path": "state.maxAnimatedRadians", + "tick": 67, + "to": 0.19119098805466825 + }, + { + "from": 8, + "path": "state.tick", + "tick": 68, + "to": 9 + }, + { + "from": 0.19119098805466825, + "path": "state.maxAnimatedRadians", + "tick": 68, + "to": 0.2461860570872399 + }, + { + "from": 9, + "path": "state.tick", + "tick": 69, + "to": 10 + }, + { + "from": 0.2461860570872399, + "path": "state.maxAnimatedRadians", + "tick": 69, + "to": 0.30833331170158496 + }, + { + "from": 10, + "path": "state.tick", + "tick": 70, + "to": 11 + }, + { + "from": 0.30833331170158496, + "path": "state.maxAnimatedRadians", + "tick": 70, + "to": 0.3772934859560291 + }, + { + "from": 11, + "path": "state.tick", + "tick": 71, + "to": 12 + }, + { + "from": 0.3772934859560291, + "path": "state.maxAnimatedRadians", + "tick": 71, + "to": 0.45590555415239853 + }, + { + "from": 12, + "path": "state.tick", + "tick": 72, + "to": 13 + }, + { + "from": 1, + "path": "state.reversals", + "tick": 72, + "to": 2 + }, + { + "from": "Walk_Loop", + "path": "state.current", + "tick": 72, + "to": "Jog_Fwd_Loop" + }, + { + "from": 2, + "path": "state.activeActions", + "tick": 72, + "to": 3 + }, + { + "from": 0.45590555415239853, + "path": "state.maxAnimatedRadians", + "tick": 72, + "to": 0.5418354950461067 + }, + { + "from": 13, + "path": "state.tick", + "tick": 73, + "to": 14 + }, + { + "from": 0.5418354950461067, + "path": "state.maxAnimatedRadians", + "tick": 73, + "to": 0.5772320250832106 + }, + { + "from": 14, + "path": "state.tick", + "tick": 74, + "to": 15 + }, + { + "from": 0.5772320250832106, + "path": "state.maxAnimatedRadians", + "tick": 74, + "to": 0.6193384045607399 + }, + { + "from": 15, + "path": "state.tick", + "tick": 75, + "to": 16 + }, + { + "from": 0.6193384045607399, + "path": "state.maxAnimatedRadians", + "tick": 75, + "to": 0.6697003860109736 + }, + { + "from": 16, + "path": "state.tick", + "tick": 76, + "to": 17 + }, + { + "from": 0.6697003860109736, + "path": "state.maxAnimatedRadians", + "tick": 76, + "to": 0.7334745970750475 + }, + { + "from": 17, + "path": "state.tick", + "tick": 77, + "to": 18 + }, + { + "from": 0.7334745970750475, + "path": "state.maxAnimatedRadians", + "tick": 77, + "to": 0.8067859961565446 + }, + { + "from": 18, + "path": "state.tick", + "tick": 78, + "to": 19 + }, + { + "from": 0.8067859961565446, + "path": "state.maxAnimatedRadians", + "tick": 78, + "to": 0.8987612471462776 + }, + { + "from": 19, + "path": "state.tick", + "tick": 79, + "to": 20 + }, + { + "from": 0.8987612471462776, + "path": "state.maxAnimatedRadians", + "tick": 79, + "to": 0.9847201686454872 + }, + { + "from": 20, + "path": "state.tick", + "tick": 80, + "to": 21 + }, + { + "from": 0.9847201686454872, + "path": "state.maxAnimatedRadians", + "tick": 80, + "to": 1.0835685049384338 + }, + { + "from": 21, + "path": "state.tick", + "tick": 81, + "to": 22 + }, + { + "from": 1.0835685049384338, + "path": "state.maxAnimatedRadians", + "tick": 81, + "to": 1.1328452613571511 + }, + { + "from": 22, + "path": "state.tick", + "tick": 82, + "to": 23 + }, + { + "from": 1.1328452613571511, + "path": "state.maxAnimatedRadians", + "tick": 82, + "to": 1.1748035702072492 + }, + { + "from": 23, + "path": "state.tick", + "tick": 83, + "to": 24 + }, + { + "from": 1.1748035702072492, + "path": "state.maxAnimatedRadians", + "tick": 83, + "to": 1.1754697662269058 + } + ], + "audio": { + "cues": {}, + "paused": 0, + "pooled": 0, + "queued": 0, + "recentCues": [], + "unsupported": [], + "voices": 0 + }, + "world": { + "seed": null + } + }, + "gameplaySeries": [ + { + "gameplay": { + "animation": { + "locomotion": { + "advancedFrames": 1, + "clip": "Walk_Loop", + "finished": false, + "stride": { + "clipGroundSpeed": 0.9797428906622734, + "groundSpeed": 0, + "overridden": true, + "rate": 0.15, + "synced": false + } + } + }, + "contacts": [], + "states": {}, + "tags": {}, + "transitions": [ + { + "from": 1, + "path": "state.tick", + "tick": 61, + "to": 2 + }, + { + "from": 0.005792785394270303, + "path": "state.maxAnimatedRadians", + "tick": 61, + "to": 0.012193110049471701 + } + ], + "audio": { + "cues": {}, + "paused": 0, + "pooled": 0, + "queued": 0, + "recentCues": [], + "unsupported": [], + "voices": 0 + }, + "world": { + "seed": null + } + }, + "label": "start-matched-trace", + "tick": 62 + }, + { + "gameplay": { + "animation": { + "locomotion": { + "advancedFrames": 11, + "clip": "Jog_Fwd_Loop", + "finished": false, + "stride": { + "clipGroundSpeed": 5.980103862694172, + "groundSpeed": 0, + "overridden": true, + "rate": 0.15, + "synced": false + } + } + }, + "contacts": [], + "states": {}, + "tags": {}, + "transitions": [ + { + "from": 1, + "path": "state.tick", + "tick": 61, + "to": 2 + }, + { + "from": 0.005792785394270303, + "path": "state.maxAnimatedRadians", + "tick": 61, + "to": 0.012193110049471701 + }, + { + "from": 2, + "path": "state.tick", + "tick": 62, + "to": 3 + }, + { + "from": 0.012193110049471701, + "path": "state.maxAnimatedRadians", + "tick": 62, + "to": 0.023630910323673586 + }, + { + "from": 3, + "path": "state.tick", + "tick": 63, + "to": 4 + }, + { + "from": 0.023630910323673586, + "path": "state.maxAnimatedRadians", + "tick": 63, + "to": 0.042515006394386566 + }, + { + "from": 4, + "path": "state.tick", + "tick": 64, + "to": 5 + }, + { + "from": 0.042515006394386566, + "path": "state.maxAnimatedRadians", + "tick": 64, + "to": 0.069017568925387 + }, + { + "from": 5, + "path": "state.tick", + "tick": 65, + "to": 6 + }, + { + "from": 0.069017568925387, + "path": "state.maxAnimatedRadians", + "tick": 65, + "to": 0.10258743618987531 + }, + { + "from": 6, + "path": "state.tick", + "tick": 66, + "to": 7 + }, + { + "from": 0.10258743618987531, + "path": "state.maxAnimatedRadians", + "tick": 66, + "to": 0.14337166326964224 + }, + { + "from": 7, + "path": "state.tick", + "tick": 67, + "to": 8 + }, + { + "from": 0.14337166326964224, + "path": "state.maxAnimatedRadians", + "tick": 67, + "to": 0.19119098805466825 + }, + { + "from": 8, + "path": "state.tick", + "tick": 68, + "to": 9 + }, + { + "from": 0.19119098805466825, + "path": "state.maxAnimatedRadians", + "tick": 68, + "to": 0.2461860570872399 + }, + { + "from": 9, + "path": "state.tick", + "tick": 69, + "to": 10 + }, + { + "from": 0.2461860570872399, + "path": "state.maxAnimatedRadians", + "tick": 69, + "to": 0.30833331170158496 + }, + { + "from": 10, + "path": "state.tick", + "tick": 70, + "to": 11 + }, + { + "from": 0.30833331170158496, + "path": "state.maxAnimatedRadians", + "tick": 70, + "to": 0.3772934859560291 + }, + { + "from": 11, + "path": "state.tick", + "tick": 71, + "to": 12 + }, + { + "from": 0.3772934859560291, + "path": "state.maxAnimatedRadians", + "tick": 71, + "to": 0.45590555415239853 + }, + { + "from": 12, + "path": "state.tick", + "tick": 72, + "to": 13 + }, + { + "from": 1, + "path": "state.reversals", + "tick": 72, + "to": 2 + }, + { + "from": "Walk_Loop", + "path": "state.current", + "tick": 72, + "to": "Jog_Fwd_Loop" + }, + { + "from": 2, + "path": "state.activeActions", + "tick": 72, + "to": 3 + }, + { + "from": 0.45590555415239853, + "path": "state.maxAnimatedRadians", + "tick": 72, + "to": 0.5418354950461067 + }, + { + "from": 13, + "path": "state.tick", + "tick": 73, + "to": 14 + }, + { + "from": 0.5418354950461067, + "path": "state.maxAnimatedRadians", + "tick": 73, + "to": 0.5772320250832106 + }, + { + "from": 14, + "path": "state.tick", + "tick": 74, + "to": 15 + }, + { + "from": 0.5772320250832106, + "path": "state.maxAnimatedRadians", + "tick": 74, + "to": 0.6193384045607399 + }, + { + "from": 15, + "path": "state.tick", + "tick": 75, + "to": 16 + }, + { + "from": 0.6193384045607399, + "path": "state.maxAnimatedRadians", + "tick": 75, + "to": 0.6697003860109736 + }, + { + "from": 16, + "path": "state.tick", + "tick": 76, + "to": 17 + }, + { + "from": 0.6697003860109736, + "path": "state.maxAnimatedRadians", + "tick": 76, + "to": 0.7334745970750475 + }, + { + "from": 17, + "path": "state.tick", + "tick": 77, + "to": 18 + }, + { + "from": 0.7334745970750475, + "path": "state.maxAnimatedRadians", + "tick": 77, + "to": 0.8067859961565446 + }, + { + "from": 18, + "path": "state.tick", + "tick": 78, + "to": 19 + }, + { + "from": 0.8067859961565446, + "path": "state.maxAnimatedRadians", + "tick": 78, + "to": 0.8987612471462776 + }, + { + "from": 19, + "path": "state.tick", + "tick": 79, + "to": 20 + }, + { + "from": 0.8987612471462776, + "path": "state.maxAnimatedRadians", + "tick": 79, + "to": 0.9847201686454872 + }, + { + "from": 20, + "path": "state.tick", + "tick": 80, + "to": 21 + }, + { + "from": 0.9847201686454872, + "path": "state.maxAnimatedRadians", + "tick": 80, + "to": 1.0835685049384338 + }, + { + "from": 21, + "path": "state.tick", + "tick": 81, + "to": 22 + }, + { + "from": 1.0835685049384338, + "path": "state.maxAnimatedRadians", + "tick": 81, + "to": 1.1328452613571511 + }, + { + "from": 22, + "path": "state.tick", + "tick": 82, + "to": 23 + }, + { + "from": 1.1328452613571511, + "path": "state.maxAnimatedRadians", + "tick": 82, + "to": 1.1748035702072492 + }, + { + "from": 23, + "path": "state.tick", + "tick": 83, + "to": 24 + }, + { + "from": 1.1748035702072492, + "path": "state.maxAnimatedRadians", + "tick": 83, + "to": 1.1754697662269058 + } + ], + "audio": { + "cues": {}, + "paused": 0, + "pooled": 0, + "queued": 0, + "recentCues": [], + "unsupported": [], + "voices": 0 + }, + "world": { + "seed": null + } + }, + "label": "hold-capture-tick", + "tick": 182 + } + ] + }, + "scene": { + "background": "color:#263346", + "camera": { + "far": 50, + "forward": [ + -0.49785263203126506, + -0.15409724324777252, + -0.8534616549107398 + ], + "fov": 42, + "near": 0.1, + "position": [ + 2.1, + 1.6, + 3.6 + ], + "type": "PerspectiveCamera" + }, + "lights": [ + { + "color": "#d5e3ff", + "intensity": 2, + "type": "AmbientLight", + "visible": true + }, + { + "color": "#ffe4c4", + "intensity": 3, + "type": "DirectionalLight", + "visible": true + } + ], + "materials": { + "MeshStandardMaterial": 3 + }, + "objects": 74, + "truncated": false, + "worldExtent": { + "max": [ + 3.5, + 1.7834981996247072, + 3.5 + ], + "min": [ + -3.5, + -0.010000000000000777, + -3.5 + ] + } + }, + "sceneNodes": [ + { + "matched": 2, + "nodes": [ + { + "bounds": { + "max": [ + 0.38008763160408854, + 1.7834981996247072, + 0.3654025900997272 + ], + "min": [ + -0.3394674649412344, + -0.00009576644003039814, + -0.32747060986185084 + ] + }, + "geometry": { + "attributes": [ + "normal", + "position", + "skinIndex", + "skinWeight" + ], + "triangles": 5732, + "vertices": 3389 + }, + "inFrustum": true, + "instances": 1, + "materials": [ + { + "color": "#e7e7e7", + "emissive": "#000000", + "lit": true, + "maps": [], + "mapsUnloaded": [], + "metalness": 0, + "name": "M_Main", + "opacity": 1, + "roughness": 0.35, + "transparent": false, + "type": "MeshStandardMaterial", + "visible": true + } + ], + "name": "Mannequin_1", + "path": "Scene/Scene/Armature/Mannequin/Mannequin_1", + "position": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ], + "skinned": { + "bones": 65 + }, + "type": "SkinnedMesh", + "visible": true, + "visibleInTree": true + }, + { + "bounds": { + "max": [ + 0.37504559319931896, + 1.6379611302808283, + 0.20166003331715016 + ], + "min": [ + -0.3232569668120342, + 0.09771703441645516, + -0.30445727360187114 + ] + }, + "geometry": { + "attributes": [ + "normal", + "position", + "skinIndex", + "skinWeight" + ], + "triangles": 8012, + "vertices": 4233 + }, + "inFrustum": true, + "instances": 1, + "materials": [ + { + "color": "#3c3d3f", + "emissive": "#000000", + "lit": true, + "maps": [], + "mapsUnloaded": [], + "metalness": 0.6, + "name": "M_Joints", + "opacity": 1, + "roughness": 0.45, + "transparent": false, + "type": "MeshStandardMaterial", + "visible": true + } + ], + "name": "Mannequin_2", + "path": "Scene/Scene/Armature/Mannequin/Mannequin_2", + "position": [ + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ], + "skinned": { + "bones": 65 + }, + "type": "SkinnedMesh", + "visible": true, + "visibleInTree": true + } + ], + "selector": { + "type": "SkinnedMesh" + }, + "truncated": false + } + ], + "signals": [], + "signalSeries": [ + { + "label": "start-matched-trace", + "signals": [], + "tick": 62 + }, + { + "label": "hold-capture-tick", + "signals": [], + "tick": 182 + } + ], + "startup": { + "phase": "ready", + "progress": 1, + "compileSettled": true, + "timeline": { + "loadStartedMs": 1631.7999999999884, + "enteredMs": 1758, + "compileSettledMs": 2747.0999999999767, + "frameworkReadyMs": 2747.0999999999767, + "readyMs": 2747.1999999999534 + }, + "rule": "compile-settled" + }, + "visual": { + "changedPixelRatio": 0.04837384259259259, + "comparisonSource": "before-after", + "nonblankRegions": [ + { + "height": 720, + "width": 960, + "x": 0, + "y": 0, + "minNonblankPixelRatio": 0.2, + "darkPixelRatio": 0.5574652777777778, + "nonblankPixelRatio": 1 + } + ] + } + }, + "artifactDirectory": "/home/runner/work/threenative/threenative/artifacts/vq04-animation/tick-24", + "assertionResults": [ + { + "id": "visual.0.region", + "pass": true, + "details": { + "after": true, + "expected": { + "equals": true + }, + "minimum": 0.2, + "observed": 1 + } + }, + { + "details": { + "after": 24, + "expected": { + "gte": 1 + }, + "id": "state", + "path": "tick", + "trivial": false + }, + "id": "resource.state.tick", + "pass": true + }, + { + "details": { + "after": 65, + "expected": { + "gte": 60 + }, + "id": "state", + "path": "bones", + "trivial": false + }, + "id": "resource.state.bones", + "pass": true + }, + { + "details": { + "after": 0, + "expected": { + "gte": 0, + "lte": 0.000001 + }, + "id": "state", + "path": "maxWeightError", + "trivial": false + }, + "id": "resource.state.maxWeightError", + "pass": true + }, + { + "details": { + "after": 0, + "expected": { + "gte": 0, + "lte": 0.000001 + }, + "id": "state", + "path": "maxPhaseJump", + "trivial": false + }, + "id": "resource.state.maxPhaseJump", + "pass": true + }, + { + "details": { + "after": 0, + "expected": { + "gte": 0, + "lte": 0.000001 + }, + "id": "state", + "path": "maxPoseJumpMetres", + "trivial": false + }, + "id": "resource.state.maxPoseJumpMetres", + "pass": true + }, + { + "details": { + "after": 4.2146848510894035e-8, + "expected": { + "gte": 0, + "lte": 0.000001 + }, + "id": "state", + "path": "maxPoseJumpRadians", + "trivial": false + }, + "id": "resource.state.maxPoseJumpRadians", + "pass": true + }, + { + "details": { + "after": 1.1754697662269058, + "expected": { + "gte": 0.2 + }, + "id": "state", + "path": "maxAnimatedRadians", + "trivial": false + }, + "id": "resource.state.maxAnimatedRadians", + "pass": true + }, + { + "details": { + "matched": 2, + "minCount": 1, + "selector": "type=SkinnedMesh" + }, + "id": "sceneNodes[0].count", + "pass": true + }, + { + "details": { + "expected": true, + "failing": [], + "observed": 2, + "of": 2, + "sampled": false + }, + "id": "sceneNodes[0].visible", + "pass": true + }, + { + "details": { + "expected": true, + "failing": [], + "observed": 2, + "of": 2, + "sampled": false + }, + "id": "sceneNodes[0].inFrustum", + "pass": true + }, + { + "details": { + "expected": 100, + "observed": 13744, + "sampled": false, + "selector": "type=SkinnedMesh" + }, + "id": "sceneNodes[0].minTriangles", + "pass": true + }, + { + "details": { + "consoleErrors": 1, + "networkErrors": 0, + "policy": { + "noConsoleErrors": true, + "noNetworkErrors": true, + "noRuntimeDiagnostics": true, + "runtimeReady": true + }, + "runtimeDiagnostics": 0 + }, + "id": "diagnostics", + "pass": false + } + ], + "pass": false, + "runtime": "web", + "scenario": "animation-reversal-real-cc0-mannequin", + "startup": { + "phase": "ready", + "progress": 1, + "compileSettled": true, + "timeline": { + "loadStartedMs": 1631.7999999999884, + "enteredMs": 1758, + "compileSettledMs": 2747.0999999999767, + "frameworkReadyMs": 2747.0999999999767, + "readyMs": 2747.1999999999534 + }, + "rule": "compile-settled" + }, + "target": "web", + "trivialityOptOutCount": 0, + "url": "http://127.0.0.1:35811/animation-reversal.html?tick=24" +} diff --git a/docs/verification/vq04/diagnostic-tick24.png b/docs/verification/vq04/diagnostic-tick24.png new file mode 100644 index 0000000000..23cb70cdb1 Binary files /dev/null and b/docs/verification/vq04/diagnostic-tick24.png differ diff --git a/docs/verification/vq04/kaykit-license.txt b/docs/verification/vq04/kaykit-license.txt new file mode 100644 index 0000000000..2f547b40ee --- /dev/null +++ b/docs/verification/vq04/kaykit-license.txt @@ -0,0 +1,64 @@ + + + KayKit : Character Animations (1.1) + + Created/distributed by Kay Lousberg (www.kaylousberg.com) + Creation date: 10/12/2025 09:00 + + ------------------------------ + + License: (Creative Commons Zero, CC0) + http://creativecommons.org/publicdomain/zero/1.0/ + + This content is free to use in personal, educational and commercial projects. + You can support me by purchasing an asset pack at the EXTRA or SOURCE tiers, leaving a donation/tip on itch.io, or joining my Patreon. + If you wish to credit me you can do so by crediting "Kay Lousberg, www.kaylousberg.com" (this is not mandatory) + + ------------------------------ + + This asset pack is here thanks to all the wonderful people who support KayKit on Patreon and those who buy EXTRA or SOURCE packs on itch.io. + + And a big, special thank you to my Super Supporters on Patreon: + + - Brian McBarron + - Silva + - Joseph Preston + - Leon Burkhardt + - NickyBHobbying + - Emilia + - Darren Laing + - Magic Pig Games + - Lennart Steinke + - OWSC + - tenthtonman + - Ducain23 + - flo-bit + - Jezzy Sukaemi + - Martyn Rushton + - Joe + - estee + - Gustavo + - AngyZinn + - Alex Cea + - Matthew A + - Stephen Williams + - Wenpu Ng + - Kevin + - Legionof Light + - Dazusu + - Arnaud Schlupp + - JB + - Neil Castle + - Cedrico Serbruyns + - ovenaut + - Pawel Jarosz + + ------------------------------ + + + Patreon: http://patreon.com/kaylousberg + + Follow here for updates: + http://twitter.com/KayLousberg + https://mastodon.gamedev.place/@Kay + http://twitter.com/KayLousberg \ No newline at end of file diff --git a/docs/verification/vq04/kaykit-provenance.md b/docs/verification/vq04/kaykit-provenance.md new file mode 100644 index 0000000000..b2b06ef69b --- /dev/null +++ b/docs/verification/vq04/kaykit-provenance.md @@ -0,0 +1,11 @@ +# KayKit Character Animations 1.1 — provenance (PRD-VQ-04 directional clips) + +- Source: https://kaylousberg.itch.io/kaykit-character-animations (itch upload 15799903, 'Free Download 1.1', 14 MB, Dec 10 2025), fetched 2026-10-04 through itch's no-account download flow. +- License: CC0 1.0. The itch page lists 'Creative Commons Zero v1.0 Universal' and says 'Free for personal and commercial use, no attribution required'; the pack's own License.txt is copied beside this file (kaykit-license.txt). +- Zip SHA-256: 65882f31f905ad2e953819648a59287cdeab8f623908d5ef701971d3758be20f +- Unmodified files copied to examples/abyss-framework/assets/ (Rig_Medium, Mannequin meshes included in each file): + - kaykit-rig-medium-general.glb: 5f725c0f745f36078c7238968cd5c04843cbc6260689eb6e7f5e2bbd7ffdd7ad (828240 bytes) + - kaykit-rig-medium-movement-advanced.glb: 8b2317cf81e3f24149e4b791c27e1932bebd46cd378ed962e7d27d642fad8c56 (719628 bytes) + - kaykit-rig-medium-movement-basic.glb: 11422afd50abf7e8c92ea107e2e0bc94abd38023d6d15f55ad580347efbfa29e (689624 bytes) +- Clips used (nothing retargeted, nothing authored by this repository): Idle_A (General); Walking_A, Running_A, Running_B (MovementBasic); Walking_Backwards, Running_Strafe_Left, Running_Strafe_Right (MovementAdvanced). One skeleton, Rig_Medium (23 joints). +- Why this pack: the repo's Quaternius UAL1/UAL2 (CC0) and every other local source were searched first and have forward gait only; the Unreal-only Game Animation Sample cannot enter this repository. diff --git a/docs/verification/vq04/locomotion-kaykit-native/gait-after-reversals.jpg b/docs/verification/vq04/locomotion-kaykit-native/gait-after-reversals.jpg new file mode 100644 index 0000000000..b33b6dbba8 Binary files /dev/null and b/docs/verification/vq04/locomotion-kaykit-native/gait-after-reversals.jpg differ diff --git a/docs/verification/vq04/locomotion-kaykit-native/gait-backward.jpg b/docs/verification/vq04/locomotion-kaykit-native/gait-backward.jpg new file mode 100644 index 0000000000..34629608d3 Binary files /dev/null and b/docs/verification/vq04/locomotion-kaykit-native/gait-backward.jpg differ diff --git a/docs/verification/vq04/locomotion-kaykit-native/gait-diagonal.jpg b/docs/verification/vq04/locomotion-kaykit-native/gait-diagonal.jpg new file mode 100644 index 0000000000..1e0de33f4c Binary files /dev/null and b/docs/verification/vq04/locomotion-kaykit-native/gait-diagonal.jpg differ diff --git a/docs/verification/vq04/locomotion-kaykit-native/gait-forward.jpg b/docs/verification/vq04/locomotion-kaykit-native/gait-forward.jpg new file mode 100644 index 0000000000..49722df5b1 Binary files /dev/null and b/docs/verification/vq04/locomotion-kaykit-native/gait-forward.jpg differ diff --git a/docs/verification/vq04/locomotion-kaykit-native/gait-strafe-left.jpg b/docs/verification/vq04/locomotion-kaykit-native/gait-strafe-left.jpg new file mode 100644 index 0000000000..b5d41cc6c1 Binary files /dev/null and b/docs/verification/vq04/locomotion-kaykit-native/gait-strafe-left.jpg differ diff --git a/docs/verification/vq04/locomotion-kaykit-native/gait-strafe-right.jpg b/docs/verification/vq04/locomotion-kaykit-native/gait-strafe-right.jpg new file mode 100644 index 0000000000..a84b4b5937 Binary files /dev/null and b/docs/verification/vq04/locomotion-kaykit-native/gait-strafe-right.jpg differ diff --git a/docs/verification/vq04/locomotion-kaykit-native/provenance.json b/docs/verification/vq04/locomotion-kaykit-native/provenance.json new file mode 100644 index 0000000000..7e56999065 --- /dev/null +++ b/docs/verification/vq04/locomotion-kaykit-native/provenance.json @@ -0,0 +1,26 @@ +{ + "scenario": "examples/abyss-framework/playtests/vq-locomotion-blend-spaces.playtest.json", + "target": "desktop", + "adapter": "NVIDIA GeForce RTX 2080 (Vulkan), private Xvfb", + "host": "packages/runtime-native/build/tn-linux/mystral, prebuilt 2026-09-26 (stale against this source, not rebuilt)", + "command": "sh scripts/xvfb.sh node packages/playtest/dist/runner/cli.js playtests/vq-locomotion-blend-spaces.playtest.json --target desktop --executable --project examples/abyss-framework --host-arg run --host-arg dist/vq-locomotion-native.js", + "result": { + "pass": true, + "frames": 586, + "assertions": 46, + "diagnostics": 0 + }, + "sourceBase": "bfbbf18fa6a7d6961bda5c21ed40f58ed701eb97", + "sourceNote": "uncommitted gait-camera and KayKit consumer changes at capture time; committed in the next commit", + "clips": "CC0 KayKit Character Animations 1.1, unmodified, see kaykit-provenance.md", + "authoredClipFacts": "Running_Strafe_Left/Right carry root yaw +60/-60 degrees (and chest -17/+17); Walking_Backwards carries a 32 degree spine lean; Running_A/Walking_A carry none. The angled bodies in the frames are the authored animation.", + "images": { + "gait-after-reversals.jpg": "580d416dc1dd9f1745725f5ed439439b6419655660318a4d86a51a40e79700ce", + "gait-backward.jpg": "c91b90a39730c50985430842bfbcbf688cb40ba517c1a2141d999d7ebfc5e5bd", + "gait-diagonal.jpg": "e0f7090bc4f255a3cc993aeab15cbd38cc1c8260b28448cc5023cfeaa44eac0d", + "gait-forward.jpg": "fdfb80506717a46035a0e1a9c7eb1fba550155363b99f62e46e6768111e95aa5", + "gait-strafe-left.jpg": "448790fea66918c2877995c0d4a4c6f0a57404cfe0723b9c0e88dd6cdc70c3ad", + "gait-strafe-right.jpg": "fcdac84d4178ba5066476bca8610e6179bfa19d1ff22a0acf460e004c0e3bf7b" + }, + "imageNote": "640x360 q72 JPEG copies of the 1280x720 playtest PNGs, small for the docs/verification budget." +} diff --git a/docs/verification/vq04/locomotion-native/after-reversals.jpg b/docs/verification/vq04/locomotion-native/after-reversals.jpg new file mode 100644 index 0000000000..32fc2b3fa9 Binary files /dev/null and b/docs/verification/vq04/locomotion-native/after-reversals.jpg differ diff --git a/docs/verification/vq04/locomotion-native/decelerating.jpg b/docs/verification/vq04/locomotion-native/decelerating.jpg new file mode 100644 index 0000000000..7d4707c519 Binary files /dev/null and b/docs/verification/vq04/locomotion-native/decelerating.jpg differ diff --git a/docs/verification/vq04/locomotion-native/idle-before-reversals.jpg b/docs/verification/vq04/locomotion-native/idle-before-reversals.jpg new file mode 100644 index 0000000000..cb0711bd4f Binary files /dev/null and b/docs/verification/vq04/locomotion-native/idle-before-reversals.jpg differ diff --git a/docs/verification/vq04/locomotion-native/jog.jpg b/docs/verification/vq04/locomotion-native/jog.jpg new file mode 100644 index 0000000000..49a5ee36c8 Binary files /dev/null and b/docs/verification/vq04/locomotion-native/jog.jpg differ diff --git a/docs/verification/vq04/locomotion-native/provenance.json b/docs/verification/vq04/locomotion-native/provenance.json new file mode 100644 index 0000000000..b95c9e04b6 --- /dev/null +++ b/docs/verification/vq04/locomotion-native/provenance.json @@ -0,0 +1,24 @@ +{ + "scenario": "examples/abyss-framework/playtests/vq-locomotion-blend-spaces.playtest.json", + "target": "desktop", + "adapter": "NVIDIA GeForce RTX 2080 (Vulkan), private Xvfb", + "host": "packages/runtime-native/build/tn-linux/mystral, prebuilt 2026-09-26 (stale versus this source)", + "command": "sh scripts/xvfb.sh node packages/playtest/dist/runner/cli.js playtests/vq-locomotion-blend-spaces.playtest.json --target desktop --executable --project examples/abyss-framework --host-arg run --host-arg dist/vq-locomotion-native.js", + "result": { + "pass": true, + "frames": 421, + "assertions": 23, + "diagnostics": 0 + }, + "sourceBase": "16e26c1403e94e60485b138b7b2dfc939d37d2d7", + "sourceNote": "camera tuning uncommitted at capture time; committed in the next commit", + "images": { + "after-reversals.jpg": "ac98aa1f269035410f949d9552923f012eb4c830666316746e8cbc385732c4cf", + "decelerating.jpg": "07c28035e6535695de72230ff3ae80333e7ca1248802b8b8db0f5f09f9861871", + "idle-before-reversals.jpg": "f1bd5196dca6136c1def2e7b256d10cdf86759755877c97c39f00b5f3f7cdeb5", + "jog.jpg": "b4c31ea28740bdd572f2194895a3ebf31bed0219f3c4a8b2fa9e49c1be8d9dad", + "sprint.jpg": "cb861c1d6c6f4c32ca447b2a81cce6db15a4bfdde9fdca0bb75941f70049d52d", + "walk.jpg": "c33c15c4ad2c4d19e8da44e7fa2a5bfadc09fd9f958058edfab367c09bf1e1bd" + }, + "imageNote": "Evidence JPEGs are 640x360 q72 copies of the 1280x720 playtest PNGs, kept small for the docs/verification budget; the original PNG hashes were not retained." +} diff --git a/docs/verification/vq04/locomotion-native/sprint.jpg b/docs/verification/vq04/locomotion-native/sprint.jpg new file mode 100644 index 0000000000..bbbace788f Binary files /dev/null and b/docs/verification/vq04/locomotion-native/sprint.jpg differ diff --git a/docs/verification/vq04/locomotion-native/walk.jpg b/docs/verification/vq04/locomotion-native/walk.jpg new file mode 100644 index 0000000000..8209070085 Binary files /dev/null and b/docs/verification/vq04/locomotion-native/walk.jpg differ diff --git a/docs/verification/vq04/locomotion-web/summary.json b/docs/verification/vq04/locomotion-web/summary.json new file mode 100644 index 0000000000..f946e3824e --- /dev/null +++ b/docs/verification/vq04/locomotion-web/summary.json @@ -0,0 +1,39 @@ +{ + "sourceSha": "275ab37eedcefd4d663ecf527b5f001344f74b3a", + "adapter": { + "architecture": "turing", + "vendor": "nvidia", + "features": "bgra8unorm-storage,chromium-experimental-multi-draw-indirect,chromium-experimental-subgroup-matrix,chromium-experimental-timestamp-query-inside-passes,clip-distances,core-features-and-limits,depth-clip-control,depth32float-stencil8,dual-source-blending,float32-blendable,float32-filterable,indirect-first-instance,primitive-index,rg11b10ufloat-renderable,subgroup-size-control,subgroups,texture-component-swizzle,texture-compression-bc,texture-compression-bc-sliced-3d,texture-formats-tier1,texture-formats-tier2,timestamp-query", + "limit.maxBindGroups": "4", + "limit.maxTextureDimension2D": "16384", + "limit.maxStorageBufferBindingSize": "2147483644" + }, + "frames": [ + { + "name": "locomotion-walk.png", + "sha256": "49e979f5d15399bb3202b3b40b239c5e41cbd6610e241592e08efe342729f07d" + }, + { + "name": "locomotion-jog.png", + "sha256": "25e6a111dea5730d87f1c6266a5715a345c75f68d6a1e79c25e5728a2f6e7190" + }, + { + "name": "locomotion-sprint.png", + "sha256": "4b1dcc6b068575559ae77cd6bd6c2e301b2199e13488929fc93afa98d05806ad" + }, + { + "name": "locomotion-decelerating.png", + "sha256": "31e91811a5ec1ab4f8b6f2141546ca1d4c50d3804f773858bfdd61f6034ce088" + }, + { + "name": "locomotion-idle-before-reversals.png", + "sha256": "07322d657a5b2d3cad5b1a0dfcc99f372c0810ed3a05ebf1acdb83f306bd8998" + }, + { + "name": "locomotion-after-reversals.png", + "sha256": "6524e39fc091745e2918867ff147d3db925efee6d24e8374aa10bc0acfd12aea" + } + ], + "correctnessOnly": true, + "imagesNote": "Frame PNGs are not tracked (docs/verification byte budget); the sha256 values are of the original run's PNGs, produced by scripts/verify-vq-locomotion.ts and uploaded as the vq04-animation CI artifact." +} diff --git a/docs/verification/vq04/provenance.json b/docs/verification/vq04/provenance.json new file mode 100644 index 0000000000..c9dae5f78c --- /dev/null +++ b/docs/verification/vq04/provenance.json @@ -0,0 +1,362 @@ +{ + "sourceSha": "a53db062fec7b1b3ae99268470dfa06d15ee3494", + "workflowRun": "https://github.com/ThreeNativeHQ/threenative/actions/runs/36996637583", + "workflowConclusion": "success", + "artifactId": 11222156451, + "artifactZipSha256": "ba1e0cff8e340330f98096695bd9e84840a3e0c15f5659a00e70a08475c91e08", + "correctnessOnly": true, + "scope": "Real CC0 mannequin interrupted-crossfade regression on headed browser WebGPU. Not 1D/2D blend-space qualification, native execution, hardware performance or human aesthetic approval.", + "visualInspection": "All five unchanged runtime PNGs inspected: full articulated mannequin visible, gait poses differ, final idle settles; no blank or missing rig.", + "frames": [ + { + "tick": 24, + "image": "tick-24.png", + "sha256": "26de9efac6b525b43efbf5f525db11a8bb7e2693f77b3cbb7712c180d289fb79", + "reportSha256": "54a55cefe853619ff52dba77f40b71e55699c8d76b501096256442b94a426eb3", + "pass": true, + "diagnostics": [], + "assertions": [ + { + "id": "visual.0.region", + "pass": true + }, + { + "id": "visual.1.region", + "pass": true + }, + { + "id": "visual.1.region.maxDarkPixels", + "pass": true + }, + { + "id": "resource.state.tick", + "pass": true + }, + { + "id": "resource.state.bones", + "pass": true + }, + { + "id": "resource.state.maxWeightError", + "pass": true + }, + { + "id": "resource.state.maxPhaseJump", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpMetres", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpRadians", + "pass": true + }, + { + "id": "resource.state.maxAnimatedRadians", + "pass": true + }, + { + "id": "sceneNodes[0].count", + "pass": true + }, + { + "id": "sceneNodes[0].visible", + "pass": true + }, + { + "id": "sceneNodes[0].inFrustum", + "pass": true + }, + { + "id": "sceneNodes[0].minTriangles", + "pass": true + }, + { + "id": "diagnostics", + "pass": true + } + ] + }, + { + "tick": 25, + "image": "tick-25.png", + "sha256": "f8371da3ad956a946cdfde635d0faeb3772be994bb909f3b362cf6657d7845f5", + "reportSha256": "e4b724a20c86123af1274a278568401e44f9426a6368149a4c99a1c85716fbab", + "pass": true, + "diagnostics": [], + "assertions": [ + { + "id": "visual.0.region", + "pass": true + }, + { + "id": "visual.1.region", + "pass": true + }, + { + "id": "visual.1.region.maxDarkPixels", + "pass": true + }, + { + "id": "resource.state.tick", + "pass": true + }, + { + "id": "resource.state.bones", + "pass": true + }, + { + "id": "resource.state.maxWeightError", + "pass": true + }, + { + "id": "resource.state.maxPhaseJump", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpMetres", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpRadians", + "pass": true + }, + { + "id": "resource.state.maxAnimatedRadians", + "pass": true + }, + { + "id": "sceneNodes[0].count", + "pass": true + }, + { + "id": "sceneNodes[0].visible", + "pass": true + }, + { + "id": "sceneNodes[0].inFrustum", + "pass": true + }, + { + "id": "sceneNodes[0].minTriangles", + "pass": true + }, + { + "id": "diagnostics", + "pass": true + } + ] + }, + { + "tick": 26, + "image": "tick-26.png", + "sha256": "bd9f0bae6a19ba7b3d79d98bed37a9d96e68c3b642b85dfff1bb3ee2e2b34b4b", + "reportSha256": "c9da0b8cbdb851acc36a0c529ec5f396890ee2ed9ddbce57b39889548976e498", + "pass": true, + "diagnostics": [], + "assertions": [ + { + "id": "visual.0.region", + "pass": true + }, + { + "id": "visual.1.region", + "pass": true + }, + { + "id": "visual.1.region.maxDarkPixels", + "pass": true + }, + { + "id": "resource.state.tick", + "pass": true + }, + { + "id": "resource.state.bones", + "pass": true + }, + { + "id": "resource.state.maxWeightError", + "pass": true + }, + { + "id": "resource.state.maxPhaseJump", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpMetres", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpRadians", + "pass": true + }, + { + "id": "resource.state.maxAnimatedRadians", + "pass": true + }, + { + "id": "sceneNodes[0].count", + "pass": true + }, + { + "id": "sceneNodes[0].visible", + "pass": true + }, + { + "id": "sceneNodes[0].inFrustum", + "pass": true + }, + { + "id": "sceneNodes[0].minTriangles", + "pass": true + }, + { + "id": "diagnostics", + "pass": true + } + ] + }, + { + "tick": 48, + "image": "tick-48.png", + "sha256": "b5099ddd1c34d3b59fde863bf871909780c8ae28552f6e950ec487403b3d9745", + "reportSha256": "9183c5f96cdffa36feaa2b3b0afda6a71d20351a17f0b1bfb3c510e50113ddbb", + "pass": true, + "diagnostics": [], + "assertions": [ + { + "id": "visual.0.region", + "pass": true + }, + { + "id": "visual.1.region", + "pass": true + }, + { + "id": "visual.1.region.maxDarkPixels", + "pass": true + }, + { + "id": "resource.state.tick", + "pass": true + }, + { + "id": "resource.state.bones", + "pass": true + }, + { + "id": "resource.state.maxWeightError", + "pass": true + }, + { + "id": "resource.state.maxPhaseJump", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpMetres", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpRadians", + "pass": true + }, + { + "id": "resource.state.maxAnimatedRadians", + "pass": true + }, + { + "id": "sceneNodes[0].count", + "pass": true + }, + { + "id": "sceneNodes[0].visible", + "pass": true + }, + { + "id": "sceneNodes[0].inFrustum", + "pass": true + }, + { + "id": "sceneNodes[0].minTriangles", + "pass": true + }, + { + "id": "diagnostics", + "pass": true + } + ] + }, + { + "tick": 108, + "image": "tick-108.png", + "sha256": "e51d58fb8e61d968055468c32faaa998862642b0d3151dbae8bb9f5f820356fd", + "reportSha256": "46d81fdcfd09f74134c9abe4bb66831108c5446a57388f521ff5b161693dc20f", + "pass": true, + "diagnostics": [], + "assertions": [ + { + "id": "visual.0.region", + "pass": true + }, + { + "id": "visual.1.region", + "pass": true + }, + { + "id": "visual.1.region.maxDarkPixels", + "pass": true + }, + { + "id": "resource.state.tick", + "pass": true + }, + { + "id": "resource.state.bones", + "pass": true + }, + { + "id": "resource.state.maxWeightError", + "pass": true + }, + { + "id": "resource.state.maxPhaseJump", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpMetres", + "pass": true + }, + { + "id": "resource.state.maxPoseJumpRadians", + "pass": true + }, + { + "id": "resource.state.maxAnimatedRadians", + "pass": true + }, + { + "id": "sceneNodes[0].count", + "pass": true + }, + { + "id": "sceneNodes[0].visible", + "pass": true + }, + { + "id": "sceneNodes[0].inFrustum", + "pass": true + }, + { + "id": "sceneNodes[0].minTriangles", + "pass": true + }, + { + "id": "diagnostics", + "pass": true + } + ] + } + ] +} diff --git a/docs/verification/vq04/runtime-summary.json b/docs/verification/vq04/runtime-summary.json new file mode 100644 index 0000000000..2da5179f83 --- /dev/null +++ b/docs/verification/vq04/runtime-summary.json @@ -0,0 +1,266 @@ +{ + "sourceSha": "a53db062fec7b1b3ae99268470dfa06d15ee3494", + "correctnessOnly": true, + "reports": [ + { + "tick": 24, + "sourceSha": "a53db062fec7b1b3ae99268470dfa06d15ee3494", + "measurement": { + "tick": 24, + "reversals": 2, + "returningRequests": 0, + "bones": 65, + "current": "Jog_Fwd_Loop", + "weights": [ + 0.28117766203703715, + 0.28117766203703703, + 0.43764467592592576 + ], + "phases": [ + 0.24000000000000007, + 0.2874999914318325, + 0.19642857059228175 + ], + "activeActions": 3, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + }, + "capture": { + "adapter": { + "architecture": "swiftshader", + "vendor": "google", + "features": "bgra8unorm-storage,chromium-experimental-timestamp-query-inside-passes,clip-distances,core-features-and-limits,depth-clip-control,depth32float-stencil8,float32-blendable,float32-filterable,indirect-first-instance,rg11b10ufloat-renderable,subgroup-size-control,subgroups,texture-component-swizzle,texture-compression-astc,texture-compression-astc-sliced-3d,texture-compression-bc,texture-compression-bc-sliced-3d,texture-compression-etc2,timestamp-query", + "limit.maxBindGroups": "4", + "limit.maxTextureDimension2D": "8192", + "limit.maxStorageBufferBindingSize": "1073741824" + }, + "browserArgs": [ + "--ozone-platform=x11", + "--enable-unsafe-webgpu", + "--disable-gpu-sandbox", + "--ignore-gpu-blocklist", + "--enable-features=Vulkan" + ], + "captureMethod": "page.screenshot", + "rendererKind": "webgpu", + "target": "web", + "viewport": { + "height": 720, + "width": 960 + } + }, + "sha256": "26de9efac6b525b43efbf5f525db11a8bb7e2693f77b3cbb7712c180d289fb79" + }, + { + "tick": 25, + "sourceSha": "a53db062fec7b1b3ae99268470dfa06d15ee3494", + "measurement": { + "tick": 25, + "reversals": 3, + "returningRequests": 1, + "bones": 65, + "current": "Idle_Loop", + "weights": [ + 0.2500000000000001, + 0.25, + 0.4999999999999999 + ], + "phases": [ + 0.24666666666666676, + 0.29999999105930353, + 0.21428571337339827 + ], + "activeActions": 3, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + }, + "capture": { + "adapter": { + "architecture": "swiftshader", + "vendor": "google", + "features": "bgra8unorm-storage,chromium-experimental-timestamp-query-inside-passes,clip-distances,core-features-and-limits,depth-clip-control,depth32float-stencil8,float32-blendable,float32-filterable,indirect-first-instance,rg11b10ufloat-renderable,subgroup-size-control,subgroups,texture-component-swizzle,texture-compression-astc,texture-compression-astc-sliced-3d,texture-compression-bc,texture-compression-bc-sliced-3d,texture-compression-etc2,timestamp-query", + "limit.maxBindGroups": "4", + "limit.maxTextureDimension2D": "8192", + "limit.maxStorageBufferBindingSize": "1073741824" + }, + "browserArgs": [ + "--ozone-platform=x11", + "--enable-unsafe-webgpu", + "--disable-gpu-sandbox", + "--ignore-gpu-blocklist", + "--enable-features=Vulkan" + ], + "captureMethod": "page.screenshot", + "rendererKind": "webgpu", + "target": "web", + "viewport": { + "height": 720, + "width": 960 + } + }, + "sha256": "f8371da3ad956a946cdfde635d0faeb3772be994bb909f3b362cf6657d7845f5" + }, + { + "tick": 26, + "sourceSha": "a53db062fec7b1b3ae99268470dfa06d15ee3494", + "measurement": { + "tick": 26, + "reversals": 3, + "returningRequests": 1, + "bones": 65, + "current": "Idle_Loop", + "weights": [ + 0.2537977430555557, + 0.24873408564814814, + 0.49746817129629617 + ], + "phases": [ + 0.25333333333333347, + 0.3124999906867745, + 0.2321428561545148 + ], + "activeActions": 3, + "maxWeightError": 0, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + }, + "capture": { + "adapter": { + "architecture": "swiftshader", + "vendor": "google", + "features": "bgra8unorm-storage,chromium-experimental-timestamp-query-inside-passes,clip-distances,core-features-and-limits,depth-clip-control,depth32float-stencil8,float32-blendable,float32-filterable,indirect-first-instance,rg11b10ufloat-renderable,subgroup-size-control,subgroups,texture-component-swizzle,texture-compression-astc,texture-compression-astc-sliced-3d,texture-compression-bc,texture-compression-bc-sliced-3d,texture-compression-etc2,timestamp-query", + "limit.maxBindGroups": "4", + "limit.maxTextureDimension2D": "8192", + "limit.maxStorageBufferBindingSize": "1073741824" + }, + "browserArgs": [ + "--ozone-platform=x11", + "--enable-unsafe-webgpu", + "--disable-gpu-sandbox", + "--ignore-gpu-blocklist", + "--enable-features=Vulkan" + ], + "captureMethod": "page.screenshot", + "rendererKind": "webgpu", + "target": "web", + "viewport": { + "height": 720, + "width": 960 + } + }, + "sha256": "bd9f0bae6a19ba7b3d79d98bed37a9d96e68c3b642b85dfff1bb3ee2e2b34b4b" + }, + { + "tick": 48, + "sourceSha": "a53db062fec7b1b3ae99268470dfa06d15ee3494", + "measurement": { + "tick": 48, + "reversals": 4, + "returningRequests": 2, + "bones": 65, + "current": "Jog_Fwd_Loop", + "weights": [ + 0.3514720775462964, + 0.07029441550925929, + 0.5782335069444443 + ], + "phases": [ + 0.4000000000000005, + 0.5874999824911367, + 0.6249999973390785 + ], + "activeActions": 3, + "maxWeightError": 1.1102230246251565e-16, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + }, + "capture": { + "adapter": { + "architecture": "swiftshader", + "vendor": "google", + "features": "bgra8unorm-storage,chromium-experimental-timestamp-query-inside-passes,clip-distances,core-features-and-limits,depth-clip-control,depth32float-stencil8,float32-blendable,float32-filterable,indirect-first-instance,rg11b10ufloat-renderable,subgroup-size-control,subgroups,texture-component-swizzle,texture-compression-astc,texture-compression-astc-sliced-3d,texture-compression-bc,texture-compression-bc-sliced-3d,texture-compression-etc2,timestamp-query", + "limit.maxBindGroups": "4", + "limit.maxTextureDimension2D": "8192", + "limit.maxStorageBufferBindingSize": "1073741824" + }, + "browserArgs": [ + "--ozone-platform=x11", + "--enable-unsafe-webgpu", + "--disable-gpu-sandbox", + "--ignore-gpu-blocklist", + "--enable-features=Vulkan" + ], + "captureMethod": "page.screenshot", + "rendererKind": "webgpu", + "target": "web", + "viewport": { + "height": 720, + "width": 960 + } + }, + "sha256": "b5099ddd1c34d3b59fde863bf871909780c8ae28552f6e950ec487403b3d9745" + }, + { + "tick": 108, + "sourceSha": "a53db062fec7b1b3ae99268470dfa06d15ee3494", + "measurement": { + "tick": 108, + "reversals": 9, + "returningRequests": 7, + "bones": 65, + "current": "Idle_Loop", + "weights": [ + 1, + 0, + 0 + ], + "phases": [ + 0.7999999999999992, + 0, + 0 + ], + "activeActions": 1, + "maxWeightError": 2.220446049250313e-16, + "maxPoseJumpMetres": 0, + "maxPoseJumpRadians": 4.2146848510894035e-8, + "maxPhaseJump": 0, + "maxAnimatedRadians": 1.1754697662269058 + }, + "capture": { + "adapter": { + "architecture": "swiftshader", + "vendor": "google", + "features": "bgra8unorm-storage,chromium-experimental-timestamp-query-inside-passes,clip-distances,core-features-and-limits,depth-clip-control,depth32float-stencil8,float32-blendable,float32-filterable,indirect-first-instance,rg11b10ufloat-renderable,subgroup-size-control,subgroups,texture-component-swizzle,texture-compression-astc,texture-compression-astc-sliced-3d,texture-compression-bc,texture-compression-bc-sliced-3d,texture-compression-etc2,timestamp-query", + "limit.maxBindGroups": "4", + "limit.maxTextureDimension2D": "8192", + "limit.maxStorageBufferBindingSize": "1073741824" + }, + "browserArgs": [ + "--ozone-platform=x11", + "--enable-unsafe-webgpu", + "--disable-gpu-sandbox", + "--ignore-gpu-blocklist", + "--enable-features=Vulkan" + ], + "captureMethod": "page.screenshot", + "rendererKind": "webgpu", + "target": "web", + "viewport": { + "height": 720, + "width": 960 + } + }, + "sha256": "e51d58fb8e61d968055468c32faaa998862642b0d3151dbae8bb9f5f820356fd" + } + ] +} diff --git a/docs/verification/vq04/tick-108.png b/docs/verification/vq04/tick-108.png new file mode 100644 index 0000000000..d4766964c1 Binary files /dev/null and b/docs/verification/vq04/tick-108.png differ diff --git a/docs/verification/vq04/tick-24.png b/docs/verification/vq04/tick-24.png new file mode 100644 index 0000000000..23cb70cdb1 Binary files /dev/null and b/docs/verification/vq04/tick-24.png differ diff --git a/docs/verification/vq04/tick-25.png b/docs/verification/vq04/tick-25.png new file mode 100644 index 0000000000..74aed72f66 Binary files /dev/null and b/docs/verification/vq04/tick-25.png differ diff --git a/docs/verification/vq04/tick-26.png b/docs/verification/vq04/tick-26.png new file mode 100644 index 0000000000..f50163030f Binary files /dev/null and b/docs/verification/vq04/tick-26.png differ diff --git a/docs/verification/vq04/tick-48.png b/docs/verification/vq04/tick-48.png new file mode 100644 index 0000000000..208422776d Binary files /dev/null and b/docs/verification/vq04/tick-48.png differ diff --git a/examples/abyss-framework/.gitignore b/examples/abyss-framework/.gitignore index 9a6d257bce..f1c66352bf 100644 --- a/examples/abyss-framework/.gitignore +++ b/examples/abyss-framework/.gitignore @@ -7,3 +7,4 @@ public/bake.receipt.json public/basis/ public/shared/ public/world/ +public/*.glb diff --git a/examples/abyss-framework/animation-reversal.html b/examples/abyss-framework/animation-reversal.html new file mode 100644 index 0000000000..19970fb6a0 --- /dev/null +++ b/examples/abyss-framework/animation-reversal.html @@ -0,0 +1,11 @@ + + + + + + + VQ04 real-rig reversal proof + + + + diff --git a/examples/abyss-framework/assets/kaykit-rig-medium-general.glb b/examples/abyss-framework/assets/kaykit-rig-medium-general.glb new file mode 100644 index 0000000000..5d16cb6815 Binary files /dev/null and b/examples/abyss-framework/assets/kaykit-rig-medium-general.glb differ diff --git a/examples/abyss-framework/assets/kaykit-rig-medium-movement-advanced.glb b/examples/abyss-framework/assets/kaykit-rig-medium-movement-advanced.glb new file mode 100644 index 0000000000..f3ea309627 Binary files /dev/null and b/examples/abyss-framework/assets/kaykit-rig-medium-movement-advanced.glb differ diff --git a/examples/abyss-framework/assets/kaykit-rig-medium-movement-basic.glb b/examples/abyss-framework/assets/kaykit-rig-medium-movement-basic.glb new file mode 100644 index 0000000000..98e965e886 Binary files /dev/null and b/examples/abyss-framework/assets/kaykit-rig-medium-movement-basic.glb differ diff --git a/examples/abyss-framework/assets/mannequin.glb b/examples/abyss-framework/assets/mannequin.glb new file mode 100644 index 0000000000..d6db862136 Binary files /dev/null and b/examples/abyss-framework/assets/mannequin.glb differ diff --git a/examples/abyss-framework/index.html b/examples/abyss-framework/index.html index 2ef04a2c7c..f11c2dede0 100644 --- a/examples/abyss-framework/index.html +++ b/examples/abyss-framework/index.html @@ -9,7 +9,9 @@