@aethervtt/shard-render 0.0.1 → 0.2.0
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/dist/ablation.d.ts +74 -0
- package/dist/ablation.js +117 -0
- package/dist/assets.d.ts +3 -0
- package/dist/assets.js +43 -2
- package/dist/atmosphere-nodes.d.ts +9 -0
- package/dist/atmosphere-nodes.js +157 -30
- package/dist/atmosphere-plugin.js +8 -2
- package/dist/atmosphere-shaders.js +17 -5
- package/dist/baseline/atmosphere.d.ts +16 -0
- package/dist/baseline/atmosphere.js +301 -0
- package/dist/baseline/data-texture.d.ts +36 -0
- package/dist/baseline/data-texture.js +181 -0
- package/dist/baseline/environment.d.ts +14 -0
- package/dist/baseline/environment.js +368 -0
- package/dist/baseline/exposure.d.ts +25 -0
- package/dist/baseline/exposure.js +133 -0
- package/dist/baseline/lights.d.ts +33 -0
- package/dist/baseline/lights.js +133 -0
- package/dist/baseline/textures.d.ts +97 -0
- package/dist/baseline/textures.js +203 -0
- package/dist/camera.d.ts +2 -0
- package/dist/camera.js +4 -0
- package/dist/crowd.d.ts +21 -0
- package/dist/crowd.js +121 -0
- package/dist/culling-shaders.js +21 -2
- package/dist/culling.d.ts +11 -2
- package/dist/culling.js +44 -18
- package/dist/cutaway-plugin.d.ts +48 -0
- package/dist/cutaway-plugin.js +364 -0
- package/dist/cutaway.d.ts +27 -0
- package/dist/cutaway.js +49 -0
- package/dist/data-store.d.ts +69 -0
- package/dist/data-store.js +105 -0
- package/dist/debug-shaders.js +17 -3
- package/dist/debug-views.js +4 -2
- package/dist/decals-plugin.d.ts +5 -0
- package/dist/decals-plugin.js +54 -0
- package/dist/decals.d.ts +69 -0
- package/dist/decals.js +326 -0
- package/dist/deferred-shaders.js +2 -0
- package/dist/deferred.js +28 -4
- package/dist/deform.d.ts +5 -4
- package/dist/deform.js +4 -9
- package/dist/describe-plugin.d.ts +4 -2
- package/dist/describe-plugin.js +75 -3
- package/dist/display-nodes.js +37 -13
- package/dist/dynamic-resolution.d.ts +2 -0
- package/dist/dynamic-resolution.js +6 -1
- package/dist/environment-plugin.js +17 -2
- package/dist/environment-state.d.ts +7 -3
- package/dist/environment-state.js +20 -9
- package/dist/environment.js +7 -1
- package/dist/features.d.ts +49 -0
- package/dist/features.js +106 -0
- package/dist/foliage-budget.d.ts +64 -0
- package/dist/foliage-budget.js +108 -0
- package/dist/foliage-path.d.ts +19 -0
- package/dist/foliage-path.js +7 -0
- package/dist/foliage-shaders.d.ts +11 -0
- package/dist/foliage-shaders.js +504 -0
- package/dist/foliage.d.ts +194 -0
- package/dist/foliage.js +1027 -0
- package/dist/forward.d.ts +38 -2
- package/dist/forward.js +608 -186
- package/dist/fxaa.js +12 -2
- package/dist/gizmos-plugin.js +7 -0
- package/dist/gizmos.d.ts +3 -3
- package/dist/gizmos.js +11 -13
- package/dist/gpu-assets.d.ts +58 -4
- package/dist/gpu-assets.js +191 -8
- package/dist/graph.d.ts +45 -0
- package/dist/graph.js +169 -19
- package/dist/health.d.ts +58 -0
- package/dist/health.js +112 -0
- package/dist/index.d.ts +40 -11
- package/dist/index.js +343 -9
- package/dist/instances.d.ts +119 -8
- package/dist/instances.js +387 -78
- package/dist/interior-plugin.d.ts +121 -0
- package/dist/interior-plugin.js +465 -0
- package/dist/interior.d.ts +24 -0
- package/dist/interior.js +13 -0
- package/dist/layers.d.ts +35 -0
- package/dist/layers.js +49 -0
- package/dist/lens-plugin.d.ts +43 -0
- package/dist/lens-plugin.js +304 -0
- package/dist/lens.d.ts +41 -0
- package/dist/lens.js +99 -0
- package/dist/lights.d.ts +50 -5
- package/dist/lights.js +101 -22
- package/dist/material-pipelines.d.ts +37 -4
- package/dist/material-pipelines.js +102 -5
- package/dist/materials.d.ts +23 -0
- package/dist/materials.js +44 -1
- package/dist/outline-plugin.d.ts +48 -0
- package/dist/outline-plugin.js +529 -0
- package/dist/outline.d.ts +15 -0
- package/dist/outline.js +42 -0
- package/dist/overlay-registry.d.ts +3 -1
- package/dist/overlay-registry.js +3 -1
- package/dist/overlays.js +6 -2
- package/dist/perf-overlay.d.ts +9 -0
- package/dist/perf-overlay.js +125 -0
- package/dist/picking.d.ts +17 -0
- package/dist/picking.js +40 -11
- package/dist/pixel-perfect-plugin.js +10 -2
- package/dist/plugin.d.ts +69 -7
- package/dist/plugin.js +223 -25
- package/dist/post-common.d.ts +22 -2
- package/dist/post-common.js +82 -2
- package/dist/post-nodes.d.ts +7 -1
- package/dist/post-nodes.js +235 -111
- package/dist/post-plugin.js +5 -3
- package/dist/post-scene.d.ts +130 -0
- package/dist/post-scene.js +164 -0
- package/dist/post-shaders.js +85 -62
- package/dist/post.js +11 -1
- package/dist/projection.d.ts +24 -0
- package/dist/projection.js +87 -0
- package/dist/readback.d.ts +1 -0
- package/dist/readback.js +1 -0
- package/dist/render-scale.js +1 -0
- package/dist/ribbons-plugin.d.ts +5 -0
- package/dist/ribbons-plugin.js +59 -0
- package/dist/ribbons.d.ts +44 -0
- package/dist/ribbons.js +288 -0
- package/dist/scenario.d.ts +70 -0
- package/dist/scenario.js +125 -0
- package/dist/screen-effects.d.ts +97 -0
- package/dist/screen-effects.js +192 -0
- package/dist/shaders.d.ts +13 -0
- package/dist/shaders.js +189 -34
- package/dist/shadow-catcher.d.ts +9 -0
- package/dist/shadow-catcher.js +133 -0
- package/dist/shadows.d.ts +27 -2
- package/dist/shadows.js +100 -20
- package/dist/standard-fields.d.ts +1 -0
- package/dist/standard-fields.js +5 -0
- package/dist/standard.js +18 -0
- package/dist/stats.d.ts +45 -1
- package/dist/stats.js +91 -3
- package/dist/surface-model.d.ts +57 -0
- package/dist/surface-model.js +289 -0
- package/dist/surface-shaders.d.ts +3 -0
- package/dist/surface-shaders.js +160 -0
- package/dist/surface.d.ts +40 -0
- package/dist/surface.js +86 -0
- package/dist/target.d.ts +1 -13
- package/dist/target.js +1 -26
- package/dist/testing.d.ts +11 -0
- package/dist/testing.js +39 -1
- package/dist/tier.d.ts +13 -0
- package/dist/tier.js +14 -0
- package/dist/timer.d.ts +18 -1
- package/dist/timer.js +73 -20
- package/dist/variants.d.ts +37 -0
- package/dist/variants.js +104 -0
- package/dist/vfx-draw.d.ts +37 -0
- package/dist/vfx-draw.js +291 -0
- package/dist/vfx-state.d.ts +42 -0
- package/dist/vfx-state.js +81 -0
- package/dist/vfx.d.ts +69 -0
- package/dist/vfx.js +103 -0
- package/dist/view-visibility-plugin.d.ts +61 -0
- package/dist/view-visibility-plugin.js +267 -0
- package/dist/view-visibility.d.ts +28 -0
- package/dist/view-visibility.js +46 -0
- package/dist/view.d.ts +29 -1
- package/dist/view.js +57 -34
- package/package.json +23 -11
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type GpuContext } from '@aethervtt/shard-gpu';
|
|
2
|
+
/** Texels per row of a data texture (the baseline rewrite's loaders read the same). */
|
|
3
|
+
export declare const DATA_WIDTH = 1024;
|
|
4
|
+
export interface DataStoreOptions {
|
|
5
|
+
label: string;
|
|
6
|
+
/** Initial size in bytes. Default 256. */
|
|
7
|
+
size?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Buffer usages beyond STORAGE (full tier) or UNIFORM (a baseline uniform block): a GPU pass that
|
|
10
|
+
* writes it, a readback. Data textures ignore it.
|
|
11
|
+
*/
|
|
12
|
+
usage?: GPUBufferUsageFlags;
|
|
13
|
+
/** How baseline shaders read it: a data texture (default), or a uniform block. */
|
|
14
|
+
kind?: 'texture' | 'uniform';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Loads the baseline tier's data textures, once. The render plugin does it as it opens a baseline
|
|
18
|
+
* device, before anything makes a DataStore; so does code that makes one on its own device.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadDataTextures(): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* The layout entry a DataStore of `kind` binds with on this device's tier, for layouts made
|
|
23
|
+
* before (or without) the store: storage on the full tier, a data texture or a uniform block on
|
|
24
|
+
* baseline.
|
|
25
|
+
*/
|
|
26
|
+
export declare function dataEntry(gpu: GpuContext, binding: number, visibility: GPUShaderStageFlags, kind?: 'texture' | 'uniform', type?: GPUBufferBindingType): GPUBindGroupLayoutEntry;
|
|
27
|
+
/**
|
|
28
|
+
* Forgets the data textures `owner` made on `gpu` (an app's render plugin, as it disposes): the
|
|
29
|
+
* ledger destroys their textures, so they must not flush again.
|
|
30
|
+
*/
|
|
31
|
+
export declare function releaseDataStores(gpu: GpuContext, owner: string): void;
|
|
32
|
+
/** Uploads what every baseline data texture of `gpu` was written this frame. Before the submit. */
|
|
33
|
+
export declare function flushDataStores(gpu: GpuContext): void;
|
|
34
|
+
/**
|
|
35
|
+
* A binding of indexed engine data that a vertex or fragment shader reads. Same calls as
|
|
36
|
+
* GpuBuffer: `ensureCapacity`, `write`, `version` (bumps when bind groups must be rebuilt); plus
|
|
37
|
+
* the layout entry and resource of whichever form the tier binds.
|
|
38
|
+
*/
|
|
39
|
+
export declare class DataStore {
|
|
40
|
+
readonly label: string;
|
|
41
|
+
readonly kind: 'texture' | 'uniform';
|
|
42
|
+
/** Who its GPU objects count against (`gpu.owner` when it was made). */
|
|
43
|
+
readonly owner: string;
|
|
44
|
+
/** The texture form: the baseline tier's `@data` binding. */
|
|
45
|
+
readonly textured: boolean;
|
|
46
|
+
private readonly gpu;
|
|
47
|
+
private readonly buffer;
|
|
48
|
+
private readonly texture;
|
|
49
|
+
constructor(gpu: GpuContext, options: DataStoreOptions);
|
|
50
|
+
/** Bumps whenever the GPU object changes: bind groups made on the old one rebuild. */
|
|
51
|
+
get version(): number;
|
|
52
|
+
get byteLength(): number;
|
|
53
|
+
/**
|
|
54
|
+
* The buffer, on the tiers that bind one (full, and baseline uniform blocks): for full-tier
|
|
55
|
+
* passes that read or write it as storage (GPU culling).
|
|
56
|
+
*/
|
|
57
|
+
get gpuBuffer(): GPUBuffer;
|
|
58
|
+
/** Grows to at least `bytes`. Returns true if the GPU object was replaced. */
|
|
59
|
+
ensureCapacity(bytes: number): boolean;
|
|
60
|
+
/** Writes a TypedArray (or a slice of it, in elements) at a byte offset, growing if needed. */
|
|
61
|
+
write(data: ArrayBufferView & ArrayLike<number>, byteOffset?: number, start?: number, count?: number): void;
|
|
62
|
+
/** The bind group layout entry for this binding on this tier. */
|
|
63
|
+
layoutEntry(binding: number, visibility: GPUShaderStageFlags, type?: GPUBufferBindingType): GPUBindGroupLayoutEntry;
|
|
64
|
+
/** What the bind group binds. Rebuild bind groups when `version` changes. */
|
|
65
|
+
resource(): GPUBindingResource;
|
|
66
|
+
/** Uploads the rows written since the last flush (data textures; the rest write through). */
|
|
67
|
+
flush(): void;
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { ShardError } from "@aethervtt/shard-core";
|
|
2
|
+
import { GpuBuffer } from "@aethervtt/shard-gpu";
|
|
3
|
+
import { DATA_TEXTURE_WIDTH } from "@aethervtt/shard-shader";
|
|
4
|
+
const DATA_WIDTH = DATA_TEXTURE_WIDTH;
|
|
5
|
+
let dataTextures;
|
|
6
|
+
async function loadDataTextures() {
|
|
7
|
+
dataTextures ??= await import("./baseline/data-texture.js");
|
|
8
|
+
}
|
|
9
|
+
function dataEntry(gpu, binding, visibility, kind = "texture", type = "read-only-storage") {
|
|
10
|
+
if (gpu.tier !== "baseline") return { binding, visibility, buffer: { type } };
|
|
11
|
+
if (kind === "uniform") return { binding, visibility, buffer: { type: "uniform" } };
|
|
12
|
+
return { binding, visibility, texture: { sampleType: "uint", viewDimension: "2d" } };
|
|
13
|
+
}
|
|
14
|
+
function releaseDataStores(gpu, owner) {
|
|
15
|
+
dataTextures?.releaseDataTextures(gpu, owner);
|
|
16
|
+
}
|
|
17
|
+
function flushDataStores(gpu) {
|
|
18
|
+
dataTextures?.flushDataTextures(gpu);
|
|
19
|
+
}
|
|
20
|
+
class DataStore {
|
|
21
|
+
label;
|
|
22
|
+
kind;
|
|
23
|
+
/** Who its GPU objects count against (`gpu.owner` when it was made). */
|
|
24
|
+
owner;
|
|
25
|
+
/** The texture form: the baseline tier's `@data` binding. */
|
|
26
|
+
textured;
|
|
27
|
+
gpu;
|
|
28
|
+
buffer;
|
|
29
|
+
texture;
|
|
30
|
+
constructor(gpu, options) {
|
|
31
|
+
this.gpu = gpu;
|
|
32
|
+
this.label = options.label;
|
|
33
|
+
this.owner = gpu.owner;
|
|
34
|
+
this.kind = options.kind ?? "texture";
|
|
35
|
+
const size = align16(Math.max(16, options.size ?? 256));
|
|
36
|
+
this.textured = gpu.tier === "baseline" && this.kind === "texture";
|
|
37
|
+
if (this.textured) {
|
|
38
|
+
if (!dataTextures) {
|
|
39
|
+
throw new ShardError(
|
|
40
|
+
"render/data-textures-not-loaded",
|
|
41
|
+
`DataStore "${options.label}" needs the baseline tier's data textures, which aren't loaded`,
|
|
42
|
+
{ hint: "Await loadDataTextures() before making DataStores on a baseline device." }
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
this.texture = new dataTextures.DataTexture(gpu, options.label, size);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
this.buffer = new GpuBuffer(gpu, {
|
|
49
|
+
label: options.label,
|
|
50
|
+
usage: (gpu.tier === "baseline" ? GPUBufferUsage.UNIFORM : GPUBufferUsage.STORAGE) | (options.usage ?? 0),
|
|
51
|
+
size
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Bumps whenever the GPU object changes: bind groups made on the old one rebuild. */
|
|
55
|
+
get version() {
|
|
56
|
+
return this.buffer ? this.buffer.version : this.texture.currentVersion;
|
|
57
|
+
}
|
|
58
|
+
get byteLength() {
|
|
59
|
+
return this.buffer ? this.buffer.byteLength : this.texture.size;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The buffer, on the tiers that bind one (full, and baseline uniform blocks): for full-tier
|
|
63
|
+
* passes that read or write it as storage (GPU culling).
|
|
64
|
+
*/
|
|
65
|
+
get gpuBuffer() {
|
|
66
|
+
if (!this.buffer) throw new Error(`${this.label} is a data texture on this tier`);
|
|
67
|
+
return this.buffer.buffer;
|
|
68
|
+
}
|
|
69
|
+
/** Grows to at least `bytes`. Returns true if the GPU object was replaced. */
|
|
70
|
+
ensureCapacity(bytes) {
|
|
71
|
+
return this.buffer ? this.buffer.ensureCapacity(bytes) : this.texture.ensureCapacity(bytes);
|
|
72
|
+
}
|
|
73
|
+
/** Writes a TypedArray (or a slice of it, in elements) at a byte offset, growing if needed. */
|
|
74
|
+
write(data, byteOffset = 0, start = 0, count) {
|
|
75
|
+
if (this.buffer) this.buffer.write(data, byteOffset, start, count);
|
|
76
|
+
else this.texture.write(data, byteOffset, start, count);
|
|
77
|
+
}
|
|
78
|
+
/** The bind group layout entry for this binding on this tier. */
|
|
79
|
+
layoutEntry(binding, visibility, type = "read-only-storage") {
|
|
80
|
+
return dataEntry(this.gpu, binding, visibility, this.kind, type);
|
|
81
|
+
}
|
|
82
|
+
/** What the bind group binds. Rebuild bind groups when `version` changes. */
|
|
83
|
+
resource() {
|
|
84
|
+
return this.buffer ? { buffer: this.buffer.buffer } : this.texture.resource();
|
|
85
|
+
}
|
|
86
|
+
/** Uploads the rows written since the last flush (data textures; the rest write through). */
|
|
87
|
+
flush() {
|
|
88
|
+
this.texture?.flush();
|
|
89
|
+
}
|
|
90
|
+
destroy() {
|
|
91
|
+
this.buffer?.destroy();
|
|
92
|
+
this.texture?.destroy();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function align16(n) {
|
|
96
|
+
return n + 15 & ~15;
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
DATA_WIDTH,
|
|
100
|
+
DataStore,
|
|
101
|
+
dataEntry,
|
|
102
|
+
flushDataStores,
|
|
103
|
+
loadDataTextures,
|
|
104
|
+
releaseDataStores
|
|
105
|
+
};
|
package/dist/debug-shaders.js
CHANGED
|
@@ -37,7 +37,7 @@ struct Line {
|
|
|
37
37
|
bits: u32,
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
@group(1) @binding(0) var<storage, read> lines: array<Line>;
|
|
40
|
+
@data @group(1) @binding(0) var<storage, read> lines: array<Line>;
|
|
41
41
|
|
|
42
42
|
struct LineOutput {
|
|
43
43
|
@builtin(position) clip: vec4f,
|
|
@@ -112,7 +112,7 @@ struct Glyph {
|
|
|
112
112
|
_pad1: u32,
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
@group(1) @binding(1) var<storage, read> glyphs: array<Glyph>;
|
|
115
|
+
@data @group(1) @binding(1) var<storage, read> glyphs: array<Glyph>;
|
|
116
116
|
@group(1) @binding(3) var atlas: texture_2d<f32>;
|
|
117
117
|
|
|
118
118
|
struct GlyphOutput {
|
|
@@ -167,11 +167,21 @@ struct PickOutput {
|
|
|
167
167
|
import shard::view::view;
|
|
168
168
|
import shard::mesh::{ instance_at, mesh_vertex_at };
|
|
169
169
|
import shard::pick::types::PickOutput;
|
|
170
|
+
@if(CUTAWAY) import shard::cutaway::cutaway_clip;
|
|
170
171
|
|
|
171
172
|
struct PickVertex {
|
|
172
173
|
@builtin(position) clip: vec4f,
|
|
173
174
|
@location(0) normal: vec3f,
|
|
174
175
|
@location(1) @interpolate(flat) entity: u32,
|
|
176
|
+
/**
|
|
177
|
+
* Clip z and w, interpolated perspective-correct: z / w is the surface's own depth. The
|
|
178
|
+
* fragment's position.z includes the depth bias ground bands draw with (0057), which would move
|
|
179
|
+
* a picked point toward the camera.
|
|
180
|
+
*/
|
|
181
|
+
@location(2) zw: vec2f,
|
|
182
|
+
/** Where cutaways (0070) test the surface. */
|
|
183
|
+
@if(CUTAWAY) @location(3) world: vec3f,
|
|
184
|
+
@if(CUTAWAY) @location(4) @interpolate(flat) flags: u32,
|
|
175
185
|
}
|
|
176
186
|
|
|
177
187
|
@vertex fn vs(
|
|
@@ -190,14 +200,18 @@ struct PickVertex {
|
|
|
190
200
|
out.clip = view.viewProjNoJitter * vec4f(m.world_position, 1.0);
|
|
191
201
|
out.normal = m.world_normal;
|
|
192
202
|
out.entity = inst.entity;
|
|
203
|
+
out.zw = out.clip.zw;
|
|
204
|
+
@if(CUTAWAY) out.world = m.world_position;
|
|
205
|
+
@if(CUTAWAY) out.flags = m.flags;
|
|
193
206
|
return out;
|
|
194
207
|
}
|
|
195
208
|
|
|
196
209
|
@fragment fn fs(in: PickVertex, @builtin(front_facing) front: bool) -> PickOutput {
|
|
210
|
+
@if(CUTAWAY) cutaway_clip(in.world, in.clip.xy, in.flags);
|
|
197
211
|
var out: PickOutput;
|
|
198
212
|
out.id = in.entity;
|
|
199
213
|
let n = normalize(in.normal);
|
|
200
|
-
out.normal = vec4f(select(-n, n, front), in.
|
|
214
|
+
out.normal = vec4f(select(-n, n, front), in.zw.x / in.zw.y);
|
|
201
215
|
return out;
|
|
202
216
|
}`
|
|
203
217
|
};
|
package/dist/debug-views.js
CHANGED
|
@@ -161,8 +161,10 @@ function describeLighting(world) {
|
|
|
161
161
|
})),
|
|
162
162
|
clusters: {
|
|
163
163
|
grid: [16, 9, 24],
|
|
164
|
-
|
|
165
|
-
|
|
164
|
+
// GPU clustering's stats; baseline bins on the CPU and drops lights past its budget.
|
|
165
|
+
maxLightsPerCluster: pv.clusters?.latest.maxLightsPerCluster ?? null,
|
|
166
|
+
overflows: pv.clusters?.latest.overflows ?? 0,
|
|
167
|
+
baselineLights: pv.baseline ? { packed: pv.baseline.count, dropped: pv.baseline.dropped } : null
|
|
166
168
|
},
|
|
167
169
|
cascades: pv.cascades.count,
|
|
168
170
|
cascadeSplits: [...pv.cascades.splits.subarray(0, pv.cascades.count)]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { defineResource, defineSystem, Last, PostUpdate } from "@aethervtt/shard-core";
|
|
2
|
+
import { definePlugin, FrameDemand } from "@aethervtt/shard-runtime";
|
|
3
|
+
import { TransformSystems } from "@aethervtt/shard-transform";
|
|
4
|
+
import { Decals, describeDecals, updateDecals } from "./decals.js";
|
|
5
|
+
import { addRenderFeatures } from "./features.js";
|
|
6
|
+
import { Graph, RenderDescribers, RenderSet, Shaders } from "./plugin.js";
|
|
7
|
+
import { registerShaders } from "./shaders.js";
|
|
8
|
+
import { DecalsPath } from "./vfx.js";
|
|
9
|
+
import { VFX_SHADERS, VfxRenderer } from "./vfx-draw.js";
|
|
10
|
+
import { computeVisibility } from "./visibility.js";
|
|
11
|
+
const DecalGpu = defineResource("render/DecalGpu", {
|
|
12
|
+
init: () => ({})
|
|
13
|
+
});
|
|
14
|
+
const prune = defineSystem({
|
|
15
|
+
name: "render/decals-prune",
|
|
16
|
+
run: (_, world) => world.resource(DecalGpu).renderer?.prune(world)
|
|
17
|
+
});
|
|
18
|
+
const decalsPlugin = definePlugin({
|
|
19
|
+
name: "render/decals",
|
|
20
|
+
dependencies: ["render/forward"],
|
|
21
|
+
provides: [Decals, DecalGpu, updateDecals],
|
|
22
|
+
build(app) {
|
|
23
|
+
app.world.initResource(DecalsPath);
|
|
24
|
+
app.world.initResource(Decals);
|
|
25
|
+
app.world.initResource(DecalGpu);
|
|
26
|
+
app.addSystems(PostUpdate, updateDecals.after(TransformSystems, computeVisibility));
|
|
27
|
+
app.addSystems(Last, prune.inSet(RenderSet.Prepare));
|
|
28
|
+
},
|
|
29
|
+
ready(app) {
|
|
30
|
+
const w = app.world, renderer = new VfxRenderer(w.resource(Decals).records, true);
|
|
31
|
+
w.resource(DecalGpu).renderer = renderer;
|
|
32
|
+
registerShaders(w.resource(Shaders), VFX_SHADERS);
|
|
33
|
+
addRenderFeatures(w, {
|
|
34
|
+
name: "render/decals",
|
|
35
|
+
description: "Clipped rigid-mesh projected decals.",
|
|
36
|
+
nodes: ["decals"],
|
|
37
|
+
baseline: { strategy: "CPU projection and the same vertex/fragment passes" }
|
|
38
|
+
});
|
|
39
|
+
w.resource(Graph).addNode("decals", renderer.node());
|
|
40
|
+
w.initResource(RenderDescribers).set("decals", describeDecals);
|
|
41
|
+
},
|
|
42
|
+
dispose(app) {
|
|
43
|
+
app.world.resource(DecalGpu).renderer?.dispose();
|
|
44
|
+
const s = app.world.resource(Decals);
|
|
45
|
+
s.records.length = 0;
|
|
46
|
+
s.receivers.length = 0;
|
|
47
|
+
s.receiverByEntity.clear();
|
|
48
|
+
app.world.tryResource(FrameDemand)?.set("render/decals", false);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
export {
|
|
52
|
+
DecalGpu,
|
|
53
|
+
decalsPlugin
|
|
54
|
+
};
|
package/dist/decals.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type Entity, type World } from '@aethervtt/shard-core';
|
|
2
|
+
import type { Mesh } from '@aethervtt/shard-mesh';
|
|
3
|
+
import { type VfxRecord } from './vfx-state.js';
|
|
4
|
+
export declare const DECAL_TRIANGLE_BUDGET = 32768;
|
|
5
|
+
export interface DecalRange {
|
|
6
|
+
start: number;
|
|
7
|
+
count: number;
|
|
8
|
+
entity: Entity;
|
|
9
|
+
layers: number;
|
|
10
|
+
}
|
|
11
|
+
interface Receiver {
|
|
12
|
+
entity: Entity;
|
|
13
|
+
matrix: Float64Array;
|
|
14
|
+
mesh: Mesh | undefined;
|
|
15
|
+
version: number;
|
|
16
|
+
mask: number;
|
|
17
|
+
visible: boolean;
|
|
18
|
+
unsupported: boolean;
|
|
19
|
+
layers: number;
|
|
20
|
+
seen: number;
|
|
21
|
+
}
|
|
22
|
+
export interface DecalRecord extends VfxRecord {
|
|
23
|
+
inverse: Float64Array;
|
|
24
|
+
settings: Float64Array;
|
|
25
|
+
geometryCount: number;
|
|
26
|
+
receiverRevision: number;
|
|
27
|
+
rebuilds: number;
|
|
28
|
+
ranges: DecalRange[];
|
|
29
|
+
clipA: Float64Array;
|
|
30
|
+
clipB: Float64Array;
|
|
31
|
+
worldTriangle: Float64Array;
|
|
32
|
+
}
|
|
33
|
+
export declare const Decals: import("@aethervtt/shard-core").ResourceDef<{
|
|
34
|
+
records: DecalRecord[];
|
|
35
|
+
receivers: Receiver[];
|
|
36
|
+
receiverByEntity: Map<number, Receiver>;
|
|
37
|
+
frame: number;
|
|
38
|
+
revision: number;
|
|
39
|
+
rejected: number;
|
|
40
|
+
}>;
|
|
41
|
+
/** Clips a convex polygon against one axis-aligned half-space. Scratch fits a clipped triangle. */
|
|
42
|
+
export declare function clipPlane(input: Float64Array, count: number, output: Float64Array, axis: number, sign: number, half: number): number;
|
|
43
|
+
/** Cold projection work. Output is all-or-nothing on budget/unsupported errors. */
|
|
44
|
+
export declare function projectDecal(r: DecalRecord, receivers: readonly Receiver[]): void;
|
|
45
|
+
export declare const updateDecals: import("@aethervtt/shard-core").SystemDef<{
|
|
46
|
+
q: import("@aethervtt/shard-core").Query;
|
|
47
|
+
receivers: import("@aethervtt/shard-core").Query;
|
|
48
|
+
}>;
|
|
49
|
+
export declare function describeDecals(world: World): {
|
|
50
|
+
rejected: number;
|
|
51
|
+
error: string | null;
|
|
52
|
+
receivers: number;
|
|
53
|
+
records: {
|
|
54
|
+
entity: number;
|
|
55
|
+
kind: "beam" | "decal" | "trail";
|
|
56
|
+
vertices: number;
|
|
57
|
+
age: number;
|
|
58
|
+
opacity: number;
|
|
59
|
+
error: string | null;
|
|
60
|
+
visible: boolean;
|
|
61
|
+
texture: string;
|
|
62
|
+
playing: boolean;
|
|
63
|
+
paused: boolean;
|
|
64
|
+
cpuBytes: number;
|
|
65
|
+
rebuilds: number;
|
|
66
|
+
triangles: number;
|
|
67
|
+
}[];
|
|
68
|
+
};
|
|
69
|
+
export {};
|