@instruktlabs/kiln 1.2.2 → 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 (214) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +74 -0
  4. package/README.md +7 -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/material-library.md +9 -5
  18. package/docs/procedural-surfaces-v3.md +276 -0
  19. package/docs/projects-and-live-review.md +1 -1
  20. package/docs/rendering.md +41 -0
  21. package/docs/sdk.md +53 -0
  22. package/docs/tools-operations.md +2 -2
  23. package/docs/tools.md +3 -3
  24. package/lib/asset-materials-node.d.ts +3 -3
  25. package/lib/asset-materials-node.js +29 -33
  26. package/lib/assets-node.d.ts +1 -1
  27. package/lib/assets-node.js +11 -4
  28. package/lib/assets.d.ts +4 -2
  29. package/lib/assets.js +61 -16
  30. package/lib/atlas-provenance.d.ts +86 -0
  31. package/lib/atlas-provenance.js +448 -0
  32. package/lib/character.d.ts +11 -2
  33. package/lib/character.js +90 -2
  34. package/lib/community-exporter.d.ts +3 -2
  35. package/lib/community-exporter.js +139 -3
  36. package/lib/discovery/helper-contracts.js +161 -7
  37. package/lib/discovery/helper-specs.js +49 -4
  38. package/lib/discovery/shapes.js +20 -7
  39. package/lib/engine-identity.d.ts +1 -1
  40. package/lib/engine-identity.js +1 -1
  41. package/lib/evaluator/authoring-diagnostic.d.ts +1 -1
  42. package/lib/evaluator/authoring-diagnostic.js +12 -0
  43. package/lib/evaluator/protocol.d.ts +2 -2
  44. package/lib/evaluator/protocol.js +16 -6
  45. package/lib/evaluator/subprocess.js +2 -2
  46. package/lib/exporter-node.js +9 -1
  47. package/lib/geometry-budget.d.ts +7 -0
  48. package/lib/geometry-budget.js +8 -0
  49. package/lib/gltf-texture-binding.d.ts +6 -0
  50. package/lib/gltf-texture-binding.js +54 -0
  51. package/lib/material-authoring-node.d.ts +6 -0
  52. package/lib/material-authoring-node.js +49 -0
  53. package/lib/material-authoring-schema.d.ts +871 -0
  54. package/lib/material-authoring-schema.js +310 -0
  55. package/lib/material-export.d.ts +5 -0
  56. package/lib/material-export.js +21 -0
  57. package/lib/material-library-node.d.ts +34 -8
  58. package/lib/material-library-node.js +691 -11
  59. package/lib/material-library.d.ts +3704 -4
  60. package/lib/material-library.js +272 -0
  61. package/lib/mcp-core.d.ts +1 -1
  62. package/lib/pinned-material-map.d.ts +33 -0
  63. package/lib/pinned-material-map.js +1 -0
  64. package/lib/portable-material-runtime.d.ts +5 -0
  65. package/lib/portable-material-runtime.js +160 -0
  66. package/lib/portable-sampler-runtime.d.ts +4 -0
  67. package/lib/portable-sampler-runtime.js +26 -0
  68. package/lib/portable-sampler.d.ts +11 -0
  69. package/lib/portable-sampler.js +42 -0
  70. package/lib/primitives.d.ts +15 -3
  71. package/lib/primitives.js +24 -4
  72. package/lib/procedural-material-v2.d.ts +2 -0
  73. package/lib/procedural-material-v2.js +2 -1
  74. package/lib/procedural-material-v3.d.ts +72 -0
  75. package/lib/procedural-material-v3.js +162 -0
  76. package/lib/procedural-texture-v2-compiler.d.ts +31 -0
  77. package/lib/procedural-texture-v2-compiler.js +172 -0
  78. package/lib/procedural-texture-v3-contract.d.ts +125 -0
  79. package/lib/procedural-texture-v3-contract.js +333 -0
  80. package/lib/procedural-texture-v3.d.ts +12 -0
  81. package/lib/procedural-texture-v3.js +165 -0
  82. package/lib/procedural-texture.d.ts +9 -31
  83. package/lib/procedural-texture.js +43 -170
  84. package/lib/project-bundle-node.d.ts +1 -1
  85. package/lib/project-bundle-node.js +70 -13
  86. package/lib/project-bundle.d.ts +91 -3
  87. package/lib/project-bundle.js +48 -7
  88. package/lib/qa/atlas.d.ts +21 -0
  89. package/lib/qa/atlas.js +474 -0
  90. package/lib/qa/index.d.ts +1 -0
  91. package/lib/qa/index.js +1 -0
  92. package/lib/qa/material.js +2 -0
  93. package/lib/qa/skin-integrity.d.ts +18 -0
  94. package/lib/qa/skin-integrity.js +221 -0
  95. package/lib/render-service-host.js +4 -7
  96. package/lib/render.d.ts +3 -3
  97. package/lib/render.js +108 -16
  98. package/lib/skin-binding.d.ts +20 -0
  99. package/lib/skin-binding.js +144 -0
  100. package/lib/texture-atlas-contract.d.ts +97 -0
  101. package/lib/texture-atlas-contract.js +414 -0
  102. package/lib/texture-atlas-pixels.d.ts +73 -0
  103. package/lib/texture-atlas-pixels.js +290 -0
  104. package/lib/texture-atlas.d.ts +10 -0
  105. package/lib/texture-atlas.js +143 -0
  106. package/lib/texture-bake.d.ts +3 -0
  107. package/lib/texture-bake.js +66 -11
  108. package/lib/texture-resolver.d.ts +22 -0
  109. package/lib/texture-resolver.js +24 -0
  110. package/lib/tools/materials.js +8 -7
  111. package/lib/tools/registry.js +5 -16
  112. package/lib/tools/review.js +1 -1
  113. package/lib/uv-data.d.ts +32 -0
  114. package/lib/uv-data.js +172 -0
  115. package/lib/uv-shapes.js +3 -0
  116. package/lib/uv.d.ts +9 -3
  117. package/lib/uv.js +53 -82
  118. package/lib/views/camera.d.ts +3 -3
  119. package/lib/views/camera.js +32 -28
  120. package/lib/views/face-policy.d.ts +9 -0
  121. package/lib/views/face-policy.js +16 -0
  122. package/lib/views/glb.js +218 -24
  123. package/lib/views/pose.d.ts +7 -1
  124. package/lib/views/pose.js +63 -12
  125. package/lib/views/raster.d.ts +6 -2
  126. package/lib/views/raster.js +49 -37
  127. package/lib/views/scene-copy.d.ts +6 -0
  128. package/lib/views/scene-copy.js +73 -0
  129. package/lib/views/scene-raster.js +2 -1
  130. package/lib/views/triangle-coverage.d.ts +8 -0
  131. package/lib/views/triangle-coverage.js +58 -0
  132. package/lib/workspace-node.js +7 -3
  133. package/lib/workspace.d.ts +2 -2
  134. package/package.json +3 -1
  135. package/plugin.json +1 -1
  136. package/plugins/kiln-engine/.claude-plugin/plugin.json +1 -1
  137. package/plugins/kiln-engine/README.md +3 -3
  138. package/plugins/kiln-engine/package-provenance.json +6 -6
  139. package/plugins/kiln-engine/plugin.json +1 -1
  140. package/plugins/kiln-engine/runtime.json +1 -1
  141. package/skills/kiln-author-asset/references/geometry-recipes.md +124 -0
  142. package/skills/kiln-author-asset/references/projects-and-materials.md +32 -2
  143. package/skills/kiln-compose-scene/references/runtime-scenes.md +19 -0
  144. package/skills/kiln-qa-asset/references/integration-checks.md +37 -2
  145. package/src/asset-materials-node.ts +38 -45
  146. package/src/assets-node.ts +22 -5
  147. package/src/assets.ts +61 -20
  148. package/src/atlas-provenance.ts +522 -0
  149. package/src/character.ts +107 -2
  150. package/src/community-exporter.ts +143 -3
  151. package/src/discovery/helper-contracts.ts +188 -7
  152. package/src/discovery/helper-specs.ts +69 -4
  153. package/src/discovery/shapes.ts +25 -9
  154. package/src/engine-identity.ts +1 -1
  155. package/src/evaluator/authoring-diagnostic.ts +19 -1
  156. package/src/evaluator/protocol.ts +19 -9
  157. package/src/evaluator/subprocess.ts +2 -2
  158. package/src/exporter-node.ts +12 -1
  159. package/src/generated/mcp-manifest.json +3 -3
  160. package/src/geometry-budget.ts +8 -0
  161. package/src/gltf-texture-binding.ts +60 -0
  162. package/src/material-authoring-node.ts +68 -0
  163. package/src/material-authoring-schema.ts +343 -0
  164. package/src/material-export.ts +25 -0
  165. package/src/material-library-node.ts +840 -16
  166. package/src/material-library.ts +348 -5
  167. package/src/pinned-material-map.ts +43 -0
  168. package/src/portable-material-runtime.ts +200 -0
  169. package/src/portable-sampler-runtime.ts +40 -0
  170. package/src/portable-sampler.ts +54 -0
  171. package/src/primitives.ts +98 -4
  172. package/src/procedural-material-v2.ts +2 -1
  173. package/src/procedural-material-v3.ts +224 -0
  174. package/src/procedural-texture-v2-compiler.ts +234 -0
  175. package/src/procedural-texture-v3-contract.ts +437 -0
  176. package/src/procedural-texture-v3.ts +220 -0
  177. package/src/procedural-texture.ts +61 -229
  178. package/src/project-bundle-node.ts +106 -14
  179. package/src/project-bundle.ts +61 -18
  180. package/src/project-cli.ts +29 -17
  181. package/src/qa/atlas.ts +597 -0
  182. package/src/qa/index.ts +1 -0
  183. package/src/qa/material.ts +2 -0
  184. package/src/qa/skin-integrity.ts +291 -0
  185. package/src/render-service-host.ts +4 -6
  186. package/src/render.ts +133 -15
  187. package/src/skin-binding.ts +225 -0
  188. package/src/texture-atlas-contract.ts +558 -0
  189. package/src/texture-atlas-pixels.ts +456 -0
  190. package/src/texture-atlas.ts +185 -0
  191. package/src/texture-bake.ts +88 -10
  192. package/src/texture-resolver.ts +55 -0
  193. package/src/tools/materials.ts +15 -11
  194. package/src/tools/registry.ts +5 -15
  195. package/src/tools/review.ts +1 -1
  196. package/src/uv-data.ts +238 -0
  197. package/src/uv-shapes.ts +3 -0
  198. package/src/uv.ts +73 -128
  199. package/src/viewer/app.ts +37 -0
  200. package/src/viewer/live-panel.ts +57 -1
  201. package/src/viewer/material-preview-panel.ts +140 -0
  202. package/src/viewer/material-preview.ts +138 -0
  203. package/src/viewer/materials-panel.ts +34 -5
  204. package/src/viewer/navigation.ts +2 -0
  205. package/src/views/camera.ts +37 -40
  206. package/src/views/face-policy.ts +20 -0
  207. package/src/views/glb.ts +294 -17
  208. package/src/views/pose.ts +71 -13
  209. package/src/views/raster.ts +73 -46
  210. package/src/views/scene-copy.ts +86 -0
  211. package/src/views/scene-raster.ts +2 -1
  212. package/src/views/triangle-coverage.ts +78 -0
  213. package/src/workspace-node.ts +11 -3
  214. package/src/workspace.ts +2 -2
