Search Unity

Resolved Creating mesh with threading

Discussion in 'Scripting' started by FederalRazer89, May 10, 2020.

  1. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Hello

    I been generating some mesh with the help of some tutorials like this one.


    After some tweaking was able to make several controls for modifying the mesh. But i have been trying for about a month to add some mulit core support, and have been following this tutorial series.

    https://github.com/SebLague/Procedural-Landmass-Generation/tree/master/Proc Gen E08/Assets/Scripts

    But the tutorial focuse on creating an endless terrain with a lot of diffrent features which is fairly embedded in the code, and for me to try and seperate the mesh generation and threading is hard. And i have googled for more information about MeshData and threading without any good results.

    If anyone could show me a simple example that would be helpful.
     
    Last edited: May 21, 2020
  2. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    What is it you are having troubles with specifically? Do you know how threading works? Or is your problem more related to the fact that you cannot work with most of the Unity API off the main Unity thread? In the end, you'll have to threat your meshes like arrays of numbers (which is what they are), which you can crunch on a different thread, and then synch that info with the main thread and create the mesh there. Purely mathematical things are comparably easy to outsource to a different thread. So you can rather easily outsource things like the perlin (or other) noise generation and do things based on it.

    I followed the tutorial by Sebastian Lague a while back when i first got into procedural generation and i believe it's pretty well made. Extracting the multithreading from one video alone may be a bit much, but if you followed along with the tutorial (ie, typed the whole thing down while following it), you should understand which part does what, and how to translate that knowledge to your own project.

    Threading itself is a rather advanced topic. There are some tools to make it easier for the user, like the newish DOTS in Unity, where you can use Jobs to rather easily outsource work off the mainthread, while the Job system manages all of the threading for you and is extremely efficient. It's also possible to write efficient mesh generation code with it, as i used it for exactly that in the past but i cant say i can recomment it to a beginner. It's an absolute nightmare due to lack of documentation and so on at the moment.

    If you are working on heightmap procedural terrain (ie, no overhangs and caves), then an 'easy' way to approach multithreading is to simply generate one full chunk per thread. This is not the most efficient thing you can do, and you will still need to create the mesh itself on the main thread, but you'd at least utilize multiple threads if multiple chunks need to be generated at the same time.
     
  3. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    More or less this. But after reading your replie and when i was about to answer it, i realised that i saw somethings from the wrong perspectiv. Think i need to check out the tutorial again.
     
  4. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    As a rule of thumb, maybe for some extra guidance, let me try to roughly explain the whole idea:

    First get yourself some threads. Creating the thread is the only somewhat expensive part about the whole thing, so you dont want to constantly create new threads. Instead keep them around if possible. Base the number you create on the number of available cores (which you can read out) and your needs, of course. Alternatively you could use DOTS, since it handles all of this for you and comes with a pretty hefty 'free' performance gain, but you'd have to get into it. Nothing better, performance wise, but i really wouldnt recommend it for now.
    After you got your worker threads up one or the other way, you want to outsource your current task to that thread - or multiple ones for a more advanced system, but let's keep it simple for now. The only thing you cannot work with off the main thread, are (big) parts of the Unity API. This means, you'll need to strip your current approach of all things that cannot be done off the main thread. Namely, but not exclusive to, meshes. This doesnt mean, however, that you cannot work with meshes. It just means you cannot use the mesh object itself off the main thread. And you dont need to. The only thing you most likely do with the mesh object, is assign mesh.vertices, mesh.triangles and mesh.uvs some content. Instead of doing that, simply calculate the content for vertices, triangles and uvs (which are just arrays of numbers) like you are used to. Once the thread is done (which you can regularily check from the main thread), you can collect these results and then assign them to some mesh on the main thread. This puts the heavy load off the main thread, which now only needs to assign some values.
    Generally speaking, just do that per thread (in case you need more than one).

    All of this said, heightmap based procedural terrain is very (comparably) cheap to calculate. Getting the load off the main thread is always a good idea for these kind of things, but if you are experiencing major slowdowns, there is probably something else wrong with your approach, so you may want to check the profiler in Unity to figure out what's going on.
     
    FederalRazer89 likes this.
  5. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    I agree to what you say. Have checked out a ECS/ jobsystem tutorial on Udemy, but i feel that its not too new at the moment and i want to learn more about the standard threading in C#, plan to try doing more things besides making games.
    I also might try doing other things, then just a procedural terrain. Thinking about adding perhaps caves and stuff, got somethings i am going to experiment with later.

    Now i have done a review in greater detail, and understand more about the mesh creating part and only got a bit more understanding about threading. I have been having problems finding documentation describing some of the threading syntax's that he uses, so i kind of understand what they do but i would not be able to utilize it.

    I would like to write it in a simpler way so i can increase readability for myself.
    (https://github.com/SebLague/Procedural-Landmass-Generation/blob/master/Proc Gen E08/Assets/Scripts/MapGenerator.cs)
    (https://github.com/SebLague/Procedural-Landmass-Generation/blob/master/Proc Gen E08/Assets/Scripts/EndlessTerrain.cs)


    Code (CSharp):
    1.  
    2. Queue<MapThreadInfo<MapData>> mapDataThreadInfoQueue = new Queue<MapThreadInfo<MapData>>();
    3.  
    4.     //Is there a simpler way to do this?
    5.     public void RequestMapData(Action<MapData> callback)
    6.     {
    7.         ThreadStart threadStart = delegate {
    8.             MapDataThread (callback);
    9.         };
    10.  
    11.         new Thread (threadStart).Start ();
    12.     }
    13.  
    14.     void MapDataThread(Action<MapData> callback)
    15.     {
    16.         MapData mapData = GenerateMapData ();
    17.         lock (mapDataThreadInfoQueue) {
    18.             mapDataThreadInfoQueue.Enqueue (new MapThreadInfo<MapData> (callback, mapData));
    19.         }
    20.     }
    The Queue function is it necessary? Cant i just use a array, list or for? I am not going to create a Endless terrain.

    I kind of understand "(Action<MapData> callback)" ,but the way the "RequestMapData" is triggered, is kind of hard to follow because is called through severals steps. If i could see a simple example that could work for my application.


    Might understand more after sleeping some, had several night dreaming about this. But any help would be appreciated.
     
  6. Yoreki

    Yoreki

    Joined:
    Apr 10, 2019
    Posts:
    2,605
    I dont really work with delegates, and last time i implemented something bigger with "normal threading" was in Java.
    However, the type you are using is up to you, as long as you make sure to prevent concurrency problems.

    If you have problems understanding some of the involved topics into threading, then try looking up what problems threading can cause. Most of the essentials of threading are fixes for those problems. Threading in general is a bit more advanced topic tho, since the problems originate in the architecture of modern CPUs. On a single thread, everything happens sequentially, so basically nothing can go wrong (other than you not properly defining what you want, ie bugs). When you share some data between threads (which may run on different CPU cores), the shared data can be read / written to at the same time from different locations. This can cause severe problems for most tasks, even trivial ones like incrementing a number a couple times. These are the things you need to understand, because these are the problems that are prevented by things like 'lock'.
     
    Last edited: May 22, 2020
  7. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Well i am going to do some testing after i get some sleep:)
     
  8. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    I have done a bit of testing and a lot of reading, also some youtube videos.
    So i kind of know what i want to do with threading and how i will try to work with it.

    But i have one thing that i couldn't find any info about threading in Unity. And that is if the idle worker threads impact performance when i create new threads or use thread pool? Is it possible to assign any task to those work threads or do you have to go thought the JOB system?

    Wouldn't mind going checking out how to use JOB system, but i planed to start learning the JOB system and ECS when i reached a point were i hade several gameplay systems up and running. And as Yoreki mentioned ECS do lack a bit of documentation, so it would take potentially a lot more time to learn how to use JOB system and ECS.
     
  9. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Tried to implement some of the things i have learned, but got stuck on with some error referring to HD pipeline.
    Might be that i introduced some bug with my script, but when i compile in VS 2019 it don't complain. So i am not sure were i should start troubleshoot.

    If anyone could point me in the right direction that would be nice

    using unity version 2019.3.3f1

    Error message:

    NullReferenceException: Object reference not set to an instance of an object
    UnityEngine.Rendering.HighDefinition.FrameSettingsHistory+MinimalHistoryContainer..ctor () (at Library/PackageCache/com.unity.render-pipelines.high-definition@7.1.8/Runtime/RenderPipeline/Settings/FrameSettingsHistory.cs:59)

    UnityEngine.Rendering.HighDefinition.FrameSettingsHistory..cctor () (at Library/PackageCache/com.unity.render-pipelines.high-definition@7.1.8/Runtime/RenderPipeline/Settings/FrameSettingsHistory.cs:67)

    Rethrow as TypeInitializationException: The type initializer for 'UnityEngine.Rendering.HighDefinition.FrameSettingsHistory' threw an exception.

    UnityEngine.Rendering.HighDefinition.DebugDisplaySettings.RegisterCamera (UnityEngine.Rendering.HighDefinition.IFrameSettingsHistoryContainer container) (at Library/PackageCache/com.unity.render-pipelines.high-definition@7.1.8/Runtime/Debug/DebugDisplay.cs:899)

    UnityEngine.Rendering.HighDefinition.HDAdditionalCameraData.RegisterDebug () (at Library/PackageCache/com.unity.render-pipelines.high-definition@7.1.8/Runtime/RenderPipeline/Camera/HDAdditionalCameraData.cs:436)

    UnityEngine.Rendering.HighDefinition.HDAdditionalCameraData.OnEnable () (at Library/PackageCache/com.unity.render-pipelines.high-definition@7.1.8/Runtime/RenderPipeline/Camera/HDAdditionalCameraData.cs:469)



    The script

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Concurrent;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6. using System.Threading;
    7.  
    8.  
    9. public class TerrainGenerator : MonoBehaviour
    10. {
    11.  
    12.     GameObject terrain;
    13.  
    14.     Mesh terrainMesh;
    15.  
    16.     Vector3[] vertices;
    17.     int[] triangles;
    18.     Vector2[] uvs;
    19.  
    20.     public int xVertices = 20;
    21.     public int zVertices = 20;
    22.  
    23.     MeshFilter meshFilter;
    24.  
    25.     //   meshFilter = meshObject.AddComponent<MeshFilter>();
    26.  
    27.     void Start()
    28.     {
    29.         terrain = new GameObject("Empty");
    30.         terrainMesh = new Mesh();
    31.  
    32.         terrain.AddComponent<MeshFilter>();
    33.         terrain.AddComponent<MeshRenderer>();
    34.         terrainMesh = terrain.GetComponent<MeshFilter>().mesh;
    35.         TileData tiledata = new TileData(xVertices, zVertices);
    36.  
    37.  
    38.         meshFilter.mesh = tiledata.CreateMesh();
    39.         CreatingTile();
    40.      
    41.  
    42.     }
    43.  
    44.  
    45.     void CreatingTile()
    46.     {
    47.  
    48.         TileData tiledata = new TileData(xVertices, zVertices);
    49.         // vertices = new Vector3[(xVertices + 1) * (zVertices + 1)];
    50.  
    51.         for (int i = 0, z = 0; z <= zVertices; z++)
    52.         {
    53.             for (int x = 0; x <= xVertices; x++)
    54.             {
    55.                 float y = Mathf.PerlinNoise(x * .3f, z * .3f) * 2f;
    56.                 tiledata.vertices[i] = new Vector3(x, y, z);
    57.                 i++;
    58.  
    59.             }
    60.  
    61.         }
    62.  
    63.         //triangles = new int[xVertices * zVertices * 6];
    64.  
    65.         int vert = 0;
    66.         int tris = 0;
    67.         for (int z = 0; z < zVertices; z++)
    68.         {
    69.             for (int x = 0; x < xVertices; x++)
    70.             {
    71.  
    72.  
    73.                 tiledata.triangles[tris + 0] = vert + 0;
    74.                 tiledata.triangles[tris + 1] = vert + xVertices + 1;
    75.                 tiledata.triangles[tris + 2] = vert + 1;
    76.                 tiledata.triangles[tris + 3] = vert + 1;
    77.                 tiledata.triangles[tris + 4] = vert + xVertices + 1;
    78.                 tiledata.triangles[tris + 5] = vert + xVertices + 2;
    79.  
    80.                 vert++;
    81.                 tris = tris + 6;
    82.  
    83.  
    84.             }
    85.             vert++;
    86.         }
    87.  
    88.         //Creating UVS
    89.         //uvs = new Vector2[vertices.Length];
    90.  
    91.         for (int i = 0, z = 0; z <= zVertices; z++)
    92.         {
    93.             for (int x = 0; x <= xVertices; x++)
    94.             {
    95.                 tiledata.uvs[i] = new Vector2((float)x / xVertices, (float)z / zVertices);
    96.                 //uvs[i] = new Vector2((float)x / xVertices, (float)z / zVertices);
    97.                 i++;
    98.             }
    99.         }
    100.  
    101.         Debug.LogWarning("count vertices = " + tiledata.vertices);
    102.         Debug.LogWarning("count triangles = " + tiledata.triangles);
    103.         Debug.LogWarning("count uvs = " + tiledata.uvs);
    104.  
    105.  
    106.     }
    107.  
    108.     public class TileData
    109.     {
    110.  
    111.         public Vector3[] vertices;
    112.         public int[] triangles;
    113.         public Vector2[] uvs;
    114.  
    115.         public TileData(int xVertices, int zVertices)
    116.         {
    117.             vertices = new Vector3[xVertices * zVertices];
    118.             uvs = new Vector2[vertices.Length];
    119.             triangles = new int[xVertices * zVertices * 6];
    120.         }
    121.  
    122.         public Mesh CreateMesh()
    123.         {
    124.             Mesh mesh = new Mesh();
    125.             mesh.vertices = vertices;
    126.             mesh.triangles = triangles;
    127.             mesh.uv = uvs;
    128.             mesh.RecalculateNormals();
    129.             return mesh;
    130.         }
    131.  
    132.     }
    133.  
    134.  
    135. }
     
  10. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    This probably has nothing (or little) to do with your code, as you don't even have any HDRP things/refrences included in there.

    I see a few things that look dubious, like that MeshFilter. Where do you assign it? I tried to run your code and it (of course) doesn't work as-is in an empty game object. That first NullReferenceException comes from that...

    I'd also suggest you check your CreatingTile method, it seems to fail, too (goes out of bounds of your array.) I'd suggest you print out enough things so that you rule out assumptions.

    And your debug prints seem to be printing a bit of gibberish here; you just print the array object (ToString) if you print out tiledata.vertices. Instead, print out tiledata.vertices.Length if you want to see count. Same goes for those others (tris, uvs.)

    There's other issues too.
     
    Last edited: May 31, 2020
    FederalRazer89 likes this.
  11. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Thanks for pointing out were i should start looking, kind of new to C# and the unity API.
    Might had spent a week trying to understand the error, would have been easier if the code didn't compile.

    Haven't gone through the CreateTile() yet, but if it gets out of bounds shouldn't the compiler complain?

    The print don't seam to work at all, added a simple Debug.LogWarning at the top of void Start() and nothing happens.

    But i still cant get rid of the error, did some changes to the void Start()

    Code (CSharp):
    1.    
    2.     void Start()
    3.     {
    4.         Debug.LogWarning("is this working?");
    5.         TileData tiledata = new TileData(xVertices, zVertices);
    6.         terrain = new GameObject("Empty");
    7.         MeshRenderer meshRenderer = terrain.AddComponent<MeshRenderer>();
    8.  
    9.         meshRenderer.sharedMaterial = new Material(Shader.Find("Standard"));
    10.  
    11.         MeshFilter meshFilter = terrain.AddComponent<MeshFilter>();
    12.  
    13.         Mesh terrainMesh = new Mesh();
    14.  
    15.         CreatingTile();
    16.      
    17.         tiledata.CreateMesh();
    18.  
    19.  
    20.         terrainMesh.vertices = tiledata.vertices;
    21.         terrainMesh.triangles = tiledata.triangles;
    22.         //terrainMesh.normals = tiledata.normals;
    23.         terrainMesh.uv = tiledata.uvs;
    24.         terrainMesh.RecalculateNormals();
    25.  
    26.         meshFilter.mesh = terrainMesh;
    27.     }
    28.     }
     
    Last edited: Jun 1, 2020
  12. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    Those out of bounds - when you check "z <= zVertices" you should probably do "z < zVertices" as the counting starts from 0, so if you had 32 units, the 32nd would be actually index number 31.

    You could just use Debug.Log?
     
  13. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Checked out the error, and it turns out that some file of HDRP was missing or corrupt so i removed the packages and reinstalled them. Now i can se the logs and errors that didn't show up by the compiler.

    Now i can at least se what i am doing wrong :)
     
  14. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    Yes it's important to fix errors as soon as they appear, otherwise that happens what happened to you :).
     
    FederalRazer89 likes this.
  15. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    It got a lot more easier to progress now :).

    Got the mesh creating now but its pink, will look at that after i have tried implementing threading but any suggestions about the pink mesh is appreciated. I think i understand the theory of threading, just need to execute it:(. Hoping it will not take a week for me to implement threading.


    Current code
    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Concurrent;
    5. using System.Collections.Generic;
    6. using UnityEngine;
    7. using System.Threading;
    8.  
    9.  
    10. public class TerrainGenerator : MonoBehaviour
    11. {
    12.  
    13.  
    14.  
    15.  
    16.  
    17.     public int xVertices = 20;
    18.     public int zVertices = 20;
    19.  
    20.  
    21.  
    22.  
    23.     void Start()
    24.     {
    25.      
    26.         TileData tileData = new TileData(xVertices, zVertices);
    27.  
    28.         Vector3 positionV3 = new Vector3(0, 0, 0);
    29.  
    30.         GameObject terrain;
    31.         terrain = new GameObject("Empty");
    32.  
    33.  
    34.         MeshRenderer meshRenderer = terrain.AddComponent<MeshRenderer>();
    35.         meshRenderer.sharedMaterial = new Material(Shader.Find("Standard"));
    36.         MeshFilter meshFilter = terrain.AddComponent<MeshFilter>();
    37.  
    38.         CreatingTile(tileData);
    39.  
    40.         terrain.transform.position = positionV3;
    41.  
    42.  
    43.         meshFilter.mesh = tileData.CreateMesh();
    44.  
    45.     }
    46.  
    47.  
    48.     void CreatingTile(TileData tileData)
    49.     {
    50.  
    51.  
    52.         for (int i = 0, z = 0; z <= zVertices; z++)
    53.         {
    54.             for (int x = 0; x <= xVertices; x++)
    55.             {
    56.                 float y = Mathf.PerlinNoise(x * .3f, z * .3f) * 2f;
    57.                 tileData.vertices[i] = new Vector3(x, y, z);
    58.                 i++;
    59.  
    60.             }
    61.  
    62.         }
    63.  
    64.         //triangles = new int[xVertices * zVertices * 6];
    65.  
    66.         int vert = 0;
    67.         int tris = 0;
    68.         for (int z = 0; z < zVertices; z++)
    69.         {
    70.             for (int x = 0; x < xVertices; x++)
    71.             {
    72.  
    73.  
    74.                 tileData.triangles[tris + 0] = vert + 0;
    75.                 tileData.triangles[tris + 1] = vert + xVertices + 1;
    76.                 tileData.triangles[tris + 2] = vert + 1;
    77.                 tileData.triangles[tris + 3] = vert + 1;
    78.                 tileData.triangles[tris + 4] = vert + xVertices + 1;
    79.                 tileData.triangles[tris + 5] = vert + xVertices + 2;
    80.  
    81.                 vert++;
    82.                 tris = tris + 6;
    83.  
    84.  
    85.             }
    86.             vert++;
    87.         }
    88.  
    89.         //Creating UVS
    90.         //uvs = new Vector2[vertices.Length];
    91.  
    92.         for (int i = 0, z = 0; z <= zVertices; z++)
    93.         {
    94.             for (int x = 0; x <= xVertices; x++)
    95.             {
    96.                 tileData.uvs[i] = new Vector2((float)x / xVertices, (float)z / zVertices);
    97.                 //uvs[i] = new Vector2((float)x / xVertices, (float)z / zVertices);
    98.                 i++;
    99.             }
    100.         }
    101.  
    102.     }
    103.  
    104.     public class TileData
    105.     {
    106.  
    107.         public Vector3[] vertices;
    108.         public int[] triangles;
    109.         public Vector2[] uvs;
    110.  
    111.         public TileData(int xVertices, int zVertices)
    112.         {
    113.             vertices = new Vector3[(xVertices + 1) * (zVertices + 1)];
    114.             uvs = new Vector2[vertices.Length];
    115.             triangles = new int[xVertices * zVertices * 6];
    116.  
    117.         }
    118.  
    119.         public Mesh CreateMesh()
    120.         {
    121.             Mesh mesh = new Mesh();
    122.             mesh.vertices = vertices;
    123.             mesh.triangles = triangles;
    124.             mesh.uv = uvs;
    125.             mesh.RecalculateNormals();
    126.             return mesh;
    127.         }
    128.  
    129.  
    130.     }
    131.  
    132.  
    133. }
    134.  
     
  16. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Are doing some testing just to try and understand threading and i encountered something i am not really sure of how to correct.

    The variable tasknumber in the example below all outputs the same value, but i intended to assign one value to each task so i can keep track of which task did what.

    Test Code:

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Collections.Concurrent;
    5. using UnityEngine;
    6. using System.Threading;
    7. using System.Threading.Tasks;
    8.  
    9. public class ThreadTest : MonoBehaviour
    10. {
    11.     ConcurrentQueue<int[]> TestCQ = new ConcurrentQueue<int[]>();
    12.     Task[] tasks = new Task[10];
    13.     bool taskDoneBool = false;
    14.  
    15.     // Start is called before the first frame update
    16.     void Start()
    17.     {
    18.        
    19.  
    20.  
    21.         for (int i = 0; i < 10; i++)
    22.         {
    23.            
    24.             tasks[i] = Task.Run(() => Adding(2000, i));
    25.  
    26.         }
    27.         Debug.Log("Starting tasks");
    28.  
    29.     }
    30.  
    31.     // Update is called once per frame
    32.     void Update()
    33.     {
    34.         int count = 0;
    35.         for (int i = 0; i < 10; i++)
    36.         {
    37.             if (tasks[i].Status == TaskStatus.RanToCompletion)
    38.             {
    39.                 count++;
    40.             }
    41.         }
    42.         if (count == 10 && !taskDoneBool)
    43.         {
    44.             TaskFinished();
    45.             taskDoneBool = true;
    46.         }
    47.     }
    48.  
    49.     void Adding(int length, int tasknumber)
    50.     {
    51.  
    52.         int[] ArrayTest = new int[2] {tasknumber, 10};
    53.         TestCQ.Enqueue(ArrayTest);
    54.         Thread.Sleep(length);
    55.  
    56.         Debug.Log("Task " + tasknumber);
    57.  
    58.     }
    59.  
    60.     void TaskFinished()
    61.     {
    62.         int[] ArrayContainer = new int[10];
    63.         for (int i = 0; i < 10; i++)
    64.         {
    65.  
    66.  
    67.  
    68.         }
    69.             Debug.Log("All Tasks are done ");
    70.  
    71.     }
    72. }
     
  17. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Was able to make it work with the "Task" metod. But got the different tiles are not aligned yet, i will have to work on that
    upload_2020-6-10_11-3-27.png

    Current code:

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Concurrent;
    4. using System.Collections.Generic;
    5. using UnityEngine;
    6. using System.Threading;
    7. using System.Threading.Tasks;
    8.  
    9.  
    10. public class TerrainGenerator : MonoBehaviour
    11. {
    12.  
    13.  
    14.  
    15.     public int tileMeshSize = 20;
    16.     public int tileCount = 10;
    17.     ConcurrentQueue<TileData> tileCQ = new ConcurrentQueue<TileData>();
    18.  
    19.  
    20.     void Start()
    21.     {
    22.         //TODO check why the material is pink
    23.         Task[] tasks = new Task[tileCount];
    24.         TileData[] tileArrayData = new TileData[tileCount];
    25.         GameObject[] terrainTiles = new GameObject[tileCount];
    26.  
    27.         for (int i = 0; i < tileCount; i++)
    28.         {
    29.             tileArrayData[i] = new TileData(tileMeshSize, i, tileMeshSize * i, tileMeshSize * i);
    30.  
    31.         }
    32.         for (int i = 0; i < tileCount; i++)
    33.         {
    34.             var x = i;
    35.             tasks[x] = Task.Run(() => CreatingTile(tileArrayData[x]));
    36.         }
    37.  
    38.     }
    39.  
    40.     void Update()
    41.     {
    42.         if (tileCQ.Count == tileCount)
    43.         {
    44.             CreatingMeshFromTileData();
    45.             enabled = false;
    46.         }
    47.    
    48.     }
    49.  
    50.     //Testing
    51.  
    52.  
    53.     void CreatingMeshFromTileData()
    54.     {
    55.         TileData[] tileArrayDataReady = new TileData[tileCount];
    56.         GameObject[] terrainCollection = new GameObject[tileCount];
    57.         MeshRenderer[] meshRenderers = new MeshRenderer[tileCount];
    58.         MeshFilter[] meshFilters = new MeshFilter[tileCount];
    59.         Vector3[] tilePos = new Vector3[tileCount];
    60.         for (int i = 0; i < tileCount; i++)
    61.         {
    62.             tileCQ.TryDequeue(out tileArrayDataReady[i]);
    63.  
    64.         }
    65.  
    66.         for (int i = 0; i < tileCount; i++)
    67.         {
    68.             tilePos[i] = new Vector3(0, 0, i * tileMeshSize);
    69.             terrainCollection[i] = new GameObject("Empty");
    70.             meshRenderers[i] = terrainCollection[i].AddComponent<MeshRenderer>();
    71.             meshFilters[i] = terrainCollection[i].AddComponent<MeshFilter>();
    72.             terrainCollection[i].transform.position = tilePos[i];
    73.  
    74.             meshFilters[i].mesh = tileArrayDataReady[i].CreateMesh();
    75.             meshRenderers[i].sharedMaterial = new Material(Shader.Find("Standard"));
    76.         }
    77.      
    78.     }
    79.  
    80.     void CreatingTile(TileData tileData)
    81.     {
    82.      
    83.  
    84.         for (int i = 0, z = 0; z <= tileMeshSize; z++)
    85.         {
    86.             for (int x = 0; x <= tileMeshSize; x++)
    87.             {
    88.                 float y = Mathf.PerlinNoise((x + tileData.xStartPos) * .3f, (z + tileData.zStartPos) * .3f) * 2f;
    89.                 tileData.vertices[i] = new Vector3(x, y, z);
    90.                 i++;
    91.  
    92.             }
    93.  
    94.         }
    95.  
    96.  
    97.         int vert = 0;
    98.         int tris = 0;
    99.         for (int z = 0; z < tileMeshSize; z++)
    100.         {
    101.             for (int x = 0; x < tileMeshSize; x++)
    102.             {
    103.  
    104.  
    105.                 tileData.triangles[tris + 0] = vert + 0;
    106.                 tileData.triangles[tris + 1] = vert + tileMeshSize + 1;
    107.                 tileData.triangles[tris + 2] = vert + 1;
    108.                 tileData.triangles[tris + 3] = vert + 1;
    109.                 tileData.triangles[tris + 4] = vert + tileMeshSize + 1;
    110.                 tileData.triangles[tris + 5] = vert + tileMeshSize + 2;
    111.  
    112.                 vert++;
    113.                 tris = tris + 6;
    114.  
    115.             }
    116.             vert++;
    117.         }
    118.  
    119.  
    120.         for (int i = 0, z = 0; z <= tileMeshSize; z++)
    121.         {
    122.             for (int x = 0; x <= tileMeshSize; x++)
    123.             {
    124.                 tileData.uvs[i] = new Vector2((float)x / tileMeshSize, (float)z / tileMeshSize);
    125.                 i++;
    126.             }
    127.         }
    128.         tileCQ.Enqueue(tileData);
    129.     }
    130.  
    131.     public class TileData
    132.     {
    133.         public int tileIndex;
    134.         public Vector3[] vertices;
    135.         public int[] triangles;
    136.         public Vector2[] uvs;
    137.  
    138.         public int xStartPos;
    139.         public int zStartPos;
    140.  
    141.         public int xEndPos;
    142.         public int zEndPos;
    143.  
    144.         public TileData(int tileMeshSize, int index, int xSPos, int zSPos)
    145.         {
    146.  
    147.             xStartPos = xSPos;
    148.             zStartPos = zSPos;
    149.  
    150.             xEndPos = xSPos + tileMeshSize;
    151.             zEndPos = zSPos + tileMeshSize;
    152.  
    153.  
    154.             tileIndex = index;
    155.             vertices = new Vector3[(tileMeshSize + 1) * (tileMeshSize + 1)];
    156.             uvs = new Vector2[vertices.Length];
    157.             triangles = new int[tileMeshSize * tileMeshSize * 6];
    158.  
    159.         }
    160.  
    161.         public Mesh CreateMesh()
    162.         {
    163.             Mesh mesh = new Mesh();
    164.             mesh.vertices = vertices;
    165.             mesh.triangles = triangles;
    166.             mesh.uv = uvs;
    167.             mesh.RecalculateNormals();
    168.             return mesh;
    169.         }
    170.  
    171.  
    172.     }
    173.  
    174. }
    175.  
    176.  
    Think i might be able to fix it by the end of the week
     
  18. FederalRazer89

    FederalRazer89

    Joined:
    Nov 24, 2019
    Posts:
    83
    Solved it now.

    Got some more work to do but if someone want to look at another example of threading with mesh creation.
    Need to add smother normals between tiles, collision, material, more comments and more controls.

    Never got this kind of hints when i was trying to use unreal for half year, and now i was able to learn and do twice the amount. Thanks for all the help a noob like me would need :).

    upload_2020-6-16_4-8-28.png


    Code (CSharp):
    1.  
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Concurrent;
    5. using System.Collections.Generic;
    6. using UnityEngine;
    7. using System.Threading;
    8. using System.Threading.Tasks;
    9.  
    10.  
    11. public class TerrainGenerator : MonoBehaviour
    12. {
    13.  
    14.  
    15.  
    16.     public int tileMeshSize = 20;
    17.     public int tileCountX = 10;
    18.     public int tileCountY = 10;
    19.     ConcurrentQueue<TileData> tileCQ = new ConcurrentQueue<TileData>();
    20.  
    21.  
    22.     void Start()
    23.     {
    24.        
    25.  
    26.         Task[] tasks = new Task[tileCountX * tileCountY];
    27.         TileData[] tileArrayData = new TileData[tileCountX * tileCountY];
    28.         GameObject[] terrainTiles = new GameObject[tileCountX * tileCountY];
    29.  
    30.         for (int i = 0,y = 0; y < tileCountY; y++)
    31.         {
    32.             for (int x = 0; x < tileCountX; x++)
    33.             {
    34.                 // Tile mesh size, int index, int X start position, int Z start position
    35.                 tileArrayData[i] = new TileData(tileMeshSize, i, (tileMeshSize * x), (tileMeshSize * y));
    36.                 i++;
    37.             }
    38.         }
    39.         for (int i = 0; i < (tileCountX * tileCountY); i++)
    40.         {
    41.             var x = i;
    42.             tasks[x] = Task.Run(() => CreatingTile(tileArrayData[x]));
    43.         }
    44.  
    45.     }
    46.  
    47.     void Update()
    48.     {
    49.         if (tileCQ.Count == (tileCountX * tileCountY))
    50.         {
    51.             CreatingMeshFromTileData();
    52.             enabled = false;
    53.         }
    54.    
    55.     }
    56.  
    57.  
    58.     //Creates multiple meshes
    59.     void CreatingMeshFromTileData()
    60.     {
    61.         TileData[] sortingArray = new TileData[tileCountX * tileCountY];
    62.         TileData[] tileArrayDataReady = new TileData[tileCountX * tileCountY];
    63.         GameObject[] terrainCollection = new GameObject[tileCountX * tileCountY];
    64.         MeshRenderer[] meshRenderers = new MeshRenderer[tileCountX * tileCountY];
    65.         MeshFilter[] meshFilters = new MeshFilter[tileCountX * tileCountY];
    66.         Vector3[] tilePos = new Vector3[tileCountX * tileCountY];
    67.         for (int i = 0; i < (tileCountX * tileCountY); i++)
    68.         {
    69.             tileCQ.TryDequeue(out tileArrayDataReady[i]);
    70.  
    71.         }
    72.  
    73.         Array.Sort(tileArrayDataReady,
    74.         delegate (TileData x, TileData y) { return x.tileIndex.CompareTo(y.tileIndex); });
    75.  
    76.  
    77.         for (int y = 0, i = 0; y < tileCountY; y++)
    78.         {
    79.             for (int x = 0; x < tileCountX; x++)
    80.             {
    81.  
    82.                 tilePos[i] = new Vector3(x * tileMeshSize, 0, y * tileMeshSize);
    83.                 terrainCollection[i] = new GameObject("Empty");
    84.                 meshRenderers[i] = terrainCollection[i].AddComponent<MeshRenderer>();
    85.                 meshFilters[i] = terrainCollection[i].AddComponent<MeshFilter>();
    86.                 terrainCollection[i].transform.position = tilePos[i];
    87.  
    88.                 meshFilters[i].mesh = tileArrayDataReady[i].CreateMesh();
    89.                 meshRenderers[i].sharedMaterial = new Material(Shader.Find("Standard"));
    90.                 i++;
    91.             }
    92.         }      
    93.  
    94.     }
    95.  
    96.     void CreatingTile(TileData tileData)
    97.     {
    98.        
    99.  
    100.         for (int i = 0, z = 0; z <= tileMeshSize; z++)
    101.         {
    102.             for (int x = 0; x <= tileMeshSize; x++)
    103.             {
    104.                 float y = Mathf.PerlinNoise((x + tileData.xStartPos) * .3f, (z + tileData.zStartPos) * .3f) * 2f;
    105.                 tileData.vertices[i] = new Vector3(x, y, z);
    106.                 i++;
    107.             }
    108.         }
    109.  
    110.  
    111.         int vert = 0;
    112.         int tris = 0;
    113.         for (int z = 0; z < tileMeshSize; z++)
    114.         {
    115.             for (int x = 0; x < tileMeshSize; x++)
    116.             {
    117.  
    118.  
    119.                 tileData.triangles[tris + 0] = vert + 0;
    120.                 tileData.triangles[tris + 1] = vert + tileMeshSize + 1;
    121.                 tileData.triangles[tris + 2] = vert + 1;
    122.                 tileData.triangles[tris + 3] = vert + 1;
    123.                 tileData.triangles[tris + 4] = vert + tileMeshSize + 1;
    124.                 tileData.triangles[tris + 5] = vert + tileMeshSize + 2;
    125.  
    126.                 vert++;
    127.                 tris = tris + 6;
    128.  
    129.             }
    130.             vert++;
    131.         }
    132.  
    133.  
    134.         for (int i = 0, z = 0; z <= tileMeshSize; z++)
    135.         {
    136.             for (int x = 0; x <= tileMeshSize; x++)
    137.             {
    138.                 tileData.uvs[i] = new Vector2((float)x / tileMeshSize, (float)z / tileMeshSize);
    139.                 i++;
    140.             }
    141.         }
    142.         tileCQ.Enqueue(tileData);
    143.     }
    144.  
    145.     public class TileData
    146.     {
    147.         public int tileIndex;
    148.         public Vector3[] vertices;
    149.         public int[] triangles;
    150.         public Vector2[] uvs;
    151.  
    152.         public int xStartPos;
    153.         public int zStartPos;
    154.  
    155.         public int xEndPos;
    156.         public int zEndPos;
    157.  
    158.         public TileData(int tileMeshSize, int index, int xSPos, int zSPos)
    159.         {
    160.  
    161.             xStartPos = xSPos;
    162.             zStartPos = zSPos;
    163.  
    164.             xEndPos = xSPos + tileMeshSize;
    165.             zEndPos = zSPos + tileMeshSize;
    166.  
    167.  
    168.             tileIndex = index;
    169.             vertices = new Vector3[(tileMeshSize + 1) * (tileMeshSize + 1)];
    170.             uvs = new Vector2[vertices.Length];
    171.             triangles = new int[tileMeshSize * tileMeshSize * 6];
    172.  
    173.         }
    174.  
    175.         public Mesh CreateMesh()
    176.         {
    177.             Mesh mesh = new Mesh();
    178.             mesh.vertices = vertices;
    179.             mesh.triangles = triangles;
    180.             mesh.uv = uvs;
    181.             mesh.RecalculateNormals();
    182.             return mesh;
    183.         }
    184.  
    185.  
    186.     }
    187.  
    188. }
    189.  
    190.