@deepblend/dsh-blender-contracts 0.2.19 → 0.3.0

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.
@@ -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 \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.",
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.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -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. 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."
34
+ "description": "Armatures this scene declares (SPEC §20 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
  "simulation": {
37
37
  "type": "object",
@@ -39,12 +39,12 @@
39
39
  "properties": {
40
40
  "gravity": {
41
41
  "type": "number",
42
- "description": "Downward acceleration in m/s\u00b2. Blender's own default (-9.81) applies when absent."
42
+ "description": "Downward acceleration in m/s². Blender's own default (-9.81) applies when absent."
43
43
  },
44
44
  "bake": {
45
45
  "type": "boolean",
46
46
  "default": true,
47
- "description": "Whether the compile bakes the simulation into the checkpoint. Baking is what makes a render show the physics rather than the initial pose, and it is not instant \u2014 so a build that wants the SETUP without paying for the bake can say so."
47
+ "description": "Whether the compile bakes the simulation into the checkpoint. Baking is what makes a render show the physics rather than the initial pose, and it is not instant — so a build that wants the SETUP without paying for the bake can say so."
48
48
  }
49
49
  },
50
50
  "description": "Scene-level physics settings. The bodies themselves are per-entity (`entity.rigidBody`), because which things move is a property of those things."
@@ -118,6 +118,22 @@
118
118
  "minimum": 0,
119
119
  "maximum": 1000,
120
120
  "default": 0.6
121
+ },
122
+ "environment": {
123
+ "type": "object",
124
+ "additionalProperties": false,
125
+ "required": [
126
+ "assetId"
127
+ ],
128
+ "properties": {
129
+ "assetId": {
130
+ "$ref": "#/$defs/id"
131
+ },
132
+ "rotation": {
133
+ "type": "number",
134
+ "description": "Rotation around world Z in radians; default zero."
135
+ }
136
+ }
121
137
  }
122
138
  }
123
139
  },
@@ -202,6 +218,12 @@
202
218
  "activeCamera": {
203
219
  "type": "string",
204
220
  "description": "Camera id the animation is seen through. The active-camera view of a view plan renders this one; when absent the first declared camera is used."
221
+ },
222
+ "referenceImages": {
223
+ "$ref": "#/$defs/referenceImages"
224
+ },
225
+ "reviewSubjectId": {
226
+ "$ref": "#/$defs/id"
205
227
  }
206
228
  }
207
229
  },
@@ -242,13 +264,18 @@
242
264
  "fbx",
243
265
  "obj",
244
266
  "usd",
245
- "blend"
267
+ "blend",
268
+ "png",
269
+ "jpg",
270
+ "jpeg",
271
+ "hdr",
272
+ "exr"
246
273
  ]
247
274
  },
248
275
  "path": {
249
276
  "type": "string",
250
277
  "minLength": 1,
251
- "description": "Project-relative POSIX path. Absolute paths and any '..' segment are rejected by the path guard (SPEC \u00a715.2)."
278
+ "description": "Project-relative POSIX path. Absolute paths and any '..' segment are rejected by the path guard (SPEC §15.2)."
252
279
  },
253
280
  "sha256": {
254
281
  "type": "string",
@@ -260,65 +287,150 @@
260
287
  }
261
288
  },