package/lib/primitives.js CHANGED
@@ -12,7 +12,7 @@ import * as THREE from 'three';
12
12
  import { AuthoringDiagnosticError } from "./evaluator/authoring-diagnostic.js";
13
13
  import { assertDimension, assertPrimitiveSegments, assertPrimitiveGrid, assertFiniteTriple, GEOMETRY_ALLOCATION_LIMITS, } from "./geometry-budget.js";
14
14
  import { buildWallPanels } from "./wall-panels.js";
15
- import { createJointChain } from "./character.js";
15
+ import { createBoneChain, createJointChain, createSkinnedPart } from "./character.js";
16
16
  import { describeAssembly, replicateAssembly } from "./assembly.js";
17
17
  import { defineLod } from "./lod.js";
18
18
  export { defineLod } from "./lod.js";
@@ -37,7 +37,8 @@ import * as profile from "./profile.js";
37
37
  import * as solids from "./solids.js";
38
38
  import * as proceduralTextures from "./procedural-texture.js";
39
39
  import * as textures from "./textures.js";
40
- import { compilePortableMaterialSpecV2 } from "./portable-material-runtime.js";
40
+ import { textureAtlas, remapUVToAtlas } from "./texture-atlas.js";
41
+ import { compilePortableMaterialSpecV2, compilePortableMaterialSpecV3, } from "./portable-material-runtime.js";
41
42
  import { createVehicleFrame, createWheelAssembly, createWheelGeometrySet } from "./vehicle.js";
