예제 #1
0
    private bool Vector3RDIsInside(Vector3RD vector)
    {
        double vector1Pos = 0;

        if (vector.x > extentRD.x)
        {
            vector1Pos += 1;
        }
        ;                                                 //positive if vector1.x > extent minX
        if (vector.y > extentRD.y)
        {
            vector1Pos += 1;
        }
        ;                                                //positive if vector1.y > extent minY
        if (extentRD.z > vector.x)
        {
            vector1Pos += 1;
        }
        ;                                               // postive if vector1.x < extent maxX
        if (extentRD.w > vector.y)
        {
            vector1Pos += 1;
        }
        ;                                                // postive if vector1.y < extent maxY

        if (vector1Pos < 4)
        {
            return(false);
        }
        else
        {
            return(true);
        }
    }
    public List <GameObject> GetTilesInLayer(Layer layer, Vector3RD bottomLeftRD, Vector3RD topRightRD)
    {
        if (layer == null)
        {
            return(new List <GameObject>());
        }
        List <GameObject> output   = new List <GameObject>();
        double            tilesize = layer.tileSize;

        Debug.Log(tilesize);
        int tileX;
        int tileY;

        foreach (var tile in layer.tiles)
        {
            tileX = tile.Key.x;
            tileY = tile.Key.y;

            if (tileX + tilesize < bottomLeftRD.x || tileX > topRightRD.x)
            {
                continue;
            }
            if (tileY + tilesize < bottomLeftRD.y || tileY > topRightRD.y)
            {
                continue;
            }
            MeshFilter[] meshFilters = tile.Value.gameObject.GetComponentsInChildren <MeshFilter>();
            foreach (var meshFilter in meshFilters)
            {
                output.Add(meshFilter.gameObject);
            }
        }
        return(output);
    }
예제 #3
0
        public void Generate(TileChange tileChange, Tile tile, System.Action <TileChange> callback = null)
        {
            napOffset = Config.activeConfiguration.zeroGroundLevelY;
            Vector3RD boundingBoxMinimum = new Vector3RD(tileChange.X, tileChange.Y, napOffset);
            Vector3RD boundingBoxMaximum = new Vector3RD(tileChange.X + tileSize, tileChange.Y + tileSize, napOffset);;

            StartCoroutine(GetSewerLinesInBoundingBox(tileChange, tile, boundingBoxMinimum, boundingBoxMaximum, callback));
        }
예제 #4
0
        /// <summary>
        /// Starts genering the sewage network based on the geometry data and points in a WFS service
        /// </summary>
        /// <param name="boxMinimum">The RD coordinates min point of a bounding box area</param>
        /// <param name="boxMaximum">The RD coordinates maximum point of a bounding box area</param>
        public void Generate(Vector3RD boxMinimum = default, Vector3RD boxMaximum = default)
        {
            napOffset          = Config.activeConfiguration.zeroGroundLevelY;
            boundingBoxMinimum = boxMinimum;
            boundingBoxMaximum = boxMaximum;

            StartCoroutine(GetSewerLinesInBoundingBox());
        }
예제 #5
0
    private Vector3RD RestoreRDVertex(Vector3 vector, Vector3RD temporaryOrigin)
    {
        Vector3RD output = new Vector3RD();

        output.x = vector.x + temporaryOrigin.x;
        output.y = vector.z + temporaryOrigin.y;
        output.z = vector.y + temporaryOrigin.z;
        return(output);
    }
예제 #6
0
    private void CreateOverlapTriangles(Vector3RD[] verticesRD, int[] originalTriangles, Vector3[] verticesUnity, Vector3 unityOrigin, Vector3RD coordinateOffset)
    {
        List <Vector3> clippingPolygon = new List <Vector3>();
        Vector3        vector          = new Vector3();

        vector.x = (float)(extentRD.x - coordinateOffset.x);
        vector.z = (float)(extentRD.y - coordinateOffset.y);
        clippingPolygon.Add(vector);
        vector   = new Vector3();
        vector.x = (float)(extentRD.z - coordinateOffset.x);
        vector.z = (float)(extentRD.y - coordinateOffset.y);
        clippingPolygon.Add(vector);
        vector   = new Vector3();
        vector.x = (float)(extentRD.z - coordinateOffset.x);
        vector.z = (float)(extentRD.w - coordinateOffset.y);
        clippingPolygon.Add(vector);
        vector   = new Vector3();
        vector.x = (float)(extentRD.x - coordinateOffset.x);
        vector.z = (float)(extentRD.w - coordinateOffset.y);
        clippingPolygon.Add(vector);


        int originalTriangleCount = originalTriangles.Length - 2;

        for (int i = 0; i < originalTriangleCount; i += 3)
        {
            Vector3RD vector1 = verticesRD[originalTriangles[i]];
            Vector3RD vector2 = verticesRD[originalTriangles[i + 1]];
            Vector3RD vector3 = verticesRD[originalTriangles[i + 2]];
            if (Vector3RDIsInside(vector1) && Vector3RDIsInside(vector2) && Vector3RDIsInside(vector3))
            {
            }
            else
            {
                List <Vector3> vectors = new List <Vector3>();
                // to use overlap.cs y en z must be flipped and winding-order must be counter-clockwise
                // to prevent inaccuracy due to large number we offset the coordinates so the first vertex is at 0,0,0
                vector   = new Vector3();
                vector.x = (float)(verticesRD[originalTriangles[i + 2]].x - coordinateOffset.x);
                vector.y = (float)(verticesRD[originalTriangles[i + 2]].z - coordinateOffset.z);
                vector.z = (float)(verticesRD[originalTriangles[i + 2]].y - coordinateOffset.y);
                vectors.Add(vector);
                vector   = new Vector3();
                vector.x = (float)(verticesRD[originalTriangles[i + 1]].x - coordinateOffset.x);
                vector.y = (float)(verticesRD[originalTriangles[i + 1]].z - coordinateOffset.z);
                vector.z = (float)(verticesRD[originalTriangles[i + 1]].y - coordinateOffset.y);
                vectors.Add(vector);
                vector   = new Vector3();
                vector.x = (float)(verticesRD[originalTriangles[i]].x - coordinateOffset.x);
                vector.y = (float)(verticesRD[originalTriangles[i]].z - coordinateOffset.z);
                vector.z = (float)(verticesRD[originalTriangles[i]].y - coordinateOffset.y);
                vectors.Add(vector);

                CreateClippedMesh(vectors, clippingPolygon, coordinateOffset, unityOrigin);
            }
        }
    }
