Skip to content

feat: refactor spring bones from GLB extras to item metadata#3395

Open
decentraland-bot wants to merge 11 commits intofeat/spring-bonesfrom
feat/spring-bones-metadata
Open

feat: refactor spring bones from GLB extras to item metadata#3395
decentraland-bot wants to merge 11 commits intofeat/spring-bonesfrom
feat/spring-bones-metadata

Conversation

@decentraland-bot
Copy link
Copy Markdown

@decentraland-bot decentraland-bot commented Apr 23, 2026

Summary

  • Removed GLB patching pipeline (patchGltfSpringBones.ts deleted)
  • Save flow now writes spring bone params to item.data.springBones metadata instead of patching GLB binary
  • Load flow reads params from item.data.springBones.models[contentHash] instead of GLB extension
  • Simplified parseSpringBones.ts to return bone names/hierarchy only (no param extraction from GLB)
  • Added springBones to WearableData type in modules/item/types.ts

Metadata shape:

{
  "springBones": {
    "version": 1,
    "models": {
      "contentHash": { "SpringBone_hair_l": { "stiffness": 2.0, ... } }
    }
  }
}

Test plan

  • Verify spring bone UI still renders correctly for wearables with spring bones
  • Verify save persists params to item.data.springBones (not GLB)
  • Verify load reads params from metadata and overlays onto parsed bones
  • Verify dual-GLB wearables use correct model filename keys
  • Verify preview updates when slider values change

Pending tasks

  • Bump @dcl/schemas dependency when version released.

Requested by Rocío Corral Mena via Slack

🤖 Generated with Claude Code

Refactor spring bone parameter storage from GLB file extensions (Option 1)
to wearable item metadata JSON (Option 2).

- Remove GLB binary patching (patchGltfSpringBones)
- Save spring bone params to item.data.springBones metadata on save
- Load spring bone params from metadata in ItemProvider
- Simplify parseSpringBones to only detect bone names/hierarchy
- Add SpringBonesData type to WearableData
- Update tests to reflect new metadata-based approach

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
builder Ready Ready Preview, Comment Apr 27, 2026 9:21pm

Request Review

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 23, 2026

Coverage Report for CI Build 25020093935

Coverage decreased (-0.3%) to 50.599%

Details

  • Coverage decreased (-0.3%) from the base build.
  • Patch coverage: 11 uncovered changes across 4 files (69 of 80 lines covered, 86.25%).
  • 2 coverage regressions across 1 file.

Uncovered Changes

File Changed Covered %
src/modules/item/sagas.ts 15 10 66.67%
src/modules/editor/utils.ts 5 1 20.0%
src/lib/parseSpringBones.ts 5 4 80.0%
src/modules/editor/sagas.ts 42 41 97.62%

Coverage Regressions

2 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
src/modules/item/sagas.ts 2 80.7%

Coverage Stats

Coverage Status
Relevant Lines: 12358
Covered Lines: 6890
Line Coverage: 55.75%
Relevant Branches: 5352
Covered Branches: 2071
Branch Coverage: 38.7%
Branches in Coverage %: Yes
Coverage Strength: 33.35 hits per line

💛 - Coveralls

if (isConnected && id && item) {
void this.loadAnimationData(item)
void this.loadSpringBonesData(item)
this.props.onLoadSpringBones(item)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
this.props.onLoadSpringBones(item)
void this.props.onLoadSpringBones(item)

if (isConnected && id && item && item.id !== prevProps.item?.id && item.type === 'emote') {
if (isConnected && id && item && item.id !== prevProps.item?.id) {
void this.loadAnimationData(item)
void this.props.onLoadSpringBones(item)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do we want to load the springBones for emotes as well?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, maybe the name is not 100% self-explanatory, it should be: onClearThenReLoadSpringBones. It clears the spring bones from the previously selected item (wearable/emote) and then loads new spring bones only for wearables. I followed the same pattern as loadAnimationData had originally (that is supposed to run only for wearables, but the condition item.type === 'emote' is inside the loadAnimationData function)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

onClearThenReLoadSpringBones sounds a bit long, but I can update if you consider it best

Comment thread src/modules/editor/sagas.ts Outdated
yield call(pushSpringBoneParamsToPreview)
}

function* parseSpringBonesForShape(item: Item, bodyShape: BodyShape) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
function* parseSpringBonesForShape(item: Item, bodyShape: BodyShape) {
function* parseSpringBonesForBodyShape(item: Item, bodyShape: BodyShape) {

Comment thread src/modules/item/sagas.ts Outdated

item.data = {
...item.data,
springBones: Object.keys(models).length > 0 ? { version: 1, models } : undefined
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could we move the 1 to a const, something like SPRING_BONES_VERSION?

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.

5 participants