Telluric planet types, resources and planetary surfaces

Published on
Last changed on

In this post I would like to cover several changes that are being introduced to the procedural generation of telluric planets; by "telluric planets" is meant planets with a good old ground to walk on - hence not planets from the "Gas giant", "Ice giant" or "Gas dwarf" categories. Moons, however are included. Belts and rings? They are likely to get their own, specific treatment.

Overall, these changes are of course related to procedural generation, but on two separate fronts:

  1. Server-side resource generation; i.e., the stuff that makes up cores, mantles, crusts and atmospheres and can become one of the factors for in-game extraction/production/etc
  2. Client-side: the related visual impact in terms of planetary surface

Now these two items may seem unrelated at first, but they are. If you muster enough patience and read through this post, you will be able to discover why. :-)

Bottom-up versus Top-down and "Begin with the end"

In an old post, I tried to briefly touch on a subject that many people/coders/enthusiasts such as myself will have probably encountered during their procedural journey(s): where to start? What determines the end result? Are we trying to simulate reality? Not at all? Something in between?

These questions may sound silly at first, but at the cost of repeating myself (the Old Man says Hi, sweetheart daughter :-)), it kind of all depends what one is trying to achieve. IMHO of course. Here goes (again):

  • Serious endeavours, be they scientific or economic, are probably trying to go for the "Bottom-up" approach, in the sense they start from very low-level constituents, who interact individually and generate - at least one would hope - some kind of macro-order whose goal is to simulate reality at a scale we can probably understand more easily. See those galactic collision or Neutron star merging simulations; this is serious stuff with a scope far, far beyond that of AdLumens.
  • Less serious endeavours include Adlumens. We are trying to make a game. Thus it makes sense to try and verge towards the "Top-down" approach, synonym with "Begin with the end".

In the context of planetary procedural generation, I see myself as being guilty of having tried the bottom-up approach. Albeit not for stars, systems, etc.
What did I do wrong? Well - I believed that rust and wasm would be able to - somehow - achieve too many things at once. And that I would be able to come up with efficient (read: fast) ways to simulate all kinds of chemical events linked to oxidation, reduction and the like. And in the rather tiny available time budget of 16 ms. In a nutshell, this is what I thought would be doable per frame, without neighbour awareness, for each vertex:

  1. Determine chemical/mineralogic composition derived/linked to redox
  2. Compute/derive macro mechanical characteristics such as elasticity/rigidity, hardness, etc. at the vertex
  3. Calculate movement at the vertex
  4. Calculate various geological consequences, the most obvious probably being elevation
  5. Calculate in real-time atmopheric/crust interactions and their outcomes: erosion, outgasing, deposits, and the like

All the above using several spherical noise fields in real time. Now wasm is fast, very fast even, but the initial tests I ran (which go beyond the current status of the planetary surface tool) weren't encouraging: there simply wasn't enough time. I could have gone the way of browser multithreading/shared workers (which I might actually do), but I want first to try and squeeze as much as I can from this little binary. Some kind of local vertical scaling before thinking about horizontal.

But my main issue, perhaps subconscious, had been to try and make a "Bottom-up" simulation. Ah well, it's not such a big deal since resource generation, planetary subtypes, etc. were going to be reworked anyway!

Complexity shift

With that mea culpa behind me, I started to think about what should be done to look at this from the other end: top-down.

Long story short/TLWNR: a lot of the complexity has been shifted from rust/wasm to python. The idea is for python to "pre-digest" a lot of the permutations, combinations, parameters and values at admin- and compile-time, thus letting rust and wasm run way less calculations, random picks and what not… and in the end be not only quicker, but with a smaller memory footprint.

Before/now

This is to describe the current live minerals/gas procedural generator, as of June 2025. This version is due to be superceded by something more clever. But very roughly, this is how it works at the moment:

