@woosh/meep-engine 2.35.16 → 2.36.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 (101) hide show
  1. package/core/binary/BinaryBuffer.js +42 -17
  2. package/core/bvh2/BinaryNode.js +1 -1
  3. package/core/bvh2/aabb3/AABB3.js +2 -2
  4. package/core/bvh2/aabb3/aabb3_array_combine.js +20 -0
  5. package/core/bvh2/aabb3/aabb3_array_intersects_frustum_array.js +20 -0
  6. package/core/bvh2/aabb3/aabb3_array_intersects_ray.js +1 -0
  7. package/core/bvh2/aabb3/aabb3_array_intersects_ray_array.js +14 -0
  8. package/core/bvh2/{aabb3_combined_surface_area.js → aabb3/aabb3_combined_surface_area.js} +3 -3
  9. package/core/bvh2/aabb3/aabb3_intersects_frustum_array.js +1 -19
  10. package/core/bvh2/bvh3/BVHNodeProxy.js +53 -0
  11. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +848 -0
  12. package/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.spec.js +142 -0
  13. package/core/bvh2/bvh3/bvh_query_leaves_generic.js +79 -0
  14. package/core/bvh2/bvh3/bvh_query_leaves_ray.js +95 -0
  15. package/core/bvh2/bvh3/query/BVHQuery.js +11 -0
  16. package/core/bvh2/bvh3/query/BVHQueryIntersectsFrustum.js +36 -0
  17. package/core/bvh2/bvh3/query/BVHQueryIntersectsRay.js +36 -0
  18. package/core/collection/array/copyArray.js +2 -2
  19. package/core/collection/table/RowFirstTable.js +25 -7
  20. package/core/collection/table/RowFirstTableSpec.js +4 -4
  21. package/core/geom/3d/aabb/aabb3_matrix4_project.spec.js +1 -1
  22. package/{engine/graphics/util → core/geom/3d/aabb}/computeBoundingBoxFromVertexData.js +1 -1
  23. package/core/geom/3d/compose_matrix4_array.js +53 -0
  24. package/{engine/graphics/util → core/geom/3d}/decompose_matrix_4_array.js +0 -0
  25. package/core/geom/AABB2.d.ts +2 -0
  26. package/core/geom/Vector1.js +2 -3
  27. package/core/geom/Vector3.js +5 -1
  28. package/core/process/undo/ActionProcessor.js +31 -3
  29. package/core/process/undo/ActionProcessor.spec.js +1 -1
  30. package/engine/animation/curve/AnimationCurve.js +40 -1
  31. package/engine/animation/curve/Keyframe.js +19 -0
  32. package/engine/animation/curve/compression/animation_curve_to_float_array.js +35 -0
  33. package/engine/animation/curve/compression/downsample_float_array.js +50 -0
  34. package/engine/animation/curve/compression/downsample_float_array_curve_by_error.js +147 -0
  35. package/engine/animation/curve/compression/downsample_float_array_curve_by_error.spec.js +47 -0
  36. package/engine/animation/curve/compression/prototypeCurveCompression.js +154 -0
  37. package/engine/animation/curve/draw/build_curve_editor.js +316 -0
  38. package/engine/animation/curve/draw/build_plot_entity_from_array.js +47 -0
  39. package/engine/animation/curve/draw/build_tangent_editor.js +115 -0
  40. package/engine/animation/curve/draw/draw_grid.js +27 -0
  41. package/engine/animation/curve/draw/draw_label.js +17 -0
  42. package/engine/animation/curve/draw/plot_array.js +76 -0
  43. package/engine/animation/curve/draw/plot_data.js +49 -0
  44. package/engine/asset/loaders/GLTFAssetLoader.js +3 -0
  45. package/engine/asset/loaders/gltf/GLTFAssetTransformMikkTSpaceTangents.js +27 -0
  46. package/engine/ecs/EntityComponentDataset.js +7 -4
  47. package/engine/ecs/foliage/InstancedFoliage.js +1 -1
  48. package/engine/ecs/foliage/ViewState.js +1 -1
  49. package/engine/ecs/parent/EntityNode.d.ts +7 -3
  50. package/engine/ecs/parent/EntityNode.js +21 -0
  51. package/engine/ecs/parent/entity_node_compute_bounding_box.js +41 -0
  52. package/engine/ecs/transform/Transform.js +2 -2
  53. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.d.ts +5 -0
  54. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +46 -12
  55. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +81 -95
  56. package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +16 -5
  57. package/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +2 -0
  58. package/engine/graphics/ecs/mesh-v2/sample/load_gltf.js +1 -0
  59. package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +3 -4
  60. package/engine/graphics/ecs/path/PathDisplaySystem.js +2 -1
  61. package/engine/graphics/ecs/path/testPathDisplaySystem.js +8 -8
  62. package/engine/graphics/ecs/path/tube/CapType.d.ts +5 -0
  63. package/engine/graphics/ecs/path/tube/CapType.js +9 -0
  64. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +2 -0
  65. package/engine/graphics/ecs/path/tube/TubePathStyle.js +10 -1
  66. package/engine/graphics/ecs/path/tube/build/GeometryOutput.js +90 -0
  67. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +3 -2
  68. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +1 -1
  69. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +1 -2
  70. package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +353 -91
  71. package/engine/graphics/ecs/path/tube/prototypeAnimatedPathMask.js +390 -0
  72. package/engine/graphics/geometry/MikkT/MikkTSpace.js +2210 -0
  73. package/engine/graphics/geometry/MikkT/MikkTSpace.spec.js +22 -0
  74. package/engine/graphics/geometry/MikkT/buffer_geometry_ensure_tangents.js +34 -0
  75. package/engine/graphics/geometry/MikkT/buffer_geometry_generate_tangents.js +28 -0
  76. package/engine/graphics/{compute_geometry_polycount.js → geometry/compute_geometry_polycount.js} +0 -0
  77. package/engine/graphics/geometry/instancing/InstancedMeshGroup.js +34 -3
  78. package/engine/graphics/geometry/skining/computeSkinnedMeshBoundingVolumes.js +1 -1
  79. package/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.js +83 -0
  80. package/engine/graphics/micron/format/serialization/collection/geometry_collection_serialization.spec.js +24 -0
  81. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +42 -19
  82. package/engine/graphics/micron/plugin/GLTF_MICRON_ID_FIELD.js +5 -0
  83. package/engine/graphics/micron/plugin/MicronRenderPlugin.js +0 -4
  84. package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +10 -0
  85. package/engine/graphics/micron/prototypeVirtualGeometry.js +5 -3
  86. package/engine/graphics/micron/render/refinement/ActivePatchList.js +2 -2
  87. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +2 -2
  88. package/engine/graphics/micron/render/v1/MicronRenderContext.js +0 -6
  89. package/engine/graphics/render/forward_plus/LightManager.js +3 -1
  90. package/engine/graphics/util/composeMatrix4.js +1 -53
  91. package/engine/graphics/util/composeMatrix4RotationScale.js +1 -1
  92. package/engine/intelligence/behavior/ecs/BehaviorComponent.js +1 -1
  93. package/engine/intelligence/blackboard/AbstractBlackboard.js +9 -0
  94. package/engine/notify/Notification.js +1 -1
  95. package/engine/ui/DraggableAspect.js +4 -1
  96. package/engine/ui/notification/ViewEmitter.js +7 -1
  97. package/engine/ui/notification/notificationGenerator.js +1 -1
  98. package/engine/ui/scene/SceneGUIContext.js +11 -130
  99. package/engine/ui/scene/initializeNotifications.js +141 -0
  100. package/package.json +1 -1
  101. package/view/elements/notify/NotificationView.js +1 -2
