Search code examples
c#winformsopenglopentk

Creating glControl Window Realtime


Basically, I am trying to create a glControl window on Widows Forms at realtime. I'm doing this because I will be using multiple windows, and more likely readjusting their position, and I don't want to be doing this manually.

I don't understand why my code doesn't work. Here it is:

using System;
using System.Drawing;
using System.Windows.Forms;

using System.Diagnostics;

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

namespace Draw3Dv01wf
{
    public partial class Form1 : Form
    {
        GLControl renderCanvas1;
        int winX, winY, winW, winH;

        public Form1()
        {
            // required method for designer support
            InitializeComponent();
        }


        // load event handler
        private void Form1_Load(object sender, EventArgs e)
        {
            // create and setup gl.control windows
            this.renderCanvas1 = new GLControl();
            //this.SuspendLayout();

            winX = this.Location.X; winY = this.Location.Y;
            winW = this.Width; winH = this.Height;

            this.renderCanvas1.BackColor = System.Drawing.Color.CadetBlue;
            this.renderCanvas1.Location = 
                new System.Drawing.Point(winX + 50, winY + 50);
            this.renderCanvas1.Name = "renderCanvas1";
            this.renderCanvas1.Size = new System.Drawing.Size(winW/3, winH/2);
            this.renderCanvas1.TabIndex = 1;
            this.renderCanvas1.VSync = false;
            this.renderCanvas1.Load += 
                new System.EventHandler(this.renderCanvas_Load);
            this.renderCanvas1.Paint += 
                new System.Windows.Forms.PaintEventHandler(
                    this.renderCanvas_Paint);
            //renderCanvas1.Paint += renderCanvas_Paint;
            //this.ResumeLayout(false);
        }

        private void renderCanvas_Paint(object sender, PaintEventArgs e)
        {
            GL.Viewport(winX + 20, winY + 25, Width, Height);

            // Clear the render canvas with the current color
            GL.Clear(
                ClearBufferMask.ColorBufferBit | 
                ClearBufferMask.DepthBufferBit);

            GL.Flush();
            renderCanvas1.SwapBuffers();
        }

        private void renderCanvas_Load(object sender, EventArgs e)
        {
            // Specify the color for clearing
            GL.ClearColor(Color.SkyBlue);
        }
    }
}

I'm just seeing the windows form. The glControl window doesn't show. However, when I add the glControl to the form manually, and add this code:

    //____________________________________________

    private void glControl1_Load(object sender, EventArgs e)
    {
        // Specify the color for clearing
        GL.ClearColor(Color.SkyBlue);

        this.Paint += new PaintEventHandler(glControl1_Paint);
    }

    private void glControl1_Paint(object sender, PaintEventArgs e)
    {
        GL.Viewport(this.Location.X, this.Location.Y, Width, Height);

        // Clear the render canvas with the current color
        GL.Clear(
            ClearBufferMask.ColorBufferBit |
            ClearBufferMask.DepthBufferBit);

        GL.Flush();
        glControl1.SwapBuffers();
    }

    //________________________________________________________

... this window shows up on the form.

The code in my program is no different to the code in the designer, so I am completely puzzled.

This is the designer code:

    this.glControl1 = new OpenTK.GLControl();
    this.SuspendLayout();
    // 
    // glControl1
    // 
    this.glControl1.BackColor = System.Drawing.Color.Black;
    this.glControl1.Location = new System.Drawing.Point(385, 12);
    this.glControl1.Name = "glControl1";
    this.glControl1.Size = new System.Drawing.Size(476, 284);
    this.glControl1.TabIndex = 0;
    this.glControl1.VSync = false;
    this.glControl1.Load += new System.EventHandler(this.glControl1_Load);
    // 
    // Form1
    // 
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.BackColor = System.Drawing.SystemColors.Desktop;
    this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
    this.ClientSize = new System.Drawing.Size(1264, 681);
    this.Controls.Add(this.glControl1);
    this.HelpButton = true;
    this.MaximumSize = new System.Drawing.Size(3840, 2160);
    this.MinimumSize = new System.Drawing.Size(640, 480);
    this.Name = "Form1";
    this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
    this.Text = "Draw3D";
    this.ResumeLayout(false);

