@needle-tools/three-animation-pointer 1.1.1 → 1.1.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/CHANGELOG.md CHANGED
@@ -4,6 +4,9 @@ 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.1.2] - 2026-06-17
8
+ - Fix: don't mutate the `KHR_animation_pointer.pointer` value in `parser.json`. The resolved three.js property path is now kept in plugin-local state, so other plugins / app code can still read the original spec-defined pointer after load — by @0b5vr in [#2](https://github.com/needle-tools/three-animation-pointer/pull/2)
9
+
7
10
  ## [1.1.1] - 2026-06-12
8
11
  - Fix: morph target (blend shape) weight animation was silently dropped for multi-material meshes without skin/bones. The multi-material morph branch only bound children that were `SkinnedMesh`; such meshes load as a `Group` of plain `Mesh` children, so no track was created (and the empty-but-truthy result skipped the fallback). Now any child with `morphTargetInfluences` is bound.
9
12
 
@@ -1,20 +1,20 @@
1
- import { InterpolateDiscrete as w, InterpolateLinear as F, AnimationClip as S, PropertyBinding as C, QuaternionKeyframeTrack as M, ColorKeyframeTrack as W, VectorKeyframeTrack as B, BooleanKeyframeTrack as D, NumberKeyframeTrack as E } from "three";
2
- const d = {
1
+ import { InterpolateDiscrete as M, InterpolateLinear as F, AnimationClip as w, PropertyBinding as C, QuaternionKeyframeTrack as S, ColorKeyframeTrack as W, VectorKeyframeTrack as B, BooleanKeyframeTrack as D, NumberKeyframeTrack as E } from "three";
2
+ const k = {
3
3
  node: "node",
4
4
  material: "material",
5
5
  camera: "camera",
6
6
  light: "light"
7
- }, y = "KHR_animation_pointer", O = {
7
+ }, R = "KHR_animation_pointer", O = {
8
8
  // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each
9
9
  // keyframe track will be initialized with a default interpolation type, then modified.
10
10
  CUBICSPLINE: void 0,
11
11
  LINEAR: F,
12
- STEP: w
12
+ STEP: M
13
13
  };
14
14
  class z {
15
15
  /** @type {import("three/examples/jsm/loaders/GLTFLoader").GLTFParser} */
16
16
  constructor(a) {
17
- this.name = y, this.parser = a, this.animationPointerResolver = null;
17
+ this.name = R, this.parser = a, this.animationPointerResolver = null, this.pointerPathMap = /* @__PURE__ */ new Map();
18
18
  }
19
19
  /**
20
20
  * @param {import("..").AnimationPointerResolver | null} animationPointerResolver
@@ -24,30 +24,30 @@ class z {
24
24
  }
25
25
  /* DUPLICATE of functionality in GLTFLoader */
26
26
  loadAnimationTargetFromChannel(a) {
27
- const t = a.target, r = t.node !== void 0 ? t.node : t.id;
27
+ const s = a.target, r = s.node !== void 0 ? s.node : s.id;
28
28
  return this.parser.getDependency("node", r);
29
29
  }
30
30
  loadAnimationTargetFromChannelWithAnimationPointer(a) {
31
- var m;
31
+ var o;
32
32
  L();
33
- const t = a.target, r = t.extensions && t.extensions[y] && t.path && t.path === "pointer";
33
+ const s = a.target, r = s.extensions && s.extensions[R] && s.path && s.path === "pointer";
34
34
  if (!r) return null;
35
- let e, s = d.node, n;
35
+ let e, t = k.node, n;
36
36
  if (r) {
37
- const h = t.extensions[y];
38
- let o = h.pointer;
39
- if (!o) {
40
- console.warn("Invalid path", h, t);
37
+ const m = s.extensions[R];
38
+ let i = m.pointer;
39
+ if (!i) {
40
+ console.warn("Invalid path", m, s);
41
41
  return;
42
42
  }
43
- if (o.startsWith("/materials/") ? s = d.material : o.startsWith("/extensions/KHR_lights_punctual/lights/") ? s = d.light : o.startsWith("/cameras/") && (s = d.camera), n = this._tryResolveTargetId(o, s), n === null || isNaN(n)) {
44
- console.warn("Failed resolving animation node id: " + n, o);
43
+ if (i.startsWith("/materials/") ? t = k.material : i.startsWith("/extensions/KHR_lights_punctual/lights/") ? t = k.light : i.startsWith("/cameras/") && (t = k.camera), n = this._tryResolveTargetId(i, t), n === null || isNaN(n)) {
44
+ console.warn("Failed resolving animation node id: " + n, i);
45
45
  return;
46
46
  }
47
- switch (s) {
48
- case d.material:
49
- const T = ("/materials/" + n.toString() + "/").length, g = o.substring(0, T);
50
- switch (e = o.substring(T), e) {
47
+ switch (t) {
48
+ case k.material:
49
+ const h = ("/materials/" + n.toString() + "/").length, y = i.substring(0, h);
50
+ switch (e = i.substring(h), e) {
51
51
  case "pbrMetallicRoughness/baseColorFactor":
52
52
  e = "color";
53
53
  break;
@@ -130,13 +130,13 @@ class z {
130
130
  e = "specularColor";
131
131
  break;
132
132
  }
133
- o = g + e;
133
+ i = y + e;
134
134
  break;
135
- case d.node:
136
- const _ = ("/nodes/" + n.toString() + "/").length, b = o.substring(0, _);
137
- if (e = o.substring(_), e.startsWith("weights/")) {
138
- const f = e.substring(8);
139
- /^\d+$/.test(f) ? e = "morphTargetInfluences[" + f + "]" : console.warn(y + ": invalid weight index", e, o);
135
+ case k.node:
136
+ const f = ("/nodes/" + n.toString() + "/").length, d = i.substring(0, f);
137
+ if (e = i.substring(f), e.startsWith("weights/")) {
138
+ const l = e.substring(8);
139
+ /^\d+$/.test(l) ? e = "morphTargetInfluences[" + l + "]" : console.warn(R + ": invalid weight index", e, i);
140
140
  } else
141
141
  switch (e) {
142
142
  case "translation":
@@ -155,11 +155,11 @@ class z {
155
155
  e = "visible";
156
156
  break;
157
157
  }
158
- o = b + e;
158
+ i = d + e;
159
159
  break;
160
- case d.light:
161
- const u = ("/extensions/KHR_lights_punctual/lights/" + n.toString() + "/").length;
162
- switch (e = o.substring(u), e) {
160
+ case k.light:
161
+ const p = ("/extensions/KHR_lights_punctual/lights/" + n.toString() + "/").length;
162
+ switch (e = i.substring(p), e) {
163
163
  case "color":
164
164
  break;
165
165
  case "intensity":
@@ -174,11 +174,11 @@ class z {
174
174
  e = "distance";
175
175
  break;
176
176
  }
177
- o = "/lights/" + n.toString() + "/" + e;
177
+ i = "/lights/" + n.toString() + "/" + e;
178
178
  break;
179
- case d.camera:
180
- const l = ("/cameras/" + n.toString() + "/").length, k = o.substring(0, l);
181
- switch (e = o.substring(l), e) {
179
+ case k.camera:
180
+ const c = ("/cameras/" + n.toString() + "/").length, b = i.substring(0, c);
181
+ switch (e = i.substring(c), e) {
182
182
  case "perspective/yfov":
183
183
  e = "fov";
184
184
  break;
@@ -200,91 +200,94 @@ class z {
200
200
  e = "zoom";
201
201
  break;
202
202
  }
203
- o = k + e;
203
+ i = b + e;
204
204
  break;
205
205
  }
206
- (m = this.animationPointerResolver) != null && m.resolvePath && (o = this.animationPointerResolver.resolvePath(o)), t.extensions[y].pointer = o;
206
+ (o = this.animationPointerResolver) != null && o.resolvePath && (i = this.animationPointerResolver.resolvePath(i));
207
+ const v = m.pointer;
208
+ this.pointerPathMap.set(v, i);
207
209
  }
208
210
  if (n == null || isNaN(n)) {
209
- console.warn("Failed resolving animation node id: " + n, t);
211
+ console.warn("Failed resolving animation node id: " + n, s);
210
212
  return;
211
213
  }
212
- let i;
213
- return s === d.node ? i = this.parser.getDependency("node", n) : s === d.material ? i = this.parser.getDependency("material", n) : s === d.light ? i = this.parser.getDependency("light", n) : s === d.camera ? i = this.parser.getDependency("camera", n) : console.error("Unhandled type", s), i;
214
+ let u;
215
+ return t === k.node ? u = this.parser.getDependency("node", n) : t === k.material ? u = this.parser.getDependency("material", n) : t === k.light ? u = this.parser.getDependency("light", n) : t === k.camera ? u = this.parser.getDependency("camera", n) : console.error("Unhandled type", t), u;
214
216
  }
215
- createAnimationTracksWithAnimationPointer(a, t, r, e, s) {
216
- var b;
217
- if (!(s.extensions && s.extensions[y] && s.path && s.path === "pointer")) return null;
218
- let i = s.extensions[y].pointer;
219
- if (!i) return null;
217
+ createAnimationTracksWithAnimationPointer(a, s, r, e, t) {
218
+ var d;
219
+ if (!(t.extensions && t.extensions[R] && t.path && t.path === "pointer")) return null;
220
+ const u = t.extensions[R].pointer;
221
+ let o = this.pointerPathMap.get(u);
222
+ if (!o) return null;
220
223
  const m = [];
221
- i = i.replaceAll("/", ".");
222
- const h = i.split(".");
223
- var T = a.name !== void 0 && a.name !== null ? a.name : a.uuid;
224
- if (h[2] = T, (b = h[3]) != null && b.startsWith("morphTargetInfluences")) {
225
- const u = h[3];
224
+ o = o.replaceAll("/", ".");
225
+ const i = o.split(".");
226
+ var h = a.name !== void 0 && a.name !== null ? a.name : a.uuid;
227
+ if (i[2] = h, (d = i[3]) != null && d.startsWith("morphTargetInfluences")) {
228
+ const p = i[3];
226
229
  if (a.type === "Group") {
227
- for (const l of a.children)
228
- l.morphTargetInfluences && (h[3] = l.name, h[4] = u, _(this.parser));
230
+ for (const c of a.children)
231
+ c.morphTargetInfluences && (i[3] = c.name, i[4] = p, f(this.parser));
229
232
  return m;
230
233
  }
231
234
  }
232
- _(this.parser);
233
- function g(u, l) {
235
+ f(this.parser);
236
+ function y(p, c) {
234
237
  try {
235
- const k = l.split(".").filter(Boolean), c = k[k.length - 1].replace(/\[.*\]$/, "");
236
- return c in u ? typeof u[c] == "boolean" : !1;
238
+ const b = c.split(".").filter(Boolean), g = b[b.length - 1].replace(/\[.*\]$/, "");
239
+ return g in p ? typeof p[g] == "boolean" : !1;
237
240
  } catch {
238
241
  return !1;
239
242
  }
240
243
  }
241
- function _(u) {
242
- i = h.join(".");
243
- let l, k = !1;
244
+ function f(p) {
245
+ o = i.join(".");
246
+ let c, b = !1;
244
247
  switch (r.itemSize) {
245
248
  case 1:
246
249
  Object.prototype.toString.call(r.array) === "[object Uint8Array]" && // UNSIGNED_BYTE as a strong hint
247
- g(a, i) ? (l = D, k = !0) : l = E;
250
+ y(a, o) ? (c = D, b = !0) : c = E;
248
251
  break;
249
252
  case 2:
250
253
  case 3:
251
- l = B;
254
+ c = B;
252
255
  break;
253
256
  case 4:
254
- i.endsWith(".quaternion") ? l = M : l = W;
257
+ o.endsWith(".quaternion") ? c = S : c = W;
255
258
  break;
256
259
  }
257
- if (!l) {
260
+ if (!c) {
258
261
  console.warn("Unsupported output accessor format", r);
259
262
  return;
260
263
  }
261
- const f = e.interpolation !== void 0 ? O[e.interpolation] : F;
262
- let c = u._getArrayFromAccessor(r);
263
- i.endsWith(".fov") && (c = c.map((p) => p / Math.PI * 180)), k && (c = c.map((p) => p > 0));
264
- const R = new l(
265
- i,
266
- t.array,
267
- c,
268
- f
264
+ const l = e.interpolation !== void 0 ? O[e.interpolation] : F;
265
+ let g = p._getArrayFromAccessor(r);
266
+ o.endsWith(".fov") && (g = g.map((x) => x / Math.PI * 180)), b && (g = g.map((x) => x > 0));
267
+ const _ = new c(
268
+ o,
269
+ s.array,
270
+ g,
271
+ l
269
272
  );
270
- if (f === "CUBICSPLINE" && u._createCubicSplineTrackInterpolant(R), m.push(R), i && r.itemSize === 4 && i.startsWith(".materials.") && i.endsWith(".color")) {
271
- const p = new Float32Array(c.length / 4);
272
- for (let x = 0, A = c.length / 4; x < A; x += 1)
273
- p[x] = c[x * 4 + 3];
274
- const v = new l(
275
- i.replace(".color", ".opacity"),
276
- t.array,
277
- p,
278
- f
273
+ if (l === "CUBICSPLINE" && p._createCubicSplineTrackInterpolant(_), m.push(_), o && r.itemSize === 4 && o.startsWith(".materials.") && o.endsWith(".color")) {
274
+ const x = new Float32Array(g.length / 4);
275
+ for (let T = 0, K = g.length / 4; T < K; T += 1)
276
+ x[T] = g[T * 4 + 3];
277
+ const I = new c(
278
+ o.replace(".color", ".opacity"),
279
+ s.array,
280
+ x,
281
+ l
279
282
  );
280
- f === "CUBICSPLINE" && u._createCubicSplineTrackInterpolant(R), m.push(v);
283
+ l === "CUBICSPLINE" && p._createCubicSplineTrackInterpolant(_), m.push(I);
281
284
  }
282
285
  }
283
286
  return m;
284
287
  }
285
- _tryResolveTargetId(a, t) {
288
+ _tryResolveTargetId(a, s) {
286
289
  let r = "";
287
- return t === "node" ? r = a.substring(7) : t === "material" ? r = a.substring(11) : t === "light" ? r = a.substring(39) : t === "camera" && (r = a.substring(9)), r = r.substring(0, r.indexOf("/")), Number.parseInt(r);
290
+ return s === "node" ? r = a.substring(7) : s === "material" ? r = a.substring(11) : s === "light" ? r = a.substring(39) : s === "camera" && (r = a.substring(9)), r = r.substring(0, r.indexOf("/")), Number.parseInt(r);
288
291
  }
289
292
  /* MOSTLY DUPLICATE of GLTFLoader.loadAnimation, but also tries to resolve KHR_animation_pointer. */
290
293
  /**
@@ -293,30 +296,30 @@ class z {
293
296
  * @return {Promise<AnimationClip>}
294
297
  */
295
298
  loadAnimation(a) {
296
- const t = this, r = this.parser.json, e = this.parser, s = r.animations[a], n = s.name ? s.name : "animation_" + a, i = [], m = [], h = [], o = [], T = [];
297
- for (let g = 0, _ = s.channels.length; g < _; g++) {
298
- const b = s.channels[g], u = s.samplers[b.sampler], l = b.target, k = s.parameters !== void 0 ? s.parameters[u.input] : u.input, f = s.parameters !== void 0 ? s.parameters[u.output] : u.output;
299
- let c = t.loadAnimationTargetFromChannelWithAnimationPointer(b);
300
- c || (c = t.loadAnimationTargetFromChannel(b)), i.push(c), m.push(e.getDependency("accessor", k)), h.push(e.getDependency("accessor", f)), o.push(u), T.push(l);
299
+ const s = this, r = this.parser.json, e = this.parser, t = r.animations[a], n = t.name ? t.name : "animation_" + a, u = [], o = [], m = [], i = [], v = [];
300
+ for (let h = 0, y = t.channels.length; h < y; h++) {
301
+ const f = t.channels[h], d = t.samplers[f.sampler], p = f.target, c = t.parameters !== void 0 ? t.parameters[d.input] : d.input, b = t.parameters !== void 0 ? t.parameters[d.output] : d.output;
302
+ let l = s.loadAnimationTargetFromChannelWithAnimationPointer(f);
303
+ l || (l = s.loadAnimationTargetFromChannel(f)), u.push(l), o.push(e.getDependency("accessor", c)), m.push(e.getDependency("accessor", b)), i.push(d), v.push(p);
301
304
  }
302
305
  return Promise.all([
303
- Promise.all(i),
304
- Promise.all(m),
305
- Promise.all(h),
306
+ Promise.all(u),
306
307
  Promise.all(o),
307
- Promise.all(T)
308
- ]).then(function(g) {
309
- const _ = g[0], b = g[1], u = g[2], l = g[3], k = g[4], f = [];
310
- for (let c = 0, R = _.length; c < R; c++) {
311
- const p = _[c], v = b[c], x = u[c], A = l[c], N = k[c];
312
- if (p === void 0) continue;
313
- p.updateMatrix && (p.updateMatrix(), p.matrixAutoUpdate = !0);
314
- let I = t.createAnimationTracksWithAnimationPointer(p, v, x, A, N);
315
- if (I || (I = e._createAnimationTracks(p, v, x, A, N)), I)
316
- for (let K = 0; K < I.length; K++)
317
- f.push(I[K]);
308
+ Promise.all(m),
309
+ Promise.all(i),
310
+ Promise.all(v)
311
+ ]).then(function(h) {
312
+ const y = h[0], f = h[1], d = h[2], p = h[3], c = h[4], b = [];
313
+ for (let l = 0, g = y.length; l < g; l++) {
314
+ const _ = y[l], x = f[l], I = d[l], T = p[l], K = c[l];
315
+ if (_ === void 0) continue;
316
+ _.updateMatrix && (_.updateMatrix(), _.matrixAutoUpdate = !0);
317
+ let P = s.createAnimationTracksWithAnimationPointer(_, x, I, T, K);
318
+ if (P || (P = e._createAnimationTracks(_, x, I, T, K)), P)
319
+ for (let N = 0; N < P.length; N++)
320
+ b.push(P[N]);
318
321
  }
319
- return new S(n, void 0, f);
322
+ return new w(n, void 0, b);
320
323
  });
321
324
  }
322
325
  }
@@ -324,38 +327,38 @@ let H = !1, U = null;
324
327
  function L() {
325
328
  if (H) return;
326
329
  H = !0;
327
- const P = U || (U = C.findNode);
328
- C.findNode = function(a, t) {
329
- if (!t) return P(a, t);
330
- if (t.startsWith(".materials.")) {
331
- const r = t.substring(11).substring(t.indexOf(".")), e = r.indexOf("."), s = e < 0 ? r : r.substring(0, e);
330
+ const A = U || (U = C.findNode);
331
+ C.findNode = function(a, s) {
332
+ if (!s) return A(a, s);
333
+ if (s.startsWith(".materials.")) {
334
+ const r = s.substring(11).substring(s.indexOf(".")), e = r.indexOf("."), t = e < 0 ? r : r.substring(0, e);
332
335
  let n = null;
333
- return a.traverse((i) => {
334
- n !== null || i.type !== "Mesh" && i.type !== "SkinnedMesh" || i.material && (i.material.uuid === s || i.material.name === s) && (n = i.material, n !== null && (r.endsWith(".map") ? n = n.map : r.endsWith(".emissiveMap") && (n = n.emissiveMap)));
336
+ return a.traverse((u) => {
337
+ n !== null || u.type !== "Mesh" && u.type !== "SkinnedMesh" || u.material && (u.material.uuid === t || u.material.name === t) && (n = u.material, n !== null && (r.endsWith(".map") ? n = n.map : r.endsWith(".emissiveMap") && (n = n.emissiveMap)));
335
338
  }), n;
336
- } else if (t.startsWith(".nodes.") || t.startsWith(".lights.") || t.startsWith(".cameras.")) {
337
- const r = t.split(".");
339
+ } else if (s.startsWith(".nodes.") || s.startsWith(".lights.") || s.startsWith(".cameras.")) {
340
+ const r = s.split(".");
338
341
  let e;
339
- for (let s = 1; s < r.length; s++) {
340
- const n = r[s];
342
+ for (let t = 1; t < r.length; t++) {
343
+ const n = r[t];
341
344
  if (n.length == 36)
342
345
  e = a.getObjectByProperty("uuid", n);
343
346
  else if (e && e[n]) {
344
- const m = Number.parseInt(n);
345
- let h = n;
346
- m >= 0 && (h = m), e = e[h];
347
+ const o = Number.parseInt(n);
348
+ let m = n;
349
+ o >= 0 && (m = o), e = e[m];
347
350
  } else {
348
- const m = a.getObjectByName(n);
349
- m && (e = m);
351
+ const o = a.getObjectByName(n);
352
+ o && (e = o);
350
353
  }
351
354
  }
352
355
  if (!e) {
353
- const s = P(a, r[2]);
354
- return s || console.warn(y + ": Property binding not found", t, a, a.name, r), s;
356
+ const t = A(a, r[2]);
357
+ return t || console.warn(R + ": Property binding not found", s, a, a.name, r), t;
355
358
  }
356
359
  return e;
357
360
  }
358
- return P(a, t);
361
+ return A(a, s);
359
362
  };
360
363
  }
361
364
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/three-animation-pointer",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "KHR_animation_pointer support for three.js",
5
5
  "type": "module",
6
6
  "main": "dist/three-animation-pointer.js",
@@ -54,6 +54,12 @@ export class GLTFAnimationPointerExtension {
54
54
  /** @type {import("..").AnimationPointerResolver | null} */
55
55
  this.animationPointerResolver = null;
56
56
 
57
+ /**
58
+ * @type {Map<string, string>}
59
+ * Maps from original pointer paths to resolved three.js property paths.
60
+ */
61
+ this.pointerPathMap = new Map();
62
+
57
63
  }
58
64
 
59
65
  /**
@@ -351,7 +357,8 @@ export class GLTFAnimationPointerExtension {
351
357
 
352
358
  }
353
359
 
354
- target.extensions[ KHR_ANIMATION_POINTER ].pointer = path;
360
+ const pointer = ext.pointer;
361
+ this.pointerPathMap.set( pointer, path );
355
362
 
356
363
  }
357
364
 
@@ -384,7 +391,8 @@ export class GLTFAnimationPointerExtension {
384
391
  const useExtension = target.extensions && target.extensions[ KHR_ANIMATION_POINTER ] && target.path && target.path === 'pointer';
385
392
  if ( ! useExtension ) return null;
386
393
 
387
- let animationPointerPropertyPath = target.extensions[ KHR_ANIMATION_POINTER ].pointer;
394
+ const pointer = target.extensions[ KHR_ANIMATION_POINTER ].pointer;
395
+ let animationPointerPropertyPath = this.pointerPathMap.get( pointer );
388
396
  if ( ! animationPointerPropertyPath ) return null;
389
397
 
390
398
  const tracks = [];
@@ -810,4 +818,4 @@ function _ensurePropertyBindingPatch() {
810
818
 
811
819
  };
812
820
 
813
- }
821
+ }