@woosh/meep-engine 2.166.0 → 2.167.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/package.json +1 -1
- package/src/engine/ecs/fow/FogOfWar.d.ts +13 -0
- package/src/engine/ecs/fow/FogOfWar.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWar.js +69 -0
- package/src/engine/ecs/fow/FogOfWarEdgeMode.d.ts +16 -0
- package/src/engine/ecs/fow/FogOfWarEdgeMode.d.ts.map +1 -0
- package/src/engine/ecs/fow/FogOfWarEdgeMode.js +39 -0
- package/src/engine/ecs/fow/FogOfWarSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarSystem.js +1 -0
- package/src/engine/ecs/fow/shader/FogOfWarRenderer.d.ts +7 -0
- package/src/engine/ecs/fow/shader/FogOfWarRenderer.d.ts.map +1 -1
- package/src/engine/ecs/fow/shader/FogOfWarRenderer.js +155 -145
- package/src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.d.ts.map +1 -1
- package/src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js +33 -8
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"description": "Pure JavaScript game engine. Fully featured and production ready.",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"author": "Alexander Goldring",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.167.0",
|
|
10
10
|
"main": "build/meep.module.js",
|
|
11
11
|
"module": "build/meep.module.js",
|
|
12
12
|
"exports": {
|
|
@@ -28,6 +28,18 @@ export class FogOfWar {
|
|
|
28
28
|
*/
|
|
29
29
|
size: Vector2;
|
|
30
30
|
color: Vector4;
|
|
31
|
+
/**
|
|
32
|
+
* What the fog does outside the fogged area — see {@link FogOfWarEdgeMode}.
|
|
33
|
+
*
|
|
34
|
+
* NOT serialized: it is configuration the owner sets when it builds the
|
|
35
|
+
* component, not accumulated reveal state, and adding it to the binary format
|
|
36
|
+
* would cost a version bump plus an updater for every existing save. A
|
|
37
|
+
* deserialized FogOfWar therefore comes back as Conceal; a consumer that
|
|
38
|
+
* wants Extend sets it after loading, in the same place it sets {@link color}.
|
|
39
|
+
*
|
|
40
|
+
* @type {FogOfWarEdgeMode}
|
|
41
|
+
*/
|
|
42
|
+
edge_mode: FogOfWarEdgeMode;
|
|
31
43
|
/**
|
|
32
44
|
* Contains indices of pixes that are currently being updated
|
|
33
45
|
* @type {RowFirstTable}
|
|
@@ -134,6 +146,7 @@ import { DataTexture } from "three";
|
|
|
134
146
|
import Vector1 from "../../../core/geom/Vector1.js";
|
|
135
147
|
import Vector2 from "../../../core/geom/Vector2.js";
|
|
136
148
|
import Vector4 from "../../../core/geom/Vector4.js";
|
|
149
|
+
import { FogOfWarEdgeMode } from "./FogOfWarEdgeMode.js";
|
|
137
150
|
import { RowFirstTable } from "../../../core/collection/table/RowFirstTable.js";
|
|
138
151
|
import Signal from "../../../core/events/signal/Signal.js";
|
|
139
152
|
import { Sampler2D } from "../../graphics/texture/sampler/Sampler2D.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FogOfWar.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWar.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FogOfWar.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWar.js"],"names":[],"mappings":"AAgDA;;GAEG;AACH;IAEI;;;OAGG;IACH,oBAFU,OAAO,CAES;IAC1B;;;OAGG;IACH,0BAFU,OAAO,CAEgB;IAEjC;;;OAGG;IACH,SAFU,WAAW,GAAC,IAAI,CAEX;IAEf;;;OAGG;IACH,OAFU,OAAO,CAEM;IAEvB;;;OAGG;IACH,MAFU,OAAO,CAEQ;IAEzB,eAAsC;IAEtC;;;;;;;;;;OAUG;IACH,WAFU,gBAAgB,CAEW;IAErC;;;OAGG;IACH,UAFU,aAAa,CAE2B;IAWlD;;MAEE;IAEF;;;OAGG;IACH,SAFU,SAAS,CAEgB;IAEnC;;;OAGG;IACH,iBAFU,SAAS,CAEwB;IAE3C;;;OAGG;IACH,sBAFW,MAAM,QA6EhB;IA4DD;;;;OAIG;IACH,eAHW,MAAM,KACN,MAAM,QAUhB;IAED,cAQC;IAED,kBAQC;IAED,mBAEC;IAED;;;;;OAKG;IACH,UAJW,MAAM,KACN,MAAM,UACN,MAAM,QAmChB;IAED;;;;;OAKG;IACH,WAJW,MAAM,KACN,MAAM,UACN,MAAM,QAmChB;IAED;;;;;;OAMG;IACH,yBAcC;IAED,gBAIC;IAmDD,sBA2CC;IAED;;;OAGG;IACH,kBAFW,MAAM,QAYhB;IAED;;;OAGG;IACH,0DA+BC;IAED;;;;;OAKG;IACH,yBAJW,MAAM,KACN,MAAM,UACN,MAAM,EAAE,QAelB;IAED;;;;;OAKG;IACH,yEAFW,MAAM,WA+EhB;IAED;;;;;OAKG;IACH,qBAJW,MAAM,KACN,MAAM,GACJ,MAAM,CASlB;IAED,6BAKC;IAED,+BASC;IAED,uBAyBC;IAGD;;;;;OAKG;IACH,UAJW,MAAM,KACN,MAAM,oBAahB;;CACJ;;;;4BA5uB2F,OAAO;oBAM/E,+BAA+B;oBAC/B,+BAA+B;oBAC/B,+BAA+B;iCAOlB,uBAAuB;8BAZ1B,iDAAiD;mBAE5D,uCAAuC;0BAQhC,6CAA6C"}
|
|
@@ -13,6 +13,7 @@ import { min2 } from "../../../core/math/min2.js";
|
|
|
13
13
|
import { computeUnsignedDistanceField } from "../../graphics/texture/sampler/distance/computeUnsignedDistanceField.js";
|
|
14
14
|
import { Sampler2D } from "../../graphics/texture/sampler/Sampler2D.js";
|
|
15
15
|
import { writeSample2DDataToDataTexture } from "../../graphics/texture/sampler/writeSampler2DDataToDataTexture.js";
|
|
16
|
+
import { FogOfWarEdgeMode } from "./FogOfWarEdgeMode.js";
|
|
16
17
|
|
|
17
18
|
const samplePosition = [];
|
|
18
19
|
|
|
@@ -81,6 +82,19 @@ export class FogOfWar {
|
|
|
81
82
|
|
|
82
83
|
color = new Vector4(0.1, 0.1, 0.1, 1);
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* What the fog does outside the fogged area — see {@link FogOfWarEdgeMode}.
|
|
87
|
+
*
|
|
88
|
+
* NOT serialized: it is configuration the owner sets when it builds the
|
|
89
|
+
* component, not accumulated reveal state, and adding it to the binary format
|
|
90
|
+
* would cost a version bump plus an updater for every existing save. A
|
|
91
|
+
* deserialized FogOfWar therefore comes back as Conceal; a consumer that
|
|
92
|
+
* wants Extend sets it after loading, in the same place it sets {@link color}.
|
|
93
|
+
*
|
|
94
|
+
* @type {FogOfWarEdgeMode}
|
|
95
|
+
*/
|
|
96
|
+
edge_mode = FogOfWarEdgeMode.Conceal;
|
|
97
|
+
|
|
84
98
|
/**
|
|
85
99
|
* Contains indices of pixes that are currently being updated
|
|
86
100
|
* @type {RowFirstTable}
|
|
@@ -401,7 +415,62 @@ export class FogOfWar {
|
|
|
401
415
|
}
|
|
402
416
|
}
|
|
403
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Copy the sampler's outermost REAL cells out into the 1-cell ring that
|
|
420
|
+
* {@link rebuildSampler} pads it with.
|
|
421
|
+
*
|
|
422
|
+
* That ring is padding — it exists so the shader's 5x5 blur and the distance
|
|
423
|
+
* field have something to read past the edge — and it is filled with full
|
|
424
|
+
* concealment. The blur then drags that concealment ~2 cells back INTO the
|
|
425
|
+
* fogged area, so the outermost cells of the world can never read as fully
|
|
426
|
+
* revealed however thoroughly they are explored; and every lookup from outside
|
|
427
|
+
* the area clamps onto it, which is what paints the void. Mirroring makes the
|
|
428
|
+
* padding agree with the edge it pads, on both counts.
|
|
429
|
+
*
|
|
430
|
+
* O(perimeter), and only on frames that upload a new texture.
|
|
431
|
+
*
|
|
432
|
+
* @private
|
|
433
|
+
*/
|
|
434
|
+
#mirrorBorderRing() {
|
|
435
|
+
const s = this.sampler;
|
|
436
|
+
|
|
437
|
+
const w = s.width;
|
|
438
|
+
const h = s.height;
|
|
439
|
+
|
|
440
|
+
if (w < 3 || h < 3) {
|
|
441
|
+
// no interior to mirror from
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const data = s.data;
|
|
446
|
+
|
|
447
|
+
const last_row = (h - 1) * w;
|
|
448
|
+
const second_last_row = (h - 2) * w;
|
|
449
|
+
|
|
450
|
+
// top and bottom edges, corners excluded — those come from the sides below
|
|
451
|
+
for (let x = 1; x < w - 1; x++) {
|
|
452
|
+
data[x] = data[w + x];
|
|
453
|
+
data[last_row + x] = data[second_last_row + x];
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// left and right edges, corners INCLUDED: the row above and below have
|
|
457
|
+
// already been mirrored, so a corner ends up with its diagonal neighbour's
|
|
458
|
+
// value rather than an arbitrary one
|
|
459
|
+
for (let y = 0; y < h; y++) {
|
|
460
|
+
const row = y * w;
|
|
461
|
+
|
|
462
|
+
data[row] = data[row + 1];
|
|
463
|
+
data[row + w - 1] = data[row + w - 2];
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
404
467
|
updateTexture() {
|
|
468
|
+
if (this.edge_mode === FogOfWarEdgeMode.Extend) {
|
|
469
|
+
// do this HERE, at the single point every reveal path funnels through
|
|
470
|
+
// on its way to the GPU, rather than in each of them
|
|
471
|
+
this.#mirrorBorderRing();
|
|
472
|
+
}
|
|
473
|
+
|
|
405
474
|
const s = this.sampler;
|
|
406
475
|
|
|
407
476
|
/**
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the fog of war does AT, and beyond, the edge of the fogged area.
|
|
3
|
+
*
|
|
4
|
+
* The fog is a screen-space effect (see screenSpaceFogOfWarShader): per pixel it
|
|
5
|
+
* reconstructs a world position from the depth buffer and reads that position out
|
|
6
|
+
* of the fog sampler. Neither end of that pipeline is defined outside the fogged
|
|
7
|
+
* rectangle — a pixel with no geometry has no world position to reconstruct, and
|
|
8
|
+
* a world position outside the sampler has no cell to read — so what happens
|
|
9
|
+
* there is a POLICY rather than a fact, and this is the knob for it.
|
|
10
|
+
*/
|
|
11
|
+
export type FogOfWarEdgeMode = number;
|
|
12
|
+
export namespace FogOfWarEdgeMode {
|
|
13
|
+
let Conceal: number;
|
|
14
|
+
let Extend: number;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=FogOfWarEdgeMode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FogOfWarEdgeMode.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWarEdgeMode.js"],"names":[],"mappings":";;;;;;;;;;+BAUU,MAAM"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the fog of war does AT, and beyond, the edge of the fogged area.
|
|
3
|
+
*
|
|
4
|
+
* The fog is a screen-space effect (see screenSpaceFogOfWarShader): per pixel it
|
|
5
|
+
* reconstructs a world position from the depth buffer and reads that position out
|
|
6
|
+
* of the fog sampler. Neither end of that pipeline is defined outside the fogged
|
|
7
|
+
* rectangle — a pixel with no geometry has no world position to reconstruct, and
|
|
8
|
+
* a world position outside the sampler has no cell to read — so what happens
|
|
9
|
+
* there is a POLICY rather than a fact, and this is the knob for it.
|
|
10
|
+
*
|
|
11
|
+
* @enum {number}
|
|
12
|
+
*/
|
|
13
|
+
export const FogOfWarEdgeMode = {
|
|
14
|
+
/**
|
|
15
|
+
* Everything outside the fogged area is CONCEALED: the sampler's border ring
|
|
16
|
+
* is left at full concealment, and a pixel with no geometry takes whatever
|
|
17
|
+
* the clamped lookup returns — which is that same border.
|
|
18
|
+
*
|
|
19
|
+
* The default, and the behaviour of every release before this option existed.
|
|
20
|
+
* Right for a world that fills the screen, where "outside" is somewhere the
|
|
21
|
+
* player is never shown.
|
|
22
|
+
*/
|
|
23
|
+
Conceal: 0,
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The fog EXTENDS outward instead: the border ring mirrors the cells just
|
|
27
|
+
* inside it, and pixels with no geometry are left alone entirely.
|
|
28
|
+
*
|
|
29
|
+
* For a world with a VISIBLE boundary — a finite arena the camera can see
|
|
30
|
+
* past. Under {@link FogOfWarEdgeMode.Conceal} such a scene gets a hard black
|
|
31
|
+
* rectangle drawn exactly on its boundary (the concealed border ring, clamped
|
|
32
|
+
* across the whole void), which both advertises where the map stops and paints
|
|
33
|
+
* over anything drawn earlier to hide it — this layer composites last, over
|
|
34
|
+
* every other effect. Under Extend the fog agrees with the ground it borders:
|
|
35
|
+
* explored terrain stays clear right out past the boundary, unexplored terrain
|
|
36
|
+
* stays dark, and nothing marks where the geometry ends.
|
|
37
|
+
*/
|
|
38
|
+
Extend: 1,
|
|
39
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FogOfWarSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWarSystem.js"],"names":[],"mappings":"AAwBA;IACI;;;OAGG;IACH,sCA0BC;IAvBG;;;OAGG;IACH,yBAAwB;IAExB,mDAAuC;IAEvC,gEAEC;IAED,mCAAoD;IAIpD;;;OAGG;IACH,eAFU,aAAa,CAEE;IAEzB,uBAAuB;IAG3B;;;OAGG;IACH,yBAFa,gBAAgB,CAuE5B;IAsBG,kBAAgC;
|
|
1
|
+
{"version":3,"file":"FogOfWarSystem.d.ts","sourceRoot":"","sources":["../../../../../src/engine/ecs/fow/FogOfWarSystem.js"],"names":[],"mappings":"AAwBA;IACI;;;OAGG;IACH,sCA0BC;IAvBG;;;OAGG;IACH,yBAAwB;IAExB,mDAAuC;IAEvC,gEAEC;IAED,mCAAoD;IAIpD;;;OAGG;IACH,eAFU,aAAa,CAEE;IAEzB,uBAAuB;IAG3B;;;OAGG;IACH,yBAFa,gBAAgB,CAuE5B;IAsBG,kBAAgC;IAgEpC,4CAEC;IAED;;;;;OAKG;IACH,UAJW,QAAQ,WACR,OAAO,UACP,MAAM,QAmBhB;IAED,kDAQC;IAED,6BAUC;CACJ;uBA1PsB,cAAc;oBACjB,2BAA2B;yBACtB,eAAe;4CAdI,oDAAoD;iCAS/D,sDAAsD;8BAPzD,0CAA0C"}
|
|
@@ -178,6 +178,7 @@ export class FogOfWarSystem extends System {
|
|
|
178
178
|
fogOfWarRenderer.setResolution(fow.size.x, fow.size.y);
|
|
179
179
|
fogOfWarRenderer.setUvTransformFromFog(fow);
|
|
180
180
|
fogOfWarRenderer.setFogColor(fow.color);
|
|
181
|
+
fogOfWarRenderer.setEdgeMode(fow.edge_mode);
|
|
181
182
|
|
|
182
183
|
|
|
183
184
|
//stop traversal
|
|
@@ -47,6 +47,12 @@ export class FogOfWarRenderer {
|
|
|
47
47
|
* @param {Vector4} color
|
|
48
48
|
*/
|
|
49
49
|
setFogColor(color: Vector4): void;
|
|
50
|
+
/**
|
|
51
|
+
* What the fog does outside the fogged area. See {@link FogOfWarEdgeMode}.
|
|
52
|
+
*
|
|
53
|
+
* @param {FogOfWarEdgeMode} mode
|
|
54
|
+
*/
|
|
55
|
+
setEdgeMode(mode: FogOfWarEdgeMode): void;
|
|
50
56
|
/**
|
|
51
57
|
*
|
|
52
58
|
* @param {WebGLRenderer} renderer
|
|
@@ -59,4 +65,5 @@ export class FogOfWarRenderer {
|
|
|
59
65
|
import { Mesh } from "three";
|
|
60
66
|
import { OrthographicCamera } from "three";
|
|
61
67
|
import { Scene } from "three";
|
|
68
|
+
import { FogOfWarEdgeMode } from "../FogOfWarEdgeMode.js";
|
|
62
69
|
//# sourceMappingURL=FogOfWarRenderer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FogOfWarRenderer.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/shader/FogOfWarRenderer.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FogOfWarRenderer.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/shader/FogOfWarRenderer.js"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,+EAFoB,MAAM,QAAE,MAAM,QAAE,MAAM,QAAE,MAAM,gBAiBjD;AAED;IAEQ;;;OAGG;IACH,yBAAgD;IAEhD,2DAAkE;IAElE,2BAAwD;IACxD,aAAwB;IAI5B;;;OAGG;IACH,2CAKC;IAED;;;;;;OAMG;IACH,wBALW,MAAM,WACN,MAAM,UACN,MAAM,UACN,MAAM,QAqBhB;IAED;;;OAGG;IACH,uCAIC;IAED;;;OAGG;IACH,oCAGC;IAED;;;;OAIG;IACH,iBAHW,MAAM,KACN,MAAM,QAIhB;IAED;;;OAGG;IACH,kCAEC;IAED;;;;OAIG;IACH,kBAFW,gBAAgB,QAI1B;IAED;;;;;;OAMG;IACH,wCAJW,oBAAkB,kBAAkB,SACpC,KAAK,mCAqBf;CACJ;qBA1J+C,OAAO;mCAAP,OAAO;sBAAP,OAAO;iCAItB,wBAAwB"}
|
|
@@ -1,145 +1,155 @@
|
|
|
1
|
-
import { Mesh, OrthographicCamera, Scene } from "three";
|
|
2
|
-
import { assert } from "../../../../core/assert.js";
|
|
3
|
-
import { isValueBetweenInclusive } from "../../../../core/math/interval/isValueBetweenInclusive.js";
|
|
4
|
-
import { FULL_SCREEN_TRIANGLE_GEOMETRY } from "../../../graphics/geometry/FULL_SCREEN_TRIANGLE_GEOMETRY.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
* @param {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.
|
|
41
|
-
this.scene
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
*
|
|
58
|
-
* @param {number}
|
|
59
|
-
* @param {number}
|
|
60
|
-
* @param {number}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
assert.
|
|
65
|
-
assert.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
assert.
|
|
69
|
-
assert.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
assert.
|
|
74
|
-
assert.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
assert.
|
|
78
|
-
assert.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
*
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
*
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
*
|
|
105
|
-
* @param {number}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
*
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
* @
|
|
122
|
-
*
|
|
123
|
-
* @param {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
1
|
+
import { Mesh, OrthographicCamera, Scene } from "three";
|
|
2
|
+
import { assert } from "../../../../core/assert.js";
|
|
3
|
+
import { isValueBetweenInclusive } from "../../../../core/math/interval/isValueBetweenInclusive.js";
|
|
4
|
+
import { FULL_SCREEN_TRIANGLE_GEOMETRY } from "../../../graphics/geometry/FULL_SCREEN_TRIANGLE_GEOMETRY.js";
|
|
5
|
+
import { FogOfWarEdgeMode } from "../FogOfWarEdgeMode.js";
|
|
6
|
+
import { buildScreenSpaceFogOfWarShader } from "./screenSpaceFogOfWarShader.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param {FogOfWar} fow
|
|
11
|
+
* @param {function(number, number, number, number)} callback
|
|
12
|
+
*/
|
|
13
|
+
export function computeUvTransformFromFogOfWar(fow, callback) {
|
|
14
|
+
|
|
15
|
+
const sampler = fow.sampler;
|
|
16
|
+
|
|
17
|
+
const samplerWidth = sampler.width;
|
|
18
|
+
const samplerHeight = sampler.height;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const scaleX = (fow.scale * fow.size.x) * (samplerWidth / (samplerWidth - 3));
|
|
22
|
+
const scaleY = (fow.scale * fow.size.y) * (samplerHeight / (samplerHeight - 3));
|
|
23
|
+
|
|
24
|
+
const offsetX = 1.5 / samplerWidth;
|
|
25
|
+
const offsetY = 1.5 / samplerHeight;
|
|
26
|
+
|
|
27
|
+
callback(offsetX, offsetY, 1 / scaleX, 1 / scaleY);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class FogOfWarRenderer {
|
|
31
|
+
constructor() {
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @type {ShaderMaterial}
|
|
35
|
+
*/
|
|
36
|
+
this.material = buildScreenSpaceFogOfWarShader();
|
|
37
|
+
|
|
38
|
+
this.quad = new Mesh(FULL_SCREEN_TRIANGLE_GEOMETRY, this.material);
|
|
39
|
+
|
|
40
|
+
this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
|
|
41
|
+
this.scene = new Scene();
|
|
42
|
+
this.scene.add(this.quad);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param {FogOfWar} fow
|
|
48
|
+
*/
|
|
49
|
+
setUvTransformFromFog(fow) {
|
|
50
|
+
computeUvTransformFromFogOfWar(fow, (offsetX, offsetY, scaleX, scaleY) => {
|
|
51
|
+
|
|
52
|
+
this.setUvTransform(offsetX, offsetY, scaleX, scaleY);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
*
|
|
58
|
+
* @param {number} offsetX UV u offset, between 0 and 1
|
|
59
|
+
* @param {number} offsetY UV v offset, between 0 and 1
|
|
60
|
+
* @param {number} scaleX Transform scale from world coordinate position to UV u coordinate, minus offsetX
|
|
61
|
+
* @param {number} scaleY Transform scale from world coordinate position to UV v coordinate, minus offsetY
|
|
62
|
+
*/
|
|
63
|
+
setUvTransform(offsetX, offsetY, scaleX, scaleY) {
|
|
64
|
+
assert.isNumber(offsetX, 'offsetX');
|
|
65
|
+
assert.notNaN(offsetX, 'offsetX');
|
|
66
|
+
assert.ok(isValueBetweenInclusive(offsetX, 0, 1), `expected offsetX to be between 0 and 1, instead was '${offsetX}'`);
|
|
67
|
+
|
|
68
|
+
assert.isNumber(offsetY, 'offsetY');
|
|
69
|
+
assert.notNaN(offsetY, 'offsetY');
|
|
70
|
+
assert.ok(isValueBetweenInclusive(offsetY, 0, 1), `expected offsetY to be between 0 and 1, instead was '${offsetY}'`);
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
assert.isNumber(scaleX, 'scaleX');
|
|
74
|
+
assert.notNaN(scaleX, 'scaleX');
|
|
75
|
+
assert.isFinite(scaleX, 'scaleX');
|
|
76
|
+
|
|
77
|
+
assert.isNumber(scaleY, 'scaleY');
|
|
78
|
+
assert.notNaN(scaleY, 'scaleY');
|
|
79
|
+
assert.isFinite(scaleY, 'scaleY');
|
|
80
|
+
|
|
81
|
+
this.material.uniforms.uFogUvTransform.value.set(offsetX, offsetY, scaleX, scaleY);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param {Texture} texture
|
|
87
|
+
*/
|
|
88
|
+
setDepthBuffer(texture) {
|
|
89
|
+
// assert.equal(texture.type, UnsignedShortType, `expected texture type to be UnsignedShort(=${UnsignedShortType}), instead got something else (=${texture.type})`)
|
|
90
|
+
|
|
91
|
+
this.material.uniforms.tDepth.value = texture;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
*
|
|
96
|
+
* @param {Texture} buffer
|
|
97
|
+
*/
|
|
98
|
+
setFogBuffer(buffer) {
|
|
99
|
+
this.material.uniforms.tFog.value = buffer;
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Resolution of the fog texture
|
|
105
|
+
* @param {number} x
|
|
106
|
+
* @param {number} y
|
|
107
|
+
*/
|
|
108
|
+
setResolution(x, y) {
|
|
109
|
+
this.material.uniforms.uResolution.value.set(x, y);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
*
|
|
114
|
+
* @param {Vector4} color
|
|
115
|
+
*/
|
|
116
|
+
setFogColor(color) {
|
|
117
|
+
this.material.uniforms.uColor.value.copy(color);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* What the fog does outside the fogged area. See {@link FogOfWarEdgeMode}.
|
|
122
|
+
*
|
|
123
|
+
* @param {FogOfWarEdgeMode} mode
|
|
124
|
+
*/
|
|
125
|
+
setEdgeMode(mode) {
|
|
126
|
+
this.material.uniforms.uEdgeExtend.value = (mode === FogOfWarEdgeMode.Extend) ? 1 : 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
*
|
|
131
|
+
* @param {WebGLRenderer} renderer
|
|
132
|
+
* @param {PerspectiveCamera|OrthographicCamera} camera
|
|
133
|
+
* @param {Scene} scene
|
|
134
|
+
* @param {WebGLRenderTarget} target
|
|
135
|
+
*/
|
|
136
|
+
render(renderer, camera, scene, target) {
|
|
137
|
+
assert.ok(camera.isCamera, 'Not a camera');
|
|
138
|
+
assert.ok(camera.isPerspectiveCamera || camera.isOrthographicCamera, 'Unsupported Camera type. Expected PerspectiveCamera or OrthographicCamera');
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
//set up uniforms
|
|
142
|
+
const uniforms = this.material.uniforms;
|
|
143
|
+
|
|
144
|
+
uniforms.uProjectionInverse.value.copy(camera.projectionMatrixInverse);
|
|
145
|
+
uniforms.uViewInverse.value.copy(camera.matrixWorld);
|
|
146
|
+
|
|
147
|
+
renderer.setRenderTarget(target);
|
|
148
|
+
renderer.clearColor();
|
|
149
|
+
|
|
150
|
+
renderer.render(this.scene, this.camera);
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
renderer.setRenderTarget(null);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenSpaceFogOfWarShader.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js"],"names":[],"mappings":"AAGA;;;GAGG;AACH,kDAFW,cAAc,
|
|
1
|
+
{"version":3,"file":"screenSpaceFogOfWarShader.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/ecs/fow/shader/screenSpaceFogOfWarShader.js"],"names":[],"mappings":"AAGA;;;GAGG;AACH,kDAFW,cAAc,CA4LxB;+BAjMyE,OAAO"}
|
|
@@ -22,9 +22,12 @@ export function buildScreenSpaceFogOfWarShader() {
|
|
|
22
22
|
uniform vec2 uResolution;
|
|
23
23
|
|
|
24
24
|
uniform vec4 uColor;
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
// FogOfWarEdgeMode as a float: 0 = Conceal (legacy), 1 = Extend
|
|
27
|
+
uniform float uEdgeExtend;
|
|
28
|
+
|
|
26
29
|
varying vec2 vUv;
|
|
27
|
-
|
|
30
|
+
|
|
28
31
|
const float c_samplesX = 5.0;
|
|
29
32
|
const float c_samplesY = 5.0;
|
|
30
33
|
|
|
@@ -43,11 +46,9 @@ export function buildScreenSpaceFogOfWarShader() {
|
|
|
43
46
|
// per tap, so a 5-tap kernel reaches +/-2 texels either side of centre.
|
|
44
47
|
const float texelSpacing = 1.0;
|
|
45
48
|
|
|
46
|
-
vec3 computeWorldPosition(){
|
|
49
|
+
vec3 computeWorldPosition(const in float normalizedDepth){
|
|
47
50
|
// Convert screen coordinates to normalized device coordinates (NDC)
|
|
48
|
-
|
|
49
|
-
float normalizedDepth = texture2D( tDepth, vUv).x ;
|
|
50
|
-
|
|
51
|
+
|
|
51
52
|
vec4 ndc = vec4(
|
|
52
53
|
(vUv.x - 0.5) * 2.0,
|
|
53
54
|
(vUv.y - 0.5) * 2.0,
|
|
@@ -105,9 +106,28 @@ export function buildScreenSpaceFogOfWarShader() {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
void main(){
|
|
109
|
+
float sceneDepth = texture2D( tDepth, vUv ).x;
|
|
110
|
+
|
|
111
|
+
// NOTHING WAS DRAWN HERE. The depth is the buffer's clear value, so
|
|
112
|
+
// the position reconstructed from it lands on the far plane: a point
|
|
113
|
+
// with no relationship to the scene, whose fog lookup is whatever the
|
|
114
|
+
// sampler's clamp happens to return. In a world with a visible
|
|
115
|
+
// boundary that is the concealed border ring, and it gets painted
|
|
116
|
+
// across the entire void as a hard rectangle on the world's edge.
|
|
117
|
+
// Under Extend, leave such a pixel alone rather than fog it on the
|
|
118
|
+
// strength of a reconstruction that means nothing.
|
|
119
|
+
//
|
|
120
|
+
// Under Conceal this branch never taken, so the legacy path is
|
|
121
|
+
// bit-identical — including the one case Extend gives up, a real
|
|
122
|
+
// surface sitting exactly on the far plane.
|
|
123
|
+
if (uEdgeExtend > 0.5 && sceneDepth >= 1.0) {
|
|
124
|
+
gl_FragColor = vec4(0.0);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
108
128
|
//get world fragment position
|
|
109
|
-
vec3 worldPosition = computeWorldPosition();
|
|
110
|
-
|
|
129
|
+
vec3 worldPosition = computeWorldPosition(sceneDepth);
|
|
130
|
+
|
|
111
131
|
vec2 fogUv = worldPosition.xz * uFogUvTransform.zw + uFogUvTransform.xy;
|
|
112
132
|
|
|
113
133
|
float fogValue = getFog( fogUv );
|
|
@@ -144,6 +164,11 @@ export function buildScreenSpaceFogOfWarShader() {
|
|
|
144
164
|
uViewInverse: { type: 'm4', value: new Matrix4() },
|
|
145
165
|
uProjectionInverse: { type: 'm4', value: new Matrix4() },
|
|
146
166
|
uColor: { type: 'c', value: new Vector4(0, 0, 0, 1) },
|
|
167
|
+
/**
|
|
168
|
+
* {@link FogOfWarEdgeMode} as a float. Defaults to Conceal, so a consumer
|
|
169
|
+
* that never sets it keeps the pre-option behaviour.
|
|
170
|
+
*/
|
|
171
|
+
uEdgeExtend: { type: 'f', value: 0 },
|
|
147
172
|
uFogUvTransform: { type: 'v4', value: new Vector4(0, 0, 1, 1) },
|
|
148
173
|
/**
|
|
149
174
|
* Fog texture resolution
|