262
289
  "proceduralTexture": {
263
- "type": "object",
264
- "additionalProperties": false,
265
- "required": [
266
- "type",
267
- "scale"
268
- ],
269
290
  "description": "A procedural surface texture. A material with only scalar parameters renders as one flat colour, which is what makes paper, wood and glazed ceramic all read as plastic; this block adds the surface relief and variation that distinguishes them. It is built as a real node graph (texture coordinate -> mapping -> pattern -> bump / roughness / base colour), not a baked image, so it needs no asset ingest and stays resolution-independent.",
270
- "properties": {
271
- "type": {
272
- "type": "string",
273
- "enum": [
274
- "noise",
275
- "wave",
276
- "voronoi"
277
- ]
278
- },
279
- "scale": {
280
- "type": "number",
281
- "exclusiveMinimum": 0,
282
- "description": "Pattern density in object space. Larger is finer."
283
- },
284
- "detail": {
285
- "type": "number",
286
- "minimum": 0,
287
- "maximum": 16
288
- },
289
- "distortion": {
290
- "type": "number",
291
- "minimum": 0
292
- },
293
- "stretch": {
294
- "type": "array",
295
- "minItems": 3,
296
- "maxItems": 3,
297
- "items": {
298
- "type": "number",
299
- "exclusiveMinimum": 0
300
- },
301
- "description": "Per-axis multiplier on the mapping, so a grain can run along one direction (wood) instead of being isotropic."
302
- },
303
- "bump": {
304
- "type": "number",
305
- "minimum": 0,
306
- "maximum": 1,
307
- "description": "Strength of the normal perturbation. 0 leaves the surface smooth."
308
- },
309
- "roughnessVariation": {
310
- "type": "number",
311
- "minimum": 0,
312
- "maximum": 1,
313
- "description": "How far the pattern swings roughness around its authored value, which is what breaks a highlight up instead of letting it read as plastic."
291
+ "oneOf": [
292
+ {
293
+ "type": "object",
294
+ "additionalProperties": false,
295
+ "required": [
296
+ "type",
297
+ "scale"
298
+ ],
299
+ "properties": {
300
+ "type": {
301
+ "type": "string",
302
+ "enum": [
303
+ "noise",
304
+ "wave",
305
+ "voronoi"
306
+ ]
307
+ },
308
+ "scale": {
309
+ "type": "number",
310
+ "exclusiveMinimum": 0,
311
+ "description": "Pattern density in the selected coordinate space. Larger is finer; UV density is relative to the UV layout."
312
+ },
313
+ "detail": {
314
+ "type": "number",
315
+ "minimum": 0,
316
+ "maximum": 16
317
+ },
318
+ "distortion": {
319
+ "type": "number",
320
+ "minimum": 0
321
+ },
322
+ "stretch": {
323
+ "type": "array",
324
+ "minItems": 3,
325
+ "maxItems": 3,
326
+ "items": {
327
+ "type": "number",
328
+ "exclusiveMinimum": 0
329
+ },
330
+ "description": "Per-axis multiplier on the mapping, so a grain can run along one direction (wood) instead of being isotropic."
331
+ },
332
+ "bump": {
333
+ "type": "number",
334
+ "minimum": 0,
335
+ "maximum": 1,
336
+ "description": "Strength of the normal perturbation. 0 leaves the surface smooth."
337
+ },
338
+ "roughnessVariation": {
339
+ "type": "number",
340
+ "minimum": 0,
341
+ "maximum": 1,
342
+ "description": "How far the pattern swings roughness around its authored value, which is what breaks a highlight up instead of letting it read as plastic."
343
+ },
344
+ "colorVariation": {
345
+ "type": "number",
346
+ "minimum": 0,
347
+ "maximum": 1,
348
+ "description": "How strongly the pattern darkens the base colour."
349
+ },
350
+ "coordinates": {
351
+ "type": "string",
352
+ "enum": [
353
+ "object"
354
+ ],
355
+ "description": "Coordinate source. Omitted means object space. UV uses the named UV map or the active render UV map on each used surface."
356
+ }
357
+ }
314
358
  },
315
- "colorVariation": {
316
- "type": "number",
317
- "minimum": 0,
318
- "maximum": 1,
319
- "description": "How strongly the pattern darkens the base colour."
359
+ {
360
+ "type": "object",
361
+ "additionalProperties": false,
362
+ "required": [
363
+ "type",
364
+ "scale",
365
+ "coordinates"
366
+ ],
367
+ "properties": {
368
+ "type": {
369
+ "type": "string",
370
+ "enum": [
371
+ "noise",
372
+ "wave",
373
+ "voronoi"
374
+ ]
375
+ },
376
+ "scale": {
377
+ "type": "number",
378
+ "exclusiveMinimum": 0,
379
+ "description": "Pattern density in the selected coordinate space. Larger is finer; UV density is relative to the UV layout."
380
+ },
381
+ "detail": {
382
+ "type": "number",
383
+ "minimum": 0,
384
+ "maximum": 16
385
+ },
386
+ "distortion": {
387
+ "type": "number",
388
+ "minimum": 0
389
+ },
390
+ "stretch": {
391
+ "type": "array",
392
+ "minItems": 3,
393
+ "maxItems": 3,
394
+ "items": {
395
+ "type": "number",
396
+ "exclusiveMinimum": 0
397
+ },
398
+ "description": "Per-axis multiplier on the mapping, so a grain can run along one direction (wood) instead of being isotropic."
399
+ },
400
+ "bump": {
401
+ "type": "number",
402
+ "minimum": 0,
403
+ "maximum": 1,
404
+ "description": "Strength of the normal perturbation. 0 leaves the surface smooth."
405
+ },
406
+ "roughnessVariation": {
407
+ "type": "number",
408
+ "minimum": 0,
409
+ "maximum": 1,
410
+ "description": "How far the pattern swings roughness around its authored value, which is what breaks a highlight up instead of letting it read as plastic."
411
+ },
412
+ "colorVariation": {
413
+ "type": "number",
414
+ "minimum": 0,
415
+ "maximum": 1,
416
+ "description": "How strongly the pattern darkens the base colour."
417
+ },
418
+ "coordinates": {
419
+ "type": "string",
420
+ "enum": [
421
+ "uv"
422
+ ],
423
+ "description": "Coordinate source. Omitted means object space. UV uses the named UV map or the active render UV map on each used surface."
424
+ },
425
+ "uvMap": {
426
+ "type": "string",
427
+ "minLength": 1,
428
+ "pattern": "\\S",
429
+ "description": "Existing UV map name. Requires coordinates: uv. Omit to use the active render UV map."
430
+ }
431
+ }
320
432
  }
321
- }
433
+ ]
322
434
  },
323
435
  "material": {
324
436
  "type": "object",
@@ -381,11 +493,29 @@
381
493
  "type": "number",
382
494
  "minimum": 0,
383
495
  "maximum": 1
496
+ },
497
+ "anisotropic": {
498
+ "type": "number",
499
+ "minimum": 0,
500
+ "maximum": 1,
501
+ "description": "Cycles Principled specular anisotropy. 0 disables; positive values need an explicit material tangent."
502
+ },
503
+ "anisotropicRotation": {
504
+ "type": "number",
505
+ "minimum": 0,
506
+ "maximum": 1,
507
+ "description": "Rotate the anisotropic tangent in turns: 0.25 is 90 degrees; 1 is one full turn. Nonzero values need an explicit tangent."
384
508
  }
385
509
  }
386
510
  },
