PersonTheCat / pangaea

A highly configurable terrain generator for Minecraft with light scripting support
GNU Lesser General Public License v2.1
1 stars 0 forks source link

[Spike] Research how to implement noise / density registry for terrain #3

Closed PersonTheCat closed 1 month ago

PersonTheCat commented 1 month ago

Vanilla terrain uses a series of density functions to control noise output affecting terrain generation. These fundamental noise generators are often hard coded and only have a handful of configurable values.

For underground noise, It is my opinion that it would be easier to design a registry of various density functions which get applied in order. This would provide a system similar to how WorldCarvers work and would run much the same way, except where generation stops at each block once the noise output is in threshold.

Research into net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator#iterateNoiseColum and design a system which uses registries instead. For now, this registry must control but not alter terrain output

PersonTheCat commented 1 month ago

How MC Terrain is Generated (1.18+)

When I first started working on this issue, I had greatly underestimated how much logic is implemented in the form of JSON functions. You can read this page on the official Minecraft wiki for more information, but here's a quick overview of how things work now:

Noise Routers and Density Functions

There isn't exactly some hard-coded logic which calculates the terrain height and fills everything below it with stone and caves. Instead, the chunk generator compares each block against a JSON-serializable density function.

Density Functions

The term "density function" most likely comes from the fact that it's designed to reflect the "density" of any given block. For example, a negative value would be air, and a positive value >1 would be stone. Anything in between could be some other block, or still stone. That's actually configured by surface rules, which we'll discuss in a later issue.

Noise Routers

Density functions are organized into separate features arbitrarily. For example, a density function which produces swiss cheese-like patterns is placed into a JSON file, data/minecraft/density_functions/overworld/sloped_cheese.json or minecraft:overworld/sloped_cheese.

A much larger density function--for example, final_density--handles routing each coordinate to this sloped cheese function or some other function, such as caves, entrances, etc. This final_density function is controlled by an appropriately named noise router.

In short, the Noise Router is responsible for housing essential density functions, which I suppose are themselves philosophically responsible for routing the input (x, y, z) to various other functions.

Thoughts on Modern MC Terrain Generation

I'm sure I don't need to get into the drawbacks on this design (I can hardly count them anyway), but here are a few:

And perhaps the most important drawback for our use cases: it may not be possible to implement a density registry (for example, to reuse cave features or terrain shapes across dimensions). Here's why:

An Approach for Pangaea

Density functions give users the ability to perform arbitrary arithmetic, enabling a variety of unknown features to be constructed in the data packs. It really is a very powerful system, which just needs a lot of work to reduce the barrier to entry and improve readability.

In my opinion, it would be a very bad idea to restrict access to this functionality in any way. What we need to do is expand on it.

Injectors

The first thing we'll need is a system which lets us inject modifications to the data packs. I'm thinking we should support injecting final_density functions, placement modifiers, and basically any partial features that otherwise would require replacing entire files in the data packs. We'll discuss how to implement these injectors in a follow-up story.

Simplified and Optimized DensityFunctions

The next step is to design a handful of highly-specialized density function which do exactly what the vanilla data pack functions do. All we need to do is combine them, give them a descriptive name, and label their parameters according to what purpose they serve.

We'll use this system to replace the final_density functions with a Density Controller, which I believe will reduce the barrier to entry for creating new data packs.

I'm also designing a DensityFunctionBuilder API, which integrates caching, blending, interpolation, and clamping into all existing density codecs, thus reducing the amount of nesting required in the configs.

New Types of Density Functions

Finally, we'll add a series of new density functions, which I'm hoping will greatly increase the power exposed to the end user. For example, we'll expose an API which lets the user inline their own density functions via XJS, as well as a variety of additional noise generators and mutators. This will only be possible through the Pangaea configs.

GUI Configurations

A critical aspect of some of these features is that I'm aiming for them to be configurable in-game. I'm hoping we'll be able to use CatLib's ConfigValue API to generate config screens on various platforms. This will give users a very quick and easy way to configure their terrain output. Note that not all features will be configurable in-game and you may have to pick and chose: either you get advanced configs or you get the GUI. Will discuss this matter in a follow-up story.