diff --git a/Runtime/Prefabs/TreeStreamer.prefab b/Runtime/Prefabs/TreeStreamer.prefab
index 00d59a14f649e04d338d116e6a98c6b9d61453bb..9d405552e6a9c22a570272cc3a2588b2ad71a658 100644
--- a/Runtime/Prefabs/TreeStreamer.prefab
+++ b/Runtime/Prefabs/TreeStreamer.prefab
@@ -9,7 +9,7 @@ GameObject:
   serializedVersion: 6
   m_Component:
   - component: {fileID: 8102445642857089983}
-  - component: {fileID: 5786002106602377772}
+  - component: {fileID: 591969842533917131}
   m_Layer: 0
   m_Name: TreeStreamer
   m_TagString: Untagged
@@ -32,7 +32,7 @@ Transform:
   m_Children: []
   m_Father: {fileID: 0}
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!114 &5786002106602377772
+--- !u!114 &591969842533917131
 MonoBehaviour:
   m_ObjectHideFlags: 0
   m_CorrespondingSourceObject: {fileID: 0}
@@ -41,21 +41,23 @@ MonoBehaviour:
   m_GameObject: {fileID: 742251135161503034}
   m_Enabled: 1
   m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: acbdecd6bc179f647b5703a55ffdafe7, type: 3}
+  m_Script: {fileID: 11500000, guid: 4643b554a54fe66479884b76c4ca9d39, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
+  activeTreeCount: 0
   originRDX: 220929.6988732884
   originRDY: 450504.48700884776
   tileSize: 860.16
   treeRoot: {fileID: 0}
   areaName: DeMarke
   targetCam: {fileID: 0}
+  numRings: 5
   wgs84Origin: {x: 52.03894, y: 6.348124}
-  numRings: 4
-  requiredTime: 3
-  removeDelay: 7
-  maxTrees: 10000
-  treePrefab: {fileID: 9046900170786976767, guid: 65926329d063fb84ea4ac2e63a6fcaa4,
-    type: 3}
-  treenGenerator: {fileID: -3667069338063608550, guid: ac835d606e3e19a48bdf1d863acfcfd0,
-    type: 3}
+  requiredTime: 1
+  removeDelay: 1
+  enableDisableKey: 49
+  types:
+  - type: {fileID: 5556296651694026700, guid: 19df7a28074cb284a8aa8562718310f5, type: 3}
+    maxTrees: 20000
+  - type: {fileID: 9046900170786976767, guid: 65926329d063fb84ea4ac2e63a6fcaa4, type: 3}
+    maxTrees: 20000
diff --git a/Runtime/Scripts/TreeStreamer2.cs b/Runtime/Scripts/TreeStreamer2.cs
index 13ff0693d9057cbd43ca7a9518f1ed98b1ad7440..31f3d4acafe424e9f70aae6518b6f4dee40a4370 100644
--- a/Runtime/Scripts/TreeStreamer2.cs
+++ b/Runtime/Scripts/TreeStreamer2.cs
@@ -1,9 +1,12 @@
+using Codice.CM.Client.Differences;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Threading;
 using System.Threading.Tasks;
 using Unity.Collections;
 using UnityEditor;
+using UnityEditor.Graphs;
 using UnityEngine;
 using UnityEngine.AddressableAssets;
 
@@ -17,20 +20,21 @@ namespace Wander
         {
             public float originTime;
             public float lastTime;
-            public bool spawned;
             public List<Vector3> treeDb = new();
-            public int spawnIndex;
             public Terrain terrain;
             public bool triedToObtainTerrain;
-            public List<int> spawnedTrees = new();
+            public int lod;
+            public Vector3 worldPos;
+            public Dictionary<int, (int offset, int num)> offsetAndNumPerType;
         }
 
+        [Serializable]
         public class TreeType
         {
             public GameObject type;
             public int maxTrees;
-            internal List<int> freeSlots;
-            internal NativeArray<Matrix4x4> positions;
+            internal int [] currentCount;
+            internal NativeArray<Matrix4x4> [] positions;
         }
 
         [ReadOnly] public int activeTreeCount;
@@ -41,28 +45,28 @@ namespace Wander
         [ReadOnly] public string areaName = "Steenbergen";
 
         public Camera targetCam;
-        public bool asTreeInstances = true;
         public int numRings = 5;
         public Vector2 wgs84Origin;
         public float requiredTime = 2;
         public float removeDelay = 1;
+        public KeyCode enableDisableKey = KeyCode.Alpha1;
         public List<TreeType> types = new();
 
         private bool allowSpawning = true;
         private Dictionary<Vector2Int, TreeTile> treeDatabase = new();
         private Dictionary<Vector2Int, TreeTile> activeTiles = new();
-        private List<int> freeTreeSlots = new();
         private Task updateMatricesTask;
         private bool isDone;
 
         // Variables copied from mainthread so can be accessed in other thread.
-        Vector3 cameraPosition;
-        float unityTime;
+        private Vector3 cameraPosition;
+        private float unityTime;
+        private System.Random random;
 
         public void SetGPSCoord( Dictionary<string, string> boundaryData )
         {
-            var wgs84Lat  = double.Parse( boundaryData["gpsx"] );
-            var wgs84Lon  = double.Parse( boundaryData["gpsy"] );
+            var wgs84Lat   = double.Parse( boundaryData["gpsx"] );
+            var wgs84Lon   = double.Parse( boundaryData["gpsy"] );
             var boundsSize = float.Parse( boundaryData["boundsSize"] );
             wgs84Origin = new Vector2( (float)wgs84Lat, (float)wgs84Lon );
             tileSize    = float.Parse( boundaryData["tileSize"] );
@@ -84,6 +88,18 @@ namespace Wander
                 await updateMatricesTask;
                 updateMatricesTask = null;
             }
+            foreach (var type in types)
+            {
+                if (type.positions == null)
+                    continue;
+                for (int i = 0;i < 4;i++)
+                {
+                    if (type.positions[i] != null && type.positions[i].IsCreated)
+                    {
+                        type.positions[i].Dispose();
+                    }
+                }
+            }
         }
 
         private void Start()
