@instruktlabs/kiln 1.2.1 → 1.2.3

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.
Files changed (219) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +86 -1
  4. package/README.md +18 -0
  5. package/dist/agent-run.mjs +6392 -1477
  6. package/dist/build.json +23 -23
  7. package/dist/cli.mjs +8453 -3407
  8. package/dist/evaluator-worker.mjs +16608 -12387
  9. package/dist/mcp-engine.mjs +6746 -1725
  10. package/dist/mcp-server.mjs +4 -4
  11. package/dist/viewer/app.js +203 -199
  12. package/dist/viewer/chat.html +209 -205
  13. package/dist/viewer/style.css +10 -0
  14. package/docs/atlas-provenance.md +23 -0
  15. package/docs/community-exporter.md +27 -0
  16. package/docs/export-profiles.md +20 -0
  17. package/docs/install.md +7 -6
  18. package/docs/material-library.md +9 -5
  19. package/docs/migration.md +16 -0
  20. package/docs/procedural-surfaces-v3.md +276 -0
  21. package/docs/projects-and-live-review.md +1 -1
  22. package/docs/rendering.md +41 -0
  23. package/docs/runtime.md +3 -3
  24. package/docs/sdk.md +55 -2
  25. package/docs/tools-operations.md +2 -2
  26. package/docs/tools.md +3 -3
  27. package/lib/asset-materials-node.d.ts +3 -3
  28. package/lib/asset-materials-node.js +29 -33
  29. package/lib/assets-node.d.ts +1 -1
  30. package/lib/assets-node.js +11 -4
  31. package/lib/assets.d.ts +4 -2
  32. package/lib/assets.js +61 -16
  33. package/lib/atlas-provenance.d.ts +86 -0
  34. package/lib/atlas-provenance.js +448 -0
  35. package/lib/character.d.ts +11 -2
  36. package/lib/character.js +90 -2
  37. package/lib/community-exporter.d.ts +3 -2
  38. package/lib/community-exporter.js +139 -3
  39. package/lib/discovery/helper-contracts.js +161 -7
  40. package/lib/discovery/helper-specs.js +49 -4
  41. package/lib/discovery/shapes.js +20 -7
  42. package/lib/engine-identity.d.ts +1 -1
  43. package/lib/engine-identity.js +1 -1
  44. package/lib/evaluator/authoring-diagnostic.d.ts +1 -1
  45. package/lib/evaluator/authoring-diagnostic.js +12 -0
  46. package/lib/evaluator/protocol.d.ts +2 -2
  47. package/lib/evaluator/protocol.js +16 -6
  48. package/lib/evaluator/subprocess.js +2 -2
  49. package/lib/exporter-node.js +9 -1
  50. package/lib/geometry-budget.d.ts +7 -0
  51. package/lib/geometry-budget.js +8 -0
  52. package/lib/gltf-texture-binding.d.ts +6 -0
  53. package/lib/gltf-texture-binding.js +54 -0
  54. package/lib/material-authoring-node.d.ts +6 -0
  55. package/lib/material-authoring-node.js +49 -0
  56. package/lib/material-authoring-schema.d.ts +871 -0
  57. package/lib/material-authoring-schema.js +310 -0
  58. package/lib/material-export.d.ts +5 -0
  59. package/lib/material-export.js +21 -0
  60. package/lib/material-library-node.d.ts +34 -8
  61. package/lib/material-library-node.js +691 -11
  62. package/lib/material-library.d.ts +3704 -4
  63. package/lib/material-library.js +272 -0
  64. package/lib/mcp-core.d.ts +1 -1
  65. package/lib/pinned-material-map.d.ts +33 -0
  66. package/lib/pinned-material-map.js +1 -0
  67. package/lib/portable-material-runtime.d.ts +5 -0
  68. package/lib/portable-material-runtime.js +160 -0
  69. package/lib/portable-sampler-runtime.d.ts +4 -0
  70. package/lib/portable-sampler-runtime.js +26 -0
  71. package/lib/portable-sampler.d.ts +11 -0
  72. package/lib/portable-sampler.js +42 -0
  73. package/lib/primitives.d.ts +15 -3
  74. package/lib/primitives.js +24 -4
  75. package/lib/procedural-material-v2.d.ts +2 -0
  76. package/lib/procedural-material-v2.js +2 -1
  77. package/lib/procedural-material-v3.d.ts +72 -0
  78. package/lib/procedural-material-v3.js +162 -0
  79. package/lib/procedural-texture-v2-compiler.d.ts +31 -0
  80. package/lib/procedural-texture-v2-compiler.js +172 -0
  81. package/lib/procedural-texture-v3-contract.d.ts +125 -0
  82. package/lib/procedural-texture-v3-contract.js +333 -0
  83. package/lib/procedural-texture-v3.d.ts +12 -0
  84. package/lib/procedural-texture-v3.js +165 -0
  85. package/lib/procedural-texture.d.ts +9 -31
  86. package/lib/procedural-texture.js +43 -170
  87. package/lib/project-bundle-node.d.ts +1 -1
  88. package/lib/project-bundle-node.js +70 -13
  89. package/lib/project-bundle.d.ts +91 -3
  90. package/lib/project-bundle.js +48 -7
  91. package/lib/qa/atlas.d.ts +21 -0
  92. package/lib/qa/atlas.js +474 -0
  93. package/lib/qa/index.d.ts +1 -0
  94. package/lib/qa/index.js +1 -0
  95. package/lib/qa/material.js +2 -0
  96. package/lib/qa/skin-integrity.d.ts +18 -0
  97. package/lib/qa/skin-integrity.js +221 -0
  98. package/lib/render-service-host.js +4 -7
  99. package/lib/render.d.ts +3 -3
  100. package/lib/render.js +108 -16
  101. package/lib/skin-binding.d.ts +20 -0
  102. package/lib/skin-binding.js +144 -0
  103. package/lib/texture-atlas-contract.d.ts +97 -0
  104. package/lib/texture-atlas-contract.js +414 -0
  105. package/lib/texture-atlas-pixels.d.ts +73 -0
  106. package/lib/texture-atlas-pixels.js +290 -0
  107. package/lib/texture-atlas.d.ts +10 -0
  108. package/lib/texture-atlas.js +143 -0
  109. package/lib/texture-bake.d.ts +3 -0
  110. package/lib/texture-bake.js +66 -11
  111. package/lib/texture-resolver.d.ts +22 -0
  112. package/lib/texture-resolver.js +24 -0
  113. package/lib/tools/materials.js +8 -7
  114. package/lib/tools/registry.js +5 -16
  115. package/lib/tools/review.js +1 -1
  116. package/lib/uv-data.d.ts +32 -0
  117. package/lib/uv-data.js +172 -0
  118. package/lib/uv-shapes.js +3 -0
  119. package/lib/uv.d.ts +9 -3
  120. package/lib/uv.js +53 -82
  121. package/lib/views/camera.d.ts +3 -3
  122. package/lib/views/camera.js +32 -28
  123. package/lib/views/face-policy.d.ts +9 -0
  124. package/lib/views/face-policy.js +16 -0
  125. package/lib/views/glb.js +218 -24
  126. package/lib/views/pose.d.ts +7 -1
  127. package/lib/views/pose.js +63 -12
  128. package/lib/views/raster.d.ts +6 -2
  129. package/lib/views/raster.js +49 -37
  130. package/lib/views/scene-copy.d.ts +6 -0
  131. package/lib/views/scene-copy.js +73 -0
  132. package/lib/views/scene-raster.js +2 -1
  133. package/lib/views/triangle-coverage.d.ts +8 -0
  134. package/lib/views/triangle-coverage.js +58 -0
  135. package/lib/workspace-node.js +7 -3
  136. package/lib/workspace.d.ts +2 -2
  137. package/package.json +3 -1
  138. package/plugin.json +1 -1
  139. package/plugins/kiln-engine/.claude-plugin/plugin.json +1 -1
  140. package/plugins/kiln-engine/README.md +3 -3
  141. package/plugins/kiln-engine/package-provenance.json +6 -6
  142. package/plugins/kiln-engine/plugin.json +1 -1
  143. package/plugins/kiln-engine/runtime.json +1 -1
  144. package/skills/kiln-author-asset/SKILL.md +1 -1
  145. package/skills/kiln-author-asset/references/camera-cli.md +13 -0
  146. package/skills/kiln-author-asset/references/geometry-recipes.md +134 -0
  147. package/skills/kiln-author-asset/references/projects-and-materials.md +32 -2
  148. package/skills/kiln-compose-scene/references/runtime-scenes.md +19 -0
  149. package/skills/kiln-qa-asset/references/integration-checks.md +37 -2
  150. package/src/asset-materials-node.ts +38 -45
  151. package/src/assets-node.ts +22 -5
  152. package/src/assets.ts +61 -20
  153. package/src/atlas-provenance.ts +522 -0
  154. package/src/character.ts +107 -2
  155. package/src/community-exporter.ts +143 -3
  156. package/src/discovery/helper-contracts.ts +188 -7
  157. package/src/discovery/helper-specs.ts +69 -4
  158. package/src/discovery/shapes.ts +25 -9
  159. package/src/engine-identity.ts +1 -1
  160. package/src/evaluator/authoring-diagnostic.ts +19 -1
  161. package/src/evaluator/protocol.ts +19 -9
  162. package/src/evaluator/subprocess.ts +2 -2
  163. package/src/exporter-node.ts +12 -1
  164. package/src/generated/mcp-manifest.json +3 -3
  165. package/src/geometry-budget.ts +8 -0
  166. package/src/gltf-texture-binding.ts +60 -0
  167. package/src/material-authoring-node.ts +68 -0
  168. package/src/material-authoring-schema.ts +343 -0
  169. package/src/material-export.ts +25 -0
  170. package/src/material-library-node.ts +840 -16
  171. package/src/material-library.ts +348 -5
  172. package/src/pinned-material-map.ts +43 -0
  173. package/src/portable-material-runtime.ts +200 -0
  174. package/src/portable-sampler-runtime.ts +40 -0
  175. package/src/portable-sampler.ts +54 -0
  176. package/src/primitives.ts +98 -4
  177. package/src/procedural-material-v2.ts +2 -1
  178. package/src/procedural-material-v3.ts +224 -0
  179. package/src/procedural-texture-v2-compiler.ts +234 -0
  180. package/src/procedural-texture-v3-contract.ts +437 -0
  181. package/src/procedural-texture-v3.ts +220 -0
  182. package/src/procedural-texture.ts +61 -229
  183. package/src/project-bundle-node.ts +106 -14
  184. package/src/project-bundle.ts +61 -18
  185. package/src/project-cli.ts +29 -17
  186. package/src/qa/atlas.ts +597 -0
  187. package/src/qa/index.ts +1 -0
  188. package/src/qa/material.ts +2 -0
  189. package/src/qa/skin-integrity.ts +291 -0
  190. package/src/render-service-host.ts +4 -6
  191. package/src/render.ts +133 -15
  192. package/src/skin-binding.ts +225 -0
  193. package/src/texture-atlas-contract.ts +558 -0
  194. package/src/texture-atlas-pixels.ts +456 -0
  195. package/src/texture-atlas.ts +185 -0
  196. package/src/texture-bake.ts +88 -10
  197. package/src/texture-resolver.ts +55 -0
  198. package/src/tools/materials.ts +15 -11
  199. package/src/tools/registry.ts +5 -15
  200. package/src/tools/review.ts +1 -1
  201. package/src/uv-data.ts +238 -0
  202. package/src/uv-shapes.ts +3 -0
  203. package/src/uv.ts +73 -128
  204. package/src/viewer/app.ts +37 -0
  205. package/src/viewer/live-panel.ts +57 -1
  206. package/src/viewer/material-preview-panel.ts +140 -0
  207. package/src/viewer/material-preview.ts +138 -0
  208. package/src/viewer/materials-panel.ts +34 -5
  209. package/src/viewer/navigation.ts +2 -0
  210. package/src/views/camera.ts +37 -40
  211. package/src/views/face-policy.ts +20 -0
  212. package/src/views/glb.ts +294 -17
  213. package/src/views/pose.ts +71 -13
  214. package/src/views/raster.ts +73 -46
  215. package/src/views/scene-copy.ts +86 -0
  216. package/src/views/scene-raster.ts +2 -1
  217. package/src/views/triangle-coverage.ts +78 -0
  218. package/src/workspace-node.ts +11 -3
  219. package/src/workspace.ts +2 -2
