<commit_before>#region LGPL License
/*
Axiom Game Engine Library
Copyright (C) 2003  Axiom Project Team

The overall design, and a majority of the core engine and rendering code 
contained within this library is a derivative of the open source Object Oriented 
Graphics Engine OGRE, which can be found at http://ogre.sourceforge.net.  
Many thanks to the OGRE team for maintaining such a high quality project.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#endregion

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Axiom.Collections;
using Axiom.Controllers;
using Axiom.Fonts;
using Axiom.Overlays;
using Axiom.Input;
using Axiom.ParticleSystems;
using Axiom.Utility;
using Axiom.Graphics;

namespace Axiom.Core {
	/// <summary>
	///		A delegate for defining frame events.
	/// </summary>
	public delegate void FrameEvent(object source, FrameEventArgs e);

	/// <summary>
	///		Used to supply info to the FrameStarted and FrameEnded events.
	/// </summary>
	public class FrameEventArgs : System.EventArgs {
		/// <summary>
		///    Time elapsed (in milliseconds) since the last frame event.
		/// </summary>
		public float TimeSinceLastEvent;

		/// <summary>
		///    Time elapsed (in milliseconds) since the last frame.
		/// </summary>
		public float TimeSinceLastFrame;

		/// <summary>
		///    Event handlers should set this to true if they wish to stop the render loop and begin shutdown.
		/// </summary>
		public bool RequestShutdown;
	}

	public enum FrameEventType {
		Start,
		End
	}

	/// <summary>
	///		The Engine class is the main container of all the subsystems.  This includes the RenderSystem, various ResourceManagers, etc.
	/// </summary>
	public class Root : IDisposable {
		#region Singleton implementation
		
		private Root() {}
		private static readonly Root instance = new Root();

		public static Root Instance {
			get {
				return instance;
			}
		}

		#endregion

		private SceneManager sceneManager;
		private SceneManagerList sceneManagerList;
		private RenderSystemCollection renderSystemList;
		private RenderSystem activeRenderSystem;
		private Log engineLog;
		private ITimer timer;

		// Framerate Related State
		private long lastStartTime, lastEndTime;
		private long lastCalculationTime;							// The Last Time We Calculated Framerate
		private long frameCount;									// Frames Drawn Counter For FPS Calculations
		private float currentFPS;									// Current FPS
		private float highestFPS;									// Highest FPS
		private float lowestFPS = 9999;								// Lowest FPS
		private float averageFPS;

		/// <summary>
		///		Global frame count since startup.
		/// </summary>
		private ulong currentFrameCount;

		/// <summary>
		///    Has the first render window been created yet?
		/// </summary>
		private bool firstTime = true;

		/// <summary>
		/// Has the Setup() method been called yet?
		/// </summary>
		private bool alreadySetup = false;
		/// <summary>
		///		True if a request has been made to shutdown the rendering engine.
		/// </summary>
		private bool queuedEnd;

		#region Events
		
		/// <summary>
		/// Fired as a frame is about to be rendered.
		/// </summary>
		public event FrameEvent FrameStarted;

		/// <summary>
		/// Fired after a frame has completed rendering.
		/// </summary>
		public event FrameEvent FrameEnded;
		
		#endregion

		#region Properties
		/// <summary>
		/// Specifies the name of the engine that will be used where needed (i.e. log files, etc).  
		/// </summary>
		public string Name {
			get {
				AssemblyTitleAttribute attribute = 
					(AssemblyTitleAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyTitleAttribute), false);

				if(attribute != null)
					return attribute.Title;
				else
					return "Not Found";
			}
		}

		/// <summary>
		/// Specifies the name of the engine that will be used where needed (i.e. log files, etc).  
		/// </summary>
		public string Copyright {
			get {
				AssemblyCopyrightAttribute attribute = 
					(AssemblyCopyrightAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyCopyrightAttribute), false);

				if(attribute != null)
					return attribute.Copyright;
				else
					return "Not Found";
			}
		}

		/// <summary>
		/// Returns the current version of the Engine assembly.
		/// </summary>
		public string Version {
			get {
				// returns the file version of this assembly
				return Assembly.GetExecutingAssembly().GetName().Version.ToString();
			}
		}

		/// <summary>
		/// The current SceneManager in use by the engine.
		/// </summary>
		public SceneManager SceneManager {
			get {
				return sceneManager;
			}
			set {
				sceneManager = value;
			}
		}

		/// <summary>
		///		
		/// </summary>
		public SceneManagerList SceneManagers {
			get {
				return sceneManagerList;
			}
		}

		/// <summary>
		/// Gets/Sets the current active RenderSystem that the engine is using.
		/// </summary>
		public RenderSystem RenderSystem {
			get {
				return activeRenderSystem;
			}
			set {
				// Sets the active rendering system
				// Can be called direct or will be called by
				// standard config dialog

				// Is there already an active renderer?
				// If so, disable it and initialize the new one
				if(activeRenderSystem != null && activeRenderSystem != value ) {
					activeRenderSystem.Shutdown();
				}

				activeRenderSystem = value;

				// Tell scene managers
				SceneManagerList.Instance.RegisterRenderSystem(activeRenderSystem);
			}
		}

		/// <summary>
		/// The list of available render systems for the engine to use (made available via plugins.
		/// </summary>
		public RenderSystemCollection RenderSystems {
			get {
				return renderSystemList;
			}
		}

		/// <summary>
		///    Gets a reference to the timer being used for timing throughout the engine.
		/// </summary>
		public ITimer Timer {
			get {
				return timer;
			}
		}

		#endregion

		/// <summary>
		/// 
		/// </summary>
		/// <param name="autoCreateWindow"></param>
		/// <returns></returns>
		public RenderWindow Initialize(bool autoCreateWindow) {			
			Debug.Assert(activeRenderSystem != null, "Engine cannot be initialized if a valid RenderSystem is not also initialized.");

			// initialize the current render system
			RenderWindow window = activeRenderSystem.Initialize(autoCreateWindow);

			// if they chose to auto create a window, also initialize several subsystems
			if(autoCreateWindow) {
				OneTimePostWindowInit();
			}

			return window;
		}

		/// <summary>
		///    Internal method for one-time tasks after first window creation.
		/// </summary>
		private void OneTimePostWindowInit() {
			if(firstTime) {
				// init material manager singleton, which parse sources for materials
				MaterialManager.Instance.ParseAllSources();

				// init the particle system manager singleton
				ParticleSystemManager.Instance.ParseAllSources();

				// init font manager singletons
				FontManager.Instance.ParseAllSources();

				// init overlay manager
				OverlayManager.Instance.ParseAllSources();

				firstTime = false;
			}
		}

		/// <summary>
		///		Overloaded method.
		/// </summary>
		/// <param name="name"></param>
		/// <param name="target"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="colorDepth"></param>
		/// <param name="isFullscreen"></param>
		/// <returns></returns>
		public RenderWindow CreateRenderWindow(string name, int width, int height, int colorDepth, bool isFullscreen) {
			return CreateRenderWindow(name, width, height, colorDepth, isFullscreen, 0, 0, true, false, IntPtr.Zero);
		}

		/// <summary>
		///		
		/// </summary>
		/// <param name="name"></param>
		/// <param name="target"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="colorDepth"></param>
		/// <param name="isFullscreen"></param>
		/// <param name="left"></param>
		/// <param name="top"></param>
		/// <param name="depthBuffer"></param>
		/// <param name="handle">
		///		A handle to a pre-created window to be used for the rendering target.
		///	 </param>
		/// <returns></returns>
		public RenderWindow CreateRenderWindow(string name, int width, int height, int colorDepth,
			bool isFullscreen, int left, int top, bool depthBuffer, bool vsync, object targetHandle) {

			Debug.Assert(activeRenderSystem != null, "Cannot create a RenderWindow without an active RenderSystem.");

			// create a new render window via the current render system
			RenderWindow window = 
				activeRenderSystem.CreateRenderWindow(
				name, width, height, colorDepth, isFullscreen, left, top,
				depthBuffer, vsync, targetHandle);

			// do any required initialization
			OneTimePostWindowInit();

			return window;
		}

		/// <summary>
		///		Asks the current API to convert an instance of ColorEx to a 4 byte packed
		///		int value the way it would expect it. 		
		/// </summary>
		/// <param name="color"></param>
		/// <returns></returns>
		public int ConvertColor(ColorEx color) {
			Debug.Assert(activeRenderSystem != null, "Cannot covert color value without an active renderer.");

			return activeRenderSystem.ConvertColor(color);
		}

		/// <summary>
		/// Used to setup the engine and all it's dependencies.  
		/// This should be called before anything else is.
		/// </summary>
		public void Setup() {
			if (alreadySetup) {
				throw new ApplicationException("Root.Setup() called twice!");
			}

			alreadySetup = true;

			// create a new engine log
			engineLog = new Log("AxiomEngine.log");

			// add the log to the list of trace listeners to capture output
			System.Diagnostics.Trace.Listeners.Add(engineLog);

			// initialize all singletons, resetting them in the case of running more than once within the same AppDomain
			InitializeSingletons();

			// create a new timer
			timer = PlatformManager.Instance.CreateTimer();

			// get the singleton instance of the SceneManagerList
			sceneManagerList = SceneManagerList.Instance;

			renderSystemList = new RenderSystemCollection();

			// write the initial info at the top of the log
			System.Diagnostics.Trace.WriteLine("*********" + this.Name + " Log *************");
			System.Diagnostics.Trace.WriteLine("---- Copyright " + this.Copyright + " ---");
			System.Diagnostics.Trace.WriteLine("----- Version: " + this.Version + " ------");
			System.Diagnostics.Trace.WriteLine("Engine initializing...");
			System.Diagnostics.Trace.WriteLine(string.Format("Operating System: {0}", Environment.OSVersion.ToString()));
			System.Diagnostics.Trace.WriteLine(string.Format(".Net Framework: {0}", Environment.Version.ToString()));

			// Load ze plugins please
			PluginManager.Init();
		}

		/// <summary>
		///		Renders one frame.
		/// </summary>
		/// <remarks>
		///		Updates all the render targets automatically and then returns, raising frame events before and after.
		/// </remarks>
		/// <returns>True if execution should continue, false if a quit was requested.</returns>
		public bool RenderOneFrame() {
			// Stop rendering if frame callback says so
			if(!OnFrameStarted()) {
				return false;
			}

			// update all current render targets
			activeRenderSystem.UpdateAllRenderTargets();

			// Stop rendering if frame callback says so
			return OnFrameEnded();
		}
		
		/// <summary>
		///		Starts the default rendering loop.
		/// </summary>
		public void StartRendering() {
			Debug.Assert(activeRenderSystem != null, "Engine cannot start rendering without an active RenderSystem.");

			// start the internal timer
			timer.Reset();

			// initialize the vars
			lastStartTime = lastEndTime = timer.Milliseconds;

			// reset to false so that rendering can begin
			queuedEnd = false;

			while(!queuedEnd) {
				// allow OS events to process (if the platform requires it
				PlatformManager.Instance.DoEvents();

				if(!RenderOneFrame()) {
					break;
				}
			}
		}

		/// <summary>
		///		Shuts down the engine and unloads plugins.
		/// </summary>
		public void Shutdown() {
			System.Diagnostics.Trace.WriteLine("***** " + this.Name + " Shutdown Initiated. ****");

			// shutdown the current render system if there is one
			if(activeRenderSystem != null)
				activeRenderSystem.Shutdown();

			// trigger a disposal of all resources
			// destroy all textures
			if(TextureManager.Instance != null)
				TextureManager.Instance.Dispose();

			// destroy all disposable objects
			GarbageManager.Instance.DisposeAll();

			// Write final performance stats
			System.Diagnostics.Trace.WriteLine("Final Stats:");
			System.Diagnostics.Trace.WriteLine("Axiom Framerate Average FPS: " + averageFPS.ToString("0.000000") + " Best FPS: " + highestFPS.ToString("0.000000") + " Worst FPS: " + lowestFPS.ToString("0.000000"));
            
			engineLog.Dispose();

			firstTime = true;
			alreadySetup = false;
		}

		/// <summary>
		///		Requests that the rendering engine shutdown at the beginning of the next frame.
		/// </summary>
		public void QueueEndRendering() {<commit_msg>Made a fix to Shutdown() that causes the engine to cleanly shut down on the next frame, rather than either running forever, or throwing an exception due to unavailable resources.<commit_after>#region LGPL License
/*
Axiom Game Engine Library
Copyright (C) 2003  Axiom Project Team

The overall design, and a majority of the core engine and rendering code 
contained within this library is a derivative of the open source Object Oriented 
Graphics Engine OGRE, which can be found at http://ogre.sourceforge.net.  
Many thanks to the OGRE team for maintaining such a high quality project.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#endregion

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Axiom.Collections;
using Axiom.Controllers;
using Axiom.Fonts;
using Axiom.Overlays;
using Axiom.Input;
using Axiom.ParticleSystems;
using Axiom.Utility;
using Axiom.Graphics;

namespace Axiom.Core {
	/// <summary>
	///		A delegate for defining frame events.
	/// </summary>
	public delegate void FrameEvent(object source, FrameEventArgs e);

	/// <summary>
	///		Used to supply info to the FrameStarted and FrameEnded events.
	/// </summary>
	public class FrameEventArgs : System.EventArgs {
		/// <summary>
		///    Time elapsed (in milliseconds) since the last frame event.
		/// </summary>
		public float TimeSinceLastEvent;

		/// <summary>
		///    Time elapsed (in milliseconds) since the last frame.
		/// </summary>
		public float TimeSinceLastFrame;

		/// <summary>
		///    Event handlers should set this to true if they wish to stop the render loop and begin shutdown.
		/// </summary>
		public bool RequestShutdown;
	}

	public enum FrameEventType {
		Start,
		End
	}

	/// <summary>
	///		The Engine class is the main container of all the subsystems.  This includes the RenderSystem, various ResourceManagers, etc.
	/// </summary>
	public class Root : IDisposable {
		#region Singleton implementation
		
		private Root() {}
		private static readonly Root instance = new Root();

		public static Root Instance {
			get {
				return instance;
			}
		}

		#endregion

		private SceneManager sceneManager;
		private SceneManagerList sceneManagerList;
		private RenderSystemCollection renderSystemList;
		private RenderSystem activeRenderSystem;
		private Log engineLog;
		private ITimer timer;

		// Framerate Related State
		private long lastStartTime, lastEndTime;
		private long lastCalculationTime;							// The Last Time We Calculated Framerate
		private long frameCount;									// Frames Drawn Counter For FPS Calculations
		private float currentFPS;									// Current FPS
		private float highestFPS;									// Highest FPS
		private float lowestFPS = 9999;								// Lowest FPS
		private float averageFPS;

		/// <summary>
		///		Global frame count since startup.
		/// </summary>
		private ulong currentFrameCount;

		/// <summary>
		///    Has the first render window been created yet?
		/// </summary>
		private bool firstTime = true;

		/// <summary>
		/// Has the Setup() method been called yet?
		/// </summary>
		private bool alreadySetup = false;
		/// <summary>
		///		True if a request has been made to shutdown the rendering engine.
		/// </summary>
		private bool queuedEnd;

		#region Events
		
		/// <summary>
		/// Fired as a frame is about to be rendered.
		/// </summary>
		public event FrameEvent FrameStarted;

		/// <summary>
		/// Fired after a frame has completed rendering.
		/// </summary>
		public event FrameEvent FrameEnded;
		
		#endregion

		#region Properties
		/// <summary>
		/// Specifies the name of the engine that will be used where needed (i.e. log files, etc).  
		/// </summary>
		public string Name {
			get {
				AssemblyTitleAttribute attribute = 
					(AssemblyTitleAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyTitleAttribute), false);

				if(attribute != null)
					return attribute.Title;
				else
					return "Not Found";
			}
		}

		/// <summary>
		/// Specifies the name of the engine that will be used where needed (i.e. log files, etc).  
		/// </summary>
		public string Copyright {
			get {
				AssemblyCopyrightAttribute attribute = 
					(AssemblyCopyrightAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyCopyrightAttribute), false);

				if(attribute != null)
					return attribute.Copyright;
				else
					return "Not Found";
			}
		}

		/// <summary>
		/// Returns the current version of the Engine assembly.
		/// </summary>
		public string Version {
			get {
				// returns the file version of this assembly
				return Assembly.GetExecutingAssembly().GetName().Version.ToString();
			}
		}

		/// <summary>
		/// The current SceneManager in use by the engine.
		/// </summary>
		public SceneManager SceneManager {
			get {
				return sceneManager;
			}
			set {
				sceneManager = value;
			}
		}

		/// <summary>
		///		
		/// </summary>
		public SceneManagerList SceneManagers {
			get {
				return sceneManagerList;
			}
		}

		/// <summary>
		/// Gets/Sets the current active RenderSystem that the engine is using.
		/// </summary>
		public RenderSystem RenderSystem {
			get {
				return activeRenderSystem;
			}
			set {
				// Sets the active rendering system
				// Can be called direct or will be called by
				// standard config dialog

				// Is there already an active renderer?
				// If so, disable it and initialize the new one
				if(activeRenderSystem != null && activeRenderSystem != value ) {
					activeRenderSystem.Shutdown();
				}

				activeRenderSystem = value;

				// Tell scene managers
				SceneManagerList.Instance.RegisterRenderSystem(activeRenderSystem);
			}
		}

		/// <summary>
		/// The list of available render systems for the engine to use (made available via plugins.
		/// </summary>
		public RenderSystemCollection RenderSystems {
			get {
				return renderSystemList;
			}
		}

		/// <summary>
		///    Gets a reference to the timer being used for timing throughout the engine.
		/// </summary>
		public ITimer Timer {
			get {
				return timer;
			}
		}

		#endregion

		/// <summary>
		/// 
		/// </summary>
		/// <param name="autoCreateWindow"></param>
		/// <returns></returns>
		public RenderWindow Initialize(bool autoCreateWindow) {			
			Debug.Assert(activeRenderSystem != null, "Engine cannot be initialized if a valid RenderSystem is not also initialized.");

			// initialize the current render system
			RenderWindow window = activeRenderSystem.Initialize(autoCreateWindow);

			// if they chose to auto create a window, also initialize several subsystems
			if(autoCreateWindow) {
				OneTimePostWindowInit();
			}

			return window;
		}

		/// <summary>
		///    Internal method for one-time tasks after first window creation.
		/// </summary>
		private void OneTimePostWindowInit() {
			if(firstTime) {
				// init material manager singleton, which parse sources for materials
				MaterialManager.Instance.ParseAllSources();

				// init the particle system manager singleton
				ParticleSystemManager.Instance.ParseAllSources();

				// init font manager singletons
				FontManager.Instance.ParseAllSources();

				// init overlay manager
				OverlayManager.Instance.ParseAllSources();

				firstTime = false;
			}
		}

		/// <summary>
		///		Overloaded method.
		/// </summary>
		/// <param name="name"></param>
		/// <param name="target"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="colorDepth"></param>
		/// <param name="isFullscreen"></param>
		/// <returns></returns>
		public RenderWindow CreateRenderWindow(string name, int width, int height, int colorDepth, bool isFullscreen) {
			return CreateRenderWindow(name, width, height, colorDepth, isFullscreen, 0, 0, true, false, IntPtr.Zero);
		}

		/// <summary>
		///		
		/// </summary>
		/// <param name="name"></param>
		/// <param name="target"></param>
		/// <param name="width"></param>
		/// <param name="height"></param>
		/// <param name="colorDepth"></param>
		/// <param name="isFullscreen"></param>
		/// <param name="left"></param>
		/// <param name="top"></param>
		/// <param name="depthBuffer"></param>
		/// <param name="handle">
		///		A handle to a pre-created window to be used for the rendering target.
		///	 </param>
		/// <returns></returns>
		public RenderWindow CreateRenderWindow(string name, int width, int height, int colorDepth,
			bool isFullscreen, int left, int top, bool depthBuffer, bool vsync, object targetHandle) {

			Debug.Assert(activeRenderSystem != null, "Cannot create a RenderWindow without an active RenderSystem.");

			// create a new render window via the current render system
			RenderWindow window = 
				activeRenderSystem.CreateRenderWindow(
				name, width, height, colorDepth, isFullscreen, left, top,
				depthBuffer, vsync, targetHandle);

			// do any required initialization
			OneTimePostWindowInit();

			return window;
		}

		/// <summary>
		///		Asks the current API to convert an instance of ColorEx to a 4 byte packed
		///		int value the way it would expect it. 		
		/// </summary>
		/// <param name="color"></param>
		/// <returns></returns>
		public int ConvertColor(ColorEx color) {
			Debug.Assert(activeRenderSystem != null, "Cannot covert color value without an active renderer.");

			return activeRenderSystem.ConvertColor(color);
		}

		/// <summary>
		/// Used to setup the engine and all it's dependencies.  
		/// This should be called before anything else is.
		/// </summary>
		public void Setup() {
			if (alreadySetup) {
				throw new ApplicationException("Root.Setup() called twice!");
			}

			alreadySetup = true;

			// create a new engine log
			engineLog = new Log("AxiomEngine.log");

			// add the log to the list of trace listeners to capture output
			System.Diagnostics.Trace.Listeners.Add(engineLog);

			// initialize all singletons, resetting them in the case of running more than once within the same AppDomain
			InitializeSingletons();

			// create a new timer
			timer = PlatformManager.Instance.CreateTimer();

			// get the singleton instance of the SceneManagerList
			sceneManagerList = SceneManagerList.Instance;

			renderSystemList = new RenderSystemCollection();

			// write the initial info at the top of the log
			System.Diagnostics.Trace.WriteLine("*********" + this.Name + " Log *************");
			System.Diagnostics.Trace.WriteLine("---- Copyright " + this.Copyright + " ---");
			System.Diagnostics.Trace.WriteLine("----- Version: " + this.Version + " ------");
			System.Diagnostics.Trace.WriteLine("Engine initializing...");
			System.Diagnostics.Trace.WriteLine(string.Format("Operating System: {0}", Environment.OSVersion.ToString()));
			System.Diagnostics.Trace.WriteLine(string.Format(".Net Framework: {0}", Environment.Version.ToString()));

			// Load ze plugins please
			PluginManager.Init();
		}

		/// <summary>
		///		Renders one frame.
		/// </summary>
		/// <remarks>
		///		Updates all the render targets automatically and then returns, raising frame events before and after.
		/// </remarks>
		/// <returns>True if execution should continue, false if a quit was requested.</returns>
		public bool RenderOneFrame() {
			// Stop rendering if frame callback says so
			if(!OnFrameStarted()) {
				return false;
			}

			// update all current render targets
			activeRenderSystem.UpdateAllRenderTargets();

			// Stop rendering if frame callback says so
			return OnFrameEnded();
		}
		
		/// <summary>
		///		Starts the default rendering loop.
		/// </summary>
		public void StartRendering() {
			Debug.Assert(activeRenderSystem != null, "Engine cannot start rendering without an active RenderSystem.");

			// start the internal timer
			timer.Reset();

			// initialize the vars
			lastStartTime = lastEndTime = timer.Milliseconds;

			// reset to false so that rendering can begin
			queuedEnd = false;

			while(!queuedEnd) {
				// allow OS events to process (if the platform requires it
				PlatformManager.Instance.DoEvents();

				if(!RenderOneFrame()) {
					break;
				}
			}
		}

		/// <summary>
		///		Shuts down the engine and unloads plugins.
		/// </summary>
		public void Shutdown() {
			System.Diagnostics.Trace.WriteLine("***** " + this.Name + " Shutdown Initiated. ****");

			// shutdown the current render system if there is one
			if(activeRenderSystem != null)
				activeRenderSystem.Shutdown();

			// trigger a disposal of all resources
			// destroy all textures
			if(TextureManager.Instance != null)
				TextureManager.Instance.Dispose();

			// destroy all disposable objects
			GarbageManager.Instance.DisposeAll();

			// Write final performance stats
			System.Diagnostics.Trace.WriteLine("Final Stats:");
			System.Diagnostics.Trace.WriteLine("Axiom Framerate Average FPS: " + averageFPS.ToString("0.000000") + " Best FPS: " + highestFPS.ToString("0.000000") + " Worst FPS: " + lowestFPS.ToString("0.000000"));
            
			engineLog.Dispose();

			firstTime = true;
			alreadySetup = false;
			QueueEndRendering();
		}

		/// <summary>
		///		Requests that the rendering engine shutdown at the beginning of the next frame.
		/// </summary>
		public void QueueEndRendering() {
{'fraction_non_alphanumeric': 0.11362070840735236, 'fraction_numerical': 0.003625421455244172, 'mean_word_length': 3.066637181188265, 'pattern_counts': {'":': 0, '<': 219, '<?xml version=': 0, '>': 219, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '739766', 'n_tokens_mistral': 8879, 'n_tokens_neox': 7475, 'n_words': 2654}