@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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +74 -0
- package/README.md +7 -0
- package/dist/agent-run.mjs +6392 -1477
- package/dist/build.json +23 -23
- package/dist/cli.mjs +8453 -3407
- package/dist/evaluator-worker.mjs +16608 -12387
- package/dist/mcp-engine.mjs +6746 -1725
- package/dist/mcp-server.mjs +4 -4
- package/dist/viewer/app.js +203 -199
- package/dist/viewer/chat.html +209 -205
- package/dist/viewer/style.css +10 -0
- package/docs/atlas-provenance.md +23 -0
- package/docs/community-exporter.md +27 -0
- package/docs/export-profiles.md +20 -0
- package/docs/material-library.md +9 -5
- package/docs/procedural-surfaces-v3.md +276 -0
- package/docs/projects-and-live-review.md +1 -1
- package/docs/rendering.md +41 -0
- package/docs/sdk.md +53 -0
- package/docs/tools-operations.md +2 -2
- package/docs/tools.md +3 -3
- package/lib/asset-materials-node.d.ts +3 -3
- package/lib/asset-materials-node.js +29 -33
- package/lib/assets-node.d.ts +1 -1
- package/lib/assets-node.js +11 -4
- package/lib/assets.d.ts +4 -2
- package/lib/assets.js +61 -16
- package/lib/atlas-provenance.d.ts +86 -0
- package/lib/atlas-provenance.js +448 -0
- package/lib/character.d.ts +11 -2
- package/lib/character.js +90 -2
- package/lib/community-exporter.d.ts +3 -2
- package/lib/community-exporter.js +139 -3
- package/lib/discovery/helper-contracts.js +161 -7
- package/lib/discovery/helper-specs.js +49 -4
- package/lib/discovery/shapes.js +20 -7
- package/lib/engine-identity.d.ts +1 -1
- package/lib/engine-identity.js +1 -1
- package/lib/evaluator/authoring-diagnostic.d.ts +1 -1
- package/lib/evaluator/authoring-diagnostic.js +12 -0
- package/lib/evaluator/protocol.d.ts +2 -2
- package/lib/evaluator/protocol.js +16 -6
- package/lib/evaluator/subprocess.js +2 -2
- package/lib/exporter-node.js +9 -1
- package/lib/geometry-budget.d.ts +7 -0
- package/lib/geometry-budget.js +8 -0
- package/lib/gltf-texture-binding.d.ts +6 -0
- package/lib/gltf-texture-binding.js +54 -0
- package/lib/material-authoring-node.d.ts +6 -0
- package/lib/material-authoring-node.js +49 -0
- package/lib/material-authoring-schema.d.ts +871 -0
- package/lib/material-authoring-schema.js +310 -0
- package/lib/material-export.d.ts +5 -0
- package/lib/material-export.js +21 -0
- package/lib/material-library-node.d.ts +34 -8
- package/lib/material-library-node.js +691 -11
- package/lib/material-library.d.ts +3704 -4
- package/lib/material-library.js +272 -0
- package/lib/mcp-core.d.ts +1 -1
- package/lib/pinned-material-map.d.ts +33 -0
- package/lib/pinned-material-map.js +1 -0
- package/lib/portable-material-runtime.d.ts +5 -0
- package/lib/portable-material-runtime.js +160 -0
- package/lib/portable-sampler-runtime.d.ts +4 -0
- package/lib/portable-sampler-runtime.js +26 -0
- package/lib/portable-sampler.d.ts +11 -0
- package/lib/portable-sampler.js +42 -0
- package/lib/primitives.d.ts +15 -3
- package/lib/primitives.js +24 -4
- package/lib/procedural-material-v2.d.ts +2 -0
- package/lib/procedural-material-v2.js +2 -1
- package/lib/procedural-material-v3.d.ts +72 -0
- package/lib/procedural-material-v3.js +162 -0
- package/lib/procedural-texture-v2-compiler.d.ts +31 -0
- package/lib/procedural-texture-v2-compiler.js +172 -0
- package/lib/procedural-texture-v3-contract.d.ts +125 -0
- package/lib/procedural-texture-v3-contract.js +333 -0
- package/lib/procedural-texture-v3.d.ts +12 -0
- package/lib/procedural-texture-v3.js +165 -0
- package/lib/procedural-texture.d.ts +9 -31
- package/lib/procedural-texture.js +43 -170
- package/lib/project-bundle-node.d.ts +1 -1
- package/lib/project-bundle-node.js +70 -13
- package/lib/project-bundle.d.ts +91 -3
- package/lib/project-bundle.js +48 -7
- package/lib/qa/atlas.d.ts +21 -0
- package/lib/qa/atlas.js +474 -0
- package/lib/qa/index.d.ts +1 -0
- package/lib/qa/index.js +1 -0
- package/lib/qa/material.js +2 -0
- package/lib/qa/skin-integrity.d.ts +18 -0
- package/lib/qa/skin-integrity.js +221 -0
- package/lib/render-service-host.js +4 -7
- package/lib/render.d.ts +3 -3
- package/lib/render.js +108 -16
- package/lib/skin-binding.d.ts +20 -0
- package/lib/skin-binding.js +144 -0
- package/lib/texture-atlas-contract.d.ts +97 -0
- package/lib/texture-atlas-contract.js +414 -0
- package/lib/texture-atlas-pixels.d.ts +73 -0
- package/lib/texture-atlas-pixels.js +290 -0
- package/lib/texture-atlas.d.ts +10 -0
- package/lib/texture-atlas.js +143 -0
- package/lib/texture-bake.d.ts +3 -0
- package/lib/texture-bake.js +66 -11
- package/lib/texture-resolver.d.ts +22 -0
- package/lib/texture-resolver.js +24 -0
- package/lib/tools/materials.js +8 -7
- package/lib/tools/registry.js +5 -16
- package/lib/tools/review.js +1 -1
- package/lib/uv-data.d.ts +32 -0
- package/lib/uv-data.js +172 -0
- package/lib/uv-shapes.js +3 -0
- package/lib/uv.d.ts +9 -3
- package/lib/uv.js +53 -82
- package/lib/views/camera.d.ts +3 -3
- package/lib/views/camera.js +32 -28
- package/lib/views/face-policy.d.ts +9 -0
- package/lib/views/face-policy.js +16 -0
- package/lib/views/glb.js +218 -24
- package/lib/views/pose.d.ts +7 -1
- package/lib/views/pose.js +63 -12
- package/lib/views/raster.d.ts +6 -2
- package/lib/views/raster.js +49 -37
- package/lib/views/scene-copy.d.ts +6 -0
- package/lib/views/scene-copy.js +73 -0
- package/lib/views/scene-raster.js +2 -1
- package/lib/views/triangle-coverage.d.ts +8 -0
- package/lib/views/triangle-coverage.js +58 -0
- package/lib/workspace-node.js +7 -3
- package/lib/workspace.d.ts +2 -2
- package/package.json +3 -1
- package/plugin.json +1 -1
- package/plugins/kiln-engine/.claude-plugin/plugin.json +1 -1
- package/plugins/kiln-engine/README.md +3 -3
- package/plugins/kiln-engine/package-provenance.json +6 -6
- package/plugins/kiln-engine/plugin.json +1 -1
- package/plugins/kiln-engine/runtime.json +1 -1
- package/skills/kiln-author-asset/references/geometry-recipes.md +124 -0
- package/skills/kiln-author-asset/references/projects-and-materials.md +32 -2
- package/skills/kiln-compose-scene/references/runtime-scenes.md +19 -0
- package/skills/kiln-qa-asset/references/integration-checks.md +37 -2
- package/src/asset-materials-node.ts +38 -45
- package/src/assets-node.ts +22 -5
- package/src/assets.ts +61 -20
- package/src/atlas-provenance.ts +522 -0
- package/src/character.ts +107 -2
- package/src/community-exporter.ts +143 -3
- package/src/discovery/helper-contracts.ts +188 -7
- package/src/discovery/helper-specs.ts +69 -4
- package/src/discovery/shapes.ts +25 -9
- package/src/engine-identity.ts +1 -1
- package/src/evaluator/authoring-diagnostic.ts +19 -1
- package/src/evaluator/protocol.ts +19 -9
- package/src/evaluator/subprocess.ts +2 -2
- package/src/exporter-node.ts +12 -1
- package/src/generated/mcp-manifest.json +3 -3
- package/src/geometry-budget.ts +8 -0
- package/src/gltf-texture-binding.ts +60 -0
- package/src/material-authoring-node.ts +68 -0
- package/src/material-authoring-schema.ts +343 -0
- package/src/material-export.ts +25 -0
- package/src/material-library-node.ts +840 -16
- package/src/material-library.ts +348 -5
- package/src/pinned-material-map.ts +43 -0
- package/src/portable-material-runtime.ts +200 -0
- package/src/portable-sampler-runtime.ts +40 -0
- package/src/portable-sampler.ts +54 -0
- package/src/primitives.ts +98 -4
- package/src/procedural-material-v2.ts +2 -1
- package/src/procedural-material-v3.ts +224 -0
- package/src/procedural-texture-v2-compiler.ts +234 -0
- package/src/procedural-texture-v3-contract.ts +437 -0
- package/src/procedural-texture-v3.ts +220 -0
- package/src/procedural-texture.ts +61 -229
- package/src/project-bundle-node.ts +106 -14
- package/src/project-bundle.ts +61 -18
- package/src/project-cli.ts +29 -17
- package/src/qa/atlas.ts +597 -0
- package/src/qa/index.ts +1 -0
- package/src/qa/material.ts +2 -0
- package/src/qa/skin-integrity.ts +291 -0
- package/src/render-service-host.ts +4 -6
- package/src/render.ts +133 -15
- package/src/skin-binding.ts +225 -0
- package/src/texture-atlas-contract.ts +558 -0
- package/src/texture-atlas-pixels.ts +456 -0
- package/src/texture-atlas.ts +185 -0
- package/src/texture-bake.ts +88 -10
- package/src/texture-resolver.ts +55 -0
- package/src/tools/materials.ts +15 -11
- package/src/tools/registry.ts +5 -15
- package/src/tools/review.ts +1 -1
- package/src/uv-data.ts +238 -0
- package/src/uv-shapes.ts +3 -0
- package/src/uv.ts +73 -128
- package/src/viewer/app.ts +37 -0
- package/src/viewer/live-panel.ts +57 -1
- package/src/viewer/material-preview-panel.ts +140 -0
- package/src/viewer/material-preview.ts +138 -0
- package/src/viewer/materials-panel.ts +34 -5
- package/src/viewer/navigation.ts +2 -0
- package/src/views/camera.ts +37 -40
- package/src/views/face-policy.ts +20 -0
- package/src/views/glb.ts +294 -17
- package/src/views/pose.ts +71 -13
- package/src/views/raster.ts +73 -46
- package/src/views/scene-copy.ts +86 -0
- package/src/views/scene-raster.ts +2 -1
- package/src/views/triangle-coverage.ts +78 -0
- package/src/workspace-node.ts +11 -3
- 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
|
|
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?:
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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
|
|
482
|
-
|
|
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 {
|
|
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
|
-
|
|
70
|
+
validateMaterialLibraryPayloadAny(options.materialResources);
|
|
71
71
|
const request = {
|
|
72
72
|
version: EVALUATOR_REQUEST_VERSION,
|
|
73
73
|
requestId: 'render-1',
|
package/lib/exporter-node.js
CHANGED
|
@@ -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')
|
package/lib/geometry-budget.d.ts
CHANGED
|
@@ -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;
|
package/lib/geometry-budget.js
CHANGED
|
@@ -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
|
+
}
|