Solution

  • I tried something by creating another glControl, but this time, not in the Load event handler of the Form.

    public Form1()
    {
        // required method for designer support
        InitializeComponent();
        initSetup();
    }
    
    private void initSetup()
    {
        winX = this.Location.X; winY = this.Location.Y;
        winW = this.Width; winH = this.Height;
    
        this.renderCanvas2 = new GLControl();
        this.renderCanvas2.BackColor = System.Drawing.Color.DeepSkyBlue;
        this.renderCanvas2.Location =
            new System.Drawing.Point(winX + 150, winY + 150);
        this.renderCanvas2.Name = "renderCanvas2";
        this.renderCanvas2.Size = 
            new System.Drawing.Size(winW / 2, winH / 2);
        this.renderCanvas2.TabIndex = 1;
        this.renderCanvas2.VSync = false;
        this.renderCanvas2.Load +=
            new System.EventHandler(this.renderCanvas2_Load);
        this.renderCanvas2.Paint +=
            new System.Windows.Forms.PaintEventHandler(
                this.renderCanvas2_Paint);
    
        this.Controls.Add(this.renderCanvas2);
    }
    

    ... and it worked. I don't know why that is. It also caused the manually created glControl window to change to black - in it's original location, or white - if moved. However, at least I got results. I'll continue to play with it and see what I can figure out. I have a feeling it may be buffer related. The other option I have is to create the windows manually, and adjust their position and size by code, since that also works. I still would welcome any input on why the code does not work in the Form's Load Event Handler, if anyone knows. Thanks

    EDIT I'm making progress. I just have one final step. So now I am using an array of glControls to create my windows, but I didn't want to create multiple event handlers to manage each window, so I am trying to get results with just one Load event and one Paint event. However, I am having trouble with setting the color on all the windows. I'm not sure if this method is possible. I'd really appreciate any help on this. Here is my test code:

    using...
    
    namespace Draw3Dv01wf
    {
        public partial class Form1 : Form
        {
            GLControl[] renderCanvas = new GLControl[10];
            private int 
                winX, winY, winW, winH, winDist, winNum, 
                aCol, rCol, gCol, bCol;
            Random randNum = new Random();
            //byte[] num = new byte[255];
            byte r, g, b, a;
            Color4 winCol;
            private bool winCreated;
            private bool eHandlerIs;
    
            TextBox tb = new TextBox();
    
            public Form1()
            {
                // required method for designer support
                InitializeComponent();
                initSetup();
            }
    
            private void initSetup()
            {
                winX = this.Location.X; winY = this.Location.Y;
                winW = this.Width; winH = this.Height;
                rCol = 255; gCol = 255; bCol = 255; aCol = 255;
    
                // debugging text
                tb.Location = new Point(5, 5);
                tb.Size = new Size(200, 15);
                tb.Name = "textBox1";
                tb.TabIndex = 0;
                tb.BackColor = Color.Black;
                tb.ForeColor = Color.White;
                tb.Text = winNum.ToString();
                this.Controls.Add(this.tb);
    
                // create windows
                for (int w=0; w<8; w++)
                {
                    // window distance 
                    winDist += 32;
                    // make sure window with index 0 is created
                    if (winCreated) { winNum += 1; }
    
                    // create windows 
                    this.renderCanvas[w] = new GLControl();
                    //this.SuspendLayout();
                    this.renderCanvas[w].BackColor = 
                        System.Drawing.Color.DeepSkyBlue;
                    this.renderCanvas[w].Location =
                        new System.Drawing.Point(winX + winDist, winY + winDist);
                    this.renderCanvas[w].Name = "renderCanvas" + w;
                    this.renderCanvas[w].Size =
                        new System.Drawing.Size(winW / 2, winH / 2);
                    this.renderCanvas[w].TabIndex = 1;
                    this.renderCanvas[w].VSync = false;
                    // create event handler 
                    this.renderCanvas[w].Load +=
                        new System.EventHandler(this.renderCanvas_Load);
                    if (!eHandlerIs)
                    {
                        this.renderCanvas[w].Paint +=
                            new System.Windows.Forms.PaintEventHandler(
                                this.renderCanvas_Paint);
                        //eHandlerIs = true;
                    }
                    //this.ResumeLayout(false);
    
                    // add specified control to the control collection 
                    this.Controls.Add(this.renderCanvas[w]);
                    winCreated = true; // first window created
                }
            }
    
            private void renderCanvas_Paint(object sender, PaintEventArgs e)
            {
                tb.Text = r.ToString();
                if (winNum < 7)
                {
                    //GL.Viewport(
                    //    renderCanvas[winNum].Location.X,
                    //    renderCanvas[winNum].Location.Y, Width, Height);
    
                    //// Clear the render canvas with the current color
                    //GL.Clear(
                    //    ClearBufferMask.ColorBufferBit |
                    //    ClearBufferMask.DepthBufferBit);
    
                    //GL.Flush();
                    //renderCanvas[winNum].SwapBuffers();
                }
                else if (winNum >= 7)
                {
                    for (int w = 0; w < 8; w++)
                    {
                        GL.Viewport(
                        renderCanvas[w].Location.X,
                        renderCanvas[w].Location.Y, Width, Height);
    
                        // Clear the render canvas with the current color
                        GL.Clear(
                            ClearBufferMask.ColorBufferBit |
                            ClearBufferMask.DepthBufferBit);
    
                        GL.Flush();
                        renderCanvas[w].SwapBuffers();
                    }
                }
            }
    
            private void renderCanvas_Load(object sender, EventArgs e)
            {
                // randomize color (min & max int)
                rCol = randNum.Next(100, 255);
                gCol = randNum.Next(100, 255);
                bCol = randNum.Next(100, 255);
                aCol = 255;
                // convert int to (32) byte
                r = (byte)(rCol >> 32);
                g = (byte)(gCol >> 32);
                b = (byte)(bCol >> 32);
                a = (byte)(aCol >> 32);
                // window final color
                winCol = new Color4(r, g, b, a);
    
                // Specify the color for clearing
                GL.ClearColor(winCol);
            }
        }
    }
    

    ... and here is the results: enter image description here

    Only one window gets a color. All the other turn black. I appreciate any feedback.

    EDIT Got it! MakeCurrent to the rescue. void OpenTK.GLControl.MakeCurrent ( )
    Makes the underlying this GLControl current in the calling thread. All OpenGL commands issued are hereafter interpreted by this GLControl. Here is an update of the code:

    GLControl[] renderCanvas = new GLControl[10];
    private int 
        winX, winY, winW, winH, winDist, winNum, win,
        aCol, rCol, gCol, bCol;
    Random randNum = new Random();
    //byte[] num = new byte[255];
    byte r, g, b, a;
    Color4 winCol;
    private bool winCreated;
    private bool eHandlerIs;
    
    TextBox tb = new TextBox();
    
    public Form1()
    {
        // required method for designer support
        InitializeComponent();
        initSetup();
    }
    
    private void initSetup()
    {
        winX = this.Location.X; winY = this.Location.Y;
        winW = this.Width; winH = this.Height;
        rCol = 255; gCol = 255; bCol = 255; aCol = 255;
    
        // debugging text
        tb.Location = new Point(5, 5);
        tb.Size = new Size(200, 15);
        tb.Name = "textBox1";
        tb.TabIndex = 0;
        tb.BackColor = Color.Black;
        tb.ForeColor = Color.White;
        tb.Text = winNum.ToString();
        this.Controls.Add(this.tb);
    
        // create windows
        for (int w=0; w<8; w++)
        {
            // window distance 
            winDist += 32;
            // make sure window with index 0 is created
            if (winCreated) { winNum += 1; }
    
            // create windows 
            this.renderCanvas[w] = new GLControl();
            //this.SuspendLayout();
            this.renderCanvas[w].BackColor = 
                System.Drawing.Color.DeepSkyBlue;
            this.renderCanvas[w].Location =
                new System.Drawing.Point(winX + winDist, winY + winDist);
            this.renderCanvas[w].Name = "renderCanvas" + w;
            this.renderCanvas[w].Size =
                new System.Drawing.Size(winW / 2, winH / 2);
            this.renderCanvas[w].TabIndex = 1;
            this.renderCanvas[w].VSync = false;
            // create event handler 
            this.renderCanvas[w].Load +=
                new System.EventHandler(this.renderCanvas_Load);
            if (!eHandlerIs)
            {
                this.renderCanvas[w].Paint +=
                    new System.Windows.Forms.PaintEventHandler(
                        this.renderCanvas_Paint);
                //eHandlerIs = true;
            }
            //this.ResumeLayout(false);
    
            // add specified control to the control collection 
            this.Controls.Add(this.renderCanvas[w]);
            winCreated = true; // first window created
        }
    }
    
    private void renderCanvas_Paint(object sender, PaintEventArgs e)
    {
        tb.Text = r.ToString();
        if (winNum < 7)
        {
    
        }
        else if (winNum >= 7)
        {
            for (int w = 0; w < 8; w++)
            {
                for (int n = 0; n < 8; n++)
                {
                    if (w != n)
                    {
                        if (renderCanvas[n].Created &&
                            renderCanvas[n].Context.IsCurrent)
                        { renderCanvas[n].Context.MakeCurrent(null); }
                    }
                }
    
                if (renderCanvas[w].Context.IsCurrent == false)
                { renderCanvas[w].MakeCurrent(); }
    
                GL.Viewport(
                renderCanvas[w].Location.X,
                renderCanvas[w].Location.Y, Width, Height);
    
                // Clear the render canvas with the current color
                GL.Clear(
                    ClearBufferMask.ColorBufferBit |
                    ClearBufferMask.DepthBufferBit);
    
                GL.Flush();
                renderCanvas[w].SwapBuffers();
            }
        }
    }
    
    private void renderCanvas_Load(object sender, EventArgs e)
    {
        // randomize color (min & max int)
        rCol = randNum.Next(100, 255);
        gCol = randNum.Next(100, 255);
        bCol = randNum.Next(100, 255);
        aCol = 255;
        // convert int to (32) byte
        r = (byte)(rCol >> 32);
        g = (byte)(gCol >> 32);
        b = (byte)(bCol >> 32);
        a = (byte)(aCol >> 32);
        // window final color
        winCol = new Color4(r, g, b, a);
    
        for (int n = 0; n < 8; n++)
        {
            if (win != n)
            {
                if (renderCanvas[n].Created && 
                    renderCanvas[n].Context.IsCurrent)
                { renderCanvas[n].Context.MakeCurrent(null); }
            }
        }
    
        if (renderCanvas[win].Context.IsCurrent == false)
        { renderCanvas[win].MakeCurrent(); }
    
        // Specify the color for clearing
        GL.ClearColor(winCol);
        win += 1;
    }
    

    ... and the result: enter image description here