@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
package/dist/viewer/style.css
CHANGED
|
@@ -49,3 +49,13 @@ main{grid-template-columns:216px minmax(0,1fr);min-height:calc(100vh - 80px)}
|
|
|
49
49
|
@media(max-width:800px){header{height:70px;padding:0 18px}main{display:block}.workspace-sidebar{height:auto;position:static;padding:12px 15px}#workspace-nav{display:flex;gap:5px}#workspace-nav button{white-space:nowrap;font-size:12px;flex:1;justify-content:center;padding:11px 7px}#workspace-nav button span{display:none}#collections{padding-top:6px}#collections:empty{padding:0}.workspace-panel{padding:25px 18px}.workspace-panel h1{font-size:31px}.project-layout{grid-template-columns:1fr;margin-top:23px}.project-list{display:flex;overflow:auto}.project-list button{min-width:180px}.review-layout{grid-template-columns:1fr}.timeline{display:flex;max-height:190px}.timeline-item{min-width:220px;max-width:260px}.timeline .empty{padding:18px}.panel-title{padding:12px 17px}.profile-grid{grid-template-columns:1fr}.review-viewports.comparing{grid-template-columns:1fr}.heading{gap:15px}.heading>button{font-size:12px;white-space:nowrap}.live-toolbar{align-items:flex-start}.live-toolbar .inline-field{flex-direction:column;align-items:flex-start}.local-dot{display:none}.live-stage{height:380px}}
|
|
50
50
|
/* Manual LOD review: whole-asset shortcuts plus independent part choices. */
|
|
51
51
|
.lod-controls{margin:12px 0;font-size:12px}.lod-total{margin:8px 0;color:var(--ink)}.lod-parts{padding:10px 12px;border:1px solid var(--line);border-radius:8px}.lod-parts summary{cursor:pointer;font-weight:600}.lod-parts .subtle{line-height:1.5}.lod-part-fields{display:grid;gap:10px;max-height:280px;overflow:auto;padding:4px 2px}.lod-part-fields .inline-field{justify-content:space-between;flex-wrap:wrap}.lod-part-fields select{max-width:100%}.comparison-lod{padding:12px 16px}.comparison-lod:has(.inline-field[hidden]){display:none}
|
|
52
|
+
.material-card[data-selected-material="true"]{border-color:var(--accent)}
|
|
53
|
+
.material-map{object-fit:contain;image-rendering:pixelated}
|
|
54
|
+
.material-map-inspection{margin:14px 0}.material-inspection{min-width:0;overflow-wrap:anywhere}
|
|
55
|
+
.material-inspection-controls{display:flex;gap:10px;flex-wrap:wrap;margin:14px 0}.material-inspection-controls .inline-field{flex-direction:column;align-items:flex-start}.material-inspection-controls select{max-width:100%;font-size:11px;padding:7px}
|
|
56
|
+
.material-pixel-frame{max-height:360px;overflow:auto;border:1px solid var(--line);border-radius:5px;background:#202020}
|
|
57
|
+
.material-pixel-frame[data-backdrop="checker"]{background-color:#d0d0d0;background-image:conic-gradient(#7a7a7a 25%,#d0d0d0 0 50%,#7a7a7a 0 75%,#d0d0d0 0);background-size:16px 16px}
|
|
58
|
+
.material-pixel-frame[data-backdrop="light"]{background:#f2f2f2}.material-pixel-frame[data-backdrop="dark"]{background:#151515}
|
|
59
|
+
.material-pixel-sheet{position:relative;flex:none;min-width:0}.material-pixel-image{display:block!important;width:100%!important;height:100%!important;max-height:none!important;object-fit:fill!important;image-rendering:pixelated;border-radius:0!important}
|
|
60
|
+
.material-region-overlay{position:absolute;inset:0;pointer-events:none}.material-region-box{position:absolute;border:1px solid #ffce37;overflow:visible}.material-region-box>span{position:absolute;left:0;top:0;font-size:10px;line-height:1.2;background:#151515df;color:#ffefaf;white-space:nowrap;max-width:200px;overflow:hidden;text-overflow:ellipsis}.material-region-box.selected{border:2px solid #fff;box-shadow:0 0 0 2px #111}.material-region-list{display:grid;gap:6px;margin:12px 0}.material-region-list h4{font-size:11px;margin:5px 0}.material-region-list button{text-align:left;font-size:10px;padding:7px;overflow-wrap:anywhere}.material-region-list button[aria-pressed="true"]{border-color:var(--accent)}
|
|
61
|
+
.material-inspection dl{font-size:11px}.material-inspection dt{color:var(--dim);margin-top:7px}.material-inspection dd{margin:0}.live-material-inspection{grid-column:1/-1}.live-material-inspection details{margin-top:12px}.asset-material-inputs{font-size:11px;margin:15px 0}.asset-material-inputs h3{font-size:12px}.asset-material-inputs a{color:var(--accent)}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Atlas provenance in delivered GLBs
|
|
2
|
+
|
|
3
|
+
`src/atlas-provenance.ts` is the internal owner of atlas metadata validation. It uses the existing atlas canonicalizer and manifest validator. It has no Node dependency and its live-scene readers are available to image-free structural QA. These internal helpers are not SDK authoring APIs.
|
|
4
|
+
|
|
5
|
+
The default glTF scene owns `extras.kilnAtlasProvenance`:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{"version":"kiln.atlas-provenance.v1","atlases":[{"id":"sha256:<manifest hash>","spec":{},"manifest":{}}]}
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Each entry contains one complete canonical recipe and validated manifest. Entries deduplicate by exact manifest identity, with a content conflict check. Recipes are never repeated per mesh. Editable source and saved material records remain the authoring owners.
|
|
12
|
+
|
|
13
|
+
Each material-slot `TextureInfo` owns `extras.kilnAtlasReference` with `version`, `id`, `effectiveSampler` (`minFilter`, `magFilter`, `wrapS`, `wrapT` as glTF values), `finalMime`, and `finalImageSha256`. Source collection creates only the version/id pair; final export fills actual sampler and encoded-image identities. The reference belongs to a texture use because one image can serve multiple samplers. glTF Transform preserves TextureInfo extras and does not preserve extras on glTF texture binding objects. Image extras would incorrectly conflate those uses.
|
|
14
|
+
|
|
15
|
+
A primitive using a named remap owns `extras.kilnAtlasBinding`: `version`, `atlasId`, `region`, `inputOrigin`, and `mode`. Export checks the complete source geometry stamp against the primary atlas, including hashes, rectangles, scale and offset. Reimport derives that full geometry stamp from the validated manifest and compact binding. A geometry-wide binding requires its exact primary atlas among the primitive material slots. Companion color/data maps may have different manifest hashes with the same region layout. Manual full-sheet atlas use carries texture references without an inferred named binding. Ordinary textures carry no atlas metadata.
|
|
16
|
+
|
|
17
|
+
Admission allows 128 unique identities, at most 256 KiB per JSON entry, a 4 MiB aggregate table including its envelope/separators, and 1,024 source binding observations. Exported documents independently bound slot references and primitive bindings to 1,024 each. Source graph admission precedes manifest cloning and scene export copies. Distinct atlas texture objects conservatively charge their full RGBA output toward a 64 MiB bake decoded-pixel budget. Encoded-image hashing admits at most 16 MiB per image and 64 MiB in aggregate before hashing. These are metadata/buffer-work bounds, not an operating-system or native codec RSS guarantee. Existing scene traversal remains proportional to the scene.
|
|
18
|
+
|
|
19
|
+
Before first PNG encoding, baking verifies raw RGBA dimensions and SHA-256 against the compiled manifest. Already encoded source maps receive bounded PNG dimension/decode verification. Editing pixels while retaining an atlas stamp fails with a stale-provenance diagnostic. A validated GLB loader establishes a private WeakMap identity for exporter-derived delivery bytes; authored flags cannot establish this identity. Re-export checks those exact bytes against the validated delivery reference rather than comparing legitimate converted normal pixels to the original compiler pixels. The source recipe/pixel identity and final encoded-image identity remain separate. No GLB contains its own final artifact hash; that hash remains in external baked provenance.
|
|
20
|
+
|
|
21
|
+
Both exporters write the same table, references and compact bindings. The optimizer validates input lineage and refreshes the final image identities after its transforms. Composition admits and merges the tables before copying source graphs and deleting their scenes. Import validates final bytes and samplers, never repairs a tampered reference. External transforms that rewrite images must update delivery references through the validated engine export path; retaining old image hashes makes the transformed artifact fail import validation.
|
|
22
|
+
|
|
23
|
+
Structural metadata proves identities, named selectors and remap formulas. It does not prove an author's intended corner orientation after arbitrary within-domain UV edits, visual quality, or inferred physical density. Generic author `kiln*` userData remains excluded; only this owner's validated engine metadata is explicitly promoted.
|
|
@@ -57,6 +57,28 @@ partitions are rejected by the shared preflight. Private `userData` is excluded;
|
|
|
57
57
|
semantic extras and bounded review clips retain their existing policy. Export copies keep caller
|
|
58
58
|
texture sources and skeleton references intact.
|
|
59
59
|
|
|
60
|
+
Both converters preserve standard magnification/minification filters and S/T wrap
|
|
61
|
+
modes on supported texture bindings, including different samplers sharing image
|
|
62
|
+
bytes. Kiln's loader and procedural texture defaults remain repeat wrapping,
|
|
63
|
+
linear magnification and trilinear minification. The default converter now writes
|
|
64
|
+
these choices explicitly; regenerated GLBs can therefore have different bytes
|
|
65
|
+
and material fingerprints from older exports. Existing saved revisions are
|
|
66
|
+
unchanged. Mipmap generation itself is a consumer policy, not a glTF sampler flag.
|
|
67
|
+
|
|
68
|
+
Already encoded PNG/JPEG images on direct, unflipped texture bindings retain their
|
|
69
|
+
original bytes. This matters for cutout RGB padding under zero alpha and for exact
|
|
70
|
+
partial-alpha color: canvas conversion can erase or round those channels. Derived
|
|
71
|
+
channel maps and explicitly flipped images still require conversion. Raw byte maps
|
|
72
|
+
used for metallic/roughness packing or normal-channel inversion are adapted on the
|
|
73
|
+
export copy; caller images and texture references remain unchanged. For cutout atlas
|
|
74
|
+
delivery, keep the canonical image orientation with `flipY: false`; pixel-exact
|
|
75
|
+
transparent RGB through a requested vertical image flip is not qualified.
|
|
76
|
+
|
|
77
|
+
If the optional native canvas binding cannot load, textured headless export reports
|
|
78
|
+
the dependency and recovery steps. Reinstall with optional dependencies enabled and
|
|
79
|
+
check the binding in the same Node runtime and platform as the Kiln host. It does not
|
|
80
|
+
silently switch converters. Flat-material exports do not require that binding.
|
|
81
|
+
|
|
60
82
|
This describes serialization capability. It does not automatically extend all authoring helpers,
|
|
61
83
|
QA rules, preview materials or optimization algorithms. In particular, skin/morph deformation,
|
|
62
84
|
complex texture transforms, material extensions in every review path, and runtime codec support
|
|
@@ -65,6 +87,11 @@ converter rather than bypassing the triangle pipeline's metadata and geometry po
|
|
|
65
87
|
|
|
66
88
|
## Qualify a consumer
|
|
67
89
|
|
|
90
|
+
Both converters reject used `BackSide` materials because glTF has no back-only
|
|
91
|
+
material flag. See the [portable sidedness contract](export-profiles.md#material-sidedness-at-export)
|
|
92
|
+
for explicit inward geometry and deliberate two-sided sheets. This prevents silent
|
|
93
|
+
front-face substitution; it does not change the default exporter or existing GLBs.
|
|
94
|
+
|
|
68
95
|
Keep a canonical GLB and name the actual engine, importer, version and render pipeline in results.
|
|
69
96
|
For example, Unity 6 Built-in RP with glTFast is a different target from Unity URP or an FBX workflow.
|
|
70
97
|
Tests should check hierarchy, world-space positions, part names, material assignments, texture pixels,
|
package/docs/export-profiles.md
CHANGED
|
@@ -44,6 +44,26 @@ unsupported UV transforms and destination feature support still need qualificati
|
|
|
44
44
|
Neither setting automatically enables mesh merging, geometry simplification, Draco,
|
|
45
45
|
Meshopt or KTX2 compression. Those require separate choices and validation.
|
|
46
46
|
|
|
47
|
+
## Material sidedness at export
|
|
48
|
+
|
|
49
|
+
glTF represents front-only or double-sided surfaces. Both Kiln converters preserve
|
|
50
|
+
`FrontSide` and `DoubleSide`. A used `BackSide` material now stops a source build
|
|
51
|
+
with `EXPORT_MATERIAL_SIDE_UNSUPPORTED`. The direct scene API names the part and
|
|
52
|
+
material; CLI/MCP carry closed repair advice without authored identifiers across
|
|
53
|
+
the evaluator boundary. Use `kiln_source` with query `BackSide` to locate the
|
|
54
|
+
authored material. This applies
|
|
55
|
+
in both `warn` and `strict` geometry modes and before texture baking or conversion;
|
|
56
|
+
unused material-array slots do not block export. Existing saved GLBs remain readable
|
|
57
|
+
and exporting their exact bytes does not rebuild them.
|
|
58
|
+
|
|
59
|
+
For a one-sided interior surface, author triangles facing inward and use
|
|
60
|
+
`FrontSide`. For a leaf or other deliberate sheet visible from either side, use
|
|
61
|
+
`DoubleSide`. Neither choice supplies thickness or missing caps. Do not flip only
|
|
62
|
+
normals to repair winding. Automatic back-only conversion is not provided: winding,
|
|
63
|
+
normal/tangent basis, groups, shared geometry and deformation data must remain
|
|
64
|
+
consistent. A destination shader override is an explicit runtime choice, separate
|
|
65
|
+
from the portable asset.
|
|
66
|
+
|
|
47
67
|
## CLI
|
|
48
68
|
|
|
49
69
|
From an asset workspace, using the IDs returned by `save` or `assets`:
|
package/docs/material-library.md
CHANGED
|
@@ -35,8 +35,9 @@ node kiln.mjs material get RETURNED_MATERIAL_ID RETURNED_REVISION
|
|
|
35
35
|
node kiln.mjs material export RETURNED_MATERIAL_ID RETURNED_REVISION --out wood-payload.json
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
Copy the returned `portableSpec` into authored source
|
|
39
|
-
`compilePortableMaterialSpecV2
|
|
38
|
+
Copy the returned `portableSpec` into authored source with the exact dependency
|
|
39
|
+
pin. Use `compilePortableMaterialSpecV2` for `portableSpec.schemaVersion === 2`
|
|
40
|
+
and `compilePortableMaterialSpecV3` for version 3. Existing presets return V2.
|
|
40
41
|
Custom draft creation uses CLI `material procedural --file draft.json`, corresponding
|
|
41
42
|
to MCP `create-procedural`. These paths need no imports from internal `dist/` files
|
|
42
43
|
or extra TypeScript loader. The public host APIs below are for a configured TypeScript
|
|
@@ -44,7 +45,10 @@ toolchain; ordinary Node cannot directly execute TypeScript inside `node_modules
|
|
|
44
45
|
|
|
45
46
|
`list` returns compact summaries and accepts a `tag`. `get` requires `materialId` and `revisionId`
|
|
46
47
|
and returns provenance plus a code-ready `portableSpec`. `create-procedural` accepts an editable
|
|
47
|
-
draft using the same bounded layer recipes as the existing procedural compiler.
|
|
48
|
+
draft using the same bounded layer recipes as the existing procedural compiler. Opt-in
|
|
49
|
+
drafts with `schemaVersion:2` also support RGBA recipes, named atlases and explicit
|
|
50
|
+
per-map samplers; see the [standalone texture workflow](procedural-surfaces-v3.md#standalone-saved-materials-and-atlases).
|
|
51
|
+
An optional
|
|
48
52
|
`derive: {"kind":"normal-from-height","strength":2}` on a normal map converts the recipe's
|
|
49
53
|
height pixels through the existing wrapped normal derivation. `import` accepts a complete
|
|
50
54
|
normalized payload. None of these operations acquires source images from a provider.
|
|
@@ -66,8 +70,8 @@ Pin the returned material ID and revision hash before using its resource IDs. A
|
|
|
66
70
|
optional: pass a pin array as MCP `materialDependencies` or CLI `--materials pins.json` for
|
|
67
71
|
an individual operation, or put the array in a project's `materialDependencies` for shared use.
|
|
68
72
|
The pin's `resourceId` is the material ID; its `revisionId` and `sha256` both equal the
|
|
69
|
-
manifest's immutable `revisionId`. `
|
|
70
|
-
material inside authored source. The host supplies complete map bytes through `materialResources`
|
|
73
|
+
manifest's immutable `revisionId`. The compiler matching `portableSpec.schemaVersion`
|
|
74
|
+
creates the material inside authored source. The host supplies complete map bytes through `materialResources`
|
|
71
75
|
before build-cache lookup, including when evaluation uses the default subprocess.
|
|
72
76
|
|
|
73
77
|
Editable standalone asset ZIPs carry the saved dependency closure in `materials.kiln.json`;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Procedural RGBA surfaces V3
|
|
2
|
+
|
|
3
|
+
`proceduralTexture({ schemaVersion: 3, ... })` creates rectangular RGBA pixels
|
|
4
|
+
through the same authored-source helper as V1/V2. The standalone compiler is
|
|
5
|
+
`compileProceduralTextureSpecV3` from the primitives SDK. Its contract helpers
|
|
6
|
+
are `canonicalizeProceduralTextureSpecV3`, `canonicalProceduralTextureJsonV3`
|
|
7
|
+
and `hashProceduralTextureSpecV3`.
|
|
8
|
+
|
|
9
|
+
V1 (absent or `1` version) and V2 remain on their existing compiler, square
|
|
10
|
+
power-of-two dimensions, opaque output, canonical JSON and hashes. An explicitly
|
|
11
|
+
undefined version retains historical V1 behavior; V3 itself accepts strict JSON,
|
|
12
|
+
so undefined fields are invalid. Unknown versions fail before dispatch. Existing
|
|
13
|
+
portable material V2 references remain V2. New portable V3 material references
|
|
14
|
+
and V2 saved material records are opt-in contracts described below.
|
|
15
|
+
|
|
16
|
+
## Contract
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
{
|
|
20
|
+
schemaVersion: 3,
|
|
21
|
+
width: 4..1024, height: 4..1024, // integers, need not be powers of two
|
|
22
|
+
usage?: 'albedo' | 'emissive' | 'normal' | 'roughness' | 'metalness'
|
|
23
|
+
| 'metallicRoughness' | 'occlusion', // default albedo
|
|
24
|
+
name?: string, // 1..80 visible characters
|
|
25
|
+
background?: [r,g,b,a], // bytes 0..255, default [0,0,0,0]
|
|
26
|
+
operations: OperationV3[], // ordered, zero to 64 entries
|
|
27
|
+
palette?: { colors: RGB[], dither?: 'none' | 'bayer4' }
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
RGBA channels are straight bytes. RGB is authored encoded color for albedo and
|
|
32
|
+
emissive; other usages retain linear data. Transparent RGBA input canonicalizes
|
|
33
|
+
to `[0,0,0,0]`, and every output pixel with zero alpha has zero RGB. Palette
|
|
34
|
+
mapping and stamps support only albedo/emissive; they reject all data usages.
|
|
35
|
+
Shapes and masks operate on declared bytes without interpreting data channels.
|
|
36
|
+
|
|
37
|
+
Operations have these exact fields:
|
|
38
|
+
|
|
39
|
+
| `op` | Fields |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `paint` | `shape`, `rgba`, optional byte `opacity` (default 255) |
|
|
42
|
+
| `mask` | `shape`, optional byte `alpha` (default 255), optional boolean `invert` (default false) |
|
|
43
|
+
| `stamps` | `shape`, `rgba`, `count`, uint32 `seed`, `area`, optional byte `opacity`, optional boolean `periodic` (default false) |
|
|
44
|
+
| `legacy-v2` | `spec` with explicit schemaVersion 2, optional byte `alpha` and byte `opacity` (both default 255) |
|
|
45
|
+
|
|
46
|
+
V3 opacity is a byte, unlike V2's fractional opacity. The legacy layer delegates
|
|
47
|
+
to the existing V2 compiler, including its six operators and first-layer rules.
|
|
48
|
+
Its square size and usage must match the V3 surface; its output RGB is composited
|
|
49
|
+
using the explicitly selected V3 alpha and opacity. It does not reinterpret V2
|
|
50
|
+
blend modes or silently migrate a V2 recipe into V3 operations.
|
|
51
|
+
|
|
52
|
+
## Coverage and compositing
|
|
53
|
+
|
|
54
|
+
Coordinates describe integer pixel edges; a pixel `(x,y)` samples `(x+0.5,y+0.5)`.
|
|
55
|
+
Shapes clip to the output extent. There is no antialiasing or fractional shape
|
|
56
|
+
coverage in this compiler version.
|
|
57
|
+
|
|
58
|
+
| Shape `kind` | Fields and coverage |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| `rectangle` | `x,y,width,height`; left/top inclusive, right/bottom exclusive |
|
|
61
|
+
| `ellipse` | `x,y,width,height`; inscribed ellipse, boundary inclusive |
|
|
62
|
+
| `polygon` | `points:[[x,y],...]`; even-odd fill, boundary inclusive, 3..64 vertices; self-intersections use that same fill rule |
|
|
63
|
+
| `line` | `from:[x,y],to:[x,y],width`; capsule with inclusive round caps; coincident endpoints create a disk |
|
|
64
|
+
|
|
65
|
+
Coordinates are integers -4096..4096. Shape widths/heights and line width are
|
|
66
|
+
integers 1..2048. A one-pixel line on an integer edge can cover centers on both
|
|
67
|
+
sides of the edge; width measures continuous distance, not a promised count of
|
|
68
|
+
covered columns. Polygon degeneracies are deterministic rather than repaired.
|
|
69
|
+
|
|
70
|
+
`paint` uses source-over. Define `round(n/d) = floor(n/d + 0.5)`. For source alpha
|
|
71
|
+
`a`, operation opacity `o` and destination alpha `d`:
|
|
72
|
+
|
|
73
|
+
```text
|
|
74
|
+
s = round(a * o / 255)
|
|
75
|
+
N = s * 255 + d * (255 - s)
|
|
76
|
+
output alpha = round(N / 255)
|
|
77
|
+
output channel = round((source channel * s * 255
|
|
78
|
+
+ destination channel * d * (255 - s)) / N)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
When `s=0`, painting is inert. RGB division uses the unrounded alpha numerator,
|
|
82
|
+
then alpha and RGB are stored as bytes after each operation. This blends authored
|
|
83
|
+
byte values; it does not linearize encoded colors or introduce floating opacity.
|
|
84
|
+
|
|
85
|
+
`mask` multiplies destination alpha by the mask byte and rounds half-up. Outside
|
|
86
|
+
the covered shape it clears alpha. With `invert:true`, outside retains the mask
|
|
87
|
+
alpha factor and inside clears alpha. It preserves destination RGB while alpha
|
|
88
|
+
remains positive and clears RGB when alpha becomes zero. A mask affects only
|
|
89
|
+
pixels already painted; subsequent paint can add pixels again.
|
|
90
|
+
|
|
91
|
+
## Palettes and periodic stamps
|
|
92
|
+
|
|
93
|
+
Palette mapping runs once after all operations. `colors` is an ordered array of
|
|
94
|
+
1..32 RGB integers 0..0xffffff. The nearest color minimizes squared distance in
|
|
95
|
+
encoded RGB bytes; a tie selects the first array entry. Alpha is preserved and
|
|
96
|
+
transparent pixels stay black. `dither:'none'` is the default.
|
|
97
|
+
|
|
98
|
+
`bayer4` repeats this fixed matrix from image origin `(0,0)`:
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
0 8 2 10
|
|
102
|
+
12 4 14 6
|
|
103
|
+
3 11 1 9
|
|
104
|
+
15 7 13 5
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Before nearest-color selection, each RGB channel receives integer offset
|
|
108
|
+
`(2 * matrixEntry - 15) * 2`, clipped to 0..255. Palette membership is therefore
|
|
109
|
+
exact, and dither does not add alpha variation or random noise.
|
|
110
|
+
|
|
111
|
+
Stamp `area` is `{x,y,width,height}`, with the same rectangle coordinate bounds.
|
|
112
|
+
Each stamp translates its local shape by two integer offsets within that area.
|
|
113
|
+
The PRNG starts from `(seed ^ 0xa3c59ac3) >>> 0`, substitutes fixed state
|
|
114
|
+
`0x6d2b79f5` if that value is zero, performs Xorshift32 with shifts 13,17,5,
|
|
115
|
+
and consumes X then Y in operation order. Offsets use unsigned state
|
|
116
|
+
modulo area width/height. Counts are 1..256. Seeds use the full unsigned 32-bit
|
|
117
|
+
range, including zero. No clock or random global is involved.
|
|
118
|
+
|
|
119
|
+
`periodic:true` explicitly wraps the translated shape's bounding origin into one
|
|
120
|
+
surface period, then clips copies translated by -period, 0 and +period on each
|
|
121
|
+
axis. Its shape bounds must fit one period on each axis. Each periodic stamp
|
|
122
|
+
counts as nine expanded copies, with at most 256 expanded copies across the
|
|
123
|
+
recipe. Ordinary stamps clip at edges. Periodic placement does not establish
|
|
124
|
+
that other operations tile; inspect repeat neighbors and the resulting patch.
|
|
125
|
+
|
|
126
|
+
## Admission and identity
|
|
127
|
+
|
|
128
|
+
Validation inspects every object and array data descriptor before field reads.
|
|
129
|
+
Custom prototypes, accessors, symbols, holes, non-index array fields, forbidden
|
|
130
|
+
prototype keys, functions, undefined, nonfinite numbers and unknown schema
|
|
131
|
+
fields are rejected. Admission precedes the output pixel allocation.
|
|
132
|
+
|
|
133
|
+
The limits are 1,048,576 output texels, 64 operations, 64 polygon vertices,
|
|
134
|
+
256 expanded stamps and 32 palette colors. JSON preflight admits at most
|
|
135
|
+
16,384 nodes and depth 10, at most 1024 fields per container, and at most
|
|
136
|
+
262,144 characters of canonical JSON. The only arbitrary text is an 80-character
|
|
137
|
+
name, so this also bounds UTF-8 recipe storage with a small fixed overhead.
|
|
138
|
+
|
|
139
|
+
The conservative work ceiling is 64,000,000 units. It includes output
|
|
140
|
+
initialization, clipped shape tests (each polygon edge counts), full mask scans,
|
|
141
|
+
per-copy stamp bounds, palette comparisons and legacy pattern/noise work. V3
|
|
142
|
+
transient pixel buffers stay below 64MiB. The compiler creates one RGBA output;
|
|
143
|
+
legacy layers temporarily create matching RGBA outputs and palette metadata is
|
|
144
|
+
bounded. These limits do not raise legacy or portable-material budgets.
|
|
145
|
+
|
|
146
|
+
The compiler ID is `kiln.procedural-rgba.v3.1`. V3 recipe hashes are SHA-256 of
|
|
147
|
+
`compilerId + '\n' + canonicalJson`; property order and omitted defaults produce
|
|
148
|
+
the same canonical result. A changed pixel algorithm requires a new compiler ID.
|
|
149
|
+
V3 metadata records that ID, canonical JSON and recipe hash. V2's hashes and
|
|
150
|
+
material-record compiler ID `kiln.procedural-texture.v2` remain unchanged.
|
|
151
|
+
|
|
152
|
+
Failures use closed evaluator diagnostic `PROCEDURAL_TEXTURE_V3_CONTRACT`;
|
|
153
|
+
direct SDK calls also include the rejected field in the exception text.
|
|
154
|
+
|
|
155
|
+
## Authored-source example
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
function build() {
|
|
159
|
+
const root = createRoot('CutoutStudy');
|
|
160
|
+
const texture = proceduralTexture({
|
|
161
|
+
schemaVersion: 3, width: 16, height: 32, name: 'LeafStudy',
|
|
162
|
+
operations: [
|
|
163
|
+
{ op: 'paint', shape: { kind: 'ellipse', x: 3, y: 2, width: 10, height: 28 },
|
|
164
|
+
rgba: [66, 100, 42, 255] },
|
|
165
|
+
{ op: 'paint', shape: { kind: 'line', from: [8, 4], to: [8, 29], width: 1 },
|
|
166
|
+
rgba: [118, 132, 60, 255] }
|
|
167
|
+
],
|
|
168
|
+
palette: { colors: [0x42642a, 0x76843c] }
|
|
169
|
+
});
|
|
170
|
+
const material = pbrMaterial({ albedo: texture, alphaMode: 'mask',
|
|
171
|
+
alphaCutoff: 0.5, doubleSided: true, roughness: 1 });
|
|
172
|
+
createPart('Card', planeGeo(0.5, 1), material, { parent: root });
|
|
173
|
+
return root;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Textures keep existing repeat/linear/mipmap defaults. Sampling choices remain
|
|
178
|
+
explicit Three.js texture settings; creating V3 does not select a pixel-art
|
|
179
|
+
policy. The material's alpha mode and sidedness are separate from pixel alpha.
|
|
180
|
+
|
|
181
|
+
## Standalone saved materials and atlases
|
|
182
|
+
|
|
183
|
+
No mesh or project is required to create or save a procedural texture. The
|
|
184
|
+
existing `kiln_material` action `create-procedural`, named operation
|
|
185
|
+
`kiln_material_create`, and `kiln material procedural --file draft.json` share
|
|
186
|
+
one JSON authoring boundary. An unversioned layered material draft retains its
|
|
187
|
+
V1 record format and existing defaults. An opt-in material draft with
|
|
188
|
+
`schemaVersion:2` accepts V2 layer recipes, V3 RGBA recipes, or named atlases and
|
|
189
|
+
saves a V2 material record. This material-record version is distinct from a
|
|
190
|
+
texture recipe's `schemaVersion`.
|
|
191
|
+
|
|
192
|
+
Each new map supplies exactly one `procedural` or `atlas` recipe, its map slot,
|
|
193
|
+
source provenance, transforms, and an explicit sampler:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"minFilter": "nearest",
|
|
198
|
+
"magFilter": "nearest",
|
|
199
|
+
"wrapS": "clamp-to-edge",
|
|
200
|
+
"wrapT": "clamp-to-edge"
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
This example is a choice for a pixel texture, not a global engine default.
|
|
205
|
+
Minification also supports `linear`, `nearest-mipmap-nearest`,
|
|
206
|
+
`nearest-mipmap-linear`, `linear-mipmap-nearest`, and `linear-mipmap-linear`.
|
|
207
|
+
Wrapping supports `repeat`, `mirrored-repeat`, and `clamp-to-edge`.
|
|
208
|
+
Evaluate minification in the consuming scene; nearest magnification alone does
|
|
209
|
+
not establish acceptable motion, distant cutouts, or atlas gutter behavior.
|
|
210
|
+
|
|
211
|
+
Read `shape:material-draft` and `shape:material-import` with Discovery for the
|
|
212
|
+
complete runtime-validated fields. Sources keep explicit creator and license
|
|
213
|
+
records. The JSON authoring surface accepts no file paths, URLs to fetch, or
|
|
214
|
+
raw image bytes. Existing external image records enter through verified import.
|
|
215
|
+
|
|
216
|
+
`textureAtlas(spec)` compiles named, fixed pixel rectangles. The atlas has
|
|
217
|
+
`schemaVersion:1`, width, height, usage, an optional sampler, and regions.
|
|
218
|
+
Coordinates are integer pixels from the top-left. Each region has a stable
|
|
219
|
+
lowercase `name`, a `content` rectangle, a `source`, and optional crop, gutter,
|
|
220
|
+
and edge mode. Default gutters are two pixels; padded allocations must fit
|
|
221
|
+
without overlap. Sources must already match the requested crop size: this
|
|
222
|
+
operation does not pack, resize, rotate, or resample images.
|
|
223
|
+
|
|
224
|
+
A source is either `{kind:'procedural',spec:<V3 recipe>}` or
|
|
225
|
+
`{kind:'resource',resourceId:<exact map id>,pin:{resourceId:<material id>,
|
|
226
|
+
revisionId:<revision hash>,sha256:<same revision hash>}}`. Resource IDs identify
|
|
227
|
+
the saved map, while the pin identifies its immutable material revision.
|
|
228
|
+
Resolution uses only the explicitly injected material library or evaluator
|
|
229
|
+
dependencies, never nearby projects or network requests. The shared dependency
|
|
230
|
+
walker admits the entire metadata graph before loading source PNGs.
|
|
231
|
+
|
|
232
|
+
`copy-edge` copies edge RGBA into the gutter. `mask-rgb-dilate` keeps gutter alpha
|
|
233
|
+
zero and fills RGB from the nearest positive-alpha source texel, with a stable
|
|
234
|
+
tie break. This reduces dark cutout fringes but does not guarantee safe mipmaps
|
|
235
|
+
for every region size or viewing distance.
|
|
236
|
+
|
|
237
|
+
Use `remapUVToAtlas(geometry, atlas, regionName, {inputOrigin,mode})` to map unit
|
|
238
|
+
UVs into a named region on an owned geometry clone. `inputOrigin` is explicit
|
|
239
|
+
`top-left` or `bottom-left`; `mode` is `content-boundary` or `texel-center`. Rich attributes,
|
|
240
|
+
morphs and skin data are retained according to the UV helper's contract. A
|
|
241
|
+
region binding describes that geometry's region; it does not infer independent
|
|
242
|
+
region assignments for material groups.
|
|
243
|
+
|
|
244
|
+
Saved atlas maps retain the canonical recipe, named-region/source manifest,
|
|
245
|
+
compiler identity, encoded-image hash, decoded pixel hash and exact source
|
|
246
|
+
revision pins. `kiln material export` includes the complete verified material
|
|
247
|
+
closure; importing it into an offline workspace restores those revisions.
|
|
248
|
+
Old-only payloads retain schemaVersion 1. Mixed or new records use schemaVersion
|
|
249
|
+
2 without rewriting old identities. Projects remain optional and pin exact
|
|
250
|
+
saved revisions when explicitly selected.
|
|
251
|
+
|
|
252
|
+
## Atlas delivery and inspection
|
|
253
|
+
|
|
254
|
+
Both GLB converters retain a bounded `kilnAtlasProvenance` table in scene extras,
|
|
255
|
+
an exact `kilnAtlasReference` on each material texture use, and a compact
|
|
256
|
+
`kilnAtlasBinding` on primitives created from a named remap. These optional extras
|
|
257
|
+
describe Kiln lineage; ordinary glTF textures, UVs and samplers still render in
|
|
258
|
+
consumers that ignore them. Manual full-sheet use needs no named binding.
|
|
259
|
+
The [delivery metadata contract](atlas-provenance.md) specifies the fields,
|
|
260
|
+
validation and bounds.
|
|
261
|
+
|
|
262
|
+
The source manifest identifies compiled RGBA pixels and recipe inputs. The
|
|
263
|
+
delivery reference separately identifies final encoded image bytes and the
|
|
264
|
+
effective sampler for that use. Conversion may legitimately alter pixels, such
|
|
265
|
+
as an explicit normal-channel conversion, so these identities are distinct.
|
|
266
|
+
Import validates the recorded delivery identity; it never silently replaces a
|
|
267
|
+
stale hash. Editing compiled atlas pixels requires recompilation or removal of
|
|
268
|
+
the atlas metadata to make an ordinary manually authored texture.
|
|
269
|
+
|
|
270
|
+
Structural QA checks declared regions, referenced UVs, slots and sampling without
|
|
271
|
+
decoding images. Quantized UV storage has a bounded precision allowance. Work
|
|
272
|
+
limits or unavailable dimensions produce partial coverage; a valid UV domain
|
|
273
|
+
does not prove the intended orientation or visual quality. Inspect exact saved
|
|
274
|
+
maps in Materials, or follow the material links from Library and Live Review.
|
|
275
|
+
Use a material-faithful rendered view and the destination scene for cutout,
|
|
276
|
+
lighting and motion decisions.
|
|
@@ -29,7 +29,7 @@ Authoring commands accept `--project` and `--project-revision`. MCP tools use `p
|
|
|
29
29
|
node kiln.mjs render asset.kiln.js --no-project --materials pins.json --out asset.glb
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
`pins.json` contains an array of exact `{resourceId, revisionId, sha256}` material dependencies. MCP accepts the same array as `materialDependencies`. These per-call pins work independently of project membership. With a project, additional pins
|
|
32
|
+
`pins.json` contains an array of exact `{resourceId, revisionId, sha256}` material dependencies. MCP accepts the same array as `materialDependencies`. These per-call pins work independently of project membership. With a project, additional pins extend its lock and an explicit pin replaces the project pin for the same resource for that invocation. The saved project revision remains unchanged. Copy returned identities rather than constructing them, and preserve the selected dependencies on subsequent calls that evaluate the source.
|
|
33
33
|
|
|
34
34
|
## Materials and coherent art direction
|
|
35
35
|
|
package/docs/rendering.md
CHANGED
|
@@ -27,6 +27,47 @@ safety. See `kiln animation --help` for the complete options.
|
|
|
27
27
|
|
|
28
28
|
CPU views show silhouette, orientation, proportion, and contact. They do not reproduce the asset's PBR materials. Translucent (opacity below 1) surfaces are composited after opaque geometry, farthest first, so parts behind glazing stay visible whatever order the scene lists them in. Use GPU views to review textures, roughness, metalness, and normal relief. GPU output can vary by device and driver.
|
|
29
29
|
|
|
30
|
+
GLB review reconstructs skins with up to four joint influences per vertex,
|
|
31
|
+
including normalized weights and inverse bind matrices. CPU triangles and pose
|
|
32
|
+
bounds follow the sampled joint pose rather than the undeformed position buffer.
|
|
33
|
+
Unsupported additional influence sets produce an explicit error. GLB review
|
|
34
|
+
also reconstructs position/normal morph deltas, node-over-mesh default weights,
|
|
35
|
+
and STEP, LINEAR or CUBICSPLINE weight animation before applying skin transforms.
|
|
36
|
+
Missing target attributes contribute zero displacement. Other morph semantics
|
|
37
|
+
retain `GLB_FLAT_MORPH_TARGETS_UNSUPPORTED`; they are not qualified by this path.
|
|
38
|
+
Expanded morph attributes are bounded to 64 MiB per review scene, including
|
|
39
|
+
zero-filled attributes. Weight tracks use the review limits of 4,096 keys and
|
|
40
|
+
16,384 scalar values. Invalid counts and nonfinite weights/deltas are rejected.
|
|
41
|
+
Morph primitive children receive unique binding-safe names beneath the original
|
|
42
|
+
named node; use that parent for a stable authored-part selection. Copies have
|
|
43
|
+
independent morph influences. Loop-closure evidence remains transform-only and
|
|
44
|
+
reports weight tracks as unassessed rather than labeling them as scale tracks.
|
|
45
|
+
Isolated, two-sided and composed-scene review
|
|
46
|
+
copies own their skeletons and preserve required hidden joint ancestors as
|
|
47
|
+
transforms without drawing their geometry. Include all referenced bones in the
|
|
48
|
+
review root; copying an incomplete hierarchy reports the missing dependency.
|
|
49
|
+
Geometry and textures remain shared read-only, while two-sided overrides own
|
|
50
|
+
their materials. Qualify material-faithful derivatives separately from the
|
|
51
|
+
whole-character CPU view.
|
|
52
|
+
|
|
53
|
+
CPU views honor front-only, back-only and double-sided source materials in both
|
|
54
|
+
camera paths. Facing follows triangle winding after deformation, with the mesh's
|
|
55
|
+
world-transform reflection parity; changing vertex normals does not change culling.
|
|
56
|
+
Visible back faces reverse their flat lighting normal. Disabling culling for a
|
|
57
|
+
diagnostic view acts as a two-sided override. Singular transforms do not flip
|
|
58
|
+
winding; collapsed triangles do not draw. CPU output remains geometry evidence,
|
|
59
|
+
not PBR evidence. Back-only source materials are not directly portable to GLB;
|
|
60
|
+
see [material sidedness at export](export-profiles.md#material-sidedness-at-export).
|
|
61
|
+
|
|
62
|
+
Both CPU camera paths sample pixel centres with top-left shared-edge ownership.
|
|
63
|
+
They evaluate all three edges directly from canonical endpoints, preventing
|
|
64
|
+
rounding gaps and duplicate blending where adjacent triangles meet. Near/far
|
|
65
|
+
clipping uses direction-independent intersections. Interpolation uses unbiased
|
|
66
|
+
edge weights, including reciprocal depth for perspective cameras. This is a
|
|
67
|
+
floating-point geometry preview, not hardware subpixel/MSAA emulation; perimeter
|
|
68
|
+
pixels can differ from GPU views. Degenerate or nonfinite projected triangles
|
|
69
|
+
are discarded, and raster loops stay within the output image.
|
|
70
|
+
|
|
30
71
|
## Review lighting
|
|
31
72
|
|
|
32
73
|
New GPU tool captures use **`review-neutral-v1`**. The rig uses the room PMREM
|
package/docs/sdk.md
CHANGED
|
@@ -80,6 +80,59 @@ and source/revision receipts for reproducible replay. Compatibility of the SDK
|
|
|
80
80
|
does not imply identical generated GLB bytes or renderer output across releases.
|
|
81
81
|
Persisted data and rebuild restrictions follow the [migration contract](migration.md).
|
|
82
82
|
|
|
83
|
+
## Procedural RGBA surfaces
|
|
84
|
+
|
|
85
|
+
The `primitives` entrypoint and authored `proceduralTexture` helper accept an
|
|
86
|
+
explicit V3 recipe for bounded rectangular RGBA images, shape coverage, alpha
|
|
87
|
+
masks, palettes, dithering and seeded stamps. Pure V3 compilation returns pixels,
|
|
88
|
+
canonical JSON and a compiler-qualified recipe hash without creating a mesh.
|
|
89
|
+
Read the [V3 contract](procedural-surfaces-v3.md) for the exact limits and rounding
|
|
90
|
+
rules. V1/V2 remain supported with their existing pixels, hashes and sampler
|
|
91
|
+
defaults. The same entrypoint exposes `textureAtlas` and `remapUVToAtlas` for
|
|
92
|
+
named regions. Portable V3 materials add explicit per-map sampling; opt-in V2
|
|
93
|
+
material records persist recipes, compiled maps and exact dependency pins.
|
|
94
|
+
See the [standalone material and atlas workflow](procedural-surfaces-v3.md#standalone-saved-materials-and-atlases).
|
|
95
|
+
|
|
96
|
+
## Explicit character skinning
|
|
97
|
+
|
|
98
|
+
The `character` and `primitives` entrypoints expose opt-in `createBoneChain` and
|
|
99
|
+
`createSkinnedPart`; both are also sandbox helpers. `createJointChain` continues
|
|
100
|
+
to return ordinary rigid `Object3D` pivots. Bone chains use the same versioned
|
|
101
|
+
joint descriptors, infer an attached declared parent role and reject ambiguous
|
|
102
|
+
roles/names before attaching nodes. They do not choose anatomy or a body plan.
|
|
103
|
+
|
|
104
|
+
`createSkinnedPart(name, geometry, material, { parent, bones, indices, weights })`
|
|
105
|
+
clones unchanged geometry and binds it to the assembled hierarchy's current rest
|
|
106
|
+
pose. Supply exactly four indices and weights per vertex, in the explicit bone
|
|
107
|
+
array order. Indices must be valid even in zero-weight lanes. By default weights
|
|
108
|
+
must be in 0..1 and sum to one within 1e-5; `normalizeWeights: true` explicitly
|
|
109
|
+
normalizes positive finite sums. Zero sums are rejected. Optional local position,
|
|
110
|
+
Euler rotation (degrees), scale and semantic metadata match `createPart`'s units.
|
|
111
|
+
|
|
112
|
+
The helper owns geometry, influence arrays and inverse bind matrices; bones and
|
|
113
|
+
materials remain shared. Limits are 1024 bones, 262144 vertices and 128 MiB of
|
|
114
|
+
cloned attributes plus new influence storage. These are helper limits, not new
|
|
115
|
+
restrictions on direct Three.js authoring. Existing skin attributes and singular
|
|
116
|
+
bind transforms are rejected. Set up rest transforms before binding; later rest
|
|
117
|
+
edits/reparenting need deliberate rebinding. Animated bounds remain a consumer
|
|
118
|
+
responsibility. The selected Three exporter is required to preserve skins;
|
|
119
|
+
calling these helpers does not change the legacy default or select a converter.
|
|
120
|
+
|
|
121
|
+
`inspectSkinIntegrity(root)` from `@instruktlabs/kiln/qa` reads source bindings
|
|
122
|
+
without updating matrices, normalizing weights or repairing the scene. It returns
|
|
123
|
+
bounded findings, checked counts and current posed world bounds (including position
|
|
124
|
+
morphs). It is not an animation envelope, deformation-quality score or proof of
|
|
125
|
+
collision clearance. Rigid meshes do not acquire skin or body-plan requirements.
|
|
126
|
+
|
|
127
|
+
Export runs this check before bounds, texture baking or conversion. Invalid source
|
|
128
|
+
bindings produce an `AssetQaBlockedError` at the scene stage with mesh/joint or
|
|
129
|
+
vertex-range context and repair advice. This is mandatory input integrity, like
|
|
130
|
+
invalid geometry buffers; QA warning mode and derivative serialization do not
|
|
131
|
+
bypass it. The partial failure report does not claim the remaining QA rules ran.
|
|
132
|
+
The independent final glTF validation remains in place. Source Three skeletons
|
|
133
|
+
need their inverse-bind arrays populated; this does not change glTF's valid
|
|
134
|
+
identity-matrix default when the file omits `inverseBindMatrices`.
|
|
135
|
+
|
|
83
136
|
## Execution and dependencies
|
|
84
137
|
|
|
85
138
|
The root SDK is Node-oriented. `/node` adapters use filesystem or server services;
|
package/docs/tools-operations.md
CHANGED
|
@@ -1123,7 +1123,7 @@ Create and retain one immutable material from a preset or procedural definition.
|
|
|
1123
1123
|
"type": "object",
|
|
1124
1124
|
"properties": {
|
|
1125
1125
|
"draft": {
|
|
1126
|
-
"description": "create-procedural:
|
|
1126
|
+
"description": "create-procedural: legacy layered draft, or schemaVersion 2 with explicit samplers and RGBA/atlas recipes. Exact atlas sources must exist in this library. Shape: kiln_discover({ ids: ['shape:material-draft'] }).",
|
|
1127
1127
|
"type": "object",
|
|
1128
1128
|
"propertyNames": {
|
|
1129
1129
|
"type": "string"
|
|
@@ -1166,7 +1166,7 @@ Import complete normalized material records and embedded PNGs. Does not fetch UR
|
|
|
1166
1166
|
"type": "object",
|
|
1167
1167
|
"properties": {
|
|
1168
1168
|
"payload": {
|
|
1169
|
-
"description": "import: schemaVersion 1
|
|
1169
|
+
"description": "import: schemaVersion 1 or 2, complete normalized records and their exact dependencies with embedded PNG bytes. Shape: kiln_discover({ ids: ['shape:material-import'] }).",
|
|
1170
1170
|
"type": "object",
|
|
1171
1171
|
"propertyNames": {
|
|
1172
1172
|
"type": "string"
|
package/docs/tools.md
CHANGED
|
@@ -1328,7 +1328,7 @@ Manage optional shared workspace projects: list discovers IDs; get reads the cur
|
|
|
1328
1328
|
|
|
1329
1329
|
## kiln_material
|
|
1330
1330
|
|
|
1331
|
-
Manage optional immutable material resources in this workspace. presets
|
|
1331
|
+
Manage optional immutable material resources in this workspace. presets lists shipped recipes; create-preset uses explicit seed, creator and license; list returns revision summaries; get returns provenance, hashes, sampling and a portable material spec. create-procedural accepts legacy layer drafts or schemaVersion 2 drafts with explicit samplers and bounded RGBA or named atlas recipes. Atlas resource sources require exact saved revisions; import requires complete normalized records and dependencies with embedded PNG bytes. Read shape:material-draft or shape:material-import through kiln_discover for full fields. No action downloads URLs or executes source. Pin the returned materialId/revisionId through per-invocation materialDependencies or project dependencies before evaluation; no project or mesh is required.
|
|
1332
1332
|
|
|
1333
1333
|
<details>
|
|
1334
1334
|
<summary>Input JSON Schema</summary>
|
|
@@ -1431,7 +1431,7 @@ Manage optional immutable material resources in this workspace. presets discover
|
|
|
1431
1431
|
"pattern": "^sha256:[a-f0-9]{64}$"
|
|
1432
1432
|
},
|
|
1433
1433
|
"draft": {
|
|
1434
|
-
"description": "create-procedural:
|
|
1434
|
+
"description": "create-procedural: legacy layered draft, or schemaVersion 2 with explicit samplers and RGBA/atlas recipes. Exact atlas sources must exist in this library. Shape: kiln_discover({ ids: ['shape:material-draft'] }).",
|
|
1435
1435
|
"type": "object",
|
|
1436
1436
|
"propertyNames": {
|
|
1437
1437
|
"type": "string"
|
|
@@ -1439,7 +1439,7 @@ Manage optional immutable material resources in this workspace. presets discover
|
|
|
1439
1439
|
"additionalProperties": {}
|
|
1440
1440
|
},
|
|
1441
1441
|
"payload": {
|
|
1442
|
-
"description": "import: schemaVersion 1
|
|
1442
|
+
"description": "import: schemaVersion 1 or 2, complete normalized records and their exact dependencies with embedded PNG bytes. Shape: kiln_discover({ ids: ['shape:material-import'] }).",
|
|
1443
1443
|
"type": "object",
|
|
1444
1444
|
"propertyNames": {
|
|
1445
1445
|
"type": "string"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Exact saved material closure, shared by standalone asset and project delivery. */
|
|
2
2
|
import type { AssetRecord } from "./assets.js";
|
|
3
|
-
import { type MaterialLibrary, type
|
|
4
|
-
export declare function dependencyManifests(asset: AssetRecord):
|
|
5
|
-
export declare function resolveSavedAssetMaterials(asset: AssetRecord, library?: MaterialLibrary): Promise<
|
|
3
|
+
import { type MaterialLibrary, type MaterialManifest, type MaterialRecord } from "./material-library.js";
|
|
4
|
+
export declare function dependencyManifests(asset: AssetRecord): MaterialManifest[];
|
|
5
|
+
export declare function resolveSavedAssetMaterials(asset: AssetRecord, library?: MaterialLibrary): Promise<MaterialRecord[]>;
|