@adatechnology/game-fluids-three 0.0.0-stage → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anderson Fernandes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,106 @@
1
- # Temporary Holding Version
1
+ # @adatechnology/game-fluids-three
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ three.js renderer for [`@adatechnology/game-fluids`](../game-fluids). It only reads the simulation state, so it draws exactly what the
4
+ deterministic world says (a LAN client with the same state draws the same liquids). `three` is a peer dependency used for types only: you pass your
5
+ own `THREE` instance at runtime.
6
+
7
+ What it draws:
8
+
9
+ - **Liquid in containers**: inside every bound object, the container's inner shape clipped by the world-space liquid surface, so the top is always
10
+ on the surface plane whatever the tilt (upright, lying down, upside down). One material per liquid (colour, opacity, roughness from gloss).
11
+ Steam puffs rise from a hot surface.
12
+ - **Streams**: a thread along the ballistic path (tube updated in place) that narrows as it accelerates, with a drop that grows at the leading tip
13
+ and a trailing drop when it ends; thin streams are a sequence of falling drops.
14
+ - **Stains**: a smooth fan from the 16 flow-front radii (never a perfect disc), lying on the surface plane a hair above it
15
+ (`polygonOffset`, oriented by the stain's normal), tinted by the liquid with a soft rim, plus an inner glossy fan at `wetRadius` that reflects
16
+ the environment and recedes while it dries. A dried mark is a faint matte patch.
17
+ - **Splashes**: pooled round droplets (`THREE.Points` with a small `ShaderMaterial`, the round sprite is drawn in the fragment shader, no textures).
18
+
19
+ Lit with the standard material, no transmission, a handful of draw calls, fixed budgets per quality.
20
+
21
+ ## Install
22
+
23
+ ```sh
24
+ pnpm add @adatechnology/game-fluids-three @adatechnology/game-fluids three
25
+ ```
26
+
27
+ Browser without a bundler (the global is `GameFluidsThree`; it bundles the few `game-fluids` helpers it calls, you still load `GameFluids` to create the world):
28
+
29
+ ```html
30
+ <script src="https://unpkg.com/three/build/three.min.js"></script>
31
+ <script src="https://unpkg.com/@adatechnology/game-fluids/dist/index.global.js"></script>
32
+ <script src="https://unpkg.com/@adatechnology/game-fluids-three/dist/index.global.js"></script>
33
+ <script>
34
+ const fluids = GameFluids.createFluids({ seed: 1 })
35
+ const view = GameFluidsThree.createFluidRenderer(THREE, { scene, fluids })
36
+ </script>
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ```ts
42
+ import * as THREE from 'three'
43
+ import { createFluids } from '@adatechnology/game-fluids'
44
+ import { createFluidRenderer } from '@adatechnology/game-fluids-three'
45
+
46
+ const fluids = createFluids({ seed: 7 })
47
+ fluids.addSurface({ id: 'floor', point: [0, 0, 0], normal: [0, 1, 0], halfSize: [10, 10], porosity: 0.05 })
48
+ fluids.addContainer(
49
+ { capacity: 0.25, liquid: 'coffee', liters: 0.25, temperature: 90, shape: { kind: 'cylinder', radius: 0.04, height: 0.1 }, opening: 0.04 },
50
+ { id: 'cup' },
51
+ )
52
+
53
+ const view = createFluidRenderer(THREE, { scene, fluids, quality: 'medium', envMap })
54
+ view.bindContainer('cup', cupObject, { innerOffset: [0, 0.005, 0] }) // inner bottom centre in the object's space
55
+
56
+ // every frame
57
+ const p = cupObject.localToWorld(new THREE.Vector3(0, 0.005, 0)) // world position of the inner bottom centre
58
+ fluids.setTransform('cup', [p.x, p.y, p.z], [q.x, q.y, q.z, q.w])
59
+ fluids.update(dt)
60
+ view.update(dt)
61
+ ```
62
+
63
+ `innerOffset` is the offset, in the bound object's space, of the origin of the container shape (the inner bottom centre). The transform given to
64
+ `fluids.setTransform` must be that point's world position and the object's world rotation. Objects are assumed to have uniform scale.
65
+
66
+ Destroying a container's object (for example from `@adatechnology/game-destruction`): call `view.unbindContainer(id)` and
67
+ `fluids.removeContainer(id)`; the stains already on the floor stay and dry on their own.
68
+
69
+ ## API
70
+
71
+ `createFluidRenderer(THREE, options)` returns a `FluidRenderer`.
72
+
73
+ | Option | |
74
+ |---|---|
75
+ | `scene` | Where the effects are added (one `fluid_renderer` group) |
76
+ | `fluids` | The `FluidWorld` to draw |
77
+ | `quality` | `'low'`, `'medium'` (default) or `'high'`: sets the mesh resolution and the budgets below |
78
+ | `envMap` | Environment map the wet surfaces reflect (otherwise `scene.environment` if the host set one) |
79
+ | `pointScale` | Pixel scale of the point sprites, about half the canvas height (default 600) |
80
+
81
+ | Method | |
82
+ |---|---|
83
+ | `bindContainer(containerId, object3D, { innerOffset? })` | Draw the liquid of a world container inside the object |
84
+ | `unbindContainer(containerId)` | Remove that liquid |
85
+ | `update(dt)` | Advance the visuals; call it after `fluids.update(dt)` |
86
+ | `clear()` | Remove stains, streams and particles (bindings stay) |
87
+ | `dispose()` | Remove everything from the scene and free geometries and materials |
88
+ | `stats()` | `{ quality, containers, streams, stains, particles, triangles, budget }` |
89
+
90
+ Budgets: stains 24 / 48 / 96, streams 12 / 24 / 48 and particles 96 / 256 / 512 for low / medium / high. When the world has more stains than the
91
+ budget, the newest are drawn.
92
+
93
+ Types exported: `FluidRenderer`, `FluidRendererOptions`, `FluidRendererStats`, `BindOptions`, `FluidQuality`, `ThreeLib`. `smoothOutline(front, subdiv)` is
94
+ exported too (the periodic Catmull-Rom used for the stain rim).
95
+
96
+ ## Português
97
+
98
+ Renderizador three.js do `@adatechnology/game-fluids`: o líquido dentro do recipiente (um volume cortado pelo plano da superfície, sempre
99
+ horizontal no mundo), fios de líquido com gota na ponta, manchas que crescem a partir de uma frente de fluxo (nunca um disco perfeito), brilho de
100
+ superfície molhada que reflete o cenário e seca da borda para dentro, vapor e gotas de respingo. Só lê o estado da simulação, então é igual em LAN.
101
+
102
+ ```ts
103
+ const view = createFluidRenderer(THREE, { scene, fluids, quality: 'medium', envMap })
104
+ view.bindContainer('copo', objetoDoCopo, { innerOffset: [0, 0.005, 0] })
105
+ // a cada frame: fluids.setTransform(...); fluids.update(dt); view.update(dt)
106
+ ```
@@ -0,0 +1,68 @@
1
+ import * as THREE_NS from 'three';
2
+ import { Vec3, FluidWorld } from '@adatechnology/game-fluids';
3
+
4
+ /** 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'>;
6
+ /** Detail level. It sets the budgets (stains, streams, particles) and the mesh resolution. */
7
+ type FluidQuality = 'low' | 'medium' | 'high';
8
+ interface FluidRendererOptions {
9
+ /** Where the effects live (stains, streams, particles are added under one group). */
10
+ scene: THREE_NS.Object3D;
11
+ /** The simulation to draw. The renderer only reads its state and listens to `splash` events: it never changes the world. */
12
+ fluids: FluidWorld;
13
+ /** Default `medium`. */
14
+ quality?: FluidQuality;
15
+ /** Environment map the wet surfaces and liquids reflect. When omitted they use `scene.environment` if the host set one. */
16
+ envMap?: THREE_NS.Texture | null;
17
+ /** Pixel scale of the round point sprites (about half the canvas height in pixels). Default 600. */
18
+ pointScale?: number;
19
+ }
20
+ interface BindOptions {
21
+ /**
22
+ * Offset, in the bound object's space, of the container's inner bottom centre (the origin of the shape given to `addContainer`).
23
+ * The world transform you give `fluids.setTransform` must be that point's world position and the object's rotation. Default `[0, 0, 0]`.
24
+ */
25
+ innerOffset?: Vec3;
26
+ }
27
+ /** Counts of what is alive right now, plus the budgets of the chosen quality. */
28
+ interface FluidRendererStats {
29
+ quality: FluidQuality;
30
+ /** Bound containers with a visible liquid volume. */
31
+ containers: number;
32
+ /** Streams drawn, including the ones that are breaking into drops after the world ended them. */
33
+ streams: number;
34
+ /** Stain meshes in use (a stain uses an outer film and an inner glossy wet fan). */
35
+ stains: number;
36
+ /** Droplet and steam particles alive. */
37
+ particles: number;
38
+ /** Triangles currently drawn by the fluids. */
39
+ triangles: number;
40
+ budget: {
41
+ stains: number;
42
+ streams: number;
43
+ particles: number;
44
+ };
45
+ }
46
+ interface FluidRenderer {
47
+ /** Draw the liquid of a world container inside `object` (a volume clipped by the liquid surface, plus steam). */
48
+ bindContainer(containerId: string, object: THREE_NS.Object3D, options?: BindOptions): void;
49
+ unbindContainer(containerId: string): void;
50
+ /** Advance the visuals (call it after `fluids.update(dt)`). */
51
+ update(dt: number): void;
52
+ /** Remove every stain, stream and particle (the bindings stay). */
53
+ clear(): void;
54
+ /** Remove everything from the scene and release geometries and materials. */
55
+ dispose(): void;
56
+ stats(): FluidRendererStats;
57
+ }
58
+
59
+ /**
60
+ * Draws a `FluidWorld` with three.js. It only reads the world state (plus `splash` events for droplets), so a LAN client that receives the
61
+ * same world state draws the same liquids.
62
+ */
63
+ declare function createFluidRenderer(THREE: ThreeLib, options: FluidRendererOptions): FluidRenderer;
64
+
65
+ /** Periodic Catmull-Rom of the 16 front radii: a smooth outline with `subdiv` points per sample. */
66
+ declare const smoothOutline: (front: number[], subdiv: number, out?: number[]) => number[];
67
+
68
+ export { type BindOptions, type FluidQuality, type FluidRenderer, type FluidRendererOptions, type FluidRendererStats, type ThreeLib, createFluidRenderer, smoothOutline };