Haighstrom's picture

[Solved] 2D Game: Layers

Hi all,

I'm gradually making progress in learning to use OpenTK to make a 2D game.

I'm using some variation of the following code to use an orthographic projection, such that I place all my Quads parallel to the Z=0 plane, with Z between 0 and 1.

GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(0, Width, Height, 0, 0, 1);

My next question is around layers, i.e. priority of drawing order.

No matter which Z I choose, the images always appear in the order of draw calls, presumably because the Orthographic projection flattens everything. This has the benefit that images are drawn direct to screen coordinates at the right size, but I lose the ability to set layers using position.

The question is, is there a way of using the Matrix Modes to allow Z coordinate to define the layer the image is drawn on, or do I have to implement code to sort the draw orders?

If my only option is the latter, I have code I can reuse from some old AS3 games (basically presorting the images when their layer value is set, so no sorting algorithm is needed within the Render calls).

Thanks
Haighstrom


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
winterhell's picture

For sprites/textures with completely opaque pixels you can use a Z-buffer like usual. If your texture has 100% transparent pixels, those could be alpha tested and discarded(through fixed fuctions and shaders). If you have, however, semi-transparent pixels that you wish to be semitransparent, you'll have to z-sort. Granted with sprites that thing is easy and fast to do as opposed to 3D where you have all different camera angles and intertwining triangles and meshes.
You can mark as well which sprites have semi-transparent pixels and which have only 0/100%, so that you have to sort only the semi-transparent. Those would have to be rendered after the opaque ones of course.
That is how I do it in my projects, and it works nice.

Dont be afraid to sort 1000 sprites every frame, it runs fine.

Haighstrom's picture

Thanks Winterhell. How do I utilise the z-buffer, do you have a code sample? I'll attempt this first before moving onto transparency.

I presume I start with:
GL.Enable( EnableCap.DepthTest )

What's next?

winterhell's picture

Yes EnableCap.DepthTest should do the trick. Things drawn with closer Z would obscure those with farther one.
You can research
EnableCap.AlphaTest for 100% transparent pixels(those wont write to the depth buffer, as if they dont exist)
EnableCap.Blend for semitransparent, you render them sorted from back to front.
I don't know if those two can work together.

Haighstrom's picture

Hi,

I got Layers working (thanks) but I can't get Alpha to work.

One image is displaying a white background, and the other a black one. The images are .PNGs if that is of any significance.

Below are the snippets that might be relevant. Any ideas?
Thanks Haighstrom
.

Main Render Loop (attempting to draw two images overlapping, with alpha and layers):

protected override void OnRenderFrame(FrameEventArgs e)
{
    // clear screen
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit );
    GL.ClearColor(HV.ScreenColour);
 
    //enable 2D textures, layers and alpha
    GL.Enable(EnableCap.Texture2D);
    GL.Enable(EnableCap.DepthTest);
    GL.Enable(EnableCap.AlphaTest);
    //GL.Enable(EnableCap.Blend);
 
    //set the projector to orthographic, so size of objects will remain fixed
    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadIdentity();
    GL.Ortho(0, -Width, Height, 0, 0, 1);
 
    //mapping the model coordinates to world coordinates (so coords are in pixels)
    GL.MatrixMode(MatrixMode.Modelview);
    Matrix4 modelview = Matrix4.LookAt(0, 0, 0, 0, 0, 1, 0, 1, 0);
    GL.LoadMatrix(ref modelview);
 
    //draw stuff here
    Bear.Render();
    Cross.Render();
 
    SwapBuffers();
    base.OnRenderFrame(e);
}

Rendering an individual object (called by object.Render() above):

public override void RenderToScreen(int x, int y, int w, int h, float layer)
{
    GL.BindTexture(TextureTarget.Texture2D, TextureID);
    GL.Begin(PrimitiveType.Quads);
    GL.TexCoord2(0, 0); GL.Vertex3(x, y, layer);
    GL.TexCoord2(1, 0); GL.Vertex3(x + w, y, layer);
    GL.TexCoord2(1, 1); GL.Vertex3(x + w, y + h, layer);
    GL.TexCoord2(0, 1); GL.Vertex3(x, y + h, layer);
    GL.End();
}

Texture Loading:

public static int LoadTexture(string path)
{
    if (String.IsNullOrEmpty(path)) throw new ArgumentException(path);
 
    int id = GL.GenTexture();
    GL.BindTexture(TextureTarget.Texture2D, id);
 
    Bitmap BMP = new Bitmap(path);
 
    BitmapData bmpData = BMP.LockBits(new Rectangle(0, 0, BMP.Width, BMP.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
    GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmpData.Width, bmpData.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bmpData.Scan0);
 
    BMP.UnlockBits(bmpData);
 
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Nearest);
    GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Nearest);
 
    return id;
}
winterhell's picture

Try these 3 things one by one to see for differences.
-Check that the png are 32 bit and actually transparent.
-Do EnableCap.Blend instead of AlphaTest
-Render from back to front

Haighstrom's picture

No luck I'm afraid.

PNG:
http://i62.tinypic.com/whk7tc.png

Blend:
http://i62.tinypic.com/1zn1u7b.png

Changing drawing order: no need to check this, as even just drawing one image does the same thing - image gets a background colour.

If you're willing to investigate, here's a link to my project (it's still nice and trim so should be easy to navigate):
https://www.dropbox.com/s/smzlb8mrmabi35j/HaighEngine.7z

Thanks
Haighstrom

Haighstrom's picture

I borrowed code from a similar question that's popped up and got it working. Using BlendMode and adding this line solves the issue:

GL.BlendFunc(BlendingFactorSrc.Src1Alpha, BlendingFactorDest.OneMinusSrcAlpha);

No doubt later on I'll run into problems with semi-transparency or shading things in a given colour but this will keep me going for now.

Thanks
Haighstrom