@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
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/** Bounded JSON and identity contract; no pixel allocations or Three.js dependency. */
|
|
2
|
+
import { canonicalizeProceduralTextureSpecV2, ProceduralTextureError, sha256Hex, } from "./procedural-material-v2.js";
|
|
3
|
+
import { TEXTURE_USAGES } from "./texture-contract.js";
|
|
4
|
+
export const PROCEDURAL_TEXTURE_V3_COMPILER_ID = 'kiln.procedural-rgba.v3.1';
|
|
5
|
+
export const PROCEDURAL_TEXTURE_SPEC_VERSION_V3 = 3;
|
|
6
|
+
export const MAX_PROCEDURAL_V3_EDGE = 1024;
|
|
7
|
+
export const MAX_PROCEDURAL_V3_TEXELS = 1_048_576;
|
|
8
|
+
export const MAX_PROCEDURAL_V3_OPERATIONS = 64;
|
|
9
|
+
export const MAX_PROCEDURAL_V3_VERTICES = 64;
|
|
10
|
+
export const MAX_PROCEDURAL_V3_STAMPS = 256;
|
|
11
|
+
export const MAX_PROCEDURAL_V3_PALETTE = 32;
|
|
12
|
+
export const MAX_PROCEDURAL_V3_WORK = 64_000_000;
|
|
13
|
+
export const MAX_PROCEDURAL_V3_TRANSIENT_BYTES = 64 * 1024 * 1024;
|
|
14
|
+
function fail(message) {
|
|
15
|
+
throw new ProceduralTextureError(`proceduralTexture V3: ${message}`, 'PROCEDURAL_TEXTURE_V3_CONTRACT');
|
|
16
|
+
}
|
|
17
|
+
const forbidden = new Set(['__proto__', 'prototype', 'constructor']);
|
|
18
|
+
/** Inspect data descriptors before reading values, including array elements. */
|
|
19
|
+
function assertProceduralJsonV3(input) {
|
|
20
|
+
let nodes = 0;
|
|
21
|
+
const visit = (value, path, depth) => {
|
|
22
|
+
if (++nodes > 16_384 || depth > 10)
|
|
23
|
+
fail(`${path} exceeds the JSON size/depth budget.`);
|
|
24
|
+
if (value === null || typeof value === 'boolean')
|
|
25
|
+
return;
|
|
26
|
+
if (typeof value === 'string') {
|
|
27
|
+
if (value.length > 4096)
|
|
28
|
+
fail(`${path} string is too long.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (typeof value === 'number') {
|
|
32
|
+
if (!Number.isFinite(value))
|
|
33
|
+
fail(`${path} must be finite.`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (typeof value !== 'object')
|
|
37
|
+
fail(`${path} must contain only JSON data.`);
|
|
38
|
+
const array = Array.isArray(value);
|
|
39
|
+
const proto = Object.getPrototypeOf(value);
|
|
40
|
+
if (array ? proto !== Array.prototype : proto !== Object.prototype && proto !== null)
|
|
41
|
+
fail(`${path} must be a plain JSON ${array ? 'array' : 'object'}.`);
|
|
42
|
+
// Reject oversized arrays before enumerating or copying element descriptors.
|
|
43
|
+
const length = array
|
|
44
|
+
? Object.getOwnPropertyDescriptor(value, 'length')?.value
|
|
45
|
+
: undefined;
|
|
46
|
+
if (array && (typeof length !== 'number' || length > 1024))
|
|
47
|
+
fail(`${path} array is too long.`);
|
|
48
|
+
const keys = Reflect.ownKeys(value);
|
|
49
|
+
if (keys.length > 1024)
|
|
50
|
+
fail(`${path} has too many fields.`);
|
|
51
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
52
|
+
for (const key of keys) {
|
|
53
|
+
if (typeof key !== 'string' || forbidden.has(key))
|
|
54
|
+
fail(`${path} has a forbidden key.`);
|
|
55
|
+
if (array && key === 'length')
|
|
56
|
+
continue;
|
|
57
|
+
const descriptor = descriptors[key];
|
|
58
|
+
if (!descriptor.enumerable || !('value' in descriptor))
|
|
59
|
+
fail(`${path}.${key} must be an enumerable JSON data field, never an accessor.`);
|
|
60
|
+
if (array && (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= length))
|
|
61
|
+
fail(`${path} has an invalid array key.`);
|
|
62
|
+
}
|
|
63
|
+
if (array && keys.length !== length + 1)
|
|
64
|
+
fail(`${path} must be a dense JSON array.`);
|
|
65
|
+
for (const key of keys)
|
|
66
|
+
if (key !== 'length' || !array)
|
|
67
|
+
visit(descriptors[key].value, `${path}.${String(key)}`, depth + 1);
|
|
68
|
+
};
|
|
69
|
+
visit(input, 'recipe', 0);
|
|
70
|
+
}
|
|
71
|
+
function record(value, allowed, path) {
|
|
72
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
73
|
+
fail(`${path} must be a JSON object.`);
|
|
74
|
+
const result = value;
|
|
75
|
+
for (const key of Object.keys(result))
|
|
76
|
+
if (!allowed.includes(key))
|
|
77
|
+
fail(`${path} has unknown field ${key}.`);
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
function integer(value, min, max, path, fallback) {
|
|
81
|
+
if (value === undefined && fallback !== undefined)
|
|
82
|
+
return fallback;
|
|
83
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < min || value > max)
|
|
84
|
+
fail(`${path} must be an integer ${min}..${max}.`);
|
|
85
|
+
return value === 0 ? 0 : value;
|
|
86
|
+
}
|
|
87
|
+
function boolean(value, path) {
|
|
88
|
+
if (value === undefined)
|
|
89
|
+
return false;
|
|
90
|
+
if (typeof value !== 'boolean')
|
|
91
|
+
fail(`${path} must be boolean.`);
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
function tuple(value, length, path, min, max) {
|
|
95
|
+
if (!Array.isArray(value) || value.length !== length)
|
|
96
|
+
fail(`${path} must be a ${length}-element array.`);
|
|
97
|
+
return value.map((v, i) => integer(v, min, max, `${path}[${i}]`));
|
|
98
|
+
}
|
|
99
|
+
function rgba(value, path) {
|
|
100
|
+
const c = tuple(value, 4, path, 0, 255);
|
|
101
|
+
return c[3] === 0 ? [0, 0, 0, 0] : c;
|
|
102
|
+
}
|
|
103
|
+
function rectangle(value, path, kind) {
|
|
104
|
+
const r = record(value, ['x', 'y', 'width', 'height', ...(kind ? ['kind'] : [])], path);
|
|
105
|
+
return {
|
|
106
|
+
x: integer(r['x'], -4096, 4096, `${path}.x`),
|
|
107
|
+
y: integer(r['y'], -4096, 4096, `${path}.y`),
|
|
108
|
+
width: integer(r['width'], 1, 2048, `${path}.width`),
|
|
109
|
+
height: integer(r['height'], 1, 2048, `${path}.height`),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function shape(value, path) {
|
|
113
|
+
const r = record(value, ['kind', 'x', 'y', 'width', 'height', 'from', 'to', 'points'], path);
|
|
114
|
+
if (r['kind'] === 'rectangle' || r['kind'] === 'ellipse')
|
|
115
|
+
return { kind: r['kind'], ...rectangle(r, path, r['kind']) };
|
|
116
|
+
if (r['kind'] === 'polygon') {
|
|
117
|
+
record(r, ['kind', 'points'], path);
|
|
118
|
+
const points = r['points'];
|
|
119
|
+
if (!Array.isArray(points) || points.length < 3 || points.length > MAX_PROCEDURAL_V3_VERTICES)
|
|
120
|
+
fail(`${path}.points requires 3..${MAX_PROCEDURAL_V3_VERTICES} vertices.`);
|
|
121
|
+
return {
|
|
122
|
+
kind: 'polygon',
|
|
123
|
+
points: points.map((p, i) => tuple(p, 2, `${path}.points[${i}]`, -4096, 4096)),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (r['kind'] === 'line') {
|
|
127
|
+
record(r, ['kind', 'from', 'to', 'width'], path);
|
|
128
|
+
return {
|
|
129
|
+
kind: 'line',
|
|
130
|
+
from: tuple(r['from'], 2, `${path}.from`, -4096, 4096),
|
|
131
|
+
to: tuple(r['to'], 2, `${path}.to`, -4096, 4096),
|
|
132
|
+
width: integer(r['width'], 1, 2048, `${path}.width`),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return fail(`${path}.kind must be rectangle, ellipse, polygon or line.`);
|
|
136
|
+
}
|
|
137
|
+
export function proceduralShapeBoundsV3(s) {
|
|
138
|
+
if (s.kind === 'rectangle' || s.kind === 'ellipse')
|
|
139
|
+
return [s.x, s.y, s.x + s.width, s.y + s.height];
|
|
140
|
+
const points = s.kind === 'polygon' ? s.points : [s.from, s.to];
|
|
141
|
+
const radius = s.kind === 'line' ? s.width / 2 : 0;
|
|
142
|
+
return [
|
|
143
|
+
Math.min(...points.map((p) => p[0])) - radius,
|
|
144
|
+
Math.min(...points.map((p) => p[1])) - radius,
|
|
145
|
+
Math.max(...points.map((p) => p[0])) + radius,
|
|
146
|
+
Math.max(...points.map((p) => p[1])) + radius,
|
|
147
|
+
];
|
|
148
|
+
}
|
|
149
|
+
export function proceduralShapeCostV3(s) {
|
|
150
|
+
return s.kind === 'polygon'
|
|
151
|
+
? s.points.length + 1
|
|
152
|
+
: s.kind === 'line'
|
|
153
|
+
? 4
|
|
154
|
+
: s.kind === 'ellipse'
|
|
155
|
+
? 3
|
|
156
|
+
: 1;
|
|
157
|
+
}
|
|
158
|
+
export function proceduralClippedBoundsV3(s, width, height, dx = 0, dy = 0) {
|
|
159
|
+
const [x0, y0, x1, y1] = proceduralShapeBoundsV3(s);
|
|
160
|
+
return [
|
|
161
|
+
Math.max(0, Math.ceil(x0 + dx - 0.5)),
|
|
162
|
+
Math.max(0, Math.ceil(y0 + dy - 0.5)),
|
|
163
|
+
Math.min(width, Math.floor(x1 + dx - 0.5) + 1),
|
|
164
|
+
Math.min(height, Math.floor(y1 + dy - 0.5) + 1),
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
/** Owns the single validation/accounting pass shared by standalone and atlas compilation. */
|
|
168
|
+
export function admitProceduralTextureSpecV3(input) {
|
|
169
|
+
assertProceduralJsonV3(input);
|
|
170
|
+
const r = record(input, ['schemaVersion', 'width', 'height', 'usage', 'name', 'background', 'operations', 'palette'], 'recipe');
|
|
171
|
+
if (r['schemaVersion'] !== 3)
|
|
172
|
+
fail('schemaVersion must be 3.');
|
|
173
|
+
const width = integer(r['width'], 4, MAX_PROCEDURAL_V3_EDGE, 'width');
|
|
174
|
+
const height = integer(r['height'], 4, MAX_PROCEDURAL_V3_EDGE, 'height');
|
|
175
|
+
const texels = width * height;
|
|
176
|
+
if (texels > MAX_PROCEDURAL_V3_TEXELS)
|
|
177
|
+
fail('output texel budget exceeded.');
|
|
178
|
+
const usage = r['usage'] === undefined ? 'albedo' : r['usage'];
|
|
179
|
+
if (!TEXTURE_USAGES.includes(usage))
|
|
180
|
+
fail('usage must be a known texture usage.');
|
|
181
|
+
const colorUsage = usage === 'albedo' || usage === 'emissive';
|
|
182
|
+
const name = r['name'];
|
|
183
|
+
if (name !== undefined &&
|
|
184
|
+
(typeof name !== 'string' ||
|
|
185
|
+
!name.length ||
|
|
186
|
+
name.length > 80 ||
|
|
187
|
+
[...name].some((c) => c.charCodeAt(0) < 32 || c.charCodeAt(0) === 127)))
|
|
188
|
+
fail('name must be 1..80 visible characters.');
|
|
189
|
+
const rawOps = r['operations'];
|
|
190
|
+
if (!Array.isArray(rawOps) || rawOps.length > MAX_PROCEDURAL_V3_OPERATIONS)
|
|
191
|
+
fail(`operations must be an array with at most ${MAX_PROCEDURAL_V3_OPERATIONS} entries.`);
|
|
192
|
+
let work = texels;
|
|
193
|
+
let expanded = 0;
|
|
194
|
+
let legacyTransientBytes = 0;
|
|
195
|
+
const operations = rawOps.map((value, i) => {
|
|
196
|
+
const path = `operations[${i}]`;
|
|
197
|
+
const op = record(value, [
|
|
198
|
+
'op',
|
|
199
|
+
'shape',
|
|
200
|
+
'rgba',
|
|
201
|
+
'opacity',
|
|
202
|
+
'alpha',
|
|
203
|
+
'invert',
|
|
204
|
+
'spec',
|
|
205
|
+
'count',
|
|
206
|
+
'seed',
|
|
207
|
+
'area',
|
|
208
|
+
'periodic',
|
|
209
|
+
], path);
|
|
210
|
+
if (op['op'] === 'legacy-v2') {
|
|
211
|
+
record(op, ['op', 'spec', 'alpha', 'opacity'], path);
|
|
212
|
+
const legacyRecord = record(op['spec'], ['schemaVersion', 'size', 'usage', 'name', 'layers'], `${path}.spec`);
|
|
213
|
+
if (legacyRecord['schemaVersion'] !== 2)
|
|
214
|
+
fail(`${path}.spec requires explicit schemaVersion 2.`);
|
|
215
|
+
const spec = canonicalizeProceduralTextureSpecV2(legacyRecord);
|
|
216
|
+
if (spec.size !== width || spec.size !== height || spec.usage !== usage)
|
|
217
|
+
fail(`${path} legacy-v2 dimensions and usage must match the V3 surface.`);
|
|
218
|
+
work +=
|
|
219
|
+
texels *
|
|
220
|
+
(1 +
|
|
221
|
+
spec.layers.reduce((cost, layer) => cost + (layer.op === 'noise' ? layer.octaves * 16 : 4), 0));
|
|
222
|
+
// Count every legacy result conservatively, even if GC retains prior buffers.
|
|
223
|
+
legacyTransientBytes += texels * 4;
|
|
224
|
+
return {
|
|
225
|
+
op: 'legacy-v2',
|
|
226
|
+
spec,
|
|
227
|
+
alpha: integer(op['alpha'], 0, 255, `${path}.alpha`, 255),
|
|
228
|
+
opacity: integer(op['opacity'], 0, 255, `${path}.opacity`, 255),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const s = shape(op['shape'], `${path}.shape`);
|
|
232
|
+
const cost = proceduralShapeCostV3(s);
|
|
233
|
+
const [x0, y0, x1, y1] = proceduralClippedBoundsV3(s, width, height);
|
|
234
|
+
const area = Math.max(0, x1 - x0) * Math.max(0, y1 - y0);
|
|
235
|
+
if (op['op'] === 'mask') {
|
|
236
|
+
record(op, ['op', 'shape', 'alpha', 'invert'], path);
|
|
237
|
+
work += texels + area * cost;
|
|
238
|
+
return {
|
|
239
|
+
op: 'mask',
|
|
240
|
+
shape: s,
|
|
241
|
+
alpha: integer(op['alpha'], 0, 255, `${path}.alpha`, 255),
|
|
242
|
+
invert: boolean(op['invert'], `${path}.invert`),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (op['op'] !== 'paint' && op['op'] !== 'stamps')
|
|
246
|
+
return fail(`${path}.op must be paint, mask, stamps or legacy-v2.`);
|
|
247
|
+
const paint = {
|
|
248
|
+
shape: s,
|
|
249
|
+
rgba: rgba(op['rgba'], `${path}.rgba`),
|
|
250
|
+
opacity: integer(op['opacity'], 0, 255, `${path}.opacity`, 255),
|
|
251
|
+
};
|
|
252
|
+
if (op['op'] === 'paint') {
|
|
253
|
+
record(op, ['op', 'shape', 'rgba', 'opacity'], path);
|
|
254
|
+
work += area * (cost + 1);
|
|
255
|
+
return { op: 'paint', ...paint };
|
|
256
|
+
}
|
|
257
|
+
record(op, ['op', 'shape', 'rgba', 'opacity', 'count', 'seed', 'area', 'periodic'], path);
|
|
258
|
+
if (!colorUsage)
|
|
259
|
+
fail('stamps are supported only for color usages albedo and emissive.');
|
|
260
|
+
const count = integer(op['count'], 1, MAX_PROCEDURAL_V3_STAMPS, `${path}.count`);
|
|
261
|
+
const periodic = boolean(op['periodic'], `${path}.periodic`);
|
|
262
|
+
const [bx0, by0, bx1, by1] = proceduralShapeBoundsV3(s);
|
|
263
|
+
if (periodic && (bx1 - bx0 > width || by1 - by0 > height))
|
|
264
|
+
fail(`${path} periodic shape bounds must fit one surface period.`);
|
|
265
|
+
expanded += count * (periodic ? 9 : 1);
|
|
266
|
+
if (expanded > MAX_PROCEDURAL_V3_STAMPS)
|
|
267
|
+
fail(`expanded stamps budget exceeds ${MAX_PROCEDURAL_V3_STAMPS}.`);
|
|
268
|
+
// Conservative per-copy bounds, independent of generated positions.
|
|
269
|
+
work +=
|
|
270
|
+
count *
|
|
271
|
+
(periodic ? 9 : 1) *
|
|
272
|
+
Math.min(width, Math.ceil(bx1 - bx0) + 1) *
|
|
273
|
+
Math.min(height, Math.ceil(by1 - by0) + 1) *
|
|
274
|
+
(cost + 1);
|
|
275
|
+
return {
|
|
276
|
+
op: 'stamps',
|
|
277
|
+
...paint,
|
|
278
|
+
count,
|
|
279
|
+
seed: integer(op['seed'], 0, 0xffffffff, `${path}.seed`),
|
|
280
|
+
area: rectangle(op['area'], `${path}.area`),
|
|
281
|
+
periodic,
|
|
282
|
+
};
|
|
283
|
+
});
|
|
284
|
+
let palette;
|
|
285
|
+
if (r['palette'] !== undefined) {
|
|
286
|
+
if (!colorUsage)
|
|
287
|
+
fail('palette and dither are supported only for color usages albedo and emissive.');
|
|
288
|
+
const p = record(r['palette'], ['colors', 'dither'], 'palette');
|
|
289
|
+
const colors = p['colors'];
|
|
290
|
+
if (!Array.isArray(colors) || colors.length < 1 || colors.length > MAX_PROCEDURAL_V3_PALETTE)
|
|
291
|
+
fail(`palette.colors requires 1..${MAX_PROCEDURAL_V3_PALETTE} entries.`);
|
|
292
|
+
const dither = p['dither'] === undefined ? 'none' : p['dither'];
|
|
293
|
+
if (dither !== 'none' && dither !== 'bayer4')
|
|
294
|
+
fail('palette.dither must be none or bayer4.');
|
|
295
|
+
palette = {
|
|
296
|
+
colors: colors.map((c, i) => integer(c, 0, 0xffffff, `palette.colors[${i}]`)),
|
|
297
|
+
dither,
|
|
298
|
+
};
|
|
299
|
+
work += texels * (colors.length + 1);
|
|
300
|
+
}
|
|
301
|
+
if (work > MAX_PROCEDURAL_V3_WORK)
|
|
302
|
+
fail(`estimated work budget ${work} exceeds ${MAX_PROCEDURAL_V3_WORK}.`);
|
|
303
|
+
if (texels * 4 + legacyTransientBytes > MAX_PROCEDURAL_V3_TRANSIENT_BYTES)
|
|
304
|
+
fail('transient allocation budget exceeded.');
|
|
305
|
+
const result = {
|
|
306
|
+
schemaVersion: 3,
|
|
307
|
+
width,
|
|
308
|
+
height,
|
|
309
|
+
usage: usage,
|
|
310
|
+
...(name !== undefined ? { name: name } : {}),
|
|
311
|
+
background: r['background'] === undefined ? [0, 0, 0, 0] : rgba(r['background'], 'background'),
|
|
312
|
+
operations,
|
|
313
|
+
...(palette ? { palette } : {}),
|
|
314
|
+
};
|
|
315
|
+
const canonicalJson = JSON.stringify(result);
|
|
316
|
+
if (canonicalJson.length > 262_144)
|
|
317
|
+
fail('canonical JSON byte budget exceeded.');
|
|
318
|
+
return {
|
|
319
|
+
spec: result,
|
|
320
|
+
canonicalJson,
|
|
321
|
+
estimatedWorkUnits: work,
|
|
322
|
+
conservativePixelAllocationBytes: texels * 4 + legacyTransientBytes,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
export function canonicalizeProceduralTextureSpecV3(input) {
|
|
326
|
+
return admitProceduralTextureSpecV3(input).spec;
|
|
327
|
+
}
|
|
328
|
+
export function canonicalProceduralTextureJsonV3(input) {
|
|
329
|
+
return admitProceduralTextureSpecV3(input).canonicalJson;
|
|
330
|
+
}
|
|
331
|
+
export function hashProceduralTextureSpecV3(input) {
|
|
332
|
+
return `sha256:${sha256Hex(`${PROCEDURAL_TEXTURE_V3_COMPILER_ID}\n${canonicalProceduralTextureJsonV3(input)}`)}`;
|
|
333
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PROCEDURAL_TEXTURE_V3_COMPILER_ID, type CanonicalProceduralTextureSpecV3 } from "./procedural-texture-v3-contract.js";
|
|
2
|
+
export { 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-contract.js";
|
|
3
|
+
export type { RgbaV3, PixelPointV3, PixelRectangleV3, ProceduralShapeV3, ProceduralOperationV3, CanonicalProceduralOperationV3, ProceduralTextureSpecV3, CanonicalProceduralTextureSpecV3, } from "./procedural-texture-v3-contract.js";
|
|
4
|
+
export interface CompiledProceduralTextureV3 {
|
|
5
|
+
spec: CanonicalProceduralTextureSpecV3;
|
|
6
|
+
pixels: Uint8Array;
|
|
7
|
+
canonicalJson: string;
|
|
8
|
+
recipeHash: `sha256:${string}`;
|
|
9
|
+
compilerId: typeof PROCEDURAL_TEXTURE_V3_COMPILER_ID;
|
|
10
|
+
}
|
|
11
|
+
/** Preflight validates all nested values and total worst-case work before allocation. */
|
|
12
|
+
export declare function compileProceduralTextureSpecV3(input: unknown): CompiledProceduralTextureV3;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/** Deterministic binary coverage and straight RGBA source-over compiler. */
|
|
2
|
+
import { compileProceduralTextureSpecV2 } from "./procedural-texture-v2-compiler.js";
|
|
3
|
+
import { canonicalizeProceduralTextureSpecV3, canonicalProceduralTextureJsonV3, hashProceduralTextureSpecV3, PROCEDURAL_TEXTURE_V3_COMPILER_ID, proceduralClippedBoundsV3, proceduralShapeBoundsV3, } from "./procedural-texture-v3-contract.js";
|
|
4
|
+
export { 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-contract.js";
|
|
5
|
+
const roundRatio = (numerator, denominator) => Math.floor(numerator / denominator + 0.5);
|
|
6
|
+
/** Pixel centers; rectangles half-open, curved and polygon boundaries inclusive. */
|
|
7
|
+
function covers(shape, x, y) {
|
|
8
|
+
const px = x + 0.5;
|
|
9
|
+
const py = y + 0.5;
|
|
10
|
+
if (shape.kind === 'rectangle')
|
|
11
|
+
return (px >= shape.x && px < shape.x + shape.width && py >= shape.y && py < shape.y + shape.height);
|
|
12
|
+
if (shape.kind === 'ellipse') {
|
|
13
|
+
const dx = 2 * px - 2 * shape.x - shape.width;
|
|
14
|
+
const dy = 2 * py - 2 * shape.y - shape.height;
|
|
15
|
+
const w2 = shape.width * shape.width;
|
|
16
|
+
const h2 = shape.height * shape.height;
|
|
17
|
+
return dx * dx * h2 + dy * dy * w2 <= w2 * h2;
|
|
18
|
+
}
|
|
19
|
+
if (shape.kind === 'line') {
|
|
20
|
+
const [ax, ay] = shape.from;
|
|
21
|
+
const dx = shape.to[0] - ax;
|
|
22
|
+
const dy = shape.to[1] - ay;
|
|
23
|
+
const vx = px - ax;
|
|
24
|
+
const vy = py - ay;
|
|
25
|
+
const length2 = dx * dx + dy * dy;
|
|
26
|
+
const dot = vx * dx + vy * dy;
|
|
27
|
+
const radius2 = (shape.width * shape.width) / 4;
|
|
28
|
+
if (dot <= 0 || length2 === 0)
|
|
29
|
+
return vx * vx + vy * vy <= radius2;
|
|
30
|
+
if (dot >= length2)
|
|
31
|
+
return (px - shape.to[0]) ** 2 + (py - shape.to[1]) ** 2 <= radius2;
|
|
32
|
+
const cross = vx * dy - vy * dx;
|
|
33
|
+
return cross * cross <= radius2 * length2;
|
|
34
|
+
}
|
|
35
|
+
let inside = false;
|
|
36
|
+
for (let i = 0, j = shape.points.length - 1; i < shape.points.length; j = i++) {
|
|
37
|
+
const [ax, ay] = shape.points[j];
|
|
38
|
+
const [bx, by] = shape.points[i];
|
|
39
|
+
const cross = (px - ax) * (by - ay) - (py - ay) * (bx - ax);
|
|
40
|
+
if (cross === 0 &&
|
|
41
|
+
px >= Math.min(ax, bx) &&
|
|
42
|
+
px <= Math.max(ax, bx) &&
|
|
43
|
+
py >= Math.min(ay, by) &&
|
|
44
|
+
py <= Math.max(ay, by))
|
|
45
|
+
return true;
|
|
46
|
+
if (ay > py !== by > py && (by > ay ? cross < 0 : cross > 0))
|
|
47
|
+
inside = !inside;
|
|
48
|
+
}
|
|
49
|
+
return inside;
|
|
50
|
+
}
|
|
51
|
+
/** All divisions round half-up. Stored channels are straight, not premultiplied. */
|
|
52
|
+
function sourceOver(pixels, offset, rgba, opacity) {
|
|
53
|
+
const sa = roundRatio(rgba[3] * opacity, 255);
|
|
54
|
+
if (sa === 0)
|
|
55
|
+
return;
|
|
56
|
+
const da = pixels[offset + 3];
|
|
57
|
+
const alphaNumerator = sa * 255 + da * (255 - sa);
|
|
58
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
59
|
+
pixels[offset + channel] = roundRatio(rgba[channel] * sa * 255 + pixels[offset + channel] * da * (255 - sa), alphaNumerator);
|
|
60
|
+
}
|
|
61
|
+
pixels[offset + 3] = roundRatio(alphaNumerator, 255);
|
|
62
|
+
}
|
|
63
|
+
function paint(pixels, width, height, shape, rgba, opacity, dx = 0, dy = 0) {
|
|
64
|
+
const [x0, y0, x1, y1] = proceduralClippedBoundsV3(shape, width, height, dx, dy);
|
|
65
|
+
for (let y = y0; y < y1; y++)
|
|
66
|
+
for (let x = x0; x < x1; x++) {
|
|
67
|
+
if (covers(shape, x - dx, y - dy))
|
|
68
|
+
sourceOver(pixels, (y * width + x) * 4, rgba, opacity);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const BAYER4 = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5];
|
|
72
|
+
function applyPalette(pixels, width, palette) {
|
|
73
|
+
const colors = palette.colors.map((c) => [(c >>> 16) & 255, (c >>> 8) & 255, c & 255]);
|
|
74
|
+
for (let i = 0; i < pixels.length; i += 4) {
|
|
75
|
+
if (pixels[i + 3] === 0)
|
|
76
|
+
continue;
|
|
77
|
+
const index = i / 4;
|
|
78
|
+
const x = index % width;
|
|
79
|
+
const y = Math.floor(index / width);
|
|
80
|
+
const offset = palette.dither === 'bayer4' ? (2 * BAYER4[(y % 4) * 4 + (x % 4)] - 15) * 2 : 0;
|
|
81
|
+
const r = Math.max(0, Math.min(255, pixels[i] + offset));
|
|
82
|
+
const g = Math.max(0, Math.min(255, pixels[i + 1] + offset));
|
|
83
|
+
const b = Math.max(0, Math.min(255, pixels[i + 2] + offset));
|
|
84
|
+
let best = colors[0];
|
|
85
|
+
let distance = Infinity;
|
|
86
|
+
for (const color of colors) {
|
|
87
|
+
const candidate = (r - color[0]) ** 2 + (g - color[1]) ** 2 + (b - color[2]) ** 2;
|
|
88
|
+
if (candidate < distance) {
|
|
89
|
+
distance = candidate;
|
|
90
|
+
best = color;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
pixels[i] = best[0];
|
|
94
|
+
pixels[i + 1] = best[1];
|
|
95
|
+
pixels[i + 2] = best[2];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/** Xorshift32 stream, initialized by a fixed salt; modulo chooses integer offsets. */
|
|
99
|
+
function stampRandom(seed) {
|
|
100
|
+
let state = (seed ^ 0xa3c59ac3) >>> 0;
|
|
101
|
+
if (state === 0)
|
|
102
|
+
state = 0x6d2b79f5;
|
|
103
|
+
return () => {
|
|
104
|
+
state ^= state << 13;
|
|
105
|
+
state ^= state >>> 17;
|
|
106
|
+
state ^= state << 5;
|
|
107
|
+
return state >>> 0;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const wrap = (value, period) => ((value % period) + period) % period;
|
|
111
|
+
/** Preflight validates all nested values and total worst-case work before allocation. */
|
|
112
|
+
export function compileProceduralTextureSpecV3(input) {
|
|
113
|
+
const spec = canonicalizeProceduralTextureSpecV3(input);
|
|
114
|
+
const { width, height } = spec;
|
|
115
|
+
const pixels = new Uint8Array(width * height * 4);
|
|
116
|
+
for (let i = 0; i < pixels.length; i += 4)
|
|
117
|
+
pixels.set(spec.background, i);
|
|
118
|
+
for (const op of spec.operations) {
|
|
119
|
+
if (op.op === 'paint')
|
|
120
|
+
paint(pixels, width, height, op.shape, op.rgba, op.opacity);
|
|
121
|
+
else if (op.op === 'mask') {
|
|
122
|
+
const [x0, y0, x1, y1] = proceduralClippedBoundsV3(op.shape, width, height);
|
|
123
|
+
for (let y = 0; y < height; y++)
|
|
124
|
+
for (let x = 0; x < width; x++) {
|
|
125
|
+
const covered = x >= x0 && x < x1 && y >= y0 && y < y1 && covers(op.shape, x, y);
|
|
126
|
+
const alpha = covered !== op.invert ? op.alpha : 0;
|
|
127
|
+
const offset = (y * width + x) * 4;
|
|
128
|
+
pixels[offset + 3] = roundRatio(pixels[offset + 3] * alpha, 255);
|
|
129
|
+
if (pixels[offset + 3] === 0)
|
|
130
|
+
pixels.fill(0, offset, offset + 3);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (op.op === 'legacy-v2') {
|
|
134
|
+
const legacy = compileProceduralTextureSpecV2(op.spec).pixels;
|
|
135
|
+
for (let i = 0; i < pixels.length; i += 4)
|
|
136
|
+
sourceOver(pixels, i, [legacy[i], legacy[i + 1], legacy[i + 2], op.alpha], op.opacity);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
const random = stampRandom(op.seed);
|
|
140
|
+
const [bx0, by0] = proceduralShapeBoundsV3(op.shape);
|
|
141
|
+
for (let stamp = 0; stamp < op.count; stamp++) {
|
|
142
|
+
let dx = op.area.x + (random() % op.area.width);
|
|
143
|
+
let dy = op.area.y + (random() % op.area.height);
|
|
144
|
+
if (!op.periodic)
|
|
145
|
+
paint(pixels, width, height, op.shape, op.rgba, op.opacity, dx, dy);
|
|
146
|
+
else {
|
|
147
|
+
dx = wrap(dx + bx0, width) - bx0;
|
|
148
|
+
dy = wrap(dy + by0, height) - by0;
|
|
149
|
+
for (const oy of [-height, 0, height])
|
|
150
|
+
for (const ox of [-width, 0, width])
|
|
151
|
+
paint(pixels, width, height, op.shape, op.rgba, op.opacity, dx + ox, dy + oy);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (spec.palette)
|
|
157
|
+
applyPalette(pixels, width, spec.palette);
|
|
158
|
+
return {
|
|
159
|
+
spec,
|
|
160
|
+
pixels,
|
|
161
|
+
canonicalJson: canonicalProceduralTextureJsonV3(spec),
|
|
162
|
+
recipeHash: hashProceduralTextureSpecV3(spec),
|
|
163
|
+
compilerId: PROCEDURAL_TEXTURE_V3_COMPILER_ID,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -37,38 +37,16 @@
|
|
|
37
37
|
* still catches.
|
|
38
38
|
*/
|
|
39
39
|
import * as THREE from 'three';
|
|
40
|
-
import
|
|
40
|
+
import type { ProceduralTextureSpecV3 } from "./procedural-texture-v3.js";
|
|
41
|
+
import { type ProceduralTextureSpec as LegacyProceduralTextureSpec } from "./procedural-material-v2.js";
|
|
41
42
|
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
|
-
export type { CanonicalPortableMaterialSpecV2, CanonicalPortableTextureRefV2, CanonicalProceduralLayerV2, CanonicalProceduralTextureSpecV2, PortableMaterialSpecV2, PortableTextureRefV2, ProceduralBlend, ProceduralLayer,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
* @example
|
|
50
|
-
* const bark = proceduralTexture({
|
|
51
|
-
* schemaVersion: 2,
|
|
52
|
-
* size: 256,
|
|
53
|
-
* usage: 'albedo',
|
|
54
|
-
* name: 'Bark',
|
|
55
|
-
* layers: [
|
|
56
|
-
* { op: 'solid', color: 0x5a4632 },
|
|
57
|
-
* { op: 'noise', colorA: 0x3d2f21, colorB: 0x7a6248, scale: 6, octaves: 4, blend: 'overlay' },
|
|
58
|
-
* { op: 'stripes', colorA: 0x000000, colorB: 0xffffff, count: 24, angleDeg: 90,
|
|
59
|
-
* blend: 'multiply', opacity: 0.15 },
|
|
60
|
-
* ],
|
|
61
|
-
* });
|
|
62
|
-
* const trunk = createPart('Trunk', cylinderGeo(0.3, 0.3, 3), pbrMaterial({ albedo: bark }), { parent: root });
|
|
63
|
-
*/
|
|
64
|
-
export interface CompiledProceduralTextureV2 {
|
|
65
|
-
spec: CanonicalProceduralTextureSpecV2;
|
|
66
|
-
pixels: Uint8Array;
|
|
67
|
-
canonicalJson: string;
|
|
68
|
-
recipeHash: `sha256:${string}`;
|
|
69
|
-
}
|
|
70
|
-
/** Validate, canonicalize, and compile one texture without constructing Three.js state. */
|
|
71
|
-
export declare function compileProceduralTextureSpecV2(input: unknown): CompiledProceduralTextureV2;
|
|
43
|
+
export type { CanonicalPortableMaterialSpecV2, CanonicalPortableTextureRefV2, CanonicalProceduralLayerV2, CanonicalProceduralTextureSpecV2, PortableMaterialSpecV2, PortableTextureRefV2, ProceduralBlend, ProceduralLayer, ProceduralTextureSpecV1, ProceduralTextureSpecV2, } from "./procedural-material-v2.js";
|
|
44
|
+
export * from "./procedural-texture-v3.js";
|
|
45
|
+
export type ProceduralTextureSpec = LegacyProceduralTextureSpec | ProceduralTextureSpecV3;
|
|
46
|
+
/** Dispatch never invokes an input accessor; absent/undefined retains V1 compatibility. */
|
|
47
|
+
export declare function proceduralTextureSchemaVersion(input: unknown): 1 | 2 | 3;
|
|
48
|
+
export { compileProceduralTextureSpecV2 } from "./procedural-texture-v2-compiler.js";
|
|
49
|
+
export type { CompiledProceduralTextureV2 } from "./procedural-texture-v2-compiler.js";
|
|
72
50
|
export declare function proceduralTexture(spec: ProceduralTextureSpec): THREE.DataTexture;
|
|
73
51
|
/**
|
|
74
52
|
* Derive a tangent-space normal map from the luminance of another texture.
|