@@ -91,6 +107,8 @@ namespace Wander
             if (!Application.isPlaying)
                 return;
 
+            random = new System.Random();
+
             // In case no camera can be found, we have at least a valid position in other thread.
             cameraPosition = transform.position;
             
@@ -98,7 +116,23 @@ namespace Wander
             if ( targetCam == null )
             {
                 targetCam = Camera.main;
-            }
+            }
+
+            // Initially each slot in the matrix arrea is free.
+            foreach (var type in types)
+            {
+                Debug.Assert( type.type != null, "No type specified." );
+                type.currentCount = new int[4];
+                type.positions    = new NativeArray<Matrix4x4>[4];
+                for (int i = 0;i<4;i++)
+                {
+                    type.positions[i] = new NativeArray<Matrix4x4>( type.maxTrees, Allocator.Persistent );
+                    for (int j = 0;j<type.positions[i].Length;j++)
+                    {
+                        type.positions[i][j] = Matrix4x4.TRS( Vector3.zero, Quaternion.identity, Vector3.zero );
+                    }
+                }
+            }
 
             // Load tree database.
             Addressables.LoadAssetAsync<TextAsset>( "Assets/" + areaName + "/trees.csv" ).Completed += (operation) =>
@@ -110,17 +144,21 @@ namespace Wander
                 }
 
                 // Cannot acccess .text in other thread, so obtain ptr first.
-                var text = operation.Result.text;
-                Task.Run( () =>
+                var text = operation.Result.text;
+                updateMatricesTask = Task.Run( () =>
                 {
-                    // Read tree database.
-                    ReadTreeDb( text );
-
-                    // Initially all matrix slots are free.
-                    InitializeFreeMatriceSlots();
+                    try
+                    {
+                        // Read tree database.
+                        ReadTreeDb( text );
 
-                    // Update matrices in the background.
-                    UpdateLoopOtherThread();
+                        // Update matrices in the background.
+                        UpdateLoopOtherThread();
+                    } 
+                    catch (Exception e)
+                    {
+                        Debug.LogException( e );
+                    }
                 } );
             };
             
