2007-07-23 00:15:18 +00:00
#region - - - License - - -
/ * Copyright ( c ) 2006 , 2007 Stefanos Apostolopoulos
* See license . txt for license info
* /
#endregion
using System ;
using System.Collections.Generic ;
using System.Text ;
2007-07-26 22:56:55 +00:00
using System.Diagnostics ;
2007-07-23 00:15:18 +00:00
using OpenTK.Platform ;
2007-08-06 09:22:04 +00:00
using OpenTK.Input ;
using System.Threading ;
2007-09-02 22:52:00 +00:00
using OpenTK.OpenGL ;
2007-07-23 00:15:18 +00:00
namespace OpenTK
{
2007-09-26 11:50:44 +00:00
/// <summary>
/// The GameWindow class contains cross-platform methods to create and render on an OpenGL window, handle input and load resources.
/// </summary>
/// <remarks>
/// GameWindow contains several events you can hook or override to add your custom logic:
/// <list>
/// <item>OnLoad: Occurs after creating the OpenGL context, but before entering the main loop. Override to load resources.</item>
/// <item>OnResize: Occurs whenever GameWindow is resized. You should update the OpenGL Viewport and Projection Matrix here.</item>
/// <item>OnUpdateFrame: Occurs at the specified logic update rate. Override to add your game logic.</item>
/// <item>OnRenderFrame: Occurs at the specified frame render rate. Override to add your rendering code.</item>
/// </list>
/// Call the Run() method to start the application's main loop. Run(double, double) takes two parameters that
/// specify the logic update rate, and the render update rate.
/// </remarks>
2007-09-26 23:13:57 +00:00
public class GameWindow : IGameWindow
2007-07-23 00:15:18 +00:00
{
2007-08-05 13:42:31 +00:00
#region - - - Fields - - -
2007-09-26 11:50:44 +00:00
INativeGLWindow glWindow ;
DisplayMode mode ;
2007-07-23 00:15:18 +00:00
2007-09-26 11:50:44 +00:00
ResizeEventArgs resizeEventArgs = new ResizeEventArgs ( ) ;
2007-08-05 13:42:31 +00:00
2007-09-26 11:50:44 +00:00
bool isExiting ;
bool disposed ;
2007-08-06 09:22:04 +00:00
2007-09-26 11:50:44 +00:00
double updateTime , renderTime , eventTime , frameTime ;
int width , height ;
#endregion
#region - - - Internal Fields - - -
bool MustResize
{
get { return glWindow . Width ! = this . Width | | glWindow . Height ! = this . Height ; }
}
2007-09-23 12:09:42 +00:00
2007-08-05 13:42:31 +00:00
#endregion
2007-08-03 00:14:31 +00:00
2007-07-23 00:15:18 +00:00
#region - - - Contructors - - -
/// <summary>
2007-09-26 11:50:44 +00:00
/// Constructs a new GameWindow. Call CreateWindow() to open a render window.
2007-07-23 00:15:18 +00:00
/// </summary>
public GameWindow ( )
{
2007-09-09 16:07:39 +00:00
switch ( Environment . OSVersion . Platform )
2007-07-23 00:15:18 +00:00
{
2007-09-09 16:07:39 +00:00
case PlatformID . Win32NT :
case PlatformID . Win32S :
case PlatformID . Win32Windows :
case PlatformID . WinCE :
glWindow = new OpenTK . Platform . Windows . WinGLNative ( ) ;
break ;
case PlatformID . Unix :
case ( PlatformID ) 128 :
glWindow = new OpenTK . Platform . X11 . X11GLNative ( ) ;
break ;
default :
throw new PlatformNotSupportedException ( "Your platform is not supported currently. Please, refer to http://opentk.sourceforge.net for more information." ) ;
2007-07-23 00:15:18 +00:00
}
2007-09-26 11:50:44 +00:00
//glWindow.Resize += new ResizeEvent(glWindow_Resize);
2007-08-10 16:55:24 +00:00
glWindow . Destroy + = new DestroyEvent ( glWindow_Destroy ) ;
2007-08-06 11:22:18 +00:00
}
2007-09-26 11:50:44 +00:00
/// <summary>
/// Constructs a new GameWindow, and opens a render window with the specified DisplayMode.
/// </summary>
/// <param name="mode">The DisplayMode of the GameWindow.</param>
public GameWindow ( DisplayMode mode )
: this ( )
{
CreateWindow ( mode ) ;
}
/// <summary>
/// Constructs a new GameWindow with the specified title, and opens a render window with the specified DisplayMode.
/// </summary>
/// <param name="mode">The DisplayMode of the GameWindow.</param>
/// <param name="title">The Title of the GameWindow.</param>
public GameWindow ( DisplayMode mode , string title )
: this ( )
{
CreateWindow ( mode , title ) ;
}
2007-08-06 11:22:18 +00:00
void glWindow_Destroy ( object sender , EventArgs e )
{
2007-08-10 16:55:24 +00:00
Debug . Print ( "GameWindow destruction imminent." ) ;
2007-08-06 11:22:18 +00:00
this . isExiting = true ;
2007-08-10 16:55:24 +00:00
this . OnDestroy ( EventArgs . Empty ) ;
2007-08-06 12:13:50 +00:00
glWindow . Destroy - = glWindow_Destroy ;
2007-09-02 00:13:52 +00:00
//this.Dispose();
2007-07-23 00:15:18 +00:00
}
void glWindow_Resize ( object sender , ResizeEventArgs e )
{
2007-09-26 11:50:44 +00:00
this . OnResizeInternal ( e ) ;
2007-07-23 00:15:18 +00:00
}
#endregion
2007-09-26 11:50:44 +00:00
#region - - - Functions - - -
2007-08-05 13:42:31 +00:00
2007-09-26 11:50:44 +00:00
#region public void CreateWindow ( DisplayMode mode , string title )
2007-08-05 13:42:31 +00:00
2007-09-26 11:50:44 +00:00
/// <summary>
/// Creates a render window for the calling GameWindow, with the specified DisplayMode and Title.
/// </summary>
/// <param name="mode">The DisplayMode of the render window.</param>
/// <param name="title">The Title of the render window.</param>
/// <remarks>
/// It is an error to call this function when a render window already exists.
/// <para>Call DestroyWindow to close the render window.</para>
/// </remarks>
/// <exception cref="ApplicationException">Occurs when a render window already exists.</exception>
public void CreateWindow ( DisplayMode mode , string title )
2007-08-05 13:42:31 +00:00
{
2007-09-26 11:50:44 +00:00
if ( ! Exists )
2007-08-05 13:42:31 +00:00
{
2007-09-26 11:50:44 +00:00
try
2007-08-05 13:42:31 +00:00
{
2007-09-26 11:50:44 +00:00
glWindow . CreateWindow ( mode ) ;
this . Title = title ;
}
catch ( ApplicationException expt )
{
Debug . Print ( expt . ToString ( ) ) ;
throw ;
2007-08-05 13:42:31 +00:00
}
2007-09-26 11:50:44 +00:00
}
else
{
throw new ApplicationException ( "A render window already exists for this GameWindow." ) ;
2007-08-05 13:42:31 +00:00
}
}
#endregion
2007-08-06 09:22:04 +00:00
#endregion
2007-08-05 13:42:31 +00:00
#region - - - INativeGLWindow Members - - -
2007-08-04 12:09:58 +00:00
#region public void Exit ( )
/// <summary>
/// Gracefully exits the current GameWindow.
2007-08-06 09:22:04 +00:00
/// Override if you want to provide yor own exit sequence.
/// If you override this method, place a call to base.Exit(), to ensure
/// proper OpenTK shutdown.
2007-08-04 12:09:58 +00:00
/// </summary>
2007-08-06 09:22:04 +00:00
public virtual void Exit ( )
2007-08-04 12:09:58 +00:00
{
2007-08-06 09:22:04 +00:00
isExiting = true ;
//glWindow.Exit();
//this.Dispose();
2007-08-04 12:09:58 +00:00
}
#endregion
2007-07-23 00:15:18 +00:00
#region public bool IsIdle
2007-08-04 12:09:58 +00:00
/// <summary>
/// Gets a value indicating whether the current GameWindow is idle.
/// If true, the OnUpdateFrame and OnRenderFrame functions should be called.
/// </summary>
2007-07-23 00:15:18 +00:00
public bool IsIdle
{
get { return glWindow . IsIdle ; }
}
#endregion
#region public bool Fullscreen
2007-09-26 15:43:28 +00:00
/// <summary>
/// TODO: This property is not implemented.
/// Gets or sets a value indicating whether the GameWindow is in fullscrren mode.
/// </summary>
2007-07-23 00:15:18 +00:00
public bool Fullscreen
{
get { return glWindow . Fullscreen ; }
set { glWindow . Fullscreen = value ; }
}
#endregion
2007-08-04 12:09:58 +00:00
#region public IGLContext Context
2007-07-23 00:15:18 +00:00
2007-08-04 12:09:58 +00:00
/// <summary>
/// Returns the opengl IGLontext associated with the current GameWindow.
/// Forces window creation.
/// </summary>
public IGLContext Context
2007-07-23 00:15:18 +00:00
{
2007-08-04 12:09:58 +00:00
get
{
2007-08-10 16:55:24 +00:00
if ( ! this . Exists & & ! this . IsExiting )
{
Debug . WriteLine ( "WARNING: OpenGL Context accessed before creating a render window. This may indicate a programming error. Force-creating a render window." ) ;
mode = new DisplayMode ( 640 , 480 ) ;
this . CreateWindow ( mode ) ;
}
return glWindow . Context ;
}
2007-07-23 00:15:18 +00:00
}
#endregion
2007-08-06 09:22:04 +00:00
#region public bool Exists
2007-07-23 00:15:18 +00:00
2007-08-04 12:09:58 +00:00
/// <summary>
2007-09-26 11:50:44 +00:00
/// Gets a value indicating whether a render window exists.
2007-08-04 12:09:58 +00:00
/// </summary>
2007-08-06 09:22:04 +00:00
public bool Exists
2007-07-23 00:15:18 +00:00
{
2007-08-10 16:55:24 +00:00
get { return glWindow = = null ? false : glWindow . Exists ; }
2007-07-23 00:15:18 +00:00
}
#endregion
2007-09-26 11:50:44 +00:00
#region public string Text
/// <summary>
/// Gets or sets the GameWindow title.
/// </summary>
public string Title
{
get
{
return glWindow . Title ;
}
set
{
glWindow . Title = value ;
}
}
#endregion
#region public bool Visible
/// <summary>
2007-09-26 15:43:28 +00:00
/// TODO: This property is not implemented
2007-09-26 11:50:44 +00:00
/// Gets or sets a value indicating whether the GameWindow is visible.
/// </summary>
public bool Visible
{
get
{
2007-09-26 15:43:28 +00:00
throw new NotImplementedException ( ) ;
2007-09-26 11:50:44 +00:00
return glWindow . Visible ;
}
set
{
2007-09-26 15:43:28 +00:00
throw new NotImplementedException ( ) ;
2007-09-26 11:50:44 +00:00
glWindow . Visible = value ;
}
}
#endregion
2007-08-05 13:42:31 +00:00
#region public IWindowInfo WindowInfo
public IWindowInfo WindowInfo
{
get { return glWindow . WindowInfo ; }
}
#endregion
2007-09-26 11:50:44 +00:00
#region public IInputDriver InputDriver
/// <summary>
/// Gets an interface to the InputDriver used to obtain Keyboard, Mouse and Joystick input.
/// </summary>
public IInputDriver InputDriver
{
get
{
return glWindow . InputDriver ;
}
}
#endregion
2007-08-06 11:22:18 +00:00
#region public void CreateWindow ( DisplayMode mode )
/// <summary>
2007-09-26 11:50:44 +00:00
/// Creates a render window for the calling GameWindow.
2007-08-06 11:22:18 +00:00
/// </summary>
/// <param name="mode">The DisplayMode of the render window.</param>
2007-09-26 11:50:44 +00:00
/// <remarks>
/// It is an error to call this function when a render window already exists.
/// <para>Call DestroyWindow to close the render window.</para>
/// </remarks>
2007-08-06 11:22:18 +00:00
/// <exception cref="ApplicationException">Occurs when a render window already exists.</exception>
public void CreateWindow ( DisplayMode mode )
{
if ( ! Exists )
{
2007-08-20 13:47:14 +00:00
try
{
glWindow . CreateWindow ( mode ) ;
}
catch ( ApplicationException expt )
{
Debug . Print ( expt . ToString ( ) ) ;
throw ;
}
2007-08-06 11:22:18 +00:00
}
else
{
2007-09-26 11:50:44 +00:00
throw new ApplicationException ( "A render window already exists for this GameWindow." ) ;
2007-08-06 11:22:18 +00:00
}
}
#endregion
#region OnCreate
2007-09-02 00:13:52 +00:00
[Obsolete("The Create event is obsolete and will be removed on later versions. Use the Load event instead.")]
2007-08-06 11:22:18 +00:00
public event CreateEvent Create ;
2007-08-10 16:55:24 +00:00
/// <summary>
/// Raises the Create event. Override in derived classes to initialize resources.
/// </summary>
/// <param name="e"></param>
2007-09-02 00:13:52 +00:00
[Obsolete("The OnCreate method is obsolete and will be removed on later versions. Use the OnLoad method instead.")]
2007-08-06 11:22:18 +00:00
public virtual void OnCreate ( EventArgs e )
{
2007-08-20 10:46:37 +00:00
Debug . WriteLine ( "Firing GameWindow.Create event" ) ;
2007-08-06 11:22:18 +00:00
if ( this . Create ! = null )
{
this . Create ( this , e ) ;
}
}
#endregion
#region public void DestroyWindow ( )
2007-08-10 16:55:24 +00:00
/// <summary>
/// Destroys the GameWindow. The Destroy event is raised before destruction commences
/// (while the opengl context still exists), to allow resource cleanup.
/// </summary>
2007-08-06 11:22:18 +00:00
public void DestroyWindow ( )
{
if ( Exists )
{
glWindow . DestroyWindow ( ) ;
}
else
{
throw new ApplicationException ( "Tried to destroy inexistent window." ) ;
}
}
#endregion
#region OnDestroy
2007-08-10 16:55:24 +00:00
/// <summary>
/// Raises the Destroy event. Override in derived classes, to modify the shutdown
/// sequence (e.g. to release resources before shutdown).
/// </summary>
/// <param name="e"></param>
2007-08-06 11:22:18 +00:00
public virtual void OnDestroy ( EventArgs e )
{
2007-08-20 10:46:37 +00:00
Debug . WriteLine ( "Firing GameWindow.Destroy event" ) ;
2007-08-06 11:22:18 +00:00
if ( this . Destroy ! = null )
{
this . Destroy ( this , e ) ;
}
}
public event DestroyEvent Destroy ;
#endregion
2007-07-23 00:15:18 +00:00
#endregion
#region - - - IGameWindow Members - - -
2007-09-26 23:13:57 +00:00
#region void Run ( )
2007-08-04 12:09:58 +00:00
2007-07-23 00:15:18 +00:00
/// <summary>
2007-09-23 12:37:07 +00:00
/// Enters the game loop of GameWindow, updating and rendering at the maximum possible frequency.
2007-07-23 00:15:18 +00:00
/// </summary>
2007-09-23 12:37:07 +00:00
/// <see cref="public virtual void Run(float update_frequency, float render_frequency)"/>
public void Run ( )
{
Run ( 0.0 , 0.0 ) ;
}
/// <summary>
/// Runs the default game loop on GameWindow at the specified update frequency, maintaining the
/// maximum possible render frequency.
/// </summary>
/// <see cref="public virtual void Run(double updateFrequency, double renderFrequency)"/>
public void Run ( double updateFrequency )
{
Run ( updateFrequency , 0.0 ) ;
}
/// <summary>
/// Runs the default game loop on GameWindow at the specified update and render frequency.
/// </summary>
/// <param name="updateFrequency">If greater than zero, indicates how many times UpdateFrame will be called per second. If less than or equal to zero, UpdateFrame is raised at maximum possible frequency.</param>
/// <param name="renderFrequency">If greater than zero, indicates how many times RenderFrame will be called per second. If less than or equal to zero, RenderFrame is raised at maximum possible frequency.</param>
2007-07-23 00:15:18 +00:00
/// <remarks>
/// <para>
2007-09-23 12:37:07 +00:00
/// A default game loop consists of three parts: Event processing, frame updating and a frame rendering.
/// This function will try to maintain the requested updateFrequency at all costs, dropping the renderFrequency if
/// there is not enough CPU time.
/// </para>
/// <para>
/// It is recommended that you specify a target for update- and renderFrequency.
/// Doing so, will yield unused CPU time to other processes, dropping power consumption
/// and maximizing batter life. If either frequency is left unspecified, the GameWindow
/// will consume all available CPU time (only useful for benchmarks and stress tests).
2007-07-23 00:15:18 +00:00
/// </para>
/// <para>
/// Override this function if you want to change the behaviour of the
/// default game loop. If you override this function, you must place
2007-08-04 12:09:58 +00:00
/// a call to the ProcessEvents function, to ensure window will respond
2007-07-23 00:15:18 +00:00
/// to Operating System events.
/// </para>
/// </remarks>
2007-09-23 12:37:07 +00:00
public virtual void Run ( double updateFrequency , double renderFrequency )
2007-07-23 00:15:18 +00:00
{
2007-08-06 11:22:18 +00:00
this . OnLoad ( EventArgs . Empty ) ;
2007-09-26 23:13:57 +00:00
// Setup timer
2007-09-23 12:09:42 +00:00
Stopwatch watch = new Stopwatch ( ) ;
2007-09-22 22:01:43 +00:00
UpdateFrameEventArgs updateArgs = new UpdateFrameEventArgs ( ) ;
2007-09-23 12:09:42 +00:00
RenderFrameEventArgs renderArgs = new RenderFrameEventArgs ( ) ;
2007-09-26 23:13:57 +00:00
// Setup update and render rates. If updateFrequency or renderFrequency <= 0.0, use full throttle for that frequency.
2007-09-23 12:09:42 +00:00
double update_target = 0.0 , render_target = 0.0 , next_update = 0.0 , next_render = 0.0 ;
double time , total_time ;
2007-09-23 12:39:15 +00:00
if ( updateFrequency > 0.0 )
2007-09-23 12:09:42 +00:00
{
2007-09-23 12:39:15 +00:00
next_update = update_target = 1.0 / updateFrequency ;
2007-09-23 12:09:42 +00:00
}
2007-09-23 12:39:15 +00:00
if ( renderFrequency > 0.0 )
2007-09-23 12:09:42 +00:00
{
2007-09-23 12:39:15 +00:00
next_render = render_target = 1.0 / renderFrequency ;
2007-09-23 12:09:42 +00:00
}
2007-09-22 22:01:43 +00:00
2007-09-26 23:13:57 +00:00
// Enter main loop:
// (1) Update total frame time (capped at 0.1 sec)
// (2) Process events and update event_time
// (3) Raise UpdateFrame event(s) and update update_time.
// If there is enough CPU time, update and render events will be 1 on 1.
// If there is not enough time, render events will be dropped in order to match the requested updateFrequency.
// If the requested updateFrequency can't be matched, processing will slow down.
// (4) Raise RenderFrame event and update render_time.
// (5) If there is any CPU time left, and we are not running full-throttle, Sleep() to lower CPU usage.
Debug . Print ( "Entering main loop." ) ;
2007-08-10 16:55:24 +00:00
while ( this . Exists & & ! IsExiting )
2007-07-23 00:15:18 +00:00
{
2007-09-26 23:13:57 +00:00
// Update total frame time.
2007-09-23 12:09:42 +00:00
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 ( ) ;
2007-09-26 23:13:57 +00:00
// Process events and update event_time
2007-09-23 12:09:42 +00:00
time = watch . Elapsed . TotalSeconds ;
2007-07-23 00:15:18 +00:00
this . ProcessEvents ( ) ;
2007-09-23 12:09:42 +00:00
eventTime = watch . Elapsed . TotalSeconds - time ;
2007-08-06 11:22:18 +00:00
if ( ! IsExiting )
{
2007-09-26 23:13:57 +00:00
// Raise UpdateFrame event(s) and update update_time.
2007-09-23 12:09:42 +00:00
time = watch . Elapsed . TotalSeconds ;
next_update - = ( total_time + time ) ;
while ( next_update < = 0.0 )
{
updateArgs . Time + = watch . Elapsed . TotalSeconds ;
2007-09-26 11:50:44 +00:00
this . OnUpdateFrameInternal ( updateArgs ) ;
2007-09-23 12:09:42 +00:00
if ( update_target = = 0.0 )
break ;
next_update + = update_target ;
}
updateTime = watch . Elapsed . TotalSeconds - time ;
2007-09-26 23:13:57 +00:00
// Raise RenderFrame event and update render_time.
2007-09-23 12:09:42 +00:00
time = watch . Elapsed . TotalSeconds ;
next_render - = ( total_time + time ) ;
if ( next_render < = 0.0 )
{
renderArgs . Time + = time ;
2007-09-26 11:50:44 +00:00
this . OnRenderFrameInternal ( renderArgs ) ;
2007-09-23 12:09:42 +00:00
next_render + = render_target ;
}
renderTime = watch . Elapsed . TotalSeconds - time ;
2007-09-26 23:13:57 +00:00
// If there is any CPU time left, and we are not running full-throttle, Sleep() to lower CPU usage.
2007-09-23 12:09:42 +00:00
if ( renderTime < render_target & & updateTime < update_target )
{
Thread . Sleep ( ( int ) ( 1000.0 * System . Math . Min (
render_target - renderTime , update_target - updateTime ) ) ) ;
}
2007-08-06 11:22:18 +00:00
}
2007-07-23 00:15:18 +00:00
}
2007-08-06 09:22:04 +00:00
2007-09-26 23:13:57 +00:00
OnUnloadInternal ( EventArgs . Empty ) ;
2007-08-10 16:55:24 +00:00
if ( this . Exists )
2007-08-06 09:22:04 +00:00
{
2007-08-10 16:55:24 +00:00
glWindow . DestroyWindow ( ) ;
while ( this . Exists )
2007-08-06 11:22:18 +00:00
{
this . ProcessEvents ( ) ;
}
2007-08-06 09:22:04 +00:00
}
2007-07-23 00:15:18 +00:00
}
2007-08-04 12:09:58 +00:00
#endregion
#region public void ProcessEvents ( )
/// <summary>
/// Processes operating system events until the GameWindow becomes idle.
/// </summary>
/// <remarks>
/// When overriding the default GameWindow game loop (provided by the Run() function)
/// you should call ProcessEvents() to ensure that your GameWindow responds to
/// operating system events.
/// <para>
/// Once ProcessEvents() returns, it is time to call update and render the next frame.
/// </para>
/// </remarks>
2007-07-23 00:15:18 +00:00
public void ProcessEvents ( )
{
2007-09-26 11:50:44 +00:00
if ( ! isExiting )
InputDriver . Poll ( ) ;
2007-07-23 00:15:18 +00:00
glWindow . ProcessEvents ( ) ;
}
2007-08-04 12:09:58 +00:00
#endregion
2007-09-26 11:50:44 +00:00
#region OnRenderFrame ( RenderFrameEventArgs e )
2007-08-04 12:09:58 +00:00
/// <summary>
2007-09-26 11:50:44 +00:00
/// Raises the RenderFrame event, and calls the public function.
2007-08-04 12:09:58 +00:00
/// </summary>
2007-09-26 11:50:44 +00:00
/// <param name="e"></param>
private void OnRenderFrameInternal ( RenderFrameEventArgs e )
2007-07-23 00:15:18 +00:00
{
2007-08-10 16:55:24 +00:00
if ( ! this . Exists & & ! this . IsExiting )
2007-08-04 12:09:58 +00:00
{
Debug . Print ( "WARNING: RenderFrame event raised, without a valid render window. This may indicate a programming error. Creating render window." ) ;
mode = new DisplayMode ( 640 , 480 ) ;
this . CreateWindow ( mode ) ;
}
if ( RenderFrame ! = null )
2007-08-06 11:22:18 +00:00
RenderFrame ( this , e ) ;
2007-09-26 11:50:44 +00:00
// Call the user's override.
OnRenderFrame ( e ) ;
}
/// <summary>
/// Override in derived classes to render a frame.
/// </summary>
/// <param name="e">Contains information necessary for frame rendering.</param>
/// <remarks>
/// The base implementation (base.OnRenderFrame) is empty, there is no need to call it.
/// </remarks>
public virtual void OnRenderFrame ( RenderFrameEventArgs e )
{
2007-07-23 00:15:18 +00:00
}
2007-08-06 11:22:18 +00:00
/// <summary>
/// Occurs when it is time to render the next frame.
/// </summary>
public event RenderFrameEvent RenderFrame ;
2007-08-04 12:09:58 +00:00
#endregion
2007-09-26 11:50:44 +00:00
#region OnUpdateFrame ( UpdateFrameEventArgs e )
2007-08-04 12:09:58 +00:00
2007-09-26 11:50:44 +00:00
private void OnUpdateFrameInternal ( UpdateFrameEventArgs e )
2007-07-23 00:15:18 +00:00
{
2007-08-10 16:55:24 +00:00
if ( ! this . Exists & & ! this . IsExiting )
2007-08-04 12:09:58 +00:00
{
Debug . Print ( "WARNING: UpdateFrame event raised, without a valid render window. This may indicate a programming error. Creating render window." ) ;
mode = new DisplayMode ( 640 , 480 ) ;
this . CreateWindow ( mode ) ;
}
2007-09-26 11:50:44 +00:00
if ( MustResize )
{
resizeEventArgs . Width = glWindow . Width ;
resizeEventArgs . Height = glWindow . Height ;
OnResizeInternal ( resizeEventArgs ) ;
}
2007-08-04 12:09:58 +00:00
if ( UpdateFrame ! = null )
2007-09-26 11:50:44 +00:00
{
2007-08-06 11:22:18 +00:00
UpdateFrame ( this , e ) ;
2007-09-26 11:50:44 +00:00
}
OnUpdateFrame ( e ) ;
}
/// <summary>
/// Override in derived classes to update a frame.
/// </summary>
/// <param name="e">Contains information necessary for frame updating.</param>
/// <remarks>
/// The base implementation (base.OnUpdateFrame) is empty, there is no need to call it.
/// </remarks>
public virtual void OnUpdateFrame ( UpdateFrameEventArgs e )
{
2007-07-23 00:15:18 +00:00
}
2007-08-04 12:09:58 +00:00
/// <summary>
/// Occurs when it is time to update the next frame.
/// </summary>
public event UpdateFrameEvent UpdateFrame ;
2007-08-06 11:22:18 +00:00
#endregion
2007-09-26 11:50:44 +00:00
#region OnLoad ( EventArgs e )
2007-08-06 11:22:18 +00:00
2007-08-04 12:09:58 +00:00
/// <summary>
2007-09-26 11:50:44 +00:00
/// Occurs after establishing an OpenGL context, but before entering the main loop.
2007-08-04 12:09:58 +00:00
/// </summary>
2007-09-26 11:50:44 +00:00
public event LoadEvent Load ;
/// <summary>
/// Raises the Load event, and calls the user's OnLoad override.
/// </summary>
/// <param name="e"></param>
private void OnLoadInternal ( EventArgs e )
2007-08-06 11:22:18 +00:00
{
2007-09-26 23:13:57 +00:00
Debug . WriteLine ( String . Format ( "OpenGL driver information: {0}, {1}, {2}" ,
GL . GetString ( GL . Enums . StringName . RENDERER ) ,
GL . GetString ( GL . Enums . StringName . VENDOR ) ,
GL . GetString ( GL . Enums . StringName . VERSION ) ) ) ;
2007-08-06 11:22:18 +00:00
if ( this . Load ! = null )
{
this . Load ( this , e ) ;
}
2007-09-26 11:50:44 +00:00
OnLoad ( e ) ;
2007-08-06 11:22:18 +00:00
}
/// <summary>
2007-09-26 11:50:44 +00:00
/// Occurs after establishing an OpenGL context, but before entering the main loop.
/// Override to load resources that should be maintained for the lifetime of the application.
2007-08-06 11:22:18 +00:00
/// </summary>
2007-09-26 11:50:44 +00:00
/// <param name="e">Not used.</param>
public virtual void OnLoad ( EventArgs e )
{
}
2007-08-06 11:22:18 +00:00
#endregion
2007-07-23 00:15:18 +00:00
2007-09-26 23:13:57 +00:00
#region OnUnload ( EventArgs e )
/// <summary>
/// Occurs after after calling GameWindow.Exit, but before destroying the OpenGL context.
/// </summary>
public event UnloadEvent Unload ;
/// <summary>
/// Raises the Unload event, and calls the user's OnUnload override.
/// </summary>
/// <param name="e"></param>
private void OnUnloadInternal ( EventArgs e )
{
if ( this . Unload ! = null )
{
this . Unload ( this , e ) ;
}
OnUnload ( e ) ;
}
/// <summary>
/// Occurs after after calling GameWindow.Exit, but before destroying the OpenGL context.
/// Override to unload application resources.
/// </summary>
/// <param name="e">Not used.</param>
public virtual void OnUnload ( EventArgs e )
{
}
#endregion
2007-09-23 12:37:07 +00:00
#region public void SwapBuffers ( )
/// <summary>
/// Swaps the front and back buffer, presenting the rendered scene to the user.
/// Only useful in double- or triple-buffered formats.
/// </summary>
/// <remarks>Calling this function is equivalent to calling Context.SwapBuffers()</remarks>
public void SwapBuffers ( )
{
Context . SwapBuffers ( ) ;
}
#endregion
2007-08-06 09:22:04 +00:00
#region public bool IsExiting
/// <summary>
/// Gets a value indicating whether the shutdown sequence has been initiated
/// for this window, by calling GameWindow.Exit() or hitting the 'close' button.
/// If this property is true, it is no longer safe to use any OpenTK.Input or
/// OpenTK.OpenGL functions or properties.
/// </summary>
public bool IsExiting
{
get { return isExiting ; }
}
#endregion
2007-09-26 11:50:44 +00:00
#region public Keyboard Keyboard
/// <summary>
/// Gets the primary Keyboard device, or null if no Keyboard exists.
/// </summary>
public KeyboardDevice Keyboard
{
get
{
if ( InputDriver . Keyboard . Count > 0 )
return InputDriver . Keyboard [ 0 ] ;
else
return null ;
}
}
#endregion
#region public Mouse Mouse
2007-08-06 09:22:04 +00:00
/// <summary>
2007-09-26 11:50:44 +00:00
/// Gets the primary Mouse device, or null if no Mouse exists.
2007-08-06 09:22:04 +00:00
/// </summary>
2007-09-26 11:50:44 +00:00
public MouseDevice Mouse
2007-08-06 09:22:04 +00:00
{
get
{
2007-09-26 11:50:44 +00:00
if ( InputDriver . Mouse . Count > 0 )
return InputDriver . Mouse [ 0 ] ;
else
return null ;
2007-08-06 09:22:04 +00:00
}
}
#endregion
2007-07-23 00:15:18 +00:00
#endregion
#region - - - IResizable Members - - -
#region public int Width , Height
2007-08-21 12:04:01 +00:00
/// <summary>
/// Gets or sets the Width of the GameWindow's rendering area, in pixels.
/// </summary>
2007-07-23 00:15:18 +00:00
public int Width
{
2007-09-26 11:50:44 +00:00
get { return width ; }
2007-07-23 00:15:18 +00:00
set
{
if ( value = = this . Width )
{
return ;
}
else if ( value > 0 )
{
glWindow . Width = value ;
}
else
{
throw new ArgumentOutOfRangeException (
"Width" ,
value ,
"Width must be greater than 0"
) ;
}
}
}
2007-08-21 12:04:01 +00:00
/// <summary>
/// Gets or sets the Height of the GameWindow's rendering area, in pixels.
/// </summary>
2007-07-23 00:15:18 +00:00
public int Height
{
2007-09-26 11:50:44 +00:00
get { return height ; }
2007-07-23 00:15:18 +00:00
set
{
if ( value = = this . Height )
{
return ;
}
else if ( value > 0 )
{
glWindow . Height = value ;
}
else
{
throw new ArgumentOutOfRangeException (
"Height" ,
value ,
"Height must be greater than 0"
) ;
}
}
}
#endregion
#region public event ResizeEvent Resize ;
2007-09-26 11:50:44 +00:00
/// <summary>
/// Occurs when the GameWindow is resized. Derived classes should override the OnResize method for better performance.
/// </summary>
2007-07-23 00:15:18 +00:00
public event ResizeEvent Resize ;
/// <summary>
/// Raises the Resize event.
/// </summary>
2007-09-26 11:50:44 +00:00
/// <param name="e">Contains information about the Resize event.</param>
private void OnResizeInternal ( ResizeEventArgs e )
2007-07-23 00:15:18 +00:00
{
2007-08-06 11:22:18 +00:00
Debug . Print ( "Firing GameWindow.Resize event: {0}." , e . ToString ( ) ) ;
2007-09-26 11:50:44 +00:00
this . width = e . Width ;
this . height = e . Height ;
2007-07-23 00:15:18 +00:00
if ( this . Resize ! = null )
this . Resize ( this , e ) ;
2007-09-26 11:50:44 +00:00
OnResize ( e ) ;
}
/// <summary>
/// Override in derived classes to respond to the Resize events.
/// </summary>
/// <param name="e">Contains information about the Resize event.</param>
protected virtual void OnResize ( ResizeEventArgs e )
{
2007-07-23 00:15:18 +00:00
}
#endregion
2007-08-21 12:04:01 +00:00
/ *
/// <summary>
/// Gets the Top coordinate of the GameWindow's rendering area, in pixel coordinates relative to the GameWindow's top left point.
/// </summary>
public int Top
{
get { return glWindow . Top ; }
}
/// <summary>
/// /// Gets the Bottom coordinate of the GameWindow's rendering area, in pixel coordinates relative to the GameWindow's top left point.
/// </summary>
public int Bottom
{
get { return glWindow . Bottom ; }
}
2007-07-23 00:15:18 +00:00
2007-08-21 12:04:01 +00:00
/// <summary>
/// Gets the Left coordinate of the GameWindow's rendering area, in pixel coordinates relative to the GameWindow's top left point.
/// </summary>
public int Left
{
get { return glWindow . Left ; }
}
/// <summary>
/// Gets the Right coordinate of the GameWindow's rendering area, in pixel coordinates relative to the GameWindow's top left point.
/// </summary>
public int Right
{
get { return glWindow . Right ; }
}
* /
2007-07-23 00:15:18 +00:00
#endregion
#region - - - IDisposable Members - - -
2007-09-26 23:13:57 +00:00
/// <summary>
/// Not used yet.
/// </summary>
private void DisposeInternal ( )
{
Dispose ( ) ; // User overridable Dispose method.
}
2007-09-25 16:45:12 +00:00
/// <summary>
/// Disposes of the GameWindow, releasing all resources consumed by it.
/// </summary>
public virtual void Dispose ( )
2007-07-23 00:15:18 +00:00
{
2007-09-26 23:13:57 +00:00
Dispose ( true ) ; // Real Dispose method.
2007-09-25 16:45:12 +00:00
GC . SuppressFinalize ( this ) ;
2007-08-06 09:22:04 +00:00
}
private void Dispose ( bool manual )
{
if ( ! disposed )
{
// Is this safe? Maybe 'Debug' has been disposed, too...
2007-09-02 00:13:52 +00:00
//Debug.Print("{0} disposing GameWindow.", manual ? "Manually" : "Automatically");
2007-08-06 09:22:04 +00:00
if ( manual )
{
if ( glWindow ! = null )
{
glWindow . Dispose ( ) ;
glWindow = null ;
}
}
disposed = true ;
}
}
2007-09-25 16:45:12 +00:00
~ GameWindow ( )
{
this . Dispose ( false ) ;
}
2007-07-23 00:15:18 +00:00
#endregion
}
2007-09-22 22:01:43 +00:00
public class UpdateFrameEventArgs : EventArgs
{
2007-09-23 12:09:42 +00:00
private double time ;
/// <summary>
/// Gets the Time elapsed between frame updates, in seconds.
/// </summary>
public double Time
{
get { return time ; }
internal set { time = value ; }
}
}
public class RenderFrameEventArgs : EventArgs
{
private double time ;
2007-09-22 22:01:43 +00:00
/// <summary>
/// Gets the Time elapsed between frame updates, in seconds.
/// </summary>
2007-09-23 12:09:42 +00:00
public double Time
2007-09-22 22:01:43 +00:00
{
get { return time ; }
internal set { time = value ; }
}
}
2007-07-23 00:15:18 +00:00
}