@woosh/meep-engine 2.38.2 → 2.39.1

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 (36) hide show
  1. package/core/geom/Quaternion.js +70 -0
  2. package/core/geom/Vector2.js +17 -0
  3. package/core/geom/v3_angle_between.js +17 -3
  4. package/engine/ecs/EntityBuilder.d.ts +3 -1
  5. package/engine/ecs/EntityBuilder.js +10 -1
  6. package/engine/ecs/EntityComponentDataset.js +1 -1
  7. package/engine/ecs/parent/ChildEntities.d.ts +3 -0
  8. package/engine/ecs/parent/ChildEntities.js +41 -0
  9. package/engine/ecs/system/AbstractContextSystem.js +4 -2
  10. package/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +1 -1
  11. package/engine/graphics/ecs/path/PathDisplayEvents.js +3 -2
  12. package/engine/graphics/ecs/path/PathDisplaySystem.js +5 -0
  13. package/engine/graphics/ecs/path/highlight/PathDisplayHighlightSystem.d.ts +7 -0
  14. package/engine/graphics/ecs/path/highlight/PathDisplayHighlightSystem.js +141 -0
  15. package/engine/graphics/ecs/path/testPathDisplaySystem.js +86 -25
  16. package/engine/graphics/ecs/path/tube/BasicMaterialDefinition.d.ts +3 -0
  17. package/engine/graphics/ecs/path/tube/BasicMaterialDefinition.js +8 -0
  18. package/engine/graphics/ecs/path/tube/TubeMaterialType.d.ts +1 -0
  19. package/engine/graphics/ecs/path/tube/TubeMaterialType.js +1 -0
  20. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +12 -0
  21. package/engine/graphics/ecs/path/tube/TubePathStyle.js +54 -9
  22. package/engine/graphics/ecs/path/tube/build/TubePathBuilder.js +67 -25
  23. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +12 -2
  24. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +13 -2
  25. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +33 -28
  26. package/engine/graphics/ecs/path/tube/build/compute_smooth_profile_normals.js +41 -0
  27. package/engine/graphics/ecs/path/tube/build/fix_shape_normal_order.js +45 -0
  28. package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +123 -347
  29. package/engine/graphics/ecs/path/tube/build/make_cap.js +274 -0
  30. package/engine/graphics/ecs/path/tube/build/make_ring_faces.js +40 -0
  31. package/engine/graphics/ecs/path/tube/build/make_ring_vertices.js +152 -0
  32. package/package.json +1 -1
  33. package/view/View.js +2 -2
  34. package/view/{compose3x3transform.js → m3_cm_compose_transform.js} +11 -3
  35. package/view/m3_rm_compose_transform.js +45 -0
  36. package/view/multiplyMatrices3.js +4 -4
@@ -1,336 +1,38 @@
1
1
  import { BufferGeometry, Vector3 } from "three";
2
2
  import { GeometryOutput } from "./GeometryOutput.js";
3
3
  import { CapType } from "../CapType.js";
4
- import { max2 } from "../../../../../../core/math/max2.js";
4
+ import { make_ring_vertices } from "./make_ring_vertices.js";
5
+ import { assert } from "../../../../../../core/assert.js";
6
+ import { make_ring_faces } from "./make_ring_faces.js";
7
+ import { compute_cap_geometry_size, make_cap } from "./make_cap.js";
8
+ import { v3_angle_cos_between } from "../../../../../../core/geom/v3_angle_between.js";
5
9
 
6
10
 
