Beginner made Solar System 3D Simulation

I have not done much with Processing, not more than 5 projects. This is, by far, the one I am most proud of. I have not followed any tutorials, used code from others (expect the PeasyCam library) nor have I looked at examples. I am far from good at Programming, I am just trying to convert my Ideas to Code. Have I made any major mistakes, anything I could do better or flawed code? Or leave your thoughts below.
PS: Giant Coding Train fan

Main Code:

import peasy.*; 

PShape sky;
PImage skyTex;
PeasyCam cam;
ArrayList<Planet> planets;
float fmax = 0;
ArrayList<PVector> stars;
float rotate;
float radius = 250;
float camheight = 1000;
float fov;
float cameraZ;
float dis1;
int steps = 400000;

int p;

PImage sun, mercury, venus, earth, mars, jupiter, saturn, uranus, neptune;

void setup() {
  //size(800, 600, OPENGL);
  fullScreen(OPENGL);
  frameRate(100);

  sun = loadImage("sun.jpg");
  mercury = loadImage("mercury.jpg");
  venus = loadImage("venus.jpg");
  earth = loadImage("earth.jpg");
  mars = loadImage("mars.jpg");
  jupiter = loadImage("jupiter.jpg");
  saturn = loadImage("saturn.jpg");
  uranus = loadImage("uranus.jpg");
  neptune = loadImage("neptune.jpg");
  
  p = 0;

  fov = radians(70);

  perspective(fov, float(width)/float(height), 1, 9999999);

  cam = new PeasyCam(this, width/2, height/2, 0, 1000);
  cam.setMinimumDistance(21);

  skyTex = loadImage("sky.jpg");
  sky = createShape(SPHERE, 500000);
 sky.setTexture(skyTex);
  sky.setStroke(0);  

  planets = new ArrayList<Planet>();

  planets.add(new Planet("Sonne", 0, 0, 0, 0, 20, 333054, 255, 230, 0, 1, 1000, 0, sun));

  planets.add(new Planet("Merkur", 62.543, 7, 0, 0.205, 8, 0.055, 132, 132, 132, 45, 13, 10, mercury)); 
  planets.add(new Planet("Venus", 145.763, 3.4, 177, 0.007, 12, 0.8149, 197, 133, 40, 50, 28, 5, venus)); 
  planets.add(new Planet("Erde", 200, 0, 23.4, 0.017, 13, 1, 84, 101, 130, 50, 48, 0.1, earth));
  planets.add(new Planet("Mars", 280.9, 1.9, 6.7, 0.094, 9, 0.1069, 183, 99, 72, 60, 76, 0.1, mars)); 
  planets.add(new Planet("Jupiter", 1006.798, 1.3, 25.2, 0.049, 40, 317, 167, 161, 150, 80, 350, 0.05, jupiter)); 
  planets.add(new Planet("Saturn", 1839.02, 2.5, 3.1, 0.057, 32, 95, 208, 193, 163, 100, 700, 0.05, saturn)); 
  planets.add(new Planet("Uranus", 3727.1, 0.8, 26.7, 0.046, 60, 14, 155, 203, 210, 132, 1450, 0.07, uranus)); 
  planets.add(new Planet("Neptune", 6042.8, 1.8, 97.8, 0.011, 70, 17, 45, 52, 132, 183, 2000, 0.08, neptune)); 
  //planets.add(new Planet("Pluto", -5032.3589395, 0, 0, 0, -0.19226, 0, 15, 0.10699933, 250, 200, 50)); 

  background(0);

  for (int i = 0; i < steps; i++) {
    pre(i);
  }
}

void draw() {  
  background(0);

  //pushMatrix();
  //translate(width/2, height/2, 0);
  //stroke(0, 0, 255);
  //line(300, 0, 0, -100, 0, 0);
  //stroke(0, 255, 0);
  //line(0, 330, 0, -0, -100, 0);
  //stroke(255, 0, 0);
  //line(0, 0, 300, 0, 0, -100);
  //popMatrix();

  float dis = (float) cam.getDistance();
  if (dis > 50000) dis = 50000;
  if (dis < 21) dis = 21;
  dis1 = map(dis, 21, 50000, 0, 255);
  fov = radians(log1(dis1));
  perspective(fov, float(width)/float(height), 1, 9999999);

  pushMatrix();
  float[] campos = cam.getPosition();
  translate(campos[0], campos[1], campos[2]);
  shape(sky);
  translate(-campos[0], -campos[1], -campos[2]);
  popMatrix();

  for (Planet planet1 : planets) {
    planet1.grav(planet1, planets);
  }

  for (Planet planet1 : planets) {
    planet1.updateVel();
  }

  Planet plan = planets.get(0);
  Planet plan2 = planets.get(p);
  //camera(width/2, /*(height/2)*/ + map(mouseY, 0, height, -1000, 1000), 1000, width/2, height/2, 0, 0, 0, 1);

  pushMatrix();
  translate((width/2) - plan2.pos.x, (height/2) - plan2.pos.y, 0 - plan2.pos.z);
  for (int i = 0; i < planets.size(); i++) {
    Planet plan1 = planets.get(i);
    if (i != 0) plan1.light(plan);
    plan1.show();
  }
  popMatrix();
}

