Initial commit

This commit is contained in:
Jakub 2025-12-07 11:30:49 +08:00
commit 789d0c1e2f
7 changed files with 158 additions and 0 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

21
.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# Godot 4+ specific ignores
.godot/
/android/
# Emacs junk
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# Org-mode
.org-id-locations
*_archive
# flymake-mode
*_flymake.*

72
README.org Normal file
View file

@ -0,0 +1,72 @@
#+title: Open Heavens
Open Heavens is a semi-realistic and deterministic star system generator, based on [[https://github.com/BirDt/stardate-legacy][Stardate Legacy]]. Open Heavens provides the "controller" part of your average MVC, so it does not provide any visualisation or interaction logic with the system (except for whatever is needed for testing).
* Dependencies & Credits
- [[https://github.com/BirDt/markov-machine][Markov Machine]] is used for name generation.
* Generation Process
Open Heavens generates systems by their mass, age (which is sparingly used), and elemental abundance, both of which are configurable when passed to the system generator. This process outline assumes you leave all settings as random, and only provide a seed. Most rates are configurable.
We start by randomly generating the solar mass of the overall system. 80% of the time, we use a random distribution of a 1.1 solar mass mean with a 0.5 solar mass deviation, and we clamp this to 0.5 and 20 on the lower and upper end. 20% of the time, we just randomly generate anywhere from 0.5 to 20 solar masses. We also generate the age of the system, in gigayears - 80% of the time this is a normal distribution with a mean of 5 and deviation of 1, otherwise it's a random value between 1 and 13. The reason for these values is that they provide 80% (this is configurable) odds of generating an interesting system with a good number of exoplanets.
We next generate abundances. The [[https://en.wikipedia.org/wiki/Abundance_of_the_chemical_elements#Universe][top ten most common elements]] are normally distributed around their mass fraction with small (0.05 - 0.00005) deviations. For the remaining natural elements, they are completely random but always add up to 100%. This is not a particularly realistic way of generating the abundance, but it leads to some fun results like thorium worlds and the like.
We generate the star of the system by removing a random portion of the total system mass - between 20% and 99%. This mass makes up the star and is used to generate it.
** Star Generation
We start by calculating the stellar class - since we only have main sequence stars this is fairly simple as we just look at the mass of the star and assign the class based on the range:
| Mass (Solar Masses) | Class |
|---------------------+-------|
| x < 0.45 | M |
| 0.45 < x < 0.8 | K |
| 0.8 < x < 1.04 | G |
| 1.04 < x < 1.4 | F |
| 1.4 < x < 2.1 | A |
| 2.1 < x < 16 | B |
| x > 16 | O |
We use a curve to determine the temperature (in Kelvin) from the mass, and color (RGB) from the temperature. These curves used known stars as sample points.
For luminosity, the following DISGUSTING formula is used.
~14341850000 + ((78.43685-14341850000)/(1 + pow(temperature/145852.2,7.237745)))-78.46~
This gives the luminosity in solar luminosity, so 1 = 3.75×10^28 lumen.
To calculate the radius (in solar radii), we first calculate a best guess: ~pow((3.0 * mass)/4.0*PI, 1.0/3.0)~.
The output of this is ~0.62035~ for 1 solar mass, and so we normalise the value so that 1 solar mass = 1 solar radius: ~(best guess/0.62035) - 1.14503109452965~
From the radius we calculate the density, which is used for habitable zone and Roche limit calculations. We do this by calculating the projected volume - ~(4.0/3.0) * PI * pow(solar_radii * SOLAR_RADIUS, 3)~ - and real mass (in Kg) - ~mass * 1.9885~ - which gives us a simple formula for the kg/m^3 density: ~(r_mass/projected_volume) * 1000 * 1000~.
We calculate the inner and outer radius of the star's habitable zone as the square root of the luminosity divided by 1.1, and the square root of the luminosity divided by 0.53 respectively. These values are in AU.
We also determine the composition of the star, however we assume it is only composed of hydrogen and helium. Hydrogen can be any percent between 65 and 85, while helium is the remainder to bring the composition to 100. This prevents too much hydrogen and helium from being used in planet generation, which prevents too many gas giants from being generated.
** Planet Generation
We always generate a minimum of 1 planetoid, which can be forced to be habitable. We continue generating planets up until all the system mass is used up - that being when only 0.05 Earth masses are remaining, which is considered as the mass for non-planet system features such as asteroids or comets.
Each time we generate a planet, we generate a mass for it (in Earth masses) and subtract that from the remaining system mass. If the planet should be forced as habitable, the mass is always between 0.107 and 9.44 Earth masses, otherwise it is either between 0.05 and 1000, or 0.05 and 15, where we select the former a third of the time. This prevents too many gas giants from showing up.
To determine the radius of the planet (in Earth radii), for most planets we sample the planet mass to radius curve (which is constructed based on real known planet mass and radius values), adding some normally distributed deviation (mean of 0, deviation of 0.15). For planets with forced habitable generation, we start with a random radius between 0.5 and 2.49. We then incrementally subtract and add from this radius until we can get an estimated surface gravity between 0.4 and 1.7. We calculate the density after the radius, similarly to the star density calculation:
1. We get a projected volume: ~(4.0/3.0) * PI * pow(planet_radius * 6378 * 1000, 3)~
2. We get the real mass in kilograms: ~mass * 5.98 * pow(10, 24)~
3. We divide the latter by the former to get the density
Surface gravity is calculated from the kilogram mass (~mass * 5.98 * pow(10, 24)~) and meter radius (~planet_radius * 6378 * 1000~). This gives a gravity of ~G * (real_mass / pow(real_rad, 2))~ meters/second^2, and ~m/s^2 * 0.10197162129779~
g's.
We calculate each planet's orbit. If the planet is forced habitable, we set the orbit distance to a random value within the star's habitable zone range. Otherwise, we calculate the roche limit based on the radius, mass, and density. If the parent body of this planet is a star (the system root) then we randomise the orbit between 0.05 AU + the roche limit and 50 AU + the outer habitable zone radius. If the parent body is a planet, we randomise it between the roche limit and 0.1 AU.
We calculate the orbit speed as ~sqrt((G * parent_mass)/distance_at_apoapsis_m) / 1000~, using real values. The orbital period follows as ~(2 * PI * orbit_distance)/orbit_speed~, and we turn that into an "Earth days" measurement with ~abs(orbital_period / 0.211 * 365)~.
We also calculate a rotation period - for forced habitable worlds, this value is a normal distribution with a mean of 1 day and a deviation of 0.2. For every other world, this is a normal distribution with a mean of 1 divided by the mass of the planet, and a deviation of 1. The rotation period has a 50% chance of being reversed. For forced habitable worlds, the axial tilt is random between 0 and 35 degrees, otherwise it's random between 0 and 90 degrees. If the rotation period and orbital period are approximately equal, we also keep track of the planet being tidally locked.
** Limitations
- Elemental abundances are fully random beyond the top 10.
- Non-unary system generation is not implemented because I don't want to write the orbit math for it.
- Only main sequence stars are currently generated.
* FAQ
** What does "semi-realistic" mean?
I am not an astrophysicist, geologist, or any other qualified position. I am just a computer programmer who wants to recreate Elite: Dangerous' galaxy generation logic. "Semi-realistic", in the context of this project, means "close enough" to realistic for simulation purposes or for the meta-setting this is inspired by (see the Stardate Legacy repo for more details). I am open to pull requests to correct any incorrect calculations or information, particularly from people with credentials in the field of whatever you are trying to correct.

1
icon.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 995 B

43
icon.svg.import Normal file
View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://caimx0c6fshux"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

15
project.godot Normal file
View file

@ -0,0 +1,15 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="Open Heavens"
config/features=PackedStringArray("4.5", "Forward Plus")
config/icon="res://icon.svg"