public static RoadTopology FromMapObjects(MapObject[] mapObjects) { var roadTopology = new RoadTopology(); var templates = new Dictionary <string, RoadTemplate>(); for (var i = 0; i < mapObjects.Length; i++) { var mapObject = mapObjects[i]; switch (mapObject.RoadType & RoadType.PrimaryType) { case RoadType.Start: case RoadType.End: var roadEnd = mapObjects[++i]; // Some maps have roads with invalid start- or endpoints. // We'll skip processing them altogether. if (mapObject.TypeName == "" || roadEnd.TypeName == "") { continue; } if (!templates.TryGetValue(mapObject.TypeName, out var template)) { template = new RoadTemplate(mapObject.TypeName); templates.Add(mapObject.TypeName, template); } roadTopology.AddSegment(template, mapObject, roadEnd); break; } } return(roadTopology); }
public void CurveType( NodePosition startPosition1, RoadType startType1, NodePosition endPosition1, RoadType endType1, NodePosition startPosition2, RoadType startType2, NodePosition endPosition2, RoadType endType2, RoadTextureType expectedCurveType) { var start1 = new MapObject(NodePositions[startPosition1], 0, startType1, default); var end1 = new MapObject(NodePositions[endPosition1], 0, endType1, default); var start2 = new MapObject(NodePositions[startPosition2], 0, startType2, default); var end2 = new MapObject(NodePositions[endPosition2], 0, endType2, default); var template = new RoadTemplate("SideWalk3"); var topology = new RoadTopology(); topology.AddSegment(template, start1, end1); topology.AddSegment(template, start2, end2); topology.AlignOrientation(); var curveNode = topology.Nodes.Single(n => n.Edges.Count == 2); var actualCurveType = CurvedRoadSegment.ChooseCurveType(topology.Edges[0], topology.Edges[1], curveNode.Position); Assert.Equal(expectedCurveType, actualCurveType); }
private void LoadObjects( AssetLoadContext loadContext, HeightMap heightMap, MapObject[] mapObjects, NamedCameras namedCameras, List <Team> teams, out WaypointCollection waypointCollection, out RoadCollection roads, out Bridge[] bridges, out CameraCollection cameras) { var waypoints = new List <Waypoint>(); var bridgesList = new List <Bridge>(); var roadTopology = new RoadTopology(); for (var i = 0; i < mapObjects.Length; i++) { var mapObject = mapObjects[i]; switch (mapObject.RoadType & RoadType.PrimaryType) { case RoadType.None: switch (mapObject.TypeName) { case Waypoint.ObjectTypeName: waypoints.Add(new Waypoint(mapObject)); break; default: GameObject.FromMapObject(mapObject, loadContext.AssetStore, GameObjects, heightMap, null, teams); break; } break; case RoadType.BridgeStart: case RoadType.BridgeEnd: // Multiple invalid bridges can be found in e.g GLA01. if ((i + 1) >= mapObjects.Length || !mapObjects[i + 1].RoadType.HasFlag(RoadType.BridgeEnd)) { Logger.Warn($"Invalid bridge: {mapObject.ToString()}, skipping..."); continue; } var bridgeEnd = mapObjects[++i]; bridgesList.Add(AddDisposable(new Bridge( loadContext, heightMap, mapObject, mapObject.Position, bridgeEnd.Position, GameObjects))); break; case RoadType.Start: case RoadType.End: var roadEnd = mapObjects[++i]; // Some maps have roads with invalid start- or endpoints. // We'll skip processing them altogether. if (mapObject.TypeName == "" || roadEnd.TypeName == "") { Logger.Warn($"Road {mapObject.ToString()} has invalid start- or endpoint, skipping..."); continue; } if (!mapObject.RoadType.HasFlag(RoadType.Start) || !roadEnd.RoadType.HasFlag(RoadType.End)) { throw new InvalidDataException(); } // Note that we're searching with the type of either end. // This is because of weirdly corrupted roads with unmatched ends in USA04, which work fine in WB and SAGE. var roadTemplate = loadContext.AssetStore.RoadTemplates.GetByName(mapObject.TypeName) ?? loadContext.AssetStore.RoadTemplates.GetByName(roadEnd.TypeName); if (roadTemplate == null) { throw new InvalidDataException($"Missing road template: {mapObject.TypeName}"); } roadTopology.AddSegment(roadTemplate, mapObject, roadEnd); break; } loadContext.GraphicsDevice.WaitForIdle(); } cameras = new CameraCollection(namedCameras?.Cameras); roads = AddDisposable(new RoadCollection(roadTopology, loadContext, heightMap)); waypointCollection = new WaypointCollection(waypoints, MapFile.WaypointsList.WaypointPaths); bridges = bridgesList.ToArray(); }
private void LoadObjects( ContentManager contentManager, HeightMap heightMap, MapObject[] mapObjects, Team[] teams, out WaypointCollection waypointCollection, out GameObjectCollection gameObjects, out Road[] roads, out Bridge[] bridges) { var waypoints = new List <Waypoint>(); gameObjects = new GameObjectCollection(contentManager); var roadsList = new List <Road>(); var bridgesList = new List <Bridge>(); var roadTopology = new RoadTopology(); for (var i = 0; i < mapObjects.Length; i++) { var mapObject = mapObjects[i]; var position = mapObject.Position; switch (mapObject.RoadType & RoadType.PrimaryType) { case RoadType.None: switch (mapObject.TypeName) { case "*Waypoints/Waypoint": waypoints.Add(CreateWaypoint(mapObject)); break; default: position.Z += heightMap.GetHeight(position.X, position.Y); var gameObject = CreateGameObject(mapObject, teams, contentManager); if (gameObject != null) { gameObject.Transform.Translation = position; gameObject.Transform.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, mapObject.Angle); gameObjects.Add(gameObject); if (gameObject.Definition.IsBridge) { // This is a landmark bridge. We need to add towers at the corners. CreateTowers(contentManager, gameObjects, gameObject, mapObject); } } break; } break; case RoadType.BridgeStart: case RoadType.BridgeEnd: // Multiple invalid bridges can be found in e.g GLA01. // TODO: Log a warning. if ((i + 1) >= mapObjects.Length || !mapObjects[i + 1].RoadType.HasFlag(RoadType.BridgeEnd)) { continue; } var bridgeEnd = mapObjects[++i]; var bridgeTemplate = GetBridgeTemplate(contentManager, mapObject); if (Bridge.TryCreateBridge( contentManager, heightMap, bridgeTemplate, mapObject.Position, bridgeEnd.Position, out var bridge)) { bridgesList.Add(AddDisposable(bridge)); } break; default: var roadEnd = mapObjects[++i]; // Some maps have roads with invalid start- or endpoints. // We'll skip processing them altogether. // TODO: Log a warning. if (mapObject.TypeName == "" || roadEnd.TypeName == "") { continue; } if (!mapObject.RoadType.HasFlag(RoadType.Start) || !roadEnd.RoadType.HasFlag(RoadType.End)) { throw new InvalidDataException(); } // Note that we're searching with the type of either end. // This is because of weirdly corrupted roads with unmatched ends in USA04, which work fine in WB and SAGE. var roadTemplate = contentManager.IniDataContext.RoadTemplates.Find(x => x.Name == mapObject.TypeName || x.Name == roadEnd.TypeName); if (roadTemplate == null) { throw new InvalidDataException($"Missing road template: {mapObject.TypeName}"); } roadTopology.AddSegment(roadTemplate, mapObject, roadEnd); break; } contentManager.GraphicsDevice.WaitForIdle(); } // The map stores road segments with no connectivity: // - a segment is from point A to point B // - with a road type name // - and start and end curve types (angled, tight curve, broad curve). // The goal is to create road networks of connected road segments, // where a network has only a single road type. // A road network is composed of 2 or more nodes. // A network is a (potentially) cyclic graph. // A road node has > 1 and <= 4 edges connected to it. // A node can be part of multiple networks. // An edge can only exist in one network. // TODO: If a node stored in the map has > 4 edges, the extra edges // are put into a separate network. var networks = roadTopology.BuildNetworks(); foreach (var network in networks) { foreach (var edge in network.Edges) { var startPosition = edge.Start.TopologyNode.Position; var endPosition = edge.End.TopologyNode.Position; startPosition.Z += heightMap.GetHeight(startPosition.X, startPosition.Y); endPosition.Z += heightMap.GetHeight(endPosition.X, endPosition.Y); roadsList.Add(AddDisposable(new Road( contentManager, heightMap, edge.TopologyEdge.Template, startPosition, endPosition))); } } waypointCollection = new WaypointCollection(waypoints); roads = roadsList.ToArray(); bridges = bridgesList.ToArray(); }