void pre(int i) {
  for (Planet planet1 : planets) {
    planet1.grav(planet1, planets);
  }

  for (Planet planet1 : planets) {
    planet1.updateVel1(i);
  }
}

float log1(float a) {
  float a2 = 2100 / (30 + 70 * exp(0.021 * -a));
  return a2;
}

void keyPressed() {
  if (key == 'w'){ 
    p++;
    if (p > planets.size()-1) p = 0;
  }
  if (key == 's'){ 
    p--;
    if (p < 0) p = planets.size()-1;
  }

}

Planet Class:

class Planet {
  PVector pos;
  PVector vel;
  PVector acc;
  float radius;
  float mass;
  float r, g, b;
  int mod;
  PVector currVel;
  String name;
  ArrayList<PVector> posStore;
  float dis;
  int traillength;
  float rotation;
  float rot;
  float winkelPole;

  float Grav = 0.0002;

  PShape planet;

  Planet(String name, float dis, float incl, float winkelPole, float ecc, float r, float m, float re, float g, float b, int traillength, int mod, float rot, PImage img) {
    float vz;
    if (dis != 0) vz = sqrt((Grav * 333054) / dis);
    else vz = 0;
    vz = vz * (1 + (ecc/2));

    this.pos = new PVector(dis, 0, 0);
    this.vel = new PVector(0, 0, vz);
    this.pos.x = dis * cos(radians(incl));
    this.pos.y = (dis * sin(radians(incl))) *-1;
    float hyp = vz / cos(radians(incl));
    this.vel.y = sin(radians(incl)) * hyp;
    this.vel.setMag(vz*-1);

    this.winkelPole = winkelPole;
    this.rotation = rot;
    this.mod = mod;
    this.radius = r;
    this.mass = /*PI * sq(r)*/ m;
    this.r = re;
    this.g = g;
    this.b = b;
    this.currVel = new PVector(this.vel.x, this.vel.y, this.vel.z);
    this.name = name;
    this.traillength = traillength;

    this.posStore = new ArrayList<PVector>(traillength);
    for (int i = 0; i < traillength; i++) {
      posStore.add(new PVector(this.pos.x, this.pos.y, this.pos.z));
    }

    planet = createShape(SPHERE, r);
    planet.setTexture(img);
    planet.setStroke(0);
  }

  void grav(Planet planet1, ArrayList<Planet> planets) {
    for (Planet planet2 : planets) {
      if (planet1 == planet2) continue;
      PVector forceDir = PVector.sub(planet2.pos, planet1.pos); 
      float d = forceDir.magSq();
      forceDir.normalize();
      PVector f = forceDir.mult(Grav * planet2.mass / d);
      currVel = planet1.vel.add(f);
    }
  }

  void updateVel() {
    this.pos.add(this.currVel);
    if (frameCount % mod == 0) posStore.add(0, new PVector(this.pos.x, this.pos.y, this.pos.z));
    if ( posStore.size() > traillength) posStore.remove(traillength);
  }

  void updateVel1(int i) {
    this.pos.add(this.currVel);
    if (i % mod == 0) posStore.add(0, new PVector(this.pos.x, this.pos.y, this.pos.z));
    if ( posStore.size() > traillength) posStore.remove(traillength);
  }

