@needle-tools/three-animation-pointer 1.0.1-next.98f6675 → 1.0.2-next.a516fc5
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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# KHR_animation_pointer for three.js
|
|
2
2
|
|
|
3
|
-
This package adds a [three.js](https://github.com/mrdoob/three.js) GLTFLoader plugin for [KHR_animation_pointer](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_animation_pointer) support.
|
|
3
|
+
This package adds a [three.js](https://github.com/mrdoob/three.js) GLTFLoader plugin for [KHR_animation_pointer](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_animation_pointer) support allowing you to animate anything in your 3D scene, from material, light or camera properties to custom code.
|
|
4
4
|
|
|
5
5
|
### Animate anything
|
|
6
6
|
- all material properties 💎 (including texture transforms)
|
|
@@ -37,13 +37,15 @@ loader.register(p => {
|
|
|
37
37
|
});
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
*Note: To animate custom code you might need to register your own path resolver in the GLTFAnimationPointerExtension plugin*
|
|
41
|
+
|
|
42
|
+
### How to create glTF files with KHR_animation_pointer animations
|
|
41
43
|
|
|
42
44
|
You can use one of the listed tools below to export glTF animations in the right format
|
|
43
45
|
|
|
44
46
|
- [Blender 4.3](https://www.blender.org/) and newer - *experimental KHR_animation_pointer support, currently only in 'scene animation' mode ([#2285](https://github.com/KhronosGroup/glTF-Blender-IO/issues/2285))*
|
|
45
|
-
- [Needle Engine for Unity](https://engine.needle.tools/docs/unity/) -
|
|
46
|
-
- [UnityGLTF](https://github.com/KhronosGroup/UnityGLTF)
|
|
47
|
+
- [Needle Engine for Unity](https://engine.needle.tools/docs/unity/) - using KHR_animation_pointer by default in all projects since 2022
|
|
48
|
+
- [UnityGLTF](https://github.com/KhronosGroup/UnityGLTF) - enable KHR_animation_pointer in settings to use in Exports
|
|
47
49
|
|
|
48
50
|
|
|
49
51
|
**Mini Unity Tutorial**
|
|
@@ -58,6 +60,7 @@ You can use one of the listed tools below to export glTF animations in the right
|
|
|
58
60
|
|
|
59
61
|
- [Khronos KHR_animation_pointer spec](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_animation_pointer)
|
|
60
62
|
- [three.js repository PR #24108](https://github.com/mrdoob/three.js/pull/24108)
|
|
63
|
+
- [three.js example](https://threejs.org/examples/?q=gltf#webgl_loader_gltf_animation_pointer) - created in Unity
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
|
|
@@ -13,40 +13,40 @@ const f = {
|
|
|
13
13
|
};
|
|
14
14
|
class j {
|
|
15
15
|
/** @type {import("three/examples/jsm/loaders/GLTFLoader").GLTFParser} */
|
|
16
|
-
constructor(
|
|
17
|
-
this.name = R, this.parser =
|
|
16
|
+
constructor(a) {
|
|
17
|
+
this.name = R, this.parser = a, this.animationPointerResolver = null;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* @param {import("..").AnimationPointerResolver | null} animationPointerResolver
|
|
21
21
|
*/
|
|
22
|
-
setAnimationPointerResolver(
|
|
23
|
-
return this.animationPointerResolver =
|
|
22
|
+
setAnimationPointerResolver(a) {
|
|
23
|
+
return this.animationPointerResolver = a, this;
|
|
24
24
|
}
|
|
25
25
|
/* DUPLICATE of functionality in GLTFLoader */
|
|
26
|
-
loadAnimationTargetFromChannel(
|
|
27
|
-
const
|
|
28
|
-
return this.parser.getDependency("node",
|
|
26
|
+
loadAnimationTargetFromChannel(a) {
|
|
27
|
+
const t = a.target, r = t.node !== void 0 ? t.node : t.id;
|
|
28
|
+
return this.parser.getDependency("node", r);
|
|
29
29
|
}
|
|
30
|
-
loadAnimationTargetFromChannelWithAnimationPointer(
|
|
30
|
+
loadAnimationTargetFromChannelWithAnimationPointer(a) {
|
|
31
31
|
var c;
|
|
32
32
|
B();
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
35
|
-
let e,
|
|
36
|
-
if (
|
|
37
|
-
const m =
|
|
33
|
+
const t = a.target, r = t.extensions && t.extensions[R] && t.path && t.path === "pointer";
|
|
34
|
+
if (!r) return null;
|
|
35
|
+
let e, n = f.node, s;
|
|
36
|
+
if (r) {
|
|
37
|
+
const m = t.extensions[R];
|
|
38
38
|
let o = m.pointer;
|
|
39
39
|
if (!o) {
|
|
40
|
-
console.warn("Invalid path", m,
|
|
40
|
+
console.warn("Invalid path", m, t);
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
|
-
if (o.startsWith("/materials/") ?
|
|
44
|
-
console.warn("Failed resolving animation node id: " +
|
|
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)) {
|
|
44
|
+
console.warn("Failed resolving animation node id: " + s, o);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
switch (
|
|
47
|
+
switch (n) {
|
|
48
48
|
case f.material:
|
|
49
|
-
const x = ("/materials/" +
|
|
49
|
+
const x = ("/materials/" + s.toString() + "/").length, h = o.substring(0, x);
|
|
50
50
|
switch (e = o.substring(x), e) {
|
|
51
51
|
case "pbrMetallicRoughness/baseColorFactor":
|
|
52
52
|
e = "color";
|
|
@@ -133,7 +133,7 @@ class j {
|
|
|
133
133
|
o = h + e;
|
|
134
134
|
break;
|
|
135
135
|
case f.node:
|
|
136
|
-
const u = ("/nodes/" +
|
|
136
|
+
const u = ("/nodes/" + s.toString() + "/").length, l = o.substring(0, u);
|
|
137
137
|
switch (e = o.substring(u), e) {
|
|
138
138
|
case "translation":
|
|
139
139
|
e = "position";
|
|
@@ -151,7 +151,7 @@ class j {
|
|
|
151
151
|
o = l + e;
|
|
152
152
|
break;
|
|
153
153
|
case f.light:
|
|
154
|
-
const p = ("/extensions/KHR_lights_punctual/lights/" +
|
|
154
|
+
const p = ("/extensions/KHR_lights_punctual/lights/" + s.toString() + "/").length;
|
|
155
155
|
switch (e = o.substring(p), e) {
|
|
156
156
|
case "color":
|
|
157
157
|
break;
|
|
@@ -167,10 +167,10 @@ class j {
|
|
|
167
167
|
e = "distance";
|
|
168
168
|
break;
|
|
169
169
|
}
|
|
170
|
-
o = "/lights/" +
|
|
170
|
+
o = "/lights/" + s.toString() + "/" + e;
|
|
171
171
|
break;
|
|
172
172
|
case f.camera:
|
|
173
|
-
const g = ("/cameras/" +
|
|
173
|
+
const g = ("/cameras/" + s.toString() + "/").length, b = o.substring(0, g);
|
|
174
174
|
switch (e = o.substring(g), e) {
|
|
175
175
|
case "perspective/yfov":
|
|
176
176
|
e = "fov";
|
|
@@ -196,25 +196,25 @@ class j {
|
|
|
196
196
|
o = b + e;
|
|
197
197
|
break;
|
|
198
198
|
}
|
|
199
|
-
(c = this.animationPointerResolver) != null && c.resolvePath && (o = this.animationPointerResolver.resolvePath(o)),
|
|
199
|
+
(c = this.animationPointerResolver) != null && c.resolvePath && (o = this.animationPointerResolver.resolvePath(o)), t.extensions[R].pointer = o;
|
|
200
200
|
}
|
|
201
|
-
if (
|
|
202
|
-
console.warn("Failed resolving animation node id: " +
|
|
201
|
+
if (s == null || isNaN(s)) {
|
|
202
|
+
console.warn("Failed resolving animation node id: " + s, t);
|
|
203
203
|
return;
|
|
204
204
|
}
|
|
205
205
|
let i;
|
|
206
|
-
return
|
|
206
|
+
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;
|
|
207
207
|
}
|
|
208
|
-
createAnimationTracksWithAnimationPointer(
|
|
209
|
-
if (!(
|
|
210
|
-
let i =
|
|
208
|
+
createAnimationTracksWithAnimationPointer(a, t, r, e, n) {
|
|
209
|
+
if (!(n.extensions && n.extensions[R] && n.path && n.path === "pointer")) return null;
|
|
210
|
+
let i = n.extensions[R].pointer;
|
|
211
211
|
if (!i) return null;
|
|
212
212
|
const c = [];
|
|
213
213
|
i = i.replaceAll("/", ".");
|
|
214
214
|
const m = i.split(".");
|
|
215
|
-
var x =
|
|
216
|
-
if (m[2] = x, m[3] === "morphTargetInfluences" &&
|
|
217
|
-
for (const u of
|
|
215
|
+
var x = a.name !== void 0 && a.name !== null ? a.name : a.uuid;
|
|
216
|
+
if (m[2] = x, m[3] === "morphTargetInfluences" && a.type === "Group") {
|
|
217
|
+
for (const u of a.children)
|
|
218
218
|
u instanceof S && u.morphTargetInfluences && (m[3] = u.name, m[4] = "morphTargetInfluences", h(this.parser));
|
|
219
219
|
return c;
|
|
220
220
|
}
|
|
@@ -222,7 +222,7 @@ class j {
|
|
|
222
222
|
function h(u) {
|
|
223
223
|
i = m.join(".");
|
|
224
224
|
let l;
|
|
225
|
-
switch (
|
|
225
|
+
switch (r.itemSize) {
|
|
226
226
|
case 1:
|
|
227
227
|
l = O;
|
|
228
228
|
break;
|
|
@@ -235,25 +235,25 @@ class j {
|
|
|
235
235
|
break;
|
|
236
236
|
}
|
|
237
237
|
if (!l) {
|
|
238
|
-
console.warn("Unsupported output accessor format",
|
|
238
|
+
console.warn("Unsupported output accessor format", r);
|
|
239
239
|
return;
|
|
240
240
|
}
|
|
241
241
|
const p = e.interpolation !== void 0 ? U[e.interpolation] : F;
|
|
242
|
-
let g = u._getArrayFromAccessor(
|
|
242
|
+
let g = u._getArrayFromAccessor(r);
|
|
243
243
|
i.endsWith(".fov") && (g = g.map((k) => k / Math.PI * 180));
|
|
244
244
|
const b = new l(
|
|
245
245
|
i,
|
|
246
|
-
|
|
246
|
+
t.array,
|
|
247
247
|
g,
|
|
248
248
|
p
|
|
249
249
|
);
|
|
250
|
-
if (p === "CUBICSPLINE" && u._createCubicSplineTrackInterpolant(b), c.push(b), i &&
|
|
250
|
+
if (p === "CUBICSPLINE" && u._createCubicSplineTrackInterpolant(b), c.push(b), i && r.itemSize === 4 && i.startsWith(".materials.") && i.endsWith(".color")) {
|
|
251
251
|
const k = new Float32Array(g.length / 4);
|
|
252
252
|
for (let T = 0, _ = g.length / 4; T < _; T += 1)
|
|
253
253
|
k[T] = g[T * 4 + 3];
|
|
254
254
|
const d = new l(
|
|
255
255
|
i.replace(".color", ".opacity"),
|
|
256
|
-
|
|
256
|
+
t.array,
|
|
257
257
|
k,
|
|
258
258
|
p
|
|
259
259
|
);
|
|
@@ -262,9 +262,9 @@ class j {
|
|
|
262
262
|
}
|
|
263
263
|
return c;
|
|
264
264
|
}
|
|
265
|
-
_tryResolveTargetId(
|
|
266
|
-
let
|
|
267
|
-
return
|
|
265
|
+
_tryResolveTargetId(a, t) {
|
|
266
|
+
let r = "";
|
|
267
|
+
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);
|
|
268
268
|
}
|
|
269
269
|
/* MOSTLY DUPLICATE of GLTFLoader.loadAnimation, but also tries to resolve KHR_animation_pointer. */
|
|
270
270
|
/**
|
|
@@ -272,12 +272,12 @@ class j {
|
|
|
272
272
|
* @param {number} animationIndex
|
|
273
273
|
* @return {Promise<AnimationClip>}
|
|
274
274
|
*/
|
|
275
|
-
loadAnimation(
|
|
276
|
-
const
|
|
277
|
-
for (let h = 0, u =
|
|
278
|
-
const l =
|
|
279
|
-
let d =
|
|
280
|
-
d || (d =
|
|
275
|
+
loadAnimation(a) {
|
|
276
|
+
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 = [];
|
|
277
|
+
for (let h = 0, u = n.channels.length; h < u; h++) {
|
|
278
|
+
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;
|
|
279
|
+
let d = t.loadAnimationTargetFromChannelWithAnimationPointer(l);
|
|
280
|
+
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);
|
|
281
281
|
}
|
|
282
282
|
return Promise.all([
|
|
283
283
|
Promise.all(i),
|
|
@@ -291,12 +291,12 @@ class j {
|
|
|
291
291
|
const _ = u[d], v = l[d], A = p[d], N = g[d], K = b[d];
|
|
292
292
|
if (_ === void 0) continue;
|
|
293
293
|
_.updateMatrix && (_.updateMatrix(), _.matrixAutoUpdate = !0);
|
|
294
|
-
let y =
|
|
294
|
+
let y = t.createAnimationTracksWithAnimationPointer(_, v, A, N, K);
|
|
295
295
|
if (y || (y = e._createAnimationTracks(_, v, A, N, K)), y)
|
|
296
296
|
for (let P = 0; P < y.length; P++)
|
|
297
297
|
k.push(y[P]);
|
|
298
298
|
}
|
|
299
|
-
return new W(
|
|
299
|
+
return new W(s, void 0, k);
|
|
300
300
|
});
|
|
301
301
|
}
|
|
302
302
|
}
|
|
@@ -305,36 +305,37 @@ function B() {
|
|
|
305
305
|
if (H) return;
|
|
306
306
|
H = !0;
|
|
307
307
|
const I = z || (z = C.findNode);
|
|
308
|
-
C.findNode = function(
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
308
|
+
C.findNode = function(a, t) {
|
|
309
|
+
if (!t) return I(a, t);
|
|
310
|
+
if (t.startsWith(".materials.")) {
|
|
311
|
+
const r = t.substring(11).substring(t.indexOf(".")), e = r.indexOf("."), n = e < 0 ? r : r.substring(0, e);
|
|
312
|
+
let s = null;
|
|
313
|
+
return a.traverse((i) => {
|
|
314
|
+
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)));
|
|
315
|
+
}), s;
|
|
316
|
+
} else if (t.startsWith(".nodes.") || t.startsWith(".lights.") || t.startsWith(".cameras.")) {
|
|
317
|
+
const r = t.split(".");
|
|
317
318
|
let e;
|
|
318
|
-
for (let
|
|
319
|
-
const
|
|
320
|
-
if (
|
|
321
|
-
e =
|
|
322
|
-
else if (e && e[
|
|
323
|
-
const c = Number.parseInt(
|
|
324
|
-
let m =
|
|
319
|
+
for (let n = 1; n < r.length; n++) {
|
|
320
|
+
const s = r[n];
|
|
321
|
+
if (s.length == 36)
|
|
322
|
+
e = a.getObjectByProperty("uuid", s);
|
|
323
|
+
else if (e && e[s]) {
|
|
324
|
+
const c = Number.parseInt(s);
|
|
325
|
+
let m = s;
|
|
325
326
|
c >= 0 && (m = c), e = e[m];
|
|
326
327
|
} else {
|
|
327
|
-
const c =
|
|
328
|
+
const c = a.getObjectByName(s);
|
|
328
329
|
c && (e = c);
|
|
329
330
|
}
|
|
330
331
|
}
|
|
331
332
|
if (!e) {
|
|
332
|
-
const
|
|
333
|
-
return
|
|
333
|
+
const n = I(a, r[2]);
|
|
334
|
+
return n || console.warn(R + ": Property binding not found", t, a, a.name, r), n;
|
|
334
335
|
}
|
|
335
336
|
return e;
|
|
336
337
|
}
|
|
337
|
-
return I(
|
|
338
|
+
return I(a, t);
|
|
338
339
|
};
|
|
339
340
|
}
|
|
340
341
|
export {
|
package/package.json
CHANGED
|
@@ -657,6 +657,10 @@ function _ensurePropertyBindingPatch() {
|
|
|
657
657
|
// "path" is the animated property path, just with translated material names.
|
|
658
658
|
PropertyBinding.findNode = function (node, path) {
|
|
659
659
|
|
|
660
|
+
// if the binding is empty fallback to original
|
|
661
|
+
if (!path) return findNode(node, path);
|
|
662
|
+
|
|
663
|
+
|
|
660
664
|
if (path.startsWith('.materials.')) {
|
|
661
665
|
|
|
662
666
|
if (_animationPointerDebug) console.log('FIND', path);
|