Trying to creating roads from a 2D texture into a 3D map using RoadArchitect


I need help with a complex task I’m trying to creating roads using RoadArchitect: https://github.com/FritzsHero/RoadArchitect/tree/RewrittenAPI I’m trying to detect all roads from a texture. I have created a model class to store every detected road using a little bit of recursion:

    [Serializable]     public class RoadNode : IPathNode     {         //[JsonConverter(typeof(RoadNodeConverter))]         public ConcurrentBag<int> Connections { get; set; } // TODO: IPathNode<T> + RoadNode : IPathNode<RoadNode> + Connections (ConcurrentBag<RoadNode>), but can't be serialized due to StackOverflow and OutOfMemory exceptions          public Point Position { get; set; }         public bool Invalid { get; set; }         public int Thickness { get; set; }         public ConcurrentBag<int> ParentNodes { get; set; }          public RoadNode()         {             //Connections = new List<RoadNode>();         }          public RoadNode(Point position, int thickness)         //: this()         {             Position = position;             Thickness = thickness;         }          public RoadNode(Point position, bool invalid, int thickness)         //: this()         {             Position = position;             Invalid = invalid;             Thickness = thickness;         }          public RoadNode(int x, int y, int thickness)             : this(new Point(x, y), thickness)         {         }          public void SetThickness(int thickness)         {             // TODO: Call this when needed and thickness == -1             Thickness = thickness;         }          public int GetKey()         {             return F.P(Position.x, Position.y, mapWidth, mapHeight);         }     } 
    public interface IPathNode     {         ConcurrentBag<int> Connections { get; }         Point Position { get; }         bool Invalid { get; }     } 

At every loaded chunk (I’m using SebLeague tutorial to create the chunks), I get all the road points for this chunk. Then, I try to iterare them. But I have problems interpreting the results.

I have part of the task done:

        public static IEnumerable<Road> CreateRoad(this IEnumerable<Point> points, StringBuilder builder)         {             if (points.IsNullOrEmpty())             {                 builder?.AppendLine($  "\tskipped...");                 yield break; // This chunk doesn't contain any road. Exiting.             }              //var dict = points.Select(p => new {Index = p.GetKey(), Point = p}).ToDictionary(x => x.Index, x => x.Point);             //var builder = new StringBuilder();             var roads = GetRoads(points, builder);             foreach (var list in roads)             {                 if (list.IsNullOrEmpty()) continue;                 //var first = road.First();                 //var backIndex = ((Point)CityGenerator.SConv.GetRealPositionOnMap(first)).GetKey();                  var road = CreateIndependantRoad(list);                 builder?.AppendLine($  "\t... finished road ({road.name}) with {list.Count} nodes.");                 yield return road;             }             //Debug.Log(builder?.ToString());         }          private static IEnumerable<List<Vector3>> GetRoads(IEnumerable<Point> points, StringBuilder builder)         {             var model = RoadGenerator.RoadModel;              var queue = new Queue<Point>(points);             int i = 0;              builder?.AppendLine($  "\tcount: {queue.Count}");              var dictionary = new Dictionary<int, List<Vector3>>();              while (queue.Count > 0)             {                 var list = new List<Vector3>();                  var pt = queue.Dequeue();                 var itemIndex = pt.GetKey();                 dictionary.Add(itemIndex, list);                  var node = model.SimplifiedRoadNodes[itemIndex];                  builder?.AppendLine($  "\troad iteration: {i}");                  //var conn = node.Connections;                 var nodes = GetRoadNodes(node, ptVal =>                 {                     if (ptVal.HasValue) queue = new Queue<Point>(queue.Remove(ptVal.Value));                     return queue;                 },                     parentNodeIndex => { return dictionary[parentNodeIndex]; },                     builder);                  foreach (var point in nodes)                     list.Add(CityGenerator.SConv.GetRealPositionOnMap((Vector2)point).GetHeightForPoint());                  yield return list;                 ++i;             }         }          private static IEnumerable<Point> GetRoadNodes(RoadNode node, Func<Point?, Queue<Point>> queueFunc, Func<int, List<Vector3>> parentFunc, StringBuilder builder, int level = -1)         {             if (queueFunc == null) throw new ArgumentNullException(nameof(queueFunc));             if (parentFunc == null) throw new ArgumentNullException(nameof(parentFunc));              var conn = node.Connections;             if (conn.IsNullOrEmpty())             {                 yield return node.Position;                 yield break;             }             if (queueFunc(null).Count == 0) yield break;              ++level;             builder?.AppendLine($  "{new string('\t', 2)}level: {level} -> {queueFunc(null).Count} items");              //if (conn.Count == 1)             //{             //    var firstNode = conn.First().GetNode();             //    ////var firstPoint = conn.First().GetPoint();              //    var list = parentFunc(firstNode.ParentNodes.First()); // TODO: parent nodes should be one...             //    list.Add(CityGenerator.SConv.GetRealPositionOnMap((Vector2)conn.First().GetPoint()).GetHeightForPoint());             //}             //else             {                 foreach (var item in conn)                 {                     var pt = item.GetPoint();                     if (!queueFunc(null).Contains(pt)) yield break;                     yield return pt;                     if (queueFunc(pt).Count == 0) yield break;                      var subnode = pt.GetKey().GetNode();                     var pts = GetRoadNodes(subnode, queueFunc, parentFunc, builder, level);                     foreach (var point in pts)                         yield return point;                 }             }         } 

You can see here the StringBuilder results: https://pastebin.com/1tW4V4BC

But as you can see I have problems with roads. Some roads only have one node, other roads only have 2 nodes…

I’m thinking that the current implementation I did isn’t right at all, because instead of grouping all nodes into one major road I’m splitting them into road chunks without sense.

By this reason I created this topic, in order to clarify myself because I’m not sure about what I’m doing and which way should I take.