/*

	EZ GL2D For NET (C#, VB, C++/CLI, F#, ironPython, etc.) 

	Relminator 2014
	Richard Eric M. Lope BSN RN

	http://rel.phatcode.net

    License:

    Copyright (c) 2010 - 2014 

    Source is released under the LGPL license with these additional provisions:
 
        Permission is granted, free of charge, for anyone(without restrictions).. 
        1. To use the source in any way as long as it does not harm someone else. 
        2. To edit/modify the source in any way they want.
        3. To distribute the source along with its accompanying files.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED.
    IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR
    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
    OTHER DEALINGS IN THE SOFTWARE.

*/


/*!
 \mainpage EZ GL2D Documentation

 \image html screenshots/screen01.png ""
	 
 \section author Author
 
    EZ GL2D For NET (C#, VB, C++/CLI, F#, ironPython, etc.) 

	Relminator 2014
 
	Richard Eric M. Lope BSN RN
 
 \section features Features
 
 - Very easy to use
 - Lightweight
 - Fast
 - Portable
 - Can be used alongside 3D scenes
 - 1:1 screen to coordinate ratio
 - Shared across modules
 - Easy sprite atlas support
 - Automatic texture binding
 - Automatic texture cleanup
 - Full hardware acceleration
 
 \section api Library API
 - \ref EZGL2D.cs "Reference"
 
 \section howto How to use
 - \ref instructions.cs "Mini-tutorial"
 
 \section intro Official Site/Facebook
 - <a href="http://rel.phatcode.net">Genso's Junkyard</a> 
 - <a href="https://www.facebook.com/anyatherese.lope">Anya's Page</a> 
 
 \section download Download
  - <a href="http://www.opentk.com/">OpenTK(EZGL2D uses this as a backend)</a>
  - <a href="http://rel.phatcode.net/junk.php?id=117">Easy GL2D for Nintendo DS</a> 
  - <a href="http://rel.phatcode.net/junk.php?id=121">Easy GL2D for C++</a> 

 \section games Example Games
 - PC
 - <a href="http://rel.phatcode.net/index.php?action=contents&item=Pyromax-Dax">Pyromax Dax</a> 
 - <a href="http://rel.phatcode.net/index.php?action=contents&item=ConvexWars-FB">Convex Wars</a> 
 <br>
 <br>
 - Nintendo DS
 - <a href="http://rel.phatcode.net/index.php?action=contents&item=Fuzed-DS">Fuzed DS</a> 
 - <a href="http://rel.phatcode.net/index.php?action=contents&item=Space-Impakto-DS">Space Impakto DS</a> 
 - <a href="http://rel.phatcode.net/index.php?action=contents&item=Bubble_Fight_EX_DS">Bubble Fight EX</a> 
 
 \section tools Misc. Tools
 - <a href="http://rel.phatcode.net/junk.php?id=106">Rel's Texture Packer</a> 
 
 \section external_links Useful links
 - <a href="http://www.devkitpro.org/">devkitPro</a>
 - <a href="http://www.freebasic.net">Freebasic</a>
 - <a href="https://www.facebook.com/groups/ProgramersDevelopers/">Programmers, Developers</a>
 
 \section screenies More Screenshots
 \image html screenshots/screen02.png "Convex Wars"
 \image html screenshots/screen03.png "Space Impakto DS"
 \image html screenshots/screen04.png "Fuzed DS"

*/