@@ -131,7 +169,7 @@ namespace Wander
             if (!Application.isPlaying)
                 return;
             
-            if (Input.GetKeyDown( KeyCode.Alpha1 ))
+            if (Input.GetKeyDown( enableDisableKey ))
             {
                 allowSpawning = !allowSpawning;
             }
@@ -141,6 +179,40 @@ namespace Wander
                 unityTime = Time.time;
                 cameraPosition = targetCam.transform.position;
             }
+
+            RenderTrees();
+        }
+
+        void RenderTrees()
+        {
+            for (int i = 0;i < types.Count;i++)
+            {
+                for (int lod = 0;lod < 4;lod++)
+                {
+                    if (types[i].type.transform.childCount == lod)
+                        break;
+
+                    var tr = types[i].type.transform.GetChild(lod); // LOD0;
+                    var mesh = tr.GetComponent<MeshFilter>().sharedMesh;
+                    var materials = tr.GetComponent<MeshRenderer>().sharedMaterials;
+
+                    int totalNum = types[i].currentCount[lod];
+                    int offset = 0;
+                    while (totalNum > 0)
+                    {
+                        int numToRender = Math.Min( 1023, totalNum );
+
+                        for (int j = 0;j<mesh.subMeshCount;j++)
+                        {
+                            RenderParams rp = new RenderParams(materials[j]);
+                            Graphics.RenderMeshInstanced( rp, mesh, j, types[i].positions[lod], numToRender, offset );
+                        }
+
+                        totalNum -= numToRender;
+                        offset += numToRender;
+                    }
+                }
+            }
         }
 
         void ReadTreeDb(string text)
@@ -169,36 +241,38 @@ namespace Wander
                     Debug.LogError( "On Line " + line );
                 }
             }
-        }
-
-        void InitializeFreeMatriceSlots()
+        }
+
+        private void UpdateLoopOtherThread()
         {
-            // Initially each slot in the matrix arrea is free.
-            foreach (var type in types)
+            while (!isDone)
             {
-                type.freeSlots = new();
-                for (int i = 0;i < type.maxTrees;i++)
+                LoadDesiredTiles();
+                SpawnTrees();
+                DespawnTrees();
+
+                int newActiveCount = 0;
+                types.ForEach( t =>
                 {
-                    freeTreeSlots.Add( i );
-                }
+                    for (int i = 0;i <4;i++)
+                    {
+                        newActiveCount += t.currentCount[i];
+                    }
+                });
+                activeTreeCount = newActiveCount;
+
+                Thread.Sleep( 100 );
             }
         }
 
-        private void UpdateLoopOtherThread()
+        int CalcLod( Vector3 pos )
         {
-            LoadDesiredTiles();
-            SpawnTrees();
-            DespawnTrees();
-
-            activeTreeCount = 0;
-            types.ForEach( t => {
-                activeTreeCount += t.maxTrees - t.freeSlots.Count;
-            } );
+            float dist = Vector3.Distance( cameraPosition, pos );
+            int lod    = Mathf.RoundToInt( dist / 400 );
+            return lod;
         }
 
