Skip to content

test(fixtures): end-to-end mixed-feature fixture#113

Merged
danko-nobre merged 3 commits into
mainfrom
feat/spec-035-mixed-feature-fixture
Jun 12, 2026
Merged

test(fixtures): end-to-end mixed-feature fixture#113
danko-nobre merged 3 commits into
mainfrom
feat/spec-035-mixed-feature-fixture

Conversation

@danko-nobre

@danko-nobre danko-nobre commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Spec 035 (project health), follow-up PR: the end-to-end mixed-feature fixture - the sixth now-item, split out from the toolchain/shipping PR (#112) as agreed.

What

One fixture that stacks every Blender-to-Godot feature in a single document, so the golden catches interactions a single-feature fixture cannot:

  • Skinned body - a 2-face polygon weighted across root / spine, exporting per-bone weights plus the per-face polygons index arrays.
  • sprite_frame mouth - a 4-frame Sprite2D bone-parented to head.
  • Drive-from-Bone - a scripted driver on mouth.proscenio.frame reading jaw ROT_Y; the animation bakes into a bone_transform track plus the driven sprite_frame track.
  • Slot with mixed attachments - face.slot holds face_neutral (mesh / Polygon2D) and face_glow (sprite / Sprite2D), default face_neutral.
  • Packed atlas - every element shares one atlas.png: meshes derive their region from UV bounds, the two sprite strips carry a manual region. One image keeps the top-level atlas field deterministic.
  • One animation mixed_anim.

Layout

Lands in the blender_to_godot/ bucket - new fixtures go there per its README; the flat legacy fixtures move later. Builder scripts under packages/fixtures/mixed_feature/ (Pillow draw_layers.py + bpy build_blend.py), golden + .blend + atlas.png + Godot wrapper under examples/generated/blender_to_godot/mixed_feature/.

Wiring + verification

  • test-blender: the golden is auto-discovered by run_tests.py; the local re-export is byte-clean (8/8 fixtures, baked on Blender 5.1.1 to match CI).
  • test-godot: a tests/fixtures/mixed_feature.proscenio copy plus a _run_mixed_checks() block in test_importer.gd walking the whole stack (skinned Polygon2D with 2 bones + 2 faces, 4-frame Sprite2D mouth, mixed slot attachments, the mixed_anim tracks, and the saved-scene no-script guard). Local run: 82 assertions, exit 0.
  • Both .proscenio validate against proscenio.schema.json (the validate-schema job).
  • sync_fixtures.py picks the fixture up (gitignored dev-project output).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Fixtures

    • Added a new mixed-feature visual fixture demonstrating combined skinned mesh + sprite attachments, slots, and animation.
  • Tests

    • Extended importer tests to validate the mixed-feature scene, structure, attachments, and animation tracks.
  • Tools

    • Added asset-generation tooling to produce the fixture atlas and Blender-based fixture builder for reproducible fixtures.
  • Documentation

    • Marked the mixed-feature fixture task as completed in project TODOs.

danko-nobre and others added 2 commits June 11, 2026 22:03
The one fixture that stacks every Blender-to-Godot feature in a single
document: a skinned body polygon (per-bone weights + multi-face
polygons), a sprite_frame mouth driven from the jaw bone, a slot with
mixed mesh + sprite attachments, every element packed into one shared
atlas, and one animation. Single-feature fixtures cannot catch the
interactions between builders; this is the safety net before the queued
schema-expressiveness wave churns the writer.

Lands in the blender_to_godot bucket (new fixtures go there per its
README). The golden is auto-discovered by run_tests.py for the
test-blender re-export diff; test-godot gains a tests/fixtures copy plus
assertions in test_importer.gd that walk the whole stack.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 64ef773f-7c1e-4f92-b6a3-d984d064a50d

📥 Commits

Reviewing files that changed from the base of the PR and between 28a6755 and 4d55146.

📒 Files selected for processing (1)
  • packages/fixtures/mixed_feature/build_blend.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/fixtures/mixed_feature/build_blend.py

📝 Walkthrough

Walkthrough

End-to-end mixed_feature fixture: Proscenio scene (skeleton, skinned body, sprite attachments, slots, animation), a generated atlas, a Blender builder that creates the .blend with drivers/animation, and extended Godot importer tests validating the built scene.

Changes

Mixed Feature Fixture Implementation

Layer / File(s) Summary
Fixture specification: skeleton, elements, slots, animation
apps/godot/tests/fixtures/mixed_feature.proscenio
Proscenio config defining a four-bone skeleton (root, spine, head, jaw), a textured two-face polygonal body with per-face vertex weights, sprite-based face attachments (face_neutral, face_glow) and a frame-based mouth, a face.slot with default attachment, atlas reference, and mixed_anim with bone_transform and sprite_frame tracks.
Atlas texture generation
packages/fixtures/mixed_feature/draw_layers.py
Standalone script generating a 128×128 atlas (four 64×64 quadrants: 4-frame mouth strip, 2-frame glow strip, body block, face disc) and saving examples/generated/blender_to_godot/mixed_feature/atlas.png.
Blender fixture builder: armature, components, drivers, animation
packages/fixtures/mixed_feature/build_blend.py
Blender orchestration that validates atlas existence, wipes session, builds a four-bone armature, creates skinned two-face body and mouth quad, assembles face.slot with mixed attachments, installs a scripted driver mapping jaw ROT_Y to mouth.proscenio.frame (expression var * 2 + 2), creates mixed_anim keyframes, saves .blend, and rewrites image paths to relative.
Godot importer test: fixture validation
apps/godot/tests/test_importer.gd
Adds MIXED_FIXTURE constant and _run_mixed_checks() to build the fixture scene and assert skeleton structure, polygonal skinned body weights and faces, mouth sprite frame count, slot mixed attachments and default visibility, presence of mixed_anim with expected tracks, and no addon scripts in the saved scene.
Project status: end-to-end fixture delivered
specs/035-project-health/TODO.md
Marks the end-to-end mixed-feature fixture as delivered and checks off authoring, baking, and wiring checklist items.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • firebound/proscenio#112: Adds saved-scene and addon-script detection helpers reused by the mixed-feature test stage.
  • firebound/proscenio#26: Implements slot/slot_attachment behavior that the mixed-feature fixture exercises.
  • firebound/proscenio#39: Related to bone-driven driver behavior (drive from bone) similar to the scripted driver installed by the Blender fixture.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding an end-to-end mixed-feature fixture that combines multiple Blender-to-Godot features. It is specific, concise, and clearly conveys the primary change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/spec-035-mixed-feature-fixture
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/spec-035-mixed-feature-fixture

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/godot/tests/fixtures/mixed_feature.proscenio (1)

6-63: mixed_feature flat bone hierarchy: likely supported; document the intent
In apps/godot/tests/fixtures/mixed_feature.proscenio (lines 6–63), root, spine, head, and jaw all have "parent": null (flat hierarchy). Other fixtures in the same folder (e.g., slots_demo.proscenio, skinned_dummy.proscenio) also contain bones with "parent": null, so this pattern appears supported/used elsewhere—if it’s intentional for the test, adding a short comment explaining the rationale would prevent it from looking like accidental/integrity-broken data.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/godot/tests/fixtures/mixed_feature.proscenio` around lines 6 - 63, Add a
short inline comment above the "bones" array in the mixed_feature.proscenio
fixture stating that bones "root", "spine", "head", and "jaw" intentionally use
a flat hierarchy with "parent": null (this mirrors other fixtures like
slots_demo and skinned_dummy) so readers know this is deliberate, not corrupted
data; update the fixture header or the bones block comment accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/fixtures/mixed_feature/build_blend.py`:
- Around line 283-315: The driver expression "var * 2 + 2" in
_install_mouth_driver maps jaw ROT_Y (radians) to invalid frame indices; change
the expression to normalize var from the jaw rotation range [-π/2, +π/2] into
integer frame indices [0, 3] by first adding π/2, dividing by π, then scaling by
3 (i.e., (var + π/2) / π * 3) and assign the same corrected expression to
mouth_obj.proscenio.driver_expression; keep the TRANSFORMS variable setup (var,
target.id=armature_obj, target.bone_target="jaw", target.transform_type="ROT_Y")
and ensure transform_space/rotation_mode remain as-is.

---

Nitpick comments:
In `@apps/godot/tests/fixtures/mixed_feature.proscenio`:
- Around line 6-63: Add a short inline comment above the "bones" array in the
mixed_feature.proscenio fixture stating that bones "root", "spine", "head", and
"jaw" intentionally use a flat hierarchy with "parent": null (this mirrors other
fixtures like slots_demo and skinned_dummy) so readers know this is deliberate,
not corrupted data; update the fixture header or the bones block comment
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ac0a3364-2150-411e-a9e2-61143ca21027

📥 Commits

Reviewing files that changed from the base of the PR and between 34aec4a and 28a6755.

⛔ Files ignored due to path filters (5)
  • examples/generated/blender_to_godot/mixed_feature/atlas.png is excluded by !**/*.png, !**/generated/**
  • examples/generated/blender_to_godot/mixed_feature/godot/MixedFeature.gd is excluded by !**/generated/**
  • examples/generated/blender_to_godot/mixed_feature/godot/MixedFeature.tscn is excluded by !**/generated/**
  • examples/generated/blender_to_godot/mixed_feature/mixed_feature.blend is excluded by !**/generated/**, !**/*.blend
  • examples/generated/blender_to_godot/mixed_feature/mixed_feature.expected.proscenio is excluded by !**/generated/**
📒 Files selected for processing (5)
  • apps/godot/tests/fixtures/mixed_feature.proscenio
  • apps/godot/tests/test_importer.gd
  • packages/fixtures/mixed_feature/build_blend.py
  • packages/fixtures/mixed_feature/draw_layers.py
  • specs/035-project-health/TODO.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
apps/godot/**/*.{gd,gdc,gdns,gdst,tscn,scn}

📄 CodeRabbit inference engine (AGENTS.md)

The Godot plugin runs only at editor import time. Generated scenes must use only built-in nodes (Skeleton2D, Bone2D, Polygon2D, Sprite2D, Node2D, AnimationPlayer, AnimationLibrary). No GDExtension or native runtime

Files:

  • apps/godot/tests/test_importer.gd
🔇 Additional comments (8)
specs/035-project-health/TODO.md (1)

37-42: LGTM!

apps/godot/tests/fixtures/mixed_feature.proscenio (2)

167-181: LGTM!

Also applies to: 182-228


256-322: LGTM!

packages/fixtures/mixed_feature/draw_layers.py (2)

49-71: LGTM!


62-64: No issue: border() supports (canvas, color[, thickness]).
packages/fixtures/_shared/_draw.py defines border(canvas: Canvas, color: RGBA, thickness: int = 1), so the call border(canvas, (0.20, 0.40, 0.25, 1.0)) is consistent (uses default thickness).

packages/fixtures/mixed_feature/build_blend.py (1)

53-71: LGTM!

apps/godot/tests/test_importer.gd (2)

253-318: LGTM!


33-33: LGTM!

Also applies to: 44-44

Comment thread packages/fixtures/mixed_feature/build_blend.py
…ture

The Drive-from-Bone expression `var * 2 + 2` overshoots [0, 3] by design;
the writer's frame-driver bake clamps it to the grid. Document that the
raw range is the point (the fixture exercises the clamp path), so it does
not read as a bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@danko-nobre danko-nobre merged commit b37db93 into main Jun 12, 2026
9 checks passed
@danko-nobre danko-nobre deleted the feat/spec-035-mixed-feature-fixture branch June 12, 2026 01:56
danko-nobre added a commit that referenced this pull request Jun 12, 2026
Specs 027-035 shipped their near-term work (PRs #104-#113), verified against code rather than the stale checkboxes. Strip the resolved rows from BACKLOGS_SUMMARY, backlog.md, backlog-bugs-found.md, backlog-ui-feedback.md, and backlog-code-quality.md; the not-now rows already moved to DEFERRED/GATED/DROPPED.md, so they leave the backlogs too. The GUI-retest rows stay with needs-retest status (they ride the verification session). Fold in the two newer capture backlogs (IK authoring ergonomics, Photoshop performance) and cspell-ignore them like the other informal backlog files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
danko-nobre added a commit that referenced this pull request Jun 12, 2026
Their near-term work shipped (PRs #104-#113), verified against code. Mark them pruned in the index (content recoverable from git history per the index recipe), add the active 039 row for completeness, and delete the nine folders. The carved-out not-now work and locked calls already landed in DEFERRED/GATED/DROPPED.md and decisions.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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