@@ -1,6 +1,10 @@
1
1
  /** Portable material-library records. Source URLs are provenance, never loading instructions. */
2
2
  import { z } from 'zod';
3
3
  import { canonicalizeProceduralTextureSpecV2, } from "./procedural-material-v2.js";
4
+ import { canonicalizeProceduralTextureSpecV3, PROCEDURAL_TEXTURE_V3_COMPILER_ID, } from "./procedural-texture-v3-contract.js";
5
+ import { canonicalizePortableSamplerV1 } from "./portable-sampler.js";
6
+ import { canonicalizeTextureAtlasSpecV1, validateTextureAtlasManifestV1, TEXTURE_ATLAS_COMPILER_ID, } from "./texture-atlas-contract.js";
7
+ import { makeMaterialAuthoringSchemas } from "./material-authoring-schema.js";
4
8
  export const MATERIAL_LIBRARY_LIMITS = Object.freeze({
5
9
  maxMapBytes: 8 * 1024 * 1024,
6
10
  maxRecordBytes: 32 * 1024 * 1024,
@@ -411,6 +415,23 @@ export function materialLibraryResourceId(manifest, slot) {
411
415
  return `kiln.library.${manifest.revisionId.slice(7)}.${slotNames[slot]}`;
412
416
  }
413
417
  export function materialLibraryPortableSpec(manifest) {
418
+ if (manifest.schemaVersion === 2) {
419
+ const modern = validateMaterialManifestAny(manifest);
420
+ return {
421
+ schemaVersion: 3,
422
+ model: 'pbrMetallicRoughness',
423
+ name: modern.name,
424
+ ...modern.parameters,
425
+ textures: Object.fromEntries(modern.maps.map((map) => [
426
+ map.slot,
427
+ {
428
+ kind: 'resource',
429
+ resourceId: materialLibraryResourceId(modern, map.slot),
430
+ sampler: { ...map.sampler },
431
+ },
432
+ ])),
433
+ };
434
+ }
414
435
  manifest = validateMaterialManifest(manifest);
415
436
  return {
416
437
  schemaVersion: 2,
@@ -423,3 +444,254 @@ export function materialLibraryPortableSpec(manifest) {
423
444
  ])),
424
445
  };
425
446
  }
