@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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +86 -1
- package/README.md +18 -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/install.md +7 -6
- package/docs/material-library.md +9 -5
- package/docs/migration.md +16 -0
- 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/runtime.md +3 -3
- package/docs/sdk.md +55 -2
- 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/SKILL.md +1 -1
- package/skills/kiln-author-asset/references/camera-cli.md +13 -0
- package/skills/kiln-author-asset/references/geometry-recipes.md +134 -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
|
@@ -37,179 +37,52 @@
|
|
|
37
37
|
* still catches.
|
|
38
38
|
*/
|
|
39
39
|
import * as THREE from 'three';
|
|
40
|
-
import {
|
|
40
|
+
import { compileProceduralTextureSpecV2 } from "./procedural-texture-v2-compiler.js";
|
|
41
|
+
import { compileProceduralTextureSpecV3 } from "./procedural-texture-v3.js";
|
|
42
|
+
import { ProceduralTextureError, } from "./procedural-material-v2.js";
|
|
41
43
|
export { MAX_NOISE_OCTAVES, MAX_PORTABLE_MATERIAL_TEXELS, MAX_PORTABLE_MATERIAL_TEXTURES, MAX_PROCEDURAL_LAYERS, MAX_PROCEDURAL_NAME_LENGTH, MAX_PROCEDURAL_PATTERN_COUNT, MAX_PROCEDURAL_SIZE, MIN_PROCEDURAL_SIZE, PORTABLE_MATERIAL_SPEC_VERSION, PROCEDURAL_TEXTURE_SPEC_VERSION, ProceduralTextureError, canonicalProceduralTextureJsonV2, canonicalizePortableMaterialSpecV2, canonicalizeProceduralTextureSpecV2, hashProceduralTextureSpecV2, migrateProceduralTextureSpecV1, } from "./procedural-material-v2.js";
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
* `period` is in lattice cells, and both axes use the same one.
|
|
60
|
-
*/
|
|
61
|
-
function valueNoise(u, v, period, seed) {
|
|
62
|
-
const x = u * period;
|
|
63
|
-
const y = v * period;
|
|
64
|
-
const xi = Math.floor(x);
|
|
65
|
-
const yi = Math.floor(y);
|
|
66
|
-
const xf = x - xi;
|
|
67
|
-
const yf = y - yi;
|
|
68
|
-
// Wrapping the lattice index (not the sample point) is what makes it tile:
|
|
69
|
-
// the cell to the right of the last one is the first one again.
|
|
70
|
-
const wrap = (n) => ((n % period) + period) % period;
|
|
71
|
-
const x0 = wrap(xi);
|
|
72
|
-
const y0 = wrap(yi);
|
|
73
|
-
const x1 = wrap(xi + 1);
|
|
74
|
-
const y1 = wrap(yi + 1);
|
|
75
|
-
const sx = smoothstep(xf);
|
|
76
|
-
const sy = smoothstep(yf);
|
|
77
|
-
const n00 = hash2(x0, y0, seed);
|
|
78
|
-
const n10 = hash2(x1, y0, seed);
|
|
79
|
-
const n01 = hash2(x0, y1, seed);
|
|
80
|
-
const n11 = hash2(x1, y1, seed);
|
|
81
|
-
const top = n00 + sx * (n10 - n00);
|
|
82
|
-
const bottom = n01 + sx * (n11 - n01);
|
|
83
|
-
return top + sy * (bottom - top);
|
|
84
|
-
}
|
|
85
|
-
function fbm(u, v, period, octaves, seed) {
|
|
86
|
-
let sum = 0;
|
|
87
|
-
let amplitude = 1;
|
|
88
|
-
let total = 0;
|
|
89
|
-
let p = period;
|
|
90
|
-
for (let o = 0; o < octaves; o++) {
|
|
91
|
-
sum += valueNoise(u, v, p, seed + o * 101) * amplitude;
|
|
92
|
-
total += amplitude;
|
|
93
|
-
amplitude *= 0.5;
|
|
94
|
-
// Doubling the period keeps every octave tileable at the base period.
|
|
95
|
-
p *= 2;
|
|
96
|
-
}
|
|
97
|
-
return total > 0 ? sum / total : 0;
|
|
98
|
-
}
|
|
99
|
-
// -----------------------------------------------------------------------------
|
|
100
|
-
// Layer evaluation
|
|
101
|
-
// -----------------------------------------------------------------------------
|
|
102
|
-
const rgbOf = (color) => [
|
|
103
|
-
(color >> 16) & 0xff,
|
|
104
|
-
(color >> 8) & 0xff,
|
|
105
|
-
color & 0xff,
|
|
106
|
-
];
|
|
107
|
-
const lerp = (a, b, t) => a + (b - a) * t;
|
|
108
|
-
/** Rotate normalized coords about the center so angled patterns stay centered. */
|
|
109
|
-
function project(u, v, angleDeg) {
|
|
110
|
-
const a = (angleDeg * Math.PI) / 180;
|
|
111
|
-
return (u - 0.5) * Math.cos(a) + (v - 0.5) * Math.sin(a) + 0.5;
|
|
112
|
-
}
|
|
113
|
-
/** Evaluate one layer at a normalized coordinate, returning 0-255 RGB. */
|
|
114
|
-
function evalLayer(layer, u, v) {
|
|
115
|
-
switch (layer.op) {
|
|
116
|
-
case 'solid':
|
|
117
|
-
return rgbOf(layer.color);
|
|
118
|
-
case 'checker': {
|
|
119
|
-
const cx = Math.floor(u * layer.squares);
|
|
120
|
-
const cy = Math.floor(v * layer.squares);
|
|
121
|
-
return rgbOf((cx + cy) % 2 === 0 ? layer.colorA : layer.colorB);
|
|
122
|
-
}
|
|
123
|
-
case 'stripes': {
|
|
124
|
-
const t = project(u, v, layer.angleDeg);
|
|
125
|
-
return rgbOf(Math.floor(t * layer.count) % 2 === 0 ? layer.colorA : layer.colorB);
|
|
126
|
-
}
|
|
127
|
-
case 'gradient': {
|
|
128
|
-
const t = Math.min(1, Math.max(0, project(u, v, layer.angleDeg)));
|
|
129
|
-
const from = rgbOf(layer.from);
|
|
130
|
-
const to = rgbOf(layer.to);
|
|
131
|
-
return [lerp(from[0], to[0], t), lerp(from[1], to[1], t), lerp(from[2], to[2], t)];
|
|
132
|
-
}
|
|
133
|
-
case 'bricks': {
|
|
134
|
-
const row = Math.floor(v * layer.rows);
|
|
135
|
-
// Offset alternate rows, wrapping into [0,1) so the pattern still tiles.
|
|
136
|
-
const shifted = (u + (row % 2 === 0 ? 0 : layer.stagger / layer.cols) + 1) % 1;
|
|
137
|
-
const inCol = (shifted * layer.cols) % 1;
|
|
138
|
-
const inRow = (v * layer.rows) % 1;
|
|
139
|
-
const isMortar = inCol < layer.mortarWidth ||
|
|
140
|
-
inCol > 1 - layer.mortarWidth ||
|
|
141
|
-
inRow < layer.mortarWidth ||
|
|
142
|
-
inRow > 1 - layer.mortarWidth;
|
|
143
|
-
return rgbOf(isMortar ? layer.mortar : layer.brick);
|
|
144
|
-
}
|
|
145
|
-
case 'noise': {
|
|
146
|
-
const n = fbm(u, v, layer.scale, layer.octaves, layer.seed);
|
|
147
|
-
const a = rgbOf(layer.colorA);
|
|
148
|
-
const b = rgbOf(layer.colorB);
|
|
149
|
-
return [lerp(a[0], b[0], n), lerp(a[1], b[1], n), lerp(a[2], b[2], n)];
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function blendChannel(mode, base, top) {
|
|
154
|
-
const b = base / 255;
|
|
155
|
-
const t = top / 255;
|
|
156
|
-
let out;
|
|
157
|
-
switch (mode) {
|
|
158
|
-
case 'multiply':
|
|
159
|
-
out = b * t;
|
|
160
|
-
break;
|
|
161
|
-
case 'screen':
|
|
162
|
-
out = 1 - (1 - b) * (1 - t);
|
|
163
|
-
break;
|
|
164
|
-
case 'overlay':
|
|
165
|
-
out = b < 0.5 ? 2 * b * t : 1 - 2 * (1 - b) * (1 - t);
|
|
166
|
-
break;
|
|
167
|
-
default:
|
|
168
|
-
out = t;
|
|
169
|
-
}
|
|
170
|
-
return out * 255;
|
|
171
|
-
}
|
|
172
|
-
/** Validate, canonicalize, and compile one texture without constructing Three.js state. */
|
|
173
|
-
export function compileProceduralTextureSpecV2(input) {
|
|
174
|
-
const spec = canonicalizeProceduralTextureSpecV2(input);
|
|
175
|
-
const { size, layers } = spec;
|
|
176
|
-
const data = new Uint8Array(size * size * 4);
|
|
177
|
-
for (let y = 0; y < size; y++) {
|
|
178
|
-
// Sample at texel centers so a pattern with N divisions lands on exact
|
|
179
|
-
// boundaries instead of drifting half a texel.
|
|
180
|
-
const v = (y + 0.5) / size;
|
|
181
|
-
for (let x = 0; x < size; x++) {
|
|
182
|
-
const u = (x + 0.5) / size;
|
|
183
|
-
let r = 0;
|
|
184
|
-
let g = 0;
|
|
185
|
-
let b = 0;
|
|
186
|
-
for (const [i, layer] of layers.entries()) {
|
|
187
|
-
const [lr, lg, lb] = evalLayer(layer, u, v);
|
|
188
|
-
if (i === 0) {
|
|
189
|
-
r = lr;
|
|
190
|
-
g = lg;
|
|
191
|
-
b = lb;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
r = lerp(r, blendChannel(layer.blend, r, lr), layer.opacity);
|
|
195
|
-
g = lerp(g, blendChannel(layer.blend, g, lg), layer.opacity);
|
|
196
|
-
b = lerp(b, blendChannel(layer.blend, b, lb), layer.opacity);
|
|
197
|
-
}
|
|
198
|
-
const o = (y * size + x) * 4;
|
|
199
|
-
data[o] = Math.max(0, Math.min(255, Math.round(r)));
|
|
200
|
-
data[o + 1] = Math.max(0, Math.min(255, Math.round(g)));
|
|
201
|
-
data[o + 2] = Math.max(0, Math.min(255, Math.round(b)));
|
|
202
|
-
data[o + 3] = 255;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
spec,
|
|
207
|
-
pixels: data,
|
|
208
|
-
canonicalJson: canonicalProceduralTextureJsonV2(spec),
|
|
209
|
-
recipeHash: hashProceduralTextureSpecV2(spec),
|
|
210
|
-
};
|
|
44
|
+
export * from "./procedural-texture-v3.js";
|
|
45
|
+
/** Dispatch never invokes an input accessor; absent/undefined retains V1 compatibility. */
|
|
46
|
+
export function proceduralTextureSchemaVersion(input) {
|
|
47
|
+
if (!input || typeof input !== 'object' || Array.isArray(input))
|
|
48
|
+
throw new ProceduralTextureError('proceduralTexture requires a plain JSON object.', 'PROCEDURAL_TEXTURE_V3_CONTRACT');
|
|
49
|
+
const prototype = Object.getPrototypeOf(input);
|
|
50
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
51
|
+
throw new ProceduralTextureError('proceduralTexture requires a plain JSON object.', 'PROCEDURAL_TEXTURE_V3_CONTRACT');
|
|
52
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, 'schemaVersion');
|
|
53
|
+
if (descriptor && (!descriptor.enumerable || !('value' in descriptor)))
|
|
54
|
+
throw new ProceduralTextureError('proceduralTexture.schemaVersion must be a JSON data field.', 'PROCEDURAL_TEXTURE_V3_CONTRACT');
|
|
55
|
+
const version = descriptor?.value;
|
|
56
|
+
if (version === undefined || version === 1)
|
|
57
|
+
return 1;
|
|
58
|
+
if (version === 2 || version === 3)
|
|
59
|
+
return version;
|
|
60
|
+
throw new ProceduralTextureError('proceduralTexture.schemaVersion must be 1, 2 or 3.', 'PROCEDURAL_TEXTURE_V3_CONTRACT');
|
|
211
61
|
}
|
|
62
|
+
export { compileProceduralTextureSpecV2 } from "./procedural-texture-v2-compiler.js";
|
|
212
63
|
export function proceduralTexture(spec) {
|
|
64
|
+
if (proceduralTextureSchemaVersion(spec) === 3) {
|
|
65
|
+
const compiled = compileProceduralTextureSpecV3(spec);
|
|
66
|
+
const { width, height, usage, name } = compiled.spec;
|
|
67
|
+
const tex = new THREE.DataTexture(compiled.pixels, width, height, THREE.RGBAFormat, THREE.UnsignedByteType);
|
|
68
|
+
if (name)
|
|
69
|
+
tex.name = name;
|
|
70
|
+
tex.colorSpace =
|
|
71
|
+
usage === 'albedo' || usage === 'emissive' ? THREE.SRGBColorSpace : THREE.NoColorSpace;
|
|
72
|
+
tex.wrapS = THREE.RepeatWrapping;
|
|
73
|
+
tex.wrapT = THREE.RepeatWrapping;
|
|
74
|
+
tex.minFilter = THREE.LinearMipmapLinearFilter;
|
|
75
|
+
tex.magFilter = THREE.LinearFilter;
|
|
76
|
+
tex.generateMipmaps = true;
|
|
77
|
+
tex.needsUpdate = true;
|
|
78
|
+
tex.userData['kilnProcedural'] = {
|
|
79
|
+
...compiled.spec,
|
|
80
|
+
canonicalJson: compiled.canonicalJson,
|
|
81
|
+
recipeHash: compiled.recipeHash,
|
|
82
|
+
compilerId: compiled.compilerId,
|
|
83
|
+
};
|
|
84
|
+
return tex;
|
|
85
|
+
}
|
|
213
86
|
const compiled = compileProceduralTextureSpecV2(spec);
|
|
214
87
|
const { size, usage, name, layers } = compiled.spec;
|
|
215
88
|
const tex = new THREE.DataTexture(compiled.pixels, size, size, THREE.RGBAFormat, THREE.UnsignedByteType);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AssetLibrary, AssetRecord } from "./assets.js";
|
|
2
2
|
import type { FileWorkspace } from "./workspace-node.js";
|
|
3
3
|
import { type ProjectRevision } from "./projects.js";
|
|
4
|
-
export declare function resolveAssetMaterialPayload(workspace: FileWorkspace, asset: AssetRecord): Promise<import("./material-library.js").
|
|
4
|
+
export declare function resolveAssetMaterialPayload(workspace: FileWorkspace, asset: AssetRecord): Promise<import("./material-library.js").MaterialLibraryPayload>;
|
|
5
5
|
export declare function exportWorkspaceProjectBundle(workspace: FileWorkspace, library: AssetLibrary, projectId: string, options?: {
|
|
6
6
|
revisionId?: string;
|
|
7
7
|
profile?: 'editable' | 'runtime';
|
|
@@ -4,9 +4,9 @@ import { link, lstat, mkdir, readFile, realpath, unlink, writeFile } from 'node:
|
|
|
4
4
|
import { join, relative, resolve, sep } from 'node:path';
|
|
5
5
|
import { verifyAssetRecord } from "./assets-node.js";
|
|
6
6
|
import { projectDraftSchema, projectIdSchema, ProjectExistsError, ProjectNotFoundError, } from "./projects.js";
|
|
7
|
-
import { canonicalMaterialJson } from "./material-library.js";
|
|
7
|
+
import { canonicalMaterialJson, MATERIAL_LIBRARY_LIMITS, } from "./material-library.js";
|
|
8
8
|
import { dependencyManifests, resolveSavedAssetMaterials } from "./asset-materials-node.js";
|
|
9
|
-
import { createMaterialLibraryPayload,
|
|
9
|
+
import { createMaterialLibraryPayload, verifyMaterialRecord, resolveMaterialRecordClosure, validateMaterialManifestIdentityAny, } from "./material-library-node.js";
|
|
10
10
|
import { PROJECT_BUNDLE_LIMITS, decodeProjectBundle, encodeProjectBundle, projectBundleHash, } from "./project-bundle.js";
|
|
11
11
|
async function assetMaterials(workspace, asset) {
|
|
12
12
|
return resolveSavedAssetMaterials(asset, workspace.materials);
|
|
@@ -32,25 +32,76 @@ export async function exportWorkspaceProjectBundle(workspace, library, projectId
|
|
|
32
32
|
record: await library.read(item.asset.collectionId, item.asset.assetId, item.asset.revisionId),
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
let materials = [];
|
|
36
36
|
if (profile === 'editable') {
|
|
37
|
+
const roots = new Map();
|
|
38
|
+
const assetRoots = new Set();
|
|
39
|
+
const embedded = new Map();
|
|
40
|
+
const addRoot = (manifest) => {
|
|
41
|
+
const key = `${manifest.materialId}/${manifest.revisionId}`;
|
|
42
|
+
const previous = roots.get(key);
|
|
43
|
+
if (previous && canonicalMaterialJson(previous) !== canonicalMaterialJson(manifest))
|
|
44
|
+
throw new Error('Conflicting project material identity');
|
|
45
|
+
roots.set(key, manifest);
|
|
46
|
+
};
|
|
37
47
|
for (const dependency of project.materialDependencies) {
|
|
38
48
|
if (dependency.sha256 !== dependency.revisionId)
|
|
39
49
|
throw new Error(`Material lock hash mismatch: ${dependency.resourceId}`);
|
|
40
|
-
let
|
|
50
|
+
let manifest;
|
|
41
51
|
try {
|
|
42
|
-
|
|
52
|
+
manifest = await workspace.materials.readManifest(dependency.resourceId, dependency.revisionId);
|
|
43
53
|
}
|
|
44
54
|
catch (error) {
|
|
45
55
|
throw new Error(`Locked project material unavailable: ${dependency.resourceId} at ${dependency.revisionId}`, { cause: error });
|
|
46
56
|
}
|
|
47
|
-
|
|
57
|
+
addRoot(manifest);
|
|
48
58
|
}
|
|
49
|
-
for (const asset of assets)
|
|
50
|
-
for (const
|
|
51
|
-
|
|
59
|
+
for (const asset of assets) {
|
|
60
|
+
for (const manifest of dependencyManifests(asset.record)) {
|
|
61
|
+
addRoot(manifest);
|
|
62
|
+
assetRoots.add(`${manifest.materialId}/${manifest.revisionId}`);
|
|
63
|
+
}
|
|
64
|
+
for (const wire of asset.record.materialResources?.records ?? []) {
|
|
65
|
+
const manifest = validateMaterialManifestIdentityAny(wire.manifest);
|
|
66
|
+
const key = `${manifest.materialId}/${manifest.revisionId}`;
|
|
67
|
+
const previous = embedded.get(key);
|
|
68
|
+
if (previous &&
|
|
69
|
+
(canonicalMaterialJson(previous.manifest) !== canonicalMaterialJson(manifest) ||
|
|
70
|
+
canonicalMaterialJson(previous.files) !== canonicalMaterialJson(wire.files)))
|
|
71
|
+
throw new Error('Conflicting embedded material identity');
|
|
72
|
+
embedded.set(key, { manifest, files: wire.files });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const lockedRead = async (id, revision, read) => {
|
|
76
|
+
try {
|
|
77
|
+
return await read();
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw new Error(`Locked ${assetRoots.has(`${id}/${revision}`) ? 'asset' : 'project'} material unavailable: ${id} at ${revision}`, { cause: error });
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
materials = await resolveMaterialRecordClosure([...roots.values()], {
|
|
84
|
+
readManifest: async (id, revision) => embedded.get(`${id}/${revision}`)?.manifest ??
|
|
85
|
+
lockedRead(id, revision, () => workspace.materials.readManifest(id, revision)),
|
|
86
|
+
read: async (id, revision) => {
|
|
87
|
+
const wire = embedded.get(`${id}/${revision}`);
|
|
88
|
+
if (!wire)
|
|
89
|
+
return lockedRead(id, revision, () => workspace.materials.readRecordFiles(id, revision));
|
|
90
|
+
return {
|
|
91
|
+
manifest: wire.manifest,
|
|
92
|
+
files: Object.fromEntries(Object.entries(wire.files).map(([name, value]) => [
|
|
93
|
+
name,
|
|
94
|
+
new Uint8Array(Buffer.from(value, 'base64')),
|
|
95
|
+
])),
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
}, {
|
|
99
|
+
maxRecords: 1024,
|
|
100
|
+
maxBytes: PROJECT_BUNDLE_LIMITS.maxBytes,
|
|
101
|
+
maxPixels: 1024 * MATERIAL_LIBRARY_LIMITS.maxRecordPixels,
|
|
102
|
+
});
|
|
52
103
|
}
|
|
53
|
-
return encodeProjectBundle({ project, assets, materials
|
|
104
|
+
return encodeProjectBundle({ project, assets, materials }, profile);
|
|
54
105
|
}
|
|
55
106
|
async function retainArchive(workspace, bytes, digest) {
|
|
56
107
|
const root = resolve(workspace.root);
|
|
@@ -117,7 +168,7 @@ export async function importWorkspaceProjectBundle(workspace, library, bytes, op
|
|
|
117
168
|
record,
|
|
118
169
|
]));
|
|
119
170
|
for (const record of decoded.materials)
|
|
120
|
-
await
|
|
171
|
+
await verifyMaterialRecord(record, { materials: decoded.materials });
|
|
121
172
|
for (const asset of decoded.assets) {
|
|
122
173
|
await verifyAssetRecord(asset.record);
|
|
123
174
|
for (const manifest of dependencyManifests(asset.record)) {
|
|
@@ -166,8 +217,14 @@ export async function importWorkspaceProjectBundle(workspace, library, bytes, op
|
|
|
166
217
|
// Nothing above performs writes. Verified immutable resources can safely survive a
|
|
167
218
|
// later disk failure or project-ID conflict; imports never overwrite other revisions.
|
|
168
219
|
await retainArchive(workspace, snapshot, digest);
|
|
169
|
-
|
|
170
|
-
|
|
220
|
+
const newMaterialContext = decoded.materials.some((record) => record.manifest.schemaVersion === 2);
|
|
221
|
+
for (let i = 0; i < decoded.materials.length; i += 100) {
|
|
222
|
+
const batch = decoded.materials.slice(i, i + 100);
|
|
223
|
+
if (newMaterialContext)
|
|
224
|
+
await workspace.materials.import(batch, { materials: decoded.materials });
|
|
225
|
+
else
|
|
226
|
+
await workspace.materials.import(batch);
|
|
227
|
+
}
|
|
171
228
|
const records = [...destinationRecords.values()];
|
|
172
229
|
for (let i = 0; i < records.length; i += 100)
|
|
173
230
|
await library.import(options.collectionId, records.slice(i, i + 100));
|
package/lib/project-bundle.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { type AssetRecord } from "./assets.js";
|
|
4
4
|
import { type ProjectRevision } from "./projects.js";
|
|
5
|
-
import { type
|
|
5
|
+
import { type MaterialRecord } from "./material-library.js";
|
|
6
6
|
export declare const PROJECT_BUNDLE_LIMITS: Readonly<{
|
|
7
7
|
maxBytes: number;
|
|
8
8
|
maxArchiveBytes: number;
|
|
@@ -38,7 +38,95 @@ export declare const projectBundleManifestSchema: z.ZodObject<{
|
|
|
38
38
|
bytes: z.ZodNumber;
|
|
39
39
|
}, z.core.$strict>>;
|
|
40
40
|
}, z.core.$strict>;
|
|
41
|
-
export
|
|
41
|
+
export declare const projectBundleManifestV2Schema: z.ZodObject<{
|
|
42
|
+
profile: z.ZodEnum<{
|
|
43
|
+
editable: "editable";
|
|
44
|
+
runtime: "runtime";
|
|
45
|
+
}>;
|
|
46
|
+
editable: z.ZodBoolean;
|
|
47
|
+
resourceClosure: z.ZodEnum<{
|
|
48
|
+
"embedded-runtime-only": "embedded-runtime-only";
|
|
49
|
+
"normalized-maps-and-procedural-recipes": "normalized-maps-and-procedural-recipes";
|
|
50
|
+
}>;
|
|
51
|
+
referenceMediaIncluded: z.ZodLiteral<false>;
|
|
52
|
+
originalAcquisitionFilesIncluded: z.ZodLiteral<false>;
|
|
53
|
+
assets: z.ZodArray<z.ZodObject<{
|
|
54
|
+
inventoryId: z.ZodString;
|
|
55
|
+
collectionId: z.ZodString;
|
|
56
|
+
assetId: z.ZodString;
|
|
57
|
+
revisionId: z.ZodString;
|
|
58
|
+
canonicalGlbSha256: z.ZodString;
|
|
59
|
+
}, z.core.$strict>>;
|
|
60
|
+
materials: z.ZodArray<z.ZodObject<{
|
|
61
|
+
materialId: z.ZodString;
|
|
62
|
+
revisionId: z.ZodString;
|
|
63
|
+
}, z.core.$strict>>;
|
|
64
|
+
files: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
65
|
+
sha256: z.ZodString;
|
|
66
|
+
bytes: z.ZodNumber;
|
|
67
|
+
}, z.core.$strict>>;
|
|
68
|
+
version: z.ZodLiteral<"kiln.project-bundle.v2">;
|
|
69
|
+
}, z.core.$strict>;
|
|
70
|
+
export declare const projectBundleManifestAnySchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
71
|
+
version: z.ZodLiteral<"kiln.project-bundle.v1">;
|
|
72
|
+
profile: z.ZodEnum<{
|
|
73
|
+
editable: "editable";
|
|
74
|
+
runtime: "runtime";
|
|
75
|
+
}>;
|
|
76
|
+
editable: z.ZodBoolean;
|
|
77
|
+
resourceClosure: z.ZodEnum<{
|
|
78
|
+
"embedded-runtime-only": "embedded-runtime-only";
|
|
79
|
+
"normalized-maps-and-procedural-recipes": "normalized-maps-and-procedural-recipes";
|
|
80
|
+
}>;
|
|
81
|
+
referenceMediaIncluded: z.ZodLiteral<false>;
|
|
82
|
+
originalAcquisitionFilesIncluded: z.ZodLiteral<false>;
|
|
83
|
+
assets: z.ZodArray<z.ZodObject<{
|
|
84
|
+
inventoryId: z.ZodString;
|
|
85
|
+
collectionId: z.ZodString;
|
|
86
|
+
assetId: z.ZodString;
|
|
87
|
+
revisionId: z.ZodString;
|
|
88
|
+
canonicalGlbSha256: z.ZodString;
|
|
89
|
+
}, z.core.$strict>>;
|
|
90
|
+
materials: z.ZodArray<z.ZodObject<{
|
|
91
|
+
materialId: z.ZodString;
|
|
92
|
+
revisionId: z.ZodString;
|
|
93
|
+
}, z.core.$strict>>;
|
|
94
|
+
files: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
95
|
+
sha256: z.ZodString;
|
|
96
|
+
bytes: z.ZodNumber;
|
|
97
|
+
}, z.core.$strict>>;
|
|
98
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
99
|
+
profile: z.ZodEnum<{
|
|
100
|
+
editable: "editable";
|
|
101
|
+
runtime: "runtime";
|
|
102
|
+
}>;
|
|
103
|
+
editable: z.ZodBoolean;
|
|
104
|
+
resourceClosure: z.ZodEnum<{
|
|
105
|
+
"embedded-runtime-only": "embedded-runtime-only";
|
|
106
|
+
"normalized-maps-and-procedural-recipes": "normalized-maps-and-procedural-recipes";
|
|
107
|
+
}>;
|
|
108
|
+
referenceMediaIncluded: z.ZodLiteral<false>;
|
|
109
|
+
originalAcquisitionFilesIncluded: z.ZodLiteral<false>;
|
|
110
|
+
assets: z.ZodArray<z.ZodObject<{
|
|
111
|
+
inventoryId: z.ZodString;
|
|
112
|
+
collectionId: z.ZodString;
|
|
113
|
+
assetId: z.ZodString;
|
|
114
|
+
revisionId: z.ZodString;
|
|
115
|
+
canonicalGlbSha256: z.ZodString;
|
|
116
|
+
}, z.core.$strict>>;
|
|
117
|
+
materials: z.ZodArray<z.ZodObject<{
|
|
118
|
+
materialId: z.ZodString;
|
|
119
|
+
revisionId: z.ZodString;
|
|
120
|
+
}, z.core.$strict>>;
|
|
121
|
+
files: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
122
|
+
sha256: z.ZodString;
|
|
123
|
+
bytes: z.ZodNumber;
|
|
124
|
+
}, z.core.$strict>>;
|
|
125
|
+
version: z.ZodLiteral<"kiln.project-bundle.v2">;
|
|
126
|
+
}, z.core.$strict>]>;
|
|
127
|
+
export type ProjectBundleManifestV1 = z.infer<typeof projectBundleManifestSchema>;
|
|
128
|
+
export type ProjectBundleManifestV2 = z.infer<typeof projectBundleManifestV2Schema>;
|
|
129
|
+
export type ProjectBundleManifest = ProjectBundleManifestV1 | ProjectBundleManifestV2;
|
|
42
130
|
export interface ProjectBundleAsset {
|
|
43
131
|
inventoryId: string;
|
|
44
132
|
collectionId: string;
|
|
@@ -46,7 +134,7 @@ export interface ProjectBundleAsset {
|
|
|
46
134
|
}
|
|
47
135
|
export interface ProjectBundleInput {
|
|
48
136
|
project: ProjectRevision;
|
|
49
|
-
materials:
|
|
137
|
+
materials: MaterialRecord[];
|
|
50
138
|
assets: ProjectBundleAsset[];
|
|
51
139
|
}
|
|
52
140
|
export interface DecodedProjectBundle extends ProjectBundleInput {
|
package/lib/project-bundle.js
CHANGED
|
@@ -4,7 +4,7 @@ import { zipSync, unzipSync } from 'three/addons/libs/fflate.module.js';
|
|
|
4
4
|
import { assetIdSchema, assetManifestSchema, validateRecordShape, validateAssetGlb, } from "./assets.js";
|
|
5
5
|
import { exportAssetGlb } from "./asset-export.js";
|
|
6
6
|
import { MAX_PROJECT_REVISIONS, projectRevisionSchema } from "./projects.js";
|
|
7
|
-
import { canonicalMaterialJson, materialManifestSchema,
|
|
7
|
+
import { canonicalMaterialJson, materialManifestSchema, materialManifestAnySchema, materialRecordDependencyPins, validateMaterialRecordShapeAny, } from "./material-library.js";
|
|
8
8
|
export const PROJECT_BUNDLE_LIMITS = Object.freeze({
|
|
9
9
|
maxBytes: 256 * 1024 * 1024,
|
|
10
10
|
maxArchiveBytes: 264 * 1024 * 1024,
|
|
@@ -49,6 +49,13 @@ export const projectBundleManifestSchema = z
|
|
|
49
49
|
.strict()),
|
|
50
50
|
})
|
|
51
51
|
.strict();
|
|
52
|
+
export const projectBundleManifestV2Schema = projectBundleManifestSchema.extend({
|
|
53
|
+
version: z.literal('kiln.project-bundle.v2'),
|
|
54
|
+
});
|
|
55
|
+
export const projectBundleManifestAnySchema = z.union([
|
|
56
|
+
projectBundleManifestSchema,
|
|
57
|
+
projectBundleManifestV2Schema,
|
|
58
|
+
]);
|
|
52
59
|
const encoder = new TextEncoder();
|
|
53
60
|
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
54
61
|
const bytesOf = (value) => encoder.encode(JSON.stringify(value));
|
|
@@ -77,7 +84,7 @@ async function verifyAsset(record) {
|
|
|
77
84
|
throw new Error('Asset file hash mismatch');
|
|
78
85
|
}
|
|
79
86
|
async function verifyMaterial(record) {
|
|
80
|
-
const manifest =
|
|
87
|
+
const manifest = validateMaterialRecordShapeAny(record);
|
|
81
88
|
const { revisionId, ...payload } = manifest;
|
|
82
89
|
if ((await projectBundleHash(encoder.encode(canonicalMaterialJson(payload)))) !== revisionId)
|
|
83
90
|
throw new Error('Material revision hash mismatch');
|
|
@@ -100,13 +107,42 @@ function assertAssetSelection(project, entries) {
|
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
109
|
function assertMaterialClosure(project, records) {
|
|
103
|
-
const
|
|
110
|
+
const manifests = records.map((record) => validateMaterialRecordShapeAny(record));
|
|
111
|
+
const identities = manifests.map((manifest) => `${manifest.materialId}/${manifest.revisionId}`);
|
|
104
112
|
if (new Set(identities).size !== identities.length)
|
|
105
113
|
throw new Error('Duplicate material revision');
|
|
106
114
|
for (const dependency of project.materialDependencies)
|
|
107
115
|
if (dependency.sha256 !== dependency.revisionId ||
|
|
108
116
|
!identities.includes(`${dependency.resourceId}/${dependency.revisionId}`))
|
|
109
117
|
throw new Error(`Missing locked material: ${dependency.resourceId}`);
|
|
118
|
+
const byKey = new Map(manifests.map((manifest) => [`${manifest.materialId}/${manifest.revisionId}`, manifest]));
|
|
119
|
+
const visited = new Set();
|
|
120
|
+
for (const key of identities) {
|
|
121
|
+
const active = new Set();
|
|
122
|
+
const stack = [{ key, exit: false }];
|
|
123
|
+
while (stack.length) {
|
|
124
|
+
const frame = stack.pop();
|
|
125
|
+
if (frame.exit) {
|
|
126
|
+
active.delete(frame.key);
|
|
127
|
+
visited.add(frame.key);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (active.has(frame.key))
|
|
131
|
+
throw new Error('Cyclic project material closure');
|
|
132
|
+
if (visited.has(frame.key))
|
|
133
|
+
continue;
|
|
134
|
+
const manifest = byKey.get(frame.key);
|
|
135
|
+
if (!manifest)
|
|
136
|
+
throw new Error('Missing transitive project material');
|
|
137
|
+
active.add(frame.key);
|
|
138
|
+
stack.push({ key: frame.key, exit: true });
|
|
139
|
+
for (const pin of materialRecordDependencyPins(manifest)) {
|
|
140
|
+
if (pin.sha256 !== pin.revisionId)
|
|
141
|
+
throw new Error('Transitive material lock hash mismatch');
|
|
142
|
+
stack.push({ key: `${pin.resourceId}/${pin.revisionId}`, exit: false });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
110
146
|
}
|
|
111
147
|
/** Editable includes normalized rebuild resources; runtime intentionally excludes source/recipes. */
|
|
112
148
|
export async function encodeProjectBundle(input, profile = 'editable') {
|
|
@@ -175,8 +211,11 @@ export async function encodeProjectBundle(input, profile = 'editable') {
|
|
|
175
211
|
add(prefix + name, bytes);
|
|
176
212
|
}
|
|
177
213
|
}
|
|
178
|
-
const
|
|
179
|
-
|
|
214
|
+
const version = profile === 'editable' && input.materials.some((record) => record.manifest.schemaVersion === 2)
|
|
215
|
+
? 'kiln.project-bundle.v2'
|
|
216
|
+
: 'kiln.project-bundle.v1';
|
|
217
|
+
const manifest = projectBundleManifestAnySchema.parse({
|
|
218
|
+
version,
|
|
180
219
|
profile,
|
|
181
220
|
editable: profile === 'editable',
|
|
182
221
|
resourceClosure: profile === 'editable' ? 'normalized-maps-and-procedural-recipes' : 'embedded-runtime-only',
|
|
@@ -232,7 +271,7 @@ export async function decodeProjectBundle(bytes) {
|
|
|
232
271
|
}
|
|
233
272
|
return value;
|
|
234
273
|
};
|
|
235
|
-
const manifest =
|
|
274
|
+
const manifest = projectBundleManifestAnySchema.parse(json('bundle.json', PROJECT_BUNDLE_LIMITS.maxManifestBytes));
|
|
236
275
|
if (manifest.editable !== (manifest.profile === 'editable') ||
|
|
237
276
|
manifest.resourceClosure !==
|
|
238
277
|
(manifest.editable ? 'normalized-maps-and-procedural-recipes' : 'embedded-runtime-only'))
|
|
@@ -298,7 +337,9 @@ export async function decodeProjectBundle(bytes) {
|
|
|
298
337
|
throw new Error('Runtime profile contains editable material records');
|
|
299
338
|
for (const identity of manifest.materials) {
|
|
300
339
|
const prefix = materialPrefix(identity);
|
|
301
|
-
const materialManifest =
|
|
340
|
+
const materialManifest = (manifest.version === 'kiln.project-bundle.v1'
|
|
341
|
+
? materialManifestSchema
|
|
342
|
+
: materialManifestAnySchema).parse(json(`${prefix}manifest.json`, 256 * 1024));
|
|
302
343
|
used.add(`${prefix}manifest.json`);
|
|
303
344
|
if (materialManifest.materialId !== identity.materialId ||
|
|
304
345
|
materialManifest.revisionId !== identity.revisionId)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { QaFinding } from "./types.js";
|
|
2
|
+
export declare const ATLAS_QA_LIMITS: Readonly<{
|
|
3
|
+
nodes: 4096;
|
|
4
|
+
depth: 128;
|
|
5
|
+
textures: 128;
|
|
6
|
+
bindings: 1024;
|
|
7
|
+
uvVisits: 1048576;
|
|
8
|
+
findings: 64;
|
|
9
|
+
metadataBytes: 4194304;
|
|
10
|
+
}>;
|
|
11
|
+
export interface AtlasStructuralInspectionV1 {
|
|
12
|
+
schemaVersion: 1;
|
|
13
|
+
findings: QaFinding[];
|
|
14
|
+
coverage: 'none' | 'complete' | 'partial';
|
|
15
|
+
texturesChecked: number;
|
|
16
|
+
bindingsChecked: number;
|
|
17
|
+
uvVerticesChecked: number;
|
|
18
|
+
nodesChecked: number;
|
|
19
|
+
truncated: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function inspectAtlasScene(root: unknown): AtlasStructuralInspectionV1;
|