top of page
Search

An example of streaming custom terrain in Unity3D

Writer: Andy DoAndy Do

Games that have large-scale terrain usually split the terrain into small chunks. Instead of loading all terrain data into memory in one time, the games usually only load the chunks that lay in a specified range. This not only make it easy to control the memory usage of the terrain, but also can improve rendering performance by culling out un-visible chunks by view frustum. Some games use technologies like the quadtree to do terrain management, so the chunks may have different size and LOD levels. But no matter what technologies are used, chunks should always be streamed into memory from disk. The streaming process is often done in special threads except for the main thread and the rendering thread, so that it would not affect the game experience by annoying lags.

I wrote a simple example to show how to stream custom terrain in Unity3D through .NET thread and file stream APIs. There isn’t hierarchy or LOD mechanism in the simple custom terrain, but it is enough to explain the streaming process. The package file ADTerrainStream.unitypackage (Unity3D 2019.2.0f1) can be gotten form here:

https://github.com/eaglehorn58/UnityPackages

The way to use the package:

  1. Import the package into Unity3D.

  2. Open the scene: Assets/ADTerrainStream/Scenes/TerrainStreamScene.unity

  3. Select the menu item ‘Tools->AD Terrain Stream’ and click the ‘Start’ button in the popped window, this will create a 4K X 4K terrain and if succeed, a file will be generated at: Assets/ADTerrainStream/Terrain.data

  4. Play the game.

  5. Use WASDQE keys to move the camera and press right button to rotate it.

  6. Adjust view distance on TestTerrain’s inspector panel, adjust camera’s speed on Main Camera’s inspector panel.

The class TestTerrain overall controls the custom terrain object. It operates the terrain data file, creates loading thread, decides what terrain chunks should be streamed in or out in each frame, sends loading requests to loading thread, unloads terrain chunks that are out of view distance, create Unity3D objects (GameObject, Mesh, Material, etc.) for rendering terrain chunks. When TestTerrain is destroyed, it terminates the loading thread at the same time.

Initialization work is done in TestTerrain.Start method. First of all, the method opens a terrain data file and keeps its handle for later use. Then it creates the loading thread and some other fields that helps the streaming operations, such chunk cache, loading request list, etc.

TestTerrain.Update is called by Unity3D every frame, it calls UpdateTerrainNodes method to update the loading status for each terrain chunk considering both the camera’s position and the view distance. If a chunk newly enters into view range, UpdateTerrainNodes sends a loading request for the chunk to the loading thread. While on the other hand if a loaded chunk is out of view range, it will be destroyed. There may be a possibility that one chunk is going to be destroyed just after its loading request was sent out in preceding several frames and its data has not been loaded. This may happen when the camera moving in a high speed and turn round suddenly. In the case to destroy the in-loading chunk in UpdateTerrainNodes is dangerous because the loading thread may be happened to load data for it. In order to handle the issue, a cancel loading flag is set to the in-loading chunk and its destruction is postponed.

The loading thread enters into waiting state as soon as it has been created. It is waked up by main thread when a loading request is generated. The thread then reads chunk’s data from terrain file, and after this it pushes the chunk into a loaded list. When all loading requests have been processed, the loading thread enters into waiting state again.

The other thing done by main thread in TestTerrain.Update is calling GenerateNodeObjects method. This method gets chunks out from the loaded list and create Unity3D objects for them. The reason that Unity3D objects weren’t created in loading thread is that Unity3D requires all its objects to be created in the main thread. If we can get rid of this limitation, rendering data (mesh, texture, material, etc.) can also be created in loading threads, just like some other engines did.


 
 
 

Comentarios


©2020 by Andy Do. Proudly created with Wix.com

bottom of page