Starting Mogre from a Windows Form

sAmurai

31-03-2011 12:14:48

Hi Mogre Community!

I am currently using MOGRE as my renderer for an advertiesment app. Here is what i want to achieve:

I want a small Windows Forms GUI as the applications main window. It has its own thread of course.
By pressing a button i want a new windows form to pop up and have run ogre in it.
From there on i want to use the main GUI to manipulate the scenery/window properties in the secondary window.
Therefore the rendering loop from ogre has to run in its own thread.

So far everything works fine. I press a button in my GUI and a new windows form pops up, ogre starts in it and shows my scenery. But as soon as i try to resize the window my program crashes. To me it looks like this must be some kind of multithreading issue, where ogre trys to update the scenery but due to the resizing operations the window is unavailable but i have locks everywhere!
Also i use ResizeBegin and ResizeEnd events to stop and resume the render loop but my program still crashes as soon as i release the mouse button after resizing the window.
I'm totally running out of ideas...

I have been following this tutorial:

http://www.ogre3d.org/tikiwiki/Mogre+Basic+Tutorial+6

And here is the important code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Display
{
public partial class RenderForm : Form
{
private Renderer mRenderer;
private Thread mRenderThread;

public IRenderControl RendererProp
{
get { return mRenderer; }
}

public RenderForm()
{
InitializeComponent();
mRenderer = new Renderer();
//Disposed += new EventHandler(mRenderer.RenderFormDisposed);
FormClosing += new FormClosingEventHandler(mRenderer.RenderFormDisposed);
Resize += new EventHandler(mRenderer.RenderFormResized);
ResizeBegin += new EventHandler(RenderForm_ResizeBegin);
ResizeEnd += new EventHandler(RenderForm_ResizeEnd);
mRenderer.Initialize(Handle);
Show();
mRenderThread = new Thread(new ThreadStart(mRenderer.Go));
mRenderThread.Start();
//mRenderer.Go();
}

private void RenderForm_ResizeBegin(object sender, EventArgs args)
{
lock (mRenderer)
{
Console.WriteLine("ResizeBegin");
mRenderer.ResizePause = true;
}
}

private void RenderForm_ResizeEnd(object sender, EventArgs args)
{
lock (mRenderer)
{
Console.WriteLine("ResizeEnd");
mRenderer.ResizePause = false;
}
}
}
}


using Mogre;
using Mogre.TutorialFramework;
using System;
using System.Windows.Forms;
using System.Threading;

