@zephyr3d/editor 0.3.7 → 0.3.8

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.
Files changed (61) hide show
  1. package/dist/assets/{index-ySiLtSxz.js → index-BZ1L8ggE.js} +43 -3
  2. package/dist/assistant/zephyr-types-index.json +13230 -9488
  3. package/dist/images/icon1024.png +0 -0
  4. package/dist/index.html +1 -1
  5. package/dist/modules/zephyr3d_backend-webgl.js +2 -0
  6. package/dist/modules/zephyr3d_backend-webgl.js.map +1 -1
  7. package/dist/modules/zephyr3d_backend-webgpu.js +457 -16
  8. package/dist/modules/zephyr3d_backend-webgpu.js.map +1 -1
  9. package/dist/modules/zephyr3d_device.js +27 -0
  10. package/dist/modules/zephyr3d_device.js.map +1 -1
  11. package/dist/modules/zephyr3d_loaders.js +208 -1
  12. package/dist/modules/zephyr3d_loaders.js.map +1 -1
  13. package/dist/modules/zephyr3d_scene.js +774 -80
  14. package/dist/modules/zephyr3d_scene.js.map +1 -1
  15. package/dist/vendor/zephyr3d/backend-webgl/dist/capabilities_webgl.js +2 -0
  16. package/dist/vendor/zephyr3d/backend-webgl/dist/capabilities_webgl.js.map +1 -1
  17. package/dist/vendor/zephyr3d/backend-webgpu/dist/capabilities_webgpu.js +2 -0
  18. package/dist/vendor/zephyr3d/backend-webgpu/dist/capabilities_webgpu.js.map +1 -1
  19. package/dist/vendor/zephyr3d/backend-webgpu/dist/commandqueue.js +55 -10
  20. package/dist/vendor/zephyr3d/backend-webgpu/dist/commandqueue.js.map +1 -1
  21. package/dist/vendor/zephyr3d/backend-webgpu/dist/computepass_webgpu.js +4 -0
  22. package/dist/vendor/zephyr3d/backend-webgpu/dist/computepass_webgpu.js.map +1 -1
  23. package/dist/vendor/zephyr3d/backend-webgpu/dist/device.js +44 -1
  24. package/dist/vendor/zephyr3d/backend-webgpu/dist/device.js.map +1 -1
  25. package/dist/vendor/zephyr3d/backend-webgpu/dist/renderpass_webgpu.js +58 -5
  26. package/dist/vendor/zephyr3d/backend-webgpu/dist/renderpass_webgpu.js.map +1 -1
  27. package/dist/vendor/zephyr3d/backend-webgpu/dist/timestamp_query.js +297 -0
  28. package/dist/vendor/zephyr3d/backend-webgpu/dist/timestamp_query.js.map +1 -0
  29. package/dist/vendor/zephyr3d/device/dist/base_types.js.map +1 -1
  30. package/dist/vendor/zephyr3d/device/dist/device.js +27 -0
  31. package/dist/vendor/zephyr3d/device/dist/device.js.map +1 -1
  32. package/dist/vendor/zephyr3d/device/dist/index.d.ts +81 -1
  33. package/dist/vendor/zephyr3d/loaders/dist/gltf/gltf_importer.js +208 -1
  34. package/dist/vendor/zephyr3d/loaders/dist/gltf/gltf_importer.js.map +1 -1
  35. package/dist/vendor/zephyr3d/loaders/dist/index.d.ts +8 -0
  36. package/dist/vendor/zephyr3d/scene/dist/animation/animationset.js +334 -63
  37. package/dist/vendor/zephyr3d/scene/dist/animation/animationset.js.map +1 -1
  38. package/dist/vendor/zephyr3d/scene/dist/animation/morphtargetgrouptrack.js +66 -0
  39. package/dist/vendor/zephyr3d/scene/dist/animation/morphtargetgrouptrack.js.map +1 -0
  40. package/dist/vendor/zephyr3d/scene/dist/asset/model.js +9 -0
  41. package/dist/vendor/zephyr3d/scene/dist/asset/model.js.map +1 -1
  42. package/dist/vendor/zephyr3d/scene/dist/index.d.ts +260 -4
  43. package/dist/vendor/zephyr3d/scene/dist/index.js +7 -0
  44. package/dist/vendor/zephyr3d/scene/dist/index.js.map +1 -1
  45. package/dist/vendor/zephyr3d/scene/dist/posteffect/sao.js +1 -1
  46. package/dist/vendor/zephyr3d/scene/dist/posteffect/sao.js.map +1 -1
  47. package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/executor.js +270 -1
  48. package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/executor.js.map +1 -1
  49. package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/forward_plus_builder.js +19 -1
  50. package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
  51. package/dist/vendor/zephyr3d/scene/dist/render/rendergraph/types.js.map +1 -1
  52. package/dist/vendor/zephyr3d/scene/dist/scene/scene.js +1 -0
  53. package/dist/vendor/zephyr3d/scene/dist/scene/scene.js.map +1 -1
  54. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/manager.js +2 -1
  55. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/manager.js.map +1 -1
  56. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/animation.js +70 -1
  57. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/animation.js.map +1 -1
  58. package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/camera.js.map +1 -1
  59. package/package.json +6 -6
  60. package/dist/vendor/zephyr3d/scene/dist/animation/joint_dynamics/convex_collider.js +0 -320
  61. package/dist/vendor/zephyr3d/scene/dist/animation/joint_dynamics/convex_collider.js.map +0 -1