WhatWhenNotes
Resources management Admin time The Django admin is used extensively to get a view of how often (or not) compounds and elements are used. The current live version has about 300 different compounds. The current dev version has around 700 minerals, with gases being worked on/designed. Given this amount, annotating querysets, etc is a strong necessity to help get more global views over tables that are very heavily normalised.
Resources printing Compile time Python prints large rust files. These are pretty much compiled runtime databases, stored in once_cell::sync::LazyFxHashMap and Arena structures.
The goal is to make the low-level data available at runtime without having to fetch anything from an external database, file, etc. Granted, it is less dynamic but the idea is that those low-level components do not ever change during runtime: they are essentially the most granular data available.
Resource pick Server-side runtime The server decides pseudo-randomly the amount of each compounds in crusts, atmopsheres, mantles, etc. This results in ppm concentrations and is pretty much what one can see on the Data explorer.
Stuff Client-side runtime I say "stuff" because not much happens just yet related to resources; only noise generation. But read below to check what is going to happen for the client.

Now there are several problems with the approach summarised in the table above:

  • I had not implemented telluric planetary subtypes yet and not started to think of exotic/alien differentiations inside nascent planetary structures; all planets were pretty much of a "silicate" type, i.e., close relatives to Earth.
  • As a consequence, the list of resulting compounds was, on average, pretty much the same across all planets/moons.
  • The calculations to simulate interactions between crusts/mantles and atmospheres were taking way too long: outgasing, atmospheric deposition, erosion, etc. All to create the illusion of geological dynamics and evolution

In the works

Time to try and describe what is currently being worked on, with advancement, problems and ideas, etc. Please bear in mind that the below reflects current work, ideas, and that what will eventually go live might not entirely match what is written!

Planetary subtypes

First things first, as it all starts here during the procedural generation of a telluric planet.

The subtypes have been carefully crafted with two goals in mind:

  1. Pseudo-realism: even though nobody knows for sure what exoplanets actually are composed of (I do not think white dwarf pollution and the like count just yet), Adlumens tries to offer subtypes that at least feel realistic.
  2. Variety: this second dimension is obvious. Variety is good for game purposes; it opens opportunites on resources, exploration, visual excitement and many others.

The subtypes are defined along several axes, which will enable the server-side procedural engine to determine what type a telluric planet belongs to. The idea is to make the existence/generation of subtypes depend on various filters/criteria that try and fit what we currently know. End result? For example, you will not see planets composed of 100% ices and have 8 times the mass of earth.

FilterComments
Likelihood A dimensionless number than expresses how "rare" or "common" a subtype is. It is rooted in "realism" but also heavily in game balance.
Mass Not all masses can result in all subtypes; As the mass is one of the starting points during generation, it determines which subtype(s) will be made available; one needs to be picked up.
This filter is a range [min, max].
Equilibrium temperature When it's too hot (star is too close, or the flux is to high or both), then certain types of planets simply cannot form. A planet primarily composed of volatiles will not have formed close to a star that is too hot. The same applies when it's too cold, albeit less strongly. This filter is a range [min, max].
Age Not all planets take the same time to aggregate/form/differentiate. As a result, you are extremely unlikely to find a metal-stripped planet around a young, burning hot O-type star. It simply hasn't had the time to form and then be stripped. This filter is a range [min, max].
Density A planet's density is one of the main criteria to determine its main type: telluric, gas giant, ice giant, etc. Within the telluric type, a planet's subtype must match its internals, which makes some combinations incompatible. This filter is a range [min, max].

But wait, there is a problem: what happens to a potential planet that meets none of the above criteria? Is it not generated? Does it fallback to some default (urgh) subtype? Does it get turned into something else than a planet? That is something I am working on!

Planetary cores, mantles and crusts

The next stage is selecting which core the planet has. Each planetary subtype is associated with one or more cores. Once the core is selected, time for the mantle. Then for the crust… and then for the atmosphere. The allowed associations are managed and made available at admin and compile time; the runtime only has to do a random pick - and so is extremely fast (sub microsecond).

This selection chain is quick at runtime and precomputed in python: it replaces complex attempts to dynamically select cores, mantles, etc, based on constituants alone (which would be the "bottom-up" way of doing things). As per "top-down", the runtime code assumes that whatever is made available is what's real. The available data is authoritative - does that make the chain data-driven?

A few details about cores, mantles, crusts and atmospheres:

TypeComments
Cores Cores are still composed of pure elements (Fe, Ni, Si, etc). The main change about them is that we are now no longer limited to Fe/Ni cores, but have a range of far more exotic ones at hand.
Mantles Mantles constituents were elements before; they are now compounds (minerals). Why was this change made? To try and better simulate pressure, crystalisation and the like. Also for meta game opportunities.
Crusts Are still composed of compounds (minerals). But those are split into two major groups: native compounds and interaction compounds. More on this below.
Atmospheres Are still composed of compounds (gases). But those are split into two major groups: native compounds and interaction compounds. More on this below.

