louis-langholtz / PlayRho

An interactive physics engine & library.
zlib License
133 stars 24 forks source link

Custom Gravity, Substeps #328

Closed ninnghazad closed 5 years ago

ninnghazad commented 5 years ago

I need some input trying to use a custom gravity thing, think N-body simulation. I apply a force to bodies' centers each frame at 50hz. Now i think my problem is that as soon as playrho has done it's first substep(s), my gravity force vector is slightly off - it would have to be updated each substep.

manually stepping world with 1/1 substeps, clearing forces and updating gravity about 10 to 20 times per frame sounds like it should work - but there have to be sideeffects to this i don't yet see ... and i think it might be a lot slower.

any ideas or experiences on this matter?

ninnghazad commented 5 years ago

Ignore this for now - this does not reproduce in pure playrho:

//g++ -std=c++17 -O3 -march=native -Isubmodules/PlayRho ideas_and_tests/cpp_playrho_gravity.cpp -o /tmp/test libs/libPlayRho.a

#include <PlayRho/PlayRho.hpp>
#include <iostream>
#include <iomanip>

using namespace playrho;
using namespace playrho::d2;

int main()
{
    auto world = World{};

    std::cout << std::fixed << std::setprecision(10);

    const auto planet = world.CreateBody(BodyConf{}
            .UseLocation(Length2{0_m, 0_m})
            .UseType(BodyType::Static));

    planet->CreateFixture(Shape{DiskShapeConf{}.UseRadius(1_m*10).UseFriction(0)});

    std::vector<decltype(world.CreateBody())> bodies;
    for(int i = 0;i < 1;++i) {
        auto body = world.CreateBody(BodyConf{}
                .UseLocation(Length2{1_m*20, 1_m*i+1.123})
                .UseType(BodyType::Dynamic));

        body->CreateFixture(Shape{EdgeShapeConf{}
            .UseVertexRadius(1_m*0.1)
            .UseFriction(0.0)
            .Set(Length2{-1,0},Length2{1,0})});

        body->SetBullet(true);
        body->SetFixedRotation(true);
        SetLinearVelocity(*body,LinearVelocity2{0.1,0.1});
        bodies.push_back(body);
    }

    std::size_t i{0};
    while(++i) {
        Step(world,Second/50.0,8,3);
        ClearForces(world);
        std::cout << i << " " << GetMagnitude(bodies[0]->GetVelocity().linear);
        for(auto & b:bodies) {
            Vec2 g{planet->GetWorldCenter() - b->GetWorldCenter()};
            Real d =  Normalize(g);
            g *= std::max(pow(1.0 / std::max(1.0,d),2),0.0001);
            std::cout << " d: " << d << " g: " << g[0] << "x" << g[1] << std::endl;
            SetAngle(*b,Atan2(g[1],g[0]));
            ApplyForceToCenter(*b,g);
        }
    }

    return 0;
}

behaves as expected. bean-shaped object flies in an arc and drops towards planet, will then slide around it forever - without infinitely accelerating as it does in my environment. i will check some more and close this in a little while if nothing points towards this not being solely a problem with parts of my code.

ninnghazad commented 5 years ago

just as a note to those coming by - this behaviour turned out to be, while unintuitive (to me), physically correct and can be seen as a weird special case of orbital mechanics. no friction or restitution together with a force of gravity and spherical objects kind of forces bodies into orbits as their "resting" state.