@aethervtt/shard-render 0.0.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 (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/assets.d.ts +104 -0
  4. package/dist/assets.js +213 -0
  5. package/dist/atmosphere-model.d.ts +96 -0
  6. package/dist/atmosphere-model.js +370 -0
  7. package/dist/atmosphere-nodes.d.ts +55 -0
  8. package/dist/atmosphere-nodes.js +780 -0
  9. package/dist/atmosphere-plugin.d.ts +5 -0
  10. package/dist/atmosphere-plugin.js +42 -0
  11. package/dist/atmosphere-shaders.d.ts +7 -0
  12. package/dist/atmosphere-shaders.js +595 -0
  13. package/dist/atmosphere-state.d.ts +72 -0
  14. package/dist/atmosphere-state.js +18 -0
  15. package/dist/atmosphere.d.ts +191 -0
  16. package/dist/atmosphere.js +950 -0
  17. package/dist/camera.d.ts +83 -0
  18. package/dist/camera.js +163 -0
  19. package/dist/clusters.d.ts +68 -0
  20. package/dist/clusters.js +288 -0
  21. package/dist/culling-shaders.d.ts +2 -0
  22. package/dist/culling-shaders.js +203 -0
  23. package/dist/culling.d.ts +84 -0
  24. package/dist/culling.js +498 -0
  25. package/dist/debug-shaders.d.ts +3 -0
  26. package/dist/debug-shaders.js +207 -0
  27. package/dist/debug-views.d.ts +60 -0
  28. package/dist/debug-views.js +197 -0
  29. package/dist/deferred-plugin.d.ts +5 -0
  30. package/dist/deferred-plugin.js +20 -0
  31. package/dist/deferred-shaders.d.ts +2 -0
  32. package/dist/deferred-shaders.js +151 -0
  33. package/dist/deferred.d.ts +22 -0
  34. package/dist/deferred.js +296 -0
  35. package/dist/deform.d.ts +80 -0
  36. package/dist/deform.js +213 -0
  37. package/dist/describe-plugin.d.ts +5 -0
  38. package/dist/describe-plugin.js +19 -0
  39. package/dist/display-nodes.d.ts +5 -0
  40. package/dist/display-nodes.js +189 -0
  41. package/dist/dynamic-resolution.d.ts +47 -0
  42. package/dist/dynamic-resolution.js +191 -0
  43. package/dist/environment-plugin.d.ts +6 -0
  44. package/dist/environment-plugin.js +44 -0
  45. package/dist/environment-shaders.d.ts +2 -0
  46. package/dist/environment-shaders.js +260 -0
  47. package/dist/environment-state.d.ts +71 -0
  48. package/dist/environment-state.js +144 -0
  49. package/dist/environment.d.ts +70 -0
  50. package/dist/environment.js +529 -0
  51. package/dist/forward.d.ts +123 -0
  52. package/dist/forward.js +1216 -0
  53. package/dist/fxaa.d.ts +2 -0
  54. package/dist/fxaa.js +65 -0
  55. package/dist/gizmo-font.d.ts +21 -0
  56. package/dist/gizmo-font.js +132 -0
  57. package/dist/gizmos-plugin.d.ts +5 -0
  58. package/dist/gizmos-plugin.js +34 -0
  59. package/dist/gizmos.d.ts +156 -0
  60. package/dist/gizmos.js +772 -0
  61. package/dist/gpu-assets.d.ts +114 -0
  62. package/dist/gpu-assets.js +494 -0
  63. package/dist/graph.d.ts +204 -0
  64. package/dist/graph.js +417 -0
  65. package/dist/index.d.ts +53 -0
  66. package/dist/index.js +540 -0
  67. package/dist/instances.d.ts +349 -0
  68. package/dist/instances.js +1335 -0
  69. package/dist/large-world.d.ts +13 -0
  70. package/dist/large-world.js +20 -0
  71. package/dist/lights.d.ts +160 -0
  72. package/dist/lights.js +468 -0
  73. package/dist/material-noise.d.ts +19 -0
  74. package/dist/material-noise.js +7 -0
  75. package/dist/material-pipelines.d.ts +46 -0
  76. package/dist/material-pipelines.js +166 -0
  77. package/dist/materials.d.ts +108 -0
  78. package/dist/materials.js +219 -0
  79. package/dist/noise.d.ts +76 -0
  80. package/dist/noise.js +286 -0
  81. package/dist/overlay-registry.d.ts +18 -0
  82. package/dist/overlay-registry.js +30 -0
  83. package/dist/overlays.d.ts +35 -0
  84. package/dist/overlays.js +344 -0
  85. package/dist/paths.d.ts +20 -0
  86. package/dist/paths.js +46 -0
  87. package/dist/picking-plugin.d.ts +2 -0
  88. package/dist/picking-plugin.js +20 -0
  89. package/dist/picking.d.ts +94 -0
  90. package/dist/picking.js +456 -0
  91. package/dist/pixel-perfect-plugin.d.ts +9 -0
  92. package/dist/pixel-perfect-plugin.js +135 -0
  93. package/dist/pixel-perfect.d.ts +27 -0
  94. package/dist/pixel-perfect.js +70 -0
  95. package/dist/plugin.d.ts +111 -0
  96. package/dist/plugin.js +182 -0
  97. package/dist/pool.d.ts +19 -0
  98. package/dist/pool.js +43 -0
  99. package/dist/post-common.d.ts +51 -0
  100. package/dist/post-common.js +148 -0
  101. package/dist/post-nodes.d.ts +39 -0
  102. package/dist/post-nodes.js +849 -0
  103. package/dist/post-plugin.d.ts +5 -0
  104. package/dist/post-plugin.js +26 -0
  105. package/dist/post-shaders.d.ts +8 -0
  106. package/dist/post-shaders.js +698 -0
  107. package/dist/post.d.ts +175 -0
  108. package/dist/post.js +453 -0
  109. package/dist/readback.d.ts +4 -0
  110. package/dist/readback.js +95 -0
  111. package/dist/render-scale.d.ts +53 -0
  112. package/dist/render-scale.js +63 -0
  113. package/dist/shaders.d.ts +122 -0
  114. package/dist/shaders.js +1168 -0
  115. package/dist/shadows.d.ts +119 -0
  116. package/dist/shadows.js +562 -0
  117. package/dist/skin-asset.d.ts +26 -0
  118. package/dist/skin-asset.js +25 -0
  119. package/dist/skinning-plugin.d.ts +5 -0
  120. package/dist/skinning-plugin.js +16 -0
  121. package/dist/skinning.d.ts +19 -0
  122. package/dist/skinning.js +343 -0
  123. package/dist/standard-fields.d.ts +67 -0
  124. package/dist/standard-fields.js +73 -0
  125. package/dist/standard.d.ts +7 -0
  126. package/dist/standard.js +37 -0
  127. package/dist/stats.d.ts +25 -0
  128. package/dist/stats.js +18 -0
  129. package/dist/target.d.ts +55 -0
  130. package/dist/target.js +70 -0
  131. package/dist/testing.d.ts +34 -0
  132. package/dist/testing.js +126 -0
  133. package/dist/timer.d.ts +28 -0
  134. package/dist/timer.js +136 -0
  135. package/dist/view.d.ts +117 -0
  136. package/dist/view.js +389 -0
  137. package/dist/visibility.d.ts +10 -0
  138. package/dist/visibility.js +84 -0
  139. package/package.json +49 -0
package/dist/noise.js ADDED
@@ -0,0 +1,286 @@
1
+ import { AssetServerResource, assetServer } from "@aethervtt/shard-assets";
2
+ import { ShardError } from "@aethervtt/shard-core";
3
+ import { GpuBuffer } from "@aethervtt/shard-gpu";
4
+ import {
5
+ NoiseGraphs,
6
+ noiseOrigins,
7
+ normalizeGrid,
8
+ patchOrigin,
9
+ patchPoints,
10
+ registerNoiseGraph
11
+ } from "@aethervtt/shard-noise";
12
+ import { definePlugin, LogResource, Time } from "@aethervtt/shard-runtime";
13
+ import { MaterialNoise } from "./material-noise.js";
14
+ import { Shaders } from "./plugin.js";
15
+ const states = /* @__PURE__ */ new WeakMap();
16
+ const loading = /* @__PURE__ */ new WeakMap();
17
+ function materialNoise(world, library, type) {
18
+ let state = states.get(type);
19
+ if (!state || state.slots.length !== type.noise.length) {
20
+ state = {
21
+ slots: type.noise.map(() => ({ ref: void 0, graph: void 0, version: -1 })),
22
+ source: "",
23
+ key: ""
24
+ };
25
+ states.set(type, state);
26
+ }
27
+ const store = world.tryResource(NoiseGraphs);
28
+ let changed = false;
29
+ for (let i = 0; i < type.noise.length; i++) {
30
+ const slot = type.noise[i];
31
+ const s = state.slots[i];
32
+ s.ref ??= world.tryResource(AssetServerResource) ? assetServer(world).resolve(slot.path) : void 0;
33
+ if (!s.ref) {
34
+ reportMissing(world, type, slot.path);
35
+ return void 0;
36
+ }
37
+ const graph = store?.get(s.ref);
38
+ if (!graph) {
39
+ startLoad(world, slot.path);
40
+ return void 0;
41
+ }
42
+ if (graph !== s.graph || graph.version !== s.version) {
43
+ s.graph = graph;
44
+ s.version = graph.version;
45
+ changed = true;
46
+ }
47
+ registerNoiseGraph(library, graph, slot.path);
48
+ }
49
+ if (changed || state.key === "") {
50
+ let source = "";
51
+ let key = "";
52
+ for (let i = 0; i < type.noise.length; i++) {
53
+ const graph = state.slots[i].graph;
54
+ const pos = graph.program.dimensions === 4 ? "vec4f" : "vec3f";
55
+ const name = type.noise[i].name;
56
+ source += `/** ${type.noise[i].path} */
57
+ fn noise_${name}(p: ${pos}, seed: u32) -> f32 {
58
+ return ${graph.module}::${graph.fn}(p, seed);
59
+ }
60
+ `;
61
+ key += `${graph.hash}:${graph.module};`;
62
+ }
63
+ state.source = source;
64
+ state.key = key;
65
+ }
66
+ return state;
67
+ }
68
+ const reported = /* @__PURE__ */ new WeakMap();
69
+ function reportMissing(world, type, path) {
70
+ let seen = reported.get(world);
71
+ if (!seen) {
72
+ seen = /* @__PURE__ */ new Set();
73
+ reported.set(world, seen);
74
+ }
75
+ const key = `${type.name}:${path}`;
76
+ if (seen.has(key)) return;
77
+ seen.add(key);
78
+ world.tryResource(LogResource)?.error(
79
+ new ShardError(
80
+ "render/noise-graph-missing",
81
+ `Material ${type.name} uses "${path}", which isn't an asset`,
82
+ {
83
+ path,
84
+ hint: 'noise paths name *.noise.json assets, e.g. { detail: "assets/noise/rock.noise.json" }.'
85
+ }
86
+ )
87
+ );
88
+ }
89
+ function startLoad(world, path) {
90
+ let set = loading.get(world);
91
+ if (!set) {
92
+ set = /* @__PURE__ */ new Set();
93
+ loading.set(world, set);
94
+ }
95
+ if (set.has(path)) return;
96
+ set.add(path);
97
+ const pending = set;
98
+ assetServer(world).load(path).catch((err) => world.tryResource(LogResource)?.error(err)).finally(() => pending.delete(path));
99
+ }
100
+ const WORKGROUP = 64;
101
+ class NoiseCompute {
102
+ gpu;
103
+ domain;
104
+ graph;
105
+ params;
106
+ origins;
107
+ points;
108
+ scratch = new ArrayBuffer(48);
109
+ f32 = new Float32Array(this.scratch);
110
+ u32 = new Uint32Array(this.scratch);
111
+ local = new Float32Array(0);
112
+ records = new Int32Array(0);
113
+ origin64 = new Float64Array(4);
114
+ bindGroups = /* @__PURE__ */ new WeakMap();
115
+ constructor(gpu, graph, domain) {
116
+ this.gpu = gpu;
117
+ this.graph = graph;
118
+ this.domain = domain;
119
+ const usage = GPUBufferUsage.STORAGE;
120
+ this.params = new GpuBuffer(gpu, {
121
+ label: "noise/params",
122
+ usage: GPUBufferUsage.UNIFORM,
123
+ size: 48
124
+ });
125
+ this.origins = new GpuBuffer(gpu, { label: "noise/origins", usage, size: 256 });
126
+ this.points = new GpuBuffer(gpu, { label: "noise/points", usage, size: 16 });
127
+ }
128
+ /** The root module path of this domain's compute shader for the graph. */
129
+ get modulePath() {
130
+ return `${this.graph.module}::compute_${this.domain === "grid2d" ? "grid" : "patch"}`;
131
+ }
132
+ /** The compute shader: one invocation per sample, calling `noise_<name>_at`. */
133
+ shaderSource() {
134
+ const g = this.graph;
135
+ const n = g.name;
136
+ const point = this.domain === "grid2d" ? "let local = vec3f(params.corner + vec2f(f32(i % params.width), f32(i / params.width)) * params.step, 0.0);" : "let local = vec3f(points[i * 3u], points[i * 3u + 1u], points[i * 3u + 2u]);";
137
+ return `import ${g.module}::{ noise_${n}_at, NoiseOrigins_${n} };
138
+
139
+ struct NoiseComputeParams {
140
+ seed: u32,
141
+ count: u32,
142
+ width: u32,
143
+ _pad: u32,
144
+ /** Grid: the first sample, relative to the grid center, and the spacing. */
145
+ corner: vec2f,
146
+ step: vec2f,
147
+ }
148
+
149
+ @group(0) @binding(0) var<uniform> params: NoiseComputeParams;
150
+ @group(0) @binding(1) var<storage, read> origins: NoiseOrigins_${n};
151
+ @group(0) @binding(2) var<storage, read_write> out: array<f32>;
152
+ ${this.domain === "sphere-patch" ? "@group(0) @binding(3) var<storage, read> points: array<f32>;\n" : ""}
153
+ @compute @workgroup_size(${WORKGROUP})
154
+ fn main(@builtin(global_invocation_id) id: vec3u) {
155
+ let i = id.x;
156
+ if (i >= params.count) { return; }
157
+ ${point}
158
+ var o = origins;
159
+ out[i] = noise_${n}_at(&o, local, params.seed);
160
+ }
161
+ `;
162
+ }
163
+ pipeline(library) {
164
+ registerNoiseGraph(library, this.graph);
165
+ const path = this.modulePath;
166
+ const source = this.shaderSource();
167
+ library.register(path, source, `noise compute ${this.graph.module}`);
168
+ const module = library.module(this.gpu, { root: path, label: `noise ${this.graph.name}` });
169
+ if (!module) return void 0;
170
+ return this.gpu.pipelines.compute({
171
+ label: `noise/${this.graph.name}/${this.domain}`,
172
+ layout: "auto",
173
+ compute: { module, entryPoint: "main" }
174
+ });
175
+ }
176
+ /**
177
+ * Writes this request's parameters and records a dispatch into `pass`. Returns false while the
178
+ * shader compiles (nothing is recorded).
179
+ */
180
+ encode(library, pass, request) {
181
+ const pipeline = this.pipeline(library);
182
+ if (!pipeline) return false;
183
+ const origin = this.origin64;
184
+ let count;
185
+ let width;
186
+ const f = this.f32;
187
+ const u = this.u32;
188
+ if (this.domain === "grid2d") {
189
+ const g = normalizeGrid(request.area);
190
+ const [nx, ny] = g.resolution;
191
+ count = nx * ny;
192
+ width = nx;
193
+ origin[0] = g.origin[0] + g.size[0] / 2;
194
+ origin[1] = g.origin[1] + g.size[1] / 2;
195
+ origin[2] = g.z ?? 0;
196
+ f[4] = -g.size[0] / 2;
197
+ f[5] = -g.size[1] / 2;
198
+ f[6] = nx > 1 ? g.size[0] / (nx - 1) : 0;
199
+ f[7] = ny > 1 ? g.size[1] / (ny - 1) : 0;
200
+ } else {
201
+ const p = request.area;
202
+ count = p.resolution * p.resolution;
203
+ width = p.resolution;
204
+ patchOrigin(p, origin);
205
+ if (this.local.length < count * 3) this.local = new Float32Array(count * 3);
206
+ patchPoints(p, origin, 0, p.resolution, this.local);
207
+ this.points.ensureCapacity(count * 12);
208
+ this.points.write(this.local.subarray(0, count * 3));
209
+ }
210
+ const records = this.graph.program.zeroOrigins.length;
211
+ if (this.records.length !== records) this.records = new Int32Array(Math.max(8, records));
212
+ noiseOrigins(this.graph, origin, this.records.subarray(0, records));
213
+ this.origins.ensureCapacity(this.records.byteLength);
214
+ this.origins.write(this.records);
215
+ u[0] = request.seed >>> 0;
216
+ u[1] = count;
217
+ u[2] = width;
218
+ this.params.write(f);
219
+ const group = this.bindGroup(pipeline, request.out);
220
+ pass.setPipeline(pipeline);
221
+ pass.setBindGroup(0, group);
222
+ pass.dispatchWorkgroups(Math.ceil(count / WORKGROUP));
223
+ return true;
224
+ }
225
+ bindGroup(pipeline, out) {
226
+ const key = `${this.params.version}:${this.origins.version}:${this.points.version}:${this.graph.hash}`;
227
+ const cached = this.bindGroups.get(out);
228
+ if (cached && cached.key === key) return cached.group;
229
+ const group = this.gpu.device.createBindGroup({
230
+ label: `noise/${this.graph.name}`,
231
+ layout: pipeline.getBindGroupLayout(0),
232
+ entries: [
233
+ { binding: 0, resource: { buffer: this.params.buffer } },
234
+ { binding: 1, resource: { buffer: this.origins.buffer } },
235
+ { binding: 2, resource: { buffer: out } },
236
+ // Grids compute their points in the shader; `auto` layouts drop the unused binding.
237
+ ...this.domain === "sphere-patch" ? [{ binding: 3, resource: { buffer: this.points.buffer } }] : []
238
+ ]
239
+ });
240
+ this.bindGroups.set(out, { key, group });
241
+ return group;
242
+ }
243
+ destroy() {
244
+ this.params.destroy();
245
+ this.origins.destroy();
246
+ this.points.destroy();
247
+ }
248
+ }
249
+ function noiseComputeNode(options) {
250
+ let compute;
251
+ let frame = -1;
252
+ return {
253
+ kind: "raw",
254
+ phase: 0,
255
+ sideEffects: true,
256
+ run(ctx) {
257
+ const library = ctx.world.resource(Shaders);
258
+ const time = ctx.world.resource(Time).frame;
259
+ if (time === frame) return;
260
+ frame = time;
261
+ const request = options.next();
262
+ if (!request) return;
263
+ if (!compute || compute.gpu !== ctx.gpu)
264
+ compute = new NoiseCompute(ctx.gpu, options.graph, options.domain);
265
+ compute.graph = options.graph;
266
+ const pass = ctx.encoder.beginComputePass({ label: `noise/${options.graph.name}` });
267
+ const ok = compute.encode(library, pass, request);
268
+ pass.end();
269
+ if (ok && options.done) ctx.afterSubmit(() => options.done(request));
270
+ }
271
+ };
272
+ }
273
+ const materialNoisePlugin = definePlugin({
274
+ name: "render/noise",
275
+ dependencies: ["render"],
276
+ provides: [MaterialNoise],
277
+ build(app) {
278
+ app.insertResource(MaterialNoise, { wrappers: materialNoise });
279
+ }
280
+ });
281
+ export {
282
+ NoiseCompute,
283
+ materialNoise,
284
+ materialNoisePlugin,
285
+ noiseComputeNode
286
+ };
@@ -0,0 +1,18 @@
1
+ import type { Entity, World } from '@aethervtt/shard-core';
2
+ import type { GizmoStore } from './gizmos.js';
3
+ export declare const OVERLAYS: readonly ['bounds', 'lights', 'cameras', 'cascades', 'normals', 'axes', 'labels'];
4
+ export type Overlay = (typeof OVERLAYS)[number];
5
+ /** An overlay another package draws (physics colliders, the navmesh, UI rects). */
6
+ export interface OverlayDef {
7
+ name: string;
8
+ description: string;
9
+ /** Draws into gizmos. `passes(entity)` applies the overlay filter (components and path). */
10
+ draw(world: World, g: GizmoStore, passes: (entity: Entity) => boolean): void;
11
+ }
12
+ /** Registers an overlay, so `debug.overlays` and `render.capture` can turn it on by name. */
13
+ export declare function defineOverlay(def: OverlayDef): OverlayDef;
14
+ /** The registered overlays by name, read-only (no copy: drawOverlays reads it every frame). */
15
+ export declare function registeredOverlays(): ReadonlyMap<string, OverlayDef>;
16
+ export declare function allOverlays(): OverlayDef[];
17
+ /** Every overlay name: the built-in ones, then registered ones. */
18
+ export declare function overlayNames(): string[];
@@ -0,0 +1,30 @@
1
+ const OVERLAYS = [
2
+ "bounds",
3
+ "lights",
4
+ "cameras",
5
+ "cascades",
6
+ "normals",
7
+ "axes",
8
+ "labels"
9
+ ];
10
+ const extraOverlays = /* @__PURE__ */ new Map();
11
+ function defineOverlay(def) {
12
+ extraOverlays.set(def.name, def);
13
+ return def;
14
+ }
15
+ function registeredOverlays() {
16
+ return extraOverlays;
17
+ }
18
+ function allOverlays() {
19
+ return [...extraOverlays.values()];
20
+ }
21
+ function overlayNames() {
22
+ return [...OVERLAYS, ...extraOverlays.keys()];
23
+ }
24
+ export {
25
+ OVERLAYS,
26
+ allOverlays,
27
+ defineOverlay,
28
+ overlayNames,
29
+ registeredOverlays
30
+ };
@@ -0,0 +1,35 @@
1
+ import { type Entity, type World } from '@aethervtt/shard-core';
2
+ import type { Mesh } from '@aethervtt/shard-mesh';
3
+ import { type InstanceStore } from './instances.js';
4
+ import { type Overlay } from './overlay-registry.js';
5
+ export { allOverlays, defineOverlay, OVERLAYS, type Overlay, type OverlayDef, overlayNames, } from './overlay-registry.js';
6
+ export declare function isOverlayOn(o: DebugOverlaysValue, name: string): boolean;
7
+ export interface OverlayFilter {
8
+ /** Only entities with every one of these components (by name, e.g. "render/PointLight"). */
9
+ components: string[];
10
+ /** Only entities whose scene path starts with this. */
11
+ path: string;
12
+ }
13
+ export interface DebugOverlaysValue extends Record<Overlay, boolean> {
14
+ /** Registered overlays (`defineOverlay`) that are on. */
15
+ extra: Record<string, boolean>;
16
+ filter: OverlayFilter;
17
+ /**
18
+ * An entity's name for labels and the path filter. The default reads the scene path
19
+ * (`scene/SceneMember`) when the scene package is installed.
20
+ */
21
+ name: (world: World, entity: Entity) => string | undefined;
22
+ }
23
+ export declare const DebugOverlays: import("@aethervtt/shard-core").ResourceDef<DebugOverlaysValue>;
24
+ /** Turns overlays on or off; `filter` limits them (see OverlayFilter). */
25
+ export declare function setOverlays(world: World, on: Partial<Record<string, boolean>>, filter?: Partial<OverlayFilter>): DebugOverlaysValue;
26
+ /** The mesh a slot draws (LOD sets: their most detailed level). */
27
+ export declare function slotMesh(store: InstanceStore, slot: number): Mesh | undefined;
28
+ /** Draws the enabled overlays into Gizmos (Last, before gizmos upload). */
29
+ export declare const drawOverlays: import("@aethervtt/shard-core").SystemDef<undefined>;
30
+ /**
31
+ * `grids`: the origin cell's bounds and the edges of its 26 neighbors, in the origin grid. The
32
+ * origin frame is the origin grid's own frame with the origin cell's centre at (0, 0, 0), so the
33
+ * origin cell spans ±cellSize/2 on each axis.
34
+ */
35
+ export declare const gridsOverlay: import("./overlay-registry.js").OverlayDef;