pelican-engine 0.0.0-stage → 0.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.
Files changed (192) hide show
  1. package/AGENTS.md +147 -0
  2. package/LICENSE +22 -0
  3. package/README.md +129 -2
  4. package/distribution.json +197 -0
  5. package/docs/AI-WORKFLOW.md +517 -0
  6. package/docs/ARCHITECTURE.md +268 -0
  7. package/docs/CAPABILITIES.md +264 -0
  8. package/docs/DECISIONS.md +107 -0
  9. package/docs/M3-USAGE.md +192 -0
  10. package/docs/PELICAN-MIGRATION.md +38 -0
  11. package/docs/RENDER-BACKENDS.md +50 -0
  12. package/docs/ROADMAP.md +239 -0
  13. package/docs/THIRD-PARTY-NOTICES.md +151 -0
  14. package/docs/api/REFERENCE.md +1435 -0
  15. package/docs/api/index.json +1139 -0
  16. package/docs/npm/DISTRIBUTION.md +130 -0
  17. package/docs/npm/GAME-AUTHOR.md +147 -0
  18. package/engine.manifest.json +15 -0
  19. package/lib/assets/audio.js +67 -0
  20. package/lib/assets/errors.js +36 -0
  21. package/lib/assets/glb.js +277 -0
  22. package/lib/assets/index.js +53 -0
  23. package/lib/assets/jpeg.js +199 -0
  24. package/lib/assets/model.js +558 -0
  25. package/lib/assets/optimize.js +95 -0
  26. package/lib/assets/texture.js +187 -0
  27. package/lib/compiler/distribution.js +17 -0
  28. package/lib/compiler/extract.js +425 -0
  29. package/lib/compiler/graph.js +366 -0
  30. package/lib/compiler/index.js +574 -0
  31. package/lib/compiler/normalize.js +1194 -0
  32. package/lib/compiler/source-map.js +25 -0
  33. package/lib/compiler/types.js +2 -0
  34. package/lib/compiler/version.js +93 -0
  35. package/lib/experiments/simulation/index.js +2 -0
  36. package/lib/experiments/simulation/math.js +41 -0
  37. package/lib/experiments/simulation/probe.js +215 -0
  38. package/lib/experiments/simulation/scenes.js +192 -0
  39. package/lib/experiments/simulation/solver.js +346 -0
  40. package/lib/experiments/simulation/types.js +1 -0
  41. package/lib/experiments/world/adapters.js +30 -0
  42. package/lib/experiments/world/geometry.js +180 -0
  43. package/lib/experiments/world/index.js +2 -0
  44. package/lib/experiments/world/probe.js +288 -0
  45. package/lib/experiments/world/types.js +1 -0
  46. package/lib/experiments/world/world.js +599 -0
  47. package/lib/navigation/index.js +157 -0
  48. package/lib/physics/index.js +909 -0
  49. package/lib/protocol/index.js +92 -0
  50. package/lib/renderer/acceleration.js +609 -0
  51. package/lib/renderer/browser.js +2015 -0
  52. package/lib/renderer/bsdf.js +260 -0
  53. package/lib/renderer/checkpoint.js +88 -0
  54. package/lib/renderer/compute-executor.js +1034 -0
  55. package/lib/renderer/compute-texture-assets.js +49 -0
  56. package/lib/renderer/compute-view-contract.js +1 -0
  57. package/lib/renderer/denoiser.js +361 -0
  58. package/lib/renderer/dielectric.js +360 -0
  59. package/lib/renderer/embedded-image-pixels.js +368 -0
  60. package/lib/renderer/emissive-lights.js +67 -0
  61. package/lib/renderer/environment-importance.js +55 -0
  62. package/lib/renderer/environment.js +346 -0
  63. package/lib/renderer/gameplay.js +464 -0
  64. package/lib/renderer/geometry.js +103 -0
  65. package/lib/renderer/graph-expressions.js +485 -0
  66. package/lib/renderer/graph-tsl.js +141 -0
  67. package/lib/renderer/graph.js +214 -0
  68. package/lib/renderer/managed-texture-sampling.js +29 -0
  69. package/lib/renderer/material-graphs.js +297 -0
  70. package/lib/renderer/medium.js +348 -0
  71. package/lib/renderer/models.js +1053 -0
  72. package/lib/renderer/motion-shader.js +40 -0
  73. package/lib/renderer/path-shader.js +196 -0
  74. package/lib/renderer/pathtrace.js +324 -0
  75. package/lib/renderer/postprocess-contract.js +1 -0
  76. package/lib/renderer/postprocess.js +494 -0
  77. package/lib/renderer/render-buffers.js +132 -0
  78. package/lib/renderer/render-textures.js +155 -0
  79. package/lib/renderer/startup.js +50 -0
  80. package/lib/renderer/surface-capture.js +199 -0
  81. package/lib/renderer/surface-gpu.js +270 -0
  82. package/lib/renderer/surface-motion.js +84 -0
  83. package/lib/renderer/surface-ray-shader.js +91 -0
  84. package/lib/renderer/surface-shader.js +88 -0
  85. package/lib/renderer/surface-tracer.js +1720 -0
  86. package/lib/renderer/surface.js +1425 -0
  87. package/lib/renderer/temporal-filter.js +512 -0
  88. package/lib/renderer/texture-pixels.js +207 -0
  89. package/lib/renderer/traversal-nodes.js +54 -0
  90. package/lib/renderer/ui.js +446 -0
  91. package/lib/renderer/vertex-bounds.js +127 -0
  92. package/lib/renderer/vertex-contract.js +1 -0
  93. package/lib/renderer/vertex-graphs.js +197 -0
  94. package/lib/renderer/volume-shader.js +50 -0
  95. package/lib/runtime/compute-contract.js +1 -0
  96. package/lib/runtime/compute-service.js +674 -0
  97. package/lib/runtime/contracts.js +1 -0
  98. package/lib/runtime/error.js +37 -0
  99. package/lib/runtime/events.js +189 -0
  100. package/lib/runtime/game-services.js +772 -0
  101. package/lib/runtime/host.js +839 -0
  102. package/lib/runtime/material-parameters.js +145 -0
  103. package/lib/runtime/plugins.js +1 -0
  104. package/lib/runtime/postprocess-contract.js +1 -0
  105. package/lib/runtime/postprocess.js +189 -0
  106. package/lib/runtime/prefabs.js +13 -0
  107. package/lib/runtime/random.js +88 -0
  108. package/lib/runtime/render-buffer-contract.js +1 -0
  109. package/lib/runtime/render-buffers.js +243 -0
  110. package/lib/runtime/render-capabilities.js +17 -0
  111. package/lib/runtime/render-settings.js +249 -0
  112. package/lib/runtime/render-texture-contract.js +1 -0
  113. package/lib/runtime/render-textures.js +271 -0
  114. package/lib/runtime/resources.js +648 -0
  115. package/lib/runtime/scheduler.js +765 -0
  116. package/lib/runtime/storage.js +169 -0
  117. package/lib/runtime/world.js +594 -0
  118. package/lib/schema/index.js +24 -0
  119. package/lib/sdk/graph-values.js +16 -0
  120. package/lib/sdk/index.d.ts +800 -0
  121. package/lib/sdk/index.js +82 -0
  122. package/lib/tools/api-index.js +113 -0
  123. package/lib/tools/browser-session.js +89 -0
  124. package/lib/tools/cli.js +513 -0
  125. package/lib/tools/compress-dist.js +59 -0
  126. package/lib/tools/control-client.js +153 -0
  127. package/lib/tools/control-server.js +633 -0
  128. package/lib/tools/diagnostic-code.js +3 -0
  129. package/lib/tools/doctor.js +496 -0
  130. package/lib/tools/npm/browser-cli.js +6 -0
  131. package/lib/tools/npm/entry.js +40 -0
  132. package/lib/tools/npm/layout.js +195 -0
  133. package/lib/tools/setup.js +153 -0
  134. package/licenses/@dimforge/rapier2d-compat/LICENSE +201 -0
  135. package/licenses/@dimforge/rapier3d-compat/LICENSE +201 -0
  136. package/licenses/@gltf-transform/core/LICENSE.md +21 -0
  137. package/licenses/@gltf-transform/extensions/LICENSE.md +21 -0
  138. package/licenses/@gltf-transform/functions/LICENSE.md +21 -0
  139. package/licenses/@img/colour/LICENSE.md +82 -0
  140. package/licenses/@img/sharp-win32-x64/LICENSE +191 -0
  141. package/licenses/@oxc-project/types/LICENSE +22 -0
  142. package/licenses/@rolldown/pluginutils/LICENSE +21 -0
  143. package/licenses/@tweenjs/tween.js/LICENSE +23 -0
  144. package/licenses/@types/ndarray/LICENSE +21 -0
  145. package/licenses/@types/node/LICENSE +21 -0
  146. package/licenses/@types/stats.js/LICENSE +21 -0
  147. package/licenses/@types/three/@dimforge/rapier3d-compat/LICENSE +201 -0
  148. package/licenses/@types/three/LICENSE +21 -0
  149. package/licenses/@types/webxr/LICENSE +21 -0
  150. package/licenses/@webgpu/types/LICENSE +26 -0
  151. package/licenses/THIRD-PARTY-NOTICES.md +139 -0
  152. package/licenses/cwise-compiler/LICENSE +22 -0
  153. package/licenses/detect-libc/LICENSE +201 -0
  154. package/licenses/esbuild/LICENSE.md +21 -0
  155. package/licenses/fast-png/LICENSE +21 -0
  156. package/licenses/fdir/LICENSE +7 -0
  157. package/licenses/fflate/LICENSE +21 -0
  158. package/licenses/iobuffer/LICENSE +22 -0
  159. package/licenses/iota-array/LICENSE +22 -0
  160. package/licenses/is-buffer/LICENSE +21 -0
  161. package/licenses/ktx-parse/LICENSE +21 -0
  162. package/licenses/lightningcss/LICENSE +373 -0
  163. package/licenses/lightningcss-win32-x64-msvc/LICENSE +373 -0
  164. package/licenses/meshoptimizer/LICENSE.md +21 -0
  165. package/licenses/nanoid/LICENSE +20 -0
  166. package/licenses/ndarray/LICENSE +22 -0
  167. package/licenses/ndarray-lanczos/LICENSE +21 -0
  168. package/licenses/ndarray-ops/LICENSE +22 -0
  169. package/licenses/ndarray-pixels/LICENSE +21 -0
  170. package/licenses/ngraph.path/LICENSE +21 -0
  171. package/licenses/picocolors/LICENSE +15 -0
  172. package/licenses/picomatch/LICENSE +21 -0
  173. package/licenses/playwright/LICENSE +202 -0
  174. package/licenses/playwright/NOTICE +5 -0
  175. package/licenses/playwright-core/LICENSE +202 -0
  176. package/licenses/playwright-core/NOTICE +5 -0
  177. package/licenses/postcss/LICENSE +20 -0
  178. package/licenses/prettier/LICENSE +7 -0
  179. package/licenses/property-graph/LICENSE +21 -0
  180. package/licenses/rolldown/LICENSE +25 -0
  181. package/licenses/semver/LICENSE +15 -0
  182. package/licenses/sharp/LICENSE +191 -0
  183. package/licenses/source-map-js/LICENSE +28 -0
  184. package/licenses/three/LICENSE +21 -0
  185. package/licenses/tinyglobby/LICENSE +21 -0
  186. package/licenses/tsx/LICENSE +21 -0
  187. package/licenses/typescript/LICENSE.txt +55 -0
  188. package/licenses/undici-types/LICENSE +21 -0
  189. package/licenses/uniq/LICENSE +22 -0
  190. package/licenses/vite/LICENSE.md +2084 -0
  191. package/package.json +49 -4
  192. package/web/index.html +34 -0