예제 #7
0
    private Vector3 OffsetRDVertex(Vector3RD vector, Vector3RD temporaryOrigin)
    {
        Vector3 output = new Vector3();

        output.x = (float)(vector.x - temporaryOrigin.x);
        output.y = (float)(vector.z);
        output.z = (float)(vector.y - temporaryOrigin.y);
        return(output);
    }
예제 #8
0
    public void ClipSubMesh(RDBoundingBox boundingBox, int subMeshNumber)
    {
        clippedVerticesRD = new List <Vector3RD>();
        if (subMeshNumber >= mesh.subMeshCount)
        {
            return;
        }

        int[] indices = mesh.GetIndices(subMeshNumber);
        temporaryOrigin = new Vector3RD(boundingBox.minX, boundingBox.minY, 0);
        Vector3        point1;
        Vector3        point2;
        Vector3        point3;
        List <Vector3> clippingPolygon    = CreateClippingPolygon(boundingBox);
        List <Vector3> clippingVectorList = new List <Vector3>();

        clippingVectorList.Capacity = 3;
        for (int i = 0; i < indices.Length; i += 3)
        {
            point1 = OffsetRDVertex(rdVertices[indices[i]], temporaryOrigin);
            point2 = OffsetRDVertex(rdVertices[indices[i + 1]], temporaryOrigin);
            point3 = OffsetRDVertex(rdVertices[indices[i + 2]], temporaryOrigin);
            trianglePosition position = GetTrianglePosition(point1, point2, point3, boundingBox);
            if (position == trianglePosition.inside)
            {
                clippedVerticesRD.Add(RestoreRDVertex(point1, temporaryOrigin));
                clippedVerticesRD.Add(RestoreRDVertex(point2, temporaryOrigin));
                clippedVerticesRD.Add(RestoreRDVertex(point3, temporaryOrigin));
            }
            else if (position == trianglePosition.overlap)
            {
                clippingVectorList.Clear();
                clippingVectorList.Add(point1);
                clippingVectorList.Add(point2);
                clippingVectorList.Add(point3);

                List <Vector3> clippedTriangle = Netherlands3D.Utilities.TriangleClipping.SutherlandHodgman.ClipPolygon(clippingVectorList, clippingPolygon);

                int vertexcount = clippedTriangle.Count;
                if (vertexcount < 3)
                {
                    continue;
                }
                clippedVerticesRD.Add(RestoreRDVertex(clippedTriangle[0], temporaryOrigin));
                clippedVerticesRD.Add(RestoreRDVertex(clippedTriangle[1], temporaryOrigin));
                clippedVerticesRD.Add(RestoreRDVertex(clippedTriangle[2], temporaryOrigin));
                // add extra vectors. vector makes a triangle with the first and the previous vector.
                for (int j = 3; j < vertexcount; j++)
                {
                    clippedVerticesRD.Add(RestoreRDVertex(clippedTriangle[0], temporaryOrigin));
                    clippedVerticesRD.Add(RestoreRDVertex(clippedTriangle[j - 1], temporaryOrigin));
                    clippedVerticesRD.Add(RestoreRDVertex(clippedTriangle[j], temporaryOrigin));
                }
            }
        }
    }
        public GameObject CreateMesh(CityModel cityModel, Vector3RD origin)
        {
            GameObject container = new GameObject();

            //Terraindata terrainData = container.AddComponent<Terraindata>();

            verts = GetVerts(cityModel, origin);
            List <Vector3> newVerts = new List <Vector3>();

            triangleLists = GetTriangleLists(cityModel);

            Vector2Int textureSize = ObjectIDMapping.GetTextureSize(triangleLists.Count);

            Debug.Log(textureSize);

            Mesh mesh = new Mesh();

            List <int>     triangles      = new List <int>();
            List <string>  objectIDs      = new List <string>();
            List <Vector2> uvs            = new List <Vector2>();
            List <int>     vectorIDs      = new List <int>();
            List <int>     triangleCount  = new List <int>();
            int            objectIDNumber = 0;
            int            vertexCounter  = 0;

            foreach (var item in triangleLists)
            {
                vertexCounter = 0;
                Vector2 uv = ObjectIDMapping.GetUV(objectIDNumber, textureSize);
                foreach (int vertexIndex in item.Value)
                {
                    newVerts.Add(verts[vertexIndex]);
                    uvs.Add(uv);
                    vectorIDs.Add(objectIDNumber);
                    triangles.Add(newVerts.Count - 1);
                    vertexCounter++;
                }
                triangleCount.Add(vertexCounter);
                objectIDs.Add(item.Key);
                objectIDNumber++;
            }

            mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            mesh.vertices    = newVerts.ToArray();
            mesh.triangles   = triangles.ToArray();
            mesh.uv          = uvs.ToArray();
            //mesh.RecalculateNormals();
            //mesh.Optimize();
            container.AddComponent <MeshFilter>().mesh = mesh;
            ObjectMapping objectMapping = container.AddComponent <ObjectMapping>();

            objectMapping.vectorIDs     = vectorIDs;
            objectMapping.BagID         = objectIDs;
            objectMapping.TriangleCount = triangleCount;
            return(container);
        }
        /// <summary>
        /// Load all the large ground tiles from AssetBundles, spawn it in our world, and start filling it with the trees that match the tile
        /// its RD coordinate rectangle. The tiles are named after the RD coordinates in origin at the bottomleft of the tile.
        /// </summary>
        private IEnumerator TraverseTileFiles()
        {
            var info     = new DirectoryInfo(sourceGroundTilesFolder);
            var fileInfo = info.GetFiles();

            var currentFile = 0;

            while (currentFile < fileInfo.Length)
            {
                FileInfo file = fileInfo[currentFile];
                if (!file.Name.Contains(".manifest") && file.Name.Contains("_"))
                {
                    Debug.Log($"Filling tile {currentFile}/{fileInfo.Length} {file.Name}");
                    yield return(new WaitForEndOfFrame());


                    string[] splitted    = file.Name.Split('_');
                    string[] coordinates = splitted[1].Split('-');

                    Vector3RD tileRDCoordinatesBottomLeft = new Vector3RD(double.Parse(coordinates[0]), double.Parse(coordinates[1]), 0);

                    var assetBundleTile = AssetBundle.LoadFromFile(file.FullName);

                    var mesh = assetBundleTile.LoadAllAssets <Mesh>().First();

                    if (mesh.bounds.size == Vector3.zero)
                    {
                        Debug.Log($"mesh bound is zero {file.Name}");
                        currentFile++;
                        continue;
                    }

                    GameObject newTile = new GameObject();
                    newTile.isStatic = true;
                    newTile.name     = file.Name;
                    newTile.AddComponent <MeshFilter>().sharedMesh   = mesh;
                    newTile.AddComponent <MeshCollider>().sharedMesh = mesh;
                    newTile.AddComponent <MeshRenderer>().material   = previewMaterial;
                    newTile.GetComponent <MeshRenderer>().materials  = _terrainMaterials.ToArray();

                    newTile.transform.position = CoordConvert.RDtoUnity(tileRDCoordinatesBottomLeft);

                    GameObject treeRoot = new GameObject();
                    treeRoot.name = file.Name.Replace("terrain", "trees");
                    treeRoot.transform.position = newTile.transform.position;

                    yield return(new WaitForEndOfFrame());                    //Make sure collider is processed

                    // yield return new WaitForSeconds(0.3f);


                    SpawnTreesInTile(treeRoot, tileRDCoordinatesBottomLeft);
                }
                currentFile++;
            }
        }
