/Graphics/OpenTK/OpenTKRenderToTexture.cs
C# | 418 lines | 315 code | 42 blank | 61 comment | 28 complexity | 221ee710a2e014a27c6ebab58d90125a MD5 | raw file
Possible License(s): Apache-2.0
- using System;
- using Delta.Engine;
- using Delta.Graphics.BaseOpenGL;
- using Delta.Utilities;
- using Delta.Utilities.Datatypes;
- using Delta.Utilities.Helpers;
- using OpenTK.Graphics.OpenGL;
-
- namespace Delta.Graphics.OpenTK
- {
- /// <summary>
- /// OpenGL implementation of RenderToTexture using FBO.
- /// </summary>
- internal class OpenTKRenderToTexture : BaseOpenGLRenderToTexture
- {
- #region Constructors
- /// <summary>
- /// Creates a OpenTKRenderTexture. Just passes everything on, which is
- /// important for the RenderToTexture.Create via the Factory.
- /// </summary>
- /// <param name="setTextureName">Name of the set texture.</param>
- /// <param name="setSize">Size of the set.</param>
- /// <param name="setIsDepthTexture">If set to <c>true</c> the texture
- /// target should be a depth texture</param>
- /// <param name="setIsShadowProjected">If set to <c>true</c> the texture
- /// target is projected shadow texture to be used in shadow mapping.
- /// </param>
- /// <param name="setUseStencilBuffer">
- /// Use the stencil flag for this RenderToTexture instance?
- /// </param>
- public OpenTKRenderToTexture(string setTextureName,
- Size setSize, bool setIsDepthTexture, bool setIsShadowProjected,
- bool setUseStencilBuffer)
- : base(setTextureName, setSize, setIsDepthTexture,
- setIsShadowProjected, setUseStencilBuffer)
- {
- // FBO creation happens dynamically.
- }
- #endregion
-
- #region Methods (Private)
-
- #region CreateFbo
- /// <summary>
- /// Create frame buffer object for render to texture, called when this
- /// render target is first used.
- /// </summary>
- private void CreateFbo()
- {
- bool supportFbo = OpenTKGraphic.SupportsExtension(
- "GL_EXT_framebuffer_object");
-
- if (supportFbo == false)
- {
- Log.Warning(
- "Can't create FBO RenderTexture. Please update your graphic card.");
- return;
- }
-
- if (UseStencilBuffer)
- {
- GL.Enable(EnableCap.StencilTest);
- GL.ClearStencil((int)StencilColor.PackedRGBA);
- }
-
- GL.GenTextures(1, out textureId);
- GL.BindTexture(TextureTarget.Texture2D, textureId);
-
- if (IsDepthTexture && UseStencilBuffer)
- {
- PixelInternalFormat depthFormat = PixelInternalFormat.DepthStencil;
- switch (Settings.Extra.DepthBufferSize)
- {
- case 16:
- Log.Warning("Cannot create depthbuffer, 16 bit depthbuffer " +
- "not supported in combination with a stencilbuffer.");
- return;
- case 24:
- depthFormat = PixelInternalFormat.Depth24Stencil8;
- break;
- case 32:
- depthFormat = PixelInternalFormat.Depth32fStencil8;
- break;
- }
-
- GL.TexImage2D(TextureTarget.Texture2D, 0,
- depthFormat, (int)PixelSize.Width,
- (int)PixelSize.Height, 0, PixelFormat.DepthStencil,
- PixelType.UnsignedByte, IntPtr.Zero);
-
- int textureFiler = (int)TextureMinFilter.Nearest;
- int textureAddress = (int)TextureWrapMode.ClampToEdge;
- // If texture is used as shadow2DProj, apply linear for
- // nice looking shadow
- if (IsShadowProjected)
- {
- textureFiler = (int)TextureMinFilter.Linear;
- textureAddress = (int)TextureWrapMode.ClampToEdge;
-
- // This is quite important
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureCompareMode,
- (int)TextureCompareMode.CompareRToTexture);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureCompareFunc,
- (int)All.Lequal);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.DepthTextureMode,
- (int)All.Intensity);
- }
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureMinFilter,
- textureFiler);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureMagFilter,
- textureFiler);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureWrapS, textureAddress);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureWrapT, textureAddress);
- }
- else if (IsDepthTexture)
- {
- PixelInternalFormat depthFormat = PixelInternalFormat.DepthComponent;
- switch (Settings.Extra.DepthBufferSize)
- {
- case 16:
- depthFormat = PixelInternalFormat.DepthComponent16;
- break;
- case 24:
- depthFormat = PixelInternalFormat.DepthComponent24;
- break;
- case 32:
- depthFormat = PixelInternalFormat.DepthComponent32;
- break;
- }
-
- GL.TexImage2D(TextureTarget.Texture2D, 0,
- depthFormat, (int)PixelSize.Width,
- (int)PixelSize.Height, 0, PixelFormat.DepthComponent,
- PixelType.UnsignedByte, IntPtr.Zero);
-
- int textureFiler = (int)TextureMinFilter.Nearest;
- int textureAddress = (int)TextureWrapMode.ClampToEdge;
- // If texture is used as shadow2DProj, apply linear for
- // nice looking shadow
- if (IsShadowProjected)
- {
- textureFiler = (int)TextureMinFilter.Linear;
- textureAddress = (int)TextureWrapMode.ClampToEdge;
-
- // This is quite important
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureCompareMode,
- (int)TextureCompareMode.CompareRToTexture);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureCompareFunc,
- (int)All.Lequal);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.DepthTextureMode,
- (int)All.Intensity);
- }
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureMinFilter,
- textureFiler);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureMagFilter,
- textureFiler);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureWrapS, textureAddress);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureWrapT, textureAddress);
- }
- else if (UseStencilBuffer)
- {
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
- GL.TexImage2D(TextureTarget.Texture2D, 0, (PixelInternalFormat)All.StencilIndex,
- (int)PixelSize.Width, (int)PixelSize.Height, 0, PixelFormat.StencilIndex,
- PixelType.UnsignedByte, IntPtr.Zero);
- }
- else
- {
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToBorder);
- GL.TexParameter(TextureTarget.Texture2D,
- TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToBorder);
- GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba8,
- (int)PixelSize.Width, (int)PixelSize.Height, 0, PixelFormat.Rgba,
- PixelType.UnsignedByte, IntPtr.Zero);
- }
- GL.BindTexture(TextureTarget.Texture2D, 0);
-
- // Create a frame buffer object
- GL.Ext.GenFramebuffers(1, out fboId);
- GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fboId);
-
- if (UseStencilBuffer)
- {
- GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
- FramebufferAttachment.StencilAttachmentExt, TextureTarget.Texture2D,
- textureId, 0);
- }
-
- if (IsDepthTexture)
- {
- GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
- FramebufferAttachment.DepthAttachmentExt, TextureTarget.Texture2D,
- textureId, 0);
-
- // Disable drawing to any buffers, we only want the depth
- GL.DrawBuffer(DrawBufferMode.None);
- GL.ReadBuffer((ReadBufferMode)All.None);
- }
- else
- {
- // Attach color texture to FBO handle
- GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt,
- FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D,
- textureId, 0);
- }
-
- #region Error check
- // Check FBO status
- FramebufferErrorCode fboStatus = GL.Ext.CheckFramebufferStatus(
- FramebufferTarget.FramebufferExt);
-
- switch (fboStatus)
- {
- case FramebufferErrorCode.FramebufferCompleteExt:
- // No need to log, it is all good.
- break;
-
- case FramebufferErrorCode.FramebufferIncompleteAttachmentExt:
- Log.Warning(
- "FBO: One or more attachment points are not framebuffer " +
- "attachment complete. This could mean theres no texture " +
- "attached or the format isnt renderable. For color textures " +
- "this means the base format must be RGB or RGBA and for depth " +
- "textures it must be a DEPTH_COMPONENT format. Other causes of " +
- "this error are that the width or height is zero or the " +
- "z-offset is out of range in case of render to volume.");
- break;
- case FramebufferErrorCode.FramebufferIncompleteMissingAttachmentExt:
- Log.Warning("FBO: There are no attachments.");
- break;
- case FramebufferErrorCode.FramebufferIncompleteDimensionsExt:
- Log.Warning(
- "FBO: Attachments are of different size. All attachments must " +
- "have the same width and height.");
- break;
- case FramebufferErrorCode.FramebufferIncompleteFormatsExt:
- Log.Warning(
- "FBO: The color attachments have different format. All color " +
- "attachments must have the same format.");
- break;
- case FramebufferErrorCode.FramebufferIncompleteDrawBufferExt:
- Log.Warning(
- "FBO: An attachment point referenced by GL.DrawBuffers() " +
- "doesnt have an attachment.");
- break;
- case FramebufferErrorCode.FramebufferIncompleteReadBufferExt:
- Log.Warning(
- "FBO: The attachment point referenced by GL.ReadBuffers() " +
- "doesnt have an attachment.");
- break;
- case FramebufferErrorCode.FramebufferUnsupportedExt:
- Log.Warning(
- "FBO: This particular FBO configuration is not supported by the " +
- "implementation.");
- break;
- default:
- Log.Warning("FBO: Status unknown (yes, this is really bad.)");
- break;
- }
-
- if (UseStencilBuffer)
- {
- int value = 0;
- GL.GetInteger(GetPName.StencilBits, out value);
- if (value == 0)
- {
- Log.Warning("FBO: No stencil buffer found.");
- }
- }
-
- // using FBO might have changed states, e.g. the FBO might not support
- // stereoscopic views or double buffering
- ErrorCode gLGetError = GL.GetError();
- if (gLGetError != ErrorCode.NoError)
- {
- Log.Warning("Last GL Error: " + gLGetError);
- }
- #endregion
-
- // switch back to window-system-provided framebuffer
- GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
- }
- #endregion
-
- #region Bind
- /// <summary>
- /// Implementation of Bind.
- /// </summary>
- protected override void Bind()
- {
- // All the context turn on
- GL.PushAttrib(AttribMask.ViewportBit);
-
- // Change screen aspect ration.
- ScreenSpace.CalculateAspectRatio(PixelSize.Width, PixelSize.Height);
-
- // In the case we render the shadow map to a higher resolution,
- // the viewport must be modified accordingly.
- GL.Viewport(0, 0, (int)PixelSize.Width, (int)PixelSize.Height);
-
- GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fboId);
-
- ClearBufferMask clearMask = ClearBufferMask.ColorBufferBit;
- // Clear with black color if depth rendering
- if (IsDepthTexture)
- {
- GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
-
- // Disable color rendering, we only want to write to the depth buffer
- GL.ColorMask(false, false, false, false);
-
- // Culling switching, rendering only backface, this is done to avoid self-shadowing
- GL.CullFace(CullFaceMode.Front);
-
- clearMask |= ClearBufferMask.DepthBufferBit;
- }
-
- // Add stencilbuffer mask if required
- if (UseStencilBuffer)
- {
- clearMask |= ClearBufferMask.StencilBufferBit;
- GL.Enable(EnableCap.StencilTest);
- GL.ClearStencil((int)StencilColor.PackedRGBA);
- GL.ColorMask(false, false, false, false);
- GL.StencilFunc(StencilFunction.Always, 1, 1);
- GL.StencilOp(StencilOp.Replace, StencilOp.Replace, StencilOp.Replace);
- }
-
- var backColor = Application.BackgroundColor;
- GL.ClearColor(backColor.R, backColor.G, backColor.B, backColor.A);
-
- // Clear previous frame values
- GL.Clear(clearMask);
- }
- #endregion
-
- #region Unbind
- /// <summary>
- /// Implementation of Unbind.
- /// </summary>
- protected override void Unbind()
- {
- GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
- GL.PopAttrib();
-
- // Reset the original aspect ration and screen size.
- ScreenSpace.CalculateAspectRatio();
-
- if (IsDepthTexture)
- {
- // When depth rendering enable color mask, disabled on Bind methods
- GL.ColorMask(true, true, true, true);
-
- // Restore cull mode
- GL.CullFace(CullFaceMode.Back);
- }
- }
- #endregion
-
- #region MakeSureRenderTargetIsInitialized
- /// <summary>
- /// Make sure is render target is initialized, called before render to
- /// make sure we can actually use this class.
- /// </summary>
- protected override void MakeSureRenderTargetIsInitialized()
- {
- if (fboId == MathHelper.InvalidIndex)
- {
- CreateFbo();
- }
- }
- #endregion
-
- #region DisposeNativeData
- /// <summary>
- /// Disposes fbo texture and gl texture too.
- /// </summary>
- protected override void DisposeNativeData()
- {
- if (fboId != MathHelper.InvalidIndex)
- {
- GL.Ext.DeleteFramebuffers(1, ref fboId);
- GL.DeleteTexture(textureId);
-
- fboId = MathHelper.InvalidIndex;
- }
- }
- #endregion
-
- #endregion
- }
- }