I just tried it myself, using my fixed snippet.
Probably i see the same problem as you do: As planets come close to each other, forces become so huge the integration seemingly fails even with tiny timesteps. If the planets come very close, which they do because gravity pulls them together, they separate very quickly after that. There is a explosive raise in energy, and the simulation clearly isn't right. Maybe i still have bugs.
No wonder we have black holes and i had to invent my own laws of physics to do better :D
I don't have the physics engine running, so i can't quickly compare with RK4. But i doubt this alone would fix it.
It would help to model collisions, so planets can not get too close, but separate before forces become too discontinuous to integrate.
It would also help to model some realistic solar system, initializing all velocities so they represent natural orbits. Then they would not collide either.
static bool testSimplePlanets = 1; ImGui::Checkbox("testSimplePlanets", &testSimplePlanets);
if (testSimplePlanets)
{
constexpr int count = 10;
struct Planet
{
sVec3 com = sVec3(0);
sVec3 vel = sVec3(0);
sVec3 force = sVec3(0);
float mass = 1;
};
static Planet planets[count];
// generate random planets (this code runs only once)
static bool init = true;
if (init || ImGui::Button("Reset Simulation"))
{
int k = 0;
auto rnd = [&](float vMin, float vMax)
{
float f = PRN::randF(k++);
return (f * vMax-vMin) + vMin;
};
for (int i=0; i<count; i++)
{
planets[i].com = sVec3(rnd(-1,1), rnd(-1,1), rnd(-1,1));
planets[i].vel = sVec3(0);//sVec3(rnd(-1,1), rnd(-1,1), rnd(-1,1)) * 0.001f;//
planets[i].force = sVec3(0);
planets[i].mass = rnd(0.001f, 0.001f);
}
init = false;
}
// function to update forces of a pair of planets
auto GravityPair = [&](Planet &bodyI, Planet &bodyJ)
{
const float G = 1.f; // some number to relate your mass metric to gravity. i just assume we use a mass unit so G becoems 1.
sVec3 diff = bodyJ.com - bodyI.com;
float dist = diff.Length();
float force = G * (bodyI.mass * bodyJ.mass) / (dist * dist);
sVec3 direction = diff / dist;
bodyI.force += direction * force;
bodyJ.force -= direction * force;
};
static float timestep = 0.001f;
ImGui::SliderFloat("timestep", ×tep, 0.0002f, 0.005f, "%.5f");
// update (this code runs once every frame)
// accumulate forces
for (int i=0; i<count-1; i++)
for (int j=i+1; j<count; j++)
GravityPair(planets[i], planets[j]);
// integrate
for (int i=0; i<count; i++)
{
Planet &p = planets[i];
sVec3 acc = p.force / p.mass;
p.vel += acc * timestep;
p.com += p.vel * timestep;
p.force = sVec3(0); // reset accumulation
}
// render
for (int i=0; i<count; i++)
{
const Planet &p = planets[i];
float vol = p.mass;
float radius = pow (vol * 3.f / (4.f * float(PI)), 1.f/3.f);
RenderCircle (radius, p.com, sVec3(0), 1,1,1);
}
}