7
- /**
8
- *
9
- * @param {GeometryOutput} out
10
- * @param {number} Px
11
- * @param {number} Py
12
- * @param {number} Pz
13
- * @param {Vector3} N normal
14
- * @param {Vector3} B binormal
15
- * @param {number} u X component of UV
16
- * @param {number} radius
17
- * @param {number} angular_step
18
- * @param {number} radial_segments
19
- */
20
- function make_ring_vertices(
21
- out,
22
- Px, Py, Pz,
23
- N, B,
24
- u,
25
- radius, angular_step, radial_segments
26
- ) {
27
- const out_positions = out.positions;
28
- const out_normals = out.normals;
29
- const out_uvs = out.uvs;
30
-
31
- for (let j = 0; j <= radial_segments; j++) {
32
- const angle = j * angular_step;
33
-
34
- const sin = Math.sin(angle);
35
- const cos = -Math.cos(angle);
36
-
37
- // normal
38
-
39
- const normal_x_r = (cos * N.x + sin * B.x);
40
- const normal_y_r = (cos * N.y + sin * B.y);
41
- const normal_z_r = (cos * N.z + sin * B.z);
42
-
43
- const normal_length = 1 / Math.hypot(normal_x_r, normal_y_r, normal_z_r);
44
-
45
- const normal_x = normal_x_r * normal_length;
46
- const normal_y = normal_y_r * normal_length;
47
- const normal_z = normal_z_r * normal_length;
48
-
49
- const cv = out.cursor_vertices;
50
- const cv3 = cv * 3;
51
-
52
- out_normals[cv3] = normal_x;
53
- out_normals[cv3 + 1] = normal_y;
54
- out_normals[cv3 + 2] = normal_z;
55
-
56
- // position
57
-
58
- const vx = Px + radius * normal_x;
59
- const vy = Py + radius * normal_y;
60
- const vz = Pz + radius * normal_z;
61
-
62
- out_positions[cv3] = vx;
63
- out_positions[cv3 + 1] = vy;
64
- out_positions[cv3 + 2] = vz;
65
-
66
- // UV
67
- const v = j / radial_segments;
68
-
69
- const cv2 = cv * 2;
70
-
71
- out_uvs[cv2] = u;
72
- out_uvs[cv2 + 1] = v;
73
-
74
- out.cursor_vertices++;
75
-
76
- }
77
- }
78
-
79
- /**
80
- *
81
- * @param {GeometryOutput} out
82
- * @param {number} index_offset
83
- * @param {number} tubular_segments
84
- * @param {number} radial_segments
85
- */
86
- function make_ring_faces(out, index_offset, tubular_segments, radial_segments) {
87
- const indices = out.indices;
88
-
89
- let i = 0, j = 0;
90
-
91
- for (j = 1; j <= tubular_segments; j++) {
92
-
93
- for (i = 1; i <= radial_segments; i++) {
94
-
95
- const s_1 = radial_segments + 1;
96
-
97
- const a = index_offset + s_1 * (j - 1) + (i - 1);
98
- const b = index_offset + s_1 * j + (i - 1);
99
- const c = index_offset + s_1 * j + i;
100
- const d = index_offset + s_1 * (j - 1) + i;
101
-
102
- // faces
103
-
104
- const ci = out.cursor_indices;
105
-
106
- indices[ci] = a;
107
- indices[ci + 1] = b;
108
- indices[ci + 2] = d;
109
-
110
- indices[ci + 3] = b;
111
- indices[ci + 4] = c;
112
- indices[ci + 5] = d;
113
-
114
- out.cursor_indices += 6;
115
- }
116
-
117
- }
118
- }
119
-
120
- /**
121
- *
122
- * @param {number} radial_segments
123
- * @returns {number}
124
- */
125
- function compute_cap_round_segment_count(radial_segments) {
126
- return max2(3, Math.ceil(radial_segments / 4));
127
- }
128
-
129
- /**
130
- *
131
- * @param {number} index
132
- * @param {Float32Array|number[]} in_positions
133
- * @param {Vector3[]} in_normals
134
- * @param {Vector3[]} in_binormals
135
- * @param {GeometryOutput} out
136
- * @param {number} radius
137
- * @param {number} radial_segments
138
- * @param {number} direction
139
- */
140
- function make_cap_round(
141
- out,
142
- index,
143
- in_positions, in_normals, in_binormals,
144
- radius, radial_segments, direction
145
- ) {
146
- // how many radial segments will be placed laterally to make up the cap
147
- const cap_segment_count = compute_cap_round_segment_count(radial_segments);
148
-
149
- const i3 = index * 3;
150
-
151
- const Px = in_positions[i3];
152
- const Py = in_positions[i3 + 1];
153
- const Pz = in_positions[i3 + 2];
154
-
155
- // retrieve corresponding normal and binormal
156
-
157
- const N = in_normals[index];
158
- const B = in_binormals[index];
159
-
160
- const tangent = new Vector3();
161
- tangent.crossVectors(N, B);
162
- tangent.normalize();
163
-
164
- const normal = direction > 0 ? N : N.clone().negate();
165
-
166
- const angular_step_i = (Math.PI / 2) / cap_segment_count;
167
- const angular_step_j = (Math.PI * 2) / radial_segments;
168
-
169
- const uv_u = index / (in_positions.length / 3 - 1);
170
- let i, j;
171
-
172
- const index_offset = out.cursor_vertices;
173
-
174
- for (i = 0; i <= cap_segment_count; i++) {
175
- const angle_b = i * angular_step_i;
176
-
177
- const sin_b = Math.sin(angle_b);
178
- const cos_b = Math.cos(angle_b);
179
-
180
- const cap_ring_radius = sin_b * radius;
181
-
182
- const _d = -radius * direction;
183
-
184
- const _px = Px + tangent.x * cos_b * _d;
185
- const _py = Py + tangent.y * cos_b * _d;
186
- const _pz = Pz + tangent.z * cos_b * _d;
187
-
188
- make_ring_vertices(
189
- out,
190
- _px, _py, _pz,
191
- normal, B, uv_u,
192
- cap_ring_radius, angular_step_j, radial_segments
193
- );
194
- }
195
-
196
- make_ring_faces(out, index_offset, cap_segment_count, radial_segments);
197
- }
198
-
199
- /**
200
- *
201
- * @param {number} index
202
- * @param {Float32Array|number[]} in_positions
203
- * @param {Vector3[]} in_normals
204
- * @param {Vector3[]} in_binormals
205
- * @param {GeometryOutput} out
206
- * @param {number} radius
207
- * @param {number} radial_segments
208
- * @param {number} direction
209
- */
210
- function make_cap_flat(
211
- out,
212
- index,
213
- in_positions, in_normals, in_binormals,
214
- radius, radial_segments, direction
215
- ) {
216
- // how many radial segments will be placed laterally to make up the cap
217
- const i3 = index * 3;
218
-
219
- const Px = in_positions[i3];
220
- const Py = in_positions[i3 + 1];
221
- const Pz = in_positions[i3 + 2];
222
-
223
- // retrieve corresponding normal and binormal
224
-
225
- const N = in_normals[index];
226
- const B = in_binormals[index];
227
-
228
- const tangent = new Vector3();
229
- tangent.crossVectors(N, B);
230
- tangent.normalize();
231
-
232
- const normal = direction > 0 ? N.clone().negate() : N;
233
-
234
- const angular_step_j = (Math.PI * 2) / radial_segments;
235
-
236
- const uv_u = index / (in_positions.length / 3 - 1);
237
-
238
- const index_offset = out.cursor_vertices;
239
-
240
- make_ring_vertices(
241
- out,
242
- Px, Py, Pz,
243
- normal, B, uv_u,
244
- radius, angular_step_j, radial_segments
245
- );
246
-
247
- make_ring_vertices(
248
- out,
249
- Px, Py, Pz,
250
- normal, B, uv_u,
251
- 0, angular_step_j, radial_segments
252
- );
253
-
254
- make_ring_faces(out, index_offset, 1, radial_segments);
255
- }
256
-
257
- /**
258
- *
259
- * @param {number} index
260
- * @param {Float32Array|number[]} in_positions
261
- * @param {Vector3[]} in_normals
262
- * @param {Vector3[]} in_binormals
263
- * @param {GeometryOutput} out
264
- * @param {number} radius
265
- * @param {number} radial_segments
266
- * @param {number} direction
267
- * @param {CapType} type
268
- */
269
- function make_cap(
270
- out,
271
- index,
272
- in_positions, in_normals, in_binormals,
273
- radius, radial_segments, direction, type
274
- ) {
275
- if (type === CapType.None) {
276
- // do nothing
277
- } else if (type === CapType.Round) {
278
- make_cap_round(
279
- out,
280
- index,
281
- in_positions, in_normals, in_binormals,
282
- radius, radial_segments,
283
- direction
284
- );
285
- } else if (type === CapType.Flat) {
286
- make_cap_flat(
287
- out,
288
- index,
289
- in_positions, in_normals, in_binormals,
290
- radius, radial_segments,
291
- direction
292
- );
293
- } else {
294
- throw new Error(`Unsupported cap type '${type}'`);
295
- }
296
- }
297
-
298
- /**
299
- *
300
- * @param {number} count how many caps
301
- * @param {{polygon_count:number, vertex_count:number}} out
302
- * @param {number} radial_segments
303
- * @param {CapType} type
304
- */
305
- function compute_cap_geometry_size(count, out, radial_segments, type) {
306
-
307
- if (type === CapType.None) {
308
- // do nothing
309
- } else if (type === CapType.Round) {
310
- const cap_segment_count = compute_cap_round_segment_count(radial_segments);
311
-
312
- out.vertex_count += count * (cap_segment_count + 1) * (radial_segments + 1);
313
- out.polygon_count += count * cap_segment_count * (radial_segments) * 2;
314
- } else if (type === CapType.Flat) {
315
- out.vertex_count += count * (2) * (radial_segments + 1);
316
- out.polygon_count += count * (radial_segments) * 2;
317
- } else {
318
- throw new Error(`Unsupported cap type '${type}'`);
319
- }
320
- }
11
+ const v4_array = new Float32Array(4);
321
12
 
