SFML community forums

Help => Graphics => Topic started by: foobarbaz on February 23, 2014, 09:22:04 pm

Title: Interpolating between 2 sf::Transforms
Post by: foobarbaz on February 23, 2014, 09:22:04 pm
Hi guys,

Is it possible to interpolate between 2 sf::Transform objects? I'm trying to implement the smoothing for my locked time step http://gafferongames.com/game-physics/fix-your-timestep/ (http://gafferongames.com/game-physics/fix-your-timestep/). I have had the locked time step there for a while, but the visual jitter is really bothering me now :P.

This would be trivial in a system where entities cannot be parented to each other, but in my case you can parent transforms, so I have a calculated global transform for every entity, and would like to interpolate between the current global transform and the previous one.

Is it possible to interpolate between 2 sf::Transform objects? If so, any hints as to how you'd do it?
Thanks in advance

EDIT:

Woops, I meant to post this in graphics, if Laurent wants to move it :P
Title: Re: Interpolating between 2 sf::Transforms
Post by: Nexus on February 23, 2014, 09:31:42 pm
It's impossible in general, or at least it wouldn't always yield what you expect. Assume a transform mirrors an image with respect to an axis (reflection). What would half the transform be?

What you can do, is extract affine transforms like translation, rotation and scale, interpolate them, and recompose them to a new transform matrix. An alternative way is to apply the matrix power, but then you need a linear algebra library.
Title: Re: Interpolating between 2 sf::Transforms
Post by: foobarbaz on February 23, 2014, 09:53:45 pm
What you can do, is extract affine transforms like translation, rotation and scale, interpolate them, and recompose them to a new transform matrix.

That's what I was afraid of. I'll have to recalculate the global transforms of all entities that moved and all of their children every frame. But I suppose I have to bite the bullet.

Thanks
Title: Re: Interpolating between 2 sf::Transforms
Post by: zsbzsb on February 23, 2014, 09:57:51 pm
tedsta, maybe you should show a complete example that seems to jitter. Because in my experience I have never seen any issues when omitting the interpolation part of that game loop.
Title: Re: Interpolating between 2 sf::Transforms
Post by: eigenbom on February 23, 2014, 11:22:02 pm
It definitely jitters if you run your physics at a lower framerate than your renderer, which is sometimes necessary. I agree with nexus and would split the important components out, like translation and rotation.
Title: Re: Interpolating between 2 sf::Transforms
Post by: foobarbaz on February 23, 2014, 11:23:01 pm
tedsta, maybe you should show a complete example that seems to jitter. Because in my experience I have never seen any issues when omitting the interpolation part of that game loop.

It's very subtle, and becomes more noticeable as the actual framerate gets lower. You can play around with the framerate cap and with and without smoothing. Maybe I've just gone crazy and my eyes deceive me, but I think the smoothing helps :P

#include <SFML/System/Vector2.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/RectangleShape.hpp>

const float lockStep = 1.f/60.f;

void step(sf::Vector2f& state, const float dt);

int main()
{
    // Setup the window
    sf::RenderWindow window;
    window.create(sf::VideoMode(800, 600), "SFML Lockstep Example");

    // Player states
    sf::Vector2f oldState;
    sf::Vector2f curState;

    // Player object
    sf::RectangleShape player(sf::Vector2f(32, 32));
    player.setFillColor(sf::Color::Red);

    // Delta time stuff
    sf::Clock dtClock;
    float dtAccum = 0.f;

    // Main loop
    while (window.isOpen())
    {
        // Framerate cap
        while (dtClock.getElapsedTime().asSeconds() < 1.f/50.f);

        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        // Calculate dt
        float dt = dtClock.restart().asSeconds();

        // Add to the dt accumulator
        dtAccum += dt;

        // While there's unsimulated time
        while (dtAccum >= lockStep)
        {
            dtAccum -= lockStep;
            oldState = curState;
            step(curState, lockStep);
        }

        float accumStepRatio = dtAccum / lockStep;

        sf::Vector2f state = curState*accumStepRatio + oldState*(1.f - accumStepRatio);

        // Smoothed state
        player.setPosition(state);

        // Non smoothed
        //player.setPosition(curState);

        window.clear(sf::Color::Black);
        window.draw(player);
        window.display();
    }

    return 0;
}

void step(sf::Vector2f& state, const float dt)
{
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::W))
        state.y -= 100.f*dt;
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::S))
        state.y += 100.f*dt;
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::A))
        state.x -= 100.f*dt;
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::D))
        state.x += 100.f*dt;
}
 

EDIT:

Also becomes more noticeable as speed increases. Try upping the speed to 200 pixels per second.
Title: Re: Interpolating between 2 sf::Transforms
Post by: zsbzsb on February 23, 2014, 11:32:33 pm
It definitely jitters if you run your physics at a lower framerate than your renderer, which is sometimes necessary.

Yes if you cap it low enough, but I always cap my update/physics code at 30-45Hz and never experience stuttering.

@tedsta Your problem comes from floating point inaccuracy. Handle your time with a time object all the way around and don't cast it away to a float until necessary.