5
\$\begingroup\$

While testing Box2D with SFML I encountered something which confuses me: The relation between physical body centroids and SFML's sprite origin.

As an example I have a test class GameObject which is used to create a physical body and a render shape using Box2D and SFML:

class GameObject
{
private:
    b2Body* physicalBody;
    sf::ConvexShape renderShape;
public:
    static enum ObjectShape { Square, EqulateralTriangle };
    GameObject(b2World& world, const GameObject::ObjectShape shape = GameObject::Square, const sf::Color &color = sf::Color::White, float xPos = 0.f, float yPos = 0.f, float sizeInPixels = 30.f)
    {
        b2BodyDef bodyDef;
        bodyDef.type = b2_dynamicBody;
        bodyDef.position.Set(xPos / SCALE , yPos / SCALE);
        bodyDef.angle = -( b2_pi / 2.f );
        physicalBody = world.CreateBody(&bodyDef);

        b2PolygonShape bodyShape;
        float sizeInMKS = sizeInPixels / SCALE;
        switch (shape)
        {
        case EqulateralTriangle:
            {
                const int pointCount = 3;
                b2Vec2 points[pointCount] = { b2Vec2(sizeInMKS,0), b2Vec2(-sizeInMKS,sizeInMKS), b2Vec2(-sizeInMKS,-sizeInMKS) };
                bodyShape.Set(points, pointCount);
                b2FixtureDef fixture;
                fixture.shape = &bodyShape;
                fixture.density = 1.0f;
                fixture.friction = 0.3f;
                fixture.restitution = 0.5f;
                physicalBody->CreateFixture(&fixture);

                renderShape.setPointCount(3);
                renderShape.setPoint(0, sf::Vector2f((points[0].x * SCALE), (points[0].y * SCALE)));
                renderShape.setPoint(1, sf::Vector2f((points[1].x * SCALE), (points[1].y * SCALE)));
                renderShape.setPoint(2, sf::Vector2f((points[2].x * SCALE), (points[2].y * SCALE)));
                break;
            }
        case Square:
        default:
            {
                bodyShape.SetAsBox((sizeInPixels / 2.f) / SCALE, (sizeInPixels / 2.f) / SCALE);
                b2FixtureDef fixture;
                fixture.shape = &bodyShape;
                fixture.density = 1.0f;
                fixture.friction = 0.3f;
                fixture.restitution = 0.5f;
                physicalBody->CreateFixture(&fixture);

                renderShape.setPointCount(4);
                renderShape.setPoint(0, sf::Vector2f(0.f, 0.f));
                renderShape.setPoint(1, sf::Vector2f(sizeInPixels, 0.f));
                renderShape.setPoint(2, sf::Vector2f(sizeInPixels,sizeInPixels));
                renderShape.setPoint(3, sf::Vector2f(0.f, sizeInPixels));
                renderShape.setOrigin(sizeInPixels / 2.f, sizeInPixels / 2.f);
                break;
            }
        }
        renderShape.setFillColor(color);
        renderShape.setPosition((physicalBody->GetPosition().x * SCALE), (physicalBody->GetPosition().y * SCALE));
        renderShape.setRotation(physicalBody->GetAngle() * (180.0f/b2_pi));
    }
    void update()
    {
        renderShape.setPosition(physicalBody->GetPosition().x * SCALE, physicalBody->GetPosition().y * SCALE);
        renderShape.setRotation((physicalBody->GetAngle() * (180.0f/b2_pi)));
    }

    void render(sf::RenderWindow& renderWindow)
    {
        renderWindow.draw(renderShape);
    }
};

Now for the triangle after I call bodyShape.Set() the body centroid gets calculated(bodyShape.m_centroid is equal to [-0.33333, 0]), however when I wish to combine it with a renderShape I MUSTN'T set the renderShape's origin to it's centroid otherwise the renderShape is offset from the body.

On the other hand when I create a square it's centroid seems to be 0 (bodyShape.m_centroid is equal to [0,0]) and if I wish for the body and the renderShape to properly reflect one another then I MUST change the renderShape's origin to it's centroid.

Now what confuses me here is that this is completely opposite from what I thought I was supposed to do. Isn't Box2D's centroid effectively the body's center? And doesn't the body's position reflect the position of it's center? If that is the case then how does the physical body with a centroid which is not 0 sync with a render body whose origin is 0 and a physical body with a centroid of 0 not sync with a render body whose origin is 0?

I'm sorry if this is confusing to read but as english is not my native language I'm having a bit of a difficulty getting my question across. Basically what I'm asking is why a SFML square with it's origin being 0 doesn't reflect a Box2D square whose centroid is 0, properly? Shouldn't they fit? Same with the triangle. Why does a SFML triangle with it's origin 0 properly reflect a Box2D triangle whose centroid is [-0.33333, 0].

So am I missing something way obvious here?

\$\endgroup\$
3
\$\begingroup\$

The physical centroid is used by the physics engine as a center of mass. The center of mass is not the same as the position of a composition of shapes.

For instance, a body can have a transform position of (0, 0), and have it's shape's vertices for a polygon located around (10, 10). The shape vertices will be specified relative to the transform's location.

In short, use the transform's position of a body to get it's location.

The center of mass is used by the physics engine itself.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.