@@ -44,10 +44,18 @@ declare class GLTFImporter extends AbstractModelImporter {
44
44
  private _numberKey;
45
45
  private _toJointDynamicsCollider;
46
46
  private _toJointDynamicsFlatPlane;
47
+ private _loadVRMHumanoidSkeletonMappings;
48
+ private _getVRMC1HumanoidMapping;
49
+ private _getVRMC0HumanoidMapping;
47
50
  private _setAssetSkeletonHumanoidMapping;
48
51
  private _getVRMAHumanoidMapping;
52
+ private _getVRMC0HumanoidMappingFromBones;
53
+ private _filterHumanoidMappingToSkeleton;
49
54
  private _getVRMAHumanBoneNode;
50
55
  private _getVRMAHumanoidNodes;
56
+ private _getVRMAExpressionMapping;
57
+ private _addVRMAExpressionMapping;
58
+ private _createVRMAExpressionInterpolator;
51
59
  private _findCommonRoot;
52
60
  private _sortSkeletonJoints;
53
61
  private _shouldGenerateMToonOutlineNormals;
@@ -4,6 +4,7 @@ import { NodeRotationTrack } from './rotationtrack.js';
4
4
  import { NodeEulerRotationTrack } from './eulerrotationtrack.js';
5
5
  import { NodeTranslationTrack } from './translationtrack.js';
6
6
  import { NodeScaleTrack } from './scaletrack.js';
7
+ import { MorphTargetGroupTrack } from './morphtargetgrouptrack.js';
7
8
  import { HumanoidBodyRig } from './skeleton.js';
8
9
  import { createSkeletalMaskedAnimationClip } from './animationmask.js';
9
10
 
@@ -232,7 +233,7 @@ function cloneInterpolator(src) {
232
233
  ...src.outputs
233
234
  ]);
234
235
  }
