• Runtimes
  • Spine-c, shear and matrices

  • تم التحرير
Related Discussions
...

Hello

I'm using spine-c along with sdl-gpu for my game engine. Everything has gone fine so far but I've ran into problems when I want to use a slot's rendering information to render text instead. This is definitely me at fault as I'm not great at matrices etc.

I have it set up so I have a slot that I track, then when I go to render this slot I instead replace it with some text. I get the slot's bone and use the worldPositionX/Y, colour etc and even this works great. It falls over when I want to shear the text though. sdl-gpu using a similar matrix system to opengl and they're an array of 16 floats like matrix[16].

I've tried a couple of things...

1) Use the matrix values directly.
a) Create an sdl-gpu identity matrix
b) Translate it by the bone's worldX/Y
c) Get the bone's 2x2 matrix (a,b,c,d).
d) Set bone's matrix on the sdl matrix.
matrix[0] = bone->a;
matrix[1] = bone->b;
matrix[4] = -bone->c;
matrix[5] = -bone->d;
(c and d are inverted so we rotate the correct way).
e) render the text at 0,0


This works when shear isn't involved, but as soon as it does it doesn't look correct. On further investigation it looks like the shearing is back to front. ie, if I set shearX to 45 and shearY to 0 in Spine, the result I get in game is what it would look like if I set shearX to 0 and shearY to 45 in Spine. And vice-versa.


2) Trying to use a shear matrix
a) Create an sdl-gpu identity matrix
b) Translate it by the bone's worldX/Y
c) Create an sdl-gpu identity matrix for the xShear
d) Set the xShear value onto the matrix
xshear[1] = (bone->shearX);
e) Create an sdl-gpu identity matrix for the yShear
f) Set the yShear value onto the matrix
yshear[1] = (bone->shearY);
g) Multiply the 3 matrices together
h) Rotate the matrix by worldRotationX
i) Render the text 0,0


Just wrong in different ways when it starts to shear.


3) Without using shear I would...
a) Translate the current matrix by worldX/Y
b) Scake the current matrix by scaleX/Y
c) Rotate the current matrix by worldRotationX
d) Render the text at 0,0


This worked but obviously doessn't take into account the shearing.

Can anyone advise on the best way forward for this? I can live without shearing but I just thought it would be nice to have while I'm working on it.

Basically I need to know how to convert a bone's a,b,c,d,worldX,worldY to something like an opengl matrix. It would let me parent other game objects to bones and have the artist control the animations.

Failing that, how do I use the worldRotationX and worldRotationY values?

Thanks!

Scott

Spine's shear is not the same as applying a shear affine matrix. This thread describes it and has some pictures that show why it's done differently.

The relevant code is here:
https://github.com/EsotericSoftware/spine-runtimes/blob/3.8/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java#L131

While the bone's world transform matrix (a,b,c,d) is computed differently than using a shear affine matrix, the result is a standard matrix (eg can be used by OpenGL) and has the shear encoded, so I'm not sure why using that doesn't work for you. Shear is achieved by the axes not being perpendicular, so in the matrix you don't have both a shear X and Y, instead you have whatever direction the X axis points and then the Y axis points 90 degrees from that when there is no shear.

Bone getWorldRotationX and Bone getWorldRotationY extract the rotation from the matrix. That results in correct values for the directions the axes point, though they may be a little surprising. There can be multiple ways to achieve the same end result, so the extracted values may not match the values used to construct the matrix. For example -1,-1 scale is the same as 180 rotation and the matrix does not contain any information about which was used to construct it.

You can try starting with your logic in #3:

a) Translate the current matrix by worldX/Y
b) Scake the current matrix by scaleX/Y
c) Rotate the current matrix by worldRotationX

With this the Y axis will be 90 degrees from worldRotationX (no shear), so next apply a shear affine matrix by the difference between worldRotationX and worldRotationY.

Hi Nate

Thanks for the quick reply. Some interesting information there. I've gone back to setting the bone's abcd params in, as like you said, I should be able to get this to work. The code I have looks like this...

float matrix[16];

GPU_MatrixIdentity(matrix);
GPU_MatrixTranslate(matrix, bone->worldX, bone->worldY, 0.0f);

matrix[0] = bone->a;
matrix[1] = bone->b;
matrix[4] = bone->c;
matrix[5] = bone->d;

GPU_LoadMatrix(matrix);

First thing wrong is that it is upside down (I have spBoneYUp = true). If I invert bone->c and bone->d it flips it the right way up - or if I multiply it with an invert matrix. When it comes to shearing I have nothing else affecting it except a 45 shearX value and it looks like the following image. It's like the shearX and Y are the wrong way around.

I've then broken everything out into its own matrix based on the code you linked and can replicate the above result. If I then swap the shearX and Y values it shears in the correct direction but the angles are no longer parallel, so it looks like this...

So that's two methods. I also tried what you suggested but must be doing something wrong. The code for that looks like...

float angleX = (spBone_getWorldRotationX(bone));
float angleY = (spBone_getWorldRotationY(bone));

float shear2[16];
GPU_MatrixIdentity(shear2);
shear2[4] = Maths_Deg2Rad(angleY-angleX);

GPU_MatrixScale(matrix, bone->scaleX, bone->scaleY, 1.0f);
GPU_MatrixRotate(matrix, angleX, 0.0f, 0.0f, 1.0f);
GPU_MultiplyAndAssign(matrix, shear2); 

And this ends up with a permanent skew on it.

Thanks for your help on this!

Scott

spBone_setYDown already flips c and d (for the root bone and so effects all bones), so a little odd that you need to flip it again.

It's hard to suggest solutions without being able to try things out, but I'd start with an identity matrix and apply say 45 degrees of shear. Once that works you should be able to get your last code listing to work. It may be a problem with order of operations, or put the shear on the other axis (shear[1]), or something else. Also check the angle you are using to shear. The angle between the axes should be 90 when there is no shear, so you'd shear by angleY - angleX - 90.