@woosh/meep-engine 2.163.1 → 2.163.2
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 +1 -1
- package/src/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.d.ts.map +1 -1
- package/src/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +306 -226
- package/src/engine/graphics/ecs/path/tube/build/make_cap.d.ts.map +1 -1
- package/src/engine/graphics/ecs/path/tube/build/make_cap.js +26 -17
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.2",
|
|
10
10
|
"main": "build/meep.module.js",
|
|
11
11
|
"module": "build/meep.module.js",
|
|
12
12
|
"exports": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build_geometry_catmullrom.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build_geometry_catmullrom.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js"],"names":[],"mappings":"AAsJA;;;;;;;;;;GAUG;AACH,mFAPW,MAAM,EAAE,gBACR,MAAM,EAAE,GAAC,YAAY,mBACrB,MAAM,EAAE,iBACR,MAAM,eACN,MAAM,GACL,MAAM,cAAc,CAkJ/B"}
|
|
@@ -1,226 +1,306 @@
|
|
|
1
|
-
import { v3_dot } from "../../../../../../core/geom/vec3/v3_dot.js";
|
|
2
|
-
import { v3_length } from "../../../../../../core/geom/vec3/v3_length.js";
|
|
3
|
-
import { clamp } from "../../../../../../core/math/clamp.js";
|
|
4
|
-
import { max2 } from "../../../../../../core/math/max2.js";
|
|
5
|
-
import {
|
|
6
|
-
computeNonuniformCaltmullRomSplineDerivative
|
|
7
|
-
} from "../../../../../../core/math/spline/computeNonuniformCaltmullRomSplineDerivative.js";
|
|
8
|
-
import { PathNormalType } from "../PathNormalType.js";
|
|
9
|
-
import { computeFrenetFrames } from "./computeFrenetFrames.js";
|
|
10
|
-
import { makeTubeGeometry } from "./makeTubeGeometry.js";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const scratch_array_0 = [];
|
|
14
|
-
const scratch_array_1 = [];
|
|
15
|
-
const scratch_array_2 = [];
|
|
16
|
-
const scratch_array_3 = [];
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
*
|
|
20
|
-
* @param {number[]} positions
|
|
21
|
-
* @param {number[]} derivatives
|
|
22
|
-
* @param {number} result_offset
|
|
23
|
-
* @param {Path} path
|
|
24
|
-
* @param {number} offset
|
|
25
|
-
*/
|
|
26
|
-
function sample_path(positions, derivatives, result_offset, path, offset) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (!path.find_index_and_normalized_distance(scratch_array_0, offset)) {
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* @type {number}
|
|
37
|
-
*/
|
|
38
|
-
const i1 = scratch_array_0[0];
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
* @type {number}
|
|
43
|
-
*/
|
|
44
|
-
const t = scratch_array_0[1];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const input_length = path.getPointCount();
|
|
48
|
-
|
|
49
|
-
const max_index = input_length - 1;
|
|
50
|
-
|
|
51
|
-
const i0 = clamp(i1 - 1, 0, max_index);
|
|
52
|
-
const i2 = clamp(i1 + 1, 0, max_index);
|
|
53
|
-
const i3 = clamp(i1 + 2, 0, max_index);
|
|
54
|
-
|
|
55
|
-
path.readPositionToArray(i0, scratch_array_0, 0);
|
|
56
|
-
path.readPositionToArray(i1, scratch_array_1, 0);
|
|
57
|
-
path.readPositionToArray(i2, scratch_array_2, 0);
|
|
58
|
-
path.readPositionToArray(i3, scratch_array_3, 0);
|
|
59
|
-
|
|
60
|
-
computeNonuniformCaltmullRomSplineDerivative(
|
|
61
|
-
positions, result_offset,
|
|
62
|
-
derivatives, result_offset,
|
|
63
|
-
scratch_array_0, scratch_array_1, scratch_array_2, scratch_array_3,
|
|
64
|
-
3, t, 0.5
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
// normalize derivative
|
|
68
|
-
const dx = derivatives[result_offset];
|
|
69
|
-
const dy = derivatives[result_offset + 1];
|
|
70
|
-
const dz = derivatives[result_offset + 2];
|
|
71
|
-
|
|
72
|
-
const mag = v3_length(dx, dy, dz);
|
|
73
|
-
|
|
74
|
-
if (mag !== 0) {
|
|
75
|
-
const inv_mag = 1 / mag;
|
|
76
|
-
|
|
77
|
-
derivatives[result_offset] = dx * inv_mag;
|
|
78
|
-
derivatives[result_offset + 1] = dy * inv_mag;
|
|
79
|
-
derivatives[result_offset + 2] = dz * inv_mag;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return i1;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* @
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
added_points
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
1
|
+
import { v3_dot } from "../../../../../../core/geom/vec3/v3_dot.js";
|
|
2
|
+
import { v3_length } from "../../../../../../core/geom/vec3/v3_length.js";
|
|
3
|
+
import { clamp } from "../../../../../../core/math/clamp.js";
|
|
4
|
+
import { max2 } from "../../../../../../core/math/max2.js";
|
|
5
|
+
import {
|
|
6
|
+
computeNonuniformCaltmullRomSplineDerivative
|
|
7
|
+
} from "../../../../../../core/math/spline/computeNonuniformCaltmullRomSplineDerivative.js";
|
|
8
|
+
import { PathNormalType } from "../PathNormalType.js";
|
|
9
|
+
import { computeFrenetFrames } from "./computeFrenetFrames.js";
|
|
10
|
+
import { makeTubeGeometry } from "./makeTubeGeometry.js";
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const scratch_array_0 = [];
|
|
14
|
+
const scratch_array_1 = [];
|
|
15
|
+
const scratch_array_2 = [];
|
|
16
|
+
const scratch_array_3 = [];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {number[]} positions
|
|
21
|
+
* @param {number[]} derivatives
|
|
22
|
+
* @param {number} result_offset
|
|
23
|
+
* @param {Path} path
|
|
24
|
+
* @param {number} offset
|
|
25
|
+
*/
|
|
26
|
+
function sample_path(positions, derivatives, result_offset, path, offset) {
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if (!path.find_index_and_normalized_distance(scratch_array_0, offset)) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @type {number}
|
|
37
|
+
*/
|
|
38
|
+
const i1 = scratch_array_0[0];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
*
|
|
42
|
+
* @type {number}
|
|
43
|
+
*/
|
|
44
|
+
const t = scratch_array_0[1];
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
const input_length = path.getPointCount();
|
|
48
|
+
|
|
49
|
+
const max_index = input_length - 1;
|
|
50
|
+
|
|
51
|
+
const i0 = clamp(i1 - 1, 0, max_index);
|
|
52
|
+
const i2 = clamp(i1 + 1, 0, max_index);
|
|
53
|
+
const i3 = clamp(i1 + 2, 0, max_index);
|
|
54
|
+
|
|
55
|
+
path.readPositionToArray(i0, scratch_array_0, 0);
|
|
56
|
+
path.readPositionToArray(i1, scratch_array_1, 0);
|
|
57
|
+
path.readPositionToArray(i2, scratch_array_2, 0);
|
|
58
|
+
path.readPositionToArray(i3, scratch_array_3, 0);
|
|
59
|
+
|
|
60
|
+
computeNonuniformCaltmullRomSplineDerivative(
|
|
61
|
+
positions, result_offset,
|
|
62
|
+
derivatives, result_offset,
|
|
63
|
+
scratch_array_0, scratch_array_1, scratch_array_2, scratch_array_3,
|
|
64
|
+
3, t, 0.5
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// normalize derivative
|
|
68
|
+
const dx = derivatives[result_offset];
|
|
69
|
+
const dy = derivatives[result_offset + 1];
|
|
70
|
+
const dz = derivatives[result_offset + 2];
|
|
71
|
+
|
|
72
|
+
const mag = v3_length(dx, dy, dz);
|
|
73
|
+
|
|
74
|
+
if (mag !== 0) {
|
|
75
|
+
const inv_mag = 1 / mag;
|
|
76
|
+
|
|
77
|
+
derivatives[result_offset] = dx * inv_mag;
|
|
78
|
+
derivatives[result_offset + 1] = dy * inv_mag;
|
|
79
|
+
derivatives[result_offset + 2] = dz * inv_mag;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return i1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Stabilise one open-end frame. computeFrenetFrames derives the tangent at a
|
|
87
|
+
* path end from the end chord (P1-P0 / P[n]-P[n-1]); the adaptive resampler sets
|
|
88
|
+
* that neighbour's spacing, so on a curve the chord direction jumps frame to
|
|
89
|
+
* frame and swings the end cap (a small wobble becomes a >100-degree flip as an
|
|
90
|
+
* animated dash slides across a path knot). The analytic spline derivative is
|
|
91
|
+
* smooth in the path parameter and resampling-independent, so adopt it for the
|
|
92
|
+
* end tangent and re-orthogonalise the end normal/binormal around it.
|
|
93
|
+
*
|
|
94
|
+
* @param {{normals:Vector3[],binormals:Vector3[],tangents:Vector3[]}} frames
|
|
95
|
+
* @param {number[]|Float32Array} derivatives normalised per-sample spline derivatives
|
|
96
|
+
* @param {number} i sample index whose frame is stabilised
|
|
97
|
+
* @param {number} derivative_index sample to read the spline derivative from
|
|
98
|
+
* (use an interior neighbour: the analytic derivative is bogus at the
|
|
99
|
+
* path's exact parametric ends, param 0/1, where the spline clamps its
|
|
100
|
+
* control points)
|
|
101
|
+
*/
|
|
102
|
+
function stabilize_end_frame(frames, derivatives, i, derivative_index) {
|
|
103
|
+
const d3 = derivative_index * 3;
|
|
104
|
+
|
|
105
|
+
let tx = derivatives[d3];
|
|
106
|
+
let ty = derivatives[d3 + 1];
|
|
107
|
+
let tz = derivatives[d3 + 2];
|
|
108
|
+
|
|
109
|
+
const tl = Math.sqrt(tx * tx + ty * ty + tz * tz);
|
|
110
|
+
if (!(tl > 1e-8)) {
|
|
111
|
+
// derivative unavailable/degenerate: keep the Frenet frame
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
tx /= tl; ty /= tl; tz /= tl;
|
|
115
|
+
|
|
116
|
+
const N = frames.normals[i];
|
|
117
|
+
|
|
118
|
+
// The spline derivative already points 'forward' (towards increasing
|
|
119
|
+
// samples), the same orientation computeFrenetFrames uses, so adopt it as-is.
|
|
120
|
+
// (Aligning it to the OLD end tangent would re-import that tangent's jitter -
|
|
121
|
+
// when the jittery chord swings past 90 degrees the sign would flip and the
|
|
122
|
+
// cap axis would reverse.)
|
|
123
|
+
frames.tangents[i].set(tx, ty, tz);
|
|
124
|
+
|
|
125
|
+
// re-orthogonalise the existing normal against the new tangent (Gram-Schmidt)
|
|
126
|
+
const dot = N.x * tx + N.y * ty + N.z * tz;
|
|
127
|
+
let nx = N.x - dot * tx;
|
|
128
|
+
let ny = N.y - dot * ty;
|
|
129
|
+
let nz = N.z - dot * tz;
|
|
130
|
+
let nl = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
131
|
+
|
|
132
|
+
if (!(nl > 1e-6)) {
|
|
133
|
+
// old normal was ~parallel to the new tangent: pick any perpendicular axis
|
|
134
|
+
const ax = Math.abs(tx), ay = Math.abs(ty), az = Math.abs(tz);
|
|
135
|
+
if (ax <= ay && ax <= az) { nx = 0; ny = -tz; nz = ty; }
|
|
136
|
+
else if (ay <= az) { nx = -tz; ny = 0; nz = tx; }
|
|
137
|
+
else { nx = -ty; ny = tx; nz = 0; }
|
|
138
|
+
nl = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
139
|
+
}
|
|
140
|
+
nx /= nl; ny /= nl; nz /= nl;
|
|
141
|
+
N.set(nx, ny, nz);
|
|
142
|
+
|
|
143
|
+
// binormal = tangent x normal (matches computeFrenetFrames' convention)
|
|
144
|
+
frames.binormals[i].set(
|
|
145
|
+
ty * nz - tz * ny,
|
|
146
|
+
tz * nx - tx * nz,
|
|
147
|
+
tx * ny - ty * nx
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
*
|
|
153
|
+
* @param {Path} path
|
|
154
|
+
* @param {TubePathStyle} style
|
|
155
|
+
* @param {number[]} shape
|
|
156
|
+
* @param {number[]|Float32Array} shape_normal
|
|
157
|
+
* @param {number[]} shape_transform
|
|
158
|
+
* @param {number} segment_start
|
|
159
|
+
* @param {number} segment_end
|
|
160
|
+
* @return {THREE.BufferGeometry}
|
|
161
|
+
*/
|
|
162
|
+
export function build_geometry_catmullrom(
|
|
163
|
+
path, style,
|
|
164
|
+
shape, shape_normal, shape_transform,
|
|
165
|
+
segment_start, segment_end
|
|
166
|
+
) {
|
|
167
|
+
|
|
168
|
+
const point_count = path.getPointCount();
|
|
169
|
+
|
|
170
|
+
// resample curve
|
|
171
|
+
const total_points = Math.ceil(point_count * style.resolution);
|
|
172
|
+
|
|
173
|
+
const reference_step_size = 1 / max2(0.00001, style.resolution);
|
|
174
|
+
const reference_min_step_size = reference_step_size * 0.25;
|
|
175
|
+
|
|
176
|
+
let step_size = reference_step_size;
|
|
177
|
+
|
|
178
|
+
const path_length = path.length;
|
|
179
|
+
|
|
180
|
+
const sample_positions_f32 = [];
|
|
181
|
+
const sample_derivatives_f32 = [];
|
|
182
|
+
|
|
183
|
+
let added_points = 0;
|
|
184
|
+
|
|
185
|
+
// initial segment
|
|
186
|
+
let last_knot_index = sample_path(
|
|
187
|
+
sample_positions_f32,
|
|
188
|
+
sample_derivatives_f32,
|
|
189
|
+
added_points * 3,
|
|
190
|
+
path,
|
|
191
|
+
segment_start * path_length
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
added_points++;
|
|
195
|
+
|
|
196
|
+
let current_offset = segment_start * path_length;
|
|
197
|
+
|
|
198
|
+
let step_resized_direction = 0;
|
|
199
|
+
|
|
200
|
+
const absolute_end_offset = segment_end * path_length;
|
|
201
|
+
|
|
202
|
+
for (; current_offset < absolute_end_offset; current_offset += step_size) {
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
const point_address = added_points * 3;
|
|
206
|
+
|
|
207
|
+
const knot_index = sample_path(
|
|
208
|
+
sample_positions_f32,
|
|
209
|
+
sample_derivatives_f32,
|
|
210
|
+
point_address,
|
|
211
|
+
path,
|
|
212
|
+
current_offset
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// check difference with previous derivative
|
|
216
|
+
const previous_point_index = added_points - 1;
|
|
217
|
+
const previous_point_address = previous_point_index * 3;
|
|
218
|
+
|
|
219
|
+
// derivatives are basically normals, and dot product is has Cosine value of angle between two vectors
|
|
220
|
+
const dot = v3_dot(
|
|
221
|
+
sample_derivatives_f32[previous_point_address], sample_derivatives_f32[previous_point_address + 1], sample_derivatives_f32[previous_point_address + 2],
|
|
222
|
+
sample_derivatives_f32[point_address], sample_derivatives_f32[point_address + 1], sample_derivatives_f32[point_address + 2]
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// angular difference results in larger visual error for longer runs
|
|
226
|
+
const error_size = (1 - dot) * step_size;
|
|
227
|
+
|
|
228
|
+
if (
|
|
229
|
+
(step_size > reference_min_step_size)
|
|
230
|
+
&& (
|
|
231
|
+
(
|
|
232
|
+
// check if we jumped over a knot, so that we can sample down to get closer to it
|
|
233
|
+
knot_index > last_knot_index
|
|
234
|
+
&& step_size > reference_min_step_size
|
|
235
|
+
)
|
|
236
|
+
|| (
|
|
237
|
+
error_size * 10 > reference_min_step_size
|
|
238
|
+
&& step_resized_direction <= 0
|
|
239
|
+
)
|
|
240
|
+
|| (
|
|
241
|
+
// check if we're getting too close to the end of the segment, this lets us create a nice end
|
|
242
|
+
current_offset + step_size > absolute_end_offset
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
) {
|
|
246
|
+
|
|
247
|
+
// step looks to be too large
|
|
248
|
+
current_offset -= step_size;
|
|
249
|
+
step_size *= 0.5;
|
|
250
|
+
step_resized_direction = -1;
|
|
251
|
+
continue; // retry
|
|
252
|
+
|
|
253
|
+
} else if (
|
|
254
|
+
error_size < 0.02
|
|
255
|
+
&& step_resized_direction >= 0
|
|
256
|
+
) {
|
|
257
|
+
|
|
258
|
+
// step looks to be too small
|
|
259
|
+
current_offset -= step_size;
|
|
260
|
+
step_size *= 2;
|
|
261
|
+
step_resized_direction = 1;
|
|
262
|
+
continue; // retry
|
|
263
|
+
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// reset adaptive step direction
|
|
267
|
+
step_resized_direction = 0;
|
|
268
|
+
// remember the last knot, so we know when we get to the end of the segment
|
|
269
|
+
last_knot_index = knot_index;
|
|
270
|
+
|
|
271
|
+
added_points++
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if ((absolute_end_offset - current_offset + step_size) > 0.0001) {
|
|
275
|
+
// end
|
|
276
|
+
|
|
277
|
+
sample_path(sample_positions_f32, sample_derivatives_f32, added_points * 3, path, absolute_end_offset);
|
|
278
|
+
|
|
279
|
+
added_points++;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// console.log(`Total Points ${added_points}`) // DEBUG info
|
|
283
|
+
|
|
284
|
+
const normal_hint = style.path_normal_type === PathNormalType.FixedStart ? style.path_normal : undefined;
|
|
285
|
+
|
|
286
|
+
const frames = computeFrenetFrames(sample_positions_f32, false, normal_hint);
|
|
287
|
+
|
|
288
|
+
// Stabilise the START frame so its round cap doesn't twitch as the adaptive
|
|
289
|
+
// resampling shifts. Only the start needs it: computeFrenetFrames' start
|
|
290
|
+
// tangent is the first chord (P1-P0), and the resampler's first-segment
|
|
291
|
+
// length/direction jitters frame to frame; the analytic derivative at the
|
|
292
|
+
// start sample is smooth in the path parameter. The END frame's tangent is
|
|
293
|
+
// already stable (the sampler approaches the end with a small, consistent
|
|
294
|
+
// final segment) and the analytic derivative is unreliable at the path's
|
|
295
|
+
// exact parametric end (param 1, where the spline clamps control points), so
|
|
296
|
+
// the end is deliberately left on its chord-based frame.
|
|
297
|
+
const last_sample_index = (sample_positions_f32.length / 3) - 1;
|
|
298
|
+
if (last_sample_index >= 2) {
|
|
299
|
+
stabilize_end_frame(frames, sample_derivatives_f32, 0, 0);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return makeTubeGeometry(
|
|
303
|
+
sample_positions_f32, frames.normals, frames.binormals, frames.tangents,
|
|
304
|
+
shape, shape_normal, shape.length / 2, shape_transform, false, style.cap_type
|
|
305
|
+
);
|
|
306
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"make_cap.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/ecs/path/tube/build/make_cap.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"make_cap.d.ts","sourceRoot":"","sources":["../../../../../../../../src/engine/graphics/ecs/path/tube/build/make_cap.js"],"names":[],"mappings":"AAiRA;;;;;;;;;;;;;;GAcG;AACH,4DAbW,MAAM,gBACN,YAAY,GAAC,MAAM,EAAE,cACrB,OAAO,EAAE,gBACT,OAAO,EAAE,eAET,OAAO,EAAE,SACT,MAAM,EAAE,gBACR,MAAM,EAAE,GAAC,YAAY,gBACrB,MAAM,mBACN,MAAM,EAAE,GAAC,YAAY,aACrB,MAAM,QACN,OAAO,QA6BjB;AAED;;;;;;GAMG;AACH,wDALW,MAAM,OACN;IAAC,aAAa,EAAC,MAAM,CAAC;IAAC,YAAY,EAAC,MAAM,CAAA;CAAC,mBAC3C,MAAM,QACN,OAAO,QAiBjB;wBAnVuB,OAAO;wBAMP,eAAe"}
|
|
@@ -79,31 +79,40 @@ function make_cap_round(
|
|
|
79
79
|
const B = in_binormals[index];
|
|
80
80
|
const T = in_tangents[index];
|
|
81
81
|
|
|
82
|
-
// Outward cap axis.
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
82
|
+
// Outward cap axis. The dome bulges along the tube tangent at the cap; the
|
|
83
|
+
// axis must point outward (away from the body) and be stable frame to frame.
|
|
84
|
+
// T is the per-sample curve tangent, oriented towards increasing samples
|
|
85
|
+
// ("forward"). Outward is therefore backward at the start cap and forward at
|
|
86
|
+
// the end cap - i.e. -direction * T - a sign that depends only on which end
|
|
87
|
+
// this is, never on the neighbour sample. The old code derived the axis from
|
|
88
|
+
// the neighbour chord (cap point minus the adjacent sample); that points
|
|
89
|
+
// outward but its DIRECTION jitters, because the adaptive resampler sets the
|
|
90
|
+
// neighbour's spacing and on a curve a longer/shorter first segment points a
|
|
91
|
+
// different way - so the chord, and the dome with it, swung as animated
|
|
92
|
+
// dashes slid across path knots (the "twitch"). The neighbour chord is now
|
|
93
|
+
// only a fallback for when T is unavailable.
|
|
88
94
|
const point_count = in_positions.length / 3;
|
|
89
95
|
const neighbour = direction > 0
|
|
90
96
|
? Math.min(index + 1, point_count - 1)
|
|
91
97
|
: Math.max(index - 1, 0);
|
|
92
98
|
const n3 = neighbour * 3;
|
|
93
99
|
|
|
94
|
-
const tangent = new Vector3(
|
|
95
|
-
Px - in_positions[n3],
|
|
96
|
-
Py - in_positions[n3 + 1],
|
|
97
|
-
Pz - in_positions[n3 + 2]
|
|
98
|
-
);
|
|
100
|
+
const tangent = new Vector3();
|
|
99
101
|
|
|
100
|
-
if (
|
|
101
|
-
tangent.normalize();
|
|
102
|
+
if (T !== undefined && T.lengthSq() > 1e-12) {
|
|
103
|
+
tangent.copy(T).normalize().multiplyScalar(-direction);
|
|
102
104
|
} else {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
const out_x = Px - in_positions[n3];
|
|
106
|
+
const out_y = Py - in_positions[n3 + 1];
|
|
107
|
+
const out_z = Pz - in_positions[n3 + 2];
|
|
108
|
+
|
|
109
|
+
if ((out_x * out_x + out_y * out_y + out_z * out_z) > 1e-12) {
|
|
110
|
+
// no usable tangent: the neighbour chord already points outward
|
|
111
|
+
tangent.set(out_x, out_y, out_z).normalize();
|
|
112
|
+
} else {
|
|
113
|
+
// fully degenerate: derive the axis from the frame
|
|
114
|
+
tangent.crossVectors(N, B).normalize().multiplyScalar(-direction);
|
|
115
|
+
}
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
// Use the path frame's N/B directly for BOTH ends. The profile orientation
|