@aethervtt/shard-sprite 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/baseline/lights2d.d.ts +6 -0
- package/dist/baseline/lights2d.js +114 -0
- package/dist/ground.d.ts +81 -0
- package/dist/ground.js +394 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +52 -2
- package/dist/lighting-shaders.js +4 -4
- package/dist/lights2d.d.ts +23 -7
- package/dist/lights2d.js +78 -40
- package/dist/methods.d.ts +82 -0
- package/dist/methods.js +246 -0
- package/dist/plugin.d.ts +2 -1
- package/dist/plugin.js +43 -6
- package/dist/render.d.ts +19 -7
- package/dist/render.js +60 -51
- package/dist/shaders.js +11 -6
- package/dist/tilemap-format.d.ts +39 -0
- package/dist/tilemap-format.js +177 -0
- package/dist/tilemap.d.ts +83 -12
- package/dist/tilemap.js +311 -58
- package/dist/validate.d.ts +11 -0
- package/dist/validate.js +49 -0
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -23,6 +23,16 @@ import {
|
|
|
23
23
|
SpriteClipStore,
|
|
24
24
|
SpriteClips
|
|
25
25
|
} from "./clip.js";
|
|
26
|
+
import {
|
|
27
|
+
GROUND_TILE_SHADERS,
|
|
28
|
+
GroundTiles,
|
|
29
|
+
syncGroundTiles,
|
|
30
|
+
TileChunk,
|
|
31
|
+
TileLit,
|
|
32
|
+
TileUnlit,
|
|
33
|
+
tileDetail,
|
|
34
|
+
tilemapOnGround
|
|
35
|
+
} from "./ground.js";
|
|
26
36
|
import {
|
|
27
37
|
Lighting2d,
|
|
28
38
|
LightOccluder2d,
|
|
@@ -51,6 +61,12 @@ import {
|
|
|
51
61
|
TILE_PIXELS,
|
|
52
62
|
TILE_STRIDE
|
|
53
63
|
} from "./lights2d.js";
|
|
64
|
+
import {
|
|
65
|
+
editTiles,
|
|
66
|
+
findTilemapData,
|
|
67
|
+
readTiles,
|
|
68
|
+
tilemapMethods
|
|
69
|
+
} from "./methods.js";
|
|
54
70
|
import {
|
|
55
71
|
boxPolygon,
|
|
56
72
|
capsulePolygon,
|
|
@@ -68,6 +84,7 @@ import { spritePlugin } from "./plugin.js";
|
|
|
68
84
|
import {
|
|
69
85
|
describeSprites,
|
|
70
86
|
prepareSprites,
|
|
87
|
+
resolveTiles,
|
|
71
88
|
SPRITE_FLOATS,
|
|
72
89
|
SpriteStore,
|
|
73
90
|
Sprites,
|
|
@@ -94,12 +111,25 @@ import {
|
|
|
94
111
|
TilemapDataSchema,
|
|
95
112
|
TilemapDataStore,
|
|
96
113
|
TilemapDatas,
|
|
97
|
-
tileAt
|
|
114
|
+
tileAt,
|
|
115
|
+
tilemapToText,
|
|
116
|
+
tileNames,
|
|
117
|
+
unknownTiles
|
|
98
118
|
} from "./tilemap.js";
|
|
119
|
+
import {
|
|
120
|
+
chunkRows,
|
|
121
|
+
readChunkRows,
|
|
122
|
+
rectRows,
|
|
123
|
+
rowsCanName,
|
|
124
|
+
TILE_FLAG_NAMES
|
|
125
|
+
} from "./tilemap-format.js";
|
|
126
|
+
import { validateTilemaps } from "./validate.js";
|
|
99
127
|
export {
|
|
100
128
|
AtlasPackImporter,
|
|
101
129
|
AtlasPackSchema,
|
|
102
130
|
CLIP_LOOPS,
|
|
131
|
+
GROUND_TILE_SHADERS,
|
|
132
|
+
GroundTiles,
|
|
103
133
|
LIGHT2D_FLOATS,
|
|
104
134
|
LightOccluder2d,
|
|
105
135
|
LightView2d,
|
|
@@ -132,6 +162,7 @@ export {
|
|
|
132
162
|
SpriteSlot,
|
|
133
163
|
SpriteStore,
|
|
134
164
|
Sprites,
|
|
165
|
+
TILE_FLAG_NAMES,
|
|
135
166
|
TILE_MAX,
|
|
136
167
|
TILE_PIXELS,
|
|
137
168
|
TILE_STRIDE,
|
|
@@ -141,8 +172,11 @@ export {
|
|
|
141
172
|
TextureAtlasSchema,
|
|
142
173
|
TextureAtlasStore,
|
|
143
174
|
TextureAtlases,
|
|
175
|
+
TileChunk,
|
|
144
176
|
TileFlags,
|
|
145
177
|
TileLayer,
|
|
178
|
+
TileLit,
|
|
179
|
+
TileUnlit,
|
|
146
180
|
Tilemap,
|
|
147
181
|
TilemapData,
|
|
148
182
|
TilemapDataAssetType,
|
|
@@ -157,11 +191,14 @@ export {
|
|
|
157
191
|
binLightsCpu,
|
|
158
192
|
boxPolygon,
|
|
159
193
|
capsulePolygon,
|
|
194
|
+
chunkRows,
|
|
160
195
|
circlePolygon,
|
|
161
196
|
convexHull,
|
|
162
197
|
describeLights2d,
|
|
163
198
|
describeSprites,
|
|
164
199
|
distanceToLoop,
|
|
200
|
+
editTiles,
|
|
201
|
+
findTilemapData,
|
|
165
202
|
layerBit,
|
|
166
203
|
lightCircle,
|
|
167
204
|
loopArea,
|
|
@@ -172,15 +209,28 @@ export {
|
|
|
172
209
|
polygonSegments,
|
|
173
210
|
prepareLights2d,
|
|
174
211
|
prepareSprites,
|
|
212
|
+
readChunkRows,
|
|
213
|
+
readTiles,
|
|
214
|
+
rectRows,
|
|
175
215
|
regionOutline,
|
|
216
|
+
resolveTiles,
|
|
217
|
+
rowsCanName,
|
|
176
218
|
setTile,
|
|
177
219
|
shadowFactorCpu,
|
|
178
220
|
shadowRowCpu,
|
|
179
221
|
simplifyLoop,
|
|
180
222
|
spriteOutline,
|
|
181
223
|
spritePlugin,
|
|
224
|
+
syncGroundTiles,
|
|
182
225
|
tileAt,
|
|
183
226
|
tileChunkEdges,
|
|
227
|
+
tileDetail,
|
|
228
|
+
tileNames,
|
|
229
|
+
tilemapMethods,
|
|
230
|
+
tilemapOnGround,
|
|
231
|
+
tilemapToText,
|
|
184
232
|
traceAlpha,
|
|
185
|
-
transformSegments
|
|
233
|
+
transformSegments,
|
|
234
|
+
unknownTiles,
|
|
235
|
+
validateTilemaps
|
|
186
236
|
};
|
package/dist/lighting-shaders.js
CHANGED
|
@@ -53,11 +53,11 @@ struct Segment2d {
|
|
|
53
53
|
import shard::sprite::light2d::types::{ Light2d, Lighting2dView, TILE_PIXELS, TILE_STRIDE, TILE_MAX, SHADOW_RES, COARSE_RES, COARSE_SPAN, TAU, PI };
|
|
54
54
|
|
|
55
55
|
@group(3) @binding(0) var<uniform> light_view: Lighting2dView;
|
|
56
|
-
@group(3) @binding(1) var<storage, read> lights: array<Light2d>;
|
|
57
|
-
@group(3) @binding(2) var<storage, read> light_tiles: array<u32>;
|
|
58
|
-
@group(3) @binding(3) var<storage, read> shadow_map: array<u32>;
|
|
56
|
+
@data @group(3) @binding(1) var<storage, read> lights: array<Light2d>;
|
|
57
|
+
@data @group(3) @binding(2) var<storage, read> light_tiles: array<u32>;
|
|
58
|
+
@data @group(3) @binding(3) var<storage, read> shadow_map: array<u32>;
|
|
59
59
|
/** Per row, per 32 angles: the nearest and farthest occluder distance. */
|
|
60
|
-
@group(3) @binding(4) var<storage, read> shadow_coarse: array<vec2f>;
|
|
60
|
+
@data @group(3) @binding(4) var<storage, read> shadow_coarse: array<vec2f>;
|
|
61
61
|
|
|
62
62
|
/** Min and max over the coarse bin holding continuous bin coordinate x. */
|
|
63
63
|
fn coarse_at(row: u32, x: f32) -> vec2f {
|
package/dist/lights2d.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Entity, type Query, type World } from '@aethervtt/shard-core';
|
|
2
2
|
import { GpuBuffer, type GpuContext } from '@aethervtt/shard-gpu';
|
|
3
|
-
import { type NodeDescriptor } from '@aethervtt/shard-render';
|
|
3
|
+
import { DataStore, type NodeDescriptor } from '@aethervtt/shard-render';
|
|
4
4
|
import { type TileLayer } from './tilemap.js';
|
|
5
5
|
/** Floats per light record (see `Light2d` in lighting-shaders.ts). */
|
|
6
6
|
export declare const LIGHT2D_FLOATS = 16;
|
|
@@ -20,6 +20,11 @@ export declare const MAX_SHADOWED = 64;
|
|
|
20
20
|
* reaches the view. Works for any camera: the radius is the larger of the projected x and y offsets.
|
|
21
21
|
*/
|
|
22
22
|
export declare function lightCircle(viewProj: ArrayLike<number>, width: number, height: number, x: number, y: number, z: number, r: number, out: Float32Array, o: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Bins light circles into 16×16-pixel tiles, like the GPU, in its layout: per tile TILE_STRIDE
|
|
25
|
+
* u32s, the full count, then up to 64 light indices. Allocates nothing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function binLightsInto(circles: Float32Array, count: number, tilesX: number, tilesY: number, out: Uint32Array): void;
|
|
23
28
|
/** Bins light circles into 16×16-pixel tiles, like the GPU: a full count and up to 64 indices. */
|
|
24
29
|
export declare function binLightsCpu(circles: Float32Array, count: number, tilesX: number, tilesY: number): {
|
|
25
30
|
counts: Uint32Array;
|
|
@@ -30,6 +35,11 @@ export declare function binLightsCpu(circles: Float32Array, count: number, tiles
|
|
|
30
35
|
* distance plus its penetration. `segments` holds SEGMENT_FLOATS per segment.
|
|
31
36
|
*/
|
|
32
37
|
export declare function shadowRowCpu(lx: number, ly: number, radius: number, layers: number, segments: Float32Array, count: number, out: Float32Array): Float32Array;
|
|
38
|
+
/**
|
|
39
|
+
* One light's shadow row written into `out` from `at`: shadowRowCpu's math, allocating nothing.
|
|
40
|
+
* `bits` views `segments` as u32 (their layer masks).
|
|
41
|
+
*/
|
|
42
|
+
export declare function writeShadowRow(lx: number, ly: number, radius: number, layers: number, segments: Float32Array, bits: Uint32Array, count: number, out: Float32Array, at: number): void;
|
|
33
43
|
/** How lit a point is by a shadow row: the fragment shader's `shadow2d`, on the CPU. */
|
|
34
44
|
export declare function shadowFactorCpu(row: Float32Array, softness: number, d: number, toX: number, toY: number): number;
|
|
35
45
|
/** One occluder entity's segments, cached in world space until it changes. */
|
|
@@ -117,17 +127,23 @@ export declare class LightView2d {
|
|
|
117
127
|
constructor(name: string);
|
|
118
128
|
ensureLights(n: number): void;
|
|
119
129
|
ensureSegments(n: number): void;
|
|
120
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* Uploads what changed. Segments go up only when the selection or an occluder changed. On
|
|
132
|
+
* baseline the tiles, shadow rows and coarse bins are made here on the CPU (baseline/lights2d.ts).
|
|
133
|
+
*/
|
|
121
134
|
upload(gpu: GpuContext, segmentKey: number): void;
|
|
122
135
|
}
|
|
123
|
-
|
|
136
|
+
/** Loads the baseline tier's CPU lighting (0064), once: the sprite plugin does, on a baseline device. */
|
|
137
|
+
export declare function loadBaselineLighting(): Promise<void>;
|
|
138
|
+
export interface ViewGpu {
|
|
124
139
|
generation: number;
|
|
125
140
|
uniform: GpuBuffer;
|
|
126
|
-
|
|
141
|
+
/** What the fragment stage reads: storage on the full tier, data textures on baseline (0064). */
|
|
142
|
+
lights: DataStore;
|
|
127
143
|
circles: GpuBuffer;
|
|
128
|
-
tiles:
|
|
129
|
-
shadowMap:
|
|
130
|
-
shadowCoarse:
|
|
144
|
+
tiles: DataStore;
|
|
145
|
+
shadowMap: DataStore;
|
|
146
|
+
shadowCoarse: DataStore;
|
|
131
147
|
segments: GpuBuffer;
|
|
132
148
|
shadowed: GpuBuffer;
|
|
133
149
|
computeGroup?: {
|
package/dist/lights2d.js
CHANGED
|
@@ -9,6 +9,8 @@ import { GpuBuffer } from "@aethervtt/shard-gpu";
|
|
|
9
9
|
import {
|
|
10
10
|
Cameras,
|
|
11
11
|
ComputedVisibility,
|
|
12
|
+
DataStore,
|
|
13
|
+
dataEntry,
|
|
12
14
|
defineOverlay,
|
|
13
15
|
Gpu,
|
|
14
16
|
RenderPhase,
|
|
@@ -59,30 +61,50 @@ function lightCircle(viewProj, width, height, x, y, z, r, out, o) {
|
|
|
59
61
|
out[o + 3] = 0;
|
|
60
62
|
return px + rp > 0 && px - rp < width && py + rp > 0 && py - rp < height;
|
|
61
63
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
64
|
+
function binLightsInto(circles, count, tilesX, tilesY, out) {
|
|
65
|
+
for (let t = 0; t < tilesX * tilesY; t++) out[t * TILE_STRIDE] = 0;
|
|
66
|
+
for (let i = 0; i < count; i++) {
|
|
67
|
+
const cx = circles[i * 4];
|
|
68
|
+
const cy = circles[i * 4 + 1];
|
|
69
|
+
const r = circles[i * 4 + 2];
|
|
70
|
+
const x0 = Math.max(0, Math.floor((cx - r) / TILE_PIXELS) - 1);
|
|
71
|
+
const x1 = Math.min(tilesX - 1, Math.floor((cx + r) / TILE_PIXELS));
|
|
72
|
+
const y0 = Math.max(0, Math.floor((cy - r) / TILE_PIXELS) - 1);
|
|
73
|
+
const y1 = Math.min(tilesY - 1, Math.floor((cy + r) / TILE_PIXELS));
|
|
74
|
+
for (let ty = y0; ty <= y1; ty++) {
|
|
75
|
+
const ly = ty * TILE_PIXELS;
|
|
74
76
|
const qy = Math.min(Math.max(cy, ly), ly + TILE_PIXELS) - cy;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
for (let tx = x0; tx <= x1; tx++) {
|
|
78
|
+
const lx = tx * TILE_PIXELS;
|
|
79
|
+
const qx = Math.min(Math.max(cx, lx), lx + TILE_PIXELS) - cx;
|
|
80
|
+
if (qx * qx + qy * qy > r * r) continue;
|
|
81
|
+
const base = (ty * tilesX + tx) * TILE_STRIDE;
|
|
82
|
+
const n = out[base];
|
|
83
|
+
if (n < TILE_MAX) out[base + 1 + n] = i;
|
|
84
|
+
out[base] = n + 1;
|
|
85
|
+
}
|
|
78
86
|
}
|
|
79
|
-
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function binLightsCpu(circles, count, tilesX, tilesY) {
|
|
90
|
+
const tiles = tilesX * tilesY;
|
|
91
|
+
const packed = new Uint32Array(tiles * TILE_STRIDE);
|
|
92
|
+
binLightsInto(circles, count, tilesX, tilesY, packed);
|
|
93
|
+
const counts = new Uint32Array(tiles);
|
|
94
|
+
const indices = new Uint32Array(tiles * TILE_MAX);
|
|
95
|
+
for (let t = 0; t < tiles; t++) {
|
|
96
|
+
counts[t] = packed[t * TILE_STRIDE];
|
|
97
|
+
indices.set(packed.subarray(t * TILE_STRIDE + 1, (t + 1) * TILE_STRIDE), t * TILE_MAX);
|
|
80
98
|
}
|
|
81
99
|
return { counts, indices };
|
|
82
100
|
}
|
|
83
101
|
function shadowRowCpu(lx, ly, radius, layers, segments, count, out) {
|
|
84
|
-
out.fill(FAR, 0, SHADOW_RES);
|
|
85
102
|
const bits = new Uint32Array(segments.buffer, segments.byteOffset, segments.length);
|
|
103
|
+
writeShadowRow(lx, ly, radius, layers, segments, bits, count, out, 0);
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
function writeShadowRow(lx, ly, radius, layers, segments, bits, count, out, at) {
|
|
107
|
+
out.fill(FAR, at, at + SHADOW_RES);
|
|
86
108
|
const step = Math.PI * 2 / SHADOW_RES;
|
|
87
109
|
for (let s = 0; s < count; s++) {
|
|
88
110
|
const o = s * SEGMENT_FLOATS;
|
|
@@ -118,12 +140,11 @@ function shadowRowCpu(lx, ly, radius, layers, segments, count, out) {
|
|
|
118
140
|
if (Math.abs(den) < 1e-12) continue;
|
|
119
141
|
const t = (ax * ey - ay * ex) / den;
|
|
120
142
|
if (t <= 0) continue;
|
|
121
|
-
const w = (k % SHADOW_RES + SHADOW_RES) % SHADOW_RES;
|
|
143
|
+
const w = at + (k % SHADOW_RES + SHADOW_RES) % SHADOW_RES;
|
|
122
144
|
const v = Math.fround(t + pen);
|
|
123
145
|
if (v < out[w]) out[w] = v;
|
|
124
146
|
}
|
|
125
147
|
}
|
|
126
|
-
return out;
|
|
127
148
|
}
|
|
128
149
|
function shadowFactorCpu(row, softness, d, toX, toY) {
|
|
129
150
|
const step = Math.PI * 2 / SHADOW_RES;
|
|
@@ -441,7 +462,10 @@ class LightView2d {
|
|
|
441
462
|
this.segments = s;
|
|
442
463
|
this.segmentBits = new Uint32Array(s.buffer);
|
|
443
464
|
}
|
|
444
|
-
/**
|
|
465
|
+
/**
|
|
466
|
+
* Uploads what changed. Segments go up only when the selection or an occluder changed. On
|
|
467
|
+
* baseline the tiles, shadow rows and coarse bins are made here on the CPU (baseline/lights2d.ts).
|
|
468
|
+
*/
|
|
445
469
|
upload(gpu, segmentKey) {
|
|
446
470
|
let g = this.gpu;
|
|
447
471
|
if (!g || g.generation !== gpu.generation) g = this.gpu = createViewGpu(gpu, this.name);
|
|
@@ -469,21 +493,28 @@ class LightView2d {
|
|
|
469
493
|
g.segments.write(this.segments, 0, 0, this.segmentCount * SEGMENT_FLOATS);
|
|
470
494
|
bytes += this.segmentCount * SEGMENT_FLOATS * 4;
|
|
471
495
|
}
|
|
496
|
+
if (baseline && gpu.tier === "baseline")
|
|
497
|
+
bytes += baseline.lightOnCpu(this, g, segmentKey !== this.segmentKey);
|
|
472
498
|
this.segmentKey = segmentKey;
|
|
473
499
|
this.uploadedBytes = bytes;
|
|
474
500
|
}
|
|
475
501
|
}
|
|
502
|
+
let baseline;
|
|
503
|
+
async function loadBaselineLighting() {
|
|
504
|
+
baseline ??= await import("./baseline/lights2d.js");
|
|
505
|
+
}
|
|
476
506
|
function createViewGpu(gpu, name) {
|
|
477
507
|
const storage = GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC;
|
|
478
508
|
const buffer = (label, size, usage = storage) => new GpuBuffer(gpu, { label: `${name}/lights2d/${label}`, usage, size });
|
|
509
|
+
const data = (label, size) => new DataStore(gpu, { label: `${name}/lights2d/${label}`, usage: GPUBufferUsage.COPY_SRC, size });
|
|
479
510
|
return {
|
|
480
511
|
generation: gpu.generation,
|
|
481
512
|
uniform: buffer("view", 48, GPUBufferUsage.UNIFORM),
|
|
482
|
-
lights:
|
|
513
|
+
lights: data("lights", LIGHT2D_FLOATS * 4 * 64),
|
|
483
514
|
circles: buffer("circles", 16 * 64),
|
|
484
|
-
tiles:
|
|
485
|
-
shadowMap:
|
|
486
|
-
shadowCoarse:
|
|
515
|
+
tiles: data("tiles", TILE_STRIDE * 4 * 1024),
|
|
516
|
+
shadowMap: data("shadow-map", SHADOW_RES * 4 * 4),
|
|
517
|
+
shadowCoarse: data("shadow-coarse", COARSE_RES * 8 * 4),
|
|
487
518
|
segments: buffer("segments", SEGMENT_FLOATS * 4 * 256),
|
|
488
519
|
shadowed: buffer("shadowed", MAX_SHADOWED * 4)
|
|
489
520
|
};
|
|
@@ -825,10 +856,10 @@ function layoutsOf(gpu) {
|
|
|
825
856
|
label: "lights2d/shade",
|
|
826
857
|
entries: [
|
|
827
858
|
{ binding: 0, visibility: F, buffer: { type: "uniform" } },
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
859
|
+
dataEntry(gpu, 1, F),
|
|
860
|
+
dataEntry(gpu, 2, F),
|
|
861
|
+
dataEntry(gpu, 3, F),
|
|
862
|
+
dataEntry(gpu, 4, F)
|
|
832
863
|
]
|
|
833
864
|
}),
|
|
834
865
|
pipelines: /* @__PURE__ */ new Map()
|
|
@@ -855,7 +886,7 @@ function litView(world, view) {
|
|
|
855
886
|
}
|
|
856
887
|
function lights2dGroup(gpu, view) {
|
|
857
888
|
const g = view.gpu;
|
|
858
|
-
const key = `${idOf(g.uniform.buffer)}/${
|
|
889
|
+
const key = `${idOf(g.uniform.buffer)}/${dataKey(g.lights)}/${dataKey(g.tiles)}/${dataKey(g.shadowMap)}/${dataKey(g.shadowCoarse)}`;
|
|
859
890
|
if (!g.renderGroup || g.renderGroup.key !== key) {
|
|
860
891
|
g.renderGroup = {
|
|
861
892
|
key,
|
|
@@ -864,16 +895,19 @@ function lights2dGroup(gpu, view) {
|
|
|
864
895
|
layout: layoutsOf(gpu).render,
|
|
865
896
|
entries: [
|
|
866
897
|
{ binding: 0, resource: { buffer: g.uniform.buffer } },
|
|
867
|
-
{ binding: 1, resource:
|
|
868
|
-
{ binding: 2, resource:
|
|
869
|
-
{ binding: 3, resource:
|
|
870
|
-
{ binding: 4, resource:
|
|
898
|
+
{ binding: 1, resource: g.lights.resource() },
|
|
899
|
+
{ binding: 2, resource: g.tiles.resource() },
|
|
900
|
+
{ binding: 3, resource: g.shadowMap.resource() },
|
|
901
|
+
{ binding: 4, resource: g.shadowCoarse.resource() }
|
|
871
902
|
]
|
|
872
903
|
})
|
|
873
904
|
};
|
|
874
905
|
}
|
|
875
906
|
return g.renderGroup.group;
|
|
876
907
|
}
|
|
908
|
+
function dataKey(d) {
|
|
909
|
+
return `${idOf(d)}:${d.version}`;
|
|
910
|
+
}
|
|
877
911
|
function computePipeline(ctx, entry) {
|
|
878
912
|
const gpu = ctx.gpu;
|
|
879
913
|
const s = layoutsOf(gpu);
|
|
@@ -896,7 +930,8 @@ function lights2dNode(world) {
|
|
|
896
930
|
return {
|
|
897
931
|
kind: "compute",
|
|
898
932
|
phase: RenderPhase.Sprites - 10,
|
|
899
|
-
|
|
933
|
+
// Compute is the full tier's: baseline lights on the CPU as it uploads (LightView2d.upload).
|
|
934
|
+
enabled: (view) => litView(world, view.name) !== void 0 && world.resource(Gpu).tier === "full",
|
|
900
935
|
writes: ["lights2d"],
|
|
901
936
|
run: (ctx) => {
|
|
902
937
|
const view = litView(ctx.world, ctx.view.name);
|
|
@@ -907,7 +942,7 @@ function lights2dNode(world) {
|
|
|
907
942
|
const coarse = computePipeline(ctx, "coarse");
|
|
908
943
|
if (!bin || !clear || !shadows || !coarse) return;
|
|
909
944
|
const g = view.gpu;
|
|
910
|
-
const key = `${idOf(g.uniform.buffer)}/${idOf(g.lights.
|
|
945
|
+
const key = `${idOf(g.uniform.buffer)}/${idOf(g.lights.gpuBuffer)}/${idOf(g.circles.buffer)}/${idOf(g.tiles.gpuBuffer)}/${idOf(g.shadowMap.gpuBuffer)}/${idOf(g.segments.buffer)}/${idOf(g.shadowed.buffer)}/${idOf(g.shadowCoarse.gpuBuffer)}`;
|
|
911
946
|
if (!g.computeGroup || g.computeGroup.key !== key) {
|
|
912
947
|
g.computeGroup = {
|
|
913
948
|
key,
|
|
@@ -916,13 +951,13 @@ function lights2dNode(world) {
|
|
|
916
951
|
layout: layoutsOf(ctx.gpu).compute,
|
|
917
952
|
entries: [
|
|
918
953
|
{ binding: 0, resource: { buffer: g.uniform.buffer } },
|
|
919
|
-
{ binding: 1, resource: { buffer: g.lights.
|
|
954
|
+
{ binding: 1, resource: { buffer: g.lights.gpuBuffer } },
|
|
920
955
|
{ binding: 2, resource: { buffer: g.circles.buffer } },
|
|
921
|
-
{ binding: 3, resource: { buffer: g.tiles.
|
|
922
|
-
{ binding: 4, resource: { buffer: g.shadowMap.
|
|
956
|
+
{ binding: 3, resource: { buffer: g.tiles.gpuBuffer } },
|
|
957
|
+
{ binding: 4, resource: { buffer: g.shadowMap.gpuBuffer } },
|
|
923
958
|
{ binding: 5, resource: { buffer: g.segments.buffer } },
|
|
924
959
|
{ binding: 6, resource: { buffer: g.shadowed.buffer } },
|
|
925
|
-
{ binding: 7, resource: { buffer: g.shadowCoarse.
|
|
960
|
+
{ binding: 7, resource: { buffer: g.shadowCoarse.gpuBuffer } }
|
|
926
961
|
]
|
|
927
962
|
})
|
|
928
963
|
};
|
|
@@ -1080,6 +1115,7 @@ export {
|
|
|
1080
1115
|
TILE_PIXELS,
|
|
1081
1116
|
TILE_STRIDE,
|
|
1082
1117
|
binLightsCpu,
|
|
1118
|
+
binLightsInto,
|
|
1083
1119
|
describeLights2d,
|
|
1084
1120
|
lightCircle,
|
|
1085
1121
|
lights2dGroup,
|
|
@@ -1087,7 +1123,9 @@ export {
|
|
|
1087
1123
|
lights2dNode,
|
|
1088
1124
|
lights2dOverlay,
|
|
1089
1125
|
litView,
|
|
1126
|
+
loadBaselineLighting,
|
|
1090
1127
|
prepareLights2d,
|
|
1091
1128
|
shadowFactorCpu,
|
|
1092
|
-
shadowRowCpu
|
|
1129
|
+
shadowRowCpu,
|
|
1130
|
+
writeShadowRow
|
|
1093
1131
|
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type World } from '@aethervtt/shard-core';
|
|
2
|
+
import type { AppMethod } from '@aethervtt/shard-runtime';
|
|
3
|
+
import { type TextureAtlas } from './atlas.js';
|
|
4
|
+
import { TilemapData } from './tilemap.js';
|
|
5
|
+
export interface TileRect {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
w: number;
|
|
9
|
+
h: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ReadTilesOptions {
|
|
12
|
+
/** Layer name (default: the first). */
|
|
13
|
+
layer?: string;
|
|
14
|
+
/** Just this chunk ([cx, cy], in the data's chunkSize). */
|
|
15
|
+
chunk?: readonly number[];
|
|
16
|
+
/** Just this rect. */
|
|
17
|
+
rect?: TileRect;
|
|
18
|
+
}
|
|
19
|
+
export interface EditTilesOptions {
|
|
20
|
+
layer?: string;
|
|
21
|
+
/** Cells to set: tile by palette name ('.' clears), flags as "fx+r90". */
|
|
22
|
+
cells?: readonly {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
tile: string;
|
|
26
|
+
flags?: string | number;
|
|
27
|
+
}[];
|
|
28
|
+
/** A rect filled with one tile. */
|
|
29
|
+
fill?: {
|
|
30
|
+
rect: TileRect;
|
|
31
|
+
tile: string;
|
|
32
|
+
};
|
|
33
|
+
/** One chunk's cells replaced by rows, as readTiles gives them. */
|
|
34
|
+
rows?: {
|
|
35
|
+
chunk: readonly number[];
|
|
36
|
+
rows: readonly string[];
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A layer's cells as rows: all non-empty chunks (keyed "cx,cy"), one chunk, or a rect. Version 1
|
|
41
|
+
* maps name their tiles through the atlas.
|
|
42
|
+
*/
|
|
43
|
+
export declare function readTiles(data: TilemapData, options?: ReadTilesOptions, atlas?: {
|
|
44
|
+
names: readonly string[];
|
|
45
|
+
}): {
|
|
46
|
+
layer: string;
|
|
47
|
+
width: number;
|
|
48
|
+
height: number;
|
|
49
|
+
chunkSize: number;
|
|
50
|
+
encoding: import("./tilemap-format.js").TileEncoding;
|
|
51
|
+
rect: TileRect;
|
|
52
|
+
rows: string[];
|
|
53
|
+
} | {
|
|
54
|
+
layer: string;
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
chunkSize: number;
|
|
58
|
+
encoding: import("./tilemap-format.js").TileEncoding;
|
|
59
|
+
chunk: number[];
|
|
60
|
+
rows: string[];
|
|
61
|
+
} | {
|
|
62
|
+
layer: string;
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
chunkSize: number;
|
|
66
|
+
encoding: import("./tilemap-format.js").TileEncoding;
|
|
67
|
+
chunks: Record<string, string[]>;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Applies cell edits through TileLayer.set, so renderers re-upload only the touched chunks.
|
|
71
|
+
* Returns how many cells changed. A version 1 map first gets a palette of the atlas's names.
|
|
72
|
+
*/
|
|
73
|
+
export declare function editTiles(data: TilemapData, options: EditTilesOptions, atlas?: {
|
|
74
|
+
names: readonly string[];
|
|
75
|
+
}): number;
|
|
76
|
+
/** A loaded tilemap data asset, and the atlas of a Tilemap drawing it (for version 1 names). */
|
|
77
|
+
export declare function findTilemapData(world: World, asset: string): {
|
|
78
|
+
data: TilemapData;
|
|
79
|
+
guid: string | undefined;
|
|
80
|
+
atlas: TextureAtlas | undefined;
|
|
81
|
+
};
|
|
82
|
+
export declare const tilemapMethods: AppMethod[];
|