Multithread x Terrain Loading

Hello.

I've made a paged terrain system here loading terrain heightmaps/splatmaps from TCP/IP (directly with the Sockets class). All is working fine, and my adjacent terrains get loaded and displayed as soon as I get near the center terrain border, so I can keep walking and I won't reach the end of the center terrain before the adjacent one is loaded.

Although it is fine, one thing still annoys me... While the data streaming in another thread (using backgroundworker to load the heightmap and splatmap data and store it, so the main thread can display the terrain (as we have the thread-unsafe unity methods which makes life harder)) is loading correctly and I can move around the map while the data comes in, when the main thread takes over the job of creating the terrain object, my "game" is getting hung until the terrain is indeed created and populated with the streamed data.

I though, then, if I should start using StartCoroutine to do so, and I tried it, without success. It does start and it does continue the flow of my Update method, but still I get a hang while creating the terrain (create object, add heightmap info, add splatmap info, attach the textures that will be used by the splatmap). How can I achieve a seamless terrain creation, without having my app freezing in the meanwhile?

Code where the StartCoroutine is called (in Update loop): (It is compared by 4 as 4 is my state in my 3x3 grid of terrains that tells the loop that this terrain is not visible yet, but background worker already loaded data for it)

if (Global.terrainloading[x, y] == 4)
            {
                GameObject obj = GameObject.Find("TerrainContainer"+y.ToString()+x.ToString());
                StartCoroutine(Global.AssignTerrain(Global.terrainx + (-1 + x), Global.terrainy + (-1 + y), x, y, obj));

            }

And this is the AssignTerrain method:

private static IEnumerator AssignTerrain(int x, int y, int tx, int ty, GameObject obj)
    {
        Debug.Log("Started terrain loading");
        obj.AddComponent(typeof(Terrain));
        TerrainData terrain = new TerrainData();
        Vector3 sz = new Vector3(100, 1000, 100);
        terrain.size = sz;
        int w2 = 1025;
        terrain.heightmapResolution = w2;

        float[,] heightmapData = (float[,])heightmaploading[tx,ty];
        float[, ,] SplatmapData = (float[,,])splatmaploading[tx, ty];

        terrain.SetHeights(0, 0, heightmapData);

        // Set up SplatPrototypes with the correct terrain textures (for now 3, 0=grass,1=rock,2=sand)
        SplatPrototype[] splatprotos = new SplatPrototype[3];
        splatprotos[0] = new SplatPrototype();
        splatprotos[0].texture = (Texture2D)Resources.Load("grass", typeof(Texture2D));
        splatprotos[0].tileOffset = new Vector2(0, 0);
        splatprotos[0].tileSize = new Vector2(15, 15);
        splatprotos[1] = new SplatPrototype();
        splatprotos[1].texture = (Texture2D)Resources.Load("rock", typeof(Texture2D));
        splatprotos[1].tileOffset = new Vector2(0, 0);
        splatprotos[1].tileSize = new Vector2(15, 15);
        splatprotos[2] = new SplatPrototype();
        splatprotos[2].texture = (Texture2D)Resources.Load("seasand", typeof(Texture2D));
        splatprotos[2].tileOffset = new Vector2(0, 0);
        splatprotos[2].tileSize = new Vector2(15, 15);
        terrain.splatPrototypes = splatprotos;
        terrain.alphamapResolution = 1025;
        terrain.baseMapResolution = 1025;

        ((TerrainCollider)obj.AddComponent(typeof(TerrainCollider))).terrainData = terrain;
        ((Terrain)obj.GetComponent(typeof(Terrain))).terrainData = terrain;

        ((Terrain)obj.GetComponent(typeof(Terrain))).terrainData.SetAlphamaps(0, 0, SplatmapData);
        ((Terrain)obj.GetComponent(typeof(Terrain))).heightmapPixelError = 10;
        Global.terrainloading[tx, ty] = 2;
        yield return null;
    }

Well, just posting to close the question.

I've contacted Unity support and we worked for months in a solution for this, and unfortunately, there is none currently, until Unity becomes more thread-friendly, that is, so the only approach is to try to minimize the effect by loading smaller terrain chunks. A pity indeed.

Well, just for future reference to anyone who enters this post, and to make Unity justice.

I found a way to make this work perfectly, but unfortunately, it requires Unity Pro.
Instead of streaming heightmap, splatmap and all the data and bake a terrainData on-the-fly, import the terrain in the editor, edit it as you will, and save it in the asset database.
Navigating to your project folder, in the asset folder, you shall find a .asset file for your terrain. Copy it and place in your server.
When requesting a terrain, stream this .asset file and in a thread, save it somewhere in the disk with the client, and then use AssetBundle methods to load the .asset file on-the-fly using StartCoRoutine. This will bring the asset to the asset database as it were when you built the client, and then just reference this terrainData to your displayed terrain.

This will avoid having to process several stuff in your main thread, and then you won’t have a load when switching terrains.

Correct me if I’m wrong, but if you save the TerrainData (which is what the .asset file you mention is), and use that to build your terrain, won’t you lose certain data about your terrain? To be sure, the TerrainData stores most of the information, but the terrain itself has the terrain setting information; pixel error, detail distance, density, etc.

If you never changed any of those settings then no big deal I guess.

Also, your current method is creating new terrains at run time, right? I don’t see how else you’d use the TerrainData. Perhaps that’s causing your lag? Woudln’t it be better to simply save your terrain into a prefab, save that prefab and the TerrainData into an assetbundle, then when you need the terrain, load the asset bundle and instantiate the terrain prefab? Don’t know how that will work, but it’s just an idea . . .