322
13
  /**
323
14
  * @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
324
15
  * @param {Float32Array|number[]} in_positions
325
16
  * @param {Vector3[]} in_normals
326
17
  * @param {Vector3[]} in_binormals
327
- * @param {number} radius
328
- * @param {number} radial_segments
18
+ * @param {Vector3[]} in_tangents
19
+ * @param {number[]} shape
20
+ * @param {number[]|Float32Array} shape_normal
21
+ * @param {number} shape_length
22
+ * @param {number[]|Float32Array} shape_transform
329
23
  * @param {boolean} [closed]
330
24
  * @param {CapType} [cap_type]
331
25
  * @returns {BufferGeometry}
332
26
  */
333
- export function makeTubeGeometry(in_positions, in_normals, in_binormals, radius, radial_segments, closed = false, cap_type = CapType.Round) {
27
+ export function makeTubeGeometry(
28
+ in_positions, in_normals, in_binormals, in_tangents,
29
+ shape, shape_normal, shape_length, shape_transform, closed = false, cap_type = CapType.Round
30
+ ) {
31
+ assert.enum(cap_type, CapType, 'cap_type');
32
+ assert.isBoolean(closed, 'closed');
33
+
34
+ assert.isNumber(shape_length, 'shape_length');
35
+ assert.isArrayLike(shape, 'shape');
334
36
 
335
37
  const out = new GeometryOutput();
336
38
 
@@ -341,12 +43,12 @@ export function makeTubeGeometry(in_positions, in_normals, in_binormals, radius,
341
43
 
342
44
 
343
45
  const geometry_size = {
344
- vertex_count: (tubular_segments + 1) * (radial_segments + 1),
345
- polygon_count: tubular_segments * radial_segments * 2
46
+ vertex_count: (tubular_segments + 1) * (shape_length + 1),
47
+ polygon_count: tubular_segments * shape_length * 2
346
48
  };
347
49
 
348
50
  if (!closed) {
349
- compute_cap_geometry_size(2, geometry_size, radial_segments, cap_type);
51
+ compute_cap_geometry_size(2, geometry_size, shape_length, cap_type);
350
52
  }
351
53
 
352
54
  out.allocate(
@@ -357,39 +59,44 @@ export function makeTubeGeometry(in_positions, in_normals, in_binormals, radius,
357
59
  // create buffer data
358
60
  if (!closed) {
359
61
  // start cap
360
- make_cap(out, 0, in_positions, in_normals, in_binormals, radius, radial_segments, 1, cap_type);
361
- }
362
-
363
- generateBufferData();
364
-
365
- if (!closed) {
366
- // end cap
367
- make_cap(out, point_count - 1, in_positions, in_normals, in_binormals, radius, radial_segments, -1, cap_type);
62
+ make_cap(
63
+ out, 0,
64
+ in_positions, in_normals, in_binormals, in_tangents,
65
+ shape, shape_normal, shape_length, shape_transform, 1, cap_type
66
+ );
368
67
  }
369
68
 
370
- // functions
371
-
372
- function generateBufferData() {
373
- const index_offset = out.cursor_vertices;
69
+ const index_offset = out.cursor_vertices;
374
70
 
375
- for (let i = 0; i < tubular_segments; i++) {
71
+ for (let i = 0; i < tubular_segments; i++) {
376
72
 
377
- generateSegment(i);
73
+ generateSegment(i);
378
74
 
379
- }
75
+ }
380
76
 
381
- // if the geometry is not closed, generate the last row of vertices and normals
382
- // at the regular position on the given path
383
- //
384
- // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)
77
+ // if the geometry is not closed, generate the last row of vertices and normals
78
+ // at the regular position on the given path
79
+ //
80
+ // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)
385
81
 
386
- generateSegment((closed === false) ? tubular_segments : 0);
82
+ generateSegment((closed === false) ? tubular_segments : 0);
387
83
 
388
- // finally create faces
389
- make_ring_faces(out, index_offset, tubular_segments, radial_segments);
84
+ // finally create faces
85
+ make_ring_faces(out, index_offset, tubular_segments, shape_length);
390
86
 
87
+ if (!closed) {
88
+ // end cap
89
+ make_cap(
90
+ out, point_count - 1,
91
+ in_positions, in_normals, in_binormals, in_tangents,
92
+ shape, shape_normal, shape_length, shape_transform, -1, cap_type
93
+ );
391
94
  }
392
95
 
96
+ /**
97
+ *
98
+ * @param {number} i
99
+ */
393
100
  function generateSegment(i) {
394
101
 
395
102
  // we use getPointAt to sample evenly distributed points from the given path
@@ -406,14 +113,14 @@ export function makeTubeGeometry(in_positions, in_normals, in_binormals, radius,
406
113
  const B = in_binormals[i];
407
114
 
408
115
  // generate normals and vertices for the current segment
409
-
410
- const angular_step = (Math.PI * 2) / radial_segments;
116
+ compute_bend_normal(v4_array, i, tubular_segments, in_positions);
411
117
 
412
118
  make_ring_vertices(
413
119
  out,
414
120
  Px, Py, Pz,
415
- N, B, i / tubular_segments,
416
- radius, angular_step, radial_segments
121
+ N, B, in_tangents[i],
122
+ i / tubular_segments, v4_array,
123
+ shape, shape_normal, shape_length, shape_transform
417
124
  );
418
125
 
419
126
  }
@@ -421,3 +128,72 @@ export function makeTubeGeometry(in_positions, in_normals, in_binormals, radius,
421
128
 
422
129
  return out.build();
423
130
  }
131
+
132
+
133
+ /**
134
+ *
135
+ * @param {number[]|Float32Array} out
136
+ * @param {number} index
137
+ * @param {number} index_count
138
+ * @param {number[]|Float32Array} positions
139
+ */
140
+ function compute_bend_normal(
141
+ out,
142
+ index,
143
+ index_count,
144
+ positions
145
+ ) {
146
+ if (index <= 0 || index >= index_count - 1) {
147
+ // end points, no bending
148
+
149
+ out[0] = 0;
150
+ out[1] = 1;
151
+ out[2] = 0;
152
+ out[3] = 0;
153
+
154
+ return;
155
+ }
156
+
157
+ const index_next = index + 1;
158
+ const index_prev = index - 1;
159
+
160
+ const address_current = index * 3;
161
+ const address_next = index_next * 3;
162
+ const address_prev = index_prev * 3;
163
+
164
+ const i0_x = positions[address_prev];
165
+ const i0_y = positions[address_prev + 1];
166
+ const i0_z = positions[address_prev + 2];
167
+
168
+ const i1_x = positions[address_current];
169
+ const i1_y = positions[address_current + 1];
170
+ const i1_z = positions[address_current + 2];
171
+
172
+ const i2_x = positions[address_next];
173
+ const i2_y = positions[address_next + 1];
174
+ const i2_z = positions[address_next + 2];
175
+
176
+ const d0_x = i0_x - i1_x;
177
+ const d0_y = i0_y - i1_y;
178
+ const d0_z = i0_z - i1_z;
179
+
180
+ const d1_x = i1_x - i2_x;
181
+ const d1_y = i1_y - i2_y;
182
+ const d1_z = i1_z - i2_z;
183
+
184
+ // compute rotation axis
185
+ const cross_x = d0_y * d1_z - d0_z * d1_y;
186
+ const cross_y = d0_z * d1_x - d0_x * d1_z;
187
+ const cross_z = d0_x * d1_y - d0_y * d1_x;
188
+
189
+ const angle = v3_angle_cos_between(d0_x, d0_y, d0_z, d1_x, d1_y, d1_z);
190
+
191
+ const length_inv = 1 / Math.hypot(cross_x, cross_y, cross_z);
192
+
193
+ out[0] = cross_x * length_inv;
194
+ out[1] = cross_y * length_inv;
195
+ out[2] = cross_z * length_inv;
196
+
197
+ // bend amount
198
+ out[3] = (1 - Math.abs(angle)) * 0.5;
199
+ }