00001 #region License
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #endregion
00027
00028 using System;
00029 using System.Collections.Generic;
00030 using System.ComponentModel;
00031 using System.Diagnostics;
00032 using System.Drawing;
00033 using System.Threading;
00034 using OpenTK.Graphics;
00035 using OpenTK.Input;
00036 using OpenTK.Platform;
00037
00038 namespace OpenTK
00039 {
00072 public class GameWindow : NativeWindow, IGameWindow, IDisposable
00073 {
00074 #region --- Fields ---
00075
00076 object exit_lock = new object();
00077
00078 IGraphicsContext glContext;
00079
00080 bool isExiting = false;
00081
00082 double update_period, render_period;
00083 double target_update_period, target_render_period;
00084
00085 double update_time, render_time;
00086 VSyncMode vsync;
00087
00088 Stopwatch update_watch = new Stopwatch(), render_watch = new Stopwatch();
00089 double next_render = 0.0, next_update = 0.0;
00090 FrameEventArgs update_args = new FrameEventArgs();
00091 FrameEventArgs render_args = new FrameEventArgs();
00092
00093 #endregion
00094
00095 #region --- Contructors ---
00096
00097 #region public GameWindow()
00098
00100 public GameWindow()
00101 : this(640, 480, GraphicsMode.Default, "OpenTK Game Window", 0, DisplayDevice.Default) { }
00102
00103 #endregion
00104
00105 #region public GameWindow(int width, int height)
00106
00110 public GameWindow(int width, int height)
00111 : this(width, height, GraphicsMode.Default, "OpenTK Game Window", 0, DisplayDevice.Default) { }
00112
00113 #endregion
00114
00115 #region public GameWindow(int width, int height, GraphicsMode mode)
00116
00121 public GameWindow(int width, int height, GraphicsMode mode)
00122 : this(width, height, mode, "OpenTK Game Window", 0, DisplayDevice.Default) { }
00123
00124 #endregion
00125
00126 #region public GameWindow(int width, int height, GraphicsMode mode, string title)
00127
00133 public GameWindow(int width, int height, GraphicsMode mode, string title)
00134 : this(width, height, mode, title, 0, DisplayDevice.Default) { }
00135
00136 #endregion
00137
00138 #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options)
00139
00146 public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options)
00147 : this(width, height, mode, title, options, DisplayDevice.Default) { }
00148
00149 #endregion
00150
00151 #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device)
00152
00160 public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device)
00161 : this(width, height, mode, title, options, device, 1, 0, GraphicsContextFlags.Default)
00162 { }
00163
00164 #endregion
00165
00166 #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device, int major, int minor, GraphicsContextFlags flags)
00167
00178 public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device,
00179 int major, int minor, GraphicsContextFlags flags)
00180 : this(width, height, mode, title, options, device, major, minor, flags, null)
00181 { }
00182
00183 #endregion
00184
00185 #region public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device, int major, int minor, GraphicsContextFlags flags, IGraphicsContext sharedContext)
00186
00198 public GameWindow(int width, int height, GraphicsMode mode, string title, GameWindowFlags options, DisplayDevice device,
00199 int major, int minor, GraphicsContextFlags flags, IGraphicsContext sharedContext)
00200 : base(width, height, title, options,
00201 mode == null ? GraphicsMode.Default : mode,
00202 device == null ? DisplayDevice.Default : device)
00203 {
00204 try
00205 {
00206 glContext = new GraphicsContext(mode == null ? GraphicsMode.Default : mode, WindowInfo, major, minor, flags);
00207 glContext.MakeCurrent(WindowInfo);
00208 (glContext as IGraphicsContextInternal).LoadAll();
00209
00210 VSync = VSyncMode.On;
00211
00212
00213 }
00214 catch (Exception e)
00215 {
00216 Debug.Print(e.ToString());
00217 base.Dispose();
00218 throw;
00219 }
00220 }
00221
00222 #endregion
00223
00224 #endregion
00225
00226 #region --- Public Members ---
00227
00228 #region Methods
00229
00230 #region Dispose
00231
00235 public override void Dispose()
00236 {
00237 try
00238 {
00239 Dispose(true);
00240 }
00241 finally
00242 {
00243 try
00244 {
00245 if (glContext != null)
00246 {
00247 glContext.Dispose();
00248 glContext = null;
00249 }
00250 }
00251 finally
00252 {
00253 base.Dispose();
00254 }
00255 }
00256 GC.SuppressFinalize(this);
00257 }
00258
00259 #endregion
00260
00261 #region Exit
00262
00270 public virtual void Exit()
00271 {
00272 Close();
00273 }
00274
00275 #endregion
00276
00277 #region MakeCurrent
00278
00282 public void MakeCurrent()
00283 {
00284 EnsureUndisposed();
00285 Context.MakeCurrent(WindowInfo);
00286 }
00287
00288 #endregion
00289
00290 #region OnClose
00291
00298 protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
00299 {
00300 base.OnClosing(e);
00301 if (!e.Cancel)
00302 {
00303 isExiting = true;
00304 OnUnloadInternal(EventArgs.Empty);
00305 }
00306 }
00307
00308
00309 #endregion
00310
00311 #region OnLoad
00312
00317 protected virtual void OnLoad(EventArgs e)
00318 {
00319 if (Load != null) Load(this, e);
00320 }
00321
00322 #endregion
00323
00324 #region OnUnload
00325
00330 protected virtual void OnUnload(EventArgs e)
00331 {
00332 if (Unload != null) Unload(this, e);
00333 }
00334
00335 #endregion
00336
00337 #region public void Run()
00338
00343 public void Run()
00344 {
00345 Run(0.0, 0.0);
00346 }
00347
00348 #endregion
00349
00350 #region public void Run(double updateFrequency)
00351
00356 public void Run(double updateRate)
00357 {
00358 Run(updateRate, 0.0);
00359 }
00360
00361 #endregion
00362
00363 #region public void Run(double updates_per_second, double frames_per_second)
00364
00377 public void Run(double updates_per_second, double frames_per_second)
00378 {
00379 EnsureUndisposed();
00380
00381 try
00382 {
00383 if (updates_per_second < 0.0 || updates_per_second > 200.0)
00384 throw new ArgumentOutOfRangeException("updates_per_second", updates_per_second,
00385 "Parameter should be inside the range [0.0, 200.0]");
00386 if (frames_per_second < 0.0 || frames_per_second > 200.0)
00387 throw new ArgumentOutOfRangeException("frames_per_second", frames_per_second,
00388 "Parameter should be inside the range [0.0, 200.0]");
00389
00390 TargetUpdateFrequency = updates_per_second;
00391 TargetRenderFrequency = frames_per_second;
00392
00393 Visible = true;
00394 OnLoadInternal(EventArgs.Empty);
00395 OnResize(EventArgs.Empty);
00396
00397
00398
00399
00400 Move += DispatchUpdateAndRenderFrame;
00401 Resize += DispatchUpdateAndRenderFrame;
00402
00403 Debug.Print("Entering main loop.");
00404 update_watch.Start();
00405 render_watch.Start();
00406 while (true)
00407 {
00408 ProcessEvents();
00409 if (Exists && !IsExiting)
00410 DispatchUpdateAndRenderFrame(this, EventArgs.Empty);
00411 else
00412 return;
00413 }
00414 }
00415 finally
00416 {
00417 Move -= DispatchUpdateAndRenderFrame;
00418 Resize -= DispatchUpdateAndRenderFrame;
00419
00420 if (Exists)
00421 {
00422
00423
00424
00425 }
00426 }
00427 }
00428
00429 void DispatchUpdateAndRenderFrame(object sender, EventArgs e)
00430 {
00431 RaiseUpdateFrame(update_watch, ref next_update, update_args);
00432 RaiseRenderFrame(render_watch, ref next_render, render_args);
00433 }
00434
00435 void RaiseUpdateFrame(Stopwatch update_watch, ref double next_update, FrameEventArgs update_args)
00436 {
00437 int num_updates = 0;
00438 double total_update_time = 0;
00439
00440
00441 double time = update_watch.Elapsed.TotalSeconds;
00442 if (time <= 0)
00443 return;
00444 if (time > 1.0)
00445 time = 1.0;
00446
00447
00448 while (next_update - time <= 0 && time > 0)
00449 {
00450 next_update -= time;
00451 update_args.Time = time;
00452 OnUpdateFrameInternal(update_args);
00453 time = update_time = update_watch.Elapsed.TotalSeconds - time;
00454
00455
00456
00457 update_watch.Reset();
00458 update_watch.Start();
00459
00460
00461
00462
00463
00464
00465 next_update += TargetUpdatePeriod;
00466 next_update = Math.Max(next_update, -1.0);
00467
00468 total_update_time += update_time;
00469
00470
00471
00472
00473 if (++num_updates >= 10 || TargetUpdateFrequency == 0.0)
00474 break;
00475 }
00476
00477
00478 if (num_updates > 0)
00479 {
00480 update_period = total_update_time / (double)num_updates;
00481 }
00482 }
00483
00484 void RaiseRenderFrame(Stopwatch render_watch, ref double next_render, FrameEventArgs render_args)
00485 {
00486
00487 double time = render_watch.Elapsed.TotalSeconds;
00488 if (time > 1.0)
00489 time = 1.0;
00490 if (time <= 0)
00491 return;
00492 double time_left = next_render - time;
00493
00494 if (time_left <= 0.0)
00495 {
00496
00497
00498 next_render = time_left + TargetRenderPeriod;
00499 if (next_render < -1.0)
00500 next_render = -1.0;
00501
00502 render_watch.Reset();
00503 render_watch.Start();
00504
00505 if (time > 0)
00506 {
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516 if (Context.IsCurrent && VSync == VSyncMode.Adaptive && TargetRenderPeriod != 0)
00517 {
00518
00519 if (RenderTime > 2.0 * TargetRenderPeriod)
00520 Context.VSync = false;
00521 else
00522 Context.VSync = true;
00523 }
00524
00525 render_period = render_args.Time = time;
00526 OnRenderFrameInternal(render_args);
00527 render_time = render_watch.Elapsed.TotalSeconds;
00528 }
00529 }
00530 }
00531
00532 #endregion
00533
00534 #region SwapBuffers
00535
00539 public void SwapBuffers()
00540 {
00541 EnsureUndisposed();
00542 this.Context.SwapBuffers();
00543 }
00544
00545 #endregion
00546
00547 #endregion
00548
00549 #region Properties
00550
00551 #region Context
00552
00556 public IGraphicsContext Context
00557 {
00558 get
00559 {
00560 EnsureUndisposed();
00561 return glContext;
00562 }
00563 }
00564
00565 #endregion
00566
00567 #region IsExiting
00568
00575 public bool IsExiting
00576 {
00577 get
00578 {
00579 EnsureUndisposed();
00580 return isExiting;
00581 }
00582 }
00583
00584 #endregion
00585
00586 #region Joysticks
00587
00591 public IList<JoystickDevice> Joysticks
00592 {
00593 get { return InputDriver.Joysticks; }
00594 }
00595
00596 #endregion
00597
00598 #region Keyboard
00599
00603 public KeyboardDevice Keyboard
00604 {
00605 get { return InputDriver.Keyboard.Count > 0 ? InputDriver.Keyboard[0] : null; }
00606 }
00607
00608 #endregion
00609
00610 #region Mouse
00611
00615 public MouseDevice Mouse
00616 {
00617 get { return InputDriver.Mouse.Count > 0 ? InputDriver.Mouse[0] : null; }
00618 }
00619
00620 #endregion
00621
00622 #region --- GameWindow Timing ---
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636 #region RenderFrequency
00637
00641 public double RenderFrequency
00642 {
00643 get
00644 {
00645 EnsureUndisposed();
00646 if (render_period == 0.0)
00647 return 1.0;
00648 return 1.0 / render_period;
00649 }
00650 }
00651
00652 #endregion
00653
00654 #region RenderPeriod
00655
00659 public double RenderPeriod
00660 {
00661 get
00662 {
00663 EnsureUndisposed();
00664 return render_period;
00665 }
00666 }
00667
00668 #endregion
00669
00670 #region RenderTime
00671
00675 public double RenderTime
00676 {
00677 get
00678 {
00679 EnsureUndisposed();
00680 return render_time;
00681 }
00682 protected set
00683 {
00684 EnsureUndisposed();
00685 render_time = value;
00686 }
00687 }
00688
00689 #endregion
00690
00691 #region TargetRenderFrequency
00692
00700 public double TargetRenderFrequency
00701 {
00702 get
00703 {
00704 EnsureUndisposed();
00705 if (TargetRenderPeriod == 0.0)
00706 return 0.0;
00707 return 1.0 / TargetRenderPeriod;
00708 }
00709 set
00710 {
00711 EnsureUndisposed();
00712 if (value < 1.0)
00713 {
00714 TargetRenderPeriod = 0.0;
00715 }
00716 else if (value <= 200.0)
00717 {
00718 TargetRenderPeriod = 1.0 / value;
00719 }
00720 else Debug.Print("Target render frequency clamped to 200.0Hz.");
00721 }
00722 }
00723
00724 #endregion
00725
00726 #region TargetRenderPeriod
00727
00735 public double TargetRenderPeriod
00736 {
00737 get
00738 {
00739 EnsureUndisposed();
00740 return target_render_period;
00741 }
00742 set
00743 {
00744 EnsureUndisposed();
00745 if (value <= 0.005)
00746 {
00747 target_render_period = 0.0;
00748 }
00749 else if (value <= 1.0)
00750 {
00751 target_render_period = value;
00752 }
00753 else Debug.Print("Target render period clamped to 1.0 seconds.");
00754 }
00755 }
00756
00757 #endregion
00758
00759 #region TargetUpdateFrequency
00760
00768 public double TargetUpdateFrequency
00769 {
00770 get
00771 {
00772 EnsureUndisposed();
00773 if (TargetUpdatePeriod == 0.0)
00774 return 0.0;
00775 return 1.0 / TargetUpdatePeriod;
00776 }
00777 set
00778 {
00779 EnsureUndisposed();
00780 if (value < 1.0)
00781 {
00782 TargetUpdatePeriod = 0.0;
00783 }
00784 else if (value <= 200.0)
00785 {
00786 TargetUpdatePeriod = 1.0 / value;
00787 }
00788 else Debug.Print("Target update frequency clamped to 200.0Hz.");
00789 }
00790 }
00791
00792 #endregion
00793
00794 #region TargetUpdatePeriod
00795
00803 public double TargetUpdatePeriod
00804 {
00805 get
00806 {
00807 EnsureUndisposed();
00808 return target_update_period;
00809 }
00810 set
00811 {
00812 EnsureUndisposed();
00813 if (value <= 0.005)
00814 {
00815 target_update_period = 0.0;
00816 }
00817 else if (value <= 1.0)
00818 {
00819 target_update_period = value;
00820 }
00821 else Debug.Print("Target update period clamped to 1.0 seconds.");
00822 }
00823 }
00824
00825 #endregion
00826
00827 #region UpdateFrequency
00828
00832 public double UpdateFrequency
00833 {
00834 get
00835 {
00836 EnsureUndisposed();
00837 if (update_period == 0.0)
00838 return 1.0;
00839 return 1.0 / update_period;
00840 }
00841 }
00842
00843 #endregion
00844
00845 #region UpdatePeriod
00846
00850 public double UpdatePeriod
00851 {
00852 get
00853 {
00854 EnsureUndisposed();
00855 return update_period;
00856 }
00857 }
00858
00859 #endregion
00860
00861 #region UpdateTime
00862
00866 public double UpdateTime
00867 {
00868 get
00869 {
00870 EnsureUndisposed();
00871 return update_time;
00872 }
00873 }
00874
00875 #endregion
00876
00877 #endregion
00878
00879 #region VSync
00880
00884 public VSyncMode VSync
00885 {
00886 get
00887 {
00888 EnsureUndisposed();
00889 GraphicsContext.Assert();
00890 return vsync;
00891 }
00892 set
00893 {
00894 EnsureUndisposed();
00895 GraphicsContext.Assert();
00896 Context.VSync = (vsync = value) != VSyncMode.Off;
00897 }
00898 }
00899
00900 #endregion
00901
00902 #region WindowState
00903
00907 public override WindowState WindowState
00908 {
00909 get
00910 {
00911 return base.WindowState;
00912 }
00913 set
00914 {
00915 base.WindowState = value;
00916 Debug.Print("Updating Context after setting WindowState to {0}", value);
00917
00918 if (Context != null)
00919 Context.Update(WindowInfo);
00920 }
00921 }
00922 #endregion
00923
00924 #endregion
00925
00926 #region Events
00927
00931 public event EventHandler<EventArgs> Load;
00932
00936 public event EventHandler<FrameEventArgs> RenderFrame;
00937
00941 public event EventHandler<EventArgs> Unload;
00942
00946 public event EventHandler<FrameEventArgs> UpdateFrame;
00947
00948 #endregion
00949
00950 #endregion
00951
00952 #region --- Protected Members ---
00953
00954 #region Dispose
00955
00960 protected virtual void Dispose(bool manual) { }
00961
00962 #endregion
00963
00964 #region OnRenderFrame
00965
00973 protected virtual void OnRenderFrame(FrameEventArgs e)
00974 {
00975 if (RenderFrame != null) RenderFrame(this, e);
00976 }
00977
00978 #endregion
00979
00980 #region OnUpdateFrame
00981
00989 protected virtual void OnUpdateFrame(FrameEventArgs e)
00990 {
00991 if (UpdateFrame != null) UpdateFrame(this, e);
00992 }
00993
00994 #endregion
00995
00996 #region OnWindowInfoChanged
00997
01002 protected virtual void OnWindowInfoChanged(EventArgs e) { }
01003
01004 #endregion
01005
01006 #region OnResize
01007
01008 protected override void OnResize(EventArgs e)
01009 {
01010 base.OnResize(e);
01011 glContext.Update(base.WindowInfo);
01012 }
01013
01014 #endregion
01015
01016 #endregion
01017
01018 #region --- Private Members ---
01019
01020 #region OnLoadInternal
01021
01022 private void OnLoadInternal(EventArgs e)
01023 {
01024 OnLoad(e);
01025 }
01026
01027 #endregion
01028
01029 #region OnRenderFrameInternal
01030
01031 private void OnRenderFrameInternal(FrameEventArgs e) { if (Exists && !isExiting) OnRenderFrame(e); }
01032
01033 #endregion
01034
01035 #region OnUnloadInternal
01036
01037 private void OnUnloadInternal(EventArgs e) { OnUnload(e); }
01038
01039 #endregion
01040
01041 #region OnUpdateFrameInternal
01042
01043 private void OnUpdateFrameInternal(FrameEventArgs e) { if (Exists && !isExiting) OnUpdateFrame(e); }
01044
01045 #endregion
01046
01047 #region OnWindowInfoChangedInternal
01048
01049 private void OnWindowInfoChangedInternal(EventArgs e)
01050 {
01051 glContext.MakeCurrent(WindowInfo);
01052
01053 OnWindowInfoChanged(e);
01054 }
01055
01056 #endregion
01057
01058 #endregion
01059 }
01060
01061 #region public enum VSyncMode
01062
01066 public enum VSyncMode
01067 {
01071 Off = 0,
01075 On,
01080 Adaptive,
01081 }
01082
01083 #endregion
01084 }