public static GameObject CombineGameObjectsOfTheSameMaterial(this List <GameObject> GameObjects) { if (GameObjects.Count == 1) { return(GameObjects[0]); } List <Vector3> CombinedVertices = new List <Vector3>(); List <int> CombinedTris = new List <int>(); List <Vector2> CombinedUVs = new List <Vector2>(); foreach (var go in GameObjects) { var vertLength = CombinedVertices.Count; CombinedVertices.AddRange(go.mesh.vertices.Select(v => v + go.position)); CombinedTris.AddRange(go.mesh.triangles.Select(v => v + vertLength)); CombinedUVs.AddRange(go.mesh.uv); } GameObject CombinedObject = new GameObject(GameObjects[0].Name); CombinedObject.material = GameObjects[0].material; CombinedObject.tag = GameObjects[0].tag; CombinedObject.type = GameObjects[0].type; CombinedObject.position = CoordinateConvertor.ChangePivot(ref CombinedVertices, CoordinateConvertor.PivotLocation.ZeroBottomCenter); CombinedObject.mesh = Rebuild(CombinedVertices.ToArray(), CombinedTris.ToArray(), CombinedUVs.ToArray()); return(CombinedObject); }
public void RecalculateNormals() { normals = new Vector3[vertices.Length]; for (int i = 0; i < vertices.Length - 2; i += 3) { var temp = CoordinateConvertor.CalculateNormal( vertices[triangles[i]], vertices[triangles[i + 1]], vertices[triangles[i + 2]] ); normals[i] = temp; normals[i + 1] = temp; normals[i + 2] = temp; } }
public static float Angle(Vector2 from, Vector2 to) { return((float)(Math.Acos(CoordinateConvertor.Clamp(Dot(from.normalized, to.normalized), -1f, 1f)) * 57.29578f)); }
public static GameObject GenerateShapeUVedWithWalls_Balanced(this Poly2Tri.Polygon shape, OSMType type, string Id, string other, string tag, string MaterialName = "Building", float WallHeight = 5, float MaximumHeight = 12, bool GenerateColliders = true) { GameObject gameObject = new GameObject(type + "|" + ((int)type) + "|" + Id + (!string.IsNullOrEmpty(other) ? "|" + other : "")); gameObject.tag = tag; if (shape.Triangles == null) { return(gameObject); } if (shape.Triangles.Count == 0) { return(gameObject); } List <Vector3> vertices = new List <Vector3>(); for (int i = 0; i < shape.Triangles.Count; i++) { vertices.AddRange(shape.Triangles[i].Points.Select(s => new Vector3((float)s.X, WallHeight, (float)s.Y)).Reverse()); } // This is the starting index in the vertex list that belongs to the walls. // We use this to distinguish between roof UVs and wall UVs. int WallStartIndex = vertices.Count; List <Vector2> UVs = new List <Vector2>(); var minXRoof = vertices.GetRange(0, WallStartIndex).Select(i => i.x).Min(); var minZRoof = vertices.GetRange(0, WallStartIndex).Select(i => i.z).Min(); var maxXRoof = vertices.GetRange(0, WallStartIndex).Select(i => i.x).Max(); var maxZRoof = vertices.GetRange(0, WallStartIndex).Select(i => i.z).Max(); // for (int i = 0; i < UVs.Length; i++) for (int i = 0; i < WallStartIndex; i++) { UVs.Add(new Vector2( linear(vertices[i].x, minXRoof, maxXRoof, 0, 1), linear(vertices[i].z, minZRoof, maxZRoof, 0, 0.5f) )); } Vector2[] UVRef = new Vector2[] { new Vector2(0, 0.5f), // top left new Vector2(1, 0.5f), // top right new Vector2(0, 0.5f + 0.5f * WallHeight / MaximumHeight), // bottom left new Vector2(1, 0.5f + 0.5f * WallHeight / MaximumHeight) // bottom right // new Vector2(0,1), // bottom left // new Vector2(1,1) // bottom right }; #region Outer wall Vector3 HeightVector = new Vector3(0, WallHeight, 0); Vector3[] OriginalVertices = shape.Points.Select(s => new Vector3((float)s.X, WallHeight, (float)s.Y)).ToArray(); // Generating Walls - Total: (OriginalVertices.Length)*4*3 for (int i = 0; i < OriginalVertices.Length - 1; i++) { Vector3 v1 = OriginalVertices[i]; Vector3 v2 = OriginalVertices[i + 1]; var dist = Vector3.Distance(v1, v2); //Debug.Log("Distance: "+dist); dist = dist.Clamp(0, 5); var UVratioTopRight = new Vector2(dist / 5, UVRef[1].y); var UVratioBottomRight = new Vector2(dist / 5, UVRef[3].y); Vector3 v3 = OriginalVertices[i] - HeightVector; Vector3 v4 = OriginalVertices[i + 1] - HeightVector; vertices.AddRange(new Vector3[] { v1, v2, v3 }); UVs.Add(UVRef[0]); UVs.Add(UVratioTopRight); //UVs.Add(UVRef[1]); UVs.Add(UVRef[2]); vertices.AddRange(new Vector3[] { v3, v2, v4 }); UVs.Add(UVRef[2]); UVs.Add(UVratioTopRight); //UVs.Add(UVRef[1]); UVs.Add(UVratioBottomRight); //UVs.Add(UVRef[3]); // Reverse vertices.AddRange(new Vector3[] { v1, v3, v2 }); UVs.Add(UVRef[0]); UVs.Add(UVRef[2]); UVs.Add(UVratioTopRight); //UVs.Add(UVRef[1]); vertices.AddRange(new Vector3[] { v3, v4, v2 }); UVs.Add(UVRef[2]); UVs.Add(UVratioBottomRight); //UVs.Add(UVRef[3]); UVs.Add(UVratioTopRight); //UVs.Add(UVRef[1]); } // Last Wall - Total: 4*3 Vector3 lv1 = OriginalVertices[OriginalVertices.Length - 1]; Vector3 lv2 = OriginalVertices[0]; var distlw = Vector3.Distance(lv1, lv2); //Debug.Log("Distance: "+distlw); distlw = distlw.Clamp(0, 5); var UVratioTopRightlw = new Vector2(distlw / 5, UVRef[1].y); var UVratioBottomRightlw = new Vector2(distlw / 5, UVRef[3].y); Vector3 lv3 = OriginalVertices[OriginalVertices.Length - 1] - HeightVector; Vector3 lv4 = OriginalVertices[0] - HeightVector; vertices.AddRange(new Vector3[] { lv1, lv2, lv3 }); UVs.Add(UVRef[0]); UVs.Add(UVratioTopRightlw); //UVs.Add(UVRef[1]); UVs.Add(UVRef[2]); vertices.AddRange(new Vector3[] { lv3, lv2, lv4 }); UVs.Add(UVRef[2]); UVs.Add(UVratioTopRightlw); //UVs.Add(UVRef[1]); UVs.Add(UVratioBottomRightlw); //UVs.Add(UVRef[3]); // Reverse vertices.AddRange(new Vector3[] { lv1, lv3, lv2 }); UVs.Add(UVRef[0]); UVs.Add(UVRef[2]); UVs.Add(UVratioTopRightlw); //UVs.Add(UVRef[1]); vertices.AddRange(new Vector3[] { lv3, lv4, lv2 }); UVs.Add(UVRef[2]); UVs.Add(UVratioBottomRightlw); //UVs.Add(UVRef[3]); UVs.Add(UVratioTopRightlw); //UVs.Add(UVRef[1]); #endregion #region Inner walls List <Vector3[]> OriginalInnerVertices = new List <Vector3[]>(); if (shape.Holes != null) { for (int i = 0; i < shape.Holes.Count; i++) { OriginalInnerVertices.Add(shape.Holes[i].Points.Select(s => new Vector3((float)s.X, WallHeight, (float)s.Y)).ToArray()); } for (int holeidx = 0; holeidx < shape.Holes.Count; holeidx++) { // Generating Walls - Total: (OriginalInnerVertices.Length)*4*3 for (int i = 0; i < OriginalInnerVertices[holeidx].Length - 1; i++) { Vector3 v1 = OriginalInnerVertices[holeidx][i]; Vector3 v2 = OriginalInnerVertices[holeidx][i + 1]; var dist = Vector3.Distance(v1, v2); //Debug.Log("Distance: "+dist); dist = dist.Clamp(0, 5); var UVratioTopRight = new Vector2(dist / 5, UVRef[1].y); var UVratioBottomRight = new Vector2(dist / 5, UVRef[3].y); Vector3 v3 = OriginalInnerVertices[holeidx][i] - HeightVector; Vector3 v4 = OriginalInnerVertices[holeidx][i + 1] - HeightVector; vertices.AddRange(new Vector3[] { v1, v2, v3 }); UVs.Add(UVRef[0]); UVs.Add(UVratioTopRight); //UVs.Add(UVRef[1]); UVs.Add(UVRef[2]); vertices.AddRange(new Vector3[] { v3, v2, v4 }); UVs.Add(UVRef[2]); UVs.Add(UVratioTopRight); //UVs.Add(UVRef[1]); UVs.Add(UVratioBottomRight); //UVs.Add(UVRef[3]); // Reverse vertices.AddRange(new Vector3[] { v1, v3, v2 }); UVs.Add(UVRef[0]); UVs.Add(UVRef[2]); UVs.Add(UVratioTopRight); //UVs.Add(UVRef[1]); vertices.AddRange(new Vector3[] { v3, v4, v2 }); UVs.Add(UVRef[2]); UVs.Add(UVratioBottomRight); //UVs.Add(UVRef[3]); UVs.Add(UVratioTopRight); //UVs.Add(UVRef[1]); } // Last Wall - Total: 4*3 Vector3 lv1_hole = OriginalInnerVertices[holeidx][OriginalInnerVertices[holeidx].Length - 1]; Vector3 lv2_hole = OriginalInnerVertices[holeidx][0]; var distlw_hole = Vector3.Distance(lv1_hole, lv2_hole); //Debug.Log("Distance: "+distlw_hole); distlw_hole = distlw_hole.Clamp(0, 5); var UVratioTopRightlw_hole = new Vector2(distlw_hole / 5, UVRef[1].y); var UVratioBottomRightlw_hole = new Vector2(distlw_hole / 5, UVRef[3].y); Vector3 lv3_hole = OriginalInnerVertices[holeidx][OriginalInnerVertices[holeidx].Length - 1] - HeightVector; Vector3 lv4_hole = OriginalInnerVertices[holeidx][0] - HeightVector; vertices.AddRange(new Vector3[] { lv1_hole, lv2_hole, lv3_hole }); UVs.Add(UVRef[0]); UVs.Add(UVratioTopRightlw_hole); //UVs.Add(UVRef[1]); UVs.Add(UVRef[2]); vertices.AddRange(new Vector3[] { lv3_hole, lv2_hole, lv4_hole }); UVs.Add(UVRef[2]); UVs.Add(UVratioTopRightlw_hole); //UVs.Add(UVRef[1]); UVs.Add(UVratioBottomRightlw_hole); //UVs.Add(UVRef[3]); // Reverse vertices.AddRange(new Vector3[] { lv1_hole, lv3_hole, lv2_hole }); UVs.Add(UVRef[0]); UVs.Add(UVRef[2]); UVs.Add(UVratioTopRightlw_hole); //UVs.Add(UVRef[1]); vertices.AddRange(new Vector3[] { lv3_hole, lv4_hole, lv2_hole }); UVs.Add(UVRef[2]); UVs.Add(UVratioBottomRightlw_hole); //UVs.Add(UVRef[3]); UVs.Add(UVratioTopRightlw_hole); //UVs.Add(UVRef[1]); } } #endregion // int[] tris = new int[shape.Triangles.Count * 3 + (OriginalVertices.Length - 1) * 4 * 3 + 4 * 3]; int innerTriCount = 0; for (int i = 0; i < OriginalInnerVertices.Count; i++) { innerTriCount += (OriginalInnerVertices[i].Length - 1) * 4 * 3 + 4 * 3; } int[] tris = new int[shape.Triangles.Count * 3 + (OriginalVertices.Length - 1) * 4 * 3 + 4 * 3 + innerTriCount]; for (int i = 0; i < tris.Length; i++) { tris[i] = i; } gameObject.position = CoordinateConvertor.ChangePivot(ref vertices, CoordinateConvertor.PivotLocation.ZeroBottomCenter); gameObject.mesh = Rebuild(vertices.ToArray(), tris, UVs.ToArray()); gameObject.material = new Material(MaterialName); return(gameObject); }
public static GameObject GenerateShapeUVedPlanar_Balanced(this PolygonCuttingEar.CPolygonShape shape, OSMType type, string Id, string other, string tag, string MaterialName = "Area", float Height = 0, bool GenerateColliders = true) { GameObject gameObject = new GameObject(type + "|" + ((int)type) + "|" + Id + (!string.IsNullOrEmpty(other) ? "|" + other : "")); gameObject.tag = tag; List <Vector3> vertices = new List <Vector3>(); for (int i = 0; i < shape.NumberOfPolygons; i++) { vertices.AddRange(shape.Polygons(i).ToVector3(Height)); } // This is the starting index in the vertex list that belongs to the walls. // We use this to distinguish between roof UVs and wall UVs. int WallStartIndex = vertices.Count; List <Vector2> UVs = new List <Vector2>(); var minXRoof = vertices.GetRange(0, WallStartIndex).Select(i => i.x).Min(); var minZRoof = vertices.GetRange(0, WallStartIndex).Select(i => i.z).Min(); var maxXRoof = vertices.GetRange(0, WallStartIndex).Select(i => i.x).Max(); var maxZRoof = vertices.GetRange(0, WallStartIndex).Select(i => i.z).Max(); var average = new Vector3( vertices.GetRange(0, WallStartIndex).Select(s => s.x).Average(), vertices.GetRange(0, WallStartIndex).Select(s => s.y).Average(), vertices.GetRange(0, WallStartIndex).Select(s => s.z).Average()); //var avgx = linear(average.x, minXRoof, maxXRoof, 0, 1); //var avgy = linear(average.z, minZRoof, maxZRoof, 0, 1); //Vector3[] OriginalVertices = shape.InputVertices.ToVector3(Height); for (int i = 0; i < WallStartIndex; i++) { // bool found = false; var x = linear(vertices[i].x, minXRoof, maxXRoof, 0, 1); var y = linear(vertices[i].z, minZRoof, maxZRoof, 0, 1); //for (int j = 0; j < OriginalVertices.Length; j++) //{ // if (Math.Abs(OriginalVertices[j].x - vertices[i].x) < 0.001f && Math.Abs(OriginalVertices[j].z - vertices[i].z) < 0.001f) // { // //if (j % 2 == 0) // // UVs.Add(new Vector2(0, 0)); // //else // // UVs.Add(new Vector2(1, 0)); // if (x > avgx && Math.Abs(x - avgx) < Math.Abs(y - avgy)) // x = 1; // else if (x <= avgx && Math.Abs(avgx - x) < Math.Abs(y - avgy)) // x = 0; // if (y > avgy && Math.Abs(y - avgy) < Math.Abs(x - avgx)) // y = 1; // else if (y <= avgy && Math.Abs(avgy - y) < Math.Abs(x - avgx)) // y = 0; // UVs.Add(new Vector2(x, y)); // found = true; // break; // } //} //if (!found) //{ // var x = linear(vertices[i].x, minXRoof, maxXRoof, 0, 1); // var y = linear(vertices[i].z, minZRoof, maxZRoof, 0, 1); UVs.Add(new Vector2(x, y)); //} } Vector2[] UVRef = new Vector2[] { new Vector2(0, 0), // top left new Vector2(1, 0), // top right new Vector2(0, 1), // bottom left new Vector2(1, 1) // bottom right // new Vector2(0,1), // bottom left // new Vector2(1,1) // bottom right }; Vector3 HeightVector = new Vector3(0, 0, 0); int[] tris = new int[shape.NumberOfPolygons * 3]; for (int i = 0; i < tris.Length; i++) { tris[i] = i; } gameObject.position = CoordinateConvertor.ChangePivot(ref vertices, CoordinateConvertor.PivotLocation.ZeroBottomCenter); gameObject.mesh = Rebuild(vertices.ToArray(), tris, UVs.ToArray()); gameObject.material = new Material(MaterialName); return(gameObject); }
/// <summary> /// Generates the mesh for a given openstreetmap element /// </summary> /// <param name="OSMid">The id of the OSM element</param> /// <param name="MinPointOnMap">Minimum point on the target map (to calculate the offset)</param> /// <param name="bounds">The boundaries of the used OSM map</param> /// <param name="properties">The details of the target scene generation.</param> /// <param name="connPostGreSql">The connection string to the database. Please refer to .Net Data Provider for MSSQL for format specifications</param> /// <param name="shape">The type of the generated mesh. Refer to <see cref="GaPSLabs.Geometry.MeshGenerator.OSMShape"/></param> /// <param name="RemoveRedundantPointsOnTheSameLine">If true, it will remove redundant points on straight parts of the shape according to<paramref name="RedundantPointErrorThreshold"/>.</param> /// <param name="RedundantPointErrorThreshold">The error toleration when removing redundant points. The default is 0.001f</param> /// <param name="SegmentLines">If true, it will segment the road geometry so that the texture will look uniform over the whole mesh.</param> /// <returns>Returns a <see cref="GaPSLabs.Geometry.GameObject"/> of the generated mesh.</returns> /// <remarks> /// <para>Removing the redundant points happen before the line segmentation. Therefore the points that were generated when setting <paramref name="SegmentLines"/> to true will not be removed.</para> /// <para>The <paramref name="RedundantPointErrorThreshold"/> specifies the difference in slope of the line segments that can be ignored.</para> /// </remarks> public static GameObject OSMtoGameObject(string OSMid, Vector3 MinPointOnMap, Bounds bounds, MapProperties properties, string connPostGreSql, OSMShape shape = OSMShape.Way, bool RemoveRedundantPointsOnTheSameLine = true, float RedundantPointErrorThreshold = 0.001f, bool SegmentLines = true) { if (rand == null) { rand = new Random((int)DateTime.Now.Ticks); } if (properties == null) { properties = Properties; } var minmaxX = properties.minMaxX; var minmaxY = properties.minMaxY; int direction = -1; float LineWidth = properties.RoadLineThickness; float BuildingWidth = properties.BuildingLineThickness; float height = properties.BuildingHeight; if (height < 4) { height = 4; } var FirstWay = OSMid; if (shape == OSMShape.Way) { if (FirstWay.Equals("41018641")) { System.Console.WriteLine("Missing road found in WCF"); } var w = new Way(FirstWay); List <OsmNode> WayNodes; List <Tag> WayTags; using (Npgsql.NpgsqlConnection con = new Npgsql.NpgsqlConnection(connPostGreSql)) { con.Open(); WayNodes = w.GetNodesPostgreSQL(FirstWay, con); WayTags = w.GetTagsPostgreSQL(FirstWay, con); con.Close(); } if (WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "landuse" || i.KeyValueSQL[0].ToLower() == "building" || i.KeyValueSQL[0].ToLower() == "highway").Count() != 0) { var tempPoints = new Vector3[WayNodes.Count]; int counter = 0; foreach (var node in WayNodes) { var result = CoordinateConvertor.SimpleInterpolation((float)node.PositionSQL.Lat, (float)node.PositionSQL.Lon, bounds, minmaxX, minmaxY); // Testing the correct direction tempPoints[counter] = new Vector3(direction * (float)result[0], 0, (float)result[1]) - MinPointOnMap; counter++; } if (RemoveRedundantPointsOnTheSameLine) { tempPoints = tempPoints.RemoveRedundantPointsInTheSameLines(); } WayNodes = null; var building = WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "building"); var highwayType = WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "highway"); var onewayType = WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "oneway").FirstOrDefault(); var lanesType = WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "lanes").FirstOrDefault(); var areaType = WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "area").FirstOrDefault(); WayTags = null; if (building.Count() != 0) { #region Buildings // Check if it has overlapping start and ending points. // NOTE: Replaced with the code to remove all the duplicates, not only the endpoints. // Checking for duplicates: tempPoints = tempPoints.ToArray().RemoveDuplicates(); var Skip = false; if (tempPoints.Length <= 2) { // Buildings that are too small to show such as 76844368 // http://www.openstreetmap.org/browse/way/76844368 // "A weird building were found and ignored. FirstWay \nRelated url: http://www.openstreetmap.org/browse/way/{0}" Skip = true; // continue; } if (!Skip) { var p2d = tempPoints.ToCPoint2D(); // TODO bug in the cpolygon, probably duplicates var shp = new PolygonCuttingEar.CPolygonShape(p2d); shp.CutEar(); p2d = null; GC.Collect(); // TODO: //var randHeight = CoordinateConvertor.linear((float)rand.NextDouble(), 0, 1, -3f, height); // var randMaterial = (randHeight > height / 2f) ? "BuildingTall" : randHeight < height / 2f ? "Building2" : "Building"; var randHeight = properties.BuildingHeightVariation.GetNextCircular(); var randMaterial = (randHeight > (properties.BuildingHeightVariation.Average + properties.BuildingHeightVariation.Max) / 2f) ? "BuildingTall" : ("Building" + rand.Next(1, 5)); var resultedGameObject = shp.GenerateShapeUVedWithWalls_Balanced( CoordinateConvertor.OSMType.Polygon, FirstWay, "Building", "Building", randMaterial, /*height +*/ randHeight, /* height + 7*/ properties.BuildingHeightVariation.Max, true); return(resultedGameObject); } #endregion } else { if (highwayType.Count() != 0) { #region Roads var hwtype = highwayType.First(); //Console.WriteLine("Generating roads..id=" + FirstWay); switch (hwtype.KeyValueSQL[1]) { case "cycleway": { var resultedGameObject = CoordinateConvertor.MeshGenerationFilledCorners(SegmentLines ? tempPoints.ToSegmentedPoints(2f) : tempPoints, properties.CyclewayWidth, CoordinateConvertor.OSMType.Line, FirstWay, hwtype.KeyValueSQL[1], "Line", properties.CycleWayMaterial.Name, -0.01f); // ObjFormat.MeshToFile(resultedGameObject,System.IO.Path.Combine( exportPath , resultedGameObject.Name.Replace("|", "-") + ".obj")); return(resultedGameObject); } case "footway": case "path": case "pedestrian": { if (areaType != null) { tempPoints = tempPoints.ToArray().RemoveDuplicates(); var Skip = false; if (tempPoints.Length <= 2) { // Buildings that are too small to show such as 76844368 // http://www.openstreetmap.org/browse/way/76844368 // "A weird building were found and ignored. FirstWay \nRelated url: http://www.openstreetmap.org/browse/way/{0}" Skip = true; // continue; } if (!Skip) { var p2d = tempPoints.ToCPoint2D(); // TODO bug in the cpolygon, probably duplicates var shp = new PolygonCuttingEar.CPolygonShape(p2d); shp.CutEar(); p2d = null; GC.Collect(); var resultedGameObject = shp.GenerateShapeUVedPlanar_Balanced( CoordinateConvertor.OSMType.Polygon, FirstWay, "FootwayArea", "Area", properties.AreaMaterial.Name, 0, true); return(resultedGameObject); } break; } else { var resultedGameObject = CoordinateConvertor.MeshGenerationFilledCorners(SegmentLines ? tempPoints.ToSegmentedPoints(4f) : tempPoints, properties.FootwayWidth, CoordinateConvertor.OSMType.Line, FirstWay, hwtype.KeyValueSQL[1], "Line", properties.FootWayMaterial.Name, -0.01f); // ObjFormat.MeshToFile(resultedGameObject,System.IO.Path.Combine( exportPath , resultedGameObject.Name.Replace("|", "-") + ".obj")); return(resultedGameObject); } } case "steps": { var resultedGameObject = CoordinateConvertor.MeshGenerationFilledCorners(SegmentLines ? tempPoints.ToSegmentedPoints(4f) : tempPoints, properties.CyclewayWidth, CoordinateConvertor.OSMType.Line, FirstWay, hwtype.KeyValueSQL[1], "Line", properties.StepsMaterial.Name, -0.01f); return(resultedGameObject); } // Miguel R.C. commented these lines. // Reason: Why breaking in the case "motorway"? That results in some roads missing when generating scenarios. // With these lines commented, the motorways will be generated in the default case. //case "motorway": // { // break; // } default: { // Calculating the corrent road width based on the available information // Parameters affecting the width are: // 1- road direction: 'oneway' tag // 2- number of lanes: 'lanes' tag. The defaults are: http://wiki.openstreetmap.org/wiki/Lane#Assumptions // // |If two-way then # of lanes is 2, and if one-way then # of lanes is 1| // highway=residential // highway=tertiary // highway=secondary // highway=primary // // |If two-way then # of lanes is 1, and if one-way then # of lanes is 1| // highway=service // highway=track // highway=path // The actual number of lanes for the following must always be tagged in the lanes value. // highway=motorway // highway=trunk //TODO: USE lanes value. var calculatedRoadWidth = properties.RoadWidth; bool useLaneInfo = false; if (lanesType != null) { var laneNumbers = lanesType.KeyValueSQL[1].Split(new char[] { '+' }, StringSplitOptions.RemoveEmptyEntries); float lanes = 0; foreach (var lane in laneNumbers) { float templane; if (float.TryParse(lane, out templane)) { lanes += templane; } } if (lanes != 0) { useLaneInfo = true; calculatedRoadWidth = properties.RoadWidth * lanes; } } if (!useLaneInfo) { if (onewayType == null) // It is a 2-way by default { if (hwtype.KeyValueSQL[1] == Tags.Highways.residential || hwtype.KeyValueSQL[1] == Tags.Highways.tertiary || hwtype.KeyValueSQL[1] == Tags.Highways.secondary || hwtype.KeyValueSQL[1] == Tags.Highways.primary) { calculatedRoadWidth = properties.RoadWidth * 2; } else if (hwtype.KeyValueSQL[1] == Tags.Highways.service || hwtype.KeyValueSQL[1] == Tags.Highways.track || hwtype.KeyValueSQL[1] == Tags.Highways.path) { calculatedRoadWidth = properties.RoadWidth; } } else if (onewayType.KeyValueSQL[1].ToLower() == "0" | onewayType.KeyValueSQL[1].ToLower() == "no") // It is still a 2-way { if (hwtype.KeyValueSQL[1] == Tags.Highways.residential || hwtype.KeyValueSQL[1] == Tags.Highways.tertiary || hwtype.KeyValueSQL[1] == Tags.Highways.secondary || hwtype.KeyValueSQL[1] == Tags.Highways.primary) { calculatedRoadWidth = properties.RoadWidth * 2; } else if (hwtype.KeyValueSQL[1] == Tags.Highways.service || hwtype.KeyValueSQL[1] == Tags.Highways.track || hwtype.KeyValueSQL[1] == Tags.Highways.path) { calculatedRoadWidth = properties.RoadWidth; } } else // It is a one-way { calculatedRoadWidth = properties.RoadWidth; } } var matName = properties.RoadMaterial.Name; if (hwtype.KeyValueSQL[1] == Tags.Highways.residential) { matName = properties.ResidentialMaterial.Name; } else if (hwtype.KeyValueSQL[1] == Tags.Highways.service) { matName = properties.ServiceMaterial.Name; } var resultedGameObject = CoordinateConvertor.MeshGenerationFilledCorners(SegmentLines ? tempPoints.ToSegmentedPoints(0.5f) : tempPoints, calculatedRoadWidth, CoordinateConvertor.OSMType.Line, FirstWay, hwtype.KeyValueSQL[1], "Line", matName, 0f); return(resultedGameObject); } } #endregion } } } } else if (shape == OSMShape.Relation) // It is probably a multi-polygon building defined in a relation. { List <OsmNode[]> OuterNodes; //List<OsmNode> OuterWay; List <OsmNode[]> InnerNodes; List <Tag> tags; Relation r = new Relation(FirstWay); Way w = new Way(); using (Npgsql.NpgsqlConnection con = new Npgsql.NpgsqlConnection(connPostGreSql)) { con.Open(); tags = r.GetTagsPostgreSQL(con); var isMultiPolygon = tags.Exists(t => t.KeyValueSQL[0] == "type" && t.KeyValueSQL[1] == "multipolygon"); if (isMultiPolygon) { var Members = r.GetMembersPostgreSQLByType(r.id, 1, con); OuterNodes = Members .Where(m => m.Role.ToLower() == Member.RoleOuter) .OrderBy(o => o.order) .Select(m => w.GetNodesPostgreSQL(m.ReferenceId, connPostGreSql).ToArray()).ToList(); InnerNodes = Members .Where(m => m.Role.ToLower() == Member.RoleInner) .OrderBy(o => o.order) .Select(m => w.GetNodesPostgreSQL(m.ReferenceId, connPostGreSql).ToArray()).ToList(); //OuterWay = new List<OsmNode>(); //foreach (var nodelist in OuterNodes) // OuterWay.AddRange(nodelist); List <Vector3[]> tempPointsOuterPoints = new List <Vector3[]>(); int counter = 0; foreach (var node in OuterNodes) { Vector3[] currrentOuter = new Vector3[node.Length]; for (int i = 0; i < node.Length; i++) { var result = CoordinateConvertor.SimpleInterpolation((float)node[i].PositionSQL.Lat, (float)node[i].PositionSQL.Lon, bounds, minmaxX, minmaxY); // Testing the correct direction currrentOuter[i] = new Vector3(direction * (float)result[0], 0, (float)result[1]) - MinPointOnMap; } tempPointsOuterPoints.Add(currrentOuter.RemoveDuplicates()); } List <Vector3[]> holes = new List <Vector3[]>(); for (int i = 0; i < InnerNodes.Count; i++) { holes.Add(new Vector3[InnerNodes[i].Length]); for (int j = 0; j < InnerNodes[i].Length; j++) { var result = CoordinateConvertor.SimpleInterpolation((float)InnerNodes[i][j].PositionSQL.Lat, (float)InnerNodes[i][j].PositionSQL.Lon, bounds, minmaxX, minmaxY); // Testing the correct direction holes[i][j] = new Vector3(direction * (float)result[0], 0, (float)result[1]) - MinPointOnMap; } holes[i] = holes[i].RemoveDuplicates(); } if (RemoveRedundantPointsOnTheSameLine) { for (int i = 0; i < tempPointsOuterPoints.Count; i++) { tempPointsOuterPoints[i] = tempPointsOuterPoints[i].RemoveRedundantPointsInTheSameLines(); } for (int i = 0; i < holes.Count; i++) { holes[i] = holes[i].RemoveRedundantPointsInTheSameLines(); } } // Check the holes against all outers List <IEnumerable <Poly2Tri.PolygonPoint> > outerpolypoints = new List <IEnumerable <Poly2Tri.PolygonPoint> >(); for (int i = 0; i < tempPointsOuterPoints.Count; i++) { outerpolypoints.Add(tempPointsOuterPoints[i].Select(s => new Poly2Tri.PolygonPoint(s.x, s.z))); } //= tempPointsOuterPoints var Polies = outerpolypoints.Select(opp => new Poly2Tri.Polygon(opp)).ToArray(); //Poly2Tri.Polygon poly = new Poly2Tri.Polygon(outerpolypoints); // TODO: poly.IsPointInside() check the holes against all pieces var innerspolypoints = new List <Poly2Tri.PolygonPoint[]>(); for (int i = 0; i < holes.Count; i++) { var currentInner = holes[i].Select(s => new Poly2Tri.PolygonPoint(s.x, s.z)).ToArray(); innerspolypoints.Add(currentInner); for (int j = 0; j < Polies.Length; j++) { if (Polies[j].IsPointInside(new Poly2Tri.TriangulationPoint(currentInner[0].X, currentInner[0].Y))) { Polies[j].AddHole(new Poly2Tri.Polygon(currentInner)); } } } for (int i = 0; i < Polies.Length; i++) { try { Poly2Tri.P2T.Triangulate(Polies[i]); } catch (Exception e) { //throw e; } } //var randHeight = CoordinateConvertor.linear((float)rand.NextDouble(), 0, 1, -3f, height); //var randMaterial = (randHeight > height / 2f) ? "BuildingTall" : randHeight < height / 2f ? "Building2" : "Building"; var randHeight = properties.BuildingHeightVariation.GetNextCircular(); var randMaterial = (randHeight > (properties.BuildingHeightVariation.Average + properties.BuildingHeightVariation.Max) / 2f) ? "BuildingTall" : ("Building" + rand.Next(1, 5)); List <GameObject> resultedGameObjects = new List <GameObject>(); for (int i = 0; i < Polies.Length; i++) { var currentGameObject = Polies[i].GenerateShapeUVedWithWalls_Balanced( CoordinateConvertor.OSMType.Relation, FirstWay, "Building", "Building", randMaterial, /*height +*/ randHeight, /* height + 7*/ properties.BuildingHeightVariation.Max, true); resultedGameObjects.Add(currentGameObject); } return(resultedGameObjects.CombineGameObjectsOfTheSameMaterial()); } con.Close(); } } return(null); }
public static GameObject OSMtoGameObject2(string OSMid, Vector3 MinPointOnMap, Bounds bounds, MapProperties properties, string connPostGreSql, OSMShape shape = OSMShape.Way) { if (rand == null) { rand = new Random((int)DateTime.Now.Ticks); } if (properties == null) { properties = Properties; } var minmaxX = properties.minMaxX; var minmaxY = properties.minMaxY; int direction = -1; float LineWidth = properties.RoadLineThickness; float BuildingWidth = properties.BuildingLineThickness; float height = properties.BuildingHeight; if (height < 4) { height = 4; } var FirstWay = OSMid; if (shape == OSMShape.Way) { var w = new Way(FirstWay); List <OsmNode> WayNodes; List <Tag> WayTags; using (Npgsql.NpgsqlConnection con = new Npgsql.NpgsqlConnection(connPostGreSql)) { con.Open(); WayNodes = w.GetNodesPostgreSQL(FirstWay, con); WayTags = w.GetTagsPostgreSQL(FirstWay, con); con.Close(); } if (WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "landuse" || i.KeyValueSQL[0].ToLower() == "building" || i.KeyValueSQL[0].ToLower() == "highway").Count() != 0) { var tempPoints = new Vector3[WayNodes.Count]; int counter = 0; foreach (var node in WayNodes) { var result = CoordinateConvertor.SimpleInterpolation((float)node.PositionSQL.Lat, (float)node.PositionSQL.Lon, bounds, minmaxX, minmaxY); // Testing the correct direction tempPoints[counter] = new Vector3(direction * (float)result[0], 0, (float)result[1]) - MinPointOnMap; counter++; } WayNodes = null; var building = WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "building"); var highwayType = WayTags.Where(i => i.KeyValueSQL[0].ToLower() == "highway"); WayTags = null; if (building.Count() != 0) { #region Buildings // Check if it has overlapping start and ending points. // NOTE: Replaced with the code to remove all the duplicates, not only the endpoints. // Checking for duplicates: tempPoints = tempPoints.ToArray().RemoveDuplicates(); var Skip = false; if (tempPoints.Length <= 2) { // Buildings that are too small to show such as 76844368 // http://www.openstreetmap.org/browse/way/76844368 // "A weird building were found and ignored. FirstWay \nRelated url: http://www.openstreetmap.org/browse/way/{0}" Skip = true; // continue; } if (!Skip) { var p2d = tempPoints.ToCPoint2D(); // TODO bug in the cpolygon, probably duplicates var shp = new PolygonCuttingEar.CPolygonShape(p2d); shp.CutEar(); p2d = null; GC.Collect(); // TODO: var randHeight = CoordinateConvertor.linear((float)rand.NextDouble(), 0, 1, -3f, height); var randMaterial = (randHeight > height / 2f) ? "BuildingTall" : randHeight < height / 2f ? "Building2" : "Building"; var resultedGameObject = shp.GenerateShapeUVedWithWalls_Balanced( CoordinateConvertor.OSMType.Polygon, FirstWay, "Building", "Building", randMaterial, height + randHeight, height + 7, true); return(resultedGameObject); } #endregion } else { if (highwayType.Count() != 0) { #region Roads var hwtype = highwayType.First(); //Console.WriteLine("Generating roads..id=" + FirstWay); switch (hwtype.KeyValueSQL[1]) { case "cycleway": { var resultedGameObject = CoordinateConvertor.MeshGenerationFilledCorners(tempPoints.ToSegmentedPoints(2f), properties.CyclewayWidth, CoordinateConvertor.OSMType.Line, FirstWay, hwtype.KeyValueSQL[1], "Line", properties.CycleWayMaterial.Name, -0.01f); // ObjFormat.MeshToFile(resultedGameObject,System.IO.Path.Combine( exportPath , resultedGameObject.Name.Replace("|", "-") + ".obj")); return(resultedGameObject); } case "footway": case "path": case "pedestrian": { var resultedGameObject = CoordinateConvertor.MeshGenerationFilledCorners(tempPoints.ToSegmentedPoints(4f), properties.FootwayWidth, CoordinateConvertor.OSMType.Line, FirstWay, hwtype.KeyValueSQL[1], "Line", properties.FootWayMaterial.Name, -0.01f); // ObjFormat.MeshToFile(resultedGameObject,System.IO.Path.Combine( exportPath , resultedGameObject.Name.Replace("|", "-") + ".obj")); return(resultedGameObject); } case "steps": { var resultedGameObject = CoordinateConvertor.MeshGenerationFilledCorners(tempPoints.ToSegmentedPoints(4f), properties.CyclewayWidth, CoordinateConvertor.OSMType.Line, FirstWay, hwtype.KeyValueSQL[1], "Line", properties.StepsMaterial.Name, -0.01f); return(resultedGameObject); } // Miguel R.C. commented these lines. // Reason: Why breaking in the case "motorway"? That results in some roads missing when generating scenarios. // With these lines commented, the motorways will be generated in the default case. //case "motorway": // { // break; // } default: { var resultedGameObject = CoordinateConvertor.MeshGenerationFilledCorners(tempPoints.ToSegmentedPoints(0.5f), properties.RoadWidth, CoordinateConvertor.OSMType.Line, FirstWay, hwtype.KeyValueSQL[1], "Line", properties.RoadMaterial.Name, 0f); return(resultedGameObject); } } #endregion } } } } else if (shape == OSMShape.Relation) // It is probably a multi-polygon building defined in a relation. { List <OsmNode[]> OuterNodes; List <OsmNode> OuterWay; List <OsmNode[]> InnerNodes; List <Tag> tags; Relation r = new Relation(FirstWay); Way w = new Way(); using (Npgsql.NpgsqlConnection con = new Npgsql.NpgsqlConnection(connPostGreSql)) { con.Open(); tags = r.GetTagsPostgreSQL(con); var isMultiPolygon = tags.Exists(t => t.KeyValueSQL[0] == "type" && t.KeyValueSQL[1] == "multipolygon"); if (isMultiPolygon) { var Members = r.GetMembersPostgreSQLByType(r.id, 1, con); OuterNodes = Members .Where(m => m.Role.ToLower() == Member.RoleOuter) .OrderBy(o => o.order) .Select(m => w.GetNodesPostgreSQL(m.ReferenceId, connPostGreSql).ToArray()).ToList(); InnerNodes = Members .Where(m => m.Role.ToLower() == Member.RoleInner) .OrderBy(o => o.order) .Select(m => w.GetNodesPostgreSQL(m.ReferenceId, connPostGreSql).ToArray()).ToList(); OuterWay = new List <OsmNode>(); foreach (var nodelist in OuterNodes) { OuterWay.AddRange(nodelist); } var tempPointsOuterPoints = new Vector3[OuterWay.Count]; int counter = 0; foreach (var node in OuterWay) { var result = CoordinateConvertor.SimpleInterpolation((float)node.PositionSQL.Lat, (float)node.PositionSQL.Lon, bounds, minmaxX, minmaxY); // Testing the correct direction tempPointsOuterPoints[counter] = new Vector3(direction * (float)result[0], 0, (float)result[1]) - MinPointOnMap; counter++; } List <Vector3[]> holes = new List <Vector3[]>(); for (int i = 0; i < InnerNodes.Count; i++) { holes.Add(new Vector3[InnerNodes[i].Length]); for (int j = 0; j < InnerNodes[i].Length; j++) { var result = CoordinateConvertor.SimpleInterpolation((float)InnerNodes[i][j].PositionSQL.Lat, (float)InnerNodes[i][j].PositionSQL.Lon, bounds, minmaxX, minmaxY); // Testing the correct direction holes[i][j] = new Vector3(direction * (float)result[0], 0, (float)result[1]) - MinPointOnMap; } } var outerpolypoints = tempPointsOuterPoints.Select(s => new Poly2Tri.PolygonPoint(s.x, s.z)); Poly2Tri.Polygon poly = new Poly2Tri.Polygon(outerpolypoints); // TODO: poly.IsPointInside() check the holes against all pieces var innerspolypoints = new List <Poly2Tri.PolygonPoint[]>(); for (int i = 0; i < holes.Count; i++) { var currentInner = holes[i].Select(s => new Poly2Tri.PolygonPoint(s.x, s.z)).ToArray(); innerspolypoints.Add(currentInner); poly.AddHole(new Poly2Tri.Polygon(currentInner)); } Poly2Tri.P2T.Triangulate(poly); var randHeight = CoordinateConvertor.linear((float)rand.NextDouble(), 0, 1, -3f, height); var randMaterial = (randHeight > height / 2f) ? "BuildingTall" : randHeight < height / 2f ? "Building2" : "Building"; var resultedGameObject = poly.GenerateShapeUVedWithWalls_Balanced( CoordinateConvertor.OSMType.Relation, FirstWay, "Building", "Building", randMaterial, height + randHeight, height + 7, true); return(resultedGameObject); } con.Close(); } } return(null); }