0
\$\begingroup\$

I'm currently using MapBox's Untiy SDK, which allows me to render 3D Buildings and roads. I want to place a gameobject at each intersection in an area. But, I'm having trouble finding a good way to do that.

The roads are gameobjects attached to a "Tile" gameobect. The roads have a local position 0,0,0 with meshes on them that dictate their rendered position. A long road can cross multiple tiles, and thus the same "road" may exist as two or more gameobjects on two or more tiles. The Mesh itself is rectangular prism extruded from some line data by Mapbox.

I have tried to use colliders with no success. For one, in order to get points of intersections between two colliders, they need to have rigidbodies attached to them and it's too expensive to do that, since there are hundreds of roads. Furthermore, many of the meshes have bad verts in them, which wont allow mesh colliders to work anyway, and box colliders wont work for non-straight roads. Some road meshes are sprawling, twisting, and winding and could "collide" with a number of roads without actually touching them.

I am not above brute force looping through every mesh's vertex in every tile to detect intersections, but I am as of yet unsure of the best way to do this or if it is necessary.

I am also not sure if MapBox has some hidden feature for this the way Google Maps Unity SDK allows for access to the road lattice and intersections. If I could get some info about the line that represents the road, it would be easy to tell if roads overlap.

The best way I can figure is to loop through the verts on a road to determine if the lines between verts overlap that of the roads within the same time, but am not sure if that is the best course of action

\$\endgroup\$
5
  • \$\begingroup\$ You can detect overlapping colliders without Rigidbodies using features such as Physics.OverlapBox(), though that doesn't sound like the most efficient solution. Have you tried just writing some code that calculates the intersection points using standard line-intersection equations? \$\endgroup\$
    – Kevin
    May 21, 2021 at 18:20
  • \$\begingroup\$ I've tried Physics.OverlapBox() but the problem with that is that it's a first-pass solution at best, as many of the roads are not perfect rectangles and are not aligned with their meshes. I've not yet tried line intersection calculations, mostly because i dont know how to get the lines out of the road data or the best way to calculate a line from a mesh. So I'm looking for some direction to start heading in \$\endgroup\$ May 21, 2021 at 18:27
  • \$\begingroup\$ @DMGregory So the only problem with that, as I understand it, is that I want every intersection in the tile, not just the intersections on a path. However, you lead me in a great direction, which is that mapbox has some utilities I missed on line intersections, if I can find access to the road data as it comes in. If I can, i'd like to avoid making extra API calls \$\endgroup\$ May 21, 2021 at 19:40
  • \$\begingroup\$ If I could get any data out of a road instead of it generating a mesh and gameobject, that would certainly make things a lot easier, but I havent yet figured out how to get that info as it comes in. \$\endgroup\$ May 21, 2021 at 19:45

1 Answer 1

0
\$\begingroup\$

Okay so it's taken me some time, but I finally have a high-level solution that seems to be working.

In my quest to calculate when edges in a road segment overlap, I discovered a better way to determine where an intersection exists. This discovery was found while creating a custom MeshModifier class. The MapBox SDK makes extensive use of ScriptableObjects to modify features as they are loaded in. For instance, you can add create a AddMonoBehavioursModifier instance in your project and tell the SDK to apply it to roads. The instance you create can hold references to scripts you want to attach to each road.

Using this system, I set out to create my own MeshModifier by looking at the LineMeshModifier class they had created themselves. In it I discovered that we do get access to points that represent road segments in the form of Vector3s. After some playing around, I was able to place them in world space where they belong.

Finally, the way to detect intersections was found.

In any tile, each road has a series of Vector3 points that dictate its shape. If any Vector3 in one road is the same as a Vector3 in another road, then they represent the location of an intersection. THIS is how I can find out where intersections are located.

There are probably additional calculations one could do to determine actual segments of road, but this part I am not interested in.

Here is the basic code I came up with. Better ways to do most of it, but this is the first pass and should work for beginners.

