diff --git a/assets/Languages/en-US.xlf b/assets/Languages/en-US.xlf
index 5d47dc075..da29a4428 100755
--- a/assets/Languages/en-US.xlf
+++ b/assets/Languages/en-US.xlf
@@ -123,6 +123,9 @@
Please check your fullscreen resolution settings.
+
+ Please first select a route file to begin.
+
The selected route is corrupt: \r\n WithTrack section missing.
@@ -209,6 +212,18 @@
Train
+
+ Choose Train...
+
+
+ Do you wish to use the default train?
+
+
+ No
+
+
+ Yes
+
Selection
diff --git a/assets/Menu/icon_disk.png b/assets/Menu/icon_disk.png
index 3be9af00d..2213b4d37 100644
Binary files a/assets/Menu/icon_disk.png and b/assets/Menu/icon_disk.png differ
diff --git a/assets/Menu/icon_folder.png b/assets/Menu/icon_folder.png
index a567e6577..d6302b49e 100644
Binary files a/assets/Menu/icon_folder.png and b/assets/Menu/icon_folder.png differ
diff --git a/assets/Menu/icon_route.png b/assets/Menu/icon_route.png
index 3b02402d9..8763b297d 100644
Binary files a/assets/Menu/icon_route.png and b/assets/Menu/icon_route.png differ
diff --git a/assets/Menu/icon_train.png b/assets/Menu/icon_train.png
index 003902f51..bcfec4fa8 100644
Binary files a/assets/Menu/icon_train.png and b/assets/Menu/icon_train.png differ
diff --git a/assets/Menu/please_select.png b/assets/Menu/please_select.png
new file mode 100644
index 000000000..c749bbae3
Binary files /dev/null and b/assets/Menu/please_select.png differ
diff --git a/assets/Shaders/default.frag b/assets/Shaders/default.frag
index b386209e4..8b0ec73c8 100644
--- a/assets/Shaders/default.frag
+++ b/assets/Shaders/default.frag
@@ -1,11 +1,10 @@
-#version 150
-
+#version 150 core
in vec4 oViewPos;
in vec2 oUv;
in vec4 oColor;
in vec4 oLightResult;
-
-uniform bool uIsTexture;
+uniform int uAlphaFunction;
+uniform float uAlphaComparison;
uniform sampler2D uTexture;
uniform int uMaterialFlags;
uniform float uBrightness;
@@ -16,14 +15,12 @@ uniform float uFogEnd;
uniform vec3 uFogColor;
uniform float uFogDensity;
uniform bool uFogIsLinear;
+out vec4 fragColor;
void main(void)
{
- vec4 finalColor = vec4(oColor.rgb, 1.0);
- if(uIsTexture)
- {
- finalColor *= texture2D(uTexture, oUv);
- }
+ vec4 finalColor = vec4(oColor.rgb, 1.0) * texture(uTexture, oUv);
+
if((uMaterialFlags & 1) == 0 && (uMaterialFlags & 4) == 0)
{
//Material is not emissive and lighting is enabled, so multiply by brightness
@@ -33,6 +30,38 @@ void main(void)
//Apply the lighting results *after* the final color has been calculated
finalColor *= oLightResult;
+ /*
+ * NOTES:
+ * Unused alpha functions must not be added to the shader
+ * This has a nasty affect on framerates
+ *
+ * A switch case block is also ~30% slower than the else-if
+ *
+ * Numbers used are those from the GL.AlphaFunction enum to allow
+ * for direct casts
+ */
+ if(uAlphaFunction == 513) // Less
+ {
+ if(finalColor.a >= uAlphaComparison)
+ {
+ discard;
+ }
+ }
+ else if(uAlphaFunction == 514) // Equal
+ {
+ if(!(abs(finalColor.a - 1.0) < 0.00001))
+ {
+ discard;
+ }
+ }
+ else if(uAlphaFunction == 516) // Greater
+ {
+ if(finalColor.a <= uAlphaComparison)
+ {
+ discard;
+ }
+ }
+
// Fog
float fogFactor = 1.0;
@@ -48,5 +77,5 @@ void main(void)
}
}
- gl_FragData[0] = vec4(mix(uFogColor, finalColor.rgb, fogFactor), finalColor.a);
+ fragColor = vec4(mix(uFogColor, finalColor.rgb, fogFactor), finalColor.a);
}
diff --git a/assets/Shaders/default.vert b/assets/Shaders/default.vert
index 8727bcced..80981c2d1 100644
--- a/assets/Shaders/default.vert
+++ b/assets/Shaders/default.vert
@@ -1,4 +1,4 @@
-#version 150
+#version 150 core
precision highp int;
struct Light
diff --git a/assets/Shaders/picking.frag b/assets/Shaders/picking.frag
index 5a4269433..bafa4e2e3 100644
--- a/assets/Shaders/picking.frag
+++ b/assets/Shaders/picking.frag
@@ -1,8 +1,8 @@
-#version 150
-
+#version 150 core
uniform int uObjectIndex;
+out vec4 fragColor;
void main(void)
{
- gl_FragData[0].r = float(uObjectIndex);
+ fragColor.r = float(uObjectIndex);
}
diff --git a/assets/Shaders/rectangle.frag b/assets/Shaders/rectangle.frag
new file mode 100644
index 000000000..efb5f2473
--- /dev/null
+++ b/assets/Shaders/rectangle.frag
@@ -0,0 +1,50 @@
+#version 150 core
+in vec2 textureCoord;
+uniform int uAlphaFunction;
+uniform float uAlphaComparison;
+uniform bool uRectangleHasColour;
+uniform vec4 uColor;
+uniform sampler2D uTexture;
+out vec4 fragColor;
+
+void main(void)
+{
+ vec4 textureColour = texture(uTexture, textureCoord);
+
+ vec4 finalColor = textureColour * uColor;
+
+ /*
+ * NOTES:
+ * Unused alpha functions must not be added to the shader
+ * This has a nasty affect on framerates
+ *
+ * A switch case block is also ~30% slower than the else-if
+ *
+ * Numbers used are those from the GL.AlphaFunction enum to allow
+ * for direct casts
+ */
+ if(uAlphaFunction == 513) // Less
+ {
+ if(finalColor.a >= uAlphaComparison)
+ {
+ discard;
+ }
+ }
+ else if(uAlphaFunction == 514) // Equal
+ {
+ if(!(abs(finalColor.a - 1.0) < 0.00001))
+ {
+ discard;
+ }
+ }
+ else if(uAlphaFunction == 516) // Greater
+ {
+ if(finalColor.a <= uAlphaComparison)
+ {
+ discard;
+ }
+ }
+
+ fragColor = finalColor;
+
+}
diff --git a/assets/Shaders/rectangle.vert b/assets/Shaders/rectangle.vert
new file mode 100644
index 000000000..49ece19d9
--- /dev/null
+++ b/assets/Shaders/rectangle.vert
@@ -0,0 +1,44 @@
+#version 150 core
+uniform mat4 uCurrentProjectionMatrix;
+uniform mat4 uCurrentModelViewMatrix;
+uniform vec2 uPoint;
+uniform vec2 uSize;
+uniform vec2 uCoordinates;
+out vec2 textureCoord;
+vec4 viewPos = vec4(0,0,0,0);
+
+void main()
+{
+ if(gl_VertexID == 0)
+ {
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x, uPoint.y, 0), 1.0);
+ textureCoord = vec2(0,0);
+ }
+ else if (gl_VertexID == 1)
+ {
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x + uSize.x, uPoint.y, 0), 1.0);
+ textureCoord = vec2(uCoordinates.x,0);
+ }
+ else if (gl_VertexID == 2)
+ {
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x + uSize.x, uPoint.y + uSize.y, 0), 1.0);
+ textureCoord = uCoordinates;
+ }
+ else if (gl_VertexID == 3)
+ {
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x, uPoint.y, 0), 1.0);
+ textureCoord = vec2(0,0);
+ }
+ else if (gl_VertexID == 4)
+ {
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x, uPoint.y + uSize.y, 0), 1.0);
+ textureCoord = vec2(0, uCoordinates.y);
+ }
+ else if (gl_VertexID == 5)
+ {
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x + uSize.x, uPoint.y + uSize.y, 0), 1.0);
+ textureCoord = uCoordinates;
+ }
+
+ gl_Position = uCurrentProjectionMatrix * viewPos;
+}
diff --git a/assets/Shaders/text.vert b/assets/Shaders/text.vert
new file mode 100644
index 000000000..8e64a5de8
--- /dev/null
+++ b/assets/Shaders/text.vert
@@ -0,0 +1,41 @@
+#version 150 core
+
+uniform mat4 uCurrentProjectionMatrix;
+uniform mat4 uCurrentModelViewMatrix;
+uniform vec2 uPoint;
+uniform vec2 uSize;
+uniform vec4 uAtlasLocation;
+out vec2 textureCoord;
+vec4 viewPos = vec4(0,0,0,0);
+
+void main()
+{
+ switch(gl_VertexID)
+ {
+ case 0:
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x, uPoint.y, 0), 1.0);
+ textureCoord = vec2(uAtlasLocation.x, uAtlasLocation.y);
+ break;
+ case 1:
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x + uSize.x, uPoint.y, 0), 1.0);
+ textureCoord = vec2(uAtlasLocation.x + uAtlasLocation.z, uAtlasLocation.y);
+ break;
+ case 2:
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x + uSize.x, uPoint.y + uSize.y, 0), 1.0);
+ textureCoord = vec2(uAtlasLocation.x + uAtlasLocation.z, uAtlasLocation.y + uAtlasLocation.w);
+ break;
+ case 3:
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x, uPoint.y, 0), 1.0);
+ textureCoord = vec2(uAtlasLocation.x, uAtlasLocation.y);
+ break;
+ case 4:
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x, uPoint.y + uSize.y, 0), 1.0);
+ textureCoord = vec2(uAtlasLocation.x, uAtlasLocation.y + uAtlasLocation.w);
+ break;
+ case 5:
+ viewPos = uCurrentModelViewMatrix * vec4(vec3(uPoint.x + uSize.x, uPoint.y + uSize.y, 0), 1.0);
+ textureCoord = vec2(uAtlasLocation.x + uAtlasLocation.z, uAtlasLocation.y + uAtlasLocation.w);
+ break;
+ }
+ gl_Position = uCurrentProjectionMatrix * viewPos;
+}
diff --git a/installers/mac/MacBundle.tgz b/installers/mac/MacBundle.tgz
index 18633f9da..2e117d61a 100644
Binary files a/installers/mac/MacBundle.tgz and b/installers/mac/MacBundle.tgz differ
diff --git a/source/LibRender2/Backgrounds/Background.cs b/source/LibRender2/Backgrounds/Background.cs
index b90a3dca5..b26b3fd98 100644
--- a/source/LibRender2/Backgrounds/Background.cs
+++ b/source/LibRender2/Backgrounds/Background.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using OpenBveApi.Math;
using OpenBveApi.Objects;
using OpenBveApi.Routes;
@@ -133,8 +133,6 @@ private void RenderStaticBackgroundRetained(StaticBackground data, float alpha,
if (data.Texture != null && renderer.currentHost.LoadTexture(data.Texture, OpenGlTextureWrapMode.RepeatClamp))
{
renderer.LastBoundTexture = data.Texture.OpenGlTextures[(int)OpenGlTextureWrapMode.RepeatClamp];
- GL.Enable(EnableCap.Texture2D);
-
if (alpha == 1.0f)
{
GL.Disable(EnableCap.Blend);
@@ -164,7 +162,6 @@ private void RenderStaticBackgroundRetained(StaticBackground data, float alpha,
}
// texture
- renderer.DefaultShader.SetIsTexture(true);
GL.BindTexture(TextureTarget.Texture2D, data.Texture.OpenGlTextures[(int)OpenGlTextureWrapMode.RepeatClamp].Name);
renderer.LastBoundTexture = null;
@@ -178,15 +175,10 @@ private void RenderStaticBackgroundRetained(StaticBackground data, float alpha,
VertexArrayObject VAO = (VertexArrayObject)data.VAO;
VAO.Bind();
renderer.lastVAO = VAO.handle;
- for (int i = 0; i + 9 < 32 * 10; i += 10)
+ for (int i = 0; i + 11 < 32 * 12; i += 12)
{
- VAO.Draw(PrimitiveType.Quads, i, 4);
- VAO.Draw(PrimitiveType.Triangles, i + 4, 3);
- VAO.Draw(PrimitiveType.Triangles, i + 7, 3);
+ VAO.Draw(PrimitiveType.Triangles, i, 12);
}
- renderer.DefaultShader.Deactivate();
-
- GL.Disable(EnableCap.Texture2D);
renderer.RestoreBlendFunc();
}
}
@@ -373,11 +365,6 @@ private void RenderBackgroundObject(BackgroundObject data)
renderer.RenderFaceImmediateMode(new ObjectState(data.Object), face, Matrix4D.NoTransformation, Matrix4D.Scale(1.0) * renderer.CurrentViewMatrix);
}
}
-
- if (renderer.AvailableNewRenderer)
- {
- renderer.DefaultShader.Deactivate();
- }
}
}
}
diff --git a/source/LibRender2/BaseRenderer.cs b/source/LibRender2/BaseRenderer.cs
index 23c1dfe94..da29e3927 100644
--- a/source/LibRender2/BaseRenderer.cs
+++ b/source/LibRender2/BaseRenderer.cs
@@ -13,11 +13,11 @@
using LibRender2.Screens;
using LibRender2.Shaders;
using LibRender2.Text;
-using LibRender2.Texts;
using LibRender2.Textures;
using LibRender2.Viewports;
using OpenBveApi;
using OpenBveApi.Colors;
+using OpenBveApi.FileSystem;
using OpenBveApi.Hosts;
using OpenBveApi.Interface;
using OpenBveApi.Math;
@@ -41,6 +41,8 @@ public abstract class BaseRenderer
/// The callback to the host application
internal HostInterface currentHost;
+ /// The host filesystem
+ internal FileSystem fileSystem;
/// Holds a reference to the current options
internal BaseOptions currentOptions;
@@ -54,6 +56,8 @@ public abstract class BaseRenderer
protected int ObjectsSortedByStartPointer;
protected int ObjectsSortedByEndPointer;
protected double LastUpdatedTrackPosition;
+ /// A dummy VAO used when working with procedural data within the shader
+ public VertexArrayObject dummyVao;
public Screen Screen;
@@ -115,7 +119,7 @@ public InterfaceType PreviousInterface
#pragma warning restore 0219
/// The current shader in use
- internal Shader CurrentShader;
+ protected internal Shader CurrentShader;
public Shader DefaultShader;
@@ -194,6 +198,45 @@ public bool ForceLegacyOpenGL
set;
}
+ protected internal Texture _programLogo;
+
+ protected internal Texture whitePixel;
+
+ private bool logoError;
+
+ /// Gets the current program logo
+ public Texture ProgramLogo
+ {
+ get
+ {
+ if (_programLogo != null || logoError)
+ {
+ return _programLogo;
+ }
+ try
+ {
+ if (Screen.Width > 1024)
+ {
+ currentHost.RegisterTexture(Path.CombineFile(fileSystem.GetDataFolder("In-game"), "logo_1024.png"), new TextureParameters(null, null), out _programLogo, true);
+ }
+ else if (Screen.Width > 512)
+ {
+ currentHost.RegisterTexture(Path.CombineFile(fileSystem.GetDataFolder("In-game"), "logo_512.png"), new TextureParameters(null, null), out _programLogo, true);
+ }
+ else
+ {
+ currentHost.RegisterTexture(Path.CombineFile(fileSystem.GetDataFolder("In-game"), "logo_256.png"), new TextureParameters(null, null), out _programLogo, true);
+ }
+ }
+ catch
+ {
+ _programLogo = null;
+ logoError = true;
+ }
+ return _programLogo;
+ }
+ }
+
/*
* List of VBO and IBO to delete on the next frame pass
* This needs to be done here as opposed to in the finalizer
@@ -219,10 +262,11 @@ protected BaseRenderer()
///
/// Call this once to initialise the renderer
///
- public virtual void Initialize(HostInterface CurrentHost, BaseOptions CurrentOptions)
+ public virtual void Initialize(HostInterface CurrentHost, BaseOptions CurrentOptions, FileSystem FileSystem)
{
currentHost = CurrentHost;
currentOptions = CurrentOptions;
+ fileSystem = FileSystem;
try
{
@@ -236,6 +280,7 @@ public virtual void Initialize(HostInterface CurrentHost, BaseOptions CurrentOpt
DefaultShader.SetMaterialSpecular(Color32.White);
lastColor = Color32.White;
DefaultShader.Deactivate();
+ dummyVao = new VertexArrayObject();
}
catch
{
@@ -246,7 +291,7 @@ public virtual void Initialize(HostInterface CurrentHost, BaseOptions CurrentOpt
Background = new Background(this);
Fog = new Fog();
- OpenGlString = new OpenGlString(this);
+ OpenGlString = new OpenGlString(this); //text shader shares the rectangle fragment shader
TextureManager = new TextureManager(currentHost, this);
Cube = new Cube(this);
Rectangle = new Rectangle(this);
@@ -257,7 +302,7 @@ public virtual void Initialize(HostInterface CurrentHost, BaseOptions CurrentOpt
StaticObjectStates = new List();
DynamicObjectStates = new List();
VisibleObjects = new VisibleObjectLibrary(currentHost, Camera, currentOptions, this);
-
+ whitePixel = new Texture(new Texture(1, 1, 32, new byte[] {255, 255, 255, 255}, null));
GL.ClearColor(0.67f, 0.67f, 0.67f, 1.0f);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.DepthTest);
@@ -271,11 +316,14 @@ public virtual void Initialize(HostInterface CurrentHost, BaseOptions CurrentOpt
GL.Hint(HintTarget.GenerateMipmapHint, HintMode.Nicest);
GL.Enable(EnableCap.CullFace);
GL.CullFace(CullFaceMode.Front);
- GL.Disable(EnableCap.Texture2D);
GL.Disable(EnableCap.Dither);
GL.Disable(EnableCap.Lighting);
GL.Disable(EnableCap.Fog);
- GL.Fog(FogParameter.FogMode, (int)FogMode.Linear);
+ if (!AvailableNewRenderer)
+ {
+ GL.Disable(EnableCap.Texture2D);
+ GL.Fog(FogParameter.FogMode, (int)FogMode.Linear);
+ }
}
/// Performs cleanup of disposed resources
@@ -316,9 +364,13 @@ public void ReleaseResources()
public virtual void ResetOpenGlState()
{
GL.Enable(EnableCap.CullFace);
- GL.Disable(EnableCap.Lighting);
- GL.Disable(EnableCap.Fog);
- GL.Disable(EnableCap.Texture2D);
+ if (!AvailableNewRenderer)
+ {
+ GL.Disable(EnableCap.Lighting);
+ GL.Disable(EnableCap.Fog);
+ GL.Disable(EnableCap.Texture2D);
+ }
+
SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
UnsetBlendFunc();
GL.Enable(EnableCap.DepthTest);
@@ -364,7 +416,7 @@ public void Reset()
currentHost.StaticObjectCache.Clear();
TextureManager.UnloadAllTextures();
- Initialize(currentHost, currentOptions);
+ Initialize(currentHost, currentOptions, fileSystem);
}
public int CreateStaticObject(StaticObject Prototype, Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, ObjectDisposalMode AccurateObjectDisposal, double AccurateObjectDisposalZOffset, double StartingDistance, double EndingDistance, double BlockLength, double TrackPosition, double Brightness)
@@ -690,10 +742,18 @@ public void DetermineMaxAFLevel()
currentOptions.AnisotropicFilteringMaximum = 16;
return;
}
+
string[] Extensions;
try
{
Extensions = GL.GetString(StringName.Extensions).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ ErrorCode error = GL.GetError();
+ if (error == ErrorCode.InvalidEnum)
+ {
+ // Doing this on a forward compatible GL context fails with invalid enum
+ currentOptions.AnisotropicFilteringMaximum = 16;
+ return;
+ }
}
catch
{
@@ -793,11 +853,12 @@ public void ResetShader(Shader Shader)
lastColor = Color32.White;
Shader.SetMaterialShininess(1.0f);
Shader.SetIsFog(false);
- Shader.SetIsTexture(false);
+ Shader.DisableTexturing();
Shader.SetTexture(0);
Shader.SetBrightness(1.0f);
Shader.SetOpacity(1.0f);
Shader.SetObjectIndex(0);
+ Shader.SetAlphaTest(false);
}
public void SetBlendFunc()
@@ -847,15 +908,32 @@ public void SetAlphaFunc(AlphaFunction Comparison, float Value)
alphaTestEnabled = true;
alphaFuncComparison = Comparison;
alphaFuncValue = Value;
- GL.Enable(EnableCap.AlphaTest);
- GL.AlphaFunc(Comparison, Value);
+ if (AvailableNewRenderer)
+ {
+ CurrentShader.SetAlphaTest(true);
+ CurrentShader.SetAlphaFunction(Comparison, Value);
+ }
+ else
+ {
+ GL.Enable(EnableCap.AlphaTest);
+ GL.AlphaFunc(Comparison, Value);
+ }
+
}
/// Disables OpenGL alpha testing
public void UnsetAlphaFunc()
{
alphaTestEnabled = false;
- GL.Disable(EnableCap.AlphaTest);
+ if (AvailableNewRenderer)
+ {
+ CurrentShader.SetAlphaTest(false);
+ }
+ else
+ {
+ GL.Disable(EnableCap.AlphaTest);
+ }
+
}
/// Restores the OpenGL alpha function to it's previous state
@@ -863,15 +941,38 @@ public void RestoreAlphaFunc()
{
if (alphaTestEnabled)
{
- GL.Enable(EnableCap.AlphaTest);
- GL.AlphaFunc(alphaFuncComparison, alphaFuncValue);
+ if (AvailableNewRenderer)
+ {
+ CurrentShader.SetAlphaTest(true);
+ CurrentShader.SetAlphaFunction(alphaFuncComparison, alphaFuncValue);
+ }
+ else
+ {
+ GL.Enable(EnableCap.AlphaTest);
+ GL.AlphaFunc(alphaFuncComparison, alphaFuncValue);
+ }
+
}
else
{
- GL.Disable(EnableCap.AlphaTest);
+ if (AvailableNewRenderer)
+ {
+ CurrentShader.SetAlphaTest(false);
+ }
+ else
+ {
+ GL.Disable(EnableCap.AlphaTest);
+ }
}
}
+
+ // Cached object state and matricies for shader drawing
+ private ObjectState lastObjectState;
+ private Matrix4D lastModelMatrix;
+ private Matrix4D lastModelViewMatrix;
+ private bool sendToShader;
+
/// Draws a face using the current shader
/// The FaceState to draw
/// Whether debug touch mode
@@ -880,20 +981,35 @@ public void RenderFace(FaceState State, bool IsDebugTouchMode = false)
RenderFace(CurrentShader, State.Object, State.Face, IsDebugTouchMode);
}
+ /// Draws a face using the specified shader and matricies
+ /// The shader to use
+ /// The ObjectState to draw
+ /// The Face within the ObjectState
+ /// The model matrix to use
+ /// The modelview matrix to use
+ public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D ModelMatrix, Matrix4D ModelViewMatrix)
+ {
+ lastModelMatrix = ModelMatrix;
+ lastModelViewMatrix = ModelViewMatrix;
+ sendToShader = true;
+ RenderFace(Shader, State, Face);
+ }
+
/// Draws a face using the specified shader
/// The shader to use
- /// The FaceState to draw
+ /// The ObjectState to draw
+ /// The Face within the ObjectState
/// Whether debug touch mode
public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, bool IsDebugTouchMode = false)
{
- Matrix4D modelMatrix = State.ModelMatrix * Camera.TranslationMatrix;
- Matrix4D modelViewMatrix = modelMatrix * CurrentViewMatrix;
- RenderFace(Shader, State, Face, modelMatrix, modelViewMatrix, IsDebugTouchMode);
- }
+ if (State != lastObjectState && !sendToShader)
+ {
+ lastObjectState = State;
+ lastModelMatrix = State.ModelMatrix * Camera.TranslationMatrix;
+ lastModelViewMatrix = lastModelMatrix * CurrentViewMatrix;
+ sendToShader = true;
+ }
- public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D modelMatrix, Matrix4D modelViewMatrix, bool IsDebugTouchMode = false)
- {
- GL.Disable(EnableCap.Lighting);
if (State.Prototype.Mesh.Vertices.Length < 1)
{
return;
@@ -921,9 +1037,13 @@ public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D
}
// matrix
- Shader.SetCurrentModelViewMatrix(modelViewMatrix);
- Shader.SetCurrentTextureMatrix(State.TextureTranslation);
-
+ if (sendToShader)
+ {
+ Shader.SetCurrentModelViewMatrix(lastModelViewMatrix);
+ Shader.SetCurrentTextureMatrix(State.TextureTranslation);
+ sendToShader = false;
+ }
+
if (OptionWireFrame || IsDebugTouchMode)
{
GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
@@ -981,7 +1101,7 @@ public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D
float distanceFactor;
if (material.GlowAttenuationData != 0)
{
- distanceFactor = (float)Glow.GetDistanceFactor(modelMatrix, State.Prototype.Mesh.Vertices, ref Face, material.GlowAttenuationData);
+ distanceFactor = (float)Glow.GetDistanceFactor(lastModelMatrix, State.Prototype.Mesh.Vertices, ref Face, material.GlowAttenuationData);
}
else
{
@@ -999,7 +1119,6 @@ public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D
// texture
if (material.DaytimeTexture != null && currentHost.LoadTexture(material.DaytimeTexture, (OpenGlTextureWrapMode)material.WrapMode))
{
- Shader.SetIsTexture(true);
if (LastBoundTexture != material.DaytimeTexture.OpenGlTextures[(int)material.WrapMode])
{
GL.BindTexture(TextureTarget.Texture2D,
@@ -1009,7 +1128,7 @@ public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D
}
else
{
- Shader.SetIsTexture(false);
+ Shader.DisableTexturing();
}
// Calculate the brightness of the poly to render
float factor;
@@ -1049,7 +1168,6 @@ public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D
if (material.NighttimeTexture != null && material.NighttimeTexture != material.DaytimeTexture && currentHost.LoadTexture(material.NighttimeTexture, (OpenGlTextureWrapMode)material.WrapMode))
{
// texture
- Shader.SetIsTexture(true);
if (LastBoundTexture != material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode])
{
GL.BindTexture(TextureTarget.Texture2D, material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode].Name);
@@ -1060,9 +1178,9 @@ public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D
GL.Enable(EnableCap.Blend);
// alpha test
- GL.Enable(EnableCap.AlphaTest);
- GL.AlphaFunc(AlphaFunction.Greater, 0.0f);
-
+ Shader.SetAlphaTest(true);
+ Shader.SetAlphaFunction(AlphaFunction.Greater, 0.0f);
+
// blend mode
float alphaFactor = distanceFactor * blendFactor;
@@ -1078,7 +1196,7 @@ public void RenderFace(Shader Shader, ObjectState State, MeshFace Face, Matrix4D
// normals
if (OptionNormals)
{
- Shader.SetIsTexture(false);
+ Shader.DisableTexturing();
Shader.SetBrightness(1.0f);
Shader.SetOpacity(1.0f);
VertexArrayObject NormalsVAO = (VertexArrayObject)State.Prototype.Mesh.NormalsVAO;
diff --git a/source/LibRender2/LibRender2.csproj b/source/LibRender2/LibRender2.csproj
index 7d3b75af6..627c5b046 100644
--- a/source/LibRender2/LibRender2.csproj
+++ b/source/LibRender2/LibRender2.csproj
@@ -70,8 +70,10 @@
+
+
@@ -94,6 +96,9 @@
+
+
+
diff --git a/source/LibRender2/Lighting/Lighting.cs b/source/LibRender2/Lighting/Lighting.cs
index 903268d88..93de8e15a 100644
--- a/source/LibRender2/Lighting/Lighting.cs
+++ b/source/LibRender2/Lighting/Lighting.cs
@@ -50,9 +50,9 @@ public void Initialize()
GL.Light(LightName.Light0, LightParameter.Diffuse, new Color4(OptionDiffuseColor.R, OptionDiffuseColor.G, OptionDiffuseColor.B, 255));
GL.LightModel(LightModelParameter.LightModelAmbient, new[] { (float)LightModel.X, (float)LightModel.Y, (float)LightModel.Z, (float)LightModel.W });
GL.Enable(EnableCap.Light0);
+ GL.Enable(EnableCap.ColorMaterial);
}
- GL.Enable(EnableCap.ColorMaterial);
-
+
float x = OptionAmbientColor.R + (float)OptionAmbientColor.G + OptionAmbientColor.B;
float y = OptionDiffuseColor.R + (float)OptionDiffuseColor.G + OptionDiffuseColor.B;
diff --git a/source/LibRender2/Loading/Loading.cs b/source/LibRender2/Loading/Loading.cs
index 22c1cf412..06e9ee0dc 100644
--- a/source/LibRender2/Loading/Loading.cs
+++ b/source/LibRender2/Loading/Loading.cs
@@ -1,6 +1,5 @@
using System;
-using System.Drawing;
-using LibRender2.Texts;
+using LibRender2.Text;
using OpenBveApi.Colors;
using OpenBveApi.Graphics;
using OpenBveApi.Interface;
@@ -30,9 +29,7 @@ public class Loading
private bool showLogo;
private bool showProgress;
private Texture TextureLoadingBkg;
- private Texture TextureLogo;
private string ProgramVersion = "1.0";
- private readonly string[] LogoFileName = { "logo_256.png", "logo_512.png", "logo_1024.png" };
internal Loading(BaseRenderer renderer)
{
@@ -65,40 +62,6 @@ public void InitLoading(string Path, string Version, bool ShowLogo = true, bool
renderer.TextureManager.RegisterTexture(backgroundFile, out TextureLoadingBkg);
}
}
-
- if (TextureLogo == null)
- {
- // choose logo size according to screen width
- string fName;
-
- if (renderer.Screen.Width > 2048)
- {
- fName = LogoFileName[2];
- }
- else if (renderer.Screen.Width > 1024)
- {
- fName = LogoFileName[1];
- }
- else
- {
- fName = LogoFileName[0];
- }
-
- string logoFile = string.Empty;
- try
- {
- logoFile = OpenBveApi.Path.CombineFile(Path, fName);
- }
- catch
- {
- //ignored
- }
-
- if (System.IO.File.Exists(logoFile))
- {
- renderer.TextureManager.RegisterTexture(logoFile, out TextureLogo);
- }
- }
}
/// Sets the loading screen background to a custom image
@@ -158,19 +121,11 @@ public void DrawLoadingScreen(OpenGlFont Font, double RouteProgress, double Trai
// (the route custom image is loaded in OldParsers/CsvRwRouteParser.cs)
if (!customLoadScreen)
{
- if (showLogo && TextureLogo != null && renderer.currentHost.LoadTexture(TextureLogo, OpenGlTextureWrapMode.ClampClamp))
+ if (showLogo && renderer.ProgramLogo != null)
{
// place the centre of the logo at from the screen top
- int logoTop = (int)(renderer.Screen.Height * logoCentreYFactor - TextureLogo.Height / 2.0);
- renderer.UnsetBlendFunc();
- renderer.SetAlphaFunc(AlphaFunction.Equal, 1.0f);
- GL.DepthMask(true);
- renderer.Rectangle.Draw(TextureLogo, new Vector2((renderer.Screen.Width - TextureLogo.Width) / 2.0, logoTop), new Vector2(TextureLogo.Width, TextureLogo.Height), Color128.White);
- renderer.SetBlendFunc();
- renderer.SetAlphaFunc(AlphaFunction.Less, 1.0f);
- GL.DepthMask(false);
- renderer.Rectangle.Draw(TextureLogo, new Vector2((renderer.Screen.Width - TextureLogo.Width) / 2.0, logoTop), new Vector2(TextureLogo.Width, TextureLogo.Height), Color128.White);
- renderer.SetAlphaFunc(AlphaFunction.Equal, 1.0f);
+ int logoTop = (int)(renderer.Screen.Height * logoCentreYFactor - renderer.ProgramLogo.Height / 2.0);
+ renderer.Rectangle.DrawAlpha(renderer.ProgramLogo, new Vector2((renderer.Screen.Width - renderer.ProgramLogo.Width) / 2.0, logoTop), new Vector2(renderer.ProgramLogo.Width, renderer.ProgramLogo.Height), Color128.White);
}
}
// ReSharper disable once RedundantIfElseBlock
@@ -189,7 +144,7 @@ public void DrawLoadingScreen(OpenGlFont Font, double RouteProgress, double Trai
// VERSION NUMBER
// place the version above the first division
int versionTop = logoBottom + blankHeight - fontHeight;
- renderer.OpenGlString.Draw(Font, "Version " + ProgramVersion, new Point(halfWidth, versionTop), TextAlignment.TopMiddle, Color128.White);
+ renderer.OpenGlString.Draw(Font, "Version " + ProgramVersion, new Vector2(halfWidth, versionTop), TextAlignment.TopMiddle, Color128.White);
// for the moment, do not show any URL; would go right below the first division
// DrawString(Fonts.SmallFont, "https://openbve-project.net",
// new Point(halfWidth, versionTop + fontHeight+2),
@@ -203,7 +158,7 @@ public void DrawLoadingScreen(OpenGlFont Font, double RouteProgress, double Trai
// draw progress message right above the second division
string text = Translations.GetInterfaceString(routeProgress < 1.0 ? "loading_loading_route" : trainProgress < 1.0 ? "loading_loading_train" : "message_loading");
- renderer.OpenGlString.Draw(Font, text, new Point(halfWidth, progressTop - fontHeight - 6), TextAlignment.TopMiddle, Color128.White);
+ renderer.OpenGlString.Draw(Font, text, new Vector2(halfWidth, progressTop - fontHeight - 6), TextAlignment.TopMiddle, Color128.White);
// sum of route progress and train progress arrives up to 2.0:
// => times 50.0 to convert to %
@@ -225,7 +180,7 @@ public void DrawLoadingScreen(OpenGlFont Font, double RouteProgress, double Trai
renderer.Rectangle.Draw(null, new Vector2(progrMargin, progressTop), new Vector2(progressWidth * (int)percent / 100.0, fontHeight + 4), ColourProgressBar);
// progress percent
- renderer.OpenGlString.Draw(Font, percStr, new Point(halfWidth, progressTop), TextAlignment.TopMiddle, Color128.Black);
+ renderer.OpenGlString.Draw(Font, percStr, new Vector2(halfWidth, progressTop), TextAlignment.TopMiddle, Color128.Black);
}
}
}
diff --git a/source/LibRender2/Overlays/Keys.cs b/source/LibRender2/Overlays/Keys.cs
index e283b5ab5..1281ae334 100644
--- a/source/LibRender2/Overlays/Keys.cs
+++ b/source/LibRender2/Overlays/Keys.cs
@@ -1,5 +1,5 @@
using System.Drawing;
-using LibRender2.Texts;
+using LibRender2.Text;
using OpenBveApi.Colors;
using OpenBveApi.Graphics;
using OpenBveApi.Math;
@@ -36,7 +36,7 @@ public void Render(int Left, int Top, int Width, OpenGlFont Font, string[][] Key
renderer.Rectangle.Draw(null, new Vector2(px - 1, py - 1), new Vector2(Width + 1, 17), new Color128(0.25f, 0.25f, 0.25f, 0.5f));
renderer.Rectangle.Draw(null, new Vector2(px - 1, py - 1), new Vector2(Width - 1, 15), new Color128(0.75f, 0.75f, 0.75f, 0.5f));
renderer.Rectangle.Draw(null, new Vector2(px, py), new Vector2(Width, 16), new Color128(0.5f, 0.5f, 0.5f, 0.5f));
- renderer.OpenGlString.Draw(Font, text, new Point(px - 1 + Width / 2, py + 7), TextAlignment.CenterMiddle, Color128.White);
+ renderer.OpenGlString.Draw(Font, text, new Vector2(px - 1 + Width / 2, py + 7), TextAlignment.CenterMiddle, Color128.White);
}
px += Width + 4;
diff --git a/source/LibRender2/Primitives/Cube.cs b/source/LibRender2/Primitives/Cube.cs
index d61226bd7..975b58a4e 100644
--- a/source/LibRender2/Primitives/Cube.cs
+++ b/source/LibRender2/Primitives/Cube.cs
@@ -254,7 +254,6 @@ private void DrawRetained(VertexArrayObject VAO, Vector3 Position, Vector3 Direc
// texture
if (TextureIndex != null && renderer.currentHost.LoadTexture(TextureIndex, OpenGlTextureWrapMode.ClampClamp))
{
- renderer.DefaultShader.SetIsTexture(true);
GL.Enable(EnableCap.Texture2D);
GL.BindTexture(TextureTarget.Texture2D, TextureIndex.OpenGlTextures[(int)OpenGlTextureWrapMode.ClampClamp].Name);
}
@@ -266,10 +265,6 @@ private void DrawRetained(VertexArrayObject VAO, Vector3 Position, Vector3 Direc
// render polygon
VAO.Bind();
VAO.Draw(PrimitiveType.Quads);
- renderer.lastVAO = -1;
- VAO.UnBind();
- renderer.DefaultShader.Deactivate();
-
GL.Disable(EnableCap.Texture2D);
}
diff --git a/source/LibRender2/Primitives/Picturebox.cs b/source/LibRender2/Primitives/Picturebox.cs
new file mode 100644
index 000000000..5a84f6227
--- /dev/null
+++ b/source/LibRender2/Primitives/Picturebox.cs
@@ -0,0 +1,94 @@
+using OpenBveApi.Colors;
+using OpenBveApi.Math;
+using OpenBveApi.Textures;
+using OpenTK.Graphics.OpenGL;
+
+namespace LibRender2.Primitives
+{
+ public class Picturebox
+ {
+ /// Holds a reference to the base renderer
+ private readonly BaseRenderer Renderer;
+ /// The texture for the picturebox
+ public Texture Texture;
+ /// The background color for the picturebox
+ public Color128 BackgroundColor;
+ /// The image sizing mode
+ public ImageSizeMode SizeMode;
+ /// The stored location for the textbox
+ public Vector2 Location;
+ /// The stored size for the textbox
+ public Vector2 Size;
+
+
+ public Picturebox(BaseRenderer renderer)
+ {
+ Renderer = renderer;
+ SizeMode = ImageSizeMode.Zoom;
+ }
+
+ public void Draw()
+ {
+ if (!Renderer.currentHost.LoadTexture(Texture, OpenGlTextureWrapMode.ClampClamp))
+ {
+ return;
+ }
+
+ GL.DepthMask(true);
+ Vector2 newSize;
+ switch (SizeMode)
+ {
+ case ImageSizeMode.Normal:
+ //Draw box containing backing color first
+ Renderer.Rectangle.Draw(Texture, Location, Size, BackgroundColor);
+ //Calculate the new size
+ newSize = new Vector2(Texture.Width, Texture.Height);
+ if (newSize.X > Size.X)
+ {
+ newSize.X = Size.X;
+ }
+
+ if (newSize.Y > Size.Y)
+ {
+ newSize.Y = Size.Y;
+ }
+ //Two-pass draw the texture in appropriate place
+ Renderer.Rectangle.DrawAlpha(Texture, Location, newSize, Color128.White, new Vector2(newSize / Size));
+ break;
+ case ImageSizeMode.Center:
+ //Draw box containing backing color first
+ Renderer.Rectangle.Draw(Texture, Location, Size, BackgroundColor);
+ //Calculate the new size
+ newSize = new Vector2(Texture.Width, Texture.Height);
+ if (newSize.X > Size.X)
+ {
+ newSize.X = Size.X;
+ }
+
+ if (newSize.Y > Size.Y)
+ {
+ newSize.Y = Size.Y;
+ }
+ //Two-pass draw the texture in appropriate place
+ Renderer.Rectangle.DrawAlpha(Texture, Location + new Vector2(newSize - Size) / 2, newSize, Color128.White, new Vector2(newSize / Size));
+ break;
+ case ImageSizeMode.Stretch:
+ //No neeed to draw a backing color box as texture covers the whole thing
+ Renderer.Rectangle.Draw(Texture, Location, Size, BackgroundColor);
+ break;
+ case ImageSizeMode.Zoom:
+ //Draw box containing backing color first
+ Renderer.Rectangle.Draw(null, Location, Size, BackgroundColor);
+ //Calculate the new size
+ double ratioW = Size.X / Texture.Width;
+ double ratioH = Size.Y / Texture.Height;
+ double newRatio = ratioW < ratioH ? ratioW : ratioH;
+ newSize = new Vector2(Texture.Width, Texture.Height) * newRatio;
+ Renderer.Rectangle.DrawAlpha(Texture, new Vector2(Location.X + (Size.X - newSize.X) / 2,Location.Y + (Size.Y - newSize.Y) / 2), newSize, Color128.White);
+ break;
+ }
+
+ }
+
+ }
+}
diff --git a/source/LibRender2/Primitives/Rectangle.cs b/source/LibRender2/Primitives/Rectangle.cs
index 08fbc233b..35ecb027d 100644
--- a/source/LibRender2/Primitives/Rectangle.cs
+++ b/source/LibRender2/Primitives/Rectangle.cs
@@ -1,4 +1,4 @@
-using System.Drawing;
+using LibRender2.Shaders;
using OpenBveApi.Colors;
using OpenBveApi.Math;
using OpenBveApi.Textures;
@@ -8,32 +8,41 @@ namespace LibRender2.Primitives
{
public class Rectangle
{
+ /// Holds a reference to the base renderer
private readonly BaseRenderer renderer;
+ /// If using GL3, the shader to draw the rectangle with
+ private readonly Shader Shader;
internal Rectangle(BaseRenderer renderer)
{
this.renderer = renderer;
+ try
+ {
+ Shader = new Shader(renderer, "rectangle", "rectangle", true);
+ }
+ catch
+ {
+ renderer.ForceLegacyOpenGL = true;
+ }
}
- /// Renders an overlay texture
- /// The texture
- /// The left co-ordinate
- /// The top co-ordinate
- /// The right co-ordinate
- /// The bottom co-ordinate
- public void RenderOverlayTexture(Texture texture, double left, double top, double right, double bottom)
- {
- Draw(texture, new Vector2(left, top), new Vector2((right - left), (bottom - top)));
- }
-
- /// Renders a solid color rectangular overlay
- /// The left co-ordinate
- /// The top co-ordinate
- /// The right co-ordinate
- /// The bottom co-ordinate
- public void RenderOverlaySolid(double left, double top, double right, double bottom)
+ /// Draws a simple 2D rectangle using two-pass alpha blending.
+ /// The texture, or a null reference.
+ /// The top-left coordinates in pixels.
+ /// The size in pixels.
+ /// The color, or a null reference.
+ /// The texture coordinates to be applied
+ public void DrawAlpha(Texture texture, Vector2 point, Vector2 size, Color128? color = null, Vector2? textureCoordinates = null)
{
- Draw(null, new Vector2(left, top), new Vector2((right - left), (bottom - top)));
+ renderer.UnsetBlendFunc();
+ renderer.SetAlphaFunc(AlphaFunction.Equal, 1.0f);
+ GL.DepthMask(true);
+ Draw(texture, point, size, color, textureCoordinates);
+ renderer.SetBlendFunc();
+ renderer.SetAlphaFunc(AlphaFunction.Less, 1.0f);
+ GL.DepthMask(false);
+ Draw(texture, point, size, color, textureCoordinates);
+ renderer.SetAlphaFunc(AlphaFunction.Equal, 1.0f);
}
/// Draws a simple 2D rectangle.
@@ -41,7 +50,27 @@ public void RenderOverlaySolid(double left, double top, double right, double bot
/// The top-left coordinates in pixels.
/// The size in pixels.
/// The color, or a null reference.
- public void Draw(Texture texture, Vector2 point, Vector2 size, Color128? color = null)
+ /// The texture coordinates to be applied
+ public void Draw(Texture texture, Vector2 point, Vector2 size, Color128? color = null, Vector2? textureCoordinates = null)
+ {
+ if (renderer.AvailableNewRenderer && Shader != null)
+ {
+ if (textureCoordinates == null)
+ {
+ DrawWithShader(texture, point, size, color, Vector2.One);
+ }
+ else
+ {
+ DrawWithShader(texture, point, size, color, (Vector2)textureCoordinates);
+ }
+ }
+ else
+ {
+ DrawImmediate(texture, point, size, color, textureCoordinates);
+ }
+ }
+
+ private void DrawImmediate(Texture texture, Vector2 point, Vector2 size, Color128? color, Vector2? textureCoordinates = null)
{
renderer.LastBoundTexture = null;
// TODO: Remove Nullable from color once RenderOverlayTexture and RenderOverlaySolid are fully replaced.
@@ -88,14 +117,29 @@ public void Draw(Texture texture, Vector2 point, Vector2 size, Color128? color =
}
GL.Begin(PrimitiveType.Quads);
- GL.TexCoord2(0.0f, 0.0f);
- GL.Vertex2(point.X, point.Y);
- GL.TexCoord2(1.0f, 0.0f);
- GL.Vertex2(point.X + size.X, point.Y);
- GL.TexCoord2(1.0f, 1.0f);
- GL.Vertex2(point.X + size.X, point.Y + size.Y);
- GL.TexCoord2(0.0f, 1.0f);
- GL.Vertex2(point.X, point.Y + size.Y);
+ if (textureCoordinates == null)
+ {
+ GL.TexCoord2(0,0);
+ GL.Vertex2(point.X, point.Y);
+ GL.TexCoord2(1,0);
+ GL.Vertex2(point.X + size.X, point.Y);
+ GL.TexCoord2(1,1);
+ GL.Vertex2(point.X + size.X, point.Y + size.Y);
+ GL.TexCoord2(0,1);
+ GL.Vertex2(point.X, point.Y + size.Y);
+ }
+ else
+ {
+ Vector2 v = (Vector2) textureCoordinates;
+ GL.TexCoord2(0,0);
+ GL.Vertex2(point.X, point.Y);
+ GL.TexCoord2(v.X, 0);
+ GL.Vertex2(point.X + size.X, point.Y);
+ GL.TexCoord2(v.X, v.Y);
+ GL.Vertex2(point.X + size.X, point.Y + size.Y);
+ GL.TexCoord2(0, v.Y);
+ GL.Vertex2(point.X, point.Y + size.Y);
+ }
GL.End();
GL.Disable(EnableCap.Texture2D);
}
@@ -105,5 +149,33 @@ public void Draw(Texture texture, Vector2 point, Vector2 size, Color128? color =
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();
}
+
+ private void DrawWithShader(Texture texture, Vector2 point, Vector2 size, Color128? color, Vector2 coordinates)
+ {
+ Shader.Activate();
+ if (texture != null && renderer.currentHost.LoadTexture(texture, OpenGlTextureWrapMode.ClampClamp))
+ {
+ GL.BindTexture(TextureTarget.Texture2D, texture.OpenGlTextures[(int)OpenGlTextureWrapMode.ClampClamp].Name);
+ renderer.LastBoundTexture = texture.OpenGlTextures[(int) OpenGlTextureWrapMode.ClampClamp];
+ }
+ else
+ {
+ Shader.DisableTexturing();
+ }
+
+ Shader.SetCurrentProjectionMatrix(renderer.CurrentProjectionMatrix);
+ Shader.SetCurrentModelViewMatrix(renderer.CurrentViewMatrix);
+ Shader.SetColor(color == null ? Color128.White : color.Value);
+ Shader.SetPoint(point);
+ Shader.SetSize(size);
+ Shader.SetCoordinates(coordinates);
+ /*
+ * In order to call GL.DrawArrays with procedural data within the shader,
+ * we first need to bind a dummy VAO
+ * If this is not done, it will generate an InvalidOperation error code
+ */
+ renderer.dummyVao.Bind();
+ GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 6);
+ }
}
}
diff --git a/source/LibRender2/Primitives/Textbox.cs b/source/LibRender2/Primitives/Textbox.cs
new file mode 100644
index 000000000..bd5b33059
--- /dev/null
+++ b/source/LibRender2/Primitives/Textbox.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using LibRender2.Text;
+using OpenBveApi.Colors;
+using OpenBveApi.Graphics;
+using OpenBveApi.Math;
+using OpenBveApi.Textures;
+
+namespace LibRender2.Primitives
+{
+ public class Textbox
+ {
+ /// Holds a reference to the base renderer
+ private readonly BaseRenderer renderer;
+ /// The font the items in this textbox are to be drawn with
+ private readonly OpenGlFont myFont;
+ /// The font color
+ private readonly Color128 myFontColor;
+ /// The string contents of the textbox
+ public string Text
+ {
+ get
+ {
+ return myText;
+ }
+ set
+ {
+ myText = value;
+ //reset the scroll value
+ topLine = 0;
+ }
+ }
+ /// The background texture
+ public Texture BackgroundTexture;
+ /// The background color
+ public Color128 BackgroundColor;
+ /// Backing property for the textbox text
+ private string myText;
+
+ /// The border width of the textbox
+ public readonly int Border;
+ /// The top line to be renderered
+ private int topLine;
+ /// The stored location for the textbox
+ public Vector2 Location;
+ /// The stored size for the textbox
+ public Vector2 Size;
+ /// Whether the textbox is currently selected by the mouse
+ public bool CurrentlySelected;
+
+ private List WrappedLines(int width)
+ {
+ string[] firstSplit = Text.Split(new[] {"\r\n"}, StringSplitOptions.None);
+ List wrappedLines = new List();
+ string currentLine = string.Empty;
+ for(int j = 0; j < firstSplit.Length; j++)
+ {
+ for (int i = 0; i < firstSplit[j].Length; i++)
+ {
+ char currentChar = firstSplit[j][i];
+ currentLine += currentChar;
+ if (myFont.MeasureString(currentLine).X > width)
+ {
+ // Exceeded length, back up to last space
+ int moveback = 0;
+ int lastChar = i - 1;
+ while (currentChar != ' ')
+ {
+ moveback++;
+ i--;
+ if (i == 0)
+ {
+ //No spaces found, so just drop back one char
+ i = lastChar;
+ moveback = 1;
+ break;
+ }
+ currentChar = firstSplit[j][i];
+ }
+ string lineToAdd = currentLine.Substring(0, currentLine.Length - moveback);
+ wrappedLines.Add(lineToAdd.TrimStart());
+ currentLine = string.Empty;
+ }
+ }
+ wrappedLines.Add(currentLine.TrimStart());
+ currentLine = string.Empty;
+ }
+
+ if (currentLine.Length > 0)
+ {
+ wrappedLines.Add(currentLine.TrimStart());
+ }
+ return wrappedLines;
+ }
+
+ public Textbox(BaseRenderer Renderer, OpenGlFont Font, Color128 FontColor, Color128 backgroundColor)
+ {
+ renderer = Renderer;
+ myFont = Font;
+ myFontColor = FontColor;
+ Border = 5;
+ topLine = 0;
+ BackgroundTexture = null;
+ BackgroundColor = backgroundColor;
+ }
+
+ public void VerticalScroll(int numberOfLines)
+ {
+ topLine += numberOfLines;
+ if (topLine < 0)
+ {
+ topLine = 0;
+ }
+ }
+
+ public void Draw()
+ {
+ renderer.Rectangle.Draw(BackgroundTexture, Location, Size, BackgroundColor); //Draw the backing rectangle first
+ if (string.IsNullOrEmpty(Text))
+ {
+ return;
+ }
+
+ List splitString = WrappedLines((int)Size.Y - Border * 2);
+ if (splitString.Count == 1)
+ {
+ //DRAW SINGLE LINE
+ renderer.OpenGlString.Draw(myFont, Text, new Vector2(Location.X + Border, Location.Y + Border), TextAlignment.TopLeft, myFontColor);
+ }
+ else
+ {
+ int maxFittingLines = (int)(Size.Y / myFont.MeasureString(Text).Y);
+ if (topLine + maxFittingLines > splitString.Count)
+ {
+ topLine = Math.Max(0, splitString.Count - maxFittingLines);
+ }
+ //DRAW SPLIT LINES
+ int currentLine = topLine;
+ for (int i = 0; i < Math.Min(maxFittingLines, splitString.Count); i++)
+ {
+ renderer.OpenGlString.Draw(myFont, splitString[currentLine], new Vector2(Location.X + Border, Location.Y + Border + myFont.FontSize * i), TextAlignment.TopLeft, myFontColor);
+ currentLine++;
+ }
+
+ }
+ }
+
+
+ }
+}
diff --git a/source/LibRender2/Shaders/Shader.cs b/source/LibRender2/Shaders/Shader.cs
index ab3d06dde..468811250 100644
--- a/source/LibRender2/Shaders/Shader.cs
+++ b/source/LibRender2/Shaders/Shader.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
@@ -7,8 +6,10 @@
using OpenBveApi.Colors;
using OpenBveApi.Math;
using OpenBveApi.Objects;
+using OpenBveApi.Textures;
using OpenTK;
using OpenTK.Graphics.OpenGL;
+using Vector2 = OpenBveApi.Math.Vector2;
using Vector3 = OpenBveApi.Math.Vector3;
using Vector4 = OpenBveApi.Math.Vector4;
@@ -76,7 +77,7 @@ public Shader(BaseRenderer Renderer, string VertexShaderName, string FragmentSha
GL.DeleteShader(vertexShader);
GL.DeleteShader(fragmentShader);
-
+ GL.BindFragDataLocation(handle, 0, "fragColor");
GL.LinkProgram(handle);
GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out status);
@@ -132,10 +133,16 @@ public void Activate()
{
return;
}
+
+ if (renderer.CurrentShader != null)
+ {
+ renderer.CurrentShader.isActive = false;
+ }
GL.UseProgram(handle);
isActive = true;
renderer.lastVAO = -1;
renderer.CurrentShader = this;
+ renderer.RestoreAlphaFunc();
}
public VertexLayout GetVertexLayout()
@@ -175,11 +182,17 @@ public UniformLayout GetUniformLayout()
FogColor = (short)GL.GetUniformLocation(handle, "uFogColor"),
FogIsLinear = (short)GL.GetUniformLocation(handle, "uFogIsLinear"),
FogDensity = (short)GL.GetUniformLocation(handle, "uFogDensity"),
- IsTexture = (short)GL.GetUniformLocation(handle, "uIsTexture"),
Texture = (short)GL.GetUniformLocation(handle, "uTexture"),
Brightness = (short)GL.GetUniformLocation(handle, "uBrightness"),
Opacity = (short)GL.GetUniformLocation(handle, "uOpacity"),
- ObjectIndex = (short)GL.GetUniformLocation(handle, "uObjectIndex")
+ ObjectIndex = (short)GL.GetUniformLocation(handle, "uObjectIndex"),
+ Point = (short)GL.GetUniformLocation(handle, "uPoint"),
+ Size = (short)GL.GetUniformLocation(handle, "uSize"),
+ Color = (short)GL.GetUniformLocation(handle, "uColor"),
+ Coordinates = (short)GL.GetUniformLocation(handle, "uCoordinates"),
+ AtlasLocation = (short)GL.GetUniformLocation(handle, "uAtlasLocation"),
+ AlphaFunction = (short)GL.GetUniformLocation(handle, "uAlphaFunction"),
+ AlphaComparison = (short)GL.GetUniformLocation(handle, "uAlphaComparison"),
};
}
@@ -221,7 +234,7 @@ private Matrix4 ConvertToMatrix4(Matrix4D mat)
public void SetCurrentProjectionMatrix(Matrix4D ProjectionMatrix)
{
Matrix4 matrix = ConvertToMatrix4(ProjectionMatrix);
- GL.UniformMatrix4(UniformLayout.CurrentProjectionMatrix, false, ref matrix);
+ GL.ProgramUniformMatrix4(handle, UniformLayout.CurrentProjectionMatrix, false, ref matrix);
}
///
@@ -254,7 +267,7 @@ public void SetCurrentModelViewMatrix(Matrix4D ModelViewMatrix)
// | m12 m22 m32 m42 |
// | m13 m23 m33 m43 |
// | m14 m24 m34 m44 |
- GL.UniformMatrix4(UniformLayout.CurrentModelViewMatrix, false, ref matrix);
+ GL.ProgramUniformMatrix4(handle, UniformLayout.CurrentModelViewMatrix, false, ref matrix);
}
///
@@ -264,106 +277,154 @@ public void SetCurrentModelViewMatrix(Matrix4D ModelViewMatrix)
public void SetCurrentTextureMatrix(Matrix4D TextureMatrix)
{
Matrix4 matrix = ConvertToMatrix4(TextureMatrix);
- GL.UniformMatrix4(UniformLayout.CurrentTextureMatrix, false, ref matrix);
+ GL.ProgramUniformMatrix4(handle, UniformLayout.CurrentTextureMatrix, false, ref matrix);
}
public void SetIsLight(bool IsLight)
{
- GL.Uniform1(UniformLayout.IsLight, IsLight ? 1 : 0);
+ GL.ProgramUniform1(handle, UniformLayout.IsLight, IsLight ? 1 : 0);
}
public void SetLightPosition(Vector3 LightPosition)
{
- GL.Uniform3(UniformLayout.LightPosition, (float)LightPosition.X, (float)LightPosition.Y, (float)LightPosition.Z);
+ GL.ProgramUniform3(handle, UniformLayout.LightPosition, (float)LightPosition.X, (float)LightPosition.Y, (float)LightPosition.Z);
}
public void SetLightAmbient(Color24 LightAmbient)
{
- GL.Uniform3(UniformLayout.LightAmbient, LightAmbient.R / 255.0f, LightAmbient.G / 255.0f, LightAmbient.B / 255.0f);
+ GL.ProgramUniform3(handle, UniformLayout.LightAmbient, LightAmbient.R / 255.0f, LightAmbient.G / 255.0f, LightAmbient.B / 255.0f);
}
public void SetLightDiffuse(Color24 LightDiffuse)
{
- GL.Uniform3(UniformLayout.LightDiffuse, LightDiffuse.R / 255.0f, LightDiffuse.G / 255.0f, LightDiffuse.B / 255.0f);
+ GL.ProgramUniform3(handle, UniformLayout.LightDiffuse, LightDiffuse.R / 255.0f, LightDiffuse.G / 255.0f, LightDiffuse.B / 255.0f);
}
public void SetLightSpecular(Color24 LightSpecular)
{
- GL.Uniform3(UniformLayout.LightSpecular, LightSpecular.R / 255.0f, LightSpecular.G / 255.0f, LightSpecular.B / 255.0f);
+ GL.ProgramUniform3(handle, UniformLayout.LightSpecular, LightSpecular.R / 255.0f, LightSpecular.G / 255.0f, LightSpecular.B / 255.0f);
}
public void SetLightModel(Vector4 LightModel)
{
- GL.Uniform4(UniformLayout.LightModel, (float)LightModel.X, (float)LightModel.Y, (float)LightModel.Z, (float)LightModel.W);
+ GL.ProgramUniform4(handle, UniformLayout.LightModel, (float)LightModel.X, (float)LightModel.Y, (float)LightModel.Z, (float)LightModel.W);
}
public void SetMaterialAmbient(Color32 MaterialAmbient)
{
- GL.Uniform4(UniformLayout.MaterialAmbient, MaterialAmbient.R / 255.0f, MaterialAmbient.G / 255.0f, MaterialAmbient.B / 255.0f, MaterialAmbient.A / 255.0f);
+ GL.ProgramUniform4(handle, UniformLayout.MaterialAmbient, MaterialAmbient.R / 255.0f, MaterialAmbient.G / 255.0f, MaterialAmbient.B / 255.0f, MaterialAmbient.A / 255.0f);
}
public void SetMaterialDiffuse(Color32 MaterialDiffuse)
{
- GL.Uniform4(UniformLayout.MaterialDiffuse, MaterialDiffuse.R / 255.0f, MaterialDiffuse.G / 255.0f, MaterialDiffuse.B / 255.0f, MaterialDiffuse.A / 255.0f);
+ GL.ProgramUniform4(handle, UniformLayout.MaterialDiffuse, MaterialDiffuse.R / 255.0f, MaterialDiffuse.G / 255.0f, MaterialDiffuse.B / 255.0f, MaterialDiffuse.A / 255.0f);
}
public void SetMaterialSpecular(Color32 MaterialSpecular)
{
- GL.Uniform4(UniformLayout.MaterialSpecular, MaterialSpecular.R / 255.0f, MaterialSpecular.G / 255.0f, MaterialSpecular.B / 255.0f, MaterialSpecular.A / 255.0f);
+ GL.ProgramUniform4(handle, UniformLayout.MaterialSpecular, MaterialSpecular.R / 255.0f, MaterialSpecular.G / 255.0f, MaterialSpecular.B / 255.0f, MaterialSpecular.A / 255.0f);
}
public void SetMaterialEmission(Color24 MaterialEmission)
{
- GL.Uniform3(UniformLayout.MaterialEmission, MaterialEmission.R / 255.0f, MaterialEmission.G / 255.0f, MaterialEmission.B / 255.0f);
+ GL.ProgramUniform3(handle, UniformLayout.MaterialEmission, MaterialEmission.R / 255.0f, MaterialEmission.G / 255.0f, MaterialEmission.B / 255.0f);
}
public void SetMaterialShininess(float MaterialShininess)
{
- GL.Uniform1(UniformLayout.MaterialShininess, MaterialShininess);
+ GL.ProgramUniform1(handle, UniformLayout.MaterialShininess, MaterialShininess);
}
public void SetMaterialFlags(MaterialFlags Flags)
{
- GL.Uniform1(UniformLayout.MaterialFlags, (int)Flags);
+ GL.ProgramUniform1(handle, UniformLayout.MaterialFlags, (int)Flags);
}
public void SetIsFog(bool IsFog)
{
- GL.Uniform1(UniformLayout.IsFog, IsFog ? 1 : 0);
+ GL.ProgramUniform1(handle, UniformLayout.IsFog, IsFog ? 1 : 0);
}
public void SetFog(Fog Fog)
{
- GL.Uniform1(UniformLayout.FogStart, Fog.Start);
- GL.Uniform1(UniformLayout.FogEnd, Fog.End);
- GL.Uniform3(UniformLayout.FogColor, Fog.Color.R / 255.0f, Fog.Color.G / 255.0f, Fog.Color.B / 255.0f);
- GL.Uniform1(UniformLayout.FogIsLinear, Fog.IsLinear ? 1 : 0);
- GL.Uniform1(UniformLayout.FogDensity, Fog.Density);
+ GL.ProgramUniform1(handle, UniformLayout.FogStart, Fog.Start);
+ GL.ProgramUniform1(handle, UniformLayout.FogEnd, Fog.End);
+ GL.ProgramUniform3(handle, UniformLayout.FogColor, Fog.Color.R / 255.0f, Fog.Color.G / 255.0f, Fog.Color.B / 255.0f);
+ GL.ProgramUniform1(handle, UniformLayout.FogIsLinear, Fog.IsLinear ? 1 : 0);
+ GL.ProgramUniform1(handle, UniformLayout.FogDensity, Fog.Density);
}
- public void SetIsTexture(bool IsTexture)
+ public void DisableTexturing()
{
- GL.Uniform1(UniformLayout.IsTexture, IsTexture ? 1 : 0);
+ if (renderer.LastBoundTexture != renderer.whitePixel.OpenGlTextures[(int)OpenGlTextureWrapMode.ClampClamp])
+ {
+ /*
+ * If we do not want to use a texture, set a single white pixel instead
+ * This eliminates some shader branching, and is marginally faster in some cases
+ */
+ renderer.currentHost.LoadTexture(renderer.whitePixel, OpenGlTextureWrapMode.ClampClamp);
+ GL.BindTexture(TextureTarget.Texture2D, renderer.whitePixel.OpenGlTextures[(int)OpenGlTextureWrapMode.ClampClamp].Name);
+ renderer.LastBoundTexture = renderer.whitePixel.OpenGlTextures[(int) OpenGlTextureWrapMode.ClampClamp];
+ }
}
public void SetTexture(int TextureUnit)
{
- GL.Uniform1(UniformLayout.Texture, TextureUnit);
+ GL.ProgramUniform1(handle, UniformLayout.Texture, TextureUnit);
}
public void SetBrightness(float Brightness)
{
- GL.Uniform1(UniformLayout.Brightness, Brightness);
+ GL.ProgramUniform1(handle, UniformLayout.Brightness, Brightness);
}
public void SetOpacity(float Opacity)
{
- GL.Uniform1(UniformLayout.Opacity, Opacity);
+ GL.ProgramUniform1(handle, UniformLayout.Opacity, Opacity);
}
public void SetObjectIndex(int ObjectIndex)
{
- GL.Uniform1(UniformLayout.ObjectIndex, ObjectIndex);
+ GL.ProgramUniform1(handle, UniformLayout.ObjectIndex, ObjectIndex);
+ }
+
+ public void SetPoint(Vector2 point)
+ {
+ GL.ProgramUniform2(handle, UniformLayout.Point, (float)point.X, (float)point.Y);
+ }
+
+ public void SetSize(Vector2 size)
+ {
+ GL.ProgramUniform2(handle, UniformLayout.Size, (float) size.X, (float) size.Y);
+ }
+
+ public void SetColor(Color128 color)
+ {
+ GL.ProgramUniform4(handle, UniformLayout.Color, color.R, color.G, color.B, color.A);
+ }
+
+ public void SetCoordinates(Vector2 coordinates)
+ {
+ GL.ProgramUniform2(handle, UniformLayout.Coordinates, (float)coordinates.X, (float)coordinates.Y);
+ }
+
+ public void SetAtlasLocation(Vector4 atlasLocation)
+ {
+ GL.ProgramUniform4(handle, UniformLayout.AtlasLocation, (float)atlasLocation.X, (float)atlasLocation.Y, (float)atlasLocation.Z, (float)atlasLocation.W);
+ }
+
+ public void SetAlphaFunction(AlphaFunction alphaFunction, float alphaComparison)
+ {
+ GL.ProgramUniform1(handle, UniformLayout.AlphaFunction, (int)alphaFunction);
+ GL.ProgramUniform1(handle, UniformLayout.AlphaComparison, alphaComparison);
+ }
+
+ public void SetAlphaTest(bool enabled)
+ {
+ if (!enabled)
+ {
+ GL.ProgramUniform1(handle, UniformLayout.AlphaFunction, (int)AlphaFunction.Never);
+ }
}
#endregion
diff --git a/source/LibRender2/Text/Fonts.cs b/source/LibRender2/Text/Fonts.cs
index 6f2f65795..e709bcd88 100644
--- a/source/LibRender2/Text/Fonts.cs
+++ b/source/LibRender2/Text/Fonts.cs
@@ -1,6 +1,6 @@
using System.Drawing;
-using LibRender2.Texts;
+using LibRender2.Text;
namespace LibRender2.Text
{
@@ -24,6 +24,29 @@ public class Fonts
public readonly OpenGlFont EvenLargerFont;
+ /// Gets the next smallest font
+ /// The font we require the smaller version for
+ /// The next smallest font
+ public OpenGlFont NextSmallestFont(OpenGlFont currentFont)
+ {
+ switch ((int)currentFont.FontSize)
+ {
+ case 9:
+ case 12:
+ return VerySmallFont;
+ case 16:
+ return SmallFont;
+ case 21:
+ return NormalFont;
+ case 27:
+ return LargeFont;
+ case 34:
+ return VeryLargeFont;
+ default:
+ return EvenLargerFont;
+ }
+ }
+
internal Fonts()
{
VerySmallFont = new OpenGlFont(FontFamily.GenericSansSerif, 9.0f);
diff --git a/source/LibRender2/Text/OpenGlFont.cs b/source/LibRender2/Text/OpenGlFont.cs
index 509fe8ad8..fb0111d0d 100644
--- a/source/LibRender2/Text/OpenGlFont.cs
+++ b/source/LibRender2/Text/OpenGlFont.cs
@@ -1,8 +1,9 @@
using System;
using System.Drawing;
+using OpenBveApi.Math;
using OpenBveApi.Textures;
-namespace LibRender2.Texts
+namespace LibRender2.Text
{
/// Represents a font.
public sealed class OpenGlFont : IDisposable
@@ -57,10 +58,10 @@ public int GetCharacterData(string text, int offset, out Texture texture, out Op
/// Measures the size of a string as it would be rendered using this font.
/// The string to render.
/// The size of the string.
- public Size MeasureString(string text)
+ public Vector2 MeasureString(string text)
{
- int width = 0;
- int height = 0;
+ double width = 0;
+ double height = 0;
if (text != null)
{
@@ -70,16 +71,16 @@ public Size MeasureString(string text)
Texture texture;
OpenGlFontChar data;
i += GetCharacterData(text, i, out texture, out data) - 1;
- width += data.TypographicSize.Width;
+ width += data.TypographicSize.X;
- if (data.TypographicSize.Height > height)
+ if (data.TypographicSize.Y > height)
{
- height = data.TypographicSize.Height;
+ height = data.TypographicSize.Y;
}
}
}
- return new Size(width, height);
+ return new Vector2(width, height);
}
private void Dispose(bool disposing)
diff --git a/source/LibRender2/Text/OpenGlFontChar.cs b/source/LibRender2/Text/OpenGlFontChar.cs
index df7073334..102bcff5e 100644
--- a/source/LibRender2/Text/OpenGlFontChar.cs
+++ b/source/LibRender2/Text/OpenGlFontChar.cs
@@ -1,24 +1,24 @@
-using System.Drawing;
+using OpenBveApi.Math;
-namespace LibRender2.Texts
+namespace LibRender2.Text
{
/// Represents a single character.
public struct OpenGlFontChar
{
// --- members ---
/// The texture coordinates that represent the character in the underlying texture.
- public RectangleF TextureCoordinates;
+ public Vector4 TextureCoordinates;
/// The physical size of the character.
- public Size PhysicalSize;
+ public Vector2 PhysicalSize;
/// The typographic size of the character.
- public Size TypographicSize;
+ public Vector2 TypographicSize;
// --- constructors ---
/// Creates a new character.
/// The texture coordinates that represent the character in the underlying texture.
/// The physical size of the character.
/// The typographic size of the character.
- public OpenGlFontChar(RectangleF textureCoordinates, Size physicalSize, Size typographicSize)
+ public OpenGlFontChar(Vector4 textureCoordinates, Vector2 physicalSize, Vector2 typographicSize)
{
TextureCoordinates = textureCoordinates;
PhysicalSize = physicalSize;
diff --git a/source/LibRender2/Text/OpenGlFontTable.cs b/source/LibRender2/Text/OpenGlFontTable.cs
index 4b6f41d9f..4ec6261d8 100644
--- a/source/LibRender2/Text/OpenGlFontTable.cs
+++ b/source/LibRender2/Text/OpenGlFontTable.cs
@@ -2,9 +2,10 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
+using OpenBveApi.Math;
using OpenBveApi.Textures;
-namespace LibRender2.Texts
+namespace LibRender2.Text
{
/// Represents a table of 256 consecutive code points rendered into the same texture.
public class OpenGlFontTable : IDisposable
@@ -29,8 +30,8 @@ public OpenGlFontTable(Font font, int offset)
/*
* Measure characters.
* */
- Size[] physicalSizes = new Size[256];
- Size[] typographicSizes = new Size[256];
+ Vector2[] physicalSizes = new Vector2[256];
+ Vector2[] typographicSizes = new Vector2[256];
bitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
@@ -40,8 +41,8 @@ public OpenGlFontTable(Font font, int offset)
string character = char.ConvertFromUtf32(offset + i);
SizeF physicalSize = graphics.MeasureString(character, font, int.MaxValue, StringFormat.GenericDefault);
SizeF typographicSize = graphics.MeasureString(character, font, int.MaxValue, StringFormat.GenericTypographic);
- physicalSizes[i] = new Size((int)Math.Ceiling(physicalSize.Width), (int)Math.Ceiling(physicalSize.Height));
- typographicSizes[i] = new Size((int)Math.Ceiling(typographicSize.Width == 0.0f ? physicalSize.Width : typographicSize.Width), (int)Math.Ceiling(typographicSize.Height == 0.0f ? physicalSize.Height : typographicSize.Height));
+ physicalSizes[i] = new Vector2((int)Math.Ceiling(physicalSize.Width), (int)Math.Ceiling(physicalSize.Height));
+ typographicSizes[i] = new Vector2((int)Math.Ceiling(typographicSize.Width == 0.0f ? physicalSize.Width : typographicSize.Width), (int)Math.Ceiling(typographicSize.Height == 0.0f ? physicalSize.Height : typographicSize.Height));
}
graphics.Dispose();
@@ -51,11 +52,11 @@ public OpenGlFontTable(Font font, int offset)
* Find suitable bitmap dimensions.
* */
const int border = 1;
- int width = border;
- int height = border;
- int lineWidth = 0;
- int lineHeight = 0;
- PointF[] coordinates = new PointF[256];
+ double width = border;
+ double height = border;
+ double lineWidth = 0;
+ double lineHeight = 0;
+ Vector2[] coordinates = new Vector2[256];
for (int i = 0; i < 256; i++)
{
@@ -72,13 +73,13 @@ public OpenGlFontTable(Font font, int offset)
lineHeight = 0;
}
- coordinates[i] = new PointF(lineWidth, height);
+ coordinates[i] = new Vector2(lineWidth, height);
- lineWidth += physicalSizes[i].Width + border;
+ lineWidth += physicalSizes[i].X + border;
- if (physicalSizes[i].Height + border > lineHeight)
+ if (physicalSizes[i].Y + border > lineHeight)
{
- lineHeight = physicalSizes[i].Height + border;
+ lineHeight = physicalSizes[i].Y + border;
}
}
@@ -93,7 +94,7 @@ public OpenGlFontTable(Font font, int offset)
/*
* Draw character to bitmap.
* */
- bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
+ bitmap = new Bitmap((int)width, (int)height, PixelFormat.Format32bppArgb);
graphics = Graphics.FromImage(bitmap);
graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
graphics.Clear(Color.Black);
@@ -101,12 +102,12 @@ public OpenGlFontTable(Font font, int offset)
for (int i = 0; i < 256; i++)
{
- graphics.DrawString(char.ConvertFromUtf32(offset + i), font, Brushes.White, coordinates[i]);
- float x0 = (coordinates[i].X - border) / width;
- float x1 = (coordinates[i].X + physicalSizes[i].Width + border) / width;
- float y0 = (coordinates[i].Y - border) / height;
- float y1 = (coordinates[i].Y + physicalSizes[i].Height + border) / height;
- Characters[i] = new OpenGlFontChar(new RectangleF(x0, y0, x1 - x0, y1 - y0), new Size(physicalSizes[i].Width + 2 * border, physicalSizes[i].Height + 2 * border), typographicSizes[i]);
+ graphics.DrawString(char.ConvertFromUtf32(offset + i), font, Brushes.White, new PointF((float)coordinates[i].X, (float)coordinates[i].Y));
+ double x0 = (coordinates[i].X - border) / width;
+ double x1 = (coordinates[i].X + physicalSizes[i].X + border) / width;
+ double y0 = (coordinates[i].Y - border) / height;
+ double y1 = (coordinates[i].Y + physicalSizes[i].Y + border) / height;
+ Characters[i] = new OpenGlFontChar(new Vector4(x0, y0, x1 - x0, y1 - y0), new Vector2(physicalSizes[i].X + 2 * border, physicalSizes[i].Y + 2 * border), typographicSizes[i]);
}
graphics.Dispose();
diff --git a/source/LibRender2/Text/OpenGlString.cs b/source/LibRender2/Text/OpenGlString.cs
index 6b0f94ad0..a2eeb4d0d 100644
--- a/source/LibRender2/Text/OpenGlString.cs
+++ b/source/LibRender2/Text/OpenGlString.cs
@@ -1,18 +1,32 @@
-using System.Drawing;
+using System;
+using System.Drawing;
+using LibRender2.Shaders;
using OpenBveApi.Colors;
using OpenBveApi.Graphics;
+using OpenBveApi.Math;
using OpenBveApi.Textures;
using OpenTK.Graphics.OpenGL;
-namespace LibRender2.Texts
+namespace LibRender2.Text
{
public class OpenGlString
{
private readonly BaseRenderer renderer;
+ private readonly Shader Shader;
+
internal OpenGlString(BaseRenderer renderer)
{
this.renderer = renderer;
+ try
+ {
+ this.Shader = new Shader(renderer, "text", "rectangle", true);
+ }
+ catch
+ {
+ renderer.ForceLegacyOpenGL = true;
+ }
+
}
/// Renders a string to the screen.
@@ -22,7 +36,7 @@ internal OpenGlString(BaseRenderer renderer)
/// The alignment.
/// The color.
/// This function sets the OpenGL blend function to glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA).
- public void Draw(OpenGlFont font, string text, Point location, TextAlignment alignment, Color128 color)
+ public void Draw(OpenGlFont font, string text, Vector2 location, TextAlignment alignment, Color128 color)
{
if (text == null || font == null)
{
@@ -33,18 +47,18 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
* Prepare the top-left coordinates for rendering, incorporating the
* orientation of the string in relation to the specified location.
* */
- int left;
+ double left;
if ((alignment & TextAlignment.Left) == 0)
{
- int width = 0;
+ double width = 0;
for (int i = 0; i < text.Length; i++)
{
Texture texture;
OpenGlFontChar data;
i += font.GetCharacterData(text, i, out texture, out data) - 1;
- width += data.TypographicSize.Width;
+ width += data.TypographicSize.X;
}
if ((alignment & TextAlignment.Right) != 0)
@@ -61,11 +75,11 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
left = location.X;
}
- int top;
+ double top;
if ((alignment & TextAlignment.Top) == 0)
{
- int height = 0;
+ double height = 0;
for (int i = 0; i < text.Length; i++)
{
@@ -73,9 +87,9 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
OpenGlFontChar data;
i += font.GetCharacterData(text, i, out texture, out data) - 1;
- if (data.TypographicSize.Height > height)
+ if (data.TypographicSize.Y > height)
{
- height = data.TypographicSize.Height;
+ height = data.TypographicSize.Y;
}
}
@@ -93,6 +107,19 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
top = location.Y;
}
+ if (renderer.AvailableNewRenderer && Shader != null)
+ {
+ DrawWithShader(text, font, left, top, color);
+ }
+ else
+ {
+ DrawImmediate(text, font, left, top, color);
+ }
+
+ }
+
+ private void DrawImmediate(string text, OpenGlFont font, double left, double top, Color128 color)
+ {
/*
* Render the string.
* */
@@ -125,8 +152,8 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
{
GL.BindTexture(TextureTarget.Texture2D, texture.OpenGlTextures[(int)OpenGlTextureWrapMode.ClampClamp].Name);
- int x = left - (data.PhysicalSize.Width - data.TypographicSize.Width) / 2;
- int y = top - (data.PhysicalSize.Height - data.TypographicSize.Height) / 2;
+ double x = left - (data.PhysicalSize.X - data.TypographicSize.X) / 2;
+ double y = top - (data.PhysicalSize.Y - data.TypographicSize.Y) / 2;
/*
* In the first pass, mask off the background with pure black.
@@ -134,14 +161,14 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
GL.BlendFunc(BlendingFactor.Zero, BlendingFactor.OneMinusSrcColor);
GL.Begin(PrimitiveType.Quads);
GL.Color4(color.A, color.A, color.A, 1.0f);
- GL.TexCoord2(data.TextureCoordinates.Left, data.TextureCoordinates.Top);
+ GL.TexCoord2(data.TextureCoordinates.X, data.TextureCoordinates.Y);
GL.Vertex2(x, y);
- GL.TexCoord2(data.TextureCoordinates.Right, data.TextureCoordinates.Top);
- GL.Vertex2(x + data.PhysicalSize.Width, y);
- GL.TexCoord2(data.TextureCoordinates.Right, data.TextureCoordinates.Bottom);
- GL.Vertex2(x + data.PhysicalSize.Width, y + data.PhysicalSize.Height);
- GL.TexCoord2(data.TextureCoordinates.Left, data.TextureCoordinates.Bottom);
- GL.Vertex2(x, y + data.PhysicalSize.Height);
+ GL.TexCoord2(data.TextureCoordinates.X + data.TextureCoordinates.Z, data.TextureCoordinates.Y);
+ GL.Vertex2(x + data.PhysicalSize.X, y);
+ GL.TexCoord2(data.TextureCoordinates.X + data.TextureCoordinates.Z, data.TextureCoordinates.Y + data.TextureCoordinates.W);
+ GL.Vertex2(x + data.PhysicalSize.X, y + data.PhysicalSize.Y);
+ GL.TexCoord2(data.TextureCoordinates.X, data.TextureCoordinates.Y + data.TextureCoordinates.W);
+ GL.Vertex2(x, y + data.PhysicalSize.Y);
GL.End();
/*
@@ -150,18 +177,18 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One);
GL.Begin(PrimitiveType.Quads);
GL.Color4(color.R, color.G, color.B, color.A);
- GL.TexCoord2(data.TextureCoordinates.Left, data.TextureCoordinates.Top);
+ GL.TexCoord2(data.TextureCoordinates.X, data.TextureCoordinates.Y);
GL.Vertex2(x, y);
- GL.TexCoord2(data.TextureCoordinates.Right, data.TextureCoordinates.Top);
- GL.Vertex2(x + data.PhysicalSize.Width, y);
- GL.TexCoord2(data.TextureCoordinates.Right, data.TextureCoordinates.Bottom);
- GL.Vertex2(x + data.PhysicalSize.Width, y + data.PhysicalSize.Height);
- GL.TexCoord2(data.TextureCoordinates.Left, data.TextureCoordinates.Bottom);
- GL.Vertex2(x, y + data.PhysicalSize.Height);
+ GL.TexCoord2(data.TextureCoordinates.X + data.TextureCoordinates.Z, data.TextureCoordinates.Y);
+ GL.Vertex2(x + data.PhysicalSize.X, y);
+ GL.TexCoord2(data.TextureCoordinates.X + data.TextureCoordinates.Z, data.TextureCoordinates.Y + data.TextureCoordinates.W);
+ GL.Vertex2(x + data.PhysicalSize.X, y + data.PhysicalSize.Y);
+ GL.TexCoord2(data.TextureCoordinates.X, data.TextureCoordinates.Y + data.TextureCoordinates.W);
+ GL.Vertex2(x, y + data.PhysicalSize.Y);
GL.End();
}
- left += data.TypographicSize.Width;
+ left += data.TypographicSize.X;
}
renderer.RestoreBlendFunc();
@@ -173,6 +200,48 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
GL.PopMatrix();
}
+ private void DrawWithShader(string text, OpenGlFont font, double left, double top, Color128 color)
+ {
+ Shader.Activate();
+ renderer.CurrentShader = Shader;
+ Shader.SetCurrentProjectionMatrix(renderer.CurrentProjectionMatrix);
+ Shader.SetCurrentModelViewMatrix(renderer.CurrentViewMatrix);
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ Texture texture;
+ OpenGlFontChar data;
+ i += font.GetCharacterData(text, i, out texture, out data) - 1;
+ if (renderer.currentHost.LoadTexture(texture, OpenGlTextureWrapMode.ClampClamp))
+ {
+ GL.BindTexture(TextureTarget.Texture2D, texture.OpenGlTextures[(int)OpenGlTextureWrapMode.ClampClamp].Name);
+ Shader.SetAtlasLocation(data.TextureCoordinates);
+ double x = left - (data.PhysicalSize.X - data.TypographicSize.X) / 2;
+ double y = top - (data.PhysicalSize.Y - data.TypographicSize.Y) / 2;
+
+ /*
+ * In the first pass, mask off the background with pure black.
+ */
+ GL.BlendFunc(BlendingFactor.Zero, BlendingFactor.OneMinusSrcColor);
+ Shader.SetColor(new Color128(color.A, color.A, color.A, 1.0f));
+ Shader.SetPoint(new Vector2(x, y));
+ Shader.SetSize(data.PhysicalSize);
+ /*
+ * In order to call GL.DrawArrays with procedural data within the shader,
+ * we first need to bind a dummy VAO
+ * If this is not done, it will generate an InvalidOperation error code
+ */
+ renderer.dummyVao.Bind();
+ GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 6);
+ GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One);
+ Shader.SetColor(color);
+ GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 6);
+ }
+ left += data.TypographicSize.X;
+ }
+ renderer.RestoreBlendFunc();
+ }
+
/// Renders a string to the screen.
/// The font to use.
/// The string to render.
@@ -181,11 +250,11 @@ public void Draw(OpenGlFont font, string text, Point location, TextAlignment ali
/// The color.
/// Whether to draw a shadow.
/// This function sets the OpenGL blend function to glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA).
- public void Draw(OpenGlFont font, string text, Point location, TextAlignment alignment, Color128 color, bool shadow)
+ public void Draw(OpenGlFont font, string text, Vector2 location, TextAlignment alignment, Color128 color, bool shadow)
{
if (shadow)
{
- Draw(font, text, new Point(location.X - 1, location.Y + 1), alignment, new Color128(0.0f, 0.0f, 0.0f, 0.5f * color.A));
+ Draw(font, text, new Vector2(location.X - 1, location.Y + 1), alignment, new Color128(0.0f, 0.0f, 0.0f, 0.5f * color.A));
Draw(font, text, location, alignment, color);
}
else
diff --git a/source/LibRender2/openGL/ShaderLayout.cs b/source/LibRender2/openGL/ShaderLayout.cs
index 3c2b840ac..9f3b3bae0 100644
--- a/source/LibRender2/openGL/ShaderLayout.cs
+++ b/source/LibRender2/openGL/ShaderLayout.cs
@@ -137,12 +137,7 @@ public class UniformLayout
/// The handle of "uFogDensity" within the shader
///
public short FogDensity = -1;
-
- ///
- /// The handle of "uIsTexture" within the shader
- ///
- public short IsTexture = -1;
-
+
///
/// The handle of "uTexture" within the shader
///
@@ -162,5 +157,40 @@ public class UniformLayout
/// The handle of "uObjectIndex" within the shader
///
public short ObjectIndex = -1;
+
+ ///
+ /// The handle "uPoint" within the shader
+ ///
+ public short Point = -1;
+
+ ///
+ /// The handle of "uSize" within the shader
+ ///
+ public short Size;
+
+ ///
+ /// The handle of "uColor" within the shader
+ ///
+ public short Color;
+
+ ///
+ /// The handle of "uCoordinates" within the shader
+ ///
+ public short Coordinates;
+
+ ///
+ /// The handle of "uAtlasLocation" within the shader
+ ///
+ public short AtlasLocation;
+
+ ///
+ /// The handle of "uAlphaFunction" within the shader
+ ///
+ public short AlphaFunction;
+
+ ///
+ /// The handle of "uAlphaComparison" within the shader
+ ///
+ public short AlphaComparison;
}
}
diff --git a/source/LibRender2/openGL/VertexArrayObject.cs b/source/LibRender2/openGL/VertexArrayObject.cs
index 88649fc15..953683d1b 100644
--- a/source/LibRender2/openGL/VertexArrayObject.cs
+++ b/source/LibRender2/openGL/VertexArrayObject.cs
@@ -338,7 +338,8 @@ public static void CreateVAO(this StaticBackground background, VertexLayout vert
Color = Color128.White
});
- indexData.AddRange(new[] { 0, 1, 2, 3 }.Select(x => x + indexOffset).Select(x => (ushort)x));
+ indexData.AddRange(new[] { 0, 1, 2 }.Select(x => x + indexOffset).Select(x => (ushort)x));
+ indexData.AddRange(new[] { 0, 2, 3 }.Select(x => x + indexOffset).Select(x => (ushort)x));
// top cap
vertexData.Add(new LibRenderVertex
diff --git a/source/ObjectViewer/Graphics/NewRendererS.cs b/source/ObjectViewer/Graphics/NewRendererS.cs
index b6e8cb0cb..e8a62e0a3 100644
--- a/source/ObjectViewer/Graphics/NewRendererS.cs
+++ b/source/ObjectViewer/Graphics/NewRendererS.cs
@@ -1,4 +1,4 @@
-using System.Drawing;
+using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;
@@ -9,6 +9,7 @@
using OpenBve;
using OpenBveApi;
using OpenBveApi.Colors;
+using OpenBveApi.FileSystem;
using OpenBveApi.Graphics;
using OpenBveApi.Hosts;
using OpenBveApi.Interface;
@@ -36,9 +37,9 @@ internal class NewRenderer : BaseRenderer
private Cube greenAxisVAO;
private Cube blueAxisVAO;
- public override void Initialize(HostInterface CurrentHost, BaseOptions CurrentOptions)
+ public override void Initialize(HostInterface CurrentHost, BaseOptions CurrentOptions, FileSystem system)
{
- base.Initialize(CurrentHost, CurrentOptions);
+ base.Initialize(CurrentHost, CurrentOptions, system);
if (!ForceLegacyOpenGL)
{
@@ -255,22 +256,22 @@ private void RenderOverlays()
{
keys = new[] { new[] { "F7" }, new[] { "F8" }, new[] { "F10" } };
Keys.Render(4, 4, 20, Fonts.SmallFont, keys);
- OpenGlString.Draw(Fonts.SmallFont, "Open one or more objects", new Point(32, 4), TextAlignment.TopLeft, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, "Display the options window", new Point(32, 24), TextAlignment.TopLeft, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, "Display the train settings window", new Point(32, 44), TextAlignment.TopLeft, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, $"v{Application.ProductVersion}", new Point(Screen.Width - 8, Screen.Height - 20), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Open one or more objects", new Vector2(32, 4), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Display the options window", new Vector2(32, 24), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Display the train settings window", new Vector2(32, 44), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"v{Application.ProductVersion}", new Vector2(Screen.Width - 8, Screen.Height - 20), TextAlignment.TopLeft, TextColor);
if (Interface.LogMessages.Count == 1)
{
Keys.Render(4, 64, 20, Fonts.SmallFont, new[] { new[] { "F9" } });
if (Interface.LogMessages[0].Type != MessageType.Information)
{
- OpenGlString.Draw(Fonts.SmallFont, "Display the 1 error message recently generated.", new Point(32, 64), TextAlignment.TopLeft, new Color128(1.0f, 0.5f, 0.5f));
+ OpenGlString.Draw(Fonts.SmallFont, "Display the 1 error message recently generated.", new Vector2(32, 64), TextAlignment.TopLeft, new Color128(1.0f, 0.5f, 0.5f));
}
else
{
//If all of our messages are information, then print the message text in grey
- OpenGlString.Draw(Fonts.SmallFont, "Display the 1 message recently generated.", new Point(32, 64), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Display the 1 message recently generated.", new Vector2(32, 64), TextAlignment.TopLeft, TextColor);
}
}
else if (Interface.LogMessages.Count > 1)
@@ -280,25 +281,25 @@ private void RenderOverlays()
if (error)
{
- OpenGlString.Draw(Fonts.SmallFont, $"Display the {Interface.LogMessages.Count.ToString(culture)} error messages recently generated.", new Point(32, 64), TextAlignment.TopLeft, new Color128(1.0f, 0.5f, 0.5f));
+ OpenGlString.Draw(Fonts.SmallFont, $"Display the {Interface.LogMessages.Count.ToString(culture)} error messages recently generated.", new Vector2(32, 64), TextAlignment.TopLeft, new Color128(1.0f, 0.5f, 0.5f));
}
else
{
- OpenGlString.Draw(Fonts.SmallFont, $"Display the {Interface.LogMessages.Count.ToString(culture)} messages recently generated.", new Point(32, 64), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"Display the {Interface.LogMessages.Count.ToString(culture)} messages recently generated.", new Vector2(32, 64), TextAlignment.TopLeft, TextColor);
}
}
}
else
{
- OpenGlString.Draw(Fonts.SmallFont, $"Position: {Camera.AbsolutePosition.X.ToString("0.00", culture)}, {Camera.AbsolutePosition.Y.ToString("0.00", culture)}, {Camera.AbsolutePosition.Z.ToString("0.00", culture)}", new Point((int)(0.5 * Screen.Width - 88), 4), TextAlignment.TopLeft, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, $"Renderer: {(AvailableNewRenderer ? "New (GL 3.0)" : "Old (GL 1.2)")}", new Point((int)(0.5 * Screen.Width - 88), 24), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"Position: {Camera.AbsolutePosition.X.ToString("0.00", culture)}, {Camera.AbsolutePosition.Y.ToString("0.00", culture)}, {Camera.AbsolutePosition.Z.ToString("0.00", culture)}", new Vector2((int)(0.5 * Screen.Width - 88), 4), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"Renderer: {(AvailableNewRenderer ? "New (GL 3.0)" : "Old (GL 1.2)")}", new Vector2((int)(0.5 * Screen.Width - 88), 24), TextAlignment.TopLeft, TextColor);
keys = new[] { new[] { "F5" }, new[] { "F7" }, new[] { "del" }, new[] { "F8" }, new[] { "F10" } };
Keys.Render(4, 4, 24, Fonts.SmallFont, keys);
- OpenGlString.Draw(Fonts.SmallFont, "Reload the currently open objects", new Point(32, 4), TextAlignment.TopLeft, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, "Open additional objects", new Point(32, 24), TextAlignment.TopLeft, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, "Clear currently open objects", new Point(32, 44), TextAlignment.TopLeft, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, "Display the options window", new Point(32, 64), TextAlignment.TopLeft, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, "Display the train settings window", new Point(32, 84), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Reload the currently open objects", new Vector2(32, 4), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Open additional objects", new Vector2(32, 24), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Clear currently open objects", new Vector2(32, 44), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Display the options window", new Vector2(32, 64), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Display the train settings window", new Vector2(32, 84), TextAlignment.TopLeft, TextColor);
keys = new[] { new[] { "F" }, new[] { "N" }, new[] { "L" }, new[] { "G" }, new[] { "B" }, new[] { "I" }, new[] { "R" } };
Keys.Render(Screen.Width - 20, 4, 16, Fonts.SmallFont, keys);
@@ -306,14 +307,14 @@ private void RenderOverlays()
Keys.Render(Screen.Width - 36, 124, 32, Fonts.SmallFont, keys);
keys = new[] { new[] { "R" } };
Keys.Render(Screen.Width - 20, 144, 16, Fonts.SmallFont, keys);
- OpenGlString.Draw(Fonts.SmallFont, $"WireFrame: {(OptionWireFrame ? "on" : "off")}", new Point(Screen.Width - 28, 4), TextAlignment.TopRight, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, $"Normals: {(OptionNormals ? "on" : "off")}", new Point(Screen.Width - 28, 24), TextAlignment.TopRight, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, $"Lighting: {(Program.LightingTarget == 0 ? "night" : "day")}", new Point(Screen.Width - 28, 44), TextAlignment.TopRight, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, $"Grid: {(OptionCoordinateSystem ? "on" : "off")}", new Point(Screen.Width - 28, 64), TextAlignment.TopRight, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, $"Background: {GetBackgroundColorName()}", new Point(Screen.Width - 28, 84), TextAlignment.TopRight, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, "Hide interface:", new Point(Screen.Width - 28, 104), TextAlignment.TopRight, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, $"{(RenderStatsOverlay ? "Hide" : "Show")} renderer statistics", new Point(Screen.Width - 44, 124), TextAlignment.TopRight, TextColor);
- OpenGlString.Draw(Fonts.SmallFont, "Switch renderer type:", new Point(Screen.Width - 28, 144), TextAlignment.TopRight, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"WireFrame: {(OptionWireFrame ? "on" : "off")}", new Vector2(Screen.Width - 28, 4), TextAlignment.TopRight, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"Normals: {(OptionNormals ? "on" : "off")}", new Vector2(Screen.Width - 28, 24), TextAlignment.TopRight, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"Lighting: {(Program.LightingTarget == 0 ? "night" : "day")}", new Vector2(Screen.Width - 28, 44), TextAlignment.TopRight, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"Grid: {(OptionCoordinateSystem ? "on" : "off")}", new Vector2(Screen.Width - 28, 64), TextAlignment.TopRight, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"Background: {GetBackgroundColorName()}", new Vector2(Screen.Width - 28, 84), TextAlignment.TopRight, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Hide interface:", new Vector2(Screen.Width - 28, 104), TextAlignment.TopRight, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"{(RenderStatsOverlay ? "Hide" : "Show")} renderer statistics", new Vector2(Screen.Width - 44, 124), TextAlignment.TopRight, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Switch renderer type:", new Vector2(Screen.Width - 28, 144), TextAlignment.TopRight, TextColor);
keys = new[] { new[] { null, "W", null }, new[] { "A", "S", "D" } };
Keys.Render(4, Screen.Height - 40, 16, Fonts.SmallFont, keys);
@@ -330,12 +331,12 @@ private void RenderOverlays()
if (Interface.LogMessages[0].Type != MessageType.Information)
{
- OpenGlString.Draw(Fonts.SmallFont, "Display the 1 error message recently generated.", new Point(32, 112), TextAlignment.TopLeft, new Color128(1.0f, 0.5f, 0.5f));
+ OpenGlString.Draw(Fonts.SmallFont, "Display the 1 error message recently generated.", new Vector2(32, 112), TextAlignment.TopLeft, new Color128(1.0f, 0.5f, 0.5f));
}
else
{
//If all of our messages are information, then print the message text in grey
- OpenGlString.Draw(Fonts.SmallFont, "Display the 1 message recently generated.", new Point(32, 112), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, "Display the 1 message recently generated.", new Vector2(32, 112), TextAlignment.TopLeft, TextColor);
}
}
else if (Interface.LogMessages.Count > 1)
@@ -345,22 +346,22 @@ private void RenderOverlays()
if (error)
{
- OpenGlString.Draw(Fonts.SmallFont, $"Display the {Interface.LogMessages.Count.ToString(culture)} error messages recently generated.", new Point(32, 112), TextAlignment.TopLeft, new Color128(1.0f, 0.5f, 0.5f));
+ OpenGlString.Draw(Fonts.SmallFont, $"Display the {Interface.LogMessages.Count.ToString(culture)} error messages recently generated.", new Vector2(32, 112), TextAlignment.TopLeft, new Color128(1.0f, 0.5f, 0.5f));
}
else
{
- OpenGlString.Draw(Fonts.SmallFont, $"Display the {Interface.LogMessages.Count.ToString(culture)} messages recently generated.", new Point(32, 112), TextAlignment.TopLeft, TextColor);
+ OpenGlString.Draw(Fonts.SmallFont, $"Display the {Interface.LogMessages.Count.ToString(culture)} messages recently generated.", new Vector2(32, 112), TextAlignment.TopLeft, TextColor);
}
}
if (RenderStatsOverlay)
{
Keys.Render(4, Screen.Height - 126, 116, Fonts.SmallFont, new[] { new[] { "Renderer Statistics" } });
- OpenGlString.Draw(Fonts.SmallFont, $"Total static objects: {VisibleObjects.Objects.Count}", new Point(4, Screen.Height - 112), TextAlignment.TopLeft, Color128.White, true);
- OpenGlString.Draw(Fonts.SmallFont, $"Total animated objects: {ObjectManager.AnimatedWorldObjectsUsed}", new Point(4, Screen.Height - 100), TextAlignment.TopLeft, Color128.White, true);
- OpenGlString.Draw(Fonts.SmallFont, $"Current frame rate: {FrameRate.ToString("0.0", culture)}fps", new Point(4, Screen.Height - 88), TextAlignment.TopLeft, Color128.White, true);
- OpenGlString.Draw(Fonts.SmallFont, $"Total opaque faces: {VisibleObjects.OpaqueFaces.Count}", new Point(4, Screen.Height - 76), TextAlignment.TopLeft, Color128.White, true);
- OpenGlString.Draw(Fonts.SmallFont, $"Total alpha faces: {VisibleObjects.AlphaFaces.Count}", new Point(4, Screen.Height - 64), TextAlignment.TopLeft, Color128.White, true);
+ OpenGlString.Draw(Fonts.SmallFont, $"Total static objects: {VisibleObjects.Objects.Count}", new Vector2(4, Screen.Height - 112), TextAlignment.TopLeft, Color128.White, true);
+ OpenGlString.Draw(Fonts.SmallFont, $"Total animated objects: {ObjectManager.AnimatedWorldObjectsUsed}", new Vector2(4, Screen.Height - 100), TextAlignment.TopLeft, Color128.White, true);
+ OpenGlString.Draw(Fonts.SmallFont, $"Current frame rate: {FrameRate.ToString("0.0", culture)}fps", new Vector2(4, Screen.Height - 88), TextAlignment.TopLeft, Color128.White, true);
+ OpenGlString.Draw(Fonts.SmallFont, $"Total opaque faces: {VisibleObjects.OpaqueFaces.Count}", new Vector2(4, Screen.Height - 76), TextAlignment.TopLeft, Color128.White, true);
+ OpenGlString.Draw(Fonts.SmallFont, $"Total alpha faces: {VisibleObjects.AlphaFaces.Count}", new Vector2(4, Screen.Height - 64), TextAlignment.TopLeft, Color128.White, true);
}
}
}
diff --git a/source/ObjectViewer/ObjectViewer.csproj b/source/ObjectViewer/ObjectViewer.csproj
index 7a03d2eae..36333264d 100644
--- a/source/ObjectViewer/ObjectViewer.csproj
+++ b/source/ObjectViewer/ObjectViewer.csproj
@@ -85,9 +85,9 @@
formTrain.cs
+
-
diff --git a/source/ObjectViewer/System/GameWindow.cs b/source/ObjectViewer/System/GameWindow.cs
index 5e748464f..da4e00ac0 100644
--- a/source/ObjectViewer/System/GameWindow.cs
+++ b/source/ObjectViewer/System/GameWindow.cs
@@ -289,7 +289,7 @@ protected override void OnLoad(EventArgs e)
MouseWheel += Program.MouseWheelEvent;
FileDrop += Program.DragFile;
Program.Renderer.Camera.Reset(new Vector3(-5.0, 2.5, -25.0));
- Program.Renderer.Initialize(Program.CurrentHost,Interface.CurrentOptions);
+ Program.Renderer.Initialize(Program.CurrentHost,Interface.CurrentOptions, Program.FileSystem);
Program.Renderer.Lighting.Initialize();
Program.Renderer.UpdateViewport();
Program.Renderer.InitializeVisibility();
diff --git a/source/OpenBVE/Game/Game.cs b/source/OpenBVE/Game/Game.cs
index a384c20e8..828ceb283 100644
--- a/source/OpenBVE/Game/Game.cs
+++ b/source/OpenBVE/Game/Game.cs
@@ -37,7 +37,11 @@ internal static void Reset(bool ResetLogs) {
// game
Interface.LogMessages.Clear();
Program.CurrentHost.MissingFiles.Clear();
- Program.Renderer.CurrentInterface = InterfaceType.Normal;
+ if (Program.Renderer.CurrentInterface != InterfaceType.Menu)
+ {
+ Program.Renderer.CurrentInterface = InterfaceType.Normal;
+ }
+
Program.CurrentRoute.Comment = "";
Program.CurrentRoute.Image = "";
Program.CurrentRoute.Atmosphere = new Atmosphere();
diff --git a/source/OpenBVE/Game/Menu/Menu.MenuCaption.cs b/source/OpenBVE/Game/Menu/Menu.MenuCaption.cs
new file mode 100644
index 000000000..ed513eec6
--- /dev/null
+++ b/source/OpenBVE/Game/Menu/Menu.MenuCaption.cs
@@ -0,0 +1,14 @@
+namespace OpenBve
+{
+ public sealed partial class Menu
+ {
+ /// A caption to be rendered at the top of the menu
+ private class MenuCaption : MenuEntry
+ {
+ internal MenuCaption(string Text)
+ {
+ this.Text = Text;
+ }
+ }
+ }
+}
diff --git a/source/OpenBVE/Game/Menu/Menu.MenuCommand.cs b/source/OpenBVE/Game/Menu/Menu.MenuCommand.cs
new file mode 100644
index 000000000..ad855ae53
--- /dev/null
+++ b/source/OpenBVE/Game/Menu/Menu.MenuCommand.cs
@@ -0,0 +1,32 @@
+using OpenBveApi.Textures;
+
+namespace OpenBve
+{
+ public sealed partial class Menu
+ {
+ /// A menu entry which performs a command when selected
+ private class MenuCommand : MenuEntry
+ {
+ /// The command tag describing the function of the menu entry
+ internal readonly MenuTag Tag;
+ /// The optional data to be passed with the command
+ internal readonly int Data;
+
+ internal MenuCommand(string Text, MenuTag Tag, int Data)
+ {
+ this.Text = Text;
+ this.Tag = Tag;
+ this.Data = Data;
+ this.Icon = null;
+ }
+
+ internal MenuCommand(string Text, MenuTag Tag, int Data, Texture Icon)
+ {
+ this.Text = Text;
+ this.Tag = Tag;
+ this.Data = Data;
+ this.Icon = Icon;
+ }
+ }
+ }
+}
diff --git a/source/OpenBVE/Game/Menu/Menu.MenuEntry.cs b/source/OpenBVE/Game/Menu/Menu.MenuEntry.cs
new file mode 100644
index 000000000..f250e14cf
--- /dev/null
+++ b/source/OpenBVE/Game/Menu/Menu.MenuEntry.cs
@@ -0,0 +1,69 @@
+using OpenBveApi.Textures;
+
+namespace OpenBve
+{
+ public sealed partial class Menu
+ {
+ /// The base abstract Menu Entry class
+ private abstract class MenuEntry
+ {
+ /// The base text of the menu entry
+ internal string Text;
+ /// The display text of the menu entry
+ internal string DisplayText(double TimeElapsed)
+ {
+ if (DisplayLength == 0)
+ {
+ return Text;
+ }
+ timer += TimeElapsed;
+ if (timer > 0.5)
+ {
+ if (pause)
+ {
+ pause = false;
+ return _displayText;
+ }
+ timer = 0;
+ scroll++;
+ if (scroll == Text.Length)
+ {
+ scroll = 0;
+ pause = true;
+ }
+ _displayText = Text.Substring(scroll);
+ if (_displayText.Length > _displayLength)
+ {
+ _displayText = _displayText.Substring(0, _displayLength);
+ }
+ }
+ return _displayText;
+ }
+ /// Backing property for display text
+ private string _displayText;
+ /// Backing property for display length
+ private int _displayLength;
+ /// The length to display
+ internal int DisplayLength
+ {
+ get
+ {
+ return _displayLength;
+ }
+ set
+ {
+ _displayLength = value;
+ _displayText = Text.Substring(0, value);
+ timer = 0;
+ }
+ }
+ /// The icon to draw for this menu entry
+ internal Texture Icon;
+ //Properties used for controlling the scrolling text if overlong
+ private double timer;
+ private int scroll;
+ private bool pause;
+
+ }
+ }
+}
diff --git a/source/OpenBVE/Game/Menu/Menu.Route.cs b/source/OpenBVE/Game/Menu/Menu.Route.cs
new file mode 100644
index 000000000..f00ace4f5
--- /dev/null
+++ b/source/OpenBVE/Game/Menu/Menu.Route.cs
@@ -0,0 +1,154 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Text;
+using LibRender2.Primitives;
+using OpenBveApi;
+using OpenBveApi.Colors;
+using OpenBveApi.Interface;
+using OpenBveApi.Textures;
+using RouteManager2;
+using Path = OpenBveApi.Path;
+
+namespace OpenBve
+{
+ public partial class Menu
+ {
+ private static BackgroundWorker routeWorkerThread;
+ private static string SearchDirectory;
+ private static string RouteFile;
+ private static Encoding RouteEncoding;
+ private static RouteState RoutefileState;
+ private static readonly Picturebox routePictureBox = new Picturebox(Program.Renderer);
+ private static readonly Textbox routeDescriptionBox = new Textbox(Program.Renderer, Program.Renderer.Fonts.NormalFont, Color128.White, Color128.Black);
+
+ private static void routeWorkerThread_doWork(object sender, DoWorkEventArgs e)
+ {
+ if (string.IsNullOrEmpty(RouteFile))
+ {
+ return;
+ }
+ RouteEncoding = TextEncoding.GetSystemEncodingFromFile(RouteFile);
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\loading.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ routeDescriptionBox.Text = Translations.GetInterfaceString("start_route_processing");
+ Game.Reset(false);
+ bool loaded = false;
+ for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++)
+ {
+ if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.CanLoadRoute(RouteFile))
+ {
+ object Route = (object)Program.CurrentRoute; //must cast to allow us to use the ref keyword.
+ string RailwayFolder = Loading.GetRailwayFolder(RouteFile);
+ string ObjectFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Object");
+ string SoundFolder = OpenBveApi.Path.CombineDirectory(RailwayFolder, "Sound");
+ if (Program.CurrentHost.Plugins[i].Route.LoadRoute(RouteFile, RouteEncoding, null, ObjectFolder, SoundFolder, true, ref Route))
+ {
+ Program.CurrentRoute = (CurrentRoute) Route;
+ }
+ else
+ {
+ if (Program.CurrentHost.Plugins[i].Route.LastException != null)
+ {
+ throw Program.CurrentHost.Plugins[i].Route.LastException; //Re-throw last exception generated by the route parser plugin so that the UI thread captures it
+ }
+ routeDescriptionBox.Text = "An unknown error was enountered whilst attempting to parse the routefile " + RouteFile;
+ RoutefileState = RouteState.Error;
+ }
+ loaded = true;
+ break;
+ }
+ }
+
+ if (!loaded)
+ {
+ throw new Exception("No plugins capable of loading routefile " + RouteFile + " were found.");
+ }
+ }
+
+ private static void routeWorkerThread_completed(object sender, RunWorkerCompletedEventArgs e)
+ {
+ RoutefileState = RouteState.Processed;
+ if (e.Error != null || Program.CurrentRoute == null)
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\route_error.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ if (e.Error != null)
+ {
+ routeDescriptionBox.Text = e.Error.Message;
+ RoutefileState = RouteState.Error;
+ }
+ routeWorkerThread.Dispose();
+ return;
+ }
+ try
+ {
+ // image
+ if (!string.IsNullOrEmpty(Program.CurrentRoute.Image))
+ {
+
+ try
+ {
+ if (File.Exists(Program.CurrentRoute.Image))
+ {
+ Program.CurrentHost.RegisterTexture(Program.CurrentRoute.Image, new TextureParameters(null, null), out routePictureBox.Texture);
+ }
+ else
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\route_unknown.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ }
+
+ }
+ catch
+ {
+ routePictureBox.Texture = null;
+ }
+ }
+ else
+ {
+ string[] f = {".png", ".bmp", ".gif", ".tiff", ".tif", ".jpeg", ".jpg"};
+ int i;
+ for (i = 0; i < f.Length; i++)
+ {
+ string g = OpenBveApi.Path.CombineFile(System.IO.Path.GetDirectoryName(RouteFile),
+ System.IO.Path.GetFileNameWithoutExtension(RouteFile) + f[i]);
+ if (System.IO.File.Exists(g))
+ {
+ try
+ {
+ using (var fs = new FileStream(g, FileMode.Open, FileAccess.Read))
+ {
+ //pictureboxRouteImage.Image = new Bitmap(fs);
+ }
+ }
+ catch
+ {
+ //pictureboxRouteImage.Image = null;
+ }
+ break;
+ }
+ }
+ if (i == f.Length)
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\route_unknown.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ }
+ }
+
+ // description
+ string Description = Program.CurrentRoute.Comment.ConvertNewlinesToCrLf();
+ if (Description.Length != 0)
+ {
+ routeDescriptionBox.Text = Description;
+ }
+ else
+ {
+ routeDescriptionBox.Text = System.IO.Path.GetFileNameWithoutExtension(RouteFile);
+ }
+ }
+ catch (Exception ex)
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\route_error.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ routeDescriptionBox.Text = ex.Message;
+ RouteFile = null;
+ }
+ }
+ }
+}
diff --git a/source/OpenBVE/Game/Menu/Menu.RouteState.cs b/source/OpenBVE/Game/Menu/Menu.RouteState.cs
new file mode 100644
index 000000000..f52dc92c9
--- /dev/null
+++ b/source/OpenBVE/Game/Menu/Menu.RouteState.cs
@@ -0,0 +1,19 @@
+namespace OpenBve
+{
+ public partial class Menu
+ {
+ private enum RouteState
+ {
+ /// No routefile is currently selected
+ NoneSelected,
+ /// The background thread is currently loading the routefile data
+ Loading,
+ /// The background thread has processed the route data
+ Processed,
+ /// An error was encountered loading the route data
+ Error
+
+ }
+ }
+
+}
diff --git a/source/OpenBVE/Game/Menu/Menu.SingleMenu.cs b/source/OpenBVE/Game/Menu/Menu.SingleMenu.cs
new file mode 100644
index 000000000..37c35de84
--- /dev/null
+++ b/source/OpenBVE/Game/Menu/Menu.SingleMenu.cs
@@ -0,0 +1,476 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using DavyKager;
+using OpenBveApi.Graphics;
+using OpenBveApi.Hosts;
+using OpenBveApi.Interface;
+using OpenBveApi.Math;
+using OpenBveApi.Textures;
+using TrainManager;
+using Path = OpenBveApi.Path;
+
+namespace OpenBve
+{
+ public sealed partial class Menu
+ {
+ /// Describes a single menu of the menu stack.
+ /// The class is private to Menu, but all its fields are public to allow 'quick-and-dirty'
+ /// access from Menu itself.
+ private class SingleMenu
+ {
+ /// The text alignment for the menu
+ public readonly TextAlignment Align;
+ /// The list of items to be shown
+ public readonly MenuEntry[] Items = { };
+ /// The smaller of the width of the largest item, and the absolute width
+ public readonly double ItemWidth = 0;
+ /// The absolute width
+ public readonly double Width = 0;
+ /// The absolute height
+ public readonly double Height = 0;
+ private readonly double MaxWidth;
+
+ private int lastSelection = int.MaxValue;
+ private int currentSelection;
+
+ public int Selection
+ {
+ get
+ {
+ return currentSelection;
+ }
+ set
+ {
+ lastSelection = currentSelection;
+ currentSelection = value;
+ if (currentSelection != lastSelection && Interface.CurrentOptions.ScreenReaderAvailable)
+ {
+ if (!Tolk.Output(Items[currentSelection].Text))
+ {
+ // failed to output to screen reader, so don't keep trying
+ Interface.CurrentOptions.ScreenReaderAvailable = false;
+ }
+ }
+ }
+ }
+ public int TopItem; // the top displayed menu item
+ internal readonly MenuType Type;
+
+
+ /********************
+ MENU C'TOR
+ *********************/
+ public SingleMenu(MenuType menuType, int data = 0, double maxWidth = 0)
+ {
+ MaxWidth = maxWidth;
+ Type = menuType;
+ int i, menuItem;
+ int jump = 0;
+ Vector2 size;
+
+ Align = TextAlignment.TopMiddle;
+ Height = Width = 0;
+ Selection = 0; // defaults to first menu item
+ switch (menuType)
+ {
+ case MenuType.GameStart: // top level menu
+ if (routeWorkerThread == null)
+ {
+ //Create the worker thread for route details processing on first launch of main menu
+ routeWorkerThread = new BackgroundWorker();
+ routeWorkerThread.DoWork += routeWorkerThread_doWork;
+ routeWorkerThread.RunWorkerCompleted += routeWorkerThread_completed;
+ //Load texture
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\loading.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ }
+ Items = new MenuEntry[3];
+ Items[0] = new MenuCommand("Open Route File", MenuTag.RouteList, 0);
+
+ if (!Interface.CurrentOptions.KioskMode)
+ {
+ //Don't allow quitting or customisation of the controls in kiosk mode
+ Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_customize_controls"), MenuTag.MenuControls, 0);
+ Items[2] = new MenuCommand(Translations.GetInterfaceString("menu_quit"), MenuTag.MenuQuit, 0);
+ }
+ else
+ {
+ Array.Resize(ref Items, Items.Length - 2);
+ }
+ SearchDirectory = Program.FileSystem.InitialRouteFolder;
+ Align = TextAlignment.TopLeft;
+ break;
+ case MenuType.RouteList:
+ string[] potentialFiles = { };
+ string[] directoryList = { };
+ bool drives = false;
+ if (SearchDirectory != string.Empty)
+ {
+ try
+ {
+ potentialFiles = Directory.GetFiles(SearchDirectory);
+ directoryList = Directory.GetDirectories(SearchDirectory);
+ }
+ catch
+ {
+ // Ignored
+ }
+ }
+ else
+ {
+ DriveInfo[] systemDrives = DriveInfo.GetDrives();
+ directoryList = new string[systemDrives.Length];
+ for (int k = 0; k < systemDrives.Length; k++)
+ {
+ directoryList[k] = systemDrives[k].Name;
+ }
+ drives = true;
+ }
+
+ Items = new MenuEntry[potentialFiles.Length + directoryList.Length + 2];
+ Items[0] = new MenuCaption(SearchDirectory);
+ Items[1] = new MenuCommand("...", MenuTag.ParentDirectory, 0);
+ int totalEntries = 2;
+ for (int j = 0; j < directoryList.Length; j++)
+ {
+ DirectoryInfo directoryInfo = new DirectoryInfo(directoryList[j]);
+ if (Program.CurrentHost.Platform != HostPlatform.MicrosoftWindows && directoryInfo.Name[0] == '.')
+ {
+ continue;
+ }
+ Items[totalEntries] = new MenuCommand(directoryInfo.Name, MenuTag.Directory, 0);
+ if (drives)
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\icon_disk.png"), new TextureParameters(null, null), out Items[totalEntries].Icon);
+ }
+ else
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\icon_folder.png"), new TextureParameters(null, null), out Items[totalEntries].Icon);
+ }
+
+ totalEntries++;
+ }
+
+ for (int j = 0; j < potentialFiles.Length; j++)
+ {
+ string fileName = System.IO.Path.GetFileName(potentialFiles[j]);
+ if (Program.CurrentHost.Platform != HostPlatform.MicrosoftWindows && fileName[0] == '.')
+ {
+ continue;
+ }
+ if (fileName.ToLowerInvariant().EndsWith(".csv") || fileName.ToLowerInvariant().EndsWith(".rw"))
+ {
+ Items[totalEntries] = new MenuCommand(fileName, MenuTag.RouteFile, 0);
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\icon_route.png"), new TextureParameters(null, null), out Items[totalEntries].Icon);
+ totalEntries++;
+ }
+ }
+ Array.Resize(ref Items, totalEntries);
+ Align = TextAlignment.TopLeft;
+ break;
+ case MenuType.TrainList:
+ potentialFiles = new string[] { };
+ directoryList = new string[] { };
+ drives = false;
+ if (SearchDirectory != string.Empty)
+ {
+ try
+ {
+ potentialFiles = Directory.GetFiles(SearchDirectory);
+ directoryList = Directory.GetDirectories(SearchDirectory);
+ }
+ catch
+ {
+ // Ignored
+ }
+ }
+ else
+ {
+ DriveInfo[] systemDrives = DriveInfo.GetDrives();
+ directoryList = new string[systemDrives.Length];
+ for (int k = 0; k < systemDrives.Length; k++)
+ {
+ directoryList[k] = systemDrives[k].Name;
+ }
+ drives = true;
+ }
+
+ Items = new MenuEntry[potentialFiles.Length + directoryList.Length + 2];
+ Items[0] = new MenuCaption(SearchDirectory);
+ Items[1] = new MenuCommand("...", MenuTag.ParentDirectory, 0);
+ totalEntries = 2;
+ for (int j = 0; j < directoryList.Length; j++)
+ {
+ bool isTrain = false;
+ for (int k = 0; k < Program.CurrentHost.Plugins.Length; k++)
+ {
+ if (Program.CurrentHost.Plugins[k].Train != null && Program.CurrentHost.Plugins[k].Train.CanLoadTrain(directoryList[j]))
+ {
+ isTrain = true;
+ break;
+ }
+ }
+ DirectoryInfo directoryInfo = new DirectoryInfo(directoryList[j]);
+ if (Program.CurrentHost.Platform != HostPlatform.MicrosoftWindows && directoryInfo.Name[0] == '.')
+ {
+ continue;
+ }
+ if (!isTrain)
+ {
+ Items[totalEntries] = new MenuCommand(directoryInfo.Name, MenuTag.Directory, 0);
+ if (drives)
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\icon_disk.png"), new TextureParameters(null, null), out Items[totalEntries].Icon);
+ }
+ else
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\icon_folder.png"), new TextureParameters(null, null), out Items[totalEntries].Icon);
+ }
+ }
+ else
+ {
+ Items[totalEntries] = new MenuCommand(directoryInfo.Name, MenuTag.TrainDirectory, 0);
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\icon_train.png"), new TextureParameters(null, null), out Items[totalEntries].Icon);
+ }
+ totalEntries++;
+ }
+ Array.Resize(ref Items, totalEntries);
+ Align = TextAlignment.TopLeft;
+ break;
+ case MenuType.Top: // top level menu
+ if (Interface.CurrentOptions.ScreenReaderAvailable)
+ {
+ if (!Tolk.Output(Translations.GetInterfaceString("menu_title")))
+ {
+ // failed to output to screen reader, so don't keep trying
+ Interface.CurrentOptions.ScreenReaderAvailable = false;
+ }
+ }
+ for (i = 0; i < Program.CurrentRoute.Stations.Length; i++)
+ if (Program.CurrentRoute.Stations[i].PlayerStops() & Program.CurrentRoute.Stations[i].Stops.Length > 0)
+ {
+ jump = 1;
+ break;
+ }
+ Items = new MenuEntry[4 + jump];
+ Items[0] = new MenuCommand(Translations.GetInterfaceString("menu_resume"), MenuTag.BackToSim, 0);
+ if (jump > 0)
+ Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_jump"), MenuTag.MenuJumpToStation, 0);
+ if (!Interface.CurrentOptions.KioskMode)
+ {
+ //Don't allow quitting or customisation of the controls in kiosk mode
+ Items[1 + jump] = new MenuCommand(Translations.GetInterfaceString("menu_exit"), MenuTag.MenuExitToMainMenu, 0);
+ Items[2 + jump] = new MenuCommand(Translations.GetInterfaceString("menu_customize_controls"), MenuTag.MenuControls, 0);
+ Items[3 + jump] = new MenuCommand(Translations.GetInterfaceString("menu_quit"), MenuTag.MenuQuit, 0);
+ }
+ else
+ {
+ Array.Resize(ref Items, Items.Length -3);
+ }
+ break;
+ case MenuType.JumpToStation: // list of stations to jump to
+ // count the number of available stations
+ menuItem = 0;
+ for (i = 0; i < Program.CurrentRoute.Stations.Length; i++)
+ if (Program.CurrentRoute.Stations[i].PlayerStops() & Program.CurrentRoute.Stations[i].Stops.Length > 0)
+ menuItem++;
+ // list available stations, selecting the next station as predefined choice
+ jump = 0; // no jump found yet
+ Items = new MenuEntry[menuItem + 1];
+ Items[0] = new MenuCommand(Translations.GetInterfaceString("menu_back"), MenuTag.MenuBack, 0);
+ menuItem = 1;
+ for (i = 0; i < Program.CurrentRoute.Stations.Length; i++)
+ if (Program.CurrentRoute.Stations[i].PlayerStops() & Program.CurrentRoute.Stations[i].Stops.Length > 0)
+ {
+ Items[menuItem] = new MenuCommand(Program.CurrentRoute.Stations[i].Name, MenuTag.JumpToStation, i);
+ // if no preferred jump-to-station found yet and this station is
+ // after the last station the user stopped at, select this item
+ if (jump == 0 && i > TrainManagerBase.PlayerTrain.LastStation)
+ {
+ jump = i;
+ Selection = menuItem;
+ }
+ menuItem++;
+ }
+ Align = TextAlignment.TopLeft;
+ break;
+
+ case MenuType.ExitToMainMenu:
+ Items = new MenuEntry[3];
+ Items[0] = new MenuCaption(Translations.GetInterfaceString("menu_exit_question"));
+ Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_exit_no"), MenuTag.MenuBack, 0);
+ Items[2] = new MenuCommand(Translations.GetInterfaceString("menu_exit_yes"), MenuTag.ExitToMainMenu, 0);
+ Selection = 1;
+ break;
+
+ case MenuType.Quit: // ask for quit confirmation
+ Items = new MenuEntry[3];
+ Items[0] = new MenuCaption(Translations.GetInterfaceString("menu_quit_question"));
+ Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_quit_no"), MenuTag.MenuBack, 0);
+ Items[2] = new MenuCommand(Translations.GetInterfaceString("menu_quit_yes"), MenuTag.Quit, 0);
+ Selection = 1;
+ break;
+
+ case MenuType.Controls:
+ //Refresh the joystick list
+ Program.Joysticks.RefreshJoysticks();
+ Items = new MenuEntry[Interface.CurrentControls.Length + 1];
+ Items[0] = new MenuCommand(Translations.GetInterfaceString("menu_back"), MenuTag.MenuBack, 0);
+ for (i = 0; i < Interface.CurrentControls.Length; i++)
+ Items[i + 1] = new MenuCommand(Interface.CurrentControls[i].Command.ToString(), MenuTag.Control, i);
+ Align = TextAlignment.TopLeft;
+ break;
+
+ case MenuType.Control:
+ //Refresh the joystick list
+ Program.Joysticks.RefreshJoysticks();
+ Selection = SelectionNone;
+ Items = new MenuEntry[4];
+ // get code name and description
+ Control loadedControl = Interface.CurrentControls[data];
+ for (int h = 0; h < Translations.CommandInfos.Length; h++)
+ {
+ if (Translations.CommandInfos[h].Command == loadedControl.Command)
+ {
+ Items[0] = new MenuCommand(loadedControl.Command.ToString() + " - " +
+ Translations.CommandInfos[h].Description, MenuTag.None, 0);
+ break;
+ }
+ }
+ // get assignment
+ String str = "";
+ switch (loadedControl.Method)
+ {
+ case ControlMethod.Keyboard:
+ string keyName = loadedControl.Key.ToString();
+ for (int k = 0; k < Translations.TranslatedKeys.Length; k++)
+ {
+ if (Translations.TranslatedKeys[k].Key == loadedControl.Key)
+ {
+ keyName = Translations.TranslatedKeys[k].Description;
+ break;
+ }
+ }
+ if (loadedControl.Modifier != KeyboardModifier.None)
+ {
+ str = Translations.GetInterfaceString("menu_keyboard") + " [" + loadedControl.Modifier + "-" + keyName + "]";
+ }
+ else
+ {
+ str = Translations.GetInterfaceString("menu_keyboard") + " [" + keyName + "]";
+ }
+ break;
+ case ControlMethod.Joystick:
+ str = Translations.GetInterfaceString("menu_joystick") + " " + loadedControl.Device + " [" + loadedControl.Component + " " + loadedControl.Element + "]";
+ switch (loadedControl.Component)
+ {
+ case JoystickComponent.FullAxis:
+ case JoystickComponent.Axis:
+ str += " " + (loadedControl.Direction == 1 ? Translations.GetInterfaceString("menu_joystickdirection_positive") : Translations.GetInterfaceString("menu_joystickdirection_negative"));
+ break;
+ // case Interface.JoystickComponent.Button: // NOTHING TO DO FOR THIS CASE!
+ // str = str;
+ // break;
+ case JoystickComponent.Hat:
+ str += " " + (OpenTK.Input.HatPosition)loadedControl.Direction;
+ break;
+ case JoystickComponent.Invalid:
+ str = Translations.GetInterfaceString("menu_joystick_notavailable");
+ break;
+ }
+ break;
+ case ControlMethod.RailDriver:
+ str = "RailDriver [" + loadedControl.Component + " " + loadedControl.Element + "]";
+ switch (loadedControl.Component)
+ {
+ case JoystickComponent.FullAxis:
+ case JoystickComponent.Axis:
+ str += " " + (loadedControl.Direction == 1 ? Translations.GetInterfaceString("menu_joystickdirection_positive") : Translations.GetInterfaceString("menu_joystickdirection_negative"));
+ break;
+ case JoystickComponent.Invalid:
+ str = Translations.GetInterfaceString("menu_joystick_notavailable");
+ break;
+ }
+ break;
+ case ControlMethod.Invalid:
+ str = Translations.GetInterfaceString("menu_joystick_notavailable");
+ break;
+ }
+ Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_assignment_current") + " " + str, MenuTag.None, 0);
+ Items[2] = new MenuCommand(" ", MenuTag.None, 0);
+ Items[3] = new MenuCommand(Translations.GetInterfaceString("menu_assign"), MenuTag.None, 0);
+ break;
+ case MenuType.TrainDefault:
+ Interface.CurrentOptions.TrainFolder = Loading.GetDefaultTrainFolder(RouteFile);
+ bool canLoad = false;
+ for (int j = 0; j < Program.CurrentHost.Plugins.Length; j++)
+ {
+ if (Program.CurrentHost.Plugins[j].Train != null && Program.CurrentHost.Plugins[j].Train.CanLoadTrain(Interface.CurrentOptions.TrainFolder))
+ {
+ canLoad = true;
+ break;
+ }
+ }
+
+ if (canLoad)
+ {
+ Items = new MenuEntry[3];
+ Items[0] = new MenuCaption(Translations.GetInterfaceString("start_train_default"));
+ Items[1] = new MenuCommand(Translations.GetInterfaceString("start_train_default_yes"), MenuTag.Yes, 0);
+ Items[2] = new MenuCommand(Translations.GetInterfaceString("start_train_default_no"), MenuTag.No, 0);
+ Selection = 1;
+ }
+ else
+ {
+ SearchDirectory = Program.FileSystem.InitialTrainFolder;
+ //Default train not found or not valid
+ Instance.PushMenu(MenuType.TrainList);
+ }
+ break;
+ }
+ // compute menu extent
+ for (i = 0; i < Items.Length; i++)
+ {
+ if (Items[i] == null)
+ {
+ continue;
+ }
+ size = Game.Menu.MenuFont.MeasureString(Items[i].Text);
+ if (Items[i].Icon != null)
+ {
+ size.X += size.Y * 1.25;
+ }
+ if (size.X > Width)
+ {
+ Width = size.X;
+ }
+
+ if (MaxWidth != 0 && size.X > MaxWidth)
+ {
+ for (int j = Items[i].Text.Length - 1; j > 0; j--)
+ {
+ string trimmedText = Items[i].Text.Substring(0, j);
+ size = Game.Menu.MenuFont.MeasureString(trimmedText);
+ double mwi = MaxWidth;
+ if (Items[i].Icon != null)
+ {
+ mwi -= size.Y * 1.25;
+ }
+ if (size.X < mwi)
+ {
+ Items[i].DisplayLength = trimmedText.Length;
+ break;
+ }
+ }
+ Width = MaxWidth;
+ }
+ if (!(Items[i] is MenuCaption && menuType!= MenuType.RouteList && menuType != MenuType.GameStart) && size.X > ItemWidth)
+ ItemWidth = size.X;
+ }
+ Height = Items.Length * Game.Menu.LineHeight;
+ TopItem = 0;
+ }
+
+ }
+ }
+}
diff --git a/source/OpenBVE/Game/Menu.cs b/source/OpenBVE/Game/Menu/Menu.cs
similarity index 52%
rename from source/OpenBVE/Game/Menu.cs
rename to source/OpenBVE/Game/Menu/Menu.cs
index 8bbc19565..6fe8ba270 100644
--- a/source/OpenBVE/Game/Menu.cs
+++ b/source/OpenBVE/Game/Menu/Menu.cs
@@ -1,15 +1,21 @@
-using OpenBveApi.Colors;
+using OpenBveApi.Colors;
using OpenBveApi.Graphics;
using OpenBveApi.Interface;
using System;
using System.Drawing;
-using DavyKager;
+using System.IO;
+using System.Text;
+using System.Windows.Forms;
+using LibRender2.Primitives;
using LibRender2.Screens;
-using LibRender2.Texts;
+using LibRender2.Text;
using OpenBve.Input;
using OpenBveApi;
using OpenBveApi.Input;
+using OpenBveApi.Textures;
using OpenTK;
+using TrainManager;
+using Path = OpenBveApi.Path;
using Vector2 = OpenBveApi.Math.Vector2;
namespace OpenBve
@@ -22,65 +28,22 @@ Implemented as a singleton.
Keeps a stack of menus, allowing navigating forward and back */
/// Implements the in-game menu system; manages addition and removal of individual menus.
- public sealed class Menu
+ public sealed partial class Menu
{
- /// The list of possible tags for a menu entry- These define the functionality of a given menu entry
- public enum MenuTag
- {
- /// Has no functionality/ is blank
- None,
- /// Is a caption for another menu item
- Caption,
- /// Moves up a menu level
- MenuBack,
- /// Enters the submenu containing the list of stations to which the player train may be jumped
- MenuJumpToStation,
- /// Enters the submenu for exiting to the main menu
- MenuExitToMainMenu,
- /// Enters the submenu for customising controls
- MenuControls,
- /// Enters the submenu for quitting the program
- MenuQuit,
- /// Returns to the simulation
- BackToSim,
- /// Jumps to the selected station
- JumpToStation,
- /// Exits to the main menu
- ExitToMainMenu,
- /// Quits the program
- Quit,
- /// Customises the selected control
- Control
- };
-
- /// The list of possible sub-menu types
- public enum MenuType
- {
- /// Not a sub menu
- None,
- /// Returns to the menu level above
- Top,
- /// The station jump menu
- JumpToStation,
- /// Returns to the main menu
- ExitToMainMenu,
- /// Provides a list of controls and allows customisation whilst in-game
- Controls,
- /// Customises the specified control
- Control,
- /// Quits the game
- Quit
- };
-
// components of the semi-transparent screen overlay
private readonly Color128 overlayColor = new Color128(0.0f, 0.0f, 0.0f, 0.2f);
private readonly Color128 backgroundColor = new Color128(0.0f, 0.0f, 0.0f, 1.0f);
private readonly Color128 highlightColor = new Color128(1.0f, 0.69f, 0.0f, 1.0f);
+ private readonly Color128 folderHighlightColor = new Color128(0.0f, 0.69f, 1.0f, 1.0f);
+ private readonly Color128 routeHighlightColor = new Color128(0.0f, 1.0f, 0.69f, 1.0f);
// text colours
private static readonly Color128 ColourCaption = new Color128(0.750f, 0.750f, 0.875f, 1.0f);
private static readonly Color128 ColourDimmed = new Color128(1.000f, 1.000f, 1.000f, 0.5f);
private static readonly Color128 ColourHighlight = Color128.Black;
private static readonly Color128 ColourNormal = Color128.White;
+ private static readonly Picturebox LogoPictureBox = new Picturebox(Program.Renderer);
+
+
// some sizes and constants
// TODO: make borders Menu fields dependent on font size
@@ -91,277 +54,9 @@ public enum MenuType
private const float LineSpacing = 1.75f; // the ratio between the font size and line distance
private const int SelectionNone = -1;
- /********************
- BASE MENU ENTRY CLASS
- *********************/
- private abstract class MenuEntry
- {
- internal string Text;
- }
-
- /********************
- DERIVED MENU ITEM CLASSES
- *********************/
- private class MenuCaption : MenuEntry
- {
- internal MenuCaption(string Text)
- {
- this.Text = Text;
- }
- }
- private class MenuCommand : MenuEntry
- {
- internal readonly MenuTag Tag;
- internal readonly int Data;
- internal MenuCommand(string Text, MenuTag Tag, int Data)
- {
- this.Text = Text;
- this.Tag = Tag;
- this.Data = Data;
- }
- }
-
- /********************
- SINGLE-MENU ENTRY CLASS
- *********************
- Describes a single menu of the menu stack.
- The class is private to Menu, but all its fields are public to allow 'quick-and-dirty'
- access from Menu itself. */
- private class SingleMenu
- {
- /********************
- MENU FIELDS
- *********************/
- public readonly TextAlignment Align;
- public readonly MenuEntry[] Items = { };
- public readonly int ItemWidth = 0;
- public readonly int Width = 0;
- public readonly int Height = 0;
-
- private int lastSelection = int.MaxValue;
- private int currentSelection;
-
- public int Selection
- {
- get
- {
- return currentSelection;
- }
- set
- {
- lastSelection = currentSelection;
- currentSelection = value;
- if (currentSelection != lastSelection && Interface.CurrentOptions.ScreenReaderAvailable)
- {
- if (!Tolk.Output(Items[currentSelection].Text))
- {
- // failed to output to screen reader, so don't keep trying
- Interface.CurrentOptions.ScreenReaderAvailable = false;
- }
- }
- }
- }
- public int TopItem; // the top displayed menu item
-
-
- /********************
- MENU C'TOR
- *********************/
- public SingleMenu(MenuType menuType, int data = 0)
- {
- int i, menuItem;
- int jump = 0;
- Size size;
-
- Align = TextAlignment.TopMiddle;
- Height = Width = 0;
- Selection = 0; // defaults to first menu item
- switch (menuType)
- {
- case MenuType.Top: // top level menu
- if (Interface.CurrentOptions.ScreenReaderAvailable)
- {
- if (!Tolk.Output(Translations.GetInterfaceString("menu_title")))
- {
- // failed to output to screen reader, so don't keep trying
- Interface.CurrentOptions.ScreenReaderAvailable = false;
- }
- }
- for (i = 0; i < Program.CurrentRoute.Stations.Length; i++)
- if (Program.CurrentRoute.Stations[i].PlayerStops() & Program.CurrentRoute.Stations[i].Stops.Length > 0)
- {
- jump = 1;
- break;
- }
- Items = new MenuEntry[4 + jump];
- Items[0] = new MenuCommand(Translations.GetInterfaceString("menu_resume"), MenuTag.BackToSim, 0);
- if (jump > 0)
- Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_jump"), MenuTag.MenuJumpToStation, 0);
- if (!Interface.CurrentOptions.KioskMode)
- {
- //Don't allow quitting or customisation of the controls in kiosk mode
- Items[1 + jump] = new MenuCommand(Translations.GetInterfaceString("menu_exit"), MenuTag.MenuExitToMainMenu, 0);
- Items[2 + jump] = new MenuCommand(Translations.GetInterfaceString("menu_customize_controls"), MenuTag.MenuControls, 0);
- Items[3 + jump] = new MenuCommand(Translations.GetInterfaceString("menu_quit"), MenuTag.MenuQuit, 0);
- }
- else
- {
- Array.Resize(ref Items, Items.Length -3);
- }
- break;
-
- case MenuType.JumpToStation: // list of stations to jump to
- // count the number of available stations
- menuItem = 0;
- for (i = 0; i < Program.CurrentRoute.Stations.Length; i++)
- if (Program.CurrentRoute.Stations[i].PlayerStops() & Program.CurrentRoute.Stations[i].Stops.Length > 0)
- menuItem++;
- // list available stations, selecting the next station as predefined choice
- jump = 0; // no jump found yet
- Items = new MenuEntry[menuItem + 1];
- Items[0] = new MenuCommand(Translations.GetInterfaceString("menu_back"), MenuTag.MenuBack, 0);
- menuItem = 1;
- for (i = 0; i < Program.CurrentRoute.Stations.Length; i++)
- if (Program.CurrentRoute.Stations[i].PlayerStops() & Program.CurrentRoute.Stations[i].Stops.Length > 0)
- {
- Items[menuItem] = new MenuCommand(Program.CurrentRoute.Stations[i].Name, MenuTag.JumpToStation, i);
- // if no preferred jump-to-station found yet and this station is
- // after the last station the user stopped at, select this item
- if (jump == 0 && i > TrainManager.PlayerTrain.LastStation)
- {
- jump = i;
- Selection = menuItem;
- }
- menuItem++;
- }
- Align = TextAlignment.TopLeft;
- break;
-
- case MenuType.ExitToMainMenu:
- Items = new MenuEntry[3];
- Items[0] = new MenuCaption(Translations.GetInterfaceString("menu_exit_question"));
- Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_exit_no"), MenuTag.MenuBack, 0);
- Items[2] = new MenuCommand(Translations.GetInterfaceString("menu_exit_yes"), MenuTag.ExitToMainMenu, 0);
- Selection = 1;
- break;
-
- case MenuType.Quit: // ask for quit confirmation
- Items = new MenuEntry[3];
- Items[0] = new MenuCaption(Translations.GetInterfaceString("menu_quit_question"));
- Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_quit_no"), MenuTag.MenuBack, 0);
- Items[2] = new MenuCommand(Translations.GetInterfaceString("menu_quit_yes"), MenuTag.Quit, 0);
- Selection = 1;
- break;
-
- case MenuType.Controls:
- //Refresh the joystick list
- Program.Joysticks.RefreshJoysticks();
- Items = new MenuEntry[Interface.CurrentControls.Length + 1];
- Items[0] = new MenuCommand(Translations.GetInterfaceString("menu_back"), MenuTag.MenuBack, 0);
- for (i = 0; i < Interface.CurrentControls.Length; i++)
- Items[i + 1] = new MenuCommand(Interface.CurrentControls[i].Command.ToString(), MenuTag.Control, i);
- Align = TextAlignment.TopLeft;
- break;
-
- case MenuType.Control:
- //Refresh the joystick list
- Program.Joysticks.RefreshJoysticks();
- Selection = SelectionNone;
- Items = new MenuEntry[4];
- // get code name and description
- Control loadedControl = Interface.CurrentControls[data];
- for (int h = 0; h < Translations.CommandInfos.Length; h++)
- {
- if (Translations.CommandInfos[h].Command == loadedControl.Command)
- {
- Items[0] = new MenuCommand(loadedControl.Command.ToString() + " - " +
- Translations.CommandInfos[h].Description, MenuTag.None, 0);
- break;
- }
- }
- // get assignment
- String str = "";
- switch (loadedControl.Method)
- {
- case ControlMethod.Keyboard:
- string keyName = loadedControl.Key.ToString();
- for (int k = 0; k < Translations.TranslatedKeys.Length; k++)
- {
- if (Translations.TranslatedKeys[k].Key == loadedControl.Key)
- {
- keyName = Translations.TranslatedKeys[k].Description;
- break;
- }
- }
- if (loadedControl.Modifier != KeyboardModifier.None)
- {
- str = Translations.GetInterfaceString("menu_keyboard") + " [" + loadedControl.Modifier + "-" + keyName + "]";
- }
- else
- {
- str = Translations.GetInterfaceString("menu_keyboard") + " [" + keyName + "]";
- }
- break;
- case ControlMethod.Joystick:
- str = Translations.GetInterfaceString("menu_joystick") + " " + loadedControl.Device + " [" + loadedControl.Component + " " + loadedControl.Element + "]";
- switch (loadedControl.Component)
- {
- case JoystickComponent.FullAxis:
- case JoystickComponent.Axis:
- str += " " + (loadedControl.Direction == 1 ? Translations.GetInterfaceString("menu_joystickdirection_positive") : Translations.GetInterfaceString("menu_joystickdirection_negative"));
- break;
- // case Interface.JoystickComponent.Button: // NOTHING TO DO FOR THIS CASE!
- // str = str;
- // break;
- case JoystickComponent.Hat:
- str += " " + (OpenTK.Input.HatPosition)loadedControl.Direction;
- break;
- case JoystickComponent.Invalid:
- str = Translations.GetInterfaceString("menu_joystick_notavailable");
- break;
- }
- break;
- case ControlMethod.RailDriver:
- str = "RailDriver [" + loadedControl.Component + " " + loadedControl.Element + "]";
- switch (loadedControl.Component)
- {
- case JoystickComponent.FullAxis:
- case JoystickComponent.Axis:
- str += " " + (loadedControl.Direction == 1 ? Translations.GetInterfaceString("menu_joystickdirection_positive") : Translations.GetInterfaceString("menu_joystickdirection_negative"));
- break;
- case JoystickComponent.Invalid:
- str = Translations.GetInterfaceString("menu_joystick_notavailable");
- break;
- }
- break;
- case ControlMethod.Invalid:
- str = Translations.GetInterfaceString("menu_joystick_notavailable");
- break;
- }
- Items[1] = new MenuCommand(Translations.GetInterfaceString("menu_assignment_current") + " " + str, MenuTag.None, 0);
- Items[2] = new MenuCommand(" ", MenuTag.None, 0);
- Items[3] = new MenuCommand(Translations.GetInterfaceString("menu_assign"), MenuTag.None, 0);
- break;
- }
-
- // compute menu extent
- for (i = 0; i < Items.Length; i++)
- {
- if (Items[i] == null)
- {
- continue;
- }
- size = Game.Menu.MenuFont.MeasureString(Items[i].Text);
- if (size.Width > Width)
- Width = size.Width;
- if (!(Items[i] is MenuCaption) && size.Width > ItemWidth)
- ItemWidth = size.Width;
- }
- Height = Items.Length * Game.Menu.LineHeight;
- TopItem = 0;
- }
-
- } // end of private class SingleMenu
+ private double lastTimeElapsed;
+
+ // end of private class SingleMenu
/********************
MENU SYSTEM FIELDS
@@ -376,8 +71,8 @@ MENU SYSTEM FIELDS
private SingleMenu[] Menus = { };
private OpenGlFont menuFont = null;
// area occupied by the items of the current menu in screen coordinates
- private int menuXmin, menuXmax, menuYmin, menuYmax;
- private int topItemY; // the top edge of top item
+ private double menuXmin, menuXmax, menuYmin, menuYmax;
+ private double topItemY; // the top edge of top item
private int visibleItems; // the number of visible items
// properties (to allow read-only access to some fields)
internal int LineHeight
@@ -446,6 +141,23 @@ private void Init()
break;
}
}
+ int quarterWidth = (int) (Program.Renderer.Screen.Width / 4.0);
+ int descriptionLoc = Program.Renderer.Screen.Width - quarterWidth - quarterWidth / 2;
+ int descriptionWidth = quarterWidth + quarterWidth / 2;
+ int descriptionHeight = descriptionWidth;
+ if (descriptionHeight + quarterWidth > Program.Renderer.Screen.Height - 50)
+ {
+ descriptionHeight = Program.Renderer.Screen.Height - quarterWidth - 50;
+ }
+ routeDescriptionBox.Location = new Vector2(descriptionLoc, quarterWidth);
+ routeDescriptionBox.Size = new Vector2(descriptionWidth, descriptionHeight);
+ int imageLoc = Program.Renderer.Screen.Width - quarterWidth - quarterWidth / 4;
+ routePictureBox.Location = new Vector2(imageLoc, 0);
+ routePictureBox.Size = new Vector2(quarterWidth, quarterWidth);
+ routePictureBox.BackgroundColor = Color128.White;
+ LogoPictureBox.Location = new Vector2(Program.Renderer.Screen.Width / 2.0, Program.Renderer.Screen.Height / 8.0);
+ LogoPictureBox.Size = new Vector2(Program.Renderer.Screen.Width / 2.0, Program.Renderer.Screen.Width / 2.0);
+ LogoPictureBox.Texture = Program.Renderer.ProgramLogo;
isInitialized = true;
}
@@ -457,6 +169,7 @@ private void Reset()
CurrMenu = -1;
Menus = new SingleMenu[] { };
isCustomisingControl = false;
+ routeDescriptionBox.CurrentlySelected = false;
}
//
@@ -466,7 +179,8 @@ private void Reset()
/// Pushes a menu into the menu stack
/// The type of menu to push
/// The index of the menu in the menu stack (If pushing an existing higher level menu)
- public void PushMenu(MenuType type, int data = 0)
+ /// Whether we are replacing the selected menu item
+ public void PushMenu(MenuType type, int data = 0, bool replace = false)
{
if (Program.Renderer.CurrentInterface != InterfaceType.Menu)
{
@@ -475,10 +189,23 @@ public void PushMenu(MenuType type, int data = 0)
}
if (!isInitialized)
Init();
- CurrMenu++;
+ if (!replace)
+ {
+ CurrMenu++;
+ }
+
if (Menus.Length <= CurrMenu)
Array.Resize(ref Menus, CurrMenu + 1);
- Menus[CurrMenu] = new Menu.SingleMenu(type, data);
+ int MaxWidth = 0;
+ if (type == MenuType.RouteList || type == MenuType.TrainList)
+ {
+ MaxWidth = Program.Renderer.Screen.Width / 2;
+ }
+ Menus[CurrMenu] = new SingleMenu(type, data, MaxWidth);
+ if (replace)
+ {
+ Menus[CurrMenu].Selection = 1;
+ }
PositionMenu();
Program.Renderer.CurrentInterface = InterfaceType.Menu;
}
@@ -491,7 +218,7 @@ public void PopMenu()
{
if (CurrMenu > 0) // if more than one menu remaining...
{
- CurrMenu--; // ...back to previous smenu
+ CurrMenu--; // ...back to previous menu
PositionMenu();
}
else
@@ -561,6 +288,21 @@ internal void ProcessMouseScroll(int Scroll)
{
// Load the current menu
SingleMenu menu = Menus[CurrMenu];
+ if (menu.Type == MenuType.RouteList || menu.Type == MenuType.TrainList)
+ {
+ if (routeDescriptionBox.CurrentlySelected)
+ {
+ if (Math.Abs(Scroll) == Scroll)
+ {
+ routeDescriptionBox.VerticalScroll(-1);
+ }
+ else
+ {
+ routeDescriptionBox.VerticalScroll(1);
+ }
+ return;
+ }
+ }
if (Math.Abs(Scroll) == Scroll)
{
//Negative
@@ -568,7 +310,6 @@ internal void ProcessMouseScroll(int Scroll)
{
menu.TopItem--;
}
-
}
else
{
@@ -604,12 +345,29 @@ internal bool ProcessMouseMove(int x, int y)
menu.Selection = menu.TopItem - 1;
return true;
}
+ if (menu.Type == MenuType.RouteList || menu.Type == MenuType.TrainList)
+ {
+ if (x > routeDescriptionBox.Location.X && x < routeDescriptionBox.Location.X + routeDescriptionBox.Size.X && y > routeDescriptionBox.Location.Y && y < routeDescriptionBox.Location.Y + routeDescriptionBox.Size.Y)
+ {
+ routeDescriptionBox.CurrentlySelected = true;
+ }
+ else
+ {
+ routeDescriptionBox.CurrentlySelected = false;
+ }
+ //HACK: Use this to trigger our menu start button!
+ if (x > Program.Renderer.Screen.Width - 200 && x < Program.Renderer.Screen.Width - 10 && y > Program.Renderer.Screen.Height - 40 && y < Program.Renderer.Screen.Height - 10)
+ {
+ menu.Selection = int.MaxValue;
+ return true;
+ }
+ }
if (x < menuXmin || x > menuXmax || y < menuYmin || y > menuYmax)
{
return false;
}
- int item = (y - topItemY) / lineHeight + menu.TopItem;
+ int item = (int) ((y - topItemY) / lineHeight + menu.TopItem);
// if the mouse is above a command item, select it
if (item >= 0 && item < menu.Items.Length && menu.Items[item] is MenuCommand)
{
@@ -620,7 +378,7 @@ internal bool ProcessMouseMove(int x, int y)
return true;
}
}
-
+
return false;
}
@@ -661,16 +419,43 @@ internal void ProcessCommand(Translations.Command cmd, double timeElapsed)
{
return;
}
+ SingleMenu menu = Menus[CurrMenu];
// MenuBack is managed independently from single menu data
if (cmd == Translations.Command.MenuBack)
{
- PopMenu();
+ if (menu.Type == MenuType.GameStart)
+ {
+ Instance.PushMenu(MenuType.Quit);
+ }
+ else
+ {
+ PopMenu();
+ }
return;
}
-
- SingleMenu menu = Menus[CurrMenu];
+
if (menu.Selection == SelectionNone) // if menu has no selection, do nothing
return;
+ if (menu.Selection == int.MaxValue)
+ {
+ if (RoutefileState == RouteState.Error)
+ return;
+ if (menu.Type == MenuType.TrainDefault || menu.Type == MenuType.TrainList)
+ {
+ Reset();
+ //Launch the game!
+ Loading.Complete = false;
+ Loading.LoadAsynchronously(RouteFile, Encoding.UTF8, Interface.CurrentOptions.TrainFolder, Encoding.UTF8);
+ OpenBVEGame g = Program.currentGameWindow as OpenBVEGame;
+ // ReSharper disable once PossibleNullReferenceException
+ g.LoadingScreenLoop();
+ Program.Renderer.CurrentInterface = InterfaceType.Normal;
+ return;
+ }
+ Instance.PushMenu(MenuType.TrainDefault);
+ return;
+
+ }
switch (cmd)
{
case Translations.Command.MenuUp: // UP
@@ -716,11 +501,78 @@ internal void ProcessCommand(Translations.Command cmd, double timeElapsed)
Reset();
Program.Renderer.CurrentInterface = InterfaceType.Normal;
break;
+ // route menu commands
+ case MenuTag.RouteList: // TO ROUTE LIST MENU
+ Menu.instance.PushMenu(MenuType.RouteList);
+ routeDescriptionBox.Text = Translations.GetInterfaceString("errors_route_please_select");
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\please_select.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ break;
+ case MenuTag.Directory: // SHOWS THE LIST OF FILES IN THE SELECTED DIR
+ SearchDirectory = SearchDirectory == string.Empty ? menu.Items[menu.Selection].Text : Path.CombineDirectory(SearchDirectory, menu.Items[menu.Selection].Text);
+ Menu.instance.PushMenu(Instance.Menus[CurrMenu].Type, 0, true);
+ break;
+ case MenuTag.ParentDirectory: // SHOWS THE LIST OF FILES IN THE PARENT DIR
+ if (string.IsNullOrEmpty(SearchDirectory))
+ {
+ return;
+ }
- // simulation commands
+ string oldSearchDirectory = SearchDirectory;
+ try
+ {
+ DirectoryInfo newDirectory = Directory.GetParent(SearchDirectory);
+ SearchDirectory = newDirectory == null ? string.Empty : Directory.GetParent(SearchDirectory)?.ToString();
+ }
+ catch
+ {
+ SearchDirectory = oldSearchDirectory;
+ return;
+ }
+ Menu.instance.PushMenu(Instance.Menus[CurrMenu].Type, 0, true);
+ break;
+ case MenuTag.RouteFile:
+ RoutefileState = RouteState.Loading;
+ RouteFile = Path.CombineFile(SearchDirectory, menu.Items[menu.Selection].Text);
+ if (!routeWorkerThread.IsBusy)
+ {
+ routeWorkerThread.RunWorkerAsync();
+ }
+
+ break;
+ case MenuTag.TrainDirectory:
+ for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++)
+ {
+ string trainDir = Path.CombineDirectory(SearchDirectory, menu.Items[menu.Selection].Text);
+ if (Program.CurrentHost.Plugins[i].Train != null && Program.CurrentHost.Plugins[i].Train.CanLoadTrain(trainDir))
+ {
+ if (Interface.CurrentOptions.TrainFolder == trainDir)
+ {
+ //enter folder
+ SearchDirectory = SearchDirectory == string.Empty ? menu.Items[menu.Selection].Text : Path.CombineDirectory(SearchDirectory, menu.Items[menu.Selection].Text);
+ Menu.instance.PushMenu(Instance.Menus[CurrMenu].Type, 0, true);
+ }
+ else
+ {
+ //Show details
+ Interface.CurrentOptions.TrainFolder = trainDir;
+ routeDescriptionBox.Text = Program.CurrentHost.Plugins[i].Train.GetDescription(trainDir);
+ Image trainImage = Program.CurrentHost.Plugins[i].Train.GetImage(trainDir);
+ if (trainImage != null)
+ {
+ Program.CurrentHost.RegisterTexture(new Bitmap(trainImage), new TextureParameters(null, null), out routePictureBox.Texture);
+ }
+ else
+ {
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\train_unknown.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ }
+ }
+ }
+ }
+ break;
+ // simulation commands
case MenuTag.JumpToStation: // JUMP TO STATION
Reset();
- TrainManager.PlayerTrain.Jump(menuItem.Data);
+ TrainManagerBase.PlayerTrain.Jump(menuItem.Data);
Program.TrainManager.JumpTFO();
break;
case MenuTag.ExitToMainMenu: // BACK TO MAIN MENU
@@ -738,6 +590,28 @@ internal void ProcessCommand(Translations.Command cmd, double timeElapsed)
Reset();
MainLoop.Quit = MainLoop.QuitMode.QuitProgram;
break;
+ case MenuTag.Yes:
+ if (menu.Type == MenuType.TrainDefault)
+ {
+ Reset();
+ //Launch the game!
+ Loading.Complete = false;
+ Loading.LoadAsynchronously(RouteFile, Encoding.UTF8, Interface.CurrentOptions.TrainFolder, Encoding.UTF8);
+ OpenBVEGame g = Program.currentGameWindow as OpenBVEGame;
+ // ReSharper disable once PossibleNullReferenceException
+ g.LoadingScreenLoop();
+ Program.Renderer.CurrentInterface = InterfaceType.Normal;
+ }
+ break;
+ case MenuTag.No:
+ if (menu.Type == MenuType.TrainDefault)
+ {
+ SearchDirectory = Program.FileSystem.InitialTrainFolder;
+ Instance.PushMenu(MenuType.TrainList);
+ routeDescriptionBox.Text = Translations.GetInterfaceString("start_train_choose");
+ Program.CurrentHost.RegisterTexture(Path.CombineFile(Program.FileSystem.DataFolder, "Menu\\please_select.png"), new TextureParameters(null, null), out routePictureBox.Texture);
+ }
+ break;
}
}
break;
@@ -757,9 +631,10 @@ internal void ProcessCommand(Translations.Command cmd, double timeElapsed)
// DRAW MENU
//
/// Draws the current menu as a screen overlay
- internal void Draw()
+ internal void Draw(double RealTimeElapsed)
{
-
+ double TimeElapsed = RealTimeElapsed - lastTimeElapsed;
+ lastTimeElapsed = RealTimeElapsed;
int i;
if (CurrMenu < 0 || CurrMenu >= Menus.Length)
@@ -769,15 +644,28 @@ internal void Draw()
// overlay background
Program.Renderer.Rectangle.Draw(null, Vector2.Null, new Vector2(Program.Renderer.Screen.Width, Program.Renderer.Screen.Height), overlayColor);
- // HORIZONTAL PLACEMENT: centre the menu in the main window
- int itemLeft = (Program.Renderer.Screen.Width - menu.ItemWidth) / 2; // item left edge
- // if menu alignment is left, left-align items, otherwise centre them in the screen
- int itemX = (menu.Align & TextAlignment.Left) != 0 ? itemLeft : Program.Renderer.Screen.Width / 2;
-
+
+ double itemLeft, itemX;
+ if (menu.Type == MenuType.GameStart || menu.Type == MenuType.RouteList || menu.Type == MenuType.TrainList)
+ {
+ itemLeft = 0;
+ itemX = 16;
+ Program.Renderer.Rectangle.Draw(null, new Vector2(0, menuYmin - MenuBorderY), new Vector2(menuXmax - menuXmin + 2.0f * MenuBorderX, menuYmax - menuYmin + 2.0f * MenuBorderY), backgroundColor);
+ }
+ else
+ {
+ itemLeft = (Program.Renderer.Screen.Width - menu.ItemWidth) / 2; // item left edge
+ // if menu alignment is left, left-align items, otherwise centre them in the screen
+ itemX = (menu.Align & TextAlignment.Left) != 0 ? itemLeft : Program.Renderer.Screen.Width / 2.0;
+ Program.Renderer.Rectangle.Draw(null, new Vector2(menuXmin - MenuBorderX, menuYmin - MenuBorderY), new Vector2(menuXmax - menuXmin + 2.0f * MenuBorderX, menuYmax - menuYmin + 2.0f * MenuBorderY), backgroundColor);
+ }
+
+ // draw the menu background
+
+
int menuBottomItem = menu.TopItem + visibleItems - 1;
- // draw the menu background
- Program.Renderer.Rectangle.Draw(null, new Vector2(menuXmin - MenuBorderX, menuYmin - MenuBorderY), new Vector2(menuXmax - menuXmin + 2.0f * MenuBorderX, menuYmax - menuYmin + 2.0f * MenuBorderY), backgroundColor);
+
// if not starting from the top of the menu, draw a dimmed ellipsis item
if (menu.Selection == menu.TopItem - 1 && !isCustomisingControl)
@@ -785,33 +673,72 @@ internal void Draw()
Program.Renderer.Rectangle.Draw(null, new Vector2(itemLeft - MenuItemBorderX, menuYmin /*-MenuItemBorderY*/), new Vector2(menu.ItemWidth + MenuItemBorderX, em + MenuItemBorderY * 2), highlightColor);
}
if (menu.TopItem > 0)
- Program.Renderer.OpenGlString.Draw(MenuFont, "...", new Point(itemX, menuYmin),
+ Program.Renderer.OpenGlString.Draw(MenuFont, "...", new Vector2(itemX, menuYmin),
menu.Align, ColourDimmed, false);
// draw the items
- int itemY = topItemY;
+ double itemY = topItemY;
for (i = menu.TopItem; i <= menuBottomItem && i < menu.Items.Length; i++)
{
if (menu.Items[i] == null)
{
continue;
}
+
+ double itemHeight = MenuFont.MeasureString(menu.Items[i].Text).Y;
+ double iconX = itemX;
+ if (menu.Items[i].Icon != null)
+ {
+ itemX += itemHeight * 1.25;
+ }
if (i == menu.Selection)
{
// draw a solid highlight rectangle under the text
// HACK! the highlight rectangle has to be shifted a little down to match
// the text body. OpenGL 'feature'?
- Program.Renderer.Rectangle.Draw(null, new Vector2(itemLeft - MenuItemBorderX, itemY /*-MenuItemBorderY*/), new Vector2(menu.ItemWidth + 2.0f * MenuItemBorderX, em + MenuItemBorderY * 2), highlightColor);
+ MenuCommand command = menu.Items[i] as MenuCommand;
+ Color128 color = highlightColor;
+ if(command != null)
+ {
+ switch (command.Tag)
+ {
+ case MenuTag.Directory:
+ case MenuTag.ParentDirectory:
+ color = folderHighlightColor;
+ break;
+ case MenuTag.RouteFile:
+ color = routeHighlightColor;
+ break;
+ default:
+ color = highlightColor;
+ break;
+ }
+ }
+
+ if (itemLeft == 0)
+ {
+ Program.Renderer.Rectangle.Draw(null, new Vector2(MenuItemBorderX, itemY /*-MenuItemBorderY*/), new Vector2(menu.Width + 2.0f * MenuItemBorderX, em + MenuItemBorderY * 2), color);
+ }
+ else
+ {
+ Program.Renderer.Rectangle.Draw(null, new Vector2(itemLeft - MenuItemBorderX, itemY /*-MenuItemBorderY*/), new Vector2(menu.ItemWidth + 2.0f * MenuItemBorderX, em + MenuItemBorderY * 2), color);
+ }
+
// draw the text
- Program.Renderer.OpenGlString.Draw(MenuFont, menu.Items[i].Text, new Point(itemX, itemY),
+ Program.Renderer.OpenGlString.Draw(MenuFont, menu.Items[i].DisplayText(TimeElapsed), new Vector2(itemX, itemY),
menu.Align, ColourHighlight, false);
}
else if (menu.Items[i] is MenuCaption)
- Program.Renderer.OpenGlString.Draw(MenuFont, menu.Items[i].Text, new Point(itemX, itemY),
+ Program.Renderer.OpenGlString.Draw(MenuFont, menu.Items[i].DisplayText(TimeElapsed), new Vector2(itemX, itemY),
menu.Align, ColourCaption, false);
else
- Program.Renderer.OpenGlString.Draw(MenuFont, menu.Items[i].Text, new Point(itemX, itemY),
+ Program.Renderer.OpenGlString.Draw(MenuFont, menu.Items[i].DisplayText(TimeElapsed), new Vector2(itemX, itemY),
menu.Align, ColourNormal, false);
itemY += lineHeight;
+ if (menu.Items[i].Icon != null)
+ {
+ Program.Renderer.Rectangle.DrawAlpha(menu.Items[i].Icon, new Vector2(iconX, itemY - itemHeight * 1.5), new Vector2(itemHeight, itemHeight), Color128.White);
+ itemX = iconX;
+ }
}
@@ -821,8 +748,80 @@ internal void Draw()
}
// if not at the end of the menu, draw a dimmed ellipsis item at the bottom
if (i < menu.Items.Length - 1)
- Program.Renderer.OpenGlString.Draw(MenuFont, "...", new Point(itemX, itemY),
+ Program.Renderer.OpenGlString.Draw(MenuFont, "...", new Vector2(itemX, itemY),
menu.Align, ColourDimmed, false);
+ switch (menu.Type)
+ {
+ case MenuType.GameStart:
+ LogoPictureBox.Draw();
+ string currentVersion = @"v" + Application.ProductVersion + Program.VersionSuffix;
+ if (IntPtr.Size != 4)
+ {
+ currentVersion += @" 64-bit";
+ }
+
+ OpenGlFont versionFont = Program.Renderer.Fonts.NextSmallestFont(MenuFont);
+ Program.Renderer.OpenGlString.Draw(versionFont, currentVersion, new Vector2(Program.Renderer.Screen.Width - Program.Renderer.Screen.Width / 4, Program.Renderer.Screen.Height - versionFont.FontSize * 2), TextAlignment.TopLeft, Color128.Black);
+ break;
+ case MenuType.RouteList:
+ case MenuType.TrainList:
+ {
+ switch (RoutefileState)
+ {
+ case RouteState.NoneSelected:
+ routePictureBox.Draw();
+ routeDescriptionBox.Draw();
+ Program.Renderer.Rectangle.Draw(null, new Vector2(Program.Renderer.Screen.Width - 200, Program.Renderer.Screen.Height - 40), new Vector2(190, 30), Color128.Black);
+ Program.Renderer.OpenGlString.Draw(MenuFont, Translations.GetInterfaceString("start_train_choose"), new Vector2(Program.Renderer.Screen.Width - 180, Program.Renderer.Screen.Height - 35), TextAlignment.TopLeft, Color128.Grey);
+ break;
+ case RouteState.Loading:
+ routePictureBox.Draw();
+ routeDescriptionBox.Draw();
+ Program.Renderer.Rectangle.Draw(null, new Vector2(Program.Renderer.Screen.Width - 200, Program.Renderer.Screen.Height - 40), new Vector2(190, 30), Color128.Black);
+ Program.Renderer.OpenGlString.Draw(MenuFont, Translations.GetInterfaceString("start_train_choose"), new Vector2(Program.Renderer.Screen.Width - 180, Program.Renderer.Screen.Height - 35), TextAlignment.TopLeft, Color128.Grey);
+ break;
+ case RouteState.Processed:
+ routePictureBox.Draw();
+ routeDescriptionBox.Draw();
+ //Game start button
+ if (menu.Selection == int.MaxValue) //HACK: Special value to make this work with minimum extra code
+ {
+ Program.Renderer.Rectangle.Draw(null, new Vector2(Program.Renderer.Screen.Width - 200, Program.Renderer.Screen.Height - 40), new Vector2(190, 30), Color128.Black);
+ Program.Renderer.Rectangle.Draw(null, new Vector2(Program.Renderer.Screen.Width - 197, Program.Renderer.Screen.Height - 37), new Vector2(184, 24), highlightColor);
+ if (menu.Type == MenuType.RouteList)
+ {
+ Program.Renderer.OpenGlString.Draw(MenuFont, Translations.GetInterfaceString("start_train_choose"), new Vector2(Program.Renderer.Screen.Width - 180, Program.Renderer.Screen.Height - 35), TextAlignment.TopLeft, Color128.Black);
+ }
+ else
+ {
+ Program.Renderer.OpenGlString.Draw(MenuFont, Translations.GetInterfaceString("start_start_start"), new Vector2(Program.Renderer.Screen.Width - 180, Program.Renderer.Screen.Height - 35), TextAlignment.TopLeft, Color128.Black);
+ }
+ }
+ else
+ {
+ Program.Renderer.Rectangle.Draw(null, new Vector2(Program.Renderer.Screen.Width - 200, Program.Renderer.Screen.Height - 40), new Vector2(190, 30), Color128.Black);
+ if (menu.Type == MenuType.RouteList)
+ {
+ Program.Renderer.OpenGlString.Draw(MenuFont, Translations.GetInterfaceString("start_train_choose"), new Vector2(Program.Renderer.Screen.Width - 180, Program.Renderer.Screen.Height - 35), TextAlignment.TopLeft, Color128.White);
+ }
+ else
+ {
+ Program.Renderer.OpenGlString.Draw(MenuFont, Translations.GetInterfaceString("start_start_start"), new Vector2(Program.Renderer.Screen.Width - 180, Program.Renderer.Screen.Height - 35), TextAlignment.TopLeft, Color128.White);
+ }
+ }
+ break;
+ case RouteState.Error:
+ routePictureBox.Draw();
+ routeDescriptionBox.Draw();
+ Program.Renderer.Rectangle.Draw(null, new Vector2(Program.Renderer.Screen.Width - 200, Program.Renderer.Screen.Height - 40), new Vector2(190, 30), Color128.Black);
+ Program.Renderer.OpenGlString.Draw(MenuFont, Translations.GetInterfaceString("start_train_choose"), new Vector2(Program.Renderer.Screen.Width - 180, Program.Renderer.Screen.Height - 35), TextAlignment.TopLeft, Color128.Grey);
+ break;
+ }
+
+ break;
+ }
+ }
+
}
//
@@ -838,8 +837,26 @@ private void PositionMenu()
return;
SingleMenu menu = Menus[CurrMenu];
- // HORIZONTAL PLACEMENT: centre the menu in the main window
- menuXmin = (Program.Renderer.Screen.Width - menu.Width) / 2; // menu left edge (border excluded)
+ for (int i = 0; i < menu.Items.Length; i++)
+ {
+ /*
+ * HACK: This is a property method, and is also used to
+ * reset the timer and display string back to the starting values
+ */
+ menu.Items[i].DisplayLength = menu.Items[i].DisplayLength;
+ }
+ if (menu.Type == MenuType.GameStart || menu.Type == MenuType.RouteList || menu.Type == MenuType.TrainList)
+ {
+ // Left aligned, used for route browser
+ menuXmin = 0;
+ }
+
+ else
+ {
+ // HORIZONTAL PLACEMENT: centre the menu in the main window
+ menuXmin = (Program.Renderer.Screen.Width - menu.Width) / 2; // menu left edge (border excluded)
+ }
+
menuXmax = menuXmin + menu.Width; // menu right edge (border excluded)
// VERTICAL PLACEMENT: centre the menu in the main window
menuYmin = (Program.Renderer.Screen.Height - menu.Height) / 2; // menu top edge (border excluded)
@@ -860,7 +877,7 @@ private void PositionMenu()
menu.TopItem = menu.Selection - (menu.Selection % visibleItems);
visibleItems = menu.Items.Length - menu.TopItem < visibleItems ? // in the last chunk,
menu.Items.Length - menu.TopItem : visibleItems; // display remaining items only
- menuYmin = (Program.Renderer.Screen.Height - numOfLines * lineHeight) / 2;
+ menuYmin = (Program.Renderer.Screen.Height - numOfLines * lineHeight) / 2.0;
menuYmax = menuYmin + numOfLines * lineHeight;
// first menu item is drawn on second line (first line is empty
// on first screen and contains an ellipsis on following screens
diff --git a/source/OpenBVE/Game/Menu/MenuTag.cs b/source/OpenBVE/Game/Menu/MenuTag.cs
new file mode 100644
index 000000000..20f330c5a
--- /dev/null
+++ b/source/OpenBVE/Game/Menu/MenuTag.cs
@@ -0,0 +1,47 @@
+namespace OpenBve
+{
+ /// The list of possible tags for a menu entry- These define the functionality of a given menu entry
+ public enum MenuTag
+ {
+ /// Is unselectable
+ Unselectable,
+ /// Has no functionality/ is blank
+ None,
+ /// Is a caption for another menu item
+ Caption,
+ /// Moves up a menu level
+ MenuBack,
+ /// Enters the submenu containing the list of stations to which the player train may be jumped
+ MenuJumpToStation,
+ /// Enters the submenu for exiting to the main menu
+ MenuExitToMainMenu,
+ /// Enters the submenu for customising controls
+ MenuControls,
+ /// Enters the submenu for quitting the program
+ MenuQuit,
+ /// Returns to the simulation
+ BackToSim,
+ /// Jumps to the selected station
+ JumpToStation,
+ /// Exits to the main menu
+ ExitToMainMenu,
+ /// Quits the program
+ Quit,
+ /// Customises the selected control
+ Control,
+ /// Displays a list of routefiles
+ RouteList,
+ /// Selects a routefile to load
+ RouteFile,
+ /// A directory
+ Directory,
+ /// Enters the parent directory
+ ParentDirectory,
+ /// Selects Yes for a menu choice
+ Yes,
+ /// Selects No for a menu choice
+ No,
+ /// A train directory
+ TrainDirectory
+ }
+}
diff --git a/source/OpenBVE/Game/Menu/MenuType.cs b/source/OpenBVE/Game/Menu/MenuType.cs
new file mode 100644
index 000000000..64cd90b7a
--- /dev/null
+++ b/source/OpenBVE/Game/Menu/MenuType.cs
@@ -0,0 +1,29 @@
+namespace OpenBve
+{
+ /// The list of possible sub-menu types
+ public enum MenuType
+ {
+ /// Not a sub menu
+ None,
+ /// Returns to the menu level above
+ Top,
+ /// The station jump menu
+ JumpToStation,
+ /// Returns to the main menu
+ ExitToMainMenu,
+ /// Provides a list of controls and allows customisation whilst in-game
+ Controls,
+ /// Customises the specified control
+ Control,
+ /// Quits the game
+ Quit,
+ /// The game start menu
+ GameStart,
+ /// Displays a list of routefiles
+ RouteList,
+ /// Asks whether the user wishes to use the default train
+ TrainDefault,
+ /// Displays a list of train folders
+ TrainList,
+ }
+}
diff --git a/source/OpenBVE/Game/MessageManager.TextualMessages.cs b/source/OpenBVE/Game/MessageManager.TextualMessages.cs
index 955267572..7e27c3a12 100644
--- a/source/OpenBVE/Game/MessageManager.TextualMessages.cs
+++ b/source/OpenBVE/Game/MessageManager.TextualMessages.cs
@@ -1,7 +1,7 @@
using System;
using System.Diagnostics.Eventing.Reader;
using DavyKager;
-using LibRender2.Texts;
+using LibRender2.Text;
using OpenBveApi;
using OpenBveApi.Trains;
using RouteManager2;
diff --git a/source/OpenBVE/Game/Score/Score.cs b/source/OpenBVE/Game/Score/Score.cs
index daea2e449..83781c4a8 100644
--- a/source/OpenBVE/Game/Score/Score.cs
+++ b/source/OpenBVE/Game/Score/Score.cs
@@ -38,6 +38,10 @@ internal class Score
/// The time elapsed since this function was last called
internal void Update(double TimeElapsed)
{
+ if (TrainManager.PlayerTrain == null)
+ {
+ return;
+ }
// doors
{
bool leftopen = false;
diff --git a/source/OpenBVE/Graphics/HUD/HUD.Element.cs b/source/OpenBVE/Graphics/HUD/HUD.Element.cs
index 0ee7e0468..6efaddfa7 100644
--- a/source/OpenBVE/Graphics/HUD/HUD.Element.cs
+++ b/source/OpenBVE/Graphics/HUD/HUD.Element.cs
@@ -1,4 +1,4 @@
-using LibRender2.Texts;
+using LibRender2.Text;
using OpenBveApi.Colors;
using OpenBveApi.Math;
diff --git a/source/OpenBVE/Graphics/NewRenderer.cs b/source/OpenBVE/Graphics/NewRenderer.cs
index 5dd619ea2..2309128dd 100644
--- a/source/OpenBVE/Graphics/NewRenderer.cs
+++ b/source/OpenBVE/Graphics/NewRenderer.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
using LibRender2;
using LibRender2.MotionBlurs;
using LibRender2.Objects;
@@ -9,6 +8,7 @@
using OpenBve.Graphics.Renderers;
using OpenBveApi;
using OpenBveApi.Colors;
+using OpenBveApi.FileSystem;
using OpenBveApi.Graphics;
using OpenBveApi.Hosts;
using OpenBveApi.Interface;
@@ -52,9 +52,9 @@ internal enum DistanceToNextStationDisplayMode
private Overlays overlays;
internal Touch Touch;
- public override void Initialize(HostInterface CurrentHost, BaseOptions CurrentOptions)
+ public override void Initialize(HostInterface CurrentHost, BaseOptions CurrentOptions, FileSystem FileSystem)
{
- base.Initialize(CurrentHost, CurrentOptions);
+ base.Initialize(CurrentHost, CurrentOptions, FileSystem);
try
{
@@ -93,6 +93,7 @@ internal int CreateStaticObject(UnifiedObject Prototype, Vector3 Position, Trans
public override void UpdateViewport(int Width, int Height)
{
+ _programLogo = null;
Screen.Width = Width;
Screen.Height = Height;
GL.Viewport(0, 0, Screen.Width, Screen.Height);
@@ -198,7 +199,11 @@ internal void RenderScene(double TimeElapsed, double RealTimeElapsed)
Fog.Color = Program.CurrentRoute.CurrentFog.Color;
Fog.Density = Program.CurrentRoute.CurrentFog.Density;
Fog.IsLinear = Program.CurrentRoute.CurrentFog.IsLinear;
- Fog.SetForImmediateMode();
+ if (!AvailableNewRenderer)
+ {
+ Fog.SetForImmediateMode();
+ }
+
}
else
{
@@ -441,14 +446,6 @@ internal void RenderScene(double TimeElapsed, double RealTimeElapsed)
face.Draw();
}
}
- if (AvailableNewRenderer)
- {
- /*
- * Must remember to de-activate at the end of the render sequence if in GL3 mode.
- * The overlays currently use immediate mode and do not work correctly with the shader active
- */
- DefaultShader.Deactivate();
- }
// render touch
OptionLighting = false;
Touch.RenderScene();
diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.ATS.cs b/source/OpenBVE/Graphics/Renderers/Overlays.ATS.cs
index f28110cf7..fd2c13f59 100644
--- a/source/OpenBVE/Graphics/Renderers/Overlays.ATS.cs
+++ b/source/OpenBVE/Graphics/Renderers/Overlays.ATS.cs
@@ -195,7 +195,7 @@ private void RenderATSLamps(HUD.Element Element, double TimeElapsed)
double q = Math.Round(Element.TextAlignment.Y < 0 ? y : Element.TextAlignment.Y > 0 ? y + lcrh - v : y + 0.5 * (lcrh - v));
p += Element.TextPosition.X;
q += Element.TextPosition.Y;
- renderer.OpenGlString.Draw(Element.Font, t, new System.Drawing.Point((int)p, (int)q), TextAlignment.TopLeft, tc, Element.TextShadow);
+ renderer.OpenGlString.Draw(Element.Font, t, new Vector2(p, q), TextAlignment.TopLeft, tc, Element.TextShadow);
}
// left overlay
if (Left.OverlayTexture != null)
diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs b/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs
index c66058b13..0af119e0e 100644
--- a/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs
+++ b/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs
@@ -45,7 +45,7 @@ private void RenderDebugOverlays()
t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual != 0 ? "L" + TrainManager.PlayerTrain.Handles.LocoBrake.Actual.ToString(Culture) : "N");
}
}
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, t, new Point(2, renderer.Screen.Height - 46), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, t, new Vector2(2, renderer.Screen.Height - 46), TextAlignment.TopLeft, Color128.White, true);
}
// safety handles
{
@@ -76,7 +76,7 @@ private void RenderDebugOverlays()
t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual != 0 ? "L" + TrainManager.PlayerTrain.Handles.LocoBrake.Actual.ToString(Culture) : "N");
}
}
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, t, new Point(2, renderer.Screen.Height - 32), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, t, new Vector2(2, renderer.Screen.Height - 32), TextAlignment.TopLeft, Color128.White, true);
}
// driver handles
{
@@ -106,7 +106,7 @@ private void RenderDebugOverlays()
t += " - " + (TrainManager.PlayerTrain.Handles.LocoBrake.Actual != 0 ? "L" + TrainManager.PlayerTrain.Handles.LocoBrake.Actual.ToString(Culture) : "N");
}
}
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, t, new Point(2, renderer.Screen.Height - 18), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, t, new Vector2(2, renderer.Screen.Height - 18), TextAlignment.TopLeft, Color128.White, true);
}
// debug information
int texturesLoaded = renderer.TextureManager.GetNumberOfLoadedTextures();
@@ -204,13 +204,13 @@ private void RenderDebugOverlays()
if (Lines[i][0] == '=')
{
string text = Lines[i].Substring(1);
- Size size = renderer.Fonts.SmallFont.MeasureString(text);
- renderer.Rectangle.Draw(null, new Vector2((float)x, (float)y), new Vector2(size.Width + 6.0f, size.Height + 2.0f), new Color128(0.35f, 0.65f, 0.90f, 0.8f));
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, text, new Point((int)x + 3, (int)y), TextAlignment.TopLeft, Color128.White);
+ Vector2 size = renderer.Fonts.SmallFont.MeasureString(text);
+ renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2(size.X + 6.0f, size.Y + 2.0f), new Color128(0.35f, 0.65f, 0.90f, 0.8f));
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, text, new Vector2(x + 3, y), TextAlignment.TopLeft, Color128.White);
}
else
{
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, Lines[i], new Point((int)x, (int)y), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, Lines[i], new Vector2(x, y), TextAlignment.TopLeft, Color128.White, true);
}
y += 14.0;
}
@@ -257,13 +257,13 @@ private void RenderATSDebugOverlay()
if (Lines[i][0] == '=')
{
string text = Lines[i].Substring(1);
- Size size = renderer.Fonts.SmallFont.MeasureString(text);
- renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2(size.Width + 6.0f, size.Height + 2.0f), new Color128(0.35f, 0.65f, 0.9f, 0.8f));
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, text, new Point((int)x + 3, (int)y), TextAlignment.TopLeft, Color128.White);
+ Vector2 size = renderer.Fonts.SmallFont.MeasureString(text);
+ renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2(size.X + 6.0f, size.Y + 2.0f), new Color128(0.35f, 0.65f, 0.9f, 0.8f));
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, text, new Vector2(x + 3, y), TextAlignment.TopLeft, Color128.White);
}
else
{
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, Lines[i], new Point((int)x, (int)y), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, Lines[i], new Vector2(x, y), TextAlignment.TopLeft, Color128.White, true);
}
y += 14.0;
if (y > renderer.Screen.Height - 20.0)
@@ -292,7 +292,7 @@ private void RenderBrakeSystemDebug()
{
if (!heading[0])
{
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Brake pipe", new Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Brake pipe", new Vector2(x, oy - 16), TextAlignment.TopLeft, Color128.White, true);
heading[0] = true;
}
renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2(w, h), Color128.Black);
@@ -306,7 +306,7 @@ private void RenderBrakeSystemDebug()
{
if (!heading[1])
{
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Auxillary reservoir", new Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Auxillary reservoir", new Vector2(x, oy - 16), TextAlignment.TopLeft, Color128.White, true);
heading[1] = true;
}
renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2(w, h), Color128.Black);
@@ -319,7 +319,7 @@ private void RenderBrakeSystemDebug()
{
if (!heading[2])
{
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Brake cylinder", new Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Brake cylinder", new Vector2(x, oy - 16), TextAlignment.TopLeft, Color128.White, true);
heading[2] = true;
}
@@ -334,7 +334,7 @@ private void RenderBrakeSystemDebug()
{
if (!heading[3])
{
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Main reservoir", new Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Main reservoir", new Vector2(x, oy - 16), TextAlignment.TopLeft, Color128.White, true);
heading[3] = true;
}
renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2(w, h), Color128.Black);
@@ -348,7 +348,7 @@ private void RenderBrakeSystemDebug()
{
if (!heading[4])
{
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Equalizing reservoir", new Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Equalizing reservoir", new Vector2(x, oy - 16), TextAlignment.TopLeft, Color128.White, true);
heading[4] = true;
}
renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2(w, h), Color128.Black);
@@ -362,7 +362,7 @@ private void RenderBrakeSystemDebug()
{
if (!heading[5])
{
- renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Straight air pipe", new Point((int)x, (int)(oy - 16)), TextAlignment.TopLeft, Color128.White, true);
+ renderer.OpenGlString.Draw(renderer.Fonts.SmallFont, "Straight air pipe", new Vector2(x, oy - 16), TextAlignment.TopLeft, Color128.White, true);
heading[5] = true;
}
renderer.Rectangle.Draw(null, new Vector2(x, y), new Vector2((float)w, (float)h), Color128.Black);
diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.GameMessages.cs b/source/OpenBVE/Graphics/Renderers/Overlays.GameMessages.cs
index c40806de0..9a7928b5d 100644
--- a/source/OpenBVE/Graphics/Renderers/Overlays.GameMessages.cs
+++ b/source/OpenBVE/Graphics/Renderers/Overlays.GameMessages.cs
@@ -23,9 +23,9 @@ private void RenderGameMessages(HUD.Element Element, double TimeElapsed)
for (int j = 0; j < n; j++)
{
//Update font size for the renderer
- Size size = Element.Font.MeasureString((string)MessageManager.TextualMessages[j].MessageToDisplay);
- MessageManager.TextualMessages[j].Width = size.Width;
- MessageManager.TextualMessages[j].Height = size.Height;
+ Vector2 size = Element.Font.MeasureString((string)MessageManager.TextualMessages[j].MessageToDisplay);
+ MessageManager.TextualMessages[j].Width = size.X;
+ MessageManager.TextualMessages[j].Height = size.Y;
//Run through the list of current messages
double a = MessageManager.TextualMessages[j].Width - j * (double)Element.Value1;
//If our width is wider than the old, use this as the NEW viewing plane width
@@ -218,7 +218,7 @@ private void RenderGameMessages(HUD.Element Element, double TimeElapsed)
: py + 0.5 * (lcrh - v));
p += Element.TextPosition.X;
q += Element.TextPosition.Y;
- renderer.OpenGlString.Draw(Element.Font, t, new Point((int)p, (int)q),
+ renderer.OpenGlString.Draw(Element.Font, t, new Vector2(p, q),
TextAlignment.TopLeft, new Color128(tc.R, tc.G, tc.B, tc.A * alpha), Element.TextShadow);
}
// left overlay
diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.HUD.cs b/source/OpenBVE/Graphics/Renderers/Overlays.HUD.cs
index a9fc941a6..34d374c08 100644
--- a/source/OpenBVE/Graphics/Renderers/Overlays.HUD.cs
+++ b/source/OpenBVE/Graphics/Renderers/Overlays.HUD.cs
@@ -896,15 +896,13 @@ private void RenderHUDElement(HUD.Element Element, double TimeElapsed)
}
}
{ // text
- System.Drawing.Size size = Element.Font.MeasureString(t);
- float u = size.Width;
- float v = size.Height;
- double p = Math.Round(Element.TextAlignment.X < 0 ? x : Element.TextAlignment.X == 0 ? x + 0.5 * (w - u) : x + w - u);
- double q = Math.Round(Element.TextAlignment.Y < 0 ? y : Element.TextAlignment.Y == 0 ? y + 0.5 * (h - v) : y + h - v);
+ Vector2 size = Element.Font.MeasureString(t);
+ double p = Math.Round(Element.TextAlignment.X < 0 ? x : Element.TextAlignment.X == 0 ? x + 0.5 * (w - size.X) : x + w - size.X);
+ double q = Math.Round(Element.TextAlignment.Y < 0 ? y : Element.TextAlignment.Y == 0 ? y + 0.5 * (h - size.Y) : y + h - size.Y);
p += Element.TextPosition.X;
q += Element.TextPosition.Y;
Color128 c = Element.TextColor.CreateTextColor(sc, alpha);
- Program.Renderer.OpenGlString.Draw(Element.Font, t, new System.Drawing.Point((int)p, (int)q), TextAlignment.TopLeft, c, Element.TextShadow);
+ Program.Renderer.OpenGlString.Draw(Element.Font, t, new Vector2(p, q), TextAlignment.TopLeft, c, Element.TextShadow);
}
// overlay
if (Element.CenterMiddle.OverlayTexture != null)
diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.Lamp.cs b/source/OpenBVE/Graphics/Renderers/Overlays.Lamp.cs
index b3b452196..7891abeaf 100644
--- a/source/OpenBVE/Graphics/Renderers/Overlays.Lamp.cs
+++ b/source/OpenBVE/Graphics/Renderers/Overlays.Lamp.cs
@@ -1,6 +1,7 @@
using System;
-using LibRender2.Texts;
+using LibRender2.Text;
using OpenBveApi.Interface;
+using OpenBveApi.Math;
using TrainManager.SafetySystems;
using TrainManager.Trains;
@@ -25,8 +26,8 @@ private struct Lamp
{
internal readonly LampType Type;
internal readonly string Text;
- internal readonly float Width;
- internal readonly float Height;
+ internal readonly double Width;
+ internal readonly double Height;
internal Lamp(LampType Type)
{
this.Type = Type;
@@ -90,16 +91,16 @@ internal Lamp(LampType Type)
break;
}
}
- System.Drawing.Size size = font.MeasureString(this.Text);
- this.Width = size.Width;
- this.Height = size.Height;
+ Vector2 size = font.MeasureString(this.Text);
+ this.Width = size.X;
+ this.Height = size.Y;
}
}
private class LampCollection
{
internal readonly Lamp[] Lamps;
- internal readonly float Width;
+ internal readonly double Width;
/// Initialises the ATS lamps for the specified train using one of the default safety systems
internal LampCollection(TrainBase Train)
diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.ScoreMessages.cs b/source/OpenBVE/Graphics/Renderers/Overlays.ScoreMessages.cs
index a29b28de6..a1cd819b9 100644
--- a/source/OpenBVE/Graphics/Renderers/Overlays.ScoreMessages.cs
+++ b/source/OpenBVE/Graphics/Renderers/Overlays.ScoreMessages.cs
@@ -16,15 +16,15 @@ private void RenderScoreMessages(HUD.Element Element, double TimeElapsed)
{
// score messages
int n = Game.ScoreMessages.Length;
- float totalwidth = 16.0f;
- float[] widths = new float[n];
- float[] heights = new float[n];
+ double totalwidth = 16.0f;
+ double[] widths = new double[n];
+ double[] heights = new double[n];
for (int j = 0; j < n; j++)
{
- Size size = Element.Font.MeasureString(Game.ScoreMessages[j].Text);
- widths[j] = size.Width;
- heights[j] = size.Height;
- float a = widths[j] - j * Element.Value1;
+ Vector2 size = Element.Font.MeasureString(Game.ScoreMessages[j].Text);
+ widths[j] = size.X;
+ heights[j] = size.Y;
+ double a = widths[j] - j * Element.Value1;
if (a > totalwidth)
{
totalwidth = a;
@@ -187,7 +187,7 @@ private void RenderScoreMessages(HUD.Element Element, double TimeElapsed)
double q = Math.Round(Element.TextAlignment.Y < 0 ? py : Element.TextAlignment.Y > 0 ? py + lcrh - v : py + 0.5 * (lcrh - v));
p += Element.TextPosition.X;
q += Element.TextPosition.Y;
- renderer.OpenGlString.Draw(Element.Font, t, new Point((int)p, (int)q), TextAlignment.TopLeft, new Color128(tc.R, tc.G, tc.B, tc.A * alpha), Element.TextShadow);
+ renderer.OpenGlString.Draw(Element.Font, t, new Vector2(p, q), TextAlignment.TopLeft, new Color128(tc.R, tc.G, tc.B, tc.A * alpha), Element.TextShadow);
}
// left overlay
if (Left.OverlayTexture != null)
diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.cs b/source/OpenBVE/Graphics/Renderers/Overlays.cs
index 595b07fcd..5152cea8c 100644
--- a/source/OpenBVE/Graphics/Renderers/Overlays.cs
+++ b/source/OpenBVE/Graphics/Renderers/Overlays.cs
@@ -153,8 +153,8 @@ internal void Render(double TimeElapsed)
{
//If paused, fade out the screen & write PAUSE
renderer.Rectangle.Draw(null, Vector2.Null, new Vector2(renderer.Screen.Width, renderer.Screen.Height), new Color128(0.0f, 0.0f, 0.0f, 0.5f));
- renderer.OpenGlString.Draw(renderer.Fonts.VeryLargeFont, Translations.GetInterfaceString("menu_pause_title"), new Point(renderer.Screen.Width / 2, renderer.Screen.Height / 2), TextAlignment.CenterMiddle, Color128.White, true);
- if (Interface.CurrentOptions.ScreenReaderAvailable && !PauseAnnounced)
+ renderer.OpenGlString.Draw(renderer.Fonts.VeryLargeFont, Translations.GetInterfaceString("menu_pause_title"), new Vector2(renderer.Screen.Width / 2.0, renderer.Screen.Height / 2.0), TextAlignment.CenterMiddle, Color128.White, true);
+ if (!PauseAnnounced)
{
if (!Tolk.Output(Translations.GetInterfaceString("menu_pause_title")))
{
@@ -165,7 +165,7 @@ internal void Render(double TimeElapsed)
break;
}
case InterfaceType.Menu:
- Game.Menu.Draw();
+ Game.Menu.Draw(TimeElapsed);
PauseAnnounced = false;
break;
default:
diff --git a/source/OpenBVE/Graphics/Screen.cs b/source/OpenBVE/Graphics/Screen.cs
index 1b5e1d3c5..e9613f612 100644
--- a/source/OpenBVE/Graphics/Screen.cs
+++ b/source/OpenBVE/Graphics/Screen.cs
@@ -3,6 +3,7 @@
using System.Windows.Forms;
using LibRender2;
using LibRender2.Viewports;
+using OpenBveApi.Hosts;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
@@ -37,12 +38,29 @@ internal static void Initialize()
try
{
DisplayDevice.Default.ChangeResolution(currentResolution);
- Program.currentGameWindow = new OpenBVEGame(currentResolution.Width, currentResolution.Height, currentGraphicsMode,
- GameWindowFlags.Default)
+ if (Interface.CurrentOptions.IsUseNewRenderer && (Program.CurrentHost.Platform == HostPlatform.AppleOSX && IntPtr.Size != 4 || Interface.CurrentOptions.ForceForwardsCompatibleContext))
{
- Visible = true,
- WindowState = WindowState.Fullscreen,
- };
+ /*
+ * OS-X is a fickle beast
+ * In order to get a functioning GL3 context, we appear to need to be running as 64-bit & explicitly specify the forwards compatible flag
+ */
+ Program.currentGameWindow = new OpenBVEGame(currentResolution.Width, currentResolution.Height, currentGraphicsMode,
+ GameWindowFlags.Default, GraphicsContextFlags.ForwardCompatible)
+ {
+ Visible = true,
+ WindowState = WindowState.Fullscreen,
+ };
+ }
+ else
+ {
+ Program.currentGameWindow = new OpenBVEGame(currentResolution.Width, currentResolution.Height, currentGraphicsMode,
+ GameWindowFlags.Default)
+ {
+ Visible = true,
+ WindowState = WindowState.Fullscreen,
+ };
+ }
+
resolutionFound = true;
break;
}
@@ -71,11 +89,27 @@ internal static void Initialize()
{
try
{
- Program.currentGameWindow = new OpenBVEGame(Interface.CurrentOptions.WindowWidth,
- Interface.CurrentOptions.WindowHeight, currentGraphicsMode, GameWindowFlags.Default)
+ if (Interface.CurrentOptions.IsUseNewRenderer && (Program.CurrentHost.Platform == HostPlatform.AppleOSX && IntPtr.Size != 4 || Interface.CurrentOptions.ForceForwardsCompatibleContext))
+ {
+ /*
+ * OS-X is a fickle beast
+ * In order to get a functioning GL3 context, we appear to need to be running as 64-bit & explicitly specify the forwards compatible flag
+ */
+ Program.currentGameWindow = new OpenBVEGame(Interface.CurrentOptions.WindowWidth,
+ Interface.CurrentOptions.WindowHeight, currentGraphicsMode, GameWindowFlags.Default, GraphicsContextFlags.ForwardCompatible)
+ {
+ Visible = true
+ };
+ }
+ else
{
- Visible = true
- };
+ Program.currentGameWindow = new OpenBVEGame(Interface.CurrentOptions.WindowWidth,
+ Interface.CurrentOptions.WindowHeight, currentGraphicsMode, GameWindowFlags.Default)
+ {
+ Visible = true
+ };
+ }
+
}
catch
{
@@ -165,7 +199,7 @@ internal static void ToggleFullscreen()
System.Threading.Thread.Sleep(20);
if (Program.currentGameWindow.WindowState != WindowState.Fullscreen)
{
- MessageBox.Show(Translations.GetInterfaceString("errors_fullscreen_switch1") + System.Environment.NewLine +
+ MessageBox.Show(Translations.GetInterfaceString("errors_fullscreen_switch1") + Environment.NewLine +
Translations.GetInterfaceString("errors_fullscreen_switch2"), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
Program.Renderer.Screen.Fullscreen = false;
}
diff --git a/source/OpenBVE/OpenBve.csproj b/source/OpenBVE/OpenBve.csproj
index c7a408d10..472dbed07 100644
--- a/source/OpenBVE/OpenBve.csproj
+++ b/source/OpenBVE/OpenBve.csproj
@@ -136,6 +136,14 @@
AI.cs
+
+
+
+
+
+
+
+
MessageManager.cs
@@ -144,7 +152,7 @@
-
+
diff --git a/source/OpenBVE/System/Functions/CrashHandler.cs b/source/OpenBVE/System/Functions/CrashHandler.cs
index 98b5491dd..cdcd83989 100644
--- a/source/OpenBVE/System/Functions/CrashHandler.cs
+++ b/source/OpenBVE/System/Functions/CrashHandler.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Threading;
using System.Windows.Forms;
+using OpenBveApi.Hosts;
namespace OpenBve
{
@@ -14,6 +15,13 @@ class CrashHandler
/// Catches all unhandled exceptions within the current appdomain
internal static void CurrentDomain_UnhandledException(Object sender, UnhandledExceptionEventArgs e)
{
+ if (Program.CurrentHost.Platform == HostPlatform.AppleOSX && IntPtr.Size !=4)
+ {
+ Console.WriteLine("UNHANDLED EXCEPTION:");
+ Console.WriteLine("--------------------");
+ Console.WriteLine(e.ExceptionObject);
+ Environment.Exit(0);
+ }
try
{
Exception ex = (Exception)e.ExceptionObject;
@@ -51,6 +59,13 @@ internal static void CurrentDomain_UnhandledException(Object sender, UnhandledEx
/// Catches all unhandled exceptions within the current UI thread
internal static void UIThreadException(object sender, ThreadExceptionEventArgs t)
{
+ if (Program.CurrentHost.Platform == HostPlatform.AppleOSX && IntPtr.Size !=4)
+ {
+ Console.WriteLine("UNHANDLED EXCEPTION:");
+ Console.WriteLine("--------------------");
+ Console.WriteLine(t.Exception);
+ Environment.Exit(0);
+ }
try
{
MessageBox.Show("Unhandled Windows Forms Exception");
diff --git a/source/OpenBVE/System/GameWindow.cs b/source/OpenBVE/System/GameWindow.cs
index ee1769a2a..662b507bb 100644
--- a/source/OpenBVE/System/GameWindow.cs
+++ b/source/OpenBVE/System/GameWindow.cs
@@ -50,6 +50,30 @@ class OpenBVEGame: GameWindow
//We need to explicitly specify the default constructor
public OpenBVEGame(int width, int height, GraphicsMode currentGraphicsMode, GameWindowFlags @default): base(width, height, currentGraphicsMode, Translations.GetInterfaceString("program_title"), @default)
{
+ Program.FileSystem.AppendToLogFile("Creating game window with standard context.");
+ if (Program.CurrentHost.Platform == HostPlatform.AppleOSX && IntPtr.Size != 4)
+ {
+ return;
+ }
+ try
+ {
+ var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ Icon ico = new Icon(OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(assemblyFolder, "Data"), "icon.ico"));
+ this.Icon = ico;
+ }
+ catch
+ {
+ //it's only an icon
+ }
+ }
+
+ public OpenBVEGame(int width, int height, GraphicsMode currentGraphicsMode, GameWindowFlags @default, GraphicsContextFlags flags): base(width, height, currentGraphicsMode, Translations.GetInterfaceString("program_title"), @default, DisplayDevice.Default, 3,3, flags)
+ {
+ Program.FileSystem.AppendToLogFile("Creating game window with forwards-compatible context.");
+ if (Program.CurrentHost.Platform == HostPlatform.AppleOSX && IntPtr.Size != 4)
+ {
+ return;
+ }
try
{
var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
@@ -85,7 +109,6 @@ protected override void OnRenderFrame(FrameEventArgs e)
{
Thread.Sleep(10);
}
- //Renderer.UpdateLighting();
Program.Renderer.RenderScene(TimeElapsed, RealTimeElapsed);
Program.currentGameWindow.SwapBuffers();
if (MainLoop.Quit != MainLoop.QuitMode.ContinueGame)
@@ -360,14 +383,25 @@ protected override void OnLoad(EventArgs e)
//Initialise the loader thread queues
jobs = new Queue(10);
locks = new Queue