Ejemplo n.º 1
0
        protected override Vector3 ToCorner(RoadSegmentEndPoint neighbor, bool atEnd)
        {
            var neighborNormal    = GetNeighborNormal(neighbor, atEnd);
            var toCornerDirection = neighbor.To switch
            {
                null => DirectionNormalNoZ,                                      // if I have no neighbor, use my own normal
                CrossingRoadSegment _ => neighborNormal,                         // if my neighbor is an unflexible crossing, use its normal
                _ => Vector3.Normalize(DirectionNormalNoZ + neighborNormal) / 2, // otherwise, meet in the middle
            };

            // This shouldn't happen but sometimes does in broken maps (Heartland Shield for example).
            if (toCornerDirection.LengthSquared() < 0.1f)
            {
                toCornerDirection = DirectionNormalNoZ;
            }

            // When two road segments meet in an angled curve, their meeting edge is tilted and thus longer than the width of the road
            // -> divide by cosine
            // For straight roads ending in a crossing:
            // -> the angles in the crossing texture may not well match the actual angles of the incoming roads
            //    (especially for x-crossings, since there's only one texture for 4-road crossings which assumes 90° everywhere)
            // -> the meeting edge may become quite tilted and noticeably longer than road width,
            //    while the road shown in the texture of the crossing always has the fixed road width
            // -> to avoid visible breaks between the road segment and the crossing texture, distort the edge so its tilted seam is 'roadwidth' long
            var cosine         = Vector3.Dot(DirectionNormalNoZ, toCornerDirection);
            var toCornerLength = neighbor.To is CrossingRoadSegment ? HalfHeight : HalfHeight / cosine;

            return(toCornerDirection * toCornerLength);
        }
Ejemplo n.º 2
0
        private static void InsertNodeSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments)
        {
            foreach (var node in topology.Nodes)
            {
                foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template))
                {
                    var template = edgesPerTemplate.Key;
                    // possible optimization: only compute angles if necessary?
                    var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate);

                    switch (edgesPerTemplate.Count())
                    {
                    // TODO support end caps
                    case 1:     // end point
                        break;

                    case 2:
                        CurvedRoadSegment.CreateCurve(incomingRoadData, node.Position, template, edgeSegments);
                        break;

                    case 3:
                    case 4:
                        CrossingRoadSegment.CreateCrossing(incomingRoadData, node.Position, template, edgeSegments);
                        break;
                    }
                }
            }
        }
Ejemplo n.º 3
0
        private static void InsertNodeSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments)
        {
            foreach (var node in topology.Nodes)
            {
                foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template))
                {
                    switch (edgesPerTemplate.Count())
                    {
                    // TODO support end caps
                    case 1:     // end point
                        break;

                    case 2:     // TODO normal road, create segments for tight/broad curves
                        break;

                    case 3:
                    case 4:
                        var template         = edgesPerTemplate.Key;
                        var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate);
                        CrossingRoadSegment.CreateCrossing(incomingRoadData, node.Position, template, edgeSegments);
                        break;
                    }
                }
            }
        }
Ejemplo n.º 4
0
        protected override Vector3 ToCorner(RoadSegmentEndPoint neighbor, bool atEnd)
        {
            var neighborNormal   = GetNeighborNormal(neighbor, atEnd);
            var segment          = (StraightRoadSegment)Segment;
            var oppositeNeighbor = atEnd ? segment.Start.To : segment.End.To;

            var toCornerDirection = neighbor.To switch
            {
                null when oppositeNeighbor is CrossingRoadSegment crossing =>   // special handling to reproduce (somewhat strange) behavior of the original engine
                OriginalNormal(crossing.Position),
                null =>                                                         // if I have no neighbor, use my own normal
                DirectionNormalNoZ,
                StraightRoadSegment _ =>                                        // if my neighbor is also a straight road segment, meet in the middle
                Vector3.Normalize(DirectionNormalNoZ + neighborNormal),
                _ => neighborNormal,                                            // otherwise use my unflexible neighbor's normal
            };

            Vector3 OriginalNormal(Vector3 crossingPosition)
            {
                var originalDirection = atEnd ?
                                        segment.EndPosition - crossingPosition :
                                        crossingPosition - segment.StartPosition;

                var originalDirectionNoZ = Vector3.Normalize(originalDirection.WithZ(0));

                return(Vector3.Cross(originalDirectionNoZ, Vector3.UnitZ));
            }

            // This shouldn't happen but sometimes does in broken maps (Heartland Shield for example).
            var toCornerLengthSquared = toCornerDirection.LengthSquared();

            if (float.IsNaN(toCornerLengthSquared) || toCornerLengthSquared < 0.1f)
            {
                toCornerDirection = DirectionNormalNoZ;
            }

            // When two road segments meet in an angled curve, their meeting edge is tilted and thus longer than the width of the road
            // -> divide by cosine
            // For straight roads ending in a curve or crossing:
            // -> the angles in the crossing texture may not well match the actual angles of the incoming roads
            //    (especially for X-crossings, since there's only one texture for 4-road crossings which assumes 90° everywhere)
            // -> the meeting edge may become quite tilted and noticeably longer than road width,
            //    while the road shown in the texture of the crossing always has the fixed road width
            // -> to avoid visible breaks between the road segment and the crossing texture, distort the edge so its tilted seam is 'roadwidth' long
            var cosine         = Vector3.Dot(DirectionNormalNoZ, toCornerDirection);
            var toCornerLength = neighbor.To is StraightRoadSegment ? HalfHeight / cosine : HalfHeight;

            return(toCornerDirection * toCornerLength);
        }
