private void AddFace(ref TriangulationList <Vector3> triangulation, int i0, int i1, int i4, int i5)
        {
            // left side tri low
            triangulation.Indices.Add(i0);
            triangulation.Indices.Add(i4);
            triangulation.Indices.Add(i5);

            // left side tri high
            triangulation.Indices.Add(i0);
            triangulation.Indices.Add(i5);
            triangulation.Indices.Add(i1);
        }
        private void Build3DTopology_Triangulation(VisualTopoModel model, IColorCalculator colorFunc)
        {
            // Build color function
            float minElevation = model.Graph.AllNodes.Min(n => n.Model.VectorLocal.Z);


            // Generate triangulation
            //
            TriangulationList <Vector3> markersTriangulation = new TriangulationList <Vector3>();
            TriangulationList <Vector3> triangulation        = GraphTraversal_Triangulation(model, null, ref markersTriangulation, model.Graph.Root, colorFunc);

            model.TriangulationFull3D = triangulation + markersTriangulation;
        }
Beispiel #3
0
        public ModelRoot AddMesh(ModelRoot model, string nodeName, TriangulationList <Vector3> triangulation, Vector4 color = default, bool doubleSided = true)
        {
            if (color == default || triangulation.HasColors)
            {
                color = Vector4.One;
            }

            var scene = model.UseScene(TERRAIN_SCENE_NAME);
            var rnode = scene.FindNode(n => n.Name == nodeName);

            if (rnode == null)
            {
                rnode = scene.CreateNode(nodeName);
            }
            var rmesh = rnode.Mesh = FindOrCreateMesh(model, string.Concat(nodeName, "Mesh"));


            var material = model.CreateMaterial(string.Concat(nodeName, "Material"))
                           .WithPBRMetallicRoughness(color, null, null, 0, 1f)
                           .WithDoubleSide(doubleSided);

            material.Alpha = (color.W < 1.0 || (triangulation.HasColors && triangulation.Colors.Any(c => c.W < 1.0))) ?
                             SharpGLTF.Schema2.AlphaMode.BLEND
                                : SharpGLTF.Schema2.AlphaMode.OPAQUE;

            // Rotate for glTF compliance
            triangulation.Positions.ToGlTFSpace();

            var normals = _meshService.ComputeMeshNormals(triangulation.Positions, triangulation.Indices);


            // create mesh primitive
            var primitive = rmesh.CreatePrimitive()
                            .WithVertexAccessor("POSITION", triangulation.Positions)
                            .WithVertexAccessor("NORMAL", normals.ToList())
                            .WithIndicesAccessor(PrimitiveType.TRIANGLES, triangulation.Indices);

            if (triangulation.HasColors)
            {
                primitive = primitive.WithVertexAccessor("COLOR_0", triangulation.Colors);
            }

            primitive = primitive.WithMaterial(material);
            return(model);
        }
        /// <summary>
        /// // Make a rectangle perpendicual to direction centered on point (should be centered at human eye (y = 2m)
        /// </summary>
        /// <param name="triangulation"></param>
        /// <param name="current"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        private TriangulationList <Vector3> AddCorridorRectangleSection(TriangulationList <Vector3> triangulation, VisualTopoData current, VisualTopoData nextData, int startIndex, IColorCalculator colorFunc)
        {
            Vector3      next      = (nextData == null) ? current.VectorLocal : nextData.VectorLocal;
            GeoPointRays rays      = current.GeoPointLocal;
            Vector3      direction = (nextData == null) ? Vector3.UnitZ * -1 : next - current.VectorLocal;

            direction = (direction == Vector3.Zero) ? Vector3.UnitZ * -1 : direction;
            var position = current.VectorLocal;

            Vector3 side = Vector3.Normalize(Vector3.Cross(direction, Vector3.UnitY));

            if (IsInvalid(side))      // Vector3 is UnitY
            {
                side = Vector3.UnitX; // set it to UnitX
            }
            Vector3 up = Vector3.Normalize(Vector3.Cross(direction, side));

            if (IsInvalid(side) || IsInvalid(up))
            {
                return(triangulation);
            }
            //var m = Matrix4x4.CreateWorld(next, direction, Vector3.UnitZ);

            triangulation.Positions.Add(position - side * rays.Left - up * rays.Down);
            triangulation.Positions.Add(position - side * rays.Left + up * rays.Up);
            triangulation.Positions.Add(position + side * rays.Right + up * rays.Up);
            triangulation.Positions.Add(position + side * rays.Right - up * rays.Down);

            //Vector4 color = (colorIndex++) % 2 == 0 ? VectorsExtensions.CreateColor(0, 255, 0) : VectorsExtensions.CreateColor(0, 0, 255);

            triangulation.Colors.AddRange(Enumerable.Repeat(colorFunc.GetColor(current, position), 4));

            // corridor sides
            if (triangulation.NumPositions > 4)
            {
                int i         = startIndex; // triangulation.NumPositions - 8;
                int lastIndex = triangulation.NumPositions - 4;
                for (int n = 0; n < 4; n++)
                {
                    AddFace(ref triangulation, i + n, i + (n + 1) % 4
                            , lastIndex + n, lastIndex + (n + 1) % 4);
                }
            }
            return(triangulation);
        }
