/SharpGL/Core/SharpGL.WPF/OpenGLControl.xaml.cs

# · C# · 335 lines · 180 code · 44 blank · 111 comment · 14 complexity · bf3ef30e7144949a7c4d9f377e0dab8f MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14. using System.Windows.Threading;
  15. using System.ComponentModel;
  16. using System.Windows.Interop;
  17. using System.Diagnostics;
  18. using SharpGL.SceneGraph;
  19. namespace SharpGL.WPF
  20. {
  21. /// <summary>
  22. /// Interaction logic for OpenGLControl.xaml
  23. /// </summary>
  24. public partial class OpenGLControl : UserControl
  25. {
  26. /// <summary>
  27. /// Initializes a new instance of the <see cref="OpenGLControl"/> class.
  28. /// </summary>
  29. public OpenGLControl()
  30. {
  31. InitializeComponent();
  32. SizeChanged += new SizeChangedEventHandler(OpenGLControl_SizeChanged);
  33. }
  34. /// <summary>
  35. /// Handles the SizeChanged event of the OpenGLControl control.
  36. /// </summary>
  37. /// <param name="sender">The source of the event.</param>
  38. /// <param name="e">The <see cref="System.Windows.SizeChangedEventArgs"/> instance containing the event data.</param>
  39. void OpenGLControl_SizeChanged(object sender, SizeChangedEventArgs e)
  40. {
  41. // Lock on OpenGL.
  42. lock (gl)
  43. {
  44. int width = (int)e.NewSize.Width;
  45. int height = (int)e.NewSize.Height;
  46. gl.SetDimensions(width, height);
  47. // Set the viewport.
  48. gl.Viewport(0, 0, width, height);
  49. // If we have a project handler, call it...
  50. if (width != -1 && height != -1)
  51. {
  52. var handler = Resized;
  53. if (handler != null)
  54. handler(this, eventArgsFast);
  55. else
  56. {
  57. // Otherwise we do our own projection.
  58. gl.MatrixMode(OpenGL.GL_PROJECTION);
  59. gl.LoadIdentity();
  60. // Calculate The Aspect Ratio Of The Window
  61. gl.Perspective(45.0f, (float)width / (float)height, 0.1f, 100.0f);
  62. gl.MatrixMode(OpenGL.GL_MODELVIEW);
  63. gl.LoadIdentity();
  64. }
  65. }
  66. }
  67. }
  68. /// <summary>
  69. /// When overridden in a derived class, is invoked whenever application code or
  70. /// internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate"/>.
  71. /// </summary>
  72. public override void OnApplyTemplate()
  73. {
  74. // Call the base.
  75. base.OnApplyTemplate();
  76. // Lock on OpenGL.
  77. lock (gl)
  78. {
  79. // Create OpenGL.
  80. gl.Create(RenderContextType, 1, 1, 32, null);
  81. }
  82. // Create our fast event args.
  83. eventArgsFast = new OpenGLEventArgs(gl);
  84. // Set the most basic OpenGL styles.
  85. gl.ShadeModel(OpenGL.GL_SMOOTH);
  86. gl.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  87. gl.ClearDepth(1.0f);
  88. gl.Enable(OpenGL.GL_DEPTH_TEST);
  89. gl.DepthFunc(OpenGL.GL_LEQUAL);
  90. gl.Hint(OpenGL.GL_PERSPECTIVE_CORRECTION_HINT, OpenGL.GL_NICEST);
  91. // Fire the OpenGL initialised event.
  92. var handler = OpenGLInitialized;
  93. if (handler != null)
  94. handler(this, eventArgsFast);
  95. // DispatcherTimer setup
  96. timer = new DispatcherTimer();
  97. timer.Tick += new EventHandler(timer_Tick);
  98. timer.Interval = new TimeSpan(0, 0, 0, 0, (int)(1000.0 / FrameRate));
  99. timer.Start();
  100. }
  101. /// <summary>
  102. /// Handles the Tick event of the timer control.
  103. /// </summary>
  104. /// <param name="sender">The source of the event.</param>
  105. /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  106. void timer_Tick(object sender, EventArgs e)
  107. {
  108. // Lock on OpenGL.
  109. lock (gl)
  110. {
  111. // Start the stopwatch so that we can time the rendering.
  112. stopwatch.Restart();
  113. // Make GL current.
  114. gl.MakeCurrent();
  115. // If there is a draw handler, then call it.
  116. var handler = OpenGLDraw;
  117. if (handler != null)
  118. handler(this, eventArgsFast);
  119. else
  120. gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT);
  121. // Draw the FPS.
  122. if (DrawFPS)
  123. {
  124. gl.DrawText(5, 5, 1.0f, 0.0f, 0.0f, "Courier New", 12.0f, string.Format("Draw Time: {0:0.0000} ms ~ {1:0.0} FPS", frameTime, 1000.0 / frameTime));
  125. gl.Flush();
  126. }
  127. // Render.
  128. gl.Blit(IntPtr.Zero);
  129. switch (RenderContextType)
  130. {
  131. case RenderContextType.DIBSection:
  132. {
  133. RenderContextProviders.DIBSectionRenderContextProvider provider = gl.RenderContextProvider as RenderContextProviders.DIBSectionRenderContextProvider;
  134. // TODO: We have to remove the alpha channel - for some reason it comes out as 0.0
  135. // meaning the drawing comes out transparent.
  136. FormatConvertedBitmap newFormatedBitmapSource = new FormatConvertedBitmap();
  137. newFormatedBitmapSource.BeginInit();
  138. newFormatedBitmapSource.Source = BitmapConversion.HBitmapToBitmapSource(provider.DIBSection.HBitmap);
  139. newFormatedBitmapSource.DestinationFormat = PixelFormats.Rgb24;
  140. newFormatedBitmapSource.EndInit();
  141. // Copy the pixels over.
  142. image.Source = newFormatedBitmapSource;
  143. }
  144. break;
  145. case RenderContextType.NativeWindow:
  146. break;
  147. case RenderContextType.HiddenWindow:
  148. break;
  149. case RenderContextType.FBO:
  150. {
  151. RenderContextProviders.FBORenderContextProvider provider = gl.RenderContextProvider as RenderContextProviders.FBORenderContextProvider;
  152. // TODO: We have to remove the alpha channel - for some reason it comes out as 0.0
  153. // meaning the drawing comes out transparent.
  154. FormatConvertedBitmap newFormatedBitmapSource = new FormatConvertedBitmap();
  155. newFormatedBitmapSource.BeginInit();
  156. newFormatedBitmapSource.Source = BitmapConversion.HBitmapToBitmapSource(provider.InternalDIBSection.HBitmap);
  157. newFormatedBitmapSource.DestinationFormat = PixelFormats.Rgb24;
  158. newFormatedBitmapSource.EndInit();
  159. // Copy the pixels over.
  160. image.Source = newFormatedBitmapSource;
  161. }
  162. break;
  163. default:
  164. break;
  165. }
  166. // Stop the stopwatch.
  167. stopwatch.Stop();
  168. // Store the frame time.
  169. frameTime = stopwatch.Elapsed.TotalMilliseconds;
  170. }
  171. }
  172. /// <summary>
  173. /// Called when the frame rate is changed.
  174. /// </summary>
  175. /// <param name="o">The object.</param>
  176. /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
  177. private static void OnFrameRateChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
  178. {
  179. // Get the control.
  180. OpenGLControl me = o as OpenGLControl;
  181. // If we have the timer, set the time.
  182. if (me.timer != null)
  183. {
  184. // Stop the timer.
  185. me.timer.Stop();
  186. // Set the timer.
  187. me.timer.Interval = new TimeSpan(0, 0, 0, 0, (int)(1000f / me.FrameRate));
  188. // Start the timer.
  189. me.timer.Start();
  190. }
  191. }
  192. /// <summary>
  193. /// A single event args for all our needs.
  194. /// </summary>
  195. private OpenGLEventArgs eventArgsFast;
  196. /// <summary>
  197. /// The OpenGL instance.
  198. /// </summary>
  199. private OpenGL gl = new OpenGL();
  200. /// <summary>
  201. /// The dispatcher timer.
  202. /// </summary>
  203. DispatcherTimer timer = null;
  204. /// <summary>
  205. /// A stopwatch used for timing rendering.
  206. /// </summary>
  207. protected Stopwatch stopwatch = new Stopwatch();
  208. /// <summary>
  209. /// The last frame time in milliseconds.
  210. /// </summary>
  211. protected double frameTime = 0;
  212. /// <summary>
  213. /// Occurs when OpenGL should be initialised.
  214. /// </summary>
  215. [Description("Called when OpenGL has been initialized."), Category("SharpGL")]
  216. public event OpenGLEventHandler OpenGLInitialized;
  217. /// <summary>
  218. /// Occurs when OpenGL drawing should occur.
  219. /// </summary>
  220. [Description("Called whenever OpenGL drawing can should occur."), Category("SharpGL")]
  221. public event OpenGLEventHandler OpenGLDraw;
  222. /// <summary>
  223. /// Occurs when the control is resized. This can be used to perform custom projections.
  224. /// </summary>
  225. [Description("Called when the control is resized - you can use this to do custom viewport projections."), Category("SharpGL")]
  226. public event OpenGLEventHandler Resized;
  227. /// <summary>
  228. /// The frame rate dependency property.
  229. /// </summary>
  230. private static readonly DependencyProperty FrameRateProperty =
  231. DependencyProperty.Register("FrameRate", typeof(double), typeof(OpenGLControl),
  232. new PropertyMetadata(28.0, new PropertyChangedCallback(OnFrameRateChanged)));
  233. /// <summary>
  234. /// Gets or sets the frame rate.
  235. /// </summary>
  236. /// <value>The frame rate.</value>
  237. public double FrameRate
  238. {
  239. get { return (double)GetValue(FrameRateProperty); }
  240. set { SetValue(FrameRateProperty, value); }
  241. }
  242. /// <summary>
  243. /// The render context type property.
  244. /// </summary>
  245. private static readonly DependencyProperty RenderContextTypeProperty =
  246. DependencyProperty.Register("RenderContextType", typeof(RenderContextType), typeof(OpenGLControl),
  247. new PropertyMetadata(RenderContextType.DIBSection, new PropertyChangedCallback(OnRenderContextTypeChanged)));
  248. /// <summary>
  249. /// Gets or sets the type of the render context.
  250. /// </summary>
  251. /// <value>The type of the render context.</value>
  252. public RenderContextType RenderContextType
  253. {
  254. get { return (RenderContextType)GetValue(RenderContextTypeProperty); }
  255. set { SetValue(RenderContextTypeProperty, value); }
  256. }
  257. /// <summary>
  258. /// Called when [render context type changed].
  259. /// </summary>
  260. /// <param name="o">The o.</param>
  261. /// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
  262. private static void OnRenderContextTypeChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
  263. {
  264. OpenGLControl me = o as OpenGLControl;
  265. }
  266. /// <summary>
  267. /// The DrawFPS property.
  268. /// </summary>
  269. private static readonly DependencyProperty DrawFPSProperty =
  270. DependencyProperty.Register("DrawFPS", typeof(bool), typeof(OpenGLControl),
  271. new PropertyMetadata(false, null));
  272. /// <summary>
  273. /// Gets or sets a value indicating whether to draw FPS.
  274. /// </summary>
  275. /// <value>
  276. /// <c>true</c> if draw FPS; otherwise, <c>false</c>.
  277. /// </value>
  278. public bool DrawFPS
  279. {
  280. get { return (bool)GetValue(DrawFPSProperty); }
  281. set { SetValue(DrawFPSProperty, value); }
  282. }
  283. /// <summary>
  284. /// Gets the OpenGL instance.
  285. /// </summary>
  286. public OpenGL OpenGL
  287. {
  288. get { return gl; }
  289. }
  290. }
  291. }