예제 #11
0
        IEnumerator GetSewerLinesInBoundingBox(TileChange tileChange, Tile tile, Vector3RD boundingBoxMinimum, Vector3RD boundingBoxMaximum, System.Action <TileChange> callback = null)
        {
            yield return(null);

            yield return(new WaitUntil(() => activeCount < maxSimultaneous));

            activeCount++;
            tile.gameObject.SetActive(true);
            string escapedUrl = sewerPipesWfsUrl;

            escapedUrl += UnityWebRequest.EscapeURL((boundingBoxMinimum.x).ToString(CultureInfo.InvariantCulture) + "," + (boundingBoxMinimum.y).ToString(CultureInfo.InvariantCulture) + "," + (boundingBoxMaximum.x).ToString(CultureInfo.InvariantCulture) + "," + (boundingBoxMaximum.y).ToString(CultureInfo.InvariantCulture));
            var sewerageRequest = UnityWebRequest.Get(escapedUrl);

            yield return(sewerageRequest.SendWebRequest());

            if (!sewerageRequest.isNetworkError && !sewerageRequest.isHttpError)
            {
                GeoJSON customJsonHandler = new GeoJSON(sewerageRequest.downloadHandler.text);
                yield return(null);

                Vector3 startpoint;
                Vector3 endpoint;
                int     parseCounter = 0;
                while (customJsonHandler.GotoNextFeature())
                {
                    parseCounter++;
                    if ((parseCounter % maxParsesPerFrame) == 0)
                    {
                        yield return(null);
                    }
                    double        diameter     = customJsonHandler.getPropertyFloatValue("diameter");
                    double        bobBeginPunt = customJsonHandler.getPropertyFloatValue("bob_beginpunt");
                    List <double> coordinates  = customJsonHandler.getGeometryLineString();
                    endpoint = ConvertCoordinates.CoordConvert.WGS84toUnity(new Vector3WGS(coordinates[0], coordinates[1], bobBeginPunt + Config.activeConfiguration.zeroGroundLevelY));
                    for (int i = 2; i < coordinates.Count; i += 2)
                    {
                        startpoint = endpoint;
                        double bobEindPunt = customJsonHandler.getPropertyFloatValue("bob_eindpunt");
                        endpoint = ConvertCoordinates.CoordConvert.WGS84toUnity(new Vector3WGS(coordinates[i], coordinates[(i + 1)], bobEindPunt + Config.activeConfiguration.zeroGroundLevelY));

                        sewerPipeSpawner.CreateSewerLine(startpoint, endpoint, diameter, tile.gameObject);
                    }
                }
                StartCoroutine(GetSewerManholesInBoundingBox(tileChange, boundingBoxMinimum, boundingBoxMaximum, tile, callback));
            }
            else
            { //callback if weberror
                Debug.Log("sewerlinedata not found");
                activeCount--;
                callback(tileChange);
            }

            yield return(null);
        }
        /// <summary>
        /// Load all the large ground tiles from AssetBundles, spawn it in our world, and start filling it with the trees that match the tile
        /// its RD coordinate rectangle. The tiles are named after the RD coordinates in origin at the bottomleft of the tile.
        /// </summary>
        private IEnumerator TraverseTileFiles()
        {
            var info     = new DirectoryInfo(sourceGroundTilesFolder);
            var fileInfo = info.GetFiles();

            var currentFile = 0;

            while (currentFile < fileInfo.Length)
            {
                FileInfo file = fileInfo[currentFile];
                if (!file.Name.Contains(".manifest") && file.Name.Contains("_"))
                {
                    Debug.Log("Filling tile " + currentFile + "/" + fileInfo.Length);
                    yield return(new WaitForEndOfFrame());

                    string filename = file.Name;
                    filename = filename.Replace("terrain_", "");
                    string[] coordinates = filename.Split('-');

                    Vector3RD tileRDCoordinatesBottomLeft = new Vector3RD(double.Parse(coordinates[0], System.Globalization.CultureInfo.InvariantCulture), double.Parse(coordinates[1], System.Globalization.CultureInfo.InvariantCulture), 0);
                    Vector3RD tileCenter          = new Vector3RD(tileRDCoordinatesBottomLeft.x + 500, tileRDCoordinatesBottomLeft.y + 500, tileRDCoordinatesBottomLeft.z);
                    var       assetBundleTile     = AssetBundle.LoadFromFile(file.FullName);
                    Mesh[]    meshesInAssetbundle = new Mesh[0];
                    try
                    {
                        meshesInAssetbundle = assetBundleTile.LoadAllAssets <Mesh>();
                    }
                    catch (Exception)
                    {
                        Debug.Log("Could not find a mesh in this assetbundle.");
                        assetBundleTile.Unload(true);
                    }

                    GameObject newTile = new GameObject();
                    newTile.isStatic = true;
                    newTile.name     = file.Name;
                    newTile.AddComponent <MeshFilter>().sharedMesh   = meshesInAssetbundle[0];
                    newTile.AddComponent <MeshCollider>().sharedMesh = meshesInAssetbundle[0];

                    newTile.AddComponent <MeshRenderer>().material = previewMaterial;
                    newTile.transform.position = CoordConvert.RDtoUnity(tileCenter);

                    GameObject treeRoot = new GameObject();
                    treeRoot.name = file.Name.Replace("terrain", "trees");
                    treeRoot.transform.position = newTile.transform.position;

                    yield return(new WaitForEndOfFrame());                    //Make sure collider is processed

                    SpawnTreesInTile(treeRoot, tileRDCoordinatesBottomLeft);
                }
                currentFile++;
            }
        }
