Hi,
I was working with the "regular" version of skeleton in Unity to do some tests (In regular i mean the non-UI version). Then I found that I need to work with the UI model for my game. However, when i try to assign a new mesh attachment to change a part, it doesnt react the same way and I can't find why. I've investigated and found that the new mesh assignement actually works (UV are correctly set), and the new sprite I correcty assigned (I can debug it). But this is the main texture which is displayed
:bang:
Here is a sample of my code :
void AssignMeshPart(string slotName, Sprite newSprite, string parentAttachment)
{
MeshAttachment parentMesh = LinkedMeshUtilities.FindAttachment(skeleton, slotName, girlSkin, parentAttachment) as MeshAttachment;
if (parentMesh == null)
{
Debug.LogError(parentAttachment + " in [" + girlSkin + "] is not a MeshAttachment.");
return;
}
AtlasRegion a = newSprite.ToAtlasRegionPMAClone(Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)"), TextureFormat.RGBA32, false);
MeshAttachment newLinkedMesh = LinkedMeshUtilities.NewLinkedMeshAttachment("name", a, parentMesh);
skeleton.FindSlot(slotName).Attachment = newLinkedMesh;
Debug.Log(skeleton.FindSlot(slotName).Attachment.GetMaterial());
}
public static class LinkedMeshUtilities
{
/// <summary>May return null if the attachment is not found.</summary>
static public Attachment FindAttachment(Skeleton skeleton, string slotName, string skinName, string attachmentName)
{
int slotIndex = skeleton.FindSlotIndex(slotName);
Skin skin = string.IsNullOrEmpty(skinName) ? skeleton.data.defaultSkin : skeleton.data.FindSkin(skinName);
Attachment attachment = skin.GetAttachment(slotIndex, attachmentName);
return attachment;
}
/// <summary>Instantiate a new MeshAttachment with a region, and linked to parentMesh.</summary>
static public MeshAttachment NewLinkedMeshAttachment(string attachmentName, AtlasRegion region, MeshAttachment parentMesh, bool inheritDeform = true)
{
if (string.IsNullOrEmpty(attachmentName)) throw new System.ArgumentException("attachmentName can't be null or empty.", "attachmentName");
if (region == null) throw new System.ArgumentNullException("region");
if (parentMesh == null) throw new System.ArgumentNullException("parentMesh");
// 1. NewMeshAttachment (AtlasAttachmentLoader.cs)
MeshAttachment mesh = new MeshAttachment(attachmentName);
SetRegion(mesh, region, false);
// 2. ReadAttachment. case: LinkedMesh (SkeletonJson.cs)
mesh.Path = attachmentName;
mesh.r = 1f;
mesh.g = 1f;
mesh.b = 1f;
mesh.a = 1f;
// Setting width and height is redundant when setting ParentMesh.
//mesh.Width = masterMesh.Width;
//mesh.Height = masterMesh.Height;
// 3. Link mesh with parent. (SkeletonJson.cs)
mesh.inheritDeform = inheritDeform;
mesh.ParentMesh = parentMesh;
mesh.UpdateUVs();
return mesh;
}
/// <summary>Sets the region (image) of a MeshAttachment</summary>
static public void SetRegion(MeshAttachment attachment, AtlasRegion region, bool updateUVs = true)
{
// (based on AtlasAttachmentLoader.cs)
attachment.RendererObject = region;
attachment.RegionU = region.u;
attachment.RegionV = region.v;
attachment.RegionU2 = region.u2;
attachment.RegionV2 = region.v2;
attachment.RegionRotate = region.rotate;
attachment.regionOffsetX = region.offsetX;
attachment.regionOffsetY = region.offsetY;
attachment.regionWidth = region.width;
attachment.regionHeight = region.height;
attachment.regionOriginalWidth = region.originalWidth;
attachment.regionOriginalHeight = region.originalHeight;
if (updateUVs) attachment.UpdateUVs();
}
}
And here is the hilarious result for the head (same apply everywhere)
Loading Image
Loading Image
*
The texture displayed is the main texture in the skeleton material linked to the skeleton atlas even if i assign a new hair sprite.
Thanks in advance for you help 🙁
EDIT : I'm currently testing this
Stay tuned
EDIT 2 : ok this is not working, the best i can have is to change the material and it changes ALL the textures of my character. The deal si I can't have an atlas but only sprites. I could have a fuckton of assets in the end and we must be able to add or remove content easily. It confirms what i thought : the SetRegion() is done corerctly for UVs, only texture is wrong, and the mainTexture is sued. If i use override texture, it overrides all my meshes of the skeleton. Any ideas?
:bang:
EDIT 3 : I've simplified my function, using the AttachmentTools
void AssignMeshPart(string slotName, Sprite newSprite)
{
AtlasRegion a = newSprite.ToAtlasRegionPMAClone(Shader.Find("Spine/SkeletonGraphic (Premultiply Alpha)"), TextureFormat.RGBA32, false);
MeshAttachment m = skeleton.FindSlot(slotName).Attachment as MeshAttachment;
skeleton.FindSlot(slotName).Attachment = m.GetLinkedMesh("newMesh", a);
}
Same error...
EDIT 4 : I'm trying the new MixAndMatch
EDIT 5 : I've got it working with two parts of skin as a test. I applied the same method to others, I extended Apply() with parameters to allow custom slots and sprites, and KABOOM. ODSS.
Loading Image
void AssignMeshPart(string slotName, string attachmentName, Sprite newSprite)
{
// STEP 0: PREPARE SKINS
// Let's prepare a new skin to be our custom skin with equips/customizations. We get a clone so our original skins are unaffected.
// This requires that all customizations are done with skin placeholders defined in Spine.
//customSkin = customSkin ?? new Skin("custom skin"); // use this if you are not customizing on the default skin and don't plan to remove
// Next let's
customSkin = customSkin ?? skeleton.UnshareSkin(true, false);
var baseSkin = skeleton.Data.FindSkin(baseSkinName);
// STEP 1: "EQUIP" ITEMS USING SPRITES
// STEP 1.1 Find the original attachment.
// Step 1.2 Get a clone of the original attachment.
// Step 1.3 Apply the Sprite image to it.
// Step 1.4 Add the remapped clone to the new custom skin.
// Let's do this for the visor.
int slotIndex = skeleton.FindSlotIndex(slotName); // You can access GetAttachment and SetAttachment via string, but caching the slotIndex is faster.
Attachment baseAttachment = baseSkin.GetAttachment(slotIndex, attachmentName); // STEP 1.1
Attachment newAttachment = baseAttachment.GetRemappedClone(newSprite, sourceMaterial); // STEP 1.2 - 1.3
customSkin.SetAttachment(slotIndex, attachmentName, newAttachment); // STEP 1.4
// customSkin.RemoveAttachment(gunSlotIndex, gunKey); // To remove an item.
//customSkin.Clear();
//Use skin.Clear() To remove all customizations.
// Customizations will fall back to the value in the default skin if it was defined there.
// To prevent fallback from happening, make sure the key is not defined in the default skin.
// STEP 3: APPLY AND CLEAN UP.
// Recommended: REPACK THE CUSTOM SKIN TO MINIMIZE DRAW CALLS
// Repacking requires that you set all source textures/sprites/atlases to be Read/Write enabled in the inspector.
// Combine all the attachment sources into one skin. Usually this means the default skin and the custom skin.
// call Skin.GetRepackedSkin to get a cloned skin with cloned attachments that all use one texture.
// Under the hood, this relies on
if (repack)
{
var repackedSkin = new Skin("repacked skin");
repackedSkin.Append(skeleton.Data.DefaultSkin);
repackedSkin.Append(customSkin);
repackedSkin = repackedSkin.GetRepackedSkin("repacked skin", sourceMaterial, out runtimeMaterial, out runtimeAtlas);
skeleton.SetSkin(repackedSkin);
}
else
{
skeleton.SetSkin(customSkin);
}
skeleton.SetSlotsToSetupPose();
skeletonGraphic.Update(0);
skeletonGraphic.OverrideTexture = runtimeAtlas;
Resources.UnloadUnusedAssets();
}
EDIT 6 : I've found out that repacking is making my skin like that. however I still have the bug...
Loading Image