Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to load rotated and/or trimmed images from an atlas?  (Read 3637 times)

0 Members and 1 Guest are viewing this topic.

Arsonide

  • Newbie
  • *
  • Posts: 4
    • View Profile
How to load rotated and/or trimmed images from an atlas?
« on: July 01, 2013, 07:46:47 am »
Forgive me if this has been asked before, but I did some extensive searching before posting. I have an atlas generation program, and I'm parsing through it's output, loading the texture from a file, and creating sprites from sub-rectangles within the texture.

Consider the following image:


Notice how TexturePacker has rotated some of the sprites, to save image space? It's also flagged them as rotated, and kept track of the angle at which they were rotated, so theoretically, all the information is there to load them like any other image. How do I do that? They are only rotated at 90 degree angles, but how do I load a rotated texture? Is it possible without any weird intermediary steps, like subclassing sprite as offsetSprite and just showing it at an offset constantly? I'd like to do it at the sprite creation stage, if possible.

Anyway, you guys get the idea. Thanks.
« Last Edit: August 02, 2013, 11:11:53 pm by Arsonide »

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: How to load rotated images from an atlas?
« Reply #1 on: July 01, 2013, 10:05:03 am »
Since you want to perform the rotation at sprite creation, just grab the pixels of the subrect you are interested in and rotate them yourself. This shouldn't be too hard since they are only rotated at 90 degree angles. Create a new image and set the pixels accordingly. If you intend to create multiple sprites quite often, it might be wise to save the intermediate image somewhere.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

Arsonide

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: How to load rotated and/or trimmed images from an atlas?
« Reply #2 on: August 02, 2013, 11:30:45 pm »
Alright, I got the rotations working. That was simply a matter of rotating the array of texCoords, so all the corners moved. I have another issue now. Trimming. I've updated the topic to reflect that. There are a lot of posts about this, but most of them are not relevant to my specific situation, for reasons I will explain.

I'm really just stuck on this because matrix transformations confuse me. It's frustrating that I haven't been able to figure this out yet after a few days of hammering on it, because there's a lot of posts about it. I realize that this question is somewhat out of the scope of SFML's feature set, but I am under the impression that it's something that is simple to add, that's just going over my head. So I'll ask anyway.

What I want is very simple. TexturePacker can trim sprite textures, saving memory by a pretty good amount on the atlas. It removes the transparent areas around the texture, but saves the offset from the top left of the image to the top left of the actual pixels in the image, allowing you to position it in your engine properly, without wasting memory on transparent data. It's a cool feature and I really want to use it. The problem is that sf::Sprite uses texture size for it's transform and bounds, and I want a class that lets me basically ignore the fact that this trimming has occurred. That sentence is structured weird, so I made an image to elaborate on this.



In this image, the texture data of a trimmed texture is the green box, the original texture data before trimming is the red box, the transparent area is blue, and the offset is the yellow line. I want a class that gives me bounds information of, and allows me to transform and rotate the RED box, sending all of these actions to the green box, as it's child. That way it's like the trimming never happened, but it did. The green square is simply offset, so it won't be MOVING in it's local space. It's just sitting at an offset. I understand that this is basically a (very small) sf::Transform hierarchy.

I already know that in the draw function of a Drawable, this is really easy to do this by using RenderStates, and combining those with the transform of the Drawable. The problem with this is that these SpriteBatchItems are not Drawable, they are just Transformables with vertex information that are sent to the SpriteBatch, which is Drawable. If I were to make them Drawable just for this feature, I feel that might affect performance slightly, and I'm doing all this work to make stuff perform as fast as possible. I've done quite a bit of research on this, and I do know about the hierarchy you can form in the draw function, but I don't think that is relevant to this particular situation, due to the objects not being Drawable.

Usually the answer is "use sf::Transform", and I'm pretty sure that I can indeed solve this problem with one sf::Transformable parent, one sf::Transform representing the offset, and a combination operation any time the transform moves. I'm just asking how, because I've been asking, looking through the documentation, and testing hacky solutions for like two days now, and I feel like I've hit a wall with what originally seemed to be a simple problem. I think one reason is that I frequently mix up a Transform and a transformed Rect, in my head. One of the hacky solutions I tried was using setOrigin in an abusive manner overloading it's set and get functions to silently add and remove the offset, but this felt dirty and the rotations didn't feel right for some reason, I'm not sure why.