예제 #13
0
        private bool PointISInsideArea(Vector3RD point, double OriginX, double OriginY, float tileSize)
        {
            if (point.x < OriginX || point.x > (OriginX + tileSize))
            {
                return(false);
            }
            if (point.y < OriginY || point.y > (OriginY + tileSize))
            {
                return(false);
            }

            return(true);
        }
예제 #14
0
        private List <Vector3RD> GetVertsRD(CityModel cityModel)
        {
            List <Vector3RD> vertsRD          = new List <Vector3RD>();
            Vector3RD        vertexCoordinate = new Vector3RD();

            foreach (Vector3Double vertex in cityModel.vertices)
            {
                vertexCoordinate.x = vertex.x;
                vertexCoordinate.y = vertex.y;
                vertexCoordinate.z = vertex.z;
                vertsRD.Add(vertexCoordinate);
            }
            return(vertsRD);
        }
예제 #15
0
    public void TestGetRDCoordinate()
    {
        var testFilePaths1 = new string[] {
            "123000_443000_utrecht_lod1",
            "utrecht_123000_443000_lod1",
            "buildings_123000_443000.1.2",
            "terrain_123000-443000",
            "terrain_123000-443000-lod1",
            "trees_123000-443000",
            "trees_123000-443000-lod1",
        };

        foreach (var filePath in testFilePaths1)
        {
            var expectedRd = new Vector3RD(123000, 443000, 0);
            var result     = filePath.GetRDCoordinate();
            Assert.AreEqual(expectedRd, result);
        }

        var testFilePaths2 = new string[] {
            "7000_392000_zee_land_lod1",
            "zee_land_7000_392000_lod1",
            "buildings_7000_392000.1.2",
            "terrain_7000-392000",
            "terrain_7000-392000-lod1",
            "trees_7000-392000",
            "trees_7000-392000-lod1",
        };

        foreach (var filePath in testFilePaths2)
        {
            var expectedRd = new Vector3RD(7000, 392000, 0);
            var result     = filePath.GetRDCoordinate();
            Assert.AreEqual(expectedRd, result);
        }

        var testFilePaths3 = new string[] {
            "AssetBundles",
            "move.py"
        };

        foreach (var filePath in testFilePaths3)
        {
            Assert.Throws <Exception>(
                delegate { filePath.GetRDCoordinate(); });
        }

        Exception ex = Assert.Throws <Exception>(
            delegate { "AssetBundles".GetRDCoordinate(); });
    }
예제 #16
0
        private List <Vector3> GetVerts(CityModel cityModel, Vector3RD origin)
        {
            List <Vector3> verts            = new List <Vector3>();
            Vector3        unityOrigin      = CoordConvert.RDtoUnity(origin);
            Vector3RD      vertexCoordinate = new Vector3RD();

            foreach (Vector3Double vertex in cityModel.vertices)
            {
                vertexCoordinate.x = vertex.x;
                vertexCoordinate.y = vertex.y;
                vertexCoordinate.z = vertex.z;
                verts.Add(CoordConvert.RDtoUnity(vertexCoordinate) - unityOrigin);
            }

            return(verts);
        }
예제 #17
0
    private IEnumerator createFile(Bounds UnityBounds, List <Layer> layerList)
    {
        freezeLayers(layerList, true);
        Debug.Log(layerList.Count);
        Vector3RD bottomLeftRD = CoordConvert.UnitytoRD(UnityBounds.min);
        Vector3RD topRightRD   = CoordConvert.UnitytoRD(UnityBounds.max);

        boundingbox = new MeshClipper.RDBoundingBox(bottomLeftRD.x, bottomLeftRD.y, topRightRD.x, topRightRD.y);
        DxfFile file = new DxfFile();

        file.SetupDXF();
        yield return(null);

        MeshClipper meshClipper = new MeshClipper();

        loadingScreen.ShowMessage("dxf-bestand genereren...");
        loadingScreen.ProgressBar.Percentage(0f);

        int layercounter = 0;

        foreach (var layer in layerList)
        {
            List <GameObject> gameObjectsToClip = getTilesInLayer(layer, bottomLeftRD, topRightRD);
            if (gameObjectsToClip.Count == 0)
            {
                continue;
            }
            foreach (var gameObject in gameObjectsToClip)
            {
                meshClipper.SetGameObject(gameObject);
                for (int submeshID = 0; submeshID < gameObject.GetComponent <MeshFilter>().sharedMesh.subMeshCount; submeshID++)
                {
                    meshClipper.clipSubMesh(boundingbox, submeshID);
                    string layerName = gameObject.GetComponent <MeshRenderer>().sharedMaterials[submeshID].name.Replace(" (Instance)", "");

                    file.AddLayer(meshClipper.clippedVerticesRD, layerName, getColor(gameObject.GetComponent <MeshRenderer>().sharedMaterials[submeshID]));
                    yield return(null);
                }
            }
            loadingScreen.ProgressBar.Percentage(50 * layercounter / layerList.Count);
            layercounter++;
        }
        freezeLayers(layerList, false);
        file.Save();
        loadingScreen.Hide();
        Debug.Log("file saved");
    }
예제 #18
0
        private Vector2[] RDuv2(Vector3[] verts, Vector3 UnityOrigin, float tileSize)
        {
            Vector3   UnityCoordinate;
            Vector3RD rdCoordinate;
            Vector3RD rdOrigin = CoordConvert.UnitytoRD(UnityOrigin);
            float     offset   = -tileSize / 2;

            Vector2[] uv2 = new Vector2[verts.Length];
            for (int i = 0; i < verts.Length; i++)
            {
                UnityCoordinate = verts[i] + UnityOrigin;
                rdCoordinate    = CoordConvert.UnitytoRD(UnityCoordinate);
                uv2[i].x        = ((float)(rdCoordinate.x - rdOrigin.x) + offset) / tileSize;
                uv2[i].y        = ((float)(rdCoordinate.y - rdOrigin.y) + offset) / tileSize;
            }
            return(uv2);
        }