-
-
-        void LoadIfIsNewTile( Vector2Int centre, int dx, int dy, float time)
+        void LoadIfIsNewTile( Vector2Int centre, int dx, int dy )
         {
             var coord = centre + new Vector2Int(dx, dy);
             if (!activeTiles.TryGetValue( coord, out var activeTile ))
@@ -207,15 +281,22 @@ namespace Wander
                 {
                     activeTiles.Add( coord, dbTile );
                     activeTile = dbTile;
-                    activeTile.spawnedTrees.Clear();
-                    activeTile.spawnIndex = 0;
-                    activeTile.spawned = true;
-                    activeTile.originTime = time;
+                    activeTile.originTime = unityTime;
+                    activeTile.worldPos = new Vector3( dx, 0, dy )*tileSize + new Vector3( tileSize, 0, tileSize ) *0.5f;
+                    activeTile.lod = -1;
+                    if (activeTile.offsetAndNumPerType == null)
+                    {
+                        activeTile.offsetAndNumPerType = new Dictionary<int, (int offset, int num)>();
+                    }
+                    else
+                    { 
+                        activeTile.offsetAndNumPerType.Clear();
+                    }
                 }
             }
             if (activeTile != null)
             {
-                activeTile.lastTime = time;
+                activeTile.lastTime = unityTime;
             }
         }
 
@@ -257,73 +338,112 @@ namespace Wander
         {
             float tpx = cameraPosition.x;
             float tpy = cameraPosition.z;
-            float time = unityTime;
 
             var rdX = Mathf.FloorToInt( (float)(( originRDX + tpx ) * 0.01f ));
             var rdY = Mathf.FloorToInt( (float)(( originRDY + tpy ) * 0.01f ));
             var centreTile = new Vector2Int(rdX, rdY);
 
-            LoadIfIsNewTile( centreTile, 0, 0, time );
+            LoadIfIsNewTile( centreTile, 0, 0 );
             for (int c = 1;c <= numRings;c++)
             {
                 for (int y2 = -c;y2 <= c;y2++)
                 {
-                    LoadIfIsNewTile( centreTile, -c, y2, time );
+                    LoadIfIsNewTile( centreTile, -c, y2 );
                 }
                 for (int y2 = -c;y2 <= c;y2++)
                 {
-                    LoadIfIsNewTile( centreTile, c, y2, time );
+                    LoadIfIsNewTile( centreTile, c, y2 );
                 }
                 for (int x2 = -c+1;x2 <= c-1 ;x2++)
                 {
-                    LoadIfIsNewTile( centreTile, x2, c, time );
+                    LoadIfIsNewTile( centreTile, x2, c );
                 }
                 for (int x2 = -c+1;x2 <= c-1; x2++)
                 {
-                    LoadIfIsNewTile( centreTile, x2, -c, time );
+                    LoadIfIsNewTile( centreTile, x2, -c );
                 }
             }
         }
 