387
511
  "texture": {
388
512
  "$ref": "#/$defs/proceduralTexture"
513
+ },
514
+ "images": {
515
+ "$ref": "#/$defs/materialImages"
516
+ },
517
+ "tangent": {
518
+ "$ref": "#/$defs/materialTangent"
389
519
  }
390
520
  }
391
521
  },
@@ -411,7 +541,7 @@
411
541
  "required": [
412
542
  "shape"
413
543
  ],
414
- "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).",
544
+ "description": "Deterministic procedural geometry. Lathe revolves a radius/height profile; handled_cup constructs a closed cup with shared-boundary flared handle roots. Coupled parameter constraints are checked before generation and the discrete output is validated.",
415
545
  "properties": {
416
546
  "shape": {
417
547
  "type": "string",
@@ -422,7 +552,10 @@
422
552
  "cone",
423
553
  "plane",
424
554
  "torus",
425
- "rounded_box"
555
+ "rounded_box",
556
+ "lathe",
557
+ "curve",
558
+ "handled_cup"
426
559
  ]
427
560
  },
428
561
  "size": {
@@ -463,6 +596,112 @@
463
596
  "maximum": 16
464
597
  }
465
598
  }
599
+ },
600
+ "profile": {
601
+ "type": "array",
602
+ "minItems": 2,
603
+ "maxItems": 128,
604
+ "items": {
605
+ "type": "array",
606
+ "minItems": 2,
607
+ "maxItems": 2,
608
+ "items": {
609
+ "type": "number"
610
+ }
611
+ },
612
+ "description": "Lathe boundary in ordered [radius, height] points, in scene units. Radius must be nonnegative. Do not repeat the first point to close it."
613
+ },
614
+ "closedProfile": {
615
+ "type": "boolean",
616
+ "description": "Join the final profile point to the first, for annular or hollow sections. Default false."
617
+ },
618
+ "capEnds": {
619
+ "type": "boolean",
620
+ "description": "Close open lathe rings or curve ends. Default true; closed paths and profiles need no caps."
621
+ },
622
+ "path": {
623
+ "type": "array",
624
+ "minItems": 2,
625
+ "maxItems": 128,
626
+ "items": {
627
+ "$ref": "#/$defs/vec3"
628
+ },
629
+ "description": "Ordered local-space points for a swept round section."
630
+ },
631
+ "pathClosed": {
632
+ "type": "boolean",
633
+ "description": "Connect the last path point to the first. Default false."
634
+ },
635
+ "pathInterpolation": {
636
+ "type": "string",
637
+ "enum": [
638
+ "poly",
639
+ "bezier"
640
+ ],
641
+ "description": "poly preserves straight segments; bezier uses automatic smooth handles. Default poly."
642
+ },
643
+ "curveResolution": {
644
+ "type": "integer",
645
+ "minimum": 1,
646
+ "maximum": 64
647
+ },
648
+ "bevelResolution": {
649
+ "type": "integer",
650
+ "minimum": 0,
651
+ "maximum": 16
652
+ },
653
+ "height": {
654
+ "$ref": "#/$defs/positiveNumber"
655
+ },
656
+ "wallThickness": {
657
+ "$ref": "#/$defs/positiveNumber"
658
+ },
659
+ "baseThickness": {
660
+ "$ref": "#/$defs/positiveNumber"
661
+ },
662
+ "footRound": {
663
+ "$ref": "#/$defs/positiveNumber"
664
+ },
665
+ "handleRadius": {
666
+ "$ref": "#/$defs/positiveNumber"
667
+ },
668
+ "handleLower": {
669
+ "$ref": "#/$defs/positiveNumber"
670
+ },
671
+ "handleUpper": {
672
+ "$ref": "#/$defs/positiveNumber"
673
+ },
674
+ "rootRadius": {
675
+ "$ref": "#/$defs/positiveNumber"
676
+ },
677
+ "rootLength": {
678
+ "$ref": "#/$defs/positiveNumber"
679
+ },
680
+ "sectionSegments": {
681
+ "type": "integer",
682
+ "minimum": 32,
683
+ "maximum": 96
684
+ },
685
+ "handleSegments": {
686
+ "type": "integer",
687
+ "minimum": 24,
688
+ "maximum": 128
689
+ },
690
+ "rootSegments": {
691
+ "type": "integer",
692
+ "minimum": 8,
693
+ "maximum": 48
694
+ },
695
+ "wallRows": {
696
+ "type": "integer",
697
+ "minimum": 16,
698
+ "maximum": 64
699
+ },
700
+ "rootTension": {
701
+ "type": "number",
702
+ "minimum": 1,
703
+ "maximum": 2.5,
704
+ "description": "Dimensionless scale of both endpoint tangent speeds for the quintic attachment. Missing or 1 preserves the original surface. Values above 1.5 require the extended recipe capability. Native output validation still applies."
466
705
  }
