@woosh/meep-engine 2.33.76 → 2.34.3

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 (62) hide show
  1. package/core/collection/Set.js +0 -61
  2. package/core/collection/array/arrayIndexByEquality.js +20 -0
  3. package/core/collection/array/arraySetDiff.js +42 -0
  4. package/core/collection/array/arraySetSortingDiff.js +67 -0
  5. package/core/collection/array/arraySetSortingDiff.spec.js +74 -0
  6. package/core/collection/array/array_filter_by_multiple.js +36 -0
  7. package/core/collection/array/weightedRandomFromArray.js +9 -5
  8. package/core/collection/list/FilteredListProjection.js +1 -1
  9. package/core/collection/list/List.js +2 -1
  10. package/core/geom/3d/topology/compareEdges.js +16 -0
  11. package/core/geom/3d/topology/compareFaces.js +9 -0
  12. package/core/geom/3d/topology/compareVertices.js +9 -0
  13. package/core/geom/3d/topology/computeTopoMeshVertexDuplicates.js +25 -6
  14. package/core/geom/3d/topology/expandConnectivityByLocality.js +35 -19
  15. package/core/geom/Vector2.js +7 -2
  16. package/core/geom/Vector3.js +2 -2
  17. package/core/geom/v3_angle_between.js +3 -1
  18. package/editor/Editor.js +2 -2
  19. package/editor/view/ecs/components/items/ItemContainerController.stories.js +1 -1
  20. package/engine/EngineHarness.js +0 -1
  21. package/engine/asset/loaders/image/ImageDecoderWorker.js +3 -2
  22. package/engine/asset/loaders/image/ImageRGBADataAsset.js +2 -6
  23. package/engine/asset/loaders/image/ImageRGBADataLoader.js +18 -2
  24. package/engine/asset/loaders/image/codec/NativeImageDecoder.js +2 -1
  25. package/engine/asset/loaders/image/png/PNG.js +3 -2
  26. package/engine/asset/loaders/image/png/PNGReader.js +1 -0
  27. package/engine/ecs/EntityComponentDataset.js +1 -1
  28. package/engine/ecs/storage/BinaryBufferSerializer.js +2 -2
  29. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +9 -0
  30. package/engine/ecs/terrain/ecs/splat/SplatMapping.js +72 -26
  31. package/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +1 -2
  32. package/engine/graphics/ecs/highlight/system/MeshHighlightSystem.d.ts +3 -2
  33. package/engine/graphics/ecs/highlight/system/RenderableHighlightSystem.d.ts +3 -1
  34. package/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.d.ts +8 -0
  35. package/engine/graphics/ecs/highlight/system/ShadedGeometryHighlightSystem.js +180 -0
  36. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +74 -16
  37. package/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +7 -0
  38. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +9 -0
  39. package/engine/graphics/ecs/mesh-v2/prototypeShadedGeometry.js +4 -2
  40. package/engine/graphics/geometry/VertexDataSpec.js +20 -0
  41. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +41 -1
  42. package/engine/graphics/micron/format/MicronGeometry.d.ts +6 -0
  43. package/engine/graphics/micron/format/MicronGeometry.js +156 -4
  44. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +10 -1
  45. package/engine/graphics/particles/particular/engine/parameter/ParticleParameter.js +7 -2
  46. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +3 -2
  47. package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +7 -3
  48. package/engine/graphics/texture/sampler/Sampler2D.js +2 -0
  49. package/engine/graphics/texture/sampler/downsampleSampler2D.js +3 -1
  50. package/engine/graphics/texture/sampler/genericResampleSampler2D.js +67 -0
  51. package/engine/graphics/texture/sampler/scaleSampler2D.js +29 -35
  52. package/engine/graphics/texture/sampler/upsampleSampler2D.js +47 -0
  53. package/generation/filtering/numeric/CellFilterReadGridLayer.js +2 -2
  54. package/generation/filtering/numeric/math/CellFilterSaturate.js +24 -0
  55. package/package.json +1 -1
  56. package/samples/terrain/editor.js +346 -0
  57. package/samples/terrain/from_image.js +289 -0
  58. package/samples/terrain/from_image_2.js +219 -0
  59. package/view/controller/controls/NativeListController.js +6 -3
  60. package/view/elements/radial/RadialMenu.js +1 -1
  61. package/engine/graphics/texture/sampler/debug/paintSamplerOnHTML.js +0 -32
  62. package/generation/theme/TerrainThemeApplicator.js +0 -6
