Support for 3d images in the renderer.
This commit is contained in:
		
							parent
							
								
									4d5e0dd8f2
								
							
						
					
					
						commit
						5aa9a2f4e6
					
				| @ -11,12 +11,14 @@ namespace Quik.Media.Defaults | ||||
|     { | ||||
|         private readonly StbImage image; | ||||
|         private QImageBuffer buffer; | ||||
|         private bool isSdf = false; | ||||
| 
 | ||||
|         public override int Width => image.Width; | ||||
| 
 | ||||
|         public override int Height => image.Height; | ||||
| 
 | ||||
|         public override int Depth => 1; | ||||
|         public override bool IsSdf => isSdf; | ||||
|         public override QImageFormat InternalFormat => Stb2QImageFormat(image.Format); | ||||
| 
 | ||||
|         public QImageStbi(Stream source) | ||||
| @ -79,6 +81,11 @@ namespace Quik.Media.Defaults | ||||
|             buffer.UnlockBits(); | ||||
|         } | ||||
| 
 | ||||
|         public void SdfHint(bool value = true) | ||||
|         { | ||||
|             isSdf = value; | ||||
|         } | ||||
| 
 | ||||
|         protected override void Dispose(bool disposing) | ||||
|         { | ||||
|             base.Dispose(disposing); | ||||
|  | ||||
| @ -10,6 +10,7 @@ namespace Quik.Media | ||||
|         public abstract QImageFormat InternalFormat { get; } | ||||
|         public virtual int MipMapLevels => 0; | ||||
|         public virtual bool Premultiplied => false; | ||||
|         public virtual bool IsSdf => false; | ||||
| 
 | ||||
|         public abstract void LockBits2d(out QImageLock imageLock, QImageLockOptions options); | ||||
|         public abstract void LockBits3d(out QImageLock imageLock, QImageLockOptions options); | ||||
|  | ||||
| @ -3,6 +3,8 @@ using System.IO; | ||||
| using System.Collections.Generic; | ||||
| using Quik.VertexGenerator; | ||||
| using static Quik.OpenGL.GLEnum; | ||||
| using Quik.Media; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace Quik.OpenGL | ||||
| { | ||||
| @ -10,18 +12,23 @@ namespace Quik.OpenGL | ||||
|     { | ||||
|         private int program; | ||||
|         private int v2Position; | ||||
|         private int iZIndex; | ||||
|         private int fZIndex; | ||||
|         private int v2TexPos; | ||||
|         private int fTexLayer; | ||||
|         private int v4Color; | ||||
|         private int m4Transforms; | ||||
|         private int iMaxZ; | ||||
|         private int fMaxZ; | ||||
|         private int iEnableSdf; | ||||
|         private int iEnableTexture; | ||||
|         private int iAlphaDiscard; | ||||
|         private int fSdfThreshold; | ||||
|         private int txTexture; | ||||
|         private int tx2d; | ||||
|         private int tx2darray; | ||||
| 
 | ||||
|         private bool isDiposed; | ||||
|         private readonly Dictionary<DrawQueue, DrawData> data = new Dictionary<DrawQueue, DrawData>(); | ||||
|         private readonly TextureManager textures = new TextureManager(); | ||||
| 
 | ||||
|         public bool IsInit { get; private set; } = false; | ||||
|         public event Action<GL21Driver> OnGCDispose; | ||||
| 
 | ||||
| @ -57,16 +64,19 @@ namespace Quik.OpenGL | ||||
|             GL.DeleteShader(fs); | ||||
| 
 | ||||
|             v2Position = GL.GetAttribLocation(program, nameof(v2Position)); | ||||
|             iZIndex    = GL.GetAttribLocation(program, nameof(iZIndex)); | ||||
|             fZIndex    = GL.GetAttribLocation(program, nameof(fZIndex)); | ||||
|             v2TexPos   = GL.GetAttribLocation(program, nameof(v2TexPos)); | ||||
|             fTexLayer  = GL.GetAttribLocation(program, nameof(fTexLayer)); | ||||
|             v4Color    = GL.GetAttribLocation(program, nameof(v4Color)); | ||||
| 
 | ||||
|             m4Transforms   = GL.GetUniformLocation(program, nameof(m4Transforms)); | ||||
|             iMaxZ          = GL.GetUniformLocation(program, nameof(iMaxZ)); | ||||
|             fMaxZ          = GL.GetUniformLocation(program, nameof(fMaxZ)); | ||||
|             fSdfThreshold  = GL.GetUniformLocation(program, nameof(fSdfThreshold)); | ||||
|             iEnableSdf     = GL.GetUniformLocation(program, nameof(iEnableSdf)); | ||||
|             iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableSdf)); | ||||
|             iEnableTexture = GL.GetUniformLocation(program, nameof(iEnableTexture)); | ||||
|             iAlphaDiscard  = GL.GetUniformLocation(program, nameof(iAlphaDiscard)); | ||||
|             txTexture      = GL.GetUniformLocation(program, nameof(txTexture)); | ||||
|             tx2d           = GL.GetUniformLocation(program, nameof(tx2d)); | ||||
|             tx2darray      = GL.GetUniformLocation(program, nameof(tx2darray)); | ||||
| 
 | ||||
|             IsInit = true; | ||||
|         } | ||||
| @ -93,23 +103,39 @@ namespace Quik.OpenGL | ||||
|             QMat4.Orthographic(out QMat4 matrix, view); | ||||
| 
 | ||||
