@woosh/meep-engine 2.48.12 → 2.48.13

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.
Files changed (27) hide show
  1. package/package.json +1 -1
  2. package/src/core/collection/queue/Deque.d.ts +9 -0
  3. package/src/core/collection/queue/Deque.js +3 -0
  4. package/src/core/collection/queue/Deque.spec.js +51 -0
  5. package/src/core/math/noise/create_noise_2d.js +193 -0
  6. package/src/engine/Engine.js +9 -5
  7. package/src/engine/asset/AssetManager.d.ts +10 -5
  8. package/src/engine/asset/AssetManager.js +22 -9
  9. package/src/engine/asset/PendingAsset.js +6 -6
  10. package/src/engine/asset/loaders/AssetLoader.d.ts +7 -2
  11. package/src/engine/asset/loaders/AssetLoader.js +17 -15
  12. package/src/engine/asset/loaders/GLTFAssetLoader.js +1 -1
  13. package/src/engine/asset/loaders/SoundAssetLoader.js +3 -5
  14. package/src/engine/development/performance/MetricStatistics.js +7 -5
  15. package/src/engine/development/performance/RingBufferMetric.js +2 -2
  16. package/src/engine/ecs/foliage/ecs/InstancedMeshUtils.js +14 -1
  17. package/src/engine/ecs/gui/GUIElementSystem.d.ts +1 -1
  18. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +3 -21
  19. package/src/engine/graphics/ecs/camera/pp/PerfectPanner.js +4 -2
  20. package/src/engine/graphics/texture/3d/SingleChannelSampler3D.js +146 -0
  21. package/src/engine/graphics/texture/3d/scs3d_read_2d_slice.js +26 -0
  22. package/src/engine/graphics/texture/sampler/Sampler2D.js +2 -2
  23. package/src/engine/physics/fluid/FluidField.js +153 -1
  24. package/src/engine/physics/fluid/prototype.js +201 -0
  25. package/src/engine/physics/fluid/solver/v3_grid_apply_diffusion.js +67 -0
  26. package/src/generation/filtering/numeric/complex/CellFilterGaussianBlur.js +17 -12
  27. package/src/generation/filtering/numeric/complex/CellFilterSimplexNoise.js +14 -10
