@woosh/meep-engine 2.34.47 → 2.35.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.
Files changed (56) hide show
  1. package/core/binary/BitSet.js +10 -3
  2. package/core/bvh2/BinaryNode.js +19 -35
  3. package/core/bvh2/aabb3/aabb3_intersects_frustum_array.js +5 -5
  4. package/core/bvh2/aabb3_combined_surface_area.js +21 -0
  5. package/core/bvh2/traversal/queryBinaryNode_FrustumIntersections.js +24 -12
  6. package/core/cache/Cache.d.ts +2 -0
  7. package/core/cache/Cache.js +27 -23
  8. package/core/geom/3d/aabb/aabb3_estimate_projected_area2.js +1 -1
  9. package/core/geom/3d/frustum/frustum_from_projection_matrix_array.js +29 -24
  10. package/core/math/max2.js +2 -6
  11. package/core/math/min2.js +2 -6
  12. package/engine/asset/loaders/geometry/geometry/computeGeometryEquality.js +3 -2
  13. package/engine/asset/loaders/geometry/geometry/computeGeometryHash.js +9 -1
  14. package/engine/ecs/EntityComponentDataset.js +7 -2
  15. package/engine/ecs/foliage/ViewState.js +13 -3
  16. package/engine/ecs/foliage/ecs/Foliage2System.js +15 -3
  17. package/engine/ecs/parent/EntityNode.js +23 -11
  18. package/engine/ecs/transform/Transform.js +15 -13
  19. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +63 -26
  20. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +63 -3
  21. package/engine/graphics/ecs/mesh-v2/render/AbstractRenderAdapter.js +67 -0
  22. package/engine/graphics/ecs/mesh-v2/render/GenericRendererAdapter.js +48 -0
  23. package/engine/graphics/ecs/mesh-v2/render/InstancedRendererAdapter.js +110 -0
  24. package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +98 -0
  25. package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +5 -0
  26. package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +23 -5
  27. package/engine/graphics/ecs/mesh-v2/three_object_to_entity_composition.js +8 -3
  28. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +23 -85
  29. package/engine/graphics/geometry/instancing/rewriteMaterial.js +9 -45
  30. package/engine/graphics/material/optimization/BUFFER_GEOMETRY_UVS.js +10 -0
  31. package/engine/graphics/material/optimization/MaterialDescriptor.js +46 -0
  32. package/engine/graphics/material/optimization/MaterialOptimizationContext.js +470 -201
  33. package/engine/graphics/material/optimization/UvContext.js +18 -0
  34. package/engine/graphics/material/optimization/is_compliant_mesh.js +71 -0
  35. package/engine/graphics/material/optimization/prepare_atlas_texture.js +23 -0
  36. package/engine/graphics/material/optimization/prototypeMaterialOptimizer.js +127 -0
  37. package/engine/graphics/micron/convert_three_object_to_micron.js +0 -2
  38. package/engine/graphics/micron/format/MicronGeometry.js +1 -3
  39. package/engine/graphics/micron/format/MicronGeometryPatch.js +34 -24
  40. package/engine/graphics/micron/format/VirtualGeometry.js +47 -11
  41. package/engine/graphics/micron/format/serialization/MicronGeometryBinarySerializationAdapter.js +22 -0
  42. package/engine/graphics/micron/prototypeVirtualGeometry.js +10 -5
  43. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +14 -55
  44. package/engine/graphics/micron/render/instanced/AttributeDataTexture.js +1 -45
  45. package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +18 -7
  46. package/engine/graphics/micron/render/refinement/RefinementSpec.js +0 -12
  47. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +118 -0
  48. package/engine/graphics/micron/render/v1/MaterialContext.js +3 -2
  49. package/engine/graphics/micron/render/v1/VGThreeRenderer.js +19 -4
  50. package/engine/graphics/render/utils/ThreeBypassRenderer.js +3 -2
  51. package/engine/graphics/texture/normalized_internal_format.js +48 -0
  52. package/engine/graphics/texture/sampler/Sampler2D.js +6 -0
  53. package/engine/graphics/texture/sampler/sampler2d_copy_channel_data.js +25 -0
  54. package/engine/graphics/texture/sampler/scaleSampler2D.js +3 -0
  55. package/engine/graphics/texture/sampler/util/drawSamplerHTML.js +14 -1
  56. package/package.json +1 -1
