Terrain Sampler Troubles

Discuss modding questions and implementation details.
Post Reply
chtujo
Posts: 51
Joined: Fri Sep 22, 2017 12:54 am

Terrain Sampler Troubles

Post by chtujo »

Hi, I've recently been trying to make my own terrain sampler, I'm not very experienced with C# or Unity. Because of this I've been studying other people's mods to try and figure out how everything works.

To do this I've been using the extract text function in the mod menu in my DFU game installation, then moving the extracted scripts over to a cloned DFU Repository / Unity project. I've tried this with 2 different mods, Distant Terrain (2.8.1) and Interesting terrains (0.5.2). I first tried Distant terrain, but when I tried to enter playmode there was a compile error:

Code: Select all

Assets/Game/Mods/distant terrain/ImprovedWorldTerrain.cs(29,48): error CS0713: Static class 'ImprovedWorldTerrain' cannot derive from type 'TerrainSampler'. Static classes must derive from object.
I then tried Interesting terrain, which compiled fine, and I could enter playmode, however the default terrain sampler was not overwritten.

I'm using Unity 2019.4.10f1 Personal.

Have I made an error, or am I just missing something? Thanks.

User avatar
Daniel87
Posts: 129
Joined: Thu Nov 28, 2019 6:25 pm

Re: Terrain Sampler Troubles

Post by Daniel87 »

I'm not sure how well a dfmod package can be reverse engineered. I think the better approach might be to study the original terrain sampler to get a basic understanding of the algorithms governing the terrain creation, copy the script and rename it and write a short startpoint script as in the tutorials here, to load your own script instead of the original one. From there you "only" have to adjust your script to do as you want or extend the existing functions.
To get a better understanding of how the machinery works, you can also try and adjust values in the original daggerfall scripts and then test run it and see if and what changed.
Debug.Log(); is you friend. Use it wherever possible to find out if the code you wrote got executed and then see in Unity what changed.

I learned a lot about terrain algorithms for other game projects from pages like the linked one below, maybe it helps you too:
https://www.redblobgames.com/maps/terrain-from-noise/
It's already good to understand things like Mathf.Clamp for creating table mountains or Ridge Noise to create canyons, rivers and mountain ridges. Knowing the basics of the Mathf class is definitely recommended. Also using ints and floats together can end up in errors you don't see because they have no console output. By example if you use Random.Range(int, int) but expect a float, you will never get it. Even if you use ints inside the function, make them floats to get a float output. Stuff like that.

For a terrain, you basically overlay the different noises and insert the parameters into the noise functions to get the result you need.
But still, procedural generation of terrain only gets you so far in terms of realism. It can be cumbersome to simulate a realistic looking erosion but it is necessary for the eye of the beholder to create a really realistic look.

I am currently working on a mod that redistributes the Nature Billboards using many hundred if/else statement and Random.Range(x,y)) > Random.Range(w,z), etc. Stochastic is the way to go for me as I want to create a variety of different kind of landscapes inside the same biome.

Here an example:

Code: Select all

case (int)MapsFile.Climates.Woodlands:

                            if (tile == 1) // Dirt
                            {
                                if(dfTerrain.MapData.heightmapSamples[hy, hx] * maxTerrainHeight < beachLine)
                                {
                                    if(Random.Range(0,100) < temperateDirtChance)
                                    {
                                        AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandBeach, scale, steepness, terrain, x, y, 0.25f); // Beach

                                        for(int i = 0; i < (int)Mathf.Round(Random.Range(0,2)); i++)
                                        {
                                            AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandBeach, scale, steepness, terrain, x, y, 1.5f); // Beach                                      
                                        }
                                    }
                                }
                                else
                                {
                                    if(Random.Range(0,100) < temperateDirtChance)
                                    {
                                        AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandDeadTrees, scale, steepness, terrain, x, y, 0.25f); // Dead Trees
                                        
                                        for(int i = 0; i < (int)Mathf.Round(Random.Range(0,2)); i++)
                                        {
                                            AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandDeadTrees, scale, steepness, terrain, x, y, 1.5f); // Dead Trees                                      
                                        }
                                    }
                                    else
                                    {
                                        if(Random.Range(0,100) < Random.Range(70,100))
                                        {
                                            if(Random.Range(0,100) < mapStyleChance)
                                            {
                                                AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandDeadTrees, scale, steepness, terrain, x, y, 1f, 3); // Needle Tree
                                                
                                                for(int i = 0; i < (int)Mathf.Round(Random.Range(0,3)); i++)
                                                {
                                                    AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandDeadTrees, scale, steepness, terrain, x, y, 0.75f, 3); // Needle Tree                                      
                                                }

                                                for(int i = 0; i < (int)Mathf.Round(Random.Range(1,3)); i++)
                                                {
                                                    AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandDeadTrees, scale, steepness, terrain, x, y, 1.25f, 3); // Needle Tree                                     
                                                }
                                            }
                                            else
                                            {
                                                AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandDeadTrees, scale, steepness, terrain, x, y, 0.25f); // Dead Trees
                                                
                                                for(int i = 0; i < (int)Mathf.Round(Random.Range(0,2)); i++)
                                                {
                                                    AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandDeadTrees, scale, steepness, terrain, x, y, 1.5f); // Dead Trees
                                                }

                                                for(int i = 0; i < (int)Mathf.Round(Random.Range(0,2)); i++)
                                                {
                                                    AddBillboardToBatch(dfTerrain, dfBillboardBatch, temperateWoodlandDeadTrees, scale, steepness, terrain, x, y, 0.75f); // Dead Trees
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            else if (tile == 2) // Grass
                            {
                            .
                            .
                            .
                            .
                            .
AddBillboardToBatch is a small function I wrote myself that places the billboard on a random position within a specified range and in some cases checks the underlying tile (are you water or not?) etc.

When I had a look into the texturing of terrains, I learned that DFU is using a simple perlin noise to generate the grass, dirt and stone texturing that looks a bit like marble when hovering above the map.
So for my purpose of creating puddles of water in the desert, I simply had to add another layer to the perlin noise algorithm for water that was a rare edge case and only occured once in a thousand runs. Inside the class for nature billboards I then drop tons of plants in the above sceme around such water tiles and voila, we got a desolate desert with an oasis once in a while.

I can imagine that generating the terrain is not much different here. I guess it's built around a 2D or 3D array, uses marching cube as building algorithm and reads out the y value for each vertex of the terrain from the height map and feeds the information into a Unity terrain instance.
There is a recycling process running that also reuses MapPixels that are out of range, I guess for memory management purposes or loading speed. Not sure as I only shallowly looked over the code.

Hope my guess work up there is of any help.

User avatar
pango
Posts: 2829
Joined: Wed Jul 18, 2018 6:14 pm
Location: France
Contact:

Re: Terrain Sampler Troubles

Post by pango »

Distant Terrain sources are here:
https://github.com/Nystul-the-Magician/ ... antTerrain

Interesting Terrain sources are here:
https://github.com/monobelisk/DFUnity-I ... ngTerrains
When a measure becomes a target, it ceases to be a good measure.
-- Charles Goodhart

Post Reply