@@ -13,6 +13,9 @@ import { build_geometry_info } from "./build_geometry_info.js";
13
13
  import { build_leaf_patches_metis } from "./clustering/build_leaf_patches_metis.js";
14
14
  import { compute_buffer_geometry_byte_size } from "../../geometry/buffered/compute_buffer_geometry_byte_size.js";
15
15
  import { deinterleaveBufferGeometry } from "../../geometry/buffered/deinterleaveBufferGeometry.js";
16
+ import { AABB3 } from "../../../../core/bvh2/aabb3/AABB3.js";
17
+ import { min2 } from "../../../../core/math/min2.js";
18
+ import { max2 } from "../../../../core/math/max2.js";
16
19
 
17
20
  /**
18
21
  * Returns current time in milliseconds
@@ -100,6 +103,38 @@ function attributes_from_buffer_geometry(buffer_geometry) {
100
103
 
101
104
  const PRINT_VERBOSE_STATS = false;
102
105
 
106
+ /**
107
+ *
108
+ * @param {AABB3} result
109
+ * @param {number[]|ArrayLike<number>} input
110
+ * @param {number} input_length
111
+ */
112
+ function aabb3_from_v3_array(result, input, input_length) {
113
+ let x0 = Infinity;
114
+ let y0 = Infinity;
115
+ let z0 = Infinity;
116
+
117
+ let x1 = -Infinity;
118
+ let y1 = -Infinity;
119
+ let z1 = -Infinity;
120
+
121
+ for (let i = 0; i < input_length; i += 3) {
122
+ const x = input[i];
123
+ const y = input[i + 1];
124
+ const z = input[i + 2];
125
+
126
+ x0 = min2(x, x0);
127
+ y0 = min2(y, y0);
128
+ z0 = min2(z, z0);
129
+
130
+ x1 = max2(x, x1);
131
+ y1 = max2(y, y1);
132
+ z1 = max2(z, z1);
133
+ }
134
+
135
+ result.setBounds(x0, y0, z0, x1, y1, z1);
136
+ }
137
+
103
138
  /**
104
139
  *
105
140
  * @param {THREE.BufferGeometry} input
@@ -118,7 +153,12 @@ export async function buildMicronGeometryFromBufferGeometry(input) {
118
153
  const vertex_position_array = position_attribute.array;
119
154
 
120
155
  topoMesh.build(vertex_position_array, face_index_array);
121
- expandConnectivityByLocality(topoMesh);
156
+
157
+ // compute aabb
158
+ const geometry_aabb = new AABB3();
159
+ aabb3_from_v3_array(geometry_aabb, vertex_position_array, vertex_position_array.length);
160
+
161
+ expandConnectivityByLocality(topoMesh, geometry_aabb);
122
162
 
123
163
  /**
124
164
  * Used to generate neighbourhood assignment
@@ -1,9 +1,15 @@
1
1
  import {BufferGeometry} from "three";
2
2
  import {VertexDataSpec} from "../../geometry/VertexDataSpec";
3
+ import {SurfacePoint3} from "../../../../core/geom/3d/SurfacePoint3";
3
4
 
4
5
  export class MicronGeometry {
5
6
  vertex_spec: VertexDataSpec
6
7
  public proxy: BufferGeometry
7
8
 
8
9
  initialize(): void
10
+
11
+ raycast(
12
+ origin_x: number, origin_y: number, origin_z: number,
13
+ direction_x: number, direction_y: number, direction_z: number
14
+ ): SurfacePoint3[]
9
15
  }
@@ -1,5 +1,8 @@
1
1
  import { VertexDataSpec } from "../../geometry/VertexDataSpec.js";
2
2
  import { assert } from "../../../../core/assert.js";
3
+ import { intersectRay } from "../../../../core/bvh2/aabb3/AABB3Math.js";
4
+ import { computeTriangleRayIntersection } from "../../../../core/geom/3d/triangle/computeTriangleRayIntersection.js";
5
+ import { SurfacePoint3 } from "../../../../core/geom/3d/SurfacePoint3.js";
3
6
 
4
7
  let id_counter = 0;
5
8
 
@@ -55,6 +58,151 @@ export class MicronGeometry {
55
58
  this.__proxy = null;
56
59
  }
57
60
 
61
+ /**
62
+ *
63
+ * @param {number} origin_x
64
+ * @param {number} origin_y
65
+ * @param {number} origin_z
66
+ * @param {number} direction_x
67
+ * @param {number} direction_y
68
+ * @param {number} direction_z
69
+ * @returns {SurfacePoint3[]}
70
+ */
71
+ raycast(
72
+ origin_x, origin_y, origin_z,
73
+ direction_x, direction_y, direction_z
74
+ ) {
75
+
76
+ /**
77
+ *
78
+ * @type {SurfacePoint3[]}
79
+ */
80
+ const hits = [];
81
+
82
+ let stack_cursor = 1;
83
+ const stack = [this.root];
84
+
85
+ const spec = this.vertex_spec;
86
+
87
+ const position_attribute_index = spec.getAttributeIndexByName('position');
88
+
89
+ // check that position attribute actually exists
90
+ assert.isNonNegativeInteger(position_attribute_index, 'position_attribute_index');
91
+
92
+ const temp_hit = new SurfacePoint3();
93
+
94
+ while (stack_cursor > 0) {
95
+ stack_cursor--;
96
+
97
+ const patch = stack[stack_cursor];
98
+
99
+ // using group bounding box because it's conservative, as we want to be able to hit LOD_0
100
+ const bb = patch.group_bounding_box;
101
+
102
+ const bb_intersection_exists = intersectRay(
103
+ bb[0], bb[1], bb[2], bb[3], bb[4], bb[5],
104
+ origin_x, origin_y, origin_z,
105
+ direction_x, direction_y, direction_z
106
+ );
107
+
108
+ if (!bb_intersection_exists) {
109
+ // no intersection
110
+ continue;
111
+ }
112
+
113
+ if (patch.lod === 0) {
114
+ // LOD 0 - source geometry
115
+
116
+ // raycast against geometry
117
+ const indices = patch.face_indices;
118
+ const index_count = indices.length;
119
+
120
+ const vertices = patch.vertex_attribute_arrays[position_attribute_index];
121
+
122
+ for (let i = 0; i < index_count;) {
123
+ const a = indices[i++];
124
+ const b = indices[i++];
125
+ const c = indices[i++];
126
+
127
+
128
+ const a_address = a * 3;
129
+ const b_address = b * 3;
130
+ const c_address = c * 3;
131
+
132
+
133
+ const ax = vertices[a_address];
134
+ const ay = vertices[a_address + 1];
135
+ const az = vertices[a_address + 2];
136
+
137
+ const bx = vertices[b_address];
138
+ const by = vertices[b_address + 1];
139
+ const bz = vertices[b_address + 2];
140
+
141
+ const cx = vertices[c_address];
142
+ const cy = vertices[c_address + 1];
143
+ const cz = vertices[c_address + 2];
144
+
145
+ if (computeTriangleRayIntersection(
146
+ temp_hit,
147
+ origin_x, origin_y, origin_z,
148
+ direction_x, direction_y, direction_z,
149
+ ax, ay, az,
150
+ bx, by, bz,
151
+ cx, cy, cz
152
+ )) {
153
+
154
+ const contact = new SurfacePoint3();
155
+
156
+ contact.copy(temp_hit);
157
+
158
+ hits.push(contact);
159
+
160
+ }
161
+ }
162
+
163
+ } else {
164
+ // refine more
165
+ const children = patch.children;
166
+
167
+ const child_count = children.length;
168
+
169
+ for (let i = 0; i < child_count; i++) {
170
+ const child = children[i];
171
+
172
+ // push to stack
173
+ stack[stack_cursor] = child;
174
+
175
+ stack_cursor++;
176
+ }
177
+ }
178
+ }
179
+
180
+ return hits;
181
+ }
182
+
183
+ /**
184
+ *
185
+ * @param {number} lod
186
+ * @returns {number}
187
+ */
188
+ computeLodFaceCount(lod) {
189
+ let r = 0;
190
+
191
+ const patches = this.patches;
192
+ const patch_count = patches.length;
193
+ for (let i = 0; i < patch_count; i++) {
194
+ const patch = patches[i];
195
+
196
+ if (patch.lod !== lod) {
197
+ continue;
198
+ }
199
+
200
+ r += patch.polygon_count;
201
+ }
202
+
203
+ return r;
204
+ }
205
+
58
206
  get patches() {
59
207
  return this.__patches;
60
208
  }
