• Runtimes
  • Spine-csharp and Skiasharp for .net9 MAUI games

Hello
Even if I'm an experienced mobile app developper, I'm new to game dev and Spine 2D animation.
I create an Android and iOS game with .net9 MAUI and Skiasharp. I've bought an essential Spine license to integrate cool 2D animated characters.
To render a skeleton (spineboy to start), I use Spine-csharp generic runtime, and I have to implement a kind of SkiasharpSkeletonRenderer.
This is where I need some help please.. I think I'm missing a little details, but after hours and hours, this is the only result I can reach :

Looks like regions orientation are wrong..
Here is my draw region code :
`private void DrawRegionAttachment(SKCanvas canvas, RegionAttachment regionAttachment, Slot slot)
{
Bone bone = slot.Bone;

// Calculate the cumulative position and rotation
float worldX = bone.WorldX;
float worldY = bone.WorldY;
float rotation = bone.WorldRotationX + regionAttachment.Rotation; // Add attachment's rotation to the bone's world rotation
float scaleX = bone.WorldScaleX * regionAttachment.ScaleX; // Combine bone and attachment scales (X)
float scaleY = bone.WorldScaleY * regionAttachment.ScaleY; // Combine bone and attachment scales (Y)

// Convert the rotation to radians for trigonometric calculations
float radians = rotation * MathF.PI / 180f;
float cos = MathF.Cos(radians);
float sin = MathF.Sin(radians);

// Calculate the world vertices
float offsetX = regionAttachment.X;
float offsetY = regionAttachment.Y;
float renderX = worldX + (offsetX * cos - offsetY * sin); // Transform X using rotation and offset
float renderY = worldY + (offsetX * sin + offsetY * cos); // Transform Y using rotation and offset (do not invert Y for SkiaSharp)

// Load the texture for the attachment
string textureName = "spineboy/spineboy-ess.png";
var bitmap = textureLoader.GetTexture(textureName);

// Get the texture dimensions and UV coordinates
float textureWidth = bitmap.Width;
float textureHeight = bitmap.Height;
var sourceRect = new SKRect(
    regionAttachment.Region.u * textureWidth,
    regionAttachment.Region.v * textureHeight,
    regionAttachment.Region.u2 * textureWidth,
    regionAttachment.Region.v2 * textureHeight
);

// Calculate the attachment's size
float attachmentWidth = regionAttachment.Width * scaleX;
float attachmentHeight = regionAttachment.Height * scaleY;

// Define the destination rectangle for rendering
var destRect = new SKRect(
    renderX - attachmentWidth / 2,
    renderY - attachmentHeight / 2,
    renderX + attachmentWidth / 2,
    renderY + attachmentHeight / 2
);

// Render the texture onto the canvas
using var paint = new SKPaint
{
    IsAntialias = true, // Enable anti-aliasing for smoother rendering
    FilterQuality = SKFilterQuality.High // Use high-quality filtering for the texture
};
canvas.DrawBitmap(bitmap, sourceRect, destRect, paint);

}
`

I would be thankfull for any help please !
Maxime

Related Discussions
...

It seems like you're on the right track with your implementation of a SkiasharpSkeletonRenderer for rendering Spine animations using SkiaSharp. The issue with the regions' orientation might be related to how the world vertices are being calculated or how the texture is being drawn. Here are a few things to check and adjust:

  1. Rotation and Scaling: Ensure that the rotation and scaling are applied correctly. The rotation should be in radians, and the scaling should be applied to both the width and height of the attachment.

  2. Texture Coordinates: Verify that the UV coordinates (u, v, u2, v2) are correctly mapped to the source rectangle. These should correspond to the correct portion of the texture atlas.

  3. World Vertices Calculation: Double-check the calculation of renderX and renderY. The transformation should correctly account for the bone's world position and the attachment's offset and rotation.

  4. Destination Rectangle: Ensure that the destination rectangle (destRect) is correctly calculated to position the attachment in the world space. The center of the attachment should align with the calculated world position.

  5. Texture Loading: Confirm that the texture is loaded correctly and matches the expected dimensions and format.

By carefully reviewing these aspects, you should be able to resolve the orientation issue. If the problem persists, consider logging intermediate values to debug the transformation and rendering process.

I have create a github so you can clone and debug espritm/Spine-skiasharp
I can't figure out my issue.
I think I have two issues :
1/ rotation are not appliyed.
2/ Y axis are inversed because 0, 0 is top left on skiasharp,

If you guys could help I would be very thankfull. I can't find the correct algorithm even with the help of Copilot 😢

Welcome! Getting rendering just right is always fun. You've gotten quite far. Having a scrambled skeleton is a lot better than a black screen with no clues. 😆

In your atlas I see rotate:90 and offsets which means the atlas was packed with rotation and whitespace stripping. I suggest repacking the atlas without those 2 features, which will simplify drawing the atlas regions. Once you get that working, repack with rotation and get that working, then repack with whitespace stripping and get that working, then finally repack with both and get that working.

When rendering even a region attachment, keep in mind it isn't always a rectangle -- it can be a rhomboid (ie a diamond shape). See this post:
https://esotericsoftware.com/forum/d/201-how-spine-calculates-the-image-vertices/3

You can avoid that in your skeletons and Spine can still be very useful, but ideally your renderer can handle drawing a quad that isn't a rectangle (as well as meshes, but eat the elephant one bite at a time! 🐘).