Unexpected rotation on multi-layered SpriteMap

Hello, everybody.
I’m a fairly new developer with Babylon.js and to learn a bit more I started developing a 2D version of a board game I like. This led me to use a multilayer SpriteMap to compose the game board itself with the floor, walls, obstacles and decorations in general… Placing one tile over another and playing with transparency.

To keep the texture sprite-sheet file light I reused some tiles by defining them in the TexturePacker JSON Array a couple of times, differentiating them using "rotated": false | true on some of them (not all tiles are suitable for rotation).

After a while, I started getting this error where -sometimes- some tiles were rotated even though the tile itself wasn’t defined as rotated at all!
I was stuck on this for a while, thinking I had done something wrong, somewhere in my code.

Then -by chance- I realized what the problem was. If you place a tile defined as rotated in a layer with a specific ID, all layers with a higher ID will be affected and their tiles will be flipped.
This behaviour is also “cumulative”:

  • If there is a rotated tiles under another rotated tile, it will be flipped “twice” (so it will be as if it had never been flipped at all).
  • If there are 2 rotated tiles under a non-rotated tile, it will be flipped twice (so you won’t notice it, because one flip cancels the other).

Here’s the link to the playground where you can reproduce it: https://playground.babylonjs.com/#YCY2IL#2244

Is this an intended behaviour?
Am I doing something wrong?

Thanks.

cc @Pryme8

Not sure off the top of my head but it does sound like a bug.

Thinking about it though I don’t know if we support rotated or trimmed. I think those where just packing options for the sprite packer program but I’m not sure if I ever added support for them (its been so long I just don’t remember). If I get a chance to look here soon then Ill relay better information to you.

1 Like

Yeah its a “bug”.

Each layer checks if its tile is rotated and if it does it rotates that cells UV (which since the prior stuff was drawn it rotates the tiles prior.

Let me think about a solution for this when I’m not on the clock and Ill get something pushed here.

Odds are it will be something like:

        if (frameData[2].z == 1.){
            tileUV.xy = fract(tUV).yx
        }else{
            tileUV.xy = fract(tUV).xy
        }

but that might end up doing the same thing, and if that is the case ill have to do an incrementor that keeps track of if its rotated or not already.

2 Likes

I have had zero personal time to look at this. It might get solved quicker if you do a quick branch and try this fix that I purposed. Sorry, dad life give me little chance to sit down and do personal work these days.

Hello! No worries!
I can lend you a helping hand with this.

1 Like

Fixes the issue and addition to this I’ve added helper function:

@Byloth can you give it it a try using the PG snapshot url above?

2 Likes

Hi @roland!
Thanks for your effort.

Your PG looks good to me… BUT…
I tried running my previous version of the PG using the snapshot linked in yours, like this: https://playground.babylonjs.com/?snapshot=refs%2Fpull%2F15646%2Fmerge#YCY2IL#2244

It seems to work: different layers don’t affect other anymore…
But I notice a different rotation of the tiles; so I did a test…
What would be the difference with a single-layer SpriteMap?

Here they are:

So…
I would say that -yes- it works and -for me- it doesn’t matter which way it will be rotated… Actually, I think it makes even more sense that it is rotated clockwise instead of flipped.
But (I don’t know if it matters) it could break compatibility with the previous versions.

Thanks again.

Let’s not break backwards compatibility. There is little to no information what “rotated” means so I guess I have to change the way it rotates the sprite. Would you mind to help me out with this and tell me how is it supposed to be so I don’t have to look it up? Is it 90CW or 90CCW, or whatever else? Thanks!

EDIT:
I’ve figured it out, hopefully :smiley:
Changes committed.

So this is the current status
Not rotated:
image

Rotated:
image

So it rotates CCW 90.

New, not rotated:
image

New, rotated:
image

CCW 90.

Should be OK!

TexurePacker docs:

As far as this is configurable when exporting from TexturePacker we need an option to configure this on our side.

I’ve added an option to the ISpriteMapOptions:

    /**
     * Rotation direction of the frame by 90 degrees.
     * Applied when the the frame's "rotated" parameter is true.
     */
    frameRotationDirection?: SpriteMapFrameRotationDirection;
3 Likes

Thank you so much Roland for taking the initiative on this. I owe you!

1 Like

I drink Jameson :joy::joy::joy::joy::joy::see_no_evil:

3 Likes

Haha, pm me where to send it to and we might be able to work something out. :face_with_monocle:

1 Like

It would be a damn expensive bottle :smiley:

Thanks to everybody for your effort!
I missed some messages… Sorry about that!

But I just saw that the PR as been merged! Awesome! :face_holding_back_tears:

Reviewing your screenshots, in one of them it looks like the sprite has just been rotated CCW by 90° (but not flipped along the Y axis)…
So, my concern was the actual flipping of the sprite, beyond the normal rotation (CW / CCW) and its backwards compatibilty.

I double-checked it with the previous version of Babylon.js and with both frameRotationDirection values and it looks fine: the sprites are both flipped and rotated.

You did a really great job, @roland!


I hope -one day- I can contribute to this amazing project too! :face_holding_back_tears:

1 Like