@@ -3,55 +3,11 @@ import { MICRON_PATCH_SIZE_MAX } from "../../build/MICRON_PATCH_SIZE_MAX.js";
3
3
  import { typed_array_copy } from "../../../../../core/collection/array/typed/typed_array_copy.js";
4
4
  import { channelCountToThreeTextureFormat } from "../../../texture/channelCountToThreeTextureFormat.js";
5
5
  import { DataTexture, NearestFilter } from "three";
6
- import { DataType } from "../../../../../core/collection/table/DataType.js";
7
6
  import { compute_micron_buffer_array_constructor } from "../../build/compute_micron_buffer_array_constructor.js";
8
7
  import { computeThreeTextureTypeFromDataType } from "../../../render/forward_plus/data/computeThreeTextureTypeFromDataType.js";
9
8
  import { computeThreeTextureInternalFormatFromDataType } from "../../../render/forward_plus/data/computeThreeTextureInternalFormatFromDataType.js";
10
9
  import { DataTypeByteSizes } from "../../../../../core/collection/table/DataTypeByteSizes.js";
11
-
12
- /**
13
- *
14
- * @returns {string}
15
- * @param {DataType} dt
16
- * @param {number} channel_count
17
- */
18
- function normalized_internal_format(dt, channel_count) {
19
- let suffix = '';
20
- switch (dt) {
21
- case DataType.Uint8:
22
- suffix = '8';
23
- break;
24
- case DataType.Int8:
25
- suffix = '8_SNORM';
26
- break;
27
- case DataType.Uint16:
28
- suffix = '16UI';
29
- break;
30
- default:
31
- throw new Error(`Unsupported data type '${dt}'`);
32
- }
33
-
34
- let prefix = '';
35
-
36
- switch (channel_count) {
37
- case 1:
38
- prefix = 'R';
39
- break;
40
- case 2:
41
- prefix = 'RG';
42
- break;
43
- case 3:
44
- prefix = 'RGB';
45
- break;
46
- case 4:
47
- prefix = 'RGBA';
48
- break;
49
- default:
50
- throw new Error(`Unsupported channel count '${channel_count}'`);
51
- }
52
-
53
- return prefix + suffix;
54
- }
10
+ import { normalized_internal_format } from "../../../texture/normalized_internal_format.js";
55
11
 
