@hraness/slopcamera 3.2.8 → 3.3.1
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/README.md +29 -9
- package/apps/desktop/application/context.ts +6 -0
- package/apps/desktop/application/default-registry.ts +26 -0
- package/apps/desktop/application/operation.ts +14 -1
- package/apps/desktop/application/operations/index.ts +4 -0
- package/apps/desktop/application/operations/spatial-behavior.ts +151 -0
- package/apps/desktop/application/operations/spatial-direction.ts +222 -0
- package/apps/desktop/application/operations/spatial-rendered-audit.ts +174 -0
- package/apps/desktop/application/operations/spatial-review.ts +194 -0
- package/apps/desktop/application/operations/spatial-scene.ts +25 -1
- package/apps/desktop/application/registry.ts +2 -2
- package/apps/desktop/application/spatial-asset-admission.ts +201 -0
- package/apps/desktop/application/spatial-assets.ts +160 -31
- package/apps/desktop/application/spatial-render.ts +217 -20
- package/apps/desktop/application/spatial-rendered-audit.ts +273 -0
- package/apps/desktop/application/spatial-review.ts +361 -0
- package/apps/desktop/application/spatial-spz.ts +48 -3
- package/apps/desktop/application/spatial-world-import.ts +26 -5
- package/apps/desktop/application/spatial-world-metadata.ts +105 -0
- package/apps/desktop/cli/args.ts +514 -11
- package/apps/desktop/cli/capability-manifest.ts +374 -0
- package/apps/desktop/cli/cinema-renderer.ts +608 -0
- package/apps/desktop/cli/command-host-resources.ts +29 -1
- package/apps/desktop/cli/commands.ts +1188 -4
- package/apps/desktop/cli/gateway-review-provider.ts +334 -0
- package/apps/desktop/cli/help.ts +172 -6
- package/apps/desktop/cli/main.ts +3 -3
- package/apps/desktop/cli/portable-surface.ts +6 -1
- package/apps/desktop/cli/spatial-asset-service.ts +124 -0
- package/apps/desktop/cli/spatial-design-service.ts +237 -0
- package/apps/desktop/cli/spatial-generate-service.ts +370 -0
- package/apps/desktop/cli/spatial-scene-service.ts +268 -4
- package/apps/desktop/cli/workflow-code.ts +1 -1
- package/apps/desktop/code/semantic-builder.ts +24 -0
- package/apps/desktop/contracts/cinema.ts +736 -0
- package/apps/desktop/contracts/index.ts +1 -0
- package/apps/desktop/contracts/spatial-asset.ts +16 -0
- package/apps/desktop/contracts/spatial-world.ts +26 -3
- package/apps/desktop/core/cinema-plan.ts +1320 -0
- package/apps/desktop/core/index.ts +1 -0
- package/apps/desktop/core/storage.ts +75 -0
- package/apps/desktop/dist/cli/main.js +725 -273
- package/apps/desktop/html-overlay/spatial.ts +619 -57
- package/apps/desktop/workflows/cinematic-world.ts +127 -0
- package/apps/desktop/workflows/index.ts +6 -0
- package/dist/cli.js +25 -429
- package/dist/code/advanced.js +1 -269
- package/dist/code/index.js +16 -2239
- package/dist/generate.js +1 -23
- package/dist/host-resources.js +1 -25
- package/dist/index-0bbesn4m.js +35 -0
- package/dist/index-296qhpf2.js +3 -0
- package/dist/index-3cxf2c58.js +3 -0
- package/dist/index-77fjfg9f.js +3 -0
- package/dist/index-80a36bc0.js +3 -0
- package/dist/index-a3vvs3rf.js +3 -0
- package/dist/index-b3geqczn.js +5 -0
- package/dist/index-cgpkyjqa.js +3 -0
- package/dist/index-f0ptv8dm.js +7 -0
- package/dist/index-grk47tgs.js +3 -0
- package/dist/index-qry58nj2.js +10 -0
- package/dist/index.js +1 -226
- package/dist/operations.js +1 -28
- package/dist/skill-install-gx7afxsd.js +2 -0
- package/dist/vectorize/worker.js +1 -141
- package/dist/workflow.js +1 -15
- package/docs/README.md +5 -1
- package/package.json +7 -5
- package/skills/slopcamera/SKILL.md +6 -1
- package/skills/slopcamera/references/directed-scenes.md +22 -2
- package/skills/slopcamera/references/gateway-media.md +15 -1
- package/skills/slopcamera/references/image-galleries.md +166 -0
- package/skills/slopcamera/references/install.md +1 -1
- package/skills/slopcamera/references/parametric-design.md +14 -0
- package/skills/slopcamera/references/scene-building.md +32 -0
- package/skills/slopcamera/references/social-collage-banners.md +204 -0
- package/skills/slopcamera/references/workflows-sdk.md +3 -3
- package/skills/slopcamera/scripts/compose-social-collage-banner.ts +728 -0
- package/src/capability-manifest.ts +361 -0
- package/src/cli.ts +222 -4
- package/src/code/canonical-json.ts +9 -0
- package/src/generate.ts +2 -2
- package/src/icon.ts +1111 -0
- package/src/image-gallery.ts +1049 -0
- package/src/index.ts +11 -0
- package/src/mcp/boundary.ts +11 -11
- package/src/mcp/index.ts +3 -0
- package/src/mcp/server.ts +2 -1
- package/src/mcp/tools.ts +1642 -130
- package/src/mcp/types.ts +19 -0
- package/src/operations.ts +412 -0
- package/src/portable-capability-manifest.ts +67 -0
- package/src/scene-gallery.ts +184 -0
- package/src/spatial-scene/asset-admission.ts +183 -0
- package/src/spatial-scene/audit-rendered.ts +692 -0
- package/src/spatial-scene/audit.ts +617 -0
- package/src/spatial-scene/behavior-audit.ts +252 -0
- package/src/spatial-scene/behavior-bake.ts +233 -0
- package/src/spatial-scene/behavior-fns.ts +585 -0
- package/src/spatial-scene/behavior-gallery.ts +109 -0
- package/src/spatial-scene/behavior-stdlib.ts +323 -0
- package/src/spatial-scene/behavior-trace.ts +325 -0
- package/src/spatial-scene/behavior.ts +618 -0
- package/src/spatial-scene/build.ts +632 -0
- package/src/spatial-scene/camera-rig.ts +125 -0
- package/src/spatial-scene/camera-track.ts +4 -2
- package/src/spatial-scene/character.ts +124 -0
- package/src/spatial-scene/contracts.ts +75 -7
- package/src/spatial-scene/design-templates.ts +351 -0
- package/src/spatial-scene/design.ts +327 -0
- package/src/spatial-scene/direction-compile.ts +584 -0
- package/src/spatial-scene/direction.ts +122 -0
- package/src/spatial-scene/effects.ts +149 -0
- package/src/spatial-scene/evaluate.ts +109 -29
- package/src/spatial-scene/gallery.ts +277 -0
- package/src/spatial-scene/generate.ts +239 -0
- package/src/spatial-scene/geometry-native.ts +165 -0
- package/src/spatial-scene/geometry.ts +1216 -0
- package/src/spatial-scene/gltf.ts +600 -77
- package/src/spatial-scene/identity.ts +55 -2
- package/src/spatial-scene/index.ts +36 -0
- package/src/spatial-scene/inspect.ts +35 -6
- package/src/spatial-scene/material-lighting.ts +609 -0
- package/src/spatial-scene/math.ts +21 -4
- package/src/spatial-scene/motion-evidence.ts +88 -0
- package/src/spatial-scene/parametric.ts +765 -0
- package/src/spatial-scene/particle-preparation.ts +183 -0
- package/src/spatial-scene/particle.ts +195 -0
- package/src/spatial-scene/patch.ts +109 -15
- package/src/spatial-scene/performance.ts +1613 -0
- package/src/spatial-scene/probe.ts +137 -0
- package/src/spatial-scene/recipe-pack.ts +86 -0
- package/src/spatial-scene/render-effects.ts +240 -0
- package/src/spatial-scene/review.ts +639 -0
- package/src/spatial-scene/simulation-bake.ts +206 -0
- package/src/spatial-scene/simulation.ts +187 -0
- package/src/spatial-scene/solve.ts +366 -0
- package/src/spatial-scene/temporal-audit.ts +354 -0
- package/src/version.ts +1 -1
- package/dist/index-42zsesc1.js +0 -2257
- package/dist/index-7131pg1c.js +0 -1820
- package/dist/index-7308egqr.js +0 -48
- package/dist/index-8txs6fkn.js +0 -712
- package/dist/index-fava6pge.js +0 -192
- package/dist/index-h1k0fnjq.js +0 -2363
- package/dist/index-r7gdhmsp.js +0 -424
- package/dist/index-sh6xbav6.js +0 -1219
- package/dist/index-z1w83f81.js +0 -4
- package/dist/index-zfnddgay.js +0 -2029
- package/dist/skill-install-dh0ntqba.js +0 -10
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { z } from "zod"
|
|
2
|
+
|
|
3
|
+
import { createBoundedJsonValueSnapshot } from "./code/json-snapshot.js"
|
|
4
|
+
import {
|
|
5
|
+
SPATIAL_SCENE_LIMITS,
|
|
6
|
+
SpatialDigestSchema,
|
|
7
|
+
SpatialPatchOperationSchema,
|
|
8
|
+
type SpatialPatchOperation,
|
|
9
|
+
type SpatialSceneV1,
|
|
10
|
+
} from "./spatial-scene/contracts.js"
|
|
11
|
+
import {
|
|
12
|
+
parseSpatialScene,
|
|
13
|
+
parseSpatialValue,
|
|
14
|
+
SpatialSceneError,
|
|
15
|
+
spatialSceneSha256,
|
|
16
|
+
spatialValueSha256,
|
|
17
|
+
} from "./spatial-scene/identity.js"
|
|
18
|
+
import {
|
|
19
|
+
applySpatialScenePatch,
|
|
20
|
+
type SpatialSceneDiffEntry,
|
|
21
|
+
} from "./spatial-scene/patch.js"
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Scene-variant galleries for agent review.
|
|
25
|
+
*
|
|
26
|
+
* One authored base scene plus a bounded list of typed patch variants derives
|
|
27
|
+
* one candidate scene per variant — no generation, no paid calls. The desktop
|
|
28
|
+
* host renders each derived scene into a beauty still and composes the stills
|
|
29
|
+
* into the shared labelled contact sheet, so an agent reviews world variants
|
|
30
|
+
* the same way it reviews generated image candidates. The authored scene is
|
|
31
|
+
* never modified: every variant is a new derived document with its own digest,
|
|
32
|
+
* and promotion is an explicit authored patch afterward.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
export const slopcameraSceneGalleryLimits = Object.freeze({
|
|
36
|
+
variants: 16,
|
|
37
|
+
variantsBytes: 1024 * 1024,
|
|
38
|
+
idEdge: 64,
|
|
39
|
+
labelEdge: 256,
|
|
40
|
+
summaryEdge: 256,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const variantId = z
|
|
44
|
+
.string()
|
|
45
|
+
.min(1)
|
|
46
|
+
.max(slopcameraSceneGalleryLimits.idEdge)
|
|
47
|
+
.regex(
|
|
48
|
+
/^[a-z0-9][a-z0-9-]*$/u,
|
|
49
|
+
"Variant ids are lowercase slugs like dusk or golden-hour-2.",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A variant patch is the versioned spatial patch document with the expected
|
|
54
|
+
* base digest optional: when omitted the planner stamps the gallery's base
|
|
55
|
+
* scene digest, when present it must match exactly.
|
|
56
|
+
*/
|
|
57
|
+
const SceneVariantPatchSchema = z.strictObject({
|
|
58
|
+
kind: z.literal("slopcamera.spatial-scene-patch"),
|
|
59
|
+
schemaVersion: z.literal(1),
|
|
60
|
+
expectedSceneSha256: SpatialDigestSchema.optional(),
|
|
61
|
+
operations: z
|
|
62
|
+
.array(SpatialPatchOperationSchema)
|
|
63
|
+
.min(1)
|
|
64
|
+
.max(SPATIAL_SCENE_LIMITS.patchOperations),
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const SceneGalleryVariantSchema = z.strictObject({
|
|
68
|
+
id: variantId,
|
|
69
|
+
label: z.string().min(1).max(slopcameraSceneGalleryLimits.labelEdge).optional(),
|
|
70
|
+
patch: SceneVariantPatchSchema,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
export const SlopcameraSceneVariantsSchema = z.strictObject({
|
|
74
|
+
kind: z.literal("slopcamera.scene-variants"),
|
|
75
|
+
schemaVersion: z.literal(1),
|
|
76
|
+
variants: z
|
|
77
|
+
.array(SceneGalleryVariantSchema)
|
|
78
|
+
.min(1)
|
|
79
|
+
.max(slopcameraSceneGalleryLimits.variants),
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
export type SlopcameraSceneVariantSpec = z.infer<typeof SceneGalleryVariantSchema>
|
|
83
|
+
|
|
84
|
+
/** A compact per-variant description recorded as the candidate's prompt. */
|
|
85
|
+
export function summarizeSceneVariantPatch(
|
|
86
|
+
operations: readonly SpatialPatchOperation[],
|
|
87
|
+
): string {
|
|
88
|
+
const parts = operations.map(operation => {
|
|
89
|
+
const target =
|
|
90
|
+
("entityId" in operation ? operation.entityId : undefined)
|
|
91
|
+
?? ("camera" in operation ? operation.camera.cameraId : undefined)
|
|
92
|
+
?? ("channel" in operation ? operation.channel.channelId : undefined)
|
|
93
|
+
?? ("asset" in operation ? operation.asset.assetId : undefined)
|
|
94
|
+
?? ("generator" in operation ? operation.generator.generatorId : undefined)
|
|
95
|
+
?? ("entity" in operation ? operation.entity.entityId : undefined)
|
|
96
|
+
?? ("override" in operation ? operation.override.entityId : undefined)
|
|
97
|
+
return target === undefined ? operation.kind : `${operation.kind} ${target}`
|
|
98
|
+
})
|
|
99
|
+
const summary = parts.join("; ")
|
|
100
|
+
return summary.length <= slopcameraSceneGalleryLimits.summaryEdge
|
|
101
|
+
? summary
|
|
102
|
+
: `${summary.slice(0, slopcameraSceneGalleryLimits.summaryEdge - 1)}…`
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function parseSlopcameraSceneVariants(
|
|
106
|
+
input: unknown,
|
|
107
|
+
): readonly SlopcameraSceneVariantSpec[] {
|
|
108
|
+
const value = createBoundedJsonValueSnapshot(
|
|
109
|
+
input,
|
|
110
|
+
slopcameraSceneGalleryLimits.variantsBytes,
|
|
111
|
+
"scene variants",
|
|
112
|
+
).value
|
|
113
|
+
const document = parseSpatialValue(
|
|
114
|
+
SlopcameraSceneVariantsSchema,
|
|
115
|
+
value,
|
|
116
|
+
"scene variants",
|
|
117
|
+
)
|
|
118
|
+
const ids = new Set<string>()
|
|
119
|
+
for (const variant of document.variants) {
|
|
120
|
+
if (ids.has(variant.id)) {
|
|
121
|
+
throw new SpatialSceneError(
|
|
122
|
+
"invalid-data",
|
|
123
|
+
`Duplicate scene variant id ${variant.id}.`,
|
|
124
|
+
"variants",
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
ids.add(variant.id)
|
|
128
|
+
}
|
|
129
|
+
return document.variants
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface SlopcameraSceneGalleryVariant {
|
|
133
|
+
readonly index: number
|
|
134
|
+
readonly id: string
|
|
135
|
+
readonly label: string
|
|
136
|
+
readonly prompt: string
|
|
137
|
+
readonly patchSha256: string
|
|
138
|
+
readonly scene: SpatialSceneV1
|
|
139
|
+
readonly sceneSha256: string
|
|
140
|
+
readonly diff: readonly SpatialSceneDiffEntry[]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface SlopcameraSceneGalleryPlan {
|
|
144
|
+
readonly scene: SpatialSceneV1
|
|
145
|
+
readonly baseSceneSha256: string
|
|
146
|
+
readonly variants: readonly SlopcameraSceneGalleryVariant[]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Parses and applies every variant patch before returning — invalid input is
|
|
151
|
+
* rejected before the host renders anything. Each derived scene is validated
|
|
152
|
+
* again by the patch transaction itself.
|
|
153
|
+
*/
|
|
154
|
+
export function planSlopcameraSceneGallery(input: {
|
|
155
|
+
readonly scene: unknown
|
|
156
|
+
readonly variants: unknown
|
|
157
|
+
}): SlopcameraSceneGalleryPlan {
|
|
158
|
+
const scene = parseSpatialScene(input.scene)
|
|
159
|
+
const baseSceneSha256 = spatialSceneSha256(scene)
|
|
160
|
+
const specs = parseSlopcameraSceneVariants(input.variants)
|
|
161
|
+
const variants = specs.map((spec, offset) => {
|
|
162
|
+
const expected = spec.patch.expectedSceneSha256 ?? baseSceneSha256
|
|
163
|
+
if (expected !== baseSceneSha256) {
|
|
164
|
+
throw new SpatialSceneError(
|
|
165
|
+
"conflict",
|
|
166
|
+
`Variant ${spec.id} was authored against a different scene revision.`,
|
|
167
|
+
"variants",
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
const patch = { ...spec.patch, expectedSceneSha256: expected }
|
|
171
|
+
const result = applySpatialScenePatch(scene, patch)
|
|
172
|
+
return {
|
|
173
|
+
diff: result.diff,
|
|
174
|
+
id: spec.id,
|
|
175
|
+
index: offset + 1,
|
|
176
|
+
label: spec.label ?? spec.id,
|
|
177
|
+
patchSha256: spatialValueSha256(patch),
|
|
178
|
+
prompt: summarizeSceneVariantPatch(patch.operations),
|
|
179
|
+
scene: result.scene,
|
|
180
|
+
sceneSha256: result.sceneSha256,
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
return { baseSceneSha256, scene, variants }
|
|
184
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { z } from "zod"
|
|
2
|
+
import {
|
|
3
|
+
SPATIAL_SCENE_LIMITS,
|
|
4
|
+
SpatialAssetIdSchema,
|
|
5
|
+
SpatialAssetManifestSchema,
|
|
6
|
+
SpatialDigestSchema,
|
|
7
|
+
SpatialEntitySchema,
|
|
8
|
+
SpatialGeneratorIdSchema,
|
|
9
|
+
SpatialPatchOperationSchema,
|
|
10
|
+
SpatialPayloadSchema,
|
|
11
|
+
} from "./contracts.js"
|
|
12
|
+
import { SPATIAL_GLB_LIMITS, SPATIAL_GLB_PROFILE, SPATIAL_GLB_PROFILE_V1, SPATIAL_GLB_RIGGED_PROFILE } from "./gltf.js"
|
|
13
|
+
import { SPATIAL_GEOMETRY_PROFILE } from "./geometry.js"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Canonical document contracts produced by `scene asset admit` and consumed by
|
|
17
|
+
* `scene audit --asset-bounds` and the MCP `audit_scene` tool. They live in the
|
|
18
|
+
* portable layer so every surface shares one strict schema; hosts only read or
|
|
19
|
+
* write the documents.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Affine guard bound. Node matrices stay below math.MAX_ABS_COMPONENT (1e12)
|
|
24
|
+
* and decoded vertices below 1e6, so every transformed vertex stays under
|
|
25
|
+
* ~3e18; 1e18 bounds the reachable evaluated range with margin.
|
|
26
|
+
*/
|
|
27
|
+
const boundsComponent = z.number().finite().min(-1e18).max(1e18)
|
|
28
|
+
const boundsVector = z.tuple([boundsComponent, boundsComponent, boundsComponent])
|
|
29
|
+
export const SpatialBoundsSchema = z.strictObject({ min: boundsVector, max: boundsVector })
|
|
30
|
+
.refine(value => value.min.every((component, index) => component <= value.max[index]!), "Bounds min must not exceed max.")
|
|
31
|
+
export type SpatialBounds = Readonly<z.infer<typeof SpatialBoundsSchema>>
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Derived facts bound to one admitted asset payload. The document persists as a
|
|
35
|
+
* canonical JSON payload behind a sibling `metadata` manifest that depends on
|
|
36
|
+
* its subject asset, so a scene may omit it without invalidating the subject
|
|
37
|
+
* manifest under the unchanged `gltf` interpretation shape.
|
|
38
|
+
*/
|
|
39
|
+
const factsUnit = z.number().finite().min(0).max(1)
|
|
40
|
+
/**
|
|
41
|
+
* One declared GLB material's capability row. `maps` lists present texture
|
|
42
|
+
* slots in fixed order; `emissiveLinear` records a declared emissiveFactor.
|
|
43
|
+
*/
|
|
44
|
+
export const SpatialAssetMaterialFactSchema = z.strictObject({
|
|
45
|
+
name: z.string().max(1_024).optional(),
|
|
46
|
+
alphaMode: z.enum(["OPAQUE", "MASK", "BLEND"]),
|
|
47
|
+
doubleSided: z.boolean(),
|
|
48
|
+
maps: z.array(z.enum(["baseColor", "metallicRoughness", "normal", "occlusion", "emissive", "clearcoat", "clearcoatRoughness", "clearcoatNormal", "transmission", "sheenColor", "sheenRoughness", "anisotropy"])).max(12),
|
|
49
|
+
textureTransforms: z.array(z.strictObject({ map: z.string().min(1).max(64), offset: z.tuple([z.number().finite(), z.number().finite()]), rotation: z.number().finite().min(-Math.PI).max(Math.PI), scale: z.tuple([z.number().finite(), z.number().finite()]) })).max(12).default([]),
|
|
50
|
+
emissiveLinear: z.tuple([factsUnit, factsUnit, factsUnit]).optional(),
|
|
51
|
+
emissiveStrength: z.number().finite().min(0).max(100_000).optional(),
|
|
52
|
+
/** PBR extension indicators: present only when the GLB declares the corresponding KHR extension on this material. */
|
|
53
|
+
clearcoat: z.strictObject({ factor: factsUnit, roughness: factsUnit }).optional(),
|
|
54
|
+
transmission: z.strictObject({ factor: factsUnit }).optional(),
|
|
55
|
+
sheen: z.strictObject({ colorLinear: z.tuple([factsUnit, factsUnit, factsUnit]), roughness: factsUnit }).optional(),
|
|
56
|
+
anisotropy: z.strictObject({ strength: z.number().finite().min(-1).max(1), rotation: z.number().finite().min(0).max(2 * Math.PI) }).optional(),
|
|
57
|
+
ior: z.number().finite().min(1).max(5).optional(),
|
|
58
|
+
})
|
|
59
|
+
const SpatialAssetRigFactsSchema = z.strictObject({
|
|
60
|
+
profile: z.literal(SPATIAL_GLB_RIGGED_PROFILE),
|
|
61
|
+
skins: z.array(z.strictObject({
|
|
62
|
+
name: z.string().max(1_024).optional(),
|
|
63
|
+
jointNodeIndices: z.array(z.number().int().min(0).max(SPATIAL_GLB_LIMITS.nodes - 1)).min(1).max(SPATIAL_GLB_LIMITS.jointsPerSkin),
|
|
64
|
+
inverseBindMatricesAccessor: z.number().int().min(0).max(SPATIAL_GLB_LIMITS.accessors - 1),
|
|
65
|
+
})).min(1).max(SPATIAL_GLB_LIMITS.skins),
|
|
66
|
+
morphTargets: z.array(z.array(z.array(z.strictObject({
|
|
67
|
+
name: z.string().min(1).max(1_024), hasPosition: z.boolean(), hasNormal: z.boolean(),
|
|
68
|
+
})).max(SPATIAL_GLB_LIMITS.morphTargetsPerPrimitive)).max(SPATIAL_GLB_LIMITS.primitives)).max(SPATIAL_GLB_LIMITS.meshes),
|
|
69
|
+
clips: z.array(z.strictObject({
|
|
70
|
+
name: z.string().max(1_024).optional(),
|
|
71
|
+
durationSeconds: z.number().finite().min(0).max(SPATIAL_GLB_LIMITS.durationSeconds),
|
|
72
|
+
channels: z.array(z.strictObject({
|
|
73
|
+
nodeIndex: z.number().int().min(0).max(SPATIAL_GLB_LIMITS.nodes - 1),
|
|
74
|
+
path: z.enum(["translation", "rotation", "scale", "weights"]),
|
|
75
|
+
})).max(SPATIAL_GLB_LIMITS.channels),
|
|
76
|
+
})).max(SPATIAL_GLB_LIMITS.clips),
|
|
77
|
+
})
|
|
78
|
+
const meterBound = z.number().finite().min(0).max(1_000_000)
|
|
79
|
+
const lodCoordinate = z.number().finite().min(-1e6).max(1e6)
|
|
80
|
+
const lodVector = z.tuple([lodCoordinate, lodCoordinate, lodCoordinate])
|
|
81
|
+
/** One deterministic LOD level: an alternate retained GLB payload plus its selection rule. */
|
|
82
|
+
export const SpatialAssetLodSchema = z.strictObject({
|
|
83
|
+
level: z.number().int().min(0).max(7),
|
|
84
|
+
/** Asset id of the retained payload for this level (level 0 is the facts subject). */
|
|
85
|
+
assetId: SpatialAssetIdSchema,
|
|
86
|
+
sha256: SpatialDigestSchema,
|
|
87
|
+
/** Selection rule: pick the greatest level whose switch distance is met. */
|
|
88
|
+
switchDistanceM: meterBound,
|
|
89
|
+
})
|
|
90
|
+
export type SpatialAssetLod = Readonly<z.infer<typeof SpatialAssetLodSchema>>
|
|
91
|
+
|
|
92
|
+
/** Bounded collision proxies in the subject's model space; hulls reference a retained native artifact. */
|
|
93
|
+
export const SpatialCollisionProxySchema = z.discriminatedUnion("kind", [
|
|
94
|
+
z.strictObject({ kind: z.literal("box"), center: lodVector, halfExtents: z.tuple([meterBound, meterBound, meterBound]).refine(v => v.every(c => c > 0), "Box half extents must be positive.") }),
|
|
95
|
+
z.strictObject({ kind: z.literal("sphere"), center: lodVector, radius: meterBound.refine(v => v > 0, "Sphere radius must be positive.") }),
|
|
96
|
+
z.strictObject({ kind: z.literal("capsule"), center: lodVector, axis: z.enum(["x", "y", "z"]), radius: meterBound.refine(v => v > 0, "Capsule radius must be positive."), halfLength: meterBound }),
|
|
97
|
+
z.strictObject({ kind: z.literal("hull-hull"), artifactSha256: SpatialDigestSchema }),
|
|
98
|
+
])
|
|
99
|
+
export type SpatialCollisionProxy = Readonly<z.infer<typeof SpatialCollisionProxySchema>>
|
|
100
|
+
|
|
101
|
+
/** Binding to one verified retained-native exchange: request and receipt digests plus emitted outputs. */
|
|
102
|
+
export const SpatialRetainedArtifactSchema = z.strictObject({
|
|
103
|
+
operation: z.enum(["mesh-repair", "complex-csg", "uv-atlas", "decimation", "hull-decomposition", "glb-emission"]),
|
|
104
|
+
requestSha256: SpatialDigestSchema,
|
|
105
|
+
receiptSha256: SpatialDigestSchema,
|
|
106
|
+
outputs: z.array(z.strictObject({ sha256: SpatialDigestSchema, bytes: z.number().int().safe().min(1).max(SPATIAL_GLB_LIMITS.bytes) })).min(1).max(16),
|
|
107
|
+
})
|
|
108
|
+
export type SpatialRetainedArtifact = Readonly<z.infer<typeof SpatialRetainedArtifactSchema>>
|
|
109
|
+
|
|
110
|
+
/** Facts for a parametric-geometry subject: generator identity, spec/parameter digests, LOD set and collision proxies. */
|
|
111
|
+
export const SpatialAssetGeneratorFactsSchema = z.strictObject({
|
|
112
|
+
generatorId: SpatialGeneratorIdSchema,
|
|
113
|
+
/** Parametric generator kind (wall, stairs, arch, …) as declared by the spec. */
|
|
114
|
+
parametricKind: z.string().min(1).max(64),
|
|
115
|
+
/** Canonical digest of the bounded parametric spec document. */
|
|
116
|
+
specSha256: SpatialDigestSchema,
|
|
117
|
+
/** Digest of the declared generator parameters (matches the generator record). */
|
|
118
|
+
parametersSha256: SpatialDigestSchema,
|
|
119
|
+
/** Sorted LOD set; level 0 must be present and is the facts subject. */
|
|
120
|
+
lods: z.array(SpatialAssetLodSchema).min(1).max(8),
|
|
121
|
+
/** Model-space collision proxies consumed by audits and hosts. */
|
|
122
|
+
collision: z.array(SpatialCollisionProxySchema).max(16),
|
|
123
|
+
/** Verified retained-native exchanges whose outputs this subject binds. */
|
|
124
|
+
retainedArtifacts: z.array(SpatialRetainedArtifactSchema).max(8).optional(),
|
|
125
|
+
})
|
|
126
|
+
export type SpatialAssetGeneratorFacts = Readonly<z.infer<typeof SpatialAssetGeneratorFactsSchema>>
|
|
127
|
+
|
|
128
|
+
export const SpatialAssetFactsV1Schema = z.strictObject({
|
|
129
|
+
kind: z.literal("slopcamera.spatial-asset-facts"), schemaVersion: z.literal(1),
|
|
130
|
+
subject: SpatialPayloadSchema,
|
|
131
|
+
subjectManifestSha256: SpatialDigestSchema,
|
|
132
|
+
profile: z.enum([SPATIAL_GLB_PROFILE, SPATIAL_GLB_PROFILE_V1, SPATIAL_GLB_RIGGED_PROFILE, SPATIAL_GEOMETRY_PROFILE]),
|
|
133
|
+
nodeCount: z.number().int().min(1).max(SPATIAL_GLB_LIMITS.nodes),
|
|
134
|
+
clipDurationsSeconds: z.array(z.number().finite().min(0).max(SPATIAL_GLB_LIMITS.durationSeconds)).max(SPATIAL_GLB_LIMITS.clips),
|
|
135
|
+
bounds: z.strictObject({ modelSpace: SpatialBoundsSchema, sceneSpace: SpatialBoundsSchema }),
|
|
136
|
+
/** Present on newly admitted assets; absent facts predate the material-facts extension. */
|
|
137
|
+
materials: z.array(SpatialAssetMaterialFactSchema).max(SPATIAL_GLB_LIMITS.materials).optional(),
|
|
138
|
+
/** Exact skeleton, morph and channel facts for the additive rigged profile. */
|
|
139
|
+
rig: SpatialAssetRigFactsSchema.optional(),
|
|
140
|
+
/** Generator identity, LOD set, collision proxies and retained artifacts for parametric subjects. */
|
|
141
|
+
generator: SpatialAssetGeneratorFactsSchema.optional(),
|
|
142
|
+
}).superRefine((facts, context) => {
|
|
143
|
+
if ((facts.profile === SPATIAL_GLB_RIGGED_PROFILE) !== (facts.rig !== undefined)) context.addIssue({ code: "custom", path: ["rig"], message: "Rig facts must be present exactly for the rigged GLB profile." })
|
|
144
|
+
if ((facts.profile === SPATIAL_GEOMETRY_PROFILE) !== (facts.generator !== undefined)) context.addIssue({ code: "custom", path: ["generator"], message: "Generator facts must be present exactly for the parametric geometry profile." })
|
|
145
|
+
if (facts.generator !== undefined) {
|
|
146
|
+
const levels = new Set(facts.generator.lods.map(lod => lod.level))
|
|
147
|
+
if (levels.size !== facts.generator.lods.length || !levels.has(0)) context.addIssue({ code: "custom", path: ["generator", "lods"], message: "LOD levels must be unique and include level 0." })
|
|
148
|
+
const distances = facts.generator.lods.map(lod => lod.switchDistanceM)
|
|
149
|
+
if (distances.some((value, index) => index > 0 && value <= distances[index - 1]!)) context.addIssue({ code: "custom", path: ["generator", "lods"], message: "LOD switch distances must strictly increase." })
|
|
150
|
+
const hulls = facts.generator.collision.filter(proxy => proxy.kind === "hull-hull")
|
|
151
|
+
for (const hull of hulls) {
|
|
152
|
+
const bound = facts.generator.retainedArtifacts?.some(artifact => artifact.outputs.some(output => output.sha256 === hull.artifactSha256)) ?? false
|
|
153
|
+
if (!bound) context.addIssue({ code: "custom", path: ["generator", "collision"], message: "Hull-hull proxies must reference a retained artifact output." })
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
export type SpatialAssetFactsV1 = Readonly<z.infer<typeof SpatialAssetFactsV1Schema>>
|
|
158
|
+
|
|
159
|
+
export const SpatialPublishedArtifactSchema = z.strictObject({
|
|
160
|
+
path: z.string().min(1).max(1_024),
|
|
161
|
+
sha256: SpatialDigestSchema,
|
|
162
|
+
bytes: z.number().int().safe().min(1).max(SPATIAL_GLB_LIMITS.bytes),
|
|
163
|
+
disposition: z.enum(["created", "exists"]),
|
|
164
|
+
})
|
|
165
|
+
export type SpatialPublishedArtifact = Readonly<z.infer<typeof SpatialPublishedArtifactSchema>>
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* The deterministic `scene asset admit` output document. Artifact paths are
|
|
169
|
+
* relative to the directory containing this document; `operations` apply in
|
|
170
|
+
* order inside one `SpatialScenePatchV1` alongside the caller's expected scene
|
|
171
|
+
* digest.
|
|
172
|
+
*/
|
|
173
|
+
export const SpatialAssetAdmissionV1Schema = z.strictObject({
|
|
174
|
+
kind: z.literal("slopcamera.spatial-asset-admission"), schemaVersion: z.literal(1),
|
|
175
|
+
manifest: SpatialAssetManifestSchema,
|
|
176
|
+
factsManifest: SpatialAssetManifestSchema,
|
|
177
|
+
facts: SpatialAssetFactsV1Schema,
|
|
178
|
+
bounds: z.strictObject({ modelSpace: SpatialBoundsSchema, sceneSpace: SpatialBoundsSchema }),
|
|
179
|
+
entity: SpatialEntitySchema,
|
|
180
|
+
artifacts: z.strictObject({ payload: SpatialPublishedArtifactSchema, facts: SpatialPublishedArtifactSchema }),
|
|
181
|
+
operations: z.array(SpatialPatchOperationSchema).min(2).max(SPATIAL_SCENE_LIMITS.patchOperations),
|
|
182
|
+
})
|
|
183
|
+
export type SpatialAssetAdmissionV1 = Readonly<z.infer<typeof SpatialAssetAdmissionV1Schema>>
|