@@ -1,161 +1,423 @@
1
- import { BufferGeometry, Float32BufferAttribute, Vector3 } from "three";
1
+ import { BufferGeometry, Vector3 } from "three";
2
+ import { GeometryOutput } from "./GeometryOutput.js";
3
+ import { CapType } from "../CapType.js";
4
+ import { max2 } from "../../../../../../core/math/max2.js";
5
+
2
6
 
3
7
  /**
4
- * @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
5
- * @param {Float32Array|number[]} points
6
- * @param {Vector3[]} _normals
7
- * @param {Vector3[]} binormals
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
8
16
  * @param {number} radius
9
- * @param {number} radialSegments
10
- * @param {boolean} [closed]
11
- * @returns {BufferGeometry}
17
+ * @param {number} angular_step
18
+ * @param {number} radial_segments
12
19
  */
13
- export function makeTubeGeometry(points, _normals, binormals, radius, radialSegments, closed = false) {
14
- const geometry = new BufferGeometry();
15
- // helper variables
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;
16
30
 
17
- const vertex = new Vector3();
18
- const normal = new Vector3();
31
+ for (let j = 0; j <= radial_segments; j++) {
32
+ const angle = j * angular_step;
19
33
 
20
- // buffer
34
+ const sin = Math.sin(angle);
35
+ const cos = -Math.cos(angle);
21
36
 
22
- const vertices = [];
23
- const normals = [];
24
- const uvs = [];
25
- const indices = [];
37
+ // normal
26
38
 
27
- const point_count = points.length / 3;
28
- const tubularSegments = point_count - 1;
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);
29
42
 
30
- // create buffer data
43
+ const normal_length = 1 / Math.hypot(normal_x_r, normal_y_r, normal_z_r);
31
44
 
32
- generateBufferData();
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;
33
48
 
34
- // build geometry
49
+ const cv = out.cursor_vertices;
50
+ const cv3 = cv * 3;
35
51
 
36
- geometry.setIndex(indices);
37
- geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3));
38
- geometry.setAttribute('normal', new Float32BufferAttribute(normals, 3));
39
- geometry.setAttribute('uv', new Float32BufferAttribute(uvs, 2));
52
+ out_normals[cv3] = normal_x;
53
+ out_normals[cv3 + 1] = normal_y;
54
+ out_normals[cv3 + 2] = normal_z;
40
55
 
