Yes, this is possible. It is a pain in the ass though.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Spine;
public class AnimationRetarget : MonoBehaviour {
public SkeletonDataAsset originDataAsset;
[SpineAnimation(dataField: "originDataAsset")]
public string[] originAnimationNames;
[SpineAnimation(dataField: "originDataAsset")]
public string autoPlayAnimation;
public SkeletonDataAsset auxiliaryDataAsset;
void Start () {
SkeletonAnimation skeletonAnimation = GetComponent<SkeletonAnimation>();
Dictionary<int, int> boneIndexTable = new Dictionary<int, int>();
Dictionary<int, int> slotIndexTable = new Dictionary<int, int>();
var originData = originDataAsset.GetSkeletonData(true);
var auxiliaryData = auxiliaryDataAsset.GetSkeletonData(true);
var destinationData = skeletonAnimation.skeletonDataAsset.GetSkeletonData(true);
for (int i = 0; i < originData.Bones.Count; i++) {
string name = originData.Bones[i].Name;
boneIndexTable.Add(i, destinationData.FindBoneIndex(name));
}
for (int i = 0; i < originData.Slots.Count; i++) {
string name = originData.Slots[i].Name;
slotIndexTable.Add(i, destinationData.FindSlotIndex(name));
}
//test data comparity
for (int i = 0; i < originData.Slots.Count; i++) {
if (originData.Slots[i].Name != auxiliaryData.Slots[i].Name) {
//Debug.Log("Index " + i + "doesnt match! " + originData.slots[i].name + " : " + auxiliaryData.slots[i].name);
}
}
foreach (var originAnimationName in originAnimationNames) {
var originAnim = originData.FindAnimation(originAnimationName);
Spine.Animation auxiliaryAnim = null;
if (auxiliaryData != null)
auxiliaryAnim = auxiliaryData.FindAnimation(originAnimationName);
Debug.Log(auxiliaryAnim.Name);
List<Timeline> timelines = new List<Timeline>();
foreach (var t in originAnim.Timelines) {
if (t is ScaleTimeline) {
var st = (ScaleTimeline)t;
ScaleTimeline stn = new ScaleTimeline(st.FrameCount);
stn.Frames = st.Frames;
stn.Curves = st.Curves;
stn.BoneIndex = boneIndexTable[st.BoneIndex];
timelines.Add(stn);
} else if (t is TranslateTimeline) {
var tt = (TranslateTimeline)t;
TranslateTimeline ttn = new TranslateTimeline(tt.FrameCount);
ttn.Frames = tt.Frames;
ttn.Curves = tt.Curves;
ttn.BoneIndex = boneIndexTable[tt.BoneIndex];
timelines.Add(ttn);
} else if (t is RotateTimeline) {
var rt = (RotateTimeline)t;
RotateTimeline rtn = new RotateTimeline(rt.FrameCount);
rtn.Frames = rt.Frames;
rtn.Curves = rt.Curves;
rtn.BoneIndex = boneIndexTable[rt.BoneIndex];
timelines.Add(rtn);
} else if (t is AttachmentTimeline) {
var at = (AttachmentTimeline)t;
AttachmentTimeline atn = new AttachmentTimeline(at.FrameCount);
atn.Frames = at.Frames;
atn.AttachmentNames = at.AttachmentNames;
atn.SlotIndex = slotIndexTable[at.SlotIndex];
timelines.Add(atn);
} else if (t is ColorTimeline) {
var ct = (ColorTimeline)t;
ColorTimeline ctn = new ColorTimeline(ct.FrameCount);
ctn.Frames = ct.Frames;
ctn.Curves = ct.Curves;
ctn.SlotIndex = slotIndexTable[ct.SlotIndex];
timelines.Add(ctn);
} else if (t is DrawOrderTimeline) {
//TODO: This is difficult because draw order keyframes are Relative...
}
}
if (auxiliaryAnim != null) {
foreach (var t in auxiliaryAnim.Timelines) {
if (t is ScaleTimeline) {
var st = (ScaleTimeline)t;
if (originData.FindBone(auxiliaryData.Bones[st.BoneIndex].Name) != null)
continue;
ScaleTimeline stn = new ScaleTimeline(st.FrameCount);
stn.Frames = st.Frames;
stn.Curves = st.Curves;
stn.BoneIndex = destinationData.FindBoneIndex(auxiliaryData.Bones[st.BoneIndex].Name);
timelines.Add(stn);
} else if (t is TranslateTimeline) {
var tt = (TranslateTimeline)t;
if (originData.FindBone(auxiliaryData.Bones[tt.BoneIndex].Name) != null)
continue;
TranslateTimeline ttn = new TranslateTimeline(tt.FrameCount);
ttn.Frames = tt.Frames;
ttn.Curves = tt.Curves;
ttn.BoneIndex = destinationData.FindBoneIndex(auxiliaryData.Bones[tt.BoneIndex].Name);
timelines.Add(ttn);
} else if (t is RotateTimeline) {
var rt = (RotateTimeline)t;
if (originData.FindBone(auxiliaryData.Bones[rt.BoneIndex].Name) != null)
continue;
RotateTimeline rtn = new RotateTimeline(rt.FrameCount);
rtn.Frames = rt.Frames;
rtn.Curves = rt.Curves;
rtn.BoneIndex = destinationData.FindBoneIndex(auxiliaryData.Bones[rt.BoneIndex].Name);
timelines.Add(rtn);
} else if (t is AttachmentTimeline) {
var at = (AttachmentTimeline)t;
if (originData.FindSlot(auxiliaryData.Bones[at.SlotIndex].Name) != null)
continue;
AttachmentTimeline atn = new AttachmentTimeline(at.FrameCount);
atn.Frames = at.Frames;
atn.AttachmentNames = at.AttachmentNames;
atn.SlotIndex = destinationData.FindSlotIndex(auxiliaryData.Slots[at.SlotIndex].Name);
timelines.Add(atn);
} else if (t is ColorTimeline) {
var ct = (ColorTimeline)t;
if (originData.FindSlot(auxiliaryData.Bones[ct.SlotIndex].Name) != null)
continue;
ColorTimeline ctn = new ColorTimeline(ct.FrameCount);
ctn.Frames = ct.Frames;
ctn.Curves = ct.Curves;
ctn.SlotIndex = destinationData.FindSlotIndex(auxiliaryData.Slots[ct.SlotIndex].Name);
timelines.Add(ctn);
} else if (t is DrawOrderTimeline) {
var dt = (DrawOrderTimeline)t;
DrawOrderTimeline dtn = new DrawOrderTimeline(dt.FrameCount);
dtn.Frames = dt.Frames;
dtn.DrawOrders = dt.DrawOrders;
for (int i = 0; i < dtn.DrawOrders.Length; i++) {
for (int n = 0; n < dtn.DrawOrders[i].Length; n++) {
dtn.DrawOrders[i][n] = destinationData.FindSlotIndex(auxiliaryData.Slots[dtn.DrawOrders[i][n]].Name);
}
}
timelines.Add(dtn);
}
}
}
Spine.Animation retarget = new Spine.Animation(originAnim.Name, timelines, originAnim.Duration);
destinationData.Animations.Add(retarget);
}
skeletonAnimation.AnimationName = autoPlayAnimation;
}
}
Here's something i wrote a while back as support to a company.
the purpose was... (assuming 3 Skeleton JSON's.... A B and C...)
A was base character with base animations
B was another character model with more bones (hair, skirt, so on)
C just filled in the gaps in the animation.
Hope that script helps you get started... it is by no means a perfect solution. Goodluck!
PS:
I'm pretty sure I had to expose the Curves and maybe Frames array as public to get everything working.