namespace Display
{
public class Renderer : IRenderControl
{
protected Root mRoot = null;
protected RenderWindow mRenderWindow;
protected IntPtr mWindowHandle;
protected SceneManager mSceneMgr;
protected Camera mCamera;
protected Entity mContent;
protected SceneNode mContentNode;
protected bool mFullscreen = false;
protected bool mGoRendering = true;
protected bool mResizePause = false;
protected UInt32 mRenderCounter = 0;

public bool ResizePause
{
get { return mResizePause; }
set { mResizePause = value; }
}

public void RenderFormDisposed(object sender, EventArgs evt)
{
lock (this)
{
mGoRendering = false;
mRoot.Dispose();
mRoot = null;
}
}

public void RenderFormResized(object sender, EventArgs evt)
{
//Console.WriteLine("Resize");
mRenderWindow.WindowMovedOrResized();
}

// Inherited from IRenderControl
public void Initialize(IntPtr windowHandle)
{
mWindowHandle = windowHandle;

//this.Go();
// init root object
mRoot = new Root();

// Define Resources
ConfigFile cf = new ConfigFile();
cf.Load("resources.cfg", "\t:=", true);

var section = cf.GetSectionIterator();
while (section.MoveNext())
{
foreach (var line in section.Current)
{
ResourceGroupManager.Singleton.AddResourceLocation(
line.Value, line.Key, section.CurrentKey);
}
}

// Setup RenderSystem
RenderSystem rs = mRoot.GetRenderSystemByName("Direct3D9 Rendering Subsystem");
// or use "OpenGL Rendering Subsystem"
rs.SetConfigOption("Full Screen", "No");
rs.SetConfigOption("Video Mode", "1024 x 768 @ 32-bit colour");
mRoot.RenderSystem = rs;

// Create Render Window
mRoot.Initialise(false, "Main Ogre Window");
NameValuePairList misc = new NameValuePairList();
misc["externalWindowHandle"] = mWindowHandle.ToString();
mRenderWindow = mRoot.CreateRenderWindow("Main RenderWindow", 1024, 768, false, misc);

// Init resources
TextureManager.Singleton.DefaultNumMipmaps = 5;
ResourceGroupManager.Singleton.InitialiseAllResourceGroups();

// Create scene manager, camera and viewport
mSceneMgr = mRoot.CreateSceneManager(SceneType.ST_GENERIC);
mCamera = mSceneMgr.CreateCamera("Camera");
mCamera.Position = new Vector3(0, 100, 200);
mCamera.LookAt(Vector3.ZERO);
mCamera.AutoAspectRatio = true;
Viewport viewport = mRenderWindow.AddViewport(mCamera);
viewport.BackgroundColour = ColourValue.White;

// create scenery
CreateScene();
}

public void Go()
{
while (mGoRendering)
{
if (!mResizePause)
{
lock (this)
{
if (mRoot != null && mRoot.RenderOneFrame())
{
Console.WriteLine("{0}", mRenderCounter);
mRenderCounter++;
Application.DoEvents();
}
}
}
}
}

// Inherited from IRenderControl
public void Translate(float x, float y, float z)
{
mContentNode.Translate(x, y, z);
}

// Inherited from IRenderControl
public void Rotate(float yaw, float pitch, float roll)
{
mContentNode.Yaw(new Degree(yaw));
mContentNode.Pitch(new Degree(pitch));
mContentNode.Roll(new Degree(roll));
}

// Inherited from IRenderControl
public void Fullscreen(bool fullscreen)
{
// this api function is buggy!
//mRenderWindow.SetFullscreen(fullscreen, 1024, 768);
}

protected void CreateScene()
{
// set lighting properties
mSceneMgr.AmbientLight = ColourValue.Black;
mSceneMgr.ShadowTechnique = ShadowTechnique.SHADOWTYPE_STENCIL_MODULATIVE;

// set skybox
//mSceneMgr.SetSkyDome(true, "Examples/CloudySky", 2, 8);

mContent = mSceneMgr.CreateEntity("Content", "teapot.mesh");
mContent.CastShadows = true;
mContentNode = mSceneMgr.RootSceneNode.CreateChildSceneNode("ContentNode");
mContentNode.AttachObject(mContent);
mContentNode.Rotate(Vector3.UNIT_Y, new Degree(180));

// create plane
Plane plane = new Plane(Vector3.UNIT_Y, 0);
MeshManager.Singleton.CreatePlane("ground", ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME, plane,
1500, 1500, 20, 20, true, 1, 5, 5, Vector3.UNIT_Z);
Entity ground = mSceneMgr.CreateEntity("GroundEntity", "ground");
ground.CastShadows = false;
//ground.ReceivesShadows = true;
MaterialManager.Singleton.Load("white", ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME);
//ground.SetMaterialName("white");
ground.SetMaterial(MaterialManager.Singleton.GetByName("white"));
mSceneMgr.RootSceneNode.CreateChildSceneNode("GroundNode").AttachObject(ground);

// test
/*Entity ent = mSceneMgr.CreateEntity("head", "ogrehead.mesh");
ent.CastShadows = true;
SceneNode node = mSceneMgr.RootSceneNode.CreateChildSceneNode("headNode");
node.AttachObject(ent);*/

// create lights
Light dirLight = mSceneMgr.CreateLight("dirLight");
dirLight.Type = Light.LightTypes.LT_DIRECTIONAL;
dirLight.DiffuseColour = new ColourValue(1, 1, 1);
dirLight.SpecularColour = new ColourValue(1, 1, 1);
dirLight.Direction = new Vector3(1, -1, 1);
}
}
}


Any advice?

Oh btw the program crashes with an System.Runtime.InteropServices.SEHException in the Root's RenderOneFrame method

Tubulii

31-03-2011 18:39:09

I am not an expert in multi-threading with (M)ogre but I think that in fact the MT is the problem.
Why do you not use the tutorial approach? I agree, that MT is a right thing but in my winforms+mogre apps i use this

Imports Mogre


Public Class MogreWinForm
Private _SceneManager As SceneManager
Private _Camera As Camera = Nothing
Private mouselastlocation As Point = Point.Empty
Private running As Boolean = True
Private _freeze As Boolean = False
Private WithEvents _Hook As Control = Nothing
Public Property CaptureInputs As Boolean = True
Public ReadOnly Property Hook As Control
Get
Return _Hook
End Get
End Property
Public ReadOnly Property Camera As Camera
Get
Return _Camera
End Get
End Property
Public Property Freeze As Boolean
Get
Return _freeze
End Get
Set(ByVal value As Boolean)
If value Then
'break update loop
_freeze = True
ElseIf value = False AndAlso _freeze = True Then
'(re)start update loop
_freeze = False
Go()
Else
'just set it to true
_freeze = False
End If

End Set
End Property
Public ReadOnly Property SceneManager As SceneManager
Get
Return _SceneManager
End Get
End Property
Public Sub Destroy()
running = False
End Sub

Private mRoot As Root
Private mWindow As RenderWindow

Public Sub New(ByVal Hook As Control)
_Hook = Hook

'Me.Size = New Size(800, 600)
'AddHandler Disposed, New EventHandler(AddressOf OgreForm_Disposed)
'AddHandler Resize, New EventHandler(AddressOf OgreForm_Resize)
End Sub

Private Sub _Hook_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles _Hook.MouseMove
If CaptureInputs Then
Dim Offset = e.Location - mouselastlocation
If e.Button = MouseButtons.Middle Then
'rotate camera
'Camera.Yaw(New Degree(Offset.X))
Camera.Pitch(New Degree(Offset.Y))

