Monday, February 8, 2016

Terrain loading design

Current Design

The Playform client divides the world into discrete, fixed-size chunks. A chunk is either entirely loaded or entirely unloaded, at some LOD. When a chunk is needed, the client requests all the voxels of the given LOD in that chunk, as well as neighboring voxels of that LOD. Each chunk in the client then "waits" to receive all the voxels it needs, before generating the mesh and loading it. Chunks are not unloaded until they are either further than the view distance, or a new chunk is being loaded at the same position.

Between adjacent chunks of different LODs, seam meshes need to be loaded to stitch together the different LODs. This is not done.

Ideal Design

Mesh generation happens around edges - every edge between four adjacent voxels can generate a mesh fragment. It doesn't require any chunking, and in fact, chunking adds annoying edge cases (heh) and things to be careful of. Ideally, voxels would just be requested at a given LOD based on their distance from the player. When the server sends a voxel back, we could examine all adjacent edges to see if any new mesh fragments should be loaded/unloaded.

There are some extra wrinkles with this approach: one is that a voxel may be received at a higher LOD than we had before, which means the new voxel is smaller. If we unload all the mesh fragments including the old voxel, in order to load the ones including the new voxel, we will have created holes. We face a similar problem right now, when chunk LODs need to be switched, which we solve by putting off unloading until all the new voxels have been received, at which point we can generate all the mesh fragments at a higher LOD. We can do something similar here, but it will be more complex, since chunks had fixed sizes and a more obvious set of legal positions.

Another related difficulty is the problem of determining which mesh fragments describe the same area. Fragments of different LODs are extracted from edges of different sizes, so we can't use any kind of equivalence relation (e.g. hashing) to find conflicting edges quickly. For example, if we receive a new voxel of size 1, we might look at its adjacent edges and find that some of them are able to generate mesh fragments that should be loaded. In this case, we would need to unload any mesh fragments associated with the same regions of space, but mesh fragments could be associated with that region not only via edges of size 1, but also of size 1/2, 1/4, 2, 4, etc. We need a data structure that gives a reasonably efficient way of finding which edges conflict with one another.

No comments:

Post a Comment