@woosh/meep-engine 2.34.30 → 2.34.34

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/core/assert.js CHANGED
@@ -316,6 +316,17 @@ assert.defined = function (value, name = "value") {
316
316
 
317
317
  };
318
318
 
319
+ /**
320
+ *
321
+ * @param {*} value
322
+ * @param {String} [name]
323
+ */
324
+ assert.isNull = function (value, name){
325
+ if (value !== null) {
326
+ throw new Error(`${name} is NOT null`);
327
+ }
328
+ }
329
+
319
330
  /**
320
331
  *
321
332
  * @param {*} value
@@ -8,7 +8,7 @@ import { arrayQuickSort } from "../collection/array/arrayQuickSort.js";
8
8
  import { max2 } from "../math/max2.js";
9
9
  import { min2 } from "../math/min2.js";
10
10
  import { computeSampleStandardDeviation } from "../math/statistics/computeSampleStandardDeviation.js";
11
- import { aabb3_intersect_aabb3, boxSurfaceArea2 } from "./aabb3/AABB3Math.js";
11
+ import { _boxSurfaceArea2, aabb3_intersect_aabb3, boxSurfaceArea2 } from "./aabb3/AABB3Math.js";
12
12
  import { isLeaf, LeafNode } from "./LeafNode.js";
13
13
  import { Node } from "./Node.js";
14
14
  import { surfaceAreaHeuristic } from "./sah/surfaceAreaHeuristic.js";
@@ -139,13 +139,135 @@ BinaryNode.prototype.setChildren = function (left, right) {
139
139
  right.parentNode = this;
140
140
  };
141
141
 
142
+ /**
143
+ *
144
+ * @param {Node} child
145
+ */
146
+ BinaryNode.prototype.setChildLeft = function (child) {
147
+ assert.isNull(this.left, 'left');
148
+
149
+ this.left = child;
150
+
151
+ this.updateLeafNodeCount();
152
+
153
+ this.refit();
154
+
155
+ child.parentNode = this;
156
+ }
157
+
158
+ /**
159
+ *
160
+ * @param {Node} child
161
+ */
162
+ BinaryNode.prototype.setChildRight = function (child) {
163
+ assert.isNull(this.right, 'right');
164
+
165
+ this.right = child;
166
+
167
+ this.updateLeafNodeCount();
168
+
169
+ this.refit();
170
+
171
+ child.parentNode = this;
172
+ }
173
+
174
+ /**
175
+ * @see "Dynamic BVH" by Erin Catto / Blizzard
176
+ * @param {AABB3} node
177
+ * @param {AABB3} addition
178
+ * @returns {number}
179
+ */
180
+ function inclusion_surface_area_heuristic(node, addition) {
181
+ const x0 = min2(node.x0, addition.x0);
182
+ const y0 = min2(node.y0, addition.y0);
183
+ const z0 = min2(node.z0, addition.z0);
184
+
185
+ const x1 = max2(node.x1, addition.x1);
186
+ const y1 = max2(node.y1, addition.y1);
187
+ const z1 = max2(node.z1, addition.z1);
188
+
189
+ return _boxSurfaceArea2(x0, y0, z0, x1, y1, z1) - boxSurfaceArea2(node);
190
+ }
191
+
192
+ /**
193
+ *
194
+ * @param {BinaryNode|LeafNode} node
195
+ * @param {AABB3} addition
196
+ */
197
+ function inclusion_cost_lower_bound(node, addition) {
198
+ let n = node;
199
+ let r = boxSurfaceArea2(addition);
200
+
201
+ while (n !== null) {
202
+
203
+ r += n.costForInclusion(addition);
204
+
205
+ n = n.parentNode;
206
+ }
207
+
208
+ return r;
209
+ }
210
+
211
+ /**
212
+ * For incremental insertion, a sibling needs to be found, newly inserted node will be parented together with this sibling
213
+ * @param {AABB3} box
214
+ * @returns {BinaryNode|LeafNode}
215
+ */
216
+ BinaryNode.prototype.__insertion_findSiblingFor = function (box) {
217
+
218
+ const open_set = [this];
219
+ let cursor = 1;
220
+
221
+ let best_cost = Infinity;
222
+ let best_node = this;
223
+
224
+ while (cursor > 0) {
225
+ const node = open_set[--cursor];
226
+
227
+ const cost = inclusion_surface_area_heuristic(node, box);
228
+
229
+ if (cost < best_cost) {
230
+ best_cost = cost;
231
+ best_node = node;
232
+ }
233
+
234
+ if (node.isBinaryNode) {
235
+
236
+ if (node.isProtected()) {
237
+ continue;
238
+ }
239
+
240
+ // consider pushing children into the queue
241
+
242
+ // compute lower bound for cost
243
+ const c_low = inclusion_cost_lower_bound(node, box);
244
+
245
+ if (c_low < best_cost) {
246
+ // worth-while to explore children
247
+ if (node.left !== null) {
248
+ open_set[cursor++] = node.left;
249
+ }
250
+ if (node.right !== null) {
251
+ open_set[cursor++] = node.right
252
+ }
253
+ }
254
+ }
255
+
256
+
257
+ }
258
+
259
+ return best_node;
260
+ }
261
+
142
262
  /**
143
263
  *
144
264
  * @param {AABB3} box
145
- * @returns {Node}
265
+ * @returns {Node|BinaryNode|LeafNode}
146
266
  */