42
43
  import { stampSemanticMetadataV1, } from "./contracts/index.js";
43
44
  import * as uv from "./uv.js";
@@ -45,11 +46,17 @@ import * as uvShapes from "./uv-shapes.js";
45
46
  import { projectUV } from "./uv-project.js";
46
47
  export { projectUV } from "./uv-project.js";
47
48
  export { remapUV } from "./uv-shapes.js";
49
+ export { textureAtlas, remapUVToAtlas } from "./texture-atlas.js";
50
+ export { TextureAtlasError, TEXTURE_ATLAS_COMPILER_ID, TEXTURE_ATLAS_LIMITS, canonicalizeTextureAtlasSpecV1, canonicalTextureAtlasJsonV1, hashTextureAtlasSpecV1, } from "./texture-atlas-contract.js";
51
+ export { compileTextureAtlasSpecV1 } from "./texture-atlas-pixels.js";
48
52
  export { createGableEndPanel, createGableRoof, createGableShell, createRoofSurfaceLayout, } from "./architecture.js";
49
- export { createJointChain } from "./character.js";
53
+ export { createBoneChain, createJointChain, createSkinnedPart } from "./character.js";
50
54
  export { createVehicleFrame, createWheelAssembly, createWheelGeometrySet, } from "./vehicle.js";
51
55
  export { materialRecipe } from "./material-recipe-runtime.js";
52
- export { compilePortableMaterialSpecV2 } from "./portable-material-runtime.js";
56
+ export { compilePortableMaterialSpecV2, compilePortableMaterialSpecV3, } from "./portable-material-runtime.js";
57
+ export { canonicalizePortableMaterialSpecV3, PortableMaterialV3Error, PORTABLE_MATERIAL_V3_LIMITS, } from "./procedural-material-v3.js";
58
+ export { canonicalizePortableSamplerV1 } from "./portable-sampler.js";
59
+ 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";
53
60
  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";
