richardbiely / Voxelmetric

An efficient voxel framework for Unity3d
GNU General Public License v3.0
158 stars 27 forks source link

Creating Proper Caves #68

Closed aesliva closed 5 years ago

aesliva commented 5 years ago

Was wondering if there were any plans for 3D noise for the generation of caves, cliffs, overhangs, etc.. Also, if you were to implement it how would you go about doing it?

Adding another axis to iterate on will really slow things down, especially since we are already iterating over 3 loops in terrain gen (x, z, terrainlayer). I'm sure the set blocks method would have to change, and how we determine terrain height as well.

Just curious!

aesliva commented 5 years ago

So, I attempted to create some caves. Naturally, I'd assume iterating over the y-axis and using 3D interpolation would be enough to do this.

Added a 'y' variable:

        int xOffset = chunk.Pos.x;
        int yOffset = chunk.Pos.y;
        int zOffset = chunk.Pos.z;

        int i = 0;
        for (int z = 0; z < ni.noiseGen.Size; z++)
        {
            float zf = (z << ni.noiseGen.Step) + zOffset;
            for (int y = 0; y < ni.noiseGen.Size; y++)
            {
                float yf = (y << ni.noiseGen.Step) + yOffset;
                for (int x = 0; x < ni.noiseGen.Size; x++)
                {
                    float xf = (x << ni.noiseGen.Step) + xOffset;
                    ni.lookupTable[i++] = NoiseUtils.GetNoise(noise.Noise, xf, yf, zf, 1f, amplitude, noise.Gain);
                }
            }
        }

Acts like the absolute layer:

    public override float GetCaveNoise(Chunk chunk, int layerIndex, int x, int y, int z, float caveNoiseSoFar, float strength)
    {
        var pools = Globals.WorkPool.GetPool(chunk.ThreadID);
        var ni = pools.noiseItems[layerIndex];

        float caveNoiseToAdd = ni.noiseGen.Interpolate(x, y, z, ni.lookupTable);
        caveNoiseToAdd += minHeight;
        caveNoiseToAdd = caveNoiseToAdd * strength;

        if (caveNoiseToAdd > caveNoiseSoFar)
        {
            return caveNoiseToAdd;
        }

        return caveNoiseSoFar;
    }

    public override float GenerateLayer(Chunk chunk, int layerIndex, int x, int y, int z, float heightSoFar, float caveNoiseSoFar, float strength)
    {
        if (caveNoiseSoFar > 50f)
        {
            SetBlocks(chunk, x, z, chunk.Pos.y + y, chunk.Pos.y + y + 1, blockToPlace);
        }
        return heightSoFar;
    }

And in TerrainGen the y-axis is used:

public void GenerateTerrainForChunk(Chunk chunk)
    {
        int maxY = chunk.Pos.y + Env.ChunkSize;
        for (int z = 0; z<Env.ChunkSize; z++)
        {
            for (int y = 0; y < Env.ChunkSize; y++)
            {
                for (int x = 0; x<Env.ChunkSize; x++)
                {
                    float height = 0f;
                    float caveNoise = 0f;
                    for (int i = 0; i < TerrainLayers.Length; i++)
                    {
                        caveNoise = TerrainLayers[i].GetCaveNoise(chunk, i, x, y, z, caveNoise, 1f);

                        height = TerrainLayers[i].GenerateLayer(chunk, i, x, y, z, height, caveNoise, 1f);

                        if (height > maxY)
                            break;
                    }
                }
            }
        }
    }

Below is the result that I got. I'm not sure what can be causing this, any insight would be greatly appreciated!

image

richardbiely commented 5 years ago

In my opinion, the way you decided to do this is hacky and design breaking in the very least.

You can implement caves (just like any other layer) this way: public class YourCavesLayer: TerrainLayer { protected override void SetUp(LayerConfig config) { }

public override float GetHeight(Chunk chunk, int layerIndex, int x, int z, float heightSoFar, float strength)
{
    return heightSoFar;
}

public override float GenerateLayer(Chunk chunk, int layerIndex, int x, int z, float heightSoFar, float strength)
{
    // you have access to your Y-axis
    for (int y = 0; y < Env.ChunkSize; y++)
    {
        // you can access 3D noise
        int someNoise = (int)NoiseUtils.GetNoise(noise.Noise, x + chunk.Pos.x, y + chunk.Pos.y, z + chunk.Pos.z, some_scale, some_max, some_power);
        int index = Helpers.GetChunkIndex1DFrom3D(x, y, z);
        // you can set your blocks individualy
        chunk.Blocks.SetRaw(index, BlockProvider.AirBlock);
    }

    return heightSoFar;
}

}

And don't forget to set your layer's config "index" properly. The bigger the number the later the layer is evaluated (not exactly a good choice for layer system but it is what it is right now).

aesliva commented 5 years ago

I appreciate your insight! (Newbie programmer, still learning and always open to feedback) :)

I do have another question though, there is a height limit to how far down terrain generation goes. Camera is following on y axis.

image

This height limit can be changed via the height variable in GenerateTerrainForChunk. Is this limit intentional? If so, how would 'infinite' terrain generation on the y axis happen?

aesliva commented 5 years ago

Got some decent results after tweaking with the noise.

image

Using fractal billow noise at a low octave. Generating air at lowest values i.e: (caveNoise < caveThreshold).

richardbiely commented 5 years ago

To make a world infinite on the Y-axis, modify you world's config so it contains: "maxY": 0, "minY": 0,