Beispiel #5
0
        public void Run()
        {
            // DjebelMarra
            var bbox    = GeometryService.GetBoundingBox("POLYGON((7.713614662985839 46.03014517094771,7.990332802634277 46.03014517094771,7.990332802634277 45.86877753239648,7.713614662985839 45.86877753239648,7.713614662985839 46.03014517094771))");
            var dataset = DEMDataSet.NASADEM;

            string modelName = $"{dataset.Name}_{DateTime.Now:yyyyMMdd_HHmmss}";
            string outputDir = Directory.GetCurrentDirectory();

            //var modelTest = _sharpGltfService.CreateNewModel();
            //var triangulation = CreateText("20 km", VectorsExtensions.CreateColor(255, 255, 255));
            //_sharpGltfService.AddMesh(modelTest, "Text", triangulation);

            //// Save model
            //modelTest.SaveGLB(Path.Combine(outputDir, modelName + ".glb"));



            var modelAndBbox = GenerateSampleModel(bbox, dataset, withTexture: true);

            if (modelAndBbox.Model != null)
            {
                var model = modelAndBbox.Model;

                // add adornments
                Stopwatch swAdornments = Stopwatch.StartNew();
                TriangulationList <Vector3> adornments = _adornmentsService.CreateModelAdornments(dataset, ImageryProvider.MapBoxSatellite, bbox, modelAndBbox.projectedBbox);
                model = _sharpGltfService.AddMesh(model, "Adornments", adornments, default(Vector4), doubleSided: true);
                swAdornments.Stop();

                _logger.LogInformation($"Adornments generation: {swAdornments.ElapsedMilliseconds:N1} ms");

                // Save model
                model.SaveGLB(Path.Combine(outputDir, modelName + ".glb"));

                _logger.LogInformation($"Model exported as {Path.Combine(outputDir, modelName + ".glb")}");
            }
        }
        private TriangulationList <GeoPoint> TriangulateRingsWalls(TriangulationList <GeoPoint> triangulation, List <int> numVerticesPerRing, int totalPoints)
        {
            int offset = numVerticesPerRing.Sum();

            Debug.Assert(totalPoints == offset);

            int ringOffset = 0;

            foreach (var numRingVertices in numVerticesPerRing)
            {
                int i = 0;
                do
                {
                    triangulation.Indices.Add(ringOffset + i);
                    triangulation.Indices.Add(ringOffset + i + offset);
                    triangulation.Indices.Add(ringOffset + i + 1);

                    triangulation.Indices.Add(ringOffset + i + offset);
                    triangulation.Indices.Add(ringOffset + i + offset + 1);
                    triangulation.Indices.Add(ringOffset + i + 1);

                    i++;
                }while (i < numRingVertices - 1);

                // Connect last vertices to start vertices
                triangulation.Indices.Add(ringOffset + i);
                triangulation.Indices.Add(ringOffset + i + offset);
                triangulation.Indices.Add(ringOffset + 0);

                triangulation.Indices.Add(ringOffset + i + offset);
                triangulation.Indices.Add(ringOffset + 0 + offset);
                triangulation.Indices.Add(ringOffset + 0);

                ringOffset += numRingVertices;
            }
            return(triangulation);
        }
        private TriangulationList <Vector3> GraphTraversal_Triangulation(VisualTopoModel visualTopoModel, TriangulationList <Vector3> triangulation, ref TriangulationList <Vector3> markersTriangulation, Node <VisualTopoData> node, IColorCalculator colorFunc)
        {
            triangulation = triangulation ?? new TriangulationList <Vector3>();

            var model = node.Model;

            if (model.IsSectionStart && triangulation.NumPositions > 0)
            {
                // Cylinder height = point depth + (terrain height above - entry Z)
                float cylinderHeight = -model.VectorLocal.Z + (float)(model.TerrainElevationAbove - visualTopoModel.EntryPoint.Elevation.Value);
                markersTriangulation += _meshService.CreateCylinder(model.VectorLocal, 0.2f, cylinderHeight, model.Set.Color);

                //var surfacePos = new Vector3(model.GlobalVector.X, model.GlobalVector.Y, (float)model.TerrainElevationAbove);
                float coneHeight = 10;
                markersTriangulation += _meshService.CreateCone(model.VectorLocal, 5, coneHeight, model.Set.Color)
                                        //.Translate(Vector3.UnitZ * -coneHeight / 2F)
                                        .Transform(Matrix4x4.CreateRotationY((float)Math.PI, new Vector3(model.VectorLocal.X, model.VectorLocal.Y, model.VectorLocal.Z + coneHeight / 2f)))
                                        //.Translate(Vector3.UnitZ * coneHeight / 2F)
                                        .Translate(Vector3.UnitZ * cylinderHeight);
            }

            if (node.Arcs.Count == 0) // leaf
            {
                Debug.Assert(triangulation.NumPositions > 0, "Triangulation should not be empty");

                // Make a rectangle perpendicual to direction centered on point(should be centered at human eye(y = 2m)
                triangulation = AddCorridorRectangleSection(triangulation, model, null, triangulation.NumPositions - 4, colorFunc);
            }
            else
            {
                int posIndex = triangulation.NumPositions - 4;
                foreach (var arc in node.Arcs)
                {
                    // Make a rectangle perpendicual to direction centered on point(should be centered at human eye(y = 2m)
                    triangulation = AddCorridorRectangleSection(triangulation, model, arc.Child.Model, posIndex, colorFunc);
                    posIndex      = triangulation.NumPositions - 4;

                    triangulation = GraphTraversal_Triangulation(visualTopoModel, triangulation, ref markersTriangulation, arc.Child, colorFunc);
                }
            }

            return(triangulation);
        }
        public TriangulationList <GeoPoint> Triangulate(BuildingModel building)
        {
            //==========================
            // Footprint triangulation
            //
            var footPrintOutline             = building.ExteriorRing.Skip(1); // In GeoJson, ring last point == first point, we must filter the first point out
            var footPrintInnerRingsFlattened = building.InteriorRings == null ? null : building.InteriorRings.Select(r => r.Skip(1));

            TriangulationList <GeoPoint> triangulation = _meshService.Tesselate(footPrintOutline, footPrintInnerRingsFlattened);
            int numFootPrintIndices = triangulation.Indices.Count;

            /////

            // Now extrude it (build the sides)
            // Algo
            // First triangulate the foot print (with inner rings if existing)
            // This triangulation is the roof top if building is flat
            building = this.ComputeBuildingHeightMeters(building);

            int totalPoints = building.ExteriorRing.Count - 1 + building.InteriorRings.Sum(r => r.Count - 1);

            // Triangulate wall for each ring
            // (We add floor indices before copying the vertices, they will be duplicated and z shifted later on)
            List <int> numVerticesPerRing = new List <int>();

            numVerticesPerRing.Add(building.ExteriorRing.Count - 1);
            numVerticesPerRing.AddRange(building.InteriorRings.Select(r => r.Count - 1));
            triangulation = this.TriangulateRingsWalls(triangulation, numVerticesPerRing, totalPoints);

            // Roof
            // Building has real elevations

            // Create floor vertices by copying roof vertices and setting their z min elevation (floor or min height)
            var floorVertices = triangulation.Positions.Select(pt => pt.Clone(building.ComputedFloorAltitude)).ToList();

            triangulation.Positions.AddRange(floorVertices);

            // Take the first vertices and z shift them
            foreach (var pt in triangulation.Positions.Take(totalPoints))
            {
                pt.Elevation = building.ComputedRoofAltitude;
            }

            //==========================
            // Colors: if walls and roof color is the same, all vertices can have the same color
            // otherwise we must duplicate vertices to ensure consistent triangles color (avoid unrealistic shades)
            // AND shift the roof triangulation indices
            // Before:
            //      Vertices: <roof_wallcolor_0..i> / <floor_wallcolor_i..j>
            //      Indices: <roof_triangulation_0..i> / <roof_wall_triangulation_0..j>
            // After:
            //      Vertices: <roof_wallcolor_0..i> / <floor_wallcolor_i..j> // <roof_roofcolor_j..k>
            //      Indices: <roof_triangulation_j..k> / <roof_wall_triangulation_0..j>
            Vector4 DefaultColor            = Vector4.One;
            bool    mustCopyVerticesForRoof = (building.Color ?? DefaultColor) != (building.RoofColor ?? building.Color);

            // assign wall or default color to all vertices
            triangulation.Colors = triangulation.Positions.Select(p => building.Color ?? DefaultColor).ToList();

            if (mustCopyVerticesForRoof)
            {
                triangulation.Positions.AddRange(triangulation.Positions.Take(totalPoints));
                triangulation.Colors.AddRange(Enumerable.Range(1, totalPoints).Select(_ => building.RoofColor ?? DefaultColor));

                // shift roof triangulation indices
                for (int i = 0; i < numFootPrintIndices; i++)
                {
                    triangulation.Indices[i] += (triangulation.Positions.Count - totalPoints);
                }
            }

            Debug.Assert(triangulation.Colors.Count == 0 || triangulation.Colors.Count == triangulation.Positions.Count);

            return(triangulation);
        }