예제 #19
0
 public void Start()
 {
     //Calculate offset. ( Our viewer expects tiles with the origin in the center )
     tileOffset = new Vector3RD()
     {
         x = Config.activeConfiguration.RelativeCenterRD.x,
         y = Config.activeConfiguration.RelativeCenterRD.y,
         z = 0
     };
     tileOffset.x      -= 500;
     tileOffset.y      -= 500;
     unityTileOffset    = CoordConvert.RDtoUnity(tileOffset);
     trees              = new List <Tree>();
     treeLines          = new List <string>();
     noPrefabFoundNames = new List <string>();
     ParseTreeData();
 }
예제 #20
0
    /// <summary>
    ///
    /// </summary>
    /// <param name="vectors">triangleVertices (unity-format, so Y is up)</param>
    /// <param name="clipboundary">coordinates of vertices of (square) clipboundary</param>
    /// <param name="coordinateOffset"></param>
    /// <param name="unityOrigin"></param>
    private void CreateClippedMesh(List <Vector3> vectors, List <Vector3> clipboundary, Vector3RD coordinateOffset, Vector3 unityOrigin)
    {
        List <Vector3> defshape = TriangleClipping.SutherlandHodgman.ClipPolygon(vectors, clipboundary);

        if (defshape.Count < 3)
        {
            return;
        }
        List <Vector3RD> verticesRD = new List <Vector3RD>();
        Vector3RD        vector;

        //restore coordinates
        for (int i = 0; i < defshape.Count; i++)
        {
            vector   = new Vector3RD();
            vector.x = defshape[i].x + coordinateOffset.x;
            vector.y = defshape[i].z + coordinateOffset.y;
            vector.z = defshape[i].y + coordinateOffset.z;
            verticesRD.Add(vector);
        }
        verticesRD.Reverse();


        List <int> indices = new List <int>();

        indices.Add(0);
        indices.Add(1);
        indices.Add(2);
        for (int i = 3; i < defshape.Count; i++)
        {
            indices.Add(0);
            indices.Add(indices[indices.Count - 2]);
            indices.Add(i);
        }
        Vector3[] verticesUnity = VerticesRDtoUnity(verticesRD.ToArray(), unityOrigin);

        Mesh newSubMesh = new Mesh();

        newSubMesh.vertices  = verticesUnity;
        newSubMesh.triangles = indices.ToArray();
        newSubMesh.uv        = getUVs(verticesRD.ToArray());
        newSubMesh.Optimize();
        newSubMesh.RecalculateNormals();
        submeshes.Add(newSubMesh);
    }
예제 #21
0
        /// <summary>
        /// Converts the VISSIM RD coordinate string into a Vector3
        /// </summary>
        /// <param name="stringVector"></param>
        /// <returns></returns>
        public Vector3 convertStringToVector(string stringVector)
        {
            //0 value is X
            //1 value is Y
            //2 value is Z
            stringVector = stringVector.Replace(".", ","); // Transforms decimal from US standard which uses a Period to European with a Comma

            string[] splitString = stringVector.Split(' '); // Splits the string into individual vectors
            double x = double.Parse(splitString[0]);
            double y = double.Parse(splitString[1]);
            double z = double.Parse(splitString[2]);
            Vector3RD rdVector = new Vector3RD(x, y, z); // Creates the Double Vector

            Vector3 convertedCoordinates = ConvertCoordinates.CoordConvert.RDtoUnity(rdVector);
            // Y Coordinates will be calculated by the vehicle to connect with the Map (Maaiveld).

            return convertedCoordinates;
        }
예제 #22
0
        IEnumerator GetSewerManholesInBoundingBox(TileChange tileChange, Vector3RD boundingBoxMinimum, Vector3RD boundingBoxMaximum, Tile tile, System.Action <TileChange> callback = null)
        {
            string escapedUrl = sewerManholesWfsUrl;

            escapedUrl += UnityWebRequest.EscapeURL((boundingBoxMinimum.x).ToString(CultureInfo.InvariantCulture) + "," + (boundingBoxMinimum.y).ToString(CultureInfo.InvariantCulture) + "," + (boundingBoxMaximum.x).ToString(CultureInfo.InvariantCulture) + "," + (boundingBoxMaximum.y).ToString(CultureInfo.InvariantCulture));
            var sewerageRequest = UnityWebRequest.Get(escapedUrl);

            yield return(sewerageRequest.SendWebRequest());

            if (!sewerageRequest.isNetworkError && !sewerageRequest.isHttpError)
            {
                StartCoroutine(SpawnManHoleObjects(sewerageRequest.downloadHandler.text, tileChange, tile, callback));
            }
            else
            {
                activeCount--;
                callback(tileChange);
            }
            yield return(null);
        }
