• Bugs
  • Skinning

Related Discussions
...

Im unsure if this is a bug with the runtime(s) or isn't a feature, I am going to assume its a bug.

Basically the skin slots only work if they are switching to/from an image in the placeholder, they can not set the placeholder(s) to null. If a skin has some unique place holders, these placeholders will not be switched to empty.

An example of how this is causing issues is, we have a character who has multiple types of armour the player can customise(buy). these pieces of armour are an addition to the character, they don't replace the characters clothes, they go on top of the clothes. If they player decides to equip shin braces, we turn the skin on. All is working at this point. But then if the player wants to add a chest plate, they skins don't get updated.

To work around this you have to play an animation which turns the desired slots visibility on

Something like this might work, (not all paths are controlled this is just a quick thought)

                spSkin * skin = spSkeletonData_findSkin(skeleton->data,skinName);
([i](spSkin[/i]*)&skeleton->skin) = skin;
for (int i =0; i < skeleton->slotsCount; i++)
{
   spSlot * slot = skeleton->slots[i];
   if (slot->data->attachmentName)
   {
      spAttachment * attachment = spSkin_getAttachment(skin,i,slot->data->attachmentName);
      if(attachment) 
      {
         spSlot_setAttachment(slot,attachment);
      }   
else { spSlot_setAttachment(slot, skeleton->slots[i]->attachment); //Keep all other attachments } } }

I guess if this was to be a thing, you prob would need a "turn off skin" function

You lost me at, "But then if the player wants to add a chest plate, they skins don't get updated." What exactly happens? Do you mean you want to activate multiple skins at the same time?

Basically, if you don't have anything in the placeholder from set up pose, once you change the skin once it wont change skin to fill another empty placeholder, ill try and create a spine project for you.


In the example, in the (c) runtime you can set either skin 1 or skin 2, and it will display the skin fine, but once you do that you can not switch to the other skin. I haven't looked at the code since last week, but IIRC, it is because the placeholder is empty in the current skin it, it decides it doesn't need to switch it out.


see: spSkin_attachAll(...) in skin.c, it calls spSkin_getAttachment(...) which checks all the attachments associated with the skin. And as my example I have attached placeholder: o does not exist in skin: 1 so when you change to skin 2, it will not display skin 2.

it is almost like the function wants to check all the current visible slots in the skeleton and see if it needs changing.

25일 후

Just a tangent.
Be careful with your terms. It might confuse beginners or anyone who will google the term to find out more.

"Skinning" is a term borrowed from 3D modeling and animation (which intersects a lot with Spine).
It's the process of assigning influences to parts of a mesh— or how much each part follows which bone.
To reduce the confusion, Esoteric decided to call it "Weights" in Spine editor.
Under the covers, they're still called Spine.SkinnedMeshAttachment.

We haven't settled on a term for the process of generating new skins from different combinations of images.
We've been calling it "mix-and-match" or "dynamic skins".

I think you're using them normally. without generating new skins at runtime, so this is just called using skins?

BinaryCats wrote

Basically the skin slots only work if they are switching to/from an image in the placeholder, they can not set the placeholder(s) to null. If a skin has some unique place holders, these placeholders will not be switched to empty.

This is also super confusing. What's a skin slot? And what's setting a placeholder to null?

Pharan wrote

We haven't settled on a term for the process of generating new skins from different combinations of images.
We've been calling it "mix-and-match" or "dynamic skins".

I think you're using them normally. without generating new skins at runtime, so this is just called using skins?

BinaryCats wrote

Basically the skin slots only work if they are switching to/from an image in the placeholder, they can not set the placeholder(s) to null. If a skin has some unique place holders, these placeholders will not be switched to empty.

This is also super confusing. What's a skin slot? And what's setting a placeholder to null?

Fistly I believe I could have just said placeholder, but I guess I was thinking something about the default skin (slot with no placeholders) which becomes an issue with the mix and match skins.

Mix Match skins cant are bugged though. They only work if all of the skins alter the same placeholders. For example, if you have 20 costumes, but only one has a tail. once you switch to the costume with the tail, it will never get unloaded. (hence request at the end for a turn off skin

A placeholder is null when there is no image in that placeholder for that skin. And if I recall correctly, the code will not fill that placeholder (because it is empty) when switching to another skin which might fill that placeholder

BinaryCats wrote

In the example, in the (c) runtime you can set either skin 1 or skin 2, and it will display the skin fine, but once you do that you can not switch to the other skin. I haven't looked at the code since last week, but IIRC, it is because the placeholder is empty in the current skin it, it decides it doesn't need to switch it out.

Sorry for the long delay! See the docs for Skin changes:

When a new skin is set and the skeleton already has a skin, then attachments from the new skin are attached if the slot had the attachment from the old skin attached. Otherwise, no attachments are changed.

Setting a skin doesn't necessarily mean that you want to change which attachments are used for each slot. The behavior described above is for convenience. If there is no skin and a skin is set, the most common use case is indeed to change which attachments are used for each slot. However, if a skin has already been set and the skin is changed, we don't know how you want your slot attachments to change, so we only change attachments for a slot if the slot's attachment is from the old skin.

Try calling setSlotsToSetupPose after changing skins. Otherwise you'll need to know what slots you want to have attachments and set them yourself. Eg, to equip a specific sword the player has.

Under the covers, they're still called Spine.SkinnedMeshAttachment.

That's a bit unfortunate isn't it. 🙂 MeshWeightAttachment any better? Maybe we should make this change for v3.x for consistency.

A placeholder is null when there is no image in that placeholder for that skin.

In Spine-the-editor "skin placeholders" are used for setup, but in the runtimes there is no such thing. I think what you mean is that you have a skin which does not contain an attachment for a name.

In the runtimes, a skin has a mapping of name to attachment. Note that name is not required to be the attachment's name. name is actually the name of the skin placeholder in Spine-the-editor. There is also a "default" skin which has all attachments not under a skin placeholder in Spine-the-editor. In the default skin name is the corresponding attachment's name.

Whenever a skeleton needs to find an attachment by name, it asks it's skin. If not found, it asks the default skin. This means the skin provides a look up mechanism to go from a name to which actual attachment is used. This allows animations and code to use the same set of names, but the actual attachments used can be anything you want.


I will prob revisit this thread with a clear head on Monday


I think my main intension was to keep the slots whos default attachments have changed via animation to remain visible. Atm, as you stated, you would have to call setSlotsToSetupPose then set the attachments which have changed, however if you have had a lot of animations change a lot of attachments, there is no reasonable way of doing this - prior to the code above which will keep the old attachment - which changed via animation.

A scaled down version of this scenario is changing weapons, imo and my advice to anybody who has a varity of weapons held in a varity of ways, is to use animation to change which gun is visible- (all guns under one slot, all in set up pose). If the character changes skin, that's fine, I believe he will keep his gun. But if he got into a mouse costume, say, with a tail. and then into a costume with out a skin you would need to call setSlotsToSetupPose then make the correct gun visible again.

Yep, I think that sounds right.

Be sure to not store application state in your skeleton. Eg, if your character has a specific weapon equipped, you might use an animation to make it visible on the skeleton, but somewhere in your app's model it will know which weapon is equipped. This means you can always use this information to hide or show the particular attachments, eg after a call to setSlotsToSetupPose.

I'm just going to get some facts down related to this:

(1) A Skin is a Map/Hashmap/Dictionary of names and attachments.
This means a Skin has a bunch of key-value pairs {attachment name, attachment object}
(More specifically, it's a {{slot index, attachment name}, attachment object}).
The runtimes use this by passing the Skin a string name and a slot id and getting the attachment object in return.

(2) In Spine Editor, you can put a bunch of "skin placeholders" under slots. These correspond to the "attachment name" key inside Skins.
When you add attachments under skin placeholders in a Skin, each will get exported as an entry in their corresponding Skin's Map/Dictionary.

(3) For different Skins in the editor, you can leave some skin placeholders empty.
On export, skin placeholders without attachments are excluded from the Skin's map.

(3.1) If all placeholders are empty, the resulting Skin would also be an empty Map/Dictionary. But empty skins are also not exported.

(4) You can change skins by calling Skeleton.setSkin.
It will not only change what data source the skeleton will pull from to know what attachments to use.
It will also attempt to apply the attachments stored in the skin immediately.

The current Java implementation is as follows (you can find this in Skeleton.java):

public void setSkin (Skin newSkin) {
   if (newSkin != null) {
      if (skin != null)
         newSkin.attachAll(this, skin);
      else {
         Array<Slot> slots = this.slots;
         for (int i = 0, n = slots.size; i < n; i++) {
            Slot slot = slots.get(i);
            String name = slot.data.attachmentName;
            if (name != null) {
               Attachment attachment = newSkin.getAttachment(i, name);
               if (attachment != null) slot.setAttachment(attachment);
            }
         }
      }
   }
   skin = newSkin;
}

String name = slot.data.attachmentName; means that it uses setup pose data, not the current state (slot.attachment?.name), to determine what to attach.

Combined with the above, if (name != null) means it will not change slots that were empty in setup pose.

Attachment attachment = newSkin.getAttachment(i, name); asks the Skin object for an attachment. It may return null, meaning the entry was not there (because empty skin placeholders are not added to the Map/Dictionary).

if (attachment != null) slot.setAttachment(attachment); means attachments are only changed if the entry existed in the skin.

(4.1)
Those key lines are where you could change the logic to get the desired behavior.

For example:
If you want nulling behavior, the if (attachment != null) conditional can be removed. slot.setAttachment will accept a null parameter.

If you want slots that were empty at setup pose to get set to null, you can add an else clause that runs slot.setAttachment(null);. This will however have an effect of nulling slots that were either animated to change or changed programmatically.

If you want to use current state instead of setup pose, you can change:
String name = slot.data.attachmentName; to

Attachment currentAttachment = slot.attachment;
String name = null;
if (currentAttachment != null) {
   name = currentAttachment.getName();
}

(4.2)
Using Skeleton.setSlotsToSetupPose is also an option that uses Setup Pose data and can also null out slots that were animated or changed programmatically. Using it should be fine if your animation system isn't too complicated.

I will relook at how we do things when I get time, maybe I was just making it overly complicated. Or worked around a bug which was unknown to be a bug (in our engine)

I think I kind of wish that it was a list of active skins, instead of just one. Potentially this could cause (minor) performance issues, but I can see lots (ok, maybe a few) of advantages of having active skins in a list.

With a list of active skins, each would need to be queried to find an attachment. By having a single skin, it is always one query (or two if it has to look in the default skin).

Nate wrote

With a list of active skins, each would need to be queried to find an attachment. By having a single skin, it is always one query (or two if it has to look in the default skin).

hence the performance issue, a boy can dream though cant he 😢

Yeah it doesn't seem like much, but it depends on how often attachment lookups are done. We used to do a lookup every frame for an AttachmentTimeline, but it proved to be a performance bottleneck for animations with many of those timelines. Keying many slots on frame zero would cause an attachment lookup for each slot every frame. It would have been a lot worse with a list of skins. We changed AttachmentTimeline to only lookup and set the attachment when the frame with the key is crossed. This has other downsides when it comes to applying multiple animations, so I'm still not super happy with it. Anyway, attachment lookups don't happen all that often any more. Plus a skin list could work since someone worried about performance can always collapse the list into a single skin.

Nate wrote

Yeah it doesn't seem like much, but it depends on how often attachment lookups are done. We used to do a lookup every frame for an AttachmentTimeline, but it proved to be a performance bottleneck for animations with many of those timelines. Keying many slots on frame zero would cause an attachment lookup for each slot every frame. It would have been a lot worse with a list of skins. We changed AttachmentTimeline to only lookup and set the attachment when the frame with the key is crossed. This has other downsides when it comes to applying multiple animations, so I'm still not super happy with it. Anyway, attachment lookups don't happen all that often any more. Plus a skin list could work since someone worried about performance can always collapse the list into a single skin.

there would also be other....intricacies when dealing with a list for instance 'how the skins are layered'. you may have a skin which alters foot and body, and another which alters foot and head. Depending on how you called them these would yield different (foot) results.

maybe the best way would be to start at the end of the list and decrement through it checking if the skin has an image for that placeholder and break out of the loop when it does.


Just a thought


I am correct in thinking at the moment you go through the skeletons slots and apply the correct image for the current skin?

why not go through the (list of) skin(s) slot and change the skeletons slot attachment to the skins attachment for that slot.

or is this just swings and roundabouts?
(some sort of flag might be needed for each slot if list was used)

Yes, the skin order if we used a list would matter. The attachment from the top most skin would be used.

The skin is only used to look up attachments. Each slot has an attachment, but there is no requirement that comes from the skin. Attaching all the attachments in a skin may be what you want sometimes, but not always.

On the subject of "downsides when it comes to applying multiple animations". Don't forget our idea, Nate!

The idea was:
(1) any number of Animation.apply calls get made.
(2) Attachment Timelines should always apply the last key, but apply it by setting the slot's string, not doing an attachment lookup.
(3) skeleton.UpdateWorldTransform (or something like it) should check if the string changed and then only do an attachment lookup if the string changed.

I guess that would mean it does a bunch of string compares for every slot.
So maybe the new setAttachmentName() setter should just apply a dirty flag when it detects a name change? To prevent unnecessary string compares? (Finally, a good reason why we use setters.) XD

Anyway. Carry on with skins topic.

Ah, that's right. Keep reminding until it gets done! 😃