From 57b72e71e15dcbcf62c9597a944e7764640fc74d Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Sun, 23 Sep 2007 12:09:42 +0000 Subject: [PATCH] Added constant and variable time-step update logic to GameWindow. Updated examples to use the new GameWindow UpdateFrame and RenderFrame events. --- Source/Examples/Tests/S02_RawInput_Logger.cs | 2 +- Source/Examples/Tests/S03_Stack_Imbalance.cs | 2 +- Source/Examples/Tutorial/T01_Simple_Window.cs | 2 +- .../Tutorial/T02_Vertex_Array_Cube.cs | 37 +++---- .../Tutorial/T03_Immediate_Mode_Cube.cs | 2 +- .../Tutorial/T07_Display_Lists_Flower.cs | 2 +- Source/Examples/Tutorial/T08_VBO.cs | 2 +- Source/Examples/Tutorial/T10_GLSL_Cube.cs | 2 +- Source/OpenTK/GameWindow.cs | 98 ++++++++++++++++--- Source/OpenTK/Platform/IGLContext.cs | 4 +- Source/OpenTK/Platform/IGameWindow.cs | 6 +- 11 files changed, 114 insertions(+), 45 deletions(-) diff --git a/Source/Examples/Tests/S02_RawInput_Logger.cs b/Source/Examples/Tests/S02_RawInput_Logger.cs index fc0f2c4f..9f8b5568 100644 --- a/Source/Examples/Tests/S02_RawInput_Logger.cs +++ b/Source/Examples/Tests/S02_RawInput_Logger.cs @@ -87,7 +87,7 @@ namespace Examples.Tests GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f); } - public override void OnRenderFrame(EventArgs e) + public override void OnRenderFrame(RenderFrameEventArgs e) { base.OnRenderFrame(e); diff --git a/Source/Examples/Tests/S03_Stack_Imbalance.cs b/Source/Examples/Tests/S03_Stack_Imbalance.cs index d34a5cdc..ff055720 100644 --- a/Source/Examples/Tests/S03_Stack_Imbalance.cs +++ b/Source/Examples/Tests/S03_Stack_Imbalance.cs @@ -30,7 +30,7 @@ namespace Examples.Tests } float[] proj = new float[16]; - public override void OnRenderFrame(EventArgs e) + public override void OnRenderFrame(RenderFrameEventArgs e) { GL.Clear(GL.Enums.ClearBufferMask.COLOR_BUFFER_BIT); diff --git a/Source/Examples/Tutorial/T01_Simple_Window.cs b/Source/Examples/Tutorial/T01_Simple_Window.cs index 43499f4d..ab285d03 100644 --- a/Source/Examples/Tutorial/T01_Simple_Window.cs +++ b/Source/Examples/Tutorial/T01_Simple_Window.cs @@ -51,7 +51,7 @@ namespace Examples.Tutorial /// will be notified of frame rendering events! /// /// Not used. - public override void OnRenderFrame(EventArgs e) + public override void OnRenderFrame(RenderFrameEventArgs e) { GL.Clear(GL.Enums.ClearBufferMask.COLOR_BUFFER_BIT); diff --git a/Source/Examples/Tutorial/T02_Vertex_Array_Cube.cs b/Source/Examples/Tutorial/T02_Vertex_Array_Cube.cs index b597ea26..d5c73e0f 100644 --- a/Source/Examples/Tutorial/T02_Vertex_Array_Cube.cs +++ b/Source/Examples/Tutorial/T02_Vertex_Array_Cube.cs @@ -92,16 +92,8 @@ namespace Examples.Tutorial Fullscreen = !Fullscreen; } - GL.MatrixMode(GL.Enums.MatrixMode.MODELVIEW); - GL.LoadIdentity(); - Glu.LookAt( - 0.0, 5.0, 5.0, - 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0 - ); - - GL.Rotate(angle, 0.0f, 1.0f, 0.0f); - angle += 180.0f * e.Time; + //angle += 180.0f * (float)e.Time; + angle += 3.0f; if (angle > 720.0f) angle -= 720.0f; } @@ -113,21 +105,23 @@ namespace Examples.Tutorial /// /// Place your rendering code here. /// - public override void OnRenderFrame(EventArgs e) + public override void OnRenderFrame(RenderFrameEventArgs e) { GL.Clear(GL.Enums.ClearBufferMask.COLOR_BUFFER_BIT | GL.Enums.ClearBufferMask.DEPTH_BUFFER_BIT); - unsafe - { - fixed (ushort* index_pointer = Shapes.Cube.Indices) - { - GL.DrawElements(GL.Enums.BeginMode.TRIANGLES, Shapes.Cube.Indices.Length, - GL.Enums.All.UNSIGNED_SHORT, index_pointer); - } - } + GL.MatrixMode(GL.Enums.MatrixMode.MODELVIEW); + GL.LoadIdentity(); + Glu.LookAt( + 0.0, 5.0, 5.0, + 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0 + ); + GL.Rotate(angle, 0.0f, 1.0f, 0.0f); + + GL.DrawElements(GL.Enums.BeginMode.TRIANGLES, Shapes.Cube.Indices.Length, + GL.Enums.All.UNSIGNED_SHORT, Shapes.Cube.Indices); Context.SwapBuffers(); - Thread.Sleep(0); } #endregion @@ -142,7 +136,8 @@ namespace Examples.Tutorial /// public void Launch() { - Run(); + // Run the main loop calling UpdateFrame and RenderFrame 60 times per second. + Run(60.0, 60.0); } #endregion diff --git a/Source/Examples/Tutorial/T03_Immediate_Mode_Cube.cs b/Source/Examples/Tutorial/T03_Immediate_Mode_Cube.cs index 35b96d9c..7d1f1105 100644 --- a/Source/Examples/Tutorial/T03_Immediate_Mode_Cube.cs +++ b/Source/Examples/Tutorial/T03_Immediate_Mode_Cube.cs @@ -118,7 +118,7 @@ namespace Examples.Tutorial /// /// Place your rendering code here. /// - public override void OnRenderFrame(EventArgs e) + public override void OnRenderFrame(RenderFrameEventArgs e) { GL.Clear(GL.Enums.ClearBufferMask.COLOR_BUFFER_BIT | GL.Enums.ClearBufferMask.DEPTH_BUFFER_BIT); diff --git a/Source/Examples/Tutorial/T07_Display_Lists_Flower.cs b/Source/Examples/Tutorial/T07_Display_Lists_Flower.cs index 6e53104c..578e06b4 100644 --- a/Source/Examples/Tutorial/T07_Display_Lists_Flower.cs +++ b/Source/Examples/Tutorial/T07_Display_Lists_Flower.cs @@ -137,7 +137,7 @@ namespace Examples.Tutorial #region OnRenderFrame - public override void OnRenderFrame(EventArgs e) + public override void OnRenderFrame(RenderFrameEventArgs e) { base.OnRenderFrame(e); diff --git a/Source/Examples/Tutorial/T08_VBO.cs b/Source/Examples/Tutorial/T08_VBO.cs index 956acbf4..166a6232 100644 --- a/Source/Examples/Tutorial/T08_VBO.cs +++ b/Source/Examples/Tutorial/T08_VBO.cs @@ -153,7 +153,7 @@ namespace Examples.Tutorial #region OnRenderFrame - public override void OnRenderFrame(EventArgs e) + public override void OnRenderFrame(RenderFrameEventArgs e) { base.OnRenderFrame(e); diff --git a/Source/Examples/Tutorial/T10_GLSL_Cube.cs b/Source/Examples/Tutorial/T10_GLSL_Cube.cs index a933b3c5..90fb2164 100644 --- a/Source/Examples/Tutorial/T10_GLSL_Cube.cs +++ b/Source/Examples/Tutorial/T10_GLSL_Cube.cs @@ -172,7 +172,7 @@ namespace Examples.Tutorial #region OnRenderFrame - public override void OnRenderFrame(EventArgs e) + public override void OnRenderFrame(RenderFrameEventArgs e) { base.OnRenderFrame(e); diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 88fc3c27..36d96ac2 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -29,6 +29,8 @@ namespace OpenTK private bool isExiting; private bool disposed; + private double updateTime, renderTime, eventTime, frameTime; + #endregion #region --- Contructors --- @@ -293,7 +295,7 @@ namespace OpenTK #region public virtual void Run() /// - /// Runs the default game loop on GameWindow (process event->update frame->render frame). + /// Runs the default game loop on GameWindow at the maximum possible update and render speed. /// /// /// @@ -307,7 +309,18 @@ namespace OpenTK /// to Operating System events. /// /// - public virtual void Run() + /// + public void Run() + { + Run(0.0f, 0.0f); + } + + /// + /// Runs the default game loop on GameWindow at the specified update and render speed. + /// + /// Indicates how many times UpdateFrame will be called per second. + /// Indicates how many times RenderFrame will be called per second. + public virtual void Run(double update_frequency, double render_frequency) { this.OnLoad(EventArgs.Empty); resizeEventArgs.Width = this.Width; @@ -316,20 +329,65 @@ namespace OpenTK Debug.Print("Entering main loop"); - System.Diagnostics.Stopwatch watch = new Stopwatch(); - watch.Reset(); + Stopwatch watch = new Stopwatch(); UpdateFrameEventArgs updateArgs = new UpdateFrameEventArgs(); + RenderFrameEventArgs renderArgs = new RenderFrameEventArgs(); + + double update_target = 0.0, render_target = 0.0, next_update = 0.0, next_render = 0.0; + double time, total_time; + + if (update_frequency > 0.0) + { + next_update = update_target = 1.0 / update_frequency; + } + if (render_frequency > 0.0) + { + next_render = render_target = 1.0 / render_frequency; + } while (this.Exists && !IsExiting) { + total_time = frameTime = watch.Elapsed.TotalSeconds; + if (total_time > 0.1) + total_time = 0.1; + updateArgs.Time = renderArgs.Time = total_time; + + watch.Reset(); + watch.Start(); + + time = watch.Elapsed.TotalSeconds; this.ProcessEvents(); + eventTime = watch.Elapsed.TotalSeconds - time; + if (!IsExiting) { - updateArgs.Time = watch.ElapsedMilliseconds / 1000.0f; - watch.Reset(); - watch.Start(); - this.OnUpdateFrame(updateArgs); - this.OnRenderFrame(EventArgs.Empty); + time = watch.Elapsed.TotalSeconds; + next_update -= (total_time + time); + while (next_update <= 0.0) + { + updateArgs.Time += watch.Elapsed.TotalSeconds; + this.OnUpdateFrame(updateArgs); + if (update_target == 0.0) + break; + next_update += update_target; + } + updateTime = watch.Elapsed.TotalSeconds - time; + + time = watch.Elapsed.TotalSeconds; + next_render -= (total_time + time); + if (next_render <= 0.0) + { + renderArgs.Time += time; + this.OnRenderFrame(renderArgs); + next_render += render_target; + } + renderTime = watch.Elapsed.TotalSeconds - time; + + if (renderTime < render_target && updateTime < update_target) + { + Thread.Sleep((int)(1000.0 * System.Math.Min( + render_target - renderTime, update_target - updateTime))); + } } } @@ -367,7 +425,7 @@ namespace OpenTK #endregion - #region public virtual void OnRenderFrame(EventArgs e) + #region public virtual void OnRenderFrame(RenderFrameEventArgs e) /// /// Raises the RenderFrame event. Override in derived classes to render a frame. @@ -376,7 +434,7 @@ namespace OpenTK /// If overriden, the base.OnRenderFrame() function should be called, to ensure /// listeners are notified of RenderFrame events. /// - public virtual void OnRenderFrame(EventArgs e) + public virtual void OnRenderFrame(RenderFrameEventArgs e) { if (!this.Exists && !this.IsExiting) { @@ -626,12 +684,26 @@ namespace OpenTK public class UpdateFrameEventArgs : EventArgs { - private float time; + private double time; /// /// Gets the Time elapsed between frame updates, in seconds. /// - public float Time + public double Time + { + get { return time; } + internal set { time = value; } + } + } + + public class RenderFrameEventArgs : EventArgs + { + private double time; + + /// + /// Gets the Time elapsed between frame updates, in seconds. + /// + public double Time { get { return time; } internal set { time = value; } diff --git a/Source/OpenTK/Platform/IGLContext.cs b/Source/OpenTK/Platform/IGLContext.cs index 80abdbd9..9797c0d2 100644 --- a/Source/OpenTK/Platform/IGLContext.cs +++ b/Source/OpenTK/Platform/IGLContext.cs @@ -62,12 +62,12 @@ namespace OpenTK.Platform void CreateContext(bool direct, IGLContext source); /// - /// Swaps buffers on a context. This presents the rendered scene to the user. + /// Swaps buffers, presenting the rendered scene to the user. /// void SwapBuffers(); /// - /// Makes this context the current rendering target. + /// Makes the Context current in the calling thread, i.e. future OpenGL commands will affect this Context. /// void MakeCurrent(); diff --git a/Source/OpenTK/Platform/IGameWindow.cs b/Source/OpenTK/Platform/IGameWindow.cs index 3b52f0d4..95329502 100644 --- a/Source/OpenTK/Platform/IGameWindow.cs +++ b/Source/OpenTK/Platform/IGameWindow.cs @@ -14,11 +14,13 @@ namespace OpenTK.Platform { void Run(); - void OnRenderFrame(EventArgs e); + void OnRenderFrame(RenderFrameEventArgs e); void OnUpdateFrame(UpdateFrameEventArgs e); void OnLoad(EventArgs e); void Exit(); + void SwapBuffers(); + event UpdateFrameEvent UpdateFrame; event RenderFrameEvent RenderFrame; event LoadEvent Load; @@ -28,6 +30,6 @@ namespace OpenTK.Platform } public delegate void UpdateFrameEvent(object sender, UpdateFrameEventArgs e); - public delegate void RenderFrameEvent(object sender, EventArgs e); + public delegate void RenderFrameEvent(object sender, RenderFrameEventArgs e); public delegate void LoadEvent(object sender, EventArgs e); }