447
+ /** New versions are explicit; every exported V1 schema/type above stays V1. */
448
+ const authoringSchemas = makeMaterialAuthoringSchemas(materialProceduralSpecSchema);
449
+ const samplerSchema = authoringSchemas.portableSamplerSchema.refine((value) => {
450
+ try {
451
+ return (canonicalMaterialJson(value) === canonicalMaterialJson(canonicalizePortableSamplerV1(value)));
452
+ }
453
+ catch {
454
+ return false;
455
+ }
456
+ });
457
+ const v3SpecSchema = authoringSchemas.canonicalProceduralTextureSpecV3Schema.refine((value) => {
458
+ try {
459
+ return (canonicalMaterialJson(value) ===
460
+ canonicalMaterialJson(canonicalizeProceduralTextureSpecV3(value)));
461
+ }
462
+ catch {
463
+ return false;
464
+ }
465
+ });
466
+ const atlasSpecSchema = authoringSchemas.canonicalTextureAtlasSpecV1Schema.refine((value) => {
467
+ try {
468
+ return (canonicalMaterialJson(value) ===
469
+ canonicalMaterialJson(canonicalizeTextureAtlasSpecV1(value)));
470
+ }
471
+ catch {
472
+ return false;
473
+ }
474
+ });
475
+ const atlasManifestSchema = authoringSchemas.textureAtlasManifestV1Schema;
476
+ export const materialMapPinSchema = z
477
+ .object({
478
+ resourceId: materialLibraryIdSchema,
479
+ revisionId: materialLibraryHashSchema,
480
+ sha256: materialLibraryHashSchema,
481
+ })
482
+ .strict()
483
+ .refine((pin) => pin.sha256 === pin.revisionId, 'Material pin hash must match revision');
484
+ const materialRecipeV2Schema = z.discriminatedUnion('kind', [
485
+ z
486
+ .object({
487
+ kind: z.literal('procedural'),
488
+ compilerId: z.enum(['kiln.procedural-texture.v2', PROCEDURAL_TEXTURE_V3_COMPILER_ID]),
489
+ spec: z.union([materialProceduralSpecSchema, v3SpecSchema]),
490
+ recipeHash: materialLibraryHashSchema,
491
+ pixelHash: materialLibraryHashSchema,
492
+ encoder: z.object({ name: z.literal('sharp'), version: text }).strict(),
493
+ derive: materialNormalDerivationSchema.optional(),
494
+ })
495
+ .strict(),
496
+ z
497
+ .object({
498
+ kind: z.literal('atlas'),
499
+ compilerId: z.literal(TEXTURE_ATLAS_COMPILER_ID),
500
+ spec: atlasSpecSchema,
501
+ manifest: atlasManifestSchema,
502
+ encoder: z.object({ name: z.literal('sharp'), version: text }).strict(),
503
+ })
504
+ .strict()
505
+ .superRefine((recipe, context) => {
506
+ try {
507
+ validateTextureAtlasManifestV1(recipe.manifest, recipe.spec);
508
+ }
509
+ catch {
510
+ context.addIssue({ code: 'custom', message: 'Invalid stored atlas manifest' });
511
+ }
512
+ }),
513
+ ]);
514
+ export const materialManifestV2Schema = materialManifestSchema
515
+ .omit({ schemaVersion: true, maps: true })
516
+ .extend({
517
+ schemaVersion: z.literal(2),
518
+ dependencies: z.array(materialMapPinSchema).max(16),
519
+ maps: z
520
+ .array(materialManifestSchema.shape.maps.element
521
+ .omit({ procedural: true })
522
+ .extend({
523
+ sampler: samplerSchema,
524
+ pixelHash: materialLibraryHashSchema,
525
+ recipe: materialRecipeV2Schema.optional(),
526
+ })
527
+ .strict())
528
+ .min(1)
529
+ .max(5),
530
+ })
531
+ .strict();
532
+ export const materialManifestAnySchema = z.discriminatedUnion('schemaVersion', [
533
+ materialManifestSchema,
534
+ materialManifestV2Schema,
535
+ ]);
536
+ export const materialLibraryPayloadV2Schema = materialLibraryPayloadSchema
537
+ .extend({
538
+ schemaVersion: z.literal(2),
539
+ records: z
540
+ .array(materialLibraryPayloadSchema.shape.records.element
541
+ .extend({ manifest: materialManifestAnySchema })
542
+ .strict())
543
+ .max(MATERIAL_LIBRARY_LIMITS.maxPayloadRecords),
544
+ })
545
+ .strict();
546
+ export const materialLibraryPayloadAnySchema = z.discriminatedUnion('schemaVersion', [
547
+ materialLibraryPayloadSchema,
548
+ materialLibraryPayloadV2Schema,
549
+ ]);
550
+ export function materialRecordDependencyPins(manifest) {
551
+ return manifest.schemaVersion === 1 ? [] : manifest.dependencies.map((pin) => ({ ...pin }));
552
+ }
553
+ /** Opt-in JSON-only standalone creation; external bytes remain host-only. */
554
+ export const proceduralMaterialDraftV2Schema = proceduralMaterialDraftSchema
555
+ .extend({
556
+ schemaVersion: z.literal(2),
557
+ maps: z
558
+ .array(proceduralMaterialDraftSchema.shape.maps.element
559
+ .omit({ procedural: true })
560
+ .extend({
561
+ sampler: samplerSchema,
562
+ procedural: z
563
+ .union([
564
+ materialProceduralSpecSchema,
565
+ authoringSchemas.proceduralTextureSpecV3Schema.refine((value) => {
566
+ try {
567
+ canonicalizeProceduralTextureSpecV3(value);
568
+ return true;
569
+ }
570
+ catch {
571
+ return false;
572
+ }
573
+ }),
574
+ ])
575
+ .optional(),
576
+ atlas: authoringSchemas.textureAtlasSpecV1Schema
577
+ .refine((value) => {
578
+ try {
579
+ canonicalizeTextureAtlasSpecV1(value);
580
+ return true;
581
+ }
582
+ catch {
583
+ return false;
584
+ }
585
+ })
586
+ .optional(),
587
+ })
588
+ .strict()
589
+ .refine((map) => (map.procedural !== undefined) !== (map.atlas !== undefined), 'Supply exactly one procedural or atlas recipe'))
590
+ .min(1)
591
+ .max(5),
592
+ })
593
+ .strict();
594
+ export const proceduralMaterialDraftAnySchema = z.union([
595
+ proceduralMaterialDraftSchema,
596
+ proceduralMaterialDraftV2Schema,
597
+ ]);
598
+ export function validateMaterialManifestAny(value) {
599
+ assertMaterialJson(value);
600
+ if (new TextEncoder().encode(JSON.stringify(value)).length >
601
+ MATERIAL_LIBRARY_LIMITS.maxManifestBytes)
602
+ throw Error('Material manifest exceeds size limit');
603
+ if (!value || typeof value !== 'object' || Array.isArray(value))
604
+ throw Error('Invalid material manifest');
605
+ if (value['schemaVersion'] === 1)
606
+ return validateMaterialManifest(value);
607
+ const manifest = materialManifestV2Schema.parse(value);
608
+ if (new Set(manifest.sources.map((s) => s.id)).size !== manifest.sources.length ||
609
+ new Set(manifest.maps.map((m) => m.slot)).size !== manifest.maps.length)
610
+ throw Error('Duplicate material source or slot');
611
+ let bytes = 0, pixels = 0;
612
+ const pins = new Map();
613
+ for (const map of manifest.maps) {
614
+ const source = manifest.sources.find((s) => s.id === map.sourceId);
615
+ if (!source ||
616
+ map.file !== `${map.slot}.png` ||
617
+ map.usage !== MATERIAL_LIBRARY_USAGE[map.slot] ||
618
+ map.colorSpace !==
619
+ (map.slot === 'baseColor' || map.slot === 'emissive' ? 'srgb' : 'linear') ||
620
+ (map.slot === 'normal') !== (map.normalConvention === 'opengl') ||
621
+ (map.slot === 'metallicRoughness') !==
622
+ (map.channelPacking === 'r-occlusion-g-roughness-b-metallic'))
623
+ throw Error('Material map source/usage/convention mismatch');
624
+ const recipe = map.recipe;
625
+ if (recipe) {
626
+ if (source.kind !== 'procedural' || map.originalFile)
627
+ throw Error('Procedural/atlas map source convention mismatch');
628
+ if (recipe.kind === 'procedural') {
629
+ const spec = recipe.spec;
630
+ if (spec.schemaVersion === 3) {
631
+ if (recipe.compilerId !== PROCEDURAL_TEXTURE_V3_COMPILER_ID ||
632
+ recipe.derive ||
633
+ spec.width !== map.width ||
634
+ spec.height !== map.height)
635
+ throw Error('V3 compiler/dimensions/derivation mismatch');
636
+ }
637
+ else if (recipe.compilerId !== 'kiln.procedural-texture.v2' ||
638
+ spec.size !== map.width ||
639
+ map.width !== map.height ||
640
+ canonicalMaterialJson(spec) !==
641
+ canonicalMaterialJson(canonicalizeProceduralTextureSpecV2(spec)))
642
+ throw Error('V2 compiler/canonical recipe dimensions mismatch');
643
+ if (spec.usage !== map.usage ||
644
+ recipe.pixelHash !== map.pixelHash ||
645
+ (recipe.derive && map.slot !== 'normal'))
646
+ throw Error('Procedural recipe pixel/usage mismatch');
647
+ }
648
+ else {
649
+ validateTextureAtlasManifestV1(recipe.manifest, recipe.spec);
650
+ if (recipe.spec.width !== map.width ||
651
+ recipe.spec.height !== map.height ||
652
+ recipe.spec.usage !== map.usage ||
653
+ canonicalMaterialJson(recipe.spec.sampler) !== canonicalMaterialJson(map.sampler) ||
654
+ recipe.manifest.decodedPixelHash !== map.pixelHash)
655
+ throw Error('Atlas recipe map mismatch');
656
+ for (const region of recipe.spec.regions)
657
+ if (region.source.kind === 'resource') {
658
+ const pin = region.source.pin;
659
+ pins.set(`${pin.resourceId}/${pin.revisionId}`, pin);
660
+ }
661
+ }
662
+ }
663
+ else if (source.kind !== 'external' ||
664
+ !source.originalFiles.some((f) => f.name === map.originalFile))
665
+ throw Error('External material source original file missing');
666
+ bytes += map.bytes;
667
+ pixels += map.width * map.height;
668
+ }
669
+ const actual = manifest.dependencies.map((p) => `${p.resourceId}/${p.revisionId}`);
670
+ if (new Set(actual).size !== actual.length ||
671
+ actual.length !== pins.size ||
672
+ actual.some((k) => !pins.has(k)))
673
+ throw Error('Material dependency pin inventory mismatch');
674
+ if (bytes > MATERIAL_LIBRARY_LIMITS.maxRecordBytes ||
675
+ pixels > MATERIAL_LIBRARY_LIMITS.maxRecordPixels)
676
+ throw Error('Material record exceeds byte/pixel budget');
677
+ return structuredClone(manifest);
678
+ }
679
+ export function validateMaterialRecordShapeAny(record) {
680
+ assertMaterialJson(record, true);
681
+ if (record.manifest.schemaVersion === 1)
682
+ return validateMaterialRecordShape(record);
683
+ if (Object.keys(record).some((k) => k !== 'manifest' && k !== 'files'))
684
+ throw Error('Invalid material record');
685
+ const manifest = validateMaterialManifestAny(record.manifest);
686
+ if (!record.files || Object.keys(record.files).length !== manifest.maps.length)
687
+ throw Error('Material file inventory mismatch');
688
+ for (const map of manifest.maps) {
689
+ const bytes = record.files[map.file];
690
+ if (!(bytes instanceof Uint8Array) || bytes.length !== map.bytes)
691
+ throw Error('Material file inventory mismatch');
692
+ const size = materialPngDimensions(bytes);
693
+ if (size.width !== map.width || size.height !== map.height)
694
+ throw Error('Material PNG dimensions mismatch');
695
+ }
696
+ return manifest;
697
+ }
package/lib/mcp-core.d.ts CHANGED
@@ -23,7 +23,7 @@ import { type ServeStdioOptions, type StdioServerHandle } from '@modelcontextpro
23
23
  import type { AssetLink } from "./assets-resources.js";
