@adatechnology/game-fluids-three 0.1.7 → 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/README.md +23 -0
- package/dist/index.d.ts +315 -4
- package/dist/index.global.js +9628 -241
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +827 -193
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -93,6 +93,29 @@ budget, the newest are drawn.
|
|
|
93
93
|
Types exported: `FluidRenderer`, `FluidRendererOptions`, `FluidRendererStats`, `BindOptions`, `FluidQuality`, `ThreeLib`. `smoothOutline(front, subdiv)` is
|
|
94
94
|
exported too (the periodic Catmull-Rom used for the stain rim).
|
|
95
95
|
|
|
96
|
+
|
|
97
|
+
## Running film
|
|
98
|
+
|
|
99
|
+
`createFilmManager(THREE, options)` draws the film of `@adatechnology/game-fluids` (blood, old blood, bile, motor oil, fuel running down a wall or a floor) over
|
|
100
|
+
the host's surfaces. The shader is the Fluid Standard's (thickness to colour, normals from the gradient, gloss, bile bubbles, oil and fuel iridescence, cracks
|
|
101
|
+
where it dried thick); it is drawn transparent, so the host's own surface shows where nothing is wet. The film stays **near the player**: at most 6 grids of
|
|
102
|
+
2 x 2 m (160 x 160 cells), only within 30 m, simulated inside a millisecond budget and uploaded by the dirty rectangle (`gpu`: pass the `WebGLRenderer` so
|
|
103
|
+
`copyTextureToTexture` can be used). A grid that is out of range, still for 20 s or squeezed out by a newer one is **baked** into the stain decals of the
|
|
104
|
+
fluid world (`fluids.splash` with the film's colours) and freed.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const film = createFilmManager(THREE, { scene, camera, random: createFilmLcg(1), fluids, gpu: renderer })
|
|
108
|
+
film.launch({ fluid: 'blood', point: [0, 1.2, 0], velocity: [1, -1, 0], radius: 4 }) // a drop; the grid is made under it
|
|
109
|
+
film.drip({ fluid: 'fuel', point: [0, 0.9, 0], every: 0.5 })
|
|
110
|
+
const bridge = createFilmBridge({ fluids, manager: film, random })
|
|
111
|
+
// every frame: fluids.update(dt); bridge.update(dt); film.update(dt)
|
|
112
|
+
bridge.splat({ fluid: 'blood', point, direction, tint: { fresh: 0x8a0a0a, dry: 0x3a1c12 } }) // a wound, in the colours of an infection age
|
|
113
|
+
bridge.setWeather({ rain: 1 }) // the rain hook: drying stops, spread x1.5 (a no-op until game-weather drives it)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`surfaceAt(point)` gives the floor, the wall and the curb of a point (`layoutFromSurfaces`); without it the film lies on the ground. A `leak` of a liquid that has a
|
|
117
|
+
film drips from the hole into it until the stream ends.
|
|
118
|
+
|
|
96
119
|
## Português
|
|
97
120
|
|
|
98
121
|
Renderizador three.js do `@adatechnology/game-fluids`: o líquido dentro do recipiente (um volume cortado pelo plano da superfície, sempre
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as THREE_NS from 'three';
|
|
2
|
-
import {
|
|
2
|
+
import { VfxWorld } from '@adatechnology/game-vfx';
|
|
3
|
+
import { ThreeLib as ThreeLib$1 } from '@adatechnology/game-vfx-three';
|
|
4
|
+
import { Vec3, FluidWorld, Film, StainTint, FilmLayout, FilmProfile, FilmRandom, LiquidId, SurfaceSpec, FilmLook, FilmRegion } from '@adatechnology/game-fluids';
|
|
3
5
|
|
|
4
6
|
/** The only three.js members the renderer needs at runtime. Pass your own `THREE` instance (the whole namespace works). */
|
|
5
|
-
type ThreeLib = Pick<typeof THREE_NS, 'BufferAttribute' | 'BufferGeometry' | 'Color' | 'DoubleSide' | 'DynamicDrawUsage' | 'Group' | 'InstancedMesh' | 'Matrix4' | 'Mesh' | 'MeshStandardMaterial' | 'Points' | 'ShaderMaterial' | 'SphereGeometry' | 'Vector3'>;
|
|
7
|
+
type ThreeLib = ThreeLib$1 & Pick<typeof THREE_NS, 'BufferAttribute' | 'PerspectiveCamera' | 'BufferGeometry' | 'Color' | 'DataTexture' | 'DoubleSide' | 'DynamicDrawUsage' | 'Group' | 'LinearFilter' | 'InstancedMesh' | 'Matrix4' | 'Mesh' | 'MeshStandardMaterial' | 'Points' | 'RGBAFormat' | 'ShaderMaterial' | 'SphereGeometry' | 'Vector2' | 'Vector3'>;
|
|
6
8
|
/** Detail level. It sets the budgets (stains, streams, particles) and the mesh resolution. */
|
|
7
9
|
type FluidQuality = 'low' | 'medium' | 'high';
|
|
8
10
|
interface FluidRendererOptions {
|
|
@@ -14,8 +16,15 @@ interface FluidRendererOptions {
|
|
|
14
16
|
quality?: FluidQuality;
|
|
15
17
|
/** Environment map the wet surfaces and liquids reflect. When omitted they use `scene.environment` if the host set one. */
|
|
16
18
|
envMap?: THREE_NS.Texture | null;
|
|
17
|
-
/** Pixel scale of the round point sprites (about half the canvas height in pixels). Default 600. */
|
|
19
|
+
/** Pixel scale of the round point sprites (about half the canvas height in pixels). Default 600. Only read when `vfx` is not given. */
|
|
18
20
|
pointScale?: number;
|
|
21
|
+
/**
|
|
22
|
+
* The effects world that holds the droplets and steam (a host lends one to every renderer and steps it once per frame). Without it the renderer
|
|
23
|
+
* makes its own world and its own three layer and steps them in `update`.
|
|
24
|
+
*/
|
|
25
|
+
vfx?: VfxWorld;
|
|
26
|
+
/** Camera of the own effects world (distance LOD); only read when `vfx` is not given. Default a camera at the origin. */
|
|
27
|
+
camera?: THREE_NS.Camera;
|
|
19
28
|
}
|
|
20
29
|
interface BindOptions {
|
|
21
30
|
/**
|
|
@@ -65,4 +74,306 @@ declare function createFluidRenderer(THREE: ThreeLib, options: FluidRendererOpti
|
|
|
65
74
|
/** Periodic Catmull-Rom of the 16 front radii: a smooth outline with `subdiv` points per sample. */
|
|
66
75
|
declare const smoothOutline: (front: number[], subdiv: number, out?: number[]) => number[];
|
|
67
76
|
|
|
68
|
-
|
|
77
|
+
interface FilmGridOptions {
|
|
78
|
+
layout: FilmLayout;
|
|
79
|
+
profile: FilmProfile;
|
|
80
|
+
random: FilmRandom;
|
|
81
|
+
/** The host's surface colour under the film, `0xRRGGBB` (the shader blends over it). Default the page's sidewalk grey. */
|
|
82
|
+
base?: number;
|
|
83
|
+
/** World point the grid is about: the distance to the player is measured from it. */
|
|
84
|
+
center: Vec3;
|
|
85
|
+
/** The splash colours the profile was tinted with, kept to tell a grid of another tint apart and to bake with them. */
|
|
86
|
+
tint?: StainTint;
|
|
87
|
+
stepRate?: number;
|
|
88
|
+
maxSteps?: number;
|
|
89
|
+
}
|
|
90
|
+
/** A rectangle of cells, inclusive. */
|
|
91
|
+
interface CellRect {
|
|
92
|
+
firstRow: number;
|
|
93
|
+
lastRow: number;
|
|
94
|
+
firstColumn: number;
|
|
95
|
+
lastColumn: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* One film grid on screen: the running film, its texture (thickness, dryness, dampness per cell as the page's `upload` writes them), the strips of the
|
|
99
|
+
* layout drawn with the film shader, and the drops in flight.
|
|
100
|
+
*/
|
|
101
|
+
declare class FilmGrid {
|
|
102
|
+
private readonly THREE;
|
|
103
|
+
readonly film: Film;
|
|
104
|
+
readonly group: THREE_NS.Group;
|
|
105
|
+
readonly texture: THREE_NS.DataTexture;
|
|
106
|
+
readonly center: Vec3;
|
|
107
|
+
readonly tint: StainTint | undefined;
|
|
108
|
+
readonly data: Uint8Array<ArrayBuffer>;
|
|
109
|
+
/** Seconds the grid has been without anything landing, flowing or drying. */
|
|
110
|
+
idle: number;
|
|
111
|
+
/** Set once the whole texture went to the GPU (the partial uploads need it there first). */
|
|
112
|
+
uploaded: boolean;
|
|
113
|
+
private readonly strips;
|
|
114
|
+
private readonly material;
|
|
115
|
+
private readonly dropsView;
|
|
116
|
+
private previous;
|
|
117
|
+
private pending;
|
|
118
|
+
private launched;
|
|
119
|
+
private lastWet;
|
|
120
|
+
constructor(THREE: ThreeLib, options: FilmGridOptions);
|
|
121
|
+
get profile(): FilmProfile;
|
|
122
|
+
setProfile(profile: FilmProfile): void;
|
|
123
|
+
/** Where the film's light and eye are (the page's `camP`, `flashP` and `flashD`). */
|
|
124
|
+
setView(camera: Vec3, forward: Vec3, time: number): void;
|
|
125
|
+
/** Steps the film one frame and refreshes the texture cells that may have changed. Returns the rectangle to upload, or null. */
|
|
126
|
+
update(dt: number, dryingSeconds: number): CellRect | null;
|
|
127
|
+
private takePending;
|
|
128
|
+
/** The mean dryness of the cells that are wet (1 when nothing is). */
|
|
129
|
+
dryness(): number;
|
|
130
|
+
/** Draws the drops and the bead; answers how many drops are in flight. */
|
|
131
|
+
syncDrops(): number;
|
|
132
|
+
dropsInFlight(): boolean;
|
|
133
|
+
/** Writes the cells of the texture from the field, as the page's `upload` does (F360). */
|
|
134
|
+
encode(rect: CellRect): void;
|
|
135
|
+
/** Encodes the whole field (a grid made from a state, or after `clear`). */
|
|
136
|
+
encodeAll(): void;
|
|
137
|
+
private union;
|
|
138
|
+
dispose(): void;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The surfaces a grid is laid over: the floor, and optionally a wall the film folds up and a curb it folds down. */
|
|
142
|
+
interface FilmSurfaces {
|
|
143
|
+
floor: SurfaceSpec;
|
|
144
|
+
wall?: SurfaceSpec;
|
|
145
|
+
curb?: SurfaceSpec;
|
|
146
|
+
}
|
|
147
|
+
/** The part of the renderer the manager uses to upload only the dirty rectangle of a texture (three 0.160 `copyTextureToTexture`). */
|
|
148
|
+
interface FilmGpu {
|
|
149
|
+
copyTextureToTexture(position: THREE_NS.Vector2, source: THREE_NS.Texture, destination: THREE_NS.Texture, level?: number): void;
|
|
150
|
+
}
|
|
151
|
+
interface FilmViewer {
|
|
152
|
+
position: Vec3;
|
|
153
|
+
forward: Vec3;
|
|
154
|
+
}
|
|
155
|
+
/** What a baked grid leaves behind in the host's stain decals. */
|
|
156
|
+
interface FilmBake {
|
|
157
|
+
fluid: LiquidId;
|
|
158
|
+
/** Where the film was thickest (world), litres of it, and the colours of its wet and dry look. */
|
|
159
|
+
point: Vec3;
|
|
160
|
+
liters: number;
|
|
161
|
+
tint: StainTint;
|
|
162
|
+
/** Radius in metres of the wet cells around `point`. */
|
|
163
|
+
radius: number;
|
|
164
|
+
reason: 'range' | 'settled' | 'budget' | 'clear';
|
|
165
|
+
}
|
|
166
|
+
interface FilmManagerOptions {
|
|
167
|
+
/** Where the grids are added (one group). */
|
|
168
|
+
scene: THREE_NS.Object3D;
|
|
169
|
+
/** The host's random stream: the film never calls `Math.random`. */
|
|
170
|
+
random: FilmRandom;
|
|
171
|
+
/** The player's eye: grids are simulated near it and drawn lit from it. A camera works; or give `viewer`. */
|
|
172
|
+
camera?: THREE_NS.Camera;
|
|
173
|
+
viewer?: () => FilmViewer;
|
|
174
|
+
/** The surfaces under a world point (floor, wall, curb), or null for none. Default: the ground plane at `groundAt`. */
|
|
175
|
+
surfaceAt?: (point: Vec3) => FilmSurfaces | null;
|
|
176
|
+
groundAt?: (x: number, z: number) => number;
|
|
177
|
+
/** The world whose stain decals take the film when a grid is baked (`fluids.splash`). Without it a bake only calls `onBake`. */
|
|
178
|
+
fluids?: FluidWorld;
|
|
179
|
+
onBake?: (bake: FilmBake) => void;
|
|
180
|
+
/** Profiles by liquid id (default the film profiles of `game-fluids`); override one to tune it. */
|
|
181
|
+
profiles?: Readonly<Record<string, FilmProfile>>;
|
|
182
|
+
/** Colour of the surface under the film (default the page's grey). */
|
|
183
|
+
base?: number;
|
|
184
|
+
maxGrids?: number;
|
|
185
|
+
/** Metres from the player past which a grid is not simulated and is baked. */
|
|
186
|
+
range?: number;
|
|
187
|
+
/** Milliseconds of film simulation per frame; the grids left out wait for the next frame. */
|
|
188
|
+
budgetMs?: number;
|
|
189
|
+
/** Seconds without change after which a grid is baked and freed. */
|
|
190
|
+
settleSeconds?: number;
|
|
191
|
+
/** Side of a grid in metres (cells are 1.25 cm) and the cell size. */
|
|
192
|
+
gridMetres?: number;
|
|
193
|
+
cellSize?: number;
|
|
194
|
+
/** Seconds of drying clock that pass in one second of real time (the host scales it; default 1). */
|
|
195
|
+
dryingSpeed?: number;
|
|
196
|
+
/** The renderer, for the partial texture uploads. Without it the whole texture goes up when it changes. */
|
|
197
|
+
gpu?: FilmGpu;
|
|
198
|
+
/** A clock in milliseconds (default `performance.now`). */
|
|
199
|
+
now?: () => number;
|
|
200
|
+
}
|
|
201
|
+
interface FilmStats {
|
|
202
|
+
grids: number;
|
|
203
|
+
drops: number;
|
|
204
|
+
wetCells: number;
|
|
205
|
+
/** Milliseconds the last update spent simulating. */
|
|
206
|
+
simMs: number;
|
|
207
|
+
/** Grids left out of the last update by the budget. */
|
|
208
|
+
starved: number;
|
|
209
|
+
baked: number;
|
|
210
|
+
/** Texels written by partial uploads in the last update. */
|
|
211
|
+
uploadedTexels: number;
|
|
212
|
+
}
|
|
213
|
+
interface FilmLaunch {
|
|
214
|
+
fluid: LiquidId;
|
|
215
|
+
point: Vec3;
|
|
216
|
+
/** Default straight down at 2 m/s. */
|
|
217
|
+
velocity?: Vec3;
|
|
218
|
+
/** Millimetres (default 3). */
|
|
219
|
+
radius?: number;
|
|
220
|
+
surfaces?: FilmSurfaces;
|
|
221
|
+
/** The colours of this splash (an infection age: fresh red, dry brown): the film is drawn with them instead of the liquid's own. */
|
|
222
|
+
tint?: StainTint;
|
|
223
|
+
}
|
|
224
|
+
interface FilmDripSpec {
|
|
225
|
+
fluid: LiquidId;
|
|
226
|
+
point: Vec3;
|
|
227
|
+
/** Average seconds between drops. */
|
|
228
|
+
every: number;
|
|
229
|
+
radius?: number;
|
|
230
|
+
surfaces?: FilmSurfaces;
|
|
231
|
+
tint?: StainTint;
|
|
232
|
+
}
|
|
233
|
+
/** A profile drawn in the colours of a splash: the thin film and the drops take the fresh colour, the thick middle a darker one, and the dried film the dry colour. */
|
|
234
|
+
declare function tinted(profile: FilmProfile, tint: StainTint): FilmProfile;
|
|
235
|
+
/**
|
|
236
|
+
* Keeps the running films near the player: at most `maxGrids` grids of 2 x 2 m, only within `range`, simulated inside a millisecond budget and uploaded by their
|
|
237
|
+
* dirty rectangle. A grid that is out of range, still, or squeezed out by a newer one is baked: its film becomes a stain of the host's world with the same
|
|
238
|
+
* colours and amount, and the grid is freed.
|
|
239
|
+
*/
|
|
240
|
+
declare class FilmManager {
|
|
241
|
+
private readonly THREE;
|
|
242
|
+
private readonly options;
|
|
243
|
+
readonly group: THREE_NS.Group;
|
|
244
|
+
private readonly grids;
|
|
245
|
+
private readonly profiles;
|
|
246
|
+
private readonly staging;
|
|
247
|
+
private readonly now;
|
|
248
|
+
private rain;
|
|
249
|
+
private time;
|
|
250
|
+
private bakedCount;
|
|
251
|
+
private last;
|
|
252
|
+
private empties;
|
|
253
|
+
constructor(THREE: ThreeLib, options: FilmManagerOptions);
|
|
254
|
+
get gridCount(): number;
|
|
255
|
+
/** The grids, for a host that wants to look at them (a bench, a test). */
|
|
256
|
+
list(): readonly FilmGrid[];
|
|
257
|
+
setWeather(weather: {
|
|
258
|
+
rain: number;
|
|
259
|
+
}): void;
|
|
260
|
+
/** Throws a drop of a liquid: it lands in the grid under it (made on the spot) and leaves the impact splash. Null when it is out of range or has no surface. */
|
|
261
|
+
launch(request: FilmLaunch): FilmGrid | null;
|
|
262
|
+
/** A drip: a bead swells at `point`, detaches and falls every `every` seconds on average. Replaces the grid's drip. */
|
|
263
|
+
drip(spec: FilmDripSpec): FilmGrid | null;
|
|
264
|
+
/** The grid of a liquid that covers `point`, made when there is none. Null when `point` is out of range or has no surface under it. */
|
|
265
|
+
gridFor(fluid: LiquidId, point: Vec3, surfaces?: FilmSurfaces, tint?: StainTint): FilmGrid | null;
|
|
266
|
+
/** One frame: simulates the grids near the player inside the budget, uploads what changed, bakes the ones that are far or still. */
|
|
267
|
+
update(dt: number): FilmStats;
|
|
268
|
+
stats(): FilmStats;
|
|
269
|
+
/** Frees every grid. `bake` leaves each one's film as a stain first. */
|
|
270
|
+
clear(bake?: boolean): void;
|
|
271
|
+
dispose(): void;
|
|
272
|
+
private columns;
|
|
273
|
+
private viewer;
|
|
274
|
+
private surfacesAt;
|
|
275
|
+
/** Whether `point` is over the grid, away from its edge, so a splash there does not fall off the strip. */
|
|
276
|
+
private covers;
|
|
277
|
+
/** The grid squeezed out for a new one at `point`: the one that has been still the longest, then the farthest from the player. */
|
|
278
|
+
private evict;
|
|
279
|
+
/** A grid that has nothing wet for two seconds goes without a trace. */
|
|
280
|
+
private emptyFor;
|
|
281
|
+
private free;
|
|
282
|
+
/** Bakes the film of a grid into the host's stain decals and frees the grid. */
|
|
283
|
+
private release;
|
|
284
|
+
/** The film's centre of volume, its litres (millimetres of film times cell area) and the radius of the wet cells. */
|
|
285
|
+
private measure;
|
|
286
|
+
/** Copies the dirty rectangle to the GPU texture; answers the texels written. */
|
|
287
|
+
private upload;
|
|
288
|
+
private stagingFor;
|
|
289
|
+
}
|
|
290
|
+
declare function createFilmManager(THREE: ThreeLib, options: FilmManagerOptions): FilmManager;
|
|
291
|
+
|
|
292
|
+
/** The page's `uFl` of each look (blood 0, bile 2, oil 3, fuel 4). */
|
|
293
|
+
declare const LOOK_INDEX: Readonly<Record<FilmLook, number>>;
|
|
294
|
+
interface FilmMaterial {
|
|
295
|
+
readonly material: THREE_NS.ShaderMaterial;
|
|
296
|
+
/** Sets the page's liquid uniforms from a profile (`setFluid`, F119-130). */
|
|
297
|
+
setProfile(profile: FilmProfile): void;
|
|
298
|
+
dispose(): void;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The film's material: the Fluid Standard's shader with the grid texture `tF` (r: thickness, g: dryness, b: dampness) and the liquid's colours.
|
|
302
|
+
* It is transparent over the host's surface: the alpha is the film's coverage.
|
|
303
|
+
*/
|
|
304
|
+
declare function createFilmMaterial(THREE: ThreeLib, texture: THREE_NS.Texture, width: number, height: number, span?: number): FilmMaterial;
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* One strip of the folded grid as a plane of the surface: `uv` runs over the region's rows and the grid's columns, so the shader reads its own cells.
|
|
308
|
+
* `aB` is the way the film runs down the strip (rows) and `aT` its columns.
|
|
309
|
+
*/
|
|
310
|
+
declare function createStripGeometry(THREE: ThreeLib, layout: FilmLayout, region: FilmRegion): THREE_NS.BufferGeometry;
|
|
311
|
+
|
|
312
|
+
interface FilmDrops {
|
|
313
|
+
readonly drops: THREE_NS.InstancedMesh;
|
|
314
|
+
readonly bead: THREE_NS.Mesh;
|
|
315
|
+
setProfile(profile: FilmProfile): void;
|
|
316
|
+
/** Writes the film's drops in flight and the drip's bead into the meshes; answers how many drops are drawn. */
|
|
317
|
+
sync(film: Film): number;
|
|
318
|
+
dispose(): void;
|
|
319
|
+
}
|
|
320
|
+
declare function createFilmDrops(THREE: ThreeLib, capacity: number, profile: FilmProfile): FilmDrops;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The film shader of the Fluid Standard page (F131-209), line by line. Where the page painted the whole surface itself (`baseCol`: wall, sidewalk, curb,
|
|
324
|
+
* road), the film is drawn over the host's own surface, so `baseCol` is the flat colour `uBase` and the alpha is the film's coverage: the wet part and,
|
|
325
|
+
* for fuel, the ground it darkened. Three more differences: the grid size and span are uniforms (`uGrid`, `uSpan`; the page hard-codes 256 x 396 and 4.95),
|
|
326
|
+
* the strip tangent comes from the geometry (`aT`; the page's was the x axis), and the output goes through the r160 colour-space chunk after turning the page's
|
|
327
|
+
* display-space colours linear. The two lines that mirror the page's own wall in the reflection (`R.z<-.02 ... env=vec3(.30,.32,.35)`, `R.y<-.02 ...`) are left out:
|
|
328
|
+
* they describe the page's scene, not a surface of the host.
|
|
329
|
+
*/
|
|
330
|
+
declare const FILM_VERTEX = "attribute vec3 aB;attribute vec3 aT;varying vec2 vUv;varying vec3 vPos;varying vec3 vN;varying vec3 vB;varying vec3 vT;\nvoid main(){vUv=uv;vPos=position;vN=normal;vB=aB;vT=aT;gl_Position=projectionMatrix*viewMatrix*vec4(position,1.);}";
|
|
331
|
+
declare const FILM_FRAGMENT = "precision highp float;\nuniform sampler2D tF;uniform vec2 tx;uniform vec2 uGrid;uniform float uSpan;uniform float uT;uniform int uFl;uniform vec3 cThin,cThick,cDry,cDryThin,uBase;uniform float opK,F0,nrm,dbg;\nuniform vec3 camP,lampP,flashP,flashD;varying vec2 vUv;varying vec3 vPos;varying vec3 vN;varying vec3 vB;varying vec3 vT;\nfloat hsh(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}\nvec2 hsh2(vec2 p){return vec2(hsh(p),hsh(p+17.7));}\nfloat nz(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);return mix(mix(hsh(i),hsh(i+vec2(1,0)),f.x),mix(hsh(i+vec2(0,1)),hsh(i+vec2(1,1)),f.x),f.y);}\nfloat vor(vec2 p){vec2 i=floor(p),f=fract(p);float d1=8.,d2=8.;for(int y=-1;y<=1;y++)for(int x=-1;x<=1;x++){vec2 o=vec2(float(x),float(y));vec2 r=o+hsh2(i+o)-f;float d=dot(r,r);if(d<d1){d2=d1;d1=d;}else if(d<d2)d2=d;}return sqrt(d2)-sqrt(d1);}\nfloat hs(vec2 u){float r=texture2D(tF,u).r;return r*r*6.;}\nvoid main(){\n vec4 S=texture2D(tF,vUv);vec2 q=tx*.5;float h=.25*(hs(vUv+q)+hs(vUv-q)+hs(vUv+vec2(q.x,-q.y))+hs(vUv+vec2(-q.x,q.y)));float dry=S.g;float wg=S.b;\n vec2 o1=vec2(tx.x*1.5,0.),o2=vec2(0.,tx.y*1.5);\n float hl=hs(vUv-o1),hr=hs(vUv+o1),hd=hs(vUv-o2),hu=hs(vUv+o2);\n vec2 g=vec2(hr-hl,hu-hd)/37.5;\n vec2 cellp=vUv*uGrid;\n float n1=nz(cellp*2.3),n2=nz(cellp*7.1);\n float hn=h+(n1-.5)*.045+(n2-.5)*.02;\n float m=smoothstep(.035,.08,hn);\n if(uFl==4){float wa=.25*(texture2D(tF,vUv+q).b+texture2D(tF,vUv-q).b+texture2D(tF,vUv+vec2(q.x,-q.y)).b+texture2D(tF,vUv+vec2(-q.x,q.y)).b);m=smoothstep(.35,.75,wa+(n1-.5)*.25)*smoothstep(.0,.012,h);wg=smoothstep(.2,.8,wa+(n1-.5)*.2);}\n vec2 p=vec2(dot(vPos,normalize(vT)),vUv.y*uSpan);\n vec3 base=uBase;\n vec3 T=normalize(vT),Bv=normalize(vB),N0=normalize(vN);\n vec3 nt=vec3(-g*nrm*m,1.);\n float dryM=smoothstep(.5,.95,dry);\n float wet=(1.-dryM)*m;\n // bile bubbles\n float bub=0.;\n if(uFl==2){vec2 bp=cellp/2.6;vec2 bi=floor(bp);vec2 bf=fract(bp)-.5;float rnd=hsh(bi);\n if(rnd>.5&&h>1.1){float t=fract(uT*(.18+.25*hsh(bi+3.1))+rnd*7.);float rad=.42*smoothstep(0.,.8,t);float d=length(bf-(hsh2(bi+9.)-.5)*.2);\n if(t<.86&&d<rad){float z=sqrt(max(0.,rad*rad-d*d));nt.xy+=(bf/max(rad,.01))*2.2*(1.-dryM);bub=z/rad;}\n else if(t>=.86){float rr=.42*1.15;float ring=1.-smoothstep(0.,.07,abs(d-rr*(.9+(t-.86)*.8)));nt.xy-=normalize(bf+.0001)*ring*.8*(1.-dryM);bub=-ring;}}}\n if(uFl==4){vec2 wp=p;nt.xy+=.05*m*vec2(nz(wp*9.+vec2(uT*1.7,0.))-.5,nz(wp*9.+vec2(0.,uT*1.4)+7.)-.5)*2.;}\n vec3 Nf=normalize(T*nt.x+Bv*nt.y+N0*nt.z);\n // albedo\n float th=smoothstep(.1,1.7,h);\n vec3 fc=mix(cThin,cThick,th);\n float op=clamp(1.-exp(-h*opK),0.,1.);\n vec3 alb;\n if(uFl==4){alb=base*mix(vec3(1.),cThin*1.15,clamp(h*.9,0.,1.));}\n else{alb=mix(base*mix(vec3(1.),fc*1.6,clamp(h*2.,0.,1.)),fc,op);}\n if(uFl==2&&bub>0.)alb=mix(alb,cThin*1.25,bub*.5);\n // dried look: matte, darker rim, cracks where it was thick\n float rim=smoothstep(.01,.05,length(g))*(1.-smoothstep(.4,1.,h));\n vec3 dc=mix(cDryThin,cDry,smoothstep(.06,.8,h))*(1.-.42*rim);\n float crk=(1.-smoothstep(.015,.05,vor(p*(uFl==2?34.:52.))))*smoothstep(.9,1.6,h);\n dc=mix(dc,base*.55,crk*.75);\n if(uFl!=3&&uFl!=4)alb=mix(alb,mix(base,dc,clamp(.4+h*1.4,0.,1.)),dryM);\n // gasoline darkens the ground while wet\n float gw=0.;if(uFl==4){gw=wg*(1.-m*.0);base*=mix(1.,.45,gw);alb=mix(base,alb,m);}\n vec3 col0=mix(base,alb*(uFl<2?mix(.62,1.,dryM):1.),m);\n // lighting\n vec3 V=normalize(camP-vPos);\n vec3 Lv=lampP-vPos;float dl=length(Lv);vec3 L=Lv/dl;float att=2.2/(1.+dl*dl*.12);\n vec3 lampC=vec3(1.,.88,.7);vec3 moonD=normalize(vec3(-3.,5.,4.));vec3 moonC=vec3(.11,.14,.19);vec3 amb=vec3(.14,.15,.17);\n vec3 Fv=flashP-vPos;float fd=length(Fv);vec3 FL=Fv/fd;float cone=smoothstep(.86,.97,dot(-FL,flashD));float fatt=cone*1.1/(1.+fd*fd*.6);\n vec3 dif=col0*(amb+lampC*att*max(dot(Nf,L),0.)+moonC*max(dot(Nf,moonD),0.)+vec3(.95,.97,1.)*fatt*max(dot(Nf,FL),0.));\n float wv=max(wet,gw*.7);\n float shin=mix(30.,320.,wv);\n vec3 Hh=normalize(L+V);vec3 Hf=normalize(FL+V);\n float sp=pow(max(dot(Nf,Hh),0.),shin)*shin*.011*att;float spf=pow(max(dot(Nf,Hf),0.),shin)*shin*.008*fatt;\n vec3 R=reflect(-V,Nf);\n vec3 env=mix(vec3(.03,.035,.04),vec3(.15,.17,.21),clamp(R.y*1.4,0.,1.));\n env+=lampC*(pow(max(dot(R,L),0.),500.)*7.+pow(max(dot(R,L),0.),40.)*.6)*att;\n float fr=F0+(1.-F0)*pow(1.-max(dot(Nf,V),0.),5.);\n vec3 col=dif*(1.-fr*wv*.7)+(env*min(1.,fr*(uFl==4?1.6:3.4))+min(lampC*sp+vec3(.9,.95,1.)*spf,vec3(.85)))*wv;\n if(uFl==3){float film=(1.-smoothstep(.04,.55,h))*m;vec3 ir=.5+.5*cos(6.283*(h*7.+n1*.5+uT*.03+vec3(0.,.33,.67)));col+=ir*film*(.10+.5*fr)*(.6+att*.4);}\n if(uFl==4){vec2 wp=p;float film=m*(.4+.6*(1.-smoothstep(.02,.2,h)));vec3 ir=.5+.5*cos(6.283*(nz(wp*6.)*1.4+uT*.02+vec3(0.,.33,.67)));col+=ir*film*.05*(.3+fr*3.);}\n if(dbg>.5){float k=clamp(h/3.,0.,1.);vec3 hc=mix(vec3(.05,.1,.5),vec3(1.,.9,.2),k);hc=mix(hc,vec3(1.,.2,.1),step(.66,k));col=mix(base*.25,mix(hc,vec3(.6),dryM*.7),step(.004,h));}\n float cover=max(m,gw*.55);\n gl_FragColor=vec4(pow(max(col,vec3(0.)),vec3(2.2)),clamp(cover,0.,1.));\n #include <colorspace_fragment>\n}";
|
|
332
|
+
|
|
333
|
+
/** A splash on the ground or a wall (a wound, a body falling): the film draws it as a drop landing, in the colours of `tint` when given. */
|
|
334
|
+
interface FilmSplat {
|
|
335
|
+
fluid: LiquidId;
|
|
336
|
+
point: Vec3;
|
|
337
|
+
/** The way the liquid travels (default straight down). */
|
|
338
|
+
direction?: Vec3;
|
|
339
|
+
/** Metres per second (default 2.5). */
|
|
340
|
+
speed?: number;
|
|
341
|
+
/** Drop radius in millimetres (default 3.2). */
|
|
342
|
+
radius?: number;
|
|
343
|
+
/** Drops thrown (default 1). */
|
|
344
|
+
count?: number;
|
|
345
|
+
surfaces?: FilmSurfaces;
|
|
346
|
+
tint?: StainTint;
|
|
347
|
+
}
|
|
348
|
+
interface FilmBridgeOptions {
|
|
349
|
+
fluids: FluidWorld;
|
|
350
|
+
manager: FilmManager;
|
|
351
|
+
/** The seeded stream that spreads the drops (the film never calls `Math.random`). */
|
|
352
|
+
random: () => number;
|
|
353
|
+
/** Which film profile a world liquid runs as: by default the liquids that have one (`blood`, `old_blood`, `bile`, `motor_oil`, `fuel`). Map `diesel` to `fuel`, say. */
|
|
354
|
+
liquids?: Readonly<Record<string, LiquidId>>;
|
|
355
|
+
/** Litres of one drop of a leak (default 0.00015). */
|
|
356
|
+
dropLiters?: number;
|
|
357
|
+
surfaceAt?: (point: Vec3) => FilmSurfaces | null;
|
|
358
|
+
}
|
|
359
|
+
interface FilmBridge {
|
|
360
|
+
/** Throws drops of a splash into the film. Returns how many found a grid. */
|
|
361
|
+
splat(splat: FilmSplat): number;
|
|
362
|
+
/** Weather of the film (the Phase 6 hook): while it rains the film does not dry and spreads 1.5 times faster. */
|
|
363
|
+
setWeather(weather: {
|
|
364
|
+
rain: number;
|
|
365
|
+
}): void;
|
|
366
|
+
/** Streams of the world that feed the film right now. */
|
|
367
|
+
readonly leaks: number;
|
|
368
|
+
/** Feeds the leaks: call it each frame with the frame's seconds, before or after `manager.update`. */
|
|
369
|
+
update(dt: number): void;
|
|
370
|
+
dispose(): void;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Joins a fluid world to the film: a `leak` of a liquid that has a film (blood, fuel, oil) drips onto the ground or wall below as drops thrown from the
|
|
374
|
+
* hole, at the rate the world gives, until the stream ends. `splat` is what the characters use for a wound (with the colours of the infection age as
|
|
375
|
+
* `tint`), and `setWeather` is the rain hook.
|
|
376
|
+
*/
|
|
377
|
+
declare function createFilmBridge(options: FilmBridgeOptions): FilmBridge;
|
|
378
|
+
|
|
379
|
+
export { type BindOptions, type CellRect, FILM_FRAGMENT, FILM_VERTEX, type FilmBake, type FilmBridge, type FilmBridgeOptions, type FilmDripSpec, type FilmDrops, type FilmGpu, FilmGrid, type FilmGridOptions, type FilmLaunch, FilmManager, type FilmManagerOptions, type FilmMaterial, type FilmSplat, type FilmStats, type FilmSurfaces, type FilmViewer, type FluidQuality, type FluidRenderer, type FluidRendererOptions, type FluidRendererStats, LOOK_INDEX, type ThreeLib, createFilmBridge, createFilmDrops, createFilmManager, createFilmMaterial, createFluidRenderer, createStripGeometry, smoothOutline, tinted };
|