@woosh/meep-engine 2.163.0 → 2.163.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.
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"description": "Pure JavaScript game engine. Fully featured and production ready.",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"author": "Alexander Goldring",
|
|
9
|
-
"version": "2.163.
|
|
9
|
+
"version": "2.163.1",
|
|
10
10
|
"main": "build/meep.module.js",
|
|
11
11
|
"module": "build/meep.module.js",
|
|
12
12
|
"exports": {
|
|
@@ -1,202 +1,218 @@
|
|
|
1
|
-
import { BufferGeometry, Vector3 } from "three";
|
|
2
|
-
import { assert } from "../../../../../../core/assert.js";
|
|
3
|
-
import { v3_angle_cos_between } from "../../../../../../core/geom/vec3/v3_angle_cos_between.js";
|
|
4
|
-
import { v3_length } from "../../../../../../core/geom/vec3/v3_length.js";
|
|
5
|
-
import { CapType } from "../CapType.js";
|
|
6
|
-
import { append_compute_cap_geometry_size, make_cap } from "./make_cap.js";
|
|
7
|
-
import { make_ring_faces } from "./make_ring_faces.js";
|
|
8
|
-
import { make_ring_vertices } from "./make_ring_vertices.js";
|
|
9
|
-
import { StreamGeometryBuilder } from "./StreamGeometryBuilder.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const v4_array = new Float32Array(4);
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
|
|
16
|
-
* @see https://github.com/hofk/THREEg.js/blob/488f1128a25321a76888aa1fa19db64750318444/THREEg.js#L3483
|
|
17
|
-
* @param {Float32Array|number[]} in_positions
|
|
18
|
-
* @param {Vector3[]} in_normals
|
|
19
|
-
* @param {Vector3[]} in_binormals
|
|
20
|
-
* @param {Vector3[]} in_tangents
|
|
21
|
-
* @param {number[]} shape
|
|
22
|
-
* @param {number[]|Float32Array} shape_normal
|
|
23
|
-
* @param {number} shape_length
|
|
24
|
-
* @param {number[]|Float32Array} shape_transform
|
|
25
|
-
* @param {boolean} [closed]
|
|
26
|
-
* @param {CapType} [cap_type]
|
|
27
|
-
* @returns {BufferGeometry}
|
|
28
|
-
*/
|
|
29
|
-
export function makeTubeGeometry(
|
|
30
|
-
in_positions, in_normals, in_binormals, in_tangents,
|
|
31
|
-
shape, shape_normal, shape_length, shape_transform, closed = false, cap_type = CapType.Round
|
|
32
|
-
) {
|
|
33
|
-
assert.enum(cap_type, CapType, 'cap_type');
|
|
34
|
-
assert.isBoolean(closed, 'closed');
|
|
35
|
-
|
|
36
|
-
assert.isNumber(shape_length, 'shape_length');
|
|
37
|
-
assert.isArrayLike(shape, 'shape');
|
|
38
|
-
|
|
39
|
-
const out = new StreamGeometryBuilder();
|
|
40
|
-
|
|
41
|
-
// helper variables
|
|
42
|
-
|
|
43
|
-
const point_count = in_positions.length / 3;
|
|
44
|
-
const tubular_segments = point_count - 1;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const geometry_size = {
|
|
48
|
-
vertex_count: (tubular_segments + 1) * (shape_length + 1),
|
|
49
|
-
polygon_count: tubular_segments * shape_length * 2
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
if (!closed) {
|
|
53
|
-
append_compute_cap_geometry_size(2, geometry_size, shape_length, cap_type);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
out.allocate(
|
|
57
|
-
geometry_size.vertex_count,
|
|
58
|
-
geometry_size.polygon_count
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
// create buffer data
|
|
62
|
-
|
|
63
|
-
if (!closed) {
|
|
64
|
-
// start cap
|
|
65
|
-
make_cap(
|
|
66
|
-
out, 0,
|
|
67
|
-
in_positions, in_normals, in_binormals, in_tangents,
|
|
68
|
-
shape, shape_normal, shape_length, shape_transform, 1, cap_type
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const index_offset = out.cursor_vertices;
|
|
73
|
-
|
|
74
|
-
for (let i = 0; i < tubular_segments; i++) {
|
|
75
|
-
|
|
76
|
-
generateSegment(i);
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// if the geometry is not closed, generate the last row of vertices and normals
|
|
81
|
-
// at the regular position on the given path
|
|
82
|
-
//
|
|
83
|
-
// if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)
|
|
84
|
-
|
|
85
|
-
generateSegment((closed === false) ? tubular_segments : 0);
|
|
86
|
-
|
|
87
|
-
// finally create faces
|
|
88
|
-
make_ring_faces(out, index_offset, tubular_segments, shape_length);
|
|
89
|
-
|
|
90
|
-
if (!closed) {
|
|
91
|
-
// end cap
|
|
92
|
-
make_cap(
|
|
93
|
-
out, point_count - 1,
|
|
94
|
-
in_positions, in_normals, in_binormals, in_tangents,
|
|
95
|
-
shape, shape_normal, shape_length, shape_transform, -1, cap_type
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
*
|
|
101
|
-
* @param {number} i
|
|
102
|
-
*/
|
|
103
|
-
function generateSegment(i) {
|
|
104
|
-
|
|
105
|
-
// we use getPointAt to sample evenly distributed points from the given path
|
|
106
|
-
|
|
107
|
-
const i3 = i * 3;
|
|
108
|
-
|
|
109
|
-
const Px = in_positions[i3];
|
|
110
|
-
const Py = in_positions[i3 + 1];
|
|
111
|
-
const Pz = in_positions[i3 + 2];
|
|
112
|
-
|
|
113
|
-
// retrieve corresponding normal and binormal
|
|
114
|
-
|
|
115
|
-
const N = in_normals[i];
|
|
116
|
-
const B = in_binormals[i];
|
|
117
|
-
|
|
118
|
-
// generate normals and vertices for the current segment
|
|
119
|
-
compute_bend_normal(v4_array, i, tubular_segments, in_positions);
|
|
120
|
-
|
|
121
|
-
make_ring_vertices(
|
|
122
|
-
out,
|
|
123
|
-
Px, Py, Pz,
|
|
124
|
-
N, B, in_tangents[i],
|
|
125
|
-
i / tubular_segments, v4_array,
|
|
126
|
-
shape, shape_normal, shape_length, shape_transform
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return out.build();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
*
|
|
138
|
-
* @param {number[]|Float32Array} out
|
|
139
|
-
* @param {number} index
|
|
140
|
-
* @param {number} index_count
|
|
141
|
-
* @param {number[]|Float32Array} positions
|
|
142
|
-
*/
|
|
143
|
-
function compute_bend_normal(
|
|
144
|
-
out,
|
|
145
|
-
index,
|
|
146
|
-
index_count,
|
|
147
|
-
positions
|
|
148
|
-
) {
|
|
149
|
-
if (index <= 0 || index >= index_count - 1) {
|
|
150
|
-
// end points, no bending
|
|
151
|
-
|
|
152
|
-
out[0] = 0;
|
|
153
|
-
out[1] = 1;
|
|
154
|
-
out[2] = 0;
|
|
155
|
-
out[3] = 0;
|
|
156
|
-
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const index_next = index + 1;
|
|
161
|
-
const index_prev = index - 1;
|
|
162
|
-
|
|
163
|
-
const address_current = index * 3;
|
|
164
|
-
const address_next = index_next * 3;
|
|
165
|
-
const address_prev = index_prev * 3;
|
|
166
|
-
|
|
167
|
-
const i0_x = positions[address_prev];
|
|
168
|
-
const i0_y = positions[address_prev + 1];
|
|
169
|
-
const i0_z = positions[address_prev + 2];
|
|
170
|
-
|
|
171
|
-
const i1_x = positions[address_current];
|
|
172
|
-
const i1_y = positions[address_current + 1];
|
|
173
|
-
const i1_z = positions[address_current + 2];
|
|
174
|
-
|
|
175
|
-
const i2_x = positions[address_next];
|
|
176
|
-
const i2_y = positions[address_next + 1];
|
|
177
|
-
const i2_z = positions[address_next + 2];
|
|
178
|
-
|
|
179
|
-
const d0_x = i0_x - i1_x;
|
|
180
|
-
const d0_y = i0_y - i1_y;
|
|
181
|
-
const d0_z = i0_z - i1_z;
|
|
182
|
-
|
|
183
|
-
const d1_x = i1_x - i2_x;
|
|
184
|
-
const d1_y = i1_y - i2_y;
|
|
185
|
-
const d1_z = i1_z - i2_z;
|
|
186
|
-
|
|
187
|
-
// compute rotation axis
|
|
188
|
-
const cross_x = d0_y * d1_z - d0_z * d1_y;
|
|
189
|
-
const cross_y = d0_z * d1_x - d0_x * d1_z;
|
|
190
|
-
const cross_z = d0_x * d1_y - d0_y * d1_x;
|
|
191
|
-
|
|
192
|
-
const angle = v3_angle_cos_between(d0_x, d0_y, d0_z, d1_x, d1_y, d1_z);
|
|
193
|
-
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
1
|
+
import { BufferGeometry, Vector3 } from "three";
|
|
2
|
+
import { assert } from "../../../../../../core/assert.js";
|
|
3
|
+
import { v3_angle_cos_between } from "../../../../../../core/geom/vec3/v3_angle_cos_between.js";
|
|
4
|
+
import { v3_length } from "../../../../../../core/geom/vec3/v3_length.js";
|
|
5
|
+
import { CapType } from "../CapType.js";
|
|
6
|
+
import { append_compute_cap_geometry_size, make_cap } from "./make_cap.js";
|
|
7
|
+
import { make_ring_faces } from "./make_ring_faces.js";
|
|
8
|
+
import { make_ring_vertices } from "./make_ring_vertices.js";
|
|
9
|
+
import { StreamGeometryBuilder } from "./StreamGeometryBuilder.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const v4_array = new Float32Array(4);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
|
|
16
|
+
* @see https://github.com/hofk/THREEg.js/blob/488f1128a25321a76888aa1fa19db64750318444/THREEg.js#L3483
|
|
17
|
+
* @param {Float32Array|number[]} in_positions
|
|
18
|
+
* @param {Vector3[]} in_normals
|
|
19
|
+
* @param {Vector3[]} in_binormals
|
|
20
|
+
* @param {Vector3[]} in_tangents
|
|
21
|
+
* @param {number[]} shape
|
|
22
|
+
* @param {number[]|Float32Array} shape_normal
|
|
23
|
+
* @param {number} shape_length
|
|
24
|
+
* @param {number[]|Float32Array} shape_transform
|
|
25
|
+
* @param {boolean} [closed]
|
|
26
|
+
* @param {CapType} [cap_type]
|
|
27
|
+
* @returns {BufferGeometry}
|
|
28
|
+
*/
|
|
29
|
+
export function makeTubeGeometry(
|
|
30
|
+
in_positions, in_normals, in_binormals, in_tangents,
|
|
31
|
+
shape, shape_normal, shape_length, shape_transform, closed = false, cap_type = CapType.Round
|
|
32
|
+
) {
|
|
33
|
+
assert.enum(cap_type, CapType, 'cap_type');
|
|
34
|
+
assert.isBoolean(closed, 'closed');
|
|
35
|
+
|
|
36
|
+
assert.isNumber(shape_length, 'shape_length');
|
|
37
|
+
assert.isArrayLike(shape, 'shape');
|
|
38
|
+
|
|
39
|
+
const out = new StreamGeometryBuilder();
|
|
40
|
+
|
|
41
|
+
// helper variables
|
|
42
|
+
|
|
43
|
+
const point_count = in_positions.length / 3;
|
|
44
|
+
const tubular_segments = point_count - 1;
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
const geometry_size = {
|
|
48
|
+
vertex_count: (tubular_segments + 1) * (shape_length + 1),
|
|
49
|
+
polygon_count: tubular_segments * shape_length * 2
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
if (!closed) {
|
|
53
|
+
append_compute_cap_geometry_size(2, geometry_size, shape_length, cap_type);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
out.allocate(
|
|
57
|
+
geometry_size.vertex_count,
|
|
58
|
+
geometry_size.polygon_count
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// create buffer data
|
|
62
|
+
|
|
63
|
+
if (!closed) {
|
|
64
|
+
// start cap
|
|
65
|
+
make_cap(
|
|
66
|
+
out, 0,
|
|
67
|
+
in_positions, in_normals, in_binormals, in_tangents,
|
|
68
|
+
shape, shape_normal, shape_length, shape_transform, 1, cap_type
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const index_offset = out.cursor_vertices;
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < tubular_segments; i++) {
|
|
75
|
+
|
|
76
|
+
generateSegment(i);
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// if the geometry is not closed, generate the last row of vertices and normals
|
|
81
|
+
// at the regular position on the given path
|
|
82
|
+
//
|
|
83
|
+
// if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)
|
|
84
|
+
|
|
85
|
+
generateSegment((closed === false) ? tubular_segments : 0);
|
|
86
|
+
|
|
87
|
+
// finally create faces
|
|
88
|
+
make_ring_faces(out, index_offset, tubular_segments, shape_length);
|
|
89
|
+
|
|
90
|
+
if (!closed) {
|
|
91
|
+
// end cap
|
|
92
|
+
make_cap(
|
|
93
|
+
out, point_count - 1,
|
|
94
|
+
in_positions, in_normals, in_binormals, in_tangents,
|
|
95
|
+
shape, shape_normal, shape_length, shape_transform, -1, cap_type
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
*
|
|
101
|
+
* @param {number} i
|
|
102
|
+
*/
|
|
103
|
+
function generateSegment(i) {
|
|
104
|
+
|
|
105
|
+
// we use getPointAt to sample evenly distributed points from the given path
|
|
106
|
+
|
|
107
|
+
const i3 = i * 3;
|
|
108
|
+
|
|
109
|
+
const Px = in_positions[i3];
|
|
110
|
+
const Py = in_positions[i3 + 1];
|
|
111
|
+
const Pz = in_positions[i3 + 2];
|
|
112
|
+
|
|
113
|
+
// retrieve corresponding normal and binormal
|
|
114
|
+
|
|
115
|
+
const N = in_normals[i];
|
|
116
|
+
const B = in_binormals[i];
|
|
117
|
+
|
|
118
|
+
// generate normals and vertices for the current segment
|
|
119
|
+
compute_bend_normal(v4_array, i, tubular_segments, in_positions);
|
|
120
|
+
|
|
121
|
+
make_ring_vertices(
|
|
122
|
+
out,
|
|
123
|
+
Px, Py, Pz,
|
|
124
|
+
N, B, in_tangents[i],
|
|
125
|
+
i / tubular_segments, v4_array,
|
|
126
|
+
shape, shape_normal, shape_length, shape_transform
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
return out.build();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
*
|
|
138
|
+
* @param {number[]|Float32Array} out
|
|
139
|
+
* @param {number} index
|
|
140
|
+
* @param {number} index_count
|
|
141
|
+
* @param {number[]|Float32Array} positions
|
|
142
|
+
*/
|
|
143
|
+
function compute_bend_normal(
|
|
144
|
+
out,
|
|
145
|
+
index,
|
|
146
|
+
index_count,
|
|
147
|
+
positions
|
|
148
|
+
) {
|
|
149
|
+
if (index <= 0 || index >= index_count - 1) {
|
|
150
|
+
// end points, no bending
|
|
151
|
+
|
|
152
|
+
out[0] = 0;
|
|
153
|
+
out[1] = 1;
|
|
154
|
+
out[2] = 0;
|
|
155
|
+
out[3] = 0;
|
|
156
|
+
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const index_next = index + 1;
|
|
161
|
+
const index_prev = index - 1;
|
|
162
|
+
|
|
163
|
+
const address_current = index * 3;
|
|
164
|
+
const address_next = index_next * 3;
|
|
165
|
+
const address_prev = index_prev * 3;
|
|
166
|
+
|
|
167
|
+
const i0_x = positions[address_prev];
|
|
168
|
+
const i0_y = positions[address_prev + 1];
|
|
169
|
+
const i0_z = positions[address_prev + 2];
|
|
170
|
+
|
|
171
|
+
const i1_x = positions[address_current];
|
|
172
|
+
const i1_y = positions[address_current + 1];
|
|
173
|
+
const i1_z = positions[address_current + 2];
|
|
174
|
+
|
|
175
|
+
const i2_x = positions[address_next];
|
|
176
|
+
const i2_y = positions[address_next + 1];
|
|
177
|
+
const i2_z = positions[address_next + 2];
|
|
178
|
+
|
|
179
|
+
const d0_x = i0_x - i1_x;
|
|
180
|
+
const d0_y = i0_y - i1_y;
|
|
181
|
+
const d0_z = i0_z - i1_z;
|
|
182
|
+
|
|
183
|
+
const d1_x = i1_x - i2_x;
|
|
184
|
+
const d1_y = i1_y - i2_y;
|
|
185
|
+
const d1_z = i1_z - i2_z;
|
|
186
|
+
|
|
187
|
+
// compute rotation axis
|
|
188
|
+
const cross_x = d0_y * d1_z - d0_z * d1_y;
|
|
189
|
+
const cross_y = d0_z * d1_x - d0_x * d1_z;
|
|
190
|
+
const cross_z = d0_x * d1_y - d0_y * d1_x;
|
|
191
|
+
|
|
192
|
+
const angle = v3_angle_cos_between(d0_x, d0_y, d0_z, d1_x, d1_y, d1_z);
|
|
193
|
+
|
|
194
|
+
const cross_length = v3_length(cross_x, cross_y, cross_z);
|
|
195
|
+
|
|
196
|
+
if (cross_length < 1e-10) {
|
|
197
|
+
// Collinear (or duplicate) neighbours: zero curvature, so no bend.
|
|
198
|
+
// The rotation axis is undefined here and computing it would be
|
|
199
|
+
// 0 / 0 = NaN, poisoning this ring's vertices (visible as shattered
|
|
200
|
+
// geometry on short / straight resampled segments, e.g. the animated
|
|
201
|
+
// marching dashes). Emit the same no-bend value the path ends use.
|
|
202
|
+
out[0] = 0;
|
|
203
|
+
out[1] = 1;
|
|
204
|
+
out[2] = 0;
|
|
205
|
+
out[3] = 0;
|
|
206
|
+
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const length_inv = 1 / cross_length;
|
|
211
|
+
|
|
212
|
+
out[0] = cross_x * length_inv;
|
|
213
|
+
out[1] = cross_y * length_inv;
|
|
214
|
+
out[2] = cross_z * length_inv;
|
|
215
|
+
|
|
216
|
+
// bend amount
|
|
217
|
+
out[3] = (1 - Math.abs(angle)) * 0.5;
|
|
218
|
+
}
|