@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
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
|
-
import type { MaterialManifestV1 } from '../material-library';
|
|
2
|
+
import type { MaterialManifest, MaterialManifestV1 } from '../material-library';
|
|
3
|
+
import { materialApiBase, materialViewerSelection } from './material-preview';
|
|
4
|
+
import { materialPreviewPanel } from './material-preview-panel';
|
|
3
5
|
import type { MaterialPresetSummary } from '../material-presets';
|
|
4
6
|
import type { ProjectRevision } from '../projects';
|
|
5
7
|
import { el, node, request, notice, empty, recordDetails } from './dom';
|
|
6
8
|
import { materialPresetRequest } from './material-preset-form';
|
|
7
9
|
|
|
8
10
|
export function mountMaterials(updated: () => Promise<void>) {
|
|
9
|
-
let materials:
|
|
11
|
+
let materials: MaterialManifest[] = [];
|
|
10
12
|
let projects: ProjectRevision[] = [];
|
|
11
13
|
let presets: MaterialPresetSummary[] = [];
|
|
12
14
|
const projectSelect = el<HTMLSelectElement>('materials-project');
|
|
@@ -121,8 +123,7 @@ export function mountMaterials(updated: () => Promise<void>) {
|
|
|
121
123
|
submit.disabled = false;
|
|
122
124
|
}
|
|
123
125
|
};
|
|
124
|
-
const base =
|
|
125
|
-
`/api/materials/${encodeURIComponent(material.materialId)}/${encodeURIComponent(material.revisionId)}`;
|
|
126
|
+
const base = materialApiBase;
|
|
126
127
|
function render() {
|
|
127
128
|
const project = projects.find((p) => p.projectId === projectSelect.value);
|
|
128
129
|
const dependencies = el('material-dependencies');
|
|
@@ -268,6 +269,24 @@ export function mountMaterials(updated: () => Promise<void>) {
|
|
|
268
269
|
maps.append(link);
|
|
269
270
|
}
|
|
270
271
|
card.append(maps);
|
|
272
|
+
const inspection = node('details', undefined, 'material-map-inspection');
|
|
273
|
+
inspection.append(node('summary', 'Inspect maps and atlas regions'));
|
|
274
|
+
let inspected = false;
|
|
275
|
+
inspection.ontoggle = () => {
|
|
276
|
+
if (inspection.open && !inspected) {
|
|
277
|
+
inspected = true;
|
|
278
|
+
inspection.append(materialPreviewPanel(material));
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
const selected = materialViewerSelection(location.search);
|
|
282
|
+
if (
|
|
283
|
+
selected?.materialId === material.materialId &&
|
|
284
|
+
selected.revisionId === material.revisionId
|
|
285
|
+
) {
|
|
286
|
+
inspection.open = true;
|
|
287
|
+
card.dataset.selectedMaterial = 'true';
|
|
288
|
+
}
|
|
289
|
+
card.append(inspection);
|
|
271
290
|
const locked = project?.materialDependencies.find(
|
|
272
291
|
(d) => d.resourceId === material.materialId,
|
|
273
292
|
);
|
|
@@ -314,8 +333,18 @@ export function mountMaterials(updated: () => Promise<void>) {
|
|
|
314
333
|
}
|
|
315
334
|
}
|
|
316
335
|
const refresh = async () => {
|
|
317
|
-
materials = (await request<{ materials:
|
|
336
|
+
materials = (await request<{ materials: MaterialManifest[] }>('/api/materials')).materials;
|
|
318
337
|
render();
|
|
338
|
+
const selected = materialViewerSelection(location.search);
|
|
339
|
+
if (
|
|
340
|
+
selected &&
|
|
341
|
+
!materials.some(
|
|
342
|
+
(m) => m.materialId === selected.materialId && m.revisionId === selected.revisionId,
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
notice(
|
|
346
|
+
`Exact material revision unavailable: ${selected.materialId} at ${selected.revisionId}. No newer revision was selected.`,
|
|
347
|
+
);
|
|
319
348
|
};
|
|
320
349
|
el('material-search').oninput = render;
|
|
321
350
|
projectSelect.onchange = render;
|
package/src/viewer/navigation.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { materialViewerSelection } from './material-preview';
|
|
1
2
|
export const workspaceSections = ['projects', 'live', 'library', 'materials'] as const;
|
|
2
3
|
|
|
3
4
|
export function initialWorkspaceSection(search: string, remembered: string | null | undefined) {
|
|
4
5
|
const params = new URLSearchParams(search);
|
|
5
6
|
if (params.has('asset') || params.has('open')) return 'library';
|
|
7
|
+
if (materialViewerSelection(search)) return 'materials';
|
|
6
8
|
return remembered && workspaceSections.some((section) => section === remembered)
|
|
7
9
|
? remembered
|
|
8
10
|
: 'library';
|
package/src/views/camera.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { Vector3, Matrix4, Euler, Group, type Object3D } from 'three';
|
|
2
2
|
import { collectTriangles, compositingOrder, measureBounds, orbitDir } from './raster';
|
|
3
3
|
import { resolveBackdrop, type BackdropId } from './background';
|
|
4
|
+
import { cloneReviewScene } from './scene-copy';
|
|
5
|
+
import { visibleNormalSign } from './face-policy';
|
|
6
|
+
import { prepareTriangleCoverage } from './triangle-coverage';
|
|
4
7
|
|
|
5
8
|
export type CameraVec3 = [number, number, number];
|
|
6
9
|
export type CameraBounds = { min: CameraVec3; max: CameraVec3 };
|
|
@@ -409,7 +412,11 @@ export function resolveAssetCamera(root: unknown, shot: CameraShotV1 = {}): Reso
|
|
|
409
412
|
measureBounds(root),
|
|
410
413
|
);
|
|
411
414
|
if (request.projection === 'orthographic')
|
|
412
|
-
camera = {
|
|
415
|
+
camera = {
|
|
416
|
+
...fit,
|
|
417
|
+
near: request.near ?? fit.near,
|
|
418
|
+
far: request.far ?? fit.far,
|
|
419
|
+
};
|
|
413
420
|
else {
|
|
414
421
|
const fov = finite(request.fovDeg ?? 50, 'fovDeg');
|
|
415
422
|
if (fov <= 0 || fov >= 180) throw new Error('invalid fovDeg');
|
|
@@ -487,9 +494,9 @@ export async function withCameraVisibility<T>(
|
|
|
487
494
|
* A derivative GLB
|
|
488
495
|
* serialized from a scene with hidden nodes (authored, or isolated by
|
|
489
496
|
* {@link withCameraVisibility}) would ship them to the GPU. Every node with
|
|
490
|
-
* `visible = false`
|
|
491
|
-
*
|
|
492
|
-
* restores `.visible` itself.
|
|
497
|
+
* `visible = false` removes drawing from its subtree. Required skeleton ancestors remain
|
|
498
|
+
* as transforms without drawing payloads. Returns the root itself when nothing is hidden,
|
|
499
|
+
* else an independent pruned hierarchy/skeleton; the caller restores `.visible` itself.
|
|
493
500
|
*/
|
|
494
501
|
export function withoutHiddenMeshes(root: Object3D): Object3D {
|
|
495
502
|
if (root.visible === false) {
|
|
@@ -504,13 +511,7 @@ export function withoutHiddenMeshes(root: Object3D): Object3D {
|
|
|
504
511
|
if (hidden(node)) any = true;
|
|
505
512
|
});
|
|
506
513
|
if (!any) return root;
|
|
507
|
-
|
|
508
|
-
const drop: Object3D[] = [];
|
|
509
|
-
copy.traverse((node) => {
|
|
510
|
-
if (hidden(node)) drop.push(node);
|
|
511
|
-
});
|
|
512
|
-
for (const node of drop) node.removeFromParent();
|
|
513
|
-
return copy;
|
|
514
|
+
return cloneReviewScene(root, { omitHidden: true });
|
|
514
515
|
}
|
|
515
516
|
/** Flat geometry renderer with exact orthographic/perspective projection and near/far clipping. */
|
|
516
517
|
export function rasterizeCamera(
|
|
@@ -531,6 +532,7 @@ export function rasterizeCamera(
|
|
|
531
532
|
const out = new Uint8Array(size * size * 3);
|
|
532
533
|
for (let i = 0; i < size * size; i++) out.set(bg, i * 3);
|
|
533
534
|
const depth = new Float64Array(size * size).fill(Infinity);
|
|
535
|
+
const weights = new Float64Array(3);
|
|
534
536
|
const clip = (points: Vector3[], plane: number, near: boolean) => {
|
|
535
537
|
const result: Vector3[] = [];
|
|
536
538
|
for (let i = 0; i < points.length; i++) {
|
|
@@ -539,7 +541,14 @@ export function rasterizeCamera(
|
|
|
539
541
|
const insideA = near ? a.z >= plane : a.z <= plane,
|
|
540
542
|
insideB = near ? b.z >= plane : b.z <= plane;
|
|
541
543
|
if (insideA) result.push(a);
|
|
542
|
-
if (insideA !== insideB)
|
|
544
|
+
if (insideA !== insideB) {
|
|
545
|
+
// Shared intersections must round identically for either edge direction.
|
|
546
|
+
const low = a.z < b.z ? a : b,
|
|
547
|
+
high = a.z < b.z ? b : a;
|
|
548
|
+
const intersection = low.clone().lerp(high, (plane - low.z) / (high.z - low.z));
|
|
549
|
+
intersection.z = plane;
|
|
550
|
+
result.push(intersection);
|
|
551
|
+
}
|
|
543
552
|
}
|
|
544
553
|
return result;
|
|
545
554
|
};
|
|
@@ -558,15 +567,16 @@ export function rasterizeCamera(
|
|
|
558
567
|
.clone()
|
|
559
568
|
.sub(world[0]!)
|
|
560
569
|
.cross(world[2]!.clone().sub(world[0]!))
|
|
561
|
-
.normalize()
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
570
|
+
.normalize()
|
|
571
|
+
.multiplyScalar(tri.windingParity);
|
|
572
|
+
const normalSign = visibleNormalSign(
|
|
573
|
+
normal.dot(camera.projection === 'perspective' ? position.clone().sub(world[0]!) : z),
|
|
574
|
+
tri.side,
|
|
575
|
+
backfaceCull,
|
|
576
|
+
);
|
|
577
|
+
if (normalSign === 0) continue;
|
|
568
578
|
if (tri.alpha <= 0) continue;
|
|
569
|
-
const light = Math.min(1, 0.25 + 1.1 * Math.max(0, normal.dot(key)));
|
|
579
|
+
const light = Math.min(1, 0.25 + 1.1 * Math.max(0, normal.dot(key) * normalSign));
|
|
570
580
|
const color = tri.color.map((c) => Math.round(srgb(c * light) * 255));
|
|
571
581
|
let polygon = world.map((p) => {
|
|
572
582
|
const d = p.clone().sub(position);
|
|
@@ -586,28 +596,15 @@ export function rasterizeCamera(
|
|
|
586
596
|
];
|
|
587
597
|
});
|
|
588
598
|
const [a, b, c] = screen as [number[], number[], number[]];
|
|
589
|
-
const
|
|
590
|
-
if (
|
|
591
|
-
for (
|
|
592
|
-
let
|
|
593
|
-
|
|
594
|
-
py++
|
|
595
|
-
)
|
|
596
|
-
for (
|
|
597
|
-
let px = Math.max(0, Math.floor(Math.min(...screen.map((s) => s[0]!))));
|
|
598
|
-
px <= Math.min(size - 1, Math.ceil(Math.max(...screen.map((s) => s[0]!))));
|
|
599
|
-
px++
|
|
600
|
-
) {
|
|
601
|
-
const w0 =
|
|
602
|
-
((b[0]! - a[0]!) * (py + 0.5 - a[1]!) - (b[1]! - a[1]!) * (px + 0.5 - a[0]!)) / area,
|
|
603
|
-
w1 =
|
|
604
|
-
((c[0]! - b[0]!) * (py + 0.5 - b[1]!) - (c[1]! - b[1]!) * (px + 0.5 - b[0]!)) / area,
|
|
605
|
-
w2 = 1 - w0 - w1;
|
|
606
|
-
if (w0 < 0 || w1 < 0 || w2 < 0) continue;
|
|
599
|
+
const triangle = prepareTriangleCoverage(a[0]!, a[1]!, b[0]!, b[1]!, c[0]!, c[1]!, size);
|
|
600
|
+
if (!triangle) continue;
|
|
601
|
+
for (let py = triangle.minY; py <= triangle.maxY; py++)
|
|
602
|
+
for (let px = triangle.minX; px <= triangle.maxX; px++) {
|
|
603
|
+
if (!triangle.sample(px + 0.5, py + 0.5, weights)) continue;
|
|
607
604
|
const d =
|
|
608
605
|
camera.projection === 'perspective'
|
|
609
|
-
? 1 / (
|
|
610
|
-
:
|
|
606
|
+
? 1 / (weights[0]! / p[0]!.z + weights[1]! / p[1]!.z + weights[2]! / p[2]!.z)
|
|
607
|
+
: weights[0]! * p[0]!.z + weights[1]! * p[1]!.z + weights[2]! * p[2]!.z;
|
|
611
608
|
const index = py * size + px;
|
|
612
609
|
if (d >= depth[index]!) continue;
|
|
613
610
|
depth[index] = d;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Standard material sides, kept numeric for cross-realm and flat GLB inputs. */
|
|
2
|
+
export type SurfaceSide = 0 | 1 | 2;
|
|
3
|
+
|
|
4
|
+
/** Match Three's affine front-face convention; a singular transform does not flip.
|
|
5
|
+
* Collapsed triangles are discarded by rasterization. No inverse is needed. */
|
|
6
|
+
export function worldWindingParity(m: ArrayLike<number>): 1 | -1 {
|
|
7
|
+
const determinant =
|
|
8
|
+
m[0]! * (m[5]! * m[10]! - m[9]! * m[6]!) -
|
|
9
|
+
m[4]! * (m[1]! * m[10]! - m[9]! * m[2]!) +
|
|
10
|
+
m[8]! * (m[1]! * m[6]! - m[5]! * m[2]!);
|
|
11
|
+
return determinant < 0 ? -1 : 1;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Facing uses deformed triangle winding corrected for the mesh world transform.
|
|
15
|
+
* Zero means culled; +/-1 orients the visible flat lighting normal. Disabling
|
|
16
|
+
* culling is a diagnostic two-sided override, matching GPU review derivatives. */
|
|
17
|
+
export function visibleNormalSign(facing: number, side: SurfaceSide, cull: boolean): -1 | 0 | 1 {
|
|
18
|
+
if (cull && ((side === 0 && facing <= 0) || (side === 1 && facing >= 0))) return 0;
|
|
19
|
+
return facing < 0 ? -1 : 1;
|
|
20
|
+
}
|