public class IntersectionModifier : MeshModifier
{

    //custom classes to make it easier to understand what information is being held
    public class TileToCheck
    {
        public bool hasBeenChecked = false;
        public GameObject tile;
        public List<RoadSegment> roads = new List<RoadSegment>();
        public TileToCheck(GameObject tile)
        {
            this.tile = tile;
        }
    }
    public class RoadSegment
    {
        public List<Vector3> points;
    }

    //our strategy is to hold a reference to each tile as it comes in so that we dont check the same tile twice
    //the string is the tile's name, which is the same as it's coordinates, so it is unique.
    public Dictionary<string, TileToCheck> roadsByTiles = new Dictionary<string, TileToCheck>();

    //MapBox's factories will call these functions and pipe in the features we want
    public override void Run(VectorFeatureUnity feature, MeshData md, float scale)
    {
        AddRoadToDictionary(feature, md);
    }
    //MapBox's factories will call these functions and pipe in the features we want
    public override void Run(VectorFeatureUnity feature, MeshData md, UnityTile tile = null)
    {
        AddRoadToDictionary(feature, md);
    }

    //Adds our data to the dictionary
    public void AddRoadToDictionary(VectorFeatureUnity feature, MeshData md)
    {
        var key = feature.Tile.gameObject.name;
        //data comes in an ordered fashion, so whenever we get a new tile, that means the last tile is finished.
        if (roadsByTiles.ContainsKey(key) == false)
        {
            //trigger calculation of intersection
            foreach (var kvp in roadsByTiles)
            {
                CalculateIntersections(kvp.Key, kvp.Value);
            }

            roadsByTiles.Add(key, new TileToCheck(feature.Tile.gameObject));

        }
        //feature.Points is a list of lists, each of those sub-lists being a road segment with points
        foreach (var roadSegment in feature.Points)
        {
            var road = new RoadSegment();
            road.points = roadSegment;

            roadsByTiles[key].roads.Add(road);
        }

    }

    public void CalculateIntersections(string name, TileToCheck tile)
    {
        //dont check the same one twice.  
        //there are better ways to do this
        if (tile.hasBeenChecked == true)
            return;
        tile.hasBeenChecked = true;

        //the following isnt necessary to determine intersection points, but you will need to 
        //align them to the tile eventually if you want real world space locations.
        //loop through the roads
        foreach (var road in tile.roads)
        {
            //separate roads by gameobject, to make things easier to debug.
            GameObject roadGo = new GameObject();
            //make sure the position is the same as the tile from the map or the positioning will be inaccurate
            roadGo.transform.position = tile.tile.transform.position;
            roadGo.transform.SetParent(tile.tile.transform);

            foreach (var point in road.points)
            {
                GameObject go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                go.transform.SetParent(roadGo.transform);
                //This position in world space is accurate as long as the map hasnt updated.
                //this system of setting parents isnt necessary if you use TransformPoint, 
                //but its helpful to understand what the "point" actually means.
                go.transform.localPosition = point;
            }
        }

        //again, better ways to do this
        //loop through each road in the tile
        for (int i = 0; i < tile.roads.Count; i++)
        {
            //loop through each other road in the tile
            for (int j = 0; j < tile.roads.Count; j++)
            {
                //ignore yourself
                //or dont? can a road intersect with itself? 
                if (j == i)
                    continue;

                //loop through the road's points
                for (int ii = 0; ii < tile.roads[i].points.Count; ii++)
                {
                    var pointA = tile.roads[i].points[ii];
                    //loop through the other road's points
                    for (int jj = 0; jj < tile.roads[j].points.Count; jj++)
                    {
                        var pointB = tile.roads[j].points[jj];
                        //if the points are the same, they are an intersection
                        if (pointB == pointA)
                        {
                            //Intersection! 
                            //this point is local to the tile's position. 

                        }
                    }
                }
            }
        }

    }
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .