PageRenderTime 86ms CodeModel.GetById 31ms app.highlight 31ms RepoModel.GetById 19ms app.codeStats 0ms

/Graphics/OpenTK/OpenTKRenderToTexture.cs

#
C# | 418 lines | 315 code | 42 blank | 61 comment | 28 complexity | 221ee710a2e014a27c6ebab58d90125a MD5 | raw file
  1using System;
  2using Delta.Engine;
  3using Delta.Graphics.BaseOpenGL;
  4using Delta.Utilities;
  5using Delta.Utilities.Datatypes;
  6using Delta.Utilities.Helpers;
  7using OpenTK.Graphics.OpenGL;
  8
  9namespace Delta.Graphics.OpenTK
 10{
 11	/// <summary>
 12	/// OpenGL implementation of RenderToTexture using FBO.
 13	/// </summary>
 14	internal class OpenTKRenderToTexture : BaseOpenGLRenderToTexture
 15	{
 16		#region Constructors
 17		/// <summary>
 18		/// Creates a OpenTKRenderTexture. Just passes everything on, which is
 19		/// important for the RenderToTexture.Create via the Factory.
 20		/// </summary>
 21		/// <param name="setTextureName">Name of the set texture.</param>
 22		/// <param name="setSize">Size of the set.</param>
 23		/// <param name="setIsDepthTexture">If set to <c>true</c> the texture
 24		/// target should be a depth texture</param>
 25		/// <param name="setIsShadowProjected">If set to <c>true</c> the texture
 26		/// target is projected shadow texture to be used in shadow mapping.
 27		/// </param>
 28		/// <param name="setUseStencilBuffer">
 29		/// Use the stencil flag for this RenderToTexture instance?
 30		/// </param>
 31		public OpenTKRenderToTexture(string setTextureName,
 32			Size setSize, bool setIsDepthTexture, bool setIsShadowProjected,
 33			bool setUseStencilBuffer)
 34			: base(setTextureName, setSize, setIsDepthTexture,
 35				setIsShadowProjected, setUseStencilBuffer)
 36		{
 37			// FBO creation happens dynamically.
 38		}
 39		#endregion
 40
 41		#region Methods (Private)
 42
 43		#region CreateFbo
 44		/// <summary>
 45		/// Create frame buffer object for render to texture, called when this
 46		/// render target is first used.
 47		/// </summary>
 48		private void CreateFbo()
 49		{
 50			bool supportFbo = OpenTKGraphic.SupportsExtension(
 51				"GL_EXT_framebuffer_object");
 52
 53			if (supportFbo == false)
 54			{
 55				Log.Warning(
 56					"Can't create FBO RenderTexture. Please update your graphic card.");
 57				return;
 58			}
 59
 60			if (UseStencilBuffer)
 61			{
 62				GL.Enable(EnableCap.StencilTest);
 63				GL.ClearStencil((int)StencilColor.PackedRGBA);
 64			}
 65
 66			GL.GenTextures(1, out textureId);
 67			GL.BindTexture(TextureTarget.Texture2D, textureId);
 68
 69			if (IsDepthTexture && UseStencilBuffer)
 70			{
 71				PixelInternalFormat depthFormat = PixelInternalFormat.DepthStencil;
 72				switch (Settings.Extra.DepthBufferSize)
 73				{
 74					case 16:
 75						Log.Warning("Cannot create depthbuffer, 16 bit depthbuffer " +
 76						            "not supported in combination with a stencilbuffer.");
 77						return;
 78					case 24:
 79						depthFormat = PixelInternalFormat.Depth24Stencil8;
 80						break;
 81					case 32:
 82						depthFormat = PixelInternalFormat.Depth32fStencil8;
 83						break;
 84				}
 85
 86				GL.TexImage2D(TextureTarget.Texture2D, 0,
 87					depthFormat, (int)PixelSize.Width,
 88					(int)PixelSize.Height, 0, PixelFormat.DepthStencil,
 89					PixelType.UnsignedByte, IntPtr.Zero);
 90
 91				int textureFiler = (int)TextureMinFilter.Nearest;
 92				int textureAddress = (int)TextureWrapMode.ClampToEdge;
 93				// If texture is used as shadow2DProj, apply linear for
 94				// nice looking shadow
 95				if (IsShadowProjected)
 96				{
 97					textureFiler = (int)TextureMinFilter.Linear;
 98					textureAddress = (int)TextureWrapMode.ClampToEdge;
 99
100					// This is quite important
101					GL.TexParameter(TextureTarget.Texture2D,
102						TextureParameterName.TextureCompareMode,
103						(int)TextureCompareMode.CompareRToTexture);
104					GL.TexParameter(TextureTarget.Texture2D,
105						TextureParameterName.TextureCompareFunc,
106						(int)All.Lequal);
107					GL.TexParameter(TextureTarget.Texture2D,
108						TextureParameterName.DepthTextureMode,
109						(int)All.Intensity);
110				}
111				GL.TexParameter(TextureTarget.Texture2D,
112					TextureParameterName.TextureMinFilter,
113					textureFiler);
114				GL.TexParameter(TextureTarget.Texture2D,
115					TextureParameterName.TextureMagFilter,
116					textureFiler);
117				GL.TexParameter(TextureTarget.Texture2D,
118					TextureParameterName.TextureWrapS, textureAddress);
119				GL.TexParameter(TextureTarget.Texture2D,
120					TextureParameterName.TextureWrapT, textureAddress);
121			}
122			else if (IsDepthTexture)
123			{
124				PixelInternalFormat depthFormat = PixelInternalFormat.DepthComponent;
125				switch (Settings.Extra.DepthBufferSize)
126				{
127					case 16:
128						depthFormat = PixelInternalFormat.DepthComponent16;
129						break;
130					case 24:
131						depthFormat = PixelInternalFormat.DepthComponent24;
132						break;
133					case 32:
134						depthFormat = PixelInternalFormat.DepthComponent32;
135						break;
136				}
137
138				GL.TexImage2D(TextureTarget.Texture2D, 0,
139					depthFormat, (int)PixelSize.Width,
140					(int)PixelSize.Height, 0, PixelFormat.DepthComponent,
141					PixelType.UnsignedByte, IntPtr.Zero);
142
143				int textureFiler = (int)TextureMinFilter.Nearest;
144				int textureAddress = (int)TextureWrapMode.ClampToEdge;
145				// If texture is used as shadow2DProj, apply linear for
146				// nice looking shadow
147				if (IsShadowProjected)
148				{
149					textureFiler = (int)TextureMinFilter.Linear;
150					textureAddress = (int)TextureWrapMode.ClampToEdge;
151
152					// This is quite important
153					GL.TexParameter(TextureTarget.Texture2D,
154						TextureParameterName.TextureCompareMode,
155						(int)TextureCompareMode.CompareRToTexture);
156					GL.TexParameter(TextureTarget.Texture2D,
157						TextureParameterName.TextureCompareFunc,
158						(int)All.Lequal);
159					GL.TexParameter(TextureTarget.Texture2D,
160						TextureParameterName.DepthTextureMode,
161						(int)All.Intensity);
162				}
163				GL.TexParameter(TextureTarget.Texture2D,
164					TextureParameterName.TextureMinFilter,
165					textureFiler);
166				GL.TexParameter(TextureTarget.Texture2D,
167					TextureParameterName.TextureMagFilter,
168					textureFiler);
169				GL.TexParameter(TextureTarget.Texture2D,
170					TextureParameterName.TextureWrapS, textureAddress);
171				GL.TexParameter(TextureTarget.Texture2D,
172					TextureParameterName.TextureWrapT, textureAddress);
173			}
174			else if (UseStencilBuffer)
175			{
176				GL.TexParameter(TextureTarget.Texture2D,
177					TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
178				GL.TexParameter(TextureTarget.Texture2D,
179					TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
180				GL.TexParameter(TextureTarget.Texture2D,
181					TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
182				GL.TexParameter(TextureTarget.Texture2D,
183					TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
184				GL.TexImage2D(TextureTarget.Texture2D, 0, (PixelInternalFormat)All.StencilIndex,
185					(int)PixelSize.Width, (int)PixelSize.Height, 0, PixelFormat.StencilIndex,
186					PixelType.UnsignedByte, IntPtr.Zero);
187			}
188			else
189			{
190				GL.TexParameter(TextureTarget.Texture2D,
191					TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
192				GL.TexParameter(TextureTarget.Texture2D,
193					TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
194				GL.TexParameter(TextureTarget.Texture2D,
195					TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
196				GL.TexParameter(TextureTarget.Texture2D,
197					TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
198				GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8,
199					(int)PixelSize.Width, (int)PixelSize.Height, 0, PixelFormat.Rgba,
200					PixelType.UnsignedByte, IntPtr.Zero);
201			}
202			GL.BindTexture(TextureTarget.Texture2D, 0);
203
204			// Create a frame buffer object
205			GL.Ext.GenFramebuffers(1, out fboId);
206			GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fboId);
207
208			if (UseStencilBuffer)
209			{
210				GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
211					FramebufferAttachment.StencilAttachmentExt, TextureTarget.Texture2D,
212					textureId, 0);
213			}
214
215			if (IsDepthTexture)
216			{
217				GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
218					FramebufferAttachment.DepthAttachmentExt, TextureTarget.Texture2D,
219					textureId, 0);
220
221				// Disable drawing to any buffers, we only want the depth
222				GL.DrawBuffer(DrawBufferMode.None);
223				GL.ReadBuffer((ReadBufferMode)All.None);
224			}
225			else
226			{
227				// Attach color texture to FBO handle
228				GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
229					FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D,
230					textureId, 0);
231			}
232
233			#region Error check
234			// Check FBO status
235			FramebufferErrorCode fboStatus = GL.Ext.CheckFramebufferStatus(
236				FramebufferTarget.FramebufferExt);
237
238			switch (fboStatus)
239			{
240				case FramebufferErrorCode.FramebufferCompleteExt:
241					// No need to log, it is all good.
242					break;
243
244				case FramebufferErrorCode.FramebufferIncompleteAttachmentExt:
245					Log.Warning(
246						"FBO: One or more attachment points are not framebuffer " +
247						"attachment complete. This could mean there’s no texture " +
248						"attached or the format isn’t renderable. For color textures " +
249						"this means the base format must be RGB or RGBA and for depth " +
250						"textures it must be a DEPTH_COMPONENT format. Other causes of " +
251						"this error are that the width or height is zero or the " +
252						"z-offset is out of range in case of render to volume.");
253					break;
254				case FramebufferErrorCode.FramebufferIncompleteMissingAttachmentExt:
255					Log.Warning("FBO: There are no attachments.");
256					break;
257				case FramebufferErrorCode.FramebufferIncompleteDimensionsExt:
258					Log.Warning(
259						"FBO: Attachments are of different size. All attachments must " +
260						"have the same width and height.");
261					break;
262				case FramebufferErrorCode.FramebufferIncompleteFormatsExt:
263					Log.Warning(
264						"FBO: The color attachments have different format. All color " +
265						"attachments must have the same format.");
266					break;
267				case FramebufferErrorCode.FramebufferIncompleteDrawBufferExt:
268					Log.Warning(
269						"FBO: An attachment point referenced by GL.DrawBuffers() " +
270						"doesn’t have an attachment.");
271					break;
272				case FramebufferErrorCode.FramebufferIncompleteReadBufferExt:
273					Log.Warning(
274						"FBO: The attachment point referenced by GL.ReadBuffers() " +
275						"doesn’t have an attachment.");
276					break;
277				case FramebufferErrorCode.FramebufferUnsupportedExt:
278					Log.Warning(
279						"FBO: This particular FBO configuration is not supported by the " +
280						"implementation.");
281					break;
282				default:
283					Log.Warning("FBO: Status unknown (yes, this is really bad.)");
284					break;
285			}
286
287			if (UseStencilBuffer)
288			{
289				int value = 0;
290				GL.GetInteger(GetPName.StencilBits, out value);
291				if (value == 0)
292				{
293					Log.Warning("FBO: No stencil buffer found.");
294				}
295			}
296
297			// using FBO might have changed states, e.g. the FBO might not support 
298			// stereoscopic views or double buffering
299			ErrorCode gLGetError = GL.GetError();
300			if (gLGetError != ErrorCode.NoError)
301			{
302				Log.Warning("Last GL Error: " + gLGetError);
303			}
304			#endregion
305
306			// switch back to window-system-provided framebuffer
307			GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
308		}
309		#endregion
310
311		#region Bind
312		/// <summary>
313		/// Implementation of Bind.
314		/// </summary>
315		protected override void Bind()
316		{
317			// All the context turn on
318			GL.PushAttrib(AttribMask.ViewportBit);
319
320			// Change screen aspect ration.
321			ScreenSpace.CalculateAspectRatio(PixelSize.Width, PixelSize.Height);
322
323			// In the case we render the shadow map to a higher resolution, 
324			// the viewport must be modified accordingly.
325			GL.Viewport(0, 0, (int)PixelSize.Width, (int)PixelSize.Height);
326
327			GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fboId);
328
329			ClearBufferMask clearMask = ClearBufferMask.ColorBufferBit;
330			// Clear with black color if depth rendering
331			if (IsDepthTexture)
332			{
333				GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
334
335				// Disable color rendering, we only want to write to the depth buffer
336				GL.ColorMask(false, false, false, false);
337
338				// Culling switching, rendering only backface, this is done to avoid self-shadowing
339				GL.CullFace(CullFaceMode.Front);
340
341				clearMask |= ClearBufferMask.DepthBufferBit;
342			}
343
344			// Add stencilbuffer mask if required
345			if (UseStencilBuffer)
346			{
347				clearMask |= ClearBufferMask.StencilBufferBit;
348				GL.Enable(EnableCap.StencilTest);
349				GL.ClearStencil((int)StencilColor.PackedRGBA);
350				GL.ColorMask(false, false, false, false);
351				GL.StencilFunc(StencilFunction.Always, 1, 1);
352				GL.StencilOp(StencilOp.Replace, StencilOp.Replace, StencilOp.Replace);
353			}
354
355			var backColor = Application.BackgroundColor;
356			GL.ClearColor(backColor.R, backColor.G, backColor.B, backColor.A);
357
358			// Clear previous frame values
359			GL.Clear(clearMask);
360		}
361		#endregion
362
363		#region Unbind
364		/// <summary>
365		/// Implementation of Unbind.
366		/// </summary>
367		protected override void Unbind()
368		{
369			GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
370			GL.PopAttrib();
371
372			// Reset the original aspect ration and screen size.
373			ScreenSpace.CalculateAspectRatio();
374
375			if (IsDepthTexture)
376			{
377				// When depth rendering enable color mask, disabled on Bind methods
378				GL.ColorMask(true, true, true, true);
379
380				// Restore cull mode
381				GL.CullFace(CullFaceMode.Back);
382			}
383		}
384		#endregion
385
386		#region MakeSureRenderTargetIsInitialized
387		/// <summary>
388		/// Make sure is render target is initialized, called before render to
389		/// make sure we can actually use this class.
390		/// </summary>
391		protected override void MakeSureRenderTargetIsInitialized()
392		{
393			if (fboId == MathHelper.InvalidIndex)
394			{
395				CreateFbo();
396			}
397		}
398		#endregion
399
400		#region DisposeNativeData
401		/// <summary>
402		/// Disposes fbo texture and gl texture too.
403		/// </summary>
404		protected override void DisposeNativeData()
405		{
406			if (fboId != MathHelper.InvalidIndex)
407			{
408				GL.Ext.DeleteFramebuffers(1, ref fboId);
409				GL.DeleteTexture(textureId);
410
411				fboId = MathHelper.InvalidIndex;
412			}
413		}
414		#endregion
415
416		#endregion
417	}
418}