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