24
24
  /** Server identity reported in the handshake and stamped into every 2026-07-28 result. */
25
25
  export declare const MCP_SERVER_NAME = "kiln";
26
- export declare const MCP_SERVER_VERSION = "1.2.1";
26
+ export declare const MCP_SERVER_VERSION = "1.2.3";
27
27
  /**
28
28
  * The spec's optional `instructions` field, returned by `initialize` and
29
29
  * `server/discover`.
@@ -0,0 +1,33 @@
1
+ /** Host-only atlas map capability. Authored code receives neither this port nor bytes. */
2
+ import type { TextureUsage } from "./texture-contract.js";
3
+ export type MaterialMapSlotV1 = 'baseColor' | 'normal' | 'metallicRoughness' | 'emissive' | 'occlusion';
4
+ export type MaterialHashV1 = `sha256:${string}`;
5
+ export interface MaterialMapPinV1 {
6
+ resourceId: string;
7
+ revisionId: string;
8
+ sha256: string;
9
+ }
10
+ export interface VerifiedPinnedMaterialMapDescriptorV1 {
11
+ materialId: string;
12
+ revisionId: MaterialHashV1;
13
+ resourceId: string;
14
+ slot: MaterialMapSlotV1;
15
+ usage: TextureUsage;
16
+ colorSpace: 'srgb' | 'linear';
17
+ width: number;
18
+ height: number;
19
+ encodedByteLength: number;
20
+ imageSha256: MaterialHashV1;
21
+ decodedPixelHash?: MaterialHashV1;
22
+ }
23
+ export interface OwnedMaterialMapPixelsV1 {
24
+ descriptor: VerifiedPinnedMaterialMapDescriptorV1;
25
+ pixels: Uint8Array;
26
+ decodedPixelHash: MaterialHashV1;
27
+ }
28
+ export interface PinnedMaterialMapPortV1 {
29
+ describePinnedMaterialMap(mapResourceId: unknown, pin: MaterialMapPinV1): VerifiedPinnedMaterialMapDescriptorV1 | undefined;
30
+ resolvePinnedMaterialMapPixels(mapResourceId: unknown, pin: MaterialMapPinV1, context?: {
31
+ signal?: AbortSignal;
32
+ }): Promise<OwnedMaterialMapPixelsV1>;
33
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -12,3 +12,8 @@ import { type TextureResolver } from "./texture-resolver.js";
12
12
  export declare function compilePortableMaterialSpecV2(input: unknown, options?: {
13
13
  resolver?: TextureResolver;
14
14
  }): Promise<THREE.MeshStandardMaterial>;
15
+ /** New V3 path owns every mutable texture/source/pixel/encoded-byte binding. */
16
+ export declare function compilePortableMaterialSpecV3(input: unknown, options?: {
17
+ resolver?: TextureResolver;
18
+ signal?: AbortSignal;
19
+ }): Promise<THREE.MeshStandardMaterial>;
@@ -3,6 +3,9 @@ import { DEFAULT_TEXTURE_RESOLVER } from "./texture-resolver.js";
3
3
  import { ProceduralTextureError, canonicalizePortableMaterialSpecV2, } from "./procedural-material-v2.js";
4
4
  import { proceduralTexture } from "./procedural-texture.js";
5
5
  import { pbrMaterial } from "./textures.js";
6
+ import { loadTexture } from "./textures.js";
7
+ import { admitPortableMaterialSpecV3, PORTABLE_MATERIAL_V3_LIMITS, PORTABLE_MATERIAL_SLOT_USAGE, PortableMaterialV3Error, } from "./procedural-material-v3.js";
8
+ import { applyPortableSamplerV1 } from "./portable-sampler-runtime.js";
6
9
  const SLOT_USAGE = {
7
10
  baseColor: 'albedo',
8
11
  normal: 'normal',
@@ -79,3 +82,160 @@ export async function compilePortableMaterialSpecV2(input, options = {}) {
79
82
  spec;
80
83
  return material;
81
84
  }
85
+ /** New V3 path owns every mutable texture/source/pixel/encoded-byte binding. */
86
+ export async function compilePortableMaterialSpecV3(input, options = {}) {
87
+ const owned = new Map();
88
+ try {
89
+ const admission = admitPortableMaterialSpecV3(input), spec = admission.spec;
90
+ const resolver = options.resolver ?? DEFAULT_TEXTURE_RESOLVER;
91
+ const entries = Object.entries(spec.textures);
92
+ // All slots are checked before even bounded encoded-byte preparation.
93
+ for (const [slot, ref] of entries)
94
+ if (ref.kind === 'resource') {
95
+ const descriptor = resolver.describeApprovedTexture?.(ref.resourceId);
96
+ if (!descriptor?.allowedSlots.includes(slot) ||
97
+ descriptor.usage !== PORTABLE_MATERIAL_SLOT_USAGE[slot])
98
+ throw new ProceduralTextureError(`${slot} requires an injected resource approved for its exact slot and usage.`);
99
+ if (!resolver.preparePortableTexture)
100
+ throw new ProceduralTextureError('V3 needs a verified-byte preparation resolver; keep V2 for a legacy resolver.');
101
+ }
102
+ const prepared = new Map();
103
+ let encoded = 0, texels = admission.texels, resourcePixels = 0;
104
+ for (const [slot, ref] of entries)
105
+ if (ref.kind === 'resource') {
106
+ options.signal?.throwIfAborted();
107
+ const raw = await resolver.preparePortableTexture(ref.resourceId, {
108
+ signal: options.signal,
109
+ });
110
+ const d = structuredClone(raw.descriptor);
111
+ if (!(raw.bytes instanceof Uint8Array) ||
112
+ raw.bytes.length < 24 ||
113
+ raw.bytes.length > 8 * 1024 * 1024 ||
114
+ d.encodedByteLength !== raw.bytes.length ||
115
+ d.resourceId !== ref.resourceId ||
116
+ d.usage !== PORTABLE_MATERIAL_SLOT_USAGE[slot] ||
117
+ !d.allowedSlots.includes(slot) ||
118
+ d.colorSpace !== (slot === 'baseColor' || slot === 'emissive' ? 'srgb' : 'linear') ||
119
+ !Number.isInteger(d.width) ||
120
+ !Number.isInteger(d.height) ||
121
+ d.width < 1 ||
122
+ d.height < 1 ||
123
+ d.width > 4096 ||
124
+ d.height > 4096 ||
125
+ !/^sha256:[a-f0-9]{64}$/.test(d.imageSha256))
126
+ throw new ProceduralTextureError('Prepared texture descriptor/bytes are invalid.');
127
+ const pixels = d.width * d.height;
128
+ texels += pixels;
129
+ resourcePixels += pixels;
130
+ encoded += raw.bytes.length;
131
+ // Decoded RGBA write/copy plus encoded snapshot/hash/decode passes are
132
+ // charged beside the sole procedural admission owner's work estimate.
133
+ const resourceWork = resourcePixels * 12 + encoded * 8;
134
+ if (admission.estimatedWorkUnits + resourceWork > PORTABLE_MATERIAL_V3_LIMITS.maxWork)
135
+ throw new ProceduralTextureError('Aggregate material work budget exceeded before compilation or decoding.');
136
+ if (texels > PORTABLE_MATERIAL_V3_LIMITS.maxTexels ||
137
+ encoded > PORTABLE_MATERIAL_V3_LIMITS.maxEncodedBytes)
138
+ throw new ProceduralTextureError('Aggregate resource texel/encoded-byte budget exceeded before decoding.');
139
+ const snapshot = {
140
+ descriptor: structuredClone(d),
141
+ bytes: raw.bytes.slice(),
142
+ userData: structuredClone(raw.userData),
143
+ };
144
+ const header = new DataView(snapshot.bytes.buffer, snapshot.bytes.byteOffset, snapshot.bytes.byteLength);
145
+ if (![137, 80, 78, 71, 13, 10, 26, 10].every((n, i) => snapshot.bytes[i] === n) ||
146
+ String.fromCharCode(...snapshot.bytes.subarray(12, 16)) !== 'IHDR' ||
147
+ header.getUint32(16) !== d.width ||
148
+ header.getUint32(20) !== d.height)
149
+ throw new ProceduralTextureError('Prepared texture PNG/header dimensions mismatch.');
150
+ const hash = new Uint8Array(await crypto.subtle.digest('SHA-256', snapshot.bytes));
151
+ if (`sha256:${Array.from(hash, (x) => x.toString(16).padStart(2, '0')).join('')}` !==
152
+ d.imageSha256)
153
+ throw new ProceduralTextureError('Prepared texture encoded hash mismatch.');
154
+ prepared.set(slot, snapshot);
155
+ }
156
+ // Includes held outputs, decoder/copy allowances, encoded snapshots and generated mips.
157
+ const transient = admission.conservativePixelAllocationBytes +
158
+ resourcePixels * 12 +
159
+ encoded * 3 +
160
+ Math.ceil((texels * 4) / 3);
161
+ if (transient > PORTABLE_MATERIAL_V3_LIMITS.maxTransientBytes)
162
+ throw new ProceduralTextureError('Aggregate material allocation budget exceeded before decoding.');
163
+ for (const [slot, ref] of entries) {
164
+ options.signal?.throwIfAborted();
165
+ let texture;
166
+ if (ref.kind === 'procedural')
167
+ texture = proceduralTexture(ref.spec);
168
+ else {
169
+ const source = prepared.get(slot);
170
+ texture = await loadTexture(source.bytes.slice(), {
171
+ usage: source.descriptor.usage,
172
+ name: ref.resourceId,
173
+ });
174
+ owned.set(slot, texture);
175
+ if (texture.image.width !== source.descriptor.width ||
176
+ texture.image.height !== source.descriptor.height ||
177
+ !texture.image.data ||
178
+ texture.image.data.length !== source.descriptor.width * source.descriptor.height * 4)
179
+ throw new ProceduralTextureError('Prepared texture decoded dimensions mismatch.');
180
+ const actual = texture.userData['kilnTexture'];
181
+ texture.userData = { ...structuredClone(source.userData), ...texture.userData };
182
+ texture.userData['kilnTexture'] = {
183
+ ...source.userData['kilnTexture'],
184
+ ...actual,
185
+ };
186
+ }
187
+ owned.set(slot, texture);
188
+ texture.flipY = false;
189
+ texture.premultiplyAlpha = false;
190
+ applyPortableSamplerV1(texture, ref.sampler);
191
+ texture.userData['kilnPortableBinding'] = {
192
+ schemaVersion: 3,
193
+ slot,
194
+ sampler: { ...ref.sampler },
195
+ };
196
+ }
197
+ options.signal?.throwIfAborted();
198
+ const material = pbrMaterial({
199
+ roughness: spec.roughness,
200
+ metalness: spec.metalness,
201
+ emissiveIntensity: spec.emissiveIntensity,
202
+ alphaMode: spec.alphaMode,
203
+ ...(spec.alphaMode === 'mask' ? { alphaCutoff: spec.alphaCutoff } : {}),
204
+ doubleSided: spec.doubleSided,
205
+ ...(owned.has('baseColor')
206
+ ? { albedo: owned.get('baseColor') }
207
+ : spec.baseColor !== undefined
208
+ ? { albedo: spec.baseColor }
209
+ : {}),
210
+ ...(owned.has('normal') ? { normal: owned.get('normal') } : {}),
211
+ ...(owned.has('metallicRoughness')
212
+ ? { metallicRoughness: owned.get('metallicRoughness') }
213
+ : {}),
214
+ ...(owned.has('emissive')
215
+ ? { emissive: owned.get('emissive') }
216
+ : spec.emissive !== undefined
217
+ ? { emissive: spec.emissive }
218
+ : {}),
219
+ ...(owned.has('occlusion') ? { aoMap: owned.get('occlusion') } : {}),
220
+ });
221
+ if (spec.name)
222
+ material.name = spec.name;
223
+ if (owned.has('baseColor') && spec.baseColor !== undefined)
224
+ material.color.setHex(spec.baseColor);
225
+ if (owned.has('emissive') && spec.emissive !== undefined)
226
+ material.emissive.setHex(spec.emissive);
227
+ material.userData['kilnPortableMaterial'] = spec;
228
+ return material;
229
+ }
230
+ catch (error) {
231
+ for (const texture of owned.values())
232
+ texture.dispose();
233
+ if (error instanceof PortableMaterialV3Error)
234
+ throw error;
235
+ const wrapped = new PortableMaterialV3Error(error instanceof ProceduralTextureError
236
+ ? error.message
237
+ : 'Texture preparation or compilation failed; verify injected bytes, metadata and cancellation state.');
238
+ wrapped.cause = error;
239
+ throw wrapped;
240
+ }
241
+ }
@@ -0,0 +1,4 @@
1
+ /** Apply only after the caller has established independent texture ownership. */
2
+ import { type Texture } from 'three';
3
+ import { type CanonicalPortableSamplerV1 } from "./portable-sampler.js";
4
+ export declare function applyPortableSamplerV1<T extends Texture>(ownedTexture: T, sampler: CanonicalPortableSamplerV1): T;
@@ -0,0 +1,26 @@
1
+ /** Apply only after the caller has established independent texture ownership. */
2
+ import { ClampToEdgeWrapping, LinearFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, MirroredRepeatWrapping, NearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, RepeatWrapping, } from 'three';
3
+ import { canonicalizePortableSamplerV1 } from "./portable-sampler.js";
4
+ const min = {
5
+ nearest: NearestFilter,
6
+ linear: LinearFilter,
7
+ 'nearest-mipmap-nearest': NearestMipmapNearestFilter,
8
+ 'linear-mipmap-nearest': LinearMipmapNearestFilter,
9
+ 'nearest-mipmap-linear': NearestMipmapLinearFilter,
10
+ 'linear-mipmap-linear': LinearMipmapLinearFilter,
11
+ };
12
+ const wrap = {
13
+ repeat: RepeatWrapping,
14
+ 'clamp-to-edge': ClampToEdgeWrapping,
15
+ 'mirrored-repeat': MirroredRepeatWrapping,
16
+ };
17
+ export function applyPortableSamplerV1(ownedTexture, sampler) {
18
+ const value = canonicalizePortableSamplerV1(sampler);
19
+ ownedTexture.minFilter = min[value.minFilter];
20
+ ownedTexture.magFilter = value.magFilter === 'nearest' ? NearestFilter : LinearFilter;
21
+ ownedTexture.wrapS = wrap[value.wrapS];
22
+ ownedTexture.wrapT = wrap[value.wrapT];
23
+ ownedTexture.generateMipmaps = value.minFilter.includes('mipmap');
24
+ ownedTexture.needsUpdate = true;
25
+ return ownedTexture;
26
+ }
@@ -0,0 +1,11 @@
1
+ export declare const PORTABLE_MIN_FILTERS_V1: readonly ['nearest', 'linear', 'nearest-mipmap-nearest', 'linear-mipmap-nearest', 'nearest-mipmap-linear', 'linear-mipmap-linear'];
2
+ export declare const PORTABLE_MAG_FILTERS_V1: readonly ['nearest', 'linear'];
3
+ export declare const PORTABLE_WRAPS_V1: readonly ['repeat', 'clamp-to-edge', 'mirrored-repeat'];
4
+ export interface PortableSamplerV1 {
5
+ minFilter: (typeof PORTABLE_MIN_FILTERS_V1)[number];
6
+ magFilter: (typeof PORTABLE_MAG_FILTERS_V1)[number];
7
+ wrapS: (typeof PORTABLE_WRAPS_V1)[number];
8
+ wrapT: (typeof PORTABLE_WRAPS_V1)[number];
9
+ }
10
+ export type CanonicalPortableSamplerV1 = PortableSamplerV1;
11
+ export declare function canonicalizePortableSamplerV1(input: unknown): CanonicalPortableSamplerV1;
@@ -0,0 +1,42 @@
1
+ /** Shared portable sampler contract; no renderer, loader or native dependencies. */
2
+ import { ProceduralTextureError } from "./procedural-material-v2.js";
3
+ export const PORTABLE_MIN_FILTERS_V1 = [
4
+ 'nearest',
5
+ 'linear',
6
+ 'nearest-mipmap-nearest',
7
+ 'linear-mipmap-nearest',
8
+ 'nearest-mipmap-linear',
9
+ 'linear-mipmap-linear',
10
+ ];
11
+ export const PORTABLE_MAG_FILTERS_V1 = ['nearest', 'linear'];
12
+ export const PORTABLE_WRAPS_V1 = ['repeat', 'clamp-to-edge', 'mirrored-repeat'];
13
+ export function canonicalizePortableSamplerV1(input) {
14
+ if (!input || typeof input !== 'object' || Array.isArray(input))
15
+ throw new ProceduralTextureError('portableSampler requires a complete JSON object.');
16
+ const prototype = Object.getPrototypeOf(input);
17
+ if (prototype !== Object.prototype && prototype !== null)
18
+ throw new ProceduralTextureError('portableSampler must be plain JSON data.');
19
+ for (const key of Reflect.ownKeys(input)) {
20
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
21
+ if (typeof key !== 'string' || !descriptor.enumerable || !('value' in descriptor))
22
+ throw new ProceduralTextureError('portableSampler requires enumerable JSON data fields, never accessors.');
23
+ }
24
+ const value = input;
25
+ const allowed = {
26
+ minFilter: PORTABLE_MIN_FILTERS_V1,
27
+ magFilter: PORTABLE_MAG_FILTERS_V1,
28
+ wrapS: PORTABLE_WRAPS_V1,
29
+ wrapT: PORTABLE_WRAPS_V1,
30
+ };
31
+ if (Object.keys(value).some((key) => !Object.hasOwn(allowed, key)))
32
+ throw new ProceduralTextureError('portableSampler contains an unsupported field.');
33
+ for (const [key, options] of Object.entries(allowed))
34
+ if (!options.includes(value[key]))
35
+ throw new ProceduralTextureError(`portableSampler.${key} requires one of ${options.join(', ')}.`);
36
+ return {
37
+ minFilter: value['minFilter'],
38
+ magFilter: value['magFilter'],
39
+ wrapS: value['wrapS'],
40
+ wrapT: value['wrapT'],
41
+ };
42
+ }
@@ -25,15 +25,27 @@ export { projectUV } from "./uv-project.js";
25
25
  export type { ProjectUVOptions } from "./uv-project.js";
26
26
  export { remapUV } from "./uv-shapes.js";
27
27
  export type { RemapUVOptions } from "./uv-shapes.js";
28
+ export { textureAtlas, remapUVToAtlas } from "./texture-atlas.js";
29
+ export type { AtlasUVOptionsV1 } from "./texture-atlas.js";
30
+ export { TextureAtlasError, TEXTURE_ATLAS_COMPILER_ID, TEXTURE_ATLAS_LIMITS, canonicalizeTextureAtlasSpecV1, canonicalTextureAtlasJsonV1, hashTextureAtlasSpecV1, } from "./texture-atlas-contract.js";
31
+ export type { AtlasRectangleV1, AtlasEdgeModeV1, AtlasResourceExpectationV1, TextureAtlasSourceV1, TextureAtlasRegionV1, TextureAtlasSpecV1, CanonicalTextureAtlasSourceV1, CanonicalTextureAtlasRegionV1, CanonicalTextureAtlasSpecV1, } from "./texture-atlas-contract.js";
32
+ export { compileTextureAtlasSpecV1 } from "./texture-atlas-pixels.js";
33
+ export type { TextureAtlasCompileOptionsV1, TextureAtlasBudgetV1, TextureAtlasSourceManifestV1, TextureAtlasRegionManifestV1, TextureAtlasManifestV1, CompiledTextureAtlasV1, } from "./texture-atlas-pixels.js";
28
34
  export { createGableEndPanel, createGableRoof, createGableShell, createRoofSurfaceLayout, } from "./architecture.js";
29
35
  export type { GableEndPanelOptions, GableRoofOptions, GableShellOptions, RoofFaceFrame, RoofSurfaceLayoutOptions, } from "./architecture.js";
30
- export { createJointChain } from "./character.js";
31
- export type { CreateJointChainOptionsV1, JointChainResultV1, JointChainSegmentV1, } from "./character.js";
36
+ export { createBoneChain, createJointChain, createSkinnedPart } from "./character.js";
37
+ export type { BoneChainResultV1, CreateSkinnedPartOptions, CreateJointChainOptionsV1, JointChainResultV1, JointChainSegmentV1, } from "./character.js";
32
38
  export { createVehicleFrame, createWheelAssembly, createWheelGeometrySet, } from "./vehicle.js";
33
39
  export { materialRecipe } from "./material-recipe-runtime.js";
34
40
  export type { ApplyMaterialRecipeOptions } from "./material-recipe-runtime.js";
35
41
  export type { MaterialRecipeId, MaterialRecipeOverridesV1 } from "./material-recipes.js";
36
- export { compilePortableMaterialSpecV2 } from "./portable-material-runtime.js";
42
+ export { compilePortableMaterialSpecV2, compilePortableMaterialSpecV3, } from "./portable-material-runtime.js";
43
+ export { canonicalizePortableMaterialSpecV3, PortableMaterialV3Error, PORTABLE_MATERIAL_V3_LIMITS, } from "./procedural-material-v3.js";
44
+ export type { PortableMaterialSpecV3, CanonicalPortableMaterialSpecV3, PortableTextureRefV3, CanonicalPortableTextureRefV3, } from "./procedural-material-v3.js";
45
+ export { canonicalizePortableSamplerV1 } from "./portable-sampler.js";
46
+ export type { PortableSamplerV1, CanonicalPortableSamplerV1 } from "./portable-sampler.js";
47
+ export { compileProceduralTextureSpecV3, canonicalizeProceduralTextureSpecV3, canonicalProceduralTextureJsonV3, hashProceduralTextureSpecV3, PROCEDURAL_TEXTURE_V3_COMPILER_ID, PROCEDURAL_TEXTURE_SPEC_VERSION_V3, MAX_PROCEDURAL_V3_EDGE, MAX_PROCEDURAL_V3_TEXELS, MAX_PROCEDURAL_V3_OPERATIONS, MAX_PROCEDURAL_V3_VERTICES, MAX_PROCEDURAL_V3_STAMPS, MAX_PROCEDURAL_V3_PALETTE, MAX_PROCEDURAL_V3_WORK, MAX_PROCEDURAL_V3_TRANSIENT_BYTES, } from "./procedural-texture-v3.js";
48
+ export type { CompiledProceduralTextureV3, RgbaV3, PixelPointV3, PixelRectangleV3, ProceduralShapeV3, ProceduralOperationV3, CanonicalProceduralOperationV3, ProceduralTextureSpecV3, CanonicalProceduralTextureSpecV3, } from "./procedural-texture-v3.js";
37
49
  export { MAX_PORTABLE_MATERIAL_TEXELS, MAX_PORTABLE_MATERIAL_TEXTURES, MAX_PROCEDURAL_LAYERS, MAX_PROCEDURAL_SIZE, PORTABLE_MATERIAL_SPEC_VERSION, PROCEDURAL_TEXTURE_SPEC_VERSION, ProceduralTextureError, canonicalProceduralTextureJsonV2, canonicalizePortableMaterialSpecV2, canonicalizeProceduralTextureSpecV2, compileProceduralTextureSpecV2, hashProceduralTextureSpecV2, migrateProceduralTextureSpecV1, } from "./procedural-texture.js";
38
50
  export type { CanonicalPortableMaterialSpecV2, CanonicalProceduralTextureSpecV2, PortableMaterialSpecV2, ProceduralLayer, ProceduralTextureSpec, ProceduralTextureSpecV1, ProceduralTextureSpecV2, } from "./procedural-texture.js";
39
51
  export type { VehicleFrameOptions, VehicleFrameResult, WheelAssemblyOptions, WheelAssemblyResult, WheelGeometrySet, WheelMaterialSet, } from "./vehicle.js";