objarni's picture

TexUtil class under development...

So since last time I tried texturing (my first blog post) I got burned by my illiteracy about TexImage2D aswell as the BitmapData-stuff. I've known that stuff - but it was like 2003 so it needed a bit of brushing-up.

I want to understand what I'm doing - sometimes that is good, sometimes it takes time!

Anyway when OpenTK0.9.6 released it's generics-overloads for lots of functions, among them the ReadPixels API, I got brave enough to try again.

So far my "TexUtil" class supports initializing the GL to support alpha-blended texturing, and creating/uploading opaque aswell as translucent textures via IMHO darn simple APIs:

int CreateRGBTexture(int width, int height, byte[] rgbArray)
int CreateRGBATexture(int width, int height, byte[] rgbaArray)

Here is some example code:

TexUtil.InitTexturing();
var white = new byte[3] { 255, 255, 255 };
var black = new byte[3] { 0, 0, 0 };
var chesspixels = new List<byte>();
chesspixels.AddRange(white);
chesspixels.AddRange(black);
chesspixels.AddRange(black);
chesspixels.AddRange(white);
int chessboard = TexUtil.CreateRGBTexture(2, 2, chesspixels.ToArray());

I have intentionally left out any resource management (deleting the texture name or wrapping it all in some Texture class) since it is such a huge topic that I can't make my mind up at this time. Also, object-based solutions may well be build ontop of this API.

Right now I'm working on a Bitmap->Texture method:

int CreateTextureFromBitmap(Bitmap bitmap);

.. which determines whether to create an opaque or translucent texture based on if it is a 24- or 32-bit bitmap. (I'm not planning any 8-bit support).

Also, right now I'm using the simplest of filters (nearest) and no mipmaps. Support for those features I will add on demand (from myself when developing some game most likely).

Here is the TexUtil class in all its present glory:

  /// <summary>
  /// The TexUtil class is released under the MIT-license.
  /// /Olof Bjarnason
  /// </summary>
  public static class TexUtil
  {
    #region Public
 
    /// <summary>
    /// Initialize OpenGL state to enable alpha-blended texturing.
    /// Disable again with GL.Disable(EnableCap.Texture2D)
    /// </summary>
    public static void InitTexturing()
    {
      GL.Disable(EnableCap.CullFace);
      GL.Enable(EnableCap.Texture2D);
      GL.Enable(EnableCap.Blend);
      GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
      GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
    }
 
    /// <summary>
    /// Create an opaque OpenGL texture object from a given byte-array of r,g,b-triplets.
    /// Make sure width and height is 1, 2, .., 32, 64, 128, 256 and so on in size since all
    /// 3d graphics cards support those dimensions. Not necessarily square.
    /// </summary>
    public static int CreateRGBTexture(int width, int height, byte[] rgb)
    {
      return CreateTexture(width, height, false, rgb);
    }
 
    /// <summary>
    /// Create a translucent OpenGL texture object from given byte-array of r,g,b,a-triplets.
    /// See CreateRGBTexture for more info.
    /// </summary>
    public static int CreateRGBATexture(int width, int height, byte[] rgba)
    {
      return CreateTexture(width, height, true, rgba);
    }
 
    #endregion
 
    private static int CreateTexture(int width, int height, bool alpha, byte[] bytes)
    {
      int expectedBytes = width * height * (alpha ? 4 : 3);
      Debug.Assert(expectedBytes == bytes.Length);
      int tex = GiveMeATexture();
      Upload(width, height, alpha, bytes);
      SetParameters();
      return tex;
    }
 
    private static int GiveMeATexture()
    {
      int tex = GL.GenTexture();
      GL.BindTexture(TextureTarget.Texture2D, tex);
      return tex;
    }
 
    private static void SetParameters()
    {
      GL.TexParameter(
        TextureTarget.Texture2D,
        TextureParameterName.TextureMinFilter,
        (int)TextureMinFilter.Nearest);
      GL.TexParameter(TextureTarget.Texture2D,
        TextureParameterName.TextureMagFilter,
        (int)TextureMagFilter.Nearest);
    }
 
    private static void Upload(int width, int height, bool alpha, byte[] bytes)
    {
      var internalFormat = alpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb;
      var format = alpha ? PixelFormat.Rgba : PixelFormat.Rgb;
      GL.TexImage2D<byte>(
        TextureTarget.Texture2D,
        0,
        internalFormat,
        width, height,
        0,
        format,
        PixelType.UnsignedByte,
        bytes);
    }
  }