arkavo-org / arkavo-org/VRMMetalKit
Silhouette: add restore/snapshot mechanism for round-trip toggle
- Dominant language
- Swift
- Stars
- 6
- Forks
- 2
- Avg merge
- 18h 51m
- Merged PRs (30d)
- 26
Description
## Summary
\`VRMRenderer.applySilhouetteMode(model:config:)\` is a one-way mutation: it sets renderer flags, reconfigures lights, and rewrites every material's albedo/emissive/outline state. There is no counterpart \`restoreFromSilhouette(model:)\` and the doc comment on \`applySilhouetteMode\` notes the limitation:
> Idempotent — safe to call before or after \`loadModel\`. Any subsequent gameplay-style scene reset would need to undo each step manually.
For host apps that want to toggle silhouette mode on/off in the same renderer instance (menu host transitions, accent shots), this is user-hostile — they have to reload the model.
## Suggested approach
Two parts, can ship together or separately:
**1. Renderer-side restore (cheap):**
- Snapshot the four renderer flags affected (\`disableAutoMaterialOverrides\`, \`additiveDirectionalRimEnabled\`, \`additiveDirectionalRimPower\`, plus the three light slots and ambient color) before mutating in \`applySilhouetteMode\`.
- Store the snapshot in a private \`silhouetteSnapshot\` property.
- Add \`restoreFromSilhouette()\` that reverts those.
**2. Material-side restore (heavier):**
- Snapshot per-material \`baseColorFactor\`, \`baseColorTexture\`, \`emissiveTexture\`, \`emissiveFactor\`, plus the MToon fields zeroed by silhouette (\`shadeColorFactor\`, \`matcapFactor\`, \`parametricRimColorFactor\`, \`giIntensityFactor\`, the four outline fields).
- Memory: ~80 bytes/material × ~15 materials = ~1.2 KB per model. Trivial.
- Reset via deep copy back into \`model.materials[i]\`.
Alternative: have the host maintain its own snapshot before calling \`applySilhouetteMode\` and restore via a fresh model load. This puts the burden on the caller but avoids API surface area inside the renderer.
## Discovered during
PR #137 review. Added unit-test coverage for \`applySilhouetteMode\` mutation invariants in \`8c2de51\`; a future restore mechanism would need the same coverage symmetrically (round-trip identity).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Contributor guide
Research direction
Start by reading VRMRenderer.applySilhouetteMode(model:config:) and the unit-test coverage added in commit 8c2de51 for its mutation invariants. Define the snapshot boundary between renderer and material state, then add symmetric round-trip tests showing that restoring after silhouette mode returns the model and renderer to their original state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- computer-graphics
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100