@deepblend/dsh-blender-contracts 0.2.13 → 0.2.15

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
@@ -185,6 +185,10 @@ export function validateSceneSpec(spec) {
185
185
  // ---- uniqueness ---------------------------------------------------------
186
186
  const collections = [
187
187
  ['assets', document.assets ?? []],
188
+ // ARMATURES ARE A COLLECTION LIKE ANY OTHER: they have ids, they can collide, and other things
189
+ // reference them by id. Leaving them out of this list meant the cross-reference check had nothing to
190
+ // look in, so a skinned entity was reported as naming a rig that does not exist — its own rig.
191
+ ['armatures', document.armatures ?? []],
188
192
  ['materials', document.materials ?? []],
189
193
  ['entities', document.entities],
190
194
  ['lights', document.lights ?? []],
@@ -279,6 +283,11 @@ export function validateSceneSpec(spec) {
279
283
  })
280
284
  }
281
285
  requiresId('materials', entity.materialId, `${at}.materialId`, 'material')
286
+ // AND THE RIG IT IS SKINNED TO. MEASURED: this check was missing at first, and the contract suite
287
+ // caught it — the schema can say `armatureId` is a string, but only a cross-reference can say it
288
+ // names something the scene declares. An entity skinned to an armature nobody declares would render
289
+ // as a static object while the file says it is rigged, which is the failure this code exists for.
290
+ requiresId('armatures', entity.armatureId, `${at}.armatureId`, 'armature')
282
291
  if (entity.type !== 'empty' && entity.materialId === undefined) {
283
292
  notices.push({
284
293
  severity: 'notice', code: 'SCENE_ENTITY_MATERIAL_DEFAULTED', path: at,
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://deepblend.local/schemas/scene-spec.schema.json",
4
4
  "title": "DeepBlend SceneSpec v1",
5
- "description": "The structural source of truth for one DeepBlend scene (SPEC §8.1: 'SceneSpec 场景结构事实来源'). A .blend file is a compile artifact of this document, never the authority. Scenes are compiled from a SceneSpec and modified only through ScenePatch operations, so that every revision is diffable, replayable and auditable.",
5
+ "description": "The structural source of truth for one DeepBlend scene (SPEC \u00a78.1: 'SceneSpec \u573a\u666f\u7ed3\u6784\u4e8b\u5b9e\u6765\u6e90'). A .blend file is a compile artifact of this document, never the authority. Scenes are compiled from a SceneSpec and modified only through ScenePatch operations, so that every revision is diffable, replayable and auditable.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -26,6 +26,13 @@
26
26
  "$ref": "#/$defs/asset"
27
27
  }
28
28
  },
29
+ "armatures": {
30
+ "type": "array",
31
+ "items": {
32
+ "$ref": "#/$defs/armature"
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."
35
+ },
29
36
  "materials": {
30
37
  "type": "array",
31
38
  "items": {
@@ -83,7 +90,12 @@
83
90
  "properties": {
84
91
  "color": {
85
92
  "$ref": "#/$defs/unitColor",
86
- "default": [0.02, 0.021, 0.026, 1]
93
+ "default": [
94
+ 0.02,
95
+ 0.021,
96
+ 0.026,
97
+ 1
98
+ ]
87
99
  },
88
100
  "strength": {
89
101
  "type": "number",
@@ -220,7 +232,7 @@
220
232
  "path": {
221
233
  "type": "string",
222
234
  "minLength": 1,
223
- "description": "Project-relative POSIX path. Absolute paths and any '..' segment are rejected by the path guard (SPEC §15.2)."
235
+ "description": "Project-relative POSIX path. Absolute paths and any '..' segment are rejected by the path guard (SPEC \u00a715.2)."
224
236
  },
225
237
  "sha256": {
226
238
  "type": "string",
@@ -383,7 +395,7 @@
383
395
  "required": [
384
396
  "shape"
385
397
  ],
386
- "description": "Procedural proxy geometry. M1 has no asset-ingest action yet, so fixtures build their hero objects from generators; asset-instance entities take over as the primary path once blender_asset_ingest lands (SPEC §11).",
398
+ "description": "Procedural proxy geometry. M1 has no asset-ingest action yet, so fixtures build their hero objects from generators; asset-instance entities take over as the primary path once blender_asset_ingest lands (SPEC \u00a711).",
387
399
  "properties": {
388
400
  "shape": {
389
401
  "type": "string",
@@ -461,6 +473,10 @@
461
473
  "type": "string",
462
474
  "description": "Required when type is asset-instance."
463
475
  },
476
+ "armatureId": {
477
+ "type": "string",
478
+ "description": "The armature this entity is skinned to, named by `armatures[].id`. The compiler binds the mesh with Blender's own automatic weights, so the entity follows the bones with no weight table to write by hand. A name the scene does not declare is refused rather than ignored, because an entity that is silently unskinned renders as a static object while the file says it is rigged."
479
+ },
464
480
  "generator": {
465
481
  "$ref": "#/$defs/generator"
466
482
  },
@@ -480,7 +496,7 @@
480
496
  },
481
497
  "locked": {
482
498
  "type": "boolean",
483
- "description": "A locked entity may still be patched explicitly, but the orchestrator must not propose changes to it autonomously (SPEC §12)."
499
+ "description": "A locked entity may still be patched explicitly, but the orchestrator must not propose changes to it autonomously (SPEC \u00a712)."
484
500
  },
485
501
  "visible": {
486
502
  "type": "boolean"
@@ -658,7 +674,11 @@
658
674
  },
659
675
  "targetKind": {
660
676
  "type": "string",
661
- "enum": ["entity", "camera", "material"],
677
+ "enum": [
678
+ "entity",
679
+ "camera",
680
+ "material"
681
+ ],
662
682
  "default": "entity",
663
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."
664
684
  },
@@ -768,7 +788,7 @@
768
788
  "maxSamplesBudget": {
769
789
  "type": "integer",
770
790
  "minimum": 1,
771
- "description": "Upper bound a render action may use. Guards the render-cost threshold of SPEC §15.4."
791
+ "description": "Upper bound a render action may use. Guards the render-cost threshold of SPEC \u00a715.4."
772
792
  }
773
793
  }
774
794
  },
@@ -786,6 +806,57 @@
786
806
  "$ref": "#/$defs/renderProfile"
787
807
  }
788
808
  }
809
+ },
810
+ "bone": {
811
+ "type": "object",
812
+ "additionalProperties": false,
813
+ "required": [
814
+ "name",
815
+ "head",
816
+ "tail"
817
+ ],
818
+ "properties": {
819
+ "name": {
820
+ "type": "string",
821
+ "minLength": 1,
822
+ "maxLength": 64,
823
+ "description": "The bone's name inside its armature. A parent names another bone by this, so it is the identity a rig is written in terms of."
824
+ },
825
+ "head": {
826
+ "$ref": "#/$defs/vec3"
827
+ },
828
+ "tail": {
829
+ "$ref": "#/$defs/vec3"
830
+ },
831
+ "parent": {
832
+ "type": "string",
833
+ "description": "The name of another bone in the SAME armature, or absent for a root. Bones are declared in any order; the compiler resolves the hierarchy, because requiring parents-first would make a rig's declaration depend on its storage order."
834
+ }
835
+ }
836
+ },
837
+ "armature": {
838
+ "type": "object",
839
+ "additionalProperties": false,
840
+ "required": [
841
+ "id",
842
+ "bones"
843
+ ],
844
+ "properties": {
845
+ "id": {
846
+ "$ref": "#/$defs/id"
847
+ },
848
+ "bones": {
849
+ "type": "array",
850
+ "minItems": 1,
851
+ "items": {
852
+ "$ref": "#/$defs/bone"
853
+ },
854
+ "description": "At least one bone, because an armature with none is a rig that cannot move anything and would silently do nothing."
855
+ },
856
+ "transform": {
857
+ "$ref": "#/$defs/transform"
858
+ }
859
+ }
789
860
  }
790
861
  }
791
862
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepblend/dsh-blender-contracts",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "type": "module",
5
5
  "description": "DeepBlend Studio Service Definition, protocol contracts, and stable error codes.",
6
6
  "main": "lib/index.js",