I'm having an issue with the C#/XNA runtime code when copying attachments from an existing skin to a dynamically created skin. If the mesh attachment's parentMesh field is not null, then calling the Copy() member function results in a linked mesh. When assigning this new skin to the skeleton, nothing is rendered for that slot. This only occurs if the parentMesh is not null, but if it is null, then the copied attachment is rendered correctly.
Say that we create a new skin, and add a single attachment from the original skin:
var atlas = new Atlas(atlasPath, textureLoader);
float scale = 1f;
SkeletonData skeletonData;
if (binary)
{
var binaryData = new SkeletonBinary(atlas);
binaryData.Scale = scale;
skeletonData = binaryData.ReadSkeletonData(spinePath);
}
else
{
SkeletonJson json = new SkeletonJson(atlas);
json.Scale = scale;
skeletonData = json.ReadSkeletonData(spinePath);
}
var skeleton = new Skeleton(skeletonData);
var stateData = new AnimationStateData(skeleton.Data);
var state = new AnimationState(stateData);
var templateSkin = skeleton.Data.FindSkin("SomeSkin");
var mySkin = new Skin("Skin1");
var slotName = "Arm"; // This slot has parentMesh != null
var itemSlotIndex = skeleton.FindSlotIndex(slotName);
var defaultAttachment = templateSkin.GetAttachment(itemSlotIndex, slotName);
var placeholderKey = slotName; // assume there is only 1 placeholder for this slot, so it's the same name
var clonedAttachment = defaultAttachment.GetCopy(false);
mySkin.SetAttachment(itemSlotIndex, placeholderKey, clonedAttachment);
skeleton.SetSkin(mySkin);
skeleton.SetSlotsToSetupPose();
state.Apply(_skeleton);
state.Update(0);
var currentTrack = _state.GetCurrent(0);
if (currentTrack != null)
{
currentTrack.Animation.Apply(_skeleton, 0f, currentTrack.TrackTime, currentTrack.Loop, null, 1, MixBlend.Setup, MixDirection.Out);
}
In AttachmentCloneExtensions.cs:
public static Attachment GetCopy (this Attachment o, bool cloneMeshesAsLinked) {
var meshAttachment = o as MeshAttachment;
if (meshAttachment != null && cloneMeshesAsLinked)
return meshAttachment.NewLinkedMesh();
return o.Copy();
}
Now, given that we have passed false to cloneMeshesAsLinked, it will call o.Copy()
on this attachment, which ends up in MeshAttachment.Copy():
public override Attachment Copy () {
if (parentMesh != null) return NewLinkedMesh(); // <= important
...
CopyTo(copy); // <= important
...
return copy;
}
internal void CopyTo (VertexAttachment attachment) {
if (bones != null) {
attachment.bones = new int[bones.Length];
Array.Copy(bones, 0, attachment.bones, 0, bones.Length);
}
else
attachment.bones = null;
if (vertices != null) {
attachment.vertices = new float[vertices.Length];
Array.Copy(vertices, 0, attachment.vertices, 0, vertices.Length);
}
else
attachment.vertices = null;
attachment.worldVerticesLength = worldVerticesLength;
attachment.deformAttachment = deformAttachment;
}
Now, this statement kicks in, because parentMesh is not null:
if (parentMesh != null) return NewLinkedMesh();
So it calls NewLinkedMesh().
///<summary>Returns a new mesh with this mesh set as the <see cref="ParentMesh"/>.
public MeshAttachment NewLinkedMesh () {
...
return mesh;
}
The reason a non-linked mesh renders correctly seems to be related to the VertexAttachment data, which is copied for non-linked meshes (the internal CopyTo() method).
There is no way to force the Copy() to return an un-linked mesh without moving this code out into a separate method, purely because of the if (parentMesh != null) return NewLinkedMesh();
.
I don't have enough knowledge of the workings of Spine to know why a linked mesh assigned to a new skin wouldn't be rendering correctly, so if someone would care to explain it then I would really appreciate it.
So, is this a bug in the rendering code when using a dynamically created skin, and assigning a cloned linked attachment from an existing skin, or is this an oversight in the Copy() method, where it is always forcing a linked mesh whether or not we want it to be linked (because of if (parentMesh != null) return NewLinkedMesh();
)?