HOWTO Guide: Unmanaged Irrlicht in .NET WinForms

A forum to store posts deemed exceptionally wise and useful
Swarmer
Posts: 100
Joined: Mon Apr 16, 2007 7:23 am

HOWTO Guide: Unmanaged Irrlicht in .NET WinForms

Post by Swarmer »

Here is a guide explaining how to use Irrlicht in a WinForm without using Irrlicht.NET. This is ideal if you want to build an application using the Windows Form Designer in Visual Studio, particularly for a map editor, or any application where an Irrlicht graphical window would work well alongside WinForm buttons, menus, and other items.

Imagine this as a supplement to Tutorial 14, which explained how to use Irrlicht in a win32 window. However, using it in WinForms takes a bit of a different approach, using Threading.

Note: I am not sure if this is the ideal way to do this (I'm a bit new to threads myself); let me know if you spot any bad design choices!

---------------

The final product will be a window that has Irrlicht running visuals inside of it.

1) Create a new Windows Forms Application in Visual Studio.

2) Create a new class: I called it IrrlichtManager. This is the main interface to all your Irrlicht operations.

3) Here is the implementation of the class:

Code: Select all


#pragma  
once
#include "stdafx.h"

using namespace irr;
public ref class IrrlichtManager
{
private:
	IrrlichtDevice*	_device;
	scene::ISceneManager* _smgr;
	video::IVideoDriver* _driver;
	bool _isRunning;

public:
	IrrlichtManager(IrrlichtDevice* device);
	void Init();
	void Close();
	void SetCubeVisiblity(bool isVisible);

private:
	void run();
};

#include "stdafx.h"
#include "IrrlichtManager.h"

// constructor
IrrlichtManager::IrrlichtManager(IrrlichtDevice* device)
: _device (device)
, _smgr	(_device->getSceneManager())
, _driver (_device->getVideoDriver())
, _isRunning (true)
{

}

// Set up some visuals, and begin running the device.
// This method will be the function pointer for the Irrlicht thread.
void IrrlichtManager::Init()
{
	scene::ICameraSceneNode* cam = _smgr->addCameraSceneNode();
	cam->setTarget(core::vector3df(0,0,0));

	scene::ISceneNodeAnimator* anim = _smgr->createFlyCircleAnimator(core::vector3df(0,15,0), 30.0f);
	cam->addAnimator(anim);
	anim->drop();

	scene::ISceneNode* cube = _smgr->addCubeSceneNode(20);
	cube->setID(10);
	cube->setMaterialTexture(0, _driver->getTexture("../media/wall.bmp"));
	cube->setMaterialTexture(1, _driver->getTexture("../media/water.jpg"));
	cube->setMaterialFlag( video::EMF_LIGHTING, false );
	cube->setMaterialType( video::EMT_REFLECTION_2_LAYER );

	_smgr->addSkyBoxSceneNode(
		_driver->getTexture("../media/irrlicht2_up.jpg"),
		_driver->getTexture("../media/irrlicht2_dn.jpg"),
		_driver->getTexture("../media/irrlicht2_lf.jpg"),
		_driver->getTexture("../media/irrlicht2_rt.jpg"),
		_driver->getTexture("../media/irrlicht2_ft.jpg"),
		_driver->getTexture("../media/irrlicht2_bk.jpg"));

	run();
}

// Stop running and dispose of the device
void IrrlichtManager::Close()
{	
	_device->closeDevice();
	_isRunning = false;
}

// This method is to demonstrate how to interact with the device
void IrrlichtManager::SetCubeVisiblity(bool isVisible)
{
	scene::ISceneNode* node = _smgr->getSceneNodeFromId(10);
	node->setVisible(isVisible);
}

// The main Irrlicht loop
void IrrlichtManager::run()
{
	while (_device->run() && _isRunning == true)
	{
		_driver->beginScene(true, true, 0);
		_smgr->drawAll();
		_driver->endScene();
	}
	_device->drop();
}
4) Next, open the Form Designer for your main form, probably called "Form1.h [Design]". Open the Toolbox on the left side of the screen, and select PictureBox. Draw a picture box inside the main form. Also, add a Button, and set the text to "Toggle Cube Visibility." Do this with the Property window.

5) Now you need to add the Irrlicht window to the form. Open up "Form1.h". Add "using namespace irr;" and "using namespace System::Threading;" to the file. Now, Locate the constructor. Add the following code so that the code around the constructor looks like this:

Code: Select all

	public:
		Thread^ irrThread;
		IrrlichtManager^ irrMgr;
		IrrlichtDevice* device;

		Form1()
		{
			InitializeComponent();
			
			SIrrlichtCreationParameters param;
			param.WindowId = reinterpret_cast<void*>(this->pictureBox1->Handle.ToPointer());
			device =  createDeviceEx(param);

			irrMgr = gcnew IrrlichtManager(device);
			irrThread = gcnew Thread(gcnew ThreadStart (irrMgr, &IrrlichtManager::Init));
			irrThread->Start();
		}