  void show() {
    for (int i = 0; i < posStore.size()-2; i++) {
      PVector p1 = posStore.get(i);
      PVector p2 = posStore.get(i+1);
      stroke(this.r, this.g, this.b);
      line(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
    }

    noStroke();
    fill(r, g, b);
    pushMatrix();
    translate(pos.x, pos.y, pos.z);
    if (rotation != 0) {
      if (rot > 1000000) rot = 0;
      rot = rot + (0.1 / rotation);
      rotateX(radians(winkelPole));
      rotateY(radians(rot));
    }
    //specular(r*1.5, g*1.5, b*1.5);     
    //shininess(5);
    //emissive(r*0.2, g*0.2, b*0.2);
    shape(planet);
    popMatrix();
  }

  void light(Planet plan) {
    lightSpecular(0, 0, 0);
    pointLight(120, 120, 110, plan.pos.x, plan.pos.y, plan.pos.z);
  }
}

Here is the data folder with all the textures:
https://anonfiles.com/B4X6W9P6o0/data_zip

5 Likes

Hi @B0tOx Welcome to the forum.

I’ve tried the code, but the first error was “too little ram” so I pumped it up from 250 to 500 MB (in menu/setiings) but then it gave the error

RuntimeException: Waited 5000ms for: <685a7da2, 669d9ed9>[count 2, qsz 0, owner <main-FPSAWTAnimator#00-Timer0>] - <main-FPSAWTAnimator#00-Timer0-FPSAWTAnimator#00-Timer1>

I’ve not looked into the code but when I take out the pictures the sketch runs nude.
Maybe you have a model “light” that is runnable on OpenProcessing?

@B0tOx

Wow! Very cool!

At a glance I have one suggestion:

In your Planet class, you have a somewhat arbitrary rotation limit check. You could do a if(rot > 360) rot = rot -360; or perhaps better: while(rot > 360) rot = rot - 360; (in the unlikely event you rotate more than 360 degrees at each step).

    if (rotation != 0) {
      //if (rot > 1000000) rot = 0;
      rot = rot + (0.1 / rotation);
      while(rot > 360) rot = rot - 360;
      rotateX(radians(winkelPole));
      rotateY(radians(rot));
    }

Btw, the “rot” variable isn’t really set, as it’s different from the “rot” in the constructor where you set this.rotation = rot; Just FYI. (Processing set it to zero as default I believe).

So I’ve been using Processing on and off for years (mostly off perhaps), and today I learned about PeasyCam! (Which I had to install first) :slight_smile:

Thanks for sharing!

And welcome to the forum!

@noel
For me the code worked out of the box btw. I haven’t set or increased the RAM size in settings either.

2 Likes

I couldn’t download the zip…

Light is a much too bright, these settings are better:

void light(Planet plan) {
    lightSpecular(0, 0, 0);
    pointLight(70, 70, 60, plan.pos.x, plan.pos.y, plan.pos.z);
  }

@raron

Rotation works like a charm! Thank you

@Chrisir
Hope this one works

Now it works.

Thanks a million.

It looks fantastic. I like the ellipses and the shadows of the planets.

Very impressive.

A few remarks

OPENGL is deprecated; use P3D instead.

ArrayList stars; is not in use. Delete.

Keys

I understand that w/s changes the planet you are looking at (planet number p, where you should use a longer name for the variable like planetCenterNumber or so).
That is very cool.
I’d suggest to mention this in a println statement like println(“Use w/s to change the planet for the camera center”); or whatever.
Not sure if I got this right, but when the cam rotates for example with the planet Jupiter around the sun, shouldn’t the stars behind the sun change? Like the stars going in the other direction? (this looks so on the outer gas planets. It looks different with Mercure or Venus…).
When using w/s the name of the newly chosen planet could flash for 3 seconds as text (use text() and millis())

  cam.beginHUD(); 
  fill(255);
  text(plan2.name, 14, 14);
  cam.endHUD();

Images

You have this line:
PImage sun, mercury, venus, earth, mars, jupiter, saturn, uranus, neptune;

In my opinion, you could instead make a field PImage texImg; in the class and tell the file name in the constructor of the class. (instead of PImage img in the constructor of the class have String strImageName).

like
planets.add(new Planet("Sonne", 0, 0, 0, 0, 20, 333054, 255, 230, 0, 1, 1000, 0, sun, "sun.jpg"));

Then you can delete the mentioned line and also this:

  sun = loadImage("sun.jpg");
 mercury = loadImage("mercury.jpg");
  venus = loadImage("venus.jpg");
  earth = loadImage("earth.jpg");
  mars = loadImage("mars.jpg");
  jupiter = loadImage("jupiter.jpg");
  saturn = loadImage("saturn.jpg");
  uranus = loadImage("uranus.jpg");
  neptune = loadImage("neptune.jpg");

Color

There is a variable type color, so instead of passing r,g,b
to the constructor just pass col to it.

r,g,b is not necessary in the class.

This stroke(this.r, this.g, this.b);
becomes stroke(col);

Well done!!

Chrisir

2 Likes

Thank you for the Feedback.

PeasyCam is kind not that intuitive to steer, does feel pretty awkward, maybe you have ended up below the solar system.
Also Have this Logistic function smoothly changing the FOV based off of the Distance from the camera to the Subject:

 float dis = (float) cam.getDistance();
  if (dis > 50000) dis = 50000;
  if (dis < 21) dis = 21;
  dis1 = map(dis, 21, 50000, 0, 255);
  fov = radians(log1(dis1));
float log1(float a) {
  float a2 = 2100 / (30 + 70 * exp(0.021 * -a));
  return a2;
}
1 Like

View from Earth, quick’n’dirty perspective change for fun.

Add this at the beginning of draw():

void draw() {
  background(0);

  Planet viewFrom = planets.get(3);
  pushMatrix();
  translate(-viewFrom.pos.x, -viewFrom.pos.y, -viewFrom.pos.z);

End then add another popMatrix() at the end of draw():

  popMatrix();
}

Not perfect but I don’t know how to change the PeasyCam position (I don’t think it has a method for it). So instead I moved the solar system. It was easier :smiley:

Just remembered, peasycam has this Method:

void	lookAt(double x, double y, double z) 

It changes the “subject” Peasycam is rotating around. Will adapt so it only uses this method to change what we are looking at.

And then I discover the W and S keys i your program… just disregard my last post!

Btw, you don’t need to translate back to the last position as long as you do a popMatrix() right afterwards.

  pushMatrix();
  float[] campos = cam.getPosition();
  translate(campos[0], campos[1], campos[2]);
  shape(sky);
  //translate(-campos[0], -campos[1], -campos[2]);
  popMatrix();

This is a great sketch, @B0tOx – thank you for sharing it!

Consider uploading your sketch folder along with the data folder to a GitHub repository! (DropBox shares tend to have a very limited lifespan, and this sketch needs its data).

2 Likes

Nice! Venus and Uranus orbit in the other direction than the other planets though (with Uranus having a weird tilt). Great project…

You can use the Mousewheel to zoom and use w/s to change the planet you are looking at. I have made Venus rotate backwards and also made Uranus the outsider it is. All 8 plantes rotate counterclockwise.
I think you Switchesd around Orbit and Rotation:

Planets . All eight planets in the Solar System orbit the Sun in the direction of the Sun’s rotation, which is counterclockwise when viewed from above the Sun’s north pole. Six of the planets also rotate about their axis in this same direction. The exceptions – the planets with retrograde rotation – are Venus and Uranus …

2 Likes

beginner is an understatement of course, it is a very good project that is well made.
one of the few things I would feel could be an enhancement would be to automatize the loading of your date. checking a folder and load all the images inside it, using the filename as identifiers for your planet data from an xml.

that way you could drop images into that folder or even get online data of planets values and create any solar system.

a method producing an array of images will probably less tiresome than typing many lines…

oh, and of course, since you already have planets orbiting the sun, moons come next using the same class you already have…

now you only need a class one level above to distribute solar systems to a galaxy…
(no, I didn’t say universe… yet)

always so nice to see a project that is so well-crafted :slight_smile:

4 Likes

Wow! Worked fine for me. This code is worth studying a bit.

2 Likes

I admire how efficient this code is and I’ve been playing with the shape() and the texture for the stars. I notice that the sky image is gallactic coordinates. I haven’t figured out why not celestial coordinates just yet. I have figured out that the sky image is backwards when viewed from inside like from some point in your solar system model. I plan to use a SQlite database and plot some stars which is why I noticed.

It looks very rich though.

2 Likes

Wow, I think I now know what you meant with studiying a bit. Isnt a quick fix for the flipped sky just to also flip the image? And I never knew that my code was anywhere near efficent. You actually put a smile on my face gg.

PS: Have moved on from this project, now am working on a wolfenstein based game

1 Like

I’m still playing with this code. It presented me with a problem to solve. The sky image you used is in galactic coordinates and everything I have done on my own has used celestial coordinates or some weird inverse since we’re inside the sphere and when you drape an image over a sphere, things get backwards.

This link https://svs.gsfc.nasa.gov/3895 didn’t have a suitably sized image of constellation boundaries in galactic coordinates which meant if I wanted to include boundaries with constellation figures or the sky image in galactic coordinates then I would need to come up with a coordinate transformation. I didn’t use any formula to do it, I used 3 sliders and adjusted until things lined up. To test if my eyeball is right, I plotted some stars from a database where the coordinates are celestial.

Here’s what I got. I just thought you might enjoy it.

2 Likes