private void MakeCurveRail(WaypointNode curr, WaypointNode prev, WaypointNode next, int segmentID) { Vector3 a = curr.transform.localPosition + GetDirection(curr.transform, prev.transform) * curr.radius; Vector3 b = curr.transform.localPosition; Vector3 c = curr.transform.localPosition + GetDirection(curr.transform, next.transform) * curr.radius; //float distance = Vector3.Distance(a, b); float distance = (Mathf.PI / 2) * curr.radius; int midpoints = (int)(distance / step); float angleStep = step / (curr.radius * (Mathf.PI / 2)); List <Vector3> points = new List <Vector3>(); TrackSection tsp = Instantiate(trackSectionPrefab); tsp.SetEnds(prev, next); tsp.Init(); for (int i = 0; i < midpoints; i++) { Vector3 x = Vector3.Lerp(a, b, i * angleStep); Vector3 y = Vector3.Lerp(b, c, i * angleStep); points.Add(Vector3.Lerp(x, y, i * angleStep)); } tsp.transform.SetParent(curr.transform); tsp.generator = this; tsp.SetPositions(points.ToArray()); }
/// <summary> /// Method to calculate, for each of the candidates, the real closest distance (squared) from mouse to track /// </summary> void CalcRealDistances() { if (realDistancesAreCalculated) { return; } List <double> existingKeys = sortedTrackCandidates.Keys.ToList(); foreach (double distanceKey in existingKeys) { TrackCandidate trackCandidate = sortedTrackCandidates[distanceKey]; if (trackCandidate.trackNode == null) { continue; } TrackSection trackSection = tsectionDat.TrackSections.Get(trackCandidate.vectorSection.SectionIndex); DistanceLon distanceLon = CalcRealDistanceSquared(trackCandidate.vectorSection, trackSection); double realDistanceSquared = (double)distanceLon.distanceSquared; // Add the trackCandidate to the sorted list with its new distance squared as key if (!sortedTrackCandidates.ContainsKey(realDistanceSquared)) { trackCandidate.distanceAlongSection = distanceLon.lengthAlongTrack; sortedTrackCandidates.Add(realDistanceSquared, trackCandidate); } } realDistancesAreCalculated = true; }
/// <summary> /// Draw (part of) a tracksection (either curved or straight) /// </summary> /// <param name="drawArea">Area to draw upon</param> /// <param name="tvs">The vectorSection itself that needs to be drawn</param> /// <param name="colors">Colorscheme to use</param> /// <param name="startOffset">Do not draw the first startOffset meters in the section</param> /// <param name="stopOffset">Do not draw past stopOffset meters (draw all if stopOffset less than 0)</param> /// <remarks>Note that his is very similar to DrawTrackSection in class DrawTrackDB, but this one allows to draw partial sections</remarks> private void DrawTrackSection(DrawArea drawArea, TrVectorSection tvs, ColorScheme colors, float startOffset, float stopOffset) { TrackSection trackSection = tsectionDat.TrackSections.Get(tvs.SectionIndex); if (trackSection == null) return; WorldLocation thisLocation = new WorldLocation(tvs.TileX, tvs.TileZ, tvs.X, 0, tvs.Z); if (trackSection.SectionCurve != null) { //curved section float radius = trackSection.SectionCurve.Radius; int sign = (trackSection.SectionCurve.Angle < 0) ? -1 : 1; float angleLength = (stopOffset < 0) ? trackSection.SectionCurve.Angle : sign*MathHelper.ToDegrees(stopOffset/radius); float angleStart = sign*MathHelper.ToDegrees(startOffset / radius); angleLength -= angleStart; drawArea.DrawArc(trackSection.SectionSize.Width, colors.TrackCurved, thisLocation, radius, tvs.AY, angleLength, angleStart); } else { // straight section float length = (stopOffset < 0) ? trackSection.SectionSize.Length : stopOffset; length -= startOffset; drawArea.DrawLine(trackSection.SectionSize.Width, colors.TrackStraight, thisLocation, length, tvs.AY, startOffset); } }
public void PlaceJunctionRight() { TrackSection section = null; if (trackLayer.CurrentSection != null) { section = trackLayer.PlaceTrack(2.0f); } else { RaycastHit hit; if (FirstPersonController.Main.RaycastCameraForward(out hit, 1000.0f, terrainMask)) { section = trackLayer.StartTrack(hit.point + Vector3.up * 0.01f, GetNewTrackRotation(hit), 2.0f); } } if (section != null) { var straight = trackLayer.PlaceTrack(length); trackLayer.MoveBack(); var right = trackLayer.PlaceTrack(length, angle); var junction = new Junction(section, straight, right); // Create game objects and group them var group = new List <TrackSectionComponent>(); TrackFactory.Instance.PlaceAndRegisterSection(section, true).AddToGroup(group); TrackFactory.Instance.PlaceAndRegisterSection(straight, true).AddToGroup(group); TrackFactory.Instance.PlaceAndRegisterSection(right, true).AddToGroup(group); // Add switch TrackFactory.Instance.AddSwitchToJunction(junction); DeselectIfConnected(); } }
private TrackSection DeserializeTrackSection() { var id = reader.ReadUInt32(); if (id == 0) { throw new System.Exception("Invalid ID 0 for track section!"); } var pos = DeserializeVector3(); var rot = DeserializeQuaternion(); var length = reader.ReadSingle(); var curve = reader.ReadSingle(); var section = new TrackSection() { UniqueID = id, Position = pos, Rotation = rot, Length = length, Curved = curve != 0, Curve = curve, }; if (loadedSections.ContainsKey(id)) { throw new System.Exception("Duplicate track id " + id); } loadedSections[id] = section; TrackDatabase.Instance.RegisterTrack(section); // TODO: This may be bad BasicTrackLayerTool.TryAutoConnect(section); return(section); }
private Wagon PlaceSelected(TrackSection section, float distance) { var wagon = Instantiate(selectedWagon); wagon.name = selectedWagon.name; selectedWagon = null; wagon.Place(section, distance); /*wagon.isKinematic = false; * * var wagon2 = Instantiate(wagon); * wagon2.name = wagon.name; * wagon2.Place(section, distance + wagon.LoC); * wagon2.isKinematic = false; * wagon2.GetComponent<LocomotiveController>().enabled = false; * * var wagon3 = Instantiate(wagon); * wagon3.name = wagon.name; * wagon3.Place(section, distance + wagon.LoC * 2); * * wagon3.isKinematic = false; * wagon3.GetComponent<LocomotiveController>().enabled = false; * * wagon2.rearCoupler.Couple(wagon.frontCoupler); * wagon3.rearCoupler.Couple(wagon2.frontCoupler);*/ return(wagon); }
/// <summary> /// Spawns a consist on a track section /// </summary> /// <param name="startSection"></param> /// <returns>The lead car of this consist</returns> public TrainController Spawn(TrackSection startSection) { Driver driverInstance = null; if (driver != null) { driverInstance = GameObject.Instantiate(driver.gameObject).GetComponent <Driver>(); } TrainController controller; TrainController previous = null; TrainController lead = null; TrackSection currentSection; currentSection = startSection; // Spawn from the end of the track float spawnPosition = currentSection.length - 10.0f; // Spawn vehicles front to back for (int i = 0; i < vehicles.Length; i++) { controller = GameObject.Instantiate(vehicles[i].vehicleController.gameObject).GetComponent <TrainController>(); //TODO: TrackVehicle facing directions works but gets ignored by driving logic, e.g. it's only visual. controller.trackVehicle = new TrackVehicle(currentSection, spawnPosition, Direction.Forward, vehicles[i].faceDirection, controller.length, controller.wheelBase); if (i == 0) { lead = controller; } // Add engines to driver if we have one TrainEngineController engine = controller as TrainEngineController; if (engine != null && driverInstance != null) { driverInstance.AddEngine(engine); } if (previous != null) { previous.next = controller; controller.previous = previous; //Move ourselfs back on the track controller.trackVehicle.SetDirection(Direction.Reverse); controller.trackVehicle.Move(controller.length * 0.5f + previous.length * 0.5f); controller.trackVehicle.SetDirection(Direction.Forward); //Change spawn position to our new location spawnPosition -= controller.length * 0.5f + previous.length * 0.5f; } previous = controller; } if (driverInstance != null) { driverInstance.RecalculateTrain(); } return(lead); }
public void RegisterTrack(TrackSection section) { var zoneStart = RoundToZone(section.Position); var zoneEnd = RoundToZone(section.EndPosition); if (!zones.ContainsKey(zoneStart)) { zones[zoneStart] = new HashSet <TrackSection>(); } zones[zoneStart].Add(section); if (zoneEnd != zoneStart) { if (!zones.ContainsKey(zoneEnd)) { zones[zoneEnd] = new HashSet <TrackSection>(); } zones[zoneEnd].Add(section); } if (section.UniqueID == 0) { section.UniqueID = nextFreeId++; } else if (nextFreeId <= section.UniqueID) { nextFreeId = section.UniqueID + 1; } sectionMap.Add(section.UniqueID, section); }
/// <summary> /// Makes a track of equal lengths outside a station/yard. /// </summary> /// <returns></returns> public static TrackSection[] MakeTrack(int trackLength, int noTracks, string startingSectionName, string startingSignalName) { TrackSection[] sections = new TrackSection[noTracks]; string tempSectionName, tempSignalName; TrackSection tempObjBefore = null, tempObj; for (int i = 0; i < noTracks; i++) { tempSectionName = $"{startingSectionName + i}"; tempSignalName = $"{startingSignalName + i}"; tempObj = new TrackSection(startingSectionName, startingSignalName, TrackSection.Crossing.STRAIGHT, trackLength, null, tempObjBefore); tempObjBefore = tempObj; // Updates the section before to have a reference to this // current section as the one in the front if (i >= 1) { sections[i - 1].SectionOnFront = tempObj; } sections[i] = tempObj; } return(sections); }
protected bool HighlightClosestEnd(TrackSection trackSection, bool onlyUnconnected, Vector3 point, out bool startIsClosest) { var toEnd = Vector3.Distance(point, trackSection.EndPosition); var toStart = Vector3.Distance(point, trackSection.Position); startIsClosest = false; if (toEnd < toStart) { if (trackSection.Next == null || !onlyUnconnected) { Highlighter.DrawHighlighter(Matrix4x4.TRS(trackSection.EndPosition, trackSection.GetRotationOnTrack(trackSection.Length), Vector3.one)); return(true); } } else { if (trackSection.Previous == null || !onlyUnconnected) { Highlighter.DrawHighlighter(Matrix4x4.TRS(trackSection.Position, trackSection.Rotation, Vector3.one)); startIsClosest = true; return(true); } } return(false); }
public void DeregisterTrack(TrackSection section) { var zoneStart = RoundToZone(section.Position); var zoneEnd = RoundToZone(section.EndPosition); if (!zones.ContainsKey(zoneStart)) { Debug.LogErrorFormat("Zone {0} does not exist, can not remove track from it", zoneStart); } else { zones[zoneStart].Remove(section); } if (zoneEnd != zoneStart) { if (!zones.ContainsKey(zoneEnd)) { Debug.LogErrorFormat("Zone {0} does not exist, can not remove track from it", zoneStart); } else { zones[zoneEnd].Remove(section); } } sectionMap.Remove(section.UniqueID); section.UniqueID = 0; }
public override void OnTrackActivate(TrackSectionComponent track, ActivateInfo info) { bool selectStart; if (info.button == ActivateButton.LeftClick && HighlightClosestEnd(track.trackSection, true, info.hit.point, out selectStart)) { if (state == State.Idle) { fromSection = track.trackSection; fromEnd = !selectStart; ToState(State.SelectedStart); } else if (state == State.SelectedStart) { // Try to place the track var toSection = track.trackSection; var toEnd = !selectStart; if (!TryPlaceTrack(fromSection, fromEnd, toSection, toEnd)) { Debug.Log("Unable to place flex section!"); } ToState(State.Idle); } } else if (info.button == ActivateButton.RightClick) { ToState(State.Idle); } }
private void MakeRailTo(WaypointNode src, WaypointNode dst, int segmentID) { Vector3 a = src.transform.localPosition; Vector3 b = dst.transform.localPosition; Vector3 dir = (b - a).normalized; a += dir * src.radius; b -= dir * dst.radius; float distance = Vector3.Distance(a, b); waypoints.Add(a); waypoints.Add(b); TrackSection tsp = Instantiate(trackSectionPrefab); tsp.SetEnds(src, dst); tsp.Init(); tsp.transform.SetParent(renderersHolder); tsp.generator = this; tsp.SetPositions(new List <Vector3> { a, b }.ToArray()); }
List <Track> loadTracks() { List <Track> tracks = new List <Track>(); foreach (TextAsset file in trackFiles) { string text = file.text; text.Replace("\r", ""); string[] lines = text.Split(new char[] { '\n' }); string[] firstLineData = lines[0].Split(new char[] { ',' }); string trackName = firstLineData[1]; List <TrackSection> sections = new List <TrackSection>(); for (int i = 1; i < lines.Length; i++) { string line = lines[i]; string[] data = line.Split(new char[] { ',' }); int sectionLength = int.Parse(data[2]); int sectionRadius = int.Parse(data[3]); bool isStraight = sectionRadius == 0; TrackSection section = new TrackSection(isStraight, sectionLength, sectionRadius); sections.Add(section); } Track track = new Track(trackName, sections); tracks.Add(track); } return(tracks); }
public static TrackSectionReadingResult Read(BinaryReader reader, int startPosition, TrackSectionCommandOptions options) { var sections = new List <TrackSection>(); reader.BaseStream.Position = startPosition; var currentSection = new TrackSection(); while (true) { byte byte1 = reader.ReadByte(); byte byte2 = reader.ReadByte(); if (byte1 == 255 && byte2 == 255) { if (currentSection.Commands.Count > 0) { // section with length 0, but has commands sections.Add(currentSection); } break; } if (byte2 > 0) { // is command var argCount = TrackSectionCommandFactory.GetArgumentCountForCommand(byte2, options); short[] arguments = new short[argCount]; arguments[0] = byte1; for (int i = 1; i < argCount; i++) { arguments[i] = reader.ReadInt16(); } currentSection.Commands.Add(TrackSectionCommandFactory.Get(byte2, arguments)); continue; } // section currentSection.Length = byte1; currentSection.Curvature = reader.ReadInt16(); currentSection.Height = reader.ReadInt16(); currentSection.Flags = reader.ReadInt16(); currentSection.RightVergeWidth = reader.ReadByte(); currentSection.LeftVergeWidth = reader.ReadByte(); sections.Add(currentSection); currentSection = new TrackSection(); } int position = (int)reader.BaseStream.Position; return(new TrackSectionReadingResult(position, sections)); }
public Mesh CreateSectionMesh(TrackSection section) { int subdivisions = Mathf.CeilToInt(section.length / length); Mesh mesh = new Mesh(); Vector3[] vertices = new Vector3[2 + subdivisions * 2]; Vector2[] uvs = new Vector2[2 + subdivisions * 2]; int[] triangles = new int[subdivisions * 2 * 6]; int vertexIndex = 0; int triangleIndex = 0; float distance = 0.0f; for (int i = 0; i < subdivisions + 1; i++) { if (i == subdivisions) { distance = section.length; } Vector3 center = section.GetPositionOnTrack(distance).Vector3() - section.position.Vector3() + Vector3.up * 0.01f; Quaternion angle = Quaternion.AngleAxis(270f - (float)section.GetRotationOnTrack(distance).Degrees, Vector3.up); Vector3 tangent = angle * Vector3.forward; Vector3 right = Vector3.Cross(tangent, Vector3.up) * width * 0.5f; Vector3 left = -right; vertices[vertexIndex] = left + center; vertices[vertexIndex + 1] = right + center; uvs[vertexIndex] = new Vector2(0, i); uvs[vertexIndex + 1] = new Vector2(1, i); //Debug.Log("i: " + i + " center: " + center.ToString() + " left: " + left.ToString() + " right " + right.ToString()); if (i > 0.0f) { triangles[triangleIndex] = vertexIndex - 2; triangles[triangleIndex + 1] = vertexIndex + 1; triangles[triangleIndex + 2] = vertexIndex - 1; triangles[triangleIndex + 3] = vertexIndex - 2; triangles[triangleIndex + 4] = vertexIndex; triangles[triangleIndex + 5] = vertexIndex + 1; triangleIndex += 6; } vertexIndex += 2; distance += section.length / (float)subdivisions; } mesh.vertices = vertices; mesh.triangles = triangles; mesh.uv = uvs; //mesh.RecalculateNormals(); return(mesh); }
/// <summary> /// Creates a new section for this junction. Section is added to TrackCollection automatically. /// </summary> /// <param name="length"></param> /// <param name="curved"></param> /// <param name="angle"></param> /// <returns>TrackSection that was created</returns> public TrackSection CreateSection(float length, bool curved, float angle) { TrackSection newSection = new TrackSection(position, rotation, length, curved, angle); newSection.PreviousSectionIndex = m_prevSectionIndex; sections.Add(TrackCollection.instance.Add(newSection)); return(newSection); }
/// <summary> /// Calculate the closest distance to a track, as well as the 'longitude' along it. /// </summary> /// <param name="trackVectorSection">The vectorsection for which we want to know the distance</param> /// <param name="trackSection">The corresponding tracksection</param> /// <returns>Distance Squared to the track as well as length along track.</returns> /// <remarks>Partly the same code as in Traveller.cs, but here no culling, and we just want the distance. /// The math here is not perfect (it is quite difficult to calculate the distances to a curved line /// for all possibilities) but good enough. The math was designed (in Traveller.cs) to work well for close distances. /// Math is modified to prevent NaN and to combine straight and curved tracks.</remarks> DistanceLon CalcRealDistanceSquared(TrVectorSection trackVectorSection, TrackSection trackSection) { //Calculate the vector from start of track to the mouse Vector3 vectorToMouse = new Vector3 { X = storedMouseLocation.Location.X - trackVectorSection.X, Z = storedMouseLocation.Location.Z - trackVectorSection.Z }; vectorToMouse.X += (storedMouseLocation.TileX - trackVectorSection.TileX) * 2048; vectorToMouse.Z += (storedMouseLocation.TileZ - trackVectorSection.TileZ) * 2048; //Now rotate the vector such that a direction along the track is in a direction (x=0, z=1) vectorToMouse = Vector3.Transform(vectorToMouse, Matrix.CreateRotationY(-trackVectorSection.AY)); float lon, lat; if (trackSection.SectionCurve == null) { //Track is straight. In this coordinate system, the distance along track (lon) and orthogonal to track (lat) are easy. lon = vectorToMouse.Z; lat = vectorToMouse.X; } else { // make sure the vector is as if the vector section turns to the left. // The center of the curved track is now a (x=-radius, z=0), track starting at (0,0), pointing in positive Z if (trackSection.SectionCurve.Angle > 0) { vectorToMouse.X *= -1; } //make vector relative to center of curve. Track now starts at (radius,0) vectorToMouse.X += trackSection.SectionCurve.Radius; float radiansAlongCurve = (float)Math.Atan2(vectorToMouse.Z, vectorToMouse.X); //The following calculations make sense when close to the track. Otherwise they are not necessarily sensible, but at least well-defined. // Distance from mouse to circle through track section. lat = (float)Math.Sqrt(vectorToMouse.X * vectorToMouse.X + vectorToMouse.Z * vectorToMouse.Z) - trackSection.SectionCurve.Radius; lon = radiansAlongCurve * trackSection.SectionCurve.Radius; } float trackSectionLength = DrawTrackDB.GetLength(trackSection); if (lon < 0) { // distance from start of track return(new DistanceLon(lat * lat + lon * lon, 0)); } if (lon > trackSectionLength) { // distance from end of track return(new DistanceLon(lat * lat + (lon - trackSectionLength) * (lon - trackSectionLength), trackSectionLength)); //idem } // somewhere along track. Distance is only in lateral direction return(new DistanceLon(lat * lat, lon)); }
public TrackSection ToTrackSection() { TrackSection track = new TrackSection(Position.ToWorldPosition(), Rotation.ToWorldRotation(), Length, Curved, Angle); track.index = Index; track.NextSectionIndex = NextIndex; track.PreviousSectionIndex = PrevIndex; return(track); }
public void Place(TrackSection trackSection, float centerDistance) { FindComponents(); foreach (var wheel in wheels) { wheel.Place(trackSection, centerDistance + wheel.transform.localPosition.z); wheel.lockRotation = false; } OnPlaced(); }
public TrackSectionComponent PlaceAndRegisterSection(TrackSection section, bool autoconnect = false) { TrackDatabase.Instance.RegisterTrack(section); var component = PlaceSectionGameObject(section); if (autoconnect) { BasicTrackLayerTool.TryAutoConnect(section); } return(component); }
private TrackSectionComponent PlaceTrackGO(TrackSection section) { var go = new GameObject("Track Section"); var tsc = go.AddComponent <TrackSectionComponent>(); tsc.SetTrackSection(section, trackMaterial); var glow = go.AddComponent <GlowObject>(); glow.glowColor = Color.red; return(tsc); }
public void SetTrackSection(TrackSection section, Material trackMaterial) { var mesh = CreateSectionMesh(section); GetComponent <Renderer>().sharedMaterial = trackMaterial; GetComponent <MeshFilter>().sharedMesh = mesh; GetComponent <MeshCollider>().sharedMesh = mesh; transform.SetPositionAndRotation(section.Position, section.Rotation); trackSection = section; trackSection.Component = this; }
public override void OnTrackHover(TrackSectionComponent track, ActivateInfo info) { if (HighlightClosestEnd(track.trackSection, true, info.hit.point, out shouldInvertOnReposition)) { lastHighlightedSection = track.trackSection; } else { lastHighlightedSection = null; } }
/// <summary> /// Draw (part of) a tracksection (either curved or straight) /// </summary> /// <param name="drawArea">Area to draw upon</param> /// <param name="tvs">The vectorSection itself that needs to be drawn</param> /// <param name="colors">Colorscheme to use</param> /// <param name="startOffset">Do not draw the first startOffset meters in the section</param> /// <param name="stopOffset">Do not draw past stopOffset meters (draw all if stopOffset less than 0)</param> /// <remarks>Note that his is very similar to DrawTrackSection in class DrawTrackDB, but this one allows to draw partial sections</remarks> private void DrawTrackSection(DrawArea drawArea, TrackVectorSection tvs, ColorScheme colors, float startOffset, float stopOffset) { TrackSection trackSection = tsectionDat.TrackSections.Get(tvs.SectionIndex); if (trackSection == null) { return; } ref readonly WorldLocation thisLocation = ref tvs.Location;
/// <summary> /// Add a section to the collection. Junctions get detected automatically. /// </summary> /// <param name="section"></param> /// <returns>Index of section</returns> public int Add(TrackSection section) { sections[currentIndex] = section; TrackJunction j = section as TrackJunction; if (j != null) { junctions[currentIndex] = j; } section.index = currentIndex; return(currentIndex++); }
private static TrackSectionBoundaryModel GetBoundaryLocation(int index, TrackSection x, Dictionary <int, TrackSectionBoundaryModel> boundaryLocations) { var id = x.ConnectedBoundaries[index].Id; if (!boundaryLocations.TryGetValue(id, out var location)) { return(new TrackSectionBoundaryModel { Id = id, Location = GetDefaultPoint() }); } return(location); }
private void Serialize(TrackSection section) { if (section.UniqueID == 0) { throw new System.Exception("Invalid ID 0 for track section!"); } writer.Write(section.UniqueID); Serialize(section.Position); Serialize(section.Rotation); writer.Write(section.Length); writer.Write(section.Curved ? section.Curve : 0); }
bool InitTrackSection(TrVectorSection section, Vector3 xnaTreePosition, int tileX, int tileZ, float treeWidth) { trackSection = Viewer.Simulator.TSectionDat.TrackSections.Get(section.SectionIndex); if (trackSection == null) { return(false); } if (trackSection.SectionCurve != null) { return(InitTrackSectionCurved(tileX, tileZ, xnaTreePosition.X, -xnaTreePosition.Z, section, treeWidth)); } return(InitTrackSectionStraight(tileX, tileZ, xnaTreePosition.X, -xnaTreePosition.Z, section, treeWidth)); }
public TrackSectionModel(TrackSection section, IEnumerable <int> threatPositions, IDictionary <int, TrackBreakpointType> breakpoints) { TrackSpaces = Enumerable.Range(1 + 5 * (section.DistanceFromShip - 1), section.Length) .Reverse() .Select(space => new TrackSpaceModel { Space = space, HasAnyThreats = threatPositions.Contains(space), Breakpoint = breakpoints.ContainsKey(space) ? breakpoints[space] : (TrackBreakpointType?)null }) .ToList(); DistanceFromShip = section.DistanceFromShip; }