235
- function createJointRetargetRemap(srcSkeleton, dstSkeleton, srcJoint, dstJoint, translationRotation, translationScale, translationAxisLocks) {
236
+ function createJointRetargetRemap(srcSkeleton, dstSkeleton, srcJoint, dstJoint, translationRotation, translationScale, translationCorrection, translationAxisLocks, rotationCorrection) {
236
237
  const si = srcSkeleton.joints.indexOf(srcJoint);
237
238
  const di = dstSkeleton.joints.indexOf(dstJoint);
238
239
  const srcBindPose = srcSkeleton.bindPose[si];
@@ -245,24 +246,28 @@ function createJointRetargetRemap(srcSkeleton, dstSkeleton, srcJoint, dstJoint,
245
246
  srcNode: srcJoint,
246
247
  srcBindRotInv: Quaternion.inverse(srcBindPose.rotation),
247
248
  dstBindRot: dstBindPose.rotation.clone(),
249
+ rotationCorrection,
248
250
  srcBindPos: srcBindPose.position.clone(),
249
251
  dstBindPos: dstBindPose.position.clone(),
250
- translationScale: (srcLen > 1e-6 ? dstLen / srcLen : 1),
252
+ translationScale: translationScale ?? (srcLen > 1e-6 ? dstLen / srcLen : 1),
251
253
  translationRotation,
254
+ translationCorrection,
252
255
  translationAxisLocks
253
256
  };
254
257
  }
255
- function createTranslationRetargetRemap(srcNode, dstNode, srcBindPos, dstBindPos, dstJointIndex, translationScale, translationRotation, translationAxisLocks) {
258
+ function createTranslationRetargetRemap(srcNode, dstNode, srcBindPos, dstBindPos, dstJointIndex, translationScale, translationRotation, translationCorrection, translationAxisLocks, rotationCorrection) {
256
259
  return {
257
260
  dstNode,
258
261
  dstJointIndex,
259
262
  srcNode,
260
263
  srcBindRotInv: Quaternion.identity(),
261
264
  dstBindRot: Quaternion.identity(),
265
+ rotationCorrection,
262
266
  srcBindPos: srcBindPos.clone(),
263
267
  dstBindPos: dstBindPos.clone(),
264
268
  translationScale,
265
269
  translationRotation,
270
+ translationCorrection,
266
271
  translationAxisLocks
267
272
  };
268
273
  }
@@ -270,6 +275,151 @@ function getBindPosition(skeleton, joint) {
270
275
  const index = skeleton.joints.indexOf(joint);
271
276
  return index >= 0 ? skeleton.bindPose[index].position : null;
272
277
  }
278
+ function getSkeletonLocalBindTransform(skeleton, node, jointSet, cache) {
279
+ let transform = cache.get(node);
280
+ if (!transform) {
281
+ const bindPose = getRigBindPoseForNode(skeleton, node);
282
+ const position = bindPose.position.clone();
283
+ const rotation = bindPose.rotation.clone();
284
+ const parent = node.parent;
285
+ if (parent && jointSet.has(parent)) {
286
+ const parentTransform = getSkeletonLocalBindTransform(skeleton, parent, jointSet, cache);
287
+ parentTransform.rotation.transform(position, position);
288
+ position.addBy(parentTransform.position);
289
+ Quaternion.multiply(parentTransform.rotation, rotation, rotation);
290
+ }
291
+ transform = {
292
+ position,
293
+ rotation
294
+ };
295
+ cache.set(node, transform);
296
+ }
297
+ return transform;
298
+ }
299
+ function getHumanoidLateralXSign(skeleton) {
300
+ const mapping = skeleton.humanoidJointMapping;
301
+ if (!mapping) {
302
+ return 0;
303
+ }
304
+ const pairs = [
305
+ [
306
+ HumanoidBodyRig.LeftShoulder,
307
+ HumanoidBodyRig.RightShoulder
308
+ ],
309
+ [
310
+ HumanoidBodyRig.LeftUpperArm,
311
+ HumanoidBodyRig.RightUpperArm
312
+ ],
313
+ [
314
+ HumanoidBodyRig.LeftLowerArm,
315
+ HumanoidBodyRig.RightLowerArm
316
+ ],
317
+ [
318
+ HumanoidBodyRig.LeftHand,
319
+ HumanoidBodyRig.RightHand
320
+ ],
321
+ [
322
+ HumanoidBodyRig.LeftUpperLeg,
323
+ HumanoidBodyRig.RightUpperLeg
324
+ ],
325
+ [
326
+ HumanoidBodyRig.LeftLowerLeg,
327
+ HumanoidBodyRig.RightLowerLeg
328
+ ],
329
+ [
330
+ HumanoidBodyRig.LeftFoot,
331
+ HumanoidBodyRig.RightFoot
332
+ ],
333
+ [
334
+ HumanoidBodyRig.LeftToes,
335
+ HumanoidBodyRig.RightToes
336
+ ]
337
+ ];
338
+ const jointSet = new Set(skeleton.joints);
339
+ const cache = new Map();
340
+ let score = 0;
341
+ let weight = 0;
342
+ for (const [leftKey, rightKey] of pairs){
343
+ const left = mapping.body[leftKey];
344
+ const right = mapping.body[rightKey];
345
+ if (!left || !right || !jointSet.has(left) || !jointSet.has(right)) {
346
+ continue;
347
+ }
348
+ const dx = getSkeletonLocalBindTransform(skeleton, left, jointSet, cache).position.x - getSkeletonLocalBindTransform(skeleton, right, jointSet, cache).position.x;
349
+ if (Math.abs(dx) > 1e-5) {
350
+ score += dx;
351
+ weight++;
352
+ }
353
+ }
354
+ if (weight === 0 || Math.abs(score) < 1e-5) {
355
+ return 0;
356
+ }
357
+ return score > 0 ? 1 : -1;
358
+ }
359
+ function getHumanoidForwardZSign(skeleton) {
360
+ const mapping = skeleton.humanoidJointMapping;
361
+ if (!mapping) {
362
+ return 0;
363
+ }
364
+ const pairs = [
365
+ [
366
+ HumanoidBodyRig.LeftFoot,
367
+ HumanoidBodyRig.LeftToes
368
+ ],
369
+ [
370
+ HumanoidBodyRig.RightFoot,
371
+ HumanoidBodyRig.RightToes
372
+ ]
373
+ ];
374
+ const jointSet = new Set(skeleton.joints);
375
+ const cache = new Map();
376
+ let score = 0;
377
+ let weight = 0;
378
+ for (const [footKey, toesKey] of pairs){
379
+ const foot = mapping.body[footKey];
380
+ const toes = mapping.body[toesKey];
381
+ if (!foot || !toes || !jointSet.has(foot) || !jointSet.has(toes)) {
382
+ continue;
383
+ }
384
+ const dz = getSkeletonLocalBindTransform(skeleton, toes, jointSet, cache).position.z - getSkeletonLocalBindTransform(skeleton, foot, jointSet, cache).position.z;
385
+ if (Math.abs(dz) > 1e-5) {
386
+ score += dz;
387
+ weight++;
388
+ }
389
+ }
390
+ if (weight === 0 || Math.abs(score) < 1e-5) {
391
+ return 0;
392
+ }
393
+ return score > 0 ? 1 : -1;
394
+ }
395
+ function getHumanoidSignFromSkeletons(skeletons, getSign) {
396
+ let score = 0;
397
+ for (const skeleton of skeletons){
398
+ score += getSign(skeleton);
399
+ }
400
+ return score > 0 ? 1 : score < 0 ? -1 : 0;
401
+ }
402
+ function getHumanoidRotationCorrection(srcSkeleton, dstSkeleton, srcSignSkeletons = [
403
+ srcSkeleton
404
+ ], dstSignSkeletons = [
405
+ dstSkeleton
406
+ ]) {
407
+ const srcLateralXSign = getHumanoidLateralXSign(srcSkeleton) || getHumanoidSignFromSkeletons(srcSignSkeletons, getHumanoidLateralXSign);
408
+ const dstLateralXSign = getHumanoidLateralXSign(dstSkeleton) || getHumanoidSignFromSkeletons(dstSignSkeletons, getHumanoidLateralXSign);
409
+ const srcForwardZSign = getHumanoidForwardZSign(srcSkeleton) || getHumanoidSignFromSkeletons(srcSignSkeletons, getHumanoidForwardZSign);
410
+ const dstForwardZSign = getHumanoidForwardZSign(dstSkeleton) || getHumanoidSignFromSkeletons(dstSignSkeletons, getHumanoidForwardZSign);
411
+ const axisXSign = srcLateralXSign && dstLateralXSign && srcLateralXSign !== dstLateralXSign ? -1 : 1;
412
+ const axisZSign = srcForwardZSign && dstForwardZSign && srcForwardZSign !== dstForwardZSign ? -1 : 1;
413
+ if (axisXSign === 1 && axisZSign === 1) {
414
+ return null;
415
+ }
416
+ const determinant = axisXSign * axisZSign;
417
+ return {
418
+ x: determinant * axisXSign,
419
+ y: determinant,
420
+ z: determinant * axisZSign
421
+ };
422
+ }
273
423
  function getRigBindPoseForNode(skeleton, node) {
274
424
  return skeleton.getBindPoseForJoint(node) ?? (node === skeleton.rootJoint ? skeleton.rootBindPose : {
275
425
  position: node.position,
@@ -313,6 +463,35 @@ function getHumanoidRootMotionScale(srcSkeleton, dstSkeleton) {
313
463
  function findTranslationTrack(tracks) {
314
464
  return tracks?.find((track)=>track instanceof NodeTranslationTrack) ?? null;
315
465
  }
466
+ function findAncestorTranslationTrack(clip, node, modelRoot) {
467
+ let current = node.parent;
468
+ while(current){
469
+ const track = findTranslationTrack(clip.tracks.get(current));
470
+ if (track) {
471
+ return {
472
+ node: current,
473
+ track
474
+ };
475
+ }
476
+ if (current === modelRoot) {
477
+ break;
478
+ }
479
+ current = current.parent;
480
+ }
481
+ return null;
482
+ }
483
+ function copyMorphTargetGroupTracks(sourceClip, targetClip, target) {
484
+ for (const tracks of sourceClip.tracks.values()){
485
+ for (const track of tracks){
486
+ if (track instanceof MorphTargetGroupTrack) {
487
+ const cloned = track.clone();
488
+ cloned.name = track.name;
489
+ cloned.target = target.persistentId;
490
+ targetClip.addTrack(target, cloned);
491
+ }
492
+ }
493
+ }
494
+ }
316
495
  function applyTranslationAxisLocks(value, bindValue, locks) {
317
496
  if (locks?.x) {
318
497
  value.x = bindValue.x;
@@ -338,13 +517,25 @@ function applyTranslationTangentAxisLocks(value, locks) {
338
517
  function retargetRotation(qSrcAnim, remap, out) {
339
518
  Quaternion.multiply(remap.srcBindRotInv, qSrcAnim, out);
340
519
  Quaternion.multiply(remap.dstBindRot, out, out);
520
+ if (remap.rotationCorrection) {
521
+ applyHumanoidRotationCorrection(out, remap.rotationCorrection, out);
522
+ }
341
523
  return out.inplaceNormalize();
342
524
  }
343
525
  function retargetRotationTangent(qSrcTangent, remap, out) {
344
526
  Quaternion.multiply(remap.srcBindRotInv, qSrcTangent, out);
345
527
  Quaternion.multiply(remap.dstBindRot, out, out);
528
+ if (remap.rotationCorrection) {
529
+ applyHumanoidRotationCorrection(out, remap.rotationCorrection, out);
530
+ }
346
531
  return out;
347
532
  }
533
+ function applyHumanoidRotationCorrection(q, correction, out) {
534
+ return out.setXYZW(q.x * correction.x, q.y * correction.y, q.z * correction.z, q.w);
535
+ }
536
+ function applyHumanoidTranslationCorrection(value, correction, out) {
537
+ return out.setXYZ(value.x * correction.x, value.y * correction.y, value.z * correction.z);
538
+ }
348
539
  function retargetTranslationValue(srcValue, remap, out) {
349
540
  out.setXYZ(srcValue.x, srcValue.y, srcValue.z);
350
541
  out.subBy(remap.srcBindPos);
@@ -352,6 +543,9 @@ function retargetTranslationValue(srcValue, remap, out) {
352
543
  if (remap.translationRotation) {
353
544
  remap.translationRotation.transform(out, out);
354
545
  }
546
+ if (remap.translationCorrection) {
547
+ applyHumanoidTranslationCorrection(out, remap.translationCorrection, out);
548
+ }
355
549
  out.addBy(remap.dstBindPos);
356
550
  applyTranslationAxisLocks(out, remap.dstBindPos, remap.translationAxisLocks);
357
551
  return out;
@@ -362,6 +556,9 @@ function retargetTranslationTangent(srcValue, remap, out) {
362
556
  if (remap.translationRotation) {
363
557
  remap.translationRotation.transform(out, out);
364
558
  }
559
+ if (remap.translationCorrection) {
560
+ applyHumanoidTranslationCorrection(out, remap.translationCorrection, out);
561
+ }
365
562
  applyTranslationTangentAxisLocks(out, remap.translationAxisLocks);
366
563
  return out;
367
564
  }
@@ -461,7 +658,7 @@ function sampleRotationTrack(track, time, fallback, out) {
461
658
  out.set(state);
462
659
  return out;
463
660
  }
464
- function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRootRotation, dstRootRotation, dstClip, remaps) {
661
+ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRootRotation, dstRootRotation, dstClip, remaps, rotationCorrection) {
465
662
  const rotationTracksByRemap = new Map();
466
663
  const times = new Set([
467
664
  0,
@@ -498,7 +695,10 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
498
695
  const outputsByRemap = new Map();
499
696
  const tmpLocalRot = new Quaternion();
500
697
  const tmpWorldDelta = new Quaternion();
698
+ const tmpRootDelta = new Quaternion();
501
699
  const tmpParentInv = new Quaternion();
700
+ const srcRootRotationInv = Quaternion.inverse(srcRootRotation);
701
+ const dstRootRotationInv = Quaternion.inverse(dstRootRotation);
502
702
  for (const remap of remaps){
503
703
  outputsByRemap.set(remap, new Float32Array(inputs.length * 4));
504
704
  }
@@ -555,6 +755,13 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
555
755
  const srcBindWorldRot = getSrcBindWorldRot(remap.srcNode);
556
756
  const srcAnimWorldRot = getSrcAnimWorldRot(remap.srcNode, time);
557
757
  Quaternion.multiply(srcAnimWorldRot, Quaternion.inverse(srcBindWorldRot, tmpWorldDelta), tmpWorldDelta);
758
+ if (rotationCorrection) {
759
+ Quaternion.multiply(srcRootRotationInv, tmpWorldDelta, tmpRootDelta);
760
+ Quaternion.multiply(tmpRootDelta, srcRootRotation, tmpRootDelta);
761
+ applyHumanoidRotationCorrection(tmpRootDelta, rotationCorrection, tmpRootDelta);
762
+ Quaternion.multiply(dstRootRotation, tmpRootDelta, tmpWorldDelta);
763
+ Quaternion.multiply(tmpWorldDelta, dstRootRotationInv, tmpWorldDelta);
764
+ }
558
765
  Quaternion.multiply(tmpWorldDelta, getDstBindWorldRot(remap.dstNode), rot);
559
766
  return rot;
560
767
  }
@@ -1449,8 +1656,7 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
1449
1656
  console.error(`copyHumanoidAnimationFrom: source skeleton does not have a humanoid joint mapping`);
1450
1657
  return null;
1451
1658
  }
1452
- const nodeMap = new Map();
1453
- const jointRemapBySrcNode = new Map();
1659
+ const jointRemapsBySrcNode = new Map();
1454
1660
  // Find a destination skeleton that has a humanoid mapping and share at least the body rig keys
1455
1661
  let srcJointsFiltered = [];
1456
1662
  let dstJointsFiltered = [];
@@ -1463,38 +1669,34 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
1463
1669
  if (!dstHumanoidMapping) {
1464
1670
  return false;
1465
1671
  }
1672
+ const srcHips = srcHumanoidMapping.body[HumanoidBodyRig.Hips];
1673
+ const dstHips = dstHumanoidMapping.body[HumanoidBodyRig.Hips];
1674
+ if (!srcHips || !dstHips || !srcSkeleton.joints.includes(srcHips) || !sk.joints.includes(dstHips)) {
1675
+ return false;
1676
+ }
1466
1677
  // Collect matched (srcJoint, dstJoint) pairs via shared humanoid rig keys
1467
1678
  const srcMatched = [];
1468
1679
  const dstMatched = [];
1469
- // Match body rig joints
1470
- for (const key of Object.keys(srcHumanoidMapping.body)){
1471
- const srcJoint = srcHumanoidMapping.body[key];
1472
- const dstJoint = dstHumanoidMapping.body[key];
1473
- if (srcJoint && dstJoint) {
1680
+ const addMatch = (srcJoint, dstJoint)=>{
1681
+ if (srcJoint && dstJoint && srcSkeleton.joints.includes(srcJoint) && sk.joints.includes(dstJoint)) {
1474
1682
  srcMatched.push(srcJoint);
1475
1683
  dstMatched.push(dstJoint);
1476
1684
  }
1685
+ };
1686
+ // Match body rig joints
1687
+ for (const key of Object.keys(srcHumanoidMapping.body)){
1688
+ addMatch(srcHumanoidMapping.body[key], dstHumanoidMapping.body[key]);
1477
1689
  }
1478
1690
  // Match left hand rig joints only when both sides define them; skip silently if either is absent
1479
1691
  if (srcHumanoidMapping.leftHand && dstHumanoidMapping.leftHand) {
1480
1692
  for (const key of Object.keys(srcHumanoidMapping.leftHand)){
1481
- const srcJoint = srcHumanoidMapping.leftHand[key];
1482
- const dstJoint = dstHumanoidMapping.leftHand[key];
1483
- if (srcJoint && dstJoint) {
1484
- srcMatched.push(srcJoint);
1485
- dstMatched.push(dstJoint);
1486
- }
1693
+ addMatch(srcHumanoidMapping.leftHand[key], dstHumanoidMapping.leftHand[key]);
1487
1694
  }
1488
1695
  }
1489
1696
  // Match right hand rig joints only when both sides define them; skip silently if either is absent
1490
1697
  if (srcHumanoidMapping.rightHand && dstHumanoidMapping.rightHand) {
1491
1698
  for (const key of Object.keys(srcHumanoidMapping.rightHand)){
1492
- const srcJoint = srcHumanoidMapping.rightHand[key];
1493
- const dstJoint = dstHumanoidMapping.rightHand[key];
1494
- if (srcJoint && dstJoint) {
1495
- srcMatched.push(srcJoint);
1496
- dstMatched.push(dstJoint);
1497
- }
1699
+ addMatch(srcHumanoidMapping.rightHand[key], dstHumanoidMapping.rightHand[key]);
1498
1700
  }
1499
1701
  }
1500
1702
  if (srcMatched.length === 0) {
@@ -1532,28 +1734,69 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
1532
1734
  const srcRootRot = computeParentChainRotation(srcRootNode, sourceSet.model, srcSkeleton);
1533
1735
  const dstRootRot = computeParentChainRotation(dstRootNode, this._model, dstSkeleton);
1534
1736
  const srcRootTranslationTrack = findTranslationTrack(sourceClip.tracks.get(srcRootNode));
1535
- const srcFallbackHipsTranslationTrack = !srcRootTranslationTrack && srcRootNode !== srcHipsNode ? findTranslationTrack(sourceClip.tracks.get(srcHipsNode)) : null;
1536
- const srcMotionTrack = srcRootTranslationTrack ?? srcFallbackHipsTranslationTrack;
1537
- const srcMotionNode = srcRootTranslationTrack ? srcRootNode : srcFallbackHipsTranslationTrack ? srcHipsNode : null;
1737
+ const srcAncestorTranslationTrackInfo = !srcRootTranslationTrack ? findAncestorTranslationTrack(sourceClip, srcRootNode, sourceSet.model) : null;
1738
+ const srcFallbackHipsTranslationTrack = !srcRootTranslationTrack && !srcAncestorTranslationTrackInfo && srcRootNode !== srcHipsNode ? findTranslationTrack(sourceClip.tracks.get(srcHipsNode)) : null;
1739
+ const srcMotionTrack = srcRootTranslationTrack ?? srcAncestorTranslationTrackInfo?.track ?? srcFallbackHipsTranslationTrack;
1740
+ const srcMotionNode = srcRootTranslationTrack ? srcRootNode : srcAncestorTranslationTrackInfo ? srcAncestorTranslationTrackInfo.node : srcFallbackHipsTranslationTrack ? srcHipsNode : null;
1538
1741
  const srcMotionParentRot = srcMotionNode ? computeParentChainRotation(srcMotionNode, sourceSet.model, srcSkeleton) : srcRootRot;
1539
1742
  const rootTranslationRotation = Quaternion.multiply(Quaternion.inverse(dstRootRot), srcMotionParentRot);
1540
1743
  const rootTranslationScale = rootMotion === 'scaled' ? typeof options.rootMotionScale === 'number' ? options.rootMotionScale : options.rootMotionScale === 'none' ? 1 : getHumanoidRootMotionScale(srcSkeleton, dstSkeleton) : 1;
1541
1744
  const jointRemaps = [];
1542
- const mappedSrcNodes = new Set();
1543
- const mappedDstNodes = new Set();
1745
+ const jointRemapsByDstSkeleton = new Map([
1746
+ [
1747
+ dstSkeleton,
1748
+ jointRemaps
1749
+ ]
1750
+ ]);
1751
+ const srcSignSkeletons = sourceSet._rigs.map((ref)=>ref.get()).filter((sk)=>!!sk);
1752
+ const dstSignSkeletons = this._rigs.map((ref)=>ref.get()).filter((sk)=>!!sk);
1753
+ const humanoidRotationCorrection = getHumanoidRotationCorrection(srcSkeleton, dstSkeleton, srcSignSkeletons, dstSignSkeletons);
1754
+ const addJointRemap = (targetSkeleton, srcJoint, dstJoint, applyRotationCorrection = true)=>{
1755
+ if (!srcJoint || !dstJoint || !srcSkeleton.joints.includes(srcJoint) || !targetSkeleton.joints.includes(dstJoint)) {
1756
+ return;
1757
+ }
1758
+ const remap = createJointRetargetRemap(srcSkeleton, targetSkeleton, srcJoint, dstJoint, undefined, undefined, humanoidRotationCorrection ?? undefined, undefined, applyRotationCorrection ? humanoidRotationCorrection ?? undefined : undefined);
1759
+ let targetRemaps = jointRemapsByDstSkeleton.get(targetSkeleton);
1760
+ if (!targetRemaps) {
1761
+ targetRemaps = [];
1762
+ jointRemapsByDstSkeleton.set(targetSkeleton, targetRemaps);
1763
+ }
1764
+ targetRemaps.push(remap);
1765
+ let srcRemaps = jointRemapsBySrcNode.get(srcJoint);
1766
+ if (!srcRemaps) {
1767
+ srcRemaps = [];
1768
+ jointRemapsBySrcNode.set(srcJoint, srcRemaps);
1769
+ }
1770
+ srcRemaps.push(remap);
1771
+ };
1772
+ const addHumanoidMappingRemaps = (targetSkeleton)=>{
1773
+ const targetMapping = targetSkeleton.humanoidJointMapping;
1774
+ if (!targetMapping) {
1775
+ return;
1776
+ }
1777
+ const applyRotationCorrection = targetSkeleton === dstSkeleton;
1778
+ for (const key of Object.keys(srcHumanoidMapping.body)){
1779
+ addJointRemap(targetSkeleton, srcHumanoidMapping.body[key], targetMapping.body[key], applyRotationCorrection);
1780
+ }
1781
+ if (srcHumanoidMapping.leftHand && targetMapping.leftHand) {
1782
+ for (const key of Object.keys(srcHumanoidMapping.leftHand)){
1783
+ addJointRemap(targetSkeleton, srcHumanoidMapping.leftHand[key], targetMapping.leftHand[key], applyRotationCorrection);
1784
+ }
1785
+ }
1786
+ if (srcHumanoidMapping.rightHand && targetMapping.rightHand) {
1787
+ for (const key of Object.keys(srcHumanoidMapping.rightHand)){
1788
+ addJointRemap(targetSkeleton, srcHumanoidMapping.rightHand[key], targetMapping.rightHand[key], applyRotationCorrection);
1789
+ }
1790
+ }
1791
+ };
1544
1792
  // Build remap for matched joint pairs
1545
1793
  for(let fi = 0; fi < srcJointsFiltered.length; fi++){
1546
- const srcJoint = srcJointsFiltered[fi];
1547
- const dstJoint = dstJointsFiltered[fi];
1548
- if (mappedSrcNodes.has(srcJoint) || mappedDstNodes.has(dstJoint)) {
1549
- continue;
1794
+ addJointRemap(dstSkeleton, srcJointsFiltered[fi], dstJointsFiltered[fi], true);
1795
+ }
1796
+ for (const targetSkeleton of dstSignSkeletons){
1797
+ if (targetSkeleton !== dstSkeleton) {
1798
+ addHumanoidMappingRemaps(targetSkeleton);
1550
1799
  }
1551
- mappedSrcNodes.add(srcJoint);
1552
- mappedDstNodes.add(dstJoint);
1553
- nodeMap.set(srcJoint, dstJoint);
1554
- const remap = createJointRetargetRemap(srcSkeleton, dstSkeleton, srcJoint, dstJoint);
1555
- jointRemaps.push(remap);
1556
- jointRemapBySrcNode.set(srcJoint, remap);
1557
1800
  }
1558
1801
  const dstClip = this.createAnimation(destName);
1559
1802
  if (!dstClip) {
@@ -1562,9 +1805,37 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
1562
1805
  dstClip.timeDuration = sourceClip.timeDuration;
1563
1806
  dstClip.weight = sourceClip.weight;
1564
1807
  dstClip.autoPlay = sourceClip.autoPlay;
1565
- // Register destination skeleton
1566
- dstClip.addSkeleton(dstSkeleton.persistentId);
1567
- bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRootRot, dstRootRot, dstClip, jointRemaps);
1808
+ copyMorphTargetGroupTracks(sourceClip, dstClip, this._model);
1809
+ for (const [targetSkeleton, targetRemaps] of jointRemapsByDstSkeleton){
1810
+ if (targetRemaps.length === 0) {
1811
+ continue;
1812
+ }
1813
+ const hasCopyableTrack = targetSkeleton === dstSkeleton || targetRemaps.some((remap)=>{
1814
+ const srcTracks = sourceClip.tracks.get(remap.srcNode);
1815
+ if (!srcTracks) {
1816
+ return false;
1817
+ }
1818
+ for (const srcTrack of srcTracks){
1819
+ if (srcTrack instanceof NodeRotationTrack || srcTrack instanceof NodeEulerRotationTrack) {
1820
+ return true;
1821
+ }
1822
+ if (srcTrack instanceof NodeTranslationTrack && remap.srcNode !== srcRootNode && srcTrack !== srcMotionTrack && jointTranslations === 'preserve') {
1823
+ return true;
1824
+ }
1825
+ if (srcTrack instanceof NodeScaleTrack) {
1826
+ return true;
1827
+ }
1828
+ }
1829
+ return false;
1830
+ });
1831
+ if (!hasCopyableTrack) {
1832
+ continue;
1833
+ }
1834
+ dstClip.addSkeleton(targetSkeleton.persistentId);
1835
+ const targetRootNode = targetSkeleton.rootJoint ?? targetSkeleton.humanoidJointMapping?.body[HumanoidBodyRig.Hips] ?? targetRemaps[0].dstNode;
1836
+ const targetRootRot = targetRootNode === dstRootNode ? dstRootRot : computeParentChainRotation(targetRootNode, this._model, targetSkeleton);
1837
+ bakeHumanoidRotationTracks(sourceClip, srcSkeleton, targetSkeleton, srcRootRot, targetRootRot, dstClip, targetRemaps, humanoidRotationCorrection);
1838
+ }
1568
1839
  if (rootMotion !== 'none') {
1569
1840
  const dstRootBindPose = getRigBindPoseForNode(dstSkeleton, dstRootNode);
1570
1841
  let dstRootTrack = null;
@@ -1572,7 +1843,7 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
1572
1843
  dstRootTrack = createConstantTranslationTrack(dstRootBindPose.position);
1573
1844
  } else if (srcMotionTrack && srcMotionNode) {
1574
1845
  const srcMotionBindPose = getRigBindPoseForNode(srcSkeleton, srcMotionNode);
1575
- const rootRemap = createTranslationRetargetRemap(srcMotionNode, dstRootNode, srcMotionBindPose.position, dstRootBindPose.position, dstSkeleton.joints.indexOf(dstRootNode), rootTranslationScale, rootTranslationRotation, options.lockRootMotionAxes);
1846
+ const rootRemap = createTranslationRetargetRemap(srcMotionNode, dstRootNode, srcMotionBindPose.position, dstRootBindPose.position, dstSkeleton.joints.indexOf(dstRootNode), rootTranslationScale, rootTranslationRotation, humanoidRotationCorrection ?? undefined, options.lockRootMotionAxes, humanoidRotationCorrection ?? undefined);
1576
1847
  dstRootTrack = retargetTranslationTrack(srcMotionTrack, rootRemap);
1577
1848
  }
1578
1849
  if (dstRootTrack) {
@@ -1582,34 +1853,33 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
1582
1853
  dstClip.addTrack(dstRootNode, dstRootTrack);
1583
1854
  }
1584
1855
  }
1585
- for (const srcNode of srcJointsFiltered){
1856
+ for (const [srcNode, srcRemaps] of jointRemapsBySrcNode){
1586
1857
  const srcTracks = sourceClip.tracks.get(srcNode);
1587
1858
  if (!srcTracks) {
1588
1859
  continue;
1589
1860
  }
1590
- const dstNode = nodeMap.get(srcNode);
1591
- const remap = jointRemapBySrcNode.get(srcNode);
1592
- for (const srcTrack of srcTracks){
1593
- let dstTrack;
1594
- if (srcTrack instanceof NodeRotationTrack) {
1595
- continue;
1596
- } else if (srcTrack instanceof NodeEulerRotationTrack) {
1597
- continue;
1598
- } else if (srcTrack instanceof NodeTranslationTrack) {
1599
- if (srcNode === srcRootNode || srcTrack === srcMotionTrack || jointTranslations !== 'preserve') {
1861
+ for (const remap of srcRemaps){
1862
+ const dstNode = remap.dstNode;
1863
+ for (const srcTrack of srcTracks){
1864
+ let dstTrack;
1865
+ if (srcTrack instanceof NodeRotationTrack || srcTrack instanceof NodeEulerRotationTrack) {
1866
+ continue;
1867
+ } else if (srcTrack instanceof NodeTranslationTrack) {
1868
+ if (srcNode === srcRootNode || srcTrack === srcMotionTrack || jointTranslations !== 'preserve') {
1869
+ continue;
1870
+ }
1871
+ dstTrack = retargetTranslationTrack(srcTrack, remap);
1872
+ } else if (srcTrack instanceof NodeScaleTrack) {
1873
+ dstTrack = new NodeScaleTrack(cloneInterpolator(srcTrack.interpolator));
1874
+ } else {
1875
+ console.warn(`copyHumanoidAnimationFrom: unsupported track type '${srcTrack.constructor.name}', skipping`);
1600
1876
  continue;
1601
1877
  }
1602
- dstTrack = retargetTranslationTrack(srcTrack, remap);
1603
- } else if (srcTrack instanceof NodeScaleTrack) {
1604
- dstTrack = new NodeScaleTrack(cloneInterpolator(srcTrack.interpolator));
1605
- } else {
1606
- console.warn(`copyHumanoidAnimationFrom: unsupported track type '${srcTrack.constructor.name}', skipping`);
1607
- continue;
1878
+ dstTrack.name = srcTrack.name;
1879
+ dstTrack.target = dstNode.persistentId;
1880
+ dstTrack.jointIndex = remap.dstJointIndex;
1881
+ dstClip.addTrack(dstNode, dstTrack);
1608
1882
  }
1609
- dstTrack.name = srcTrack.name;
1610
- dstTrack.target = dstNode.persistentId;
1611
- dstTrack.jointIndex = remap.dstJointIndex;
1612
- dstClip.addTrack(dstNode, dstTrack);
1613
1883
  }
1614
1884
  }
1615
1885
  return dstClip;
@@ -1702,6 +1972,7 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
1702
1972
  dstClip.autoPlay = sourceClip.autoPlay;
1703
1973
  // Register destination skeleton
1704
1974
  dstClip.addSkeleton(dstSkeleton.persistentId);
1975
+ copyMorphTargetGroupTracks(sourceClip, dstClip, this._model);
1705
1976
  for (const srcNode of srcJointsFiltered){
1706
1977
  const srcTracks = sourceClip.tracks.get(srcNode);
1707
1978
  if (!srcTracks) {