|             GL.UseProgram(program); | ||||
|             GL.Uniform1(iMaxZ, queue.ZDepth+1); | ||||
|             GL.Uniform1(fMaxZ, (float)(queue.ZDepth+1)); | ||||
|             GL.UniformMatrix4(m4Transforms, false, in matrix); | ||||
|             GL.Uniform1(fSdfThreshold, 0.0f); | ||||
|             GL.Uniform1(txTexture, 0); | ||||
|             GL.Uniform1(tx2d, 0); | ||||
|             GL.Enable(GL_BLEND); | ||||
|             GL.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||||
| 
 | ||||
|             foreach (DrawCall call in queue) | ||||
|             { | ||||
|                 GL.Viewport( | ||||
|                     (int)call.Bounds.Left, | ||||
|                     (int)(view.Bottom - call.Bounds.Bottom), | ||||
|                     (int)call.Bounds.Right, | ||||
|                     (int)(view.Bottom - call.Bounds.Top)); | ||||
|                 GL.ActiveTexture(GL_TEXTURE0); | ||||
|                 GL.BindTexture(GL_TEXTURE_2D, 0); | ||||
|                 if (call.Texture != null) | ||||
|                 { | ||||
|                     // TODO: actually use textures? | ||||
|                     GL.Uniform1(iEnableTexture, 1); | ||||
|                     GL.Uniform1(iEnableSdf, call.Texture.SignedDistanceField ? 1 : 0); | ||||
|                     GL.Uniform1(iEnableSdf, call.Texture.IsSdf ? 1 : 0); | ||||
|                     GL.Uniform1(iAlphaDiscard, 1); | ||||
| 
 | ||||
|                     if (call.Texture.Depth > 1) | ||||
|                     { | ||||
|                         GL.Uniform1(iEnableTexture, 3); | ||||
|                         GL.ActiveTexture(GL_TEXTURE1); | ||||
|                         GL.BindTexture(GL_TEXTURE_2D_ARRAY, textures.GetTexture(call.Texture)); | ||||
|                     } | ||||
|                     else | ||||
|                     { | ||||
|                         GL.Uniform1(iEnableTexture, 2); | ||||
|                         GL.ActiveTexture(GL_TEXTURE0); | ||||
|                         GL.BindTexture(GL_TEXTURE_2D, textures.GetTexture(call.Texture)); | ||||
|                     } | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
| @ -254,13 +280,15 @@ namespace Quik.OpenGL | ||||
|                 GL.BufferData(GL_ARRAY_BUFFER, QuikVertex.Stride * Queue.VertexCount, Queue.VertexArray, GL_STREAM_DRAW); | ||||
| 
 | ||||
|                 GL.VertexAttribPointer(driver.v2Position, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.PositionOffset); | ||||
|                 GL.VertexAttribIPointer(driver.iZIndex, 1, GL_UNSIGNED_INT, QuikVertex.Stride, QuikVertex.ZIndexOffset); | ||||
|                 GL.VertexAttribPointer(driver.fZIndex, 1, GL_UNSIGNED_INT, false, QuikVertex.Stride, QuikVertex.ZIndexOffset); | ||||
|                 GL.VertexAttribPointer(driver.v2TexPos, 2, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.TextureCoordinatesOffset); | ||||
|                 GL.VertexAttribPointer(driver.fTexLayer, 1, GL_FLOAT, false, QuikVertex.Stride, QuikVertex.TextureLayerOffset); | ||||
|                 GL.VertexAttribPointer(driver.v4Color, 4, GL_UNSIGNED_BYTE, true, QuikVertex.Stride, QuikVertex.ColorOffset); | ||||
|                 GL.EnableVertexAttribArray(driver.v2Position); | ||||
|                 GL.EnableVertexAttribArray(driver.iZIndex); | ||||
|                 GL.EnableVertexAttribArray(driver.fZIndex); | ||||
|                 GL.EnableVertexAttribArray(driver.v2TexPos); | ||||
|                 GL.EnableVertexAttribArray(driver.v4Color); | ||||
|                 GL.EnableVertexAttribArray(driver.fTexLayer); | ||||
| 
 | ||||
|                 GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); | ||||
|                 GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, Queue.ElementCount * sizeof(int), Queue.ElementArray, GL_STREAM_DRAW); | ||||
| @ -287,4 +315,115 @@ namespace Quik.OpenGL | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     internal class TextureManager : IDisposable | ||||
|     { | ||||
|         private readonly Dictionary<QImage, int> textures = new Dictionary<QImage, int>(); | ||||
|         private readonly HashSet<QImage> imagesNotUsed = new HashSet<QImage>(); | ||||
|         private bool isDisposed = false; | ||||
| 
 | ||||
|         public void BeginFrame() | ||||
|         { | ||||
|             if (imagesNotUsed.Count > 0) | ||||
|             { | ||||
|                 foreach (QImage image in imagesNotUsed) | ||||
|                 { | ||||
|                     GL.DeleteTexture(textures[image]); | ||||
|                 } | ||||
| 
 | ||||
|                 imagesNotUsed.Clear(); | ||||
|             } | ||||
| 
 | ||||
|             foreach (QImage image in textures.Keys) | ||||
|             { | ||||
|                 imagesNotUsed.Add(image); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public int GetTexture(QImage image) | ||||
|         { | ||||
|             if (textures.TryGetValue(image, out int texture)) | ||||
|             { | ||||
|                 return texture; | ||||
|             } | ||||
| 
 | ||||
|             if (image.Depth > 1) | ||||
|             { | ||||
|                 texture = UploadTexture3d(image); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 texture = UploadTexture2d(image); | ||||
|             } | ||||
|             | ||||
|             return textures[image] = texture; | ||||
|         } | ||||
| 
 | ||||
|         public int UploadTexture3d(QImage image3d) | ||||
|         { | ||||
|             int texture = GL.GenTexture(); | ||||
| 
 | ||||
|             GL.BindTexture(GL_TEXTURE_2D_ARRAY, texture); | ||||
| 
 | ||||
|             image3d.LockBits3d(out QImageLock lck, QImageLockOptions.Default); | ||||
|                 GL.TexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, lck.Width, lck.Height, lck.Depth, 0, s_InternalFormat[lck.Format], s_PixelType[lck.Format], lck.ImagePtr); | ||||
|             image3d.UnlockBits(); | ||||
| 
 | ||||
|             GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
|             GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
| 
 | ||||
|             return texture; | ||||
|         } | ||||
| 
 | ||||
|         public int UploadTexture2d(QImage image2d) | ||||
|         { | ||||
|             int texture = GL.GenTexture(); | ||||
| 
 | ||||
|             GL.BindTexture(GL_TEXTURE_2D, texture); | ||||
| 
 | ||||
|             image2d.LockBits2d(out QImageLock lck, QImageLockOptions.Default); | ||||
|             GL.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, lck.Width, lck.Height, 0, s_InternalFormat[lck.Format], s_PixelType[lck.Format], lck.ImagePtr); | ||||
|             image2d.UnlockBits(); | ||||
| 
 | ||||
|             GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
|             GL.TexParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
| 
 | ||||
|             return texture; | ||||
|         } | ||||
| 
 | ||||
|         public void Dispose() | ||||
|         { | ||||
|             if (isDisposed) | ||||
|                 return; | ||||
| 
 | ||||
|             isDisposed = true; | ||||
| 
 | ||||
|             int[] ids = textures.Values.ToArray(); | ||||
|             GL.DeleteTextures(ids); | ||||
|         } | ||||
| 
 | ||||
|         private static readonly Dictionary<QImageFormat, GLEnum> s_InternalFormat = new Dictionary<QImageFormat, GLEnum>() | ||||
|         { | ||||
|             [QImageFormat.AlphaF] = GL_ALPHA, | ||||
|             [QImageFormat.AlphaU8] = GL_ALPHA, | ||||
|             [QImageFormat.RedF] = GL_RED, | ||||
|             [QImageFormat.RedU8] = GL_RED, | ||||
|             [QImageFormat.RgbF] = GL_RGB, | ||||
|             [QImageFormat.RgbU8] = GL_RGB, | ||||
|             [QImageFormat.RgbaU8] = GL_RGBA, | ||||
|             [QImageFormat.RgbaF] = GL_RGBA, | ||||
|         }; | ||||
| 
 | ||||
|         private static readonly Dictionary<QImageFormat, GLEnum> s_PixelType = new Dictionary<QImageFormat, GLEnum>() | ||||
|         { | ||||
|             [QImageFormat.AlphaF] = GL_FLOAT, | ||||
|             [QImageFormat.RedF] = GL_FLOAT, | ||||
|             [QImageFormat.RgbF] = GL_FLOAT, | ||||
|             [QImageFormat.RgbaF] = GL_FLOAT, | ||||
|             [QImageFormat.AlphaU8] = GL_UNSIGNED_BYTE, | ||||
|             [QImageFormat.RedU8] = GL_UNSIGNED_BYTE, | ||||
|             [QImageFormat.RgbU8] = GL_UNSIGNED_BYTE, | ||||
|             [QImageFormat.RgbaU8] = GL_UNSIGNED_BYTE, | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| @ -2,16 +2,34 @@ | ||||
|  * QUIK: User Interface Kit | ||||
|  * Copyright (C) 2023 Halit Utku Maden, et al. | ||||
|  */ | ||||
| #version 120 | ||||
| #version 130 | ||||
| 
 | ||||
| varying vec2        fv2TexPos; | ||||
| varying vec4        fv4Color; | ||||
| in vec2        fv2TexPos; | ||||
| in vec4        fv4Color; | ||||
| in float       ffTexLayer; | ||||
| 
 | ||||
| uniform int         iEnableSdf; | ||||
| uniform int         iEnableTexture; | ||||
| uniform int         iAlphaDiscard; | ||||
| uniform float       fSdfThreshold; | ||||
| uniform sampler2D   txTexture; | ||||
| out vec4 fragColor; | ||||
| 
 | ||||
| uniform int            iEnableSdf; | ||||
| uniform int            iEnableTexture; | ||||
| uniform int            iAlphaDiscard; | ||||
| uniform float          fSdfThreshold; | ||||
| uniform sampler2D      tx2d; | ||||
| uniform sampler2DArray tx2dArray; | ||||
| 
 | ||||
| const float fAlphaThreshold = 0.01; | ||||
| 
 | ||||
| vec4 getTexture() | ||||
| { | ||||
|     if (iEnableTexture == 3) | ||||
|     { | ||||
|         return texture(tx2dArray, vec3(fv2TexPos, ffTexLayer)); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         return texture(tx2d, fv2TexPos); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void main(void) | ||||
| { | ||||
| @ -19,16 +37,19 @@ void main(void) | ||||
| 
 | ||||
|     if (iEnableTexture != 0) | ||||
|     { | ||||
|         vec4 value = texture2D(txTexture, fv2TexPos); | ||||
|         vec4 value = getTexture(); | ||||
| 
 | ||||
|         if (iEnableSdf != 0) | ||||
|         { | ||||
|             float a = max(value.r, value.a); | ||||
| 
 | ||||
|             value = (a >= fSdfThreshold) ? vec4(1,1,1,1) : vec4(0,0,0,0); | ||||
|             value =  | ||||
|                 (a >= fSdfThreshold) ? | ||||
|                     vec4(1.0,1.0,1.0,1.0) : | ||||
|                     vec4(0.0,0.0,0.0,0.0); | ||||
|         } | ||||
| 
 | ||||
|         if (iAlphaDiscard != 0 && value.a == 0.0) | ||||
|         if (iAlphaDiscard != 0 && value.a <= fAlphaThreshold) | ||||
|         { | ||||
|             discard; | ||||
|         } | ||||
| @ -36,5 +57,5 @@ void main(void) | ||||
|         albedo = albedo * value; | ||||
|     } | ||||
| 
 | ||||
|     gl_FragColor = albedo; | ||||
|     fragColor = albedo; | ||||
| } | ||||
| @ -2,24 +2,34 @@ | ||||
|  * QUIK: User Interface Kit | ||||
|  * Copyright (C) 2023 Halit Utku Maden, et al. | ||||
|  */ | ||||
| #version 120 | ||||
| #version 130 | ||||
| 
 | ||||
| attribute vec2  v2Position;     /**< The vertex position.*/ | ||||
| attribute int   iZIndex;        /**< The z index. */ | ||||
| attribute vec2  v2TexPos;       /**< The texture coorindates. */ | ||||
| attribute vec4  v4Color;        /**< The vertex color. */ | ||||
| in vec2  v2Position;     /**< The vertex position.*/ | ||||
| in float fZIndex;        /**< The z index. */ | ||||
| in vec2  v2TexPos;       /**< The texture coorindates. */ | ||||
| in float fTexLayer;      /**< The texture layer for 3D textures. */ | ||||
| in vec4  v4Color;        /**< The vertex color. */ | ||||
| 
 | ||||
| varying vec2    fv2TexPos; | ||||
| varying vec4    fv4Color; | ||||
| out vec2    fv2TexPos; | ||||
| out float   ffTexLayer; | ||||
| out vec4    fv4Color; | ||||
| 
 | ||||
| uniform mat4        m4Transforms;   /**< The view matrix. */ | ||||
| uniform int         iMaxZ;          /**< Highest Z coordinate. */ | ||||
| uniform float       fMaxZ;          /**< Highest Z coordinate. */ | ||||
| 
 | ||||
| const mat4 m4BaseTransforms = mat4( | ||||
|     vec4( 2.0,  0.0,  0.0,  0.0), | ||||
|     vec4( 0.0, -2.0,  0.0,  0.0), | ||||
|     vec4( 0.0,  0.0,  1.0,  0.0), | ||||
|     vec4(-1.0,  1.0,  0.0,  1.0) | ||||
| ); | ||||
| 
 | ||||
| void main(void) | ||||
| { | ||||
|     vec4 v = vec4(v2Position, float(iZIndex)/float(iMaxZ), 1); | ||||
|     gl_Position = v * m4Transforms; | ||||
|     vec4 v      = vec4(v2Position, fZIndex/fMaxZ, 1); | ||||
|     gl_Position = m4Transforms * v; | ||||
| 
 | ||||
|     fv2TexPos = v2TexPos; | ||||
|     fv4Color = v4Color; | ||||
| } | ||||
|     ffTexLayer = fTexLayer; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user