/Graphics/OpenTK/OpenTKGeometry.cs
# · C# · 302 lines · 199 code · 25 blank · 78 comment · 30 complexity · 60c7a163576523e26b14a515d0b92877 MD5 · raw file
- // Here you can disable rendering geometry (then nothing is visible on screen,
- // should be used together with DISABLE_SWAP_BUFFERS_FOR_PERFORMANCE_TESTING).
- // This tests CPU performance much closer without having to wait for GPU each
- // frame. This only makes a difference for very high fps (>5000 on fast PCs).
- // Note: Also useful to find native memory leaks related to geometry rendering!
- //#define DISABLE_GEOMETRY_RENDERING_FOR_PERFORMANCE_TESTING
-
- using System;
- using Delta.ContentSystem.Rendering;
- using Delta.Engine;
- using Delta.Graphics.BaseOpenGL;
- using Delta.Utilities;
- using Delta.Utilities.Helpers;
- using OpenTK.Graphics.OpenGL;
-
- namespace Delta.Graphics.OpenTK
- {
- /// <summary>
- /// OpenTK geometry implementation (with VAO if supported to improve
- /// performance). We use also VBO and IBO for this implementation. For
- /// details see http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=45
- /// </summary>
- public class OpenTKGeometry : BaseOpenGLGeometry
- {
- #region drawMode (Private)
- /// <summary>
- /// The cached draw mode of this geometry which is either Lines or Triangles.
- /// </summary>
- private readonly BeginMode drawMode;
- #endregion
-
- #region Constructor
- /// <summary>
- /// Create mesh from already created MeshData (for creating dynamic meshes,
- /// like for Lines, UI rendering or Effects).
- /// </summary>
- /// <param name="setMeshData">The set mesh data.</param>
- public OpenTKGeometry(GeometryData setMeshData)
- : base(setMeshData)
- {
- drawMode =
- Data.IsLineData
- ? BeginMode.Lines
- : BeginMode.Triangles;
- }
- #endregion
-
- #region Create
- /// <summary>
- /// Create native mesh data (vertex buffers, vertex array objects, etc.).
- /// Used to create the native vertex buffer, etc. when we have not created
- /// it yet or if the content gets updated by the content system.
- /// </summary>
- internal override void Create(BaseShader shader)
- {
- if (OpenTKGraphic.VaoSupport)
- {
- // Create and bind our VAO if we have not created it yet.
- if (nativeVertexArrayID == MathHelper.InvalidIndex)
- {
- // Create and setup vertex format
- GL.GenVertexArrays(1, out nativeVertexArrayID);
- }
-
- // Always bind it
- GL.BindVertexArray(nativeVertexArrayID);
- ErrorCode error = GL.GetError();
- if (error != ErrorCode.NoError)
- {
- Log.Warning(
- "There was an OpenGL error while creating the Vertex " +
- "Array Object: '" + error + "'. This usually happens when " +
- "something went wrong a little earlier, enable OpenTK debugging " +
- "and figure the problem out at the original place.");
- }
- }
-
- bool vertexDataChanged;
- bool indexDataChanged;
- CheckIfDataChanged(out vertexDataChanged, out indexDataChanged);
-
- #region VBO
- if (vertexDataChanged)
- {
- // Increase VBO if needed
- if (Data.MaxNumberOfVertices > currentMaxNumberOfVertices)
- {
- currentMaxNumberOfVertices = Data.MaxNumberOfVertices;
- int vertexSizeInBytes =
- Data.MaxNumberOfVertices * Data.Format.LengthInBytes;
- // Create or update VBO
- if (nativeVertexBufferID == MathHelper.InvalidIndex)
- {
- GL.GenBuffers(1, out nativeVertexBufferID);
- GL.BindBuffer(BufferTarget.ArrayBuffer, nativeVertexBufferID);
- // Reserve space for currentMaxNumberOfVertices
- GL.BufferData(BufferTarget.ArrayBuffer,
- new IntPtr(vertexSizeInBytes), IntPtr.Zero,
- Data.IsStatic
- ? BufferUsageHint.StaticDraw
- : BufferUsageHint.StreamDraw);
- }
- else
- {
- GL.BindBuffer(BufferTarget.ArrayBuffer, nativeVertexBufferID);
- GL.BufferData(BufferTarget.ArrayBuffer,
- new IntPtr(vertexSizeInBytes), IntPtr.Zero,
- BufferUsageHint.StreamDraw);
- }
-
- if (OpenTKGraphic.VaoSupport)
- {
- //Not helping at all, the issue is with SetupVertexFormat!
- //VaoSupport: return false;
-
- // Setup the vertex format for all geometry we will render.
- // Note: Needs to be done for every geometry, else pointers are
- // not correct! See
- // http://stackoverflow.com/questions/3665671/is-vertexattribpointer-needed-after-each-bindbuffer
- BaseOpenGLGraphic.SetupVertexFormat(shader as BaseOpenGLShader,
- Data.Format);
- }
- }
- else
- {
- // Just bind the buffer. Note: gDEBugger might report that this is
- // not needed, but it is not already set from last time causing
- // skipped geometry and other render errors, do not remove this!
- GL.BindBuffer(BufferTarget.ArrayBuffer, nativeVertexBufferID);
- }
-
- // changing geometries might be a lot more efficient!
- // This need to be re-thinked even of Engine side, we need to create
- // some kind of vertices atlas.
-
- // Update data in VBO
- byte[] dataBytes = Data.GetVerticesBytes();
- int dataSize =
- Data.VertexDataLengthInBytes * Data.NumberOfUsedVertices;
-
- GL.BufferSubData<byte>(BufferTarget.ArrayBuffer, IntPtr.Zero,
- (IntPtr)dataSize, dataBytes);
- }
- #endregion
-
- #region IBO
- if (Data.NumberOfUsedIndices > 0 &&
- indexDataChanged)
- {
- // recreate IBO if needed
- if (Data.Indices.Length > currentMaxNumberOfIndices)
- {
- currentMaxNumberOfIndices = Data.Indices.Length;
- // Create or update IBO
- if (nativeIndexBufferID == MathHelper.InvalidIndex)
- {
- GL.GenBuffers(1, out nativeIndexBufferID);
- GL.BindBuffer(BufferTarget.ElementArrayBuffer,
- nativeIndexBufferID);
- // Reserve space for currentMaxNumberOfIndices
- // 2 is the size of an ushort
- GL.BufferData(BufferTarget.ElementArrayBuffer,
- new IntPtr(currentMaxNumberOfIndices * 2), IntPtr.Zero,
- Data.IsStatic
- ? BufferUsageHint.StaticDraw
- : BufferUsageHint.StreamDraw);
- }
- else
- {
- GL.BindBuffer(BufferTarget.ElementArrayBuffer,
- nativeIndexBufferID);
- // 2 is the size of an ushort
- GL.BufferData(BufferTarget.ElementArrayBuffer,
- new IntPtr(currentMaxNumberOfIndices * 2), IntPtr.Zero,
- BufferUsageHint.StreamDraw);
- }
- }
- else
- {
- // Just bind the buffer. Note: gDEBugger might report that this is
- // not needed, but it is not already set from last time causing
- // skipped geometry and other render errors, do not remove this!
- GL.BindBuffer(BufferTarget.ElementArrayBuffer, nativeIndexBufferID);
- }
-
- // Update data in IBO (2 is the size of an ushort)
- int dataSize = Data.NumberOfUsedIndices * 2;
- GL.BufferSubData<ushort>(BufferTarget.ElementArrayBuffer, IntPtr.Zero,
- (IntPtr)dataSize, Data.Indices);
- }
- #endregion
-
- #region Dispose geometry data for static geometry
- // For static geometry we can get rid of the vertices and indices as
- // we have them now on the GPU. This saves memory and the content can
- // be reloaded at any time too.
- if (Data.IsStaticAndLoadedFromContent)
- {
- Data.Dispose();
- }
- #endregion
- }
- #endregion
-
- #region Render
- /// <summary>
- /// Draw the current mesh at the already specified location (handled in
- /// MaterialManager and ModelManager).
- /// </summary>
- /// <param name="shader">The shader.</param>
- internal override void Render(BaseShader shader)
- {
- // Nothing to draw? Then quit!
- if (Data.NumberOfUsedVertices == 0)
- {
- Log.Warning("Nothing to render, you should not call Render!");
- return;
- }
-
-
- // Dynamic vertex data needs to be updated each frame (if there are used
- // vertices for this frame). We also need to create the native vertex
- // buffer if that has not been done yet (the very first draw time here).
- if (Data.IsStatic == false &&
- Data.HasDataChanged ||
- nativeVertexArrayID == MathHelper.InvalidIndex)
- {
- try
- {
- Create(shader);
- }
- catch (Exception ex)
- {
- // Note: This should never happen, but if debug mode OpenTK is on
- // this might catch some errors and report some useful details.
- Log.Warning("Failed to create vertex buffer geometry for data=" +
- Data + ": " + ex);
- }
- } // if
-
- if (Graphic.Instance.HasShaderSupport == false)
- {
- GL.BindBuffer(BufferTarget.ArrayBuffer, nativeVertexBufferID);
-
- // If no support for shaders bind out FFP.
- BaseOpenGLGraphic.SetupFFPVertexFormat(Data.Format);
- }
- else
- {
- if (OpenTKGraphic.VaoSupport == false)
- {
- BaseOpenGLGraphic.SetupVertexFormat(
- shader as BaseOpenGLShader, Data.Format);
- }
- else
- {
- // Bind our VAO object
- GL.BindVertexArray(nativeVertexArrayID);
- }
- }
-
- // Line rendering needs some extra checks
- if (Data.IsLineData)
- {
- bool useAA = Settings.Extra.UseAntiAliasing;
- // Disable Depth writes when rendering anti aliased lines
- if (useAA)
- {
- GL.DepthMask(false);
- }
-
- // Lines are always drawn without index buffer
- GL.DrawArrays(drawMode, 0, Data.NumberOfUsedVertices);
-
- // Enable Depth writes when finished rendering anti aliased lines
- if (useAA)
- {
- GL.DepthMask(true);
- }
- }
- // Draw stuff normally
- else if (Data.NumberOfUsedIndices <= 0)
- {
- // Without index buffer
- GL.DrawArrays(drawMode, 0, Data.NumberOfUsedVertices);
- }
- else
- {
- // Bind our index buffer for correct rendering.
- GL.BindBuffer(BufferTarget.ElementArrayBuffer, nativeIndexBufferID);
-
- // With index buffer. Note since an IBO was setup above
- // we do not need the last parameter: See
- // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=258926
- GL.DrawElements(drawMode, Data.NumberOfUsedIndices,
- DrawElementsType.UnsignedShort, IntPtr.Zero);
- }
- }
- #endregion
- }
- }