Hello people.
I don’t have anything near that biome-stuff that was posted, but I can probably help with a few things to get you started.
I have never touched game-developement before this, so what I do is probably not the best way, keep that in mind.
This is all also really freaking hard to explain in a single post, so it’s gonna be hard to read.
First off, this is what I have at the moment:
This planet is bigger than Jupiter. I set its radius to 70.000 km, but because my “noise-generator” has a few flaws, it’s actually about 5-10% bigger or something.
The last thing I was working on was trying to add UE’s SkyAtmosphere, but since my planets are so huge, there are issues. Here on a 100km radius planet:
Anyway, where to begin… Research!
My plan was to make it possible to travel from the surface of a planet all the way to the surface of a planet in another galaxy.
I know this is possible because of the videos from the magnificent @anonymous_user_c1111830
I also learned a lot from that blog.
There are several websites and posts and videos out there doing different and similar things.
Sebastian Lague has some really interesting and extremely well-made tutorials for making planets in Unity. The logic is applicable in UE4.
Anyway…
When creating an entire universe, there are A LOT of things to consider. Everything should be based on noise and a seed. But first, your universe should be split into different kinds of “worlds”.
For instance, my plan was to make these:
- PlanetWorld
- SolarWorld
- GalaxyWorld
- UniverseWorld
So when you’re in “UniverseWorld” (empty space between galaxies) and look at the nearby galaxies, these galaxies are pretty much static. They individually move relative to you, so you can fly past them, but the stars in each galaxy are just grouped into one single texture.
When you move close to one of these galaxies, you enter a GalaxyWorld. Each star in that galaxy will now become individually static. Now, all the external galaxies are collected on a “SkyBox”, and won’t move relative to you.
Technically, the static galaxy you approached will be destroyed, and replaced by this new GalaxyWorld’s own star-meshes.
When you move close to a star in your galaxy, you enter SolarWorld. The other stars in the galaxy will become the static “SkyBox”. But now you can see the planets as small meshes.
When you move close to a planet, you enter PlanetWorld. The other planets in the solar system are now textures on the skybox (either combined with the other skybox, or individual). But not necessarily actual meshes.
The planet-mesh you approached is deleted and replaced by a complex mesh, consisting of several meshes, that increase in resolution when you move closer.
To keep track of which world you’re in, when to transition, and where to spawn the different worlds’ objects, you need a single WorldController. In fact, that’s the only thing I have in my “level” before clicking play.
This WorldController keeps track of your position in the universe. Each “world” needs its own coordinate-system, and you’d have to convert between them.
To be honest, I never got further than creating the PlanetWorld, so I don’t have a solar system etc.
I don’t even know if any of that logic would work. @anonymous_user_c1111830 could probably answer if this is somewhat the way it was done for Galaxia.
Before we talk about creating the planet, you need to know about floating point precision, and the errors it creates.
Let’s say you want to spawn a planet in your UE4 editor, and you want it to be 70.000 km in radius like the one I showed above (which in UE-units(centimeter) is 7000000000). Then let’s say you want to spawn a rock on the surface. This is problematic.
If your planet is spawned at world zero (x:0, y: 0, z: 0), then your rock would have a coordinate/translation of e.g (x:7000000000, y:0, z:0). That number is too high!
It’s hard to explain, but since we’re dealing with such huge numbers with angles and vectors etc, the higher the number, the lower the precision.
It’s something like, for every “10 to the power of X” you move away from world zero, the same amout of precision is lost.
I.E, the higher the number, the fewer the decimal points are possible.
In your UE-editor, just try to put anything at x:7000000000 and look at it. Here’s what that looks like.
The above sphere is at 0,0,0. The one below is at x:7000000000.
The spehere looks allright, and I can move it nicely in y and z. But if I try to add 1 to X, it doesn’t move at all. It resets to the same.
If you try to move it to 7000010000, it snaps to 7000010240. It’s just… not good enough.
So basically, you can’t spawn a planet mesh at world zero and expect to be able to have anything on the surface. Everything would move in huge steps. Including the player.
How did I solve this?
First off, world rebasing. When I move more than 4km in any direction, I tell the UWorld to “rebase”, so that my position is now the new world origin, and UE moves all the other actors.
This way, anything spawned near me, always has good precision. That’s important. If I spawn a rock next to me, it will be near (0,0,0). If I move 100km, the world rebases several times, and if I spawn a new rock next to me again, that one would also be near 0,0,0.
The old rock, which would now be at -10000000 (if it still existed) probably has precision issues, but it doesn’t matter because it’s so far away, you can’t tell.
Every time I rebase the world (every 4km) I also need to add these ~4km to my own “private” coordinate systems so that I always know where I am, and where the planet is (because neither of us are at exactly world zero).
THE PLANET
Generating an entire planet as one object with a fixed “resolution” is not practical (nor possible). You need to split it up into several meshes with several LODs, which automatically increase/decrease in resolution based on distance from player.
To create mesh in runtime, I’m using RuntimeMeshComponent.
To handle the LOD-system, I’ve created what’s called a quadtree. It’s basically just a node structure.
Basically I “visualize” my planet as six planes in the form of a 2x2x2 cube. That seems to be the most common way to do this, since a normal “sphere” has issues with the poles, and icosahedrons are nightmares for textures.
My “Planet instance” stores a “PlanetSetting”-struct which contains stuff like its radius and its noise-generator (stuff based on the seed etc.)
The planet also stores six QuadTree-nodes, which I’ll explain later. These specific nodes represent each side in the cube. The root nodes. All these nodes store 4 vertices (corners) on the cube. (e.g (-1, 1, 1), (1, 1, -1) etc)
Each tick (or so) I tell each of these six nodes to generate their mesh, based on their four vertices in the cube.
When a node generates its mesh, it calculates a range of vertices between its four corners (e.g 64x64 vertices). At the same time, it calculates where on a SPHERE relative to sphere center all these vertices should be, using some fancy math. Now we have a spherical smooth planet.
Then I ask a noise-generator to get a “heightMultiplier” for each of these coordinates and multiply that, to get the terrain. (e.g 1.00005 for one vertex, 1.00014 for the next etc.). Now we have mountains. For noise, I’m using FastNoise.
Then I add the planet’s offset from world center to each vertex, so that I can spawn this mesh relative to world center instead of planet center. That’s important.
All the above math must be done with DOUBLES instead of floats, to avoid the precision error.
When all these calculations are done, I can convert the numbers to float, and spawn it in the world.
Sorry, this is really tricky to explain…
So what’s with the QuadTree?
Well, whenever I ask a Node to generate its mesh, it can also say “hey, you are too close to me”. So instead of generating its own mesh, it can divide itself into FOUR children.
The existing mesh (if any) will be destroyed, and the node now has four children nodes. Which are exactly the same kind of object as the previous one. It’s a recursive thing.
So it creates four children, and then tell all of them to generate their meshes.
And if I get too close to one of these children, it creates four children of its own. Etc. etc.
So when I tell the original six nodes to generate their meshes on every tick, some of them might say “I’ll tell my children to generate mesh instead”.
You still need to go through the entire node tree like this every time, because if you move far enough away, the “parent” must be able to say “Hey, I won’t ask my children, but they already exist, so I will kill my children and create my own mesh”.
There is a lot of information about quad trees out there if this was confusing.
QuadTrees + world rebasing + private coordinate system, means that I can spawn the new child-node-terrains closer to my player. Meaning that when I’m on the surface, each vertex of the terrain has a value of less than 4 kilometers, because it’s relative to the dynamic world center, instead of being relative to the planet center which is several thousand kilometers away.
What I’m explaining now is just the idea. There are LOTS of other stuff that needs to be considered. Calculating the normals is a real pain.
Especially because you want normals to be smooth between different nodes, so you need to either completely change my approach so you can send in a bigger area than your targeted mesh-area, or calculate surrounding external vertices based on internal vertices.
And then there’s tangents, UV/textures etc.
And the calculations for all vertices per mesh must be done asynchronously, by use of multi threading, so the game thread isn’t overflowed and cause lagging.
It’s a lot of work. Good luck.
This took way too long to write. And I even wrote it twice, because during the first writing I pressed ctrl+z to remove the last few letters, but it actually removed everything after the first youtube-link. yay…
Anyway… I have practically given up on my own project.
I started before I had a base idea for the actual game, and now I’m sitting here with a planet, without knowing what to do with it. And I’m back to my real-life-job, so I have a lot less time to spend on this. So I’ll probably not get much further than this…
Edit: Oh, and the gravity is done by using (and slightly modifying) the free Directional Gravityplugin.