Anyway. I would really appreciate help getting this PROPERLY implemented, because I want to start using this to actually do stuff. Once I get this stuff written, I plan on dropping it on the wiki for everyone else. I just have to get it written first. Thanks again.

TLDR: I need a small (size 2) child/parent hierarchy of sf::Transforms/sf::Transformables. I can't use the draw function, because the objects are not drawable. Halp.
« Last Edit: August 02, 2013, 11:34:51 pm by Arsonide »

Arsonide

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: How to load rotated and/or trimmed images from an atlas?
« Reply #3 on: August 03, 2013, 02:36:19 pm »
A quick update: I had a breakthrough with this problem, and no longer need assistance.



I will post more information later, along with the source, once I get all this up and running smoothly, which might take a while, but for now, suffice it to say that it required a Transformable and four points transformed using getTransform().transformPoint(), which is exactly what Sprite uses.

NoobsArePeople2

  • Newbie
  • *
  • Posts: 47
    • View Profile
    • Email
Re: How to load rotated and/or trimmed images from an atlas?
« Reply #4 on: November 21, 2013, 08:37:57 pm »
I will post more information later, along with the source, once I get all this up and running smoothly, which might take a while, but for now, suffice it to say that it required a Transformable and four points transformed using getTransform().transformPoint(), which is exactly what Sprite uses.

Did you ever post about this? If you did can you provide a link? If not could you provide more details? I'm looking into doing exactly the same thing and I'd love to have a look at how you accomplished this. Thanks!

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: How to load rotated and/or trimmed images from an atlas?
« Reply #5 on: November 21, 2013, 08:58:16 pm »
Did you ever post about this? If you did can you provide a link? If not could you provide more details? I'm looking into doing exactly the same thing and I'd love to have a look at how you accomplished this. Thanks!

Use setOrigin on your sprite with a negative value of what the bound box width/height should be.
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

NoobsArePeople2

  • Newbie
  • *
  • Posts: 47
    • View Profile
    • Email
Re: How to load rotated and/or trimmed images from an atlas?
« Reply #6 on: November 23, 2013, 06:30:41 pm »
Use setOrigin on your sprite with a negative value of what the bound box width/height should be.

Here's what I ended up doing. I'm using TexturePacker to create my texture atlas and like the OP I'm using its "trim" feature which removes transparent pixels from source images so that more images can be packed into the atlas while also storing information like "sourceSize" (original size) and "spriteSourceSize" (essentially the offset for the trimmed sprite relative to the original).

In this instance I'm also using a modified version of the AnimatedSprite class from the wiki as I'm working on my game's animations at the moment.

For my AnimatedSprite I'm setting the origin at the center of the sprite as I need to be able to scale the sprite on the x-axis to get left/right versions of the sprite. I set the origin for the AnimatedSprite based on "sourceSize", or the original size of the graphic (i.e., setOrigin(sourceSize.w / 2, sourceSize.h / 2)). For this to work it's important that all graphics in the atlas have the same sourceSize (I suppose it could be made to work otherwise but using the same sourceSize for everything greatly simplifies my life).

Next I added a new class, AnimationFrame, that has three properties an sf::IntRect and two floats, "offsetX", and "offsetY". AnimationFrame replaces the sf::IntRect that is used to define frame sizes in the above-linked AnimatedSprite. AnimationFrame's sf::IntRect defines the bounds of the frame. The offset values are the x/y distances of the top left of the trimmed sprite from the top left of sourceSize. They allow me to properly position the trimmed sprite in the AnimatedSprite.

Finally, in AnimatedSprite::setFrame() the bounds of the frame are used to set the sf::Vertex positions that will later be used for drawing. I've updated this to add the offsetX and offsetY values from my AnimationFrame class to each vertex's position.

I've only had this up and running for a day or so but so far all seems well.