Ver código fonte

[workflows] Fix dart format check workflow - use && instead of &

Mario Zechner 1 mês atrás
pai
commit
86f888b1fc

+ 1 - 1
.github/workflows/format-check-dart.yml

@@ -25,4 +25,4 @@ jobs:
     - name: Check for changes
       run: |
         git ls-files -m
-        if [[ `git ls-files -m` ]]; then echo "Detected Dart formatting errors!" & exit 1; fi
+        if [[ `git ls-files -m` ]]; then echo "Detected Dart formatting errors!" && exit 1; fi

+ 1011 - 0
spine-cpp/docs.md

@@ -0,0 +1,1011 @@
+# spine-c Runtime Documentation
+
+> **Licensing**
+>
+> Please see the [Spine Runtimes License](/spine-runtimes-license) before integrating the Spine Runtimes into your applications.
+
+# Introduction
+spine-c is a generic runtime for integrating Spine animations in game engines and frameworks written in C, C++, Swift, Rust, Objective-C or any other language that can natively interface with C.
+
+spine-c provides functionality to
+
+* [Load Spine skeletons](/spine-loading-skeleton-data) exported to JSON or binary files.
+* [Load texture atlases](/spine-loading-skeleton-data) storing the images used by the Spine skeletons.
+* Manage and apply [skins](/spine-runtime-skins) to the skeleton.
+* Manage and apply [animations](/spine-applying-animations) to the skeleton.
+* Manipulate and compute data required for rendering and physics based on the current [skeleton pose, slots & attachments states](/spine-runtime-skeletons).
+
+As the spine-c runtime is a generic, engine independent runtime, users are required to implement a set of functions to provide engine specific file i/o and image loading to the spine-c runtime,  render the data generated by the spine-c runtime via the engine's rendering system, and optionally integrate the data with the engine's physics system if more advanced use cases such as rag dolls are required.
+
+The spine-c runtime is written using C99 to guarantee compatibility with a wide range of platforms and compilers.
+
+Other official Spine runtimes are based on spine-c and can serve as examples for integration with your engine of choice:
+
+* [spine-sfml](/git/spine-runtimes/tree/spine-sfml/c), a runtime for [SFML](http://www.sfml-dev.org/).
+* [spine-sdl](/git/spine-runtimes/tree/spine-sdl/), a runtime for [SDL](https://www.libsdl.org/).
+
+The following sections give a brief, engine independent overview of the spine-c runtime and how to use it. Most of the official Spine runtimes based on spine-c will encapsulate (parts of) the spine-c API in their own, easier to use API. It is still beneficial to understand the basics of the underlying spine-c runtime.
+
+> **Note:** This guide assumes you are familiar with the basic [runtime architecture](/spine-runtime-architecture) and terminology used by Spine. Please also consult the [API reference](/spine-api-reference) to explore more advanced functions of the runtime.
+
+# Exporting Spine assets for spine-c
+![](/img/spine-runtimes-guide/spine-ue4/export.png)
+Please follow the instructions in the Spine User Guide on how to
+
+1. [Export skeleton & animation data](/spine-export) to JSON or our binary format
+2. [Export texture atlases containing the images of your skeleton](/spine-texture-packer)
+
+An export of the skeleton data and texture atlas of your skeleton will yield the following files:
+
+![](/img/spine-runtimes-guide/spine-ue4/exported-files.png)
+
+1. `skeleton-name.json` or `skeleton-name.skel`, containing your skeleton and animation data.
+2. `skeleton-name.atlas`, containing information about the texture atlas.
+3. One or more `.png` files, each representing one page of your texture atlas containing the packed images your skeleton uses.
+
+> **Note:** Instead of creating one texture atlas per skeleton, you may also pack the images of multiple skeletons into a single texture atlas. Please refer to the [texture packing guide](/spine-texture-packer).
+
+# Loading Spine assets
+spine-c provides APIs to load texture atlases, Spine skeleton data (bones, slots, attachments, skins, animations) and define mix times between animations through [animation state data](/spine-applying-animations#Mix-times). These three types of data, also known as setup pose data, are generally loaded once and then shared by every game object. The sharing mechanism is achieved by giving each game object its own skeleton and [animation state](/spine-applying-animations#AnimationState-API), also known as instance data.
+
+> **Note:** For a more detailed description of the overall loading architecture consult the generic [Spine Runtime Documentation](/spine-loading-skeleton-data).
+
+## Loading texture atlases
+Texture atlas data is stored in a custom [atlas format](/spine-atlas-format) that describes the location of individual images within atlas pages. The atlas pages themselves are stored as plain `.png` files next to the atlas.
+
+spine-c provides the functions [spAtlas_createFromFile](/git/spine-runtimes/blob/spine-c/spine-c/src/spine/Atlas.c#L301) and [spAtlas_create](/git/spine-runtimes/blob/spine-c/spine-c/src/spine/Atlas.c#L167) for this task. The former loads the atlas from a file, the latter reads the atlas from memory. If loading the atlas failed, the functions return 0.
+
+```
+// Load the atlas from a file. The last argument is a void* that will be
+// stored in atlas->rendererObject.
+spAtlas* atlas = spAtlas_createFromFile("myatlas.atlas", 0);
+
+// Load the atlas from memory, giving the memory location, the number
+// of bytes and the directory relative to which atlas page textures
+// should be loaded. The last argument is a void* that will be stored
+// in atlas-rendererObject.
+spAtlas* atlas = spAtlas_create(atlasInMemory, atlasDataLengthInBytes, dir, 0);
+```
+
+## Loading skeleton data
+Skeleton data (bones, slots, attachments, skins, animations) can be exported to human readable [JSON](/spine-json-format) or a custom [binary format](/spine-binary-format). spine-c stores skeleton data in `spSkeletonData` structs.
+
+For loading the skeleton data from a JSON export, we create a `spSkeletonJson` which takes the previously loaded `spAtlas`, set the scale of the skeleton and finally read the skeleton data from the file:
+
+```
+// Create a spSkeletonJson used for loading and set the scale
+// to make the loaded data two times as big as the original data
+spSkeletonJson* json = spSkeletonJson_create(atlas);
+json->scale = 2;
+
+// Load the skeleton .json file into a spSkeletonData
+spSkeletonData* skeletonData = spSkeletonJson_readSkeletonDataFile(json, filename);
+
+// If loading failed, print the error and exit the app
+if (!skeletonData) {
+	printf("%s\n", json->error);
+	spSkeletonJson_dispose(json);
+	exit(0);
+}
+
+// Dispose the spSkeletonJson as we no longer need it after loading.
+spSkeletonJson_dispose(json);
+```
+
+Loading skeleton data from a binary export works the same, except we use a `spSkeletonBinary` instead:
+
+```
+// Create a spSkeletonJson used for loading and set the scale
+// to make the loaded data two times as big as the original data
+spSkeletonBinary* binary = spSkeletonBinary_create(atlas);
+binary->scale = 2;
+
+// Load the skeleton .skel file into a spSkeletonData
+spSkeletonData* skeletonData = spSkeletonBinary_readSkeletonDataFile(binary, filename);
+
+// If loading failed, print the error and exit the app
+if (!skeletonData) {
+	printf("%s\n", binary->error);
+	spSkeletonBinary_dispose(json);
+	exit(0);
+}
+
+// Dispose the spSkeletonBinary as we no longer need it after loading.
+SkeletonBinary_dispose(json);
+```
+
+## Preparing animation state data
+Spine supports smooth transitions (crossfades) when switching from one animation to another. The crossfades are achieved by mixing one animation with another for a specific mix time. The spine-c runtime provides the `spAnimationStateData` struct to define these mix times:
+
+```
+// Create the spAnimationStateData
+spAnimationStateData* animationStateData = spAnimationStateData_create(skeletonData);
+
+// Set the default mix time between any pair of animations in seconds.
+spAnimationStateData_setDefaultMix(animationStateData, 0.1f);
+
+// Set the mix time between from the "jump" to the "walk" animation to 0.2 seconds,
+// overwriting the default mix time for this from/to pair.
+spAnimationStateData_setMixByName(animationStateData, "jump", "walk", 0.2f);
+```
+
+The mix times defined in `spAnimationStateData` can also be overwritten explicitly when applying animations (see below).
+
+# Skeletons
+Setup pose data (skeleton data, texture atlases) are supposed to be shared between game objects. spine-c provides the `spSkeleton` struct to facilitate this sharing. Every game object receives its own instance of `spSkeleton` which in turn references an `spSkeletonData` and `spAtlas` instance as data sources.
+
+The `spSkeleton` can be freely modified, e.g. by procedurally modifying bones, applying animations or setting attachments and skins specific to a game object, while the underlying skeleton data and texture atlas stay in tact. This mechanism and setup allow sharing `spSkeletonData` and `spAtlas` instances by any amount of game objects.
+
+## Creating skeletons
+To create a `spSkeleton` instance, call `spSkeleton_create`:
+
+```
+spSkeleton* skeleton = spSkeleton_create(skeletonData);
+```
+
+Every game object will need its own `spSkeleton`. The bulk of the data remains in `spSkeletonData` and `spAtlas` and will be shared by all `spSkeleton` instances to vastly reduce memory consumption and texture switches. The life-time of an `spSkeleton` is thus coupled with the life-time of its corresponding game object.
+
+## Bones
+A skeleton is a hierarchy of bones, with slots attached to bones, and attachments attached to slots.
+
+### Finding bones
+All bones in a skeleton have a unique name by which they can be fetched from the skeleton:
+
+```
+// returns 0 if no bone of that name could be found
+spBone* bone = spSkeleton_findBone("mybone");
+```
+
+### Local transform
+A bone is affected by its parent bone, all the way back to the root bone. E.g. when rotating a bone, all its child bones and all their children are also rotated. To accomplish these hierarchical transformations, each bone has a local transformation relative to its parent bone consisting of:
+
+* `x` and `y` coordinates relative to the parent.
+* `rotation` in degrees.
+* `scaleX` and `scaleY`.
+* `shearX` and `shearY` in degrees.
+
+The local transform of a bone can be manipulated procedurally or via applying an animation. The former allows to implement dynamic behavior like having a bone point at a mouse cursor, let feet bones follow the terrain etc. Both procedural modification of the local transform as well as applying animations can be done simultaneously. The end result will be a single combined local transform.
+
+### World transform
+Once all the local transforms are setup, either through procedurally modifying the local transforms of bones or by applying animations, we need the world transform of each bone for rendering and physics.
+
+The calculation starts at the root bone, and then recursively calculates all child bone world transforms. The calculation also applies [IK](/spine-ik-constraints), [transform](/spine-transform-constraints) and [path](/spine-path-constraints) constraints defined by the artist in the Spine editor.
+
+To calculate the world transforms, we need to first update the skeleton's frame time for physics, followed by calculating the actual transforms:
+
+```
+spSkeleton_update(deltaTime);
+spSkeleton_updateWorldTransform(skeleton, SP_PHYSICS_UPDATE);
+```
+
+`deltaTime` specifies the time passed between the current and last frame, given in seconds. The second parameter to `spSkeleton_updateWorldTransform` specifies if and how physics should be applied. `SP_PHYSICS_UPDATE` is a good default value.
+
+The result is stored on each bone, and consists of:
+
+* `a`, `b`, `c`, and `d`, a 2x2 column major matrix encoding rotation, scale and shear of the bone.
+* `worldX`, `worldY`, storing the world position of the bone.
+
+Note that `worldX` and `worldY` are offset by `skeleton->x` and `skeleton->y`. These two fields can be used to position the skeleton in your game engine's world coordinate system.
+
+In general, the bones world transforms should never be modified directly. Instead, they should always be derived from the local transforms of the bones in the skeleton by calling `spSkeleton_updateWorldTransform`. The local transforms can be set either procedurally, e.g. setting the rotation of a bone so it points to the mouse cursor, or by applying animations (see below), or both. Once the (procedural) animation is applied, `spSkeleton_updateWorldTransform` is called and the resulting world transforms are recalculated based on the local transform as well as any constraints that are applied to bones.
+
+### Converting between coordinate systems
+It is often easier to manipulate bones in the world coordinate system, as this is where coordinates from other entities or input events are usually given. However, since the world transform should not be directly manipulated, we need to apply any changes to a bone based on world coordinate system calculations to the local transform of that bone.
+
+The spine-c runtimes provides functions to extract rotation and scale information from the 2x2 world transform matrix of a bone, and transform locations and rotations from local space to world space and vice versa. All these functions assume that the bones' world transforms have been calculated before by calling `spSkeleton_updateWorldTransform`:
+
+```
+spBone* bone = spSkeleton_findBone("mybone");
+
+// Get the rotation of a bone in world space relative to the world space x-axis in degrees
+float rotationX = spBone_getWorldRotationX(bone);
+
+// Get the rotation of a bone in world space relative to the world space y-axis in degrees
+float rotationY = spBone_getWorldRotationY(bone);
+
+// Get the scale of a bone in world space relative to the world space x-axis
+float scaleX = spBone_getWorldScaleX(bone);
+
+// Get the scale of a bone in world space relative to the world space y-axis
+float scaleY = spBone_getWorldScaleY(bone);
+
+// Transform a position given in world space to a bone's local space
+float localX = 0, localY = 0;
+spBone_worldToLocal(bone, worldX, worldY, &localX, &localY);
+
+// Transform a position given in a bone's local space  to world space
+float worldX = 0, worldY = 0;
+spBone_localToWorld(bone, localX, localY, &worldX, &worldY);
+
+// Transform a rotation given in the bone's world transform relative to the world space x-axis to // a rotation given in local space relative to the local space x-axis in degrees.
+float localRotationX = spBone_worldToLocalRotationX(bone)
+
+// Transform a rotation given in the bone's world transform relative to the world space y-axis to // a rotation given in local space relative to the local space y-axis in degrees.
+float localRotationY = spBone_worldToLocalRotationY(bone)
+```
+
+> **Note:** Your modifications to the local transform of a bone (and thereby all its children) will be reflect in the bone's world transform after the next call to `spSkeleton_updateWorldTransform`.
+
+## Positioning
+By default, a skeleton is assumed to be at the origin of the game's world coordinate system. To position a skeleton in a game's world coordinate system, you can use the `x` and `y` field:
+
+```
+// make a skeleton follow a game object in world space
+skeleton->x = myGameObject->worldX;
+skeleton->y = myGameObject->worldY;
+```
+
+> **Note:** Your modifications to the skeleton `x` and `y` fields will be reflect in the bone world transforms after the next call to `spSkeleton_updateWorldTransform`.
+
+## Flipping
+A skeleton can be flipped vertically or horizontally. This allows reusing animations made for one direction for the opposing direction, or for working in coordinate systems with the y-axis pointing downwards (Spine assumes y-axis up by default):
+
+```
+// flip vertically around the x-axis
+skeleton->scaleY = -1;
+
+// flip horizontally around the y-axis
+skeleton->scaleX = -1;
+```
+
+> **Note:** Your modifications to the skeleton `scaleX` and `scaleY` fields will be reflected in the bone world transforms after the next call to `spSkeleton_updateWorldTransform`.
+
+## Setting skins
+The artist creating the Spine skeleton may have added multiple [skins](/spine-runtime-skins) to the skeleton to add visual variations of the same skeleton, e.g. a female and male version. The spine-c runtime stores skins in instances of `spSkin`.
+
+A [skin at runtime](/spine-runtime-skins) is a map defining which [attachment](/spine-basic-concepts#Attachments) goes into which [slot](/spine-basic-concepts#Slots) of the skeleton. Every skeleton has at least one skin which defines which attachment is on what slot in the skeleton's setup pose. Additional skins have a name to identify them.
+
+Setting a skin on a skeleton via spine-c:
+
+```
+// set a skin by name
+spSkeleton_setSkin(skeleton, "my_skin_name");
+
+// set the default setup pose skin by passing 0
+spSkeleton_setSkin(skeleton, 0);
+```
+
+> **Note:** Setting a skin takes into account what skin and hence which attachments have previously been set. Please refer to the generic runtime guide for [more information on setting skins](/spine-runtime-skins#Skin-changes).
+
+## Setting attachments
+spine-c allows setting a single attachment on a skeleton's slot directly, e.g. to switch out weapons. The attachment is first searched in the active skin, and if this fails, in the default setup pose skin:
+
+```
+// Set the attachment called "sword" on the "hand" slot
+spSkeleton_setAttachment(skeleton, "hand", "sword");
+
+// Clear the attachment on the slot "hand" so nothing is shown
+spSkeleton_setAttachment(skeleton, "hand", 0);
+```
+
+### Tinting
+You can tint all attachments in a skeleton by setting the skeleton's color:
+
+```
+spSkeleton* skeleton = ...
+
+// tint all attachments with red and make the skeleton half translucent.
+skeleton->r = 1.0f;
+skeleton->g = 0.0f;
+skeleton->b = 0.0f;
+skeleton->a = 0.5f;
+```
+
+> **Note:** Colors in spine-c are given as RGBA, with values for each channel in the range [0-1].
+
+When rendering a skeleton, the renderer walks though the draw order of slots on the skeleton and renders the currently active attachment on each slot. In addition to the skeleton's color, every slot also has a color which can be manipulated at runtime:
+
+```
+spSlot* slot = skeleton->findSlotByName("mySlot");
+slot->r = 0.0f;
+slot->g = 1.0f;
+slot->b = 0.0f;
+slot->a = 1.0f;
+```
+
+Note that slot colors can also be animated. If you manually change a slot's color and then apply an animation that keys that slot's color, your manual change will be overwritten.
+
+# Applying animations
+The Spine editor lets artists create multiple, uniquely named [animations](/spine-animating). An animation is a set of [timelines](/spine-api-reference#Timeline). Each timeline specifies at what frame what property of a bone or the skeleton should change to what value. There are many different types of timelines, from timelines defining the transform of a bone over time, to timelines that change the drawing order. Timelines are part of the skeleton data and stored in `spSkeletonData` in spine-c.
+
+## Timeline API
+spine-c provides a [timeline API](/spine-applying-animations#Timeline-API) should the need arise to directly work with timelines. This low-level functionality allows you to fully customize the way animations defined by your artist are applied to a skeleton.
+
+## Animation state API
+In almost all cases, you should use the [animation state API](/spine-applying-animations#AnimationState-API) instead of the timeline API. The animation state API makes task such as applying animations over time, queueing animations, mixing between animations, and applying multiple animations at the same time considerably easier than the low-level timeline API. The animation state API uses the timeline API internally and can thus be seen as a wrapper.
+
+spine-c represents an animation state via the `spAnimationState` struct. Just like skeletons, an `spAnimationState` is instantiated per game object. In general, you will have one `spSkeleton` and one `spAnimationState` instance per game object in your game. And just like `spSkeleton`, the `spAnimationState` will share `spSkeletonData` (wherein animations and their timelines are stored) and `spAnimationStateData` (wherein mix times are stored) with all other `spAnimationState` instances sourcing the same skeleton data.
+
+### Creating animation states
+To create an `spAnimationState`instance, call `spAnimationState_create`:
+
+```
+spAnimationState* animationState = spAnimationState_create(animationStateData);
+```
+
+The function takes an `spAnimationStateData` which is usually created when the skeleton data is loaded, and which defines the default mix time as well as mix times between specific animations for [crossfades](/spine-applying-animations#Mix-times).
+
+### Tracks & Queueing
+An animation state manages one or more [tracks](/spine-applying-animations#Tracks). Each track is a list of animations that should be played back in the order they were added to the track. This is known as [queuing](/spine-applying-animations#Queuing). Tracks are indexed starting from 0.
+
+You can queue an animation on a track like this:
+
+```
+// Add the animation "walk" to track 0, without delay, and let it loop indefinitely
+int track = 0;
+int loop = 1;
+float delay = 0;
+spAnimationState_addAnimationByName(animationState, track, "walk", loop, delay);
+```
+
+You can queue multiple animations at once as a fire and forget way to create animation sequences:
+
+```
+// Start walking (note the looping)
+spAnimationState_addAnimationByName(animationState, 0, "walk", 1, 0);
+
+// Jump after 3 seconds
+spAnimationState_addAnimationByName(animationState, 0, "jump", 0, 3);
+
+// Once we are done jumping, idle indefinitely
+spAnimationState_addAnimationByName(animationState, 0, "idle", 1, 0);
+```
+
+You can also clear all animations queued in a track:
+
+```
+// Clear all animations queued on track 0
+spAnimationState_clearTrack(animationState, 0);
+
+// Clear all animations queued on all tracks
+spAnimationState_clearTracks(animationState);
+```
+
+Instead of clearing and adding a new animation to a track, you can call `spAnimationState_setAnimationByName`. This will clear all tracks, but remember what the last played back animation was before clearing and crossfade to the newly set animation. This way you can smoothly transition from one animation sequence to the next. You can add more animations to the track after calling `spAnimationState_setAnimationByName` by calling `spAnimationState_addAnimationByName`:
+
+```
+// Whatever is currently playing on track 0, clear the track and crossfade
+// to the "shot" animation, which should not be looped (last parameter).
+spAnimationState_setAnimationByName(animationState, 0, "shot", 0);
+
+// After shooting, we want to idle again
+spAnimationState_addAnimationByName(animationState, 0, "idle", 1, 0);
+```
+
+To crossfade to the setup pose of the skeleton from an animation, you can use `spAnimationState_setEmptyAnimation`, `spAnimationState_addEmptyAnimation`, where the former clears the current track and crossfades to the skeleton, and the later enqueues a crossfade to the setup pose as part of the animation sequence on the track:
+
+```
+// Whatever is currently playing on track 0, clear the track and crossfade
+// to the setup pose for 0.5 seconds (mix time) with a delay of 1 second.
+spAnimationState_setEmptyAnimation(animationState, 0, 0.5f, 1);
+
+// Add a crossfade to the setup pose for 0.5 seconds as part of the animation
+// sequence in track 0
+spAnimationState_addEmptyAnimation(animationState, 0, 0.5f)
+```
+
+For simple games, using a single track is usually enough to achieve your goals. More complex games may want to queue animations on separate tracks, e.g. to simultaneously play back a walk animation while shooting. This is where the real power of Spine comes into play:
+
+```
+// Apply the "walk" animation on track 0 indefinitely.
+spAnimationState_setAnimationByName(animationState, 0, "walk", 1);
+
+// Simultaneously apply a "shot" animation on track 1 once.
+spAnimationState_setAnimationByName(animationState, 1, "shot", 0);
+```
+
+Note that if you apply animations simultaneously like that, animations on the higher track will overwrite animations on the lower track for every value both animations have keyed. For authoring animations, this means to make sure two animations to be played back simultaneously don't key the same values in the skeleton, e.g. the same bone, attachment, color etc.
+
+You can control the mixing of animations on different track via [track entries](/spine-applying-animations#TrackEntry)
+
+### Track Entries
+Every time you enqueue an animation on a track of an animation state, the corresponding functions will return an `spTrackEntry` instance. This track entry allows you to further customize both the queued animation, as well as its mixing behaviour with regards to animations on the same or other tracks. See the [TrackEntry documentation](/spine-api-reference#TrackEntry) for a full list of fields you can modify.
+
+As an example, lets assume the mix time between a "walk" and "run" animation defined in an `spAnimationStateData` is to high for this specific game object in its current situation. You can modify the mix time between "walk" and "run" ad-hoc, just for this specific queued animation:
+
+```
+// Walk indefinitely
+spAnimationState_setAnimationByName(animationState, 0, "walk", 1);
+
+// At some point, queue the run animation. We want to speed up the mixing
+// between "walk" and "run" defined in the `spAnimationStateData` (let's say 0.5 seconds)
+// for this one specific call to be faster (0.1 seconds).
+spTrackEntry* entry = spAnimationState_addAnimationByName(animationState, 0, "run", 1, 0);
+entry->mixDuration = 0.1f;
+```
+
+You can hold on to the `spTrackEntry` to modify it over time. The `spTrackEntry` will be valid for as long as the animation is queued on that track. Once the animation is completed, the `spTrackEntry` will be deallocated. Any subsequent access will be invalid and likely result in a segfault. You can register a listener to get notified when the animation and hence the track entry are no longer valid.
+
+### Events
+An animation state generates events while playing back queued animations to notify a listener about changes:
+
+* An animation **started**.
+* An animation was **interrupted**, e.g. by clearing a track.
+* An animation was **completed**, which may occur multiple times if looped.
+* An animation has **ended**, either due to interruption or it has completed and is not looped.
+* An animation and its corresponding `spTrackEntry` have been **disposed** and are no longer valid.
+* A [user defined **event**](/spine-events) was fired.
+
+You can listen for these events by registering a function either with the animation state, or with individual `spTrackEntry` instances returned by the animation state.
+
+```
+// Define the function that will be called when an event happens
+void myListener(spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event) {
+	switch (type) {
+	//
+	case SP_ANIMATION_START:
+		printf("Animation %s started on track %i\n", entry->animation->data->name, entry->trackIndex);
+		break;
+	case SP_ANIMATION_INTERRUPT:
+		printf("Animation %s interrupted on track %i\n", entry->animation->data->name, entry->trackIndex);
+		break;
+	case SP_ANIMATION_END:
+		printf("Animation %s ended on track %i\n", entry->animation->data->name, entry->trackIndex);
+		break;
+	case SP_ANIMATION_COMPLETE:
+		printf("Animation %s completed on track %i\n", entry->animation->data->name, entry->trackIndex);
+		break;
+	case SP_ANIMATION_DISPOSE:
+		printf("Track entry for animation %s disposed on track %i\n", entry->animation->data->name, entry->trackIndex);
+		break;
+	case SP_ANIMATION_EVENT:
+		printf("User defined event for animation %s on track %i\n", entry->animation->data->name, entry->trackIndex);
+		printf("Event: %s: %d, %f, %s\n", event->data->name, event->intValue, event->floatValue, event->stringValue);
+		break;
+	default:
+		printf("Unknown event type: %i", type);
+	}
+}
+
+// Register the function as a listener on the animation state. It will be called for all
+// animations queued on the animation state.
+animationState->listener = myListener;
+
+// Or you can register the function as a listener for events for a specific animation you enqueued
+spTrackEntry* trackEntry = spAnimationState_setAnimationByName(animationState, 0, "walk", 1);
+trackEntry->listener = myListener;
+```
+
+User defined events are perfect to markup times in an animation at which sounds should be played back, e.g. foot steps.
+
+Changes made to the animation state within a listener, such as setting a new animation, are not applied to skeletons until the next time `spAnimationState_apply` is called. You can immediately apply the changes within the listener:
+
+```
+void myListener(spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event) {
+	if (somecondition) {
+		spAnimationState_setAnimation(state, 0, "run", 0);
+		spAnimationState_update(0);
+		spAnimationState_apply(skeleton);
+	}
+}
+```
+
+### Applying animation states
+Animation states are inherently time-based. To advance their state, you will need to update them every tick, supplying the amount of time that has passed since the last update in seconds:
+
+```
+spAnimationState_update(deltaTime);
+```
+
+This will advance the playback of animations on each track, coordinate crossfades and call any listeners you might have registered.
+
+After updating the animation state, you generally want to apply it to a skeleton to update its bones local transforms, attachments, slot colors, draw order and anything else that can be animated:
+
+```
+spAnimationState_apply(skeleton);
+```
+
+With the skeleton posed and animated, you finally update its frame time and bones' world transforms to prepare it for rendering or physics:
+
+```
+spSkeleton_update(deltaTime);
+spSkeleton_updateWorldTransform(skeleton);
+```
+
+# Memory management
+We have tried to make spine-c memory as straight forward as possible. Any struct that is allocated via `spStructName_create` needs to be deallocated with the corresponding `spStructName_dispose`. The life-times of structs depends on what type of struct it is. General rules of thumb:
+
+* Create setup pose data shared by instance data (`spAtlas`, `spSkeletonData`, `spAnimationStateData`) at game or level startup, dispose it at game or level end.
+* Create instance data (`spSkeleton`, `spAnimationState`) when the corresponding game object is created, dispose it when the corresponding game object is destroyed.
+
+Track entries (`spTrackEntry`) are valid from a call to one of the enqueuing animation state functions (`spAnimationState_setAnimationByName`, `spAnimationState_addAnimationByName`, `spAnimationState_setEmptyAnimation`, `spAnimationState_addEmptyAnimation`) until the `SP_ANIMATION_DISPOSE` event is send to your listener. Accessing the track entry after this event will likely result in a segmentation fault.
+
+When creating structs, you often pass in other structs as references. The referencing struct will never dispose the referenced struct. E.g. an `spSkeleton` references an `spSkeletonData` which in turn references an `spAtlas`.
+
+* Disposing the `spSkeleton` will not dispose the `spSkeletonData` nor the `spAtlas`. This makes sense, as the `spSkeletonData` is likely shared by other `spSkeleton`instances.
+* Disposing the `spSkeletonData` will not dispose the `spAtlas`. This also makes sense, as the `spAtlas` may be shared by other `spSkeletonData` instances, e.g. if the atlas contains the images of multiple skeletons.
+
+If you use a custom allocator, you can overwrite Spine's allocation strategy (using `malloc`, `realloc` and `free`, by changing the defines in [extension.h](/git/spine-runtimes/blob/spine-c/spine-c/include/spine/extension.h#L65-L67).
+
+# Putting it all together
+Here's a simplified example of how to put all the above together, from loading and instantiating to applying animations (scroll to see all the code):
+
+```
+// Setup pose data, shared by all skeletons
+spAtlas* atlas;
+spSkeletonData* skeletonData;
+spAnimationStateData* animationStateData;
+
+// 5 skeleton instances and their animation states
+spSkeleton* skeleton[5];
+spAnimationState* animationState[5];
+char* animationNames[] = { "walk", "run", "shot" };
+
+void setup() {
+	// setup your engine so textures can be loaded for atlases, create a window, etc.
+	engine_setup();
+
+	// Load the texture atlas
+	atlas = spAtlas_createFromFile("spineboy.atlas", 0);
+	if (!atlas) {
+		printf("Failed to load atlas");
+		exit(0);
+	}
+
+	// Load the skeleton data
+	spSkeletonJson* json = spSkeletonJson_create(atlas);
+	skeletonData = spSkeletonJson_readSkeletonDataFile(json, "spineboy.json");
+	if (!skeletonData) {
+		printf("Failed to load skeleton data");
+		spAtlas_dispose(atlas);
+		exit(0);
+	}
+	spSkeletonJson_dispose(json);
+
+	// Setup mix times
+	animationStateData = spAnimationStateData_create(skeletonData);
+	animationStateData->defaultMix = 0.5f;
+	spAnimationStateData_setMixByName("walk", "run", 0.2f);
+	spAnimationStateData_setMixByName("walk", "shot", 0.1f);
+}
+
+void mainLoop() {
+	// Create 5 skeleton instances and animation states
+	// representing 5 game objects
+	for (int i = 0; i < 5; i++) {
+		// Create the skeleton and put it at a random position
+		spSkeleton* skeleton = spSkeleton_create(skeletonData);
+		skeleton->x = random(0, 200);
+		skeleton->y = random(0, 200);
+
+		// Create the animation state and enqueue a random animation, looping
+		spAnimationState animationState = spAnimationState_create(animationStateData);
+		spAnimationState_setAnimation(animationState, 0, animationNames[random(0, 3)], 1);
+	}
+
+	while (engine_gameIsRunning()) {
+		engine_clearScreen();
+
+		// update the game objects
+		for (int i = 0; i < 5; i++) {
+			spSkeleton* skeleton = skeletons[i];
+			spAnimationState* animationState = animationStates[i];
+
+			// First update the animation state by the delta time
+			spAnimationState_update(animationState, engine_getDeltaTime());
+
+			// Next, apply the state to the skeleton
+			spAnimationState_apply(animationState, skeleton);
+
+         // Update the skeleton's frame time for physics
+         spSkeleton_update(engine_getDeltaTime());
+
+			// Calculate world transforms for rendering
+			spSkeleton_updateWorldTransform(skeleton, SP_PHYSICS_UPDATE);
+
+			// Hand off rendering the skeleton to the engine
+			engine_drawSkeleton(skeleton);
+		}
+	}
+
+	// Dispose of the instance data. Normally you'd do this when
+	// a game object is disposed.
+	for (int i = 0; i < 5) {
+		spSkeleton_dispose(skeleton);
+		spAnimationState_dispose(animationState);
+	}
+}
+
+void dispose() {
+	// dispose all the shared resources
+	spAtlas_dispose(atlas);
+	spSkeletonData_dispose(skeletonData);
+	spAnimationStateData_dispose(animationStateData);
+}
+
+int main(int argc, char* argv) {
+	setup();
+	mainLoop();
+	dispose();
+}
+```
+
+Note the distinction of setup pose data (`spAtlas`, `spSkeletonData`, `spAnimationStateData`) and instance data (`spSkeleton`, `spAnimationState`) and their different life-times.
+
+# Integrating spine-c in your engine
+## Integrating the sources
+spine-c is a set a of C header and implementation files found in the [spine-c/spine-c](/git/spine-runtimes/tree/spine-c/spine-c) folder of the runtime Git repository. You can either copy the sources into your project, or use CMake's `FetchContent`
+
+### Copy sources
+1. Clone the [Spine runtime repository](https://github.com/EsotericSoftware/spine-runtimes). Use the [version branch](/spine-versioning) corresponding with your Spine Editor branch.
+2. Copy the sources from the [spine-c/spine-c/src/spine](/git/spine-runtimes/tree/spine-c/spine-c/src/spine) folder to the source folder of your project and make sure they are part of the compilation step of your project.
+3. copy the folder `spine` containing the headers from the [spine-c/spine-c/include](/git/spine-runtimes/tree/spine-c/spine-c/include/spine) folder to the header folder of your project and make sure they are part of the include path the compiler uses to look up headers. Make sure to keep the `spine` folder as the spine-c sources include headers via `#include "spine/xxx.h"`.
+
+### CMake `FetchContent`
+Starting with Spine version 4.2, you can also use CMake's `FetchContent` feature to easily integrate the spine-c runtime in your project, as illustrated by this example `CMakeLists.txt` file.
+
+```
+cmake_minimum_required(VERSION 3.14)
+project(MyProject C)
+
+set(CMAKE_C_STANDARD 99)
+set(CMAKE_C_STANDARD_REQUIRED ON)
+set(FETCHCONTENT_QUIET NO)
+
+# Fetch the spine-runtimes repository and make the spine-c library available
+include(FetchContent)
+FetchContent_Declare(
+  spine-runtimes
+  GIT_REPOSITORY https://github.com/esotericsoftware/spine-runtimes.git
+  GIT_TAG 4.2
+  GIT_SHALLOW TRUE
+)
+FetchContent_MakeAvailable(spine-runtimes)
+FetchContent_GetProperties(spine-runtimes)
+if(NOT spine-runtimes_POPULATED)
+    FetchContent_Populate(spine-runtimes)
+endif()
+add_subdirectory(${spine-runtimes_SOURCE_DIR}/spine-c ${CMAKE_BINARY_DIR}/spine-runtimes)
+
+
+# Create a simple C executable
+file(GLOB SOURCES "src/*.c")
+add_executable(MyExecutable ${SOURCES})
+target_include_directories(MyExecutable PRIVATE src/)
+
+# Link the spine-c library
+target_link_libraries(MyExecutable spine-c)
+
+```
+
+## Implementing extension functions
+If you compile your project you will receive linker errors for the functions the spine-c runtime expects you to implement. E.g. compiling with Clang will yield something like:
+
+```
+Undefined symbols for architecture x86_64:
+  "__spAtlasPage_createTexture", referenced from:
+      _spAtlas_create in libspine-c.a(Atlas.c.o)
+  "__spAtlasPage_disposeTexture", referenced from:
+      _spAtlasPage_dispose in libspine-c.a(Atlas.c.o)
+  "__spUtil_readFile", referenced from:
+      _spAtlas_createFromFile in libspine-c.a(Atlas.c.o)
+      _spSkeletonBinary_readSkeletonDataFile in libspine-c.a(SkeletonBinary.c.o)
+      _spSkeletonJson_readSkeletonDataFile in libspine-c.a(SkeletonJson.c.o)
+ld: symbol(s) not found for architecture x86_64
+```
+
+The 3 functions the linker can not find are called extension functions. The spine-c runtime expects you to implement these via APIs provided by your engine. The extension functions are defined in [extension.h](/git/spine-runtimes/blob/spine-c/spine-c/include/spine/extension.h#L144-L150).
+
+> **Note:** if you are using any of the official Spine runtimes that are based on the spine-c runtime, these functions plus rendering are already implemented for you. You can ignore this section.
+
+### Implementing _spUtil_readFile
+spine-c uses the `_spUtil_readFile` function to read the full content [JSON and binary skeleton files (.json/.skel)](/spine-export) exported from the Spine editor, as well as [texture atlas files (.atlas)](/spine-texture-packer#Texture-Packing) into memory. Use the "Find usages" or equivalent function of your C/C++ IDE to see where `_spUtil_readFile` is called.
+
+The signature of `_spUtil_readFile` is as follows:
+
+```
+char* _spUtil_readFile (const char* path, int* length);
+```
+
+The function takes a UTF-8 path to the file it should read, as well as a pointer to an `int` in which the function will store the number of bytes read from the file. The function returns `0` in case the file could not be read or a pointer to a memory block the function allocated and read all bytes of the file into.
+
+Code that calls `_spUtil_readFile` is supposed to deallocate the returned memory once it is done processing the data. All code in spine-c that uses this function will make sure the memory is deallocated so you do not have to worry about it.
+
+The memory is supposed to be allocated with the [MALLOC or CALLOC macros](/git/spine-runtimes/blob/spine-c/spine-c/include/spine/extension.h#L65-L66) defined in `extension.h`. The spine-c code will then deallocate the memory using the [FREE](/git/spine-runtimes/blob/spine-c/spine-c/include/spine/extension.h#L85) macro. You can redefine `MALLOC`, `CALLOC` and `FREE` to use a custom memory allocation scheme throughout the spine-c runtime.
+
+The simplest implementation of `_spUtil_readFile` looks like this:
+
+```
+char* _spUtil_readFile (const char* path, int* length){
+	return _readFile(path, length);
+}
+```
+
+It uses a function called `_readFile` which is provided by spine-c in [extension.c](/git/spine-runtimes/blob/spine-c/spine-c/src/spine/extension.c#L64). `_readFile` uses `fopen` to read the file relative to the current working directory.
+
+If your engine uses a more sophisticated way of managing files, e.g. by packing them into a single compressed file with an internal file structure, you can implement `_spUtil_readFile` using your engine's file i/o API.
+
+### Implementing _spAtlasPage_createTexture and _spAtlasPage_disposeTexture
+spine-c uses the `_spAtlasPage_createTexture` function to load and create an engine specific texture representation for a single page of a [texture atlas](/spine-texture-packer) exported for a Spine skeleton by the Spine editor. The function is called as part of [spAtlas_create](/git/spine-runtimes/blob/spine-c/spine-c/src/spine/Atlas.c#L167) and [spAtlas_createFromFile](/git/spine-runtimes/blob/spine-c/spine-c/src/spine/Atlas.c#L301), the code responsible for reading a Spine texture atlas file (.atlas) and the corresponding image files (usually .png) making up the pages of the atlas.
+
+The signature of `_spAtlasPage_createTexture` is as follows:
+
+```
+void _spAtlasPage_createTexture (spAtlasPage* self, const char* path);
+```
+
+The function is supposed to load the atlas page image file to an engine specific texture and store it on the `spAtlasPage` passed to the function. The `spAtlasPage` structure has a special field called `rendererObject` of type `void*` in which the engine specific texture should be stored. This engine specific texture stored in `rendererObject` is then later used to render a Spine skeleton using the engine APIs.
+
+The function is also supposed to set the width and height of the `spAtlasPage` in pixels according to the texture file loaded by the engine. This data is required to compute texture coordinates by spine-c.
+
+The `path` parameter is the path to the page image file, relative to the `.atlas` file path passed to [spAtlas_createFromFile](/git/spine-runtimes/blob/spine-c/spine-c/src/spine/Atlas.c#L301) or relative to the `dir` parameter passed to [spAtlas_create](/git/spine-runtimes/blob/spine-c/spine-c/src/spine/Atlas.c#L167). These two functions are used to load a texture atlas from a file or a memory block respectively.
+
+For the purpose of illustration, assume your engine provides the following API to work with textures:
+
+```
+typedef struct Texture {
+	// ... OpenGL handle, image data, whatever ...
+	int width;
+	int height;
+} Texture;
+
+Texture* engine_loadTexture(const char* file);
+void engine_disposeTexture(Texture* texture);
+```
+
+Implementing `_spAtlasPage_createTexture` is then as simple as:
+
+```
+void _spAtlasPage_createTexture (AtlasPage* self, const char* path){
+	Texture* texture = engine_loadTexture(path);
+
+	// if texture loading failed, self->rendererObject, self->width and
+	// self->height remain 0 and we simply return.
+	if (!texture) return;
+
+	// store the Texture on the rendererObject so we can
+	// retrieve it later for rendering.
+	self->rendererObject = texture;
+
+	// store the texture width and height on the spAtlasPage
+	// so spine-c can calculate texture coordinates for
+	// rendering.
+	self->width = texture->width;
+	self->height = texture->height;
+}
+```
+
+The second function that needs to be implemented is `_spAtlasPage_disposeTexture`. It is responsible for disposing the texture of an `spAtlasPage` when the corresponding `spAtlas` is disposed via a call to [spAtlas_dispose](/git/spine-runtimes/blob/spine-c/spine-c/src/spine/Atlas.c#L327). The signature of `_spAtlasPage_disposeTexture` is as follows:
+
+```
+void _spAtlasPage_disposeTexture (spAtlasPage* self);
+```
+
+Given our assumed engine API, an implementation would look like this:
+
+```
+void _spAtlasPage_disposeTexture (spAtlasPage* self) {
+	// if the rendererObject is not set, loading failed
+	// so we do not need to dispose of anything.
+	if (!self->rendererObject) return;
+
+	// Dispose the texture
+	Texture* texture = (Texture*)self->rendererObject;
+	engine_disposeTexture(texture);
+}
+```
+
+## Implementing Rendering
+Rendering a Spine skeleton means rendering all currently active attachments in the current [draw order](/spine-basic-concepts#Slots). The draw order is defined as an [array of slots](/git/spine-runtimes/blob/spine-c/spine-c/include/spine/Skeleton.h#L54) on the skeleton.
+
+Drawable attachments ([regions](/spine-regions), [(deformable) meshes](/spine-meshes)) define UV mapped, vertex colored, triangle meshes. The [rendererObject](/git/spine-runtimes/blob/spine-c/spine-c/include/spine/RegionAttachment.h#L52) on a drawable attachment stores a reference to the texture atlas region to which the triangles of the attachment mesh are mapped to.
+
+Assuming you already have animated your skeleton, either procedurally or via an animation state, and that you have updated the skeleton bones' world transform via a call to `spSkeleton_updateWorldTransform`, you can render a skeleton as follows:
+
+* For each slot in the draw order array of the skeleton
+  * Fetch the currently active attachment from the slot (can be null if no attachment is active)
+  * Fetch the blend mode from the slot and translate it to your engine's API
+  * Calculate the tint color based on the skeleton's and slot's color
+  * Check the type of the attachment
+    * If it is a region attachment
+       * Compute its world vertices by calling `spRegionAttachment_computeWorldVertices`
+       * Fetch the atlas page texture from the attachment's render object
+       * Calculate the tint color of the attachment by multiplying the skeleton, slot, and attachment color, given as RGBA in the range [0-1] per channel
+       * Combine the world space vertices, UVs and color into a triangle mesh
+       * Bind the texture through your engine's API
+       * Submit the mesh for rendering
+    * If it is a mesh attachment
+       * Compute its world vertices by calling `spVertexAttachment_computeWorldVertices`
+       * Fetch the atlas page texture from the attachment's render object
+       * Calculate the tint color of the attachment by multiplying the skeleton, slot, and attachment color, given as RGBA in the range [0-1] per channel
+       * Combine the world space vertices, UVs and color into a triangle mesh
+       * Bind the texture through your engine's API
+       * Submit the mesh for rendering
+
+Translating this to your engine should be trivial, provided your engine allows rendering of UV mapped, vertex colored triangle meshes. For illustration purposes, let's assume the following engine API:
+
+```
+// A single vertex with UV
+typedef struct Vertex {
+	// Position in x/y plane
+	float x, y;
+
+	// UV coordinates
+	float u, v;
+
+	// Color, each channel in the range from 0-1
+	// (Should really be a 32-bit RGBA packed color)
+	float r, g, b, a;
+} Vertex;
+
+enum BlendMode {
+	// See http://esotericsoftware.com/git/spine-runtimes/blob/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/BlendMode.java#L37
+	// for how these translate to OpenGL source/destination blend modes.
+	BLEND_NORMAL,
+	BLEND_ADDITIVE,
+	BLEND_MULTIPLY,
+	BLEND_SCREEN,
+}
+
+// Draw the given mesh.
+// - vertices is a pointer to an array of Vertex structures
+// - start defines from which vertex in the vertices array to start
+// - count defines how many vertices to use for rendering (should be divisible by 3, as we render triangles, each triangle requiring 3 vertices)
+// - texture the texture to use
+// - blendMode the blend mode to use
+void engine_drawMesh(Vertex* vertices, int start, int count, Texture* texture, BlendMode blendmode);
+```
+
+The rendering process can then be implemented like this:
+
+```
+#define MAX_VERTICES_PER_ATTACHMENT 2048
+float worldVerticesPositions[MAX_VERTICES_PER_ATTACHMENT];
+Vertex vertices[MAX_VERTICES_PER_ATTACHMENT];
+
+// Little helper function to add a vertex to the scratch buffer. Index will be increased
+// by one after a call to this function.
+void addVertex(float x, float y, float u, float v, float r, float g, float b, float a, int* index) {
+	Vertex* vertex = &vertices[*index];
+	vertex->x = x;
+	vertex->y = y;
+	vertex->u = u;
+	vertex->v = v;
+	vertex->r = r;
+	vertex->g = g;
+	vertex->b = b;
+	vertex->a = a;
+	*index += 1;
+}
+
+void drawSkeleton(spSkeleton* skeleton) {
+	// For each slot in the draw order array of the skeleton
+	for (int i = 0; i < skeleton->slotsCount; ++i) {
+		spSlot* slot = skeleton->drawOrder[i];
+
+		// Fetch the currently active attachment, continue
+		// with the next slot in the draw order if no
+		// attachment is active on the slot
+		spAttachment* attachment = slot->attachment;
+		if (!attachment) continue;
+
+		// Fetch the blend mode from the slot and
+		// translate it to the engine blend mode
+		BlendMode engineBlendMode;
+		switch (slot->data->blendMode) {
+			case SP_BLEND_MODE_NORMAL:
+				engineBlendMode = BLEND_NORMAL;
+				break;
+			case SP_BLEND_MODE_ADDITIVE:
+				engineBlendMode = BLEND_ADDITIVE;
+				break;
+			case SP_BLEND_MODE_MULTIPLY:
+				engineBlendMode = BLEND_MULTIPLY;
+				break;
+			case SP_BLEND_MODE_SCREEN:
+				engineBlendMode = BLEND_SCREEN;
+				break;
+			default:
+				// unknown Spine blend mode, fall back to
+				// normal blend mode
+				engineBlendMode = BLEND_NORMAL;
+		}
+
+		// Fill the vertices array depending on the type of attachment
+		Texture* texture = 0;
+		int vertexIndex = 0;
+		if (attachment->type == ATTACHMENT_REGION) {
+			// Cast to an spRegionAttachment so we can get the rendererObject
+			// and compute the world vertices
+			spRegionAttachment* regionAttachment = (spRegionAttachment*)attachment;
+
+			// Calculate the tinting color based on the skeleton's color
+			// and the slot's color. Each color channel is given in the
+			// range [0-1], you may have to multiply by 255 and cast to
+			// and int if your engine uses integer ranges for color channels.
+			float tintR = skeleton->color.r * slot->color.r * regionAttachment.color.r;
+			float tintG = skeleton->color.g * slot->color.g * regionAttachment.color.g;
+			float tintB = skeleton->color.b * slot->color.b * regionAttachment.color.b;
+			float tintA = skeleton->color.a * slot->color.a * regionAttachment.color.a;
+
+			// Computed the world vertices positions for the 4 vertices that make up
+			// the rectangular region attachment. This assumes the world transform of the
+			// bone to which the slot (and hence attachment) is attached has been calculated
+			// before rendering via spSkeleton_updateWorldTransform
+			spRegionAttachment_computeWorldVertices(regionAttachment, slot->bone, worldVerticesPositions, 0, 2);
+
+			// Our engine specific Texture is stored in the spAtlasRegion which was
+			// assigned to the attachment on load. It represents the texture atlas
+			// page that contains the image the region attachment is mapped to
+			texture = (Texture*)((spAtlasRegion*)regionAttachment->rendererObject)->page->rendererObject;
+
+			// Create 2 triangles, with 3 vertices each from the region's
+			// world vertex positions and its UV coordinates (in the range [0-1]).
+			addVertex(worldVerticesPositions[0], worldVerticesPositions[1],
+					  regionAttachment->uvs[0], regionAttachment->uvs[1],
+					  tintR, tintG, tintB, tintA, &vertexIndex);
+
+			addVertex(worldVerticesPositions[2], worldVerticesPositions[3],
+					  regionAttachment->uvs[2], regionAttachment->uvs[3],
+					  tintR, tintG, tintB, tintA, &vertexIndex);
+
+			addVertex(worldVerticesPositions[4], worldVerticesPositions[5],
+					  regionAttachment->uvs[4], regionAttachment->uvs[5],
+					  tintR, tintG, tintB, tintA, &vertexIndex);
+
+			addVertex(worldVerticesPositions[4], worldVerticesPositions[5],
+					  regionAttachment->uvs[4], regionAttachment->uvs[5],
+					  tintR, tintG, tintB, tintA, &vertexIndex);
+
+			addVertex(worldVerticesPositions[6], worldVerticesPositions[7],
+					  regionAttachment->uvs[6], regionAttachment->uvs[7],
+					  tintR, tintG, tintB, tintA, &vertexIndex);
+
+			addVertex(worldVerticesPositions[0], worldVerticesPositions[1],
+					  regionAttachment->uvs[0], regionAttachment->uvs[1],
+					  tintR, tintG, tintB, tintA, &vertexIndex);
+		} else if (attachment->type == ATTACHMENT_MESH) {
+			// Cast to an spMeshAttachment so we can get the rendererObject
+			// and compute the world vertices
+			spMeshAttachment* mesh = (spMeshAttachment*)attachment;
+
+			// Calculate the tinting color based on the skeleton's color
+			// and the slot's color. Each color channel is given in the
+			// range [0-1], you may have to multiply by 255 and cast to
+			// and int if your engine uses integer ranges for color channels.
+			float tintR = skeleton->color.r * slot->color.r * regionAttachment.color.r;
+			float tintG = skeleton->color.g * slot->color.g * regionAttachment.color.g;
+			float tintB = skeleton->color.b * slot->color.b * regionAttachment.color.b;
+			float tintA = skeleton->color.a * slot->color.a * regionAttachment.color.a;
+
+			// Check the number of vertices in the mesh attachment. If it is bigger
+			// than our scratch buffer, we don't render the mesh. We do this here
+			// for simplicity, in production you want to reallocate the scratch buffer
+			// to fit the mesh.
+			if (mesh->super.worldVerticesLength > MAX_VERTICES_PER_ATTACHMENT) continue;
+
+			// Computed the world vertices positions for the vertices that make up
+			// the mesh attachment. This assumes the world transform of the
+			// bone to which the slot (and hence attachment) is attached has been calculated
+			// before rendering via spSkeleton_updateWorldTransform
+			spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, worldVerticesPositions, 0, 2);
+
+			// Our engine specific Texture is stored in the spAtlasRegion which was
+			// assigned to the attachment on load. It represents the texture atlas
+			// page that contains the image the mesh attachment is mapped to
+			texture = (Texture*)((spAtlasRegion*)mesh->rendererObject)->page->rendererObject;
+
+			// Mesh attachments use an array of vertices, and an array of indices to define which
+			// 3 vertices make up each triangle. We loop through all triangle indices
+			// and simply emit a vertex for each triangle's vertex.
+			for (int i = 0; i < mesh->trianglesCount; ++i) {
+				int index = mesh->triangles[i] << 1;
+				addVertex(worldVerticesPositions[index], worldVerticesPositions[index + 1],
+						  mesh->uvs[index], mesh->uvs[index + 1],
+						  tintR, tintG, tintB, tintA, &vertexIndex);
+			}
+
+		}
+
+		// Draw the mesh we created for the attachment
+		engine_drawMesh(vertices, 0, vertexIndex, texture, engineBlendMode);
+	}
+}
+```
+
+This naive implementation will get you up and running quickly. However, there are a couple of low-hanging fruit in terms of optimization:
+
+* `engine_drawMesh` is assumed to submit the mesh for rendering immediately. This means we issue one draw call per attachment on the skeleton. A production grade implementation should batch all the meshes into a single mesh. Ideally, if all attachments use the same texture atlas page and blend mode, drawing a skeleton will only incur a single draw call. If all skeletons in a scene share the same texture atlas page and blend mode, you can even batch all skeletons into a single mesh, and hence a single draw call.
+* The current setup does not use indexed meshes, but instead submits 3 vertices per triangle.
+* The current setup does not support two color tinting.
+* The current setup does not support clipping. See the [spine-sfml](https://github.com/EsotericSoftware/spine-runtimes/blob/4.0/spine-sfml/c/src/spine/spine-sfml.cpp#L293) runtime for an example on how to implement clipping.

+ 595 - 0
spine-flutter/docs-new.md

@@ -0,0 +1,595 @@
+# spine-flutter Runtime Documentation
+
+> **Licensing**
+>
+> Please see the [Spine Runtimes License](/spine-runtimes-license) before integrating the Spine Runtimes into your applications.
+
+# Getting Started
+The spine-flutter runtime is implemented as a [Flutter FFI plugin](https://docs.flutter.dev/development/packages-and-plugins/developing-packages#plugin-ffi) on top of [spine-cpp](/spine-cpp). It supports all platforms supported by Flutter (desktop, Android, iOS, web), and supports all Spine features except tint black and screen blend mode.
+
+
+## Installation
+spine-flutter is supported from Flutter 3.16.0 onwards. To use spine-flutter in your Flutter project, add the following dependency to your project's `pubspec.yaml` file:
+
+```yaml
+dependencies:
+  ...
+  spine_flutter: ^4.3.0
+```
+
+See [spine_flutter on pub.dev](https://pub.dev/packages/spine_flutter) for the latest version.
+
+Ensure that the `major.minor` version of spine-flutter matches the `major.minor` Spine Editor version you are exporting from. See [Spine Versioning](/spine-versioning#Synchronizing-versions) for more information.
+
+In your `main()` function, add these two lines in the beginning to initialize the spine-flutter runtime:
+
+```dart
+void main() async {
+    await initSpineFlutter(enableMemoryDebugging: false);
+    ...
+}
+```
+
+> **Note:** the `main()` method must be `async`.
+
+## Samples
+The spine-flutter runtime includes several samples that showcase its feature set.
+
+You can run the example project following these steps:
+
+1. Install the [Flutter SDK](https://docs.flutter.dev/get-started/install), then run `flutter doctor` which will instruct you what other dependencies to install.
+2. Clone the spine-runtimes repository: `git clone https://github.com/esotericsoftware/spine-runtimes`
+3. Run the `setup.sh` script in the `spine-flutter/` folder. On Windows, you can use [Git Bash](https://gitforwindows.org/) included in Git for Window to run the `setup.sh` Bash script.
+
+You can then open `spine-flutter` in an IDE or editor of your choice that supports Flutter, like [IntelliJ IDEA/Android Studio](https://docs.flutter.dev/get-started/editor?tab=androidstudio) or [Visual Studio Code](https://docs.flutter.dev/get-started/editor?tab=vscode) to inspect and run the example.
+
+Alternatively, you can run the example from the [command line](https://docs.flutter.dev/get-started/test-drive?tab=terminal).
+
+The example project contains the following examples:
+* [`example/lib/simple_animation.dart`](/git/spine-runtimes/spine-flutter/example/lib/simple_animation.dart): demonstrates the basic use of `SpineWidget` and `SpineWidgetController` to load an exported Spine skeleton, display it in the widget, and playback a specific animation.
+* [`example/lib/pause_play_animation.dart`](/git/spine-runtimes/spine-flutter/example/lib/pause_play_animation.dart): demonstrates how to pause and resume an animation.
+* [`example/lib/animation_state_events.dart`](/git/spine-runtimes/spine-flutter/example/lib/animation_state_events.dart): demonstrates how set a slot's color, how to queue multiple animations, and how to to listen for animation state events.
+* [`example/lib/debug_rendering.dart`](/git/spine-runtimes/spine-flutter/example/lib/debug_rendering.dart): shows how to perform custom drawing on top of the rendered skeleton via the `SpineWidgetController` `onAfterPaint` callback.
+* [`example/lib/dress_up.dart`](/git/spine-runtimes/spine-flutter/example/lib/dress_up.dart): demonstrates Spine's skins feature as well as rendering a skeleton to a thumbnail for use in a character creation UI.
+* [`example/lib/ik_following.dart`](/git/spine-runtimes/spine-flutter/example/lib/ik_following.dart): demonstrates how to let the user drag one of the skeleton's bones via mouse or touch input.
+* [`example/lib/physics.dart`](/git/spine-runtimes/spine-flutter/example/lib/physics.dart): demonstrates physics constraints in action.
+* [`example/lib/animated_login.dart`](/git/spine-runtimes/spine-flutter/example/lib/animated_login.dart): shows how to integrate spine animations into a login form UI.
+* [`example/lib/flame_example.dart`](/git/spine-runtimes/spine-flutter/example/lib/flame_example.dart): demonstrates how to write a simple [Flame](https://flame-engine.org/) component to use spine-flutter with the Flame game engine.
+
+## Updating the spine-flutter Runtime
+Before updating your project's spine-flutter runtime, please consult our [guide on Spine editor and runtime version management](/spine-runtime-architecture#Versioning).
+
+To update the spine-flutter runtime, simply modify the version string of the `spine_flutter` package in your `pubspec.yaml`.
+
+> **Note:** If you change the `major.minor` version of the `spine_flutter` package, you have to re-export your Spine skeletons with the same Spine Editor `major.minor` version!
+
+# Using spine-flutter
+The spine-flutter runtime is an idiomatic [Dart FFI wrapper](https://dart.dev/guides/libraries/c-interop) around the generic [spine-cpp](/spine-cpp) which supports loading, playback and manipulation of animations created with Spine. The spine-flutter runtime exposes almost all of the spine-cpp API as idiomatic Dart and provides Flutter and [Flame](https://flame-engine.org/) specific classes to easily display and interact with Spine skeletons.
+
+The spine-flutter runtime supports all Spine features except tint black and screen blend mode.
+
+## Asset Management
+### Exporting for spine-flutter
+![](/img/spine-runtimes-guide/spine-ue4/export.png)
+Please follow the instructions in the Spine User Guide on how to
+
+1. [Export skeleton & animation data](/spine-export)
+2. [Export texture atlases containing the images of your skeleton](/spine-texture-packer)
+
+An export of the skeleton data and texture atlas of your skeleton will yield the following files:
+
+![](/img/spine-runtimes-guide/spine-ue4/exported-files.png)
+
+1. `skeleton-name.json` or `skeleton-name.skel`, containing your skeleton and animation data.
+2. `skeleton-name.atlas`, containing information about the texture atlas.
+3. One or more `.png` files, each representing on page of your texture atlas containing the packed images your skeleton uses.
+
+> **Note**: You should prefer binary skeleton exports over JSON exports, as they are smaller in size and faster to load.
+
+The files can be loaded via spine-flutter classes like `AtlasFlutter`, `SkeletonDataFlutter`, `SkeletonDrawableFlutter`, `SpineWidget`.
+
+> **Note**: The spine-flutter runtime currently does not support atlases exported using pre-multiplied alpha due to technical limitations in Flutter. Flutter's rendering engine ensures that common non-premultiplied alpha artifacts are avoided.
+
+### Updating Spine Assets
+During development, you may frequently update your Spine skeleton data and texture atlas files. You can simply overwrite these source files (`.json`, `.skel`, `.atlas`, `.png`) by re-exporting from the Spine Editor and replacing the existing files in your Flutter project.
+
+Ensure that the `major.minor` version of spine-flutter matches the `major.minor` Spine Editor version you are exporting from. See [Spine Versioning](/spine-versioning#Synchronizing-versions) for more information.
+
+## Core classes
+The spine-flutter API is built on top of the generic [spine-cpp](/spine-cpp) runtime, which provides platform independent  core classes and algorithms to load, query, modify, and animate Spine skeletons. The core classes are wrapped via Dart FFI and exposed as idiomatic Dart classes.
+
+Here, we will briefly discuss the most important core classes that you will  encounter in your day-to-day use of spine-flutter. Please consult the [Spine Runtimes Guide](/spine-runtimes-guide) for a detailed overview of the Spine Runtimes architecture, core classes, and API usage.
+
+### spine-dart classes
+
+The [`Atlas`](/git/spine-runtimes/spine-flutter/lib/generated/atlas.dart) class stores the data loaded from an `.atlas` file. This is the base class used internally by spine-flutter.
+
+The [`SkeletonData`](/git/spine-runtimes/spine-flutter/lib/generated/skeleton_data.dart) class stores the data loaded from a `.json` or `.skel` skeleton file.  The skeleton data contains information about the bone hierarchy, slots, attachments, constraints, skins, and animations. A `SkeletonData` instance is usually loaded by also providing an `Atlas` from which it sources the images to be used by the skeleton it represents. It serves as a blueprint for creating `Skeleton` instances. Multiple skeletons can be instantiated from the same atlas and skeleton data, which then share the loaded data, minimizing both load times and memory consumption at runtime.
+
+The [`Skeleton`](/git/spine-runtimes/spine-flutter/lib/generated/skeleton.dart) class stores an instance of a skeleton, created from a `SkeletonData` instance. A skeleton stores its current pose, that is the position of bones and the current configuration of slots, attachments, and active skin. The current pose can be computed by either manually modifying the bone hierarchy, or, more commonly, by applying animations via an `AnimationState`.
+
+The [`AnimationState`](/git/spine-runtimes/spine-flutter/lib/generated/animation_state.dart) class is responsible for keeping track of which animation(s) should be applied to a skeleton, advancing and mixing those animations based on the elapsed time between the last and current rendering frame, and applying the animations to a skeleton instance, thereby setting its current pose. The `AnimationState` queries an [`AnimationStateData`](/git/spine-runtimes/spine-flutter/lib/generated/animation_state_data.dart) instance to retrieve mixing times between animations, or fetches the default mix time if no mixing time is available for a pair of animations.
+
+### Flutter-specific classes
+
+spine-flutter provides Flutter-specific wrapper classes that handle texture loading, rendering, and lifecycle management:
+
+The [`AtlasFlutter`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L28) class extends the base `Atlas` class and additionally manages Flutter `Image` objects and `Paint` objects for each atlas page and blend mode. It provides static methods to load atlases from assets, files, or URLs:
+
+```dart
+final atlas = await AtlasFlutter.fromAsset("assets/skeleton.atlas");
+```
+
+The [`SkeletonDataFlutter`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L122) class extends the base `SkeletonData` class and provides convenient loading methods that work with `AtlasFlutter`:
+
+```dart
+final skeletonData = await SkeletonDataFlutter.fromAsset(atlas, "assets/skeleton.skel");
+```
+
+The [`SkeletonDrawableFlutter`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L304) class extends the base `SkeletonDrawable` and provides Flutter-specific rendering capabilities, including the ability to render to `Canvas`, `PictureRecorder`, PNG, or raw image data.
+
+The spine-flutter runtime builds on top of these core classes.
+
+## SpineWidget
+![/img/spine-runtimes-guide/spine-flutter/simple-animation.png](/img/spine-runtimes-guide/spine-flutter/simple-animation.png)
+
+A [`SpineWidget`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L261) is a [StatefulWidget](https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html) responsible for loading and displaying a Spine skeleton. At a minimum, the widget needs to know from where to load the skeleton and atlas files, and it must receive a `SpineWidgetController` instance that is responsible for modifying the state of the widget, such as setting an animation, or changing the skin of the skeleton.
+
+In the simplest case, a `SpineWidget` can be instantiated inside another widget's `build()` method like this:
+
+```dart
+@override
+Widget build(BuildContext context) {
+    final controller = SpineWidgetController(onInitialized: (controller) {
+      // Set the walk animation on track 0, let it loop
+      controller.animationState.setAnimation(0, "walk", true);
+    });
+
+    return Scaffold(
+      appBar: AppBar(title: const Text('Simple Animation')),
+      body: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller)
+    );
+}
+```
+
+Upon instantiation, the `SpineWidget` will asynchronously load the specified files and construct the underlying core class instances from them, namely instances of `AtlasFlutter`, `SkeletonDataFlutter`, `Skeleton`, `AnimationStateData`, and `AnimationState`.
+
+Once loading is complete, the `SpineWidgetController` is called, allowing it to modify the state of the widget, such as setting one or more animations, manipulating the bone hierarchy, or modifying the skin of the skeleton. See the section on `SpineWidgetController` below.
+
+The `SpineWidget` class provides multiple static factory methods to load skeleton and atlas files from different sources:
+
+* `SpineWidget.fromAsset()` loads files from the root bundle, or a provided bundle.
+* `SpineWidget.fromFile()` loads files from the file system.
+* `SpineWidget.fromHttp()` loads files from URLs.
+* `SpineWidget.fromDrawable()` constructs a widget from a `SkeletonDrawableFlutter`. This is useful when the skeleton data should be preloaded, cached, and/or shared between `SpineWidget` instances. See the section "Pre-loading and sharing skeleton data" below.
+
+All factory methods have optional arguments that let you further define how the Spine skeleton is fitted and aligned inside the widget, and how the widget is sized.
+
+* `fit`, the [BoxFit](https://api.flutter.dev/flutter/painting/BoxFit.html) to use to fit the skeleton inside the widget.
+* `alignment`, the [Alignment](https://api.flutter.dev/flutter/painting/Alignment-class.html) to use to align the skeleton inside the widget.
+* `BoundsProvider`, used to calculate the pixel size of the bounding box to be used for the skeleton when computing the fit and alignment. By default, the skeleton's setup pose bounding box is used. See the class documentation for [`SetupPoseBounds`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L173), [`RawBounds`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L183), and [`SkinAndAnimationBounds`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L196) for additional information.
+* `sizedByBounds`, defines whether to size the widgets by the bounds computed by the `BoundsProvider`, or have it sized by its parent widget.
+
+## Pre-loading and sharing skeleton data
+Pre-loading allows you to share atlas and skeleton data between multiple `SpineWidget` instances, saving both load time and memory. The key is understanding the ownership parameter when creating drawables.
+
+### Sharing data across multiple widgets
+When you want multiple widgets to share the same atlas and skeleton data:
+
+```dart
+// Pre-load the atlas and skeleton data once
+final atlas = await AtlasFlutter.fromAsset("assets/test.atlas");
+final skeletonData = await SkeletonDataFlutter.fromAsset(atlas, "assets/test.skel");
+
+// Create drawables without taking ownership (pass false)
+final drawable1 = SkeletonDrawableFlutter(atlas, skeletonData, false);
+final drawable2 = SkeletonDrawableFlutter(atlas, skeletonData, false);
+
+// Use in multiple widgets
+SpineWidget.fromDrawable(drawable1, controller1);
+SpineWidget.fromDrawable(drawable2, controller2);
+```
+
+With `ownsAtlasAndSkeletonData: false`, the drawables will NOT dispose the atlas and skeleton data when they are disposed. You must manually manage their lifecycle:
+
+```dart
+// Dispose drawables when done
+drawable1.dispose();
+drawable2.dispose();
+
+// Manually dispose shared data when completely done
+skeletonData.dispose();
+atlas.dispose();
+```
+
+### Single-use with ownership
+If you only need one widget and want automatic cleanup:
+
+```dart
+final atlas = await AtlasFlutter.fromAsset("assets/test.atlas");
+final skeletonData = await SkeletonDataFlutter.fromAsset(atlas, "assets/test.skel");
+
+// Create drawable with ownership (pass true)
+final drawable = SkeletonDrawableFlutter(atlas, skeletonData, true);
+SpineWidget.fromDrawable(drawable, controller);
+
+// When disposed, this will also dispose atlas and skeletonData
+drawable.dispose();
+```
+
+## SpineWidgetController
+A [`SpineWidgetController`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L64) controls how the skeleton of a `SpineWidget` is animated and rendered. The controller is provided with a set of optional callbacks as constructor arguments, which are called at specific times during the life-time of the `SpineWidget`.
+
+The controller exposes the skeleton state through getters returning Spine Runtimes API objects such as the `AtlasFlutter`, `SkeletonDataFlutter`, `Skeleton`, and `AnimationState`, through which the state can be manipulated. See the [Spine Runtimes Guide](/spine-runtimes-guide), and the [class documentation](/git/spine-runtimes/spine-flutter/lib/generated/api.dart) for more information.
+
+Upon initialization of a `SpineWidget`, the controller's `onInitialized()` callback method is invoked once. This method can be used to setup the initial animation(s) to be played back, or set the skin of the skeleton, among other things.
+
+After initialization is complete, the `SpineWidget` is rendered continuously at the screen refresh rate. Each frame, the `AnimationState` is updated based on the currently queued animations, and applied to the `Skeleton`.
+
+Next, the optional `onBeforeUpdateWorldTransforms()` callback is invoked, which can modify the skeleton before its current pose is calculated using `Skeleton.updateWorldTransform()`.
+
+After the current pose has been calculated, the optional `onAfterUpdateWorldTransforms()` callback is invoked, which can further modify the current pose before the skeleton is rendered. This is a good place to manually position bones.
+
+Before the skeleton is rendered by the `SpineWidget`, the optional `onBeforePaint()` callback is invoked, which allows rendering backgrounds or other objects that should go behind the skeleton on the [`Canvas`](https://api.flutter.dev/flutter/dart-ui/Canvas-class.html).
+
+After the `SpineWidget` has rendered the current skeleton pose to the `Canvas`, the optional `onAfterPaint()` callback is invoked, which allows rendering additional objects on top of the skeleton.
+
+By default, the widget updates and renders the skeleton every frame. The `SpineWidgetController.pause()` method can be used to pause updating and rendering the skeleton. The `SpineWidgetController.resume()` method resumes updating and rendering the skeleton. The `SpineWidgetController.isPlaying()` getter reports the current playback state. See the [`example/lib/pause_play_animation.dart`](/git/spine-runtimes/spine-flutter/example/lib/pause_play_animation.dart) example.
+
+## SkeletonDrawableFlutter
+A `SkeletonDrawableFlutter` bundles loading, storing, updating, and rendering a `Skeleton` and its associated `AnimationState` into a single, easy to use class. The class can be used as the basis for a custom widget implementation. The `SpineWidget` encapsulates the state of the skeleton it displays via an instance of a `SkeletonDrawableFlutter`.
+
+Use the static `fromAsset()`, `fromFile()`, or `fromHttp()` methods to construct a `SkeletonDrawableFlutter` from file assets. To share `AtlasFlutter` and `SkeletonDataFlutter` among multiple `SkeletonDrawableFlutter` instances, instantiate the drawables via the constructor, passing the same atlas and skeleton data to each of them.
+
+The `SkeletonDrawableFlutter` exposes the `atlasFlutter`, `skeletonData`, `skeleton`, `animationStateData`, and `animationState` to query, modify, and animate the skeleton.
+
+To animate the skeleton, queue animations on one or more tracks via the `AnimationState` API, such as `AnimationState.setAnimation()` or `AnimationState.addAnimation()`.
+
+To update the animation state, apply it to the skeleton, and update the current skeleton pose, call the `SkeletonDrawableFlutter.update()` method, providing it a delta time in seconds to advance the animations.
+
+To render the current pose of the skeleton, use the rendering methods `SkeletonDrawableFlutter.renderFlutter()`, `SkeletonDrawableFlutter.renderToCanvas()`, `SkeletonDrawableFlutter.renderToPictureRecorder()`, `SkeletonDrawableFlutter.renderToPng()`, or `SkeletonDrawableFlutter.renderToRawImageData()`.
+
+The `SkeletonDrawableFlutter` stores objects allocated on the native heap. The native objects need to be manually disposed of via a call to `SkeletonDrawableFlutter.dispose()` if the `SkeletonDrawableFlutter` is no longer needed. Not doing so will result in a native memory leak.
+
+> **Note:** when using `SpineWidget`, you do not have to manually dispose of the `SkeletonDrawableFlutter` the widget uses. The widget will dispose the `SkeletonDrawableFlutter` when it is disposed itself.
+
+## Applying Animations
+Applying animations to a skeleton displayed by a `SpineWidget` is done through the `AnimationState` in the callbacks of a `SpineWidgetController`.
+
+> **Note:** See [Applying Animations](/spine-applying-animations#AnimationState-API) in the Spine Runtimes Guide for more in-depth information, specifically about animation tracks and animation queueing.
+
+To set a specific animation on track 0, call `AnimationState.setAnimation()`:
+
+```dart
+final controller = SpineWidgetController(onInitialized: (controller) {
+   // Set the walk animation on track 0, let it loop
+   controller.animationState.setAnimation(0, "walk", true);
+});
+```
+
+The first parameter specifies the track, the second parameter is the name of the animation, and the third parameter defines whether to loop the animation.
+
+You can queue multiple animations:
+
+```dart
+controller.animationState.setAnimation(0, "walk", true);
+controller.animationState.addAnimation(0, "jump", false, 2);
+controller.animationState.addAnimation(0, "run", true, 0);
+```
+
+The first parameter to `addAnimation()` is the track. The second parameter is the name of the animation. The third parameter specifies whether to loop the animation. The final parameter defines the delay in seconds, after which this animation should replace the previous animation on the track.
+
+In the example above, the `"walk"` animation is played back first. 2 seconds later, the `"jump"` animation is played back once, followed by a transition to the `"run"` animation, which will be looped.
+
+When transitioning from one animation to another, `AnimationState` will mix the animations for a specificable duration. These mix times are defined in an `AnimationStateData` instance, from which the `AnimationState` retrieves mix times.
+
+The `AnimationStateData` instance is also available through the controller. You can set the default mix time, or the mix time for a specific pair of animations:
+
+```dart
+controller.animationStateData.defaultMix = 0.2;
+controller.animationStateData.setMix("walk", "jump", 0.1);
+```
+
+When setting or adding an animation, a `TrackEntry` object is returned, which allows further modification of that animation's playback. For example, you can set the track entry to reverse the animation playback:
+
+```dart
+final entry = controller.animationState.setAnimation(0, "walk", true);
+entry.reverse = true;
+```
+
+See the [`TrackEntry` class documentation](/git/spine-runtimes/spine-flutter/lib/generated/track_entry.dart) for more options.
+
+> **Note:** Do not hold on to `TrackEntry` instances outside the function you are using them in. Track entries are re-used internally and will thus become invalid once the animation it represents has been completed.
+
+You can set or queue empty animations on an animation track to smoothly reset the skeleton back to its setup pose:
+
+```dart
+controller.animationState.setEmptyAnimation(0, 0.5);
+controller.animationState.addEmptyAnimation(0, 0.5, 0.5);
+```
+
+The first parameter to `setEmptyAnimation()` specifies the track. The second parameter specifies the mix duration in seconds used to mix out the previous animation and mix in the "empty" animation.
+
+The first parameter to `addEmptyAnimation()` specifies the track. The second parameter is the mix duration. The third parameter specifies the delay in seconds, after which the empty animation should replace the previous animation on the track via mixing.
+
+All animations on a track can be cleared immediately via `AnimationState.clearTrack()`. To clear all tracks at once, `AnimationState.clearTracks()` can be used. This will leave the skeleton in the last pose it was in.
+
+To reset the pose of a skeleton to the setup pose, use `Skeleton.setupPose()`:
+
+```dart
+controller.skeleton.setupPose();
+```
+
+This will reset both the bones and slots to their setup pose configuration. Use `Skeleton.setupPoseSlots()` to only reset the slots to their setup pose configuration.
+
+## AnimationState Events
+An `AnimationState` emits events during the life-cycle of an animation that is being played back. You can listen for this events to react as needed. The Spine Runtimes API defines the following [event types](/git/spine-runtimes/spine-flutter/lib/generated/event_type.dart):
+
+* `start`: emitted when an animation is started.
+* `interrupt`: emitted when an animation's track was cleared, or a new animation was set.
+* `complete`: emitted when an animation completes a loop.
+* `end`: emitted when an animation will never be applied again.
+* `dispose`: emitted when the animation's track entry is disposed.
+* `event`: emitted when a user defined [event](/spine-events#Events) happened.
+
+To receive events, you can register an [`AnimationStateListener`](/git/spine-runtimes/spine-flutter/lib/spine_dart.dart#L229) callback with either the `AnimationState` to receive events across all animations, or with the `TrackEntry` of a specific animation queued for playback:
+
+```dart
+final entry = controller.animationState.setAnimation(0, "walk", true);
+entry.setListener((type, trackEntry, event) {
+   if (type == EventType.event) {
+      print("User defined event: ${event?.data.name}");
+   }
+});
+
+controller.animationState.setListener((type, trackEntry, event) {
+   print("Animation state event $type");
+});
+```
+
+See the [`example/lib/animation_state_events.dart`](/git/spine-runtimes/spine-flutter/example/lib/animation_state_events.dart) example.
+
+## Skins
+![/img/spine-runtimes-guide/spine-flutter/skins.png](/img/spine-runtimes-guide/spine-flutter/skins.png)
+
+Many applications and games allow users to create custom avatars out of many individual items, such as hair, eyes, pants, or accessories like earrings or bags. With Spine, this can be achieved by [mixing and matching skins](/spine-examples-mix-and-match).
+
+You can create custom skins from other skins like this:
+
+```dart
+final data = controller.skeletonData;
+final skeleton = controller.skeleton;
+final customSkin = Skin("custom-skin");
+customSkin.addSkin(data.findSkin("skin-base")!);
+customSkin.addSkin(data.findSkin("nose/short")!);
+customSkin.addSkin(data.findSkin("eyelids/girly")!);
+customSkin.addSkin(data.findSkin("eyes/violet")!);
+customSkin.addSkin(data.findSkin("hair/brown")!);
+customSkin.addSkin(data.findSkin("clothes/hoodie-orange")!);
+customSkin.addSkin(data.findSkin("legs/pants-jeans")!);
+customSkin.addSkin(data.findSkin("accessories/bag")!);
+customSkin.addSkin(data.findSkin("accessories/hat-red-yellow")!);
+skeleton.setSkin2(customSkin);
+skeleton.setupPoseSlots();
+```
+
+Create a custom skin with the `Skin()` constructor.
+
+Next, fetch the `SkeletonData` from the controller. It is used to look up skins by name via `SkeletonData.findSkin()`.
+
+Add all the skins you want to combine into the new custom skin via `Skin.addSkin()`.
+
+Finally, set the new skin on the `Skeleton` using `Skeleton.setSkin2()` and call `Skeleton.setupPoseSlots()` to ensure no attachments from previous skins and/or animations are left over.
+
+> **Note:** A `Skin` wraps an underlying C++ object. It needs to be manually disposed via a call to `Skin.dispose()` when it is no longer in use.
+
+See the [`example/lib/dress_up.dart`](/git/spine-runtimes/spine-flutter/example/lib/dress_up.dart) example, which also demonstrate how to render thumbnail previews of skins using `SkeletonDrawableFlutter`.
+
+## Setting Bone Transforms
+![/img/spine-runtimes-guide/spine-flutter/simple-animation.png](/img/spine-runtimes-guide/spine-flutter/bone-transform.png)
+
+When authoring a skeleton in the Spine Editor, the skeleton is defined in what is called the skeleton coordinate system. This coordinate system may not align with the coordinate system of the `SpineWidget` the skeleton is rendered by. Touch coordinates relative to the `SpineWidget` need thus be converted to the skeleton coordinate system, e.g. if a user should be able to move a bone by touch.
+
+The `SpineWidgetController` offers the method `toSkeletonCoordinates()` which takes an [`Offset`](https://api.flutter.dev/flutter/dart-ui/Offset-class.html) relative to the `SpineWidget` it is associated with, and converts it to the skeleton's coordinate system.
+
+See the [`example/lib/ik_following.dart`](/git/spine-runtimes/spine-flutter/example/lib/ik_following.dart) example.
+
+## Flame Integration
+![/img/spine-runtimes-guide/spine-flutter/flame.png](/img/spine-runtimes-guide/spine-flutter/flame.png)
+
+spine-flutter includes an example that shows how to load and renderer Spine skeletons in [Flame Engine](https://flame-engine.org/). See the [`example/lib/flame_example.dart`](/git/spine-runtimes/spine-flutter/example/lib/flame_example.dart) source file.
+
+The example features a simple `SpineComponent` that extends Flame's `PositionComponent`. The `SpineComponent` can be instantiated through the static `SpineComponent.fromAsset()` method, or through the constructor.
+
+The static method can be used as a quick, one-off loading mechanism when the skeleton and atlas data doesn't have to be shared with other components. The example contains a `FlameGame` implementation called `SimpleFlameExample` which demonstrates this simple way of getting a Spine skeleton on screen as part of a Flame game.
+
+Creating a `SpineComponent` via the constructor allows more fine-grained management of the data loading and sharing by taking a `SkeletonDrawableFlutter`. E.g. you can pre-load the skeleton data and atlas, then share it across multiple `SpineComponent` instances. This will both improve memory usage and rendering performance, as data is shared, and rendering can be batched. See the `FlameGame` implementation called `PreloadAndShareSpineDataExample` for an example.
+
+By design, Flame can not know when a component has reached its end of life. However, a `SpineComponent` handles native resources that need to be released at the end of its life. It is thus your responsibility to either call `SpineComponent.dispose()` if a `SpineComponent` is no longer in use. If the `SpineComponent` was constructed from a `SkeletonDrawableFlutter`, you may also have to manually dispose the `SkeletonDataFlutter` and `AtlasFlutter` from which it was constructed, like in the `PreloadAndShareSpineDataExample` example.
+
+# Spine Runtimes API access
+spine-flutter maps almost all of the Spine Runtime API to Dart. Objects returned by `SpineWidgetController` or `SkeletonDrawableFlutter`, like `Skeleton` or `AnimationState` are 1:1 translations of the spine-cpp API to Dart. You can thus apply almost all of the materials in the generic [Spine Runtimes Guide](/spine-runtimes-guide) to your Dart code.
+
+Due to the nature of the spine-cpp to Dart FFI bridge, there are some considerations:
+
+* Arrays returned by the API (like `ArrayFloat`, `ArrayInt`) are direct wrappers around native memory. They provide List-like access to the underlying C++ data and modifications through the array's methods will affect the native data.
+* You can create bones and slots using their factory constructors (e.g., `Bone(boneData, parent)`, `Slot(slotData, skeleton)`). However, you are responsible for disposing any manually created objects.
+* The C++ class hierarchy is fully translated to Dart, including all timeline and constraint classes with proper inheritance relationships and the same nullability patterns as the Java reference implementation.
+
+## Development
+
+This section details the development workflow and architecture of spine-flutter, including code generation, building, and testing.
+
+### Architecture Overview
+
+spine-flutter is built on a multi-layer architecture:
+
+```
+┌─────────────────────────────────────────────────────────────┐
+│                        spine-cpp                            │
+│          Core C++ Spine runtime implementation              │
+└─────────────────────────────────────────────────────────────┘
+                              ↓
+┌─────────────────────────────────────────────────────────────┐
+│                        spine-c                              │
+│              C wrapper API around spine-cpp                 │
+│         (Auto-generated + manual extensions)                │
+└─────────────────────────────────────────────────────────────┘
+                              ↓
+┌─────────────────────────────────────────────────────────────┐
+│                    FFI Bindings Layer                       │
+│         Low-level Dart FFI bindings to spine-c              │
+│              (Generated by Dart's ffigen)                   │
+└─────────────────────────────────────────────────────────────┘
+                              ↓
+┌─────────────────────────────────────────────────────────────┐
+│                  Dart Wrapper Classes                       │
+│           Idiomatic, type-safe Dart API                     │
+│              (Generated by custom codegen)                  │
+└─────────────────────────────────────────────────────────────┘
+                              ↓
+┌─────────────────────────────────────────────────────────────┐
+│                 Flutter Integration Layer                   │
+│    Flutter-specific classes (SpineWidget, rendering)        │
+│                    (Hand-written)                           │
+└─────────────────────────────────────────────────────────────┘
+```
+
+### Binding Generation
+
+The binding generation process is automated through the `generate-bindings.sh` script, which orchestrates several steps:
+
+#### 1. Generate spine-c Bindings
+First, the spine-c bindings are generated from spine-cpp headers:
+```bash
+cd ../spine-c && ./build.sh codegen
+```
+
+This uses Clang's AST to parse C++ headers and generate:
+- C wrapper functions for all spine-cpp classes
+- Type information in JSON format
+- Header and implementation files
+
+#### 2. Copy Source Files
+The `setup.sh` script copies necessary source files:
+- spine-cpp sources to `src/spine-cpp/`
+- spine-c sources to `src/spine-c/`
+- Platform-specific setup for iOS/macOS CocoaPods
+
+#### 3. Generate Dart Bindings
+The custom TypeScript-based code generator creates idiomatic Dart wrappers:
+```bash
+npx tsx codegen/src/index.ts
+```
+
+This generates:
+- Dart wrapper classes for all Spine types (`lib/generated/*.dart`)
+- FFI bindings configuration (`ffigen.yaml`)
+- Low-level FFI bindings (`spine_dart_bindings_generated.dart`)
+
+See the [codegen README](codegen/README.md) for detailed information about the code generation architecture.
+
+#### 4. Build Test Library
+For headless testing, a native shared library is built:
+```bash
+cd test && ./build.sh
+```
+
+### WASM Compilation
+
+Web platform support requires compiling spine-cpp to WebAssembly:
+
+```bash
+./compile-wasm.sh
+```
+
+This script:
+1. Creates the assets directory structure
+2. Compiles all spine-cpp and spine-c sources using Emscripten
+3. Generates `libspine_flutter.js` and `libspine_flutter.wasm`
+4. Places them in `lib/assets/` for web platform loading
+
+Key Emscripten flags:
+- `-O2`: Optimization level that preserves function names
+- `--closure 1`: Closure compiler for size optimization
+- `-s MODULARIZE=1`: Creates a module-based output
+- `-s EXPORT_ALL=1`: Exports all functions for FFI access
+- `-s ALLOW_MEMORY_GROWTH=1`: Dynamic memory allocation
+
+### Testing
+
+spine-flutter includes both Flutter widget tests and headless Dart tests:
+
+#### Headless Testing
+Pure Dart tests that don't require Flutter:
+```bash
+cd test
+dart headless_test.dart
+```
+
+These tests verify:
+- Atlas and skeleton data loading
+- Core API functionality
+- Memory management
+- Extension functions
+
+#### Flutter Testing
+Widget and integration tests:
+```bash
+flutter test
+```
+
+#### Running Examples
+The example app showcases all features:
+```bash
+cd example
+flutter run
+```
+
+### Build Scripts
+
+Several utility scripts facilitate development:
+
+- **`setup.sh`**: Prepares the project structure
+  - Copies spine-cpp sources for native builds
+  - Sets up iOS/macOS specific files
+  - Creates necessary directories
+
+- **`clean.sh`**: Cleans all build artifacts
+  - Removes CocoaPods installations
+  - Cleans Flutter dependencies
+  - Deletes generated files
+  - Useful for resolving build issues
+
+- **`generate-bindings.sh`**: Complete binding generation
+  - Runs spine-c code generation
+  - Executes Dart code generation
+  - Builds test libraries
+
+- **`compile-wasm.sh`**: Web platform compilation
+  - Compiles C++ to WebAssembly
+  - Generates JavaScript module
+
+> **Note:** On Windows, use [Git Bash](https://gitforwindows.org/) to run these bash scripts.
+
+### Code Generation Details
+
+The code generation system is central to spine-flutter's maintainability. For detailed information about:
+- How C++ types are transformed to Dart
+- Nullability handling
+- RTTI-based type instantiation
+- Method overloading resolution
+- Extension system architecture
+
+Please refer to the comprehensive [codegen README](codegen/README.md).
+
+### Contributing
+
+When contributing to spine-flutter:
+
+1. **Core API changes**: Modify spine-cpp, then regenerate bindings
+2. **Extension functions**: Add to spine-c `extensions.h/.cpp`, then wrap in `spine_dart.dart`
+3. **Flutter integration**: Modify Flutter-specific classes in `lib/`
+4. **Examples**: Add to the example app to showcase new features
+
+Always run the full test suite and ensure examples work on all platforms before submitting changes.

+ 370 - 0
spine-flutter/docs.md

@@ -0,0 +1,370 @@
+# spine-flutter Runtime Documentation
+
+> **Licensing**
+>
+> Please see the [Spine Runtimes License](/spine-runtimes-license) before integrating the Spine Runtimes into your applications.
+
+# Getting Started
+The spine-flutter runtime is implemented as a [Flutter FFI plugin](https://docs.flutter.dev/development/packages-and-plugins/developing-packages#plugin-ffi) on top of [spine-cpp](/spine-cpp). It supports all platforms supported by Flutter (desktop, Android, iOS, web), and supports all Spine features except tint black and screen blend mode.
+
+
+## Installation
+spine-flutter is supported from Flutter 3.10.5 onwards. To use spine-flutter in your Flutter project, add the following dependency to your project's `pubspec.yaml` file:
+
+```yaml
+dependencies:
+  ...
+  spine_flutter: ^4.2.36
+```
+
+See [spine_flutter on pub.dev](https://pub.dev/packages/spine_flutter) for the latest version.
+
+Ensure that the `major.minor` version of spine-flutter matches the `major.minor` Spine Editor version you are exporting from. See [Spine Versioning](/spine-versioning#Synchronizing-versions) for more information.
+
+In your `main()` function, add these two lines in the beginning to initialize the spine-flutter runtime:
+
+```dart
+void main() async {
+    await initSpineFlutter(enableMemoryDebugging: false);
+    ...
+}
+```
+
+> **Note:** the `main()` method must be `async`.
+
+## Samples
+The spine-flutter runtime includes several samples that showcase its feature set.
+
+You can run the example project following these steps:
+
+1. Install the [Flutter SDK](https://docs.flutter.dev/get-started/install), then run `flutter doctor` which will instruct you what other dependencies to install.
+2. Clone the spine-runtimes repository: `git clone https://github.com/esotericsoftware/spine-runtimes`
+3. Run the `setup.sh` script in the `spine-flutter/` folder. On Windows, you can use [Git Bash](https://gitforwindows.org/) included in Git for Window to run the `setup.sh` Bash script.
+
+You can then open `spine-flutter` in an IDE or editor of your choice that supports Flutter, like [IntelliJ IDEA/Android Studio](https://docs.flutter.dev/get-started/editor?tab=androidstudio) or [Visual Studio Code](https://docs.flutter.dev/get-started/editor?tab=vscode) to inspect and run the example.
+
+Alternatively, you can run the example from the [command line](https://docs.flutter.dev/get-started/test-drive?tab=terminal).
+
+The example project contains the following examples:
+* [`example/lib/simple_animation.dart`](/git/spine-runtimes/spine-flutter/example/lib/simple_animation.dart): demonstrates the basic use of `SpineWidget` and `SpineWidgetController` to load an exported Spine skeleton, display it in the widget, and playback a specific animation.
+* [`example/lib/pause_play_animation.dart`](/git/spine-runtimes/spine-flutter/example/lib/pause_play_animation.dart): demonstrates how to pause and resume an animation.
+* [`example/lib/animation_state_events`](/git/spine-runtimes/spine-flutter/example/lib/animation_state_events.dart): demonstrates how set a slot's color, how to queue multiple animations, and how to to listen for animation state events.
+* [`example/lib/debug_rendering.dart`](/git/spine-runtimes/spine-flutter/example/lib/debug_rendering.dart): shows how to perform custom drawing on top of the rendered skeleton via the `SpineWidgetController` `onAfterPaint` callback.
+* [`example/lib/dress_up.dart`](/git/spine-runtimes/spine-flutter/example/lib/dress_up.dart): demonstrates Spine's skins feature as well as rendering a skeleton to a thumbnail for use in a character creation UI.
+* [`example/lib/ik_following.dart`](/git/spine-runtimes/spine-flutter/example/lib/ik_following.dart): demonstrates how to let the user drag one of the skeleton's bones via mouse or touch input.
+* [`example/lib/flame_example.dart`](/git/spine-runtimes/spine-flutter/example/lib/flame_example.dart): demonstrates how to write a simple [Flame](https://flame-engine.org/) component to use spine-flutter with the Flame game engine.
+
+## Updating the spine-flutter Runtime
+Before updating your project's spine-flutter runtime, please consult our [guide on Spine editor and runtime version management](/spine-runtime-architecture#Versioning).
+
+To update the spine-flutter runtime, simply modify the version string of the `spine_flutter` package in your `pubspec.yaml`.
+
+> **Note:** If you change the `major.minor` version of the `spine_flutter` package, you have to re-export your Spine skeletons with the same Spine Editor `major.minor` version!
+
+# Using spine-flutter
+The spine-flutter runtime is an idiomatic [Dart FFI wrapper](https://dart.dev/guides/libraries/c-interop) around the generic [spine-cpp](/spine-cpp) which supports loading, playback and manipulation of animations created with Spine. The spine-flutter runtime exposes almost all of the spine-cpp API as idiomatic Dart and provides Flutter and [Flame](https://flame-engine.org/) specific classes to easily display and interact with Spine skeletons.
+
+The spine-flutter runtime supports all Spine features except tint black and screen blend mode.
+
+## Asset Management
+### Exporting for spine-flutter
+![](/img/spine-runtimes-guide/spine-ue4/export.png)
+Please follow the instructions in the Spine User Guide on how to
+
+1. [Export skeleton & animation data](/spine-export)
+2. [Export texture atlases containing the images of your skeleton](/spine-texture-packer)
+
+An export of the skeleton data and texture atlas of your skeleton will yield the following files:
+
+![](/img/spine-runtimes-guide/spine-ue4/exported-files.png)
+
+1. `skeleton-name.json` or `skeleton-name.skel`, containing your skeleton and animation data.
+2. `skeleton-name.atlas`, containing information about the texture atlas.
+3. One or more `.png` files, each representing on page of your texture atlas containing the packed images your skeleton uses.
+
+> **Note**: You should prefer binary skeleton exports over JSON exports, as they are smaller in size and faster to load.
+
+The files can be loaded via spine-flutter classes like `Atlas`, `SkeletonData`, `SkeletonDrawable`, `SpineWidget`.
+
+> **Note**: The spine-flutter runtime currently does not support atlases exported using pre-multiplied alpha due to technical limitations in Flutter. Flutter's rendering engine ensures that common non-premultiplied alpha artifacts are avoided.
+
+### Updating Spine Assets
+During development, you may frequently update your Spine skeleton data and texture atlas files. You can simply overwrite these source files (`.json`, `.skel`, `.atlas`, `.png`) by re-exporting from the Spine Editor and replacing the existing files in your Flutter project.
+
+Ensure that the `major.minor` version of spine-flutter matches the `major.minor` Spine Editor version you are exporting from. See [Spine Versioning](/spine-versioning#Synchronizing-versions) for more information.
+
+## Core classes
+The spine-flutter API is built on top of the generic [spine-cpp](/spine-cpp) runtime, which provides platform independent  core classes and algorithms to load, query, modify, and animate Spine skeletons. The core classes are wrapped via Dart FFI and exposed as idiomatic Dart classes.
+
+Here, we will briefly discuss the most important core classes that you will  encounter in your day-to-day use of spine-flutter. Please consult the [Spine Runtimes Guide](/spine-runtimes-guide)
+for a detailed overview of the Spine Runtimes architecture, core classes, and API usage.
+
+The [`Atlas`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L102) class stores the data loaded from an `.atlas` file and its corresponding `.png` image files.
+
+The [`SkeletonData`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L199) class stores the data loaded from a `.json` or `.skel` skeleton file.  The skeleton data contains information about the bone hierarchy, slots, attachments, constraints, skins, and animations. A `SkeletonData` instance is usually loaded by also providing an `Atlas` from which it sources the images to be used by the skeleton it represents. It serves as a blueprint for creating `Skeleton` instances. Multiple skeletons can be instantiated from the same atlas and skeleton data, which then share the loaded data, minimizing both load times and memory consumption at runtime.
+
+The [`Skeleton`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L2734) class stores an instance of a skeleton, created from a `SkeletonData` instance. A skeleton stores its current pose, that is the position of bones and the current configuration of slots, attachments, and active skin. The current pose can be computed by either manually modifying the bone hierarchy, or, more commonly, by applying animations via an `AnimationState`.
+
+The [`AnimationState`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L3663) class is responsible for keeping track of which animation(s) should be applied to a skeleton, advancing and mixing those animations based on the elapsed time between the last and current rendering frame, and applying the animations to a skeleton instance, thereby setting its current pose. The `AnimationState` queries an [`AnimationStateData`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L3600) instance to retrieve mixing times between animations, or fetches the default mix time if no mixing time is available for a pair of animations.
+
+The spine-flutter runtime builds on top of these core classes.
+
+## SpineWidget
+![/img/spine-runtimes-guide/spine-flutter/simple-animation.png](/img/spine-runtimes-guide/spine-flutter/simple-animation.png)
+
+A [`SpineWidget`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L261) is a [StatefulWidget](https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html) responsible for loading and displaying a Spine skeleton. At a minimum, the widget needs to know from where to load the skeleton and atlas files, and it must receive a `SpineWidgetController` instance that is responsible for modifying the state of the widget, such as setting an animation, or changing the skin of the skeleton.
+
+In the simplest case, a `SpineWidget` can be instantiated inside another widget's `build()` method like this:
+
+```dart
+@override
+Widget build(BuildContext context) {
+    final controller = SpineWidgetController(onInitialized: (controller) {
+      // Set the walk animation on track 0, let it loop
+      controller.animationState.setAnimationByName(0, "walk", true);
+    });
+
+    return Scaffold(
+      appBar: AppBar(title: const Text('Simple Animation')),
+      body: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller)
+    );
+}
+```
+
+Upon instantiation, the `SpineWidget` will asynchronously load the specified files and construct the underlying core class instances from them, namely instances of `Atlas`, `SkeletonData`, `Skeleton`, `AnimationStateData`, and `AnimationState`.
+
+Once loading is complete, the `SpineWidgetController` is called, allowing it to modify the state of the widget, such as setting one or more animations, manipulating the bone hierarchy, or modifying the skin of the skeleton. See the section on `SpineWidgetController` below.
+
+The `SpineWidget` class provides multiple static factory methods to load skeleton and atlas files from different sources:
+
+* `SpineWidget.fromAsset()` loads files from the root bundle, or a provided bundle.
+* `SpineWidget.fromFile()` loads files from the file system.
+* `SpineWidget.fromHttp()` loads files from URLs.
+* `SpineWidget.fromDrawable()` constructs a widget from a `SkeletonDrawable`. This is useful when the skeleton data should be preloaded, cached, and/or shared between `SpineWidget` instances. See the section "Pre-loading and sharing skeleton data" below.
+
+All factory methods have optional arguments that let you further define how the Spine skeleton is fitted and aligned inside the widget, and how the widget is sized.
+
+* `fit`, the [BoxFit](https://api.flutter.dev/flutter/painting/BoxFit.html) to use to fit the skeleton inside the widget.
+* `alignment`, the [Alignment](https://api.flutter.dev/flutter/painting/Alignment-class.html) to use to align the skeleton inside the widget.
+* `BoundsProvider`, used to calculate the pixel size of the bounding box to be used for the skeleton when computing the fit and alignment. By default, the skeleton's setup pose bounding box is used. See the class documentation for [`SetupPoseBounds`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L173), [`RawBounds`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L183), and [`SkinAndAnimationBounds`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L196) for additional information.
+* `sizedByBounds`, defines whether to size the widgets by the bounds computed by the `BoundsProvider`, or have it sized by its parent widget.
+
+## Pre-loading and sharing skeleton data
+If you want to share the atlas and skeleton data between multiple `SpineWidget` instances, you can manually pre-load the assets:
+
+```
+final atlas = await Atlas.fromAsset("assets/test.atlas");
+final skeletonData = await SkeletonData.fromAsset("assets/test.json", atlas);
+```
+
+You can then instantiate one or more `SpineWidget` instances from the same data, saving on load time and memory:
+
+```
+SpineWidget.fromDrawable(SkeletonDrawable(skeletonData, atlas));
+```
+
+You are responsible for disposing of the atlas and skeleton data one no `SpineWidget` (or `SkeletonDrawable`) refercing them exists anymore.
+
+```
+skeletonData.dispose();
+atlas.dispose();
+```
+
+## SpineWidgetController
+A [`SpineWidgetController`](/git/spine-runtimes/spine-flutter/lib/spine_widget.dart#L64) controls how the skeleton of a `SpineWidget` is animated and rendered. The controller is provided with a set of optional callbacks as constructor arguments, which are called at specific times during the life-time of the `SpineWidget`.
+
+The controller exposes the skeleton state through getters returning Spine Runtimes API objects such as the `Atlas`, `SkeletonData`, `Skeleton`, and `AnimationState`, through which the state can be manipulated. See the [Spine Runtimes Guide](/spine-runtimes-guide), and the [class documentation](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart) for more information.
+
+Upon initialization of a `SpineWidget`, the controller's `onInitialized()` callback method is invoked once. This method can be used to setup the initial animation(s) to be played back, or set the skin of the skeleton, among other things.
+
+After initialization is complete, the `SpineWidget` is rendered continuously at the screen refresh rate. Each frame, the `AnimationState` is updated based on the currently queued animations, and applied to the `Skeleton`.
+
+Next, the optional `onBeforeUpdateWorldTransforms()` callback is invoked, which can modify the skeleton before its current pose is calculated using `Skeleton.updateWorldTransform()`.
+
+After the current pose has been calculated, the optional `onAfterUpdateWorldTransforms()` callback is invoked, which can further modify the current pose before the skeleton is rendered. This is a good place to manually position bones.
+
+Before the skeleton is rendered by the `SpineWidget`, the optional `onBeforePaint()` callback is invoked, which allows rendering backgrounds or other objects that should go behind the skeleton on the [`Canvas`](https://api.flutter.dev/flutter/dart-ui/Canvas-class.html).
+
+After the `SpineWidget` has rendered the current skeleton pose to the `Canvas`, the optional `onAfterPaint()` callback is invoked, which allows rendering additional objects on top of the skeleton.
+
+By default, the widget updates and renders the skeleton every frame. The `SpineWidgetController.pause()` method can be used to pause updating and rendering the skeleton. The `SpineWidgetController.resume()` method resumes updating and rendering the skeleton. The `SpineWidgetController.isPlaying()` getter reports the current playback state. See the [`example/lib/animation_state_events.dart`](/git/spine-runtimes/spine-flutter/example/lib/animation_state_events.dart) example.
+
+## SkeletonDrawable
+A `SkeletonDrawable` bundles loading, storing, updating, and rendering a `Skeleton` and its associated `AnimationState` into a single, easy to use class. The class can be used as the basis for a custom widget implementation. The `SpineWidget` encapsulates the state of the skeleton it displays via an instance of a `SkeletonDrawable`.
+
+Use the `fromAsset()`, `fromFile()`, or `fromHttp()` methods to construct a `SkeletonDrawable` from file assets. To share `Atlas` and `SkeletonData` among multiple `SkeletonDrawable` instances, instantiate the drawables via the constructor, passing the same atlas and skeleton data to each of them.
+
+The `SkeletonDrawable` exposes the `Skeleton` and `AnimationState` to query, modify, and animate the skeleton. It also exposes the `Atlas` and `SkeletonData` from which the skeleton and animation state have been constructed.
+
+To animate the skeleton, queue animations on one or more tracks via the `AnimationState` API, such as `AnimationState.setAnimation()` or `AnimationState.addAnimation()`.
+
+To update the animation state, apply it to the skeleton, and update the current skeleton pose, call the `SkeletonDrawable.update()` method, providing it a delta time in seconds to advance the animations.
+
+To render the current pose of the skeleton, use the rendering methods `SkeletonDrawable.render()`, `SkeletonDrawable.renderToCanvas()`, `SkeletonDrawable.renderToPictureRecorder()`, `SkeletonDrawable.renderToPng()`, or `SkeletonDrawable.renderToRawImageData()`.
+
+The `SkeletonDrawable` stores objects allocated on the native heap. The native objects need to be manually disposed of via a call to `SkeletonDrawable.dispose()` if the `SkeletonDrawable` is no longer needed. Not doing so will result in a native memory leak.
+
+> **Note:** when using `SpineWidget`, you do not have to manually dispose of the `SkeletonDrawable` the widget uses. The widget will dispose the `SkeletonDrawable` when it is disposed itself.
+
+## Applying Animations
+Applying animations to a skeleton displayed by a `SpineWidget` is done through the `AnimationState` in the callbacks of a `SpineWidgetController`.
+
+> **Note:** See [Applying Animations](/spine-applying-animations#AnimationState-API) in the Spine Runtimes Guide for more in-depth information, specifically about animation tracks and animation queueing.
+
+To set a specific animation on track 0, call `AnimationState.setAnimation()`:
+
+```dart
+final controller = SpineWidgetController(onInitialized: (controller) {
+   // Set the walk animation on track 0, let it loop
+   controller.animationState.setAnimationByName(0, "walk", true);
+});
+```
+
+The first parameter specifies the track, the second parameter is the name of the animation, and the third parameter defines whether to loop the animation.
+
+You can queue multiple animations:
+
+```dart
+controller.animationState.setAnimationByName(0, "walk", true);
+controller.animationState.addAnimationByName(0, "jump", false, 2);
+controller.animationState.addAnimationByName(0, "run", true, 0);
+```
+
+The first parameter to `addAnimationByName()` is the track. The second parameter is the name of the animation. The third parameter specifies the delay in seconds, after which this animation should replace the previous animation on the track. The final parameter defines whether to loop the animation.
+
+In the example above, the `"walk"` animation is played back first. 2 seconds later, the `"jump"` animation is played back once, followed by a transition to the `"run"` animation, which will be looped.
+
+When transitioning from one animation to another, `AnimationState` will mix the animations for a specificable duration. These mix times are defined in an `AnimationStateData` instance, from which the `AnimationState` retrieves mix times.
+
+The `AnimationStateData` instance is also available through the controller. You can set the default mix time, or the mix time for a specific pair of animations:
+
+```dart
+controller.animationStateData.setDefaultMix(0.2);
+controller.animationStateData.setMixByName("walk", "jump", 0.1);
+```
+
+When setting or adding an animation, a `TrackEntry` object is returned, which allows further modification of that animation's playback. For example, you can set the track entry to reverse the animation playback:
+
+```dart
+final entry = controller.animationState.setAnimationByName(0, "walk", true);
+entry.setReverse(true);
+```
+
+See the [`TrackEntry` class documentation](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L3100) for more options.
+
+> **Note:** Do not hold on to `TrackEntry` instances outside the function you are using them in. Track entries are re-used internally and will thus become invalid once the animation it represents has been completed.
+
+You can set or queue empty animations on an animation track to smoothly reset the skeleton back to its setup pose:
+
+```dart
+controller.animationState.setEmptyAnimation(0, 0.5);
+controller.animationState.addEmptyAnimation(0, 0.5, 0.5);
+```
+
+The first parameter to `setEmptyAnimation()` specifies the track. The second parameter specifies the mix duration in seconds used to mix out the previous animation and mix in the "empty" animation.
+
+The first parameter to `addEmptyAnimation()` specifies the track. The second parameter specifies the mix duration. The third parameter is the delay in seconds, after which the empty animation should replace the previous animation on the track via mixing.
+
+All animations on a track can be cleared immediately via `AnimationState.clearTrack()`. To clear all tracks at once, `AnimationState.clearTracks()` can be used. This will leave the skeleton in the last pose it was in.
+
+To reset the pose of a skeleton to the setup pose, use `Skeleton.setToSetupPose()`:
+
+```dart
+controller.skeleton.setToSetupPose();
+```
+
+This will reset both the bones and slots to their setup pose configuration. Use `Skeleton.setSlotsToSetupPose()` to only reset the slots to their setup pose configuration.
+
+## AnimationState Events
+An `AnimationState` emits events during the life-cycle of an animation that is being played back. You can listen for this events to react as needed. The Spine Runtimes API defines the following [event types](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L3429):
+
+* `Start`: emitted when an animation is started.
+* `Interrupted`: emitted when an animation's track was cleared, or a new animation was set.
+* `Completed`: emitted when an animation completes a loop.
+* `Ended`: emitted when an animation will never be applied again.
+* `Disposed`: emitted when the animation's track entry is disposed.
+* `Event`: emitted when a user defined [event](/spine-events#Events) happened.
+
+To receive events, you can register an [`AnimationStateListener`](/git/spine-runtimes/spine-flutter/lib/spine_flutter.dart#L3597) callback with either the `AnimationState` to receive events across all animations, or with the `TrackEntry` of a specific animation queued for playback:
+
+```dart
+final entry = controller.animationState.setAnimationByName(0, "walk", true);
+entry.setListener((type, trackEntry, event) {
+   if (type == EventType.event) {
+      print("User defined event: ${event?.getData().getName()}");
+   }
+});
+
+controller.animationState.setListener((type, trackEntry, event) {
+   print("Animation state event $type");
+});
+```
+
+See the [`example/lib/animation_state_events.dart`](/git/spine-runtimes/spine-flutter/example/lib/animation_state_events.dart) example.
+
+## Skins
+![/img/spine-runtimes-guide/spine-flutter/skins.png](/img/spine-runtimes-guide/spine-flutter/skins.png)
+
+Many applications and games allow users to create custom avatars out of many individual items, such as hair, eyes, pants, or accessories like earrings or bags. With Spine, this can be achieved by [mixing and matching skins](/spine-examples-mix-and-match).
+
+You can create custom skins from other skins like this:
+
+```dart
+final data = controller.skeletonData;
+final skeleton = controller.skeleton;
+final customSkin = Skin("custom-skin");
+customSkin.addSkin(data.findSkin("skin-base")!);
+customSkin.addSkin(data.findSkin("nose/short")!);
+customSkin.addSkin(data.findSkin("eyelids/girly")!);
+customSkin.addSkin(data.findSkin("eyes/violet")!);
+customSkin.addSkin(data.findSkin("hair/brown")!);
+customSkin.addSkin(data.findSkin("clothes/hoodie-orange")!);
+customSkin.addSkin(data.findSkin("legs/pants-jeans")!);
+customSkin.addSkin(data.findSkin("accessories/bag")!);
+customSkin.addSkin(data.findSkin("accessories/hat-red-yellow")!);
+skeleton.setSkin(customSkin);
+skeleton.setSlotsToSetupPose();
+```
+
+Create a custom skin with the `Skin()` constructor.
+
+Next, fetch the `SkeletonData` from the controller. It is used to look up skins by name via `SkeletonData.findSkin()`.
+
+Add all the skins you want to combine into the new custom skin via `Skin.addSkin()`.
+
+Finally, set the new skin on the `Skeleton` and call `Skeleton.setSlotsToSetupPose()` to ensure no attachments from previous skins and/or animations are left over.
+
+> **Note:** A `Skin` wraps an underlying C++ object. It needs to be manually disposed via a call to `Skin.dispose()` when it is no longer in use.
+
+See the [`example/lib/dress_up.dart`](/git/spine-runtimes/spine-flutter/example/lib/dress_up.dart) example, which also demonstrate how to render thumbnail previews of skins using `SkeletonDrawable`.
+
+## Setting Bone Transforms
+![/img/spine-runtimes-guide/spine-flutter/simple-animation.png](/img/spine-runtimes-guide/spine-flutter/bone-transform.png)
+
+When authoring a skeleton in the Spine Editor, the skeleton is defined in what is called the skeleton coordinate system. This coordinate system may not align with the coordinate system of the `SpineWidget` the skeleton is rendered by. Touch coordinates relative to the `SpineWidget` need thus be converted to the skeleton coordinate system, e.g. if a user should be able to move a bone by touch.
+
+The `SpineWidgetController` offers the method `toSkeletonCoordinates()` which takes an [`Offset`](https://api.flutter.dev/flutter/dart-ui/Offset-class.html) relative to the `SpineWidget` it is associated with, and converts it to the skeleton's coordinate system.
+
+See the [`example/lib/ik_following.dart`](/git/spine-runtimes/spine-flutter/example/lib/ik_following.dart) example.
+
+## Flame Integration
+![/img/spine-runtimes-guide/spine-flutter/flame.png](/img/spine-runtimes-guide/spine-flutter/flame.png)
+
+spine-flutter includes an example that shows how to load and renderer Spine skeletons in [Flame Engine](https://flame-engine.org/). See the [`example/lib/flame_example.dart`](/git/spine-runtimes/spine-flutter/example/lib/flame_example.dart) source file.
+
+The example features a simple `SpineComponent` that extends Flame's `PositionComponent`. The `SpineComponent` can be instantiated through the static `SpineComponent.fromAsset()` method, or through the constructor.
+
+The static method can be used as a quick, one-off loading mechanism when the skeleton and atlas data doesn't have to be shared with other components. The example contains a `FlameGame` implementation called `SimpleFlameExample` which demonstrates this simple way of getting a Spine skeleton on screen as part of a Flame game.
+
+Creating a `SpineComponent` via the constructor allows more fine-grained management of the data loading and sharing by taking a `SkeletonDrawable`. E.g. you can pre-load the skeleton data and atlas, then share it across multiple `SpineComponent` instances. This will both improve memory usage and rendering performance, as data is shared, and rendering can be batched. See the `FlameGame` implementation called `PreloadAndShareSpineDataExample` for an example.
+
+By design, Flame can not know when a component has reached its end of life. However, a `SpineComponent` handles native resources that need to be released at the end of its life. It is thus your responsibility to either call `SpineComponent.dispose()` if a `SpineComponent` is no longer in use. If the `SpineComponent` was constructed from a `SkeletonDrawable`, you may also have to manually dispose the `SkeletonData` and `Atlas` from which it was constructed, like in the `PreloadAndShareSpineDataExample` example.
+
+# Spine Runtimes API access
+spine-flutter maps almost all of the Spine Runtime API to Dart. Objects returned by `SpineWidgetController` or `SkeletonDrawable`, like `Skeleton` or `AnimationState` are 1:1 translations of the spine-cpp API to Dart. You can thus apply almost all of the materials in the generic [Spine Runtimes Guide](/spine-runtimes-guide) to your Dart code.
+
+Due to the nature of the spine-cpp to Dart FFI bridge, there are however a few limitations:
+
+* Any returned array or map is a copy of the internal array. Modification will not have an effect. However, returned `Float32List` or `Int32List` instances are wrappers around the underlying native memory, and can thus be used to modify the native data.
+* You can not create, add or remove bones, slots, and other Spine objects directly.
+* The C++ class hierarchies of timelines are not exposed in Dart.

+ 548 - 0
todos/work/2025-07-31-16-50-17-generate-swift-bindings/analysis.md

@@ -0,0 +1,548 @@
+# Swift Bindings Generator Analysis
+
+## spine-c Codegen System Analysis
+
+### Purpose and Architecture of the spine-c Codegen System
+
+The spine-c codegen system is a sophisticated TypeScript-based code generator that automatically creates a complete C wrapper API for the Spine C++ runtime. Its primary purpose is to:
+
+1. **Automatic API Wrapping**: Parse spine-cpp headers using Clang's AST and generate a systematic C API with opaque types
+2. **Multi-language Binding Support**: Build inheritance maps and interface information to support binding generation for languages like Dart, Swift, Java, and others
+3. **Type Safety and Consistency**: Apply systematic type conversion rules and extensive validation to ensure correctness
+
+#### Architecture Overview
+
+The system follows a **multi-stage pipeline architecture**:
+
+1. **Type Extraction** - Uses Clang's `-ast-dump=json` to parse C++ headers
+2. **Type Processing** - Filters and validates extracted types
+3. **Validation** - Extensive checks for conflicts and unsupported patterns
+4. **Array Scanning** - Detects and generates specialized array types
+5. **IR Generation** - Converts C++ types to C intermediate representation
+6. **Code Writing** - Generates header and implementation files
+7. **Inheritance Analysis** - Builds inheritance maps for multi-language bindings
+
+### Structure of Types Available from generate()
+
+The `generate()` method returns a comprehensive data structure containing:
+
+```typescript
+{
+  cTypes,           // Generated C wrapper types for classes
+  cEnums,           // Generated C enum types
+  cArrayTypes,      // Specialized array types (Array<T> → spine_array_T)
+  inheritance,      // extends/implements map for single-inheritance languages
+  supertypes,       // Legacy RTTI supertypes map
+  subtypes,         // Legacy RTTI subtypes map
+  isInterface       // Pure interface detection map
+}
+```
+
+#### Key Type Definitions
+
+From `types.ts`, the system defines several core interfaces:
+
+- **ClassOrStruct**: Represents C++ classes/structs with members, inheritance, template info
+- **Method**: Method definitions with parameters, virtuality, const-ness
+- **Field**: Public fields with type and location information
+- **Constructor/Destructor**: Special member functions
+- **Enum**: Enumeration types with values
+- **ArraySpecialization**: Specialized array type information
+
+### Information Extracted from C Headers
+
+The system extracts comprehensive information from spine-cpp headers:
+
+#### Class/Struct Information:
+- **Members**: All public methods, fields, constructors, destructors
+- **Inheritance**: Supertype relationships and template inheritance
+- **Properties**: Abstract status, template parameters, virtual methods
+- **Location**: File and line number information for each member
+
+#### Method Details:
+- **Signatures**: Return types, parameter types and names
+- **Modifiers**: Static, virtual, pure virtual, const qualifiers
+- **Inheritance**: Which supertype the method comes from
+
+#### Type Analysis:
+- **Template Detection**: Identifies and handles template types
+- **Interface Classification**: Distinguishes pure interfaces from concrete classes
+- **Inheritance Mapping**: Builds complete inheritance hierarchies
+
+#### Validation Information:
+- **Conflict Detection**: Const/non-const method conflicts
+- **Type Support**: Multi-level pointers, unsupported patterns
+- **Name Conflicts**: Method/type name collisions
+
+### How This Information Can Be Used for Generating Bindings
+
+The extracted information enables sophisticated binding generation for multiple target languages:
+
+#### 1. **Type System Mapping**
+- **Opaque Types**: C++ classes become opaque pointers (`Skeleton*` → `spine_skeleton`)
+- **Primitive Passthrough**: Direct mapping for int, float, bool, etc.
+- **Special Conversions**: `String` → `const char*`, `PropertyId` → `int64_t`
+- **Array Specializations**: `Array<T>` → `spine_array_T` with full CRUD operations
+
+#### 2. **Inheritance Support**
+The inheritance maps enable proper class hierarchies in target languages:
+- **Single Inheritance**: `extends` relationships for concrete parent classes
+- **Interface Implementation**: `mixins` for pure interface types
+- **Conflict Detection**: Prevents multiple concrete inheritance (unsupported in many languages)
+
+#### 3. **Memory Management**
+- **Constructor Wrapping**: Generates `spine_type_create()` functions
+- **Destructor Wrapping**: Generates `spine_type_dispose()` functions
+- **RTTI Support**: Maintains Spine's custom RTTI system for type checking
+
+#### 4. **Method and Field Access**
+- **Method Wrapping**: `Class::method()` → `spine_class_method()`
+- **Field Accessors**: Automatic getter/setter generation for public fields
+- **Parameter Marshaling**: Proper conversion between C++ and C calling conventions
+
+#### 5. **Language-Specific Features**
+- **Nullability**: Identifies nullable pointer types vs non-null references
+- **Array Operations**: Complete CRUD operations for specialized array types
+- **Enum Conversion**: Systematic enum name conversion with prefixes
+
+#### 6. **Validation and Safety**
+The extensive validation ensures generated bindings are safe and correct:
+- **Type Safety**: Prevents unsupported type patterns
+- **Name Conflicts**: Ensures no function name collisions
+- **Interface Compliance**: Verifies inheritance patterns work in target languages
+
+This comprehensive system allows binding generators for languages like Dart, Swift, Java, etc. to automatically create type-safe, idiomatic APIs that properly expose the full Spine C++ functionality while respecting each language's conventions and constraints.
+
+## Dart Codegen Implementation Analysis
+
+Based on my analysis of the Dart codegen implementation in spine-flutter, here's a comprehensive breakdown of how it works and the patterns that would be applicable to Swift:
+
+### **1. Architecture Overview**
+
+The Dart codegen follows a clean layered architecture:
+
+1. **Input**: Uses the `generate()` function from spine-c's codegen to get the C Intermediate Representation (CIR)
+2. **Transform**: Converts CIR to clean Dart model using `DartWriter`
+3. **Generate**: Creates idiomatic Dart code from the model
+4. **Output**: Writes individual files plus arrays and exports
+
+### **2. How It Uses the generate() Output**
+
+From `/Users/badlogic/workspaces/spine-runtimes/spine-flutter/codegen/src/index.ts`:
+
+```typescript
+const { cTypes, cEnums, cArrayTypes, inheritance, supertypes, subtypes, isInterface } = await generate();
+```
+
+The codegen consumes:
+- **cTypes**: All C wrapper types with nullability information
+- **cEnums**: Enum definitions  
+- **cArrayTypes**: Array specializations
+- **inheritance**: Extends/implements relationships
+- **isInterface**: Map of which types are pure interfaces
+- **supertypes**: Type hierarchy for RTTI-based instantiation
+
+### **3. Type Hierarchy and Inheritance Handling**
+
+The implementation elegantly handles complex inheritance:
+
+#### **Concrete Classes** (like `Skeleton`)
+```dart
+class Skeleton {
+  final Pointer<spine_skeleton_wrapper> _ptr;
+  Skeleton.fromPointer(this._ptr);
+  Pointer get nativePtr => _ptr;
+  // ... methods
+}
+```
+
+#### **Abstract Classes** (like `Attachment`)
+```dart
+abstract class Attachment {
+  final Pointer<spine_attachment_wrapper> _ptr;
+  Attachment.fromPointer(this._ptr);
+  Pointer get nativePtr => _ptr;
+  // ... concrete methods with RTTI switching
+}
+```
+
+#### **Interfaces** (like `Constraint`)
+```dart
+abstract class Constraint implements Update {
+  @override
+  Pointer get nativePtr;
+  ConstraintData get data;  // abstract getters
+  void sort(Skeleton skeleton);  // abstract methods
+  static Rtti rttiStatic() { /* implementation */ }
+}
+```
+
+**Key Pattern**: The implementation uses single inheritance (`extends`) for concrete parent classes and multiple interface implementation (`implements`) for mixins.
+
+### **4. Nullability Handling**
+
+The nullability system is comprehensive and automatic:
+
+#### **Nullable Return Values**
+```dart
+Bone? get rootBone {
+  final result = SpineBindings.bindings.spine_skeleton_get_root_bone(_ptr);
+  return result.address == 0 ? null : Bone.fromPointer(result);
+}
+```
+
+#### **Nullable Parameters**
+```dart
+void sortBone(Bone? bone) {
+  SpineBindings.bindings.spine_skeleton_sort_bone(
+    _ptr, 
+    bone?.nativePtr.cast() ?? Pointer.fromAddress(0)
+  );
+}
+```
+
+#### **Non-nullable Types**
+```dart
+String get name {  // No '?' - guaranteed non-null
+  final result = SpineBindings.bindings.spine_attachment_get_name(_ptr);
+  return result.cast<Utf8>().toDartString();
+}
+```
+
+The nullability information flows from:
+1. **C++ analysis**: Pointers can be null, references cannot
+2. **CIR encoding**: `returnTypeNullable` and `parameter.isNullable` flags
+3. **Dart generation**: Automatic `?` types and null checks
+
+### **5. Generated Code Structure**
+
+#### **Class Structure**
+Each generated class follows a consistent pattern:
+- License header
+- Imports (FFI, bindings, related types)
+- Class declaration with inheritance
+- Pointer field (`_ptr`)
+- Pointer constructor (`fromPointer`)
+- Native pointer getter
+- Factory constructors
+- Properties (getters/setters)
+- Methods
+- Dispose method (for concrete classes)
+
+#### **Method Generation**
+The codegen intelligently detects:
+- **Getters**: `spine_skeleton_get_data` → `SkeletonData get data`
+- **Setters**: `spine_skeleton_set_x` → `set x(double value)`
+- **Methods**: `spine_skeleton_update` → `void update(double delta)`
+- **Constructors**: `spine_skeleton_create` → `factory Skeleton()`
+
+#### **Method Overloading**
+Handles C's numbered method pattern:
+```dart
+// spine_skeleton_set_skin_1 → setSkin(String)
+void setSkin(String skinName) { ... }
+
+// spine_skeleton_set_skin_2 → setSkin2(Skin?)  
+void setSkin2(Skin? newSkin) { ... }
+```
+
+### **6. RTTI-Based Instantiation**
+
+For abstract types, the codegen generates runtime type checking:
+
+```dart
+Attachment? getAttachment(String slotName, String attachmentName) {
+  final result = SpineBindings.bindings.spine_skeleton_get_attachment_1(...);
+  if (result.address == 0) return null;
+  
+  final rtti = SpineBindings.bindings.spine_attachment_get_rtti(result);
+  final className = SpineBindings.bindings.spine_rtti_get_class_name(rtti)
+      .cast<Utf8>().toDartString();
+      
+  switch (className) {
+    case 'spine_region_attachment':
+      return RegionAttachment.fromPointer(result.cast());
+    case 'spine_mesh_attachment':
+      return MeshAttachment.fromPointer(result.cast());
+    // ... other concrete types
+    default:
+      throw UnsupportedError('Unknown concrete type: $className');
+  }
+}
+```
+
+### **7. Array Types**
+
+Arrays get specialized wrapper classes extending `NativeArray<T>`:
+
+```dart
+class ArrayFloat extends NativeArray<double> {
+  final bool _ownsMemory;
+  
+  ArrayFloat.fromPointer(Pointer<spine_array_float_wrapper> ptr, {bool ownsMemory = false})
+      : _ownsMemory = ownsMemory, super(ptr);
+      
+  factory ArrayFloat() { /* create constructor */ }
+  
+  @override
+  int get length { /* implementation */ }
+  
+  @override  
+  double operator [](int index) { /* implementation */ }
+  
+  void dispose() { /* only if _ownsMemory */ }
+}
+```
+
+**Key Features**:
+- Memory ownership tracking
+- Bounds checking
+- Null handling for object arrays
+- Factory constructors for creation
+
+### **8. Enum Generation**
+
+Enums use Dart's modern enum syntax with values:
+
+```dart
+enum BlendMode {
+  normal(0),
+  additive(1),
+  multiply(2),
+  screen(3);
+
+  const BlendMode(this.value);
+  final int value;
+
+  static BlendMode fromValue(int value) {
+    return values.firstWhere(
+      (e) => e.value == value,
+      orElse: () => throw ArgumentError('Invalid BlendMode value: $value'),
+    );
+  }
+}
+```
+
+### **9. Special Patterns for Swift**
+
+Several patterns from the Dart implementation would translate well to Swift:
+
+#### **Memory Management**
+```swift
+// Swift equivalent of pointer wrapping
+class Skeleton {
+    private let ptr: OpaquePointer
+    
+    init(fromPointer ptr: OpaquePointer) {
+        self.ptr = ptr
+    }
+    
+    var nativePtr: OpaquePointer { ptr }
+    
+    deinit {
+        spine_skeleton_dispose(ptr)
+    }
+}
+```
+
+#### **Optional Handling**
+```swift
+// Swift's optionals map naturally to Dart's nullability
+var rootBone: Bone? {
+    let result = spine_skeleton_get_root_bone(ptr)
+    return result == nil ? nil : Bone(fromPointer: result!)
+}
+```
+
+#### **RTTI Switching**
+```swift
+// Swift's switch with associated values
+func getAttachment(_ slotName: String, _ attachmentName: String) -> Attachment? {
+    guard let result = spine_skeleton_get_attachment_1(ptr, slotName, attachmentName) else {
+        return nil
+    }
+    
+    let rtti = spine_attachment_get_rtti(result)
+    let className = String(cString: spine_rtti_get_class_name(rtti))
+    
+    switch className {
+    case "spine_region_attachment":
+        return RegionAttachment(fromPointer: result)
+    case "spine_mesh_attachment":
+        return MeshAttachment(fromPointer: result)
+    default:
+        fatalError("Unknown concrete type: \(className)")
+    }
+}
+```
+
+#### **Protocol-Based Interfaces**
+```swift
+// Swift protocols map well to Dart interfaces
+protocol Constraint: Update {
+    var nativePtr: OpaquePointer { get }
+    var data: ConstraintData { get }
+    func sort(_ skeleton: Skeleton)
+    var isSourceActive: Bool { get }
+}
+```
+
+### **10. Key Takeaways for Swift Implementation**
+
+1. **Consistent Architecture**: Use the same 3-step process (input CIR → transform → generate)
+2. **Nullability Mapping**: Leverage Swift's optionals to match CIR nullability exactly  
+3. **Memory Management**: Use automatic reference counting with `deinit` for cleanup
+4. **Type Safety**: Generate compile-time safe wrappers around C pointers
+5. **RTTI Handling**: Use Swift's powerful switch statements for type resolution
+6. **Protocol Orientation**: Use Swift protocols for interfaces/mixins
+7. **Value Types**: Use Swift enums with raw values for C enums
+8. **Collection Types**: Create Array wrapper classes similar to Dart's approach
+9. **Method Overloading**: Swift's native overloading can handle numbered C methods more elegantly
+10. **Property Synthesis**: Use Swift's computed properties for getters/setters
+
+The Dart implementation provides an excellent blueprint for creating idiomatic, type-safe wrappers around the C API while maintaining full compatibility with the underlying spine-c layer.
+
+## Existing Swift Implementation Analysis
+
+### 1. Current Code Generation Approach (Python-based)
+
+**File**: `/Users/badlogic/workspaces/spine-runtimes/spine-cpp/spine-cpp-lite/spine-cpp-lite-codegen.py`
+
+The current generator uses a Python script that:
+
+- **Parses C++ header file** (`spine-cpp-lite.h`) using regex patterns to extract:
+  - Opaque types (between `@start: opaque_types` and `@end: opaque_types`)
+  - Function declarations (between `@start: function_declarations` and `@end: function_declarations`) 
+  - Enums (between `@start: enums` and `@end: enums`)
+
+- **Type mapping** approach:
+  ```python
+  supported_types_to_swift_types = {
+      'void *': 'UnsafeMutableRawPointer',
+      'const utf8 *': 'String?',
+      'uint64_t': 'UInt64',
+      'float *': 'Float?',
+      'float': 'Float',
+      'int32_t': 'Int32',
+      'utf8 *': 'String?',
+      'int32_t *': 'Int32?',
+      'uint16_t *': 'UInt16',
+      'spine_bool': 'Bool'
+  }
+  ```
+
+- **Swift class generation pattern**:
+  - Each opaque type becomes a Swift class inheriting from `NSObject`
+  - Uses `@objc` and `@objcMembers` for Objective-C compatibility
+  - Internal `wrappee` property holds the C++ pointer
+  - Automatic generation of `isEqual` and `hash` methods
+  - Smart getter/setter detection for computed properties
+
+### 2. Current Generated Code Structure
+
+**File**: `/Users/badlogic/workspaces/spine-runtimes/spine-ios/Sources/Spine/Spine.Generated.swift`
+
+The generated code follows these patterns:
+
+- **Type aliases for enums**:
+  ```swift
+  public typealias BlendMode = spine_blend_mode
+  public typealias MixBlend = spine_mix_blend
+  // etc.
+  ```
+
+- **Class structure**:
+  ```swift
+  @objc(SpineTransformConstraintData)
+  @objcMembers
+  public final class TransformConstraintData: NSObject {
+      internal let wrappee: spine_transform_constraint_data
+      
+      internal init(_ wrappee: spine_transform_constraint_data) {
+          self.wrappee = wrappee
+          super.init()
+      }
+      
+      public override func isEqual(_ object: Any?) -> Bool {
+          guard let other = object as? TransformConstraintData else { return false }
+          return self.wrappee == other.wrappee
+      }
+      
+      public override var hash: Int {
+          var hasher = Hasher()
+          hasher.combine(self.wrappee)
+          return hasher.finalize()
+      }
+  }
+  ```
+
+- **Smart property generation**:
+  - Detects getter/setter pairs and creates computed properties
+  - Handles array types with companion `get_num_*` functions
+  - Boolean conversion (`spine_bool` ↔ Swift `Bool`)
+  - Optional handling with `flatMap`
+
+### 3. Extensions and Manual Code
+
+**File**: `/Users/badlogic/workspaces/spine-runtimes/spine-ios/Sources/Spine/Spine.Generated+Extensions.swift`
+
+Contains manually written extensions that provide:
+- Async loading methods (`fromBundle`, `fromFile`, `fromHttp`)
+- Swift-friendly error handling with `SpineError`
+- Integration with iOS types (`UIImage`, `CGRect`)
+- Memory management and resource disposal
+- SwiftUI integration
+
+### 4. Module Map Setup
+
+**Files**: 
+- `/Users/badlogic/workspaces/spine-runtimes/spine-ios/Sources/SpineCppLite/include/module.modulemap`
+- `/Users/badlogic/workspaces/spine-runtimes/spine-ios/Sources/SpineShadersStructs/module.modulemap`
+
+Simple module maps that expose C++ headers to Swift:
+```
+module SpineCppLite {
+    header "spine-cpp-lite.h"
+    export *
+}
+```
+
+### 5. TypeScript Infrastructure (Newer)
+
+The codebase shows transition to TypeScript-based generation:
+- Uses shared `spine-c-codegen` package
+- More sophisticated parsing and IR generation
+- Better type safety and maintainability
+
+## Recommendations
+
+### What to Keep:
+1. **Module map approach** - Simple and effective for C++ interop
+2. **Opaque pointer wrapping pattern** - Safe and follows Swift best practices
+3. **NSObject inheritance** - Good for Objective-C compatibility
+4. **Smart getter/setter detection** - Creates idiomatic Swift APIs
+5. **Array handling with num functions** - Proper memory management
+6. **Extensions pattern** - Separates generated from manual code
+
+### What Needs Improvement:
+1. **Move from Python to TypeScript** - Align with other runtimes
+2. **Better error handling** - More sophisticated null analysis
+3. **Memory management** - Explicit lifetime management
+4. **Documentation generation** - Add doc comments to generated code
+5. **Testing integration** - Generate test scaffolding
+
+### Swift-Specific Patterns to Maintain:
+1. **Sendable conformance** - For modern Swift concurrency
+2. **@objc compatibility** - For mixed Objective-C/Swift projects
+3. **Result types** - For better error handling
+4. **Async/await integration** - Already present in extensions
+5. **Property wrappers** - Could be useful for resource management
+
+### Suggested Architecture:
+1. Use the existing TypeScript infrastructure from `spine-c-codegen`
+2. Create Swift-specific type mappings and templates
+3. Generate both the main classes and extension scaffolding
+4. Maintain backward compatibility with existing APIs
+5. Add modern Swift features (Sendable, async/await, Result types)
+
+The current implementation is solid but could benefit from modernization and alignment with the TypeScript-based generation approach used elsewhere in the codebase.

+ 76 - 0
todos/work/2025-07-31-16-50-17-generate-swift-bindings/task.md

@@ -0,0 +1,76 @@
+# Generate bindings for Swift from spine-c
+**Status:** InProgress
+**Agent PID:** 43132
+
+## Original Todo
+Generate bindings for Swift from spine-c generate() like dart-writer.ts
+
+## Description
+Create a TypeScript-based Swift bindings generator that uses the spine-c codegen infrastructure to generate idiomatic Swift code. The generator will replace the existing Python-based generator and produce type-safe Swift wrappers around the spine-c API, similar to how the Dart generator works. It will generate classes with proper inheritance hierarchy, nullability annotations, memory management, and maintain compatibility with the existing Swift API while adding modern Swift features.
+
+Read [analysis.md](./analysis.md) in full for analysis results and context.
+
+## Implementation Plan
+- [x] Create the Swift codegen package structure (spine-ios/codegen/)
+   - Set up TypeScript project with package.json, tsconfig.json
+   - Add dependency on spine-c-codegen package
+   - Create src/index.ts as entry point
+   - Create src/swift-writer.ts for Swift code generation
+
+- [x] Implement SwiftWriter class (spine-ios/codegen/src/swift-writer.ts)
+   - Use DartWriter as reference for structure but implement Swift-specific patterns
+   - Map C types to Swift types (OpaquePointer, String, Bool, etc.)
+   - Handle nullability with Swift optionals
+   - Generate proper class/protocol inheritance
+
+- [ ] Generate Swift classes from CIR
+   - Concrete classes with NSObject inheritance and @objc annotations
+   - Abstract classes with proper subclass requirements
+   - Protocols for interface types (like Constraint, Update)
+   - Proper memory management with deinit methods
+
+- [ ] Generate Swift enums
+   - Use Swift enums with raw values for C enums
+   - Add fromValue static methods for reverse lookup
+   - Generate proper case names from C enum values
+
+- [ ] Generate array wrapper types
+   - Create Swift array wrapper classes for each spine_array_* type
+   - Implement subscript operators and collection protocols
+   - Handle memory ownership and disposal
+
+- [ ] Handle RTTI-based instantiation
+   - Generate switch statements for abstract type instantiation
+   - Map C++ class names to Swift concrete types
+   - Handle unknown types gracefully
+
+- [ ] Generate method implementations
+   - Convert C function calls to Swift methods
+   - Handle method overloading (numbered C methods)
+   - Generate computed properties for getter/setter pairs
+   - Add proper null checking and optional unwrapping
+
+- [ ] Create output file structure
+   - Generate individual .swift files per type
+   - Create Arrays.swift for all array types
+   - Create Enums.swift for all enum types
+   - Generate main exports file
+
+- [ ] Add build integration
+   - Create build script to run the generator
+   - Update spine-ios Package.swift to include generated files
+
+- [ ] Create minimal test package (spine-ios/test/) inspired by spine-flutter/test
+   - Minimal Package.swift for quick iteration
+   - Test harness without full spine-ios dependencies
+   - Basic examples to verify generated bindings work
+
+- [ ] Test the generated code
+   - Verify compilation with Swift compiler
+   - Test against the minimal test package
+   - Ensure backward compatibility with existing API
+
+- [ ] User test: Generate Swift bindings and verify they compile and work correctly in the test package
+
+## Notes
+[Implementation notes]