ElseIf e.Button = MouseButtons.Right Then
'move camera
Camera.Position = Camera.Position + New Vector3(Offset.X, Offset.Y, 0)
End If

mouselastlocation = New Point(e.Location.X, e.Location.Y)
End If
End Sub

Private Sub OgreForm_Resize(ByVal sender As Object, ByVal e As EventArgs) Handles _Hook.Resize
mWindow.WindowMovedOrResized()
End Sub

Private Sub OgreForm_Disposed(ByVal sender As Object, ByVal e As EventArgs) Handles _Hook.Disposed
mRoot.Dispose()
mRoot = Nothing
End Sub

Public Sub Go()

While mRoot IsNot Nothing AndAlso mRoot.RenderOneFrame()
If Freeze = True Then Exit Sub ' if freeue = true, break update loop

Try
Application.DoEvents()
Catch ex As Exception
Throw ex
End Try

End While
End Sub
Private Sub DoKeyBoardInputs()

End Sub

Protected Overridable Function FrameStarted(ByVal e As FrameEvent) As Boolean
If CaptureInputs Then DoKeyBoardInputs()




Return running
End Function

Public Sub Init()
' Create root object
mRoot = New Root("plugins.cfg", "", "Ogre.log")

AddHandler mRoot.FrameStarted, AddressOf FrameStarted


' Define Resources
Dim cf As New ConfigFile()
cf.Load("resources.cfg", vbTab & ":=", True)
Dim seci As ConfigFile.SectionIterator = cf.GetSectionIterator()
Dim secName As String, typeName As String, archName As String

While seci.MoveNext()
secName = seci.CurrentKey
Dim settings As ConfigFile.SettingsMultiMap = seci.Current
For Each pair As KeyValuePair(Of String, String) In settings
typeName = pair.Key
archName = pair.Value
ResourceGroupManager.Singleton.AddResourceLocation(archName, typeName, secName)
Next
End While

' Setup RenderSystem
Dim rs As RenderSystem = mRoot.GetRenderSystemByName("Direct3D9 Rendering Subsystem")
' or use "OpenGL Rendering Subsystem"
mRoot.RenderSystem = rs
rs.SetConfigOption("Full Screen", "No")
rs.SetConfigOption("Video Mode", "640 x 480 @ 32-bit colour")

' Create Render Window

mRoot.Initialise(False, "Main Ogre Window")
Dim misc As New NameValuePairList()
misc("externalWindowHandle") = Hook.Handle.ToString()
Dim const_list As Const_NameValuePairList = misc.ReadOnlyInstance
mWindow = mRoot.CreateRenderWindow("Main RenderWindow", 640, 480, False, const_list)


' Init resources
TextureManager.Singleton.DefaultNumMipmaps = 5
ResourceGroupManager.Singleton.InitialiseAllResourceGroups()

' Create a Simple Scene
_SceneManager = mRoot.CreateSceneManager(SceneType.ST_GENERIC)
_Camera = SceneManager.CreateCamera("Camera")
Camera.AutoAspectRatio = True
mWindow.AddViewport(Camera)
Camera.Viewport.BackgroundColour = ColourValue.Green






Dim ent As Entity = SceneManager.CreateEntity("ninja", "ninja.mesh")
SceneManager.RootSceneNode.CreateChildSceneNode().AttachObject(ent)

Camera.Position = New Vector3(0, 400, -800)
Camera.LookAt(ent.BoundingBox.Center)


End Sub

Private Sub _Hook_MouseWheel(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles _Hook.MouseWheel
If CaptureInputs AndAlso e.Delta <> 0 Then
Camera.Position += Camera.Direction * e.Delta

End If
End Sub
End Class


Just convert this file ;) to c# code and create a new MOgreWinForm. Fist call 'Init', than 'go'. 'Go' starts a loop with appication.doevents (<- recommend approach). No further code changes are required. (Note: This code is copied directly from one of my apps. Some classes are mybe missing. Just delete these lines)

Edit: Check the ogre.log in your app dir! and post the errors (if there're any)

good luck!

sAmurai

01-04-2011 02:51:21

The tutorial approach, which i basically used, works just fine. The problem starts when i start the whole mogre initialization from another windows form.
That means i have a main form which is just a simple gui. Then i press a button on that form and another form is created in which mogre is running.
While mogre is running in that form i still want to be able to control it via my main form. That's why i have to use 2 threads, so the main forms input can still be processed.

Edit: Ok i found a way to prevent the crash:

First i started the mogre thread like this:

Main_form _button_click -> init mogre form -> init mogre -> start render loop in new thread

now i am doing this:

Main_form_button_click -> start new thread -> init mogre -> start render loop

I don't exactly understand why this works, but it does.
The only downside is that while i am resizing the mogre form, my d3d9 device keeps on dying and being recovered from lost state like 10 times. Depends on how long i need to resize the window.
Probably not the best way to go, but it doesn't matter in my case i guess..