Search code examples
c#opentk

Sprite Sheet Animation in OpenTK


I have been trying to make my first ever game engine (in OpenTK and Im stuck. I am trying to incoperate animations and I'm trying to use sprite sheets because they would greatly decrease filesize.

I know that I need to use a for loop to draw all the sprites in sequence, but I dont have a clue about what I should do to make it work with spritesheets.


Here is my current drawing code:

    //The Basis fuction for all drawing done in the application
    public static void Draw (Texture2D texture, Vector2 position, Vector2 scale, Color color, Vector2 origin, RectangleF? SourceRec = null)
    {
        Vector2[] vertices = new Vector2[4]
        {
            new Vector2(0, 0),
            new Vector2(1, 0),
            new Vector2(1, 1),
            new Vector2(0, 1),
        };

        GL.BindTexture(TextureTarget.Texture2D, texture.ID);

        //Beginning to draw on the screen
        GL.Begin(PrimitiveType.Quads);

        GL.Color3(color);
        for (int i = 0; i < 4; i++)
        {

                GL.TexCoord2((SourceRec.Value.Left + vertices[i].X * SourceRec.Value.Width) / texture.Width, (SourceRec.Value.Left + vertices[i].Y * SourceRec.Value.Height) / texture.Height);

            vertices[i].X *= texture.Width;
            vertices[i].Y *= texture.Height;
            vertices[i] -= origin;
            vertices[i] *= scale;
            vertices[i] += position;

            GL.Vertex2(vertices[i]);
        }

        GL.End();
    }

    public static void Begin(int screenWidth, int screenHeight)
    {
        GL.MatrixMode(MatrixMode.Projection);
        GL.LoadIdentity();
        GL.Ortho(-screenWidth / 2, screenWidth / 2, screenHeight / 2, -screenHeight / 2, 0f, 1f);
    }

Thanks in advance!!! :)


Solution

  • Firstly, I beg you, abandon the fixed function pipeline. It blinds you and restricts your understanding and creativity. Move to core 3.1+, it's structural perfection is matched only by it's potentiality, and the collaborative forethought and innovation will truly move you.

    Now, you'll want to store your sprite sheet images as a Texture2DArray. It took me some time to figure out how to load these correctly:

    public SpriteSheet(string filename, int spriteWidth, int spriteHeight)
    {
        // Assign ID and get name
        this.textureID = GL.GenTexture();
        this.spriteSheetName = Path.GetFileNameWithoutExtension(filename);
    
        // Bind the Texture Array and set appropriate parameters
        GL.BindTexture(TextureTarget.Texture2DArray, textureID);
        GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
        GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
        GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
        GL.TexParameter(TextureTarget.Texture2DArray, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
    
        // Load the image file
        Bitmap image = new Bitmap(@"Tiles/" + filename);
        BitmapData data = image.LockBits(new System.Drawing.Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
    
        // Determine columns and rows
        int spriteSheetwidth = image.Width;
        int spriteSheetheight = image.Height;
        int columns = spriteSheetwidth / spriteWidth;
        int rows = spriteSheetheight / spriteHeight;
    
        // Allocate storage
        GL.TexStorage3D(TextureTarget3d.Texture2DArray, 1, SizedInternalFormat.Rgba8, spriteWidth, spriteHeight, rows * columns);
    
        // Split the loaded image into individual Texture2D slices
        GL.PixelStore(PixelStoreParameter.UnpackRowLength, spriteSheetwidth);
        for (int i = 0; i < columns * rows; i++)
        {
            GL.TexSubImage3D(TextureTarget.Texture2DArray,
                             0, 0, 0, i, spriteWidth, spriteHeight, 1,
                             OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte,
                             data.Scan0 + (spriteWidth * 4 * (i % columns)) + (spriteSheetwidth * 4 * spriteHeight * (i / columns)));     // 4 bytes in an Bgra value.
        }
        image.UnlockBits(data);
    }
    

    Then you can simply draw a quad, and the Z texture co-ordinate is the Texture2D index in the Texture2DArray. Note frag_texcoord is of type vec3.

    #version 330 core
    
    in vec3 frag_texcoord;
    out vec4 outColor;
    
    uniform sampler2DArray tex;
    
    void main()
    {
        outColor = texture(tex, vec3(frag_texcoord));
    }