Search code examples
c#openglopentk

GL.BindTexture is acting strange


I've run into a curious thing after banging my head for almost 3 days now.

I'm basically trying to create a map application that in essence first draws a map texture to an OpenTK.GLControl, then creates several "dots" onto this map representing entities, and then lastly print the names of the entities beside each dot.

I've finally managed to do just that, see this image here.

And here is the paint method for achieving this:

    private void glControl_Paint(object sender, PaintEventArgs e)
    {
        if (!glControlLoaded)
            return;

        GL.Clear(ClearBufferMask.ColorBufferBit);

        if (currentMapImage != null)
        {
            GL.BindTexture(TextureTarget.Texture2D, mapTextureId);

            GL.Enable(EnableCap.Texture2D);
            GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Decal);
            GL.Begin(PrimitiveType.Quads);
            GL.TexCoord2(currentZoomRectangle.Left, currentZoomRectangle.Top); GL.Vertex3(0, 0, 0.0f);
            GL.TexCoord2(currentZoomRectangle.Right, currentZoomRectangle.Top); GL.Vertex3(glControl.Width, 0, 0.0f);
            GL.TexCoord2(currentZoomRectangle.Right, currentZoomRectangle.Bottom); GL.Vertex3(glControl.Width, glControl.Height, 0.0f);
            GL.TexCoord2(currentZoomRectangle.Left, currentZoomRectangle.Bottom); GL.Vertex3(0, glControl.Height, 0.0f);
            GL.End();
            GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Modulate);
            GL.Disable(EnableCap.Texture2D);
        }

        GL.Begin(PrimitiveType.Points);
        foreach (Entity entity in entityList)
        {
            if (filtersForm.getShowEntity(entity.Name))
            {
                if (filtersForm.getShowDot(entity.TypeConverted))
                {
                    Color dotColor = settingsForm.getDotColor(entity.TypeConverted);
                    GL.Color3(dotColor);

                    PointF? dotGlControlPos = getGlControlPos(entity.ZeroToOnePosition);
                    if (dotGlControlPos != null)
                    {
                        GL.Vertex3(dotGlControlPos.Value.X, dotGlControlPos.Value.Y, 0.0);
                    }
                }
            }
        }
        GL.End();

        using (Graphics gfx = Graphics.FromImage(textBitmap))
        {
            gfx.Clear(Color.Transparent);
            gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

            foreach (Entity entity in entityList)
            {
                if (filtersForm.getShowEntity(entity.Name))
                {
                    if (filtersForm.getShowDot(entity.TypeConverted))
                    {
                        if (filtersForm.getShowName(entity.TypeConverted))
                        {
                            PointF? nameGlControlPos = getGlControlPos(entity.ZeroToOnePosition);
                            if (nameGlControlPos != null)
                            {
                                Color nameColor = nameColor = settingsForm.getNameColor(entity.TypeConverted);
                                nameGlControlPos = new PointF(nameGlControlPos.Value.X + 5, nameGlControlPos.Value.Y - settingsForm.EntityFontHeightInPixels - 5);
                                gfx.DrawString(entity.Name, settingsForm.EntityFont, new SolidBrush(nameColor), nameGlControlPos.Value);
                            }
                        }
                    }
                }
            }
        }

        GL.BindTexture(TextureTarget.Texture2D, textTextureId);

        System.Drawing.Imaging.BitmapData data = textBitmap.LockBits(new Rectangle(0, 0, textBitmap.Width, textBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, glControl.Width, glControl.Height, PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
        textBitmap.UnlockBits(data);

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

        GL.Begin(PrimitiveType.Quads);
        GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(0, 0, -0.1f);
        GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(glControl.Width, 0, -0.1f);
        GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(glControl.Width, glControl.Height, -0.1f);
        GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(0, glControl.Height, -0.1f);
        GL.End();

        GL.BindTexture(TextureTarget.Texture2D, mapTextureId);

        glControl.SwapBuffers();
    }

Now the funny thing is, if I remove the last GL.BindTexture()-call in the above method (that is the one before the glControl.SwapBuffers()-call), like this:

private void glControl_Paint(object sender, PaintEventArgs e)
{
    ...
    ...
    ...

    //GL.BindTexture(TextureTarget.Texture2D, mapTextureId);

    glControl.SwapBuffers();
}

I get this as a result.

I have no clue as to how I came up with the idea to add that call in the first place. It doesn't make any sense to me... I thought I would actually just need to call it once (in the beginning when I'm actually drawing the map texture).

So I guess my question is, can anyone tell me what's going on here?


Solution

  • What's going on is that you didn't bind mapTextureId before you tried to render the map.

    See, when you did GL.BindTexture(TextureTarget.Texture2D, textTextureId);, that means that all further rendering will use that texture. Well, until the next bind command. Which you just commented out.

    The only other time you bound mapTextureId was when you first created the texture; you never bound it again, except in the line you commented out.

    State in OpenGL doesn't change just because you forget about it. The reason your code worked was because of that line you commented out. By binding the texture, you're saying that any future rendering commands will use that texture. Even if those future rendering commands happen on the next frame.

    Basically, your code worked by accident.

    What you ought to do is bind the texture, render the stuff you want to render with that texture, then unbind the texture (by binding 0).