Dmitriy Sechin 77e2e34e8f
Handle skeleton loading problems without crashes (#2276)
This pull request improve the loading of binary skeleton so that there is no crash in case of attachment problems. 
Essentially, it will have the same behavior as for JSON skeleton, just returning NULL, and you can print pSkeleton->error to the output for the debug purpose.

The functions spSkeletonBinary_create() and spSkeletonJson_create() both use spAtlasAttachmentLoader, which creates attachments in its _spAtlasAttachmentLoader_createAttachment(). 
This function can return NULL in case of any problems with the atlas regions also setting internal error1,error2 to "Region not found: ", region_path

In case of SkeletonJson.c, there is a check for attachment != NULL after calling spAttachmentLoader_createAttachment(). 
However, unfortunately, there is no such check for SkeletonBinary.c, and if the region is not found, there is simply a crash deep inside the spine lib.

I fixed it like this

Currently, all calls to spAttachmentLoader_createAttachment() in SkeletonBinary.c are located inside the spSkeletonBinary_readAttachment() function, which is convenient. It is possible to check for the validity of the attachment after each _createAttachment() call. We can use
	if (!attachment)
		return NULL;

In fact, the problem with the attachment can only be in three cases inside spSkeletonBinary_readAttachment()
case SP_ATTACHMENT_REGION:
case SP_ATTACHMENT_MESH:
case SP_ATTACHMENT_LINKED_MESH:

But for the sake of consistency, it may be worth checking the other four cases:
SP_ATTACHMENT_BOUNDING_BOX:
SP_ATTACHMENT_PATH:
SP_ATTACHMENT_POINT:
SP_ATTACHMENT_CLIPPING:
(I do attachment check here, but maybe we can just "trust" that the attachment is always valid in this cases).

Now that spSkeletonBinary_readAttachment() can return NULL instead of crashing, so go on and fix the next function - spSkeletonBinary_readSkin()
Here, we also check the attachment for validity in this code 'for (i = 0; i < slotCount; ++i)'. 
Unfortunately, we cannot use 'continue' inside the loop here, as it is done in SkeletonJson.c, as we cannot move 'input->cursor' to the beginning of the data for the next slot. Here, we can only exit using return NULL, which generally suits us

And finally, in the function spSkeletonBinary_readSkeletonData(), there are only two calls to spSkeletonBinary_readSkin() - one for the default skin and another inside the loop for other skins. 
Here, you can apply a check 'if(self->attachmentLoader->error1)', and in case of problems, clear the data by spSkeletonData_dispose() and exit with return NULL; similarly to how it is done below for errors "Skin not found: " and "Parent mesh not found: ".

Thus, there will be no crash inside spine lib in case of attachment problems. Instead, NULL will be returned when calling spSkeletonBinary_readSkeletonData(), which the game engine can recognize and output the error message from pSkeleton->error in the output. 
This will help understand why my wonderful spine animation is not loading.
2023-04-24 09:39:05 +02:00
..
2016-04-04 22:16:24 +02:00
2013-03-28 04:22:39 +01:00
2018-05-24 14:31:26 +02:00
2021-10-14 00:47:13 +02:00

spine-c

The spine-c runtime provides basic functionality to load and manipulate Spine skeletal animation data using ANSI C89. It does not perform rendering but can be extended to enable Spine animations for other C-based projects, including C++ or Objective-C projects.

Licensing

You are welcome to evaluate the Spine Runtimes and the examples we provide in this repository free of charge.

You can integrate the Spine Runtimes into your software free of charge, but users of your software must have their own Spine license. Please make your users aware of this requirement! This option is often chosen by those making development tools, such as an SDK, game toolkit, or software library.

In order to distribute your software containing the Spine Runtimes to others that don't have a Spine license, you need a Spine license at the time of integration. Then you can distribute your software containing the Spine Runtimes however you like, provided others don't modify it or use it to create new software. If others want to do that, they'll need their own Spine license.

For the official legal terms governing the Spine Runtimes, please read the Spine Runtimes License Agreement and Section 2 of the Spine Editor License Agreement.

Spine version

spine-c works with data exported from Spine 4.1.xx.

spine-c supports all Spine features.

Setup

  1. Download the Spine Runtimes source using git or by downloading it as a zip via the download button above.
  2. Copy the contents of the spine-c/spine-c/src and spine-c/spine-c/include directories into your project. Be sure your header search is configured to find the contents of the spine-c/spine-c/include directory. Note that the includes use spine/Xxx.h, so the spine directory cannot be omitted when copying the files.

If SPINE_SHORT_NAMES is defined, the sp prefix for all structs and functions is optional. Only use this if the spine-c names won't cause a conflict.

Usage

Please see the spine-c guide for full documentation

Extension

Extending spine-c requires implementing three methods:

  • _spAtlasPage_createTexture Loads a texture and stores it and its size in the void* rendererObject, width and height fields of an spAtlasPage struct.
  • _spAtlasPage_disposeTexture Disposes of a texture loaded with _spAtlasPage_createTexture.
  • _spUtil_readFile Reads a file. If this doesn't need to be customized, _readFile is provided which reads a file using fopen.

With these implemented, the spine-c API can then be used to load Spine animation data. Rendering is done by enumerating the slots for a skeleton and rendering the attachment for each slot. Each attachment has a rendererObject field that is set when the attachment is loaded.

For example, AtlasAttachmentLoader is typically used to load attachments when using a Spine texture atlas. When AtlasAttachmentLoader loads a RegionAttachment, the attachment's void* rendererObject is set to an AtlasRegion. Rendering code can then obtain the AtlasRegion from the attachment, get the AtlasPage it belongs to, and get the page's void* rendererObject. This is the renderer specific texture object set by _spAtlasPage_createTexture. Attachment loading can be customized if not using AtlasAttachmentLoader or to provider different renderer specific data.

spine-sfml serves as a simple example of extending spine-c.

spine-c uses an OOP style of programming where each "class" is made up of a struct and a number of functions prefixed with the struct name. More detals about how this works are available in extension.h. This mechanism allows you to provide your own implementations for spAttachmentLoader, spAttachment and spTimeline, if necessary.

Runtimes extending spine-c