These planetary building blocks are called templates: the procedural engine uses them to determine the final constituents of the layers they represent. In order for it to be able to do that, templates are quite literally runtime databases exposing values, ranges, constraints etc. For each compound/component, the end result is a ppm.

Native versus interaction compounds

As hinted at in the table above, crustal and atmospheric compounds can have two origins: native and interaction.

The native origin is applied to compounds that are there from the very birth of the planet. For atmospheres, this is a primary atmosphere, aggregated from the stellar nebula/accretion disk. For crusts, this is the result of core/mantle differentiation (when and if it happens!).

The interaction origin is applied to compounds that are the result of the interaction between the crust and the atmosphere:

  • crustal interaction compounds are the result of atmospheric deposition, erosion, chemical solubility, etc.
  • Atmospheric interaction compounds are the result of outgasing from the crust. They are also subjected to stellar wind "erosion" although this is calculated at runtime.

Interaction compounds are not calculated at runtime; this would be too expensive. They are managed at admin time and calculated at compile time. There is a strong assumption behind this: the planetary equilibrium is assumed to have been reached. This means that we do not have to loop multiple times until a convergence and/or such an equilibrium is reached, as one would using a bottom-up approach. You are looking at the planet as it is now, the result or millions, sometimes billions of years of crust/atmosphere interaction.

Not all interaction compounds will be right, that is for sure 8-} But again, that is not a permanent issue as all is data driven: found an interaction definition that is not right? Remove it or tweak it and compile.

Rock types

The current version introduces the concept of rock type. What is that? Well, on our good ol'Earth, rock are classified as three main, broad families:

  • Igneous rocks
  • Metamorphic rocks
  • Sedimentary rocks

And it works well on Earth, is extremely well documented, etc. I mean, this has been studied for centuries!

But what of exoplanets? Couldn't they potentially host rock types whose formation processes, structures, are not found on Earth? Long story short, Adlumens takes a leap of faith and assumes this is indeed the case, and I tried to design alien rock types in addition to those identified/known on Earth. An upcoming subsection in the Data explorer will reveal those rock types.

Adding a new rock type is simple, but a little bit involved, as rock types are used as the main proxy to determine compound concentration at a given coordinate at the surface of a planet. Client-side with wasm, this is about finding the colour at a vertex (which is in turn derived from a composition). As a consequence, rock types are likely to change, but not massively.

Aberrations / planetary quirks

In spite of the variety that can be encountered as a result of more options and paths during the procedural generation, I would like to introduce extras; whether they are called aberrations, quirks or idiosyncrasies, I would very much like them to be able to insert features similar to the great Mars canyon (or Olympus Mons) and many others.

This is very much still at the drawing board stage and implies playing with:

  • matplotlib in 3d,
  • procedural paths,
  • superformulas
  • and perhaps perlin worms

Client-side and Server-side

The above description is mostly about server-side generation. Indeed the server needs to have knowledge of all the planetary values to be able to generate resources.
The client does need some information as well, but less. Way less:

StepServer-side importanceClient-side importanceNotes
Mineral compositionMajorIndirectServer-side, mineral composition at a point determines the resources available at that point; a major point for the game engine. This is mainly obtained via obtaining rock types at the point versus physical characteristics/values at that point: age, boundary score, compression, as briefly documented here.
Client-side, the same applies although we do not care about resources themselves; their type is the only thing that matters, as a proxy.
ColourNoneMajorServer-side is colour-blind.
Client-side… well eye candy is somewhat important, so vertex colours are determined via an averaging of the mineral colours it contains, plus a gamma correction. Textures will probably be influenced by rock type and be changed in the shaders using those base colours.

What about other planet types? Belts? Rings?

These satellite types will be dealt with in the same way. With a few major differences:

  • "Giants" do not have a crust
  • Belts and rings only have a crust

All in due time 8-}

Data explorer enrichment

New sections will be introduced in the data explorer:

  • Telluric planet subtypes
  • Templates for cores, mantles, crusts and atmospheres
  • Rock types
  • Mineral families
  • Compounds: minerals and gases

Thank you for reading!

Please signin to add your comment.