@@ -84,17 +232,21 @@ export class MicronGeometry {
84
232
 
85
233
  /**
86
234
  * Build geometry proxy from the patches
87
- * @param {number} percent percentage of the original geometry to keep
235
+ * @param {number} fraction percentage of the original geometry to keep
236
+ * @param {number} min minimum number of triangles to keep, this will be respected first before percentage value
88
237
  */
89
- buildProxy(percent) {
238
+ buildProxy(fraction, min) {
90
239
  throw new Error('Not Implemented');
91
240
  }
92
241
 
93
242
  computeByteSize() {
94
243
  let result = 0;
95
244
 
96
- for (let i = 0; i < this.__patches.length; i++) {
97
- result += this.__patches[i].computeByteSize();
245
+ const patches = this.__patches;
246
+ const patch_count = patches.length;
247
+
248
+ for (let i = 0; i < patch_count; i++) {
249
+ result += patches[i].computeByteSize();
98
250
  }
99
251
 
100
252
  return result;
@@ -98,7 +98,16 @@ class GLTFAssetTransformer extends AssetTransformer {
98
98
 
99
99
  const base = computePathDirectory(ad.path);
100
100
 
101
- const micron_data = await this.__am.promise(`${base}/${micron_uri}`, ASSET_TYPE_ARRAY_BUFFER);
101
+ let micron_data;
102
+
103
+ const micron_path = `${base}/${micron_uri}`;
104
+
105
+ try {
106
+ micron_data = await this.__am.promise(micron_path, ASSET_TYPE_ARRAY_BUFFER);
107
+ } catch (e) {
108
+ console.error(`Failed to load micron geometry '${micron_path}' referenced in GLTF (will skip): ${e}`);
109
+ return;
110
+ }
102
111
 
103
112
  // deserialize micron data
104
113
  const buffer = BinaryBuffer.fromArrayBuffer(micron_data.create());
@@ -173,8 +173,12 @@ export class ParticleParameter {
173
173
  //lock default track
174
174
  this.defaultTrackValue.disableWriteMode();
175
175
 
176
- for (i = 0; i < this.trackCount; i++) {
177
- track = this.tracks.get(i);
176
+ const track_count = this.trackCount;
177
+ const tracks = this.tracks;
178
+
179
+ for (i = 0; i < track_count; i++) {
180
+
181
+ track = tracks.get(i);
178
182
 
179
183
  if (track === undefined) {
180
184
  //write the default track into the parameter
@@ -183,6 +187,7 @@ export class ParticleParameter {
183
187
  //lock track
184
188
  track.disableWriteMode();
185
189
  }
190
+
186
191
  }
187
192
 
188
193
  this.computeStatistics();
@@ -433,9 +433,10 @@ async function sample_scene_nam(engine) {
433
433
  "distanceMax": 1000
434
434
  });
435
435
 
436
- // load_model('moicon/20_Dec_2021_Nammo/with_assets(for_alex)/model.gltf', ecd)
437
- load_model('moicon/20_Dec_2021_Nammo/with_assets_micron_conveted/model.gltf', ecd)
436
+ load_model('moicon/20_Dec_2021_Nammo/with_assets(for_alex)/model.gltf', ecd)
437
+ // load_model('moicon/20_Dec_2021_Nammo/with_assets_micron_conveted/model.gltf', ecd)
438
438
  // load_model('moicon/micron_container/model.gltf', ecd)
439
+ // load_model('moicon/test_philippe_jan_2022/test/model.gltf', ecd)
439
440
 
440
441
  const P_SIZE_SMALL = 0.15;
441
442
  const P_SIZE_MEDIUM = 0.6;
@@ -30,13 +30,19 @@ export function query_bvh_frustum_from_texture(
30
30
  let result = 0;
31
31
  let plane_address;
32
32
 
33
+ const binary_node_count = root.getBinaryNodeCount();
34
+
35
+ if (binary_node_count <= 0) {
36
+ // this should not happen
37
+ return 0;
38
+ }
39
+
33
40
  /**
34
41
  * After performing empirical tests, stack-based depth-first traversal turns out faster than using a queue
35
42
  * @type {number}
36
43
  */
37
44
  stack[0] = 0;
38
45
 
39
- const binary_node_count = root.getBinaryNodeCount();
40
46
  const last_valid_index = binary_node_count + root.getLeafNodeCount();
41
47
  const data_view = root.getDataView();
42
48
 
@@ -122,8 +128,6 @@ export function query_bvh_frustum_from_texture(
122
128
 
123
129
  } else if (light_type === 3) {
124
130
  // decal
125
- debugger;
126
-
127
131
  } else {
128
132
  throw new Error(`Unsupported light type ${light_type}`);
129
133
  }
@@ -1374,6 +1374,8 @@ Sampler2D.prototype.traverseCircle = function (centerX, centerY, radius, visitor
1374
1374
  * @param {boolean} [preserveData=true]
1375
1375
  */
1376
1376
  Sampler2D.prototype.resize = function (x, y, preserveData = true) {
1377
+ assert.isNonNegativeInteger(x, 'x');
1378
+ assert.isNonNegativeInteger(y, 'y');
1377
1379
 
1378
1380
  const _w = this.width;
1379
1381
  const _h = this.height;
@@ -56,9 +56,11 @@ export function downsampleSampler2D(input, output) {
56
56
 
57
57
  // accumulate sample
58
58
  for (j = 0; j < sH; j++) {
59
+ const row_index = (sampleOffsetY + j) * iW;
60
+
59
61
  for (i = 0; i < sW; i++) {
60
62
 
61
- const iAddress = ((sampleOffsetX + i) + (sampleOffsetY + j) * iW) * itemSize;
63
+ const iAddress = ((sampleOffsetX + i) + row_index) * itemSize;
62
64
 
63
65
  for (k = 0; k < itemSize; k++) {
64
66
  sample[k] += iData[k + iAddress];
@@ -0,0 +1,67 @@
1
+ import { max2 } from "../../../../core/math/max2.js";
2
+
3
+ /**
4
+ *
5
+ * @param {Sampler2D} source
6
+ * @param {Sampler2D} target
7
+ */
8
+ export function genericResampleSampler2D(source, target) {
9
+ const target_width = target.width;
10
+ const target_height = target.height;
11
+
12
+ // build kernel
13
+
14
+ // TODO add gaussian weights kernel for samples
15
+
16
+ const u_scale = source.width / target_width;
17
+ const v_scale = source.height / target_height;
18
+
19
+ const kernel_width = max2(1, Math.ceil(u_scale));
20
+ const kernel_height = max2(1, Math.ceil(v_scale));
21
+
22
+ const kernel_size = kernel_width * kernel_height;
23
+
24
+ const itemSize = source.itemSize;
25
+
26
+ const sample = new Float32Array(itemSize);
27
+
28
+ let column_index;
29
+ let row_index;
30
+ let k_y;
31
+ let k_x;
32
+ let i;
33
+
34
+ for (column_index = 0; column_index < target_height; column_index++) {
35
+ for (row_index = 0; row_index < target_width; row_index++) {
36
+
37
+ // figure out source offset
38
+ const source_x = row_index * u_scale;
39
+ const source_y = column_index * v_scale;
40
+
41
+ // reset sample
42
+ for (i = 0; i < itemSize; i++) {
43
+ sample[i] = 0;
44
+ }
45
+
46
+ // accumulate kernel samples
47
+ for (k_y = 0; k_y < kernel_height; k_y++) {
48
+
49
+ for (k_x = 0; k_x < kernel_width; k_x++) {
50
+
51
+ for (i = 0; i < itemSize; i++) {
52
+ sample[i] += source.sampleChannelBilinear(source_x + k_x, source_y + k_y, i);
53
+ }
54
+
55
+ }
56
+ }
57
+
58
+ for (i = 0; i < itemSize; i++) {
59
+ // dilute sample
60
+ const channel_value = sample[i] / kernel_size;
61
+ // write out
62
+ target.data[(column_index * target_width + row_index) * itemSize + i] = channel_value;
63
+ }
64
+
65
+ }
66
+ }
67
+ }
@@ -1,47 +1,41 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
+ import { downsampleSampler2D } from "./downsampleSampler2D.js";
3
+ import { Sampler2D } from "./Sampler2D.js";
4
+ import { upsampleSampler2D } from "./upsampleSampler2D.js";
5
+ import { genericResampleSampler2D } from "./genericResampleSampler2D.js";
2
6
 
3
7
  /**
4
8
  *
5
- * @param {Sampler2D} source
6
- * @param {Sampler2D} target
9
+ * @param {Sampler2D} input
10
+ * @param {Sampler2D} output
7
11
  */
8
- export function scaleSampler2D(source, target) {
9
- const targetItemSize = target.itemSize;
12
+ export function scaleSampler2D(input, output) {
13
+ const targetItemSize = output.itemSize;
10
14
 
11
- assert.equal(source.itemSize, targetItemSize, 'both source and target must have the same number of channels');
15
+ assert.equal(input.itemSize, targetItemSize, 'both source and target must have the same number of channels');
12
16
 
13
- const targetWidth = target.width;
14
- const targetHeight = target.height;
15
- const sourceWidth = source.width;
16
- const sourceHeight = source.height;
17
-
18
-
19
- const target_data = target.data;
17
+ const targetWidth = output.width;
18
+ const targetHeight = output.height;
19
+ const sourceWidth = input.width;
20
+ const sourceHeight = input.height;
20
21
 
21
22
  if (sourceWidth === targetWidth && sourceHeight === targetHeight) {
22
- // exact size match, nothing to do
23
- target_data.set(source.data);
24
- return;
25
- }
26
-
27
- const targetRowSize = targetItemSize * targetWidth;
28
-
29
- const source_scale_x = sourceWidth / targetWidth;
30
- const source_scale_y = sourceHeight / targetHeight;
31
-
32
- for (let y = 0; y < targetHeight; y++) {
33
-
34
-
35
- const sample_y = y * source_scale_y;
36
- const target_row_address = y * targetRowSize;
37
-
38
- for (let x = 0; x < targetWidth; x++) {
39
-
40
-
41
- const target_offset = target_row_address + x * targetItemSize;
42
-
43
- source.sampleBilinear(x * source_scale_x, sample_y, target_data, target_offset);
44
-
23
+ // same dimensions, copy directly
24
+ output.data.set(input.data);
25
+ } else if (sourceWidth > targetWidth && sourceHeight > targetHeight) {
26
+ // downscaling
27
+ if (Number.isInteger(sourceWidth / targetWidth) && Number.isInteger(sourceHeight / targetHeight)) {
28
+ // dimensions are multiples of source/target
29
+ downsampleSampler2D(input, output);
30
+ } else {
31
+ // generic downsample
32
+ genericResampleSampler2D(input, output);
45
33
  }
34
+ } else if (sourceWidth < targetWidth && sourceHeight < targetHeight) {
35
+ // upscaling
36
+ upsampleSampler2D(input, output);
37
+ } else {
38
+ // mixed resampling
39
+ genericResampleSampler2D(input, output);
46
40
  }
47
41
  }
@@ -0,0 +1,47 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+
3
+ /**
4
+ *
5
+ * @param {Sampler2D} input
6
+ * @param {Sampler2D} output
7
+ */
8
+ export function upsampleSampler2D(input, output) {
9
+ const targetItemSize = output.itemSize;
10
+
11
+ assert.equal(input.itemSize, targetItemSize, 'both source and target must have the same number of channels');
12
+
13
+ const targetWidth = output.width;
14
+ const targetHeight = output.height;
15
+ const sourceWidth = input.width;
16
+ const sourceHeight = input.height;
17
+
18
+
19
+ const target_data = output.data;
20
+
21
+ if (sourceWidth === targetWidth && sourceHeight === targetHeight) {
22
+ // exact size match, nothing to do
23
+ target_data.set(input.data);
24
+ return;
25
+ }
26
+
27
+ const targetRowSize = targetItemSize * targetWidth;
28
+
29
+ const source_scale_x = sourceWidth / targetWidth;
30
+ const source_scale_y = sourceHeight / targetHeight;
31
+
32
+ for (let y = 0; y < targetHeight; y++) {
33
+
34
+
35
+ const sample_y = y * source_scale_y;
36
+ const target_row_address = y * targetRowSize;
37
+
38
+ for (let x = 0; x < targetWidth; x++) {
39
+
40
+
41
+ const target_offset = target_row_address + x * targetItemSize;
42
+
43
+ input.sampleBilinear(x * source_scale_x, sample_y, target_data, target_offset);
44
+
45
+ }
46
+ }
47
+ }
@@ -36,8 +36,8 @@ export class CellFilterReadGridLayer extends CellFilter {
36
36
  // cache required values
37
37
  this.__sampler = this.__layer.sampler;
38
38
 
39
- this.__u_scale = grid.width / this.__sampler.width;
40
- this.__v_scale = grid.height / this.__sampler.height;
39
+ this.__u_scale = this.__sampler.width / grid.width;
40
+ this.__v_scale = this.__sampler.height / grid.height;
41
41
  }
42
42
 
43
43
  /**
@@ -0,0 +1,24 @@
1
+ import { CellFilterUnaryOperation } from "../../core/CellFilterUnaryOperation.js";
2
+ import { clamp } from "../../../../core/math/clamp.js";
3
+ import { assert } from "../../../../core/assert.js";
4
+
5
+ export class CellFilterSaturate extends CellFilterUnaryOperation {
6
+ operation(v) {
7
+ return clamp(v, 0, 1);
8
+ }
9
+
10
+ /**
11
+ *
12
+ * @param {CellFilter} v
13
+ * @return {CellFilterUnaryOperation}
14
+ */
15
+ static from(v) {
16
+ assert.equal(v.isCellFilter, true, "v.isCellFilter !== true");
17
+
18
+ const r = new CellFilterSaturate();
19
+
20
+ r.source = v;
21
+
22
+ return r;
23
+ }
24
+ }
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.33.76",
8
+ "version": "2.34.3",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",