@@ -0,0 +1,145 @@
1
+ import { graphValue, isGraphType } from "../sdk/graph-values.js";
2
+ const snapshotCode = "TC_RENDER_PARAMETERS_SNAPSHOT";
3
+ const fail = (code, at, detail) => {
4
+ throw new Error(`${code}: ${at}: ${detail} Repair: Use declared material IDs/names, matching finite f32 values, and snapshots from identical definitions.`);
5
+ };
6
+ const plain = (value) => {
7
+ if (!value || typeof value !== "object" || Array.isArray(value))
8
+ return false;
9
+ const proto = Object.getPrototypeOf(value);
10
+ return (proto === Object.prototype || proto === null) && Reflect.ownKeys(value).every(key => {
11
+ const d = Object.getOwnPropertyDescriptor(value, key);
12
+ return typeof key === "string" && d.enumerable && "value" in d;
13
+ });
14
+ };
15
+ const exact = (value, keys) => plain(value) && Reflect.ownKeys(value).length === keys.length && keys.every(key => Object.hasOwn(value, key));
16
+ const dense = (value) => {
17
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || Reflect.ownKeys(value).length !== value.length + 1)
18
+ return false;
19
+ for (let i = 0; i < value.length; i++) {
20
+ const d = Object.getOwnPropertyDescriptor(value, String(i));
21
+ if (!d || !d.enumerable || !("value" in d))
22
+ return false;
23
+ }
24
+ return true;
25
+ };
26
+ const checked = (type, value, code, at) => {
27
+ if (Array.isArray(value) && !dense(value))
28
+ return fail(code, at, "Expected a dense plain vector.");
29
+ return graphValue(type, value) ?? fail(code, at, `Expected ${type} with finite f32 components.`);
30
+ };
31
+ const clone = (value, frozen = false) => {
32
+ if (!Array.isArray(value))
33
+ return value;
34
+ const result = [...value];
35
+ return (frozen ? Object.freeze(result) : result);
36
+ };
37
+ const same = (a, b) => Array.isArray(a) ? Array.isArray(b) && a.length === b.length && a.every((v, i) => v === b[i]) : a === b;
38
+ const copyValues = (values, frozen = false) => Object.fromEntries(Object.entries(values).map(([name, value]) => [name, clone(value, frozen)]));
39
+ /** CPU-only owner. Snapshots stage all values before any live state is changed. */
40
+ export function createMaterialParameters(definitions) {
41
+ const materials = new Map();
42
+ if (!dense(definitions))
43
+ fail("TC_RENDER_MATERIAL", "*:*", "Expected a dense definitions array.");
44
+ for (const definition of definitions) {
45
+ if (!plain(definition) || typeof definition.id !== "string" || !/^[a-zA-Z][a-zA-Z0-9_.-]*$/.test(definition.id))
46
+ fail("TC_RENDER_MATERIAL", "*:*", "Expected a valid material ID.");
47
+ const id = definition.id;
48
+ if (materials.has(id))
49
+ fail("TC_RENDER_MATERIAL", `${id}:*`, "Duplicate material ID.");
50
+ const source = definition.parameters === undefined ? [] : definition.parameters;
51
+ if (!dense(source))
52
+ fail("TC_RENDER_PARAMETER", `${id}:*`, "Expected a dense parameters array.");
53
+ const names = new Set(), parameters = [];
54
+ for (const parameter of source) {
55
+ if (!exact(parameter, ["name", "type", "default"]) || typeof parameter.name !== "string" || !parameter.name.trim())
56
+ fail("TC_RENDER_PARAMETER", `${id}:*`, "Expected a nonempty parameter name, type and default.");
57
+ const at = `${id}:${parameter.name}`;
58
+ if (names.has(parameter.name))
59
+ fail("TC_RENDER_PARAMETER", at, "Duplicate parameter name.");
60
+ if (!isGraphType(parameter.type))
61
+ fail("TC_RENDER_PARAMETER", at, "Unknown graph type.");
62
+ names.add(parameter.name);
63
+ parameters.push({ name: parameter.name, type: parameter.type, default: checked(parameter.type, parameter.default, "TC_RENDER_PARAMETER_VALUE", at) });
64
+ }
65
+ parameters.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
66
+ materials.set(id, { parameters, values: Object.fromEntries(parameters.map(p => [p.name, clone(p.default, true)])) });
67
+ }
68
+ const canonical = [...materials].filter(([, m]) => m.parameters.length)
69
+ .sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([id, m]) => ({ id, parameters: m.parameters }));
70
+ let disposed = false, revision = 0;
71
+ const active = () => {
72
+ if (disposed)
73
+ throw new Error("TC_RENDER_DISPOSED: *:*: Material parameters owner is disposed. Repair: Create a new owner.");
74
+ };
75
+ const material = (id) => materials.get(id) ?? fail("TC_RENDER_MATERIAL", `${id}:*`, "Unknown material.");
76
+ const validate = (value) => {
77
+ active();
78
+ const staged = new Map();
79
+ if (!canonical.length && value === undefined)
80
+ return staged;
81
+ const bad = (at = "*:*") => fail(snapshotCode, at, "Incompatible material parameters snapshot.");
82
+ if (!canonical.length || !exact(value, ["version", "definitions", "values"]) || value.version !== 1 || !dense(value.definitions) || value.definitions.length !== canonical.length)
83
+ return bad();
84
+ if (!exact(value.values, canonical.map(d => d.id)))
85
+ return bad();
86
+ for (let i = 0; i < canonical.length; i++) {
87
+ const expected = canonical[i], actual = value.definitions[i];
88
+ if (!exact(actual, ["id", "parameters"]) || actual.id !== expected.id || !dense(actual.parameters) || actual.parameters.length !== expected.parameters.length)
89
+ return bad(`${expected.id}:*`);
90
+ const entries = [], values = value.values[expected.id];
91
+ if (!exact(values, expected.parameters.map(p => p.name)))
92
+ return bad(`${expected.id}:*`);
93
+ for (let j = 0; j < expected.parameters.length; j++) {
94
+ const p = expected.parameters[j], descriptor = actual.parameters[j], at = `${expected.id}:${p.name}`;
95
+ if (!exact(descriptor, ["name", "type", "default"]) || descriptor.name !== p.name || descriptor.type !== p.type || !same(checked(p.type, descriptor.default, snapshotCode, at), p.default))
96
+ return bad(at);
97
+ entries.push([p.name, checked(p.type, values[p.name], snapshotCode, at)]);
98
+ }
99
+ staged.set(expected.id, Object.fromEntries(entries));
100
+ }
101
+ return staged;
102
+ };
103
+ return {
104
+ set(id, name, value) {
105
+ active();
106
+ const m = material(id), parameter = m.parameters.find(p => p.name === name);
107
+ if (!parameter)
108
+ return fail("TC_RENDER_PARAMETER", `${id}:${name}`, "Unknown parameter.");
109
+ const next = checked(parameter.type, value, "TC_RENDER_PARAMETER_VALUE", `${id}:${name}`);
110
+ if (!same(m.values[name], next)) {
111
+ m.values[name] = next;
112
+ revision++;
113
+ }
114
+ },
115
+ get(id) {
116
+ active();
117
+ return Object.freeze(copyValues(material(id).values, true));
118
+ },
119
+ snapshot() {
120
+ active();
121
+ if (!canonical.length)
122
+ return undefined;
123
+ return {
124
+ version: 1,
125
+ definitions: canonical.map(d => ({ id: d.id, parameters: d.parameters.map(p => ({ ...p, default: clone(p.default) })) })),
126
+ values: Object.fromEntries(canonical.map(d => [d.id, copyValues(material(d.id).values)])),
127
+ };
128
+ },
129
+ validateSnapshot(value) { validate(value); },
130
+ restore(value) {
131
+ const staged = validate(value);
132
+ const changed = [...staged].some(([id, values]) => Object.keys(values).some(name => !same(material(id).values[name], values[name])));
133
+ if (changed) {
134
+ for (const [id, values] of staged)
135
+ material(id).values = values;
136
+ revision++;
137
+ }
138
+ },
139
+ dispose() { disposed = true; },
140
+ inspect() {
141
+ active();
142
+ return { materials: materials.size, parameters: canonical.reduce((n, d) => n + d.parameters.length, 0), revision };
143
+ },
144
+ };
145
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,189 @@
1
+ import { createMaterialParameters } from "./material-parameters.js";
2
+ const remediation = "Use declared pass IDs and parameters, boolean enabled flags, matching finite f32 values, and snapshots from identical ordered graphs; create a new owner after disposal.";
3
+ const error = (code, detail) => {
4
+ throw Object.assign(new Error(`${code}: ${detail} Repair: ${remediation}`), { code, remediation });
5
+ };
6
+ const fail = (code, at, detail) => error(code, `${at}: ${detail}`);
7
+ const plain = (value) => {
8
+ if (!value || typeof value !== "object" || Array.isArray(value))
9
+ return false;
10
+ const proto = Object.getPrototypeOf(value);
11
+ return (proto === Object.prototype || proto === null) && Reflect.ownKeys(value).every(key => {
12
+ const d = Object.getOwnPropertyDescriptor(value, key);
13
+ return typeof key === "string" && d.enumerable && "value" in d;
14
+ });
15
+ };
16
+ const exact = (value, keys) => plain(value) && Reflect.ownKeys(value).length === keys.length && keys.every(key => Object.hasOwn(value, key));
17
+ const dense = (value) => {
18
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype || Reflect.ownKeys(value).length !== value.length + 1)
19
+ return false;
20
+ for (let i = 0; i < value.length; i++) {
21
+ const d = Object.getOwnPropertyDescriptor(value, String(i));
22
+ if (!d || !d.enumerable || !("value" in d))
23
+ return false;
24
+ }
25
+ return true;
26
+ };
27
+ // Stable plain-data copies bind snapshots to kernels without retaining input aliases.
28
+ const canonical = (value, code, seen = new Set()) => {
29
+ if (value === null || typeof value === "string" || typeof value === "boolean")
30
+ return value;
31
+ if (typeof value === "number" && Number.isFinite(value) && Number.isFinite(Math.fround(value)))
32
+ return value;
33
+ if (!value || typeof value !== "object" || seen.has(value))
34
+ return fail(code, "definitions", "Expected acyclic finite f32 plain data.");
35
+ seen.add(value);
36
+ let result;
37
+ if (dense(value))
38
+ result = value.map(v => canonical(v, code, seen));
39
+ else if (plain(value))
40
+ result = Object.fromEntries(Object.keys(value).sort().map(k => [k, canonical(value[k], code, seen)]));
41
+ else
42
+ return fail(code, "definitions", "Expected plain records and dense arrays without accessors.");
43
+ seen.delete(value);
44
+ return result;
45
+ };
46
+ // Keep one implementation of parameter typing, finite-f32 checks and vector isolation.
47
+ const parameterCall = (action, snapshot = false) => {
48
+ try {
49
+ return action();
50
+ }
51
+ catch (cause) {
52
+ const match = cause instanceof Error && /^TC_RENDER_([A-Z_]+): ([\s\S]*?) Repair:/.exec(cause.message);
53
+ if (!match)
54
+ throw cause;
55
+ const codes = {
56
+ MATERIAL: "TC_POSTPROCESS_ID", PARAMETER: "TC_POSTPROCESS_PARAMETER",
57
+ PARAMETER_VALUE: "TC_POSTPROCESS_PARAMETER_VALUE", PARAMETERS_SNAPSHOT: "TC_POSTPROCESS_SNAPSHOT",
58
+ };
59
+ return error(snapshot ? "TC_POSTPROCESS_SNAPSHOT" : codes[match[1]] ?? "TC_POSTPROCESS_DEFINITION", match[2]);
60
+ }
61
+ };
62
+ /** CPU state only. History revisions request GPU image clears, not image restoration. */
63
+ export function createPostProcessOwner(definitions = []) {
64
+ if (!dense(definitions))
65
+ fail("TC_POSTPROCESS_DEFINITION", "*:*", "Expected a dense definitions array.");
66
+ const kernels = [], passes = new Map();
67
+ for (const definition of definitions) {
68
+ if (!exact(definition, ["id", "enabled", "graph", "source", ...(definition && typeof definition === "object" && Object.hasOwn(definition, "buffers") ? ["buffers"] : []), ...(definition && typeof definition === "object" && Object.hasOwn(definition, "textures") ? ["textures"] : [])]) || typeof definition.id !== "string" || !/^[a-zA-Z][a-zA-Z0-9_.-]*$/.test(definition.id) || definition.id.startsWith("scene."))
69
+ fail("TC_POSTPROCESS_DEFINITION", "*:*", "Expected a nonreserved pass ID, enabled flag, compiled graph and source.");
70
+ const id = definition.id;
71
+ if (passes.has(id))
72
+ fail("TC_POSTPROCESS_ID", `${id}:*`, "Duplicate pass ID.");
73
+ if (typeof definition.enabled !== "boolean")
74
+ fail("TC_POSTPROCESS_CONFIGURE", `${id}:*`, "Enabled must be boolean.");
75
+ const graph = canonical(definition.graph, "TC_POSTPROCESS_DEFINITION");
76
+ if (!plain(graph) || !dense(graph.nodes) || typeof graph.output !== "string" || !graph.output || graph.outputType !== "vec4" || !plain(graph.nodeTypes))
77
+ fail("TC_POSTPROCESS_DEFINITION", `${id}:*`, "Expected a compiled vec4 output graph.");
78
+ kernels.push({ id, enabled: definition.enabled, graph, ...(definition.buffers ? { buffers: canonical(definition.buffers, "TC_POSTPROCESS_DEFINITION") } : {}), ...(definition.textures ? { textures: canonical(definition.textures, "TC_POSTPROCESS_DEFINITION") } : {}) });
79
+ passes.set(id, { enabled: definition.enabled, history: 0 });
80
+ }
81
+ const parameters = parameterCall(() => createMaterialParameters(kernels.map(k => ({ id: k.id, parameters: k.graph.parameters }))));
82
+ for (const kernel of kernels) {
83
+ if (kernel.graph.parameters)
84
+ kernel.graph.parameters = [...kernel.graph.parameters].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
85
+ }
86
+ const definitionText = JSON.stringify(canonical(kernels, "TC_POSTPROCESS_DEFINITION"));
87
+ let disposed = false, revision = 0;
88
+ const active = () => { if (disposed)
89
+ fail("TC_POSTPROCESS_DISPOSED", "*:*", "Postprocess owner is disposed."); };
90
+ const pass = (id) => {
91
+ active();
92
+ if (typeof id !== "string" || !passes.has(id))
93
+ return fail("TC_POSTPROCESS_ID", `${typeof id === "string" ? id : "<invalid>"}:*`, "Unknown pass ID.");
94
+ return passes.get(id);
95
+ };
96
+ const state = (id) => {
97
+ const p = pass(id);
98
+ return Object.freeze({ id, enabled: p.enabled, parameters: parameterCall(() => parameters.get(id)) });
99
+ };
100
+ const states = () => {
101
+ active();
102
+ return Object.freeze([...passes.keys()].map(state));
103
+ };
104
+ const historyRevisions = () => {
105
+ active();
106
+ return Object.freeze(Object.fromEntries([...passes].map(([id, p]) => [id, p.history])));
107
+ };
108
+ const resetAll = () => { for (const p of passes.values())
109
+ p.history++; };
110
+ const validate = (value) => {
111
+ active();
112
+ if (!kernels.length && value === undefined)
113
+ return { states: [], parameters: undefined };
114
+ const bad = (at = "*:*", detail = "Incompatible postprocess snapshot.") => fail("TC_POSTPROCESS_SNAPSHOT", at, detail);
115
+ if (!kernels.length || !exact(value, ["version", "definitions", "states"]) || value.version !== 1)
116
+ return bad();
117
+ const copy = canonical(value, "TC_POSTPROCESS_SNAPSHOT");
118
+ if (JSON.stringify(copy.definitions) !== definitionText || !dense(copy.states) || copy.states.length !== kernels.length)
119
+ return bad();
120
+ const stagedParameters = parameters.snapshot();
121
+ for (let i = 0; i < kernels.length; i++) {
122
+ const kernel = kernels[i], s = copy.states[i];
123
+ if (!exact(s, ["id", "enabled", "parameters"]) || s.id !== kernel.id || typeof s.enabled !== "boolean" || !exact(s.parameters, Object.keys(parameters.get(kernel.id))))
124
+ return bad(`${kernel.id}:*`);
125
+ }
126
+ if (stagedParameters) {
127
+ stagedParameters.values = Object.fromEntries(copy.states.filter(s => Object.keys(s.parameters).length).map(s => [s.id, s.parameters]));
128
+ parameterCall(() => parameters.validateSnapshot(stagedParameters), true);
129
+ }
130
+ return { states: copy.states, parameters: stagedParameters };
131
+ };
132
+ const service = {
133
+ configure(id, settings) {
134
+ const p = pass(id);
135
+ if (!exact(settings, ["enabled"]) || typeof settings.enabled !== "boolean")
136
+ fail("TC_POSTPROCESS_CONFIGURE", `${id}:*`, "Expected only a boolean enabled flag.");
137
+ if (p.enabled !== settings.enabled) {
138
+ p.enabled = settings.enabled;
139
+ revision++;
140
+ resetAll();
141
+ }
142
+ },
143
+ state,
144
+ setParameter(id, name, value) {
145
+ pass(id);
146
+ if (typeof name !== "string")
147
+ fail("TC_POSTPROCESS_PARAMETER", `${id}:<invalid>`, "Expected a declared parameter name.");
148
+ const before = parameters.inspect().revision;
149
+ parameterCall(() => parameters.set(id, name, value));
150
+ if (parameters.inspect().revision !== before)
151
+ revision++;
152
+ },
153
+ resetHistory(id) {
154
+ active();
155
+ if (id === undefined)
156
+ resetAll();
157
+ else
158
+ pass(id).history++;
159
+ },
160
+ };
161
+ return {
162
+ service, states, historyRevisions,
163
+ snapshot() {
164
+ active();
165
+ if (!kernels.length)
166
+ return undefined;
167
+ return { version: 1, definitions: structuredClone(kernels), states: structuredClone(states()) };
168
+ },
169
+ validateSnapshot(value) { validate(value); },
170
+ restore(value) {
171
+ const staged = validate(value), before = parameters.inspect().revision;
172
+ const changed = staged.states.some(s => passes.get(s.id).enabled !== s.enabled);
173
+ parameterCall(() => parameters.restore(staged.parameters), true);
174
+ for (const s of staged.states)
175
+ passes.get(s.id).enabled = s.enabled;
176
+ if (changed || before !== parameters.inspect().revision)
177
+ revision++;
178
+ resetAll();
179
+ },
180
+ dispose() { if (!disposed) {
181
+ disposed = true;
182
+ parameters.dispose();
183
+ } },
184
+ inspect() {
185
+ active();
186
+ return { passes: passes.size, enabled: [...passes.values()].filter(p => p.enabled).length, parameters: parameters.inspect().parameters, revision };
187
+ },
188
+ };
189
+ }
@@ -0,0 +1,13 @@
1
+ /** Resolve authored presentation/physics through immutable content identity in World. */
2
+ export function resolveEntityDefinition(ir, record) {
3
+ const authored = ir.scene.entities.find((entity) => entity.id === record.id);
4
+ if (authored &&
5
+ (record.prefab === undefined || record.prefab === authored.prefab))
6
+ return authored;
7
+ if (record.prefab === undefined)
8
+ return undefined;
9
+ const prefab = ir.prefabs.find((entity) => entity.id === record.prefab);
10
+ if (!prefab)
11
+ throw new Error("TC_PREFAB_MISSING: " + record.prefab);
12
+ return { ...prefab, id: record.id, prefab: record.prefab };
13
+ }
@@ -0,0 +1,88 @@
1
+ export function cloneSerializable(value) {
2
+ const ancestors = new Set();
3
+ const visit = (input, path) => {
4
+ if (input === null ||
5
+ typeof input === "string" ||
6
+ typeof input === "boolean")
7
+ return input;
8
+ if (typeof input === "number" && Number.isFinite(input))
9
+ return input;
10
+ if (typeof input !== "object" || input === null)
11
+ throw new Error(`TC_SERIALIZATION: Invalid JSON value at ${path}.`);
12
+ if (ancestors.has(input))
13
+ throw new Error(`TC_SERIALIZATION: Circular value at ${path}.`);
14
+ const array = Array.isArray(input);
15
+ const prototype = Object.getPrototypeOf(input);
16
+ if (!array && prototype !== Object.prototype && prototype !== null)
17
+ throw new Error(`TC_SERIALIZATION: Expected a plain object at ${path}.`);
18
+ if (Object.getOwnPropertySymbols(input).length)
19
+ throw new Error(`TC_SERIALIZATION: Symbol keys at ${path}.`);
20
+ ancestors.add(input);
21
+ const result = array ? [] : {};
22
+ const keys = Object.getOwnPropertyNames(input).filter((key) => !(array && key === "length"));
23
+ if (array &&
24
+ (keys.length !== input.length ||
25
+ keys.some((key, index) => key !== String(index))))
26
+ throw new Error(`TC_SERIALIZATION: Sparse array or array properties at ${path}.`);
27
+ for (const key of keys) {
28
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
29
+ if (!descriptor.enumerable || !("value" in descriptor))
30
+ throw new Error(`TC_SERIALIZATION: Accessor or hidden property at ${path}.${key}.`);
31
+ Object.defineProperty(result, key, {
32
+ value: visit(descriptor.value, `${path}.${key}`),
33
+ enumerable: true,
34
+ writable: true,
35
+ configurable: true,
36
+ });
37
+ }
38
+ ancestors.delete(input);
39
+ return result;
40
+ };
41
+ return visit(value, "$");
42
+ }
43
+ export function validateRandomSnapshot(value) {
44
+ const data = cloneSerializable(value);
45
+ if (!data ||
46
+ data.format !== "pelican.random" ||
47
+ data.version !== 1 ||
48
+ data.algorithm !== "mulberry32" ||
49
+ !Number.isInteger(data.state) ||
50
+ data.state < 0 ||
51
+ data.state > 0xffffffff ||
52
+ !Number.isSafeInteger(data.draws) ||
53
+ data.draws < 0)
54
+ throw new Error("TC_RANDOM_SNAPSHOT: Unsupported or malformed random snapshot.");
55
+ return data;
56
+ }
57
+ /** CPU seeded stream, not cryptographic and not a cross-GPU determinism promise. */
58
+ export function createRandom(seed = 0) {
59
+ if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff)
60
+ throw new Error("TC_RANDOM_SEED: Expected a uint32 seed.");
61
+ let state = seed >>> 0, draws = 0;
62
+ return {
63
+ next() {
64
+ if (!Number.isSafeInteger(draws + 1))
65
+ throw new Error("TC_RANDOM_LIMIT: Random draw counter exhausted.");
66
+ state = (state + 0x6d2b79f5) >>> 0;
67
+ let t = state;
68
+ t = Math.imul(t ^ (t >>> 15), t | 1);
69
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
70
+ draws++;
71
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
72
+ },
73
+ snapshot() {
74
+ return {
75
+ format: "pelican.random",
76
+ version: 1,
77
+ algorithm: "mulberry32",
78
+ state,
79
+ draws,
80
+ };
81
+ },
82
+ restore(value) {
83
+ const data = validateRandomSnapshot(value);
84
+ state = data.state;
85
+ draws = data.draws;
86
+ },
87
+ };
88
+ }
@@ -0,0 +1 @@
1
+ export {};