147
267
  BinaryNode.prototype.findParentFor = function (box) {
148
268
  let n = this;
269
+ let aCost = 0;
270
+ let bCost = 0;
149
271
 
150
272
  for (; ;) {
151
273
 
@@ -181,8 +303,8 @@ BinaryNode.prototype.findParentFor = function (box) {
181
303
  }
182
304
  }
183
305
 
184
- let aCost = a.costForInclusion(box);
185
- let bCost = b.costForInclusion(box);
306
+ aCost = a.costForInclusion(box);
307
+ bCost = b.costForInclusion(box);
186
308
 
187
309
  if (aCost === bCost) {
188
310
 
@@ -196,9 +318,9 @@ BinaryNode.prototype.findParentFor = function (box) {
196
318
 
197
319
  }
198
320
 
199
- //change costs to be surface areas instead
200
- aCost = a.computeSAH();
201
- bCost = b.computeSAH();
321
+ //change costs to be based on balance
322
+ aCost = a.leafNodeCount / boxSurfaceArea2(a);
323
+ bCost = b.leafNodeCount / boxSurfaceArea2(b);
202
324
  }
203
325
 
204
326
  if (aCost === bCost) {
@@ -518,26 +640,29 @@ function countLeaves(node) {
518
640
  }
519
641
 
520
642
  /**
521
- *
643
+ * @deprecated Incorrect usage of SAH
522
644
  * @return {number}
523
645
  */
524
646
  BinaryNode.prototype.computeSAH = function () {
525
647
  let leftLeaves, rightLeaves, leftArea, rightArea;
526
648
 
527
- if (this.left === null) {
649
+ const left = this.left;
650
+
651
+ if (left === null) {
528
652
  leftArea = 0;
529
653
  leftLeaves = 0;
530
654
  } else {
531
- leftArea = boxSurfaceArea2(this.left);
532
- leftLeaves = this.left.leafNodeCount;
655
+ leftArea = boxSurfaceArea2(left);
656
+ leftLeaves = left.leafNodeCount;
533
657
  }
534
658
 
535
- if (this.right === null) {
659
+ const right = this.right;
660
+ if (right === null) {
536
661
  rightArea = 0;
537
662
  rightLeaves = 0;
538
663
  } else {
539
- rightArea = boxSurfaceArea2(this.right);
540
- rightLeaves = this.right.leafNodeCount;
664
+ rightArea = boxSurfaceArea2(right);
665
+ rightLeaves = right.leafNodeCount;
541
666
  }
542
667
 
543
668
  const thisArea = boxSurfaceArea2(this);
@@ -1065,34 +1190,96 @@ function transplantNewCommonParent(node, child) {
1065
1190
 
1066
1191
  parent.bubbleExpandToFit(bNode);
1067
1192
 
1193
+ parent.updateLeafNodeCount();
1194
+
1068
1195
  return bNode;
1069
1196
  }
1070
1197
 
1198
+
1071
1199
  /**
1072
1200
  *
1073
1201
  * @param {Node|BinaryNode|LeafNode} child
1074
1202
  */
1075
1203
  BinaryNode.prototype.insertNode = function (child) {
1076
- assert.notEqual(child, null, "child node is null");
1077
- assert.notEqual(child, undefined, "child node is undefined");
1078
- assert.ok(child instanceof Node, undefined, "child node is not an instance of Node");
1204
+
1205
+ assert.notNull(child, "child");
1206
+ assert.defined(child, "child");
1207
+ assert.isInstanceOf(child, Node, 'child', "Node");
1208
+ assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
1209
+
1210
+ const sibling = this.__insertion_findSiblingFor(child);
1211
+
1212
+ const sibling_parent = sibling.parentNode;
1213
+ if (sibling_parent === null) {
1214
+ // root node
1215
+
1216
+ if (sibling.left === null) {
1217
+ sibling.setChildLeft(child);
1218
+ } else if (sibling.right === null) {
1219
+ sibling.setChildRight(child);
1220
+ } else {
1221
+ const parent = new BinaryNode();
1222
+
1223
+ parent.setChildren(sibling.right, child);
1224
+ sibling.right = parent;
1225
+ parent.parentNode = sibling;
1226
+ }
1227
+
1228
+ } else {
1229
+
1230
+ // non-root
1231
+
1232
+ if (sibling_parent.left === sibling && sibling_parent.right === null) {
1233
+
1234
+ // right node is free, take it
1235
+ sibling_parent.setChildRight(child);
1236
+
1237
+ } else if (sibling_parent.right === sibling && sibling_parent.left === null) {
1238
+
1239
+ // left node is free, take it
1240
+ sibling_parent.setChildLeft(child);
1241
+
1242
+ } else {
1243
+
1244
+ transplantNewCommonParent(sibling, child)
1245
+
1246
+ }
1247
+
1248
+ sibling_parent.bubbleRefit();
1249
+
1250
+ }
1251
+ }
1252
+
1253
+ /**
1254
+ * @deprecated
1255
+ * @param {Node|BinaryNode|LeafNode} child
1256
+ */
1257
+ BinaryNode.prototype.insertNode_old = function (child) {
1258
+ assert.notNull(child, "child");
1259
+ assert.defined(child, "child");
1260
+ assert.isInstanceOf(child, Node, 'child', "Node");
1079
1261
  assert.equal(child.parentNode, null, "parentNode must be null, otherwise Node still belongs to another tree");
1080
1262
 
1081
1263
  const node = this.findParentFor(child);
1082
1264
 
1083
1265
  let bNode;
1084
1266
 
1085
- if (node.isBinaryNode) {
1086
- if ((node.isProtected() && node !== this)) {
1087
- transplantNewCommonParent(node, child);
1088
- } else if (node.left === null) {
1267
+ if (
1268
+ node.isBinaryNode &&
1269
+ !(node.isProtected() && node !== this) // protected node, can't insert into this subtree
1270
+ ) {
1271
+
1272
+ if (node.left === null) {
1089
1273
  node.left = child;
1090
1274
  child.parentNode = node;
1091
1275
  node.bubbleExpandToFit(child);
1276
+
1092
1277
  } else if (node.right === null) {
1278
+
1093
1279
  node.right = child;
1094
1280
  child.parentNode = node;
1095
1281
  node.bubbleExpandToFit(child);
1282
+
1096
1283
  } else {
1097
1284
  //take right child and insert another binary node there
1098
1285
  bNode = new BinaryNode();
@@ -1106,18 +1293,17 @@ BinaryNode.prototype.insertNode = function (child) {
1106
1293
  node.updateLeafNodeCount();
1107
1294
  } else {
1108
1295
  //need to do transplanting and introduce a new common parent
1109
- bNode = transplantNewCommonParent(node, child);
1296
+ transplantNewCommonParent(node, child);
1110
1297
 
1111
- bNode.updateLeafNodeCount();
1112
1298
  }
1113
1299
 
1114
1300
  };
1115
1301
 
1116
1302
  /**
1117
- *
1303
+ * @param {boolean} deep
1118
1304
  * @returns {BinaryNode}
1119
1305
  */
1120
- BinaryNode.prototype.clone = function (deep) {
1306
+ BinaryNode.prototype.clone = function (deep = false) {
1121
1307
  const clone = new BinaryNode();
1122
1308
 
1123
1309
  clone.x0 = this.x0;
@@ -6,7 +6,7 @@
6
6
  import { aabb3_computeDistanceAbovePlane } from "../../geom/3d/aabb/aabb3_computeDistanceAbovePlane.js";
7
7
  import { computeAABB3PlaneSide } from "../../geom/3d/aabb/computeAABB3PlaneSide.js";
8
8
  import computeMortonCode from "../../geom/3d/morton/Morton.js";
9
- import { _boxSurfaceArea2, intersectRay, intersectSegment } from "./AABB3Math.js";
9
+ import { boxSurfaceArea, intersectRay, intersectSegment } from "./AABB3Math.js";
10
10
  import { aabb3_build_corners } from "../../geom/3d/aabb/aabb3_build_corners.js";
11
11
  import { assert } from "../../assert.js";
12
12
 
@@ -81,7 +81,7 @@ AABB3.prototype.computeMortonCode = function () {
81
81
  * @returns {number}
82
82
  */
83
83
  AABB3.prototype.computeSurfaceArea = function () {
84
- return _boxSurfaceArea2(this.x0, this.y0, this.z0, this.x1, this.y1, this.z1);
84
+ return boxSurfaceArea(this.x0, this.y0, this.z0, this.x1, this.y1, this.z1);
85
85
  };
86
86
 
87
87
  /**
@@ -296,7 +296,8 @@ AABB3.prototype.costForInclusion = function (other) {
296
296
  };
297
297
 
298
298
  /**
299
- *
299
+ * Surface area delta when including a given AABB
300
+ * 0 means that including a given AABB would not cause any change to total surface area
300
301
  * @param {number} x0
301
302
  * @param {number} y0
302
303
  * @param {number} z0
@@ -540,7 +540,7 @@ function boxSurfaceArea(x0, y0, z0, x1, y1, z1) {
540
540
  const dx = x1 - x0;
541
541
  const dy = y1 - y0;
542
542
  const dz = z1 - z0;
543
- return (dx * dy + dy * dz + dz * dx) * 2; //2 of each side
543
+ return (dy * (dx + dz) + dz * dx) * 2; //2 of each side
544
544
  }
545
545
 
546
546
  /**
@@ -719,40 +719,6 @@ List.prototype.copy = function (other) {
719
719
  }
720
720
  };
721
721
 
722
- /**
723
- *
724
- * @param {List} other
725
- * @param {function} elementComparator
726
- * @returns {number} -1, 0 or 1
727
- */
728
- List.prototype.compare = function (other, elementComparator) {
729
- const thisLength = this.length;
730
- const otherLength = other.length;
731
-
732
- const deltaLength = thisLength - otherLength;
733
-
734
- if (deltaLength !== 0) {
735
- return deltaLength;
736
- }
737
-
738
- for (let i = 0; i < thisLength; i++) {
739
- const thisElement = this.get(i);
740
- const otherElement = other.get(i);
741
-
742
- if (thisElement === otherElement) {
743
- continue;
744
- }
745
-
746
- const elementDelta = elementComparator(thisElement, otherElement);
747
-
748
- if (elementDelta !== 0) {
749
- return elementDelta;
750
- }
751
- }
752
-
753
- return 0;
754
- };
755
-
756
722
  /**
757
723
  * NOTE: do not modify resulting array
758
724
  * @returns {T[]}
@@ -19,3 +19,19 @@ export const DataTypeByteSizes = {
19
19
  [DataType.Float32]: 4,
20
20
  [DataType.Float64]: 8
21
21
  };
22
+
23
+
24
+ /**
25
+ *
26
+ * @param {DataType} type
27
+ * @return {number}
28
+ */
29
+ export function dataTypeByteSize(type) {
30
+ const size = DataTypeByteSizes[type];
31
+
32
+ if (size === undefined) {
33
+ throw new Error(`Unsupported type '${type}'`);
34
+ }
35
+
36
+ return size;
37
+ }
package/engine/Engine.js CHANGED
@@ -131,6 +131,11 @@ class Engine {
131
131
  const v_fps = (1 / stats.mean).toFixed(2);
132
132
 
133
133
 
134
+ // clear stats
135
+ m_render.clear();
136
+ m_sim.clear();
137
+ m_frame.clear();
138
+
134
139
  return `FPS: ${v_fps}, RENDER: ${v_render}ms, SIMULATION: ${v_sim}ms`;
135
140
  });
136
141
 
@@ -21,4 +21,8 @@ export class AbstractMetric {
21
21
  computeStats(result) {
22
22
  throw new Error('Not implemented');
23
23
  }
24
+
25
+ clear(){
26
+ throw new Error('Not implemented');
27
+ }
24
28
  }
@@ -31,4 +31,8 @@ export class RingBufferMetric extends AbstractMetric {
31
31
  result.max = computeArrayMax(array, 0, data_count);
32
32
  result.min = computeArrayMin(array, 0, data_count);
33
33
  }
34
+
35
+ clear() {
36
+ this.__data.clear();
37
+ }
34
38
  }
@@ -162,7 +162,7 @@ export class Transform {
162
162
 
163
163
  /**
164
164
  *
165
- * @param {Matrix4} matrix
165
+ * @param {THREE.Matrix4} matrix
166
166
  */
167
167
  fromThreeMatrix4(matrix) {
168
168
  this.fromMatrix4(matrix.elements);
@@ -125,16 +125,29 @@ export class ShadedGeometry {
125
125
  }
126
126
 
127
127
  __build_object() {
128
+ /**
129
+ * @type {THREE.Object3D}
130
+ */
131
+ let object;
132
+
128
133
  const mode = this.mode;
134
+ const shader = this.material;
135
+ const geo = this.geometry;
136
+
129
137
  if (mode === DrawMode.Triangles) {
130
- this.__cached_object = new Mesh(this.geometry, this.material);
138
+ object = new Mesh(geo, shader);
131
139
  } else if (mode === DrawMode.Lines) {
132
- this.__cached_object = new Line(this.geometry, this.material);
140
+ object = new Line(geo, shader);
133
141
  } else if (mode === DrawMode.LineSegments) {
134
- this.__cached_object = new LineSegments(this.geometry, this.material);
142
+ object = new LineSegments(geo, shader);
135
143
  } else {
136
144
  throw new Error(`Unsupported draw mode '${mode}'`);
137
145
  }
146
+
147
+ object.matrixAutoUpdate = false;
148
+ object.frustumCulled = false;
149
+
150
+ this.__cached_object = object;
138
151
  }
139
152
 
140
153
  get bvh() {
@@ -81,6 +81,9 @@ export class ShadedGeometrySystem extends System {
81
81
 
82
82
  const leaf_node = sg.bvh;
83
83
 
84
+ // record entity on the BVH leaf
85
+ leaf_node.entity = entity;
86
+
84
87
  // remember entity for lookups
85
88
  sg.__entity = entity;
86
89
 
@@ -0,0 +1,54 @@
1
+ import { EngineHarness } from "../../../../EngineHarness.js";
2
+ import { ShadedGeometrySystem } from "../ShadedGeometrySystem.js";
3
+ import { GameAssetType } from "../../../../asset/GameAssetType.js";
4
+ import { EngineConfiguration } from "../../../../EngineConfiguration.js";
5
+ import { GLTFAssetLoader } from "../../../../asset/loaders/GLTFAssetLoader.js";
6
+ import { three_object_to_entity_composition } from "../three_object_to_entity_composition.js";
7
+ import { AttachmentSystem } from "../../../../ecs/attachment/AttachmentSystem.js";
8
+
9
+ const eh = new EngineHarness();
10
+
11
+ /**
12
+ *
13
+ * @param {EngineHarness} harness
14
+ * @return {Promise<void>}
15
+ */
16
+ async function init(harness) {
17
+ const engine = harness.engine;
18
+
19
+ const config = new EngineConfiguration();
20
+
21
+ config.addSystem(new ShadedGeometrySystem(engine));
22
+ config.addSystem(new AttachmentSystem());
23
+ config.addLoader(GameAssetType.ModelGLTF_JSON, new GLTFAssetLoader());
24
+
25
+ await config.apply(engine);
26
+
27
+ eh.initialize().then(main);
28
+ }
29
+
30
+ /**
31
+ *
32
+ * @param {Engine} engine
33
+ */
34
+ async function main(engine) {
35
+
36
+ const em = engine.entityManager;
37
+
38
+ EngineHarness.buildBasics({
39
+ engine,
40
+ enableWater: false
41
+ })
42
+
43
+ const ecd = em.dataset;
44
+
45
+ const asset = await engine.assetManager.promise('moicon/06_04_hapro/08-clickable-assets-new/model.gltf', GameAssetType.ModelGLTF_JSON);
46
+
47
+ const three_object = asset.create();
48
+
49
+ const composition = three_object_to_entity_composition(three_object);
50
+
51
+ composition.build(ecd);
52
+ }
53
+
54
+ init(eh);
@@ -1,8 +1,8 @@
1
- import { EngineHarness } from "../../../EngineHarness.js";
2
- import { ShadedGeometrySystem } from "./ShadedGeometrySystem.js";
3
- import EntityBuilder from "../../../ecs/EntityBuilder.js";
4
- import { Transform } from "../../../ecs/transform/Transform.js";
5
- import { ShadedGeometry } from "./ShadedGeometry.js";
1
+ import { EngineHarness } from "../../../../EngineHarness.js";
2
+ import { ShadedGeometrySystem } from "../ShadedGeometrySystem.d.ts";
3
+ import EntityBuilder from "../../../../ecs/EntityBuilder.d.ts";
4
+ import { Transform } from "../../../../ecs/transform/Transform.d.ts";
5
+ import { ShadedGeometry } from "../ShadedGeometry.d.ts";
6
6
  import { BoxBufferGeometry, MeshBasicMaterial } from "three";
7
7
 
8
8
  const eh = new EngineHarness();
@@ -0,0 +1,37 @@
1
+ import { EntityNode } from "../../../ecs/parent/EntityNode.js";
2
+ import { Transform } from "../../../ecs/transform/Transform.js";
3
+ import { ShadedGeometry } from "./ShadedGeometry.js";
4
+
5
+ /**
6
+ *
7
+ * @param {THREE.Object3D} root
8
+ * @returns {EntityNode}
9
+ */
10
+ export function three_object_to_entity_composition(root) {
11
+ const node = new EntityNode();
12
+
13
+ node.transform.fromMatrix4(root.matrix.elements);
14
+
15
+ node.entity.add(new Transform());
16
+
17
+ if (root.isMesh) {
18
+ if (root.isSkinnedMesh) {
19
+ throw new Error(`Skinned meshes are not supported`);
20
+ }
21
+
22
+ node.entity.add(ShadedGeometry.from(root.geometry, root.material));
23
+ }
24
+
25
+ const children = root.children;
26
+ const child_count = children.length;
27
+ for (let i = 0; i < child_count; i++) {
28
+ const child = children[i];
29
+
30
+ const child_node = three_object_to_entity_composition(child);
31
+
32
+
33
+ node.addChild(child_node);
34
+ }
35
+
36
+ return node;
37
+ }
@@ -51,6 +51,17 @@ function custom_attribute_format_uv(spec, source) {
51
51
  spec.type = DataType.Float16;
52
52
  }
53
53
 
54
+ /**
55
+ *
56
+ * @param {AttributeSpec} spec
57
+ * @param {THREE.BufferAttribute} source
58
+ */
59
+ function custom_attribute_format_color(spec, source) {
60
+ // crush color data to 8 bit
61
+ spec.type = DataType.Uint8;
62
+ spec.normalized = true;
63
+ }
64
+
54
65
  /**
55
66
  *
56
67
  * @param {AttributeSpec} spec
@@ -70,7 +81,11 @@ function format_attribute(spec, source, name) {
70
81
  case 'normal':
71
82
  custom_attribute_format_normal(spec, source);
72
83
  break;
84
+ case 'color':
85
+ custom_attribute_format_color(spec, source);
86
+ break;
73
87
  default:
88
+ spec.normalized = source.normalized;
74
89
  spec.type = typedArrayToDataType(source.array)
75
90
  }
76
91
 
@@ -14,6 +14,8 @@ export function compute_micron_buffer_array_constructor(type) {
14
14
  return Uint16Array;
15
15
  case DataType.Uint16:
16
16
  return Uint16Array;
17
+ case DataType.Uint8:
18
+ return Uint8Array;
17
19
  case DataType.Int8:
18
20
  return Int8Array;
19
21
  default:
@@ -9,6 +9,9 @@ import { DataType } from "../../../../core/collection/table/DataType.js";
9
9
  import { passThrough } from "../../../../core/function/Functions.js";
10
10
  import { to_half_float_uint16 } from "../../../../core/binary/to_half_float_uint16.js";
11
11
  import { compute_micron_buffer_array_constructor } from "./compute_micron_buffer_array_constructor.js";
12
+ import { typedArrayToDataType } from "../../../../core/collection/array/typedArrayToDataType.js";
13
+ import { dataTypeByteSize } from "../../../../core/collection/table/DataTypeByteSizes.js";
14
+ import { clamp } from "../../../../core/math/clamp.js";
12
15
 
13
16
  function UintArrayForCount(count) {
14
17
 
@@ -90,6 +93,17 @@ function copy_attribute_data(vertex_count, index_map_backward, itemSize, attribu
90
93
  }
91
94
  }
92
95
 
96
+ /**
97
+ *
98
+ * @param {number} value
99
+ * @returns {number}
100
+ */
101
+ function resample_normalized_uint16_to_uint8(value) {
102
+ const normalized = clamp(value / 65535, 0, 1);
103
+
104
+ return Math.round(normalized * 255);
105
+ }
106
+
93
107
  /**
94
108
  *
95
109
  * @param {number} value
@@ -105,7 +119,8 @@ function normalize_to_int8(value) {
105
119
  const DataConverterType = {
106
120
  None: passThrough,
107
121
  Number2HalfFloat: to_half_float_uint16,
108
- Normalize2Int8: normalize_to_int8
122
+ Normalize2Int8: normalize_to_int8,
123
+ ResampleNormalized_Uint16_to_Uint8: resample_normalized_uint16_to_uint8
109
124
  };
110
125
 
111
126
  /**
@@ -115,17 +130,36 @@ const DataConverterType = {
115
130
  * @returns {DataConverterType}
116
131
  */
117
132
  function compute_attribute_data_converter(source, spec) {
118
- if (spec.type === DataType.Float16) {
133
+ const target_type = spec.type;
134
+ if (target_type === DataType.Float16) {
119
135
  return DataConverterType.Number2HalfFloat;
120
136
  } else if (spec.normalized && !source.normalized) {
121
137
  // source data can be normalized but isn't
122
- if (spec.type === DataType.Int8) {
138
+ if (target_type === DataType.Int8) {
123
139
  return DataConverterType.Normalize2Int8;
124
140
  } else {
125
- throw new Error(`Unsupported normalized type '${spec.type}'`);
141
+ throw new Error(`Unsupported normalized type '${target_type}'`);
126
142
  }
127
143
  } else {
128
- return DataConverterType.None;
144
+ const source_array = source.array;
145
+
146
+ const source_type = typedArrayToDataType(source_array);
147
+
148
+ const target_byte_size = dataTypeByteSize(target_type);
149
+ const source_byte_size = dataTypeByteSize(source_type)
150
+
151
+ if (target_byte_size !== source_byte_size) {
152
+ // wrong data size
153
+
154
+ if (target_type === DataType.Uint8 && source_type === DataType.Uint16) {
155
+ return DataConverterType.ResampleNormalized_Uint16_to_Uint8;
156
+ } else {
157
+ throw new Error(`Unsupported conversion ${source_type} -> ${target_type}`);
158
+ }
159
+
160
+ } else {
161
+ return DataConverterType.None;
162
+ }
129
163
  }
130
164
  }
131
165
 
@@ -3,6 +3,7 @@ import { assert } from "../../../../core/assert.js";
3
3
  import { computeTriangleRayIntersection } from "../../../../core/geom/3d/triangle/computeTriangleRayIntersection.js";
4
4
  import { SurfacePoint3 } from "../../../../core/geom/3d/SurfacePoint3.js";
5
5
  import { aabb3_array_intersects_ray } from "../../../../core/bvh2/aabb3/aabb3_array_intersects_ray.js";
6
+ import { sphere_array_intersects_ray } from "../../../../core/geom/3d/sphere/sphere_array_intersects_ray.js";
6
7
 
7
8
  let id_counter = 0;
8
9
 
@@ -105,25 +106,18 @@ export class MicronGeometry {
105
106
 
106
107
  const patch = stack[stack_cursor];
107
108
 
108
- // using group bounds because it's conservative, as we want to be able to hit LOD_0
109
-
110
- // sphere test
111
-
112
-
113
109
  if (patch.lod === 0) {
114
110
  // LOD 0 - source geometry
115
111
 
116
112
  // cast against bounds
117
113
 
118
114
  // sphere test
119
-
120
- // TODO check what's wrong with sphere tests and re-enable them
121
- // if (!sphere_array_intersects_ray(patch.bounding_sphere,
122
- // origin_x, origin_y, origin_z,
123
- // direction_x, direction_y, direction_z
124
- // )) {
125
- // continue;
126
- // }
115
+ if (!sphere_array_intersects_ray(patch.bounding_sphere,
116
+ origin_x, origin_y, origin_z,
117
+ direction_x, direction_y, direction_z
118
+ )) {
119
+ continue;
120
+ }
127
121
 
128
122
  // box test
129
123
  if (!aabb3_array_intersects_ray(
@@ -183,13 +177,15 @@ export class MicronGeometry {
183
177
 
184
178
  } else {
185
179
 
186
- // TODO check what's wrong with sphere tests and re-enable them
187
- // if (!sphere_array_intersects_ray(patch.group_bounding_sphere,
188
- // origin_x, origin_y, origin_z,
189
- // direction_x, direction_y, direction_z
190
- // )) {
191
- // continue;
192
- // }
180
+ // using group bounds because it's conservative, as we want to be able to hit LOD_0
181
+
182
+ // sphere test
183
+ if (!sphere_array_intersects_ray(patch.group_bounding_sphere,
184
+ origin_x, origin_y, origin_z,
185
+ direction_x, direction_y, direction_z
186
+ )) {
187
+ continue;
188
+ }
193
189
 
194
190
 
195
191
  // box test (more expensive, different volume)
@@ -455,7 +455,7 @@ function init() {
455
455
  }
456
456
 
457
457
  // sample_gltf_sponza();
458
- gltf_sample('data/models/RTS_Buildings_Humans/18/Building_R_18_out/Building_R_18.gltf');
458
+ // gltf_sample('data/models/RTS_Buildings_Humans/18/Building_R_18_out/Building_R_18.gltf');
459
459
  // sample_lucy();
460
460
  // sample_gltf_hapro_2();
461
461
  // sample_gltf_hapro_1();
@@ -468,6 +468,12 @@ function init() {
468
468
  // gltf_sample('moicon/29_Dec_2021_Nammo/1/model.gltf');
469
469
  // gltf_sample('moicon/29_Dec_2021_Nammo/1-onematerial/model.gltf');
470
470
  // gltf_sample('moicon/29_Dec_2021_Nammo/2/model.gltf');
471
+ // gltf_sample('moicon/03_Jan_22_Demo/1-ground_floor/model.gltf');
472
+ // gltf_sample('moicon/03_Jan_22_Demo/2-1st_floor/model.gltf');
473
+ // gltf_sample('moicon/03_Jan_22_Demo/3-area_ground_floor/model.gltf');
474
+ // gltf_sample('moicon/03_Jan_22_Demo/4-area_1st_floor/model.gltf');
475
+ // gltf_sample('moicon/03_Jan_22_Demo/8-assets/model.gltf');
476
+ gltf_sample('moicon/03_Jan_22_Demo/worker_test/model.gltf');
471
477
 
472
478
  document.addEventListener("mousemove", onDocumentMouseMove);
473
479
 
@@ -24,6 +24,9 @@ function normalized_internal_format(dt, channel_count) {
24
24
  case DataType.Int8:
25
25
  suffix = '8_SNORM';
26
26
  break;
27
+ case DataType.Uint16:
28
+ suffix = '16UI';
29
+ break;
27
30
  default:
28
31
  throw new Error(`Unsupported data type '${dt}'`);
29
32
  }
@@ -160,6 +160,29 @@ export function shader_rewrite_standard(shader, uniforms, spec, column_count) {
160
160
 
161
161
  if (attribute_spec_color !== undefined) {
162
162
  // using color
163
+ const vx_color_value = `texelFetch(${gen_micron_vertex_attribute_texture_name('color')}, micron_vertex_uv, 0)`
164
+
165
+ rewrittenVertexShader = rewrittenVertexShader.replace(
166
+ '#include <color_vertex>',
167
+ `
168
+ #if defined( USE_COLOR_ALPHA )
169
+ vColor = vec4( 1.0 );
170
+ #elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR )
171
+ vColor = vec3( 1.0 );
172
+ #endif
173
+
174
+ #ifdef USE_COLOR
175
+
176
+ #ifdef USE_COLOR_ALPHA
177
+ vColor *= ${vx_color_value}.rgba;
178
+ #else
179
+ vColor *= ${vx_color_value}.rgb;
180
+ #endif
181
+
182
+ #endif
183
+
184
+ `
185
+ );
163
186
 
164
187
  }
165
188
 
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.30",
8
+ "version": "2.34.34",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",