@deepblend/dsh-blender-contracts 0.2.15 → 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/scene-spec.js CHANGED
@@ -96,6 +96,8 @@ export const ANIMATION_PROPERTIES_BY_KIND = Object.freeze({
96
96
  export function collectionNameForKind(kind) {
97
97
  if (kind === 'camera') return 'cameras'
98
98
  if (kind === 'material') return 'materials'
99
+ // A BONE TRACK RESOLVES AGAINST THE ARMATURES, because that is the collection its id belongs to.
100
+ if (kind === 'bone') return 'armatures'
99
101
  return 'entities'
100
102
  }
101
103
 
@@ -360,6 +362,25 @@ export function validateSceneSpec(spec) {
360
362
  // is what every track written before kinds existed meant.
361
363
  const kind = track.targetKind ?? 'entity'
362
364
  requiresId(collectionNameForKind(kind), track.targetEntityId, `${at}.targetEntityId`, kind)
365
+ // AND THE BONE INSIDE IT. MEASURED: this is the same shape as `armatureId` last round — the schema
366
+ // can say `boneName` is a string, and only a cross-reference can say the armature HAS that bone.
367
+ // A track pointing at a bone nobody declares animates nothing while the file says the rig moves.
368
+ if (kind === 'bone') {
369
+ if (typeof track.boneName !== 'string' || track.boneName.length === 0) {
370
+ errors.push({
371
+ severity: 'error', code: 'SCENE_REFERENCE_MISSING', path: `${at}.boneName`,
372
+ message: 'a bone track must name the bone it drives',
373
+ })
374
+ } else {
375
+ const armature = (document.armatures ?? []).find(entry => entry?.id === track.targetEntityId)
376
+ if (armature !== undefined && !(armature.bones ?? []).some(bone => bone?.name === track.boneName)) {
377
+ errors.push({
378
+ severity: 'error', code: 'SCENE_REFERENCE_MISSING', path: `${at}.boneName`,
379
+ message: `bone "${track.boneName}" does not exist in armature "${track.targetEntityId}"`,
380
+ })
381
+ }
382
+ }
383
+ }
363
384
 
364
385
  // A property that does not belong to the target's kind is not a typo the
365
386
  // compiler can shrug off: `emissionStrength` on an entity, or `location.x` on a
@@ -31,7 +31,7 @@
31
31
  "items": {
32
32
  "$ref": "#/$defs/armature"
33
33
  },
34
- "description": "Armatures this scene declares (SPEC \u00a720 M6, character animation). Each is one skeleton, created as its own object and named `<prefix><id>` like every other product object. WHAT THIS DOES NOT DO YET: animate a bone. Entities ARE skinned to an armature now (`entity.armatureId`, with Blender's automatic weights), so a rig drives its mesh \u2014 but nothing moves the rig until animation tracks can target bones, which is the next slice."
34
+ "description": "Armatures this scene declares (SPEC \u00a720 M6, character animation). Each is one skeleton, created as its own object and named `<prefix><id>` like every other product object. Entities are skinned to an armature (`entity.armatureId`, with Blender's automatic weights) and animation tracks can drive a bone of it (`targetKind: \"bone\"` with `boneName`), so a rig both moves and moves its mesh."
35
35
  },
36
36
  "materials": {
37
37
  "type": "array",
@@ -677,15 +677,21 @@
677
677
  "enum": [
678
678
  "entity",
679
679
  "camera",
680
- "material"
680
+ "material",
681
+ "bone"
681
682
  ],
682
683
  "default": "entity",
683
- "description": "What `targetEntityId` names: an entity (default), a camera, or a material. Only entity tracks existed before, which is why \"the camera orbits the product\" and \"the dial lights up\" both had to be faked with entity transforms."
684
+ "description": "What `targetEntityId` names: an entity (default), a camera, a material, or AN ARMATURE whose bone this track drives (with `boneName`). Ids are unique per collection, so the kind is the only thing that says whether \"watch-dial\" means the mesh or the material \u2014 and `bone` is what made a rig able to move rather than merely exist."
684
685
  },
685
686
  "targetEntityId": {
686
687
  "type": "string",
687
688
  "description": "The id of the target named by `targetKind`. The field name says Entity because every track written before target kinds existed used it and revisions are immutable; renaming it would make r0001-r0023 unreadable. Resolution is against the collection `targetKind` names, which matters because ids are unique per collection, not globally."
688
689
  },
690
+ "boneName": {
691
+ "type": "string",
692
+ "maxLength": 64,
693
+ "description": "Required when targetKind is `bone`: the name of the bone inside the named armature. A name the armature does not have is refused \u2014 a track that silently animates nothing is a scene that claims to move and does not."
694
+ },
689
695
  "property": {
690
696
  "type": "string",
691
697
  "enum": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepblend/dsh-blender-contracts",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "type": "module",
5
5
  "description": "DeepBlend Studio Service Definition, protocol contracts, and stable error codes.",
6
6
  "main": "lib/index.js",