using System; using System.Collections.Generic; using System.Threading; using System.Diagnostics; using org.budnhead.graphics; using org.budnhead.tools; using OpenTK.Graphics; namespace org.budnhead.core { public delegate void UpdateDelegate(float elapsed); public static class Engine { /** Objects of the Game World **/ /** Engine management stuff **/ private static Object engineLock = new object(); private static Boolean initialized = false; private static readonly float tickTimeSpan = (1.0f / Stopwatch.Frequency); private static readonly float tickTimeSpanMS = (1000.0f / Stopwatch.Frequency); private static long lastWorldUpdateTick; private static ConsumerProducerLock consumerProducerLock = new ConsumerProducerLock(); internal static List sceneWindows = new List(); private static JobThreader sceneWindowJobs; private static Thread updateThread = new Thread(updateWorldThread); public static event UpdateDelegate BeforeUpdate, AfterUpdate; private static Boolean exiting; private static Object syncGraphicsStartup = new object(); private static float targetUpdatePeriod = 100; public static SceneWindow[] SceneWindows { get { lock (sceneWindows) { return sceneWindows.ToArray(); } } } public static float TargetUpdatePeriod { get { return targetUpdatePeriod; } set { targetUpdatePeriod = value; } } public static float TargetUpdateFrequency { get { return targetUpdatePeriod == 0 ? 0 : 1.0f / targetUpdatePeriod; } set { targetUpdatePeriod = value == 0 ? 0 : 1.0f / value; } } public static Boolean ShouldContinue { get { lock(engineLock) { return !Engine.exiting; } } } public static void Shutdown(){ lock (engineLock){ exiting = true; } } public static void Initialize(){ InitializeGraphics(); InitializeUpdateThread(); } public static void InitializeUpdateThread(){ } public static void InitializeGraphics(){ GraphicsContext.ShareContexts = true; sceneWindowJobs = new JobThreader(); SceneWindow primaryWindow = new SceneWindow(); primaryWindow.MakeCurrent(); initialized = GlobalDefaults.initialize(); } public static void run(){ updateThread.Start(); sceneWindowsThread(); } private static void updateWorld(float elapsed){ consumerProducerLock.ProducerEnter(); if (BeforeUpdate!=null){ BeforeUpdate(elapsed); } Actor.updateActors(elapsed); if (AfterUpdate!=null){ AfterUpdate(elapsed); } consumerProducerLock.producerExclusive(); cycleBufferedValues(); consumerProducerLock.ProducerExit(); } public static bool cycleBufferedValues(){ BufferedValueInstance.cycle(); return true; } public static void ConsumerEnter(){ consumerProducerLock.ConsumerEnter(); } public static void ConsumerExit(){ consumerProducerLock.ConsumerExit(); } public static SceneWindow createSceneWindow(){ return sceneWindowJobs.execute(delegate { return new SceneWindow(); }); } static void sceneWindowsThread(){ while (Engine.ShouldContinue){ sceneWindowJobs.execute(); lock(Engine.sceneWindows){ foreach (SceneWindow sceneWindow in Engine.sceneWindows.ToArray()){ if (sceneWindow.Visible){ sceneWindow.ProcessEvents(); } } foreach (SceneWindow sceneWindow in Engine.sceneWindows.ToArray()){ sceneWindow.paint(); } } } } static void updateWorldThread(){ long lastWorldUpdateTick = Stopwatch.GetTimestamp(); float elapsed = 0; float update_needs; while (Engine.ShouldContinue){ long ticks = Stopwatch.GetTimestamp(); elapsed = tickTimeSpan * (ticks - lastWorldUpdateTick); updateWorld(elapsed); update_needs = tickTimeSpanMS * (Stopwatch.GetTimestamp() - ticks); lastWorldUpdateTick = ticks; if (update_needs < targetUpdatePeriod){ Thread.Sleep( (int)(targetUpdatePeriod - update_needs) ); } } } } }