54
61
  function vectorToTuple(v) {
55
62
  return [v.x, v.y, v.z];
@@ -1429,6 +1436,8 @@ export function buildSandboxGlobals(usage, options = {}) {
1429
1436
  replicateAssembly: wrap('replicateAssembly', replicateAssembly),
1430
1437
  createPivot: wrap('createPivot', createPivot),
1431
1438
  createJointChain: wrap('createJointChain', createJointChain),
1439
+ createBoneChain: wrap('createBoneChain', createBoneChain),
1440
+ createSkinnedPart: wrap('createSkinnedPart', createSkinnedPart),
1432
1441
  createVehicleFrame: wrap('createVehicleFrame', createVehicleFrame),
1433
1442
  createWheelGeometrySet: wrap('createWheelGeometrySet', createWheelGeometrySet),
1434
1443
  createWheelAssembly: wrap('createWheelAssembly', createWheelAssembly),
@@ -1477,6 +1486,11 @@ export function buildSandboxGlobals(usage, options = {}) {
1477
1486
  return Promise.reject(new TypeError('compilePortableMaterialSpecV2 accepts exactly one material spec'));
1478
1487
  return compilePortableMaterialSpecV2(args[0], { resolver: options.textureResolver });
1479
1488
  }),
1489
+ compilePortableMaterialSpecV3: wrap('compilePortableMaterialSpecV3', (...args) => {
1490
+ if (args.length !== 1)
1491
+ return Promise.reject(new TypeError('compilePortableMaterialSpecV3 accepts exactly one material spec'));
1492
+ return compilePortableMaterialSpecV3(args[0], { resolver: options.textureResolver });
1493
+ }),
1480
1494
  basicMaterial: wrap('basicMaterial', basicMaterial),
1481
1495
  glassMaterial: wrap('glassMaterial', glassMaterial),
1482
1496
  lambertMaterial: wrap('lambertMaterial', lambertMaterial),
@@ -1529,6 +1543,7 @@ export function buildSandboxGlobals(usage, options = {}) {
1529
1543
  // Shape-aware unwraps (sync — preserve built-in directional UVs)
1530
1544
  projectUV: wrap('projectUV', projectUV),
1531
1545
  remapUV: wrap('remapUV', uvShapes.remapUV),
1546
+ remapUVToAtlas: wrap('remapUVToAtlas', remapUVToAtlas),
1532
1547
  // Parametric primitives
1533
1548
  gearGeo: wrapGeo('gearGeo', gears.gearGeo),
1534
1549
  bladeGeo: wrapGeo('bladeGeo', gears.bladeGeo),
@@ -1537,6 +1552,11 @@ export function buildSandboxGlobals(usage, options = {}) {
1537
1552
  loadApprovedTexture: wrap('loadApprovedTexture', loadApprovedTexture),
1538
1553
  // Procedural textures (sync — no I/O, so no await)
1539
1554
  proceduralTexture: wrap('proceduralTexture', proceduralTextures.proceduralTexture),
1555
+ textureAtlas: wrap('textureAtlas', (...args) => {
1556
+ if (args.length !== 1)
1557
+ return Promise.reject(new TypeError('textureAtlas accepts exactly one atlas spec'));
1558
+ return textureAtlas(args[0], { resources: options.textureResolver?.pinnedMaterialMaps });
1559
+ }),
1540
1560
  normalMapFromHeight: wrap('normalMapFromHeight', proceduralTextures.normalMapFromHeight),
1541
1561
  pbrMaterial: wrap('pbrMaterial', textures.pbrMaterial),
1542
1562
  foliageMaterial: wrap('foliageMaterial', textures.foliageMaterial),
@@ -184,6 +184,8 @@ export declare class ProceduralTextureError extends AuthoringDiagnosticError {
184
184
  export declare function migrateProceduralTextureSpecV1(input: unknown): ProceduralTextureSpecV2;
185
185
  export declare function canonicalizeProceduralTextureSpecV2(input: unknown): CanonicalProceduralTextureSpecV2;
186
186
  export declare function canonicalProceduralTextureJsonV2(input: unknown): string;
187
+ /** Browser-safe SHA-256 shared by versioned recipe contracts. */
188
+ export declare function sha256Hex(text: string): string;
187
189
  export declare function hashProceduralTextureSpecV2(input: unknown): `sha256:${string}`;
188
190
  export declare function canonicalizePortableMaterialSpecV2(input: unknown): CanonicalPortableMaterialSpecV2;
189
191
  export {};
@@ -294,7 +294,8 @@ const SHA256_K = new Uint32Array([
294
294
  0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
295
295
  ]);
296
296
  const rotateRight = (value, bits) => (value >>> bits) | (value << (32 - bits));
297
- function sha256Hex(text) {
297
+ /** Browser-safe SHA-256 shared by versioned recipe contracts. */
298
+ export function sha256Hex(text) {
298
299
  const source = new TextEncoder().encode(text);
299
300
  const bitLength = source.length * 8;
300
301
  const paddedLength = Math.ceil((source.length + 9) / 64) * 64;
@@ -0,0 +1,72 @@
1
+ /** Strict opt-in PBR material data. Pixel/byte loading is outside this module. */
2
+ import { ProceduralTextureError } from "./procedural-material-v2.js";
3
+ import { type CanonicalProceduralTextureSpecV3, type ProceduralTextureSpecV3 } from "./procedural-texture-v3-contract.js";
4
+ import { type CanonicalPortableSamplerV1, type PortableSamplerV1 } from "./portable-sampler.js";
5
+ import type { MaterialMapSlotV1 } from "./pinned-material-map.js";
6
+ export declare const PORTABLE_MATERIAL_V3_LIMITS: Readonly<{
7
+ maxBindings: 5;
8
+ maxTexels: number;
9
+ maxEncodedBytes: number;
10
+ maxWork: 64000000;
11
+ maxTransientBytes: number;
12
+ maxJsonBytes: number;
13
+ }>;
14
+ export declare const PORTABLE_MATERIAL_SLOT_USAGE: {
15
+ readonly baseColor: 'albedo';
16
+ readonly normal: 'normal';
17
+ readonly metallicRoughness: 'metallicRoughness';
18
+ readonly emissive: 'emissive';
19
+ readonly occlusion: 'occlusion';
20
+ };
21
+ export type PortableTextureRefV3 = {
22
+ kind: 'procedural';
23
+ spec: ProceduralTextureSpecV3;
24
+ sampler: PortableSamplerV1;
25
+ } | {
26
+ kind: 'resource';
27
+ resourceId: string;
28
+ sampler: PortableSamplerV1;
29
+ };
30
+ export type CanonicalPortableTextureRefV3 = {
31
+ kind: 'procedural';
32
+ spec: CanonicalProceduralTextureSpecV3;
33
+ sampler: CanonicalPortableSamplerV1;
34
+ } | {
35
+ kind: 'resource';
36
+ resourceId: string;
37
+ sampler: CanonicalPortableSamplerV1;
38
+ };
39
+ export interface PortableMaterialSpecV3 {
40
+ schemaVersion: 3;
41
+ model: 'pbrMetallicRoughness';
42
+ name?: string;
43
+ baseColor?: number;
44
+ roughness?: number;
45
+ metalness?: number;
46
+ emissive?: number;
47
+ emissiveIntensity?: number;
48
+ alphaMode?: 'opaque' | 'mask' | 'blend';
49
+ alphaCutoff?: number;
50
+ doubleSided?: boolean;
51
+ textures?: Partial<Record<MaterialMapSlotV1, PortableTextureRefV3>>;
52
+ }
53
+ export interface CanonicalPortableMaterialSpecV3 extends Omit<PortableMaterialSpecV3, 'textures' | 'roughness' | 'metalness' | 'emissiveIntensity' | 'alphaMode' | 'alphaCutoff' | 'doubleSided'> {
54
+ roughness: number;
55
+ metalness: number;
56
+ emissiveIntensity: number;
57
+ alphaMode: 'opaque' | 'mask' | 'blend';
58
+ alphaCutoff: number;
59
+ doubleSided: boolean;
60
+ textures: Partial<Record<MaterialMapSlotV1, CanonicalPortableTextureRefV3>>;
61
+ }
62
+ export declare class PortableMaterialV3Error extends ProceduralTextureError {
63
+ constructor(message: string);
64
+ }
65
+ export interface PortableMaterialAdmissionV3 {
66
+ spec: CanonicalPortableMaterialSpecV3;
67
+ texels: number;
68
+ estimatedWorkUnits: number;
69
+ conservativePixelAllocationBytes: number;
70
+ }
71
+ export declare function admitPortableMaterialSpecV3(input: unknown): PortableMaterialAdmissionV3;
72
+ export declare function canonicalizePortableMaterialSpecV3(input: unknown): CanonicalPortableMaterialSpecV3;
@@ -0,0 +1,162 @@
1
+ /** Strict opt-in PBR material data. Pixel/byte loading is outside this module. */
2
+ import { canonicalizePortableMaterialSpecV2, ProceduralTextureError, } from "./procedural-material-v2.js";
3
+ import { admitProceduralTextureSpecV3, } from "./procedural-texture-v3-contract.js";
4
+ import { canonicalizePortableSamplerV1, } from "./portable-sampler.js";
5
+ export const PORTABLE_MATERIAL_V3_LIMITS = Object.freeze({
6
+ maxBindings: 5,
7
+ maxTexels: 4 * 1024 * 1024,
8
+ maxEncodedBytes: 16 * 1024 * 1024,
9
+ maxWork: 64_000_000,
10
+ maxTransientBytes: 64 * 1024 * 1024,
11
+ maxJsonBytes: 1024 * 1024,
12
+ });
13
+ export const PORTABLE_MATERIAL_SLOT_USAGE = {
14
+ baseColor: 'albedo',
15
+ normal: 'normal',
16
+ metallicRoughness: 'metallicRoughness',
17
+ emissive: 'emissive',
18
+ occlusion: 'occlusion',
19
+ };
20
+ export class PortableMaterialV3Error extends ProceduralTextureError {
21
+ constructor(message) {
22
+ super(`portableMaterial V3: ${message}`, 'PORTABLE_MATERIAL_V3_CONTRACT');
23
+ this.name = 'PortableMaterialV3Error';
24
+ }
25
+ }
26
+ function fail(message) {
27
+ throw new PortableMaterialV3Error(message);
28
+ }
29
+ /** Bound untrusted data before reading any accessor or making canonical copies. */
30
+ function assertJson(input) {
31
+ let nodes = 0;
32
+ const visit = (value, depth) => {
33
+ if (++nodes > 65536 || depth > 16)
34
+ fail('JSON size/depth budget exceeded.');
35
+ if (value === null || typeof value === 'boolean')
36
+ return;
37
+ if (typeof value === 'string') {
38
+ if (value.length > PORTABLE_MATERIAL_V3_LIMITS.maxJsonBytes)
39
+ fail('string budget exceeded.');
40
+ return;
41
+ }
42
+ if (typeof value === 'number' && Number.isFinite(value))
43
+ return;
44
+ if (!value || typeof value !== 'object')
45
+ fail('only plain JSON data is supported.');
46
+ const array = Array.isArray(value), proto = Object.getPrototypeOf(value);
47
+ if (array ? proto !== Array.prototype : proto !== Object.prototype && proto !== null)
48
+ fail('only plain JSON prototypes are supported.');
49
+ const descriptors = Object.getOwnPropertyDescriptors(value), keys = Reflect.ownKeys(value);
50
+ if (keys.length > 1025)
51
+ fail('field/array budget exceeded.');
52
+ const length = array ? descriptors['length']?.value : undefined;
53
+ if (array && (length > 1024 || keys.length !== length + 1))
54
+ fail('arrays must be bounded dense JSON data.');
55
+ for (const key of keys) {
56
+ if (array && key === 'length')
57
+ continue;
58
+ if (typeof key !== 'string' || ['__proto__', 'prototype', 'constructor'].includes(key))
59
+ fail('forbidden JSON field.');
60
+ const d = descriptors[key];
61
+ if (!d.enumerable || !('value' in d))
62
+ fail('JSON accessors/hidden fields are unsupported.');
63
+ if (array && (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= length))
64
+ fail('invalid array key.');
65
+ visit(d.value, depth + 1);
66
+ }
67
+ };
68
+ visit(input, 0);
69
+ if (new TextEncoder().encode(JSON.stringify(input)).length >
70
+ PORTABLE_MATERIAL_V3_LIMITS.maxJsonBytes)
71
+ fail('JSON byte budget exceeded.');
72
+ }
73
+ function record(value, keys) {
74
+ if (!value || typeof value !== 'object' || Array.isArray(value))
75
+ fail('expected a JSON object.');
76
+ const result = value;
77
+ if (Object.keys(result).some((key) => !keys.includes(key)))
78
+ fail('unknown field in material or texture reference.');
79
+ return result;
80
+ }
81
+ function admitMaterial(input) {
82
+ assertJson(input);
83
+ const scalarKeys = [
84
+ 'baseColor',
85
+ 'roughness',
86
+ 'metalness',
87
+ 'emissive',
88
+ 'emissiveIntensity',
89
+ 'alphaMode',
90
+ 'alphaCutoff',
91
+ 'doubleSided',
92
+ ];
93
+ const r = record(input, ['schemaVersion', 'model', 'name', 'textures', ...scalarKeys]);
94
+ if (r['schemaVersion'] !== 3 || r['model'] !== 'pbrMetallicRoughness')
95
+ fail('requires schemaVersion 3 and model pbrMetallicRoughness.');
96
+ const name = r['name'];
97
+ if (name !== undefined && (typeof name !== 'string' || name.length < 1 || name.length > 200))
98
+ fail('name requires 1..200 characters.');
99
+ const common = canonicalizePortableMaterialSpecV2({
100
+ schemaVersion: 2,
101
+ model: 'pbrMetallicRoughness',
102
+ ...Object.fromEntries(scalarKeys.filter((k) => r[k] !== undefined).map((k) => [k, r[k]])),
103
+ });
104
+ const refs = record(r['textures'] ?? {}, Object.keys(PORTABLE_MATERIAL_SLOT_USAGE));
105
+ const textures = {};
106
+ let texels = 0, work = 0, bytes = 0;
107
+ for (const [key, value] of Object.entries(refs)) {
108
+ const slot = key;
109
+ const ref = record(value, ['kind', 'spec', 'resourceId', 'sampler']);
110
+ const sampler = canonicalizePortableSamplerV1(ref['sampler']);
111
+ if (ref['kind'] === 'procedural') {
112
+ if (ref['resourceId'] !== undefined)
113
+ fail('procedural references cannot carry a resource ID.');
114
+ const admitted = admitProceduralTextureSpecV3(ref['spec']);
115
+ if (admitted.spec.usage !== PORTABLE_MATERIAL_SLOT_USAGE[slot])
116
+ fail(`${slot} requires ${PORTABLE_MATERIAL_SLOT_USAGE[slot]} data.`);
117
+ texels += admitted.spec.width * admitted.spec.height;
118
+ work += admitted.estimatedWorkUnits;
119
+ bytes += admitted.conservativePixelAllocationBytes;
120
+ textures[slot] = { kind: 'procedural', spec: admitted.spec, sampler };
121
+ }
122
+ else if (ref['kind'] === 'resource') {
123
+ if (ref['spec'] !== undefined ||
124
+ typeof ref['resourceId'] !== 'string' ||
125
+ ref['resourceId'].length > 128 ||
126
+ !/^kiln\.[a-z0-9][a-z0-9._-]+$/.test(ref['resourceId']))
127
+ fail('resource requires only a bounded closed kiln.* ID and sampler.');
128
+ textures[slot] = { kind: 'resource', resourceId: ref['resourceId'], sampler };
129
+ }
130
+ else
131
+ fail('texture kind must be procedural or resource.');
132
+ }
133
+ if (texels > PORTABLE_MATERIAL_V3_LIMITS.maxTexels ||
134
+ work > PORTABLE_MATERIAL_V3_LIMITS.maxWork ||
135
+ bytes > PORTABLE_MATERIAL_V3_LIMITS.maxTransientBytes)
136
+ fail('aggregate texel/work/allocation budget exceeded.');
137
+ const { schemaVersion: _, textures: __, ...parameters } = common;
138
+ return {
139
+ spec: {
140
+ schemaVersion: 3,
141
+ ...parameters,
142
+ ...(name !== undefined ? { name: name } : {}),
143
+ textures,
144
+ },
145
+ texels,
146
+ estimatedWorkUnits: work,
147
+ conservativePixelAllocationBytes: bytes,
148
+ };
149
+ }
150
+ export function admitPortableMaterialSpecV3(input) {
151
+ try {
152
+ return admitMaterial(input);
153
+ }
154
+ catch (error) {
155
+ if (error instanceof PortableMaterialV3Error)
156
+ throw error;
157
+ throw new PortableMaterialV3Error(error instanceof ProceduralTextureError ? error.message : 'Invalid bounded material data.');
158
+ }
159
+ }
160
+ export function canonicalizePortableMaterialSpecV3(input) {
161
+ return admitPortableMaterialSpecV3(input).spec;
162
+ }
@@ -0,0 +1,31 @@
1
+ /** Legacy V1/V2 pixel compiler. Extraction preserves arithmetic and recipe identity. */
2
+ import { type CanonicalProceduralTextureSpecV2 } from "./procedural-material-v2.js";
3
+ /**
4
+ * Build a tiling texture from a bounded, deterministic layer stack.
5
+ *
6
+ * The result is a plain RGBA `DataTexture`; `renderSceneToGLB` bakes it to PNG
7
+ * and embeds it, so nothing else has to be done to ship it.
8
+ *
9
+ * @example
10
+ * const bark = proceduralTexture({
11
+ * schemaVersion: 2,
12
+ * size: 256,
13
+ * usage: 'albedo',
14
+ * name: 'Bark',
15
+ * layers: [
16
+ * { op: 'solid', color: 0x5a4632 },
17
+ * { op: 'noise', colorA: 0x3d2f21, colorB: 0x7a6248, scale: 6, octaves: 4, blend: 'overlay' },
18
+ * { op: 'stripes', colorA: 0x000000, colorB: 0xffffff, count: 24, angleDeg: 90,
19
+ * blend: 'multiply', opacity: 0.15 },
20
+ * ],
21
+ * });
22
+ * const trunk = createPart('Trunk', cylinderGeo(0.3, 0.3, 3), pbrMaterial({ albedo: bark }), { parent: root });
23
+ */
24
+ export interface CompiledProceduralTextureV2 {
25
+ spec: CanonicalProceduralTextureSpecV2;
26
+ pixels: Uint8Array;
27
+ canonicalJson: string;
28
+ recipeHash: `sha256:${string}`;
29
+ }
30
+ /** Validate, canonicalize, and compile one texture without constructing Three.js state. */
31
+ export declare function compileProceduralTextureSpecV2(input: unknown): CompiledProceduralTextureV2;
@@ -0,0 +1,172 @@
1
+ /** Legacy V1/V2 pixel compiler. Extraction preserves arithmetic and recipe identity. */
2
+ import { canonicalProceduralTextureJsonV2, canonicalizeProceduralTextureSpecV2, hashProceduralTextureSpecV2, } from "./procedural-material-v2.js";
3
+ // -----------------------------------------------------------------------------
4
+ // Deterministic value noise
5
+ // -----------------------------------------------------------------------------
6
+ /**
7
+ * Integer hash of a lattice coordinate. Not a cryptographic hash — it only has
8
+ * to decorrelate neighbouring integers and return the same value forever.
9
+ */
10
+ function hash2(x, y, seed) {
11
+ let h = (Math.imul(x, 374761393) + Math.imul(y, 668265263) + Math.imul(seed, 1274126177)) | 0;
12
+ h = h ^ (h >>> 13);
13
+ h = Math.imul(h, 1274126177) | 0;
14
+ h = h ^ (h >>> 16);
15
+ return (h >>> 0) / 4294967296;
16
+ }
17
+ const smoothstep = (t) => t * t * (3 - 2 * t);
18
+ /**
19
+ * Value noise on a lattice that wraps at `period`, so the result tiles.
20
+ * `period` is in lattice cells, and both axes use the same one.
21
+ */
22
+ function valueNoise(u, v, period, seed) {
23
+ const x = u * period;
24
+ const y = v * period;
25
+ const xi = Math.floor(x);
26
+ const yi = Math.floor(y);
27
+ const xf = x - xi;
28
+ const yf = y - yi;
29
+ // Wrapping the lattice index (not the sample point) is what makes it tile:
30
+ // the cell to the right of the last one is the first one again.
31
+ const wrap = (n) => ((n % period) + period) % period;
32
+ const x0 = wrap(xi);
33
+ const y0 = wrap(yi);
34
+ const x1 = wrap(xi + 1);
35
+ const y1 = wrap(yi + 1);
36
+ const sx = smoothstep(xf);
37
+ const sy = smoothstep(yf);
38
+ const n00 = hash2(x0, y0, seed);
39
+ const n10 = hash2(x1, y0, seed);
40
+ const n01 = hash2(x0, y1, seed);
41
+ const n11 = hash2(x1, y1, seed);
42
+ const top = n00 + sx * (n10 - n00);
43
+ const bottom = n01 + sx * (n11 - n01);
44
+ return top + sy * (bottom - top);
45
+ }
46
+ function fbm(u, v, period, octaves, seed) {
47
+ let sum = 0;
48
+ let amplitude = 1;
49
+ let total = 0;
50
+ let p = period;
51
+ for (let o = 0; o < octaves; o++) {
52
+ sum += valueNoise(u, v, p, seed + o * 101) * amplitude;
53
+ total += amplitude;
54
+ amplitude *= 0.5;
55
+ // Doubling the period keeps every octave tileable at the base period.
56
+ p *= 2;
57
+ }
58
+ return total > 0 ? sum / total : 0;
59
+ }
60
+ // -----------------------------------------------------------------------------
61
+ // Layer evaluation
62
+ // -----------------------------------------------------------------------------
63
+ const rgbOf = (color) => [
64
+ (color >> 16) & 0xff,
65
+ (color >> 8) & 0xff,
66
+ color & 0xff,
67
+ ];
68
+ const lerp = (a, b, t) => a + (b - a) * t;
69
+ /** Rotate normalized coords about the center so angled patterns stay centered. */
70
+ function project(u, v, angleDeg) {
71
+ const a = (angleDeg * Math.PI) / 180;
72
+ return (u - 0.5) * Math.cos(a) + (v - 0.5) * Math.sin(a) + 0.5;
73
+ }
74
+ /** Evaluate one layer at a normalized coordinate, returning 0-255 RGB. */
75
+ function evalLayer(layer, u, v) {
76
+ switch (layer.op) {
77
+ case 'solid':
78
+ return rgbOf(layer.color);
79
+ case 'checker': {
80
+ const cx = Math.floor(u * layer.squares);
81
+ const cy = Math.floor(v * layer.squares);
82
+ return rgbOf((cx + cy) % 2 === 0 ? layer.colorA : layer.colorB);
83
+ }
84
+ case 'stripes': {
85
+ const t = project(u, v, layer.angleDeg);
86
+ return rgbOf(Math.floor(t * layer.count) % 2 === 0 ? layer.colorA : layer.colorB);
87
+ }
88
+ case 'gradient': {
89
+ const t = Math.min(1, Math.max(0, project(u, v, layer.angleDeg)));
90
+ const from = rgbOf(layer.from);
91
+ const to = rgbOf(layer.to);
92
+ return [lerp(from[0], to[0], t), lerp(from[1], to[1], t), lerp(from[2], to[2], t)];
93
+ }
94
+ case 'bricks': {
95
+ const row = Math.floor(v * layer.rows);
96
+ // Offset alternate rows, wrapping into [0,1) so the pattern still tiles.
97
+ const shifted = (u + (row % 2 === 0 ? 0 : layer.stagger / layer.cols) + 1) % 1;
98
+ const inCol = (shifted * layer.cols) % 1;
99
+ const inRow = (v * layer.rows) % 1;
100
+ const isMortar = inCol < layer.mortarWidth ||
101
+ inCol > 1 - layer.mortarWidth ||
102
+ inRow < layer.mortarWidth ||
103
+ inRow > 1 - layer.mortarWidth;
104
+ return rgbOf(isMortar ? layer.mortar : layer.brick);
105
+ }
106
+ case 'noise': {
107
+ const n = fbm(u, v, layer.scale, layer.octaves, layer.seed);
108
+ const a = rgbOf(layer.colorA);
109
+ const b = rgbOf(layer.colorB);
110
+ return [lerp(a[0], b[0], n), lerp(a[1], b[1], n), lerp(a[2], b[2], n)];
111
+ }
112
+ }
113
+ }
114
+ function blendChannel(mode, base, top) {
115
+ const b = base / 255;
116
+ const t = top / 255;
117
+ let out;
118
+ switch (mode) {
119
+ case 'multiply':
120
+ out = b * t;
121
+ break;
122
+ case 'screen':
123
+ out = 1 - (1 - b) * (1 - t);
124
+ break;
125
+ case 'overlay':
126
+ out = b < 0.5 ? 2 * b * t : 1 - 2 * (1 - b) * (1 - t);
127
+ break;
128
+ default:
129
+ out = t;
130
+ }
131
+ return out * 255;
132
+ }
133
+ /** Validate, canonicalize, and compile one texture without constructing Three.js state. */
134
+ export function compileProceduralTextureSpecV2(input) {
135
+ const spec = canonicalizeProceduralTextureSpecV2(input);
136
+ const { size, layers } = spec;
137
+ const data = new Uint8Array(size * size * 4);
138
+ for (let y = 0; y < size; y++) {
139
+ // Sample at texel centers so a pattern with N divisions lands on exact
140
+ // boundaries instead of drifting half a texel.
141
+ const v = (y + 0.5) / size;
142
+ for (let x = 0; x < size; x++) {
143
+ const u = (x + 0.5) / size;
144
+ let r = 0;
145
+ let g = 0;
146
+ let b = 0;
147
+ for (const [i, layer] of layers.entries()) {
148
+ const [lr, lg, lb] = evalLayer(layer, u, v);
149
+ if (i === 0) {
150
+ r = lr;
151
+ g = lg;
152
+ b = lb;
153
+ continue;
154
+ }
155
+ r = lerp(r, blendChannel(layer.blend, r, lr), layer.opacity);
156
+ g = lerp(g, blendChannel(layer.blend, g, lg), layer.opacity);
157
+ b = lerp(b, blendChannel(layer.blend, b, lb), layer.opacity);
158
+ }
159
+ const o = (y * size + x) * 4;
160
+ data[o] = Math.max(0, Math.min(255, Math.round(r)));
161
+ data[o + 1] = Math.max(0, Math.min(255, Math.round(g)));
162
+ data[o + 2] = Math.max(0, Math.min(255, Math.round(b)));
163
+ data[o + 3] = 255;
164
+ }
165
+ }
166
+ return {
167
+ spec,
168
+ pixels: data,
169
+ canonicalJson: canonicalProceduralTextureJsonV2(spec),
170
+ recipeHash: hashProceduralTextureSpecV2(spec),
171
+ };
172
+ }
@@ -0,0 +1,125 @@
1
+ /** Bounded JSON and identity contract; no pixel allocations or Three.js dependency. */
2
+ import { type CanonicalProceduralTextureSpecV2, type ProceduralTextureSpecV2 } from "./procedural-material-v2.js";
3
+ import { type TextureUsage } from "./texture-contract.js";
4
+ export declare const PROCEDURAL_TEXTURE_V3_COMPILER_ID: 'kiln.procedural-rgba.v3.1';
5
+ export declare const PROCEDURAL_TEXTURE_SPEC_VERSION_V3: 3;
6
+ export declare const MAX_PROCEDURAL_V3_EDGE = 1024;
7
+ export declare const MAX_PROCEDURAL_V3_TEXELS = 1048576;
8
+ export declare const MAX_PROCEDURAL_V3_OPERATIONS = 64;
9
+ export declare const MAX_PROCEDURAL_V3_VERTICES = 64;
10
+ export declare const MAX_PROCEDURAL_V3_STAMPS = 256;
11
+ export declare const MAX_PROCEDURAL_V3_PALETTE = 32;
12
+ export declare const MAX_PROCEDURAL_V3_WORK = 64000000;
13
+ export declare const MAX_PROCEDURAL_V3_TRANSIENT_BYTES: number;
14
+ export type RgbaV3 = [number, number, number, number];
15
+ export type PixelPointV3 = [number, number];
16
+ export interface PixelRectangleV3 {
17
+ x: number;
18
+ y: number;
19
+ width: number;
20
+ height: number;
21
+ }
22
+ export type ProceduralShapeV3 = (PixelRectangleV3 & {
23
+ kind: 'rectangle';
24
+ }) | (PixelRectangleV3 & {
25
+ kind: 'ellipse';
26
+ }) | {
27
+ kind: 'polygon';
28
+ points: PixelPointV3[];
29
+ } | {
30
+ kind: 'line';
31
+ from: PixelPointV3;
32
+ to: PixelPointV3;
33
+ width: number;
34
+ };
35
+ export type ProceduralOperationV3 = {
36
+ op: 'paint';
37
+ shape: ProceduralShapeV3;
38
+ rgba: RgbaV3;
39
+ opacity?: number;
40
+ } | {
41
+ op: 'mask';
42
+ shape: ProceduralShapeV3;
43
+ alpha?: number;
44
+ invert?: boolean;
45
+ } | {
46
+ op: 'stamps';
47
+ shape: ProceduralShapeV3;
48
+ rgba: RgbaV3;
49
+ opacity?: number;
50
+ count: number;
51
+ seed: number;
52
+ area: PixelRectangleV3;
53
+ periodic?: boolean;
54
+ } | {
55
+ op: 'legacy-v2';
56
+ spec: ProceduralTextureSpecV2;
57
+ alpha?: number;
58
+ opacity?: number;
59
+ };
60
+ export interface ProceduralTextureSpecV3 {
61
+ schemaVersion: 3;
62
+ width: number;
63
+ height: number;
64
+ usage?: TextureUsage;
65
+ name?: string;
66
+ background?: RgbaV3;
67
+ operations: ProceduralOperationV3[];
68
+ palette?: {
69
+ colors: number[];
70
+ dither?: 'none' | 'bayer4';
71
+ };
72
+ }
73
+ export type CanonicalProceduralOperationV3 = {
74
+ op: 'paint';
75
+ shape: ProceduralShapeV3;
76
+ rgba: RgbaV3;
77
+ opacity: number;
78
+ } | {
79
+ op: 'mask';
80
+ shape: ProceduralShapeV3;
81
+ alpha: number;
82
+ invert: boolean;
83
+ } | {
84
+ op: 'stamps';
85
+ shape: ProceduralShapeV3;
86
+ rgba: RgbaV3;
87
+ opacity: number;
88
+ count: number;
89
+ seed: number;
90
+ area: PixelRectangleV3;
91
+ periodic: boolean;
92
+ } | {
93
+ op: 'legacy-v2';
94
+ spec: CanonicalProceduralTextureSpecV2;
95
+ alpha: number;
96
+ opacity: number;
97
+ };
98
+ export interface CanonicalProceduralTextureSpecV3 {
99
+ schemaVersion: 3;
100
+ width: number;
101
+ height: number;
102
+ usage: TextureUsage;
103
+ name?: string;
104
+ background: RgbaV3;
105
+ operations: CanonicalProceduralOperationV3[];
106
+ palette?: {
107
+ colors: number[];
108
+ dither: 'none' | 'bayer4';
109
+ };
110
+ }
111
+ export declare function proceduralShapeBoundsV3(s: ProceduralShapeV3): [number, number, number, number];
112
+ export declare function proceduralShapeCostV3(s: ProceduralShapeV3): number;
113
+ export declare function proceduralClippedBoundsV3(s: ProceduralShapeV3, width: number, height: number, dx?: number, dy?: number): [number, number, number, number];
114
+ /** Internal composition admission; byte accounting is total RGBA allocations, not live peak/RSS. */
115
+ export interface ProceduralTextureAdmissionV3 {
116
+ spec: CanonicalProceduralTextureSpecV3;
117
+ canonicalJson: string;
118
+ estimatedWorkUnits: number;
119
+ conservativePixelAllocationBytes: number;
120
+ }
121
+ /** Owns the single validation/accounting pass shared by standalone and atlas compilation. */
122
+ export declare function admitProceduralTextureSpecV3(input: unknown): ProceduralTextureAdmissionV3;
123
+ export declare function canonicalizeProceduralTextureSpecV3(input: unknown): CanonicalProceduralTextureSpecV3;
124
+ export declare function canonicalProceduralTextureJsonV3(input: unknown): string;
125
+ export declare function hashProceduralTextureSpecV3(input: unknown): `sha256:${string}`;