+        void RemoveTileFromTypeArrays( TreeTile tile, int oldLod )
+        {
+            if (oldLod == -1)
+                return;
+
+            foreach (var spawnees in tile.offsetAndNumPerType)
+            {
+                var type      = spawnees.Key;
+                var offset    = spawnees.Value.offset;
+                var numSpawn  = spawnees.Value.num;
+                var numToMove = types[type].currentCount[oldLod] - (offset+numSpawn);
+                for (int i = 0;i < numToMove;i++)
+                {
+                    types[type].positions[oldLod][offset+i] =
+                        types[type].positions[oldLod][offset+i+numSpawn];
+                }
+                types[type].currentCount[oldLod] -= numSpawn;
+            }
+
+            tile.offsetAndNumPerType.Clear();
+        }
+
         void SpawnTrees()
         {
             foreach (var kvp in activeTiles)
             {
                 var tile = kvp.Value;
-                if (Time.time - tile.originTime < requiredTime) 
+
+                // Only start spawning trees from tile, if certain time tile is requested.
+                if (unityTime - tile.originTime < requiredTime) 
                 {
                     continue;
                 }
-                while (tile.spawnIndex < tile.treeDb.Count)
-                {
-                    if (freeTreeSlots.Count == 0)
-                        break;
-
-                    var treeDb = tile.treeDb[tile.spawnIndex];
-                    tile.spawnIndex++;
 
-                    var treeType = Mathf.RoundToInt( treeDb.z );
-                    if (!(treeType >= 0 && treeType < treenGenerator.types.Count))
-                        continue;
+                // Only change trees, when lod has changed of tile.
+                int newLod = Mathf.Clamp( CalcLod( tile.worldPos ), 0, 3 );
+                if (newLod == tile.lod)
+                {
+                    continue;
+                }
+                int oldLod = tile.lod;
+                tile.lod = newLod;
+
+                RemoveTileFromTypeArrays( tile, oldLod );
 
-                    var treePrefab = treenGenerator.types[treeType].trees.Random();
-                    if (treePrefab == null)
-                        continue;
+                // For each tree in tile, see if the type of tree can be spawned as there are max tree counts per type.
+                for (int i = 0; i< tile.treeDb.Count; i++)
+                {
+                    var treeDb = tile.treeDb[i];
+                    var type   = Mathf.RoundToInt( treeDb.z );
 
-                    var treePos = new Vector3( (float)(treeDb.x-originRDX), 0, (float)(treeDb.y-originRDY) );
+                    // If type is invalid, continue to next. Don't spawn this one.
+                    if (type < 0 || type >= types.Count)
+                    {
+                        continue;
+                    }
 
-                    
-                    if ( HeightFromTerrains( tile, treePos, out float height ))
-                    {
-                        var slot = freeTreeSlots.Last();
-                        freeTreeSlots.RemoveAt( freeTreeSlots.Count-1 );
-                        tile.spawnedTrees.Add( slot );
-                        var tree = treeRoot.transform.GetChild( slot );
-                        tree.transform.position = treePos + new Vector3( 0, height-100, 0 );
-                        var lg = tree.gameObject.GetComponent<LODGroup>();
-                        lg.ForceLOD( -1 );
+                    // See if free slot available.
+                    if (types[type].currentCount[newLod] < types[type].positions[newLod].Length)
+                    {
+                        // Find out offset on type per lod.
+                        int offset = types[type].currentCount[newLod]++;
 
-                    }
+                        // Obtain tree position.
+                        var treePos = new Vector3( (float)(treeDb.x-originRDX), 20, (float)(treeDb.y-originRDY) );
+
+                        // Set position on type per lod, using offset.
+                        types[type].positions[newLod][offset] = Matrix4x4.TRS( treePos, Quaternion.Euler( 0, random.Next()%360, 0 ), Vector3.one );
+
+                        // Remember the offset and num spawned per type. Because need to move per type num away, to avoid rendering instances that are not visible (vertex processor is invoked).
+                        if (!tile.offsetAndNumPerType.TryGetValue( type, out var offsetAndType ))
+                        {
+                            tile.offsetAndNumPerType.Add( type, (offset, 1) );
+                        }
+                        else
+                        {
+                            offsetAndType.num++;
+                        }                        
+                    }
                 }
             }
         }
@@ -334,25 +454,23 @@ namespace Wander
             foreach (var kvp in activeTiles)
             {
                 var tile = kvp.Value;
-                if (tile.lastTime - Time.time < -removeDelay)
+
+                // Only despawn tile if certain time not desired anymore. This to avoid flipping in and out.
+                if (tile.lastTime - unityTime < -removeDelay)
                 {
-                    if ( tile.spawnedTrees.Count != 0 )
-                    {
-                        for( int i = 0; i< tile.spawnedTrees.Count; i++ )
-                        {
-                            treeRoot.transform.GetChild( tile.spawnedTrees[i] ).gameObject.GetComponent<LODGroup>().ForceLOD( 4 );
-                        }
-                        freeTreeSlots.AddRange( tile.spawnedTrees );
-                        tile.spawnedTrees.Clear();
+                    // Remove trees for type arrays.
+                    RemoveTileFromTypeArrays( tile, tile.lod );
 
-                        if (removeList==null)
-                            removeList = new();
-                        removeList.Add( kvp.Key );
-                        kvp.Value.spawned = false;
-                    }
+                    // Initialize remove list if was not used yet.
+                    if (removeList==null)
+                        removeList = new();
+
+                    // Add this tile to remove list.
+                    removeList.Add( kvp.Key );
                 }
             }
 
+            // Remove actual inactive tiles.
             removeList?.ForEach( rm =>
             {
                 activeTiles.Remove( rm );