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 StraightRoadSegment _ => Vector3.Normalize(DirectionNormalNoZ + neighborNormal), // if my neighbor is also a straight road segment, meet in the middle _ => neighborNormal, // otherwise use my unflexible neighbor's normal }; // 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 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); }
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); }
private static IncomingRoadData GetIncomingRoadData(RoadTopologyNode node, RoadTopologyEdge incomingEdge, StraightRoadSegment edgeSegment) { var isStart = incomingEdge.Start.Position == node.Position; var fromPosition = isStart ? edgeSegment.EndPosition : edgeSegment.StartPosition; var segmentVector = (edgeSegment.EndPosition - edgeSegment.StartPosition) * (isStart ? 1 : -1); var direction = segmentVector.LengthSquared() < 0.01f ? Vector3.UnitX : Vector3.Normalize(segmentVector); return(new IncomingRoadData( incomingEdge, fromPosition, direction, MathF.Atan2(direction.Y, direction.X))); }