What's going on here is that you are creating a new thread where the Irrlicht device main rendering loop will be running. You are using the picture box you added earlier as the Irrlicht window. Those funny caret things are basically pointers to a managed class. "gcnew" is also the managed version of "new". These are weird .NET things that you can read up about later.

6) Now we will add code for the button. Clicking on this button will toggle the visibility of the cube node. This is just to show one way of interacting with the Irrlicht device. Go back to the Form1.h Designer and double click on button1. It should take you have to Form1.h with a method generated for you.
Make it look like this:

Code: Select all

		void button1_Click(System::Object^  sender, EventArgs^  e)
		{
			static bool visible = true;
			visible = !visible;
			irrMgr->SetCubeVisiblity(visible);
		}
7) Almost done! We just need to add some code to properly close the Irrlicht device when the window is closed. Go back to the Form1.h Designer again. Select Form1. In the Properties window, click on the Events button. It's represented by a lightning bolt symbol. Find the event "Form Closing" and double click on the empty field on the right. Just like before, it should take you to Form1.h with a new method stub. Just add these two lines so it looks like this:

Code: Select all

		void Form1_FormClosing (System::Object^ sender, FormClosingEventArgs^  e)
		{
			irrMgr->Close();
			irrThread->Join();
		}
The Join method will wait for the Irrlicht thread to finish before continuing.

8) Now we just add the right references. My stdafx.h file looks like this:

Code: Select all

#pragma once

#pragma comment( lib, "irrlicht.lib" )
#pragma unmanaged
#include <irrlicht.h>
#pragma managed
#include  "IrrlichtManager.h"
The #pragma unmanged and managed lines are important. Make sure you #include the stdafx.h where you need it, like your main() file. Also, this is a good time to set the following option. Go to your property settings. In the general Configuration Properties, change the "Common Language Runtime support" field to the /clr one. You can't use /clr:pure or /clr:safe.

9) You're done! Compile it and hopefully it will work. This is what it should look like:

Image

The camera should be rotating around the cube. When you click the button, the cube should disappear or appear.

For further information, it's a good idea to read up on Threading and the Windows Form Designer.

I hope this helps you get started on your project!
Last edited by Swarmer on Sun Aug 03, 2008 12:49 am, edited 1 time in total.
Swarmer
Posts: 100
Joined: Mon Apr 16, 2007 7:23 am

Post by Swarmer »

Notice that I have run() check my own bool, _isRunning. This is because _device->run() doesn't seem to detect that _device->closeDevice() occurred. Am I doing something wrong with my threads that is causing this? Or is this quick fix acceptable?
darkmaster83
Posts: 24
Joined: Tue Oct 14, 2008 7:41 am

Post by darkmaster83 »

Hi! Are there any snippets, classes or tutorial in order to make Irrlicht catches inputs from mouse and keyboard in a Windows Forms Application?
Adding these things to previous tutorial would be a very good work.
night_hawk
Posts: 153
Joined: Mon Mar 03, 2008 8:42 am
Location: Suceava - Romania
Contact:

Post by night_hawk »

Nice tut, but I've managed this by sort-of hacking the Application::Run(). I split it in 2.

Code: Select all

form1^ myform = gcnew form1();
myform->Visible=true;

//Load the irrlicht device and set it's window handle
//bla bla irrlicht params.. etc
void* windowid=reinterpret_cast<void*>(myform->panel2->Handle.ToPointer());
param.WindowId = windowid;
//do whatever needed etc...

while(device->run() && !myform->IsDisposed)
	{
		myform->Update(); 
		if(myform->panel2->Focused) { // check if Irrlicht's panel is focused
			editor->driver->beginScene(true,true,178,windowid);
			editor->device->getGUIEnvironment()->drawAll();
			editor->driver->endScene();

			//Also a demo to see how to change from irrstring to System String
			irr::core::stringw str;
			str="Rendering FPS: ";
			str+=irr::core::stringw(editor->driver->getFPS());

			System::String^ str2=gcnew System::String(str.c_str());
			myform->toolStripStatusLabel1->Text=str2;
		}
	}
This version is a tad bit faster to get up and running.

Also, input is available to irrlicht as long as the control it's on has focus.
darkmaster83
Posts: 24
Joined: Tue Oct 14, 2008 7:41 am

Post by darkmaster83 »

Maybe it is easy, but I don't understand where I have to place this snippet in order to make program works correctly...what part of Swarmer's code have I to change?

night_hawk wrote:Nice tut, but I've managed this by sort-of hacking the Application::Run(). I split it in 2.

Code: Select all

form1^ myform = gcnew form1();
myform->Visible=true;

