unity – 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.