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

Author Topic: How to correctly use sf::Transformable and sf::Drawable?  (Read 4379 times)

0 Members and 1 Guest are viewing this topic.

CyanDog

  • Newbie
  • *
  • Posts: 15
    • View Profile
How to correctly use sf::Transformable and sf::Drawable?
« on: July 27, 2017, 05:26:04 pm »
Hello, I do not understand how to correctly use the sf::Transformable and sf::Drawable classes. My Car class inherits from them, and it overrides the draw() function. The car has a sf::Sprite member data.

The problem is that the sprite's global bounding box is not axis-aligned; when the car is rotated, so too is the sprite's bounding box.

The questions I have are:
  • What member data should a class that inherits from sf::Transformable and sf::Drawable contain? Sprites, rectangles, etc.?
  • Is my Car class supposed to implement the getLocalBounds() and getGlobalBounds() member functions?

Following the tutorial steps I now have the equivalent of this simplified code:

//
// Car.hpp
//
class Car : public sf::Transformable, public sf::Drawable
{
private:

    sf::Texture     texture;
    sf::Sprite      sprite;

public:

    Car()
    {
        // load texture, and
        // load sprite using texture
    }

private:

    void draw(sf::RenderTarget &target, sf::RenderStates states) const override
    {
        states.transform.combine(getTransform());
        target.draw(sprite, states);

        // and draw sprite.getGlobalBounds() rectangle
    }
};

//
// main.cpp
//
int main()
{
    // typical window code from tutorials

    Car car;

    car.rotate(45);

    // typical event and looping code from tutorials
    {
        render_window.draw(car); // <-- BUG: the bounding box is at 45 degrees!
    }
}
 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to correctly use sf::Transformable and sf::Drawable?
« Reply #1 on: July 27, 2017, 06:57:22 pm »
Quote
The problem is that the sprite's global bounding box is not axis-aligned; when the car is rotated, so too is the sprite's bounding box.
Because you draw the bounding box with the same transform as your sprite. Don't do that and it will be correctly drawn.
Laurent Gomila - SFML developer

CyanDog

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: How to correctly use sf::Transformable and sf::Drawable?
« Reply #2 on: July 27, 2017, 07:55:36 pm »
Your suggestion doesn't work in my case: the bounding box will be drawn in the (default?) upper left corner of the screen and will not be affected by the sprite's rotation.

Do I need to directly .rotate() and .move() the sprite when the Car is? That would be a problem.

EDIT: the problem was apparently solved by setting the position and rotation of the sprite and not combining the state transforms.

// inside a Car::update function
sprite.setPosition(getPosition());
sprite.setRotation(getRotation());

Let me know if this approach is wrong, thanks.
« Last Edit: July 27, 2017, 08:12:41 pm by CyanDog »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to correctly use sf::Transformable and sf::Drawable?
« Reply #3 on: July 27, 2017, 09:22:47 pm »
Quote
Your suggestion doesn't work in my case: the bounding box will be drawn in the (default?) upper left corner of the screen and will not be affected by the sprite's rotation.
Right, sorry. The correct approach would be to build the car's local bounding rect (which should be the same as the sprite's one, if it's the only entity it contains), and then transform it by the car's transform (car.getTransform()).

Quote
Let me know if this approach is wrong, thanks.
It is wrong. Your car is correctly transformed and drawn, right? So don't make the Car class more complicated than it should; your only problem is to draw the bounding rect with the correct transform, which my suggestion above should solve.
« Last Edit: July 27, 2017, 09:24:24 pm by Laurent »
Laurent Gomila - SFML developer

CyanDog

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: How to correctly use sf::Transformable and sf::Drawable?
« Reply #4 on: July 27, 2017, 11:23:40 pm »
The correct approach would be to build the car's local bounding rect (which should be the same as the sprite's one, if it's the only entity it contains), and then transform it by the car's transform (car.getTransform()).

I do not understand what I need to do. The car has a sf::Sprite and a sf::Texture, as data.
  • Do you mean that there should also be a sf::FloatRect to hold the bounding box data?
  • Or a rectangle entity from which to extract the bounding box? If yes, why is it wrong to use the sprite directly for this?
  • Or do you want me to take the local bounding box of the sprite and then transform it? But that doesn't make sense to me.

Simplified example code would probably be easier for me to understand at this point.

Hapax

  • Hero Member
  • *****
  • Posts: 3379
  • My number of posts is shown in hexadecimal.
    • View Profile
    • Links
Re: How to correctly use sf::Transformable and sf::Drawable?
« Reply #5 on: July 27, 2017, 11:38:15 pm »
The bounding boxes are usually communicated via sf::FloatRects.
If you take the local bounding rect of the sprite and apply the transform to that rectangle, you have an axis-aligned rectangle that bounds the transformed local rectangle.

(click to show/hide)
Selba Ward -SFML drawables
Cheese Map -Drawable Layered Tile Map
Kairos -Timing Library
Grambol
 *Hapaxia Links*

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: How to correctly use sf::Transformable and sf::Drawable?
« Reply #6 on: July 28, 2017, 07:59:05 am »
What is the global bounding rectangle? The local geometry of the entity, to which we apply the same transformations as the entity, so that it is defined in the final coordinates system.

So you need these two things:
- the local geometry of the entity is the sprite, since it's what internally composes your car entity; so the car's local bounding rect is just sprite.getLocalBounds()
- the transformations of your car entity are car.getTransform(), so the global bounds of the car can finally be computed with getTransform().transformRect(sprite.getLocalBounds()) (as already mentioned ;))

If you want to mimic SFML classes, you can define these functions in your Car class:
class Car
{
public:

    sf::FloatRect getLocalBounds() const {return sprite.getLocalBounds();}

    sf::FloatRect getGlobalBounds() const {return getTransform().transformRect(getLocalBounds());}
};
Laurent Gomila - SFML developer

CyanDog

  • Newbie
  • *
  • Posts: 15
    • View Profile
Re: How to correctly use sf::Transformable and sf::Drawable?
« Reply #7 on: July 28, 2017, 01:43:05 pm »
Thank you for your help. I have only minor questions now, because things seem to work fine.

My code currently works by drawing the sprite with a combined states (so the sprite entity itself is not transformed) and then it draws the sprite's local bounding box as transformed by the car's matrix.

Questions:
  • How should I handle wanting to center the sprite?
  • In sf::Drawable::draw() is it often that states.transform isn't the identity matrix?

Expanding on question 1, I'm currently doing setOrigin() in the car constructor and it works fine, but is it the right approach?

 

anything