//Load the irrlicht device and set it's window handle
//bla bla irrlicht params.. etc
void* windowid=reinterpret_cast<void*>(myform->panel2->Handle.ToPointer());
param.WindowId = windowid;
//do whatever needed etc...

while(device->run() && !myform->IsDisposed)
	{
		myform->Update(); 
		if(myform->panel2->Focused) { // check if Irrlicht's panel is focused
			editor->driver->beginScene(true,true,178,windowid);
			editor->device->getGUIEnvironment()->drawAll();
			editor->driver->endScene();

			//Also a demo to see how to change from irrstring to System String
			irr::core::stringw str;
			str="Rendering FPS: ";
			str+=irr::core::stringw(editor->driver->getFPS());

			System::String^ str2=gcnew System::String(str.c_str());
			myform->toolStripStatusLabel1->Text=str2;
		}
	}
This version is a tad bit faster to get up and running.

Also, input is available to irrlicht as long as the control it's on has focus.
night_hawk
Posts: 153
Joined: Mon Mar 03, 2008 8:42 am
Location: Suceava - Romania
Contact:

Post by night_hawk »

My version is a stand-alone snippet. Instead of Application::Run, you do the loop the way I do.
Jeckel
Posts: 3
Joined: Sat Dec 27, 2008 2:37 am

Post by Jeckel »

Thank you so much for this. The only thing I didn't like about irrlicht was having to give up my Windows Forms.
scriptr
Posts: 16
Joined: Wed Aug 12, 2009 12:14 pm

Post by scriptr »

hi,

i am using irrlicht in windows forms like the way as explained in this thred. But i can not capture "alt key" (especially left alt key) pressed event. Any way to solve this problem?
Lonesome Ducky
Competition winner
Posts: 1123
Joined: Sun Jun 10, 2007 11:14 pm

Post by Lonesome Ducky »

Assuming window.h is included:

Code: Select all

if (GetAsyncKeyState(VK_ALT)<0)
Hopefully it's in there
hybrid
Admin
Posts: 14143
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

If it's a problem with the Irrlicht event handling this might have been fixed already in one of the latest versions (either 1.5.1 or the upcoming 1.6), where we've added checks for the system key events.
scriptr
Posts: 16
Joined: Wed Aug 12, 2009 12:14 pm

Post by scriptr »

i've added GetAsyncKeyState control but the compiler gives the following errors:

Code: Select all

Error	1	error LNK2028: unresolved token (0A0002EB) "extern "C" short __stdcall GetAsyncKeyState(int)" (?GetAsyncKeyState@@$$J14YGFH@Z) referenced in function "private: void __clrcall IrrlichtManager::run(void)" (?run@IrrlichtManager@@$$FA$AAMXXZ)	ProjeXEditor2.obj	ProjeXEditor
Error	2	error LNK2019: unresolved external symbol "extern "C" short __stdcall GetAsyncKeyState(int)" (?GetAsyncKeyState@@$$J14YGFH@Z) referenced in function "private: void __clrcall IrrlichtManager::run(void)" (?run@IrrlichtManager@@$$FA$AAMXXZ)	ProjeXEditor2.obj	ProjeXEditor
Error	3	fatal error LNK1120: 2 unresolved externals	C:\Documents and Settings\Mehmet Ecevit\My Documents\Visual Studio 2008\Projects\ProjeXEditor2\Debug\ProjeXEditor.exe	ProjeXEditor
Dorth
Posts: 931
Joined: Sat May 26, 2007 11:03 pm

Post by Dorth »

Link the lib...
asanbr
Posts: 5
Joined: Mon Jun 18, 2007 3:42 pm

Post by asanbr »

night_hawk wrote:My version is a stand-alone snippet. Instead of Application::Run, you do the loop the way I do.
Night_hawk, thanks for the tips! I've added your code in my main function, instead of Application::Run(), but i think it loose the event handing property :?

If I just use Application::Run() and execute, the form will wait for an event, if I use the while loop, it will process every time and block the thread.

Is there a way to avoid this? Maybe a better "run" function?

There is another question, this function run under main(), but if I want to put the irrlicht object in other form? In the other forms in a app we don't have the Run() function to change, we just call Show() or ShowDialog().


Thanks,

asanbr.

P.S.: sorry for my english, I don't speak english. So forgive me any error.
kapooo
Posts: 1
Joined: Thu Jul 22, 2010 10:17 am

Post by kapooo »

Hi,

what version of irrlicht you used for this example?

I try with 1.7.1 but the program crash (it's compiled without errors).
DtD
Posts: 264
Joined: Mon Aug 11, 2008 7:05 am
Location: Kansas
Contact:

Post by DtD »

Thanks for this! It worked great! Although, it does not work with Static Irrlicht due to a conflict between the "Multi-threaded debug" and /clr switches. This seems to be a limitation of mixing managed and unmanaged C++ in MSVS.

kapooo, it is working fine for me with Irrlicht 1.7.1 and MSVS2008.
Post Reply