Skip to content

feat(quadruped): gait engine, glTF pose evaluate, AKIRA hound demo - #422

Open
arkavo-com wants to merge 2 commits into
mainfrom
feat/quadruped-gait
Open

feat(quadruped): gait engine, glTF pose evaluate, AKIRA hound demo#422
arkavo-com wants to merge 2 commits into
mainfrom
feat/quadruped-gait

Conversation

@arkavo-com

Copy link
Copy Markdown
Contributor

Summary

Adds a public QuadrupedGait library (procedural trot + wheel-drive), a glTF runtime pose API (GLTFAsset.evaluate / restPose / nodeIndex), and two executables that generate and headless-render an AKIRA-style quadruped.

What's in this PR

  • QuadrupedGaitQuadrupedGaitEngine (diagonal trot pairing, 2-bone foot clamp, drive wheels, drive↔walk blend) and QuadrupedRigController mapping engine output onto named glTF nodes.
  • GLTFMetalKit — extracts shared rebuildDrawCalls from baked animation playback; adds evaluate(poses:) for procedural node overrides without mutating the asset.
  • AKIRAHoundGen — emits AKIRA_Hound.glb (TRS-only, −Z forward, rest-pose wheel-cancel).
  • AKIRAHoundDemo — headless Metal stills/video for drive / walk / transition, optional VRM rider.
  • .gitignore — ignore local /hound-demo-output/ and /perf-review-output/.

Usage

swift run AKIRAHoundGen AKIRA_Hound.glb
swift run AKIRAHoundDemo AKIRA_Hound.glb --mode walk -o walk.png
swift run AKIRAHoundDemo AKIRA_Hound.glb --mode drive --video drive.mov

Tests

  • QuadrupedGaitEngineTests — 7 passed
  • GLTFNodePoseEvaluationTests — 5 passed
  • GLTFAnimationTests / GLTFAnimatedLightsTests / GLTFMorphTests — 11 passed (cover the rebuildDrawCalls extract)

Follow-ups

  • evaluate / restPose currently seed TRS only; matrix-authored nodes become identity (the new fixture is TRS-only).
  • Demo applies +Z-forward engine output onto the −Z-forward hound without a sagittal conversion, so walk/roll currently travel toward the tail.
  • CLI value flags should bounds-check the next argv; video encode should lock pixel buffers writable.
  • QuadrupedRigError should conform to LocalizedError.

Add QuadrupedGait (procedural trot + wheel-drive) and GLTFAsset.evaluate /
restPose / nodeIndex for runtime node-level posing. AKIRAHoundGen emits a
glTF hound; AKIRAHoundDemo headless-renders walk, drive, and transition.
Comment thread Sources/AKIRAHoundDemo/main.swift
Comment thread Sources/AKIRAHoundDemo/main.swift Outdated
Comment thread Sources/QuadrupedGait/QuadrupedRigController.swift Outdated
Comment thread Sources/GLTFMetalKit/Loader/GLTFNodePoseEvaluation.swift
@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown

Claude Code Review ran on 79e8071 — result: failure.

Findings (if any) are posted as inline comments; none means the review found nothing to flag. Run log.

Ground clamp now targets stance ground and swing stepHeight so a chain
longer than standingHeight actually steps. restPose/evaluate decompose
node.matrix. Demo value flags bounds-check argv, lock pixel buffers
writable, and fail on adaptor.append. QuadrupedRigError is LocalizedError.
@arkavo-com

Copy link
Copy Markdown
Contributor Author

Gitar review

@gitar-bot

gitar-bot Bot commented Aug 22, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your team has used its included automatic processing for this billing period (headroom scales with your seat count). You can still comment "Gitar review" to run one anytime, and automatic reviews resume on their own by September 1. Add seats for more headroom.
Learn more

Code Review ✅ Approved 4 resolved / 4 findings

Adds a procedural quadruped gait engine, glTF pose evaluation APIs, and AKIRA hound demo executables. Addressed missing argument bounds checks, pixel buffer write locking, LocalizedError conformance, and matrix-authored node handling.

✅ 4 resolved
Bug: CLI value flags crash with index-out-of-range if value is omitted

📄 Sources/AKIRAHoundDemo/main.swift:146-160
In parseArguments(), flags that expect a value (-o, --speed, --time, --steering, --accel, --size, --rider, --video, --duration, --fps) do i += 1 and then index args[i] without checking i < args.count. Running e.g. AKIRAHoundDemo model.glb --mode walk -o (flag as the last argument) causes a fatal array-index-out-of-range crash instead of a clean HoundDemoError.invalidArguments. --mode and --camera already guard this correctly (guard i < args.count, ...) — apply the same pattern to the other cases.

Bug: CVPixelBuffer locked .readOnly while being written to

📄 Sources/AKIRAHoundDemo/main.swift:293 📄 Sources/AKIRAHoundDemo/main.swift:298-303 📄 Sources/AKIRAHoundDemo/main.swift:849
copyTextureToPixelBuffer locks the pixel buffer with CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly) (main.swift:293) but then writes into baseAddress via texture.getBytes (main.swift:298-303), and is later unlocked with the same .readOnly option (main.swift:849). This violates the CVPixelBuffer locking contract; pass an empty CVPixelBufferLockFlags (writable) instead, since .readOnly is documented to potentially return a lockable-but-non-writable region depending on backing store.

Quality: QuadrupedRigError lacks LocalizedError conformance

📄 Sources/QuadrupedGait/QuadrupedRigController.swift:55-58
QuadrupedRigError.missingNode(String) (QuadrupedRigController.swift:55-58) conforms only to Error, Equatable, so error.localizedDescription falls back to a generic Cocoa/Swift message instead of surfacing the missing node name. AKIRAHoundDemo relies on LocalizedError.errorDescription for its top-level error reporting (see HoundDemoError), so a thrown QuadrupedRigError here prints an unhelpful message to the user instead of naming the missing rig node.

Edge Case: evaluate()/restPose() silently treat matrix-authored nodes as identity

📄 Sources/GLTFMetalKit/Loader/GLTFNodePoseEvaluation.swift:55-69 📄 Sources/GLTFMetalKit/Loader/GLTFNodePoseEvaluation.swift:90-104
restPose(ofNode:) and evaluate(poses:) (GLTFNodePoseEvaluation.swift:55-78, 90-125) only read node.translation/rotation/scale; glTF nodes authored with matrix instead of TRS (a valid glTF construct per GLTFNode.matrix) are silently decomposed to identity with no warning/error, which could cause visibly broken poses on any asset using matrix-based nodes for driven joints. Consider decomposing node.matrix into TRS (or documenting/asserting TRS-only support loudly) so callers don't hit a silent identity substitution.

Was this helpful? React with 👍 / 👎 | Gitar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant