@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 +6 -0
- package/README.md +1 -0
- package/dist/three-animation-pointer.js +125 -117
- package/package.json +2 -2
- package/src/GLTFLoaderAnimationPointer.js +39 -22
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 {
|
|
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
|
-
},
|
|
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:
|
|
12
|
+
STEP: w
|
|
13
13
|
};
|
|
14
|
-
class
|
|
14
|
+
class z {
|
|
15
15
|
/** @type {import("three/examples/jsm/loaders/GLTFLoader").GLTFParser} */
|
|
16
16
|
constructor(a) {
|
|
17
|
-
this.name =
|
|
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
|
|
32
|
-
|
|
33
|
-
const t = a.target, r = t.extensions && t.extensions[
|
|
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,
|
|
35
|
+
let e, s = d.node, n;
|
|
36
36
|
if (r) {
|
|
37
|
-
const h = t.extensions[
|
|
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/") ?
|
|
44
|
-
console.warn("Failed resolving animation node id: " +
|
|
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 (
|
|
47
|
+
switch (s) {
|
|
48
48
|
case d.material:
|
|
49
|
-
const T = ("/materials/" +
|
|
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 =
|
|
133
|
+
o = g + e;
|
|
134
134
|
break;
|
|
135
135
|
case d.node:
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
|
158
|
-
switch (e = o.substring(
|
|
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/" +
|
|
177
|
+
o = "/lights/" + n.toString() + "/" + e;
|
|
174
178
|
break;
|
|
175
179
|
case d.camera:
|
|
176
|
-
const
|
|
177
|
-
switch (e = o.substring(
|
|
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 =
|
|
203
|
+
o = k + e;
|
|
200
204
|
break;
|
|
201
205
|
}
|
|
202
|
-
(
|
|
206
|
+
(m = this.animationPointerResolver) != null && m.resolvePath && (o = this.animationPointerResolver.resolvePath(o)), t.extensions[y].pointer = o;
|
|
203
207
|
}
|
|
204
|
-
if (
|
|
205
|
-
console.warn("Failed resolving animation node id: " +
|
|
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
|
|
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,
|
|
212
|
-
|
|
213
|
-
|
|
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
|
|
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]
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
225
|
-
function
|
|
232
|
+
_(this.parser);
|
|
233
|
+
function g(u, l) {
|
|
226
234
|
try {
|
|
227
|
-
const
|
|
228
|
-
return
|
|
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
|
|
241
|
+
function _(u) {
|
|
234
242
|
i = h.join(".");
|
|
235
|
-
let
|
|
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
|
-
|
|
247
|
+
g(a, i) ? (l = D, k = !0) : l = E;
|
|
240
248
|
break;
|
|
241
249
|
case 2:
|
|
242
250
|
case 3:
|
|
243
|
-
|
|
251
|
+
l = B;
|
|
244
252
|
break;
|
|
245
253
|
case 4:
|
|
246
|
-
i.endsWith(".quaternion") ?
|
|
254
|
+
i.endsWith(".quaternion") ? l = M : l = W;
|
|
247
255
|
break;
|
|
248
256
|
}
|
|
249
|
-
if (!
|
|
257
|
+
if (!l) {
|
|
250
258
|
console.warn("Unsupported output accessor format", r);
|
|
251
259
|
return;
|
|
252
260
|
}
|
|
253
|
-
const
|
|
254
|
-
let
|
|
255
|
-
i.endsWith(".fov") && (
|
|
256
|
-
const
|
|
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
|
-
|
|
260
|
-
|
|
267
|
+
c,
|
|
268
|
+
f
|
|
261
269
|
);
|
|
262
|
-
if (
|
|
263
|
-
const
|
|
264
|
-
for (let
|
|
265
|
-
|
|
266
|
-
const
|
|
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
|
-
|
|
277
|
+
p,
|
|
278
|
+
f
|
|
271
279
|
);
|
|
272
|
-
|
|
280
|
+
f === "CUBICSPLINE" && u._createCubicSplineTrackInterpolant(R), m.push(v);
|
|
273
281
|
}
|
|
274
282
|
}
|
|
275
|
-
return
|
|
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,
|
|
289
|
-
for (let
|
|
290
|
-
const
|
|
291
|
-
let
|
|
292
|
-
|
|
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(
|
|
304
|
+
Promise.all(m),
|
|
297
305
|
Promise.all(h),
|
|
298
306
|
Promise.all(o),
|
|
299
307
|
Promise.all(T)
|
|
300
|
-
]).then(function(
|
|
301
|
-
const
|
|
302
|
-
for (let
|
|
303
|
-
const
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
let
|
|
307
|
-
if (
|
|
308
|
-
for (let
|
|
309
|
-
|
|
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
|
|
319
|
+
return new S(n, void 0, f);
|
|
312
320
|
});
|
|
313
321
|
}
|
|
314
322
|
}
|
|
315
|
-
let H = !1,
|
|
316
|
-
function
|
|
323
|
+
let H = !1, U = null;
|
|
324
|
+
function L() {
|
|
317
325
|
if (H) return;
|
|
318
326
|
H = !0;
|
|
319
|
-
const
|
|
327
|
+
const P = U || (U = C.findNode);
|
|
320
328
|
C.findNode = function(a, t) {
|
|
321
|
-
if (!t) return
|
|
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("."),
|
|
324
|
-
let
|
|
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
|
-
|
|
327
|
-
}),
|
|
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
|
|
332
|
-
const
|
|
333
|
-
if (
|
|
334
|
-
e = a.getObjectByProperty("uuid",
|
|
335
|
-
else if (e && e[
|
|
336
|
-
const
|
|
337
|
-
let h =
|
|
338
|
-
|
|
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
|
|
341
|
-
|
|
348
|
+
const m = a.getObjectByName(n);
|
|
349
|
+
m && (e = m);
|
|
342
350
|
}
|
|
343
351
|
}
|
|
344
352
|
if (!e) {
|
|
345
|
-
const
|
|
346
|
-
return
|
|
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
|
|
358
|
+
return P(a, t);
|
|
351
359
|
};
|
|
352
360
|
}
|
|
353
361
|
export {
|
|
354
|
-
|
|
355
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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 ]
|
|
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
|
|
410
|
+
console.log( 'Detected multi-material mesh export', animationPointerPropertyPath, node );
|
|
395
411
|
|
|
396
|
-
//
|
|
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
|
|
416
|
+
if ( ch.morphTargetInfluences ) {
|
|
400
417
|
|
|
401
418
|
parts[ 3 ] = ch.name;
|
|
402
|
-
parts[ 4 ] =
|
|
419
|
+
parts[ 4 ] = property;
|
|
403
420
|
__createTrack( this.parser );
|
|
404
421
|
|
|
405
422
|
}
|