budnhead/org.budnhead/core/Engine.cs

175 lines
4.0 KiB
C#

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<SceneWindow>
sceneWindows = new List<SceneWindow>();
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<object>.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) );
}
}
}
}
}