/*! \file EZGL2D.cs
	\brief A very small and simple NET-based PC/MAC/DS/Mobile hardware accelerated rendering lib OpenGL(via OpenTK) to render 2D stuff.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;


namespace EZGL2D
{
    /*! \brief GL2D Static Class. */
    public class GL2D
    {




        /*! \brief Enums for controlling how blending works. */
        public enum BLEND_MODE
        {

            GL2D_TRANS = 0,   	/*!< Transparent blit */
            GL2D_SOLID,			/*!< Solid blit */
            GL2D_BLENDED,		/*!< Alpha Blended blit */
            GL2D_GLOW,			/*!< Additive Blended blit */
            GL2D_ALPHA,			/*!< Alpha Transparent blit */
        }


        /*! \brief Struct for our GL-Based Images<BR>
        This is our struct to hold our image
	    attributes. You don't need to worry about this 
	    most of the time. */
        public class SpriteGL
        {

            /*! \brief Constants to select flipping mode.
            *
            *	These enums are bits for flipping the sprites. <Br>
            *	You can <b>"|"</b> (or) GL_FLIP_V and GL_FLIP_H to flip 
            *	both ways. <Br><Br>
            *  <ul>
            *	<li> Related functions:
            * 		<ol>
            *	 	<li>Sprite()
            *	 	<li>SpriteRotateScale()
            * 		</ol>
            * 	</ul>	
            */

            public static int FLIP_NONE = 1 << 0;  /*!< No flip */
            public static int FLIP_V = 1 << 1;     /*!< Vertical flip */
            public static int FLIP_H = 1 << 2;     /*!< Horizontal flip */

            public int width;				/*!< Width of the sprite */
            public int height;				/*!< Height of the sprite */
            public float u_off;			    /*!< Horizontal texture offset of the sprite */
            public float v_off;			    /*!< Vertical texture offset of the sprite */
            public float u_width;			/*!< Horizontal texture offset width of the sprite */
            public float v_height;			/*!< Vertical texture offset height of the sprite */

            public int textureID;	        /*!< OpenGL generated texture ID of the sprite */

            /*! \brief SpriteGL constructor. <Br> 
	            Use this constructor to instantiate a SpriteGL object.
             */
            public SpriteGL()
            {

            }

        }


        private static IList<int> textureIDs = new List<int>();

        private static int currentTexture = 0;
        private static int screenWidth = 640;
        private static int screenHeight = 480;

        /*! \brief Sets up OpenGL for 2d rendering. <Br> 
            Call this before drawing any of GL2D's drawing or sprite functions.
         */
        public static void Begin2D()
        {
            //currentTexture = 0;
            GL.Disable(EnableCap.DepthTest);
            GL.Disable(EnableCap.CullFace);

            GL.Enable(EnableCap.Blend);
            GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);

            GL.Enable(EnableCap.ColorMaterial);

            GL.Enable(EnableCap.Texture2D);

            GL.Disable(EnableCap.StencilTest);
            GL.Disable(EnableCap.Texture1D);
            GL.Disable(EnableCap.Lighting);
            GL.Disable(EnableCap.Fog);
            GL.Disable(EnableCap.Dither);

            GL.Disable(EnableCap.ColorLogicOp);

            GL.PointSize(1.0f);
            GL.LineWidth(1.0f);

            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
            GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest);

            GL.MatrixMode(MatrixMode.Projection);
            GL.PushMatrix();
            GL.LoadIdentity();
            GL.Ortho(0, screenWidth, screenHeight, 0, -1, 1);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.PushMatrix();
            GL.LoadIdentity();
            GL.Translate(0.375f, 0.375f, 0.0f);

            GL.ClearColor(0, 0, 0, 0);
            GL.Color4(1.0f, 1.0f, 1.0f, 1.0f);

        }


        /*! \brief Issue this after drawing 2d so that we don't mess the matrix stack. <Br> 
    	    The compliment of glBegin2D().
        */
        public static void End2D()
        {
            GL.MatrixMode(MatrixMode.Modelview);
            GL.PopMatrix();
            GL.MatrixMode(MatrixMode.Projection);
            GL.PopMatrix();
            GL.MatrixMode(MatrixMode.Modelview);
        }

        /*! \brief Initializes GL in 2D mode
            \param width width of the window.
            \param height height of the window.
        */
        public static void Screen2D(int width, int height)
        {

            screenWidth = width;
            screenHeight = height;

            GL.Viewport(0, 0, screenWidth, screenHeight);

            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();

            double aspect_ratio = screenWidth / (double)screenHeight;

            OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, (float)aspect_ratio, 1, 500);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref perspective);

            GL.MatrixMode(MatrixMode.Modelview);

            GL.LoadIdentity();

            Matrix4 lookat = Matrix4.LookAt(0, 5, 5, 0, 0, 0, 0, 1, 0);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref lookat);


            GL.ShadeModel(ShadingModel.Smooth);
            GL.ClearColor(0, 0, 0, 1.0f);
            GL.DepthFunc(DepthFunction.Lequal);

            GL.Disable(EnableCap.DepthTest);
            GL.Disable(EnableCap.CullFace);

            GL.Enable(EnableCap.Blend);
            GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);

            GL.Enable(EnableCap.ColorMaterial);

            GL.Enable(EnableCap.Texture2D);

            GL.Disable(EnableCap.StencilTest);
            GL.Disable(EnableCap.Texture1D);
            GL.Disable(EnableCap.Lighting);
            GL.Disable(EnableCap.Fog);
            GL.Disable(EnableCap.Dither);

            GL.Disable(EnableCap.ColorLogicOp);

            GL.PointSize(1.0f);
            GL.LineWidth(1.0f);

            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
            GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest);



        }

        /*! \brief Cleans up EZGL2D. Call this at program end.
	        Automatic texture management.*/

        public static void Close()
        {
            int[] textures = textureIDs.ToArray();
            GL.DeleteTextures(textures.Length, textures);

        }

        /*! \brief Sets the default blending/blitting mode
        \param mode enums for blending see example files.
        */
        public static void BlendMode(BLEND_MODE mode)
        {
            switch (mode)
            {
                case BLEND_MODE.GL2D_TRANS:
                case BLEND_MODE.GL2D_ALPHA:
                    GL.Enable(EnableCap.Blend);
                    GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
                    break;
                case BLEND_MODE.GL2D_SOLID:
                    GL.Disable(EnableCap.Blend);
                    break;
                case BLEND_MODE.GL2D_BLENDED:
                    GL.Enable(EnableCap.Blend);
                    GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.One);
                    break;
                case BLEND_MODE.GL2D_GLOW:
                    GL.Enable(EnableCap.Blend);
                    GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.One);
                    break;
                default:
                    break;
            }

        }

        /*! \brief Clears the GL2D screen buffer*/
        public static void ClearScreen()
        {

            GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

        }

        /*! \brief Draws a Pixel
            \param r Red channel of the color.
            \param g Green channel of the color.
            \param b Blue channel of the color.
            \param a Alpha channel of the color.
        */
        public static void SetColor(float r, float g, float b, float a)
        {
            GL.Color4(r, g, b, a);
        }

        /*! \brief Enables or disables AA depending on the switch
            \param flag true == enable; false(0) == disable.
        */
        public static void EnableAntialias(bool flag)
        {
            if (flag)
            {
                GL.Enable(EnableCap.PointSmooth);
                GL.Enable(EnableCap.LineSmooth);
            }
            else
            {
                GL.Disable(EnableCap.PointSmooth);
                GL.Disable(EnableCap.LineSmooth);
            }

        }

        /*! \brief Draws a Pixel
        \param x X position of the pixel.
        \param y Y position of the pixel.
        */
        public static void PutPixel(int x, int y)
        {
            GL.Disable(EnableCap.Texture2D);
            GL.Begin(PrimitiveType.Points);
            GL.Vertex2(x, y);
            GL.End();
            GL.Enable(EnableCap.Texture2D);

        }

        /*! \brief Draws a Line
          \param x1,y1 Start point of the line.
          \param x2,y2 End point of the line.
        */
        public static void Line(int x1, int y1, int x2, int y2)
        {

            GL.Disable(EnableCap.Texture2D);
            GL.Begin(PrimitiveType.Lines);
            GL.Vertex2(x1, y1);
            GL.Vertex2(x2, y2);
            GL.End();
            GL.Enable(EnableCap.Texture2D);

        }

        /*! \brief Draws a Box
            \param x1,y1 Top-Left coordinate of the box.
            \param x2,y2 Bottom-Right coordinate of the box.
        */
        public static void Box(int x1, int y1, int x2, int y2)
        {
            GL.Disable(EnableCap.Texture2D);

            GL.Begin(PrimitiveType.LineStrip);
            GL.Vertex2(x1, y1);
            GL.Vertex2(x2, y1);
            GL.Vertex2(x2, y2);
            GL.Vertex2(x1, y2);
            GL.Vertex2(x1, y1);
            GL.End();

            GL.Enable(EnableCap.Texture2D);

        }

        /*! \brief Draws a Box filled with a single color
         \param x1,y1 Top-Left coordinate of the box.
         \param x2,y2 Bottom-Right coordinate of the box.
        */
        public static void BoxFilled(int x1, int y1, int x2, int y2)
        {
            x2++;
            y2++;

            GL.Disable(EnableCap.Texture2D);
            GL.Begin(PrimitiveType.Quads);
            GL.Vertex2(x1, y1);
            GL.Vertex2(x1, y2);
            GL.Vertex2(x2, y2);
            GL.Vertex2(x2, y1);
            GL.End();
            GL.Enable(EnableCap.Texture2D);

        }

        /*! \brief Draws a Triangle
        \param x1,y1 Vertex 1 of the triangle.
        \param x2,y2 Vertex 2 of the triangle.
        \param x3,y3 Vertex 3 of the triangle.
        */
        public static void Triangle(int x1, int y1, int x2, int y2, int x3, int y3)
        {
            GL.Disable(EnableCap.Texture2D);
            GL.Begin(PrimitiveType.LineStrip);
            GL.Vertex2(x1, y1);
            GL.Vertex2(x2, y2);
            GL.Vertex2(x3, y3);
            GL.Vertex2(x1, y1);
            GL.End();
            GL.Enable(EnableCap.Texture2D);
        }

        /*! \brief Draws a filled Triangle
            \param x1,y1 Vertex 1 of the triangle.
            \param x2,y2 Vertex 2 of the triangle.
            \param x3,y3 Vertex 3 of the triangle.
        */
        public static void TriangleFilled(int x1, int y1, int x2, int y2, int x3, int y3)
        {
            GL.Disable(EnableCap.Texture2D);
            GL.Begin(PrimitiveType.Triangles);
            GL.Vertex2(x1, y1);
            GL.Vertex2(x2, y2);
            GL.Vertex2(x3, y3);
            GL.End();
            GL.Enable(EnableCap.Texture2D);
        }


        /*! \brief Draws a Sprite
        \param x X position of the sprite.
        \param y Y position of the sprite.
        \param flipmode mode for flipping (see FLIP_MODE constants).
        \param sprite a glImage. 
        */
        public static void Sprite(int x, int y, int flipmode, SpriteGL sprite)
        {
            int x1 = x;
            int y1 = y;
            int x2 = x + sprite.width;
            int y2 = y + sprite.height;

            float u1 = ((flipmode & SpriteGL.FLIP_H) != 0) ? sprite.u_off + sprite.u_width : sprite.u_off;
            float u2 = ((flipmode & SpriteGL.FLIP_H) != 0) ? sprite.u_off : sprite.u_off + sprite.u_width;
            float v1 = ((flipmode & SpriteGL.FLIP_V) != 0) ? sprite.v_off + sprite.v_height : sprite.v_off;
            float v2 = ((flipmode & SpriteGL.FLIP_V) != 0) ? sprite.v_off : sprite.v_off + sprite.v_height;


            if (sprite.textureID != currentTexture)
            {
                GL.BindTexture(TextureTarget.Texture2D, sprite.textureID);
                currentTexture = sprite.textureID;
            }

            GL.Begin(PrimitiveType.Quads);

            GL.TexCoord2(u1, v1);
            GL.Vertex2(x1, y1);
            GL.TexCoord2(u1, v2);
            GL.Vertex2(x1, y2);
            GL.TexCoord2(u2, v2);
            GL.Vertex2(x2, y2);
            GL.TexCoord2(u2, v1);
            GL.Vertex2(x2, y1);

            GL.End();


        }

        /*! \brief Draws a Center Rotated Axis-Exclusive Scaled Sprite
            \param x X position of the sprite center.
            \param y Y position of the sprite center.
            \param angle Angle(in degrees) to rotate the sprite.
            \param scaleX X-Axis scale value (1.0f is normal).
            \param scaleY Y-Axis scale value (1.0f is normal).
            \param flipmode mode for flipping (see FLIP_MODE constants).
            \param sprite a glImage. 
        */
        public static void SpriteRotateScale(int x, int y, float angle, float scaleX, float scaleY, int flipmode, SpriteGL sprite)
        {

            float hx = (sprite.width / 2) * scaleX;
            float hy = (sprite.height / 2) * scaleY;

            float hx1 = -hx;    // top left
            float hy1 = -hy;
            float hx2 = -hx;	// bottom left
            float hy2 = hy;
            float hx3 = hx;	    // bottom right
            float hy3 = hy;
            float hx4 = hx;	    // top right
            float hy4 = -hy;

            double a = angle * Math.PI / 180;
            float cz = (float)Math.Cos(a);
            float sz = (float)Math.Sin(a);

            float x1 = cz * hx1 - sz * hy1;
            float y1 = sz * hx1 + cz * hy1;
            float x2 = cz * hx2 - sz * hy2;
            float y2 = sz * hx2 + cz * hy2;
            float x3 = cz * hx3 - sz * hy3;
            float y3 = sz * hx3 + cz * hy3;
            float x4 = cz * hx4 - sz * hy4;
            float y4 = sz * hx4 + cz * hy4;

            float u1 = ((flipmode & SpriteGL.FLIP_H) != 0) ? sprite.u_off + sprite.u_width : sprite.u_off;
            float u2 = ((flipmode & SpriteGL.FLIP_H) != 0) ? sprite.u_off : sprite.u_off + sprite.u_width;
            float v1 = ((flipmode & SpriteGL.FLIP_V) != 0) ? sprite.v_off + sprite.v_height : sprite.v_off;
            float v2 = ((flipmode & SpriteGL.FLIP_V) != 0) ? sprite.v_off : sprite.v_off + sprite.v_height;

            if (sprite.textureID != currentTexture)
            {
                GL.BindTexture(TextureTarget.Texture2D, sprite.textureID);
                currentTexture = sprite.textureID;
            }

            GL.Begin(PrimitiveType.Quads);

            GL.TexCoord2(u1, v1);
            GL.Vertex2(x + x1, y + y1);
            GL.TexCoord2(u1, v2);
            GL.Vertex2(x + x2, y + y2);
            GL.TexCoord2(u2, v2);
            GL.Vertex2(x + x3, y + y3);
            GL.TexCoord2(u2, v1);
            GL.Vertex2(x + x4, y + y4);

            GL.End();

        }


        /*! \brief Draws a textured line
            \param x1,y1 Start point of the line.
            \param x2,y2 End point of the line.
            \param thickness Thickness of the line.
            \param sprite a glImage used to texture the line.
        */
        public static void SpriteOnLine(int x1, int y1, int x2, int y2, int thickness, SpriteGL sprite)
        {


            // Only change active texture when there is a need
            // Speeds up the rendering by batching textures
            if (sprite.textureID != currentTexture)
            {
                GL.BindTexture(TextureTarget.Texture2D, sprite.textureID);
                currentTexture = sprite.textureID;
            }


            float nx = -(y2 - y1);
            float ny = (x2 - x1);

            float invLength = 1 / (float)(Math.Sqrt(nx * nx + ny * ny));

            nx *= invLength;
            ny *= invLength;

            nx *= thickness / 2.0f;
            ny *= thickness / 2.0f;

            float vx = (x2 - x1);
            float vy = (y2 - y1);
            invLength = 1 / (float)(Math.Sqrt(vx * vx + vy * vy));
            vx *= invLength;
            vy *= invLength;

            vx *= thickness / 2.0f;
            vy *= thickness / 2.0f;


            float lx1 = x2 + nx;
            float ly1 = y2 + ny;
            float lx2 = x2 - nx;
            float ly2 = y2 - ny;
            float lx3 = x1 - nx;
            float ly3 = y1 - ny;
            float lx4 = x1 + nx;
            float ly4 = y1 + ny;
            float lx5 = lx1 + vx;
            float ly5 = ly1 + vy;
            float lx6 = lx2 + vx;
            float ly6 = ly2 + vy;
            float lx7 = lx4 - vx;
            float ly7 = ly4 - vy;
            float lx8 = lx3 - vx;
            float ly8 = ly3 - vy;

            float u1 = sprite.u_off;
            float v1 = sprite.v_off;
            float u2 = sprite.u_off + sprite.u_width;
            float v2 = sprite.v_off + sprite.v_height;
            float su = (u1 + u2) / 2.0f;

            // center
            spriteOnQuad(lx1, ly1,
                          lx2, ly2,
                          lx3, ly3,
                          lx4, ly4,
                          su, v2,
                          su, v1
                          );

            // right
            spriteOnQuad(lx5, ly5,
                          lx6, ly6,
                          lx2, ly2,
                          lx1, ly1,
                          u2, v2,
                          su, v1
                          );

            // left
            spriteOnQuad(lx8, ly8,
                          lx7, ly7,
                          lx4, ly4,
                          lx3, ly3,
                          u1, v1,
                          su, v2
                          );

        }



        /*! \brief Loads an image file(prefer PNGs) directly to a SpriteGL object <Br>
            Image file needs to be have power of 2 dimension(2, 4, 8,.... 2048,...) 
            \param filename filename of the TGA file to load ie. "ships.png".
            \param filtermode TextureMinFilter.Linear, TextureMinFilter.Nearest, etc.
            \return SpriteGL that can be directly used by the sprite routines.
        */
        public static SpriteGL LoadSprite(string filename, int filtermode)
        {

            Bitmap bitmap = new Bitmap(filename);

            int textureID;

            GL.Enable(EnableCap.Texture2D);

            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);

            GL.GenTextures(1, out textureID);
            GL.BindTexture(TextureTarget.Texture2D, textureID);

            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, filtermode);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, filtermode);

            BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);

            bitmap.UnlockBits(data);


            SpriteGL sprite = new SpriteGL();

            sprite.textureID = textureID;
            sprite.width = bitmap.Width;
            sprite.height = bitmap.Height;
            sprite.u_off = 0.0f; // set x-coord
            sprite.v_off = 0.0f; // y-coord
            sprite.u_width = 1.0f; // set x-coord
            sprite.v_height = 1.0f; // y-coord

            GL.BindTexture(TextureTarget.Texture2D, 0);

            bitmap.Dispose();

            textureIDs.Add(textureID);

            return sprite;

        }


        /*! \brief Initializes our spriteset with Texture Packer generated UV coordinates <Br>
	         Very safe and easy to use..
            Image file needs to be have power of 2 dimension(2, 4, 8,.... 2048,...) 
            \param filename filename of the image file to load ie. "ships.png".
            \param numframes number of frames in a spriteset.
            \param texcoords Texture Packer auto-generated array of UV coords.
            \param filtermode TextureMinFilter.Linear, TextureMinFilter.Nearest, etc.
            \return SpriteGL array that can be directly used by the sprite routines.
        */
        public static SpriteGL[] LoadSpriteSet(string filename, int numframes, int[] texcoords, int filtermode)
        {

            IList<GL2D.SpriteGL> tileSet = new List<GL2D.SpriteGL>();

            Bitmap bitmap = new Bitmap(filename);

            int textureID;

            GL.Enable(EnableCap.Texture2D);

            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);

            GL.GenTextures(1, out textureID);
            GL.BindTexture(TextureTarget.Texture2D, textureID);

            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, filtermode);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, filtermode);

            BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);

            int width = bitmap.Width;
            int height = bitmap.Height;

            bitmap.UnlockBits(data);

            GL.BindTexture(TextureTarget.Texture2D, 0);

            // init sprites texture coords and texture ID
            for (int i = 0; i < numframes; i++)
            {
                int j = i * 4; // texcoords array is u_off, wid, hei
                SpriteGL sprite = new SpriteGL();
                sprite.textureID = textureID;
                sprite.width = texcoords[j + 2];
                sprite.height = texcoords[j + 3];
                sprite.u_off = texcoords[j] / (float)width; // set x-coord
                sprite.v_off = texcoords[j + 1] / (float)height; // y-coord
                sprite.u_width = texcoords[j + 2] / (float)width; // set x-coord
                sprite.v_height = texcoords[j + 3] / (float)height; // y-coord
                tileSet.Add(sprite);
            }

            textureIDs.Add(textureID);

            bitmap.Dispose();

            return tileSet.ToArray();

        }


        /*! \brief Initializes our Tileset. <Br>
	        Can only be used when tiles in a tilset are of the same dimensions.
            Image file needs to be have power of 2 dimension(2, 4, 8,.... 2048,...) 
            \param filename filename of the image file to load ie. "ships.png".
            \param tileWidth Width of each tile in the texture.
            \param tileHeight Height of each tile in the texture.
            \param filtermode TextureMinFilter.Linear, TextureMinFilter.Nearest, etc.
            \return SpriteGL array that can be directly used by the sprite routines.
        */
        public static SpriteGL[] LoadTileSet(string filename, int tileWidth, int tileHeight, int filtermode)
        {

            IList<GL2D.SpriteGL> tileSet = new List<GL2D.SpriteGL>();

            Bitmap bitmap = new Bitmap(filename);

            int textureID;

            GL.Enable(EnableCap.Texture2D);

            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);

            GL.GenTextures(1, out textureID);
            GL.BindTexture(TextureTarget.Texture2D, textureID);

            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, filtermode);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, filtermode);

            BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
                ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);


            bitmap.UnlockBits(data);

            GL.BindTexture(TextureTarget.Texture2D, 0);

            int textureWidth = bitmap.Width;
            int textureHeight = bitmap.Height;

            // init sprites texture coords and texture ID
            for (int y = 0; y < (textureHeight / tileHeight); y++)
            {
                for (int x = 0; x < (textureWidth / tileWidth); x++)
                {
                    SpriteGL sprite = new SpriteGL();
                    sprite.width = tileWidth;
                    sprite.height = tileHeight;
                    sprite.u_off = (x * tileWidth) / (float)textureWidth;
                    sprite.v_off = (y * tileHeight) / (float)textureHeight;
                    sprite.u_width = tileWidth / (float)textureWidth;
                    sprite.v_height = tileHeight / (float)textureHeight;
                    sprite.textureID = textureID;
                    tileSet.Add(sprite);
                }
            }

            textureIDs.Add(textureID);

            return tileSet.ToArray();
        }

        private static void spriteOnQuad(float x1,
                               float y1,
                               float x2,
                               float y2,
                               float x3,
                               float y3,
                               float x4,
                               float y4,
                               float u1,
                               float v1,
                               float u2,
                               float v2)
        {


            GL.Begin(PrimitiveType.Quads);

            GL.TexCoord2(u1, v1);
            GL.Vertex2(x1, y1);
            GL.TexCoord2(u1, v2);
            GL.Vertex2(x2, y2);
            GL.TexCoord2(u2, v2);
            GL.Vertex2(x3, y3);
            GL.TexCoord2(u2, v1);
            GL.Vertex2(x4, y4);

            GL.End();
        }


    }   // GL2D

}   // namespace
