@needle-tools/three-animation-pointer 1.0.7 → 1.1.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/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.1.1] - 2026-06-12
8
+ - 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
+
10
+ ## [1.1.0] - 2026-06-01
11
+ - Add: support for `/nodes/{}/weights/{}` to animate individual morph target weights (works with multi-material meshes / primitives) — by @0b5vr in [#1](https://github.com/needle-tools/three-animation-pointer/pull/1)
12
+
7
13
  ## [1.0.7] - 2025-10-20
8
14
  - Fix: missing "extensions" in KHR_node_visibility
9
15
 
package/README.md CHANGED
@@ -6,6 +6,7 @@ This package adds a [three.js](https://github.com/mrdoob/three.js) GLTFLoader pl
6
6
  - all material properties 💎 (including texture transforms)
7
7
  - cameras 🎥
8
8
  - lights 💡
9
+ - morph target weights 😀 (all weights or individual ones via `/nodes/{}/weights/{}`)
9
10
  - custom code/scripts 📄
10
11
 
11
12
 
@@ -1,20 +1,20 @@
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";
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
2
  const d = {
3
3
  node: "node",
4
4
  material: "material",
5
5
  camera: "camera",
6
6
  light: "light"
7
- }, R = "KHR_animation_pointer", U = {
7
+ }, y = "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: M
12
+ STEP: w
13
13
  };
14
- class G {
14
+ class z {
15
15
  /** @type {import("three/examples/jsm/loaders/GLTFLoader").GLTFParser} */
16
16
  constructor(a) {
17
- this.name = R, this.parser = a, this.animationPointerResolver = null;
17
+ this.name = y, this.parser = a, this.animationPointerResolver = null;
18
18
  }
19
19
  /**
20
20
  * @param {import("..").AnimationPointerResolver | null} animationPointerResolver
@@ -28,25 +28,25 @@ class G {
28
28
  return this.parser.getDependency("node", r);
29
29
  }
30
30
  loadAnimationTargetFromChannelWithAnimationPointer(a) {
31
- var u;
32
- j();
33
- const t = a.target, r = t.extensions && t.extensions[R] && t.path && t.path === "pointer";
31
+ var m;
32
+ L();
33
+ const t = a.target, r = t.extensions && t.extensions[y] && t.path && t.path === "pointer";
34
34
  if (!r) return null;
35
- let e, n = d.node, s;
35
+ let e, s = d.node, n;
36
36
  if (r) {
37
- const h = t.extensions[R];
37
+ const h = t.extensions[y];
38
38
  let o = h.pointer;
39
39
  if (!o) {
40
40
  console.warn("Invalid path", h, t);
41
41
  return;
42
42
  }
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
- console.warn("Failed resolving animation node id: " + s, o);
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);
45
45
  return;
46
46
  }
47
- switch (n) {
47
+ switch (s) {
48
48
  case d.material:
49
- const T = ("/materials/" + s.toString() + "/").length, f = o.substring(0, T);
49
+ const T = ("/materials/" + n.toString() + "/").length, g = o.substring(0, T);
50
50
  switch (e = o.substring(T), e) {
51
51
  case "pbrMetallicRoughness/baseColorFactor":
52
52
  e = "color";
@@ -130,32 +130,36 @@ class G {
130
130
  e = "specularColor";
131
131
  break;
132
132
  }
133
- o = f + e;
133
+ o = g + e;
134
134
  break;
135
135
  case d.node:
136
- const k = ("/nodes/" + s.toString() + "/").length, l = o.substring(0, k);
137
- switch (e = o.substring(k), e) {
138
- case "translation":
139
- e = "position";
140
- break;
141
- case "rotation":
142
- e = "quaternion";
143
- break;
144
- case "scale":
145
- e = "scale";
146
- break;
147
- case "weights":
148
- e = "morphTargetInfluences";
149
- break;
150
- case "extensions/KHR_node_visibility/visible":
151
- e = "visible";
152
- break;
153
- }
154
- o = l + e;
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);
140
+ } else
141
+ switch (e) {
142
+ case "translation":
143
+ e = "position";
144
+ break;
145
+ case "rotation":
146
+ e = "quaternion";
147
+ break;
148
+ case "scale":
149
+ e = "scale";
150
+ break;
151
+ case "weights":
152
+ e = "morphTargetInfluences";
153
+ break;
154
+ case "extensions/KHR_node_visibility/visible":
155
+ e = "visible";
156
+ break;
157
+ }
158
+ o = b + e;
155
159
  break;
156
160
  case d.light:
157
- const c = ("/extensions/KHR_lights_punctual/lights/" + s.toString() + "/").length;
158
- switch (e = o.substring(c), e) {
161
+ const u = ("/extensions/KHR_lights_punctual/lights/" + n.toString() + "/").length;
162
+ switch (e = o.substring(u), e) {
159
163
  case "color":
160
164
  break;
161
165
  case "intensity":
@@ -170,11 +174,11 @@ class G {
170
174
  e = "distance";
171
175
  break;
172
176
  }
173
- o = "/lights/" + s.toString() + "/" + e;
177
+ o = "/lights/" + n.toString() + "/" + e;
174
178
  break;
175
179
  case d.camera:
176
- const g = ("/cameras/" + s.toString() + "/").length, b = o.substring(0, g);
177
- switch (e = o.substring(g), e) {
180
+ const l = ("/cameras/" + n.toString() + "/").length, k = o.substring(0, l);
181
+ switch (e = o.substring(l), e) {
178
182
  case "perspective/yfov":
179
183
  e = "fov";
180
184
  break;
@@ -196,83 +200,87 @@ class G {
196
200
  e = "zoom";
197
201
  break;
198
202
  }
199
- o = b + e;
203
+ o = k + e;
200
204
  break;
201
205
  }
202
- (u = this.animationPointerResolver) != null && u.resolvePath && (o = this.animationPointerResolver.resolvePath(o)), t.extensions[R].pointer = o;
206
+ (m = this.animationPointerResolver) != null && m.resolvePath && (o = this.animationPointerResolver.resolvePath(o)), t.extensions[y].pointer = o;
203
207
  }
204
- if (s == null || isNaN(s)) {
205
- console.warn("Failed resolving animation node id: " + s, t);
208
+ if (n == null || isNaN(n)) {
209
+ console.warn("Failed resolving animation node id: " + n, t);
206
210
  return;
207
211
  }
208
212
  let 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;
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;
210
214
  }
211
- createAnimationTracksWithAnimationPointer(a, t, r, e, n) {
212
- if (!(n.extensions && n.extensions[R] && n.path && n.path === "pointer")) return null;
213
- let i = n.extensions[R].pointer;
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;
214
219
  if (!i) return null;
215
- const u = [];
220
+ const m = [];
216
221
  i = i.replaceAll("/", ".");
217
222
  const h = i.split(".");
218
223
  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;
224
+ if (h[2] = T, (b = h[3]) != null && b.startsWith("morphTargetInfluences")) {
225
+ const u = h[3];
226
+ if (a.type === "Group") {
227
+ for (const l of a.children)
228
+ l.morphTargetInfluences && (h[3] = l.name, h[4] = u, _(this.parser));
229
+ return m;
230
+ }
223
231
  }
224
- k(this.parser);
225
- function f(l, c) {
232
+ _(this.parser);
233
+ function g(u, l) {
226
234
  try {
227
- const g = c.split(".").filter(Boolean), m = g[g.length - 1].replace(/\[.*\]$/, "");
228
- return m in l ? typeof l[m] == "boolean" : !1;
235
+ const k = l.split(".").filter(Boolean), c = k[k.length - 1].replace(/\[.*\]$/, "");
236
+ return c in u ? typeof u[c] == "boolean" : !1;
229
237
  } catch {
230
238
  return !1;
231
239
  }
232
240
  }
233
- function k(l) {
241
+ function _(u) {
234
242
  i = h.join(".");
235
- let c, g = !1;
243
+ let l, k = !1;
236
244
  switch (r.itemSize) {
237
245
  case 1:
238
246
  Object.prototype.toString.call(r.array) === "[object Uint8Array]" && // UNSIGNED_BYTE as a strong hint
239
- f(a, i) ? (c = E, g = !0) : c = O;
247
+ g(a, i) ? (l = D, k = !0) : l = E;
240
248
  break;
241
249
  case 2:
242
250
  case 3:
243
- c = D;
251
+ l = B;
244
252
  break;
245
253
  case 4:
246
- i.endsWith(".quaternion") ? c = w : c = B;
254
+ i.endsWith(".quaternion") ? l = M : l = W;
247
255
  break;
248
256
  }
249
- if (!c) {
257
+ if (!l) {
250
258
  console.warn("Unsupported output accessor format", r);
251
259
  return;
252
260
  }
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(
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(
257
265
  i,
258
266
  t.array,
259
- m,
260
- b
267
+ c,
268
+ f
261
269
  );
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(
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(
267
275
  i.replace(".color", ".opacity"),
268
276
  t.array,
269
- _,
270
- b
277
+ p,
278
+ f
271
279
  );
272
- b === "CUBICSPLINE" && l._createCubicSplineTrackInterpolant(p), u.push(x);
280
+ f === "CUBICSPLINE" && u._createCubicSplineTrackInterpolant(R), m.push(v);
273
281
  }
274
282
  }
275
- return u;
283
+ return m;
276
284
  }
277
285
  _tryResolveTargetId(a, t) {
278
286
  let r = "";
@@ -285,72 +293,72 @@ class G {
285
293
  * @return {Promise<AnimationClip>}
286
294
  */
287
295
  loadAnimation(a) {
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);
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);
293
301
  }
294
302
  return Promise.all([
295
303
  Promise.all(i),
296
- Promise.all(u),
304
+ Promise.all(m),
297
305
  Promise.all(h),
298
306
  Promise.all(o),
299
307
  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]);
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]);
310
318
  }
311
- return new W(s, void 0, m);
319
+ return new S(n, void 0, f);
312
320
  });
313
321
  }
314
322
  }
315
- let H = !1, L = null;
316
- function j() {
323
+ let H = !1, U = null;
324
+ function L() {
317
325
  if (H) return;
318
326
  H = !0;
319
- const I = L || (L = C.findNode);
327
+ const P = U || (U = C.findNode);
320
328
  C.findNode = function(a, t) {
321
- if (!t) return I(a, t);
329
+ if (!t) return P(a, t);
322
330
  if (t.startsWith(".materials.")) {
323
- const r = t.substring(11).substring(t.indexOf(".")), e = r.indexOf("."), n = e < 0 ? r : r.substring(0, e);
324
- let s = null;
331
+ const r = t.substring(11).substring(t.indexOf(".")), e = r.indexOf("."), s = e < 0 ? r : r.substring(0, e);
332
+ let n = null;
325
333
  return a.traverse((i) => {
326
- s !== null || i.type !== "Mesh" && i.type !== "SkinnedMesh" || i.material && (i.material.uuid === n || i.material.name === n) && (s = i.material, s !== null && (r.endsWith(".map") ? s = s.map : r.endsWith(".emissiveMap") && (s = s.emissiveMap)));
327
- }), s;
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)));
335
+ }), n;
328
336
  } else if (t.startsWith(".nodes.") || t.startsWith(".lights.") || t.startsWith(".cameras.")) {
329
337
  const r = t.split(".");
330
338
  let e;
331
- for (let n = 1; n < r.length; n++) {
332
- const s = r[n];
333
- if (s.length == 36)
334
- e = a.getObjectByProperty("uuid", s);
335
- else if (e && e[s]) {
336
- const u = Number.parseInt(s);
337
- let h = s;
338
- u >= 0 && (h = u), e = e[h];
339
+ for (let s = 1; s < r.length; s++) {
340
+ const n = r[s];
341
+ if (n.length == 36)
342
+ e = a.getObjectByProperty("uuid", n);
343
+ else if (e && e[n]) {
344
+ const m = Number.parseInt(n);
345
+ let h = n;
346
+ m >= 0 && (h = m), e = e[h];
339
347
  } else {
340
- const u = a.getObjectByName(s);
341
- u && (e = u);
348
+ const m = a.getObjectByName(n);
349
+ m && (e = m);
342
350
  }
343
351
  }
344
352
  if (!e) {
345
- const n = I(a, r[2]);
346
- return n || console.warn(R + ": Property binding not found", t, a, a.name, r), n;
353
+ const s = P(a, r[2]);
354
+ return s || console.warn(y + ": Property binding not found", t, a, a.name, r), s;
347
355
  }
348
356
  return e;
349
357
  }
350
- return I(a, t);
358
+ return P(a, t);
351
359
  };
352
360
  }
