@needle-tools/three-animation-pointer 1.0.4 → 1.0.6

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/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [1.0.5] - 2025-10-15
8
+ - Fix: Remove leftover log
9
+
10
+ ## [1.0.5] - 2025-10-15
11
+ - Add: support for boolean animation tracks (when using ubyte type)
12
+
7
13
  ## [1.0.4] - 2025-10-08
8
14
  - Add: KHR_node_visibility extension
9
15
 
@@ -1,5 +1,5 @@
1
- import { SkinnedMesh as S, InterpolateDiscrete as M, InterpolateLinear as F, AnimationClip as W, PropertyBinding as C, QuaternionKeyframeTrack as w, ColorKeyframeTrack as D, VectorKeyframeTrack as E, NumberKeyframeTrack as O } from "three";
2
- const f = {
1
+ import { SkinnedMesh as S, InterpolateDiscrete as M, InterpolateLinear as F, AnimationClip as W, PropertyBinding as C, QuaternionKeyframeTrack as w, ColorKeyframeTrack as B, VectorKeyframeTrack as D, BooleanKeyframeTrack as E, NumberKeyframeTrack as O } from "three";
2
+ const d = {
3
3
  node: "node",
4
4
  material: "material",
5
5
  camera: "camera",
@@ -11,7 +11,7 @@ const f = {
11
11
  LINEAR: F,
12
12
  STEP: M
13
13
  };
14
- class j {
14
+ class G {
15
15
  /** @type {import("three/examples/jsm/loaders/GLTFLoader").GLTFParser} */
16
16
  constructor(a) {
17
17
  this.name = R, this.parser = a, this.animationPointerResolver = null;
@@ -28,26 +28,26 @@ class j {
28
28
  return this.parser.getDependency("node", r);
29
29
  }
30
30
  loadAnimationTargetFromChannelWithAnimationPointer(a) {
31
- var c;
32
- B();
31
+ var u;
32
+ j();
33
33
  const t = a.target, r = t.extensions && t.extensions[R] && t.path && t.path === "pointer";
34
34
  if (!r) return null;
35
- let e, n = f.node, s;
35
+ let e, n = d.node, s;
36
36
  if (r) {
37
- const m = t.extensions[R];
38
- let o = m.pointer;
37
+ const h = t.extensions[R];
38
+ let o = h.pointer;
39
39
  if (!o) {
40
- console.warn("Invalid path", m, t);
40
+ console.warn("Invalid path", h, t);
41
41
  return;
42
42
  }
43
- if (o.startsWith("/materials/") ? n = f.material : o.startsWith("/extensions/KHR_lights_punctual/lights/") ? n = f.light : o.startsWith("/cameras/") && (n = f.camera), s = this._tryResolveTargetId(o, n), s === null || isNaN(s)) {
43
+ if (o.startsWith("/materials/") ? n = d.material : o.startsWith("/extensions/KHR_lights_punctual/lights/") ? n = d.light : o.startsWith("/cameras/") && (n = d.camera), s = this._tryResolveTargetId(o, n), s === null || isNaN(s)) {
44
44
  console.warn("Failed resolving animation node id: " + s, o);
45
45
  return;
46
46
  }
47
47
  switch (n) {
48
- case f.material:
49
- const x = ("/materials/" + s.toString() + "/").length, h = o.substring(0, x);
50
- switch (e = o.substring(x), e) {
48
+ case d.material:
49
+ const T = ("/materials/" + s.toString() + "/").length, f = o.substring(0, T);
50
+ switch (e = o.substring(T), e) {
51
51
  case "pbrMetallicRoughness/baseColorFactor":
52
52
  e = "color";
53
53
  break;
@@ -130,11 +130,11 @@ class j {
130
130
  e = "specularColor";
131
131
  break;
132
132
  }
133
- o = h + e;
133
+ o = f + e;
134
134
  break;
135
- case f.node:
136
- const u = ("/nodes/" + s.toString() + "/").length, l = o.substring(0, u);
137
- switch (e = o.substring(u), e) {
135
+ case d.node:
136
+ const k = ("/nodes/" + s.toString() + "/").length, l = o.substring(0, k);
137
+ switch (e = o.substring(k), e) {
138
138
  case "translation":
139
139
  e = "position";
140
140
  break;
@@ -153,9 +153,9 @@ class j {
153
153
  }
154
154
  o = l + e;
155
155
  break;
156
- case f.light:
157
- const p = ("/extensions/KHR_lights_punctual/lights/" + s.toString() + "/").length;
158
- switch (e = o.substring(p), e) {
156
+ case d.light:
157
+ const c = ("/extensions/KHR_lights_punctual/lights/" + s.toString() + "/").length;
158
+ switch (e = o.substring(c), e) {
159
159
  case "color":
160
160
  break;
161
161
  case "intensity":
@@ -172,7 +172,7 @@ class j {
172
172
  }
173
173
  o = "/lights/" + s.toString() + "/" + e;
174
174
  break;
175
- case f.camera:
175
+ case d.camera:
176
176
  const g = ("/cameras/" + s.toString() + "/").length, b = o.substring(0, g);
177
177
  switch (e = o.substring(g), e) {
178
178
  case "perspective/yfov":
@@ -199,71 +199,80 @@ class j {
199
199
  o = b + e;
200
200
  break;
201
201
  }
202
- (c = this.animationPointerResolver) != null && c.resolvePath && (o = this.animationPointerResolver.resolvePath(o)), t.extensions[R].pointer = o;
202
+ (u = this.animationPointerResolver) != null && u.resolvePath && (o = this.animationPointerResolver.resolvePath(o)), t.extensions[R].pointer = o;
203
203
  }
204
204
  if (s == null || isNaN(s)) {
205
205
  console.warn("Failed resolving animation node id: " + s, t);
206
206
  return;
207
207
  }
208
208
  let i;
209
- return n === f.node ? i = this.parser.getDependency("node", s) : n === f.material ? i = this.parser.getDependency("material", s) : n === f.light ? i = this.parser.getDependency("light", s) : n === f.camera ? i = this.parser.getDependency("camera", s) : console.error("Unhandled type", n), i;
209
+ return n === d.node ? i = this.parser.getDependency("node", s) : n === d.material ? i = this.parser.getDependency("material", s) : n === d.light ? i = this.parser.getDependency("light", s) : n === d.camera ? i = this.parser.getDependency("camera", s) : console.error("Unhandled type", n), i;
210
210
  }
211
211
  createAnimationTracksWithAnimationPointer(a, t, r, e, n) {
212
212
  if (!(n.extensions && n.extensions[R] && n.path && n.path === "pointer")) return null;
213
213
  let i = n.extensions[R].pointer;
214
214
  if (!i) return null;
215
- const c = [];
215
+ const u = [];
216
216
  i = i.replaceAll("/", ".");
217
- const m = i.split(".");
218
- var x = a.name !== void 0 && a.name !== null ? a.name : a.uuid;
219
- if (m[2] = x, m[3] === "morphTargetInfluences" && a.type === "Group") {
220
- for (const u of a.children)
221
- u instanceof S && u.morphTargetInfluences && (m[3] = u.name, m[4] = "morphTargetInfluences", h(this.parser));
222
- return c;
217
+ const h = i.split(".");
218
+ var T = a.name !== void 0 && a.name !== null ? a.name : a.uuid;
219
+ if (h[2] = T, h[3] === "morphTargetInfluences" && a.type === "Group") {
220
+ for (const l of a.children)
221
+ l instanceof S && l.morphTargetInfluences && (h[3] = l.name, h[4] = "morphTargetInfluences", k(this.parser));
222
+ return u;
223
223
  }
224
- h(this.parser);
225
- function h(u) {
226
- i = m.join(".");
227
- let l;
224
+ k(this.parser);
225
+ function f(l, c) {
226
+ try {
227
+ const g = c.split(".").filter(Boolean), m = g[g.length - 1].replace(/\[.*\]$/, "");
228
+ return m in l ? typeof l[m] == "boolean" : !1;
229
+ } catch {
230
+ return !1;
231
+ }
232
+ }
233
+ function k(l) {
234
+ i = h.join(".");
235
+ let c, g = !1;
228
236
  switch (r.itemSize) {
229
237
  case 1:
230
- l = O;
238
+ Object.prototype.toString.call(r.array) === "[object Uint8Array]" && // UNSIGNED_BYTE as a strong hint
239
+ f(a, i) ? (c = E, g = !0) : c = O;
231
240
  break;
232
241
  case 2:
233
242
  case 3:
234
- l = E;
243
+ c = D;
235
244
  break;
236
245
  case 4:
237
- i.endsWith(".quaternion") ? l = w : l = D;
246
+ i.endsWith(".quaternion") ? c = w : c = B;
238
247
  break;
239
248
  }
240
- if (!l) {
249
+ if (!c) {
241
250
  console.warn("Unsupported output accessor format", r);
242
251
  return;
243
252
  }
244
- const p = e.interpolation !== void 0 ? U[e.interpolation] : F;
245
- let g = u._getArrayFromAccessor(r);
246
- i.endsWith(".fov") && (g = g.map((k) => k / Math.PI * 180));
247
- const b = new l(
253
+ const b = e.interpolation !== void 0 ? U[e.interpolation] : F;
254
+ let m = l._getArrayFromAccessor(r);
255
+ i.endsWith(".fov") && (m = m.map((_) => _ / Math.PI * 180)), g && (m = m.map((_) => _ > 0));
256
+ const p = new c(
248
257
  i,
249
258
  t.array,
250
- g,
251
- p
259
+ m,
260
+ b
252
261
  );
253
- if (p === "CUBICSPLINE" && u._createCubicSplineTrackInterpolant(b), c.push(b), i && r.itemSize === 4 && i.startsWith(".materials.") && i.endsWith(".color")) {
254
- const k = new Float32Array(g.length / 4);
255
- for (let T = 0, _ = g.length / 4; T < _; T += 1)
256
- k[T] = g[T * 4 + 3];
257
- const d = new l(
262
+ if (b === "CUBICSPLINE" && l._createCubicSplineTrackInterpolant(p), u.push(p), i && r.itemSize === 4 && i.startsWith(".materials.") && i.endsWith(".color")) {
263
+ const _ = new Float32Array(m.length / 4);
264
+ for (let y = 0, P = m.length / 4; y < P; y += 1)
265
+ _[y] = m[y * 4 + 3];
266
+ const x = new c(
258
267
  i.replace(".color", ".opacity"),
259
268
  t.array,
260
- k,
261
- p
269
+ _,
270
+ b
262
271
  );
263
- p === "CUBICSPLINE" && u._createCubicSplineTrackInterpolant(b), c.push(d);
272
+ b === "CUBICSPLINE" && l._createCubicSplineTrackInterpolant(p), u.push(x);
264
273
  }
265
274
  }
266
- return c;
275
+ return u;
267
276
  }
268
277
  _tryResolveTargetId(a, t) {
269
278
  let r = "";
@@ -276,38 +285,38 @@ class j {
276
285
  * @return {Promise<AnimationClip>}
277
286
  */
278
287
  loadAnimation(a) {
279
- const t = this, r = this.parser.json, e = this.parser, n = r.animations[a], s = n.name ? n.name : "animation_" + a, i = [], c = [], m = [], o = [], x = [];
280
- for (let h = 0, u = n.channels.length; h < u; h++) {
281
- const l = n.channels[h], p = n.samplers[l.sampler], g = l.target, b = n.parameters !== void 0 ? n.parameters[p.input] : p.input, k = n.parameters !== void 0 ? n.parameters[p.output] : p.output;
282
- let d = t.loadAnimationTargetFromChannelWithAnimationPointer(l);
283
- d || (d = t.loadAnimationTargetFromChannel(l)), i.push(d), c.push(e.getDependency("accessor", b)), m.push(e.getDependency("accessor", k)), o.push(p), x.push(g);
288
+ const t = this, r = this.parser.json, e = this.parser, n = r.animations[a], s = n.name ? n.name : "animation_" + a, i = [], u = [], h = [], o = [], T = [];
289
+ for (let f = 0, k = n.channels.length; f < k; f++) {
290
+ const l = n.channels[f], c = n.samplers[l.sampler], g = l.target, b = n.parameters !== void 0 ? n.parameters[c.input] : c.input, m = n.parameters !== void 0 ? n.parameters[c.output] : c.output;
291
+ let p = t.loadAnimationTargetFromChannelWithAnimationPointer(l);
292
+ p || (p = t.loadAnimationTargetFromChannel(l)), i.push(p), u.push(e.getDependency("accessor", b)), h.push(e.getDependency("accessor", m)), o.push(c), T.push(g);
284
293
  }
285
294
  return Promise.all([
286
295
  Promise.all(i),
287
- Promise.all(c),
288
- Promise.all(m),
296
+ Promise.all(u),
297
+ Promise.all(h),
289
298
  Promise.all(o),
290
- Promise.all(x)
291
- ]).then(function(h) {
292
- const u = h[0], l = h[1], p = h[2], g = h[3], b = h[4], k = [];
293
- for (let d = 0, T = u.length; d < T; d++) {
294
- const _ = u[d], P = l[d], A = p[d], K = g[d], N = b[d];
295
- if (_ === void 0) continue;
296
- _.updateMatrix && (_.updateMatrix(), _.matrixAutoUpdate = !0);
297
- let y = t.createAnimationTracksWithAnimationPointer(_, P, A, K, N);
298
- if (y || (y = e._createAnimationTracks(_, P, A, K, N)), y)
299
- for (let v = 0; v < y.length; v++)
300
- k.push(y[v]);
299
+ Promise.all(T)
300
+ ]).then(function(f) {
301
+ const k = f[0], l = f[1], c = f[2], g = f[3], b = f[4], m = [];
302
+ for (let p = 0, _ = k.length; p < _; p++) {
303
+ const x = k[p], y = l[p], P = c[p], K = g[p], N = b[p];
304
+ if (x === void 0) continue;
305
+ x.updateMatrix && (x.updateMatrix(), x.matrixAutoUpdate = !0);
306
+ let v = t.createAnimationTracksWithAnimationPointer(x, y, P, K, N);
307
+ if (v || (v = e._createAnimationTracks(x, y, P, K, N)), v)
308
+ for (let A = 0; A < v.length; A++)
309
+ m.push(v[A]);
301
310
  }
302
- return new W(s, void 0, k);
311
+ return new W(s, void 0, m);
303
312
  });
304
313
  }
305
314
  }
306
- let H = !1, z = null;
307
- function B() {
315
+ let H = !1, L = null;
316
+ function j() {
308
317
  if (H) return;
309
318
  H = !0;
310
- const I = z || (z = C.findNode);
319
+ const I = L || (L = C.findNode);
311
320
  C.findNode = function(a, t) {
312
321
  if (!t) return I(a, t);
313
322
  if (t.startsWith(".materials.")) {
@@ -324,12 +333,12 @@ function B() {
324
333
  if (s.length == 36)
325
334
  e = a.getObjectByProperty("uuid", s);
326
335
  else if (e && e[s]) {
327
- const c = Number.parseInt(s);
328
- let m = s;
329
- c >= 0 && (m = c), e = e[m];
336
+ const u = Number.parseInt(s);
337
+ let h = s;
338
+ u >= 0 && (h = u), e = e[h];
330
339
  } else {
331
- const c = a.getObjectByName(s);
332
- c && (e = c);
340
+ const u = a.getObjectByName(s);
341
+ u && (e = u);
333
342
  }
334
343
  }
335
344
  if (!e) {
@@ -342,6 +351,6 @@ function B() {
342
351
  };
343
352
  }
344
353
  export {
345
- j as GLTFAnimationPointerExtension,
346
- j as default
354
+ G as GLTFAnimationPointerExtension,
355
+ G as default
347
356
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/three-animation-pointer",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "KHR_animation_pointer support for three.js",
5
5
  "type": "module",
6
6
  "main": "dist/three-animation-pointer.js",
@@ -7,7 +7,8 @@ import {
7
7
  PropertyBinding,
8
8
  QuaternionKeyframeTrack,
9
9
  VectorKeyframeTrack,
10
- SkinnedMesh
10
+ SkinnedMesh,
11
+ BooleanKeyframeTrack
11
12
  } from 'three';
12
13
 
13
14
  /**
@@ -414,6 +415,29 @@ export class GLTFAnimationPointerExtension {
414
415
  // default
415
416
  __createTrack( this.parser );
416
417
 
418
+
419
+ function isBooleanTarget(node, trackPath) {
420
+ // trackPath like ".nodes.MyNode.visible" or ".materials.<uuid>.wireframe"
421
+ try {
422
+
423
+ // get last token after '.'
424
+ const sections = trackPath.split('.').filter(Boolean);
425
+ let last = sections[sections.length - 1]; // e.g., 'visible' or 'iridescenceThicknessRange[0]'
426
+
427
+ // strip indexers like [0]
428
+ const propName = last.replace(/\[.*\]$/, '');
429
+
430
+ // bail if not a direct property
431
+ if (!(propName in node)) return false;
432
+
433
+ const val = node[propName];
434
+ return typeof val === 'boolean';
435
+ }
436
+ catch {
437
+ return false;
438
+ }
439
+ }
440
+
417
441
  /** Create a new track using the current parts array */
418
442
  function __createTrack( parser ) {
419
443
 
@@ -423,11 +447,22 @@ export class GLTFAnimationPointerExtension {
423
447
  console.log( node, inputAccessor, outputAccessor, target, animationPointerPropertyPath );
424
448
 
425
449
  let TypedKeyframeTrack;
426
-
450
+ let convertToBoolean = false;
427
451
  switch ( outputAccessor.itemSize ) {
428
452
 
429
- case 1:
430
- TypedKeyframeTrack = NumberKeyframeTrack;
453
+ case 1:
454
+ // Get type of outputAccessor.array elements
455
+ const arrayType = Object.prototype.toString.call(outputAccessor.array);
456
+ const isUInt8 = (arrayType === '[object Uint8Array]');
457
+
458
+ const looksLikeBool = isUInt8 && // UNSIGNED_BYTE as a strong hint
459
+ isBooleanTarget(node, animationPointerPropertyPath);
460
+ if (looksLikeBool) {
461
+ TypedKeyframeTrack = BooleanKeyframeTrack;
462
+ convertToBoolean = true; // map 0 => false, >0 => true
463
+ } else {
464
+ TypedKeyframeTrack = NumberKeyframeTrack;
465
+ }
431
466
  break;
432
467
  case 2:
433
468
  case 3:
@@ -457,7 +492,11 @@ export class GLTFAnimationPointerExtension {
457
492
  if ( animationPointerPropertyPath.endsWith( '.fov' ) ) {
458
493
 
459
494
  outputArray = outputArray.map( value => value / Math.PI * 180 );
495
+ }
460
496
 
497
+ if (convertToBoolean) {
498
+ // boolean values are stored as ubyte in glTF, 0 = false, anything else = true
499
+ outputArray = outputArray.map( v => v > 0 );
461
500
  }
462
501
 
463
502
  const track = new TypedKeyframeTrack(