@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,267 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChildOf,
|
|
3
|
+
Children,
|
|
4
|
+
defineSystem,
|
|
5
|
+
Last
|
|
6
|
+
} from "@aethervtt/shard-core";
|
|
7
|
+
import { definePlugin } from "@aethervtt/shard-runtime";
|
|
8
|
+
import { Camera3d } from "./camera.js";
|
|
9
|
+
import { addRenderFeatures } from "./features.js";
|
|
10
|
+
import { InstanceSlot, Instances, prepareInstances } from "./instances.js";
|
|
11
|
+
import { RenderDescribers, RenderSet } from "./plugin.js";
|
|
12
|
+
import { Cameras } from "./view.js";
|
|
13
|
+
import { HiddenSetsResource, ViewVisibility } from "./view-visibility.js";
|
|
14
|
+
class HiddenSets {
|
|
15
|
+
/** Words per set: one bit per instance slot of the store's capacity. */
|
|
16
|
+
words = 0;
|
|
17
|
+
data = new Uint32Array(0);
|
|
18
|
+
sets = /* @__PURE__ */ new Map();
|
|
19
|
+
/** Free set indices, reused before growing. */
|
|
20
|
+
free = [];
|
|
21
|
+
count = 0;
|
|
22
|
+
dirty = new Uint8Array(0);
|
|
23
|
+
dirtyLo = Number.POSITIVE_INFINITY;
|
|
24
|
+
dirtyHi = -1;
|
|
25
|
+
/** Every word uploads next time (a new layout, buffer, region or device). */
|
|
26
|
+
all = true;
|
|
27
|
+
/** Where the last upload went: a buffer version (it bumps when replaced) and byte offset. */
|
|
28
|
+
target;
|
|
29
|
+
targetVersion = -1;
|
|
30
|
+
targetOffset = -1;
|
|
31
|
+
/** Bytes uploaded by the last `upload`. */
|
|
32
|
+
uploadedBytes = 0;
|
|
33
|
+
/** Resolves since start (tests: a still frame resolves nothing). */
|
|
34
|
+
resolves = 0;
|
|
35
|
+
/** The set of `camera`, made on first use. */
|
|
36
|
+
setOf(camera) {
|
|
37
|
+
let set = this.sets.get(camera);
|
|
38
|
+
if (set) return set;
|
|
39
|
+
const index = this.free.pop() ?? this.count++;
|
|
40
|
+
if (index * this.words + this.words > this.data.length) this.layout(this.words, index + 1);
|
|
41
|
+
const base = index * this.words;
|
|
42
|
+
set = {
|
|
43
|
+
camera,
|
|
44
|
+
base,
|
|
45
|
+
bits: this.data.subarray(base, base + this.words),
|
|
46
|
+
slots: 0,
|
|
47
|
+
hide: [],
|
|
48
|
+
shadows: false,
|
|
49
|
+
frame: -1
|
|
50
|
+
};
|
|
51
|
+
this.sets.set(camera, set);
|
|
52
|
+
return set;
|
|
53
|
+
}
|
|
54
|
+
/** Frees a set: its words clear (and upload, so a reused index starts empty). */
|
|
55
|
+
drop(set) {
|
|
56
|
+
for (let w = 0; w < set.bits.length; w++) if (set.bits[w] !== 0) this.write(set.base + w, 0);
|
|
57
|
+
this.sets.delete(set.camera);
|
|
58
|
+
this.free.push(set.base / Math.max(1, this.words));
|
|
59
|
+
}
|
|
60
|
+
/** Makes room for `capacity` slots per set; a new width re-lays every set and uploads all. */
|
|
61
|
+
ensureSlots(capacity) {
|
|
62
|
+
const words = Math.ceil(capacity / 32);
|
|
63
|
+
if (words > this.words) this.layout(words, this.count);
|
|
64
|
+
}
|
|
65
|
+
layout(words, sets) {
|
|
66
|
+
const data = new Uint32Array(words * Math.max(1, sets * 2));
|
|
67
|
+
for (const set of this.sets.values()) {
|
|
68
|
+
const index = set.base / Math.max(1, this.words);
|
|
69
|
+
data.set(set.bits, index * words);
|
|
70
|
+
set.base = index * words;
|
|
71
|
+
}
|
|
72
|
+
this.data = data;
|
|
73
|
+
for (const set of this.sets.values()) set.bits = data.subarray(set.base, set.base + words);
|
|
74
|
+
this.words = words;
|
|
75
|
+
this.dirty = new Uint8Array(data.length);
|
|
76
|
+
this.all = true;
|
|
77
|
+
}
|
|
78
|
+
write(word, value) {
|
|
79
|
+
if (this.data[word] === value) return;
|
|
80
|
+
this.data[word] = value;
|
|
81
|
+
if (this.dirty[word]) return;
|
|
82
|
+
this.dirty[word] = 1;
|
|
83
|
+
if (word < this.dirtyLo) this.dirtyLo = word;
|
|
84
|
+
if (word > this.dirtyHi) this.dirtyHi = word;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Writes a set's new bits (from `next`, `words` long), marking only words that differ. Returns
|
|
88
|
+
* whether any did.
|
|
89
|
+
*/
|
|
90
|
+
assign(set, next2) {
|
|
91
|
+
let changed = false;
|
|
92
|
+
for (let w = 0; w < set.bits.length; w++) {
|
|
93
|
+
const value = next2[w];
|
|
94
|
+
if (set.bits[w] !== value) {
|
|
95
|
+
this.write(set.base + w, value);
|
|
96
|
+
changed = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return changed;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Uploads changed words in runs to `buffer` at `byteOffset`; everything when that's not where
|
|
103
|
+
* the last upload went, or the buffer was replaced (grown, or a new device).
|
|
104
|
+
*/
|
|
105
|
+
upload(buffer, byteOffset) {
|
|
106
|
+
buffer.ensureCapacity(byteOffset + this.data.byteLength);
|
|
107
|
+
void buffer.buffer;
|
|
108
|
+
if (buffer !== this.target || buffer.version !== this.targetVersion || byteOffset !== this.targetOffset) {
|
|
109
|
+
this.target = buffer;
|
|
110
|
+
this.targetVersion = buffer.version;
|
|
111
|
+
this.targetOffset = byteOffset;
|
|
112
|
+
this.all = true;
|
|
113
|
+
}
|
|
114
|
+
let bytes = 0;
|
|
115
|
+
if (this.all) {
|
|
116
|
+
this.all = false;
|
|
117
|
+
this.dirty.fill(0);
|
|
118
|
+
this.dirtyLo = Number.POSITIVE_INFINITY;
|
|
119
|
+
this.dirtyHi = -1;
|
|
120
|
+
if (this.data.length === 0) return 0;
|
|
121
|
+
buffer.write(this.data, byteOffset);
|
|
122
|
+
this.uploadedBytes = this.data.byteLength;
|
|
123
|
+
return this.uploadedBytes;
|
|
124
|
+
}
|
|
125
|
+
let start = -1;
|
|
126
|
+
for (let w = this.dirtyLo; w <= this.dirtyHi + 1; w++) {
|
|
127
|
+
if (w <= this.dirtyHi && this.dirty[w] === 1) {
|
|
128
|
+
this.dirty[w] = 0;
|
|
129
|
+
if (start < 0) start = w;
|
|
130
|
+
} else if (start >= 0) {
|
|
131
|
+
buffer.write(this.data, byteOffset + start * 4, start, w - start);
|
|
132
|
+
bytes += (w - start) * 4;
|
|
133
|
+
start = -1;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
this.dirtyLo = Number.POSITIVE_INFINITY;
|
|
137
|
+
this.dirtyHi = -1;
|
|
138
|
+
this.uploadedBytes = bytes;
|
|
139
|
+
return bytes;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
let next = new Uint32Array(0);
|
|
143
|
+
const stack = [];
|
|
144
|
+
function resolve(world, roots, out) {
|
|
145
|
+
out.fill(0);
|
|
146
|
+
let slots = 0;
|
|
147
|
+
stack.length = 0;
|
|
148
|
+
for (const e of roots) if (e !== null && e !== void 0) stack.push(e);
|
|
149
|
+
while (stack.length > 0) {
|
|
150
|
+
const e = stack.pop();
|
|
151
|
+
if (!world.isAlive(e)) continue;
|
|
152
|
+
const table = world.entityTable(e);
|
|
153
|
+
const row = world.entityRow(e);
|
|
154
|
+
if (table.has(InstanceSlot)) {
|
|
155
|
+
const slot = table.column(InstanceSlot, "slot")[row] - 1;
|
|
156
|
+
if (slot >= 0 && (out[slot >>> 5] & 1 << (slot & 31)) === 0) {
|
|
157
|
+
out[slot >>> 5] = out[slot >>> 5] | 1 << (slot & 31);
|
|
158
|
+
slots++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (table.has(Children)) {
|
|
162
|
+
const children = table.column(Children, "entities")[row];
|
|
163
|
+
if (children) {
|
|
164
|
+
for (const c of children) if (c !== null) stack.push(c);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return slots;
|
|
169
|
+
}
|
|
170
|
+
const resolveViewVisibility = defineSystem({
|
|
171
|
+
name: "render/resolve-view-visibility",
|
|
172
|
+
description: "Resolves cameras' ViewVisibility lists to hidden slot bitsets (0070).",
|
|
173
|
+
setup: (world) => ({
|
|
174
|
+
q: world.query({ with: [Camera3d, ViewVisibility] }),
|
|
175
|
+
hierarchy: world.query({ with: [ChildOf] }),
|
|
176
|
+
epoch: -1,
|
|
177
|
+
frame: 0
|
|
178
|
+
}),
|
|
179
|
+
run: (state, world, ctx) => {
|
|
180
|
+
const sets = world.tryResource(HiddenSetsResource);
|
|
181
|
+
if (!sets) return;
|
|
182
|
+
const cameras = world.resource(Cameras);
|
|
183
|
+
const store = world.resource(Instances);
|
|
184
|
+
const frame = ++state.frame;
|
|
185
|
+
let listed = 0;
|
|
186
|
+
for (const table of state.q.tables) listed += table.count;
|
|
187
|
+
if (listed === 0 && sets.sets.size === 0) return;
|
|
188
|
+
sets.ensureSlots(store.capacity);
|
|
189
|
+
if (next.length < sets.words) next = new Uint32Array(sets.words);
|
|
190
|
+
const since = ctx.lastRunTick;
|
|
191
|
+
let moved = store.slotEpoch !== state.epoch;
|
|
192
|
+
state.epoch = store.slotEpoch;
|
|
193
|
+
if (!moved) {
|
|
194
|
+
for (const table of state.hierarchy.tables) {
|
|
195
|
+
if (table.lastStructural > since || table.lastChanged(ChildOf) > since) {
|
|
196
|
+
moved = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
for (const table of state.q.tables) {
|
|
202
|
+
const hide = table.column(ViewVisibility, "hide");
|
|
203
|
+
const shadows = table.column(ViewVisibility, "shadows");
|
|
204
|
+
const changed = table.changedTicks(ViewVisibility);
|
|
205
|
+
for (let i = 0; i < table.count; i++) {
|
|
206
|
+
const camera = table.entities[i];
|
|
207
|
+
const fresh = !sets.sets.has(camera);
|
|
208
|
+
const set = sets.setOf(camera);
|
|
209
|
+
set.frame = frame;
|
|
210
|
+
const cam = cameras.get(camera);
|
|
211
|
+
if (cam) cam.hidden = set;
|
|
212
|
+
const hideShadows = shadows[i] === 1;
|
|
213
|
+
if (!fresh && !moved && changed[i] <= since && set.shadows === hideShadows) continue;
|
|
214
|
+
const list = hide[i] ?? [];
|
|
215
|
+
const words = next.subarray(0, sets.words);
|
|
216
|
+
set.slots = resolve(world, list, words);
|
|
217
|
+
set.hide = list.filter((e) => e !== null);
|
|
218
|
+
sets.resolves++;
|
|
219
|
+
if (sets.assign(set, words) && hideShadows || set.shadows !== hideShadows)
|
|
220
|
+
store.shadowEpoch++;
|
|
221
|
+
set.shadows = hideShadows;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (sets.sets.size > listed) {
|
|
225
|
+
for (const set of sets.sets.values()) {
|
|
226
|
+
if (set.frame === frame) continue;
|
|
227
|
+
const cam = cameras.get(set.camera);
|
|
228
|
+
if (cam?.hidden === set) cam.hidden = void 0;
|
|
229
|
+
if (set.shadows) store.shadowEpoch++;
|
|
230
|
+
sets.drop(set);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
function describeViewVisibility(world) {
|
|
236
|
+
const out = {};
|
|
237
|
+
for (const set of world.tryResource(HiddenSetsResource)?.sets.values() ?? [])
|
|
238
|
+
out[`camera:${set.camera}`] = {
|
|
239
|
+
hide: [...set.hide],
|
|
240
|
+
slots: set.slots,
|
|
241
|
+
shadows: set.shadows ? "hide" : "keep"
|
|
242
|
+
};
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
const viewVisibilityPlugin = definePlugin({
|
|
246
|
+
name: "render/view-visibility",
|
|
247
|
+
dependencies: ["render/forward"],
|
|
248
|
+
build(app) {
|
|
249
|
+
app.insertResource(HiddenSetsResource, new HiddenSets());
|
|
250
|
+
app.addSystems(Last, resolveViewVisibility.inSet(RenderSet.Prepare).after(prepareInstances));
|
|
251
|
+
},
|
|
252
|
+
ready(app) {
|
|
253
|
+
app.world.initResource(RenderDescribers).set("viewVisibility", describeViewVisibility);
|
|
254
|
+
addRenderFeatures(app.world, {
|
|
255
|
+
name: "render/view-visibility",
|
|
256
|
+
description: "Cameras' hidden lists (ViewVisibility).",
|
|
257
|
+
nodes: [],
|
|
258
|
+
baseline: { strategy: "CPU culls read the same bits" }
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
export {
|
|
263
|
+
HiddenSets,
|
|
264
|
+
describeViewVisibility,
|
|
265
|
+
resolveViewVisibility,
|
|
266
|
+
viewVisibilityPlugin
|
|
267
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Entity, type World } from '@aethervtt/shard-core';
|
|
2
|
+
import type { HiddenSets } from './view-visibility-plugin.js';
|
|
3
|
+
export declare const VIEW_SHADOWS: readonly ['keep', 'hide'];
|
|
4
|
+
export declare const ViewVisibility: import("@aethervtt/shard-core").ComponentDef<{
|
|
5
|
+
readonly hide: import("@aethervtt/shard-core").FieldType<(number | null)[], "object">;
|
|
6
|
+
readonly shadows: import("@aethervtt/shard-core").FieldType<"hide" | "keep", "u8">;
|
|
7
|
+
}>;
|
|
8
|
+
/** `hiddenBase` of a view with no hidden set. */
|
|
9
|
+
export declare const NO_HIDDEN = 4294967295;
|
|
10
|
+
/** One camera's hidden set: a bit per instance slot, in its range of `HiddenSets.data`. */
|
|
11
|
+
export interface HiddenSet {
|
|
12
|
+
camera: Entity;
|
|
13
|
+
/** Its first word in `HiddenSets.data` (and the GPU buffer). */
|
|
14
|
+
base: number;
|
|
15
|
+
/** Its words: bit `s & 31` of word `s >> 5` is slot `s`. */
|
|
16
|
+
bits: Uint32Array;
|
|
17
|
+
/** Slots it resolves to. */
|
|
18
|
+
slots: number;
|
|
19
|
+
/** The entities listed, as last resolved. */
|
|
20
|
+
hide: Entity[];
|
|
21
|
+
/** Its camera's cascades leave the slots out too. */
|
|
22
|
+
shadows: boolean;
|
|
23
|
+
/** The frame it was last seen on (sets of cameras that lose the component are freed). */
|
|
24
|
+
frame: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const HiddenSetsResource: import("@aethervtt/shard-core").ResourceDef<HiddenSets>;
|
|
27
|
+
/** Warns once per world about a ViewVisibility without its plugin (render/feature-missing). */
|
|
28
|
+
export declare function observeViewVisibilityWithoutPlugin(world: World): void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineComponent,
|
|
3
|
+
defineResource,
|
|
4
|
+
onAdd,
|
|
5
|
+
t
|
|
6
|
+
} from "@aethervtt/shard-core";
|
|
7
|
+
import { Camera3d } from "./camera.js";
|
|
8
|
+
import { warnFeatureMissing } from "./features.js";
|
|
9
|
+
const VIEW_SHADOWS = ["keep", "hide"];
|
|
10
|
+
const ViewVisibility = defineComponent(
|
|
11
|
+
"render/ViewVisibility",
|
|
12
|
+
{
|
|
13
|
+
hide: t.list(t.entity, {
|
|
14
|
+
description: "Entities this camera doesn't draw, with their descendants."
|
|
15
|
+
}),
|
|
16
|
+
shadows: t.enum(VIEW_SHADOWS, {
|
|
17
|
+
description: "keep: they still cast into this camera's shadows (a body its own camera doesn't draw). hide: its cascades leave them out too. Spot and point shadow maps are shared by every camera and always keep them."
|
|
18
|
+
})
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
description: "Hides entities from one camera (0070): other cameras still draw them. For a first-person body, split-screen markers, or a game master previewing a player view.",
|
|
22
|
+
requires: [Camera3d]
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
const NO_HIDDEN = 4294967295;
|
|
26
|
+
const HiddenSetsResource = defineResource("render/HiddenSets", {
|
|
27
|
+
description: "Each camera's hidden slots (ViewVisibility, 0070), as the culls read them."
|
|
28
|
+
});
|
|
29
|
+
function observeViewVisibilityWithoutPlugin(world) {
|
|
30
|
+
world.observe(onAdd(ViewVisibility), ({ world: w }) => {
|
|
31
|
+
if (!w.hasResource(HiddenSetsResource))
|
|
32
|
+
warnFeatureMissing(
|
|
33
|
+
w,
|
|
34
|
+
"A camera has ViewVisibility",
|
|
35
|
+
"viewVisibilityPlugin",
|
|
36
|
+
"it hides nothing"
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
HiddenSetsResource,
|
|
42
|
+
NO_HIDDEN,
|
|
43
|
+
VIEW_SHADOWS,
|
|
44
|
+
ViewVisibility,
|
|
45
|
+
observeViewVisibilityWithoutPlugin
|
|
46
|
+
};
|
package/dist/view.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { RenderView } from './graph.js';
|
|
|
3
3
|
import { type DrawList } from './instances.js';
|
|
4
4
|
import { type PixelPerfectLayout } from './pixel-perfect.js';
|
|
5
5
|
import { type PostSettings } from './post.js';
|
|
6
|
+
import type { HiddenSet } from './view-visibility.js';
|
|
6
7
|
export declare const TONEMAP_CURVES: readonly ['aces', 'agx', 'pbr-neutral', 'reinhard', 'none'];
|
|
7
8
|
export type TonemapCurve = (typeof TONEMAP_CURVES)[number];
|
|
8
9
|
/** The curve for cameras without a Tonemapping component. */
|
|
@@ -56,9 +57,17 @@ export interface CameraData {
|
|
|
56
57
|
/** Scene luminance → pre-exposed HDR. */
|
|
57
58
|
exposure: number;
|
|
58
59
|
ev100: number;
|
|
59
|
-
/** Linear clear color, as displayed before the tonemap curve. */
|
|
60
|
+
/** Linear clear color, as displayed before the tonemap curve, premultiplied by its alpha. */
|
|
60
61
|
clear: GPUColor;
|
|
62
|
+
/**
|
|
63
|
+
* Clears to an alpha below 1 (0052): the passes carry alpha to the target, the sky and the
|
|
64
|
+
* environment background aren't drawn, and light raises alpha, so the view composites over
|
|
65
|
+
* whatever is under its surface.
|
|
66
|
+
*/
|
|
67
|
+
alphaOutput: boolean;
|
|
61
68
|
msaa: number;
|
|
69
|
+
/** MSAA was asked for, but the device can't multisample HDR targets (0064): `msaa` is 1. */
|
|
70
|
+
msaaCapped: boolean;
|
|
62
71
|
curve: number;
|
|
63
72
|
dither: boolean;
|
|
64
73
|
/** Culled draws of this camera, rebuilt each frame. */
|
|
@@ -67,6 +76,14 @@ export interface CameraData {
|
|
|
67
76
|
transparent: DrawList;
|
|
68
77
|
/** Deferred views: opaque draws the G-buffer can't take (custom lighting), drawn forward. */
|
|
69
78
|
forwardOnly: DrawList;
|
|
79
|
+
/** Ground draws (0057): coplanar bands in (band, order) order, after opaque geometry. */
|
|
80
|
+
ground: DrawList;
|
|
81
|
+
/** Ground draws from the overlay band up: after transparent objects and projected fog (0058). */
|
|
82
|
+
overlay: DrawList;
|
|
83
|
+
/** Render layers this camera draws (Camera3d.layers). */
|
|
84
|
+
layers: number;
|
|
85
|
+
/** Slots it doesn't draw (ViewVisibility, 0070), while it has the component. */
|
|
86
|
+
hidden: HiddenSet | undefined;
|
|
70
87
|
/** Renders through the G-buffer (RenderPath deferred). */
|
|
71
88
|
deferred: boolean;
|
|
72
89
|
/** G-buffer channel to show for a capture (-1: none), also forcing a G-buffer in forward views. */
|
|
@@ -85,7 +102,18 @@ export interface CameraData {
|
|
|
85
102
|
post: PostSettings;
|
|
86
103
|
/** Set for PixelPerfect cameras: the low-resolution target and how it scales up. */
|
|
87
104
|
pixelPerfect: PixelPerfectLayout | undefined;
|
|
105
|
+
/** The display size and pixel ratio the last CameraMoved was sent for. */
|
|
106
|
+
movedWidth: number;
|
|
107
|
+
movedHeight: number;
|
|
108
|
+
movedRatio: number;
|
|
88
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Sent after extraction, once per camera whose projection moved this frame (its unjittered view-
|
|
112
|
+
* projection, display size or pixel ratio changed), and on its first frame (0057).
|
|
113
|
+
*/
|
|
114
|
+
export declare const CameraMoved: import("@aethervtt/shard-core").EventDef<{
|
|
115
|
+
camera: Entity;
|
|
116
|
+
}>;
|
|
89
117
|
export declare const Cameras: import("@aethervtt/shard-core").ResourceDef<Map<number, CameraData>>;
|
|
90
118
|
/** Global render settings the camera extraction applies to every view. */
|
|
91
119
|
export interface ViewSettingsValue {
|
package/dist/view.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
affine,
|
|
3
3
|
defineComponent,
|
|
4
|
+
defineEvent,
|
|
4
5
|
defineResource,
|
|
5
6
|
defineSystem,
|
|
6
7
|
frustum,
|
|
@@ -12,13 +13,15 @@ import { LogResource, Time } from "@aethervtt/shard-runtime";
|
|
|
12
13
|
import { GlobalTransform } from "@aethervtt/shard-transform";
|
|
13
14
|
import { RenderTargets } from "./assets.js";
|
|
14
15
|
import { Camera3d, Exposure, exposureScale } from "./camera.js";
|
|
16
|
+
import { warnFeatureMissing } from "./features.js";
|
|
15
17
|
import { createDrawList } from "./instances.js";
|
|
18
|
+
import { Lens, LensPath } from "./lens.js";
|
|
16
19
|
import {
|
|
17
20
|
PixelPerfect,
|
|
18
21
|
PixelPerfectPath,
|
|
19
22
|
pixelPerfectLayout
|
|
20
23
|
} from "./pixel-perfect.js";
|
|
21
|
-
import { Views, Window } from "./plugin.js";
|
|
24
|
+
import { Gpu, Views, Window } from "./plugin.js";
|
|
22
25
|
import {
|
|
23
26
|
antialiasingOf,
|
|
24
27
|
CORE_EFFECTS,
|
|
@@ -58,13 +61,21 @@ const RenderPath = defineComponent(
|
|
|
58
61
|
const DeferredPath = defineResource("render/DeferredPath", {
|
|
59
62
|
description: "Present when the deferred path (deferredPlugin) is installed."
|
|
60
63
|
});
|
|
64
|
+
const CameraMoved = defineEvent("render/CameraMoved", {
|
|
65
|
+
description: "A camera moved or its view resized: DOM overlays following the scene reposition."
|
|
66
|
+
});
|
|
67
|
+
function sameMatrix(a, b) {
|
|
68
|
+
for (let i = 0; i < 16; i++) if (a[i] !== b[i]) return false;
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
61
71
|
const Cameras = defineResource("render/Cameras", {
|
|
62
72
|
description: "Per-camera render data by entity, extracted each frame.",
|
|
63
73
|
init: () => /* @__PURE__ */ new Map()
|
|
64
74
|
});
|
|
65
75
|
const ViewSettings = defineResource("render/ViewSettings", {
|
|
66
76
|
description: "Render settings shared by all views: the default MSAA sample count, and the display density past which it is skipped.",
|
|
67
|
-
init: () => ({ msaa: 4, msaaMaxPixelRatio: 1.5 })
|
|
77
|
+
init: () => ({ msaa: 4, msaaMaxPixelRatio: 1.5 }),
|
|
78
|
+
hostWritable: true
|
|
68
79
|
});
|
|
69
80
|
const scratchProj = mat4.create();
|
|
70
81
|
const scratchSnap = affine.create();
|
|
@@ -90,32 +101,6 @@ function viewAliases(cam) {
|
|
|
90
101
|
return cam.msaa > 1 ? MSAA_ALIASES : NO_MSAA_ALIASES;
|
|
91
102
|
}
|
|
92
103
|
const reportedEffects = /* @__PURE__ */ new WeakMap();
|
|
93
|
-
const reportedDeferred = /* @__PURE__ */ new WeakSet();
|
|
94
|
-
function warnMissingDeferred(world) {
|
|
95
|
-
if (reportedDeferred.has(world)) return;
|
|
96
|
-
reportedDeferred.add(world);
|
|
97
|
-
world.tryResource(LogResource)?.log(
|
|
98
|
-
"warn",
|
|
99
|
-
"A camera asks for RenderPath deferred, but deferredPlugin isn't installed; it renders forward",
|
|
100
|
-
{
|
|
101
|
-
code: "render/feature-missing",
|
|
102
|
-
hint: "Add deferredPlugin from '@aethervtt/shard-render' (forwardPlugin includes it)."
|
|
103
|
-
}
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
const reportedPixel = /* @__PURE__ */ new WeakSet();
|
|
107
|
-
function warnMissingPixelPerfect(world) {
|
|
108
|
-
if (reportedPixel.has(world)) return;
|
|
109
|
-
reportedPixel.add(world);
|
|
110
|
-
world.tryResource(LogResource)?.log(
|
|
111
|
-
"warn",
|
|
112
|
-
"A camera has PixelPerfect, but pixelPerfectPlugin isn't installed; it renders at full resolution",
|
|
113
|
-
{
|
|
114
|
-
code: "render/feature-missing",
|
|
115
|
-
hint: "Add pixelPerfectPlugin from '@aethervtt/shard-render' (forwardPlugin includes it)."
|
|
116
|
-
}
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
104
|
function warnMissingEffects(world, missing) {
|
|
120
105
|
const reported = reportedEffects.get(world) ?? 0;
|
|
121
106
|
const fresh = missing & ~reported;
|
|
@@ -148,6 +133,8 @@ const extractCameras = defineSystem({
|
|
|
148
133
|
const installed = world.tryResource(PostFeatures)?.effects ?? CORE_EFFECTS;
|
|
149
134
|
const deferredInstalled = world.hasResource(DeferredPath);
|
|
150
135
|
const pixelInstalled = world.hasResource(PixelPerfectPath);
|
|
136
|
+
const lensInstalled = world.hasResource(LensPath);
|
|
137
|
+
const maxSamples = world.resource(Gpu).capabilities.hdrSampleCount;
|
|
151
138
|
for (const table of q.tables) {
|
|
152
139
|
const projection = table.column(Camera3d, "projection");
|
|
153
140
|
const fovY = table.column(Camera3d, "fovY");
|
|
@@ -158,13 +145,22 @@ const extractCameras = defineSystem({
|
|
|
158
145
|
const clear = table.column(Camera3d, "clearColor");
|
|
159
146
|
const target = table.column(Camera3d, "target");
|
|
160
147
|
const active = table.column(Camera3d, "active");
|
|
148
|
+
const layers = table.column(Camera3d, "layers");
|
|
161
149
|
const g = table.column(GlobalTransform, "matrix");
|
|
162
150
|
const ev = table.column(Exposure, "ev100");
|
|
163
151
|
const path = table.has(RenderPath) ? table.column(RenderPath, "mode") : void 0;
|
|
164
152
|
const hasTonemap = table.has(Tonemapping);
|
|
165
153
|
const curve = hasTonemap ? table.column(Tonemapping, "curve") : void 0;
|
|
166
154
|
const dither = hasTonemap ? table.column(Tonemapping, "dither") : void 0;
|
|
167
|
-
if (table.has(PixelPerfect) && !pixelInstalled)
|
|
155
|
+
if (table.has(PixelPerfect) && !pixelInstalled)
|
|
156
|
+
warnFeatureMissing(
|
|
157
|
+
world,
|
|
158
|
+
"A camera has PixelPerfect",
|
|
159
|
+
"pixelPerfectPlugin",
|
|
160
|
+
"it renders at full resolution"
|
|
161
|
+
);
|
|
162
|
+
if (table.has(Lens) && !lensInstalled)
|
|
163
|
+
warnFeatureMissing(world, "A camera has Lens", "lensPlugin", "it renders unbent");
|
|
168
164
|
const pixel = pixelInstalled && table.has(PixelPerfect);
|
|
169
165
|
const ppu = pixel ? table.column(PixelPerfect, "pixelsPerUnit") : void 0;
|
|
170
166
|
const snap = pixel ? table.column(PixelPerfect, "snap") : void 0;
|
|
@@ -205,12 +201,18 @@ const extractCameras = defineSystem({
|
|
|
205
201
|
exposure: 1,
|
|
206
202
|
ev100: 0,
|
|
207
203
|
clear: { r: 0, g: 0, b: 0, a: 1 },
|
|
204
|
+
alphaOutput: false,
|
|
208
205
|
msaa: 1,
|
|
206
|
+
msaaCapped: false,
|
|
209
207
|
curve: 1,
|
|
210
208
|
dither: true,
|
|
211
209
|
draws: createDrawList(),
|
|
212
210
|
transparent: createDrawList(),
|
|
213
211
|
forwardOnly: createDrawList(),
|
|
212
|
+
ground: createDrawList(),
|
|
213
|
+
overlay: createDrawList(),
|
|
214
|
+
layers: 4294967295,
|
|
215
|
+
hidden: void 0,
|
|
214
216
|
deferred: false,
|
|
215
217
|
gbufferDebug: -1,
|
|
216
218
|
lodState: new Uint8Array(0),
|
|
@@ -220,11 +222,15 @@ const extractCameras = defineSystem({
|
|
|
220
222
|
frames: 0,
|
|
221
223
|
debug: 0,
|
|
222
224
|
post: createPostSettings(),
|
|
223
|
-
pixelPerfect: void 0
|
|
225
|
+
pixelPerfect: void 0,
|
|
226
|
+
movedWidth: 0,
|
|
227
|
+
movedHeight: 0,
|
|
228
|
+
movedRatio: 0
|
|
224
229
|
};
|
|
225
230
|
cameras.set(entity, cam);
|
|
226
231
|
}
|
|
227
232
|
cam.orthographic = projection[i] !== 0;
|
|
233
|
+
cam.layers = layers[i];
|
|
228
234
|
let height = orthoHeight[i];
|
|
229
235
|
cam.pixelPerfect = void 0;
|
|
230
236
|
if (ppu && cam.orthographic) {
|
|
@@ -258,7 +264,13 @@ const extractCameras = defineSystem({
|
|
|
258
264
|
}
|
|
259
265
|
const wantsDeferred = path ? path[i] === 1 : false;
|
|
260
266
|
cam.deferred = wantsDeferred && deferredInstalled;
|
|
261
|
-
if (wantsDeferred && !deferredInstalled)
|
|
267
|
+
if (wantsDeferred && !deferredInstalled)
|
|
268
|
+
warnFeatureMissing(
|
|
269
|
+
world,
|
|
270
|
+
"A camera asks for RenderPath deferred",
|
|
271
|
+
"deferredPlugin",
|
|
272
|
+
"it renders forward"
|
|
273
|
+
);
|
|
262
274
|
const missing = extractPost(table, i, cam, delta, true, installed);
|
|
263
275
|
if (missing !== 0) warnMissingEffects(world, missing);
|
|
264
276
|
const world_ = g.subarray(i * 12, i * 12 + 12);
|
|
@@ -275,6 +287,12 @@ const extractCameras = defineSystem({
|
|
|
275
287
|
if (cam.frames > 0) mat4.copy(cam.prevViewProj, cam.viewProjNoJitter);
|
|
276
288
|
mat4.multiply(cam.viewProjNoJitter, scratchProj, cam.view);
|
|
277
289
|
if (cam.frames === 0) mat4.copy(cam.prevViewProj, cam.viewProjNoJitter);
|
|
290
|
+
if (cam.frames === 0 || !sameMatrix(cam.viewProjNoJitter, cam.prevViewProj) || cam.displayWidth !== cam.movedWidth || cam.displayHeight !== cam.movedHeight || cam.pixelRatio !== cam.movedRatio) {
|
|
291
|
+
cam.movedWidth = cam.displayWidth;
|
|
292
|
+
cam.movedHeight = cam.displayHeight;
|
|
293
|
+
cam.movedRatio = cam.pixelRatio;
|
|
294
|
+
world.send(CameraMoved, { camera: entity });
|
|
295
|
+
}
|
|
278
296
|
const jitter = cam.post.jitter;
|
|
279
297
|
if (cam.post.effects & PostEffect.Taa) {
|
|
280
298
|
const k = cam.frames % 8 + 1;
|
|
@@ -309,10 +327,14 @@ const extractCameras = defineSystem({
|
|
|
309
327
|
cam.ev100 = ev[i];
|
|
310
328
|
cam.exposure = exposureScale(ev[i]);
|
|
311
329
|
const c = i * 4;
|
|
312
|
-
|
|
330
|
+
const ca = Math.min(1, Math.max(0, clear[c + 3]));
|
|
331
|
+
cam.clear = { r: clear[c] * ca, g: clear[c + 1] * ca, b: clear[c + 2] * ca, a: ca };
|
|
332
|
+
cam.alphaOutput = ca < 1;
|
|
313
333
|
const aa = antialiasingOf(table, i);
|
|
314
334
|
const dense = settings.msaaMaxPixelRatio > 0 && cam.pixelRatio >= settings.msaaMaxPixelRatio;
|
|
315
|
-
|
|
335
|
+
const msaa = cam.deferred ? 1 : aa < 0 ? dense ? 1 : settings.msaa : aa === 3 ? 4 : 1;
|
|
336
|
+
cam.msaa = Math.min(msaa, maxSamples);
|
|
337
|
+
cam.msaaCapped = cam.msaa < msaa;
|
|
316
338
|
cam.curve = curve ? curve[i] : TONEMAP_CURVES.indexOf(DEFAULT_CURVE);
|
|
317
339
|
cam.dither = dither ? dither[i] !== 0 : true;
|
|
318
340
|
cam.frames++;
|
|
@@ -333,7 +355,7 @@ const extractCameras = defineSystem({
|
|
|
333
355
|
name: `pixel-perfect:${entity}`,
|
|
334
356
|
target: shown,
|
|
335
357
|
order: order[i] + 0.5,
|
|
336
|
-
data: { upscale: cam.pixelPerfect }
|
|
358
|
+
data: { upscale: cam.pixelPerfect, alphaOutput: cam.alphaOutput }
|
|
337
359
|
});
|
|
338
360
|
}
|
|
339
361
|
}
|
|
@@ -374,6 +396,7 @@ function cameraOf(view) {
|
|
|
374
396
|
return view.data.camera;
|
|
375
397
|
}
|
|
376
398
|
export {
|
|
399
|
+
CameraMoved,
|
|
377
400
|
Cameras,
|
|
378
401
|
DEFAULT_CURVE,
|
|
379
402
|
DeferredPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aethervtt/shard-render",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
11
|
},
|
|
12
|
+
"./crowd": {
|
|
13
|
+
"types": "./dist/crowd.d.ts",
|
|
14
|
+
"default": "./dist/crowd.js"
|
|
15
|
+
},
|
|
16
|
+
"./post-scene": {
|
|
17
|
+
"types": "./dist/post-scene.d.ts",
|
|
18
|
+
"default": "./dist/post-scene.js"
|
|
19
|
+
},
|
|
12
20
|
"./testing": {
|
|
13
21
|
"types": "./dist/testing.d.ts",
|
|
14
22
|
"default": "./dist/testing.js"
|
|
@@ -16,6 +24,10 @@
|
|
|
16
24
|
"./noise": {
|
|
17
25
|
"types": "./dist/noise.d.ts",
|
|
18
26
|
"default": "./dist/noise.js"
|
|
27
|
+
},
|
|
28
|
+
"./surface": {
|
|
29
|
+
"types": "./dist/surface.d.ts",
|
|
30
|
+
"default": "./dist/surface.js"
|
|
19
31
|
}
|
|
20
32
|
},
|
|
21
33
|
"files": [
|
|
@@ -24,15 +36,15 @@
|
|
|
24
36
|
"README.md"
|
|
25
37
|
],
|
|
26
38
|
"dependencies": {
|
|
27
|
-
"@aethervtt/shard-assets": "0.0
|
|
28
|
-
"@aethervtt/shard-core": "0.0
|
|
29
|
-
"@aethervtt/shard-gpu": "0.0
|
|
30
|
-
"@aethervtt/shard-mesh": "0.0
|
|
31
|
-
"@aethervtt/shard-noise": "0.0
|
|
32
|
-
"@aethervtt/shard-runtime": "0.0
|
|
33
|
-
"@aethervtt/shard-shader": "0.0
|
|
34
|
-
"@aethervtt/shard-texture": "0.0
|
|
35
|
-
"@aethervtt/shard-transform": "0.0
|
|
39
|
+
"@aethervtt/shard-assets": "0.2.0",
|
|
40
|
+
"@aethervtt/shard-core": "0.2.0",
|
|
41
|
+
"@aethervtt/shard-gpu": "0.2.0",
|
|
42
|
+
"@aethervtt/shard-mesh": "0.2.0",
|
|
43
|
+
"@aethervtt/shard-noise": "0.2.0",
|
|
44
|
+
"@aethervtt/shard-runtime": "0.2.0",
|
|
45
|
+
"@aethervtt/shard-shader": "0.2.0",
|
|
46
|
+
"@aethervtt/shard-texture": "0.2.0",
|
|
47
|
+
"@aethervtt/shard-transform": "0.2.0"
|
|
36
48
|
},
|
|
37
49
|
"repository": {
|
|
38
50
|
"type": "git",
|
|
@@ -44,6 +56,6 @@
|
|
|
44
56
|
"provenance": true
|
|
45
57
|
},
|
|
46
58
|
"shard": {
|
|
47
|
-
"buildSha": "
|
|
59
|
+
"buildSha": "32c22a3944ed23f5d1baa90bf7cc9fae724f5fd8"
|
|
48
60
|
}
|
|
49
61
|
}
|