djk's picture

GameWindow as an MDI child

Success is sometimes you own worst enemy.

The application I am working on is a ship seakeeping simulator, up to now the glControl has provided all the functionality that was needed, the use cases we focused on were similar to what you would expect in any object oriented cad modeling system.

We are now at the point where simulation data is being computed and we are plotting results in a 2d plotting control (ZedGraph). We had a very sucessful demonstration last week and the sponsor being more of a manager has asked that we provide a 3d view of the simulation for non-engineers.

The GameWindow has all the functionality that I need to create the 3d view of the simulation. The issue is that the application is built on an MDI framework and it seems the GameWindow does not currently support the MdiParent property.

I have looked through the code this morning and it is not obvious to me where to start or that it is even possible. I would not be adverse to creating a GameControl/SimControl etc that implements most of the GameWindow functionality to donate back to the OpenTK project if that is the best choice. Ideas, comments anyone?

djk


Comments

Comment viewing options

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

I have never coded an application that uses MDI - it might be possible to add support in the current GameWindow, but I do not so know what it would take and what the limitations would be (but see below).

GameWindow is designed to work as a standalone top-level window, and does not follow the Winfows.Forms programming model (isn't MdiParent a Windows.Forms property?) Moreover, it runs its own event loop, so it cannot be hosted in a Windows.Forms application in a straightforward way - at least in its current incarnation.

What are the options?

One option is to create a new GameWindow "driver", on top of a Windows.Forms Control/Form. The "driver", actually an implementation of the INativeGLWindow interface, is responsible for event processing, which means it can be made to play nicely inside a Windows.Forms application.

The other option is to create a brand new GameControl/SimControl with the necessary functionality (probably on top of the low-level GLControl).

The first option should be much simpler, as it allows to reuse the existing GameWindow logic and interface. The drawback is that a) there is no API for embedding/setting the parent of a GameWindow (this can be changed though) and b) you'd need to hack OpenTK itself, as there is no API to provide alternative implementations at this point.

The second option has the advantage that it can be implemented on top of the public OpenTK API (which is more stable) and allows you to control exactly what goes into the implementation (GameWindow might contain things you do not need). On the other hand it would require more coding.

Personally, I'd say the first option is better. INativeGLWindow basically provides methods for creating, destroying and resizing windows, which are trivial to implement on top of a Windows.Forms.Form. This leaves the matter of making the frontend (GameWindow) Mdi-aware: the current GameWindow implementation decides which "driver" to use inside the constructor, so the simplest solution would be to add a new constructor with an MdiParent parameter, which would select the Windows.Forms "driver".

What do you think?

djk's picture

I understand most of what you said, I will look through the INativeGLWindow in more detail this afternoon to digest the rest of you reply.

djk's picture

I agree that a Forms implementation of INativeGLWindow is the best long term solution, I am going to discuss this with the other developers in the office.

In the short term I have managed to get the following snippet to work, the issue is as you indicated it does not play nice with the MDI application because of the event loop, and I doubt there is an equivilent under mono. It also does not share the glContext nicely either.

        [DllImport("user32.dll")]
        private static extern int SetParent(IntPtr chwnd, int phwnd);
 
        private void tsSimulationView_Click(object sender, EventArgs e)
        {
            SimulationView view = new SimulationView();  //derives from GameWindow
 
            // Get the MdiClient from the parent form.
            for (int i = 0; i < this.Controls.Count; i++)
            {
                // If the form is an MDI container, it will contain an MdiClient control
                // just as it would any other control.
                MdiClient mdiClient = this.Controls[i] as MdiClient;
 
                if (mdiClient != null)
                {
                    SetParent(view.WindowInfo.Handle, mdiClient.Handle.ToInt32());   
                    break;
                }
            }
 
            view.Run();          
        }
the Fiddler's picture

This page in MSDN indicates that you can create MDI children in different threads. In this case, try launching the SimulationView in a different thread and see if this works better.

It also looks like the WS_EX_MDICHILD style is needed for MDI clients to work correctly (this would be added in Source/OpenTK/Platform/Windows/WinGLNative.cs). These could provide a decent workaround until a more permanent solution is available.

djk's picture

Placing the SimulationView on a separate thread seems to be problematic, the event loop seems to be running but no scene updates. After a 20-30 seconds an AccessViolationException occurs in GL.cs in the following function on the Delegates... line. Since I am using the cube from one of the examples to render it is probably not suprising it crashes on a vertex.

    public static 
        void Vertex3(Single x, Single y, Single z)
        {
            Delegates.glVertex3f((Single)x, (Single)y, (Single)z);
        }

I added the mdi child style to the WinGLNative file before I tried threads.

the Fiddler's picture

I've only seen an AccessViolationException like this in a program without a valid OpenGL context. This could also indicate stack corruption at some other point, although I can't think where. One more observation is that GameWindow is not currently threadsafe, which might have something to do with the crash.

The question is, is the GameWindow.Resize event raised? If not, this would indicate a problem during window construction (error handling is somewhat lacking in this area).

Edit: Quite a lot of debug information is written to the System.Diagnostics.Debug stream, try redirecting that to a file to see if any clues show up:

using System.Diagnostics;
 
Debug.Listeners.Clear();
Debug.Listeners.Add(new TextWriterTraceListener("debug.log"));
Debug.Listeners.Add(new ConsoleTraceListener());
Debug.AutoFlush = true;