353
361
  export {
354
- G as GLTFAnimationPointerExtension,
355
- G as default
362
+ z as GLTFAnimationPointerExtension,
363
+ z as default
356
364
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/three-animation-pointer",
3
- "version": "1.0.7",
3
+ "version": "1.1.1",
4
4
  "description": "KHR_animation_pointer support for three.js",
5
5
  "type": "module",
6
6
  "main": "dist/three-animation-pointer.js",
@@ -46,4 +46,4 @@
46
46
  "registry": "https://registry.npmjs.org/",
47
47
  "access": "public"
48
48
  }
49
- }
49
+ }
@@ -7,7 +7,6 @@ import {
7
7
  PropertyBinding,
8
8
  QuaternionKeyframeTrack,
9
9
  VectorKeyframeTrack,
10
- SkinnedMesh,
11
10
  BooleanKeyframeTrack
12
11
  } from 'three';
13
12
 
@@ -247,23 +246,37 @@ export class GLTFAnimationPointerExtension {
247
246
  const pathStartNode = path.substring( 0, pathIndexNode );
248
247
  targetProperty = path.substring( pathIndexNode );
249
248
 
250
- switch ( targetProperty ) {
249
+ if ( targetProperty.startsWith( 'weights/' ) ) {
250
+ // `/nodes/{}/weights/{}` - control individual morph weights
251
+ const weightIndex = targetProperty.substring( 'weights/'.length );
252
+ if ( /^\d+$/.test( weightIndex ) ) {
251
253
 
252
- case 'translation':
253
- targetProperty = 'position';
254
- break;
255
- case 'rotation':
256
- targetProperty = 'quaternion';
257
- break;
258
- case 'scale':
259
- targetProperty = 'scale';
260
- break;
261
- case 'weights':
262
- targetProperty = 'morphTargetInfluences';
263
- break;
264
- case "extensions/KHR_node_visibility/visible":
265
- targetProperty = 'visible';
266
- break;
254
+ targetProperty = 'morphTargetInfluences[' + weightIndex + ']';
255
+
256
+ } else {
257
+
258
+ console.warn( KHR_ANIMATION_POINTER + ': invalid weight index', targetProperty, path );
259
+
260
+ }
261
+ } else {
262
+ switch ( targetProperty ) {
263
+
264
+ case 'translation':
265
+ targetProperty = 'position';
266
+ break;
267
+ case 'rotation':
268
+ targetProperty = 'quaternion';
269
+ break;
270
+ case 'scale':
271
+ targetProperty = 'scale';
272
+ break;
273
+ case 'weights':
274
+ targetProperty = 'morphTargetInfluences';
275
+ break;
276
+ case "extensions/KHR_node_visibility/visible":
277
+ targetProperty = 'visible';
278
+ break;
279
+ }
267
280
  }
268
281
 
269
282
  path = pathStartNode + targetProperty;
@@ -385,21 +398,25 @@ export class GLTFAnimationPointerExtension {
385
398
 
386
399
  // specially handle the morphTargetInfluences property for multi-material meshes
387
400
  // in which case the target object is a Group and the children are the actual targets
401
+ // Note that there is also morphTargetInfluences[i] for individual morph targets
388
402
  // see NE-3311
389
- if ( parts[ 3 ] === 'morphTargetInfluences' ) {
403
+ if ( parts[ 3 ]?.startsWith( 'morphTargetInfluences' ) ) {
404
+
405
+ const property = parts[ 3 ];
390
406
 
391
407
  if ( node.type === 'Group' ) {
392
408
 
393
409
  if ( _animationPointerDebug )
394
- console.log( 'Detected multi-material skinnedMesh export', animationPointerPropertyPath, node );
410
+ console.log( 'Detected multi-material mesh export', animationPointerPropertyPath, node );
395
411
 
396
- // We assume the children are skinned meshes
412
+ // Bind any child that has morph influences. The children may be plain
413
+ // Mesh (no skin/bones) or SkinnedMesh, so we must not require SkinnedMesh.
397
414
  for ( const ch of node.children ) {
398
415
 
399
- if ( ch instanceof SkinnedMesh && ch.morphTargetInfluences ) {
416
+ if ( ch.morphTargetInfluences ) {
400
417
 
401
418
  parts[ 3 ] = ch.name;
402
- parts[ 4 ] = 'morphTargetInfluences';
419
+ parts[ 4 ] = property;
403
420
  __createTrack( this.parser );
404
421
 
405
422
  }