@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
package/dist/vfx-draw.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { assetServer } from "@aethervtt/shard-assets";
|
|
2
|
+
import { GpuBuffer } from "@aethervtt/shard-gpu";
|
|
3
|
+
import { Textures } from "@aethervtt/shard-texture";
|
|
4
|
+
import { GpuAssetsResource } from "./gpu-assets.js";
|
|
5
|
+
import { RenderPhase } from "./graph.js";
|
|
6
|
+
import { InstanceSlot } from "./instances.js";
|
|
7
|
+
import { Shaders, Views } from "./plugin.js";
|
|
8
|
+
import { bindingDimension } from "./tier.js";
|
|
9
|
+
import { cameraOf } from "./view.js";
|
|
10
|
+
const VFX_SHADERS = {
|
|
11
|
+
"shard::vfx::ribbon": `
|
|
12
|
+
struct View { vp: mat4x4f, eye: vec4f, forward: vec4f, params: vec4f, tint: vec4f }
|
|
13
|
+
@group(0) @binding(0) var<uniform> view: View;
|
|
14
|
+
@group(0) @binding(1) var image: texture_2d<f32>;
|
|
15
|
+
@group(0) @binding(2) var image_sampler: sampler;
|
|
16
|
+
struct Input {
|
|
17
|
+
@location(0) position: vec3f,
|
|
18
|
+
@location(1) tangent: vec3f,
|
|
19
|
+
@location(2) uv: vec2f,
|
|
20
|
+
@location(3) color: vec4f,
|
|
21
|
+
@location(4) width_side: vec2f,
|
|
22
|
+
}
|
|
23
|
+
struct Output { @builtin(position) position: vec4f, @location(0) uv: vec2f, @location(1) color: vec4f }
|
|
24
|
+
@vertex fn vs(in: Input) -> Output {
|
|
25
|
+
var out: Output;
|
|
26
|
+
var pos = in.position;
|
|
27
|
+
if (in.width_side.x > 0.0) {
|
|
28
|
+
var toward = view.eye.xyz - pos;
|
|
29
|
+
if (view.eye.w > 0.5) { toward = -view.forward.xyz; }
|
|
30
|
+
var side = cross(in.tangent, toward);
|
|
31
|
+
if (dot(side, side) < 1e-12) { side = cross(in.tangent, vec3f(0.0, 1.0, 0.0)); }
|
|
32
|
+
if (dot(side, side) < 1e-12) { side = vec3f(1.0, 0.0, 0.0); }
|
|
33
|
+
pos += normalize(side) * in.width_side.x * in.width_side.y;
|
|
34
|
+
}
|
|
35
|
+
out.position = view.vp * vec4f(pos, 1.0);
|
|
36
|
+
out.uv = in.uv;
|
|
37
|
+
out.color = in.color * view.tint;
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
@fragment fn fs(in: Output) -> @location(0) vec4f {
|
|
41
|
+
let c = textureSample(image, image_sampler, in.uv) * in.color;
|
|
42
|
+
let alpha = c.a * view.params.x;
|
|
43
|
+
if (alpha <= 0.00001) { discard; }
|
|
44
|
+
return vec4f(c.rgb * alpha, alpha);
|
|
45
|
+
}`
|
|
46
|
+
};
|
|
47
|
+
const OVER = {
|
|
48
|
+
color: { srcFactor: "one", dstFactor: "one-minus-src-alpha" },
|
|
49
|
+
alpha: { srcFactor: "one", dstFactor: "one-minus-src-alpha" }
|
|
50
|
+
};
|
|
51
|
+
const ADD = {
|
|
52
|
+
color: { srcFactor: "one", dstFactor: "one" },
|
|
53
|
+
alpha: { srcFactor: "zero", dstFactor: "one" }
|
|
54
|
+
};
|
|
55
|
+
const BUFFERS = [
|
|
56
|
+
{
|
|
57
|
+
arrayStride: 56,
|
|
58
|
+
attributes: [
|
|
59
|
+
{ shaderLocation: 0, offset: 0, format: "float32x3" },
|
|
60
|
+
{ shaderLocation: 1, offset: 12, format: "float32x3" },
|
|
61
|
+
{ shaderLocation: 2, offset: 24, format: "float32x2" },
|
|
62
|
+
{ shaderLocation: 3, offset: 32, format: "float32x4" },
|
|
63
|
+
{ shaderLocation: 4, offset: 48, format: "float32x2" }
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
class VfxRenderer {
|
|
68
|
+
draws = [];
|
|
69
|
+
generation = -1;
|
|
70
|
+
layout;
|
|
71
|
+
pipelines = [void 0, void 0];
|
|
72
|
+
white;
|
|
73
|
+
whiteView;
|
|
74
|
+
sampler;
|
|
75
|
+
decal;
|
|
76
|
+
records;
|
|
77
|
+
constructor(records, decal) {
|
|
78
|
+
this.records = records;
|
|
79
|
+
this.decal = decal;
|
|
80
|
+
}
|
|
81
|
+
dispose() {
|
|
82
|
+
for (let i = 0; i < this.draws.length; i++) {
|
|
83
|
+
this.draws[i].vertices.destroy();
|
|
84
|
+
this.draws[i].uniform.destroy();
|
|
85
|
+
}
|
|
86
|
+
this.draws.length = 0;
|
|
87
|
+
this.white?.destroy();
|
|
88
|
+
this.white = void 0;
|
|
89
|
+
this.whiteView = void 0;
|
|
90
|
+
}
|
|
91
|
+
prune(world) {
|
|
92
|
+
const views = world.resource(Views).list;
|
|
93
|
+
for (let i = this.draws.length - 1; i >= 0; i--) {
|
|
94
|
+
const d = this.draws[i];
|
|
95
|
+
let view = false;
|
|
96
|
+
for (let j = 0; j < views.length; j++)
|
|
97
|
+
if (views[j].name === d.view) {
|
|
98
|
+
view = true;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (!view || !d.source.count || !this.records.includes(d.source)) {
|
|
102
|
+
d.vertices.destroy();
|
|
103
|
+
d.uniform.destroy();
|
|
104
|
+
this.draws.splice(i, 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
init(gpu) {
|
|
109
|
+
if (this.generation === gpu.generation) return;
|
|
110
|
+
this.dispose();
|
|
111
|
+
this.generation = gpu.generation;
|
|
112
|
+
this.pipelines[0] = void 0;
|
|
113
|
+
this.pipelines[1] = void 0;
|
|
114
|
+
this.layout = gpu.layouts.bindGroupLayout({
|
|
115
|
+
label: "vfx",
|
|
116
|
+
entries: [
|
|
117
|
+
{
|
|
118
|
+
binding: 0,
|
|
119
|
+
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
120
|
+
buffer: { type: "uniform" }
|
|
121
|
+
},
|
|
122
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, texture: { sampleType: "float" } },
|
|
123
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, sampler: { type: "filtering" } }
|
|
124
|
+
]
|
|
125
|
+
});
|
|
126
|
+
this.white = gpu.device.createTexture({
|
|
127
|
+
label: "vfx/white",
|
|
128
|
+
size: [1, 1],
|
|
129
|
+
format: "rgba8unorm",
|
|
130
|
+
usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST,
|
|
131
|
+
...bindingDimension(gpu, "2d")
|
|
132
|
+
});
|
|
133
|
+
gpu.device.queue.writeTexture(
|
|
134
|
+
{ texture: this.white },
|
|
135
|
+
new Uint8Array([255, 255, 255, 255]),
|
|
136
|
+
{ bytesPerRow: 4 },
|
|
137
|
+
[1, 1]
|
|
138
|
+
);
|
|
139
|
+
this.whiteView = this.white.createView();
|
|
140
|
+
this.sampler = gpu.device.createSampler({
|
|
141
|
+
magFilter: "linear",
|
|
142
|
+
minFilter: "linear",
|
|
143
|
+
addressModeU: this.decal ? "clamp-to-edge" : "repeat",
|
|
144
|
+
addressModeV: "clamp-to-edge"
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
node() {
|
|
148
|
+
return {
|
|
149
|
+
kind: "render",
|
|
150
|
+
phase: RenderPhase.Resolve + (this.decal ? 5 : 15),
|
|
151
|
+
reads: ["depth"],
|
|
152
|
+
writes: ["hdr"],
|
|
153
|
+
color: [{ resource: "hdr" }],
|
|
154
|
+
depth: { resource: "depth", readOnly: true },
|
|
155
|
+
enabled: (view) => {
|
|
156
|
+
const cam = cameraOf(view);
|
|
157
|
+
if (!cam) return false;
|
|
158
|
+
for (let i = 0; i < this.records.length; i++) {
|
|
159
|
+
const r = this.records[i];
|
|
160
|
+
if (r.count && r.visible && r.opacity > 0 && r.layers & cam.layers) return true;
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
},
|
|
164
|
+
run: (ctx) => this.draw(ctx)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
draw(ctx) {
|
|
168
|
+
const gpu = ctx.gpu, cam = cameraOf(ctx.view), pass = ctx.renderPass;
|
|
169
|
+
this.init(gpu);
|
|
170
|
+
const textures = ctx.world.resource(Textures), assets = ctx.world.resource(GpuAssetsResource);
|
|
171
|
+
for (let i = 0; i < this.records.length; i++) {
|
|
172
|
+
const r = this.records[i];
|
|
173
|
+
if (!r.count || !r.visible || r.opacity <= 0 || !(r.layers & cam.layers)) continue;
|
|
174
|
+
const index = r.additive ? 1 : 0;
|
|
175
|
+
let pipeline = this.pipelines[index];
|
|
176
|
+
if (!pipeline) {
|
|
177
|
+
const module = ctx.world.resource(Shaders).module(gpu, { root: "shard::vfx::ribbon" });
|
|
178
|
+
if (!module) {
|
|
179
|
+
gpu.pipelines.skipped++;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
pipeline = gpu.pipelines.render({
|
|
183
|
+
label: index ? "vfx/additive" : "vfx/alpha",
|
|
184
|
+
layout: gpu.layouts.pipelineLayout({ bindGroupLayouts: [this.layout] }),
|
|
185
|
+
vertex: { module, entryPoint: "vs", buffers: BUFFERS },
|
|
186
|
+
fragment: {
|
|
187
|
+
module,
|
|
188
|
+
entryPoint: "fs",
|
|
189
|
+
targets: [{ format: "rgba16float", blend: index ? ADD : OVER }]
|
|
190
|
+
},
|
|
191
|
+
primitive: { topology: "triangle-list", cullMode: "none" },
|
|
192
|
+
depthStencil: {
|
|
193
|
+
format: "depth32float",
|
|
194
|
+
depthWriteEnabled: false,
|
|
195
|
+
depthCompare: "greater-equal",
|
|
196
|
+
// Resolved MSAA depth comes from a subsample, not the fragment center.
|
|
197
|
+
// Positive reversed-Z slope bias keeps coplanar decals visible on every adapter.
|
|
198
|
+
depthBias: this.decal ? 1 : 0,
|
|
199
|
+
depthBiasSlopeScale: this.decal ? 1 : 0
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
if (!pipeline) {
|
|
203
|
+
gpu.pipelines.skipped++;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
this.pipelines[index] = pipeline;
|
|
207
|
+
}
|
|
208
|
+
let d;
|
|
209
|
+
for (let j = 0; j < this.draws.length; j++)
|
|
210
|
+
if (this.draws[j].source === r && this.draws[j].view === ctx.view.name) {
|
|
211
|
+
d = this.draws[j];
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
if (!d) {
|
|
215
|
+
d = {
|
|
216
|
+
source: r,
|
|
217
|
+
view: ctx.view.name,
|
|
218
|
+
vertices: new GpuBuffer(gpu, {
|
|
219
|
+
label: "vfx/vertices",
|
|
220
|
+
usage: GPUBufferUsage.VERTEX,
|
|
221
|
+
size: r.vertices.byteLength
|
|
222
|
+
}),
|
|
223
|
+
uniform: new GpuBuffer(gpu, {
|
|
224
|
+
label: "vfx/view",
|
|
225
|
+
usage: GPUBufferUsage.UNIFORM,
|
|
226
|
+
size: 128
|
|
227
|
+
}),
|
|
228
|
+
data: new Float32Array(32),
|
|
229
|
+
group: void 0,
|
|
230
|
+
texture: void 0,
|
|
231
|
+
version: -1,
|
|
232
|
+
generation: gpu.generation
|
|
233
|
+
};
|
|
234
|
+
this.draws.push(d);
|
|
235
|
+
}
|
|
236
|
+
if (d.version !== r.version) {
|
|
237
|
+
d.vertices.write(r.vertices, 0, 0, r.count * 14);
|
|
238
|
+
d.version = r.version;
|
|
239
|
+
}
|
|
240
|
+
const texture = r.texture ? textures.get(r.texture) : void 0, gt = texture ? assets.texture(texture) : void 0;
|
|
241
|
+
r.textureState = !r.texture ? "none" : gt ? "ready" : assetServer(ctx.world).entry(r.texture) ? "loading" : "missing";
|
|
242
|
+
const image = gt ? texture.usage === "color" ? gt.srgb : gt.linear : this.whiteView;
|
|
243
|
+
if (!d.group || d.texture !== image) {
|
|
244
|
+
d.texture = image;
|
|
245
|
+
d.group = gpu.device.createBindGroup({
|
|
246
|
+
label: "vfx/draw",
|
|
247
|
+
layout: this.layout,
|
|
248
|
+
entries: [
|
|
249
|
+
{ binding: 0, resource: { buffer: d.uniform.buffer } },
|
|
250
|
+
{ binding: 1, resource: image },
|
|
251
|
+
{ binding: 2, resource: this.sampler }
|
|
252
|
+
]
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
d.data.set(cam.viewProj);
|
|
256
|
+
d.data.set(cam.position, 16);
|
|
257
|
+
d.data[19] = cam.orthographic ? 1 : 0;
|
|
258
|
+
d.data.set(cam.forward, 20);
|
|
259
|
+
d.data[24] = r.opacity;
|
|
260
|
+
if (this.decal) {
|
|
261
|
+
const colors = r.columns.color;
|
|
262
|
+
for (let k = 0; k < 4; k++) d.data[28 + k] = colors[r.row * 4 + k];
|
|
263
|
+
} else {
|
|
264
|
+
d.data[28] = 1;
|
|
265
|
+
d.data[29] = 1;
|
|
266
|
+
d.data[30] = 1;
|
|
267
|
+
d.data[31] = 1;
|
|
268
|
+
}
|
|
269
|
+
d.uniform.write(d.data);
|
|
270
|
+
pass.setPipeline(pipeline);
|
|
271
|
+
pass.setBindGroup(0, d.group);
|
|
272
|
+
pass.setVertexBuffer(0, d.vertices.buffer);
|
|
273
|
+
if (this.decal) {
|
|
274
|
+
const ranges = r.ranges;
|
|
275
|
+
for (let j = 0; j < ranges.length; j++) {
|
|
276
|
+
const range = ranges[j];
|
|
277
|
+
if (!(range.layers & cam.layers)) continue;
|
|
278
|
+
if (cam.hidden && ctx.world.has(range.entity, InstanceSlot)) {
|
|
279
|
+
const table = ctx.world.entityTableUnchecked(range.entity), row = ctx.world.entityRowUnchecked(range.entity), slot = table.column(InstanceSlot, "slot")[row] - 1;
|
|
280
|
+
if (slot >= 0 && (cam.hidden.bits[slot >> 5] ?? 0) & 1 << (slot & 31)) continue;
|
|
281
|
+
}
|
|
282
|
+
pass.draw(range.count, 1, range.start);
|
|
283
|
+
}
|
|
284
|
+
} else pass.draw(r.count);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
export {
|
|
289
|
+
VFX_SHADERS,
|
|
290
|
+
VfxRenderer
|
|
291
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AssetRef, ComponentDef, Entity, Table, TypedArray, World } from '@aethervtt/shard-core';
|
|
2
|
+
export declare const VFX_STRIDE = 14;
|
|
3
|
+
export interface VfxRecord {
|
|
4
|
+
entity: Entity;
|
|
5
|
+
def: ComponentDef;
|
|
6
|
+
kind: 'trail' | 'beam' | 'decal';
|
|
7
|
+
table: Table;
|
|
8
|
+
row: number;
|
|
9
|
+
columns: Record<string, TypedArray | unknown[]>;
|
|
10
|
+
matrix: Float64Array;
|
|
11
|
+
vertices: Float32Array;
|
|
12
|
+
count: number;
|
|
13
|
+
version: number;
|
|
14
|
+
age: number;
|
|
15
|
+
wasPlaying: boolean;
|
|
16
|
+
error: string | null;
|
|
17
|
+
seen: number;
|
|
18
|
+
opacity: number;
|
|
19
|
+
texture: AssetRef<'Texture'> | null;
|
|
20
|
+
layers: number;
|
|
21
|
+
additive: boolean;
|
|
22
|
+
textureState: string;
|
|
23
|
+
visible: boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function createRecord(world: World, entity: Entity, def: ComponentDef, kind: VfxRecord['kind'], vertices: number): VfxRecord;
|
|
26
|
+
export declare function refreshRecord(world: World, r: VfxRecord): void;
|
|
27
|
+
export declare function number(r: VfxRecord, field: string): number;
|
|
28
|
+
export declare function matrixChanged(world: World, entity: Entity, out: Float64Array): boolean;
|
|
29
|
+
export declare function point(out: Float64Array, at: number, m: ArrayLike<number>, x: number, y: number, z: number): void;
|
|
30
|
+
export declare function recordDescription(r: VfxRecord): {
|
|
31
|
+
entity: number;
|
|
32
|
+
kind: "beam" | "decal" | "trail";
|
|
33
|
+
vertices: number;
|
|
34
|
+
age: number;
|
|
35
|
+
opacity: number;
|
|
36
|
+
error: string | null;
|
|
37
|
+
visible: boolean;
|
|
38
|
+
cpuBytes: number;
|
|
39
|
+
texture: string;
|
|
40
|
+
playing: boolean;
|
|
41
|
+
paused: boolean;
|
|
42
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { GlobalTransform } from "@aethervtt/shard-transform";
|
|
2
|
+
import { ComputedVisibility } from "./visibility.js";
|
|
3
|
+
const VFX_STRIDE = 14;
|
|
4
|
+
function createRecord(world, entity, def, kind, vertices) {
|
|
5
|
+
const table = world.entityTable(entity);
|
|
6
|
+
return {
|
|
7
|
+
entity,
|
|
8
|
+
def,
|
|
9
|
+
kind,
|
|
10
|
+
table,
|
|
11
|
+
row: world.entityRow(entity),
|
|
12
|
+
columns: table.storage(def).byName,
|
|
13
|
+
matrix: new Float64Array(12),
|
|
14
|
+
vertices: new Float32Array(vertices * VFX_STRIDE),
|
|
15
|
+
count: 0,
|
|
16
|
+
version: 0,
|
|
17
|
+
age: 0,
|
|
18
|
+
wasPlaying: false,
|
|
19
|
+
error: null,
|
|
20
|
+
seen: 0,
|
|
21
|
+
opacity: 1,
|
|
22
|
+
texture: null,
|
|
23
|
+
layers: 1,
|
|
24
|
+
additive: false,
|
|
25
|
+
textureState: "none",
|
|
26
|
+
visible: true
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function refreshRecord(world, r) {
|
|
30
|
+
r.table = world.entityTableUnchecked(r.entity);
|
|
31
|
+
r.row = world.entityRowUnchecked(r.entity);
|
|
32
|
+
r.columns = r.table.storage(r.def).byName;
|
|
33
|
+
r.texture = r.columns.texture[r.row];
|
|
34
|
+
r.opacity = number(r, "opacity");
|
|
35
|
+
r.layers = number(r, "layers");
|
|
36
|
+
r.additive = number(r, "blend") === 1;
|
|
37
|
+
r.visible = !r.table.has(ComputedVisibility) || r.table.column(ComputedVisibility, "visible")[r.row] !== 0;
|
|
38
|
+
}
|
|
39
|
+
function number(r, field) {
|
|
40
|
+
return r.columns[field][r.row];
|
|
41
|
+
}
|
|
42
|
+
function matrixChanged(world, entity, out) {
|
|
43
|
+
const t = world.entityTableUnchecked(entity), row = world.entityRowUnchecked(entity);
|
|
44
|
+
const m = t.column(GlobalTransform, "matrix");
|
|
45
|
+
let changed = false;
|
|
46
|
+
for (let i = 0; i < 12; i++) {
|
|
47
|
+
const v = m[row * 12 + i];
|
|
48
|
+
if (out[i] !== v) changed = true;
|
|
49
|
+
out[i] = v;
|
|
50
|
+
}
|
|
51
|
+
return changed;
|
|
52
|
+
}
|
|
53
|
+
function point(out, at, m, x, y, z) {
|
|
54
|
+
out[at] = m[0] * x + m[1] * y + m[2] * z + m[3];
|
|
55
|
+
out[at + 1] = m[4] * x + m[5] * y + m[6] * z + m[7];
|
|
56
|
+
out[at + 2] = m[8] * x + m[9] * y + m[10] * z + m[11];
|
|
57
|
+
}
|
|
58
|
+
function recordDescription(r) {
|
|
59
|
+
return {
|
|
60
|
+
entity: r.entity,
|
|
61
|
+
kind: r.kind,
|
|
62
|
+
vertices: r.count,
|
|
63
|
+
age: r.age,
|
|
64
|
+
opacity: r.opacity,
|
|
65
|
+
error: r.error,
|
|
66
|
+
visible: r.visible,
|
|
67
|
+
cpuBytes: r.vertices.byteLength,
|
|
68
|
+
texture: r.textureState,
|
|
69
|
+
playing: !!number(r, "playing"),
|
|
70
|
+
paused: !!number(r, "paused")
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
VFX_STRIDE,
|
|
75
|
+
createRecord,
|
|
76
|
+
matrixChanged,
|
|
77
|
+
number,
|
|
78
|
+
point,
|
|
79
|
+
recordDescription,
|
|
80
|
+
refreshRecord
|
|
81
|
+
};
|
package/dist/vfx.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type World } from '@aethervtt/shard-core';
|
|
2
|
+
export declare const Trail: import("@aethervtt/shard-core").ComponentDef<{
|
|
3
|
+
readonly playing: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
|
|
4
|
+
readonly paused: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
|
|
5
|
+
readonly opacity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
6
|
+
readonly color: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
7
|
+
readonly texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
8
|
+
readonly blend: import("@aethervtt/shard-core").FieldType<"additive" | "alpha", "u8">;
|
|
9
|
+
readonly layers: import("@aethervtt/shard-core").FieldType<number, "u16">;
|
|
10
|
+
readonly width: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
11
|
+
readonly endColor: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
12
|
+
readonly widthCurve: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
13
|
+
at: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
14
|
+
value: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
15
|
+
}>[], "object">;
|
|
16
|
+
readonly repeat: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
17
|
+
readonly scroll: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
18
|
+
readonly offset: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
19
|
+
readonly lifetime: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
20
|
+
readonly capacity: import("@aethervtt/shard-core").FieldType<number, "u16">;
|
|
21
|
+
readonly minDistance: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
22
|
+
readonly breakDistance: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
23
|
+
}>;
|
|
24
|
+
export declare const Beam: import("@aethervtt/shard-core").ComponentDef<{
|
|
25
|
+
readonly playing: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
|
|
26
|
+
readonly paused: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
|
|
27
|
+
readonly opacity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
28
|
+
readonly color: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
29
|
+
readonly texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
30
|
+
readonly blend: import("@aethervtt/shard-core").FieldType<"additive" | "alpha", "u8">;
|
|
31
|
+
readonly layers: import("@aethervtt/shard-core").FieldType<number, "u16">;
|
|
32
|
+
readonly width: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
33
|
+
readonly endColor: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
34
|
+
readonly widthCurve: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").InferFields<{
|
|
35
|
+
at: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
36
|
+
value: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
37
|
+
}>[], "object">;
|
|
38
|
+
readonly repeat: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
39
|
+
readonly scroll: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
40
|
+
readonly start: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
41
|
+
readonly end: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
42
|
+
readonly target: import("@aethervtt/shard-core").FieldType<number | null, "f64">;
|
|
43
|
+
readonly points: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3[], "object">;
|
|
44
|
+
readonly segments: import("@aethervtt/shard-core").FieldType<number, "u16">;
|
|
45
|
+
readonly noise: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
46
|
+
readonly seed: import("@aethervtt/shard-core").FieldType<number, "u32">;
|
|
47
|
+
}>;
|
|
48
|
+
export declare const Decal: import("@aethervtt/shard-core").ComponentDef<{
|
|
49
|
+
readonly playing: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
|
|
50
|
+
readonly paused: import("@aethervtt/shard-core").FieldType<boolean, "u8">;
|
|
51
|
+
readonly opacity: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
52
|
+
readonly color: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Color, "f32">;
|
|
53
|
+
readonly texture: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").AssetRef<"Texture"> | null, "object">;
|
|
54
|
+
readonly blend: import("@aethervtt/shard-core").FieldType<"additive" | "alpha", "u8">;
|
|
55
|
+
readonly layers: import("@aethervtt/shard-core").FieldType<number, "u16">;
|
|
56
|
+
readonly size: import("@aethervtt/shard-core").FieldType<import("@aethervtt/shard-core").Vec3, "f32">;
|
|
57
|
+
readonly receiverMask: import("@aethervtt/shard-core").FieldType<number, "u16">;
|
|
58
|
+
readonly maxAngle: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
59
|
+
readonly offset: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
60
|
+
readonly lifetime: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
61
|
+
readonly fadeOut: import("@aethervtt/shard-core").FieldType<number, "f32">;
|
|
62
|
+
readonly maxVertices: import("@aethervtt/shard-core").FieldType<number, "u32">;
|
|
63
|
+
}>;
|
|
64
|
+
export declare const DecalReceiver: import("@aethervtt/shard-core").ComponentDef<{
|
|
65
|
+
readonly mask: import("@aethervtt/shard-core").FieldType<number, "u16">;
|
|
66
|
+
}>;
|
|
67
|
+
export declare const RibbonsPath: import("@aethervtt/shard-core").ResourceDef<boolean>;
|
|
68
|
+
export declare const DecalsPath: import("@aethervtt/shard-core").ResourceDef<boolean>;
|
|
69
|
+
export declare function observeVfxWithoutPlugin(world: World): void;
|
package/dist/vfx.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { defineComponent, defineResource, onAdd, t } from "@aethervtt/shard-core";
|
|
2
|
+
import { Transform } from "@aethervtt/shard-transform";
|
|
3
|
+
import { warnFeatureMissing } from "./features.js";
|
|
4
|
+
const presentation = {
|
|
5
|
+
playing: t.bool({ default: true }),
|
|
6
|
+
paused: t.bool(),
|
|
7
|
+
opacity: t.f32({ default: 1, min: 0, max: 1 }),
|
|
8
|
+
color: t.color({ default: [1, 1, 1, 1] }),
|
|
9
|
+
texture: t.handle("Texture"),
|
|
10
|
+
blend: t.enum(["alpha", "additive"]),
|
|
11
|
+
layers: t.u16({ default: 1 })
|
|
12
|
+
};
|
|
13
|
+
const ribbon = {
|
|
14
|
+
...presentation,
|
|
15
|
+
width: t.f32({ default: 0.1, min: 0, max: 1e3 }),
|
|
16
|
+
endColor: t.color({ default: [1, 1, 1, 0] }),
|
|
17
|
+
widthCurve: t.list(
|
|
18
|
+
t.struct({ at: t.f32({ min: 0, max: 1 }), value: t.f32({ min: 0, max: 16 }) })
|
|
19
|
+
),
|
|
20
|
+
repeat: t.f32({ default: 1, min: 1e-4, max: 1e4 }),
|
|
21
|
+
scroll: t.f32({ min: -1e4, max: 1e4 })
|
|
22
|
+
};
|
|
23
|
+
const Trail = defineComponent(
|
|
24
|
+
"render/Trail",
|
|
25
|
+
{
|
|
26
|
+
...ribbon,
|
|
27
|
+
offset: t.vec3(),
|
|
28
|
+
lifetime: t.f32({ default: 0.5, min: 1e-3, max: 3600 }),
|
|
29
|
+
capacity: t.u16({ default: 64, min: 2, max: 256 }),
|
|
30
|
+
minDistance: t.f32({ default: 0.01, min: 1e-5, max: 1e3 }),
|
|
31
|
+
breakDistance: t.f32({ default: 10, min: 1e-3, max: 1e6 })
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
requires: [Transform],
|
|
35
|
+
description: "World-space ribbon history of this entity/bone; stops emitting when playing is false and drains by lifetime (0082)."
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
const Beam = defineComponent(
|
|
39
|
+
"render/Beam",
|
|
40
|
+
{
|
|
41
|
+
...ribbon,
|
|
42
|
+
start: t.vec3(),
|
|
43
|
+
end: t.vec3({ default: [0, 0, -1] }),
|
|
44
|
+
target: t.entity(),
|
|
45
|
+
points: t.list(t.vec3()),
|
|
46
|
+
segments: t.u16({ default: 16, min: 1, max: 255 }),
|
|
47
|
+
noise: t.f32({ min: 0, max: 1e3 }),
|
|
48
|
+
seed: t.u32()
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
requires: [Transform],
|
|
52
|
+
description: "Camera-facing beam between transformed endpoints, or along local points; deterministic seeded displacement (0082)."
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
const Decal = defineComponent(
|
|
56
|
+
"render/Decal",
|
|
57
|
+
{
|
|
58
|
+
...presentation,
|
|
59
|
+
size: t.vec3({ default: [1, 1, 1] }),
|
|
60
|
+
receiverMask: t.u16({ default: 1 }),
|
|
61
|
+
maxAngle: t.f32({ default: 80, min: 0, max: 89.9 }),
|
|
62
|
+
offset: t.f32({ default: 2e-3, min: 0, max: 1 }),
|
|
63
|
+
lifetime: t.f32({ min: 0, max: 86400 }),
|
|
64
|
+
fadeOut: t.f32({ default: 1, min: 0, max: 86400 }),
|
|
65
|
+
maxVertices: t.u32({ default: 12288, min: 3, max: 65535 })
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
requires: [Transform],
|
|
69
|
+
description: "Box projection onto DecalReceiver rigid meshes: local XY is UV, projection along -Z. Zero lifetime persists (0083)."
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
const DecalReceiver = defineComponent(
|
|
73
|
+
"render/DecalReceiver",
|
|
74
|
+
{
|
|
75
|
+
mask: t.u16({ default: 1 })
|
|
76
|
+
},
|
|
77
|
+
{ description: "Eligible rigid Mesh3d surface for matching decal receiver masks (0083)." }
|
|
78
|
+
);
|
|
79
|
+
const RibbonsPath = defineResource("render/RibbonsPath", { init: () => true });
|
|
80
|
+
const DecalsPath = defineResource("render/DecalsPath", { init: () => true });
|
|
81
|
+
function observeVfxWithoutPlugin(world) {
|
|
82
|
+
world.observe(onAdd(Trail), ({ world: w }) => {
|
|
83
|
+
if (!w.hasResource(RibbonsPath))
|
|
84
|
+
warnFeatureMissing(w, "Trail", "ribbonsPlugin", "it draws no ribbon");
|
|
85
|
+
});
|
|
86
|
+
world.observe(onAdd(Beam), ({ world: w }) => {
|
|
87
|
+
if (!w.hasResource(RibbonsPath))
|
|
88
|
+
warnFeatureMissing(w, "Beam", "ribbonsPlugin", "it draws no ribbon");
|
|
89
|
+
});
|
|
90
|
+
world.observe(onAdd(Decal), ({ world: w }) => {
|
|
91
|
+
if (!w.hasResource(DecalsPath))
|
|
92
|
+
warnFeatureMissing(w, "Decal", "decalsPlugin", "it draws no decal");
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
export {
|
|
96
|
+
Beam,
|
|
97
|
+
Decal,
|
|
98
|
+
DecalReceiver,
|
|
99
|
+
DecalsPath,
|
|
100
|
+
RibbonsPath,
|
|
101
|
+
Trail,
|
|
102
|
+
observeVfxWithoutPlugin
|
|
103
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type Entity, type World } from '@aethervtt/shard-core';
|
|
2
|
+
import type { GpuBuffer } from '@aethervtt/shard-gpu';
|
|
3
|
+
import { type HiddenSet } from './view-visibility.js';
|
|
4
|
+
/**
|
|
5
|
+
* Every camera's hidden set, one range of words each. The GPU culler reads them from a region of
|
|
6
|
+
* its LOD state buffer; only words that changed upload there.
|
|
7
|
+
*/
|
|
8
|
+
export declare class HiddenSets {
|
|
9
|
+
/** Words per set: one bit per instance slot of the store's capacity. */
|
|
10
|
+
words: number;
|
|
11
|
+
data: Uint32Array<ArrayBuffer>;
|
|
12
|
+
readonly sets: Map<number, HiddenSet>;
|
|
13
|
+
/** Free set indices, reused before growing. */
|
|
14
|
+
private readonly free;
|
|
15
|
+
private count;
|
|
16
|
+
private dirty;
|
|
17
|
+
private dirtyLo;
|
|
18
|
+
private dirtyHi;
|
|
19
|
+
/** Every word uploads next time (a new layout, buffer, region or device). */
|
|
20
|
+
private all;
|
|
21
|
+
/** Where the last upload went: a buffer version (it bumps when replaced) and byte offset. */
|
|
22
|
+
private target;
|
|
23
|
+
private targetVersion;
|
|
24
|
+
private targetOffset;
|
|
25
|
+
/** Bytes uploaded by the last `upload`. */
|
|
26
|
+
uploadedBytes: number;
|
|
27
|
+
/** Resolves since start (tests: a still frame resolves nothing). */
|
|
28
|
+
resolves: number;
|
|
29
|
+
/** The set of `camera`, made on first use. */
|
|
30
|
+
setOf(camera: Entity): HiddenSet;
|
|
31
|
+
/** Frees a set: its words clear (and upload, so a reused index starts empty). */
|
|
32
|
+
drop(set: HiddenSet): void;
|
|
33
|
+
/** Makes room for `capacity` slots per set; a new width re-lays every set and uploads all. */
|
|
34
|
+
ensureSlots(capacity: number): void;
|
|
35
|
+
private layout;
|
|
36
|
+
private write;
|
|
37
|
+
/**
|
|
38
|
+
* Writes a set's new bits (from `next`, `words` long), marking only words that differ. Returns
|
|
39
|
+
* whether any did.
|
|
40
|
+
*/
|
|
41
|
+
assign(set: HiddenSet, next: Uint32Array): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Uploads changed words in runs to `buffer` at `byteOffset`; everything when that's not where
|
|
44
|
+
* the last upload went, or the buffer was replaced (grown, or a new device).
|
|
45
|
+
*/
|
|
46
|
+
upload(buffer: GpuBuffer, byteOffset: number): number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolves each camera's ViewVisibility into its hidden set, after the instance slots are current.
|
|
50
|
+
* Lists resolve again only when they change, a slot is allocated or freed, or a ChildOf changes.
|
|
51
|
+
*/
|
|
52
|
+
export declare const resolveViewVisibility: import("@aethervtt/shard-core").SystemDef<{
|
|
53
|
+
q: import("@aethervtt/shard-core").Query;
|
|
54
|
+
hierarchy: import("@aethervtt/shard-core").Query;
|
|
55
|
+
epoch: number;
|
|
56
|
+
frame: number;
|
|
57
|
+
}>;
|
|
58
|
+
/** render.describe's viewVisibility section: per camera, what it hides and the slots it resolves to. */
|
|
59
|
+
export declare function describeViewVisibility(world: World): Record<string, unknown>;
|
|
60
|
+
/** Per-view hiding (0070): cameras' ViewVisibility lists, resolved for their culls. */
|
|
61
|
+
export declare const viewVisibilityPlugin: import("@aethervtt/shard-runtime").Plugin;
|