예제 #23
0
    //private IEnumerator LoadAreas()
    //{
    //    for (int x = areaXmin; x < areaXmax; x+=(int)tileSize)
    //    {
    //        for (int y = areaYmin; y < areaYmax; y += (int)tileSize)
    //        {
    //            yield return new WaitWhile(() => areaLoadingFinished == false);
    //            areaLoadingFinished = false;

    //            LoadArea((float)x, (float)y);
    //        }
    //    }



    //}

    //void LoadArea(float rdX, float rdY)
    //{
    //    submeshes = new List<Mesh>();

    //    extentRD.x = rdX;
    //    extentRD.y = rdY;
    //    extentRD.z = extentRD.x + tileSize;
    //    extentRD.w = extentRD.y + tileSize;


    //    Vector3WGS RDVector = CoordConvert.RDtoWGS84(extentRD.x, extentRD.y);
    //    extentWGS.x = (float)RDVector.lon;
    //    extentWGS.y = (float)RDVector.lat;
    //    RDVector = CoordConvert.RDtoWGS84(extentRD.z, extentRD.w);
    //    extentWGS.z = (float)RDVector.lon;
    //    extentWGS.w = (float)RDVector.lat;

    //    StartCoroutine(IdentifyQMTileNames(extentWGS, (didError) => {
    //        HandleTiles();
    //    }));
    //}


    //private void HandleTiles()
    //{
    //    int submeshcount = submeshes.Count;
    //    CombineInstance[] combine = new CombineInstance[submeshcount];
    //    for (int i = 0; i < submeshcount; i++)
    //    {
    //        combine[i].mesh = submeshes[i];
    //    }
    //    Vector3 position = CoordConvert.RDtoUnity(new Vector3RD(extentRD.x, (extentRD.y), 0));
    //    position.y = 0;
    //    Mesh combinedMesh = new Mesh();
    //    combinedMesh.CombineMeshes(combine,true,false);
    //    Debug.Log(combinedMesh.bounds.extents);
    //    GameObject go = new GameObject();
    //    go.transform.position = position;

    //    go.AddComponent<MeshFilter>().mesh = combinedMesh;
    //    SaveMesh(combinedMesh);
    //    Debug.Log("Tile Handled");
    //    areaLoadingFinished = true;
    //}

    //private void SaveMesh(Mesh mesh)
    //{
    //    string meshname = "Terrain_"+extentRD.x.ToString()+"_"+extentRD.y.ToString() + "_lod0.mesh";
    //    string meshFolderName = "Assets/Terrain/LOD0/";
    //    AssetDatabase.CreateAsset(mesh, meshFolderName + meshname);
    //    AssetDatabase.SaveAssets();
    //    AssetImporter.GetAtPath(meshFolderName + meshname).SetAssetBundleNameAndVariant(extentRD.x.ToString() + "_" + extentRD.y.ToString() + "_terrain_lod0","");
    //    AssetDatabase.SaveAssets();
    //}

    //private void CreateClippedMesh(Mesh mesh, int X, int Y)
    //{

    //    Debug.Log(X + "-" + Y + " :overlapTile");
    //    Vector3WGS originWGS = TerrainTileCenterWGS(X, Y);
    //    Vector3 unityOrigin = CoordConvert.WGS84toUnity(extentWGS.x, extentWGS.y);

    //    Vector3RD[] verticesRD = getVerticesRD(terrainTile, X, Y, originWGS);
    //    int[] originalTriangles = getTriangles(terrainTile);
    //    List<int> insideTriangles = GetInsideTriangles(verticesRD, originalTriangles);


    //    Vector3[] verticesUnity = VerticesRDtoUnity(verticesRD, unityOrigin);
    //    CreateOverlapTriangles(verticesRD, originalTriangles, verticesUnity, unityOrigin);
    //    Mesh newSubMesh = new Mesh();
    //    newSubMesh.vertices = verticesUnity;
    //    newSubMesh.triangles = insideTriangles.ToArray();
    //    newSubMesh.uv = getUVs(verticesRD);
    //    newSubMesh.Optimize();
    //    newSubMesh.RecalculateNormals();
    //    submeshes.Add(newSubMesh);

    //    activeTiles--;

    //}

    private List <Vector3> CreateClippingPolygon(Vector4 extendRD, Vector3RD coordinateOffset)
    {
        List <Vector3> clippingPolygon = new List <Vector3>();
        Vector3        vector          = new Vector3();

        vector.x = (float)(extentRD.x - coordinateOffset.x);
        vector.z = (float)(extentRD.y - coordinateOffset.y);
        clippingPolygon.Add(vector);
        vector   = new Vector3();
        vector.x = (float)(extentRD.z - coordinateOffset.x);
        vector.z = (float)(extentRD.y - coordinateOffset.y);
        clippingPolygon.Add(vector);
        vector   = new Vector3();
        vector.x = (float)(extentRD.z - coordinateOffset.x);
        vector.z = (float)(extentRD.w - coordinateOffset.y);
        clippingPolygon.Add(vector);
        vector   = new Vector3();
        vector.x = (float)(extentRD.x - coordinateOffset.x);
        vector.z = (float)(extentRD.w - coordinateOffset.y);
        clippingPolygon.Add(vector);
        return(clippingPolygon);
    }
예제 #24
0
    private List <int> GetInsideTriangles(Vector3RD[] verticesRD, int[] originalTriangles)
    {
        List <int> Triangles             = new List <int>();
        int        originalTriangleCount = originalTriangles.Length - 2;

        for (int i = 0; i < originalTriangleCount; i += 3)
        {
            Vector3RD vector1 = verticesRD[originalTriangles[i]];
            Vector3RD vector2 = verticesRD[originalTriangles[i + 1]];
            Vector3RD vector3 = verticesRD[originalTriangles[i + 2]];
            if (Vector3RDIsInside(vector1) && Vector3RDIsInside(vector2) && Vector3RDIsInside(vector3))
            {
                //winding-order is clockwise
                Triangles.Add(originalTriangles[i]);
                Triangles.Add(originalTriangles[i + 1]);
                Triangles.Add(originalTriangles[i + 2]);
            }
            else
            {
            }
        }
        return(Triangles);
    }
예제 #25
0
        IEnumerator GetSewerManholesInBoundingBox(TileChange tileChange, Vector3RD boundingBoxMinimum, Vector3RD boundingBoxMaximum, Tile tile, System.Action <TileChange> callback = null)
        {
            string escapedUrl = Config.activeConfiguration.sewerManholesWfsUrl;

            escapedUrl += UnityWebRequest.EscapeURL($"{boundingBoxMinimum.x.ToInvariant()},{boundingBoxMinimum.y.ToInvariant()},{boundingBoxMaximum.x.ToInvariant()},{boundingBoxMaximum.y.ToInvariant()}");

            var sewerageRequest = UnityWebRequest.Get(escapedUrl);

            tile.runningWebRequest = sewerageRequest;
            yield return(sewerageRequest.SendWebRequest());

            tile.runningWebRequest = null;

            if (!sewerageRequest.isNetworkError && !sewerageRequest.isHttpError)
            {
                yield return(SpawnManHoleObjects(sewerageRequest.downloadHandler.text, tileChange, tile, callback));
            }
            else
            {
                callback(tileChange);
            }
            yield return(null);
        }
        /// <summary>
        /// Spawn all the trees located within the RD coordinate bounds of the 1x1km tile.
        /// </summary>
        /// <param name="treeTile">The target 1x1 km ground tile</param>
        /// <param name="tileCoordinates">RD Coordinates of the tile</param>
        /// <returns></returns>
        private void SpawnTreesInTile(GameObject treeTile, Vector3RD tileCoordinates)
        {
            //TODO: Add all trees within this tile (1x1km)
            int treeChecked = trees.Count - 1;

            while (treeChecked >= 0)
            {
                Tree tree = trees[treeChecked];

                if (tree.RD.x > tileCoordinates.x && tree.RD.y > tileCoordinates.y && tree.RD.x < tileCoordinates.x + tileSize && tree.RD.y < tileCoordinates.y + tileSize)
                {
                    SpawnTreeOnGround(treeTile, tree);
                    trees.RemoveAt(treeChecked);
                }
                treeChecked--;
            }

            //Define a preview position to preview the tree tile in our scene
            Vector3 previewPosition = treeTile.transform.position + Vector3.down * Config.activeConfiguration.zeroGroundLevelY;

            treeTile.transform.position = unityTileOffset;

            CreateTreeTile(treeTile, previewPosition);
        }