467
706
  }
468
707
  },
@@ -495,7 +734,7 @@
495
734
  },
496
735
  "rigidBody": {
497
736
  "$ref": "#/$defs/rigidBody",
498
- "description": "This entity takes part in physics (SPEC \u00a720 M6, complex simulation). WHAT THIS SLICE DOES: rigid bodies \u2014 active ones fall and collide, passive ones are what they collide with, and the compile BAKES the result into the checkpoint so a render shows it. WHAT IT DOES NOT DO YET: cloth, soft bodies and fluids, which are the next slices."
737
+ "description": "This entity takes part in physics (SPEC §20 M6, complex simulation). WHAT THIS SLICE DOES: rigid bodies — active ones fall and collide, passive ones are what they collide with, and the compile BAKES the result into the checkpoint so a render shows it. WHAT IT DOES NOT DO YET: cloth, soft bodies and fluids, which are the next slices."
499
738
  },
500
739
  "cloth": {
501
740
  "$ref": "#/$defs/cloth",
@@ -505,6 +744,10 @@
505
744
  "$ref": "#/$defs/softBody",
506
745
  "description": "This entity is simulated as a SOFT BODY: it deforms and it remembers its shape, which is what separates it from cloth. A separate field because it is a separate modifier with separate settings; WHAT THIS DOES NOT DO YET: fluids, the last slice."
507
746
  },
