diff --git a/source/LibRender2/BaseRenderer.cs b/source/LibRender2/BaseRenderer.cs
index 1e31ad2d7..68a454d24 100644
--- a/source/LibRender2/BaseRenderer.cs
+++ b/source/LibRender2/BaseRenderer.cs
@@ -560,6 +560,17 @@ public void Reset()
VisibleObjects.Clear();
}
+ /// Resets the renderer state selectively from a given track position.
+ /// The track position from which to clear objects.
+ public void ResetSelective(double startPosition)
+ {
+ currentHost.AnimatedObjectCollectionCache.Clear();
+ // clear out world objects from the change point onwards so we can re-add them
+ StaticObjectStates.RemoveAll(s => s.TrackPosition >= startPosition || (s.TrackPosition == -1.0f && s.StartingDistance >= startPosition));
+ DynamicObjectStates.RemoveAll(s => s.TrackPosition >= startPosition || (s.TrackPosition == -1.0f && s.StartingDistance >= startPosition));
+ VisibleObjects.ClearSelective(startPosition);
+ }
+
public int CreateStaticObject(StaticObject Prototype, Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, ObjectDisposalMode AccurateObjectDisposal, double AccurateObjectDisposalZOffset, double StartingDistance, double EndingDistance, double BlockLength, double TrackPosition)
{
Matrix4D Translate = Matrix4D.CreateTranslation(Position.X, Position.Y, -Position.Z);
@@ -643,6 +654,7 @@ public int CreateStaticObject(Vector3 Position, StaticObject Prototype, Transfor
Rotate = Rotate,
StartingDistance = startingDistance,
EndingDistance = endingDistance,
+ TrackPosition = (float)TrackPosition,
WorldPosition = Position
});
diff --git a/source/LibRender2/Objects/ObjectLibrary.cs b/source/LibRender2/Objects/ObjectLibrary.cs
index a0d5dfc02..88dde206e 100644
--- a/source/LibRender2/Objects/ObjectLibrary.cs
+++ b/source/LibRender2/Objects/ObjectLibrary.cs
@@ -85,6 +85,24 @@ public void Clear()
myOverlayAlphaFaces.Clear();
renderer.StaticObjectStates.Clear();
renderer.DynamicObjectStates.Clear();
+ quadTree.Clear();
+ }
+ }
+
+ /// Selective cleanup of objects and spatial partitioning tree.
+ /// The track position from which to clear.
+ public void ClearSelective(double startPosition)
+ {
+ lock (LockObject)
+ {
+ // remove objects effectively from the modification point onwards
+ List toRemove = Objects.Keys.Where(s => s.StartingDistance >= startPosition).ToList();
+ foreach (var state in toRemove)
+ {
+ RemoveObject(state);
+ }
+ // clear the tree so it gets rebuilt properly with the new world objects
+ quadTree.Clear();
}
}
diff --git a/source/OpenBveApi/Objects/ObjectState.cs b/source/OpenBveApi/Objects/ObjectState.cs
index 37b5cc34f..4d2011176 100644
--- a/source/OpenBveApi/Objects/ObjectState.cs
+++ b/source/OpenBveApi/Objects/ObjectState.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using OpenBveApi.Math;
namespace OpenBveApi.Objects
@@ -79,6 +79,8 @@ public Matrix4D ModelMatrix
public float StartingDistance;
/// The ending track position, for static objects only.
public float EndingDistance;
+ /// The track position marker (used for selective reload cleanup)
+ public float TrackPosition;
/// Creates a new ObjectState
public ObjectState()
@@ -90,6 +92,7 @@ public ObjectState()
TextureTranslation = Matrix4D.Identity;
StartingDistance = 0.0f;
EndingDistance = 0.0f;
+ TrackPosition = -1.0f;
updateModelMatrix = false;
}
diff --git a/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs b/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs
index d367e317a..b0615a899 100644
--- a/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs
+++ b/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObject.cs
@@ -760,6 +760,27 @@ public void Update(AbstractTrain Train, int CarIndex, double TrackPosition, Vect
/// The index of the section if placed using a SigF command
/// The absolute track position
public void CreateObject(Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, int sectionIndex, double TrackPosition)
+ {
+ CreateObject(Position, WorldTransformation, LocalTransformation, sectionIndex, 0.0f, 0.0f, TrackPosition);
+ }
+
+ /// The starting distance
+ /// The ending distance
+ /// The absolute track position
+ public void CreateObject(Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, int sectionIndex, double StartingDistance, double EndingDistance, double TrackPosition)
+ {
+ CreateObject(Position, WorldTransformation, LocalTransformation, sectionIndex, (float)StartingDistance, (float)EndingDistance, TrackPosition);
+ }
+
+ /// Creates the animated object within the worldspace
+ /// The absolute position
+ /// The world transformation to apply (e.g. ground, rail)
+ /// The local transformation to apply in order to rotate the model
+ /// The index of the section if placed using a SigF command
+ /// The starting distance
+ /// The ending distance
+ /// The absolute track position
+ public void CreateObject(Vector3 Position, Transformation WorldTransformation, Transformation LocalTransformation, int sectionIndex, float StartingDistance, float EndingDistance, double TrackPosition)
{
int a = currentHost.AnimatedWorldObjectsUsed;
@@ -770,6 +791,9 @@ public void CreateObject(Vector3 Position, Transformation WorldTransformation, T
{
var o = this.Clone();
currentHost.CreateDynamicObject(ref o.internalObject);
+ o.internalObject.StartingDistance = StartingDistance;
+ o.internalObject.EndingDistance = EndingDistance;
+ o.internalObject.TrackPosition = (float)TrackPosition;
TrackFollowingObject currentObject = new TrackFollowingObject(currentHost)
{
Position = Position,
@@ -814,6 +838,9 @@ public void CreateObject(Vector3 Position, Transformation WorldTransformation, T
{
var o = this.Clone();
currentHost.CreateDynamicObject(ref o.internalObject);
+ o.internalObject.StartingDistance = StartingDistance;
+ o.internalObject.EndingDistance = EndingDistance;
+ o.internalObject.TrackPosition = (float)TrackPosition;
o.SectionIndex = sectionIndex;
AnimatedWorldObject currentObject = new AnimatedWorldObject(currentHost)
{
diff --git a/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObjectCollection.cs b/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObjectCollection.cs
index 599489730..a1e425ed8 100644
--- a/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObjectCollection.cs
+++ b/source/OpenBveApi/Objects/ObjectTypes/AnimatedObject/AnimatedObjectCollection.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using OpenBveApi.Hosts;
using OpenBveApi.Math;
@@ -75,7 +75,7 @@ public override void CreateObject(Vector3 Position, Transformation WorldTransfor
}
else
{
- Objects[i].Clone().CreateObject(Position, WorldTransformation, LocalTransformation, SectionIndex, TrackPosition);
+ Objects[i].Clone().CreateObject(Position, WorldTransformation, LocalTransformation, SectionIndex, StartingDistance, EndingDistance, TrackPosition);
}
}
}
@@ -86,7 +86,7 @@ public override void CreateObject(Vector3 Position, Transformation WorldTransfor
{
if (Objects[i].States.Length != 0)
{
- Objects[i].Clone().CreateObject(Position, WorldTransformation, LocalTransformation, SectionIndex, TrackPosition);
+ Objects[i].Clone().CreateObject(Position, WorldTransformation, LocalTransformation, SectionIndex, StartingDistance, EndingDistance, TrackPosition);
}
}
}
diff --git a/source/OpenBveApi/Objects/ObjectTypes/KeyframeAnimatedObject/KeyframeAnimatedObject.cs b/source/OpenBveApi/Objects/ObjectTypes/KeyframeAnimatedObject/KeyframeAnimatedObject.cs
index 3c9941d39..9e171360f 100644
--- a/source/OpenBveApi/Objects/ObjectTypes/KeyframeAnimatedObject/KeyframeAnimatedObject.cs
+++ b/source/OpenBveApi/Objects/ObjectTypes/KeyframeAnimatedObject/KeyframeAnimatedObject.cs
@@ -99,6 +99,9 @@ public override void CreateObject(Vector3 position, Transformation worldTransfor
Objects[i].WorldPosition = position;
Objects[i].Rotate = Matrix4D.NoTransformation;
Objects[i].Translation = Matrix4D.NoTransformation;
+ Objects[i].StartingDistance = (float)startingDistance;
+ Objects[i].EndingDistance = (float)endingDistance;
+ Objects[i].TrackPosition = (float)trackPosition;
currentHost.ShowObject(Objects[i], ObjectType.Dynamic);
Objects[i].Matricies = matriciesToShader;
}
diff --git a/source/OpenBveApi/Routes/RouteInterface.cs b/source/OpenBveApi/Routes/RouteInterface.cs
index 970942c0d..d0ebfe5ff 100644
--- a/source/OpenBveApi/Routes/RouteInterface.cs
+++ b/source/OpenBveApi/Routes/RouteInterface.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace OpenBveApi.Routes
{
@@ -41,5 +41,8 @@ public virtual void Unload() { }
/// Holds the current loading progress
public double CurrentProgress;
+
+ /// The track position to start parsing from (for selective reloading)
+ public double StartTrackPosition = 0;
}
}
diff --git a/source/Plugins/Route.CsvRw/CsvRwRouteParser.ApplyRouteData.cs b/source/Plugins/Route.CsvRw/CsvRwRouteParser.ApplyRouteData.cs
index f47881be2..6b2451c35 100644
--- a/source/Plugins/Route.CsvRw/CsvRwRouteParser.ApplyRouteData.cs
+++ b/source/Plugins/Route.CsvRw/CsvRwRouteParser.ApplyRouteData.cs
@@ -633,7 +633,11 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
int gi = Data.Blocks[i].Cycle[ci];
if (Data.Structure.Ground.ContainsKey(gi))
{
- Data.Structure.Ground[Data.Blocks[i].Cycle[ci]].CreateObject(Position + new Vector3(0.0, -Data.Blocks[i].Height, 0.0), GroundTransformation, StartingDistance, EndingDistance, StartingDistance);
+ // only re-create objects if they are in the modified section
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Structure.Ground[Data.Blocks[i].Cycle[ci]].CreateObject(Position + new Vector3(0.0, -Data.Blocks[i].Height, 0.0), GroundTransformation, StartingDistance, EndingDistance, StartingDistance);
+ }
}
}
// ground-aligned free objects
@@ -641,13 +645,19 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
{
for (int j = 0; j < Data.Blocks[i].GroundFreeObj.Count; j++)
{
- Data.Blocks[i].GroundFreeObj[j].CreateGroundAligned(Data.Structure.FreeObjects, Position, GroundTransformation, Direction, Data.Blocks[i].Height, StartingDistance, EndingDistance);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Blocks[i].GroundFreeObj[j].CreateGroundAligned(Data.Structure.FreeObjects, Position, GroundTransformation, Direction, Data.Blocks[i].Height, StartingDistance, EndingDistance);
+ }
}
}
if (!PreviewOnly && Data.Structure.WeatherObjects.ContainsKey(Data.Blocks[i].WeatherObject))
{
- UnifiedObject obj = Data.Structure.WeatherObjects[Data.Blocks[i].WeatherObject];
- obj.CreateObject(Position, GroundTransformation, Data.Blocks[i].Height, StartingDistance, EndingDistance);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ UnifiedObject obj = Data.Structure.WeatherObjects[Data.Blocks[i].WeatherObject];
+ obj.CreateObject(Position, GroundTransformation, Data.Blocks[i].Height, StartingDistance, EndingDistance);
+ }
}
// rail-aligned objects
{
@@ -784,7 +794,10 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
if (Data.Structure.RailObjects.ContainsKey(Data.Blocks[i].RailType[railKey]))
{
- Data.Structure.RailObjects[Data.Blocks[i].RailType[railKey]]?.CreateObject(pos, RailTransformation, StartingDistance, EndingDistance, StartingDistance);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Structure.RailObjects[Data.Blocks[i].RailType[railKey]]?.CreateObject(pos, RailTransformation, StartingDistance, EndingDistance, StartingDistance);
+ }
}
// points of interest
@@ -822,19 +835,28 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
// poles
if (Data.Blocks[i].RailPole.Length > railKey)
{
- Data.Blocks[i].RailPole[railKey].Create(Data.Structure.Poles, pos, RailTransformation, Direction, planar, updown, StartingDistance, EndingDistance);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Blocks[i].RailPole[railKey].Create(Data.Structure.Poles, pos, RailTransformation, Direction, planar, updown, StartingDistance, EndingDistance);
+ }
}
// walls
if (Data.Blocks[i].RailWall.ContainsKey(railKey))
{
- Data.Blocks[i].RailWall[railKey].Create(pos, RailTransformation, StartingDistance, EndingDistance);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Blocks[i].RailWall[railKey].Create(pos, RailTransformation, StartingDistance, EndingDistance);
+ }
}
// dikes
if (Data.Blocks[i].RailDike.ContainsKey(railKey))
{
- Data.Blocks[i].RailDike[railKey].Create(pos, RailTransformation, StartingDistance, EndingDistance);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Blocks[i].RailDike[railKey].Create(pos, RailTransformation, StartingDistance, EndingDistance);
+ }
}
// sounds
@@ -873,7 +895,10 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
{
for (int k = 0; k < Data.Blocks[i].RailFreeObj[railKey].Count; k++)
{
- Data.Blocks[i].RailFreeObj[railKey][k].CreateRailAligned(Data.Structure.FreeObjects, new Vector3(pos), RailTransformation, StartingDistance, EndingDistance);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Blocks[i].RailFreeObj[railKey][k].CreateRailAligned(Data.Structure.FreeObjects, new Vector3(pos), RailTransformation, StartingDistance, EndingDistance);
+ }
}
}
@@ -891,9 +916,17 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
// patterns key off rail 0
while (Data.Blocks[i].PatternObjs[key].LastPlacement + Data.Blocks[i].PatternObjs[key].Interval < (i + 1) * Data.BlockInterval)
{
- if (!Data.Blocks[i].PatternObjs[key].CreateRailAligned(Data.Structure.FreeObjects, new Vector3(pos), RailTransformation, StartingDistance, EndingDistance))
+ if (StartingDistance >= StartTrackPosition)
{
- break;
+ if (!Data.Blocks[i].PatternObjs[key].CreateRailAligned(Data.Structure.FreeObjects, new Vector3(pos), RailTransformation, StartingDistance, EndingDistance))
+ {
+ break;
+ }
+ }
+ else
+ {
+ // we still need to "pretend" to place it to keep the state correct
+ Data.Blocks[i].PatternObjs[key].LastPlacement += Data.Blocks[i].PatternObjs[key].Interval;
}
}
@@ -910,18 +943,27 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
{
for (int k = 0; k < Data.Blocks[i].Transponders.Length; k++)
{
- double b = 0.25 + 0.75 * Data.GetBrightness(Data.Blocks[i].Transponders[k].TrackPosition);
- Data.Blocks[i].Transponders[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, b, Data.Structure.Beacon);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ double b = 0.25 + 0.75 * Data.GetBrightness(Data.Blocks[i].Transponders[k].TrackPosition);
+ Data.Blocks[i].Transponders[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, b, Data.Structure.Beacon);
+ }
}
for (int k = 0; k < Data.Blocks[i].DestinationChanges.Length; k++)
{
- Data.Blocks[i].DestinationChanges[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, Data.Structure.Beacon);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Blocks[i].DestinationChanges[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, Data.Structure.Beacon);
+ }
}
for (int k = 0; k < Data.Blocks[i].HornBlows.Length; k++)
{
- Data.Blocks[i].HornBlows[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, Data.Structure.Beacon);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Blocks[i].HornBlows[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, Data.Structure.Beacon);
+ }
}
}
@@ -931,7 +973,10 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
// signals
for (int k = 0; k < Data.Blocks[i].Signals.Length; k++)
{
- Data.Blocks[i].Signals[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, 0.27 + 0.75 * Data.GetBrightness(Data.Blocks[i].Signals[k].TrackPosition));
+ if (StartingDistance >= StartTrackPosition)
+ {
+ Data.Blocks[i].Signals[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, 0.27 + 0.75 * Data.GetBrightness(Data.Blocks[i].Signals[k].TrackPosition));
+ }
}
// sections
@@ -957,8 +1002,11 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
{
if (railKey == Data.Blocks[i].Limits[k].RailIndex)
{
- double b = 0.25 + 0.75 * Data.GetBrightness(Data.Blocks[i].Limits[k].TrackPosition);
- Data.Blocks[i].Limits[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, b, Data.UnitOfSpeed);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ double b = 0.25 + 0.75 * Data.GetBrightness(Data.Blocks[i].Limits[k].TrackPosition);
+ Data.Blocks[i].Limits[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, b, Data.UnitOfSpeed);
+ }
}
}
@@ -967,8 +1015,11 @@ private void ApplyRouteData(string FileName, ref RouteData Data, bool PreviewOnl
{
for (int k = 0; k < Data.Blocks[i].StopPositions.Length; k++)
{
- double b = 0.25 + 0.75 * Data.GetBrightness(Data.Blocks[i].StopPositions[k].TrackPosition);
- Data.Blocks[i].StopPositions[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, b);
+ if (StartingDistance >= StartTrackPosition)
+ {
+ double b = 0.25 + 0.75 * Data.GetBrightness(Data.Blocks[i].StopPositions[k].TrackPosition);
+ Data.Blocks[i].StopPositions[k].Create(new Vector3(pos), RailTransformation, StartingDistance, EndingDistance, b);
+ }
}
}
}
diff --git a/source/Plugins/Route.CsvRw/CsvRwRouteParser.cs b/source/Plugins/Route.CsvRw/CsvRwRouteParser.cs
index ffa4f86a7..7b3e021b9 100644
--- a/source/Plugins/Route.CsvRw/CsvRwRouteParser.cs
+++ b/source/Plugins/Route.CsvRw/CsvRwRouteParser.cs
@@ -24,6 +24,7 @@ internal partial class Parser {
internal readonly bool IsRW;
internal readonly Plugin Plugin;
internal bool IsHmmsim;
+ internal double StartTrackPosition = 0;
internal Parser(Plugin plugin, bool isRW)
{
@@ -486,7 +487,19 @@ private void ParseRouteForData(string FileName, System.Text.Encoding Encoding, E
case "track":
if (Enum.TryParse(Command, true, out TrackCommand parsedCommand))
{
+ // speed up reloads by skipping objects before the edit point (we already have them in the renderer)
+ // however we still need to parse geometry/signaling stuff to keep the state correct
+ if (Data.TrackPosition < StartTrackPosition)
+ {
+ switch (parsedCommand)
+ {
+ case TrackCommand.FreeObj:
+ case TrackCommand.SigF:
+ goto skipCommand;
+ }
+ }
ParseTrackCommand(parsedCommand, Arguments, FileName, UnitOfLength, Expressions[j], ref Data, BlockIndex, PreviewOnly, IsRW);
+ skipCommand:;
}
else
{
diff --git a/source/Plugins/Route.CsvRw/Plugin.cs b/source/Plugins/Route.CsvRw/Plugin.cs
index 9598ad6ab..889780ba0 100644
--- a/source/Plugins/Route.CsvRw/Plugin.cs
+++ b/source/Plugins/Route.CsvRw/Plugin.cs
@@ -125,6 +125,7 @@ public override bool LoadRoute(string path, Encoding textEncoding, string trainP
try
{
Parser parser = new Parser(this, isRw);
+ parser.StartTrackPosition = StartTrackPosition;
parser.ParseRoute(path, textEncoding, trainPath, objectPath, soundPath, PreviewOnly);
IsLoading = false;
return true;
diff --git a/source/RouteViewer/GameR.cs b/source/RouteViewer/GameR.cs
index 0387cfe64..6253a3924 100644
--- a/source/RouteViewer/GameR.cs
+++ b/source/RouteViewer/GameR.cs
@@ -34,7 +34,67 @@ internal static void Reset(bool resetRenderer = true) {
{
Program.Renderer.Reset();
}
+ // object manager
+ Program.Renderer.InitializeVisibility();
+ ObjectManager.AnimatedWorldObjects = new WorldObject[4];
+ ObjectManager.AnimatedWorldObjectsUsed = 0;
+ ResetInternal();
+ }
+
+ /// Selective reset for auto-reloading.
+ /// The track position from which to clear.
+ internal static void ResetSelective(double startPosition)
+ {
+ Program.Renderer.ResetSelective(startPosition);
+ // Selective cleanup of animated world objects:
+ // We need to explicitly hide each removed object from the renderer,
+ // otherwise its mesh faces stay in the draw lists and keep rendering as ghosts
+ int newCount = 0;
+ for (int i = 0; i < ObjectManager.AnimatedWorldObjectsUsed; i++)
+ {
+ if (ObjectManager.AnimatedWorldObjects[i].TrackPosition < startPosition)
+ {
+ // keep this one, it's before the edit point
+ ObjectManager.AnimatedWorldObjects[newCount++] = ObjectManager.AnimatedWorldObjects[i];
+ }
+ else
+ {
+ // this object is at or past the edit point, hide it from the renderer
+ // so its faces get removed from the draw lists
+ var wo = ObjectManager.AnimatedWorldObjects[i];
+
+ // KeyframeWorldObject shadows Object with KeyframeAnimatedObject which has an Objects[] array
+ if (wo is KeyframeWorldObject kwo && kwo.Object?.Objects != null)
+ {
+ for (int j = 0; j < kwo.Object.Objects.Length; j++)
+ {
+ if (kwo.Object.Objects[j] != null)
+ {
+ Program.Renderer.VisibleObjects.HideObject(kwo.Object.Objects[j]);
+ Program.Renderer.DynamicObjectStates.Remove(kwo.Object.Objects[j]);
+ }
+ }
+ }
+ // all other WorldObject subtypes use the base AnimatedObject.internalObject
+ else if (wo.Object?.internalObject != null)
+ {
+ Program.Renderer.VisibleObjects.HideObject(wo.Object.internalObject);
+ Program.Renderer.DynamicObjectStates.Remove(wo.Object.internalObject);
+ }
+ }
+ }
+ ObjectManager.AnimatedWorldObjectsUsed = newCount;
+ for (int i = newCount; i < ObjectManager.AnimatedWorldObjects.Length; i++)
+ {
+ ObjectManager.AnimatedWorldObjects[i] = null;
+ }
+
+ ResetInternal();
+ }
+
+ private static void ResetInternal()
+ {
// track manager
Program.CurrentRoute.Tracks = new Dictionary();
Program.CurrentRoute.Tracks.Add(0, new Track());
@@ -64,11 +124,6 @@ internal static void Reset(bool resetRenderer = true) {
Program.Renderer.InfoTotalQuads = 0;
Program.Renderer.InfoTotalQuadStrip = 0;
Program.Renderer.InfoTotalPolygon = 0;
- Program.Renderer.VisibleObjects.quadTree.Clear();
- // object manager
- Program.Renderer.InitializeVisibility();
- ObjectManager.AnimatedWorldObjects = new WorldObject[4];
- ObjectManager.AnimatedWorldObjectsUsed = 0;
// renderer / sound
Program.Sounds.StopAllSounds();
GC.Collect();
diff --git a/source/RouteViewer/LoadingR.cs b/source/RouteViewer/LoadingR.cs
index ed29a0d78..6d378c4ba 100644
--- a/source/RouteViewer/LoadingR.cs
+++ b/source/RouteViewer/LoadingR.cs
@@ -49,15 +49,23 @@ internal static bool Cancel
internal static bool Complete;
private static string CurrentRouteFile;
private static Encoding CurrentRouteEncoding;
+ private static double currentStartPosition;
internal static bool JobAvailable;
// load
- internal static void Load(string routeFile, Encoding routeEncoding, byte[] textureBytes)
+ internal static void Load(string routeFile, Encoding routeEncoding, byte[] textureBytes, double startPosition = 0)
{
Program.Renderer.GameWindow.TargetRenderFrequency = 0;
// reset
- Game.Reset();
+ if (startPosition > 0)
+ {
+ Game.ResetSelective(startPosition);
+ }
+ else
+ {
+ Game.Reset();
+ }
Program.Renderer.Loading.InitLoading(Program.FileSystem.GetDataFolder("In-game"), typeof(NewRenderer).Assembly.GetName().Version.ToString(), Interface.CurrentOptions.LoadingLogo, Interface.CurrentOptions.LoadingProgressBar);
if (textureBytes != null && textureBytes.Length > 0)
{
@@ -70,7 +78,7 @@ internal static void Load(string routeFile, Encoding routeEncoding, byte[] textu
CurrentRouteFile = routeFile;
CurrentRouteEncoding = routeEncoding;
// thread
- Loading.LoadAsynchronously(CurrentRouteFile, CurrentRouteEncoding);
+ Loading.LoadAsynchronously(CurrentRouteFile, CurrentRouteEncoding, startPosition);
RouteViewer.LoadingScreenLoop();
}
@@ -163,13 +171,14 @@ private static async Task LoadThreaded()
}
}
- internal static void LoadAsynchronously(string RouteFile, Encoding RouteEncoding)
+ internal static void LoadAsynchronously(string RouteFile, Encoding RouteEncoding, double startPosition = 0)
{
// members
Cancel = false;
Complete = false;
CurrentRouteFile = RouteFile;
CurrentRouteEncoding = RouteEncoding;
+ currentStartPosition = startPosition;
//Set the route and train folders in the info class
// ReSharper disable once UnusedVariable
@@ -191,6 +200,7 @@ private static void LoadEverythingThreaded() {
if (Program.CurrentHost.Plugins[i].Route != null && Program.CurrentHost.Plugins[i].Route.CanLoadRoute(CurrentRouteFile))
{
object Route = (object)Program.CurrentRoute; //must cast to allow us to use the ref keyword.
+ Program.CurrentHost.Plugins[i].Route.StartTrackPosition = currentStartPosition;
if (Program.CurrentHost.Plugins[i].Route.LoadRoute(CurrentRouteFile, CurrentRouteEncoding, null, ObjectFolder, SoundFolder, false, ref Route))
{
Program.CurrentRoute = (CurrentRoute) Route;
diff --git a/source/RouteViewer/Options.cs b/source/RouteViewer/Options.cs
index 68bd87573..7a5f97707 100644
--- a/source/RouteViewer/Options.cs
+++ b/source/RouteViewer/Options.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Globalization;
using System.IO;
using System.Reflection;
@@ -41,6 +41,7 @@ public override void Save(string fileName)
Builder.AppendLine("isUseNewRenderer = " + (IsUseNewRenderer ? "true" : "false"));
Builder.AppendLine("viewingdistance = " + ViewingDistance);
Builder.AppendLine("quadleafsize = " + QuadTreeLeafSize);
+ Builder.AppendLine("autoReloadObjects = " + (AutoReloadObjects ? "true" : "false"));
Builder.AppendLine();
Builder.AppendLine("[quality]");
Builder.AppendLine("interpolation = " + Interpolation);
@@ -106,6 +107,7 @@ internal static void LoadOptions()
block.GetValue(OptionsKey.IsUseNewRenderer, out Interface.CurrentOptions.IsUseNewRenderer);
block.TryGetValue(OptionsKey.ViewingDistance, ref Interface.CurrentOptions.ViewingDistance, NumberRange.Positive);
block.TryGetValue(OptionsKey.QuadLeafSize, ref Interface.CurrentOptions.QuadTreeLeafSize, NumberRange.Positive);
+ block.GetValue(OptionsKey.AutoReloadObjects, out Interface.CurrentOptions.AutoReloadObjects);
break;
case OptionsSection.Quality:
block.GetEnumValue(OptionsKey.Interpolation, out Interface.CurrentOptions.Interpolation);
diff --git a/source/RouteViewer/ProgramR.cs b/source/RouteViewer/ProgramR.cs
index c5b3bc15c..2b73274ce 100644
--- a/source/RouteViewer/ProgramR.cs
+++ b/source/RouteViewer/ProgramR.cs
@@ -43,6 +43,10 @@ internal static class Program {
internal static string JumpToPositionValue = "";
internal static double MinimumJumpToPositionValue = 0;
internal static bool processCommandLineArgs;
+ /// Last time the route was reloaded (UTC).
+ internal static DateTime LastReloadTime = DateTime.UtcNow;
+ private static double reloadCheckTimer;
+ private static string[] lastRouteFileLines;
// keys
private static bool ShiftPressed = false;
@@ -191,13 +195,14 @@ internal static void Main(string[] args)
Renderer.GameWindow.TargetRenderFrequency = 0;
Renderer.GameWindow.Title = "Route Viewer";
processCommandLineArgs = true;
+ LastReloadTime = DateTime.UtcNow;
Renderer.GameWindow.Run();
//Unload
Sounds.DeInitialize();
}
// load route
- internal static bool LoadRoute(byte[] textureBytes = null) {
+ internal static bool LoadRoute(byte[] textureBytes = null, bool autoReload = false, double startPosition = 0) {
if (string.IsNullOrEmpty(CurrentRouteFile))
{
return false;
@@ -207,13 +212,20 @@ internal static bool LoadRoute(byte[] textureBytes = null) {
try
{
Encoding encoding = TextEncoding.GetSystemEncodingFromFile(CurrentRouteFile);
- Loading.Load(CurrentRouteFile, encoding, textureBytes);
+ if (lastRouteFileLines == null)
+ {
+ lastRouteFileLines = System.IO.File.ReadAllLines(CurrentRouteFile, encoding);
+ }
+ Loading.Load(CurrentRouteFile, encoding, textureBytes, startPosition);
result = true;
} catch (Exception ex) {
- MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ if (!autoReload)
+ {
+ MessageBox.Show(ex.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Hand);
+ CurrentRouteFile = null;
+ }
Game.Reset();
result = false;
- CurrentRouteFile = null;
}
if (Loading.Cancel)
@@ -255,6 +267,163 @@ internal static bool LoadRoute(byte[] textureBytes = null) {
return result;
}
+ /// Called to reload the current route file.
+ /// Whether this is an automatic reload (triggered by a file change).
+ /// The starting track position for a selective reload.
+ internal static void ReloadRoute(bool autoReload = false, double startPosition = 0)
+ {
+ if (CurrentRouteFile != null && CurrentlyLoading == false)
+ {
+ CurrentlyLoading = true;
+ lock (Renderer.VisibilityUpdateLock)
+ {
+ Renderer.OptionInterface = false;
+ UpdateCaption();
+ byte[] textureBytes = { };
+ if (!autoReload && !Interface.CurrentOptions.LoadingBackground)
+ {
+ Renderer.RenderScene(0.0);
+ Renderer.GameWindow.SwapBuffers();
+ textureBytes = new byte[Renderer.Screen.Width * Renderer.Screen.Height * 4];
+ GL.ReadPixels(0, 0, Renderer.Screen.Width, Renderer.Screen.Height, PixelFormat.Rgba, PixelType.UnsignedByte, textureBytes);
+ // GL.ReadPixels is reversed for what it wants as a texture, so we've got to flip it
+ byte[] tmp = new byte[Renderer.Screen.Width * 4]; // temp row
+ int currentLine = 0;
+ while (currentLine < Renderer.Screen.Height / 2)
+ {
+ int start = currentLine * Renderer.Screen.Width * 4;
+ int flipStart = (Renderer.Screen.Height - currentLine - 1) * Renderer.Screen.Width * 4;
+ Buffer.BlockCopy(textureBytes, start, tmp, 0, Renderer.Screen.Width * 4);
+ Buffer.BlockCopy(textureBytes, flipStart, textureBytes, start, Renderer.Screen.Width * 4);
+ Buffer.BlockCopy(tmp, 0, textureBytes, flipStart, Renderer.Screen.Width * 4);
+ currentLine++;
+ }
+ }
+
+ // Note: Game.ResetSelective / Game.Reset is called inside Loading.Load,
+ // so we don't do it here. Doing it here would bypass the animated object cleanup.
+ CameraAlignment a = Renderer.Camera.Alignment;
+
+ if (LoadRoute(textureBytes, autoReload, startPosition))
+ {
+ Renderer.Camera.Alignment = a;
+ Renderer.CameraTrackFollower.UpdateAbsolute(-1.0, true, false);
+ Renderer.CameraTrackFollower.UpdateAbsolute(a.TrackPosition, true, false);
+ ObjectManager.UpdateAnimatedWorldObjects(0.0, true);
+ Renderer.InitializeVisibility();
+ }
+ else
+ {
+ Renderer.Camera.Reset(CurrentRoute.Tracks[0].Direction == TrackDirection.Reverse);
+ Renderer.UpdateViewport(ViewportChangeMode.NoChange);
+ World.UpdateAbsoluteCamera(0.0);
+ Renderer.UpdateViewingDistances(CurrentRoute.CurrentBackground.BackgroundImageDistance);
+ }
+ }
+
+ CurrentlyLoading = false;
+ Renderer.OptionInterface = true;
+ System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
+ GC.Collect();
+ UpdateCaption();
+ LastReloadTime = DateTime.UtcNow;
+ }
+ }
+
+ /// Checks if the current route file has been updated externally.
+ internal static void CheckFileChanges(double timeElapsed)
+ {
+ if (Interface.CurrentOptions.AutoReloadObjects == false || string.IsNullOrEmpty(CurrentRouteFile) || (reloadCheckTimer += timeElapsed) < 0.1)
+ {
+ return;
+ }
+ reloadCheckTimer = 0.0;
+
+ try
+ {
+ DateTime time = System.IO.File.GetLastWriteTimeUtc(CurrentRouteFile);
+ if ((DateTime.UtcNow - time).TotalSeconds < 0.05 || time.Year == 1601)
+ {
+ return;
+ }
+ if (time > LastReloadTime)
+ {
+ if (CurrentRouteFile.EndsWith(".csv", StringComparison.InvariantCultureIgnoreCase) || CurrentRouteFile.EndsWith(".rw", StringComparison.InvariantCultureIgnoreCase))
+ {
+ try
+ {
+ string[] currentLines = System.IO.File.ReadAllLines(CurrentRouteFile, TextEncoding.GetSystemEncodingFromFile(CurrentRouteFile));
+ if (lastRouteFileLines != null && currentLines.Length > 0)
+ {
+ int firstChangedLine = -1;
+ for (int i = 0; i < Math.Min(currentLines.Length, lastRouteFileLines.Length); i++)
+ {
+ if (currentLines[i] != lastRouteFileLines[i])
+ {
+ firstChangedLine = i;
+ break;
+ }
+ }
+
+ // find the first line that actually changed
+ if (firstChangedLine == -1 && currentLines.Length != lastRouteFileLines.Length)
+ {
+ firstChangedLine = Math.Min(currentLines.Length, lastRouteFileLines.Length);
+ }
+
+ if (firstChangedLine != -1)
+ {
+ // find the track position at or before the change in both versions.
+ // we take the minimum of both to avoid "ghost" objects if a track marker was moved forward.
+ double startPosNew = 0;
+ for (int i = firstChangedLine; i >= 0; i--)
+ {
+ string line = currentLines[i];
+ int commentIdx = line.IndexOf(';');
+ if (commentIdx != -1) line = line.Substring(0, commentIdx);
+ string[] parts = line.Split(',');
+ if (parts.Length > 0 && double.TryParse(parts[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double p))
+ {
+ startPosNew = p;
+ break;
+ }
+ }
+ double startPosOld = 0;
+ for (int i = Math.Min(firstChangedLine, lastRouteFileLines.Length - 1); i >= 0; i--)
+ {
+ string line = lastRouteFileLines[i];
+ int commentIdx = line.IndexOf(';');
+ if (commentIdx != -1) line = line.Substring(0, commentIdx);
+ string[] parts = line.Split(',');
+ if (parts.Length > 0 && double.TryParse(parts[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out double p))
+ {
+ startPosOld = p;
+ break;
+ }
+ }
+ double startPos = Math.Min(startPosNew, startPosOld);
+ lastRouteFileLines = currentLines;
+ ReloadRoute(true, startPos);
+ return;
+ }
+ }
+ lastRouteFileLines = currentLines;
+ }
+ catch
+ {
+ // fallback
+ }
+ }
+ ReloadRoute(true);
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+
// jump to station
private static void JumpToStation(int Direction) {
if (CurrentRoute.Tracks[0].Direction == TrackDirection.Reverse)
@@ -298,7 +467,7 @@ internal static void UpdateGraphicsSettings()
Renderer.RenderScene(0.0);
Renderer.GameWindow.SwapBuffers();
CameraAlignment a = Renderer.Camera.Alignment;
- if (LoadRoute())
+ if (LoadRoute(null, false))
{
Renderer.Camera.Alignment = a;
Renderer.CameraTrackFollower.UpdateAbsolute(-1.0, true, false);
@@ -447,60 +616,7 @@ internal static void KeyDownEvent(object sender, KeyboardKeyEventArgs e)
JumpToPositionEnabled = false;
JumpToPositionValue = string.Empty;
}
- byte[] textureBytes = {};
- if (CurrentRouteFile != null && CurrentlyLoading == false)
- {
- CurrentlyLoading = true;
- lock (Renderer.VisibilityUpdateLock)
- {
- Renderer.OptionInterface = false;
- UpdateCaption();
- if (!Interface.CurrentOptions.LoadingBackground)
- {
- Renderer.RenderScene(0.0);
- Renderer.GameWindow.SwapBuffers();
- textureBytes = new byte[Renderer.Screen.Width * Renderer.Screen.Height * 4];
- GL.ReadPixels(0, 0, Renderer.Screen.Width, Renderer.Screen.Height, PixelFormat.Rgba, PixelType.UnsignedByte, textureBytes);
- // GL.ReadPixels is reversed for what it wants as a texture, so we've got to flip it
- byte[] tmp = new byte[Renderer.Screen.Width * 4]; // temp row
- int currentLine = 0;
- while (currentLine < Renderer.Screen.Height / 2)
- {
- int start = currentLine * Renderer.Screen.Width * 4;
- int flipStart = (Renderer.Screen.Height - currentLine - 1) * Renderer.Screen.Width * 4;
- Buffer.BlockCopy(textureBytes, start, tmp, 0, Renderer.Screen.Width * 4);
- Buffer.BlockCopy(textureBytes, flipStart, textureBytes, start, Renderer.Screen.Width * 4);
- Buffer.BlockCopy(tmp, 0, textureBytes, flipStart, Renderer.Screen.Width * 4);
- currentLine++;
- }
- }
-
- Renderer.Reset();
- CameraAlignment a = Renderer.Camera.Alignment;
-
- if (LoadRoute(textureBytes))
- {
- Renderer.Camera.Alignment = a;
- Renderer.CameraTrackFollower.UpdateAbsolute(-1.0, true, false);
- Renderer.CameraTrackFollower.UpdateAbsolute(a.TrackPosition, true, false);
- ObjectManager.UpdateAnimatedWorldObjects(0.0, true);
- }
- else
- {
- Renderer.Camera.Reset(CurrentRoute.Tracks[0].Direction == TrackDirection.Reverse);
- Renderer.UpdateViewport(ViewportChangeMode.NoChange);
- World.UpdateAbsoluteCamera(0.0);
- Renderer.UpdateViewingDistances(CurrentRoute.CurrentBackground.BackgroundImageDistance);
- }
- }
-
- CurrentlyLoading = false;
- Renderer.OptionInterface = true;
- GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
- GC.Collect();
- UpdateCaption();
-
- }
+ ReloadRoute();
break;
case Key.F7:
if (CurrentHost.Platform == HostPlatform.AppleOSX && IntPtr.Size != 4)
@@ -541,7 +657,7 @@ internal static void KeyDownEvent(object sender, KeyboardKeyEventArgs e)
lock (Renderer.VisibilityUpdateLock)
{
- if (canLoad && LoadRoute())
+ if (canLoad && LoadRoute(null, false))
{
ObjectManager.UpdateAnimatedWorldObjects(0.0, true);
Renderer.Camera.Reset(CurrentRoute.Tracks[0].Direction == TrackDirection.Reverse);
diff --git a/source/RouteViewer/System/Gamewindow.cs b/source/RouteViewer/System/Gamewindow.cs
index 98cf96270..be0d67d6e 100644
--- a/source/RouteViewer/System/Gamewindow.cs
+++ b/source/RouteViewer/System/Gamewindow.cs
@@ -1,4 +1,4 @@
-using LibRender2.Viewports;
+using LibRender2.Viewports;
using OpenBveApi;
using OpenBveApi.Hosts;
using OpenBveApi.Math;
@@ -79,6 +79,7 @@ protected override void OnRenderFrame(FrameEventArgs e)
ObjectManager.UpdateAnimatedWorldObjects(TimeElapsed, false);
World.UpdateAbsoluteCamera(TimeElapsed);
Program.Sounds.Update(TimeElapsed, SoundModels.Linear);
+ Program.CheckFileChanges(TimeElapsed);
}
Program.Renderer.Lighting.UpdateLighting(Program.CurrentRoute.SecondsSinceMidnight, Program.CurrentRoute.LightDefinitions);
Program.Renderer.RenderScene(TimeElapsed);
diff --git a/source/RouteViewer/formOptions.Designer.cs b/source/RouteViewer/formOptions.Designer.cs
index 3c5b080dc..456bfbd78 100644
--- a/source/RouteViewer/formOptions.Designer.cs
+++ b/source/RouteViewer/formOptions.Designer.cs
@@ -57,6 +57,8 @@ private void InitializeComponent()
this.comboBoxNewXParser = new System.Windows.Forms.ComboBox();
this.label15 = new System.Windows.Forms.Label();
this.numericUpDownViewingDistance = new System.Windows.Forms.NumericUpDown();
+ this.labelAutoReload = new System.Windows.Forms.Label();
+ this.checkBoxAutoReload = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.width)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.height)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.AnsiotropicLevel)).BeginInit();
@@ -139,7 +141,7 @@ private void InitializeComponent()
//
// button1
//
- this.button1.Location = new System.Drawing.Point(211, 447);
+ this.button1.Location = new System.Drawing.Point(211, 470);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 9;
@@ -307,11 +309,29 @@ private void InitializeComponent()
this.checkBoxProgressBar.TabIndex = 23;
this.checkBoxProgressBar.UseVisualStyleBackColor = true;
//
+ // labelAutoReload
+ //
+ this.labelAutoReload.AutoSize = true;
+ this.labelAutoReload.Location = new System.Drawing.Point(15, 326);
+ this.labelAutoReload.Name = "labelAutoReload";
+ this.labelAutoReload.Size = new System.Drawing.Size(100, 13);
+ this.labelAutoReload.TabIndex = 44;
+ this.labelAutoReload.Text = "Auto Reload Route:";
+ //
+ // checkBoxAutoReload
+ //
+ this.checkBoxAutoReload.AutoSize = true;
+ this.checkBoxAutoReload.Location = new System.Drawing.Point(248, 326);
+ this.checkBoxAutoReload.Name = "checkBoxAutoReload";
+ this.checkBoxAutoReload.Size = new System.Drawing.Size(15, 14);
+ this.checkBoxAutoReload.TabIndex = 45;
+ this.checkBoxAutoReload.UseVisualStyleBackColor = true;
+ //
// label13
//
this.label13.AutoSize = true;
this.label13.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.label13.Location = new System.Drawing.Point(99, 333);
+ this.label13.Location = new System.Drawing.Point(99, 356);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(42, 15);
this.label13.TabIndex = 26;
@@ -320,7 +340,7 @@ private void InitializeComponent()
// label14
//
this.label14.AutoSize = true;
- this.label14.Location = new System.Drawing.Point(16, 377);
+ this.label14.Location = new System.Drawing.Point(16, 400);
this.label14.Name = "label14";
this.label14.Size = new System.Drawing.Size(106, 13);
this.label14.TabIndex = 28;
@@ -332,7 +352,7 @@ private void InitializeComponent()
this.comboBoxNewObjParser.Items.AddRange(new object[] {
"OriginalObjParser",
"AssimpObjParser"});
- this.comboBoxNewObjParser.Location = new System.Drawing.Point(166, 377);
+ this.comboBoxNewObjParser.Location = new System.Drawing.Point(166, 400);
this.comboBoxNewObjParser.Name = "comboBoxNewObjParser";
this.comboBoxNewObjParser.Size = new System.Drawing.Size(121, 21);
this.comboBoxNewObjParser.TabIndex = 27;
@@ -340,7 +360,7 @@ private void InitializeComponent()
// label12
//
this.label12.AutoSize = true;
- this.label12.Location = new System.Drawing.Point(16, 348);
+ this.label12.Location = new System.Drawing.Point(16, 371);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(97, 13);
this.label12.TabIndex = 32;
@@ -353,7 +373,7 @@ private void InitializeComponent()
"OriginalXParser",
"NewXParser",
"AssimpXParser"});
- this.comboBoxNewXParser.Location = new System.Drawing.Point(166, 348);
+ this.comboBoxNewXParser.Location = new System.Drawing.Point(166, 371);
this.comboBoxNewXParser.Name = "comboBoxNewXParser";
this.comboBoxNewXParser.Size = new System.Drawing.Size(121, 21);
this.comboBoxNewXParser.TabIndex = 31;
@@ -361,7 +381,7 @@ private void InitializeComponent()
// label15
//
this.label15.AutoSize = true;
- this.label15.Location = new System.Drawing.Point(12, 412);
+ this.label15.Location = new System.Drawing.Point(12, 435);
this.label15.Name = "label15";
this.label15.Size = new System.Drawing.Size(109, 13);
this.label15.TabIndex = 34;
@@ -369,7 +389,7 @@ private void InitializeComponent()
//
// numericUpDownViewingDistance
//
- this.numericUpDownViewingDistance.Location = new System.Drawing.Point(166, 410);
+ this.numericUpDownViewingDistance.Location = new System.Drawing.Point(166, 433);
this.numericUpDownViewingDistance.Maximum = new decimal(new int[] {
4096,
0,
@@ -393,7 +413,9 @@ private void InitializeComponent()
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(311, 482);
+ this.ClientSize = new System.Drawing.Size(311, 505);
+ this.Controls.Add(this.labelAutoReload);
+ this.Controls.Add(this.checkBoxAutoReload);
this.Controls.Add(this.label15);
this.Controls.Add(this.numericUpDownViewingDistance);
this.Controls.Add(this.label12);
@@ -469,5 +491,7 @@ private void InitializeComponent()
private System.Windows.Forms.ComboBox comboBoxNewXParser;
private System.Windows.Forms.Label label15;
private System.Windows.Forms.NumericUpDown numericUpDownViewingDistance;
+ private System.Windows.Forms.Label labelAutoReload;
+ private System.Windows.Forms.CheckBox checkBoxAutoReload;
}
}
\ No newline at end of file
diff --git a/source/RouteViewer/formOptions.cs b/source/RouteViewer/formOptions.cs
index f275c31cc..090f554ca 100644
--- a/source/RouteViewer/formOptions.cs
+++ b/source/RouteViewer/formOptions.cs
@@ -26,6 +26,7 @@ public FormOptions()
comboBoxNewXParser.SelectedIndex = (int) Interface.CurrentOptions.CurrentXParser;
comboBoxNewObjParser.SelectedIndex = (int) Interface.CurrentOptions.CurrentObjParser;
numericUpDownViewingDistance.Value = Math.Min(Interface.CurrentOptions.ViewingDistance, numericUpDownViewingDistance.Minimum);
+ checkBoxAutoReload.Checked = Interface.CurrentOptions.AutoReloadObjects;
}
internal static DialogResult ShowOptions()
@@ -126,6 +127,7 @@ private void button1_Click(object sender, EventArgs e)
}
}
Interface.CurrentOptions.ViewingDistance = (int)numericUpDownViewingDistance.Value;
+ Interface.CurrentOptions.AutoReloadObjects = checkBoxAutoReload.Checked;
Interface.CurrentOptions.QuadTreeLeafSize = Math.Max(50, (int)Math.Ceiling(Interface.CurrentOptions.ViewingDistance / 10.0d) * 10); // quad tree size set to 10% of viewing distance to the nearest 10
Interface.CurrentOptions.Save(Path.CombineFile(Program.FileSystem.SettingsFolder, "1.5.0/options_rv.cfg"));
for (int i = 0; i < Program.CurrentHost.Plugins.Length; i++)