Joost

I'm trying to combine skins using the C runtime. What I want to do is take a base skin that has the entire body and then replace specific parts with whatever is present in several other skins. So maybe I have an armor skin with arm and torso armor, and a helmet skin with an alternative head. In code I don't know which skin overwrites what, that's up to the artists. All I need to know in code is that a skin overwrites whatever it contains. I found the explanation on Runtime Skins (http://esotericsoftware.com/spine-runtime-skins) and this forum topic (http://esotericsoftware.com/forum/Generating-a-skin-at-runtime-libGDX-9092?p=41209).

However, I can't seem to find several of the functions I need in the C runtime. I might be looking in the wrong place, I don't know. I got it to work, but only by casting to an internal Spine struct and by leaking memory. I suspect that's not as intended. :p

My current approach is this:
void update()
{
combinedSkin = spSkin_create("combined");

// Add elements from base skin
overwriteCombinedSkinAttachments("Normal", combinedSkin);

// Add elements from skins that contain only specific parts
overwriteCombinedSkinAttachments("Mohawk", combinedSkin);
overwriteCombinedSkinAttachments("Moustache01", combinedSkin);

spSkeleton_setSkin(skeleton, combinedSkin);
}


void SpineRenderable::overwriteCombinedSkinAttachments(const char* overwritingSkinName, spSkin* combinedSkin)
{
_spSkin* overwritingSkin = (_spSkin*)(spSkeletonData_findSkin(skeletonData, overwritingSkinName));
_Entry* iter = overwritingSkin->entries;
while (iter != nullptr && iter->attachment != nullptr)
{
spSkin_addAttachment(combinedSkin, iter->slotIndex, iter->name, iter->attachment);
iter = iter->next;
}
}
If I try to delete the skin before creating a new one through spSkin_dispose(combinedSkin) I get a crash. Maybe this also deletes the attachments or something like that? What's the right way to delete a skin I made myself, and is there some way to clear a skin (instead of deleting it every time)?

Also, I'm using the internal struct _spSkin here which works, but I don't think it's as intended. What's the correct way to do this? I couldn't find any other way to iterate over the attachments in a skin.
Joost
Mesajlar: 4

badlogic

Welcome to the wonderful world of skins! As you identified, there's currently no way to iterate over the attachments inside an spSkin apart from casting to the internal _spSkin structure. I just opened an issue for adding the method from this forum post by Nate (Generating a skin at runtime (libGDX)) to all other runtimes: [runtimes] API to add all attachments of a skin to another (empty/new) skin · #990 Until that is implemented, you can continue casting to _spSkin.

spSkin_dispose currently assumes that the skin being disposed owns the attachments it contains. The function will thus not only delete the _Entry structs in the skin, but also call spAttachment_dispose. Any attachments you've added from another skin will thus be disposed and point to invalid memory. I've created an issue to add a function that won't dispose the attachments here: [c] Add spSkin_dipose that does not dispose attachments · #991. You can temporarily use this code:
void spSkin_shallowDispose (spSkin* self) {
_Entry* entry = SUB_CAST(_spSkin, self)->entries;
while (entry) {
_Entry* nextEntry = entry->next;
FREE(entry->name);
FREE(entry);
entry = nextEntry;
}

FREE(self->name);
FREE(self);
}
Kullanıcı avatarı
badlogic

Mario
Mesajlar: 1093

Joost

Thanks for the explanation!

At first your shallowDispose still crashed the game, but it turns out this was because I deleted the old skin before assigning a new one. I saw in the source now that spSkeleton_setSkin starts by doing things with the old skin, which crashes if the old skin has already been deleted. So the solution was to delete the old skin only after the new skin had been created and set.
Joost
Mesajlar: 4

badlogic

Glad it works for you! Follow the issues i linked to on updates to the official API. Thanks for the feedback!
Kullanıcı avatarı
badlogic

Mario
Mesajlar: 1093

Joost

Hmm, I just stumbled into another issue with this: I can only replace items through a skin, not add them.

The setup I'm trying now is that my character has no parts in the "default" skin. The full character is in the "normal" skin and certain parts are replaced by specific skins that I add to it with the code from my starting post. This works fine if the specific skins overwrite elements, but doesn't work if the elements don't exist in the "normal" skin. So for example, if the "normal" skin has hair, then the "mohawk" skin can replace that hair with something different. But if the "normal" skin doesn't have a sword, then I can't add a sword in a skin. This seems odd to me, since looking at the code for spSkin_addAttachment it doesn't seem to check for anything, it just adds stuff and I indeed see the entries in the debugger, but they don't appear in-game.

To give an example, on the below JSON skin descriptions the "Mohawk" skin works but "BroadSword" does not. (Note that I've removed most other elements to keep the example short.)
"bones": [
{ "name": "root" },
{ "name": "Pelvis", "parent": "root", "x": 9.6, "y": 206.69, "color": "80ff00ff" },
{ "name": "Head", "parent": "Chest", "length": 59.21, "rotation": -2.54, "x": 132.88 },
{ "name": "Hair_Back", "parent": "Hair", "length": 130.25, "rotation": 163.97, "x": 7.08, "y": 19.29 },
{ "name": "LWeapon", "parent": "LWrist", "length": 291.39, "rotation": 82.91, "x": 28.52, "y": -23.43 }
],
"slots": [
{ "name": "Hair_Back", "bone": "Hair_Back", "attachment": "Hair_Back" },
{ "name": "LWeapon", "bone": "LWeapon", "attachment": "LWeapon" },
{ "name": "Head", "bone": "Head", "attachment": "Head" }
],
"skins": {
"default": {},
"BroadSword": {
"LWeapon": {
"LWeapon": { "name": "broadsword", "x": 182.54, "y": 7.29, "rotation": 3.02, "width": 405, "height": 77 }
}
},
"Mohawk": {
"Hair_Back": {
"Hair_Back": { "name": "hair_b", "x": 15.38, "y": 1.24, "rotation": 109.3, "width": 131, "height": 177 }
}
},
"Normal": {
"Hair_Back": {
"Hair_Back": { "name": "hair_a_back", "x": 69.25, "y": -10.54, "rotation": 109.3, "width": 111, "height": 162 }
},
"Head": {
"Head": { "name": "Head_neut", "x": 26.33, "y": 3.28, "rotation": -86.73, "width": 68, "height": 71 }
},
},
},
How should I code this to make it work?

---

By the way, an ugly workaround we found is that if we add a weapon texture that is just one empty pixel to the "Normal" skin then it works: since it's transparent it's not visible and the "BroadSword" skin can replace that texture. I do feel a bit dirty now so I'd still like to know what the proper way is to achieve this. :)
Joost
Mesajlar: 4

Nate

Adding an attachment to the skin doesn't set the slot's attachment. The skin is only used by Skeleton setAttachment to find an attachment. After adding an attachment to the skin, you need to set Slot attachment or use Skeleton setAttachment or another way of setting the slot's attachment.

Also note you don't need to use a skin at all, you can just get the slot and set its attachment. The main reason to use a skin is that it provides a layer of indirection to allow animations to key attachment changes while having the actual attachment come from the skin.
Kullanıcı avatarı
Nate

Nate
Mesajlar: 7647

Joost

Okay, thanks for the explanation, I'll give that a try! :)
Joost
Mesajlar: 4


Dön Runtimes