예제 #27
0
        public GameObject CreateMesh(CityModel cityModel, Vector3RD origin)
        {
            GameObject container = new GameObject();

            //Terraindata terrainData = container.AddComponent<Terraindata>();

            verts = GetVerts(cityModel, origin);

            triangleLists = GetTriangleLists(cityModel);

            Mesh mesh = new Mesh();

            mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            mesh.SetVertices(verts.ToArray());
            mesh.subMeshCount = triangleLists.Count;

            int submeshnumber = 0;

            foreach (var item in triangleLists)
            {
                //terrainData.terrainTypes.Add(item.Key);
                //if (submeshnumber==0 || submeshnumber==1)
                //{
                mesh.SetTriangles(item.Value.ToArray(), submeshnumber);

                //}
                submeshnumber++;
            }

            mesh.RecalculateNormals();
            mesh.Optimize();
            container.AddComponent <MeshFilter>().mesh = mesh;
            container.AddComponent <MeshRenderer>().sharedMaterials = new Material[submeshnumber];

            return(container);
        }
예제 #28
0
        /// <summary>
        /// Parse files whose name contains coordinates that overlap the provided tile area
        /// </summary>
        /// <param name="rdCoordinates">Bottom left RD coordinates of the tile</param>
        /// <returns></returns>
        private IEnumerator ParseSpecificFiles(Vector2Int rdCoordinates)
        {
            //Read files list
            var info     = new DirectoryInfo(geoJsonSourceFilesFolder);
            var fileInfo = info.GetFiles();

            //First create gameobjects for all the buildigns we parse
            int parsed = 0;

            for (int i = 0; i < fileInfo.Length; i++)
            {
                var file = fileInfo[i];

                string[] fileNameParts = file.Name.Replace(".json", "").Split('_');

                //Determine parts of the filename
                var id    = fileNameParts[0];
                var count = fileNameParts[1];
                var xmin  = double.Parse(fileNameParts[3]);
                var ymin  = double.Parse(fileNameParts[4]);
                var xmax  = double.Parse(fileNameParts[5]);
                var ymax  = double.Parse(fileNameParts[6]);

                //Skip if these filename bounds are not within our selected rectangle
                if (xmin > rdCoordinates.x + tileSize || xmax < rdCoordinates.x || ymin > rdCoordinates.y + tileSize || ymax < rdCoordinates.y)
                {
                    continue;
                }
                Debug.Log("Parsing " + file.Name);
                if (!Application.isBatchMode)
                {
                    yield return(new WaitForEndOfFrame());
                }

                //Parse the file
                var jsonstring   = File.ReadAllText(file.FullName);
                var cityjsonNode = JSON.Parse(jsonstring);
                if (cityjsonNode["CityObjects"] == null)
                {
                    Debug.Log("FAILURE PARSING: " + file.Name);
                    continue; //Failed to parse the json
                }

                //Get vertices
                allVerts = new List <Vector3>();

                //Optionaly parse transform scale and offset
                var transformScale = (cityjsonNode["transform"] != null && cityjsonNode["transform"]["scale"] != null) ? new Vector3Double(
                    cityjsonNode["transform"]["scale"][0].AsDouble,
                    cityjsonNode["transform"]["scale"][1].AsDouble,
                    cityjsonNode["transform"]["scale"][2].AsDouble
                    ) : new Vector3Double(1, 1, 1);

                var transformOffset = (cityjsonNode["transform"] != null && cityjsonNode["transform"]["translate"] != null) ? new Vector3Double(
                    cityjsonNode["transform"]["translate"][0].AsDouble,
                    cityjsonNode["transform"]["translate"][1].AsDouble,
                    cityjsonNode["transform"]["translate"][2].AsDouble
                    ) : new Vector3Double(0, 0, 0);

                //Now load all the vertices with the scaler and offset applied
                foreach (JSONNode node in cityjsonNode["vertices"])
                {
                    var rd = new Vector3RD(
                        node[0].AsDouble * transformScale.x + transformOffset.x,
                        node[1].AsDouble * transformScale.y + transformOffset.y,
                        node[2].AsDouble * transformScale.z + transformOffset.z
                        );
                    var unityCoordinates = CoordConvert.RDtoUnity(rd);
                    allVerts.Add(unityCoordinates);
                }

                //Now build the meshes and create objects for these buildings
                int buildingCount = 0;
                foreach (JSONNode buildingNode in cityjsonNode["CityObjects"])
                {
                    //A building
                    var name = buildingNode["attributes"]["identificatie"].Value.Replace("NL.IMBAG.Pand.", "");

                    //Check if this name/ID exists in our list of manualy added child objects. If it is there, skip it.
                    if (overrideChildObjects.Where(overrideGameObject => overrideGameObject.name == name).SingleOrDefault())
                    {
                        print("Skipped parsing " + name + " because we have added a custom object for that");
                        continue;
                    }

                    GameObject building = new GameObject();
                    building.transform.SetParent(this.transform, false);
                    building.name = name;

                    //The building verts/triangles
                    var boundaries = buildingNode["geometry"][lodSlot]["boundaries"][0];
                    meshTriangles = new List <int>();
                    List <Vector3> thisMeshVerts = new List <Vector3>();
                    foreach (JSONNode boundary in boundaries)
                    {
                        JSONNode triangle = boundary[0];

                        vertIndex = triangle[2].AsInt;
                        thisMeshVerts.Add(allVerts[vertIndex]);
                        meshTriangles.Add(thisMeshVerts.Count - 1); //TODO. Group same verts

                        vertIndex = triangle[1].AsInt;
                        thisMeshVerts.Add(allVerts[vertIndex]);
                        meshTriangles.Add(thisMeshVerts.Count - 1);

                        vertIndex = triangle[0].AsInt;
                        thisMeshVerts.Add(allVerts[vertIndex]);
                        meshTriangles.Add(thisMeshVerts.Count - 1);
                    }

                    //Construct the mesh
                    Mesh buildingMesh = new Mesh();
                    if (thisMeshVerts.Count > Mathf.Pow(2, 16))
                    {
                        buildingMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
                    }

                    buildingMesh.vertices  = thisMeshVerts.ToArray();
                    buildingMesh.triangles = meshTriangles.ToArray();
                    buildingMesh.RecalculateNormals();

                    var meshRenderer = building.AddComponent <MeshRenderer>();
                    meshRenderer.material = DefaultMaterial;
                    meshRenderer.enabled  = renderInViewport;
                    building.AddComponent <MeshFilter>().sharedMesh = buildingMesh;
                    buildingCount++;
                }

                parsed++;
                print("Parsed GeoJSONS to fill tile: " + parsed + ". Buildings in tile: " + buildingCount);
            }
        }
