PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Graphics/OpenTK/OpenTKGeometry.cs

#
C# | 302 lines | 199 code | 25 blank | 78 comment | 30 complexity | 60c7a163576523e26b14a515d0b92877 MD5 | raw file
Possible License(s): Apache-2.0
  1. // Here you can disable rendering geometry (then nothing is visible on screen,
  2. // should be used together with DISABLE_SWAP_BUFFERS_FOR_PERFORMANCE_TESTING).
  3. // This tests CPU performance much closer without having to wait for GPU each
  4. // frame. This only makes a difference for very high fps (>5000 on fast PCs).
  5. // Note: Also useful to find native memory leaks related to geometry rendering!
  6. //#define DISABLE_GEOMETRY_RENDERING_FOR_PERFORMANCE_TESTING
  7. using System;
  8. using Delta.ContentSystem.Rendering;
  9. using Delta.Engine;
  10. using Delta.Graphics.BaseOpenGL;
  11. using Delta.Utilities;
  12. using Delta.Utilities.Helpers;
  13. using OpenTK.Graphics.OpenGL;
  14. namespace Delta.Graphics.OpenTK
  15. {
  16. /// <summary>
  17. /// OpenTK geometry implementation (with VAO if supported to improve
  18. /// performance). We use also VBO and IBO for this implementation. For
  19. /// details see http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=45
  20. /// </summary>
  21. public class OpenTKGeometry : BaseOpenGLGeometry
  22. {
  23. #region drawMode (Private)
  24. /// <summary>
  25. /// The cached draw mode of this geometry which is either Lines or Triangles.
  26. /// </summary>
  27. private readonly BeginMode drawMode;
  28. #endregion
  29. #region Constructor
  30. /// <summary>
  31. /// Create mesh from already created MeshData (for creating dynamic meshes,
  32. /// like for Lines, UI rendering or Effects).
  33. /// </summary>
  34. /// <param name="setMeshData">The set mesh data.</param>
  35. public OpenTKGeometry(GeometryData setMeshData)
  36. : base(setMeshData)
  37. {
  38. drawMode =
  39. Data.IsLineData
  40. ? BeginMode.Lines
  41. : BeginMode.Triangles;
  42. }
  43. #endregion
  44. #region Create
  45. /// <summary>
  46. /// Create native mesh data (vertex buffers, vertex array objects, etc.).
  47. /// Used to create the native vertex buffer, etc. when we have not created
  48. /// it yet or if the content gets updated by the content system.
  49. /// </summary>
  50. internal override void Create(BaseShader shader)
  51. {
  52. if (OpenTKGraphic.VaoSupport)
  53. {
  54. // Create and bind our VAO if we have not created it yet.
  55. if (nativeVertexArrayID == MathHelper.InvalidIndex)
  56. {
  57. // Create and setup vertex format
  58. GL.GenVertexArrays(1, out nativeVertexArrayID);
  59. }
  60. // Always bind it
  61. GL.BindVertexArray(nativeVertexArrayID);
  62. ErrorCode error = GL.GetError();
  63. if (error != ErrorCode.NoError)
  64. {
  65. Log.Warning(
  66. "There was an OpenGL error while creating the Vertex " +
  67. "Array Object: '" + error + "'. This usually happens when " +
  68. "something went wrong a little earlier, enable OpenTK debugging " +
  69. "and figure the problem out at the original place.");
  70. }
  71. }
  72. bool vertexDataChanged;
  73. bool indexDataChanged;
  74. CheckIfDataChanged(out vertexDataChanged, out indexDataChanged);
  75. #region VBO
  76. if (vertexDataChanged)
  77. {
  78. // Increase VBO if needed
  79. if (Data.MaxNumberOfVertices > currentMaxNumberOfVertices)
  80. {
  81. currentMaxNumberOfVertices = Data.MaxNumberOfVertices;
  82. int vertexSizeInBytes =
  83. Data.MaxNumberOfVertices * Data.Format.LengthInBytes;
  84. // Create or update VBO
  85. if (nativeVertexBufferID == MathHelper.InvalidIndex)
  86. {
  87. GL.GenBuffers(1, out nativeVertexBufferID);
  88. GL.BindBuffer(BufferTarget.ArrayBuffer, nativeVertexBufferID);
  89. // Reserve space for currentMaxNumberOfVertices
  90. GL.BufferData(BufferTarget.ArrayBuffer,
  91. new IntPtr(vertexSizeInBytes), IntPtr.Zero,
  92. Data.IsStatic
  93. ? BufferUsageHint.StaticDraw
  94. : BufferUsageHint.StreamDraw);
  95. }
  96. else
  97. {
  98. GL.BindBuffer(BufferTarget.ArrayBuffer, nativeVertexBufferID);
  99. GL.BufferData(BufferTarget.ArrayBuffer,
  100. new IntPtr(vertexSizeInBytes), IntPtr.Zero,
  101. BufferUsageHint.StreamDraw);
  102. }
  103. if (OpenTKGraphic.VaoSupport)
  104. {
  105. //Not helping at all, the issue is with SetupVertexFormat!
  106. //VaoSupport: return false;
  107. // Setup the vertex format for all geometry we will render.
  108. // Note: Needs to be done for every geometry, else pointers are
  109. // not correct! See
  110. // http://stackoverflow.com/questions/3665671/is-vertexattribpointer-needed-after-each-bindbuffer
  111. BaseOpenGLGraphic.SetupVertexFormat(shader as BaseOpenGLShader,
  112. Data.Format);
  113. }
  114. }
  115. else
  116. {
  117. // Just bind the buffer. Note: gDEBugger might report that this is
  118. // not needed, but it is not already set from last time causing
  119. // skipped geometry and other render errors, do not remove this!
  120. GL.BindBuffer(BufferTarget.ArrayBuffer, nativeVertexBufferID);
  121. }
  122. // changing geometries might be a lot more efficient!
  123. // This need to be re-thinked even of Engine side, we need to create
  124. // some kind of vertices atlas.
  125. // Update data in VBO
  126. byte[] dataBytes = Data.GetVerticesBytes();
  127. int dataSize =
  128. Data.VertexDataLengthInBytes * Data.NumberOfUsedVertices;
  129. GL.BufferSubData<byte>(BufferTarget.ArrayBuffer, IntPtr.Zero,
  130. (IntPtr)dataSize, dataBytes);
  131. }
  132. #endregion
  133. #region IBO
  134. if (Data.NumberOfUsedIndices > 0 &&
  135. indexDataChanged)
  136. {
  137. // recreate IBO if needed
  138. if (Data.Indices.Length > currentMaxNumberOfIndices)
  139. {
  140. currentMaxNumberOfIndices = Data.Indices.Length;
  141. // Create or update IBO
  142. if (nativeIndexBufferID == MathHelper.InvalidIndex)
  143. {
  144. GL.GenBuffers(1, out nativeIndexBufferID);
  145. GL.BindBuffer(BufferTarget.ElementArrayBuffer,
  146. nativeIndexBufferID);
  147. // Reserve space for currentMaxNumberOfIndices
  148. // 2 is the size of an ushort
  149. GL.BufferData(BufferTarget.ElementArrayBuffer,
  150. new IntPtr(currentMaxNumberOfIndices * 2), IntPtr.Zero,
  151. Data.IsStatic
  152. ? BufferUsageHint.StaticDraw
  153. : BufferUsageHint.StreamDraw);
  154. }
  155. else
  156. {
  157. GL.BindBuffer(BufferTarget.ElementArrayBuffer,
  158. nativeIndexBufferID);
  159. // 2 is the size of an ushort
  160. GL.BufferData(BufferTarget.ElementArrayBuffer,
  161. new IntPtr(currentMaxNumberOfIndices * 2), IntPtr.Zero,
  162. BufferUsageHint.StreamDraw);
  163. }
  164. }
  165. else
  166. {
  167. // Just bind the buffer. Note: gDEBugger might report that this is
  168. // not needed, but it is not already set from last time causing
  169. // skipped geometry and other render errors, do not remove this!
  170. GL.BindBuffer(BufferTarget.ElementArrayBuffer, nativeIndexBufferID);
  171. }
  172. // Update data in IBO (2 is the size of an ushort)
  173. int dataSize = Data.NumberOfUsedIndices * 2;
  174. GL.BufferSubData<ushort>(BufferTarget.ElementArrayBuffer, IntPtr.Zero,
  175. (IntPtr)dataSize, Data.Indices);
  176. }
  177. #endregion
  178. #region Dispose geometry data for static geometry
  179. // For static geometry we can get rid of the vertices and indices as
  180. // we have them now on the GPU. This saves memory and the content can
  181. // be reloaded at any time too.
  182. if (Data.IsStaticAndLoadedFromContent)
  183. {
  184. Data.Dispose();
  185. }
  186. #endregion
  187. }
  188. #endregion
  189. #region Render
  190. /// <summary>
  191. /// Draw the current mesh at the already specified location (handled in
  192. /// MaterialManager and ModelManager).
  193. /// </summary>
  194. /// <param name="shader">The shader.</param>
  195. internal override void Render(BaseShader shader)
  196. {
  197. // Nothing to draw? Then quit!
  198. if (Data.NumberOfUsedVertices == 0)
  199. {
  200. Log.Warning("Nothing to render, you should not call Render!");
  201. return;
  202. }
  203. // Dynamic vertex data needs to be updated each frame (if there are used
  204. // vertices for this frame). We also need to create the native vertex
  205. // buffer if that has not been done yet (the very first draw time here).
  206. if (Data.IsStatic == false &&
  207. Data.HasDataChanged ||
  208. nativeVertexArrayID == MathHelper.InvalidIndex)
  209. {
  210. try
  211. {
  212. Create(shader);
  213. }
  214. catch (Exception ex)
  215. {
  216. // Note: This should never happen, but if debug mode OpenTK is on
  217. // this might catch some errors and report some useful details.
  218. Log.Warning("Failed to create vertex buffer geometry for data=" +
  219. Data + ": " + ex);
  220. }
  221. } // if
  222. if (Graphic.Instance.HasShaderSupport == false)
  223. {
  224. GL.BindBuffer(BufferTarget.ArrayBuffer, nativeVertexBufferID);
  225. // If no support for shaders bind out FFP.
  226. BaseOpenGLGraphic.SetupFFPVertexFormat(Data.Format);
  227. }
  228. else
  229. {
  230. if (OpenTKGraphic.VaoSupport == false)
  231. {
  232. BaseOpenGLGraphic.SetupVertexFormat(
  233. shader as BaseOpenGLShader, Data.Format);
  234. }
  235. else
  236. {
  237. // Bind our VAO object
  238. GL.BindVertexArray(nativeVertexArrayID);
  239. }
  240. }
  241. // Line rendering needs some extra checks
  242. if (Data.IsLineData)
  243. {
  244. bool useAA = Settings.Extra.UseAntiAliasing;
  245. // Disable Depth writes when rendering anti aliased lines
  246. if (useAA)
  247. {
  248. GL.DepthMask(false);
  249. }
  250. // Lines are always drawn without index buffer
  251. GL.DrawArrays(drawMode, 0, Data.NumberOfUsedVertices);
  252. // Enable Depth writes when finished rendering anti aliased lines
  253. if (useAA)
  254. {
  255. GL.DepthMask(true);
  256. }
  257. }
  258. // Draw stuff normally
  259. else if (Data.NumberOfUsedIndices <= 0)
  260. {
  261. // Without index buffer
  262. GL.DrawArrays(drawMode, 0, Data.NumberOfUsedVertices);
  263. }
  264. else
  265. {
  266. // Bind our index buffer for correct rendering.
  267. GL.BindBuffer(BufferTarget.ElementArrayBuffer, nativeIndexBufferID);
  268. // With index buffer. Note since an IBO was setup above
  269. // we do not need the last parameter: See
  270. // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=258926
  271. GL.DrawElements(drawMode, Data.NumberOfUsedIndices,
  272. DrawElementsType.UnsignedShort, IntPtr.Zero);
  273. }
  274. }
  275. #endregion
  276. }
  277. }