56
12
  export class AttributeDataTexture {
57
13
  /**
@@ -132,15 +132,16 @@ export class ThreeInstancedAdapter {
132
132
 
133
133
  /**
134
134
  *
135
- * @param {MicronGeometryPatchOccurrence[]} patches
135
+ * @param {Uint32Array[]} patches
136
136
  * @param {number} patch_count
137
+ * @param {VirtualGeometry} virtual_geometry
137
138
  */
138
- build(patches, patch_count) {
139
+ build(patches, patch_count, virtual_geometry) {
139
140
  this.__prepare_capacity(patch_count);
140
141
 
141
142
  const geo = this.__buffer_geometry;
142
143
 
143
- const patch_transform_attribute = geo.getAttribute(ATTRIBUTE_NAME_TRANSFORM);
144
+ const patch_transform_attribute = geo.attributes[ATTRIBUTE_NAME_TRANSFORM];
144
145
  /**
145
146
  *
146
147
  * @type {Float32Array}
@@ -149,21 +150,31 @@ export class ThreeInstancedAdapter {
149
150
 
150
151
  patch_transform_attribute.needsUpdate = true;
151
152
 
152
- const patch_address_attribute = geo.getAttribute(ATTRIBUTE_NAME_ADDRESS);
153
+ const patch_address_attribute = geo.attributes[ATTRIBUTE_NAME_ADDRESS];
153
154
  const patch_address_array = patch_address_attribute.array;
154
155
 
155
156
  patch_address_attribute.needsUpdate = true;
156
157
 
157
158
  const patch_data = this.__patch_data;
159
+ const transforms = virtual_geometry.lookup_instance_transforms;
160
+ const geometries = virtual_geometry.loolup_geometries;
158
161
 
159
162
  for (let i = 0; i < patch_count; i++) {
160
- const occurrence = patches[i];
163
+ const i3 = i * 3;
164
+
165
+ const instance_id = patches[i3];
166
+ const geometry_id = patches[i3 + 1];
167
+ const patch_id = patches[i3 + 2];
168
+
169
+ const transform = transforms[instance_id];
170
+ const micron_geometry = geometries[geometry_id];
171
+ const patch = micron_geometry.patches[patch_id];
161
172
 
162
173
  const i16 = i * 16;
163
174
 
164
- patch_transform_array.set(occurrence.transform, i16);
175
+ patch_transform_array.set(transform, i16);
165
176
 
166
- const patch_slot_id = patch_data.ensure(occurrence.patch);
177
+ const patch_slot_id = patch_data.ensure(patch);
167
178
 
168
179
  patch_address_array[i] = patch_slot_id;
169
180
  }
@@ -37,7 +37,6 @@ export class RefinementSpec {
37
37
  */
38
38
  const array_buffer = new ArrayBuffer(160);
39
39
 
40
-
41
40
  /**
42
41
  * Contains transformation from camera space to object space
43
42
  * @type {Float32Array|number[]|mat4}
@@ -50,17 +49,6 @@ export class RefinementSpec {
50
49
  * @type {Float32Array}
51
50
  */
52
51
  this.frustum_f32 = new Float32Array(array_buffer, 64, 24);
53
-
54
-
55
- /**
56
- * Object-space camera position
57
- * @type {Float32Array}
58
- */
59
- this.view_position = new Float32Array(3);
60
- }
61
-
62
- get frustum() {
63
- throw new Error('deprecated, use .frustum_f32 instead');
64
52
  }
65
53
 
66
54
  /**
@@ -0,0 +1,118 @@
1
+ import { BitSet } from "../../../../../core/binary/BitSet.js";
2
+ import { aabb3_intersects_frustum_array } from "../../../../../core/bvh2/aabb3/aabb3_intersects_frustum_array.js";
3
+ import { aabb3_estimate_projected_area2 } from "../../../../../core/geom/3d/aabb/aabb3_estimate_projected_area2.js";
4
+
5
+
6
+ /**
7
+ *
8
+ * @type {BitSet}
9
+ */
10
+ const scratch_traversal_bitset = new BitSet();
11
+ scratch_traversal_bitset.preventShrink();
12
+
13
+ /**
14
+ * Using a pre-allocated continuous chunk of memory gets us better cache coherence
15
+ * @readonly
16
+ * @type {number[]}
17
+ */
18
+ const scratch_traversal_stack = new Uint32Array(781250);
19
+
20
+ /**
21
+ *
22
+ * @param {VirtualGeometry} destination
23
+ * @param {RefinementSpec} spec
24
+ * @param {MicronGeometry} geometry
25
+ * @param instance_id
26
+ * @param transform
27
+ */
28
+ export function get_geometry_patch_cut(
29
+ destination,
30
+ spec,
31
+ geometry,
32
+ instance_id,
33
+ transform
34
+ ) {
35
+
36
+ const frustum_planes = spec.frustum_f32;
37
+ const projection_matrix = spec.projection_matrix;
38
+ const error_threshold = spec.error_threshold_normalized;
39
+
40
+ const geometry_id = geometry.id;
41
+
42
+ let output_patch_cursor = destination.patch_count;
43
+
44
+ let i;
45
+ let stack_cursor = 1;
46
+
47
+ // initialize stack
48
+ const stack = scratch_traversal_stack;
49
+ const root = geometry.root;
50
+ stack[0] = root.id;
51
+
52
+ const included_set = scratch_traversal_bitset;
53
+ included_set.reset();
54
+
55
+ included_set.set(root.id, true);
56
+
57
+ const patches = geometry.patches;
58
+ const destination_patches = destination.patches;
59
+
60
+ while (stack_cursor > 0) {
61
+ stack_cursor--;
62
+
63
+ const patch_id = stack[stack_cursor];
64
+ const patch = patches[patch_id];
65
+
66
+ /*
67
+ check if node is in view frustum
68
+ NOTE: following suggestion in "FastMesh: Efficient View-dependent Meshing" by Renato Pajarola et al, we can do a more coarse frustum test approximating frustum as a cone initially
69
+ */
70
+ const in_view_frustum = aabb3_intersects_frustum_array(patch.bounding_box, frustum_planes);
71
+
72
+ if (!in_view_frustum) {
73
+ continue;
74
+ }
75
+
76
+ // check if record needs to be split
77
+ if (patch.lod > 0) {
78
+
79
+ // not the lowest level of LOD, see if subdivision might be necessary
80
+
81
+ // compute average triangle area
82
+ // const group_screen_area = aabb3_project_to_2d(patch.group_bounding_box_corners, projection_matrix);
83
+ const group_screen_area = aabb3_estimate_projected_area2(patch.group_bounding_box, projection_matrix);
84
+ const screen_space_error = patch.group_error * group_screen_area;
85
+
86
+ if (screen_space_error > error_threshold) {
87
+ // error too large, needs to be split
88
+
89
+ const children = patch.children;
90
+ const child_count = children.length;
91
+
92
+ for (i = 0; i < child_count; i++) {
93
+ const child = children[i];
94
+
95
+ if (included_set.getAndSet(child.id)) {
96
+ continue;
97
+ }
98
+
99
+ stack[stack_cursor++] = child.id;
100
+ }
101
+
102
+ continue;
103
+ }
104
+ }
105
+
106
+
107
+ const target_address = output_patch_cursor * 3;
108
+
109
+ destination_patches[target_address] = instance_id;
110
+ destination_patches[target_address + 1] = geometry_id;
111
+ destination_patches[target_address + 2] = patch_id;
112
+
113
+ output_patch_cursor++;
114
+
115
+ }
116
+
117
+ destination.patch_count = output_patch_cursor;
118
+ }
@@ -144,8 +144,9 @@ export class MaterialContext {
144
144
 
145
145
  /**
146
146
  *
147
+ * @param {VirtualGeometry} virtual_geometry
147
148
  */
148
- build() {
149
+ build(virtual_geometry) {
149
150
 
150
151
  if (this.__patch_count === 0) {
151
152
  this.mesh.visible = false;
@@ -154,7 +155,7 @@ export class MaterialContext {
154
155
  this.mesh.visible = true;
155
156
  }
156
157
 
157
- this.adapter.build(this.__patches, this.__patch_count);
158
+ this.adapter.build(this.__patches, this.__patch_count, virtual_geometry);
158
159
 
159
160
  }
160
161
  }
@@ -16,6 +16,7 @@ const scratch_mvc = new MaterialVertexSpec();
16
16
  export class VGThreeRenderer {
17
17
  constructor() {
18
18
  this.geometry = new VirtualGeometry();
19
+
19
20
  this.builder = new VirtualGeometryBuilder();
20
21
 
21
22
  /**
@@ -158,6 +159,10 @@ export class VGThreeRenderer {
158
159
  const contexts = this.__contexts_array;
159
160
  const context_count = contexts.length;
160
161
 
162
+ /**
163
+ *
164
+ * @type {Map<number, MaterialContext>}
165
+ */
161
166
  const instance_to_ctx = new Map();
162
167
 
163
168
  for (let i = 0; i < context_count; i++) {
@@ -175,18 +180,28 @@ export class VGThreeRenderer {
175
180
  const patches = geometry.patches;
176
181
 
177
182
  for (let i = 0; i < patch_count; i++) {
178
- const patch = patches[i];
183
+ const i3 = i * 3;
184
+
185
+ const instance_id = patches[i3];
186
+ const geometry_id = patches[i3 + 1];
187
+ const patch_id = patches[i3 + 2];
188
+
189
+ const ctx = instance_to_ctx.get(instance_id);
190
+
191
+ const target_address = ctx.__patch_count * 3;
179
192
 
180
- const ctx = instance_to_ctx.get(patch.instance_id);
193
+ ctx.__patches[target_address] = instance_id;
194
+ ctx.__patches[target_address + 1] = geometry_id;
195
+ ctx.__patches[target_address + 2] = patch_id;
181
196
 
182
- ctx.__patches[ctx.__patch_count++] = patch;
197
+ ctx.__patch_count++;
183
198
  }
184
199
 
185
200
  // build each context
186
201
  for (let i = 0; i < context_count; i++) {
187
202
  const ctx = contexts[i];
188
203
 
189
- ctx.build();
204
+ ctx.build(this.geometry);
190
205
  }
191
206
  }
192
207
 
@@ -72,8 +72,9 @@ export class ThreeBypassRenderer {
72
72
 
73
73
  let stack_top = 0;
74
74
  let j = 0;
75
+ let i = 0;
75
76
 
76
- for (let i = 0; i < input_size; i++) {
77
+ for (; i < input_size; i++) {
77
78
  const object3D = input[i];
78
79
 
79
80
  if (input_filter(object3D)) {
@@ -119,7 +120,7 @@ export class ThreeBypassRenderer {
119
120
  const children = object3D.children;
120
121
  const child_count = children.length;
121
122
 
122
- for (let i = 0; i < child_count; i++) {
123
+ for (i = 0; i < child_count; i++) {
123
124
  const child = children[i];
124
125
 
125
126
  stack[stack_top++] = child;
@@ -0,0 +1,48 @@
1
+ import { DataType } from "../../../core/collection/table/DataType.js";
2
+ import { assert } from "../../../core/assert.js";
3
+
4
+ /**
5
+ *
6
+ * @returns {string}
7
+ * @param {DataType} dt
8
+ * @param {number} channel_count
9
+ */
10
+ export function normalized_internal_format(dt, channel_count) {
11
+ assert.isNonNegativeInteger(channel_count, 'channel_count');
12
+
13
+ let suffix = '';
14
+ switch (dt) {
15
+ case DataType.Uint8:
16
+ suffix = '8';
17
+ break;
18
+ case DataType.Int8:
19
+ suffix = '8_SNORM';
20
+ break;
21
+ case DataType.Uint16:
22
+ suffix = '16UI';
23
+ break;
24
+ default:
25
+ throw new Error(`Unsupported data type '${dt}'`);
26
+ }
27
+
28
+ let prefix = '';
29
+
30
+ switch (channel_count) {
31
+ case 1:
32
+ prefix = 'R';
33
+ break;
34
+ case 2:
35
+ prefix = 'RG';
36
+ break;
37
+ case 3:
38
+ prefix = 'RGB';
39
+ break;
40
+ case 4:
41
+ prefix = 'RGBA';
42
+ break;
43
+ default:
44
+ throw new Error(`Unsupported channel count '${channel_count}'`);
45
+ }
46
+
47
+ return prefix + suffix;
48
+ }
@@ -1555,3 +1555,9 @@ Sampler2D.prototype.traverseOrthogonalNeighbours = function (x, y, visitor, this
1555
1555
  }
1556
1556
  };
1557
1557
 
1558
+ /**
1559
+ * @readonly
1560
+ * @type {boolean}
1561
+ */
1562
+ Sampler2D.prototype.isSampler2D = true;
1563
+
@@ -0,0 +1,25 @@
1
+ //
2
+
3
+ /**
4
+ * Copy single channel from one sampler to another
5
+ * @param {Sampler2D} source
6
+ * @param {number} source_channel
7
+ * @param {Sampler2D} destination
8
+ * @param {number} destination_channel
9
+ */
10
+ export function sampler2d_copy_channel_data(source, source_channel, destination, destination_channel) {
11
+ const pixel_count = source.height * source.width;
12
+
13
+ const source_item_size = source.itemSize;
14
+ const destination_item_size = destination.itemSize;
15
+
16
+ const source_data = source.data;
17
+ const destination_data = destination.data;
18
+
19
+ for (let i = 0; i < pixel_count; i++) {
20
+ const address_source = i * source_item_size + source_channel;
21
+ const address_destination = i * destination_item_size + destination_channel;
22
+
23
+ destination_data[address_destination] = source_data[address_source];
24
+ }
25
+ }
@@ -10,6 +10,9 @@ import { genericResampleSampler2D } from "./genericResampleSampler2D.js";
10
10
  * @param {Sampler2D} output
11
11
  */
12
12
  export function scaleSampler2D(input, output) {
13
+ assert.equal(input.isSampler2D, true, 'input.isSampler2D !== true');
14
+ assert.equal(output.isSampler2D, true, 'output.isSampler2D !== true');
15
+
13
16
  const targetItemSize = output.itemSize;
14
17
 
15
18
  assert.equal(input.itemSize, targetItemSize, 'both source and target must have the same number of channels');
@@ -16,7 +16,20 @@ import { typedArrayConstructorByInstance } from "../typedArrayConstructorByInsta
16
16
  export function drawSamplerHTML(parentNode, sampler, x, y, scale, offset, size) {
17
17
 
18
18
 
19
- const target = new Sampler2D(typedArrayConstructorByInstance(sampler.data), sampler.itemSize, size, size);
19
+ const TA = typedArrayConstructorByInstance(sampler.data);
20
+
21
+
22
+ let target_width = size;
23
+ let target_height = size;
24
+
25
+ // apply aspect ratio
26
+ if (sampler.width > sampler.height) {
27
+ target_height = Math.ceil(size * (sampler.height / sampler.width));
28
+ } else if (sampler.width < sampler.height) {
29
+ target_width = Math.ceil(size * (sampler.width / sampler.height));
30
+ }
31
+
32
+ const target = new Sampler2D(new TA(target_width * target_height * sampler.itemSize), sampler.itemSize, target_width, target_height);
20
33
 
21
34
  scaleSampler2D(sampler, target);
22
35
 
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.34.47",
8
+ "version": "2.35.0",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",