@@ -99,13 +99,10 @@ class ClingToTerrainSystem extends System {
99
99
  * @param {int} entityId
100
100
  */
101
101
  link(cling, transform, entityId) {
102
- const self = this;
103
102
 
104
- function update() {
105
- self.requestUpdate(entityId);
106
- }
103
+ const update = () => this.requestUpdate(entityId);
107
104
 
108
- self.data[entityId] = {
105
+ this.data[entityId] = {
109
106
  update,
110
107
  transform,
111
108
  component: cling
@@ -139,21 +136,6 @@ class ClingToTerrainSystem extends System {
139
136
  this.updateQueue.remove(entityId);
140
137
  }
141
138
 
142
- reset() {
143
- this.updateQueue.clear();
144
-
145
- const data = this.data;
146
- for (let entity in data) {
147
- if (!data.hasOwnProperty(entity)) {
148
- continue;
149
- }
150
- const datum = data[entity];
151
- deregister(datum);
152
- }
153
-
154
- this.data = [];
155
- }
156
-
157
139
  update(timeDelta) {
158
140
  const em = this.entityManager;
159
141
 
@@ -192,7 +174,7 @@ class ClingToTerrainSystem extends System {
192
174
  the entity is gone
193
175
  */
194
176
 
195
- console.warn("ClingToTerrain component was in the update queue, but no data found. Skipping");
177
+ //console.warn("ClingToTerrain component was in the update queue, but no data found. Skipping");
196
178
  continue;
197
179
  }
198
180
 
@@ -80,6 +80,8 @@ export class PerfectPanner {
80
80
 
81
81
  const ray = make_ray_from_viewport_position(engine, new Vector2(viewport_x, viewport_y));
82
82
 
83
+ // console.log(`Ray origin:${ray.origin}, direction:${ray.direction}`);
84
+
83
85
  this.#camera.rayPlaneIntersection(
84
86
  out,
85
87
  ray.origin.x, ray.origin.y, ray.origin.z,
@@ -93,7 +95,7 @@ export class PerfectPanner {
93
95
  * @param {Vector2} screen_position
94
96
  */
95
97
  update(screen_position) {
96
- //console.log(`PerfectPanner/update: ${screen_position}`)
98
+ // console.log(`PerfectPanner/update: ${screen_position}`)
97
99
 
98
100
  const C = this.#grab_world_reference;
99
101
  const A = this.#grab_eye_position;
@@ -121,7 +123,7 @@ export class PerfectPanner {
121
123
  }
122
124
 
123
125
  start(screen_position) {
124
- //console.log(`PerfectPanner/start: ${screen_position}`)
126
+ // console.log(`PerfectPanner/start: ${screen_position}`)
125
127
 
126
128
  // obtain reference point
127
129
 
@@ -0,0 +1,146 @@
1
+ import { clamp } from "../../../../core/math/clamp.js";
2
+ import { lerp } from "../../../../core/math/lerp.js";
3
+
4
+ export class SingleChannelSampler3D {
5
+
6
+ data = new Float32Array(0)
7
+ #resolution = [0, 0, 0]
8
+
9
+ set resolution(v) {
10
+ const width = v[0];
11
+ const height = v[1];
12
+ const depth = v[2];
13
+
14
+ const old_width = this.#resolution[0];
15
+ const old_height = this.#resolution[1];
16
+ const old_depth = this.#resolution[2];
17
+
18
+ if (old_width === width && old_height === height && old_depth === depth) {
19
+ // no change
20
+ return;
21
+ }
22
+
23
+ this.#resolution[0] = width;
24
+ this.#resolution[1] = height;
25
+ this.#resolution[2] = depth
26
+
27
+ const new_data_size = width * height * depth;
28
+
29
+ if (new_data_size !== old_width * old_height * old_depth) {
30
+
31
+ this.data = new Float32Array(new_data_size);
32
+
33
+ }
34
+ }
35
+
36
+ get resolution() {
37
+ return this.#resolution;
38
+ }
39
+
40
+ /**
41
+ *
42
+ * @param {number} x
43
+ * @param {number} y
44
+ * @param {number} z
45
+ * @param {number} value
46
+ */
47
+ write(x, y, z, value) {
48
+ const resolution = this.#resolution;
49
+
50
+ const res_x = resolution[0];
51
+ const res_y = resolution[1];
52
+
53
+ this.data[z * res_y * res_x + y * res_x + x] = value;
54
+ }
55
+
56
+ /**
57
+ *
58
+ * @param {number} x
59
+ * @param {number} y
60
+ * @param {number} z
61
+ * @returns {number}
62
+ */
63
+ sample_channel_linear(x, y, z) {
64
+ const resolution = this.#resolution;
65
+
66
+ const res_x = resolution[0];
67
+ const res_y = resolution[1];
68
+ const res_z = resolution[2];
69
+
70
+ const x_max = res_x - 1;
71
+ const y_max = res_y - 1;
72
+ const z_max = res_z - 1;
73
+
74
+ const x_clamped = clamp(x, 0, x_max);
75
+ const y_clamped = clamp(y, 0, y_max);
76
+ const z_clamped = clamp(z, 0, z_max);
77
+
78
+ const x0 = x_clamped | 0;
79
+ const y0 = y_clamped | 0;
80
+ const z0 = z_clamped | 0;
81
+
82
+ const f_x = x_clamped - x0;
83
+ const f_y = y_clamped - y0;
84
+ const f_z = z_clamped - z0;
85
+
86
+ const x1 = x_clamped === x0 ? x0 : x0 + 1;
87
+ const y1 = y_clamped === y0 ? y0 : y0 + 1;
88
+ const z1 = z_clamped === z0 ? z0 : z0 + 1;
89
+
90
+ // get 8 points
91
+ const data = this.data;
92
+
93
+ const z0_offset = z0 * (res_x * res_y);
94
+
95
+ const y0_offset = y0 * res_x;
96
+
97
+ const x0_offset = x0;
98
+ const x1_offset = x1;
99
+
100
+ const x0y0z0 = data[z0_offset + y0_offset + x0_offset];
101
+ const x1y0z0 = data[z0_offset + y0_offset + x1_offset];
102
+
103
+ const y1_offset = y1 * res_x;
104
+
105
+ const x0y1z0 = data[z0_offset + y1_offset + x0_offset];
106
+ const x1y1z0 = data[z0_offset + y1_offset + x1_offset];
107
+
108
+ const z1_offset = z1 * (res_x * res_y);
109
+
110
+ const x0y0z1 = data[z1_offset + y0_offset + x0_offset];
111
+ const x1y0z1 = data[z1_offset + y0_offset + x1_offset];
112
+
113
+ const x0y1z1 = data[z1_offset + y1_offset + x0_offset];
114
+ const x1y1z1 = data[z1_offset + y1_offset + x1_offset];
115
+
116
+ // filter on z0
117
+ const lerp_z0_y0 = lerp(x0y0z0, x1y0z0, f_x);
118
+ const lerp_z0_y1 = lerp(x0y1z0, x1y1z0, f_x);
119
+ const lerp_z0 = lerp(lerp_z0_y0, lerp_z0_y1, f_y);
120
+
121
+ // filter on z1
122
+ const lerp_z1_y0 = lerp(x0y0z1, x1y0z1, f_x);
123
+ const lerp_z1_y1 = lerp(x0y1z1, x1y1z1, f_x);
124
+ const lerp_z1 = lerp(lerp_z1_y0, lerp_z1_y1, f_y);
125
+
126
+ return lerp(lerp_z0, lerp_z1, f_z);
127
+ }
128
+
129
+ /**
130
+ *
131
+ * @param {SingleChannelSampler3D} other
132
+ */
133
+ copy(other) {
134
+ this.resolution = other.#resolution;
135
+
136
+ this.data.set(other.data);
137
+ }
138
+
139
+ clone() {
140
+ const r = new SingleChannelSampler3D();
141
+
142
+ r.copy(this);
143
+
144
+ return r;
145
+ }
146
+ }
@@ -0,0 +1,26 @@
1
+ import { array_copy } from "../../../../core/collection/array/copyArray.js";
2
+
3
+ /**
4
+ *
5
+ * @param {SingleChannelSampler3D} source
6
+ * @param {Sampler2D} output
7
+ * @param {number} z_index
8
+ */
9
+ export function scs3d_read_2d_slice(source, output, z_index) {
10
+ const source_resolution = source.resolution;
11
+
12
+ const source_width = source_resolution[0];
13
+ const source_height = source_resolution[1];
14
+
15
+ if (source_width !== output.width || source_height !== output.height) {
16
+ throw new Error('Incompatible resolutions');
17
+ }
18
+
19
+ if (output.itemSize !== 1) {
20
+ throw new Error(`Expected output to have 1 channel, instead got ${output.itemSize}`);
21
+ }
22
+
23
+ const slice_size = source_width * source_height;
24
+
25
+ array_copy(source.data, slice_size * z_index, output.data, 0, slice_size);
26
+ }
@@ -474,14 +474,14 @@ export class Sampler2D {
474
474
  //
475
475
  let x1, y1;
476
476
 
477
- if (clamped_x === x0 || x0 >= x_max) {
477
+ if (clamped_x === x0) {
478
478
  x1 = x0;
479
479
  } else {
480
480
  x1 = x0 + 1;
481
481
  }
482
482
 
483
483
 
484
- if (clamped_y === y0 || y0 >= y_max) {
484
+ if (clamped_y === y0) {
485
485
  y1 = y0;
486
486
  } else {
487
487
  y1 = y0 + 1;
@@ -1,9 +1,161 @@
1
+ import { clamp } from "../../../core/math/clamp.js";
2
+ import { lerp } from "../../../core/math/lerp.js";
3
+
4
+ /**
5
+ *
6
+ * @param {Float32Array} data
7
+ * @param {number[]} resolution
8
+ */
9
+ function v3_grid_apply_advection_forward(data, resolution) {
10
+
11
+ }
12
+
13
+ /**
14
+ *
15
+ * @param {Float32Array} data
16
+ * @param {number[]} resolution
17
+ */
18
+ function v3_grid_apply_advection_reverse(data, resolution) {
19
+
20
+ }
21
+
1
22
  /**
2
23
  * Represents a space where fluid simulation happens
3
24
  *
4
25
  * @see 2019 GDC talk by Runard Rupert "Wind Simulation in God of War" https://www.youtube.com/watch?v=dDgyBKkSf7A
5
26
  * @see Inspired by GDC talk "Interactive Wind and Vegetation in 'God of War'" - https://www.youtube.com/watch?v=MKX45_riWQA
6
27
  */
7
- export class FluidField{
28
+ export class FluidField {
29
+ data = new Float32Array(0)
30
+ #size = [0, 0, 0]
31
+ #resolution = [0, 0, 0]
32
+
33
+
34
+ build() {
35
+ this.data = new Float32Array(
36
+ this.#resolution[0] * this.#resolution[1] * this.#resolution[2] * 3
37
+ );
38
+ }
39
+
40
+ /**
41
+ *
42
+ * @param {number[]} out
43
+ * @param {number} u
44
+ * @param {number} v
45
+ * @param {number} w
46
+ */
47
+ sample_linear_uv(out, u, v, w) {
48
+ const max_x = this.#resolution[0] - 1;
49
+ const max_y = this.#resolution[1] - 1;
50
+ const max_z = this.#resolution[2] - 1;
51
+
52
+ this.sample_linear(out, u * max_x, v * max_y, w * max_z);
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param {number[]} out
58
+ * @param {number} x
59
+ * @param {number} y
60
+ * @param {number} z
61
+ */
62
+ sample_linear(out, x, y, z) {
63
+ out[0] = this.sample_channel_linear(x, y, z, 0);
64
+ out[1] = this.sample_channel_linear(x, y, z, 1);
65
+ out[2] = this.sample_channel_linear(x, y, z, 2);
66
+ }
67
+
68
+ /**
69
+ *
70
+ * @param {number} x
71
+ * @param {number} y
72
+ * @param {number} z
73
+ * @param {number} channel
74
+ * @returns {number}
75
+ */
76
+ sample_channel_linear(x, y, z, channel) {
77
+ const resolution = this.#resolution;
78
+
79
+ const res_x = resolution[0];
80
+ const res_y = resolution[1];
81
+ const res_z = resolution[2];
82
+
83
+ const x_max = res_x - 1;
84
+ const y_max = res_y - 1;
85
+ const z_max = res_z - 1;
86
+
87
+ const x_clamped = clamp(x, 0, x_max);
88
+ const y_clamped = clamp(y, 0, y_max);
89
+ const z_clamped = clamp(z, 0, z_max);
90
+
91
+ const x0 = x_clamped | 0;
92
+ const y0 = y_clamped | 0;
93
+ const z0 = z_clamped | 0;
94
+
95
+ const f_x = x_clamped - x0;
96
+ const f_y = y_clamped - y0;
97
+ const f_z = z_clamped - z0;
98
+
99
+ const x1 = x_clamped === x0 ? x0 : x0 + 1;
100
+ const y1 = y_clamped === y0 ? y0 : y0 + 1;
101
+ const z1 = z_clamped === z0 ? z0 : z0 + 1;
102
+
103
+ // get 8 points
104
+ const data = this.data;
105
+
106
+ const z0_offset = z0 * (res_x * res_y) * 3;
107
+
108
+ const y0_offset = y0 * res_x * 3;
109
+
110
+ const x0_offset = x0 * 3 + channel;
111
+ const x1_offset = x1 * 3 + channel;
112
+
113
+ const x0y0z0 = data[z0_offset + y0_offset + x0_offset];
114
+ const x1y0z0 = data[z0_offset + y0_offset + x1_offset];
115
+
116
+ const y1_offset = y1 * res_x * 3;
117
+
118
+ const x0y1z0 = data[z0_offset + y1_offset + x0_offset];
119
+ const x1y1z0 = data[z0_offset + y1_offset + x1_offset];
120
+
121
+ const z1_offset = z1 * (res_x * res_y) * 3;
122
+
123
+ const x0y0z1 = data[z1_offset + y0_offset + x0_offset];
124
+ const x1y0z1 = data[z1_offset + y0_offset + x1_offset];
125
+
126
+ const x0y1z1 = data[z1_offset + y1_offset + x0_offset];
127
+ const x1y1z1 = data[z1_offset + y1_offset + x1_offset];
128
+
129
+ // filter on z0
130
+ const lerp_z0_y0 = lerp(x0y0z0, x1y0z0, f_x);
131
+ const lerp_z0_y1 = lerp(x0y1z0, x1y1z0, f_x);
132
+ const lerp_z0 = lerp(lerp_z0_y0, lerp_z0_y1, f_y);
133
+
134
+ // filter on z1
135
+ const lerp_z1_y0 = lerp(x0y0z1, x1y0z1, f_x);
136
+ const lerp_z1_y1 = lerp(x0y1z1, x1y1z1, f_x);
137
+ const lerp_z1 = lerp(lerp_z1_y0, lerp_z1_y1, f_y);
138
+
139
+ return lerp(lerp_z0, lerp_z1, f_z);
140
+ }
141
+
142
+ set size(v) {
143
+ const width = v[0];
144
+ const height = v[1];
145
+ const depth = v[2];
146
+
147
+ this.#size[0] = width;
148
+ this.#size[1] = height;
149
+ this.#size[2] = depth
150
+ }
151
+
152
+ set resolution(v) {
153
+ const width = v[0];
154
+ const height = v[1];
155
+ const depth = v[2];
8
156
 
157
+ this.#resolution[0] = width;
158
+ this.#resolution[1] = height;
159
+ this.#resolution[2] = depth
160
+ }
9
161
  }
@@ -0,0 +1,201 @@
1
+ import { EngineHarness } from "../../EngineHarness.js";
2
+ import { SingleChannelSampler3D } from "../../graphics/texture/3d/SingleChannelSampler3D.js";
3
+ import GUIElementSystem from "../../ecs/gui/GUIElementSystem.js";
4
+ import EntityBuilder from "../../ecs/EntityBuilder.js";
5
+ import GUIElement from "../../ecs/gui/GUIElement.js";
6
+ import EmptyView from "../../../view/elements/EmptyView.js";
7
+ import { Sampler2D } from "../../graphics/texture/sampler/Sampler2D.js";
8
+ import { CanvasView } from "../../../view/elements/CanvasView.js";
9
+ import { float2uint8 } from "../../../core/binary/float2uint8.js";
10
+ import { clamp } from "../../../core/math/clamp.js";
11
+ import { randomIntegerBetween } from "../../../core/math/random/randomIntegerBetween.js";
12
+ import { seededRandom } from "../../../core/math/random/seededRandom.js";
13
+ import { BehaviorComponent } from "../../intelligence/behavior/ecs/BehaviorComponent.js";
14
+ import { BehaviorSystem } from "../../intelligence/behavior/ecs/BehaviorSystem.js";
15
+ import { v3_grid_apply_diffusion } from "./solver/v3_grid_apply_diffusion.js";
16
+ import { max2 } from "../../../core/math/max2.js";
17
+ import { MetricCollection } from "../../development/performance/MetricCollection.js";
18
+ import { PeriodicConsolePrinter } from "../../development/performance/monitor/PeriodicConsolePrinter.js";
19
+ import { MetricStatistics } from "../../development/performance/MetricStatistics.js";
20
+
21
+ const harness = new EngineHarness();
22
+
23
+ class SliceVisualiser extends EmptyView {
24
+
25
+ /**
26
+ *
27
+ * @param {SingleChannelSampler3D} source
28
+ */
29
+ constructor(source) {
30
+ super({ classList: ['slice-visualizer'] });
31
+
32
+ this.source = source;
33
+
34
+ const slices = [];
35
+ const views = [];
36
+
37
+ const resolution = source.resolution;
38
+ const resolution_x = resolution[0];
39
+ const resolution_y = resolution[1];
40
+ const resolution_z = resolution[2];
41
+
42
+ const slice_size = resolution_x * resolution_y;
43
+
44
+ /**
45
+ *
46
+ * @type {ImageData|null}
47
+ */
48
+ let imageData = null;
49
+
50
+ for (let i = 0; i < resolution_z; i++) {
51
+ slices[i] = new Sampler2D(new Float32Array(source.data.buffer, i * slice_size * 4, slice_size), 1, resolution_x, resolution_y);
52
+
53
+ const child = new CanvasView();
54
+ child.size.set(resolution_x, resolution_y);
55
+
56
+ if (imageData === null) {
57
+ imageData = child.context2d.getImageData(0, 0, resolution_x, resolution_y);
58
+ }
59
+
60
+ child.css({
61
+ position: "absolute",
62
+ left: 0,
63
+ top: 0
64
+ });
65
+
66
+ views[i] = child;
67
+
68
+ this.addChild(child);
69
+ }
70
+
71
+ this.draw = () => {
72
+
73
+ for (let i = 0; i < resolution_z; i++) {
74
+ const view = views[i];
75
+ const slice = slices[i];
76
+
77
+ for (let j = 0; j < slice_size; j++) {
78
+ const j4 = j * 4;
79
+
80
+
81
+ const uint8_value = float2uint8(slice.data[j]);
82
+
83
+ imageData.data[j4] = clamp(uint8_value, 0, 255);
84
+ imageData.data[j4 + 3] = 255;
85
+ }
86
+
87
+ view.context2d.putImageData(imageData, 0, 0);
88
+ }
89
+
90
+ };
91
+
92
+ const layout = () => {
93
+ const width = this.size.x;
94
+
95
+ const grid_width = max2(1, Math.floor(width / resolution_x));
96
+
97
+ for (let i = 0; i < resolution_z; i++) {
98
+ const view = views[i];
99
+
100
+ const grid_x = i % grid_width;
101
+ const grid_y = Math.floor(i / grid_width);
102
+
103
+ view.position.set(grid_x * resolution_x, grid_y * resolution_y);
104
+ }
105
+ }
106
+
107
+ this.on.linked.add(layout);
108
+ this.size.onChanged.add(layout);
109
+ }
110
+
111
+
112
+ }
113
+
114
+ const metrics = new MetricCollection();
115
+
116
+ metrics.create({ name: 'sim' })
117
+
118
+ /**
119
+ *
120
+ * @param {Engine} engine
121
+ */
122
+ function main(engine) {
123
+
124
+ const ecd = engine.entityManager.dataset;
125
+
126
+ const sampler = new SingleChannelSampler3D();
127
+
128
+ sampler.resolution = [32, 32, 16];
129
+
130
+ const random = seededRandom();
131
+
132
+ sampler.data.fill(1000, 20, 40);
133
+
134
+ for (let i = 0; i < 100; i++) {
135
+
136
+ sampler.write(
137
+ randomIntegerBetween(random, 0, 16),
138
+ randomIntegerBetween(random, 0, 16),
139
+ randomIntegerBetween(random, 0, 4),
140
+ random()
141
+ );
142
+
143
+ }
144
+
145
+
146
+ const slice_view = new SliceVisualiser(sampler);
147
+
148
+ slice_view.size.set(432, 1000);
149
+ slice_view.scale.setScalar(2);
150
+ slice_view.transformOrigin.set(0, 0);
151
+
152
+ const sampler_0 = sampler.clone();
153
+ const sampler_1 = sampler.clone();
154
+
155
+ const ping_pong = [sampler_0, sampler_1];
156
+ let ping_pong_index = 0;
157
+
158
+
159
+ new EntityBuilder()
160
+ .add(BehaviorComponent.looping_function(time_delta => {
161
+
162
+ const source = ping_pong[ping_pong_index % 2];
163
+ const target = ping_pong[(ping_pong_index + 1) % 2];
164
+
165
+ ping_pong_index++;
166
+
167
+ const t0 = performance.now();
168
+
169
+ for (let i = 0; i < 5; i++) {
170
+ v3_grid_apply_diffusion(target.data, source.data, source.resolution);
171
+ }
172
+
173
+ metrics.get('sim').record(performance.now() - t0);
174
+
175
+ sampler.copy(target);
176
+
177
+ slice_view.draw();
178
+
179
+ }))
180
+ .build(ecd);
181
+
182
+ const metric_stats = new MetricStatistics();
183
+
184
+ new PeriodicConsolePrinter(3, () => metrics.list().map(m => {
185
+ metrics.get(m).computeStats(metric_stats);
186
+
187
+ return `${m}: ${metric_stats}`;
188
+ }).join('\n')).start()
189
+
190
+ new EntityBuilder()
191
+ .add(GUIElement.fromView(slice_view))
192
+ .build(ecd);
193
+
194
+ }
195
+
196
+ harness.initialize({
197
+ configuration(config, engine) {
198
+ config.addSystem(new GUIElementSystem(engine.gameView, engine))
199
+ config.addSystem(new BehaviorSystem(engine))
200
+ }
201
+ }).then(main)
@@ -0,0 +1,67 @@
1
+ /**
2
+ *
3
+ * @param {Float32Array} output
4
+ * @param {Float32Array} input
5
+ * @param {number[]} resolution
6
+ */
7
+ export function v3_grid_apply_diffusion(output, input, resolution) {
8
+ const res_x = resolution[0];
9
+ const res_y = resolution[1];
10
+ const res_z = resolution[2];
11
+
12
+ const factor = 1 / 7;
13
+
14
+ let sum = 0;
15
+
16
+ const slice_size = res_y * res_x;
17
+
18
+ for (let z = 0; z < res_z; z++) {
19
+ for (let y = 0; y < res_y; y++) {
20
+ for (let x = 0; x < res_x; x++) {
21
+
22
+ let sample_count = 1;
23
+
24
+ sum = 0;
25
+
26
+ // sample cells around
27
+ if (z > 0) {
28
+ sample_count++;
29
+ sum += input[(z - 1) * slice_size + y * res_x + x]
30
+ }
31
+
32
+ if (y > 0) {
33
+ sample_count++;
34
+ sum += input[z * slice_size + (y - 1) * res_x + x]
35
+ }
36
+
37
+ if (x > 0) {
38
+ sample_count++;
39
+ sum += input[z * slice_size + y * res_x + (x - 1)]
40
+ }
41
+
42
+
43
+ const current = z * slice_size + y * res_x + x;
44
+
45
+ sum += input[current];
46
+
47
+ if (x < res_x - 1) {
48
+ sample_count++;
49
+ sum += input[z * slice_size + y * res_x + (x + 1)]
50
+ }
51
+
52
+ if (y < res_y - 1) {
53
+ sample_count++;
54
+ sum += input[z * slice_size + (y + 1) * res_x + x]
55
+ }
56
+
57
+ if (z < res_z - 1) {
58
+ sample_count++;
59
+ sum += input[(z + 1) * slice_size + y * res_x + x]
60
+ }
61
+
62
+ output[current] = sum / sample_count;
63
+
64
+ }
65
+ }
66
+ }
67
+ }