PageRenderTime 213ms CodeModel.GetById 200ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 1ms

/Graphics/OpenTK/OpenTKGeometry.cs

#
C# | 302 lines | 199 code | 25 blank | 78 comment | 30 complexity | 60c7a163576523e26b14a515d0b92877 MD5 | raw file
  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