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; }
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); }
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); }