Ejemplo n.º 5
0
 private static void InsertCrossingSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments)
 {
     foreach (var node in topology.Nodes)
     {
         foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template))
         {
             var connectedEdges = edgesPerTemplate.Count();
             if (connectedEdges == 3 || connectedEdges == 4)
             {
                 var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate, edgeSegments);
                 CrossingRoadSegment.CreateCrossing(incomingRoadData, node.Position, edgesPerTemplate.Key, edgeSegments);
             }
         }
     }
 }
Ejemplo n.º 6
0
        private static void InsertNodeSegments(RoadTopology topology, IReadOnlyDictionary <RoadTopologyEdge, StraightRoadSegment> edgeSegments)
        {
            foreach (var node in topology.Nodes)
            {
                foreach (var edgesPerTemplate in node.Edges.GroupBy(e => e.Template))
                {
                    var template         = edgesPerTemplate.Key;
                    var incomingRoadData = ComputeRoadAngles(node, edgesPerTemplate);

                    switch (edgesPerTemplate.Count())
                    {
                    case 2:
                        CurvedRoadSegment.CreateCurve(incomingRoadData, node.Position, template, edgeSegments);
                        break;

                    case 3:
                    case 4:
                        CrossingRoadSegment.CreateCrossing(incomingRoadData, node.Position, template, edgeSegments);
                        break;
                    }
                }
            }
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Generate vector from the start/end position of an edge to the corner of the mesh's base geometry
        /// </summary>
        /// <param name="neighbor"></param>
        /// <param name="atEnd"></param>
        /// <returns></returns>
        private Vector3 ToCorner(RoadSegmentEndPoint neighbor, bool atEnd)
        {
            var neighborDirection = (atEnd ? -1 : 1) * neighbor?.IncomingDirection ?? Vector3.Zero;
            var neighborNormal    = Vector3.Cross(Vector3.Normalize(neighborDirection.WithZ(0)), Vector3.UnitZ);
            var toCornerDirection = neighbor.To switch
            {
                null => DirectionNormalNoZ,                          // if I have no neighbor, use my own normal
                CrossingRoadSegment _ => neighborNormal,             // if my neighbor is an unflexible crossing, use its normal
                _ => (DirectionNormalNoZ + neighborNormal) / 2,      // otherwise, meet in the middle
            };

            // When two road segments meet in an angled curve, their meeting edge is tilted and thus longer than the width of the road
            // -> divide by cosine
            // For straight roads ending in a crossing:
            // -> the angles in the crossing texture may not well match the actual angles of the incoming roads
            //    (especially for x-crossings, since there's only one texture for 4-road crossings which assumes 90° everywhere)
            // -> the meeting edge may become quite tilted and noticeably longer than road width,
            //    while the road shown in the texture of the crossing always has the fixed road width
            // -> to avoid visible breaks between the road segment and the crossing texture, distort the edge so its tilted seam is 'roadwidth' long
            var cosine         = Vector3.Dot(DirectionNormalNoZ, toCornerDirection);
            var toCornerLength = neighbor.To is CrossingRoadSegment ? HalfHeight : HalfHeight / cosine;

            return(toCornerDirection * toCornerLength);
        }