41
- // functions
56
+ // position
42
57
 
43
- function generateBufferData() {
58
+ const vx = Px + radius * normal_x;
59
+ const vy = Py + radius * normal_y;
60
+ const vz = Pz + radius * normal_z;
44
61
 
45
- for (let i = 0; i < tubularSegments; i++) {
62
+ out_positions[cv3] = vx;
63
+ out_positions[cv3 + 1] = vy;
64
+ out_positions[cv3 + 2] = vz;
46
65
 
47
- generateSegment(i);
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;
48
109
 
110
+ indices[ci + 3] = b;
111
+ indices[ci + 4] = c;
112
+ indices[ci + 5] = d;
113
+
114
+ out.cursor_indices += 6;
49
115
  }
50
116
 
51
- // if the geometry is not closed, generate the last row of vertices and normals
52
- // at the regular position on the given path
53
- //
54
- // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)
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);
55
148
 
56
- generateSegment((closed === false) ? tubularSegments : 0);
149
+ const i3 = index * 3;
57
150
 
58
- // uvs are generated in a separate function.
59
- // this makes it easy compute correct values for closed geometries
151
+ const Px = in_positions[i3];
152
+ const Py = in_positions[i3 + 1];
153
+ const Pz = in_positions[i3 + 2];
60
154
 
61
- generateUVs();
155
+ // retrieve corresponding normal and binormal
62
156
 
63
- // finally create faces
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;
64
171
 
65
- generateIndices();
172
+ const index_offset = out.cursor_vertices;
66
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
+ );
67
194
  }
68
195
 
69
- function generateSegment(i) {
196
+ make_ring_faces(out, index_offset, cap_segment_count, radial_segments);
197
+ }
70
198
 
71
- // we use getPointAt to sample evenly distributed points from the given path
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;
72
218
 
73
- const i3 = i * 3;
219
+ const Px = in_positions[i3];
220
+ const Py = in_positions[i3 + 1];
221
+ const Pz = in_positions[i3 + 2];
74
222
 
75
- const Px = points[i3];
76
- const Py = points[i3 + 1];
77
- const Pz = points[i3 + 2];
223
+ // retrieve corresponding normal and binormal
78
224
 
79
- // retrieve corresponding normal and binormal
225
+ const N = in_normals[index];
226
+ const B = in_binormals[index];
80
227
 
81
- const N = _normals[i];
82
- const B = binormals[i];
228
+ const tangent = new Vector3();
229
+ tangent.crossVectors(N, B);
230
+ tangent.normalize();
83
231
 
84
- // generate normals and vertices for the current segment
232
+ const normal = direction > 0 ? N.clone().negate() : N;
233
+
234
+ const angular_step_j = (Math.PI * 2) / radial_segments;
85
235
 
86
- const angular_step = (Math.PI * 2) / radialSegments;
236
+ const uv_u = index / (in_positions.length / 3 - 1);
87
237
 
88
- for (let j = 0; j <= radialSegments; j++) {
89
- const v = j * angular_step;
238
+ const index_offset = out.cursor_vertices;
90
239
 
91
- const sin = Math.sin(v);
92
- const cos = -Math.cos(v);
240
+ make_ring_vertices(
241
+ out,
242
+ Px, Py, Pz,
243
+ normal, B, uv_u,
244
+ radius, angular_step_j, radial_segments
245
+ );
93
246
 
94
- // normal
247
+ make_ring_vertices(
248
+ out,
249
+ Px, Py, Pz,
250
+ normal, B, uv_u,
251
+ 0, angular_step_j, radial_segments
252
+ );
95
253
 
96
- normal.x = (cos * N.x + sin * B.x);
97
- normal.y = (cos * N.y + sin * B.y);
98
- normal.z = (cos * N.z + sin * B.z);
99
- normal.normalize();
254
+ make_ring_faces(out, index_offset, 1, radial_segments);
255
+ }
100
256
 
101
- normals.push(normal.x, normal.y, normal.z);
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
+ }
102
297
 
103
- // vertex
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
+ }
104
321
 
105
- vertex.x = Px + radius * normal.x;
106
- vertex.y = Py + radius * normal.y;
107
- vertex.z = Pz + radius * normal.z;
322
+ /**
323
+ * @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
324
+ * @param {Float32Array|number[]} in_positions
325
+ * @param {Vector3[]} in_normals
326
+ * @param {Vector3[]} in_binormals
327
+ * @param {number} radius
328
+ * @param {number} radial_segments
329
+ * @param {boolean} [closed]
330
+ * @param {CapType} [cap_type]
331
+ * @returns {BufferGeometry}
332
+ */
333
+ export function makeTubeGeometry(in_positions, in_normals, in_binormals, radius, radial_segments, closed = false, cap_type = CapType.Round) {
108
334
 
109
- vertices.push(vertex.x, vertex.y, vertex.z);
335
+ const out = new GeometryOutput();
110
336
 
111
- }
337
+ // helper variables
338
+
339
+ const point_count = in_positions.length / 3;
340
+ const tubular_segments = point_count - 1;
341
+
342
+
343
+ const geometry_size = {
344
+ vertex_count: (tubular_segments + 1) * (radial_segments + 1),
345
+ polygon_count: tubular_segments * radial_segments * 2
346
+ };
112
347
 
348
+ if (!closed) {
349
+ compute_cap_geometry_size(2, geometry_size, radial_segments, cap_type);
113
350
  }
114
351
 
115
- function generateIndices() {
116
- let i = 0, j = 0;
352
+ out.allocate(
353
+ geometry_size.vertex_count,
354
+ geometry_size.polygon_count
355
+ );
117
356
 
118
- for (j = 1; j <= tubularSegments; j++) {
357
+ // create buffer data
358
+ if (!closed) {
359
+ // start cap
360
+ make_cap(out, 0, in_positions, in_normals, in_binormals, radius, radial_segments, 1, cap_type);
361
+ }
119
362
 
120
- for (i = 1; i <= radialSegments; i++) {
363
+ generateBufferData();
121
364
 
122
- const s_1 = radialSegments + 1;
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);
368
+ }
123
369
 
124
- const a = s_1 * (j - 1) + (i - 1);
125
- const b = s_1 * j + (i - 1);
126
- const c = s_1 * j + i;
127
- const d = s_1 * (j - 1) + i;
370
+ // functions
128
371
 
129
- // faces
372
+ function generateBufferData() {
373
+ const index_offset = out.cursor_vertices;
130
374
 
131
- indices.push(a, b, d);
132
- indices.push(b, c, d);
375
+ for (let i = 0; i < tubular_segments; i++) {
133
376
 
134
- }
377
+ generateSegment(i);
135
378
 
136
379
  }
137
380
 
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)
385
+
386
+ generateSegment((closed === false) ? tubular_segments : 0);
387
+
388
+ // finally create faces
389
+ make_ring_faces(out, index_offset, tubular_segments, radial_segments);
390
+
138
391
  }
139
392
 
140
- function generateUVs() {
141
- let i = 0, j = 0;
393
+ function generateSegment(i) {
142
394
 
143
- for (i = 0; i <= tubularSegments; i++) {
395
+ // we use getPointAt to sample evenly distributed points from the given path
396
+
397
+ const i3 = i * 3;
144
398
 
145
- const u = i / tubularSegments;
399
+ const Px = in_positions[i3];
400
+ const Py = in_positions[i3 + 1];
401
+ const Pz = in_positions[i3 + 2];
146
402
 
147
- for (j = 0; j <= radialSegments; j++) {
403
+ // retrieve corresponding normal and binormal
148
404
 
149
- const v = j / radialSegments;
405
+ const N = in_normals[i];
406
+ const B = in_binormals[i];
150
407
 
151
- uvs.push(u, v);
408
+ // generate normals and vertices for the current segment
152
409
 
153
- }
410
+ const angular_step = (Math.PI * 2) / radial_segments;
154
411
 
155
- }
412
+ make_ring_vertices(
413
+ out,
414
+ Px, Py, Pz,
415
+ N, B, i / tubular_segments,
416
+ radius, angular_step, radial_segments
417
+ );
156
418
 
157
419
  }
158
420
 
159
421
 
160
- return geometry;
422
+ return out.build();
161
423
  }