@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
@@ -3,7 +3,7 @@ import { type RequirementsContext } from "../requirements-context.js";
3
3
  import type { AssetQaReport } from "../qa/types.js";
4
4
  import type { RenderGlbOptions, RenderResult } from "../render.js";
5
5
  import type { KhronosGltfValidationReport } from "../qa/gltf.js";
6
- import type { MaterialLibraryPayloadV1 } from "../material-library.js";
6
+ import { type MaterialLibraryPayload } from "../material-library.js";
7
7
  export declare const EVALUATOR_REQUEST_VERSION: 'kiln.evaluator.request.v2';
8
8
  export declare const EVALUATOR_RESULT_VERSION: 'kiln.evaluator.result.v2';
9
9
  export declare const MAX_EVALUATOR_CODE_BYTES: number;
@@ -96,7 +96,7 @@ export declare function encodeRenderResultV2(requestId: string, render: RenderRe
96
96
  export declare function decodeEvaluatorResultV2(json: string, maxGlbBytes: number, expectedRequestId?: string): EvaluatorResultV2;
97
97
  export declare function createEvaluatorPortV2(transport: EvaluatorTransportV2, defaults?: EvaluatorPortCallControlsV2): EvaluatorPortV2;
98
98
  /** All transports bind decoded acceptance evidence to the same current-host snapshot. */
99
- export declare function assertEvaluatorResultMaterialResources(result: EvaluatorResultV2, expected?: MaterialLibraryPayloadV1): void;
99
+ export declare function assertEvaluatorResultMaterialResources(result: EvaluatorResultV2, expected?: MaterialLibraryPayload): void;
100
100
  /** All transports bind decoded acceptance evidence to the same current-host snapshot. */
101
101
  export declare function assertEvaluatorResultRequirements(result: EvaluatorResultV2, expected: RequirementsContext): void;
102
102
  /** Explicit trusted/test compatibility port. Production hosts must inject a
@@ -2,7 +2,8 @@ import { authoringDiagnosticAdvice, EXECUTION_REJECTED_ADVICE, } from "./authori
2
2
  import { createHash } from 'node:crypto';
3
3
  import { assertNoLegacyRuntimePolicy, validateRequirementsBinding, validateRequirementsContext, resolveRequirementsContext, requirementsContextsEqual, } from "../requirements-context.js";
4
4
  import { validateRequirementsQaReport } from "../qa/requirements-report.js";
5
- import { validateMaterialLibraryPayload, validateMaterialManifestIdentity, } from "../material-library-node.js";
5
+ import { validateMaterialLibraryPayloadAny, validateMaterialManifestIdentityAny, } from "../material-library-node.js";
6
+ import { canonicalMaterialJson } from "../material-library.js";
6
7
  import { rebuildOptionsSchema } from "../rebuild-options.js";
7
8
  export const EVALUATOR_REQUEST_VERSION = 'kiln.evaluator.request.v2';
8
9
  export const EVALUATOR_RESULT_VERSION = 'kiln.evaluator.result.v2';
@@ -134,7 +135,7 @@ function parseOptions(value) {
134
135
  }
135
136
  const options = {};
136
137
  if (value.materialResources !== undefined)
137
- options.materialResources = validateMaterialLibraryPayload(value.materialResources);
138
+ options.materialResources = validateMaterialLibraryPayloadAny(value.materialResources);
138
139
  if (value.gltfExporter !== undefined) {
139
140
  if (value.gltfExporter !== 'legacy' && value.gltfExporter !== 'three')
140
141
  fail('request');
@@ -272,6 +273,9 @@ export function decodeEvaluatorResultV2(json, maxGlbBytes, expectedRequestId) {
272
273
  value.error.diagnostic !== 'ROUNDED_BOX_RADIUS' &&
273
274
  value.error.diagnostic !== 'PROCEDURAL_TEXTURE_UNKNOWN_KEY' &&
274
275
  value.error.diagnostic !== 'PROCEDURAL_TEXTURE_BLEND' &&
276
+ value.error.diagnostic !== 'PROCEDURAL_TEXTURE_V3_CONTRACT' &&
277
+ value.error.diagnostic !== 'PORTABLE_MATERIAL_V3_CONTRACT' &&
278
+ value.error.diagnostic !== 'TEXTURE_ATLAS_CONTRACT' &&
275
279
  value.error.diagnostic !== 'MATERIAL_FRACTION_RANGE' &&
276
280
  value.error.diagnostic !== 'MATERIAL_RECIPE_TEXTURE_BINDING' &&
277
281
  value.error.diagnostic !== 'PORTABLE_TEXTURE_REFERENCE' &&
@@ -297,7 +301,10 @@ export function decodeEvaluatorResultV2(json, maxGlbBytes, expectedRequestId) {
297
301
  value.error.diagnostic !== 'PROGRAM_TYPE_ERROR' &&
298
302
  value.error.diagnostic !== 'PROGRAM_RANGE_ERROR' &&
299
303
  value.error.diagnostic !== 'LOD_SET' &&
300
- value.error.diagnostic !== 'OPEN_SHELL')))
304
+ value.error.diagnostic !== 'OPEN_SHELL' &&
305
+ value.error.diagnostic !== 'EXPORT_MATERIAL_SIDE_UNSUPPORTED' &&
306
+ value.error.diagnostic !== 'BONE_CHAIN_ARGUMENT' &&
307
+ value.error.diagnostic !== 'SKIN_BINDING_ARGUMENT')))
301
308
  return fail('result');
302
309
  if (value.error.qa !== undefined) {
303
310
  if (value.error.code !== 'QA_BLOCKED' ||
@@ -352,7 +359,7 @@ export function decodeEvaluatorResultV2(json, maxGlbBytes, expectedRequestId) {
352
359
  fail('result');
353
360
  try {
354
361
  for (const manifest of value.render.materialLibraryDependencies)
355
- validateMaterialManifestIdentity(manifest);
362
+ validateMaterialManifestIdentityAny(manifest);
356
363
  }
357
364
  catch {
358
365
  return fail('result');
@@ -478,8 +485,11 @@ export function assertEvaluatorResultMaterialResources(result, expected) {
478
485
  if (!result.ok)
479
486
  return;
480
487
  const actual = result.render.materialLibraryDependencies ?? [];
481
- const wanted = expected?.records.map((record) => record.manifest.revisionId).sort() ?? [];
482
- if (JSON.stringify(actual.map((manifest) => manifest.revisionId).sort()) !== JSON.stringify(wanted))
488
+ const wanted = expected?.records.map((record) => record.manifest) ?? [];
489
+ const identity = (manifest) => `${manifest.materialId}/${manifest.revisionId}`;
490
+ if (new Set(actual.map(identity)).size !== actual.length ||
491
+ JSON.stringify(actual.map(canonicalMaterialJson).sort()) !==
492
+ JSON.stringify(wanted.map(canonicalMaterialJson).sort()))
483
493
  throw new EvaluatorPortError('PROTOCOL_ERROR');
484
494
  }
485
495
  /** All transports bind decoded acceptance evidence to the same current-host snapshot. */
@@ -3,7 +3,7 @@ import { spawn } from 'node:child_process';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { AssetQaBlockedError } from "../qa/run.js";
5
5
  import { assertNoLegacyRuntimePolicy, resolveRequirementsContext } from "../requirements-context.js";
6
- import { validateMaterialLibraryPayload } from "../material-library-node.js";
6
+ import { validateMaterialLibraryPayloadAny } from "../material-library-node.js";
7
7
  import { decodeEvaluatorResultV2, assertEvaluatorResultRequirements, assertEvaluatorResultMaterialResources, evaluatorOutcomeMessage, EVALUATOR_REQUEST_VERSION, MAX_EVALUATOR_REQUEST_BYTES, } from "./protocol.js";
8
8
  const DEFAULT_DEADLINE_MS = 60_000;
9
9
  const DEFAULT_MAX_GLB_BYTES = 16 * 1024 * 1024;
@@ -67,7 +67,7 @@ export async function renderGLBViaProcessLaunch(code, options = {}, controls = {
67
67
  const maxGlbBytes = boundedInteger(controls.maxGlbBytes, DEFAULT_MAX_GLB_BYTES, 64 * 1024 * 1024);
68
68
  const maxResponseBytes = boundedInteger(controls.maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES, 96 * 1024 * 1024);
69
69
  if (options.materialResources !== undefined)
70
- validateMaterialLibraryPayload(options.materialResources);
70
+ validateMaterialLibraryPayloadAny(options.materialResources);
71
71
  const request = {
72
72
  version: EVALUATOR_REQUEST_VERSION,
73
73
  requestId: 'render-1',
@@ -16,8 +16,16 @@ export async function prepareNodeExport(hasTextures) {
16
16
  host['FileReader'] = ExportFileReader;
17
17
  if (!hasTextures || host['OffscreenCanvas'] || host['document'])
18
18
  return;
19
- const { Canvas, ImageData } = await import('@napi-rs/canvas');
19
+ const { Canvas, ImageData } = await import('@napi-rs/canvas').catch((cause) => {
20
+ throw new Error('Headless textured export requires @napi-rs/canvas. Reinstall Kiln with optional dependencies enabled, then verify the native binding in the same Node runtime and platform used by the Kiln host.', { cause });
21
+ });
20
22
  class ExportCanvas extends Canvas {
23
+ constructor(width, height) {
24
+ super(width, height);
25
+ // Native Canvas.data() is a method, not a DataTexture image byte array.
26
+ // Shadow it on the instance: the native constructor supplies its own prototype.
27
+ Object.defineProperty(this, 'data', { value: undefined });
28
+ }
21
29
  async convertToBlob(options = {}) {
22
30
  const type = options.type ?? options.mime ?? 'image/png';
23
31
  if (type !== 'image/png' && type !== 'image/jpeg' && type !== 'image/webp')
@@ -7,6 +7,13 @@ export declare const GEOMETRY_ALLOCATION_LIMITS: Readonly<{
7
7
  repeatedMeshes: 10000;
8
8
  primitiveSegments: 4096;
9
9
  primitiveGridVertices: 262144;
10
+ unwrapVertices: 65536;
11
+ unwrapTriangles: 1000000;
12
+ unwrapAttributeBytes: number;
13
+ unwrapResolution: 4096;
14
+ skinBones: 1024;
15
+ skinVertices: 262144;
16
+ skinAttributeBytes: number;
10
17
  }>;
11
18
  export declare function assertDimension(name: string, value: number, allowZero?: boolean): void;
12
19
  export declare function assertPrimitiveSegments(name: string, value: number, minimum: number): void;
@@ -7,6 +7,14 @@ export const GEOMETRY_ALLOCATION_LIMITS = Object.freeze({
7
7
  repeatedMeshes: 10_000,
8
8
  primitiveSegments: 4096,
9
9
  primitiveGridVertices: 262_144,
10
+ // xatlasjs 0.2 returns Uint16 indices and source-vertex correspondence.
11
+ unwrapVertices: 65_536,
12
+ unwrapTriangles: 1_000_000,
13
+ unwrapAttributeBytes: 128 * 1024 * 1024,
14
+ unwrapResolution: 4096,
15
+ skinBones: 1024,
16
+ skinVertices: 262_144,
17
+ skinAttributeBytes: 128 * 1024 * 1024,
10
18
  });
11
19
  export function assertDimension(name, value, allowZero = false) {
12
20
  if (!Number.isFinite(value) ||
@@ -0,0 +1,6 @@
1
+ import * as THREE from 'three';
2
+ import type { TextureInfo } from '@gltf-transform/core';
3
+ /** glTF image orientation and sampling belong to every texture use, not just atlases. */
4
+ export declare function restoreGltfTextureBinding(texture: THREE.Texture, info: TextureInfo): void;
5
+ /** Two uses of one image may differ in sampler, UV channel, transform or atlas provenance. */
6
+ export declare function gltfTextureBindingKey(info: TextureInfo | null | undefined): string;
@@ -0,0 +1,54 @@
1
+ import * as THREE from 'three';
2
+ const MIN = {
3
+ 9728: THREE.NearestFilter,
4
+ 9729: THREE.LinearFilter,
5
+ 9984: THREE.NearestMipmapNearestFilter,
6
+ 9985: THREE.LinearMipmapNearestFilter,
7
+ 9986: THREE.NearestMipmapLinearFilter,
8
+ 9987: THREE.LinearMipmapLinearFilter,
9
+ };
10
+ const WRAP = {
11
+ 33071: THREE.ClampToEdgeWrapping,
12
+ 33648: THREE.MirroredRepeatWrapping,
13
+ 10497: THREE.RepeatWrapping,
14
+ };
15
+ /** glTF image orientation and sampling belong to every texture use, not just atlases. */
16
+ export function restoreGltfTextureBinding(texture, info) {
17
+ texture.minFilter = MIN[info.getMinFilter() ?? 9729];
18
+ texture.magFilter = info.getMagFilter() === 9728 ? THREE.NearestFilter : THREE.LinearFilter;
19
+ texture.wrapS = WRAP[info.getWrapS()];
20
+ texture.wrapT = WRAP[info.getWrapT()];
21
+ texture.generateMipmaps = [9984, 9985, 9986, 9987].includes(info.getMinFilter() ?? 9729);
22
+ texture.flipY = false;
23
+ const transform = info.getExtension('KHR_texture_transform');
24
+ texture.channel = transform?.getTexCoord() ?? info.getTexCoord();
25
+ if (transform) {
26
+ texture.offset.fromArray(transform.getOffset());
27
+ texture.repeat.fromArray(transform.getScale());
28
+ texture.rotation = transform.getRotation();
29
+ // glTF composes T*R*S. Three's automatic UV matrix composes nonuniform
30
+ // repeat and rotation in a different order; retain the actual glTF basis.
31
+ const c = Math.cos(texture.rotation), s = Math.sin(texture.rotation);
32
+ const [sx, sy] = transform.getScale(), [x, y] = transform.getOffset();
33
+ texture.matrix.set(sx * c, sy * s, x, -sx * s, sy * c, y, 0, 0, 1);
34
+ texture.matrixAutoUpdate = false;
35
+ }
36
+ }
37
+ /** Two uses of one image may differ in sampler, UV channel, transform or atlas provenance. */
38
+ export function gltfTextureBindingKey(info) {
39
+ if (!info)
40
+ return '';
41
+ const transform = info.getExtension('KHR_texture_transform');
42
+ return JSON.stringify([
43
+ info.getMinFilter(),
44
+ info.getMagFilter(),
45
+ info.getWrapS(),
46
+ info.getWrapT(),
47
+ info.getTexCoord(),
48
+ transform?.getTexCoord(),
49
+ transform?.getOffset(),
50
+ transform?.getScale(),
51
+ transform?.getRotation(),
52
+ info.getExtras()['kilnAtlasReference'],
53
+ ]);
54
+ }
@@ -0,0 +1,6 @@
1
+ /** Shared CLI/MCP JSON authoring boundary. Library pins never imply project membership. */
2
+ import { type MaterialLibrary, type MaterialRecord } from "./material-library.js";
3
+ export declare function createProceduralMaterialRecord(value: unknown, library: MaterialLibrary): Promise<{
4
+ record: MaterialRecord;
5
+ dependencies: MaterialRecord[];
6
+ }>;
@@ -0,0 +1,49 @@
1
+ /** Shared CLI/MCP JSON authoring boundary. Library pins never imply project membership. */
2
+ import { MATERIAL_LIBRARY_LIMITS, assertMaterialJson, proceduralMaterialDraftSchema, proceduralMaterialDraftV2Schema, } from "./material-library.js";
3
+ import { createMaterialRecordV1, createMaterialRecordV2, resolveMaterialRecordClosure, } from "./material-library-node.js";
4
+ import { canonicalizeTextureAtlasSpecV1 } from "./texture-atlas-contract.js";
5
+ export async function createProceduralMaterialRecord(value, library) {
6
+ assertMaterialJson(value);
7
+ if (new TextEncoder().encode(JSON.stringify(value)).length >
8
+ MATERIAL_LIBRARY_LIMITS.maxManifestBytes)
9
+ throw new Error('Material draft exceeds size limit');
10
+ if (!value || typeof value !== 'object' || Array.isArray(value))
11
+ throw new Error('Material draft must be a JSON object');
12
+ if (!Object.hasOwn(value, 'schemaVersion'))
13
+ return {
14
+ record: await createMaterialRecordV1(proceduralMaterialDraftSchema.parse(value)),
15
+ dependencies: [],
16
+ };
17
+ const draft = proceduralMaterialDraftV2Schema.parse(value);
18
+ const pins = new Map();
19
+ for (const map of draft.maps) {
20
+ if (!map.atlas)
21
+ continue;
22
+ for (const region of canonicalizeTextureAtlasSpecV1(map.atlas).regions) {
23
+ if (region.source.kind !== 'resource')
24
+ continue;
25
+ const pin = region.source.pin;
26
+ pins.set(`${pin.resourceId}@${pin.revisionId}`, pin);
27
+ }
28
+ }
29
+ if (pins.size > MATERIAL_LIBRARY_LIMITS.maxPayloadRecords)
30
+ throw new Error('Material source closure count limit exceeded');
31
+ const roots = [];
32
+ if (pins.size && !library.readManifest)
33
+ throw new Error('Pinned atlas creation requires a material library with readManifest support.');
34
+ for (const pin of pins.values()) {
35
+ const manifest = await library.readManifest(pin.resourceId, pin.revisionId);
36
+ if (manifest.materialId !== pin.resourceId || manifest.revisionId !== pin.revisionId)
37
+ throw new Error('Material source pin identity mismatch');
38
+ roots.push(manifest);
39
+ }
40
+ const dependencies = roots.length
41
+ ? await resolveMaterialRecordClosure(roots, {
42
+ readManifest: (id, revision) => library.readManifest(id, revision),
43
+ read: (id, revision) => library.readRecordFiles
44
+ ? library.readRecordFiles(id, revision)
45
+ : library.read(id, revision),
46
+ })
47
+ : [];
48
+ return { record: await createMaterialRecordV2(draft, { materials: dependencies }), dependencies };
49
+ }