@aethervtt/shard-render 0.0.1 → 0.1.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/assets.d.ts +3 -0
- package/dist/assets.js +43 -2
- package/dist/atmosphere-nodes.d.ts +9 -0
- package/dist/atmosphere-nodes.js +57 -18
- package/dist/atmosphere-plugin.js +8 -2
- 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 +120 -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/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/deferred-shaders.js +2 -0
- package/dist/deferred.js +24 -3
- 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 +15 -4
- 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/forward.d.ts +38 -2
- package/dist/forward.js +582 -184
- 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 +11 -0
- package/dist/graph.js +30 -5
- package/dist/health.d.ts +58 -0
- package/dist/health.js +112 -0
- package/dist/index.d.ts +30 -11
- package/dist/index.js +286 -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/overlays.js +1 -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 +58 -6
- package/dist/plugin.js +191 -24
- package/dist/post-common.d.ts +2 -0
- package/dist/post-common.js +3 -0
- package/dist/post-nodes.d.ts +2 -1
- package/dist/post-nodes.js +111 -19
- package/dist/post-plugin.js +5 -3
- package/dist/post-shaders.js +51 -6
- 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/screen-effects.d.ts +97 -0
- package/dist/screen-effects.js +192 -0
- package/dist/shaders.d.ts +13 -0
- package/dist/shaders.js +131 -29
- package/dist/shadow-catcher.d.ts +9 -0
- package/dist/shadow-catcher.js +133 -0
- package/dist/shadows.d.ts +25 -1
- package/dist/shadows.js +97 -19
- package/dist/standard-fields.d.ts +1 -0
- package/dist/standard-fields.js +5 -0
- package/dist/standard.js +12 -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 +10 -0
- package/dist/testing.js +33 -1
- package/dist/tier.d.ts +13 -0
- package/dist/tier.js +14 -0
- package/dist/timer.d.ts +2 -0
- package/dist/timer.js +7 -1
- package/dist/variants.d.ts +37 -0
- package/dist/variants.js +104 -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 +15 -11
package/dist/instances.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AssetServerResource } from "@aethervtt/shard-assets";
|
|
1
2
|
import {
|
|
2
3
|
defineComponent,
|
|
3
4
|
defineResource,
|
|
@@ -5,13 +6,16 @@ import {
|
|
|
5
6
|
onRemove,
|
|
6
7
|
t
|
|
7
8
|
} from "@aethervtt/shard-core";
|
|
8
|
-
import {
|
|
9
|
-
import { LogResource } from "@aethervtt/shard-runtime";
|
|
9
|
+
import { DevMode } from "@aethervtt/shard-runtime";
|
|
10
10
|
import { toHalf } from "@aethervtt/shard-texture";
|
|
11
11
|
import { GlobalTransform, Transform } from "@aethervtt/shard-transform";
|
|
12
12
|
import { MaterialAsset, Materials, Meshes } from "./assets.js";
|
|
13
|
+
import { Cutaway, CutawayPath } from "./cutaway.js";
|
|
14
|
+
import { DataStore } from "./data-store.js";
|
|
13
15
|
import { DEFORM_WORDS, DeformStore } from "./deform.js";
|
|
16
|
+
import { warnFeatureMissing } from "./features.js";
|
|
14
17
|
import { GpuAssetsResource } from "./gpu-assets.js";
|
|
18
|
+
import { GroundLayer, OVERLAY_BAND, RenderLayers } from "./layers.js";
|
|
15
19
|
import { isTransparent } from "./materials.js";
|
|
16
20
|
import { ComputedVisibility, Visibility } from "./visibility.js";
|
|
17
21
|
const InstanceSlot = defineComponent(
|
|
@@ -32,6 +36,11 @@ const NotShadowCaster = defineComponent(
|
|
|
32
36
|
{},
|
|
33
37
|
{ description: "Tag: this mesh casts no shadows." }
|
|
34
38
|
);
|
|
39
|
+
const ShadowWhenHidden = defineComponent(
|
|
40
|
+
"render/ShadowWhenHidden",
|
|
41
|
+
{},
|
|
42
|
+
{ description: "Tag: while hidden, this mesh still casts shadows (camera views skip it)." }
|
|
43
|
+
);
|
|
35
44
|
const NotShadowReceiver = defineComponent(
|
|
36
45
|
"render/NotShadowReceiver",
|
|
37
46
|
{},
|
|
@@ -153,8 +162,21 @@ const InstanceFlags = {
|
|
|
153
162
|
/** Skinned: the vertex stage reads joint matrices; culling reads the slot's own sphere. */
|
|
154
163
|
Skinned: 32,
|
|
155
164
|
/** Morphed: the vertex stage adds morph target deltas. */
|
|
156
|
-
Morph: 64
|
|
165
|
+
Morph: 64,
|
|
166
|
+
/** Hidden, but drawn in shadow views (ShadowWhenHidden); Visible is off. */
|
|
167
|
+
ShadowOnly: 128,
|
|
168
|
+
/**
|
|
169
|
+
* May be cut away around a camera's reveal points (Cutaway, 0070). In the record's flags word
|
|
170
|
+
* it rides in bit 28 (`CUTAWAY_BIT`), above the layers and the LOD.
|
|
171
|
+
*/
|
|
172
|
+
Cutaway: 256
|
|
157
173
|
};
|
|
174
|
+
const CUTAWAY_BIT = 1 << 28;
|
|
175
|
+
function recordFlags(flags, layers) {
|
|
176
|
+
return (flags & 255 | layers << LAYER_SHIFT | (flags & InstanceFlags.Cutaway ? CUTAWAY_BIT : 0)) >>> 0;
|
|
177
|
+
}
|
|
178
|
+
const LAYER_SHIFT = 8;
|
|
179
|
+
const LAYER_BITS = 65535 << LAYER_SHIFT;
|
|
158
180
|
const NO_BATCH = 4294967295;
|
|
159
181
|
const LOD_BIT = 2147483648;
|
|
160
182
|
const SLOT_MASK = 268435455;
|
|
@@ -237,7 +259,7 @@ class InstanceStore {
|
|
|
237
259
|
* Each slot's InstanceFlags, mirrored from its record: prepare compares against these every
|
|
238
260
|
* frame, and reading them from the 64-byte records would touch a cache line per instance.
|
|
239
261
|
*/
|
|
240
|
-
flags = new
|
|
262
|
+
flags = new Uint16Array(0);
|
|
241
263
|
/** Hidden instances per ECS table id, from the last time prepare visited the table. */
|
|
242
264
|
tableHidden = [];
|
|
243
265
|
/** Batch index (>= 0), LOD set (-2 - index), or -1 (none), per slot. */
|
|
@@ -275,9 +297,24 @@ class InstanceStore {
|
|
|
275
297
|
/** Batches in draw order (sorted by pipeline, then material, then mesh). */
|
|
276
298
|
sorted = [];
|
|
277
299
|
byMesh = /* @__PURE__ */ new Map();
|
|
300
|
+
byMeshGround = /* @__PURE__ */ new Map();
|
|
301
|
+
/**
|
|
302
|
+
* Render layer mask per slot (0057); 1 for slots without RenderLayers. It also rides in bits
|
|
303
|
+
* 8–23 of the record's flags word, where the GPU culler reads it.
|
|
304
|
+
*/
|
|
305
|
+
layers = new Uint16Array(0);
|
|
306
|
+
/** Ground key per slot, (level, band, order) packed; NaN off the ground (0057, 0067). */
|
|
307
|
+
groundKeys = new Float64Array(0);
|
|
278
308
|
lodSets = [];
|
|
279
309
|
lodByKey = /* @__PURE__ */ new Map();
|
|
280
310
|
defaultMaterial = new MaterialAsset();
|
|
311
|
+
/**
|
|
312
|
+
* What stand-ins for failed meshes are drawn with (0061): matte gray, or magenta in a dev build
|
|
313
|
+
* (`DevMode`, read by prepareInstances).
|
|
314
|
+
*/
|
|
315
|
+
missingMaterial = new MaterialAsset({ baseColor: [0.5, 0.5, 0.5, 1], roughness: 1 });
|
|
316
|
+
/** A development build: the missing material turns magenta. */
|
|
317
|
+
dev = false;
|
|
281
318
|
/** Bytes of instance data uploaded this frame. */
|
|
282
319
|
uploadedBytes = 0;
|
|
283
320
|
/** Slots with a batch and not hidden, and hidden ones: stats for GPU-culled views. */
|
|
@@ -285,6 +322,13 @@ class InstanceStore {
|
|
|
285
322
|
hiddenCount = 0;
|
|
286
323
|
/** Bumps when batches or LOD sets change shape (the GPU tables re-upload). */
|
|
287
324
|
structureVersion = 0;
|
|
325
|
+
/** Bumps when a slot is allocated or freed (hidden sets resolve again, 0070). */
|
|
326
|
+
slotEpoch = 0;
|
|
327
|
+
/**
|
|
328
|
+
* Bumps when what casts shadows changes other than by moving: a slot's batch, its caster or
|
|
329
|
+
* visible flag, or a batch's mesh data. Cached shadow maps (0055) redraw when it moves.
|
|
330
|
+
*/
|
|
331
|
+
shadowEpoch = 0;
|
|
288
332
|
/** Visible slot lists of every CPU-culled view this frame, concatenated. */
|
|
289
333
|
visible = new Uint32Array(1024);
|
|
290
334
|
visibleCount = 0;
|
|
@@ -305,23 +349,11 @@ class InstanceStore {
|
|
|
305
349
|
constructor(gpu) {
|
|
306
350
|
this.gpu = gpu;
|
|
307
351
|
this.generation = gpu.generation;
|
|
308
|
-
this.instanceBuffer = new
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
size: INSTANCE_BYTES * 256
|
|
312
|
-
});
|
|
313
|
-
this.prevBuffer = new GpuBuffer(gpu, {
|
|
314
|
-
label: "instances/previous",
|
|
315
|
-
usage: GPUBufferUsage.STORAGE,
|
|
316
|
-
size: PREV_BYTES * 256
|
|
317
|
-
});
|
|
318
|
-
this.visibleBuffer = new GpuBuffer(gpu, {
|
|
319
|
-
label: "instances/visible",
|
|
320
|
-
usage: GPUBufferUsage.STORAGE,
|
|
321
|
-
size: 4 * 1024
|
|
322
|
-
});
|
|
323
|
-
this.layout = createInstanceLayout(gpu);
|
|
352
|
+
this.instanceBuffer = new DataStore(gpu, { label: "instances", size: INSTANCE_BYTES * 256 });
|
|
353
|
+
this.prevBuffer = new DataStore(gpu, { label: "instances/previous", size: PREV_BYTES * 256 });
|
|
354
|
+
this.visibleBuffer = new DataStore(gpu, { label: "instances/visible", size: 4 * 1024 });
|
|
324
355
|
this.deform = new DeformStore(gpu);
|
|
356
|
+
this.layout = createInstanceLayout(gpu, this);
|
|
325
357
|
this.grow(256);
|
|
326
358
|
}
|
|
327
359
|
get live() {
|
|
@@ -344,7 +376,7 @@ class InstanceStore {
|
|
|
344
376
|
const dirty = new Uint8Array(capacity);
|
|
345
377
|
dirty.set(this.dirty);
|
|
346
378
|
this.dirty = dirty;
|
|
347
|
-
const flags = new
|
|
379
|
+
const flags = new Uint16Array(capacity);
|
|
348
380
|
flags.set(this.flags);
|
|
349
381
|
this.flags = flags;
|
|
350
382
|
const prev = new Float32Array(capacity * 12);
|
|
@@ -356,6 +388,12 @@ class InstanceStore {
|
|
|
356
388
|
const movedFrame = new Uint32Array(capacity);
|
|
357
389
|
movedFrame.set(this.movedFrame);
|
|
358
390
|
this.movedFrame = movedFrame;
|
|
391
|
+
const layers = new Uint16Array(capacity).fill(1);
|
|
392
|
+
layers.set(this.layers);
|
|
393
|
+
this.layers = layers;
|
|
394
|
+
const groundKeys = new Float64Array(capacity).fill(Number.NaN);
|
|
395
|
+
groundKeys.set(this.groundKeys);
|
|
396
|
+
this.groundKeys = groundKeys;
|
|
359
397
|
this.capacity = capacity;
|
|
360
398
|
this.deform.ensureSlots(capacity);
|
|
361
399
|
}
|
|
@@ -366,16 +404,39 @@ class InstanceStore {
|
|
|
366
404
|
slot = this.high++;
|
|
367
405
|
}
|
|
368
406
|
this.u32[slot * INSTANCE_FLOATS + 12] = NO_BATCH;
|
|
369
|
-
this.u32[slot * INSTANCE_FLOATS + 13] =
|
|
407
|
+
this.u32[slot * INSTANCE_FLOATS + 13] = 1 << LAYER_SHIFT;
|
|
370
408
|
this.flags[slot] = 0;
|
|
371
409
|
this.u32[slot * INSTANCE_FLOATS + 14] = 0;
|
|
372
410
|
this.u32[slot * INSTANCE_FLOATS + 15] = entity % 4294967296;
|
|
373
411
|
this.batchOf[slot] = -1;
|
|
374
412
|
this.memberIndex[slot] = -1;
|
|
375
413
|
this.lods[slot] = null;
|
|
414
|
+
this.groundKeys[slot] = Number.NaN;
|
|
415
|
+
this.layers[slot] = 1;
|
|
376
416
|
this.markDirty(slot);
|
|
417
|
+
this.slotEpoch++;
|
|
377
418
|
return slot;
|
|
378
419
|
}
|
|
420
|
+
/** A slot's render layer mask (0057). What casts shadows in a view may change: cached maps redraw. */
|
|
421
|
+
setLayers(slot, mask) {
|
|
422
|
+
const m = mask & 65535;
|
|
423
|
+
if (this.layers[slot] === m) return;
|
|
424
|
+
this.layers[slot] = m;
|
|
425
|
+
const o = slot * INSTANCE_FLOATS + 13;
|
|
426
|
+
this.u32[o] = (this.u32[o] & ~LAYER_BITS | m << LAYER_SHIFT) >>> 0;
|
|
427
|
+
this.markDirty(slot);
|
|
428
|
+
this.shadowEpoch++;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* A slot's ground key (0057): `groundKey(band, order, level)`, or NaN off the ground. Returns whether
|
|
432
|
+
* it moved on or off the ground (its batch must be resolved again).
|
|
433
|
+
*/
|
|
434
|
+
setGround(slot, key) {
|
|
435
|
+
const old = this.groundKeys[slot];
|
|
436
|
+
if (Object.is(old, key)) return false;
|
|
437
|
+
this.groundKeys[slot] = key;
|
|
438
|
+
return Number.isNaN(old) !== Number.isNaN(key);
|
|
439
|
+
}
|
|
379
440
|
release(slot) {
|
|
380
441
|
this.assign(slot, -1);
|
|
381
442
|
this.u32[slot * INSTANCE_FLOATS + 13] = 0;
|
|
@@ -387,6 +448,7 @@ class InstanceStore {
|
|
|
387
448
|
this.deform.clear(slot);
|
|
388
449
|
this.markDirty(slot);
|
|
389
450
|
this.free.push(slot);
|
|
451
|
+
this.slotEpoch++;
|
|
390
452
|
}
|
|
391
453
|
boundsDirty = true;
|
|
392
454
|
cachedBounds = new Float32Array(6);
|
|
@@ -401,12 +463,11 @@ class InstanceStore {
|
|
|
401
463
|
b[0] = b[1] = b[2] = Number.POSITIVE_INFINITY;
|
|
402
464
|
b[3] = b[4] = b[5] = Number.NEGATIVE_INFINITY;
|
|
403
465
|
const sphere = scratchSphere;
|
|
404
|
-
const need = InstanceFlags.Visible | InstanceFlags.Caster;
|
|
405
466
|
for (let s = 0; s < this.high; s++) {
|
|
406
467
|
const a = this.batchOf[s];
|
|
407
|
-
if (a === -1 || (this.u32[s * INSTANCE_FLOATS + 13]
|
|
468
|
+
if (a === -1 || !casts(this.u32[s * INSTANCE_FLOATS + 13])) continue;
|
|
408
469
|
const batch = this.batches[a >= 0 ? a : this.lodSets[-2 - a].batches[0]];
|
|
409
|
-
if (batch.transparent) continue;
|
|
470
|
+
if (batch.transparent || batch.ground) continue;
|
|
410
471
|
sphereOfSlot(sphere, this, s, this.u32[s * INSTANCE_FLOATS + 13], batch.mesh.bounds);
|
|
411
472
|
for (let k = 0; k < 3; k++) {
|
|
412
473
|
if (sphere[k] - sphere[3] < b[k]) b[k] = sphere[k] - sphere[3];
|
|
@@ -505,6 +566,9 @@ class InstanceStore {
|
|
|
505
566
|
assign(slot, assignment) {
|
|
506
567
|
const old = this.batchOf[slot];
|
|
507
568
|
if (old === assignment) return;
|
|
569
|
+
this.shadowEpoch++;
|
|
570
|
+
const cut = this.flags[slot] & InstanceFlags.Cutaway;
|
|
571
|
+
if (cut) this.countCutaway(slot, -1);
|
|
508
572
|
if (old >= 0) {
|
|
509
573
|
this.batches[old].count--;
|
|
510
574
|
this.removeMember(slot, this.batches[old]);
|
|
@@ -523,8 +587,55 @@ class InstanceStore {
|
|
|
523
587
|
}
|
|
524
588
|
this.batchOf[slot] = assignment;
|
|
525
589
|
this.u32[slot * INSTANCE_FLOATS + 12] = assignment >= 0 ? assignment : assignment <= -2 ? (LOD_BIT | -2 - assignment) >>> 0 : NO_BATCH;
|
|
590
|
+
if (cut) this.countCutaway(slot, 1);
|
|
526
591
|
this.markDirty(slot);
|
|
527
592
|
}
|
|
593
|
+
/** Adds `delta` to the cutaway count of every batch the slot can land in (0070). */
|
|
594
|
+
countCutaway(slot, delta) {
|
|
595
|
+
const a = this.batchOf[slot];
|
|
596
|
+
if (a >= 0) this.batches[a].cutaway += delta;
|
|
597
|
+
else if (a <= -2)
|
|
598
|
+
for (const b of this.lodSets[-2 - a].batches) this.batches[b].cutaway += delta;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Bumps `shadowEpoch` if any batch's mesh data changed since the last call (a structure chunk
|
|
602
|
+
* rebuilt in place). One compare per batch.
|
|
603
|
+
*/
|
|
604
|
+
checkMeshVersions() {
|
|
605
|
+
const batches = this.batches;
|
|
606
|
+
for (let i = 0; i < batches.length; i++) {
|
|
607
|
+
const b = batches[i];
|
|
608
|
+
if (b.meshVersion !== b.mesh.version) {
|
|
609
|
+
b.meshVersion = b.mesh.version;
|
|
610
|
+
this.shadowEpoch++;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Whether a visible caster that moved this frame touches the half-spaces `planes`, where it
|
|
616
|
+
* was or where it is now. Call after prepare. Skinned and morphed poses aren't moves; see
|
|
617
|
+
* `deforming`.
|
|
618
|
+
*/
|
|
619
|
+
movedCasterIn(planes) {
|
|
620
|
+
const sphere = scratchSphere;
|
|
621
|
+
for (let i = 0; i < this.lastMovedCount; i++) {
|
|
622
|
+
const slot = this.lastMoved[i];
|
|
623
|
+
if (slot >= this.high || !casts(this.flags[slot])) continue;
|
|
624
|
+
const a = this.batchOf[slot];
|
|
625
|
+
if (a === -1) continue;
|
|
626
|
+
const batch = this.batches[a >= 0 ? a : this.lodSets[-2 - a].batches[0]];
|
|
627
|
+
if (batch.transparent || batch.ground) continue;
|
|
628
|
+
sphereOfSlot(sphere, this, slot, this.flags[slot], batch.mesh.bounds);
|
|
629
|
+
if (sphereInFrustum(planes, sphere)) return true;
|
|
630
|
+
slotSphere(sphere, this.prev, slot * 12, batch.mesh.bounds);
|
|
631
|
+
if (sphereInFrustum(planes, sphere)) return true;
|
|
632
|
+
}
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
/** Whether skinned or morphed poses are being written this frame (their shadows move). */
|
|
636
|
+
get deforming() {
|
|
637
|
+
return this.deform.poseCount > 0;
|
|
638
|
+
}
|
|
528
639
|
/** A plain batch assignment. */
|
|
529
640
|
setBatch(slot, index) {
|
|
530
641
|
this.assign(slot, index);
|
|
@@ -546,11 +657,12 @@ class InstanceStore {
|
|
|
546
657
|
this.memberIndex[last] = i;
|
|
547
658
|
this.memberIndex[slot] = -1;
|
|
548
659
|
}
|
|
549
|
-
batchFor(mesh, material, meshGuid, materialGuid) {
|
|
550
|
-
|
|
660
|
+
batchFor(mesh, material, meshGuid, materialGuid, ground = false) {
|
|
661
|
+
const byMesh = ground ? this.byMeshGround : this.byMesh;
|
|
662
|
+
let byMaterial = byMesh.get(mesh);
|
|
551
663
|
if (!byMaterial) {
|
|
552
664
|
byMaterial = /* @__PURE__ */ new Map();
|
|
553
|
-
|
|
665
|
+
byMesh.set(mesh, byMaterial);
|
|
554
666
|
}
|
|
555
667
|
let batch = byMaterial.get(material);
|
|
556
668
|
if (!batch) {
|
|
@@ -570,7 +682,10 @@ class InstanceStore {
|
|
|
570
682
|
scratch: new Uint32Array(64),
|
|
571
683
|
scratchCount: 0,
|
|
572
684
|
forwardScratch: new Uint32Array(16),
|
|
573
|
-
forwardCount: 0
|
|
685
|
+
forwardCount: 0,
|
|
686
|
+
meshVersion: mesh.version,
|
|
687
|
+
cutaway: 0,
|
|
688
|
+
ground
|
|
574
689
|
};
|
|
575
690
|
byMaterial.set(material, batch);
|
|
576
691
|
this.batches.push(batch);
|
|
@@ -648,7 +763,7 @@ class InstanceStore {
|
|
|
648
763
|
upload() {
|
|
649
764
|
if (this.generation !== this.gpu.generation) {
|
|
650
765
|
this.generation = this.gpu.generation;
|
|
651
|
-
this.layout = createInstanceLayout(this.gpu);
|
|
766
|
+
this.layout = createInstanceLayout(this.gpu, this);
|
|
652
767
|
this.bindGroup = void 0;
|
|
653
768
|
this.gpuBindGroup = void 0;
|
|
654
769
|
this.dirtyLo = 0;
|
|
@@ -716,13 +831,19 @@ class InstanceStore {
|
|
|
716
831
|
tBatches = new Int32Array(64);
|
|
717
832
|
tDepths = new Float32Array(64);
|
|
718
833
|
tOrder = [];
|
|
834
|
+
// Ground instances this cull: slot, batch, and (band, order) key.
|
|
835
|
+
groundCount = 0;
|
|
836
|
+
gSlots = new Uint32Array(64);
|
|
837
|
+
gBatches = new Int32Array(64);
|
|
838
|
+
gKeys = new Float64Array(64);
|
|
839
|
+
gOrder = [];
|
|
719
840
|
/**
|
|
720
841
|
* CPU culling of every slot into `list` (and, for camera views, `transparent` back to front and
|
|
721
842
|
* deferred `forwardOnly`). Same tests as the GPU culler: bounding sphere against the frustum,
|
|
722
843
|
* visibility range, LOD level. `bounds` gets the union of what passed (cascade fitting). With
|
|
723
844
|
* `transparentOnly`, only blended batches are culled (the GPU culls the rest).
|
|
724
845
|
*/
|
|
725
|
-
cullCpu(list, params, bounds, transparent, forward, forwardOnly, transparentOnly = false) {
|
|
846
|
+
cullCpu(list, params, bounds, transparent, forward, forwardOnly, transparentOnly = false, ground, overlay) {
|
|
726
847
|
if (bounds) {
|
|
727
848
|
bounds[0] = bounds[1] = bounds[2] = Number.POSITIVE_INFINITY;
|
|
728
849
|
bounds[3] = bounds[4] = bounds[5] = Number.NEGATIVE_INFINITY;
|
|
@@ -735,6 +856,10 @@ class InstanceStore {
|
|
|
735
856
|
list.cullView = -1;
|
|
736
857
|
this.transparentCount = 0;
|
|
737
858
|
this.forwardOnlyCount = 0;
|
|
859
|
+
this.groundCount = 0;
|
|
860
|
+
const layers = this.layers;
|
|
861
|
+
const viewLayers = params.layers ?? 4294967295;
|
|
862
|
+
const hiddenBits = params.hidden;
|
|
738
863
|
const batches = this.batches;
|
|
739
864
|
for (let b = 0; b < batches.length; b++) {
|
|
740
865
|
batches[b].scratchCount = 0;
|
|
@@ -743,16 +868,25 @@ class InstanceStore {
|
|
|
743
868
|
const f = this.f32;
|
|
744
869
|
const u32 = this.u32;
|
|
745
870
|
const mask = InstanceFlags.Visible | params.require;
|
|
871
|
+
const shadowOnly = (params.require & InstanceFlags.Caster) !== 0 ? InstanceFlags.ShadowOnly : 0;
|
|
746
872
|
const eye = params.eye;
|
|
747
873
|
const sphere = scratchSphere;
|
|
748
874
|
for (let s = 0; s < this.high; s++) {
|
|
749
875
|
const assignment = this.batchOf[s];
|
|
750
876
|
if (assignment === -1) continue;
|
|
751
877
|
const flags = u32[s * INSTANCE_FLOATS + 13];
|
|
752
|
-
if ((flags & mask) !== mask) {
|
|
878
|
+
if (((flags & shadowOnly ? flags | InstanceFlags.Visible : flags) & mask) !== mask) {
|
|
753
879
|
if ((flags & InstanceFlags.Visible) === 0) list.hidden++;
|
|
754
880
|
continue;
|
|
755
881
|
}
|
|
882
|
+
if ((layers[s] & viewLayers) === 0) {
|
|
883
|
+
list.culled++;
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if (hiddenBits !== void 0 && (hiddenBits[s >>> 5] & 1 << (s & 31)) !== 0) {
|
|
887
|
+
list.hidden++;
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
756
890
|
let set;
|
|
757
891
|
let b = assignment;
|
|
758
892
|
if (assignment <= -2) {
|
|
@@ -765,6 +899,7 @@ class InstanceStore {
|
|
|
765
899
|
list.pending++;
|
|
766
900
|
continue;
|
|
767
901
|
}
|
|
902
|
+
if (batch.ground && (params.require !== 0 || !ground)) continue;
|
|
768
903
|
if (batch.transparent && (params.require !== 0 || !transparent)) continue;
|
|
769
904
|
if (flags & InstanceFlags.Skinned) sphereOfSlot(sphere, this, s, flags, batch.mesh.bounds);
|
|
770
905
|
else slotSphere(sphere, f, s * INSTANCE_FLOATS, batch.mesh.bounds);
|
|
@@ -814,6 +949,10 @@ class InstanceStore {
|
|
|
814
949
|
if (sphere[k] + sphere[3] > bounds[k + 3]) bounds[k + 3] = sphere[k] + sphere[3];
|
|
815
950
|
}
|
|
816
951
|
}
|
|
952
|
+
if (batch.ground) {
|
|
953
|
+
this.pushGround(entry, b);
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
817
956
|
if (batch.transparent) {
|
|
818
957
|
this.pushTransparent(entry, b, eye, forward);
|
|
819
958
|
list.visible++;
|
|
@@ -839,6 +978,7 @@ class InstanceStore {
|
|
|
839
978
|
}
|
|
840
979
|
this.pack(list);
|
|
841
980
|
if (transparent) this.packTransparent(transparent);
|
|
981
|
+
if (ground) this.packGround(ground, overlay);
|
|
842
982
|
if (forwardOnly) {
|
|
843
983
|
for (const batch of this.batches) {
|
|
844
984
|
const n = batch.forwardCount;
|
|
@@ -859,12 +999,16 @@ class InstanceStore {
|
|
|
859
999
|
const u32 = this.u32;
|
|
860
1000
|
const eye = params.eye;
|
|
861
1001
|
const sphere = scratchSphere;
|
|
1002
|
+
const viewLayers = params.layers ?? 4294967295;
|
|
1003
|
+
const hiddenBits = params.hidden;
|
|
862
1004
|
for (const batch of this.batches) {
|
|
863
|
-
if (!batch.transparent || !batch.ready || batch.memberCount === 0) continue;
|
|
1005
|
+
if (!batch.transparent || batch.ground || !batch.ready || batch.memberCount === 0) continue;
|
|
864
1006
|
for (let m = 0; m < batch.memberCount; m++) {
|
|
865
1007
|
const s = batch.members[m];
|
|
866
1008
|
const flags = u32[s * INSTANCE_FLOATS + 13];
|
|
867
1009
|
if ((flags & InstanceFlags.Visible) === 0) continue;
|
|
1010
|
+
if ((this.layers[s] & viewLayers) === 0) continue;
|
|
1011
|
+
if (hiddenBits !== void 0 && (hiddenBits[s >>> 5] & 1 << (s & 31)) !== 0) continue;
|
|
868
1012
|
sphereOfSlot(sphere, this, s, flags, batch.mesh.bounds);
|
|
869
1013
|
if (params.planes && !sphereInFrustum(params.planes, sphere)) continue;
|
|
870
1014
|
if (flags & InstanceFlags.Range && eye) {
|
|
@@ -879,6 +1023,125 @@ class InstanceStore {
|
|
|
879
1023
|
}
|
|
880
1024
|
this.packTransparent(list);
|
|
881
1025
|
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Ground-only CPU cull over ground batches' members (0057), for views the GPU culls: the ground
|
|
1028
|
+
* list is always built on the CPU, in (band, order) order.
|
|
1029
|
+
*/
|
|
1030
|
+
cullGroundMembers(list, params, overlay) {
|
|
1031
|
+
this.groundCount = 0;
|
|
1032
|
+
const u32 = this.u32;
|
|
1033
|
+
const sphere = scratchSphere;
|
|
1034
|
+
const viewLayers = params.layers ?? 4294967295;
|
|
1035
|
+
const hiddenBits = params.hidden;
|
|
1036
|
+
for (const batch of this.batches) {
|
|
1037
|
+
if (!batch.ground || !batch.ready || batch.memberCount === 0) continue;
|
|
1038
|
+
for (let m = 0; m < batch.memberCount; m++) {
|
|
1039
|
+
const s = batch.members[m];
|
|
1040
|
+
const flags = u32[s * INSTANCE_FLOATS + 13];
|
|
1041
|
+
if ((flags & InstanceFlags.Visible) === 0) continue;
|
|
1042
|
+
if ((this.layers[s] & viewLayers) === 0) continue;
|
|
1043
|
+
if (hiddenBits !== void 0 && (hiddenBits[s >>> 5] & 1 << (s & 31)) !== 0) continue;
|
|
1044
|
+
slotSphere(sphere, this.f32, s * INSTANCE_FLOATS, batch.mesh.bounds);
|
|
1045
|
+
if (params.planes && !sphereInFrustum(params.planes, sphere)) continue;
|
|
1046
|
+
this.pushGround(s, batch.index);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
this.packGround(list, overlay);
|
|
1050
|
+
}
|
|
1051
|
+
pushGround(entry, batch) {
|
|
1052
|
+
const n = this.groundCount;
|
|
1053
|
+
if (n >= this.gSlots.length) {
|
|
1054
|
+
const size = this.gSlots.length * 2;
|
|
1055
|
+
const s = new Uint32Array(size);
|
|
1056
|
+
s.set(this.gSlots);
|
|
1057
|
+
this.gSlots = s;
|
|
1058
|
+
const b = new Int32Array(size);
|
|
1059
|
+
b.set(this.gBatches);
|
|
1060
|
+
this.gBatches = b;
|
|
1061
|
+
const k = new Float64Array(size);
|
|
1062
|
+
k.set(this.gKeys);
|
|
1063
|
+
this.gKeys = k;
|
|
1064
|
+
}
|
|
1065
|
+
this.gSlots[n] = entry;
|
|
1066
|
+
this.gBatches[n] = batch;
|
|
1067
|
+
this.gKeys[n] = this.groundKeys[entry & SLOT_MASK];
|
|
1068
|
+
this.groundCount = n + 1;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Packs visible-list entries (a slot in the low 28 bits, 4 spare bits a pass may use, like an
|
|
1072
|
+
* outline style) into `list`, one draw per batch: for passes that draw chosen slots (0057's
|
|
1073
|
+
* outline mask). `batches` holds each entry's batch index. Skips entries whose batch isn't ready.
|
|
1074
|
+
*/
|
|
1075
|
+
packEntries(list, entries, batches, count) {
|
|
1076
|
+
list.length = 0;
|
|
1077
|
+
list.cullView = -1;
|
|
1078
|
+
list.visible = 0;
|
|
1079
|
+
if (count === 0) return;
|
|
1080
|
+
const order = this.gOrder;
|
|
1081
|
+
order.length = count;
|
|
1082
|
+
for (let i = 0; i < count; i++) order[i] = i;
|
|
1083
|
+
order.sort((a, b) => batches[a] - batches[b] || entries[a] - entries[b]);
|
|
1084
|
+
this.reserve(count);
|
|
1085
|
+
let item;
|
|
1086
|
+
for (let k = 0; k < count; k++) {
|
|
1087
|
+
const i = order[k];
|
|
1088
|
+
const batch = this.batches[batches[i]];
|
|
1089
|
+
if (!batch?.ready) continue;
|
|
1090
|
+
if (!item || item.batch !== batch) {
|
|
1091
|
+
item = this.item(list, batch);
|
|
1092
|
+
item.first = this.visibleCount;
|
|
1093
|
+
item.count = 0;
|
|
1094
|
+
}
|
|
1095
|
+
this.visible[this.visibleCount++] = entries[i];
|
|
1096
|
+
item.count++;
|
|
1097
|
+
list.visible++;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Sorts this cull's ground instances by (level, band, order), then batch; runs of a batch share a
|
|
1102
|
+
* draw. With `overlay`, bands from OVERLAY_BAND up go there instead (0058).
|
|
1103
|
+
*/
|
|
1104
|
+
packGround(list, overlay) {
|
|
1105
|
+
list.length = 0;
|
|
1106
|
+
list.cullView = -1;
|
|
1107
|
+
if (overlay) {
|
|
1108
|
+
overlay.length = 0;
|
|
1109
|
+
overlay.cullView = -1;
|
|
1110
|
+
overlay.visible = 0;
|
|
1111
|
+
}
|
|
1112
|
+
const n = this.groundCount;
|
|
1113
|
+
list.visible = n;
|
|
1114
|
+
if (n === 0) return;
|
|
1115
|
+
const order = this.gOrder;
|
|
1116
|
+
order.length = n;
|
|
1117
|
+
for (let i = 0; i < n; i++) order[i] = i;
|
|
1118
|
+
const keys = this.gKeys;
|
|
1119
|
+
const batches = this.gBatches;
|
|
1120
|
+
const slots = this.gSlots;
|
|
1121
|
+
order.sort((a, b) => keys[a] - keys[b] || batches[a] - batches[b] || slots[a] - slots[b]);
|
|
1122
|
+
this.reserve(n);
|
|
1123
|
+
let item;
|
|
1124
|
+
let into = list;
|
|
1125
|
+
for (let k = 0; k < n; k++) {
|
|
1126
|
+
const i = order[k];
|
|
1127
|
+
const batch = this.batches[batches[i]];
|
|
1128
|
+
if (overlay && into === list && bandOfKey(keys[i]) >= OVERLAY_BAND) {
|
|
1129
|
+
into = overlay;
|
|
1130
|
+
item = void 0;
|
|
1131
|
+
}
|
|
1132
|
+
if (!item || item.batch !== batch) {
|
|
1133
|
+
item = this.item(into, batch);
|
|
1134
|
+
item.first = this.visibleCount;
|
|
1135
|
+
item.count = 0;
|
|
1136
|
+
}
|
|
1137
|
+
this.visible[this.visibleCount++] = slots[i];
|
|
1138
|
+
item.count++;
|
|
1139
|
+
if (into === overlay) {
|
|
1140
|
+
overlay.visible++;
|
|
1141
|
+
list.visible--;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
882
1145
|
/**
|
|
883
1146
|
* CPU culling with positional arguments: planes, flag mask, bounds, transparent list, eye, view
|
|
884
1147
|
* direction, and the deferred forward-only list. LOD selects fresh (no hysteresis state).
|
|
@@ -970,7 +1233,8 @@ class InstanceStore {
|
|
|
970
1233
|
const n = batch.memberCount;
|
|
971
1234
|
if (n === 0 || n !== batch.count || n > 8) return true;
|
|
972
1235
|
for (let i = 0; i < n; i++) {
|
|
973
|
-
|
|
1236
|
+
const flags = this.u32[batch.members[i] * INSTANCE_FLOATS + 13];
|
|
1237
|
+
if (flags & (InstanceFlags.Visible | InstanceFlags.ShadowOnly)) return true;
|
|
974
1238
|
}
|
|
975
1239
|
return false;
|
|
976
1240
|
}
|
|
@@ -1040,12 +1304,12 @@ class InstanceStore {
|
|
|
1040
1304
|
label,
|
|
1041
1305
|
layout: this.layout,
|
|
1042
1306
|
entries: [
|
|
1043
|
-
{ binding: 0, resource:
|
|
1044
|
-
{ binding: 1, resource:
|
|
1045
|
-
{ binding: 2, resource:
|
|
1046
|
-
{ binding: 3, resource:
|
|
1047
|
-
{ binding: 4, resource:
|
|
1048
|
-
{ binding: 5, resource:
|
|
1307
|
+
{ binding: 0, resource: this.instanceBuffer.resource() },
|
|
1308
|
+
{ binding: 1, resource: visible },
|
|
1309
|
+
{ binding: 2, resource: this.prevBuffer.resource() },
|
|
1310
|
+
{ binding: 3, resource: this.deform.recordBuffer.resource() },
|
|
1311
|
+
{ binding: 4, resource: this.deform.poseBuffer.resource() },
|
|
1312
|
+
{ binding: 5, resource: this.deform.vertexBuffer.resource() }
|
|
1049
1313
|
]
|
|
1050
1314
|
});
|
|
1051
1315
|
}
|
|
@@ -1058,14 +1322,14 @@ class InstanceStore {
|
|
|
1058
1322
|
const deformKey = `${d.recordBuffer.version}/${d.poseBuffer.version}/${d.vertexBuffer.version}`;
|
|
1059
1323
|
const key = `${this.instanceBuffer.version}/${this.visibleBuffer.version}/${this.prevBuffer.version}/${deformKey}/${this.gpu.generation}`;
|
|
1060
1324
|
if (!this.bindGroup || this.bound !== key) {
|
|
1061
|
-
this.bindGroup = this.createBindGroup("instances", this.visibleBuffer.
|
|
1325
|
+
this.bindGroup = this.createBindGroup("instances", this.visibleBuffer.resource());
|
|
1062
1326
|
this.bound = key;
|
|
1063
1327
|
}
|
|
1064
1328
|
const g = this.gpuVisible;
|
|
1065
1329
|
if (g) {
|
|
1066
1330
|
const gkey = `${this.instanceBuffer.version}/${g.version}/${this.prevBuffer.version}/${deformKey}/${this.gpu.generation}`;
|
|
1067
1331
|
if (!this.gpuBindGroup || this.gpuBound !== gkey) {
|
|
1068
|
-
this.gpuBindGroup = this.createBindGroup("instances/gpu-culled", g.buffer);
|
|
1332
|
+
this.gpuBindGroup = this.createBindGroup("instances/gpu-culled", { buffer: g.buffer });
|
|
1069
1333
|
this.gpuBound = gkey;
|
|
1070
1334
|
}
|
|
1071
1335
|
}
|
|
@@ -1094,25 +1358,19 @@ function idOf(o) {
|
|
|
1094
1358
|
}
|
|
1095
1359
|
return id;
|
|
1096
1360
|
}
|
|
1097
|
-
function createInstanceLayout(gpu) {
|
|
1361
|
+
function createInstanceLayout(gpu, store) {
|
|
1362
|
+
const VF = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT;
|
|
1363
|
+
const V = GPUShaderStage.VERTEX;
|
|
1098
1364
|
return gpu.layouts.bindGroupLayout({
|
|
1099
1365
|
label: "instances",
|
|
1100
1366
|
entries: [
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
buffer: { type: "read-only-storage" }
|
|
1105
|
-
},
|
|
1106
|
-
{
|
|
1107
|
-
binding: 1,
|
|
1108
|
-
visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
|
|
1109
|
-
buffer: { type: "read-only-storage" }
|
|
1110
|
-
},
|
|
1111
|
-
{ binding: 2, visibility: GPUShaderStage.VERTEX, buffer: { type: "read-only-storage" } },
|
|
1367
|
+
store.instanceBuffer.layoutEntry(0, VF),
|
|
1368
|
+
store.visibleBuffer.layoutEntry(1, VF),
|
|
1369
|
+
store.prevBuffer.layoutEntry(2, V),
|
|
1112
1370
|
// Deform records, poses (joint matrices, morph weights), and per-vertex deform data.
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1371
|
+
store.deform.recordBuffer.layoutEntry(3, V),
|
|
1372
|
+
store.deform.poseBuffer.layoutEntry(4, V),
|
|
1373
|
+
store.deform.vertexBuffer.layoutEntry(5, V)
|
|
1116
1374
|
]
|
|
1117
1375
|
});
|
|
1118
1376
|
}
|
|
@@ -1120,7 +1378,16 @@ const Instances = defineResource("render/Instances", {
|
|
|
1120
1378
|
description: "The persistent GPU instance buffer: one slot per Mesh3d entity."
|
|
1121
1379
|
});
|
|
1122
1380
|
function tableChanged(table, since) {
|
|
1123
|
-
return table.lastStructural > since || table.lastChanged(GlobalTransform) > since || table.lastChanged(Mesh3d) > since || table.lastChanged(ComputedVisibility) > since || table.lastChanged(InstanceSlot) > since || table.has(MeshMaterial) && table.lastChanged(MeshMaterial) > since || table.has(Lod) && table.lastChanged(Lod) > since || table.has(VisibilityRange) && table.lastChanged(VisibilityRange) > since || table.has(InstanceData) && table.lastChanged(InstanceData) > since;
|
|
1381
|
+
return table.lastStructural > since || table.lastChanged(GlobalTransform) > since || table.lastChanged(Mesh3d) > since || table.lastChanged(ComputedVisibility) > since || table.lastChanged(InstanceSlot) > since || table.has(MeshMaterial) && table.lastChanged(MeshMaterial) > since || table.has(Lod) && table.lastChanged(Lod) > since || table.has(VisibilityRange) && table.lastChanged(VisibilityRange) > since || table.has(InstanceData) && table.lastChanged(InstanceData) > since || table.has(RenderLayers) && table.lastChanged(RenderLayers) > since || table.has(GroundLayer) && table.lastChanged(GroundLayer) > since;
|
|
1382
|
+
}
|
|
1383
|
+
function casts(flags) {
|
|
1384
|
+
return (flags & InstanceFlags.Caster) !== 0 && (flags & (InstanceFlags.Visible | InstanceFlags.ShadowOnly)) !== 0;
|
|
1385
|
+
}
|
|
1386
|
+
function groundKey(band, order, level = 0) {
|
|
1387
|
+
return (level + 16) * 281474976710656 + (band + 32768) * 4294967296 + (order + 2147483648);
|
|
1388
|
+
}
|
|
1389
|
+
function bandOfKey(key) {
|
|
1390
|
+
return Math.floor(key / 4294967296) % 65536 - 32768;
|
|
1124
1391
|
}
|
|
1125
1392
|
let zeros = new Uint32Array(1024);
|
|
1126
1393
|
function zeroTicks(n) {
|
|
@@ -1130,19 +1397,6 @@ function zeroTicks(n) {
|
|
|
1130
1397
|
const DeformPath = defineResource("render/DeformPath", {
|
|
1131
1398
|
description: "Present when skinning and morph targets (skinningPlugin) are installed."
|
|
1132
1399
|
});
|
|
1133
|
-
const warnedDeforms = /* @__PURE__ */ new WeakSet();
|
|
1134
|
-
function warnNoDeforms(world) {
|
|
1135
|
-
if (warnedDeforms.has(world)) return;
|
|
1136
|
-
warnedDeforms.add(world);
|
|
1137
|
-
world.tryResource(LogResource)?.log(
|
|
1138
|
-
"warn",
|
|
1139
|
-
"An entity has SkinnedMesh or MorphWeights, but skinningPlugin isn't installed; it draws in its rest pose",
|
|
1140
|
-
{
|
|
1141
|
-
code: "render/feature-missing",
|
|
1142
|
-
hint: "Add skinningPlugin from '@aethervtt/shard-render' (forwardPlugin includes it)."
|
|
1143
|
-
}
|
|
1144
|
-
);
|
|
1145
|
-
}
|
|
1146
1400
|
const prepareInstances = defineSystem({
|
|
1147
1401
|
name: "render/prepare-instances",
|
|
1148
1402
|
description: "Assigns instance slots, writes changed transforms and batches, and uploads dirty slots.",
|
|
@@ -1156,7 +1410,13 @@ const prepareInstances = defineSystem({
|
|
|
1156
1410
|
const assets = world.resource(GpuAssetsResource);
|
|
1157
1411
|
assets.beginFrame();
|
|
1158
1412
|
store.beginFrame();
|
|
1413
|
+
const dev = world.tryResource(DevMode)?.enabled ?? false;
|
|
1414
|
+
if (dev !== store.dev) {
|
|
1415
|
+
store.dev = dev;
|
|
1416
|
+
store.missingMaterial.set({ baseColor: dev ? [1, 0, 1, 1] : [0.5, 0.5, 0.5, 1] });
|
|
1417
|
+
}
|
|
1159
1418
|
const deforms = world.hasResource(DeformPath);
|
|
1419
|
+
const cutaways = world.hasResource(CutawayPath);
|
|
1160
1420
|
const since = ctx.lastRunTick;
|
|
1161
1421
|
let hidden = 0;
|
|
1162
1422
|
let rows = 0;
|
|
@@ -1191,9 +1451,24 @@ const prepareInstances = defineSystem({
|
|
|
1191
1451
|
const dataY = hasData ? table.column(InstanceData, "y") : void 0;
|
|
1192
1452
|
const dataChanged = hasData ? table.changedTicks(InstanceData) : zeroTicks(n);
|
|
1193
1453
|
const vis = table.column(ComputedVisibility, "visible");
|
|
1454
|
+
const hiddenFlag = table.has(ShadowWhenHidden) ? InstanceFlags.ShadowOnly : 0;
|
|
1455
|
+
const layerMasks = table.has(RenderLayers) ? table.column(RenderLayers, "mask") : void 0;
|
|
1456
|
+
const hasGround = table.has(GroundLayer);
|
|
1457
|
+
const bands = hasGround ? table.column(GroundLayer, "band") : void 0;
|
|
1458
|
+
const orders = hasGround ? table.column(GroundLayer, "order") : void 0;
|
|
1459
|
+
const levels = hasGround ? table.column(GroundLayer, "level") : void 0;
|
|
1194
1460
|
const deformed = table.has(SkinnedMesh) || table.has(MorphWeights);
|
|
1195
|
-
if (deformed && !deforms)
|
|
1196
|
-
|
|
1461
|
+
if (deformed && !deforms)
|
|
1462
|
+
warnFeatureMissing(
|
|
1463
|
+
world,
|
|
1464
|
+
"An entity has SkinnedMesh or MorphWeights",
|
|
1465
|
+
"skinningPlugin",
|
|
1466
|
+
"it draws in its rest pose"
|
|
1467
|
+
);
|
|
1468
|
+
const cut = table.has(Cutaway);
|
|
1469
|
+
if (cut && !cutaways)
|
|
1470
|
+
warnFeatureMissing(world, "An entity has Cutaway", "cutawayPlugin", "it draws whole");
|
|
1471
|
+
const tableFlags = (cut && cutaways ? InstanceFlags.Cutaway : 0) | (deforms && table.has(SkinnedMesh) ? InstanceFlags.Skinned : 0) | (deforms && table.has(MorphWeights) ? InstanceFlags.Morph : 0) | (table.has(NotShadowCaster) ? 0 : InstanceFlags.Caster) | (table.has(NotShadowReceiver) ? 0 : InstanceFlags.Receiver) | (hasRange ? InstanceFlags.Range : 0) | (hasLod ? InstanceFlags.Lod : 0);
|
|
1197
1472
|
let u32 = store.u32;
|
|
1198
1473
|
let slotFlags = store.flags;
|
|
1199
1474
|
for (let i = 0; i < n; i++) {
|
|
@@ -1209,10 +1484,15 @@ const prepareInstances = defineSystem({
|
|
|
1209
1484
|
const o = slot * INSTANCE_FLOATS;
|
|
1210
1485
|
const visible = vis[i] !== 0;
|
|
1211
1486
|
if (!visible) hidden++;
|
|
1212
|
-
const flags = tableFlags | (visible ? InstanceFlags.Visible :
|
|
1487
|
+
const flags = tableFlags | (visible ? InstanceFlags.Visible : hiddenFlag);
|
|
1213
1488
|
const oldFlags = slotFlags[slot];
|
|
1214
1489
|
if (fresh || gChanged[i] > since) store.moveSlot(slot, g, i * 12, fresh);
|
|
1215
|
-
|
|
1490
|
+
store.setLayers(slot, layerMasks ? layerMasks[i] : 1);
|
|
1491
|
+
const onOffGround = store.setGround(
|
|
1492
|
+
slot,
|
|
1493
|
+
hasGround ? groundKey(bands[i], orders[i], levels[i]) : Number.NaN
|
|
1494
|
+
);
|
|
1495
|
+
if (fresh || onOffGround || ((oldFlags ^ flags) & InstanceFlags.Lod) !== 0 || meshChanged[i] > since || materialChanged[i] > since || lodChanged[i] > since) {
|
|
1216
1496
|
store.meshRefs[slot] = meshRefs[i] ?? null;
|
|
1217
1497
|
store.materialRefs[slot] = materialRefs?.[i] ?? null;
|
|
1218
1498
|
store.lods[slot] = hasLod ? {
|
|
@@ -1223,9 +1503,13 @@ const prepareInstances = defineSystem({
|
|
|
1223
1503
|
resolveSlot(store, slot, meshes, materials);
|
|
1224
1504
|
}
|
|
1225
1505
|
if (oldFlags !== flags) {
|
|
1226
|
-
u32[o + 13] = flags;
|
|
1506
|
+
u32[o + 13] = recordFlags(flags, store.layers[slot]);
|
|
1227
1507
|
slotFlags[slot] = flags;
|
|
1508
|
+
if ((oldFlags ^ flags) & InstanceFlags.Cutaway)
|
|
1509
|
+
store.countCutaway(slot, flags & InstanceFlags.Cutaway ? 1 : -1);
|
|
1228
1510
|
store.markDirty(slot);
|
|
1511
|
+
if ((oldFlags ^ flags) & (InstanceFlags.Visible | InstanceFlags.Caster | InstanceFlags.ShadowOnly))
|
|
1512
|
+
store.shadowEpoch++;
|
|
1229
1513
|
}
|
|
1230
1514
|
if (fresh || rangeChanged[i] > since) {
|
|
1231
1515
|
if (hasRange) store.setRange(slot, rangeStart[i], rangeEnd[i]);
|
|
@@ -1240,7 +1524,18 @@ const prepareInstances = defineSystem({
|
|
|
1240
1524
|
store.hiddenCount = hidden;
|
|
1241
1525
|
store.drawableCount = rows - store.pending.size;
|
|
1242
1526
|
if (store.pending.size > 0) {
|
|
1243
|
-
|
|
1527
|
+
const server = world.tryResource(AssetServerResource);
|
|
1528
|
+
for (const slot of store.pending) {
|
|
1529
|
+
if (server) {
|
|
1530
|
+
const meshRef = store.meshRefs[slot];
|
|
1531
|
+
if (meshRef?.guid !== void 0 && !meshes.has(meshRef.guid))
|
|
1532
|
+
server.missing(meshRef, "Mesh");
|
|
1533
|
+
const materialRef = store.materialRefs[slot];
|
|
1534
|
+
if (materialRef?.guid !== void 0 && !materials.has(materialRef.guid))
|
|
1535
|
+
server.missing(materialRef, "Material");
|
|
1536
|
+
}
|
|
1537
|
+
resolveSlot(store, slot, meshes, materials);
|
|
1538
|
+
}
|
|
1244
1539
|
}
|
|
1245
1540
|
for (const batch of store.batches) {
|
|
1246
1541
|
if (batch.count === 0) continue;
|
|
@@ -1287,9 +1582,17 @@ function resolveSlot(store, slot, meshes, materials) {
|
|
|
1287
1582
|
return;
|
|
1288
1583
|
}
|
|
1289
1584
|
store.pending.delete(slot);
|
|
1585
|
+
const ground = !Number.isNaN(store.groundKeys[slot]);
|
|
1586
|
+
const drawn = mesh.missing ? store.missingMaterial : material;
|
|
1290
1587
|
store.assign(
|
|
1291
1588
|
slot,
|
|
1292
|
-
store.batchFor(
|
|
1589
|
+
store.batchFor(
|
|
1590
|
+
mesh,
|
|
1591
|
+
drawn,
|
|
1592
|
+
meshRef?.guid,
|
|
1593
|
+
mesh.missing ? void 0 : materialRef?.guid ?? void 0,
|
|
1594
|
+
ground
|
|
1595
|
+
).index
|
|
1293
1596
|
);
|
|
1294
1597
|
}
|
|
1295
1598
|
function observeInstanceRemovals(world) {
|
|
@@ -1303,6 +1606,7 @@ function observeInstanceRemovals(world) {
|
|
|
1303
1606
|
});
|
|
1304
1607
|
}
|
|
1305
1608
|
export {
|
|
1609
|
+
CUTAWAY_BIT,
|
|
1306
1610
|
DeformPath,
|
|
1307
1611
|
INSTANCE_BYTES,
|
|
1308
1612
|
INSTANCE_FLOATS,
|
|
@@ -1311,6 +1615,8 @@ export {
|
|
|
1311
1615
|
InstanceSlot,
|
|
1312
1616
|
InstanceStore,
|
|
1313
1617
|
Instances,
|
|
1618
|
+
LAYER_BITS,
|
|
1619
|
+
LAYER_SHIFT,
|
|
1314
1620
|
LOD_BIT,
|
|
1315
1621
|
LOD_UNSET,
|
|
1316
1622
|
Lod,
|
|
@@ -1322,13 +1628,16 @@ export {
|
|
|
1322
1628
|
NotShadowCaster,
|
|
1323
1629
|
NotShadowReceiver,
|
|
1324
1630
|
SLOT_MASK,
|
|
1631
|
+
ShadowWhenHidden,
|
|
1325
1632
|
SkinnedMesh,
|
|
1326
1633
|
VisibilityRange,
|
|
1327
1634
|
createDrawList,
|
|
1328
1635
|
createInstanceLayout,
|
|
1636
|
+
groundKey,
|
|
1329
1637
|
lodSize,
|
|
1330
1638
|
observeInstanceRemovals,
|
|
1331
1639
|
prepareInstances,
|
|
1640
|
+
recordFlags,
|
|
1332
1641
|
selectLod,
|
|
1333
1642
|
slotSphere,
|
|
1334
1643
|
sphereInFrustum
|