747
+ "fluid": {
748
+ "$ref": "#/$defs/fluid",
749
+ "description": "This entity takes part in a FLUID simulation (SPEC §20 M6, complex simulation). Unlike the other three mechanisms, a fluid needs more than one entity — a domain and something flowing into it — which is why the field carries a role."
750
+ },
508
751
  "generator": {
509
752
  "$ref": "#/$defs/generator"
510
753
  },
@@ -524,10 +767,22 @@
524
767
  },
525
768
  "locked": {
526
769
  "type": "boolean",
527
- "description": "A locked entity may still be patched explicitly, but the orchestrator must not propose changes to it autonomously (SPEC \u00a712)."
770
+ "description": "A locked entity may still be patched explicitly, but the orchestrator must not propose changes to it autonomously (SPEC §12)."
528
771
  },
529
772
  "visible": {
530
773
  "type": "boolean"
774
+ },
775
+ "modifiers": {
776
+ "type": "array",
777
+ "maxItems": 8,
778
+ "items": {
779
+ "$ref": "#/$defs/modelModifier"
780
+ },
781
+ "description": "Ordered mesh operations, applied before animation and simulation. Boolean dependencies compile before their consumers. Each participating entity must contain exactly one mesh."
782
+ },
783
+ "materialBindings": {
784
+ "$ref": "#/$defs/materialBindings",
785
+ "description": "Asset mesh part overrides, preserving original slots and face indices. Part-wide overrides precede slot-specific overrides."
531
786
  }
532
787
  }
533
788
  },
@@ -709,7 +964,7 @@
709
964
  "bone"
710
965
  ],
711
966
  "default": "entity",
712
- "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."
967
+ "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 — and `bone` is what made a rig able to move rather than merely exist."
713
968
  },
714
969
  "targetEntityId": {
715
970
  "type": "string",
@@ -718,7 +973,7 @@
718
973
  "boneName": {
719
974
  "type": "string",
720
975
  "maxLength": 64,
721
- "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."
976
+ "description": "Required when targetKind is `bone`: the name of the bone inside the named armature. A name the armature does not have is refused — a track that silently animates nothing is a scene that claims to move and does not."
722
977
  },
723
978
  "property": {
724
979
  "type": "string",
@@ -744,7 +999,9 @@
744
999
  "baseColor.b",
745
1000
  "emissionColor.r",
746
1001
  "emissionColor.g",
747
- "emissionColor.b"
1002
+ "emissionColor.b",
1003
+ "anisotropic",
1004
+ "anisotropicRotation"
748
1005
  ],
749
1006
  "description": "The channel to animate. Transform channels apply to entities and cameras; material parameters apply to materials and use the same names `material.parameter.update` takes."
750
1007
  },
@@ -822,7 +1079,7 @@
822
1079
  "maxSamplesBudget": {
823
1080
  "type": "integer",
824
1081
  "minimum": 1,
825
- "description": "Upper bound a render action may use. Guards the render-cost threshold of SPEC \u00a715.4."
1082
+ "description": "Upper bound a render action may use. Guards the render-cost threshold of SPEC §15.4."
826
1083
  }
827
1084
  }
828
1085
  },
@@ -905,7 +1162,7 @@
905
1162
  "active",
906
1163
  "passive"
907
1164
  ],
908
- "description": "`active` means physics moves it; `passive` means it moves nothing and everything collides with it \u2014 a floor is passive, which is why the two are one field rather than a boolean: a body that is not simulated and a body that is simulated are not the only two things a body can be."
1165
+ "description": "`active` means physics moves it; `passive` means it moves nothing and everything collides with it — a floor is passive, which is why the two are one field rather than a boolean: a body that is not simulated and a body that is simulated are not the only two things a body can be."
909
1166
  },
