8
\$\begingroup\$

I'm currently working on an infinite world, mostly inspired by minecraft.
A Chunk consists of 16x16x16 blocks. A block(cube) is 1x1x1.

This runs very smoothly with a ViewRange of 12 Chunks (12x16) on my computer. Fine.
When I change the Chunk height to 256 this becomes - obviously - incredible laggy.

So what I basically want to do is stacking chunks. That means my world could be [∞,16,∞] Chunks large.

The question is now how to generate chunks on the fly?
At the moment I generate not existing chunks circular around my position (near to far). Since I don't stack chunks yet, this is not very complex.

As important side note here: I also want to have biomes, with different min/max height. So in Biome Flatlands the highest layer with blocks would be 8 (8x16) - in Biome Mountains the highest layer with blocks would be 14 (14x16). Just as example.

What I could do would be loading 1 Chunk above and below me for example.
But here the problem would be, that transitions between different bioms could be larger than one chunk on y.




Transitions between Biomes




My current chunk loading in action

Chunk Loading Example



For the completeness here my current chunk loading "algorithm"

private IEnumerator UpdateChunks(){
    for (int i = 1; i < VIEW_RANGE; i += ChunkWidth) {
        float vr = i;
        for (float x = transform.position.x - vr; x < transform.position.x + vr; x += ChunkWidth) {
            for (float z = transform.position.z - vr; z < transform.position.z + vr; z += ChunkWidth) {

                _pos.Set(x, 0, z); // no y, yet
                _pos.x = Mathf.Floor(_pos.x/ChunkWidth)*ChunkWidth;
                _pos.z = Mathf.Floor(_pos.z/ChunkWidth)*ChunkWidth;

                Chunk chunk = Chunk.FindChunk(_pos);

                // If Chunk is already created, continue
                if (chunk != null)
                    continue;

                // Create a new Chunk..
                chunk = (Chunk) Instantiate(ChunkFab, _pos, Quaternion.identity);
            }
        }
        // Skip to next frame
        yield return 0;
    }
}
\$\endgroup\$
1
\$\begingroup\$

What you need to consider loading/creating one chunk above and below the surface in any given stack when the player is on the surface, so your generation algorithm needs to worry about stacks at the top level rather than chunks... when the player is below ground one above and below the current chunk level is fine. To clarify, a stack is a vertical column of chunks from bedrock to stratosphere :)

Another way to look at it would be to say if the surface is below the player's current chunk level -- generate the surface and one below, otherwise generate the current level and one above and below.

So lets say your world will be 256 chunks high (* 16 = 4096 voxel blocks), and at any time if a stack is within view range, you will have from 1 to 3 chunks in that stack actually loaded and rendering.

Biomes introduce an additional problem of blending heights at the edges, but you can handles that in the biome specific code that will be called to generate the surface and subsurface features. If you are using perlin/simplex noise to generate heights, if a chunk borders a chunk that is a different biome, you can get the noise values that both biome types would generate, then average them.

\$\endgroup\$
0
\$\begingroup\$

What you can do, is, making a chunk 256 high in y-direction and divide it into 16 sections, each being 16 blocks high. You then generate the data for the chunk, and build the geometry inside the sections.

One advantage would be, that you have access to the data of a complete chunk, which makes it easier to access the data above and below a section.

This also has the advantage of being able to easily cull away lots of geometry that does not lie inside the viewing frustum. There will also be many sections which do not have any geometry at all.

If you do not use it already, loading the chunks in another thread may also give you better frame rates, while generating the data for each chunk.

\$\endgroup\$
  • 1
    \$\begingroup\$ Well, maybe I've explained my self a bit vague. I already have planned to separate one chunk into 16 layers. But how do I decide which layers to load? Every biome has it's own min/max height. Imagine Biome A[100,20] Biome B[160,200] - I am on the edge of both biomes. They have a smooth transition. How to decide which layers to load? But as writing this I think i just have to check every layer (from top to down) and create it, when the layer above is empty/transparent - and stop when the first layer is created. Hope you get this ;) It's annoying you cant add blank lines in comments \$\endgroup\$ – Brettetete Aug 2 '14 at 17:22
  • 3
    \$\begingroup\$ I see your point. But if you implement an algorithm like greedy meshing, you should be able to load all of the layers without huge performance loss. You can find an article about greedy meshing here. I do the same in my voxel engine and it is working good so far. \$\endgroup\$ – user000user Aug 2 '14 at 20:56
  • \$\begingroup\$ This technique is just amazing. I'll try to implement this into my engine. The given code is a bit weird, would you share your implementation? :) \$\endgroup\$ – Brettetete Aug 3 '14 at 23:34
  • 2
    \$\begingroup\$ I pasted my C++ implementation in here and added some comments. Hope that helps :) \$\endgroup\$ – user000user Aug 4 '14 at 11:03
  • \$\begingroup\$ Awesome, this seems to be very helpful. Thankyou! I'll try to translate this now. But on #67 - #68 there is a double && - did you missed / doubled accidentally something to copy? :) And could you shortly explain / post the SHIFT_ methods ? Also I don't see any declaration / use of tmp - Well, sory for all that questions \$\endgroup\$ – Brettetete Aug 4 '14 at 16:52
0
\$\begingroup\$

I don't believe you can do this with simply loading certain layers because of the problem of transitions.

My inclination would be to store some metadata with each chunk:

1) Is the block entirely air. If so there's no need to render it.

2) For each face of the block is it opaque. An opaque face means you do not need to consider the next chunk. (Note, though, that depending on where the chunk is there could be as many as three faces involved--a chunk must be rendered if any of the three are not opaque. I suspect this is best pre-calculated--render so long either b1 is visible and has a non-opaque face f1 or b2 is visible has a non-opaque face f2 or b3 is visible has a non-opaque face f3.)

There are unfortunately over 7000 chunks within your 12 chunk sight range. However, I would expect few locations to have more than three vertical chunks that actually need to be rendered using this approach which cuts the chunk count to probably no more than 1500.

I would apply the same sort of logic within a chunk--when you load a chunk calculate what junctions are transparent and what junctions are opaque touching opaque--you only need to actually render faces where someone can see them. (Note that in Minecraft you have three types of block--transparent, opaque and vision-altering--glass, doors, fences etc. You can only skip transparent-transparent and opaque-opaque.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.