public static void GenerateTrees(TerrainContainerObject container, List <GameObject> m_treesPrefabs, float m_treeDensity, float TreeScaleFactor, float TreeRandomScaleFactor, float m_TreeDistance, float m_BillBoardStartDistance, Dictionary <string, OSMNode> nodes, Dictionary <string, OSMWay> ways, List <OSMMapMembers> relations)
        {
            TreeDistance           = m_TreeDistance;
            BillBoardStartDistance = m_BillBoardStartDistance;

            AddTreesToTerrains(container, m_treesPrefabs);

            treeNodes   = new List <OSMNode>();
            woodWays    = new List <OSMWay>();
            treeRowWays = new List <OSMWay>();

            foreach (KeyValuePair <string, OSMNode> pair in nodes)
            {
                OSMNode n = pair.Value;
                if (n.HasTag("natural", "tree"))
                {
                    treeNodes.Add(n);
                }
            }

            foreach (KeyValuePair <string, OSMWay> pair in ways)
            {
                OSMWay w = pair.Value;
                if (w.HasTag("natural", "wood") || w.HasTags("landuse", "forest", "park"))
                {
                    woodWays.Add(w);
                }
                else if (w.HasTag("natural", "tree_row"))
                {
                    treeRowWays.Add(w);
                }
            }


            totalTreeCount = treeNodes.Count + treeRowWays.Count + woodWays.Count;

            if (totalTreeCount == 0)
            {
                return;
            }

            alreadyCreated = new HashSet <string>();

            var treeDensity = m_treeDensity;

            var TLPMercator_X = container.TLPointMercator.x;
            var TLPMercator_Y = container.TLPointMercator.y;

            var DRPMercator_X = container.DRPointMercator.x;
            var DRPMercator_Y = container.DRPointMercator.y;


            for (int i = 0; i < treeNodes.Count; i++)
            {
                OSMNode node = treeNodes[i];

                if (alreadyCreated.Contains(node.id))
                {
                    continue;
                }

                alreadyCreated.Add(node.id);

                var NodeP_Merc = GeoRefConversion.LatLongToMercat(node.Longitude, node.Latitude);

                double Offest_x = (NodeP_Merc.x - TLPMercator_X) / (DRPMercator_X - TLPMercator_X);

                double Offest_y = 1 - (NodeP_Merc.y - TLPMercator_Y) / (DRPMercator_Y - TLPMercator_Y);

                Vector3 WSPos = new Vector3((float)(container.transform.position.x + container.size.x * Offest_x), 0, (float)(container.size.z + container.size.z * Offest_y));

                SetTreeToTerrain(TreeScaleFactor, TreeRandomScaleFactor, container, WSPos);
            }

            for (int index = 0; index < treeRowWays.Count; index++)
            {
                OSMWay way = treeRowWays[index];
                if (alreadyCreated.Contains(way.id))
                {
                    continue;
                }
                alreadyCreated.Add(way.id);
                List <Vector3> points = OSMWay.GetGlobalPointsFromWay(way, nodes);

                for (int i = 0; i < points.Count; i++)
                {
                    Vector3 WSPos = points[i];

                    var WSPos_Merc = GeoRefConversion.LatLongToMercat(WSPos.x, WSPos.z);

                    double Offest_x = (WSPos_Merc.x - TLPMercator_X) / (DRPMercator_X - TLPMercator_X);
                    double Offest_y = 1 - (WSPos_Merc.y - TLPMercator_Y) / (DRPMercator_Y - TLPMercator_Y);

                    WSPos.x = (float)(container.transform.position.x + container.size.x * Offest_x);
                    WSPos.z = (float)(container.transform.position.z + container.size.z * Offest_y);

                    points[i] = WSPos;
                }

                for (int i = 0; i < points.Count - 1; i++)
                {
                    int len = Mathf.RoundToInt((points[i] - points[i + 1]).magnitude / m_treeDensity);
                    if (len > 0)
                    {
                        for (int j = 0; j <= len; j++)
                        {
                            SetTreeToTerrain(TreeScaleFactor, TreeRandomScaleFactor, container, Vector3.Lerp(points[i], points[i + 1], j / (float)len));
                        }
                    }
                    else
                    {
                        SetTreeToTerrain(TreeScaleFactor, TreeRandomScaleFactor, container, points[i]);
                    }
                }
            }

            for (int index = 0; index < woodWays.Count; index++)
            {
                OSMWay way = woodWays[index];
                if (alreadyCreated.Contains(way.id))
                {
                    continue;
                }
                alreadyCreated.Add(way.id);
                List <Vector3> points = OSMWay.GetGlobalPointsFromWay(way, nodes);

                for (int i = 0; i < points.Count; i++)
                {
                    Vector3 p = points[i];

                    var sp = GeoRefConversion.LatLongToMercat(p.x, p.z);

                    double rx = (sp.x - TLPMercator_X) / (DRPMercator_X - TLPMercator_X);
                    double ry = 1 - (sp.y - TLPMercator_Y) / (DRPMercator_Y - TLPMercator_Y);

                    p.x = (float)(container.transform.position.x + container.size.x * rx);
                    p.z = (float)(container.transform.position.z + container.size.z * ry);

                    points[i] = p;
                }

                Rect rect = Extensions.GetRectFromPoints(points);
                int  lx   = Mathf.RoundToInt(rect.width / m_treeDensity);
                int  ly   = Mathf.RoundToInt(rect.height / m_treeDensity);

                if (lx > 0 && ly > 0)
                {
                    m_currentWayID = way.id;

                    GenerateTerrainsTrees(TreeScaleFactor, TreeRandomScaleFactor, container, treeDensity, lx, ly, rect, points);
                }
            }
        }
        public static void GenerateGrass(TerrainContainerObject m_container, List <GISTerrainLoaderSO_Grass> m_GrassPrefabs, float m_GrassDensity, float m_GrassScaleFactor, float m_DetailDistance, Dictionary <string, OSMNode> nodes, Dictionary <string, OSMWay> ways, List <OSMMapMembers> relations)
        {
            GrassPrefabs     = m_GrassPrefabs;
            container        = m_container;
            GrassScaleFactor = m_GrassScaleFactor;
            DetailDistance   = m_DetailDistance;

            AddDetailsLayersToTerrains();

            TerrainData tdata            = container.terrains[0, 0].terrainData;
            int         detailResolution = tdata.detailResolution;


            alreadyCreated = new List <string>();

            details = new List <int[, ]>(container.terrains.Length);

            foreach (var item in container.terrains)
            {
                for (int i = 0; i < GrassPrefabs.Count; i++)
                {
                    details.Add(new int[detailResolution, detailResolution]);
                }
            }

            var detailsInPoint = new float[GrassPrefabs.Count];

            var grassWays = new List <OSMWay>();

            foreach (KeyValuePair <string, OSMWay> pair in ways)
            {
                OSMWay w = pair.Value;
                if (w.HasTags("landuse", "grass", "farmland", "forest", "meadow", "park", "pasture", "recreation_ground") ||
                    w.HasTags("leisure", "park", "golf_course") || w.HasTags("natural", "scrub", "wood"))
                {
                    grassWays.Add(w);
                }
            }

            var totalCount = grassWays.Count + container.terrainCount.x;

            float density = m_GrassDensity / 100f;

            if (density > 1)
            {
                density = 1;
            }

            density *= 64;

            for (int i = 0; i < grassWays.Count; i++)
            {
                OSMWay way = grassWays[i];

                if (alreadyCreated.Contains(way.id))
                {
                    continue;
                }
                alreadyCreated.Add(way.id);

                if (way.nodeRefs.Count == 0)
                {
                    continue;
                }

                List <Vector3> Points = new List <Vector3>();

                float pxmin = float.MaxValue, pxmax = float.MinValue, pymin = float.MaxValue, pymax = float.MinValue;

                for (int m = 0; m < way.nodeRefs.Count; m++)
                {
                    string nodeRef = way.nodeRefs[m];

                    OSMNode node;
                    if (!nodes.TryGetValue(nodeRef, out node))
                    {
                        continue;
                    }


                    var NodeP_Merc = GeoRefConversion.LatLongToMercat(node.Longitude, node.Latitude);

                    Vector3 wspostion = GeoRefConversion.MercatCoordsToWorld(NodeP_Merc.x, 0, NodeP_Merc.y, container) - container.transform.position;

                    wspostion = new Vector3(wspostion.x / tdata.size.x * detailResolution, 0, wspostion.z / tdata.size.z * detailResolution);

                    if (wspostion.x < pxmin)
                    {
                        pxmin = wspostion.x;
                    }
                    if (wspostion.x > pxmax)
                    {
                        pxmax = wspostion.x;
                    }
                    if (wspostion.z < pymin)
                    {
                        pymin = wspostion.z;
                    }
                    if (wspostion.z > pymax)
                    {
                        pymax = wspostion.z;
                    }

                    Points.Add(wspostion);
                }

                if (Points.Count < 3)
                {
                    continue;
                }

                Vector3[] points = Points.ToArray();
                for (int x = (int)pxmin; x < pxmax; x++)
                {
                    int tix = Mathf.FloorToInt(x / (float)detailResolution);
                    if (tix < 0 || tix >= container.terrainCount.x)
                    {
                        continue;
                    }

                    int tx = x - tix * detailResolution;

                    for (int y = (int)pymin; y < pymax; y++)
                    {
                        int tiy = Mathf.FloorToInt(y / (float)detailResolution);
                        if (tiy >= container.terrainCount.y || tiy < 0)
                        {
                            continue;
                        }

                        int tIndex = tiy * container.terrainCount.x + tix;
                        if (tIndex < 0 || tIndex >= container.terrains.Length)
                        {
                            continue;
                        }

                        bool intersect = Extensions.IsPointInPolygon(points, x + 0.5f, y - 0.5f);
                        if (!intersect)
                        {
                            continue;
                        }

                        int ty = y - tiy * detailResolution;

                        if (GrassPrefabs.Count == 1)
                        {
                            details[tIndex][ty, tx] = (int)density;
                        }
                        else
                        {
                            float totalInPoint = 0;

                            int tIndex2 = tIndex * GrassPrefabs.Count;

                            for (int k = 0; k < GrassPrefabs.Count; k++)
                            {
                                float v = Random.Range(0f, 1f);
                                detailsInPoint[k] = v;
                                totalInPoint     += v;
                            }

                            for (int k = 0; k < GrassPrefabs.Count; k++)
                            {
                                int v = (int)(detailsInPoint[k] / totalInPoint * density);
                                if (v > 255)
                                {
                                    v = 255;
                                }
                                details[tIndex2 + k][ty, tx] = v;
                            }
                        }
                    }
                }
            }


            for (int x = 0; x < container.terrainCount.x; x++)
            {
                for (int y = 0; y < container.terrainCount.y; y++)
                {
                    for (int prefabIndex = 0; prefabIndex < GrassPrefabs.Count; prefabIndex++)
                    {
                        int tIndex = y * container.terrainCount.x + x;

                        container.terrains[x, y].terrainData.SetDetailLayer(0, 0, prefabIndex,
                                                                            details[tIndex * GrassPrefabs.Count + prefabIndex]);
                    }
                }
            }
        }