예제 #29
0
        private IEnumerator SpawnLineObjects(SewerLines sewerLines, TileChange tileChange, Vector3RD boundingBoxMinimum, Vector3RD boundingBoxMaximum, Tile tile, System.Action <TileChange> callback = null)
        {
            tile.gameObject.SetActive(isEnabled);
            SewerLines.Feature sewerLineFeature;
            for (int i = 0; i < sewerLines.features.Length; i++)
            {
                if ((i % maxSpawnsPerFrame) == 0)
                {
                    yield return(new WaitForEndOfFrame());
                }

                sewerLineFeature = sewerLines.features[i];

                sewerPipeSpawner.CreateSewerLine(
                    sewerLineFeature.geometry.unity_coordinates[0],
                    sewerLineFeature.geometry.unity_coordinates[1],
                    float.Parse(sewerLineFeature.properties.diameter),
                    tile.gameObject
                    );
            }

            //Lines are done spawing. Start loading and spawing the manholes.
            StartCoroutine(GetSewerManholesInBoundingBox(tileChange, boundingBoxMinimum, boundingBoxMaximum, tile, callback));

            yield return(null);
        }
예제 #30
0
        private Mesh CreateCityObjectMesh(CityModel cityModel, string cityObjectType, double originX, double originY, float tileSize, string bgtProperty, List <string> bgtValues, bool include)
        {
            List <Vector3RD> RDTriangles = GetTriangleListRD(cityModel, cityObjectType, bgtProperty, bgtValues, include);

            List <Vector3RD> clippedRDTriangles = new List <Vector3RD>();
            List <Vector3>   vectors            = new List <Vector3>();

            List <Vector3> clipboundary = CreateClippingPolygon(tileSize);

            if (RDTriangles.Count == 0)
            {
                return(CreateEmptyMesh());
            }

            //clip all the triangles
            for (int i = 0; i < RDTriangles.Count; i += 3)
            {
                if (PointISInsideArea(RDTriangles[i], originX, originY, tileSize) && PointISInsideArea(RDTriangles[i + 1], originX, originY, tileSize) && PointISInsideArea(RDTriangles[i + 2], originX, originY, tileSize))
                {
                    clippedRDTriangles.Add(RDTriangles[i + 2]);
                    clippedRDTriangles.Add(RDTriangles[i + 1]);
                    clippedRDTriangles.Add(RDTriangles[i]);
                    continue;
                }


                //offset RDvertices so coordinates can be saved as a float
                // flip y and z-axis so clippingtool works
                //reverse order to make them clockwise so the clipping-algorithm can use them
                vectors.Clear();
                vectors.Add(new Vector3((float)(RDTriangles[i + 2].x - originX), (float)RDTriangles[i + 2].z, (float)(RDTriangles[i + 2].y - originY)));
                vectors.Add(new Vector3((float)(RDTriangles[i + 1].x - originX), (float)RDTriangles[i + 1].z, (float)(RDTriangles[i + 1].y - originY)));
                vectors.Add(new Vector3((float)(RDTriangles[i].x - originX), (float)RDTriangles[i].z, (float)(RDTriangles[i].y - originY)));


                List <Vector3> defshape = Netherlands3D.Utilities.TriangleClipping.SutherlandHodgman.ClipPolygon(vectors, clipboundary);

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

                if (defshape[0].x.ToString() == "NaN")
                {
                    continue;
                }

                Vector3RD vectorRD = new Vector3RD();
                // add first three vectors

                vectorRD.x = defshape[0].x + originX;
                vectorRD.y = defshape[0].z + originY;
                vectorRD.z = defshape[0].y;
                clippedRDTriangles.Add(vectorRD);

                vectorRD.x = defshape[1].x + originX;
                vectorRD.y = defshape[1].z + originY;
                vectorRD.z = defshape[1].y;
                clippedRDTriangles.Add(vectorRD);
                vectorRD.x = defshape[2].x + originX;
                vectorRD.y = defshape[2].z + originY;
                vectorRD.z = defshape[2].y;
                clippedRDTriangles.Add(vectorRD);

                // add extra vectors. vector makes a triangle with the first and the previous vector.
                for (int j = 3; j < defshape.Count; j++)
                {
                    vectorRD.x = defshape[0].x + originX;
                    vectorRD.y = defshape[0].z + originY;
                    vectorRD.z = defshape[0].y;
                    clippedRDTriangles.Add(vectorRD);

                    vectorRD.x = defshape[j - 1].x + originX;
                    vectorRD.y = defshape[j - 1].z + originY;
                    vectorRD.z = defshape[j - 1].y;
                    clippedRDTriangles.Add(vectorRD);

                    vectorRD.x = defshape[j].x + originX;
                    vectorRD.y = defshape[j].z + originY;
                    vectorRD.z = defshape[j].y;
                    clippedRDTriangles.Add(vectorRD);
                }
            }

            //createMesh
            List <Vector3> verts        = new List <Vector3>();
            Vector3RD      tileCenterRD = new Vector3RD();

            tileCenterRD.x = originX + (tileSize / 2);
            tileCenterRD.y = originY + (tileSize / 2);
            tileCenterRD.z = 0;
            Vector3    tileCenterUnity = CoordConvert.RDtoUnity(tileCenterRD);
            List <int> ints            = new List <int>();

            for (int i = 0; i < clippedRDTriangles.Count; i++)
            {
                Vector3 coord = CoordConvert.RDtoUnity(clippedRDTriangles[i]) - tileCenterUnity;
                ints.Add(i);
                verts.Add(coord);
            }
            ints.Reverse(); //reverse the trianglelist to make the triangles counter-clockwise again

            if (ints.Count == 0)
            {
                return(CreateEmptyMesh());
            }
            Mesh mesh = new Mesh();

            mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
            mesh.vertices    = verts.ToArray();
            mesh.triangles   = ints.ToArray();
            mesh             = WeldVertices(mesh);
            mesh.RecalculateNormals();
            mesh.Optimize();
            return(mesh);
        }