diff --git a/VersionCreator.sh b/VersionCreator.sh index 9ce3711670..3debee67a4 100755 --- a/VersionCreator.sh +++ b/VersionCreator.sh @@ -69,7 +69,7 @@ Maintainer: leezer3 Architecture: all Version: $Version Provides: bve-engine -Depends: debhelper (>= 9), mono-runtime (>= 5.20.1), libmono-corlib4.5-cil (>= 5.20.1), libmono-system-drawing4.0-cil (>= 1.0), libmono-system-windows-forms4.0-cil (>= 1.0), libmono-system4.0-cil (>= 5.20.1), libmono-system-xml-linq4.0-cil (>= 5.20.1), libmono-i18n-cjk4.0-cil, libmono-i18n-mideast4.0-cil, libmono-i18n-other4.0-cil, libmono-i18n-rare4.0-cil, libmono-i18n-west4.0-cil, libmono-microsoft-csharp4.0-cil (>= 5.20.1), libopenal1, libusb-1.0-0, fonts-noto-core, fonts-noto-cjk +Depends: debhelper (>= 9), mono-runtime (>= 5.20.1), libmono-corlib4.5-cil (>= 5.20.1), libmono-system-drawing4.0-cil (>= 1.0), libmono-system-windows-forms4.0-cil (>= 1.0), libmono-system4.0-cil (>= 5.20.1), libmono-system-xml-linq4.0-cil (>= 5.20.1), libmono-i18n4.0-all, libmono-microsoft-csharp4.0-cil (>= 5.20.1), libopenal1, libusb-1.0-0, fonts-noto-core, fonts-noto-cjk Recommends: bve-route, bve-train Homepage: http://openbve-project.net Description: realistic 3D train/railway simulator (main program) diff --git a/assets/Compatibility/RoutePatches/Hungary.xml b/assets/Compatibility/RoutePatches/Hungary.xml index f6d2f1003f..cf0ce1d518 100644 --- a/assets/Compatibility/RoutePatches/Hungary.xml +++ b/assets/Compatibility/RoutePatches/Hungary.xml @@ -6,7 +6,6 @@ - true @@ -90,11 +89,5 @@ true true - - 8444E8B8286A703C70C55C31B4FCE7EB2636C77EA72297004FE4EC2FD61AB842 - Teglagyari-kisvasut.csv - true - true - \ No newline at end of file diff --git a/assets/Compatibility/RoutePatches/Japan.xml b/assets/Compatibility/RoutePatches/Japan.xml index cc2ca0756e..86e4134cd3 100644 --- a/assets/Compatibility/RoutePatches/Japan.xml +++ b/assets/Compatibility/RoutePatches/Japan.xml @@ -201,12 +201,5 @@ 1367S(曳舟→渋谷)bve4.csv true - - - - DF28B51375CD2E1ECB1A894876A18398BBDCB42D1F3F09D6BA0EBE26EDA6E60E - l-テエSlJoテゥテ嘘.csv - Texture.Background(1) gaku2\Back_Mt1.bmp - \ No newline at end of file diff --git a/assets/Compatibility/RoutePatches/UnitedKingdom.xml b/assets/Compatibility/RoutePatches/UnitedKingdom.xml index f744204d3e..1d726346eb 100644 --- a/assets/Compatibility/RoutePatches/UnitedKingdom.xml +++ b/assets/Compatibility/RoutePatches/UnitedKingdom.xml @@ -51,21 +51,6 @@ true Bakerloo v3 routefile detected- Applying cylinder hack. - - - 58A8458F15C8C007EA0333652E1D9F06D6716A700C925E052E3E4774049C91DE - BVE2 Edgware-Morden via Bank.csv - true - Northern Line v3.2 routefile detected- Applying cylinder hack. - LU.xml - - - 310C70EF764E226062E40B094E5C8A2CAAD80BA4EA97E100E42D3307C033EB33 - BVE2 Kennington-High Barnet.csv - true - Northern Line v3.2 routefile detected- Applying cylinder hack. - LU.xml - diff --git a/assets/Languages/en-US.xlf b/assets/Languages/en-US.xlf index 79a7582719..25be7ef51a 100755 --- a/assets/Languages/en-US.xlf +++ b/assets/Languages/en-US.xlf @@ -1204,18 +1204,6 @@ Miscellaneous - - Camera options - - - Smooth interior transition - - - Smooth exterior transition - - - Transition duration (sec): - Detail of simulation @@ -1421,29 +1409,6 @@ UI Scale Factor: - - - The texture filtering mode used to smooth textures. - - - Higher levels of anisotropic filtering improve texture quality when viewed at an angle. - - - Reduces jagged edges on polygons. - - - Sets the quality of transparency rendering. - - - The maximum distance at which objects are rendered. - - - Enables motion blur effects when moving at speed. - - - Enable the modern shader-based renderer. Recommended for better visuals and performance on modern hardware. - - CSV files diff --git a/assets/Languages/id-ID.xlf b/assets/Languages/id-ID.xlf index 6caf778145..92a6e9bda9 100644 --- a/assets/Languages/id-ID.xlf +++ b/assets/Languages/id-ID.xlf @@ -1,4 +1,4 @@ - + @@ -1635,11 +1635,11 @@ Previous Page... - Sebelumnya... + Berikutnya... Next Page... - Berikutnya... + Sebelumnya.. Kiosk Mode @@ -1812,36 +1812,6 @@ Intensitas Bayangan: - - - The texture filtering mode used to smooth textures. - Mode pemfilteran tekstur yang digunakan untuk menghaluskan tekstur. - - - Higher levels of anisotropic filtering improve texture quality when viewed at an angle. - Level pemfilteran anisotropik yang lebih tinggi meningkatkan kualitas tekstur saat dilihat dari sudut miring. - - - Reduces jagged edges on polygons. - Mengurangi tepi bergerigi pada poligon. - - - Sets the quality of transparency rendering. - Mengatur kualitas rendering transparansi. - - - The maximum distance at which objects are rendered. - Jarak maksimum di mana objek akan ditampilkan. - - - Enables motion blur effects when moving at speed. - Mengaktifkan efek motion blur saat bergerak dengan kecepatan tinggi. - - - Enable the modern shader-based renderer. Recommended for better visuals and performance on modern hardware. - Aktifkan renderer berbasis shader modern. Direkomasikan untuk visual dan performa yang lebih baik pada perangkat keras modern. - - CSV files diff --git a/assets/Shaders/default.frag b/assets/Shaders/default.frag index 1d0a2e9e66..ace1756be0 100644 --- a/assets/Shaders/default.frag +++ b/assets/Shaders/default.frag @@ -28,33 +28,30 @@ in vec4 oViewPos; in vec2 oUv; in vec4 oColor; in vec4 oLightResult; -// --- SHADOW MAPPING --- -uniform bool uShadowEnabled; -uniform float uShadowStrength; -uniform int uShadowCascadeCount; - -uniform sampler2DShadow uShadowMap0; -uniform sampler2DShadow uShadowMap1; -uniform sampler2DShadow uShadowMap2; -uniform sampler2DShadow uShadowMap3; - -uniform float uShadowSplit0; // Boundary where cascade 0 ends and 1 begins -uniform float uShadowSplit1; // Boundary where cascade 1 ends and 2 begins -uniform float uShadowSplit2; // Boundary where cascade 2 ends and 3 begins -uniform float uShadowSplit3; // Final shadow distance boundary - -uniform float uShadowBias0; -uniform float uShadowBias1; -uniform float uShadowBias2; -uniform float uShadowBias3; - -uniform float uShadowNormalBias0; -uniform float uShadowNormalBias1; -uniform float uShadowNormalBias2; -uniform float uShadowNormalBias3; - -uniform vec2 uAlphaTest; +uniform sampler2DShadow uShadowMap0; // Cascade 0 (near) +uniform sampler2DShadow uShadowMap1; // Cascade 1 (mid) +uniform sampler2DShadow uShadowMap2; // Cascade 2 (far) +uniform sampler2DShadow uShadowMap3; // Cascade 3 (extra far) +uniform float uCascadeFarDist0; +uniform float uCascadeFarDist1; +uniform float uCascadeFarDist2; +uniform float uCascadeFarDist3; +uniform float uCascadeBias0; +uniform float uCascadeBias1; +uniform float uCascadeBias2; +uniform float uCascadeBias3; +uniform float uNormalBias0; +uniform float uNormalBias1; +uniform float uNormalBias2; +uniform float uNormalBias3; +uniform float uShadowStrength; +uniform bool uShadowEnabled; +uniform int uCascadeCount; // 2, 3, or 4 +uniform vec2 uAlphaTest; uniform sampler2D uTexture; +uniform sampler2D uNightTexture; +uniform bool uIsNightTexture; +uniform float uNightBlendFactor; struct Light { @@ -72,6 +69,7 @@ in vec4 vPosLightSpace0; in vec4 vPosLightSpace1; in vec4 vPosLightSpace2; in vec4 vPosLightSpace3; +in float vViewDepth; uniform int uMaterialFlags; uniform float uBrightness; uniform float uOpacity; @@ -83,8 +81,8 @@ uniform float uFogDensity; uniform bool uFogIsLinear; out vec4 fragColor; -/// Samples a single cascade using hardware PCF. -float GetCascadeShadowFactor(sampler2DShadow shadowMap, vec4 posLightSpace, float bias, float normalBias) +/// Samples a single cascade with 4-tap PCF via hardware comparison. +float SampleCascade(sampler2DShadow shadowMap, vec4 posLightSpace, float bias, float normalBias) { vec3 projCoords = posLightSpace.xyz / posLightSpace.w; projCoords = projCoords * 0.5 + 0.5; @@ -102,7 +100,7 @@ float GetCascadeShadowFactor(sampler2DShadow shadowMap, vec4 posLightSpace, floa vec3 lightDir = normalize(uLight.position); float biasScale = clamp(1.0 - dot(normal, lightDir), 0.0, 1.0); // Multiply the base Z-bias by a slope factor to perfectly cure acne on thin meshes - float activeBias = bias * (1.0 + biasScale * normalBias); + float activeBias = bias * (1.1 + biasScale * normalBias); float currentDepth = projCoords.z - activeBias; @@ -121,50 +119,43 @@ float GetCascadeShadowFactor(sampler2DShadow shadowMap, vec4 posLightSpace, floa return shadow; } -/// Helper to sample a cascade by index. float SampleCascadeByIndex(int idx) { - if (idx == 0) return GetCascadeShadowFactor(uShadowMap0, vPosLightSpace0, uShadowBias0, uShadowNormalBias0); - if (idx == 1) return GetCascadeShadowFactor(uShadowMap1, vPosLightSpace1, uShadowBias1, uShadowNormalBias1); - if (idx == 2) return GetCascadeShadowFactor(uShadowMap2, vPosLightSpace2, uShadowBias2, uShadowNormalBias2); - if (idx == 3) return GetCascadeShadowFactor(uShadowMap3, vPosLightSpace3, uShadowBias3, uShadowNormalBias3); + if (idx == 0) return SampleCascade(uShadowMap0, vPosLightSpace0, uCascadeBias0, uNormalBias0); + if (idx == 1) return SampleCascade(uShadowMap1, vPosLightSpace1, uCascadeBias1, uNormalBias1); + if (idx == 2) return SampleCascade(uShadowMap2, vPosLightSpace2, uCascadeBias2, uNormalBias2); + if (idx == 3) return SampleCascade(uShadowMap3, vPosLightSpace3, uCascadeBias3, uNormalBias3); return 1.0; } -/// Helper to get the split distance of a cascade by index. -float GetShadowSplitDistance(int idx) +float GetCascadeFarDist(int idx) { - if (idx == 0) return uShadowSplit0; - if (idx == 1) return uShadowSplit1; - if (idx == 2) return uShadowSplit2; - if (idx == 3) return uShadowSplit3; + if (idx == 0) return uCascadeFarDist0; + if (idx == 1) return uCascadeFarDist1; + if (idx == 2) return uCascadeFarDist2; + if (idx == 3) return uCascadeFarDist3; return 0.0; } -/// Calculates the final shadow factor using CSM with smooth blending. -float CalculateShadowFactor() +/// Full CSM sampling with cascade selection and smooth blending. +float CSMShadow() { - if (!uShadowEnabled) return 1.0; - - // Calculate view depth per-pixel for perspective correctness (crucial for large polygons like ground) - float vViewDepth = abs(oViewPos.z); - float blendRange = 15.0; float shadow = 1.0; - int cascadeCount = uShadowCascadeCount; + int cascadeCount = uCascadeCount; for (int i = 0; i < cascadeCount; i++) { - float splitDist = GetShadowSplitDistance(i); + float farDist = GetCascadeFarDist(i); - if (vViewDepth < splitDist) + if (vViewDepth < farDist) { shadow = SampleCascadeByIndex(i); // Blend toward next cascade near the boundary if (i < cascadeCount - 1) { - float blendStart = splitDist - blendRange; + float blendStart = farDist - blendRange; if (vViewDepth > blendStart) { float nextShadow = SampleCascadeByIndex(i + 1); @@ -175,15 +166,15 @@ float CalculateShadowFactor() else { // Last cascade: fade out at far edge - float fadeStart = splitDist - blendRange * 2.0; + float fadeStart = farDist - blendRange * 2.0; if (vViewDepth > fadeStart) { - float t = (vViewDepth - fadeStart) / (splitDist - fadeStart); + float t = (vViewDepth - fadeStart) / (farDist - fadeStart); shadow = mix(shadow, 1.0, t); } } - break; + break; // Found our cascade, stop searching } } @@ -250,13 +241,33 @@ void main(void) * as otherwise light coming through a semi-transparent material will * affect it's final opacity, and hence whether its discarded or not */ - float shadow = CalculateShadowFactor(); + float shadow = 1.0; + if (uShadowEnabled) + { + shadow = CSMShadow(); + } if ((uMaterialFlags & 1) == 0 && (uMaterialFlags & 4) == 0) { // Material is not emissive, apply shadow to the light factor finalColor.rgb *= (oLightResult.rgb * shadow); finalColor.a *= oLightResult.a; + + if (uIsNightTexture) + { + vec4 nightColor; + if ((uMaterialFlags & 16) == 0) + { + nightColor = vec4(oColor.rgb, 1.0) * texture(uNightTexture, oUv); + } + else + { + nightColor = vec4(oColor.rgb, 1.0) * vec4(texture(uNightTexture, oUv).xyz, 1.0); + } + // Nighttime textures in OpenBVE are usually blended based on DNB (DaytimeNighttimeBlend) + finalColor.rgb = mix(finalColor.rgb, nightColor.rgb, uNightBlendFactor); + finalColor.a = mix(finalColor.a, nightColor.a, uNightBlendFactor); + } } else { diff --git a/assets/Shaders/default.vert b/assets/Shaders/default.vert index fb37ec3176..f86bd5e493 100644 --- a/assets/Shaders/default.vert +++ b/assets/Shaders/default.vert @@ -79,6 +79,7 @@ out vec4 vPosLightSpace0; out vec4 vPosLightSpace1; out vec4 vPosLightSpace2; out vec4 vPosLightSpace3; +out float vViewDepth; out vec3 vNormal; vec4 getLightResult() @@ -221,6 +222,8 @@ void main() vPosLightSpace1 = uLightSpaceMatrix1 * worldPos4; vPosLightSpace2 = uLightSpaceMatrix2 * worldPos4; vPosLightSpace3 = uLightSpaceMatrix3 * worldPos4; + + vViewDepth = abs(oViewPos.z); } else { @@ -228,6 +231,7 @@ void main() vPosLightSpace1 = vec4(0.0); vPosLightSpace2 = vec4(0.0); vPosLightSpace3 = vec4(0.0); + vViewDepth = 0.0; } oUv = (uCurrentTextureMatrix * vec4(iUv, 1.0, 1.0)).xy; diff --git a/assets/Shaders/shadow_depth.vert b/assets/Shaders/shadow_depth.vert index 18117b507d..8fdb4e17e7 100644 --- a/assets/Shaders/shadow_depth.vert +++ b/assets/Shaders/shadow_depth.vert @@ -30,7 +30,6 @@ layout(location = 4) in ivec3 iMatrixChain; uniform mat4 uLightSpaceMatrix; uniform mat4 uModelMatrix; -uniform mat4 uTextureMatrix; out vec2 vUv; @@ -90,7 +89,7 @@ void main() // OpenBVE explicitly negates Z for world geometry. pos.z = -pos.z; - vUv = (uTextureMatrix * vec4(iUv, 1.0, 1.0)).xy; + vUv = iUv; gl_Position = uLightSpaceMatrix * uModelMatrix * vec4(pos, 1.0); } diff --git a/source/AssimpParser/app.config b/source/AssimpParser/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/AssimpParser/app.config +++ b/source/AssimpParser/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/CarXMLConvertor/App.config b/source/CarXMLConvertor/App.config index e83b408bea..3a071c0277 100644 --- a/source/CarXMLConvertor/App.config +++ b/source/CarXMLConvertor/App.config @@ -13,10 +13,6 @@ - - - - diff --git a/source/InputDevicePlugins/DefaultDisplayPlugin/app.config b/source/InputDevicePlugins/DefaultDisplayPlugin/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/InputDevicePlugins/DefaultDisplayPlugin/app.config +++ b/source/InputDevicePlugins/DefaultDisplayPlugin/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/InputDevicePlugins/DenshaDeGoInput/app.config b/source/InputDevicePlugins/DenshaDeGoInput/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/InputDevicePlugins/DenshaDeGoInput/app.config +++ b/source/InputDevicePlugins/DenshaDeGoInput/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/InputDevicePlugins/KatoInput/app.config b/source/InputDevicePlugins/KatoInput/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/InputDevicePlugins/KatoInput/app.config +++ b/source/InputDevicePlugins/KatoInput/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/InputDevicePlugins/SanYingInput/app.config b/source/InputDevicePlugins/SanYingInput/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/InputDevicePlugins/SanYingInput/app.config +++ b/source/InputDevicePlugins/SanYingInput/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/InputDevicePlugins/ZuikiInput/app.config b/source/InputDevicePlugins/ZuikiInput/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/InputDevicePlugins/ZuikiInput/app.config +++ b/source/InputDevicePlugins/ZuikiInput/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/LibRender2/Backgrounds/Background.cs b/source/LibRender2/Backgrounds/Background.cs index ee0d16b0d0..3bba583a9c 100644 --- a/source/LibRender2/Backgrounds/Background.cs +++ b/source/LibRender2/Backgrounds/Background.cs @@ -124,11 +124,11 @@ private void RenderStaticBackgroundRetained(StaticBackground data, float alpha, renderer.LastBoundTexture = t.OpenGlTextures[(int)OpenGlTextureWrapMode.RepeatClamp]; if (alpha == 1.0f) { - GL.Disable(EnableCap.Blend); + renderer.UnsetBlendFunc(); } else { - GL.Enable(EnableCap.Blend); + renderer.SetBlendFunc(); } if (data.VAO == null) @@ -205,11 +205,11 @@ private void RenderStaticBackgroundImmediate(StaticBackground data, float alpha, GL.Enable(EnableCap.Texture2D); if (alpha == 1.0f) { - GL.Disable(EnableCap.Blend); + renderer.UnsetBlendFunc(); } else { - GL.Enable(EnableCap.Blend); + renderer.SetBlendFunc(); renderer.SetAlphaFunc(AlphaFunction.Greater, 0.0f); } GL.BindTexture(TextureTarget.Texture2D, t.OpenGlTextures[(int)OpenGlTextureWrapMode.RepeatClamp].Name); @@ -223,7 +223,7 @@ private void RenderStaticBackgroundImmediate(StaticBackground data, float alpha, { GL.CallList(data.DisplayList); GL.Disable(EnableCap.Texture2D); - GL.Enable(EnableCap.Blend); + renderer.SetBlendFunc(); GL.PopMatrix(); GL.MatrixMode(MatrixMode.Projection); GL.PopMatrix(); @@ -301,7 +301,7 @@ private void RenderStaticBackgroundImmediate(StaticBackground data, float alpha, GL.EndList(); GL.CallList(data.DisplayList); GL.Disable(EnableCap.Texture2D); - GL.Enable(EnableCap.Blend); + renderer.SetBlendFunc(); GL.PopMatrix(); GL.MatrixMode(MatrixMode.Projection); GL.PopMatrix(); @@ -312,7 +312,7 @@ private void RenderStaticBackgroundImmediate(StaticBackground data, float alpha, /// The background object private void RenderBackgroundObject(BackgroundObject data) { - GL.Enable(EnableCap.Blend); + renderer.SetBlendFunc(); // alpha test renderer.SetAlphaFunc(AlphaFunction.Greater, 0.0f); if (renderer.AvailableNewRenderer) diff --git a/source/LibRender2/BaseRenderer.cs b/source/LibRender2/BaseRenderer.cs index 57f4ca9510..67eaa6e0c7 100644 --- a/source/LibRender2/BaseRenderer.cs +++ b/source/LibRender2/BaseRenderer.cs @@ -15,10 +15,12 @@ using LibRender2.MotionBlurs; using LibRender2.Objects; using LibRender2.Overlays; +using LibRender2.Managers; +using LibRender2.Pipeline; using LibRender2.Primitives; using LibRender2.Screens; using LibRender2.Shaders; -using LibRender2.ShadowMapping; +using LibRender2.Shadows; using LibRender2.Text; using LibRender2.Textures; using LibRender2.Viewports; @@ -56,16 +58,11 @@ public abstract class BaseRenderer internal FileSystem fileSystem; /// Holds a reference to the current options - internal BaseOptions currentOptions; + protected internal BaseOptions currentOptions; - public List StaticObjectStates; - public List DynamicObjectStates; - public VisibleObjectLibrary VisibleObjects; + /// The scene manager. + public SceneManager Scene; - protected int[] ObjectsSortedByStart; - protected int[] ObjectsSortedByEnd; - protected int ObjectsSortedByStartPointer; - protected int ObjectsSortedByEndPointer; protected internal double LastUpdatedTrackPosition; /// Whether ReShade is in use /// Don't use OpenGL error checking with ReShade, as this breaks @@ -73,6 +70,12 @@ public abstract class BaseRenderer /// A dummy VAO used when working with procedural data within the shader public VertexArrayObject dummyVao; + /// The graphics device state manager. + public GraphicsDevice Device; + + /// The render pipeline. + public RenderPipeline Pipeline; + public Screen Screen; /// The track follower for the main camera @@ -135,6 +138,48 @@ public Vector2 ScaleFactor public MotionBlur MotionBlur; public Fonts Fonts; + /// The GPU resource manager. + public LibRender2.Managers.GpuResourceManager ResourceManager; + + public VisibleObjectLibrary VisibleObjects => Scene.VisibleObjects; + public object VisibilityUpdateLock => Scene.VisibilityUpdateLock; + public bool VisibilityThreadShouldRun + { + get => Scene.VisibilityThreadShouldRun; + set => Scene.VisibilityThreadShouldRun = value; + } + + public void BindCSMToDefaultShader() + { + if (CSMShadowMaps == null || DefaultShader == null) + { + return; + } + + Shader shader = DefaultShader as Shader; + if (shader == null) return; + + shader.SetShadowEnabled(true); + shader.SetCascadeCount(CSMCaster.CascadeCount); + + for (int i = 0; i < CSMCaster.CascadeCount; i++) + { + // Shadows use texture units 4, 5, 6, 7 + SetActiveTexture(TextureUnit.Texture4 + i); + GL.BindTexture(TextureTarget.Texture2D, CSMShadowMaps.DepthTextures[i]); + shader.SetCascadeShadowMapUnit(i, 4 + i); + shader.SetCascadeLightSpaceMatrix(i, CSMCaster.LightSpaceMatrices[i]); + shader.SetCascadeFarDistance(i, (float)CSMCaster.CascadeFarDistances[i]); + shader.SetCascadeBias(i, CSMCaster.CascadeBiases[i] + (float)currentOptions.ShadowBias); + shader.SetNormalBias(i, (float)currentOptions.ShadowNormalBias); + } + + shader.SetShadowStrength(Lighting.ShadowStrength); + SetActiveTexture(TextureUnit.Texture0); + } + + public ConcurrentQueue RenderThreadJobs => Scene.RenderThreadJobs; + public Matrix4D CurrentProjectionMatrix; public Matrix4D CurrentViewMatrix; @@ -157,14 +202,20 @@ public Vector2 ScaleFactor public Shader DefaultShader; - /// Manages the Cascaded Shadow Mapping (CSM) system. - public Shadows Shadows; - + /// Cascaded shadow map FBOs + depth textures. + public CascadedShadowMap CSMShadowMaps; + + /// Computes per-cascade light-space matrices. + public CascadedShadowCaster CSMCaster; + + /// The shadow depth shader for the depth-only pass. + public ShadowDepthShader ShadowDepthShaderProgram; + /// Whether shadows are enabled. - public bool ShadowsEnabled => Shadows?.Enabled ?? false; + public bool ShadowsEnabled = true; /// Shadow strength: 0=invisible, 1=full darkness. - public float ShadowStrength => Shadows?.Strength ?? 0.7f; + public float ShadowStrength = 0.7f; /// Whether lighting is enabled in the debug options public bool OptionLighting = true; @@ -203,20 +254,61 @@ public Vector2 ScaleFactor public int InfoTotalQuads; /// The total number of OpenGL polygons in the current frame + private struct BlendState + { + public bool Enabled; + public BlendingFactor SrcFactor; + public BlendingFactor DestFactor; + } + + private readonly Stack blendStack = new Stack(); + + public void PushBlendFunc() + { + blendStack.Push(new BlendState + { + Enabled = blendEnabled, + SrcFactor = blendSrcFactor, + DestFactor = blendDestFactor + }); + } + + public void PopBlendFunc() + { + if (blendStack.Count > 0) + { + BlendState state = blendStack.Pop(); + blendEnabled = state.Enabled; + blendSrcFactor = state.SrcFactor; + blendDestFactor = state.DestFactor; + RestoreBlendFunc(); + } + } + public int InfoTotalPolygon; /// The game's current framerate public double FrameRate = 1.0; /// Whether Blend is enabled in openGL - private bool blendEnabled; + internal bool blendEnabled; - private BlendingFactor blendSrcFactor; + internal BlendingFactor blendSrcFactor = BlendingFactor.SrcAlpha; - private BlendingFactor blendDestFactor; + internal BlendingFactor blendDestFactor = BlendingFactor.OneMinusSrcAlpha; /// Whether Alpha Testing is enabled in openGL private bool alphaTestEnabled; + private bool cullFaceEnabled; + private CullFaceMode cullFaceMode; + private bool depthTestEnabled; + private DepthFunction depthTestFunction; + private bool depthMaskEnabled = true; + + private AlphaFunction alphaTestFunction; + private float alphaTestComparison; + + private TextureUnit lastActiveTextureUnit = TextureUnit.Texture0; /// The current AlphaFunc comparison private AlphaFunction alphaFuncComparison; @@ -226,6 +318,7 @@ public Vector2 ScaleFactor /// Stores the most recently bound texture public OpenGlTexture LastBoundTexture; + private OpenGlTexture lastBoundNightTexture; private Color32 lastColor; @@ -242,7 +335,7 @@ public bool ForceLegacyOpenGL protected internal Texture whitePixel; /// A dummy 1x1 depth texture with comparison enabled, used when shadows are disabled to satisfy driver requirements. - internal int nullDepthMap; + private int nullDepthMap; private bool logoError; @@ -300,14 +393,6 @@ public bool LoadLogo() return currentHost.LoadTexture(ref _programLogo, OpenGlTextureWrapMode.ClampClamp); } - /* - * List of VBO and IBO to delete on the next frame pass - * This needs to be done here as opposed to in the finalizer - */ - internal static readonly List vaoToDelete = new List(); - internal static readonly List vboToDelete = new List(); - internal static readonly List iboToDelete = new List(); - public Dictionary> CubesToDraw = new Dictionary>(); public bool AvailableNewRenderer => currentOptions != null && currentOptions.IsUseNewRenderer && !ForceLegacyOpenGL; @@ -329,14 +414,14 @@ protected BaseRenderer(HostInterface CurrentHost, BaseOptions CurrentOptions, Fi Camera = new CameraProperties(this); Lighting = new Lighting(this); Marker = new Marker(this); - Shadows = new Shadows(this); projectionMatrixList = new List(); viewMatrixList = new List(); - Fonts = new Fonts(currentHost, this, CurrentOptions.Font); - VisibilityThread = new Thread(RunVisibiliityThread); - VisibilityThread.Start(); - RenderThreadJobs = new ConcurrentQueue(); + Device = new GraphicsDevice(); + Pipeline = new RenderPipeline(); + Scene = new SceneManager(this); + ResourceManager = new LibRender2.Managers.GpuResourceManager(); + Fonts = new Fonts(currentHost, this, currentOptions.Font); } @@ -357,6 +442,7 @@ public virtual void Initialize() DefaultShader.SetMaterialAmbient(Color32.White); DefaultShader.SetMaterialDiffuse(Color32.White); DefaultShader.SetMaterialSpecular(Color32.White); + DefaultShader.SetNightTexture(1); lastColor = Color32.White; DefaultShader.Deactivate(); dummyVao = new VertexArrayObject(); @@ -408,9 +494,9 @@ public virtual void Initialize() Keys = new Keys(this); MotionBlur = new MotionBlur(this); - StaticObjectStates = new List(); - DynamicObjectStates = new List(); - VisibleObjects = new VisibleObjectLibrary(this); + Scene.StaticObjectStates = new List(); + Scene.DynamicObjectStates = new List(); + Scene.VisibleObjects = new VisibleObjectLibrary(this); whitePixel = new Texture(new Texture(1, 1, PixelFormat.RGBAlpha, new byte[] {255, 255, 255, 255}, null)); if (AvailableNewRenderer) { @@ -422,7 +508,7 @@ public virtual void Initialize() GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest); GL.BindTexture(TextureTarget.Texture2D, 0); } - GL.ClearColor(currentOptions.ClearColor.R * inv255, currentOptions.ClearColor.G * inv255, currentOptions.ClearColor.B * inv255, 1.0f); + GL.ClearColor(0.67f, 0.67f, 0.67f, 1.0f); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.Enable(EnableCap.DepthTest); GL.DepthFunc(DepthFunction.Lequal); @@ -469,19 +555,91 @@ public virtual void Initialize() currentHost.RegisterTexture(Path.CombineFile(fileSystem.GetDataFolder("Menu"), "joystick.png"), TextureParameters.NoChange, out JoystickTexture); currentHost.RegisterTexture(Path.CombineFile(fileSystem.GetDataFolder("Menu"), "raildriver.png"), TextureParameters.NoChange, out RailDriverTexture); - Lighting.Initialize(); - if (AvailableNewRenderer) { - Shadows.Initialize(); + InitializeShadows(); } } - /// Initializes (or reinitializes) shadow mapping from current options. - public void InitializeShadows() => Shadows.Initialize(); + /// + /// Initializes (or reinitializes) shadow mapping from current options. + /// Call after GL context creation and whenever shadow settings change. + /// + public void InitializeShadows() + { + // Read options + var opts = currentOptions; + + // If shadows are off, dispose and disable + if (opts.ShadowResolution == ShadowMapResolution.Off) + { + DisposeShadows(); + ShadowsEnabled = false; + fileSystem.AppendToLogFile("[CSM] Shadows disabled by user setting."); + return; + } + + int resolution = Math.Max(1, (int)opts.ShadowResolution); + int cascadeCount = (int)opts.ShadowCascades; + double shadowDistance = opts.ShadowDrawDistance == ShadowDistance.ViewingDistance ? opts.ViewingDistance : (double)(int)opts.ShadowDrawDistance; + shadowDistance = Math.Max(1.0, shadowDistance); + float shadowStrength = (float)opts.ShadowStrength; + + try + { + if (CSMShadowMaps == null) + { + // First-time creation + CSMShadowMaps = new CascadedShadowMap(cascadeCount, resolution); + } + else + { + // Hot-reload: resize existing maps + CSMShadowMaps.Resize(cascadeCount, resolution); + } + + if (CSMCaster == null || cascadeCount != CSMCaster.CascadeCount) + { + // Create, or update if cascade count changed + CSMCaster = new CascadedShadowCaster(cascadeCount); + } + + CSMCaster.ShadowDistance = shadowDistance; + CSMCaster.Resolution = resolution; + CSMCaster.SplitLambda = 0.75; + CSMCaster.DepthMargin = 150.0; + + ShadowStrength = shadowStrength; + Lighting.ShadowStrength = shadowStrength; + + if (ShadowDepthShaderProgram == null) + { + ShadowDepthShaderProgram = new ShadowDepthShader(this, "shadow_depth", "shadow_depth", true); + } + + ShadowsEnabled = true; + + fileSystem.AppendToLogFile($"[CSM] Initialized: {cascadeCount} cascades, " + $"{resolution}×{resolution}, distance={shadowDistance}m, " + $"strength={shadowStrength:P0}"); + } + catch (Exception ex) + { + fileSystem.AppendToLogFile($"[CSM] Init failed: {ex.Message}"); + ShadowsEnabled = false; + // Purge lingering OpenGL error state if any, to avoid crashing later in ResetShader + GL.GetError(); + } + } /// Disposes all shadow GPU resources. - public void DisposeShadows() => Shadows.Dispose(); + public void DisposeShadows() + { + CSMShadowMaps?.Dispose(); + CSMShadowMaps = null; + ShadowDepthShaderProgram?.Dispose(); + ShadowDepthShaderProgram = null; + CSMCaster = null; + ShadowsEnabled = false; + } /// /// Call this when the user changes shadow settings at runtime @@ -503,8 +661,7 @@ public void DeInitialize() nullDepthMap = 0; } GameWindow?.Dispose(); - // terminate spinning thread - VisibilityThreadShouldRun = false; + Scene?.DeInitialize(); } /// Should be called when the OpenGL version is switched mid-game @@ -523,11 +680,15 @@ public void SwitchOpenGLVersion() while (GL.GetError() != ErrorCode.NoError) { } } - /// Performs the CSM shadow depth rendering pass for all geometry. - protected void PerformCSMShadowPass() => Shadows.RenderPass(); + /// + /// Executes all registered render passes in the pipeline. + /// + /// The current rendering context. + public void ExecutePipeline(RenderContext context) + { + Pipeline.Execute(context); + } - /// Binds cascading shadow data to the default shader. - protected void BindCSMToDefaultShader() => Shadows.Bind(DefaultShader); internal PrimitiveType GetPrimitiveType(FaceFlags flags) { @@ -541,36 +702,10 @@ internal PrimitiveType GetPrimitiveType(FaceFlags flags) } } - /// Performs cleanup of disposed resources + /// Should be called at the start of each frame to release any GPU resources queued for deletion public void ReleaseResources() { - //Must remember to lock on the lists as the destructor is in a different thread - lock (vaoToDelete) - { - foreach (int VAO in vaoToDelete) - { - GL.DeleteVertexArray(VAO); - } - vaoToDelete.Clear(); - } - - lock (vboToDelete) - { - foreach (int VBO in vboToDelete) - { - GL.DeleteBuffer(VBO); - } - vboToDelete.Clear(); - } - - lock (iboToDelete) - { - foreach (int IBO in iboToDelete) - { - GL.DeleteBuffer(IBO); - } - iboToDelete.Clear(); - } + ResourceManager.ReleaseResources(); } /// @@ -580,6 +715,9 @@ public virtual void ResetOpenGlState() { GL.Enable(EnableCap.CullFace); GL.CullFace(CullFaceMode.Front); + cullFaceEnabled = true; + cullFaceMode = CullFaceMode.Front; + if (!AvailableNewRenderer) { GL.Disable(EnableCap.Lighting); @@ -589,10 +727,18 @@ public virtual void ResetOpenGlState() SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); UnsetBlendFunc(); + GL.Enable(EnableCap.DepthTest); GL.DepthFunc(DepthFunction.Lequal); + depthTestEnabled = true; + depthTestFunction = DepthFunction.Lequal; + GL.Disable(EnableCap.DepthClamp); + GL.DepthMask(true); + depthMaskEnabled = true; + + SetActiveTexture(TextureUnit.Texture0); SetAlphaFunc(AlphaFunction.Greater, 0.9f); } @@ -630,17 +776,7 @@ public void PopMatrix(MatrixMode Mode) public void Reset() { - currentHost.AnimatedObjectCollectionCache.Clear(); - List> keys = currentHost.StaticObjectCache.Keys.ToList(); - for (int i = 0; i < keys.Count; i++) - { - if (!File.Exists(keys[i].Item1) || File.GetLastWriteTime(keys[i].Item1) != keys[i].Item3) - { - currentHost.StaticObjectCache.Remove(keys[i]); - } - } - TextureManager.UnloadAllTextures(true); - VisibleObjects.Clear(); + Scene.Reset(); } public int CreateStaticObject(StaticObject Prototype, Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, ObjectDisposalMode AccurateObjectDisposal, ObjectCreationParameters Parameters, double BlockLength) @@ -652,119 +788,12 @@ public int CreateStaticObject(StaticObject Prototype, Vector3 Position, Transfor public int CreateStaticObject(Vector3 Position, StaticObject Prototype, Transformation LocalTransformation, Matrix4D Rotate, Matrix4D Translate, ObjectDisposalMode AccurateObjectDisposal, ObjectCreationParameters Parameters, double BlockLength) { - if (Prototype == null) - { - return -1; - } - - if (Prototype.Mesh.Faces.Length == 0) - { - //Null object- Waste of time trying to calculate anything for these - return -1; - } - - float startingDistance = float.MaxValue; - float endingDistance = float.MinValue; - - if (AccurateObjectDisposal == ObjectDisposalMode.Accurate) - { - foreach (VertexTemplate vertex in Prototype.Mesh.Vertices) - { - Vector3 Coordinates = new Vector3(vertex.Coordinates); - Coordinates.Rotate(LocalTransformation ?? Transformation.NullTransformation); - - if (Coordinates.Z < startingDistance) - { - startingDistance = (float)Coordinates.Z; - } - - if (Coordinates.Z > endingDistance) - { - endingDistance = (float)Coordinates.Z; - } - } - - startingDistance += (float)Parameters.AccurateObjectDisposalZOffset; - endingDistance += (float)Parameters.AccurateObjectDisposalZOffset; - } - - const double minBlockLength = 20.0; - - if (BlockLength < minBlockLength) - { - BlockLength *= Math.Ceiling(minBlockLength / BlockLength); - } - - switch (AccurateObjectDisposal) - { - case ObjectDisposalMode.Accurate: - startingDistance += (float)Parameters.TrackPosition; - endingDistance += (float)Parameters.TrackPosition; - double z = BlockLength * Math.Floor(Parameters.TrackPosition / BlockLength); - Parameters.StartingDistance = Math.Min(z - BlockLength, startingDistance); - Parameters.EndingDistance = Math.Max(z + 2.0 * BlockLength, endingDistance); - startingDistance = (float)(BlockLength * Math.Floor(Parameters.StartingDistance / BlockLength)); - endingDistance = (float)(BlockLength * Math.Ceiling(Parameters.EndingDistance / BlockLength)); - break; - case ObjectDisposalMode.Legacy: - startingDistance = (float)Parameters.StartingDistance; - endingDistance = (float)Parameters.EndingDistance; - break; - case ObjectDisposalMode.Mechanik: - startingDistance = (float)Parameters.StartingDistance; - endingDistance = (float)Parameters.EndingDistance + 1500; - if (startingDistance < 0) - { - startingDistance = 0; - } - break; - } - StaticObjectStates.Add(new ObjectState - { - Prototype = Prototype, - Translation = Translate, - Rotate = Rotate, - StartingDistance = startingDistance, - EndingDistance = endingDistance, - WorldPosition = Position, - DisableShadowCasting = Parameters.DisableShadowCasting - }); - - foreach (MeshFace face in Prototype.Mesh.Faces) - { - switch (face.Flags & FaceFlags.FaceTypeMask) - { - case FaceFlags.Triangles: - InfoTotalTriangles++; - break; - case FaceFlags.TriangleStrip: - InfoTotalTriangleStrip++; - break; - case FaceFlags.Quads: - InfoTotalQuads++; - break; - case FaceFlags.QuadStrip: - InfoTotalQuadStrip++; - break; - case FaceFlags.Polygon: - InfoTotalPolygon++; - break; - } - } - - return StaticObjectStates.Count - 1; + return Scene.CreateStaticObject(Position, Prototype, LocalTransformation, Rotate, Translate, AccurateObjectDisposal, Parameters, BlockLength); } public void CreateDynamicObject(ref ObjectState internalObject) { - if (internalObject == null) - { - internalObject = new ObjectState( new StaticObject(currentHost)); - } - - internalObject.Prototype.Dynamic = true; - - DynamicObjectStates.Add(internalObject); + Scene.CreateDynamicObject(ref internalObject); } /// Initializes the visibility of all objects within the game world @@ -772,276 +801,17 @@ public void CreateDynamicObject(ref ObjectState internalObject) /// the required VAO objects public void InitializeVisibility() { - if (!ForceLegacyOpenGL) // as we might want to switch renderer types - { - for (int i = 0; i < StaticObjectStates.Count; i++) - { - VAOExtensions.CreateVAO(StaticObjectStates[i].Prototype.Mesh, false, DefaultShader.VertexLayout, this); - /* - * n.b. - * Only create the actual matrix buffer at first frame render time - * I can't find why at the minute, but Object Viewer otherwise doesn't show them, and attempting - * to retrieve previously set matricies from the shader shows all zeros - * - * Probably a timing issue, but it works doing it that way :/ - */ - } - for (int i = 0; i < DynamicObjectStates.Count; i++) - { - VAOExtensions.CreateVAO(DynamicObjectStates[i].Prototype.Mesh, false, DefaultShader.VertexLayout, this); - } - } - ObjectsSortedByStart = StaticObjectStates.Select((x, i) => new { Index = i, Distance = x.StartingDistance }).OrderBy(x => x.Distance).Select(x => x.Index).ToArray(); - ObjectsSortedByEnd = StaticObjectStates.Select((x, i) => new { Index = i, Distance = x.EndingDistance }).OrderBy(x => x.Distance).Select(x => x.Index).ToArray(); - ObjectsSortedByStartPointer = 0; - ObjectsSortedByEndPointer = 0; - - if (currentOptions.ObjectDisposalMode == ObjectDisposalMode.QuadTree) - { - foreach (ObjectState state in StaticObjectStates) - { - VisibleObjects.quadTree.Add(state, Orientation3.Default); - } - VisibleObjects.quadTree.Initialize(currentOptions.QuadTreeLeafSize); - UpdateQuadTreeVisibility(); - } - else - { - double p = CameraTrackFollower.TrackPosition + Camera.Alignment.Position.Z; - foreach (ObjectState state in StaticObjectStates.Where(recipe => recipe.StartingDistance <= p + Camera.ForwardViewingDistance & recipe.EndingDistance >= p - Camera.BackwardViewingDistance)) - { - VisibleObjects.ShowObject(state, ObjectType.Static); - } - } - } - - private VisibilityUpdate updateVisibility; - /// The lock to be held whilst visibility updates or loading operations are in progress - public object VisibilityUpdateLock = new object(); - - public bool VisibilityThreadShouldRun = true; - - public Thread VisibilityThread; - - private void RunVisibiliityThread() - { - while (VisibilityThreadShouldRun) - { - lock (VisibilityUpdateLock) - { - if (updateVisibility != VisibilityUpdate.None && CameraTrackFollower != null) - { - UpdateVisibility(CameraTrackFollower.TrackPosition + Camera.Alignment.Position.Z); - } - } - - if (updateVisibility == VisibilityUpdate.None) - { - Thread.Sleep(100); - } - } + Scene.InitializeVisibility(); } public void UpdateVisibility(bool force) { - updateVisibility = force ? VisibilityUpdate.Force : VisibilityUpdate.Normal; - } - - private void UpdateVisibility(double trackPosition) - { - if (currentOptions.ObjectDisposalMode == ObjectDisposalMode.QuadTree) - { - UpdateQuadTreeVisibility(); - } - else - { - if (updateVisibility == VisibilityUpdate.Normal) - { - UpdateLegacyVisibility(trackPosition); - } - else - { - /* - * The original visibility algorithm fails to handle correctly cases where the - * camera angle is rotated, but the track position does not change - * - * Horrible kludge... - */ - UpdateLegacyVisibility(trackPosition + 0.01); - UpdateLegacyVisibility(trackPosition - 0.01); - } - - } - - updateVisibility = VisibilityUpdate.None; - } - - private void UpdateQuadTreeVisibility() - { - if (VisibleObjects == null || VisibleObjects.quadTree == null) - { - Thread.Sleep(10); - return; - } - Camera.UpdateQuadTreeLeaf(); - } - - private void UpdateLegacyVisibility(double trackPosition) - { - if (ObjectsSortedByStart == null || ObjectsSortedByStart.Length == 0 || StaticObjectStates.Count == 0) - { - return; - } - double d = trackPosition - LastUpdatedTrackPosition; - int n = ObjectsSortedByStart.Length; - double p = CameraTrackFollower.TrackPosition + Camera.Alignment.Position.Z; - - if (d < 0.0) - { - if (ObjectsSortedByStartPointer >= n) - { - ObjectsSortedByStartPointer = n - 1; - } - - if (ObjectsSortedByEndPointer >= n) - { - ObjectsSortedByEndPointer = n - 1; - } - - // dispose - while (ObjectsSortedByStartPointer >= 0) - { - int o = ObjectsSortedByStart[ObjectsSortedByStartPointer]; - - if (StaticObjectStates[o].StartingDistance > p + Camera.ForwardViewingDistance) - { - VisibleObjects.HideObject(StaticObjectStates[o]); - ObjectsSortedByStartPointer--; - } - else - { - break; - } - } - - // introduce - while (ObjectsSortedByEndPointer >= 0) - { - int o = ObjectsSortedByEnd[ObjectsSortedByEndPointer]; - - if (StaticObjectStates[o].EndingDistance >= p - Camera.BackwardViewingDistance) - { - if (StaticObjectStates[o].StartingDistance <= p + Camera.ForwardViewingDistance) - { - VisibleObjects.ShowObject(StaticObjectStates[o], ObjectType.Static); - } - - ObjectsSortedByEndPointer--; - } - else - { - break; - } - } - } - else if (d > 0.0) - { - if (ObjectsSortedByStartPointer < 0) - { - ObjectsSortedByStartPointer = 0; - } - - if (ObjectsSortedByEndPointer < 0) - { - ObjectsSortedByEndPointer = 0; - } - - // dispose - while (ObjectsSortedByEndPointer < n) - { - int o = ObjectsSortedByEnd[ObjectsSortedByEndPointer]; - - if (StaticObjectStates[o].EndingDistance < p - Camera.BackwardViewingDistance) - { - VisibleObjects.HideObject(StaticObjectStates[o]); - ObjectsSortedByEndPointer++; - } - else - { - break; - } - } - n = ObjectsSortedByStart.Length; - - // introduce - while (ObjectsSortedByStartPointer < n) - { - int o = ObjectsSortedByStart[ObjectsSortedByStartPointer]; - - if (StaticObjectStates[o].StartingDistance <= p + Camera.ForwardViewingDistance) - { - if (StaticObjectStates[o].EndingDistance >= p - Camera.BackwardViewingDistance) - { - VisibleObjects.ShowObject(StaticObjectStates[o], ObjectType.Static); - } - - ObjectsSortedByStartPointer++; - } - else - { - break; - } - } - } - - LastUpdatedTrackPosition = trackPosition; + Scene.UpdateVisibility(force); } public void UpdateViewingDistances(double backgroundImageDistance) { - double f = Math.Atan2(CameraTrackFollower.WorldDirection.Z, CameraTrackFollower.WorldDirection.X); - double c = Math.Atan2(Camera.AbsoluteDirection.Z, Camera.AbsoluteDirection.X) - f; - if (c < -Math.PI) - { - c += 2.0 * Math.PI; - } - else if (c > Math.PI) - { - c -= 2.0 * Math.PI; - } - - double a0 = c - 0.5 * Camera.HorizontalViewingAngle; - double a1 = c + 0.5 * Camera.HorizontalViewingAngle; - double max; - if (a0 <= 0.0 & a1 >= 0.0) - { - max = 1.0; - } - else - { - double c0 = Math.Cos(a0); - double c1 = Math.Cos(a1); - max = c0 > c1 ? c0 : c1; - if (max < 0.0) max = 0.0; - } - - double min; - if (a0 <= -Math.PI | a1 >= Math.PI) - { - min = -1.0; - } - else - { - double c0 = Math.Cos(a0); - double c1 = Math.Cos(a1); - min = c0 < c1 ? c0 : c1; - if (min > 0.0) min = 0.0; - } - - double d = backgroundImageDistance + Camera.ExtraViewingDistance; - Camera.ForwardViewingDistance = d * max; - Camera.BackwardViewingDistance = -d * min; - updateVisibility = VisibilityUpdate.Force; + Scene.UpdateViewingDistances(backgroundImageDistance); } /// Determines the maximum Anisotropic filtering level the system supports @@ -1174,14 +944,12 @@ public void ResetShader(Shader shader) public void SetBlendFunc() { + blendEnabled = true; SetBlendFunc(blendSrcFactor, blendDestFactor); } public void SetBlendFunc(BlendingFactor srcFactor, BlendingFactor destFactor) { - blendEnabled = true; - blendSrcFactor = srcFactor; - blendDestFactor = destFactor; GL.Enable(EnableCap.Blend); GL.BlendFunc(srcFactor, destFactor); } @@ -1205,6 +973,86 @@ public void RestoreBlendFunc() } } + public void SetColorMask(bool red, bool green, bool blue, bool alpha) + { + GL.ColorMask(red, green, blue, alpha); + } + + public void SetDepthMask(bool enabled) + { + if (depthMaskEnabled != enabled) + { + GL.DepthMask(enabled); + depthMaskEnabled = enabled; + } + } + + public void SetCullFace(bool enabled, CullFaceMode mode = CullFaceMode.Front) + { + if (enabled) + { + if (!cullFaceEnabled) + { + GL.Enable(EnableCap.CullFace); + cullFaceEnabled = true; + } + + if (cullFaceMode != mode) + { + GL.CullFace(mode); + cullFaceMode = mode; + } + } + else + { + if (cullFaceEnabled) + { + GL.Disable(EnableCap.CullFace); + cullFaceEnabled = false; + } + } + } + + public void SetDepthTest(bool enabled, DepthFunction function = DepthFunction.Lequal) + { + if (enabled) + { + if (!depthTestEnabled) + { + GL.Enable(EnableCap.DepthTest); + depthTestEnabled = true; + } + + if (depthTestFunction != function) + { + GL.DepthFunc(function); + depthTestFunction = function; + } + } + else + { + if (depthTestEnabled) + { + GL.Disable(EnableCap.DepthTest); + depthTestEnabled = false; + } + } + } + + public void SetActiveTexture(TextureUnit unit) + { + if (lastActiveTextureUnit != unit) + { + GL.ActiveTexture(unit); + lastActiveTextureUnit = unit; + } + } + + public void SetViewport(int x, int y, int width, int height) + { + GL.Viewport(x, y, width, height); + } + /// Specifies the OpenGL alpha function to perform public void SetAlphaFunc() { @@ -1292,6 +1140,81 @@ public void RenderFace(FaceState state, bool isDebugTouchMode = false) RenderFace(CurrentShader as Shader, state.Object, state.Face, isDebugTouchMode); } + public void RenderFace(AbstractShader shader, ObjectState state, MeshFace face, bool debugTouchMode = false, bool screenSpace = false, int vertexCount = -1) + { + if (shader is Shader defaultShader) + { + RenderFace(defaultShader, state, face, debugTouchMode, screenSpace, vertexCount); + return; + } + + if (shader is ShadowDepthShader shadowShader) + { + RenderFaceShadow(shadowShader, state, face, vertexCount); + return; + } + } + + private void RenderFaceShadow(ShadowDepthShader shader, ObjectState state, MeshFace face, int vertexCount) + { + if (state.Prototype == null || state.Prototype.Mesh == null || state.Prototype.Mesh.VAO == null) + { + return; + } + + if (vertexCount == -1) + { + vertexCount = face.Vertices.Length; + } + + if (state != lastObjectState || state.Prototype.Dynamic) + { + lastModelMatrix = state.ModelMatrix * Camera.TranslationMatrix; + shader.SetModelMatrix(lastModelMatrix); + + if (state.Matricies != null && state.Matricies.Length > 0) + { + shader.SetCurrentAnimationMatricies(state); +#pragma warning disable CS0618 + GL.BindBufferBase(BufferTarget.UniformBuffer, 0, state.MatrixBufferIndex); +#pragma warning restore CS0618 + } + } + + MeshMaterial material = state.Prototype.Mesh.Materials[face.Material]; + VertexArrayObject VAO = (VertexArrayObject)state.Prototype.Mesh.VAO; + + if (lastVAO != VAO.handle) + { + VAO.Bind(); + lastVAO = VAO.handle; + } + + bool needsTexture = material.DaytimeTexture != null && + ((material.Flags & MaterialFlags.TransparentColor) != 0 || + material.Color.A < 255); + + if (needsTexture && currentHost.LoadTexture(ref material.DaytimeTexture, (OpenGlTextureWrapMode)(material.WrapMode ?? OpenGlTextureWrapMode.ClampClamp))) + { + SetActiveTexture(TextureUnit.Texture0); + GL.BindTexture(TextureTarget.Texture2D, material.DaytimeTexture.OpenGlTextures[(int)(material.WrapMode ?? OpenGlTextureWrapMode.ClampClamp)].Name); + shader.SetHasTexture(true); + } + else + { + shader.SetHasTexture(false); + } + + shader.SetAlphaCutoff(0.5f); + shader.SetMaterialAlpha(material.Color.A / 255.0f); + shader.SetMaterialFlags(material.Flags); + + PrimitiveType drawMode = GetPrimitiveType(face.Flags); + VAO.Draw(drawMode, face.IboStartIndex, vertexCount); + + lastObjectState = state; + } + /// Draws a face using the specified shader and matricies /// The shader to use /// The ObjectState to draw @@ -1312,8 +1235,12 @@ public void RenderFace(Shader shader, ObjectState state, MeshFace face, Matrix4D /// The Face within the ObjectState /// Whether debug touch mode /// Used when a forced matrix, for items which are in screen space not camera space - public void RenderFace(Shader shader, ObjectState state, MeshFace face, bool debugTouchMode = false, bool screenSpace = false) + public void RenderFace(Shader shader, ObjectState state, MeshFace face, bool debugTouchMode = false, bool screenSpace = false, int vertexCount = -1) { + if (vertexCount == -1) + { + vertexCount = face.Vertices.Length; + } if ((state != lastObjectState || state.Prototype.Dynamic) && !screenSpace) { lastModelMatrix = state.ModelMatrix * Camera.TranslationMatrix; @@ -1337,13 +1264,13 @@ public void RenderFace(Shader shader, ObjectState state, MeshFace face, bool deb if (!OptionBackFaceCulling || (face.Flags & FaceFlags.Face2Mask) != 0) { - GL.Disable(EnableCap.CullFace); + SetCullFace(false); } else if (OptionBackFaceCulling) { if ((face.Flags & FaceFlags.Face2Mask) == 0) { - GL.Enable(EnableCap.CullFace); + SetCullFace(true); } } @@ -1457,8 +1384,7 @@ public void RenderFace(Shader shader, ObjectState state, MeshFace face, bool deb { //Additive blending- Full brightness factor = 1.0f; - GL.Enable(EnableCap.Blend); - GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); + SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); shader.SetFog(false); } else if (material.NighttimeTexture == null || material.NighttimeTexture == material.DaytimeTexture) @@ -1473,6 +1399,24 @@ public void RenderFace(Shader shader, ObjectState state, MeshFace face, bool deb } shader.SetBrightness(factor); + if (blendFactor != 0 && material.NighttimeTexture != null && material.NighttimeTexture != material.DaytimeTexture && currentHost.LoadTexture(ref material.NighttimeTexture, (OpenGlTextureWrapMode)material.WrapMode)) + { + OpenGlTexture nightTexture = material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode]; + if (lastBoundNightTexture != nightTexture) + { + SetActiveTexture(TextureUnit.Texture1); + GL.BindTexture(TextureTarget.Texture2D, nightTexture.Name); + lastBoundNightTexture = nightTexture; + SetActiveTexture(TextureUnit.Texture0); + } + shader.SetIsNightTexture(true); + shader.SetNightBlendFactor(blendFactor); + } + else + { + shader.SetIsNightTexture(false); + } + float alphaFactor = distanceFactor; if (material.NighttimeTexture != null && (material.Flags & MaterialFlags.CrossFadeTexture) != 0) { @@ -1482,35 +1426,12 @@ public void RenderFace(Shader shader, ObjectState state, MeshFace face, bool deb shader.SetOpacity(inv255 * material.Color.A * alphaFactor); // render polygon - VAO.Draw(drawMode, face.IboStartIndex, face.Vertices.Length); - } - - // nighttime polygon - if (blendFactor != 0 && material.NighttimeTexture != null && material.NighttimeTexture != material.DaytimeTexture && currentHost.LoadTexture(ref material.NighttimeTexture, (OpenGlTextureWrapMode)material.WrapMode)) - { - // texture - if (LastBoundTexture != material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode]) + VAO.Draw(drawMode, face.IboStartIndex, vertexCount); + if (material.BlendMode == MeshMaterialBlendMode.Additive) { - GL.BindTexture(TextureTarget.Texture2D, material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode].Name); - LastBoundTexture = material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode]; + RestoreBlendFunc(); + shader.SetFog(true); } - - - GL.Enable(EnableCap.Blend); - - // alpha test - shader.SetAlphaTest(true); - shader.SetAlphaFunction(AlphaFunction.Greater, 0.0f); - - // blend mode - float alphaFactor = distanceFactor * blendFactor; - - shader.SetOpacity(inv255 * material.Color.A * alphaFactor); - - // render polygon - VAO.Draw(drawMode, face.IboStartIndex, face.Vertices.Length); - RestoreBlendFunc(); - RestoreAlphaFunc(); } @@ -1523,7 +1444,7 @@ public void RenderFace(Shader shader, ObjectState state, MeshFace face, bool deb VertexArrayObject normalsVao = (VertexArrayObject)state.Prototype.Mesh.NormalsVAO; normalsVao.Bind(); lastVAO = normalsVao.handle; - normalsVao.Draw(PrimitiveType.Lines, face.NormalsIboStartIndex, face.Vertices.Length * 2); + normalsVao.Draw(PrimitiveType.Lines, face.NormalsIboStartIndex, vertexCount * 2); } // finalize @@ -1685,8 +1606,7 @@ public void RenderFaceImmediateMode(ObjectState state, MeshFace face, Matrix4D m if (material.BlendMode == MeshMaterialBlendMode.Additive) { factor = 1.0f; - GL.Enable(EnableCap.Blend); - GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); + SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); GL.Disable(EnableCap.Fog); } else if (material.NighttimeTexture == null) @@ -1747,7 +1667,7 @@ public void RenderFaceImmediateMode(ObjectState state, MeshFace face, Matrix4D m LastBoundTexture = material.NighttimeTexture.OpenGlTextures[(int)material.WrapMode]; } - GL.Enable(EnableCap.Blend); + Device.SetBlend(true); // alpha test GL.Enable(EnableCap.AlphaTest); @@ -1856,7 +1776,15 @@ public void SetWindowSize(int width, int height) } } - public ConcurrentQueue RenderThreadJobs; + public void ShowObject(ObjectState Object, ObjectType Type) + { + Scene.ShowObject(Object, Type); + } + + public void HideObject(ObjectState Object) + { + Scene.HideObject(Object); + } /// This method is used during loading to run commands requiring an OpenGL context in the main render loop /// The OpenGL command diff --git a/source/LibRender2/Camera/Camera.cs b/source/LibRender2/Camera/Camera.cs index 53c127d068..01ae3cb140 100644 --- a/source/LibRender2/Camera/Camera.cs +++ b/source/LibRender2/Camera/Camera.cs @@ -61,7 +61,7 @@ public CameraAlignment AlignmentDirection get => alignmentDirection; set { - Renderer.UpdateVisibility(true); + Renderer.Scene.UpdateVisibility(true); alignmentDirection = value; } } @@ -102,21 +102,6 @@ public CameraViewMode CurrentMode public CameraAlignment SavedTrack; /// The current quad tree leaf node public QuadTreeLeafNode QuadTreeLeaf; - - /// The target camera car index for queued transitions - public int TargetCameraCar = -1; - /// The previous camera car index (used for transitions) - public int PreviousCameraCar = -1; - /// The current transition timer - public double CameraCarTransitionTimer = 0.0; - /// Whether the camera is transitioning between cars - public bool IsTransitioning = false; - /// The start anchor for mode transitions (fly-in) - public (Vector3 Position, Vector3 Direction, Vector3 Up, Vector3 Side, double TrackPosition, CameraAlignment Alignment) ModeTransitionStart; - /// The timer for mode transitions (fly-in) - public double ModeTransitionTimer = 1.0; - /// The duration of the camera car transition in seconds - public const double CameraCarTransitionDuration = 0.4; private Vector3 absolutePosition; private CameraAlignment alignmentDirection; diff --git a/source/LibRender2/LibRender2.csproj b/source/LibRender2/LibRender2.csproj index dcea6d70e2..34443aecfb 100644 --- a/source/LibRender2/LibRender2.csproj +++ b/source/LibRender2/LibRender2.csproj @@ -120,7 +120,16 @@ - + + + + + + + + + + diff --git a/source/LibRender2/Lighting/Lighting.cs b/source/LibRender2/Lighting/Lighting.cs index 1ad677db87..de7850ccfc 100644 --- a/source/LibRender2/Lighting/Lighting.cs +++ b/source/LibRender2/Lighting/Lighting.cs @@ -1,4 +1,4 @@ -using System; +using System; using OpenBveApi.Colors; using OpenBveApi.Math; using OpenBveApi.Routes; @@ -17,6 +17,9 @@ public class Lighting /// The current dynamic cab brightness public double DynamicCabBrightness = 255; + /// The strength of the shadows (0.0 to 1.0) + public float ShadowStrength = 1.0f; + /// The current ambient light color public Color24 OptionAmbientColor = Color24.LightGrey; diff --git a/source/LibRender2/Managers/GpuResourceManager.cs b/source/LibRender2/Managers/GpuResourceManager.cs new file mode 100644 index 0000000000..039e1a602c --- /dev/null +++ b/source/LibRender2/Managers/GpuResourceManager.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using OpenTK.Graphics.OpenGL; + +namespace LibRender2.Managers +{ + /// + /// Manages the lifecycle of GPU resources to prevent leaks. + /// + public class GpuResourceManager + { + private static readonly List vaosToDelete = new List(); + private static readonly List vbosToDelete = new List(); + private static readonly List ibosToDelete = new List(); + + /// + /// Queues a Vertex Array Object for deletion. + /// + public static void QueueVaoForDeletion(int handle) + { + if (handle <= 0) return; + lock (vaosToDelete) + { + vaosToDelete.Add(handle); + } + } + + /// + /// Queues a Vertex Buffer Object for deletion. + /// + public static void QueueVboForDeletion(int handle) + { + if (handle <= 0) return; + lock (vbosToDelete) + { + vbosToDelete.Add(handle); + } + } + + /// + /// Queues an Index Buffer Object for deletion. + /// + public static void QueueIboForDeletion(int handle) + { + if (handle <= 0) return; + lock (ibosToDelete) + { + ibosToDelete.Add(handle); + } + } + + /// + /// Deletes all queued resources. Must be called from a thread with a valid OpenGL context. + /// + public void ReleaseResources() + { + lock (vaosToDelete) + { + if (vaosToDelete.Count > 0) + { + foreach (int handle in vaosToDelete) + { + int h = handle; + GL.DeleteVertexArrays(1, ref h); + } + vaosToDelete.Clear(); + } + } + + lock (vbosToDelete) + { + if (vbosToDelete.Count > 0) + { + foreach (int handle in vbosToDelete) + { + int h = handle; + GL.DeleteBuffers(1, ref h); + } + vbosToDelete.Clear(); + } + } + + lock (ibosToDelete) + { + if (ibosToDelete.Count > 0) + { + foreach (int handle in ibosToDelete) + { + int h = handle; + GL.DeleteBuffers(1, ref h); + } + ibosToDelete.Clear(); + } + } + } + } +} diff --git a/source/LibRender2/Managers/SceneManager.cs b/source/LibRender2/Managers/SceneManager.cs new file mode 100644 index 0000000000..49f71f66da --- /dev/null +++ b/source/LibRender2/Managers/SceneManager.cs @@ -0,0 +1,470 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using LibRender2.Objects; +using OpenBveApi.Math; +using OpenBveApi.Objects; +using OpenBveApi.Routes; +using OpenBveApi.World; + +namespace LibRender2.Managers +{ + /// + /// Manages the objects within the scene, including visibility and dynamic object updates. + /// + public class SceneManager + { + private readonly BaseRenderer renderer; + + /// The list of static object states in the scene. + public List StaticObjectStates = new List(); + + /// The list of dynamic object states in the scene. + public List DynamicObjectStates = new List(); + + /// The library of currently visible objects. + public VisibleObjectLibrary VisibleObjects; + + public int[] ObjectsSortedByStart; + public int[] ObjectsSortedByEnd; + public int ObjectsSortedByStartPointer; + public int ObjectsSortedByEndPointer; + public double LastUpdatedTrackPosition; + + /// Whether the visibility update thread should continue running. + public bool VisibilityThreadShouldRun = true; + + /// The lock to be held whilst visibility updates or loading operations are in progress + public readonly object VisibilityUpdateLock = new object(); + + private readonly object LockObject = new object(); + + private VisibilityUpdate updateVisibility; + private Thread visibilityThread; + + public void ShowObject(ObjectState Object, ObjectType Type) + { + VisibleObjects.ShowObject(Object, Type); + } + + public void HideObject(ObjectState Object) + { + VisibleObjects.HideObject(Object); + } + + /// A queue of jobs to be executed on the render thread (often related to object creation/disposal) + public readonly ConcurrentQueue RenderThreadJobs; + + public SceneManager(BaseRenderer renderer) + { + this.renderer = renderer; + StaticObjectStates = new List(); + DynamicObjectStates = new List(); + VisibleObjects = new VisibleObjectLibrary(renderer); + RenderThreadJobs = new ConcurrentQueue(); + + visibilityThread = new Thread(RunVisibilityThread) + { + IsBackground = true, + Name = "VisibilityThread" + }; + visibilityThread.Start(); + } + + /// + /// Deinitializes the scene manager and stops the visibility thread. + /// + public void DeInitialize() + { + VisibilityThreadShouldRun = false; + } + + private void RunVisibilityThread() + { + while (VisibilityThreadShouldRun) + { + lock (VisibilityUpdateLock) + { + if (updateVisibility != VisibilityUpdate.None && renderer.CameraTrackFollower != null) + { + UpdateVisibility(renderer.CameraTrackFollower.TrackPosition + renderer.Camera.Alignment.Position.Z); + } + } + + if (updateVisibility == VisibilityUpdate.None) + { + Thread.Sleep(100); + } + } + } + + public void UpdateVisibility(bool force) + { + updateVisibility = force ? VisibilityUpdate.Force : VisibilityUpdate.Normal; + } + + private void UpdateVisibility(double trackPosition) + { + if (renderer.currentOptions.ObjectDisposalMode == ObjectDisposalMode.QuadTree) + { + UpdateQuadTreeVisibility(); + } + else + { + if (updateVisibility == VisibilityUpdate.Normal) + { + UpdateLegacyVisibility(trackPosition); + } + else + { + UpdateLegacyVisibility(trackPosition + 0.01); + UpdateLegacyVisibility(trackPosition - 0.01); + } + } + + updateVisibility = VisibilityUpdate.None; + } + + private void UpdateQuadTreeVisibility() + { + if (VisibleObjects == null || VisibleObjects.quadTree == null) + { + Thread.Sleep(10); + return; + } + renderer.Camera.UpdateQuadTreeLeaf(); + } + + private void UpdateLegacyVisibility(double trackPosition) + { + if (ObjectsSortedByStart == null || ObjectsSortedByEnd == null || ObjectsSortedByStart.Length == 0 || StaticObjectStates.Count == 0) + { + return; + } + + double d = trackPosition - LastUpdatedTrackPosition; + int n = ObjectsSortedByStart.Length; + double p = renderer.CameraTrackFollower.TrackPosition + renderer.Camera.Alignment.Position.Z; + + if (d < 0.0) + { + if (ObjectsSortedByStartPointer >= n) + { + ObjectsSortedByStartPointer = n - 1; + } + + if (ObjectsSortedByEndPointer >= n) + { + ObjectsSortedByEndPointer = n - 1; + } + + while (ObjectsSortedByStartPointer >= 0) + { + int o = ObjectsSortedByStart[ObjectsSortedByStartPointer]; + + if (StaticObjectStates[o].StartingDistance > p + renderer.Camera.ForwardViewingDistance) + { + VisibleObjects.HideObject(StaticObjectStates[o]); + ObjectsSortedByStartPointer--; + } + else + { + break; + } + } + + while (ObjectsSortedByEndPointer >= 0) + { + int o = ObjectsSortedByEnd[ObjectsSortedByEndPointer]; + + if (StaticObjectStates[o].EndingDistance >= p - renderer.Camera.BackwardViewingDistance) + { + if (StaticObjectStates[o].StartingDistance <= p + renderer.Camera.ForwardViewingDistance) + { + VisibleObjects.ShowObject(StaticObjectStates[o], ObjectType.Static); + } + + ObjectsSortedByEndPointer--; + } + else + { + break; + } + } + } + else if (d > 0.0) + { + if (ObjectsSortedByStartPointer < 0) + { + ObjectsSortedByStartPointer = 0; + } + + if (ObjectsSortedByEndPointer < 0) + { + ObjectsSortedByEndPointer = 0; + } + + while (ObjectsSortedByEndPointer < n) + { + int o = ObjectsSortedByEnd[ObjectsSortedByEndPointer]; + + if (StaticObjectStates[o].EndingDistance < p - renderer.Camera.BackwardViewingDistance) + { + VisibleObjects.HideObject(StaticObjectStates[o]); + ObjectsSortedByEndPointer++; + } + else + { + break; + } + } + + while (ObjectsSortedByStartPointer < n) + { + int o = ObjectsSortedByStart[ObjectsSortedByStartPointer]; + + if (StaticObjectStates[o].StartingDistance <= p + renderer.Camera.ForwardViewingDistance) + { + if (StaticObjectStates[o].EndingDistance >= p - renderer.Camera.BackwardViewingDistance) + { + VisibleObjects.ShowObject(StaticObjectStates[o], ObjectType.Static); + } + + ObjectsSortedByStartPointer++; + } + else + { + break; + } + } + } + + LastUpdatedTrackPosition = trackPosition; + } + + public void UpdateViewingDistances(double backgroundImageDistance) + { + double f = Math.Atan2(renderer.CameraTrackFollower.WorldDirection.Z, renderer.CameraTrackFollower.WorldDirection.X); + double c = Math.Atan2(renderer.Camera.AbsoluteDirection.Z, renderer.Camera.AbsoluteDirection.X) - f; + if (c < -Math.PI) + { + c += 2.0 * Math.PI; + } + else if (c > Math.PI) + { + c -= 2.0 * Math.PI; + } + + double a0 = c - 0.5 * renderer.Camera.HorizontalViewingAngle; + double a1 = c + 0.5 * renderer.Camera.HorizontalViewingAngle; + double max; + if (a0 <= 0.0 & a1 >= 0.0) + { + max = 1.0; + } + else + { + double c0 = Math.Cos(a0); + double c1 = Math.Cos(a1); + max = c0 > c1 ? c0 : c1; + if (max < 0.0) max = 0.0; + } + + double min; + if (a0 <= -Math.PI | a1 >= Math.PI) + { + min = -1.0; + } + else + { + double c0 = Math.Cos(a0); + double c1 = Math.Cos(a1); + min = c0 < c1 ? c0 : c1; + if (min > 0.0) min = 0.0; + } + + double d = Math.Max(backgroundImageDistance, renderer.currentOptions.ViewingDistance) + renderer.Camera.ExtraViewingDistance; + renderer.Camera.ForwardViewingDistance = d * max; + renderer.Camera.BackwardViewingDistance = -d * min; + updateVisibility = VisibilityUpdate.Force; + } + + public int CreateStaticObject(StaticObject Prototype, Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, ObjectDisposalMode AccurateObjectDisposal, ObjectCreationParameters Parameters, double BlockLength) + { + Matrix4D Translate = Matrix4D.CreateTranslation(Position.X, Position.Y, -Position.Z); + Matrix4D Rotate = (Matrix4D)new Transformation(LocalTransformation, WorldTransformation); + return CreateStaticObject(Position, Prototype, LocalTransformation, Rotate, Translate, AccurateObjectDisposal, Parameters, BlockLength); + } + + public int CreateStaticObject(Vector3 Position, StaticObject Prototype, Transformation LocalTransformation, Matrix4D Rotate, Matrix4D Translate, ObjectDisposalMode AccurateObjectDisposal, ObjectCreationParameters Parameters, double BlockLength) + { + if (Prototype == null) + { + return -1; + } + + if (Prototype.Mesh.Faces.Length == 0) + { + //Null object- Waste of time trying to calculate anything for these + return -1; + } + + float startingDistance = float.MaxValue; + float endingDistance = float.MinValue; + + if (AccurateObjectDisposal == ObjectDisposalMode.Accurate) + { + foreach (VertexTemplate vertex in Prototype.Mesh.Vertices) + { + Vector3 Coordinates = new Vector3(vertex.Coordinates); + Coordinates.Rotate(LocalTransformation); + + if (Coordinates.Z < startingDistance) + { + startingDistance = (float)Coordinates.Z; + } + + if (Coordinates.Z > endingDistance) + { + endingDistance = (float)Coordinates.Z; + } + } + + startingDistance += (float)Parameters.AccurateObjectDisposalZOffset; + endingDistance += (float)Parameters.AccurateObjectDisposalZOffset; + } + + const double minBlockLength = 20.0; + + if (BlockLength < minBlockLength) + { + BlockLength *= Math.Ceiling(minBlockLength / BlockLength); + } + + switch (AccurateObjectDisposal) + { + case ObjectDisposalMode.Accurate: + startingDistance += (float)Parameters.TrackPosition; + endingDistance += (float)Parameters.TrackPosition; + double z = BlockLength * Math.Floor(Parameters.TrackPosition / BlockLength); + Parameters.StartingDistance = Math.Min(z - BlockLength, startingDistance); + Parameters.EndingDistance = Math.Max(z + 2.0 * BlockLength, endingDistance); + startingDistance = (float)(BlockLength * Math.Floor(Parameters.StartingDistance / BlockLength)); + endingDistance = (float)(BlockLength * Math.Ceiling(Parameters.EndingDistance / BlockLength)); + break; + case ObjectDisposalMode.Legacy: + startingDistance = (float)Parameters.StartingDistance; + endingDistance = (float)Parameters.EndingDistance; + break; + case ObjectDisposalMode.Mechanik: + startingDistance = (float)Parameters.StartingDistance; + endingDistance = (float)Parameters.EndingDistance + 1500; + if (startingDistance < 0) + { + startingDistance = 0; + } + break; + } + StaticObjectStates.Add(new ObjectState + { + Prototype = Prototype, + Translation = Translate, + Rotate = Rotate, + StartingDistance = startingDistance, + EndingDistance = endingDistance, + WorldPosition = Position, + DisableShadowCasting = Parameters.DisableShadowCasting + }); + + foreach (MeshFace face in Prototype.Mesh.Faces) + { + switch (face.Flags & FaceFlags.FaceTypeMask) + { + case FaceFlags.Triangles: + renderer.InfoTotalTriangles++; + break; + case FaceFlags.TriangleStrip: + renderer.InfoTotalTriangleStrip++; + break; + case FaceFlags.Quads: + renderer.InfoTotalQuads++; + break; + case FaceFlags.QuadStrip: + renderer.InfoTotalQuadStrip++; + break; + case FaceFlags.Polygon: + renderer.InfoTotalPolygon++; + break; + } + } + + return StaticObjectStates.Count - 1; + } + + public void CreateDynamicObject(ref ObjectState internalObject) + { + if (internalObject == null) + { + internalObject = new ObjectState(new StaticObject(renderer.currentHost)); + } + + internalObject.Prototype.Dynamic = true; + + DynamicObjectStates.Add(internalObject); + } + + public void InitializeVisibility() + { + if (!renderer.ForceLegacyOpenGL) // as we might want to switch renderer types + { + for (int i = 0; i < StaticObjectStates.Count; i++) + { + VAOExtensions.CreateVAO(StaticObjectStates[i].Prototype.Mesh, false, renderer.DefaultShader.VertexLayout, renderer); + } + for (int i = 0; i < DynamicObjectStates.Count; i++) + { + VAOExtensions.CreateVAO(DynamicObjectStates[i].Prototype.Mesh, false, renderer.DefaultShader.VertexLayout, renderer); + } + } + ObjectsSortedByStart = StaticObjectStates.Select((x, i) => new { Index = i, Distance = x.StartingDistance }).OrderBy(x => x.Distance).Select(x => x.Index).ToArray(); + ObjectsSortedByEnd = StaticObjectStates.Select((x, i) => new { Index = i, Distance = x.EndingDistance }).OrderBy(x => x.Distance).Select(x => x.Index).ToArray(); + ObjectsSortedByStartPointer = 0; + ObjectsSortedByEndPointer = 0; + + if (renderer.currentOptions.ObjectDisposalMode == ObjectDisposalMode.QuadTree) + { + foreach (ObjectState state in StaticObjectStates) + { + VisibleObjects.quadTree.Add(state, Orientation3.Default); + } + VisibleObjects.quadTree.Initialize(renderer.currentOptions.QuadTreeLeafSize); + UpdateQuadTreeVisibility(); + } + else + { + double p = renderer.CameraTrackFollower.TrackPosition + renderer.Camera.Alignment.Position.Z; + foreach (ObjectState state in StaticObjectStates.Where(recipe => recipe.StartingDistance <= p + renderer.Camera.ForwardViewingDistance & recipe.EndingDistance >= p - renderer.Camera.BackwardViewingDistance)) + { + VisibleObjects.ShowObject(state, ObjectType.Static); + } + } + } + + public void Reset() + { + renderer.currentHost.AnimatedObjectCollectionCache.Clear(); + List> keys = renderer.currentHost.StaticObjectCache.Keys.ToList(); + for (int i = 0; i < keys.Count; i++) + { + if (!System.IO.File.Exists(keys[i].Item1) || System.IO.File.GetLastWriteTime(keys[i].Item1) != keys[i].Item3) + { + renderer.currentHost.StaticObjectCache.Remove(keys[i]); + } + } + renderer.TextureManager.UnloadAllTextures(true); + VisibleObjects.Clear(); + } + } +} diff --git a/source/LibRender2/Menu/Menu.OptionType.cs b/source/LibRender2/Menu/Menu.OptionType.cs index 73d2beb992..6f5d01a112 100644 --- a/source/LibRender2/Menu/Menu.OptionType.cs +++ b/source/LibRender2/Menu/Menu.OptionType.cs @@ -1,4 +1,4 @@ -//Simplified BSD License (BSD-2-Clause) +//Simplified BSD License (BSD-2-Clause) // //Copyright (c) 2024, Maurizo M. Gavioli, The OpenBVE Project // @@ -46,8 +46,6 @@ public enum OptionType /// Sets whether to automatically reload the current objects AutoReloadObjects, /// Sets the shadow quality - ShadowQuality, - /// Sets whether shadow casters are filtered per cascade - ShadowFilterCascades + ShadowQuality } } diff --git a/source/LibRender2/Menu/MenuEntries/MenuOption.cs b/source/LibRender2/Menu/MenuEntries/MenuOption.cs index c4330b9898..6bb95e63dd 100644 --- a/source/LibRender2/Menu/MenuEntries/MenuOption.cs +++ b/source/LibRender2/Menu/MenuEntries/MenuOption.cs @@ -1,4 +1,4 @@ -//Simplified BSD License (BSD-2-Clause) +//Simplified BSD License (BSD-2-Clause) // //Copyright (c) 2024, Maurizo M. Gavioli, The OpenBVE Project // @@ -169,9 +169,6 @@ public MenuOption(AbstractMenu menu, OptionType type, string text, object[] entr break; } return; - case OptionType.ShadowFilterCascades: - CurrentlySelectedOption = BaseMenu.CurrentOptions.ShadowFilterCascades ? 0 : 1; - return; } CurrentlySelectedOption = 0; } @@ -319,9 +316,6 @@ public void Flip() } BaseMenu.Renderer.InitializeShadows(); break; - case OptionType.ShadowFilterCascades: - BaseMenu.CurrentOptions.ShadowFilterCascades = !BaseMenu.CurrentOptions.ShadowFilterCascades; - break; } diff --git a/source/LibRender2/Objects/FaceState.cs b/source/LibRender2/Objects/FaceState.cs index 04b951ecdb..1db48d77b5 100644 --- a/source/LibRender2/Objects/FaceState.cs +++ b/source/LibRender2/Objects/FaceState.cs @@ -1,4 +1,4 @@ -using OpenBveApi.Objects; +using OpenBveApi.Objects; namespace LibRender2.Objects { @@ -11,12 +11,17 @@ public class FaceState public readonly MeshFace Face; /// Holds the reference to the base renderer public readonly BaseRenderer Renderer; + /// The distance from the camera used for sorting (alpha faces only) + internal double SortDistance; + /// The internal index used for stable sorting + internal readonly int InternalIndex; - public FaceState(ObjectState _object, MeshFace face, BaseRenderer renderer) + public FaceState(ObjectState _object, MeshFace face, BaseRenderer renderer, int internalIndex) { Object = _object; Face = face; Renderer = renderer; + InternalIndex = internalIndex; if (Object.Prototype.Mesh.VAO == null && !Renderer.ForceLegacyOpenGL) { VAOExtensions.CreateVAO(Object.Prototype.Mesh, Object.Prototype.Dynamic, Renderer.DefaultShader.VertexLayout, Renderer); diff --git a/source/LibRender2/Objects/ObjectLibrary.cs b/source/LibRender2/Objects/ObjectLibrary.cs index 7c3b389c67..cef9ba487f 100644 --- a/source/LibRender2/Objects/ObjectLibrary.cs +++ b/source/LibRender2/Objects/ObjectLibrary.cs @@ -13,6 +13,25 @@ namespace LibRender2.Objects { + public class FaceComparer : IComparer + { + /// Compares two faces for sorting, primarily by distance from camera. + public int Compare(FaceState a, FaceState b) + { + if (ReferenceEquals(a, b)) return 0; + if (a == null) return -1; + if (b == null) return 1; + + // Sort by distance from camera (Back-to-Front for alpha blending) + int result = b.SortDistance.CompareTo(a.SortDistance); + + // Tie-breakers to ensure stable sort order and prevent flickering + if (result == 0) result = a.InternalIndex.CompareTo(b.InternalIndex); + + return result; + } + } + public class VisibleObjectLibrary { private readonly BaseRenderer renderer; @@ -24,12 +43,15 @@ public class VisibleObjectLibrary private readonly List myAlphaFaces; private readonly List myOverlayOpaqueFaces; private List myOverlayAlphaFaces; + private readonly FaceComparer faceComparer = new FaceComparer(); public readonly ReadOnlyCollection OpaqueFaces; // StaticOpaque and DynamicOpaque public readonly ReadOnlyCollection OverlayOpaqueFaces; public readonly ReadOnlyCollection AlphaFaces; // DynamicAlpha public ReadOnlyCollection OverlayAlphaFaces; public readonly object LockObject = new object(); + private bool needsSort; + private int faceCount; internal VisibleObjectLibrary(BaseRenderer Renderer) { @@ -64,6 +86,7 @@ private void RemoveObject(ObjectState state) myAlphaFaces.RemoveAll(x => x.Object == state); myOverlayOpaqueFaces.RemoveAll(x => x.Object == state); myOverlayAlphaFaces.RemoveAll(x => x.Object == state); + needsSort = true; } } @@ -78,8 +101,8 @@ public void Clear() myAlphaFaces.Clear(); myOverlayOpaqueFaces.Clear(); myOverlayAlphaFaces.Clear(); - renderer.StaticObjectStates.Clear(); - renderer.DynamicObjectStates.Clear(); + renderer.Scene.StaticObjectStates = new List(); + renderer.Scene.DynamicObjectStates = new List(); } } @@ -107,16 +130,15 @@ public void ShowObject(ObjectState State, ObjectType Type) * Unfortunately, there appear to be X objects in the wild which expect a non-default wrapping mode * which means the best fast exit we can do is to check for RepeatRepeat.... * - */ - for (int i = 0; i < face.Vertices.Length; i++) + */ + foreach (VertexTemplate vertex in State.Prototype.Mesh.Vertices) { - int v = face.Vertices[i].Index; - if (State.Prototype.Mesh.Vertices[v].TextureCoordinates.X < 0.0f || State.Prototype.Mesh.Vertices[v].TextureCoordinates.X > 1.0f) + if (vertex.TextureCoordinates.X < 0.0f || vertex.TextureCoordinates.X > 1.0f) { wrap |= OpenGlTextureWrapMode.RepeatClamp; } - if (State.Prototype.Mesh.Vertices[v].TextureCoordinates.Y < 0.0f || State.Prototype.Mesh.Vertices[v].TextureCoordinates.Y > 1.0f) + if (vertex.TextureCoordinates.Y < 0.0f || vertex.TextureCoordinates.Y > 1.0f) { wrap |= OpenGlTextureWrapMode.ClampRepeat; } @@ -132,7 +154,7 @@ public void ShowObject(ObjectState State, ObjectType Type) bool alpha = false; - if (Type == ObjectType.Overlay && renderer.Camera.CurrentRestriction != CameraRestrictionMode.NotAvailable) + if (Type == ObjectType.Overlay && (renderer.Camera.CurrentRestriction == CameraRestrictionMode.On || renderer.Camera.CurrentRestriction == CameraRestrictionMode.Off)) { alpha = true; } @@ -233,42 +255,15 @@ public void ShowObject(ObjectState State, ObjectType Type) { if (!alpha) { - /* - * If an opaque face, itinerate through the list to see if the prototype is present in the list - * When the new renderer is in use, this prevents re-binding the VBO as it is simply re-drawn with - * a different translation matrix - * NOTE: The shader isn't currently smart enough to do depth discards, so if this changes may need to - * be revisited - */ - if (list.Count == 0) - { - list.Add(new FaceState(State, face, renderer)); - } - else - { - for (int i = 0; i < list.Count; i++) - { - - if (list[i].Object.Prototype == State.Prototype) - { - list.Insert(i, new FaceState(State, face, renderer)); - break; - } - - if (i == list.Count - 1) - { - list.Add(new FaceState(State, face, renderer)); - break; - } - } - } + list.Add(new FaceState(State, face, renderer, faceCount++)); + needsSort = true; } else { /* * Alpha faces should be inserted at the end of the list- We're going to sort it anyway so it makes no odds */ - list.Add(new FaceState(State, face, renderer)); + list.Add(new FaceState(State, face, renderer, faceCount++)); } } } @@ -281,52 +276,52 @@ public void HideObject(ObjectState State) public List GetSortedPolygons(bool overlay = false) { - if (overlay) + lock (LockObject) { - myOverlayAlphaFaces = GetSortedPolygons(myOverlayAlphaFaces.AsReadOnly()); - OverlayAlphaFaces = myOverlayAlphaFaces.AsReadOnly(); - return OverlayAlphaFaces.ToList(); + if (overlay) + { + SortPolygons(myOverlayAlphaFaces, true); + return new List(myOverlayAlphaFaces); + } + + if (needsSort) + { + /* + * Sort opaque faces by Prototype (VAO) to minimize binds + * When the new renderer is in use, this prevents re-binding the VBO as it is simply re-drawn with + * a different translation matrix + * NOTE: The shader isn't currently smart enough to do depth discards, so if this changes may need to + * be revisited (e.g. Front-to-Back sorting for Early-Z) + */ + myOpaqueFaces.Sort((x, y) => + { + if (x.Object.Prototype == y.Object.Prototype) return 0; + return x.Object.Prototype.GetHashCode().CompareTo(y.Object.Prototype.GetHashCode()); + }); + needsSort = false; + } + + SortPolygons(myAlphaFaces); + return new List(myAlphaFaces); } - return GetSortedPolygons(AlphaFaces); } - private List GetSortedPolygons(ReadOnlyCollection faces) + private void SortPolygons(List faces, bool overlay = false) { - // calculate distance - double[] distances = new double[faces.Count]; + // calculate distance from camera forward vector + Vector3 forward = overlay + ? new Vector3(renderer.Camera.AbsoluteDirection.X, renderer.Camera.AbsoluteDirection.Y, -renderer.Camera.AbsoluteDirection.Z) + : renderer.Camera.AbsoluteDirection; + + Vector3 cameraPos = overlay ? Vector3.Zero : renderer.Camera.AbsolutePosition; - Parallel.For(0, faces.Count, i => + foreach (var face in faces) { - if (faces[i].Face.Vertices.Length >= 3) - { - Vector4 v0 = new Vector4(faces[i].Object.Prototype.Mesh.Vertices[faces[i].Face.Vertices[0].Index].Coordinates, 1.0); - Vector4 v1 = new Vector4(faces[i].Object.Prototype.Mesh.Vertices[faces[i].Face.Vertices[1].Index].Coordinates, 1.0); - Vector4 v2 = new Vector4(faces[i].Object.Prototype.Mesh.Vertices[faces[i].Face.Vertices[2].Index].Coordinates, 1.0); - Vector4 w1 = v1 - v0; - Vector4 w2 = v2 - v0; - v0.Z *= -1.0; - w1.Z *= -1.0; - w2.Z *= -1.0; - v0 = Vector4.Transform(v0, faces[i].Object.ModelMatrix); - w1 = Vector4.Transform(w1, faces[i].Object.ModelMatrix); - w2 = Vector4.Transform(w2, faces[i].Object.ModelMatrix); - v0.Z *= -1.0; - w1.Z *= -1.0; - w2.Z *= -1.0; - Vector3 d = Vector3.Cross(w1.Xyz, w2.Xyz); - double t = d.Norm(); - - if (t != 0.0) - { - d /= t; - Vector3 w0 = v0.Xyz - renderer.Camera.AbsolutePosition; - t = Vector3.Dot(d, w0); - distances[i] = -t * t; - } - } - }); + face.SortDistance = Vector3.Dot(face.Object.WorldPosition - cameraPos, forward); + } // sort - return faces.Select((face, index) => new { Face = face, Distance = distances[index] }).OrderBy(list => list.Distance).Select(list => list.Face).ToList(); + faces.Sort(faceComparer); } + } } diff --git a/source/LibRender2/Passes/GeometryPass.cs b/source/LibRender2/Passes/GeometryPass.cs new file mode 100644 index 0000000000..a2e3bd2231 --- /dev/null +++ b/source/LibRender2/Passes/GeometryPass.cs @@ -0,0 +1,164 @@ +using System.Collections.Generic; +using System.Linq; +using LibRender2.Objects; +using LibRender2.Pipeline; +using OpenBveApi.Graphics; +using OpenBveApi.Interface; +using OpenBveApi.Objects; +using OpenTK.Graphics.OpenGL; + +namespace LibRender2.Passes +{ + /// + /// Renders the opaque and alpha-tested world geometry. + /// + public class GeometryPass : IRenderPass + { + public void Execute(RenderContext context) + { + BaseRenderer renderer = context.Renderer; + + // 1. Setup lighting and fog for world geometry + if (renderer.AvailableNewRenderer) + { + if (renderer.OptionLighting) + { + renderer.DefaultShader.SetIsLight(true); + renderer.DefaultShader.SetLightPosition(renderer.TransformedLightPosition); + renderer.DefaultShader.SetLightAmbient(renderer.Lighting.OptionAmbientColor); + renderer.DefaultShader.SetLightDiffuse(renderer.Lighting.OptionDiffuseColor); + renderer.DefaultShader.SetLightSpecular(renderer.Lighting.OptionSpecularColor); + renderer.DefaultShader.SetLightModel(renderer.Lighting.LightModel); + } + renderer.Fog.Set(); + renderer.DefaultShader.SetTexture(0); + renderer.CurrentProjectionMatrix = context.ProjectionMatrix; + renderer.CurrentViewMatrix = context.ViewMatrix; + renderer.DefaultShader.SetCurrentProjectionMatrix(renderer.CurrentProjectionMatrix); + renderer.DefaultShader.SetCurrentViewMatrix(renderer.CurrentViewMatrix); + renderer.BindCSMToDefaultShader(); + } + + renderer.ResetOpenGlState(); + GL.DepthFunc(DepthFunction.Lequal); + + if (renderer.OptionWireFrame) + { + GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line); + } + + List alphaFaces; + lock (renderer.Scene.VisibleObjects.LockObject) + { + // 2. Render Opaque Faces with Batching + int opaqueCount = renderer.Scene.VisibleObjects.OpaqueFaces.Count; + for (int i = 0; i < opaqueCount; i++) + { + FaceState firstFace = renderer.Scene.VisibleObjects.OpaqueFaces[i]; + int batchCount = firstFace.Face.Vertices.Length; + int j = i + 1; + + // Try to batch contiguous faces from the same object and material + while (j < opaqueCount) + { + FaceState nextFace = renderer.Scene.VisibleObjects.OpaqueFaces[j]; + if (nextFace.Object == firstFace.Object && nextFace.Face.Material == firstFace.Face.Material) + { + // Check if they are contiguous in the index buffer + if (nextFace.Face.IboStartIndex == firstFace.Face.IboStartIndex + batchCount) + { + batchCount += nextFace.Face.Vertices.Length; + j++; + continue; + } + } + break; + } + + if (j > i + 1) + { + // Draw the batch + renderer.RenderFace(renderer.DefaultShader, firstFace.Object, firstFace.Face, vertexCount: batchCount); + i = j - 1; + } + else + { + firstFace.Draw(); + } + } + alphaFaces = renderer.Scene.VisibleObjects.GetSortedPolygons(); + } + + + // 3. Render Alpha Faces + renderer.ResetOpenGlState(); + GL.DepthFunc(DepthFunction.Lequal); + + if (renderer.currentOptions.TransparencyMode == TransparencyMode.Performance) + { + renderer.SetBlendFunc(); + renderer.SetAlphaFunc(AlphaFunction.Greater, 0.0f); + renderer.SetDepthMask(false); + + foreach (FaceState face in alphaFaces) + { + face.Draw(); + } + } + else + { + // Quality Transparency Mode + renderer.UnsetBlendFunc(); + renderer.SetAlphaFunc(AlphaFunction.Equal, 1.0f); + renderer.SetDepthMask(true); + + foreach (FaceState face in alphaFaces) + { + var material = face.Object.Prototype.Mesh.Materials[face.Face.Material]; + if (material.BlendMode == MeshMaterialBlendMode.Normal && material.GlowAttenuationData == 0) + { + if (material.Color.A == 255) + { + face.Draw(); + } + } + } + + renderer.SetBlendFunc(); + renderer.SetAlphaFunc(AlphaFunction.Less, 1.0f); + renderer.SetDepthMask(false); + bool additive = false; + + foreach (FaceState face in alphaFaces) + { + var material = face.Object.Prototype.Mesh.Materials[face.Face.Material]; + if (material.BlendMode == MeshMaterialBlendMode.Additive) + { + if (!additive) + { + renderer.UnsetAlphaFunc(); + additive = true; + } + } + else + { + if (additive) + { + renderer.SetAlphaFunc(); + additive = false; + } + } + face.Draw(); + } + } + + // Restore default depth mask + renderer.SetDepthMask(true); + + if (renderer.OptionWireFrame) + { + GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); + } + } + } +} diff --git a/source/LibRender2/Passes/OverlayPass.cs b/source/LibRender2/Passes/OverlayPass.cs new file mode 100644 index 0000000000..52ecf67cb3 --- /dev/null +++ b/source/LibRender2/Passes/OverlayPass.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using LibRender2.Cameras; +using LibRender2.Objects; +using LibRender2.Pipeline; +using LibRender2.Viewports; +using OpenBveApi.Colors; +using OpenBveApi.Graphics; +using OpenBveApi.Interface; +using OpenBveApi.Math; +using OpenBveApi.Objects; +using OpenTK.Graphics.OpenGL; + +namespace LibRender2.Passes +{ + /// + /// Renders the overlays, including the train cab (2D/3D) and the user interface. + /// + public class OverlayPass : IRenderPass + { + private readonly Action renderUiAction; + + /// + /// Initializes a new OverlayPass. + /// + /// A callback to render the application-specific UI. + public OverlayPass(Action renderUiAction) + { + this.renderUiAction = renderUiAction; + } + + public void Execute(RenderContext context) + { + BaseRenderer renderer = context.Renderer; + + // 1. Setup for Cab/Interior layer + renderer.Fog.Enabled = false; + renderer.UpdateViewport(ViewportChangeMode.ChangeToCab); + + if (renderer.AvailableNewRenderer) + { + renderer.DefaultShader.Activate(); + renderer.ResetShader(renderer.DefaultShader); + renderer.DefaultShader.SetCurrentProjectionMatrix(renderer.CurrentProjectionMatrix); + } + + renderer.CurrentViewMatrix = Matrix4D.LookAt(Vector3.Zero, new Vector3(context.Camera.AbsoluteDirection.X, context.Camera.AbsoluteDirection.Y, -context.Camera.AbsoluteDirection.Z), new Vector3(context.Camera.AbsoluteUp.X, context.Camera.AbsoluteUp.Y, -context.Camera.AbsoluteUp.Z)); + context.ViewMatrix = renderer.CurrentViewMatrix; + + if (renderer.AvailableNewRenderer) + { + renderer.DefaultShader.SetCurrentViewMatrix(renderer.CurrentViewMatrix); + } + + List overlayOpaqueFaces, overlayAlphaFaces; + lock (renderer.Scene.VisibleObjects.LockObject) + { + overlayOpaqueFaces = renderer.Scene.VisibleObjects.OverlayOpaqueFaces.ToList(); + overlayAlphaFaces = renderer.Scene.VisibleObjects.GetSortedPolygons(true); + } + + if (context.Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable || context.Camera.CurrentRestriction == CameraRestrictionMode.Restricted3D) + { + // 3D Cab Rendering + renderer.ResetOpenGlState(); + GL.Clear(ClearBufferMask.DepthBufferBit); + renderer.OptionLighting = true; + + Color24 prevOptionAmbientColor = renderer.Lighting.OptionAmbientColor; + Color24 prevOptionDiffuseColor = renderer.Lighting.OptionDiffuseColor; + renderer.Lighting.OptionAmbientColor = Color24.LightGrey; + renderer.Lighting.OptionDiffuseColor = Color24.LightGrey; + + if (renderer.AvailableNewRenderer) + { + renderer.DefaultShader.SetIsLight(true); + Vector3 lightPos = new Vector3(renderer.Lighting.OptionLightPosition.X, renderer.Lighting.OptionLightPosition.Y, -renderer.Lighting.OptionLightPosition.Z); + renderer.DefaultShader.SetLightPosition(lightPos); + renderer.DefaultShader.SetLightAmbient(renderer.Lighting.OptionAmbientColor); + renderer.DefaultShader.SetLightDiffuse(renderer.Lighting.OptionDiffuseColor); + renderer.DefaultShader.SetLightSpecular(renderer.Lighting.OptionSpecularColor); + renderer.DefaultShader.SetLightModel(renderer.Lighting.LightModel); + } + + // Render opaque faces + foreach (FaceState face in overlayOpaqueFaces) + { + face.Draw(); + } + + // Render alpha faces + renderer.ResetOpenGlState(); + if (renderer.currentOptions.TransparencyMode == TransparencyMode.Performance) + { + renderer.SetBlendFunc(); + renderer.SetAlphaFunc(AlphaFunction.Greater, 0.0f); + renderer.SetDepthMask(false); + + foreach (FaceState face in overlayAlphaFaces) + { + face.Draw(); + } + } + else + { + renderer.UnsetBlendFunc(); + renderer.SetAlphaFunc(AlphaFunction.Equal, 1.0f); + renderer.SetDepthMask(true); + + foreach (FaceState face in overlayAlphaFaces) + { + var material = face.Object.Prototype.Mesh.Materials[face.Face.Material]; + if (material.BlendMode == MeshMaterialBlendMode.Normal && material.GlowAttenuationData == 0) + { + if (material.Color.A == 255) + { + face.Draw(); + } + } + } + + renderer.SetBlendFunc(); + renderer.SetAlphaFunc(AlphaFunction.Less, 1.0f); + renderer.SetDepthMask(false); + bool additive = false; + + foreach (FaceState face in overlayAlphaFaces) + { + var material = face.Object.Prototype.Mesh.Materials[face.Face.Material]; + if (material.BlendMode == MeshMaterialBlendMode.Additive) + { + if (!additive) + { + renderer.UnsetAlphaFunc(); + additive = true; + } + } + else + { + if (additive) + { + renderer.SetAlphaFunc(); + additive = false; + } + } + face.Draw(); + } + } + + renderer.Lighting.OptionAmbientColor = prevOptionAmbientColor; + renderer.Lighting.OptionDiffuseColor = prevOptionDiffuseColor; + renderer.Lighting.Initialize(); + } + else + { + // 2D Cab Rendering + renderer.ResetOpenGlState(); + renderer.OptionLighting = false; + if (renderer.AvailableNewRenderer) + { + renderer.DefaultShader.SetIsLight(false); + } + + renderer.SetBlendFunc(); + renderer.UnsetAlphaFunc(); + renderer.SetDepthTest(true); + renderer.SetDepthMask(true); + + foreach (FaceState face in overlayOpaqueFaces) + { + face.Draw(); + } + + renderer.SetDepthTest(false); + renderer.SetDepthMask(false); + + foreach (FaceState face in overlayAlphaFaces) + { + face.Draw(); + } + } + + // 2. Render UI and other overlays + renderer.OptionLighting = false; + renderer.ResetOpenGlState(); + renderer.SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); + renderer.SetAlphaFunc(AlphaFunction.Greater, 0.0f); + renderer.SetDepthTest(false); + + if (renderer.AvailableNewRenderer) + { + renderer.CurrentViewMatrix = Matrix4D.Identity; + renderer.DefaultShader.SetCurrentViewMatrix(renderer.CurrentViewMatrix); + } + + renderUiAction?.Invoke(context); + } + } +} diff --git a/source/LibRender2/Passes/ShadowPass.cs b/source/LibRender2/Passes/ShadowPass.cs new file mode 100644 index 0000000000..88facf2223 --- /dev/null +++ b/source/LibRender2/Passes/ShadowPass.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using LibRender2.Objects; +using LibRender2.Pipeline; +using LibRender2.Shaders; +using OpenBveApi.Graphics; +using OpenBveApi.Interface; +using OpenBveApi.Math; +using OpenBveApi.Objects; +using OpenBveApi.Textures; +using OpenTK.Graphics.OpenGL; + +namespace LibRender2.Passes +{ + /// + /// Performs the Cascaded Shadow Map (CSM) shadow depth rendering pass. + /// + public class ShadowPass : IRenderPass + { + public void Execute(RenderContext context) + { + BaseRenderer renderer = context.Renderer; + + if (!renderer.ShadowsEnabled || renderer.CSMShadowMaps == null || renderer.CSMCaster == null || renderer.ShadowDepthShaderProgram == null) + return; + + // 1. Get light direction pointing FROM the sun TOWARD the scene + Vector3 lightDir = new Vector3( + -renderer.Lighting.OptionLightPosition.X, + -renderer.Lighting.OptionLightPosition.Y, + -renderer.Lighting.OptionLightPosition.Z + ); + + if (lightDir.IsNullVector()) + { + return; + } + + // 2. Update cascade matrices + renderer.CSMCaster.Resolution = renderer.CSMShadowMaps.Resolution; + if (renderer.currentOptions.ShadowDrawDistance == ShadowDistance.ViewingDistance) + { + renderer.CSMCaster.ShadowDistance = renderer.currentOptions.ViewingDistance; + } + else + { + renderer.CSMCaster.ShadowDistance = (double)renderer.currentOptions.ShadowDrawDistance; + } + renderer.CSMCaster.Update(lightDir, context.ViewMatrix, context.ProjectionMatrix, 0.1, context.Camera.VerticalViewingAngle, renderer.Screen.AspectRatio); + + // 3. Setup rendering state + renderer.CurrentShader?.Deactivate(); + renderer.ShadowDepthShaderProgram.Activate(); + + renderer.SetDepthTest(true, DepthFunction.Less); + renderer.SetCullFace(false); + renderer.SetDepthMask(true); + + renderer.ShadowDepthShaderProgram.SetTexture(0); // always use texture unit 0 + + for (int cascade = 0; cascade < renderer.CSMCaster.CascadeCount; cascade++) + { + renderer.CSMShadowMaps.BindCascadeForWriting(cascade); + GL.Clear(ClearBufferMask.DepthBufferBit); + renderer.ShadowDepthShaderProgram.SetLightSpaceMatrix(renderer.CSMCaster.LightSpaceMatrices[cascade]); + + lock (renderer.Scene.VisibilityUpdateLock) + { + int lastVAOHandle = -1; + foreach (var face in renderer.Scene.VisibleObjects.OpaqueFaces) + { + renderer.RenderFace(renderer.ShadowDepthShaderProgram, face.Object, face.Face); + } + + foreach (var face in renderer.Scene.VisibleObjects.AlphaFaces) + { + renderer.RenderFace(renderer.ShadowDepthShaderProgram, face.Object, face.Face); + } + + // Dynamic objects (trains) are rendered regardless of camera visibility to ensure they always cast shadows + foreach (var state in renderer.Scene.DynamicObjectStates) + { + foreach (var face in state.Prototype.Mesh.Faces) + { + renderer.RenderFace(renderer.ShadowDepthShaderProgram, state, face); + } + } + } + renderer.CSMShadowMaps.Unbind(); + } + + // 4. Restore viewport + renderer.SetViewport(0, 0, renderer.Screen.Width, renderer.Screen.Height); + + renderer.LastBoundTexture = null; + } + + private PrimitiveType GetPrimitiveType(FaceFlags flags) + { + switch (flags & FaceFlags.FaceTypeMask) + { + case FaceFlags.Triangles: return PrimitiveType.Triangles; + case FaceFlags.TriangleStrip: return PrimitiveType.TriangleStrip; + case FaceFlags.Quads: return PrimitiveType.Quads; + case FaceFlags.QuadStrip: return PrimitiveType.QuadStrip; + default: return PrimitiveType.Polygon; + } + } + } +} diff --git a/source/LibRender2/Passes/SkyPass.cs b/source/LibRender2/Passes/SkyPass.cs new file mode 100644 index 0000000000..0735f82e38 --- /dev/null +++ b/source/LibRender2/Passes/SkyPass.cs @@ -0,0 +1,39 @@ +using System; +using LibRender2.Pipeline; +using OpenTK.Graphics.OpenGL; + +namespace LibRender2.Passes +{ + /// + /// Renders the background/sky. + /// + public class SkyPass : IRenderPass + { + private readonly Action updateBackgroundAction; + + /// + /// Initializes a new SkyPass. + /// + /// A callback to update and render the background. + /// This is usually handled by the application-specific route data. + public SkyPass(Action updateBackgroundAction) + { + this.updateBackgroundAction = updateBackgroundAction; + } + + public void Execute(RenderContext context) + { + BaseRenderer renderer = context.Renderer; + + // Background must be rendered with depth testing and writing disabled, no shadow mapping + renderer.SetDepthTest(false); + renderer.SetDepthMask(false); + renderer.DefaultShader.SetShadowEnabled(false); + + updateBackgroundAction?.Invoke(context); + + renderer.SetDepthMask(true); + + } + } +} diff --git a/source/LibRender2/Pipeline/GraphicsDevice.cs b/source/LibRender2/Pipeline/GraphicsDevice.cs new file mode 100644 index 0000000000..b717ef3c79 --- /dev/null +++ b/source/LibRender2/Pipeline/GraphicsDevice.cs @@ -0,0 +1,114 @@ +using OpenTK.Graphics.OpenGL; + +namespace LibRender2.Pipeline +{ + /// + /// Manages and caches the OpenGL state to prevent redundant API calls. + /// + public class GraphicsDevice + { + private bool blendEnabled; + private BlendingFactor blendSrcFactor; + private BlendingFactor blendDestFactor; + + private bool depthTestEnabled = true; + private DepthFunction depthFunction = DepthFunction.Lequal; + private bool depthMask = true; + + private bool cullFaceEnabled = true; + private CullFaceMode cullFaceMode = CullFaceMode.Front; + + /// + /// Sets the blending state. + /// + public void SetBlend(bool enabled, BlendingFactor src = BlendingFactor.SrcAlpha, BlendingFactor dest = BlendingFactor.OneMinusSrcAlpha) + { + if (blendEnabled != enabled) + { + blendEnabled = enabled; + if (enabled) GL.Enable(EnableCap.Blend); + else GL.Disable(EnableCap.Blend); + } + + if (enabled && (blendSrcFactor != src || blendDestFactor != dest)) + { + blendSrcFactor = src; + blendDestFactor = dest; + GL.BlendFunc(src, dest); + } + } + + /// + /// Sets the depth test state. + /// + public void SetDepthTest(bool enabled, DepthFunction function = DepthFunction.Lequal) + { + if (depthTestEnabled != enabled) + { + depthTestEnabled = enabled; + if (enabled) GL.Enable(EnableCap.DepthTest); + else GL.Disable(EnableCap.DepthTest); + } + + if (enabled && depthFunction != function) + { + depthFunction = function; + GL.DepthFunc(function); + } + } + + /// + /// Sets whether depth writing is enabled. + /// + public void SetDepthMask(bool enabled) + { + if (depthMask != enabled) + { + depthMask = enabled; + GL.DepthMask(enabled); + } + } + + /// + /// Sets the face culling state. + /// + public void SetCullFace(bool enabled, CullFaceMode mode = CullFaceMode.Front) + { + if (cullFaceEnabled != enabled) + { + cullFaceEnabled = enabled; + if (enabled) GL.Enable(EnableCap.CullFace); + else GL.Disable(EnableCap.CullFace); + } + + if (enabled && cullFaceMode != mode) + { + cullFaceMode = mode; + GL.CullFace(mode); + } + } + + /// + /// Resets the cached state to match the current OpenGL defaults or a known state. + /// + public void Reset() + { + // Reset to OpenGL defaults + GL.Disable(EnableCap.Blend); + blendEnabled = false; + + GL.Enable(EnableCap.DepthTest); + depthTestEnabled = true; + GL.DepthFunc(DepthFunction.Less); + depthFunction = DepthFunction.Less; + + GL.DepthMask(true); + depthMask = true; + + GL.Enable(EnableCap.CullFace); + cullFaceEnabled = true; + GL.CullFace(CullFaceMode.Back); + cullFaceMode = CullFaceMode.Back; + } + } +} diff --git a/source/LibRender2/Pipeline/IRenderPass.cs b/source/LibRender2/Pipeline/IRenderPass.cs new file mode 100644 index 0000000000..96518fb55e --- /dev/null +++ b/source/LibRender2/Pipeline/IRenderPass.cs @@ -0,0 +1,14 @@ +namespace LibRender2.Pipeline +{ + /// + /// Represents a single stage in the rendering pipeline. + /// + public interface IRenderPass + { + /// + /// Executes the rendering logic for this pass. + /// + /// The current rendering context. + void Execute(RenderContext context); + } +} diff --git a/source/LibRender2/Pipeline/RenderContext.cs b/source/LibRender2/Pipeline/RenderContext.cs new file mode 100644 index 0000000000..b8eb47a415 --- /dev/null +++ b/source/LibRender2/Pipeline/RenderContext.cs @@ -0,0 +1,41 @@ +using OpenBveApi.Math; +using OpenBveApi.Objects; +using LibRender2.Cameras; +using LibRender2.Lightings; +using LibRender2.Passes; + +namespace LibRender2.Pipeline +{ + /// + /// Contains all necessary information for a single frame rendering pass. + /// + public class RenderContext + { + /// The current projection matrix. + public Matrix4D ProjectionMatrix; + + /// The current view matrix. + public Matrix4D ViewMatrix; + + /// The current camera properties. + public CameraProperties Camera; + + /// The current lighting properties. + public Lighting Lighting; + + /// The time elapsed since the last frame in seconds. + public double TimeElapsed; + + /// The real-time elapsed since the last frame in seconds. + public double RealTimeElapsed; + + /// The renderer instance. + public BaseRenderer Renderer; + + public RenderContext(BaseRenderer renderer, double timeElapsed) + { + Renderer = renderer; + TimeElapsed = timeElapsed; + } + } +} diff --git a/source/LibRender2/Pipeline/RenderPipeline.cs b/source/LibRender2/Pipeline/RenderPipeline.cs new file mode 100644 index 0000000000..0303de430c --- /dev/null +++ b/source/LibRender2/Pipeline/RenderPipeline.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; + +namespace LibRender2.Pipeline +{ + /// + /// Orchestrates the execution of multiple rendering passes. + /// + public class RenderPipeline + { + /// + /// The list of passes in this pipeline, executed in order. + /// + public readonly List Passes = new List(); + + /// + /// Executes all registered passes in the pipeline. + /// + /// The current rendering context. + public void Execute(RenderContext context) + { + foreach (var pass in Passes) + { + pass.Execute(context); + } + } + + /// + /// Adds a pass to the end of the pipeline. + /// + public void AddPass(IRenderPass pass) + { + Passes.Add(pass); + } + + /// + /// Clears all passes from the pipeline. + /// + public void Clear() + { + Passes.Clear(); + } + } +} diff --git a/source/LibRender2/Primitives/Rectangle.cs b/source/LibRender2/Primitives/Rectangle.cs index 93f0965064..fec60ae9c7 100644 --- a/source/LibRender2/Primitives/Rectangle.cs +++ b/source/LibRender2/Primitives/Rectangle.cs @@ -86,11 +86,11 @@ public void DrawAlpha(Texture texture, Vector2 point, Color128? color = null, Ve } renderer.UnsetBlendFunc(); renderer.SetAlphaFunc(AlphaFunction.Equal, 1.0f); - GL.DepthMask(true); + renderer.Device.SetDepthMask(true); Draw(texture, point, texture.Size, color, textureCoordinates); renderer.SetBlendFunc(); renderer.SetAlphaFunc(AlphaFunction.Less, 1.0f); - GL.DepthMask(false); + renderer.Device.SetDepthMask(false); Draw(texture, point, texture.Size, color, textureCoordinates); renderer.SetAlphaFunc(AlphaFunction.Equal, 1.0f); } diff --git a/source/LibRender2/Shaders/AbstractShader.cs b/source/LibRender2/Shaders/AbstractShader.cs index ade573d72d..99b8101532 100644 --- a/source/LibRender2/Shaders/AbstractShader.cs +++ b/source/LibRender2/Shaders/AbstractShader.cs @@ -1,4 +1,4 @@ -//Simplified BSD License (BSD-2-Clause) +//Simplified BSD License (BSD-2-Clause) // //Copyright (c) 2024, Christopher Lees, S520, Aditiya Afrizal, The OpenBVE Project // @@ -147,6 +147,7 @@ public virtual void Activate() GL.UseProgram(Handle); IsActive = true; Renderer.lastVAO = -1; + Renderer.lastObjectState = null; Renderer.CurrentShader = this; Renderer.RestoreAlphaFunc(); } diff --git a/source/LibRender2/Shaders/Shader.cs b/source/LibRender2/Shaders/Shader.cs index d2a97bd39f..71d0e4bd68 100644 --- a/source/LibRender2/Shaders/Shader.cs +++ b/source/LibRender2/Shaders/Shader.cs @@ -43,30 +43,43 @@ public class Shader : AbstractShader public readonly VertexLayout VertexLayout; public readonly UniformLayout UniformLayout; private readonly int uShadowEnabledLocation; - private readonly int uShadowStrengthLocation; - private readonly int uShadowCascadeCountLocation; - private readonly int uShadowMap0Location; - private readonly int uShadowMap1Location; - private readonly int uShadowMap2Location; - private readonly int uShadowMap3Location; - private readonly int uShadowSplit0Location; - private readonly int uShadowSplit1Location; - private readonly int uShadowSplit2Location; - private readonly int uShadowSplit3Location; - private readonly int uShadowBias0Location; - private readonly int uShadowBias1Location; - private readonly int uShadowBias2Location; - private readonly int uShadowBias3Location; - private readonly int uShadowNormalBias0Location; - private readonly int uShadowNormalBias1Location; - private readonly int uShadowNormalBias2Location; - private readonly int uShadowNormalBias3Location; private readonly int uLightSpaceMatrix0Location; private readonly int uLightSpaceMatrix1Location; private readonly int uLightSpaceMatrix2Location; - private readonly int uLightSpaceMatrix3Location; + private readonly int uShadowMap0Location; + private readonly int uShadowMap1Location; + private readonly int uShadowMap2Location; + private readonly int uCascadeFarDist0Location; + private readonly int uCascadeFarDist1Location; + private readonly int uCascadeFarDist2Location; + private readonly int uCascadeBias0Location; + private readonly int uCascadeBias1Location; + private readonly int uCascadeBias2Location; + private readonly int uShadowStrengthLocation; private readonly int uModelMatrixLocation; private readonly int uCurrentViewMatrixLocation; + private readonly int uLightSpaceMatrix3Location; + private readonly int uShadowMap3Location; + private readonly int uCascadeFarDist3Location; + private readonly int uCascadeBias3Location; + private readonly int uNormalBias0Location; + private readonly int uNormalBias1Location; + private readonly int uNormalBias2Location; + private readonly int uNormalBias3Location; + private readonly int uCascadeCountLocation; + + // Caches + private bool? lastIsLight; + private Vector3? lastLightPosition; + private Color24? lastLightAmbient; + private Color24? lastLightDiffuse; + private Color24? lastLightSpecular; + private float? lastOpacity; + private int? lastMaterialFlags; + private Vector2? lastAlphaTest; + private bool? lastIsNightTexture; + private float? lastNightBlendFactor; + private Matrix4[] matrixCache; /// @@ -79,30 +92,30 @@ public class Shader : AbstractShader public Shader(BaseRenderer Renderer, string vertexShaderName, string fragmentShaderName, bool isFromStream = false) : base(Renderer, vertexShaderName, fragmentShaderName, isFromStream, true) { uShadowEnabledLocation = GL.GetUniformLocation(Handle, "uShadowEnabled"); - uShadowStrengthLocation = GL.GetUniformLocation(Handle, "uShadowStrength"); - uShadowCascadeCountLocation = GL.GetUniformLocation(Handle, "uShadowCascadeCount"); - uShadowMap0Location = GL.GetUniformLocation(Handle, "uShadowMap0"); - uShadowMap1Location = GL.GetUniformLocation(Handle, "uShadowMap1"); - uShadowMap2Location = GL.GetUniformLocation(Handle, "uShadowMap2"); - uShadowMap3Location = GL.GetUniformLocation(Handle, "uShadowMap3"); - uShadowSplit0Location = GL.GetUniformLocation(Handle, "uShadowSplit0"); - uShadowSplit1Location = GL.GetUniformLocation(Handle, "uShadowSplit1"); - uShadowSplit2Location = GL.GetUniformLocation(Handle, "uShadowSplit2"); - uShadowSplit3Location = GL.GetUniformLocation(Handle, "uShadowSplit3"); - uShadowBias0Location = GL.GetUniformLocation(Handle, "uShadowBias0"); - uShadowBias1Location = GL.GetUniformLocation(Handle, "uShadowBias1"); - uShadowBias2Location = GL.GetUniformLocation(Handle, "uShadowBias2"); - uShadowBias3Location = GL.GetUniformLocation(Handle, "uShadowBias3"); - uShadowNormalBias0Location = GL.GetUniformLocation(Handle, "uShadowNormalBias0"); - uShadowNormalBias1Location = GL.GetUniformLocation(Handle, "uShadowNormalBias1"); - uShadowNormalBias2Location = GL.GetUniformLocation(Handle, "uShadowNormalBias2"); - uShadowNormalBias3Location = GL.GetUniformLocation(Handle, "uShadowNormalBias3"); uLightSpaceMatrix0Location = GL.GetUniformLocation(Handle, "uLightSpaceMatrix0"); uLightSpaceMatrix1Location = GL.GetUniformLocation(Handle, "uLightSpaceMatrix1"); uLightSpaceMatrix2Location = GL.GetUniformLocation(Handle, "uLightSpaceMatrix2"); - uLightSpaceMatrix3Location = GL.GetUniformLocation(Handle, "uLightSpaceMatrix3"); + uShadowMap0Location = GL.GetUniformLocation(Handle, "uShadowMap0"); + uShadowMap1Location = GL.GetUniformLocation(Handle, "uShadowMap1"); + uShadowMap2Location = GL.GetUniformLocation(Handle, "uShadowMap2"); + uCascadeFarDist0Location = GL.GetUniformLocation(Handle, "uCascadeFarDist0"); + uCascadeFarDist1Location = GL.GetUniformLocation(Handle, "uCascadeFarDist1"); + uCascadeFarDist2Location = GL.GetUniformLocation(Handle, "uCascadeFarDist2"); + uCascadeBias0Location = GL.GetUniformLocation(Handle, "uCascadeBias0"); + uCascadeBias1Location = GL.GetUniformLocation(Handle, "uCascadeBias1"); + uCascadeBias2Location = GL.GetUniformLocation(Handle, "uCascadeBias2"); + uShadowStrengthLocation = GL.GetUniformLocation(Handle, "uShadowStrength"); uModelMatrixLocation = GL.GetUniformLocation(Handle, "uModelMatrix"); uCurrentViewMatrixLocation = GL.GetUniformLocation(Handle, "uCurrentViewMatrix"); + uLightSpaceMatrix3Location = GL.GetUniformLocation(Handle, "uLightSpaceMatrix3"); + uShadowMap3Location = GL.GetUniformLocation(Handle, "uShadowMap3"); + uCascadeFarDist3Location = GL.GetUniformLocation(Handle, "uCascadeFarDist3"); + uCascadeBias3Location = GL.GetUniformLocation(Handle, "uCascadeBias3"); + uNormalBias0Location = GL.GetUniformLocation(Handle, "uNormalBias0"); + uNormalBias1Location = GL.GetUniformLocation(Handle, "uNormalBias1"); + uNormalBias2Location = GL.GetUniformLocation(Handle, "uNormalBias2"); + uNormalBias3Location = GL.GetUniformLocation(Handle, "uNormalBias3"); + uCascadeCountLocation = GL.GetUniformLocation(Handle, "uCascadeCount"); VertexLayout = GetVertexLayout(); UniformLayout = GetUniformLayout(); @@ -115,7 +128,7 @@ public Shader(BaseRenderer Renderer, string vertexShaderName, string fragmentSha GL.ProgramUniform1(Handle, uShadowMap3Location, 7); // Also ensure shadow is disabled by default GL.ProgramUniform1(Handle, uShadowEnabledLocation, 0); - GL.ProgramUniform1(Handle, uShadowCascadeCountLocation, 0); + GL.ProgramUniform1(Handle, uCascadeCountLocation, 0); GL.ProgramUniform1(Handle, uShadowStrengthLocation, 1.0f); } @@ -160,6 +173,9 @@ public UniformLayout GetUniformLayout() FogIsLinear = (short)GL.GetUniformLocation(Handle, "uFogIsLinear"), FogDensity = (short)GL.GetUniformLocation(Handle, "uFogDensity"), Texture = (short)GL.GetUniformLocation(Handle, "uTexture"), + NightTexture = (short)GL.GetUniformLocation(Handle, "uNightTexture"), + IsNightTexture = (short)GL.GetUniformLocation(Handle, "uIsNightTexture"), + NightBlendFactor = (short)GL.GetUniformLocation(Handle, "uNightBlendFactor"), Brightness = (short)GL.GetUniformLocation(Handle, "uBrightness"), Opacity = (short)GL.GetUniformLocation(Handle, "uOpacity"), ObjectIndex = (short)GL.GetUniformLocation(Handle, "uObjectIndex"), @@ -209,11 +225,15 @@ public void SetCurrentProjectionMatrix(Matrix4D ProjectionMatrix) public void SetCurrentAnimationMatricies(ObjectState objectState) { Renderer.lastObjectState = null; // clear the cached object state, as otherwise it might be stale - Matrix4[] matriciesToShader = new Matrix4[objectState.Matricies.Length]; + int count = objectState.Matricies.Length; + if (matrixCache == null || matrixCache.Length < count) + { + matrixCache = new Matrix4[count]; + } - for (int i = 0; i < objectState.Matricies.Length; i++) + for (int i = 0; i < count; i++) { - matriciesToShader[i] = ConvertToMatrix4(objectState.Matricies[i]); + matrixCache[i] = ConvertToMatrix4(objectState.Matricies[i]); } unsafe @@ -224,7 +244,7 @@ public void SetCurrentAnimationMatricies(ObjectState objectState) } GL.BindBuffer(BufferTarget.UniformBuffer, objectState.MatrixBufferIndex); - GL.BufferData(BufferTarget.UniformBuffer, sizeof(Matrix4) * matriciesToShader.Length, matriciesToShader, BufferUsageHint.StaticDraw); + GL.BufferData(BufferTarget.UniformBuffer, sizeof(Matrix4) * count, matrixCache, BufferUsageHint.StaticDraw); } } @@ -275,12 +295,20 @@ public void SetCurrentTextureMatrix(Matrix4D TextureMatrix) public void SetIsLight(bool IsLight) { - GL.ProgramUniform1(Handle, UniformLayout.IsLight, IsLight ? 1 : 0); + if (lastIsLight != IsLight) + { + GL.ProgramUniform1(Handle, UniformLayout.IsLight, IsLight ? 1 : 0); + lastIsLight = IsLight; + } } public void SetLightPosition(Vector3 LightPosition) { - GL.ProgramUniform3(Handle, UniformLayout.LightPosition, (float)LightPosition.X, (float)LightPosition.Y, (float)LightPosition.Z); + if (lastLightPosition != LightPosition) + { + GL.ProgramUniform3(Handle, UniformLayout.LightPosition, (float)LightPosition.X, (float)LightPosition.Y, (float)LightPosition.Z); + lastLightPosition = LightPosition; + } } public void SetLightAmbient(Color24 LightAmbient) @@ -331,7 +359,11 @@ public void SetMaterialShininess(float materialShininess) public void SetMaterialFlags(MaterialFlags Flags) { - GL.ProgramUniform1(Handle, UniformLayout.MaterialFlags, (int)Flags); + if (lastMaterialFlags != (int)Flags) + { + GL.ProgramUniform1(Handle, UniformLayout.MaterialFlags, (int)Flags); + lastMaterialFlags = (int)Flags; + } } public override void SetFog(bool enabled) @@ -369,6 +401,29 @@ public void SetTexture(int textureUnit) private float lastBrightness; + public void SetNightTexture(int textureUnit) + { + GL.ProgramUniform1(Handle, UniformLayout.NightTexture, textureUnit); + } + + public void SetIsNightTexture(bool enabled) + { + if (lastIsNightTexture != enabled) + { + GL.ProgramUniform1(Handle, UniformLayout.IsNightTexture, enabled ? 1 : 0); + lastIsNightTexture = enabled; + } + } + + public void SetNightBlendFactor(float factor) + { + if (lastNightBlendFactor != factor) + { + GL.ProgramUniform1(Handle, UniformLayout.NightBlendFactor, factor); + lastNightBlendFactor = factor; + } + } + public void SetBrightness(float brightness) { if(brightness == lastBrightness) @@ -381,7 +436,11 @@ public void SetBrightness(float brightness) public void SetOpacity(float opacity) { - GL.ProgramUniform1(Handle, UniformLayout.Opacity, opacity); + if (lastOpacity != opacity) + { + GL.ProgramUniform1(Handle, UniformLayout.Opacity, opacity); + lastOpacity = opacity; + } } public void SetObjectIndex(int objectIndex) @@ -416,8 +475,11 @@ public void SetAtlasLocation(Vector4 atlasLocation) public override void SetAlphaFunction(AlphaFunction alphaFunction, float alphaComparison) { - GL.ProgramUniform2(Handle, UniformLayout.AlphaFunction, (int)alphaFunction, alphaComparison); - + if (lastAlphaTest == null || lastAlphaTest.Value.X != (int)alphaFunction || lastAlphaTest.Value.Y != alphaComparison) + { + GL.ProgramUniform2(Handle, UniformLayout.AlphaFunction, (int)alphaFunction, alphaComparison); + lastAlphaTest = new Vector2((int)alphaFunction, alphaComparison); + } } public override void SetAlphaTest(bool enabled) @@ -462,15 +524,15 @@ public void SetCascadeShadowMapUnit(int cascade, int textureUnit) GL.ProgramUniform1(Handle, loc, textureUnit); } - public void SetShadowSplitDistance(int cascade, float distance) + public void SetCascadeFarDistance(int cascade, float distance) { int loc; switch (cascade) { - case 0: loc = uShadowSplit0Location; break; - case 1: loc = uShadowSplit1Location; break; - case 2: loc = uShadowSplit2Location; break; - case 3: loc = uShadowSplit3Location; break; + case 0: loc = uCascadeFarDist0Location; break; + case 1: loc = uCascadeFarDist1Location; break; + case 2: loc = uCascadeFarDist2Location; break; + case 3: loc = uCascadeFarDist3Location; break; default: return; } GL.ProgramUniform1(Handle, loc, distance); @@ -481,10 +543,10 @@ public void SetCascadeBias(int cascade, float bias) int loc; switch (cascade) { - case 0: loc = uShadowBias0Location; break; - case 1: loc = uShadowBias1Location; break; - case 2: loc = uShadowBias2Location; break; - case 3: loc = uShadowBias3Location; break; + case 0: loc = uCascadeBias0Location; break; + case 1: loc = uCascadeBias1Location; break; + case 2: loc = uCascadeBias2Location; break; + case 3: loc = uCascadeBias3Location; break; default: return; } GL.ProgramUniform1(Handle, loc, bias); @@ -495,18 +557,18 @@ public void SetNormalBias(int cascade, float bias) int loc; switch (cascade) { - case 0: loc = uShadowNormalBias0Location; break; - case 1: loc = uShadowNormalBias1Location; break; - case 2: loc = uShadowNormalBias2Location; break; - case 3: loc = uShadowNormalBias3Location; break; + case 0: loc = uNormalBias0Location; break; + case 1: loc = uNormalBias1Location; break; + case 2: loc = uNormalBias2Location; break; + case 3: loc = uNormalBias3Location; break; default: return; } GL.ProgramUniform1(Handle, loc, bias); } - public void SetShadowCascadeCount(int count) + public void SetCascadeCount(int count) { - GL.ProgramUniform1(Handle, uShadowCascadeCountLocation, count); + GL.ProgramUniform1(Handle, uCascadeCountLocation, count); } public void SetShadowStrength(float strength) diff --git a/source/LibRender2/Shaders/ShadowDepthShader.cs b/source/LibRender2/Shaders/ShadowDepthShader.cs index 1d2d853015..0c36694862 100644 --- a/source/LibRender2/Shaders/ShadowDepthShader.cs +++ b/source/LibRender2/Shaders/ShadowDepthShader.cs @@ -37,7 +37,13 @@ public class ShadowDepthShader : AbstractShader private readonly int uAlphaCutoff; private readonly int uMaterialAlpha; // Uniform location for material color alpha private readonly int uMaterialFlags; - private readonly int uTextureMatrix; + + // Cache + private bool? lastHasTexture; + private float? lastAlphaCutoff; + private float? lastMaterialAlpha; + private int? lastMaterialFlags; + private OpenTK.Matrix4[] matrixCache; public ShadowDepthShader(BaseRenderer Renderer, string vertexShaderName, string fragmentShaderName, bool isFromStream = false) : base(Renderer, vertexShaderName, fragmentShaderName, isFromStream, false) { @@ -57,7 +63,6 @@ public ShadowDepthShader(BaseRenderer Renderer, string vertexShaderName, string uAlphaCutoff = GL.GetUniformLocation(Handle, "uAlphaCutoff"); uMaterialAlpha = GL.GetUniformLocation(Handle, "uMaterialAlpha"); // Cache the material alpha location uMaterialFlags = GL.GetUniformLocation(Handle, "uMaterialFlags"); - uTextureMatrix = GL.GetUniformLocation(Handle, "uTextureMatrix"); } public void SetLightSpaceMatrix(OpenBveApi.Math.Matrix4D m) @@ -66,18 +71,6 @@ public void SetLightSpaceMatrix(OpenBveApi.Math.Matrix4D m) GL.UniformMatrix4(uLightSpaceMatrix, false, ref matrix); } - public void SetModelMatrix(OpenBveApi.Math.Matrix4D m) - { - OpenTK.Matrix4 matrix = ConvertToMatrix4(m); - GL.UniformMatrix4(uModelMatrix, false, ref matrix); - } - - public void SetTextureMatrix(OpenBveApi.Math.Matrix4D m) - { - OpenTK.Matrix4 matrix = ConvertToMatrix4(m); - GL.UniformMatrix4(uTextureMatrix, false, ref matrix); - } - public void SetTexture(int unit) { GL.Uniform1(uTexture, unit); @@ -85,33 +78,63 @@ public void SetTexture(int unit) public void SetHasTexture(bool hasTexture) { - GL.Uniform1(uHasTexture, hasTexture ? 1 : 0); + if (lastHasTexture != hasTexture) + { + GL.Uniform1(uHasTexture, hasTexture ? 1 : 0); + lastHasTexture = hasTexture; + } } public void SetAlphaCutoff(float cutoff) { - GL.Uniform1(uAlphaCutoff, cutoff); + if (lastAlphaCutoff != cutoff) + { + GL.Uniform1(uAlphaCutoff, cutoff); + lastAlphaCutoff = cutoff; + } } /// Sets the material color alpha (0.0–1.0) for semi-transparent shadow discard public void SetMaterialAlpha(float alpha) { - GL.Uniform1(uMaterialAlpha, alpha); + if (lastMaterialAlpha != alpha) + { + GL.Uniform1(uMaterialAlpha, alpha); + lastMaterialAlpha = alpha; + } } /// Sets the material flags for shadow discard public void SetMaterialFlags(MaterialFlags flags) { - GL.Uniform1(uMaterialFlags, (int)flags); + if (lastMaterialFlags != (int)flags) + { + GL.Uniform1(uMaterialFlags, (int)flags); + lastMaterialFlags = (int)flags; + } + } + + public void SetModelMatrix(OpenBveApi.Math.Matrix4D m) + { + OpenTK.Matrix4 matrix = ConvertToMatrix4(m); + GL.UniformMatrix4(uModelMatrix, false, ref matrix); } public void SetCurrentAnimationMatricies(OpenBveApi.Objects.ObjectState objectState) { - OpenTK.Matrix4[] matriciesToShader = new OpenTK.Matrix4[objectState.Matricies.Length]; + if (objectState.Matricies == null) + { + return; + } + int count = objectState.Matricies.Length; + if (matrixCache == null || matrixCache.Length < count) + { + matrixCache = new OpenTK.Matrix4[count]; + } - for (int i = 0; i < objectState.Matricies.Length; i++) + for (int i = 0; i < count; i++) { - matriciesToShader[i] = ConvertToMatrix4(objectState.Matricies[i]); + matrixCache[i] = ConvertToMatrix4(objectState.Matricies[i]); } unsafe @@ -122,7 +145,7 @@ public void SetCurrentAnimationMatricies(OpenBveApi.Objects.ObjectState objectSt } GL.BindBuffer(BufferTarget.UniformBuffer, objectState.MatrixBufferIndex); - GL.BufferData(BufferTarget.UniformBuffer, sizeof(OpenTK.Matrix4) * matriciesToShader.Length, matriciesToShader, BufferUsageHint.StaticDraw); + GL.BufferData(BufferTarget.UniformBuffer, sizeof(OpenTK.Matrix4) * count, matrixCache, BufferUsageHint.StaticDraw); } } diff --git a/source/LibRender2/Shadows/CascadedShadowCaster.cs b/source/LibRender2/Shadows/CascadedShadowCaster.cs index 6c9f63ecc7..32c527203c 100644 --- a/source/LibRender2/Shadows/CascadedShadowCaster.cs +++ b/source/LibRender2/Shadows/CascadedShadowCaster.cs @@ -1,7 +1,7 @@ using System; using OpenBveApi.Math; -namespace LibRender2.ShadowMapping +namespace LibRender2.Shadows { /// /// Computes per-cascade light-space matrices by fitting an orthographic @@ -29,12 +29,8 @@ public class CascadedShadowCaster public Matrix4D[] LightSpaceMatrices { get; private set; } /// Per-cascade split distances (view-space Z). - /// - /// Length = CascadeCount. Renamed from FarDistance to SplitDistance to better reflect - /// standard CSM/PSSM terminology. These values represent the frustum slice boundaries - /// in view-space Z where the shadow transition between cascades occurs. - /// - public float[] SplitDistances { get; private set; } + ///>Length = CascadeCount. Used in the fragment shader to pick cascade. + public float[] CascadeFarDistances { get; private set; } /// Per-cascade depth bias for shadow acne prevention. public float[] CascadeBiases { get; private set; } @@ -43,7 +39,7 @@ public CascadedShadowCaster(int cascadeCount = 3) { CascadeCount = cascadeCount; LightSpaceMatrices = new Matrix4D[cascadeCount]; - SplitDistances = new float[cascadeCount]; + CascadeFarDistances = new float[cascadeCount]; CascadeBiases = new float[cascadeCount]; for (int i = 0; i < cascadeCount; i++) @@ -141,7 +137,7 @@ public void Update(Vector3 lightDirection, Matrix4D cameraView, Matrix4D cameraP Matrix4D.CreateOrthographic(orthoSize * 2.0, orthoSize * 2.0, zNear, zFar, out Matrix4D lightProj); LightSpaceMatrices[i] = lightView * lightProj; - SplitDistances[i] = (float)splits[i + 1]; + CascadeFarDistances[i] = (float)splits[i + 1]; // Z-Bias: Convert physical texel size into a Depth Buffer fraction. // This ensures we push the depth exactly enough to cure acne, but no more. diff --git a/source/LibRender2/Shadows/CascadedShadowMap.cs b/source/LibRender2/Shadows/CascadedShadowMap.cs index 2f2f505e24..c0ae11b574 100644 --- a/source/LibRender2/Shadows/CascadedShadowMap.cs +++ b/source/LibRender2/Shadows/CascadedShadowMap.cs @@ -1,7 +1,7 @@ using System; using OpenTK.Graphics.OpenGL; -namespace LibRender2.ShadowMapping +namespace LibRender2.Shadows { /// /// Manages multiple depth-only FBOs for Cascaded Shadow Mapping. diff --git a/source/LibRender2/Shadows/FrustumUtils.cs b/source/LibRender2/Shadows/FrustumUtils.cs index ad345f414c..ec8b81590e 100644 --- a/source/LibRender2/Shadows/FrustumUtils.cs +++ b/source/LibRender2/Shadows/FrustumUtils.cs @@ -2,7 +2,7 @@ using System.Linq; using OpenBveApi.Math; -namespace LibRender2.ShadowMapping +namespace LibRender2.Shadows { /// /// Utility class for camera frustum calculations, specifically for Cascaded Shadow Mapping. diff --git a/source/LibRender2/Shadows/Shadows.cs b/source/LibRender2/Shadows/Shadows.cs deleted file mode 100644 index 9de935cee1..0000000000 --- a/source/LibRender2/Shadows/Shadows.cs +++ /dev/null @@ -1,330 +0,0 @@ -using System; -using System.Collections.Generic; -using LibRender2.Objects; -using LibRender2.Shaders; -using OpenBveApi.Graphics; -using OpenBveApi.Interface; -using OpenBveApi.Math; -using OpenBveApi.Objects; -using OpenBveApi.Textures; -using OpenTK.Graphics.OpenGL; - -namespace LibRender2.ShadowMapping -{ - /// - /// Manages the Cascaded Shadow Mapping (CSM) system, including depth passes and shader binding. - /// - public class Shadows - { - private readonly BaseRenderer renderer; - - /// The shadow map textures and framebuffers. - internal CascadedShadowMap Map; - /// The math engine for computing cascade frustums and matrices. - internal CascadedShadowCaster Caster; - /// The shader used for rendering the shadow depth pass. - internal ShadowDepthShader DepthShader; - - /// Whether shadows are currently active. - public bool Enabled; - /// The current darkness of the shadows (0.0 to 1.0). - public float Strength; - - public Shadows(BaseRenderer renderer) - { - this.renderer = renderer; - } - - /// - /// Initializes (or reinitializes) shadow mapping from current options. - /// - public void Initialize() - { - var opts = renderer.currentOptions; - - if (opts.ShadowResolution == ShadowMapResolution.Off) - { - Dispose(); - Enabled = false; - renderer.fileSystem.AppendToLogFile("[CSM] Shadows disabled by user setting."); - return; - } - - int resolution = Math.Max(1, (int)opts.ShadowResolution); - int cascadeCount = (int)opts.ShadowCascades; - double shadowDistance = opts.ShadowDrawDistance == ShadowDistance.ViewingDistance ? opts.ViewingDistance : (double)(int)opts.ShadowDrawDistance; - shadowDistance = Math.Max(1.0, shadowDistance); - Strength = (float)opts.ShadowStrength; - - try - { - if (Map == null) - { - Map = new CascadedShadowMap(cascadeCount, resolution); - } - else - { - Map.Resize(cascadeCount, resolution); - } - - if (Caster == null || cascadeCount != Caster.CascadeCount) - { - Caster = new CascadedShadowCaster(cascadeCount); - } - - Caster.ShadowDistance = shadowDistance; - Caster.Resolution = resolution; - Caster.SplitLambda = 0.75; - Caster.DepthMargin = 150.0; - - if (DepthShader == null) - { - DepthShader = new ShadowDepthShader(renderer, "shadow_depth", "shadow_depth", true); - } - - Enabled = true; - renderer.fileSystem.AppendToLogFile($"[CSM] Initialized: {cascadeCount} cascades, {resolution}×{resolution}, distance={shadowDistance}m, strength={Strength:P0}"); - } - catch (Exception ex) - { - renderer.fileSystem.AppendToLogFile($"[CSM] Init failed: {ex.Message}"); - Enabled = false; - GL.GetError(); - } - } - - /// - /// Performs the shadow depth pass for all cascades. - /// - public void RenderPass() - { - if (!Enabled || Map == null || Caster == null || DepthShader == null) - { - return; - } - - // 1. Get light direction pointing FROM the sun TOWARD the scene - // Sun position is in OpenBVE coordinates (X: right, Y: up, Z: backward) - // Light direction needs to be negated for some components to match the shadow math. - Vector3 lightDir = new Vector3( - -renderer.Lighting.OptionLightPosition.X, - -renderer.Lighting.OptionLightPosition.Y, - renderer.Lighting.OptionLightPosition.Z - ); - - if (lightDir.IsNullVector()) - { - return; - } - - // 2. Update cascade matrices - // NOTE: We pass renderer.CurrentViewMatrix here which reflects the camera's rotation. - // The Caster will use this to align the shadow frustums with the view direction. - Caster.Resolution = Map.Resolution; - if (renderer.currentOptions.ShadowDrawDistance == ShadowDistance.ViewingDistance) - { - Caster.ShadowDistance = renderer.currentOptions.ViewingDistance; - } - Caster.Update(lightDir, renderer.CurrentViewMatrix, renderer.CurrentProjectionMatrix, 0.1, renderer.Camera.VerticalViewingAngle, renderer.Screen.AspectRatio); - - // 3. Setup state for depth pass - renderer.CurrentShader?.Deactivate(); - DepthShader.Activate(); - GL.Enable(EnableCap.DepthTest); - GL.DepthFunc(DepthFunction.Less); - if (renderer.OptionBackFaceCulling) - { - GL.Enable(EnableCap.CullFace); - GL.CullFace(CullFaceMode.Front); // OpenBVE culls Front faces by default - } - else - { - GL.Disable(EnableCap.CullFace); - } - GL.DepthMask(true); - DepthShader.SetTexture(0); - - for (int i = 0; i < Caster.CascadeCount; i++) - { - Map.BindCascadeForWriting(i); - GL.Clear(ClearBufferMask.DepthBufferBit); - DepthShader.SetLightSpaceMatrix(Caster.LightSpaceMatrices[i]); - - lock (renderer.VisibleObjects.LockObject) - { - int lastVAO = -1; - /* - * Culling Per-Cascade: - * Distant objects don't need to be rendered into near-field high-res shadow maps. - * We use a safety margin (150m) to catch long shadows from tall objects. - */ - double maxDistance = renderer.currentOptions.ShadowFilterCascades ? Caster.SplitDistances[i] + 150.0 : double.MaxValue; - double maxDistanceSquared = maxDistance * maxDistance; - - RenderFacesFiltered(renderer.VisibleObjects.OpaqueFaces, ref lastVAO, maxDistanceSquared); - RenderFacesFiltered(renderer.VisibleObjects.AlphaFaces, ref lastVAO, maxDistanceSquared); - } - Map.Unbind(); - } - - // 4. Restore state - GL.DepthFunc(DepthFunction.Lequal); - GL.CullFace(CullFaceMode.Front); - GL.Viewport(0, 0, renderer.Screen.Width, renderer.Screen.Height); - // Shadow pass corrupts the GL texture state, so ensure we null it out - // so the next render pass re-binds what it needs. - renderer.LastBoundTexture = null; - } - - /// - /// Renders a collection of faces while minimizing VAO and texture state switches. - /// - /// The faces to render. - /// A reference to the last bound VAO handle, to avoid redundant binds. - private void RenderFaces(IEnumerable faces, ref int lastVAO) - { - RenderFacesFiltered(faces, ref lastVAO, double.MaxValue); - } - - /// - /// Renders a collection of faces filtered by distance to optimize per-cascade draw calls. - /// - /// The faces to render. - /// A reference to the last bound VAO handle, to avoid redundant binds. - /// The squared maximum distance from camera for an object to cast shadows into this cascade. - private void RenderFacesFiltered(IEnumerable faces, ref int lastVAO, double maxDistanceSquared) - { - Vector3 cameraPos = renderer.Camera.AbsolutePosition; - - foreach (var face in faces) - { - if (face.Object.Prototype.Mesh.VAO == null || face.Object.DisableShadowCasting) - { - continue; - } - - ObjectState state = face.Object; - - // Per-cascade distance culling - if (maxDistanceSquared < double.MaxValue) - { - double dx = state.WorldPosition.X - cameraPos.X; - double dy = state.WorldPosition.Y - cameraPos.Y; - double dz = state.WorldPosition.Z - cameraPos.Z; - - if (dx * dx + dy * dy + dz * dz > maxDistanceSquared) - { - continue; - } - } - - DepthShader.SetModelMatrix(state.ModelMatrix * renderer.Camera.TranslationMatrix); - DepthShader.SetTextureMatrix(state.TextureTranslation); - - var material = face.Object.Prototype.Mesh.Materials[face.Face.Material]; - if ((material.Flags & MaterialFlags.NoShadow) != 0 || material.BlendMode == MeshMaterialBlendMode.Additive) - { - continue; - } - if (material.DaytimeTexture != null && renderer.currentHost.LoadTexture(ref material.DaytimeTexture, (OpenGlTextureWrapMode)(material.WrapMode ?? OpenGlTextureWrapMode.ClampClamp))) - { - GL.ActiveTexture(TextureUnit.Texture0); - GL.BindTexture(TextureTarget.Texture2D, material.DaytimeTexture.OpenGlTextures[(int)(material.WrapMode ?? OpenGlTextureWrapMode.ClampClamp)].Name); - DepthShader.SetHasTexture(true); - } - else - { - DepthShader.SetHasTexture(false); - } - - DepthShader.SetAlphaCutoff(0.5f); - DepthShader.SetMaterialAlpha(material.Color.A / 255.0f); - DepthShader.SetMaterialFlags(material.Flags); - - if (state.Matricies != null && state.Matricies.Length > 0) - { - DepthShader.SetCurrentAnimationMatricies(state); - GL.BindBufferBase(BufferTarget.UniformBuffer, 0, state.MatrixBufferIndex); - } - - VertexArrayObject vao = (VertexArrayObject)face.Object.Prototype.Mesh.VAO; - if (vao.handle != lastVAO) - { - vao.Bind(); - lastVAO = vao.handle; - } - if (renderer.OptionBackFaceCulling) - { - if ((face.Face.Flags & FaceFlags.Face2Mask) != 0) - { - // Double-sided faces (Face2) must not be culled to ensure they cast shadows from both sides - GL.Disable(EnableCap.CullFace); - } - else - { - GL.Enable(EnableCap.CullFace); - } - } - PrimitiveType drawMode = renderer.GetPrimitiveType(face.Face.Flags); - vao.Draw(drawMode, face.Face.IboStartIndex, face.Face.Vertices.Length); - } - } - - /// - /// Binds shadow data to the main scene shader. - /// - public void Bind(Shader shader) - { - if (!Enabled || Map == null || Caster == null) - { - shader.SetShadowEnabled(false); - // To satisfy strict OpenGL drivers, always bind something to the shadow map units - // even if shadow is disabled, to avoid "sampler collision" errors. - for (int i = 0; i < 4; i++) - { - GL.ActiveTexture(TextureUnit.Texture4 + i); - GL.BindTexture(TextureTarget.Texture2D, renderer.nullDepthMap); - } - GL.ActiveTexture(TextureUnit.Texture0); - return; - } - - shader.Activate(); - shader.SetShadowEnabled(true); - shader.SetShadowStrength((float)renderer.currentOptions.ShadowStrength); - shader.SetCurrentViewMatrix(renderer.CurrentViewMatrix); - - Map.BindAllCascadesForReading(TextureUnit.Texture4); - - int cascadeCount = Caster.CascadeCount; - for (int i = 0; i < cascadeCount; i++) - { - shader.SetCascadeLightSpaceMatrix(i, Caster.LightSpaceMatrices[i]); - shader.SetCascadeShadowMapUnit(i, 4 + i); - // Split distance = the view-space Z where this cascade ends. - shader.SetShadowSplitDistance(i, (float)Caster.SplitDistances[i]); - shader.SetCascadeBias(i, Caster.CascadeBiases[i] + (float)renderer.currentOptions.ShadowBias); - shader.SetNormalBias(i, (float)renderer.currentOptions.ShadowNormalBias); - } - - for (int i = cascadeCount; i < 4; i++) - { - shader.SetShadowSplitDistance(i, 0.0f); - } - shader.SetShadowCascadeCount(cascadeCount); - } - - /// - /// Disposes shadow resources. - /// - public void Dispose() - { - Map?.Dispose(); - Map = null; - DepthShader?.Dispose(); - DepthShader = null; - Caster = null; - Enabled = false; - } - } -} diff --git a/source/LibRender2/Smoke/ParticleSource.cs b/source/LibRender2/Smoke/ParticleSource.cs index e4b36638a5..542b5f2841 100644 --- a/source/LibRender2/Smoke/ParticleSource.cs +++ b/source/LibRender2/Smoke/ParticleSource.cs @@ -1,4 +1,4 @@ -//Simplified BSD License (BSD-2-Clause) +//Simplified BSD License (BSD-2-Clause) // //Copyright (c) 2025, The OpenBVE Project // @@ -112,7 +112,7 @@ public ParticleSource(BaseRenderer renderer, AbstractCar car, Vector3 offset, do public void Update(double timeElapsed, bool currentlyVisible) { - if (!Renderer.AvailableNewRenderer || Controller == null) + if (!Renderer.AvailableNewRenderer) { return; } diff --git a/source/LibRender2/Text/OpenGlString.cs b/source/LibRender2/Text/OpenGlString.cs index 85af581ca3..e5a88d9e2c 100644 --- a/source/LibRender2/Text/OpenGlString.cs +++ b/source/LibRender2/Text/OpenGlString.cs @@ -117,6 +117,7 @@ public void Draw(OpenGlFont font, string text, Vector2 location, TextAlignment a private void DrawImmediate(string text, OpenGlFont font, double left, double top, Color128 color) { + renderer.PushBlendFunc(); /* * Render the string. * */ @@ -153,7 +154,7 @@ private void DrawImmediate(string text, OpenGlFont font, double left, double top /* * In the first pass, mask off the background with pure black. * */ - GL.BlendFunc(BlendingFactor.Zero, BlendingFactor.OneMinusSrcColor); + renderer.SetBlendFunc(BlendingFactor.Zero, BlendingFactor.OneMinusSrcColor); GL.Begin(PrimitiveType.Quads); GL.Color4(color.A, color.A, color.A, 1.0f); GL.TexCoord2(data.TextureCoordinates.X, data.TextureCoordinates.Y); @@ -169,7 +170,7 @@ private void DrawImmediate(string text, OpenGlFont font, double left, double top /* * In the second pass, add the character onto the background. * */ - GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); + renderer.SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); GL.Begin(PrimitiveType.Quads); GL.Color4(color.R, color.G, color.B, color.A); GL.TexCoord2(data.TextureCoordinates.X, data.TextureCoordinates.Y); @@ -186,7 +187,7 @@ private void DrawImmediate(string text, OpenGlFont font, double left, double top left += data.TypographicSize.X; } - renderer.RestoreBlendFunc(); + renderer.PopBlendFunc(); GL.Disable(EnableCap.Texture2D); GL.PopMatrix(); @@ -197,6 +198,7 @@ private void DrawImmediate(string text, OpenGlFont font, double left, double top private void DrawWithShader(string text, OpenGlFont font, double left, double top, Color128 color) { + renderer.PushBlendFunc(); Shader.Activate(); renderer.CurrentShader = Shader; Shader.SetCurrentProjectionMatrix(renderer.CurrentProjectionMatrix); @@ -215,7 +217,7 @@ private void DrawWithShader(string text, OpenGlFont font, double left, double to /* * In the first pass, mask off the background with pure black. */ - GL.BlendFunc(BlendingFactor.Zero, BlendingFactor.OneMinusSrcColor); + renderer.SetBlendFunc(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); @@ -226,13 +228,13 @@ private void DrawWithShader(string text, OpenGlFont font, double left, double to */ renderer.dummyVao.Bind(); GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 6); - GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); + renderer.SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.One); Shader.SetColor(color); GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 6); } left += data.TypographicSize.X; } - renderer.RestoreBlendFunc(); + renderer.PopBlendFunc(); } /// Renders a string to the screen. diff --git a/source/LibRender2/app.config b/source/LibRender2/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/LibRender2/app.config +++ b/source/LibRender2/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/LibRender2/openGL/IndexBufferObject.cs b/source/LibRender2/openGL/IndexBufferObject.cs index 0fde7825e1..288fe6bd0e 100644 --- a/source/LibRender2/openGL/IndexBufferObject.cs +++ b/source/LibRender2/openGL/IndexBufferObject.cs @@ -71,10 +71,7 @@ public void Dispose() return; } - lock (BaseRenderer.iboToDelete) - { - BaseRenderer.iboToDelete.Add(handle); - } + Managers.GpuResourceManager.QueueIboForDeletion(handle); } } diff --git a/source/LibRender2/openGL/ShaderLayout.cs b/source/LibRender2/openGL/ShaderLayout.cs index aab2176414..b2e2c6f864 100644 --- a/source/LibRender2/openGL/ShaderLayout.cs +++ b/source/LibRender2/openGL/ShaderLayout.cs @@ -152,6 +152,9 @@ public class UniformLayout /// The handle of "uTexture" within the shader /// public short Texture = -1; + public short NightTexture = -1; + public short IsNightTexture = -1; + public short NightBlendFactor = -1; /// /// The handle of "uBrightness" within the shader diff --git a/source/LibRender2/openGL/VertexArrayObject.cs b/source/LibRender2/openGL/VertexArrayObject.cs index 24784cc690..06cb1e83ae 100644 --- a/source/LibRender2/openGL/VertexArrayObject.cs +++ b/source/LibRender2/openGL/VertexArrayObject.cs @@ -153,10 +153,7 @@ public void Dispose() return; } - lock (BaseRenderer.vaoToDelete) - { - BaseRenderer.vaoToDelete.Add(handle); - } + Managers.GpuResourceManager.QueueVaoForDeletion(handle); } } diff --git a/source/LibRender2/openGL/VertexBufferObject.cs b/source/LibRender2/openGL/VertexBufferObject.cs index 490d085936..fe7e5225da 100644 --- a/source/LibRender2/openGL/VertexBufferObject.cs +++ b/source/LibRender2/openGL/VertexBufferObject.cs @@ -172,10 +172,7 @@ public void Dispose() return; } - lock (BaseRenderer.vboToDelete) - { - BaseRenderer.vboToDelete.Add(handle); - } + Managers.GpuResourceManager.QueueVboForDeletion(handle); } } } diff --git a/source/ObjectBender/app.config b/source/ObjectBender/app.config index 2c700c093c..0a9966da17 100644 --- a/source/ObjectBender/app.config +++ b/source/ObjectBender/app.config @@ -11,10 +11,6 @@ - - - - diff --git a/source/ObjectViewer/FunctionScripts.cs b/source/ObjectViewer/FunctionScripts.cs index 5861b646c0..3a76686052 100644 --- a/source/ObjectViewer/FunctionScripts.cs +++ b/source/ObjectViewer/FunctionScripts.cs @@ -265,17 +265,7 @@ internal static void ExecuteFunctionScript(FunctionScript Function, TrainBase Tr Function.Stack[s] = 1; } s++; break; - case Instructions.CameraCar: - if (IsPartOfTrain && Train != null) - { - Function.Stack[s] = Train.CameraCar; - } - else - { - Function.Stack[s] = 0.0; - } - s++; break; - // train + // train case Instructions.PlayerTrain: if (IsPartOfTrain && Train != null) { diff --git a/source/ObjectViewer/Graphics/NewRendererS.cs b/source/ObjectViewer/Graphics/NewRendererS.cs index 86d1b1b41f..d4a357167f 100644 --- a/source/ObjectViewer/Graphics/NewRendererS.cs +++ b/source/ObjectViewer/Graphics/NewRendererS.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Windows.Forms; using LibRender2; +using LibRender2.Pipeline; +using LibRender2.Passes; using LibRender2.Objects; using LibRender2.Primitives; using LibRender2.Screens; @@ -51,6 +53,16 @@ public override void Initialize() greenAxisVAO = new Cube(this, Color128.Green); blueAxisVAO = new Cube(this, Color128.Blue); } + + // Initialize Pipeline + Pipeline.Clear(); + Pipeline.AddPass(new ShadowPass()); + Pipeline.AddPass(new GeometryPass()); + Pipeline.AddPass(new OverlayPass(ctx => RenderOverlays(ctx.TimeElapsed))); + + // Set the viewing distance for ObjectViewer from options + Camera.ForwardViewingDistance = currentOptions.ViewingDistance; + Camera.BackwardViewingDistance = currentOptions.ViewingDistance; } internal string GetBackgroundColorName() @@ -144,111 +156,15 @@ internal void RenderScene(double timeElapsed) Cube.Draw(Vector3.Zero, Vector3.Forward, Vector3.Down, Vector3.Right, new Vector3(0.01, 0.01, 100.0), Camera.AbsolutePosition, null); } } - // opaque face - if (AvailableNewRenderer) - { - PerformCSMShadowPass(); - - //Setup the shader for rendering the scene - DefaultShader.Activate(); - BindCSMToDefaultShader(); - if (OptionLighting) - { - DefaultShader.SetIsLight(true); - DefaultShader.SetLightPosition(TransformedLightPosition); - DefaultShader.SetLightAmbient(Lighting.OptionAmbientColor); - DefaultShader.SetLightDiffuse(Lighting.OptionDiffuseColor); - DefaultShader.SetLightSpecular(Lighting.OptionSpecularColor); - DefaultShader.SetLightModel(Lighting.LightModel); - } - DefaultShader.SetTexture(0); - DefaultShader.SetCurrentProjectionMatrix(CurrentProjectionMatrix); - } - ResetOpenGlState(); - List opaqueFaces, alphaFaces; - lock (VisibleObjects.LockObject) - { - opaqueFaces = VisibleObjects.OpaqueFaces.ToList(); - alphaFaces = VisibleObjects.GetSortedPolygons(); - } - - foreach (FaceState face in opaqueFaces) - { - face.Draw(); - } - - // alpha face - ResetOpenGlState(); - - if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance) - { - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Greater, 0.0f); - GL.DepthMask(false); - - foreach (FaceState face in alphaFaces) - { - face.Draw(); - } - } - else - { - UnsetBlendFunc(); - SetAlphaFunc(AlphaFunction.Equal, 1.0f); - GL.DepthMask(true); - foreach (FaceState face in alphaFaces) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Normal && face.Object.Prototype.Mesh.Materials[face.Face.Material].GlowAttenuationData == 0) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].Color.A == 255) - { - face.Draw(); - } - } - } - - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Less, 1.0f); - GL.DepthMask(false); - bool additive = false; - - foreach (FaceState face in alphaFaces) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Additive) - { - if (!additive) - { - UnsetAlphaFunc(); - additive = true; - } - } - else - { - if (additive) - { - SetAlphaFunc(); - additive = false; - } - } - face.Draw(); - } - } + // Execute Pipeline + RenderContext context = new RenderContext(this, timeElapsed); + ExecutePipeline(context); if (AvailableNewRenderer) { - DefaultShader.Deactivate(); - lastVAO = -1; + CurrentShader.Deactivate(); } - - // render overlays - ResetOpenGlState(); - OptionLighting = false; - UnsetAlphaFunc(); - GL.Disable(EnableCap.DepthTest); - SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); //FIXME: Remove when text switches between two renderer types - RenderOverlays(timeElapsed); - OptionLighting = true; } private void RenderOverlays(double timeElapsed) @@ -267,7 +183,7 @@ private void RenderOverlays(double timeElapsed) { string[][] keys; int objectCount, animatedObjectsUsed, opaqueFaces, alphaFaces; - lock (VisibilityUpdateLock) + lock (Scene.VisibilityUpdateLock) { objectCount = VisibleObjects.Objects.Count; animatedObjectsUsed = ObjectManager.AnimatedWorldObjectsUsed; diff --git a/source/ObjectViewer/Options.cs b/source/ObjectViewer/Options.cs index 3162aed6b6..76188eba29 100644 --- a/source/ObjectViewer/Options.cs +++ b/source/ObjectViewer/Options.cs @@ -95,6 +95,7 @@ public override void Save(string fileName) Builder.AppendLine("shadownormalbias = " + ShadowNormalBias.ToString("0.00", Culture)); Builder.AppendLine("lightazimuth = " + LightAzimuth.ToString(Culture)); Builder.AppendLine("lightelevation = " + LightElevation.ToString(Culture)); + Builder.AppendLine("viewingDistance = " + ViewingDistance.ToString(Culture)); Builder.AppendLine(); Builder.AppendLine("[Parsers]"); Builder.AppendLine("xObject = " + CurrentXParser); @@ -125,7 +126,7 @@ internal static void LoadOptions() { Interface.CurrentOptions = new Options { - ViewingDistance = 1000, // fixed + ViewingDistance = 1000, CameraMoveLeft = Key.A, CameraMoveRight = Key.D, CameraMoveUp = Key.W, @@ -169,11 +170,14 @@ internal static void LoadOptions() block.TryGetValue(OptionsKey.NearClipBase, ref Interface.CurrentOptions.NearClipBase, NumberRange.Positive); // ensure viewing distance is greater than the near clipping plane to avoid rendering issues if (Interface.CurrentOptions.ViewingDistance <= Interface.CurrentOptions.NearClipBase) + { Interface.CurrentOptions.ViewingDistance = (int)Math.Ceiling(Interface.CurrentOptions.NearClipBase) + 1; } + block.GetValue(OptionsKey.IsUseNewRenderer, out Interface.CurrentOptions.IsUseNewRenderer); block.GetValue(OptionsKey.AutoReloadObjects, out Interface.CurrentOptions.AutoReloadObjects); + block.TryGetValue(OptionsKey.ViewingDistance, ref Interface.CurrentOptions.ViewingDistance, NumberRange.Positive); break; case OptionsSection.Quality: block.GetEnumValue(OptionsKey.Interpolation, out Interface.CurrentOptions.Interpolation); @@ -188,6 +192,7 @@ internal static void LoadOptions() block.TryGetValue(OptionsKey.ShadowNormalBias, ref Interface.CurrentOptions.ShadowNormalBias); block.TryGetValue(OptionsKey.LightAzimuth, ref Interface.CurrentOptions.LightAzimuth); block.TryGetValue(OptionsKey.LightElevation, ref Interface.CurrentOptions.LightElevation); + block.TryGetValue(OptionsKey.ViewingDistance, ref Interface.CurrentOptions.ViewingDistance, NumberRange.Positive); break; case OptionsSection.Parsers: block.GetEnumValue(OptionsKey.XObject, out Interface.CurrentOptions.CurrentXParser); diff --git a/source/ObjectViewer/ProgramS.cs b/source/ObjectViewer/ProgramS.cs index 9d53fb7cc9..81ba263a7c 100644 --- a/source/ObjectViewer/ProgramS.cs +++ b/source/ObjectViewer/ProgramS.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using System.Windows.Forms; using LibRender2.Menu; @@ -28,7 +27,7 @@ internal static class Program { internal static FileSystem FileSystem = null; // members - internal static HashSet Files = new HashSet(); + internal static List Files = new List(); /// Last time the objects were reloaded (UTC). internal static DateTime LastReloadTime = DateTime.UtcNow; /// The number of failed auto-reloads @@ -162,8 +161,7 @@ internal static void Main(string[] args) if (filesToLoad.Count != 0) { - Files.Clear(); - Files.UnionWith(filesToLoad); + Files = filesToLoad; } } @@ -257,10 +255,6 @@ internal static void MouseEvent(object sender, MouseButtonEventArgs e) internal static void DragFile(object sender, FileDropEventArgs e) { - if (Files.Contains(e.FileName)) - { - return; - } Files.Add(System.IO.Path.GetFullPath(e.FileName)); // reset LightingRelative = -1.0; @@ -345,13 +339,13 @@ internal static void RefreshObjects(bool autoReload = false) Renderer.Reset(); Game.Reset(); formTrain.Instance?.DisableUI(); - foreach (string currentFile in Files) - { + for (int i = 0; i < Files.Count; i++) + { try { - if(currentFile.EndsWith(".dat", StringComparison.InvariantCultureIgnoreCase) || currentFile.EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase) || currentFile.EndsWith(".cfg", StringComparison.InvariantCultureIgnoreCase) || currentFile.EndsWith(".con", StringComparison.InvariantCultureIgnoreCase)) + if(Files[i].EndsWith(".dat", StringComparison.InvariantCultureIgnoreCase) || Files[i].EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase) || Files[i].EndsWith(".cfg", StringComparison.InvariantCultureIgnoreCase) || Files[i].EndsWith(".con", StringComparison.InvariantCultureIgnoreCase)) { - string currentTrain = currentFile; + string currentTrain = Files[i]; if (currentTrain.EndsWith("extensions.cfg", StringComparison.InvariantCultureIgnoreCase)) { currentTrain = System.IO.Path.GetDirectoryName(currentTrain); @@ -393,12 +387,12 @@ internal static void RefreshObjects(bool autoReload = false) else { //As we now attempt to load the train as a whole, the most likely outcome is that the train.dat file is MIA - Interface.AddMessage(MessageType.Critical, false, "No plugin found capable of loading file " + currentFile + "."); + Interface.AddMessage(MessageType.Critical, false, "No plugin found capable of loading file " + Files[i] + "."); } } else { - if (CurrentHost.LoadObject(currentFile, Encoding.UTF8, out UnifiedObject o)) + if (CurrentHost.LoadObject(Files[i], Encoding.UTF8, out UnifiedObject o)) { o.CreateObject(Vector3.Zero, new ObjectCreationParameters()); } @@ -414,11 +408,11 @@ internal static void RefreshObjects(bool autoReload = false) { // failure counter must be above 5 Interface.CurrentOptions.AutoReloadObjects = false; - Interface.AddMessage(MessageType.Critical, false, "Stopped automatically re-loading objects due to the Unhandled error (" + ex.Message + ") encountered while processing the file " + currentFile + "."); + Interface.AddMessage(MessageType.Critical, false, "Stopped automatically re-loading objects due to the Unhandled error (" + ex.Message + ") encountered while processing the file " + Files[i] + "."); } else { - Interface.AddMessage(MessageType.Critical, false, "Unhandled error (" + ex.Message + ") encountered while processing the file " + currentFile + "."); + Interface.AddMessage(MessageType.Critical, false, "Unhandled error (" + ex.Message + ") encountered while processing the file " + Files[i] + "."); } } @@ -439,7 +433,7 @@ internal static void RefreshObjects(bool autoReload = false) formTrain.Instance?.EnableUI(); Renderer.InitializeVisibility(); - Renderer.UpdateViewingDistances(600); + Renderer.UpdateViewingDistances(Interface.CurrentOptions.ViewingDistance); Renderer.UpdateVisibility(true); ObjectManager.UpdateAnimatedWorldObjects(0.01, true); Program.TrainManager.UpdateTrainObjects(0.0, true); @@ -447,7 +441,7 @@ internal static void RefreshObjects(bool autoReload = false) if (Files.Count == 1) { - Renderer.GameWindow.Title = "Object Viewer - " + Path.GetFileName(Files.ToList()[0]); + Renderer.GameWindow.Title = "Object Viewer - " + Path.GetFileName(Files[0]); } else { @@ -466,18 +460,18 @@ internal static void CheckFileChanges(double timeElapsed) } reloadCheckTimer = 0.0; - foreach (string currentFile in Files) + for (int i = 0; i < Files.Count; i++) { - try + try { - DateTime time = System.IO.File.GetLastWriteTimeUtc(currentFile); + DateTime time = System.IO.File.GetLastWriteTimeUtc(Files[i]); if ((DateTime.Now - time).TotalSeconds < 5 || time.Year == 1601) { // file is held open for constant write (within last 5s) // file no longer exists (returns 01/01/1601) continue; } - if (System.IO.File.GetLastWriteTimeUtc(currentFile) > LastReloadTime) + if (System.IO.File.GetLastWriteTimeUtc(Files[i]) > LastReloadTime) { RefreshObjects(); return; @@ -628,7 +622,7 @@ internal static void KeyDown(object sender, KeyboardKeyEventArgs e) case Key.Delete: LightingRelative = -1.0; Game.Reset(); - Files.Clear(); + Files = new List(); LastReloadTime = DateTime.UtcNow; NearestTrain.UpdateSpecs(); Renderer.ApplyBackgroundColor(); diff --git a/source/ObjectViewer/app.config b/source/ObjectViewer/app.config index 8d045bfe8d..9706ed099b 100644 --- a/source/ObjectViewer/app.config +++ b/source/ObjectViewer/app.config @@ -16,10 +16,6 @@ - - - - diff --git a/source/ObjectViewer/formOptions.Designer.cs b/source/ObjectViewer/formOptions.Designer.cs index 9fa6e2a248..12030f03c1 100644 --- a/source/ObjectViewer/formOptions.Designer.cs +++ b/source/ObjectViewer/formOptions.Designer.cs @@ -53,6 +53,8 @@ private void InitializeComponent() this.labelInterpolationMode = new System.Windows.Forms.Label(); this.labelInterpolationSettings = new System.Windows.Forms.Label(); this.InterpolationMode = new System.Windows.Forms.ComboBox(); + this.labelViewingDistance = new System.Windows.Forms.Label(); + this.numericUpDownViewingDistance = new System.Windows.Forms.NumericUpDown(); this.tabPageKeys = new System.Windows.Forms.TabPage(); this.labelControls = new System.Windows.Forms.Label(); this.comboBoxBackwards = new System.Windows.Forms.ComboBox(); @@ -90,8 +92,6 @@ private void InitializeComponent() this.numericUpDownShadowBias = new System.Windows.Forms.NumericUpDown(); this.labelShadowNormalBias = new System.Windows.Forms.Label(); this.numericUpDownShadowNormalBias = new System.Windows.Forms.NumericUpDown(); - this.labelShadowFilterCascades = new System.Windows.Forms.Label(); - this.checkBoxShadowFilterCascades = new System.Windows.Forms.CheckBox(); this.tabControl1.SuspendLayout(); this.tabPageOptions.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.AntialiasingLevel)).BeginInit(); @@ -117,7 +117,7 @@ private void InitializeComponent() this.tabControl1.Location = new System.Drawing.Point(1, 12); this.tabControl1.Name = "tabControl1"; this.tabControl1.SelectedIndex = 0; - this.tabControl1.Size = new System.Drawing.Size(312, 411); + this.tabControl1.Size = new System.Drawing.Size(312, 381); this.tabControl1.TabIndex = 26; // // tabPageOptions @@ -147,11 +147,13 @@ private void InitializeComponent() this.tabPageOptions.Controls.Add(this.labelInterpolationMode); this.tabPageOptions.Controls.Add(this.labelInterpolationSettings); this.tabPageOptions.Controls.Add(this.InterpolationMode); + this.tabPageOptions.Controls.Add(this.labelViewingDistance); + this.tabPageOptions.Controls.Add(this.numericUpDownViewingDistance); this.tabPageOptions.AutoScroll = true; this.tabPageOptions.Location = new System.Drawing.Point(4, 22); this.tabPageOptions.Name = "tabPageOptions"; this.tabPageOptions.Padding = new System.Windows.Forms.Padding(3); - this.tabPageOptions.Size = new System.Drawing.Size(304, 385); + this.tabPageOptions.Size = new System.Drawing.Size(304, 355); this.tabPageOptions.TabIndex = 0; this.tabPageOptions.Text = "Options"; this.tabPageOptions.UseVisualStyleBackColor = true; @@ -419,6 +421,37 @@ private void InitializeComponent() this.labelInterpolationSettings.TabIndex = 27; this.labelInterpolationSettings.Text = "Interpolation Settings"; // + // labelViewingDistance + // + this.labelViewingDistance.AutoSize = true; + this.labelViewingDistance.Location = new System.Drawing.Point(6, 131); + this.labelViewingDistance.Name = "labelViewingDistance"; + this.labelViewingDistance.Size = new System.Drawing.Size(92, 13); + this.labelViewingDistance.TabIndex = 50; + this.labelViewingDistance.Text = "Viewing Distance:"; + // + // numericUpDownViewingDistance + // + this.numericUpDownViewingDistance.Location = new System.Drawing.Point(160, 131); + this.numericUpDownViewingDistance.Maximum = new decimal(new int[] { + 100000, + 0, + 0, + 0}); + this.numericUpDownViewingDistance.Minimum = new decimal(new int[] { + 10, + 0, + 0, + 0}); + this.numericUpDownViewingDistance.Name = "numericUpDownViewingDistance"; + this.numericUpDownViewingDistance.Size = new System.Drawing.Size(120, 20); + this.numericUpDownViewingDistance.TabIndex = 51; + this.numericUpDownViewingDistance.Value = new decimal(new int[] { + 600, + 0, + 0, + 0}); + // // InterpolationMode // this.InterpolationMode.FormattingEnabled = true; @@ -455,8 +488,6 @@ private void InitializeComponent() this.tabPageShadows.Controls.Add(this.numericUpDownShadowBias); this.tabPageShadows.Controls.Add(this.labelShadowNormalBias); this.tabPageShadows.Controls.Add(this.numericUpDownShadowNormalBias); - this.tabPageShadows.Controls.Add(this.checkBoxShadowFilterCascades); - this.tabPageShadows.Controls.Add(this.labelShadowFilterCascades); this.tabPageShadows.AutoScroll = true; this.tabPageShadows.Location = new System.Drawing.Point(4, 22); this.tabPageShadows.Name = "tabPageShadows"; @@ -561,7 +592,7 @@ private void InitializeComponent() // this.labelSunDirection.AutoSize = true; this.labelSunDirection.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.labelSunDirection.Location = new System.Drawing.Point(6, 220); + this.labelSunDirection.Location = new System.Drawing.Point(6, 190); this.labelSunDirection.Name = "labelSunDirection"; this.labelSunDirection.Size = new System.Drawing.Size(96, 15); this.labelSunDirection.TabIndex = 36; @@ -570,7 +601,7 @@ private void InitializeComponent() // labelSunAzimuth // this.labelSunAzimuth.AutoSize = true; - this.labelSunAzimuth.Location = new System.Drawing.Point(6, 242); + this.labelSunAzimuth.Location = new System.Drawing.Point(6, 212); this.labelSunAzimuth.Name = "labelSunAzimuth"; this.labelSunAzimuth.Size = new System.Drawing.Size(47, 13); this.labelSunAzimuth.TabIndex = 37; @@ -578,7 +609,7 @@ private void InitializeComponent() // // trackBarSunAzimuth // - this.trackBarSunAzimuth.Location = new System.Drawing.Point(6, 258); + this.trackBarSunAzimuth.Location = new System.Drawing.Point(6, 228); this.trackBarSunAzimuth.Maximum = 180; this.trackBarSunAzimuth.Minimum = -180; this.trackBarSunAzimuth.Name = "trackBarSunAzimuth"; @@ -591,7 +622,7 @@ private void InitializeComponent() // labelSunAzimuthValue // this.labelSunAzimuthValue.AutoSize = true; - this.labelSunAzimuthValue.Location = new System.Drawing.Point(247, 262); + this.labelSunAzimuthValue.Location = new System.Drawing.Point(247, 232); this.labelSunAzimuthValue.Name = "labelSunAzimuthValue"; this.labelSunAzimuthValue.Size = new System.Drawing.Size(31, 13); this.labelSunAzimuthValue.TabIndex = 39; @@ -600,7 +631,7 @@ private void InitializeComponent() // labelSunElevation // this.labelSunElevation.AutoSize = true; - this.labelSunElevation.Location = new System.Drawing.Point(6, 306); + this.labelSunElevation.Location = new System.Drawing.Point(6, 276); this.labelSunElevation.Name = "labelSunElevation"; this.labelSunElevation.Size = new System.Drawing.Size(54, 13); this.labelSunElevation.TabIndex = 40; @@ -608,7 +639,7 @@ private void InitializeComponent() // // trackBarSunElevation // - this.trackBarSunElevation.Location = new System.Drawing.Point(6, 322); + this.trackBarSunElevation.Location = new System.Drawing.Point(6, 292); this.trackBarSunElevation.Maximum = 90; this.trackBarSunElevation.Minimum = -90; this.trackBarSunElevation.Name = "trackBarSunElevation"; @@ -621,7 +652,7 @@ private void InitializeComponent() // labelSunElevationValue // this.labelSunElevationValue.AutoSize = true; - this.labelSunElevationValue.Location = new System.Drawing.Point(247, 326); + this.labelSunElevationValue.Location = new System.Drawing.Point(247, 296); this.labelSunElevationValue.Name = "labelSunElevationValue"; this.labelSunElevationValue.Size = new System.Drawing.Size(25, 13); this.labelSunElevationValue.TabIndex = 42; @@ -816,48 +847,29 @@ private void InitializeComponent() // // labelAutoReloadChanged // - this.labelAutoReloadChanged.AutoSize = true; - this.labelAutoReloadChanged.Location = new System.Drawing.Point(10, 353); + this.labelAutoReloadChanged.Location = new System.Drawing.Point(10, 324); this.labelAutoReloadChanged.Name = "labelAutoReloadChanged"; - this.labelAutoReloadChanged.Size = new System.Drawing.Size(201, 13); + this.labelAutoReloadChanged.Size = new System.Drawing.Size(131, 28); this.labelAutoReloadChanged.TabIndex = 48; this.labelAutoReloadChanged.Text = "Automatically Reload Changed Objects:"; // // checkBoxAutoReload // this.checkBoxAutoReload.AutoSize = true; - this.checkBoxAutoReload.Location = new System.Drawing.Point(265, 353); + this.checkBoxAutoReload.Location = new System.Drawing.Point(265, 328); this.checkBoxAutoReload.Name = "checkBoxAutoReload"; this.checkBoxAutoReload.Size = new System.Drawing.Size(15, 14); this.checkBoxAutoReload.TabIndex = 49; this.checkBoxAutoReload.UseVisualStyleBackColor = true; // - // labelShadowFilterCascades - // - this.labelShadowFilterCascades.AutoSize = true; - this.labelShadowFilterCascades.Location = new System.Drawing.Point(6, 190); - this.labelShadowFilterCascades.Name = "labelShadowFilterCascades"; - this.labelShadowFilterCascades.Size = new System.Drawing.Size(126, 13); - this.labelShadowFilterCascades.TabIndex = 54; - this.labelShadowFilterCascades.Text = "Per-cascade culling:"; - // - // checkBoxShadowFilterCascades - // - this.checkBoxShadowFilterCascades.AutoSize = true; - this.checkBoxShadowFilterCascades.Location = new System.Drawing.Point(160, 190); - this.checkBoxShadowFilterCascades.Name = "checkBoxShadowFilterCascades"; - this.checkBoxShadowFilterCascades.Size = new System.Drawing.Size(15, 14); - this.checkBoxShadowFilterCascades.TabIndex = 55; - this.checkBoxShadowFilterCascades.UseVisualStyleBackColor = true; - // // formOptions // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(311, 464); + this.ClientSize = new System.Drawing.Size(311, 434); this.Controls.Add(this.CloseButton); this.Controls.Add(this.tabControl1); - this.MinimumSize = new System.Drawing.Size(330, 500); + this.MinimumSize = new System.Drawing.Size(330, 400); this.Name = "formOptions"; this.ShowIcon = false; this.ShowInTaskbar = false; @@ -876,6 +888,7 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)(this.trackBarSunElevation)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownShadowBias)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.numericUpDownShadowNormalBias)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownViewingDistance)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.nearClip)).EndInit(); this.tabPageKeys.ResumeLayout(false); this.tabPageKeys.PerformLayout(); @@ -947,7 +960,7 @@ private void InitializeComponent() private System.Windows.Forms.NumericUpDown numericUpDownShadowNormalBias; private System.Windows.Forms.CheckBox checkBoxAutoReload; private System.Windows.Forms.Label labelAutoReloadChanged; - private System.Windows.Forms.Label labelShadowFilterCascades; - private System.Windows.Forms.CheckBox checkBoxShadowFilterCascades; + private System.Windows.Forms.Label labelViewingDistance; + private System.Windows.Forms.NumericUpDown numericUpDownViewingDistance; } } diff --git a/source/ObjectViewer/formOptions.cs b/source/ObjectViewer/formOptions.cs index aa93a17b65..12cf364034 100644 --- a/source/ObjectViewer/formOptions.cs +++ b/source/ObjectViewer/formOptions.cs @@ -64,6 +64,11 @@ private formOptions() numericUpDownShadowStrength.Value = (decimal)(Interface.CurrentOptions.ShadowStrength * 100.0); numericUpDownShadowBias.Value = (decimal)Interface.CurrentOptions.ShadowBias; numericUpDownShadowNormalBias.Value = (decimal)Interface.CurrentOptions.ShadowNormalBias; + numericUpDownViewingDistance.Value = (decimal)Interface.CurrentOptions.ViewingDistance; + if (numericUpDownShadowBias.Value == 0) + { + numericUpDownShadowBias.Value = 0.000050m; + } // Initialize sun direction sliders from current light position @@ -86,13 +91,12 @@ private formOptions() comboBoxBackwards.DataSource = Enum.GetValues(typeof(Key)); comboBoxBackwards.SelectedItem = Interface.CurrentOptions.CameraMoveBackward; checkBoxAutoReload.Checked = Interface.CurrentOptions.AutoReloadObjects; - checkBoxShadowFilterCascades.Checked = Interface.CurrentOptions.ShadowFilterCascades; } private void InitializeSunSliders() { - trackBarSunElevation.Value = Math.Max(trackBarSunElevation.Minimum, Math.Min((int)Interface.CurrentOptions.LightElevation, trackBarSunElevation.Maximum)); - trackBarSunAzimuth.Value = Math.Max(trackBarSunAzimuth.Minimum, Math.Min((int)Interface.CurrentOptions.LightAzimuth, trackBarSunAzimuth.Maximum)); + trackBarSunElevation.Value = (int)Interface.CurrentOptions.LightElevation; + trackBarSunAzimuth.Value = (int)Interface.CurrentOptions.LightAzimuth; labelSunAzimuthValue.Text = trackBarSunAzimuth.Value + "\u00b0"; labelSunElevationValue.Text = trackBarSunElevation.Value + "\u00b0"; } @@ -110,7 +114,6 @@ private void UpdateShadowControlsEnabled() trackBarSunAzimuth.Enabled = enabled; trackBarSunElevation.Enabled = enabled; - checkBoxShadowFilterCascades.Enabled = enabled; } private void comboBoxShadowResolution_SelectedIndexChanged(object sender, EventArgs e) @@ -280,7 +283,7 @@ private void CloseButton_Click(object sender, EventArgs e) Interface.CurrentOptions.ShadowStrength = (double)numericUpDownShadowStrength.Value / 100.0; Interface.CurrentOptions.ShadowBias = (double)numericUpDownShadowBias.Value; Interface.CurrentOptions.ShadowNormalBias = (double)numericUpDownShadowNormalBias.Value; - Interface.CurrentOptions.ShadowFilterCascades = checkBoxShadowFilterCascades.Checked; + Interface.CurrentOptions.ViewingDistance = (int)numericUpDownViewingDistance.Value; Interface.CurrentOptions.Save(Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/options_ov.cfg")); Program.RefreshObjects(); diff --git a/source/OpenBVE/Audio/Sounds.Update.cs b/source/OpenBVE/Audio/Sounds.Update.cs index 894c8987be..ffe7a16c74 100644 --- a/source/OpenBVE/Audio/Sounds.Update.cs +++ b/source/OpenBVE/Audio/Sounds.Update.cs @@ -37,7 +37,7 @@ protected override void UpdateInverseModel(double timeElapsed) } AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); - float[] Orientation = {(float) listenerOrientation.Z.X, (float) listenerOrientation.Z.Y, (float) listenerOrientation.Z.Z,-(float) listenerOrientation.Y.X, -(float) listenerOrientation.Y.Y, -(float) listenerOrientation.Y.Z}; + var Orientation = new[]{(float) listenerOrientation.Z.X, (float) listenerOrientation.Z.Y, (float) listenerOrientation.Z.Z,-(float) listenerOrientation.Y.X, -(float) listenerOrientation.Y.Y, -(float) listenerOrientation.Y.Z}; AL.Listener(ALListenerfv.Orientation, ref Orientation); /* * Set up the atmospheric attributes. @@ -120,7 +120,7 @@ protected override void UpdateInverseModel(double timeElapsed) * */ if (Sources[i].State == SoundSourceState.Playing) { AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out int state); - if (state != (int)ALSourceState.Initial && state != (int)ALSourceState.Playing) { + if (state != (int)ALSourceState.Initial & state != (int)ALSourceState.Playing) { /* * The sound is not playing any longer. * Remove it from the list of sound sources. @@ -142,11 +142,11 @@ protected override void UpdateInverseModel(double timeElapsed) switch (Sources[i].Type) { case SoundType.TrainCar: - AbstractCar Car = (AbstractCar)Sources[i].Parent; + var Car = (AbstractCar)Sources[i].Parent; Car.CreateWorldCoordinates(Sources[i].Position, out position, out _); break; case SoundType.AnimatedObject: - WorldSound WorldSound = (WorldSound)Sources[i].Parent; + var WorldSound = (WorldSound)Sources[i].Parent; position = WorldSound.Follower.WorldPosition + WorldSound.Position; break; default: diff --git a/source/OpenBVE/Game/AI/AI.PreTrain.cs b/source/OpenBVE/Game/AI/AI.PreTrain.cs index 71e3c83055..f8dc5a8061 100644 --- a/source/OpenBVE/Game/AI/AI.PreTrain.cs +++ b/source/OpenBVE/Game/AI/AI.PreTrain.cs @@ -32,7 +32,7 @@ public override void Trigger(double timeElapsed) double bp = double.MinValue, bt = double.MinValue; for (int i = 0; i < Program.CurrentRoute.BogusPreTrainInstructions.Length; i++) { - if (Program.CurrentRoute.BogusPreTrainInstructions[i].Time < Program.CurrentRoute.SecondsSinceMidnight || at == double.MaxValue) + if (Program.CurrentRoute.BogusPreTrainInstructions[i].Time < Program.CurrentRoute.SecondsSinceMidnight | at == double.MaxValue) { at = Program.CurrentRoute.BogusPreTrainInstructions[i].Time; ap = Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition; @@ -40,13 +40,13 @@ public override void Trigger(double timeElapsed) } for (int i = Program.CurrentRoute.BogusPreTrainInstructions.Length - 1; i >= 0; i--) { - if (Program.CurrentRoute.BogusPreTrainInstructions[i].Time > at || bt == double.MinValue) + if (Program.CurrentRoute.BogusPreTrainInstructions[i].Time > at | bt == double.MinValue) { bt = Program.CurrentRoute.BogusPreTrainInstructions[i].Time; bp = Program.CurrentRoute.BogusPreTrainInstructions[i].TrackPosition; } } - if (at != double.MaxValue && bt != double.MinValue && Program.CurrentRoute.SecondsSinceMidnight <= Program.CurrentRoute.BogusPreTrainInstructions[Program.CurrentRoute.BogusPreTrainInstructions.Length - 1].Time) + if (at != double.MaxValue & bt != double.MinValue & Program.CurrentRoute.SecondsSinceMidnight <= Program.CurrentRoute.BogusPreTrainInstructions[Program.CurrentRoute.BogusPreTrainInstructions.Length - 1].Time) { double r = bt - at; if (r > 0.0) diff --git a/source/OpenBVE/Game/AI/AI.SimpleHuman.cs b/source/OpenBVE/Game/AI/AI.SimpleHuman.cs index 285cfd51ab..23849af9e1 100644 --- a/source/OpenBVE/Game/AI/AI.SimpleHuman.cs +++ b/source/OpenBVE/Game/AI/AI.SimpleHuman.cs @@ -822,14 +822,10 @@ private void LookAheadForwards() if (elim == 0.0) { double redSignalStopDistance; - if (Train.Station >= 0 & Train.StationState == TrainStopState.Completed) + if (Train.Station >= 0 & Train.StationState == TrainStopState.Completed & dist < 120.0) { - redSignalStopDistance = dist > 120 ? 35 : 25; - if (dist <= redSignalStopDistance) - { - // as otherwise the AI will get stuck - redSignalStopDistance = dist - 1; - } + dist = 1.0; + redSignalStopDistance = 25.0; } else if (Train.Station >= 0 & Train.StationState == TrainStopState.Pending | stopDistance < dist) { @@ -1141,14 +1137,10 @@ private void LookAheadBackwards() if (elim == 0.0) { double redSignalStopDistance; - if (Train.Station >= 0 & Train.StationState == TrainStopState.Completed) + if (Train.Station >= 0 & Train.StationState == TrainStopState.Completed & dist < 120.0) { - redSignalStopDistance = dist > 120 ? 35 : 25; - if (dist <= redSignalStopDistance) - { - // as otherwise the AI will get stuck - redSignalStopDistance = dist - 1; - } + dist = 1.0; + redSignalStopDistance = 25.0; } else if (Train.Station >= 0 & Train.StationState == TrainStopState.Pending | stopDistance < dist) { @@ -1390,7 +1382,7 @@ private void LookAheadBackwards() } } - /// The timer until the doors may be opened + /// The timer unti the doors may be opened private double doorWaitingTimer = 2.0; /// Controls the AI operating the wipers private double wiperTimer; diff --git a/source/OpenBVE/Game/Game.cs b/source/OpenBVE/Game/Game.cs index 51212cb446..1b10e7dd0f 100644 --- a/source/OpenBVE/Game/Game.cs +++ b/source/OpenBVE/Game/Game.cs @@ -93,50 +93,48 @@ internal static void UpdateBlackBox() { BlackBoxNextUpdate = Program.CurrentRoute.SecondsSinceMidnight + 1.0; } } - internal static void AddBlackBoxEntry() - { - if (!Interface.CurrentOptions.BlackBox) - { - return; - } - BlackBoxEntry newEntry = new BlackBoxEntry(); - newEntry.Time = Program.CurrentRoute.SecondsSinceMidnight; - newEntry.Position = TrainManager.PlayerTrain.Cars[0].TrackPosition; - newEntry.Speed = (float)TrainManager.PlayerTrain.CurrentSpeed; - newEntry.Acceleration = (float)TrainManager.PlayerTrain.Specs.CurrentAverageAcceleration; - newEntry.ReverserDriver = (short)TrainManager.PlayerTrain.Handles.Reverser.Driver; - newEntry.ReverserSafety = (short)TrainManager.PlayerTrain.Handles.Reverser.Actual; - newEntry.PowerDriver = (BlackBoxPower)TrainManager.PlayerTrain.Handles.Power.Driver; - newEntry.PowerSafety = (BlackBoxPower)TrainManager.PlayerTrain.Handles.Power.Safety; - if (TrainManager.PlayerTrain.Handles.EmergencyBrake.Driver) { - newEntry.BrakeDriver = BlackBoxBrake.Emergency; - } else if (TrainManager.PlayerTrain.Handles.HoldBrake.Driver) { - newEntry.BrakeDriver = BlackBoxBrake.HoldBrake; - } else if (TrainManager.PlayerTrain.Handles.Brake is AirBrakeHandle) { - switch ((AirBrakeHandleState)TrainManager.PlayerTrain.Handles.Brake.Driver) { - case AirBrakeHandleState.Release: newEntry.BrakeDriver = BlackBoxBrake.Release; break; - case AirBrakeHandleState.Lap: newEntry.BrakeDriver = BlackBoxBrake.Lap; break; - case AirBrakeHandleState.Service: newEntry.BrakeDriver = BlackBoxBrake.Service; break; - default: newEntry.BrakeDriver = BlackBoxBrake.Emergency; break; + internal static void AddBlackBoxEntry() { + if (Interface.CurrentOptions.BlackBox) { + + BlackBoxEntry newEntry = new BlackBoxEntry(); + newEntry.Time = Program.CurrentRoute.SecondsSinceMidnight; + newEntry.Position = TrainManager.PlayerTrain.Cars[0].TrackPosition; + newEntry.Speed = (float)TrainManager.PlayerTrain.CurrentSpeed; + newEntry.Acceleration = (float)TrainManager.PlayerTrain.Specs.CurrentAverageAcceleration; + newEntry.ReverserDriver = (short)TrainManager.PlayerTrain.Handles.Reverser.Driver; + newEntry.ReverserSafety = (short)TrainManager.PlayerTrain.Handles.Reverser.Actual; + newEntry.PowerDriver = (BlackBoxPower)TrainManager.PlayerTrain.Handles.Power.Driver; + newEntry.PowerSafety = (BlackBoxPower)TrainManager.PlayerTrain.Handles.Power.Safety; + if (TrainManager.PlayerTrain.Handles.EmergencyBrake.Driver) { + newEntry.BrakeDriver = BlackBoxBrake.Emergency; + } else if (TrainManager.PlayerTrain.Handles.HoldBrake.Driver) { + newEntry.BrakeDriver = BlackBoxBrake.HoldBrake; + } else if (TrainManager.PlayerTrain.Handles.Brake is AirBrakeHandle) { + switch ((AirBrakeHandleState)TrainManager.PlayerTrain.Handles.Brake.Driver) { + case AirBrakeHandleState.Release: newEntry.BrakeDriver = BlackBoxBrake.Release; break; + case AirBrakeHandleState.Lap: newEntry.BrakeDriver = BlackBoxBrake.Lap; break; + case AirBrakeHandleState.Service: newEntry.BrakeDriver = BlackBoxBrake.Service; break; + default: newEntry.BrakeDriver = BlackBoxBrake.Emergency; break; + } + } else { + newEntry.BrakeDriver = (BlackBoxBrake)TrainManager.PlayerTrain.Handles.Brake.Driver; } - } else { - newEntry.BrakeDriver = (BlackBoxBrake)TrainManager.PlayerTrain.Handles.Brake.Driver; - } - if (TrainManager.PlayerTrain.Handles.EmergencyBrake.Safety) { - newEntry.BrakeSafety = BlackBoxBrake.Emergency; - } else if (TrainManager.PlayerTrain.Handles.HoldBrake.Actual) { - newEntry.BrakeSafety = BlackBoxBrake.HoldBrake; - } else if (TrainManager.PlayerTrain.Handles.Brake is AirBrakeHandle) { - switch ((AirBrakeHandleState)TrainManager.PlayerTrain.Handles.Brake.Safety) { - case AirBrakeHandleState.Release: newEntry.BrakeSafety = BlackBoxBrake.Release; break; - case AirBrakeHandleState.Lap: newEntry.BrakeSafety = BlackBoxBrake.Lap; break; - case AirBrakeHandleState.Service: newEntry.BrakeSafety = BlackBoxBrake.Service; break; - default: newEntry.BrakeSafety = BlackBoxBrake.Emergency; break; + if (TrainManager.PlayerTrain.Handles.EmergencyBrake.Safety) { + newEntry.BrakeSafety = BlackBoxBrake.Emergency; + } else if (TrainManager.PlayerTrain.Handles.HoldBrake.Actual) { + newEntry.BrakeSafety = BlackBoxBrake.HoldBrake; + } else if (TrainManager.PlayerTrain.Handles.Brake is AirBrakeHandle) { + switch ((AirBrakeHandleState)TrainManager.PlayerTrain.Handles.Brake.Safety) { + case AirBrakeHandleState.Release: newEntry.BrakeSafety = BlackBoxBrake.Release; break; + case AirBrakeHandleState.Lap: newEntry.BrakeSafety = BlackBoxBrake.Lap; break; + case AirBrakeHandleState.Service: newEntry.BrakeSafety = BlackBoxBrake.Service; break; + default: newEntry.BrakeSafety = BlackBoxBrake.Emergency; break; + } + } else { + newEntry.BrakeSafety = (BlackBoxBrake)TrainManager.PlayerTrain.Handles.Brake.Safety; } - } else { - newEntry.BrakeSafety = (BlackBoxBrake)TrainManager.PlayerTrain.Handles.Brake.Safety; + BlackBoxEntries.Add(newEntry); } - BlackBoxEntries.Add(newEntry); } } } diff --git a/source/OpenBVE/Game/Menu/Menu.Route.cs b/source/OpenBVE/Game/Menu/Menu.Route.cs index 71bb4c68a9..6188e6f95e 100644 --- a/source/OpenBVE/Game/Menu/Menu.Route.cs +++ b/source/OpenBVE/Game/Menu/Menu.Route.cs @@ -184,7 +184,7 @@ private static void routeWorkerThread_doWork(object sender, DoWorkEventArgs e) { // ReSharper disable once RedundantCast object Route = (object)Program.CurrentRoute; // must cast to allow us to use the ref keyword correctly. - string RailwayFolder = Program.FileSystem.GetRailwayFolder(currentFile, System.Windows.Forms.Application.StartupPath); + string RailwayFolder = Loading.GetRailwayFolder(currentFile); string ObjectFolder = Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = Path.CombineDirectory(RailwayFolder, "Sound"); if (Program.CurrentHost.Plugins[i].Route.LoadRoute(currentFile, RouteEncoding, null, ObjectFolder, SoundFolder, true, ref Route)) diff --git a/source/OpenBVE/Game/Menu/Menu.SingleMenu.cs b/source/OpenBVE/Game/Menu/Menu.SingleMenu.cs index c627725e4c..4fad18b4d8 100644 --- a/source/OpenBVE/Game/Menu/Menu.SingleMenu.cs +++ b/source/OpenBVE/Game/Menu/Menu.SingleMenu.cs @@ -198,7 +198,7 @@ public SingleMenu(AbstractMenu menu, MenuType menuType, int data = 0, double Max } Items[totalEntries] = new MenuCommand(menu, fileName, MenuTag.File, 0); string ext = System.IO.Path.GetExtension(fileName); - if (!iconCache.TryGetValue(ext, out Items[totalEntries].Icon)) + if (!iconCache.ContainsKey(ext)) { // As some people have used arbitrary extensions for packages, let's show all files // Try and pull out the default icon from the cache for something a little nicer looking @@ -218,13 +218,17 @@ public SingleMenu(AbstractMenu menu, MenuType menuType, int data = 0, double Max } } + else + { + Items[totalEntries].Icon = iconCache[ext]; + } totalEntries++; } Array.Resize(ref Items, totalEntries); Align = TextAlignment.TopLeft; break; case MenuType.Options: - Items = new MenuEntry[12]; + Items = new MenuEntry[11]; Items[0] = new MenuCaption(menu, Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"panel","options"})); Items[1] = new MenuOption(menu, OptionType.ScreenResolution, Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","resolution"}), Program.Renderer.Screen.AvailableResolutions.ToArray()); Items[2] = new MenuOption(menu, OptionType.FullScreen, Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","display_mode_fullscreen"}), new[] { "true", "false" }); @@ -250,8 +254,7 @@ public SingleMenu(AbstractMenu menu, MenuType menuType, int data = 0, double Max Translations.GetInterfaceString(HostApplication.OpenBve, new[] { "options", "shadows_resolution_high" }), Translations.GetInterfaceString(HostApplication.OpenBve, new[] { "options", "shadows_resolution_ultra" }) }); - Items[10] = new MenuOption(menu, OptionType.ShadowFilterCascades, "Per-cascade culling", new[] { "true", "false" }); - Items[11] = new MenuCommand(menu, Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"menu","back"}), MenuTag.MenuBack, 0); + Items[10] = new MenuCommand(menu, Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"menu","back"}), MenuTag.MenuBack, 0); Align = TextAlignment.TopLeft; break; case MenuType.RouteList: diff --git a/source/OpenBVE/Game/Menu/Menu.cs b/source/OpenBVE/Game/Menu/Menu.cs index 86fe5527fa..c82aaee951 100644 --- a/source/OpenBVE/Game/Menu/Menu.cs +++ b/source/OpenBVE/Game/Menu/Menu.cs @@ -898,6 +898,8 @@ public override void Draw(double RealTimeElapsed) nextStepButton.Draw(); //Choose Train and Game start button + bool allowNextPhase = + nextStepButton.Enabled = (menu.Type == MenuType.RouteList && RoutefileState == RouteState.Processed) || (menu.Type == MenuType.TrainList && Interface.CurrentOptions.TrainFolder != string.Empty); break; } diff --git a/source/OpenBVE/Game/MessageManager.TextualMessages.cs b/source/OpenBVE/Game/MessageManager.TextualMessages.cs index 304e4dd6ed..08a983d12d 100644 --- a/source/OpenBVE/Game/MessageManager.TextualMessages.cs +++ b/source/OpenBVE/Game/MessageManager.TextualMessages.cs @@ -118,7 +118,7 @@ public override void Update(double timeElapsed) case MessageDependency.StationDeparture: { int j = TrainManager.PlayerTrain.Station; - if (j >= 0 && TrainManager.PlayerTrain.StationState != TrainStopState.Completed) + if (j >= 0 & TrainManager.PlayerTrain.StationState != TrainStopState.Completed) { double d = TrainManager.PlayerTrain.StationDepartureTime - Program.CurrentRoute.SecondsSinceMidnight + 1.0; if (d < 0.0) d = 0.0; @@ -158,7 +158,7 @@ public override void Update(double timeElapsed) } //Remove the message if it has completely faded out //NOTE: The fadeout is done in the renderer itself... - if (Timeout <= 0 && RendererAlpha == 0.0) + if (Timeout <= 0 & RendererAlpha == 0.0) { QueueForRemoval = true; } diff --git a/source/OpenBVE/Game/MessageManager.cs b/source/OpenBVE/Game/MessageManager.cs index e9e53c5393..1b1f804e12 100644 --- a/source/OpenBVE/Game/MessageManager.cs +++ b/source/OpenBVE/Game/MessageManager.cs @@ -71,7 +71,7 @@ internal static void AddMessage(AbstractMessage message) ImageMessages.Add(message); return; } - GameMessage m = message as GameMessage; + var m = message as GameMessage; if (m != null && m.Depencency == MessageDependency.PassedRedSignal) { for (int i = TextualMessages.Count -1; i >= 0; i--) diff --git a/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs b/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs index 3710a10d0f..c4f2d0f506 100644 --- a/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs +++ b/source/OpenBVE/Game/ObjectManager/AnimatedObjects/FunctionScripts.cs @@ -267,16 +267,6 @@ internal static void ExecuteFunctionScript(FunctionScript Function, TrainBase Tr Function.Stack[s] = 1; } s++; break; - case Instructions.CameraCar: - if (IsPartOfTrain && Train != null) - { - Function.Stack[s] = Train.CameraCar; - } - else - { - Function.Stack[s] = 0.0; - } - s++; break; // train case Instructions.PlayerTrain: if (IsPartOfTrain && Train != null) diff --git a/source/OpenBVE/Game/RouteInfoOverlay.cs b/source/OpenBVE/Game/RouteInfoOverlay.cs index b3c98246c8..ff49a81299 100644 --- a/source/OpenBVE/Game/RouteInfoOverlay.cs +++ b/source/OpenBVE/Game/RouteInfoOverlay.cs @@ -88,8 +88,9 @@ public void Show() case OverlayState.Gradient: Program.Renderer.Rectangle.Draw(gradientImage, origin, gradientSize); // get current train position in track + int trackPos = (int)(TrainManager.PlayerTrain.FrontCarTrackPosition); // convert to gradient profile offset - Pos.X = gradientSize.Y * (TrainManager.PlayerTrain.FrontCarTrackPosition - Program.CurrentRoute.Information.GradientMinTrack) / + Pos.X = gradientSize.Y * (trackPos - Program.CurrentRoute.Information.GradientMinTrack) / (Program.CurrentRoute.Information.GradientMaxTrack - Program.CurrentRoute.Information.GradientMinTrack); // draw a vertical bar at the current train position Program.Renderer.Rectangle.Draw(null, new Vector2(Pos.X, gradientSize.Y / 2), @@ -104,26 +105,21 @@ private void SetState(OverlayState newState) { switch (newState) { - case OverlayState.Map: - if (mapImage == null) - { - mapImage = new Texture(Program.CurrentRoute.Information.RouteMap); - mapSize = new Vector2(Program.CurrentRoute.Information.RouteMap.Width, - Program.CurrentRoute.Information.RouteMap.Height); - } - - break; - case OverlayState.Gradient: - if (gradientImage == null) - { - gradientImage = new Texture(Program.CurrentRoute.Information.GradientProfile); - gradientSize = new Vector2(Program.CurrentRoute.Information.GradientProfile.Width, - Program.CurrentRoute.Information.GradientProfile.Height); - } - - break; + case OverlayState.Map: + if (mapImage == null) + { + mapImage = new Texture(Program.CurrentRoute.Information.RouteMap); + mapSize = new Vector2(Program.CurrentRoute.Information.RouteMap.Width, Program.CurrentRoute.Information.RouteMap.Height); + } + break; + case OverlayState.Gradient: + if (gradientImage == null) + { + gradientImage = new Texture(Program.CurrentRoute.Information.GradientProfile); + gradientSize = new Vector2(Program.CurrentRoute.Information.GradientProfile.Width, Program.CurrentRoute.Information.GradientProfile.Height); + } + break; } - currentState = newState; } } diff --git a/source/OpenBVE/Game/Timetable.cs b/source/OpenBVE/Game/Timetable.cs index e147d8cf8c..0651fa364e 100644 --- a/source/OpenBVE/Game/Timetable.cs +++ b/source/OpenBVE/Game/Timetable.cs @@ -222,11 +222,11 @@ internal void RenderData(ref Texture timetableTexture) } // prepare timetable int w = 384, h = 192; - int xOffset = 0; - int actualHeight = h; - float descriptionWidth = 256; - float descriptionHeight = 16; - float stationNameWidth = 16; + int offsetx = 0; + int actualheight = h; + float descriptionwidth = 256; + float descriptionheight = 16; + float stationnamewidth = 16; for (int k = 0; k < 2; k++) { Bitmap b = new Bitmap(w, h); @@ -234,20 +234,20 @@ internal void RenderData(ref Texture timetableTexture) g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.Clear(Color.Transparent); - g.FillRectangle(Brushes.White, new RectangleF(xOffset, 0, w, actualHeight)); + g.FillRectangle(Brushes.White, new RectangleF(offsetx, 0, w, actualheight)); Font f = new Font(FontFamily.GenericSansSerif, 13.0f, GraphicsUnit.Pixel); Font fs = new Font(FontFamily.GenericSansSerif, 11.0f, GraphicsUnit.Pixel); Font fss = new Font(FontFamily.GenericSansSerif, 9.0f, GraphicsUnit.Pixel); // draw timetable string t; // description - float x0 = xOffset + 8; + float x0 = offsetx + 8; float y0 = 8; if (k == 1) { t = Program.CurrentRoute.Information.DefaultTimetableDescription; - g.DrawString(t, f, Brushes.Black, new RectangleF(x0, 6, descriptionWidth, descriptionHeight + 8)); - y0 += descriptionHeight + 2; + g.DrawString(t, f, Brushes.Black, new RectangleF(x0, 6, descriptionwidth, descriptionheight + 8)); + y0 += descriptionheight + 2; } // highest speed @@ -266,7 +266,7 @@ internal void RenderData(ref Texture timetableTexture) if (x > x1) x1 = x; } - g.DrawLine(Pens.LightGray, new PointF(x1 - 2, 4 + descriptionHeight), new PointF(x1 - 2, y0a + 18 * Tracks.Length - 1)); + g.DrawLine(Pens.LightGray, new PointF(x1 - 2, 4 + descriptionheight), new PointF(x1 - 2, y0a + 18 * Tracks.Length - 1)); // driving time t = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"timetable","drivingtime"}); s = g.MeasureString(t, fs); @@ -309,10 +309,10 @@ internal void RenderData(ref Texture timetableTexture) for (int i = 0; i < Tracks.Length; i++) { float y = y0a + 18 * i; - g.DrawLine(Pens.LightGray, new PointF(xOffset + 4, y - 1), new PointF(x2 - 2, y - 1)); + g.DrawLine(Pens.LightGray, new PointF(offsetx + 4, y - 1), new PointF(x2 - 2, y - 1)); } - g.DrawLine(Pens.LightGray, new PointF(x2 - 2, 4 + descriptionHeight), new PointF(x2 - 2, y0a + 18 * Tracks.Length - 1)); + g.DrawLine(Pens.LightGray, new PointF(x2 - 2, 4 + descriptionheight), new PointF(x2 - 2, y0a + 18 * Tracks.Length - 1)); // station name float y2 = y0; t = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"timetable","stationname"}); @@ -324,7 +324,7 @@ internal void RenderData(ref Texture timetableTexture) float y = y0 + 18 * (i + 1) + 2; g.DrawLine(Pens.LightGray, new PointF(x2 - 2, y - 1), new PointF(w - 4, y - 1)); t = Stations[i].Name; - if (Stations[i].NameJapanese && Stations[i].Name.Length > 1) + if (Stations[i].NameJapanese & Stations[i].Name.Length > 1) { float[] sizes = new float[t.Length]; float totalsize = 0.0f; @@ -334,7 +334,7 @@ internal void RenderData(ref Texture timetableTexture) totalsize += sizes[j]; } - float space = (stationNameWidth - totalsize) / (t.Length - 1); + float space = (stationnamewidth - totalsize) / (t.Length - 1); float x = 0.0f; for (int j = 0; j < t.Length; j++) { @@ -354,10 +354,10 @@ internal void RenderData(ref Texture timetableTexture) } } - g.DrawLine(Pens.LightGray, new PointF(x3 - 2, 4 + descriptionHeight), new PointF(x3 - 2, y0 + 18 * (Stations.Count + 1))); + g.DrawLine(Pens.LightGray, new PointF(x3 - 2, 4 + descriptionheight), new PointF(x3 - 2, y0 + 18 * (Stations.Count + 1))); if (k == 0) { - stationNameWidth = x3 - x2 - 6; + stationnamewidth = x3 - x2 - 6; } // arrival time @@ -393,7 +393,7 @@ internal void RenderData(ref Texture timetableTexture) s = g.MeasureString(t, fs); float x = x3 + s.Width; - if (Stations[i].Arrival.Minute.Length != 0 && Stations[i].Arrival.Second.Length != 0) + if (Stations[i].Arrival.Minute.Length != 0 & Stations[i].Arrival.Second.Length != 0) { t = Stations[i].Arrival.Minute + ":" + Stations[i].Arrival.Second; } @@ -406,7 +406,7 @@ internal void RenderData(ref Texture timetableTexture) } } - g.DrawLine(Pens.LightGray, new PointF(x4 - 2, 4 + descriptionHeight), new PointF(x4 - 2, y0 + 18 * (Stations.Count + 1))); + g.DrawLine(Pens.LightGray, new PointF(x4 - 2, 4 + descriptionheight), new PointF(x4 - 2, y0 + 18 * (Stations.Count + 1))); // departure time t = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"timetable","departuretime"}); s = g.MeasureString(t, f); @@ -429,20 +429,23 @@ internal void RenderData(ref Texture timetableTexture) } else { - t = "00"; if (Stations[i].Departure.Hour.Length != 0) { t = Stations[i].Departure.Hour; g.DrawString(t, fs, Brushes.Black, x4, y); } + else + { + t = "00"; + } + s = g.MeasureString(t, fs); float x = x4 + s.Width; - - t = ""; - if (Stations[i].Departure.Minute.Length != 0 && Stations[i].Departure.Second.Length != 0) + if (Stations[i].Departure.Minute.Length != 0 & Stations[i].Departure.Second.Length != 0) { t = Stations[i].Departure.Minute + ":" + Stations[i].Departure.Second; } + else t = ""; g.DrawString(t, f, Brushes.Black, x, y); s = g.MeasureString(t, f); @@ -460,10 +463,10 @@ internal void RenderData(ref Texture timetableTexture) // border if (k == 1) { - g.DrawLine(Pens.Black, new PointF(xOffset + 4, 4), new PointF(xOffset + 4, y0a + 18 * Tracks.Length - 1)); - g.DrawLine(Pens.Black, new PointF(xOffset + 4, y0a + 18 * Tracks.Length - 1), new PointF(x2 - 2, y0a + 18 * Tracks.Length - 1)); - g.DrawLine(Pens.Black, new PointF(xOffset + 4, 4), new PointF(w - 4, 4)); - g.DrawLine(Pens.Black, new PointF(xOffset + 4, 4 + descriptionHeight), new PointF(w - 4, 4 + descriptionHeight)); + g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4), new PointF(offsetx + 4, y0a + 18 * Tracks.Length - 1)); + g.DrawLine(Pens.Black, new PointF(offsetx + 4, y0a + 18 * Tracks.Length - 1), new PointF(x2 - 2, y0a + 18 * Tracks.Length - 1)); + g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4), new PointF(w - 4, 4)); + g.DrawLine(Pens.Black, new PointF(offsetx + 4, 4 + descriptionheight), new PointF(w - 4, 4 + descriptionheight)); g.DrawLine(Pens.Black, new PointF(x2 - 2, y0 + 18 * (Stations.Count + 1)), new PointF(w - 4, y0 + 18 * (Stations.Count + 1))); g.DrawLine(Pens.Black, new PointF(w - 4, 4), new PointF(w - 4, y0 + 18 * (Stations.Count + 1))); g.DrawLine(Pens.Black, new PointF(x2 - 2, y0a + 18 * Tracks.Length - 1), new PointF(x2 - 2, y0 + 18 * (Stations.Count + 1))); @@ -477,8 +480,8 @@ internal void RenderData(ref Texture timetableTexture) { t = Program.CurrentRoute.Information.DefaultTimetableDescription; s = g.MeasureString(t, f, w - 16); - descriptionWidth = s.Width; - descriptionHeight = s.Height + 2; + descriptionwidth = s.Width; + descriptionheight = s.Height + 2; h += (int) Math.Ceiling(s.Height) + 4; } f.Dispose(); @@ -489,9 +492,9 @@ internal void RenderData(ref Texture timetableTexture) { // measures int nw = Program.Renderer.TextureManager.RoundUpToPowerOfTwo(w); - xOffset = nw - w; + offsetx = nw - w; w = nw; - actualHeight = h; + actualheight = h; h = Program.Renderer.TextureManager.RoundUpToPowerOfTwo(h); } else @@ -540,7 +543,7 @@ internal static void UpdateCustomTimetable(Texture daytime, Texture nighttime) { if (nighttime != null) { CurrentCustomTimetableNighttimeTexture = nighttime; } - if (CurrentCustomTimetableDaytimeTexture != null || CurrentCustomTimetableNighttimeTexture != null) { + if (CurrentCustomTimetableDaytimeTexture != null | CurrentCustomTimetableNighttimeTexture != null) { CustomTimetableAvailable = true; } else { CustomTimetableAvailable = false; diff --git a/source/OpenBVE/Game/World.cs b/source/OpenBVE/Game/World.cs index 5262b6f291..373cf3e78c 100644 --- a/source/OpenBVE/Game/World.cs +++ b/source/OpenBVE/Game/World.cs @@ -244,7 +244,7 @@ internal static void UpdateAbsoluteCamera(double TimeElapsed = 0.0) { // cab pitch and yaw Vector3 d2 = new Vector3(dF); Vector3 u2 = new Vector3(uF); - if (TrainManager.PlayerTrain != null && (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead)) { + if ((Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { int c = TrainManager.PlayerTrain.DriverCar; if (c >= 0) { if (TrainManager.PlayerTrain.Cars[c].CarSections.ContainsKey(CarSectionType.Interior)) { @@ -259,12 +259,16 @@ internal static void UpdateAbsoluteCamera(double TimeElapsed = 0.0) { } // yaw, pitch, roll double headYaw = Program.Renderer.Camera.Alignment.Yaw + lookaheadYaw; - if (TrainManager.PlayerTrain != null && (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead)) { - headYaw += TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverYaw; + if ((Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { + if (TrainManager.PlayerTrain.DriverCar >= 0) { + headYaw += TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverYaw; + } } double headPitch = Program.Renderer.Camera.Alignment.Pitch + lookaheadPitch; if ((Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead) & TrainManager.PlayerTrain != null) { - headPitch += TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverPitch; + if (TrainManager.PlayerTrain.DriverCar >= 0) { + headPitch += TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].DriverPitch; + } } double headRoll = Program.Renderer.Camera.Alignment.Roll; @@ -354,16 +358,6 @@ internal static void UpdateAbsoluteCamera(double TimeElapsed = 0.0) { Program.Renderer.Camera.AbsoluteDirection = dF; Program.Renderer.Camera.AbsoluteUp = uF; Program.Renderer.Camera.AbsoluteSide = sF; - - if (Program.Renderer.Camera.ModeTransitionTimer < 1.0) - { - double transitionProgress = Program.Renderer.Camera.ModeTransitionTimer; - var start = Program.Renderer.Camera.ModeTransitionStart; - Program.Renderer.Camera.AbsolutePosition = Vector3.CosineInterpolate(start.Position, Program.Renderer.Camera.AbsolutePosition, transitionProgress); - Program.Renderer.Camera.AbsoluteDirection = Vector3.CosineInterpolate(start.Direction, Program.Renderer.Camera.AbsoluteDirection, transitionProgress); - Program.Renderer.Camera.AbsoluteUp = Vector3.CosineInterpolate(start.Up, Program.Renderer.Camera.AbsoluteUp, transitionProgress); - Program.Renderer.Camera.AbsoluteSide = Vector3.CosineInterpolate(start.Side, Program.Renderer.Camera.AbsoluteSide, transitionProgress); - } } } } diff --git a/source/OpenBVE/Graphics/NewRenderer.cs b/source/OpenBVE/Graphics/NewRenderer.cs index 7ae9dca3e5..a7e8551eea 100644 --- a/source/OpenBVE/Graphics/NewRenderer.cs +++ b/source/OpenBVE/Graphics/NewRenderer.cs @@ -5,6 +5,8 @@ using LibRender2.Screens; using LibRender2.Shaders; using LibRender2.Viewports; +using LibRender2.Pipeline; +using LibRender2.Passes; using OpenBve.Graphics.Renderers; using OpenBveApi; using OpenBveApi.Colors; @@ -16,6 +18,7 @@ using OpenBveApi.Objects; using OpenBveApi.Routes; using OpenBveApi.Textures; +using OpenBveApi.World; using OpenTK.Graphics.OpenGL; using System; using System.Collections.Generic; @@ -40,6 +43,10 @@ internal class NewRenderer : BaseRenderer private Overlays overlays; internal Touch Touch; + public NewRenderer(HostInterface currentHost, BaseOptions currentOptions, FileSystem fileSystem) : base(currentHost, currentOptions, fileSystem) + { + } + public override void Initialize() { base.Initialize(); @@ -66,13 +73,104 @@ public override void Initialize() events = new Events(this); overlays = new Overlays(this); Touch = new Touch(this); - ObjectsSortedByStart = new int[] { }; - ObjectsSortedByEnd = new int[] { }; - - + + // Initialize Pipeline + Pipeline.Clear(); + Pipeline.AddPass(new ShadowPass()); + Pipeline.AddPass(new SkyPass(UpdateBackground)); + Pipeline.AddPass(new GeometryPass()); + Pipeline.AddPass(new MotionBlurPass(this)); + Pipeline.AddPass(new OverlayPass(RenderUI)); + Pipeline.AddPass(new TouchPickingPass(this)); + Program.FileSystem.AppendToLogFile("Renderer initialised successfully."); } - + private void UpdateBackground(RenderContext context) + { + Program.CurrentRoute.UpdateBackground(context.TimeElapsed, Program.Renderer.CurrentInterface != InterfaceType.Normal); + + // fog setup + float aa = Program.CurrentRoute.CurrentFog.Start; + float bb = Program.CurrentRoute.CurrentFog.End; + + if (aa < bb & aa < Program.CurrentRoute.CurrentBackground.BackgroundImageDistance) + { + Fog.Enabled = true; + Fog.Start = aa; + Fog.End = bb; + Fog.Color = Program.CurrentRoute.CurrentFog.Color; + Fog.Density = Program.CurrentRoute.CurrentFog.Density; + Fog.IsLinear = Program.CurrentRoute.CurrentFog.IsLinear; + Fog.Set(); + } + else + { + Fog.Enabled = false; + } + } + + private void RenderUI(RenderContext context) + { + // particle sources + if (CurrentInterface != InterfaceType.GLMainMenu) + { + foreach (TrainBase train in Program.TrainManager.Trains) + { + train.UpdateParticleSources(context.TimeElapsed); + } + + foreach (ScriptedTrain scriptedTrain in Program.TrainManager.TFOs) + { + scriptedTrain.UpdateParticleSources(context.TimeElapsed); + } + } + + if (Interface.CurrentOptions.ShowEvents) + { + if (Math.Abs(CameraTrackFollower.TrackPosition - events.LastTrackPosition) > 50) + { + events.LastTrackPosition = CameraTrackFollower.TrackPosition; + CubesToDraw.Clear(); + for (int i = 0; i < Program.CurrentRoute.Tracks.Count; i++) + { + int railIndex = Program.CurrentRoute.Tracks.ElementAt(i).Key; + events.FindEvents(railIndex); + } + } + + for (int i = 0; i < CubesToDraw.Count; i++) + { + Texture T = CubesToDraw.ElementAt(i).Key; + foreach (Vector3 v in CubesToDraw[T]) + { + Cube.Draw(v, Camera.AbsoluteDirection, Camera.AbsoluteUp, Camera.AbsoluteSide, 0.2, Camera.AbsolutePosition, T); + } + } + } + + overlays.Render(context.RealTimeElapsed); + switch (CurrentInterface) + { + case InterfaceType.Menu: + case InterfaceType.GLMainMenu: + Game.Menu.Draw(context.TimeElapsed); + break; + case InterfaceType.SwitchChangeMap: + Game.SwitchChangeDialog.Draw(); + break; + } + } + + internal int CreateStaticObject(UnifiedObject Prototype, Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, ObjectDisposalMode AccurateObjectDisposal, ObjectCreationParameters Parameters, double BlockLength) + { + if (!(Prototype is StaticObject obj)) + { + Interface.AddMessage(MessageType.Error, false, "Attempted to use an animated object where only static objects are allowed."); + return -1; + } + return base.CreateStaticObject(obj, Position, WorldTransformation, LocalTransformation, AccurateObjectDisposal, Parameters, BlockLength); + } + protected override void UpdateViewport(int width, int height) { _programLogo = null; @@ -87,22 +185,29 @@ protected override void UpdateViewport(int width, int height) { case ViewportMode.Scenery: double cd = Program.CurrentRoute.CurrentBackground is BackgroundObject b ? Math.Max(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance, b.ClipDistance) : Program.CurrentRoute.CurrentBackground.BackgroundImageDistance; - double nearClipScenery = Math.Max(0.01, Interface.CurrentOptions.NearClipScenery); + cd = Math.Max(cd, currentOptions.ViewingDistance); + double nearClipScenery = Math.Max(0.01, currentOptions.NearClipScenery); CurrentProjectionMatrix = Matrix4D.CreatePerspectiveFieldOfView(Camera.VerticalViewingAngle, Screen.AspectRatio, nearClipScenery, cd); break; case ViewportMode.Cab: - double nearClipCab = Math.Max(0.01, Interface.CurrentOptions.NearClipCab); + double nearClipCab = Math.Max(0.01, currentOptions.NearClipCab); CurrentProjectionMatrix = Matrix4D.CreatePerspectiveFieldOfView(Camera.VerticalViewingAngle, Screen.AspectRatio, nearClipCab, 50.0); break; } Touch.UpdateViewport(); } - + internal void RenderScene(double TimeElapsed, double RealTimeElapsed) { + if (GameWindow != null && !Screen.Minimized) + { + Screen.Width = GameWindow.Width; + Screen.Height = GameWindow.Height; + } + UpdateViewport(ViewportChangeMode.ChangeToScenery); + UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); ReleaseResources(); - // initialize ResetOpenGlState(); if (OptionWireFrame) @@ -123,43 +228,34 @@ internal void RenderScene(double TimeElapsed, double RealTimeElapsed) } else { - GL.ClearColor(Interface.CurrentOptions.ClearColor.R * inv255, Interface.CurrentOptions.ClearColor.G * inv255, Interface.CurrentOptions.ClearColor.B * inv255, 1.0f); + GL.ClearColor(0.67f, 0.67f, 0.67f, 1.0f); } GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); - - // set up camera and lighting early for shadows - CurrentViewMatrix = Matrix4D.LookAt(Vector3.Zero, new Vector3(Camera.AbsoluteDirection.X, Camera.AbsoluteDirection.Y, -Camera.AbsoluteDirection.Z), new Vector3(Camera.AbsoluteUp.X, Camera.AbsoluteUp.Y, -Camera.AbsoluteUp.Z)); - TransformedLightPosition = new Vector3(Lighting.OptionLightPosition.X, Lighting.OptionLightPosition.Y, -Lighting.OptionLightPosition.Z); - TransformedLightPosition.Transform(CurrentViewMatrix); - - UpdateViewport(ViewportChangeMode.ChangeToScenery); - if (AvailableNewRenderer) + // Setup Render Context + RenderContext context = new RenderContext(this, TimeElapsed) { - PerformCSMShadowPass(); - } + RealTimeElapsed = RealTimeElapsed, + Camera = Camera, + ProjectionMatrix = CurrentProjectionMatrix + }; + + // Update View Matrix + CurrentViewMatrix = Matrix4D.LookAt(Vector3.Zero, new Vector3(Camera.AbsoluteDirection.X, Camera.AbsoluteDirection.Y, -Camera.AbsoluteDirection.Z), new Vector3(Camera.AbsoluteUp.X, Camera.AbsoluteUp.Y, -Camera.AbsoluteUp.Z)); + context.ViewMatrix = CurrentViewMatrix; if (Lighting.ShouldInitialize) { Lighting.Initialize(); Lighting.ShouldInitialize = false; } - - if (!AvailableNewRenderer) - { - GL.Light(LightName.Light0, LightParameter.Position, new[] { (float)TransformedLightPosition.X, (float)TransformedLightPosition.Y, (float)TransformedLightPosition.Z, 0.0f }); - } Lighting.OptionLightingResultingAmount = (Lighting.OptionAmbientColor.R + Lighting.OptionAmbientColor.G + Lighting.OptionAmbientColor.B) / 480.0f; + if (Lighting.OptionLightingResultingAmount > 1.0f) Lighting.OptionLightingResultingAmount = 1.0f; - if (Lighting.OptionLightingResultingAmount > 1.0f) - { - Lighting.OptionLightingResultingAmount = 1.0f; - } - // fog + // Fog track interpolation double fd = Program.CurrentRoute.NextFog.TrackPosition - Program.CurrentRoute.PreviousFog.TrackPosition; - if (fd != 0.0) { float fr = (float)((CameraTrackFollower.TrackPosition - Program.CurrentRoute.PreviousFog.TrackPosition) / fd); @@ -179,350 +275,53 @@ internal void RenderScene(double TimeElapsed, double RealTimeElapsed) Program.CurrentRoute.CurrentFog = Program.CurrentRoute.PreviousFog; } - if (AvailableNewRenderer) - { - DefaultShader.Activate(); - BindCSMToDefaultShader(); - } - - // render background - // n.b. must disable shadows - GL.Disable(EnableCap.DepthTest); - if (AvailableNewRenderer) - { - DefaultShader.SetShadowEnabled(false); - } - - Program.CurrentRoute.UpdateBackground(TimeElapsed, Program.Renderer.CurrentInterface != InterfaceType.Normal); - if (AvailableNewRenderer) - { - DefaultShader.SetShadowEnabled(ShadowsEnabled); - } - - - // fog - float aa = Program.CurrentRoute.CurrentFog.Start; - float bb = Program.CurrentRoute.CurrentFog.End; - - if (aa < bb & aa < Program.CurrentRoute.CurrentBackground.BackgroundImageDistance) - { - Fog.Enabled = true; - Fog.Start = aa; - Fog.End = bb; - Fog.Color = Program.CurrentRoute.CurrentFog.Color; - Fog.Density = Program.CurrentRoute.CurrentFog.Density; - Fog.IsLinear = Program.CurrentRoute.CurrentFog.IsLinear; - Fog.Set(); - } - else - { - Fog.Enabled = false; - } - - // world layer - // opaque face - if (AvailableNewRenderer) - { - //Setup the shader for rendering the scene - if (OptionLighting) - { - DefaultShader.SetIsLight(true); - DefaultShader.SetLightPosition(TransformedLightPosition); - DefaultShader.SetLightAmbient(Lighting.OptionAmbientColor); - DefaultShader.SetLightDiffuse(Lighting.OptionDiffuseColor); - DefaultShader.SetLightSpecular(Lighting.OptionSpecularColor); - DefaultShader.SetLightModel(Lighting.LightModel); - } - Fog.Set(); - DefaultShader.SetTexture(0); - DefaultShader.SetCurrentProjectionMatrix(CurrentProjectionMatrix); - } - ResetOpenGlState(); - List opaqueFaces, alphaFaces, overlayOpaqueFaces, overlayAlphaFaces; - lock (VisibleObjects.LockObject) - { - opaqueFaces = VisibleObjects.OpaqueFaces.ToList(); - alphaFaces = VisibleObjects.GetSortedPolygons(); - overlayOpaqueFaces = VisibleObjects.OverlayOpaqueFaces.ToList(); - overlayAlphaFaces = VisibleObjects.GetSortedPolygons(true); - } - - foreach (FaceState face in opaqueFaces) - { - face.Draw(); - } - - // alpha face - ResetOpenGlState(); - - if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance) - { - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Greater, 0.0f); - GL.DepthMask(false); - - foreach (FaceState face in alphaFaces) - { - face.Draw(); - } - } - else - { - UnsetBlendFunc(); - SetAlphaFunc(AlphaFunction.Equal, 1.0f); - GL.DepthMask(true); - - foreach (FaceState face in alphaFaces) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Normal && face.Object.Prototype.Mesh.Materials[face.Face.Material].GlowAttenuationData == 0) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].Color.A == 255) - { - face.Draw(); - } - } - } - - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Less, 1.0f); - GL.DepthMask(false); - bool additive = false; - - foreach (FaceState face in alphaFaces) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Additive) - { - if (!additive) - { - UnsetAlphaFunc(); - additive = true; - } - } - else - { - if (additive) - { - SetAlphaFunc(); - additive = false; - } - } - face.Draw(); - } - } + TransformedLightPosition = new Vector3(Lighting.OptionLightPosition.X, Lighting.OptionLightPosition.Y, -Lighting.OptionLightPosition.Z); + TransformedLightPosition.Transform(CurrentViewMatrix); - // motion blur - ResetOpenGlState(); - SetAlphaFunc(AlphaFunction.Greater, 0.0f); + // Execute Pipeline + ExecutePipeline(context); + } + } + internal class MotionBlurPass : IRenderPass + { + private readonly NewRenderer renderer; + internal MotionBlurPass(NewRenderer renderer) + { + this.renderer = renderer; + } + public void Execute(RenderContext context) + { + renderer.ResetOpenGlState(); + renderer.SetAlphaFunc(AlphaFunction.Greater, 0.0f); GL.Disable(EnableCap.DepthTest); GL.DepthMask(false); - OptionLighting = false; + renderer.OptionLighting = false; if (Interface.CurrentOptions.MotionBlur != MotionBlurMode.None) { - if (AvailableNewRenderer) + if (renderer.AvailableNewRenderer) { - DefaultShader.Deactivate(); + renderer.DefaultShader.Deactivate(); } - MotionBlur.RenderFullscreen(Interface.CurrentOptions.MotionBlur, FrameRate, Math.Abs(Camera.CurrentSpeed)); + renderer.MotionBlur.RenderFullscreen(Interface.CurrentOptions.MotionBlur, renderer.FrameRate, Math.Abs(renderer.Camera.CurrentSpeed)); } - // particle sources - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Greater, 0.0f); - if(CurrentInterface != InterfaceType.GLMainMenu) - { - foreach (TrainBase train in Program.TrainManager.Trains) - { - train.UpdateParticleSources(TimeElapsed); - } - - // ReSharper disable once PossibleInvalidCastExceptionInForeachLoop - foreach (ScriptedTrain scriptedTrain in Program.TrainManager.TFOs) - { - scriptedTrain.UpdateParticleSources(TimeElapsed); - } - - } - - if (Interface.CurrentOptions.ShowEvents) - { - if (Math.Abs(CameraTrackFollower.TrackPosition - events.LastTrackPosition) > 50) - { - events.LastTrackPosition = CameraTrackFollower.TrackPosition; - CubesToDraw.Clear(); - for (int i = 0; i < Program.CurrentRoute.Tracks.Count; i++) - { - int railIndex = Program.CurrentRoute.Tracks.ElementAt(i).Key; - events.FindEvents(railIndex); - } - } - - for (int i = 0; i < CubesToDraw.Count; i++) - { - Texture T = CubesToDraw.ElementAt(i).Key; - foreach (Vector3 v in CubesToDraw[T]) - { - Cube.Draw(v, Camera.AbsoluteDirection, Camera.AbsoluteUp, Camera.AbsoluteSide, 0.2, Camera.AbsolutePosition, T); - } - } - } - - // overlay (cab / interior) layer - Fog.Enabled = false; - UpdateViewport(ViewportChangeMode.ChangeToCab); - - if (AvailableNewRenderer) - { - /* - * We must reset the shader between overlay and world layers for correct lighting results. - * Additionally, the viewport change updates the projection matrix - */ - DefaultShader.Activate(); - ResetShader(DefaultShader); - DefaultShader.SetCurrentProjectionMatrix(CurrentProjectionMatrix); - } - CurrentViewMatrix = Matrix4D.LookAt(Vector3.Zero, new Vector3(Camera.AbsoluteDirection.X, Camera.AbsoluteDirection.Y, -Camera.AbsoluteDirection.Z), new Vector3(Camera.AbsoluteUp.X, Camera.AbsoluteUp.Y, -Camera.AbsoluteUp.Z)); - - if (Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable || Camera.CurrentRestriction == CameraRestrictionMode.Restricted3D) - { - ResetOpenGlState(); // TODO: inserted - GL.Clear(ClearBufferMask.DepthBufferBit); - OptionLighting = true; - Color24 prevOptionAmbientColor = Lighting.OptionAmbientColor; - Color24 prevOptionDiffuseColor = Lighting.OptionDiffuseColor; - Lighting.OptionAmbientColor = Color24.LightGrey; - Lighting.OptionDiffuseColor = Color24.LightGrey; - if (AvailableNewRenderer) - { - DefaultShader.SetIsLight(true); - TransformedLightPosition = new Vector3(Lighting.OptionLightPosition.X, Lighting.OptionLightPosition.Y, -Lighting.OptionLightPosition.Z); - DefaultShader.SetLightPosition(TransformedLightPosition); - DefaultShader.SetLightAmbient(Lighting.OptionAmbientColor); - DefaultShader.SetLightDiffuse(Lighting.OptionDiffuseColor); - DefaultShader.SetLightSpecular(Lighting.OptionSpecularColor); - DefaultShader.SetLightModel(Lighting.LightModel); - } - else - { - GL.Light(LightName.Light0, LightParameter.Ambient, new[] { inv255 * 178, inv255 * 178, inv255 * 178, 1.0f }); - GL.Light(LightName.Light0, LightParameter.Diffuse, new[] { inv255 * 178, inv255 * 178, inv255 * 178, 1.0f }); - } - - - // overlay opaque face - foreach (FaceState face in overlayOpaqueFaces) - { - face.Draw(); - } - - // overlay alpha face - ResetOpenGlState(); - if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance) - { - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Greater, 0.0f); - GL.DepthMask(false); - - foreach (FaceState face in overlayAlphaFaces) - { - face.Draw(); - } - } - else - { - UnsetBlendFunc(); - SetAlphaFunc(AlphaFunction.Equal, 1.0f); - GL.DepthMask(true); - - foreach (FaceState face in overlayAlphaFaces) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Normal && face.Object.Prototype.Mesh.Materials[face.Face.Material].GlowAttenuationData == 0) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].Color.A == 255) - { - face.Draw(); - } - } - } - - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Less, 1.0f); - GL.DepthMask(false); - bool additive = false; - - foreach (FaceState face in overlayAlphaFaces) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Additive) - { - if (!additive) - { - UnsetAlphaFunc(); - additive = true; - } - } - else - { - if (additive) - { - SetAlphaFunc(); - additive = false; - } - } - face.Draw(); - } - } - - Lighting.OptionAmbientColor = prevOptionAmbientColor; - Lighting.OptionDiffuseColor = prevOptionDiffuseColor; - Lighting.Initialize(); - } - else - { - /* - * Render 2D Cab - * This is actually an animated object generated on the fly and held in memory - */ - ResetOpenGlState(); - OptionLighting = false; - if (AvailableNewRenderer) - { - DefaultShader.SetIsLight(false); - } - - SetBlendFunc(); - UnsetAlphaFunc(); - GL.Disable(EnableCap.DepthTest); - GL.DepthMask(false); - foreach (FaceState face in overlayAlphaFaces) - { - face.Draw(); - } - } - // render touch - OptionLighting = false; - Touch.RenderScene(); - - // render overlays - ResetOpenGlState(); - UnsetAlphaFunc(); - SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); //FIXME: Remove when text switches between two renderer types - GL.Disable(EnableCap.DepthTest); - overlays.Render(RealTimeElapsed); - switch (CurrentInterface) - { - case InterfaceType.Menu: - case InterfaceType.GLMainMenu: - Game.Menu.Draw(TimeElapsed); - break; - case InterfaceType.SwitchChangeMap: - Game.SwitchChangeDialog.Draw(); - break; - } - OptionLighting = true; + renderer.OptionLighting = true; } + } - public NewRenderer(HostInterface currentHost, BaseOptions CurrentOptions, FileSystem fileSystem) : base(currentHost, CurrentOptions, fileSystem) + internal class TouchPickingPass : IRenderPass + { + private readonly NewRenderer renderer; + internal TouchPickingPass(NewRenderer renderer) + { + this.renderer = renderer; + } + public void Execute(RenderContext context) { + renderer.OptionLighting = false; + renderer.Touch.RenderScene(); + renderer.OptionLighting = true; } } } diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs b/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs index 2077c222a4..568064c9f1 100644 --- a/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs +++ b/source/OpenBVE/Graphics/Renderers/Overlays.Debug.cs @@ -187,7 +187,7 @@ private void RenderDebugOverlays() "=route", "track limit: " + (TrainManager.PlayerTrain.CurrentRouteLimit == double.PositiveInfinity ? "unlimited" : ((TrainManager.PlayerTrain.CurrentRouteLimit * 3.6).ToString("0.0", Culture) + " km/h")), "signal limit: " + (TrainManager.PlayerTrain.CurrentSectionLimit == double.PositiveInfinity ? "unlimited" : ((TrainManager.PlayerTrain.CurrentSectionLimit * 3.6).ToString("0.0", Culture) + " km/h")), - "total static objects: " + (Program.Renderer.StaticObjectStates.Count + Program.Renderer.DynamicObjectStates.Count).ToString(Culture), + "total static objects: " + (Program.Renderer.Scene.StaticObjectStates.Count + Program.Renderer.Scene.DynamicObjectStates.Count).ToString(Culture), "total static GL_TRIANGLES: " + Program.Renderer.InfoTotalTriangles.ToString(Culture), "total static GL_TRIANGLE_STRIP: " + Program.Renderer.InfoTotalTriangleStrip.ToString(Culture), "total static GL_QUADS: " + Program.Renderer.InfoTotalQuads.ToString(Culture), diff --git a/source/OpenBVE/Graphics/Renderers/Overlays.cs b/source/OpenBVE/Graphics/Renderers/Overlays.cs index eb0f25d707..a2df40ab51 100644 --- a/source/OpenBVE/Graphics/Renderers/Overlays.cs +++ b/source/OpenBVE/Graphics/Renderers/Overlays.cs @@ -1,4 +1,4 @@ -using System; +using System; using DavyKager; using LibRender2.Overlays; using LibRender2.Screens; @@ -45,10 +45,16 @@ internal void Render(double timeElapsed) //Initialize openGL renderer.SetBlendFunc(); GL.Enable(EnableCap.Blend); + GL.Disable(EnableCap.DepthTest); renderer.PushMatrix(MatrixMode.Projection); Matrix4D.CreateOrthographicOffCenter(0.0f, renderer.Screen.Width, renderer.Screen.Height, 0.0f, -1.0f, 1.0f, out renderer.CurrentProjectionMatrix); + if (renderer.AvailableNewRenderer) + { + renderer.DefaultShader.SetCurrentProjectionMatrix(renderer.CurrentProjectionMatrix); + } renderer.PushMatrix(MatrixMode.Modelview); renderer.CurrentViewMatrix = Matrix4D.Identity; + renderer.SetAlphaFunc(AlphaFunction.Greater, 0.0f); //Check which overlays to show switch (renderer.CurrentOutputMode) @@ -194,6 +200,7 @@ internal void Render(double timeElapsed) } // finalize + GL.Enable(EnableCap.DepthTest); renderer.PopMatrix(MatrixMode.Projection); renderer.PopMatrix(MatrixMode.Modelview); } diff --git a/source/OpenBVE/Graphics/Screen.cs b/source/OpenBVE/Graphics/Screen.cs index 9a6d71b3a0..8b4ca67e83 100644 --- a/source/OpenBVE/Graphics/Screen.cs +++ b/source/OpenBVE/Graphics/Screen.cs @@ -48,7 +48,7 @@ internal static void Initialize() GameWindowFlags.Default, GraphicsContextFlags.ForwardCompatible) { Visible = true, - WindowState = WindowState.Fullscreen + WindowState = WindowState.Fullscreen, }; } else @@ -57,7 +57,7 @@ internal static void Initialize() GameWindowFlags.Default) { Visible = true, - WindowState = WindowState.Fullscreen + WindowState = WindowState.Fullscreen, }; } diff --git a/source/OpenBVE/OpenBve.csproj b/source/OpenBVE/OpenBve.csproj index 33f74fcc7e..17e81a06ec 100644 --- a/source/OpenBVE/OpenBve.csproj +++ b/source/OpenBVE/OpenBve.csproj @@ -104,13 +104,10 @@ AnyCPU 7.3 prompt - Project - /glmenu=true + Project + /glmenu=true - - ..\..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\netstandard2.0\Microsoft.Bcl.AsyncInterfaces.dll - ..\..\packages\OpenTK-OpenBVE.1.0.4\lib\net20\OpenTK.dll @@ -121,8 +118,8 @@ False ..\..\dependencies\PIEHid64Net.dll - - ..\..\packages\SharpCompress.0.48.0\lib\netstandard2.0\SharpCompress.dll + + ..\..\packages\SharpCompress.0.32.2\lib\net461\SharpCompress.dll @@ -139,11 +136,8 @@ ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - ..\..\packages\System.Text.Encoding.CodePages.8.0.0\lib\netstandard2.0\System.Text.Encoding.CodePages.dll - - - ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + ..\..\packages\System.Text.Encoding.CodePages.6.0.0\lib\net461\System.Text.Encoding.CodePages.dll ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll diff --git a/source/OpenBVE/Parsers/Script/TrackFollowingObjectParser.cs b/source/OpenBVE/Parsers/Script/TrackFollowingObjectParser.cs index 3e42a584bd..eb1d2ff4fd 100644 --- a/source/OpenBVE/Parsers/Script/TrackFollowingObjectParser.cs +++ b/source/OpenBVE/Parsers/Script/TrackFollowingObjectParser.cs @@ -344,43 +344,37 @@ private static TravelStopData ParseTravelStopBlock(Block 0.0 | doorBoth; - } - if (sectionElement.GetValue(TrackFollowingObjectKey.Direction, out string travelDirection)) - { - int d; - switch (travelDirection.ToLowerInvariant()) + if (sectionElement.GetValue(TrackFollowingObjectKey.Direction, out string travelDirection)) { - case "f": - d = 1; - break; - case "r": - d = -1; - break; - default: - if (!NumberFormats.TryParseIntVb6(travelDirection, out d) || - !Enum.IsDefined(typeof(TravelDirection), d)) - { - Interface.AddMessage(MessageType.Error, false, - $"TravelDirection is invalid in {sectionElement.Key} in {sectionElement.FileName}"); + int d; + switch (travelDirection.ToLowerInvariant()) + { + case "f": d = 1; - } + break; + case "r": + d = -1; + break; + default: + if (!NumberFormats.TryParseIntVb6(travelDirection, out d) || !Enum.IsDefined(typeof(TravelDirection), d)) + { + Interface.AddMessage(MessageType.Error, false, $"TravelDirection is invalid in {sectionElement.Key} in {sectionElement.FileName}"); + d = 1; + } + break; + } - break; + travelStopData.Direction = (TravelDirection)d; } - travelStopData.Direction = (TravelDirection)d; + travelStopData.OpenLeftDoors = doorSide < 0.0 | doorBoth; + travelStopData.OpenRightDoors = doorSide > 0.0 | doorBoth; } - - - return travelStopData; } diff --git a/source/OpenBVE/System/GameWindow.cs b/source/OpenBVE/System/GameWindow.cs index d139b55959..41e931bf0b 100644 --- a/source/OpenBVE/System/GameWindow.cs +++ b/source/OpenBVE/System/GameWindow.cs @@ -31,7 +31,6 @@ using OpenTK.Graphics.OpenGL; using RouteManager2.MessageManager; using SoundManager; -using TrainManager.Car; using TrainManager.Trains; using Path = System.IO.Path; using Vector2 = OpenTK.Vector2; @@ -60,7 +59,7 @@ public OpenBVEGame(int width, int height, GraphicsMode currentGraphicsMode, Game } try { - string assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); Icon ico = new Icon(OpenBveApi.Path.CombineFile(OpenBveApi.Path.CombineDirectory(assemblyFolder, "Data"), "icon.ico")); Icon = ico; } @@ -166,64 +165,10 @@ protected override void OnRenderFrame(FrameEventArgs e) //We need to update the camera position in the render sequence //Not doing this means that the camera doesn't move // update in one piece - CameraViewMode cameraMode = Program.Renderer.Camera.CurrentMode; - if (cameraMode == CameraViewMode.Interior || cameraMode == CameraViewMode.InteriorLookAhead || cameraMode == CameraViewMode.Exterior) + if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead | Program.Renderer.Camera.CurrentMode == CameraViewMode.Exterior) { - CameraProperties cameraProperties = Program.Renderer.Camera; - TrackFollower trackFollower = Program.Renderer.CameraTrackFollower; - - if (cameraProperties.IsTransitioning) cameraProperties.CameraCarTransitionTimer += RealTimeElapsed; - if (cameraProperties.ModeTransitionTimer < 1.0) cameraProperties.ModeTransitionTimer += RealTimeElapsed / Interface.CurrentOptions.CameraTransitionSpeed; - double carTransitionProgress = Math.Min(1.0, cameraProperties.CameraCarTransitionTimer / Interface.CurrentOptions.CameraTransitionSpeed); - if ((cameraMode == CameraViewMode.Exterior && Interface.CurrentOptions.CameraExteriorTransition == false) || (cameraMode != CameraViewMode.Exterior && Interface.CurrentOptions.CameraInteriorTransition == false) || cameraMode == CameraViewMode.Interior) - { - // disabled transition - carTransitionProgress = 1.0; - cameraProperties.ModeTransitionTimer = 1.0; - } - if (carTransitionProgress >= 1.0) - { - cameraProperties.IsTransitioning = false; - if (cameraProperties.TargetCameraCar != -1) - { - cameraProperties.PreviousCameraCar = TrainManager.PlayerTrain.CameraCar; - TrainManager.PlayerTrain.CameraCar = cameraProperties.TargetCameraCar; - cameraProperties.TargetCameraCar = -1; - cameraProperties.IsTransitioning = true; - cameraProperties.CameraCarTransitionTimer = 0.0; - carTransitionProgress = 0.0; - } - } - - CarBase previousCar = (cameraProperties.IsTransitioning && cameraProperties.PreviousCameraCar != -1) ? TrainManager.PlayerTrain.Cars[cameraProperties.PreviousCameraCar] : null; //Update the in-car camera based upon the current driver car (Cabview or passenger view) - TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.CameraCar].UpdateCamera(previousCar, carTransitionProgress); - - if (cameraProperties.ModeTransitionTimer < 1.0) - { - double modeTransitionLinear = Math.Min(1.0, cameraProperties.ModeTransitionTimer); - double modeTransitionCosine = (1.0 - Math.Cos(modeTransitionLinear * Math.PI)) / 2.0; - var startAnchor = cameraProperties.ModeTransitionStart; - - trackFollower.WorldPosition = OpenBveApi.Math.Vector3.CosineInterpolate(startAnchor.Position, trackFollower.WorldPosition, modeTransitionLinear); - trackFollower.WorldDirection = OpenBveApi.Math.Vector3.CosineInterpolate(startAnchor.Direction, trackFollower.WorldDirection, modeTransitionLinear); - trackFollower.WorldDirection.Normalize(); - trackFollower.WorldUp = OpenBveApi.Math.Vector3.CosineInterpolate(startAnchor.Up, trackFollower.WorldUp, modeTransitionLinear); - trackFollower.WorldUp.Normalize(); - trackFollower.WorldSide = OpenBveApi.Math.Vector3.CosineInterpolate(startAnchor.Side, trackFollower.WorldSide, modeTransitionLinear); - trackFollower.WorldSide.Normalize(); - trackFollower.UpdateAbsolute( - startAnchor.TrackPosition + (trackFollower.TrackPosition - startAnchor.TrackPosition) * modeTransitionCosine, - false, - false); - - // Smoothly decay alignment back to zero - cameraProperties.AlignmentDirection.Yaw = startAnchor.Alignment.Yaw * (1.0 - modeTransitionCosine); - cameraProperties.AlignmentDirection.Pitch = startAnchor.Alignment.Pitch * (1.0 - modeTransitionCosine); - cameraProperties.AlignmentDirection.Roll = startAnchor.Alignment.Roll * (1.0 - modeTransitionCosine); - cameraProperties.AlignmentDirection.Position = startAnchor.Alignment.Position * (1.0 - modeTransitionCosine); - cameraProperties.AlignmentDirection.Zoom = startAnchor.Alignment.Zoom + (0.0 - startAnchor.Alignment.Zoom) * modeTransitionCosine; - } + TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.CameraCar].UpdateCamera(); } if (Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable || Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.Restricted3D) @@ -447,7 +392,7 @@ protected override void OnLoad(EventArgs e) if (w == Interface.CurrentOptions.WindowWidth && h == Interface.CurrentOptions.WindowHeight) { // If we are not in full-screen, but height and width are equal to resolution, hide taskbar - // e.g. Borderless windowed full-screen + // e.g. Borderless windowed fulllscreen int hwnd = FindWindow("Shell_TrayWnd",""); ShowWindow(hwnd,0); int hstart = FindWindowEx(GetDesktopWindow(), 0, "button", 0); @@ -547,7 +492,7 @@ protected override void OnClosing(CancelEventArgs e) } } Program.Renderer.TextureManager.UnloadAllTextures(false); - Program.Renderer.VisibilityThreadShouldRun = false; + Program.Renderer.Scene.VisibilityThreadShouldRun = false; for (int i = 0; i < InputDevicePlugin.AvailablePluginInfos.Count; i++) { InputDevicePlugin.CallPluginUnload(i); diff --git a/source/OpenBVE/System/Input/Keyboard.cs b/source/OpenBVE/System/Input/Keyboard.cs index 2a0de29337..119ab79a5b 100644 --- a/source/OpenBVE/System/Input/Keyboard.cs +++ b/source/OpenBVE/System/Input/Keyboard.cs @@ -47,7 +47,7 @@ internal static void KeyDownEvent(object sender, KeyboardKeyEventArgs e) //Compare the current and previous keyboard states //Only process if they are different if (!Enum.IsDefined(typeof(OpenBveApi.Input.Key), Interface.CurrentControls[i].Key)) continue; - if ((OpenBveApi.Input.Key)e.Key == Interface.CurrentControls[i].Key && Interface.CurrentControls[i].Modifier == CurrentKeyboardModifier) + if ((OpenBveApi.Input.Key)e.Key == Interface.CurrentControls[i].Key & Interface.CurrentControls[i].Modifier == CurrentKeyboardModifier) { Interface.CurrentControls[i].AnalogState = 1.0; @@ -114,7 +114,7 @@ internal static void KeyUpEvent(object sender, KeyboardKeyEventArgs e) //Compare the current and previous keyboard states //Only process if they are different if (!Enum.IsDefined(typeof(OpenBveApi.Input.Key), Interface.CurrentControls[i].Key)) continue; - if ((OpenBveApi.Input.Key)e.Key == Interface.CurrentControls[i].Key && Interface.CurrentControls[i].AnalogState == 1.0 && Interface.CurrentControls[i].DigitalState > DigitalControlState.Released) + if ((OpenBveApi.Input.Key)e.Key == Interface.CurrentControls[i].Key & Interface.CurrentControls[i].AnalogState == 1.0 & Interface.CurrentControls[i].DigitalState > DigitalControlState.Released) { Interface.CurrentControls[i].AnalogState = 0.0; Interface.CurrentControls[i].DigitalState = DigitalControlState.Released; diff --git a/source/OpenBVE/System/Input/ProcessControls.Digital.cs b/source/OpenBVE/System/Input/ProcessControls.Digital.cs index b4e00ddaeb..e1a410627c 100644 --- a/source/OpenBVE/System/Input/ProcessControls.Digital.cs +++ b/source/OpenBVE/System/Input/ProcessControls.Digital.cs @@ -61,9 +61,9 @@ private static void ProcessDigitalControl(double TimeElapsed, ref Control Contro Game.Menu.PushMenu(MenuType.Quit); break; case Translations.Command.CameraInterior: - // camera: interior (Handle camera transition from exterior mode to cab mode) + // camera: interior SaveCameraSettings(); - if (Program.Renderer.Camera.CurrentMode != CameraViewMode.InteriorLookAhead && Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable) + if (Program.Renderer.Camera.CurrentMode != CameraViewMode.InteriorLookAhead & Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable) { MessageManager.AddMessage(Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"notification","interior_lookahead"}), MessageDependency.CameraView, GameMode.Expert, @@ -77,35 +77,13 @@ private static void ProcessDigitalControl(double TimeElapsed, ref Control Contro MessageColor.White, 2, null); } - bool hasCab = TrainManager.PlayerTrain.Cars[0].InteriorCamera != null || TrainManager.PlayerTrain.Cars[0].HasInteriorView; - bool isAtCabCar = TrainManager.PlayerTrain.CameraCar == 0 || TrainManager.PlayerTrain.CameraCar == TrainManager.PlayerTrain.DriverCar; - bool transition = false; - // Transition only if switching from Exterior mode and at the head of the train (Car 0 or Driver Car) - if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Exterior && isAtCabCar && hasCab && Interface.CurrentOptions.CameraInteriorTransition) - { - Program.Renderer.Camera.ModeTransitionStart = ( - Program.Renderer.Camera.AbsolutePosition, - Program.Renderer.Camera.AbsoluteDirection, - Program.Renderer.Camera.AbsoluteUp, - Program.Renderer.Camera.AbsoluteSide, - Program.Renderer.CameraTrackFollower.TrackPosition, - new CameraAlignment( - Program.Renderer.Camera.AlignmentDirection.Position, - Program.Renderer.Camera.AlignmentDirection.Yaw, - Program.Renderer.Camera.AlignmentDirection.Pitch, - Program.Renderer.Camera.AlignmentDirection.Roll, - Program.Renderer.Camera.AlignmentDirection.TrackPosition, - Program.Renderer.Camera.AlignmentDirection.Zoom) - ); - transition = true; - } Program.Renderer.Camera.CurrentMode = CameraViewMode.Interior; RestoreCameraSettings(); for (int j = 0; j < TrainManager.PlayerTrain.Cars.Length; j++) { if (j == TrainManager.PlayerTrain.CameraCar) { - if (TrainManager.PlayerTrain.Cars[j].HasInteriorView) + if (TrainManager.PlayerTrain.Cars[j].CarSections.ContainsKey(CarSectionType.Interior)) { TrainManager.PlayerTrain.Cars[j].ChangeCarSection(CarSectionType.Interior); } @@ -128,10 +106,7 @@ private static void ProcessDigitalControl(double TimeElapsed, ref Control Contro TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar].ChangeCarSection(CarSectionType.Interior); } - if (!hasCab) - { - Program.Renderer.Camera.AlignmentDirection = new CameraAlignment(); - } + Program.Renderer.Camera.AlignmentDirection = new CameraAlignment(); Program.Renderer.Camera.AlignmentSpeed = new CameraAlignment(); Program.Renderer.UpdateViewport(ViewportChangeMode.NoChange); World.UpdateAbsoluteCamera(TimeElapsed); @@ -148,11 +123,6 @@ private static void ProcessDigitalControl(double TimeElapsed, ref Control Contro { Program.Renderer.Camera.CurrentMode = CameraViewMode.InteriorLookAhead; } - - if (transition) - { - Program.Renderer.Camera.ModeTransitionTimer = 0.0; - } break; case Translations.Command.CameraHeadOutLeft: case Translations.Command.CameraHeadOutRight: @@ -286,29 +256,7 @@ private static void ProcessDigitalControl(double TimeElapsed, ref Control Contro } break; case Translations.Command.CameraExterior: - // camera: exterior (Handle camera transition from cab mode to exterior mode) - bool hasCabLeaving = TrainManager.PlayerTrain.Cars[0].InteriorCamera != null || TrainManager.PlayerTrain.Cars[0].HasInteriorView; - bool isAtCabCarLeaving = TrainManager.PlayerTrain.CameraCar == 0 || TrainManager.PlayerTrain.CameraCar == TrainManager.PlayerTrain.DriverCar; - bool transitionLeaving = false; - // Transition only if switching from Interior mode and at the head of the train - if ((Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior || Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead) && isAtCabCarLeaving && hasCabLeaving && Interface.CurrentOptions.CameraExteriorTransition) - { - Program.Renderer.Camera.ModeTransitionStart = ( - Program.Renderer.Camera.AbsolutePosition, - Program.Renderer.Camera.AbsoluteDirection, - Program.Renderer.Camera.AbsoluteUp, - Program.Renderer.Camera.AbsoluteSide, - Program.Renderer.CameraTrackFollower.TrackPosition, - new CameraAlignment( - Program.Renderer.Camera.AlignmentDirection.Position, - Program.Renderer.Camera.AlignmentDirection.Yaw, - Program.Renderer.Camera.AlignmentDirection.Pitch, - Program.Renderer.Camera.AlignmentDirection.Roll, - Program.Renderer.Camera.AlignmentDirection.TrackPosition, - Program.Renderer.Camera.AlignmentDirection.Zoom) - ); - transitionLeaving = true; - } + // camera: exterior SaveCameraSettings(); Program.Renderer.Camera.CurrentMode = CameraViewMode.Exterior; RestoreCameraSettings(); @@ -334,11 +282,6 @@ private static void ProcessDigitalControl(double TimeElapsed, ref Control Contro Program.Renderer.UpdateViewport(ViewportChangeMode.NoChange); World.UpdateAbsoluteCamera(TimeElapsed); Program.Renderer.UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); - - if (transitionLeaving) - { - Program.Renderer.Camera.ModeTransitionTimer = 0.0; - } break; case Translations.Command.CameraTrack: case Translations.Command.CameraFlyBy: @@ -715,7 +658,7 @@ private static void ProcessDigitalControl(double TimeElapsed, ref Control Contro break; case Translations.Command.UncoupleFront: /* - * Need to remember that the coupler we're talking about here is actually that on the rear of the preceding car, i.e. IDX - 1 + * Need to remember that the coupler we're talking about here is actually that on the rear of the preceeding car, i.e. IDX - 1 * This connects to the following car and is therefore it's front coupler * * However, car indexing is zero-based, so we need to *add* one to get the correct display number diff --git a/source/OpenBVE/System/Input/ProcessControls.cs b/source/OpenBVE/System/Input/ProcessControls.cs index de7a9d7c72..2bf42c1fbb 100644 --- a/source/OpenBVE/System/Input/ProcessControls.cs +++ b/source/OpenBVE/System/Input/ProcessControls.cs @@ -65,7 +65,7 @@ internal static void ProcessControls(double TimeElapsed) TrainManager.PlayerTrain.CameraCar = TrainManager.PlayerTrain.DriverCar; SaveCameraSettings(); bool lookahead = false; - if (Program.Renderer.Camera.CurrentMode != CameraViewMode.InteriorLookAhead && (Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable || Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.Restricted3D)) + if (Program.Renderer.Camera.CurrentMode != CameraViewMode.InteriorLookAhead & (Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.NotAvailable || Program.Renderer.Camera.CurrentRestriction == CameraRestrictionMode.Restricted3D)) { MessageManager.AddMessage(Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"notification","interior_lookahead"}), MessageDependency.CameraView, GameMode.Expert, diff --git a/source/OpenBVE/System/Interface.cs b/source/OpenBVE/System/Interface.cs index b58db909cb..0ca05cb2bc 100644 --- a/source/OpenBVE/System/Interface.cs +++ b/source/OpenBVE/System/Interface.cs @@ -31,7 +31,7 @@ internal static bool TryParseTime(string Expression, out double Value) if (i >= 1) { if (int.TryParse(Expression.Substring(0, i), NumberStyles.Integer, Culture, out int h)) { int n = Expression.Length - i - 1; - if (n == 1 || n == 2) { + if (n == 1 | n == 2) { if (uint.TryParse(Expression.Substring(i + 1, n), NumberStyles.None, Culture, out uint m)) { Value = 3600.0 * h + 60.0 * m; return true; diff --git a/source/OpenBVE/System/Loading.cs b/source/OpenBVE/System/Loading.cs index 2874f65285..617c34988a 100644 --- a/source/OpenBVE/System/Loading.cs +++ b/source/OpenBVE/System/Loading.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; @@ -81,7 +82,89 @@ internal static void LoadAsynchronously(string routeFile, Encoding routeEncoding Loader.Start(); } - + /// Gets the absolute Railway folder for a given route file + /// The absolute on-disk path of the railway folder + internal static string GetRailwayFolder(string routeFile) { + try + { + string currentFolder = Path.GetDirectoryName(routeFile); + + while (true) + { + string Subfolder = Path.CombineDirectory(currentFolder, "Railway"); + if (Directory.Exists(Subfolder)) + { + if (Directory.EnumerateDirectories(Subfolder).Any() || Directory.EnumerateFiles(Subfolder).Any()) + { + //HACK: Ignore completely empty directories + //Doesn't handle wrong directories, or those with stuff missing, TODO..... + Program.FileSystem.AppendToLogFile(Subfolder + " : Railway folder found."); + return Subfolder; + } + + Program.FileSystem.AppendToLogFile(Subfolder + " : Railway folder candidate rejected- Directory empty."); + } + + if (currentFolder == null) continue; + DirectoryInfo directoryInfo = Directory.GetParent(currentFolder); + if (directoryInfo == null) break; + currentFolder = directoryInfo.FullName; + } + } + catch + { + // ignored + } + + // If the Route, Object and Sound folders exist, but are not in a railway folder..... + try + { + string currentFolder = Path.GetDirectoryName(routeFile); + if (currentFolder == null) + { + // Unlikely to work, but attempt to make the best of it + Program.FileSystem.AppendToLogFile("The route file appears to be stored on a root path- Returning the " + Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"program","title"}) + " startup path."); + return Application.StartupPath; + } + string candidate = null; + while (true) + { + string routeFolder = Path.CombineDirectory(currentFolder, "Route"); + string objectFolder = Path.CombineDirectory(currentFolder, "Object"); + string soundFolder = Path.CombineDirectory(currentFolder, "Sound"); + if (Directory.Exists(routeFolder) && Directory.Exists(objectFolder) && Directory.Exists(soundFolder)) + { + Program.FileSystem.AppendToLogFile(currentFolder + " : Railway folder found."); + return currentFolder; + } + + if (Directory.Exists(routeFolder) && Directory.Exists(objectFolder)) + { + candidate = currentFolder; + } + + DirectoryInfo directoryInfo = Directory.GetParent(currentFolder); + if (directoryInfo == null) + { + if (candidate != null) + { + Program.FileSystem.AppendToLogFile(currentFolder + " : The best candidate for the Railway folder has been selected- Sound folder not detected."); + return candidate; + } + + break; + } + + currentFolder = directoryInfo.FullName; + } + } + catch + { + // ignored + } + Program.FileSystem.AppendToLogFile("No Railway folder found- Returning the " + Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"program","title"}) + " startup path."); + return Application.StartupPath; + } /// Gets the default train folder for a given route file /// The absolute on-disk path of the train folder @@ -242,7 +325,7 @@ private static void LoadThreaded() { } private static void LoadEverythingThreaded() { - string railwayFolder = Program.FileSystem.GetRailwayFolder(CurrentRouteFile, Application.StartupPath); + string railwayFolder = GetRailwayFolder(CurrentRouteFile); string objectFolder = Path.CombineDirectory(railwayFolder, "Object"); string soundFolder = Path.CombineDirectory(railwayFolder, "Sound"); Game.Reset(true); diff --git a/source/OpenBVE/System/Options.cs b/source/OpenBVE/System/Options.cs index db2bd96ce3..1fcf0a48fa 100644 --- a/source/OpenBVE/System/Options.cs +++ b/source/OpenBVE/System/Options.cs @@ -105,13 +105,6 @@ internal class Options : BaseOptions internal int FPSLimit; internal bool DailyBuildUpdates; - - /// Whether smooth transitions are enabled for interior camera modes - internal bool CameraInteriorTransition; - /// Whether smooth transitions are enabled for exterior camera modes - internal bool CameraExteriorTransition; - /// The duration of camera transitions in seconds - internal double CameraTransitionSpeed; /// Creates a new instance of the options class with default values set internal Options() @@ -186,9 +179,6 @@ internal Options() UseGDIDecoders = false; EnableBve5ScriptedTrain = true; UserInterfaceScaleFactor = 1; - CameraInteriorTransition = true; - CameraExteriorTransition = true; - CameraTransitionSpeed = 0.4; CultureInfo currentCultureInfo = CultureInfo.CurrentCulture; switch (Program.CurrentHost.Platform) { @@ -302,9 +292,6 @@ public override void Save(string fileName) Builder.AppendLine("isUseNewRenderer = " + (IsUseNewRenderer ? "true" : "false")); Builder.AppendLine("forwardsCompatibleContext = " + (ForceForwardsCompatibleContext ? "true" : "false")); Builder.AppendLine("uiscalefactor = " + UserInterfaceScaleFactor); - Builder.AppendLine("cameraInteriorTransition = " + (CameraInteriorTransition ? "true" : "false")); - Builder.AppendLine("cameraExteriorTransition = " + (CameraExteriorTransition ? "true" : "false")); - Builder.AppendLine("cameraTransitionSpeed = " + CameraTransitionSpeed.ToString(Culture)); Builder.AppendLine(); Builder.AppendLine("[quality]"); Builder.AppendLine("interpolation = " + Interpolation); @@ -325,7 +312,6 @@ public override void Save(string fileName) Builder.AppendLine("shadowstrength = " + ShadowStrength.ToString(Culture)); Builder.AppendLine("shadowbias = " + ShadowBias.ToString(Culture)); Builder.AppendLine("shadownormalbias = " + ShadowNormalBias.ToString(Culture)); - Builder.AppendLine("shadowfiltercascades = " + (ShadowFilterCascades ? "true" : "false")); Builder.AppendLine("fpslimit = " + FPSLimit.ToString(Culture)); Builder.AppendLine(); Builder.AppendLine("[objectOptimization]"); @@ -470,7 +456,6 @@ internal static void LoadOptions() block.GetValue(OptionsKey.DailyBuildUpdates, out Interface.CurrentOptions.DailyBuildUpdates); break; case OptionsSection.Display: - { block.GetValue(OptionsKey.PreferNativeBackend, out CurrentOptions.PreferNativeBackend); block.GetValue(OptionsKey.Mode, out string m); CurrentOptions.FullscreenMode = string.Compare(m, "fullscreen", StringComparison.OrdinalIgnoreCase) == 0; @@ -500,35 +485,16 @@ internal static void LoadOptions() } block.TryGetValue(OptionsKey.UIScaleFactor, ref CurrentOptions.UserInterfaceScaleFactor); - block.GetValue(OptionsKey.CameraInteriorTransition, out CurrentOptions.CameraInteriorTransition); - block.GetValue(OptionsKey.CameraExteriorTransition, out CurrentOptions.CameraExteriorTransition); - block.TryGetValue(OptionsKey.CameraTransitionSpeed, ref CurrentOptions.CameraTransitionSpeed, NumberRange.Positive); - if (CurrentOptions.CameraTransitionSpeed == 0) - { - CurrentOptions.CameraTransitionSpeed = 0.4; - } break; - } case OptionsSection.Quality: - { block.GetEnumValue(OptionsKey.Interpolation, out Interface.CurrentOptions.Interpolation); block.TryGetValue(OptionsKey.AnisotropicFilteringMaximum, ref CurrentOptions.AnisotropicFilteringMaximum); block.TryGetValue(OptionsKey.AnisotropicFilteringLevel, ref Interface.CurrentOptions.AnisotropicFilteringLevel); block.TryGetValue(OptionsKey.AntiAliasingLevel, ref Interface.CurrentOptions.AntiAliasingLevel); block.GetEnumValue(OptionsKey.TransparencyMode, out Interface.CurrentOptions.TransparencyMode); block.GetValue(OptionsKey.OldTransparencyMode, out CurrentOptions.OldTransparencyMode); - block.TryGetValue(OptionsKey.ViewingDistance, ref Interface.CurrentOptions.ViewingDistance, NumberRange.Positive); - block.TryGetValue(OptionsKey.QuadLeafSize, ref Interface.CurrentOptions.QuadTreeLeafSize, NumberRange.Positive); - block.TryGetValue(OptionsKey.NearClipScenery, ref Interface.CurrentOptions.NearClipScenery, NumberRange.Positive); - block.TryGetValue(OptionsKey.NearClipCab, ref Interface.CurrentOptions.NearClipCab, NumberRange.Positive); - block.TryGetValue(OptionsKey.NearClipBase, ref Interface.CurrentOptions.NearClipBase, NumberRange.Positive); - // ensure viewing distance is greater than the near clipping plane to avoid rendering issues - double maxNearClip = Math.Max(Interface.CurrentOptions.NearClipScenery, Math.Max(Interface.CurrentOptions.NearClipCab, Interface.CurrentOptions.NearClipBase)); - - if (Interface.CurrentOptions.ViewingDistance <= maxNearClip) - { - Interface.CurrentOptions.ViewingDistance = (int)Math.Ceiling(maxNearClip) + 1; - } + block.TryGetValue(OptionsKey.ViewingDistance, ref Interface.CurrentOptions.ViewingDistance); + block.TryGetValue(OptionsKey.QuadLeafSize, ref CurrentOptions.QuadTreeLeafSize); block.GetEnumValue(OptionsKey.MotionBlur, out CurrentOptions.MotionBlur); block.TryGetEnumValue(OptionsKey.ShadowResolution, ref Interface.CurrentOptions.ShadowResolution); block.TryGetEnumValue(OptionsKey.ShadowDrawDistance, ref Interface.CurrentOptions.ShadowDrawDistance); @@ -539,7 +505,6 @@ internal static void LoadOptions() if (CurrentOptions.ShadowBias > 1.0) CurrentOptions.ShadowBias = 1.0; block.TryGetValue(OptionsKey.ShadowNormalBias, ref CurrentOptions.ShadowNormalBias); if (CurrentOptions.ShadowNormalBias < 0.0) CurrentOptions.ShadowNormalBias = 0.0; - block.GetValue(OptionsKey.ShadowFilterCascades, out Interface.CurrentOptions.ShadowFilterCascades); block.GetValue(OptionsKey.FPSLimit, out CurrentOptions.FPSLimit); if (CurrentOptions.FPSLimit < 0) { @@ -547,14 +512,11 @@ internal static void LoadOptions() } break; - } case OptionsSection.ObjectOptimization: - { block.GetValue(OptionsKey.BasicThreshold, out CurrentOptions.ObjectOptimizationBasicThreshold); block.GetValue(OptionsKey.FullThreshold, out CurrentOptions.ObjectOptimizationFullThreshold); block.GetValue(OptionsKey.VertexCulling, out CurrentOptions.ObjectOptimizationVertexCulling); break; - } case OptionsSection.Simulation: block.GetValue(OptionsKey.Toppling, out CurrentOptions.Toppling); block.GetValue(OptionsKey.Collisions, out CurrentOptions.Collisions); diff --git a/source/OpenBVE/System/Scripting.cs b/source/OpenBVE/System/Scripting.cs index b8cc61f7a0..f2d9a53b82 100644 --- a/source/OpenBVE/System/Scripting.cs +++ b/source/OpenBVE/System/Scripting.cs @@ -29,19 +29,20 @@ public static int CurrentAspect(TrainBase Train, int SectionIndex, bool IsPartOf if (IsPartOfTrain) { int nextSectionIndex = Train.CurrentSectionIndex + 1; - if (nextSectionIndex >= 0 && nextSectionIndex < Program.CurrentRoute.Sections.Length) + if (nextSectionIndex >= 0 & nextSectionIndex < Program.CurrentRoute.Sections.Length) { int a = Program.CurrentRoute.Sections[nextSectionIndex].CurrentAspect; - if (a >= 0 && a < Program.CurrentRoute.Sections[nextSectionIndex].Aspects.Length) + if (a >= 0 & a < Program.CurrentRoute.Sections[nextSectionIndex].Aspects.Length) { return Program.CurrentRoute.Sections[nextSectionIndex].Aspects[a].Number; } + return 0; } } - else if (SectionIndex >= 0 && SectionIndex < Program.CurrentRoute.Sections.Length) + else if (SectionIndex >= 0 & SectionIndex < Program.CurrentRoute.Sections.Length) { int a = Program.CurrentRoute.Sections[SectionIndex].CurrentAspect; - if (a >= 0 && a < Program.CurrentRoute.Sections[SectionIndex].Aspects.Length) + if (a >= 0 & a < Program.CurrentRoute.Sections[SectionIndex].Aspects.Length) { return Program.CurrentRoute.Sections[SectionIndex].Aspects[a].Number; } diff --git a/source/OpenBVE/UserInterface/formBugReport.cs b/source/OpenBVE/UserInterface/formBugReport.cs index 25090a3f6e..aa8d549932 100644 --- a/source/OpenBVE/UserInterface/formBugReport.cs +++ b/source/OpenBVE/UserInterface/formBugReport.cs @@ -44,8 +44,8 @@ private void ApplyLanguage() { private void buttonViewLog_Click(object sender, EventArgs e) { try - { - string file = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "log.txt"); + { + var file = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "log.txt"); if(File.Exists(file)) { if (Program.CurrentHost.Platform == HostPlatform.MicrosoftWindows) @@ -73,8 +73,8 @@ private void buttonViewCrashLog_Click(object sender, EventArgs e) { try { - DirectoryInfo directory = new DirectoryInfo(Program.FileSystem.SettingsFolder); - FileInfo file = directory.GetFiles("OpenBVE Crash*.log").OrderByDescending(f => f.LastWriteTime).First(); + var directory = new DirectoryInfo(Program.FileSystem.SettingsFolder); + var file = directory.GetFiles("OpenBVE Crash*.log").OrderByDescending(f => f.LastWriteTime).First(); if (Program.CurrentHost.Platform == HostPlatform.MicrosoftWindows) { Process.Start(file.FullName); @@ -98,7 +98,7 @@ private void buttonReportProblem_Click(object sender, EventArgs e) { using (var ProblemReport = File.OpenWrite(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName))) { - using (var zipWriter = WriterFactory.OpenWriter(ProblemReport, ArchiveType.Zip, new WriterOptions(CompressionType.LZMA))) + using (var zipWriter = WriterFactory.Open(ProblemReport, ArchiveType.Zip, CompressionType.LZMA)) { //Add log file to the archive var file = OpenBveApi.Path.CombineFile(Program.FileSystem.SettingsFolder, "log.txt"); diff --git a/source/OpenBVE/UserInterface/formMain.Designer.cs b/source/OpenBVE/UserInterface/formMain.Designer.cs index 2918843547..06cb2d0921 100644 --- a/source/OpenBVE/UserInterface/formMain.Designer.cs +++ b/source/OpenBVE/UserInterface/formMain.Designer.cs @@ -162,11 +162,6 @@ private void InitializeComponent() { this.groupboxSound = new System.Windows.Forms.GroupBox(); this.updownSoundNumber = new System.Windows.Forms.NumericUpDown(); this.labelSoundNumber = new System.Windows.Forms.Label(); - this.groupboxCamera = new System.Windows.Forms.GroupBox(); - this.checkboxCameraInteriorTransition = new System.Windows.Forms.CheckBox(); - this.checkboxCameraExteriorTransition = new System.Windows.Forms.CheckBox(); - this.labelCameraTransitionSpeed = new System.Windows.Forms.Label(); - this.updownCameraTransitionSpeed = new System.Windows.Forms.NumericUpDown(); this.panelOptionsPage2 = new System.Windows.Forms.Panel(); this.groupboxDistance = new System.Windows.Forms.GroupBox(); this.updownNearClipBase = new System.Windows.Forms.NumericUpDown(); @@ -183,7 +178,6 @@ private void InitializeComponent() { this.groupboxShadows = new System.Windows.Forms.GroupBox(); this.labelShadowResolution = new System.Windows.Forms.Label(); this.comboboxShadowResolution = new System.Windows.Forms.ComboBox(); - this.checkboxShadowFilterCascades = new System.Windows.Forms.CheckBox(); this.labelShadowDistance = new System.Windows.Forms.Label(); this.comboboxShadowDistance = new System.Windows.Forms.ComboBox(); this.labelShadowCascades = new System.Windows.Forms.Label(); @@ -542,8 +536,6 @@ private void InitializeComponent() { this.groupboxSimulation.SuspendLayout(); this.groupboxSound.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownSoundNumber)).BeginInit(); - this.groupboxCamera.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.updownCameraTransitionSpeed)).BeginInit(); this.panelOptionsPage2.SuspendLayout(); this.groupboxDistance.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.updownNearClipBase)).BeginInit(); @@ -1590,6 +1582,7 @@ private void InitializeComponent() { this.panelOptionsLeft.Controls.Add(this.groupboxDisplayMode); this.panelOptionsLeft.Controls.Add(this.groupboxWindow); this.panelOptionsLeft.Controls.Add(this.groupboxFullscreen); + this.panelOptionsLeft.Controls.Add(this.groupboxInterpolation); this.panelOptionsLeft.Location = new System.Drawing.Point(8, 72); this.panelOptionsLeft.Name = "panelOptionsLeft"; this.panelOptionsLeft.Size = new System.Drawing.Size(316, 576); @@ -1940,9 +1933,9 @@ private void InitializeComponent() { this.groupboxInterpolation.Controls.Add(this.labelInterpolation); this.groupboxInterpolation.Controls.Add(this.trackbarTransparency); this.groupboxInterpolation.ForeColor = System.Drawing.Color.Black; - this.groupboxInterpolation.Location = new System.Drawing.Point(0, 158); + this.groupboxInterpolation.Location = new System.Drawing.Point(0, 381); this.groupboxInterpolation.Name = "groupboxInterpolation"; - this.groupboxInterpolation.Size = new System.Drawing.Size(321, 160); + this.groupboxInterpolation.Size = new System.Drawing.Size(316, 160); this.groupboxInterpolation.TabIndex = 7; this.groupboxInterpolation.TabStop = false; this.groupboxInterpolation.Text = "Interpolation"; @@ -2073,7 +2066,7 @@ private void InitializeComponent() { this.panelOptionsRight.Controls.Add(this.groupBoxRailDriver); this.panelOptionsRight.Controls.Add(this.groupboxControls); this.panelOptionsRight.Controls.Add(this.groupboxVerbosity); - this.panelOptionsRight.Controls.Add(this.groupBoxKioskMode); + this.panelOptionsRight.Controls.Add(this.groupboxSimulation); this.panelOptionsRight.Controls.Add(this.groupboxSound); this.panelOptionsRight.Location = new System.Drawing.Point(360, 72); this.panelOptionsRight.Name = "panelOptionsRight"; @@ -2087,7 +2080,7 @@ private void InitializeComponent() { this.groupBoxOther.Controls.Add(this.comboBoxTimeTableDisplayMode); this.groupBoxOther.Controls.Add(this.labelTimeTableDisplayMode); this.groupBoxOther.ForeColor = System.Drawing.Color.Black; - this.groupBoxOther.Location = new System.Drawing.Point(0, 365); + this.groupBoxOther.Location = new System.Drawing.Point(0, 347); this.groupBoxOther.Name = "groupBoxOther"; this.groupBoxOther.Size = new System.Drawing.Size(316, 48); this.groupBoxOther.TabIndex = 19; @@ -2246,7 +2239,7 @@ private void InitializeComponent() { this.groupboxVerbosity.Controls.Add(this.checkboxErrorMessages); this.groupboxVerbosity.Controls.Add(this.checkboxWarningMessages); this.groupboxVerbosity.ForeColor = System.Drawing.Color.Black; - this.groupboxVerbosity.Location = new System.Drawing.Point(0, 298); + this.groupboxVerbosity.Location = new System.Drawing.Point(0, 283); this.groupboxVerbosity.Name = "groupboxVerbosity"; this.groupboxVerbosity.Size = new System.Drawing.Size(316, 64); this.groupboxVerbosity.TabIndex = 12; @@ -2293,9 +2286,9 @@ private void InitializeComponent() { this.groupboxSimulation.Controls.Add(this.checkboxCollisions); this.groupboxSimulation.Controls.Add(this.checkboxToppling); this.groupboxSimulation.ForeColor = System.Drawing.Color.Black; - this.groupboxSimulation.Location = new System.Drawing.Point(330, 485); + this.groupboxSimulation.Location = new System.Drawing.Point(0, 203); this.groupboxSimulation.Name = "groupboxSimulation"; - this.groupboxSimulation.Size = new System.Drawing.Size(321, 80); + this.groupboxSimulation.Size = new System.Drawing.Size(316, 80); this.groupboxSimulation.TabIndex = 11; this.groupboxSimulation.TabStop = false; this.groupboxSimulation.Text = "Detail of simulation"; @@ -2407,13 +2400,8 @@ private void InitializeComponent() { // panelOptionsPage2 // this.panelOptionsPage2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(243)))), ((int)(((byte)(255)))), ((int)(((byte)(243))))); - this.panelOptionsPage2.Controls.Add(this.groupboxCamera); this.panelOptionsPage2.Controls.Add(this.groupboxDistance); this.panelOptionsPage2.Controls.Add(this.groupboxShadows); - this.panelOptionsPage2.Controls.Add(this.groupboxInterpolation); - this.panelOptionsPage2.Controls.Add(this.groupBoxAdvancedOptions); - this.panelOptionsPage2.Controls.Add(this.groupBoxObjectParser); - this.panelOptionsPage2.Controls.Add(this.groupboxSimulation); this.panelOptionsPage2.Location = new System.Drawing.Point(0, 72); this.panelOptionsPage2.Name = "panelOptionsPage2"; this.panelOptionsPage2.Size = new System.Drawing.Size(683, 583); @@ -2644,86 +2632,14 @@ private void InitializeComponent() { this.groupboxShadows.Controls.Add(this.updownShadowBias); this.groupboxShadows.Controls.Add(this.labelShadowNormalBias); this.groupboxShadows.Controls.Add(this.updownShadowNormalBias); - this.groupboxShadows.Controls.Add(this.checkboxShadowFilterCascades); this.groupboxShadows.ForeColor = System.Drawing.Color.Black; - this.groupboxShadows.Location = new System.Drawing.Point(330, 0); + this.groupboxShadows.Location = new System.Drawing.Point(0, 155); this.groupboxShadows.Name = "groupboxShadows"; - this.groupboxShadows.Size = new System.Drawing.Size(321, 240); + this.groupboxShadows.Size = new System.Drawing.Size(321, 210); this.groupboxShadows.TabIndex = 30; this.groupboxShadows.TabStop = false; this.groupboxShadows.Text = "Shadows"; // - // groupboxCamera - // - this.groupboxCamera.Controls.Add(this.checkboxCameraInteriorTransition); - this.groupboxCamera.Controls.Add(this.checkboxCameraExteriorTransition); - this.groupboxCamera.Controls.Add(this.labelCameraTransitionSpeed); - this.groupboxCamera.Controls.Add(this.updownCameraTransitionSpeed); - this.groupboxCamera.ForeColor = System.Drawing.Color.Black; - this.groupboxCamera.Location = new System.Drawing.Point(330, 245); - this.groupboxCamera.Name = "groupboxCamera"; - this.groupboxCamera.Size = new System.Drawing.Size(321, 120); - this.groupboxCamera.TabIndex = 22; - this.groupboxCamera.TabStop = false; - this.groupboxCamera.Text = "Camera options"; - // - // checkboxCameraInteriorTransition - // - this.checkboxCameraInteriorTransition.AutoSize = true; - this.checkboxCameraInteriorTransition.Location = new System.Drawing.Point(8, 21); - this.checkboxCameraInteriorTransition.Name = "checkboxCameraInteriorTransition"; - this.checkboxCameraInteriorTransition.Size = new System.Drawing.Size(150, 17); - this.checkboxCameraInteriorTransition.TabIndex = 0; - this.checkboxCameraInteriorTransition.Text = "Smooth interior transition"; - this.checkboxCameraInteriorTransition.UseVisualStyleBackColor = true; - // - // checkboxCameraExteriorTransition - // - this.checkboxCameraExteriorTransition.AutoSize = true; - this.checkboxCameraExteriorTransition.Location = new System.Drawing.Point(8, 44); - this.checkboxCameraExteriorTransition.Name = "checkboxCameraExteriorTransition"; - this.checkboxCameraExteriorTransition.Size = new System.Drawing.Size(150, 17); - this.checkboxCameraExteriorTransition.TabIndex = 1; - this.checkboxCameraExteriorTransition.Text = "Smooth exterior transition"; - this.checkboxCameraExteriorTransition.UseVisualStyleBackColor = true; - // - // labelCameraTransitionSpeed - // - this.labelCameraTransitionSpeed.AutoSize = true; - this.labelCameraTransitionSpeed.Location = new System.Drawing.Point(8, 73); - this.labelCameraTransitionSpeed.Name = "labelCameraTransitionSpeed"; - this.labelCameraTransitionSpeed.Size = new System.Drawing.Size(130, 13); - this.labelCameraTransitionSpeed.TabIndex = 2; - this.labelCameraTransitionSpeed.Text = "Transition duration (sec):"; - // - // updownCameraTransitionSpeed - // - this.updownCameraTransitionSpeed.DecimalPlaces = 1; - this.updownCameraTransitionSpeed.Increment = new decimal(new int[] { - 1, - 0, - 0, - 65536}); - this.updownCameraTransitionSpeed.Location = new System.Drawing.Point(200, 71); - this.updownCameraTransitionSpeed.Maximum = new decimal(new int[] { - 50, - 0, - 0, - 65536}); - this.updownCameraTransitionSpeed.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 65536}); - this.updownCameraTransitionSpeed.Name = "updownCameraTransitionSpeed"; - this.updownCameraTransitionSpeed.Size = new System.Drawing.Size(52, 20); - this.updownCameraTransitionSpeed.TabIndex = 3; - this.updownCameraTransitionSpeed.Value = new decimal(new int[] { - 4, - 0, - 0, - 65536}); - // // labelShadowResolution // this.labelShadowResolution.AutoSize = true; @@ -2749,16 +2665,6 @@ private void InitializeComponent() { this.comboboxShadowResolution.TabIndex = 1; this.comboboxShadowResolution.SelectedIndexChanged += new System.EventHandler(this.comboboxShadowResolution_SelectedIndexChanged); // - // checkboxShadowFilterCascades - // - this.checkboxShadowFilterCascades.AutoSize = true; - this.checkboxShadowFilterCascades.Location = new System.Drawing.Point(8, 215); - this.checkboxShadowFilterCascades.Name = "checkboxShadowFilterCascades"; - this.checkboxShadowFilterCascades.Size = new System.Drawing.Size(150, 17); - this.checkboxShadowFilterCascades.TabIndex = 14; - this.checkboxShadowFilterCascades.Text = "Per-cascade culling"; - this.checkboxShadowFilterCascades.UseVisualStyleBackColor = true; - // // labelShadowDistance // this.labelShadowDistance.AutoSize = true; @@ -2922,7 +2828,7 @@ private void InitializeComponent() { this.buttonOptionsNext.TabIndex = 18; this.buttonOptionsNext.Text = "Next Page..."; this.buttonOptionsNext.UseVisualStyleBackColor = true; - this.buttonOptionsNext.Click += new System.EventHandler(this.buttonOptionsNext_Click); + this.buttonOptionsNext.Click += new System.EventHandler(this.buttonOptionsPrevious_Click); // // panelOptionsPage3 // @@ -2930,6 +2836,9 @@ private void InitializeComponent() { | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.panelOptionsPage3.Controls.Add(this.groupBoxInputDevice); + this.panelOptionsPage3.Controls.Add(this.groupBoxObjectParser); + this.panelOptionsPage3.Controls.Add(this.groupBoxKioskMode); + this.panelOptionsPage3.Controls.Add(this.groupBoxAdvancedOptions); this.panelOptionsPage3.Controls.Add(this.groupBoxPackageOptions); this.panelOptionsPage3.Location = new System.Drawing.Point(0, 72); this.panelOptionsPage3.Name = "panelOptionsPage3"; @@ -2946,9 +2855,9 @@ private void InitializeComponent() { this.groupBoxInputDevice.Controls.Add(this.checkBoxInputDeviceEnable); this.groupBoxInputDevice.Controls.Add(this.buttonInputDeviceConfig); this.groupBoxInputDevice.ForeColor = System.Drawing.Color.Black; - this.groupBoxInputDevice.Location = new System.Drawing.Point(0, 190); + this.groupBoxInputDevice.Location = new System.Drawing.Point(6, 396); this.groupBoxInputDevice.Name = "groupBoxInputDevice"; - this.groupBoxInputDevice.Size = new System.Drawing.Size(674, 385); + this.groupBoxInputDevice.Size = new System.Drawing.Size(674, 181); this.groupBoxInputDevice.TabIndex = 24; this.groupBoxInputDevice.TabStop = false; this.groupBoxInputDevice.Text = "Input Device Plugin"; @@ -2983,7 +2892,7 @@ private void InitializeComponent() { this.listviewInputDevice.MultiSelect = false; this.listviewInputDevice.Name = "listviewInputDevice"; this.listviewInputDevice.ShowGroups = false; - this.listviewInputDevice.Size = new System.Drawing.Size(658, 303); + this.listviewInputDevice.Size = new System.Drawing.Size(658, 103); this.listviewInputDevice.TabIndex = 1; this.listviewInputDevice.UseCompatibleStateImageBehavior = false; this.listviewInputDevice.View = System.Windows.Forms.View.Details; @@ -3015,7 +2924,7 @@ private void InitializeComponent() { // this.checkBoxInputDeviceEnable.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.checkBoxInputDeviceEnable.Enabled = false; - this.checkBoxInputDeviceEnable.Location = new System.Drawing.Point(8, 344); + this.checkBoxInputDeviceEnable.Location = new System.Drawing.Point(8, 144); this.checkBoxInputDeviceEnable.Name = "checkBoxInputDeviceEnable"; this.checkBoxInputDeviceEnable.Size = new System.Drawing.Size(230, 34); this.checkBoxInputDeviceEnable.TabIndex = 2; @@ -3029,7 +2938,7 @@ private void InitializeComponent() { this.buttonInputDeviceConfig.BackColor = System.Drawing.SystemColors.ButtonFace; this.buttonInputDeviceConfig.Enabled = false; this.buttonInputDeviceConfig.ForeColor = System.Drawing.SystemColors.ControlText; - this.buttonInputDeviceConfig.Location = new System.Drawing.Point(270, 352); + this.buttonInputDeviceConfig.Location = new System.Drawing.Point(270, 148); this.buttonInputDeviceConfig.MaximumSize = new System.Drawing.Size(106, 25); this.buttonInputDeviceConfig.Name = "buttonInputDeviceConfig"; this.buttonInputDeviceConfig.Size = new System.Drawing.Size(106, 25); @@ -3046,9 +2955,9 @@ private void InitializeComponent() { this.groupBoxObjectParser.Controls.Add(this.labelXparser); this.groupBoxObjectParser.Controls.Add(this.comboBoxXparser); this.groupBoxObjectParser.ForeColor = System.Drawing.Color.Black; - this.groupBoxObjectParser.Location = new System.Drawing.Point(330, 370); + this.groupBoxObjectParser.Location = new System.Drawing.Point(377, 288); this.groupBoxObjectParser.Name = "groupBoxObjectParser"; - this.groupBoxObjectParser.Size = new System.Drawing.Size(321, 110); + this.groupBoxObjectParser.Size = new System.Drawing.Size(305, 110); this.groupBoxObjectParser.TabIndex = 23; this.groupBoxObjectParser.TabStop = false; this.groupBoxObjectParser.Text = "Object Parser"; @@ -3098,15 +3007,14 @@ private void InitializeComponent() { // // groupBoxKioskMode // - this.groupBoxKioskMode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxKioskMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.groupBoxKioskMode.Controls.Add(this.labelKioskTimeout); this.groupBoxKioskMode.Controls.Add(this.numericUpDownKioskTimeout); this.groupBoxKioskMode.Controls.Add(this.checkBoxEnableKiosk); this.groupBoxKioskMode.ForeColor = System.Drawing.Color.Black; - this.groupBoxKioskMode.Location = new System.Drawing.Point(0, 203); + this.groupBoxKioskMode.Location = new System.Drawing.Point(377, 190); this.groupBoxKioskMode.Name = "groupBoxKioskMode"; - this.groupBoxKioskMode.Size = new System.Drawing.Size(316, 92); + this.groupBoxKioskMode.Size = new System.Drawing.Size(305, 92); this.groupBoxKioskMode.TabIndex = 22; this.groupBoxKioskMode.TabStop = false; this.groupBoxKioskMode.Text = "Kiosk Mode"; @@ -3159,9 +3067,9 @@ private void InitializeComponent() { this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxIsUseNewRenderer); this.groupBoxAdvancedOptions.Controls.Add(this.checkBoxLoadInAdvance); this.groupBoxAdvancedOptions.ForeColor = System.Drawing.Color.Black; - this.groupBoxAdvancedOptions.Location = new System.Drawing.Point(0, 323); + this.groupBoxAdvancedOptions.Location = new System.Drawing.Point(8, 190); this.groupBoxAdvancedOptions.Name = "groupBoxAdvancedOptions"; - this.groupBoxAdvancedOptions.Size = new System.Drawing.Size(321, 208); + this.groupBoxAdvancedOptions.Size = new System.Drawing.Size(358, 208); this.groupBoxAdvancedOptions.TabIndex = 21; this.groupBoxAdvancedOptions.TabStop = false; this.groupBoxAdvancedOptions.Text = "Advanced Options"; @@ -6475,9 +6383,6 @@ private void InitializeComponent() { this.groupboxSimulation.PerformLayout(); this.groupboxSound.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.updownSoundNumber)).EndInit(); - this.groupboxCamera.ResumeLayout(false); - this.groupboxCamera.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.updownCameraTransitionSpeed)).EndInit(); this.panelOptionsPage2.ResumeLayout(false); this.groupboxDistance.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.updownNearClipBase)).EndInit(); @@ -6579,7 +6484,6 @@ private void InitializeComponent() { private System.Windows.Forms.Panel panelOptionsRight; private System.Windows.Forms.Label labelShadowResolution; private System.Windows.Forms.ComboBox comboboxShadowResolution; - private System.Windows.Forms.CheckBox checkboxShadowFilterCascades; private System.Windows.Forms.Label labelShadowDistance; private System.Windows.Forms.ComboBox comboboxShadowDistance; private System.Windows.Forms.Label labelShadowCascades; @@ -7031,11 +6935,6 @@ private void InitializeComponent() { private System.Windows.Forms.Button buttonMSTSTrainsetDirectory; private System.Windows.Forms.Label label1; private System.Windows.Forms.TextBox textBoxMSTSTrainsetDirectory; - private System.Windows.Forms.GroupBox groupboxCamera; - private System.Windows.Forms.CheckBox checkboxCameraInteriorTransition; - private System.Windows.Forms.CheckBox checkboxCameraExteriorTransition; - private System.Windows.Forms.Label labelCameraTransitionSpeed; - private System.Windows.Forms.NumericUpDown updownCameraTransitionSpeed; private System.Windows.Forms.Panel panelOptionsPage2; } } diff --git a/source/OpenBVE/UserInterface/formMain.Options.cs b/source/OpenBVE/UserInterface/formMain.Options.cs index d6322bd841..f63c0e7512 100644 --- a/source/OpenBVE/UserInterface/formMain.Options.cs +++ b/source/OpenBVE/UserInterface/formMain.Options.cs @@ -53,22 +53,21 @@ private void comboboxShadowResolution_SelectedIndexChanged(object sender, EventA labelShadowStrengthValue.Enabled = shadowEnabled; updownShadowBias.Enabled = shadowEnabled; updownShadowNormalBias.Enabled = shadowEnabled; - checkboxShadowFilterCascades.Enabled = shadowEnabled; if (!shadowEnabled) { // Visual hint: grey out the strength label - labelShadowStrengthValue.Text = @"—"; + labelShadowStrengthValue.Text = "—"; } else { - labelShadowStrengthValue.Text = trackbarShadowStrength.Value + @"%"; + labelShadowStrengthValue.Text = trackbarShadowStrength.Value + "%"; } } private void trackbarShadowStrength_Scroll(object sender, EventArgs e) { - labelShadowStrengthValue.Text = trackbarShadowStrength.Value + @"%"; + labelShadowStrengthValue.Text = trackbarShadowStrength.Value + "%"; } private void updownShadowBias_ValueChanged(object sender, EventArgs e) diff --git a/source/OpenBVE/UserInterface/formMain.Packages.cs b/source/OpenBVE/UserInterface/formMain.Packages.cs index f551704b74..7a9fc4087d 100644 --- a/source/OpenBVE/UserInterface/formMain.Packages.cs +++ b/source/OpenBVE/UserInterface/formMain.Packages.cs @@ -663,9 +663,21 @@ private void dataGridViewPackages_SelectionChanged(object sender, EventArgs e) private void buttonUninstallPackage_Click(object sender, EventArgs e) { - if (currentPackage != null && currentPackage.PackageType != PackageType.NotFound) + if (currentPackage != null) { - UninstallPackage(currentPackage); + switch (currentPackage.PackageType) + { + case PackageType.Route: + UninstallPackage(currentPackage); + break; + case PackageType.Train: + UninstallPackage(currentPackage); + break; + case PackageType.Other: + case PackageType.Loksim3D: + UninstallPackage(currentPackage); + break; + } } else { @@ -1083,6 +1095,7 @@ private void Q1_CheckedChanged(object sender, EventArgs e) panelReplacePackage.Hide(); panelNewPackage.Show(); panelNewPackage.Enabled = true; + string GUID = Guid.NewGuid().ToString(); currentPackage = new Package { Name = textBoxPackageName.Text, @@ -1090,7 +1103,7 @@ private void Q1_CheckedChanged(object sender, EventArgs e) Description = textBoxPackageDescription.Text.Replace("\r\n", "\\r\\n"), //TODO: //Website = linkLabelPackageWebsite.Links[0], - GUID = Guid.NewGuid().ToString(), + GUID = GUID, PackageVersion = new Version(0, 0, 0, 0), PackageType = newPackageType }; @@ -1448,13 +1461,13 @@ private void addPackageItemsButton_Click(object sender, EventArgs e) { filesToPackageBox.Text += folderDisplay + Environment.NewLine; - List tempList = new List(); + var tempList = new List(); for (int i = 0; i < files.Length; i++) { - PackageFile tempFile = new PackageFile + var tempFile = new PackageFile { absolutePath = files[i], - relativePath = files[i].Replace(folder, "") + relativePath = files[i].Replace(folder, ""), }; tempList.Add(tempFile); } diff --git a/source/OpenBVE/UserInterface/formMain.Review.cs b/source/OpenBVE/UserInterface/formMain.Review.cs index 61b1efde35..660c0149a3 100644 --- a/source/OpenBVE/UserInterface/formMain.Review.cs +++ b/source/OpenBVE/UserInterface/formMain.Review.cs @@ -59,7 +59,7 @@ private void ShowScoreLog(bool penaltiesOnly) { int sum = 0; for (int i = 0; i < Game.ScoreLogs.Count; i++) { sum += Game.ScoreLogs[i].Value; - if (!penaltiesOnly || Game.ScoreLogs[i].Value < 0) { + if (!penaltiesOnly | Game.ScoreLogs[i].Value < 0) { double x = Game.ScoreLogs[i].Time; int h = (int)Math.Floor(x / 3600.0); x -= 3600.0 * h; diff --git a/source/OpenBVE/UserInterface/formMain.Start.cs b/source/OpenBVE/UserInterface/formMain.Start.cs index 8d9aa06c8a..3f74913701 100644 --- a/source/OpenBVE/UserInterface/formMain.Start.cs +++ b/source/OpenBVE/UserInterface/formMain.Start.cs @@ -1269,7 +1269,7 @@ private void PreviewLoadRoute(LaunchParameters result) // ReSharper disable once RedundantCast object route = (object)Program.CurrentRoute; //must cast to allow us to use the ref keyword. - string railwayFolder = Program.FileSystem.GetRailwayFolder(result.RouteFile, Application.StartupPath); + string railwayFolder = Loading.GetRailwayFolder(result.RouteFile); string objectFolder = Path.CombineDirectory(railwayFolder, "Object"); string soundFolder = Path.CombineDirectory(railwayFolder, "Sound"); @@ -1346,9 +1346,9 @@ private void ShowRouteInformation(Exception e) } // description - string routeDescription = Program.CurrentRoute.Comment.ConvertNewlinesToCrLf(); - textboxRouteDescription.Text = routeDescription.Length != 0 ? routeDescription : System.IO.Path.GetFileNameWithoutExtension(Result.RouteFile); - textboxRouteEncodingPreview.Text = routeDescription.ConvertNewlinesToCrLf(); + string Description = Program.CurrentRoute.Comment.ConvertNewlinesToCrLf(); + textboxRouteDescription.Text = Description.Length != 0 ? Description : System.IO.Path.GetFileNameWithoutExtension(Result.RouteFile); + textboxRouteEncodingPreview.Text = Description.ConvertNewlinesToCrLf(); if (Interface.CurrentOptions.TrainName != null) { checkboxTrainDefault.Text = $@"{Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"start","train_usedefault"})} ({Interface.CurrentOptions.TrainName})"; @@ -1414,7 +1414,7 @@ internal void DisposePreviewRouteThread() // show route - private void ShowRoute(bool userSelectedEncoding) + private void ShowRoute(bool UserSelectedEncoding) { Cursor = System.Windows.Forms.Cursors.WaitCursor; TryLoadImage(pictureboxRouteImage, "loading.png"); @@ -1422,7 +1422,7 @@ private void ShowRoute(bool userSelectedEncoding) textboxRouteDescription.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"start","route_processing"}); // determine encoding - if (!userSelectedEncoding) + if (!UserSelectedEncoding) { Result.RouteEncoding = TextEncoding.GetSystemEncodingFromFile(Result.RouteFile); comboboxRouteEncoding.Tag = new object(); @@ -1469,7 +1469,7 @@ private void ShowRoute(bool userSelectedEncoding) } // show train - private void ShowTrain(bool userSelectedEncoding) + private void ShowTrain(bool UserSelectedEncoding) { lock (previewLock) { @@ -1487,7 +1487,7 @@ private void ShowTrain(bool userSelectedEncoding) { canLoad = true; trainImage = Program.CurrentHost.Plugins[i].Train.GetImage(Result.TrainFolder); - if (!userSelectedEncoding) + if (!UserSelectedEncoding) { string descriptionFile = Path.CombineFile(Result.TrainFolder, "train.txt"); if (!File.Exists(descriptionFile)) diff --git a/source/OpenBVE/UserInterface/formMain.cs b/source/OpenBVE/UserInterface/formMain.cs index f48c5853a9..b929c7d81c 100644 --- a/source/OpenBVE/UserInterface/formMain.cs +++ b/source/OpenBVE/UserInterface/formMain.cs @@ -29,7 +29,6 @@ internal partial class formMain : Form private formMain() { InitializeComponent(); - toolTip = new ToolTip(components); } public sealed override string Text @@ -40,25 +39,24 @@ public sealed override string Text internal static LaunchParameters ShowMainDialog(LaunchParameters initial) { - using (formMain mainDialog = new formMain()) + using (formMain Dialog = new formMain()) { - mainDialog.Result = initial; - mainDialog.ShowDialog(); - LaunchParameters result = mainDialog.Result; + Dialog.Result = initial; + Dialog.ShowDialog(); + LaunchParameters result = Dialog.Result; //Dispose of the worker thread when closing the form //If it's still running, it attempts to update a non-existent form and crashes nastily - mainDialog.DisposePreviewRouteThread(); + Dialog.DisposePreviewRouteThread(); if (!OpenTK.Configuration.RunningOnMacOS) { - mainDialog.trainWatcher.Dispose(); - mainDialog.routeWatcher.Dispose(); + Dialog.trainWatcher.Dispose(); + Dialog.routeWatcher.Dispose(); } return result; } } // members - private readonly ToolTip toolTip; private LaunchParameters Result; private int[] EncodingCodepages; private Image JoystickImage; @@ -75,11 +73,11 @@ internal static LaunchParameters ShowMainDialog(LaunchParameters initial) private void formMain_Load(object sender, EventArgs e) { MinimumSize = Size; - if (Interface.CurrentOptions.MainMenuWidth == -1 && Interface.CurrentOptions.MainMenuHeight == -1) + if (Interface.CurrentOptions.MainMenuWidth == -1 & Interface.CurrentOptions.MainMenuHeight == -1) { WindowState = FormWindowState.Maximized; } - else if (Interface.CurrentOptions.MainMenuWidth > 0 && Interface.CurrentOptions.MainMenuHeight > 0) + else if (Interface.CurrentOptions.MainMenuWidth > 0 & Interface.CurrentOptions.MainMenuHeight > 0) { Size = new Size(Interface.CurrentOptions.MainMenuWidth, Interface.CurrentOptions.MainMenuHeight); CenterToScreen(); @@ -493,15 +491,13 @@ private void formMain_Load(object sender, EventArgs e) } // Shadow Strength trackbarShadowStrength.Value = (int)(Interface.CurrentOptions.ShadowStrength * 100.0); - labelShadowStrengthValue.Text = trackbarShadowStrength.Value + @"%"; + labelShadowStrengthValue.Text = trackbarShadowStrength.Value + "%"; updownShadowBias.Value = (decimal)Interface.CurrentOptions.ShadowBias; updownShadowNormalBias.Value = (decimal)Interface.CurrentOptions.ShadowNormalBias; - checkboxShadowFilterCascades.Checked = Interface.CurrentOptions.ShadowFilterCascades; // Enable/disable shadow sub-controls based on resolution setting bool shadowEnabled = Interface.CurrentOptions.ShadowResolution != ShadowMapResolution.Off; comboboxShadowDistance.Enabled = shadowEnabled; comboboxShadowCascades.Enabled = shadowEnabled; - checkboxShadowFilterCascades.Enabled = shadowEnabled; trackbarShadowStrength.Enabled = shadowEnabled; updownShadowBias.Enabled = shadowEnabled; updownShadowNormalBias.Enabled = shadowEnabled; @@ -526,9 +522,6 @@ private void formMain_Load(object sender, EventArgs e) checkBoxEnableKiosk.Checked = Interface.CurrentOptions.KioskMode; numericUpDownKioskTimeout.Value = (decimal)Interface.CurrentOptions.KioskModeTimer; checkBoxAccessibility.Checked = Interface.CurrentOptions.Accessibility; - checkboxCameraInteriorTransition.Checked = Interface.CurrentOptions.CameraInteriorTransition; - checkboxCameraExteriorTransition.Checked = Interface.CurrentOptions.CameraExteriorTransition; - updownCameraTransitionSpeed.Value = (decimal)Interface.CurrentOptions.CameraTransitionSpeed; ListInputDevicePlugins(); if (Program.CurrentHost.MonoRuntime) { @@ -779,10 +772,6 @@ private void ApplyLanguage() comboboxShadowCascades.Items[2] = Translations.GetInterfaceString(HostApplication.OpenBve, new[] { "options", "shadows_cascades_4" }); //Simulation groupboxSimulation.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","misc_simulation"}); - groupboxCamera.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","camera"}); - checkboxCameraInteriorTransition.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","camera_interior_transition"}); - checkboxCameraExteriorTransition.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","camera_exterior_transition"}); - labelCameraTransitionSpeed.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","camera_transition_duration"}); checkboxToppling.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","misc_simulation_toppling"}); checkboxCollisions.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","misc_simulation_collisions"}); checkboxDerailments.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"options","misc_simulation_derailments"}); @@ -934,7 +923,7 @@ private void ApplyLanguage() case GameMode.Arcade: labelRatingModeValue.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"mode","arcade"}); break; case GameMode.Normal: labelRatingModeValue.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"mode","normal"}); break; case GameMode.Expert: labelRatingModeValue.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"mode","expert"}); break; - default: labelRatingModeValue.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"mode","unknown"}); break; + default: labelRatingModeValue.Text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"mode","unkown"}); break; } { double ratio = Game.CurrentScore.Maximum == 0 ? 0.0 : (double)Game.CurrentScore.CurrentValue / Game.CurrentScore.Maximum; @@ -1163,32 +1152,6 @@ private void ApplyLanguage() } - - ApplyToolTips(); - } - - /// Applies tooltips to the various controls on the form - private void ApplyToolTips() - { - SetToolTip("interpolation", labelInterpolation, comboboxInterpolation); - SetToolTip("anisotropic", labelAnisotropic, updownAnisotropic); - SetToolTip("antialiasing", labelAntiAliasing, updownAntiAliasing); - SetToolTip("transparency", labelTransparency, trackbarTransparency); - SetToolTip("viewingdistance", labelDistance, updownDistance); - SetToolTip("motionblur", labelMotionBlur, comboboxMotionBlur); - SetToolTip("new_renderer", checkBoxIsUseNewRenderer); - } - - /// Sets the tooltip for one or more controls using a translation key - /// The translation key in the 'tooltips' group - /// The controls to apply the tooltip to - private void SetToolTip(string key, params Control[] controls) - { - string text = Translations.GetInterfaceString(HostApplication.OpenBve, new[] { "tooltips", key }); - foreach (Control control in controls) - { - toolTip.SetToolTip(control, text); - } } // form closing @@ -1263,7 +1226,6 @@ private void formMain_FormClosing() Interface.CurrentOptions.ShadowStrength = trackbarShadowStrength.Value / 100.0; Interface.CurrentOptions.ShadowBias = (double)updownShadowBias.Value; Interface.CurrentOptions.ShadowNormalBias = (double)updownShadowNormalBias.Value; - Interface.CurrentOptions.ShadowFilterCascades = checkboxShadowFilterCascades.Checked; Interface.CurrentOptions.GameMode = (GameMode)comboboxMode.SelectedIndex; Interface.CurrentOptions.BlackBox = checkboxBlackBox.Checked; Interface.CurrentOptions.LoadingSway = checkBoxLoadingSway.Checked; @@ -1283,9 +1245,6 @@ private void formMain_FormClosing() Interface.CurrentOptions.CurrentObjParser = (ObjParsers)comboBoxObjparser.SelectedIndex; Interface.CurrentOptions.Panel2ExtendedMode = checkBoxPanel2Extended.Checked; Interface.CurrentOptions.Accessibility = checkBoxAccessibility.Checked; - Interface.CurrentOptions.CameraInteriorTransition = checkboxCameraInteriorTransition.Checked; - Interface.CurrentOptions.CameraExteriorTransition = checkboxCameraExteriorTransition.Checked; - Interface.CurrentOptions.CameraTransitionSpeed = (double)updownCameraTransitionSpeed.Value; switch (trackBarHUDSize.Value) { case 0: @@ -1570,7 +1529,7 @@ private void formMain_Shown(object sender, EventArgs e) if (WindowState != FormWindowState.Maximized) { System.Windows.Forms.Screen s = System.Windows.Forms.Screen.FromControl(this); - if (Width >= 0.95 * s.WorkingArea.Width || Height >= 0.95 * s.WorkingArea.Height) + if (Width >= 0.95 * s.WorkingArea.Width | Height >= 0.95 * s.WorkingArea.Height) { WindowState = FormWindowState.Maximized; } @@ -1667,10 +1626,6 @@ private void radiobuttonOptions_CheckedChanged(object sender, EventArgs e) panelPackages.Visible = false; pictureboxJoysticks.Visible = false; UpdatePanelColor(); - if (radiobuttonOptions.Checked) - { - SetOptionsPage(0); - } } private void radioButtonPackages_CheckedChanged(object sender, EventArgs e) { @@ -2091,50 +2046,26 @@ private void linkLabelCheckUpdates_Click(object sender, EventArgs e) CheckForUpdate(); } - private Control[][] optionsPages; - private int currentOptionsPage = 0; - - private void SetOptionsPage(int pageIndex) + private void buttonOptionsPrevious_Click(object sender, EventArgs e) { - if (optionsPages == null) - { - optionsPages = new[] { - new Control[] { panelOptionsLeft, panelOptionsRight }, - new Control[] { panelOptionsPage2 }, - new Control[] { panelOptionsPage3 } - }; - } - currentOptionsPage = pageIndex; - for (int i = 0; i < optionsPages.Length; i++) - { - for (int j = 0; j < optionsPages[i].Length; j++) - { - optionsPages[i][j].Visible = (i == pageIndex); - } - } - buttonOptionsPrevious.Enabled = (currentOptionsPage > 0); - buttonOptionsNext.Enabled = (currentOptionsPage < optionsPages.Length - 1); - - if (panelOptionsPage2.Visible) + if (panelOptionsLeft.Visible) { + panelOptionsLeft.Hide(); + panelOptionsRight.Hide(); + panelOptionsPage2.Show(); //HACK: Column Header in list view won't appear in Mono without resizing it... listviewInputDevice.AutoResizeColumns(ColumnHeaderAutoResizeStyle.None); } - } - - private void buttonOptionsPrevious_Click(object sender, EventArgs e) - { - if (currentOptionsPage > 0) + else if(panelOptionsPage2.Visible) { - SetOptionsPage(currentOptionsPage - 1); + panelOptionsPage2.Hide(); + panelOptionsPage3.Show(); } - } - - private void buttonOptionsNext_Click(object sender, EventArgs e) - { - if (currentOptionsPage < optionsPages.Length - 1) + else { - SetOptionsPage(currentOptionsPage + 1); + panelOptionsPage3.Hide(); + panelOptionsLeft.Show(); + panelOptionsRight.Show(); } } diff --git a/source/OpenBVE/UserInterface/formViewLog.cs b/source/OpenBVE/UserInterface/formViewLog.cs index c5a13ba4d1..d21b568a78 100644 --- a/source/OpenBVE/UserInterface/formViewLog.cs +++ b/source/OpenBVE/UserInterface/formViewLog.cs @@ -17,7 +17,7 @@ public FormViewLog(string text) private void SetText(string text) { - string originalTitle = Text; + var originalTitle = Text; Text += Translations.GetInterfaceString(HostApplication.OpenBve, new[] {"message","loading"}); Shown += (sender, e) => { textBoxLog.Text = text; diff --git a/source/OpenBVE/app.config b/source/OpenBVE/app.config index 4495e3fe51..e5213d2369 100644 --- a/source/OpenBVE/app.config +++ b/source/OpenBVE/app.config @@ -17,10 +17,6 @@ - - - - diff --git a/source/OpenBVE/packages.config b/source/OpenBVE/packages.config index a179bdd455..ad7c03cd92 100644 --- a/source/OpenBVE/packages.config +++ b/source/OpenBVE/packages.config @@ -1,13 +1,11 @@  - - + - - + \ No newline at end of file diff --git a/source/OpenBveApi/FunctionScripts/FunctionScript.cs b/source/OpenBveApi/FunctionScripts/FunctionScript.cs index d9db77f7c4..b56beb3149 100644 --- a/source/OpenBveApi/FunctionScripts/FunctionScript.cs +++ b/source/OpenBveApi/FunctionScripts/FunctionScript.cs @@ -449,11 +449,7 @@ public FunctionScript(HostInterface Host, string Expression, bool Infix) if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1); InstructionSet[n] = Instructions.CameraView; n++; s++; if (s >= m) m = s; break; - case "cameracar": - if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1); - InstructionSet[n] = Instructions.CameraCar; - n++; s++; if (s >= m) m = s; break; - // train + // train case "playertrain": if (n >= InstructionSet.Length) Array.Resize(ref InstructionSet, InstructionSet.Length << 1); InstructionSet[n] = Instructions.PlayerTrain; diff --git a/source/OpenBveApi/FunctionScripts/Instructions.cs b/source/OpenBveApi/FunctionScripts/Instructions.cs index 355a66bd00..2e59b542f7 100644 --- a/source/OpenBveApi/FunctionScripts/Instructions.cs +++ b/source/OpenBveApi/FunctionScripts/Instructions.cs @@ -17,7 +17,7 @@ public enum Instructions { SystemHalt, SystemValue, SystemDelta, StackCopy, StackSwap, MathRandom, MathRandomInt, - TimeSecondsSinceMidnight, TimeHourDigit, TimeMinuteDigit, TimeSecondDigit, CameraDistance, CameraXDistance, CameraYDistance, CameraZDistance, CameraView, CameraCar, + TimeSecondsSinceMidnight, TimeHourDigit, TimeMinuteDigit, TimeSecondDigit, CameraDistance, CameraXDistance, CameraYDistance, CameraZDistance,CameraView, TrainCars, TrainCarNumber, TrainDestination, PlayerTrain, TrainLength, TrainSpeed, TrainSpeedometer, TrainAcceleration, TrainAccelerationMotor, diff --git a/source/OpenBveApi/Interface/Input/InputDevice.cs b/source/OpenBveApi/Interface/Input/InputDevice.cs index 6d1bb7c790..64dc837c38 100644 --- a/source/OpenBveApi/Interface/Input/InputDevice.cs +++ b/source/OpenBveApi/Interface/Input/InputDevice.cs @@ -19,7 +19,7 @@ public class InputEventArgs : EventArgs /// Control's information public InputEventArgs(InputControl control) { - Control = control; + this.Control = control; } /// @@ -232,7 +232,7 @@ public static void LoadPlugins(FileSystem.FileSystem fileSystem) Assembly Plugin; try { - Plugin = Assembly.LoadFile(File); + Plugin = Assembly.LoadFrom(File); } catch { diff --git a/source/OpenBveApi/Math/Vectors/Vector3.cs b/source/OpenBveApi/Math/Vectors/Vector3.cs index 09c77609f8..9d9b5a98dd 100644 --- a/source/OpenBveApi/Math/Vectors/Vector3.cs +++ b/source/OpenBveApi/Math/Vectors/Vector3.cs @@ -1,3 +1,4 @@ +using System; using System.Globalization; using OpenBveApi.World; // ReSharper disable UnusedMember.Global @@ -321,6 +322,10 @@ public static implicit operator Vector3f(Vector3 v) /// The multiplied vector public static Vector3 operator *(Vector3 v, Transformation t) { + if (t == null) + { + return v; + } v = t.X * v.X + t.Y * v.Y + t.Z * v.Z; return v; } @@ -497,6 +502,10 @@ public void Rotate(Orientation3 orientation) { /// The transformation public void Rotate(Transformation transformation) { + if (transformation == null) + { + return; + } double x = transformation.X.X * X + transformation.Y.X * Y + transformation.Z.X * Z; double y = transformation.X.Y * X + transformation.Y.Y * Y + transformation.Z.Y * Z; double z = transformation.X.Z * X + transformation.Y.Z * Y + transformation.Z.Z * Z; diff --git a/source/OpenBveApi/Objects/Helpers/MeshBuilder.cs b/source/OpenBveApi/Objects/Helpers/MeshBuilder.cs index 404c1be8b7..3d54a2fed7 100644 --- a/source/OpenBveApi/Objects/Helpers/MeshBuilder.cs +++ b/source/OpenBveApi/Objects/Helpers/MeshBuilder.cs @@ -39,11 +39,6 @@ public MeshBuilder(Hosts.HostInterface Host) /// Applies the MeshBuilder's data to a StaticObject public void Apply(ref StaticObject Object, bool EnableHacks = false, bool IgnoreW = true) { - if (Vertices.Count == 0) - { - return; - } - if (TransformMatrix != Matrix4D.NoTransformation) { for (int i = 0; i < Vertices.Count; i++) diff --git a/source/OpenBveApi/Objects/Mesh.cs b/source/OpenBveApi/Objects/Mesh.cs index ae6eb478b2..8dff1909bd 100644 --- a/source/OpenBveApi/Objects/Mesh.cs +++ b/source/OpenBveApi/Objects/Mesh.cs @@ -1,4 +1,4 @@ -using OpenBveApi.Colors; +using OpenBveApi.Colors; using OpenBveApi.Math; namespace OpenBveApi.Objects @@ -19,6 +19,8 @@ public class Mesh public object VAO; /// The OpenGL/OpenTK VAO for the normals public object NormalsVAO; + /// The radius of the bounding sphere + public double BoundingSphereRadius; /// Creates a new empty mesh public Mesh() @@ -122,7 +124,49 @@ private void CreateNormals(int FaceIndex) } } } + /// Creates the bounding box and bounding sphere for this mesh + public void CreateBoundingBox() + { + if (Vertices.Length == 0) + { + BoundingBox = new[] { Vector3.Zero, Vector3.Zero }; + BoundingSphereRadius = 0; + return; + } + + Vector3 min = new Vector3(double.MaxValue, double.MaxValue, double.MaxValue); + Vector3 max = new Vector3(double.MinValue, double.MinValue, double.MinValue); + + for (int i = 0; i < Vertices.Length; i++) + { + if (Vertices[i].Coordinates.X < min.X) min.X = Vertices[i].Coordinates.X; + if (Vertices[i].Coordinates.Y < min.Y) min.Y = Vertices[i].Coordinates.Y; + if (Vertices[i].Coordinates.Z < min.Z) min.Z = Vertices[i].Coordinates.Z; + + if (Vertices[i].Coordinates.X > max.X) max.X = Vertices[i].Coordinates.X; + if (Vertices[i].Coordinates.Y > max.Y) max.Y = Vertices[i].Coordinates.Y; + if (Vertices[i].Coordinates.Z > max.Z) max.Z = Vertices[i].Coordinates.Z; + } - + BoundingBox = new[] { min, max }; + + // Compute bounding sphere radius (from center of box) + Vector3 center = (min + max) * 0.5; + double maxRadiusSq = 0; + for (int i = 0; i < Vertices.Length; i++) + { + double distSq = (Vertices[i].Coordinates - center).NormSquared(); + if (distSq > maxRadiusSq) maxRadiusSq = distSq; + } + BoundingSphereRadius = System.Math.Sqrt(maxRadiusSq) + center.Norm(); // Add center norm for radius from origin if needed, but standard radius is from center + // Actually, OpenBVE uses origin as anchor usually. Let's use max distance from origin for simplest culling. + double maxDistFromOriginSq = 0; + for (int i = 0; i < Vertices.Length; i++) + { + double distSq = Vertices[i].Coordinates.NormSquared(); + if (distSq > maxDistFromOriginSq) maxDistFromOriginSq = distSq; + } + BoundingSphereRadius = System.Math.Sqrt(maxDistFromOriginSq); + } } } diff --git a/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs b/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs index e2afba0157..d367e317a8 100644 --- a/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs +++ b/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs @@ -719,7 +719,6 @@ public void Update(AbstractTrain Train, int CarIndex, double TrackPosition, Vect double dz = -Camera.Alignment.Position.Z; Vector3 add = Camera.AbsolutePosition + dx * Camera.AbsoluteSide + dy * Camera.AbsoluteUp + dz * Camera.AbsoluteDirection; internalObject.Translation = Matrix4D.CreateTranslation(add.X, add.Y, -add.Z); - internalObject.WorldPosition = add; } else { @@ -728,7 +727,6 @@ public void Update(AbstractTrain Train, int CarIndex, double TrackPosition, Vect // translate internalObject.Translation = Matrix4D.CreateTranslation(Position.X, Position.Y, -Position.Z); - internalObject.WorldPosition = Position; } if (ColorFunction != null && Colors != null) diff --git a/source/OpenBveApi/Objects/ObjectTypes/StaticObject.cs b/source/OpenBveApi/Objects/ObjectTypes/StaticObject.cs index a7d447fe32..ca298f74c2 100644 --- a/source/OpenBveApi/Objects/ObjectTypes/StaticObject.cs +++ b/source/OpenBveApi/Objects/ObjectTypes/StaticObject.cs @@ -960,6 +960,7 @@ public override void OptimizeObject(bool preserveVerticies, int faceThreshold, b { Array.Resize(ref Mesh.Faces, f); } + Mesh.CreateBoundingBox(); } } } diff --git a/source/OpenBveApi/Objects/ObjectTypes/UnifiedObject.cs b/source/OpenBveApi/Objects/ObjectTypes/UnifiedObject.cs index a628858d01..c768351ce6 100644 --- a/source/OpenBveApi/Objects/ObjectTypes/UnifiedObject.cs +++ b/source/OpenBveApi/Objects/ObjectTypes/UnifiedObject.cs @@ -8,7 +8,9 @@ public abstract class UnifiedObject { /// Creates the object within the worldspace without using track based transforms /// The world position - /// The parameters to use when creating the object + /// The track distance at which this is displayed by the renderer + /// The track distance at which this hidden by the renderer + /// The absolute track position at which this object is placed public void CreateObject(Vector3 Position, ObjectCreationParameters Parameters) { CreateObject(Position, Transformation.NullTransformation, Transformation.NullTransformation, Parameters); @@ -17,7 +19,9 @@ public void CreateObject(Vector3 Position, ObjectCreationParameters Parameters) /// Creates the object within the worldspace using a single track based transforms /// The world position /// The world transformation to apply (e.g. ground, rail) - /// The parameters to use when creating the object + /// The track distance at which this is displayed by the renderer + /// The track distance at which this hidden by the renderer + /// The absolute track position at which this object is placed public void CreateObject(Vector3 Position, Transformation WorldTransformation, ObjectCreationParameters Parameters) { CreateObject(Position, WorldTransformation, Transformation.NullTransformation, Parameters); @@ -27,7 +31,9 @@ public void CreateObject(Vector3 Position, Transformation WorldTransformation, O /// The world position /// The world transformation to apply (e.g. ground, rail) /// The local transformation to apply in order to rotate the model - /// The parameters to use when creating the object + /// The track distance at which this is displayed by the renderer + /// The track distance at which this hidden by the renderer + /// The absolute track position at which this object is placed public abstract void CreateObject(Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, ObjectCreationParameters Parameters); /// Call this method to optimize the object diff --git a/source/OpenBveApi/OpenBveApi.csproj b/source/OpenBveApi/OpenBveApi.csproj index c75e05d797..29d2f7ef65 100644 --- a/source/OpenBveApi/OpenBveApi.csproj +++ b/source/OpenBveApi/OpenBveApi.csproj @@ -70,12 +70,9 @@ ..\..\packages\CS-Script.lib.3.30.3\lib\net46\CSScriptLibrary.dll - - ..\..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\netstandard2.0\Microsoft.Bcl.AsyncInterfaces.dll - - - ..\..\packages\SharpCompress.0.48.0\lib\netstandard2.0\SharpCompress.dll + + ..\..\packages\SharpCompress.0.32.2\lib\net461\SharpCompress.dll @@ -93,11 +90,8 @@ - - ..\..\packages\System.Text.Encoding.CodePages.8.0.0\lib\netstandard2.0\System.Text.Encoding.CodePages.dll - - - ..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + ..\..\packages\System.Text.Encoding.CodePages.6.0.0\lib\net461\System.Text.Encoding.CodePages.dll ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll diff --git a/source/OpenBveApi/Packages/Packages.Loksim3D.cs b/source/OpenBveApi/Packages/Packages.Loksim3D.cs index 0fafa37c50..0d1afcf5de 100644 --- a/source/OpenBveApi/Packages/Packages.Loksim3D.cs +++ b/source/OpenBveApi/Packages/Packages.Loksim3D.cs @@ -67,6 +67,7 @@ internal static Package Parse(XmlDocument currentXML, string fileName, ref strin } break; case "DeleteFiles": + break; case "DeinstallPackages": break; } diff --git a/source/OpenBveApi/Packages/Packages.cs b/source/OpenBveApi/Packages/Packages.cs index 2d525b710c..b6a5d50f28 100644 --- a/source/OpenBveApi/Packages/Packages.cs +++ b/source/OpenBveApi/Packages/Packages.cs @@ -239,7 +239,7 @@ public static void ExtractPackage(Package currentPackage, string extractionDirec using (Stream stream = File.OpenRead(currentPackage.PackageFile)) { - var reader = ArchiveFactory.OpenArchive(stream); + var reader = ArchiveFactory.Open(stream); List PackageFiles = new List(); j = reader.Entries.Count(); foreach (var archiveEntry in reader.Entries) @@ -256,7 +256,7 @@ public static void ExtractPackage(Package currentPackage, string extractionDirec else { //Extract everything else, preserving directory structure - archiveEntry.WriteToDirectory(extractionDirectory, new ExtractionOptions(true, true)); + archiveEntry.WriteToDirectory(extractionDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true }); //We don't want to add directories to the list of files if (!archiveEntry.IsDirectory) { @@ -308,7 +308,7 @@ public static void CreatePackage(Package currentPackage, CompressionType compres string fp = string.Empty; try { - using (FileStream zip = File.OpenWrite(packageFile)) + using (var zip = File.OpenWrite(packageFile)) { ArchiveType type; SharpCompress.Common.CompressionType compression; @@ -331,7 +331,7 @@ public static void CreatePackage(Package currentPackage, CompressionType compres compression = SharpCompress.Common.CompressionType.LZMA; break; } - using (var zipWriter = WriterFactory.OpenWriter(zip, type, new WriterOptions(compression))) + using (var zipWriter = WriterFactory.Open(zip, type, compression)) { if (packageFiles != null && packageFiles.Count > 0) { @@ -455,14 +455,14 @@ public static Package ReadPackage(string packageFile) { try { - var reader = ReaderFactory.OpenReader(stream); + var reader = ReaderFactory.Open(stream); while (reader.MoveToNextEntry()) { //Search for the package.xml file- This must be located in the archive root if (reader.Entry.Key.ToLowerInvariant() == "package.xml" && !InfoFound) { - reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions(true, true)); + reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true }); //Load the XML file InfoFound = true; XmlSerializer listReader = new XmlSerializer(typeof(SerializedPackage)); @@ -474,7 +474,7 @@ public static Package ReadPackage(string packageFile) if (reader.Entry.Key.ToLowerInvariant() == "packageinfo.xml" && packageFile.ToLowerInvariant().EndsWith(".l3dpack") && !InfoFound) { - reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions(true, true)); + reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true }); //Load the XML file try { @@ -496,7 +496,7 @@ public static Package ReadPackage(string packageFile) if (reader.Entry.Key.ToLowerInvariant() == ImageFile && currentPackage.PackageImage == null) { //Extract the package.png to the uniquely assigned temp directory - reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions(true, true)); + reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true }); try { packageImage = Image.FromFile(Path.CombineFile(TempDirectory, ImageFile)); @@ -515,7 +515,7 @@ public static Package ReadPackage(string packageFile) if (reader.Entry.Key.ToLowerInvariant() == "package.rtf") { //Extract the package.rtf description file to the uniquely assigned temp directory - reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions(true, true)); + reader.WriteEntryToDirectory(TempDirectory, new ExtractionOptions { ExtractFullPath = true, Overwrite = true }); //PackageDescription.LoadFile(OpenBveApi.Path.CombineFile(TempDirectory, "package.rtf")); } diff --git a/source/OpenBveApi/Runtime/Train/AIData.cs b/source/OpenBveApi/Runtime/Train/AIData.cs index 05a5566f02..e744ab19ed 100644 --- a/source/OpenBveApi/Runtime/Train/AIData.cs +++ b/source/OpenBveApi/Runtime/Train/AIData.cs @@ -17,9 +17,9 @@ public class AIData /// The driver handles. public AIData(Handles handles) { - MyHandles = handles; - MyResponse = AIResponse.None; - TimeElapsed = 0; + this.MyHandles = handles; + this.MyResponse = AIResponse.None; + this.TimeElapsed = 0; } /// Creates a new instance of this class. @@ -27,9 +27,9 @@ public AIData(Handles handles) /// The elapsed time public AIData(Handles handles, double timeElapsed) { - MyHandles = handles; - MyResponse = AIResponse.None; - TimeElapsed = timeElapsed; + this.MyHandles = handles; + this.MyResponse = AIResponse.None; + this.TimeElapsed = timeElapsed; } /// Gets or sets the driver handles. diff --git a/source/OpenBveApi/Sounds/Sounds.cs b/source/OpenBveApi/Sounds/Sounds.cs index c6e670d4e8..0d767e515e 100644 --- a/source/OpenBveApi/Sounds/Sounds.cs +++ b/source/OpenBveApi/Sounds/Sounds.cs @@ -10,16 +10,17 @@ namespace OpenBveApi.Sounds { * Modifications can be made at will. * ---------------------------------------- */ + // --- structures --- + /// Represents a sound. - public class Sound - { + public class Sound { + // --- members --- /// The number of samples per second. - public readonly int SampleRate; + private readonly int MySampleRate; /// The number of bits per sample. Allowed values are 8 or 16. - public readonly int BitsPerSample; + private readonly int MyBitsPerSample; /// The PCM sound data per channel. For 8 bits per sample, samples are unsigned from 0 to 255. For 16 bits per sample, samples are signed from -32768 to 32767 and in little endian byte order. - public readonly byte[][] Bytes; - + private readonly byte[][] MyBytes; // --- constructors --- /// Creates a new instance of this class. /// The number of samples per second. @@ -53,13 +54,22 @@ public Sound(int sampleRate, int bitsPerSample, byte[][] bytes) { throw new ArgumentException("The data bytes of the channels are of unequal length."); } } - SampleRate = sampleRate; - BitsPerSample = bitsPerSample; - Bytes = bytes; + MySampleRate = sampleRate; + MyBitsPerSample = bitsPerSample; + MyBytes = bytes; } - + // --- properties --- + /// Gets the number of samples per second. + public int SampleRate => MySampleRate; + + /// Gets the number of bits per sample. Allowed values are 8 or 16. + public int BitsPerSample => MyBitsPerSample; + + /// Gets the PCM sound data per channel. For 8 bits per sample, samples are unsigned from 0 to 255. For 16 bits per sample, samples are signed from -32768 to 32767 and in little endian byte order. + public byte[][] Bytes => MyBytes; + /// Gets the duration of the sound in seconds. - public double Duration => 8.0 * Bytes[0].Length / BitsPerSample / SampleRate; + public double Duration => 8.0 * MyBytes[0].Length / MyBitsPerSample / MySampleRate; // --- operators --- /// Checks whether two sound are equal. @@ -70,13 +80,13 @@ public Sound(int sampleRate, int bitsPerSample, byte[][] bytes) { if (ReferenceEquals(a, b)) return true; if (a is null) return false; if (b is null) return false; - if (a.SampleRate != b.SampleRate) return false; - if (a.BitsPerSample != b.BitsPerSample) return false; - if (a.Bytes.Length != b.Bytes.Length) return false; - for (int i = 0; i < a.Bytes.Length; i++) { - if (a.Bytes[i].Length != b.Bytes[i].Length) return false; - for (int j = 0; j < a.Bytes[i].Length; j++) { - if (a.Bytes[i][j] != b.Bytes[i][j]) return false; + if (a.MySampleRate != b.MySampleRate) return false; + if (a.MyBitsPerSample != b.MyBitsPerSample) return false; + if (a.MyBytes.Length != b.MyBytes.Length) return false; + for (int i = 0; i < a.MyBytes.Length; i++) { + if (a.MyBytes[i].Length != b.MyBytes[i].Length) return false; + for (int j = 0; j < a.MyBytes[i].Length; j++) { + if (a.MyBytes[i][j] != b.MyBytes[i][j]) return false; } } return true; @@ -89,13 +99,13 @@ public Sound(int sampleRate, int bitsPerSample, byte[][] bytes) { if (ReferenceEquals(a, b)) return false; if (a is null) return true; if (b is null) return true; - if (a.SampleRate != b.SampleRate) return true; - if (a.BitsPerSample != b.BitsPerSample) return true; - if (a.Bytes.Length != b.Bytes.Length) return true; - for (int i = 0; i < a.Bytes.Length; i++) { - if (a.Bytes[i].Length != b.Bytes[i].Length) return true; - for (int j = 0; j < a.Bytes[i].Length; j++) { - if (a.Bytes[i][j] != b.Bytes[i][j]) return true; + if (a.MySampleRate != b.MySampleRate) return true; + if (a.MyBitsPerSample != b.MyBitsPerSample) return true; + if (a.MyBytes.Length != b.MyBytes.Length) return true; + for (int i = 0; i < a.MyBytes.Length; i++) { + if (a.MyBytes[i].Length != b.MyBytes[i].Length) return true; + for (int j = 0; j < a.MyBytes[i].Length; j++) { + if (a.MyBytes[i][j] != b.MyBytes[i][j]) return true; } } return false; @@ -108,13 +118,13 @@ public override bool Equals(object obj) { if (obj is null) return false; if (!(obj is Sound)) return false; Sound x = (Sound)obj; - if (SampleRate != x.SampleRate) return false; - if (BitsPerSample != x.BitsPerSample) return false; - if (Bytes.Length != x.Bytes.Length) return false; - for (int i = 0; i < Bytes.Length; i++) { - if (Bytes[i].Length != x.Bytes[i].Length) return false; - for (int j = 0; j < Bytes[i].Length; j++) { - if (Bytes[i][j] != x.Bytes[i][j]) return false; + if (MySampleRate != x.MySampleRate) return false; + if (MyBitsPerSample != x.MyBitsPerSample) return false; + if (MyBytes.Length != x.MyBytes.Length) return false; + for (int i = 0; i < MyBytes.Length; i++) { + if (MyBytes[i].Length != x.MyBytes[i].Length) return false; + for (int j = 0; j < MyBytes[i].Length; j++) { + if (MyBytes[i][j] != x.MyBytes[i][j]) return false; } } return true; @@ -131,32 +141,32 @@ public byte[] GetMonoMix() * Convert integer samples to floating-point samples. */ float[][] samples; - if (Bytes.Length == 1 || Bytes[0].Length == 0) + if (MyBytes.Length == 1 || MyBytes[0].Length == 0) { - return Bytes[0]; + return MyBytes[0]; } switch (BitsPerSample) { case 8: - samples = new float[Bytes.Length][]; - for (int i = 0; i < Bytes.Length; i++) + samples = new float[MyBytes.Length][]; + for (int i = 0; i < MyBytes.Length; i++) { - samples[i] = new float[Bytes[i].Length]; - for (int j = 0; j < Bytes[i].Length; j++) + samples[i] = new float[MyBytes[i].Length]; + for (int j = 0; j < MyBytes[i].Length; j++) { - byte value = Bytes[i][j]; + byte value = MyBytes[i][j]; samples[i][j] = (value - 128.0f) / (value < 128 ? 128.0f : 127.0f); } } break; case 16: - samples = new float[Bytes.Length][]; - for (int i = 0; i < Bytes.Length; i++) + samples = new float[MyBytes.Length][]; + for (int i = 0; i < MyBytes.Length; i++) { - samples[i] = new float[Bytes[i].Length >> 1]; - for (int j = 0; j + 1 < Bytes[i].Length; j += 2) + samples[i] = new float[MyBytes[i].Length >> 1]; + for (int j = 0; j + 1 < MyBytes[i].Length; j += 2) { - short value = (short)(ushort)(Bytes[i][j] | (Bytes[i][j + 1] << 8)); + short value = (short)(ushort)(MyBytes[i][j] | (MyBytes[i][j + 1] << 8)); samples[i][j >> 1] = value / (value < 0 ? 32768.0f : 32767.0f); } } diff --git a/source/OpenBveApi/System/BaseOptions.OptionsKey.cs b/source/OpenBveApi/System/BaseOptions.OptionsKey.cs index 9d93fac265..2854a9c98d 100644 --- a/source/OpenBveApi/System/BaseOptions.OptionsKey.cs +++ b/source/OpenBveApi/System/BaseOptions.OptionsKey.cs @@ -38,9 +38,6 @@ public enum OptionsKey NearClipCab, NearClipBase, AutoReloadObjects, - CameraInteriorTransition, - CameraExteriorTransition, - CameraTransitionSpeed, // Quality Interpolation, AnisotropicFilteringLevel, @@ -56,7 +53,6 @@ public enum OptionsKey ShadowStrength, ShadowBias, ShadowNormalBias, - ShadowFilterCascades, LightAzimuth, LightElevation, diff --git a/source/OpenBveApi/System/BaseOptions.cs b/source/OpenBveApi/System/BaseOptions.cs index 135f660222..f295d687b3 100644 --- a/source/OpenBveApi/System/BaseOptions.cs +++ b/source/OpenBveApi/System/BaseOptions.cs @@ -1,4 +1,3 @@ -using OpenBveApi.Colors; using OpenBveApi.Graphics; using OpenBveApi.Objects; using OpenBveApi.Routes; @@ -91,11 +90,9 @@ public abstract class BaseOptions /// Shadow darkness strength. 0.0 = invisible, 1.0 = full black. public double ShadowStrength = 0.7; /// Shadow bias to prevent shadow acne. - public double ShadowBias = 0.000005; // default synced to 0.000005 + public double ShadowBias = 0.000050; // default synced to 0.000050 /// Shadow normal bias (slope scale multiplier) to perfectly cure acne on curved/thin meshes. public double ShadowNormalBias = 2.0; - /// Whether to filter shadow casters per cascade to improve performance. - public bool ShadowFilterCascades = true; /// The sun azimuth in degrees @@ -144,9 +141,6 @@ public abstract class BaseOptions public double NearClipCab = 0.025; /// The near clipping plane for the base renderer public double NearClipBase = 0.2; - /// The color used by the renderer when issuing GL.Clear() - /// Not saved - public Color24 ClearColor = new Color24(170, 170, 170); /// Saves the options to the specified filename /// The filename to save the options to diff --git a/source/OpenBveApi/System/FileSystem.cs b/source/OpenBveApi/System/FileSystem.cs index fbe2236259..c25d4146b3 100644 --- a/source/OpenBveApi/System/FileSystem.cs +++ b/source/OpenBveApi/System/FileSystem.cs @@ -1,12 +1,10 @@ -using Microsoft.Win32; -using OpenBveApi.Hosts; -using OpenBveApi.Interface; using System; using System.IO; -using System.Linq; using System.Reflection; using System.Text; using System.Windows.Forms; +using Microsoft.Win32; +using OpenBveApi.Hosts; // ReSharper disable PossibleNullReferenceException // ReSharper disable AssignNullToNotNullAttribute @@ -508,90 +506,5 @@ public void AppendToLogFile(string text, bool addTimestamp = true) { // ignored } } - - /// Gets the absolute Railway folder for a given route file - /// The absolute on-disk path of the railway folder - public string GetRailwayFolder(string routeFile, string startupPath) - { - try - { - string currentFolder = Path.GetDirectoryName(routeFile); - - while (true) - { - string Subfolder = Path.CombineDirectory(currentFolder, "Railway"); - if (Directory.Exists(Subfolder)) - { - if (Directory.EnumerateDirectories(Subfolder).Any() || Directory.EnumerateFiles(Subfolder).Any()) - { - //HACK: Ignore completely empty directories - //Doesn't handle wrong directories, or those with stuff missing, TODO..... - AppendToLogFile(Subfolder + " : Railway folder found."); - return Subfolder; - } - - AppendToLogFile(Subfolder + " : Railway folder candidate rejected- Directory empty."); - } - - if (currentFolder == null) continue; - DirectoryInfo directoryInfo = Directory.GetParent(currentFolder); - if (directoryInfo == null) break; - currentFolder = directoryInfo.FullName; - } - } - catch - { - // ignored - } - - // If the Route, Object and Sound folders exist, but are not in a railway folder..... - try - { - string currentFolder = Path.GetDirectoryName(routeFile); - if (currentFolder == null) - { - // Unlikely to work, but attempt to make the best of it - AppendToLogFile("The route file appears to be stored on a root path- Returning the " + Translations.GetInterfaceString(HostApplication.OpenBve, new[] { "program", "title" }) + " startup path."); - return Application.StartupPath; - } - string candidate = null; - while (true) - { - string routeFolder = Path.CombineDirectory(currentFolder, "Route"); - string objectFolder = Path.CombineDirectory(currentFolder, "Object"); - string soundFolder = Path.CombineDirectory(currentFolder, "Sound"); - if (Directory.Exists(routeFolder) && Directory.Exists(objectFolder) && Directory.Exists(soundFolder)) - { - AppendToLogFile(currentFolder + " : Railway folder found."); - return currentFolder; - } - - if (Directory.Exists(routeFolder) && Directory.Exists(objectFolder)) - { - candidate = currentFolder; - } - - DirectoryInfo directoryInfo = Directory.GetParent(currentFolder); - if (directoryInfo == null) - { - if (candidate != null) - { - AppendToLogFile(currentFolder + " : The best candidate for the Railway folder has been selected- Sound folder not detected."); - return candidate; - } - - break; - } - - currentFolder = directoryInfo.FullName; - } - } - catch - { - // ignored - } - AppendToLogFile("No Railway folder found- Returning the " + Translations.GetInterfaceString(HostApplication.OpenBve, new[] { "program", "title" }) + " startup path."); - return startupPath; - } } } diff --git a/source/OpenBveApi/System/Text.cs b/source/OpenBveApi/System/Text.cs index 285a4cdb09..ed29b15a10 100644 --- a/source/OpenBveApi/System/Text.cs +++ b/source/OpenBveApi/System/Text.cs @@ -109,10 +109,6 @@ public static bool IsJapanese(this string Name) { /// The string for which all line-endings should be converted to CR-LF /// The converted StringBuilder public static string ConvertNewlinesToCrLf(this string Text) { - if (string.IsNullOrEmpty(Text)) - { - return string.Empty; - } StringBuilder Builder = new StringBuilder(); for (int i = 0; i < Text.Length; i++) { int a = char.ConvertToUtf32(Text, i); diff --git a/source/OpenBveApi/Textures/Textures.ClipRegion.cs b/source/OpenBveApi/Textures/Textures.ClipRegion.cs index b2db977b0e..39e79ec0d1 100644 --- a/source/OpenBveApi/Textures/Textures.ClipRegion.cs +++ b/source/OpenBveApi/Textures/Textures.ClipRegion.cs @@ -7,14 +7,27 @@ namespace OpenBveApi.Textures /// Represents a region in a texture to be extracted. public class TextureClipRegion { + // --- members --- + /// The left coordinate. + private readonly int MyLeft; + /// The top coordinate. + private readonly int MyTop; + /// The width. + private readonly int MyWidth; + /// The height. + private readonly int MyHeight; + // --- properties --- /// Gets the left coordinate. - public readonly int Left; + public int Left => MyLeft; + /// Gets the top coordinate. - public readonly int Top; + public int Top => MyTop; + /// Gets the width. - public readonly int Width; + public int Width => MyWidth; + /// Gets the height. - public readonly int Height; + public int Height => MyHeight; // --- constructors --- /// Creates a new clip region. @@ -36,10 +49,10 @@ public TextureClipRegion(int left, int top, int width, int height) throw new ArgumentException("The width or height are non-positive."); } - Left = left; - Top = top; - Width = width; - Height = height; + MyLeft = left; + MyTop = top; + MyWidth = width; + MyHeight = height; } // --- operators --- @@ -52,10 +65,10 @@ public TextureClipRegion(int left, int top, int width, int height) if (ReferenceEquals(a, b)) return true; if (a is null) return false; if (b is null) return false; - if (a.Left != b.Left) return false; - if (a.Top != b.Top) return false; - if (a.Width != b.Width) return false; - if (a.Height != b.Height) return false; + if (a.MyLeft != b.MyLeft) return false; + if (a.MyTop != b.MyTop) return false; + if (a.MyWidth != b.MyWidth) return false; + if (a.MyHeight != b.MyHeight) return false; return true; } @@ -68,10 +81,10 @@ public TextureClipRegion(int left, int top, int width, int height) if (ReferenceEquals(a, b)) return false; if (a is null) return true; if (b is null) return true; - if (a.Left != b.Left) return true; - if (a.Top != b.Top) return true; - if (a.Width != b.Width) return true; - if (a.Height != b.Height) return true; + if (a.MyLeft != b.MyLeft) return true; + if (a.MyTop != b.MyTop) return true; + if (a.MyWidth != b.MyWidth) return true; + if (a.MyHeight != b.MyHeight) return true; return false; } @@ -84,10 +97,10 @@ public override bool Equals(object obj) if (obj is null) return false; if (!(obj is TextureClipRegion)) return false; TextureClipRegion x = (TextureClipRegion) obj; - if (Left != x.Left) return false; - if (Top != x.Top) return false; - if (Width != x.Width) return false; - if (Height != x.Height) return false; + if (MyLeft != x.MyLeft) return false; + if (MyTop != x.MyTop) return false; + if (MyWidth != x.MyWidth) return false; + if (MyHeight != x.MyHeight) return false; return true; } } diff --git a/source/OpenBveApi/Textures/Textures.Functions.cs b/source/OpenBveApi/Textures/Textures.Functions.cs index db5f18ddda..6449a27fce 100644 --- a/source/OpenBveApi/Textures/Textures.Functions.cs +++ b/source/OpenBveApi/Textures/Textures.Functions.cs @@ -52,15 +52,8 @@ internal static Texture ExtractClipRegion(Texture texture, TextureClipRegion reg throw new ArgumentException(); } int width = texture.Width; - byte[] bytes; - if (texture.Bytes == null) - { - texture.Origin.GetTexture(out texture); - } - - bytes = texture.Bytes; - - int clipLeft = region.Left; + byte[] bytes = texture.Bytes; + int clipLeft = region.Left; int clipTop = region.Top; int clipWidth = region.Width; int clipHeight = region.Height; @@ -187,7 +180,6 @@ internal static Texture ApplyTransparentColor(Texture texture, Color24? color) newFrames[i] = ApplyTransparentColor(texture.Bytes, texture.PixelFormat, texture.Width, texture.Height, color.Value, ref usedTransparentColor); texture.CurrentFrame++; } - texture.CurrentFrame = 0; if (usedTransparentColor) { @@ -216,57 +208,33 @@ private static byte[] ApplyTransparentTexture(byte[] source, PixelFormat pixelFo case PixelFormat.Grayscale: for (int i = 0; i < source.Length; i++, targetIndex += 4) { - + Color24 c = transparencyTexture.GetPixel(i); target[targetIndex] = source[i]; target[targetIndex + 1] = source[i]; target[targetIndex + 2] = source[i]; - if (transparencyTexture.PixelFormat == PixelFormat.Grayscale || transparencyTexture.PixelFormat == PixelFormat.RGB) - { - Color24 c = transparencyTexture.GetPixel(i); - target[targetIndex + 3] = (byte)(255 * c.GetBrightness()); - } - else - { - target[i + 3] = System.Math.Min(source[i + 1], transparencyTexture.GetAlpha(i)); - } - + target[targetIndex + 3] = (byte)(255 * c.GetBrightness()); } break; case PixelFormat.GrayscaleAlpha: for (int i = 0; i < source.Length; i += 2, targetIndex += 4) { int pix = i / 2; + Color24 c = transparencyTexture.GetPixel(pix); target[targetIndex] = source[i]; target[targetIndex + 1] = source[i]; target[targetIndex + 2] = source[i]; - if (transparencyTexture.PixelFormat == PixelFormat.Grayscale || transparencyTexture.PixelFormat == PixelFormat.RGB) - { - Color24 c = transparencyTexture.GetPixel(pix); - target[targetIndex + 3] = (byte)(255 * c.GetBrightness()); - } - else - { - target[i + 3] = System.Math.Min(source[i + 1], transparencyTexture.GetAlpha(pix)); - } + target[targetIndex + 3] = (byte)(255 * c.GetBrightness()); } break; case PixelFormat.RGB: for (int i = 0; i < source.Length; i += 3, targetIndex += 4) { int pix = i / 3; + Color24 c = transparencyTexture.GetPixel(pix); target[targetIndex] = source[i]; target[targetIndex + 1] = source[i + 1]; target[targetIndex + 2] = source[i + 2]; - if (transparencyTexture.PixelFormat == PixelFormat.Grayscale || - transparencyTexture.PixelFormat == PixelFormat.RGB) - { - Color24 c = transparencyTexture.GetPixel(pix); - target[targetIndex + 3] = (byte)(255 * c.GetBrightness()); - } - else - { - target[i + 3] = System.Math.Min(source[i + 3], transparencyTexture.GetAlpha(pix)); - } + target[targetIndex + 3] = (byte)(255 * c.GetBrightness()); } break; case PixelFormat.RGBAlpha: @@ -279,19 +247,11 @@ private static byte[] ApplyTransparentTexture(byte[] source, PixelFormat pixelFo for (int i = 4; i < source.Length; i += 4) { int pix = i / 4; + Color24 c = transparencyTexture.GetPixel(pix); target[i + 0] = source[i + 0]; target[i + 1] = source[i + 1]; target[i + 2] = source[i + 2]; - if (transparencyTexture.PixelFormat == PixelFormat.Grayscale || transparencyTexture.PixelFormat == PixelFormat.RGB) - { - Color24 c = transparencyTexture.GetPixel(pix); - target[i + 3] = (byte)(255 * c.GetBrightness()); - } - else - { - target[i + 3] = System.Math.Min(source[i + 3], transparencyTexture.GetAlpha(pix)); - } - + target[i + 3] = (byte)(255 * c.GetBrightness()); } break; } diff --git a/source/OpenBveApi/Textures/Textures.TextureParameters.cs b/source/OpenBveApi/Textures/Textures.TextureParameters.cs index 468ff19726..605e4f97b6 100644 --- a/source/OpenBveApi/Textures/Textures.TextureParameters.cs +++ b/source/OpenBveApi/Textures/Textures.TextureParameters.cs @@ -9,11 +9,20 @@ public class TextureParameters { // --- members --- /// The region in the texture to be extracted, or a null reference for the entire texture. - public readonly TextureClipRegion ClipRegion; + private readonly TextureClipRegion myClipRegion; /// The color in the texture that should become transparent, or a null reference for no transparent color. - public readonly Color24? TransparentColor; + private readonly Color24? myTransparentColor; /// The alpha channel texture - public Texture TransparencyTexture; + private readonly Texture myTransparencyTexture; + // --- properties --- + /// Gets the region in the texture to be extracted, or a null reference for the entire texture. + public TextureClipRegion ClipRegion => myClipRegion; + + /// Gets the color in the texture that should become transparent, or a null reference for no transparent color. + public Color24? TransparentColor => myTransparentColor; + + /// Gets the separate alpha channel texture + public Texture TransparencyTexture => myTransparencyTexture; /// Texture parameters, which apply no changes. public static TextureParameters NoChange = new TextureParameters(null, null); @@ -24,9 +33,9 @@ public class TextureParameters /// The texture to be applied to the alpha channel public TextureParameters(TextureClipRegion clipRegion, Color24? transparentColor, Texture transparencyTexture = null) { - ClipRegion = clipRegion; - TransparentColor = transparentColor; - TransparencyTexture = transparencyTexture; + myClipRegion = clipRegion; + myTransparentColor = transparentColor; + myTransparencyTexture = transparencyTexture; } // --- operators --- @@ -39,8 +48,8 @@ public TextureParameters(TextureClipRegion clipRegion, Color24? transparentColor if (ReferenceEquals(a, b)) return true; if (a is null) return false; if (b is null) return false; - if (a.ClipRegion != b.ClipRegion) return false; - if (a.TransparentColor != b.TransparentColor) return false; + if (a.myClipRegion != b.myClipRegion) return false; + if (a.myTransparentColor != b.myTransparentColor) return false; return true; } @@ -53,8 +62,8 @@ public TextureParameters(TextureClipRegion clipRegion, Color24? transparentColor if (ReferenceEquals(a, b)) return false; if (a is null) return true; if (b is null) return true; - if (a.ClipRegion != b.ClipRegion) return true; - if (a.TransparentColor != b.TransparentColor) return true; + if (a.myClipRegion != b.myClipRegion) return true; + if (a.myTransparentColor != b.myTransparentColor) return true; return false; } @@ -67,8 +76,8 @@ public override bool Equals(object obj) if (obj is null) return false; if (!(obj is TextureParameters)) return false; TextureParameters x = (TextureParameters) obj; - if (ClipRegion != x.ClipRegion) return false; - if (TransparentColor != x.TransparentColor) return false; + if (myClipRegion != x.myClipRegion) return false; + if (myTransparentColor != x.myTransparentColor) return false; return true; } } diff --git a/source/OpenBveApi/Textures/Textures.TransparencyType.cs b/source/OpenBveApi/Textures/Textures.TransparencyType.cs index 318dd63684..b4efe25af0 100644 --- a/source/OpenBveApi/Textures/Textures.TransparencyType.cs +++ b/source/OpenBveApi/Textures/Textures.TransparencyType.cs @@ -8,8 +8,6 @@ public enum TextureTransparencyType /// All pixels in the texture are either fully opaque or fully transparent. Partial = 2, /// Some pixels in the texture are neither fully opaque nor fully transparent. - Alpha = 3, - /// All pixels in the texture are fully transparent - Transparent + Alpha = 3 } } diff --git a/source/OpenBveApi/Textures/Textures.cs b/source/OpenBveApi/Textures/Textures.cs index 6e029c0f54..7e78be0188 100644 --- a/source/OpenBveApi/Textures/Textures.cs +++ b/source/OpenBveApi/Textures/Textures.cs @@ -11,13 +11,13 @@ namespace OpenBveApi.Textures { public class Texture { /// The size of the texture in pixels - public Vector2 Size; + private Vector2 MySize; /// The pixel format of the texture. - public readonly PixelFormat PixelFormat; + private readonly PixelFormat MyPixelFormat; /// The texture data. Pixels are stored row-based from top to bottom, and within a row from left to right. For 32 bits per pixel, four bytes are used in the order red, green, blue and alpha. private readonly byte[][] MyBytes; /// The restricted color palette for this texture, or a null reference if the texture was 24/ 32 bit originally - public readonly Color24[] Palette; + private readonly Color24[] MyPalette; /// Whether the texture is invalid and should be ignored /// Set when loading the texture fails unexpectedly public bool Ignore; @@ -49,7 +49,7 @@ public class Texture /// The frame public Color24 GetPixel(int pix, int frame = 0) { - if (pix > Size.X * Size.Y) + if (pix > MySize.X * MySize.Y) { throw new ArgumentException("Pixel is outside the bounds of the image"); } @@ -59,39 +59,20 @@ public Color24 GetPixel(int pix, int frame = 0) { case PixelFormat.Grayscale: firstByte = pix; - return new Color24(MyBytes[frame][firstByte], MyBytes[frame][firstByte], MyBytes[frame][firstByte]); + break; case PixelFormat.GrayscaleAlpha: firstByte = 2 * pix; - return new Color24(MyBytes[frame][firstByte], MyBytes[frame][firstByte], MyBytes[frame][firstByte]); + break; case PixelFormat.RGB: firstByte = 3 * pix; - return new Color24(MyBytes[frame][firstByte], MyBytes[frame][firstByte + 1], MyBytes[frame][firstByte + 2]); + break; case PixelFormat.RGBAlpha: firstByte = 4 * pix; - return new Color24(MyBytes[frame][firstByte], MyBytes[frame][firstByte + 1], MyBytes[frame][firstByte + 2]); + break; default: throw new Exception("Unable to get a pixel value with invalid data."); } - } - - /// Gets the alpha value of the given pixel - /// The pixel index - /// The frame - /// - public byte GetAlpha(int pix, int frame = 0) - { - switch (PixelFormat) - { - case PixelFormat.Grayscale: - case PixelFormat.RGB: - return 255; - case PixelFormat.GrayscaleAlpha: - return MyBytes[frame][2 * pix + 1]; - case PixelFormat.RGBAlpha: - return MyBytes[frame][4 * pix + 3]; - default: - return 255; - } + return new Color24(MyBytes[frame][firstByte], MyBytes[frame][firstByte + 1], MyBytes[frame][firstByte + 2]); } /// Creates a new instance of this class. @@ -117,12 +98,12 @@ public Texture(int width, int height, PixelFormat pixelFormat, byte[] bytes, Col this.Origin = new ByteArrayOrigin(width, height, bytes); this.MyOpenGlTextures = new OpenGlTexture[1][]; this.MyOpenGlTextures[0] = new[] {new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture()}; - this.Size.X = width; - this.Size.Y = height; - this.PixelFormat = pixelFormat; + this.MySize.X = width; + this.MySize.Y = height; + this.MyPixelFormat = pixelFormat; this.MyBytes = new byte[1][]; this.MyBytes[0] = bytes; - this.Palette = palette; + this.MyPalette = palette; } /// Creates a new instance of this class. @@ -147,11 +128,11 @@ public Texture(int width, int height, PixelFormat pixelFormat, byte[][] bytes, d } Origin = new ByteArrayOrigin(width, height, bytes, frameInterval); - Size.X = width; - Size.Y = height; - PixelFormat = pixelFormat; + MySize.X = width; + MySize.Y = height; + MyPixelFormat = pixelFormat; MyBytes = bytes; - Palette = null; + MyPalette = null; MultipleFrames = true; FrameInterval = frameInterval; TotalFrames = bytes.Length; @@ -169,8 +150,6 @@ public Texture(int width, int height, PixelFormat pixelFormat, byte[][] bytes, d public Texture(string path, TextureParameters parameters, Hosts.HostInterface currentHost) { Origin = new PathOrigin(path, parameters, currentHost); - Origin.GetTexture(out Texture t); - PixelFormat = t.PixelFormat; MyOpenGlTextures = new OpenGlTexture[1][]; MyOpenGlTextures[0] = new[] {new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture(), new OpenGlTexture()}; @@ -216,29 +195,37 @@ public Texture(TextureOrigin origin) /// Gets the width of the texture in pixels. public int Width { - get => (int)Size.X; - set => Size.X = value; + get => (int)MySize.X; + set => MySize.X = value; } /// Gets the height of the texture in pixels. public int Height { - get => (int)Size.Y; - set => Size.Y = value; + get => (int)MySize.Y; + set => MySize.Y = value; + } + + /// Gets the size of the texture in pixels + public Vector2 Size + { + get => MySize; + set => MySize = value; } - + /// Gets the aspect ratio of the texture - public double AspectRatio => Size.X / Size.Y; - + public double AspectRatio => MySize.X / MySize.Y; + + /// Gets the pixel format. + public PixelFormat PixelFormat => MyPixelFormat; + + /// Gets the restricted color palette for this texture, or a null reference if not applicable + public Color24[] Palette => MyPalette; + /// Gets the texture data. Pixels are stored row-based from top to bottom, and within a row from left to right. For 32 bits per pixel, four bytes are used in the order red, green, blue and alpha. public byte[] Bytes { get { - if (MyBytes == null && Origin != null) - { - Origin.GetTexture(out Texture t); - return t.Bytes; - } if (MultipleFrames == false) { return MyBytes[0]; @@ -272,8 +259,9 @@ public OpenGlTexture[] OpenGlTextures if (b is null) return false; if (a.MultipleFrames != b.MultipleFrames) return false; if (a.Origin != b.Origin) return false; - if (a.Size != b.Size) return false; - if (a.PixelFormat != b.PixelFormat) return false; + if (a.MySize.X != b.MySize.X) return false; + if (a.MySize.Y != b.MySize.Y) return false; + if (a.MyPixelFormat != b.MyPixelFormat) return false; if (a.MyBytes.Length != b.MyBytes.Length) return false; for (int i = 0; i < a.MyBytes.Length; i++) { @@ -293,8 +281,9 @@ public OpenGlTexture[] OpenGlTextures if (b is null) return true; if (a.MultipleFrames != b.MultipleFrames) return true; if (a.Origin != b.Origin) return true; - if (a.Size != b.Size) return true; - if (a.PixelFormat != b.PixelFormat) return true; + if (a.MySize.X != b.MySize.X) return true; + if (a.MySize.Y != b.MySize.Y) return true; + if (a.MyPixelFormat != b.MyPixelFormat) return true; if (a.MyBytes == null) { return b.MyBytes != null; @@ -317,8 +306,9 @@ public override bool Equals(object obj) if (!(obj is Texture x)) return false; if (MultipleFrames != x.MultipleFrames) return false; if (Origin != x.Origin) return false; - if (Size != x.Size) return false; - if (PixelFormat != x.PixelFormat) return false; + if (MySize.X != x.MySize.X) return false; + if (MySize.Y != x.MySize.Y) return false; + if (MyPixelFormat != x.MyPixelFormat) return false; if (MyBytes == null) { return x.MyBytes == null; @@ -353,46 +343,30 @@ public TextureTransparencyType GetTransparencyType() } knownTransparencyType = true; - switch (PixelFormat) + switch (MyPixelFormat) { case PixelFormat.RGB: transparencyType = TextureTransparencyType.Opaque; break; case PixelFormat.RGBAlpha: - transparencyType = TextureTransparencyType.Opaque; for (int i = 3; i < this.MyBytes[CurrentFrame].Length; i += 4) { - - switch (MyBytes[CurrentFrame][i]) + if (this.MyBytes[CurrentFrame][i] != 255) { - case 0: - if (i == 3) + for (int j = i; j < this.MyBytes[CurrentFrame].Length; j += 4) + { + if (this.MyBytes[CurrentFrame][j] != 0 & this.MyBytes[CurrentFrame][j] != 255) { - transparencyType = TextureTransparencyType.Transparent; + transparencyType = TextureTransparencyType.Alpha; + return TextureTransparencyType.Alpha; } - else - { - if (transparencyType == TextureTransparencyType.Opaque) - { - transparencyType = TextureTransparencyType.Partial; - } - } - break; - case 255: - if (transparencyType != TextureTransparencyType.Opaque) - { - transparencyType = TextureTransparencyType.Partial; - } - // nothing - break; - default: - transparencyType = TextureTransparencyType.Alpha; - return transparencyType; + } + + transparencyType = TextureTransparencyType.Partial; + return TextureTransparencyType.Partial; } } - - return transparencyType; - + break; } return TextureTransparencyType.Opaque; } diff --git a/source/OpenBveApi/World/Transformations.cs b/source/OpenBveApi/World/Transformations.cs index 0f3a786d0e..34b68b6db4 100644 --- a/source/OpenBveApi/World/Transformations.cs +++ b/source/OpenBveApi/World/Transformations.cs @@ -1,4 +1,4 @@ -using OpenBveApi.Math; +using OpenBveApi.Math; namespace OpenBveApi.World { @@ -81,9 +81,9 @@ public Transformation(double Yaw, double Pitch, double Roll) /// public Transformation(Transformation Transformation, double Yaw, double Pitch, double Roll) { - X = new Vector3(Transformation.X); - Y = new Vector3(Transformation.Y); - Z = new Vector3(Transformation.Z); + X = new Vector3(Transformation?.X ?? Vector3.Right); + Y = new Vector3(Transformation?.Y ?? Vector3.Down); + Z = new Vector3(Transformation?.Z ?? Vector3.Forward); X.Rotate(Y, Yaw); Z.Rotate(Y, Yaw); // In the left-handed coordinate system, the clock-wise rotation is positive when the origin is viewed from the positive direction of the axis. @@ -99,9 +99,10 @@ public Transformation(Transformation Transformation, double Yaw, double Pitch, d /// The transformation to apply second public Transformation(Transformation firstTransformation, Transformation secondTransformation) { - X = new Vector3(firstTransformation.X); - Y = new Vector3(firstTransformation.Y); - Z = new Vector3(firstTransformation.Z); + X = new Vector3(firstTransformation?.X ?? Vector3.Right); + Y = new Vector3(firstTransformation?.Y ?? Vector3.Down); + Z = new Vector3(firstTransformation?.Z ?? Vector3.Forward); + secondTransformation = secondTransformation ?? NullTransformation; X.Rotate(secondTransformation); Y.Rotate(secondTransformation); Z.Rotate(secondTransformation); diff --git a/source/OpenBveApi/app.config b/source/OpenBveApi/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/OpenBveApi/app.config +++ b/source/OpenBveApi/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/OpenBveApi/packages.config b/source/OpenBveApi/packages.config index 82cc9c0a41..c9d8226501 100644 --- a/source/OpenBveApi/packages.config +++ b/source/OpenBveApi/packages.config @@ -1,14 +1,12 @@  - - + - - + \ No newline at end of file diff --git a/source/Plugins/Formats.Msts/app.config b/source/Plugins/Formats.Msts/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Formats.Msts/app.config +++ b/source/Plugins/Formats.Msts/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Formats.OpenBve/Block.cs b/source/Plugins/Formats.OpenBve/Block.cs index 4dd330866d..04b96797a6 100644 --- a/source/Plugins/Formats.OpenBve/Block.cs +++ b/source/Plugins/Formats.OpenBve/Block.cs @@ -178,7 +178,7 @@ public bool TryGetValue(T2 key, ref double value, NumberRange range = NumberRang value = newValue; return true; } - currentHost.AddMessage(MessageType.Warning, false, "Value " + s.Value + " is not a positive double in Key " + key + " in Section " + Key + " at line " + s.Key); + currentHost.AddMessage(MessageType.Warning, false, "Value " + s + " is not a positive double in Key " + key + " in Section " + Key + " at line " + s.Key); return false; case NumberRange.NonNegative: if (newValue >= 0) @@ -186,7 +186,7 @@ public bool TryGetValue(T2 key, ref double value, NumberRange range = NumberRang value = newValue; return true; } - currentHost.AddMessage(MessageType.Warning, false, "Value " + s.Value + " is not a non-negative double in Key " + key + " in Section " + Key + " at line " + s.Key); + currentHost.AddMessage(MessageType.Warning, false, "Value " + s + " is not a non-negative double in Key " + key + " in Section " + Key + " at line " + s.Key); return false; case NumberRange.NonZero: if (newValue != 0) @@ -194,11 +194,11 @@ public bool TryGetValue(T2 key, ref double value, NumberRange range = NumberRang value = newValue; return true; } - currentHost.AddMessage(MessageType.Warning, false, "Value " + s.Value + " is not a non-zero double in Key " + key + " in Section " + Key + " at line " + s.Key); + currentHost.AddMessage(MessageType.Warning, false, "Value " + s + " is not a non-zero double in Key " + key + " in Section " + Key + " at line " + s.Key); return false; } } - currentHost.AddMessage(MessageType.Warning, false, "Value " + s.Value + " is not a valid double in Key " + key + " in Section " + Key + " at line " + s.Key); + currentHost.AddMessage(MessageType.Warning, false, "Value " + s + " is not a valid double in Key " + key + " in Section " + Key + " at line " + s.Key); } return false; } @@ -233,7 +233,7 @@ public virtual bool TryGetValue(T2 key, ref int value, NumberRange range = Numbe value = newValue; return true; } - currentHost.AddMessage(MessageType.Warning, false, "Value " + s.Value + " is not a positive integer in Key " + key + " in Section " + Key + " at line " + s.Key); + currentHost.AddMessage(MessageType.Warning, false, "Value " + s + " is not a positive integer in Key " + key + " in Section " + Key + " at line " + s.Key); return false; case NumberRange.NonNegative: if (newValue >= 0) @@ -241,7 +241,7 @@ public virtual bool TryGetValue(T2 key, ref int value, NumberRange range = Numbe value = newValue; return true; } - currentHost.AddMessage(MessageType.Warning, false, "Value " + s.Value + " is not a non-negative integer in Key " + key + " in Section " + Key + " at line " + s.Key); + currentHost.AddMessage(MessageType.Warning, false, "Value " + s + " is not a non-negative integer in Key " + key + " in Section " + Key + " at line " + s.Key); return false; case NumberRange.NonZero: if (newValue != 0) @@ -249,7 +249,7 @@ public virtual bool TryGetValue(T2 key, ref int value, NumberRange range = Numbe value = newValue; return true; } - currentHost.AddMessage(MessageType.Warning, false, "Value " + s.Value + " is not a non-zero integer in Key " + key + " in Section " + Key + " at line " + s.Key); + currentHost.AddMessage(MessageType.Warning, false, "Value " + s + " is not a non-zero integer in Key " + key + " in Section " + Key + " at line " + s.Key); return false; } } @@ -291,7 +291,7 @@ public virtual bool GetIndexedPath(string absolutePath, out int index, out strin try { - finalPath = value.Value != string.Empty ? Path.CombineFile(absolutePath, value.Value) : string.Empty; + finalPath = Path.CombineFile(absolutePath, value.Value); } catch { diff --git a/source/Plugins/Formats.OpenBve/CFG/ConfigFile.cs b/source/Plugins/Formats.OpenBve/CFG/ConfigFile.cs index 3151ac637b..198a44ad1a 100644 --- a/source/Plugins/Formats.OpenBve/CFG/ConfigFile.cs +++ b/source/Plugins/Formats.OpenBve/CFG/ConfigFile.cs @@ -317,7 +317,7 @@ public override bool GetPath(T2 key, string absolutePath, out string finalPath) string relativePath = value.Value; try { - finalPath = relativePath != string.Empty ? Path.CombineFile(absolutePath, relativePath) : string.Empty; + finalPath = Path.CombineFile(absolutePath, relativePath); } catch { diff --git a/source/Plugins/Formats.OpenBve/Enums/TrainXML/TrainXMLKey.cs b/source/Plugins/Formats.OpenBve/Enums/TrainXML/TrainXMLKey.cs index 670983a936..9a43e8df80 100644 --- a/source/Plugins/Formats.OpenBve/Enums/TrainXML/TrainXMLKey.cs +++ b/source/Plugins/Formats.OpenBve/Enums/TrainXML/TrainXMLKey.cs @@ -117,7 +117,6 @@ public enum TrainXMLKey StageTwoSpeed, StageTwoExponent, Multiplier, - MotorBrakeNotch, - Function + MotorBrakeNotch } } diff --git a/source/Plugins/Formats.OpenBve/Formats.OpenBve.csproj b/source/Plugins/Formats.OpenBve/Formats.OpenBve.csproj index cd27f74dfb..a41dc09369 100644 --- a/source/Plugins/Formats.OpenBve/Formats.OpenBve.csproj +++ b/source/Plugins/Formats.OpenBve/Formats.OpenBve.csproj @@ -95,8 +95,6 @@ OpenBveApi - - - + \ No newline at end of file diff --git a/source/Plugins/Formats.OpenBve/XML/XMLFile.cs b/source/Plugins/Formats.OpenBve/XML/XMLFile.cs index d3de072a4c..2b231fd0a0 100644 --- a/source/Plugins/Formats.OpenBve/XML/XMLFile.cs +++ b/source/Plugins/Formats.OpenBve/XML/XMLFile.cs @@ -120,55 +120,6 @@ public XMLFile(XDocument currentXML, string fileName, string rootPath, HostInter } - public override bool GetPath(T2 key, string absolutePath, out string finalPath) - { - if (keyValuePairs.TryRemove(key, out var value)) - { - if (!Path.ContainsInvalidChars(value.Value)) - { - - string relativePath = value.Value; - try - { - finalPath = Path.CombineFile(absolutePath, relativePath); - } - catch - { - finalPath = string.Empty; - } - - if (File.Exists(finalPath)) - { - return true; - } - - try - { - finalPath = Path.CombineFile(absolutePath, relativePath); - } - catch - { - finalPath = string.Empty; - return false; - } - - if (File.Exists(finalPath)) - { - return true; - } - - currentHost.AddMessage(MessageType.Warning, false, "File " + value.Value + " was not found in Key " + key + " in Section " + Key + " at line " + value.Key); - finalPath = string.Empty; - return false; - - } - - currentHost.AddMessage(MessageType.Warning, false, "Path contains invalid characters for " + key + " in Section " + Key + " at line " + value.Key); - } - finalPath = string.Empty; - return false; - } - public override Block ReadNextBlock() { Block block = subBlocks[0]; diff --git a/source/Plugins/Formats.OpenBve/app.config b/source/Plugins/Formats.OpenBve/app.config deleted file mode 100644 index 7afad42acb..0000000000 --- a/source/Plugins/Formats.OpenBve/app.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/source/Plugins/Object.Animated/app.config b/source/Plugins/Object.Animated/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Object.Animated/app.config +++ b/source/Plugins/Object.Animated/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Object.CsvB3d/app.config b/source/Plugins/Object.CsvB3d/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Object.CsvB3d/app.config +++ b/source/Plugins/Object.CsvB3d/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Object.DirectX/Parsers/NewXParser.cs b/source/Plugins/Object.DirectX/Parsers/NewXParser.cs index 656ee7f9ec..c43e416dda 100644 --- a/source/Plugins/Object.DirectX/Parsers/NewXParser.cs +++ b/source/Plugins/Object.DirectX/Parsers/NewXParser.cs @@ -26,6 +26,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.Remoting.Messaging; using System.Text; using Object.DirectX; using OpenBve.Formats.DirectX; diff --git a/source/Plugins/Object.DirectX/app.config b/source/Plugins/Object.DirectX/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Object.DirectX/app.config +++ b/source/Plugins/Object.DirectX/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Object.LokSim/app.config b/source/Plugins/Object.LokSim/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Object.LokSim/app.config +++ b/source/Plugins/Object.LokSim/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Object.Msts/Object.Msts.csproj b/source/Plugins/Object.Msts/Object.Msts.csproj index c8916e9c8e..8194f5e2a8 100644 --- a/source/Plugins/Object.Msts/Object.Msts.csproj +++ b/source/Plugins/Object.Msts/Object.Msts.csproj @@ -48,11 +48,8 @@ prompt - - ..\..\..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\netstandard2.0\Microsoft.Bcl.AsyncInterfaces.dll - - - ..\..\..\packages\SharpCompress.0.48.0\lib\netstandard2.0\SharpCompress.dll + + ..\..\..\packages\SharpCompress.0.32.2\lib\net461\SharpCompress.dll @@ -70,11 +67,8 @@ ..\..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - ..\..\..\packages\System.Text.Encoding.CodePages.8.0.0\lib\netstandard2.0\System.Text.Encoding.CodePages.dll - - - ..\..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + ..\..\..\packages\System.Text.Encoding.CodePages.6.0.0\lib\net461\System.Text.Encoding.CodePages.dll diff --git a/source/Plugins/Object.Msts/app.config b/source/Plugins/Object.Msts/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Object.Msts/app.config +++ b/source/Plugins/Object.Msts/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Object.Msts/packages.config b/source/Plugins/Object.Msts/packages.config index c0bb4d9ebb..5d2acb5f31 100644 --- a/source/Plugins/Object.Msts/packages.config +++ b/source/Plugins/Object.Msts/packages.config @@ -1,11 +1,9 @@  - - + - - + \ No newline at end of file diff --git a/source/Plugins/Object.Wavefront/Parsers/AssimpObjParser.cs b/source/Plugins/Object.Wavefront/Parsers/AssimpObjParser.cs index 427c8d0fc8..e6d6af7c6f 100644 --- a/source/Plugins/Object.Wavefront/Parsers/AssimpObjParser.cs +++ b/source/Plugins/Object.Wavefront/Parsers/AssimpObjParser.cs @@ -24,6 +24,7 @@ using System; using System.Collections.Generic; +using System.Drawing.Text; using OpenBveApi.Colors; using OpenBveApi.Interface; using OpenBveApi.Math; @@ -31,11 +32,10 @@ using AssimpNET.Obj; using OpenBveApi; using Material = AssimpNET.Obj.Material; -using Mesh = AssimpNET.Obj.Mesh; namespace Plugin { - internal class AssimpObjParser + class AssimpObjParser { private static string currentFolder; @@ -47,99 +47,119 @@ internal static StaticObject ReadObject(string fileName) ObjFileParser parser = new ObjFileParser(System.IO.File.ReadAllLines(fileName), null, System.IO.Path.GetFileNameWithoutExtension(fileName), fileName); Model model = parser.GetModel(); - StaticObject obj = new StaticObject(Plugin.CurrentHost); - MeshBuilder builder = new MeshBuilder(Plugin.CurrentHost); + StaticObject obj = new StaticObject(Plugin.currentHost); + MeshBuilder builder = new MeshBuilder(Plugin.currentHost); + + List allVertices = new List(); + foreach (var vertex in model.Vertices) + { + allVertices.Add(new Vertex(vertex * model.ScaleFactor)); + } + + List allTexCoords = new List(); + foreach (var texCoord in model.TextureCoord) + { + Vector2 textureCoordinate = new Vector2(texCoord.X, texCoord.Y); + switch (model.Exporter) + { + case ModelExporter.SketchUp: + textureCoordinate.X *= -1.0; + textureCoordinate.Y *= -1.0; + break; + case ModelExporter.Blender: + case ModelExporter.BlockBench: + textureCoordinate.Y *= -1.0; + break; + } + allTexCoords.Add(textureCoordinate); + + } + + List allNormals = new List(); + foreach (var normal in model.Normals) + { + allNormals.Add(new Vector3(normal.X, normal.Y, normal.Z)); + } + Material lastMaterial = null; - foreach (Mesh mesh in model.Meshes) + foreach (AssimpNET.Obj.Mesh mesh in model.Meshes) { - //mesh.Faces.GroupBy(x => x.Material).OrderByDescending(g => g.Count()).SelectMany(x => x).ToList(); foreach (Face face in mesh.Faces) { - if (face.Material != lastMaterial) - { - builder.Apply(ref obj); - builder = new MeshBuilder(Plugin.CurrentHost); - uint materialIndex = mesh.MaterialIndex; - if (materialIndex != Mesh.NoMaterial) - { - Material material = model.MaterialMap[model.MaterialLib[(int)materialIndex]]; - builder.Materials[0].Color = new Color32(material.Diffuse); -#pragma warning disable 0219 - //Current openBVE renderer does not support specular color - // ReSharper disable once UnusedVariable - Color24 mSpecular = new Color24(material.Specular); -#pragma warning restore 0219 - // Wrap Color24 in Color32 for RGBA support; alpha defaults to 255 (opaque) - builder.Materials[0].EmissiveColor = new Color32(new Color24(material.Emissive)); - builder.Materials[0].Flags |= MaterialFlags.Emissive; //TODO: Check exact behaviour - if (material.TransparentUsed) - { - builder.Materials[0].TransparentColor = new Color24(material.Transparent); - builder.Materials[0].Flags |= MaterialFlags.TransparentColor; - } - - if (material.Texture != null) - { - builder.Materials[0].DaytimeTexture = Path.CombineFile(currentFolder, material.Texture); - if (!System.IO.File.Exists(builder.Materials[0].DaytimeTexture)) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, true, "Texture " + builder.Materials[0].DaytimeTexture + " was not found in file " + fileName); - builder.Materials[0].DaytimeTexture = null; - } - } - } - } - - if (face.Vertices.Count == 0) + int nVerts = face.Vertices.Count; + int bVerts = builder.Vertices.Count; + if (nVerts == 0) { throw new Exception("nVertices must be greater than zero"); } - int startingVertex = builder.Vertices.Count; - for (int i = 0; i < face.Vertices.Count; i++) + for (int i = 0; i < nVerts; i++) { - VertexTemplate v = new Vertex(model.Vertices[(int)face.Vertices[i]] * model.ScaleFactor); - - if (model.TextureCoord.Count > 0 && i <= model.TextureCoord.Count && face.TexturCoords.Count > 0 && i <= face.TexturCoords.Count) + VertexTemplate v = allVertices[(int)face.Vertices[i]].Clone(); + if (allTexCoords.Count > 0 && i <= allTexCoords.Count && face.TexturCoords.Count > 0 && i <= face.TexturCoords.Count) { - Vector2 textureCoordinate = new Vector2(model.TextureCoord[i].X, model.TextureCoord[i].Y); - switch (model.Exporter) - { - case ModelExporter.SketchUp: - textureCoordinate.X *= -1.0; - textureCoordinate.Y *= -1.0; - break; - case ModelExporter.Blender: - case ModelExporter.BlockBench: - textureCoordinate.Y *= -1.0; - break; - } - v.TextureCoordinates = textureCoordinate; + v.TextureCoordinates = allTexCoords[(int)face.TexturCoords[i]]; } - builder.Vertices.Add(v); } - MeshFace f = new MeshFace(face.Vertices.Count); - - for (int i = 0; i < face.Vertices.Count; i++) + MeshFace f = new MeshFace(nVerts); + for (int i = 0; i < nVerts; i++) { - f.Vertices[i].Index = startingVertex + i; + f.Vertices[i].Index = bVerts + i; if (face.Normals.Count > i) { - f.Vertices[i].Normal = model.Normals[(int)face.Normals[i]]; + f.Vertices[i].Normal = allNormals[(int)face.Normals[i]]; } } - - f.Material = 0; - f.Flags |= FaceFlags.Face2Mask; + f.Material = 1; builder.Faces.Add(f); + + int m = builder.Materials.Length; + Array.Resize(ref builder.Materials, m + 1); + builder.Materials[m] = new OpenBveApi.Objects.Material(); + uint materialIndex = mesh.MaterialIndex; + if (materialIndex != AssimpNET.Obj.Mesh.NoMaterial) + { + AssimpNET.Obj.Material material = model.MaterialMap[model.MaterialLib[(int)materialIndex]]; + builder.Materials[m].Color = new Color32(material.Diffuse); +#pragma warning disable 0219 + //Current openBVE renderer does not support specular color + // ReSharper disable once UnusedVariable + Color24 mSpecular = new Color24(material.Specular); +#pragma warning restore 0219 + // Wrap Color24 in Color32 for RGBA support; alpha defaults to 255 (opaque) + builder.Materials[m].EmissiveColor = new Color32(new Color24(material.Emissive)); + builder.Materials[m].Flags |= MaterialFlags.Emissive; //TODO: Check exact behaviour + if (material.TransparentUsed) + { + builder.Materials[m].TransparentColor = new Color24(material.Transparent); + builder.Materials[m].Flags |= MaterialFlags.TransparentColor; + } + + if (material.Texture != null) + { + builder.Materials[m].DaytimeTexture = Path.CombineFile(currentFolder, material.Texture); + if (!System.IO.File.Exists(builder.Materials[m].DaytimeTexture)) + { + Plugin.currentHost.AddMessage(MessageType.Error, true, "Texture " + builder.Materials[m].DaytimeTexture + " was not found in file " + fileName); + builder.Materials[m].DaytimeTexture = null; + } + } + } + if (model.Exporter >= ModelExporter.UnknownLeftHanded) { Array.Reverse(builder.Faces[builder.Faces.Count -1].Vertices, 0, builder.Faces[builder.Faces.Count -1].Vertices.Length); } + + if (face.Material != lastMaterial) + { + builder.Apply(ref obj); + builder = new MeshBuilder(Plugin.currentHost); + } lastMaterial = face.Material; } } @@ -149,7 +169,7 @@ internal static StaticObject ReadObject(string fileName) } catch (Exception e) { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, e.Message + " in " + fileName); + Plugin.currentHost.AddMessage(MessageType.Error, false, e.Message + " in " + fileName); return null; } } diff --git a/source/Plugins/Object.Wavefront/Parsers/Commands.cs b/source/Plugins/Object.Wavefront/Parsers/Commands.cs index 54d1ae6b39..4e430ffbc7 100644 --- a/source/Plugins/Object.Wavefront/Parsers/Commands.cs +++ b/source/Plugins/Object.Wavefront/Parsers/Commands.cs @@ -16,7 +16,7 @@ internal enum WavefrontObjCommands /// Adds a face F, /// Creates a new object - /// Roughly analogous to a new MeshBuilder + /// Rougly analagous to a new MeshBuilder O, /// Starts a new face group /// Usually applies a new texture @@ -28,7 +28,7 @@ internal enum WavefrontObjCommands /// /// Uses a material from the library /// - UseMtl + UseMtl, } internal enum WavefrontMtlCommands diff --git a/source/Plugins/Object.Wavefront/Parsers/WavefrontObjParser.cs b/source/Plugins/Object.Wavefront/Parsers/WavefrontObjParser.cs index 7cc1c39c07..8db3d83626 100644 --- a/source/Plugins/Object.Wavefront/Parsers/WavefrontObjParser.cs +++ b/source/Plugins/Object.Wavefront/Parsers/WavefrontObjParser.cs @@ -35,6 +35,8 @@ namespace Plugin { internal static class WavefrontObjParser { + + /// Loads a Wavefront object from a file. /// The text file to load the object from. Must be an absolute file name. /// The encoding the file is saved in. @@ -42,9 +44,9 @@ internal static class WavefrontObjParser internal static StaticObject ReadObject(string fileName, System.Text.Encoding encoding) { ModelExporter modelExporter = ModelExporter.Unknown; - StaticObject parsedObject = new StaticObject(Plugin.CurrentHost); + StaticObject parsedObject = new StaticObject(Plugin.currentHost); - MeshBuilder meshBuilder = new MeshBuilder(Plugin.CurrentHost); + MeshBuilder meshBuilder = new MeshBuilder(Plugin.currentHost); /* * Temporary arrays @@ -105,7 +107,7 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en currentScale = 1.0; break; default: - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Unrecognised units value " + units + " at line "+ i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unrecognised units value " + units + " at line "+ i); break; } } @@ -137,15 +139,15 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en Vector3 vertex = new Vector3(); if (!double.TryParse(arguments[1], out vertex.X)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Vertex at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Vertex at Line " + i); } if (!double.TryParse(arguments[2], out vertex.Y)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Y co-ordinate in Vertex at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Y co-ordinate in Vertex at Line " + i); } if (!double.TryParse(arguments[3], out vertex.Z)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Z co-ordinate in Vertex at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Z co-ordinate in Vertex at Line " + i); } vertex *= currentScale; tempVertices.Add(vertex); @@ -154,11 +156,11 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en Vector2 coords = new Vector2(); if (!double.TryParse(arguments[1], out coords.X)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-ordinates at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-ordinates at Line " + i); } if (!double.TryParse(arguments[2], out coords.Y)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-Ordinates at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Texture Co-Ordinates at Line " + i); } tempCoords.Add(coords); break; @@ -166,15 +168,15 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en Vector3 normal = new Vector3(); if (!double.TryParse(arguments[1], out normal.X)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Vertex Normal at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid X co-ordinate in Vertex Normal at Line " + i); } if (!double.TryParse(arguments[2], out normal.Y)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Y co-ordinate in Vertex Normal at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Y co-ordinate in Vertex Normal at Line " + i); } if (!double.TryParse(arguments[3], out normal.Z)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Z co-ordinate in Vertex Normal at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Z co-ordinate in Vertex Normal at Line " + i); } tempNormals.Add(normal); break; @@ -182,8 +184,8 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en throw new NotSupportedException("Parameter space verticies are not supported by this parser"); case WavefrontObjCommands.F: //Create the temp list to hook out the vertices - - MeshFaceVertex[] meshVerticies = new MeshFaceVertex[arguments.Count - 1]; + List vertices = new List(); + List normals = new List(); for (int f = 1; f < arguments.Count; f++) { Vertex newVertex = new Vertex(); @@ -191,7 +193,7 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en int idx; if (!int.TryParse(faceArguments[0], out idx)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Vertex index in Face " + f + " at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Vertex index in Face " + f + " at Line " + i); continue; } @@ -208,7 +210,7 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en } if (currentVertex > tempVertices.Count) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Vertex index " + idx + " was greater than the available number of vertices in Face " + f + " at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Vertex index " + idx + " was greater than the available number of vertices in Face " + f + " at Line " + i); continue; } newVertex.Coordinates = tempVertices[currentVertex - 1]; @@ -216,17 +218,18 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en { newVertex.Coordinates.X *= -1.0; } - - - if (faceArguments.Length > 1) + if (faceArguments.Length <= 1) + { + normals.Add(new Vector3()); + } + else { if (!int.TryParse(faceArguments[1], out idx)) { if (!string.IsNullOrEmpty(faceArguments[1])) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Texture Co-ordinate index in Face " + f + " at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Texture Co-ordinate index in Face " + f + " at Line " + i); } - newVertex.TextureCoordinates = new Vector2(); } else @@ -244,7 +247,7 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en } if (currentCoord > tempCoords.Count) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Texture Co-ordinate index " + currentCoord + " was greater than the available number of texture co-ordinates in Face " + f + " at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Texture Co-ordinate index " + currentCoord + " was greater than the available number of texture co-ordinates in Face " + f + " at Line " + i); } else { @@ -264,15 +267,19 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en } } } - Vector3 vertexNormal = new Vector3(); - if (faceArguments.Length > 2) + if (faceArguments.Length <= 2) + { + normals.Add(new Vector3()); + } + else { if (!int.TryParse(faceArguments[2], out idx)) { if (!string.IsNullOrEmpty(faceArguments[2])) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Vertex Normal index in Face " + f + " at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Vertex Normal index in Face " + f + " at Line " + i); } + normals.Add(new Vector3()); } else { @@ -289,19 +296,24 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en } if (currentNormal > tempNormals.Count) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Vertex Normal index " + currentNormal + " was greater than the available number of normals in Face " + f + " at Line " + i); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Vertex Normal index " + currentNormal + " was greater than the available number of normals in Face " + f + " at Line " + i); + normals.Add(new Vector3()); } else { - vertexNormal = tempNormals[currentNormal - 1]; + normals.Add(tempNormals[currentNormal - 1]); } } } - meshBuilder.Vertices.Add(newVertex); - meshVerticies[f - 1].Index = meshBuilder.Vertices.Count - 1; - meshVerticies[f - 1].Normal = vertexNormal; + vertices.Add(newVertex); + } + MeshFaceVertex[] meshVerticies = new MeshFaceVertex[vertices.Count]; + for (int k = 0; k < vertices.Count; k++) + { + meshBuilder.Vertices.Add(vertices[k]); + meshVerticies[k].Index = (ushort)(meshBuilder.Vertices.Count -1); + meshVerticies[k].Normal = normals[k]; } - int materialIndex = -1; if (currentMaterial != string.Empty) { @@ -317,33 +329,28 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en if (materialIndex == -1) { - if (currentMaterial != string.Empty) + materialIndex = meshBuilder.Materials.Length; + Array.Resize(ref meshBuilder.Materials, meshBuilder.Materials.Length + 1); + if (tempMaterials.ContainsKey(currentMaterial)) { - materialIndex = meshBuilder.Materials.Length; - Array.Resize(ref meshBuilder.Materials, meshBuilder.Materials.Length + 1); - if (tempMaterials.ContainsKey(currentMaterial)) - { - meshBuilder.Materials[materialIndex] = tempMaterials[currentMaterial]; - } - else - { - meshBuilder.Materials[materialIndex] = new Material(); - } + meshBuilder.Materials[materialIndex] = tempMaterials[currentMaterial]; + } + else + { + meshBuilder.Materials[materialIndex] = new Material(); } + } if (modelExporter >= ModelExporter.UnknownLeftHanded) { Array.Reverse(meshVerticies, 0, meshVerticies.Length); } - - MeshFace face = currentMaterial == string.Empty ? new MeshFace(meshVerticies, 0) : new MeshFace(meshVerticies, (ushort)materialIndex); - face.Flags |= FaceFlags.Face2Mask; - meshBuilder.Faces.Add(face); + meshBuilder.Faces.Add(currentMaterial == string.Empty ? new MeshFace(meshVerticies, 0) : new MeshFace(meshVerticies, (ushort)materialIndex)); break; case WavefrontObjCommands.O: case WavefrontObjCommands.G: meshBuilder.Apply(ref parsedObject); - meshBuilder = new MeshBuilder(Plugin.CurrentHost); + meshBuilder = new MeshBuilder(Plugin.currentHost); break; case WavefrontObjCommands.S: /* @@ -367,22 +374,17 @@ internal static StaticObject ReadObject(string fileName, System.Text.Encoding en } break; case WavefrontObjCommands.UseMtl: - string lastMaterial = currentMaterial; currentMaterial = arguments[1].ToLowerInvariant(); if (!tempMaterials.ContainsKey(currentMaterial)) { currentMaterial = string.Empty; - Plugin.CurrentHost.AddMessage(MessageType.Error, true, "Material " + arguments[1] + " was not found."); - } - - if (lastMaterial != currentMaterial) - { - meshBuilder.Apply(ref parsedObject); - meshBuilder = new MeshBuilder(Plugin.CurrentHost); + Plugin.currentHost.AddMessage(MessageType.Error, true, "Material " + arguments[1] + " was not found."); } + meshBuilder.Apply(ref parsedObject); + meshBuilder = new MeshBuilder(Plugin.currentHost); break; default: - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Unrecognised command " + arguments[0]); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Unrecognised command " + arguments[0]); break; } } @@ -428,7 +430,7 @@ private static void LoadMaterials(string fileName, ref Dictionary= 2 && !double.TryParse(arguments[1], out r)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Ambient Color R in Material Definition for " + currentKey); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Ambient Color R in Material Definition for " + currentKey); } if (arguments.Count >= 3 && !double.TryParse(arguments[2], out g)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Ambient Color G in Material Definition for " + currentKey); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Ambient Color G in Material Definition for " + currentKey); } if (arguments.Count >= 4 && !double.TryParse(arguments[3], out b)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Ambient Color B in Material Definition for " + currentKey); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Ambient Color B in Material Definition for " + currentKey); } r = 255 * r; g = 255 * g; @@ -465,7 +467,7 @@ private static void LoadMaterials(string fileName, ref Dictionary= 2 && !double.TryParse(arguments[1], out a)) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "Invalid Alpha in Material Definition for " + currentKey); + Plugin.currentHost.AddMessage(MessageType.Warning, false, "Invalid Alpha in Material Definition for " + currentKey); } materials[currentKey].Color.A = (byte)(a * 255); break; @@ -474,7 +476,7 @@ private static void LoadMaterials(string fileName, ref Dictionary 2) - { - // HACK: re-join the split array (minus command) in case of spaces in folder names - arguments.RemoveAt(0); - string arg = string.Join(" ", arguments.ToArray()); - try - { - tday = OpenBveApi.Path.CombineFile(Path.GetDirectoryName(fileName), arg); - } - catch - { - // ignored - } - - if (File.Exists(tday)) - { - materials[currentKey].DaytimeTexture = tday; - } - else - { - Plugin.CurrentHost.AddMessage(MessageType.Error, true, "Material texture file " + arg + " was not found."); - } - } - else - { - Plugin.CurrentHost.AddMessage(MessageType.Error, true, "Material texture file " + arguments[arguments.Count - 1] + " was not found."); - } - + Plugin.currentHost.AddMessage(MessageType.Error, true, "Material texture file " + arguments[arguments.Count -1] + " was not found."); } break; diff --git a/source/Plugins/Object.Wavefront/Plugin.cs b/source/Plugins/Object.Wavefront/Plugin.cs index d0775523e8..f18579b8f4 100644 --- a/source/Plugins/Object.Wavefront/Plugin.cs +++ b/source/Plugins/Object.Wavefront/Plugin.cs @@ -33,13 +33,13 @@ namespace Plugin { public class Plugin : ObjectInterface { - internal static HostInterface CurrentHost; + internal static HostInterface currentHost; private static ObjParsers currentObjParser = ObjParsers.Original; public override string[] SupportedStaticObjectExtensions => new[] { ".obj" }; public override void Load(HostInterface host, FileSystem fileSystem) { - CurrentHost = host; + currentHost = host; } public override void SetObjectParser(object parserType) @@ -56,7 +56,11 @@ public override bool CanLoadObject(string path) { return false; } - return path.EndsWith(".obj", StringComparison.InvariantCultureIgnoreCase); + if (path.EndsWith(".obj", StringComparison.InvariantCultureIgnoreCase)) + { + return true; + } + return false; } public override bool LoadObject(string path, System.Text.Encoding textEncoding, out UnifiedObject unifiedObject) @@ -71,7 +75,7 @@ public override bool LoadObject(string path, System.Text.Encoding textEncoding, } catch (Exception ex) { - CurrentHost.AddMessage(MessageType.Error, false, "The new Obj parser raised the following exception: " + ex); + currentHost.AddMessage(MessageType.Error, false, "The new Obj parser raised the following exception: " + ex); } } try @@ -82,7 +86,7 @@ public override bool LoadObject(string path, System.Text.Encoding textEncoding, catch { unifiedObject = null; - CurrentHost.AddMessage(MessageType.Error, false, "An unexpected error occured whilst attempting to load the following object: " + path); + currentHost.AddMessage(MessageType.Error, false, "An unexpected error occured whilst attempting to load the following object: " + path); } return false; } diff --git a/source/Plugins/Object.Wavefront/app.config b/source/Plugins/Object.Wavefront/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Object.Wavefront/app.config +++ b/source/Plugins/Object.Wavefront/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/OpenBveAts/app.config b/source/Plugins/OpenBveAts/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/OpenBveAts/app.config +++ b/source/Plugins/OpenBveAts/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Route.Bve5/MapParser/ConfirmComponents.cs b/source/Plugins/Route.Bve5/MapParser/ConfirmComponents.cs index a2c139688e..01b148d6f0 100644 --- a/source/Plugins/Route.Bve5/MapParser/ConfirmComponents.cs +++ b/source/Plugins/Route.Bve5/MapParser/ConfirmComponents.cs @@ -587,13 +587,10 @@ private static void ConfirmRepeater(bool PreviewOnly, MapData ParseData, RouteDa Repeater.ObjectKeys = new string[d.StructureKeys.Count]; - HashSet missingObjectKeys = new HashSet(); d.StructureKeys.CopyTo(Repeater.ObjectKeys, 0); for (int i = 0; i < Repeater.ObjectKeys.Length; i++) { - // empty string == no object placed - // also only add the error once per position (even if the object appears multiple times in the cycle) - if (!RouteData.Objects.ContainsKey(Repeater.ObjectKeys[i]) && missingObjectKeys.Add(Repeater.ObjectKeys[i]) && !string.IsNullOrEmpty(Repeater.ObjectKeys[i])) + if (!RouteData.Objects.ContainsKey(Repeater.ObjectKeys[i])) { Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BVE5: Structure " + Repeater.ObjectKeys[i] + " was not found in Repeater " + Statement.Key + " on track " + d.TrackKey + " at track position " + Statement.Distance + "m"); } diff --git a/source/Plugins/Route.Bve5/Plugin.cs b/source/Plugins/Route.Bve5/Plugin.cs index b666f34c59..39770a8000 100644 --- a/source/Plugins/Route.Bve5/Plugin.cs +++ b/source/Plugins/Route.Bve5/Plugin.cs @@ -25,7 +25,6 @@ using System; using System.Text; using OpenBveApi; -using OpenBveApi.Colors; using OpenBveApi.FileSystem; using OpenBveApi.Hosts; using OpenBveApi.Objects; @@ -104,7 +103,6 @@ public override bool LoadRoute(string path, Encoding textEncoding, string trainP } } - CurrentOptions.ClearColor = Color24.Black; return true; } diff --git a/source/Plugins/Route.Bve5/Route.Bve5.csproj b/source/Plugins/Route.Bve5/Route.Bve5.csproj index 425e1fd4c1..f6d5fcf95e 100644 --- a/source/Plugins/Route.Bve5/Route.Bve5.csproj +++ b/source/Plugins/Route.Bve5/Route.Bve5.csproj @@ -44,8 +44,8 @@ ..\..\..\packages\Antlr4.Runtime.4.6.6\lib\net45\Antlr4.Runtime.dll - - ..\..\..\packages\Bve5_Parsing.OpenBVE.1.1.19\lib\net45\Bve5_Parsing.dll + + ..\..\..\packages\Bve5_Parsing.OpenBVE.1.1.17\lib\net45\Bve5_Parsing.dll diff --git a/source/Plugins/Route.Bve5/ScenarioParser.cs b/source/Plugins/Route.Bve5/ScenarioParser.cs index d370174072..8b927f7f0a 100644 --- a/source/Plugins/Route.Bve5/ScenarioParser.cs +++ b/source/Plugins/Route.Bve5/ScenarioParser.cs @@ -90,7 +90,7 @@ internal static void ParseScenario(string fileName, bool previewOnly) ScenarioGrammarParser Parser = new ScenarioGrammarParser(); ScenarioData Data = Parser.Parse(File.ReadAllText(fileName, Encoding)); - Plugin.CurrentRoute.Comment = Data.Comment ?? string.Empty; + Plugin.CurrentRoute.Comment = Data.Comment; Plugin.CurrentRoute.Stations = Array.Empty(); CurrentStation = 0; if (!string.IsNullOrEmpty(Data.Image)) diff --git a/source/Plugins/Route.Bve5/app.config b/source/Plugins/Route.Bve5/app.config index a7c51ba61e..da8aeea264 100644 --- a/source/Plugins/Route.Bve5/app.config +++ b/source/Plugins/Route.Bve5/app.config @@ -10,10 +10,6 @@ - - - - diff --git a/source/Plugins/Route.Bve5/packages.config b/source/Plugins/Route.Bve5/packages.config index 6df1c162d8..ebd841fae1 100644 --- a/source/Plugins/Route.Bve5/packages.config +++ b/source/Plugins/Route.Bve5/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/source/Plugins/Route.CsvRw/CsvRwRouteParser.Preprocess.cs b/source/Plugins/Route.CsvRw/CsvRwRouteParser.Preprocess.cs index 7e867d24a2..44e43e312e 100644 --- a/source/Plugins/Route.CsvRw/CsvRwRouteParser.Preprocess.cs +++ b/source/Plugins/Route.CsvRw/CsvRwRouteParser.Preprocess.cs @@ -537,12 +537,6 @@ private void PreprocessChrRndSub(string FileName, System.Text.Encoding Encoding, } } else { continueWithNextExpression = true; - if (NumberFormats.TryParseIntVb6(s, out int r)) - { - // only one number- return this as the result - Expressions[i].Text = Expressions[i].Text.Substring(0, j) + r.ToString(Culture) + Expressions[i].Text.Substring(h + 1); - continueWithNextExpression = false; - } Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Two arguments are expected in " + t + Epilog); } } break; diff --git a/source/Plugins/Route.CsvRw/Namespaces/NonTrack/Structure.cs b/source/Plugins/Route.CsvRw/Namespaces/NonTrack/Structure.cs index bb70348352..8079d0e718 100644 --- a/source/Plugins/Route.CsvRw/Namespaces/NonTrack/Structure.cs +++ b/source/Plugins/Route.CsvRw/Namespaces/NonTrack/Structure.cs @@ -84,7 +84,7 @@ private void ParseStructureCommand(StructureCommand Command, string[] Arguments, string f = Path.CombineFile(ObjectPath, Arguments[0]); if (!File.Exists(f)) { - Plugin.CurrentHost.AddMessage(MessageType.Error, true, "FileName " + Arguments[0] + " not found in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); + Plugin.CurrentHost.AddMessage(MessageType.Error, true, "FileName " + f + " not found in " + Command + " at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); } else { diff --git a/source/Plugins/Route.CsvRw/Namespaces/Track/Track.cs b/source/Plugins/Route.CsvRw/Namespaces/Track/Track.cs index bab5cc0bdc..cc53832f37 100644 --- a/source/Plugins/Route.CsvRw/Namespaces/Track/Track.cs +++ b/source/Plugins/Route.CsvRw/Namespaces/Track/Track.cs @@ -628,14 +628,10 @@ private void ParseTrackCommand(TrackCommand Command, string[] Arguments, string num = -2; } - if (num == 0) + if (num == 0 && IsRW) { - //Aspects value of zero produces a 2-aspect R/G signal in BVE2 - if (IsRw || Plugin.CurrentOptions.EnableBveTsHacks) - { - num = -2; - } - + //Aspects value of zero in RW routes produces a 2-aspect R/G signal + num = -2; } if (num != 1 & num != -2 & num != 2 & num != -3 & num != 3 & num != -4 & num != 4 & num != -5 & num != 5 & num != 6) @@ -2589,19 +2585,13 @@ private void ParseTrackCommand(TrackCommand Command, string[] Arguments, string if (Arguments.Length >= 6 && Arguments[5].Length > 0 && !NumberFormats.TryParseDoubleVb6(Arguments[5], out pitch)) { - if (!Data.IgnorePitchRoll) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Pitch is invalid in Track.FreeObj at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); - } + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Pitch is invalid in Track.FreeObj at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); pitch = 0.0; } if (Arguments.Length >= 7 && Arguments[6].Length > 0 && !NumberFormats.TryParseDoubleVb6(Arguments[6], out roll)) { - if (!Data.IgnorePitchRoll) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Roll is invalid in Track.FreeObj at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); - } + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Roll is invalid in Track.FreeObj at line " + Expression.Line.ToString(Culture) + ", column " + Expression.Column.ToString(Culture) + " in file " + Expression.File); roll = 0.0; } diff --git a/source/Plugins/Route.CsvRw/Plugin.cs b/source/Plugins/Route.CsvRw/Plugin.cs index fa91a80005..9598ad6ab8 100644 --- a/source/Plugins/Route.CsvRw/Plugin.cs +++ b/source/Plugins/Route.CsvRw/Plugin.cs @@ -3,7 +3,6 @@ using System.Text; using System.Threading; using OpenBveApi; -using OpenBveApi.Colors; using OpenBveApi.FileSystem; using OpenBveApi.Hosts; using OpenBveApi.Interface; @@ -128,7 +127,6 @@ public override bool LoadRoute(string path, Encoding textEncoding, string trainP Parser parser = new Parser(this, isRw); parser.ParseRoute(path, textEncoding, trainPath, objectPath, soundPath, PreviewOnly); IsLoading = false; - CurrentOptions.ClearColor = new Color24(170, 170, 170); return true; } catch(Exception ex) diff --git a/source/Plugins/Route.CsvRw/Structures/Expression.cs b/source/Plugins/Route.CsvRw/Structures/Expression.cs index 87719ffc1d..f65f6df419 100644 --- a/source/Plugins/Route.CsvRw/Structures/Expression.cs +++ b/source/Plugins/Route.CsvRw/Structures/Expression.cs @@ -92,11 +92,6 @@ internal void SeparateCommandsAndArguments(out string Command, out string Argume // 普通播州赤穂行 - brackets in station name Text = ".Sta [貨]"+ Text.Substring(8); } - else if (Text.StartsWith(".freeobj (9")) - { - // East Linconshire Railway bridges - Text = ".freeobj(9" + Text.Substring(11); - } if (IsRw && CurrentSection.ToLowerInvariant() == "track") { diff --git a/source/Plugins/Route.CsvRw/Structures/Signals/SignalData.BVE4.cs b/source/Plugins/Route.CsvRw/Structures/Signals/SignalData.BVE4.cs index b369f01ca8..0f71741e6e 100644 --- a/source/Plugins/Route.CsvRw/Structures/Signals/SignalData.BVE4.cs +++ b/source/Plugins/Route.CsvRw/Structures/Signals/SignalData.BVE4.cs @@ -22,96 +22,98 @@ internal class Bve4SignalData : SignalObject public override void Create(Vector3 wpos, Transformation RailTransformation, Transformation LocalTransformation, int SectionIndex, double StartingDistance, double EndingDistance, double TrackPosition, double Brightness) { - if (SignalTextures.Length == 0) return; - int m = Math.Max(SignalTextures.Length, GlowTextures.Length); - int zn = 0; - for (int l = 0; l < m; l++) + if (SignalTextures.Length != 0) { - if (l < SignalTextures.Length && SignalTextures[l] != null || l < GlowTextures.Length && GlowTextures[l] != null) + int m = Math.Max(SignalTextures.Length, GlowTextures.Length); + int zn = 0; + for (int l = 0; l < m; l++) { - zn++; - } - } - - AnimatedObjectCollection aoc = new AnimatedObjectCollection(Plugin.CurrentHost) - { - Objects = new[] - { - new AnimatedObject(Plugin.CurrentHost) + if (l < SignalTextures.Length && SignalTextures[l] != null || l < GlowTextures.Length && GlowTextures[l] != null) { - States = new ObjectState[zn] + zn++; } } - }; - int zi = 0; - string expr = string.Empty; - for (int l = 0; l < m; l++) - { - bool qs = l < SignalTextures.Length && SignalTextures[l] != null; - bool qg = l < GlowTextures.Length && GlowTextures[l] != null; - StaticObject so = new StaticObject(Plugin.CurrentHost); - StaticObject go = null; - if (qs & qg) + AnimatedObjectCollection aoc = new AnimatedObjectCollection(Plugin.CurrentHost) { - - if (BaseObject != null) + Objects = new[] { - so = BaseObject.Clone(SignalTextures[l], null); + new AnimatedObject(Plugin.CurrentHost) + { + States = new ObjectState[zn] + } } + }; - if (GlowObject != null) + int zi = 0; + string expr = string.Empty; + for (int l = 0; l < m; l++) + { + bool qs = l < SignalTextures.Length && SignalTextures[l] != null; + bool qg = l < GlowTextures.Length && GlowTextures[l] != null; + StaticObject so = new StaticObject(Plugin.CurrentHost); + StaticObject go = null; + if (qs & qg) { - go = GlowObject.Clone(GlowTextures[l], null); + + if (BaseObject != null) + { + so = BaseObject.Clone(SignalTextures[l], null); + } + + if (GlowObject != null) + { + go = GlowObject.Clone(GlowTextures[l], null); + } + + so.JoinObjects(go); + aoc.Objects[0].States[zi] = new ObjectState(so); } + else if (qs) + { + if (BaseObject != null) + { + so = BaseObject.Clone(SignalTextures[l], null); + } - so.JoinObjects(go); - aoc.Objects[0].States[zi] = new ObjectState(so); - } - else if (qs) - { - if (BaseObject != null) + aoc.Objects[0].States[zi] = new ObjectState(so); + } + else if (qg) { - so = BaseObject.Clone(SignalTextures[l], null); + if (GlowObject != null) + { + go = GlowObject.Clone(GlowTextures[l], null); + } + + //BUG: Should we join the glow object here? Test what BVE4 does with missing state, but provided glow + aoc.Objects[0].States[zi] = new ObjectState(so); } - aoc.Objects[0].States[zi] = new ObjectState(so); - } - else if (qg) - { - if (GlowObject != null) + if (qs | qg) { - go = GlowObject.Clone(GlowTextures[l], null); - } + CultureInfo Culture = CultureInfo.InvariantCulture; + if (zi < zn - 1) + { + expr += "section " + l.ToString(Culture) + " <= " + zi.ToString(Culture) + " "; + } + else + { + expr += zi.ToString(Culture); + } - //BUG: Should we join the glow object here? Test what BVE4 does with missing state, but provided glow - aoc.Objects[0].States[zi] = new ObjectState(so); + zi++; + } } - if (qs | qg) + for (int l = 0; l < zn - 1; l++) { - CultureInfo Culture = CultureInfo.InvariantCulture; - if (zi < zn - 1) - { - expr += "section " + l.ToString(Culture) + " <= " + zi.ToString(Culture) + " "; - } - else - { - expr += zi.ToString(Culture); - } - - zi++; + expr += " ?"; } - } - for (int l = 0; l < zn - 1; l++) - { - expr += " ?"; + aoc.Objects[0].StateFunction = new FunctionScript(Plugin.CurrentHost, expr, false); + aoc.Objects[0].RefreshRate = 1.0 + 0.01 * Plugin.RandomNumberGenerator.NextDouble(); + aoc.CreateObject(wpos, RailTransformation, LocalTransformation, new ObjectCreationParameters(TrackPosition, StartingDistance, EndingDistance, SectionIndex)); } - - aoc.Objects[0].StateFunction = new FunctionScript(Plugin.CurrentHost, expr, false); - aoc.Objects[0].RefreshRate = 1.0 + 0.01 * Plugin.RandomNumberGenerator.NextDouble(); - aoc.CreateObject(wpos, RailTransformation, LocalTransformation, new ObjectCreationParameters(TrackPosition, StartingDistance, EndingDistance, 1.0, SectionIndex)); } } } diff --git a/source/Plugins/Route.CsvRw/XML/DynamicLight.cs b/source/Plugins/Route.CsvRw/XML/DynamicLight.cs index 0ebe6ebeb7..1569067559 100644 --- a/source/Plugins/Route.CsvRw/XML/DynamicLight.cs +++ b/source/Plugins/Route.CsvRw/XML/DynamicLight.cs @@ -1,4 +1,4 @@ -using Formats.OpenBve; +using Formats.OpenBve; using Formats.OpenBve.XML; using OpenBveApi.Interface; using OpenBveApi.Math; @@ -48,8 +48,7 @@ public static bool ReadLightingXML(string fileName, out LightDefinition[] LightD ld = lightBlock.TryGetVector3(DynamicLightKey.LightDirection, ',', ref currentLight.LightPosition); if (!ld && lightBlock.GetVector2(DynamicLightKey.SphericalLightDirection, ',', out Vector2 temp)) { - double theta = temp.X.ToRadians(), phi = temp.Y.ToRadians(); - currentLight.LightPosition = new Vector3(-Math.Cos(theta) * Math.Sin(phi), Math.Sin(theta), -Math.Cos(theta) * Math.Cos(phi)); + currentLight.LightPosition = new Vector3(Math.Cos(temp.X) * Math.Sin(temp.Y), -Math.Sin(temp.X), Math.Cos(temp.X) * Math.Cos(temp.Y)); ld = true; } //We want to be able to add a completely default light element, but not one that's not been defined in the XML properly diff --git a/source/Plugins/Route.CsvRw/app.config b/source/Plugins/Route.CsvRw/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Route.CsvRw/app.config +++ b/source/Plugins/Route.CsvRw/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Route.Mechanik/Plugin.cs b/source/Plugins/Route.Mechanik/Plugin.cs index a23aab479b..4eaaf97c0f 100644 --- a/source/Plugins/Route.Mechanik/Plugin.cs +++ b/source/Plugins/Route.Mechanik/Plugin.cs @@ -22,19 +22,18 @@ //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; using OpenBveApi; -using OpenBveApi.Colors; using OpenBveApi.FileSystem; using OpenBveApi.Hosts; using OpenBveApi.Interface; using OpenBveApi.Routes; using RouteManager2; using RouteManager2.Stations; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; using Path = OpenBveApi.Path; namespace MechanikRouteParser @@ -143,8 +142,7 @@ public override bool LoadRoute(string path, System.Text.Encoding textEncoding, s return false; } IsLoading = false; - CurrentOptions.ClearColor = new Color24(170, 170, 170); - return true; + return true; } catch(Exception ex) { diff --git a/source/Plugins/Route.Mechanik/app.config b/source/Plugins/Route.Mechanik/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Route.Mechanik/app.config +++ b/source/Plugins/Route.Mechanik/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Sound.Flac/Decoder.cs b/source/Plugins/Sound.Flac/Decoder.cs index 1ec15e1f8a..fbfecfdbef 100644 --- a/source/Plugins/Sound.Flac/Decoder.cs +++ b/source/Plugins/Sound.Flac/Decoder.cs @@ -320,7 +320,7 @@ internal static int[][] Decode(string file, out int sampleRate, out int bitsPerS predictor += coefficients[k] * blockSamples[j - k - 1]; } predictor >>= predictorShift; - blockSamples[j] = (sbyte)(predictor + residuals[j]); + blockSamples[j] = (int)(sbyte)(predictor + residuals[j]); } } else { for (int j = predictorOrder; j < blockNumberOfSamples; j++) { @@ -329,7 +329,7 @@ internal static int[][] Decode(string file, out int sampleRate, out int bitsPerS predictor += (long)coefficients[k] * (long)blockSamples[j - k - 1]; } predictor >>= predictorShift; - blockSamples[j] = (sbyte)(predictor + residuals[j]); + blockSamples[j] = (int)(sbyte)(predictor + residuals[j]); } } } else if (subframeBitsPerSample == 16) { @@ -340,7 +340,7 @@ internal static int[][] Decode(string file, out int sampleRate, out int bitsPerS predictor += coefficients[k] * blockSamples[j - k - 1]; } predictor >>= predictorShift; - blockSamples[j] = (short)(predictor + residuals[j]); + blockSamples[j] = (int)(short)(predictor + residuals[j]); } } else { for (int j = predictorOrder; j < blockNumberOfSamples; j++) { @@ -349,7 +349,7 @@ internal static int[][] Decode(string file, out int sampleRate, out int bitsPerS predictor += (long)coefficients[k] * (long)blockSamples[j - k - 1]; } predictor >>= predictorShift; - blockSamples[j] = (short)(predictor + residuals[j]); + blockSamples[j] = (int)(short)(predictor + residuals[j]); } } } else { @@ -420,92 +420,74 @@ internal static int[][] Decode(string file, out int sampleRate, out int bitsPerS break; } } - if (md5Set) - { - int pos = 0; - MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); - byte[] check; - switch (bitsPerSample) - { - case 8: - /* For 8 bits per sample */ - bytes = new byte[numberOfChannels * samplesUsed]; - for (int i = 0; i < samplesUsed; i++) - { - for (int j = 0; j < numberOfChannels; j++) - { - bytes[pos] = (byte)(samples[j][i] >> 24); - pos++; - } - } - - check = provider.ComputeHash(bytes); - if (check.Length != md5.Length) - { - throw new InvalidOperationException(); - } - for (int i = 0; i < check.Length; i++) - { - if (check[i] != md5[i]) - { - throw new InvalidDataException("MD5 failed."); - } + if (md5Set) { + if (bitsPerSample == 8) { + /* For 8 bits per sample */ + bytes = new byte[numberOfChannels * samplesUsed]; + int pos = 0; + for (int i = 0; i < samplesUsed; i++) { + for (int j = 0; j < numberOfChannels; j++) { + bytes[pos] = (byte)(samples[j][i] >> 24); + pos++; } - break; - case 16: - /* For 16 bits per sample */ - bytes = new byte[2 * numberOfChannels * samplesUsed]; - for (int i = 0; i < samplesUsed; i++) - { - for (int j = 0; j < numberOfChannels; j++) - { - bytes[pos + 0] = (byte)(samples[j][i] >> 16); - bytes[pos + 1] = (byte)(samples[j][i] >> 24); - pos += 2; - } - } - check = provider.ComputeHash(bytes); - if (check.Length != md5.Length) - { - throw new InvalidOperationException(); + } + MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); + byte[] check = provider.ComputeHash(bytes); + if (check.Length != md5.Length) { + throw new InvalidOperationException(); + } + for (int i = 0; i < check.Length; i++) { + if (check[i] != md5[i]) { + throw new InvalidDataException("MD5 failed."); } - for (int i = 0; i < check.Length; i++) - { - if (check[i] != md5[i]) - { - throw new InvalidDataException("MD5 failed."); - } + } + } else if (bitsPerSample == 16) { + /* For 16 bits per sample */ + bytes = new byte[2 * numberOfChannels * samplesUsed]; + int pos = 0; + for (int i = 0; i < samplesUsed; i++) { + for (int j = 0; j < numberOfChannels; j++) { + bytes[pos + 0] = (byte)(samples[j][i] >> 16); + bytes[pos + 1] = (byte)(samples[j][i] >> 24); + pos += 2; } - break; - case 24: - /* For 24 bits per sample */ - bytes = new byte[3 * numberOfChannels * samplesUsed]; - for (int i = 0; i < samplesUsed; i++) - { - for (int j = 0; j < numberOfChannels; j++) - { - bytes[pos + 0] = (byte)(samples[j][i] >> 8); - bytes[pos + 1] = (byte)(samples[j][i] >> 16); - bytes[pos + 2] = (byte)(samples[j][i] >> 24); - pos += 3; - } + } + MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); + byte[] check = provider.ComputeHash(bytes); + if (check.Length != md5.Length) { + throw new InvalidOperationException(); + } + for (int i = 0; i < check.Length; i++) { + if (check[i] != md5[i]) { + throw new InvalidDataException("MD5 failed."); } - check = provider.ComputeHash(bytes); - if (check.Length != md5.Length) - { - throw new InvalidOperationException(); + } + } else if (bitsPerSample == 24) { + /* For 24 bits per sample */ + bytes = new byte[3 * numberOfChannels * samplesUsed]; + int pos = 0; + for (int i = 0; i < samplesUsed; i++) { + for (int j = 0; j < numberOfChannels; j++) { + bytes[pos + 0] = (byte)(samples[j][i] >> 8); + bytes[pos + 1] = (byte)(samples[j][i] >> 16); + bytes[pos + 2] = (byte)(samples[j][i] >> 24); + pos += 3; } - for (int i = 0; i < check.Length; i++) - { - if (check[i] != md5[i]) - { - throw new InvalidDataException("MD5 failed."); - } + } + MD5CryptoServiceProvider provider = new MD5CryptoServiceProvider(); + byte[] check = provider.ComputeHash(bytes); + if (check.Length != md5.Length) { + throw new InvalidOperationException(); + } + for (int i = 0; i < check.Length; i++) { + if (check[i] != md5[i]) { + throw new InvalidDataException("MD5 failed."); } - break; + } + } else { + /* Let's just skip the MD5 check in other cases + * or feel free to implement the check here. */ } - /* Let's just skip the MD5 check in other cases - * or feel free to implement the check here. */ } #endif // --- end of file --- @@ -559,22 +541,22 @@ private static int[] ReadResiduals(BitReader reader, int predictorOrder, int blo } } return residuals; + } else { + // --- not supported --- + throw new InvalidDataException(); } - // --- not supported --- (methods 3+ are stated as reserved in FLAC specification) - throw new InvalidDataException(); } /// Gets a signed integer from an unsigned integer assuming the unsigned integer is stored in two's complement notation. /// The unsigned integer. /// The value range, e.g. 16 for 4 bits, 256 for 8 bits, 65536 for 16 bits, etc. /// The signed integer. - private static int FromTwosComplement(uint value, uint range) - { + private static int FromTwosComplement(uint value, uint range) { if (value < (range >> 1)) { return (int)value; + } else { + return (int)value - (int)range; } - - return (int)value - (int)range; } } diff --git a/source/Plugins/Sound.Flac/Plugin.cs b/source/Plugins/Sound.Flac/Plugin.cs index c81ba5a1f4..851d6ed633 100644 --- a/source/Plugins/Sound.Flac/Plugin.cs +++ b/source/Plugins/Sound.Flac/Plugin.cs @@ -22,10 +22,6 @@ public override bool CanLoadSound(string path) { } using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { - if (stream.Length == 0) - { - return false; - } using (BinaryReader reader = new BinaryReader(stream)) { if (reader.ReadUInt32() != 0x43614C66) diff --git a/source/Plugins/Sound.Flac/app.config b/source/Plugins/Sound.Flac/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Sound.Flac/app.config +++ b/source/Plugins/Sound.Flac/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Sound.MP3/Plugin.cs b/source/Plugins/Sound.MP3/Plugin.cs index 45427cdb80..c7867c2e47 100644 --- a/source/Plugins/Sound.MP3/Plugin.cs +++ b/source/Plugins/Sound.MP3/Plugin.cs @@ -50,10 +50,6 @@ public override bool CanLoadSound(string path) using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { - if (stream.Length == 0) - { - return false; - } using (BinaryReader reader = new BinaryReader(stream)) { byte[] magicNumber = reader.ReadBytes(3); diff --git a/source/Plugins/Sound.MP3/app.config b/source/Plugins/Sound.MP3/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Sound.MP3/app.config +++ b/source/Plugins/Sound.MP3/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Sound.RiffWave/Chunks/DataChunk.cs b/source/Plugins/Sound.RiffWave/Chunks/DataChunk.cs index 465c9f3495..ef793e9724 100644 --- a/source/Plugins/Sound.RiffWave/Chunks/DataChunk.cs +++ b/source/Plugins/Sound.RiffWave/Chunks/DataChunk.cs @@ -7,15 +7,14 @@ internal class DataChunk : Chunk { public DataChunk(byte[] dataBytes, WaveFormatEx format) : base(format.Channels) { - if (format is WaveFormatAdPcm) + if (!(format is WaveFormatAdPcm)) { - BytesPerSample = 2; + BytesPerSample = format.BitsPerSample / 8; } else { - BytesPerSample = format.BitsPerSample / 8; - } - + BytesPerSample = 2; + } // The size of the data chunk may not be correct, so we use sampleCount as the standard thereafter. int sampleCount = dataBytes.Length / (format.Channels * BytesPerSample); diff --git a/source/Plugins/Sound.RiffWave/Plugin.Parser.cs b/source/Plugins/Sound.RiffWave/Plugin.Parser.cs index 6e26a76ffe..10f1f56714 100644 --- a/source/Plugins/Sound.RiffWave/Plugin.Parser.cs +++ b/source/Plugins/Sound.RiffWave/Plugin.Parser.cs @@ -1,15 +1,22 @@ +using System; +using System.Collections.Generic; +using System.IO; using NAudio.Wave; +using NLayer.NAudioSupport; using OpenBveApi; using OpenBveApi.Math; +using OpenBveApi.Objects; using OpenBveApi.Sounds; -using System; -using System.Collections.Generic; -using System.IO; namespace Plugin { public partial class Plugin { + + + + // --- functions --- + /// Reads wave data from a WAVE file. /// The file name of the WAVE file. /// The wave data. @@ -17,54 +24,49 @@ private static Sound LoadFromFile(string fileName) { using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { - return LoadFromStream(stream); - } - } + using (BinaryReader reader = new BinaryReader(stream)) + { + Endianness endianness; + uint headerCkID = reader.ReadUInt32(); - private static Sound LoadFromStream(Stream stream) - { - using (BinaryReader reader = new BinaryReader(stream)) - { - Endianness endianness; - uint headerCkID = reader.ReadUInt32(); + // "RIFF" + if (headerCkID == 0x46464952) + { + endianness = Endianness.Little; + } + // "RIFX" + else if (headerCkID == 0x58464952) + { + endianness = Endianness.Big; + } + else + { + // unsupported format + throw new InvalidDataException("Invalid header chunk ID"); + } - // "RIFF" - if (headerCkID == 0x46464952) - { - endianness = Endianness.Little; - } - // "RIFX" - else if (headerCkID == 0x58464952) - { - endianness = Endianness.Big; - } - else - { - // unsupported format - throw new InvalidDataException("Invalid header chunk ID"); - } + uint headerCkSize = reader.ReadUInt32(endianness); - uint headerCkSize = reader.ReadUInt32(endianness); + uint formType = reader.ReadUInt32(endianness); - uint formType = reader.ReadUInt32(endianness); + // "WAVE" + if (formType == 0x45564157) + { + return WaveLoadFromStream(reader, endianness, headerCkSize + 8); + } - // "WAVE" - if (formType == 0x45564157) - { - return WaveLoadFromStream(reader, endianness, headerCkSize + 8); + // unsupported format + throw new InvalidDataException("Unsupported format"); } - - // unsupported format - throw new InvalidDataException("Unsupported format"); } } - /// Reads sound data from a MP3 stream, using NAudio - /// The stream of the MP3 - /// The raw sound data + /// Reads sound data from a MP3 stream. + /// The stream of the MP3. + /// The raw sound data. private static Sound Mp3LoadFromStream(Stream stream) { - using (Mp3FileReader reader = new Mp3FileReader(stream)) + using (Mp3FileReader reader = new Mp3FileReader(stream, wf => new Mp3FrameDecompressor(wf))) { byte[] dataBytes = new byte[reader.Length]; @@ -98,11 +100,6 @@ private static Sound Mp3LoadFromStream(Stream stream) sample = 1.0f; } - if (float.IsNaN(sample)) - { - sample = 0; - } - writer.Write((short)(sample * short.MaxValue)); } } @@ -123,25 +120,6 @@ private static Sound Mp3LoadFromStream(Stream stream) } } - /// Loads sound data from a stream using NAudio to resample it to something we can read - /// The stream - /// The sound - private static Sound NAudioLoadFromStream(Stream stream) - { - using (WaveStream wf = new WaveFileReader(stream)) - { - using (WaveFormatConversionStream cf = (WaveFormatConversionStream)WaveFormatConversionStream.CreatePcmStream(wf)) - { - using (MemoryStream ms = new MemoryStream()) - { - WaveFileWriter.WriteWavFileToStream(ms, cf); - ms.Seek(0, SeekOrigin.Begin); - return LoadFromStream(ms); - } - } - } - } - private static Sound WaveLoadFromStream(BinaryReader reader, Endianness endianness, uint fileSize) { long stopPosition = Math.Min(fileSize, reader.BaseStream.Length); @@ -154,7 +132,6 @@ private static Sound WaveLoadFromStream(BinaryReader reader, Endianness endianne { ChunkID ckID = (ChunkID)reader.ReadUInt32(endianness); uint ckSize = reader.ReadUInt32(endianness); - long ckStart = reader.BaseStream.Position; // "fmt " switch (ckID) @@ -176,12 +153,6 @@ private static Sound WaveLoadFromStream(BinaryReader reader, Endianness endianne case 0x0002: format = new WaveFormatAdPcm(wFormatTag); break; - case 0x0011: - case 0x0031: - // 0x0011 - Intel DVI ADPCM - // 0x0031 - GSM610 - reader.BaseStream.Seek(0, 0); - return NAudioLoadFromStream(reader.BaseStream); case 0x0050: case 0x0055: format = new WaveFormatMp3(wFormatTag); @@ -394,15 +365,6 @@ private static Sound WaveLoadFromStream(BinaryReader reader, Endianness endianne } } } - - if (format is WaveFormatMp3) - { - using (MemoryStream dataStream = new MemoryStream(dataBytes)) - { - return Mp3LoadFromStream(dataStream); - } - } - chunks.Add(new DataChunk(dataBytes, format)); break; case ChunkID.CUE: @@ -427,9 +389,6 @@ private static Sound WaveLoadFromStream(BinaryReader reader, Endianness endianne break; } - // ensure we're actually at the end of the chunk - reader.BaseStream.Seek(ckStart + ckSize, SeekOrigin.Begin); - // pad byte if ((ckSize & 1) == 1) { @@ -443,6 +402,14 @@ private static Sound WaveLoadFromStream(BinaryReader reader, Endianness endianne throw new InvalidDataException("File contains no DATA or SLNT chunks."); } + if (format is WaveFormatMp3) + { + using (MemoryStream dataStream = new MemoryStream(dataBytes)) + { + return Mp3LoadFromStream(dataStream); + } + } + byte[][] buffers = new byte[format.Channels][]; int startPosition = 0; diff --git a/source/Plugins/Sound.RiffWave/Plugin.cs b/source/Plugins/Sound.RiffWave/Plugin.cs index 97f7439eed..c9d650d0a6 100644 --- a/source/Plugins/Sound.RiffWave/Plugin.cs +++ b/source/Plugins/Sound.RiffWave/Plugin.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using OpenBveApi; using OpenBveApi.Hosts; using OpenBveApi.Math; @@ -29,10 +29,6 @@ public override bool CanLoadSound(string path) using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { - if (stream.Length == 0) - { - return false; - } using (BinaryReader reader = new BinaryReader(stream)) { Endianness endianness; diff --git a/source/Plugins/Sound.RiffWave/app.config b/source/Plugins/Sound.RiffWave/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Sound.RiffWave/app.config +++ b/source/Plugins/Sound.RiffWave/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Sound.Vorbis/Plugin.cs b/source/Plugins/Sound.Vorbis/Plugin.cs index d24c2abb1b..1e6f727212 100644 --- a/source/Plugins/Sound.Vorbis/Plugin.cs +++ b/source/Plugins/Sound.Vorbis/Plugin.cs @@ -27,10 +27,6 @@ public override bool CanLoadSound(string path) using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { - if (stream.Length == 0) - { - return false; - } using (BinaryReader reader = new BinaryReader(stream)) { uint headerCkID = reader.ReadUInt32(); diff --git a/source/Plugins/Sound.Vorbis/app.config b/source/Plugins/Sound.Vorbis/app.config index 10fc19e69c..7a7be13580 100644 --- a/source/Plugins/Sound.Vorbis/app.config +++ b/source/Plugins/Sound.Vorbis/app.config @@ -8,7 +8,7 @@ - + diff --git a/source/Plugins/Texture.Ace/Plugin.Parser.cs b/source/Plugins/Texture.Ace/Plugin.Parser.cs index 71f309684f..bf88d74d23 100644 --- a/source/Plugins/Texture.Ace/Plugin.Parser.cs +++ b/source/Plugins/Texture.Ace/Plugin.Parser.cs @@ -118,7 +118,12 @@ private static bool CanLoadUncompressedData(byte[] data) } identifier = LittleEndianBinaryExtensions.ToUInt64(data, 8); - return identifier == DataStart; + if (identifier != DataStart) + { + return false; + } + + return true; } /// Queries the texture dimensions of the specified file. @@ -345,7 +350,7 @@ private static OpenBveApi.Textures.Texture LoadFromUncompressedData(byte[] data) int counter = 0; for (int x = 0; x < width; x++) { - byte mask = (byte)(0x80 >> (x & 0x7)); + var mask = (byte)(0x80 >> (x & 0x7)); if (counter == 0) { value = reader.ReadByte(); @@ -521,6 +526,7 @@ private static byte[] DecompressAce(byte[] data) } byte flg = reader.ReadByte(); + // int fcheck = flg & 31; if ((256 * cmf + flg) % 31 != 0) { throw new InvalidDataException(); diff --git a/source/Plugins/Texture.Ace/Plugin.cs b/source/Plugins/Texture.Ace/Plugin.cs index 59a154750a..46fed5aa0c 100644 --- a/source/Plugins/Texture.Ace/Plugin.cs +++ b/source/Plugins/Texture.Ace/Plugin.cs @@ -28,7 +28,10 @@ public override bool QueryTextureDimensions(string path, out int width, out int /// Whether the plugin can load the specified texture. public override bool CanLoadTexture(string path) { - return File.Exists(path) && CanLoadFile(path); + if (File.Exists(path)) { + return CanLoadFile(path); + } + return false; } /// Loads the specified texture. diff --git a/source/Plugins/Texture.Ace/app.config b/source/Plugins/Texture.Ace/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Texture.Ace/app.config +++ b/source/Plugins/Texture.Ace/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Texture.BmpGifJpegPngTiff/BMP/BmpDecoder.cs b/source/Plugins/Texture.BmpGifJpegPngTiff/BMP/BmpDecoder.cs index 441c7f08d7..c837eab55d 100644 --- a/source/Plugins/Texture.BmpGifJpegPngTiff/BMP/BmpDecoder.cs +++ b/source/Plugins/Texture.BmpGifJpegPngTiff/BMP/BmpDecoder.cs @@ -72,7 +72,7 @@ internal class BmpDecoder : IDisposable internal BitsPerPixel BitsPerPixel; /// The image data internal byte[] ImageData; - /// Stores the color table when using a restricted palette + /// Stores the color table when using a restricted pallette internal Color24[] ColorTable; /// The bitmap format internal BmpFormat Format; diff --git a/source/Plugins/Texture.BmpGifJpegPngTiff/GIF/GifDecoder.cs b/source/Plugins/Texture.BmpGifJpegPngTiff/GIF/GifDecoder.cs index d5ff87a16e..9ad0006c55 100644 --- a/source/Plugins/Texture.BmpGifJpegPngTiff/GIF/GifDecoder.cs +++ b/source/Plugins/Texture.BmpGifJpegPngTiff/GIF/GifDecoder.cs @@ -545,7 +545,7 @@ protected void ReadContents() app += (char) block[i]; } - if (app.Equals(@"NETSCAPE2.0") || app.Equals(@"ANIMEXTS1.0")) + if (app.Equals("NETSCAPE2.0") || app.Equals("ANIMEXTS1.0")) { ReadNetscapeExt(); } @@ -731,7 +731,7 @@ protected void ResetFrame() dispose = DisposeMode.NoAction; } - /// Skips variable length blocks up-to and including the next zero length block + /// Skips variable length blocks upto and including the next zero length block protected void Skip() { do diff --git a/source/Plugins/Texture.BmpGifJpegPngTiff/PNG/ColorType.cs b/source/Plugins/Texture.BmpGifJpegPngTiff/PNG/ColorType.cs index 44000e3d7b..fc26fbeae5 100644 --- a/source/Plugins/Texture.BmpGifJpegPngTiff/PNG/ColorType.cs +++ b/source/Plugins/Texture.BmpGifJpegPngTiff/PNG/ColorType.cs @@ -9,7 +9,7 @@ internal enum ColorType : byte Rgb = 2, /// Each pixel is a palette index ///A PLTE chunk must be present - Paletted = 3, + Palleted = 3, /// Each pixel is a grayscale sample, followed by an alpha sample GrayscaleAlpha = 4, /// Each pixel is a R,G,B triple, followed by an alpha sample diff --git a/source/Plugins/Texture.BmpGifJpegPngTiff/PNG/PngDecoder.cs b/source/Plugins/Texture.BmpGifJpegPngTiff/PNG/PngDecoder.cs index 263028c18b..f5208b60f0 100644 --- a/source/Plugins/Texture.BmpGifJpegPngTiff/PNG/PngDecoder.cs +++ b/source/Plugins/Texture.BmpGifJpegPngTiff/PNG/PngDecoder.cs @@ -148,7 +148,7 @@ internal bool Read(string fileName) switch (ColorType) { - case ColorType.Paletted: // each filtered byte becomes a palette index + case ColorType.Palleted: // each filtered byte becomes a pallete index case ColorType.Grayscale: BytesPerPixel = 1; break; @@ -166,9 +166,9 @@ internal bool Read(string fileName) return false; } - ScanlineLength = Math.Max(1, (int)Math.Ceiling((Width * BytesPerPixel) / (double)ScanlineLength)); // scanline must be a minimum of 1 byte in length. Always round up + ScanlineLength = Math.Max(1, (int)Math.Ceiling((Width * BytesPerPixel) / (double)ScanlineLength)); // scanline must be a minumum of 1 byte in length. Always round up - pixelBuffer = ColorType != ColorType.Paletted && ColorType != ColorType.GrayscaleAlpha && BytesPerPixel != 8 ? new byte[Width * Height * BytesPerPixel] : new byte[Width * Height * 4]; + pixelBuffer = ColorType != ColorType.Palleted && ColorType != ColorType.GrayscaleAlpha && BytesPerPixel != 8 ? new byte[Width * Height * BytesPerPixel] : new byte[Width * Height * 4]; break; case ChunkType.PLTE: colorPalette = new Palette(chunkBuffer); @@ -276,7 +276,7 @@ internal bool Read(string fileName) switch (ColorType) { - case ColorType.Paletted: + case ColorType.Palleted: { int pixelIndex = 0; switch (BitDepth) @@ -505,7 +505,7 @@ internal bool Read(string fileName) Adam7.GetPixelIndexForScanlineInPass(currentPass, currentScanline, j, out pixelX, out pixelY); switch (ColorType) { - case ColorType.Paletted: + case ColorType.Palleted: { // we're always converting to 4bpp in the output, but need the native bpp to find our position in the array, so don't actually set it int start = Width * 4 * pixelY + pixelX * 4; @@ -590,7 +590,7 @@ internal bool Read(string fileName) } } - if (ColorType == ColorType.Paletted || ColorType == ColorType.GrayscaleAlpha || BytesPerPixel == 8) + if (ColorType == ColorType.Palleted || ColorType == ColorType.GrayscaleAlpha || BytesPerPixel == 8) { // need the final bpp to reflect what we've converted the image to, not the bpp in the file used whilst loading BytesPerPixel = 4; diff --git a/source/Plugins/Texture.BmpGifJpegPngTiff/app.config b/source/Plugins/Texture.BmpGifJpegPngTiff/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Texture.BmpGifJpegPngTiff/app.config +++ b/source/Plugins/Texture.BmpGifJpegPngTiff/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Texture.Dds/Dds.FourCc.cs b/source/Plugins/Texture.Dds/Dds.FourCc.cs index 55f323c128..0c5650252a 100644 --- a/source/Plugins/Texture.Dds/Dds.FourCc.cs +++ b/source/Plugins/Texture.Dds/Dds.FourCc.cs @@ -13,7 +13,7 @@ namespace Texture.Dds { /// The known DDS FourCC values - internal enum FourCC : uint + enum FourCC : uint { DXT1 = 0x31545844, DXT2 = 0x32545844, diff --git a/source/Plugins/Texture.Dds/Plugin.Parser.cs b/source/Plugins/Texture.Dds/Plugin.Parser.cs index b0be386a19..4e7e5a866b 100644 --- a/source/Plugins/Texture.Dds/Plugin.Parser.cs +++ b/source/Plugins/Texture.Dds/Plugin.Parser.cs @@ -402,7 +402,7 @@ private static uint HalfToFloat(ushort y) // // Normalized number // - e += 127 - 15; + e += (127 - 15); m <<= 13; // @@ -454,38 +454,38 @@ private byte[] DecompressData(DdsHeader header, byte[] data, PixelFormat pixelFo switch (pixelFormat) { case PixelFormat.RGBA: - rawData = DecompressRGBA(header, data, pixelFormat); + rawData = this.DecompressRGBA(header, data, pixelFormat); break; case PixelFormat.RGB: - rawData = DecompressRGB(header, data, pixelFormat); + rawData = this.DecompressRGB(header, data, pixelFormat); break; case PixelFormat.LUMINANCE: case PixelFormat.LUMINANCE_ALPHA: - rawData = DecompressLum(header, data, pixelFormat); + rawData = this.DecompressLum(header, data, pixelFormat); break; case PixelFormat.DXT1: - rawData = DecompressDXT1(header, data, pixelFormat); + rawData = this.DecompressDXT1(header, data, pixelFormat); break; case PixelFormat.DXT2: - rawData = DecompressDXT2(header, data, pixelFormat); + rawData = this.DecompressDXT2(header, data, pixelFormat); break; case PixelFormat.DXT3: - rawData = DecompressDXT3(header, data, pixelFormat); + rawData = this.DecompressDXT3(header, data, pixelFormat); break; case PixelFormat.DXT4: - rawData = DecompressDXT4(header, data, pixelFormat); + rawData = this.DecompressDXT4(header, data, pixelFormat); break; case PixelFormat.DXT5: - rawData = DecompressDXT5(header, data, pixelFormat); + rawData = this.DecompressDXT5(header, data, pixelFormat); break; case PixelFormat.THREEDC: - rawData = Decompress3Dc(header, data, pixelFormat); + rawData = this.Decompress3Dc(header, data, pixelFormat); break; case PixelFormat.ATI1N: - rawData = DecompressAti1n(header, data, pixelFormat); + rawData = this.DecompressAti1n(header, data, pixelFormat); break; case PixelFormat.RXGB: - rawData = DecompressRXGB(header, data, pixelFormat); + rawData = this.DecompressRXGB(header, data, pixelFormat); break; case PixelFormat.R16F: case PixelFormat.G16R16F: @@ -493,7 +493,7 @@ private byte[] DecompressData(DdsHeader header, byte[] data, PixelFormat pixelFo case PixelFormat.R32F: case PixelFormat.G32R32F: case PixelFormat.A32B32G32R32F: - rawData = DecompressFloat(header, data, pixelFormat); + rawData = this.DecompressFloat(header, data, pixelFormat); break; default: throw new InvalidDataException("Unsupported DDS PixelFormat value"); @@ -506,8 +506,8 @@ private unsafe byte[] DecompressDXT1(DdsHeader header, byte[] data, PixelFormat { int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + int sizeofplane = bps * header.height; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; Color32[] colours = new Color32[4]; colours[0].A = 0xFF; @@ -562,7 +562,7 @@ private unsafe byte[] DecompressDXT1(DdsHeader header, byte[] data, PixelFormat Color32 col = colours[select]; if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); rawData[offset + 0] = col.R; rawData[offset + 1] = col.G; rawData[offset + 2] = col.B; @@ -589,8 +589,8 @@ private unsafe byte[] DecompressDXT3(DdsHeader header, byte[] data, PixelFormat { int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + int sizeofplane = bps * header.height; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; Color32[] colours = new Color32[4]; fixed (byte* bytePtr = data) { @@ -625,7 +625,7 @@ private unsafe byte[] DecompressDXT3(DdsHeader header, byte[] data, PixelFormat if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); rawData[offset + 0] = colours[select].R; rawData[offset + 1] = colours[select].G; rawData[offset + 2] = colours[select].B; @@ -640,7 +640,7 @@ private unsafe byte[] DecompressDXT3(DdsHeader header, byte[] data, PixelFormat { if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp + 3); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); rawData[offset] = (byte)(word & 0x0F); rawData[offset] = (byte)(rawData[offset] | (rawData[offset] << 4)); } @@ -667,9 +667,9 @@ private unsafe byte[] DecompressDXT5(DdsHeader header, byte[] data, PixelFormat // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; Color32[] colours = new Color32[4]; ushort[] alphas = new ushort[8]; @@ -687,7 +687,7 @@ private unsafe byte[] DecompressDXT5(DdsHeader header, byte[] data, PixelFormat alphas[0] = temp[0]; alphas[1] = temp[1]; - byte* alphaMask = (temp + 2); + byte* alphamask = (temp + 2); temp += 8; DxtcReadColors(temp, ref colours); @@ -709,7 +709,7 @@ private unsafe byte[] DecompressDXT5(DdsHeader header, byte[] data, PixelFormat // only put pixels out < width or height if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); rawData[offset] = col.R; rawData[offset + 1] = col.G; rawData[offset + 2] = col.B; @@ -745,7 +745,8 @@ private unsafe byte[] DecompressDXT5(DdsHeader header, byte[] data, PixelFormat // it operates on a 6-byte system. // First three bytes - uint bits = (uint)((alphaMask[0]) | (alphaMask[1] << 8) | (alphaMask[2] << 16)); + //uint bits = (uint)(alphamask[0]); + uint bits = (uint)((alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16)); for (int j = 0; j < 2; j++) { for (int i = 0; i < 4; i++) @@ -753,7 +754,7 @@ private unsafe byte[] DecompressDXT5(DdsHeader header, byte[] data, PixelFormat // only put pixels out < width or height if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp + 3); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); rawData[offset] = (byte)alphas[bits & 0x07]; } bits >>= 3; @@ -761,7 +762,8 @@ private unsafe byte[] DecompressDXT5(DdsHeader header, byte[] data, PixelFormat } // Last three bytes - bits = (uint)((alphaMask[3]) | (alphaMask[4] << 8) | (alphaMask[5] << 16)); + //bits = (uint)(alphamask[3]); + bits = (uint)((alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16)); for (int j = 2; j < 4; j++) { for (int i = 0; i < 4; i++) @@ -769,7 +771,7 @@ private unsafe byte[] DecompressDXT5(DdsHeader header, byte[] data, PixelFormat // only put pixels out < width or height if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp + 3); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); rawData[offset] = (byte)alphas[bits & 0x07]; } bits >>= 3; @@ -788,9 +790,9 @@ private unsafe byte[] DecompressRGB(DdsHeader header, byte[] data, PixelFormat p // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; uint valMask; unchecked { @@ -831,9 +833,9 @@ private unsafe byte[] DecompressRGBA(DdsHeader header, byte[] data, PixelFormat // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; uint valMask; unchecked { @@ -878,9 +880,9 @@ private unsafe byte[] Decompress3Dc(DdsHeader header, byte[] data, PixelFormat p // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; byte[] yColours = new byte[8]; byte[] xColours = new byte[8]; @@ -982,9 +984,9 @@ private unsafe byte[] DecompressAti1n(DdsHeader header, byte[] data, PixelFormat // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; byte[] colours = new byte[8]; uint offset = 0; @@ -1051,9 +1053,9 @@ private unsafe byte[] DecompressLum(DdsHeader header, byte[] data, PixelFormat p // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; int lShift1; int lMul; int lShift2; ComputeMaskParams(header.pixelFormat.rbitmask, out lShift1, out lMul, out lShift2); @@ -1081,9 +1083,9 @@ private unsafe byte[] DecompressRXGB(DdsHeader header, byte[] data, PixelFormat // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; Color32 color_0 = new Color32(); Color32 color_1 = new Color32(); @@ -1103,7 +1105,7 @@ private unsafe byte[] DecompressRXGB(DdsHeader header, byte[] data, PixelFormat break; alphas[0] = temp[0]; alphas[1] = temp[1]; - byte* alphaMask = temp + 2; + byte* alphamask = temp + 2; temp += 8; DxtcReadColors(temp, ref color_0, ref color_1); @@ -1147,7 +1149,7 @@ private unsafe byte[] DecompressRXGB(DdsHeader header, byte[] data, PixelFormat // only put pixels out < width or height if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp); rawData[offset + 0] = col.R; rawData[offset + 1] = col.G; rawData[offset + 2] = col.B; @@ -1182,7 +1184,7 @@ private unsafe byte[] DecompressRXGB(DdsHeader header, byte[] data, PixelFormat // Note: Have to separate the next two loops, // it operates on a 6-byte system. // First three bytes - uint bits = *((uint*)alphaMask); + uint bits = *((uint*)alphamask); for (int j = 0; j < 2; j++) { for (int i = 0; i < 4; i++) @@ -1190,7 +1192,7 @@ private unsafe byte[] DecompressRXGB(DdsHeader header, byte[] data, PixelFormat // only put pixels out < width or height if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp + 3); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); rawData[offset] = alphas[bits & 0x07]; } bits >>= 3; @@ -1198,7 +1200,7 @@ private unsafe byte[] DecompressRXGB(DdsHeader header, byte[] data, PixelFormat } // Last three bytes - bits = *((uint*)&alphaMask[3]); + bits = *((uint*)&alphamask[3]); for (int j = 2; j < 4; j++) { for (int i = 0; i < 4; i++) @@ -1206,7 +1208,7 @@ private unsafe byte[] DecompressRXGB(DdsHeader header, byte[] data, PixelFormat // only put pixels out < width or height if (((x + i) < header.width) && ((y + j) < header.height)) { - uint offset = (uint)(z * sizeOfPlane + (y + j) * bps + (x + i) * bpp + 3); + uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3); rawData[offset] = alphas[bits & 0x07]; } bits >>= 3; @@ -1224,24 +1226,26 @@ private unsafe byte[] DecompressFloat(DdsHeader header, byte[] data, PixelFormat // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; fixed (byte* bytePtr = data) { - fixed (byte* destPtr = rawData) + byte* temp = bytePtr; + fixed (byte* destPtr = rawData) { - int size; + byte* destData = destPtr; + int size; switch (pixelFormat) { case PixelFormat.R32F: // Red float, green = blue = max size = header.width * header.height * header.depth * 3; for (int i = 0, j = 0; i < size; i += 3, j++) { - ((float*)destPtr)[i] = ((float*)bytePtr)[j]; - ((float*)destPtr)[i + 1] = 1.0f; - ((float*)destPtr)[i + 2] = 1.0f; + ((float*)destData)[i] = ((float*)temp)[j]; + ((float*)destData)[i + 1] = 1.0f; + ((float*)destData)[i + 2] = 1.0f; } break; @@ -1253,25 +1257,25 @@ private unsafe byte[] DecompressFloat(DdsHeader header, byte[] data, PixelFormat size = header.width * header.height * header.depth * 3; for (int i = 0, j = 0; i < size; i += 3, j += 2) { - ((float*)destPtr)[i] = ((float*)bytePtr)[j]; - ((float*)destPtr)[i + 1] = ((float*)bytePtr)[j + 1]; - ((float*)destPtr)[i + 2] = 1.0f; + ((float*)destData)[i] = ((float*)temp)[j]; + ((float*)destData)[i + 1] = ((float*)temp)[j + 1]; + ((float*)destData)[i + 2] = 1.0f; } break; case PixelFormat.R16F: // Red float, green = blue = max size = header.width * header.height * header.depth * bpp; - ConvR16ToFloat32((uint*)destPtr, (ushort*)bytePtr, (uint)size); + ConvR16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); break; case PixelFormat.A16B16G16R16F: // Just convert from half to float. size = header.width * header.height * header.depth * bpp; - ConvFloat16ToFloat32((uint*)destPtr, (ushort*)bytePtr, (uint)size); + ConvFloat16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); break; case PixelFormat.G16R16F: // Convert from half to float, set blue = max. size = header.width * header.height * header.depth * bpp; - ConvG16R16ToFloat32((uint*)destPtr, (ushort*)bytePtr, (uint)size); + ConvG16R16ToFloat32((uint*)destData, (ushort*)temp, (uint)size); break; default: throw new NotImplementedException("Decompression of PixelFormat " + pixelFormat + " has not been implemented in this plugin."); @@ -1287,13 +1291,13 @@ private unsafe byte[] DecompressARGB(DdsHeader header, byte[] data, PixelFormat // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; if (header.Check16BitComponents()) return DecompressARGB16(header, data, pixelFormat); int sizeOfData = (header.width * header.pixelFormat.rgbbitcount / 8) * header.height * header.depth; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; if ((pixelFormat == PixelFormat.LUMINANCE) && (header.pixelFormat.rgbbitcount == 16) && (header.pixelFormat.rbitmask == 0xFFFF)) { @@ -1381,10 +1385,10 @@ private unsafe byte[] DecompressARGB16(DdsHeader header, byte[] data, PixelForma // allocate bitmap int bpp = PixelFormatToBpp(pixelFormat, header.pixelFormat.rgbbitcount); int bps = header.width * bpp * PixelFormatToBpc(pixelFormat); - int sizeOfPlane = bps * header.height; + int sizeofplane = bps * header.height; int sizeOfData = (header.width * header.pixelFormat.rgbbitcount / 8) * header.height * header.depth; - byte[] rawData = new byte[header.depth * sizeOfPlane + header.height * bps + header.width * bpp]; + byte[] rawData = new byte[header.depth * sizeofplane + header.height * bps + header.width * bpp]; uint readI = 0; uint redL, redR; diff --git a/source/Plugins/Texture.Dds/app.config b/source/Plugins/Texture.Dds/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Texture.Dds/app.config +++ b/source/Plugins/Texture.Dds/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Texture.Tga/app.config b/source/Plugins/Texture.Tga/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Texture.Tga/app.config +++ b/source/Plugins/Texture.Tga/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Train.MsTs/Train.MsTs.csproj b/source/Plugins/Train.MsTs/Train.MsTs.csproj index 297deff4c3..bed86088c2 100644 --- a/source/Plugins/Train.MsTs/Train.MsTs.csproj +++ b/source/Plugins/Train.MsTs/Train.MsTs.csproj @@ -31,12 +31,9 @@ 4 - - ..\..\..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\netstandard2.0\Microsoft.Bcl.AsyncInterfaces.dll - - - ..\..\..\packages\SharpCompress.0.48.0\lib\netstandard2.0\SharpCompress.dll + + ..\..\..\packages\SharpCompress.0.32.2\lib\net461\SharpCompress.dll @@ -54,11 +51,8 @@ ..\..\..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - ..\..\..\packages\System.Text.Encoding.CodePages.8.0.0\lib\netstandard2.0\System.Text.Encoding.CodePages.dll - - - ..\..\..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + ..\..\..\packages\System.Text.Encoding.CodePages.6.0.0\lib\net461\System.Text.Encoding.CodePages.dll ..\..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll diff --git a/source/Plugins/Train.MsTs/app.config b/source/Plugins/Train.MsTs/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Train.MsTs/app.config +++ b/source/Plugins/Train.MsTs/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Train.MsTs/packages.config b/source/Plugins/Train.MsTs/packages.config index 320403e49f..095a12e071 100644 --- a/source/Plugins/Train.MsTs/packages.config +++ b/source/Plugins/Train.MsTs/packages.config @@ -1,12 +1,10 @@  - - + - - + \ No newline at end of file diff --git a/source/Plugins/Train.OpenBve/Panel/PanelAnimatedXmlParser.cs b/source/Plugins/Train.OpenBve/Panel/PanelAnimatedXmlParser.cs index a2391d615c..d58e05270d 100644 --- a/source/Plugins/Train.OpenBve/Panel/PanelAnimatedXmlParser.cs +++ b/source/Plugins/Train.OpenBve/Panel/PanelAnimatedXmlParser.cs @@ -222,7 +222,7 @@ private void CreateTouchElement(ElementsGroup Group, Vector3 Position, Vector3 S Vertex t7 = new Vertex(-Size.X, Size.Y, Size.Z); StaticObject Object = new StaticObject(Plugin.CurrentHost); Object.Mesh.Vertices = new VertexTemplate[] { t0, t1, t2, t3, t4, t5, t6, t7 }; - Object.Mesh.Faces = new[] { new MeshFace(new[] { 0, 1, 2, 0, 2 ,3 }), new MeshFace(new[] { 0, 4, 5, 0, 5, 1 }), new MeshFace(new[] { 0, 3, 7, 0, 7, 4 }), new MeshFace(new[] { 6, 5, 4, 6, 4, 7 }), new MeshFace(new[] { 6, 7, 3, 6, 3, 2 }), new MeshFace(new[] { 6, 2, 1, 6, 1, 5 }) }; + Object.Mesh.Faces = new[] { new MeshFace(new[] { 0, 1, 2, 3 }), new MeshFace(new[] { 0, 4, 5, 1 }), new MeshFace(new[] { 0, 3, 7, 4 }), new MeshFace(new[] { 6, 5, 4, 7 }), new MeshFace(new[] { 6, 7, 3, 2 }), new MeshFace(new[] { 6, 2, 1, 5 }) }; Object.Mesh.Faces[0].Flags |= FaceFlags.Triangles; Object.Mesh.Faces[1].Flags |= FaceFlags.Triangles; Object.Mesh.Faces[2].Flags |= FaceFlags.Triangles; diff --git a/source/Plugins/Train.OpenBve/Panel/PanelCfgParser.cs b/source/Plugins/Train.OpenBve/Panel/PanelCfgParser.cs index ec23e51971..7f81362970 100644 --- a/source/Plugins/Train.OpenBve/Panel/PanelCfgParser.cs +++ b/source/Plugins/Train.OpenBve/Panel/PanelCfgParser.cs @@ -40,9 +40,6 @@ internal PanelCfgParser(Plugin plugin) private double WorldLeft, WorldTop; private double SemiHeight = 240; - private Texture panelTexture; - private bool isBackground = true; - /// Parses a BVE1 panel.cfg file /// The on-disk path to the train /// The train's text encoding @@ -84,13 +81,11 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, if (File.Exists(PanelBackground)) { - Plugin.CurrentHost.RegisterTexture(PanelBackground, new TextureParameters(null, Color24.Blue), out panelTexture, true); + Plugin.CurrentHost.RegisterTexture(PanelBackground, new TextureParameters(null, Color24.Blue), out var panelTexture, true); SemiHeight = PanelSize.Y - panelTexture.Height; - CreateElement(Car, 0, SemiHeight, panelTexture.Width, panelTexture.Height, WorldZ + EyeDistance, false, panelTexture, Color32.White); + CreateElement(Car, 0, SemiHeight, panelTexture.Width, panelTexture.Height, WorldZ + EyeDistance, panelTexture, Color32.White); } - isBackground = false; - string compatibilityFolder = Plugin.FileSystem.GetDataFolder("Compatibility"); while (cfg.RemainingSubBlocks > 0) { @@ -116,7 +111,7 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, case PanelSections.PressureGauge: Angle = 45; Block.GetValue(PanelKey.Type, out Type); - PanelSubject[] NeedleType = { PanelSubject.Unknown, PanelSubject.Unknown }; + int[] NeedleType = { 0, 0 }; Color32[] NeedleColor = { Color32.Black, Color32.Black }; double UnitFactor = 1000; @@ -129,13 +124,13 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, if (Block.GetEnumValue(PanelKey.LowerHand, out PanelSubject lowerSubject, out Color32 lowerColor)) { - NeedleType[0] = lowerSubject; + NeedleType[0] = (int)lowerSubject; NeedleColor[0] = lowerColor; } if (Block.GetEnumValue(PanelKey.UpperHand, out PanelSubject upperSubject, out Color32 upperColor)) { - NeedleType[1] = upperSubject; + NeedleType[1] = (int)upperSubject; NeedleColor[1] = upperColor; } @@ -176,14 +171,14 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, if (!string.IsNullOrEmpty(Background) && File.Exists(Background)) { Plugin.CurrentHost.RegisterTexture(Background, new TextureParameters(null, Color24.Blue), out var pressureBackgroundTexture, true); - CreateElement(Car, Center.X - 0.5 * pressureBackgroundTexture.Width, Center.Y + SemiHeight - 0.5 * pressureBackgroundTexture.Height, WorldZ + EyeDistance - 3.0 * StackDistance, false, pressureBackgroundTexture); + CreateElement(Car, Center.X - 0.5 * pressureBackgroundTexture.Width, Center.Y + SemiHeight - 0.5 * pressureBackgroundTexture.Height, WorldZ + EyeDistance - 3.0 * StackDistance, pressureBackgroundTexture); } // cover if (!string.IsNullOrEmpty(Cover) && File.Exists(Cover)) { Plugin.CurrentHost.RegisterTexture(Cover, new TextureParameters(null, Color24.Blue), out var pressureCoverTexture, true); - CreateElement(Car, Center.X - 0.5 * pressureCoverTexture.Width, Center.Y + SemiHeight - 0.5 * pressureCoverTexture.Height, WorldZ + EyeDistance - 6.0 * StackDistance, false, pressureCoverTexture); + CreateElement(Car, Center.X - 0.5 * pressureCoverTexture.Width, Center.Y + SemiHeight - 0.5 * pressureCoverTexture.Height, WorldZ + EyeDistance - 6.0 * StackDistance, pressureCoverTexture); } if (Type == 0) @@ -195,7 +190,7 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, { TextureFile = Path.CombineFile(compatibilityFolder, k == 0 ? "needle_pressuregauge_lower.png" : "needle_pressuregauge_upper.png"); Plugin.CurrentHost.RegisterTexture(TextureFile, TextureParameters.NoChange, out var pressureNeedleTexture, true); - int j = CreateElement(Car, Center.X - Radius * pressureNeedleTexture.AspectRatio, Center.Y + SemiHeight - Radius, 2.0 * Radius * pressureNeedleTexture.AspectRatio, 2.0 * Radius, WorldZ + EyeDistance - (4 + k) * StackDistance, true, pressureNeedleTexture, NeedleColor[k]); + int j = CreateElement(Car, Center.X - Radius * pressureNeedleTexture.AspectRatio, Center.Y + SemiHeight - Radius, 2.0 * Radius * pressureNeedleTexture.AspectRatio, 2.0 * Radius, WorldZ + EyeDistance - (4 + k) * StackDistance, pressureNeedleTexture, NeedleColor[k]); Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateZDirection = Vector3.Backward; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateXDirection = Vector3.Right; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateYDirection = Vector3.Cross(Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateZDirection, Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateXDirection); @@ -204,19 +199,19 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, string Variable = "0"; switch (NeedleType[k]) { - case PanelSubject.BC: + case 1: Variable = "brakecylinder"; break; - case PanelSubject.SAP: + case 2: Variable = "straightairpipe"; break; - case PanelSubject.BP: + case 3: Variable = "brakepipe"; break; - case PanelSubject.ER: + case 4: Variable = "equalizingreservoir"; break; - case PanelSubject.MR: + case 5: Variable = "mainreservoir"; break; } @@ -230,7 +225,7 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, // leds if (NeedleType[1] != 0) { - int j = CreateElement(Car, Center.X - Radius, Center.Y + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, WorldZ + EyeDistance - 5.0 * StackDistance, true, null, NeedleColor[1]); + int j = CreateElement(Car, Center.X - Radius, Center.Y + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, WorldZ + EyeDistance - 5.0 * StackDistance, null, NeedleColor[1]); double x0 = Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].States[0].Prototype.Mesh.Vertices[0].Coordinates.X; double y0 = Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].States[0].Prototype.Mesh.Vertices[0].Coordinates.Y; double z0 = Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].States[0].Prototype.Mesh.Vertices[0].Coordinates.Z; @@ -278,19 +273,19 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, string Variable; switch (NeedleType[1]) { - case PanelSubject.BC: + case 1: Variable = "brakecylinder"; break; - case PanelSubject.SAP: + case 2: Variable = "straightairpipe"; break; - case PanelSubject.BP: + case 3: Variable = "brakepipe"; break; - case PanelSubject.ER: + case 4: Variable = "equalizingreservoir"; break; - case PanelSubject.MR: + case 5: Variable = "mainreservoir"; break; default: @@ -343,14 +338,14 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, { // background/led Plugin.CurrentHost.RegisterTexture(Background, new TextureParameters(null, Color24.Blue), out var speedometerBackgroundTexture, true); - CreateElement(Car, Center.X - 0.5 * speedometerBackgroundTexture.Width, Center.Y + SemiHeight - 0.5 * speedometerBackgroundTexture.Height, WorldZ + EyeDistance - 3.0 * StackDistance, false, speedometerBackgroundTexture); + CreateElement(Car, Center.X - 0.5 * speedometerBackgroundTexture.Width, Center.Y + SemiHeight - 0.5 * speedometerBackgroundTexture.Height, WorldZ + EyeDistance - 3.0 * StackDistance, speedometerBackgroundTexture); } if (!string.IsNullOrEmpty(Cover)) { // cover Plugin.CurrentHost.RegisterTexture(Cover, new TextureParameters(null, Color24.Blue), out var speedometerCoverTexture, true); - CreateElement(Car, Center.X - 0.5 * speedometerCoverTexture.Width, Center.Y + SemiHeight - 0.5 * speedometerCoverTexture.Height, WorldZ + EyeDistance - 6.0 * StackDistance, false, speedometerCoverTexture); + CreateElement(Car, Center.X - 0.5 * speedometerCoverTexture.Width, Center.Y + SemiHeight - 0.5 * speedometerCoverTexture.Height, WorldZ + EyeDistance - 6.0 * StackDistance, speedometerCoverTexture); } if (!string.IsNullOrEmpty(ATCPath)) @@ -420,11 +415,11 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, Plugin.CurrentHost.RegisterTexture(ATCPath, new TextureParameters(new TextureClipRegion(j * atcHeight, 0, atcHeight, atcHeight), Color24.Blue), out var ATCTexture, true); if (j == 0) { - k = CreateElement(Car, x, y, atcHeight, atcHeight, WorldZ + EyeDistance - 4.0 * StackDistance, false, ATCTexture, Color32.White); + k = CreateElement(Car, x, y, atcHeight, atcHeight, WorldZ + EyeDistance - 4.0 * StackDistance, ATCTexture, Color32.White); } else { - CreateElement(Car, x, y, atcHeight, atcHeight, WorldZ + EyeDistance - 4.0 * StackDistance, false, ATCTexture, Color32.White, true); + CreateElement(Car, x, y, atcHeight, atcHeight, WorldZ + EyeDistance - 4.0 * StackDistance, ATCTexture, Color32.White, true); } } @@ -437,7 +432,7 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, // needle TextureFile = Path.CombineFile(compatibilityFolder, "needle_speedometer.png"); Plugin.CurrentHost.RegisterTexture(TextureFile, TextureParameters.NoChange, out var speedometerNeedleTexture, true); - int j = CreateElement(Car, Center.X - Radius * speedometerNeedleTexture.AspectRatio, Center.Y + SemiHeight - Radius, 2.0 * Radius * speedometerNeedleTexture.AspectRatio, 2.0 * Radius, WorldZ + EyeDistance - 5.0 * StackDistance, true, speedometerNeedleTexture, needleColor); + int j = CreateElement(Car, Center.X - Radius * speedometerNeedleTexture.AspectRatio, Center.Y + SemiHeight - Radius, 2.0 * Radius * speedometerNeedleTexture.AspectRatio, 2.0 * Radius, WorldZ + EyeDistance - 5.0 * StackDistance, speedometerNeedleTexture, needleColor); Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateZDirection = Vector3.Backward; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateXDirection = Vector3.Right; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateYDirection = Vector3.Cross(Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateZDirection, Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].RotateXDirection); @@ -449,7 +444,7 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, { // led if (!needleColorOverridden) needleColor = Color32.Black; - int j = CreateElement(Car, Center.X - Radius, Center.Y + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, WorldZ + EyeDistance - 5.0 * StackDistance, true, null, needleColor); + int j = CreateElement(Car, Center.X - Radius, Center.Y + SemiHeight - Radius, 2.0 * Radius, 2.0 * Radius, WorldZ + EyeDistance - 5.0 * StackDistance, null, needleColor); double x0 = Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].States[0].Prototype.Mesh.Vertices[0].Coordinates.X; double y0 = Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].States[0].Prototype.Mesh.Vertices[0].Coordinates.Y; double z0 = Car.CarSections[CarSectionType.Interior].Groups[0].Elements[j].States[0].Prototype.Mesh.Vertices[0].Coordinates.Z; @@ -589,11 +584,11 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, { if (j == 0) { - k = CreateElement(Car, Corner.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, false, digitalNumberTextures[j], Color32.White); + k = CreateElement(Car, Corner.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, digitalNumberTextures[j], Color32.White); } else { - CreateElement(Car, Corner.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, false, digitalNumberTextures[j], Color32.White, true); + CreateElement(Car, Corner.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, digitalNumberTextures[j], Color32.White, true); } } Car.CarSections[CarSectionType.Interior].Groups[0].Elements[k].StateFunction = new FunctionScript(Plugin.CurrentHost, "speedometer abs " + UnitFactor.ToString(Culture) + " * ~ 100 >= <> 100 quotient 10 mod 10 ?", false); @@ -604,11 +599,11 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, { if (j == 0) { - k = CreateElement(Car, Corner.X + Size.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, false, digitalNumberTextures[j], Color32.White); + k = CreateElement(Car, Corner.X + Size.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, digitalNumberTextures[j], Color32.White); } else { - CreateElement(Car, Corner.X + Size.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, false, digitalNumberTextures[j], Color32.White, true); + CreateElement(Car, Corner.X + Size.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, digitalNumberTextures[j], Color32.White, true); } } Car.CarSections[CarSectionType.Interior].Groups[0].Elements[k].StateFunction = new FunctionScript(Plugin.CurrentHost, "speedometer abs " + UnitFactor.ToString(Culture) + " * ~ 10 >= <> 10 quotient 10 mod 10 ?", false); @@ -619,11 +614,11 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, { if (j == 0) { - k = CreateElement(Car, Corner.X + 2.0 * Size.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, false, digitalNumberTextures[j], Color32.White); + k = CreateElement(Car, Corner.X + 2.0 * Size.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, digitalNumberTextures[j], Color32.White); } else { - CreateElement(Car, Corner.X + 2.0 * Size.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, false, digitalNumberTextures[j], Color32.White, true); + CreateElement(Car, Corner.X + 2.0 * Size.X, Corner.Y + SemiHeight, Size.X, Size.Y, WorldZ + EyeDistance - 7.0 * StackDistance, digitalNumberTextures[j], Color32.White, true); } } @@ -644,8 +639,8 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, Plugin.CurrentHost.RegisterTexture(turnOnPath, new TextureParameters(null, Color24.Blue), out var t0, true); Plugin.CurrentHost.RegisterTexture(turnOffPath, new TextureParameters(null, Color24.Blue), out var t1, true); - int elementIndex = CreateElement(Car, Corner.X, Corner.Y + SemiHeight, WorldZ + EyeDistance - 2.0 * StackDistance, false, t0); - CreateElement(Car, Corner.X, Corner.Y + SemiHeight, WorldZ + EyeDistance - 2.0 * StackDistance, false, t1, true); + int elementIndex = CreateElement(Car, Corner.X, Corner.Y + SemiHeight, WorldZ + EyeDistance - 2.0 * StackDistance, t0); + CreateElement(Car, Corner.X, Corner.Y + SemiHeight, WorldZ + EyeDistance - 2.0 * StackDistance, t1, true); Car.CarSections[CarSectionType.Interior].Groups[0].Elements[elementIndex].StateFunction = new FunctionScript(Plugin.CurrentHost, "doors 0 !=", false); break; case PanelSections.Watch: @@ -663,13 +658,13 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, if (!string.IsNullOrEmpty(Background)) { Plugin.CurrentHost.RegisterTexture(Background, new TextureParameters(null, Color24.Blue), out var watchBackgroundTexture, true); - CreateElement(Car, Center.X - 0.5 * watchBackgroundTexture.Width, Center.Y + SemiHeight - 0.5 * watchBackgroundTexture.Height, WorldZ + EyeDistance - 3.0 * StackDistance, false, watchBackgroundTexture); + CreateElement(Car, Center.X - 0.5 * watchBackgroundTexture.Width, Center.Y + SemiHeight - 0.5 * watchBackgroundTexture.Height, WorldZ + EyeDistance - 3.0 * StackDistance, watchBackgroundTexture); } // hour TextureFile = Path.CombineFile(compatibilityFolder, "needle_hour.png"); Plugin.CurrentHost.RegisterTexture(TextureFile, TextureParameters.NoChange, out var hourTexture, true); - int handElement = CreateElement(Car, Center.X - handRadius * hourTexture.AspectRatio, Center.Y + SemiHeight - handRadius, 2.0 * handRadius * hourTexture.AspectRatio, 2.0 * handRadius, WorldZ + EyeDistance - 4.0 * StackDistance, true, hourTexture, handColor); + int handElement = CreateElement(Car, Center.X - handRadius * hourTexture.AspectRatio, Center.Y + SemiHeight - handRadius, 2.0 * handRadius * hourTexture.AspectRatio, 2.0 * handRadius, WorldZ + EyeDistance - 4.0 * StackDistance, hourTexture, handColor); Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateZDirection = Vector3.Backward; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateXDirection = Vector3.Right; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateYDirection = Vector3.Cross(Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateZDirection, Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateXDirection); @@ -678,7 +673,7 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, // minute TextureFile = Path.CombineFile(compatibilityFolder, "needle_minute.png"); Plugin.CurrentHost.RegisterTexture(TextureFile, TextureParameters.NoChange, out var minuteTexture, true); - handElement = CreateElement(Car, Center.X - handRadius * minuteTexture.AspectRatio, Center.Y + SemiHeight - handRadius, 2.0 * handRadius * minuteTexture.AspectRatio, 2.0 * handRadius, WorldZ + EyeDistance - 5.0 * StackDistance, true, minuteTexture, handColor); + handElement = CreateElement(Car, Center.X - handRadius * minuteTexture.AspectRatio, Center.Y + SemiHeight - handRadius, 2.0 * handRadius * minuteTexture.AspectRatio, 2.0 * handRadius, WorldZ + EyeDistance - 5.0 * StackDistance, minuteTexture, handColor); Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateZDirection = Vector3.Backward; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateXDirection = Vector3.Right; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateYDirection = Vector3.Cross(Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateZDirection, Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateXDirection); @@ -687,7 +682,7 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, // second TextureFile = Path.CombineFile(compatibilityFolder, "needle_second.png"); Plugin.CurrentHost.RegisterTexture(TextureFile, TextureParameters.NoChange, out var secondTexture, true); - handElement = CreateElement(Car, Center.X - handRadius * secondTexture.AspectRatio, Center.Y + SemiHeight - handRadius, 2.0 * handRadius * secondTexture.AspectRatio, 2.0 * handRadius, WorldZ + EyeDistance - 6.0 * StackDistance, true, secondTexture, handColor); + handElement = CreateElement(Car, Center.X - handRadius * secondTexture.AspectRatio, Center.Y + SemiHeight - handRadius, 2.0 * handRadius * secondTexture.AspectRatio, 2.0 * handRadius, WorldZ + EyeDistance - 6.0 * StackDistance, secondTexture, handColor); Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateZDirection = Vector3.Backward; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateXDirection = Vector3.Right; Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateYDirection = Vector3.Cross(Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateZDirection, Car.CarSections[CarSectionType.Interior].Groups[0].Elements[handElement].RotateXDirection); @@ -719,11 +714,11 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, Plugin.CurrentHost.RegisterTexture(brakeIndicatorPath, new TextureParameters(clip, Color24.Blue), out var brakeIndicatorTexture); if (j == 0) { - k = CreateElement(Car, Corner.X, Corner.Y + SemiHeight, indicatorWidth, h, WorldZ + EyeDistance - StackDistance, false, brakeIndicatorTexture, Color32.White); + k = CreateElement(Car, Corner.X, Corner.Y + SemiHeight, indicatorWidth, h, WorldZ + EyeDistance - StackDistance, brakeIndicatorTexture, Color32.White); } else { - CreateElement(Car, Corner.X, Corner.Y + SemiHeight, indicatorWidth, h, WorldZ + EyeDistance - StackDistance, false, brakeIndicatorTexture, Color32.White, true); + CreateElement(Car, Corner.X, Corner.Y + SemiHeight, indicatorWidth, h, WorldZ + EyeDistance - StackDistance, brakeIndicatorTexture, Color32.White, true); } } @@ -756,63 +751,14 @@ internal void ParsePanelConfig(string TrainPath, System.Text.Encoding Encoding, } } - private int CreateElement(CarBase Car, double Left, double Top, double WorldZ, bool IsNeedle, Texture Texture, bool AddStateToLastElement = false) + private int CreateElement(CarBase Car, double Left, double Top, double WorldZ, Texture Texture, bool AddStateToLastElement = false) { - return CreateElement(Car, Left, Top, Texture.Width, Texture.Height, WorldZ, IsNeedle, Texture, Color32.White, AddStateToLastElement); + return CreateElement(Car, Left, Top, Texture.Width, Texture.Height, WorldZ, Texture, Color32.White, AddStateToLastElement); } // create element - private int CreateElement(CarBase Car, double Left, double Top, double Width, double Height, double WorldZ, bool IsNeedle, Texture Texture, Color32 Color, bool AddStateToLastElement = false) + private int CreateElement(CarBase Car, double Left, double Top, double Width, double Height, double WorldZ, Texture Texture, Color32 Color, bool AddStateToLastElement = false) { - if (Plugin.CurrentOptions.EnableBveTsHacks && isBackground == false) - { - /* - * In regions where the main panel texture is transparent, - * BVE2 renders any other overlapping elements as transparent also - * - * e.g. Portuguese trains: - * CP1400 - * CP2100 - * CP3500 - * CP4000 - * - * All of these have junk extra elements in the main panel view - * (Appears to have come from copy+pasting between trains) - * - * So, what we need to do is to extract the region of the main overlapping texture - * Now, if this is *not* opaque, apply as a mask to the alpha channel - * - */ - double clipWidth = Math.Min(Width, panelTexture.Width - Left); - double clipHeight = Math.Min(Height, panelTexture.Height + SemiHeight - Top); - Texture temp = panelTexture.ApplyParameters(new TextureParameters(new TextureClipRegion(Math.Max(0, (int)Left) - , Math.Max(0, (int)(Top - SemiHeight)), (int)clipWidth, (int)clipHeight), Color24.Blue)); - - temp.Origin.GetTexture(out Texture t); - switch (t.GetTransparencyType()) - { - case TextureTransparencyType.Transparent: - // totally hidden - Texture = new Texture(1, 1, PixelFormat.RGBAlpha, new byte[] { 0, 0, 0, 0 }, new Color24[] { }); - break; - case TextureTransparencyType.Partial: - if (IsNeedle) - { - // as needles rotate, we can't just apply a mask and have to hide the whole thing - Texture = new Texture(1, 1, PixelFormat.RGBAlpha, new byte[] { 0, 0, 0, 0 }, new Color24[] { }); - } - else - { - Texture.Origin.GetTexture(out Texture tt); - Texture = tt.ApplyParameters(new TextureParameters(null, null, t)); - } - - break; - } - - } - - // create object StaticObject Object = new StaticObject(Plugin.CurrentHost); Vector3[] v = new Vector3[4]; diff --git a/source/Plugins/Train.OpenBve/Panel/PanelXmlParser.cs b/source/Plugins/Train.OpenBve/Panel/PanelXmlParser.cs index 60030cea41..048dfdafbd 100644 --- a/source/Plugins/Train.OpenBve/Panel/PanelXmlParser.cs +++ b/source/Plugins/Train.OpenBve/Panel/PanelXmlParser.cs @@ -2114,7 +2114,7 @@ internal void CreateTouchElement(ElementsGroup Group, Vector2 Location, Vector2 Vertex t3 = new Vertex(v[3], new Vector2(1.0f, 1.0f)); StaticObject Object = new StaticObject(Plugin.CurrentHost); Object.Mesh.Vertices = new VertexTemplate[] { t0, t1, t2, t3 }; - Object.Mesh.Faces = new[] { new MeshFace(new[] { 0, 1, 2, 0, 2, 3 }) }; + Object.Mesh.Faces = new[] { new MeshFace(new[] { 0, 1, 2, 3 }) }; Object.Mesh.Faces[0].Flags |= FaceFlags.Triangles; Object.Mesh.Materials = new MeshMaterial[1]; Object.Mesh.Materials[0].Flags = 0; diff --git a/source/Plugins/Train.OpenBve/Sound/SoundCfg.Xml.cs b/source/Plugins/Train.OpenBve/Sound/SoundCfg.Xml.cs index 861bf32ac3..67a176026c 100644 --- a/source/Plugins/Train.OpenBve/Sound/SoundCfg.Xml.cs +++ b/source/Plugins/Train.OpenBve/Sound/SoundCfg.Xml.cs @@ -181,6 +181,10 @@ internal void Parse(string fileName, ref TrainBase Train, ref CarBase car, bool } break; case SoundXMLSection.Door: + if (!isDriverCar) + { + break; + } while (subBlock.RemainingSubBlocks > 0) { Block doorSubBlock = subBlock.ReadNextBlock(); @@ -551,17 +555,15 @@ private void ParseMotorSoundTableBlock(Block block if (motorSound is BVEMotorSound bveMotorSound) { - ParseBlock(subBlock, out SoundBuffer buffer, ref Position, Radius); for (int i = 0; i < bveMotorSound.Tables.Length; i++) { bveMotorSound.Tables[i].Buffer = null; bveMotorSound.Tables[i].Source = null; - for (int j = 0; j < bveMotorSound.Tables[i].Entries.Length; j++) { if (idx == bveMotorSound.Tables[i].Entries[j].SoundIndex) { - bveMotorSound.Tables[i].Entries[j].Buffer = buffer; + ParseBlock(subBlock, out bveMotorSound.Tables[i].Entries[j].Buffer, ref Position, Radius); } } } diff --git a/source/Plugins/Train.OpenBve/Train/BVE/ExtensionsCfgParser.cs b/source/Plugins/Train.OpenBve/Train/BVE/ExtensionsCfgParser.cs index 44058dd66f..dca8ca6faf 100644 --- a/source/Plugins/Train.OpenBve/Train/BVE/ExtensionsCfgParser.cs +++ b/source/Plugins/Train.OpenBve/Train/BVE/ExtensionsCfgParser.cs @@ -18,286 +18,286 @@ internal ExtensionsCfgParser(Plugin plugin) Plugin = plugin; } - internal static bool UnicodeCheck; + internal static bool unicodeCheck; - - internal void ParseExtensionsConfig(string trainPath, Encoding trainEncoding, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, ref UnifiedObject[] CouplerObjects, out bool[] VisibleFromInterior, TrainBase Train) + // parse extensions config + internal void ParseExtensionsConfig(string TrainPath, Encoding Encoding, ref UnifiedObject[] CarObjects, ref UnifiedObject[] BogieObjects, ref UnifiedObject[] CouplerObjects, out bool[] VisibleFromInterior, TrainBase Train) { VisibleFromInterior = new bool[Train.Cars.Length]; bool[] CarObjectsReversed = new bool[Train.Cars.Length]; bool[] BogieObjectsReversed = new bool[Train.Cars.Length * 2]; bool[] CarsDefined = new bool[Train.Cars.Length]; bool[] BogiesDefined = new bool[Train.Cars.Length * 2]; - string fileName = Path.CombineFile(trainPath, "extensions.cfg"); - if (!System.IO.File.Exists(fileName)) - { - return; - } + string FileName = Path.CombineFile(TrainPath, "extensions.cfg"); + if (System.IO.File.Exists(FileName)) { + Encoding = TextEncoding.GetSystemEncodingFromFile(FileName, Encoding); - trainEncoding = TextEncoding.GetSystemEncodingFromFile(fileName, trainEncoding); - - string[] Lines = System.IO.File.ReadAllLines(fileName, trainEncoding); - if (Lines.Length == 1) - { - if (UnicodeCheck) + string[] Lines = System.IO.File.ReadAllLines(FileName, Encoding); + if (Lines.Length == 1) { + if (unicodeCheck) + { + return; + } + + unicodeCheck = true; + /* + * If only one line, there's a good possibility that our file is NOT Unicode at all + * and that the misdetection has turned it into garbage + * + * Try again with ASCII instead + */ + ParseExtensionsConfig(TrainPath, Encoding.GetEncoding(1252), ref CarObjects, ref BogieObjects, ref CouplerObjects, out VisibleFromInterior, Train); return; } + ConfigFile cfg = new ConfigFile(Lines, FileName, Plugin.CurrentHost); - UnicodeCheck = true; - /* - * If only one line, there's a good possibility that our file is NOT Unicode at all - * and that the misdetection has turned it into garbage - * - * Try again with ASCII instead - */ - ParseExtensionsConfig(trainPath, Encoding.GetEncoding(1252), ref CarObjects, ref BogieObjects, ref CouplerObjects, out VisibleFromInterior, Train); - return; - } - ConfigFile cfg = new ConfigFile(Lines, fileName, Plugin.CurrentHost); - - double perBlockProgress = cfg.RemainingSubBlocks == 0 ? 0.25 : 0.25 / cfg.RemainingSubBlocks; - int readBlocks = 0; - while (cfg.RemainingSubBlocks > 0) - { - Plugin.CurrentProgress = Plugin.LastProgress + perBlockProgress * readBlocks; - Block block = cfg.ReadNextBlock(); - switch (block.Key) + double perBlockProgress = cfg.RemainingSubBlocks == 0 ? 0.25 : 0.25 / cfg.RemainingSubBlocks; + int readBlocks = 0; + while (cfg.RemainingSubBlocks > 0) { - case ExtensionCfgSection.Exterior: - while (block.RemainingDataValues > 0 && block.GetIndexedPath(trainPath, out var carIndex, out string exteriorObject)) - { - Plugin.CurrentHost.LoadObject(exteriorObject, trainEncoding, out CarObjects[carIndex]); - } - break; - case ExtensionCfgSection.Car: - if (block.Index == -1) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid or missing CarIndex in file " + block.FileName); + Plugin.CurrentProgress = Plugin.LastProgress + perBlockProgress * readBlocks; + Block block = cfg.ReadNextBlock(); + switch (block.Key) + { + case ExtensionCfgSection.Exterior: + while (block.RemainingDataValues > 0 && block.GetIndexedPath(TrainPath, out var carIndex, out var fileName)) + { + Plugin.CurrentHost.LoadObject(fileName, Encoding, out CarObjects[carIndex]); + } break; - } + case ExtensionCfgSection.Car: + if (block.Index == -1) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid or missing CarIndex in file " + block.FileName); + break; + } - if (block.Index >= Train.Cars.Length) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "CarIndex " + block.Index + " does not reference an existing car in in file " + block.FileName); - break; - } + if (block.Index >= Train.Cars.Length) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "CarIndex " + block.Index + " does not reference an existing car in in file " + block.FileName); + break; + } - if (CarsDefined[block.Index]) - { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "CarIndex " + block.Index + " has already been declared in in file " + block.FileName); - } + if (CarsDefined[block.Index]) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "CarIndex " + block.Index + " has already been declared in in file " + block.FileName); + } - CarsDefined[block.Index] = true; - if (block.GetPath(ExtensionCfgKey.Object, trainPath, out string carObject)) - { - Plugin.CurrentHost.LoadObject(carObject, trainEncoding, out CarObjects[block.Index]); - } + CarsDefined[block.Index] = true; + if (block.GetPath(ExtensionCfgKey.Object, TrainPath, out string carObject)) + { + Plugin.CurrentHost.LoadObject(carObject, Encoding, out CarObjects[block.Index]); + } - bool definedLength = false; - if (block.GetValue(ExtensionCfgKey.Length, out double carLength, NumberRange.Positive)) - { - Train.Cars[block.Index].Length = carLength; - Train.Cars[block.Index].BeaconReceiverPosition = 0.5 * carLength; - definedLength = true; - } - block.GetValue(ExtensionCfgKey.Reversed, out CarObjectsReversed[block.Index]); - block.GetValue(ExtensionCfgKey.VisibleFromInterior, out VisibleFromInterior[block.Index]); - block.GetValue(ExtensionCfgKey.LoadingSway, out Train.Cars[block.Index].EnableLoadingSway); - if (block.GetVector2(ExtensionCfgKey.Axles, ',', out Vector2 carAxles)) - { - if (carAxles.X >= carAxles.Y) + bool definedLength = false; + if (block.GetValue(ExtensionCfgKey.Length, out double carLength, NumberRange.Positive)) { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front for Car " + block.Index + " in file " + block.FileName); + Train.Cars[block.Index].Length = carLength; + Train.Cars[block.Index].BeaconReceiverPosition = 0.5 * carLength; + definedLength = true; } - else + block.GetValue(ExtensionCfgKey.Reversed, out CarObjectsReversed[block.Index]); + block.GetValue(ExtensionCfgKey.VisibleFromInterior, out VisibleFromInterior[block.Index]); + block.GetValue(ExtensionCfgKey.LoadingSway, out Train.Cars[block.Index].EnableLoadingSway); + if (block.GetVector2(ExtensionCfgKey.Axles, ',', out Vector2 carAxles)) { - Train.Cars[block.Index].RearAxle.Position = carAxles.X; - Train.Cars[block.Index].FrontAxle.Position = carAxles.Y; + if (carAxles.X >= carAxles.Y) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front for Car " + block.Index + " in file " + block.FileName); + } + else + { + Train.Cars[block.Index].RearAxle.Position = carAxles.X; + Train.Cars[block.Index].FrontAxle.Position = carAxles.Y; + } } - } - else - { - if (definedLength == false) + else { - double axleDistance = 0.4 * Train.Cars[block.Index].Length; - Train.Cars[block.Index].RearAxle.Position = -axleDistance; - Train.Cars[block.Index].FrontAxle.Position = axleDistance; + if (definedLength == false) + { + double axleDistance = 0.4 * Train.Cars[block.Index].Length; + Train.Cars[block.Index].RearAxle.Position = -axleDistance; + Train.Cars[block.Index].FrontAxle.Position = axleDistance; + } } - } - break; - case ExtensionCfgSection.Coupler: - if (block.Index == -1 || block.Index >= Train.Cars.Length) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid or missing CouplerIndex in file " + block.FileName); break; - } - if (block.GetVector2(ExtensionCfgKey.Distances, ',', out Vector2 distances)) - { - if (distances.X > distances.Y) + case ExtensionCfgSection.Coupler: + if (block.Index == -1 || block.Index >= Train.Cars.Length) { - // NOTE: Current error is misleading... - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Minimum is expected to be less than Maximum in for Coupler " + block.Index + " in file " + block.FileName); + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid or missing CouplerIndex in file " + block.FileName); + break; } - else + if (block.GetVector2(ExtensionCfgKey.Distances, ',', out Vector2 distances)) { - Train.Cars[block.Index].Coupler.MinimumDistanceBetweenCars = distances.X; - Train.Cars[block.Index].Coupler.MaximumDistanceBetweenCars = distances.Y; + if (distances.X > distances.Y) + { + // NOTE: Current error is misleading... + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Minimum is expected to be less than Maximum in for Coupler " + block.Index + " in file " + block.FileName); + } + else + { + Train.Cars[block.Index].Coupler.MinimumDistanceBetweenCars = distances.X; + Train.Cars[block.Index].Coupler.MaximumDistanceBetweenCars = distances.Y; + } + } + if (block.GetPath(ExtensionCfgKey.Object, TrainPath, out string couplerObject)) + { + Plugin.CurrentHost.LoadObject(couplerObject, Encoding, out CouplerObjects[block.Index]); } - } - if (block.GetPath(ExtensionCfgKey.Object, trainPath, out string couplerObject)) - { - Plugin.CurrentHost.LoadObject(couplerObject, trainEncoding, out CouplerObjects[block.Index]); - } - break; - case ExtensionCfgSection.Bogie: - if (block.Index == -1) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid or missing BogieIndex in file " + block.FileName); break; - } + case ExtensionCfgSection.Bogie: + if (block.Index == -1) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Invalid or missing BogieIndex in file " + block.FileName); + break; + } - if (block.Index > Train.Cars.Length * 2) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BogieIndex " + block.Index + " does not reference an existing bogie in in file " + block.FileName); - break; - } + if (block.Index > Train.Cars.Length * 2) + { + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "BogieIndex " + block.Index + " does not reference an existing bogie in in file " + block.FileName); + break; + } - if (BogiesDefined[block.Index]) - { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "BogieIndex " + block.Index + " has already been declared in in file " + block.FileName); - } - BogiesDefined[block.Index] = true; - //Assuming that there are two bogies per car - bool IsOdd = (block.Index % 2 != 0); - int CarIndex = block.Index / 2; + if (BogiesDefined[block.Index]) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "BogieIndex " + block.Index + " has already been declared in in file " + block.FileName); + } + BogiesDefined[block.Index] = true; + //Assuming that there are two bogies per car + bool IsOdd = (block.Index % 2 != 0); + int CarIndex = block.Index / 2; - if (block.GetPath(ExtensionCfgKey.Object, trainPath, out string bogieObject)) - { - Plugin.CurrentHost.LoadObject(bogieObject, trainEncoding, out BogieObjects[block.Index]); - } - block.GetValue(ExtensionCfgKey.Reversed, out BogieObjectsReversed[block.Index]); - if (block.GetVector2(ExtensionCfgKey.Axles, ',', out Vector2 bogieAxles)) - { - if (bogieAxles.X >= bogieAxles.Y) + if (block.GetPath(ExtensionCfgKey.Object, TrainPath, out string bogieObject)) { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front for Bogie " + block.Index + " in file " + block.FileName); + Plugin.CurrentHost.LoadObject(bogieObject, Encoding, out BogieObjects[block.Index]); } - else + block.GetValue(ExtensionCfgKey.Reversed, out BogieObjectsReversed[block.Index]); + if (block.GetVector2(ExtensionCfgKey.Axles, ',', out Vector2 bogieAxles)) { - if (IsOdd) + if (bogieAxles.X >= bogieAxles.Y) { - Train.Cars[CarIndex].FrontBogie.RearAxle.Position = bogieAxles.X; - Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = bogieAxles.Y; + Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Rear is expected to be less than Front for Bogie " + block.Index + " in file " + block.FileName); } else { - Train.Cars[CarIndex].RearBogie.RearAxle.Position = bogieAxles.X; - Train.Cars[CarIndex].RearBogie.FrontAxle.Position = bogieAxles.Y; + if (IsOdd) + { + Train.Cars[CarIndex].FrontBogie.RearAxle.Position = bogieAxles.X; + Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = bogieAxles.Y; + } + else + { + Train.Cars[CarIndex].RearBogie.RearAxle.Position = bogieAxles.X; + Train.Cars[CarIndex].RearBogie.FrontAxle.Position = bogieAxles.Y; + } } } - } - else - { - if (IsOdd) - { - double axleDistance = 0.4 * Train.Cars[CarIndex].FrontBogie.Length; - Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -axleDistance; - Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = axleDistance; - } else { - double axleDistance = 0.4 * Train.Cars[CarIndex].RearBogie.Length; - Train.Cars[CarIndex].RearBogie.RearAxle.Position = -axleDistance; - Train.Cars[CarIndex].RearBogie.FrontAxle.Position = axleDistance; + if (IsOdd) + { + double axleDistance = 0.4 * Train.Cars[CarIndex].FrontBogie.Length; + Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -axleDistance; + Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = axleDistance; + } + else + { + double axleDistance = 0.4 * Train.Cars[CarIndex].RearBogie.Length; + Train.Cars[CarIndex].RearBogie.RearAxle.Position = -axleDistance; + Train.Cars[CarIndex].RearBogie.FrontAxle.Position = axleDistance; + } } - } - break; + break; + } + block.ReportErrors(); + readBlocks++; } - block.ReportErrors(); - readBlocks++; - } - // check for car objects and reverse if necessary - int carObjects = 0; - for (int i = 0; i < Train.Cars.Length; i++) { - if (CarObjects[i] != null) { - carObjects++; - if (CarObjectsReversed[i]) { - { - // reverse axle positions - double temp = Train.Cars[i].FrontAxle.Position; - Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position; - Train.Cars[i].RearAxle.Position = -temp; - } - if (CarObjects[i] is StaticObject) { - StaticObject obj = (StaticObject)CarObjects[i].Clone(); - obj.ApplyScale(-1.0, 1.0, -1.0); - CarObjects[i] = obj; - } else if (CarObjects[i] is AnimatedObjectCollection) { - AnimatedObjectCollection obj = (AnimatedObjectCollection)CarObjects[i].Clone(); - obj.Reverse(); - CarObjects[i] = obj; - } else if (CarObjects[i] is KeyframeAnimatedObject) { - KeyframeAnimatedObject obj = (KeyframeAnimatedObject)CarObjects[i].Clone(); - obj.Reverse(); - CarObjects[i] = obj; - } else { - throw new NotImplementedException(); + // check for car objects and reverse if necessary + int carObjects = 0; + for (int i = 0; i < Train.Cars.Length; i++) { + if (CarObjects[i] != null) { + carObjects++; + if (CarObjectsReversed[i]) { + { + // reverse axle positions + double temp = Train.Cars[i].FrontAxle.Position; + Train.Cars[i].FrontAxle.Position = -Train.Cars[i].RearAxle.Position; + Train.Cars[i].RearAxle.Position = -temp; + } + if (CarObjects[i] is StaticObject) { + StaticObject obj = (StaticObject)CarObjects[i].Clone(); + obj.ApplyScale(-1.0, 1.0, -1.0); + CarObjects[i] = obj; + } else if (CarObjects[i] is AnimatedObjectCollection) { + AnimatedObjectCollection obj = (AnimatedObjectCollection)CarObjects[i].Clone(); + obj.Reverse(); + CarObjects[i] = obj; + } else if (CarObjects[i] is KeyframeAnimatedObject) { + KeyframeAnimatedObject obj = (KeyframeAnimatedObject)CarObjects[i].Clone(); + obj.Reverse(); + CarObjects[i] = obj; + } else { + throw new NotImplementedException(); + } } } } - } - //Check for bogie objects and reverse if necessary..... - int bogieObjects = 0; - for (int i = 0; i < Train.Cars.Length * 2; i++) - { - int CarIndex = i / 2; - if (BogieObjects[i] != null) + //Check for bogie objects and reverse if necessary..... + int bogieObjects = 0; + for (int i = 0; i < Train.Cars.Length * 2; i++) { - bogieObjects++; - if (BogieObjectsReversed[i]) + bool IsOdd = (i % 2 != 0); + int CarIndex = i/2; + if (BogieObjects[i] != null) { - // reverse axle positions - if (i % 2 != 0) + bogieObjects++; + if (BogieObjectsReversed[i]) { - double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position; - Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position; - Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -temp; - } - else - { - double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position; - Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position; - Train.Cars[CarIndex].RearBogie.RearAxle.Position = -temp; - } - if (BogieObjects[i] is StaticObject) { - StaticObject obj = (StaticObject)BogieObjects[i].Clone(); - obj.ApplyScale(-1.0, 1.0, -1.0); - BogieObjects[i] = obj; - } else if (BogieObjects[i] is AnimatedObjectCollection) { - AnimatedObjectCollection obj = (AnimatedObjectCollection)BogieObjects[i].Clone(); - obj.Reverse(); - BogieObjects[i] = obj; - } else if (BogieObjects[i] is KeyframeAnimatedObject) { - KeyframeAnimatedObject obj = (KeyframeAnimatedObject)BogieObjects[i].Clone(); - obj.Reverse(); - BogieObjects[i] = obj; - } else { - throw new NotImplementedException(); + { + // reverse axle positions + if (IsOdd) + { + double temp = Train.Cars[CarIndex].FrontBogie.FrontAxle.Position; + Train.Cars[CarIndex].FrontBogie.FrontAxle.Position = -Train.Cars[CarIndex].FrontBogie.RearAxle.Position; + Train.Cars[CarIndex].FrontBogie.RearAxle.Position = -temp; + } + else + { + double temp = Train.Cars[CarIndex].RearBogie.FrontAxle.Position; + Train.Cars[CarIndex].RearBogie.FrontAxle.Position = -Train.Cars[CarIndex].RearBogie.RearAxle.Position; + Train.Cars[CarIndex].RearBogie.RearAxle.Position = -temp; + } + } + if (BogieObjects[i] is StaticObject) { + StaticObject obj = (StaticObject)BogieObjects[i].Clone(); + obj.ApplyScale(-1.0, 1.0, -1.0); + BogieObjects[i] = obj; + } else if (BogieObjects[i] is AnimatedObjectCollection) { + AnimatedObjectCollection obj = (AnimatedObjectCollection)BogieObjects[i].Clone(); + obj.Reverse(); + BogieObjects[i] = obj; + } else if (BogieObjects[i] is KeyframeAnimatedObject) { + KeyframeAnimatedObject obj = (KeyframeAnimatedObject)BogieObjects[i].Clone(); + obj.Reverse(); + BogieObjects[i] = obj; + } else { + throw new NotImplementedException(); + } } } } - } - if (carObjects > 0 && carObjects < Train.Cars.Length) { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + fileName); - } + if (carObjects > 0 && carObjects < Train.Cars.Length) { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "An incomplete set of exterior objects was provided in file " + FileName); + } - if (bogieObjects > 0 && bogieObjects < Train.Cars.Length * 2) - { - Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + fileName); + if (bogieObjects > 0 && bogieObjects < Train.Cars.Length * 2) + { + Plugin.CurrentHost.AddMessage(MessageType.Warning, false, "An incomplete set of bogie objects was provided in file " + FileName); + } } } diff --git a/source/Plugins/Train.OpenBve/Train/BVE/TrainDatParser.cs b/source/Plugins/Train.OpenBve/Train/BVE/TrainDatParser.cs index d2c13cb541..6701c8629f 100644 --- a/source/Plugins/Train.OpenBve/Train/BVE/TrainDatParser.cs +++ b/source/Plugins/Train.OpenBve/Train/BVE/TrainDatParser.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -1257,7 +1257,7 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) { } Train.Cars[i].CarBrake.MainReservoir = new MainReservoir(MainReservoirMinimumPressure, MainReservoirMaximumPressure, 0.01, (trainBrakeType == BrakeSystemType.AutomaticAirBrake ? 0.25 : 0.075) / Cars); - Train.Cars[i].CarBrake.MainReservoir.Volume = 0.5; // Organization for Co-Operation between Railways specifies 340L to 680L main reservoir capacity for EMU, so let's pick something in the middle (in m³) + Train.Cars[i].CarBrake.MainReservoir.Volume = 0.5; // Organization for Co-Operation between Railways specifies 340L to 680L main reservoir capacity for EMU, so let's pick something in the middle (in m) Train.Cars[i].CarBrake.EqualizingReservoir = new EqualizingReservoir(50000.0, 250000.0, 200000.0); Train.Cars[i].CarBrake.EqualizingReservoir.NormalPressure = 1.005 * OperatingPressure; @@ -1437,57 +1437,9 @@ internal void Parse(string FileName, Encoding Encoding, TrainBase Train) { { Train.Cars[DriverCar].HasInteriorView = true; } - - /* - * Determine the maximum width of the handle strings for drawing purposes - * (As translators may use much longer strings than we expect) - */ - if (Plugin.Renderer?.Fonts?.NormalFont != null && Translations.QuickReferences != null) - { - Vector2 rs = Plugin.Renderer.Fonts.NormalFont.MeasureString(Translations.QuickReferences.HandleForward); - if (rs.X > Train.Handles.Reverser.MaxWidth) - { - Train.Handles.Reverser.MaxWidth = rs.X; - } - rs = Plugin.Renderer.Fonts.NormalFont.MeasureString(Translations.QuickReferences.HandleNeutral); - if (rs.X > Train.Handles.Reverser.MaxWidth) - { - Train.Handles.Reverser.MaxWidth = rs.X; - } - rs = Plugin.Renderer.Fonts.NormalFont.MeasureString(Translations.QuickReferences.HandleBackward); - if (rs.X > Train.Handles.Reverser.MaxWidth) - { - Train.Handles.Reverser.MaxWidth = rs.X; - } - - rs = Plugin.Renderer.Fonts.NormalFont.MeasureString(Translations.QuickReferences.HandlePower); - if (rs.X > Train.Handles.Power.MaxWidth) - { - Train.Handles.Power.MaxWidth = rs.X; - } - rs = Plugin.Renderer.Fonts.NormalFont.MeasureString(Translations.QuickReferences.HandlePowerNull); - if (rs.X > Train.Handles.Power.MaxWidth) - { - Train.Handles.Power.MaxWidth = rs.X; - } - - rs = Plugin.Renderer.Fonts.NormalFont.MeasureString(Translations.QuickReferences.HandleBrake); - if (rs.X > Train.Handles.Brake.MaxWidth) - { - Train.Handles.Brake.MaxWidth = rs.X; - } - rs = Plugin.Renderer.Fonts.NormalFont.MeasureString(Translations.QuickReferences.HandleBrakeNull); - if (rs.X > Train.Handles.Brake.MaxWidth) - { - Train.Handles.Brake.MaxWidth = rs.X; - } - rs = Plugin.Renderer.Fonts.NormalFont.MeasureString(Translations.QuickReferences.HandleEmergency); - if (rs.X > Train.Handles.Brake.MaxWidth) - { - Train.Handles.Brake.MaxWidth = rs.X; - } - } + // finish + } } diff --git a/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.CarNode.cs b/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.CarNode.cs index 85c24e56f9..0d664d29b9 100644 --- a/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.CarNode.cs +++ b/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.CarNode.cs @@ -26,7 +26,6 @@ using LibRender2.Smoke; using LibRender2.Trains; using OpenBveApi; -using OpenBveApi.FunctionScripting; using OpenBveApi.Graphics; using OpenBveApi.Interface; using OpenBveApi.Math; @@ -456,16 +455,6 @@ private void ParseCarBlock(Block block, int Car, r Plugin.CurrentHost.RegisterTexture(texturePath, TextureParameters.NoChange, out particleTexture); } ParticleSource particleSource = new ParticleSource(Plugin.Renderer, Train.Cars[Car], emitterLocation, maximumSize, maximumGrownSize, initialMotion, maximumLifeSpan); - - if(particleSourceBlock.GetFunctionScript(new [] {TrainXMLKey.Function }, currentXMLPath, out AnimationScript function)) - { - particleSource.Controller = function as FunctionScript; - } - else - { - particleSource.Controller = new FunctionScript(Plugin.CurrentHost, Car + " enginepowerindex", false); - } - particleSourceBlock.TryGetValue(TrainXMLKey.EmitsAtIdle, ref particleSource.EmitsAtIdle); particleSource.ParticleTexture = particleTexture; Train.Cars[Car].ParticleSources.Add(particleSource); diff --git a/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.cs b/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.cs index bc9fef87b6..9f679b8e51 100644 --- a/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.cs +++ b/source/Plugins/Train.OpenBve/Train/XML/TrainXmlParser.cs @@ -83,12 +83,7 @@ internal void Parse(string fileName, TrainBase Train, ref UnifiedObject[] carObj } carBlocks[i].GetValue(TrainXMLKey.Minimum, out Train.Cars[carIndex - 1].Coupler.MinimumDistanceBetweenCars); - carBlocks[i].GetValue(TrainXMLKey.Maximum, out Train.Cars[carIndex - 1].Coupler.MaximumDistanceBetweenCars); - if (Train.Cars[carIndex - 1].Coupler.MaximumDistanceBetweenCars < Train.Cars[carIndex - 1].Coupler.MinimumDistanceBetweenCars) - { - Plugin.CurrentHost.AddMessage(MessageType.Error, false, "Maximum should be greater than or equal to Minimum for Coupler " + (carIndex -1) + " in XML file " + xmlFile.FileName); - Train.Cars[carIndex - 1].Coupler.MaximumDistanceBetweenCars = Train.Cars[carIndex - 1].Coupler.MinimumDistanceBetweenCars; - } + carBlocks[i].GetValue(TrainXMLKey.Maximum, out Train.Cars[carIndex - 1].Coupler.MinimumDistanceBetweenCars); if (carBlocks[i].GetPath(TrainXMLKey.Object, currentPath, out string objectPath)) { Plugin.CurrentHost.LoadObject(objectPath, Encoding.Default, out couplerObjects[carIndex - 1]); @@ -136,7 +131,7 @@ internal void Parse(string fileName, TrainBase Train, ref UnifiedObject[] carObj } } } - if (subBlock.TryGetStringArray(TrainXMLKey.LocoBrake, separatorChars, ref Train.Handles.LocoBrake.NotchDescriptions)) + if (subBlock.TryGetStringArray(TrainXMLKey.Brake, separatorChars, ref Train.Handles.LocoBrake.NotchDescriptions)) { for (int j = 0; j < Train.Handles.LocoBrake.NotchDescriptions.Length; j++) { diff --git a/source/Plugins/Train.OpenBve/app.config b/source/Plugins/Train.OpenBve/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/Plugins/Train.OpenBve/app.config +++ b/source/Plugins/Train.OpenBve/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/Plugins/Win32PluginProxy/App.config b/source/Plugins/Win32PluginProxy/App.config index 15eb89674c..fd89cc910b 100644 --- a/source/Plugins/Win32PluginProxy/App.config +++ b/source/Plugins/Win32PluginProxy/App.config @@ -13,10 +13,6 @@ - - - - \ No newline at end of file diff --git a/source/RouteManager2/CurrentRoute.cs b/source/RouteManager2/CurrentRoute.cs index e288ae4f80..27e23fdf4d 100644 --- a/source/RouteManager2/CurrentRoute.cs +++ b/source/RouteManager2/CurrentRoute.cs @@ -221,10 +221,13 @@ public void UpdateSection(Section Section, out Section PreviousSection) foreach (AbstractTrain t in currentHost.Trains) { - if (t.State == TrainState.Available && t.TimetableDelta > b) + if (t.State == TrainState.Available) { - b = t.TimetableDelta; - train = t; + if (t.TimetableDelta > b) + { + b = t.TimetableDelta; + train = t; + } } } } @@ -563,10 +566,13 @@ public bool ApplyPointOfInterest(TrackDirection direction) t = double.NegativeInfinity; for (int i = 0; i < PointsOfInterest.Length; i++) { - if (PointsOfInterest[i].TrackPosition < renderer.CameraTrackFollower.TrackPosition && PointsOfInterest[i].TrackPosition > t) + if (PointsOfInterest[i].TrackPosition < renderer.CameraTrackFollower.TrackPosition) { - t = PointsOfInterest[i].TrackPosition; - j = i; + if (PointsOfInterest[i].TrackPosition > t) + { + t = PointsOfInterest[i].TrackPosition; + j = i; + } } } } @@ -575,10 +581,13 @@ public bool ApplyPointOfInterest(TrackDirection direction) t = double.PositiveInfinity; for (int i = 0; i < PointsOfInterest.Length; i++) { - if (PointsOfInterest[i].TrackPosition > renderer.CameraTrackFollower.TrackPosition && PointsOfInterest[i].TrackPosition < t) + if (PointsOfInterest[i].TrackPosition > renderer.CameraTrackFollower.TrackPosition) { - t = PointsOfInterest[i].TrackPosition; - j = i; + if (PointsOfInterest[i].TrackPosition < t) + { + t = PointsOfInterest[i].TrackPosition; + j = i; + } } } } @@ -612,11 +621,14 @@ public int PlayerFirstStationIndex { for (int i = Stations.Length -1; i > 0; i--) { - if (!string.IsNullOrEmpty(InitialStationName) && string.Equals(InitialStationName, Stations[i].Name, StringComparison.InvariantCultureIgnoreCase)) + if (!string.IsNullOrEmpty(InitialStationName)) { - return i; + if (string.Equals(InitialStationName, Stations[i].Name, StringComparison.InvariantCultureIgnoreCase)) + { + return i; + } } - if (Stations[i].StopMode == StationStopMode.AllStop | Stations[i].StopMode == StationStopMode.PlayerStop && Stations[i].Stops.Length != 0) + if (Stations[i].StopMode == StationStopMode.AllStop | Stations[i].StopMode == StationStopMode.PlayerStop & Stations[i].Stops.Length != 0) { if (f == false) { @@ -630,11 +642,14 @@ public int PlayerFirstStationIndex { for (int i = 0; i < Stations.Length; i++) { - if (!string.IsNullOrEmpty(InitialStationName) && string.Equals(InitialStationName, Stations[i].Name, StringComparison.InvariantCultureIgnoreCase)) + if (!string.IsNullOrEmpty(InitialStationName)) { - return i; + if (string.Equals(InitialStationName, Stations[i].Name, StringComparison.InvariantCultureIgnoreCase)) + { + return i; + } } - if (Stations[i].StopMode == StationStopMode.AllStop | Stations[i].StopMode == StationStopMode.PlayerStop && Stations[i].Stops.Length != 0) + if (Stations[i].StopMode == StationStopMode.AllStop | Stations[i].StopMode == StationStopMode.PlayerStop & Stations[i].Stops.Length != 0) { if (f == false) { diff --git a/source/RouteManager2/Illustrations/RouteMap.cs b/source/RouteManager2/Illustrations/RouteMap.cs index 27a151eab6..2e8a1fb7d4 100644 --- a/source/RouteManager2/Illustrations/RouteMap.cs +++ b/source/RouteManager2/Illustrations/RouteMap.cs @@ -18,6 +18,10 @@ namespace RouteManager2 /// Used for creating illustrations public static class Illustrations { + /// Holds the current lock for the illustrations drawing functions + /// GDI Plus is not thread-safe- This object should be locked on when drawing a route illustration / gradient profile + public static readonly object Locker = new object(); + internal static CurrentRoute CurrentRoute; private const double LeftPad = 8.0; diff --git a/source/RouteManager2/RouteInformation.cs b/source/RouteManager2/RouteInformation.cs index bb6dda2c4b..367ad5dabb 100644 --- a/source/RouteManager2/RouteInformation.cs +++ b/source/RouteManager2/RouteInformation.cs @@ -1,5 +1,4 @@ using System.Drawing; -using LibRender2; using OpenBveApi.Textures; namespace RouteManager2 @@ -41,7 +40,7 @@ public class RouteInformation public void LoadInformation() { - lock (BaseRenderer.GdiPlusLock) + lock (Illustrations.Locker) { RouteMap = Illustrations.CreateRouteMap(500, 500, true, out _); RouteMinX = Illustrations.LastRouteMinX; diff --git a/source/RouteManager2/app.config b/source/RouteManager2/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/RouteManager2/app.config +++ b/source/RouteManager2/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/RouteViewer/Audio/Sounds.Update.cs b/source/RouteViewer/Audio/Sounds.Update.cs index 642de11651..4a403706f8 100644 --- a/source/RouteViewer/Audio/Sounds.Update.cs +++ b/source/RouteViewer/Audio/Sounds.Update.cs @@ -24,7 +24,7 @@ protected override void UpdateInverseModel(double timeElapsed) Vector3 listenerPosition = Program.Renderer.Camera.AbsolutePosition; Orientation3 listenerOrientation = new Orientation3(Program.Renderer.Camera.AbsoluteSide, Program.Renderer.Camera.AbsoluteUp, Program.Renderer.Camera.AbsoluteDirection); Vector3 listenerVelocity; - if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior || Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead || Program.Renderer.Camera.CurrentMode == CameraViewMode.Exterior) + if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead | Program.Renderer.Camera.CurrentMode == CameraViewMode.Exterior) { CarBase car = TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]; Vector3 diff = car.FrontAxle.Follower.WorldPosition - car.RearAxle.Follower.WorldPosition; @@ -44,7 +44,7 @@ protected override void UpdateInverseModel(double timeElapsed) AL.Listener(ALListener3f.Position, 0.0f, 0.0f, 0.0f); AL.Listener(ALListener3f.Velocity, (float)listenerVelocity.X, (float)listenerVelocity.Y, (float)listenerVelocity.Z); - float[] Orientation = { (float) listenerOrientation.Z.X, (float) listenerOrientation.Z.Y, (float) listenerOrientation.Z.Z,-(float) listenerOrientation.Y.X, -(float) listenerOrientation.Y.Y, -(float) listenerOrientation.Y.Z }; + float[] Orientation = new[]{(float) listenerOrientation.Z.X, (float) listenerOrientation.Z.Y, (float) listenerOrientation.Z.Z,-(float) listenerOrientation.Y.X, -(float) listenerOrientation.Y.Y, -(float) listenerOrientation.Y.Z}; AL.Listener(ALListenerfv.Orientation, ref Orientation); /* * Set up the atmospheric attributes. @@ -135,7 +135,7 @@ protected override void UpdateInverseModel(double timeElapsed) if (Sources[i].State == SoundSourceState.Playing) { AL.GetSource(Sources[i].OpenAlSourceName, ALGetSourcei.SourceState, out int state); - if (state != (int)ALSourceState.Initial && state != (int)ALSourceState.Playing) + if (state != (int)ALSourceState.Initial & state != (int)ALSourceState.Playing) { /* * The sound is not playing any longer. @@ -174,7 +174,7 @@ protected override void UpdateInverseModel(double timeElapsed) Vector3 positionDifference = position - listenerPosition; double distance = positionDifference.Norm(); double radius = Sources[i].Radius; - if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior || Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead) + if (Program.Renderer.Camera.CurrentMode == CameraViewMode.Interior | Program.Renderer.Camera.CurrentMode == CameraViewMode.InteriorLookAhead) { if (Sources[i].Parent != TrainManager.PlayerTrain.Cars[TrainManager.PlayerTrain.DriverCar]) { diff --git a/source/RouteViewer/FunctionScripts.cs b/source/RouteViewer/FunctionScripts.cs index 02f6b4377b..80b3a67ce2 100644 --- a/source/RouteViewer/FunctionScripts.cs +++ b/source/RouteViewer/FunctionScripts.cs @@ -8,13 +8,12 @@ using TrainManager.Handles; using TrainManager.Car.Systems; using TrainManager.SafetySystems; -using TrainManager.Trains; namespace RouteViewer { internal static class FunctionScripts { // execute function script - internal static void ExecuteFunctionScript(FunctionScript Function, TrainBase Train, int CarIndex, Vector3 Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed, int CurrentState) { + internal static void ExecuteFunctionScript(FunctionScript Function, TrainManager.Train Train, int CarIndex, Vector3 Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed, int CurrentState) { int s = 0, c = 0; for (int i = 0; i < Function.InstructionSet.Length; i++) { switch (Function.InstructionSet[i]) { @@ -264,17 +263,7 @@ internal static void ExecuteFunctionScript(FunctionScript Function, TrainBase Tr Function.Stack[s] = 1; } s++; break; - case Instructions.CameraCar: - if (IsPartOfTrain && Train != null) - { - Function.Stack[s] = Train.CameraCar; - } - else - { - Function.Stack[s] = 0.0; - } - s++; break; - // train + // train case Instructions.PlayerTrain: if (IsPartOfTrain && Train != null) { @@ -712,7 +701,13 @@ internal static void ExecuteFunctionScript(FunctionScript Function, TrainBase Tr } break; case Instructions.PilotLamp: + //Not currently supported in viewers + Function.Stack[s] = 0.0; + s++; break; case Instructions.PassAlarm: + //Not currently supported in viewers + Function.Stack[s] = 0.0; + s++; break; case Instructions.StationAdjustAlarm: //Not currently supported in viewers Function.Stack[s] = 0.0; diff --git a/source/RouteViewer/Game/Menu.cs b/source/RouteViewer/Game/Menu.cs index bc88134b63..9e7a81f922 100644 --- a/source/RouteViewer/Game/Menu.cs +++ b/source/RouteViewer/Game/Menu.cs @@ -2,7 +2,6 @@ using System.ComponentModel; using System.IO; using System.Text; -using System.Windows.Forms; using LibRender2.Menu; using LibRender2.Primitives; using LibRender2.Screens; @@ -453,7 +452,7 @@ private static void routeWorkerThread_doWork(object sender, DoWorkEventArgs e) { // ReSharper disable once RedundantCast object Route = (object)Program.CurrentRoute; // must cast to allow us to use the ref keyword correctly. - string RailwayFolder = Program.FileSystem.GetRailwayFolder(currentFile, Application.StartupPath); + string RailwayFolder = Loading.GetRailwayFolder(currentFile); string ObjectFolder = Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = Path.CombineDirectory(RailwayFolder, "Sound"); if (Program.CurrentHost.Plugins[i].Route.LoadRoute(currentFile, RouteEncoding, null, ObjectFolder, SoundFolder, true, ref Route)) @@ -466,7 +465,7 @@ private static void routeWorkerThread_doWork(object sender, DoWorkEventArgs e) { 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 encountered whilst attempting to parse the routefile " + currentFile; + routeDescriptionBox.Text = "An unknown error was enountered whilst attempting to parse the routefile " + currentFile; RoutefileState = RouteState.Error; } loaded = true; diff --git a/source/RouteViewer/LoadingR.cs b/source/RouteViewer/LoadingR.cs index f79ba18d39..ed29a0d785 100644 --- a/source/RouteViewer/LoadingR.cs +++ b/source/RouteViewer/LoadingR.cs @@ -6,6 +6,7 @@ // ╚═════════════════════════════════════════════════════════════╝ using System; +using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; @@ -49,6 +50,8 @@ internal static bool Cancel private static string CurrentRouteFile; private static Encoding CurrentRouteEncoding; + internal static bool JobAvailable; + // load internal static void Load(string routeFile, Encoding routeEncoding, byte[] textureBytes) { @@ -67,10 +70,85 @@ internal static void Load(string routeFile, Encoding routeEncoding, byte[] textu CurrentRouteFile = routeFile; CurrentRouteEncoding = routeEncoding; // thread - LoadAsynchronously(CurrentRouteFile, CurrentRouteEncoding); + Loading.LoadAsynchronously(CurrentRouteFile, CurrentRouteEncoding); RouteViewer.LoadingScreenLoop(); } - + + /// Gets the absolute Railway folder for a given route file + /// The absolute on-disk path of the railway folder + internal static string GetRailwayFolder(string RouteFile) { + try + { + string Folder = Path.GetDirectoryName(RouteFile); + + while (true) + { + string Subfolder = Path.CombineDirectory(Folder, "Railway"); + if (System.IO.Directory.Exists(Subfolder)) + { + if (System.IO.Directory.EnumerateDirectories(Subfolder).Any() || System.IO.Directory.EnumerateFiles(Subfolder).Any()) + { + //HACK: Ignore completely empty directories + //Doesn't handle wrong directories, or those with stuff missing, TODO..... + return Subfolder; + } + } + + if (Folder == null) continue; + System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder); + if (Info == null) break; + Folder = Info.FullName; + } + } + catch + { + //ignored + } + + //If the Route, Object and Sound folders exist, but are not in a railway folder..... + try + { + string Folder = Path.GetDirectoryName(RouteFile); + if (Folder == null) + { + // Unlikely to work, but attempt to make the best of it + return Application.StartupPath; + } + string candidate = null; + while (true) + { + string RouteFolder = Path.CombineDirectory(Folder, "Route"); + string ObjectFolder = Path.CombineDirectory(Folder, "Object"); + string SoundFolder = Path.CombineDirectory(Folder, "Sound"); + if (System.IO.Directory.Exists(RouteFolder) && System.IO.Directory.Exists(ObjectFolder) && System.IO.Directory.Exists(SoundFolder)) + { + return Folder; + } + + if (System.IO.Directory.Exists(RouteFolder) && System.IO.Directory.Exists(ObjectFolder)) + { + candidate = Folder; + } + + System.IO.DirectoryInfo Info = System.IO.Directory.GetParent(Folder); + if (Info == null) + { + if (candidate != null) + { + return candidate; + } + break; + } + Folder = Info.FullName; + } + } + catch + { + //ignored + } + return Application.StartupPath; + } + // load threaded private static async Task LoadThreaded() { @@ -102,7 +180,7 @@ internal static void LoadAsynchronously(string RouteFile, Encoding RouteEncoding } private static void LoadEverythingThreaded() { - string RailwayFolder = Program.FileSystem.GetRailwayFolder(CurrentRouteFile, Application.StartupPath); + string RailwayFolder = GetRailwayFolder(CurrentRouteFile); string ObjectFolder = Path.CombineDirectory(RailwayFolder, "Object"); string SoundFolder = Path.CombineDirectory(RailwayFolder, "Sound"); Program.Renderer.Camera.CurrentMode = CameraViewMode.Track; diff --git a/source/RouteViewer/NewRendererR.cs b/source/RouteViewer/NewRendererR.cs index 7a54a7115e..85a7c75a18 100644 --- a/source/RouteViewer/NewRendererR.cs +++ b/source/RouteViewer/NewRendererR.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Windows.Forms; using LibRender2; +using LibRender2.Pipeline; +using LibRender2.Passes; using LibRender2.Objects; using LibRender2.Screens; using OpenBveApi; @@ -70,11 +72,19 @@ public override void Initialize() TextureManager.RegisterTexture(Path.CombineFile(Folder, "runsound.png"), out RunSoundTexture); TextureManager.RegisterTexture(Path.CombineFile(Folder, "lighting.png"), out LightingEventTexture); TextureManager.RegisterTexture(Path.CombineFile(Folder, "weather.png"), out WeatherEventTexture); + + // Initialize Pipeline + Pipeline.Clear(); + Pipeline.AddPass(new ShadowPass()); + Pipeline.AddPass(new SkyPass(ctx => Program.CurrentRoute.UpdateBackground(ctx.TimeElapsed, false))); + Pipeline.AddPass(new GeometryPass()); + Pipeline.AddPass(new OverlayPass(ctx => RenderOverlays(ctx.TimeElapsed))); } // render scene internal void RenderScene(double timeElapsed) { + UpdateViewingDistances(Program.CurrentRoute.CurrentBackground.BackgroundImageDistance); lastObjectState = null; ReleaseResources(); // initialize @@ -98,7 +108,7 @@ internal void RenderScene(double timeElapsed) } else { - GL.ClearColor(Interface.CurrentOptions.ClearColor.R * inv255, Interface.CurrentOptions.ClearColor.G * inv255, Interface.CurrentOptions.ClearColor.B * inv255, 1.0f); + GL.ClearColor(0.67f, 0.67f, 0.67f, 1.0f); } GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); @@ -147,129 +157,9 @@ internal void RenderScene(double timeElapsed) Program.CurrentRoute.CurrentFog = Program.CurrentRoute.PreviousFog; } - if (AvailableNewRenderer) - { - PerformCSMShadowPass(); - DefaultShader.Activate(); - BindCSMToDefaultShader(); - } - - - // render background - GL.Disable(EnableCap.DepthTest); - DefaultShader.SetShadowEnabled(false); - Program.CurrentRoute.UpdateBackground(timeElapsed, false); - DefaultShader.SetShadowEnabled(ShadowsEnabled); - - // fog - float aa = Program.CurrentRoute.CurrentFog.Start; - float bb = Program.CurrentRoute.CurrentFog.End; - - if (aa < bb & aa < Program.CurrentRoute.CurrentBackground.BackgroundImageDistance) - { - Fog.Enabled = true; - Fog.Start = aa; - Fog.End = bb; - Fog.Color = Program.CurrentRoute.CurrentFog.Color; - Fog.Density = Program.CurrentRoute.CurrentFog.Density; - Fog.IsLinear = Program.CurrentRoute.CurrentFog.IsLinear; - Fog.Set(); - } - else - { - Fog.Enabled = false; - } - - // world layer - // opaque face - - - if (AvailableNewRenderer) - { - //Setup the shader for rendering the scene - if (OptionLighting) - { - DefaultShader.SetIsLight(true); - DefaultShader.SetLightPosition(TransformedLightPosition); - DefaultShader.SetLightAmbient(Lighting.OptionAmbientColor); - DefaultShader.SetLightDiffuse(Lighting.OptionDiffuseColor); - DefaultShader.SetLightSpecular(Lighting.OptionSpecularColor); - DefaultShader.SetLightModel(Lighting.LightModel); - } - Fog.Set(); - DefaultShader.SetTexture(0); - DefaultShader.SetCurrentProjectionMatrix(CurrentProjectionMatrix); - } - ResetOpenGlState(); - List opaqueFaces, alphaFaces; - lock (VisibleObjects.LockObject) - { - opaqueFaces = VisibleObjects.OpaqueFaces.ToList(); - alphaFaces = VisibleObjects.GetSortedPolygons(); - } - - foreach (FaceState face in opaqueFaces) - { - face.Draw(); - } - - // alpha face - ResetOpenGlState(); - - if (Interface.CurrentOptions.TransparencyMode == TransparencyMode.Performance) - { - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Greater, 0.0f); - GL.DepthMask(false); - - foreach (FaceState face in alphaFaces) - { - face.Draw(); - } - } - else - { - UnsetBlendFunc(); - SetAlphaFunc(AlphaFunction.Equal, 1.0f); - GL.DepthMask(true); - - foreach (FaceState face in alphaFaces) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Normal && face.Object.Prototype.Mesh.Materials[face.Face.Material].GlowAttenuationData == 0) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].Color.A == 255) - { - face.Draw(); - } - } - } - - SetBlendFunc(); - SetAlphaFunc(AlphaFunction.Less, 1.0f); - GL.DepthMask(false); - bool additive = false; - - foreach (FaceState face in alphaFaces) - { - if (face.Object.Prototype.Mesh.Materials[face.Face.Material].BlendMode == MeshMaterialBlendMode.Additive) - { - if (!additive) - { - UnsetAlphaFunc(); - additive = true; - } - } - else - { - if (additive) - { - SetAlphaFunc(); - additive = false; - } - } - face.Draw(); - } - } + // Execute Pipeline + RenderContext context = new RenderContext(this, timeElapsed); + ExecutePipeline(context); if (OptionPaths) { @@ -339,19 +229,10 @@ internal void RenderScene(double timeElapsed) } - // render overlays if (AvailableNewRenderer) { DefaultShader.Deactivate(); } - ResetOpenGlState(); - OptionLighting = false; - Fog.Enabled = false; - UnsetAlphaFunc(); - GL.Disable(EnableCap.DepthTest); - SetBlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); //FIXME: Remove when text switches between two renderer types - RenderOverlays(timeElapsed); - OptionLighting = true; } private double lastTrackPosition; diff --git a/source/RouteViewer/Options.cs b/source/RouteViewer/Options.cs index defcfd3b44..9a38fbd730 100644 --- a/source/RouteViewer/Options.cs +++ b/source/RouteViewer/Options.cs @@ -39,15 +39,15 @@ public override void Save(string fileName) Builder.AppendLine("windowWidth = " + Program.Renderer.Screen.Width.ToString(Culture)); Builder.AppendLine("windowHeight = " + Program.Renderer.Screen.Height.ToString(Culture)); Builder.AppendLine("isUseNewRenderer = " + (IsUseNewRenderer ? "true" : "false")); + Builder.AppendLine("viewingdistance = " + ViewingDistance); + Builder.AppendLine("nearclipbase = " + NearClipBase.ToString(Culture)); + Builder.AppendLine("quadleafsize = " + QuadTreeLeafSize); Builder.AppendLine(); Builder.AppendLine("[quality]"); Builder.AppendLine("interpolation = " + Interpolation); Builder.AppendLine("anisotropicfilteringlevel = " + AnisotropicFilteringLevel.ToString(Culture)); Builder.AppendLine("antialiasinglevel = " + AntiAliasingLevel.ToString(Culture)); Builder.AppendLine("transparencyMode = " + ((int)TransparencyMode).ToString(Culture)); - Builder.AppendLine("viewingdistance = " + ViewingDistance); - Builder.AppendLine("nearclipbase = " + NearClipBase.ToString(Culture)); - Builder.AppendLine("quadleafsize = " + QuadTreeLeafSize); Builder.AppendLine("shadowresolution = " + (int)ShadowResolution); Builder.AppendLine("shadowdrawdistance = " + ShadowDrawDistance); Builder.AppendLine("shadowcascades = " + (int)ShadowCascades); @@ -129,15 +129,6 @@ internal static void LoadOptions() block.TryGetValue(OptionsKey.AnisotropicFilteringLevel, ref Interface.CurrentOptions.AnisotropicFilteringLevel); block.TryGetValue(OptionsKey.AntiAliasingLevel, ref Interface.CurrentOptions.AntiAliasingLevel); block.GetEnumValue(OptionsKey.TransparencyMode, out Interface.CurrentOptions.TransparencyMode); - block.TryGetValue(OptionsKey.ViewingDistance, ref Interface.CurrentOptions.ViewingDistance, NumberRange.Positive); - block.TryGetValue(OptionsKey.QuadLeafSize, ref Interface.CurrentOptions.QuadTreeLeafSize, NumberRange.Positive); - block.TryGetValue(OptionsKey.NearClipBase, ref Interface.CurrentOptions.NearClipBase, NumberRange.Positive); - // ensure viewing distance is greater than the near clipping plane to avoid rendering issues - if (Interface.CurrentOptions.ViewingDistance <= Interface.CurrentOptions.NearClipBase) - - { - Interface.CurrentOptions.ViewingDistance = (int)Math.Ceiling(Interface.CurrentOptions.NearClipBase) + 1; - } block.TryGetEnumValue(OptionsKey.ShadowResolution, ref Interface.CurrentOptions.ShadowResolution); block.TryGetEnumValue(OptionsKey.ShadowDrawDistance, ref Interface.CurrentOptions.ShadowDrawDistance); block.TryGetEnumValue(OptionsKey.ShadowCascades, ref Interface.CurrentOptions.ShadowCascades); diff --git a/source/RouteViewer/ProgramR.cs b/source/RouteViewer/ProgramR.cs index b718bd574f..ea85cf5a9d 100644 --- a/source/RouteViewer/ProgramR.cs +++ b/source/RouteViewer/ProgramR.cs @@ -109,45 +109,44 @@ internal static void Main(string[] args) { for (int i = 0; i < args.Length; i++) { - if (args[i] == null) + if (args[i] != null) { - continue; - } - if (System.IO.File.Exists(args[i])) - { - for (int j = 0; j < CurrentHost.Plugins.Length; j++) + if (System.IO.File.Exists(args[i])) { - if (CurrentHost.Plugins[j].Object != null && CurrentHost.Plugins[j].Object.CanLoadObject(args[i])) + for (int j = 0; j < CurrentHost.Plugins.Length; j++) { - objectsToLoad += args[i] + " "; - continue; - } + if (CurrentHost.Plugins[j].Object != null && CurrentHost.Plugins[j].Object.CanLoadObject(args[i])) + { + objectsToLoad += args[i] + " "; + continue; + } - if (CurrentHost.Plugins[j].Route != null && CurrentHost.Plugins[j].Route.CanLoadRoute(args[i])) - { - if (string.IsNullOrEmpty(CurrentRouteFile)) + if (CurrentHost.Plugins[j].Route != null && CurrentHost.Plugins[j].Route.CanLoadRoute(args[i])) { - CurrentRouteFile = args[i]; - processCommandLineArgs = true; + if (string.IsNullOrEmpty(CurrentRouteFile)) + { + CurrentRouteFile = args[i]; + processCommandLineArgs = true; + } } } } - } - else if (args[i].ToLowerInvariant() == "/enablehacks") - { - //Deliberately undocumented option for debugging use - Interface.CurrentOptions.EnableBveTsHacks = true; - for (int j = 0; j < CurrentHost.Plugins.Length; j++) + else if (args[i].ToLowerInvariant() == "/enablehacks") { - if (CurrentHost.Plugins[j].Object != null) + //Deliberately undocumented option for debugging use + Interface.CurrentOptions.EnableBveTsHacks = true; + for (int j = 0; j < CurrentHost.Plugins.Length; j++) { - CompatabilityHacks enabledHacks = new CompatabilityHacks + if (CurrentHost.Plugins[j].Object != null) { - BveTsHacks = true, - CylinderHack = false, - BlackTransparency = true - }; - CurrentHost.Plugins[j].Object.SetCompatibilityHacks(enabledHacks); + CompatabilityHacks enabledHacks = new CompatabilityHacks + { + BveTsHacks = true, + CylinderHack = false, + BlackTransparency = true + }; + CurrentHost.Plugins[j].Object.SetCompatibilityHacks(enabledHacks); + } } } } diff --git a/source/RouteViewer/System/Gamewindow.cs b/source/RouteViewer/System/Gamewindow.cs index 81473826fe..fe81d4def6 100644 --- a/source/RouteViewer/System/Gamewindow.cs +++ b/source/RouteViewer/System/Gamewindow.cs @@ -197,7 +197,7 @@ public static void LoadingScreenLoop() double time = CPreciseTimer.GetElapsedTime(); double wait = 1000.0 / 60.0 - time * 1000 - 50; if (wait > 0) - Thread.Sleep((int)wait); + Thread.Sleep((int)(wait)); } Program.Renderer.Loading.CompleteLoading(); if (!Loading.Cancel) diff --git a/source/RouteViewer/System/Host.cs b/source/RouteViewer/System/Host.cs index cd0f3a9209..78fdab2ba2 100644 --- a/source/RouteViewer/System/Host.cs +++ b/source/RouteViewer/System/Host.cs @@ -479,7 +479,7 @@ public override bool LoadObject(string path, System.Text.Encoding Encoding, out public override void ExecuteFunctionScript(OpenBveApi.FunctionScripting.FunctionScript functionScript, AbstractTrain train, int CarIndex, Vector3 Position, double TrackPosition, int SectionIndex, bool IsPartOfTrain, double TimeElapsed, int CurrentState) { - FunctionScripts.ExecuteFunctionScript(functionScript, (TrainBase)train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed, CurrentState); + FunctionScripts.ExecuteFunctionScript(functionScript, (TrainManager.Train)train, CarIndex, Position, TrackPosition, SectionIndex, IsPartOfTrain, TimeElapsed, CurrentState); } public override int CreateStaticObject(StaticObject Prototype, Vector3 Position, ObjectCreationParameters Parameters, Transformation WorldTransformation, Transformation LocalTransformation = null) @@ -575,11 +575,11 @@ public override AbstractTrain ClosestTrain(AbstractTrain Train) { for (int i = 0; i < Program.TrainManager.Trains.Count; i++) { - if (Program.TrainManager.Trains[i] != baseTrain && Program.TrainManager.Trains[i].State == TrainState.Available && baseTrain.Cars.Length > 0) + if (Program.TrainManager.Trains[i] != baseTrain & Program.TrainManager.Trains[i].State == TrainState.Available & baseTrain.Cars.Length > 0) { int c = Program.TrainManager.Trains[i].Cars.Length - 1; double z = Program.TrainManager.Trains[i].Cars[c].RearAxle.Follower.TrackPosition - Program.TrainManager.Trains[i].Cars[c].RearAxle.Position - 0.5 * Program.TrainManager.Trains[i].Cars[c].Length; - if (z >= baseTrain.FrontCarTrackPosition && z < bestLocation) + if (z >= baseTrain.FrontCarTrackPosition & z < bestLocation) { bestLocation = z; closestTrain = Program.TrainManager.Trains[i]; diff --git a/source/RouteViewer/TrainManagerR.cs b/source/RouteViewer/TrainManagerR.cs index 2722f1c903..3760e68763 100644 --- a/source/RouteViewer/TrainManagerR.cs +++ b/source/RouteViewer/TrainManagerR.cs @@ -9,7 +9,10 @@ using OpenBveApi; using OpenBveApi.FileSystem; using OpenBveApi.Hosts; +using OpenBveApi.Trains; using TrainManager; +using TrainManager.Handles; +using TrainManager.Trains; namespace RouteViewer { @@ -18,5 +21,22 @@ internal class TrainManager : TrainManagerBase { public TrainManager(HostInterface host, BaseRenderer renderer, BaseOptions options, FileSystem fileSystem) : base(host, renderer, options, fileSystem) { } + + // train + internal class Train : TrainBase { + internal Train() : base(TrainState.Pending, TrainType.LocalPlayerTrain) + { + Handles.Power = new PowerHandle(8, this); + Handles.Brake = new BrakeHandle(8, null, this); + Handles.HoldBrake = new HoldBrakeHandle(this); + } + public override int NumberOfCars => this.Cars.Length; + + public override double FrontCarTrackPosition => Cars[0].FrontAxle.Follower.TrackPosition - Cars[0].FrontAxle.Position + 0.5 * Cars[0].Length; + + public override double RearCarTrackPosition => Cars[Cars.Length - 1].RearAxle.Follower.TrackPosition - Cars[Cars.Length - 1].RearAxle.Position - 0.5 * Cars[Cars.Length - 1].Length; + + public override bool IsPlayerTrain => true; + } } } diff --git a/source/RouteViewer/app.config b/source/RouteViewer/app.config index 8d045bfe8d..9706ed099b 100644 --- a/source/RouteViewer/app.config +++ b/source/RouteViewer/app.config @@ -16,10 +16,6 @@ - - - - diff --git a/source/RouteViewer/formOptions.Designer.cs b/source/RouteViewer/formOptions.Designer.cs index 4292dca849..3315babef9 100644 --- a/source/RouteViewer/formOptions.Designer.cs +++ b/source/RouteViewer/formOptions.Designer.cs @@ -78,8 +78,6 @@ private void InitializeComponent() this.numericUpDownShadowBias = new System.Windows.Forms.NumericUpDown(); this.labelShadowNormalBias = new System.Windows.Forms.Label(); this.numericUpDownShadowNormalBias = new System.Windows.Forms.NumericUpDown(); - this.labelShadowFilterCascades = new System.Windows.Forms.Label(); - this.checkBoxShadowFilterCascades = new System.Windows.Forms.CheckBox(); this.labelNearClip = new System.Windows.Forms.Label(); this.numericUpDownNearClip = new System.Windows.Forms.NumericUpDown(); this.button1 = new System.Windows.Forms.Button(); @@ -490,8 +488,6 @@ private void InitializeComponent() this.tabPageShadows.Controls.Add(this.numericUpDownShadowBias); this.tabPageShadows.Controls.Add(this.labelShadowNormalBias); this.tabPageShadows.Controls.Add(this.numericUpDownShadowNormalBias); - this.tabPageShadows.Controls.Add(this.checkBoxShadowFilterCascades); - this.tabPageShadows.Controls.Add(this.labelShadowFilterCascades); this.tabPageShadows.Location = new System.Drawing.Point(4, 22); this.tabPageShadows.Name = "tabPageShadows"; this.tabPageShadows.Padding = new System.Windows.Forms.Padding(3); @@ -590,7 +586,7 @@ private void InitializeComponent() // this.labelSunDirection.AutoSize = true; this.labelSunDirection.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.labelSunDirection.Location = new System.Drawing.Point(6, 220); + this.labelSunDirection.Location = new System.Drawing.Point(6, 190); this.labelSunDirection.Name = "labelSunDirection"; this.labelSunDirection.Size = new System.Drawing.Size(94, 15); this.labelSunDirection.TabIndex = 36; @@ -599,7 +595,7 @@ private void InitializeComponent() // labelSunAzimuth // this.labelSunAzimuth.AutoSize = true; - this.labelSunAzimuth.Location = new System.Drawing.Point(6, 242); + this.labelSunAzimuth.Location = new System.Drawing.Point(6, 212); this.labelSunAzimuth.Name = "labelSunAzimuth"; this.labelSunAzimuth.Size = new System.Drawing.Size(47, 13); this.labelSunAzimuth.TabIndex = 37; @@ -607,7 +603,7 @@ private void InitializeComponent() // // trackBarSunAzimuth // - this.trackBarSunAzimuth.Location = new System.Drawing.Point(6, 258); + this.trackBarSunAzimuth.Location = new System.Drawing.Point(6, 228); this.trackBarSunAzimuth.Maximum = 180; this.trackBarSunAzimuth.Minimum = -180; this.trackBarSunAzimuth.Name = "trackBarSunAzimuth"; @@ -620,7 +616,7 @@ private void InitializeComponent() // labelSunAzimuthValue // this.labelSunAzimuthValue.AutoSize = true; - this.labelSunAzimuthValue.Location = new System.Drawing.Point(247, 262); + this.labelSunAzimuthValue.Location = new System.Drawing.Point(247, 232); this.labelSunAzimuthValue.Name = "labelSunAzimuthValue"; this.labelSunAzimuthValue.Size = new System.Drawing.Size(29, 13); this.labelSunAzimuthValue.TabIndex = 39; @@ -629,7 +625,7 @@ private void InitializeComponent() // labelSunElevation // this.labelSunElevation.AutoSize = true; - this.labelSunElevation.Location = new System.Drawing.Point(6, 306); + this.labelSunElevation.Location = new System.Drawing.Point(6, 276); this.labelSunElevation.Name = "labelSunElevation"; this.labelSunElevation.Size = new System.Drawing.Size(54, 13); this.labelSunElevation.TabIndex = 40; @@ -637,7 +633,7 @@ private void InitializeComponent() // // trackBarSunElevation // - this.trackBarSunElevation.Location = new System.Drawing.Point(6, 322); + this.trackBarSunElevation.Location = new System.Drawing.Point(6, 292); this.trackBarSunElevation.Maximum = 90; this.trackBarSunElevation.Minimum = -90; this.trackBarSunElevation.Name = "trackBarSunElevation"; @@ -650,7 +646,7 @@ private void InitializeComponent() // labelSunElevationValue // this.labelSunElevationValue.AutoSize = true; - this.labelSunElevationValue.Location = new System.Drawing.Point(247, 326); + this.labelSunElevationValue.Location = new System.Drawing.Point(247, 296); this.labelSunElevationValue.Name = "labelSunElevationValue"; this.labelSunElevationValue.Size = new System.Drawing.Size(23, 13); this.labelSunElevationValue.TabIndex = 42; @@ -720,24 +716,6 @@ private void InitializeComponent() 0, 0}); // - // labelShadowFilterCascades - // - this.labelShadowFilterCascades.AutoSize = true; - this.labelShadowFilterCascades.Location = new System.Drawing.Point(6, 194); - this.labelShadowFilterCascades.Name = "labelShadowFilterCascades"; - this.labelShadowFilterCascades.Size = new System.Drawing.Size(126, 13); - this.labelShadowFilterCascades.TabIndex = 54; - this.labelShadowFilterCascades.Text = "Per-cascade culling:"; - // - // checkBoxShadowFilterCascades - // - this.checkBoxShadowFilterCascades.AutoSize = true; - this.checkBoxShadowFilterCascades.Location = new System.Drawing.Point(160, 194); - this.checkBoxShadowFilterCascades.Name = "checkBoxShadowFilterCascades"; - this.checkBoxShadowFilterCascades.Size = new System.Drawing.Size(15, 14); - this.checkBoxShadowFilterCascades.TabIndex = 55; - this.checkBoxShadowFilterCascades.UseVisualStyleBackColor = true; - // // labelNearClip // this.labelNearClip.AutoSize = true; @@ -850,7 +828,7 @@ private void InitializeComponent() private System.Windows.Forms.NumericUpDown numericUpDownViewingDistance; private System.Windows.Forms.Label labelNearClip; private System.Windows.Forms.NumericUpDown numericUpDownNearClip; - private System.Windows.Forms.Label labelShadowResolution; + private System.Windows.Forms.Label labelShadowResolution; private System.Windows.Forms.ComboBox comboBoxShadowResolution; private System.Windows.Forms.Label labelShadowDistance; private System.Windows.Forms.ComboBox comboBoxShadowDistance; @@ -869,7 +847,5 @@ private void InitializeComponent() private System.Windows.Forms.NumericUpDown numericUpDownShadowBias; private System.Windows.Forms.Label labelShadowNormalBias; private System.Windows.Forms.NumericUpDown numericUpDownShadowNormalBias; - private System.Windows.Forms.Label labelShadowFilterCascades; - private System.Windows.Forms.CheckBox checkBoxShadowFilterCascades; } } diff --git a/source/RouteViewer/formOptions.cs b/source/RouteViewer/formOptions.cs index 84ff0d240d..77a1988231 100644 --- a/source/RouteViewer/formOptions.cs +++ b/source/RouteViewer/formOptions.cs @@ -66,6 +66,10 @@ public FormOptions() if (numericUpDownShadowStrength.Value < 1) numericUpDownShadowStrength.Value = 1; numericUpDownShadowStrength.Refresh(); numericUpDownShadowBias.Value = (decimal)Interface.CurrentOptions.ShadowBias; + if (numericUpDownShadowBias.Value == 0) + { + numericUpDownShadowBias.Value = 0.000050m; + } numericUpDownShadowBias.Refresh(); numericUpDownShadowNormalBias.DecimalPlaces = 2; @@ -88,14 +92,13 @@ public FormOptions() { labelNearClip.Text = Translations.GetInterfaceString(OpenBveApi.Hosts.HostApplication.OpenBve, new[] { "options", "quality_distance_nearclip" }); } - checkBoxShadowFilterCascades.Checked = Interface.CurrentOptions.ShadowFilterCascades; } private void InitializeSunSliders() { - trackBarSunElevation.Value = Math.Max(trackBarSunElevation.Minimum, Math.Min((int)Interface.CurrentOptions.LightElevation, trackBarSunElevation.Maximum)); - trackBarSunAzimuth.Value = Math.Max(trackBarSunAzimuth.Minimum, Math.Min((int)Interface.CurrentOptions.LightAzimuth, trackBarSunAzimuth.Maximum)); - labelSunAzimuthValue.Text = trackBarSunAzimuth.Value + "\u00b0"; + trackBarSunElevation.Value = (int)Interface.CurrentOptions.LightElevation; + trackBarSunAzimuth.Value = (int)Interface.CurrentOptions.LightAzimuth; + labelSunAzimuthValue.Text = trackBarSunAzimuth.Value + "\u00b0"; labelSunElevationValue.Text = trackBarSunElevation.Value + "\u00b0"; } @@ -112,7 +115,6 @@ private void UpdateShadowControlsEnabled() trackBarSunAzimuth.Enabled = enabled; trackBarSunElevation.Enabled = enabled; - checkBoxShadowFilterCascades.Enabled = enabled; } private void comboBoxShadowResolution_SelectedIndexChanged(object sender, EventArgs e) @@ -174,7 +176,6 @@ private void button1_Click(object sender, EventArgs e) double previousShadowStrength = Interface.CurrentOptions.ShadowStrength; double previousShadowBias = Interface.CurrentOptions.ShadowBias; double previousShadowNormalBias = Interface.CurrentOptions.ShadowNormalBias; - bool previousShadowFilterCascades = Interface.CurrentOptions.ShadowFilterCascades; //Interpolation mode InterpolationMode previousInterpolationMode = Interface.CurrentOptions.Interpolation; @@ -292,7 +293,6 @@ private void button1_Click(object sender, EventArgs e) Interface.CurrentOptions.ShadowStrength = (double)numericUpDownShadowStrength.Value / 100.0; Interface.CurrentOptions.ShadowBias = (double)numericUpDownShadowBias.Value; Interface.CurrentOptions.ShadowNormalBias = (double)numericUpDownShadowNormalBias.Value; - Interface.CurrentOptions.ShadowFilterCascades = checkBoxShadowFilterCascades.Checked; @@ -313,7 +313,7 @@ private void button1_Click(object sender, EventArgs e) if (previousInterpolationMode != Interface.CurrentOptions.Interpolation || previousAnisotropicLevel != Interface.CurrentOptions.AnisotropicFilteringLevel || GraphicsModeChanged || Interface.CurrentOptions.ViewingDistance != previousViewingDistance || previousShadowResolution != Interface.CurrentOptions.ShadowResolution || previousShadowDistance != Interface.CurrentOptions.ShadowDrawDistance || previousShadowCascades != Interface.CurrentOptions.ShadowCascades || previousShadowStrength != Interface.CurrentOptions.ShadowStrength || previousShadowBias != Interface.CurrentOptions.ShadowBias || previousShadowNormalBias != Interface.CurrentOptions.ShadowNormalBias || - Interface.CurrentOptions.NearClipBase != previousNearClipBase || previousShadowFilterCascades != Interface.CurrentOptions.ShadowFilterCascades) + Interface.CurrentOptions.NearClipBase != previousNearClipBase) { this.DialogResult = DialogResult.OK; } diff --git a/source/SoundManager/app.config b/source/SoundManager/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/SoundManager/app.config +++ b/source/SoundManager/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file diff --git a/source/TrainEditor/app.config b/source/TrainEditor/app.config index 2c700c093c..0a9966da17 100644 --- a/source/TrainEditor/app.config +++ b/source/TrainEditor/app.config @@ -11,10 +11,6 @@ - - - - diff --git a/source/TrainEditor2/App.config b/source/TrainEditor2/App.config index a7794cc247..92fc90eb4e 100644 --- a/source/TrainEditor2/App.config +++ b/source/TrainEditor2/App.config @@ -27,10 +27,6 @@ - - - - diff --git a/source/TrainManager/Car/CarBase.cs b/source/TrainManager/Car/CarBase.cs index a41e2a2010..38f42d7340 100644 --- a/source/TrainManager/Car/CarBase.cs +++ b/source/TrainManager/Car/CarBase.cs @@ -1143,46 +1143,34 @@ public void UpdateTopplingCantAndSpring(double TimeElapsed) } /// Updates the position of the camera relative to this car - public void UpdateCamera(CarBase previousCar = null, double mu = 1.0) + public void UpdateCamera() { - var a2 = GetAnchor(); - var trackFollower = TrainManagerBase.Renderer.CameraTrackFollower; - if (previousCar != null && mu < 1.0) - { - var a1 = previousCar.GetAnchor(); - trackFollower.WorldPosition = Vector3.CosineInterpolate(a1.worldPosition, a2.worldPosition, mu); - trackFollower.WorldDirection = Vector3.CosineInterpolate(a1.worldDirection, a2.worldDirection, mu); - trackFollower.WorldUp = Vector3.CosineInterpolate(a1.worldUp, a2.worldUp, mu); - trackFollower.WorldSide = Vector3.CosineInterpolate(a1.worldSide, a2.worldSide, mu); - double mu2 = (1.0 - Math.Cos(mu * Math.PI)) / 2.0; - trackFollower.UpdateAbsolute(a1.trackPosition + (a2.trackPosition - a1.trackPosition) * mu2, false, false); - } - else - { - trackFollower.WorldPosition = a2.worldPosition; - trackFollower.WorldDirection = a2.worldDirection; - trackFollower.WorldUp = a2.worldUp; - trackFollower.WorldSide = a2.worldSide; - trackFollower.UpdateAbsolute(a2.trackPosition, false, false); - } - } - - public (Vector3 worldPosition, Vector3 worldDirection, Vector3 worldUp, Vector3 worldSide, double trackPosition) GetAnchor() - { - Vector3 worldDirection = FrontAxle.Follower.WorldPosition - RearAxle.Follower.WorldPosition; - worldDirection.Normalize(); - Vector3 worldSide = Vector3.Cross(Up, worldDirection); - Vector3 worldPosition = 0.5 * (FrontAxle.Follower.WorldPosition + RearAxle.Follower.WorldPosition); + Vector3 direction = new Vector3(FrontAxle.Follower.WorldPosition - RearAxle.Follower.WorldPosition); + direction *= direction.Magnitude(); + double sx = direction.Z * Up.Y - direction.Y * Up.Z; + double sy = direction.X * Up.Z - direction.Z * Up.X; + double sz = direction.Y * Up.X - direction.X * Up.Y; + double rx = 0.5 * (FrontAxle.Follower.WorldPosition.X + RearAxle.Follower.WorldPosition.X); + double ry = 0.5 * (FrontAxle.Follower.WorldPosition.Y + RearAxle.Follower.WorldPosition.Y); + double rz = 0.5 * (FrontAxle.Follower.WorldPosition.Z + RearAxle.Follower.WorldPosition.Z); + Vector3 cameraPosition; Vector3 driverPosition = HasInteriorView ? Driver : baseTrain.Cars[baseTrain.DriverCar].Driver; - worldPosition += worldSide * driverPosition.X + Up * driverPosition.Y + worldDirection * driverPosition.Z; - double interpolationRatio = (Driver.Z - RearAxle.Position) / (FrontAxle.Position - RearAxle.Position); - if (double.IsNaN(interpolationRatio)) + cameraPosition.X = rx + sx * driverPosition.X + Up.X * driverPosition.Y + direction.X * driverPosition.Z; + cameraPosition.Y = ry + sy * driverPosition.X + Up.Y * driverPosition.Y + direction.Y * driverPosition.Z; + cameraPosition.Z = rz + sz * driverPosition.X + Up.Z * driverPosition.Y + direction.Z * driverPosition.Z; + + TrainManagerBase.Renderer.CameraTrackFollower.WorldPosition = cameraPosition; + TrainManagerBase.Renderer.CameraTrackFollower.WorldDirection = direction; + TrainManagerBase.Renderer.CameraTrackFollower.WorldUp = new Vector3(Up); + TrainManagerBase.Renderer.CameraTrackFollower.WorldSide = new Vector3(sx, sy, sz); + double f = (Driver.Z - RearAxle.Position) / (FrontAxle.Position - RearAxle.Position); + if (double.IsNaN(f)) { // car with both axles at zero and a driver position of zero creates NaN (guarded against in original BVE parser) - interpolationRatio = 0; + f = 0; } - double trackPosition = (1.0 - interpolationRatio) * RearAxle.Follower.TrackPosition + interpolationRatio * FrontAxle.Follower.TrackPosition; - return (worldPosition, worldDirection, new Vector3(Up), worldSide, trackPosition); + double tp = (1.0 - f) * RearAxle.Follower.TrackPosition + f * FrontAxle.Follower.TrackPosition; + TrainManagerBase.Renderer.CameraTrackFollower.UpdateAbsolute(tp, false, false); } public void UpdateSpeed(double TimeElapsed, double DecelerationDueToMotor, double DecelerationDueToBrake, out double Speed) diff --git a/source/TrainManager/Car/Systems/ReadhesionDevice.cs b/source/TrainManager/Car/Systems/ReadhesionDevice.cs index 4dabce2ded..538c5cf0e5 100644 --- a/source/TrainManager/Car/Systems/ReadhesionDevice.cs +++ b/source/TrainManager/Car/Systems/ReadhesionDevice.cs @@ -1,4 +1,5 @@ -using TrainManager.Car.Systems; +using System; +using TrainManager.Car.Systems; namespace TrainManager.Car { diff --git a/source/TrainManager/Car/Systems/RunSounds.cs b/source/TrainManager/Car/Systems/RunSounds.cs index 13861b3707..11eea1d21b 100644 --- a/source/TrainManager/Car/Systems/RunSounds.cs +++ b/source/TrainManager/Car/Systems/RunSounds.cs @@ -45,14 +45,14 @@ public void Update(double TimeElapsed) NextReasynchronizationPosition = baseCar.baseTrain.Cars[0].FrontAxle.Follower.TrackPosition; } } - else if (double.IsPositiveInfinity(NextReasynchronizationPosition) & baseCar.FrontAxle.RunIndex >= 0) + else if (NextReasynchronizationPosition == double.MaxValue & baseCar.FrontAxle.RunIndex >= 0) { double distance = Math.Abs(baseCar.FrontAxle.Follower.TrackPosition - TrainManagerBase.Renderer.CameraTrackFollower.TrackPosition); const double minDistance = 150.0; const double maxDistance = 750.0; if (distance > minDistance) { - if (Sounds.TryGetValue(baseCar.FrontAxle.RunIndex, out CarSound runSound) && runSound.Buffer != null) + if (Sounds.TryGetValue(baseCar.FrontAxle.RunIndex, out var runSound) && runSound.Buffer != null) { if (runSound.Buffer.Duration > 0.0) { @@ -65,7 +65,7 @@ public void Update(double TimeElapsed) if (baseCar.FrontAxle.Follower.TrackPosition >= NextReasynchronizationPosition) { - NextReasynchronizationPosition = double.PositiveInfinity; + NextReasynchronizationPosition = double.MaxValue; basegain = 0.0; } else diff --git a/source/TrainManager/Car/Systems/Sanders.cs b/source/TrainManager/Car/Systems/Sanders.cs index 2cc50d5b10..1058c1c3c6 100644 --- a/source/TrainManager/Car/Systems/Sanders.cs +++ b/source/TrainManager/Car/Systems/Sanders.cs @@ -71,7 +71,7 @@ public override void Update(double timeElapsed) return; } - if (State == SandersState.Active) + if (State != SandersState.Inactive) { if ((ActivationSound != null && !ActivationSound.IsPlaying) || ActivationSound == null) { @@ -98,7 +98,7 @@ public override void Update(double timeElapsed) } else { - if (State == SandersState.Active && SandingRate > 0 && SandingRate < double.MaxValue) + if (SandingRate > 0 && SandingRate < double.MaxValue) { SandLevel -= SandingRate * timeElapsed; } @@ -179,7 +179,10 @@ public void SetActive(bool willBeActive) ActivationSound?.Play(Car, false); } - State = willBeActive ? SandersState.Active : SandersState.Inactive; + if (willBeActive) + { + State = SandersState.Active; + } } public void Toggle() diff --git a/source/TrainManager/Car/Windscreen/Windscreen.cs b/source/TrainManager/Car/Windscreen/Windscreen.cs index 4760bc2157..ed9e8e9dd7 100644 --- a/source/TrainManager/Car/Windscreen/Windscreen.cs +++ b/source/TrainManager/Car/Windscreen/Windscreen.cs @@ -79,7 +79,7 @@ public void Update(double TimeElapsed) { int nextDrop = PickDrop(); dropTimer += TimeElapsed * 1000; - int dev = (int)(0.4 * 2000 / Intensity); + var dev = (int)(0.4 * 2000 / Intensity); int dropInterval = 2000 / Intensity + TrainManagerBase.RandomNumberGenerator.Next(dev, dev * 2); if (dropTimer > dropInterval) { diff --git a/source/TrainManager/Handles/Brake/Handles.Brake.cs b/source/TrainManager/Handles/Brake/Handles.Brake.cs index c4d031da58..291ee1f94d 100644 --- a/source/TrainManager/Handles/Brake/Handles.Brake.cs +++ b/source/TrainManager/Handles/Brake/Handles.Brake.cs @@ -187,25 +187,27 @@ public override string GetNotchDescription(out MessageColor color) return Translations.QuickReferences.HandleBrakeNull; } - - if (baseTrain.Handles.EmergencyBrake.Driver) + else { - color = MessageColor.Red; - return NotchDescriptions[0]; - } + if (baseTrain.Handles.EmergencyBrake.Driver) + { + color = MessageColor.Red; + return NotchDescriptions[0]; + } - if (baseTrain.Handles.HasHoldBrake && baseTrain.Handles.HoldBrake.Driver) { - color = MessageColor.Green; - return NotchDescriptions[1]; - } + if (baseTrain.Handles.HasHoldBrake && baseTrain.Handles.HoldBrake.Driver) { + color = MessageColor.Green; + return NotchDescriptions[1]; + } - if (Driver != 0) - { - color = MessageColor.Orange; - return NotchDescriptions[offset + Driver]; - } + if (Driver != 0) + { + color = MessageColor.Orange; + return NotchDescriptions[offset + Driver]; + } - return NotchDescriptions[offset]; + return NotchDescriptions[offset]; + } } } diff --git a/source/TrainManager/Handles/Brake/Handles.EmergencyBrake.cs b/source/TrainManager/Handles/Brake/Handles.EmergencyBrake.cs index de5f22d669..8c4ecdd397 100644 --- a/source/TrainManager/Handles/Brake/Handles.EmergencyBrake.cs +++ b/source/TrainManager/Handles/Brake/Handles.EmergencyBrake.cs @@ -1,4 +1,5 @@ -using OpenBveApi; +using System; +using OpenBveApi; using OpenBveApi.Colors; using OpenBveApi.Interface; using RouteManager2.MessageManager; diff --git a/source/TrainManager/Handles/Brake/Handles.LocoBrake.cs b/source/TrainManager/Handles/Brake/Handles.LocoBrake.cs index cf1326c970..c0ad24e3d7 100644 --- a/source/TrainManager/Handles/Brake/Handles.LocoBrake.cs +++ b/source/TrainManager/Handles/Brake/Handles.LocoBrake.cs @@ -244,21 +244,23 @@ public override string GetNotchDescription(out MessageColor color) return Translations.QuickReferences.HandleBrakeNull; } - - if (baseTrain.Handles.EmergencyBrake.Driver) + else { - color = MessageColor.Red; - return NotchDescriptions[0]; - } + if (baseTrain.Handles.EmergencyBrake.Driver) + { + color = MessageColor.Red; + return NotchDescriptions[0]; + } - if (Driver != 0) - { - color = MessageColor.Orange; - return NotchDescriptions[Driver + 1]; - } + if (Driver != 0) + { + color = MessageColor.Orange; + return NotchDescriptions[Driver + 1]; + } - return NotchDescriptions[1]; + return NotchDescriptions[1]; + } } } diff --git a/source/TrainManager/Handles/Power/PowerHandle.cs b/source/TrainManager/Handles/Power/PowerHandle.cs index 1db8e065e7..32424a7aad 100644 --- a/source/TrainManager/Handles/Power/PowerHandle.cs +++ b/source/TrainManager/Handles/Power/PowerHandle.cs @@ -196,8 +196,10 @@ public override string GetNotchDescription(out MessageColor color) return Translations.QuickReferences.HandlePowerNull; } - - return NotchDescriptions[Driver]; + else + { + return NotchDescriptions[Driver]; + } } } } diff --git a/source/TrainManager/SafetySystems/Plugin/ProxyPlugin.cs b/source/TrainManager/SafetySystems/Plugin/ProxyPlugin.cs index 21685b5f84..7a16886e57 100644 --- a/source/TrainManager/SafetySystems/Plugin/ProxyPlugin.cs +++ b/source/TrainManager/SafetySystems/Plugin/ProxyPlugin.cs @@ -52,10 +52,10 @@ internal ProxyPlugin(string pluginFile, TrainBase train) externalCrashed = false; PluginTitle = System.IO.Path.GetFileName(pluginFile); //Load the plugin via the proxy callback - IntPtr handle = Process.GetCurrentProcess().MainWindowHandle; + var handle = Process.GetCurrentProcess().MainWindowHandle; try { - Process hostProcess = new Process(); + var hostProcess = new Process(); hostProcess.StartInfo.FileName = @"Win32PluginProxy.exe"; hostProcess.Start(); HostInterface.Win32PluginHostReady.WaitOne(); @@ -134,24 +134,12 @@ public override bool Load(VehicleSpecs specs, InitializationModes mode) public override void Unload() { - if (externalCrashed == false) - { - pipeProxy.Unload(); - } + pipeProxy.Unload(); } public override void BeginJump(InitializationModes mode) { - try - { - pipeProxy.BeginJump(mode); - } - catch(Exception ex) - { - lastError = ex.ToString(); - externalCrashed = true; - } - + pipeProxy.BeginJump(mode); if (SupportsAI == AISupport.Program) { AI.BeginJump(mode); @@ -180,16 +168,15 @@ protected override void Elapse(ref ElapseData data) } } Train.UnloadPlugin(); - if (!string.IsNullOrEmpty(lastError)) - { - - TrainManagerBase.currentHost.AddMessage("The train plugin " + PluginTitle + " has been unloaded due to an error. Some train features may no longer work."); - TrainManagerBase.FileSystem.AppendToLogFile(lastError); - lastError = string.Empty; - } return; } - + if (!string.IsNullOrEmpty(lastError)) + { + + //TrainManagercurrentHost.A("ERROR: The proxy plugin " + PluginFile + " generated the following error:"); + //Program.FileSystem.AppendToLogFile(pluginProxy.callback.lastError); + lastError = string.Empty; + } try { diff --git a/source/TrainManager/TrackFollowingObject/AI/TrackFollowingObject.AI.cs b/source/TrainManager/TrackFollowingObject/AI/TrackFollowingObject.AI.cs index 9ec2862935..39fb210f9f 100644 --- a/source/TrainManager/TrackFollowingObject/AI/TrackFollowingObject.AI.cs +++ b/source/TrainManager/TrackFollowingObject/AI/TrackFollowingObject.AI.cs @@ -29,7 +29,6 @@ using OpenBveApi.Motor; using OpenBveApi.Routes; using OpenBveApi.Trains; -using TrainManager.Car; using TrainManager.Handles; using TrainManager.Motor; @@ -146,7 +145,7 @@ public override void Trigger(double TimeElapsed) Train.Specs.CurrentAverageAcceleration = 0.0; } - foreach (CarBase Car in Train.Cars) + foreach (var Car in Train.Cars) { SetRailIndex(NewMileage, NewDirection, Car.FrontAxle.Follower); SetRailIndex(NewMileage, NewDirection, Car.RearAxle.Follower); diff --git a/source/TrainManager/TrackFollowingObject/ScriptedTrain.cs b/source/TrainManager/TrackFollowingObject/ScriptedTrain.cs index 01ed06a8f9..5fffd05c15 100644 --- a/source/TrainManager/TrackFollowingObject/ScriptedTrain.cs +++ b/source/TrainManager/TrackFollowingObject/ScriptedTrain.cs @@ -1,7 +1,6 @@ using System; using LibRender2.Trains; using OpenBveApi.Trains; -using TrainManager.Car; using TrainManager.SafetySystems; namespace TrainManager.Trains @@ -135,7 +134,7 @@ private void UpdatePhysicsAndControls(double TimeElapsed) UpdateDoors(TimeElapsed); // Update Run and Motor sounds - foreach (CarBase Car in Cars) + foreach (var Car in Cars) { Car.Run.Update(TimeElapsed); Car.TractionModel?.Update(TimeElapsed); diff --git a/source/TrainManager/Train/TrainBase.cs b/source/TrainManager/Train/TrainBase.cs index 2a9a237d74..b673c1144f 100644 --- a/source/TrainManager/Train/TrainBase.cs +++ b/source/TrainManager/Train/TrainBase.cs @@ -1,4 +1,3 @@ -using LibRender2.Cameras; using LibRender2.Screens; using LibRender2.Trains; using OpenBveApi; @@ -16,6 +15,7 @@ using SoundManager; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using TrainManager.BrakeSystems; @@ -662,7 +662,7 @@ private void UpdateSpeeds(double timeElapsed) double min = Cars[p].Coupler.MinimumDistanceBetweenCars; double max = Cars[p].Coupler.MaximumDistanceBetweenCars; double d = CenterOfCarPositions[p] - CenterOfCarPositions[s] - 0.5 * (Cars[p].Length + Cars[s].Length); - if (d < min && min < max) + if (d < min) { double t = (min - d) / (Cars[p].CurrentMass + Cars[s].CurrentMass); double tp = t * Cars[s].CurrentMass; @@ -673,22 +673,6 @@ private void UpdateSpeeds(double timeElapsed) CenterOfCarPositions[s] -= ts; CouplerCollision[p] = true; } - else if (d < min) - { - /* - * References: - * https://github.com/leezer3/OpenBVE/issues/1258 - * https://github.com/leezer3/OpenBVE/issues/1298 - * If min == max we don't want to move our cars here - * (as the following code may then move them again in the opposite direction, causing a 'vibrating' effect) - * - * However what the original fix overlooked, is that if we don't set the collision flag, and collision - * is not detected in the following code, when the speed is updated the car carries on 'into' the car in front, - * as the updates speeds loop relies on the CouplerCollisions array - * - */ - CouplerCollision[p] = true; - } else if (d > max & !Cars[p].Derailed & !Cars[s].Derailed) { double t = (d - max) / (Cars[p].CurrentMass + Cars[s].CurrentMass); @@ -1115,31 +1099,25 @@ public void ChangeCameraCar(bool shouldIncrement) // If in the reverse direction, the last car is Car0 and the direction of increase is reversed shouldIncrement = !shouldIncrement; } - - int currentTarget = TrainManagerBase.Renderer.Camera.TargetCameraCar != -1 ? TrainManagerBase.Renderer.Camera.TargetCameraCar : CameraCar; - int nextCar = shouldIncrement ? currentTarget + 1 : currentTarget - 1; - - if (nextCar >= 0 && nextCar < Cars.Length) + + if (shouldIncrement) { - TrainManagerBase.Renderer.Camera.TargetCameraCar = nextCar; - - if (!TrainManagerBase.Renderer.Camera.IsTransitioning) + if (CameraCar < Cars.Length - 1) { - TrainManagerBase.Renderer.Camera.PreviousCameraCar = CameraCar; - CameraCar = nextCar; - TrainManagerBase.Renderer.Camera.TargetCameraCar = -1; - TrainManagerBase.Renderer.Camera.IsTransitioning = true; - TrainManagerBase.Renderer.Camera.CameraCarTransitionTimer = 0.0; + CameraCar++; + TrainManagerBase.currentHost.AddMessage(Translations.GetInterfaceString(HostApplication.OpenBve, new [] {"notification","exterior"}) + " " + (CurrentDirection == TrackDirection.Reverse ? Cars.Length - CameraCar : CameraCar + 1), MessageDependency.CameraView, GameMode.Expert, + MessageColor.White, 2.0, null); } - - TrainManagerBase.currentHost.AddMessage( - Translations.GetInterfaceString(HostApplication.OpenBve, new[] { "notification", "exterior" }) + " " + (CurrentDirection == TrackDirection.Reverse ? Cars.Length - nextCar : nextCar + 1), - MessageDependency.CameraView, - GameMode.Expert, - MessageColor.White, - 2.0, - null); } + else + { + if (CameraCar > 0) + { + CameraCar--; + TrainManagerBase.currentHost.AddMessage(Translations.GetInterfaceString(HostApplication.OpenBve, new [] {"notification","exterior"}) + " " + (CurrentDirection == TrackDirection.Reverse ? Cars.Length - CameraCar : CameraCar + 1), MessageDependency.CameraView, GameMode.Expert, MessageColor.White, 2.0, null); + } + } + } public override void Couple(AbstractTrain train, bool front) diff --git a/source/TrainManager/app.config b/source/TrainManager/app.config index 8a787375cc..a1e2bff25d 100644 --- a/source/TrainManager/app.config +++ b/source/TrainManager/app.config @@ -10,10 +10,6 @@ - - - - \ No newline at end of file