public LightDefinition(Color24 ambientColor, Color24 diffuseColor, Vector3 lightPosition, int time, double cab) { AmbientColor = ambientColor; DiffuseColor = diffuseColor; LightPosition = lightPosition; Time = time; CabBrightness = cab; }
internal Material() { this.Color = new Color32(255, 255, 255, 255); this.EmissiveColor = new Color24(0, 0, 0); this.EmissiveColorUsed = false; this.TransparentColor = new Color24(0, 0, 0); this.TransparentColorUsed = false; this.DaytimeTexture = null; this.NighttimeTexture = null; this.BlendMode = World.MeshMaterialBlendMode.Normal; this.GlowAttenuationData = 0; }
internal Material(Material Prototype) { this.Color = Prototype.Color; this.EmissiveColor = Prototype.EmissiveColor; this.EmissiveColorUsed = Prototype.EmissiveColorUsed; this.TransparentColor = Prototype.TransparentColor; this.TransparentColorUsed = Prototype.TransparentColorUsed; this.DaytimeTexture = Prototype.DaytimeTexture; this.NighttimeTexture = Prototype.NighttimeTexture; this.BlendMode = Prototype.BlendMode; this.GlowAttenuationData = Prototype.GlowAttenuationData; }
internal Material() { this.Color = new Color32(255, 255, 255, 255); this.EmissiveColor = new Color24(0, 0, 0); this.EmissiveColorUsed = false; this.TransparentColor = new Color24(0, 0, 0); this.TransparentColorUsed = false; this.DaytimeTexture = null; this.NighttimeTexture = null; this.BlendMode = World.MeshMaterialBlendMode.Normal; this.GlowAttenuationData = 0; this.TextColor = System.Drawing.Color.Black; this.BackgroundColor = System.Drawing.Color.White; this.TextPadding = new Vector2(0, 0); this.Font = "Arial"; }
// --- apply transparent color --- /// <summary>Applies a transparent color onto a texture.</summary> /// <param name="texture">The original texture.</param> /// <param name="color">The transparent color, or a null reference.</param> /// <returns>The texture with the transparent color applied.</returns> /// <exception cref="System.NotSupportedException">Raised when the number of bits per pixel in the texture is not supported.</exception> internal static Texture ApplyTransparentColor(Texture texture, Color24? color) { if (color == null) { return texture; } else if (texture.BitsPerPixel == 32) { int width = texture.Width; int height = texture.Height; byte[] source = texture.Bytes; byte[] target = new byte[4 * width * height]; byte r = color.Value.R; byte g = color.Value.G; byte b = color.Value.B; if (source[0] == r && source[1] == g && source[2] == b) { target[0] = 128; target[1] = 128; target[2] = 128; target[3] = 0; } else { target[0] = source[0]; target[1] = source[1]; target[2] = source[2]; target[3] = source[3]; } for (int i = 4; i < source.Length; i += 4) { if (source[i] == r && source[i + 1] == g && source[i + 2] == b) { target[i + 0] = target[i - 4]; target[i + 1] = target[i - 3]; target[i + 2] = target[i - 2]; target[i + 3] = 0; } else { target[i + 0] = source[i + 0]; target[i + 1] = source[i + 1]; target[i + 2] = source[i + 2]; target[i + 3] = source[i + 3]; } } return new Texture(width, height, 32, target); } else { throw new NotSupportedException(); } }
/// <summary>Creates a new color.</summary> /// <param name="color">The solid color.</param> /// <remarks>The alpha component is set to full opacity.</remarks> public Color128(Color24 color) { this.R = color.R; this.G = color.G; this.B = color.B; this.A = 1.0f; }
/// <summary>Creates a new color.</summary> /// <param name="color">The solid color.</param> /// <param name="a">The alpha component.</param> public Color128(Color24 color, float a) { this.R = color.R; this.G = color.G; this.B = color.B; this.A = a; }
/// <summary>Creates a new color.</summary> /// <param name="color">The solid color.</param> /// <remarks>The alpha component is set to full opacity.</remarks> public Color32(Color24 color) { this.R = color.R; this.G = color.G; this.B = color.B; this.A = 255; }
/// <summary>Creates a new color.</summary> /// <param name="color">The solid color.</param> /// <param name="a">The alpha component.</param> public Color32(Color24 color, byte a) { this.R = color.R; this.G = color.G; this.B = color.B; this.A = a; }
internal Fog(float Start, float End, Color24 Color, double TrackPosition) { this.Start = Start; this.End = End; this.Color = Color; this.TrackPosition = TrackPosition; }
// reset internal static void Reset() { LoadTexturesImmediately = LoadTextureImmediatelyMode.NotYet; Objects = new Object[256]; ObjectCount = 0; StaticOpaque = new ObjectGroup[] { }; StaticOpaqueForceUpdate = true; DynamicOpaque = new ObjectList(); DynamicAlpha = new ObjectList(); OverlayOpaque = new ObjectList(); OverlayAlpha = new ObjectList(); OptionLighting = true; OptionAmbientColor = new Color24(160, 160, 160); OptionDiffuseColor = new Color24(160, 160, 160); OptionLightPosition = new World.Vector3Df(0.223606797749979f, 0.86602540378444f, -0.447213595499958f); OptionLightingResultingAmount = 1.0f; OptionClock = false; OptionBrakeSystems = false; }
/// <summary>Checks whether two colors are equal.</summary> /// <param name="a">The first color.</param> /// <param name="b">The second color.</param> /// <returns>Whether the two colors are equal.</returns> public bool Equals(Color24 a, Color24 b) { return(a.R == b.R & a.G == b.G & a.B == b.B); }
// --- constructors --- /// <summary>Creates new texture parameters.</summary> /// <param name="clipRegion">The region in the texture to be extracted, or a null reference for the entire texture.</param> /// <param name="transparentColor">The color in the texture that should become transparent, or a null reference for no transparent color.</param> public TextureParameters(TextureClipRegion clipRegion, Color24? transparentColor) { this.MyClipRegion = clipRegion; this.MyTransparentColor = transparentColor; }
// try parse hex color internal static bool TryParseHexColor(string Expression, out Color24 Color) { if (Expression.StartsWith("#")) { string a = Expression.Substring(1).TrimStart(); int x; if (int.TryParse(a, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out x)) { int r = (x >> 16) & 0xFF; int g = (x >> 8) & 0xFF; int b = x & 0xFF; if (r >= 0 & r <= 255 & g >= 0 & g <= 255 & b >= 0 & b <= 255) { Color = new Color24((byte)r, (byte)g, (byte)b); return true; } else { Color = new Color24(0, 0, 255); return false; } } else { Color = new Color24(0, 0, 255); return false; } } else { Color = new Color24(0, 0, 255); return false; } }
/// <summary>Interpolates between two Color24 values using a simple Cosine algorithm</summary> /// <param name="Color1">The first color</param> /// <param name="Color2">The second color</param> /// <param name="mu">The position on the curve of the new color</param> /// <returns>The interpolated color</returns> public static Color24 CosineInterpolate(Color24 Color1, Color24 Color2, double mu) { var mu2 = (1 - System.Math.Cos(mu * System.Math.PI)) / 2; return new Color24((byte)(Color1.R * (1 - mu2) + Color2.R * mu2), (byte)(Color1.G * (1 - mu2) + Color2.G * mu2), (byte)(Color1.B * (1 - mu2) + Color2.B * mu2)); }
// parse panel config internal static void ParsePanel2Config(string TrainPath, System.Text.Encoding Encoding, TrainManager.Train Train) { // read lines System.Globalization.CultureInfo Culture = System.Globalization.CultureInfo.InvariantCulture; string FileName = OpenBveApi.Path.CombineFile(TrainPath, "panel2.cfg"); string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); for (int i = 0; i < Lines.Length; i++) { Lines[i] = Lines[i].Trim(); int j = Lines[i].IndexOf(';'); if (j >= 0) { Lines[i] = Lines[i].Substring(0, j).TrimEnd(); } } // initialize double DriverX = Train.Cars[Train.DriverCar].DriverX; double DriverY = Train.Cars[Train.DriverCar].DriverY; double DriverZ = Train.Cars[Train.DriverCar].DriverZ; double PanelResolution = 1024.0; double PanelLeft = 0.0, PanelRight = 1024.0; double PanelTop = 0.0, PanelBottom = 1024.0; double PanelCenterX = 0.0, PanelCenterY = 512.0; double PanelOriginX = 0.0, PanelOriginY = 512.0; double PanelBitmapWidth = 1024.0, PanelBitmapHeight = 1024.0; string PanelDaytimeImage = null; string PanelNighttimeImage = null; Color24 PanelTransparentColor = new Color24(0, 0, 255); // parse lines for panel for (int i = 0; i < Lines.Length; i++) { if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // panel case "this": i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "resolution": double pr = 0.0; if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out pr)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (pr > 100) { PanelResolution = pr; } else { //Parsing very low numbers (Probable typos) for the panel resolution causes some very funky graphical bugs //Cap the minimum panel resolution at 100px wide (BVE1 panels are 480px wide, so this is probably a safe minimum) Interface.AddMessage(Interface.MessageType.Error, false, "A panel resolution of less than 10px was given at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "left": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelLeft)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line" + (i + 1).ToString(Culture) + " in " + FileName); } break; case "right": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelRight)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "top": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelTop)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "bottom": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out PanelBottom)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { PanelDaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelDaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelDaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); PanelDaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { PanelNighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(PanelNighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + PanelNighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); PanelNighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out PanelTransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "center": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out PanelCenterX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out PanelCenterY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } case "origin": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out PanelOriginX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out PanelOriginY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } } i++; } i--; break; } } } } { // camera restriction double WorldWidth, WorldHeight; if (Screen.Width >= Screen.Height) { WorldWidth = 2.0 * Math.Tan(0.5 * World.HorizontalViewingAngle) * EyeDistance; WorldHeight = WorldWidth / World.AspectRatio; } else { WorldHeight = 2.0 * Math.Tan(0.5 * World.VerticalViewingAngle) * EyeDistance / World.AspectRatio; WorldWidth = WorldHeight * World.AspectRatio; } double x0 = (PanelLeft - PanelCenterX) / PanelResolution; double x1 = (PanelRight - PanelCenterX) / PanelResolution; double y0 = (PanelCenterY - PanelBottom) / PanelResolution * World.AspectRatio; double y1 = (PanelCenterY - PanelTop) / PanelResolution * World.AspectRatio; World.CameraRestrictionBottomLeft = new Vector3(x0 * WorldWidth, y0 * WorldHeight, EyeDistance); World.CameraRestrictionTopRight = new Vector3(x1 * WorldWidth, y1 * WorldHeight, EyeDistance); Train.Cars[Train.DriverCar].DriverYaw = Math.Atan((PanelCenterX - PanelOriginX) * WorldWidth / PanelResolution); Train.Cars[Train.DriverCar].DriverPitch = Math.Atan((PanelOriginY - PanelCenterY) * WorldWidth / PanelResolution); } // create panel if (PanelDaytimeImage != null) { if (!System.IO.File.Exists(PanelDaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "The daytime panel bitmap could not be found in " + FileName); PanelDaytimeImage = null; } else { Textures.Texture tday; Textures.RegisterTexture(PanelDaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(PanelTransparentColor.R, PanelTransparentColor.G, PanelTransparentColor.B)), out tday); Textures.Texture tnight = null; if (PanelNighttimeImage != null) { if (!System.IO.File.Exists(PanelNighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "The nighttime panel bitmap could not be found in " + FileName); PanelNighttimeImage = null; } else { Textures.RegisterTexture(PanelNighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(PanelTransparentColor.R, PanelTransparentColor.G, PanelTransparentColor.B)), out tnight); } } OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); }); PanelBitmapWidth = (double)tday.Width; PanelBitmapHeight = (double)tday.Height; CreateElement(Train, 0.0, 0.0, PanelBitmapWidth, PanelBitmapHeight, 0.5, 0.5, 0.0, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, new Color32(255, 255, 255, 255), false); } } // parse lines for rest double invfac = Lines.Length == 0 ? Loading.TrainProgressCurrentWeight : Loading.TrainProgressCurrentWeight / (double)Lines.Length; for (int i = 0; i < Lines.Length; i++) { Loading.TrainProgress = Loading.TrainProgressCurrentSum + invfac * (double)i; if ((i & 7) == 0) { System.Threading.Thread.Sleep(1); if (Loading.Cancel) return; } if (Lines[i].Length > 0) { if (Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal)) { string Section = Lines[i].Substring(1, Lines[i].Length - 2).Trim(); switch (Section.ToLowerInvariant()) { // pilotlamp case "pilotlamp": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color24 TransparentColor = new Color24(0, 0, 255); int Layer = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null) { Textures.Texture tday; Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday); Textures.Texture tnight = null; if (NighttimeImage != null) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight); } OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); }); int w = tday.Width; int h = tday.Height; int j = CreateElement(Train, LocationX, LocationY, w, h, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, new Color32(255, 255, 255, 255), false); string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f + " 1 == --"); } } break; // needle case "needle": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color32 Color = new Color32(255, 255, 255, 255); Color24 TransparentColor = new Color24(0, 0, 255); double OriginX = -1.0, OriginY = -1.0; bool OriginDefined = false; double Layer = 0.0, Radius = 0.0; double InitialAngle = -2.0943951023932, LastAngle = 2.0943951023932; double Minimum = 0.0, Maximum = 1000.0; double NaturalFrequency = -1.0, DampingRatio = -1.0; bool Backstop = false; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterX is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterY is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; case "radius": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is expected to be non-zero in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "color": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out Color)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "origin": { int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out OriginX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out OriginY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); OriginX = -OriginX; } OriginDefined = true; } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } break; case "initialangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out InitialAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "lastangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out LastAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "minimum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "maximum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "naturalfreq": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out NaturalFrequency)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (NaturalFrequency < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NaturalFrequency = -NaturalFrequency; } break; case "dampingratio": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out DampingRatio)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (DampingRatio < 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DampingRatio = -DampingRatio; } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "backstop": if (Value.Length != 0 && Value.ToLowerInvariant() == "true" || Value == "1") { Backstop = true; } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null) { Textures.Texture tday; Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday); Textures.Texture tnight = null; if (NighttimeImage != null) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight); } OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); }); double w = (double)tday.Width; double h = (double)tday.Height; if (!OriginDefined) { OriginX = 0.5 * w; OriginY = 0.5 * h; } double ox = OriginX / w; double oy = OriginY / h; double n = Radius == 0.0 | OriginY == 0.0 ? 1.0 : Radius / OriginY; double nx = n * w; double ny = n * h; int j = CreateElement(Train, LocationX - ox * nx, LocationY - oy * ny, nx, ny, ox, oy, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, Color, false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection = new Vector3(0.0, 0.0, -1.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection = new Vector3(1.0, 0.0, 0.0); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateYDirection = Vector3.Cross(Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDirection, Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateXDirection); string f; switch (Subject.ToLowerInvariant()) { case "hour": f = "0.000277777777777778 time * floor"; break; case "min": f = "0.0166666666666667 time * floor"; break; case "sec": f = "time floor"; break; default: f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); break; } //Convert angles from degrees to radians InitialAngle *= 0.0174532925199433; LastAngle *= 0.0174532925199433; double a0 = (InitialAngle * Maximum - LastAngle * Minimum) / (Maximum - Minimum); double a1 = (LastAngle - InitialAngle) / (Maximum - Minimum); f += " " + a1.ToString(Culture) + " * " + a0.ToString(Culture) + " +"; if (NaturalFrequency >= 0.0 & DampingRatio >= 0.0) { Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZDamping = new ObjectManager.Damping(NaturalFrequency, DampingRatio); } Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); if (Backstop) { Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction.Minimum = InitialAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].RotateZFunction.Maximum = LastAngle; } } } break; case "lineargauge": { string Subject = "true"; int Width = 0; Vector2 Direction = new Vector2(1,0); double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; double Minimum = 0.0, Maximum = 0.0; Color24 TransparentColor = new Color24(0, 0, 255); int Layer = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "minimum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "maximum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "width": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "direction": { string[] s = Value.Split(','); if (s.Length == 2) { double x, y; if (!double.TryParse(s[0], System.Globalization.NumberStyles.Float, Culture, out x)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in LinearGauge Direction at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else if (!double.TryParse(s[1], System.Globalization.NumberStyles.Float, Culture, out y)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in LinearGauge Direction at line " + (i + 1).ToString(Culture) + " in file " + FileName); } else { Direction = new Vector2(x, y); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Exactly 2 arguments are expected in LinearGauge Direction at line " + (i + 1).ToString(Culture) + " in file " + FileName); } } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null) { Textures.Texture tday; Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday); Textures.Texture tnight = null; if (NighttimeImage != null) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(null, new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight); } OpenBVEGame.RunInRenderThread(() => { Textures.LoadTexture(tday, Textures.OpenGlTextureWrapMode.ClampClamp); }); int w = tday.Width; int h = tday.Height; int j = CreateElement(Train, LocationX, LocationY, w, h, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday, tnight, new Color32(255, 255, 255, 255), false); if (Maximum < Minimum) { Interface.AddMessage(Interface.MessageType.Error, false, "Maximum value must be greater than minimum value " + Section + " in " + FileName); break; } string tf = GetInfixFunction(Train, Subject, Minimum, Maximum, Width, tday.Width, Section + " in " + FileName); if (tf != String.Empty) { Train.Cars[Train.DriverCar].CarSections[0].Elements[j].TextureShiftXDirection = Direction; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].TextureShiftXFunction = FunctionScripts.GetFunctionScriptFromInfixNotation(tf); } } } break; // digitalnumber case "digitalnumber": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; string DaytimeImage = null, NighttimeImage = null; Color24 TransparentColor = new Color24(0, 0, 255); double Layer = 0.0; int Interval = 0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "Left is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Top is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "daytimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { DaytimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(DaytimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + DaytimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); DaytimeImage = null; } } break; case "nighttimeimage": if (!System.IO.Path.HasExtension(Value)) Value += ".bmp"; if (Interface.ContainsInvalidPathChars(Value)) { Interface.AddMessage(Interface.MessageType.Error, false, "FileName contains illegal characters in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { NighttimeImage = OpenBveApi.Path.CombineFile(TrainPath, Value); if (!System.IO.File.Exists(NighttimeImage)) { Interface.AddMessage(Interface.MessageType.Error, true, "FileName " + NighttimeImage + " could not be found in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); NighttimeImage = null; } } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "interval": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Interval)) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Interval <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is expected to be non-negative in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (DaytimeImage == null) { Interface.AddMessage(Interface.MessageType.Error, false, "DaytimeImage is required to be specified in " + Section + " in " + FileName); } if (Interval <= 0) { Interface.AddMessage(Interface.MessageType.Error, false, "Interval is required to be specified in " + Section + " in " + FileName); } // create element if (DaytimeImage != null & Interval > 0) { int wday, hday; Program.CurrentHost.QueryTextureDimensions(DaytimeImage, out wday, out hday); if (wday > 0 & hday > 0) { int nday = hday / Interval; Textures.Texture[] tday = new Textures.Texture[nday]; Textures.Texture[] tnight; for (int k = 0; k < nday; k++) { Textures.RegisterTexture(DaytimeImage, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(0, k * Interval, wday, Interval), new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tday[k]); } if (NighttimeImage != null) { int wnight, hnight; Program.CurrentHost.QueryTextureDimensions(NighttimeImage, out wnight, out hnight); int nnight = hnight / Interval; if (nnight > nday) nnight = nday; tnight = new Textures.Texture[nday]; for (int k = 0; k < nnight; k++) { Textures.RegisterTexture(NighttimeImage, new OpenBveApi.Textures.TextureParameters(new OpenBveApi.Textures.TextureClipRegion(0, k * Interval, wday, Interval), new Color24(TransparentColor.R, TransparentColor.G, TransparentColor.B)), out tnight[k]); } for (int k = nnight; k < nday; k++) { tnight[k] = null; } } else { tnight = new Textures.Texture[nday]; for (int k = 0; k < nday; k++) { tnight[k] = null; } } int j = -1; for (int k = 0; k < tday.Length; k++) { int l = CreateElement(Train, LocationX, LocationY, (double)wday, (double)Interval, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, tday[k], tnight[k], new Color32(255, 255, 255, 255), k != 0); if (k == 0) j = l; } string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); } } } break; // digitalgauge case "digitalgauge": { string Subject = "true"; double LocationX = 0.0, LocationY = 0.0; Color32 Color = new Color32(0, 0, 0, 255); double Radius = 0.0; int Layer = 0; double InitialAngle = -2.0943951023932, LastAngle = 2.0943951023932; double Minimum = 0.0, Maximum = 1000.0; double Step = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "subject": Subject = Value; break; case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterX is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "CenterY is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "radius": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Radius)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is expected to be non-zero in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 16.0; } break; case "color": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out Color)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "initialangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out InitialAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { InitialAngle *= 0.0174532925199433; } break; case "lastangle": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out LastAngle)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInDegrees is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else { LastAngle *= 0.0174532925199433; } break; case "minimum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Minimum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "maximum": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Maximum)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "step": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Step)) { Interface.AddMessage(Interface.MessageType.Error, false, "Value is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseIntVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; if (Radius == 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Radius is required to be non-zero in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (Minimum == Maximum) { Interface.AddMessage(Interface.MessageType.Error, false, "Minimum and Maximum must not be equal in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); Radius = 0.0; } if (Math.Abs(InitialAngle - LastAngle) > 6.28318531) { Interface.AddMessage(Interface.MessageType.Warning, false, "The absolute difference between InitialAngle and LastAngle exceeds 360 degrees in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (Radius != 0.0) { // create element int j = CreateElement(Train, LocationX - Radius, LocationY - Radius, 2.0 * Radius, 2.0 * Radius, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, null, null, Color, false); InitialAngle = InitialAngle + Math.PI; LastAngle = LastAngle + Math.PI; double x0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.X; double y0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Y; double z0 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[0].Coordinates.Z; double x1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.X; double y1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Y; double z1 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[1].Coordinates.Z; double x2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.X; double y2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Y; double z2 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[2].Coordinates.Z; double x3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.X; double y3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Y; double z3 = Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh.Vertices[3].Coordinates.Z; double cx = 0.25 * (x0 + x1 + x2 + x3); double cy = 0.25 * (y0 + y1 + y2 + y3); double cz = 0.25 * (z0 + z1 + z2 + z3); World.Vertex[] vertices = new World.Vertex[11]; int[][] faces = new int[][] { new int[] { 0, 1, 2 }, new int[] { 0, 3, 4 }, new int[] { 0, 5, 6 }, new int[] { 0, 7, 8 }, new int[] { 0, 9, 10 } }; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].States[0].Object.Mesh = new World.Mesh(vertices, faces, Color); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDClockwiseWinding = InitialAngle <= LastAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDInitialAngle = InitialAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDLastAngle = LastAngle; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDVectors = new Vector3[] { new Vector3(x0, y0, z0), new Vector3(x1, y1, z1), new Vector3(x2, y2, z2), new Vector3(x3, y3, z3), new Vector3(cx, cy, cz) }; string f = GetStackLanguageFromSubject(Train, Subject, Section + " in " + FileName); double a0 = (InitialAngle * Maximum - LastAngle * Minimum) / (Maximum - Minimum); double a1 = (LastAngle - InitialAngle) / (Maximum - Minimum); if (Step == 1.0) { f += " floor"; } else if (Step != 0.0) { string s = (1.0 / Step).ToString(Culture); string t = Step.ToString(Culture); f += " " + s + " * floor " + t + " *"; } f += " " + a1.ToString(Culture) + " " + a0.ToString(Culture) + " fma"; Train.Cars[Train.DriverCar].CarSections[0].Elements[j].LEDFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation(f); } else { Interface.AddMessage(Interface.MessageType.Error, false, "Radius is required to be specified in " + Section + " in " + FileName); } } break; // timetable case "timetable": { double LocationX = 0.0, LocationY = 0.0; double Width = 0.0, Height = 0.0; //We read the transparent color for the timetable from the config file, but it is never used //TODO: Fix or depreciate?? Color24 TransparentColor = new Color24(0, 0, 255); double Layer = 0.0; i++; while (i < Lines.Length && !(Lines[i].StartsWith("[", StringComparison.Ordinal) & Lines[i].EndsWith("]", StringComparison.Ordinal))) { int j = Lines[i].IndexOf('='); if (j >= 0) { string Key = Lines[i].Substring(0, j).TrimEnd(); string Value = Lines[i].Substring(j + 1).TrimStart(); switch (Key.ToLowerInvariant()) { case "location": int k = Value.IndexOf(','); if (k >= 0) { string a = Value.Substring(0, k).TrimEnd(); string b = Value.Substring(k + 1).TrimStart(); if (a.Length != 0 && !Interface.TryParseDoubleVb6(a, out LocationX)) { Interface.AddMessage(Interface.MessageType.Error, false, "X is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } if (b.Length != 0 && !Interface.TryParseDoubleVb6(b, out LocationY)) { Interface.AddMessage(Interface.MessageType.Error, false, "Y is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } } else { Interface.AddMessage(Interface.MessageType.Error, false, "Two arguments are expected in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "width": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Width)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Width <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is required to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "height": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Height)) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } else if (Height <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "ValueInPixels is required to be positive in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "transparentcolor": if (Value.Length != 0 && !Interface.TryParseHexColor(Value, out TransparentColor)) { Interface.AddMessage(Interface.MessageType.Error, false, "HexColor is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; case "layer": if (Value.Length != 0 && !Interface.TryParseDoubleVb6(Value, out Layer)) { Interface.AddMessage(Interface.MessageType.Error, false, "LayerIndex is invalid in " + Key + " in " + Section + " at line " + (i + 1).ToString(Culture) + " in " + FileName); } break; } } i++; } i--; // create element if (Width <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Width is required to be specified in " + Section + " in " + FileName); } if (Height <= 0.0) { Interface.AddMessage(Interface.MessageType.Error, false, "Height is required to be specified in " + Section + " in " + FileName); } if (Width > 0.0 & Height > 0.0) { int j = CreateElement(Train, LocationX, LocationY, Width, Height, 0.5, 0.5, (double)Layer * StackDistance, PanelResolution, PanelLeft, PanelRight, PanelTop, PanelBottom, PanelBitmapWidth, PanelBitmapHeight, PanelCenterX, PanelCenterY, PanelOriginX, PanelOriginY, DriverX, DriverY, DriverZ, null, null, new Color32(255, 255, 255, 255), false); Train.Cars[Train.DriverCar].CarSections[0].Elements[j].StateFunction = FunctionScripts.GetFunctionScriptFromPostfixNotation("timetable"); Timetable.AddObjectForCustomTimetable(Train.Cars[Train.DriverCar].CarSections[0].Elements[j]); } } break; } } } } }
/// <summary>Interpolates between two Color24 values using a simple Cosine algorithm</summary> /// <param name="Color1">The first color</param> /// <param name="Color2">The second color</param> /// <param name="mu">The position on the curve of the new color</param> /// <returns>The interpolated color</returns> public static Color24 CosineInterpolate(Color24 Color1, Color24 Color2, double mu) { var mu2 = (1 - System.Math.Cos(mu * System.Math.PI)) / 2; return(new Color24((byte)(Color1.R * (1 - mu2) + Color2.R * mu2), (byte)(Color1.G * (1 - mu2) + Color2.G * mu2), (byte)(Color1.B * (1 - mu2) + Color2.B * mu2))); }