910
1167
  "mass": {
911
1168
  "type": "number",
@@ -954,7 +1211,7 @@
954
1211
  "type": "number",
955
1212
  "minimum": 0,
956
1213
  "maximum": 1,
957
- "description": "How strongly the body remembers its shape, 0 to 1. Blender's own default applies when absent, and that default is high \u2014 which is why a soft body falls while keeping its shape rather than collapsing, and why both of those are asserted."
1214
+ "description": "How strongly the body remembers its shape, 0 to 1. Blender's own default applies when absent, and that default is high — which is why a soft body falls while keeping its shape rather than collapsing, and why both of those are asserted."
958
1215
  },
959
1216
  "mass": {
960
1217
  "type": "number",
@@ -962,6 +1219,374 @@
962
1219
  "description": "Kilograms. Blender's own default applies when absent."
963
1220
  }
964
1221
  }
1222
+ },
1223
+ "fluid": {
1224
+ "type": "object",
1225
+ "additionalProperties": false,
1226
+ "required": [
1227
+ "role"
1228
+ ],
1229
+ "properties": {
1230
+ "role": {
1231
+ "type": "string",
1232
+ "enum": [
1233
+ "domain",
1234
+ "inflow",
1235
+ "outflow"
1236
+ ],
1237
+ "description": "A fluid takes at least TWO entities: one DOMAIN, which is the volume the liquid lives in, and one or more INFLOW/OUTFLOW objects inside it. That is why this is a role rather than a flag — there is no such thing as a single entity that is a fluid."
1238
+ },
1239
+ "domainType": {
1240
+ "type": "string",
1241
+ "enum": [
1242
+ "liquid",
1243
+ "gas"
1244
+ ],
1245
+ "default": "liquid",
1246
+ "description": "On a domain: what fills it. THE DEFAULT IS `liquid` AND THAT IS DELIBERATE: Blender's own default is `gas`, and MEASURED, a LIQUID inflow into a GAS domain produces nothing at all — the bake reports success, both caches report baked, and the domain's evaluated mesh is its own cube. A default that produces nothing while every signal says it worked is the exact trap this product exists around."
1247
+ },
1248
+ "resolution": {
1249
+ "type": "integer",
1250
+ "minimum": 8,
1251
+ "maximum": 256,
1252
+ "description": "On a domain: the simulation's grid resolution. It is the cost knob — the bake's time and memory grow with it — so it is stated rather than left to a default nobody can see."
1253
+ },
1254
+ "flowType": {
1255
+ "type": "string",
1256
+ "enum": [
1257
+ "liquid",
1258
+ "smoke",
1259
+ "fire",
1260
+ "both"
1261
+ ],
1262
+ "default": "liquid",
1263
+ "description": "On an inflow or outflow: what it emits."
1264
+ }
1265
+ }
1266
+ },
1267
+ "modelModifier": {
1268
+ "oneOf": [
1269
+ {
1270
+ "type": "object",
1271
+ "additionalProperties": false,
1272
+ "required": [
1273
+ "type",
1274
+ "thickness"
1275
+ ],
1276
+ "properties": {
1277
+ "type": {
1278
+ "const": "solidify"
1279
+ },
1280
+ "thickness": {
1281
+ "type": "number"
1282
+ },
1283
+ "offset": {
1284
+ "type": "number",
1285
+ "minimum": -1,
1286
+ "maximum": 1
1287
+ }
1288
+ }
1289
+ },
1290
+ {
1291
+ "type": "object",
1292
+ "additionalProperties": false,
1293
+ "required": [
1294
+ "type",
1295
+ "axis"
1296
+ ],
1297
+ "properties": {
1298
+ "type": {
1299
+ "const": "mirror"
1300
+ },
1301
+ "axis": {
1302
+ "type": "string",
1303
+ "enum": [
1304
+ "x",
1305
+ "y",
1306
+ "z"
1307
+ ]
1308
+ },
1309
+ "merge": {
1310
+ "type": "boolean"
1311
+ }
1312
+ }
1313
+ },
1314
+ {
1315
+ "type": "object",
1316
+ "additionalProperties": false,
1317
+ "required": [
1318
+ "type",
1319
+ "count",
1320
+ "offset"
1321
+ ],
1322
+ "properties": {
1323
+ "type": {
1324
+ "const": "array"
1325
+ },
1326
+ "count": {
1327
+ "type": "integer",
1328
+ "minimum": 2,
1329
+ "maximum": 64
1330
+ },
1331
+ "offset": {
1332
+ "$ref": "#/$defs/vec3"
1333
+ }
1334
+ }
1335
+ },
1336
+ {
1337
+ "type": "object",
1338
+ "additionalProperties": false,
1339
+ "required": [
1340
+ "type",
1341
+ "operation",
1342
+ "targetEntityId"
1343
+ ],
1344
+ "properties": {
1345
+ "type": {
1346
+ "const": "boolean"
1347
+ },
1348
+ "operation": {
1349
+ "type": "string",
1350
+ "enum": [
1351
+ "union",
1352
+ "difference",
1353
+ "intersect"
1354
+ ]
1355
+ },
1356
+ "targetEntityId": {
1357
+ "$ref": "#/$defs/id"
1358
+ }
1359
+ }
1360
+ },
1361
+ {
1362
+ "type": "object",
1363
+ "additionalProperties": false,
1364
+ "required": [
1365
+ "type",
1366
+ "width"
1367
+ ],
1368
+ "properties": {
1369
+ "type": {
1370
+ "const": "bevel"
1371
+ },
1372
+ "width": {
1373
+ "type": "number",
1374
+ "exclusiveMinimum": 0
1375
+ },
1376
+ "segments": {
1377
+ "type": "integer",
1378
+ "minimum": 1,
1379
+ "maximum": 16
1380
+ },
1381
+ "angle": {
1382
+ "type": "number",
1383
+ "minimum": 0,
1384
+ "maximum": 180
1385
+ },
1386
+ "miterInner": {
1387
+ "type": "string",
1388
+ "enum": [
1389
+ "arc",
1390
+ "sharp"
1391
+ ],
1392
+ "default": "arc",
1393
+ "description": "Inner corner join for this bevel modifier. Omitted values preserve the existing arc behavior."
1394
+ }
1395
+ }
1396
+ }
1397
+ ]
1398
+ },
1399
+ "imageBinding": {
1400
+ "type": "object",
1401
+ "additionalProperties": false,
1402
+ "required": [
1403
+ "assetId"
1404
+ ],
1405
+ "properties": {
1406
+ "assetId": {
1407
+ "$ref": "#/$defs/id"
1408
+ },
1409
+ "scale": {
1410
+ "$ref": "#/$defs/vec3"
1411
+ },
1412
+ "offset": {
1413
+ "$ref": "#/$defs/vec3"
1414
+ },
1415
+ "uvMap": {
1416
+ "type": "string",
1417
+ "minLength": 1,
1418
+ "maxLength": 64
1419
+ },
1420
+ "channel": {
1421
+ "type": "string",
1422
+ "enum": [
1423
+ "r",
1424
+ "g",
1425
+ "b",
1426
+ "a"
1427
+ ]
1428
+ },
1429
+ "strength": {
1430
+ "type": "number",
1431
+ "minimum": 0,
1432
+ "maximum": 10
1433
+ }
1434
+ }
1435
+ },
1436
+ "materialImages": {
1437
+ "type": "object",
1438
+ "additionalProperties": false,
1439
+ "properties": {
1440
+ "baseColor": {
1441
+ "$ref": "#/$defs/imageBinding"
1442
+ },
1443
+ "roughness": {
1444
+ "$ref": "#/$defs/imageBinding"
1445
+ },
1446
+ "metallic": {
1447
+ "$ref": "#/$defs/imageBinding"
1448
+ },
1449
+ "normal": {
1450
+ "$ref": "#/$defs/imageBinding"
1451
+ },
1452
+ "alpha": {
1453
+ "$ref": "#/$defs/imageBinding"
1454
+ },
1455
+ "emissionColor": {
1456
+ "$ref": "#/$defs/imageBinding"
1457
+ }
1458
+ }
1459
+ },
1460
+ "materialBinding": {
1461
+ "type": "object",
1462
+ "additionalProperties": false,
1463
+ "required": [
1464
+ "partId",
1465
+ "materialId"
1466
+ ],
1467
+ "properties": {
1468
+ "partId": {
1469
+ "type": "string",
1470
+ "minLength": 2,
1471
+ "maxLength": 4096,
1472
+ "pattern": "^/",
1473
+ "description": "Exact source path returned in assetParts; JSON Pointer escaped segments."
1474
+ },
1475
+ "slotIndex": {
1476
+ "type": "integer",
1477
+ "minimum": 0,
1478
+ "description": "Original material slot index. Omit to replace every slot of this mesh part."
1479
+ },
1480
+ "materialId": {
1481
+ "$ref": "#/$defs/id"
1482
+ }
1483
+ }
1484
+ },
1485
+ "materialBindings": {
1486
+ "type": "array",
1487
+ "maxItems": 128,
1488
+ "items": {
1489
+ "$ref": "#/$defs/materialBinding"
1490
+ }
1491
+ },
1492
+ "materialTangent": {
1493
+ "oneOf": [
1494
+ {
1495
+ "type": "object",
1496
+ "additionalProperties": false,
1497
+ "required": [
1498
+ "mode",
1499
+ "uvMap"
1500
+ ],
1501
+ "properties": {
1502
+ "mode": {
1503
+ "const": "uv"
1504
+ },
1505
+ "uvMap": {
1506
+ "type": "string",
1507
+ "minLength": 1,
1508
+ "maxLength": 63
1509
+ }
1510
+ },
1511
+ "description": "Use the tangent along the named UV map U direction. The map must exist on every mesh face using this material."
1512
+ },
1513
+ {
1514
+ "type": "object",
1515
+ "additionalProperties": false,
1516
+ "required": [
1517
+ "mode",
1518
+ "axis"
1519
+ ],
1520
+ "properties": {
1521
+ "mode": {
1522
+ "const": "radial"
1523
+ },
1524
+ "axis": {
1525
+ "enum": [
1526
+ "x",
1527
+ "y",
1528
+ "z"
1529
+ ]
1530
+ }
1531
+ },
1532
+ "description": "Blender cylindrical-projection tangent around the selected object-local axis; no UV map required."
1533
+ }
1534
+ ]
1535
+ },
1536
+ "referenceImage": {
1537
+ "type": "object",
1538
+ "additionalProperties": false,
1539
+ "required": [
1540
+ "id",
1541
+ "assetId",
1542
+ "sha256",
1543
+ "label",
1544
+ "purposes"
1545
+ ],
1546
+ "properties": {
1547
+ "id": {
1548
+ "$ref": "#/$defs/id"
1549
+ },
1550
+ "assetId": {
1551
+ "$ref": "#/$defs/id"
1552
+ },
1553
+ "sha256": {
1554
+ "type": "string",
1555
+ "pattern": "^[a-f0-9]{64}$"
1556
+ },
1557
+ "label": {
1558
+ "type": "string",
1559
+ "minLength": 1,
1560
+ "maxLength": 160
1561
+ },
1562
+ "purposes": {
1563
+ "type": "array",
1564
+ "minItems": 1,
1565
+ "maxItems": 4,
1566
+ "items": {
1567
+ "type": "string",
1568
+ "enum": [
1569
+ "geometry",
1570
+ "materials",
1571
+ "lighting",
1572
+ "goalFit"
1573
+ ]
1574
+ },
1575
+ "description": "Distinct review dimensions this reference informs; duplicate purposes are rejected semantically."
1576
+ },
1577
+ "notes": {
1578
+ "type": "string",
1579
+ "maxLength": 1000
1580
+ }
1581
+ }
1582
+ },
1583
+ "referenceImages": {
1584
+ "type": "array",
1585
+ "maxItems": 4,
1586
+ "items": {
1587
+ "$ref": "#/$defs/referenceImage"
1588
+ },
1589
+ "description": "Visual review references only. Each id is unique and points to a declared, hash-pinned PNG/JPEG project asset; this does not apply an object texture or environment map."
965
1590
  }
966
1591
  }
967
1592
  }